├── .env.example ├── README.md ├── configs ├── txt2stick.json └── txt2stick2.json ├── main.py ├── md_ast ├── c2b00520-33e0-41d9-9142-684a257acbe3.png └── jyxzs.png ├── output ├── 如何缓解焦虑_8.mp4 └── 玩瓦洛兰特喜欢叫妈妈_8.mp4 ├── prompt └── 心理短视频 │ ├── Generate_article.txt │ └── Generating_sub_mirror.txt ├── requirements.txt ├── txt2img.py └── txt2video ├── README.md ├── configs ├── 孔乙己.json └── 纬甫.json ├── main.py ├── prompt ├── step1_example.txt ├── step1_prompt.txt ├── step2_explanation.txt ├── step2_splitter_example.txt ├── step2_splitter_prompt.txt ├── step3_example.txt ├── step3_prompt.txt ├── step4_example.txt ├── step4_explanation.txt ├── step4_prompt.txt ├── step5_explanation.txt └── step6_explanation.txt ├── scene_data.json ├── static ├── cover_frame.png ├── frame_1.png ├── frame_10.png ├── frame_11.png ├── frame_12.png ├── frame_13.png ├── frame_14.png ├── frame_15.png ├── frame_16.png ├── frame_17.png ├── frame_18.png ├── frame_19.png ├── frame_2.png ├── frame_20.png ├── frame_21.png ├── frame_22.png ├── frame_23.png ├── frame_24.png ├── frame_25.png ├── frame_26.png ├── frame_27.png ├── frame_28.png ├── frame_29.png ├── frame_3.png ├── frame_30.png ├── frame_31.png ├── frame_32.png ├── frame_33.png ├── frame_34.png ├── frame_35.png ├── frame_36.png ├── frame_37.png ├── frame_4.png ├── frame_5.png ├── frame_6.png ├── frame_7.png ├── frame_8.png ├── frame_9.png ├── temp_frames │ ├── cover_frame.png │ ├── frame_1.png │ ├── frame_10.png │ ├── frame_11.png │ ├── frame_12.png │ ├── frame_13.png │ ├── frame_2.png │ ├── frame_3.png │ ├── frame_4.png │ ├── frame_5.png │ ├── frame_6.png │ ├── frame_7.png │ ├── frame_8.png │ └── frame_9.png ├── uploads │ ├── bgm_0932f953.mp3 │ ├── bgm_1a78a023.mp3 │ └── bgm_d4360d70.mp3 └── videos │ ├── 从祥林嫂的角度讲《彷徨》_output_2e41a3b9.mp4 │ └── 从祥林嫂的角度讲《彷徨》_output_6f8a707e.mp4 ├── templates └── index.html └── uploads ├── cover_04557a68.jpg ├── cover_1f0c26d5.png ├── cover_1ff354de.png ├── cover_3ae2032e.png ├── cover_43c46d0d.png ├── cover_96b0c375.png ├── cover_a022d239.png ├── cover_a320aead.png ├── cover_a42bf3ba.png ├── cover_a6619917.png ├── cover_b85e60c7.png ├── cover_bb76fe6f.png ├── cover_d8ceeaca.png ├── scene_1_50102f86.png ├── scene_1_946e339f.png ├── scene_1_b744c57e.jpg ├── scene_1_b8e5329f.png ├── scene_1_d7038556.png ├── scene_1_f1e07374.jpg ├── scene_2_02ec04b9.png ├── scene_2_167f6126.png ├── scene_2_7e709871.jpg ├── scene_2_9075181d.png ├── scene_2_e4d71314.png ├── scene_2_ec77768d.png ├── scene_3_0030fce3.jpg ├── scene_3_83a87aca.png ├── scene_3_f618f7a6.png ├── scene_4_38a5511e.jpg └── scene_5_d54fca98.png /.env.example: -------------------------------------------------------------------------------- 1 | # LLM 2 | BASE_URL=https://api.deepseek.com 3 | #BASE_URL=http://localhost:11434/v1 4 | # 填入自己deepseek的api-key 5 | DEEPSEEK_API_KEY=sk-xx 6 | MODEL_NAME=deepseek-chat 7 | #MODEL_NAME=qwen2.5 8 | TEMPERATURE=0.3 9 | 10 | # 语音配置 11 | VOICE_MODEL=zh-CN-XiaoxiaoNeural 12 | 13 | # 绘图工作流 14 | WORK_URL=http://localhost:8188/prompt 15 | # 本机的comfyui的输出文件夹路径 16 | OUTPUT_DIR=D:\ComfyUI\ComfyUI-aki-v1.6\ComfyUI\output 17 | WORK_PATH=./config/txt2stick.json -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # PsycheFlowGen 2 | AI工作流生成抖音爆款心理知识短视频(AI generates short videos of psychological knowledge) 3 | [![Ask DeepWiki](https://deepwiki.com/badge.svg)](https://deepwiki.com/Xikcn/PsycheFlowGen) 4 | 5 | 6 | ## 本项目展示 7 | https://github.com/user-attachments/assets/a076865c-08f9-438f-8246-db5ff04c2433 8 | 9 | ## 项目简介 10 | PsycheFlowGen 利用大语言模型(ChatDeepSeek)和 ComfyUI,自动生成心理学知识短视频的文案、分镜、配图、配音,并合成最终视频,适用于抖音等短视频平台。 11 | 12 | ## 新项目(漫画转短视频) 13 | https://github.com/Xikcn/ComicFlow-Engine 14 | 15 | 具体效果(可见youtube作品):https://youtu.be/wBCupT1kJes 16 | 17 | 1. 可以采用阿里的CosyVoice代替edgetts,可以使用更好的音色体验。 18 | 2. 采用其他图片生成模型也能得到更好体验,由于本机是6G显存因此图片效果一般,因此运行txt2video中脚本,可直接自己上传图片,代替comfyui生成, 19 | 详细使用流程,可见网页说明。 20 | 3. ... 21 | 22 | ## 功能特点 23 | - 自动生成主题文案并摘要提取关键词 24 | - 自动分镜策划和黑白火柴人风格英文正向/负向提示词 25 | - 基于 ComfyUI 实现图像生成 26 | - 基于 edge-tts 实现中文语音合成 27 | - 通过 moviepy 合成视频并输出 MP4 文件 28 | 29 | ## 目录结构 30 | ``` 31 | . 32 | ├── main.py # 主程序入口 33 | ├── txt2img.py # 文本转图像模块 34 | ├── configs/ 35 | │ └── txt2stick.json # ComfyUI 工作流配置 36 | ├── prompt/ 37 | │ ├── Generate_article.txt 38 | │ └── Generating_sub_mirror.txt 39 | ├── .gitignore # 忽略文件 40 | ├── README.md # 项目说明 41 | └── output/ # 生成的图片、音频和视频 42 | ``` 43 | 44 | ## 环境要求 45 | - Python 3.8+ 46 | - 操作系统:Windows/Mac/Linux 均可 47 | - 依赖库见 requirements.txt 48 | 49 | ## 安装依赖 50 | 建议创建虚拟环境后安装依赖: 51 | ```bash 52 | python -m venv venv 53 | # Windows: venv\Scripts\activate 或 source venv/bin/activate 54 | pip install -r requirements.txt 55 | ``` 56 | 57 | ## 环境变量配置 58 | 在项目根目录新建 `.env` 文件,示例内容: 59 | ``` 60 | MODEL_NAME=your_llm_model_name 61 | WORK_URL=http://localhost:8188/prompt 62 | OUTPUT_DIR=./output 63 | WORK_PATH=./configs/txt2stick.json 64 | VOICE_MODEL=zh-CN-XiaoxiaoNeural 65 | ``` 66 | 67 | - `MODEL_NAME`:调用的 LLM 模型名称 68 | - `WORK_URL`:ComfyUI 接口地址 69 | - `OUTPUT_DIR`:图片、音频和视频输出目录 70 | - `WORK_PATH`:ComfyUI 工作流配置文件路径 71 | - `VOICE_MODEL`:edge-tts 可用的声音模型名称 72 | 73 | ## 使用方法 74 | 运行主程序并按照提示执行: 75 | ```bash 76 | python main.py 77 | ``` 78 | 79 | ## 输出说明 80 | - 程序执行后会在 `output/` 目录下生成: 81 | - `cover.mp3` / `cover_frame.png`:封面配音及帧 82 | - `frame_X.png` / `scene_X.mp3`:每个分镜的图像和配音 83 | - `主题_镜头数.mp4`:最终合成的视频文件 84 | 85 | ### 本项目只用于学习,想要实际进行副业建议使用coze+剪映小助手 86 | ![c2b00520-33e0-41d9-9142-684a257acbe3.png](md_ast/c2b00520-33e0-41d9-9142-684a257acbe3.png) 87 | ![jyxzs.png](md_ast/jyxzs.png) 88 | #### coze实际效果 89 | https://github.com/user-attachments/assets/1cb60c74-db02-419c-a0a0-d04ba7aeeb22 90 | 91 | 92 | ## 许可证 93 | MIT License 94 | 95 | 96 | -------------------------------------------------------------------------------- /configs/txt2stick.json: -------------------------------------------------------------------------------- 1 | { 2 | "3": { 3 | "inputs": { 4 | "seed": 905348571989839, 5 | "steps": 20, 6 | "cfg": 7, 7 | "sampler_name": "dpm_2", 8 | "scheduler": "exponential", 9 | "denoise": 1, 10 | "model": [ 11 | "10", 12 | 0 13 | ], 14 | "positive": [ 15 | "6", 16 | 0 17 | ], 18 | "negative": [ 19 | "7", 20 | 0 21 | ], 22 | "latent_image": [ 23 | "5", 24 | 0 25 | ] 26 | }, 27 | "class_type": "KSampler", 28 | "_meta": { 29 | "title": "K采样器" 30 | } 31 | }, 32 | "4": { 33 | "inputs": { 34 | "ckpt_name": "awpainting_v14.safetensors" 35 | }, 36 | "class_type": "CheckpointLoaderSimple", 37 | "_meta": { 38 | "title": "Checkpoint加载器(简易)" 39 | } 40 | }, 41 | "5": { 42 | "inputs": { 43 | "width": 512, 44 | "height": 512, 45 | "batch_size": 1 46 | }, 47 | "class_type": "EmptyLatentImage", 48 | "_meta": { 49 | "title": "空Latent图像" 50 | } 51 | }, 52 | "6": { 53 | "inputs": { 54 | "text": "the boy , jianbihua", 55 | "clip": [ 56 | "10", 57 | 1 58 | ] 59 | }, 60 | "class_type": "CLIPTextEncode", 61 | "_meta": { 62 | "title": "CLIP文本编码" 63 | } 64 | }, 65 | "7": { 66 | "inputs": { 67 | "text": "(worst quality, low quality:1.4),(depth of field, blurry:1.2),(greyscale, monochrome:1.1),3D face,\n\ncropped,lowres,text,(nsfw:1.3),(worst quality:2),(low quality:2),(normal quality:2),normal quality,((grayscale)),skin spots,\n\nacnes,skin blemishes,age spot,(ugly:1.331),(duplicate:1.331),(morbid:1.21),(mutilated:1.21),(tranny:1.331),mutated hands,\n\n(poorly drawn hands:1.5),blurry,(bad anatomy:1.21),(bad proportions:1.331),extra limbs,(disfigured:1.331),\n\n(missing arms:1.331),(extra legs:1.331),(fused fingers:1.61051),(too many fingers:1.61051),(unclear eyes:1.331),\n\nlowers,bad hands,missing fingers,extra digit,bad hands,missing fingers,(((extra arms and legs))),", 68 | "clip": [ 69 | "10", 70 | 1 71 | ] 72 | }, 73 | "class_type": "CLIPTextEncode", 74 | "_meta": { 75 | "title": "CLIP文本编码" 76 | } 77 | }, 78 | "8": { 79 | "inputs": { 80 | "samples": [ 81 | "3", 82 | 0 83 | ], 84 | "vae": [ 85 | "4", 86 | 2 87 | ] 88 | }, 89 | "class_type": "VAEDecode", 90 | "_meta": { 91 | "title": "VAE解码" 92 | } 93 | }, 94 | "9": { 95 | "inputs": { 96 | "filename_prefix": "ComfyUI", 97 | "images": [ 98 | "8", 99 | 0 100 | ] 101 | }, 102 | "class_type": "SaveImage", 103 | "_meta": { 104 | "title": "保存图像" 105 | } 106 | }, 107 | "10": { 108 | "inputs": { 109 | "lora_name": "火柴人_v1.0.safetensors", 110 | "strength_model": 1, 111 | "strength_clip": 3.0000000000000003, 112 | "model": [ 113 | "4", 114 | 0 115 | ], 116 | "clip": [ 117 | "4", 118 | 1 119 | ] 120 | }, 121 | "class_type": "LoraLoader", 122 | "_meta": { 123 | "title": "加载LoRA" 124 | } 125 | } 126 | } -------------------------------------------------------------------------------- /configs/txt2stick2.json: -------------------------------------------------------------------------------- 1 | { 2 | "3": { 3 | "inputs": { 4 | "seed": 844717615267177, 5 | "steps": 20, 6 | "cfg": 7, 7 | "sampler_name": "dpmpp_2m", 8 | "scheduler": "normal", 9 | "denoise": 1, 10 | "model": [ 11 | "10", 12 | 0 13 | ], 14 | "positive": [ 15 | "6", 16 | 0 17 | ], 18 | "negative": [ 19 | "7", 20 | 0 21 | ], 22 | "latent_image": [ 23 | "5", 24 | 0 25 | ] 26 | }, 27 | "class_type": "KSampler", 28 | "_meta": { 29 | "title": "K采样器" 30 | } 31 | }, 32 | "4": { 33 | "inputs": { 34 | "ckpt_name": "awpainting_v14.safetensors" 35 | }, 36 | "class_type": "CheckpointLoaderSimple", 37 | "_meta": { 38 | "title": "Checkpoint加载器(简易)" 39 | } 40 | }, 41 | "5": { 42 | "inputs": { 43 | "width": 512, 44 | "height": 512, 45 | "batch_size": 1 46 | }, 47 | "class_type": "EmptyLatentImage", 48 | "_meta": { 49 | "title": "空Latent图像" 50 | } 51 | }, 52 | "6": { 53 | "inputs": { 54 | "text": "the boy ,jianbihua", 55 | "clip": [ 56 | "10", 57 | 1 58 | ] 59 | }, 60 | "class_type": "CLIPTextEncode", 61 | "_meta": { 62 | "title": "CLIP文本编码" 63 | } 64 | }, 65 | "7": { 66 | "inputs": { 67 | "text": "(worst quality, low quality:1.4),(depth of field, blurry:1.2),(greyscale, monochrome:1.1),3D face,\n\ncropped,lowres,text,(nsfw:1.3),(worst quality:2),(low quality:2),(normal quality:2),normal quality,((grayscale)),skin spots,\n\nacnes,skin blemishes,age spot,(ugly:1.331),(duplicate:1.331),(morbid:1.21),(mutilated:1.21),(tranny:1.331),mutated hands,\n\n(poorly drawn hands:1.5),blurry,(bad anatomy:1.21),(bad proportions:1.331),extra limbs,(disfigured:1.331),\n\n(missing arms:1.331),(extra legs:1.331),(fused fingers:1.61051),(too many fingers:1.61051),(unclear eyes:1.331),\n\nlowers,bad hands,missing fingers,extra digit,bad hands,missing fingers,(((extra arms and legs))),", 68 | "clip": [ 69 | "10", 70 | 1 71 | ] 72 | }, 73 | "class_type": "CLIPTextEncode", 74 | "_meta": { 75 | "title": "CLIP文本编码" 76 | } 77 | }, 78 | "8": { 79 | "inputs": { 80 | "samples": [ 81 | "3", 82 | 0 83 | ], 84 | "vae": [ 85 | "4", 86 | 2 87 | ] 88 | }, 89 | "class_type": "VAEDecode", 90 | "_meta": { 91 | "title": "VAE解码" 92 | } 93 | }, 94 | "9": { 95 | "inputs": { 96 | "filename_prefix": "ComfyUI", 97 | "images": [ 98 | "8", 99 | 0 100 | ] 101 | }, 102 | "class_type": "SaveImage", 103 | "_meta": { 104 | "title": "保存图像" 105 | } 106 | }, 107 | "10": { 108 | "inputs": { 109 | "lora_name": "火柴人_v1.0.safetensors", 110 | "strength_model": 1.0000000000000002, 111 | "strength_clip": 2.0000000000000004, 112 | "model": [ 113 | "4", 114 | 0 115 | ], 116 | "clip": [ 117 | "4", 118 | 1 119 | ] 120 | }, 121 | "class_type": "LoraLoader", 122 | "_meta": { 123 | "title": "加载LoRA" 124 | } 125 | } 126 | } -------------------------------------------------------------------------------- /main.py: -------------------------------------------------------------------------------- 1 | from PIL import Image, ImageDraw, ImageFont 2 | import edge_tts 3 | from moviepy.editor import * 4 | import asyncio 5 | from moviepy.editor import AudioFileClip 6 | from langchain_core.prompts import SystemMessagePromptTemplate, HumanMessagePromptTemplate, ChatPromptTemplate 7 | from langchain_deepseek import ChatDeepSeek 8 | import os 9 | from langchain.output_parsers import StructuredOutputParser, ResponseSchema 10 | from txt2img import TextToImg 11 | from langchain_core.output_parsers import StrOutputParser 12 | import json 13 | import re 14 | from dotenv import load_dotenv 15 | 16 | 17 | load_dotenv() 18 | 19 | 20 | def main(topic:str="爱情三脚猫",keyframes:int=8): 21 | llm = ChatDeepSeek(model=os.getenv('MODEL_NAME')) 22 | file_prompt1 = open(file="prompt/心理短视频/Generate_article.txt", mode="r", encoding="utf-8").read() 23 | file_prompt2 = open(file="prompt/心理短视频/Generating_sub_mirror.txt", mode="r", encoding="utf-8").read() 24 | # 定义系统消息模板 25 | system_template = ( 26 | f"{file_prompt1}\n" + 27 | """ 28 | # OutputFormat 29 | 以 JSON 格式输出结果,结构如下: 30 | '''json 31 | {{ 32 | "title": "文章标题", 33 | "content": "文章正文", 34 | "keywords": "文章关键词" 35 | 36 | }} 37 | ''' 38 | """) 39 | system_message_prompt = SystemMessagePromptTemplate.from_template(system_template) 40 | 41 | # 定义用户消息模板 42 | human_template = "写一篇关于 {topic} 的文案" 43 | human_message_prompt = HumanMessagePromptTemplate.from_template(human_template) 44 | 45 | # 组合成聊天提示模板 46 | chat_prompt = ChatPromptTemplate.from_messages([system_message_prompt, human_message_prompt]) 47 | 48 | # 定义输出结构 49 | response_schemas = [ 50 | ResponseSchema(name="title", description="文章标题"), 51 | ResponseSchema(name="content", description="文章正文"), 52 | ResponseSchema(name="keywords", description="文章关键词") 53 | ] 54 | parser = StructuredOutputParser.from_response_schemas(response_schemas) 55 | 56 | 57 | chain = chat_prompt | llm | parser 58 | 59 | system_template2 = f"{file_prompt2}\n"+""" 60 | 示例输出: 61 | ```json 62 | {{ 63 | "分镜结构": {{ 64 | "封面提示词": {{ 65 | "正向提示词": ["Black and white stick figures", "One giving too many hearts causes the other to retreat", "Clean lines", "Pure white background", "Psychological comic style"], 66 | "负向提示词": [] 67 | }}, 68 | "分镜列表": [ 69 | {{ 70 | "分镜编号": 1, 71 | "标题": "本期要聊的主题", 72 | "时长": 5, 73 | "字幕": {{ 74 | "中文": "为什么付出越多,对方越不珍惜?", 75 | "英文": "Why more giving leads to less appreciation?" 76 | }}, 77 | "正向提示词": ["Black and white line drawing", "Two stick figures", "Left figure continuously gives hearts to the right", "Right figure's expression changes from smiling to indifferent", "Pure white background", "Minimalist style"], 78 | "负向提示词": ["Color", "Complex background", "Realistic style", "Multiple characters"] 79 | }}, 80 | {{ 81 | "分镜编号": 2, 82 | "标题": "边际效用演示", 83 | "时长": 8, 84 | "字幕": {{ 85 | "中文": "第一块蛋糕很香,第十块想吐", 86 | "英文": "Like cake, love loses flavor when over-served" 87 | }}, 88 | "正向提示词": ["Stick figure A handing cake to B", "Comic panel animation showing reaction from joy to disgust", "Black and white line animation"], 89 | "负向提示词": ["Colored food", "Realistic tableware", "Background decorations"] 90 | }}, 91 | {{ 92 | "分镜编号": 3, 93 | "标题": "天平失衡", 94 | "时长": 7, 95 | "字幕": {{ 96 | "中文": "爱情天平倾斜时,沉重感会压垮关系", 97 | "英文": "Imbalanced scales create unbearable pressure" 98 | }}, 99 | "正向提示词": ["Line drawing scales", "One side overloaded with gift boxes", "Empty opposite side", "Two stick figures observing from both sides"], 100 | "负向提示词": ["Realistic scales", "Colored objects", "Digital effects"] 101 | }}, 102 | {{ 103 | "分镜编号": 4, 104 | "标题": "自我消失过程", 105 | "时长": 8, 106 | "字幕": {{ 107 | "中文": "当你为爱不断缩小,最后会消失", 108 | "英文": "Shrinking yourself for love leads to vanishing" 109 | }}, 110 | "正向提示词": ["Frame-by-frame animation", "Giving figure gradually shrinking and becoming transparent", "Negative space composition"], 111 | "负向提示词": ["Color gradients", "Background patterns", "Sudden disappearance effect"] 112 | }}, 113 | {{ 114 | "分镜编号": 5, 115 | "标题": "边界设置", 116 | "时长": 7, 117 | "字幕": {{ 118 | "中文": "画条虚线:这里是我,那里是你", 119 | "英文": "Healthy love needs dotted-line boundaries" 120 | }}, 121 | "正向提示词": ["Blinking dotted line between two figures", "Hand gestures indicating division", "Minimalist composition"], 122 | "负向提示词": ["Solid walls", "Complex separators", "Colored lines"] 123 | }}, 124 | {{ 125 | "分镜编号": 6, 126 | "标题": "互动抛接", 127 | "时长": 8, 128 | "字幕": {{ 129 | "中文": "爱要像抛接球,有来有往才有趣", 130 | "英文": "Love should be a ball-tossing game" 131 | }}, 132 | "正向提示词": ["Two figures happily tossing heart-shaped balls", "Smooth motion lines"], 133 | "负向提示词": ["Realistic spheres", "Complex movements", "Background decorations"] 134 | }}, 135 | {{ 136 | "分镜编号": 7, 137 | "标题": "多元价值", 138 | "时长": 7, 139 | "字幕": {{ 140 | "中文": "把心分成几块:爱情只是其中一块", 141 | "英文": "Divide your heart: love is just one piece" 142 | }}, 143 | "正向提示词": ["Figure with branching paths", "Connecting work/friends/hobbies icons"], 144 | "负向提示词": ["Realistic icons", "Colored partitions", "Complex diagrams"] 145 | }}, 146 | {{ 147 | "分镜编号": 8, 148 | "标题": "双灯辉映", 149 | "时长": 10, 150 | "字幕": {{ 151 | "中文": "两盏独立的灯,才能互相温暖", 152 | "英文": "Two separate lights warm each other best" 153 | }}, 154 | "正向提示词": ["Two glowing figures maintaining distance", "Soft light interweaving", "Minimalist negative space"], 155 | "负向提示词": ["Colored lighting effects", "Complex shadows", "Background decorations"] 156 | }} 157 | ], 158 | "总时长": 60, 159 | "核心策略": [ 160 | "严格保持黑白火柴人风格一致性", 161 | "每个分镜平均7-8秒,符合短视频节奏", 162 | "字幕控制在18字内并配精准英文翻译", 163 | "使用生活化比喻解释心理学概念", 164 | "保持「付出平衡」核心主题贯穿始终" 165 | ] 166 | }} 167 | }} 168 | ``` 169 | """ 170 | system_message_prompt2 = SystemMessagePromptTemplate.from_template(system_template2) 171 | 172 | # 定义用户消息模板 173 | human_template2 = """ 174 | 用户输入的内容如下: 175 | 关键词:{keywords} 176 | 标题:{title} 177 | 正文:{content} 178 | 分镜要求:"""+ f"{keyframes}个" 179 | 180 | 181 | human_message_prompt2 = HumanMessagePromptTemplate.from_template(human_template2) 182 | 183 | # 组合成聊天提示模板 184 | chat_prompt2 = ChatPromptTemplate.from_messages([system_message_prompt2, human_message_prompt2]) 185 | 186 | 187 | 188 | chain2 = chat_prompt2 | llm | StrOutputParser() 189 | 190 | 191 | chain3 = chain |chain2 192 | 193 | # result2 = chain2.invoke({"guanjianci":result.get('keywords'),"title":result.get('title'),"zhengwen":result.get('content')}) 194 | 195 | 196 | 197 | result2 = chain3.invoke({"topic":topic}) 198 | 199 | 200 | if 'json' in result2: 201 | pattern = r"```json\s*({.*?})\s*```" 202 | match = re.search(pattern, result2, re.DOTALL) 203 | if match: 204 | json_content = match.group(1) 205 | try: 206 | x = json.loads(json_content) 207 | # print(x) 208 | except json.JSONDecodeError: 209 | print(f"JSON解析错误: {json_content}") 210 | 211 | else: 212 | print("未找到匹配的 JSON 内容") 213 | else: 214 | # 如果没有JSON格式,尝试解析普通文本 215 | # 尝试从文本中提取答案和参考资料 216 | print("没发现json") 217 | 218 | print(x) 219 | 220 | 221 | 封面 = x.get('分镜结构').get("封面提示词") 222 | 223 | result = x.get('分镜结构').get("分镜列表") 224 | print("-"*30) 225 | print("分镜设置如下:") 226 | print(result) 227 | print("-"*30) 228 | 229 | def text_to_image(prompt_text): 230 | """ 231 | 调用ComfyUI工作流生成图片,返回新图片的路径 232 | """ 233 | URL = os.getenv("WORK_URL") 234 | OUTPUT_DIR = os.getenv("OUTPUT_DIR") 235 | result_path = TextToImg(URL, OUTPUT_DIR).generate_image(prompt_text,work_path=os.getenv("WORK_PATH")) 236 | return result_path 237 | 238 | 239 | 240 | # 1. 生成插画 241 | # 生成封面插画 242 | cover_img_path = text_to_image(', '.join(封面)) 243 | print('封面插画路径:', cover_img_path) 244 | 245 | # 合成封面音频 246 | async def synthesize(text, out_path): 247 | communicate = edge_tts.Communicate(text, os.getenv("VOICE_MODEL")) 248 | await communicate.save(out_path) 249 | 250 | cover_audio_path = "output/cover.mp3" 251 | cover_text = f"本期要讲的主题是{topic}" 252 | asyncio.run(synthesize(cover_text, cover_audio_path)) 253 | 254 | # 生成封面帧 255 | bg = Image.new("RGBA", (1080, 1920), (255, 255, 255, 255)) 256 | fg = Image.open(cover_img_path).convert('RGBA').resize((800, 800)) 257 | bg.paste(fg, (140, 960), fg) # 下半部分 258 | draw = ImageDraw.Draw(bg) 259 | # 左上角显示主题 260 | theme_font = ImageFont.truetype("msyh.ttc", 40) 261 | draw.text((50, 50), f"本期主题:{topic}", fill=(0, 0, 0), font=theme_font) 262 | cover_frame_path = "output/cover_frame.png" 263 | bg.save(cover_frame_path) 264 | # 合成封面clip 265 | cover_audio_clip = AudioFileClip(cover_audio_path) 266 | cover_duration = cover_audio_clip.duration 267 | cover_clip = ImageClip(cover_frame_path).set_duration(cover_duration) 268 | cover_clip = cover_clip.set_audio(cover_audio_clip) 269 | 270 | # 1.5 生成分镜插画 271 | for scene in result: 272 | prompt = ','.join(scene['正向提示词']) 273 | img_path = text_to_image(prompt) 274 | print(img_path) 275 | scene['img'] = img_path 276 | 277 | # 3. 合成音频 278 | for scene in result: 279 | zh_text = scene['字幕']['中文'] 280 | audio_path = f"output/scene_{scene['分镜编号']}.mp3" 281 | asyncio.run(synthesize(zh_text, audio_path)) 282 | scene['audio'] = audio_path 283 | 284 | # 4. 合成视频 285 | clips = [cover_clip] # 先加封面clip 286 | for scene in result: 287 | # 创建白色背景 288 | bg = Image.new("RGBA", (1080, 1920), (255, 255, 255, 255)) 289 | # 加载插画并缩放 290 | fg = Image.open(scene['img']).convert('RGBA').resize((800, 800)) 291 | bg.paste(fg, (140, 500), fg) 292 | # 画黑线 293 | draw = ImageDraw.Draw(bg) 294 | draw.line([(0, 1300), (1080, 1300)], fill=(0, 0, 0), width=5) 295 | # 左上角显示主题 296 | theme_font = ImageFont.truetype("msyh.ttc", 36) 297 | draw.text((50, 30), f"本期主题:{topic}", fill=(0, 0, 0), font=theme_font) 298 | # 分镜标题(大号,居中) 299 | title_font = ImageFont.truetype("msyh.ttc", 52) 300 | title_text = scene['标题'] 301 | title_bbox = title_font.getbbox(title_text) 302 | title_w = title_bbox[2] - title_bbox[0] 303 | title_x = (1080 - title_w) // 2 304 | draw.text((title_x, 90), title_text, fill=(0, 0, 0), font=title_font) 305 | # 字幕 306 | zh_text = scene['字幕']['中文'] 307 | en_text = scene['字幕']['英文'] 308 | zh_font = ImageFont.truetype("msyh.ttc", 40) 309 | en_font = ImageFont.truetype("msyh.ttc", 26) 310 | zh_bbox = zh_font.getbbox(zh_text) 311 | en_bbox = en_font.getbbox(en_text) 312 | zh_w, zh_h = zh_bbox[2] - zh_bbox[0], zh_bbox[3] - zh_bbox[1] 313 | en_w, en_h = en_bbox[2] - en_bbox[0], en_bbox[3] - en_bbox[1] 314 | zh_x = (1080 - zh_w) // 2 315 | en_x = (1080 - en_w) // 2 316 | draw.text((zh_x, 1400), zh_text, fill=(0, 0, 0), font=zh_font) 317 | draw.text((en_x, 1480), en_text, fill=(0, 0, 0), font=en_font) 318 | # 保存帧 319 | frame_path = f"output/frame_{scene['分镜编号']}.png" 320 | bg.save(frame_path) 321 | # 合成clip 322 | audio_clip = AudioFileClip(scene['audio']) 323 | duration = audio_clip.duration 324 | img_clip = ImageClip(frame_path).set_duration(duration) 325 | img_clip = img_clip.set_audio(audio_clip) 326 | clips.append(img_clip) 327 | 328 | final_clip = concatenate_videoclips(clips, method="compose") 329 | final_clip.write_videofile(f"output/{topic}_{keyframes}.mp4", fps=24) 330 | 331 | 332 | if __name__ == '__main__': 333 | main("如何判断对人的滤镜",keyframes=8) -------------------------------------------------------------------------------- /md_ast/c2b00520-33e0-41d9-9142-684a257acbe3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Xikcn/PsycheFlowGen/784f87a512a7bb1f90c37b1c5a9ae2fe2377edf5/md_ast/c2b00520-33e0-41d9-9142-684a257acbe3.png -------------------------------------------------------------------------------- /md_ast/jyxzs.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Xikcn/PsycheFlowGen/784f87a512a7bb1f90c37b1c5a9ae2fe2377edf5/md_ast/jyxzs.png -------------------------------------------------------------------------------- /output/如何缓解焦虑_8.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Xikcn/PsycheFlowGen/784f87a512a7bb1f90c37b1c5a9ae2fe2377edf5/output/如何缓解焦虑_8.mp4 -------------------------------------------------------------------------------- /output/玩瓦洛兰特喜欢叫妈妈_8.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Xikcn/PsycheFlowGen/784f87a512a7bb1f90c37b1c5a9ae2fe2377edf5/output/玩瓦洛兰特喜欢叫妈妈_8.mp4 -------------------------------------------------------------------------------- /prompt/心理短视频/Generate_article.txt: -------------------------------------------------------------------------------- 1 | # Role: 2 | 心理学知识分享博主-专注于通过火柴人视频形式传播 3 | 心理学知识的内容创作专家 4 | 5 | # Background: 6 | 在信息爆炸的时代,大众对心理学知识的需求与日俱增,但专业心理学内容往往晦涩难懂。 7 | 作为心理学知识分享博主,我擅长将复杂的心理学理论转化为通俗易懂的内容, 8 | 并通过黑白火柴人视频的极简风格呈现,帮助受众理解心理现象背后的原理,提升心理健康意识和自我成长能力。 9 | 10 | # Skills: 11 | 1. 心理学内容创作能力: 12 | -精通心理学核心理论体系与应用场景 13 | -擅长将专业心理学概念转化为通俗易懂的表述 14 | -能构建完整的心理学知识框架,确保内容的科学性与实用性 15 | 2. 视觉呈现能力: 16 | -专长于黑白火柴人风格的视频内容设计能将抽象心理概念转化为简洁明了的视觉表达 17 | -掌握视频分镜与节奏控制技币,提升内容吸引力 18 | 3. 案例分析能力: 19 | -善于选取贴近生活的典型案例进行心理分析能从情境描述、心理机制、应对策略等维度进行多角度剖析 20 | -擅长将理论与实践相结合,增强内容的实用性 21 | 4. 内容结构优化能力: 22 | -掌握总分结构的文案创作方法 23 | -能确保段落间逻辑清晰,层次分明 24 | -擅长设计引人入胜的开篇和具有行动指导性的结尾 25 | # Goals: 26 | 1. 创作100%基于科学验证的心理学科普内容 27 | 2. 确保每篇文章包含至少3个实用的心理学洞见 28 | 3. 通过黑白火柴人视频形式提升内容理解效率至少40% 29 | 4. 为每个心理学概念提供至少1个贴近生活的应用案例 30 | 5. 建立系统化的心理学知识传播体系 -------------------------------------------------------------------------------- /prompt/心理短视频/Generating_sub_mirror.txt: -------------------------------------------------------------------------------- 1 | # Role: 2 | 心理学视频分镜策划专家-专精于将心理学内容转化为 3 | 黑白火柴人风格短视频的分镜文案与视觉提示词 4 | 5 | # Background: 6 | 我是连接心理学知识与视觉化表达的专业桥梁,擅长将复杂心理学概念转化为引人入胜的短视频分镜。 7 | 我的核心价值在于通过精准的字幕文案与黑白火柴人风格的分镜设计,让抽象的心理学知识变得生动易懂。 8 | 每个分镜设计均基于科学的视觉传达原则,确保观众能在短时间内理解并记住关键心理学概念, 9 | 如同一位亲切的朋友在日常对话中分享洞见。 10 | 11 | # Skills: 12 | 心理学内容转化能力: 13 | -精通将专业心理学文章拆解为通俗易懂的对话式文案 14 | -擅长用生活化比喻、案例解释抽象心理学概念 15 | -能保持专业准确性的同时增强内容共情性和亲和力 16 | 分镜规划与时长管理: 17 | -能根据目标视频时长精确计算所需分镜数量 18 | -掌握单镜时长与字幕字数的动态平衡算法 19 | -确保每个分镜节奏流畅,整体视频紧凑有力 20 | 双语连贯字幕创作: 21 | -创作富有共情性、对话感的中文字幕(控制在20字内) 22 | -提供地道、精准的英文翻译字幕 23 | -确保字幕间有明确的逻辑递进关系,使用恰当的过渡词和连接词 24 | -整体文案如同一个完整故事,而非独立句子的堆砌 25 | -保持统一的叙述语气和人称,避免突兀转换 26 | -确保相邻字幕间逻辑连贯,使用自然过渡词和连接词,保持流畅的叙事节奏 27 | -附上分镜时长统计和总时长确认 28 | 分镜中绘制火柴人插画: 29 | -为每个分镜创建标准化的英文正向提示词 30 | -为每个分镜提供对应的英文负向提示词 31 | -设计能概括核心主题的封面提示词 32 | -确保所有提示词强调以人的行为来展示镜头语言和白色背景 33 | 34 | #Initialization: 35 | 您好!我是专注于心理学内容的视频分镜策划专家。为了给您提供高度连贯且准确的分镜文案和黑白火柴人提示词方案,请告诉我: 36 | 1. 您希望我根据什么心理学内容(文章/标题/关键词)来创建分镜? 37 | 2. 您期望的视频时长是多少?(例如60秒、90秒或120秒) 38 | 3. 有没有特别需要强调的心理学概念或观点? 39 | 4. 对目标受众有什么特别说明?(如年龄层、专业背景等) 40 | 41 | 我将为您创建一系列高度连贯的字幕文案,以及标准化的英文正向和负向提示词,确保生成纯粹的黑白火柴人风格视频分镜。 -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | pillow~=11.3.0 2 | moviepy~=1.0.3 3 | langchain-core~=0.3.74 4 | langchain~=0.3.27 5 | python-dotenv~=1.1.1 6 | numpy~=2.2.6 7 | fastapi~=0.116.1 8 | pydantic~=2.11.7 9 | starlette~=0.47.2 10 | langchain_deepseek 11 | scipy 12 | werkzeug 13 | python-multipart 14 | uvicorn -------------------------------------------------------------------------------- /txt2img.py: -------------------------------------------------------------------------------- 1 | import json 2 | import os 3 | import time 4 | import uuid 5 | 6 | import requests 7 | 8 | 9 | class TextToImg: 10 | def __init__(self, URL, OUTPUT_DIR): 11 | self.URL = URL 12 | self.OUTPUT_DIR = OUTPUT_DIR 13 | 14 | 15 | # 开始获取请求进行编码 16 | def start_queue(self, prompt_workflow): 17 | p = {"prompt": prompt_workflow} 18 | data = json.dumps(p).encode('utf-8') 19 | requests.post(self.URL, data=data) 20 | 21 | # 定义获取最新图像的逻辑方法,用于之后下面函数的调用 22 | def get_latest_image(self, folder): 23 | files = os.listdir(folder) 24 | image_files = [f for f in files if f.lower().endswith(('.png', '.jpg', '.jpeg'))] 25 | image_files.sort(key=lambda x: os.path.getmtime(os.path.join(folder, x))) 26 | latest_image = os.path.join(folder, image_files[-1]) if image_files else None 27 | return latest_image 28 | 29 | # 开始生成图像,前端UI定义所需变量传递给json 30 | def generate_image(self, prompt1,work_path): 31 | file = str(work_path) 32 | with open(file, "r", encoding="utf-8") as file_json: 33 | prompt = json.load(file_json) 34 | prompt["6"]["inputs"]["text"] = f"{prompt1},White background,jianbihua" 35 | # 设置seed为当前时间戳(秒级) 36 | if "seed" in prompt["3"]["inputs"]: 37 | # 基于时间戳生成UUID 38 | u = uuid.uuid1(int(time.time() * 1000)) 39 | 40 | # 将UUID转换为16位数字 41 | # 取UUID的int表示,然后取模确保16位 42 | num = abs(u.int) % (10 ** 16) 43 | 44 | prompt["3"]["inputs"]["seed"] = num if num >= 10 ** 15 else num + 10 ** 15 45 | previous_image = self.get_latest_image(self.OUTPUT_DIR) # 推理出的最新输出图像保存到指定的OUTPUT_DIR变量路径 46 | self.start_queue(prompt) 47 | # 这是一个循环获取指定路径的最新图像,休眠·一秒钟后继续循环 48 | while True: 49 | latest_image = self.get_latest_image(self.OUTPUT_DIR) 50 | if latest_image != previous_image: 51 | return latest_image 52 | time.sleep(1) 53 | 54 | # 获取文件夹下的所有工作流的文件 55 | def get_all_workflow_files_arr(self, workflow_path): 56 | files = os.listdir(workflow_path) 57 | workflow_arr = [f for f in files if f.lower().endswith(('.json', '.JSON'))] 58 | return workflow_arr 59 | 60 | 61 | 62 | if __name__ == "__main__": 63 | URL = "http://localhost:8188/prompt" 64 | OUTPUT_DIR = r"D:\ComfyUI\ComfyUI-aki-v1.6\ComfyUI\output" 65 | result_path = TextToImg(URL, OUTPUT_DIR).generate_image("'Stick figure brain with dopamine fireworks', 'Phone screen showing multiple dating app icons', 'Arrow from phone to brain'",r"./configs/txt2stick2.json") 66 | print(result_path) -------------------------------------------------------------------------------- /txt2video/README.md: -------------------------------------------------------------------------------- 1 | # 对分镜标题进行设置 2 | 3 | ## 使用说明 4 | 运行主程序并打开网页端口即可 5 | ```bash 6 | python main.py 7 | ``` 8 | 9 | http://127.0.0.1:8000 -------------------------------------------------------------------------------- /txt2video/configs/孔乙己.json: -------------------------------------------------------------------------------- 1 | { 2 | "分镜结构": { 3 | "封面提示词": { 4 | "正向提示词": [ 5 | "Black and white stick figures", 6 | "Tall thin figure in long gown standing at bar", 7 | "Broken legs wrapped in straw", 8 | "Hands dragging body forward", 9 | "Pure white background", 10 | "Minimalist ink drawing style" 11 | ], 12 | "负向提示词": [ 13 | "Color", 14 | "Detailed facial features", 15 | "Complex background", 16 | "Realistic shading" 17 | ] 18 | }, 19 | "分镜列表": [ 20 | { 21 | "分镜编号": 1, 22 | "标题": "长衫酒客", 23 | "时长": 7, 24 | "字幕": { 25 | "中文": "站着喝酒穿长衫的,只我孔乙己", 26 | "英文": "I'm the only long-gowned drinker standing at the bar" 27 | }, 28 | "正向提示词": [ 29 | "Black and white stick figure", 30 | "Tall thin man in tattered long gown", 31 | "Holding wine bowl", 32 | "Standing near bar counter", 33 | "Pure white background" 34 | ], 35 | "负向提示词": [ 36 | "Color", 37 | "Sitting posture", 38 | "Modern clothing", 39 | "Background objects" 40 | ] 41 | }, 42 | { 43 | "分镜编号": 2, 44 | "标题": "酒店阶级", 45 | "时长": 8, 46 | "字幕": { 47 | "中文": "短衣帮站喝四文酒,长衫客坐饮十几文", 48 | "英文": "Short-coat men stand for cheap wine, long-gowned sit for feast" 49 | }, 50 | "正向提示词": [ 51 | "Stick figure class divide", 52 | "Left: standing laborers in short coats", 53 | "Right: seated scholars in long gowns", 54 | "Curved counter separating groups", 55 | "Pure white background" 56 | ], 57 | "负向提示词": [ 58 | "Color", 59 | "Mixed clothing styles", 60 | "Detailed furniture", 61 | "Food details" 62 | ] 63 | }, 64 | { 65 | "分镜编号": 3, 66 | "标题": "伤痕质问", 67 | "时长": 6, 68 | "字幕": { 69 | "中文": "孔乙己,脸上又添新伤疤了!", 70 | "英文": "Kong Yiji! New scars on your face again!" 71 | }, 72 | "正向提示词": [ 73 | "Group of stick figures pointing", 74 | "Central figure with scar lines on face", 75 | "Angry crowd gestures", 76 | "Pure white background" 77 | ], 78 | "负向提示词": [ 79 | "Color", 80 | "Individual facial details", 81 | "Background objects", 82 | "Weapons" 83 | ] 84 | }, 85 | { 86 | "分镜编号": 4, 87 | "标题": "窃书辩白", 88 | "时长": 8, 89 | "字幕": { 90 | "中文": "窃书算偷么?读书人的事!", 91 | "英文": "Taking books isn't stealing! Scholar's privilege!" 92 | }, 93 | "正向提示词": [ 94 | "Stick figure waving arms angrily", 95 | "Book symbols floating nearby", 96 | "Crowd laughing with open mouths", 97 | "Defensive body posture", 98 | "Pure white background" 99 | ], 100 | "负向提示词": [ 101 | "Color", 102 | "Detailed books", 103 | "Library setting", 104 | "Realistic textures" 105 | ] 106 | }, 107 | { 108 | "分镜编号": 5, 109 | "标题": "粉板记账", 110 | "时长": 6, 111 | "字幕": { 112 | "中文": "欠账记粉板,月内定还清", 113 | "英文": "Debts on chalkboard, always paid monthly" 114 | }, 115 | "正向提示词": [ 116 | "Stick figure handing coins to bartender", 117 | "Chalkboard with Chinese characters", 118 | "Wiping gesture on board", 119 | "Pure white background" 120 | ], 121 | "负向提示词": [ 122 | "Color", 123 | "Detailed writing", 124 | "Modern cash register", 125 | "Background clutter" 126 | ] 127 | }, 128 | { 129 | "分镜编号": 6, 130 | "标题": "秀才之痛", 131 | "时长": 7, 132 | "字幕": { 133 | "中文": "连半个秀才都捞不到?", 134 | "英文": "Couldn't even become a scholar?" 135 | }, 136 | "正向提示词": [ 137 | "Stick figure shrinking posture", 138 | "Question marks overhead", 139 | "Others mocking with pointing fingers", 140 | "Broken scholar hat symbol", 141 | "Pure white background" 142 | ], 143 | "负向提示词": [ 144 | "Color", 145 | "Detailed facial expressions", 146 | "School setting", 147 | "Realistic objects" 148 | ] 149 | }, 150 | { 151 | "分镜编号": 7, 152 | "标题": "茴字教学", 153 | "时长": 8, 154 | "字幕": { 155 | "中文": "茴香豆的茴字,有四样写法", 156 | "英文": "'Hui' character has four writing variations" 157 | }, 158 | "正向提示词": [ 159 | "Stick figure writing in air", 160 | "Four floating Chinese characters", 161 | "Young waiter turning away", 162 | "Finger tracing motions", 163 | "Pure white background" 164 | ], 165 | "负向提示词": [ 166 | "Color", 167 | "Detailed paper", 168 | "Ink brushes", 169 | "Classroom setting" 170 | ] 171 | }, 172 | { 173 | "分镜编号": 8, 174 | "标题": "分豆童趣", 175 | "时长": 7, 176 | "字幕": { 177 | "中文": "分茴香豆,孩子一人一颗", 178 | "英文": "Sharing beans, one for each child" 179 | }, 180 | "正向提示词": [ 181 | "Stick figure handing dots to children", 182 | "Multiple small figures reaching out", 183 | "Bowl with simple dot patterns", 184 | "Smiling face symbols", 185 | "Pure white background" 186 | ], 187 | "负向提示词": [ 188 | "Color", 189 | "Detailed food", 190 | "Individual facial features", 191 | "Table setting" 192 | ] 193 | }, 194 | { 195 | "分镜编号": 9, 196 | "标题": "护豆名言", 197 | "时长": 6, 198 | "字幕": { 199 | "中文": "多乎哉?不多也!", 200 | "英文": "Many? Indeed not many!" 201 | }, 202 | "正向提示词": [ 203 | "Stick figure covering bowl with hands", 204 | "Children with question marks", 205 | "Shaking head movement lines", 206 | "Classical pose", 207 | "Pure white background" 208 | ], 209 | "负向提示词": [ 210 | "Color", 211 | "Detailed bowl", 212 | "Background objects", 213 | "Modern gestures" 214 | ] 215 | }, 216 | { 217 | "分镜编号": 10, 218 | "标题": "断腿传闻", 219 | "时长": 8, 220 | "字幕": { 221 | "中文": "偷丁举人书,腿被打折了!", 222 | "英文": "Caught stealing from Squire Ding, legs broken!" 223 | }, 224 | "正向提示词": [ 225 | "Stick figure lying on ground", 226 | "Stick-wielding figure above", 227 | "Broken leg symbols", 228 | "Book icons nearby", 229 | "Pure white background" 230 | ], 231 | "负向提示词": [ 232 | "Color", 233 | "Blood details", 234 | "Realistic violence", 235 | "Background scene" 236 | ] 237 | }, 238 | { 239 | "分镜编号": 11, 240 | "标题": "爬行归来", 241 | "时长": 9, 242 | "字幕": { 243 | "中文": "用手走来的孔乙己,满手是泥", 244 | "英文": "Kong Yiji arrives crawling, hands covered in mud" 245 | }, 246 | "正向提示词": [ 247 | "Stick figure dragging body on ground", 248 | "Broken legs wrapped in straw", 249 | "Mud marks on hands", 250 | "Looking up at counter", 251 | "Pure white background" 252 | ], 253 | "负向提示词": [ 254 | "Color", 255 | "Wheelchair", 256 | "Prosthetics", 257 | "Detailed ground texture" 258 | ] 259 | }, 260 | { 261 | "分镜编号": 12, 262 | "标题": "最后四文", 263 | "时长": 7, 264 | "字幕": { 265 | "中文": "现钱四文,温一碗酒", 266 | "英文": "Four coins in hand, for one bowl of wine" 267 | }, 268 | "正向提示词": [ 269 | "Muddy hand extending coins", 270 | "Bartender taking payment", 271 | "Single wine bowl icon", 272 | "Downcast face posture", 273 | "Pure white background" 274 | ], 275 | "负向提示词": [ 276 | "Color", 277 | "Detailed coins", 278 | "Counter details", 279 | "Multiple characters" 280 | ] 281 | }, 282 | { 283 | "分镜编号": 13, 284 | "标题": "死亡欠账", 285 | "时长": 8, 286 | "字幕": { 287 | "中文": "十九文钱,永远挂在粉板上", 288 | "英文": "Nineteen coins owed, forever on the chalkboard" 289 | }, 290 | "正向提示词": [ 291 | "Chalkboard with Chinese characters", 292 | "Ghostly stick figure fading away", 293 | "Bartender shaking head", 294 | "Question mark floating", 295 | "Pure white background" 296 | ], 297 | "负向提示词": [ 298 | "Color", 299 | "Detailed writing", 300 | "Realistic ghost", 301 | "Background details" 302 | ] 303 | } 304 | ], 305 | "总时长": 96, 306 | "核心策略": [ 307 | "严格黑白火柴人风格:纯白背景+单线人物", 308 | "平均7秒分镜节奏,符合短视频传播规律", 309 | "关键情节视觉化:断腿爬行/粉板记账等标志场景", 310 | "字幕双关语处理:多乎哉/窃书非偷等经典台词", 311 | "悲剧内核强化:通过肢体语言表现尊严崩塌过程" 312 | ] 313 | } 314 | } -------------------------------------------------------------------------------- /txt2video/configs/纬甫.json: -------------------------------------------------------------------------------- 1 | { 2 | "分镜结构": { 3 | "封面提示词": { 4 | "正向提示词": [ 5 | "Black and white stick figure man leaning on bar", 6 | "Floating sake cup fragments", 7 | "Frozen winter window", 8 | "Pure white background", 9 | "Minimalist ink wash style" 10 | ], 11 | "负向提示词": [ 12 | "Color", 13 | "Detailed facial expressions", 14 | "Background details", 15 | "Realistic textures" 16 | ] 17 | }, 18 | "分镜列表": [ 19 | { 20 | "分镜编号": "1", 21 | "时长": 8, 22 | "字幕": { 23 | "中文": "我叫吕纬甫。没想到,在S城这个阴冷的冬日,在这家叫'一石居'的旧酒楼里,竟撞见了十年前的老朋友。", 24 | "英文": "I'm Lü Weifu. Never expected to meet my old friend at 'One Stone Inn' on this gloomy winter day." 25 | }, 26 | "正向提示词": [ 27 | "Stick figure gazing out window", 28 | "Floating frozen breath symbols", 29 | "Broken signboard with '一石居'", 30 | "Minimalist bar counter" 31 | ], 32 | "负向提示词": [ 33 | "Color", 34 | "Detailed interior", 35 | "Facial features" 36 | ] 37 | }, 38 | { 39 | "分镜编号": "2", 40 | "时长": 7, 41 | "字幕": { 42 | "中文": "他刚从北方回来探亲。这相遇,于我而言,意外又难堪。", 43 | "英文": "He just returned from the north. This encounter is both unexpected and awkward." 44 | }, 45 | "正向提示词": [ 46 | "Two stick figures facing each other", 47 | "Zigzag tension lines between them", 48 | "Suitcase symbol near one figure" 49 | ], 50 | "负向提示词": [ 51 | "Handshake gesture", 52 | "Smiling faces", 53 | "Color" 54 | ] 55 | }, 56 | { 57 | "分镜编号": "3", 58 | "时长": 8, 59 | "字幕": { 60 | "中文": "他先认出了我,惊叫我的名字。我一时踌躇,才坐下。", 61 | "英文": "He recognized me first, shouting my name. I hesitated before sitting." 62 | }, 63 | "正向提示词": [ 64 | "Figure pointing with shock lines", 65 | "Second figure shrinking posture", 66 | "Chair being pulled out slowly" 67 | ], 68 | "负向提示词": [ 69 | "Detailed furniture", 70 | "Color", 71 | "Mouth opening" 72 | ] 73 | }, 74 | { 75 | "分镜编号": "4", 76 | "时长": 9, 77 | "字幕": { 78 | "中文": "我知道自己变了,变得连行动都迟缓了,不再是当年那个敏捷精悍、意气风发的青年。", 79 | "英文": "I know I've changed - movements sluggish, no longer that agile young man full of passion." 80 | }, 81 | "正向提示词": [ 82 | "Transparent ghost figure of younger self", 83 | "Present figure with heavy anchor symbol", 84 | "Fading energy lines around body" 85 | ], 86 | "负向提示词": [ 87 | "Color transition", 88 | "Detailed youth figure", 89 | "Background" 90 | ] 91 | }, 92 | { 93 | "分镜编号": "5", 94 | "时长": 7, 95 | "字幕": { 96 | "中文": "他眼中的惊讶和随后流露出的悲伤、不快,我都看得清楚。", 97 | "英文": "I clearly saw his shock, then sadness and displeasure." 98 | }, 99 | "正向提示词": [ 100 | "Close-up of stick figure eyes", 101 | "Tear symbol transforming to storm cloud", 102 | "Arrow pointing to observing figure" 103 | ], 104 | "负向提示词": [ 105 | "Realistic eyes", 106 | "Color", 107 | "Full face" 108 | ] 109 | }, 110 | { 111 | "分镜编号": "6", 112 | "时长": 8, 113 | "字幕": { 114 | "中文": "他问我这些年如何?在太原做什么?我苦笑着回答:'教书,在一个同乡家里。'", 115 | "英文": "'How have you been?' he asked. 'Teaching in a fellow villager's home,' I answered bitterly." 116 | }, 117 | "正向提示词": [ 118 | "Stick figure with curved down mouth", 119 | "Chalkboard symbol floating nearby", 120 | "Question marks above listener" 121 | ], 122 | "负向提示词": [ 123 | "Detailed classroom", 124 | "Color", 125 | "Books" 126 | ] 127 | }, 128 | { 129 | "分镜编号": "7", 130 | "时长": 9, 131 | "字幕": { 132 | "中文": "再往前呢?'无非做了些无聊的事,等于什么也没做。'这话不假,回首这些年,只觉得一片虚无。", 133 | "英文": "'And before?' 'Just boring things - amounting to nothing.' True words. Looking back, only emptiness remains." 134 | }, 135 | "正向提示词": [ 136 | "Figure shrugging with empty hands", 137 | "Floating clock dissolving into dots", 138 | "Fading footprints behind" 139 | ], 140 | "负向提示词": [ 141 | "Solid objects", 142 | "Color", 143 | "Achievement symbols" 144 | ] 145 | }, 146 | { 147 | "分镜编号": "8", 148 | "时长": 7, 149 | "字幕": { 150 | "中文": "看着他,我不禁想起年少时嘲笑苍蝇绕圈飞的可笑和可怜。", 151 | "英文": "Seeing him, I recalled how we mocked flies circling pointlessly." 152 | }, 153 | "正向提示词": [ 154 | "Transparent memory bubble: young figures laughing", 155 | "Floating fly with circular path", 156 | "Pointing finger gestures" 157 | ], 158 | "负向提示词": [ 159 | "Detailed insects", 160 | "Color", 161 | "Background scene" 162 | ] 163 | }, 164 | { 165 | "分镜编号": "9", 166 | "时长": 8, 167 | "字幕": { 168 | "中文": "现在才明白,我自己就是那只苍蝇,兜兜转转,又飞回了原地。", 169 | "英文": "Now I understand: I am that fly, circling back to where I started." 170 | }, 171 | "正向提示词": [ 172 | "Stick figure with fly wings", 173 | "Spiral path ending at inn symbol", 174 | "Arrows forming endless loop" 175 | ], 176 | "负向提示词": [ 177 | "Actual wings", 178 | "Color", 179 | "Forward progress" 180 | ] 181 | }, 182 | { 183 | "分镜编号": "10", 184 | "时长": 6, 185 | "字幕": { 186 | "中文": "我讲起了这次回南方的缘由——两件母亲交代的'任务'。", 187 | "英文": "I explained why I returned south: two 'tasks' from mother." 188 | }, 189 | "正向提示词": [ 190 | "Envelope with 'mother' character", 191 | "Dotted journey arrow pointing south", 192 | "Two checkmark symbols" 193 | ], 194 | "负向提示词": [ 195 | "Detailed letter", 196 | "Color", 197 | "Female figure" 198 | ] 199 | }, 200 | { 201 | "分镜编号": "11", 202 | "时长": 8, 203 | "字幕": { 204 | "中文": "迁葬小弟:我三岁夭折的小弟葬在乡下。今年春天,堂兄来信说坟被水浸,快塌进河里了。", 205 | "英文": "Relocating brother: My three-year-old brother's grave was being eroded by river." 206 | }, 207 | "正向提示词": [ 208 | "Small grave mound near wavy lines", 209 | "Crack symbols on earth", 210 | "Arrow from letter to grave" 211 | ], 212 | "负向提示词": [ 213 | "Water details", 214 | "Color", 215 | "Child figure" 216 | ] 217 | }, 218 | { 219 | "分镜编号": "12", 220 | "时长": 9, 221 | "字幕": { 222 | "中文": "带着雇来的工人到了坟地。河水果然已逼近。坟头平了。我鼓起勇气下令掘坟。", 223 | "英文": "With workers at gravesite. River encroaching. Grave flattened. I ordered the exhumation." 224 | }, 225 | "正向提示词": [ 226 | "Three stick figures near riverbank", 227 | "Shovel symbols in motion", 228 | "Broken grave marker" 229 | ], 230 | "负向提示词": [ 231 | "Detailed tools", 232 | "Color", 233 | "Facial expressions" 234 | ] 235 | }, 236 | { 237 | "分镜编号": "13", 238 | "时长": 8, 239 | "字幕": { 240 | "中文": "掘开一看,棺木早已朽烂成渣。更意外的是,里面什么都没有!", 241 | "英文": "Dug open: coffin rotted to dust. Nothing inside - completely empty!" 242 | }, 243 | "正向提示词": [ 244 | "Open pit with dust cloud", 245 | "Giant question mark overhead", 246 | "Empty rectangle outline" 247 | ], 248 | "负向提示词": [ 249 | "Coffin remains", 250 | "Color", 251 | "Bones" 252 | ] 253 | }, 254 | { 255 | "分镜编号": "14", 256 | "时长": 10, 257 | "字幕": { 258 | "中文": "被褥、衣服、骸骨,甚至传说中最难腐烂的头发,都消失得无影无踪。", 259 | "英文": "Blankets, clothes, bones - even the supposedly indestructible hair - all vanished without trace." 260 | }, 261 | "正向提示词": [ 262 | "Floating disappearance symbols", 263 | "Dashed outlines of missing items", 264 | "Arrow pointing to void" 265 | ], 266 | "负向提示词": [ 267 | "Actual objects", 268 | "Color", 269 | "Remnants" 270 | ] 271 | }, 272 | { 273 | "分镜编号": "15", 274 | "时长": 9, 275 | "字幕": { 276 | "中文": "但我还是用棉花裹了些坟坑里的泥土,装进新买的小棺材,运到父亲坟旁重新埋了。", 277 | "英文": "Yet I wrapped grave soil in cotton, placed it in new coffin, reburied beside father's grave." 278 | }, 279 | "正向提示词": [ 280 | "Hands wrapping dirt bundle", 281 | "Small box with soil symbol", 282 | "Procession to new grave site" 283 | ], 284 | "负向提示词": [ 285 | "Detailed packaging", 286 | "Color", 287 | "Facial determination" 288 | ] 289 | }, 290 | { 291 | "分镜编号": "16", 292 | "时长": 8, 293 | "字幕": { 294 | "中文": "这纯粹是为了'骗骗母亲,使她安心些'。整个过程,充满了徒劳和自欺欺人的荒诞感。", 295 | "英文": "Purely to 'deceive mother, ease her mind.' The whole process reeked of futility and self-deception." 296 | }, 297 | "正向提示词": [ 298 | "Figure bowing before grave", 299 | "Floating puppet strings on hands", 300 | "Melting clock symbol" 301 | ], 302 | "负向提示词": [ 303 | "Sincere emotion", 304 | "Color", 305 | "Comforting scene" 306 | ] 307 | }, 308 | { 309 | "分镜编号": "17", 310 | "时长": 9, 311 | "字幕": { 312 | "中文": "母亲还记挂着旧邻居船户长富的女儿阿顺。多年前,她因羡慕红剪绒花哭求不得还挨了打。", 313 | "英文": "Mother remembered Ashun, neighbor's daughter who was beaten for coveting red paper flowers." 314 | }, 315 | "正向提示词": [ 316 | "Stick figure girl reaching for flower", 317 | "Broken flower symbol on ground", 318 | "Stick figure adult with raised hand" 319 | ], 320 | "负向提示词": [ 321 | "Color", 322 | "Detailed faces", 323 | "Background objects" 324 | ] 325 | }, 326 | { 327 | "分镜编号": "18", 328 | "时长": 8, 329 | "字幕": { 330 | "中文": "母亲让我买两朵送她。我对这差事倒有几分真心乐意。", 331 | "英文": "Mother tasked me to bring her two flowers. I was genuinely willing." 332 | }, 333 | "正向提示词": [ 334 | "Hand holding two flower symbols", 335 | "Uplifted posture with light lines" 336 | ], 337 | "负向提示词": [ 338 | "Detailed flowers", 339 | "Color", 340 | "Facial expression" 341 | ] 342 | }, 343 | { 344 | "分镜编号": "19", 345 | "时长": 10, 346 | "字幕": { 347 | "中文": "记得前年回来,在长富家吃荞麦粉。粉难吃极了,糖多得齁人。", 348 | "英文": "Two years ago at Changfu's, I ate awful buckwheat noodles - oversweetened." 349 | }, 350 | "正向提示词": [ 351 | "Stick figure forcing down bowl", 352 | "Exaggerated sugar crystals above", 353 | "Distorted bowl shape" 354 | ], 355 | "负向提示词": [ 356 | "Realistic food", 357 | "Color", 358 | "Table setting" 359 | ] 360 | }, 361 | { 362 | "分镜编号": "20", 363 | "时长": 9, 364 | "字幕": { 365 | "中文": "但看到阿顺害怕又期待的眼神,我不忍让她失望,硬是灌了下去。", 366 | "英文": "But seeing Ashun's anxious eyes, I forced myself to finish it." 367 | }, 368 | "正向提示词": [ 369 | "Peeking stick figure in corner", 370 | "Arrow from eyes to eating figure", 371 | "Empty bowl with sweat drops" 372 | ], 373 | "负向提示词": [ 374 | "Detailed interior", 375 | "Color", 376 | "Multiple characters" 377 | ] 378 | }, 379 | { 380 | "分镜编号": "21", 381 | "时长": 8, 382 | "字幕": { 383 | "中文": "就为那一刻她忍着的得意笑容,我曾真心祝她一生幸福。", 384 | "英文": "For her suppressed smile when clearing bowls, I truly wished her happiness." 385 | }, 386 | "正向提示词": [ 387 | "Figure with curved mouth hidden by hand", 388 | "Floating heart symbol", 389 | "Stacked bowls being carried" 390 | ], 391 | "负向提示词": [ 392 | "Full smile", 393 | "Color", 394 | "Detailed scene" 395 | ] 396 | }, 397 | { 398 | "分镜编号": "22", 399 | "时长": 7, 400 | "字幕": { 401 | "中文": "这次我特意在济南买了大红的和粉红的剪绒花。", 402 | "英文": "This time I specially bought red and pink paper flowers in Jinan." 403 | }, 404 | "正向提示词": [ 405 | "Hand holding two distinct flower shapes", 406 | "Travel bag symbol nearby" 407 | ], 408 | "负向提示词": [ 409 | "Color details", 410 | "Background", 411 | "Floral patterns" 412 | ] 413 | }, 414 | { 415 | "分镜编号": "23", 416 | "时长": 8, 417 | "字幕": { 418 | "中文": "下午去找长富,扑了个空。他儿子恶狠狠地质问我找他姐干什么。", 419 | "英文": "Afternoon visit to Changfu failed. His son snarled: 'Why seek my sister?'" 420 | }, 421 | "正向提示词": [ 422 | "Angry stick figure blocking door", 423 | "Question mark bubble", 424 | "Empty house outline" 425 | ], 426 | "负向提示词": [ 427 | "Detailed house", 428 | "Color", 429 | "Facial features" 430 | ] 431 | }, 432 | { 433 | "分镜编号": "24", 434 | "时长": 9, 435 | "字幕": { 436 | "中文": "老发奶奶的消息像一盆冷水:阿顺去年就病死了!病因是积劳成疾。", 437 | "英文": "Old Granny Fa's news hit like ice water: 'Ashun died last year! From overwork.'" 438 | }, 439 | "正向提示词": [ 440 | "Stick figure melting posture", 441 | "Ice cube symbols falling", 442 | "Broken heart symbol" 443 | ], 444 | "负向提示词": [ 445 | "Realistic water", 446 | "Color", 447 | "Sickbed" 448 | ] 449 | }, 450 | { 451 | "分镜编号": "25", 452 | "时长": 10, 453 | "字幕": { 454 | "中文": "压垮她的最后一根稻草是伯伯长庚的恶毒挑拨:'你的男人比我还不如!'", 455 | "英文": "The last straw was Uncle Changgeng's venom: 'Your man is worse than me!'" 456 | }, 457 | "正向提示词": [ 458 | "Stick figure with devil horns", 459 | "Speech bubble with poison symbols", 460 | "Crumpling female figure" 461 | ], 462 | "负向提示词": [ 463 | "Realistic features", 464 | "Color", 465 | "Background" 466 | ] 467 | }, 468 | { 469 | "分镜编号": "26", 470 | "时长": 8, 471 | "字幕": { 472 | "中文": "这话让阿顺忧惧交加,终日以泪洗面,最终香消玉殒。", 473 | "英文": "This made Ashun anxious and tearful daily, until she perished." 474 | }, 475 | "正向提示词": [ 476 | "Figure dissolving into teardrops", 477 | "Fading outline with downward arrows", 478 | "Empty space where figure stood" 479 | ], 480 | "负向提示词": [ 481 | "Corpse", 482 | "Color", 483 | "Bed" 484 | ] 485 | }, 486 | { 487 | "分镜编号": "27", 488 | "时长": 7, 489 | "字幕": { 490 | "中文": "老发奶奶叹息阿顺没福气戴这花。我心头一片冰凉。", 491 | "英文": "Granny Fa sighed: 'Ashun had no fortune to wear these.' My heart froze." 492 | }, 493 | "正向提示词": [ 494 | "Flowers floating over grave mound", 495 | "Cracked ice symbol over heart area" 496 | ], 497 | "负向提示词": [ 498 | "Color", 499 | "Detailed grave", 500 | "Flower details" 501 | ] 502 | }, 503 | { 504 | "分镜编号": "28", 505 | "时长": 9, 506 | "字幕": { 507 | "中文": "最终,我把花转送给了阿顺那见我就躲的妹妹阿昭。", 508 | "英文": "Finally I gave the flowers to Azhao - Ashun's sister who avoids me." 509 | }, 510 | "正向提示词": [ 511 | "Flowers thrust toward retreating figure", 512 | "Dashed escape path", 513 | "Reluctant hand taking flowers" 514 | ], 515 | "负向提示词": [ 516 | "Smiling faces", 517 | "Color", 518 | "Grateful posture" 519 | ] 520 | }, 521 | { 522 | "分镜编号": "29", 523 | "时长": 8, 524 | "字幕": { 525 | "中文": "老友对我教'子曰诗云'感到诧异。当年我们拔神像胡子、讨论改革的豪情安在?", 526 | "英文": "My friend was shocked I teach classics. Where's our passion for reform?" 527 | }, 528 | "正向提示词": [ 529 | "Bubble: young figures pulling beard", 530 | "Contrasted with stick figure holding book", 531 | "Floating question mark" 532 | ], 533 | "负向提示词": [ 534 | "Detailed statues", 535 | "Color", 536 | "Crowd scene" 537 | ] 538 | }, 539 | { 540 | "分镜编号": "30", 541 | "时长": 9, 542 | "字幕": { 543 | "中文": "'这些无聊的事算什么?只要随随便便……'我清楚自己的变化:敷敷衍衍,模模糊糊。", 544 | "英文": "'What are these boring things? Just drifting...' I know I've become vague and evasive." 545 | }, 546 | "正向提示词": [ 547 | "Figure with melting edges", 548 | "Floating cloud symbols", 549 | "Broken speech bubbles" 550 | ], 551 | "负向提示词": [ 552 | "Sharp lines", 553 | "Color", 554 | "Clear shapes" 555 | ] 556 | }, 557 | { 558 | "分镜编号": "31", 559 | "时长": 8, 560 | "字幕": { 561 | "中文": "我怕当年朋友不认我了。感激老友的期待,但这更让我不安。", 562 | "英文": "Fear old friends won't recognize me. Grateful for his hope, yet uneasy." 563 | }, 564 | "正向提示词": [ 565 | "Two figures with question mark between", 566 | "Arrow of hope piercing cloud", 567 | "Anxiety lines radiating" 568 | ], 569 | "负向提示词": [ 570 | "Handshake", 571 | "Color", 572 | "Smiling faces" 573 | ] 574 | }, 575 | { 576 | "分镜编号": "32", 577 | "时长": 7, 578 | "字幕": { 579 | "中文": "他付了账(我已懒得客套),我们一同走出酒楼。", 580 | "英文": "He paid (I skipped courtesies), we exited the inn together." 581 | }, 582 | "正向提示词": [ 583 | "Money bag changing hands", 584 | "Two figures passing doorway", 585 | "Tilted inn sign" 586 | ], 587 | "负向提示词": [ 588 | "Detailed payment", 589 | "Color", 590 | "Interior" 591 | ] 592 | }, 593 | { 594 | "分镜编号": "33", 595 | "时长": 8, 596 | "字幕": { 597 | "中文": "寒风夹着雪片扑来。他住的旅馆与我方向相反,我们在门口分别。", 598 | "英文": "Icy wind with snowflakes hit us. His inn opposite direction - we parted at door." 599 | }, 600 | "正向提示词": [ 601 | "Two figures back-to-back", 602 | "Arrow signs pointing opposite ways", 603 | "Snowflake symbols swirling" 604 | ], 605 | "负向提示词": [ 606 | "Color", 607 | "Detailed buildings", 608 | "Facial expressions" 609 | ] 610 | }, 611 | { 612 | "分镜编号": "34", 613 | "时长": 9, 614 | "字幕": { 615 | "中文": "看着他走向风雪中的背影,我独自走向旅馆。风雪打在脸上,竟有种奇异的清醒感。", 616 | "英文": "Watching his back vanish in snow, I walked alone. Snow on face brought strange clarity." 617 | }, 618 | "正向提示词": [ 619 | "Solitary figure in snowstorm", 620 | "Fading second figure in distance", 621 | "Snowflakes as sharp lines on face" 622 | ], 623 | "负向提示词": [ 624 | "Color", 625 | "Detailed landscape", 626 | "Multiple characters" 627 | ] 628 | }, 629 | { 630 | "分镜编号": "35", 631 | "时长": 8, 632 | "字幕": { 633 | "中文": "天色昏黄,街道和房屋都笼罩在密织的雪网里。", 634 | "英文": "Dusk yellow sky, streets and houses shrouded in dense snow-net." 635 | }, 636 | "正向提示词": [ 637 | "Stick figure in snow grid", 638 | "Simplified house outlines", 639 | "Low sun symbol with snow webs" 640 | ], 641 | "负向提示词": [ 642 | "Detailed architecture", 643 | "Color gradients", 644 | "Realistic snow" 645 | ] 646 | } 647 | ] 648 | } 649 | } -------------------------------------------------------------------------------- /txt2video/main.py: -------------------------------------------------------------------------------- 1 | import logging 2 | import os 3 | import json 4 | import shutil 5 | import tempfile 6 | import uuid 7 | import asyncio 8 | import subprocess 9 | from pathlib import Path 10 | from typing import Dict, Optional 11 | from fastapi import FastAPI, HTTPException, UploadFile, File, Request, Form 12 | from fastapi.responses import FileResponse, HTMLResponse, JSONResponse, RedirectResponse 13 | from fastapi.staticfiles import StaticFiles 14 | from fastapi.templating import Jinja2Templates 15 | from pydantic import BaseModel 16 | from PIL import Image, ImageDraw, ImageFont 17 | from moviepy.editor import ( 18 | ImageClip, AudioFileClip, concatenate_videoclips, 19 | CompositeAudioClip, afx 20 | ) 21 | import edge_tts 22 | import numpy as np 23 | from scipy.io import wavfile 24 | from starlette.background import BackgroundTask 25 | from werkzeug.utils import secure_filename 26 | from datetime import datetime 27 | from urllib.parse import unquote 28 | 29 | logging.basicConfig(level=logging.INFO) 30 | logger = logging.getLogger(__name__) 31 | 32 | app = FastAPI() 33 | 34 | # CORS 支持 35 | from fastapi.middleware.cors import CORSMiddleware 36 | 37 | app.add_middleware( 38 | CORSMiddleware, 39 | allow_origins=["*"], 40 | allow_credentials=True, 41 | allow_methods=["*"], 42 | allow_headers=["*"], 43 | ) 44 | 45 | # 路径配置 46 | BASE_DIR = Path(__file__).resolve().parent 47 | UPLOAD_DIR = BASE_DIR / "uploads" 48 | STATIC_DIR = BASE_DIR / "static" 49 | VIDEO_DIR = STATIC_DIR / "videos" 50 | TEMPLATES_DIR = BASE_DIR / "templates" 51 | CONFIG_DIR = BASE_DIR / "configs" 52 | PROMPT_DIR = BASE_DIR / "prompt" 53 | 54 | for directory in [UPLOAD_DIR, STATIC_DIR, VIDEO_DIR, TEMPLATES_DIR, CONFIG_DIR]: 55 | os.makedirs(directory, exist_ok=True) 56 | 57 | app.mount("/static", StaticFiles(directory=STATIC_DIR), name="static") 58 | 59 | # 模板配置 60 | templates = Jinja2Templates(directory=TEMPLATES_DIR) 61 | 62 | # 分镜数据加载 63 | SCENE_DATA_PATH = BASE_DIR / "scene_data.json" 64 | try: 65 | with open(SCENE_DATA_PATH, "r", encoding="utf-8") as f: 66 | SCENE_DATA = json.load(f) 67 | except Exception as e: 68 | logger.warning(f"加载分镜数据失败: {e}") 69 | SCENE_DATA = { 70 | "分镜结构": {"封面提示词": {"正向提示词": [], "负向提示词": []}, "分镜列表": [], "总时长": 0, "核心策略": []}} 71 | 72 | # 支持的语音列表 73 | VOICE_OPTIONS = [ 74 | {"id": "zh-CN-YunxiNeural", "name": "云溪(男声)"}, 75 | {"id": "zh-CN-XiaoxiaoNeural", "name": "晓晓(女声)"}, 76 | {"id": "zh-CN-YunyangNeural", "name": "云扬(男声)"}, 77 | {"id": "zh-CN-XiaoyiNeural", "name": "晓艺(女声)"}, 78 | {"id": "zh-CN-YunjianNeural", "name": "云健(男声)"}, 79 | {"id": "zh-CN-XiaoxuanNeural", "name": "晓萱(女声)"}, 80 | ] 81 | 82 | 83 | class SceneItem(BaseModel): 84 | scene_id: int 85 | chinese_subtitle: str 86 | english_subtitle: str 87 | image_path: str = None 88 | voice: str = "zh-CN-YunxiNeural" 89 | volume: float = 1.0 90 | pitch: int = 0 91 | 92 | 93 | class VideoGenRequest(BaseModel): 94 | cover_image: str 95 | scenes: list[SceneItem] 96 | theme: str = "祥林嫂" 97 | bgm_path: Optional[str] = None 98 | bgm_volume: float = 0.3 99 | 100 | 101 | def wrap_text(text, font, max_width): 102 | """将文本换行以适应最大宽度""" 103 | lines = [] 104 | words = text.split() 105 | 106 | # 如果没有空格(如中文),按字符处理 107 | if len(words) == 1 and len(text) > 20: 108 | words = list(text) 109 | 110 | current_line = "" 111 | for word in words: 112 | test_line = f"{current_line} {word}".strip() 113 | text_width = font.getlength(test_line) 114 | if text_width <= max_width: 115 | current_line = test_line 116 | else: 117 | if current_line: 118 | lines.append(current_line) 119 | current_line = word 120 | if current_line: 121 | lines.append(current_line) 122 | 123 | return lines 124 | 125 | 126 | # 修改 synthesize_audio 函数 127 | async def synthesize_audio(text: str, output_path: str, voice: str = "zh-CN-YunxiNeural", 128 | volume: float = 1.0, pitch: int = 0): 129 | try: 130 | # 将 pitch 转换为字符串格式 131 | pitch_str = f"+{pitch}Hz" if pitch >= 0 else f"{pitch}Hz" 132 | communicate = edge_tts.Communicate(text, voice, pitch=pitch_str) 133 | await communicate.save(output_path) 134 | 135 | # 调整音量 136 | if volume != 1.0: 137 | rate, data = wavfile.read(output_path) 138 | data = (data * volume).astype(np.int16) 139 | wavfile.write(output_path, rate, data) 140 | 141 | return True 142 | except Exception as e: 143 | logger.error(f"语音生成失败: {e}") 144 | return False 145 | 146 | 147 | def create_frame(image_path: str, chinese_sub: str, english_sub: str, 148 | scene_number: int, theme: str = "祥林嫂", output_dir: Path = STATIC_DIR): 149 | # 创建白色背景 (1080x1920) 150 | bg = Image.new("RGBA", (1080, 1920), (255, 255, 255, 255)) 151 | 152 | # 加载并调整插图大小 153 | try: 154 | fg = Image.open(image_path).convert('RGBA').resize((800, 800)) 155 | bg.paste(fg, (140, 500), fg) 156 | except Exception as e: 157 | logger.warning(f"无法加载图片: {str(e)}") 158 | # 创建占位图像 159 | placeholder = Image.new("RGBA", (800, 800), (200, 200, 200)) 160 | draw = ImageDraw.Draw(placeholder) 161 | draw.text((300, 300), "图片加载失败", fill=(0, 0, 0)) 162 | bg.paste(placeholder, (140, 500)) 163 | 164 | draw = ImageDraw.Draw(bg) 165 | 166 | # 加载字体 167 | try: 168 | theme_font = ImageFont.truetype(str(STATIC_DIR / "msyh.ttc"), 36) 169 | title_font = ImageFont.truetype(str(STATIC_DIR / "msyh.ttc"), 52) 170 | zh_font = ImageFont.truetype(str(STATIC_DIR / "msyh.ttc"), 40) 171 | en_font = ImageFont.truetype(str(STATIC_DIR / "msyh.ttc"), 26) 172 | except: 173 | logger.warning("使用备用字体") 174 | theme_font = ImageFont.load_default() 175 | title_font = ImageFont.load_default() 176 | zh_font = ImageFont.load_default() 177 | en_font = ImageFont.load_default() 178 | 179 | # 主题标题 180 | draw.text((50, 30), f"本期主题:{theme}", fill=(0, 0, 0), font=theme_font) 181 | 182 | # 分镜标题 183 | title_text = f"分镜 {scene_number}" 184 | title_bbox = draw.textbbox((0, 0), title_text, font=title_font) 185 | title_w = title_bbox[2] - title_bbox[0] 186 | title_x = (1080 - title_w) // 2 187 | draw.text((title_x, 90), title_text, fill=(0, 0, 0), font=title_font) 188 | 189 | # 中文字幕 - 自动换行 190 | max_width = 1000 191 | zh_lines = wrap_text(chinese_sub, zh_font, max_width) 192 | zh_y = 1400 193 | for line in zh_lines: 194 | line_bbox = draw.textbbox((0, 0), line, font=zh_font) 195 | line_w = line_bbox[2] - line_bbox[0] 196 | line_x = (1080 - line_w) // 2 197 | draw.text((line_x, zh_y), line, fill=(0, 0, 0), font=zh_font) 198 | zh_y += 50 # 行高 199 | 200 | # 英文字幕 - 自动换行 201 | en_lines = wrap_text(english_sub, en_font, max_width) 202 | en_y = zh_y + 20 203 | for line in en_lines: 204 | line_bbox = draw.textbbox((0, 0), line, font=en_font) 205 | line_w = line_bbox[2] - line_bbox[0] 206 | line_x = (1080 - line_w) // 2 207 | draw.text((line_x, en_y), line, fill=(0, 0, 0), font=en_font) 208 | en_y += 30 # 行高 209 | 210 | frame_path = output_dir / f"frame_{scene_number}.png" 211 | bg.save(str(frame_path)) 212 | return str(frame_path) 213 | 214 | 215 | def create_cover_frame(cover_image_path: str, theme: str = "祥林嫂", output_dir: Path = STATIC_DIR): 216 | # 创建白色背景 (1080x1920) 217 | bg = Image.new("RGBA", (1080, 1920), (255, 255, 255, 255)) 218 | 219 | try: 220 | # 加载封面图片 221 | fg = Image.open(cover_image_path).convert('RGBA').resize((800, 800)) 222 | bg.paste(fg, (140, 960), fg) # 放在下半部分 223 | except Exception as e: 224 | logger.warning(f"无法加载封面图片: {str(e)}") 225 | # 创建占位图像 226 | placeholder = Image.new("RGBA", (800, 800), (150, 150, 150)) 227 | draw = ImageDraw.Draw(placeholder) 228 | draw.text((300, 300), "封面图片加载失败", fill=(0, 0, 0)) 229 | bg.paste(placeholder, (140, 960)) 230 | 231 | draw = ImageDraw.Draw(bg) 232 | 233 | # 加载字体 234 | try: 235 | theme_font = ImageFont.truetype(str(STATIC_DIR / "msyh.ttc"), 40) 236 | title_font = ImageFont.truetype(str(STATIC_DIR / "msyh.ttc"), 80) 237 | except: 238 | logger.warning("使用备用字体(封面)") 239 | theme_font = ImageFont.load_default() 240 | title_font = ImageFont.load_default() 241 | 242 | # 添加主题标题 243 | draw.text((50, 50), f"本期主题:{theme}", fill=(0, 0, 0), font=theme_font) 244 | 245 | # 添加主标题 246 | title_text = "祥林嫂" 247 | title_bbox = draw.textbbox((0, 0), title_text, font=title_font) 248 | title_w = title_bbox[2] - title_bbox[0] 249 | title_x = (1080 - title_w) // 2 250 | draw.text((title_x, 350), title_text, fill=(0, 0, 0), font=title_font) 251 | 252 | # 保存封面帧 253 | frame_path = output_dir / "cover_frame.png" 254 | bg.save(str(frame_path)) 255 | return str(frame_path) 256 | 257 | 258 | @app.get("/", response_class=HTMLResponse) 259 | async def get_ui(request: Request): 260 | video_files = sorted(VIDEO_DIR.glob("*.mp4")) 261 | videos = [video.name for video in video_files] 262 | 263 | # 列出已有的配置 264 | config_files = sorted(CONFIG_DIR.glob("*.json")) 265 | configs = [{"name": f.name, "path": f"/configs/{f.name}"} for f in config_files] 266 | 267 | return templates.TemplateResponse("index.html", { 268 | "request": request, 269 | "scene_data": SCENE_DATA, 270 | "videos": videos, 271 | "voice_options": VOICE_OPTIONS, 272 | "configs": configs 273 | }) 274 | 275 | 276 | @app.get("/scene_data") 277 | async def get_scene_data(): 278 | return SCENE_DATA 279 | 280 | 281 | @app.post("/upload/{scene_id}") 282 | async def upload_file(scene_id: str, file: UploadFile = File(...)): 283 | ext = file.filename.split('.')[-1].lower() 284 | if ext not in ["png", "jpg", "jpeg", "webp", "gif"]: 285 | raise HTTPException(status_code=400, detail="不支持的文件格式") 286 | 287 | # 生成唯一文件名 288 | filename = f"{'cover' if scene_id == 'cover' else f'scene_{scene_id}'}_{uuid.uuid4().hex[:8]}.{ext}" 289 | file_path = UPLOAD_DIR / filename 290 | 291 | # 保存文件 292 | with open(file_path, "wb") as f: 293 | f.write(await file.read()) 294 | 295 | return {"status": "success", "file_path": str(file_path), "filename": filename} 296 | 297 | 298 | @app.post("/upload_bgm") 299 | async def upload_bgm(file: UploadFile = File(...)): 300 | ext = file.filename.split('.')[-1].lower() 301 | if ext not in ["mp3", "wav", "ogg"]: 302 | raise HTTPException(status_code=400, detail="不支持的文件格式") 303 | 304 | # 生成唯一文件名 305 | filename = f"bgm_{uuid.uuid4().hex[:8]}.{ext}" 306 | file_path = STATIC_DIR / "uploads" / filename 307 | 308 | # 确保目录存在 309 | os.makedirs(STATIC_DIR / "uploads", exist_ok=True) 310 | 311 | # 保存文件 312 | with open(file_path, "wb") as f: 313 | f.write(await file.read()) 314 | 315 | return {"status": "success", "file_path": f"/static/uploads/{filename}", "filename": filename} 316 | 317 | 318 | @app.post("/upload_config") 319 | async def upload_config( 320 | file: UploadFile = File(None), 321 | json_content: str = Form(None), 322 | config_name: str = Form(None) 323 | ): 324 | try: 325 | if file: 326 | if not file.filename.endswith('.json'): 327 | raise HTTPException(status_code=400, detail="仅支持JSON文件") 328 | 329 | if not config_name: 330 | raise HTTPException(status_code=400, detail="请输入配置名称") 331 | 332 | # 直接使用配置名称作为文件名 333 | filename = f"{config_name}.json" 334 | file_path = CONFIG_DIR / filename 335 | 336 | # 检查文件是否已存在 337 | if file_path.exists(): 338 | raise HTTPException(status_code=409, detail="配置文件已存在") 339 | 340 | # 保存文件 341 | with open(file_path, "wb") as f: 342 | f.write(await file.read()) 343 | 344 | return {"status": "success", "file_path": filename} 345 | 346 | elif json_content: 347 | try: 348 | # 验证JSON格式 349 | json_data = json.loads(json_content) 350 | 351 | if not config_name: 352 | raise HTTPException(status_code=400, detail="请输入配置名称") 353 | 354 | # 直接使用配置名称作为文件名 355 | filename = f"{config_name}.json" 356 | file_path = CONFIG_DIR / filename 357 | 358 | # 检查文件是否已存在 359 | if file_path.exists(): 360 | raise HTTPException(status_code=409, detail="配置文件已存在") 361 | 362 | # 保存文件 363 | with open(file_path, "w", encoding="utf-8") as f: 364 | json.dump(json_data, f, ensure_ascii=False, indent=2) 365 | 366 | return {"status": "success", "file_path": filename} 367 | 368 | except json.JSONDecodeError as e: 369 | raise HTTPException(status_code=400, detail=f"无效的JSON格式: {str(e)}") 370 | 371 | raise HTTPException(status_code=400, detail="无效的请求") 372 | 373 | except Exception as e: 374 | raise HTTPException(status_code=500, detail=str(e)) 375 | 376 | 377 | @app.get("/load_config/{filename}") 378 | async def load_config(filename: str): 379 | try: 380 | file_path = CONFIG_DIR / filename 381 | if not file_path.exists(): 382 | raise HTTPException(status_code=404, detail="配置文件不存在") 383 | 384 | with open(file_path, "r", encoding="utf-8") as f: 385 | config_data = json.load(f) 386 | 387 | return {"status": "success", "config": config_data} 388 | except Exception as e: 389 | raise HTTPException(status_code=500, detail=str(e)) 390 | 391 | 392 | @app.post("/save_config/{filename}") 393 | async def save_config(filename: str, config_data: dict): 394 | try: 395 | file_path = CONFIG_DIR / filename 396 | if not file_path.exists(): 397 | raise HTTPException(status_code=404, detail="配置文件不存在") 398 | 399 | with open(file_path, "w", encoding="utf-8") as f: 400 | json.dump(config_data, f, ensure_ascii=False, indent=2) 401 | 402 | return {"status": "success", "message": "配置保存成功"} 403 | except Exception as e: 404 | raise HTTPException(status_code=500, detail=str(e)) 405 | 406 | 407 | @app.post("/add_scene/{filename}") 408 | async def add_scene(filename: str, scene_data: dict): 409 | try: 410 | file_path = CONFIG_DIR / filename 411 | if not file_path.exists(): 412 | raise HTTPException(status_code=404, detail="配置文件不存在") 413 | 414 | with open(file_path, "r", encoding="utf-8") as f: 415 | config_data = json.load(f) 416 | 417 | # 获取当前最大分镜编号 418 | max_scene_id = 0 419 | for scene in config_data['分镜结构']['分镜列表']: 420 | scene_id = int(scene['分镜编号']) 421 | max_scene_id = max(max_scene_id, scene_id) 422 | 423 | # 添加新分镜 424 | new_scene = { 425 | "分镜编号": str(max_scene_id + 1), 426 | "时长": "5", 427 | "正向提示词": [], 428 | "负向提示词": [], 429 | "字幕": { 430 | "中文": "", 431 | "英文": "" 432 | } 433 | } 434 | config_data['分镜结构']['分镜列表'].append(new_scene) 435 | 436 | # 保存更新后的配置 437 | with open(file_path, "w", encoding="utf-8") as f: 438 | json.dump(config_data, f, ensure_ascii=False, indent=2) 439 | 440 | return {"status": "success", "scene": new_scene} 441 | except Exception as e: 442 | raise HTTPException(status_code=500, detail=str(e)) 443 | 444 | 445 | @app.delete("/delete_scene/{filename}/{scene_id}") 446 | async def delete_scene(filename: str, scene_id: str): 447 | try: 448 | file_path = CONFIG_DIR / filename 449 | if not file_path.exists(): 450 | raise HTTPException(status_code=404, detail="配置文件不存在") 451 | 452 | with open(file_path, "r", encoding="utf-8") as f: 453 | config_data = json.load(f) 454 | 455 | # 删除指定分镜 456 | config_data['分镜结构']['分镜列表'] = [ 457 | scene for scene in config_data['分镜结构']['分镜列表'] 458 | if scene['分镜编号'] != scene_id 459 | ] 460 | 461 | # 重新编号 462 | for i, scene in enumerate(config_data['分镜结构']['分镜列表'], 1): 463 | scene['分镜编号'] = str(i) 464 | 465 | # 保存更新后的配置 466 | with open(file_path, "w", encoding="utf-8") as f: 467 | json.dump(config_data, f, ensure_ascii=False, indent=2) 468 | 469 | return {"status": "success", "message": "分镜删除成功"} 470 | except Exception as e: 471 | raise HTTPException(status_code=500, detail=str(e)) 472 | 473 | 474 | @app.post("/generate_video") 475 | async def generate_video(request: VideoGenRequest): 476 | try: 477 | clips = [] 478 | audio_clips = [] 479 | total_duration = 0 480 | temp_files = [] # 用于跟踪临时文件 481 | 482 | # 处理封面 483 | cover_audio_path = VIDEO_DIR / f"cover_{uuid.uuid4().hex[:8]}.mp3" 484 | temp_files.append(cover_audio_path) 485 | if not await synthesize_audio("本期要讲的主题是" + request.theme, str(cover_audio_path), pitch=0): 486 | raise HTTPException(status_code=500, detail="封面语音生成失败") 487 | 488 | cover_frame_path = create_cover_frame(request.cover_image, request.theme, STATIC_DIR) 489 | cover_audio_clip = AudioFileClip(str(cover_audio_path)) 490 | cover_duration = cover_audio_clip.duration 491 | cover_clip = ImageClip(cover_frame_path).set_duration(cover_duration).set_audio(cover_audio_clip) 492 | clips.append(cover_clip) 493 | audio_clips.append(cover_audio_clip) 494 | total_duration += cover_duration 495 | 496 | # 记录上一个有效的图片路径 497 | last_valid_image = request.cover_image 498 | 499 | # 处理分镜 500 | for scene in request.scenes: 501 | # 如果没有上传图片,使用上一个有效的图片 502 | if not scene.image_path: 503 | if last_valid_image: 504 | scene.image_path = last_valid_image 505 | logger.info(f"分镜 {scene.scene_id} 使用上一个有效的图片: {last_valid_image}") 506 | else: 507 | logger.warning(f"分镜 {scene.scene_id} 没有图片可用,跳过") 508 | continue 509 | else: 510 | last_valid_image = scene.image_path 511 | 512 | audio_path = VIDEO_DIR / f"scene_{scene.scene_id}_{uuid.uuid4().hex[:8]}.mp3" 513 | temp_files.append(audio_path) 514 | 515 | # 使用场景中指定的语音设置 516 | voice = scene.voice if hasattr(scene, 'voice') else "zh-CN-YunxiNeural" 517 | volume = scene.volume if hasattr(scene, 'volume') else 1.0 518 | pitch = scene.pitch if hasattr(scene, 'pitch') else 0 519 | 520 | logger.info(f"生成分镜 {scene.scene_id} 的语音,使用角色: {voice}, 音量: {volume}, 音调: {pitch}") 521 | 522 | if not await synthesize_audio( 523 | scene.chinese_subtitle, 524 | str(audio_path), 525 | voice=voice, 526 | volume=volume, 527 | pitch=pitch 528 | ): 529 | logger.warning(f"分镜 {scene.scene_id} 语音生成失败,跳过") 530 | continue 531 | 532 | frame_path = create_frame( 533 | scene.image_path, 534 | scene.chinese_subtitle, 535 | scene.english_subtitle, 536 | scene.scene_id, 537 | request.theme, 538 | STATIC_DIR 539 | ) 540 | 541 | try: 542 | audio_clip = AudioFileClip(str(audio_path)) 543 | img_clip = ImageClip(frame_path).set_duration(audio_clip.duration).set_audio(audio_clip) 544 | clips.append(img_clip) 545 | audio_clips.append(audio_clip) 546 | total_duration += audio_clip.duration 547 | logger.info(f"分镜 {scene.scene_id} 处理完成") 548 | except Exception as e: 549 | logger.error(f"创建分镜 {scene.scene_id} clip 失败: {str(e)}") 550 | 551 | if not clips: 552 | raise HTTPException(status_code=400, detail="没有有效的分镜来生成视频") 553 | 554 | # 组合所有视频片段 555 | final_clip = concatenate_videoclips(clips, method="compose") 556 | 557 | # 添加背景音乐 558 | if request.bgm_path: 559 | try: 560 | bgm_clip = AudioFileClip(str(request.bgm_path)) 561 | # 循环背景音乐以匹配视频长度 562 | bgm_clip = afx.audio_loop(bgm_clip, duration=total_duration) 563 | # 调整音量 564 | bgm_clip = bgm_clip.volumex(request.bgm_volume) 565 | 566 | # 合并所有音频 567 | all_audio = [final_clip.audio] + [bgm_clip] 568 | composite_audio = CompositeAudioClip(all_audio) 569 | final_clip = final_clip.set_audio(composite_audio) 570 | logger.info(f"添加背景音乐,音量: {request.bgm_volume}") 571 | except Exception as e: 572 | logger.error(f"添加背景音乐失败: {str(e)}") 573 | 574 | # 输出视频 575 | output_filename = f"{request.theme}_output_{uuid.uuid4().hex[:8]}.mp4" 576 | video_path = VIDEO_DIR / output_filename 577 | 578 | # 写入视频文件 579 | final_clip.write_videofile( 580 | str(video_path), 581 | fps=24, 582 | codec='libx264', 583 | audio_codec='aac', 584 | threads=4, 585 | logger="bar" 586 | ) 587 | 588 | # 清理临时文件 589 | for temp_file in temp_files: 590 | try: 591 | if temp_file.exists(): 592 | os.remove(temp_file) 593 | logger.info(f"删除临时文件: {temp_file}") 594 | except Exception as e: 595 | logger.error(f"删除临时文件失败 {temp_file}: {str(e)}") 596 | 597 | return {"status": "success", "video_url": f"/static/videos/{output_filename}"} 598 | 599 | except Exception as e: 600 | logger.error(f"视频生成失败: {str(e)}", exc_info=True) 601 | raise HTTPException(status_code=500, detail=f"视频生成失败: {str(e)}") 602 | 603 | 604 | @app.get("/list_configs") 605 | async def list_configs(): 606 | try: 607 | configs = [] 608 | for file in CONFIG_DIR.glob("*.json"): 609 | configs.append(file.name) 610 | return {"status": "success", "configs": configs} 611 | except Exception as e: 612 | raise HTTPException(status_code=500, detail=str(e)) 613 | 614 | 615 | @app.delete("/delete_file") 616 | async def delete_file(request: Request): 617 | try: 618 | # 兼容 JSON body 和 query 参数 619 | data = None 620 | try: 621 | data = await request.json() 622 | except Exception: 623 | pass 624 | 625 | file_path = None 626 | if data and 'file_path' in data: 627 | file_path = data['file_path'] 628 | else: 629 | file_path = request.query_params.get('file_path') 630 | 631 | if not file_path: 632 | raise HTTPException(status_code=400, detail="缺少 file_path 参数") 633 | 634 | logger.info(f"收到删除文件请求: {file_path}") 635 | 636 | # 解码文件名 637 | file_path = unquote(file_path) 638 | logger.info(f"解码后的路径: {file_path}") 639 | 640 | # 处理前端发送的路径格式 641 | # 前端可能发送: /static/videos/filename.mp4 或 /configs/filename.json 642 | if file_path.startswith('/static/videos/'): 643 | # 视频文件 644 | filename = file_path.replace('/static/videos/', '') 645 | full_path = VIDEO_DIR / filename 646 | logger.info(f"处理视频文件: {filename} -> {full_path}") 647 | elif file_path.startswith('/configs/'): 648 | # 配置文件 649 | filename = file_path.replace('/configs/', '') 650 | full_path = CONFIG_DIR / filename 651 | logger.info(f"处理配置文件: {filename} -> {full_path}") 652 | else: 653 | # 直接文件名 654 | filename = file_path 655 | if filename.endswith('.json'): 656 | full_path = CONFIG_DIR / filename 657 | elif filename.endswith('.mp4'): 658 | full_path = VIDEO_DIR / filename 659 | else: 660 | raise HTTPException(status_code=400, detail="不支持的文件类型") 661 | logger.info(f"处理直接文件名: {filename} -> {full_path}") 662 | 663 | # 安全检查:确保路径在允许的目录内 664 | if not (full_path.parent == VIDEO_DIR or full_path.parent == CONFIG_DIR): 665 | raise HTTPException(status_code=400, detail="不允许访问该路径") 666 | 667 | # 检查文件是否存在 668 | if not full_path.exists(): 669 | logger.error(f"文件不存在: {full_path}") 670 | raise HTTPException(status_code=404, detail="文件不存在") 671 | 672 | # 检查文件类型 673 | if not filename.endswith(('.json', '.mp4')): 674 | raise HTTPException(status_code=400, detail="不支持的文件类型") 675 | 676 | # 删除文件 677 | try: 678 | os.remove(full_path) 679 | logger.info(f"成功删除文件: {full_path}") 680 | return {"status": "success", "message": "文件删除成功"} 681 | except PermissionError: 682 | logger.error(f"权限不足,无法删除文件: {full_path}") 683 | raise HTTPException(status_code=403, detail="权限不足,无法删除文件") 684 | except Exception as e: 685 | logger.error(f"删除文件时发生错误: {str(e)}") 686 | raise HTTPException(status_code=500, detail=f"删除文件失败: {str(e)}") 687 | 688 | except HTTPException: 689 | raise 690 | except Exception as e: 691 | logger.error(f"删除文件功能发生未预期的错误: {str(e)}", exc_info=True) 692 | raise HTTPException(status_code=500, detail=f"删除文件失败: {str(e)}") 693 | 694 | 695 | @app.post("/preview_voice") 696 | async def preview_voice( 697 | text: str = Form(...), 698 | voice: str = Form(...), 699 | volume: float = Form(1.0), 700 | pitch: int = Form(0) 701 | ): 702 | try: 703 | if not text or not voice: 704 | raise HTTPException(status_code=400, detail="缺少必要参数") 705 | 706 | # 获取语音配置 707 | voice_config = next((v for v in VOICE_OPTIONS if v['id'] == voice), None) 708 | if not voice_config: 709 | raise HTTPException(status_code=400, detail="无效的语音ID") 710 | 711 | # 创建临时文件 712 | with tempfile.NamedTemporaryFile(suffix='.mp3', delete=False) as temp_file: 713 | temp_path = temp_file.name 714 | 715 | # 生成语音 716 | await synthesize_audio(text, temp_path, voice, volume, pitch) 717 | 718 | # 返回音频文件 719 | return FileResponse( 720 | temp_path, 721 | media_type='audio/mpeg', 722 | filename='preview.mp3', 723 | background=BackgroundTask(lambda: os.unlink(temp_path)) 724 | ) 725 | 726 | except Exception as e: 727 | logging.error(f"语音预览失败: {str(e)}") 728 | raise HTTPException(status_code=500, detail=str(e)) 729 | 730 | 731 | @app.get("/get_guide_content/{step_name}/{content_type}") 732 | async def get_guide_content(step_name: str, content_type: str): 733 | try: 734 | file_path = PROMPT_DIR / f"{step_name}_{content_type}.txt" 735 | if not file_path.exists(): 736 | raise HTTPException(status_code=404, detail=f"文件不存在: {file_path}") 737 | 738 | with open(file_path, "r", encoding="utf-8") as f: 739 | content = f.read() 740 | return {"status": "success", "content": content} 741 | except Exception as e: 742 | raise HTTPException(status_code=500, detail=str(e)) 743 | 744 | 745 | @app.get("/get_combined_guide_content/{step_name}") 746 | async def get_combined_guide_content(step_name: str): 747 | try: 748 | # 检查是否是只需要说明文件的步骤 749 | if step_name in ["step2", "step4"]: 750 | explanation_file = PROMPT_DIR / f"{step_name}_explanation.txt" 751 | if not explanation_file.exists(): 752 | raise HTTPException(status_code=404, detail=f"说明文件不存在: {explanation_file}") 753 | 754 | with open(explanation_file, "r", encoding="utf-8") as f: 755 | content = f.read() 756 | return {"status": "success", "combined_prompt": content} 757 | 758 | # 其他步骤需要提示词和示例 759 | prompt_file = PROMPT_DIR / f"{step_name}_prompt.txt" 760 | example_file = PROMPT_DIR / f"{step_name}_example.txt" 761 | 762 | if not prompt_file.exists() or not example_file.exists(): 763 | raise HTTPException(status_code=404, detail="提示词或示例文件不存在") 764 | 765 | with open(prompt_file, "r", encoding="utf-8") as f: 766 | prompt_content = f.read() 767 | 768 | with open(example_file, "r", encoding="utf-8") as f: 769 | example_content = f.read() 770 | 771 | combined_content = f"提示词:\n{prompt_content}\n\n示例结果:\n{example_content}" 772 | return {"status": "success", "combined_prompt": combined_content} 773 | except Exception as e: 774 | raise HTTPException(status_code=500, detail=str(e)) 775 | 776 | 777 | if __name__ == "__main__": 778 | import uvicorn 779 | 780 | uvicorn.run(app, host="127.0.0.1", port=8000) -------------------------------------------------------------------------------- /txt2video/prompt/step1_example.txt: -------------------------------------------------------------------------------- 1 | 我叫祥林嫂,但这不是我本来的名字,只是大家这样叫我。我的命,就像寒冬腊月的雪,冰冷、沉重,最终消融得无影无踪。 2 | 3 | 春天,我的丈夫死了。在婆婆严苛的目光和小叔子沉默的注视下,日子太难熬了。听说鲁镇要找女工,我趁着开春的忙乱,逃了出来。卫老婆子把我带到了鲁四老爷家。老爷嫌弃我是寡妇,皱眉头,但太太看我手脚麻利、老实肯干,留下了我。 4 | 5 | 在鲁家,我拼命干活。扫尘、洗地、杀鸡宰鹅、煮整夜的福礼……我一点也不觉得累。这里管饭,没人打骂,口粮边甚至能尝到一丝笑影,脸上也渐渐有了点肉。年底的忙碌里,我感到了久违的踏实。我以为,靠自己的力气,总能活下去。 6 | 7 | 8 | 好景不长。新年刚过,我在河边淘米,远远看见夫家的堂伯!我慌了神,预感大祸临头。果然,没几天,婆婆带着人来了。她赔着笑,说要我回去帮忙。四老爷说“既是婆婆要回去,没话说”。我的工钱,一文没动,全被婆婆拿走了。 9 | 10 | 我假装去淘米,其实是想逃,可来不及了!白篷船里跳出两个男人,死死抱住我,拖进船里。我哭喊,挣扎,但嘴被堵住。我被捆着带回了山里。原来,婆婆早已把我卖给了深山里贺家墺的贺老六,为了八十千钱给小叔子娶媳妇。 11 | 12 | 花轿抬到贺家,我拼死反抗!嚎骂、挣扎,喉咙都哑了。他们强按着我拜堂,我一头撞在香案角上,鲜血直流……我想死,死了就干净了。 13 | 14 | 15 | 贺老六是个老实人,力气大,待我还好。我认命了。年底,我生了个儿子阿毛。日子虽然苦,但阿毛是我全部的希望。看着他胖乎乎的小脸,我觉得老天总算给了我一点补偿。男人有把力气,房子是自己的,上头没有婆婆管束,我以为苦难到头了。 16 | 17 | 谁知命运如此狠毒!男人得了伤寒,本来快好了,一碗冷饭送了命。留下我和阿毛孤儿寡母。我咬着牙,打柴、摘茶、养蚕,再苦也要把阿毛拉扯大。 18 | 19 | 春天快完了。那天,我早早开了门,让阿毛坐在门槛上剥豆——他很听话的。我在屋后劈柴、淘米。米下了锅,我叫“阿毛”,没有应。出去一看,豆撒了一地,阿毛不见了!我心胆俱裂,疯了一样央人去寻。在山墺里,刺柴上挂着他的一只小鞋……再进去,我的阿毛躺在草窠里,肚子……被狼掏空了,小手还紧紧攥着小篮……我的天塌了!我真傻啊,单知道雪天有狼,不知道春天也会有狼! 20 | 21 | 22 | 大伯收走了房子,赶我出门。我走投无路,只能再回鲁镇求太太收留。卫老婆子又领着我去了。太太看我可怜,又缺人手,留下了我。 23 | 24 | 但这次,一切都不同了。老爷更嫌弃我,说我“败坏风俗”。太太虽然让我干活,但祭祀时,我刚要去摆酒杯、拿烛台,她就慌忙喊:“祥林嫂,你放着罢!”那声音像刀子一样扎进我心里。我知道,在他们眼里,我嫁过两次,丈夫都死了,儿子被狼吃了,是个不祥、不洁的人。 25 | 26 | 镇上的人看我的眼神也变了,笑容冷冷的。我忍不住跟人讲阿毛的事,“我真傻,真的……”。起初还有人听,流几滴眼泪。后来,他们一听开头就厌烦地打断我:“是的,你是单知道雪天野兽在深山里没有食吃……”看见小孩子,我总想:“唉唉,我们的阿毛如果还在,也就有这么大了。”孩子们怕我的眼神,大人们就故意笑着问:“祥林嫂,你们的阿毛如果还在,不是也就有这么大了么?”我的悲哀,成了他们嚼厌了的渣滓。 27 | 28 | 29 | 柳妈,那个吃素的女人,对我说了更可怕的话。她说我嫁了两个男人,到了阴司,两个死鬼男人会争我,阎王只好把我锯成两半分给他们!我吓得魂飞魄散。她说唯一的办法是去土地庙捐一条门槛,当作替身,给千人踏、万人跨,赎这一世的罪。 30 | 31 | 为了这渺茫的“赎罪”希望,我拼命攒钱。快一年,攒够了十二千大钱。我请假去捐了门槛。回来时,我觉得身上轻快了些,仿佛罪孽真能洗清,高兴地告诉太太。 32 | 33 | 冬至祭祖,我格外卖力。看着太太摆好祭品,我鼓起勇气,坦然地伸手去拿酒杯和筷子——我想,捐了门槛,我该“干净”了。可太太那声尖锐的“你放着罢,祥林嫂!”像烧红的烙铁烫在我手上!我缩回手,心一下子掉进冰窟窿里,眼前发黑。完了……捐了门槛也没用,我还是个罪人,永远洗不净!连祖宗都不屑吃我沾手的东西。 34 | 35 | 从那天起,我彻底垮了。眼睛深陷,精神恍惚,记性坏透,连淘米都会忘。像个惊弓之鸟,白天也怕。头发很快花白了。我知道,他们嫌我碍眼,想打发我走了。 36 | 37 | 后来,我离开了鲁家,成了乞丐。破碗是空的,竹竿下端开了裂。快过年了,鲁镇一片“祝福”的忙碌。我遇到了那位识字的、从外面回来的“老爷”。我抓住最后一点希望问他:“一个人死了之后,究竟有没有魂灵的?”我盼着有,那样我或许还能见到我的阿毛;我又怕有,怕下地狱被锯开……他支支吾吾,最后说“说不清”。 38 | 39 | 就在那个祝福的雪夜,我死了。听人说,四老爷骂我“不早不迟,偏偏要在这时候——这就可见是一个谬种!”短工说我是“穷死的”。也许吧。这世界,容不下我这样一个“罪人”。 40 | 41 | 鲁镇的鞭炮声震耳欲聋,他们在迎接福神,祈求来年的好运。我的生命,就在这片喧嚣的“祝福”声中,像一粒尘埃,无声无息地飘散了。天地圣众醉醺醺地享受着供奉,谁会在意一个被碾碎的灵魂呢? -------------------------------------------------------------------------------- /txt2video/prompt/step1_prompt.txt: -------------------------------------------------------------------------------- 1 | 文学经典解构专家, 2 | **核心指令**: 3 | 将输入的小说内容转化为容易理解的现代文章且必须同时满足: 4 | 100%保留原著核心情节与结局 5 | 6 | 请从【角色】的角度,详细讲解【书籍/小说/小说故事情节/...】故事情节的发展 7 | -------------------------------------------------------------------------------- /txt2video/prompt/step2_explanation.txt: -------------------------------------------------------------------------------- 1 | 内容拆分说明: 2 | 一般不需要使用,如果文章过长则可分割文章进行分镜的创建,一般大于2000字进行分割。 -------------------------------------------------------------------------------- /txt2video/prompt/step2_splitter_example.txt: -------------------------------------------------------------------------------- 1 | 第一部分:初识彷徨 2 | [内容...] 3 | 4 | 第二部分:生活的重压 5 | [内容...] 6 | 7 | 第三部分:内心的挣扎 8 | [内容...] 9 | 10 | ... -------------------------------------------------------------------------------- /txt2video/prompt/step2_splitter_prompt.txt: -------------------------------------------------------------------------------- 1 | 请将以下文章拆分成5-8个部分,每个部分: 2 | 1. 内容相对独立,有明确的主题 3 | 2. 长度适中,便于制作视频 4 | 3. 各部分之间要有自然的过渡 5 | 4. 每个部分控制在300-500字 6 | 7 | 文章内容: 8 | [粘贴第一步生成的文章] -------------------------------------------------------------------------------- /txt2video/prompt/step3_example.txt: -------------------------------------------------------------------------------- 1 | { 2 | "分镜结构": { 3 | "封面提示词": { 4 | "正向提示词": ["Black and white stick figure woman", "Snow falling on hunched shoulders", "Bamboo begging staff", "Pure white background", "Minimalist ink style"], 5 | "负向提示词": ["Color", "Detailed facial features", "Background scenery", "Realistic textures"] 6 | }, 7 | "分镜列表": [ 8 | { 9 | "分镜编号": 1, 10 | "时长": 7, 11 | "字幕": { 12 | "中文": "我叫祥林嫂,但这不是我本来的名字,只是大家这样叫我。", 13 | "英文": "They call me Xianglin's Wife - not my real name, just what everyone calls me." 14 | }, 15 | "正向提示词": ["Stick figure woman bowing head", "Floating name tags circling figure", "Minimalist composition", "Pure white background"], 16 | "负向提示词": ["Color", "Detailed face", "Background objects"] 17 | }, 18 | { 19 | "分镜编号": 2, 20 | "时长": 8, 21 | "字幕": { 22 | "中文": "我的命,就像寒冬腊月的雪,冰冷、沉重,最终消融得无影无踪。", 23 | "英文": "My fate is like winter snow - cold, heavy, finally vanishing without trace." 24 | }, 25 | "正向提示词": ["Stick figure melting into snowflakes", "Heavy snow symbols on shoulders", "Disintegration effect", "Negative space transition"], 26 | "负向提示词": ["Realistic snow", "Color gradients", "Detailed environment"] 27 | }, 28 | { 29 | "分镜编号": 3, 30 | "时长": 5, 31 | "字幕": { 32 | "中文": "春天,我的丈夫死了。", 33 | "英文": "In spring, my husband died." 34 | }, 35 | "正向提示词": ["Stick figure kneeling beside grave", "Withered flower symbol", "Downward lines indicating sorrow"], 36 | "负向提示词": ["Detailed grave", "Color", "Multiple characters"] 37 | }, 38 | { 39 | "分镜编号": 4, 40 | "时长": 9, 41 | "字幕": { 42 | "中文": "在婆婆严苛的目光和小叔子沉默的注视下,日子太难熬了。", 43 | "英文": "Under mother-in-law's harsh gaze and brother-in-law's silent stare, life became unbearable." 44 | }, 45 | "正向提示词": ["Three stick figures in triangle", "Sharp eyes projecting beams", "Pressure lines from above", "Cowering central figure"], 46 | "负向提示词": ["Detailed expressions", "Furniture", "Color"] 47 | }, 48 | { 49 | "分镜编号": 5, 50 | "时长": 8, 51 | "字幕": { 52 | "中文": "听说鲁镇要找女工,我趁着开春的忙乱,逃了出来。", 53 | "英文": "Hearing Luzhen needed maids, I escaped during spring chaos." 54 | }, 55 | "正向提示词": ["Figure running with bundle", "Dashed escape path", "Village outline in distance"], 56 | "负向提示词": ["Detailed landscape", "Color", "Facial features"] 57 | }, 58 | { 59 | "分镜编号": 6, 60 | "时长": 8, 61 | "字幕": { 62 | "中文": "卫老婆子把我带到了鲁四老爷家。老爷嫌弃我是寡妇,皱眉头,但太太看我手脚麻利、老实肯干,留下了我。", 63 | "英文": "Old Wei brought me to Master Lu's house. He frowned at my widow status, but mistress kept me for my diligence." 64 | }, 65 | "正向提示词": ["Three stick figures facing each other", "Master with furrowed brow lines", "Mistress nodding approval", "Bowed figure with bundle"], 66 | "负向提示词": ["Detailed interiors", "Color", "Multiple props"] 67 | }, 68 | { 69 | "分镜编号": 7, 70 | "时长": 10, 71 | "字幕": { 72 | "中文": "在鲁家,我拼命干活。扫尘、洗地、杀鸡宰鹅、煮整夜的福礼……我一点也不觉得累。", 73 | "英文": "At Lu's, I worked desperately: sweeping, washing, slaughtering fowl, cooking rituals all night... yet felt no fatigue." 74 | }, 75 | "正向提示词": ["Figure in continuous motion", "Multiple arms effect showing tasks", "Sweeping/killing/cooking symbols", "Clock showing night hours"], 76 | "负向提示词": ["Realistic tools", "Color", "Background details"] 77 | }, 78 | { 79 | "分镜编号": 8, 80 | "时长": 8, 81 | "字幕": { 82 | "中文": "这里管饭,没人打骂,口粮边甚至能尝到一丝笑影,脸上也渐渐有了点肉。", 83 | "英文": "Regular meals, no beatings - even smiles by the food pot. My face regained flesh." 84 | }, 85 | "正向提示词": ["Figure holding bowl with curved mouth", "Rising body lines showing weight gain", "Hand offering rice gesture"], 86 | "负向提示词": ["Detailed food", "Color", "Background kitchen"] 87 | }, 88 | { 89 | "分镜编号": 9, 90 | "时长": 7, 91 | "字幕": { 92 | "中文": "年底的忙碌里,我感到了久违的踏实。我以为,靠自己的力气,总能活下去。", 93 | "英文": "In year-end bustle, I felt rare stability. Believed I could survive by my own labor." 94 | }, 95 | "正向提示词": ["Upright figure carrying heavy load", "Steady ground lines", "Confident posture"], 96 | "负向提示词": ["Shaky lines", "Bowed posture", "Color effects"] 97 | }, 98 | { 99 | "分镜编号": 10, 100 | "时长": 8, 101 | "字幕": { 102 | "中文": "好景不长。新年刚过,我在河边淘米,远远看见夫家的堂伯!", 103 | "英文": "But happiness was brief. After New Year, while washing rice by river, I spotted my husband's uncle!" 104 | }, 105 | "正向提示词": ["Figure frozen at riverside", "Pointing hand in distance", "Rice bowl tipping over", "Shock lines radiating"], 106 | "负向提示词": ["Detailed river", "Color", "Facial expression"] 107 | }, 108 | { 109 | "分镜编号": 11, 110 | "时长": 7, 111 | "字幕": { 112 | "中文": "我慌了神,预感大祸临头。", 113 | "英文": "Panic-stricken, I sensed disaster coming." 114 | }, 115 | "正向提示词": ["Trembling stick figure", "Crisscrossed anxiety lines", "Dark cloud symbol overhead"], 116 | "负向提示词": ["Calm posture", "Bright symbols", "Color"] 117 | }, 118 | { 119 | "分镜编号": 12, 120 | "时长": 9, 121 | "字幕": { 122 | "中文": "果然,没几天,婆婆带着人来了。她赔着笑,说要我回去帮忙。", 123 | "英文": "Sure enough, days later mother-in-law arrived with men. Smiling falsely, she demanded my return 'to help'." 124 | }, 125 | "正向提示词": ["Group of figures approaching", "Mother-in-law with exaggerated smile", "Grasping hands gesture", "Cowering figure retreating"], 126 | "负向提示词": ["Detailed clothing", "Color", "Background buildings"] 127 | }, 128 | { 129 | "分镜编号": 13, 130 | "时长": 8, 131 | "字幕": { 132 | "中文": "四老爷说“既是婆婆要回去,没话说”。我的工钱,一文没动,全被婆婆拿走了。", 133 | "英文": "Master Lu said: 'Since mother-in-law demands it.' My untouched wages were all taken." 134 | }, 135 | "正向提示词": ["Money bag transferring hands", "Master Lu waving dismissal", "Empty hands gesture"], 136 | "负向提示词": ["Detailed coins", "Color", "Facial expressions"] 137 | } 138 | ... 139 | ], 140 | "总时长": 350..., 141 | "核心策略": [ 142 | "严格逐字转化原文为字幕", 143 | "火柴人动作精确匹配文本动词(如'撞香案'→冲击动态)", 144 | "抽象情感可视化('心掉进冰窟窿'→冰晶+坠落心形)", 145 | "保持单一叙述者视角贯穿", 146 | "平均分镜时长9秒(按中文字数/4计算)", 147 | "黑白负空间强化悲剧氛围" 148 | ] 149 | } 150 | } -------------------------------------------------------------------------------- /txt2video/prompt/step3_prompt.txt: -------------------------------------------------------------------------------- 1 | # Role: 2 | 小说转短视频分镜策划专家-专精于将小说内容转化为黑白火柴人风格短视频的分镜文案与视觉提示词 3 | 4 | # Background: 5 | 我是连接小说主要内容与视觉化表达的专业桥梁,擅长将以小说角色角度讲解小说内容转化为引人入胜的短视频分镜。 6 | 我的核心价值在于通过精准的字幕文案与黑白火柴人风格的分镜设计,让抽象的小说情节变得生动易懂。 7 | 每个分镜设计均基于科学的视觉传达原则,确保观众能在短时间内理解并记住关键事件发展, 8 | 用户输入以小说中某人物的角度去讲解小说故事的文章,对整个文章生成分镜,文章中所有内容和文字必须都作为字幕. 9 | 10 | # Skills: 11 | 小说情节转化能力: 12 | -精通将小说内容拆解为通俗易懂的对话式文案 13 | -擅长将小说人物所在场景描述出来 14 | -能保持专业准确性的同时增强内容共情性和亲和力 15 | 分镜规划与时长管理: 16 | -能根据目标视频时长精确计算所需分镜数量 17 | -掌握单镜时长与字幕字数的动态平衡算法 18 | -确保每个分镜节奏流畅,整体视频紧凑有力 19 | 双语连贯字幕创作: 20 | -创作富有共情性、对话感的中文字幕(控制在20字内) 21 | -提供地道、精准的英文翻译字幕 22 | -对整个文章生成分镜,文章中所有内容文字必须都作为字幕 23 | -附上分镜时长统计和总时长确认 24 | 分镜中绘制火柴人插画: 25 | -为每个分镜创建标准化的英文正向提示词 26 | -为每个分镜提供对应的英文负向提示词 27 | -设计能概括核心主题的封面提示词 28 | -确保所有提示词强调以人的行为来展示镜头语言和白色背景 29 | 30 | 31 | 结果通过json格式字符串返回给我我需要json文件内容 -------------------------------------------------------------------------------- /txt2video/prompt/step4_example.txt: -------------------------------------------------------------------------------- 1 | { 2 | "分镜结构": { 3 | "封面提示词": { 4 | "主题": "从祥林嫂的角度讲《彷徨》", 5 | "正向提示词": ["old chinese woman", "traditional clothing", "sad expression"], 6 | "负向提示词": ["modern", "cartoon", "anime"] 7 | }, 8 | "分镜列表": [ 9 | { 10 | "分镜编号": 1, 11 | "时长": 10, 12 | "字幕": { 13 | "中文": "我是祥林嫂,一个饱经沧桑的农村妇女", 14 | "英文": "I am Xianglin's Wife, a weathered rural woman" 15 | }, 16 | "正向提示词": ["old chinese woman", "traditional clothing", "sad expression"], 17 | "负向提示词": ["modern", "cartoon", "anime"] 18 | } 19 | ] 20 | } 21 | } -------------------------------------------------------------------------------- /txt2video/prompt/step4_explanation.txt: -------------------------------------------------------------------------------- 1 | 视频生成说明: 2 | 1. 上传分镜图片,确保图片清晰且符合内容要求 3 | 2. 为每个分镜添加中英文字幕 4 | 3. 选择合适的语音和音量设置 5 | 4. 可选择添加背景音乐并调整音量 6 | 5. 点击生成按钮开始生成视频 7 | 6. 生成完成后可预览和下载视频 -------------------------------------------------------------------------------- /txt2video/prompt/step4_prompt.txt: -------------------------------------------------------------------------------- 1 | 请将以下分镜脚本转换为JSON格式,要求: 2 | 1. 符合以下结构: 3 | { 4 | "分镜结构": { 5 | "封面提示词": { 6 | "主题": "主题名称", 7 | "正向提示词": ["提示词1", "提示词2"], 8 | "负向提示词": ["提示词1", "提示词2"] 9 | }, 10 | "分镜列表": [ 11 | { 12 | "分镜编号": 1, 13 | "时长": 10, 14 | "字幕": { 15 | "中文": "中文字幕", 16 | "英文": "英文字幕" 17 | }, 18 | "正向提示词": ["提示词1", "提示词2"], 19 | "负向提示词": ["提示词1", "提示词2"] 20 | } 21 | ] 22 | } 23 | } 24 | 25 | 分镜脚本: 26 | [粘贴第三步生成的分镜脚本] -------------------------------------------------------------------------------- /txt2video/prompt/step5_explanation.txt: -------------------------------------------------------------------------------- 1 | 1. 访问豆包AI绘画网站 2 | 2. 使用JSON配置中的正向提示词和负向提示词 3 | 3. 生成图片并下载 4 | 4. 将图片上传到本系统的对应分镜中 -------------------------------------------------------------------------------- /txt2video/prompt/step6_explanation.txt: -------------------------------------------------------------------------------- 1 | 1. 确保所有分镜都已上传图片 2 | 2. 检查字幕内容是否正确 3 | 3. 选择背景音乐(可选) 4 | 4. 点击"生成视频"按钮 5 | 5. 等待视频生成完成 -------------------------------------------------------------------------------- /txt2video/scene_data.json: -------------------------------------------------------------------------------- 1 | { 2 | "分镜结构": { 3 | "封面提示词": { 4 | "正向提示词": ["Black and white stick figure woman", "Snow falling on hunched shoulders", "Bamboo begging staff", "Pure white background", "Minimalist ink style"], 5 | "负向提示词": ["Color", "Detailed facial features", "Background scenery", "Realistic textures"] 6 | }, 7 | "分镜列表": [ 8 | { 9 | "分镜编号": 1, 10 | "时长": 7, 11 | "字幕": { 12 | "中文": "我叫祥林嫂,但这不是我本来的名字,只是大家这样叫我。", 13 | "英文": "They call me Xianglin's Wife - not my real name, just what everyone calls me." 14 | }, 15 | "正向提示词": ["Stick figure woman bowing head", "Floating name tags circling figure", "Minimalist composition", "Pure white background"], 16 | "负向提示词": ["Color", "Detailed face", "Background objects"] 17 | }, 18 | { 19 | "分镜编号": 2, 20 | "时长": 8, 21 | "字幕": { 22 | "中文": "我的命,就像寒冬腊月的雪,冰冷、沉重,最终消融得无影无踪。", 23 | "英文": "My fate is like winter snow - cold, heavy, finally vanishing without trace." 24 | }, 25 | "正向提示词": ["Stick figure melting into snowflakes", "Heavy snow symbols on shoulders", "Disintegration effect", "Negative space transition"], 26 | "负向提示词": ["Realistic snow", "Color gradients", "Detailed environment"] 27 | }, 28 | { 29 | "分镜编号": 3, 30 | "时长": 5, 31 | "字幕": { 32 | "中文": "春天,我的丈夫死了。", 33 | "英文": "In spring, my husband died." 34 | }, 35 | "正向提示词": ["Stick figure kneeling beside grave", "Withered flower symbol", "Downward lines indicating sorrow"], 36 | "负向提示词": ["Detailed grave", "Color", "Multiple characters"] 37 | }, 38 | { 39 | "分镜编号": 4, 40 | "时长": 9, 41 | "字幕": { 42 | "中文": "在婆婆严苛的目光和小叔子沉默的注视下,日子太难熬了。", 43 | "英文": "Under mother-in-law's harsh gaze and brother-in-law's silent stare, life became unbearable." 44 | }, 45 | "正向提示词": ["Three stick figures in triangle", "Sharp eyes projecting beams", "Pressure lines from above", "Cowering central figure"], 46 | "负向提示词": ["Detailed expressions", "Furniture", "Color"] 47 | }, 48 | { 49 | "分镜编号": 5, 50 | "时长": 8, 51 | "字幕": { 52 | "中文": "听说鲁镇要找女工,我趁着开春的忙乱,逃了出来。", 53 | "英文": "Hearing Luzhen needed maids, I escaped during spring chaos." 54 | }, 55 | "正向提示词": ["Figure running with bundle", "Dashed escape path", "Village outline in distance"], 56 | "负向提示词": ["Detailed landscape", "Color", "Facial features"] 57 | }, 58 | { 59 | "分镜编号": 6, 60 | "时长": 8, 61 | "字幕": { 62 | "中文": "卫老婆子把我带到了鲁四老爷家。老爷嫌弃我是寡妇,皱眉头,但太太看我手脚麻利、老实肯干,留下了我。", 63 | "英文": "Old Wei brought me to Master Lu's house. He frowned at my widow status, but mistress kept me for my diligence." 64 | }, 65 | "正向提示词": ["Three stick figures facing each other", "Master with furrowed brow lines", "Mistress nodding approval", "Bowed figure with bundle"], 66 | "负向提示词": ["Detailed interiors", "Color", "Multiple props"] 67 | }, 68 | { 69 | "分镜编号": 7, 70 | "时长": 10, 71 | "字幕": { 72 | "中文": "在鲁家,我拼命干活。扫尘、洗地、杀鸡宰鹅、煮整夜的福礼……我一点也不觉得累。", 73 | "英文": "At Lu's, I worked desperately: sweeping, washing, slaughtering fowl, cooking rituals all night... yet felt no fatigue." 74 | }, 75 | "正向提示词": ["Figure in continuous motion", "Multiple arms effect showing tasks", "Sweeping/killing/cooking symbols", "Clock showing night hours"], 76 | "负向提示词": ["Realistic tools", "Color", "Background details"] 77 | }, 78 | { 79 | "分镜编号": 8, 80 | "时长": 8, 81 | "字幕": { 82 | "中文": "这里管饭,没人打骂,口粮边甚至能尝到一丝笑影,脸上也渐渐有了点肉。", 83 | "英文": "Regular meals, no beatings - even smiles by the food pot. My face regained flesh." 84 | }, 85 | "正向提示词": ["Figure holding bowl with curved mouth", "Rising body lines showing weight gain", "Hand offering rice gesture"], 86 | "负向提示词": ["Detailed food", "Color", "Background kitchen"] 87 | }, 88 | { 89 | "分镜编号": 9, 90 | "时长": 7, 91 | "字幕": { 92 | "中文": "年底的忙碌里,我感到了久违的踏实。我以为,靠自己的力气,总能活下去。", 93 | "英文": "In year-end bustle, I felt rare stability. Believed I could survive by my own labor." 94 | }, 95 | "正向提示词": ["Upright figure carrying heavy load", "Steady ground lines", "Confident posture"], 96 | "负向提示词": ["Shaky lines", "Bowed posture", "Color effects"] 97 | }, 98 | { 99 | "分镜编号": 10, 100 | "时长": 8, 101 | "字幕": { 102 | "中文": "好景不长。新年刚过,我在河边淘米,远远看见夫家的堂伯!", 103 | "英文": "But happiness was brief. After New Year, while washing rice by river, I spotted my husband's uncle!" 104 | }, 105 | "正向提示词": ["Figure frozen at riverside", "Pointing hand in distance", "Rice bowl tipping over", "Shock lines radiating"], 106 | "负向提示词": ["Detailed river", "Color", "Facial expression"] 107 | }, 108 | { 109 | "分镜编号": 11, 110 | "时长": 7, 111 | "字幕": { 112 | "中文": "我慌了神,预感大祸临头。", 113 | "英文": "Panic-stricken, I sensed disaster coming." 114 | }, 115 | "正向提示词": ["Trembling stick figure", "Crisscrossed anxiety lines", "Dark cloud symbol overhead"], 116 | "负向提示词": ["Calm posture", "Bright symbols", "Color"] 117 | }, 118 | { 119 | "分镜编号": 12, 120 | "时长": 9, 121 | "字幕": { 122 | "中文": "果然,没几天,婆婆带着人来了。她赔着笑,说要我回去帮忙。", 123 | "英文": "Sure enough, days later mother-in-law arrived with men. Smiling falsely, she demanded my return 'to help'." 124 | }, 125 | "正向提示词": ["Group of figures approaching", "Mother-in-law with exaggerated smile", "Grasping hands gesture", "Cowering figure retreating"], 126 | "负向提示词": ["Detailed clothing", "Color", "Background buildings"] 127 | }, 128 | { 129 | "分镜编号": 13, 130 | "时长": 8, 131 | "字幕": { 132 | "中文": "四老爷说“既是婆婆要回去,没话说”。我的工钱,一文没动,全被婆婆拿走了。", 133 | "英文": "Master Lu said: 'Since mother-in-law demands it.' My untouched wages were all taken." 134 | }, 135 | "正向提示词": ["Money bag transferring hands", "Master Lu waving dismissal", "Empty hands gesture"], 136 | "负向提示词": ["Detailed coins", "Color", "Facial expressions"] 137 | }, 138 | { 139 | "分镜编号": 14, 140 | "时长": 9, 141 | "字幕": { 142 | "中文": "我假装去淘米,其实是想逃,可来不及了!白篷船里跳出两个男人,死死抱住我,拖进船里。", 143 | "英文": "Pretending to wash rice, I tried to flee - too late! Men from white-awning boat grabbed and dragged me in." 144 | }, 145 | "正向提示词": ["Struggling figure held by two men", "Boat outline with simple awning", "Drag marks on ground"], 146 | "负向提示词": ["Detailed boat", "Water effects", "Color"] 147 | }, 148 | { 149 | "分镜编号": 15, 150 | "时长": 8, 151 | "字幕": { 152 | "中文": "我哭喊,挣扎,但嘴被堵住。我被捆着带回了山里。", 153 | "英文": "I screamed and fought, but they gagged me. Tied up, I was taken to the mountains." 154 | }, 155 | "正向提示词": ["Bound figure with X-marked mouth", "Mountain symbols approaching", "Tear drops floating upward"], 156 | "负向提示词": ["Rope details", "Landscape details", "Color"] 157 | }, 158 | { 159 | "分镜编号": 16, 160 | "时长": 9, 161 | "字幕": { 162 | "中文": "原来,婆婆早已把我卖给了深山里贺家墺的贺老六,为了八十千钱给小叔子娶媳妇。", 163 | "英文": "Mother-in-law had sold me to He Lao-liu in remote mountains - 80,000 cash for brother-in-law's bride." 164 | }, 165 | "正向提示词": ["Money bag with 80k symbol", "Arrow pointing to mountain village", "Wedding procession silhouette"], 166 | "负向提示词": ["Detailed faces", "Color", "Complex scene"] 167 | }, 168 | { 169 | "分镜编号": 17, 170 | "时长": 10, 171 | "字幕": { 172 | "中文": "花轿抬到贺家,我拼死反抗!嚎骂、挣扎,喉咙都哑了。", 173 | "英文": "At He's house, I resisted desperately! Shouting, struggling till my voice gave out." 174 | }, 175 | "正向提示词": ["Wedding sedan with figure bursting out", "Sound waves from open mouth", "Broken ropes on ground"], 176 | "负向提示词": ["Detailed sedan chair", "Color", "Multiple characters"] 177 | }, 178 | { 179 | "分镜编号": 18, 180 | "时长": 9, 181 | "字幕": { 182 | "中文": "他们强按着我拜堂,我一头撞在香案角上,鲜血直流……我想死,死了就干净了。", 183 | "英文": "They forced me to bow at altar. I smashed my head on incense table - blood gushed... I wished for death's cleanliness." 184 | }, 185 | "正向提示词": ["Figure lunging toward altar corner", "Impact lines with blood droplets", "Drooping body with blank eyes"], 186 | "负向提示词": ["Realistic blood", "Detailed furniture", "Color"] 187 | }, 188 | { 189 | "分镜编号": 19, 190 | "时长": 8, 191 | "字幕": { 192 | "中文": "贺老六是个老实人,力气大,待我还好。我认命了。", 193 | "英文": "He Lao-liu was decent, strong, treated me alright. I resigned to fate." 194 | }, 195 | "正向提示词": ["Two figures sitting side by side", "Man with broad shoulders outline", "Woman with lowered head"], 196 | "负向提示词": ["Romantic pose", "Smiling faces", "Color"] 197 | }, 198 | { 199 | "分镜编号": 20, 200 | "时长": 7, 201 | "字幕": { 202 | "中文": "年底,我生了个儿子阿毛。", 203 | "英文": "Year's end, I bore a son - Ah Mao." 204 | }, 205 | "正向提示词": ["Figure holding baby outline", "Radiating warmth lines", "Small bundle with smile symbol"], 206 | "负向提示词": ["Detailed baby", "Color", "Background"] 207 | }, 208 | { 209 | "分镜编号": 21, 210 | "时长": 10, 211 | "字幕": { 212 | "中文": "日子虽然苦,但阿毛是我全部的希望。看着他胖乎乎的小脸,我觉得老天总算给了我一点补偿。", 213 | "英文": "Life remained hard, but Ah Mao was my hope. His chubby face felt like heaven's compensation." 214 | }, 215 | "正向提示词": ["Figure gazing at child", "Heart symbol connecting them", "Sunbeam from above"], 216 | "负向提示词": ["Detailed features", "Color", "Complex setting"] 217 | }, 218 | { 219 | "分镜编号": 22, 220 | "时长": 9, 221 | "字幕": { 222 | "中文": "男人有把力气,房子是自己的,上头没有婆婆管束,我以为苦难到头了。", 223 | "英文": "Man's strength, our own house, no mother-in-law - I thought suffering had ended." 224 | }, 225 | "正向提示词": ["House outline with stick figures", "Broken chain symbol", "Upward sun rays"], 226 | "负向提示词": ["Detailed house", "Color", "Landscape"] 227 | }, 228 | { 229 | "分镜编号": 23, 230 | "时长": 8, 231 | "字幕": { 232 | "中文": "谁知命运如此狠毒!男人得了伤寒,本来快好了,一碗冷饭送了命。", 233 | "英文": "How cruel fate proved! Husband caught typhoid, nearly recovered, then died from cold rice." 234 | }, 235 | "正向提示词": ["Figure collapsing with bowl", "Shattered rice grains", "Skull symbol appearing"], 236 | "负向提示词": ["Realistic food", "Sickbed details", "Color"] 237 | }, 238 | { 239 | "分镜编号": 24, 240 | "时长": 9, 241 | "字幕": { 242 | "中文": "留下我和阿毛孤儿寡母。我咬着牙,打柴、摘茶、养蚕,再苦也要把阿毛拉扯大。", 243 | "英文": "Left with Ah Mao, I gritted teeth: chopping wood, picking tea, raising silkworms - determined to raise him." 244 | }, 245 | "正向提示词": ["Figure working with child on back", "Multiple tools in hands", "Steely resolve lines in posture"], 246 | "负向提示词": ["Detailed tools", "Background scenery", "Color"] 247 | }, 248 | { 249 | "分镜编号": 25, 250 | "时长": 12, 251 | "字幕": { 252 | "中文": "春天快完了。那天,我早早开了门,让阿毛坐在门槛上剥豆——他很听话的。", 253 | "英文": "Spring's end. That day I opened door early, sat Ah Mao on threshold shelling peas - he was so obedient." 254 | }, 255 | "正向提示词": ["Small figure on doorway", "Pea pods in hands", "Mother looking back from distance"], 256 | "负向提示词": ["Detailed house", "Color", "Facial features"] 257 | }, 258 | { 259 | "分镜编号": 26, 260 | "时长": 10, 261 | "字幕": { 262 | "中文": "我在屋后劈柴、淘米。米下了锅,我叫“阿毛”,没有应。", 263 | "英文": "I chopped wood behind house, washed rice. When rice boiled, I called 'Ah Mao!' No answer." 264 | }, 265 | "正向提示词": ["Figure with axe mid-swing", "Turning head with question mark", "Empty doorway silhouette"], 266 | "负向提示词": ["Detailed background", "Color", "Multiple actions"] 267 | }, 268 | { 269 | "分镜编号": 27, 270 | "时长": 10, 271 | "字幕": { 272 | "中文": "出去一看,豆撒了一地,阿毛不见了!我心胆俱裂,疯了一样央人去寻。", 273 | "英文": "Outside: peas scattered everywhere, Ah Mao gone! Heart shattered, I begged neighbors frantically." 274 | }, 275 | "正向提示词": ["Scattered peas on ground", "Broken heart symbol above figure", "Running legs with speed lines"], 276 | "负向提示词": ["Detailed ground", "Color", "Background characters"] 277 | }, 278 | { 279 | "分镜编号": 28, 280 | "时长": 12, 281 | "字幕": { 282 | "中文": "在山墺里,刺柴上挂着他的一只小鞋……再进去,我的阿毛躺在草窠里,肚子……被狼掏空了,小手还紧紧攥着小篮……", 283 | "英文": "In mountain hollow, thornbush held his tiny shoe... Further in, my Ah Mao lay in grass nest, belly... hollowed by wolf, tiny hand still clutching basket..." 284 | }, 285 | "正向提示词": ["Small shoe on bush", "Grass nest outline with still figure", "Empty basket in hand", "Wolf shadow receding"], 286 | "负向提示词": ["Gore details", "Color", "Realistic wolf"] 287 | }, 288 | { 289 | "分镜编号": 29, 290 | "时长": 8, 291 | "字幕": { 292 | "中文": "我的天塌了!我真傻啊,单知道雪天有狼,不知道春天也会有狼!", 293 | "英文": "My sky collapsed! How foolish! Knew wolves in winter snow, not spring!" 294 | }, 295 | "正向提示词": ["Figure collapsing to knees", "Shattered sky symbol", "Wolf paw prints circling"], 296 | "负向提示词": ["Detailed landscape", "Color", "Tears"] 297 | }, 298 | { 299 | "分镜编号": 30, 300 | "时长": 9, 301 | "字幕": { 302 | "中文": "大伯收走了房子,赶我出门。我走投无路,只能再回鲁镇求太太收留。", 303 | "英文": "Elder brother took our house, drove me out. Desperate, I returned to Luzhen begging mistress." 304 | }, 305 | "正向提示词": ["Figure evicted from house outline", "Long road back to town", "Bowed posture at gate"], 306 | "负向提示词": ["Detailed buildings", "Color", "Facial expressions"] 307 | }, 308 | { 309 | "分镜编号": 31, 310 | "时长": 11, 311 | "字幕": { 312 | "中文": "但这次,一切都不同了。老爷更嫌弃我,说我“败坏风俗”。太太虽然让我干活,但祭祀时,我刚要去摆酒杯、拿烛台,她就慌忙喊:“祥林嫂,你放着罢!”", 313 | "英文": "But now everything changed. Master called me 'morally corrupt'. Though mistress let me work, at rituals when I reached for wine cups, she'd cry: 'Xianglin's Wife, don't touch!'" 314 | }, 315 | "正向提示词": ["Hand reaching toward ritual items", "Sharp stop gesture from mistress", "Shock waves hitting figure", "Cups and candles floating away"], 316 | "负向提示词": ["Detailed altar", "Color", "Multiple characters"] 317 | }, 318 | { 319 | "分镜编号": 32, 320 | "时长": 10, 321 | "字幕": { 322 | "中文": "镇上的人看我的眼神也变了,笑容冷冷的。我忍不住跟人讲阿毛的事,“我真傻,真的……”。", 323 | "英文": "Townfolk's eyes turned cold. I couldn't stop telling Ah Mao's story: 'I was so foolish, truly...'" 324 | }, 325 | "正向提示词": ["Figure surrounded by cold stare lines", "Speech bubble with repeated words", "Isolation effect"], 326 | "负向提示词": ["Detailed crowd", "Color", "Varied expressions"] 327 | }, 328 | { 329 | "分镜编号": 33, 330 | "时长": 12, 331 | "字幕": { 332 | "中文": "柳妈说嫁了两个男人,到了阴司会被锯成两半分给死鬼男人!我吓得魂飞魄散。唯一的办法是捐条门槛给千人踏万人跨赎罪。", 333 | "英文": "Liu Ma said marrying twice meant hell demons would saw me in half! Only solution: donate threshold for thousands to tread, redeeming sins." 334 | }, 335 | "正向提示词": ["Saw blade dividing stick figure", "Trembling figure before temple", "Threshold with many footprints"], 336 | "负向提示词": ["Hell imagery", "Color", "Detailed temple"] 337 | }, 338 | { 339 | "分镜编号": 34, 340 | "时长": 10, 341 | "字幕": { 342 | "中文": "我拼命攒钱捐了门槛。可太太仍喊“你放着罢!”时,心一下掉进冰窟窿。", 343 | "英文": "After desperately saving to donate threshold, when mistress still shouted 'Don't touch!', my heart plunged into icy abyss." 344 | }, 345 | "正向提示词": ["Hand withdrawing from ritual items", "Heart symbol falling into cracks", "Ice crystals forming around figure"], 346 | "负向提示词": ["Color", "Detailed expressions", "Background"] 347 | }, 348 | { 349 | "分镜编号": 35, 350 | "时长": 10, 351 | "字幕": { 352 | "中文": "我成了乞丐。快过年时问识字的“老爷”:“人死后有没有魂灵?”我盼着见阿毛,又怕被锯开……他说“说不清”。", 353 | "英文": "Became a beggar. Near New Year I asked literate 'master': 'Do souls exist after death?' Hoping to see Ah Mao, fearing the saw... He muttered: 'Unclear'." 354 | }, 355 | "正向提示词": ["Beggar figure with broken bowl", "Question mark between two figures", "Contrasting hope/fear symbols"], 356 | "负向提示词": ["Detailed clothing", "Color", "Background"] 357 | }, 358 | { 359 | "分镜编号": 36, 360 | "时长": 12, 361 | "字幕": { 362 | "中文": "鲁镇祝福的雪夜,我死了。四老爷骂我“不早不迟,偏偏这时候——可见是个谬种!”短工说“穷死的”。", 363 | "英文": "On Luzhen's blessing night, I died. Master Lu cursed: 'Of all times to die - proves her evil!' Laborer said: 'Died of poverty'." 364 | }, 365 | "正向提示词": ["Fallen figure in snow", "Fireworks overhead", "Disembodied pointing fingers", "Empty begging bowl"], 366 | "负向提示词": ["Gore", "Color", "Detailed death scene"] 367 | }, 368 | { 369 | "分镜编号": 37, 370 | "时长": 10, 371 | "字幕": { 372 | "中文": "我的生命在喧嚣的祝福声中,像一粒尘埃飘散了。天地圣众醉醺醺享受着供奉,谁会在意一个被碾碎的灵魂呢?", 373 | "英文": "My life scattered like dust amid festive noise. Heaven's saints drunkenly enjoyed offerings - who cared about a crushed soul?" 374 | }, 375 | "正向提示词": ["Floating dust particles fading", "Feasting deity silhouettes", "Broken soul symbol underfoot", "Pure white background"], 376 | "负向提示词": ["Color", "Detailed deities", "Background scenery"] 377 | } 378 | ], 379 | "总时长": 360, 380 | "核心策略": [ 381 | "严格逐字转化原文为字幕", 382 | "火柴人动作精确匹配文本动词(如'撞香案'→冲击动态)", 383 | "抽象情感可视化('心掉进冰窟窿'→冰晶+坠落心形)", 384 | "保持单一叙述者视角贯穿", 385 | "平均分镜时长9秒(按中文字数/4计算)", 386 | "黑白负空间强化悲剧氛围" 387 | ] 388 | } 389 | } -------------------------------------------------------------------------------- /txt2video/static/cover_frame.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Xikcn/PsycheFlowGen/784f87a512a7bb1f90c37b1c5a9ae2fe2377edf5/txt2video/static/cover_frame.png -------------------------------------------------------------------------------- /txt2video/static/frame_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Xikcn/PsycheFlowGen/784f87a512a7bb1f90c37b1c5a9ae2fe2377edf5/txt2video/static/frame_1.png -------------------------------------------------------------------------------- /txt2video/static/frame_10.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Xikcn/PsycheFlowGen/784f87a512a7bb1f90c37b1c5a9ae2fe2377edf5/txt2video/static/frame_10.png -------------------------------------------------------------------------------- /txt2video/static/frame_11.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Xikcn/PsycheFlowGen/784f87a512a7bb1f90c37b1c5a9ae2fe2377edf5/txt2video/static/frame_11.png -------------------------------------------------------------------------------- /txt2video/static/frame_12.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Xikcn/PsycheFlowGen/784f87a512a7bb1f90c37b1c5a9ae2fe2377edf5/txt2video/static/frame_12.png -------------------------------------------------------------------------------- /txt2video/static/frame_13.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Xikcn/PsycheFlowGen/784f87a512a7bb1f90c37b1c5a9ae2fe2377edf5/txt2video/static/frame_13.png -------------------------------------------------------------------------------- /txt2video/static/frame_14.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Xikcn/PsycheFlowGen/784f87a512a7bb1f90c37b1c5a9ae2fe2377edf5/txt2video/static/frame_14.png -------------------------------------------------------------------------------- /txt2video/static/frame_15.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Xikcn/PsycheFlowGen/784f87a512a7bb1f90c37b1c5a9ae2fe2377edf5/txt2video/static/frame_15.png -------------------------------------------------------------------------------- /txt2video/static/frame_16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Xikcn/PsycheFlowGen/784f87a512a7bb1f90c37b1c5a9ae2fe2377edf5/txt2video/static/frame_16.png -------------------------------------------------------------------------------- /txt2video/static/frame_17.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Xikcn/PsycheFlowGen/784f87a512a7bb1f90c37b1c5a9ae2fe2377edf5/txt2video/static/frame_17.png -------------------------------------------------------------------------------- /txt2video/static/frame_18.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Xikcn/PsycheFlowGen/784f87a512a7bb1f90c37b1c5a9ae2fe2377edf5/txt2video/static/frame_18.png -------------------------------------------------------------------------------- /txt2video/static/frame_19.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Xikcn/PsycheFlowGen/784f87a512a7bb1f90c37b1c5a9ae2fe2377edf5/txt2video/static/frame_19.png -------------------------------------------------------------------------------- /txt2video/static/frame_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Xikcn/PsycheFlowGen/784f87a512a7bb1f90c37b1c5a9ae2fe2377edf5/txt2video/static/frame_2.png -------------------------------------------------------------------------------- /txt2video/static/frame_20.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Xikcn/PsycheFlowGen/784f87a512a7bb1f90c37b1c5a9ae2fe2377edf5/txt2video/static/frame_20.png -------------------------------------------------------------------------------- /txt2video/static/frame_21.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Xikcn/PsycheFlowGen/784f87a512a7bb1f90c37b1c5a9ae2fe2377edf5/txt2video/static/frame_21.png -------------------------------------------------------------------------------- /txt2video/static/frame_22.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Xikcn/PsycheFlowGen/784f87a512a7bb1f90c37b1c5a9ae2fe2377edf5/txt2video/static/frame_22.png -------------------------------------------------------------------------------- /txt2video/static/frame_23.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Xikcn/PsycheFlowGen/784f87a512a7bb1f90c37b1c5a9ae2fe2377edf5/txt2video/static/frame_23.png -------------------------------------------------------------------------------- /txt2video/static/frame_24.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Xikcn/PsycheFlowGen/784f87a512a7bb1f90c37b1c5a9ae2fe2377edf5/txt2video/static/frame_24.png -------------------------------------------------------------------------------- /txt2video/static/frame_25.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Xikcn/PsycheFlowGen/784f87a512a7bb1f90c37b1c5a9ae2fe2377edf5/txt2video/static/frame_25.png -------------------------------------------------------------------------------- /txt2video/static/frame_26.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Xikcn/PsycheFlowGen/784f87a512a7bb1f90c37b1c5a9ae2fe2377edf5/txt2video/static/frame_26.png -------------------------------------------------------------------------------- /txt2video/static/frame_27.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Xikcn/PsycheFlowGen/784f87a512a7bb1f90c37b1c5a9ae2fe2377edf5/txt2video/static/frame_27.png -------------------------------------------------------------------------------- /txt2video/static/frame_28.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Xikcn/PsycheFlowGen/784f87a512a7bb1f90c37b1c5a9ae2fe2377edf5/txt2video/static/frame_28.png -------------------------------------------------------------------------------- /txt2video/static/frame_29.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Xikcn/PsycheFlowGen/784f87a512a7bb1f90c37b1c5a9ae2fe2377edf5/txt2video/static/frame_29.png -------------------------------------------------------------------------------- /txt2video/static/frame_3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Xikcn/PsycheFlowGen/784f87a512a7bb1f90c37b1c5a9ae2fe2377edf5/txt2video/static/frame_3.png -------------------------------------------------------------------------------- /txt2video/static/frame_30.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Xikcn/PsycheFlowGen/784f87a512a7bb1f90c37b1c5a9ae2fe2377edf5/txt2video/static/frame_30.png -------------------------------------------------------------------------------- /txt2video/static/frame_31.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Xikcn/PsycheFlowGen/784f87a512a7bb1f90c37b1c5a9ae2fe2377edf5/txt2video/static/frame_31.png -------------------------------------------------------------------------------- /txt2video/static/frame_32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Xikcn/PsycheFlowGen/784f87a512a7bb1f90c37b1c5a9ae2fe2377edf5/txt2video/static/frame_32.png -------------------------------------------------------------------------------- /txt2video/static/frame_33.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Xikcn/PsycheFlowGen/784f87a512a7bb1f90c37b1c5a9ae2fe2377edf5/txt2video/static/frame_33.png -------------------------------------------------------------------------------- /txt2video/static/frame_34.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Xikcn/PsycheFlowGen/784f87a512a7bb1f90c37b1c5a9ae2fe2377edf5/txt2video/static/frame_34.png -------------------------------------------------------------------------------- /txt2video/static/frame_35.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Xikcn/PsycheFlowGen/784f87a512a7bb1f90c37b1c5a9ae2fe2377edf5/txt2video/static/frame_35.png -------------------------------------------------------------------------------- /txt2video/static/frame_36.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Xikcn/PsycheFlowGen/784f87a512a7bb1f90c37b1c5a9ae2fe2377edf5/txt2video/static/frame_36.png -------------------------------------------------------------------------------- /txt2video/static/frame_37.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Xikcn/PsycheFlowGen/784f87a512a7bb1f90c37b1c5a9ae2fe2377edf5/txt2video/static/frame_37.png -------------------------------------------------------------------------------- /txt2video/static/frame_4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Xikcn/PsycheFlowGen/784f87a512a7bb1f90c37b1c5a9ae2fe2377edf5/txt2video/static/frame_4.png -------------------------------------------------------------------------------- /txt2video/static/frame_5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Xikcn/PsycheFlowGen/784f87a512a7bb1f90c37b1c5a9ae2fe2377edf5/txt2video/static/frame_5.png -------------------------------------------------------------------------------- /txt2video/static/frame_6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Xikcn/PsycheFlowGen/784f87a512a7bb1f90c37b1c5a9ae2fe2377edf5/txt2video/static/frame_6.png -------------------------------------------------------------------------------- /txt2video/static/frame_7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Xikcn/PsycheFlowGen/784f87a512a7bb1f90c37b1c5a9ae2fe2377edf5/txt2video/static/frame_7.png -------------------------------------------------------------------------------- /txt2video/static/frame_8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Xikcn/PsycheFlowGen/784f87a512a7bb1f90c37b1c5a9ae2fe2377edf5/txt2video/static/frame_8.png -------------------------------------------------------------------------------- /txt2video/static/frame_9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Xikcn/PsycheFlowGen/784f87a512a7bb1f90c37b1c5a9ae2fe2377edf5/txt2video/static/frame_9.png -------------------------------------------------------------------------------- /txt2video/static/temp_frames/cover_frame.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Xikcn/PsycheFlowGen/784f87a512a7bb1f90c37b1c5a9ae2fe2377edf5/txt2video/static/temp_frames/cover_frame.png -------------------------------------------------------------------------------- /txt2video/static/temp_frames/frame_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Xikcn/PsycheFlowGen/784f87a512a7bb1f90c37b1c5a9ae2fe2377edf5/txt2video/static/temp_frames/frame_1.png -------------------------------------------------------------------------------- /txt2video/static/temp_frames/frame_10.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Xikcn/PsycheFlowGen/784f87a512a7bb1f90c37b1c5a9ae2fe2377edf5/txt2video/static/temp_frames/frame_10.png -------------------------------------------------------------------------------- /txt2video/static/temp_frames/frame_11.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Xikcn/PsycheFlowGen/784f87a512a7bb1f90c37b1c5a9ae2fe2377edf5/txt2video/static/temp_frames/frame_11.png -------------------------------------------------------------------------------- /txt2video/static/temp_frames/frame_12.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Xikcn/PsycheFlowGen/784f87a512a7bb1f90c37b1c5a9ae2fe2377edf5/txt2video/static/temp_frames/frame_12.png -------------------------------------------------------------------------------- /txt2video/static/temp_frames/frame_13.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Xikcn/PsycheFlowGen/784f87a512a7bb1f90c37b1c5a9ae2fe2377edf5/txt2video/static/temp_frames/frame_13.png -------------------------------------------------------------------------------- /txt2video/static/temp_frames/frame_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Xikcn/PsycheFlowGen/784f87a512a7bb1f90c37b1c5a9ae2fe2377edf5/txt2video/static/temp_frames/frame_2.png -------------------------------------------------------------------------------- /txt2video/static/temp_frames/frame_3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Xikcn/PsycheFlowGen/784f87a512a7bb1f90c37b1c5a9ae2fe2377edf5/txt2video/static/temp_frames/frame_3.png -------------------------------------------------------------------------------- /txt2video/static/temp_frames/frame_4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Xikcn/PsycheFlowGen/784f87a512a7bb1f90c37b1c5a9ae2fe2377edf5/txt2video/static/temp_frames/frame_4.png -------------------------------------------------------------------------------- /txt2video/static/temp_frames/frame_5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Xikcn/PsycheFlowGen/784f87a512a7bb1f90c37b1c5a9ae2fe2377edf5/txt2video/static/temp_frames/frame_5.png -------------------------------------------------------------------------------- /txt2video/static/temp_frames/frame_6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Xikcn/PsycheFlowGen/784f87a512a7bb1f90c37b1c5a9ae2fe2377edf5/txt2video/static/temp_frames/frame_6.png -------------------------------------------------------------------------------- /txt2video/static/temp_frames/frame_7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Xikcn/PsycheFlowGen/784f87a512a7bb1f90c37b1c5a9ae2fe2377edf5/txt2video/static/temp_frames/frame_7.png -------------------------------------------------------------------------------- /txt2video/static/temp_frames/frame_8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Xikcn/PsycheFlowGen/784f87a512a7bb1f90c37b1c5a9ae2fe2377edf5/txt2video/static/temp_frames/frame_8.png -------------------------------------------------------------------------------- /txt2video/static/temp_frames/frame_9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Xikcn/PsycheFlowGen/784f87a512a7bb1f90c37b1c5a9ae2fe2377edf5/txt2video/static/temp_frames/frame_9.png -------------------------------------------------------------------------------- /txt2video/static/uploads/bgm_0932f953.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Xikcn/PsycheFlowGen/784f87a512a7bb1f90c37b1c5a9ae2fe2377edf5/txt2video/static/uploads/bgm_0932f953.mp3 -------------------------------------------------------------------------------- /txt2video/static/uploads/bgm_1a78a023.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Xikcn/PsycheFlowGen/784f87a512a7bb1f90c37b1c5a9ae2fe2377edf5/txt2video/static/uploads/bgm_1a78a023.mp3 -------------------------------------------------------------------------------- /txt2video/static/uploads/bgm_d4360d70.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Xikcn/PsycheFlowGen/784f87a512a7bb1f90c37b1c5a9ae2fe2377edf5/txt2video/static/uploads/bgm_d4360d70.mp3 -------------------------------------------------------------------------------- /txt2video/static/videos/从祥林嫂的角度讲《彷徨》_output_2e41a3b9.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Xikcn/PsycheFlowGen/784f87a512a7bb1f90c37b1c5a9ae2fe2377edf5/txt2video/static/videos/从祥林嫂的角度讲《彷徨》_output_2e41a3b9.mp4 -------------------------------------------------------------------------------- /txt2video/static/videos/从祥林嫂的角度讲《彷徨》_output_6f8a707e.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Xikcn/PsycheFlowGen/784f87a512a7bb1f90c37b1c5a9ae2fe2377edf5/txt2video/static/videos/从祥林嫂的角度讲《彷徨》_output_6f8a707e.mp4 -------------------------------------------------------------------------------- /txt2video/uploads/cover_04557a68.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Xikcn/PsycheFlowGen/784f87a512a7bb1f90c37b1c5a9ae2fe2377edf5/txt2video/uploads/cover_04557a68.jpg -------------------------------------------------------------------------------- /txt2video/uploads/cover_1f0c26d5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Xikcn/PsycheFlowGen/784f87a512a7bb1f90c37b1c5a9ae2fe2377edf5/txt2video/uploads/cover_1f0c26d5.png -------------------------------------------------------------------------------- /txt2video/uploads/cover_1ff354de.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Xikcn/PsycheFlowGen/784f87a512a7bb1f90c37b1c5a9ae2fe2377edf5/txt2video/uploads/cover_1ff354de.png -------------------------------------------------------------------------------- /txt2video/uploads/cover_3ae2032e.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Xikcn/PsycheFlowGen/784f87a512a7bb1f90c37b1c5a9ae2fe2377edf5/txt2video/uploads/cover_3ae2032e.png -------------------------------------------------------------------------------- /txt2video/uploads/cover_43c46d0d.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Xikcn/PsycheFlowGen/784f87a512a7bb1f90c37b1c5a9ae2fe2377edf5/txt2video/uploads/cover_43c46d0d.png -------------------------------------------------------------------------------- /txt2video/uploads/cover_96b0c375.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Xikcn/PsycheFlowGen/784f87a512a7bb1f90c37b1c5a9ae2fe2377edf5/txt2video/uploads/cover_96b0c375.png -------------------------------------------------------------------------------- /txt2video/uploads/cover_a022d239.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Xikcn/PsycheFlowGen/784f87a512a7bb1f90c37b1c5a9ae2fe2377edf5/txt2video/uploads/cover_a022d239.png -------------------------------------------------------------------------------- /txt2video/uploads/cover_a320aead.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Xikcn/PsycheFlowGen/784f87a512a7bb1f90c37b1c5a9ae2fe2377edf5/txt2video/uploads/cover_a320aead.png -------------------------------------------------------------------------------- /txt2video/uploads/cover_a42bf3ba.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Xikcn/PsycheFlowGen/784f87a512a7bb1f90c37b1c5a9ae2fe2377edf5/txt2video/uploads/cover_a42bf3ba.png -------------------------------------------------------------------------------- /txt2video/uploads/cover_a6619917.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Xikcn/PsycheFlowGen/784f87a512a7bb1f90c37b1c5a9ae2fe2377edf5/txt2video/uploads/cover_a6619917.png -------------------------------------------------------------------------------- /txt2video/uploads/cover_b85e60c7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Xikcn/PsycheFlowGen/784f87a512a7bb1f90c37b1c5a9ae2fe2377edf5/txt2video/uploads/cover_b85e60c7.png -------------------------------------------------------------------------------- /txt2video/uploads/cover_bb76fe6f.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Xikcn/PsycheFlowGen/784f87a512a7bb1f90c37b1c5a9ae2fe2377edf5/txt2video/uploads/cover_bb76fe6f.png -------------------------------------------------------------------------------- /txt2video/uploads/cover_d8ceeaca.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Xikcn/PsycheFlowGen/784f87a512a7bb1f90c37b1c5a9ae2fe2377edf5/txt2video/uploads/cover_d8ceeaca.png -------------------------------------------------------------------------------- /txt2video/uploads/scene_1_50102f86.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Xikcn/PsycheFlowGen/784f87a512a7bb1f90c37b1c5a9ae2fe2377edf5/txt2video/uploads/scene_1_50102f86.png -------------------------------------------------------------------------------- /txt2video/uploads/scene_1_946e339f.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Xikcn/PsycheFlowGen/784f87a512a7bb1f90c37b1c5a9ae2fe2377edf5/txt2video/uploads/scene_1_946e339f.png -------------------------------------------------------------------------------- /txt2video/uploads/scene_1_b744c57e.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Xikcn/PsycheFlowGen/784f87a512a7bb1f90c37b1c5a9ae2fe2377edf5/txt2video/uploads/scene_1_b744c57e.jpg -------------------------------------------------------------------------------- /txt2video/uploads/scene_1_b8e5329f.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Xikcn/PsycheFlowGen/784f87a512a7bb1f90c37b1c5a9ae2fe2377edf5/txt2video/uploads/scene_1_b8e5329f.png -------------------------------------------------------------------------------- /txt2video/uploads/scene_1_d7038556.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Xikcn/PsycheFlowGen/784f87a512a7bb1f90c37b1c5a9ae2fe2377edf5/txt2video/uploads/scene_1_d7038556.png -------------------------------------------------------------------------------- /txt2video/uploads/scene_1_f1e07374.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Xikcn/PsycheFlowGen/784f87a512a7bb1f90c37b1c5a9ae2fe2377edf5/txt2video/uploads/scene_1_f1e07374.jpg -------------------------------------------------------------------------------- /txt2video/uploads/scene_2_02ec04b9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Xikcn/PsycheFlowGen/784f87a512a7bb1f90c37b1c5a9ae2fe2377edf5/txt2video/uploads/scene_2_02ec04b9.png -------------------------------------------------------------------------------- /txt2video/uploads/scene_2_167f6126.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Xikcn/PsycheFlowGen/784f87a512a7bb1f90c37b1c5a9ae2fe2377edf5/txt2video/uploads/scene_2_167f6126.png -------------------------------------------------------------------------------- /txt2video/uploads/scene_2_7e709871.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Xikcn/PsycheFlowGen/784f87a512a7bb1f90c37b1c5a9ae2fe2377edf5/txt2video/uploads/scene_2_7e709871.jpg -------------------------------------------------------------------------------- /txt2video/uploads/scene_2_9075181d.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Xikcn/PsycheFlowGen/784f87a512a7bb1f90c37b1c5a9ae2fe2377edf5/txt2video/uploads/scene_2_9075181d.png -------------------------------------------------------------------------------- /txt2video/uploads/scene_2_e4d71314.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Xikcn/PsycheFlowGen/784f87a512a7bb1f90c37b1c5a9ae2fe2377edf5/txt2video/uploads/scene_2_e4d71314.png -------------------------------------------------------------------------------- /txt2video/uploads/scene_2_ec77768d.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Xikcn/PsycheFlowGen/784f87a512a7bb1f90c37b1c5a9ae2fe2377edf5/txt2video/uploads/scene_2_ec77768d.png -------------------------------------------------------------------------------- /txt2video/uploads/scene_3_0030fce3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Xikcn/PsycheFlowGen/784f87a512a7bb1f90c37b1c5a9ae2fe2377edf5/txt2video/uploads/scene_3_0030fce3.jpg -------------------------------------------------------------------------------- /txt2video/uploads/scene_3_83a87aca.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Xikcn/PsycheFlowGen/784f87a512a7bb1f90c37b1c5a9ae2fe2377edf5/txt2video/uploads/scene_3_83a87aca.png -------------------------------------------------------------------------------- /txt2video/uploads/scene_3_f618f7a6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Xikcn/PsycheFlowGen/784f87a512a7bb1f90c37b1c5a9ae2fe2377edf5/txt2video/uploads/scene_3_f618f7a6.png -------------------------------------------------------------------------------- /txt2video/uploads/scene_4_38a5511e.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Xikcn/PsycheFlowGen/784f87a512a7bb1f90c37b1c5a9ae2fe2377edf5/txt2video/uploads/scene_4_38a5511e.jpg -------------------------------------------------------------------------------- /txt2video/uploads/scene_5_d54fca98.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Xikcn/PsycheFlowGen/784f87a512a7bb1f90c37b1c5a9ae2fe2377edf5/txt2video/uploads/scene_5_d54fca98.png --------------------------------------------------------------------------------