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(串流媒體搬運工)

把一條影片連結變成帶中文硬字幕的成片。四個階段依序執行:獲取 → 轉錄 → 翻譯 → 壓制,每個階段把產物寫到磁碟,所以中途失敗不會丟掉已完成的部分。


⚠️ 前置條件:轉錄需要 Key

這一條必須先確認,否則任務會在轉錄階段失敗。

能力是否需要 Key
下載、翻譯、壓制不需要
語音轉錄(ASR)需要 OPENAI_API_KEY(或 WHISPER_API_KEY),或已安裝 VideoCaptioner CLI
平台原生字幕軌(YouTube / Bilibili 等)不需要——有原生軌就不跑 ASR

v0.1 宣稱的「純 Python 零 Key 閉環」已經失效:所有免 Key 的語音辨識端點都已停用(實測 2026-09-22;或使用 [asr-local] extra 做免 Key 的本機辨識)。沒有 Key 時,若影片也沒有平台字幕軌,任務會在 transcribe 階段以 every speech-to-text backend failed 結束。

所以:先跑 plan,它會直接告訴你這條影片走原生字幕軌還是走 ASR、以及是否可行。


呼叫入口

CLI 入口透過 uvx 執行。Windows PowerShell 請用原生 .ps1,不要假定 Git Bash / WSL 可用:

腳本用途
scripts/porter.shBash 主入口,轉發給 porter CLI。
scripts/porter.ps1Windows PowerShell 主入口,同樣支援 inspect / plan / run / jobs 等子命令。
scripts/inspect.shBash 預檢,等價於 porter.sh inspect。
scripts/bootstrap.sh從倉庫檢出建立 .venv 的備用入口;不隨技能安裝 Python 原始碼。
# 預檢
<SKILL_DIR>/scripts/inspect.sh "https://youtu.be/dQw4w9WgXcQ"
# 規劃
<SKILL_DIR>/scripts/porter.sh plan "https://youtu.be/dQw4w9WgXcQ"
# 執行
<SKILL_DIR>/scripts/porter.sh run "https://youtu.be/dQw4w9WgXcQ" -o ./porter_output

Windows PowerShell 使用 & '<SKILL_DIR>\scripts\porter.ps1' inspect '<URL>' --json,plan / run / jobs 也呼叫同一入口。inspect.sh 和下面的 nohup、grep 命令是 Bash 範例,不能原樣貼上到 PowerShell。

uvx 首次執行需要連網解析依賴。預設只安裝 porter-workflow[llm](基礎包已有 yt-dlp);需要本機 ASR 時,經使用者同意設定 PORTER_SKILL_SPEC=porter-workflow[llm,asr-local]。離線安裝請在倉庫檢出裡準備本機依賴,而不是指望技能目錄自帶原始碼。

在只能寫工作區的宿主,先確認獲准的絕對目錄,設定 PORTER_SKILL_STATE_DIR 為其中的 .state,執行 porter.sh --host-check 或 porter.ps1 --host-check;它會把 uv、yt-dlp 與作業註冊表快取重新導向進去(覆蓋宿主已設的快取路徑)。輸出 -o 也要位於獲准目錄。未設定時保留宿主預設路徑。命令、限制及 Windows 範例見 references/HOSTS.md。


工作流程閉環

按順序執行。每一步都比後一步便宜,目的是在花掉長時間計算之前先否掉壞輸入。

1. 預檢 —— inspect

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

回傳平台、標題、時長、解析度、畫幅、是否有字幕軌。下載任何媒體之前完成,幾秒鐘。

判讀:is_valid 才是「連結可用」,ok 只是「呼叫成功」。連結失效時 is_valid=false,此時不要重試——重試死結沒有意義。

退出碼 1 表示連結不可用。

2. 規劃 —— plan

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

這是最省時間的一步,它回答「這條影片會怎麼走、能不能走通」:

  • subtitles.route:platform(用平台自帶字幕軌)還是 asr(跑語音辨識)
  • translation.needed:已有中文軌時為 false,可整段跳過翻譯
  • feasible / blocking_issues:若為 false 就停下,別開作業
  • notes:必讀。兩條最重要:
    • 平台字幕軌是 「requested, not guaranteed」——抓取可能失敗(平台限流,HTTP 429)並回退到語音辨識
    • 若可用的辨識後端全是未經實測的逆向端點,note 會明說

退出碼 1 表示不可行。

3. 與使用者確認

一條作業可能下載幾百 MB、跑幾分鐘。把 plan 的結論報給使用者並取得同意再開跑(走哪條軌、跑哪些階段、是否壓制)。若 feasible=false,報 blocking issue,不要開作業。

4. 執行 —— run

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

長影片不要用前台呼叫等它跑完。 背景啟動,然後用作業註冊表輪詢:

nohup <SKILL_DIR>/scripts/porter.sh run "<URL>" -o ./porter_output > run.log 2>&1 &
# 輪詢(作業記錄寫在共享的註冊表檔案裡,跨行程可見)
<SKILL_DIR>/scripts/porter.sh jobs list
<SKILL_DIR>/scripts/porter.sh jobs status <job_id>

