/dubbing
Perso AI Dubbing API를 통해 비디오를 자동으로 더빙하는 스킬입니다.
핵심 규칙 (반드시 따라야 함)
- 작업자만 원시 키를 봅니다.
Read로 키를 열거나, 에코하거나, 명령줄 인수로 전달하지 마세요. 작업자는XP-API-KEY헤더에만 키를 보냅니다. - 하나의 명령 = 전체 작업.
scripts/dubbing.mjs는 업로드, 분할, 번역, 병합, 저장을 자체적으로 처리합니다. 절대로 그 전에prepare_input.mjs나probe_split.mjs를 실행하지 마세요. 이들은 디버그 도구이며,probe_split.mjs는 실제 업로드를 수행합니다(비디오가 두 번 업로드됩니다). - 항상
dubbing.mjs를 백그라운드에서 실행하세요. 작업은 몇 분에서 몇 시간이 걸리며, 포그라운드 셸 타임아웃이 실행을 중간에 중단시킵니다. - 언어가 지정되지 않음 → 소스
auto, 타겟en. - 여러 언어는 하나의 명령어로 처리 (
--target en,zh,ja) — 업로드/분할이 한 번 발생하고 언어별로 재사용됩니다. 언어별로 한 번씩 실행하지 마세요(매번 소스를 다시 업로드합니다). - 진행 상황을 충실히 전달하세요. 작업자의 표준 출력
[progress]줄을 그대로 또는 요약하여 채팅에 전달하고, 나타나지 않은 단계(분할/병합은 제한을 초과하는 비디오에만 존재)를 알리지 마세요. 들여쓰기된 표준 오류 세부 로그는 전달할 필요가 없습니다. - 실행 중에는 사용자에게 정보를 제공하세요. 더빙, 립싱크 및 오디오 분리는 모두
[status]하트비트(단계 + 항목 수, 예:ja 4/6 · en 1/6또는separating — file 2/5,next check ~Nm힌트 포함)를 출력하고, 완료된 각 항목은[progress]줄로 스트리밍됩니다. 시작 후 약 3분 후에 백그라운드 출력을 확인한 다음, 힌트된 간격(10분 이하 미디어는 10분, 더 긴 경우 30분)으로 새[progress]줄을 즉시 전달하고, 그렇지 않으면 최신[status]줄을 한 줄 업데이트로 게시하세요. 백분율, 경과 시간 또는 ETA를 절대 만들지 말고, 줄이 말하는 내용만 전달하세요. 사용자가 필요로 하는 중단([space-select],[split-confirm],[free-limit],[resume-check],[credit-check], 크레딧 부족, 완료)은 프로세스를 종료하므로 폴링 없이 표시됩니다. - 텔레메트리를 위해 런타임 이름을 지정하세요(자동). 작업자 명령에
--host <claude-code|codex|cursor|antigravity|gemini>를 추가하여, 실행 중인 에이전트를 명명합니다(알 수 있는 경우). 사용 태그 텔레메트리만 표시하며, 사용자에게 절대 표시하지 말고 질문하지 마세요. 확실하지 않으면 생략하세요. - 지원되지 않는 형식은 자동으로 건너뛰고 나머지는 계속 처리됩니다. 건너뛰기 알림을 전달하세요.
- 일시 중지는 실패가 아닙니다.
[space-select],[split-confirm],[free-limit],[credit-check]또는[resume-check]줄은 실행이 사용자에게 질문하기 위해 중단되었음을 의미합니다. 종료 코드는 0이지만(오류처럼 보이지 않음), 작업이 완료된 것은 아닙니다. 이러한 줄 중 하나가 있으면 실행이 완료되었다고 보고하지 말고, 전달하고 조치하세요. 신호는 종료 코드가 아니라 줄입니다.
설정 (지연 — 사전 단계 없음)
- API 키 — 먼저 실행하는 단계가 아닙니다. Perso API가 필요한 명령이 실행되는 순간 작업자가 스스로 게이트를 설정합니다. 첫 번째 실행 시 브라우저 페이지를 열거나(로그인, 한 번 클릭 — 키가 암호화되어 저장됨), 브라우저를 열 수 없으면 파일 기반 지침을 출력합니다. 사용자에게 전달하세요. 헤드리스/SSH 머신에서는 작업자가 브라우저 흐름을 완전히 건너뛰고 파일/
--import등록으로 시작합니다(브라우저 페이지는 실행 중인 머신에만 키를 전달할 수 있음). 동일한 방식으로 해당 지침을 전달하세요. 키 등록을 직접 트리거하지 마세요. 작업자는 필요할 때connect.mjs/resolve_key.mjs --watch를 실행하므로, 이를 실행하지 말고, 사전 단계로resolve_key.mjs --check를 실행하지 마세요(사용자가 키가 등록되었는지 명시적으로 묻는 경우에만 — 키가 없어도 오류가 아닙니다). 키를 채팅에 붙여넣지 마세요. 키 받기: https://developers.perso.ai/api-keys - ffmpeg/ffprobe — 비디오가 요금제 한도를 초과하여 분할해야 하는 경우에만 자동 설치됩니다(권한 요청 시 승인). 수동 확인:
node scripts/check_deps.mjs.
실행
입력(로컬 경로 또는 URL — 누락된 경우 다시 요청)을 수집하고 백그라운드에서 실행하세요. 작업자는 필요 시 처음으로 키 게이트를 자체 수행합니다. 등록이 시작되면 인쇄된 지침을 전달하기만 하면 됩니다.
- 단일:
node scripts/dubbing.mjs "<file|URL>" [--source auto] [--target en] [--space "space name"] [--out result.mp4] [--lipsync] [--no-save] - 다중 언어 / 다중 입력(하나 이상의 입력; URL과 파일 혼합 가능):
node scripts/dubbing.mjs "<URL>" "<file>" --target en,ja— 입력 × 언어당 하나의 출력, 각 소스 옆에 저장됨(--out <folder>는 수집). - 폴더(일괄):
node scripts/dubbing.mjs "<folder>" [--target en,zh] [--recursive] [--out output-folder]
언어 코드 — --target은 언어 코드(en, ja) 또는 지역 태그를 사용합니다. 지역 변형은 영어(en = 미국 기본값, en-GB = 영국), 포르투갈어(pt = 브라질 기본값, pt-PT = 포르투갈) 및 스페인어(es = 멕시코 기본값, es-ES = 스페인)에만 존재합니다. en-US, pt-BR 및 es-MX는 해당 기본값의 허용된 철자입니다. 다른 모든 언어는 단일 변형을 가지며, --source는 언어만 필요합니다(지역 태그는 일반 언어로 처리됨).
- 요청에 지역이 지정됨 → 해당 정확한 태그를 사용하세요. 변형이 없는 지역(예: 호주 영어) → 사용자에게 나열된 옵션만 존재한다고 알리고 실행 전에 선택하게 하세요.
- 두 글자 토큰은 언어 코드이지 국가가 아닙니다:
uk는 우크라이나어 — 영국 영어는en-GB입니다.ca(카탈루냐어),be(벨라루스어),eu(바스크어)에도 동일한 함정이 있습니다. 사용자의 표현이 둘 중 하나를 의미할 수 있는 경우, 실행 전에 어느 것을 의미하는지 물어보세요. - 지역 없이 지정된 언어 → 기본 변형. 해당 세 언어의 경우, 전달 노트에 적용된 기본값을 명명하세요(예: "Spanish (Mexico) — use
es-ESfor Spain").
저장 위치 — --out은 폴더(기존 디렉토리 또는 /로 끝나는 경로)를 사용하여 결과를 내부에 수집하거나, 단일 입력에 대한 파일 경로(경로에 확장자가 없으면 실제 확장자가 추가됨)를 사용합니다. 기본값은 각 소스 파일 옆입니다. 묻지 마세요. 세션의 첫 번째 작업에서 시작 줄에 명시하고(예: "Results will be saved next to the originals — tell me now if you'd like a different folder") 답변을 기다리지 않고 바로 시작하세요. 사용자가 폴더를 지정하면(이 요청 또는 이 세션의 이후 시점에) --out "<folder>"로 실행하고, 변경할 때까지 이후 모든 작업에 해당 폴더를 계속 사용하세요. 실행이 이미 시작된 후 사용자가 리디렉션하면 실행이 완료될 때 완료된 파일을 해당 폴더로 이동하세요. 예외: 모든 입력이 URL인 경우(로컬 소스 폴더 없음), 시작하기 전에 저장 위치를 물어보세요.
공간 선택 — 여러 워크스페이스가 있는 경우 작업자는 업로드 전에 중지하고 [space-select] 줄(이름 | (요금제) | 남은 크레딧)을 출력하고 중지합니다. 사용자에게 해당 옵션만 표시하고(내부 번호 없음), 어느 것을 원하는지 물어보고 --space "<space name>"으로 다시 실행하세요. 하나의 공간 → 질문 없음. PERSO_SPACE_SEQ가 고정합니다.
분할 확인 — 입력이 길이 또는 크기 제한을 초과하여 자동 분할 및 병합(더빙, 더빙+립싱크 또는 오디오 분리)이 필요한 경우, 작업자는 중지하고 [split-confirm] 줄을 출력합니다. 전달하고 사용자에게 물어보세요: 길이/크기 제한을 초과하므로 자동 분할 → 처리 → 병합이 필요하며, 직접 분할하는 것보다 덜 정교할 수 있습니다 — 자동으로 진행할까요? 예인 경우, 동일한 명령을 --allow-split과 함께 다시 실행하세요(이 시점까지 청구되는 것이 없으므로 재실행은 무료입니다). 일괄 실행: --allow-split은 실행의 모든 분할을 승인합니다.
저장 안 함(서버 전용) — 사용자가 비디오를 더빙하길 원하지만 로컬 파일로 저장하지 않으려는 경우 --no-save를 추가하세요. 작업자는 결과를 사용자의 Perso 워크스페이스에 남겨두고 다운로드를 건너뛰며, Kept on server, not saved: … → project <seq>를 출력합니다([project-ref] 줄은 여전히 출력되므로 더빙을 립싱크하거나 나중에 검색할 수 있습니다). 단일/분할되지 않은 비디오 전용 — 분할된 비디오의 병합된 파일은 로컬 다운로드가 필요하므로 정상적으로 저장되며 작업자가 그렇게 말합니다. --lipsync과 결합할 수 없습니다(립싱크된 비디오는 다운로드해야 함).
무료 요금제(결과는 Perso에 남음) — 무료 워크스페이스는 더빙, 립싱크 및 분리를 정상적으로 수행할 수 있지만 서버가 모든 다운로드를 거부하므로 작업자는 파일을 저장하지 않습니다. Free plan — <name>: … ready on Perso → <url> (downloads need a paid plan)를 출력합니다. 해당 링크를 전달된 결과로 전달하세요. 실행이 완료되었으며 실패한 것도, 재개할 것도 없습니다. 사용자가 실제 파일을 원한다면 유료 요금제가 필요합니다. 업그레이드 경로를 제공하고(요금제 업그레이드 및 크레딧 참조), 재실행하지 마세요.
무료 요금제 [free-limit] 일시 중지 — 두 가지 모두 청구 전에 발생합니다.
- 30초 미리보기. 30초보다 긴 더빙/립싱크 입력은
[free-limit]줄과 함께 중지됩니다. 무료 요금제에서는 처음 30초만 더빙되고 나머지는 제외됩니다. 질문을 전달하고, 예인 경우 동일한 명령을--allow-preview와 함께 다시 실행하세요(작업자가 처음 30초를 로컬에서 자르고 해당 부분만 제출합니다). Perso에서 가져온 플랫폼 링크는 자를 수 없습니다. 이 경우 답변은 로컬 파일, 더 짧은 클립 또는 업그레이드입니다. - 요금제 한도 초과. 요금제의 길이/크기 제한을 초과하는 미디어는 무료 요금제에서 분할 제안이 제공되지 않습니다(
--allow-split이 적용되지 않음).[free-limit]줄은 더 짧은 파일 또는 업그레이드만이 유일한 방법이라고 말합니다. 전달하고 중지하세요.
실행 중(대기 시간 설명용):
- 분할: 전체 파일이 먼저 업로드됩니다. 요금제 제한 거부 시에만 ffmpeg를 설치하고 무손실로 분할합니다. 외부 URL(YouTube·TikTok·Drive·Vimeo)은 서버 측에서 처리됩니다.
- 대기열: 모든 입력 × 부분 × 언어는 하나의 풀을 공유합니다. 전체 대기열은 5분마다 다시 확인됩니다. 부분의 엔진 오류는 해당 부분의 다른 언어를 취소합니다. 자동 부분은 원본을 통과시킵니다. 유휴 가드는 무한 대기를 방지합니다.
- 저장: 부분은 (입력 × 언어)당 하나의 파일로 다시 병합됩니다. 분할되지 않은 출력은 Perso 파일 이름을 유지합니다. 병합된 것은
<original-name>.dubbed.<lang>.<ext>입니다. 충돌 시_2,_3…이 추가됩니다.
립싱크
립싱크(입이 더빙된 오디오에 맞춰짐)는 더빙 후, 완성된 더빙 프로젝트에서 실행됩니다. 비디오 전용 — 오디오 입력은 거부됩니다. 오래 걸리는 작업입니다. 백그라운드에서 실행하고 사용자에게 더빙보다 상당히 오래 걸린다고 미리 알리세요. 크레딧(서버 청구가 권위적): 더빙 ≈ 초×1 · 립싱크 ≈ ×2 · 둘 다 ≈ ×3 — 지금 더빙 + 나중에 립싱크는 동시에 하는 것과 비용이 같습니다. 4K+ 소스: 프로/비즈니스/엔터프라이즈 요금제에서 모든 요율 ×3(예: 1분 4K 더빙+립싱크 ≈ 60×3×3 = 540) — 비디오가 4K인 경우 언급하세요.
존재하는 항목에 따라 흐름을 선택하세요.
- 새 비디오 + 립싱크 — 하나의 명령이 전체 체인(더빙 → 립싱크 → 저장)을 실행합니다. 두 단계 모두 청구된다고 경고하세요:
node scripts/dubbing.mjs "<file|URL>" --target en --lipsync[credit-check]줄이 출력되고 중지되면, 예상치가 남은 크레딧을 초과한 것입니다. 해당 줄(충전 URL 포함)을 표시하고, 사용자가 충전하거나 계속 진행을 승인한 후에만--force로 다시 실행하세요. - 이 세션에서 더빙됨 — 완료된 모든 실행은
[project-ref] {...}줄을 출력합니다. 보관하고 사용자에게 절대 표시하지 마세요. 재더빙 없이 립싱크(×2, 더빙 요금 없음):node scripts/dubbing.mjs --lipsync-only '<that [project-ref] JSON>' - 이 세션에 [project-ref] 없음 — 사용자가 Perso 포털에서 프로젝트 번호를 알고 있는지 물어보세요(
--lipsync-only <number>, ×2). 그렇지 않으면 비디오를 다시 더빙해야 합니다(--lipsync, ×3). 재더빙 전에 확인하세요.
규칙:
- 동일한 프로젝트에서 립싱크를 반복하면 다시 청구됩니다(서버 측 중복 제거 없음). 이 세션에서 이미 립싱크했다면 기존 파일을 가리키고 명시적 확인 후에만 다시 실행하세요.
- 립싱크가 실패하면 작업자가 대신 더빙된 비디오를 저장하고 최종 보고서에서 그렇게 말합니다. 이를 명확히 전달하세요. 더빙 크레딧이 낭비되지 않습니다.
- 더빙과 립싱크 사이에 크레딧이 소진됨: 더빙된 비디오가 저장되고 계속 진행하면 립싱크만 완료됩니다. 충전 URL을 전달하고, 결제 후
[resume-state]경로를 통해 계속하세요(중단 및 재개 참조).
스피커(더빙 프로젝트 전용)
이미 더빙된 프로젝트의 하나 이상의 문장에 새 스피커를 할당하려면 다음을 실행하세요.
node scripts/speaker.mjs "<project URL|projectSeq>" --at "5s,22s,98s" (쉼표로 구분된 시간 코드/범위)
node scripts/speaker.mjs "<project URL|projectSeq>" --text "first line|second line" (파이프로 구분, 인용된 텍스트)
node scripts/speaker.mjs "<project URL|projectSeq>" --sentence 1234,1288 ([sentence-select] 질문 후 재실행)
node scripts/speaker.mjs "<project URL|projectSeq>" --list (프로젝트의 문장을 검색하여 올바른 대상을 찾음)
더빙 프로젝트 전용 — 작업자는 자막(STT), 오디오 분리 및 립싱크 프로젝트와 미완료/실패한 프로젝트를 거부합니다. 프로젝트 링크에서 워크스페이스를 자체적으로 파악하므로, 어떤 공간인지 물을 필요가 없습니다.
일시 중지는 실패가 아닙니다. [sentence-select] 줄은 작업자가 단일 대상을 확정할 수 없음을 의미합니다(아무 것도 제공되지 않았거나, 일치하는 항목이 없거나, 둘 이상의 문장이 일치). 실패했기 때문에가 아니라 질문하기 위해 깔끔하게 종료됩니다. 그 아래의 번호 매기기 목록(인덱스, 시간 레이블, 텍스트)은 그대로 사용자에게 전달해도 안전합니다. 해당 목록 바로 뒤에 [sentence-ref] 줄은 동일한 번호를 작업자의 내부 문장 번호에 매핑하는 JSON 맵을 출력합니다. 해당 줄은 조회 전용입니다. 절대 표시하거나 읽어주지 마세요. 사용자가 번호를 선택하면 해당 맵에서 조회하고 동일한 명령을 --sentence로 해당 값으로 다시 실행하세요. 그와 함께 [speaker-resolve] 블록이 있다면 일부 대상이 이미 확인되었지만 아직 아무 것도 기록되지 않았음을 의미합니다. 자체 번호 매기기 목록과 자체 [sentence-ref] 줄이 있습니다. 재실행의 --sentence 목록에는 이미 확인된 값과 새로 선택한 값을 모두 포함해야 합니다. 그렇지 않으면 확인된 값이 삭제됩니다. 실행에서 둘 이상의 [sentence-select] 그룹(한 번에 여러 모호한 대상)을 출력하는 경우, 각 그룹
…
[번역을 위해 잘림]