{
  "openapi": "3.0.3",
  "info": {
    "title": "音乐播放 API",
    "version": "1.3.0",
    "description": "音乐搜索、推荐、歌单解析、歌词及异步播放/下载 API。当前应用接口无需 API Key 或浏览器 Session；不要复制浏览器的 sec-ch-ua、Referer 等请求头。返回的 URL 可为相对路径或绝对 URL，用 https://music.op.wiki 作为解析基址，保留全部查询参数。应用分流已于 2026-10-20 00:04:35（UTC+8）发布：仅 completed 普通音频及 ALAC packet 使用 https://music.zr.wiki（Cloudflare），页面、API、歌词、封面和生成中音频保持 https://music.op.wiki。music.op.wiki 的入口方案为中国大陆走 AxisNow、海外走 Cloudflare，DNS 配置尚待迁移侧完成；应用配置保持不变，本次应用发布未修改 DNS/CDN。同源配置仍受支持；始终以 API 实际返回的 URL 为准。音频下载为匿名请求，不转发 Cookie 或 Authorization。普通下载流程：搜索歌曲 → POST /api/v1/playbacks（省略 streaming_mode）→ 依据返回的 status_url 轮询至 completed → GET audio_url 保存二进制文件；无需 WASM 解码器。任务 status=failed 时读取 error 并停止轮询。推荐是服务端维护的共享推荐来源，不代表调用者个人账号。框架参数解析失败可能返回 text/plain（400/415/422），请先检查 HTTP 状态与 Content-Type，再决定是否解析 JSON。文档更新于 2026-10-20；实际媒体地址取决于服务端配置。"
  },
  "servers": [
    {
      "url": "https://music.op.wiki",
      "description": "API 基础地址；动态媒体与未配置独立媒体域的完成文件使用此域"
    },
    {
      "url": "http://127.0.0.1:8000",
      "description": "自部署默认监听地址（按实际配置调整）"
    }
  ],
  "tags": [
    {
      "name": "系统",
      "description": "健康状态与支持平台"
    },
    {
      "name": "音乐",
      "description": "搜索、推荐、歌单、歌词与播放下载"
    }
  ],
  "paths": {
    "/health": {
      "get": {
        "tags": [
          "系统"
        ],
        "summary": "健康检查",
        "operationId": "getHealth",
        "responses": {
          "200": {
            "description": "服务可用",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HealthResponse"
                },
                "example": {
                  "status": "ok",
                  "service": "music-api"
                }
              }
            },
            "headers": {
              "Cache-Control": {
                "description": "动态、未就绪及错误响应禁止缓存。",
                "schema": {
                  "type": "string",
                  "example": "no-store"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/platforms": {
      "get": {
        "tags": [
          "音乐"
        ],
        "summary": "获取支持的平台列表",
        "operationId": "listPlatforms",
        "responses": {
          "200": {
            "description": "支持的平台列表",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/PlatformInfo"
                  }
                },
                "example": [
                  {
                    "id": "applemusic",
                    "label": "Apple Music",
                    "aliases": [
                      "am",
                      "apple",
                      "applemusic",
                      "apple-music",
                      "apple_music",
                      "苹果音乐"
                    ]
                  },
                  {
                    "id": "tencent",
                    "label": "QQ音乐",
                    "aliases": [
                      "qq",
                      "qqmusic",
                      "tencent",
                      "tx",
                      "qq音乐",
                      "腾讯",
                      "腾讯音乐"
                    ]
                  }
                ]
              }
            },
            "headers": {
              "Cache-Control": {
                "description": "动态、未就绪及错误响应禁止缓存。",
                "schema": {
                  "type": "string",
                  "example": "no-store"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/search": {
      "get": {
        "tags": [
          "音乐"
        ],
        "summary": "搜索音乐",
        "operationId": "searchMusic",
        "parameters": [
          {
            "name": "platform",
            "in": "query",
            "required": true,
            "description": "平台 ID 或别名。支持 applemusic、bilibili、kugou、netease、soda、tencent、youtubemusic；完整别名见 /api/v1/platforms。",
            "schema": {
              "type": "string",
              "example": "tencent"
            }
          },
          {
            "name": "q",
            "in": "query",
            "required": true,
            "description": "搜索关键词；部分平台也接受来源链接或平台内 ID。请用 URL 编码传递。",
            "schema": {
              "type": "string",
              "example": "晴天"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "请求返回数量；服务端将非负值钳制到 1..20。默认 10。实际结果可能少于此值。",
            "schema": {
              "type": "integer",
              "minimum": 0,
              "default": 10,
              "example": 10
            }
          }
        ],
        "responses": {
          "200": {
            "description": "搜索结果",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/SearchItem"
                  }
                },
                "example": [
                  {
                    "platform": "tencent",
                    "id": "0039MnYb0qxYhV",
                    "song": "晴天",
                    "singer": "周杰伦",
                    "cover": ""
                  }
                ]
              }
            },
            "headers": {
              "Cache-Control": {
                "description": "动态、未就绪及错误响应禁止缓存。",
                "schema": {
                  "type": "string",
                  "example": "no-store"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/api/v1/recommendations": {
      "get": {
        "tags": [
          "音乐"
        ],
        "summary": "获取推荐歌曲",
        "description": "读取服务端共享推荐来源，并从候选池重新采样。不会按调用者身份建立个人推荐，也不会回传收听记录。响应 Cache-Control: no-store；可使用 exclude 减少已展示内容。推荐资源可为空或少于 limit。",
        "operationId": "getRecommendations",
        "parameters": [
          {
            "name": "platform",
            "in": "query",
            "required": false,
            "description": "推荐来源平台，当前仅 applemusic。",
            "schema": {
              "type": "string",
              "default": "applemusic"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "推荐歌曲数量；服务端钳制到 1..50，默认 20。",
            "schema": {
              "type": "integer",
              "minimum": 0,
              "default": 20
            }
          },
          {
            "name": "refresh",
            "in": "query",
            "required": false,
            "description": "请求刷新候选池；受最短 30 秒刷新间隔约束，不保证每次都从上游重新获取。",
            "schema": {
              "type": "boolean",
              "default": false
            }
          },
          {
            "name": "exclude",
            "in": "query",
            "required": false,
            "description": "逗号分隔的已展示歌曲数字 ID。去重并忽略无效值，最多采用 500 个；ID 为 1..20 位数字且不能全为 0。",
            "schema": {
              "type": "string",
              "default": ""
            }
          }
        ],
        "responses": {
          "200": {
            "description": "推荐歌曲",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/SearchItem"
                  }
                }
              }
            },
            "headers": {
              "Cache-Control": {
                "description": "动态、未就绪及错误响应禁止缓存。",
                "schema": {
                  "type": "string",
                  "example": "no-store"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/api/v1/recommendations/home": {
      "get": {
        "tags": [
          "音乐"
        ],
        "summary": "获取 Apple Music 推荐首页分区",
        "description": "读取服务端共享推荐来源，并从候选池重新采样。不会按调用者身份建立个人推荐，也不会回传收听记录。响应 Cache-Control: no-store；可使用 exclude 减少已展示内容。推荐资源可为空或少于 limit。",
        "operationId": "getRecommendationHome",
        "parameters": [
          {
            "name": "platform",
            "in": "query",
            "required": false,
            "description": "推荐来源平台，当前仅 applemusic。",
            "schema": {
              "type": "string",
              "default": "applemusic"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "推荐歌曲数量；服务端钳制到 1..50，默认 20。",
            "schema": {
              "type": "integer",
              "minimum": 0,
              "default": 20
            }
          },
          {
            "name": "refresh",
            "in": "query",
            "required": false,
            "description": "请求刷新候选池；受最短 30 秒刷新间隔约束，不保证每次都从上游重新获取。",
            "schema": {
              "type": "boolean",
              "default": false
            }
          },
          {
            "name": "exclude",
            "in": "query",
            "required": false,
            "description": "逗号分隔的已展示歌曲数字 ID。去重并忽略无效值，最多采用 500 个；ID 为 1..20 位数字且不能全为 0。",
            "schema": {
              "type": "string",
              "default": ""
            }
          }
        ],
        "responses": {
          "200": {
            "description": "推荐首页分区。",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RecommendationHome"
                }
              }
            },
            "headers": {
              "Cache-Control": {
                "description": "动态、未就绪及错误响应禁止缓存。",
                "schema": {
                  "type": "string",
                  "example": "no-store"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/api/v1/recommendations/continue": {
      "post": {
        "tags": [
          "音乐"
        ],
        "summary": "获取队列续播候选",
        "description": "从种子歌曲获取相关候选，不足时用共享推荐池补充；排除种子和 exclude 中的歌曲。只返回元数据，不创建播放任务、不自动播放、不回传收听记录。候选耗尽时 tracks 可为空；上游失败且无候选时返回 503。",
        "operationId": "getContinuationRecommendations",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/RecommendationContinuationRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "续播候选，Cache-Control: no-store。",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RecommendationContinuation"
                }
              }
            },
            "headers": {
              "Cache-Control": {
                "description": "动态、未就绪及错误响应禁止缓存。",
                "schema": {
                  "type": "string",
                  "example": "no-store"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          },
          "503": {
            "$ref": "#/components/responses/ServiceUnavailable"
          },
          "415": {
            "$ref": "#/components/responses/UnsupportedMediaType"
          },
          "422": {
            "$ref": "#/components/responses/InvalidBody"
          },
          "413": {
            "$ref": "#/components/responses/PayloadTooLarge"
          }
        }
      },
      "get": {
        "tags": [
          "音乐"
        ],
        "summary": "获取队列续播候选（短查询）",
        "operationId": "getContinuationRecommendationsQuery",
        "parameters": [
          {
            "name": "platform",
            "in": "query",
            "required": false,
            "description": "当前仅 applemusic。",
            "schema": {
              "type": "string",
              "default": "applemusic"
            }
          },
          {
            "name": "seed_id",
            "in": "query",
            "required": true,
            "description": "",
            "schema": {
              "type": "string",
              "description": "作为推荐起点的 Apple Music 歌曲数字 ID；1..20 位且不能全为 0。",
              "pattern": "^(?!0+$)[0-9]{1,20}$",
              "example": "1579625893"
            }
          },
          {
            "name": "exclude",
            "in": "query",
            "required": false,
            "description": "逗号分隔歌曲数字 ID；去重、忽略无效值，最多采用 10000 个。长列表请使用 POST，避免 URL 长度限制。",
            "schema": {
              "type": "string",
              "default": ""
            }
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "服务端钳制到 1..50。",
            "schema": {
              "type": "integer",
              "minimum": 0,
              "default": 10
            }
          }
        ],
        "responses": {
          "200": {
            "description": "续播候选，Cache-Control: no-store。",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RecommendationContinuation"
                }
              }
            },
            "headers": {
              "Cache-Control": {
                "description": "动态、未就绪及错误响应禁止缓存。",
                "schema": {
                  "type": "string",
                  "example": "no-store"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          },
          "503": {
            "$ref": "#/components/responses/ServiceUnavailable"
          }
        },
        "description": "从种子歌曲获取相关候选，不足时用共享推荐池补充；排除种子和 exclude 中的歌曲。只返回元数据，不创建播放任务、不自动播放、不回传收听记录。候选耗尽时 tracks 可为空；上游失败且无候选时返回 503。"
      }
    },
    "/api/v1/collections/resolve": {
      "get": {
        "tags": [
          "音乐"
        ],
        "summary": "解析平台歌单",
        "description": "解析平台歌单、专辑或集合链接并返回歌曲清单。省略 limit 时完整解析，最多支持 5000 首，超限明确返回 400；显式 limit（1..5000）仅请求最多指定数量的预览，不保证完整。该接口不创建播放任务，也不直接暴露 Telegram music_cache。",
        "operationId": "resolveCollection",
        "parameters": [
          {
            "name": "platform",
            "in": "query",
            "required": true,
            "description": "平台 ID 或别名。",
            "schema": {
              "type": "string",
              "example": "applemusic"
            }
          },
          {
            "name": "url",
            "in": "query",
            "required": true,
            "description": "平台歌单、专辑或集合链接；部分平台也接受可识别的集合 ID，例如 Apple Music 的 pl.* 歌单 ID 或 album:数字ID。",
            "schema": {
              "type": "string",
              "example": "https://music.apple.com/cn/playlist/example/pl.u-example"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "省略时完整导入，最多支持 5000 首；超过时返回 400，避免静默截断。显式提供时返回最多指定数量的预览，不保证完整。",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 5000,
              "example": 200
            }
          }
        ],
        "responses": {
          "200": {
            "description": "歌单歌曲清单",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CollectionResponse"
                }
              }
            },
            "headers": {
              "Cache-Control": {
                "description": "动态、未就绪及错误响应禁止缓存。",
                "schema": {
                  "type": "string",
                  "example": "no-store"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/api/v1/collections/match": {
      "post": {
        "tags": [
          "歌单"
        ],
        "summary": "跨平台匹配歌单",
        "description": "把解析出的源平台歌曲匹配到目标平台。只有歌名、歌手和候选差距都可信时才标记为 matched；review 需要用户从候选中确认，missing 不会自动导入。 每个请求 items 必须为 1..200 首，同平台匹配也适用；空数组或超限返回 400。长歌单由客户端分批提交，响应数量和顺序与每批请求一致。",
        "operationId": "matchCollection",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/MatchCollectionRequest"
              },
              "example": {
                "source_platform": "netease",
                "target_platform": "applemusic",
                "items": [
                  {
                    "id": "494858498",
                    "song": "pure imagination",
                    "singer": "Rook1e / J'san",
                    "cover": ""
                  }
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "逐首匹配结果",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MatchCollectionResponse"
                }
              }
            },
            "headers": {
              "Cache-Control": {
                "description": "动态、未就绪及错误响应禁止缓存。",
                "schema": {
                  "type": "string",
                  "example": "no-store"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          },
          "415": {
            "$ref": "#/components/responses/UnsupportedMediaType"
          },
          "422": {
            "$ref": "#/components/responses/InvalidBody"
          },
          "413": {
            "$ref": "#/components/responses/PayloadTooLarge"
          }
        }
      }
    },
    "/api/v1/lyrics": {
      "get": {
        "tags": [
          "音乐"
        ],
        "summary": "获取歌词",
        "description": "按平台和曲目 ID 查询歌词。只传基础参数时查询 provider 原生歌词；传入歌曲元数据后，在原生歌词缺少原文或翻译、或原生请求失败时，按元数据从网易云、QQ、酷狗、酷我匹配并对齐翻译歌词。无歌词时返回 available=false。",
        "operationId": "getLyrics",
        "parameters": [
          {
            "name": "platform",
            "in": "query",
            "required": true,
            "description": "平台 ID 或别名。",
            "schema": {
              "type": "string",
              "example": "applemusic"
            }
          },
          {
            "name": "track_id",
            "in": "query",
            "required": true,
            "description": "provider 可识别的曲目 ID 或歌曲链接。",
            "schema": {
              "type": "string",
              "example": "905226305"
            }
          },
          {
            "name": "title",
            "in": "query",
            "required": false,
            "description": "歌曲标题。传入后启用多源歌词匹配。",
            "schema": {
              "type": "string",
              "example": "我怀念的"
            }
          },
          {
            "name": "artist",
            "in": "query",
            "required": false,
            "description": "歌手，用于候选歌词身份校验。",
            "schema": {
              "type": "string",
              "example": "孙燕姿"
            }
          },
          {
            "name": "album",
            "in": "query",
            "required": false,
            "description": "专辑，用于候选歌词身份校验。",
            "schema": {
              "type": "string",
              "example": "未完成"
            }
          },
          {
            "name": "duration",
            "in": "query",
            "required": false,
            "description": "歌曲时长，单位为秒，用于候选歌词身份校验。",
            "schema": {
              "type": "integer",
              "minimum": 0,
              "example": 267
            }
          }
        ],
        "responses": {
          "200": {
            "description": "歌词查询结果",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/LyricsResponse"
                },
                "example": {
                  "platform": "applemusic",
                  "track_id": "905226305",
                  "available": true,
                  "plain": "[00:00.00]我怀念的",
                  "translation": ""
                }
              }
            },
            "headers": {
              "Cache-Control": {
                "description": "动态、未就绪及错误响应禁止缓存。",
                "schema": {
                  "type": "string",
                  "example": "no-store"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/api/v1/playbacks": {
      "post": {
        "tags": [
          "音乐"
        ],
        "summary": "创建播放任务",
        "operationId": "createPlayback",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreatePlaybackRequest"
              },
              "examples": {
                "downloadById": {
                  "summary": "下载已知 ID 的 Hi-Res 歌曲（无需 WASM）",
                  "value": {
                    "platform": "applemusic",
                    "query": "1579625893",
                    "selected_id": "1579625893",
                    "quality": "hires",
                    "include_cover": true
                  }
                },
                "downloadBySearch": {
                  "summary": "按搜索词解析默认高音质歌曲",
                  "value": {
                    "platform": "applemusic",
                    "query": "示例歌名 示例歌手"
                  }
                },
                "advancedAlacStream": {
                  "summary": "高级客户端显式请求 ALAC 分包流",
                  "value": {
                    "platform": "applemusic",
                    "query": "1579625893",
                    "selected_id": "1579625893",
                    "quality": "lossless",
                    "streaming_mode": "wasm_alac"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "任务已经 completed，包含缓存命中或复用已完成任务。",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CreatePlaybackResponse"
                }
              }
            },
            "headers": {
              "Cache-Control": {
                "description": "动态、未就绪及错误响应禁止缓存。",
                "schema": {
                  "type": "string",
                  "example": "no-store"
                }
              }
            }
          },
          "202": {
            "description": "任务已接受，状态可能是 queued、processing 或 streaming。请使用 status_url 查询。",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CreatePlaybackResponse"
                }
              }
            },
            "headers": {
              "Cache-Control": {
                "description": "动态、未就绪及错误响应禁止缓存。",
                "schema": {
                  "type": "string",
                  "example": "no-store"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          },
          "415": {
            "$ref": "#/components/responses/UnsupportedMediaType"
          },
          "422": {
            "$ref": "#/components/responses/InvalidBody"
          },
          "413": {
            "$ref": "#/components/responses/PayloadTooLarge"
          }
        },
        "description": "创建或复用共享播放任务。歌曲和音质相同时复用同一任务；include_cover 和 streaming_mode 不形成独立任务。200 表示已完成（可直接下载），202 表示仍在排队/准备/流式生成。优先通过搜索结果的 id 传 selected_id，query 仍必填。普通下载省略 streaming_mode，轮询 status_url 至 completed 后 GET audio_url。仅 GET 任务状态不会再次下载歌曲。不要持续重复 POST 代替轮询。"
      }
    },
    "/api/v1/playbacks/{id}": {
      "get": {
        "tags": [
          "音乐"
        ],
        "summary": "获取播放任务详情",
        "operationId": "getPlayback",
        "parameters": [
          {
            "$ref": "#/components/parameters/PlaybackId"
          }
        ],
        "responses": {
          "200": {
            "description": "播放任务",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PlaybackTask"
                }
              }
            },
            "headers": {
              "Cache-Control": {
                "description": "动态、未就绪及错误响应禁止缓存。",
                "schema": {
                  "type": "string",
                  "example": "no-store"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        },
        "description": "查询现有任务状态，不创建下载。推荐间隔约 1 秒，准备较久可渐进退避；这只是客户端建议，不是服务端轮询配额。completed 后下载，failed 后停止轮询；重启后尚未持久缓存的任务可能返回 404，可重新创建。"
      }
    },
    "/api/v1/playbacks/{id}/audio": {
      "get": {
        "tags": [
          "音乐"
        ],
        "summary": "播放或下载标准音频文件",
        "description": "普通下载使用此接口，建议等待任务 completed 后请求。返回可保存的音频二进制，无需 WASM。无损源可能按兼容性返回 FLAC，保留无损音频；其格式/长度可与任务中的源元数据不同，以实际响应头为准，不要固定命名为 .mp3 或 .m4a。即使任务 completed，首次读取仍可能准备兼容文件或取回缓存，请允许合理的连接/读取超时。部分原生流在 streaming 阶段可读取；wasm_alac 任务在该阶段访问本接口返回 409。 媒体地址由 API 返回；配置独立媒体域时仅 completed 使用媒体域绝对 URL，queued/processing/streaming 保持 API 域相对路径；同源配置仍受支持。不要自行构造或删除 rev 查询参数。完整成功媒体 public,max-age=86400,no-transform；动态流与错误响应 no-store。匿名访问，保留 Range、身份与长度校验；跨域媒体还须保留 CORS。",
        "operationId": "getPlaybackAudio",
        "parameters": [
          {
            "$ref": "#/components/parameters/PlaybackId"
          },
          {
            "$ref": "#/components/parameters/Range"
          },
          {
            "$ref": "#/components/parameters/MediaRevision"
          }
        ],
        "responses": {
          "200": {
            "description": "完整响应或正在生成的响应流。",
            "headers": {
              "Content-Disposition": {
                "description": "inline 文件名；保存文件时可采用该文件名，扩展名应与实际 Content-Type 一致。",
                "schema": {
                  "type": "string",
                  "example": "inline; filename=\"audio.flac\""
                }
              },
              "Content-Length": {
                "description": "响应体字节数（有此头时）；206 是当前区间长度，生成中的流无固定长度。",
                "schema": {
                  "type": "integer",
                  "minimum": 0
                }
              },
              "Accept-Ranges": {
                "description": "本地完整文件为 bytes；生成中的流为 none，远端路径可能省略。",
                "schema": {
                  "type": "string"
                }
              },
              "X-Audio-Stream-Id": {
                "description": "可选的不透明文件实例标识，供判断当前流是否还是同一文件；不是歌曲 ID。",
                "schema": {
                  "type": "string"
                }
              },
              "Cache-Control": {
                "description": "完成成功媒体允许缓存 24 小时且不得转换；200 生成中的流为 no-store, no-transform。",
                "schema": {
                  "type": "string",
                  "example": "public, max-age=86400, no-transform"
                }
              },
              "Content-Encoding": {
                "description": "音频字节不应被 gzip/br 等转换；应省略此头或为 identity。Range 接管须验证。",
                "schema": {
                  "type": "string",
                  "example": "identity"
                }
              }
            },
            "content": {
              "audio/mp4": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              },
              "audio/mpeg": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              },
              "audio/flac": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              },
              "audio/ogg": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              },
              "audio/webm": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              },
              "application/octet-stream": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              }
            }
          },
          "206": {
            "description": "单段字节范围响应，请核对 Content-Range。",
            "headers": {
              "Content-Disposition": {
                "description": "inline 文件名；保存文件时可采用该文件名，扩展名应与实际 Content-Type 一致。",
                "schema": {
                  "type": "string",
                  "example": "inline; filename=\"audio.flac\""
                }
              },
              "Content-Length": {
                "description": "响应体字节数（有此头时）；206 是当前区间长度，生成中的流无固定长度。",
                "schema": {
                  "type": "integer",
                  "minimum": 0
                }
              },
              "Accept-Ranges": {
                "description": "本地完整文件为 bytes；生成中的流为 none，远端路径可能省略。",
                "schema": {
                  "type": "string"
                }
              },
              "X-Audio-Stream-Id": {
                "description": "可选的不透明文件实例标识，供判断当前流是否还是同一文件；不是歌曲 ID。",
                "schema": {
                  "type": "string"
                }
              },
              "Content-Range": {
                "schema": {
                  "type": "string",
                  "example": "bytes 0-1023/100000"
                }
              },
              "Cache-Control": {
                "description": "完成成功媒体允许缓存 24 小时且不得转换；200 生成中的流为 no-store, no-transform。",
                "schema": {
                  "type": "string",
                  "example": "public, max-age=86400, no-transform"
                }
              },
              "Content-Encoding": {
                "description": "音频字节不应被 gzip/br 等转换；应省略此头或为 identity。Range 接管须验证。",
                "schema": {
                  "type": "string",
                  "example": "identity"
                }
              }
            },
            "content": {
              "audio/mp4": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              },
              "audio/mpeg": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              },
              "audio/flac": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              },
              "audio/ogg": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              },
              "audio/webm": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              },
              "application/octet-stream": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "416": {
            "$ref": "#/components/responses/RangeNotSatisfiable"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          },
          "502": {
            "$ref": "#/components/responses/BadGateway"
          }
        },
        "servers": [
          {
            "url": "https://music.op.wiki",
            "description": "生成中音频与同源配置的完成媒体"
          },
          {
            "url": "https://music.zr.wiki",
            "description": "已发布的 completed 媒体域；仅完整文件与 Range 下载，使用 API 实际返回的完整 URL"
          },
          {
            "url": "http://127.0.0.1:8000",
            "description": "自部署同源默认地址"
          }
        ]
      }
    },
    "/api/v1/playbacks/{id}/audio/stream": {
      "get": {
        "tags": [
          "音乐"
        ],
        "summary": "获取 WASM ALAC 分包流",
        "description": "高级接口：仅供明确实现 splayer-alac-v2 / SPLALAC1 协议的 ALAC 解码客户端使用。需要任务实际启用 wasm_alac；返回 application/x-splayer-alac，不能当作 M4A/FLAC 文件，也不能直接交给普通 audio 元素。streaming 阶段为持续增长流，无固定长度且不支持 Range；completed 后分包文件可支持单段 Range。普通下载请使用 /audio。 媒体地址由 API 返回；配置独立媒体域时仅 completed 使用媒体域绝对 URL，queued/processing/streaming 保持 API 域相对路径；同源配置仍受支持。不要自行构造或删除 rev 查询参数。完整成功媒体 public,max-age=86400,no-transform；动态流与错误响应 no-store。匿名访问，保留 Range、身份与长度校验；跨域媒体还须保留 CORS。",
        "operationId": "getPlaybackAlacStream",
        "parameters": [
          {
            "$ref": "#/components/parameters/PlaybackId"
          },
          {
            "$ref": "#/components/parameters/Range"
          },
          {
            "$ref": "#/components/parameters/MediaRevision"
          }
        ],
        "responses": {
          "200": {
            "description": "完整响应或正在生成的响应流。",
            "headers": {
              "Content-Disposition": {
                "description": "inline 文件名；保存文件时可采用该文件名，扩展名应与实际 Content-Type 一致。",
                "schema": {
                  "type": "string",
                  "example": "inline; filename=\"audio.alacstream\""
                }
              },
              "Content-Length": {
                "description": "响应体字节数（有此头时）；206 是当前区间长度，生成中的流无固定长度。",
                "schema": {
                  "type": "integer",
                  "minimum": 0
                }
              },
              "Accept-Ranges": {
                "description": "本地完整文件为 bytes；生成中的流为 none，远端路径可能省略。",
                "schema": {
                  "type": "string"
                }
              },
              "X-Audio-Stream-Id": {
                "description": "可选的不透明文件实例标识，供判断当前流是否还是同一文件；不是歌曲 ID。",
                "schema": {
                  "type": "string"
                }
              },
              "Cache-Control": {
                "description": "完成成功媒体允许缓存 24 小时且不得转换；200 生成中的流为 no-store, no-transform。",
                "schema": {
                  "type": "string",
                  "example": "public, max-age=86400, no-transform"
                }
              },
              "Content-Encoding": {
                "description": "音频字节不应被 gzip/br 等转换；应省略此头或为 identity。Range 接管须验证。",
                "schema": {
                  "type": "string",
                  "example": "identity"
                }
              }
            },
            "content": {
              "application/x-splayer-alac": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              }
            }
          },
          "206": {
            "description": "单段字节范围响应，请核对 Content-Range。",
            "headers": {
              "Content-Disposition": {
                "description": "inline 文件名；保存文件时可采用该文件名，扩展名应与实际 Content-Type 一致。",
                "schema": {
                  "type": "string",
                  "example": "inline; filename=\"audio.alacstream\""
                }
              },
              "Content-Length": {
                "description": "响应体字节数（有此头时）；206 是当前区间长度，生成中的流无固定长度。",
                "schema": {
                  "type": "integer",
                  "minimum": 0
                }
              },
              "Accept-Ranges": {
                "description": "本地完整文件为 bytes；生成中的流为 none，远端路径可能省略。",
                "schema": {
                  "type": "string"
                }
              },
              "X-Audio-Stream-Id": {
                "description": "可选的不透明文件实例标识，供判断当前流是否还是同一文件；不是歌曲 ID。",
                "schema": {
                  "type": "string"
                }
              },
              "Content-Range": {
                "schema": {
                  "type": "string",
                  "example": "bytes 0-1023/100000"
                }
              },
              "Cache-Control": {
                "description": "完成成功媒体允许缓存 24 小时且不得转换；200 生成中的流为 no-store, no-transform。",
                "schema": {
                  "type": "string",
                  "example": "public, max-age=86400, no-transform"
                }
              },
              "Content-Encoding": {
                "description": "音频字节不应被 gzip/br 等转换；应省略此头或为 identity。Range 接管须验证。",
                "schema": {
                  "type": "string",
                  "example": "identity"
                }
              }
            },
            "content": {
              "application/x-splayer-alac": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "416": {
            "$ref": "#/components/responses/RangeNotSatisfiable"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          },
          "502": {
            "$ref": "#/components/responses/BadGateway"
          },
          "406": {
            "$ref": "#/components/responses/NotAcceptable"
          }
        },
        "servers": [
          {
            "url": "https://music.op.wiki",
            "description": "生成中音频与同源配置的完成媒体"
          },
          {
            "url": "https://music.zr.wiki",
            "description": "已发布的 completed 媒体域；仅完整文件与 Range 下载，使用 API 实际返回的完整 URL"
          },
          {
            "url": "http://127.0.0.1:8000",
            "description": "自部署同源默认地址"
          }
        ]
      }
    },
    "/api/v1/playbacks/{id}/cover": {
      "get": {
        "tags": [
          "音乐"
        ],
        "summary": "获取播放封面",
        "operationId": "getPlaybackCover",
        "parameters": [
          {
            "$ref": "#/components/parameters/PlaybackId"
          },
          {
            "$ref": "#/components/parameters/Range"
          }
        ],
        "responses": {
          "200": {
            "description": "任务完成后的封面文件。",
            "headers": {
              "Content-Disposition": {
                "description": "inline 文件名；保存文件时可采用该文件名，扩展名应与实际 Content-Type 一致。",
                "schema": {
                  "type": "string",
                  "example": "inline; filename=\"cover.jpg\""
                }
              },
              "Content-Length": {
                "description": "响应体字节数（有此头时）；206 是当前区间长度。",
                "schema": {
                  "type": "integer",
                  "minimum": 0
                }
              },
              "Accept-Ranges": {
                "description": "本地完整文件为 bytes；远端路径可能省略。",
                "schema": {
                  "type": "string"
                }
              },
              "X-Audio-Stream-Id": {
                "description": "可选的不透明文件实例标识，供判断当前流是否还是同一文件；不是歌曲 ID。",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "image/jpeg": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              },
              "image/png": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              },
              "image/webp": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              },
              "application/octet-stream": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              }
            }
          },
          "206": {
            "description": "单段字节范围响应，请核对 Content-Range。",
            "headers": {
              "Content-Disposition": {
                "description": "inline 文件名；保存文件时可采用该文件名，扩展名应与实际 Content-Type 一致。",
                "schema": {
                  "type": "string",
                  "example": "inline; filename=\"cover.jpg\""
                }
              },
              "Content-Length": {
                "description": "响应体字节数（有此头时）；206 是当前区间长度。",
                "schema": {
                  "type": "integer",
                  "minimum": 0
                }
              },
              "Accept-Ranges": {
                "description": "本地完整文件为 bytes；远端路径可能省略。",
                "schema": {
                  "type": "string"
                }
              },
              "X-Audio-Stream-Id": {
                "description": "可选的不透明文件实例标识，供判断当前流是否还是同一文件；不是歌曲 ID。",
                "schema": {
                  "type": "string"
                }
              },
              "Content-Range": {
                "schema": {
                  "type": "string",
                  "example": "bytes 0-1023/100000"
                }
              }
            },
            "content": {
              "image/jpeg": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              },
              "image/png": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              },
              "image/webp": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              },
              "application/octet-stream": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "416": {
            "$ref": "#/components/responses/RangeNotSatisfiable"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          },
          "502": {
            "$ref": "#/components/responses/BadGateway"
          }
        },
        "description": "任务完成后读取封面二进制。封面可能不存在（404）；include_cover=false 或上游无封面时不保证可用。封面缺失不影响音频下载。"
      }
    },
    "/api/v1/playbacks/{id}/lyrics": {
      "get": {
        "tags": [
          "音乐"
        ],
        "summary": "获取播放任务歌词",
        "description": "根据播放任务解析出的 track_id 查询歌词；优先使用 provider 原生歌词；缺少原文、翻译或原生请求失败时，使用任务元数据从网易云、QQ、酷狗、酷我匹配并对齐翻译歌词。任务未完成时使用 selected_id 或 query 回退。无歌词时返回 available=false。",
        "operationId": "getPlaybackLyrics",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "稳定播放 ID（由 platform、track_id、quality 决定）。",
            "schema": {
              "type": "string",
              "example": "tencent--0039MnYb0qxYhV--high"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "播放任务歌词",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/LyricsResponse"
                }
              }
            },
            "headers": {
              "Cache-Control": {
                "description": "动态、未就绪及错误响应禁止缓存。",
                "schema": {
                  "type": "string",
                  "example": "no-store"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/api/v1/playbacks/{id}/loudness": {
      "get": {
        "tags": [
          "音乐"
        ],
        "summary": "获取播放响度分析",
        "description": "只测量服务器已存在的完整本地音频，不会为响度分析额外下载远端缓存。响应使用 Cache-Control: no-store；pending 时可稍后再查，unavailable 时跳过等响处理。仅返回分析值，不修改音频，也不阻塞普通下载。",
        "operationId": "getPlaybackLoudness",
        "parameters": [
          {
            "$ref": "#/components/parameters/PlaybackId"
          }
        ],
        "responses": {
          "200": {
            "description": "响度分析状态与可选测量值。",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/LoudnessResponse"
                }
              }
            },
            "headers": {
              "Cache-Control": {
                "description": "动态、未就绪及错误响应禁止缓存。",
                "schema": {
                  "type": "string",
                  "example": "no-store"
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    }
  },
  "components": {
    "parameters": {
      "PlaybackId": {
        "name": "id",
        "in": "path",
        "required": true,
        "description": "稳定播放 ID（由 platform、track_id、quality 决定）。",
        "schema": {
          "type": "string",
          "example": "tencent--0039MnYb0qxYhV--high"
        }
      },
      "Range": {
        "name": "Range",
        "in": "header",
        "required": false,
        "description": "完成文件支持单段字节 Range，例如 bytes=0-1023、bytes=1024-、bytes=-1024。生成中的流不处理 Range，返回 200 和 Accept-Ranges: none；远端缓存路径依赖上游 Range 能力，必须检查实际 206 和 Content-Range。",
        "schema": {
          "type": "string",
          "example": "bytes=0-1023"
        }
      },
      "MediaRevision": {
        "name": "rev",
        "in": "query",
        "required": false,
        "description": "由服务端媒体 URL 提供的不透明生成版本与表示标识，原样保留；不是内容哈希。与当前版本不符时返回 409/no-store。省略仍兼容旧客户端，但失去版本保护。",
        "schema": {
          "type": "string"
        }
      }
    },
    "responses": {
      "BadRequest": {
        "description": "业务参数错误（JSON detail），或查询参数/JSON 语法错误（可能为 text/plain）。",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorResponse"
            },
            "example": {
              "detail": "query 不能为空"
            }
          },
          "text/plain": {
            "schema": {
              "type": "string"
            }
          }
        },
        "headers": {
          "Cache-Control": {
            "description": "动态、未就绪及错误响应禁止缓存。",
            "schema": {
              "type": "string",
              "example": "no-store"
            }
          }
        }
      },
      "InternalError": {
        "description": "服务端内部错误",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorResponse"
            },
            "example": {
              "detail": "internal error"
            }
          }
        },
        "headers": {
          "Cache-Control": {
            "description": "动态、未就绪及错误响应禁止缓存。",
            "schema": {
              "type": "string",
              "example": "no-store"
            }
          }
        }
      },
      "NotFound": {
        "description": "任务或文件不存在。任务重启后无法从缓存恢复时也可能返回 404。",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorResponse"
            }
          }
        },
        "headers": {
          "Cache-Control": {
            "description": "动态、未就绪及错误响应禁止缓存。",
            "schema": {
              "type": "string",
              "example": "no-store"
            }
          }
        }
      },
      "Conflict": {
        "description": "音频或封面尚未就绪；请等待任务状态后重试。",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorResponse"
            }
          }
        },
        "headers": {
          "Cache-Control": {
            "description": "动态、未就绪及错误响应禁止缓存。",
            "schema": {
              "type": "string",
              "example": "no-store"
            }
          }
        }
      },
      "NotAcceptable": {
        "description": "该任务没有启用 wasm_alac 分包传输。",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorResponse"
            }
          }
        },
        "headers": {
          "Cache-Control": {
            "description": "动态、未就绪及错误响应禁止缓存。",
            "schema": {
              "type": "string",
              "example": "no-store"
            }
          }
        }
      },
      "RangeNotSatisfiable": {
        "description": "Range 无效、越界或本地文件不支持多段 Range。",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorResponse"
            }
          }
        },
        "headers": {
          "Cache-Control": {
            "description": "动态、未就绪及错误响应禁止缓存。",
            "schema": {
              "type": "string",
              "example": "no-store"
            }
          }
        }
      },
      "BadGateway": {
        "description": "上游文件服务暂时不可用。",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorResponse"
            }
          }
        },
        "headers": {
          "Cache-Control": {
            "description": "动态、未就绪及错误响应禁止缓存。",
            "schema": {
              "type": "string",
              "example": "no-store"
            }
          }
        }
      },
      "ServiceUnavailable": {
        "description": "上游续播推荐不可用且没有候选歌曲。",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorResponse"
            }
          }
        },
        "headers": {
          "Cache-Control": {
            "description": "动态、未就绪及错误响应禁止缓存。",
            "schema": {
              "type": "string",
              "example": "no-store"
            }
          }
        }
      },
      "UnsupportedMediaType": {
        "description": "JSON 请求缺少或使用了不支持的 Content-Type；请使用 application/json。",
        "content": {
          "text/plain": {
            "schema": {
              "type": "string"
            }
          }
        },
        "headers": {
          "Cache-Control": {
            "description": "动态、未就绪及错误响应禁止缓存。",
            "schema": {
              "type": "string",
              "example": "no-store"
            }
          }
        }
      },
      "InvalidBody": {
        "description": "JSON 缺少必填字段或字段类型错误。框架校验通常返回纯文本。",
        "content": {
          "text/plain": {
            "schema": {
              "type": "string"
            }
          }
        },
        "headers": {
          "Cache-Control": {
            "description": "动态、未就绪及错误响应禁止缓存。",
            "schema": {
              "type": "string",
              "example": "no-store"
            }
          }
        }
      },
      "PayloadTooLarge": {
        "description": "JSON 请求体超出服务框架允许的大小。",
        "content": {
          "text/plain": {
            "schema": {
              "type": "string"
            }
          }
        },
        "headers": {
          "Cache-Control": {
            "description": "动态、未就绪及错误响应禁止缓存。",
            "schema": {
              "type": "string",
              "example": "no-store"
            }
          }
        }
      }
    },
    "schemas": {
      "CollectionResponse": {
        "type": "object",
        "required": [
          "platform",
          "id",
          "title",
          "source_url",
          "items"
        ],
        "properties": {
          "platform": {
            "type": "string",
            "example": "applemusic",
            "enum": [
              "applemusic",
              "bilibili",
              "kugou",
              "netease",
              "soda",
              "tencent",
              "youtubemusic"
            ]
          },
          "id": {
            "type": "string",
            "example": "pl.u-example"
          },
          "title": {
            "type": "string",
            "example": "示例歌单"
          },
          "source_url": {
            "type": "string",
            "example": "https://music.apple.com/cn/playlist/example/pl.u-example",
            "description": "请求 url 去除首尾空白后的原值；可能是链接或平台集合 ID，不保证是 URI。"
          },
          "items": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/SearchItem"
            },
            "maxItems": 5000,
            "description": "省略 limit 时为完整清单（超过 5000 首返回错误）；显式 limit 时为最多该数量的预览。"
          }
        }
      },
      "HealthResponse": {
        "type": "object",
        "required": [
          "status",
          "service"
        ],
        "properties": {
          "status": {
            "type": "string",
            "example": "ok"
          },
          "service": {
            "type": "string",
            "example": "music-api"
          }
        }
      },
      "PlatformInfo": {
        "type": "object",
        "required": [
          "id",
          "label",
          "aliases"
        ],
        "properties": {
          "id": {
            "type": "string",
            "enum": [
              "applemusic",
              "bilibili",
              "kugou",
              "netease",
              "soda",
              "tencent",
              "youtubemusic"
            ],
            "example": "tencent"
          },
          "label": {
            "type": "string",
            "example": "QQ音乐"
          },
          "aliases": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "example": [
              "qq",
              "qqmusic",
              "tencent",
              "tx",
              "qq音乐",
              "腾讯",
              "腾讯音乐"
            ]
          }
        }
      },
      "SearchItem": {
        "type": "object",
        "required": [
          "platform",
          "id",
          "song",
          "singer",
          "cover"
        ],
        "properties": {
          "platform": {
            "type": "string",
            "enum": [
              "applemusic",
              "bilibili",
              "kugou",
              "netease",
              "soda",
              "tencent",
              "youtubemusic"
            ],
            "example": "tencent"
          },
          "id": {
            "type": "string",
            "description": "平台内歌曲或视频 ID，可作为 selected_id 使用。",
            "example": "0039MnYb0qxYhV"
          },
          "song": {
            "type": "string",
            "example": "示例歌名"
          },
          "singer": {
            "type": "string",
            "example": "示例歌手"
          },
          "cover": {
            "type": "string",
            "description": "封面 URL；平台未返回封面时可能为空字符串。",
            "example": ""
          }
        }
      },
      "CreatePlaybackRequest": {
        "type": "object",
        "required": [
          "platform",
          "query"
        ],
        "properties": {
          "platform": {
            "type": "string",
            "description": "平台 ID 或别名。",
            "example": "applemusic"
          },
          "query": {
            "type": "string",
            "description": "必填且去除首尾空白后不能为空。可用歌曲链接、ID 或搜索词；即使提供 selected_id，也必须传 query。已知歌曲 ID 时可同时作为 query 和 selected_id。",
            "example": "1579625893"
          },
          "selected_id": {
            "type": "string",
            "description": "可选的明确平台歌曲 ID，通常取自 SearchItem.id。提供后跳过创建阶段的关键词选曲；空字符串视为未提供。",
            "nullable": true,
            "example": "1579625893"
          },
          "quality": {
            "type": "string",
            "description": "建议使用 standard（标准）、high（高音质）、lossless（无损）、hires（高解析无损）。省略或 null 为 high；平台支持情况不同，实际可用编码和音质取决于歌曲及来源。此字段在通用 API 层不是严格枚举校验，请使用上述规范值。",
            "nullable": true,
            "default": "high",
            "example": "hires"
          },
          "include_cover": {
            "type": "boolean",
            "default": true,
            "description": "是否请求附带封面。省略为 true；同一歌曲与音质复用既有任务，不能保证改变既有任务的封面设置。"
          },
          "streaming_mode": {
            "type": "string",
            "description": "普通文件下载请省略。高级客户端可传 wasm_alac，请求 ALAC 分包流；只在支持的来源/音质和缓存格式下生效，务必检查响应 streaming_url。其他字符串目前按未启用处理。",
            "nullable": true,
            "example": "wasm_alac"
          }
        }
      },
      "CreatePlaybackResponse": {
        "type": "object",
        "required": [
          "playback",
          "status_url",
          "audio_url",
          "cover_url",
          "lyrics_url"
        ],
        "properties": {
          "playback": {
            "$ref": "#/components/schemas/PlaybackTask"
          },
          "status_url": {
            "type": "string",
            "example": "/api/v1/playbacks/tencent--0039MnYb0qxYhV--high",
            "description": "API 域状态地址，不迁移到媒体域；以 API 基址解析相对路径。"
          },
          "audio_url": {
            "type": "string",
            "example": "/api/v1/playbacks/tencent--0039MnYb0qxYhV--high/audio?rev=example-native-revision",
            "description": "标准音频 URL；completed 时可为配置媒体域的绝对 URL，其他状态或同源配置使用 API 域相对路径。下载时使用最新 completed 状态里的 audio_url 并保留 rev。"
          },
          "cover_url": {
            "type": "string",
            "example": "/api/v1/playbacks/tencent--0039MnYb0qxYhV--high/cover",
            "description": "API 域相对地址，始终以 API 基址解析。"
          },
          "lyrics_url": {
            "type": "string",
            "description": "API 域相对地址，始终以 API 基址解析。",
            "example": "/api/v1/playbacks/tencent--0039MnYb0qxYhV--high/lyrics"
          }
        },
        "description": "创建或复用任务的响应。请直接使用返回的 URL，不依赖内部播放 ID 拼接规则。流式能力字段位于 playback 对象内。"
      },
      "PlaybackTask": {
        "type": "object",
        "required": [
          "id",
          "status",
          "platform",
          "query",
          "selected_id",
          "quality",
          "include_cover",
          "title",
          "artist",
          "album",
          "source_url",
          "content_type",
          "byte_size",
          "bitrate",
          "duration",
          "format",
          "decrypt_elapsed_seconds",
          "error",
          "audio_url",
          "cover_url",
          "track_id",
          "lyrics_url",
          "streaming_mode",
          "streaming_codec",
          "streaming_protocol",
          "streaming_url",
          "media_revision"
        ],
        "properties": {
          "id": {
            "type": "string",
            "example": "tencent--0039MnYb0qxYhV--high"
          },
          "status": {
            "$ref": "#/components/schemas/PlaybackStatus"
          },
          "platform": {
            "type": "string",
            "enum": [
              "applemusic",
              "bilibili",
              "kugou",
              "netease",
              "soda",
              "tencent",
              "youtubemusic"
            ],
            "example": "tencent"
          },
          "query": {
            "type": "string",
            "example": "晴天"
          },
          "selected_id": {
            "type": "string",
            "nullable": true,
            "example": "0039MnYb0qxYhV"
          },
          "quality": {
            "type": "string",
            "example": "high"
          },
          "include_cover": {
            "type": "boolean",
            "example": true
          },
          "title": {
            "type": "string",
            "nullable": true,
            "example": "晴天"
          },
          "artist": {
            "type": "string",
            "nullable": true,
            "example": "周杰伦"
          },
          "album": {
            "type": "string",
            "nullable": true,
            "example": "叶惠美"
          },
          "source_url": {
            "type": "string",
            "nullable": true,
            "example": "https://y.qq.com/..."
          },
          "content_type": {
            "type": "string",
            "nullable": true,
            "example": "audio/mp4",
            "description": "源音频元数据；可能与 audio_url 返回的兼容文件不同，请以下载响应头为准。"
          },
          "byte_size": {
            "type": "integer",
            "format": "int64",
            "nullable": true,
            "minimum": 0,
            "example": 9216000,
            "description": "源音频元数据；可能与 audio_url 返回的兼容文件不同，请以下载响应头为准。"
          },
          "bitrate": {
            "type": "integer",
            "format": "int32",
            "nullable": true,
            "minimum": 0,
            "description": "码率，单位 bit/s。",
            "example": 320000
          },
          "duration": {
            "type": "integer",
            "format": "int32",
            "nullable": true,
            "minimum": 0,
            "description": "时长，单位秒。",
            "example": 269
          },
          "format": {
            "type": "string",
            "nullable": true,
            "example": "m4a",
            "description": "源音频元数据；可能与 audio_url 返回的兼容文件不同，请以下载响应头为准。"
          },
          "decrypt_elapsed_seconds": {
            "type": "number",
            "format": "double",
            "nullable": true,
            "minimum": 0,
            "example": null,
            "description": "服务端记录的解密耗时（秒），可能为 null。"
          },
          "error": {
            "type": "string",
            "nullable": true,
            "example": null,
            "description": "失败原因；status 为 failed 时应停止轮询并展示此信息。"
          },
          "audio_url": {
            "type": "string",
            "example": "/api/v1/playbacks/tencent--0039MnYb0qxYhV--high/audio?rev=example-native-revision",
            "description": "标准音频地址；completed 时可为配置媒体域的绝对 URL，其他状态或同源配置使用 API 域相对路径。普通下载使用最新 completed 状态中的此字段，保留 rev；不要将 streaming_url 当作常规音乐文件。"
          },
          "cover_url": {
            "type": "string",
            "example": "/api/v1/playbacks/tencent--0039MnYb0qxYhV--high/cover",
            "description": "API 域相对地址，不迁移到媒体域。"
          },
          "track_id": {
            "type": "string",
            "nullable": true,
            "description": "解析出的平台歌曲 ID；与播放任务 id 不同。",
            "example": "0039MnYb0qxYhV"
          },
          "lyrics_url": {
            "type": "string",
            "description": "API 域相对地址，不迁移到媒体域。",
            "example": "/api/v1/playbacks/tencent--0039MnYb0qxYhV--high/lyrics"
          },
          "streaming_mode": {
            "type": "string",
            "nullable": true,
            "example": null,
            "description": "任务启用的传输模式；通常为 null 或 wasm_alac。后续状态查询可能反映其他客户端为共享任务启用的模式，普通下载仍使用 audio_url。"
          },
          "streaming_codec": {
            "type": "string",
            "nullable": true,
            "example": "alac"
          },
          "streaming_protocol": {
            "type": "string",
            "nullable": true,
            "example": "splayer-alac-v2",
            "description": "ALAC 分包协议标识，当前为 splayer-alac-v2；不是 HLS、MPEG-DASH 或标准音频文件格式。"
          },
          "streaming_url": {
            "type": "string",
            "nullable": true,
            "example": "/api/v1/playbacks/tencent--0039MnYb0qxYhV--lossless/audio/stream?rev=example-packet-revision",
            "description": "高级解码器使用的专用分包端点；null 表示不提供。completed 时可为配置媒体域的绝对 URL，生成中或同源配置使用 API 域相对路径；保留 rev，不能混用普通音频 Range。"
          },
          "media_revision": {
            "type": "string",
            "description": "本次媒体生成实例的不透明标识，不是内容哈希。媒体 URL 的 rev 还区分普通音频与 ALAC packet；同一次生成状态变为 completed 不改变版本。"
          }
        },
        "description": "任务及源音频元数据。字段始终存在，但准备阶段或来源未提供时可为 null。音频响应可能是无损兼容格式，因此 content_type、format、byte_size 不一定等于 GET audio_url 的实际响应；保存文件以响应 Content-Type/Content-Disposition 为准，长度以 Content-Length/Content-Range 为准。"
      },
      "PlaybackStatus": {
        "type": "string",
        "enum": [
          "queued",
          "processing",
          "streaming",
          "completed",
          "failed"
        ],
        "example": "completed",
        "description": "queued=排队；processing=准备中；streaming=音频仍在生成，兼容客户端可开始读取；completed=任务完成；failed=任务失败。普通文件下载等待 completed，而非 streaming。"
      },
      "ErrorResponse": {
        "type": "object",
        "required": [
          "detail"
        ],
        "properties": {
          "detail": {
            "type": "string",
            "example": "query 不能为空"
          }
        }
      },
      "LyricsResponse": {
        "type": "object",
        "required": [
          "platform",
          "track_id",
          "available",
          "plain",
          "translation"
        ],
        "properties": {
          "platform": {
            "type": "string",
            "description": "平台 ID。",
            "example": "applemusic"
          },
          "track_id": {
            "type": "string",
            "description": "请求或播放任务解析出的平台曲目 ID。",
            "example": "905226305"
          },
          "available": {
            "type": "boolean",
            "description": "是否有原生或聚合歌词可用",
            "example": true
          },
          "plain": {
            "type": "string",
            "description": "原文同步歌词，通常为 LRC 格式。",
            "example": "[00:00.00]我怀念的"
          },
          "translation": {
            "type": "string",
            "description": "翻译同步歌词，可能为空字符串；按时间戳对齐后每行最多使用一次",
            "example": ""
          }
        }
      },
      "MatchSourceItem": {
        "type": "object",
        "required": [
          "id",
          "song",
          "singer"
        ],
        "properties": {
          "id": {
            "type": "string"
          },
          "song": {
            "type": "string",
            "example": "示例歌名"
          },
          "singer": {
            "type": "string",
            "example": "示例歌手"
          },
          "cover": {
            "type": "string"
          }
        }
      },
      "MatchCollectionRequest": {
        "type": "object",
        "required": [
          "source_platform",
          "target_platform",
          "items"
        ],
        "properties": {
          "source_platform": {
            "type": "string"
          },
          "target_platform": {
            "type": "string"
          },
          "items": {
            "type": "array",
            "maxItems": 200,
            "items": {
              "$ref": "#/components/schemas/MatchSourceItem"
            },
            "minItems": 1,
            "description": "当前匹配批次，必须为 1..200 首；较长歌单需分批提交。"
          }
        }
      },
      "MatchCollectionItem": {
        "type": "object",
        "required": [
          "source",
          "status",
          "confidence",
          "candidates"
        ],
        "properties": {
          "source": {
            "$ref": "#/components/schemas/MatchSourceItem"
          },
          "status": {
            "type": "string",
            "enum": [
              "matched",
              "review",
              "missing"
            ]
          },
          "confidence": {
            "type": "integer",
            "minimum": 0,
            "maximum": 100
          },
          "candidates": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/SearchItem"
            }
          }
        }
      },
      "MatchCollectionResponse": {
        "type": "object",
        "required": [
          "source_platform",
          "target_platform",
          "matched_count",
          "review_count",
          "missing_count",
          "items"
        ],
        "properties": {
          "source_platform": {
            "type": "string"
          },
          "target_platform": {
            "type": "string"
          },
          "matched_count": {
            "type": "integer"
          },
          "review_count": {
            "type": "integer"
          },
          "missing_count": {
            "type": "integer"
          },
          "items": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/MatchCollectionItem"
            },
            "minItems": 1,
            "maxItems": 200,
            "description": "与当前请求批次的数量和顺序一致。"
          }
        }
      },
      "RecommendationTag": {
        "type": "object",
        "required": [
          "id",
          "label",
          "subtitle",
          "playlist_ids"
        ],
        "properties": {
          "id": {
            "type": "string"
          },
          "label": {
            "type": "string"
          },
          "subtitle": {
            "type": "string"
          },
          "playlist_ids": {
            "type": "array",
            "items": {
              "type": "string"
            }
          }
        }
      },
      "RecommendationPlaylist": {
        "type": "object",
        "required": [
          "id",
          "resource_type",
          "name",
          "curator",
          "description",
          "cover",
          "url",
          "recommendation_id"
        ],
        "properties": {
          "id": {
            "type": "string"
          },
          "resource_type": {
            "type": "string",
            "description": "资源类型，例如 playlists 或 albums。"
          },
          "name": {
            "type": "string"
          },
          "curator": {
            "type": "string"
          },
          "description": {
            "type": "string"
          },
          "cover": {
            "type": "string"
          },
          "url": {
            "type": "string",
            "description": "把此链接传给 /api/v1/collections/resolve 可读取歌曲列表。"
          },
          "recommendation_id": {
            "type": "string"
          }
        }
      },
      "RecommendationHome": {
        "type": "object",
        "required": [
          "source",
          "tags",
          "playlists",
          "tracks"
        ],
        "properties": {
          "source": {
            "type": "string",
            "example": "applemusic-personal"
          },
          "tags": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/RecommendationTag"
            }
          },
          "playlists": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/RecommendationPlaylist"
            }
          },
          "tracks": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/SearchItem"
            }
          }
        }
      },
      "RecommendationContinuation": {
        "type": "object",
        "required": [
          "source",
          "tracks"
        ],
        "properties": {
          "source": {
            "type": "string",
            "description": "来源标识，例如 applemusic-related 或 applemusic-personal；可能混合相关候选和推荐补充。"
          },
          "tracks": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/SearchItem"
            }
          }
        }
      },
      "RecommendationContinuationRequest": {
        "type": "object",
        "required": [
          "seed_id"
        ],
        "properties": {
          "platform": {
            "type": "string",
            "default": "applemusic"
          },
          "seed_id": {
            "type": "string",
            "description": "作为推荐起点的 Apple Music 歌曲数字 ID；1..20 位且不能全为 0。",
            "pattern": "^(?!0+$)[0-9]{1,20}$",
            "example": "1579625893"
          },
          "exclude": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "default": [],
            "description": "已播放或队列中歌曲 ID。会去重、忽略无效 ID，并最多采用 10000 个有效 ID；额外项忽略。"
          },
          "limit": {
            "type": "integer",
            "minimum": 0,
            "default": 10,
            "description": "服务端钳制到 1..50。"
          }
        }
      },
      "LoudnessResponse": {
        "type": "object",
        "required": [
          "state",
          "version"
        ],
        "properties": {
          "state": {
            "type": "string",
            "description": "pending=等待任务完成或测量中；ready=可用；unavailable=当前无法测量。",
            "enum": [
              "pending",
              "ready",
              "unavailable"
            ]
          },
          "integrated_lufs": {
            "type": "number",
            "format": "double",
            "description": "整首综合响度，单位 LUFS。仅 ready 时存在。"
          },
          "true_peak_dbfs": {
            "type": "number",
            "format": "double",
            "description": "真实峰值，实际单位为 dBTP；字段名为兼容性保留。仅 ready 时存在。"
          },
          "gain_db": {
            "type": "number",
            "format": "double",
            "description": "建议播放增益（dB），由客户端可选应用；下载文件本身不会调整。仅 ready 时存在。"
          },
          "version": {
            "type": "integer",
            "example": 1
          }
        }
      }
    }
  },
  "security": []
}
