ListenHubSDKs & CLI
JavaScript SDK

빠른 시작

SDK를 설치하고 API 키로 인증한 뒤, 팟캐스트를 생성하고 완료될 때까지 폴링해 오디오 URL을 읽어 보세요.

이 가이드는 @marswave/listenhub-sdk JavaScript 클라이언트를 사용해 빈 프로젝트에서 완성된 오디오 에피소드까지 가는 과정을 다룹니다. SDK를 설치하고, API 키로 OpenAPIClient를 생성하고, 팟캐스트를 시작하고, 작업이 성공할 때까지 폴링(polling)한 다음, 만들어진 오디오 URL을 읽습니다.

생성은 비동기로 처리됩니다. 모든 create 호출은 즉시 episodeId를 반환하며, 이후 processStatuspending에서 벗어날 때까지 get* 메서드를 폴링합니다. 전체 루프는 이 페이지 끝에 있으며 복사해서 바로 실행할 수 있습니다.

API 키가 필요합니다. listenhub.ai/settings/api-keys에서 발급받으세요. 키는 lh_sk_... 형태입니다. 키는 서버 측에만 두고, 브라우저나 모바일 코드에는 절대 포함하지 마세요. 사용자 대상 앱에서는 대신 OAuth를 쓰는 ListenHubClient를 사용하세요(인증 참고).

사전 준비

단계

SDK 설치

npm i @marswave/listenhub-sdk

API 키 설정

OpenAPIClient를 인자 없이 생성하면 환경 변수에서 LISTENHUB_API_KEY를 읽습니다. 셸에서 다음과 같이 내보내세요:

export LISTENHUB_API_KEY=lh_sk_...

키를 명시적으로 전달할 수도 있습니다: new OpenAPIClient({ apiKey: 'lh_sk_...' }). 시크릿이 소스 관리에 들어가지 않도록 환경 변수 방식을 권장합니다.

클라이언트 생성

import { OpenAPIClient } from '@marswave/listenhub-sdk';

const client = new OpenAPIClient(); // reads LISTENHUB_API_KEY

이 클라이언트는 https://api.marswave.ai/openapi를 대상으로 하며, 모든 요청에 Authorization: Bearer $LISTENHUB_API_KEY를 보냅니다. { code, message, data } 래퍼를 대신 벗겨 주고, 429 응답은 자동으로 재시도합니다. base URL, 타임아웃, 재시도 횟수를 재정의하려면 설정을 참고하세요.

스피커 선택

팟캐스트에는 최소 한 명의 스피커(speaker)가 필요합니다. 특정 언어에서 사용 가능한 보이스 목록을 조회해 speakerId를 가져오세요:

const { items: speakers } = await client.listSpeakers({ language: 'en' });
const host = speakers[0];
console.log(`Using speaker: ${host.name} (${host.speakerId})`);

팟캐스트 생성

createPodcast는 원하는 내용을 서술하는 query, 콘텐츠의 근거로 삼을 선택적 sources, 그리고 speakers 배열을 받습니다. 즉시 episodeId를 반환하며 생성은 백그라운드에서 진행됩니다.

const { episodeId } = await client.createPodcast({
  query: 'Explain how transformers work in large language models',
  sources: [
    {
      type: 'url',
      content: 'https://en.wikipedia.org/wiki/Transformer_(deep_learning_architecture)',
    },
  ],
  speakers: [{ speakerId: host.speakerId }],
  language: 'en',
});

console.log(`Created podcast: ${episodeId}`);

source{ type: 'text' | 'url', content } 형태입니다. 원문 텍스트를 그대로 넘기려면 type: 'text'를, 모델이 읽어야 할 웹 페이지를 가리키려면 type: 'url'을 사용하세요.

완료될 때까지 폴링

일정 간격으로 getPodcast(episodeId)를 호출합니다. processStatuspending으로 시작하며, 값이 바뀌면 에피소드가 성공했거나(audioUrl이 존재) 실패한 것입니다(failCodemessage를 확인하세요).

let detail = await client.getPodcast(episodeId);

while (detail.processStatus === 'pending') {
  await sleep(5000); // poll every 5 seconds
  detail = await client.getPodcast(episodeId);
  console.log(`Status: ${detail.processStatus}`);
}

오디오 URL 읽기

폴링이 끝나면 상세 정보에서 audioUrl을 읽습니다. 완성된 에피소드에는 title, outline, scripts도 함께 담겨 있습니다.