PowerShell 請用宿主提供的背景作業工具非同步啟動 & '<SKILL_DIR>\scripts\porter.ps1' run ...,並持續使用相同的 PORTER_SKILL_STATE_DIR 呼叫 jobs status。不要用 Out-String 把耗時命令的進度累積到結束才輸出。

不要用「把工具逾時設成 1200 秒」來硬扛長任務(v0.1 的做法)。那在影片更長時照樣失敗,而且失敗時你拿不到任何進度。作業註冊表就是為這個設計的。

5. 檢品(必做)

不要只看退出碼就說完成了。 至少確認:

# 1. 中文字幕確實存在且含 CJK(防止「未翻譯的假熟肉」)
grep -qP '[\x{4e00}-\x{9fff}]' ./porter_output/<task>/cooked/subtitle_zh.srt \
  && echo "OK: 含中文" || echo "FAIL: 無中文"

# 2. 成片可解碼且 moov atom 完好(防止發布截斷檔案)
ffprobe -v error -show_entries format=duration -of csv=p=0 ./porter_output/<task>/cooked/video_zh.mp4

成片大小異常小通常意味著壓制失敗,而不是影片短。

6. 報錯時

<SKILL_DIR>/scripts/porter.sh doctor     # 這台機器能不能幹活:ffmpeg/libass、JS 執行時期、字型、編碼器
<SKILL_DIR>/scripts/porter.sh config list # 解析後的設定(金鑰已打碼)

常用參數

# 只要字幕不壓制
run "<URL>" --burn skip

# 只要純中文成片(不生成雙語)
run "<URL>" --burn zh_only

# 指定輸出目錄 / 目標語言
run "<URL>" -o /path/to/out --target-lang zh-Hans

# 讓某個後端先跑(其餘仍作回退,不是「只用它」)
run "<URL>" --translator google
run "<URL>" --asr-engine whisper-api

# 本次作業換一個 LLM 模型(不改設定)
run "<URL>" --llm-model deepseek-reasoner

# 已有現成的來源字幕:直接用,不跑辨識(.srt / .vtt)
run "<URL>" --subtitle-file ./my_subtitles.srt

# 需要登入才能看的影片
run "<URL>" --cookies-from-browser chrome
run "<URL>" --cookies ./cookies.txt

# 重跑(忽略已快取的階段結果)
run "<URL>" --force

# 供腳本消費
run "<URL>" --json

--only-phase 的語義是 「到此階段為止」,不是「只跑這個階段」——每個階段要消費上一階段的輸出,所以更早的階段仍會執行。


產物結構

<task_dir>/
├── raw/                       # 母版與中間產物
│   ├── video.mp4              # 標準化母版(無損串流複製,不二次編碼)
│   ├── audio.wav              # 基準音軌
│   ├── audio_enhanced.wav     # ASR 專用增強音軌(降噪+人聲均衡),成片不用它
│   ├── subtitle.srt           # 來源語言字幕(平台軌或 ASR 結果)
│   ├── transcript.json/.txt   # 結構化台詞本
│   ├── cover.jpg
│   └── metadata.json
└── cooked/                    # 成品
    ├── subtitle.srt / .ass    # 雙語
    ├── subtitle_zh.srt / .ass # 純中文
    ├── video_bilingual.mp4    # 雙語硬字幕成片
    └── video_zh.mp4           # 純中文硬字幕成片

成片的音訊是直通複製(-c:a copy)原始母版,增強音軌只供辨識使用——辨識準確率與成片音質兼得。


設定

設定搜尋順序(後者覆蓋前者):

  1. 內建預設值
  2. 環境變數:OPENAI_API_KEY、OPENAI_BASE_URL、OPENAI_MODEL、WHISPER_API_KEY、PORTER_ASR_ENGINE
  3. 使用者設定(porter config list 會列印實際路徑)
  4. 專案設定:當前目錄的 porter.json / porter.toml / config.json
  5. --config <path>

完整鍵表見 references/CONFIG.md,模板見 assets/config.example.json。


透過 MCP 使用(可選,但更優)

如果宿主已設定 porter MCP server,優先用它而不是 shell:

  • 結構化結果,不必解析 stdout(也避開 shell 轉義與編碼問題)
  • porter_job_start 立即回傳 + porter_job_status 輪詢,長任務不會撞工具逾時
  • 唯一能拿到「零 Key 的 LLM 級翻譯」:§8.3 sampling 用宿主模型翻譯,CLI 行程拿不到宿主模型

工具名對照與設定方法見 references/MCP.md。

注意:npx skills add 只安裝本技能目錄,不會安裝 MCP server。全新安裝時以 CLI 路徑為準。


參考檔案

檔案內容
references/ARCHITECTURE.md四階段管線、平台與後端回退鏈、設計約束
references/CONFIG.md全部設定鍵、環境變數、優先順序
references/MCP.mdMCP 工具對照表與設定方法
references/HOSTS.mdWindows 原生入口與受限宿主預檢
assets/config.example.json設定模板

相關技能