Communitygithub.com

视频本地化:一条链接出硬字幕成片

用 Porter 把 YouTube、X、Instagram、TikTok 或 B 站链接变成本地化成片:获取、转录、翻译,再用 FFmpeg 压制双语或纯中文字幕,每个阶段都落盘保存。

视频本地化:一条链接出硬字幕成片 是什么?

「视频本地化」配方第 1 步(端到端流水线),来自 RolinShmily/porter-workflow。Porter 依次执行获取 → 转录 → 翻译 → 压制四个可恢复阶段,每步产物写入磁盘,中途失败不丢已完成部分。Agent 先跑 `plan`,判断视频是否有平台原生字幕轨(无需 ASR 和 Key),还是需要 Whisper 兼容转录;随后产出双语与纯中文 SRT,并用 libass 压制硬字幕 MP4。同一引擎提供 CLI、MCP 服务、Web 工作台和本 Agent Skill,Windows 有原生 PowerShell 入口。

兼容平台~Claude Code✓Codex CLI~Cursor
npx skills add https://github.com/RolinShmily/porter-workflow/tree/HEAD/skills/porter-skill

在你喜欢的 AI 中提问

打开一个已预加载此 Agent Skill 的新对话。

文档

Porter Skill (Streaming Media Porter)

Turn a video link into a finished video with Chinese hard subtitles. The workflow executes in four sequential stages: Fetch → Transcribe → Translate → Burn. Each stage writes its output to disk, so a failure mid-way won't lose completed work.


⚠️ Prerequisite: Transcription Needs a Key

You must confirm this first, otherwise the task will fail at the transcription stage.

CapabilityKey Required?
Download, Translate, BurnNo
Speech Transcription (ASR)Requires OPENAI_API_KEY (or WHISPER_API_KEY), or a locally installed VideoCaptioner CLI
Platform Native Subtitle Track (YouTube / Bilibili, etc.)No – if a native track exists, ASR is skipped

The v0.1 claim of a "pure Python zero-key closed loop" is no longer valid: all key-free speech recognition endpoints have been decommissioned (tested 2026-09-22; use the [asr-local] extra for key-free local recognition). Without a key, and if the video has no platform subtitle track, the task will end at the transcribe stage with every speech-to-text backend failed.

So: run plan first – it will tell you directly whether the video uses a native subtitle track or ASR, and whether it's feasible.


Calling the Entry Point

The CLI is invoked via uvx. On Windows PowerShell, use the native .ps1 script; do not assume Git Bash / WSL is available:

ScriptPurpose
scripts/porter.shBash main entry, forwards to the porter CLI.
scripts/porter.ps1Windows PowerShell main entry, also supports subcommands like inspect / plan / run / jobs.
scripts/inspect.shBash pre-check, equivalent to porter.sh inspect.
scripts/bootstrap.shAlternative entry to create a .venv from a repo checkout; does not install Python source code with the skill.
# Pre-check
<SKILL_DIR>/scripts/inspect.sh "https://youtu.be/dQw4w9WgXcQ"
# Plan
<SKILL_DIR>/scripts/porter.sh plan "https://youtu.be/dQw4w9WgXcQ"
# Execute
<SKILL_DIR>/scripts/porter.sh run "https://youtu.be/dQw4w9WgXcQ" -o ./porter_output

Windows PowerShell uses & '<SKILL_DIR>\scripts\porter.ps1' inspect '<URL>' --json; plan / run / jobs also call this same entry. The inspect.sh and the nohup/grep commands below are Bash examples and cannot be pasted directly into PowerShell.

uvx needs network access on first run to resolve dependencies. By default, only porter-workflow[llm] is installed (the base package includes yt-dlp); when local ASR is needed, set PORTER_SKILL_SPEC=porter-workflow[llm,asr-local] after user consent. For offline installation, prepare local dependencies inside the repo checkout, not by relying on the skill directory having source code.

On a host that only allows writing to a workspace, first confirm the allowed absolute directory, set PORTER_SKILL_STATE_DIR to a .state subdirectory within it, then run porter.sh --host-check or porter.ps1 --host-check; this will redirect uv, yt-dlp, and the job registry cache there (overriding any cache paths already set by the host). The output -o directory must also be within the allowed directory. When not set, default host paths are used. See references/HOSTS.md for commands, restrictions, and Windows examples.


Workflow Closed Loop

Execute in order. Each step is cheaper than the next, designed to weed out bad inputs before spending significant compute time.

1. Pre-check – inspect

<SKILL_DIR>/scripts/inspect.sh "<URL>" --json

Returns platform, title, duration, resolution, aspect ratio, and whether a subtitle track exists. Completes before downloading any media, in seconds.

Interpretation: is_valid indicates "the link is usable", while ok only means "the call succeeded". If the link is dead, is_valid=false – do not retry; retrying a dead link is pointless.

Exit code 1 means the link is not usable.

2. Plan – plan

<SKILL_DIR>/scripts/porter.sh plan "<URL>" --json