if (detail.audioUrl) {
  console.log(`Title: ${detail.title}`);
  console.log(`Audio: ${detail.audioUrl}`);
} else {
  console.error(`Generation failed (failCode ${detail.failCode}): ${detail.message}`);
}

전체 예제

복사해서 바로 실행할 수 있는 단일 스크립트입니다. 스피커를 조회하고, 팟캐스트를 생성하고, 작업이 pending에서 벗어날 때까지 폴링한 뒤, 오디오 URL을 출력하고 남은 크레딧을 보고합니다.

import { OpenAPIClient, ListenHubError } from '@marswave/listenhub-sdk';

const client = new OpenAPIClient(); // reads LISTENHUB_API_KEY

// 1. Pick a speaker.
const { items: speakers } = await client.listSpeakers({ language: 'en' });
const host = speakers[0];
console.log(`Using speaker: ${host.name}`);

// 2. Start the podcast (returns immediately).
const { episodeId } = await client.createPodcast({
  query: 'Explain how transformers work in large language models',
  sources: [
    {
      type: 'url',
      content: 'https://en.wikipedia.org/wiki/Transformer_(deep_learning_architecture)',
    },
  ],
  speakers: [{ speakerId: host.speakerId }],
  language: 'en',
});
console.log(`Created podcast: ${episodeId}`);

// 3. Poll until generation leaves "pending".
let detail = await client.getPodcast(episodeId);
while (detail.processStatus === 'pending') {
  await sleep(5000);
  detail = await client.getPodcast(episodeId);
  console.log(`Status: ${detail.processStatus}`);
}

// 4. Read the result.
if (detail.audioUrl) {
  console.log(`Title: ${detail.title}`);
  console.log(`Audio: ${detail.audioUrl}`);
} else {
  console.error(`Generation failed (failCode ${detail.failCode}): ${detail.message}`);
}

// 5. Check remaining credits.
const sub = await client.getSubscription();
console.log(`Credits remaining: ${sub.totalAvailableCredits}`);

function sleep(ms: number) {
  return new Promise((resolve) => setTimeout(resolve, ms));
}

팟캐스트 대신 flow speech 사용하기

Flow speech는 텍스트나 URL을 하나 이상의 보이스로 내레이션한 오디오로 바꿔 줍니다. 구조는 동일합니다. 생성한 뒤 get* 메서드를 폴링하며, 메서드 이름만 달라집니다. 진행자들이 대화하는 형식이 아니라 원문을 읽어 주는 형식이 필요할 때 사용하세요.

const { episodeId } = await client.createPodcast({
  query: 'Explain how transformers work in large language models',
  speakers: [{ speakerId: host.speakerId }],
  language: 'en',
});

let detail = await client.getPodcast(episodeId);
while (detail.processStatus === 'pending') {
  await sleep(5000);
  detail = await client.getPodcast(episodeId);
}
console.log(detail.audioUrl);
const { episodeId } = await client.createFlowSpeech({
  sources: [{ type: 'text', content: 'Hello world, this is ListenHub.' }],
  speakers: [{ speakerId: host.speakerId }],
  language: 'en',
});

let detail = await client.getFlowSpeech(episodeId);
while (detail.processStatus === 'pending') {
  await sleep(5000);
  detail = await client.getFlowSpeech(episodeId);
}
console.log(detail.audioUrl);

에러 처리

code가 0이 아니거나 HTTP 에러가 발생하면, 메서드는 status, code, requestId를 담은 ListenHubError를 던집니다. 이를 catch하면 API 에러와 그 밖의 실패를 구분할 수 있습니다:

import { ListenHubError } from '@marswave/listenhub-sdk';

try {
  await client.getPodcast('nonexistent-id');
} catch (err) {
  if (err instanceof ListenHubError) {
    console.error(`API error [${err.status}] code ${err.code} (request ${err.requestId})`);
  } else {
    throw err;
  }
}

생성 전에 비용 확인하기

크레딧 비용은 제품, 길이, 옵션에 따라 달라지므로 이 가이드에서는 구체적인 수치를 제시하지 않습니다. getSubscription()totalAvailableCredits 필드로 실시간 잔액을 확인하고, 비용이 큰 작업을 실행하기 전에는 해당 제품의 estimate-credits 엔드포인트(예: estimateVideoCredits)를 사용하세요. 어떤 제품이 추정 엔드포인트를 제공하는지는 OpenAPI 레퍼런스를 참고하세요.

다음 단계

이 페이지의 내용