short-editing
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseShort Editing
短视频剪辑
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./studio-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/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。/studio-setup tools - 使用创作者的语言交流。在每个耗时步骤(安装、转录、渲染)前,用一行说明要做什么以及预计耗时。
- 读取 ,确认语言和强调色。如果没有语音文件:询问这两项,或使用英语和
video-agent/voice.md。#FFD400 - 如果请求中通过 指定了命名样式(由
style <name>创建):读取/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"
接下来确定格式。如果用户已经指定(、、“for YouTube”、“a reel”):直接采用。否则根据素材选择,并用一行说明选择结果:
shortlong| 格式 | 构图 | 适用场景 | 默认设置 |
|---|---|---|---|
| 短视频 | | 少于 90 秒的素材,或用户需要短视频、Reel、TikTok | 开启字幕、hook card、每 8 至 10 秒一个 callout |
| 长视频 | | 超过 90 秒的横版素材、完整 YouTube 视频、教程或 vlog | 开启字幕(询问:YouTube 观众通常更喜欢只使用 |
- 横版素材用于 Short:会被居中裁切。如果人物不在画面中央,要提醒用户。
- 竖版素材用于 Long:会被裁成很窄的一条。不要这样做;建议改用 Short。
- 长视频:先说明预计时间。在笔记本电脑上使用 模型转录时,速度约为实时(20 分钟的素材约需 20 分钟;
medium模型快 2 至 3 倍),渲染每 1 秒视频需要 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 代码 skill,以获得更优代码:。
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- 如果某个间隔未被 silencedetect 报告(一次呼吸、一个很轻的词、背景音乐),把前后两个范围重新合并。
- 重拍:逐行核对脚本。当某一行(或其前 3 个词)出现多次时,保留最后一次完整尝试,并从第一次尝试的首词开始到保留版本的起点之间的范围全部删除。
- 删除错误开始(重新录制前的“呃”或半个词)、hook 之前的内容,以及偏离脚本的闲聊。
长视频:采用相同规则,但将间隔阈值从 0.35 秒改为 0.5 秒():完整视频需要保留一些呼吸空间,思考时的停顿可以保留。保留有实际内容的插话;删除没有意义的插话。没有脚本时,如果同一句话连续说了两遍,就属于重拍:保留后一遍。
G=500渲染前先展示结果,格式如下:
text
素材 1:42 → 短视频 0:47 (-55 s)
- 剪掉 38 处停顿 (41 s)
- 删除 3 次重拍:第 2 行 (x2)、第 7 行
- 删除 0:58 处的“好吧,我再做一次”5. Motion
5. 动态效果(Motion)
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根据脚本中的视觉备注(如果没有,则自行选择):
- Hook card:屏幕上的 hook 不超过 6 个词,其中一个词用 强调色 标出,持续 2 至 3 秒。
- Callout:标注人物提到的数字、结果或工具名称。短视频约每 8 至 10 秒一个,4 秒内不要出现两个;长视频最多每 30 至 60 秒一个,并且只标关键数字。等于 JSON 中对应词的
atMs。startMs
编写 (格式见 remotion-short.md 第 4 节)。
public/<slug>.edit.json6. QC, then render
6. QC,然后渲染
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 张静帧:hook(第 30 帧)、一个 callout、视频中段的字幕、最后一秒(长视频:在全片均匀抽取 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(长视频:读取每张静帧):红色区域内不能有文字(应用按钮、名称和说明会遮挡这些区域),字幕应位于人物面部下方,hook card 应清晰可读,callout 不能遮挡眼睛,并确认强调色正确。修正编辑文件,然后:
-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>.mp4out/<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: .
/publish <slug>在 中提供:
video-agent/edits/<slug>/- :可直接发布。
final.mp4 - :将单词移动到剪切后的时间线上,每个字幕块最多 7 个词,每句话后开始新的字幕块。平台可将其作为 CC 字幕读取。
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
然后说明:视频长度、剪掉了什么、文件位置,并提供一项修改建议(“hook card 更短?callout 更少?换一种强调色?”)。修改 = 编辑 JSON,再次渲染(第 6 和第 7 步)。下一步:。
/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.
studio-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 个字符应能独立成立(应用会在“more”之后截断内容)。然后写 1 至 2 行短文,以及一个来自 voice.md 的引导请求。
- 搜索词:观众会输入来查找该视频的 2 至 3 个词,以自然的方式写入标题或第一行。
- Hashtags:在末尾添加 3 至 5 个与主题相关的标签。不要使用 #fyp 或 #viral。
- 不要加入视频中未提及的内容:不要添加新数字、声明或承诺。
如果已安装该 skill,则对文案运行 的 humanize 流程(其 )。
studio-scriptreferences/humanize.mdtext
标题:你的 hook 太长了
配文:没人能听清 hook 的第 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
有些创作者更习惯使用自己的剪辑软件,而且 Remotion 对人数超过 3 人的公司需要付费许可证。完成第 1 至第 4 步,编写包含 的 (、),跳过第 5 至第 7 步,并提供 (第 8 步)以及:
rangespublic/<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)。