{"slug":"znyupup-ai-video-editing-skill-bb97b3","title":"\u65c5\u884cVlog\u81ea\u52a8\u526a\u8f91\u5de5\u4f5c\u6d41","description":"\u9762\u5411\u65c5\u884c/\u65e5\u5e38\u7d20\u6750\u7684\u4e03\u9636\u6bb5\u81ea\u52a8\u526a\u8f91\u6d41\u7a0b\uff1a\u7d20\u6750\u76d8\u70b9\u3001\u53c2\u8003\u7814\u7a76\u3001\u4e09\u7ef4\u5206\u6790\uff08ASR+\u97f3\u91cf+\u89c6\u89c9\uff09\u3001\u7cbe\u4fee\u9884\u5904\u7406\u3001LLM\u53d9\u4e8b\u7f16\u6392\u3001\u8bed\u97f3\u622a\u65ad\u6821\u9a8c\u3001ffmpeg\u6e32\u67d3\u4e0e\u9a8c\u6536\uff0c\u542b\u7f16\u7801\u62fc\u63a5\u4e0e\u97f3\u753b\u540c\u6b65\u7684\u5b9e\u6218\u6ce8\u610f\u4e8b\u9879\u3002","category":"\u526a\u8f91\u4e0e\u5305\u88c5","tags":["znyupup","\u526a\u8f91\u4e0e\u5305\u88c5"],"author":"znyupup","source_url":"https://github.com/znyupup/ai-video-editing-skill/blob/b6429ab550a64c595dc68d42c47cf2b489a50619/SKILL.md","license":"MIT","status":"published","verification":"source_reviewed","verification_notes":"\u5df2\u6838\u5bf9\u539f\u59cb\u4ed3\u5e93\u3001\u56fa\u5b9a\u63d0\u4ea4\u3001\u8bb8\u53ef\u3001\u6280\u80fd\u6b63\u6587\u4e0e\u5305\u7ed3\u6784\uff0c\u5e76\u8865\u5145\u4e2d\u6587\u6267\u884c\u9a8c\u6536\u8bf4\u660e\uff1b\u672a\u8fd0\u884c\u5305\u5185\u811a\u672c\u3002","revision":"8ff3aec771834397b81e41ef5a1ec4ba","created_at":"2026-11-02T14:47:08+00:00","updated_at":"2026-11-02T14:47:08+00:00","source_repo":"znyupup/ai-video-editing-skill","source_path":"SKILL.md","source_commit":"b6429ab550a64c595dc68d42c47cf2b489a50619","source_updated_at":"2026-11-02T14:46:04+00:00","stars":0,"proxy_score":null,"score":null,"grade":null,"dimensions":{},"evaluation":{},"installs":0,"platforms":[],"formats":[],"category_slug":"packaging","category_name":"\u526a\u8f91\u4e0e\u5305\u88c5","quality":{"review_type":"text_and_package_review","readiness":"requires_setup","reviewed_at":"2026-11-02T14:46:04+00:00","review_summary":"\u4e03\u9636\u6bb5\u6d41\u7a0b\u4e0e\u5927\u91cf\u7f16\u7801\u3001\u62fc\u63a5\u3001\u97f3\u753b\u540c\u6b65\u6ce8\u610f\u4e8b\u9879\u5177\u4f53\u53ef\u6267\u884c\uff0c\u9a8c\u6536\u6e05\u5355\u660e\u786e\uff1b\u4f46\u811a\u672c\u672a\u8fd0\u884c\u3001\u6a21\u578b\u4e0eAPI\u9700\u81ea\u5907\uff0c\u5c5e\u9700\u914d\u7f6e\u73af\u5883\u540e\u4f7f\u7528\u7684\u6280\u80fd\u3002","cost_notes":"\u89c6\u89c9\u6a21\u578bAPI\u4e0eAI\u97f3\u4e50\u751f\u6210\u53ef\u80fd\u4ea7\u751f\u8d39\u7528\uff0c\u9700\u7528\u6237\u81ea\u884c\u786e\u8ba4\uff1bASR\u6a21\u578b\u4e0b\u8f7d\u5360\u7528\u5e26\u5bbd\u4e0e\u78c1\u76d8\uff0c\u672a\u505a\u62a5\u4ef7\u3002","source_sha256":"913d45d6b15f76b2a7cfd32d1442aa5e19d29a6b8b057c0fb108953a509eb539","prerequisites":["\u7cfb\u7edf\u5b89\u88c5ffmpeg\u4e0effprobe","Python 3.9+\uff0c\u5b89\u88c5openai-whisper\u6216funasr+modelscope+torchaudio\uff0c\u4ee5\u53caPillow","\u89c6\u89c9\u7406\u89e3\u6a21\u578bAPI\uff08\u5982\u667a\u8c31GLM-4.6V-Flash\u3001GPT-4o\u3001Qwen-VL\uff09\u53ca\u5bc6\u94a5","\u9996\u6b21\u8fd0\u884c\u9700\u4e0b\u8f7dASR\u6a21\u578b\uff08whisper medium\u7ea61.4GB\u6216FunASR\u7ea61.1GB\uff09","\u53ef\u9009yt-dlp\u3001scenedetect\u3001AI\u97f3\u4e50\u751f\u6210\u5de5\u5177"],"checks":["\u6838\u5bf9\u6bcf\u6bb5\u7247\u6bb5\u97f3\u89c6\u9891\u65f6\u957f\u5dee\u662f\u5426\u5c0f\u4e8e0.05s\uff0c\u8d85\u5dee\u662f\u5426\u5df2\u7528apad\u8865\u9f50","\u62d6\u5230\u6210\u54c1\u540e\u534a\u6bb5\u68c0\u67e5\u97f3\u753b\u662f\u5426\u540c\u6b65\uff0c\u786e\u8ba4\u97f3\u9891\u672a\u88ab-shortest\u622a\u65ad","\u7528ffmpeg -i output.mp4 -f null - \u68c0\u67e5\u5b9e\u9645\u5e27\u6570\u4e0e\u9ed1\u5e27\u60c5\u51b5","\u6838\u5bf9edit_plan\u4e2d\u6bcf\u4e2aclip\u7684start/end\u662f\u5426\u901a\u8fc7\u8bed\u97f3\u622a\u65ad\u4e8c\u6b21\u6821\u9a8c","\u6253\u5f00dashboard.html\u786e\u8ba4\u7d20\u6750\u7f29\u7565\u56fe\u4e0e\u6210\u54c1QC\u5e27\u53ef\u6b63\u5e38\u663e\u793a"],"limitations":["\u5305\u5185\u811a\u672c\u672a\u8fd0\u884c\uff0cgen_dashboard.py\u4e0egen_storyboard.py\u7684\u5b9e\u9645\u8f93\u51fa\u672a\u9a8c\u8bc1","ASR\u4e0e\u89c6\u89c9\u6a21\u578b\u6548\u679c\u4f9d\u8d56\u5177\u4f53\u6a21\u578b\u4e0e\u7d20\u6750\uff0c\u672a\u505a\u6548\u679c\u5b9e\u6d4b","\u526a\u6620\u7b49\u7b2c\u4e09\u65b9\u7f16\u8f91\u5668\u4e0d\u53ef\u7a0b\u5e8f\u5316\u64cd\u4f5c\uff0c\u4ec5\u8d70ffmpeg\u8def\u7ebf","BGM\u751f\u6210\u4f9d\u8d56\u5916\u90e8\u5de5\u5177\uff0c\u672a\u968f\u5305\u63d0\u4f9b"],"issues":["SKILL.md\u4e2d\u89c6\u89c9API\u793a\u4f8b\u542b\u5360\u4f4d\u5bc6\u94a5\u4e0e\u7aef\u70b9\uff0c\u9700\u7528\u6237\u81ea\u884c\u66ff\u6362\uff0c\u672a\u9a8c\u8bc1\u53ef\u7528\u6027","gen_dashboard.py\u652f\u6301\u4e24\u79cd\u5206\u6790\u683c\u5f0f\uff0c\u4f46\u683c\u5f0f\u517c\u5bb9\u6027\u672a\u5b9e\u6d4b","\u90e8\u5206ffmpeg\u547d\u4ee4\u4e3a\u7247\u6bb5\u793a\u4f8b\uff0c\u5b8c\u6574\u4e32\u8054\u811a\u672c\u672a\u968f\u5305\u63d0\u4f9b"],"missing_files":[]},"quality_url":"https://shiliuai.cn/api/v2/skills/znyupup-ai-video-editing-skill-bb97b3/files/SHILIU-QUALITY.md","usage_guide":{"task":"\u628a\u4e00\u6279\u624b\u673a\u62cd\u6444\u7684\u65c5\u884c\u7d20\u6750\uff0c\u7ecf\u7d20\u6750\u5206\u6790\u3001\u53d9\u4e8b\u7f16\u6392\u4e0effmpeg\u6e32\u67d3\uff0c\u526a\u6210\u7ed3\u6784\u5b8c\u6574\u7684Vlog\u6210\u54c1\u3002","inputs":"\u539f\u59cb\u89c6\u9891\u7d20\u6750\u6587\u4ef6\u5939\uff1b\u7cfb\u7edf\u7ea7ffmpeg\u4e0effprobe\uff1bPython 3.9+\u53cawhisper\u6216FunASR\uff08\u542btorchaudio\uff09\u3001Pillow\uff1b\u4e00\u4e2a\u652f\u6301OpenAI Chat Completions\u683c\u5f0f\u3001\u53ef\u63a5\u6536base64\u56fe\u7247\u7684\u89c6\u89c9\u6a21\u578bAPI\u53ca\u5bc6\u94a5\uff1b\u53ef\u9009yt-dlp\u3001scenedetect\u7528\u4e8e\u53c2\u8003\u7814\u7a76\uff1b\u53ef\u9009AI\u97f3\u4e50\u5de5\u5177\u751f\u6210BGM\u3002","outputs":"clip_analysis.json\u7d20\u6750\u5206\u6790\u3001edit_plan.json\u4e0e\u4fee\u590d\u540e\u7684\u526a\u8f91\u65b9\u6848\u3001dashboard.html\u9884\u89c8\u9875\u3001\u5206\u6bb5\u89c6\u9891\u4e0e\u6807\u9898\u53e0\u52a0\u3001BGM\u6df7\u97f3\u540e\u7684final.mp4\u6210\u54c1\uff0c\u4ee5\u53ca\u9a8c\u6536\u6e05\u5355\u6838\u5bf9\u7ed3\u679c\u3002","example_input":"footage/ \u91cc\u6709\u8fd9\u6b21\u53bb\u54c8\u5c14\u6ee8\u73a9\u768430\u6761\u624b\u673a\u7d20\u6750\uff0c\u5e2e\u6211\u81ea\u52a8\u526a\u6210\u4e00\u4e2a3\u5206\u949f\u5de6\u53f3\u7684\u65c5\u884cVlog\uff0c\u65c1\u767d\u4fdd\u7559\uff0cBGM\u8f7b\u4e00\u70b9\u3002","example_output":"\u4ea4\u4ed8\u7ed3\u6784\u793a\u610f\uff08\u975e\u771f\u5b9e\u7ed3\u679c\uff09\uff1aanalysis/clip_analysis.json\u3001edit_plan.json\u4e0eedit_plan_fixed.json\u3001output/dashboard.html\u3001output/segments/\u3001output/sections/\u3001output/final.mp4\uff1b\u9644\u9a8c\u6536\u6e05\u5355\u52fe\u9009\u60c5\u51b5\u4e0e\u672a\u89e3\u51b3\u9879\u8bf4\u660e\u3002"},"read_url":"https://shiliuai.cn/api/v2/skills/znyupup-ai-video-editing-skill-bb97b3","skill_url":"https://shiliuai.cn/api/v2/skills/znyupup-ai-video-editing-skill-bb97b3/files/SKILL.md","download_url":"https://shiliuai.cn/api/v2/skills/znyupup-ai-video-editing-skill-bb97b3/download","install_url":"https://shiliuai.cn/api/v2/skills/znyupup-ai-video-editing-skill-bb97b3/install","files":[{"path":"LICENSE","bytes":1098,"url":"https://shiliuai.cn/api/v2/skills/znyupup-ai-video-editing-skill-bb97b3/files/LICENSE"},{"path":"README.md","bytes":10291,"url":"https://shiliuai.cn/api/v2/skills/znyupup-ai-video-editing-skill-bb97b3/files/README.md"},{"path":"SHILIU-QUALITY.md","bytes":2134,"url":"https://shiliuai.cn/api/v2/skills/znyupup-ai-video-editing-skill-bb97b3/files/SHILIU-QUALITY.md"},{"path":"SKILL.md","bytes":36995,"url":"https://shiliuai.cn/api/v2/skills/znyupup-ai-video-editing-skill-bb97b3/files/SKILL.md"},{"path":"examples/clip_analysis.json","bytes":5040,"url":"https://shiliuai.cn/api/v2/skills/znyupup-ai-video-editing-skill-bb97b3/files/examples/clip_analysis.json"},{"path":"examples/edit_plan.json","bytes":2268,"url":"https://shiliuai.cn/api/v2/skills/znyupup-ai-video-editing-skill-bb97b3/files/examples/edit_plan.json"},{"path":"examples/project_structure.md","bytes":3145,"url":"https://shiliuai.cn/api/v2/skills/znyupup-ai-video-editing-skill-bb97b3/files/examples/project_structure.md"},{"path":"scripts/gen_dashboard.py","bytes":32331,"url":"https://shiliuai.cn/api/v2/skills/znyupup-ai-video-editing-skill-bb97b3/files/scripts/gen_dashboard.py"},{"path":"scripts/gen_storyboard.py","bytes":10864,"url":"https://shiliuai.cn/api/v2/skills/znyupup-ai-video-editing-skill-bb97b3/files/scripts/gen_storyboard.py"},{"path":"shiliu-quality.json","bytes":1903,"url":"https://shiliuai.cn/api/v2/skills/znyupup-ai-video-editing-skill-bb97b3/files/shiliu-quality.json"},{"path":"shiliu-source.json","bytes":259,"url":"https://shiliuai.cn/api/v2/skills/znyupup-ai-video-editing-skill-bb97b3/files/shiliu-source.json"},{"path":"templates/edit_plan_prompt.md","bytes":3419,"url":"https://shiliuai.cn/api/v2/skills/znyupup-ai-video-editing-skill-bb97b3/files/templates/edit_plan_prompt.md"},{"path":"usage.json","bytes":1083,"url":"https://shiliuai.cn/api/v2/skills/znyupup-ai-video-editing-skill-bb97b3/files/usage.json"}],"content":"---\nname: vlog-auto-edit\ndescription: AI Agent\u81ea\u52a8\u526a\u8f91\u65c5\u884cVlog\u7684\u5b8c\u6574\u5de5\u4f5c\u6d41\u3002\u4ece\u539f\u59cb\u7d20\u6750\u5230\u6210\u54c1\u89c6\u9891\uff0c\u7cfb\u7edf\u7ea7\u53ea\u9700ffmpeg\uff0c\u5176\u4f59\u5728Python venv\u5185\u5b8c\u6210\u3002by nyx\u7814\u7a76\u6240 (GitHub @znyupup \u00b7 B\u7ad9/\u5c0f\u7ea2\u4e66 @nyx\u7814\u7a76\u6240)\nversion: 1.1.0\ntags: [video-editing, vlog, ffmpeg, whisper, funasr, automation]\nauthor: nyx\u7814\u7a76\u6240 (https://github.com/znyupup)\nstatus: stable\n---\n\n# AI Agent \u81ea\u52a8\u526a\u8f91\u65c5\u884c Vlog\n\n**Author:** nyx\u7814\u7a76\u6240 \u00b7 [GitHub](https://github.com/znyupup) \u00b7 [B\u7ad9 @nyx\u7814\u7a76\u6240](https://space.bilibili.com/4330525) \u00b7 \u5c0f\u7ea2\u4e66 @nyx\u7814\u7a76\u6240 \u00b7 [X @znyupup_music](https://x.com/znyupup_music)\n\n> \u628a\u4e00\u5806\u624b\u673a\u62cd\u7684\u65c5\u884c\u7d20\u6750\uff0c\u7528AI\u81ea\u52a8\u526a\u6210\u4e00\u4e2a\u5b8c\u6574vlog\u3002\n> \u6700\u5c0f\u4f9d\u8d56\uff1affmpeg + Python(whisper\u6216FunASR+Pillow) + \u89c6\u89c9API\u3002\u7cfb\u7edf\u7ea7\u53ea\u88c5ffmpeg\uff0c\u5176\u4f59pip\u88c5\u3002\n\n## \u89e6\u53d1\u6761\u4ef6\n\n- \u7528\u6237\u6709\u4e00\u6279\u65c5\u884c/\u65e5\u5e38\u89c6\u9891\u7d20\u6750\uff0c\u60f3\u81ea\u52a8\u526a\u6210vlog\n- \u7528\u6237\u8bf4\"\u5e2e\u6211\u526a\u4e2a\u89c6\u9891\"\u3001\"\u81ea\u52a8\u526a\u8f91\"\u3001\"vlog\u526a\u8f91\"\n\n## \u524d\u7f6e\u8981\u6c42\n\n### 1. \u7cfb\u7edf\u5de5\u5177\n\n| \u5de5\u5177 | \u7528\u9014 | \u5b89\u88c5 |\n|------|------|------|\n| ffmpeg | \u89c6\u9891\u88c1\u526a/\u7f16\u7801/\u62fc\u63a5/\u62bd\u5e27/\u97f3\u91cf\u68c0\u6d4b | `brew install ffmpeg` (macOS) / `apt install ffmpeg` (Linux) |\n| Python 3.9+ | \u811a\u672c\u80f6\u6c34 | macOS/Linux \u7cfb\u7edf\u81ea\u5e26 |\n\n### 2. Python\u4f9d\u8d56\uff08\u5148\u68c0\u6d4b\uff0c\u5df2\u6709\u5219\u8df3\u8fc7\uff09\n\n**\u5148\u68c0\u6d4b\u7cfb\u7edf\u662f\u5426\u5df2\u5b89\u88c5\u6240\u9700\u5305\uff0c\u4e0d\u8981\u65e0\u8111\u521b\u5efa venv\uff1a**\n\n```bash\n# \u68c0\u6d4b whisper\npython3 -c \"import whisper; print('\u2705 whisper\u5df2\u5b89\u88c5:', whisper.__file__)\"\n\n# \u68c0\u6d4b Pillow\npython3 -c \"from PIL import Image; print('\u2705 Pillow\u5df2\u5b89\u88c5')\"\n```\n\n**\u26a0 \u91cd\u8981\uff1a\u4e0d\u8981\u7528 `2>/dev/null` \u5403\u6389\u9519\u8bef\uff01\u8981\u770b\u5230\u5b9e\u9645\u62a5\u9519\u624d\u80fd\u5224\u65ad\u662f\u771f\u6ca1\u88c5\u8fd8\u662fPATH\u95ee\u9898\u3002**\n\n**\u26a0 macOS \u6ce8\u610f\uff1a** \u68c0\u67e5\u7cfb\u7edf\u5df2\u5b89\u88c5\u7684\u5305\u518d\u51b3\u5b9a\u662f\u5426\u9700\u8981 venv\u3002\u5982\u679c\u7cfb\u7edf python3 \u5df2\u7ecf\u80fd `import whisper` \u548c `from PIL import Image`\uff0c\u76f4\u63a5\u7528\u5c31\u884c\uff0c\u4e0d\u8981\u76f2\u76ee\u521b\u5efa venv \u5bfc\u81f4\u627e\u4e0d\u5230\u5df2\u6709\u7684\u5305\u3002\n\n**\u4ec5\u5728\u68c0\u6d4b\u4e0d\u901a\u8fc7\u65f6\u624d\u5b89\u88c5\uff1a**\n\n```bash\n# \u65b9\u6848A: \u76f4\u63a5\u88c5\u5230\u7528\u6237\u73af\u5883\uff08\u63a8\u8350\uff09\npip install openai-whisper Pillow\n\n# \u65b9\u6848B: \u5982\u679c\u7528\u6237\u73af\u5883\u6709\u51b2\u7a81\uff0c\u518d\u7528 venv\npython3 -m venv .venv && source .venv/bin/activate\npip install openai-whisper Pillow\n```\n\n**\u26a0 \u4e0d\u8981\u6bcf\u4e2a\u9879\u76ee\u90fd\u65b0\u5efa venv \u91cd\u88c5\u4e00\u904d\uff01whisper\u6a21\u578b\u6587\u4ef61.4GB\uff0cpip install\u4e5f\u8981\u51e0\u5206\u949f\u3002**\n\n**\u26a0 ASR\u5f15\u64ce\u4e8c\u9009\u4e00\uff1aWhisper \u6216 FunASR**\n\n\u672c\u5de5\u4f5c\u6d41\u652f\u6301\u4e24\u4e2aASR\u5f15\u64ce\uff0c\u4f18\u5148\u7528 whisper\uff0cwhisper \u88c5\u4e0d\u4e0a\u5c31\u7528 FunASR\uff1a\n\n| | Whisper (OpenAI) | FunASR (\u963f\u91cc\u8fbe\u6469\u9662) |\n|---|---|---|\n| \u5b89\u88c5 | `pip install openai-whisper` | `pip install funasr modelscope torchaudio` |\n| \u6a21\u578b | medium (1.4GB, \u4eceGitHub\u4e0b\u8f7d) | paraformer-zh (1.05GB, \u4eceModelScope\u4e0b\u8f7d) |\n| \u9644\u52a0\u6a21\u578b | \u65e0 | VAD\u6a21\u578b + \u6807\u70b9\u6a21\u578b (\u9996\u6b21\u8fd0\u884c\u81ea\u52a8\u4e0b\u8f7d, \u5171~50MB) |\n| \u4e2d\u6587\u6548\u679c | \u597d | \u597d\uff08\u4e0ewhisper medium\u76f8\u5f53\uff0c\u7ec6\u8282\u7565\u591a\uff09 |\n| \u901f\u5ea6 | 14s\u97f3\u9891 \u2248 5-10s (CPU) | 14s\u97f3\u9891 \u2248 1.9s (CPU, RTF=0.132) \u26a1 |\n| \u65f6\u95f4\u6233\u7c92\u5ea6 | \u53e5\u7ea7\u522b (\u6bcf\u53e5\u8bdd\u4e00\u4e2astart/end) | \u5b57\u7ea7\u522b (\u6bcf\u4e2a\u5b57\u4e00\u4e2a\u65f6\u95f4\u6233) |\n| \u6a21\u578b\u4e0b\u8f7d\u6e90 | GitHub (\u56fd\u5185\u4e0d\u7a33\u5b9a\uff0c\u5bb9\u6613\u5931\u8d25) | ModelScope (\u56fd\u5185\u6781\u5feb\uff0c~15MB/s) |\n| \u9690\u6027\u4f9d\u8d56 | torch | torch + torchaudio (\u26a0 \u5fc5\u987b\u989d\u5916\u88c5) |\n\n**\u68c0\u6d4b\u5df2\u5b89\u88c5\u7684ASR\u5f15\u64ce\uff1a**\n```bash\npython3 -c \"import whisper; print('\u2705 whisper\u53ef\u7528')\" 2>/dev/null \\\n  || python3 -c \"from funasr import AutoModel; print('\u2705 funasr\u53ef\u7528')\" 2>/dev/null \\\n  || echo \"\u274c \u6ca1\u6709\u53ef\u7528\u7684ASR\u5f15\u64ce\uff0c\u9700\u8981\u5b89\u88c5\u4e00\u4e2a\"\n```\n\n**\u26a0 FunASR \u5b89\u88c5\u6ce8\u610f\u4e8b\u9879\uff1a**\n- \u5fc5\u987b\u88c5 torchaudio\uff01`pip install funasr modelscope` \u4e0d\u4f1a\u81ea\u52a8\u88c5 torchaudio\uff0cimport \u65f6\u4f1a\u62a5 `No module named 'torchaudio'`\n- \u6b63\u786e\u5b89\u88c5\u547d\u4ee4\uff1a`pip install funasr modelscope torchaudio`\n- \u9996\u6b21\u8fd0\u884c\u4f1a\u81ea\u52a8\u4ece ModelScope \u4e0b\u8f7d3\u4e2a\u6a21\u578b\uff08ASR + VAD + \u6807\u70b9\uff09\uff0c\u603b\u5171\u7ea61.1GB\uff0c\u56fd\u5185\u901f\u5ea6\u5feb\n- \u6a21\u578b\u7f13\u5b58\u5728 `~/.cache/modelscope/hub/models/iic/` \u76ee\u5f55\u4e0b\n- import \u65f6\u4f1a\u6709 urllib3 \u7684 SSL \u8b66\u544a\uff08NotOpenSSLWarning\uff09\uff0c\u53ef\u5ffd\u7565\uff0c\u4e0d\u5f71\u54cd\u529f\u80fd\n\n**\u6807\u9898\u5361\u65b9\u6848\u81ea\u52a8\u9009\u62e9\uff1a**\n```bash\nffmpeg -filters 2>&1 | grep drawtext\n# \u6709drawtext \u2192 \u76f4\u63a5\u7528ffmpeg\uff0c\u4e0d\u9700\u8981Pillow\n# \u6ca1\u6709 \u2192 \u7528Pillow\u751f\u6210\u900f\u660ePNG\u518doverlay\uff08macOS brew ffmpeg\u901a\u5e38\u6ca1\u7f16freetype\uff09\n```\n\n**\u53ef\u9009\uff08\u53c2\u8003\u5206\u6790\u9636\u6bb5\u7528\uff09\uff1a**\n```bash\nwhich yt-dlp && echo \"\u2705 yt-dlp\u5df2\u5b89\u88c5\" || pip install yt-dlp\npython3 -c \"import scenedetect\" 2>/dev/null && echo \"\u2705 scenedetect\u5df2\u5b89\u88c5\" || pip install scenedetect\n```\n\n### 3. \u89c6\u89c9\u7406\u89e3\u6a21\u578b\uff08API\uff09\n\n\u9700\u8981\u4e00\u4e2a\u80fd\u7406\u89e3\u56fe\u50cf\u5185\u5bb9\u7684\u89c6\u89c9\u6a21\u578bAPI\uff0c\u7528\u4e8e\u5206\u6790\u7d20\u6750\u753b\u9762\u3002\n\n**\u8981\u6c42\uff1a**\n- \u652f\u6301 OpenAI Chat Completions \u683c\u5f0f\uff08messages + image_url\uff09\n- \u652f\u6301 base64 \u56fe\u7247\u8f93\u5165\uff08\u672c\u5730\u7d20\u6750\u62bd\u5e27\u540e\u7f16\u7801\u4e0a\u4f20\uff09\n- \u4e2d\u6587\u7406\u89e3\u80fd\u529b\uff08\u9700\u8981\u63cf\u8ff0\u753b\u9762\u5185\u5bb9\u3001\u6807\u6ce8\u955c\u5934\u7c7b\u578b\u7b49\uff09\n\n**\u63a8\u8350\u6a21\u578b\uff1a**\n\n| \u6a21\u578b | \u8d39\u7528 | \u8bf4\u660e |\n|------|------|------|\n| \u667a\u8c31 GLM-4.6V-Flash | \u514d\u8d39 | \u6ce8\u518c https://open.bigmodel.cn \u5373\u7528\uff0c\u4e2d\u6587\u7406\u89e3\u597d |\n| GPT-4o | \u4ed8\u8d39 | \u6548\u679c\u6700\u597d |\n| Qwen-VL | \u4ed8\u8d39 | \u963f\u91cc\u4e91\uff0c\u4e2d\u6587\u597d |\n\n**\u8c03\u7528\u793a\u4f8b\uff1a**\n\n```python\nimport base64, json, urllib.request\n\nAPI_URL = 'YOUR_VISION_API_ENDPOINT'   # \u66ff\u6362\u4e3a\u4f60\u7684\u89c6\u89c9\u6a21\u578b\u7aef\u70b9\nAPI_KEY='***'               # \u66ff\u6362\u4e3a\u4f60\u7684API Key\nMODEL = 'YOUR_MODEL_NAME'             # \u66ff\u6362\u4e3a\u4f60\u7684\u6a21\u578b\u540d\n\nwith open('frame.jpg', 'rb') as f:\n    img_b64 = base64.b64encode(f.read()).decode()\n\npayload = {\n    'model': MODEL,\n    'messages': [{'role': 'user', 'content': [\n        {'type': 'image_url', 'image_url': {'url': f'data:image/jpeg;base64,{img_b64}'}},\n        {'type': 'text', 'text': '\u7b80\u6d01\u63cf\u8ff0\u753b\u9762\u5185\u5bb9\uff0c\u6807\u6ce8\uff1a\u955c\u5934\u7c7b\u578b(\u8fdc/\u5168/\u4e2d/\u8fd1/\u7279\u5199)\u3001\u62cd\u6444\u624b\u6cd5(\u56fa\u5b9a/\u624b\u6301/\u79fb\u52a8)\u3001\u753b\u9762\u6c1b\u56f4\u3002\u683c\u5f0f\uff1a\u5185\u5bb9|\u7c7b\u578b|\u624b\u6cd5|\u6c1b\u56f4'}\n    ]}],\n    'max_tokens': 200\n}\n\nreq = urllib.request.Request(API_URL, json.dumps(payload).encode(),\n    {'Content-Type': 'application/json', 'Authorization': f'Bearer {API_KEY}'})\nwith urllib.request.urlopen(req, timeout=30) as resp:\n    result = json.loads(resp.read())\n    print(result['choices'][0]['message']['content'])\n```\n\n**\u26a0 \u89c6\u89c9API\u8c03\u7528\u6ce8\u610f\u4e8b\u9879\uff1a**\n- \u8f93\u5165\u683c\u5f0f\uff1affmpeg\u62bd\u5e27\u4e3aJPEG \u2192 base64\u7f16\u7801 \u2192 `data:image/jpeg;base64,{b64}` \u653e\u5165 image_url\n- \u62bd\u5e27\u5c3a\u5bf8\uff1a\u4e0d\u9700\u8981\u539f\u59cb\u5206\u8fa8\u7387\uff0c\u7f29\u5230 720p \u5373\u53ef\uff08\u8282\u7701\u4e0a\u4f20\u5e26\u5bbd\u548ctoken\uff09\n- \u7aef\u70b9\u533a\u5206\uff1a\u6709\u4e9b\u5e73\u53f0\u89c6\u89c9\u6a21\u578b\u548c\u6587\u672c\u6a21\u578b\u7aef\u70b9\u4e0d\u540c\uff0c\u786e\u8ba4\u7528\u5bf9\n- \u9650\u6d41\uff1a\u9ad8\u5cf0\u65f6\u6bb5\u5bb9\u6613\u88ab\u9650\u6d41\uff0c\u52a0 retry + sleep\n\n### \u4e0d\u9700\u8981\u7684\uff08\u8e29\u8fc7\u7684\u5751\uff09\n\n- \u274c \u526a\u6620/CapCut \u2014 v10.4+ \u9879\u76ee\u6587\u4ef6\u52a0\u5bc6\uff0c\u65e0\u6cd5\u7a0b\u5e8f\u5316\u64cd\u4f5c\n- \u274c moviepy \u2014 ffmpeg\u547d\u4ee4\u884c\u5df2\u591f\u7528\uff0c\u591a\u4e00\u5c42\u62bd\u8c61\u53cd\u800c\u4e0d\u7075\u6d3b\n- \u274c capcut-cli (GitHub) \u2014 \u57fa\u4e8e\u660e\u6587JSON\uff0c\u5bf9\u52a0\u5bc6\u540e\u7684\u65b0\u7248\u65e0\u6548\n- \u274c ImageMagick \u2014 Pillow\u5df2\u591f\u7528\uff0c\u4e0d\u9700\u8981\u518d\u88c5\u4e00\u4e2a\u56fe\u50cf\u5de5\u5177\n\n## \u5de5\u4f5c\u6d41\uff087\u4e2a\u9636\u6bb5\uff09\n\n### \u9636\u6bb51: \u7d20\u6750\u76d8\u70b9\n\n\u76ee\u6807\uff1a\u4e86\u89e3\u4f60\u6709\u4ec0\u4e48\u7d20\u6750\u3002\n\n```bash\n# \u6279\u91cf\u83b7\u53d6\u7d20\u6750\u4fe1\u606f\nfor f in footage/*.{MOV,mp4,MP4}; do\n  echo \"=== $f ===\"\n  ffprobe -v quiet -print_format json -show_format -show_streams \"$f\" \\\n    | python3 -c \"import json,sys; d=json.load(sys.stdin); \\\n      s=d['streams'][0]; f=d['format']; \\\n      print(f\\\"  \u65f6\u957f: {float(f['duration']):.1f}s\\\"); \\\n      print(f\\\"  \u5206\u8fa8\u7387: {s['width']}x{s['height']}\\\"); \\\n      print(f\\\"  \u7f16\u7801: {s['codec_name']}\\\")\"\ndone\n```\n\n\u8f93\u51fa\u4e00\u4efd\u7d20\u6750\u6e05\u5355\uff1a\u6570\u91cf\u3001\u603b\u65f6\u957f\u3001\u5206\u8fa8\u7387\u5206\u5e03\u3001\u62cd\u6444\u65f6\u95f4\u8303\u56f4\u3002\n\n### \u9636\u6bb52: \u53c2\u8003\u7814\u7a76\uff08\u53ef\u9009\u4f46\u5f3a\u70c8\u5efa\u8bae\uff09\n\n\u76ee\u6807\uff1a\u5efa\u7acb\"\u597dvlog\u957f\u4ec0\u4e48\u6837\"\u7684\u8ba4\u77e5\u3002\u4e0d\u505a\u8fd9\u6b65\u76f4\u63a5\u526a\uff0c\u6548\u679c\u4f1a\u5f88\u5dee\u3002\n\n**\u6b65\u9aa4\uff1a**\n1. \u627e2-3\u4e2a\u540c\u7c7b\u578b\u4f18\u8d28vlog\uff08B\u7ad9/YouTube\uff09\n2. `yt-dlp` \u4e0b\u8f7d720p\u89c6\u9891 + \u97f3\u9891\n3. `whisper` \u8f6c\u5f55\u65c1\u767d\uff08\u63d0\u53d6\u53d9\u4e8b\u7ed3\u6784\uff09\n4. `scenedetect` \u68c0\u6d4b\u955c\u5934\u5207\u6362\uff08\u7edf\u8ba1\u8282\u594f\uff09\n5. `ffmpeg` \u62bd\u5173\u952e\u5e27 + \u89c6\u89c9API\u5206\u6790\uff08\u7406\u89e3\u753b\u9762\u6784\u6210\uff09\n\n**\u5df2\u9a8c\u8bc1\u7684\u89c4\u5f8b\uff1a**\n\n- \u955c\u5934\u8282\u594f: \u5e73\u57473-4\u79d2/\u955c\u5934\u662f\u9ec4\u91d1\u8282\u594f\n- \u5185\u5bb9\u914d\u6bd4: \u7f8e\u98df40-45% + \u98ce\u666f30% + \u4eba\u726915% + \u65e5\u5e3815%\n- \u955c\u5934\u7c7b\u578b: \u8fd1\u666f/\u4e2d\u666f\u4e3a\u4e3b(60-65%)\uff0c\u5168\u666f/\u8fdc\u666f\u7a7f\u63d2(35-40%)\n- \u53d9\u4e8b\u7ed3\u6784: \u7cbe\u5f69\u7247\u5934 \u2192 \u51fa\u53d1/\u5230\u8fbe \u2192 \u6309\u5730\u70b9\u4e32\u8054 \u2192 \u60c5\u611f\u5347\u534e\u6536\u5c3e\n- \u957f\u77ed\u4ea4\u66ff: \u77ed\u955c\u5934(<2s)\u505a\u8499\u592a\u5947\uff0c\u957f\u955c\u5934(>8s)\u505a\u53d9\u4e8b\n\n**\u526a\u8f91\u6559\u7a0b\u8981\u70b9\uff1a**\n1. \u627e\u4fa7\u91cd\u70b9\uff0c\u4e0d\u8981\u6d41\u6c34\u8d26\n2. \u4e09\u5e55\u5f0f\u7ed3\u6784: 25%\u60ac\u5ff5 + 50%\u53d1\u5c55 + 25%\u6536\u5c3e\n3. \u7cbe\u5f69\u653e\u7247\u5934\n4. \u60c5\u611f\u536051%\uff08Walter Murch\u516d\u6cd5\u5219\uff09\n\n### \u9636\u6bb53: \u7d20\u6750\u4e09\u7ef4\u5206\u6790\n\n\u76ee\u6807\uff1a\u8ba9AI\u7406\u89e3\u6bcf\u6761\u7d20\u6750\u7684\u5185\u5bb9\u3002\u6bcf\u6761\u7d20\u6750\u505a\u4e09\u7ef4\u5206\u6790\uff1a\n\n**a) \u97f3\u9891\u5206\u6790 \u2014 ASR\u8f6c\u5f55\uff08Whisper \u6216 FunASR\uff09**\n```bash\n# \u63d0\u53d6\u97f3\u9891\uff0816kHz\u5355\u58f0\u9053WAV\uff0c\u4e24\u4e2a\u5f15\u64ce\u901a\u7528\u7684\u6700\u4f73\u8f93\u5165\u683c\u5f0f\uff09\nffmpeg -i footage/INPUT.MOV -vn -acodec pcm_s16le -ar 16000 -ac 1 /tmp/audio.wav\n```\n\n**\u65b9\u68481: Whisper\u8f6c\u5f55\uff08\u4f18\u5148\u4f7f\u7528\uff09**\n```python\nimport whisper, json\nmodel = whisper.load_model('medium')\nresult = model.transcribe('/tmp/audio.wav', language='zh')\nsegments = [{\"start\": s['start'], \"end\": s['end'], \"text\": s['text']} for s in result['segments']]\n# \u8f93\u51fa: [{\"start\": 0.5, \"end\": 2.3, \"text\": \"\u6211\u4eec\u73b0\u5728\u5728\u4e2d\u592e\u5927\u8857\"}, ...]\n```\n\n**\u65b9\u68482: FunASR\u8f6c\u5f55\uff08Whisper\u88c5\u4e0d\u4e0a\u65f6\u7684\u66ff\u4ee3\uff09**\n```python\nimport warnings\nwarnings.filterwarnings(\"ignore\")\nfrom funasr import AutoModel\n\nmodel = AutoModel(\n    model=\"paraformer-zh\",      # Paraformer-large\uff0c\u4e2d\u6587ASR\u4e3b\u6a21\u578b(1.05GB)\n    vad_model=\"fsmn-vad\",       # \u8bed\u97f3\u6d3b\u52a8\u68c0\u6d4b\uff0c\u81ea\u52a8\u5207\u5206\u9759\u97f3\u6bb5\n    punc_model=\"ct-punc\",       # \u81ea\u52a8\u52a0\u6807\u70b9\n    log_level=\"ERROR\",\n    disable_update=True         # \u8df3\u8fc7\u7248\u672c\u68c0\u67e5\uff0c\u52a0\u5feb\u542f\u52a8\n)\n\nresult = model.generate(input=\"/tmp/audio.wav\")\ntext = result[0][\"text\"]        # \u5b8c\u6574\u8bc6\u522b\u6587\u672c\uff08\u5df2\u52a0\u6807\u70b9\uff09\ntimestamps = result[0].get(\"timestamp\", [])  # \u5b57\u7ea7\u522b\u65f6\u95f4\u6233 [[start_ms, end_ms], ...]\n\n# \u26a0 FunASR\u65f6\u95f4\u6233\u662f\u5b57\u7ea7\u522b(\u6beb\u79d2)\uff0c\u9700\u8981\u805a\u5408\u6210\u53e5\u7ea7\u522b\u624d\u80fd\u548cwhisper\u683c\u5f0f\u7edf\u4e00\n# \u805a\u5408\u7b56\u7565\uff1a\u6309\u6807\u70b9\u7b26\u53f7\uff08\u3002\uff01\uff1f\uff0c\uff09\u5207\u5206\u6210\u53e5\u5b50\nsegments = []\nif timestamps and text:\n    puncs = set(\"\u3002\uff01\uff1f\uff0c,.!?\")\n    current_start = timestamps[0][0] / 1000.0\n    current_text = \"\"\n    chars = list(text)\n    for i, (char, ts) in enumerate(zip(chars, timestamps)):\n        current_text += char\n        if char in puncs or i == len(chars) - 1:\n            segments.append({\n                \"start\": round(current_start, 1),\n                \"end\": round(ts[1] / 1000.0, 1),\n                \"text\": current_text.strip()\n            })\n            if i < len(chars) - 1:\n                current_start = timestamps[i + 1][0] / 1000.0\n            current_text = \"\"\n# \u8f93\u51fa\u683c\u5f0f\u4e0ewhisper\u4e00\u81f4: [{\"start\": 0.6, \"end\": 2.9, \"text\": \"\u6211\u4eec\u73b0\u5728\u5728\u4e2d\u592e\u5927\u8857\u7684\u5165\u53e3\uff0c\"}, ...]\n```\n\n**\u26a0 FunASR \u8f93\u51fa\u5dee\u5f02\u6ce8\u610f\uff1a**\n- FunASR \u8fd4\u56de\u7684\u662f\u5b8c\u6574\u6587\u672c + \u5b57\u7ea7\u522b\u65f6\u95f4\u6233\uff0c\u4e0d\u50cf whisper \u76f4\u63a5\u7ed9\u53e5\u7ea7\u522b segments\n- \u4e0a\u9762\u7684\u805a\u5408\u4ee3\u7801\u628a\u5b57\u7ea7\u522b\u65f6\u95f4\u6233\u6309\u6807\u70b9\u5207\u5206\u6210\u53e5\u5b50\uff0c\u8f93\u51fa\u683c\u5f0f\u4e0e whisper \u5bf9\u9f50\n- \u5982\u679c\u7d20\u6750\u6ca1\u6709\u8bed\u97f3\uff0cFunASR \u8fd4\u56de\u7a7a\u6587\u672c `\"\"`\uff0ctimestamps \u4e3a\u7a7a `[]`\n- FunASR \u901f\u5ea6\u6bd4 whisper \u5feb 3-5x\uff0814s\u97f3\u9891\u53ea\u97001.9s\uff09\uff0c\u6279\u91cf\u5904\u7406\u65f6\u4f18\u52bf\u660e\u663e\n\n**b) \u97f3\u91cf\u68c0\u6d4b \u2014 ffmpeg**\n```bash\nffmpeg -i footage/INPUT.MOV -af volumedetect -f null /dev/null 2>&1 | grep volume\n# mean_volume: \u5e73\u5747\u97f3\u91cf(dB)  max_volume: \u5cf0\u503c\u97f3\u91cf(dB)\n# mean < -40dB \u57fa\u672c\u65e0\u58f0  mean > -20dB \u6709\u660e\u663e\u58f0\u97f3/\u8bed\u97f3\n```\n\n**c) \u89c6\u89c9\u5206\u6790 \u2014 ffmpeg\u62bd\u5e27 + \u89c6\u89c9\u7406\u89e3\u6a21\u578b**\n```bash\n# \u62bd\u5e27\u7b56\u7565\uff08\u6309\u65f6\u957f\u5206\u6bb5\uff09\n# \u226420s \u2192 3\u5e27(\u9996/\u4e2d/\u5c3e)\n# 20-60s \u2192 5\u5e27\n# >60s \u2192 \u6bcf15s\u4e00\u5e27\n\n# \u62bd\u9996/\u4e2d/\u5c3e\u4e09\u5e27\u793a\u4f8b\uff08\u7f29\u5230720p\u8282\u7701\u5e26\u5bbd\uff09\nffmpeg -i footage/INPUT.MOV -vf \"select=eq(n\\,0),scale=1280:-1\" -frames:v 1 -q:v 2 frame_start.jpg\nffmpeg -i footage/INPUT.MOV -vf \"select=eq(n\\,MIDDLE),scale=1280:-1\" -frames:v 1 -q:v 2 frame_mid.jpg\nffmpeg -i footage/INPUT.MOV -vf \"reverse,scale=1280:-1\" -frames:v 1 -q:v 2 frame_end.jpg\n```\n\n**d) \u8f93\u51fa\u683c\u5f0f**\uff08\u6bcf\u6761\u7d20\u6750\u4e00\u4e2aJSON\uff09\n```json\n{\n  \"filename\": \"clip_001.MOV\",\n  \"duration\": 15.3,\n  \"resolution\": \"1920x1080\",\n  \"visual\": [\n    {\"time\": \"0:00\", \"description\": \"...\", \"shot_type\": \"\u8fd1\u666f\", \"camera\": \"\u624b\u6301\", \"mood\": \"\u6e29\u99a8\"}\n  ],\n  \"audio\": {\n    \"has_speech\": true,\n    \"mean_volume_db\": -20.5,\n    \"max_volume_db\": -3.2,\n    \"transcript\": [{\"start\": 0.5, \"end\": 2.3, \"text\": \"...\"}]\n  }\n}\n```\n\n### \u9636\u6bb53.5: \u7d20\u6750\u7cbe\u4fee\u9884\u5904\u7406\n\n\u76ee\u6807\uff1a\u5728\u7ed9LLM\u4e4b\u524d\uff0c\u81ea\u52a8\u6807\u6ce8\u6bcf\u6761\u7d20\u6750\u7684\"\u63a8\u8350\u6709\u6548\u533a\u95f4\"\uff0c\u8ba9LLM\u4e13\u6ce8\u53d9\u4e8b\u7f16\u6392\u3002\n\n**\u4e3a\u4ec0\u4e48\u9700\u8981\u8fd9\u6b65\uff1a**\n\u539f\u59cb\u624b\u673a\u7d20\u6750\u666e\u904d\u5b58\u5728\uff1a\u5f00\u5934\u5f55\u5236\u53e3\u4ee4\u3001\u540c\u4e00\u53e5\u8bdd\u91cd\u590d\u591a\u904d\u3001\u524d1-2s\u4e3e\u624b\u673a\u6643\u52a8\u3001\u8bf4\u5b8c\u8bdd\u540e\u62d6\u5f88\u957f\u7684\u65e0\u610f\u4e49\u753b\u9762\u3002\u5982\u679c\u628a\u8fd9\u4e9b\u539f\u59cb\u6570\u636e\u76f4\u63a5\u7ed9LLM\uff0cLLM\u8f93\u51fa\u7684plan\u8fd8\u9700\u8981\u9010\u6761\u4fee\u8865\u3002\n\n**\u9884\u5904\u7406\u505a\u56db\u4ef6\u4e8b\uff1a**\n\n#### a) \u5f55\u5236\u53e3\u4ee4\u68c0\u6d4b\n\n```python\nRECORDING_CUES = [\n    \"\u5f00\u59cb\u4e86\", \"\u597d\u4e86\u5f00\u59cb\", \"\u5f00\u59cb\u5f55\u4e86\", \"\u597d\u4e86 \u5f00\u59cb\",\n    \"\u8d70\u4e86\", \"\u597d\u4e86\", \"\u6765\u4e86\",  # \u4ec5\u5728\u524d3s\u5185\u51fa\u73b0\u65f6\u7b97\u53e3\u4ee4\n]\n\ndef detect_cues(transcript_segments, duration):\n    \"\"\"\u68c0\u6d4b\u5f55\u5236\u53e3\u4ee4\uff0c\u8fd4\u56deskip zones\"\"\"\n    skip_zones = []\n    for seg in transcript_segments:\n        text = seg['text'].strip()\n        if seg['start'] < 3.0 and any(text.startswith(c) or text == c for c in RECORDING_CUES):\n            skip_zones.append({\n                'type': 'recording_cue',\n                'range': [seg['start'], seg['end']],\n                'text': text,\n                'action': 'skip'\n            })\n        if seg['end'] > duration - 3.0 and text in [\"\u597d\u4e86\", \"\u8d70\u4e86\", \"\u597d\u4e86 \u8d70\"]:\n            skip_zones.append({\n                'type': 'recording_cue',\n                'range': [seg['start'], seg['end']],\n                'text': text,\n                'action': 'skip'\n            })\n    return skip_zones\n```\n\n#### b) \u91cd\u590d\u8bed\u97f3\u68c0\u6d4b\n\n```python\nfrom difflib import SequenceMatcher\n\ndef detect_repeats(segments, threshold=0.6):\n    \"\"\"\u68c0\u6d4b\u91cd\u590d\u8bed\u97f3\uff0c\u6807\u8bb0\u5efa\u8bae\u8df3\u8fc7\u7684\u91cd\u590d\u6bb5\"\"\"\n    repeats = []\n    for i, a in enumerate(segments):\n        for j, b in enumerate(segments):\n            if j <= i: continue\n            ratio = SequenceMatcher(None, a['text'], b['text']).ratio()\n            if ratio >= threshold and len(a['text']) > 4:\n                skip = a if len(a['text']) <= len(b['text']) else b\n                repeats.append({\n                    'type': 'repeat',\n                    'range': [skip['start'], skip['end']],\n                    'text': skip['text'],\n                    'kept': b['text'] if skip == a else a['text'],\n                    'action': 'suggest_skip'\n                })\n    return repeats\n```\n\n#### c) \u8d77\u624b\u6643\u52a8 / \u7ed3\u5c3e\u62d6\u62fd\u68c0\u6d4b\n\n```python\ndef detect_trim_points(segments, duration, mean_volume_db):\n    \"\"\"\u57fa\u4e8e\u8bed\u97f3+\u97f3\u91cf\u63a8\u8350\u8d77\u6b62\u70b9\"\"\"\n    suggested_start = 0.0\n    suggested_end = duration\n\n    if segments:\n        first_speech = segments[0]['start']\n        last_speech_end = segments[-1]['end']\n        if first_speech > 2.0:\n            suggested_start = max(0, first_speech - 0.5)\n        if duration - last_speech_end > 3.0:\n            suggested_end = last_speech_end + 1.5\n    else:\n        if duration > 5.0:\n            suggested_start = 1.0\n\n    return suggested_start, suggested_end\n```\n\n#### d) \u7eaf\u753b\u9762\u65f6\u957f\u9650\u5236\n\n| \u6a21\u5f0f     | \u7eaf\u753b\u9762\u4e0a\u9650 | \u53e3\u4ee4\u5904\u7406 | \u91cd\u590d\u5904\u7406       | \u8d77\u624b\u6643\u52a8 |\n|----------|-----------|---------|---------------|---------| \n| strict   | 8-10s     | \u5168\u90e8\u53bb\u9664 | \u53ea\u75591\u904d        | \u53bb1.5s  |\n| normal   | 12-15s    | \u53bb\u9664\u660e\u786e\u53e3\u4ee4 | \u53bb\u660e\u663e\u91cd\u590d   | \u53bb1.0s  |\n| loose    | 20-25s    | \u4ec5\u53bb\"\u5f00\u59cb\u4e86\" | \u4fdd\u7559\u5927\u90e8\u5206    | \u53bb0.5s  |\n\n#### e) \u8f93\u51fa\u683c\u5f0f\n\n\u9884\u5904\u7406\u7ed3\u679c\u8ffd\u52a0\u5230\u7d20\u6750\u5206\u6790\u6570\u636e\u91cc\uff1a\n\n```\n\u2501\u2501\u2501 clip_012.mp4 | \u65f6\u957f25.8s | 1920x1080 \u2501\u2501\u2501\n  \u3010\u753b\u9762\u3011...\n  \u3010\u8bed\u97f3\u3011...\n  \u3010\u7cbe\u4fee\u5efa\u8bae\u3011\u6a21\u5f0f: normal\n    \u539f\u59cb\u533a\u95f4: [0.0 - 25.8]\n    \u63a8\u8350\u533a\u95f4: [2.4 - 25.8]\n    \u2298 [0.0-2.4] \u8df3\u8fc7: \u5f55\u5236\u53e3\u4ee4 \"\u5f00\u59cb\u4e86\"\n    \u6709\u6548\u8bed\u97f3: [2.62-25.8]\n```\n\n**\u26a0 \u91cd\u8981\u539f\u5219\uff1a\u9884\u5904\u7406\u53ea\u505a\u6807\u6ce8\u548c\u5efa\u8bae\uff0c\u4e0d\u505a\u786c\u88c1\u526a\u3002LLM\u4fdd\u7559\u6700\u7ec8\u51b3\u5b9a\u6743\u3002**\n\n### \u9636\u6bb54: LLM\u53d9\u4e8b\u7f16\u6392\n\n\u76ee\u6807\uff1a\u628a\u9884\u5904\u7406\u540e\u7684\u7d20\u6750\u6570\u636e\u4ea4\u7ed9LLM\uff0c\u751f\u6210\u526a\u8f91\u65b9\u6848\u3002\n\n\u8f93\u5165\uff1a\u7d20\u6750\u5206\u6790 + \u7cbe\u4fee\u5efa\u8bae(\u63a8\u8350\u533a\u95f4) + \u53c2\u8003\u7814\u7a76\u7ed3\u8bba\n\u8f93\u51fa\uff1a\u526a\u8f91\u65b9\u6848JSON\uff08\u7ed3\u6784\u89c1\u4e0b\u65b9schema\uff09\n\n#### edit_plan JSON Schema\n\n```json\n{\n  \"title\": \"\u89c6\u9891\u6807\u9898\",\n  \"structure\": [\n    {\n      \"section\": \"\u6bb5\u843d\u540d \u2014 \u526f\u6807\u9898\",\n      \"description\": \"\u672c\u6bb5\u843d\u5185\u5bb9\u6982\u8ff0\",\n      \"clips\": [\n        {\n          \"file\": \"\u7d20\u6750\u6587\u4ef6\u540d.mp4\",\n          \"start\": 0.0,\n          \"end\": 12.0,\n          \"note\": \"\u753b\u9762\u5185\u5bb9\u7b80\u8ff0\",\n          \"subtitle\": \"\u4fdd\u7559\u7684\u8bed\u97f3\u6587\u5b57\"\n        }\n      ]\n    }\n  ],\n  \"bgm_suggestion\": \"BGM\u98ce\u683c\u5efa\u8bae\uff08\u542bgenre/mood/instruments/tempo/bpm\uff09\",\n  \"editing_notes\": \"\u6574\u4f53\u526a\u8f91\u8bf4\u660e\"\n}\n```\n\n**\u7ea6\u675f\uff1a**\n- \u540c\u4e00\u7d20\u6750\u53ef\u51fa\u73b0\u5728\u591a\u4e2aclip\u4e2d\uff08\u4e0d\u540c\u65f6\u95f4\u533a\u95f4\uff09\n- start/end \u4e0d\u80fd\u622a\u65ad\u8bed\u97f3\u4e2d\u95f4\uff08\u9636\u6bb54.5\u4f1a\u6821\u9a8c\uff09\n- \u7b2c\u4e00\u4e2asection\u5efa\u8bae\u662f\"\u5f00\u7bc7\"\u7c7b\u5f15\u5165\u6bb5\u843d\n- \u6700\u540e\u4e00\u4e2asection\u5efa\u8bae\u662f\"\u6536\u5c3e\"\u7c7b\u6bb5\u843d\n\n\u8be6\u7ec6prompt\u6a21\u677f\u89c1 `templates/edit_plan_prompt.md`\u3002\n\n### \u9636\u6bb54+ : \u5206\u955c\u53ef\u89c6\u5316 + Dashboard\uff08\u6d4f\u89c8\u5668\u9884\u89c8\uff09\n\n\u76ee\u6807\uff1a\u628aLLM\u8f93\u51fa\u7684\u526a\u8f91\u65b9\u6848\u505a\u6210\u56fe\u6587\u5e76\u8302\u7684\u7f51\u9875\uff0c\u65b9\u4fbf\u7528\u6237\u76f4\u89c2Review\u3002\n\n**Dashboard \u5305\u542b\u4e24\u4e2a\u9762\u677f\uff1a**\n\n1. **\ud83d\udccb \u7d20\u6750\u603b\u89c8** \u2014 \u7d20\u6750\u7f29\u7565\u56fe\u7f51\u683c + \u65f6\u957f/\u8bed\u97f3/\u97f3\u91cf\u6807\u6ce8 + \u7b5b\u9009(\u5168\u90e8/\u5df2\u7528/\u672a\u7528/\u542b\u8bed\u97f3) + \u70b9\u51fb\u67e5\u770b\u8be6\u60c5\n2. **\u2705 \u6210\u54c1\u8d28\u91cf\u68c0\u67e5** \u2014 \u6210\u54c1\u89c6\u9891\u5b9a\u65f6\u62bd\u5e27(\u6bcf30\u79d2) + \u6bb5\u843d\u5b9a\u4f4d + \u70b9\u51fb\u653e\u5927\n\n**\u4f7f\u7528 `gen_dashboard.py` \u811a\u672c\uff1a**\n\n```bash\n# \u6700\u5c0f\u7528\u6cd5\uff08\u53ea\u751f\u6210\u7d20\u6750\u9762\u677f\uff09\npython3 scripts/gen_dashboard.py \\\n  --analysis clip_analysis.json \\\n  --plan edit_plan.json \\\n  --footage footage/ \\\n  --out output/\n\n# \u5b8c\u6574\u4e24\u9762\u677f\uff08\u542b\u6210\u54c1QC\u5e27\uff09\npython3 scripts/gen_dashboard.py \\\n  --analysis clip_analysis.json \\\n  --plan edit_plan.json \\\n  --footage footage/ \\\n  --video output/final.mp4 \\\n  --out output/\n```\n\n**\u652f\u6301\u4e24\u79cd\u5206\u6790\u6570\u636e\u683c\u5f0f\uff08\u81ea\u52a8\u68c0\u6d4b\uff09\uff1a**\n- \u6807\u51c6\u683c\u5f0f `clip_analysis.json`: `{filename, duration, visual: [...], audio: {...}}`\n- \u7d27\u51d1\u683c\u5f0f `clips_compact.json`: `{file, dur, visual: \"string\", speech: [...]}`\n\n\u7528\u6237\u786e\u8ba4\u5206\u955c\u65b9\u6848\u540e\uff0c\u518d\u8fdb\u5165\u6e32\u67d3\u9636\u6bb5\u3002\n\n### \u9636\u6bb54.5: \u8bed\u97f3\u622a\u65ad\u6821\u9a8c\n\n\u76ee\u6807\uff1a\u81ea\u52a8\u6821\u9a8cLLM\u8f93\u51fa\u7684plan\uff0c\u786e\u4fdd\u6ca1\u6709\u628a\u4efb\u4f55\u8bed\u97f3\u4ece\u4e2d\u95f4\u622a\u65ad\u3002\n\n```python\ndef validate_speech(plan_clips, speech_data):\n    \"\"\"\u68c0\u67e5\u6bcf\u4e2aclip\u7684start/end\u662f\u5426\u622a\u65ad\u4e86\u8bed\u97f3\"\"\"\n    issues = []\n    for clip in plan_clips:\n        f = clip['file']\n        cs, ce = clip['start'], clip['end']\n        if f not in speech_data: continue\n        for ss, se, txt in speech_data[f]:\n            if ss < ce and se > cs:  # \u8bed\u97f3\u4e0eclip\u6709\u4ea4\u96c6\n                if ss < cs - 0.3:    # \u8bed\u97f3\u5f00\u59cb\u5728clip\u4e4b\u524d\n                    issues.append(f\"\u622a\u65ad\u5f00\u5934: {f} clip\u4ece{cs}\u5f00\u59cb\uff0c\u4f46\u8bed\u97f3\\\"{txt}\\\"\u4ece{ss}\u5f00\u59cb\")\n                if se > ce + 0.5:    # \u8bed\u97f3\u7ed3\u675f\u5728clip\u4e4b\u540e\n                    issues.append(f\"\u622a\u65ad\u7ed3\u5c3e: {f} clip\u5728{ce}\u7ed3\u675f\uff0c\u4f46\u8bed\u97f3\\\"{txt}\\\"\u5230{se}\u7ed3\u675f\")\n    return issues\n```\n\n**\u6821\u9a8c\u4e0d\u901a\u8fc7\u65f6\uff1a\u81ea\u52a8\u4fee\u590dstart/end\uff0c\u4e0d\u9700\u8981\u56deLLM\u91cd\u65b0\u7f16\u6392\u3002**\n\n#### \u81ea\u52a8\u4fee\u590d\u903b\u8f91\n\n```python\ndef fix_speech_cuts(plan, speech_data, margin=0.3):\n    \"\"\"\n    \u81ea\u52a8\u4fee\u590d\u8bed\u97f3\u622a\u65ad\u95ee\u9898\u3002\u76f4\u63a5\u4fee\u6539plan\u4e2d\u7684start/end\u3002\n    \n    \u53c2\u6570:\n        plan: edit_plan dict (\u4f1a\u88ab\u539f\u5730\u4fee\u6539)\n        speech_data: dict, {filename: [(start, end, text), ...]}\n        margin: float, \u8bed\u97f3\u8fb9\u754c\u5bb9\u5dee(\u79d2)\n    \n    \u8fd4\u56de:\n        fixes: list of str, \u4fee\u590d\u65e5\u5fd7\n    \"\"\"\n    fixes = []\n    \n    for sec in plan[\"structure\"]:\n        for clip in sec[\"clips\"]:\n            f = clip[\"file\"]\n            cs = float(clip[\"start\"])\n            ce = float(clip[\"end\"])\n            \n            if f not in speech_data:\n                continue\n            \n            for ss, se, txt in speech_data[f]:\n                if ss >= ce or se <= cs:\n                    continue\n                \n                # \u5f00\u5934\u622a\u65ad\uff1aclip.start > speech.start + margin\n                if ss < cs - margin and se > cs:\n                    old_start = cs\n                    new_start = max(0, ss - 0.1)\n                    clip[\"start\"] = round(new_start, 1)\n                    fixes.append(\n                        f\"  \u4fee\u590d\u5f00\u5934: {f} [{old_start}\u2192{new_start}] \"\n                        f\"\u8bed\u97f3\\\"{txt[:20]}\\\"\u4ece{ss}s\u5f00\u59cb\"\n                    )\n                    cs = new_start\n                \n                # \u7ed3\u5c3e\u622a\u65ad\uff1aclip.end < speech.end - margin\n                if se > ce + margin and ss < ce:\n                    old_end = ce\n                    new_end = se + 0.2\n                    clip[\"end\"] = round(new_end, 1)\n                    fixes.append(\n                        f\"  \u4fee\u590d\u7ed3\u5c3e: {f} [{old_end}\u2192{new_end}] \"\n                        f\"\u8bed\u97f3\\\"{txt[:20]}\\\"\u5230{se}s\u7ed3\u675f\"\n                    )\n                    ce = new_end\n    \n    return fixes\n\n\ndef validate_and_fix(plan, speech_data):\n    \"\"\"\n    \u7aef\u5230\u7aef\u6821\u9a8c+\u4fee\u590d\u5165\u53e3\u3002\n    \n    \u7528\u6cd5:\n        import json\n        # \u52a0\u8f7dplan\n        plan = json.load(open('edit_plan.json'))\n        # \u52a0\u8f7d\u8bed\u97f3\u6570\u636e (\u683c\u5f0f: {filename: [{start, end, text}, ...]})\n        raw = json.load(open('clip_analysis.json'))\n        speech_data = {}\n        for clip_info in raw:\n            fname = clip_info['filename']\n            if clip_info['audio']['has_speech']:\n                speech_data[fname] = [\n                    (s['start'], s['end'], s['text'])\n                    for s in clip_info['audio']['transcript']\n                ]\n        \n        # \u6821\u9a8c\n        all_clips = [c for sec in plan['structure'] for c in sec['clips']]\n        issues = validate_speech(all_clips, speech_data)\n        print(f'\u53d1\u73b0 {len(issues)} \u4e2a\u622a\u65ad\u95ee\u9898')\n        \n        # \u4fee\u590d\n        fixes = fix_speech_cuts(plan, speech_data)\n        print(f'\u81ea\u52a8\u4fee\u590d {len(fixes)} \u5904')\n        \n        # \u4e8c\u6b21\u6821\u9a8c\u786e\u8ba4\n        all_clips2 = [c for sec in plan['structure'] for c in sec['clips']]\n        issues2 = validate_speech(all_clips2, speech_data)\n        assert len(issues2) == 0, f'\u4ecd\u6709 {len(issues2)} \u4e2a\u95ee\u9898\u672a\u4fee\u590d!'\n        print('\u2705 \u4e8c\u6b21\u6821\u9a8c\u901a\u8fc7')\n        \n        # \u4fdd\u5b58\u4fee\u590d\u540e\u7684plan\n        json.dump(plan, open('edit_plan_fixed.json', 'w', ensure_ascii=False), indent=2)\n    \"\"\"\n    all_clips = [c for sec in plan['structure'] for c in sec['clips']]\n    issues = validate_speech(all_clips, speech_data)\n    fixes = fix_speech_cuts(plan, speech_data)\n    return issues, fixes\n```\n\n**\u4fee\u590d\u7b56\u7565\uff1a**\n\n| \u622a\u65ad\u7c7b\u578b | \u68c0\u6d4b\u6761\u4ef6 | \u4fee\u590d\u65b9\u5f0f |\n|----------|---------|---------| \n| \u5f00\u5934\u622a\u65ad | clip.start > speech.start + margin | clip.start \u2192 speech.start - 0.1s |\n| \u7ed3\u5c3e\u622a\u65ad | clip.end < speech.end - margin | clip.end \u2192 speech.end + 0.2s |\n\n**\u8fb9\u754c\u5bb9\u5dee (margin=0.3s)\uff1a** Whisper/FunASR \u65f6\u95f4\u6233\u6709\u00b10.2-0.3s\u8bef\u5dee\u3002\u4fee\u590d\u540e\u5fc5\u987b\u8dd1\u4e8c\u6b21\u6821\u9a8c\u3002\n\n### \u9636\u6bb55: ffmpeg\u81ea\u52a8\u6267\u884c\n\n\u76ee\u6807\uff1a\u628a\u526a\u8f91\u65b9\u6848\u8f6c\u6210ffmpeg\u547d\u4ee4\u5e76\u6267\u884c\u3002\n\n#### \u6e32\u67d3\u7b56\u7565\uff1a\u9010\u7247\u6bb5\u88c1\u526a \u2192 \u62fc\u63a5\n\n```bash\n# \u5355\u4e2a\u7247\u6bb5\u88c1\u526a+\u7f16\u7801\nffmpeg -y -ss 00:00:02.400 -i footage/INPUT.mp4 -t 00:00:23.400 \\\n  -vf \"scale=1920:1080:force_original_aspect_ratio=decrease,pad=1920:1080:(ow-iw)/2:(oh-ih)/2,setsar=1\" \\\n  -r 30 -c:v libx264 -preset medium -crf 18 -pix_fmt yuv420p \\\n  -c:a aac -b:a 128k -ar 44100 -ac 2 \\\n  -movflags +faststart segments/seg_001.mp4\n\n# \u62fc\u63a5\u6210\u54c1\nffmpeg -y -f concat -safe 0 -i concat.txt -c copy -movflags +faststart output.mp4\n```\n\n#### \u53d8\u901f\n\n```bash\n# 2\u500d\u901f\uff08\u89c6\u9891+\u97f3\u9891\u540c\u6b65\uff09\nffmpeg -i input.mp4 -filter:v \"setpts=0.5*PTS\" -filter:a \"atempo=2.0\" out.mp4\n```\n\n#### \u7247\u5934\u9ad8\u5149\u8499\u592a\u5947\n\n\u4ece\u5404\u6bb5\u843d\u9009\u6700\u6709\u89c6\u89c9\u51b2\u51fb\u529b\u7684\u955c\u5934\uff0c\u5feb\u5207\u62fc\u63a5\u653e\u5728\u89c6\u9891\u6700\u524d\u9762\u3002\n\n**\u9009\u7247\u539f\u5219\uff1a**\n- \u6bcf\u4e2a\u4e3b\u8981\u6bb5\u843d\u90091\u4e2a\u7cbe\u5f69\u753b\u9762\n- \u6bcf\u4e2a\u955c\u59340.5-1\u79d2\uff0c\u5feb\u5207\u8282\u594f\n- \u603b\u51716-10\u4e2a\u955c\u5934\uff0c\u603b\u65f6\u957f5-8\u79d2\n- \u6392\u9664\u7ad6\u5c4f\u7d20\u6750\n- \u4e0d\u52a0\u97f3\u9891\uff08-an\uff09\uff0c\u6700\u7ec8\u7531BGM\u8986\u76d6\n\n```python\nimport subprocess, os\n\nhighlights = [\n    ('footage/clip_a.mp4',   1.5, 2.2),\n    ('footage/clip_b.mp4',   2.0, 2.7),\n    # ... \u6bcf\u4e2a0.7\u79d2\u5de6\u53f3\n]\n\nfor i, (f, start, end) in enumerate(highlights, 1):\n    dur = end - start\n    cmd = f'ffmpeg -y -ss {start} -i \"{f}\" -t {dur} ' \\\n          f'-vf \"scale=1920:1080:force_original_aspect_ratio=decrease,' \\\n          f'pad=1920:1080:(ow-iw)/2:(oh-ih)/2\" ' \\\n          f'-c:v libx264 -preset ultrafast -crf 23 -pix_fmt yuv420p ' \\\n          f'-an highlights/hl_{i}.mp4'\n    subprocess.run(cmd, shell=True)\n\n# concat\nwith open('highlights/concat.txt', 'w') as f:\n    for i in range(1, len(highlights)+1):\n        f.write(f\"file 'hl_{i}.mp4'\\n\")\n\nsubprocess.run('ffmpeg -y -f concat -safe 0 -i highlights/concat.txt '\n               '-c copy highlights/montage.mp4', shell=True)\n```\n\n#### \u6bb5\u843d\u6807\u9898\u53e0\u52a0\n\n\u7528 Pillow \u751f\u6210\u900f\u660eRGBA\u7684PNG\uff0cffmpeg overlay\u5230\u89c6\u9891\u753b\u9762\u4e0a\u3002**\u4e0d\u8981\u7528\u9ed1\u5e95\u6807\u9898\u5361**\u3002\n\n```python\nfrom PIL import Image, ImageDraw, ImageFont, ImageFilter\n\nW, H = 1920, 1080\ntitle = \"\u6bb5\u843d\u6807\u9898\"\n\nimg = Image.new(\"RGBA\", (W, H), (0, 0, 0, 0))\ndraw = ImageDraw.Draw(img)\n\n# \u5b57\u4f53\uff1amacOS\u7528\u51ac\u9752\u9ed1\u4f53\uff0cLinux\u7528Noto Sans CJK\n# font = ImageFont.truetype(\"/System/Library/Fonts/Hiragino Sans GB.ttc\", 60)  # macOS\n# font = ImageFont.truetype(\"/usr/share/fonts/opentype/noto/NotoSansCJK-Regular.ttc\", 60)  # Linux\nfont = ImageFont.truetype(\"YOUR_FONT_PATH\", 60)\n\nbbox = draw.textbbox((0, 0), title, font=font)\ntw, th = bbox[2] - bbox[0], bbox[3] - bbox[1]\nx, y = (W - tw) // 2, (H - th) // 2\n\n# \u67d4\u548c\u9634\u5f71\nshadow = Image.new(\"RGBA\", (W, H), (0, 0, 0, 0))\nsd = ImageDraw.Draw(shadow)\nsd.text((x+2, y+2), title, fill=(0, 0, 0, 180), font=font)\nshadow = shadow.filter(ImageFilter.GaussianBlur(radius=6))\n\nimg = Image.alpha_composite(img, shadow)\ndraw = ImageDraw.Draw(img)\ndraw.text((x, y), title, fill=(255, 255, 255, 240), font=font)\nimg.save(\"title_overlay.png\")\n```\n\n**overlay\u5230\u89c6\u9891\u4e0a\uff1a**\n\n```bash\n# \u6807\u9898\u663e\u793a\u524d3\u79d2\nffmpeg -y -i section.mp4 -i title.png \\\n  -filter_complex \"overlay=0:0:enable='between(t,0,3)'\" \\\n  -c:v libx264 -preset medium -crf 18 -pix_fmt yuv420p \\\n  -c:a aac -b:a 128k -movflags +faststart \\\n  section_titled.mp4\n```\n\n**\u26a0 overlay\u5173\u952ePitfall\uff1a**\n- \u274c \u4e0d\u8981\u52a0 `shortest=1` \u2014 PNG\u53ea\u67091\u5e27\uff0c\u4f1a\u5bfc\u81f4\u89c6\u9891\u6d41\u7acb\u523b\u622a\u65ad\n- \u2705 \u76f4\u63a5\u7528 `overlay=0:0:enable='between(t,0,3)'` \u6700\u7b80\u5355\u53ef\u9760\n- \u2705 \u5148\u7528\u77ed\u7247\u6bb5(5-8s)\u9a8c\u8bc1overlay\u6548\u679c\n\n#### \u6bb5\u843d\u62fc\u63a5\uff08\u63a8\u8350\u65b9\u6848\uff09\n\n**\u2705 \u6b63\u786e\u65b9\u6848\uff1a\u9010\u6bb5\u7f16\u7801 \u2192 concat copy**\n\n```bash\necho \"file 'final_sec_01.mp4'\" > concat.txt\necho \"file 'final_sec_02.mp4'\" >> concat.txt\nffmpeg -y -f concat -safe 0 -i concat.txt -c copy -movflags +faststart output.mp4\n```\n\n**\u274c \u4e0d\u8981\u7528xfade\u94fe\u5f0f\u5408\u5e76\uff1a** \u94fe\u5f0fxfade\u4f1a\u5bfc\u81f4\u5e27\u6570\u7d2f\u79ef\u4e22\u5931\uff0c\u540e\u534a\u6bb5\u5168\u9ed1\u3002\n\n**\u6bb5\u843d\u95f4\u8fc7\u6e21**\uff1a\u6bb5\u843d\u5185\u786c\u5207\u5373\u53ef\u3002\u5982\u9700\u6e10\u53d8\uff0c\u6bcf\u6bb5\u9996\u5c3e\u52a0fade\u6bd4xfade\u66f4\u53ef\u9760\u3002\n\n#### BGM\u751f\u6210\u4e0e\u6df7\u97f3\uff08\u53ef\u9009\uff09\n\nBGM \u53ef\u4ee5\u624b\u52a8\u6dfb\u52a0\uff0c\u4e5f\u53ef\u4ee5\u7528 AI \u97f3\u4e50\u751f\u6210\u5de5\u5177\uff08\u5982 MiniMax music\u3001Suno \u7b49\uff09\u3002\n\n##### a) AI \u751f\u6210 BGM\n\n**\u98ce\u683c\u6620\u5c04\u53c2\u8003\uff08vlog\u5e38\u7528\uff09\uff1a**\n\n| Vlog \u98ce\u683c | genre | mood | instruments | tempo |\n|-----------|-------|------|-------------|-------|\n| \u8f7b\u677e\u65e5\u5e38 | indie pop | cheerful, carefree | ukulele, acoustic guitar | moderate |\n| \u6587\u827a\u6cbb\u6108 | folk, acoustic | warm, gentle | acoustic guitar, piano, strings | slow |\n| \u7f8e\u98df\u63a2\u5e97 | jazz, bossa nova | cozy, playful | piano, upright bass | moderate |\n| \u51b0\u96ea/\u51ac\u5b63 | cinematic, ambient | serene, majestic | piano, celesta, strings | slow |\n| \u70ed\u5e26/\u6d77\u5c9b | tropical house | sunny, relaxed | steel drums, marimba | upbeat |\n| \u57ce\u5e02\u63a2\u7d22 | lo-fi hip hop | chill, urban | keys, vinyl crackle | moderate |\n\n##### b) \u6df7\u5408\u5230\u89c6\u9891\n\n```bash\n# BGM \u97f3\u91cf 10-15%\uff08\u6709\u8bed\u97f3\u7684vlog\u8981\u538b\u4f4eBGM\uff09\nffmpeg -y -i video_no_bgm.mp4 -i bgm.m4a \\\n  -filter_complex \"[0:a]volume=1.0[orig];[1:a]volume=0.12[bgm];[orig][bgm]amix=inputs=2:duration=first:dropout_transition=2[aout]\" \\\n  -map 0:v -map \"[aout]\" -c:v copy -c:a aac -b:a 192k \\\n  -movflags +faststart output/final.mp4\n```\n\n**BGM \u97f3\u91cf\u5efa\u8bae\uff1a**\n- \u6709\u5927\u91cf\u8bed\u97f3/\u65c1\u767d \u2192 8-12%\n- \u8bed\u97f3\u8f83\u5c11\uff0c\u7a7a\u955c\u4e3a\u4e3b \u2192 15-25%\n- \u7eaf\u7a7a\u955c\u8499\u592a\u5947\u6bb5\u843d \u2192 30-40%\n\n#### \u53d8\u901f\n\n```bash\nffmpeg -i input.mp4 -filter:v \"setpts=0.5*PTS\" -filter:a \"atempo=2.0\" out.mp4\n```\n\n## Pitfalls\n\n1. **\u526a\u6620\u4e0d\u53ef\u7528** \u2014 v10.4+ \u5bf9 draft_info.json \u52a0\u5bc6\uff0c\u4e0d\u8981\u6d6a\u8d39\u65f6\u95f4\n2. **\u89c6\u89c9API\u7aef\u70b9\u8981\u533a\u5206** \u2014 \u6709\u4e9b\u5e73\u53f0\u89c6\u89c9\u6a21\u578b\u548c\u6587\u672c\u6a21\u578b\u7aef\u70b9\u4e0d\u540c\uff0c\u786e\u8ba4\u7528\u5bf9\n3. **Whisper\u6a21\u578b\u9009\u62e9** \u2014 base\u4e2d\u6587\u592a\u5dee\uff0clarge\u592a\u6162\uff0cmedium\u662f\u6027\u4ef7\u6bd4\u6700\u4f18\n4. **\u4e0d\u8981\u8df3\u8fc7\u53c2\u8003\u7814\u7a76** \u2014 \u6ca1\u6709\"\u597dvlog\u6807\u51c6\"\u7684AI\u4f1a\u526a\u51fa\u6d41\u6c34\u8d26\n5. **ffmpeg\u7f16\u7801\u517c\u5bb9** \u2014 \u62fc\u63a5\u65f6\u6ce8\u610f\u7d20\u6750\u7f16\u7801\u4e00\u81f4\u6027\uff0c\u5148\u9010\u7247\u6bb5\u7edf\u4e00\u7f16\u7801\u518dconcat\n6. **\u89c6\u89c9API\u9650\u6d41** \u2014 \u9ad8\u5cf0\u65f6\u6bb5\u5bb9\u6613\u9650\u6d41\uff0c\u52a0retry\u548csleep\n7. **\u9884\u5904\u7406\u5728LLM\u4e4b\u524d\u505a** \u2014 \u53e3\u4ee4/\u91cd\u590d/\u6643\u52a8\u68c0\u6d4b\u5728\u9636\u6bb53.5\u5904\u7406\n8. **\u9884\u5904\u7406\u662f\u5efa\u8bae\u4e0d\u662f\u786c\u88c1\u526a** \u2014 LLM\u4fdd\u7559\u8986\u76d6\u6743\uff0c\u67d0\u4e9b\u53e3\u4ee4\u53ef\u80fd\u6709\u53d9\u4e8b\u4ef7\u503c\n9. **\u8bed\u97f3\u622a\u65ad\u5fc5\u987b\u81ea\u52a8\u6821\u9a8c** \u2014 LLM\u8f93\u51fa\u7684plan\u53ef\u80fd\u5728\u8bed\u97f3\u4e2d\u95f4\u5207\u65ad\n10. **Whisper\u5e7b\u89c9** \u2014 \u7eaf\u97f3\u4e50/\u73af\u5883\u97f3\u6bb5\u843dwhisper\u4f1a\u7f16\u9020\u6587\u5b57\uff0c\u9700\u7528\u97f3\u91cf\u9608\u503c(-40dB)\u8fc7\u6ee4\n11. **\u7ad6\u5c4f\u6df7\u6a2a\u5c4f** \u2014 \u7d20\u6750\u5206\u6790\u9636\u6bb5\u5c31\u8981\u68c0\u6d4b\uff0c\u7ad6\u5c4f(9:16)\u6df7\u5728\u6a2a\u5c4f\u91cc\u5f88\u7a81\u5140\uff0c\u5efa\u8bae\u53bb\u6389\n12. **overlay\u4e0d\u8981\u52a0shortest=1** \u2014 PNG\u53ea\u67091\u5e27\uff0cshortest=1\u4f1a\u8ba9\u89c6\u9891\u6d41\u622a\u65ad\n13. **\u6807\u9898\u5361\u98ce\u683c** \u2014 \u767d\u5b57+\u67d4\u548c\u9634\u5f71\u53e0\u5728\u89c6\u9891\u753b\u9762\u4e0a\uff0c**\u4e0d\u8981\u9ed1\u5e95\u6807\u9898\u5361**\n14. **ffmpeg drawtext\u4e0d\u53ef\u7528** \u2014 macOS Homebrew ffmpeg\u9ed8\u8ba4\u6ca1\u7f16\u8bd1freetype\uff0c\u7528Pillow\u751f\u6210PNG overlay\u4ee3\u66ff\n15. **HEVC\u8b66\u544a\u53ef\u5ffd\u7565** \u2014 \"Error constructing the frame RPS\" \u662fHEVC\u89e3\u7801\u8b66\u544a\uff0c\u4e0d\u5f71\u54cd\u8f93\u51fa\n16. **Non-monotonic DTS** \u2014 concat\u62fc\u63a5\u65f6\u5e38\u89c1\u7684\u65f6\u95f4\u6233\u8b66\u544a\uff0c\u64ad\u653e\u4e0d\u53d7\u5f71\u54cd\n17. **\u7a7a\u955c\u4e0d\u5b9c\u8fc7\u957f** \u2014 \u65e0\u8bed\u97f3\u7684\u7eaf\u753b\u9762>6s\u504f\u957f\uff0c\u5efa\u8bae\u538b\u7f29\u52305-8s\n18. **xfade\u94fe\u5f0f\u5408\u5e76\u4e0d\u53ef\u9760** \u2014 \u94fe\u5f0fxfade\u4f1a\u5bfc\u81f4\u5e27\u6570\u7d2f\u79ef\u4e22\u5931\uff0c\u540e\u534a\u6bb5\u5168\u9ed1\uff0c\u7528concat\u4ee3\u66ff\n19. **\u5148\u7528\u77ed\u7247\u6bb5\u9a8c\u8bc1** \u2014 \u6bcf\u6b21\u6539\u6e32\u67d3\u65b9\u6848\u90fd\u5148\u75285-8\u79d2\u77ed\u7247\u6bb5\u9a8c\u8bc1\u6548\u679c\n20. **\u6e32\u67d3\u5b8c\u5fc5\u987b\u9a8c\u8bc1\u5e27\u6570** \u2014 `ffmpeg -i output.mp4 -f null -` \u68c0\u67e5\u5b9e\u9645frame\u6570\n21. **BGM\u522b\u592a\u5927\u58f0** \u2014 \u6709\u8bed\u97f3\u6bb5BGM\u63a7\u5236\u57288-12%\uff0c\u5148\u8bd5\u542c\u518d\u5b9a\n22. **\u591a\u6bb5BGM\u8854\u63a5** \u2014 \u4fdd\u6301\u76f8\u8fd1\u7684key\u548ctempo(bpm\u5dee\u503c<20)\n23. **\u6df7\u97f3\u540e\u5fc5\u987b\u8bd5\u542c** \u2014 \u4e0d\u540c\u7d20\u6750\u539f\u59cb\u97f3\u91cf\u5dee\u5f02\u5927\n24. **\u4e0d\u8981\u91cd\u590d\u5b89\u88c5\u4f9d\u8d56** \u2014 whisper/Pillow\u7b49\u5148\u68c0\u6d4b\u518d\u88c5\uff0c\u4e0d\u8981\u6bcf\u6b21\u90fd\u65b0\u5efavenv\n25. **FunASR\u5fc5\u987b\u88c5torchaudio** \u2014 `pip install funasr modelscope` \u4e0d\u591f\uff0c\u5fc5\u987b `pip install funasr modelscope torchaudio`\uff0c\u5426\u5219 import \u62a5 `No module named 'torchaudio'`\n26. **FunASR\u65f6\u95f4\u6233\u662f\u5b57\u7ea7\u522b** \u2014 \u4e0d\u50cfwhisper\u76f4\u63a5\u7ed9\u53e5\u7ea7\u522bsegments\uff0cFunASR\u8fd4\u56de\u6bcf\u4e2a\u5b57\u7684[start_ms, end_ms]\uff0c\u9700\u8981\u6309\u6807\u70b9\u805a\u5408\u6210\u53e5\u5b50\u624d\u80fd\u5582\u7ed9\u540e\u7eed\u7684\u8bed\u97f3\u622a\u65ad\u6821\u9a8c(\u9636\u6bb54.5)\n27. **FunASR\u5e7b\u89c9\u6bd4whisper\u8f7b** \u2014 \u7eaf\u73af\u5883\u97f3/\u97f3\u4e50\u65f6FunASR\u901a\u5e38\u8fd4\u56de\u7a7a\u6587\u672c\uff0c\u4e0d\u50cfwhisper\u4f1a\u7f16\u9020\u5185\u5bb9\u3002\u4f46\u4ecd\u9700\u7528\u97f3\u91cf\u9608\u503c(-40dB)\u505a\u4e8c\u6b21\u786e\u8ba4\n\n### \u7f16\u7801\u4e0e\u62fc\u63a5\u7c7b\uff08\u89c6\u9891\u6559\u7a0b\u5b9e\u6218\u65b0\u589e\uff09\n\n28. **\u26a0\ufe0f atempo\u52a0\u901f\u540e\u97f3\u89c6\u9891\u65f6\u957f\u4e0d\u4e00\u81f4** \u2014 atempo\u5904\u7406\u540e\u97f3\u9891\u6bd4\u89c6\u9891\u77ed0.4-0.8\u79d2/\u6bb5\u30028\u6bb5\u7d2f\u79ef\u5dee\u597d\u51e0\u79d2\uff0c\u540e\u534a\u6bb5\u97f3\u753b\u5b8c\u5168\u5bf9\u4e0d\u4e0a\u3002**\u6bcf\u6bb5\u526a\u8f91\u540e\u5fc5\u987b\u7acb\u5373\u9a8c\u8bc1\u97f3\u89c6\u9891\u65f6\u957f\u5dee<0.05s**\uff0c\u8d85\u8fc7\u5219\u7528apad\u8865\u9f50\uff1a\n```bash\n# \u9a8c\u8bc1\nffprobe -show_entries stream=duration -select_streams v -of csv=p=0 seg.mp4  # \u89c6\u9891\u65f6\u957f\nffprobe -show_entries stream=duration -select_streams a -of csv=p=0 seg.mp4  # \u97f3\u9891\u65f6\u957f\n# \u8865\u9f50\nffmpeg -y -i seg.mp4 -af \"apad\" -c:v copy -c:a aac -shortest seg_fixed.mp4\n```\n\n29. **\u26a0\ufe0f concat copy\u97f3\u9891\u65f6\u95f4\u6233\u4e0d\u8fde\u7eed** \u2014 \u591a\u6bb5concat copy\u540e\u97f3\u9891\u5494\u5494\u54cd/\u526a\u6620\u97f3\u753b\u504f\u79fb\u3002**\u6b63\u786e\u65b9\u6848\uff1a\u97f3\u9891\u5355\u72ec\u63d0\u53d6wav+apad\u8865\u9f50+concat filter\u62fc\u63a5\uff0c\u89c6\u9891\u5355\u72ecconcat copy\uff0c\u6700\u540e\u5408\u5e76**\uff1a\n```bash\n# \u6bcf\u6bb5\u63d0\u53d6\u97f3\u9891\uff08apad\u8865\u9f50\u5230\u89c6\u9891\u65f6\u957f\uff09\nffmpeg -y -i seg.mp4 -vn -af \"apad\" -ar 48000 -ac 2 -c:a pcm_s16le -t {\u89c6\u9891\u65f6\u957f} seg.wav\n# concat filter\u62fc\u63a5\u97f3\u9891\uff08\u4e0d\u662fdemuxer\uff09\nffmpeg -y -i a.wav -i b.wav -filter_complex \"[0:a][1:a]concat=n=2:v=0:a=1[aout]\" -map \"[aout]\" -c:a aac audio.m4a\n# \u5408\u5e76\nffmpeg -y -i video.mp4 -i audio.m4a -c:v copy -c:a copy output.mp4\n```\n\n30. **\u526a\u6620\u517c\u5bb9\u7f16\u7801** \u2014 \u5bfc\u5165\u526a\u6620\u97f3\u753b\u4e0d\u540c\u6b65\u7684\u4fee\u590d\uff1a`-r 30 -vsync cfr`(\u56fa\u5b9a\u5e27\u7387) + `-x264-params \"bframes=0\"`(\u65e0B\u5e27) + `-ar 48000 -ac 2`(\u7edf\u4e00\u97f3\u9891)\n\n31. **crf\u503c\u5bf9\u5f55\u5c4f\u7c7b\u5185\u5bb9\u8981\u4f4e** \u2014 \u5f55\u5c4f\u6587\u5b57\u5bc6\u96c6\uff0ccrf 22\u7801\u7387\u4e0d\u591f\u4f1a\u7cca\u3002\u5f55\u5c4f\u7528`-crf 18`\u6216`-b:v 3M`\u3002\u4e00\u822cvlog\u7d20\u6750crf 22\u5373\u53ef\n\n32. **\u591a\u6b21\u91cd\u7f16\u7801\u753b\u8d28\u7d2f\u79ef\u635f\u5931** \u2014 \u526a\u8f91\u2192\u62fc\u63a5\u2192\u52a0\u80cc\u666f\u2192\u53e0\u8fdb\u5ea6\u6761\uff0c\u6bcf\u6b21\u7f16\u7801\u90fd\u635f\u5931\u753b\u8d28\u3002**\u5c3d\u91cf\u4e00\u6b21filter\u94fe\u641e\u5b9a**\uff0c\u51cf\u5c11\u7f16\u7801\u6b21\u6570\n\n33. **\u540e\u53f0ffmpeg\u8fdb\u7a0b\u6cc4\u6f0f** \u2014 kill\u540e\u53f0\u4efb\u52a1\u65f6\u53ef\u80fd\u53ea\u6740shell\u4e0d\u6740ffmpeg\u5b50\u8fdb\u7a0b\u3002\u6bcf\u6b21kill\u540e`ps aux | grep ffmpeg`\u786e\u8ba4\uff0c\u6b8b\u7559\u7528`kill -9 PID`\n\n34. **\u5f00\u5de5\u524d\u7b2c\u4e00\u4ef6\u4e8b\u786e\u8ba4\u8f93\u51fa\u5206\u8fa8\u7387** \u2014 \u5168\u6d41\u7a0b\u7edf\u4e00\u5206\u8fa8\u7387\u4e0d\u8981\u4e2d\u9014\u6539\u3002\u5f55\u5c4f\u7c7b\u75281080p\uff08\u6587\u5b57\u5bc6\u96c6\u5bf9\u5206\u8fa8\u7387\u654f\u611f\uff09\uff0cvlog\u7d20\u6750\u7528\u539f\u59cb\u5206\u8fa8\u7387\u62161080p\n\n35. **\u26a0\ufe0f overlay\u89c6\u9891\u65f6\u97f3\u9891\u88ab\u8986\u76d6/\u4e22\u5931** \u2014 \u7528filter_complex overlay\u53e0\u52a0\u8fdb\u5ea6\u6761/\u6c34\u5370\u7b49\u89c6\u9891\u65f6\uff0cffmpeg\u53ef\u80fd\u81ea\u52a8\u9009\u53d6overlay\u6e90\uff08\u9759\u97f3\uff09\u7684\u97f3\u9891\u800c\u4e0d\u662f\u539f\u89c6\u9891\u7684\u97f3\u9891\u3002**\u5fc5\u987b\u7528`-map`\u660e\u786e\u6307\u5b9a\u97f3\u9891\u6765\u6e90**\uff1a\n```bash\n# \u274c \u4e0d\u6307\u5b9amap\uff0c\u97f3\u9891\u53ef\u80fd\u6765\u81eaoverlay\u6e90\uff08\u9759\u97f3\uff09\nffmpeg -i main.mp4 -i overlay.mp4 -filter_complex \"overlay=0:0\" output.mp4\n\n# \u2705 \u660e\u786emap\uff1a\u89c6\u9891\u53d6filter\u8f93\u51fa\uff0c\u97f3\u9891\u53d6\u539f\u89c6\u9891\nffmpeg -i main.mp4 -i overlay.mp4 \\\n  -filter_complex \"[0:v][1:v]overlay=0:0[vout]\" \\\n  -map \"[vout]\" -map 0:a \\\n  -c:v libx264 -c:a copy output.mp4\n```\n\u5982\u679c\u8fd8\u4e0d\u884c\uff08\u97f3\u9891\u7f16\u89e3\u7801\u95ee\u9898\uff09\uff0c**\u5206\u6b65\u505a\u66f4\u53ef\u9760**\uff1a\n```bash\n# Step1: \u53ea\u5904\u7406\u89c6\u9891\uff08-an\u53bb\u6389\u97f3\u9891\uff09\nffmpeg -i main.mp4 -i overlay.mp4 -filter_complex \"overlay\" -map \"[vout]\" -an video_only.mp4\n# Step2: \u628a\u539f\u59cb\u97f3\u9891\u5408\u56de\u6765\nffmpeg -i video_only.mp4 -i main.mp4 -map 0:v -map 1:a -c:v copy -c:a copy final.mp4\n```\n\n36. **\u26a0\ufe0f -shortest\u4f1a\u622a\u65ad\u97f3\u9891** \u2014 \u89c6\u9891\u548c\u97f3\u9891\u65f6\u957f\u4e0d\u5b8c\u5168\u4e00\u81f4\u65f6\uff0c`-shortest`\u4f1a\u4e22\u6389\u6700\u540e\u51e0\u79d2\u7684\u97f3\u9891\u3002**\u4f18\u5148\u7528`-t {\u7cbe\u786e\u65f6\u957f}`\u66ff\u4ee3**\uff0c\u6216\u8005\u786e\u4fdd\u97f3\u89c6\u9891\u7b49\u957f\u540e\u4e0d\u52a0-shortest\n\n## \u7aef\u5230\u7aef\u793a\u4f8b\n\n### \u9879\u76ee\u6587\u4ef6\u7ed3\u6784\n\n```\nmy-vlog-project/\n\u251c\u2500\u2500 footage/                    # \u539f\u59cb\u7d20\u6750\uff08\u7528\u6237\u63d0\u4f9b\uff09\n\u2502   \u251c\u2500\u2500 clip_001.MOV\n\u2502   \u2514\u2500\u2500 ...\n\u2502\n\u251c\u2500\u2500 analysis/                   # \u9636\u6bb53\u8f93\u51fa\n\u2502   \u2514\u2500\u2500 clip_analysis.json\n\u2502\n\u251c\u2500\u2500 edit_plan.json              # \u9636\u6bb54\u8f93\u51fa\n\u251c\u2500\u2500 edit_plan_fixed.json        # \u9636\u6bb54.5\u8f93\u51fa\n\u2502\n\u251c\u2500\u2500 output/\n\u2502   \u251c\u2500\u2500 dashboard.html          # Dashboard\u7f51\u9875\n\u2502   \u251c\u2500\u2500 thumbnails/             # \u7d20\u6750\u7f29\u7565\u56fe\n\u2502   \u251c\u2500\u2500 qc_frames/              # \u6210\u54c1QC\u5e27\n\u2502   \u251c\u2500\u2500 segments/               # \u88c1\u526a\u540e\u7684\u7247\u6bb5\n\u2502   \u251c\u2500\u2500 highlights/             # \u7247\u5934\u9ad8\u5149\u8499\u592a\u5947\n\u2502   \u251c\u2500\u2500 titles/                 # \u6bb5\u843d\u6807\u9898PNG\n\u2502   \u251c\u2500\u2500 sections/               # \u5e26\u6807\u9898\u7684\u6bb5\u843d\u89c6\u9891\n\u2502   \u251c\u2500\u2500 bgm/                    # BGM\u6587\u4ef6\n\u2502   \u251c\u2500\u2500 video_no_bgm.mp4        # \u65e0BGM\u7684\u5b8c\u6574\u89c6\u9891\n\u2502   \u2514\u2500\u2500 final.mp4               # \ud83c\udfac \u6210\u54c1\u89c6\u9891\n\u2502\n\u2514\u2500\u2500 reference/                  # \u9636\u6bb52\u8f93\u51fa\uff08\u53ef\u9009\uff09\n```\n\n### \u4e2d\u95f4\u4ea7\u7269\u793a\u4f8b\n\n\u89c1 `examples/` \u76ee\u5f55\u4e0b\u7684\u793a\u4f8b\u6587\u4ef6\u3002\n\n### \u6267\u884c\u6d41\u7a0b\u901f\u67e5\n\n```\n\u7d20\u6750\u6587\u4ef6\u5939 footage/\n     \u2502\n     \u25bc\n \u9636\u6bb51: \u7d20\u6750\u76d8\u70b9 \u2192 ffprobe\u6279\u91cf\u626b\u63cf \u2192 \u6e05\u5355\n     \u2502\n     \u25bc\n \u9636\u6bb52: \u53c2\u8003\u7814\u7a76 \u2192 \u4e0b\u8f7d\u4f18\u8d28vlog \u2192 \u5206\u6790\u8282\u594f\uff08\u53ef\u9009\uff09\n     \u2502\n     \u25bc\n \u9636\u6bb53: \u7d20\u6750\u4e09\u7ef4\u5206\u6790 \u2192 whisper/FunASR+\u97f3\u91cf+\u89c6\u89c9 \u2192 clip_analysis.json\n     \u2502\n     \u25bc\n \u9636\u6bb53.5: \u7cbe\u4fee\u9884\u5904\u7406 \u2192 \u53e3\u4ee4/\u91cd\u590d/\u6643\u52a8/\u7eaf\u753b\u9762\u6807\u6ce8\n     \u2502\n     \u25bc\n \u9636\u6bb54: LLM\u53d9\u4e8b\u7f16\u6392 \u2192 edit_plan.json\n     \u2502\n     \u25bc\n \u9636\u6bb54+: Dashboard\u53ef\u89c6\u5316 \u2192 \u6d4f\u89c8\u5668\u9884\u89c8\u786e\u8ba4\n     \u2502\n     \u25bc\n \u9636\u6bb54.5: \u8bed\u97f3\u622a\u65ad\u6821\u9a8c \u2192 \u81ea\u52a8\u4fee\u590d\n     \u2502\n     \u25bc\n \u9636\u6bb55: ffmpeg\u6e32\u67d3 \u2192 \u88c1\u526a\u2192\u6807\u9898\u2192\u62fc\u63a5\u2192BGM \u2192 final.mp4 \ud83c\udfac\n```\n\n## \u9a8c\u8bc1\n\n- [ ] \u7d20\u6750\u76d8\u70b9\u5b8c\u6210\uff0c\u751f\u6210\u6e05\u5355\n- [ ] \u53c2\u8003\u7814\u7a76\u5b8c\u6210\uff0c\u63d0\u53d6\u89c4\u5f8b\n- [ ] \u7d20\u6750\u4e09\u7ef4\u5206\u6790\u5b8c\u6210\uff08\u89c6\u89c9+\u97f3\u9891+\u97f3\u91cf\uff09\n- [ ] \u7d20\u6750\u7cbe\u4fee\u9884\u5904\u7406\u5b8c\u6210\n- [ ] LLM\u526a\u8f91\u65b9\u6848\u751f\u6210\n- [ ] \u8bed\u97f3\u622a\u65ad\u6821\u9a8c\u901a\u8fc7\n- [ ] **\u6bcf\u6bb5\u7247\u6bb5\u97f3\u89c6\u9891\u65f6\u957f\u5dee<0.05s**\n- [ ] ffmpeg\u6267\u884c\u6210\u529f\uff0c\u8f93\u51fa\u6210\u54c1\u89c6\u9891\n- [ ] **\u6210\u54c1\u97f3\u9891\u5b8c\u6574\uff08\u4e0d\u88ab-shortest\u622a\u65ad\uff09**\n- [ ] **\u62d6\u5230\u89c6\u9891\u540e\u534a\u6bb5\u68c0\u67e5\u97f3\u753b\u540c\u6b65**\n- [ ] \u6210\u54c1\u89c6\u9891\u8d28\u91cf\u4eba\u5de5\u786e\u8ba4\n\n---\n\n**Author:** nyx\u7814\u7a76\u6240 \u00b7 [GitHub](https://github.com/znyupup) \u00b7 [B\u7ad9](https://space.bilibili.com/4330525) \u00b7 \u5c0f\u7ea2\u4e66 @nyx\u7814\u7a76\u6240\n**License:** MIT\n","usage":"\u8bfb\u53d6 SKILL.md\uff0c\u5e76\u6309\u9700\u8bfb\u53d6\u914d\u5957\u6587\u4ef6\uff1b\u4e0b\u8f7d ZIP \u83b7\u53d6\u5b8c\u6574\u5305\u3002\u6280\u80fd\u662f\u4efb\u52a1\u65b9\u6cd5\uff0c\u4e0d\u6388\u4e88\u65b0\u589e\u8d26\u6237\u3001\u4ed8\u8d39\u6216\u7cfb\u7edf\u6743\u9650\u3002"}
