关于 Reelsaw
Reelsaw 是可自托管的视频剪辑服务,可将长视频转为带字幕的竖屏短片,适合制作社交媒体视频的创作者。它结合转录、LLM 片段筛选、自动画面重构和可视化编辑,并提供 MCP、CLI 与 Webhook 等操作方式。
主要功能
- 01从链接或上传视频生成短片
- 02转录并筛选适合剪辑的片段
- 03自动重构 9:16 画面布局
- 04编辑字幕、片段范围和布局
- 05导出视频、字幕及平台元数据
- 06提供 CLI、Webhook 和分享链接
适用场景
- 01将长视频制作成竖屏社交媒体短片
- 02调整片段构图并编辑字幕
- 03批量导出短片及发布所需元数据
- 04在片段完成时通过 Webhook 触发后续流程
MCP 相关
提供 MCP 工具以管理项目、片段、转录、分享链接和 Webhook 等功能。MCP 与 Web 应用及 CLI 共用相关操作能力。
配置
本地服务的配置在各客户端中通用,复制后粘贴到客户端的 MCP 配置文件即可。
docker run -i --rm ghcr.io/osbytes/reelsaw:0.31.0本地服务需要在你的电脑上安装运行环境(如 Node.js 18+)。各客户端的配置文件位置不同,请参考客户端文档。
ghcr.io/osbytes/reelsaw:0.31.0stdio工具
本地服务需要在你的电脑上运行后才能读取工具,维护者暂未提供工具列表。
详细介绍
可自托管、由 AI 驱动的长视频转短视频剪辑工具。粘贴 YouTube 链接或上传视频;Reelsaw 会将其转录,向大模型询问最适合剪辑的片段,然后渲染成竖屏 9:16 短片,并配上逐词高亮字幕和开场钩子。托管服务 app.reelsaw.com 运行相同的代码(免费使用,之后每月收费 $12 / $29 / $59,按源视频分钟数计量)。
纯 TypeScript。通过 AI SDK 支持任意 AI 厂商。使用 Docker Compose 一步部署。
一个视频经过五个队列:导入 → 转录 → 优化 ×3 → 选取片段 → 渲染。工作进程在渲染过程中重启后,会从上次保存的位置恢复。高画质 MP4,或在 reelsaw.com 上播放。
单个短片中的自动重新取景:分割 → 人脸 → 堆叠 → 适配
功能
流水线
-
任何 AI 厂商 – 语言模型、转录模型和字幕对齐模型分别使用
provider:model字符串:OpenAI、Anthropic、Google、Groq、Deepgram、AssemblyAI、Vercel AI Gateway,或任何兼容 OpenAI 的服务(Ollama、vLLM、LM Studio、本地 Whisper 服务)。支持完全本地运行,无需 API 密钥。 -
有研究依据的片段选择 – 映射 → 池化 → 排序:转录文本会在较长停顿处分块,重叠率为 20%(长视频中不会出现“中间丢失”);每个分块中的独立片段会并行收集,候选片段随后会对齐并去重,然后按评分标准(吸引力/价值/流畅度/分享性,以及聚焦主题后的相关性)评分,取 k 个样本的中位数。排名前 10 的片段会进行 K 轮滑动式两两对决(PRP-Sliding-K;
ANALYZE_PAIRWISE_PASSES,默认值为 3,因此最先看到的前 3 个片段能得到可靠排序),并交换位置以抵消评审偏差。成本:最多 Σ(n−1−p) 对 × 2 次调用,即 3 轮时排名前 10 的片段需要 48 次调用(1 轮时为 18 次);同一对片段若评审两次,则只会实际评审一次。无需指定目标数量:自动模式会根据本次运行的结果筛除质量较弱的候选片段,而不是固定数量,因为不同评审模型的逐项评分无法直接比较:低于汇总评分中位数减去ANALYZE_SCORE_MAD_K= 1 × MAD 与ANALYZE_MIN_SCORE_FLOOR= 25 两者中的较大值的片段会被剔除,并且始终保留最佳片段(设置ANALYZE_SCORE_RULE=absolute可恢复旧的固定 45 分规则)。阈值及其中位数/MAD 会记录在项目的candidates产物中。评分失败的候选片段不会按评分标准总分进行比较;只有当通过评分的片段不足 3 个时,它们才会补入。可选设置“最多 N 个”的上限;“查找关于……的片段”只保留相关片段。点击片段的分数可查看评分明细。ANALYZE_JUDGE_MODEL可让其他模型进行评分和比较(相比评审自己的吸引力开场,自我偏好更低)。ANALYZE_CONCURRENCY、ANALYZE_SCORE_SAMPLES、ANALYZE_PAIRWISE可调整成本(自动模式:可自托管评审模型一次进行 1 次调用、使用 1 个样本且不进行两两比较;云端则为 4 / 3 / 开启)。 -
清理转录文本 – 根据词汇提示和受约束的大模型校正来修正名称,并通过跳切删去填充词、重复内容和说到一半的内容。每处剪切都会通过强制对齐定位,并通过试听剪切点来检查,因此卡拉 OK 高亮会准确落在音节上。每处修正都会以内联形式显示在转录文本中,并提供接受/拒绝选项;较长的停顿也可以缩短(详情见下文)。
-
智能 9:16 画面重构 – UltraFace(1.2 MB ONNX,通过
onnxruntime-node在 CPU 上运行,约 7 ms/帧)以每秒 4 帧的频率采样每个片段,并为每个片段选择一种布局(片段会在场景切换和人脸出现状态变化处拆分,因此画中画开场可以采用“适应”布局,其后的讲话者镜头则采用“人脸”布局;每个片段都会检测镜头切换,采用多机位剪辑、每隔一两秒在特写和广角镜头间切换的视频,也会在每次切换时更改布局):人脸(裁剪画面跟随讲话者,并采用“重型三脚架”式平滑处理:死区、平移速度上限、场景切换时立即跳转)、分屏(两位讲话者上下排列)、堆叠(幻灯片/屏幕在上,摄像头画面在下——即 Zoom、Teams、Meet 或 OBS 录制中的画中画框,而不是裁出一个人脸大小的画面并侵入幻灯片区域),或 适应(没有人脸时,例如图形和辅助素材,或三人及以上同框时,在模糊背景上显示完整画面)。广角镜头中有两个人时,裁剪画面会跟随正在讲话的人(嘴部动作与音频包络相关,每个镜头至少 2.5 秒);如果检测置信度不足,则退回分屏布局。可针对每个项目、片段或章节设置覆盖选项,包括手动设置裁剪。每种布局、触发条件、注意事项和测试素材的参考文档:docs/LAYOUTS.md。 -
字幕 – 为每个片段由 AI 挑选关键词并进行着色和放大(可在编辑器中编辑);支持可读性限制(约 2 行短句、按逗号/句子断开,每行显示时长足以阅读:至少 0.7 秒、每个单词 0.3 秒、每秒 20 个字符),ASS 卡拉 OK 单词高亮(任意颜色)、方框或描边;可调整字号、屏幕上显示的单词数(1 = 一次显示一个单词)和大小写;吸引力开场叠加层可单独设置时长。字幕和吸引力开场的位置可通过在实时 9:16 预览中拖动来任意设置 x/y 坐标;预览采用渲染器的裁剪算法和字体度量,并叠加平台安全区域。对于时长达到 2.5 秒或更长的分屏和堆叠片段,字幕会显示在两个画面之间的分界线上,而非下方讲话者的画面上(可通过开关关闭)。支持项目级默认设置和片段级覆盖设置。使用 ffmpeg + libass 烧录。 Editing
-
工作区和片段编辑器 — 左侧是源视频播放器和可点击的转录文本;右侧会随着片段渲染完成而逐个显示片段,并提供评分、布局徽标、帖子文案与话题标签的复制和下载功能。编辑器会实时预览带有片段构图和字幕的源视频,并提供多轨时间线(刻度尺、转录文本词语、片段范围、布局轨道、图层),支持缩放、吸附到词语/场景切换点/播放头、微调按钮、手动输入时间戳,以及用于逐帧精确修剪的 I/O 键。可将布局轨道拆分为多个区段(⌘/Ctrl+B、Split 按钮、双击或右键菜单),并将区段设为 Auto,或强制采用 face/split/stack/fit/centre 布局;拖动画面可重新居中区段;右键可选择 Set crop…(在源视频上框选,或在
update_clip中使用crop/crop2/contentCrop);"Use for all clips…" 会将一个片段的构图应用到整个项目(apply_framing)。可添加、拖动和删除转场;⌘/Ctrl+Z / ⇧⌘Z 可撤销和重做所有编辑。 -
删除操作可撤销 — 删除项目或片段(卡片上的 ⋯、选择栏、编辑器、⌘K)会停止其任务并将其隐藏。提示消息始终提供 Undo、Delete forever 和 See deletions。所有可恢复内容都在 Deleted(账户菜单)中,以可选择列表的形式显示:每行注明删除时间和永久删除时间;紧凑工具栏可恢复所选内容,或将其永久删除。永久删除前仍会计入存储用量(MCP
list_deleted以及purge_project/purge_clip/purge_asset/purge_layer_template;reelsaw purge-project、purge-clip、library purge、templates purge)。分钟数、每小时创建次数和首次免费视频额度均不予退还。重新挑选片段时,会保留你创建或编辑过的所有片段(文本、边界、构图、样式、片头/片尾),只替换未修改的 AI 选段;被替换的内容也可以恢复。MCP(delete_clip、delete_clips、restore_project、restore_clip、list_deleted)和 CLI(reelsaw deleted、restore、delete-clip、delete-clips、restore-clip、purge、purge-project、purge-clip)同样支持此功能。 -
从你的决策中学习 — 系统会将你接受、拒绝、剪切、纠正和编辑的内容记录在决策日志中,其中包括相关词语或片段文本、提出建议的模型,以及源视频的指纹;即使项目已删除,日志也会保留。你拒绝的剪切内容会作为应保留的示例提供给填充检测器;导出的决策可转换为评估用例(
reelsaw decisions export、export_decisions、decisions套件)。在 Settings → Learning 中可关闭此功能或清除所有记录;删除项目(或选择 "Delete forever")后,可清除系统从该项目中学到的内容,并在项目被清除时执行,也可自动执行。相关数据绝不会离开你的实例。参见 docs/specs/decision-log.md。 -
命令面板和批量导出 — ⌘K 可按状态、时长和创建时间搜索项目与片段,并打开某行的操作菜单(打开、导出、继续、重试失败的片段、重新运行优化、重新渲染、下载、删除项目或片段);也可粘贴链接(每个链接对应一个项目,或打开已有项目),并提供全部重试的快捷方式。"Export all" 会将所有已渲染片段打包为 zip,并附上适用于各平台的元数据:可直接粘贴的
.txt(标题、描述、标签、说明文字)、每个片段对应的.youtube.json(YouTube Data API v3videos.insert请求体,默认为私密)和.tiktok.json(Content Posting APIpost_info,SELF_ONLY),以及manifest.json、clips.csv(含渲染后时长)和 README。描述中会附上带时间戳的链接,直达源视频中的对应时刻。 -
分享链接 — 片段的 ⋯ 菜单可复制无需登录即可下载的链接(项目和选择菜单也可用相同方式获取 zip 链接),适用于排程工具(Buffer、Metricool)、Instagram 的
video_url、Zapier/Make 文件字段或分享给他人。链接会直接返回文件(普通的 200 响应,不重定向),提供片段的最新渲染版本,默认有效期为 90 天(MEDIA_LINK_TTL_DAYS;若设置MEDIA_LINK_ALLOW_NEVER,则可选 "Never"),并可在 Settings → Share links 中单独撤销、按片段或项目撤销,或一次性全部撤销。MCPget_media_links、list_media_links、revoke_media_link;reelsaw media-links。参见 docs/specs/media-links.md。 -
Webhooks — Settings → Webhooks 会在
project.created、project.done(含每个片段的分享链接和 zip)、project.failed、clip.done和clip.failed事件发生时,向你的 URL 发送已签名的 JSON(Standard Webhooks:webhook-id、webhook-timestamp、webhook-signature),让 n8n、Zapier、Make 或脚本可以在片段就绪时立即执行操作,而不必轮询。投递与事件一同提交;失败时会在约 22 小时内重试 8 次。若端点连续 3 天失败,系统会将其停用并发送邮件;每次投递都有日志,记录请求、响应和 Resend。项目的metadata(create_project)会随该项目的每个 webhook 一同返回。MCPcreate_webhook/delete_webhook(Zapier 或 Make 应用的订阅方式)、list_webhooks、update_webhook、rotate_webhook_secret、test_webhook、list_webhook_deliveries、resend_webhook_delivery;reelsaw webhooks。参见 docs/specs/webhooks.md。 -
SRT / VTT 导入与导出 — 每个已渲染片段的 ⋯ 菜单都可将字幕导出为
.srt或.vtt(行数和时间与视频一致,包含片头,即使样式设置为不显示字幕也一样);"Export all" 会将字幕文件放在对应片段旁边,文件名为clips/NN-title.srt和.vtt。此功能推出前渲染的片段需要先重新渲染。项目的 ⋯ 菜单("Transcript files…")可用相同方式下载转录文本(VTT 可为每个词添加时间戳),也可导入已有字幕文件:可在创建项目后立即导入(此时只会获取视频),也可在项目完成或失败后导入(系统会整理转录文本并重新挑选片段)。导入的字幕条目会转换为分段,并按词语长度将词语分配到字幕条目的时间范围内;渲染时会根据音频重新对齐词语。另提供GET /api/clips/:id/captions.srt|vtt、GET /api/projects/:id/transcript.srt|vtt(VTT 可通过?words=1获取词语时间)、MCP 工具get_clip_captions、get_project_transcript_file和import_transcript,以及reelsaw captions、transcript-file和import-transcript。 素材库、图层和模板 -
素材库——片头和片尾、Logo、贴纸、B-roll、音乐和音效(PNG、JPG、GIF、WebP、SVG、视频、音频)。可一次拖入多个文件(会检测重复文件),搜索你的素材和内置目录;使用管理员密钥时,还可搜索 Pexels、Pixabay、Freesound、Iconify、Giphy 和 Klipy。每个片段都会在一次编码中渲染为「片头 + 片段 + 片尾」,并置于模糊背景上以适配竖屏。
-
项目和片段设置——项目可沿用默认片头和片尾、不使用任何素材,或指定素材;每个片段可沿用项目设置、不使用素材,或自行选择素材。无法使用的素材会被跳过,并显示易懂的事件说明,而不会导致片段失败。
-
图层——一个时间线轨道用于放置图片/视频叠加层、文本和音频(
update_clip { layers }、reelsaw clip-layers):可设置位置、大小、不透明度;使用镜像文件中附带的字体;添加多条音乐轨道并设置淡入淡出、循环播放和语音下的侧链闪避。在 9:16 预览中右键单击图层,即可编辑、居中、调整顺序、复制或移除;双击文本即可重新输入。使用 ⌘C / ⌘V 复制和粘贴图层。 -
图层模板——将一种样式保存一次(例如,开头几秒弹出的 Logo、结尾的关注卡片、水印或音乐垫底,以及片头和片尾),并设置从开头、结尾、整个片段或按片段长度比例计算的时间,然后应用于选定片段,或将其设为项目模板,让每个新片段都自动应用(
list_layer_templates、apply_layer_template、reelsaw templates)。
Agent 和自动化
- MCP 服务——
/api/mcp(Streamable HTTP)允许 Claude Code、Claude Desktop、Cursor 或脚本创建项目、等待项目完成、读取带时间戳的转录文本、编辑图层和画面构图、创建并重新渲染片段、管理素材库和图层模板,以及导出结果。托管方案(包括 Free)也提供相同的工具。可使用 Settings 中的 API 密钥进行身份验证,也可设置MCP_OAUTH=true,让 claude.ai 和 Claude Desktop 连接器通过浏览器登录。 - 运维 CLI——
docker compose exec app reelsaw …可检查并修复正在运行的实例:status、events、clip、rerender、stuck、recover、logs、cuts、align、hear、flags、storage、retention、deleted、restore、purge、users、approve、make-admin、set-password、library、templates、apply-framing、import-report等(所有命令均列于 运维 CLI)。
托管和账号
- 账号、审批和配额——首个账号为管理员;
SIGNUP_MODE=open|approval|closed决定谁可以注册。管理员可在 Users 页面审批、暂停、提升权限和移除用户,并查看每个账号的用量(项目数、源视频分钟数、片段数、模型调用次数和令牌数、已存储字节数)。还可设置可选的用户配额、旧文件保留期限、登录验证码、电子邮件功能(审批通知、密码重置、验证)以及条款/隐私链接。每个用户都有自己的 Project defaults(词汇、指令、语言、片段时长、停顿、字幕样式、片头和片尾)和 API 密钥。 - 运行时的模型和提供商——管理员可在 Settings → System → Models 下切换语言、转录和对齐模型,无需重启:选择提供商,在此处输入密钥或服务器 URL(加密存储,之后不会再次显示),并从实时模型列表中选择;本地 Ollama 和 Speaches 模型会显示进度条进行下载。保存时会测试密钥和模型。
运维
- 可靠、可恢复、可扩展 —— 每个阶段都是 Postgres 中的一个 pg-boss 任务,会检查点保存输出,因此崩溃或重启的工作进程可以从中断处继续;停止发送心跳(每 15 s 一次)的工作进程,其任务会在大约一两分钟内由 pg-boss 重新入队,协调器也会重新入队任何孤立任务。长阶段会在各部分完成时保存结果(每个 3-minute 的 Finding moments 窗口),因此重试或慢速本地模型超时后,都能从中断处继续,而不是从头开始;超时信息会说明进度(
LLM_CALL_TIMEOUT_SECONDS、STAGE_IDLE_TIMEOUT_MINUTES)。增加worker容器即可扩展。每次运行(首次处理、重试、重新渲染、重新分析)都会记录各自的耗时,UI 会根据已完成项目中积累的数据,显示每个阶段的已耗时间与预计时间。浏览器通过 tRPC SSE 订阅获取实时更新,并自动重新连接。 - 存储与安装 —— 本地卷或
s3(AWS S3、MinIO、Cloudflare R2、Backblaze B2……);每小时清理已被替代的渲染结果、过期源文件和孤立文件(reelsaw storage可查看报告,reelsaw storage sweep可执行清理)。提供适用于 macOS/Linux(bash)和 Windows(PowerShell)的一行式安装程序;报告问题会保存项目数据、决策和日志的 zip 文件,并排除密钥。 - 可观测且快速失败 —— 每项服务都会在
main中构建并进行自检,因此如果模型 id 错误、密钥缺失、二进制文件不存在、存储桶无法访问或数据库不可用,进程会以易读的原因退出。LLM_TRACE=logs或langfuse会记录每次提示词/响应(令牌、延迟、项目/片段/阶段);OTEL_ENABLED=true则会为 HTTP、Postgres、pg-boss 任务、AI SDK 调用、ffmpeg/yt-dlp 和各阶段启用 OTLP 跟踪与指标,包括每秒视频的阶段、下载和转录耗时,以及各代理的下载健康状况。
贡献
请参阅 CONTRIBUTING.md(原则、测试、签署)。关于如何从用户决策中学习的路线图:docs/SELF-IMPROVEMENT.md。
请先阅读 docs/PRINCIPLES.md:每次变更都必须遵循其中的统一性、并发和 UI 规则。叠加层堆叠(Dialog 内的 Select、z-index、portal)和移动端视口(使用 h-dvh,而不是 h-screen)详见 docs/UI.md。Agent 指令位于 AGENTS.md(唯一的 Agent 文件;另请参阅流程原则)。
安装(无需源码)
需要 Docker(macOS 和 Windows 上使用 Docker Desktop)。只需运行一条命令:它会检查机器的情况(内存、GPU、Docker 的内存、可用磁盘空间、已运行的 Ollama 及其模型、端口),提出配置方案,在终端中询问三个问题(使用本地 Ollama 还是托管服务商、选择哪个模型、是否继续),如果你选择本地方式且尚未安装 Ollama,还会为你安装 Ollama(通过 Homebrew、ollama.com 或 winget):
curl -fsSL https://raw.githubusercontent.com/osbytes/reelsaw/main/deploy/install.sh | bash
在 Windows(Docker Desktop)上,通过 PowerShell 运行:
irm https://raw.githubusercontent.com/osbytes/reelsaw/main/deploy/install.ps1 | iex
WSL2 用户可以直接运行 bash 单行命令(需开启 Docker Desktop 的 WSL 集成)。
它会写入 ~/reelsaw(包含 compose 文件和带有生成密钥的 .env),拉取已发布的镜像和模型,启动所有服务,并等待 http://localhost:3000 响应(或改用下一个可用端口)。如果机器能够运行实用的模型,默认会完全在本地运行:内存为 24 GB 或配备 12 GB GPU 时使用 qwen2.5:14b(约 9 GB),内存为 12 GB 时使用 qwen2.5:7b,更低配置则使用 gemma3:4b;如果 Ollama 中已有合适的模型,则会复用,而不会重新下载;配备 NVIDIA GPU 和容器工具包时,Ollama 和 Speaches 会使用 GPU(渲染时也会通过 NVENC 编码;参见下文“使用 GPU”)。托管服务商(OpenAI、Anthropic、Google、Groq、Vercel AI Gateway)也可以在同一提示中选择:程序会询问密钥且不会回显;Anthropic 和 Google 会搭配本地 Speaches 进行语音转文本。没有终端时,或设置了 REELSAW_YES=1 时,将采用建议的选项;也可以提前设置 REELSAW_AI、REELSAW_LLM、REELSAW_PORT 等变量来回答,设置 REELSAW_DRY_RUN=1 则会打印计划并退出(所有选项都列在脚本顶部)。
手动配置:deploy/README.md。
安装脚本为 deploy/install.sh 和 deploy/install.ps1;它们从 main 分支提供(参见 docs/RELEASING.md)。
快速开始(从源码开始)
git clone <this repo> reelsaw && cd reelsaw
cp .env.example .env
# edit .env: set BETTER_AUTH_SECRET, APP_URL, and at least one AI provider key
docker compose up -d --build
打开 http://localhost:3000,创建账户(该账户将成为管理员),然后启动一个项目。如果实例公开,之后请将 SIGNUP_MODE=closed 或 approval。
扩展工作进程:
docker compose --profile workers up -d --scale worker=2
每个工作进程都会为其处理的源文件和渲染内容保留独立的临时副本。使用 S3 存储时,这些副本会作为缓存:空闲的已完成项目副本会在 SCRATCH_CACHE_HOURS(24)后清除;磁盘空间不足时,会先清除最近最少使用的副本;如果工作进程仍无法腾出空间,就会把任务交给已有该副本的工作进程,并暂停接收媒体任务,直到磁盘空间恢复(在“设置”→“系统”→“工作进程”中会显示“磁盘已满”)。任务会在写入第一个字节前,针对其卷上的活动租约登记即将写入的临时数据,因此同一台机器上的两个大型导入任务不会同时通过可用空间检查,随后又在下载中途都耗尽空间:第二个任务会等待第一个完成。共享同一卷的进程(同一主机上的应用和工作进程副本)共用一份账本;另一台主机上的工作进程不会争用。
使用 MinIO 在本地测试 S3:
docker compose --profile minio up -d
# then in .env: STORAGE_DRIVER=s3 S3_ENDPOINT=http://minio:9000 S3_FORCE_PATH_STYLE=true S3_BUCKET=reelsaw S3_ACCESS_KEY_ID=reelsaw S3_SECRET_ACCESS_KEY=reelsaw123
(通过 http://localhost:9001 的 MinIO 控制台创建一次存储桶即可。)
模型配置
语言模型和转录模型相互独立,可以自由混用不同服务商(Anthropic 不提供语音转文本,因此需搭配 Deepgram/OpenAI/AssemblyAI/Groq)。每个服务商只需提供自己的密钥。
| 环境变量 | 示例值 |
|---|---|
AI_MODEL | openai:gpt-4o-mini、anthropic:claude-sonnet-4-5、google:gemini-2.5-flash、groq:llama-3.3-70b-versatile、gateway:anthropic/claude-sonnet-4-5、compat:qwen2.5:14b |
TRANSCRIPTION_MODEL | openai:whisper-1、deepgram:nova-3、assemblyai:best、groq:whisper-large-v3-turbo |
转录模型必须返回词级时间戳(字幕和剪辑都需要),因此不支持不会返回时间戳的 openai:gpt-4o-transcribe。
完全本地运行(无需 API 密钥)
Ollama 只提供语言模型,不具备语音转文本功能。因此,本地技术栈使用 Ollama 提供 AI_MODEL,并使用 Speaches(通过兼容 OpenAI 的 API 提供 faster-whisper,支持词级时间戳)提供 TRANSCRIPTION_MODEL。两者都通过 compose 配置文件提供:
docker compose --profile local-ai up -d # Speaches; use your native Ollama (macOS: Metal GPU)
docker compose --profile local-ai --profile ollama up -d # Linux: also run Ollama in a container
ollama pull qwen2.5:14b # or: docker compose exec ollama ollama pull qwen2.5:14b
.env:
AI_MODEL=compat:qwen2.5:14b # qwen2.5:7b or gemma3:4b on less RAM
OPENAI_COMPATIBLE_BASE_URL=http://host.docker.internal:11434/v1 # native Ollama; http://ollama:11434/v1 for the container
OPENAI_COMPATIBLE_API_KEY=ollama
TRANSCRIPTION_MODEL=openai:Systran/faster-whisper-small
OPENAI_BASE_URL=http://speaches:8000/v1
OPENAI_API_KEY=local
注意:应用会向兼容服务器请求 response_format: json_schema(设置 OPENAI_COMPATIBLE_STRUCTURED_OUTPUTS=false 可禁用),如果模型忽略该请求,则会回退到宽松解析;但小型模型仍可能选出效果不佳的片段,并写出平淡无奇的钩子。gemma3 4b 可以使用;qwen2.5 14b(安装程序在 24 GB 或更大内存配置下的默认模型)、llama3.1 8b 或 gemma3 12b 的效果明显更好。首次启动时,应用会自动将 whisper 模型下载到 Speaches,并验证 Ollama 模型是否已拉取(否则会退出并提示执行 ollama pull <model>)。在 CPU 上使用 Speaches 和 faster-whisper-small,运行速度大约与实时相当;在 GPU 上使用 latest-cuda 镜像和 faster-whisper-large-v3。由于 Speaches 使用 OpenAI 音频 API,因此会复用 OPENAI_* 变量,这也是 LLM 通过 compat 提供方运行的原因。
使用 GPU
有三个组件可以使用 GPU,各自在独立容器中运行,因此 GPU 配置通过 Compose 覆盖文件设置,而不是使用不同的镜像:docker-compose.gpu.yml 会将所有 NVIDIA 设备分配给 speaches、ollama、app 和 worker。主机端需要安装 NVIDIA 驱动(nvidia-smi 可正常运行)和
NVIDIA Container Toolkit
(sudo nvidia-ctk runtime configure --runtime=docker && sudo systemctl restart docker;Windows 上仅支持使用 Windows 驱动的 WSL2 版 Docker Desktop)。然后在 .env 中设置:
# Linux/macOS (colon). On Windows use a semicolon: docker-compose.yml;docker-compose.gpu.yml
COMPOSE_FILE=docker-compose.yml:docker-compose.gpu.yml
SPEACHES_IMAGE=ghcr.io/speaches-ai/speaches:latest-cuda
WHISPER_MODEL=Systran/faster-whisper-large-v3 # or deepdml/faster-whisper-large-v3-turbo-ct2
TRANSCRIPTION_MODEL=openai:Systran/faster-whisper-large-v3
并像之前一样运行 docker compose --profile local-ai up -d。具体变化如下:
- 转录是最明显的变化:在 CUDA 上运行的 faster-whisper 处理
large-v3的时间远短于实时,而 CPU 上的small大约需要实时长度的时间(见下表中的转录系数)。 - 语言模型会在 Ollama 运行于
ollama容器中时使用显卡(--profile ollama)。原生运行的 Ollama(macOS 或 Linux 主机)本身就会使用 GPU。 - 渲染会使用
h264_nvenc编码,而不是 libx264:FFMPEG_ENCODER=auto会在启动时检测并使用它;如果没有可用显卡,则继续使用 libx264。滤镜(裁剪、模糊、字幕)仍在 CPU 上运行,而且渲染本来就是耗时较短的阶段,因此预计耗时是几秒,而不是几分钟。如果硬件编码在片段中途失败(NVENC 会话数限制、驱动重置),该片段会改用 libx264 重新编码。 - 设备端模型(人脸、文本、对齐)默认仍在 CPU 上运行。设置
ONNX_EXECUTION_PROVIDER=cuda可将它们移至 GPU,但已发布的镜像不包含 onnxruntime 所需的 CUDA 库;而在 Apple 芯片上,实测 CoreML 对这三种模型的速度都不如 CPU;详见docs/CONFIG.md。
设置 → 系统 → 概览和 docker compose exec app reelsaw machine 会显示应用进程可用的 GPU、正在使用的编码器及其原因,以及每个设备端模型的运行位置。工作进程(以及 reelsaw workers)会列出每个 API 和工作进程副本的规格及其处理内容;当执行任务的进程不是 HTTP 进程时,此信息必不可少。macOS 上的 Docker 不支持 CUDA:原生运行 Ollama 以使用 Metal,并让 Speaches 保持在 CPU 上运行(或者将 OPENAI_BASE_URL 指向运行在主机上的 whisper.cpp 或 MLX 服务,这些服务使用相同的 API)。
预计处理时间
每个阶段的处理时间都会随视频长度而变化。阶段预计耗时(秒)≈ 视频秒数 × 系数,系数取决于模型(本地模型或供应商模型)和你的机器:
| 阶段 | 系数取决于 | 本地 CPU(实测¹) | 供应商(实测²) |
|---|---|---|---|
| 下载 | 你的带宽和网站 | 0.015 | 0.015 |
| 转录 | CPU/GPU 上的 Whisper 模型大小,或 STT 供应商 | 0.25 | 0.065 |
| 润色(填充词、人名) | LLM 延迟 × 候选片段数 | 0.5 | 0.03 |
| 查找精彩片段 | LLM 延迟 ×(视频分钟数 ÷ 5 + 1)次调用 | 0.2 | 0.02–0.07 |
| 渲染 | 每个片段的 ffmpeg + 人脸追踪 ÷ 渲染并发数 | 0.04(3 个片段) | 0.05(3 个片段) |
经验法则:供应商模型栈处理完一个视频的时间约为视频时长的五分之一;本地 CPU 模型栈则大约需要与视频时长相同的时间。 润色与查找精彩片段同时进行,因此各阶段耗时相加并不完全准确。查找精彩片段会保存已完成的部分,因此,对于长视频和运行缓慢的模型,重试或超时后会从上次完成的部分继续,而不是从头开始;项目会显示“已完成 N/M 个部分,预计还需 X”。
¹ Apple 芯片笔记本电脑,Ollama 上的 qwen2.5:14b + Speaches 上的 faster-whisper-small(CPU),20 分钟播客:下载 15 秒,转录 4.9 分钟,润色 10 分钟,查找精彩片段 4 分钟,渲染 47 秒;总计 20 分钟。本地 14B 模型的瓶颈是语流不畅验证器(每个候选片段调用一次,每次约 40 秒),而非转录;使用较小的模型或设置 DISFLUENCY_LLM=false 可大幅缩短润色时间。² 同一视频通过 Vercel AI Gateway,使用 whisper-1 + claude-haiku-4.5:下载 13 秒,转录 77 秒,润色 31 秒,查找精彩片段 24 秒,渲染 56 秒;总计 3.4 分钟。Gemini 2.5 Flash 耗时 5 分钟(“思考”输出较长),Sonnet 4.5 约 4 分钟。将供应商 LLM 与本地 TRANSCRIPT_ALIGN_MODEL 混用时,渲染会成为最慢的阶段(5–9 分钟:每个剪辑都要在 CPU 上重新对齐);请让对齐模型与转录模型使用同一家供应商。完整数据见 eval/bench/results/。
这些系数只是起点。应用会从已完成的项目中学习:每个阶段的预计时间是你近期使用相同模型运行的项目耗时中位数。运行期间,该数据会显示在项目页面的已用时间旁;阶段完成后会显示在活动动态中(Transcribe finished in 4:50 (expected ~4:00));此外,Settings → System → Machine 下方还会显示各阶段预计耗时与实际耗时的对照表。无需校准步骤;运行过程本身就是校准。
无需重启即可切换模型
.env 用于指定默认模型和密钥;管理员可随时在 Settings →
System → Models 下更改两者。三个选择器(语言、转录、对齐)都会先显示供应商:选择供应商后,如果尚未配置密钥或服务器 URL,可直接在此处填写;随后便会显示该供应商的模型。云端供应商会实时查询可用模型;可自托管的 Ollama 或 Speaches 会显示已安装的模型,并通过进度条下载其他模型;搜索框则会同时筛选所有供应商。保存密钥前会先测试(该供应商的模型会随之列出),错误的密钥会被拒绝;保存模型时也会进行检查(密钥已配置、服务器可访问、模型已安装,且转录会返回单词时间戳)。密钥会使用从 BETTER_AUTH_SECRET 派生的密钥加密后存储在数据库中,之后不再显示(界面只会显示末四位);更换该密钥会导致已存储的密钥无法读取,界面会要求重新输入。更改会在重启后保留,并在几秒内同步至工作进程;已在运行的阶段会继续使用启动时的模型。“Back to .env” / “Remove” 会移除覆盖设置。通过 API 操作:admin.models.*、admin.providers.*。
账户和角色
实例中创建的第一个账户是 管理员;其他人都是用户。管理员可以查看系统设置(模型、存储、资源用量)和 用户页面;用户只能查看自己的项目和设置。SIGNUP_MODE 决定哪些人可以使用:
SIGNUP_MODE | 行为 |
|---|---|
open(默认) | 任何人都可以注册并立即使用应用。适合单用户设备,以及已验证电子邮件的公开实例。 |
approval | 新账户会停留在“等待批准”页面,直到管理员批准(通过用户页面或运行 reelsaw approve <email>)。设置了 EMAIL_DRIVER 后,管理员会收到电子邮件通知。适合私人设备,不适合公开产品。 |
closed | 不允许注册。(旧设置 ALLOW_SIGNUP=false 仍会被视为 closed;请使用 SIGNUP_MODE。) |
每位用户都有 设置 → 项目默认值(词汇、说明、语言、片段长度、暂停、字幕样式……),这些设置会应用于他们或其 Agent 启动的每个新项目;新建项目表单会以这些设置为初始值,也可以将当前值保存为默认值。管理员还可以查看 系统部分(模型、存储、资源用量),并且只有管理员(或设置了 HEALTH_DETAILS=true)才能在 /api/health 中看到机器数据。
在用户页面停用账户会结束其会话,并在其下次请求时拒绝其 API 密钥;删除账户会删除其项目、片段和文件。如果忘记密码,可以在启用电子邮件时通过登录页面的“忘记密码”找回。未启用电子邮件时,操作员可以在 Shell 中设置新密码(docker compose exec app reelsaw set-password [email protected]);--must-change 会要求用户在下次登录时自行设置密码,而无需输入临时密码(会话证明他们已通过验证)。要在 Shell 中将账户提升为管理员,请运行:docker compose exec app reelsaw make-admin [email protected]。
启用电子邮件(EMAIL_DRIVER=smtp|resend、EMAIL_FROM、SMTP_URL 或 RESEND_API_KEY)后,可发送批准通知、密码重置邮件;设置 REQUIRE_EMAIL_VERIFICATION=true 后,还会要求用户在首次登录前完成验证。确认和重置链接有效期为 24 小时,且只能使用一次;链接过期后会跳转到一个可发送新链接的页面。启用电子邮件后,登录页面会显示 忘记密码?;设置新密码会使其他设备退出登录。未启用电子邮件时,其他功能仍可使用;账户审批通过用户页面处理。
面向多人使用的托管
以下功能默认全部关闭,通过环境变量启用(deploy/.env.example 中列出了这些变量),因此单用户设备仍保持原样:
- 配额:
MAX_PROJECTS_PER_USER、MAX_SOURCE_MINUTES_PER_DAY、MAX_SOURCE_MINUTES_PER_MONTH、MAX_NEW_PROJECTS_PER_HOUR、MAX_STORAGE_MB、MAX_LIVE_JOBS_PER_USER(由单个账户自己的请求排队或运行的任务)、MAX_RERENDERS_PER_HOUR(每小时重新渲染、手动制作片段,以及重新分析、优化等重跑次数)、MAX_REQUESTS_PER_MINUTE、MAX_REQUESTS_PER_HOUR和MAX_WEBHOOKS_PER_USER(0 = 不限), 并可在 Users 页面为每位用户设置覆盖值。存储用量包括来源文件、上传文件(已复制到项目中的上传文件只计一次)、渲染片段和片头/片尾,并会在文件进入系统的每种途径上进行检查。拒绝时会用一句话说明该怎么做(“已达到每月 300 分钟的限额(已使用 312 分钟);将于 11 月 1 日重置”)。有套餐的账户按其计费周期计算每月窗口,否则按 UTC 日历月计算。对于设有限分钟数的账户,如果网站报告了链接内容的时长,则在创建项目时就会拒绝该链接(并提供升级套餐选项),因此不会为其创建项目。否则会在下载前再次检查时长,并在探测文件后再检查一次;超长视频会以相应原因使项目失败,且不会重复获取。对于含免费额度的套餐,首次视频指时长已知的第一个视频。 - 水印:
WATERMARK_NON_ADMINS(默认开启)会将徽标(WATERMARK_LOGO_PATH)和下方的WATERMARK_TEXT(“reelsaw.com”)烧录到账户非管理员的片段中;付费套餐会移除水印,管理员也可在 Users 页面(Limits)按用户切换。渲染时才会确定是否添加水印,因此更改后重新渲染会应用新设置。已渲染的片段会保留渲染时的设置。 - 用量(Users → Usage):项目数、来源文件分钟数、已渲染片段数、模型调用次数和令牌数(每次模型调用都会写入一条归属项目所有者的
llm_usage记录),以及已存储字节数。 - 保留期限:
SOURCE_RETENTION_DAYS/CLIP_RETENTION_DAYS会删除已完成项目中的存储文件(存储对象和本地副本);记录和历史记录会保留。片段会从来源文件重新渲染;来源文件移除后,“重新获取”(项目中的 Retry)会先重新下载来源文件。reelsaw retention可手动执行清理。使用 S3 存储时,SCRATCH_CACHE_HOURS(24)表示项目完成且处于空闲状态后,工作进程保留存储文件本地副本的时长;存储桶中的副本会保留,并在任务需要时重新取回。 - 历史记录清理:
EVENT_RETENTION_DAYS(90)、LLM_USAGE_RETENTION_DAYS(180)和ADMIN_EVENT_RETENTION_DAYS(365)会每天分批删除旧记录(0 表示全部保留);项目的运行骨架记录(已创建、运行已开始、已完成)始终保留。 - 机器人防护:设置
CAPTCHA_PROVIDER(Cloudflare Turnstile、reCAPTCHA、hCaptcha)以及站点密钥和密钥后,注册、登录和密码重置页面会显示验证组件。 - 免费套餐滥用防护(除非设置,否则均关闭;
apps/server/src/abuse.ts):MAX_SIGNUPS_PER_IP_PER_DAY限制每个网络地址可创建的账户数(位于代理后时,TRUST_PROXY会读取CF-Connecting-IP、X-Real-IP、X-Forwarded-For);BLOCK_DISPOSABLE_EMAILS会拒绝临时邮箱域名;每个真实邮箱只能注册一个账户([email protected]会视为[email protected]);MAX_REQUESTS_PER_MINUTE/_HOUR限制每个账户的变更请求数(API 修改、MCP 工具调用),并可像其他配额一样按套餐和用户设置覆盖值。系统每小时会扫描并标记共用注册地址、短时间内集中注册或使用临时域名的账户;Users 页面会显示这些标记(“Flagged only”),reelsaw abuse会列出这些账户,系统不会自动暂停任何账户。每次拒绝都会用一句话说明原因。 - 从数据中心使用 yt-dlp:YouTube 会拒绝许多云端 IP。
YTDLP_PROXY可设置一个 URL 或按运营方优先级排列的代理池(家用 Pi、廉价 VPS、付费代理)。URL 中的{session}会在每次下载时替换,以便住宅网关为一个视频保持会话,并在下次下载时轮换。可靠性通过每个候选代理上的熔断器处理,不会改变健康代理之间的排序。YTDLP_COOKIES_FILE也有帮助;下载被拦截时会提示“改为上传文件”。reelsaw ytdlp-proxies可显示代理池。 - 法律信息:
TERMS_URL/PRIVACY_URL会显示在注册表单上。 - 通过 OAuth 使用 MCP(
MCP_OAUTH=true):Reelsaw 会成为 MCP 连接器(claude.ai、Claude Desktop)的 OAuth 2.1 授权服务器,支持动态客户端注册、浏览器登录和授权确认页面,并在/.well-known/oauth-protected-resource/api/mcp提供发现信息(其中会指定颁发者<APP_URL>/api/auth,其元数据位于/.well-known/oauth-authorization-server/api/auth)。API 密钥仍可供 CLI 使用。授权确认页面会显示授权后将返回的地址;每个网络地址每天最多可注册 20 个客户端;每个人都可在 Settings → Account → Connected apps 下查看并撤销已授权的应用。 - 代理后的客户端地址(
TRUST_PROXY、TRUSTED_PROXY_HEADER、TRUSTED_PROXY_HOPS): 注册数量上限和客户端注册限额都按客户端地址计算。使用默认的x-forwarded-for时,会从右侧取TRUSTED_PROXY_HOPS指定位置的地址(位于 Caddy 后时为 1,位于 Cloudflare 和 Caddy 后时为 2),因此客户端自行前置的地址不会计入;只有选中其他标头时,才会读取这些标头。
计费(企业版)
app.reelsaw.com 上的托管服务通过 Stripe 提供套餐和计费功能。相关代码位于
apps/server/src/ee 和 apps/web/src/ee,受其各自的许可证约束(LICENSING.md),
随同一镜像一起发布,且仅在设置 REELSAW_EE=true、STRIPE_SECRET_KEY 和
STRIPE_WEBHOOK_SECRET 后运行。可自托管安装的其他部分不受影响。
- 套餐是上述限制的一组命名配置(
ee/billing/plans.ts);账户套餐的优先级介于 实例设置和管理员对单个用户的覆盖设置之间。Free 也是一种套餐:没有订阅或订阅已过期的账户适用此套餐。 - Stripe 是唯一事实来源。Webhook(
POST /api/billing/webhook)仅在事件记录和billing.sync任务 一并提交后才会确认收到;该任务会根据 Stripe 重写账户记录,因此迟到、重复或乱序到达的事件最终都会收敛一致。每日对账 和reelsaw billing sync可修复 Webhook 丢失后遗留的问题。 - Settings → Billing 会显示套餐、当期用量和套餐列表;Checkout 和
Billing Portal 则是 Stripe 的页面。MCP 也提供相同功能:
get_billing、billing_checkout_url和billing_portal_url;reelsaw billing <email>会显示账户记录、Stripe 中的信息以及最近的 Webhook 事件。 - 付款失败后,账户会在
BILLING_GRACE_DAYS(7)天内保留原套餐,之后将使用 Free 套餐。 管理员可以在不使用 Stripe 的情况下授予套餐(Users → Limits → Plan,或reelsaw billing set-plan)。 套餐到期或降级不会给已渲染的片段重新添加水印;只有新渲染的片段会添加水印。 如果账户已有有效的 Stripe 订阅,用户会被引导至 Billing Portal 进行更改, 而不会进入第二个 Checkout。 - 设置 Stripe 账户:
pnpm --filter @reelsaw/server stripe:setup --webhook --portal(会先重建@reelsaw/shared) 会根据套餐列表幂等地创建产品、价格(按查找键)、Webhook 端点和门户配置。无需 Stripe 账户即可开发:pnpm --filter @reelsaw/server stripe:mock会模拟 api.stripe.com(STRIPE_API_BASE=http://host.docker.internal:4242),test/billing-stack.test.ts则通过签名 Webhook 跑完整个生命周期。
设计和决策:docs/specs/ee.md。
MCP:通过 Agent 操作 Reelsaw
Reelsaw 是一个位于 /api/mcp 的 MCP 服务(模型上下文协议,Streamable HTTP)。在 Settings → API keys and MCP 中创建 API 密钥
(仅显示一次),并将客户端指向该密钥:
claude mcp add --transport http reelsaw https://your-host/api/mcp --header "Authorization: Bearer rs_…"
{ "mcpServers": { "reelsaw": { "type": "http", "url": "https://your-host/api/mcp", "headers": { "Authorization": "Bearer rs_…" } } } }
密钥权限。 密钥会以你的身份运行,但有一个例外:实例管理工具
(get_instance_settings、set_instance_settings、list_workers、get_queue、list_failures、
get_failure、retry_failures、check_for_update 和实例级 report_issue)需要使用创建时勾选
Allow instance administration (admin tools over MCP) 的密钥。管理员可在 Settings 中看到此复选框。
默认创建的密钥不包含此权限;缺少此权限的密钥会被拒绝,并显示“This
key cannot change instance settings”。数据库升级时,已有密钥会获授管理权限,因此仍可继续使用;该权限会显示在 Settings 中,你可以撤销权限
或更换密钥。没有权限记录的密钥绝不会被视为管理员。
通过 OAuth 登录的连接器(MCP_OAUTH=true)永远无法使用管理工具,无论
登录者是谁。
通过启动命令而非使用带请求头的 URL 连接的客户端(不启用
MCP_OAUTH 的 Claude Desktop、Cursor 的 stdio 配置)使用 reelsaw mcp:它通过 stdio 提供相同的工具,并转发至正在运行的
实例。它不需要数据库或模型,只需要 REELSAW_URL(默认值:同一容器中的应用)和 REELSAW_API_KEY:
{ "mcpServers": { "reelsaw": { "command": "docker", "args": ["exec", "-i", "-e", "REELSAW_API_KEY=rs_…", "reelsaw-app-1", "reelsaw", "mcp"] } } }
Reelsaw 已在 MCP 注册表 中列出,名称为
io.github.osbytes/reelsaw(server.json,每次发布时更新),因此支持注册表的客户端可以直接从镜像安装桥接服务,无需启动容器后再执行命令:
{ "mcpServers": { "reelsaw": { "command": "docker", "args": ["run", "-i", "--rm", "-e", "REELSAW_URL=https://your-host", "-e", "REELSAW_API_KEY=rs_…", "ghcr.io/osbytes/reelsaw", "reelsaw", "mcp"] } } }
自动化工具无需 MCP 客户端即可调用同一端点:该端点是无状态的,并返回纯 JSON,因此一次 tools/call POST 就是一次 HTTP 请求。docs.n8n 提供了一个现成的 n8n 工作流,可剪辑 YouTube 频道上的每个新上传视频。若要在片段准备就绪时收到通知,而不是等待,请添加 webhook(create_webhook,或前往 Settings → Webhooks),并使用任意 Standard Webhooks 库验证每次推送:
import { Webhook } from "standardwebhooks";
const event = new Webhook(process.env.REELSAW_WEBHOOK_SECRET).verify(rawBody, request.headers); // throws if forged or stale
if (event.type === "project.done") for (const clip of event.data.clips) console.log(clip.title, clip.shareUrl);
这些工具(由 apps/server/src/mcp-tools.ts 生成;每个工具的完整说明和输入架构都会说明其返回内容、是否在后台运行,以及应改用哪个相邻工具):
| 工具 | 功能 |
|---|---|
list_projects | 列出你的项目,按最近更新时间从新到旧排序。 |
get_project | 获取你的某个项目:状态、设置、实际生效的片头/片尾、转录稿摘要,以及每个片段(id、标题、钩子、边界、评分、状态、下载链接);replacedClips 列出已删除或已替换的片段,可用于 restore_clip。 |
create_project | 从 URL(yt-dlp 能抓取的任何内容)或你上传的文件开始剪辑视频:将文件作为请求体,通过 POST /api/upload 上传,设置 x-filename 请求头,并在 Authorization: Bearer 中提供你的 API 密钥,然后传入响应返回的 uploadId。 |
update_project | 重命名项目和/或更改已保存的设置(focus、instructions、clip length、maxClips、caption style、framing、pauses、refineEntities、refineDisfluency、disfluencyLlm、refineScenes);只会更改你传入的字段。 |
wait_for_project | 阻塞等待项目完成或失败,或直到 timeoutSeconds 秒过去。 |
get_transcript | 获取项目的转录稿,格式为带时间戳的行 "[m:ss] text",也可只获取指定时间范围。 |
get_clip_captions | 获取片段上次渲染的字幕,格式为 SRT 或 WebVTT 文件:行和时间与视频一致(包括片头),即使样式设置为关闭字幕也一样。 |
get_project_transcript_file | 获取项目转录稿的 SRT 或 WebVTT 字幕文件,每个转录片段对应一个字幕条目,时间以源视频秒数为准。 |
import_transcript | 使用 SRT 或 WebVTT 文件作为项目的转录稿,而不进行转录:可在 create_project 后立即导入(此时只会获取视频),也可在项目完成或失败后导入(之后会重新清理转录稿并挑选片段)。 |
search_clips | 根据标题、钩子或字幕中的词语查找你的片段和项目(必须匹配所有词语;不区分大小写,可容忍拼写错误;按最佳匹配排序)。 |
get_project_events | 获取项目的流水线历史记录,按时间从新到旧排序(与 Activity 面板显示的事件相同;实时进度更新不会保存):阶段开始和完成、片段渲染、重试,以及附带原因的失败。 |
get_clip | 获取某个片段的全部信息:文本、边界、评分、强调内容、图层(素材、文本、声音)、渲染诊断信息、视频/缩略图/编辑器链接,以及下一次渲染将使用的内容及其来源:style { own, effective, from: clip|project|default }、framing { own, effective: { mode, adjust, sections }, from }、bumpers { own, effective }(片头/片尾)。 |
update_clip | 编辑一个片段(3 秒至 10 分钟):只会更改你传入的字段;style、layoutSections 和 layers 会整体替换(null = 项目的设置/无)。 |
create_clip | 根据源视频的时间范围添加片段(需要转录稿)。 |
delete_clip | 删除一个进行中的项目中的某个片段;正在进行的渲染会停止。 |
delete_clips | 一次删除某个进行中的项目中的多个片段(对应选择栏的 Delete);正在进行的渲染会停止。 |
rerender_clip | 将一个片段加入队列以重新渲染(适用于 rerender: false 的编辑、渲染失败或片头/片尾更改之后)。 |
rerender_clips | 一次将某个项目中的多个片段加入队列以重新渲染(适用于编辑或渲染失败之后)。 |
get_media_links | 获取任何人都可在不带 Authorization 请求头的情况下下载的链接,可用于调度工具(Buffer、Metricool)、Instagram 的 video_url、Zapier/Make 或个人下载。 |
list_media_links | 列出你的分享链接,按创建时间从新到旧排序。 |
revoke_media_link | 立即停用分享链接:可指定一个链接(linkId)、某个片段的链接(clipId)、某个项目的链接(projectId)或所有链接(all: true)。 |
list_webhooks | 列出你的 webhook 端点。 |
create_webhook | 订阅某个 URL 以接收事件:project.created、project.done(包含所有片段、分享链接和 zip 文件)、project.failed、clip.done(也包含重新渲染)以及 clip.failed。 |
update_webhook | 更改 webhook 的 url、events 或 description,或启用/停用它(enabled)。 |
delete_webhook | 移除 webhook(取消订阅);不会再向其发送任何内容,其投递日志也会一并删除。 |
rotate_webhook_secret | 为 webhook 生成新的签名密钥。 |
test_webhook | 立即发送一条 webhook.test 投递,并等待响应。 |
list_webhook_deliveries | 获取投递日志,按时间从新到旧排序。 |
resend_webhook_delivery | 再次发送一条投递(开启新一轮尝试,webhook-id 和请求体不变,重新签名)。 |
export_project | 获取 zip 下载链接,其中包含项目渲染完成的片段及 YouTube/TikTok 元数据。 |
report_issue | 获取问题报告 zip 的下载链接(与应用中 Report an issue 保存的文件相同;管理员可能会加入整个实例范围的诊断信息)。 |
retry_project | 从最远的已完成阶段恢复失败或卡住的项目:如果已有片段,则重新渲染未完成的片段;否则重新挑选片段、优化或转录。 |
refine_project | 对现有转录稿重新运行优化步骤:实体(姓名拼写)、口语赘词(填充词和重复内容剪除)、场景(镜头剪切);省略 steps 时会运行全部三个步骤。 |
reanalyze_project | 根据现有转录稿重新挑选片段(需要转录稿),也可选择使用新设置。 |
search_assets | 查找素材。 |
add_asset | 只能通过一个来源添加素材:公开 URL 或 uploadId(视频、图片、GIF、SVG 或声音)会成为你素材库中的一个上传项;search_assets 返回的目录/提供方结果(provider + externalId)会生成一个实例共享副本(已验证校验和并记录许可证;不会列在你的素材库中)。 |
update_asset | 重命名你素材库中的某个项目、替换其标签,或更改静态图片的显示时长(也适用于已删除项目;共享目录和提供方项目无法更改)。 |
delete_asset | 删除你上传的某个素材:该素材会从素材库和默认设置中移除,并移至 Deleted。 |
restore_asset | 恢复你素材库中已删除的某个项目(使用 delete_asset 或 list_deleted 返回的 id);恢复未删除的项目不会产生任何更改。 |
asset_usage | 查看素材库中的某个项目被用在何处:项目和片段的片头、片尾、叠加层或音乐中(不包括已替换的片段),以及图层模板中。 |
set_default_bumper | 将素材库中的某个项目设为默认片头或片尾,供你之后创建的项目使用(assetId 为 null 时会清除该设置)。 |
set_project_bumpers | 设置某个项目的片头和/或片尾:"default"(你当前的默认设置)、"none" 或素材库中的某个 asset id;未指定的项保持不变,introSec/outroSec 会与项目一同保存。 |
set_clip_bumpers | 为同一项目中最多 200 个片段覆盖片头和/或片尾:"inherit"(沿用项目设置)、"none" 或素材库中的某个 asset id;未指定的项保持不变。 |
list_layer_templates | 列出你的图层模板(适用于任意片段长度的图层,以及可选的片头/片尾),按创建时间从新到旧排序。 |
save_layer_template | 只能通过 clipId(该片段的图层:贯穿片段时长的图层设为填充;接触起始位置的设为头部;接触结束位置的设为尾部;否则锚定到较近的一侧;还包括该片段渲染时使用的片头和片尾)或 layers(每个图层都带有时间设置,参见 list_layer_templates;搭配 bumpers 使用 [] 可创建仅含片头/片尾的模板)中的一个来创建模板。 |
update_layer_template | 重命名图层模板、替换其图层(整个列表,每个图层都带有时间设置)和/或替换其片头/片尾(bumpers;null = 不设置片头或片尾);未指定的字段保持不变。 |
delete_layer_template | 删除模板(将其移至 Deleted),并从项目默认设置中移除;已应用其图层的片段会保留这些图层。 |
restore_layer_template | 恢复已删除的图层模板(使用 list_layer_templates deleted 或 list_deleted 返回的 id)。 |
apply_framing | 将某个片段的取景设置应用为整个项目的取景设置,适用于布局始终不变的录制内容(如带有摄像头画面的幻灯片或固定机位):片段中 at 指定的分段(否则为第一个带取景设置的分段)会提供模式以及微调/主体/裁剪设置,这些设置将成为项目的 reframe 和 reframeAdjust;每个片段自己的分段都会被清除(除非设置 keepOverrides),并会重新渲染已完成的片段。 |
apply_layer_template | 将模板应用到某个项目的片段(最多 200 个 clipIds,或全部片段):将模板图层适配到每个片段的时长,并将模板设置的片头/片尾设为每个片段自己的片头/片尾。 |
delete_project | 删除你的某个项目及其所有片段;正在进行的工作会停止。 |
purge_project | 永久移除已删除的项目及其文件,以释放存储空间(通过 list_deleted 或 list_projects deleted: true 查找)。 |
restore_project | 恢复已删除的项目及其片段和文件(通过 list_deleted 或 list_projects deleted: true 查找);恢复未删除的项目不会产生任何更改。 |
restore_clip | 恢复已删除或被重新分析所替换的片段(通过 list_deleted 或 get_project → replacedClips 查找);恢复的片段会移至项目片段列表的末尾。 |
list_deleted | 列出可恢复的删除项(Deleted 页面):按时间从新到旧统一列出项目、进行中项目的片段、素材库项目和模板。 |
purge_clip | 永久移除进行中项目中的某个已删除片段及其渲染文件,以释放存储空间(通过 list_deleted 查找)。 |
purge_asset | 永久移除已删除的素材库上传项及其文件,以释放存储空间(通过 list_deleted 查找)。 |
purge_layer_template | 永久移除已删除的图层模板(通过 list_deleted 查找)。 |
check_for_update | 仅限管理员:是否发布了更新版本的 Reelsaw? |
list_releases | 列出 GitHub(reelsaw.com/releases)上已发布的 Reelsaw 版本:标签、日期、简短摘要和变更说明。 |
get_instance_settings | 仅限管理员:获取当前生效的实例设置(注册模式、电子邮件验证、验证码、法律链接、配额、上传限制、保留期限、卡顿监控、更新检查、运行状况详情)及每项设置的来源。 |
list_workers | 仅限管理员:列出所有已签到的 Reelsaw 进程(API、worker、组合应用):CPU、内存、磁盘(剩余/总计 MB,-1 = 无法测量;diskLeasedMB = 已被进行中任务预留的卷剩余空间)、GPU、渲染槽位、监听的队列及其任务。 |
get_failure | 仅限管理员:获取某个失败的项目或片段(标题、所有者电子邮件、阶段、完整记录原因、发生时间)。 |
get_queue | 仅限管理员:获取每个流水线阶段中等待、运行和重试的任务数量,以及当前正在执行的任务(项目标题、尝试次数、开始时间)。 |
list_failures | 仅限管理员:列出整个实例中失败的项目和片段(所有者电子邮件、阶段、原因、发生时间)。 |
retry_failures | 仅限管理员:通过与 retry_project / rerender_clip 相同的生命周期重试符合条件的失败项目和片段(不会直接操作队列)。 |
set_instance_settings | 仅限管理员:无需重启即可更改实例设置;每个进程都会在几秒内应用更改,并记录此操作。 |
get_billing | 托管实例:获取你的套餐和订阅、计费周期、当前生效的限额及其来源(实例、套餐或管理员覆盖设置)、本周期的用量、渲染是否带水印,以及可选套餐。 |
billing_checkout_url | 托管实例:获取套餐对应的 Stripe Checkout URL(如果账户已在付费,则获取用于确认套餐变更的 Billing Portal 页面 URL)。 |
list_transcript_flags | 列出 Reelsaw 在项目转录稿中修正或建议修正的内容:误听的姓名、要剪除的填充词和重复内容,以及你自己所做的剪除和更正。 |
review_transcript_flags | 接受或拒绝建议的转录稿修正(使用 list_transcript_flags 返回的 ids),也可为接受的姓名修正提供你自己的替换文本。 |
add_transcript_correction | 让字幕显示“text”,替代 startSec 和 endSec 之间说出的内容(例如误听的姓名);该时间范围必须覆盖完整词语。 |
add_transcript_cut | 从转录稿中移除 startSec 和 endSec 之间说出的内容,让字幕和渲染后的视频跳过这些内容;该时间范围必须覆盖完整词语。 |
get_project_defaults | 获取每个新项目开始时使用的设置(Settings、Project defaults):语言、词汇表、说明、重点、片段长度、停顿、字幕外观和优化步骤。 |
set_project_defaults | 替换每个新项目开始时使用的设置;现有项目会保留各自的设置。 |
set_project_source_text | 隐藏或保留源视频中烧录的文字(字幕、徽标),适用于整个项目:hide 会遮挡文字,keep 会保留文字,always 会始终显示文字。 |
update_project_style | 更改整个项目的字幕/钩子外观(字体、颜色、大小、位置、每行字数)。 |
list_decisions | 列出你的决策日志:你接受、拒绝、剪除、更正、编辑、删除或恢复的内容,按时间从新到旧排序;即使删除项目,日志也会保留,供 Reelsaw 学习。 |
export_decisions | 以导出格式分页列出你的决策日志,按时间从旧到新排序,供评估使用。 |
forget_decisions | 删除决策日志中的条目,让 Reelsaw 停止从中学习:可指定某个项目(projectId,仍存在或已永久删除),也可省略 projectId 以删除全部条目。 |
set_learning | 设置 Reelsaw 是否记录你的决策(接受、拒绝、剪除、片段编辑)并从中学习(Settings → Learning,默认开启)。 |
billing_portal_url | 托管实例:获取 Stripe Billing Portal URL,用于查看账单、更换银行卡或在当前周期结束时取消订阅。 |
get_project / get_clip 会显示每个片段的实际片头/片尾及其来源。 | |
它们调用与 Web 应用相同的 tRPC 过程,因此所有权检查和验证逻辑完全一致(apps/server/src/mcp.ts;两个传输方式公布的工具目录见 | |
apps/server/src/mcp-tools.ts)。同一个密钥也用于验证 tRPC | |
(/api/trpc/*)、上传和媒体下载,因此 Agent 可以获取渲染后的片段。密钥以哈希形式静态存储(@better-auth/api-key);可在 Settings 中撤销。 |
操作员 CLI
docker compose exec app reelsaw <command>(请参阅 TROUBLESHOOTING,了解适用场景)。根据 apps/server/src/cli.ts 的文件头生成。
运行命令:docker compose exec app reelsaw <command>(源码中使用:pnpm --filter @reelsaw/server cli <command>)。
| 命令 | 功能 |
|---|---|
reelsaw version [--json] | 当前运行版本、最新发布版本以及升级方法 |
reelsaw releases [--json] [--limit n] | 从 GitHub 获取已发布版本(reelsaw.com/releases / list_releases) |
reelsaw users | 列出账户:角色、状态、注册日期 |
reelsaw approve <email> | 批准待审核的账户(邮件功能关闭或管理员被锁定时使用) |
reelsaw make-admin <email> | 将账户提升为管理员(如有需要,会先批准该账户) |
reelsaw set-password <email> [--must-change] | 通过提示输入账户密码(或从 stdin 读取两行);--must-change 会要求用户下次登录时更改密码。请勿将密码放在命令行中 |
reelsaw retention | 立即运行保留期限清理(SOURCE_RETENTION_DAYS / CLIP_RETENTION_DAYS) |
reelsaw deleted [email] | 将可恢复的删除项及其清除日期列在一个列表中。不指定邮箱:列出整个安装中的项目。指定邮箱:列出该账户的项目(Deleted 页面 / list_deleted) |
reelsaw restore <projectId> | 撤销项目删除(等同于 projects.restore 和 restore_project 工具) |
reelsaw restore-clip <clipId> | 恢复已删除的片段,或恢复被重新分析替换的片段 |
reelsaw delete-clip <clipId> | 删除一个片段,可像项目一样恢复(等同于 clips.delete 和 delete_clip) |
reelsaw delete-clips <projectId> <clipId…> | 一次删除同一项目中的多个片段(等同于选择栏、clips.deleteMany 和 delete_clips) |
reelsaw purge-clip <clipId> | 永久删除一个已删除的片段(等同于 clips.purge 和 purge_clip) |
reelsaw captions <clipId> [--format srt|vtt] [--words] | 将片段渲染后的字幕输出为 SRT 或 VTT 文件(等同于 clips.captions 和 get_clip_captions) |
reelsaw transcript-file <projectId> [--format srt|vtt] [--words] | 将项目转录文本输出为 SRT 或 VTT 文件(等同于 projects.transcriptFile 和 get_project_transcript_file) |
reelsaw import-transcript <projectId> <file.srt|file.vtt> [--language xx] | 使用字幕文件作为转录文本,而不是进行转录(等同于 projects.importTranscript 和 import_transcript) |
reelsaw update-project <projectId> [--title t] [--set key=value]… [--clear key]… | 重命名项目或更改其设置,不重新运行任何任务(等同于 projects.update 和 update_project);值可以是 JSON 或文本 |
reelsaw source-text <projectId> <hide|keep|always> | 隐藏或保留源视频中烧录的文字,并重新渲染已完成的片段(等同于 projects.setSourceText 和 set_project_source_text) |
reelsaw update-style <projectId> [--set key=value]… [--keep-overrides] [--no-rerender] | 更改整个项目的字幕 / Hook 样式,清除各片段的覆盖设置并重新渲染(等同于 projects.updateStyle 和 update_project_style);值可以是 JSON 或文本 |
reelsaw create-project <url> [--user <email|id>] [--title t] [--set key=value]… | 为某个账户从链接创建项目(指定用户;否则使用唯一账户);等同于 projects.create 和 create_project;接受与 update-project 相同的设置 |
reelsaw retry <projectId> | 从最远的已完成阶段恢复失败或卡住的项目(等同于 Retry 按钮、projects.retry 和 retry_project) |
reelsaw reanalyze <projectId> [--set key=value]… | 根据现有转录文本重新挑选片段,可选用新的选择设置;手动创建或编辑的片段会保留(等同于 projects.reanalyze 和 reanalyze_project) |
reelsaw refine <projectId> [--steps entities,disfluency,scenes] [--vocabulary text] | 对现有转录文本重新运行精修步骤,然后重新渲染(等同于 projects.refine 和 refine_project) |
reelsaw delete-project <projectId> [--forget-decisions] | 删除项目,可像片段一样恢复(等同于 projects.delete 和 delete_project);清除项目时,--forget-decisions 会一并清除其决策记录 |
reelsaw export <projectId> [clipId…] --out <file.zip> | 将已渲染的片段及 YouTube / TikTok 元数据写入 zip 文件;条件与 Export 按钮和 export_project 相同(所有选中的片段都必须属于该项目,且至少有一个已渲染) |
reelsaw create-clip <projectId> <startSec> <endSec> [--mode exact|ai] | 根据源视频的时间范围添加片段:exact 保留边界,ai 会在范围内挑选最佳时刻(等同于 clips.create 和 create_clip) |
reelsaw update-clip <clipId> [--title t] [--hook t] [--caption t] [--start s] [--end s] [--rerender|--no-rerender] | 编辑一个片段;除非指定不重新渲染,否则会重新渲染视频中烧录内容有所更改的片段(等同于 clips.update 和 update_clip) |
reelsaw purge [--dry-run] | 立即清除超过 DELETED_RETENTION_DAYS 的已删除项目和被替换的片段 |
reelsaw purge-project <projectId> [--forget-decisions] | 永久删除一个已删除的项目(释放存储空间;已用分钟数仍计入);其决策记录会保留在决策日志中,除非指定 --forget-decisions |
reelsaw decisions <email> [--kind k] [--project id] | 账户的决策日志:接受、拒绝、剪切和编辑了哪些内容(Settings → Learning / list_decisions) |
reelsaw decisions export <email> [--out f.jsonl] [--since iso] | 将决策日志导出为 JSON 行,用于评测(export_decisions;harvest-decisions.ts、decisions suite) |
reelsaw decisions forget <email> [--project id] | 删除该账户的决策记录,或删除某个项目的记录(forget_decisions) |
reelsaw decisions learning <email> on|off | 记录或停止记录该账户的决策(Settings → Learning / set_learning) |
reelsaw decisions backfill [--dry-run] | 将日志创建之前仍保留在转录文本中的决策写入日志 |
reelsaw media-links <email> [--project id] [--json] [--active] | 账户的分享链接:目标、状态、到期时间、上次使用时间(Settings → Share links / list_media_links) |
reelsaw media-links create <projectId> [clipId…] [--kind clip|thumb|srt|vtt|export] [--ttl days] [--label text] | 为每个片段创建一个无需登录的分享链接(或为 zip 创建一个链接)(get_media_links);除非指定 --ttl 或 --label,否则会复用现有的有效链接 |
reelsaw media-links revoke <linkId> | --project <id> | --clip <id> | --all <email> | 立即停用分享链接(revoke_media_link) |
reelsaw webhooks <email> [--json] | 账户的 webhook 端点:隐藏部分信息的 URL、事件、状态、上次投递情况(Settings → Webhooks / list_webhooks) |
reelsaw webhooks deliveries <endpointId> [--status failed] [--json] | 端点的投递日志(list_webhook_deliveries) |
reelsaw webhooks resend <deliveryId> | test <endpointId> | enable <endpointId> | disable <endpointId> | 重新发送一条投递、立即发送测试消息,或启用 / 停用端点(resend_webhook_delivery、test_webhook、update_webhook) |
reelsaw machine [--json] | 此设备的信息:CPU、内存、磁盘、GPU、正在使用的 ffmpeg 编码器及原因、各设备端模型的运行位置(等同于 Settings → System → Overview) |
reelsaw ytdlp-proxies [--json] | yt-dlp 出站代理池:运营者优先级与熔断状态(可靠性),不会显示 URL |
reelsaw workers [--json] | 所有已签到的进程:角色、规格、上次出现时间、当前任务(等同于 Settings → System → Workers) |
reelsaw queue [--json] | 各阶段等待中 / 运行中 / 重试中的任务,以及正在处理的内容(等同于 Settings → System → Queue) |
reelsaw failure <projectId|clipId> [--json] | 查看一个失败的项目或片段:所有者、阶段、完整的记录原因(等同于点击 Settings → System → Queue → Failed work 下的一行) |
reelsaw retry-failures [--stage s] [--q text] [--since 24h] [--json] | 平台修复后,重试原因符合条件的失败项目和片段(等同于 Settings → System → Queue → Retry all matching) |
reelsaw status <projectId> | 项目、精修步骤、片段、实时任务、上次错误 |
reelsaw events <projectId> [n=30] | 最近的流水线事件 |
reelsaw clip <clipId> | 一个片段的所有信息,包括渲染诊断信息 |
reelsaw faces <clipId> [a-b] [--json] | 每个采样帧中的人脸框 + 拆分后的说话人轨迹 |
reelsaw rerender <clipId…> | 将一个或多个片段加入重新渲染队列;尚未获得 AI 标题的片段会先进行准备(等同于 UI 按钮、clips.rerender / rerenderMany 和 rerender_clip / rerender_clips) |
reelsaw import-report <zip> [--user <email>] [--source <file>] [--no-download] [--allow-any-url] | 将问题报告重新载入为新项目(仅限开发环境;zip 内容是不可信数据,请参阅其 README) |
reelsaw report [projectId] [--out file.zip] [--instance] [--description text] [--user email] | 写入问题报告 zip 文件(等同于 Report an issue);--instance 会添加所有账户中的未完成工作、失败任务、用量和错误日志(运营者) |
reelsaw cuts <clipId> [--save <dir>] [--fixture <dir>] | 对片段的语气不流畅内容剪切搜索进行试运行,并打印每个候选项的判定;可保存被移除的音频和上下文音频,并将每个标记的片段导出为离线回归用例,供 pnpm eval:cuts 使用 |
reelsaw align <projectId> <start> <end> | 获取源视频指定时间范围的对齐模型词级时间戳 |
reelsaw hear <clipId> [start] [dur] | 转录片段渲染后版本的指定范围(即观众听到的内容) |
reelsaw flags <projectId> [from] [to] | 带状态的转录标记(实体 / 语气不流畅 / 循环折叠) |
reelsaw storage [sweep [--dry-run] | reclaim <MB> [--dry-run]] | 磁盘用量:唯一文件、仅暂存文件、存储文件(S3)的暂存副本、已租给正在运行的任务的文件、重复源文件、已过期但仍存在的字节;sweep 会移除没有任何引用的残留文件,并清除空闲缓存副本(协调器每小时执行的清理);reclaim 会逐出最近最少使用的副本,直到释放指定 MB 的空间(磁盘空间不足的任务会自行执行此操作) |
reelsaw stuck | 所有未完成的项目 / 片段:运行时长、上次事件、实时任务、问题 |
reelsaw recover <clipId|projectId> | 片段:重新运行相应步骤(准备或渲染);失败的项目:重试;其他项目:重新加入丢失的任务 |
reelsaw logs <id> [n=60] [--since 2h] [--level debug] [--module render] [--raw] | 从 LOG_FILE 中查找提及项目 / 片段 / 任务 / 请求 ID 的日志行(重新部署后仍保留) |
reelsaw log-level [<level|module=level,...>|reset] [--for 15m] | 显示当前生效的日志级别,或临时设置日志级别(例如 render=debug);所有进程会在约 5 秒内同步 |
reelsaw scenes <projectId> [--store] | 重新检测镜头切换点(自适应);--store 会保存检测结果,供布局 / 吸附 / 叠加层使用 |
reelsaw library list|search|add|update|rename|delete|restore|purge|usage|default|reindex [--user <email>] | 媒体库:片头 / 片尾视频和图片、徽标、贴纸、GIF、SVG、音乐和音效。list 显示你的文件,search 会搜索你的媒体库、Reelsaw 目录(默认)或已启用的提供方(pexels、pixabay、freesound、iconify、openverse、giphy、klipy);add 接受文件、链接或搜索结果(会记录许可和署名信息);update 设置标签,rename 重命名,default 设置新项目的默认片头或片尾。以指定用户身份操作;否则使用此安装中的唯一账户。reindex 会为每个项目重建资源引用表(“使用位置”、清除安全检查),直至完成 |
reelsaw catalog status|refresh | 当前生效的 Reelsaw 目录清单(版本、来源、上次检查时间);refresh 会立即请求 CATALOG_URL |
reelsaw project-bumpers <projectId> [--intro default|none|<id>] [--outro …] [--intro-sec n] [--outro-sec n] [--rerender] | 设置项目的片头 / 片尾:使用媒体库默认值、不使用,或指定某个项目 |
reelsaw clip-bumpers <projectId> <clipId…> [--intro inherit|none|<id>] [--outro …] [--intro-sec n] [--outro-sec n] [--rerender] | 设置片段自己的选项(继承项目设置、不使用,或指定某个项目);两者均以项目所有者身份操作;--rerender 仅重新渲染受影响的片段 |
reelsaw clip-layers <projectId> <clipId> [--set '<json array>' | --add <assetId> --from <sec> --to <sec> [--x f] [--y f] [--w f] [--opacity f] [--loop|--no-loop] | --add-text <text> --from <sec> --to <sec> [--x f] [--y f] [--size f] [--color #RRGGBB] [--font <family>] [--box] [--opacity f] | --add-sound <assetId> --from <sec> [--to <sec>] [--volume 0-1] [--offset <sec>] [--fade <sec>] [--duck|--no-duck] [--loop|--no-loop] | --remove <layerId> | --clear] [--rerender] | 叠加在片段上或在片段下方播放的所有内容,以一个列表管理(源视频秒数,x/y/w/size 为 9:16 画面中的比例);时长不超过 8 秒的音效会以完整音量播放一次,其他内容则作为音乐处理(闪避、淡入淡出、循环);不指定更改标记时会列出图层 |
reelsaw apply-framing <clipId> [--at <sec>] [--keep-overrides] [--no-rerender] | 将一个片段的取景设置(--at 指定位置的片段段落;否则使用首个已取景段落:模式加微调 / 主体 / 裁剪)设为项目中所有片段的默认设置,清除其他片段自己的设置段落,并重新渲染已完成的片段(等同于编辑器的 "Use for all clips" 和 apply_framing) |
reelsaw templates list | show <id> | save <projectId> <clipId> <name…> | rename <id> <name…> | set <id> [--layers '<json array>'] [--intro keep|none|<id>] [--outro …] [--intro-sec n] [--outro-sec n] | delete <id> | restore <id> | purge <id> | apply <templateId> <projectId> [clipId…] [--add] [--rerender] | 图层模板:图层的时间设置可适用于任意片段时长(开头 / 结尾 / 填充 / 相对位置),并可选配片头 / 片尾(keep = 保留片段自己的设置);save 根据片段的图层、片头和片尾创建模板,apply 将模板应用到项目的片段上(不指定片段 ID 时应用到所有片段;--add 会保留片段自己的图层,默认则替换;无论哪种情况,模板设置的片头 / 片尾都会替换片段现有设置);--rerender 会重新渲染已完成的片段。操作方式与 Templates 页面和 MCP 工具相同 |
reelsaw overlays <projectId> [--store] | 扫描源视频中的烧录字幕 / 水印;--store 会保存扫描结果(渲染时隐藏它们) |
reelsaw usage [--days 30] | 各账户用量:项目数、源视频分钟数、片段数、模型调用 / token / 报告费用、转录分钟数、存储字节数(与 Users → Usage 显示的数据相同) |
reelsaw limits <email> [--watermark always|never|follow] | 账户配额:实例值、套餐、覆盖设置、当前生效值、当前用量;--watermark 设置每位用户的覆盖值(等同于 Users → Limits) |
reelsaw abuse [--run] | 每小时滥用检查所标记的账户(共用的注册地址、突发注册、临时域名);--run 会立即重新计算标记 |
reelsaw billing <email> | sync [<email>] | set-plan <email> <plan|none> | 托管版(REELSAW_EE):账户的套餐、订阅、Stripe 显示信息及最近的 webhook 事件;sync 会重新读取一个账户或所有订阅的 Stripe 信息(手动运行每日核对);set-plan 无需 Stripe 即可授予套餐(none 会移除套餐) |
reelsaw settings [KEY=value…] [--clear KEY…] | 当前生效的实例设置(注册模式、配额、保留期限等):.env 默认值、管理员覆盖值、最终生效值;KEY=value 可在不重启的情况下更改设置,--clear KEY 会恢复为 .env 的值(等同于 Settings → System 和 set_instance_settings) |
reelsaw mcp | 通过 stdio 为启动命令的客户端提供 MCP 服务:与 /api/mcp 使用相同的工具,并通过 REELSAW_URL 转发,使用 REELSAW_API_KEY 进行身份验证(无需数据库;请参阅 src/mcp-stdio.ts) |
设置和优先级
值的来源按优先级从低到高排列(后面的链接会覆盖前面的链接):
| 内容 | 链 |
|---|---|
| 模型(语言、转录、对齐) | .env(AI_MODEL、TRANSCRIPTION_MODEL、TRANSCRIPT_ALIGN_MODEL),然后是管理员的运行时覆盖设置(Settings → System → Models,存储在 instance_settings 中)。已在运行的阶段会继续使用启动时的模型。 |
| 提供商密钥和 URL | .env,然后是管理员在 Settings 中输入的加密密钥。 |
| 实例设置(注册模式、电子邮件验证、验证码、法律链接、配额、上传限制、精修阶段开关、保留策略、卡滞监视器、更新检查、健康详情) | .env,然后是管理员的运行时覆盖设置(Settings → System、reelsaw settings、set_instance_settings;存储在 instance_settings 中,并在几秒内应用到每个进程)。如果覆盖设置的前置条件不再满足(例如,启用了电子邮件验证但未配置电子邮件驱动程序),则会忽略该设置并显示这一情况。 |
精修开关(TRANSCRIPT_CORRECTION、DISFLUENCY_REMOVAL、DISFLUENCY_LLM、REFINE_SCENES = 屏幕文本扫描;镜头切换始终会检测) | .env,然后是实例设置(Settings → System → Pipeline)。TRANSCRIPT_CORRECTION 和 REFINE_SCENES 默认开启,语气词相关开关默认关闭。该值是一个上限:关闭时,所有项目都会跳过此步骤。开启时,由各项目的 refineEntities / refineDisfluency / disfluencyLlm / refineScenes(以及用户的 Project defaults)决定:除非关闭,否则会运行名称修正和文本扫描;只有开启时才会移除语气词。 |
其他阶段参数(TRANSCRIPT_BIAS、VAD、ALIGNER、FACE_DETECTION、TEXT_DETECTION、并发) | 仅限 .env。没有 UI 或按项目覆盖设置。 |
| 项目设置 | 内置默认值,然后是用户的 Project defaults,再然后是创建表单或 Agent 发送的内容。结果会冻结到 projects.settings 中;之后的更改(updateStyle、使用新设置重新分析)会更新该行。 |
| 片段样式 | 项目样式,然后是片段自己的覆盖设置(clips.style)。除非指定保留,否则 updateStyle 会清除片段覆盖设置。 |
| 图层模板 | 用户的 Project defaults layerTemplateId,然后是项目设置(创建时冻结到 projects.settings 中),此后项目创建的每个片段都会获得该模板的图层以及模板设置的片头 / 片尾(作为片段自己的选择)。现有片段不会随设置改变;可通过 apply_layer_template / 选择栏中的 Template… / reelsaw templates apply 执行这些操作。 |
| 构图 | auto(由人脸检测决定),然后是项目的 reframe 及其 reframeAdjust(微调、主体或裁剪;apply_framing 可从一个片段同时设置两者),然后是片段的 layoutOverride,再然后是片段的某个区段(layoutSections[i],包括模式和调整)。 |
| 配额 | .env 中的 MAX_*(0 = 无限制),然后是管理员在 Settings → System → Limits 下保存的实例设置,再然后是账户方案授予的额度(托管版、REELSAW_EE),最后是 Users 页面上设置的单用户限制。Users 页面会说明当前适用哪项限制。 |
| 水印 | 管理员针对用户的单独设置(Users → Limits);否则使用方案设置(付费方案会移除水印);否则,对所有非管理员应用 WATERMARK_NON_ADMINS。 |
| 分享链接 | .env 中的 MEDIA_LINK_TTL_DAYS(默认 90)和 MEDIA_LINK_ALLOW_NEVER(默认 true;托管版设为 false),然后是实例设置(Settings → System → Limits)。链接可以请求更短的有效期,不能请求更长的有效期。MEDIA_LINKS_REDIRECT(仅限 .env,S3)会返回指向预签名 URL 的重定向,而不是文件本身。 |
| Webhook | MAX_WEBHOOKS_PER_USER 遵循配额链(默认 10)。ALLOW_PRIVATE_WEBHOOK_URLS(默认 false:仅允许公开 https)和 WEBHOOK_DELIVERY_RETENTION_DAYS(默认 14)是实例设置;WEBHOOK_CONCURRENCY 仅限 .env。 |
| 学习(决策日志) | 所有账户均默认开启;用户可通过 Settings → Learning 开关(或 set_learning)关闭记录。DECISION_RETENTION_DAYS(实例设置,默认 0 = 保留)会清理旧决策;清理时会保留项目决策,除非用户在删除项目时勾选“Also forget what Reelsaw learned from it”,或在“Delete forever”对话框中勾选(所有清理操作都会遵循,包括保留期清理)。 |
| 有两点需要了解: |
- 环境/实例级细化开关优先。 即使项目要求移除填充词,
DISFLUENCY_REMOVAL=false(或对应的 Pipeline 设置)也会为所有人跳过填充词移除;新建项目表单会禁用该切换项,并说明原因。TRANSCRIPT_BIAS=false仍会静默忽略词汇提示(仅限.env)。如果某项项目设置似乎没有生效,请先检查开关(故障排除 中的“A project or clip setting has no effect”)。 - 现有项目会保留其冻结的设置。 更改 Project 默认值只会影响新项目。
代码:apps/server/src/pipeline/settings.ts(项目)、packages/shared/src/index.ts 中的 resolveStyle(风格)、apps/server/src/pipeline/render.ts(构图)、apps/server/src/ai/models.ts(模型)、apps/server/src/quota.ts(配额)。
架构
apps/web React 19 + Vite + Tailwind + TanStack Query + tRPC client + Better Auth client
apps/server Hono + tRPC + Better Auth + Drizzle (Postgres) + pg-boss workers + ffmpeg/yt-dlp
packages/shared zod schemas and types shared by both
服务端组合(apps/server/src/services.ts):createServices() 会依次构建 config → media tools → ai → storage → database (+migrations) → events → queue → auth,并逐项检查。生成的 Services 对象会注入 tRPC 上下文、HTTP 应用和每个 Pipeline 阶段(StageContext),因此不会有任何组件访问全局对象。启动日志会为每个步骤输出一行 boot step ok(JSON 格式;在终端中则采用易读格式),包含步骤名称及其耗时(单位为 ms):config、data dir、resources、media tools、LLM call capture、AI models、on-device detectors、storage、database、model settings、event bus、job queue、email、auth。功能已关闭的步骤会在名称中注明(face detector (disabled))。如果某个步骤失败,进程会以退出码 78 停止,并显示指出该步骤及修复方法的消息,例如转录模型未返回词级时间戳,或模型服务端无法访问。某些步骤取决于进程的角色:RUN_WORKERS=false 的进程在可用磁盘空间低于 MIN_FREE_DISK_MB 时只会发出警告(这样磁盘已满也不会阻止管理员界面释放空间),并跳过人脸、文本和语音检测器;RUN_HTTP=false 的进程会跳过邮件服务器检查。AI models 步骤会检查环境中的模型;数据库启动后,也会检查生效模型(应用管理员的运行时覆盖后)。
Pipeline(每个方框都是一个 pg-boss 队列;每个阶段都会发出事件并将下一个阶段加入队列):
project.ingest ─▶ project.transcribe ─┬▶ refine.entities ─┐
yt-dlp / upload audio → chunks ├▶ refine.disfluency ─┼▶ project.analyze ─▶ clip.render (×N)
ffprobe parallel transcribe └▶ refine.scenes ─┘ LLM picks moments layout + captions
store source checkpoint per chunk (fan-out, fan-in snap to sentences cuts + crossfades
on the transcript row) finalize project
三个细化任务彼此独立:每个任务都会在转录文本的行锁下写入结果(实体标记优先于重叠的语气词标记),最后完成的任务会将 analyze 加入队列。细化步骤用尽重试次数后会标记为失败并跳过;它不会阻塞项目。“重新运行细化”会将任意子集重新加入队列,并沿用现有转录文本,例如编辑词汇后即可使用。分块转录会并行运行 TRANSCRIBE_CONCURRENCY 次调用(自动设置:自托管 whisper 为 1,云服务提供商为 3)。
持久性模型:
pgboss.job是“待运行内容”的事实来源。projects.status/clips.status是“已发生内容”的事实来源。- 每个阶段都会先检查是否已有输出(存储中的源文件、转录分块行、片段行、存储中的渲染片段),并跳过已完成的工作。
- 队列使用
policy: exclusive和singletonKey,确保同一项目/片段的阶段不会同时运行多次。 - 每个队列的
heartbeatSeconds(15 s):如果工作进程崩溃,pg-boss 会在下一次监督检查(每 60 s 一次)时发现心跳超时,将任务标记为失败,并在延迟 15 s 后于其他位置重试;每次重试都会将延迟加倍。请预留一两分钟,而不是几秒;每次中断都会消耗一次重试。 - 启动时以及此后每隔 5 分钟,协调器都会将没有活动任务的进行中项目或片段重新加入队列。
- 手动 重试 会从最远的已完成阶段继续;重新分析 会保留转录文本并挑选新的片段。
开发
在 Docker 中运行热重载循环(挂载源代码,不重建镜像;仅在依赖项变更时重建开发镜像):
docker compose -f docker-compose.yml -f docker-compose.dev.yml --profile local-ai up -d app
open http://localhost:5173 # Vite + HMR; /api and /media proxied to the server (tsx watch)
docker compose exec app reelsaw stuck
在 Docker 外部运行所需的模型:pnpm models:download(人脸、屏幕文字、语音活动、强制对齐;约 105 MB)。
pnpm install
docker compose up -d db
cp .env.example .env # set DATABASE_URL=postgres://reelsaw:reelsaw@localhost:5432/reelsaw
pnpm dev # server on :3000 (tsx watch), web on :5173 (vite, proxies /api and /media)
本地开发时,PATH 中需要有 ffmpeg、ffprobe 和 yt-dlp。架构变更:编辑 apps/server/src/db/schema.ts,然后运行 pnpm db:generate;迁移会在启动时自动运行。
大多数测试都针对本地栈运行(会自行创建和删除用户及数据);CI 只运行无需栈的测试,因此请在自己的栈上运行你改动涉及的测试:
pnpm --filter @reelsaw/server test
发布与分发
每个镜像都由发布工作流构建,并附带 SBOM 和 SLSA 来源证明,以及针对其摘要的 GitHub 工件证明。运行前请验证:
gh attestation verify oci://ghcr.io/osbytes/reelsaw:latest --owner osbytes
docker buildx imagetools inspect ghcr.io/osbytes/reelsaw:latest --format '{{ json .SBOM }}' | head -c 400
用户从已发布的镜像安装。运行时镜像仅包含经过压缩的服务端构建包(apps/server/scripts/bundle.mjs)、Web 构建产物和生产依赖项,scripts/check-image.sh <image> 会验证这些内容。
- 标记
vX.Y.Z→.github/workflows/release.yml构建 amd64 + arm64 镜像,检查镜像,将其推送到 GHCR(若设置了vars.DOCKERHUB_IMAGE+DOCKERHUB_USERNAME/DOCKERHUB_TOKEN,也会推送到 Docker Hub),并将reelsaw-X.Y.Z.zip(已固定镜像版本的deploy/文件夹)附加到发布中。 - 标记前:在本地构建,运行
scripts/check-image.sh reelsaw:latest,并在仅包含.env的临时目录中启动deploy/(参见规范中的验证部分)。 - 版本和变更日志由 changesets 管理:每个 PR 都要添加一个 changeset(
pnpm changeset),"Release" PR 会提升版本,合并后会推送标签。完整流程、版本策略、预发布和热修复:参见 docs/RELEASING.md。
问题报告
报告问题(项目 ⋯ 菜单、失败的片段、账户菜单)会下载一个 zip,其中包含项目数据、流水线决策(stage_artifacts)、日志、系统信息、近期错误和用户描述。报告中不会包含密钥。管理员可以勾选 实例级诊断信息,以纳入所有账户的未完成工作、失败任务、使用情况和错误日志——适用于队列、磁盘或机器问题,而不仅限于他们自己的项目。在本地复现问题:
docker compose cp ~/Downloads/reelsaw-report-….zip app:/data/tmp/report.zip
docker compose exec app reelsaw import-report /data/tmp/report.zip [--user [email protected]]
报告中除 README 和清单外的所有内容都属于不可信数据;让 Agent 处理报告前,请参阅 TROUBLESHOOTING。规范:docs/specs/distribution-and-issue-reports.md。
环境变量参考
所有变量及其默认值和含义均列于 docs/CONFIG.md(从 apps/server/src/config.ts 生成;运行 pnpm docs:gen 可更新)。.env.example 和 deploy/.env.example 由同一来源生成。
可观测性
健康状态。 GET /api/health 是就绪检查,不是 ping。当 Postgres 有响应、事件监听器已连接、任务队列已启动,并且(在运行 worker 的进程中)worker 心跳是最新的,它会返回 200 {"ok": true, "checks": {…}};每项检查的超时时间为 2 s。否则会返回 503 {"ok": false, "checks": {…}},并为每项失败检查(db、events、queue、workers)提供简短原因,详情则记录在日志中。Docker HEALTHCHECK 和指向该服务的任何负载均衡器都会重启或摘除虽已运行但无法工作的进程。
错误和产品分析(可选)。 设置 SENTRY_DSN 可启用 Sentry:服务器会将所有包含错误的 error 或 fatal 日志行报告给 Sentry,并附上该行的 ID(5xx 响应、意外的 MCP 工具错误、任务最终失败的尝试、协调器放弃的任务、失败的电子邮件和账单同步、进程崩溃;仍有待重试时则记录为警告);Web 应用会报告渲染错误和未处理的拒绝,但不会报告已经向用户显示的 4xx 拒绝。工作线程心跳(heartbeat-<WORKER_ID or role>)、协调器和每日账单对账会以 Sentry cron 监控的形式报到,因此工作线程停止运行或循环不再执行时会触发警报。SENTRY_REPLAYS_ON_ERROR_SAMPLE_RATE(默认值为 0)会保留浏览器错误发生前时刻的完全屏蔽回放。设置 POSTHOG_KEY(以及 POSTHOG_HOST,用于欧盟云或可自托管实例)可启用 PostHog:服务器会发送注册、审批、电子邮件验证、项目已创建/已完成/已失败/已取消、片段已完成/已失败、用户请求的任务、配额拒绝、片段下载和导出、MCP 工具调用(工具、结果、客户端名称),以及账单和滥用标记事件,并为每个事件标注其来源(web、api、mcp、cli);Web 应用会发送页面浏览量和少量产品事件(lib/telemetry.ts)。事件属性仅包含 ID、计数、时长和名称,不包含标题、错误消息或其他自由文本;用户仅通过用户 ID、角色和套餐进行识别,不启用自动捕获或会话录制。除非设置相关配置,否则两者均处于关闭状态;浏览器通过 system.status 获取公开密钥,且只有启用时才会请求 SDK 代码块。提示词、转录文本和媒体绝不会发送到任一服务(apps/server/src/telemetry.ts)。发布镜像会在发布工作流配置了 SENTRY_AUTH_TOKEN 时将源映射上传到 Sentry(.github/workflows/release.yml);镜像中不会包含映射文件。
OTEL_ENABLED=true
OTEL_EXPORTER_OTLP_ENDPOINT=http://otel-collector:4318 # OTLP/HTTP; traces + metrics
OTEL_SERVICE_NAME=reelsaw
Sentry 的 OTLP 端点会接收跟踪和日志(…/integration/otlp,以及 OTEL_EXPORTER_OTLP_HEADERS)。每条达到相应级别的 pino 日志都会导出到 /v1/logs,并附上当前跟踪 ID;提示词、转录文本和地址字段会保留在本地日志中。Sentry 不会接收 OTLP 指标。设置 SENTRY_DSN 并保持 OTEL_METRICS_ENABLED 开启时,服务器会通过 DSN 发送 Node 运行时量表(内存、CPU、事件循环)以及下方所列的指标。设置 OTEL_METRICS_ENABLED=false 可保留跟踪和日志,同时不发送这些指标。
指标。时长单位为秒。属性取值范围有限(阶段、代理 ID、模型、结果),绝不会包含项目、片段或用户 ID。处理工作量会随视频长度增长,因此阶段、下载和转录数据各自都包含一个实时因子:每秒媒体内容所需的实际秒数(0.25 = 1 小时视频需要 15 分钟),可用于比较 30 秒片段和 2 小时演讲。
| 指标 | 单位 | 属性 | 可回答的问题 |
|---|---|---|---|
reelsaw.pipeline.stage.duration | s | stage、outcome(ok、error、superseded、gone),渲染时还有 encoder | 每个阶段的作业耗时,以及失败频率 |
reelsaw.pipeline.media.duration | s | stage、encoder | 每个已完成作业的源媒体时长(prepare 和 render 阶段为片段时长) |
reelsaw.pipeline.realtime_factor | 1 | stage、encoder | 每秒媒体时长对应的阶段耗时;render 不包括等待空闲槽位的时间 |
reelsaw.ytdlp.download.duration | s | proxy、outcome、fallback | 通过代理池下载一个链接所需的时间,包括回退和等待 |
reelsaw.ytdlp.download.realtime_factor | 1 | proxy | 按代理统计的视频每秒下载耗时 |
reelsaw.ytdlp.download.throughput | By/s | proxy | 按代理统计的源数据字节传输速率 |
reelsaw.ytdlp.attempts | {attempt} | proxy、outcome(ok、proxy、content、other、abort、busy、circuit_open)、panic | 尝试了哪个候选项,以及失败原因:proxy 故障计入出口代理,content 故障计入视频 |
reelsaw.ytdlp.attempt.duration | s | proxy、outcome | yt-dlp 进程通过一个候选项运行的实际耗时 |
reelsaw.ytdlp.proxy.circuit.changes | {change} | proxy、state | 熔断器打开(冷却)、半开和关闭的情况 |
reelsaw.ytdlp.proxy.healthy / .in_flight / .success_rate | gauge | proxy | 熔断器已关闭(1)或正在冷却(0)、正在运行的下载,以及最近 20 次的成功率 |
reelsaw.stt.duration / .audio.duration / .realtime_factor | s、s、1 | provider、model、outcome | 转录耗时、每次调用的音频时长,以及每秒音频对应的耗时 |
gen_ai.client.operation.duration | s | gen_ai.request.model、reelsaw.stage、reelsaw.call、outcome | 每个工作流步骤的大模型延迟 |
gen_ai.client.token.usage | {token} | gen_ai.request.model、gen_ai.token.type、reelsaw.stage、reelsaw.call | 每次调用的令牌数 |
reelsaw.llm.cost | USD | gen_ai.request.model、reelsaw.stage | 提供方报告的费用(AI Gateway) |
reelsaw.render.slots | gauge | state(running、limit) | 工作进程中正在运行的渲染任务数,以及 CPU 和内存允许的上限 |
reelsaw.worker.jobs.active | gauge | queue | 工作进程正在运行的作业数 |
reelsaw.span.duration | s | span、status | 每个 withSpan:每个阶段,以及每次 exec ffmpeg、exec ffprobe、exec yt-dlp |
通过 OTLP,自动插桩会添加 http.server.request.duration,pg-boss 会添加 messaging.process.duration 和 pgboss.queue.jobs(按队列和状态统计积压量)。代理仪表板:按 proxy 统计 reelsaw.ytdlp.download.realtime_factor 的 p50/p95,按 proxy 和 outcome 统计 reelsaw.ytdlp.attempts 速率,以及按 proxy 统计 reelsaw.ytdlp.proxy.healthy。
跨度:按路由命名的入站 HTTP(POST /api/trpc/projects.create;排除 /api/health)、Postgres 查询、向 AI 服务商发出的 fetch 请求、每个作业对应的 pipeline.<stage>(带有 reelsaw.project_id / reelsaw.clip_id / reelsaw.attempt)、exec ffmpeg|ffprobe|yt-dlp,以及 AI SDK 自带的 ai.generateText / ai.transcribe 跨度(不记录输入/输出)。pg-boss 会将跟踪上下文从 send() 传播到工作进程跨度,因此单条跟踪记录会贯穿一个项目,从创建变更到每个阶段。没有父级的客户端跨度(例如 S3 读取,或没有请求或作业关联的查询,例如队列轮询)不会被记录,除非设置了 OTEL_TRACES_SAMPLER。
浏览器会以 reelsaw-web 的名称导出自己的跨度(页面加载、导航、fetch 和实时订阅;不包括点击),并在 API 调用(包括订阅流)中发送 traceparent,因此一次点击及其触发的服务器工作会处于同一条跟踪记录中。OTEL_TRACES_SAMPLE_RATE(默认值为 1)表示浏览器和服务器共同保留的比例。Sentry 浏览器 SDK 不会进行跟踪;否则这些跨度会重复一份。
实体感知转录
Whisper 会听错名称(“Copia” → “Copious”)。以下三个可选层级可解决这个问题:
- 词汇提示(
TRANSCRIPT_BIAS=true):从频道名称、标题、描述、标签和章节中提取专有名词,再加上在项目表单“Names & terms”中输入的内容,一并传递给转录器(OpenAI/Groq/Speaches 使用prompt,Deepgram 使用keyterm,AssemblyAI 使用wordBoost)。效果最显著:在我们的评测中,WER 减半,实体召回率从 64% 提升至 95%,且无需任何成本。 - 受约束的 LLM 校正(
TRANSCRIPT_CORRECTION=true):听起来像已知实体的转录片段会成为候选项;大模型会结合上下文确认每个候选项并给出置信度分数。只有候选项可以更改,因此模型无法改写转录内容,并且会保留词级时间戳。 - 在 UI 中审核:置信度达到或超过
TRANSCRIPT_AUTO_FIX_CONFIDENCE(0.85)的校正会自动应用并标记为“auto-fixed”;其余校正会显示在转录行下方,并附带置信度以及接受/拒绝按钮。应用审核决定后,只会重新渲染字幕与更改内容重叠的片段。
**语言:**填充词/重复规则、近音名称搜索和 CTC 剪切对齐器仅支持英语。对于检测为其他语言的转录,这些步骤会跳过,并显示通俗易懂的事件提示(“not available for de”),而不是盲目运行;基于语言模型的检查和基于 whisper 的剪切搜索仍可使用。
不流畅表达
填充词(“um”)、重复(“the the”)和错误起句(“your pay actual pay dates”)会在转录后检测(DISFLUENCY_REMOVAL=true):先通过规则处理,置信度接近确定;然后进行模型处理(DISFLUENCY_LLM=true),由模型清理并重写每个窗口,再与原文比对,确保模型只能删除内容。只有当被删除的每个词都是填充词,或在接下来的几个词内被重新表述时,才会保留模型的删除结果;修正内容会作为待审核标记。标记会内嵌显示在转录中(应用后显示删除线),并使用相同的接受/拒绝弹出菜单(通过 MCP:list_transcript_flags、review_transcript_flags、add_transcript_correction、add_transcript_cut)。字幕会移除已接受的不流畅表达,并始终通过跳切将其从音频/视频中剪掉。较长的停顿也会缩短(项目设置 Pauses:自然为 0.6 s,紧凑为 0.35 s,或保留),并通过对片段音频运行 Silero VAD 检测。剪切边界不会取自分块级 ASR 时间戳(它们可能漂移一个词的长度):每次剪切都会先通过 CTC 强制对齐(wav2vec2,结合音频和已知词语,以 20 ms 精度确定精确的词边界,CPU 上约需 0.2 s)定位,并通过对接缝进行一次转录来验证;只有在此失败时,才会通过对接缝周围窗口进行转录(TRANSCRIPT_ALIGN_MODEL),以不同偏移量重新对齐两次(词语时间会随窗口变化),将边界对齐至相邻词语旁最安静的 5 ms 帧,并搜索候选位置,保留第一个通过验证的接缝:两个相邻词语能连续听到,中间没有残留内容,且被删除的词语已消失(或者,当识别器根据上下文补回这些词语时,单独播放被移除的音频仍能听出这些词语)。无法验证的剪切会跳过,并列在片段上。接缝会使用 20 ms 等功率交叉淡化(acrossfade),并采用长度补偿裁剪,确保音频和视频保持同步。规则会检测功能词之间的替换错误(“into between”、“a the”)。正常的复合表达(“out of”、“up to”)会排除在外。
评估
回归评估集中在一个运行器中,并支持可插拔的套件(selection、framing、asr-loops、scenes、pauses、alignment、speakers、overlays、cuts、transcription、disfluency、decisions、selection-model);测试样例较小并纳入 git,大型媒体会下载到被 git 忽略的缓存中,基准版本会进行管理,运行时如出现回归则会失败。参见 docs/EVALS.md,其中介绍了如何将新 bug 转化为测试用例。
pnpm eval # all suites
pnpm eval cuts framing # some suites
pnpm eval --offline # skip suites that need models
pnpm eval --update # accept new numbers as the baseline
转录套件使用 macOS 文本转语音合成测试用例,因此其真实标注准确无误(eval/generate-audio.sh);该套件会按模型 × 词汇偏置 × LLM 校正报告 WER、实体召回率和实体误插入数。
打包测试用例的结果(43 次实体提及,在 CPU 上运行 Speaches,以 qwen2.5:14b 作为评判模型):
| model | vocab bias | LLM correction | WER | entity recall | false insertions |
|---|---|---|---|---|---|
| faster-whisper-small | no | no | 9.0% | 63% | 0 |
| faster-whisper-small | no | yes | 6.6% | 79% | 0 |
| faster-whisper-small | yes | no | 4.3% | 95% | 0 |
| faster-whisper-small | yes | yes | 4.3% | 95% | 0 |
| faster-whisper-medium | yes | no | 3.7% | 95% | 0 |
结论:词汇偏置是关键手段;更大的模型能略微改善 WER;校正流程则可兜底处理提示中未涵盖的名字(其余问题由审核界面捕获)。
无需 AI 密钥进行测试
scripts/mock-ai.mjs 是一个精简的 OpenAI 兼容服务器(聊天补全 + 带词级时间戳的转录)。将应用指向它,即可离线运行整个流程:
OPENAI_BASE_URL=http://localhost:4545/v1 OPENAI_API_KEY=mock AI_MODEL=openai:mock TRANSCRIPTION_MODEL=openai:whisper-1
故障排除
先运行 docker compose exec app reelsaw status <projectId>;有关命令行、结构化日志、诊断和评估的信息,请参见 docs/TROUBLESHOOTING.md,其中还包括“设置未生效”检查清单。
路线图
参见 docs/RESEARCH.md,了解这些优先事项背后的竞品审查和研究评估。仍待完成的重点项目:
- 在强调词上加入推入式缩放。
- 活跃说话人:增加带标签的双向对话案例;如果同步性启发式方法的效果停滞,则采用学习型音视频模型(Light-ASD ONNX)。看不到人脸时,使用显著性回退方案。
- 直接发布到 YouTube Shorts / TikTok / Instagram。
许可证
Reelsaw 是依据 GNU Affero General Public License v3.0 发布的自由软件。你可以运行、修改并托管它;如果将修改版本作为网络服务提供,则必须以相同许可证公开你的修改。托管服务的附加功能(计费)位于 apps/server/src/ee 和 apps/web/src/ee,遵循 Reelsaw Enterprise License,且仅在 REELSAW_EE=true 时加载;参见 LICENSING.md。贡献需要 DCO 签署(git commit -s),参见 CONTRIBUTING.md。
版权所有 (C) 2026 osbytes
评论
一个开放协议,规定了 AI 应用如何以统一方式连接外部工具与数据源。客户端负责与模型对话,MCP 服务负责提供工具、资源和提示模板,两者通过标准消息通信,同一个服务可以被不同客户端复用。