비디오 번역 (HeyGen)
기존 비디오를 여러 언어로 번역하고 더빙하여, 립싱크와 자연스러운 음성 패턴을 유지합니다. 비디오 URL 또는 HeyGen 비디오 ID를 제공하기만 하면 되며, HeyGen에서 먼저 비디오를 생성할 필요가 없습니다.
인증
모든 요청에는 X-Api-Key 헤더가 필요합니다. HEYGEN_API_KEY 환경 변수를 설정하세요.
curl -X POST "https://api.heygen.com/v2/video_translate" \
-H "X-Api-Key: $HEYGEN_API_KEY" \
-H "Content-Type: application/json" \
-d '{"video_url": "https://example.com/video.mp4", "output_language": "es-ES"}'
기본 워크플로
- 비디오 URL 또는 HeyGen 비디오 ID 제공
POST /v2/video_translate호출 (대상 언어 포함)GET /v2/video_translate/{translate_id}로 상태가completed가 될 때까지 폴링- 반환된 URL에서 번역된 비디오 다운로드
번역 작업 생성하기
요청 필드
| 필드 | 타입 | 필수 | 설명 |
|---|---|---|---|
video_url | string | Y* | 번역할 비디오 URL (*또는 video_id) |
video_id | string | Y* | HeyGen 비디오 ID (*또는 video_url) |
output_language | string | Y | 대상 언어 코드 (예: "es-ES") |
title | string | 번역된 비디오 이름 | |
translate_audio_only | boolean | 오디오만 번역, 립싱크 없음 (더 빠름) | |
speaker_num | number | 비디오 내 화자 수 | |
callback_id | string | 웹훅 추적을 위한 사용자 정의 ID | |
callback_url | string | 완료 알림을 위한 URL |
video_url 또는 video_id 중 하나는 반드시 제공해야 합니다.
curl
curl -X POST "https://api.heygen.com/v2/video_translate" \
-H "X-Api-Key: $HEYGEN_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"video_url": "https://example.com/original-video.mp4",
"output_language": "es-ES",
"title": "스페인어 버전"
}'
TypeScript
interface VideoTranslateRequest {
video_url?: string;
video_id?: string;
output_language: string;
title?: string;
translate_audio_only?: boolean;
speaker_num?: number;
callback_id?: string;
callback_url?: string;
}
interface VideoTranslateResponse {
error: null | string;
data: {
video_translate_id: string;
};
}
async function translateVideo(config: VideoTranslateRequest): Promise<string> {
const response = await fetch("https://api.heygen.com/v2/video_translate", {
method: "POST",
headers: {
"X-Api-Key": process.env.HEYGEN_API_KEY!,
"Content-Type": "application/json",
},
body: JSON.stringify(config),
});
const json: VideoTranslateResponse = await response.json();
if (json.error) {
throw new Error(json.error);
}
return json.data.video_translate_id;
}
Python
import requests
import os
def translate_video(config: dict) -> str:
response = requests.post(
"https://api.heygen.com/v2/video_translate",
headers={
"X-Api-Key": os.environ["HEYGEN_API_KEY"],
"Content-Type": "application/json"
},
json=config
)
data = response.json()
if data.get("error"):
raise Exception(data["error"])
return data["data"]["video_translate_id"]
지원 언어
| 언어 | 코드 | 비고 |
|---|---|---|
| 영어 (미국) | en-US | 기본 소스 |
| 스페인어 (스페인) | es-ES | 유럽 스페인어 |
| 스페인어 (멕시코) | es-MX | 라틴 아메리카 |
| 프랑스어 | fr-FR | 표준 프랑스어 |
| 독일어 | de-DE | 표준 독일어 |
| 이탈리아어 | it-IT | 표준 이탈리아어 |
| 포르투갈어 (브라질) | pt-BR | 브라질 포르투갈어 |
| 일본어 | ja-JP | 표준 일본어 |
| 한국어 | ko-KR | 표준 한국어 |
| 중국어 (만다린) | zh-CN | 간체 중국어 |
| 힌디어 | hi-IN | 표준 힌디어 |
| 아랍어 | ar-SA | 현대 표준 아랍어 |
번역 옵션
기본 번역 (립싱크 포함)
const config = {
video_url: "https://example.com/original.mp4",
output_language: "es-ES",
title: "스페인어 번역",
};
오디오 전용 번역 (더 빠름, 립싱크 없음)
const config = {
video_url: "https://example.com/original.mp4",
output_language: "es-ES",
translate_audio_only: true,
};
다중 화자 비디오
const config = {
video_url: "https://example.com/interview.mp4",
output_language: "fr-FR",
speaker_num: 2,
};
고급 옵션 (v4 API)
번역을 더 세밀하게 제어하려면 다음을 사용하세요:
interface VideoTranslateV4Request {
input_video_id?: string;
google_url?: string;
output_languages: string[]; // 한 번 호출로 여러 언어
name: string;
srt_key?: string; // 사용자 정의 SRT 자막
instruction?: string;
vocabulary?: string[]; // 그대로 유지할 용어
brand_voice_id?: string;
speaker_num?: number;
keep_the_same_format?: boolean;
input_language?: string;
enable_video_stretching?: boolean;
disable_music_track?: boolean;
enable_speech_enhancement?: boolean;
srt_role?: "input" | "output";
translate_audio_only?: boolean;
}
여러 출력 언어
const config = {
input_video_id: "original_video_id",
output_languages: ["es-ES", "fr-FR", "de-DE"],
name: "다국어 번역",
};
사용자 정의 어휘 (특정 용어 보존)
const config = {
video_url: "https://example.com/product-demo.mp4",
output_language: "ja-JP",
vocabulary: ["SuperWidget", "Pro Max", "TechCorp"],
};
사용자 정의 SRT 자막
const config = {
video_url: "https://example.com/video.mp4",
output_language: "es-ES",
srt_key: "path/to/custom-subtitles.srt",
srt_role: "input",
};
번역 상태 확인
curl
curl -X GET "https://api.heygen.com/v2/video_translate/{translate_id}" \
-H "X-Api-Key: $HEYGEN_API_KEY"
TypeScript
interface TranslateStatusResponse {
error: null | string;
data: {
id: string;
status: "pending" | "processing" | "completed" | "failed";
video_url?: string;
message?: string;
};
}
async function getTranslateStatus(translateId: string): Promise<TranslateStatusResponse["data"]> {
const response = await fetch(
`https://api.heygen.com/v2/video_translate/${translateId}`,
{ headers: { "X-Api-Key": process.env.HEYGEN_API_KEY! } }
);
const json: TranslateStatusResponse = await response.json();
if (json.error) {
throw new Error(json.error);
}
return json.data;
}
완료 폴링
번역은 일반 비디오 생성보다 시간이 더 걸립니다. 최대 30분까지 허용하세요.
async function waitForTranslation(
translateId: string,
maxWaitMs = 1800000,
pollIntervalMs = 30000
): Promise<string> {
const startTime = Date.now();
while (Date.now() - startTime < maxWaitMs) {
const status = await getTranslateStatus(translateId);
switch (status.status) {
case "completed":
return status.video_url!;
case "failed":
throw new Error(status.message || "번역 실패");
default:
console.log(`상태: ${status.status}...`);
await new Promise((r) => setTimeout(r, pollIntervalMs));
}
}
throw new Error("번역 시간 초과");
}
전체 워크플로
async function translateAndDownload(
videoUrl: string,
targetLanguage: string
): Promise<string> {
console.log(`${targetLanguage}(으)로 번역 시작...`);
const translateId = await translateVideo({
video_url: videoUrl,
output_language: targetLanguage,
});
console.log(`번역 ID: ${translateId}`);
console.log("번역 처리 중...");
const translatedVideoUrl = await waitForTranslation(translateId);
console.log(`번역 완료: ${translatedVideoUrl}`);
return translatedVideoUrl;
}
const spanishVideo = await translateAndDownload(
"https://example.com/my-video.mp4",
"es-ES"
);
일괄 번역
여러 언어로 병렬 번역:
async function translateToMultipleLanguages(
sourceVideoUrl: string,
targetLanguages: string[]
): Promise<Record<string, string>> {
const results: Record<string, string> = {};
const translatePromises = targetLanguages.map(async (lang) => {
const translateId = await translateVideo({
video_url: sourceVideoUrl,
output_language: lang,
});
return { lang, translateId };
});
const translationJobs = await Promise.all(translatePromises);
for (const job of translationJobs) {
try {
const videoUrl = await waitForTranslation(job.translateId);
results[job.lang] = videoUrl;
} catch (error) {
results[job.lang] = `오류: ${error.message}`;
}
}
return results;
}
const translations = await translateToMultipleLanguages(
"https://example.com/original.mp4",
["es-ES", "fr-FR", "de-DE", "ja-JP"]
);
기능
- 립싱크 — 번역된 오디오에 맞춰 화자의 입술 움직임을 자동으로 조정합니다.
- 음성 복제 — 번역된 오디오가 원본 화자의 음성 특성과 일치합니다.
- 음악 트랙 제어 —
disable_music_track: true로 배경 음악을 선택적으로 제거할 수 있습니다. - 음성 강화 —
enable_speech_enhancement: true로 오디오 품질을 개선합니다.
모범 사례
- 소스 품질이 중요합니다 — 더 나은 결과를 위해 고품질 소스 비디오를 사용하세요.
- 명확한 오디오 — 명확한 음성이 포함된 비디오가 더 잘 번역됩니다.
- 단일 화자 — 단일 화자 콘텐츠에서 최상의 결과를 얻을 수 있습니다.
- 적절한 속도 — 매우 빠른 말하기는 품질에 영향을 줄 수 있습니다.
- 먼저 테스트하세요 — 긴 비디오를 번역하기 전에 짧은 클립으로 시도해 보세요.
- 추가 시간을 허용하세요 — 번역은 비디오 생성보다 시간이 더 걸립니다 (최대 30분).
오류 처리
일반적인 오류와 처리 방법:
async function safeTranslate(
videoUrl: string,
targetLanguage: string
): Promise<{ success: boolean; result?: string; error?: string }> {
try {
const url = await translateAndDownload(videoUrl, targetLanguage);
return { success: true, result: url };
} catch (error) {
if (error.message.includes("quota")) {
return { success: false, error: "크레딧이 부족합니다" };
}
if (error.message.includes("duration")) {
return { success: false, error: "비디오가 너무 깁니다" };
}
if (error.message.includes("format")) {
return { success: false, error: "지원되지 않는 비디오 형식입니다" };
}
return { success: false, error: error.message };
}
}