GET /channels/:channelId/episodes
認証不要。公開中のエピソードは誰でも取得可能。認証済みの場合、自分のチャンネルのエピソードは非公開でも取得可能。非公開含む全エピソードの管理は GET /me/channels/:channelId/episodes を使用。
クエリパラメータ:
| パラメータ | 型 | デフォルト | 説明 |
|---|---|---|---|
| sort | string | createdAt |
ソート対象: createdAt / updatedAt |
| order | string | asc |
ソート順: asc(昇順) / desc(降順) |
| limit | int | 20 | 取得件数(最大 100) |
| offset | int | 0 | オフセット |
レスポンス:
{
"data": [
{
"id": "uuid",
"owner": { "id": "uuid", "username": "testuser", "displayName": "テストユーザー", "avatar": null },
"title": "エピソードタイトル",
"description": "エピソードの説明",
"fullAudio": { "id": "uuid", "url": "...", "durationMs": 180000 },
"scriptLineCount": 12,
"playCount": 123,
"publishedAt": "2025-01-01T00:00:00Z",
"createdAt": "2025-01-01T00:00:00Z",
"updatedAt": "2025-01-01T00:00:00Z"
}
],
"pagination": {
"total": 100,
"limit": 20,
"offset": 0
}
}GET /channels/:channelId/episodes/:episodeId
認証不要。公開中のエピソードは誰でも取得可能。認証済みの場合、自分のチャンネルのエピソードは非公開でも取得可能。
レスポンス:
{
"data": {
"id": "uuid",
"owner": { "id": "uuid", "username": "testuser", "displayName": "テストユーザー", "avatar": null },
"title": "エピソードタイトル",
"description": "エピソードの説明",
"bgm": { "id": "uuid", "url": "..." },
"voiceAudio": { "id": "uuid", "url": "...", "durationMs": 120000 },
"fullAudio": { "id": "uuid", "url": "..." },
"scriptLines": [
{
"id": "uuid",
"lineOrder": 0,
"speaker": {
"id": "uuid",
"name": "太郎",
"persona": "明るい青年",
"voice": { "id": "uuid", "name": "ボイス名", "provider": "google", "gender": "male" }
},
"text": "こんにちは",
"emotion": null,
"createdAt": "2025-01-01T00:00:00Z",
"updatedAt": "2025-01-01T00:00:00Z"
},
{
"id": "uuid",
"lineOrder": 1,
"speaker": {
"id": "uuid",
"name": "花子",
"persona": "元気な女の子",
"voice": { "id": "uuid", "name": "ボイス名", "provider": "google", "gender": "female" }
},
"text": "やあ、元気?",
"emotion": "excited",
"createdAt": "2025-01-01T00:00:00Z",
"updatedAt": "2025-01-01T00:00:00Z"
}
],
"scriptLineCount": 2,
"playback": {
"progressMs": 60000,
"completed": false,
"playedAt": "2025-01-01T00:00:00Z"
},
"playlistIds": ["uuid1", "uuid2"],
"playCount": 123,
"publishedAt": "2025-01-01T00:00:00Z",
"createdAt": "2025-01-01T00:00:00Z",
"updatedAt": "2025-01-01T00:00:00Z"
}
}Note:
voiceAudioはオーナーのみに表示されます。他ユーザーがアクセスした場合は含まれません。Note:
playbackは認証済みの場合のみ含まれます。未認証または再生履歴がない場合はnullになります。Note:
playlistIdsは認証済みの場合のみ含まれます。未認証の場合はnullになります。エピソードがどの再生リストにも含まれていない場合は空配列[]になります。Note: BGM の設定・削除は専用エンドポイント(エピソード BGM 設定 / エピソード BGM 削除)を使用してください。
POST /channels/:channelId/episodes
リクエスト:
{
"title": "エピソードタイトル",
"description": "エピソードの説明",
"artworkImageId": "uuid"
}バリデーション:
| フィールド | ルール |
|---|---|
| title | 必須、255文字以内 |
| description | 必須、2000文字以内 |
PATCH /channels/:channelId/episodes/:episodeId
リクエスト:
{
"title": "新しいタイトル",
"description": "新しい説明",
"artworkImageId": "uuid"
}バリデーション:
| フィールド | ルール |
|---|---|
| title | 255文字以内 |
| description | 2000文字以内 |
Note: 公開状態の変更は専用エンドポイント(エピソード公開 / エピソード非公開)を使用してください。
Note: BGM の変更は専用エンドポイント(エピソード BGM 設定 / エピソード BGM 削除)を使用してください。
DELETE /channels/:channelId/episodes/:episodeId
POST /channels/:channelId/episodes/:episodeId/publish
エピソードを公開状態にする。publishedAt を省略すると現在時刻で即時公開、指定すると予約公開になる。
リクエスト:
{
"publishedAt": "2025-01-01T00:00:00Z"
}| フィールド | 型 | 必須 | 説明 |
|---|---|---|---|
| publishedAt | string | 公開日時(RFC3339 形式)。省略時は現在時刻で即時公開 |
レスポンス(200 OK):
{
"data": {
"id": "uuid",
"title": "エピソードタイトル",
"description": "エピソードの説明",
"publishedAt": "2025-01-01T00:00:00Z",
"createdAt": "2025-01-01T00:00:00Z",
"updatedAt": "2025-01-01T00:00:00Z"
}
}POST /channels/:channelId/episodes/:episodeId/unpublish
エピソードを非公開(下書き)状態に戻す。
レスポンス(200 OK):
{
"data": {
"id": "uuid",
"title": "エピソードタイトル",
"description": "エピソードの説明",
"publishedAt": null,
"createdAt": "2025-01-01T00:00:00Z",
"updatedAt": "2025-01-01T00:00:00Z"
}
}PUT /channels/:channelId/episodes/:episodeId/bgm
エピソードに BGM を設定する。ユーザー BGM またはシステム BGM のどちらかを指定する。
リクエスト:
{
"bgmId": "uuid",
"systemBgmId": "uuid"
}バリデーション:
| フィールド | ルール |
|---|---|
| bgmId | UUID 形式、自分が所有する BGM のみ |
| systemBgmId | UUID 形式、is_active = true のシステム BGM のみ |
Note:
bgmIdとsystemBgmIdは同時に指定できません。いずれか一方を必ず指定してください。
レスポンス(200 OK):
{
"data": {
"id": "uuid",
"title": "エピソードタイトル",
"description": "エピソードの説明",
"bgm": {
"id": "uuid",
"name": "Chill BGM",
"isSystem": false,
"audio": {
"id": "uuid",
"url": "https://storage.example.com/audios/xxx.mp3?signature=...",
"durationMs": 180000
}
},
"createdAt": "2025-01-01T00:00:00Z",
"updatedAt": "2025-01-01T00:00:00Z"
}
}エラー(400 Bad Request):
{
"error": {
"code": "VALIDATION_ERROR",
"message": "bgmId と systemBgmId は同時に指定できません"
}
}エラー(403 Forbidden):
{
"error": {
"code": "FORBIDDEN",
"message": "このエピソードの BGM 設定権限がありません"
}
}DELETE /channels/:channelId/episodes/:episodeId/bgm
エピソードの BGM 設定を削除する。
レスポンス(200 OK):
{
"data": {
"id": "uuid",
"title": "エピソードタイトル",
"description": "エピソードの説明",
"bgm": null,
"createdAt": "2025-01-01T00:00:00Z",
"updatedAt": "2025-01-01T00:00:00Z"
}
}エラー(403 Forbidden):
{
"error": {
"code": "FORBIDDEN",
"message": "このエピソードの BGM 削除権限がありません"
}
}DELETE /channels/:channelId/episodes/:episodeId/audio
エピソードに紐づく音声(VoiceAudio / FullAudio)を削除する。対応する audios レコードと GCS ファイルも削除される。
レスポンス(204 No Content):
レスポンスボディなし。
エラー(403 Forbidden):
{
"error": {
"code": "FORBIDDEN",
"message": "このエピソードの音声削除権限がありません"
}
}エラー(404 Not Found):
{
"error": {
"code": "NOT_FOUND",
"message": "エピソードが見つかりません"
}
}Note: 音声が設定されていない場合でもエラーにはならず、204 を返します。
PUT /channels/:channelId/episodes/:episodeId/audio
ユーザーが録音・編集した音声ファイルを直接エピソードにアップロードする。アップロードされたファイルは voiceAudio と fullAudio の両方に設定される(remix で fullAudio のみ BGM 付きに差し替え可能)。
リクエスト: multipart/form-data
| フィールド | 型 | 必須 | 説明 |
|---|---|---|---|
| file | File | ◯ | アップロードする音声ファイル(mp3, wav, ogg, aac, m4a、最大 50MB) |
レスポンス(200 OK):
{
"data": {
"id": "uuid",
"title": "エピソードタイトル",
"description": "エピソードの説明",
"voiceAudio": {
"id": "uuid",
"url": "https://storage.example.com/voice-audio.mp3",
"mimeType": "audio/mpeg",
"fileSize": 1024000,
"durationMs": 180000
},
"fullAudio": {
"id": "uuid",
"url": "https://storage.example.com/full-audio.mp3",
"mimeType": "audio/mpeg",
"fileSize": 1024000,
"durationMs": 180000
},
"createdAt": "2025-01-01T00:00:00Z",
"updatedAt": "2025-01-01T00:00:00Z"
}
}エラー(400 Bad Request):
{
"error": {
"code": "VALIDATION_ERROR",
"message": "ファイルは必須です"
}
}エラー(403 Forbidden):
{
"error": {
"code": "FORBIDDEN",
"message": "このエピソードの音声アップロード権限がありません"
}
}エラー(404 Not Found):
{
"error": {
"code": "NOT_FOUND",
"message": "エピソードが見つかりません"
}
}Note: 既存の voiceAudio / fullAudio がある場合は上書きされ、古い音声の GCS ファイルはベストエフォートで削除されます。
GET /me/channels/:channelId/episodes
自分のチャンネルに紐付くエピソード一覧を取得(非公開含む)。編集画面での使用を想定。
パスパラメータ:
| パラメータ | 型 | 説明 |
|---|---|---|
| channelId | uuid | チャンネル ID |
クエリパラメータ:
| パラメータ | 型 | デフォルト | 説明 |
|---|---|---|---|
| status | string | - | 公開状態でフィルタ: published / draft |
| sort | string | createdAt |
ソート対象: createdAt / updatedAt |
| order | string | asc |
ソート順: asc(昇順) / desc(降順) |
| limit | int | 20 | 取得件数(最大 100) |
| offset | int | 0 | オフセット |
レスポンス:
{
"data": [
{
"id": "uuid",
"owner": { "id": "uuid", "username": "testuser", "displayName": "テストユーザー", "avatar": null },
"title": "エピソードタイトル",
"description": "エピソードの説明",
"fullAudio": { "id": "uuid", "url": "...", "durationMs": 180000 },
"scriptLineCount": 12,
"playCount": 123,
"publishedAt": "2025-01-01T00:00:00Z",
"createdAt": "2025-01-01T00:00:00Z",
"updatedAt": "2025-01-01T00:00:00Z"
}
],
"pagination": {
"total": 100,
"limit": 20,
"offset": 0
}
}エラー(403 Forbidden):
{
"error": {
"code": "FORBIDDEN",
"message": "このチャンネルへのアクセス権限がありません"
}
}エラー(404 Not Found):
{
"error": {
"code": "NOT_FOUND",
"message": "チャンネルが見つかりません"
}
}GET /me/channels/:channelId/episodes/:episodeId
自分のチャンネルに紐付くエピソードを取得(非公開含む)。編集画面での使用を想定。
パスパラメータ:
| パラメータ | 型 | 説明 |
|---|---|---|
| channelId | uuid | チャンネル ID |
| episodeId | uuid | エピソード ID |
レスポンス:
{
"data": {
"id": "uuid",
"owner": { "id": "uuid", "username": "testuser", "displayName": "テストユーザー", "avatar": null },
"title": "エピソードタイトル",
"description": "エピソードの説明",
"artwork": { "id": "uuid", "url": "..." },
"fullAudio": { "id": "uuid", "url": "...", "durationMs": 180000 },
"playback": {
"progressMs": 60000,
"completed": false,
"playedAt": "2025-01-01T00:00:00Z"
},
"playlistIds": ["uuid1", "uuid2"],
"playCount": 123,
"publishedAt": "2025-01-01T00:00:00Z",
"createdAt": "2025-01-01T00:00:00Z",
"updatedAt": "2025-01-01T00:00:00Z"
}
}Note:
playbackは認証済みの場合のみ含まれます。再生履歴がない場合はnullになります。Note:
playlistIdsは認証済みユーザーの場合のみ含まれます。エピソードがどの再生リストにも含まれていない場合は空配列[]になります。
エラー(403 Forbidden):
{
"error": {
"code": "FORBIDDEN",
"message": "このチャンネルへのアクセス権限がありません"
}
}エラー(404 Not Found):
{
"error": {
"code": "NOT_FOUND",
"message": "エピソードが見つかりません"
}
}POST /episodes/:episodeId/play
エピソードの再生回数をインクリメントする。クライアントは再生開始から 30 秒経過した時点でこの API を呼び出す。
パスパラメータ:
| パラメータ | 型 | 説明 |
|---|---|---|
| episodeId | uuid | エピソード ID |
レスポンス(204 No Content):
レスポンスボディなし。
エラー(404 Not Found):
{
"error": {
"code": "NOT_FOUND",
"message": "エピソードが見つかりません"
}
}Note: 公開中のエピソードのみカウント対象。同一ユーザーによる重複カウントを許容する(毎回 +1)。
Note: 音声生成 API の詳細仕様は docs/specs/audio-generate-async-api.md を参照してください。
typeパラメータでvoice(TTS のみ)、full(TTS + BGM)、remix(BGM 差し替え)を切り替えます。