Communitygithub.com

calesthio/OpenMontage

OpenMontage HeyGen 비디오 번역 스킬: /v2/video_translate API를 통해 립싱크를 유지하며 다국어 더빙을 제공합니다. 61,000★ 에이전트 비디오 스튜디오의 일부입니다.

OpenMontage란 무엇인가요?

OpenMontage는 HeyGen의 비디오 번역 API를 기반으로 한 AI 에이전트 스킬입니다. 기존 비디오의 URL이나 HeyGen 비디오 ID를 입력하면 여러 언어로 번역하고 더빙하며, 원본 음성의 립싱크를 자동으로 보정합니다. Claude Code, Cursor, Codex 등 다양한 AI 에이전트 플랫폼에서 사용할 수 있으며, 컬(curl) 명령어, TypeScript, Python 예제 코드를 통해 손쉽게 연동할 수 있습니다. 기본 워크플로는 비디오 제출 → 번역 작업 생성 → 상태 폴링 → 완료된 비디오 다운로드 순서로 진행되며, 최대 30분까지 소요될 수 있습니다. 여러 언어로 동시 번역(batch), 오디오만 번역(빠른 처리), 다중 화자 지원, 사용자 정의 어휘 보존, SRT 자막 커스터마이징 등 고급 옵션을 제공합니다. 또한, 음악 트랙 제어, 음성 강화, 브랜드 보이스 설정 등 v4 API의 다양한 고급 기능을 활용할 수 있습니다. 번역 품질은 소스 비디오의 화질과 음성 선명도에 크게 의존하므로, 고화질의 명확한 음성이 포함된 비디오를 권장합니다.

지원 대상~Claude Code~Codex CLI~Cursor✓Antigravity
npx skills add https://github.com/calesthio/OpenMontage/tree/main/.agents/skills/video-translate

즐겨 사용하는 AI에게 물어보기

이 에이전트 스킬이 미리 로드된 새 채팅을 엽니다.

문서

비디오 번역 (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"}'

기본 워크플로

  1. 비디오 URL 또는 HeyGen 비디오 ID 제공
  2. POST /v2/video_translate 호출 (대상 언어 포함)
  3. GET /v2/video_translate/{translate_id}로 상태가 completed가 될 때까지 폴링
  4. 반환된 URL에서 번역된 비디오 다운로드

번역 작업 생성하기

요청 필드

필드타입필수설명
video_urlstringY*번역할 비디오 URL (*또는 video_id)
video_idstringY*HeyGen 비디오 ID (*또는 video_url)
output_languagestringY대상 언어 코드 (예: "es-ES")
titlestring번역된 비디오 이름
translate_audio_onlyboolean오디오만 번역, 립싱크 없음 (더 빠름)
speaker_numnumber비디오 내 화자 수
callback_idstring웹훅 추적을 위한 사용자 정의 ID
callback_urlstring완료 알림을 위한 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로 오디오 품질을 개선합니다.

모범 사례

  1. 소스 품질이 중요합니다 — 더 나은 결과를 위해 고품질 소스 비디오를 사용하세요.
  2. 명확한 오디오 — 명확한 음성이 포함된 비디오가 더 잘 번역됩니다.
  3. 단일 화자 — 단일 화자 콘텐츠에서 최상의 결과를 얻을 수 있습니다.
  4. 적절한 속도 — 매우 빠른 말하기는 품질에 영향을 줄 수 있습니다.
  5. 먼저 테스트하세요 — 긴 비디오를 번역하기 전에 짧은 클립으로 시도해 보세요.
  6. 추가 시간을 허용하세요 — 번역은 비디오 생성보다 시간이 더 걸립니다 (최대 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 };
  }
}

Individual skills in this repo

This repo contains 1 individual skill — each has its own dedicated page.

관련 스킬