This is the most time-saving step – it answers "how will this video be processed, and can it be completed?":

  • subtitles.route: platform (use the platform's native subtitle track) or asr (run speech recognition)
  • translation.needed: false if a Chinese track already exists, allowing the entire translation phase to be skipped
  • feasible / blocking_issues: If false, stop – do not open a job
  • notes: Must read. The two most important notes:
    • The platform subtitle track is "requested, not guaranteed" – downloading may fail (platform rate limiting, HTTP 429) and fall back to speech recognition
    • If all available recognition backends are untested reverse-engineered endpoints, the note will explicitly state this

Exit code 1 means not feasible.

3. Confirm with the User

A job may download hundreds of MB and run for minutes. Report the plan conclusion to the user and get their consent before starting (which track will be used, which phases will run, whether to burn subtitles). If feasible=false, report the blocking issue and do not open a job.

4. Execute – run

<SKILL_DIR>/scripts/porter.sh run "<URL>" -o ./porter_output

For long videos, do not use a foreground call and wait for it to finish. Start it in the background, then poll the job registry:

nohup <SKILL_DIR>/scripts/porter.sh run "<URL>" -o ./porter_output > run.log 2>&1 &
# Poll (job records are written to a shared registry file, visible across processes)
<SKILL_DIR>/scripts/porter.sh jobs list
<SKILL_DIR>/scripts/porter.sh jobs status <job_id>

On PowerShell, use the host's background job tool to start & '<SKILL_DIR>\scripts\porter.ps1' run ... asynchronously, and continuously call jobs status using the same PORTER_SKILL_STATE_DIR. Do not use Out-String to accumulate progress from lengthy commands until they finish.

Do not use the approach of "set the tool timeout to 1200 seconds" to brute-force long tasks (as in v0.1). That still fails for longer videos, and you get no progress on failure. The job registry is designed exactly for this.

5. Quality Check (Mandatory)

Do not assume completion based solely on exit code. At minimum, verify:

# 1. Chinese subtitles exist and contain CJK characters (prevent "untranslated fake finished product")
grep -qP '[\x{4e00}-\x{9fff}]' ./porter_output/<task>/cooked/subtitle_zh.srt \
  && echo "OK: Contains Chinese" || echo "FAIL: No Chinese"

# 2. The finished video is decodable and the moov atom is intact (prevent publishing truncated files)
ffprobe -v error -show_entries format=duration -of csv=p=0 ./porter_output/<task>/cooked/video_zh.mp4

An unusually small finished file usually indicates a burn failure, not that the video is short.

6. On Error

<SKILL_DIR>/scripts/porter.sh doctor     # Check if this machine can work: ffmpeg/libass, JS runtime, fonts, encoder
<SKILL_DIR>/scripts/porter.sh config list # Display parsed configuration (keys are masked)

Common Parameters

# Download subtitles only, no burning
run "<URL>" --burn skip

# Output only a pure Chinese finished video (no bilingual)
run "<URL>" --burn zh_only

# Specify output directory / target language
run "<URL>" -o /path/to/out --target-lang zh-Hans

# Prioritize one backend (others remain as fallbacks, not "use only this one")
run "<URL>" --translator google
run "<URL>" --asr-engine whisper-api

# Use a different LLM model for this job (without changing config)
run "<URL>" --llm-model deepseek-reasoner

# Use an existing source subtitle file directly, skip recognition (.srt / .vtt)
run "<URL>" --subtitle-file ./my_subtitles.srt

# For videos that require login to view
run "<URL>" --cookies-from-browser chrome
run "<URL>" --cookies ./cookies.txt

# Rerun (ignore cached stage results)
run "<URL>" --force

# For script consumption
run "<URL>" --json

The semantics of --only-phase is "up to this phase", not "run only this phase" – each phase consumes the output of the previous one, so earlier phases still execute.


Output Structure

<task_dir>/
├── raw/                       # Master and intermediate files
│   ├── video.mp4              # Standardized master (lossless stream copy, no re-encoding)
│   ├── audio.wav              # Reference audio track
│   ├── audio_enhanced.wav     # Enhanced audio track for ASR only (denoising + vocal EQ), not used in the final video
│   ├── subtitle.srt           # Source language subtitle (platform track or ASR result)
│   ├── transcript.json/.txt   # Structured transcript
│   ├── cover.jpg
│   └── metadata.json
└── cooked/                    # Finished products
    ├── subtitle.srt / .ass    # Bilingual
    ├── subtitle_zh.srt / .ass # Pure Chinese
    ├── video_bilingual.mp4    # Bilingual hard-subtitled video
    └── video_zh.mp4           # Pure Chinese hard-subtitled video

The audio in the final video is directly copied (-c:a copy) from the original master; the enhanced audio track is only used for recognition – achieving both recognition accuracy and final video quality.


Configuration

Configuration search order (later overrides earlier):

  1. Built-in defaults
  2. Environment variables: OPENAI_API_KEY, OPENAI_BASE_URL, OPENAI_MODEL, WHISPER_API_KEY, PORTER_ASR_ENGINE
  3. User configuration (porter config list will print the actual path)
  4. Project configuration: porter.json / porter.toml / config.json in the current directory
  5. --config <path>

See references/CONFIG.md for the full key table and assets/config.example.json for a template.


Using via MCP (Optional, but Preferred)

If the host already has a porter MCP server configured, prefer it over shell:

  • Structured results, no need to parse stdout (also avoids shell escaping and encoding issues)
  • porter_job_start returns immediately + porter_job_status polling, long tasks won't hit tool timeouts
  • The only way to get "zero-key LLM-level translation": §8.3 sampling uses the host model for translation; the CLI process cannot access the host model

See references/MCP.md for tool name mapping and configuration.

Note: npx skills add only installs this skill's directory, not the MCP server. For a fresh install, use the CLI entry point.


Reference Files

FileContent
references/ARCHITECTURE.mdFour-stage pipeline, platform and backend fallback chain, design constraints
references/CONFIG.mdAll configuration keys, environment variables, priorities
references/MCP.mdMCP tool mapping and configuration
references/HOSTS.mdWindows native entry and restricted host pre-checks
assets/config.example.jsonConfiguration template

相关技能