/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) — 업로드/분할은 한 번 수행되고 언어별로 재사용됩니다. 언어별로 한 번씩 실행하지 마십시오(매번 소스를 다시 업로드함). - 진행 상황을 충실히 전달하십시오. 작업자의 stdout
[progress]줄을 채팅에 그대로 또는 요약하여 전달하고, 나타나지 않은 단계(분할/병합은 제한을 초과하는 비디오에만 존재)를 알리지 마십시오. 들여쓰기된 stderr 세부 로그는 전달할 필요가 없습니다. - 실행 중에는 사용자에게 정보를 계속 제공하십시오. 더빙, 립싱크 및 오디오 분리는 모두
[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(바스크어)도 동일한 함정입니다. 사용자의 표현이 둘 중 하나를 의미할 수 있는 경우, 실행 전에 어느 것을 의미하는지 물어보십시오. - 지역 없이 이름이 지정된 언어 → 기본 변형입니다. 해당 세 언어의 경우, 전달 노트에 적용된 기본값을 명명하십시오(예: "스페인어(멕시코) — 스페인어의 경우
es-ES사용").
저장 위치 — --out은 폴더(기존 디렉토리 또는 /로 끝나는 경로 — 결과를 내부에 수집) 또는 파일 경로(단일 입력용 — 경로에 확장자가 없으면 실제 확장자가 추가됨)를 사용합니다. 기본값은 각 소스 파일 옆입니다. 묻지 마십시오. 세션의 첫 번째 작업에서 시작 줄에 명시하고(예: "결과는 원본 옆에 저장됩니다 — 다른 폴더를 원하면 지금 알려주세요") 즉시 시작하며, 답변을 기다리지 않습니다. 사용자가 폴더를 지정하면 — 이 요청에서든 이 세션의 나중에든 — --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] 그룹(한 번에 여러 모호한 대상)을 인쇄하는 경우 ...
[번역을 위해 잘림]