video-edit
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseVideo Edit
视频编辑
One raw take in, one finished video out: . Either a vertical short, or a full horizontal video for YouTube.
video-agent/edits/<slug>/final.mp4The tools are free and run on the creator's machine:
- Remotion (videos written in React) and its official TikTok template, which ships local speech-to-text with word timings (whisper.cpp).
- ffmpeg for the checks and the sound.
The look is fixed and simple: references/style.md. The code that draws it is in references/remotion-short.md.
一段原始素材进,一个成片出:。可输出竖版短片,或用于 YouTube 的完整横版视频。
video-agent/edits/<slug>/final.mp4这些工具免费,且在创作者自己的设备上运行:
- Remotion(使用 React 编写视频)及其官方 TikTok 模板。模板内置了带逐词时间戳的本地语音转文字功能(whisper.cpp)。
- ffmpeg 用于检查和声音处理。
视觉风格固定且简洁:references/style.md。实现该视觉效果的代码位于 references/remotion-short.md。
How to run it
如何运行
- Preflight: . Anything missing: stop and send them to
node -v && ffmpeg -version && git --version./video-setup tools - Talk in the creator's language. Say what you do in one line before each long step (install, transcription, render) and how long it takes.
- Read for the language and the accent color. No voice file: ask both, or use English and
video-agent/voice.md.#FFD400 - A named style (in the request, made by
style <name>): read/video-stealand apply it on top of style.md. Its values win, its sound effects, stickers and grade go into the edit as it says, and everything it does not name stays as in style.md.video-agent/styles/<name>.md - : lowercase, dashes, no dots or spaces (the transcriber cuts file names at the first dot).
<slug> - Steps 1 and 2 run from the project root. From step 3 on, every command runs in and its paths are relative to it.
video-agent/studio/
- 预检:。如果缺少任何工具:停止操作,并提示用户运行
node -v && ffmpeg -version && git --version。/video-setup tools - 使用创作者的语言交流。在每个耗时步骤(安装、转录、渲染)前,用一行说明你将执行什么以及预计需要多长时间。
- 阅读 ,获取语言和强调色。如果没有 voice 文件:询问这两项,或使用英语和
video-agent/voice.md。#FFD400 - 如果请求中指定了样式名称(由 创建):读取
/video-steal,并在 style.md 的基础上应用它。其中的值优先;它指定的声音效果、贴纸和调色应按其说明加入剪辑,未提及的内容则保持 style.md 中的设置。video-agent/styles/<name>.md - :全部小写,使用短横线分隔,不含点号或空格(转录工具会在第一个点号处截断文件名)。
<slug> - 第 1 步和第 2 步在项目根目录运行。从第 3 步开始,每条命令都在 中运行,其路径均相对于该目录。
video-agent/studio/
1. The take and the format
1. 素材与格式
Use the file they name, else the newest file in . Check it:
video-agent/takes/bash
ffprobe -v error -show_entries format=duration:stream=codec_type,codec_name,width,height,r_frame_rate,avg_frame_rate,color_transfer -of compact "<take>"- No audio stream: stop and say so.
- is
color_transferorsmpte2084: an HDR take (the iPhone default). Don't convert it: Remotion maps it to normal colors when it renders. Tell them to turn off HDR video in the camera settings for the next takes.arib-std-b67 - Otherwise, a or a variable frame rate (
.webmandr_frame_ratediffer): convert it, Remotion and the ffmpeg cut both drift on those. The new file is the take from now on:avg_frame_ratebashffmpeg -y -i "<take>" -vf fps=30 -c:v libx264 -crf 18 -preset veryfast -pix_fmt yuv420p -c:a aac -b:a 192k -ar 48000 "video-agent/takes/<slug>-30fps.mp4"
Then the format. They said it (, , "for YouTube", "a reel"): take it. Otherwise pick from the take and say which in one line:
shortlong| Format | Composition | When | Defaults |
|---|---|---|---|
| Short | | a take under 90 s, or they want a short, a reel, a TikTok | captions on, hook card, a callout every 8 to 10 s |
| Long | | a horizontal take over 90 s, a full YouTube video, a tutorial, a vlog | captions on (ask: YouTube viewers often prefer the .srt alone, |
- Horizontal take in Short: it is cropped to the center. Warn them if they are not in the middle of the frame.
- Vertical take in Long: it would be cropped to a thin band. Don't; offer Short.
- Long: say the time first. Transcription runs at about real time on a laptop with the model (a 20-minute take, about 20 minutes;
mediumis 2 to 3 times faster), and the render takes 3 to 5 s per second of video (a 15-minute cut, about 1 hour). Both run in the background.small
<comp>ShortWideFind the script it comes from: the matching script in (same slug or same first line). No script is fine; the cuts then rely on the transcript only.
video-agent/scripts/使用用户指定的文件;否则,使用 中最新的文件。检查素材:
video-agent/takes/bash
ffprobe -v error -show_entries format=duration:stream=codec_type,codec_name,width,height,r_frame_rate,avg_frame_rate,color_transfer -of compact "<take>"- 没有音频流:停止操作并告知用户。
- 为
color_transfer或smpte2084:这是 HDR 素材(iPhone 的默认设置)。不要转换它;Remotion 在渲染时会将其映射为普通颜色。提醒用户在后续拍摄时关闭相机设置中的 HDR 视频。arib-std-b67 - 其他情况下,如果素材是 或使用可变帧率(
.webm与r_frame_rate不同),则需要转换;Remotion 和 ffmpeg 剪切在这类素材上都会产生时间漂移。转换后的文件将从现在起作为当前素材:avg_frame_ratebashffmpeg -y -i "<take>" -vf fps=30 -c:v libx264 -crf 18 -preset veryfast -pix_fmt yuv420p -c:a aac -b:a 192k -ar 48000 "video-agent/takes/<slug>-30fps.mp4"
接下来确定格式。如果用户已经说明过(、、“用于 YouTube”、“a reel”):遵从其要求。否则,根据素材选择格式,并用一行说明选择结果:
shortlong| 格式 | 合成 | 适用情况 | 默认设置 |
|---|---|---|---|
| 短片 | | 素材短于 90 秒,或用户需要短片、Reel、TikTok 视频 | 开启字幕、钩子卡片,每 8 至 10 秒添加一个重点标注 |
| 长视频 | | 横版素材超过 90 秒,或需要完整的 YouTube 视频、教程、Vlog | 开启字幕(询问:YouTube 观众通常只需要 .srt,是否设置 |
- 在短片中使用横版素材:画面会从中央裁切。如果人物不在画面中央,提醒用户。
- 在长视频中使用竖版素材:画面会被裁成一条很窄的横带。不要这样处理;建议改用短片。
- 长视频:先说明预计耗时。转录在笔记本电脑上的速度大约接近实时,使用 模型时耗时约为素材时长的同等时间(20 分钟素材约需 20 分钟;
medium模型快 2 至 3 倍),渲染速度约为每秒视频需要 3 至 5 秒(15 分钟成片约需 1 小时)。两项任务均在后台运行。small
下文中的 为 或 。
<comp>ShortWide查找素材对应的脚本: 中 slug 相同或首行相同的脚本。没有脚本也可以;此时剪切将仅依据转录文本进行。
video-agent/scripts/2. The studio (first time only)
2. Studio(仅首次需要)
Tell them first: about 1 GB of packages, then on the first transcription the speech engine and its model (0.5 to 1.5 GB) and a few minutes of setup.
bash
git clone --depth 1 https://github.com/remotion-dev/template-tiktok video-agent/studio
cd video-agent/studio
npm i- Delete so the studio is a plain folder of their project.
video-agent/studio/.git - Language, in :
whisper-config.mjs- English: keep and
WHISPER_MODEL = "medium.en".WHISPER_LANG = "en" - Any other language: (or
WHISPER_MODEL = "medium"on a slower machine, 466 MB) and"small"= its code (WHISPER_LANG,"fr","es","de"...)."pt"
- English: keep
- Add the and
Shortcompositions and the font package: follow references/remotion-short.md sections 1 to 3, thenWide. A studio made beforenpx tsc --noEmitexisted: replaceWideandsrc/Short/index.tsxwith the current sections 2 and 3.src/Root.tsx - Optional, better Remotion code from Claude: .
npx skills add remotion-dev/skills
先告知用户:需要下载约 1 GB 的软件包;首次转录时还需下载语音引擎及其模型(0.5 至 1.5 GB),并花费几分钟完成设置。
bash
git clone --depth 1 https://github.com/remotion-dev/template-tiktok video-agent/studio
cd video-agent/studio
npm i- 删除 ,使 studio 成为项目中的普通文件夹。
video-agent/studio/.git - 在 中设置语言:
whisper-config.mjs- 英语:保留 和
WHISPER_MODEL = "medium.en"。WHISPER_LANG = "en" - 其他语言:使用 (如果设备较慢,则使用
WHISPER_MODEL = "medium",大小为 466 MB),并将"small"设为对应语言代码(WHISPER_LANG、"fr"、"es"、"de"等)。"pt"
- 英语:保留
- 添加 和
Short合成以及字体包:按照 references/remotion-short.md 的第 1 至第 3 节操作,然后运行Wide。如果 studio 是在npx tsc --noEmit出现之前创建的:将Wide和src/Short/index.tsx替换为当前文档的第 2 节和第 3 节内容。src/Root.tsx - 可选:安装 Claude 提供的更优质 Remotion 代码:。
npx skills add remotion-dev/skills
3. Transcribe
3. 转录
bash
cp "../takes/<take file>" public/<slug>.mp4
node sub.mjs public/<slug>.mp4It writes : one entry per word, . It skips a file that already has its JSON: delete the JSON to transcribe again.
public/<slug>.json{ "text": " word", "startMs", "endMs", "confidence" }Fix misheard words from the script (names, brands, numbers): change only, never the times.
textbash
cp "../takes/<take file>" public/<slug>.mp4
node sub.mjs public/<slug>.mp4它会生成 :每个单词对应一个条目,格式为 。如果文件已有对应的 JSON,它会跳过该文件:删除 JSON 即可重新转录。
public/<slug>.json{ "text": " word", "startMs", "endMs", "confidence" }根据脚本修正误识别的单词(姓名、品牌、数字):只修改 ,绝不修改时间戳。
text4. The cut list
4. 剪切清单
Find the real silences, then list the gaps between words:
bash
ffmpeg -nostats -i public/<slug>.mp4 -af silencedetect=noise=-35dB:d=0.3 -f null - 2>&1 | grep silence_
node -e 'const w=require("./public/<slug>.json"),G=350,B=100,A=150,s=(ms)=>(ms/1000).toFixed(2);let r=[{fromMs:Math.max(0,w[0].startMs-B)}];for(let i=1;i<w.length;i++){const g=w[i].startMs-w[i-1].endMs;if(g>G){console.error(`gap ${s(w[i-1].endMs)} -> ${s(w[i].startMs)} (${g} ms) after "${w[i-1].text.trim()}"`);r[r.length-1].toMs=w[i-1].endMs+A;r.push({fromMs:w[i].startMs-B})}}r[r.length-1].toMs=w[w.length-1].endMs+300;console.log(JSON.stringify(r))'The second command prints every gap over 0.35 s with the word before it, then the kept with the margins of style.md. Then fix the ranges:
ranges- A gap that silencedetect does not report (a breath, a soft word, music): join its two ranges back.
- Retakes: walk the script line by line. When a line (or its first 3 words) appears more than once, keep the last complete attempt and drop the ranges from the first attempt's first word to the start of the kept one.
- Drop false starts (an "uh" or a half word before a restart), anything before the hook, and off-script talk.
Long video: same rules, with gaps over 0.5 s instead of 0.35 s (): a full video needs some air, a thinking pause can stay. Keep asides that add something; cut the ones that go nowhere. With no script, a retake is a sentence said twice in a row: keep the last one.
G=500Show the result before rendering, in a few lines:
text
Take 1:42 → short 0:47 (-55 s)
- 38 pauses cut (41 s)
- 3 retakes dropped: line 2 (x2), line 7
- dropped "ok let me do that again" at 0:58找出真正的静音,然后列出单词之间的间隔:
bash
ffmpeg -nostats -i public/<slug>.mp4 -af silencedetect=noise=-35dB:d=0.3 -f null - 2>&1 | grep silence_
node -e 'const w=require("./public/<slug>.json"),G=350,B=100,A=150,s=(ms)=>(ms/1000).toFixed(2);let r=[{fromMs:Math.max(0,w[0].startMs-B)}];for(let i=1;i<w.length;i++){const g=w[i].startMs-w[i-1].endMs;if(g>G){console.error(`gap ${s(w[i-1].endMs)} -> ${s(w[i].startMs)} (${g} ms) after "${w[i-1].text.trim()}"`);r[r.length-1].toMs=w[i-1].endMs+A;r.push({fromMs:w[i].startMs-B})}}r[r.length-1].toMs=w[w.length-1].endMs+300;console.log(JSON.stringify(r))'第二条命令会输出所有超过 0.35 秒的间隔及其前一个单词,然后输出保留的 ,其中包含 style.md 规定的边距。接下来调整 ranges:
ranges- 如果某个间隔未被 silencedetect 报告(可能是呼吸声、较轻的单词或音乐),将前后两个 ranges 合并。
- 重录片段:逐行核对脚本。当某一行(或其前 3 个单词)出现多次时,保留最后一次完整尝试,并从第一次尝试的首词开始处剪切到所保留尝试的起始位置。
- 删除错误开头(重新录制前的“呃”或半个单词)、钩子之前的所有内容,以及偏离脚本的闲聊。
长视频遵循相同规则,但将间隔阈值改为超过 0.5 秒():完整视频需要保留一些呼吸空间,思考时的停顿可以保留。保留有价值的补充内容,删除没有实际信息的闲聊。没有脚本时,如果同一句话连续说了两遍,则视为重录并保留后一遍。
G=500渲染前,用几行文字展示结果:
text
素材 1:42 → 短片 0:47(-55 秒)
- 剪掉 38 处停顿(41 秒)
- 删除 3 次重录:第 2 行(x2)、第 7 行
- 删除 0:58 处的“好,让我再来一次”5. Motion
5. 动态效果
From the script's visual notes (or your own picks when there are none):
- Hook card: the on-screen hook, 6 words or fewer, one word, 2 to 3 s.
*accent* - Callouts: a number, a result or a tool name the person says, about one every 8 to 10 s, never two within 4 s (Long: one every 30 to 60 s at most, only the key numbers). = that word's
atMsin the JSON.startMs
Write (format in remotion-short.md section 4).
public/<slug>.edit.json根据脚本中的视觉说明进行设计;如果没有视觉说明,则自行选择:
- 钩子卡片:显示在屏幕上的开场钩子,最多 6 个词,其中一个词使用强调样式,持续 2 至 3 秒。
- 重点标注:标注人物提到的数字、结果或工具名称,大约每 8 至 10 秒出现一次,任意 4 秒内不得出现两个。长视频中每 30 至 60 秒最多出现一次,并且仅标注关键数字。等于该词在 JSON 中的
atMs。startMs
编写 (格式参见 remotion-short.md 第 4 节)。
public/<slug>.edit.json6. QC, then render
6. 质检,然后渲染
Render 3 or 4 stills: the hook (frame 30), a callout, a caption mid-video, the last second (Long: 6 to 8 stills spread over the video). Short only: lay the platforms' safe zone over each one:
bash
npx remotion still <comp> out/<slug>-qc-30.png --frame=30 --props=public/<slug>.edit.json
ffmpeg -y -v error -i out/<slug>-qc-30.png -vf "drawbox=x=0:y=0:w=iw:h=230:color=red@0.35:t=fill,drawbox=x=0:y=1440:w=iw:h=480:color=red@0.35:t=fill,drawbox=x=850:y=900:w=230:h=540:color=red@0.35:t=fill" out/<slug>-qc-30-safe.pngRead each PNG (Long: each still): no text in a red zone (the app's buttons, name and description cover it), captions below the face, hook card readable, callout not covering the eyes, accent color right. Fix the edit file, then:
-safebash
npx remotion render <comp> out/<slug>.mp4 --props=public/<slug>.edit.jsonAbout 3 to 5 s of render per second of video on a laptop. Run it in the background and say how long.
先渲染 3 或 4 张静帧:钩子(第 30 帧)、一个重点标注、视频中段的一处字幕、最后一秒(长视频:均匀分布 6 至 8 张静帧)。仅短片需要在每张图上叠加各平台的安全区域:
bash
npx remotion still <comp> out/<slug>-qc-30.png --frame=30 --props=public/<slug>.edit.json
ffmpeg -y -v error -i out/<slug>-qc-30.png -vf "drawbox=x=0:y=0:w=iw:h=230:color=red@0.35:t=fill,drawbox=x=0:y=1440:w=iw:h=480:color=red@0.35:t=fill,drawbox=x=850:y=900:w=230:h=540:color=red@0.35:t=fill" out/<slug>-qc-30-safe.png读取每张 PNG(长视频:读取每张静帧):红色区域内不得有文字(应用按钮、名称和描述会遮挡该区域),字幕应位于人脸下方,钩子卡片应清晰可读,重点标注不得遮挡眼睛,强调色应正确。修正编辑文件,然后运行:
-safebash
npx remotion render <comp> out/<slug>.mp4 --props=public/<slug>.edit.json在笔记本电脑上,每秒视频的渲染时间约为 3 至 5 秒。在后台运行渲染,并说明预计耗时。
7. Sound
7. 声音
Music (only if they ask, royalty-free, e.g. the YouTube Audio Library) goes in first, 22 dB under the voice:
bash
ffmpeg -y -i out/<slug>.mp4 -stream_loop -1 -i "<music file>" -filter_complex "[1:a]volume=-22dB[m];[0:a][m]amix=inputs=2:duration=first:normalize=0[a]" -map 0:v -map "[a]" -c:v copy -c:a aac -b:a 192k out/<slug>-music.mp4Then level the render ( when there is music). Remotion does not level the voice, and a plain pass lands 2 to 3 LU too quiet on clips this short. Measure:
out/<slug>-music.mp4loudnormbash
ffmpeg -nostats -i out/<slug>.mp4 -af ebur128=peak=true -f null - 2>&1 | grep -E "^\s+(I|Peak):"Gain = -14 minus the value (e.g. I = -17.0 → ), with a limiter:
I:3.0dBbash
mkdir -p ../edits/<slug>
ffmpeg -y -i out/<slug>.mp4 -c:v copy -af "volume=<gain>dB,alimiter=limit=0.8:level=false" -c:a aac -b:a 192k -ar 48000 ../edits/<slug>/final.mp4Measure the same way: I between -15 and -13 and Peak under -1 dBFS is right.
final.mp4仅在用户要求时添加音乐(免版税音乐,例如 YouTube Audio Library)。先加入音乐,音量比人声低 22 dB:
bash
ffmpeg -y -i out/<slug>.mp4 -stream_loop -1 -i "<music file>" -filter_complex "[1:a]volume=-22dB[m];[0:a][m]amix=inputs=2:duration=first:normalize=0[a]" -map 0:v -map "[a]" -c:v copy -c:a aac -b:a 192k out/<slug>-music.mp4然后对渲染文件进行响度标准化(添加音乐时使用 )。Remotion 不会对人声进行响度标准化;对于这种时长的片段,直接使用 会低 2 至 3 LU。进行测量:
out/<slug>-music.mp4loudnormbash
ffmpeg -nostats -i out/<slug>.mp4 -af ebur128=peak=true -f null - 2>&1 | grep -E "^\s+(I|Peak):"增益 = -14 减去 的值(例如,I = -17.0 → ),并使用限幅器:
I:3.0dBbash
mkdir -p ../edits/<slug>
ffmpeg -y -i out/<slug>.mp4 -c:v copy -af "volume=<gain>dB,alimiter=limit=0.8:level=false" -c:a aac -b:a 192k -ar 48000 ../edits/<slug>/final.mp4以相同方式测量 :I 介于 -15 至 -13 且 Peak 低于 -1 dBFS 即为合适。
final.mp48. Deliver
8. 交付
In :
video-agent/edits/<slug>/- , ready to post.
final.mp4 - : the words moved to the cut timeline, up to 7 words per cue, a new cue after each sentence. Platforms read it as closed captions.
captions.srtbashnode -e 'const e=require("./public/<slug>.edit.json"),w=require("./public/"+e.captions),F=30,fr=(ms)=>Math.round(ms/1000*F);let at=0;const p=e.ranges.map(r=>{const f=fr(r.fromMs),n=Math.max(1,fr(r.toMs)-f),q={...r,s:(at-f)/F*1000};at+=n;return q});const o=w.flatMap(x=>{const q=p.find(q=>x.startMs>=q.fromMs&&x.startMs<q.toMs);return q?[{t:x.text.trim(),a:x.startMs+q.s,b:Math.min(x.endMs,q.toMs)+q.s}]:[]});const ts=(ms)=>new Date(Math.max(0,Math.round(ms))).toISOString().slice(11,23).replace(".",",");let c=[],n=0,out="";const flush=()=>{if(c.length)out+=`${++n}\n${ts(c[0].a)} --> ${ts(c[c.length-1].b)}\n${c.map(x=>x.t).join(" ")}\n\n`;c=[]};for(const x of o){c.push(x);if(c.length===7||/[.?!]$/.test(x.t))flush()}flush();process.stdout.write(out)' > ../edits/<slug>/captions.srt - : a copy of
edit.json, to redo the video later.public/<slug>.edit.json - : the text to paste when posting (below).
post.md - Long only, in : chapters for the YouTube description, one every 1 to 3 minutes where the topic changes, times on the cut timeline (from
post.md), the first atcaptions.srt, at least 3, each 10 s or longer.0:00
Then say: length, what was cut, where the files are, and offer one change ("shorter hook card? fewer callouts? another accent?"). A change = edit the JSON, render again (steps 6 and 7). Next: .
/video-publish <slug>在 中提供:
video-agent/edits/<slug>/- :可直接发布。
final.mp4 - :将单词移动到剪辑后的时间轴上,每个字幕块最多 7 个词,每句话后开始一个新的字幕块。各平台会将其作为关闭式字幕读取。
captions.srtbashnode -e 'const e=require("./public/<slug>.edit.json"),w=require("./public/"+e.captions),F=30,fr=(ms)=>Math.round(ms/1000*F);let at=0;const p=e.ranges.map(r=>{const f=fr(r.fromMs),n=Math.max(1,fr(r.toMs)-f),q={...r,s:(at-f)/F*1000};at+=n;return q});const o=w.flatMap(x=>{const q=p.find(q=>x.startMs>=q.fromMs&&x.startMs<q.toMs);return q?[{t:x.text.trim(),a:x.startMs+q.s,b:Math.min(x.endMs,q.toMs)+q.s}]:[]});const ts=(ms)=>new Date(Math.max(0,Math.round(ms))).toISOString().slice(11,23).replace(".",",");let c=[],n=0,out="";const flush=()=>{if(c.length)out+=`${++n}\n${ts(c[0].a)} --> ${ts(c[c.length-1].b)}\n${c.map(x=>x.t).join(" ")}\n\n`;c=[]};for(const x of o){c.push(x);if(c.length===7||/[.?!]$/.test(x.t))flush()}flush();process.stdout.write(out)' > ../edits/<slug>/captions.srt - :
edit.json的副本,以便日后重新制作视频。public/<slug>.edit.json - :发布时粘贴的文案(见下文)。
post.md - 仅长视频需要在 中提供 章节:为 YouTube 描述添加章节,在主题发生变化的位置每 1 至 3 分钟设置一个;时间点基于剪辑后的时间轴(来自
post.md);第一个章节位于captions.srt;至少设置 3 个;每个章节至少持续 10 秒。0:00
然后说明:成片时长、剪除了哪些内容、文件位置,并提供一项修改建议(“缩短钩子卡片?减少重点标注?更换强调色?”)。如需修改,则编辑 JSON 并重新渲染(第 6 步和第 7 步)。下一步:。
/video-publish <slug>Post copy
发布文案
post.md- Title (YouTube Shorts): under 60 characters, the topic in the first 3 words. Long: under 70 characters, the promise of the video, true to it.
- Caption: the first 125 characters work on their own (apps cut there behind "more"). Then 1 or 2 short lines and one ask, the one from voice.md.
- Search words: the 2 or 3 words their viewer would type to find this video, written plainly in the title or the first line.
- Hashtags: 3 to 5 at the end, about the topic. No #fyp or #viral.
- Nothing the video doesn't say: no new number, claim or promise.
Run the humanize pass of (its ) on it when that skill is installed.
video-scriptreferences/humanize.mdtext
Title: Your hook is too long
Caption: Nobody hears word 12 of your hook. Cut it to 12 words and put the result first.
Try it on your next three videos.
Follow for one script fix a day.
#shortformvideo #videohooks #contentcreatorpost.md- 标题(YouTube Shorts):少于 60 个字符,前 3 个词包含主题。长视频:少于 70 个字符,准确呈现视频所作的承诺。
- 说明文字:前 125 个字符单独看也应成立(应用会在“更多”处截断内容)。然后写 1 至 2 行短句和一个行动号召,行动号召取自 voice.md。
- 搜索词:写出观众会输入来查找该视频的 2 至 3 个词,以自然方式出现在标题或第一行中。
- Hashtags:在结尾添加 3 至 5 个与主题相关的标签。不要使用 #fyp 或 #viral。
- 不要加入视频中未提及的内容:不得添加新的数字、主张或承诺。
如果已安装 skill,则对文案运行其 humanize 流程()。
video-scriptreferences/humanize.mdtext
标题:你的钩子太长
说明:没人能听清钩子的第 12 个词。将它缩短到 12 个词,并放在最前面。
在接下来的三个视频中试试。
每天关注一个脚本改进。
#shortformvideo #videohooks #contentcreatorCapCut, Premiere or no Remotion
CapCut、Premiere 或不使用 Remotion
Some creators prefer their editor, and Remotion needs a paid license for companies of more than 3 people. Do steps 1 to 4, write with the ranges (, ), skip steps 5 to 7, and give them (step 8) plus:
public/<slug>.edit.json"hook": null"callouts": []captions.srt- , the kept ranges to cut by hand:
cuts.mdbashmkdir -p ../edits/<slug> node -e 'const e=require("./public/<slug>.edit.json"),t=(ms)=>new Date(ms).toISOString().slice(14,23);for(const r of e.ranges)console.log(t(r.fromMs)+" → "+t(r.toMs))' > ../edits/<slug>/cuts.md - or a plain cut with ffmpeg alone:
bash
SEL=$(node -e 'const e=require("./public/<slug>.edit.json");console.log(e.ranges.map(r=>`between(t,${r.fromMs/1000},${r.toMs/1000})`).join("+"))') ffmpeg -y -i public/<slug>.mp4 -vf "select='$SEL',setpts=N/FRAME_RATE/TB" -af "aselect='$SEL',asetpts=N/SR/TB" -c:v libx264 -crf 18 -c:a aac ../edits/<slug>/cut.mp4
有些创作者更习惯使用自己的编辑器,而且人数超过 3 人的公司需要为 Remotion 购买付费许可证。完成第 1 至第 4 步,编写 ,其中仅包含 ranges(、),跳过第 5 至第 7 步,然后提供 (第 8 步)以及:
public/<slug>.edit.json"hook": null"callouts": []captions.srt- :列出保留下来供手动剪切的范围:
cuts.mdbashmkdir -p ../edits/<slug> node -e 'const e=require("./public/<slug>.edit.json"),t=(ms)=>new Date(ms).toISOString().slice(14,23);for(const r of e.ranges)console.log(t(r.fromMs)+" → "+t(r.toMs))' > ../edits/<slug>/cuts.md - 或仅使用 ffmpeg 直接剪切:
bash
SEL=$(node -e 'const e=require("./public/<slug>.edit.json");console.log(e.ranges.map(r=>`between(t,${r.fromMs/1000},${r.toMs/1000})`).join("+"))') ffmpeg -y -i public/<slug>.mp4 -vf "select='$SEL',setpts=N/FRAME_RATE/TB" -af "aselect='$SEL',asetpts=N/SR/TB" -c:v libx264 -crf 18 -c:a aac ../edits/<slug>/cut.mp4
Honest limits
已知限制
- whisper.cpp mishears names and jargon: the script fixes them, check numbers by eye.
- The cuts are good, not perfect: a cut can clip a breath or a soft last syllable. Watch the result once before posting.
- Claude cannot hear the take: it trusts the timings and silencedetect. Bad audio (music, echo, two voices) makes both worse.
- The safe zone is a middle ground between TikTok, Reels and Shorts; each app moves its buttons a little.
- Remotion license: free for individuals, companies of up to 3 people and non-profits; bigger companies need a company license (remotion.pro/license).
- whisper.cpp 可能会误识别姓名和行话:使用脚本进行修正,并人工检查数字。
- 剪切效果良好,但并不完美:切口可能会截掉呼吸声或较轻的尾音。发布前务必观看一次成片。
- Claude 无法听到原始素材,只能依赖时间戳和 silencedetect。糟糕的音频(音乐、回声、两个人同时说话)会降低两者的工作质量。
- 安全区域是 TikTok、Reels 和 Shorts 之间的折中方案;每个应用的位置都会有所不同。
- Remotion 许可证:个人、3 人及以下的公司和非营利组织免费;更大的公司需要购买公司许可证(remotion.pro/license)。