{ "swagger": "2.0", "info": { "description": "轻量级音乐服务器 API 文档,支持本地音乐管理、网络歌曲、电台和歌单功能", "title": "Songloft API", "contact": { "name": "API Support", "url": "https://github.com/songloft-org/songloft", "email": "im.hanxi@gmail.com" }, "license": { "name": "Apache 2.0", "url": "https://www.apache.org/licenses/LICENSE-2.0" }, "version": "2.10.0" }, "host": "localhost:58091", "basePath": "/api/v1", "paths": { "/api/v1/jsplugin-assets/{path}": { "get": { "description": "服务由主程序嵌入的插件通用 CSS、JS 和字体文件,自动注入到所有插件 HTML 页面。", "produces": [ "application/octet-stream" ], "tags": [ "JS 插件" ], "summary": "插件公共资源", "parameters": [ { "type": "string", "description": "资源路径", "name": "*", "in": "path", "required": true } ], "responses": { "200": { "description": "资源文件" }, "404": { "description": "资源不存在", "schema": { "type": "object", "additionalProperties": { "type": "string" } } } } } }, "/api/v1/songs/organize": { "post": { "security": [ { "BearerAuth": [] } ], "description": "批量移动/重命名本地歌曲文件到指定目录结构。target_path 为相对于 music_path 的路径(含目录和文件名),扩展名必须与原文件一致。CUE 拆分歌曲会被跳过(status=skip);目标文件已存在时拒绝覆盖(status=error)。music_path 由服务端自取。", "consumes": [ "application/json" ], "produces": [ "application/json" ], "tags": [ "歌曲管理" ], "summary": "批量整理歌曲文件", "parameters": [ { "description": "整理项目列表", "name": "request", "in": "body", "required": true, "schema": { "type": "array", "items": { "$ref": "#/definitions/services.OrganizeItem" } } } ], "responses": { "200": { "description": "整理结果", "schema": { "type": "array", "items": { "$ref": "#/definitions/services.OrganizeResult" } } }, "400": { "description": "请求错误", "schema": { "type": "object", "additionalProperties": { "type": "string" } } } } } }, "/api/v1/songs/organize/preview": { "post": { "security": [ { "BearerAuth": [] } ], "description": "dry-run 预览目录整理变更,返回每项 old_path→new_path 与状态(ok/conflict/skip/error),不移动任何文件、不改数据库。target_path 为相对 music_path 的路径。CUE 歌曲 skip;目标已存在或批内撞名 conflict。music_path 由服务端自取。", "consumes": [ "application/json" ], "produces": [ "application/json" ], "tags": [ "歌曲管理" ], "summary": "预览批量整理歌曲文件", "parameters": [ { "description": "整理项目列表", "name": "request", "in": "body", "required": true, "schema": { "type": "array", "items": { "$ref": "#/definitions/services.OrganizeItem" } } } ], "responses": { "200": { "description": "预览结果", "schema": { "type": "array", "items": { "$ref": "#/definitions/services.OrganizePreviewResult" } } }, "400": { "description": "请求错误", "schema": { "type": "object", "additionalProperties": { "type": "string" } } } } } }, "/api/v1/songs/{id}/tags": { "put": { "security": [ { "BearerAuth": [] } ], "description": "将元数据写入数据库和本地音频文件标签(仅本地歌曲)。cover_data(base64) 优先于 cover_url。非空字段覆盖,空值保留原值。设置 clear_cover=true 可显式清空封面。rename_file=true 时按新标题重命名本地音频文件(保留原目录与扩展名,仅本地非 CUE 歌曲生效);标题清理后为空或目标文件名已存在时返回 400,与原文件同名则不移动仅写库。", "consumes": [ "application/json" ], "produces": [ "application/json" ], "tags": [ "歌曲管理" ], "summary": "写入歌曲标签", "parameters": [ { "type": "integer", "description": "歌曲ID", "name": "id", "in": "path", "required": true }, { "description": "标签数据", "name": "request", "in": "body", "required": true, "schema": { "$ref": "#/definitions/handlers.WriteSongTagsRequest" } } ], "responses": { "200": { "description": "写入结果", "schema": { "type": "object", "properties": { "file_write": { "type": "string" }, "song": { "$ref": "#/definitions/models.Song" } } } }, "400": { "description": "请求错误", "schema": { "type": "object", "additionalProperties": { "type": "string" } } }, "404": { "description": "歌曲不存在", "schema": { "type": "object", "additionalProperties": { "type": "string" } } } } } }, "/auth/login": { "post": { "description": "用户登录获取访问令牌", "consumes": [ "application/json" ], "produces": [ "application/json" ], "tags": [ "认证管理" ], "summary": "用户登录", "parameters": [ { "description": "登录请求", "name": "request", "in": "body", "required": true, "schema": { "$ref": "#/definitions/models.LoginRequest" } } ], "responses": { "200": { "description": "登录成功", "schema": { "$ref": "#/definitions/models.LoginResponse" } }, "400": { "description": "请求数据错误", "schema": { "$ref": "#/definitions/models.ErrorResponse" } }, "401": { "description": "用户名或密码错误", "schema": { "$ref": "#/definitions/models.ErrorResponse" } }, "500": { "description": "服务器错误", "schema": { "$ref": "#/definitions/models.ErrorResponse" } } } } }, "/auth/logout": { "post": { "security": [ { "BearerAuth": [] } ], "description": "用户登出,撤销当前访问令牌", "consumes": [ "application/json" ], "produces": [ "application/json" ], "tags": [ "认证管理" ], "summary": "用户登出", "responses": { "200": { "description": "登出成功", "schema": { "$ref": "#/definitions/models.SuccessResponse" } }, "401": { "description": "未授权", "schema": { "$ref": "#/definitions/models.ErrorResponse" } }, "500": { "description": "服务器错误", "schema": { "$ref": "#/definitions/models.ErrorResponse" } } } } }, "/auth/refresh": { "post": { "description": "使用刷新令牌获取新的访问令牌", "consumes": [ "application/json" ], "produces": [ "application/json" ], "tags": [ "认证管理" ], "summary": "刷新令牌", "parameters": [ { "description": "刷新令牌请求", "name": "request", "in": "body", "required": true, "schema": { "$ref": "#/definitions/models.RefreshTokenRequest" } } ], "responses": { "200": { "description": "刷新成功", "schema": { "$ref": "#/definitions/services.RefreshResponse" } }, "400": { "description": "请求数据错误", "schema": { "$ref": "#/definitions/models.ErrorResponse" } }, "401": { "description": "刷新令牌无效", "schema": { "$ref": "#/definitions/models.ErrorResponse" } }, "500": { "description": "服务器错误", "schema": { "$ref": "#/definitions/models.ErrorResponse" } } } } }, "/auth/tokens": { "get": { "security": [ { "BearerAuth": [] } ], "description": "获取当前用户的所有活跃令牌列表", "consumes": [ "application/json" ], "produces": [ "application/json" ], "tags": [ "认证管理" ], "summary": "列出活跃令牌", "parameters": [ { "enum": [ "access", "refresh" ], "type": "string", "description": "令牌类型", "name": "type", "in": "query" }, { "type": "integer", "default": 20, "description": "每页数量", "name": "limit", "in": "query" }, { "type": "integer", "default": 0, "description": "偏移量", "name": "offset", "in": "query" } ], "responses": { "200": { "description": "令牌列表", "schema": { "type": "object", "additionalProperties": true } }, "401": { "description": "未授权", "schema": { "$ref": "#/definitions/models.ErrorResponse" } }, "500": { "description": "服务器错误", "schema": { "$ref": "#/definitions/models.ErrorResponse" } } } } }, "/auth/tokens/{token_id}": { "get": { "security": [ { "BearerAuth": [] } ], "description": "获取指定令牌的详细信息", "consumes": [ "application/json" ], "produces": [ "application/json" ], "tags": [ "认证管理" ], "summary": "获取令牌信息", "parameters": [ { "type": "string", "description": "令牌ID", "name": "token_id", "in": "path", "required": true } ], "responses": { "200": { "description": "令牌信息", "schema": { "$ref": "#/definitions/models.TokenInfo" } }, "401": { "description": "未授权", "schema": { "$ref": "#/definitions/models.ErrorResponse" } }, "404": { "description": "令牌不存在", "schema": { "$ref": "#/definitions/models.ErrorResponse" } } } }, "delete": { "security": [ { "BearerAuth": [] } ], "description": "撤销指定的令牌", "consumes": [ "application/json" ], "produces": [ "application/json" ], "tags": [ "认证管理" ], "summary": "撤销令牌", "parameters": [ { "type": "string", "description": "令牌ID", "name": "token_id", "in": "path", "required": true }, { "description": "撤销令牌请求", "name": "request", "in": "body", "required": true, "schema": { "$ref": "#/definitions/models.RevokeTokenRequest" } } ], "responses": { "200": { "description": "撤销成功", "schema": { "$ref": "#/definitions/models.SuccessResponse" } }, "400": { "description": "请求数据错误", "schema": { "$ref": "#/definitions/models.ErrorResponse" } }, "401": { "description": "未授权", "schema": { "$ref": "#/definitions/models.ErrorResponse" } }, "500": { "description": "服务器错误", "schema": { "$ref": "#/definitions/models.ErrorResponse" } } } } }, "/cache-manage/clean": { "post": { "security": [ { "BearerAuth": [] } ], "description": "删除服务端所有已缓存的音乐文件,清理后需要重新下载", "consumes": [ "application/json" ], "produces": [ "application/json" ], "tags": [ "缓存管理" ], "summary": "清理全部音乐缓存", "responses": { "200": { "description": "清理成功", "schema": { "type": "object", "additionalProperties": { "type": "string" } } }, "500": { "description": "清理失败", "schema": { "type": "object", "additionalProperties": { "type": "string" } } } } } }, "/cache-manage/config": { "get": { "security": [ { "BearerAuth": [] } ], "description": "获取服务端音乐缓存的配置信息,包括最大缓存大小限制和缓存目录路径。cache_dir 为空表示使用 default_cache_dir。", "consumes": [ "application/json" ], "produces": [ "application/json" ], "tags": [ "缓存管理" ], "summary": "获取缓存配置", "responses": { "200": { "description": "缓存配置", "schema": { "$ref": "#/definitions/services.CacheConfigResponse" } }, "500": { "description": "服务器错误", "schema": { "type": "object", "additionalProperties": { "type": "string" } } } } }, "put": { "security": [ { "BearerAuth": [] } ], "description": "更新服务端音乐缓存的配置,如最大缓存大小和缓存目录。cache_dir 为空字符串时恢复使用默认目录。更新后会自动触发 LRU 淘汰检查。切换目录时不会自动迁移旧缓存文件。", "consumes": [ "application/json" ], "produces": [ "application/json" ], "tags": [ "缓存管理" ], "summary": "更新缓存配置", "parameters": [ { "description": "缓存配置", "name": "request", "in": "body", "required": true, "schema": { "$ref": "#/definitions/services.CacheConfig" } } ], "responses": { "200": { "description": "更新后的缓存配置", "schema": { "$ref": "#/definitions/services.CacheConfigResponse" } }, "400": { "description": "请求参数无效", "schema": { "type": "object", "additionalProperties": { "type": "string" } } }, "500": { "description": "更新失败", "schema": { "type": "object", "additionalProperties": { "type": "string" } } } } } }, "/cache-manage/stats": { "get": { "security": [ { "BearerAuth": [] } ], "description": "获取服务端音乐缓存的统计信息,包括总大小、文件数量和最大缓存限制", "consumes": [ "application/json" ], "produces": [ "application/json" ], "tags": [ "缓存管理" ], "summary": "获取缓存统计信息", "responses": { "200": { "description": "缓存统计信息", "schema": { "$ref": "#/definitions/services.CacheStats" } }, "500": { "description": "服务器错误", "schema": { "type": "object", "additionalProperties": { "type": "string" } } } } } }, "/cache-manage/validate-dir": { "post": { "security": [ { "BearerAuth": [] } ], "description": "验证指定目录是否可用作缓存目录。目录不存在时自动创建,检查可写性并返回磁盘空间信息。", "consumes": [ "application/json" ], "produces": [ "application/json" ], "tags": [ "缓存管理" ], "summary": "验证缓存目录", "parameters": [ { "description": "目录路径", "name": "request", "in": "body", "required": true, "schema": { "$ref": "#/definitions/handlers.dirValidateRequest" } } ], "responses": { "200": { "description": "验证结果", "schema": { "$ref": "#/definitions/handlers.dirValidateResponse" } }, "400": { "description": "请求参数无效", "schema": { "type": "object", "additionalProperties": { "type": "string" } } } } } }, "/configs": { "get": { "security": [ { "BearerAuth": [] } ], "description": "获取配置列表,支持关键词搜索和分页", "consumes": [ "application/json" ], "produces": [ "application/json" ], "tags": [ "配置管理" ], "summary": "获取配置列表", "parameters": [ { "type": "string", "description": "搜索关键词", "name": "keyword", "in": "query" }, { "type": "integer", "default": 20, "description": "每页数量", "name": "limit", "in": "query" }, { "type": "integer", "default": 0, "description": "偏移量", "name": "offset", "in": "query" } ], "responses": { "200": { "description": "成功返回配置列表", "schema": { "type": "object", "additionalProperties": true } }, "500": { "description": "服务器错误", "schema": { "type": "object", "additionalProperties": { "type": "string" } } } } }, "post": { "security": [ { "BearerAuth": [] } ], "description": "创建一个新的配置", "consumes": [ "application/json" ], "produces": [ "application/json" ], "tags": [ "配置管理" ], "summary": "创建配置", "parameters": [ { "description": "配置信息", "name": "request", "in": "body", "required": true, "schema": { "$ref": "#/definitions/models.CreateConfigRequest" } } ], "responses": { "201": { "description": "创建成功", "schema": { "$ref": "#/definitions/models.Config" } }, "400": { "description": "请求数据错误", "schema": { "type": "object", "additionalProperties": { "type": "string" } } }, "500": { "description": "创建失败", "schema": { "type": "object", "additionalProperties": { "type": "string" } } } } } }, "/configs/{key}": { "get": { "security": [ { "BearerAuth": [] } ], "description": "根据配置键获取详细信息", "consumes": [ "application/json" ], "produces": [ "application/json" ], "tags": [ "配置管理" ], "summary": "获取单个配置详情", "parameters": [ { "type": "string", "description": "配置键", "name": "key", "in": "path", "required": true } ], "responses": { "200": { "description": "成功返回配置详情", "schema": { "$ref": "#/definitions/models.Config" } }, "404": { "description": "配置不存在", "schema": { "type": "object", "additionalProperties": { "type": "string" } } } } }, "put": { "security": [ { "BearerAuth": [] } ], "description": "更新配置信息", "consumes": [ "application/json" ], "produces": [ "application/json" ], "tags": [ "配置管理" ], "summary": "更新配置", "parameters": [ { "type": "string", "description": "配置键", "name": "key", "in": "path", "required": true }, { "description": "配置信息", "name": "request", "in": "body", "required": true, "schema": { "$ref": "#/definitions/models.UpdateConfigRequest" } } ], "responses": { "200": { "description": "更新成功", "schema": { "$ref": "#/definitions/models.Config" } }, "400": { "description": "请求数据错误", "schema": { "type": "object", "additionalProperties": { "type": "string" } } }, "404": { "description": "配置不存在", "schema": { "type": "object", "additionalProperties": { "type": "string" } } } } }, "delete": { "security": [ { "BearerAuth": [] } ], "description": "根据配置键删除配置", "consumes": [ "application/json" ], "produces": [ "application/json" ], "tags": [ "配置管理" ], "summary": "删除配置", "parameters": [ { "type": "string", "description": "配置键", "name": "key", "in": "path", "required": true } ], "responses": { "200": { "description": "删除成功", "schema": { "type": "object", "additionalProperties": { "type": "string" } } }, "400": { "description": "无效的配置键", "schema": { "type": "object", "additionalProperties": { "type": "string" } } }, "500": { "description": "删除失败", "schema": { "type": "object", "additionalProperties": { "type": "string" } } } } } }, "/health": { "get": { "description": "检查应用是否正常运行", "consumes": [ "application/json" ], "produces": [ "application/json" ], "tags": [ "系统管理" ], "summary": "检查应用健康状态", "responses": { "200": { "description": "成功返回健康状态", "schema": { "type": "object", "additionalProperties": { "type": "string" } } } } } }, "/jsplugin/{entryPath}": { "get": { "description": "JS 插件入口 HTML。{entryPath} 由运行时按已安装插件决定,注入 \u003cbase\u003e 标签和 auth-bridge 脚本后返回 static/index.html。无需认证。", "produces": [ "text/html" ], "tags": [ "JS 插件" ], "summary": "插件根页面(动态路由)", "parameters": [ { "type": "string", "description": "插件入口标识(运行时动态)", "name": "entryPath", "in": "path", "required": true } ], "responses": { "200": { "description": "插件 index.html(已注入 \u003cbase\u003e 与 auth-bridge)", "schema": { "type": "string" } }, "404": { "description": "插件未安装或缺 static/index.html", "schema": { "type": "string" } } } } }, "/jsplugin/{entryPath}/files/{path}": { "get": { "security": [ { "BearerAuth": [] } ], "description": "通过 Go 原生 http.ServeFile 直接返回插件可访问范围内的文件,支持 Range 请求和 HTTP 缓存。", "produces": [ "application/octet-stream" ], "tags": [ "JS 插件" ], "summary": "插件文件直接访问", "parameters": [ { "type": "string", "description": "插件入口标识", "name": "entryPath", "in": "path", "required": true }, { "type": "string", "description": "文件路径", "name": "path", "in": "path", "required": true } ], "responses": { "200": { "description": "文件内容", "schema": { "type": "file" } }, "206": { "description": "部分文件内容(Range 请求)", "schema": { "type": "file" } }, "404": { "description": "文件不存在或权限不足", "schema": { "type": "string" } } } }, "head": { "security": [ { "BearerAuth": [] } ], "description": "通过 Go 原生 http.ServeFile 直接返回插件可访问范围内的文件,支持 Range 请求和 HTTP 缓存。", "produces": [ "application/octet-stream" ], "tags": [ "JS 插件" ], "summary": "插件文件直接访问", "parameters": [ { "type": "string", "description": "插件入口标识", "name": "entryPath", "in": "path", "required": true }, { "type": "string", "description": "文件路径", "name": "path", "in": "path", "required": true } ], "responses": { "200": { "description": "文件内容", "schema": { "type": "file" } }, "206": { "description": "部分文件内容(Range 请求)", "schema": { "type": "file" } }, "404": { "description": "文件不存在或权限不足", "schema": { "type": "string" } } } } }, "/jsplugin/{entryPath}/static": { "get": { "description": "服务 static/index.html,是 handlePluginStatic 的「带 /static 后缀」变体。无需认证。", "produces": [ "text/html" ], "tags": [ "JS 插件" ], "summary": "插件 static 目录根(动态路由)", "parameters": [ { "type": "string", "description": "插件入口标识(运行时动态)", "name": "entryPath", "in": "path", "required": true } ], "responses": { "200": { "description": "插件 index.html", "schema": { "type": "string" } }, "404": { "description": "插件未安装或缺 static/index.html", "schema": { "type": "string" } } } } }, "/jsplugin/{entryPath}/static/{path}": { "get": { "description": "从插件磁盘目录返回 CSS/JS/图片等静态资源;未命中且非 index.html 时 SPA fallback 到 index.html。无需认证。", "produces": [ "application/octet-stream" ], "tags": [ "JS 插件" ], "summary": "插件静态资源文件(动态路由)", "parameters": [ { "type": "string", "description": "插件入口标识(运行时动态)", "name": "entryPath", "in": "path", "required": true } ], "responses": { "200": { "description": "静态资源文件内容", "schema": { "type": "file" } }, "404": { "description": "文件不存在且无 index.html fallback", "schema": { "type": "string" } } } } }, "/jsplugin/{entryPath}/{path}": { "get": { "security": [ { "BearerAuth": [] } ], "description": "接受任意 HTTP 方法,分发到插件 static 兜底、入站 WebSocket upgrade(调用 onWebSocket),或转发到 QuickJS 沙盒中的插件代码。{entryPath} 和子路径均由运行时决定,OpenAPI 仅作占位。需要 BearerAuth。", "consumes": [ "application/json" ], "produces": [ "application/json" ], "tags": [ "JS 插件" ], "summary": "插件 API 转发 catch-all(动态路由)", "parameters": [ { "type": "string", "description": "插件入口标识(运行时动态)", "name": "entryPath", "in": "path", "required": true } ], "responses": { "200": { "description": "插件自定义响应", "schema": { "type": "object", "additionalProperties": true } }, "403": { "description": "插件未启用", "schema": { "type": "object", "additionalProperties": { "type": "string" } } }, "404": { "description": "插件不存在", "schema": { "type": "object", "additionalProperties": { "type": "string" } } }, "503": { "description": "插件不可用或运行异常(健康检查会自愈)", "schema": { "type": "object", "additionalProperties": { "type": "string" } } }, "504": { "description": "JS 运行时调用超时", "schema": { "type": "object", "additionalProperties": { "type": "string" } } } } }, "put": { "security": [ { "BearerAuth": [] } ], "description": "接受任意 HTTP 方法,分发到插件 static 兜底、入站 WebSocket upgrade(调用 onWebSocket),或转发到 QuickJS 沙盒中的插件代码。{entryPath} 和子路径均由运行时决定,OpenAPI 仅作占位。需要 BearerAuth。", "consumes": [ "application/json" ], "produces": [ "application/json" ], "tags": [ "JS 插件" ], "summary": "插件 API 转发 catch-all(动态路由)", "parameters": [ { "type": "string", "description": "插件入口标识(运行时动态)", "name": "entryPath", "in": "path", "required": true } ], "responses": { "200": { "description": "插件自定义响应", "schema": { "type": "object", "additionalProperties": true } }, "403": { "description": "插件未启用", "schema": { "type": "object", "additionalProperties": { "type": "string" } } }, "404": { "description": "插件不存在", "schema": { "type": "object", "additionalProperties": { "type": "string" } } }, "503": { "description": "插件不可用或运行异常(健康检查会自愈)", "schema": { "type": "object", "additionalProperties": { "type": "string" } } }, "504": { "description": "JS 运行时调用超时", "schema": { "type": "object", "additionalProperties": { "type": "string" } } } } }, "post": { "security": [ { "BearerAuth": [] } ], "description": "接受任意 HTTP 方法,分发到插件 static 兜底、入站 WebSocket upgrade(调用 onWebSocket),或转发到 QuickJS 沙盒中的插件代码。{entryPath} 和子路径均由运行时决定,OpenAPI 仅作占位。需要 BearerAuth。", "consumes": [ "application/json" ], "produces": [ "application/json" ], "tags": [ "JS 插件" ], "summary": "插件 API 转发 catch-all(动态路由)", "parameters": [ { "type": "string", "description": "插件入口标识(运行时动态)", "name": "entryPath", "in": "path", "required": true } ], "responses": { "200": { "description": "插件自定义响应", "schema": { "type": "object", "additionalProperties": true } }, "403": { "description": "插件未启用", "schema": { "type": "object", "additionalProperties": { "type": "string" } } }, "404": { "description": "插件不存在", "schema": { "type": "object", "additionalProperties": { "type": "string" } } }, "503": { "description": "插件不可用或运行异常(健康检查会自愈)", "schema": { "type": "object", "additionalProperties": { "type": "string" } } }, "504": { "description": "JS 运行时调用超时", "schema": { "type": "object", "additionalProperties": { "type": "string" } } } } }, "delete": { "security": [ { "BearerAuth": [] } ], "description": "接受任意 HTTP 方法,分发到插件 static 兜底、入站 WebSocket upgrade(调用 onWebSocket),或转发到 QuickJS 沙盒中的插件代码。{entryPath} 和子路径均由运行时决定,OpenAPI 仅作占位。需要 BearerAuth。", "consumes": [ "application/json" ], "produces": [ "application/json" ], "tags": [ "JS 插件" ], "summary": "插件 API 转发 catch-all(动态路由)", "parameters": [ { "type": "string", "description": "插件入口标识(运行时动态)", "name": "entryPath", "in": "path", "required": true } ], "responses": { "200": { "description": "插件自定义响应", "schema": { "type": "object", "additionalProperties": true } }, "403": { "description": "插件未启用", "schema": { "type": "object", "additionalProperties": { "type": "string" } } }, "404": { "description": "插件不存在", "schema": { "type": "object", "additionalProperties": { "type": "string" } } }, "503": { "description": "插件不可用或运行异常(健康检查会自愈)", "schema": { "type": "object", "additionalProperties": { "type": "string" } } }, "504": { "description": "JS 运行时调用超时", "schema": { "type": "object", "additionalProperties": { "type": "string" } } } } } }, "/jsplugins": { "get": { "security": [ { "BearerAuth": [] } ], "description": "获取 JS 插件列表", "consumes": [ "application/json" ], "produces": [ "application/json" ], "tags": [ "JS插件管理" ], "summary": "列出所有 JS 插件", "responses": { "200": { "description": "JS插件列表", "schema": { "type": "object", "additionalProperties": true } }, "401": { "description": "未授权", "schema": { "$ref": "#/definitions/models.ErrorResponse" } }, "500": { "description": "服务器错误", "schema": { "$ref": "#/definitions/models.ErrorResponse" } } } } }, "/jsplugins/registry/install": { "post": { "security": [ { "BearerAuth": [] } ], "description": "从注册表中的 download_url 下载 ZIP 并安装插件。如果 entry_path 已存在则自动走更新路径。支持 GitHub 代理。可选传入 token 字段用于从需要认证的私有源下载;若未提供 token 但提供了 source_url(「全部」聚合模式),后端会自动从 plugin_registries 配置解析该源存储的 token。", "consumes": [ "application/json" ], "produces": [ "application/json" ], "tags": [ "JS插件管理" ], "summary": "从注册表安装插件", "parameters": [ { "description": "安装请求", "name": "request", "in": "body", "required": true, "schema": { "$ref": "#/definitions/handlers.registryInstallRequest" } } ], "responses": { "200": { "description": "安装结果(更新已有插件)", "schema": { "$ref": "#/definitions/handlers.jsPluginUploadResponse" } }, "201": { "description": "安装结果(新插件)", "schema": { "$ref": "#/definitions/handlers.jsPluginUploadResponse" } }, "400": { "description": "请求格式错误", "schema": { "$ref": "#/definitions/models.ErrorResponse" } }, "500": { "description": "下载或安装失败", "schema": { "$ref": "#/definitions/models.ErrorResponse" } } } } }, "/jsplugins/registry/refresh": { "post": { "security": [ { "BearerAuth": [] } ], "description": "拉取订阅源(含递归 includes),去重合并后返回分页的可用插件列表。每个插件标注是否已安装及是否有更新。默认拉取单个 registry_url,可选传入 token 字段访问需要认证的私有源(如 GitHub 私有仓库 PAT)。当 all_sources=true 时忽略 registry_url/token,改为聚合已保存的所有启用订阅源(各源用自身存储的 token),跨源按 entry_path 去重、高版本优先。", "consumes": [ "application/json" ], "produces": [ "application/json" ], "tags": [ "JS插件管理" ], "summary": "刷新插件注册表", "parameters": [ { "description": "刷新请求", "name": "request", "in": "body", "required": true, "schema": { "$ref": "#/definitions/handlers.registryRefreshRequest" } } ], "responses": { "200": { "description": "插件列表", "schema": { "$ref": "#/definitions/handlers.registryRefreshResponse" } }, "400": { "description": "请求格式错误", "schema": { "$ref": "#/definitions/models.ErrorResponse" } }, "500": { "description": "拉取注册表失败", "schema": { "$ref": "#/definitions/models.ErrorResponse" } } } } }, "/jsplugins/storage/cleanup": { "post": { "security": [ { "BearerAuth": [] } ], "description": "删除 plugin_storage 表中不属于任何已安装插件的数据。当插件被卸载后,其持久化存储数据会保留在数据库中;此端点用于清理这些无主数据。", "produces": [ "application/json" ], "tags": [ "JS插件管理" ], "summary": "清理孤儿持久化存储", "responses": { "200": { "description": "清理完成", "schema": { "type": "object", "additionalProperties": { "type": "string" } } }, "500": { "description": "服务器错误", "schema": { "$ref": "#/definitions/models.ErrorResponse" } } } } }, "/jsplugins/update-all": { "post": { "security": [ { "BearerAuth": [] } ], "description": "检查并更新所有具有远程更新源的 JS 插件。跳过无 update_url 的插件和已是最新版的插件,逐个下载并安装更新,失败不中断其他插件的更新流程。设置 force=true 可跳过版本检查强制重新下载安装所有插件。", "consumes": [ "application/json" ], "produces": [ "application/json" ], "tags": [ "JS插件管理" ], "summary": "批量更新所有 JS 插件", "parameters": [ { "description": "请求参数", "name": "body", "in": "body", "schema": { "type": "object" } } ], "responses": { "200": { "description": "批量更新结果", "schema": { "$ref": "#/definitions/handlers.jsPluginBatchUpdateResponse" } }, "500": { "description": "服务器错误", "schema": { "$ref": "#/definitions/models.ErrorResponse" } } } } }, "/jsplugins/upload": { "post": { "security": [ { "BearerAuth": [] } ], "description": "上传新的 JS 插件文件(.jsplugin.zip 压缩包)", "consumes": [ "multipart/form-data" ], "produces": [ "application/json" ], "tags": [ "JS插件管理" ], "summary": "上传安装 JS 插件", "parameters": [ { "type": "file", "description": "JS插件文件 (.jsplugin.zip)", "name": "file", "in": "formData", "required": true } ], "responses": { "201": { "description": "上传成功", "schema": { "type": "object", "additionalProperties": true } }, "400": { "description": "请求数据错误", "schema": { "$ref": "#/definitions/models.ErrorResponse" } }, "401": { "description": "未授权", "schema": { "$ref": "#/definitions/models.ErrorResponse" } }, "500": { "description": "服务器错误", "schema": { "$ref": "#/definitions/models.ErrorResponse" } } } } }, "/jsplugins/{id}": { "get": { "security": [ { "BearerAuth": [] } ], "description": "根据插件ID获取 JS 插件的详细信息", "consumes": [ "application/json" ], "produces": [ "application/json" ], "tags": [ "JS插件管理" ], "summary": "获取 JS 插件详情", "parameters": [ { "type": "integer", "description": "插件ID", "name": "id", "in": "path", "required": true } ], "responses": { "200": { "description": "JS插件信息", "schema": { "type": "object", "additionalProperties": true } }, "401": { "description": "未授权", "schema": { "$ref": "#/definitions/models.ErrorResponse" } }, "404": { "description": "插件不存在", "schema": { "$ref": "#/definitions/models.ErrorResponse" } } } }, "put": { "security": [ { "BearerAuth": [] } ], "description": "上传新的 JS 插件文件以更新现有插件", "consumes": [ "multipart/form-data" ], "produces": [ "application/json" ], "tags": [ "JS插件管理" ], "summary": "更新 JS 插件", "parameters": [ { "type": "integer", "description": "插件ID", "name": "id", "in": "path", "required": true }, { "type": "file", "description": "JS插件文件 (.jsplugin.zip)", "name": "file", "in": "formData", "required": true } ], "responses": { "200": { "description": "更新成功", "schema": { "type": "object", "additionalProperties": true } }, "400": { "description": "请求数据错误", "schema": { "$ref": "#/definitions/models.ErrorResponse" } }, "401": { "description": "未授权", "schema": { "$ref": "#/definitions/models.ErrorResponse" } }, "404": { "description": "插件不存在", "schema": { "$ref": "#/definitions/models.ErrorResponse" } }, "500": { "description": "服务器错误", "schema": { "$ref": "#/definitions/models.ErrorResponse" } } } }, "delete": { "security": [ { "BearerAuth": [] } ], "description": "根据插件ID删除 JS 插件。可通过 keep_data 参数保留插件数据目录(文件系统存储),持久化存储(数据库)始终保留。", "consumes": [ "application/json" ], "produces": [ "application/json" ], "tags": [ "JS插件管理" ], "summary": "删除 JS 插件", "parameters": [ { "type": "integer", "description": "插件ID", "name": "id", "in": "path", "required": true }, { "type": "string", "description": "是否保留插件数据目录(true/false,默认 false)", "name": "keep_data", "in": "query" } ], "responses": { "200": { "description": "删除成功", "schema": { "type": "object", "additionalProperties": true } }, "401": { "description": "未授权", "schema": { "$ref": "#/definitions/models.ErrorResponse" } }, "404": { "description": "插件不存在", "schema": { "$ref": "#/definitions/models.ErrorResponse" } }, "500": { "description": "服务器错误", "schema": { "$ref": "#/definitions/models.ErrorResponse" } } } } }, "/jsplugins/{id}/check-update": { "get": { "security": [ { "BearerAuth": [] } ], "description": "检查指定 JS 插件的远程更新", "consumes": [ "application/json" ], "produces": [ "application/json" ], "tags": [ "JS插件管理" ], "summary": "检查 JS 插件更新", "parameters": [ { "type": "integer", "description": "插件ID", "name": "id", "in": "path", "required": true } ], "responses": { "200": { "description": "更新信息", "schema": { "type": "object", "additionalProperties": true } }, "401": { "description": "未授权", "schema": { "$ref": "#/definitions/models.ErrorResponse" } }, "404": { "description": "插件不存在", "schema": { "$ref": "#/definitions/models.ErrorResponse" } }, "500": { "description": "服务器错误", "schema": { "$ref": "#/definitions/models.ErrorResponse" } } } } }, "/jsplugins/{id}/disable": { "post": { "security": [ { "BearerAuth": [] } ], "description": "禁用指定的 JS 插件", "consumes": [ "application/json" ], "produces": [ "application/json" ], "tags": [ "JS插件管理" ], "summary": "禁用 JS 插件", "parameters": [ { "type": "integer", "description": "插件ID", "name": "id", "in": "path", "required": true } ], "responses": { "200": { "description": "禁用成功", "schema": { "type": "object", "additionalProperties": true } }, "401": { "description": "未授权", "schema": { "$ref": "#/definitions/models.ErrorResponse" } }, "404": { "description": "插件不存在", "schema": { "$ref": "#/definitions/models.ErrorResponse" } }, "500": { "description": "服务器错误", "schema": { "$ref": "#/definitions/models.ErrorResponse" } } } } }, "/jsplugins/{id}/enable": { "post": { "security": [ { "BearerAuth": [] } ], "description": "启用指定的 JS 插件", "consumes": [ "application/json" ], "produces": [ "application/json" ], "tags": [ "JS插件管理" ], "summary": "启用 JS 插件", "parameters": [ { "type": "integer", "description": "插件ID", "name": "id", "in": "path", "required": true } ], "responses": { "200": { "description": "启用成功", "schema": { "type": "object", "additionalProperties": true } }, "401": { "description": "未授权", "schema": { "$ref": "#/definitions/models.ErrorResponse" } }, "404": { "description": "插件不存在", "schema": { "$ref": "#/definitions/models.ErrorResponse" } }, "500": { "description": "服务器错误", "schema": { "$ref": "#/definitions/models.ErrorResponse" } } } } }, "/jsplugins/{id}/update": { "post": { "security": [ { "BearerAuth": [] } ], "description": "从远程下载并更新指定的 JS 插件。设置 force=true 可跳过版本检查强制重新下载安装。", "consumes": [ "application/json" ], "produces": [ "application/json" ], "tags": [ "JS插件管理" ], "summary": "下载并更新 JS 插件", "parameters": [ { "type": "integer", "description": "插件ID", "name": "id", "in": "path", "required": true } ], "responses": { "200": { "description": "更新成功", "schema": { "type": "object", "additionalProperties": true } }, "401": { "description": "未授权", "schema": { "$ref": "#/definitions/models.ErrorResponse" } }, "404": { "description": "插件不存在", "schema": { "$ref": "#/definitions/models.ErrorResponse" } }, "500": { "description": "服务器错误", "schema": { "$ref": "#/definitions/models.ErrorResponse" } } } } }, "/playlists": { "get": { "security": [ { "BearerAuth": [] } ], "description": "获取歌单列表,支持按类型过滤和分页。默认排除隐藏歌单,传 exclude_labels=none 显示全部", "consumes": [ "application/json" ], "produces": [ "application/json" ], "tags": [ "歌单管理" ], "summary": "获取歌单列表", "parameters": [ { "enum": [ "normal", "radio" ], "type": "string", "description": "歌单类型", "name": "type", "in": "query" }, { "type": "string", "default": "hidden", "description": "要排除的标签(逗号分隔), 默认排除 hidden; 传 none 显示全部", "name": "exclude_labels", "in": "query" }, { "type": "integer", "default": 20, "description": "每页数量", "name": "limit", "in": "query" }, { "type": "integer", "default": 0, "description": "偏移量", "name": "offset", "in": "query" } ], "responses": { "200": { "description": "成功返回歌单列表", "schema": { "type": "object", "additionalProperties": true } }, "500": { "description": "服务器错误", "schema": { "type": "object", "additionalProperties": { "type": "string" } } } } }, "post": { "security": [ { "BearerAuth": [] } ], "description": "创建一个新的歌单", "consumes": [ "application/json" ], "produces": [ "application/json" ], "tags": [ "歌单管理" ], "summary": "创建歌单", "parameters": [ { "description": "歌单信息", "name": "request", "in": "body", "required": true, "schema": { "$ref": "#/definitions/models.Playlist" } } ], "responses": { "201": { "description": "创建成功", "schema": { "$ref": "#/definitions/models.Playlist" } }, "400": { "description": "请求数据错误", "schema": { "type": "object", "additionalProperties": { "type": "string" } } }, "500": { "description": "创建失败", "schema": { "type": "object", "additionalProperties": { "type": "string" } } } } } }, "/playlists/batch-delete": { "post": { "security": [ { "BearerAuth": [] } ], "description": "根据歌单 ID 列表批量删除歌单,内置歌单会被跳过", "consumes": [ "application/json" ], "produces": [ "application/json" ], "tags": [ "歌单管理" ], "summary": "批量删除歌单", "parameters": [ { "description": "批量删除请求", "name": "request", "in": "body", "required": true, "schema": { "$ref": "#/definitions/models.BatchDeletePlaylistsRequest" } } ], "responses": { "200": { "description": "删除成功", "schema": { "$ref": "#/definitions/models.BatchDeletePlaylistsResponse" } }, "400": { "description": "请求数据错误", "schema": { "type": "object", "additionalProperties": { "type": "string" } } }, "500": { "description": "删除失败", "schema": { "type": "object", "additionalProperties": { "type": "string" } } } } } }, "/playlists/export": { "get": { "security": [ { "BearerAuth": [] } ], "description": "导出所有歌单(含曲目关联)为 JSON 文件,响应头携带 Content-Disposition 直接触发浏览器下载", "produces": [ "application/json" ], "tags": [ "数据备份" ], "summary": "导出歌单备份", "responses": { "200": { "description": "歌单备份 JSON 文件", "schema": { "type": "file" } }, "500": { "description": "导出失败", "schema": { "type": "object", "additionalProperties": { "type": "string" } } } } } }, "/playlists/import": { "post": { "security": [ { "BearerAuth": [] } ], "description": "从 multipart/form-data 上传的 JSON 备份文件还原歌单。已存在的歌单按名称合并,曲目按内容匹配去重。", "consumes": [ "multipart/form-data" ], "produces": [ "application/json" ], "tags": [ "数据备份" ], "summary": "导入歌单备份", "parameters": [ { "type": "file", "description": "歌单备份 JSON 文件", "name": "file", "in": "formData", "required": true } ], "responses": { "200": { "description": "导入结果(新建/合并歌单数、新建/匹配曲目数)", "schema": { "$ref": "#/definitions/models.ImportResult" } }, "400": { "description": "请求格式错误或文件无效", "schema": { "type": "object", "additionalProperties": { "type": "string" } } }, "500": { "description": "导入失败", "schema": { "type": "object", "additionalProperties": { "type": "string" } } } } } }, "/playlists/reorder": { "put": { "security": [ { "BearerAuth": [] } ], "description": "重新排序歌单列表", "consumes": [ "application/json" ], "produces": [ "application/json" ], "tags": [ "歌单管理" ], "summary": "重新排序歌单列表", "parameters": [ { "description": "歌单 ID 列表", "name": "request", "in": "body", "required": true, "schema": { "type": "object", "properties": { "playlist_ids": { "type": "array", "items": { "type": "integer", "format": "int64" } } } } } ], "responses": { "200": { "description": "排序成功", "schema": { "type": "object", "additionalProperties": { "type": "string" } } }, "400": { "description": "请求数据错误", "schema": { "type": "object", "additionalProperties": { "type": "string" } } }, "500": { "description": "排序失败", "schema": { "type": "object", "additionalProperties": { "type": "string" } } } } } }, "/playlists/{id}": { "get": { "security": [ { "BearerAuth": [] } ], "description": "根据歌单ID获取详细信息", "consumes": [ "application/json" ], "produces": [ "application/json" ], "tags": [ "歌单管理" ], "summary": "获取单个歌单详情", "parameters": [ { "type": "integer", "description": "歌单ID", "name": "id", "in": "path", "required": true } ], "responses": { "200": { "description": "成功返回歌单详情", "schema": { "$ref": "#/definitions/models.Playlist" } }, "400": { "description": "无效的歌单ID", "schema": { "type": "object", "additionalProperties": { "type": "string" } } }, "404": { "description": "歌单不存在", "schema": { "type": "object", "additionalProperties": { "type": "string" } } } } }, "put": { "security": [ { "BearerAuth": [] } ], "description": "更新歌单信息。支持通过 cover_song_id 从指定歌曲复制封面,与 cover_path/cover_url 互斥且优先级更高", "consumes": [ "application/json" ], "produces": [ "application/json" ], "tags": [ "歌单管理" ], "summary": "更新歌单", "parameters": [ { "type": "integer", "description": "歌单ID", "name": "id", "in": "path", "required": true }, { "description": "歌单信息", "name": "request", "in": "body", "required": true, "schema": { "$ref": "#/definitions/models.UpdatePlaylistRequest" } } ], "responses": { "200": { "description": "更新成功", "schema": { "$ref": "#/definitions/models.Playlist" } }, "400": { "description": "请求数据错误", "schema": { "type": "object", "additionalProperties": { "type": "string" } } }, "500": { "description": "更新失败", "schema": { "type": "object", "additionalProperties": { "type": "string" } } } } }, "delete": { "security": [ { "BearerAuth": [] } ], "description": "根据歌单ID删除歌单", "consumes": [ "application/json" ], "produces": [ "application/json" ], "tags": [ "歌单管理" ], "summary": "删除歌单", "parameters": [ { "type": "integer", "description": "歌单ID", "name": "id", "in": "path", "required": true } ], "responses": { "200": { "description": "删除成功", "schema": { "type": "object", "additionalProperties": { "type": "string" } } }, "400": { "description": "无效的歌单ID", "schema": { "type": "object", "additionalProperties": { "type": "string" } } }, "500": { "description": "删除失败", "schema": { "type": "object", "additionalProperties": { "type": "string" } } } } } }, "/playlists/{id}/cover": { "get": { "security": [ { "BearerAuth": [] } ], "description": "返回歌单封面图片文件", "produces": [ "image/jpeg" ], "tags": [ "歌单管理" ], "summary": "获取歌单封面", "parameters": [ { "type": "integer", "description": "歌单ID", "name": "id", "in": "path", "required": true } ], "responses": { "200": { "description": "封面图片", "schema": { "type": "file" } }, "404": { "description": "封面不存在", "schema": { "type": "object", "additionalProperties": { "type": "string" } } }, "500": { "description": "读取失败", "schema": { "type": "object", "additionalProperties": { "type": "string" } } } } }, "post": { "security": [ { "BearerAuth": [] } ], "description": "上传本地图片作为歌单封面", "consumes": [ "multipart/form-data" ], "produces": [ "application/json" ], "tags": [ "歌单管理" ], "summary": "上传歌单封面", "parameters": [ { "type": "integer", "description": "歌单ID", "name": "id", "in": "path", "required": true }, { "type": "file", "description": "封面图片文件", "name": "file", "in": "formData", "required": true } ], "responses": { "200": { "description": "上传成功", "schema": { "$ref": "#/definitions/models.Playlist" } }, "400": { "description": "请求数据错误", "schema": { "type": "object", "additionalProperties": { "type": "string" } } }, "500": { "description": "上传失败", "schema": { "type": "object", "additionalProperties": { "type": "string" } } } } } }, "/playlists/{id}/songs": { "get": { "security": [ { "BearerAuth": [] } ], "description": "获取指定歌单中的歌曲,支持分页、排序和搜索", "consumes": [ "application/json" ], "produces": [ "application/json" ], "tags": [ "歌单管理" ], "summary": "获取歌单中的歌曲", "parameters": [ { "type": "integer", "description": "歌单 ID", "name": "id", "in": "path", "required": true }, { "type": "integer", "default": 20, "description": "每页数量", "name": "limit", "in": "query" }, { "type": "integer", "default": 0, "description": "偏移量", "name": "offset", "in": "query" }, { "type": "string", "description": "排序字段: position(默认)/added_at/title/artist/album/duration/updated_at/file_modified_at", "name": "sort", "in": "query" }, { "type": "string", "description": "排序方向: asc(默认)/desc", "name": "order", "in": "query" }, { "type": "string", "description": "搜索关键词(匹配标题/艺术家/专辑)", "name": "keyword", "in": "query" } ], "responses": { "200": { "description": "成功返回歌曲列表", "schema": { "type": "object", "additionalProperties": true } }, "400": { "description": "无效的歌单 ID", "schema": { "type": "object", "additionalProperties": { "type": "string" } } }, "500": { "description": "获取失败", "schema": { "type": "object", "additionalProperties": { "type": "string" } } } } }, "post": { "security": [ { "BearerAuth": [] } ], "description": "将多首歌曲添加到指定歌单,跳过已存在的歌曲", "consumes": [ "application/json" ], "produces": [ "application/json" ], "tags": [ "歌单管理" ], "summary": "批量添加歌曲到歌单", "parameters": [ { "type": "integer", "description": "歌单 ID", "name": "id", "in": "path", "required": true }, { "description": "歌曲 ID 列表", "name": "request", "in": "body", "required": true, "schema": { "type": "object", "properties": { "song_ids": { "type": "array", "items": { "type": "integer", "format": "int64" } } } } } ], "responses": { "200": { "description": "添加成功", "schema": { "type": "object", "additionalProperties": true } }, "400": { "description": "请求数据错误", "schema": { "type": "object", "additionalProperties": { "type": "string" } } }, "500": { "description": "添加失败", "schema": { "type": "object", "additionalProperties": { "type": "string" } } } } } }, "/playlists/{id}/songs/reorder": { "put": { "security": [ { "BearerAuth": [] } ], "description": "重新排序歌单中的歌曲", "consumes": [ "application/json" ], "produces": [ "application/json" ], "tags": [ "歌单管理" ], "summary": "重新排序歌单中的歌曲", "parameters": [ { "type": "integer", "description": "歌单 ID", "name": "id", "in": "path", "required": true }, { "description": "歌曲 ID 列表", "name": "request", "in": "body", "required": true, "schema": { "type": "object", "properties": { "song_ids": { "type": "array", "items": { "type": "integer", "format": "int64" } } } } } ], "responses": { "200": { "description": "排序成功", "schema": { "type": "object", "additionalProperties": { "type": "string" } } }, "400": { "description": "请求数据错误", "schema": { "type": "object", "additionalProperties": { "type": "string" } } }, "500": { "description": "排序失败", "schema": { "type": "object", "additionalProperties": { "type": "string" } } } } } }, "/playlists/{id}/songs/{songId}": { "delete": { "security": [ { "BearerAuth": [] } ], "description": "从指定歌单移除歌曲", "consumes": [ "application/json" ], "produces": [ "application/json" ], "tags": [ "歌单管理" ], "summary": "从歌单移除歌曲", "parameters": [ { "type": "integer", "description": "歌单 ID", "name": "id", "in": "path", "required": true }, { "type": "integer", "description": "歌曲 ID", "name": "songId", "in": "path", "required": true } ], "responses": { "200": { "description": "移除成功", "schema": { "type": "object", "additionalProperties": { "type": "string" } } }, "400": { "description": "请求数据错误", "schema": { "type": "object", "additionalProperties": { "type": "string" } } }, "500": { "description": "移除失败", "schema": { "type": "object", "additionalProperties": { "type": "string" } } } } } }, "/playlists/{id}/touch": { "post": { "security": [ { "BearerAuth": [] } ], "description": "仅更新歌单的 updated_at 字段,用于记录最后播放时间", "consumes": [ "application/json" ], "produces": [ "application/json" ], "tags": [ "歌单管理" ], "summary": "更新歌单最后播放时间", "parameters": [ { "type": "integer", "description": "歌单ID", "name": "id", "in": "path", "required": true } ], "responses": { "200": { "description": "更新成功", "schema": { "type": "object", "additionalProperties": { "type": "string" } } }, "400": { "description": "无效的歌单ID", "schema": { "type": "object", "additionalProperties": { "type": "string" } } }, "500": { "description": "更新失败", "schema": { "type": "object", "additionalProperties": { "type": "string" } } } } } }, "/playlists/{id}/visibility": { "put": { "security": [ { "BearerAuth": [] } ], "description": "切换歌单的隐藏状态。内置歌单(收藏、电台收藏)不允许隐藏", "consumes": [ "application/json" ], "produces": [ "application/json" ], "tags": [ "歌单管理" ], "summary": "设置歌单可见性", "parameters": [ { "type": "integer", "description": "歌单ID", "name": "id", "in": "path", "required": true }, { "description": "可见性设置", "name": "request", "in": "body", "required": true, "schema": { "$ref": "#/definitions/models.SetPlaylistVisibilityRequest" } } ], "responses": { "200": { "description": "更新后的歌单", "schema": { "$ref": "#/definitions/models.Playlist" } }, "400": { "description": "请求错误或内置歌单不可隐藏", "schema": { "type": "object", "additionalProperties": { "type": "string" } } }, "404": { "description": "歌单不存在", "schema": { "type": "object", "additionalProperties": { "type": "string" } } }, "500": { "description": "服务器错误", "schema": { "type": "object", "additionalProperties": { "type": "string" } } } } } }, "/plugins/health": { "get": { "security": [ { "BearerAuth": [] } ], "description": "返回各音乐源插件的下载成功率、健康度分类(green/yellow/red)与最近 5 条失败原因。", "produces": [ "application/json" ], "tags": [ "JS插件管理" ], "summary": "音源健康度", "responses": { "200": { "description": "OK", "schema": { "$ref": "#/definitions/handlers.pluginHealthResponse" } } } } }, "/proxy": { "get": { "security": [ { "BearerAuth": [] } ], "description": "代理外部资源(图片、音频、视频流等),解决浏览器 CORS 限制。支持流式转发、Range 请求透传、Content-Type 透传和域名白名单校验", "produces": [ "application/octet-stream" ], "tags": [ "资源代理" ], "summary": "代理外部资源", "parameters": [ { "type": "string", "description": "目标资源的 URL(URL 编码)", "name": "url", "in": "query", "required": true } ], "responses": { "200": { "description": "代理的资源内容", "schema": { "type": "file" } }, "206": { "description": "部分内容(Range 请求)", "schema": { "type": "file" } }, "400": { "description": "缺少 url 参数或 URL 无效", "schema": { "type": "object", "additionalProperties": { "type": "string" } } }, "403": { "description": "域名不在白名单中", "schema": { "type": "object", "additionalProperties": { "type": "string" } } }, "502": { "description": "上游请求失败", "schema": { "type": "object", "additionalProperties": { "type": "string" } } } } } }, "/scan": { "post": { "security": [ { "BearerAuth": [] } ], "description": "异步扫描音乐目录并导入新发现的音乐文件到数据库,立即返回,可通过进度接口查询状态。\nreimport=true 时对已入库文件也重新提取元数据;默认 false 走增量(跳过已存在且时长有效的文件)。\npaths 为目录级定向扫描(Issue #262):省略/为空时扫描整个音乐根目录;非空时只扫描给定目录(含子目录),\n且过期记录清理仅收敛到这些目录之内(不影响其余曲库)。每个目录必须位于音乐根目录之下,否则返回 400。", "consumes": [ "application/json" ], "produces": [ "application/json" ], "tags": [ "扫描管理" ], "summary": "扫描并导入本地音乐", "parameters": [ { "description": "扫描请求参数", "name": "request", "in": "body", "schema": { "$ref": "#/definitions/handlers.ScanRequest" } } ], "responses": { "200": { "description": "扫描任务已启动", "schema": { "type": "object", "additionalProperties": true } }, "400": { "description": "指定目录不在音乐目录下", "schema": { "type": "object", "additionalProperties": { "type": "string" } } }, "409": { "description": "扫描正在进行中", "schema": { "type": "object", "additionalProperties": { "type": "string" } } }, "500": { "description": "启动扫描失败", "schema": { "type": "object", "additionalProperties": { "type": "string" } } } } } }, "/scan/cancel": { "post": { "security": [ { "BearerAuth": [] } ], "description": "取消正在进行的扫描任务", "consumes": [ "application/json" ], "produces": [ "application/json" ], "tags": [ "扫描管理" ], "summary": "取消扫描", "responses": { "200": { "description": "取消成功", "schema": { "type": "object", "additionalProperties": true } }, "400": { "description": "没有正在进行的扫描任务", "schema": { "type": "object", "additionalProperties": { "type": "string" } } } } } }, "/scan/dir-names": { "get": { "security": [ { "BearerAuth": [] } ], "description": "递归收集音乐目录下所有唯一的目录名称,按字母排序返回,用于排除目录名称的自动补全", "consumes": [ "application/json" ], "produces": [ "application/json" ], "tags": [ "扫描管理" ], "summary": "获取所有目录名称", "responses": { "200": { "description": "目录名称列表", "schema": { "type": "object", "additionalProperties": true } }, "500": { "description": "收集目录名称失败", "schema": { "type": "object", "additionalProperties": { "type": "string" } } } } } }, "/scan/directories": { "get": { "security": [ { "BearerAuth": [] } ], "description": "返回指定路径下的一级子目录列表,用于目录树懒加载。path 为空时返回音乐根目录下的子目录", "consumes": [ "application/json" ], "produces": [ "application/json" ], "tags": [ "扫描管理" ], "summary": "获取子目录列表", "parameters": [ { "type": "string", "description": "目录路径(为空时使用音乐根目录)", "name": "path", "in": "query" } ], "responses": { "200": { "description": "子目录列表", "schema": { "type": "object", "additionalProperties": true } }, "400": { "description": "无效的路径", "schema": { "type": "object", "additionalProperties": { "type": "string" } } }, "500": { "description": "读取目录失败", "schema": { "type": "object", "additionalProperties": { "type": "string" } } } } } }, "/scan/fingerprints": { "post": { "security": [ { "BearerAuth": [] } ], "description": "异步为本地歌曲计算音频指纹,需要 ffmpeg 支持 chromaprint。若已有任务在运行则打断重启。传入 recompute_all=true 时清空已有指纹后重新计算全部。", "consumes": [ "application/json" ], "produces": [ "application/json" ], "tags": [ "扫描管理" ], "summary": "触发批量指纹计算", "parameters": [ { "description": "计算选项", "name": "request", "in": "body", "schema": { "$ref": "#/definitions/handlers.startFingerprintRequest" } } ], "responses": { "200": { "description": "任务已启动", "schema": { "type": "object", "additionalProperties": true } }, "400": { "description": "chromaprint 不可用", "schema": { "type": "object", "additionalProperties": { "type": "string" } } } } } }, "/scan/fingerprints/progress": { "get": { "security": [ { "BearerAuth": [] } ], "description": "查询当前指纹计算任务的进度", "produces": [ "application/json" ], "tags": [ "扫描管理" ], "summary": "获取指纹计算进度", "responses": { "200": { "description": "计算进度", "schema": { "$ref": "#/definitions/services.FingerprintProgress" } } } } }, "/scan/fingerprints/status": { "get": { "security": [ { "BearerAuth": [] } ], "description": "返回 ffmpeg chromaprint 可用性以及本地歌曲指纹计算统计", "produces": [ "application/json" ], "tags": [ "扫描管理" ], "summary": "获取指纹计算状态", "responses": { "200": { "description": "指纹状态", "schema": { "type": "object", "additionalProperties": true } } } } }, "/scan/progress": { "get": { "security": [ { "BearerAuth": [] } ], "description": "获取当前扫描任务的进度信息", "consumes": [ "application/json" ], "produces": [ "application/json" ], "tags": [ "扫描管理" ], "summary": "获取扫描进度", "responses": { "200": { "description": "扫描进度信息", "schema": { "$ref": "#/definitions/services.ScanProgress" } } } } }, "/settings/auto-scan": { "get": { "security": [ { "BearerAuth": [] } ], "description": "返回自动扫描的启用状态和扫描间隔(秒)。默认关闭,间隔 3600 秒(1 小时)。", "produces": [ "application/json" ], "tags": [ "扫描管理" ], "summary": "获取自动扫描配置", "responses": { "200": { "description": "OK", "schema": { "$ref": "#/definitions/handlers.AutoScanSetting" } } } }, "put": { "security": [ { "BearerAuth": [] } ], "description": "设置自动扫描的启用状态和扫描间隔。interval_seconds 有效范围 [60, 86400]。更新后立即生效(无需重启)。", "consumes": [ "application/json" ], "produces": [ "application/json" ], "tags": [ "扫描管理" ], "summary": "更新自动扫描配置", "parameters": [ { "description": "自动扫描配置", "name": "request", "in": "body", "required": true, "schema": { "$ref": "#/definitions/handlers.AutoScanSetting" } } ], "responses": { "200": { "description": "OK", "schema": { "$ref": "#/definitions/handlers.AutoScanSetting" } }, "400": { "description": "请求格式错误或参数无效", "schema": { "type": "object", "additionalProperties": { "type": "string" } } }, "500": { "description": "保存配置失败", "schema": { "type": "object", "additionalProperties": { "type": "string" } } } } } }, "/settings/equalizer": { "get": { "security": [ { "BearerAuth": [] } ], "description": "获取全局均衡器(EQ)配置,包含启用状态、预设名称和 10 段频段增益(31Hz–16kHz,单位 dB,范围 -12 ~ +12)。未配置时返回默认值(关闭 + flat 预设 + 全 0)。", "produces": [ "application/json" ], "tags": [ "设置" ], "summary": "获取均衡器配置", "responses": { "200": { "description": "均衡器配置", "schema": { "$ref": "#/definitions/handlers.equalizerSetting" } } } }, "put": { "security": [ { "BearerAuth": [] } ], "description": "保存全局均衡器(EQ)配置。bands 必须包含 10 个元素,每个值在 -12 ~ +12 范围内(单位 dB)。preset 为预设名称(flat/rock/pop/jazz/classical/bass_boost/treble_boost/vocal/custom)。", "consumes": [ "application/json" ], "produces": [ "application/json" ], "tags": [ "设置" ], "summary": "保存均衡器配置", "parameters": [ { "description": "均衡器配置", "name": "request", "in": "body", "required": true, "schema": { "$ref": "#/definitions/handlers.equalizerSetting" } } ], "responses": { "200": { "description": "保存后的均衡器配置", "schema": { "$ref": "#/definitions/handlers.equalizerSetting" } }, "400": { "description": "请求格式错误", "schema": { "$ref": "#/definitions/models.ErrorResponse" } }, "500": { "description": "保存配置失败", "schema": { "$ref": "#/definitions/models.ErrorResponse" } } } } }, "/settings/github-proxy": { "get": { "security": [ { "BearerAuth": [] } ], "description": "获取检查更新 / 升级时使用的 GitHub 代理前缀(如 https://ghfast.top/)。前端会记住上次使用的代理并在检查更新时自动带上。未配置时返回空字符串(直连)。", "produces": [ "application/json" ], "tags": [ "系统升级" ], "summary": "获取 GitHub 更新代理配置", "responses": { "200": { "description": "GitHub 更新代理配置", "schema": { "$ref": "#/definitions/handlers.githubProxySetting" } } } }, "put": { "security": [ { "BearerAuth": [] } ], "description": "设置检查更新 / 升级使用的 GitHub 代理前缀(如 https://ghfast.top/)。设为空字符串则直连。仅持久化,不影响其它模块的全局 HTTP 代理。", "consumes": [ "application/json" ], "produces": [ "application/json" ], "tags": [ "系统升级" ], "summary": "保存 GitHub 更新代理配置", "parameters": [ { "description": "GitHub 更新代理配置", "name": "request", "in": "body", "required": true, "schema": { "$ref": "#/definitions/handlers.githubProxySetting" } } ], "responses": { "200": { "description": "保存后的配置", "schema": { "$ref": "#/definitions/handlers.githubProxySetting" } }, "400": { "description": "请求格式错误", "schema": { "$ref": "#/definitions/models.ErrorResponse" } }, "500": { "description": "保存配置失败", "schema": { "$ref": "#/definitions/models.ErrorResponse" } } } } }, "/settings/hls-proxy": { "get": { "security": [ { "BearerAuth": [] } ], "description": "获取“HLS 电台流通过本机反代回客户端”开关的当前状态", "produces": [ "application/json" ], "tags": [ "电台与 HLS" ], "summary": "获取 HLS 代理开关", "responses": { "200": { "description": "返回 enabled 字段表示开关状态", "schema": { "type": "object", "additionalProperties": { "type": "boolean" } } } } }, "put": { "security": [ { "BearerAuth": [] } ], "description": "开启/关闭 HLS 反代。开启时电台切片字节全部经本机转发(解决源站 Referer/CORS 拦截),关闭时仅 302 给 player。", "consumes": [ "application/json" ], "produces": [ "application/json" ], "tags": [ "电台与 HLS" ], "summary": "更新 HLS 代理开关", "parameters": [ { "description": "开关请求", "name": "request", "in": "body", "required": true, "schema": { "$ref": "#/definitions/handlers.hlsProxySettingRequest" } } ], "responses": { "200": { "description": "返回 enabled 字段表示更新后的开关状态", "schema": { "type": "object", "additionalProperties": { "type": "boolean" } } }, "400": { "description": "请求格式错误", "schema": { "type": "object", "additionalProperties": { "type": "string" } } }, "500": { "description": "保存配置失败", "schema": { "type": "object", "additionalProperties": { "type": "string" } } } } } }, "/settings/http-proxy": { "get": { "security": [ { "BearerAuth": [] } ], "description": "获取全局 HTTP 代理地址。所有后端外发请求(插件下载、注册表拉取、升级检查等)会通过此代理转发。未配置时返回空字符串(直连)。", "produces": [ "application/json" ], "tags": [ "设置" ], "summary": "获取 HTTP 代理配置", "responses": { "200": { "description": "代理配置", "schema": { "$ref": "#/definitions/handlers.httpProxySetting" } } } }, "put": { "security": [ { "BearerAuth": [] } ], "description": "设置全局 HTTP 代理地址(如 http://192.168.1.1:7890)。设为空字符串则关闭代理。保存后即时生效,无需重启。", "consumes": [ "application/json" ], "produces": [ "application/json" ], "tags": [ "设置" ], "summary": "保存 HTTP 代理配置", "parameters": [ { "description": "代理配置", "name": "request", "in": "body", "required": true, "schema": { "$ref": "#/definitions/handlers.httpProxySetting" } } ], "responses": { "200": { "description": "保存后的代理配置", "schema": { "$ref": "#/definitions/handlers.httpProxySetting" } }, "400": { "description": "请求格式错误或代理地址无效", "schema": { "$ref": "#/definitions/models.ErrorResponse" } }, "500": { "description": "保存配置失败", "schema": { "$ref": "#/definitions/models.ErrorResponse" } } } } }, "/settings/log-level": { "get": { "security": [ { "BearerAuth": [] } ], "description": "返回当前 slog 全局日志等级", "produces": [ "application/json" ], "tags": [ "设置" ], "summary": "获取日志等级", "responses": { "200": { "description": "返回 level 字段:debug/info/warn/error", "schema": { "type": "object", "additionalProperties": { "type": "string" } } } } }, "put": { "security": [ { "BearerAuth": [] } ], "description": "切换 slog 全局日志等级并持久化。新等级即时生效,重启后从 DB 恢复。", "consumes": [ "application/json" ], "produces": [ "application/json" ], "tags": [ "设置" ], "summary": "更新日志等级", "parameters": [ { "description": "等级请求(debug/info/warn/error)", "name": "request", "in": "body", "required": true, "schema": { "$ref": "#/definitions/handlers.logLevelSettingRequest" } } ], "responses": { "200": { "description": "返回更新后的 level", "schema": { "type": "object", "additionalProperties": { "type": "string" } } }, "400": { "description": "请求格式错误或等级非法", "schema": { "type": "object", "additionalProperties": { "type": "string" } } }, "500": { "description": "保存配置失败", "schema": { "type": "object", "additionalProperties": { "type": "string" } } } } } }, "/settings/music-path": { "get": { "security": [ { "BearerAuth": [] } ], "produces": [ "application/json" ], "tags": [ "扫描管理" ], "summary": "获取音乐路径与扫描排除配置", "responses": { "200": { "description": "OK", "schema": { "$ref": "#/definitions/handlers.MusicPathSetting" } } } }, "put": { "security": [ { "BearerAuth": [] } ], "description": "写入 music_path 配置并触发 Scanner 重建 + 清理排除目录中的歌曲(与 admin /configs PUT 的副作用一致)。", "consumes": [ "application/json" ], "produces": [ "application/json" ], "tags": [ "扫描管理" ], "summary": "更新音乐路径与扫描排除配置", "parameters": [ { "description": "配置内容", "name": "request", "in": "body", "required": true, "schema": { "$ref": "#/definitions/handlers.MusicPathSetting" } } ], "responses": { "200": { "description": "OK", "schema": { "$ref": "#/definitions/handlers.MusicPathSetting" } }, "400": { "description": "请求格式错误", "schema": { "type": "object", "additionalProperties": { "type": "string" } } }, "500": { "description": "保存配置失败", "schema": { "type": "object", "additionalProperties": { "type": "string" } } } } } }, "/settings/plugin-auto-update": { "get": { "security": [ { "BearerAuth": [] } ], "description": "获取“后台自动更新已安装插件”开关的当前状态。开启后,服务会在启动后延迟数分钟检查一次、之后每 6 小时定时检查所有具有远程更新源的插件并自动更新。默认关闭。", "produces": [ "application/json" ], "tags": [ "设置" ], "summary": "获取插件自动更新开关", "responses": { "200": { "description": "返回 enabled 字段表示开关状态", "schema": { "$ref": "#/definitions/handlers.pluginAutoUpdateSetting" } } } }, "put": { "security": [ { "BearerAuth": [] } ], "description": "开启/关闭插件后台自动更新。开启后后台 ticker 会定时对有更新源的插件执行“检查更新 + 下载安装 + 热重载”。开关即时生效,无需重启。", "consumes": [ "application/json" ], "produces": [ "application/json" ], "tags": [ "设置" ], "summary": "保存插件自动更新开关", "parameters": [ { "description": "开关请求", "name": "request", "in": "body", "required": true, "schema": { "$ref": "#/definitions/handlers.pluginAutoUpdateSetting" } } ], "responses": { "200": { "description": "返回 enabled 字段表示更新后的开关状态", "schema": { "$ref": "#/definitions/handlers.pluginAutoUpdateSetting" } }, "400": { "description": "请求格式错误", "schema": { "$ref": "#/definitions/models.ErrorResponse" } }, "500": { "description": "保存配置失败", "schema": { "$ref": "#/definitions/models.ErrorResponse" } } } } }, "/settings/plugin-keep-alive": { "get": { "security": [ { "BearerAuth": [] } ], "description": "获取不会被自动休眠的插件 entryPath 列表。白名单中的插件即使空闲超过 10 分钟也不会被卸载。未配置时返回空列表。", "produces": [ "application/json" ], "tags": [ "设置" ], "summary": "获取插件常驻白名单", "responses": { "200": { "description": "常驻白名单", "schema": { "$ref": "#/definitions/handlers.pluginKeepAliveSetting" } } } }, "put": { "security": [ { "BearerAuth": [] } ], "description": "设置不会被自动休眠的插件 entryPath 列表。保存后即时生效,白名单中的插件将跳过空闲检查,始终保持运行。", "consumes": [ "application/json" ], "produces": [ "application/json" ], "tags": [ "设置" ], "summary": "保存插件常驻白名单", "parameters": [ { "description": "常驻白名单", "name": "request", "in": "body", "required": true, "schema": { "$ref": "#/definitions/handlers.pluginKeepAliveSetting" } } ], "responses": { "200": { "description": "保存后的白名单", "schema": { "$ref": "#/definitions/handlers.pluginKeepAliveSetting" } }, "400": { "description": "请求格式错误", "schema": { "$ref": "#/definitions/models.ErrorResponse" } }, "500": { "description": "保存配置失败", "schema": { "$ref": "#/definitions/models.ErrorResponse" } } } } }, "/settings/plugin-registries": { "get": { "security": [ { "BearerAuth": [] } ], "description": "获取用户保存的所有插件注册表订阅源 URL。未配置时返回空列表。", "produces": [ "application/json" ], "tags": [ "JS插件管理" ], "summary": "获取插件订阅源列表", "responses": { "200": { "description": "订阅源列表", "schema": { "$ref": "#/definitions/handlers.pluginRegistriesSetting" } } } }, "put": { "security": [ { "BearerAuth": [] } ], "description": "保存用户配置的插件注册表订阅源 URL 列表。每个源包含 URL、名称和是否启用。", "consumes": [ "application/json" ], "produces": [ "application/json" ], "tags": [ "JS插件管理" ], "summary": "保存插件订阅源列表", "parameters": [ { "description": "订阅源列表", "name": "request", "in": "body", "required": true, "schema": { "$ref": "#/definitions/handlers.pluginRegistriesSetting" } } ], "responses": { "200": { "description": "保存后的订阅源列表", "schema": { "$ref": "#/definitions/handlers.pluginRegistriesSetting" } }, "400": { "description": "请求格式错误", "schema": { "$ref": "#/definitions/models.ErrorResponse" } }, "500": { "description": "保存配置失败", "schema": { "$ref": "#/definitions/models.ErrorResponse" } } } } }, "/settings/remote-title-source": { "get": { "security": [ { "BearerAuth": [] } ], "description": "tag:元数据刷新时用音频标签覆盖标题;filename(默认):保持文件名作为标题,不覆盖。", "produces": [ "application/json" ], "tags": [ "歌曲管理" ], "summary": "获取网络歌曲标题来源配置", "responses": { "200": { "description": "返回 title_source 字段", "schema": { "$ref": "#/definitions/handlers.remoteTitleSourceRequest" } } } }, "put": { "security": [ { "BearerAuth": [] } ], "description": "tag:元数据刷新时用音频标签覆盖标题;filename(默认):保持文件名作为标题,不覆盖。", "consumes": [ "application/json" ], "produces": [ "application/json" ], "tags": [ "歌曲管理" ], "summary": "更新网络歌曲标题来源配置", "parameters": [ { "description": "标题来源配置", "name": "request", "in": "body", "required": true, "schema": { "$ref": "#/definitions/handlers.remoteTitleSourceRequest" } } ], "responses": { "200": { "description": "返回 title_source 字段", "schema": { "$ref": "#/definitions/handlers.remoteTitleSourceRequest" } }, "400": { "description": "请求格式错误或参数无效", "schema": { "type": "object", "additionalProperties": { "type": "string" } } }, "500": { "description": "保存配置失败", "schema": { "type": "object", "additionalProperties": { "type": "string" } } } } } }, "/settings/scan-auto-create-playlists": { "get": { "security": [ { "BearerAuth": [] } ], "description": "控制扫描完成后是否根据音乐目录结构自动创建歌单。默认启用(true)。关闭后扫描仅入库歌曲,不再自动建歌单。", "produces": [ "application/json" ], "tags": [ "扫描管理" ], "summary": "获取「扫描后自动创建歌单」开关", "responses": { "200": { "description": "返回 enabled 字段", "schema": { "type": "object", "additionalProperties": { "type": "boolean" } } } } }, "put": { "security": [ { "BearerAuth": [] } ], "description": "控制扫描完成后是否根据音乐目录结构自动创建歌单。", "consumes": [ "application/json" ], "produces": [ "application/json" ], "tags": [ "扫描管理" ], "summary": "更新「扫描后自动创建歌单」开关", "parameters": [ { "description": "开关请求", "name": "request", "in": "body", "required": true, "schema": { "$ref": "#/definitions/handlers.scanAutoCreatePlaylistsRequest" } } ], "responses": { "200": { "description": "返回 enabled 字段", "schema": { "type": "object", "additionalProperties": { "type": "boolean" } } }, "400": { "description": "请求格式错误", "schema": { "type": "object", "additionalProperties": { "type": "string" } } }, "500": { "description": "保存配置失败", "schema": { "type": "object", "additionalProperties": { "type": "string" } } } } } }, "/settings/scan-playlist-mode": { "get": { "security": [ { "BearerAuth": [] } ], "description": "返回扫描后自动创建歌单的目录归并模式。directory:每个文件夹生成独立歌单;top_level:按一级子目录合并歌单;bubble_up:歌曲同时出现在所有上级文件夹歌单。默认 directory。", "produces": [ "application/json" ], "tags": [ "扫描管理" ], "summary": "获取歌单创建方式", "responses": { "200": { "description": "返回 mode 字段", "schema": { "$ref": "#/definitions/handlers.scanPlaylistModeResponse" } } } }, "put": { "security": [ { "BearerAuth": [] } ], "description": "设置扫描后自动创建歌单的目录归并模式。directory:每个文件夹生成独立歌单;top_level:按一级子目录合并歌单;bubble_up:歌曲同时出现在所有上级文件夹歌单。", "consumes": [ "application/json" ], "produces": [ "application/json" ], "tags": [ "扫描管理" ], "summary": "更新歌单创建方式", "parameters": [ { "description": "模式请求", "name": "request", "in": "body", "required": true, "schema": { "$ref": "#/definitions/handlers.scanPlaylistModeRequest" } } ], "responses": { "200": { "description": "返回 mode 字段", "schema": { "$ref": "#/definitions/handlers.scanPlaylistModeResponse" } }, "400": { "description": "请求格式错误或 mode 值非法", "schema": { "type": "object", "additionalProperties": { "type": "string" } } }, "500": { "description": "保存配置失败", "schema": { "type": "object", "additionalProperties": { "type": "string" } } } } } }, "/settings/scan-title-source": { "get": { "security": [ { "BearerAuth": [] } ], "description": "tag:优先使用音频标签中的标题(默认);filename:始终使用文件名(不含扩展名)作为标题。切换后需以「重新导入」模式扫描才能生效。", "produces": [ "application/json" ], "tags": [ "扫描管理" ], "summary": "获取扫描标题来源配置", "responses": { "200": { "description": "返回 title_source 字段", "schema": { "$ref": "#/definitions/handlers.scanTitleSourceRequest" } } } }, "put": { "security": [ { "BearerAuth": [] } ], "description": "tag:优先使用音频标签中的标题;filename:始终使用文件名(不含扩展名)作为标题。切换后需以「重新导入」模式扫描才能生效。", "consumes": [ "application/json" ], "produces": [ "application/json" ], "tags": [ "扫描管理" ], "summary": "更新扫描标题来源配置", "parameters": [ { "description": "标题来源配置", "name": "request", "in": "body", "required": true, "schema": { "$ref": "#/definitions/handlers.scanTitleSourceRequest" } } ], "responses": { "200": { "description": "返回 title_source 字段", "schema": { "$ref": "#/definitions/handlers.scanTitleSourceRequest" } }, "400": { "description": "请求格式错误或参数无效", "schema": { "type": "object", "additionalProperties": { "type": "string" } } }, "500": { "description": "保存配置失败", "schema": { "type": "object", "additionalProperties": { "type": "string" } } } } } }, "/settings/tab-config": { "get": { "security": [ { "BearerAuth": [] } ], "description": "获取用户自定义的底部导航栏 Tab 配置。首页和设置固定显示,歌曲库和歌单可关闭,可选项(歌曲库+歌单+插件 Tab)总数不超过 10 个。未配置时返回默认值(4 个 Tab:首页、歌曲库、歌单、设置)。移动端超过 5 个时自动折叠到「更多」菜单,桌面端侧边栏可全部展示。", "produces": [ "application/json" ], "tags": [ "设置" ], "summary": "获取底部导航栏 Tab 配置", "responses": { "200": { "description": "Tab 配置", "schema": { "$ref": "#/definitions/handlers.tabConfigSetting" } } } }, "put": { "security": [ { "BearerAuth": [] } ], "description": "保存用户自定义的底部导航栏 Tab 配置。首页和设置固定显示(不在配置中),可选项为歌曲库、歌单和插件 Tab,可选项总数不超过 10 个。每个插件 Tab 的 entry_path 和 name 不能为空,且不能重复。移动端超过 5 个时自动折叠到「更多」菜单。", "consumes": [ "application/json" ], "produces": [ "application/json" ], "tags": [ "设置" ], "summary": "保存底部导航栏 Tab 配置", "parameters": [ { "description": "Tab 配置", "name": "request", "in": "body", "required": true, "schema": { "$ref": "#/definitions/handlers.tabConfigSetting" } } ], "responses": { "200": { "description": "保存后的 Tab 配置", "schema": { "$ref": "#/definitions/handlers.tabConfigSetting" } }, "400": { "description": "请求格式错误或校验失败", "schema": { "$ref": "#/definitions/models.ErrorResponse" } }, "500": { "description": "保存配置失败", "schema": { "$ref": "#/definitions/models.ErrorResponse" } } } } }, "/settings/user-preferences": { "get": { "security": [ { "BearerAuth": [] } ], "description": "获取用户跨设备同步的偏好设置,包括主题、播放模式、视图模式、音质、缓存上限和音量。未配置时返回默认值。", "produces": [ "application/json" ], "tags": [ "设置" ], "summary": "获取用户偏好设置", "responses": { "200": { "description": "用户偏好设置", "schema": { "$ref": "#/definitions/handlers.userPreferencesSetting" } } } }, "put": { "security": [ { "BearerAuth": [] } ], "description": "保存用户跨设备同步的偏好设置。客户端登录后拉取、修改偏好时推送,实现多设备间偏好同步。", "consumes": [ "application/json" ], "produces": [ "application/json" ], "tags": [ "设置" ], "summary": "保存用户偏好设置", "parameters": [ { "description": "用户偏好设置", "name": "request", "in": "body", "required": true, "schema": { "$ref": "#/definitions/handlers.userPreferencesSetting" } } ], "responses": { "200": { "description": "保存后的用户偏好设置", "schema": { "$ref": "#/definitions/handlers.userPreferencesSetting" } }, "400": { "description": "请求格式错误", "schema": { "$ref": "#/definitions/models.ErrorResponse" } }, "500": { "description": "保存配置失败", "schema": { "$ref": "#/definitions/models.ErrorResponse" } } } } }, "/songs": { "get": { "security": [ { "BearerAuth": [] } ], "description": "获取歌曲列表,支持按类型过滤、关键词搜索和分页。默认排除隐藏歌单里的歌,传 exclude_playlist_labels=none 显示全部", "consumes": [ "application/json" ], "produces": [ "application/json" ], "tags": [ "歌曲管理" ], "summary": "获取歌曲列表", "parameters": [ { "enum": [ "local", "remote", "radio" ], "type": "string", "description": "歌曲类型", "name": "type", "in": "query" }, { "type": "string", "description": "搜索关键词", "name": "keyword", "in": "query" }, { "type": "string", "description": "按 file_path 前缀过滤(如 music/Pop)", "name": "path_prefix", "in": "query" }, { "type": "string", "description": "按流派精确过滤", "name": "genre", "in": "query" }, { "type": "string", "description": "按歌手精确过滤", "name": "artist", "in": "query" }, { "type": "string", "description": "按专辑精确过滤", "name": "album", "in": "query" }, { "type": "string", "description": "按语种精确过滤", "name": "language", "in": "query" }, { "type": "string", "description": "按风格精确过滤", "name": "style", "in": "query" }, { "type": "integer", "description": "按发行年份精确过滤", "name": "year", "in": "query" }, { "type": "integer", "description": "按年代过滤(起始年,如 1990 匹配 1990-1999)", "name": "decade", "in": "query" }, { "type": "string", "default": "hidden", "description": "排除属于这些 label 歌单的歌曲(逗号分隔), 默认 hidden; 传 none 显示全部", "name": "exclude_playlist_labels", "in": "query" }, { "type": "integer", "default": 20, "description": "每页数量", "name": "limit", "in": "query" }, { "type": "integer", "default": 0, "description": "偏移量", "name": "offset", "in": "query" }, { "enum": [ "id", "title", "artist", "album", "duration", "added_at", "updated_at", "file_modified_at", "year", "genre" ], "type": "string", "description": "排序字段,缺省 added_at", "name": "sort", "in": "query" }, { "enum": [ "asc", "desc" ], "type": "string", "description": "排序方向,缺省 desc", "name": "order", "in": "query" } ], "responses": { "200": { "description": "成功返回歌曲列表", "schema": { "type": "object", "additionalProperties": true } }, "500": { "description": "服务器错误", "schema": { "type": "object", "additionalProperties": { "type": "string" } } } } } }, "/songs/batch-delete": { "post": { "security": [ { "BearerAuth": [] } ], "description": "根据歌曲 ID 列表批量删除歌曲。设置 delete_files=true 时同步删除本地音频文件(用于去重等场景)", "consumes": [ "application/json" ], "produces": [ "application/json" ], "tags": [ "歌曲管理" ], "summary": "批量删除歌曲", "parameters": [ { "description": "批量删除请求", "name": "request", "in": "body", "required": true, "schema": { "$ref": "#/definitions/models.BatchDeleteSongsRequest" } } ], "responses": { "200": { "description": "删除成功", "schema": { "$ref": "#/definitions/models.BatchDeleteSongsResponse" } }, "400": { "description": "请求数据错误", "schema": { "type": "object", "additionalProperties": { "type": "string" } } }, "500": { "description": "删除失败", "schema": { "type": "object", "additionalProperties": { "type": "string" } } } } } }, "/songs/clean": { "post": { "security": [ { "BearerAuth": [] } ], "description": "清理本地歌曲中文件已不存在或位于排除目录中的记录,同时删除关联的封面文件", "consumes": [ "application/json" ], "produces": [ "application/json" ], "tags": [ "歌曲管理" ], "summary": "清理无效的本地歌曲", "responses": { "200": { "description": "清理成功", "schema": { "type": "object", "additionalProperties": true } }, "500": { "description": "清理失败", "schema": { "type": "object", "additionalProperties": { "type": "string" } } } } } }, "/songs/duplicates": { "get": { "security": [ { "BearerAuth": [] } ], "description": "通过音频指纹查询本地歌曲中内容相同的重复组", "produces": [ "application/json" ], "tags": [ "歌曲管理" ], "summary": "获取重复歌曲组", "responses": { "200": { "description": "重复歌曲组列表", "schema": { "type": "object", "additionalProperties": true } } } } }, "/songs/facets": { "get": { "security": [ { "BearerAuth": [] } ], "description": "按指定维度聚合曲库,返回该维度下所有非空取值及各自的歌曲数量,用于「分类浏览」的清单页。\n支持维度:genre(流派)/artist(歌手)/album(专辑)/language(语种)/style(风格)/year(年份)/decade(年代)。\nyear/decade 的 value 为数字字符串(年代如 \"1990\" 表示 1990-1999)。取到某取值后可用 /songs?\u003cfield\u003e=\u003cvalue\u003e 拉取该分类下歌曲。", "produces": [ "application/json" ], "tags": [ "歌曲管理" ], "summary": "曲库标签分类聚合", "parameters": [ { "enum": [ "genre", "artist", "album", "language", "style", "year", "decade" ], "type": "string", "description": "聚合维度", "name": "field", "in": "query", "required": true } ], "responses": { "200": { "description": "成功返回聚合结果 {field, facets:[{value,count}]}", "schema": { "type": "object", "additionalProperties": true } }, "400": { "description": "缺少或不支持的 field", "schema": { "type": "object", "additionalProperties": { "type": "string" } } }, "500": { "description": "服务器错误", "schema": { "type": "object", "additionalProperties": { "type": "string" } } } } } }, "/songs/ids": { "get": { "security": [ { "BearerAuth": [] } ], "description": "与 /songs 共享过滤条件,仅返回 ID。用于「全选当前筛选范围」场景。", "consumes": [ "application/json" ], "produces": [ "application/json" ], "tags": [ "歌曲管理" ], "summary": "获取匹配歌曲的 ID 列表", "parameters": [ { "type": "string", "description": "歌曲类型", "name": "type", "in": "query" }, { "type": "string", "description": "搜索关键词", "name": "keyword", "in": "query" }, { "type": "string", "description": "按 file_path 前缀过滤", "name": "path_prefix", "in": "query" }, { "type": "string", "description": "按流派精确过滤", "name": "genre", "in": "query" }, { "type": "string", "description": "按歌手精确过滤", "name": "artist", "in": "query" }, { "type": "string", "description": "按专辑精确过滤", "name": "album", "in": "query" }, { "type": "string", "description": "按语种精确过滤", "name": "language", "in": "query" }, { "type": "string", "description": "按风格精确过滤", "name": "style", "in": "query" }, { "type": "integer", "description": "按发行年份精确过滤", "name": "year", "in": "query" }, { "type": "integer", "description": "按年代过滤(起始年,如 1990 匹配 1990-1999)", "name": "decade", "in": "query" }, { "type": "string", "default": "hidden", "description": "排除属于这些 label 歌单的歌曲(逗号分隔), 默认 hidden; 传 none 显示全部", "name": "exclude_playlist_labels", "in": "query" }, { "enum": [ "id", "title", "artist", "album", "duration", "added_at", "updated_at", "file_modified_at", "year", "genre" ], "type": "string", "description": "排序字段,缺省 added_at", "name": "sort", "in": "query" }, { "enum": [ "asc", "desc" ], "type": "string", "description": "排序方向,缺省 desc", "name": "order", "in": "query" } ], "responses": { "200": { "description": "成功返回 ID 列表", "schema": { "type": "object", "additionalProperties": true } }, "500": { "description": "服务器错误", "schema": { "type": "object", "additionalProperties": { "type": "string" } } } } } }, "/songs/radio": { "post": { "security": [ { "BearerAuth": [] } ], "description": "批量添加电台/广播到数据库", "consumes": [ "application/json" ], "produces": [ "application/json" ], "tags": [ "歌曲管理" ], "summary": "批量添加电台/广播", "parameters": [ { "description": "电台/广播列表", "name": "request", "in": "body", "required": true, "schema": { "type": "array", "items": { "type": "object", "properties": { "cover_url": { "type": "string" }, "title": { "type": "string" }, "url": { "type": "string" } } } } } ], "responses": { "201": { "description": "添加成功", "schema": { "type": "object", "properties": { "count": { "type": "integer" }, "songs": { "type": "array", "items": { "$ref": "#/definitions/models.Song" } } } } }, "400": { "description": "请求数据错误", "schema": { "type": "object", "additionalProperties": { "type": "string" } } }, "500": { "description": "添加失败", "schema": { "type": "object", "additionalProperties": { "type": "string" } } } } } }, "/songs/refresh-metadata": { "post": { "security": [ { "BearerAuth": [] } ], "description": "对所有元数据缺失的远程歌曲,通过 ffprobe 探测时长、比特率、采样率、格式及标签并回填。已在运行时返回 409。", "produces": [ "application/json" ], "tags": [ "歌曲管理" ], "summary": "刷新远程歌曲元数据", "responses": { "202": { "description": "已启动", "schema": { "type": "object", "additionalProperties": { "type": "string" } } }, "409": { "description": "已在运行", "schema": { "type": "object", "additionalProperties": { "type": "string" } } }, "500": { "description": "启动失败", "schema": { "type": "object", "additionalProperties": { "type": "string" } } } } } }, "/songs/refresh-metadata/cancel": { "post": { "security": [ { "BearerAuth": [] } ], "description": "取消正在执行的远程歌曲元数据刷新任务", "produces": [ "application/json" ], "tags": [ "歌曲管理" ], "summary": "取消元数据刷新", "responses": { "204": { "description": "已取消" } } } }, "/songs/refresh-metadata/progress": { "get": { "security": [ { "BearerAuth": [] } ], "description": "轮询远程歌曲元数据刷新的执行状态和进度", "produces": [ "application/json" ], "tags": [ "歌曲管理" ], "summary": "获取元数据刷新进度", "responses": { "200": { "description": "进度信息", "schema": { "$ref": "#/definitions/services.MetadataRefreshProgress" } } } } }, "/songs/remote": { "post": { "security": [ { "BearerAuth": [] } ], "description": "批量添加网络歌曲到数据库。cover_url 支持以 \"/\" 开头的相对路径(插件场景下由服务端自动解析为内部 URL,与歌词 lyric_remote_url 的解析机制一致)。lyric_remote_url 为歌词远程 URL 直传字段,提供时优先于 lyric + lyric_source=url 的间接方式。副作用:插入成功后,对缺失技术元数据(duration/bitrate/samplerate/format)的歌曲异步探测补齐(限并发后台执行,不阻塞响应),确保 WebDAV 等无法自带时长的音源在首次播放前就落库 duration,供音箱等仅依赖服务端时长的消费端自动切歌。", "consumes": [ "application/json" ], "produces": [ "application/json" ], "tags": [ "歌曲管理" ], "summary": "批量添加网络歌曲", "parameters": [ { "description": "网络歌曲列表", "name": "request", "in": "body", "required": true, "schema": { "type": "array", "items": { "type": "object", "properties": { "album": { "type": "string" }, "artist": { "type": "string" }, "cover_url": { "type": "string" }, "dedup_key": { "type": "string" }, "duration": { "type": "number" }, "lyric": { "type": "string" }, "lyric_remote_url": { "type": "string" }, "lyric_source": { "type": "string" }, "plugin_entry_path": { "type": "string" }, "source_data": { "type": "string" }, "title": { "type": "string" }, "url": { "type": "string" } } } } } ], "responses": { "201": { "description": "添加成功", "schema": { "type": "object", "properties": { "count": { "type": "integer" }, "songs": { "type": "array", "items": { "$ref": "#/definitions/models.Song" } } } } }, "400": { "description": "请求数据错误", "schema": { "type": "object", "additionalProperties": { "type": "string" } } }, "500": { "description": "添加失败", "schema": { "type": "object", "additionalProperties": { "type": "string" } } } } } }, "/songs/{id}": { "get": { "security": [ { "BearerAuth": [] } ], "description": "根据歌曲ID获取详细信息", "consumes": [ "application/json" ], "produces": [ "application/json" ], "tags": [ "歌曲管理" ], "summary": "获取单个歌曲详情", "parameters": [ { "type": "integer", "description": "歌曲ID", "name": "id", "in": "path", "required": true } ], "responses": { "200": { "description": "成功返回歌曲详情", "schema": { "$ref": "#/definitions/models.Song" } }, "400": { "description": "无效的歌曲ID", "schema": { "type": "object", "additionalProperties": { "type": "string" } } }, "404": { "description": "歌曲不存在", "schema": { "type": "object", "additionalProperties": { "type": "string" } } } } }, "put": { "security": [ { "BearerAuth": [] } ], "description": "更新歌曲信息(仅支持网络歌曲和电台)", "consumes": [ "application/json" ], "produces": [ "application/json" ], "tags": [ "歌曲管理" ], "summary": "更新歌曲信息", "parameters": [ { "type": "integer", "description": "歌曲ID", "name": "id", "in": "path", "required": true }, { "description": "歌曲信息", "name": "request", "in": "body", "required": true, "schema": { "type": "object", "properties": { "album": { "type": "string" }, "artist": { "type": "string" }, "cover_url": { "type": "string" }, "title": { "type": "string" }, "url": { "type": "string" } } } } ], "responses": { "200": { "description": "更新成功", "schema": { "$ref": "#/definitions/models.Song" } }, "400": { "description": "请求数据错误", "schema": { "type": "object", "additionalProperties": { "type": "string" } } }, "404": { "description": "歌曲不存在", "schema": { "type": "object", "additionalProperties": { "type": "string" } } }, "500": { "description": "更新失败", "schema": { "type": "object", "additionalProperties": { "type": "string" } } } } }, "delete": { "security": [ { "BearerAuth": [] } ], "description": "根据歌曲ID删除歌曲。设置 delete_files=true 时同步删除本地音频文件", "consumes": [ "application/json" ], "produces": [ "application/json" ], "tags": [ "歌曲管理" ], "summary": "删除歌曲", "parameters": [ { "type": "integer", "description": "歌曲ID", "name": "id", "in": "path", "required": true }, { "type": "boolean", "description": "是否同时删除本地音频文件", "name": "delete_files", "in": "query" } ], "responses": { "200": { "description": "删除成功", "schema": { "type": "object", "additionalProperties": { "type": "string" } } }, "400": { "description": "无效的歌曲ID", "schema": { "type": "object", "additionalProperties": { "type": "string" } } }, "500": { "description": "删除失败", "schema": { "type": "object", "additionalProperties": { "type": "string" } } } } } }, "/songs/{id}/activate": { "post": { "security": [ { "BearerAuth": [] } ], "description": "客户端切歌前调用,让后端 cancel 同一会话下其他歌曲的进行中工作(prefetch/transcode/reassign)。其他客户端会话不受影响。", "produces": [ "application/json" ], "tags": [ "歌曲管理" ], "summary": "标记当前活跃歌曲", "parameters": [ { "type": "integer", "description": "歌曲 ID", "name": "id", "in": "path", "required": true } ], "responses": { "204": { "description": "无内容" }, "400": { "description": "无效的 song_id", "schema": { "type": "object", "additionalProperties": { "type": "string" } } } } } }, "/songs/{id}/cover": { "get": { "security": [ { "BearerAuth": [] } ], "description": "根据歌曲 ID 获取封面图片。优先使用本地封面文件(CoverPath),其次代理 CoverURL。CoverURL 支持以 \"/\" 开头的相对路径,服务端自动经 InternalURLResolver 解析为内部 URL(含 access_token),用于插件歌曲封面代理。", "produces": [ "image/jpeg" ], "tags": [ "歌曲管理" ], "summary": "获取歌曲封面图片", "parameters": [ { "type": "integer", "description": "歌曲 ID", "name": "id", "in": "path", "required": true } ], "responses": { "200": { "description": "封面图片", "schema": { "type": "file" } }, "400": { "description": "无效的歌曲 ID", "schema": { "type": "object", "additionalProperties": { "type": "string" } } }, "404": { "description": "歌曲或封面不存在", "schema": { "type": "object", "additionalProperties": { "type": "string" } } }, "500": { "description": "服务器错误", "schema": { "type": "object", "additionalProperties": { "type": "string" } } } } } }, "/songs/{id}/hls/playlist": { "get": { "security": [ { "BearerAuth": [] } ], "description": "HLS 反代开启时,由 ServeProxy 改写后的 m3u8 内回链触发。拉取上游 m3u8 → 同源校验 → 改写 URI → 回写给 player。", "produces": [ "application/vnd.apple.mpegurl" ], "tags": [ "电台与 HLS" ], "summary": "反代 HLS 子层 m3u8", "parameters": [ { "type": "integer", "description": "歌曲 ID", "name": "id", "in": "path", "required": true }, { "type": "string", "description": "上游 m3u8 URL(base64url 编码)", "name": "u", "in": "query", "required": true } ], "responses": { "200": { "description": "改写后的 m3u8 文本", "schema": { "type": "string" } }, "400": { "description": "song_id 或 u 参数无效", "schema": { "type": "object", "additionalProperties": { "type": "string" } } }, "403": { "description": "非同源 URL 拒绝代理(SSRF 防护)", "schema": { "type": "object", "additionalProperties": { "type": "string" } } }, "404": { "description": "歌曲不存在", "schema": { "type": "string" } }, "502": { "description": "上游不可用", "schema": { "type": "object", "additionalProperties": { "type": "string" } } } } } }, "/songs/{id}/hls/segment": { "get": { "security": [ { "BearerAuth": [] } ], "description": "由 HandlePlaylist 改写后的相对路径触发,反代音频切片、加密 key、init 段等二进制资源。透传 Range 请求。", "produces": [ "application/octet-stream" ], "tags": [ "电台与 HLS" ], "summary": "反代 HLS 切片 / key / init 段", "parameters": [ { "type": "integer", "description": "歌曲 ID", "name": "id", "in": "path", "required": true }, { "type": "string", "description": "上游切片 URL(base64url 编码)", "name": "u", "in": "query", "required": true } ], "responses": { "200": { "description": "切片二进制内容", "schema": { "type": "file" } }, "206": { "description": "Range 请求的部分内容", "schema": { "type": "file" } }, "400": { "description": "song_id 或 u 参数无效", "schema": { "type": "object", "additionalProperties": { "type": "string" } } }, "403": { "description": "非同源 URL 拒绝代理", "schema": { "type": "object", "additionalProperties": { "type": "string" } } }, "404": { "description": "歌曲不存在", "schema": { "type": "string" } }, "502": { "description": "上游不可用", "schema": { "type": "object", "additionalProperties": { "type": "string" } } } } } }, "/songs/{id}/lyric": { "get": { "security": [ { "BearerAuth": [] } ], "description": "根据 song.ID 返回 LyricPayload JSON,含 lyric/tlyric/rlyric/lxlyric。", "produces": [ "application/json" ], "tags": [ "歌曲管理" ], "summary": "获取歌曲歌词", "parameters": [ { "type": "integer", "description": "歌曲 ID", "name": "id", "in": "path", "required": true } ], "responses": { "200": { "description": "LyricPayload", "schema": { "type": "object", "additionalProperties": true } }, "404": { "description": "歌曲或歌词不存在", "schema": { "type": "string" } }, "502": { "description": "歌词获取失败", "schema": { "type": "string" } } } } }, "/songs/{id}/lyrics": { "put": { "security": [ { "BearerAuth": [] } ], "description": "更新指定歌曲的歌词内容和来源。url 来源传 lyric_remote_url,其它来源传 lyric/tlyric/rlyric/lxlyric 四字段。响应里的 file_write_status 表示是否把元数据回写到本地音频文件:written=已写入,unchanged=未变更(非本地歌曲/无文件路径/不支持的扩展名/url 来源),skipped=标签已一致无需写入,failed=尝试写入但失败(DB 已成功)。lyric_source=manual 用于标记用户手动调整,scanner 重扫时不会覆盖", "consumes": [ "application/json" ], "produces": [ "application/json" ], "tags": [ "歌曲管理" ], "summary": "更新歌曲歌词", "parameters": [ { "type": "integer", "description": "歌曲 ID", "name": "id", "in": "path", "required": true }, { "description": "歌词信息", "name": "request", "in": "body", "required": true, "schema": { "type": "object", "properties": { "lxlyric": { "type": "string" }, "lyric": { "type": "string" }, "lyric_remote_url": { "type": "string" }, "lyric_source": { "type": "string" }, "rlyric": { "type": "string" }, "tlyric": { "type": "string" } } } } ], "responses": { "200": { "description": "更新成功", "schema": { "type": "object", "properties": { "file_write_status": { "type": "string" }, "message": { "type": "string" } } } }, "400": { "description": "请求数据错误", "schema": { "type": "object", "additionalProperties": { "type": "string" } } }, "404": { "description": "歌曲不存在", "schema": { "type": "object", "additionalProperties": { "type": "string" } } }, "500": { "description": "更新失败", "schema": { "type": "object", "additionalProperties": { "type": "string" } } } } } }, "/songs/{id}/play": { "get": { "security": [ { "BearerAuth": [] } ], "description": "按 song.ID 流式返回音频。内部根据 song.type 分发到本地文件 / 缓存下载 / 直链下载 / 电台 302。", "produces": [ "application/octet-stream" ], "tags": [ "歌曲管理" ], "summary": "流式播放歌曲", "parameters": [ { "type": "integer", "description": "歌曲 ID", "name": "id", "in": "path", "required": true }, { "type": "string", "description": "目标转码格式(如 mp3、ogg),用于平台兼容性转码", "name": "format", "in": "query" }, { "type": "string", "description": "目标音质码率(128/192/320),不传或不合法值表示原始音质。指定后默认转码为 mp3(除非同时指定了 format)", "name": "quality", "in": "query" }, { "type": "string", "description": "传 1 时异步预热缓存/转码,立即返回 202", "name": "prefetch", "in": "query" }, { "type": "string", "description": "传 video 时按视频播放:直出原容器(忽略 format/quality 转码,避免 -vn 丢画面),并按容器真实类型返回 Content-Type(如 video/mp4)。用于应用内视频画面渲染与 DLNA 视频投屏", "name": "media", "in": "query" } ], "responses": { "200": { "description": "音频文件", "schema": { "type": "file" } }, "202": { "description": "预拉取已触发", "schema": { "type": "string" } }, "302": { "description": "电台流重定向", "schema": { "type": "string" } }, "404": { "description": "歌曲不存在", "schema": { "type": "string" } }, "502": { "description": "音源不可用", "schema": { "type": "string" } } } } }, "/songs/{id}/play.m3u8": { "get": { "security": [ { "BearerAuth": [] } ], "description": "按 song.ID 流式返回音频。内部根据 song.type 分发到本地文件 / 缓存下载 / 直链下载 / 电台 302。", "produces": [ "application/octet-stream" ], "tags": [ "歌曲管理" ], "summary": "流式播放歌曲", "parameters": [ { "type": "integer", "description": "歌曲 ID", "name": "id", "in": "path", "required": true }, { "type": "string", "description": "目标转码格式(如 mp3、ogg),用于平台兼容性转码", "name": "format", "in": "query" }, { "type": "string", "description": "目标音质码率(128/192/320),不传或不合法值表示原始音质。指定后默认转码为 mp3(除非同时指定了 format)", "name": "quality", "in": "query" }, { "type": "string", "description": "传 1 时异步预热缓存/转码,立即返回 202", "name": "prefetch", "in": "query" }, { "type": "string", "description": "传 video 时按视频播放:直出原容器(忽略 format/quality 转码,避免 -vn 丢画面),并按容器真实类型返回 Content-Type(如 video/mp4)。用于应用内视频画面渲染与 DLNA 视频投屏", "name": "media", "in": "query" } ], "responses": { "200": { "description": "音频文件", "schema": { "type": "file" } }, "202": { "description": "预拉取已触发", "schema": { "type": "string" } }, "302": { "description": "电台流重定向", "schema": { "type": "string" } }, "404": { "description": "歌曲不存在", "schema": { "type": "string" } }, "502": { "description": "音源不可用", "schema": { "type": "string" } } } } }, "/songs/{id}/played": { "post": { "security": [ { "BearerAuth": [] } ], "description": "客户端在歌曲开始播放、播放完成或被跳过时调用此端点,后端将事件广播给已订阅播放事件的 JS 插件(通过 songloft.events.onPlayEvent 注册)。source 参数标识调用来源,如 songloft-player(官方客户端)、miot(小爱音箱插件)等。type 参数标识事件类型:play(开始播放)、finish(播放完成)、skip(用户跳过)。", "produces": [ "application/json" ], "tags": [ "歌曲管理" ], "summary": "通知歌曲播放事件", "parameters": [ { "type": "integer", "description": "歌曲 ID", "name": "id", "in": "path", "required": true }, { "type": "string", "description": "调用来源标识,如 songloft-player、miot", "name": "source", "in": "query" }, { "enum": [ "play", "finish", "skip" ], "type": "string", "description": "事件类型:play、finish、skip,默认 finish", "name": "type", "in": "query" } ], "responses": { "204": { "description": "无内容" }, "400": { "description": "无效的歌曲 ID 或事件类型", "schema": { "$ref": "#/definitions/models.ErrorResponse" } }, "404": { "description": "歌曲不存在", "schema": { "$ref": "#/definitions/models.ErrorResponse" } } } } }, "/upgrade/check": { "get": { "security": [ { "BearerAuth": [] } ], "description": "检查是否有可用的新版本", "consumes": [ "application/json" ], "produces": [ "application/json" ], "tags": [ "系统升级" ], "summary": "检查更新", "responses": { "200": { "description": "成功返回更新检查结果", "schema": { "type": "object", "additionalProperties": true } }, "403": { "description": "非 Docker 环境不支持升级", "schema": { "$ref": "#/definitions/models.ErrorResponse" } }, "500": { "description": "检查更新失败", "schema": { "$ref": "#/definitions/models.ErrorResponse" } } } } }, "/upgrade/progress": { "get": { "security": [ { "BearerAuth": [] } ], "description": "获取当前升级任务的进度信息", "consumes": [ "application/json" ], "produces": [ "application/json" ], "tags": [ "系统升级" ], "summary": "获取升级进度", "responses": { "200": { "description": "成功返回升级进度", "schema": { "$ref": "#/definitions/models.UpgradeProgress" } }, "403": { "description": "非 Docker 环境不支持升级", "schema": { "$ref": "#/definitions/models.ErrorResponse" } } } } }, "/upgrade/reset": { "post": { "security": [ { "BearerAuth": [] } ], "description": "将二进制文件回退到 Docker 镜像中的原始版本,然后重启服务", "consumes": [ "application/json" ], "produces": [ "application/json" ], "tags": [ "系统升级" ], "summary": "回退到底包版本", "responses": { "200": { "description": "回退已开始", "schema": { "$ref": "#/definitions/models.SuccessResponse" } }, "403": { "description": "非 Docker 环境不支持回退", "schema": { "$ref": "#/definitions/models.ErrorResponse" } } } } }, "/upgrade/start": { "post": { "security": [ { "BearerAuth": [] } ], "description": "开始升级到指定版本", "consumes": [ "application/json" ], "produces": [ "application/json" ], "tags": [ "系统升级" ], "summary": "开始升级", "parameters": [ { "description": "升级请求 {version_type: stable|dev}", "name": "request", "in": "body", "required": true, "schema": { "type": "object", "additionalProperties": { "type": "string" } } } ], "responses": { "200": { "description": "升级已开始", "schema": { "$ref": "#/definitions/models.SuccessResponse" } }, "400": { "description": "请求参数错误", "schema": { "$ref": "#/definitions/models.ErrorResponse" } }, "403": { "description": "非 Docker 环境不支持升级", "schema": { "$ref": "#/definitions/models.ErrorResponse" } }, "500": { "description": "升级失败", "schema": { "$ref": "#/definitions/models.ErrorResponse" } } } } }, "/upgrade/versions": { "get": { "security": [ { "BearerAuth": [] } ], "description": "获取正式版和测试版的版本信息", "consumes": [ "application/json" ], "produces": [ "application/json" ], "tags": [ "系统升级" ], "summary": "获取可用版本信息", "responses": { "200": { "description": "成功返回版本信息", "schema": { "type": "object", "additionalProperties": true } }, "403": { "description": "非 Docker 环境不支持升级", "schema": { "$ref": "#/definitions/models.ErrorResponse" } }, "500": { "description": "获取版本信息失败", "schema": { "$ref": "#/definitions/models.ErrorResponse" } } } } }, "/version": { "get": { "description": "获取应用的版本信息,包括版本号、Git提交哈希和构建时间", "consumes": [ "application/json" ], "produces": [ "application/json" ], "tags": [ "系统管理" ], "summary": "获取应用版本信息", "responses": { "200": { "description": "成功返回版本信息", "schema": { "type": "object", "additionalProperties": { "type": "string" } } } } } } }, "definitions": { "handlers.AutoScanSetting": { "type": "object", "properties": { "enabled": { "type": "boolean" }, "interval_seconds": { "type": "integer" } } }, "handlers.MusicPathSetting": { "type": "object", "properties": { "auto_create_exclude_dirs": { "type": "array", "items": { "type": "string" } }, "exclude_dirs": { "type": "array", "items": { "type": "string" } }, "exclude_paths": { "type": "array", "items": { "type": "string" } }, "path": { "type": "string" } } }, "handlers.ScanRequest": { "type": "object", "properties": { "paths": { "description": "Paths 为目录级定向扫描(Issue #262):为空时扫描整个音乐根目录(默认行为);\n非空时只扫描给定目录(含子目录),过期记录清理也仅收敛到这些目录之内。\n每个目录必须位于音乐根目录之下,否则返回 400。", "type": "array", "items": { "type": "string" } }, "reimport": { "type": "boolean" } } }, "handlers.WriteSongTagsRequest": { "type": "object", "properties": { "album": { "type": "string" }, "artist": { "type": "string" }, "clear_cover": { "type": "boolean" }, "cover_data": { "type": "string" }, "cover_url": { "type": "string" }, "genre": { "type": "string" }, "language": { "type": "string" }, "lyrics": { "type": "string" }, "rename_file": { "description": "RenameFile 为 true 时按新标题重命名本地音频文件(保留原目录与扩展名),仅对本地非 CUE 歌曲生效。", "type": "boolean" }, "style": { "type": "string" }, "title": { "type": "string" }, "track": { "type": "string" }, "year": { "type": "integer" } } }, "handlers.dirValidateRequest": { "type": "object", "properties": { "path": { "type": "string" } } }, "handlers.dirValidateResponse": { "type": "object", "properties": { "created": { "type": "boolean" }, "error": { "type": "string" }, "free_size": { "type": "integer" }, "total_size": { "type": "integer" }, "valid": { "type": "boolean" } } }, "handlers.equalizerSetting": { "type": "object", "properties": { "bands": { "type": "array", "items": { "type": "number" } }, "enabled": { "type": "boolean" }, "preset": { "type": "string" } } }, "handlers.githubProxySetting": { "type": "object", "properties": { "proxy": { "type": "string" } } }, "handlers.hlsProxySettingRequest": { "type": "object", "properties": { "enabled": { "type": "boolean" } } }, "handlers.httpProxySetting": { "type": "object", "properties": { "proxy": { "type": "string" } } }, "handlers.jsPluginBatchUpdateResponse": { "type": "object", "properties": { "failed": { "type": "integer" }, "message": { "type": "string" }, "results": { "type": "array", "items": { "$ref": "#/definitions/handlers.jsPluginBatchUpdateResult" } }, "skipped": { "type": "integer" }, "total": { "type": "integer" }, "updated": { "type": "integer" } } }, "handlers.jsPluginBatchUpdateResult": { "type": "object", "properties": { "current_version": { "type": "string" }, "entry_path": { "type": "string" }, "error": { "type": "string" }, "has_update": { "type": "boolean" }, "new_version": { "type": "string" }, "plugin_id": { "type": "integer" }, "plugin_name": { "type": "string" }, "success": { "type": "boolean" } } }, "handlers.jsPluginUploadResponse": { "type": "object", "properties": { "failed": { "type": "integer" }, "message": { "type": "string" }, "results": { "type": "array", "items": { "$ref": "#/definitions/handlers.jsPluginUploadResult" } }, "success": { "type": "integer" }, "total": { "type": "integer" } } }, "handlers.jsPluginUploadResult": { "type": "object", "properties": { "error": { "type": "string" }, "file_name": { "type": "string" }, "plugin": { "$ref": "#/definitions/jsplugin.JSPlugin" }, "success": { "type": "boolean" } } }, "handlers.logLevelSettingRequest": { "type": "object", "properties": { "level": { "type": "string" } } }, "handlers.pluginAutoUpdateSetting": { "type": "object", "properties": { "enabled": { "type": "boolean" } } }, "handlers.pluginHealthResponse": { "type": "object", "properties": { "plugins": { "type": "array", "items": { "$ref": "#/definitions/source.PluginHealthSnapshot" } } } }, "handlers.pluginKeepAliveSetting": { "type": "object", "properties": { "plugins": { "type": "array", "items": { "type": "string" } } } }, "handlers.pluginRegistriesSetting": { "type": "object", "properties": { "registries": { "type": "array", "items": { "$ref": "#/definitions/jsplugin.RegistryConfig" } } } }, "handlers.pluginTabEntry": { "type": "object", "properties": { "entry_path": { "type": "string" }, "name": { "type": "string" }, "plugin_id": { "type": "integer" } } }, "handlers.registryInstallRequest": { "type": "object", "properties": { "download_url": { "type": "string" }, "github_proxy": { "type": "string" }, "source_url": { "description": "SourceURL 插件所属订阅源 URL。「全部」聚合模式安装时回传:\n当未显式提供 token 时,后端据此从 plugin_registries 配置解析该源的 token。", "type": "string" }, "token": { "type": "string" } } }, "handlers.registryPluginEntry": { "type": "object", "properties": { "author": { "type": "string" }, "description": { "type": "string" }, "download_url": { "type": "string" }, "entry_path": { "type": "string" }, "has_update": { "type": "boolean" }, "homepage": { "type": "string" }, "icon": { "type": "string" }, "installed": { "type": "boolean" }, "installed_version": { "type": "string" }, "name": { "type": "string" }, "source_url": { "description": "SourceURL 该插件所属订阅源 URL(仅「全部」聚合模式返回),\n安装时回传给后端以按源解析私有源 token。", "type": "string" }, "version": { "type": "string" } } }, "handlers.registryRefreshRequest": { "type": "object", "properties": { "all_sources": { "type": "boolean" }, "github_proxy": { "type": "string" }, "page": { "type": "integer" }, "page_size": { "type": "integer" }, "registry_url": { "type": "string" }, "search": { "type": "string" }, "token": { "type": "string" } } }, "handlers.registryRefreshResponse": { "type": "object", "properties": { "page": { "type": "integer" }, "page_size": { "type": "integer" }, "plugins": { "type": "array", "items": { "$ref": "#/definitions/handlers.registryPluginEntry" } }, "total": { "type": "integer" }, "warnings": { "type": "array", "items": { "type": "string" } } } }, "handlers.remoteTitleSourceRequest": { "type": "object", "properties": { "title_source": { "type": "string", "enum": [ "tag", "filename" ], "example": "filename" } } }, "handlers.scanAutoCreatePlaylistsRequest": { "type": "object", "properties": { "enabled": { "type": "boolean" } } }, "handlers.scanPlaylistModeRequest": { "type": "object", "properties": { "mode": { "type": "string", "enum": [ "directory", "top_level", "bubble_up" ], "example": "directory" } } }, "handlers.scanPlaylistModeResponse": { "type": "object", "properties": { "mode": { "type": "string", "example": "directory" } } }, "handlers.scanTitleSourceRequest": { "type": "object", "properties": { "title_source": { "type": "string", "enum": [ "tag", "filename" ], "example": "tag" } } }, "handlers.startFingerprintRequest": { "type": "object", "properties": { "recompute_all": { "type": "boolean" } } }, "handlers.tabConfigSetting": { "type": "object", "properties": { "plugin_tabs": { "type": "array", "items": { "$ref": "#/definitions/handlers.pluginTabEntry" } }, "show_library": { "type": "boolean" }, "show_playlists": { "type": "boolean" } } }, "handlers.userPreferencesSetting": { "type": "object", "properties": { "audio_quality": { "type": "string" }, "local_cache_max_size": { "type": "integer" }, "play_mode": { "type": "string" }, "playlist_view_mode": { "type": "string" }, "theme_mode": { "type": "string" }, "volume": { "type": "number" } } }, "jsplugin.JSPlugin": { "type": "object", "properties": { "author": { "type": "string" }, "created_at": { "type": "string" }, "description": { "type": "string" }, "download_url": { "type": "string" }, "entry_hash": { "description": "main.js/main.jsc 内容 SHA256", "type": "string" }, "entry_path": { "description": "路由前缀(如 \"myplugin\")", "type": "string" }, "external_paths": { "description": "可访问的外部绝对路径目录", "type": "array", "items": { "type": "string" } }, "file_mod_time": { "type": "string" }, "file_path": { "description": "ZIP 文件相对路径", "type": "string" }, "homepage": { "type": "string" }, "icon": { "type": "string" }, "id": { "type": "integer" }, "license": { "type": "string" }, "main": { "description": "入口文件路径(如 \"main.js\")", "type": "string" }, "min_host_version": { "type": "string" }, "name": { "type": "string" }, "permissions": { "description": "权限列表", "type": "array", "items": { "type": "string" } }, "public_paths": { "description": "无需 JWT 认证的路径前缀", "type": "array", "items": { "type": "string" } }, "status": { "$ref": "#/definitions/models.JSPluginStatus" }, "update_url": { "type": "string" }, "updated_at": { "type": "string" }, "version": { "type": "string" }, "zip_hash": { "description": "ZIP 文件 SHA256", "type": "string" } } }, "jsplugin.RegistryConfig": { "type": "object", "properties": { "enabled": { "type": "boolean" }, "name": { "type": "string" }, "token": { "type": "string" }, "url": { "type": "string" } } }, "models.BatchDeletePlaylistsRequest": { "type": "object", "properties": { "ids": { "description": "要删除的歌单 ID 列表", "type": "array", "items": { "type": "integer" }, "example": [ 1 ] } } }, "models.BatchDeletePlaylistsResponse": { "type": "object", "properties": { "deleted": { "description": "实际删除的数量", "type": "integer", "example": 3 } } }, "models.BatchDeleteSongsRequest": { "type": "object", "properties": { "delete_files": { "description": "是否同步删除本地音频文件", "type": "boolean", "example": false }, "ids": { "description": "要删除的歌曲 ID 列表", "type": "array", "items": { "type": "integer" }, "example": [ 1 ] } } }, "models.BatchDeleteSongsResponse": { "type": "object", "properties": { "deleted": { "description": "实际删除的数量", "type": "integer", "example": 3 } } }, "models.Config": { "type": "object", "properties": { "id": { "description": "配置ID", "type": "integer", "example": 1 }, "key": { "description": "配置键", "type": "string", "example": "music_path" }, "updated_at": { "description": "更新时间", "type": "string", "example": "2024-01-01T12:00:00Z" }, "value": { "description": "配置值(JSON格式)", "type": "string", "example": "{\"path\":\"/music\"}" } } }, "models.CreateConfigRequest": { "type": "object", "required": [ "key", "value" ], "properties": { "key": { "description": "配置键", "type": "string", "example": "music_path" }, "value": { "description": "配置值(JSON 格式)", "type": "string", "example": "{\"path\":\"/music\"}" } } }, "models.ErrorResponse": { "type": "object", "properties": { "detail": { "description": "详细错误信息(可选)", "type": "string", "example": "详细错误信息" }, "error": { "description": "错误信息", "type": "string", "example": "操作失败" } } }, "models.ImportResult": { "type": "object", "properties": { "playlists_created": { "type": "integer" }, "playlists_merged": { "type": "integer" }, "songs_created": { "type": "integer" }, "songs_matched": { "type": "integer" }, "songs_skipped": { "type": "integer" } } }, "models.JSPluginStatus": { "type": "string", "enum": [ "active", "inactive", "error" ], "x-enum-comments": { "JSPluginStatusActive": "激活状态", "JSPluginStatusError": "错误状态", "JSPluginStatusInactive": "未激活状态" }, "x-enum-descriptions": [ "激活状态", "未激活状态", "错误状态" ], "x-enum-varnames": [ "JSPluginStatusActive", "JSPluginStatusInactive", "JSPluginStatusError" ] }, "models.LoginRequest": { "type": "object", "required": [ "password", "username" ], "properties": { "password": { "description": "密码", "type": "string", "example": "admin" }, "username": { "description": "用户名", "type": "string", "example": "admin" } } }, "models.LoginResponse": { "type": "object", "properties": { "access_token": { "description": "Access Token", "type": "string", "example": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." }, "expires_in": { "description": "Access Token 过期时间(秒)", "type": "integer", "example": 604800 }, "refresh_token": { "description": "Refresh Token", "type": "string", "example": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." }, "token_type": { "description": "Token 类型", "type": "string", "example": "Bearer" } } }, "models.Playlist": { "type": "object", "properties": { "cover_url": { "description": "封面图片 URL", "type": "string", "example": "https://example.com/playlist.jpg" }, "created_at": { "description": "创建时间", "type": "string", "example": "2024-01-01T12:00:00Z" }, "description": { "description": "歌单描述", "type": "string", "example": "收藏的经典歌曲" }, "id": { "description": "歌单 ID", "type": "integer", "example": 1 }, "labels": { "description": "歌单标签,如 [\"built_in\"]", "type": "array", "items": { "type": "string" }, "example": [ "[\"built_in\"]" ] }, "name": { "description": "歌单名称", "type": "string", "example": "我的最爱" }, "song_count": { "description": "歌曲数量", "type": "integer", "example": 10 }, "type": { "description": "歌单类型:normal/radio", "type": "string", "enum": [ "normal", "radio" ], "example": "normal" }, "updated_at": { "description": "最后更新时间", "type": "string", "example": "2024-01-01T12:00:00Z" } } }, "models.RefreshTokenRequest": { "type": "object", "required": [ "refresh_token" ], "properties": { "refresh_token": { "description": "刷新令牌", "type": "string", "example": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." } } }, "models.RevokeTokenRequest": { "type": "object", "properties": { "reason": { "description": "撤销原因", "type": "string", "example": "用户主动登出" } } }, "models.SetPlaylistVisibilityRequest": { "type": "object", "properties": { "hidden": { "description": "是否隐藏歌单", "type": "boolean", "example": true } } }, "models.Song": { "type": "object", "properties": { "added_at": { "description": "添加时间", "type": "string", "example": "2024-01-01T12:00:00Z" }, "album": { "description": "专辑名称", "type": "string", "example": "十一月的萧邦" }, "artist": { "description": "艺术家/歌手", "type": "string", "example": "周杰伦" }, "bit_rate": { "description": "比特率(kbps)", "type": "integer", "example": 320 }, "cover_url": { "description": "封面图片URL", "type": "string", "example": "https://example.com/cover.jpg" }, "cue_source_path": { "description": "CUE 来源路径(非空表示 CUE 拆分歌曲)", "type": "string" }, "cue_track_index": { "description": "CUE track 序号 (1-99)", "type": "integer" }, "dedup_key": { "description": "去重 key(由插件定义,典型形态 \"\u003cplatform\u003e:\u003cplatform_id\u003e\");与 PluginEntryPath 组成 UNIQUE", "type": "string" }, "duration": { "description": "播放时长(秒)", "type": "number", "example": 253.5 }, "file_modified_at": { "description": "文件修改时间(mtime,本地歌曲扫描时记录;未知为 nil)", "type": "string" }, "file_path": { "description": "本地文件路径", "type": "string", "example": "/music/周杰伦/夜曲.mp3" }, "file_size": { "description": "文件大小(字节)", "type": "integer", "example": 10485760 }, "fingerprint": { "description": "音频指纹(Chromaprint)", "type": "string" }, "fingerprint_duration": { "description": "指纹对应音频时长", "type": "number" }, "format": { "description": "音频格式", "type": "string", "example": "mp3" }, "genre": { "description": "流派", "type": "string", "example": "Pop" }, "id": { "description": "歌曲ID", "type": "integer", "example": 1 }, "is_live": { "description": "是否为直播流", "type": "boolean", "example": false }, "is_video": { "description": "是否含真实视频轨(扫描时 ffprobe 探测,排除封面);客户端据此渲染画面/选择投屏 mime", "type": "boolean", "example": false }, "isrc": { "description": "ISRC(国际标准录音编码)", "type": "string" }, "language": { "description": "语种", "type": "string", "example": "国语" }, "lyric_remote_url": { "description": "lyric_source=url 时的原始 URL(运行时由 LyricFetcher 拉取)", "type": "string" }, "lyric_url": { "description": "歌词端点 URL(客户端唯一可见字段,指向 /api/v1/songs/{id}/lyric)", "type": "string" }, "plugin_entry_path": { "description": "音源插件 entryPath(网络歌曲)", "type": "string", "example": "my-source" }, "sample_rate": { "description": "采样率(Hz)", "type": "integer", "example": 44100 }, "source_cover_url": { "description": "原始封面 URL(仅 JSON 输出,CoverURL 非空时保留原始值供编辑使用)", "type": "string" }, "source_data": { "description": "音源元数据 JSON(给插件 music/url 接口用,opaque)", "type": "string" }, "source_url": { "description": "原始音源 URL(仅 JSON 输出,radio/remote 类型返回原始流地址供编辑使用)", "type": "string" }, "style": { "description": "风格", "type": "string", "example": "抒情" }, "title": { "description": "标题", "type": "string", "example": "夜曲" }, "track": { "description": "音轨号,可为 \"3\" 或 \"3/12\"(轨号/总数)", "type": "string", "example": "3/12" }, "type": { "description": "歌曲类型:local/remote/radio", "type": "string", "enum": [ "local", "remote", "radio" ], "example": "local" }, "updated_at": { "description": "最后更新时间", "type": "string", "example": "2024-01-01T12:00:00Z" }, "url": { "description": "网络地址", "type": "string", "example": "https://example.com/song.mp3" }, "year": { "description": "发行年份", "type": "integer", "example": 2005 } } }, "models.SuccessResponse": { "type": "object", "properties": { "message": { "description": "成功信息", "type": "string", "example": "操作成功" } } }, "models.TokenInfo": { "type": "object", "properties": { "client_info": { "description": "客户端信息", "type": "string", "example": "Mozilla/5.0 AppleWebKit/605.1.15" }, "created_at": { "description": "创建时间", "type": "string", "example": "2024-01-01T12:00:00Z" }, "expires_at": { "description": "过期时间", "type": "string", "example": "2024-01-08T12:00:00Z" }, "revoked_at": { "description": "撤销时间", "type": "string", "example": "2024-01-01T12:00:00Z" }, "revoked_by": { "description": "撤销者", "type": "string", "example": "user" }, "revoked_reason": { "description": "撤销原因", "type": "string", "example": "用户主动登出" }, "token_id": { "description": "令牌 ID", "type": "string", "example": "abc123" }, "token_type": { "description": "令牌类型", "type": "string", "enum": [ "access", "refresh" ], "example": "access" } } }, "models.UpdateConfigRequest": { "type": "object", "required": [ "value" ], "properties": { "value": { "description": "配置值(JSON 格式)", "type": "string", "example": "{\"path\":\"/new_music\"}" } } }, "models.UpdatePlaylistRequest": { "type": "object", "properties": { "cover_path": { "description": "封面图片本地路径(传空字符串清除)", "type": "string", "example": "" }, "cover_song_id": { "description": "从指定歌曲复制封面(与 cover_path/cover_url 互斥,优先级更高)", "type": "integer", "example": 42 }, "cover_url": { "description": "封面图片 URL(传空字符串清除)", "type": "string", "example": "" }, "description": { "description": "歌单描述", "type": "string", "example": "收藏的经典歌曲" }, "name": { "description": "歌单名称", "type": "string", "example": "我的最爱" } } }, "models.UpgradeProgress": { "type": "object", "properties": { "current_step": { "description": "当前步骤描述", "type": "string", "example": "正在下载新版本..." }, "error": { "description": "错误信息(如有)", "type": "string", "example": "下载失败" }, "progress": { "description": "进度百分比 (0-100)", "type": "integer", "example": 50 }, "status": { "description": "状态", "type": "string", "enum": [ "idle", "downloading", "testing", "replacing", "restarting", "completed", "failed" ], "example": "downloading" } } }, "services.CacheConfig": { "type": "object", "properties": { "cache_dir": { "description": "自定义缓存目录,空字符串表示使用默认目录", "type": "string" }, "max_size": { "description": "最大缓存大小(字节),0 表示无限制", "type": "integer" } } }, "services.CacheConfigResponse": { "type": "object", "properties": { "cache_dir": { "type": "string" }, "default_cache_dir": { "type": "string" }, "max_size": { "type": "integer" } } }, "services.CacheStats": { "type": "object", "properties": { "file_count": { "description": "文件数量", "type": "integer" }, "max_size": { "description": "最大缓存大小(字节),0 表示无限制", "type": "integer" }, "total_size": { "description": "总大小(字节)", "type": "integer" } } }, "services.FingerprintProgress": { "type": "object", "properties": { "computed": { "type": "integer" }, "failed": { "type": "integer" }, "status": { "description": "idle, running, done", "type": "string" }, "total": { "type": "integer" } } }, "services.MetadataRefreshProgress": { "type": "object", "properties": { "failed": { "type": "integer" }, "processed": { "type": "integer" }, "status": { "type": "string" }, "total": { "type": "integer" } } }, "services.OrganizeItem": { "type": "object", "properties": { "id": { "type": "integer" }, "target_path": { "type": "string" } } }, "services.OrganizePreviewResult": { "type": "object", "properties": { "error": { "type": "string" }, "id": { "type": "integer" }, "new_path": { "type": "string" }, "old_path": { "type": "string" }, "status": { "type": "string" } } }, "services.OrganizeResult": { "type": "object", "properties": { "error": { "type": "string" }, "file_path": { "type": "string" }, "id": { "type": "integer" }, "status": { "type": "string" } } }, "services.RefreshResponse": { "type": "object", "properties": { "access_token": { "type": "string" }, "expires_in": { "type": "integer" }, "refresh_token": { "type": "string" }, "token_type": { "type": "string" } } }, "services.ScanProgress": { "type": "object", "properties": { "cleaned_files": { "description": "清理的过期文件数", "type": "integer" }, "cue_split_sources": { "description": "splitting_cue 阶段已处理的 CUE 来源数", "type": "integer" }, "current_file": { "description": "当前处理的文件", "type": "string" }, "discovered_files": { "description": "scanning 阶段已发现的音频文件数", "type": "integer" }, "end_time": { "description": "结束时间", "type": "string" }, "error": { "description": "错误信息", "type": "string" }, "failed_files": { "description": "失败的文件数", "type": "integer" }, "imported_files": { "description": "已导入文件数", "type": "integer" }, "local_song_count": { "description": "扫描完成后数据库中本地歌曲总数", "type": "integer" }, "scanned_files": { "description": "已扫描文件数", "type": "integer" }, "skipped_files": { "description": "跳过的文件数(已存在)", "type": "integer" }, "start_time": { "description": "开始时间", "type": "string" }, "status": { "description": "当前状态", "allOf": [ { "$ref": "#/definitions/services.ScanStatus" } ] }, "total_files": { "description": "总文件数", "type": "integer" } } }, "services.ScanStatus": { "type": "string", "enum": [ "idle", "scanning", "importing", "splitting_cue", "creating_playlists", "completed", "failed", "cancelling", "cancelled" ], "x-enum-comments": { "ScanStatusCancelled": "已取消", "ScanStatusCancelling": "取消中", "ScanStatusCompleted": "已完成", "ScanStatusCreatingPlaylists": "自动创建歌单中", "ScanStatusFailed": "失败", "ScanStatusIdle": "空闲", "ScanStatusImporting": "导入中", "ScanStatusScanning": "扫描文件中", "ScanStatusSplittingCue": "CUE 整轨切分中" }, "x-enum-descriptions": [ "空闲", "扫描文件中", "导入中", "CUE 整轨切分中", "自动创建歌单中", "已完成", "失败", "取消中", "已取消" ], "x-enum-varnames": [ "ScanStatusIdle", "ScanStatusScanning", "ScanStatusImporting", "ScanStatusSplittingCue", "ScanStatusCreatingPlaylists", "ScanStatusCompleted", "ScanStatusFailed", "ScanStatusCancelling", "ScanStatusCancelled" ] }, "source.HealthClass": { "type": "string", "enum": [ "green", "yellow", "red" ], "x-enum-comments": { "HealthGreen": "成功率 ≥ green_threshold 且 samples ≥ min_samples", "HealthRed": "成功率 \u003c red_threshold 且 samples ≥ min_samples", "HealthYellow": "介于 green 与 red 之间;或 samples 不足" }, "x-enum-descriptions": [ "成功率 ≥ green_threshold 且 samples ≥ min_samples", "介于 green 与 red 之间;或 samples 不足", "成功率 \u003c red_threshold 且 samples ≥ min_samples" ], "x-enum-varnames": [ "HealthGreen", "HealthYellow", "HealthRed" ] }, "source.Outcome": { "type": "object", "properties": { "latency_ns": { "description": "纳秒;swagger 显式声明避免 time.Duration 解析失败", "type": "integer" }, "plugin_entry_path": { "type": "string" }, "reason": { "type": "string" }, "result": { "$ref": "#/definitions/source.OutcomeResult" }, "size_bytes": { "type": "integer" }, "timestamp": { "type": "string" } } }, "source.OutcomeResult": { "type": "string", "enum": [ "success", "network_fail", "probe_fail", "validation_fail", "plugin_invocation_fail" ], "x-enum-comments": { "OutcomeNetworkFail": "HTTP 错误、超时、读流中断", "OutcomePluginInvocationFail": "插件 music/url 返回错误或非 200", "OutcomeProbeFail": "ffprobe / tag.ReadFrom 失败", "OutcomeValidationFail": "校验未通过(过短、码率过低等)" }, "x-enum-descriptions": [ "", "HTTP 错误、超时、读流中断", "ffprobe / tag.ReadFrom 失败", "校验未通过(过短、码率过低等)", "插件 music/url 返回错误或非 200" ], "x-enum-varnames": [ "OutcomeSuccess", "OutcomeNetworkFail", "OutcomeProbeFail", "OutcomeValidationFail", "OutcomePluginInvocationFail" ] }, "source.PluginHealthSnapshot": { "type": "object", "properties": { "class": { "$ref": "#/definitions/source.HealthClass" }, "last_failures": { "description": "最近 N 次失败,便于排查", "type": "array", "items": { "$ref": "#/definitions/source.Outcome" } }, "plugin_entry_path": { "type": "string" }, "samples": { "type": "integer" }, "success_rate": { "type": "number" } } } }, "securityDefinitions": { "BearerAuth": { "description": "输入 \"Bearer {token}\" 进行认证", "type": "apiKey", "name": "Authorization", "in": "header" } } }