From 696c266a75da4b8b058ad86cb2d9321b1c638b49 Mon Sep 17 00:00:00 2001 From: gitbobobo Date: Fri, 21 Mar 2025 16:56:39 +0800 Subject: [PATCH] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E8=8B=B1=E8=AF=AD=E6=96=87?= =?UTF-8?q?=E6=A1=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 2 + README_en.md | 58 + docs/features.md | 10 +- docs/guides/_category_.json | 4 +- docs/guides/carplay.md | 6 +- docs/guides/install.md | 10 +- docs/guides/interfaces.md | 20 +- docs/guides/others.md | 10 +- docs/guides/play.md | 8 +- docs/guides/sync_mode.md | 10 +- docs/intro.md | 14 +- docs/notes/_category_.json | 4 +- docs/notes/adaptive/_category_.json | 4 +- docs/notes/plugins.md | 10 +- docs/notes/resources.md | 8 +- docs/notes/services/_category_.json | 4 +- docs/plans.md | 12 +- docs/problems.md | 42 +- docs/services.md | 6 +- docs/tips/_category_.json | 4 +- docs/tips/detection_deletion_duplicate.md | 6 +- docs/tips/focus_specific_audio.md | 2 +- docs/tips/lastfm.md | 8 +- docs/tips/shortcuts.md | 4 +- docs/versions/1.2.x.md | 4 +- docs/versions/_category_.json | 4 +- docusaurus.config.js | 73 +- i18n/en/code.json | 313 + .../current.json | 54 + .../current/features.md | 188 + .../current/guides/carplay.md | 24 + .../current/guides/install.md | 67 + .../current/guides/interfaces.md | 181 + .../current/guides/others.md | 56 + .../current/guides/play.md | 51 + .../current/guides/sync_mode.md | 54 + .../current/intro.md | 52 + .../current/notes/adaptive/smtc.md | 350 + .../current/notes/plugins.md | 551 + .../current/notes/resources.md | 30 + .../current/notes/services/audiostation.md | 1184 ++ .../current/notes/services/emby.md | 1975 +++ .../current/notes/services/jellyfin.md | 2113 +++ .../current/notes/services/navidrome.md | 568 + .../current/notes/services/plex.md | 824 + .../current/notes/services/subsonic.md | 632 + .../current/plans.md | 41 + .../current/problems.md | 126 + .../current/services.md | 41 + .../tips/detection_deletion_duplicate.md | 25 + .../current/tips/focus_specific_audio.md | 13 + .../current/tips/lastfm.md | 33 + .../current/tips/shortcuts.md | 34 + .../current/versions/1.0.x.md | 156 + .../current/versions/1.1.x.md | 248 + .../current/versions/1.2.x.md | 348 + .../current/versions/latest.md | 508 + i18n/en/docusaurus-theme-classic/navbar.json | 10 + i18n/zh-Hans/code.json | 343 + .../current.json | 54 + .../docusaurus-theme-classic/navbar.json | 10 + package-lock.json | 14706 ---------------- package.json | 14 +- pnpm-lock.yaml | 4687 ++++- src/components/HomepageFeatures/index.js | 43 +- src/pages/index.js | 55 +- 66 files changed, 15388 insertions(+), 15721 deletions(-) create mode 100644 README_en.md create mode 100644 i18n/en/code.json create mode 100644 i18n/en/docusaurus-plugin-content-docs/current.json create mode 100644 i18n/en/docusaurus-plugin-content-docs/current/features.md create mode 100644 i18n/en/docusaurus-plugin-content-docs/current/guides/carplay.md create mode 100644 i18n/en/docusaurus-plugin-content-docs/current/guides/install.md create mode 100644 i18n/en/docusaurus-plugin-content-docs/current/guides/interfaces.md create mode 100644 i18n/en/docusaurus-plugin-content-docs/current/guides/others.md create mode 100644 i18n/en/docusaurus-plugin-content-docs/current/guides/play.md create mode 100644 i18n/en/docusaurus-plugin-content-docs/current/guides/sync_mode.md create mode 100644 i18n/en/docusaurus-plugin-content-docs/current/intro.md create mode 100644 i18n/en/docusaurus-plugin-content-docs/current/notes/adaptive/smtc.md create mode 100644 i18n/en/docusaurus-plugin-content-docs/current/notes/plugins.md create mode 100644 i18n/en/docusaurus-plugin-content-docs/current/notes/resources.md create mode 100644 i18n/en/docusaurus-plugin-content-docs/current/notes/services/audiostation.md create mode 100644 i18n/en/docusaurus-plugin-content-docs/current/notes/services/emby.md create mode 100644 i18n/en/docusaurus-plugin-content-docs/current/notes/services/jellyfin.md create mode 100644 i18n/en/docusaurus-plugin-content-docs/current/notes/services/navidrome.md create mode 100644 i18n/en/docusaurus-plugin-content-docs/current/notes/services/plex.md create mode 100644 i18n/en/docusaurus-plugin-content-docs/current/notes/services/subsonic.md create mode 100644 i18n/en/docusaurus-plugin-content-docs/current/plans.md create mode 100644 i18n/en/docusaurus-plugin-content-docs/current/problems.md create mode 100644 i18n/en/docusaurus-plugin-content-docs/current/services.md create mode 100644 i18n/en/docusaurus-plugin-content-docs/current/tips/detection_deletion_duplicate.md create mode 100644 i18n/en/docusaurus-plugin-content-docs/current/tips/focus_specific_audio.md create mode 100644 i18n/en/docusaurus-plugin-content-docs/current/tips/lastfm.md create mode 100644 i18n/en/docusaurus-plugin-content-docs/current/tips/shortcuts.md create mode 100644 i18n/en/docusaurus-plugin-content-docs/current/versions/1.0.x.md create mode 100644 i18n/en/docusaurus-plugin-content-docs/current/versions/1.1.x.md create mode 100644 i18n/en/docusaurus-plugin-content-docs/current/versions/1.2.x.md create mode 100644 i18n/en/docusaurus-plugin-content-docs/current/versions/latest.md create mode 100644 i18n/en/docusaurus-theme-classic/navbar.json create mode 100644 i18n/zh-Hans/code.json create mode 100644 i18n/zh-Hans/docusaurus-plugin-content-docs/current.json create mode 100644 i18n/zh-Hans/docusaurus-theme-classic/navbar.json delete mode 100644 package-lock.json diff --git a/README.md b/README.md index 57a6f78..c7acff7 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,8 @@ 支持 Android、iOS、MacOS、Windows 平台的 Subsonic/Navidrome/Jellyfin/Emby/AudioStation/Plex 客户端。 +简体中文 | [English](./README_en.md) + ![](https://oss.aqzscn.cn/resource/blog/img/2024/60b87-ca3fefb87f148b5c03ecf069fb1f95ab.png) 本项目非开源项目。 diff --git a/README_en.md b/README_en.md new file mode 100644 index 0000000..491a5fe --- /dev/null +++ b/README_en.md @@ -0,0 +1,58 @@ +# Stream Music + +A Subsonic/Navidrome/Jellyfin/Emby/AudioStation/Plex client for Android, iOS, macOS, and Windows platforms. + +[简体中文](./README.md) | English + +![](https://oss.aqzscn.cn/resource/blog/img/2024/60b87-ca3fefb87f148b5c03ecf069fb1f95ab.png) + +**This is not an open-source project.** + +For bug reports or suggestions, please create an issue under **Issues** with detailed steps and screenshots if possible. + +## Download + +Official Website: [music.aqzscn.cn](https://music.aqzscn.cn/) + +Android/macOS/Windows: [Version Updates](https://music.aqzscn.cn/docs/versions/latest) + +iOS: Search **[音流](https://apps.apple.com/cn/app/%E9%9F%B3%E6%B5%81-%E8%BF%9E%E6%8E%A5%E4%BD%A0%E7%9A%84%E9%9F%B3%E4%B9%90/id6449966496)** or `Stream Music` on App Store. + +Beta Channel: [devmusic.aqzscn.cn](https://devmusic.aqzscn.cn/) (Irregular updates, limited packages). + +## Platform Adaptation + +Stream Music aims to support multiple platforms and music services, rather than being a professional audio player (e.g., no HiFi/audio effects tuning - I'm not an audiophile 😄). + +### macOS Roadmap +- [x] Menu bar support +- [x] Dynamic window title modification +- [x] Lyrics in status bar +- [ ] Dock context menu +- [ ] Desktop UI adaptation (to align with Windows) + +### Windows Roadmap +- [x] Window title modification +- [x] Media notifications +- [x] Taskbar quick actions +- [ ] Desktop lyrics +- [x] System tray minimization +- [ ] Desktop UI adaptation + +### Linux Roadmap + +> Not yet planned. + +### tvOS/watchOS Roadmap + +> Implementation pending. + +## Viewing Logs + +Enable Developer Mode by tapping the version number repeatedly on the About page. + +> Logs older than 2 weeks are automatically cleared. + +## Special Thanks + +Thanks to [小众软件](https://www.appinn.com/) for featuring this project! \ No newline at end of file diff --git a/docs/features.md b/docs/features.md index 5fed289..2f09495 100644 --- a/docs/features.md +++ b/docs/features.md @@ -2,18 +2,18 @@ sidebar_position: 3 --- -# 功能比较 +# 功能比较 {#feature} 音流的基础功能免费使用,部分功能需要购买会员解锁。 -## 收费方式 +## 收费方式 {#pricing} **买断制**。一次购买,终身全平台可用。 - 会员身份可同时在 7 台设备上使用,超出最大数量后,最早登录的设备的会员身份将被取消。 - 会员验证方式采用**联网验证**,每次启动时随机触发验证,因此请尽量保证设备可以连接到互联网。 -## 购买及退款方式 +## 购买及退款方式 {#purchase} 仅可在手机端购买: @@ -24,7 +24,7 @@ sidebar_position: 3 **邮箱绑定成功后暂不支持修改,因此请勿使用临时邮箱**。 -## 功能比较 +## 功能比较 {#comparison} @@ -170,7 +170,7 @@ sidebar_position: 3 1. DLNA 功能目前兼容性较差,购买会员时请假定此功能不可用。 2. 通过 CarPlay 及快捷指令播放音乐时,若 APP 尚未打开,之后再次打开 APP 音乐会停止播放。 -## 新版本抢先体验 +## 新版本抢先体验 {#test} 音流的测试频道地址是:https://devmusic.aqzscn.cn/docs/versions/latest ,这里会不定期更新最新版本,安卓版需要注意:此频道只会提供通用版本的安装包,若之前安装了其他版本的,需要卸载后才能安装通用版本。 diff --git a/docs/guides/_category_.json b/docs/guides/_category_.json index a0dfa8f..573d2fe 100644 --- a/docs/guides/_category_.json +++ b/docs/guides/_category_.json @@ -1,8 +1,8 @@ { - "label": "使用说明", + "label": "guides", "position": 4, "link": { "type": "generated-index", - "description": "音流安装及使用说明。" + "description": "Installation and usage instructions for StreamMusic." } } diff --git a/docs/guides/carplay.md b/docs/guides/carplay.md index a76dabf..285405c 100644 --- a/docs/guides/carplay.md +++ b/docs/guides/carplay.md @@ -2,15 +2,15 @@ sidebar_position: 5 --- -# CarPlay +# CarPlay {#carplay} -## 歌词显示 +## 歌词显示 {#lyrics} 苹果出于安全考量,是不允许在 CarPlay 中显示歌词的。 因此音流能做的就是只显示单行歌词,此功能需要开启「歌词 - 歌词通知」开关。 -## 使用限制 +## 使用限制 {#limitation} 在当前版本,必须在手机上打开音流才能使用 CarPlay。 diff --git a/docs/guides/install.md b/docs/guides/install.md index 1020e72..b3ab9a9 100644 --- a/docs/guides/install.md +++ b/docs/guides/install.md @@ -2,11 +2,11 @@ sidebar_position: 1 --- -# 安装 +# 安装 {#installation} Android 和 Windows 安装教程,最新安装包可在[这里](../versions/latest)找到。 -## Android +## Android {#android} Android 一共提供四个安装包,分别对应不同的芯片架构。 @@ -18,7 +18,7 @@ Android 一共提供四个安装包,分别对应不同的芯片架构。 ::: -## Windows +## Windows {#windows} Windows 版音流目前只提供 x64 架构的 msix 格式安装包,这是微软应用商店的安装格式,因此在商店外安装,需要先在电脑上安装开发者对应的证书。 @@ -27,7 +27,7 @@ import DownloadIcon from '@mui/icons-material/Download'; -### 一、标准安装方式 +### 一、标准安装方式 {#standard} 双击证书文件开始安装,安装位置选择`本地计算机`。 @@ -45,7 +45,7 @@ import DownloadIcon from '@mui/icons-material/Download'; 安装完成后,以后就可以直接双击 `msix` 格式的安装包进行安装了。 -### 二、非标准安装方式 +### 二、非标准安装方式 {#legacy} msix 格式对系统的要求较高,需要 win10 以上才可使用,对于还停留在 win7 或 win8 的用户,可通过此种方式安装。 diff --git a/docs/guides/interfaces.md b/docs/guides/interfaces.md index 27c0b63..ae4fa2e 100644 --- a/docs/guides/interfaces.md +++ b/docs/guides/interfaces.md @@ -2,19 +2,19 @@ sidebar_position: 4 --- -# 自定义 API +# 自定义 API {#api} 某些情况下,音乐服务提供的接口可能无法返回我们需要的信息,故提供自定义 API,高级用户可以自定义返回内容。 -## 鉴权(可选) +## 鉴权(可选) {#auth} | 配置项 | 位置 | 键 | 说明 | | -------- | -------------- | -------------- | ---- | | 授权信息 | Request Header | Authorization | | -## 接口列表 +## 接口列表 {#list} -### 获取歌词 +### 获取歌词 {#lyrics} 请求方式:GET @@ -47,7 +47,7 @@ sidebar_position: 4 ] ``` -### 确认歌词 +### 确认歌词 {#confirm-lyrics} 适用于 1.2.0 及以上版本。 @@ -81,7 +81,7 @@ sidebar_position: 4 ::: -### 获取封面 +### 获取封面 {#cover} 请求方式:GET @@ -104,7 +104,7 @@ sidebar_position: 4 文件流。 -### 歌曲详情 +### 歌曲详情 {#detail} 适用于 1.2.4 及以上版本,用于在浏览器中跳转到音乐服务对应的歌曲链接。 @@ -128,9 +128,9 @@ sidebar_position: 4 /app/media/ff.flac ``` -## 简易服务 +## 简易服务 {#example} -### 搭配 Nginx 加载同目录下 Lrc 文件 `@ZaneYork` +### 搭配 Nginx 加载同目录下 Lrc 文件 `@ZaneYork` {#nginx} APP 内直接配置 API 地址为 `你的服务器地址 + /lyrics/` 即可。 @@ -154,7 +154,7 @@ APP 内直接配置 API 地址为 `你的服务器地址 + /lyrics/` 即可。 } ``` -### 搭配 php 返回同名 Lrc 文件 `@lingluos` +### 搭配 php 返回同名 Lrc 文件 `@lingluos` {#php} 在Linux系统上使用php方法返回lrc文件 要求lrc文件和歌曲文件名称完全相同 diff --git a/docs/guides/others.md b/docs/guides/others.md index 9b2dbfc..82ab5ef 100644 --- a/docs/guides/others.md +++ b/docs/guides/others.md @@ -2,9 +2,9 @@ sidebar_position: 5 --- -# 其他 +# 其他 {#other} -## 分区 +## 分区 {#partition} 部分用户不止用音流聆听音乐,还会用音流收听有声书,而媒体库的分区功能可以很好的区分不同类型的音频,避免干扰到我们想要聆听的内容。 @@ -15,7 +15,7 @@ sidebar_position: 5 - 对于 Emby ,可以将媒体库类型设置为有声读物或音乐。 - 对于 Jellyfin 和 Plex,只能将媒体库类型设置为音乐,因为 Jellyfin 的书籍类型包含对文本类型书籍的支持,数据的展示方式和音乐是截然不同的。 -## 列表元素 +## 列表元素 {#list} 绿色序号表示您从未听过这首歌,这在听有声书时会比较有用。 @@ -29,7 +29,7 @@ sidebar_position: 5 ![](https://oss.aqzscn.cn/resource/blog/img/2024/7ec2a-423ad72f94ace289a7d801936d02e3f6.png) -## 设备间同步配置信息 +## 设备间同步配置信息 {#qr} 您可能会在多个设备安装音流,每次安装后都要输入一遍甚至更多遍服务器信息,想必是非常痛苦的。 @@ -44,7 +44,7 @@ sidebar_position: 5 ::: -## 歌词 +## 歌词 {#lyrics} 目前只支持 LRC 格式的歌词,详细可参考 [维基百科](https://zh.wikipedia.org/wiki/LRC%E6%A0%BC%E5%BC%8F)。 diff --git a/docs/guides/play.md b/docs/guides/play.md index 4702d80..2ca623d 100644 --- a/docs/guides/play.md +++ b/docs/guides/play.md @@ -2,13 +2,13 @@ sidebar_position: 3 --- -# 播放逻辑 +# 播放逻辑 {#playback} 音流的播放逻辑与常见音乐 APP 并不完全一致,加入了一些我自己的理解。 若您初次使用音流,强烈建议您阅读此章节以更加得心应手地使用音流。 -## 播放模式 +## 播放模式 {#mode} 音流默认是队列播放,如需循环播放,可打开此开关,对队列播放和随机播放都会生效。 @@ -33,7 +33,7 @@ sidebar_position: 3 反之,从播放列表或播放页面切换播放模式则是临时操作,仅当次生效。 -## 播放控制 +## 播放控制 {#control} ![](https://oss.aqzscn.cn//resource/blog/img/2023/15d11e29bb8e27f58378743eb86f6fd0.png) @@ -41,7 +41,7 @@ sidebar_position: 3 从歌词区域向右滑动切换上一曲,向左滑动切换下一曲。 -## DLNA +## DLNA {#dlna} - 目前 DLNA 功能会将服务器的播放地址投送到 DLNA 设备上,因此请保证 DLNA 设备可以连接到服务器。 - 为提升 DLNA 兼容性,目前采用轮询方式获取 DLNA 设备的播放状态,因此部分操作可能会有 2 秒左右的延迟。 \ No newline at end of file diff --git a/docs/guides/sync_mode.md b/docs/guides/sync_mode.md index 1dc5520..8822835 100644 --- a/docs/guides/sync_mode.md +++ b/docs/guides/sync_mode.md @@ -2,9 +2,9 @@ sidebar_position: 2 --- -# 同步模式 +# 同步模式 {#sync} -## 媒体库模式 +## 媒体库模式 {#library-mode} 媒体库模式下,音流需要将服务器端的歌曲列表、专辑列表和歌手列表完全同步到本地,以此获得完整的音乐库数据。 @@ -12,7 +12,7 @@ sidebar_position: 2 但凡事有利就有弊,由于需要完整同步服务端数据,**若您的曲库较大,同步过程会耗费很多的时间**。 -### 自动同步 +### 自动同步 {#auto} 应用启动时会检测服务端歌曲数量,如果大于本地副本歌曲数量,则增量同步缺失的数据。 @@ -24,7 +24,7 @@ sidebar_position: 2 应用启动时需要恢复上次播放列表,某些音乐服务的歌曲资源或图片资源需要登录后才能获取,因此将播放器的初始化放在了登录操作之后,若您在播放歌曲时遇到了**播放器正在初始化**的提示,请耐心等待片刻。 -### 手动同步 +### 手动同步 {#manual} 若自动同步无法满足您的需求,请点击音乐资料库的「立即同步」按钮手动更新本地副本。 @@ -34,7 +34,7 @@ sidebar_position: 2 ::: -## 直连模式 +## 直连模式 {#direct} 在直连模式下,音流不会将服务端的数据同步到本地,而是在您每次操作时单独获取数据。若您的曲库较大,可以尝试开启此选项,以提升首次进入应用时的体验。 diff --git a/docs/intro.md b/docs/intro.md index c26139b..f51ac71 100644 --- a/docs/intro.md +++ b/docs/intro.md @@ -2,7 +2,7 @@ sidebar_position: 1 --- -# 介绍 +# 介绍 {#introduction} 音流是一款 **NAS 音乐播放器**,可以连接到您部署在 NAS 上的**音乐服务**1,能让您在**不同平台**2享受一致的音乐盛宴。 @@ -11,7 +11,7 @@ sidebar_position: 1 1. 目前支持的音乐服务: Subsonic,Navidrome,Emby,Jellyfin,AudioStation,Plex. 2. 目前支持的平台: Android 6.0, iOS 14, macOS 10.14, Windows 10. -## 要求 +## 要求 {#requirements} 在使用音流之前,您必须拥有一台运行着音乐服务的 NAS,因为音流是一个纯粹的音乐播放器,专注于串流您已拥有的音乐,并不提供任何音乐资料。 @@ -21,19 +21,19 @@ NAS 是 `家庭云存储` 的简称,如果您尚未听说过,那么音流可 ::: -## 特色功能 +## 特色功能 {#features} - 🎵 兼容多种音乐服务。 - 🎙️ 支持歌词显示,通过自定义 API 还可让不提供歌词接口的音乐服务也拥有显示歌词的能力! - 🖥️📱 使用 Flutter 构建,为您提供接近原生的使用体验,在跨平台适配上拥有无限的可能性。 -## 下载 +## 下载 {#download} -### iOS +### iOS {#ios} 点击跳转 [App Store 页面](https://apps.apple.com/cn/app/%E9%9F%B3%E6%B5%81-%E8%BF%9E%E6%8E%A5%E4%BD%A0%E7%9A%84%E9%9F%B3%E4%B9%90/id6449966496) 或在 App Store 搜索 `音流` 或 `Stream Music`。 -### Android、macOS、Windows +### Android、macOS、Windows {#other-platform} 由于音流暂未上架其他应用商店,请在[此页面](./versions/latest)查找最新版本下载。 @@ -44,7 +44,7 @@ Windows 版安装包是 msix 格式,安装方法[点此](./guides/install)查 ::: -## 反馈建议 +## 反馈建议 {#feedback} 请通过 GitHub 的 issue 功能提交您的宝贵意见。 diff --git a/docs/notes/_category_.json b/docs/notes/_category_.json index 698778c..be0075e 100644 --- a/docs/notes/_category_.json +++ b/docs/notes/_category_.json @@ -1,8 +1,8 @@ { - "label": "开发笔记", + "label": "developmentNotes", "position": 9, "link": { "type": "generated-index", - "description": "音流开发过程的经验分享,随缘记录。" + "description": "Experience sharing during the development of StreamMusic." } } diff --git a/docs/notes/adaptive/_category_.json b/docs/notes/adaptive/_category_.json index ef2c379..3b55491 100644 --- a/docs/notes/adaptive/_category_.json +++ b/docs/notes/adaptive/_category_.json @@ -1,8 +1,8 @@ { - "label": "功能适配", + "label": "adaptation", "position": 3, "link": { "type": "generated-index", - "description": "音流开发过程的经验分享,随缘记录。" + "description": "Experience sharing during the development of StreamMusic." } } diff --git a/docs/notes/plugins.md b/docs/notes/plugins.md index 475d3ee..51c0ce1 100644 --- a/docs/notes/plugins.md +++ b/docs/notes/plugins.md @@ -8,7 +8,7 @@ sidebar_position: 1 如果您觉得音流的某个功能帮到了您,可以考虑为这些项目点亮 🌟 或提供支持。 -## 界面 +## 界面 {#ui} ### macos_ui @@ -32,7 +32,7 @@ sidebar_position: 1 ![](https://oss.aqzscn.cn/resource/blog/img/2024/faae1-9ad0cf3582cd7ff590b3e02645f19bc7.png) -## 平台特性 +## 平台特性 {#platform} ### macos_window_utils @@ -85,7 +85,7 @@ impl RsImage { 相比其他语言,写 Rust 代码确实会让我感到有些别扭,比如捕获异常的方式、可选值的解包、所有权借用之类的问题,但问问 GPT 大部分问题也都能解决。毕竟在多平台享受到了 Rust 的生态,这点苦吃了也无妨哈哈~ -## 网络 +## 网络 {#network} ### dio @@ -125,7 +125,7 @@ dart 的 HTTP 请求是自己实现的,仅适配了标准的 HTTP 请求,处 [url_launcher](https://pub.dev/packages/url_launcher) 可用于在浏览器中打开链接。 -## 音频 +## 音频 {#audio} ### media_kit @@ -534,7 +534,7 @@ if (Platform.isAndroid) { \* The tag will be **read only**, due to lack of official support -## Flutter 社区 +## Flutter 社区 {#community} ### Flutter Candies diff --git a/docs/notes/resources.md b/docs/notes/resources.md index 3b61505..d6a8c7a 100644 --- a/docs/notes/resources.md +++ b/docs/notes/resources.md @@ -2,23 +2,23 @@ sidebar_position: 3 --- -# 设计资源 +# 设计资源 {#resource} 以下是 **音流 APP** 中使用到的免版权素材,若音流不小心侵犯了您的作品的权益或您有更适合在 app 中展示的免版权素材,请发送邮件至 aqzscn@qq.com 联系我进行删除或投稿,万分感谢! -## 图片背景 +## 图片背景 {#image} - [歌曲列表默认封面](https://pixabay.com/zh/vectors/record-disk-disc-music-musical-23281/) by Clker-Free-Vector-Images - [tv端歌手详情页面默认背景](https://pixabay.com/zh/photos/concert-singer-singing-stage-2566002/) by StockSnap -## 彩色插画 +## 彩色插画 {#illustrations} - [警告](https://pixabay.com/zh/vectors/warning-caution-alert-icon-media-145066/) by OpenClipart-Vectors - [成功](https://pixabay.com/zh/vectors/icon-symbol-confirmation-hook-ok-803718/) by Leovinus - [错误](https://pixabay.com/zh/vectors/error-button-failure-warning-24842/) by Clker-Free-Vector-Images - [信息](https://pixabay.com/zh/vectors/info-information-tips-icon-support-148099/) by OpenClipart-Vectors -## 单色插画 +## 单色插画 {#mono} - [tv端会员页面插画](https://pixabay.com/zh/vectors/jewelry-crown-king-royal-queen-2025491/) by OpenClipart-Vectors - [tv端关于页面插画](https://pixabay.com/zh/vectors/found-icons-requests-1294561/) by OpenClipart-Vectors diff --git a/docs/notes/services/_category_.json b/docs/notes/services/_category_.json index c84a0da..b5b0760 100644 --- a/docs/notes/services/_category_.json +++ b/docs/notes/services/_category_.json @@ -1,8 +1,8 @@ { - "label": "音乐服务", + "label": "musicServices", "position": 2, "link": { "type": "generated-index", - "description": "记录音流用到的一些音乐服务接口(非全部接口,仅记录与音乐相关的),一是方便自己查阅,二是可以让有需要的人少走些弯路。可能有些地方理解不到位,欢迎大家指正和补充。" + "description": "Documenting some of the music service APIs used by Yinliu (not all APIs, only those related to music). This is to facilitate my own reference and to help others avoid some pitfalls. There may be some misunderstandings, and corrections and additions are welcome." } } diff --git a/docs/plans.md b/docs/plans.md index 978bb3a..a524606 100644 --- a/docs/plans.md +++ b/docs/plans.md @@ -2,13 +2,13 @@ sidebar_position: 8 --- -# 开发计划 +# 开发计划 {#roadmap} -## 正在进行 +## 正在进行 {#working} 服务端 DLNA -## 上架安卓应用商店 +## 上架安卓应用商店 {#android-distribution} | 材料准备 | 进度 | | --- | --- | @@ -16,7 +16,7 @@ sidebar_position: 8 | 待定... | | -## 车机版和 TV 版 +## 车机版和 TV 版 {#more-platform} 不准备单独推出车机版,现有版本适配了手机、平板的界面,只要能安装,应该就能在车机上使用。 @@ -24,7 +24,7 @@ sidebar_position: 8 Apple TV 版在调研适配可行性后,可能会在 1.4.0 开始适配,但**不保证一定会推出**。 -## 适配鸿蒙next(纯血版) +## 适配鸿蒙next(纯血版) {#ohos} 因为音流是用 Flutter 开发的,Flutter 是谷歌的产品,所以 Flutter 官方支持鸿蒙的可能性极低。 @@ -36,7 +36,7 @@ Apple TV 版在调研适配可行性后,可能会在 1.4.0 开始适配,但* ::: -## 支持新的音乐服务 +## 支持新的音乐服务 {#protocols} 威联通音乐套件有支持计划,敬请期待。 diff --git a/docs/problems.md b/docs/problems.md index 459c3e6..822d640 100644 --- a/docs/problems.md +++ b/docs/problems.md @@ -2,7 +2,7 @@ sidebar_position: 7 --- -# 常见问题 +# 常见问题 {#issues} 如果软件使用过程中遇到问题或有任何建议,请在 Github 上找到 [此仓库](https://github.com/gitbobobo/StreamMusic): @@ -15,9 +15,9 @@ sidebar_position: 7 ::: -## 音乐服务 +## 音乐服务 {#services} -### 无法登录 +### 无法登录 {#login} 可能原因: @@ -26,7 +26,7 @@ sidebar_position: 7 - 使用了自签名的证书或证书的证书链不完整 - 使用了代理软件,代理软件把 127.0.0.1 拦截了 -### Navidrome 扫描到 50000 首歌曲后失败 +### Navidrome 扫描到 50000 首歌曲后失败 {#navidrome-scan} 在 Navidrome 0.52 及之前版本,按添加时间查询歌曲的逻辑有些问题,请升级到最新版本以解决此问题。 @@ -38,15 +38,15 @@ sidebar_position: 7 ::: -### 无法模糊搜索 +### 无法模糊搜索 {#search} 在直连模式下,模糊搜索是否可用取决于服务端是否支持。例如,若要开启 Navidrome 的模糊搜索,需要在环境变量中开启`SearchFullString`。 媒体库模式下,由客户端提供模糊搜索的特性,若您发现某个页面的模糊搜索功能失效,请及时反馈。 -## 播放 +## 播放 {#playback} -### 安卓后台播放自动暂停(没有声音) +### 安卓后台播放自动暂停(没有声音) {#background} 步骤 1: 音流 - 帮助 - 关闭电池优化 @@ -56,13 +56,13 @@ sidebar_position: 7 目前已在 魅族 20 和华为平板测过这样的配置就可以保持后台播放。 -### 播放时总时长一直在更新 +### 播放时总时长一直在更新 {#transcoding} 这表示当前歌曲正在服务端转码,播放器无法得知歌曲真实的总时长,因此会显示当前已获取的音频内容的总时长。 等待歌曲转码完成即可,这是正常现象。 -### 移动网络下无法播放 +### 移动网络下无法播放 {#ffmpeg} Wi-Fi 环境可以播放,但移动网络无法播放时,首先检查资料库地址是否能够通过公网访问。 @@ -74,9 +74,9 @@ Wi-Fi 环境可以播放,但移动网络无法播放时,首先检查资料 ::: -## 平台相关 +## 平台相关 {#platform} -### 安卓通知栏播放控制失效 +### 安卓通知栏播放控制失效 {#notification} 这似乎是安卓系统的 bug,目前的解决方案是:在系统设置中关闭音流的电池优化。 @@ -88,17 +88,17 @@ Wi-Fi 环境可以播放,但移动网络无法播放时,首先检查资料 ::: -### 安卓车载蓝牙歌曲信息不更新或更新有延迟 +### 安卓车载蓝牙歌曲信息不更新或更新有延迟 {#bluetooth} 这似乎发生在安卓手机锁屏之后,目前暂未发现解决办法,具体处理进度参见问题 [Android MediaItem is not updating on car display through bluetooth](https://github.com/ryanheise/audio_service/issues/908)。 -### 安卓开启桌面歌词后 Bitwarden 应用内部分区域无法点击 +### 安卓开启桌面歌词后 Bitwarden 应用内部分区域无法点击 {#overlay} 锁定桌面歌词后,正常来说,悬浮窗里的点击事件是可以传递给下面的界面的。 可能 Bitwarden 使用了不一样的方式识别点击事件,此时可以先在通知中心解锁或关闭桌面歌词,等操作完成后再恢复。 -### 应用发烫或 CPU 占用过高 +### 应用发烫或 CPU 占用过高 {#cpu} 这个属于 Flutter 的优化问题,在界面中存在循环动画时,CPU 占用就会异常的高。 @@ -110,29 +110,29 @@ Wi-Fi 环境可以播放,但移动网络无法播放时,首先检查资料 ::: -### CarPlay 或通知中心无法控制上一曲下一曲 +### CarPlay 或通知中心无法控制上一曲下一曲 {#carplay-notification} 请在设置中关闭 `与其他应用同时播放` 的选项。 这是 iOS 的系统特性,非软件 bug。 -## 会员 +## 会员 {#vip} -### 恢复购买时提示签名信息错误 +### 恢复购买时提示签名信息错误 {#signature} 大概率是你的设备时间和正常时间相差太大导致的。 -### 邮箱格式错误 +### 邮箱格式错误 {#email} 请手动输入邮箱,避免粘贴时可能多产生的空格和回车等空白字符。 -### 设备数量 +### 设备数量 {#device-limit} 目前新设备上线会自动将超出设备数量的旧设备踢下线,因此无需手动管理上线的设备。 -## 其他问题 +## 其他问题 {#other} -### 安全问题 +### 安全问题 {#security} 部分用户可能担心软件会私自传输他们的服务器账号密码,我觉得这种担心是没有必要的。原因如下: diff --git a/docs/services.md b/docs/services.md index 4d0e94c..b437806 100644 --- a/docs/services.md +++ b/docs/services.md @@ -2,11 +2,11 @@ sidebar_position: 2 --- -# 音乐服务 +# 音乐服务 {#services} 就我个人使用体验来讲,目前并不存在完美的音乐服务,它们各有优缺点,需要根据自己的需求酌情选择。 -## 支持版本 +## 支持版本 {#versions} - Navidrome 0.49.3 及以上 - Plex 1.29.2 及以上,支持 OTP 验证码登录,暂不支持登陆到共享音乐库 @@ -15,7 +15,7 @@ sidebar_position: 2 - Jellyfin 10.8.10 及以上 - Subsonic 1.15.0 及以上 -## 功能比较 +## 功能比较 {#comparison} `-` 未知 diff --git a/docs/tips/_category_.json b/docs/tips/_category_.json index b50b7ce..1554a8e 100644 --- a/docs/tips/_category_.json +++ b/docs/tips/_category_.json @@ -1,8 +1,8 @@ { - "label": "实用技巧", + "label": "tips", "position": 5, "link": { "type": "generated-index", - "description": "光有好剑是不够的,你还需要好的剑谱。" + "description": "Having a good sword is not enough; you also need a good sword manual." } } diff --git a/docs/tips/detection_deletion_duplicate.md b/docs/tips/detection_deletion_duplicate.md index 655b1e9..582f98f 100644 --- a/docs/tips/detection_deletion_duplicate.md +++ b/docs/tips/detection_deletion_duplicate.md @@ -2,11 +2,11 @@ sidebar_position: 2 --- -# 重复歌曲的检测与删除 +# 重复歌曲的检测与删除 {#detect} 随着曲库的增加,不可避免的会出现重复歌曲,这对于强迫症患者来讲简直太折磨了,遇到喜欢的歌曲都不知道该收藏哪个版本。 -在音流中通过[媒体库模式](../guides/sync_mode#%E5%AA%92%E4%BD%93%E5%BA%93%E6%A8%A1%E5%BC%8F)连接并完全同步了服务端的数据后,在歌手详情页中,如果该歌手的歌曲存在重复项目,则会有**重复歌曲检测**功能的入口。 +在音流中通过[媒体库模式](../guides/sync_mode#library-mode)连接并完全同步了服务端的数据后,在歌手详情页中,如果该歌手的歌曲存在重复项目,则会有**重复歌曲检测**功能的入口。 进入后即可按歌曲查看与其重复的各个版本,之后便可以根据页面的一些提示信息删除重复歌曲了。 @@ -16,7 +16,7 @@ sidebar_position: 2 ::: -但这里有个问题,如果[连接的服务端不支持删除文件](../services#%E5%8A%9F%E8%83%BD%E6%AF%94%E8%BE%83)那该怎么办呢? +但这里有个问题,如果[连接的服务端不支持删除文件](../services#comparison)那该怎么办呢? 第一个解决办法,如果音流获取到的歌曲路径是真实路径,则可以复制页面中的删除命令,在其他软件中通过 SSH 连接并执行命令即可。 diff --git a/docs/tips/focus_specific_audio.md b/docs/tips/focus_specific_audio.md index 7cc3947..5c600e0 100644 --- a/docs/tips/focus_specific_audio.md +++ b/docs/tips/focus_specific_audio.md @@ -2,7 +2,7 @@ sidebar_position: 1 --- -# 专注于某种音频类型 +# 专注于某种音频类型 {#focus} 在存放音乐之外,我们可能还会将有声读物等音频加入我们的曲库。 diff --git a/docs/tips/lastfm.md b/docs/tips/lastfm.md index a7c3dad..5f7e2b2 100644 --- a/docs/tips/lastfm.md +++ b/docs/tips/lastfm.md @@ -2,7 +2,7 @@ sidebar_position: 4 --- -# 通过 Last.FM 记录播放记录 +# 通过 Last.FM 记录播放记录 {#lastfm} 年末的时候看到别人在发**听歌年度报告**而自己却没有?不用担心,你可以通过 Last.FM 记录你的播放记录,然后就可以在年末的时候愉快地向朋友分享了~ @@ -16,17 +16,17 @@ sidebar_position: 4 ::: -## 注册 Last.FM 账号 +## 注册 Last.FM 账号 {#account} 前往 https://www.last.fm/ 注册即可,**使用 QQ 邮箱可能会导致无法注册**。 -## 通过 Navidrome 上报播放记录 +## 通过 Navidrome 上报播放记录 {#navidrome} 在 Navidrome 的网页端启用 Last.FM 的喜好记录即可。 ![](https://oss.aqzscn.cn/resource/blog/img/2024/b269c-862548c7f7e351f285de76a02c0b8389.png) -## 通过 Emby 上报播放记录 +## 通过 Emby 上报播放记录 {#emby} 在 Emby 的管理页面中,安装元数据分类的 Last.fm 插件后重启服务器,即可在侧边栏进行配置。 diff --git a/docs/tips/shortcuts.md b/docs/tips/shortcuts.md index f7bec18..d4cfd0d 100644 --- a/docs/tips/shortcuts.md +++ b/docs/tips/shortcuts.md @@ -2,7 +2,7 @@ sidebar_position: 3 --- -# 通过快捷指令播放音乐 +# 通过快捷指令播放音乐 {#shortcuts} 音流在 1.3.4 版本支持了 iOS 的快捷指令功能,以便在不打开 APP 的情况下可以通过快捷指令或 Siri 播放音乐。 @@ -25,7 +25,7 @@ sidebar_position: 3 9. 获取播放状态 10. 从歌单中添加或移除歌曲列表 -## 抛砖引玉 +## 抛砖引玉 {#example} **播放我喜欢的音乐** diff --git a/docs/versions/1.2.x.md b/docs/versions/1.2.x.md index 8532a03..cb41acf 100644 --- a/docs/versions/1.2.x.md +++ b/docs/versions/1.2.x.md @@ -82,8 +82,8 @@ import AndroidIcon from '@mui/icons-material/Android'; 文档更新: - 增加直连模式说明 -- 增加[音乐服务接口文档](/docs/category/音乐服务) -- 提供 [Navidrome 同步到 50000 首歌曲后无法继续同步的问题的解决方案](/docs/problems#navidrome-%E6%89%AB%E6%8F%8F%E5%88%B0-50000-%E9%A6%96%E6%AD%8C%E6%9B%B2%E5%90%8E%E5%A4%B1%E8%B4%A5) +- 增加音乐服务接口文档 +- 提供 [Navidrome 同步到 50000 首歌曲后无法继续同步的问题的解决方案](/docs/problems#navidrome-scan) - 补充了一些[常见问题](/docs/problems) ## V1.2.8(2024-04-17) diff --git a/docs/versions/_category_.json b/docs/versions/_category_.json index b3e4574..deb8ae4 100644 --- a/docs/versions/_category_.json +++ b/docs/versions/_category_.json @@ -1,9 +1,9 @@ { - "label": "版本列表", + "label": "versions", "position": 6, "link": { "type": "generated-index", - "description": "包含历史版本更新说明和安装包下载地址。" + "description": "Contains historical version update notes and download links for installation packages." }, "customProps": { "reversed": true diff --git a/docusaurus.config.js b/docusaurus.config.js index c03dfcb..08eae5d 100644 --- a/docusaurus.config.js +++ b/docusaurus.config.js @@ -4,52 +4,55 @@ // There are various equivalent ways to declare your Docusaurus config. // See: https://docusaurus.io/docs/api/docusaurus-config -import {themes as prismThemes} from 'prism-react-renderer'; +import { themes as prismThemes } from "prism-react-renderer"; /** @type {import('@docusaurus/types').Config} */ const config = { - title: '音流', - tagline: '连接你的音乐', - favicon: 'img/favicon.ico', + title: "音流", + tagline: "连接你的音乐", + favicon: "img/favicon.ico", // Set the production url of your site here - url: 'https://music.aqzscn.cn/', + url: "https://music.aqzscn.cn/", // Set the // pathname under which your site is served // For GitHub pages deployment, it is often '//' - baseUrl: '/', + baseUrl: "/", // GitHub pages deployment config. // If you aren't using GitHub pages, you don't need these. - organizationName: 'gitbobobo', // Usually your GitHub org/user name. - projectName: 'StreamMusic', // Usually your repo name. + organizationName: "gitbobobo", // Usually your GitHub org/user name. + projectName: "StreamMusic", // Usually your repo name. - onBrokenLinks: 'throw', - onBrokenMarkdownLinks: 'warn', + onBrokenLinks: "throw", + onBrokenMarkdownLinks: "warn", // Even if you don't use internationalization, you can use this field to set // useful metadata like html lang. For example, if your site is Chinese, you // may want to replace "en" with "zh-Hans". i18n: { - defaultLocale: 'zh-Hans', - locales: ['zh-Hans'], + defaultLocale: "zh-Hans", + locales: ["zh-Hans", "en"], }, presets: [ [ - 'classic', + "classic", /** @type {import('@docusaurus/preset-classic').Options} */ ({ docs: { - sidebarPath: './sidebars.js', + sidebarPath: "./sidebars.js", // Please change this to your repo. // Remove this to remove the "edit this page" links. - editUrl: - 'https://github.com/gitbobobo/StreamMusic/blob/main/', - async sidebarItemsGenerator({defaultSidebarItemsGenerator, ...args}) { - const sidebarItems = await defaultSidebarItemsGenerator(args); - return reverseSidebarItems(sidebarItems); - }, + editUrl: "https://github.com/gitbobobo/StreamMusic/blob/main/", + async sidebarItemsGenerator({ + defaultSidebarItemsGenerator, + ...args + }) { + const sidebarItems = await defaultSidebarItemsGenerator(args); + return reverseSidebarItems(sidebarItems); }, + }, + blog: false, // blog: { // showReadingTime: true, // // Please change this to your repo. @@ -58,7 +61,7 @@ const config = { // 'https://github.com/facebook/docusaurus/tree/main/packages/create-docusaurus/templates/shared/', // }, theme: { - customCss: './src/css/custom.css', + customCss: "./src/css/custom.css", }, }), ], @@ -68,12 +71,12 @@ const config = { /** @type {import('@docusaurus/preset-classic').ThemeConfig} */ ({ // Replace with your project's social card - image: 'img/docusaurus-social-card.jpg', + image: "img/docusaurus-social-card.jpg", navbar: { - title: '音流', + title: "音流", logo: { - alt: 'Stream Music\'s Logo', - src: 'img/logo.png', + alt: "Stream Music's Logo", + src: "img/logo.png", }, items: [ // { @@ -82,11 +85,15 @@ const config = { // position: 'left', // label: '文档', // }, - // {to: '/blog', label: '博客', position: 'left'}, { - type: 'html', - position: 'right', - value: '', + type: "localeDropdown", + position: "right", + }, + { + type: "html", + position: "right", + value: + '', }, ], }, @@ -97,7 +104,7 @@ const config = { prism: { theme: prismThemes.github, darkTheme: prismThemes.dracula, - additionalLanguages: ['json', 'php', 'dart', 'toml'], + additionalLanguages: ["json", "php", "dart", "toml"], }, }), }; @@ -105,10 +112,10 @@ const config = { function reverseSidebarItems(items, reverse = false) { // Reverse items in categories const result = items.map((item) => { - if (item.type === 'category') { + if (item.type === "category") { // console.log(item) - const r = item.customProps && item.customProps.reversed - return {...item, items: reverseSidebarItems(item.items, r)}; + const r = item.customProps && item.customProps.reversed; + return { ...item, items: reverseSidebarItems(item.items, r) }; } // console.log(item) return item; diff --git a/i18n/en/code.json b/i18n/en/code.json new file mode 100644 index 0000000..db8f883 --- /dev/null +++ b/i18n/en/code.json @@ -0,0 +1,313 @@ +{ + "theme.ErrorPageContent.title": { + "message": "This page crashed.", + "description": "The title of the fallback page when the page crashed" + }, + "theme.BackToTopButton.buttonAriaLabel": { + "message": "Scroll back to top", + "description": "The ARIA label for the back to top button" + }, + "theme.blog.archive.title": { + "message": "Archive", + "description": "The page & hero title of the blog archive page" + }, + "theme.blog.archive.description": { + "message": "Archive", + "description": "The page & hero description of the blog archive page" + }, + "theme.blog.paginator.navAriaLabel": { + "message": "Blog list page navigation", + "description": "The ARIA label for the blog pagination" + }, + "theme.blog.paginator.newerEntries": { + "message": "Newer entries", + "description": "The label used to navigate to the newer blog posts page (previous page)" + }, + "theme.blog.paginator.olderEntries": { + "message": "Older entries", + "description": "The label used to navigate to the older blog posts page (next page)" + }, + "theme.blog.post.paginator.navAriaLabel": { + "message": "Blog post page navigation", + "description": "The ARIA label for the blog posts pagination" + }, + "theme.blog.post.paginator.newerPost": { + "message": "Newer post", + "description": "The blog post button label to navigate to the newer/previous post" + }, + "theme.blog.post.paginator.olderPost": { + "message": "Older post", + "description": "The blog post button label to navigate to the older/next post" + }, + "theme.tags.tagsPageLink": { + "message": "View all tags", + "description": "The label of the link targeting the tag list page" + }, + "theme.colorToggle.ariaLabel": { + "message": "Switch between dark and light mode (currently {mode})", + "description": "The ARIA label for the navbar color mode toggle" + }, + "theme.colorToggle.ariaLabel.mode.dark": { + "message": "dark mode", + "description": "The name for the dark color mode" + }, + "theme.colorToggle.ariaLabel.mode.light": { + "message": "light mode", + "description": "The name for the light color mode" + }, + "theme.docs.DocCard.categoryDescription.plurals": { + "message": "1 item|{count} items", + "description": "The default description for a category card in the generated index about how many items this category includes" + }, + "theme.docs.breadcrumbs.navAriaLabel": { + "message": "Breadcrumbs", + "description": "The ARIA label for the breadcrumbs" + }, + "theme.docs.paginator.navAriaLabel": { + "message": "Docs pages", + "description": "The ARIA label for the docs pagination" + }, + "theme.docs.paginator.previous": { + "message": "Previous", + "description": "The label used to navigate to the previous doc" + }, + "theme.docs.paginator.next": { + "message": "Next", + "description": "The label used to navigate to the next doc" + }, + "theme.docs.versionBadge.label": { + "message": "Version: {versionLabel}" + }, + "theme.docs.versions.unreleasedVersionLabel": { + "message": "This is unreleased documentation for {siteTitle} {versionLabel} version.", + "description": "The label used to tell the user that he's browsing an unreleased doc version" + }, + "theme.docs.versions.unmaintainedVersionLabel": { + "message": "This is documentation for {siteTitle} {versionLabel}, which is no longer actively maintained.", + "description": "The label used to tell the user that he's browsing an unmaintained doc version" + }, + "theme.docs.versions.latestVersionSuggestionLabel": { + "message": "For up-to-date documentation, see the {latestVersionLink} ({versionLabel}).", + "description": "The label used to tell the user to check the latest version" + }, + "theme.docs.versions.latestVersionLinkLabel": { + "message": "latest version", + "description": "The label used for the latest version suggestion link label" + }, + "theme.docs.tagDocListPageTitle.nDocsTagged": { + "message": "One doc tagged|{count} docs tagged", + "description": "Pluralized label for \"{count} docs tagged\". Use as much plural forms (separated by \"|\") as your language support (see https://www.unicode.org/cldr/cldr-aux/charts/34/supplemental/language_plural_rules.html)" + }, + "theme.docs.tagDocListPageTitle": { + "message": "{nDocsTagged} with \"{tagName}\"", + "description": "The title of the page for a docs tag" + }, + "theme.common.editThisPage": { + "message": "Edit this page", + "description": "The link label to edit the current page" + }, + "theme.common.headingLinkTitle": { + "message": "Direct link to {heading}", + "description": "Title for link to heading" + }, + "theme.lastUpdated.atDate": { + "message": " on {date}", + "description": "The words used to describe on which date a page has been last updated" + }, + "theme.lastUpdated.byUser": { + "message": " by {user}", + "description": "The words used to describe by who the page has been last updated" + }, + "theme.lastUpdated.lastUpdatedAtBy": { + "message": "Last updated{atDate}{byUser}", + "description": "The sentence used to display when a page has been last updated, and by who" + }, + "theme.NotFound.title": { + "message": "Page Not Found", + "description": "The title of the 404 page" + }, + "theme.navbar.mobileVersionsDropdown.label": { + "message": "Versions", + "description": "The label for the navbar versions dropdown on mobile view" + }, + "theme.tags.tagsListLabel": { + "message": "Tags:", + "description": "The label alongside a tag list" + }, + "theme.admonition.caution": { + "message": "caution", + "description": "The default label used for the Caution admonition (:::caution)" + }, + "theme.admonition.danger": { + "message": "danger", + "description": "The default label used for the Danger admonition (:::danger)" + }, + "theme.admonition.info": { + "message": "info", + "description": "The default label used for the Info admonition (:::info)" + }, + "theme.admonition.note": { + "message": "note", + "description": "The default label used for the Note admonition (:::note)" + }, + "theme.admonition.tip": { + "message": "tip", + "description": "The default label used for the Tip admonition (:::tip)" + }, + "theme.admonition.warning": { + "message": "warning", + "description": "The default label used for the Warning admonition (:::warning)" + }, + "theme.AnnouncementBar.closeButtonAriaLabel": { + "message": "Close", + "description": "The ARIA label for close button of announcement bar" + }, + "theme.blog.sidebar.navAriaLabel": { + "message": "Blog recent posts navigation", + "description": "The ARIA label for recent posts in the blog sidebar" + }, + "theme.CodeBlock.copied": { + "message": "Copied", + "description": "The copied button label on code blocks" + }, + "theme.CodeBlock.copyButtonAriaLabel": { + "message": "Copy code to clipboard", + "description": "The ARIA label for copy code blocks button" + }, + "theme.CodeBlock.copy": { + "message": "Copy", + "description": "The copy button label on code blocks" + }, + "theme.CodeBlock.wordWrapToggle": { + "message": "Toggle word wrap", + "description": "The title attribute for toggle word wrapping button of code block lines" + }, + "theme.DocSidebarItem.expandCategoryAriaLabel": { + "message": "Expand sidebar category '{label}'", + "description": "The ARIA label to expand the sidebar category" + }, + "theme.DocSidebarItem.collapseCategoryAriaLabel": { + "message": "Collapse sidebar category '{label}'", + "description": "The ARIA label to collapse the sidebar category" + }, + "theme.NavBar.navAriaLabel": { + "message": "Main", + "description": "The ARIA label for the main navigation" + }, + "theme.NotFound.p1": { + "message": "We could not find what you were looking for.", + "description": "The first paragraph of the 404 page" + }, + "theme.NotFound.p2": { + "message": "Please contact the owner of the site that linked you to the original URL and let them know their link is broken.", + "description": "The 2nd paragraph of the 404 page" + }, + "theme.TOCCollapsible.toggleButtonLabel": { + "message": "On this page", + "description": "The label used by the button on the collapsible TOC component" + }, + "theme.navbar.mobileLanguageDropdown.label": { + "message": "Languages", + "description": "The label for the mobile language switcher dropdown" + }, + "theme.blog.post.readMore": { + "message": "Read more", + "description": "The label used in blog post item excerpts to link to full blog posts" + }, + "theme.blog.post.readMoreLabel": { + "message": "Read more about {title}", + "description": "The ARIA label for the link to full blog posts from excerpts" + }, + "theme.blog.post.readingTime.plurals": { + "message": "One min read|{readingTime} min read", + "description": "Pluralized label for \"{readingTime} min read\". Use as much plural forms (separated by \"|\") as your language support (see https://www.unicode.org/cldr/cldr-aux/charts/34/supplemental/language_plural_rules.html)" + }, + "theme.docs.breadcrumbs.home": { + "message": "Home page", + "description": "The ARIA label for the home page in the breadcrumbs" + }, + "theme.docs.sidebar.collapseButtonTitle": { + "message": "Collapse sidebar", + "description": "The title attribute for collapse button of doc sidebar" + }, + "theme.docs.sidebar.collapseButtonAriaLabel": { + "message": "Collapse sidebar", + "description": "The title attribute for collapse button of doc sidebar" + }, + "theme.docs.sidebar.navAriaLabel": { + "message": "Docs sidebar", + "description": "The ARIA label for the sidebar navigation" + }, + "theme.docs.sidebar.closeSidebarButtonAriaLabel": { + "message": "Close navigation bar", + "description": "The ARIA label for close button of mobile sidebar" + }, + "theme.docs.sidebar.toggleSidebarButtonAriaLabel": { + "message": "Toggle navigation bar", + "description": "The ARIA label for hamburger menu button of mobile navigation" + }, + "theme.navbar.mobileSidebarSecondaryMenu.backButtonLabel": { + "message": "← Back to main menu", + "description": "The label of the back button to return to main menu, inside the mobile navbar sidebar secondary menu (notably used to display the docs sidebar)" + }, + "theme.docs.sidebar.expandButtonTitle": { + "message": "Expand sidebar", + "description": "The ARIA label and title attribute for expand button of doc sidebar" + }, + "theme.docs.sidebar.expandButtonAriaLabel": { + "message": "Expand sidebar", + "description": "The ARIA label and title attribute for expand button of doc sidebar" + }, + "theme.blog.post.plurals": { + "message": "One post|{count} posts", + "description": "Pluralized label for \"{count} posts\". Use as much plural forms (separated by \"|\") as your language support (see https://www.unicode.org/cldr/cldr-aux/charts/34/supplemental/language_plural_rules.html)" + }, + "theme.blog.tagTitle": { + "message": "{nPosts} tagged with \"{tagName}\"", + "description": "The title of the page for a blog tag" + }, + "theme.blog.author.pageTitle": { + "message": "{authorName} - {nPosts}", + "description": "The title of the page for a blog author" + }, + "theme.blog.authorsList.pageTitle": { + "message": "Authors", + "description": "The title of the authors page" + }, + "theme.blog.authorsList.viewAll": { + "message": "View all authors", + "description": "The label of the link targeting the blog authors page" + }, + "theme.blog.author.noPosts": { + "message": "This author has not written any posts yet.", + "description": "The text for authors with 0 blog post" + }, + "theme.contentVisibility.unlistedBanner.title": { + "message": "Unlisted page", + "description": "The unlisted content banner title" + }, + "theme.contentVisibility.unlistedBanner.message": { + "message": "This page is unlisted. Search engines will not index it, and only users having a direct link can access it.", + "description": "The unlisted content banner message" + }, + "theme.contentVisibility.draftBanner.title": { + "message": "Draft page", + "description": "The draft content banner title" + }, + "theme.contentVisibility.draftBanner.message": { + "message": "This page is a draft. It will only be visible in dev and be excluded from the production build.", + "description": "The draft content banner message" + }, + "theme.ErrorPageContent.tryAgain": { + "message": "Try again", + "description": "The label of the button to try again rendering when the React error boundary captures an error" + }, + "theme.common.skipToMainContent": { + "message": "Skip to main content", + "description": "The skip to content label used for accessibility, allowing to rapidly navigate to main content with keyboard tab/enter navigation" + }, + "theme.tags.tagsPageTitle": { + "message": "Tags", + "description": "The title of the tag list page" + } +} diff --git a/i18n/en/docusaurus-plugin-content-docs/current.json b/i18n/en/docusaurus-plugin-content-docs/current.json new file mode 100644 index 0000000..0ff6d9a --- /dev/null +++ b/i18n/en/docusaurus-plugin-content-docs/current.json @@ -0,0 +1,54 @@ +{ + "version.label": { + "message": "Next", + "description": "The label for version current" + }, + "sidebar.tutorialSidebar.category.guides": { + "message": "Guides", + "description": "The label for category Guides in sidebar tutorialSidebar" + }, + "sidebar.tutorialSidebar.category.guides.link.generated-index.description": { + "message": "Installation and usage instructions for StreamMusic.", + "description": "The generated-index page description for category Guides in sidebar tutorialSidebar" + }, + "sidebar.tutorialSidebar.category.tips": { + "message": "Tips", + "description": "The label for category Tips in sidebar tutorialSidebar" + }, + "sidebar.tutorialSidebar.category.tips.link.generated-index.description": { + "message": "Having a good sword is not enough; you also need a good sword manual.", + "description": "The generated-index page description for category Tips in sidebar tutorialSidebar" + }, + "sidebar.tutorialSidebar.category.versions": { + "message": "Versions", + "description": "The label for category Versions in sidebar tutorialSidebar" + }, + "sidebar.tutorialSidebar.category.versions.link.generated-index.description": { + "message": "Contains historical version update notes and download links for installation packages.", + "description": "The generated-index page description for category Versions in sidebar tutorialSidebar" + }, + "sidebar.tutorialSidebar.category.developmentNotes": { + "message": "Notes", + "description": "The label for category developmentNotes in sidebar tutorialSidebar" + }, + "sidebar.tutorialSidebar.category.developmentNotes.link.generated-index.description": { + "message": "Experience sharing during the development of StreamMusic.", + "description": "The generated-index page description for category developmentNotes in sidebar tutorialSidebar" + }, + "sidebar.tutorialSidebar.category.musicServices": { + "message": "Music Services", + "description": "The label for category Music Services in sidebar tutorialSidebar" + }, + "sidebar.tutorialSidebar.category.musicServices.link.generated-index.description": { + "message": "Documenting some of the music service APIs used by Yinliu (not all APIs, only those related to music). This is to facilitate my own reference and to help others avoid some pitfalls. There may be some misunderstandings, and corrections and additions are welcome.", + "description": "The generated-index page description for category musicServices in sidebar tutorialSidebar" + }, + "sidebar.tutorialSidebar.category.adaptation": { + "message": "Adaptation", + "description": "The label for category Adaptation in sidebar tutorialSidebar" + }, + "sidebar.tutorialSidebar.category.adaptation.link.generated-index.description": { + "message": "Experience sharing during the development of StreamMusic.", + "description": "The generated-index page description for category Adaptation in sidebar tutorialSidebar" + } +} diff --git a/i18n/en/docusaurus-plugin-content-docs/current/features.md b/i18n/en/docusaurus-plugin-content-docs/current/features.md new file mode 100644 index 0000000..554b677 --- /dev/null +++ b/i18n/en/docusaurus-plugin-content-docs/current/features.md @@ -0,0 +1,188 @@ +--- +sidebar_position: 3 +--- + +# Features {#feature} + +Stream Music offers free basic features, while premium features require a membership purchase. + +## Pricing Model {#pricing} + +**One-time Purchase**. Buy once, use across all platforms for life. + +- Membership can be activated on up to **7 devices**. Exceeding this limit will deactivate the earliest logged-in device. +- Membership verification uses **online validation** with random checks at startup. Ensure your device has internet access. + +## Purchase & Refunds {#purchase} + +Available only on mobile devices: + +- **iOS**: Purchase via App Store. Refunds can be requested through App Store within 3 months (not controlled by the developer). +- **Android**: Purchase via Alipay. Refunds can be requested by emailing `aqzscn@qq.com` within 6 months. Include your Alipay order number. + +After purchase, bind your email to restore membership on other devices via verification codes. + +:::warning +**Bound emails cannot be modified later**. Do NOT use temporary email addresses. +::: + +## Feature Comparison {#comparison} + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
FeatureFreePremiumFeatureFreePremium
⚡️ Streaming & Playback
Cache While PlayingAuto Play
Auto DownloadSleep Timer
Sync via QR CodeReplay Gain⭕️
Download⭕️DLNA Playback1⭕️
🎙️ Lyrics
Lyrics Display & EditFloating Lyrics (Android)⭕️
Status Bar Lyrics (Android/macOS)⭕️PiP Lyrics (iOS)⭕️
📱 System Integration
Widgets (Android/iOS)Global Hotkeys (macOS/Windows)
Mini Window (macOS/Windows)⭕️CarPlay2⭕️
Shortcuts⭕️
🎈 Customization
Enhanced GesturesCustom API
Custom Content⭕️Themes⭕️
Long Audio Preferences⭕️
📁 Library Management
Multi-Server Switching⭕️Folder View (Library Mode)⭕️
Duplicate Detection (Library Mode)⭕️
🪄 Others
Fallback Servers⭕️
+ +1. DLNA compatibility is currently limited. Assume this feature may not work when purchasing. +2. Music may stop if opened via CarPlay/Shortcuts while the app is closed. Reopening the app resumes playback. + +## Beta Access {#test} + +Stream Music's beta channel: [https://devmusic.aqzscn.cn/docs/versions/latest](https://devmusic.aqzscn.cn/docs/versions/latest). +- Android: Universal APK only. Uninstall previous versions before installing. +- iOS: TestFlight available **only to existing premium users**. + +**To request TestFlight access**, email `aqzscn@qq.com` with: +**Subject**: `Request Stream Music TestFlight` +**Content** (all required): +1. Purchase proof (screenshot of Apple receipt or Alipay order with order number). +2. Email linked to your Apple ID. +3. Your primary music server type. + +Requests are processed periodically. Please be patient. \ No newline at end of file diff --git a/i18n/en/docusaurus-plugin-content-docs/current/guides/carplay.md b/i18n/en/docusaurus-plugin-content-docs/current/guides/carplay.md new file mode 100644 index 0000000..ea10681 --- /dev/null +++ b/i18n/en/docusaurus-plugin-content-docs/current/guides/carplay.md @@ -0,0 +1,24 @@ +--- +sidebar_position: 5 +--- + +# CarPlay {#carplay} + +## Lyrics Display {#lyrics} + +Due to Apple's safety restrictions, **full lyrics display is prohibited in CarPlay**. +Stream Music provides a **single-line lyric preview** by enabling the **"Lyrics - Lyric Notifications"** switch. + +## Limitations {#limitation} + +**The app must be open on your phone** for CarPlay functionality. + +When the app is closed, the Flutter engine remains uninitialized, preventing CarPlay connection detection and playback control. + +:::info + +Related issue: [CarPlay app requires Flutter App open first?](https://github.com/oguzhnatly/flutter_carplay/issues/12) + +Current workarounds allow CarPlay interface display without opening the app, but **music playback fails**, and the UI may freeze after manually launching the app. Further investigation is ongoing. + +::: \ No newline at end of file diff --git a/i18n/en/docusaurus-plugin-content-docs/current/guides/install.md b/i18n/en/docusaurus-plugin-content-docs/current/guides/install.md new file mode 100644 index 0000000..fc8ac3a --- /dev/null +++ b/i18n/en/docusaurus-plugin-content-docs/current/guides/install.md @@ -0,0 +1,67 @@ +--- +sidebar_position: 1 +--- + +# Installation {#installation} + +Installation guides for Android and Windows. Latest installers can be found [here](../versions/latest). + +## Android {#android} + +Four APK variants are provided for different chip architectures: + +- For modern smartphones (released in recent years), choose `app-arm64-v8a-release.apk`. +- If unsure about your device's architecture, download `app-release.apk`. + +:::caution + +APKs for different architectures **cannot** be installed over each other. Uninstall existing versions before switching architectures. + +::: + +## Windows {#windows} + +Windows version currently provides only **x64 MSIX** installer (Microsoft Store format). To install outside the Store, you must first install the developer certificate. + +import Button from '@mui/material/Button'; +import DownloadIcon from '@mui/icons-material/Download'; + + + +### Standard Installation {#standard} + +1. Double-click the certificate file. Select **Local Computer** during installation. + + ![Snipaste_2023-12-29_20-37-44](https://oss.aqzscn.cn/halo/2023/Snipaste_2023-12-29_20-37-44.png) + +2. Store the certificate in **Trusted Root Certification Authorities**. + + :::info + + User feedback: Adding to "Trusted People" also works. + + ::: + + ![Snipaste_2023-12-29_20-39-16](https://oss.aqzscn.cn/halo/2023/Snipaste_2023-12-29_20-39-16.png) + +3. After certificate installation, double-click the MSIX file to install Stream Music. + +### Alternative Installation (Legacy Windows) {#legacy} + +MSIX requires Windows 10 or later. For Windows 7/8 users: + +:::warning + +**Not recommended for Windows 7** due to missing dependencies and lack of Flutter support. + +::: + +1. Download the MSIX file and open it with any archive tool (e.g., 7-Zip): + + ![Snipaste_2023-12-29_20-46-42](https://oss.aqzscn.cn/halo/2023/Snipaste_2023-12-29_20-46-42.png) + +2. Extract all files to your desired installation directory. + +3. Launch via `stream_music.exe`: + + ![Snipaste_2023-12-29_20-49-22](https://oss.aqzscn.cn/halo/2023/Snipaste_2023-12-29_20-49-22.png) \ No newline at end of file diff --git a/i18n/en/docusaurus-plugin-content-docs/current/guides/interfaces.md b/i18n/en/docusaurus-plugin-content-docs/current/guides/interfaces.md new file mode 100644 index 0000000..d7d6bf0 --- /dev/null +++ b/i18n/en/docusaurus-plugin-content-docs/current/guides/interfaces.md @@ -0,0 +1,181 @@ +--- +sidebar_position: 4 +--- + +# Custom APIs {#api} + +Advanced users can customize API responses when music services lack required features. + +## Authentication (Optional) {#auth} + +| Configuration | Location | Key | Description | +| ------------- | -------------- | --------------- | ----------- | +| Authorization | Request Header | Authorization | | + +## API List {#list} + +### Get Lyrics {#lyrics} + +**Method**: GET + +| Configuration | Location | Key | Description | +| ---------------------- | ------------ | --------- | -------------------------------------------------------------------------------------------------------------------- | +| Base URL | URL | - | Example: `https://example.com/lyrics` | +| Track Title | URL Params | `title` | Final URL: `https://example.com/lyrics?title={title}` | +| Artist | URL Params | `artist` | | +| Album | URL Params | `album` | | +| File Path (Optional) | URL Params | `path` | Available for Subsonic/Navidrome. May be empty for other protocols. | +| Duration (Optional) | URL Params | `duration`| In seconds. Supported since v1.1.3. | +| Offset (Optional) | URL Params | `offset` | Start line index (0-based). Supported since v1.1.6 (fixed to 0). | +| Limit (Optional) | URL Params | `limit` | Supported since v1.1.6 (fixed to 10). | + +**Response** + +Return lyrics content directly if found. Return empty or `404` if not found. + +For pagination (v1.1.6+), set `Content-Type: application/json` and return: + +```json + [ + { + "id": "", + "title": "", + "artist": "", + "lyrics": "" + }, + ... + ] +``` + +### Confirm Lyrics {#confirm-lyrics} + +Applicable to version 1.2.0 and above. + +Triggered ​**after** a user manually clicks the ​**Save** button on the lyrics switching interface. + +Request method: POST + +| Configuration Item | Location | Key | Description | +| ------------------- | -------- | ------------- | -------------------------------------------- | +| Base URL | URL | - | Example: `https://example.com/lyrics/confirm` | +| Content Type | headers | content-type | `application/json` | + +Request Body: +```json +{ + "path": "File path", + "title": "Song title", + "artist": "Artist", + "album": "Album", + "lyrics": "Lyrics content to confirm", + "lyricsId": "Lyrics ID (may be null)" +} +``` + +Response Code: If the response code is not `20x`, display "Lyrics confirmation exception" to the user. No message is shown if there's no exception. + +:::note +Starting from v1.3.1, adjusting the lyrics offset and clicking the checkmark button will also trigger the lyrics confirmation interface. In this case, the request data will ​**exclude** the `lyricsId` field. +::: + +### Get Cover Art {#cover} + +Request method: GET + +| Configuration Item | Location | Key | Description | +| ------------------------- | ----------- | ------ | ---------------------------------------------------------------------------- | +| Base URL | URL | - | Example: `https://example.com/covers` | +| Song title (optional) | URL Params | title | Final URL: `https://example.com/covers?title=SongTitle` | +| Artist name | URL Params | artist | | +| Album name (optional) | URL Params | album | | +| File path (optional) | URL Params | path | Only exists when querying song cover art. Supported from v1.3.3 | + +Through this interface, the app can attempt to obtain different types of cover art by controlling parameters: + +- All three parameters: Get song cover art +- No song title: Get album cover art +- Only artist name: Get artist image + +**Response Body** +File stream. + +### Song Details {#detail} + +Applicable to version 1.2.4 and above. Used to redirect to the music service's song link in the browser. + +| Configuration Item | Location | Key | Description | +| ------------------------- | ----------- | ------- | ---------------------------------------------------------------------------- | +| Base URL | URL | - | Example: `https://example.com/details` | +| Song path | URL Params | path | Final URL: `https://example.com/details?path=SongPath` | +| Target | URL Params | target | Currently fixed as `detail` | + +Example configuration: + +![](https://oss.aqzscn.cn/resource/blog/img/2024/891a6-5de6d34514b48759827b9e08e0236602.png) + +**Path Replacement** +Used when your music service is deployed via Docker: the detected path might differ from the actual music file path. By replacing the first matching string through ​**Path Replacement**, it can be converted to a path recognizable by the Docker service, i.e.: + +``` +// Actual path +/volume1/music/ff.flac +// Replaced path +/app/media/ff.flac +``` + +## Simple Service {#example} + +### Configuring Nginx to Load LRC Files from Same Directory `@ZaneYork` {#nginx} + +Directly configure the API address in the app as `your_server_address + /lyrics/`. + +```conf title="default.conf" + # Redirect requests for flac/mp3 resources to corresponding LRC files + location ~* \.(flac|mp3)$ { + rewrite ^/(.*)\.(flac|mp3)$ https://$host:8443/$1.lrc redirect; + } + location /lyrics/ { + # Fix space characters being converted to plus signs in request paths + if ($args ~ ^(?.*)\+(?.*)$) { + rewrite ^(.*)$ "$1?$first%20$second?" last; + } + # %2Fmusic%2F represents your Navidrome song root directory (URL-encoded /) + if ($query_string ~* "path=%2Fmusic%2F(.+)\.(mp3|flac)$") { + set $argv1 $1; + rewrite .* https://$host:8443/lyrics/$argv1.lrc? redirect; + } + + # Nginx proxy logic for static resources in song directory omitted + } +``` + +### Serving LRC Files with PHP (Same Filename) `@lingluos` {#php} + +A PHP-based method to return LRC files on Linux systems. Requires the LRC file to have the exact same name as the audio file. + +Assume `index.php` is placed in `/var/www/html/lyrics`, and LRC files are stored in `/var/www/html/lyrics/lrc`. + +```php title="index.php" + +``` + +In theory, simply enter `http://your_domain/lyrics/` in the custom API lyrics interface to display the lyrics. + +As a side note, if the lyrics appear garbled, try converting the LRC file encoding to UTF-8. \ No newline at end of file diff --git a/i18n/en/docusaurus-plugin-content-docs/current/guides/others.md b/i18n/en/docusaurus-plugin-content-docs/current/guides/others.md new file mode 100644 index 0000000..7e6fce0 --- /dev/null +++ b/i18n/en/docusaurus-plugin-content-docs/current/guides/others.md @@ -0,0 +1,56 @@ +--- +sidebar_position: 5 +--- + +# Miscellaneous {#other} + +## Partitions {#partition} + +Some users utilize Stream Music not only for music but also for audiobooks. The partition feature helps separate different audio types to avoid interference. + +Currently, **Emby, Jellyfin, and Plex** support multiple media libraries (partitions): + +- **Emby**: Set media library type to "Audiobooks" or "Music". +- **Jellyfin/Plex**: Must use "Music" type, as their "Books" category is designed for text-based content with incompatible layouts. + +## List Elements {#list} + +- **Green index numbers** indicate unplayed tracks (useful for audiobook progress tracking). + ![](https://oss.aqzscn.cn/resource/blog/img/2024/90011-293fd6ac80a1ce9bc85adf176c807e19.png) + +- **Track numbers** in album lists show disc number (top-right) and track order (index). + ![](https://oss.aqzscn.cn/resource/blog/img/2024/90642-5882f5ffcf3b92988a49aaa5dac913c3.png) + +- **Wrench icon** in artist lists indicates duplicate files. + ![](https://oss.aqzscn.cn/resource/blog/img/2024/7ec2a-423ad72f94ace289a7d801936d02e3f6.png) + +## Cross-Device Configuration Sync {#qr} + +Sync server settings across devices via QR code or LAN URL to avoid repetitive setup: + +- Access via **"Add Library"** or **Help → Data Sync**. +- Desktop users can manually enter the URL shown below the QR code. + +:::caution + +1. Data is transmitted over LAN with basic encryption. Use only in trusted networks. +2. QR scanning uses [mobile_scanner](https://github.com/juliansteenbakker/mobile_scanner). Retry if recognition fails. + +::: + +## Lyrics {#lyrics} + +Supports **LRC format** ([Wikipedia](https://en.wikipedia.org/wiki/LRC_(file_format))). Bilingual lyrics formats: + +**Same timestamp** + +``` +[00:01.123] Lyrics +[00:01.123] Translation +``` + +**Bracketed format** + +``` +[00:01.123] Lyrics【Translation】 +``` \ No newline at end of file diff --git a/i18n/en/docusaurus-plugin-content-docs/current/guides/play.md b/i18n/en/docusaurus-plugin-content-docs/current/guides/play.md new file mode 100644 index 0000000..ab7b6eb --- /dev/null +++ b/i18n/en/docusaurus-plugin-content-docs/current/guides/play.md @@ -0,0 +1,51 @@ +--- +sidebar_position: 3 +--- + +# Playback Logic {#playback} + +Stream Music's playback logic differs slightly from common music apps, incorporating unique design philosophies. +**New users are strongly encouraged to read this section for optimal usage.** + +## Playback Modes {#mode} + +By default, Stream Music uses **Queue Playback**. Enable the loop switch to apply looping to both queue and shuffle modes. + +![](https://oss.aqzscn.cn//resource/blog/img/2023/860419a28223c06c03964ff34a5a8668.png) + +When selecting songs from a list, queue playback is the default behavior. + +![](https://oss.aqzscn.cn//resource/blog/img/2023/e3f0f7469a58b3955598f8965eeef658.png) + +If the list header includes the following buttons: +- Click the **circle icon** to jump to the current track. +- Click the **double-arrow icon** to switch the list's playback mode. + +:::tip Track Navigation Upgrade (v1.3.0): + +1. If the playback queue matches the current list, navigation jumps to the current track. +2. If the queues differ, navigation jumps to the first unplayed track in the list. + +For paginated lists: If no matching track exists on the current page, scrolling to the bottom triggers loading the next page. +::: + +**Playback mode changes from song lists are saved per list type.** +Changes from the playback queue or player page are temporary and session-only. + +## Playback Controls {#control} + +![](https://oss.aqzscn.cn//resource/blog/img/2023/15d11e29bb8e27f58378743eb86f6fd0.png) + +Control bar features: +- Tap the cover to play/pause. +- Tap the lyrics area to enter full-screen playback. +- Tap the right-side button to open the current queue. + +**Swipe gestures on lyrics area**: +- Swipe right ➔ Previous track. +- Swipe left ➔ Next track. + +## DLNA {#dlna} + +- DLNA streams the server's playback URL to devices. Ensure DLNA devices can access your server. +- For compatibility, playback status updates use **polling**, causing ~2-second delays for some operations. \ No newline at end of file diff --git a/i18n/en/docusaurus-plugin-content-docs/current/guides/sync_mode.md b/i18n/en/docusaurus-plugin-content-docs/current/guides/sync_mode.md new file mode 100644 index 0000000..2b4f7e7 --- /dev/null +++ b/i18n/en/docusaurus-plugin-content-docs/current/guides/sync_mode.md @@ -0,0 +1,54 @@ +--- +sidebar_position: 2 +--- + +# Sync Modes {#sync} + +## Library Mode {#library-mode} + +In **Library Mode**, Stream Music fully synchronizes server-side song lists, album lists, and artist lists to the local device, enabling access to complete music library data. + +This mode allows Stream Music to implement features not supported by the server, such as folder views, playlist imports, and advanced filtering. + +**However**, if your music library is large, the initial sync process may take significant time. + +### Automatic Sync {#auto} + +On app startup, Stream Music detects if the server has more songs than the local copy and performs an **incremental sync** for missing data. + +:::warning + +Incremental sync relies on querying songs by their creation time in reverse order. If songs are deleted or modified on the server, manually trigger a full sync via the sync button. + +::: + +After login, the app initializes the player to restore the last playback session. If you see a "**Player initializing**" message, wait briefly. + +### Manual Sync {#manual} + +If automatic sync fails, click the **"Sync Now"** button in the music library to force a full update. + +:::info + +Manual sync is a full update. If interrupted, incremental sync resumes on the next launch. + +::: + +## Direct Mode {#direct} + +In **Direct Mode**, Stream Music fetches data directly from the server without local synchronization. This improves initial app performance for large libraries. + +**Limitations** due to server API dependencies: + +- **Daily Mix** may include long audio tracks without duration filtering. +- No duplicate song detection. +- Folder views (for Subsonic/Navidrome/Jellyfin) only display locally cached data (manually queried folders). +- Some sorting/filtering features are unavailable. + +:::info Work in Progress... + +As someone with a growing music library, I find Direct Mode effective despite minor feature gaps. **Future optimizations will focus on Direct Mode**. + +However, functionality depends on server APIs. A long-term vision is to develop a dedicated Stream Music server for enhanced compatibility. + +::: \ No newline at end of file diff --git a/i18n/en/docusaurus-plugin-content-docs/current/intro.md b/i18n/en/docusaurus-plugin-content-docs/current/intro.md new file mode 100644 index 0000000..68dda73 --- /dev/null +++ b/i18n/en/docusaurus-plugin-content-docs/current/intro.md @@ -0,0 +1,52 @@ +--- +sidebar_position: 1 +--- + +# Introduction {#introduction} + +Stream Music is a **NAS music player** that connects to **music services**1 deployed on your NAS, allowing you to enjoy a consistent music experience across **multiple platforms**2. + +![](https://oss.aqzscn.cn/resource/blog/img/2024/60b87-ca3fefb87f148b5c03ecf069fb1f95ab.png) + +1. Currently supported music services: Subsonic, Navidrome, Emby, Jellyfin, AudioStation, Plex. +2. Currently supported platforms: Android 6.0, iOS 14, macOS 10.14, Windows 10. + +## Requirements {#requirements} + +Before using Stream Music, you must have a NAS running a music service. Stream Music is a pure music player focused on streaming your existing music library and does not provide any music content. + +:::info + +NAS stands for `Network Attached Storage`. If you are unfamiliar with this term, Stream Music may not be suitable for you. + +::: + +## Key Features {#features} + +- 🎵 Compatible with multiple music services. +- 🎙️ Lyrics display support. Custom APIs enable lyrics display even for services without native lyrics interfaces! +- 🖥️📱 Built with Flutter, delivering a near-native experience and unlimited cross-platform adaptability. + +## Download {#download} + +### iOS {#ios} + +Visit the [App Store page](https://apps.apple.com/cn/app/%E9%9F%B3%E6%B5%81-%E8%BF%9E%E6%8E%A5%E4%BD%A0%E7%9A%84%E9%9F%B3%E4%B9%90/id6449966496) or search for `音流` or `Stream Music` in the App Store. + +### Android, macOS, Windows {#other-platform} + +Since Stream Music is not yet listed on other app stores, please find the latest version for download on [this page](./versions/latest). + +:::info + +The Windows installer uses the MSIX format. For installation instructions, [click here](./guides/install). + +::: + +## Feedback & Suggestions {#feedback} + +Please submit your feedback via GitHub Issues. + +Follow our official account to receive updates about new releases: + +![](https://oss.aqzscn.cn/resource/blog/img/2024/5645e-8796c787d6453480d3e7ac250bfdd930.png) \ No newline at end of file diff --git a/i18n/en/docusaurus-plugin-content-docs/current/notes/adaptive/smtc.md b/i18n/en/docusaurus-plugin-content-docs/current/notes/adaptive/smtc.md new file mode 100644 index 0000000..224f2e9 --- /dev/null +++ b/i18n/en/docusaurus-plugin-content-docs/current/notes/adaptive/smtc.md @@ -0,0 +1,350 @@ +# SMTC 适配 + +做音乐播放器,绕不开的就是要把当前播放的媒体告知操作系统。在 Android、iOS 及 macOS 平台,[audio_service](https://github.com/ryanheise/audio_service) 插件都能很好的完成任务。 + +在 Windows 系统上,SMTC(SystemMediaTransportControls, 系统媒体传输控件) 负责显示媒体内容并提供控制按钮。 + +此前音流一直使用 [smtc_windows](https://pub.dev/packages/smtc_windows) 这个插件来完成这个任务,但使用这个插件对我来说有两个痛点: + +- 无法使用本地图片(插件作者是 spotube 的作者,软件中大部分资源都是在线获取的,所以可能未来也不会加入此功能) +- 构建产物在 x86 的机器上运行就会报错(导致我只能在 arm 架构的 Windows 系统打包)。 + +所以一直以来就想通过 [flutter_rust_bridge](https://cjycode.com/flutter_rust_bridge/quickstart) 这个插件自己实现一下,奈何自己不懂 Rust,实在不知道该怎么从一个空白文件开始写起。 + +还好最近在掘金上看到一个大佬写的文章:[Flutter 应用如何支持 SMTC](https://juejin.cn/post/7363556508603432972),遂下定决心改造当前项目。 + +## 启用 Melos + +[Melos](https://invertase.docs.page/melos) 用于管理多个包在一个存储库的协同工作,对我来说,以下两个特点是吸引我使用它的原因: + +- Executing simultaneous commands across packages.(跨包执行同时命令。) +- Listing of local packages & their dependencies.(本地包及其依赖项的列表。) + +:::tip + +启用 Melos 这个步骤对于 **SMTC 适配**来说并非必须的,只是我个人的选择。 + +如果只想了解如何适配 SMTC,直接阅读原文章 [Flutter 应用如何支持 SMTC](https://juejin.cn/post/7363556508603432972) 是更好的选择。 + +::: + +在命令行使用 `dart pub global activate melos` 命令全局激活一下,按照[官方文档指引](https://invertase.docs.page/melos/getting-started)创建对应的文件与文件夹结构。 + +``` +my_project +├── apps +│ ├── apps_1 +│ └── apps_2 +├── packages +│ ├── package_1 +│ └── package_2 +├── melos.yaml +├── pubspec.yaml +└── README.md +``` + +对于 `melos.yaml`,我提供一个目前我所使用的示例文件,仅供参考: + +```yaml title="melos.yaml" +name: stream_music + +packages: + - apps/**/ + - packages/**/ + +ignore: + - '**/.dart_tool/**' + - '**/.idea/**' + - '**/.vscode/**' + - '**/build/**' + - '**/example' + +scripts: + gen:frb: + run: | + melos exec -c 6 --fail-fast -- \ + "flutter_rust_bridge_codegen generate" + description: | + Generate dart files for FlutterRustBridge. + packageFilters: + scope: 'stream_music' + + build:windows: + run: | + melos exec -c 6 --fail-fast -- \ + "flutter build windows" + description: | + Build app for Windows. + packageFilters: + dirExists: + - windows + scope: 'stream_music' + + build:msix: + run: | + melos exec -c 6 --fail-fast -- \ + "dart run msix:publish" + description: | + Build app for Windows msix. + packageFilters: + dirExists: + - windows + scope: 'stream_music' + + build:ios: + run: | + melos exec -c 6 --fail-fast -- \ + "flutter build ios --config-only" + description: | + 更新 iOS 应用配置. + packageFilters: + dirExists: + - ios + scope: 'stream_music' + + clean: + run: | + melos exec -c 6 --fail-fast -- \ + "flutter clean" + description: | + Clean all packages. + packageFilters: + scope: 'stream_music' +``` + +## 迁移项目 + +在 Rust 安装完成后,执行命令 `cargo install flutter_rust_bridge_codegen` 以安装 flutter_rust_bridge 的脚手架工具。 + +尽管 flutter_rust_bridge 提供了迁移已有项目的命令:`flutter_rust_bridge_codegen integrate`,但似乎在我的项目中并不好用。 + +因此我直接使用 `flutter_rust_bridge_codegen create my_app` 命令创建一个新的项目,然后一点点比较新旧项目的不同,最后也算是完成了迁移。 + +迁移完成后,使用 `melos bs` 命令引导一下项目,这个命令会安装所有包依赖项,并将所有本地包链接在一起,而且在 `melos.yaml` 中定义的脚本命令也会直接出现在 Android Studio 的运行配置中。 + +![](https://oss.aqzscn.cn/resource/blog/img/2024/2877c-551314d6ad5cb4871c61dd880b232e37.png) + +## 适配 SMTC + +这个步骤大部分参考大佬的 [Flutter 应用如何支持 SMTC](https://juejin.cn/post/7363556508603432972),再次表示感谢~ + +添加 Rust 依赖: + +```toml title="rust/Cargo.toml" +[dependencies] +flutter_rust_bridge = "=2.3.0" +windows = { version = "0.58.0", features = [ + "Media_Playback", + "Storage", + "Storage_Streams", + "Storage_FileProperties" +]} +``` + +添加 `smtc` 模块 + +```rust title="rust/src/api/mod.rs" +pub mod smtc; + +``` + +补充 `smtc` 逻辑,这里的代码与原文的有些许不同,主要目的是为了能在不同平台编译通过,且尽量不修改 dart 的调用代码。 + +```rust title="rust/src/api/smtc.rs" +use flutter_rust_bridge::frb; +#[cfg(target_os = "windows")] +use windows::{ + core::HSTRING, + Foundation::TypedEventHandler, + Media::{ + MediaPlaybackStatus, MediaPlaybackType, Playback::MediaPlayer, + SystemMediaTransportControls, SystemMediaTransportControlsButton, + SystemMediaTransportControlsButtonPressedEventArgs, + }, + Storage::{FileProperties::ThumbnailMode, StorageFile, Streams::RandomAccessStreamReference}, +}; + +use crate::frb_generated::StreamSink; + +pub struct SmtcFlutter { + #[cfg(target_os = "windows")] + _smtc: SystemMediaTransportControls, + #[cfg(target_os = "windows")] + _player: MediaPlayer, +} + +pub enum SMTCControlEvent { + Play, + Pause, + Previous, + Next, + Unknown, +} + +pub enum SMTCState { + Paused, + Playing, +} + +/// Apis for Flutter +impl SmtcFlutter { + #[frb(sync)] + pub fn new() -> Self { + #[cfg(target_os = "windows")] + return Self::_new().unwrap(); + #[cfg(not(target_os = "windows"))] + return SmtcFlutter {}; + } + + pub fn subscribe_to_control_events(&self, sink: StreamSink) { + #[cfg(target_os = "windows")] + self._smtc + .ButtonPressed(&TypedEventHandler::< + SystemMediaTransportControls, + SystemMediaTransportControlsButtonPressedEventArgs, + >::new(move |_, event| { + let event = event.as_ref().unwrap().Button().unwrap(); + let event = match event { + SystemMediaTransportControlsButton::Play => SMTCControlEvent::Play, + SystemMediaTransportControlsButton::Pause => SMTCControlEvent::Pause, + SystemMediaTransportControlsButton::Next => SMTCControlEvent::Next, + SystemMediaTransportControlsButton::Previous => SMTCControlEvent::Previous, + _ => SMTCControlEvent::Unknown, + }; + sink.add(event).unwrap(); + + Ok(()) + })) + .unwrap(); + } + + pub fn update_state(&self, state: SMTCState) { + #[cfg(target_os = "windows")] + self._update_state(state).unwrap(); + } + + pub fn update_display(&self, title: String, artist: String, album: String, path: Option) { + #[cfg(target_os = "windows")] + self._update_display( + HSTRING::from(title), + HSTRING::from(artist), + HSTRING::from(album), + path.map(HSTRING::from), + ) + .unwrap(); + } + + pub fn close(self) { + #[cfg(target_os = "windows")] + self._player.Close().unwrap(); + } +} + +#[cfg(target_os = "windows")] +impl SmtcFlutter { + fn _init_controls(smtc: &SystemMediaTransportControls) -> Result<(), windows::core::Error> { + // 下一首 + smtc.SetIsNextEnabled(true)?; + // 暂停 + smtc.SetIsPauseEnabled(true)?; + // 播放(恢复) + smtc.SetIsPlayEnabled(true)?; + // 上一首 + smtc.SetIsPreviousEnabled(true)?; + + Ok(()) + } + + fn _new() -> Result { + let _player = MediaPlayer::new()?; + _player.CommandManager()?.SetIsEnabled(false)?; + + let _smtc = _player.SystemMediaTransportControls()?; + Self::_init_controls(&_smtc)?; + + Ok(Self { _smtc, _player }) + } + + fn _update_state(&self, state: SMTCState) -> Result<(), windows::core::Error> { + let state = match state { + SMTCState::Playing => MediaPlaybackStatus::Playing, + SMTCState::Paused => MediaPlaybackStatus::Paused, + }; + self._smtc.SetPlaybackStatus(state)?; + + Ok(()) + } + + fn _update_display( + &self, + title: HSTRING, + artist: HSTRING, + album: HSTRING, + path: Option, + ) -> Result<(), windows::core::Error> { + let updater = self._smtc.DisplayUpdater()?; + updater.SetType(MediaPlaybackType::Music)?; + + let music_properties = updater.MusicProperties()?; + music_properties.SetTitle(&title)?; + music_properties.SetArtist(&artist)?; + music_properties.SetAlbumTitle(&album)?; + + if let Some(path) = path { + let file = StorageFile::GetFileFromPathAsync(&path)?.get()?; + let thumbnail = file + .GetThumbnailAsyncOverloadDefaultSizeDefaultOptions(ThumbnailMode::MusicView)? + .get()?; + updater.SetThumbnail(&RandomAccessStreamReference::CreateFromStream(&thumbnail)?)?; + } + + updater.Update()?; + + if !(self._smtc.IsEnabled()?) { + self._smtc.SetIsEnabled(true)?; + } + + Ok(()) + } +} +``` + +执行命令 `flutter_rust_bridge_codegen generate` 生成 dart 端代码,根据自己的业务逻辑进行调用即可。 + +```dart title="audio_handler.dart" +SmtcFlutter? _smtc; + +if (Platform.isWindows) { + _smtc = SmtcFlutter(); + // 监听点击事件 + _smtc?.subscribeToControlEvents().listen((event) { + switch (event) { + case SMTCControlEvent.play: + play(); + break; + case SMTCControlEvent.pause: + pause(); + break; + case SMTCControlEvent.previous: + skipToPrevious(); + break; + case SMTCControlEvent.next: + skipToNext(); + break; + case SMTCControlEvent.unknown: + break; + } + }); +} + +// 更新媒体信息 +_smtc?.updateDisplay( + title: event?.title ?? '', + artist: event?.artist ?? '', + album: event?.album ?? '', + path: event?.artUri?.toString(), +); + +// 更新播放状态 +_smtc?.updateState(state: event ? SMTCState.playing : SMTCState.paused); +``` diff --git a/i18n/en/docusaurus-plugin-content-docs/current/notes/plugins.md b/i18n/en/docusaurus-plugin-content-docs/current/notes/plugins.md new file mode 100644 index 0000000..026cce3 --- /dev/null +++ b/i18n/en/docusaurus-plugin-content-docs/current/notes/plugins.md @@ -0,0 +1,551 @@ +--- +sidebar_position: 1 +--- + +# Used Plugins + +Without the support of open-source projects, StreamMusic wouldn't exist as it does today~ + +If any feature of StreamMusic has helped you, consider giving these projects a 🌟 or supporting them. + +## UI {#ui} + +### macos_ui + +[macos_ui](https://github.com/macosui/macos_ui), like Flutter's built-in Material Widgets and Cupertino Widgets, implements component libraries following macOS design guidelines. + +[Click here to view](https://macosui.github.io/macos_ui/#/) the live demo. + +![](https://oss.aqzscn.cn/resource/blog/img/2024/84187-8f40569512f26dedc256e709c73a4de9.png) + +### fluent_ui + +[fluent_ui](https://github.com/bdlukaa/fluent_ui) implements Windows Fluent Design components. Compared to `macos_ui`, its components are more feature-complete. + +[Click here to view](https://bdlukaa.github.io/fluent_ui/) the live demo. + +![](https://oss.aqzscn.cn/resource/blog/img/2024/65e8d-fc810c37b1bd57ddc5161705e21c8343.png) + +### pulldown_button + +[pulldown_button](https://github.com/notDmDrl/pull_down_button) implements iOS-style pull-down menus with smooth animations. It would be perfect if it supported nested menus. + +![](https://oss.aqzscn.cn/resource/blog/img/2024/faae1-9ad0cf3582cd7ff590b3e02645f19bc7.png) + +## Platform Features {#platform} + +### macos_window_utils + +[macos_window_utils](https://github.com/macosui/macos_window_utils.dart), another creation by the `macos_ui` author, controls window properties like blur effect and title bar visibility. Best used with macos_ui. + +### flutter_acrylic + +[flutter_acrylic](https://github.com/alexmercerind/flutter_acrylic) enables window blur/transparency effects on macOS/Windows/Linux. On macOS, it depends on `macos_window_utils`, so no need to include both. + +### window_manager + +[window_manager](https://pub.dev/packages/window_manager) controls window properties on desktop platforms. + +### tray_manager + +[tray_manager](https://pub.dev/packages/tray_manager) customizes system tray behaviors. + +### windows_taskbar + +[windows_taskbar](https://pub.dev/packages/windows_taskbar) adds preview buttons and progress bars to Windows taskbar icons - particularly useful for music players. + +### flutter_carplay + +[flutter_carplay](https://pub.dev/packages/flutter_carplay) enables CarPlay support for iOS (minimum iOS 14). + +The original author hasn't updated it recently - consider using community forks. +A current challenge is launching the Flutter engine when CarPlay connects while ensuring smooth app switching between CarPlay and phone. + +### flutter_rust_bridge + +As the name suggests, [flutter_rust_bridge](https://cjycode.com/flutter_rust_bridge/quickstart) bridges Flutter and Rust by generating glue code. Flutter apps gain access to Rust's rich ecosystem, while Rust apps leverage Flutter's cross-platform UI. + +I've followed this project for years but hesitated to adopt it due to my Rust inexperience. After optimizing Windows SMTC features using [this guide](https://juejin.cn/post/7363556508603432972), I've gained [some insights](./adaptive/smtc) and feel ready to migrate. + +The core workflow when using Rust plugins involves two steps: + +```rust title="image.rs" +// Define struct +pub struct RsImage {} +// Implement methods +impl RsImage { + pub fn blur(img_path: String) -> Result, String> { + // Use Rust crates to implement features... + } +} +``` +For a Rust Newbie, Understanding ownership concepts is essential. Lifetime issues might arise, but my current principle is to resolve compiler errors without explicit lifetime annotations whenever possible. As a simple API caller, I haven't encountered situations requiring manual lifetime tagging yet. + +Compared to other languages, Rust does feel awkward initially - error handling patterns, optional unwrapping, ownership borrowing etc. But most issues can be solved with GPT's help. The benefits of accessing Rust's cross-platform ecosystem make these growing pains worthwhile 😄 + +## Networking {#network} + +### dio + +[dio](https://pub.dev/packages/dio) is a powerful HTTP client featuring interceptors, request cancellation, and custom adapters. Community plugins further simplify networking tasks. + +Dart's native HTTP client only handles standard requests properly. Non-standard implementations can fail, making it inferior to platform-native networking. + +While dio offers [native_dio_adapter](https://github.com/cfug/dio/tree/main/plugins/native_dio_adapter) for native platform requests, it only works on Android/iOS - insufficient for StreamMusic's multi-platform needs. + +### rhttp(rust) + +[rhttp](https://github.com/Tienisto/rhttp) leverages Rust's renowned `reqwest` for requests, offering better compatibility than Dart's HTTP stack. + +StreamMusic v1.3.2 uses rhttp for image fetching and song caching with improved compatibility and system proxy support. Future plans include migrating all networking to rhttp if stability holds. + +### rupnp(rust) + +[rupnp](https://github.com/jakobhellermann/rupnp) (Rust UPnP) replaced Dart's [upnp2](https://github.com/daniel-naegele/upnp.dart) due to mysterious Windows DLNA device discovery failures with the Dart version. + +### shelf + +[shelf](https://pub.dev/packages/shelf) creates local HTTP servers, with [shelf_proxy](https://pub.dev/packages/shelf_proxy) simplifying proxy setups. Note: iOS suspends local servers when app enters background - implement detection and restart mechanisms. + +### connectivity_plus + +[connectivity_plus](https://pub.dev/packages/connectivity_plus) monitors network status changes. + +### network_info_plus + +[network_info_plus](https://pub.dev/packages/network_info_plus) retrieves Wi-Fi details like IP addresses. + +### url_launcher + +[url_launcher](https://pub.dev/packages/url_launcher) opens URLs in default browsers. + +## Audio {#audio} + +### media_kit + +[media_kit](https://github.com/media-kit/media-kit) uses MPV under the hood, achieving unparalleled [format compatibility](https://github.com/media-kit/media-kit?tab=readme-ov-file#supported-formats) for audio/video playback - a key differentiator from other Flutter players. + +
+ Supported Formats (Click to Expand) + +- 3dostr 3DO STR +- 4xm 4X Technologies +- aa Audible AA format files +- aac raw ADTS AAC (Advanced Audio Coding) +- aax CRI AAX +- ac3 raw AC-3 +- ace tri-Ace Audio Container +- acm Interplay ACM +- act ACT Voice file format +- adf Artworx Data Format +- adp ADP +- ads Sony PS2 ADS +- adx CRI ADX +- aea MD STUDIO audio +- afc AFC +- aiff Audio IFF +- aix CRI AIX +- alaw PCM A-law +- alias_pix Alias/Wavefront PIX image +- alp LEGO Racers ALP +- amr 3GPP AMR +- amrnb raw AMR-NB +- amrwb raw AMR-WB +- anm Deluxe Paint Animation +- apac raw APAC +- apc CRYO APC +- ape Monkey's Audio +- apm Ubisoft Rayman 2 APM +- apng Animated Portable Network Graphics +- aptx raw aptX +- aptx_hd raw aptX HD +- aqtitle AQTitle subtitles +- argo_asf Argonaut Games ASF +- argo_brp Argonaut Games BRP +- argo_cvg Argonaut Games CVG +- asf ASF (Advanced / Active Streaming Format) +- asf_o ASF (Advanced / Active Streaming Format) +- ass SSA (SubStation Alpha) subtitle +- ast AST (Audio Stream) +- au Sun AU +- av1 AV1 Annex B +- avi AVI (Audio Video Interleaved) +- avr AVR (Audio Visual Research) +- avs Argonaut Games Creature Shock +- avs2 raw AVS2-P2/IEEE1857.4 +- avs3 raw AVS3-P2/IEEE1857.10 +- bethsoftvid Bethesda Softworks VID +- bfi Brute Force & Ignorance +- bfstm BFSTM (Binary Cafe Stream) +- bin Binary text +- bink Bink +- binka Bink Audio +- bit G.729 BIT file format +- bitpacked Bitpacked +- bmp_pipe piped bmp sequence +- bmv Discworld II BMV +- boa Black Ops Audio +- bonk raw Bonk +- brender_pix BRender PIX image +- brstm BRSTM (Binary Revolution Stream) +- c93 Interplay C93 +- caf Apple CAF (Core Audio Format) +- cavsvideo raw Chinese AVS (Audio Video Standard) +- cdg CD Graphics +- cdxl Commodore CDXL video +- cine Phantom Cine +- codec2 codec2 .c2 demuxer +- codec2raw raw codec2 demuxer +- concat Virtual concatenation script +- cri_pipe piped cri sequence +- dash Dynamic Adaptive Streaming over HTTP +- data raw data +- daud D-Cinema audio +- dcstr Sega DC STR +- dds_pipe piped dds sequence +- derf Xilam DERF +- dfa Chronomaster DFA +- dfpwm raw DFPWM1a +- dhav Video DAV +- dirac raw Dirac +- dnxhd raw DNxHD (SMPTE VC-3) +- dpx_pipe piped dpx sequence +- dsf DSD Stream File (DSF) +- dshow DirectShow capture +- dsicin Delphine Software International CIN +- dss Digital Speech Standard (DSS) +- dts raw DTS +- dtshd raw DTS-HD +- dv DV (Digital Video) +- dvbsub raw dvbsub +- dvbtxt dvbtxt +- dxa DXA +- ea Electronic Arts Multimedia +- ea_cdata Electronic Arts cdata +- eac3 raw E-AC-3 +- epaf Ensoniq Paris Audio File +- exr_pipe piped exr sequence +- f32be PCM 32-bit floating-point big-endian +- f32le PCM 32-bit floating-point little-endian +- f64be PCM 64-bit floating-point big-endian +- f64le PCM 64-bit floating-point little-endian +- ffmetadata FFmpeg metadata in text +- film_cpk Sega FILM / CPK +- filmstrip Adobe Filmstrip +- fits Flexible Image Transport System +- flac raw FLAC +- flic FLI/FLC/FLX animation +- flv FLV (Flash Video) +- frm Megalux Frame +- fsb FMOD Sample Bank +- fwse Capcom's MT Framework sound +- g722 raw G.722 +- g723_1 G.723.1 +- g726 raw big-endian G.726 ("left aligned") +- g726le raw little-endian G.726 ("right aligned") +- g729 G.729 raw format demuxer +- gdigrab GDI API Windows frame grabber +- gdv Gremlin Digital Video +- gem_pipe piped gem sequence +- genh GENeric Header +- gif CompuServe Graphics Interchange Format (GIF) +- gif_pipe piped gif sequence +- gsm raw GSM +- gxf GXF (General eXchange Format) +- h261 raw H.261 +- h263 raw H.263 +- h264 raw H.264 video +- hca CRI HCA +- hcom Macintosh HCOM +- hdr_pipe piped hdr sequence +- hevc raw HEVC video +- hls Apple HTTP Live Streaming +- hnm Cryo HNM v4 +- ico Microsoft Windows ICO +- idcin id Cinematic +- idf iCE Draw File +- iff IFF (Interchange File Format) +- ifv IFV CCTV DVR +- ilbc iLBC storage +- image2 image2 sequence +- image2pipe piped image2 sequence +- imf IMF (Interoperable Master Format) +- ingenient raw Ingenient MJPEG +- ipmovie Interplay MVE +- ipu raw IPU Video +- ircam Berkeley/IRCAM/CARL Sound Format +- iss Funcom ISS +- iv8 IndigoVision 8000 video +- ivf On2 IVF +- ivr IVR (Internet Video Recording) +- j2k_pipe piped j2k sequence +- jacosub JACOsub subtitle format +- jpeg_pipe piped jpeg sequence +- jpegls_pipe piped jpegls sequence +- jpegxl_pipe piped jpegxl sequence +- jv Bitmap Brothers JV +- kux KUX (YouKu) +- kvag Simon & Schuster Interactive VAG +- laf LAF (Limitless Audio Format) +- lavfi Libavfilter virtual input device +- live_flv live RTMP FLV (Flash Video) +- lmlm4 raw lmlm4 +- loas LOAS AudioSyncStream +- lrc LRC lyrics +- luodat Video CCTV DAT +- lvf LVF +- lxf VR native stream (LXF) +- m4v raw MPEG-4 video +- matroska,webm Matroska / WebM +- mca MCA Audio Format +- mcc MacCaption +- mgsts Metal Gear Solid: The Twin Snakes +- microdvd MicroDVD subtitle format +- mjpeg raw MJPEG video +- mjpeg_2000 raw MJPEG 2000 video +- mlp raw MLP +- mlv Magic Lantern Video (MLV) +- mm American Laser Games MM +- mmf Yamaha SMAF +- mods MobiClip MODS +- moflex MobiClip MOFLEX +- mov,mp4,m4a,3gp,3g2,mj2 QuickTime / MOV +- mp3 MP2/3 (MPEG audio layer 2/3) +- mpc Musepack +- mpc8 Musepack SV8 +- mpeg MPEG-PS (MPEG-2 Program Stream) +- mpegts MPEG-TS (MPEG-2 Transport Stream) +- mpegtsraw raw MPEG-TS (MPEG-2 Transport Stream) +- mpegvideo raw MPEG video +- mpjpeg MIME multipart JPEG +- mpl2 MPL2 subtitles +- mpsub MPlayer subtitles +- msf Sony PS3 MSF +- msnwctcp MSN TCP Webcam stream +- msp Microsoft Paint (MSP)) +- mtaf Konami PS2 MTAF +- mtv MTV +- mulaw PCM mu-law +- musx Eurocom MUSX +- mv Silicon Graphics Movie +- mvi Motion Pixels MVI +- mxf MXF (Material eXchange Format) +- mxg MxPEG clip +- nc NC camera feed +- nistsphere NIST SPeech HEader REsources +- nsp Computerized Speech Lab NSP +- nsv Nullsoft Streaming Video +- nut NUT +- nuv NuppelVideo +- obu AV1 low overhead OBU +- ogg Ogg +- oma Sony OpenMG audio +- paf Amazing Studio Packed Animation File +- pam_pipe piped pam sequence +- pbm_pipe piped pbm sequence +- pcx_pipe piped pcx sequence +- pfm_pipe piped pfm sequence +- pgm_pipe piped pgm sequence +- pgmyuv_pipe piped pgmyuv sequence +- pgx_pipe piped pgx sequence +- phm_pipe piped phm sequence +- photocd_pipe piped photocd sequence +- pictor_pipe piped pictor sequence +- pjs PJS (Phoenix Japanimation Society) subtitles +- pmp Playstation Portable PMP +- png_pipe piped png sequence +- pp_bnk Pro Pinball Series Soundbank +- ppm_pipe piped ppm sequence +- psd_pipe piped psd sequence +- psxstr Sony Playstation STR +- pva TechnoTrend PVA +- pvf PVF (Portable Voice Format) +- qcp QCP +- qdraw_pipe piped qdraw sequence +- qoi_pipe piped qoi sequence +- r3d REDCODE R3D +- rawvideo raw video +- realtext RealText subtitle format +- redspark RedSpark +- rka RKA (RK Audio) +- rl2 RL2 +- rm RealMedia +- roq id RoQ +- rpl RPL / ARMovie +- rsd GameCube RSD +- rso Lego Mindstorms RSO +- rtp RTP input +- rtsp RTSP input +- s16be PCM signed 16-bit big-endian +- s16le PCM signed 16-bit little-endian +- s24be PCM signed 24-bit big-endian +- s24le PCM signed 24-bit little-endian +- s32be PCM signed 32-bit big-endian +- s32le PCM signed 32-bit little-endian +- s337m SMPTE 337M +- s8 PCM signed 8-bit +- sami SAMI subtitle format +- sap SAP input +- sbc raw SBC (low-complexity subband codec) +- sbg SBaGen binaural beats script +- scc Scenarist Closed Captions +- scd Square Enix SCD +- sdns Xbox SDNS +- sdp SDP +- sdr2 SDR2 +- sds MIDI Sample Dump Standard +- sdx Sample Dump eXchange +- ser SER (Simple uncompressed video format for astronomical capturing) +- sga Digital Pictures SGA +- sgi_pipe piped sgi sequence +- shn raw Shorten +- siff Beam Software SIFF +- simbiosis_imx Simbiosis Interactive IMX +- sln Asterisk raw pcm +- smjpeg Loki SDL MJPEG +- smk Smacker +- smush LucasArts Smush +- sol Sierra SOL +- sox SoX native +- spdif IEC 61937 (compressed data in S/PDIF) +- srt SubRip subtitle +- stl Spruce subtitle format +- subviewer SubViewer subtitle format +- subviewer1 SubViewer v1 subtitle format +- sunrast_pipe piped sunrast sequence +- sup raw HDMV Presentation Graphic Stream subtitles +- svag Konami PS2 SVAG +- svg_pipe piped svg sequence +- svs Square SVS +- swf SWF (ShockWave Flash) +- tak raw TAK +- tedcaptions TED Talks captions +- thp THP +- tiertexseq Tiertex Limited SEQ +- tiff_pipe piped tiff sequence +- tmv 8088flex TMV +- truehd raw TrueHD +- tta TTA (True Audio) +- tty Tele-typewriter +- txd Renderware TeXture Dictionary +- ty TiVo TY Stream +- u16be PCM unsigned 16-bit big-endian +- u16le PCM unsigned 16-bit little-endian +- u24be PCM unsigned 24-bit big-endian +- u24le PCM unsigned 24-bit little-endian +- u32be PCM unsigned 32-bit big-endian +- u32le PCM unsigned 32-bit little-endian +- u8 PCM unsigned 8-bit +- v210 Uncompressed 4:2:2 10-bit +- v210x Uncompressed 4:2:2 10-bit +- vag Sony PS2 VAG +- vbn_pipe piped vbn sequence +- vc1 raw VC-1 +- vc1test VC-1 test bitstream +- vfwcap VfW video capture +- vidc PCM Archimedes VIDC +- vividas Vividas VIV +- vivo Vivo +- vmd Sierra VMD +- vobsub VobSub subtitle format +- voc Creative Voice +- vpk Sony PS2 VPK +- vplayer VPlayer subtitles +- vqf Nippon Telegraph and Telephone Corporation (NTT) TwinVQ +- w64 Sony Wave64 +- wady Marble WADY +- wav WAV / WAVE (Waveform Audio) +- wavarc Waveform Archiver +- wc3movie Wing Commander III movie +- webm_dash_manifest WebM DASH Manifest +- webp_pipe piped webp sequence +- webvtt WebVTT subtitle +- wsaud Westwood Studios audio +- wsd Wideband Single-bit Data (WSD) +- wsvqa Westwood Studios VQA +- wtv Windows Television (WTV) +- wv WavPack +- wve Psion 3 audio +- xa Maxis XA +- xbin eXtended BINary text (XBIN) +- xbm_pipe piped xbm sequence +- xmd Konami XMD +- xmv Microsoft XMV +- xpm_pipe piped xpm sequence +- xvag Sony PS3 XVAG +- xwd_pipe piped xwd sequence +- xwma Microsoft xWMA +- yop Psygnosis YOP +- yuv4mpegpipe YUV4MPEG pipe + +
+ +I can't contain my enthusiasm for this plugin. After struggling to find a worthy replacement for `just_audio`, media_kit has effortlessly resolved numerous playback issues I previously encountered. + +So folks, now that you've read this, go show some love and [give media_kit a 🌟](https://github.com/media-kit/media-kit)! + +:::note Dev Tip + +Using default configurations may cause crashes when playing high-bitrate files on HarmonyOS. Apply this workaround: + +```dart +NativePlayer get nativePlayer => player.platform as NativePlayer; + +if (Platform.isAndroid) { + // Fix crash on HarmonyOS when playing high-bitrate files + await nativePlayer.setProperty("ao", "audiotrack,opensles,"); +} +``` + +此处代码参考了 [spotube](https://github.com/KRTirtho/spotube) 项目的 [custom_player.dart](https://github.com/KRTirtho/spotube/blob/cb95663412fcc9a829c5657e0160132f13fb0649/lib/services/audio_player/custom_player.dart#L68)。 + +::: + +### audio_service + +[audio_service](https://github.com/ryanheise/audio_service) handles system music notifications, media controls interaction, and background playback persistence. + +Currently supports Android, iOS, macOS. For Windows support, see my [modified version](https://github.com/gitbobobo/audio_service) which: +1. Adds Android status bar lyrics support +2. Implements Windows media notifications via [smtc_windows](https://github.com/KRTirtho/smtc_windows) + +### lofty(rust) + +[lofty](https://github.com/Serial-ATA/lofty-rs) - Rust library for reading audio file metadata tags. Supported formats: + +| File Format | Metadata Format(s) | +|----------------|------------------------------------| +| AAC (ADTS) | `ID3v2`, `ID3v1` | +| Ape | `APE`, `ID3v2`\*, `ID3v1` | +| AIFF | `ID3v2`, `Text Chunks` | +| FLAC | `Vorbis Comments`, `ID3v2`\* | +| MP3 | `ID3v2`, `ID3v1`, `APE` | +| MP4 | `iTunes-style ilst` | +| MPC | `APE`, `ID3v2`\*, `ID3v1`\* | +| Opus | `Vorbis Comments` | +| Ogg Vorbis | `Vorbis Comments` | +| Speex | `Vorbis Comments` | +| WAV | `ID3v2`, `RIFF INFO` | +| WavPack | `APE`, `ID3v1` | + +\* Read-only due to lack of official spec support + +## Flutter Community {#community} + +### Flutter Candies + +[Flutter Candies](https://github.com/fluttercandies) - A thriving plugin ecosystem that fills gaps in official components' functionality. The maintainer actively shares insights on [Juejin](https://juejin.cn/user/254742428916408/posts) (I'm a silent follower 😉). + +StreamMusic uses these Flutter Candies plugins: + +- [extended_nested_scroll_view](https://github.com/fluttercandies/extended_nested_scroll_view) + Solves transparent header scrolling issues and syncs list scrolling in song list/artist detail pages. + +- [loading_more_list](https://github.com/fluttercandies/loading_more_list) + Implements "load more" functionality without ScrollController. + +- [scrollview_observer](https://github.com/fluttercandies/flutter_scrollview_observer) + Powers dynamic grid/list view switching with AZ jumping. + +- [flutter_smart_dialog](https://github.com/fluttercandies/flutter_smart_dialog) + Context-free global dialogs/toasts. \ No newline at end of file diff --git a/i18n/en/docusaurus-plugin-content-docs/current/notes/resources.md b/i18n/en/docusaurus-plugin-content-docs/current/notes/resources.md new file mode 100644 index 0000000..e44a69f --- /dev/null +++ b/i18n/en/docusaurus-plugin-content-docs/current/notes/resources.md @@ -0,0 +1,30 @@ +--- +sidebar_position: 3 +--- + +# Design Resources {#resource} + +Below are the **royalty-free materials** used in **Stream Music APP**. If your copyrighted work is unintentionally infringed or you have royalty-free materials suitable for the app, please email [aqzscn@qq.com](mailto:aqzscn@qq.com) for removal or contribution. Thank you! + +## Image Backgrounds {#image} + +- [Default Album Art for Song Lists](https://pixabay.com/zh/vectors/record-disk-disc-music-musical-23281/) by Clker-Free-Vector-Images +- [TV Edition Artist Page Default Background](https://pixabay.com/zh/photos/concert-singer-singing-stage-2566002/) by StockSnap + +## Color Illustrations {#illustrations} + +- [Warning](https://pixabay.com/zh/vectors/warning-caution-alert-icon-media-145066/) by OpenClipart-Vectors +- [Success](https://pixabay.com/zh/vectors/icon-symbol-confirmation-hook-ok-803718/) by Leovinus +- [Error](https://pixabay.com/zh/vectors/error-button-failure-warning-24842/) by Clker-Free-Vector-Images +- [Info](https://pixabay.com/zh/vectors/info-information-tips-icon-support-148099/) by OpenClipart-Vectors + +## Monochrome Illustrations {#mono} + +- [TV Edition Membership Page](https://pixabay.com/zh/vectors/jewelry-crown-king-royal-queen-2025491/) by OpenClipart-Vectors +- [TV Edition About Page](https://pixabay.com/zh/vectors/found-icons-requests-1294561/) by OpenClipart-Vectors +- [TV Edition Help Page](https://pixabay.com/zh/vectors/drum-drums-jazz-kit-music-1295656/) by OpenClipart-Vectors +- [TV Edition Track Details Page](https://pixabay.com/zh/vectors/b-w-guitar-instrument-musical-2029526/) by OpenClipart-Vectors +- [TV Edition Personalization Page](https://pixabay.com/zh/vectors/head-music-silhouette-avatar-5405110/) by GDJ +- [TV Edition Themes Page](https://pixabay.com/zh/vectors/harp-mistletoe-music-line-art-5602052/) by GDJ +- [TV Edition Auto-Download Page](https://pixabay.com/zh/vectors/musical-notes-music-smile-merry-6159007/) by ArtRose +- [TV Edition Transcoding Page](https://pixabay.com/zh/illustrations/record-player-music-7000988/) by Vika_Glitter \ No newline at end of file diff --git a/i18n/en/docusaurus-plugin-content-docs/current/notes/services/audiostation.md b/i18n/en/docusaurus-plugin-content-docs/current/notes/services/audiostation.md new file mode 100644 index 0000000..ea32b2d --- /dev/null +++ b/i18n/en/docusaurus-plugin-content-docs/current/notes/services/audiostation.md @@ -0,0 +1,1184 @@ +--- +sidebar_position: 5 +--- + +# AudioStation 接口文档 + +:::note 参考文档: + +- [DSM 登录 Web API 指南](https://global.synologydownload.com/download/Document/Software/DeveloperGuide/Os/DSM/All/enu/DSM_Login_Web_API_Guide_enu.pdf) + +AudioStation 没有官方的接口文档,以下均为抓包所得。 + +::: + +## 认证 + +:::tip + +AudioStation 部分节点返回的是正常的 json 格式,部分节点返回的是转为字符串的 json 格式,需要手动判断并处理一下。 + +::: + +### Serv.php 获取访问地址(可选) + +POST: `https://global.quickconnect.cn/Serv.php` + +body(`application/json`): + +| 参数名 | 备注 | +| --- | --- | +| version | 版本,固定填 `1` | +| id | 固定填 `dsm` | +| serverID | quickconnect ID | +| get_ca_fingerprints | 固定填 `true` | +| command | 固定填 `get_server_info` | + +response: + +```json +{ + "command": "get_server_info", + "env": { + "control_host": "cnc.quickconnect.cn", + "relay_region": "cn" + }, + "errno": 0, + "server": { + "ca_fingerprints": [ + "xxx", + "xxxx", + "xxxxx" + ], + "ddns": "NULL", + "ds_state": "CONNECTED", + "external": { + "ip": "00.00.00.000", + "ipv6": "::" + }, + "fqdn": "NULL", + "gateway": "192.168.00.1", + "interface": [ + { + "ip": "192.168.00.5", + "ipv6": [ + { + "addr_type": 0, + "address": "xxxx:15b0:xxxx:4246:xxxx:d0ff:xxxx:db15", + "prefix_length": 64, + "scope": "global" + }, + { + "addr_type": 32, + "address": "fe80::xxxx:d0ff:xxxx:db15", + "prefix_length": 64, + "scope": "link" + } + ], + "mask": "255.255.255.0", + "name": "eth0" + } + ], + "ipv6_tunnel": [], + "is_bsm": false, + "pingpong_path": "", + "redirect_prefix": "", + "serverID": "0000000", + "tcp_punch_port": 0, + "udp_punch_port": 45785 + }, + "service": { + "port": 5000, + "ext_port": 0, + "pingpong": "UNKNOWN", + "pingpong_desc": [] + }, + "smartdns": { + "host": "xxxx.direct.quickconnect.cn", + "lan": [ + "192-168-00-5.xxxx.direct.quickconnect.cn" + ], + "lanv6": [ + "syn6-xxxx.xxxx.direct.quickconnect.cn", + "syn6-xxxxxx.xxxx.direct.quickconnect.cn" + ], + "hole_punch": "127-0-0-1.xxxx.direct.quickconnect.cn" + }, + "version": 1 +} +``` + +:::note + +server, service, env 字段可能不存在,不存在时表示此节点未保存当前 quickconnect ID 的信息,需要通过 `sites` 字段保存的其他节点列表重新调用此接口查询。 + +::: + +### Serv.php 开启代理(可选) + +controlHost 从上一步的 `env.control_host` 获取,上一步若未获取到 `service.relay_ip`,则表示此时未开启代理,可通过此接口开启代理。 + +POST: `https://[controlHost]/Serv.php` + +body(`application/json`): + +| 参数名 | 备注 | +| --- | --- | +| version | 版本,固定填 `1` | +| id | 固定填 `dsm` | +| serverID | quickconnect ID | +| platform | 平台,可选值:`Web`, `Android`, `iOS`, `MacOSX`, `Windows`, `Linux` | +| command | 固定填 `request_tunnel` | + +response: + +```json +{ + "command": "request_tunnel", + "env": { + "control_host": "cnc.quickconnect.cn", + "relay_region": "xxx" + }, + "errno": 0, + "server": { + "ddns": "NULL", + "ds_state": "CONNECTED", + "external": { + "ip": "00.00.00.000", + "ipv6": "::" + }, + "fqdn": "NULL", + "gateway": "192.168.00.5", + "interface": [ + { + "ip": "192.168.00.00", + "ipv6": [ + { + "addr_type": 0, + "address": "xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:db15", + "prefix_length": 64, + "scope": "global" + }, + { + "addr_type": 32, + "address": "fe80::xxxx:xxxx:xxxx:db15", + "prefix_length": 64, + "scope": "link" + } + ], + "mask": "255.255.255.0", + "name": "eth0" + } + ], + "ipv6_tunnel": [], + "is_bsm": false, + "pingpong_path": "", + "redirect_prefix": "", + "serverID": "0000", + "tcp_punch_port": 0, + "udp_punch_port": 45785 + }, + "service": { + "port": 5000, + "ext_port": 0, + "pingpong": "UNKNOWN", + "pingpong_desc": [], + "relay_ip": "xx.xx.xxx.xx", + "relay_dn": "synr-xxx.xxxx.direct.quickconnect.cn", + "relay_port": 39833, + "vpn_ip": "xxx.xxx.xxx.xxx", + "https_ip": "xxx.xxx.xxx.xxx", + "https_port": 443 + }, + "smartdns": { + "host": "xxxx.direct.quickconnect.cn", + "lan": [ + "192-168-00-00.xxxx.direct.quickconnect.cn" + ], + "lanv6": [ + "syn6-xxxx.xx.direct.quickconnect.cn", + "syn6-xxxx.xx.direct.quickconnect.cn" + ], + "hole_punch": "127-0-0-1.xxxx.direct.quickconnect.cn" + }, + "version": 1 +} +``` + +### pingpong.cgi 测试服务器连通性 + +通过以上两步可以获取到包括内网地址,外网地址以及代理地址,通过此接口是否超时判断地址是否可用,程序中按自定义的优先级使用即可。 + +GET: `[host]/webman/pingpong.cgi?quickconnect=true` + +### query.cgi 获取可用端点 + +GET: `[host]/webapi/query.cgi` + +query: + +| 参数名 | 备注 | +| --- | --- | +| version | 版本,固定填 `1` | +| api | 固定填 `SYNO.API.Info` | +| method | 固定填 `query` | +| query | 查询范围,可选值:`all` | + +```json +{ + "data": { + "SYNO.API.Auth": { + "maxVersion": 7, + "minVersion": 1, + "path": "entry.cgi" + }, + "SYNO.API.Auth.Key": { + "maxVersion": 7, + "minVersion": 7, + "path": "entry.cgi", + "requestFormat": "JSON" + }, + "SYNO.API.Auth.Key.Code": { + "maxVersion": 7, + "minVersion": 7, + "path": "entry.cgi", + "requestFormat": "JSON" + }, + "SYNO.API.Auth.RedirectURI": { + "maxVersion": 1, + "minVersion": 1, + "path": "entry.cgi", + "requestFormat": "JSON" + }, + "SYNO.API.Auth.Type": { + "maxVersion": 1, + "minVersion": 1, + "path": "entry.cgi", + "requestFormat": "JSON" + }, + "SYNO.API.Auth.UIConfig": { + "maxVersion": 1, + "minVersion": 1, + "path": "entry.cgi", + "requestFormat": "JSON" + }, + "SYNO.API.Encryption": { + "maxVersion": 1, + "minVersion": 1, + "path": "entry.cgi", + "requestFormat": "JSON" + }, + "SYNO.API.Info": { + "maxVersion": 1, + "minVersion": 1, + "path": "entry.cgi", + "requestFormat": "JSON" + }, + "SYNO.API.OTP": { + "maxVersion": 1, + "minVersion": 1, + "path": "otp.cgi" + }, + "SYNO.AudioPlayer": { + "maxVersion": 2, + "minVersion": 2, + "path": "entry.cgi", + "requestFormat": "JSON" + }, + "SYNO.AudioPlayer.Stream": { + "maxVersion": 2, + "minVersion": 2, + "path": "entry.cgi", + "requestFormat": "JSON" + }, + "SYNO.AudioStation.Album": { + "maxVersion": 3, + "minVersion": 1, + "path": "AudioStation/album.cgi" + }, + "SYNO.AudioStation.Artist": { + "maxVersion": 4, + "minVersion": 1, + "path": "AudioStation/artist.cgi" + }, + "SYNO.AudioStation.Browse.Playlist": { + "maxVersion": 1, + "minVersion": 1, + "path": "entry.cgi", + "requestFormat": "JSON" + }, + "SYNO.AudioStation.Composer": { + "maxVersion": 2, + "minVersion": 1, + "path": "AudioStation/composer.cgi" + }, + "SYNO.AudioStation.Cover": { + "maxVersion": 3, + "minVersion": 1, + "path": "AudioStation/cover.cgi" + }, + "SYNO.AudioStation.Download": { + "maxVersion": 1, + "minVersion": 1, + "path": "AudioStation/download.cgi" + }, + "SYNO.AudioStation.Folder": { + "maxVersion": 3, + "minVersion": 1, + "path": "AudioStation/folder.cgi" + }, + "SYNO.AudioStation.Genre": { + "maxVersion": 3, + "minVersion": 1, + "path": "AudioStation/genre.cgi" + }, + "SYNO.AudioStation.Info": { + "maxVersion": 6, + "minVersion": 1, + "path": "AudioStation/info.cgi" + }, + "SYNO.AudioStation.Lyrics": { + "maxVersion": 2, + "minVersion": 1, + "path": "AudioStation/lyrics.cgi" + }, + "SYNO.AudioStation.LyricsSearch": { + "maxVersion": 2, + "minVersion": 1, + "path": "AudioStation/lyrics_search.cgi" + }, + "SYNO.AudioStation.MediaServer": { + "maxVersion": 1, + "minVersion": 1, + "path": "AudioStation/media_server.cgi" + }, + "SYNO.AudioStation.Pin": { + "maxVersion": 1, + "minVersion": 1, + "path": "entry.cgi", + "requestFormat": "JSON" + }, + "SYNO.AudioStation.Playlist": { + "maxVersion": 3, + "minVersion": 1, + "path": "AudioStation/playlist.cgi" + }, + "SYNO.AudioStation.Proxy": { + "maxVersion": 2, + "minVersion": 1, + "path": "AudioStation/proxy.cgi" + }, + "SYNO.AudioStation.Radio": { + "maxVersion": 2, + "minVersion": 1, + "path": "AudioStation/radio.cgi" + }, + "SYNO.AudioStation.RemotePlayer": { + "maxVersion": 3, + "minVersion": 1, + "path": "AudioStation/remote_player.cgi" + }, + "SYNO.AudioStation.RemotePlayerStatus": { + "maxVersion": 2, + "minVersion": 1, + "path": "AudioStation/remote_player_status.cgi" + }, + "SYNO.AudioStation.Search": { + "maxVersion": 1, + "minVersion": 1, + "path": "AudioStation/search.cgi" + }, + "SYNO.AudioStation.Song": { + "maxVersion": 3, + "minVersion": 1, + "path": "AudioStation/song.cgi" + }, + "SYNO.AudioStation.Stream": { + "maxVersion": 2, + "minVersion": 1, + "path": "AudioStation/stream.cgi" + }, + "SYNO.AudioStation.Tag": { + "maxVersion": 1, + "minVersion": 1, + "path": "entry.cgi", + "requestFormat": "JSON" + }, + "SYNO.AudioStation.VoiceAssistant.Browse": { + "maxVersion": 1, + "minVersion": 1, + "path": "entry.cgi", + "requestFormat": "JSON" + }, + "SYNO.AudioStation.VoiceAssistant.Challenge": { + "maxVersion": 1, + "minVersion": 1, + "path": "entry.cgi", + "requestFormat": "JSON" + }, + "SYNO.AudioStation.VoiceAssistant.Info": { + "maxVersion": 1, + "minVersion": 1, + "path": "entry.cgi", + "requestFormat": "JSON" + }, + "SYNO.AudioStation.VoiceAssistant.Stream": { + "maxVersion": 1, + "minVersion": 1, + "path": "entry.cgi", + "requestFormat": "JSON" + }, + "SYNO.AudioStation.WebPlayer": { + "maxVersion": 1, + "minVersion": 1, + "path": "AudioStation/web_player.cgi" + }, + "SYNO.Auth.ForgotPwd": { + "maxVersion": 1, + "minVersion": 1, + "path": "entry.cgi", + "requestFormat": "JSON" + }, + "SYNO.Auth.RescueEmail": { + "maxVersion": 1, + "minVersion": 1, + "path": "entry.cgi", + "requestFormat": "JSON" + }, + }, + "success": true +} +``` + +通过此端点返回的端点列表,可获取到对应端点的请求路径,在后续请求中均会先通过固定的`端点 key` 来获取请求路径,建议第一次获取之后在程序中保存一下方便后续使用。 + +### Auth 登录 + +key:`SYNO.API.Auth` + +POST: `[host]/webapi/[path]` + +body(`application/json`): + +| 参数名 | 备注 | +| --- | --- | +| version | 版本,固定填 `6` | +| api | 固定填 `SYNO.API.Auth` | +| method | 固定填 `login` | +| session | 固定填 `audiostation` | +| device_name | 设备名 | +| account | 用户名 | +| passwd | 密码,若用户配置了二次验证,在登录重试时应填上次登录返回的 token | +| enable_device_token | 固定填 `yes` 以保证下次自动登录时无需再让用户输入验证码 | +| otp_code | OTP 验证码,可选 | +| device_id | 设备 ID,登录成功后 `did` 字段的值 | + +403 response(需要传 OTP 验证码): + +```json +{ + "error": { + "code": 403, + "errors": { + "token": "eyJ0eXAiOiJKV1Qxxx", + "types": [ + { + "type": "otp" + } + ] + } + }, + "success": false +} +``` + +response: + +```json +{ + "data": { + "did": "Bonqh4M59r3vxxx", + "is_portal_port": false, + "sid": "EGVGrBZsJ7fKJ1GpYhplKJewLxxx" + }, + "success": true +} +``` + +header: + +```json +{ + "set-cookie": [ + "id=xxxx;expires=Sat, 27-Apr-2024 03:09:10 GMT;max-age=604800;path=/;HttpOnly", + "did=xxxx;expires=Sun, 20-Apr-2025 03:09:10 GMT;max-age=31536000;path=/;HttpOnly" + ] +} +``` + +程序在登录成功后,应保存 `did`, `sid`, `id` 字段的值,在 DSM 6 中,仅首次登录后会返回 did。 + +## 请求节点 + +以下请求均需从上面的 [获取可用端点](#querycgi-获取可用端点) 取得对应的访问路径,最终组装的路径如:`[host]/webapi/[path]`,下文不再赘述,仅写出端点对应的 key,若请求中需要填写参数,则为参数名 `api` 的值。 + +### Album 获取专辑列表 + +GET query: + +| 参数名 | 备注 | +| --- | --- | +| version | 版本,固定填 `3` | +| api | `SYNO.AudioStation.Album` | +| method | `list` | +| library | 查询范围,可选值: `all`, `personal` | +| additional | `avg_rating` | +| offset | 行数偏移 | +| limit | 结果数量,可选 | +| sort_by | 排序方式,可选值:`time`, `random`, `year`, `name`, `display_artist`, `avg_rating` | +| sort_direction | 排序,可选值:`ASC`, `DESC` | +| filter | 专辑名,可选 | +| artist | 歌手名,可选 | +| genre | 类型名,可选 | + +response: + +```json +{ + "data": { + "albums": [ + { + "additional": { + "avg_rating": { + "rating": 5 + } + }, + "album_artist": "麦小兜", + "artist": "", + "display_artist": "麦小兜", + "name": "9420", + "year": 2017 + } + ], + "offset": 0, + "total": 3528 + }, + "success": true +} +``` + +### Artist 获取歌手列表 + +GET query: + +| 参数名 | 备注 | +| --- | --- | +| version | 版本,固定填 `3` | +| api | `SYNO.AudioStation.Artist` | +| method | `list` | +| library | 查询范围,可选值: `all`, `personal` | +| additional | `avg_rating` | +| genre | 类型名,可选 | +| offset | 行数偏移 | +| limit | 结果数量,可选 | +| sort_by | 排序方式,可选值:`name` | +| sort_direction | 排序,可选值:`ASC`, `DESC` | + +response: + +```json +{ + "data": { + "artists": [ + { + "additional": { + "avg_rating": { + "rating": 0 + } + }, + "name": "多田葵 (ただ あおい)" + } + ], + "offset": 100, + "total": 939 + }, + "success": true +} +``` + +:::caution + +此接口返回的歌手列表大概率是专辑艺术家列表而非艺术家列表,这会导致部分歌手无法从此接口获取。 + +::: + +### Cover 图片链接 + +GET query: + +| 参数名 | 备注 | +| --- | --- | +| version | 版本,固定填 `1` | +| api | `SYNO.AudioStation.Cover` | +| method | 可选值:`getsongcover`, `getcover` | +| library | 查询范围,可选值: `all`, `personal` | +| id | 歌曲ID,`method=getsongcover`时填写 | +| artist_name | 歌手名,`method=getcover` 且要获取歌手图片时填写 | +| album_name | 专辑名,`method=getcover` 且要获取专辑封面时填写 | +| album_artist_name | 专辑艺术家名,`method=getcover` 且要获取专辑封面时填写 | +| _sid | Session ID,登录后的 `sid` 字段 | + +### Playlist 创建歌单 + +POST query: + +| 参数名 | 备注 | +| --- | --- | +| version | 版本,固定填 `2` | +| api | `SYNO.AudioStation.Playlist` | +| method | `create` | +| library | 查询范围,可选值: `all`, `personal` | +| name | 歌单名 | + +response: + +```json +{ + "data": { + "id": "playlist_personal_normal/19" + }, + "success": true +} +``` + +### Playlist 删除歌单 + +POST query: + +| 参数名 | 备注 | +| --- | --- | +| version | 版本,固定填 `2` | +| api | `SYNO.AudioStation.Playlist` | +| method | `delete` | +| id | 歌单ID | + +### Folder 获取目录列表 + +POST data: + +| 参数名 | 备注 | +| --- | --- | +| version | 版本,固定填 `2` | +| api | `SYNO.AudioStation.Folder` | +| method | `list` | +| library | `all` | +| additional | 附加信息,可选值:`song_tag`, `song_audio`, `song_rating` | +| sort_by | `song_rating` | +| sort_direction | `ASC` | +| id | 目录ID,不传则查询根目录,示例:`dir_1609` | +| offset | 行数偏移 | +| limit | 结果数量 | + +response: + +```json +{ + "data": { + "folder_total": 14, + "id": "dir_1413", + "items": [ + { + "id": "dir_4380", + "is_personal": false, + "path": "/music/阿悄/阿悄单曲集", + "title": "阿悄单曲集", + "type": "folder" + }, + { + "id": "dir_6213", + "is_personal": false, + "path": "/music/阿悄/最坚强的...阿悄勇敢作品集", + "title": "最坚强的...阿悄勇敢作品集", + "type": "folder" + }, + { + "additional": { + "song_audio": { + "bitrate": 320000, + "channel": 2, + "codec": "mp3", + "container": "mp3", + "duration": 213, + "filesize": 8633172, + "frequency": 44100 + }, + "song_rating": { + "rating": 0 + }, + "song_tag": { + "album": "流行网络歌", + "album_artist": "", + "artist": "徐良/阿悄", + "comment": "", + "composer": "", + "disc": 0, + "genre": "Blues", + "track": 47, + "year": 2008 + } + }, + "id": "music_4607", + "path": "/music/阿悄/徐良 _ 阿悄 - 犯贱.mp3", + "title": "犯贱", + "type": "file" + } + ], + "offset": 0, + "total": 15 + }, + "success": true +} +``` + +### Genre 获取类型列表 + +POST data: + +| 参数名 | 备注 | +| --- | --- | +| version | 版本,固定填 `3` | +| api | `SYNO.AudioStation.Genre` | +| method | `list` | +| library | 查询范围,可选值: `all`, `personal` | +| offset | 行数偏移 | +| limit | 结果数量,可选 | +| sort_by | 排序方式,`name` | +| sort_direction | 排序方向,`ASC`, `DESC` | + +response: + +```json +{ + "data": { + "genres": [ + { + "additional": { + "avg_rating": { + "rating": 0 + } + }, + "name": "" + }, + { + "additional": { + "avg_rating": { + "rating": 5 + } + }, + "name": "国际流行" + }, + { + "additional": { + "avg_rating": { + "rating": 5 + } + }, + "name": "国语流行" + }, + { + "additional": { + "avg_rating": { + "rating": 0 + } + }, + "name": "" + } + ], + "offset": 0, + "total": 44 + }, + "success": true +} +``` + +:::note +AudioStation 的空字符串显示为`未知的歌曲类型`. +::: + +### Lyrics 获取歌词 + +GET query: + +| 参数名 | 备注 | +| --- | --- | +| version | 版本,固定填 `2` | +| api | `SYNO.AudioStation.Lyrics` | +| method | `getlyrics` | +| id | 歌曲ID | + +response: + +```json +{ + "data": { + "lyrics": "" + }, + "success": true +} +``` + +### LyricsSearch 搜索歌词 + +此接口依赖 AudioStation 安装的歌词插件。 + +GET query: + +| 参数名 | 备注 | +| --- | --- | +| version | 版本,固定填 `2` | +| api | `SYNO.AudioStation.LyricsSearch` | +| method | `searchlyrics` | +| title | 标题 | +| artist | 歌手 | +| limit | 结果数量 | +| additional | `full_lyrics` | + +response: + +```json +{ + "data": { + "lyrics": [ + { + "additional": { + "full_lyrics": "[ti:原来你也在这里 (《她从海上来》电视剧主题曲)]\n[ar:刘若英]\n[al:我的失败与伟大 2nd Version]\n[by:]\n[offset:0]\n[00:00.00]原来你也在这里 - 刘若英 (Rene Liu)\n[00:03.45]词:姚谦\n[00:06.90]曲:中岛美雪\n[00:10.35]编曲:屠颖\n[00:13.81]请允许我尘埃落定\n[00:16.93]用沉默埋葬了过去\n[00:20.29]满身风雨我从海上来\n[00:23.42]才隐居在这沙漠里\n[00:26.37]\n[00:27.04]该隐瞒的事总清晰\n[00:30.25]千言万语只能无语\n[00:33.61]爱是天时地利的迷信\n[00:36.69]喔 原来你也在这里\n[00:40.30]啊 那一个人\n[00:42.88]是不是只存在梦境里\n[00:46.24]\n[00:47.09]为什么我用尽全身力气\n[00:50.27]却换来半生回忆\n[00:52.57]\n[00:53.80]若不是你渴望眼睛\n[00:56.98]若不是我救赎心情\n[00:59.81]\n[01:00.33]在千山万水人海相遇\n[01:03.42]喔 原来你也在这里\n[01:06.43]\n[01:37.15]请允许我尘埃落定\n[01:40.34]用沉默埋葬了过去\n[01:43.60]满身风雨我从海上来\n[01:46.78]才隐居在这沙漠里\n[01:49.42]\n[01:50.27]该隐瞒的事总清晰\n[01:53.59]千言万语只能无语\n[01:56.98]爱是天时地利的迷信\n[02:00.14]喔 原来你也在这里\n[02:03.59]啊 那一个人\n[02:06.18]是不是只存在梦境里\n[02:09.46]\n[02:10.17]为什么我用尽全身力气\n[02:13.56]却换来半生回忆\n[02:16.28]\n[02:17.05]若不是你渴望眼睛\n[02:20.36]若不是我救赎心情\n[02:23.63]在千山万水人海相遇\n[02:26.82]喔 原来你也在这里\n[02:30.44]啊 那一个人\n[02:32.86]是不是只存在梦境里\n[02:36.26]\n[02:37.01]为什么我用尽全身力气\n[02:40.30]却换来半生回忆\n[02:42.96]\n[02:43.70]若不是你渴望眼睛\n[02:47.00]若不是我救赎心情\n[02:50.24]在千山万水人海相遇\n[02:53.47]喔 原来你也在这里\n[02:56.44]\n[02:56.96]该隐瞒的事总清晰\n[03:00.33]千言万语只能无语\n[03:03.72]爱是天时地利的迷信\n[03:07.01]喔 原来你也在这里" + }, + "artist": "刘若英", + "id": "Lrc@136473", + "partial_lyrics": "136473; Album: 我的失败与伟大", + "plugin": "pluginName", + "title": "原来你也在这里" + } + ] + }, + "success": true +} +``` + +### Playlist 获取歌单列表 + +GET query: + +| 参数名 | 备注 | +| --- | --- | +| version | 版本,固定填 `2` | +| api | `SYNO.AudioStation.Playlist` | +| method | `list` | +| library | 查询范围,可选值: `all`, `personal` | +| offset | 行数偏移 | +| limit | 结果数量,可选 | + +response: + +```json +{ + "data": { + "offset": 0, + "playlists": [ + { + "id": "playlist_personal_normal/__SYNO_AUDIO_SHARED_SONGS__", + "library": "personal", + "name": "__SYNO_AUDIO_SHARED_SONGS__", + "sharing_status": "none", + "type": "normal" + }, + { + "id": "playlist_personal_normal/19", + "library": "personal", + "name": "6655", + "path": "/homes/xxx/music/playlists", + "sharing_status": "none", + "type": "normal" + }, + { + "id": "playlist_personal_smart/ぐされ", + "library": "personal", + "name": "ぐされ", + "sharing_status": "valid", + "type": "smart" + } + ], + "total": 6 + }, + "success": true +} +``` + +### Song 歌曲评分 + +POST query: + +| 参数名 | 备注 | +| --- | --- | +| version | 版本,固定填 `3` | +| api | `SYNO.AudioStation.Song` | +| method | `setrating` | +| id | 歌曲ID | +| rating | 分数,0-5之间 | + +### Info 获取服务器信息 + +GET query: + +| 参数名 | 备注 | +| --- | --- | +| version | 版本,固定填 `1` | +| api | `SYNO.AudioStation.Info` | +| method | `getinfo` | + +response: + +```json +{ + "data": { + "path": "/webman/3rdparty/AudioStation", + "version": { + "build": "5508", + "major": "7", + "minor": "1" + } + }, + "success": true +} +``` + +### Search 搜索歌曲/专辑/歌手 + +GET query: + +| 参数名 | 备注 | +| --- | --- | +| version | 版本,固定填 `1` | +| api |`SYNO.AudioStation.Search` | +| method | `list` | +| library | 查询范围,可选值: `all`, `personal` | +| offset | 行数偏移 | +| limit | 结果数量,可选 | +| keyword | 关键词 | +| sort_by | 排序方式,可选值:`title` | +| sort_direction | 排序,可选值:`ASC`, `DESC` | +| additional | 附加信息,可选值:`song_tag`, `song_audio`, `song_rating` | + +response: + +```json +{ + "data": { + "albumTotal": 3, + "albums": [ + { + "album_artist": "王菲", + "artist": "", + "display_artist": "王菲", + "name": "王菲", + "year": 0 + }, + { + "album_artist": "王菲", + "artist": "", + "display_artist": "王菲", + "name": "王菲 2001同名专辑", + "year": 0 + }, + { + "album_artist": "王菲", + "artist": "", + "display_artist": "王菲", + "name": "王菲珍藏集", + "year": 2004 + } + ], + "artistTotal": 2, + "artists": [ + { + "name": "王菲" + }, + { + "name": "王菲/邓丽君" + }, + ], + "songTotal": 0, + "songs": [] + }, + "success": true +} +``` + +### Song 获取歌曲列表 + +POST data: + +| 参数名 | 备注 | +| --- | --- | +| version | 版本,固定填 `3` | +| api |`SYNO.AudioStation.Song` | +| method | `list` | +| library | 查询范围,可选值: `all`, `personal` | +| offset | 行数偏移 | +| limit | 结果数量,可选 | +| rating_filter | 评分,可选 | +| album_artist | 专辑艺术家,可选 | +| album | 专辑名,可选 | +| artist | 歌手名,可选 | +| genre | 类型名,可选 | +| sort_by | 排序方式,可选值:`title`, `name`, `artist`, `random` | +| sort_direction | 排序,可选值:`ASC`, `DESC` | +| additional | 附加信息列表,可选值:`song_tag`, `song_audio`, `song_rating` | + +:::note + +目前没有找到如何按添加时间排序的方式,似乎只能按专辑的添加时间排序后,一个个从专辑中获取? + +::: + +response: + +```json +{ + "data": { + "offset": 0, + "songs": [ + { + "additional": { + "song_audio": { + "bitrate": 982000, + "channel": 2, + "codec": "flac", + "container": "flac", + "duration": 238, + "filesize": 29618503, + "frequency": 44100 + }, + "song_rating": { + "rating": 0 + }, + "song_tag": { + "album": "如果没有你", + "album_artist": "莫文蔚", + "artist": "莫文蔚", + "comment": "", + "composer": "", + "disc": 0, + "genre": "", + "rg_album_gain": "-8.19", + "rg_album_peak": "1", + "rg_track_gain": "-8.92", + "rg_track_peak": "1", + "track": 4, + "year": 2006 + } + }, + "id": "music_36034", + "path": "/music/莫文蔚/如果没有你/莫文蔚-24Hrs.flac", + "title": "24Hrs", + "type": "file" + }, + ], + "total": 59 + }, + "success": true +} +``` + +### Playlist 获取歌单中的歌曲 + +GET query: + +| 参数名 | 备注 | +| --- | --- | +| version | 版本,固定填 `2` | +| api |`SYNO.AudioStation.Playlist` | +| method | `getinfo` | +| library | 查询范围,可选值: `all`, `personal` | +| offset | 行数偏移 | +| limit | 结果数量,可选 | +| id | 歌单ID | +| sort_direction | 排序,可选值:`ASC` | +| additional | 附加信息列表,可选值:`songs_song_tag`, `songs_song_audio`, `songs_song_rating` | + +:::note + +从歌单中移除歌曲时,传入的 ID 列表是此接口返回的歌曲顺序索引,从 0 开始。 + +::: + +### 收藏歌曲 + +AudioStation 没有此接口,建议使用评分为 5 星的歌曲来替代,通过 `rating_filter` 可以较为方便地获取到歌曲列表。 + +### Stream 歌曲播放链接 + +GET query: + +| 参数名 | 备注 | +| --- | --- | +| version | 版本,固定填 `2` | +| api | `SYNO.AudioStation.Stream` | +| method | 可选值:`stream`, `transcode` | +| id | 歌曲ID | +| format | 歌曲格式,`method=transcode` 时填写,可选值:`mp3` | +| _sid | Session ID,登录后的 `sid` 字段 | + +当 `method=transcode` 时,请求路径后需添加 `0.mp3`,即 `[host]/webapi/[path]/0.mp3?xxx=xx`。 + +:::note + +若歌曲是整轨文件的某个音轨,歌曲 ID 的格式为 `music_v_1111`。对于这种文件我自己尝试是无法播放的,建议识别到歌曲 ID 包含 `_v_` 的歌曲时,强制使用转码的播放链接。 + +::: + +### Playlist 从歌单中移除歌曲 + +POST query: + +| 参数名 | 备注 | +| --- | --- | +| version | 版本,固定填 `2` | +| api |`SYNO.AudioStation.Playlist` | +| method | `updatesongs` | +| offset | 待移除的歌曲的起始行数 | +| limit | 需要移除的歌曲数量,注:从起始行数开始的所有歌曲都会被移除 | +| songs | 待添加的歌曲 ID 列表,用于回溯上面两个参数中间误删的歌曲 | +| id | 歌单ID | + +:::caution + +这个接口的删除逻辑较难理解,建议自己在网页端抓包尝试一下。 + +::: + +### Playlist 添加歌曲到歌单 + +POST query: + +| 参数名 | 备注 | +| --- | --- | +| version | 版本,固定填 `2` | +| api |`SYNO.AudioStation.Playlist` | +| method | `updatesongs` | +| offset | `-1` | +| limit | `0` | +| songs | 待添加的歌曲 ID 列表 | +| id | 歌单ID | + +### Playlist 重命名歌单 + +POST query: + +| 参数名 | 备注 | +| --- | --- | +| version | 版本,固定填 `2` | +| api |`SYNO.AudioStation.Playlist` | +| method | `rename` | +| new_name | 歌单名 | +| id | 歌单ID | \ No newline at end of file diff --git a/i18n/en/docusaurus-plugin-content-docs/current/notes/services/emby.md b/i18n/en/docusaurus-plugin-content-docs/current/notes/services/emby.md new file mode 100644 index 0000000..2ce74e3 --- /dev/null +++ b/i18n/en/docusaurus-plugin-content-docs/current/notes/services/emby.md @@ -0,0 +1,1975 @@ +--- +sidebar_position: 4 +--- + +# Emby 接口文档 + +:::note 参考文档: + +- [Emby API](https://github.com/MediaBrowser/Emby/wiki) + +::: + +## 认证 + +### AuthenticateByName 登录 + +参见 Jellyfin 的 [AuthenticateByName](./jellyfin#authenticatebyname-登录) 接口。 + +### Ping 测试服务器连通性 + +GET: `[host]/System/Ping` + +## 请求节点 + +### Items 获取专辑列表 + +GET: `[host]/Users/[UserId]/Items` + +query: + +| 参数名 | 备注 | +| --- | --- | +| SortBy | 排序方式列表,可选值: `Random`, `DateCreated`, `PremiereDate`, `PlayCount`, `DatePlayed`, `SortName`, `CommunityRating` | +| SortOrder | 排序,可选值: `Ascending`, `Descending` | +| IncludeItemTypes | 包含的项目类型,可选值:`MusicAlbum` | +| Recursive | 是否递归查询 | +| Fields | 包含的字段列表,可选值:`SortName`, `BasicSyncInfo`, `ChildCount`, `DateCreated`, `ProductionYear` | +| ImageTypeLimit | 返回的每个图片类型图片数量 | +| EnableImageTypes | 图片类型列表,可选值:`Primary`, `Backdrop`, `Thumb` | +| StartIndex | 起始行数 | +| Limit | 最大数量,可选 | +| ParentId | 父视图ID,可选 | +| artistIds | 歌手 id 列表,可选 | +| GenreIds | 类型ID列表,可选 | +| SearchTerm | 搜索词,可选 | + +response: + +```json +{ + "Items": [ + { + "Name": "爆笑三国演义", + "ServerId": "54068b26c84949dc809677381d1267a9", + "Id": "79106", + "DateCreated": "2024-04-12T01:50:05.0000000Z", + "SupportsSync": true, + "SortName": "爆笑三国演义", + "RunTimeTicks": 3305120000, + "ProductionYear": 2023, + "IsFolder": true, + "Type": "MusicAlbum", + "UserData": { + "PlaybackPositionTicks": 0, + "PlayCount": 0, + "IsFavorite": false, + "Played": false + }, + "Artists": [ + "佳音少儿、 金德哥哥" + ], + "ArtistItems": [ + { + "Name": "佳音少儿、 金德哥哥", + "Id": "79105" + } + ], + "Composers": [], + "AlbumArtist": "佳音少儿、 金德哥哥", + "AlbumArtists": [ + { + "Name": "佳音少儿、 金德哥哥", + "Id": "79105" + } + ], + "ImageTags": {}, + "BackdropImageTags": [] + }, + { + "Name": "森之恋歌", + "ServerId": "54068b26c84949dc809677381d1267a9", + "Id": "79103", + "DateCreated": "2024-04-10T04:24:53.0000000Z", + "SupportsSync": true, + "SortName": "森之恋歌", + "RunTimeTicks": 2332317080, + "ProductionYear": 2022, + "IsFolder": true, + "Type": "MusicAlbum", + "UserData": { + "PlaybackPositionTicks": 0, + "PlayCount": 0, + "IsFavorite": false, + "Played": false + }, + "Artists": [ + "单依纯" + ], + "ArtistItems": [ + { + "Name": "单依纯", + "Id": "7113" + } + ], + "Composers": [], + "AlbumArtist": "单依纯", + "AlbumArtists": [ + { + "Name": "单依纯", + "Id": "7113" + } + ], + "ImageTags": { + "Primary": "08f7baa96fe117a2d22ced7f34f292f1" + }, + "BackdropImageTags": [] + } + ], + "TotalRecordCount": 3654 +} +``` + +### Items/[id] 获取专辑信息 + +GET: `[host]/Users/[UserId]/Items/[id]` + +response: + +```json +{ + "Name": "森之恋歌", + "ServerId": "54068b26c84949dc809677381d1267a9", + "Id": "79103", + "Etag": "3fe9c1df1c602725cefae4ab30e445b6", + "DateCreated": "2024-04-10T04:24:53.0000000Z", + "CanDelete": true, + "CanDownload": false, + "PresentationUniqueKey": "b1e9c0c3922344d4b852b46bd0130b23", + "SupportsSync": true, + "SortName": "森之恋歌", + "ForcedSortName": "森之恋歌", + "ExternalUrls": [], + "Taglines": [], + "Genres": [], + "RunTimeTicks": 2332317080, + "PlayAccess": "Full", + "ProductionYear": 2022, + "RemoteTrailers": [], + "ProviderIds": {}, + "IsFolder": true, + "Type": "MusicAlbum", + "Studios": [], + "GenreItems": [], + "TagItems": [], + "UserData": { + "PlaybackPositionTicks": 0, + "PlayCount": 0, + "IsFavorite": false, + "Played": false + }, + "DisplayPreferencesId": "f13d7f51d4f1f8b6fcd620855eb88c1e", + "PrimaryImageAspectRatio": 1, + "Artists": [ + "单依纯" + ], + "ArtistItems": [ + { + "Name": "单依纯", + "Id": "7113" + } + ], + "Composers": [], + "AlbumArtist": "单依纯", + "AlbumArtists": [ + { + "Name": "单依纯", + "Id": "7113" + } + ], + "ImageTags": { + "Primary": "08f7baa96fe117a2d22ced7f34f292f1" + }, + "BackdropImageTags": [], + "LockedFields": [], + "LockData": false +} +``` + +### AlbumArtists 获取专辑艺术家列表 + +GET: `[host]/Artists/AlbumArtists` + +其他格式与歌手列表一致。 + +### Artists 获取歌手列表 + +参见 Jellyfin 的 [Artists](./jellyfin#artists-获取歌手列表) 接口。 + +补充 query: + +| 参数名 | 备注 | +| --- | --- | +| ParentId | 父视图ID,可选 | +| GenreIds | 类型ID列表,可选 | + +### Items/[id] 获取歌手信息 + +参见 Jellyfin 的 [Items/id](./jellyfin#itemsid-获取歌手信息) 接口。 + +### Similar 获取相似歌手 + +参见 Jellyfin 的 [Similar](./jellyfin#similar-获取相似歌手) 接口。 + +### Images 图片链接 + +GET: `[host]/Items/[id]/Primary?fillHeight=600&fillWidth=600` + +:::note + +请求头需要添加授权信息。 + +::: + +### Playlists 创建歌单 + +POST: `[host]/Playlists` + +body(`application/json`): + +| 参数名 | 备注 | +| --- | --- | +| Name | 歌单名 | +| Ids | 包含的歌曲ID列表,不建议传空列表,因为 Emby 的电影和音乐可以混在一个歌单里,初始化时有音乐方便后续只查询包含音乐的歌单。 | +| MediaType | 媒体类型,可选值:`Audio` | + +response: + +```json +{ + "Id": "bea612652ad4b3578c76fa87a13e7fa7" +} +``` + +### Items/[id] 删除歌单 + +DELETE: `[host]/Items/[id]` + +response code: 204 + +### Items 从服务器删除歌曲 + +DELETE: `[host]/Items` + +query: + +| 参数名 | 备注 | +| --- | --- | +| Ids | 包含的歌曲ID列表 | + +:::caution + +危险操作,此接口需要管理员权限。 + +::: + +### Items 获取目录列表 + +GET: `[host]/Users/[UserId]/Items` + +query: + +| 参数名 | 备注 | +| --- | --- | +| ParentId | 目录ID,传音乐库ID时查询根目录 | +| EnableImageTypes | `Primary,Backdrop,Thumb` | +| ImageTypeLimit | `1` | +| SortBy | 排序方式,可选值:`IsFolder`, `Filename` | +| StartIndex | 行数偏移 | +| Limit | 结果数量 | +| Fields | 返回的列,可选值:`BasicSyncInfo,CanDelete,PrimaryImageAspectRatio,ProductionYear,Status,EndDate` | + +response: + +```json +{ + "Items": [ + { + "Name": "阿悄单曲集", + "ServerId": "5472030a784c45f4a90845bd9886bc98", + "Id": "27163", + "CanDelete": true, + "SupportsSync": true, + "IsFolder": true, + "Type": "Folder", + "UserData": { + "PlaybackPositionTicks": 0, + "PlayCount": 0, + "IsFavorite": false, + "Played": false + }, + "ImageTags": {}, + "BackdropImageTags": [], + "PrimaryImageItemId": "27182", + "PrimaryImageTag": "1b50e7c8bca70c47ddb8ba969c9d34ad" + }, + { + "Name": "最坚强的...阿悄勇敢作品集", + "ServerId": "5472030a784c45f4a90845bd9886bc98", + "Id": "27173", + "CanDelete": true, + "SupportsSync": true, + "IsFolder": true, + "Type": "Folder", + "UserData": { + "PlaybackPositionTicks": 0, + "PlayCount": 0, + "IsFavorite": false, + "Played": false + }, + "ImageTags": {}, + "BackdropImageTags": [], + "PrimaryImageItemId": "27193", + "PrimaryImageTag": "60c07108c93ed897bd7b5ad4624e1090" + }, + { + "Name": "犯贱", + "ServerId": "5472030a784c45f4a90845bd9886bc98", + "Id": "27159", + "CanDelete": true, + "SupportsSync": true, + "RunTimeTicks": 2138906120, + "ProductionYear": 2008, + "IndexNumber": 47, + "IsFolder": false, + "Type": "Audio", + "UserData": { + "PlaybackPositionTicks": 0, + "PlayCount": 0, + "IsFavorite": false, + "Played": false + }, + "PrimaryImageAspectRatio": 1, + "Artists": [ + "徐良", + "阿悄" + ], + "ArtistItems": [ + { + "Name": "徐良", + "Id": "54622" + }, + { + "Name": "阿悄", + "Id": "55566" + } + ], + "Composers": [], + "Album": "流行网络歌", + "AlbumArtists": [], + "ImageTags": { + "Primary": "f50019d3a0df167ac2cd3797aa3fe0b9" + }, + "BackdropImageTags": [], + "MediaType": "Audio" + } + ], + "TotalRecordCount": 15 +} +``` + +### Genres 获取全部类型 + +GET: `[host]/Genres` + +query: + +| 参数名 | 备注 | +| --- | --- | +| ParentId | 20111 | +| Recursive | 是否递归查询 | +| IncludeItemTypes | 固定值,`MusicAlbum` | +| userId | 用户ID | +| StartIndex | 起始行数 | +| Limit | 最大行数 | +| SortBy | 固定值,`SortName` | +| SortOrder | 固定值,`Ascending` | + +response: + +```json +{ + "Items": [ + { + "Name": "播客", + "ServerId": "5472030a784c45f4a90845bd9886bc98", + "Id": "59145", + "CanDelete": false, + "SupportsSync": true, + "Type": "MusicGenre", + "UserData": { + "PlaybackPositionTicks": 0, + "PlayCount": 0, + "IsFavorite": false, + "Played": false + }, + "PrimaryImageAspectRatio": 1, + "ImageTags": { + "Primary": "c7a60444a93cb13209189ed883f8a638" + }, + "BackdropImageTags": [] + }, + { + "Name": "古风", + "ServerId": "5472030a784c45f4a90845bd9886bc98", + "Id": "61242", + "CanDelete": false, + "SupportsSync": true, + "Type": "MusicGenre", + "UserData": { + "PlaybackPositionTicks": 0, + "PlayCount": 0, + "IsFavorite": false, + "Played": false + }, + "PrimaryImageAspectRatio": 1, + "ImageTags": { + "Primary": "fb59591640202c85ef243b7e1df8789d" + }, + "BackdropImageTags": [] + }, + ], + "TotalRecordCount": 38 +} +``` + +### Stream.js 获取歌词 + +GET: `[host]/Items/[id]/[MediaSourceId]/Subtitles/[SubtitleIndex]/Stream.js` + +> SubtitleIndex 从歌曲信息中获取,取 `MediaStreams[Type=Subtitle&Codec=lrc].Index` 的值 + +query: + +| 参数名 | 备注 | +| --- | --- | +| MediaBrowser Client | 客户端名称 | +| Device | 设备名 | +| DeviceId | 设备Id | +| Version | 客户端版本号 | +| Token | 访问令牌,登录后从响应中的 `AccessToken` 字段获取 | + +response: + +```json +{ + "TrackEvents": [ + { + "Text": "艾里甫与赛乃姆 - 刀郎", + "StartPositionTicks": 0, + "EndPositionTicks": 90000000 + }, + { + "Text": "词:刀郎", + "StartPositionTicks": 90000000, + "EndPositionTicks": 180000000 + }, + { + "Text": "曲:刀郎", + "StartPositionTicks": 180000000, + "EndPositionTicks": 270100000 + }, + { + "Text": "从小和你青梅竹马相约在天山下", + "StartPositionTicks": 270100000, + "EndPositionTicks": 339300000 + }, + { + "Text": "", + "StartPositionTicks": 339300000, + "EndPositionTicks": 371200000 + }, + { + "Text": "我们本来是天底下最幸福的人啊", + "StartPositionTicks": 371200000, + "EndPositionTicks": 457400000 + }, + { + "Text": "", + "StartPositionTicks": 457400000, + "EndPositionTicks": 470100000 + }, + { + "Text": "赛乃姆你是花丛中最美的石榴花", + "StartPositionTicks": 470100000, + "EndPositionTicks": 551700000 + }, + { + "Text": "", + "StartPositionTicks": 551700000, + "EndPositionTicks": 570800000 + }, + { + "Text": "艾里甫我却是巴格达上孤独的阿卡", + "StartPositionTicks": 570800000, + "EndPositionTicks": 643300000 + }, + { + "Text": "", + "StartPositionTicks": 643300000, + "EndPositionTicks": 696000000 + }, + { + "Text": "夜莺歌声在每个夜晚都会陪伴她", + "StartPositionTicks": 696000000, + "EndPositionTicks": 765400000 + }, + { + "Text": "", + "StartPositionTicks": 765400000, + "EndPositionTicks": 795500000 + }, + { + "Text": "我的琴声却飘荡在遥远的巴格达", + "StartPositionTicks": 795500000, + "EndPositionTicks": 877900000 + }, + { + "Text": "", + "StartPositionTicks": 877900000, + "EndPositionTicks": 896800000 + }, + { + "Text": "为了爱情我被放逐在天涯", + "StartPositionTicks": 896800000, + "EndPositionTicks": 976600000 + }, + { + "Text": "", + "StartPositionTicks": 976600000, + "EndPositionTicks": 996000000 + }, + { + "Text": "莫非今生和你厮守变成了神话", + "StartPositionTicks": 996000000, + "EndPositionTicks": 1067300000 + }, + { + "Text": "", + "StartPositionTicks": 1067300000, + "EndPositionTicks": 1095900000 + }, + { + "Text": "我寻遍天山南北我要找到你赛乃姆", + "StartPositionTicks": 1095900000, + "EndPositionTicks": 1181700000 + }, + { + "Text": "", + "StartPositionTicks": 1181700000, + "EndPositionTicks": 1195800000 + }, + { + "Text": "不管是跋山涉水历尽千辛万苦", + "StartPositionTicks": 1195800000, + "EndPositionTicks": 1281000000 + }, + { + "Text": "", + "StartPositionTicks": 1281000000, + "EndPositionTicks": 1295300000 + }, + { + "Text": "花园里种不出天山上的雪莲花", + "StartPositionTicks": 1295300000, + "EndPositionTicks": 1376800000 + }, + { + "Text": "", + "StartPositionTicks": 1376800000, + "EndPositionTicks": 1395600000 + }, + { + "Text": "不历经磨难我找不到今生的幸福", + "StartPositionTicks": 1395600000, + "EndPositionTicks": 1481900000 + }, + { + "Text": "", + "StartPositionTicks": 1481900000, + "EndPositionTicks": 1758000000 + }, + { + "Text": "夜莺歌声在每个夜晚都会陪伴她", + "StartPositionTicks": 1758000000, + "EndPositionTicks": 1826600000 + }, + { + "Text": "", + "StartPositionTicks": 1826600000, + "EndPositionTicks": 1857600000 + }, + { + "Text": "我的琴声却飘荡在遥远的巴格达", + "StartPositionTicks": 1857600000, + "EndPositionTicks": 1939100000 + }, + { + "Text": "", + "StartPositionTicks": 1939100000, + "EndPositionTicks": 1958600000 + }, + { + "Text": "为了爱情我被放逐在天涯", + "StartPositionTicks": 1958600000, + "EndPositionTicks": 2039600000 + }, + { + "Text": "", + "StartPositionTicks": 2039600000, + "EndPositionTicks": 2059000000 + }, + { + "Text": "莫非今生和你厮守变成了神话", + "StartPositionTicks": 2059000000, + "EndPositionTicks": 2131000000 + }, + { + "Text": "", + "StartPositionTicks": 2131000000, + "EndPositionTicks": 2158800000 + }, + { + "Text": "我寻遍天山南北我要找到你赛乃姆", + "StartPositionTicks": 2158800000, + "EndPositionTicks": 2242800000 + }, + { + "Text": "", + "StartPositionTicks": 2242800000, + "EndPositionTicks": 2258300000 + }, + { + "Text": "不管是跋山涉水历尽千辛万苦", + "StartPositionTicks": 2258300000, + "EndPositionTicks": 2343300000 + }, + { + "Text": "", + "StartPositionTicks": 2343300000, + "EndPositionTicks": 2358900000 + }, + { + "Text": "花园里种不出天山上的雪莲花", + "StartPositionTicks": 2358900000, + "EndPositionTicks": 2439100000 + }, + { + "Text": "", + "StartPositionTicks": 2439100000, + "EndPositionTicks": 2458700000 + }, + { + "Text": "不历经磨难我找不到今生的幸福", + "StartPositionTicks": 2458700000, + "EndPositionTicks": 2543500000 + }, + { + "Text": "", + "StartPositionTicks": 2543500000, + "EndPositionTicks": 2572400000 + }, + { + "Text": "我寻遍天山南北我要找到你赛乃姆", + "StartPositionTicks": 2572400000, + "EndPositionTicks": 2655000000 + }, + { + "Text": "", + "StartPositionTicks": 2655000000, + "EndPositionTicks": 2669100000 + }, + { + "Text": "不管是跋山涉水历尽千辛万苦", + "StartPositionTicks": 2669100000, + "EndPositionTicks": 2756300000 + }, + { + "Text": "", + "StartPositionTicks": 2756300000, + "EndPositionTicks": 2771600000 + }, + { + "Text": "花园里种不出天山上的雪莲花", + "StartPositionTicks": 2771600000, + "EndPositionTicks": 2851700000 + }, + { + "Text": "", + "StartPositionTicks": 2851700000, + "EndPositionTicks": 2869600000 + }, + { + "Text": "不历经磨难我找不到今生的幸福", + "StartPositionTicks": 2869600000 + } + ] +} +``` + +### Views 获取视图列表 + +GET: `[host]/Users/[UserId]/Views` + +response: + +```json +{ + "Items": [ + { + "Name": "电影", + "ServerId": "54068b26c84949dc809677381d1267a9", + "Id": "11013", + "Guid": "3227ce1e069754c594af25ea66d69fc7", + "Etag": "f0a63776848cf0513618763c2e49df1e", + "DateCreated": "2023-12-07T05:32:20.0000000Z", + "CanDelete": false, + "CanDownload": false, + "PresentationUniqueKey": "3227ce1e069754c594af25ea66d69fc7", + "SortName": "电影", + "ForcedSortName": "电影", + "ExternalUrls": [], + "Path": "/var/packages/EmbyServer/var/root/default/电影", + "Taglines": [], + "FileName": "电影", + "PlayAccess": "Full", + "RemoteTrailers": [], + "ProviderIds": {}, + "IsFolder": true, + "ParentId": "1", + "Type": "CollectionFolder", + "UserData": { + "PlaybackPositionTicks": 0, + "PlayCount": 0, + "IsFavorite": false, + "Played": false + }, + "ChildCount": 3, + "DisplayPreferencesId": "3227ce1e069754c594af25ea66d69fc7", + "PrimaryImageAspectRatio": 1.7777777777777777, + "CollectionType": "movies", + "ImageTags": { + "Primary": "405c8c5a67bf8e5ded4bd593b5af9926" + }, + "BackdropImageTags": [], + "LockedFields": [], + "LockData": false + }, + { + "Name": "电视剧", + "ServerId": "54068b26c84949dc809677381d1267a9", + "Id": "12075", + "Guid": "fc8fa12ee6bc083abb484b1b0bbc420f", + "Etag": "57b2fc16eb7cf0f78b1bf89743af526c", + "DateCreated": "2023-12-07T05:35:46.0000000Z", + "CanDelete": false, + "CanDownload": false, + "PresentationUniqueKey": "fc8fa12ee6bc083abb484b1b0bbc420f", + "SortName": "电视剧", + "ForcedSortName": "电视剧", + "ExternalUrls": [], + "Path": "/var/packages/EmbyServer/var/root/default/电视剧", + "Taglines": [], + "FileName": "电视剧", + "PlayAccess": "Full", + "RemoteTrailers": [], + "ProviderIds": {}, + "IsFolder": true, + "ParentId": "1", + "Type": "CollectionFolder", + "UserData": { + "PlaybackPositionTicks": 0, + "PlayCount": 0, + "IsFavorite": false, + "Played": false + }, + "ChildCount": 4, + "DisplayPreferencesId": "fc8fa12ee6bc083abb484b1b0bbc420f", + "PrimaryImageAspectRatio": 0.6666666666666666, + "CollectionType": "tvshows", + "ImageTags": { + "Primary": "71dd3f151805e02efca3fa7331ea0ffc" + }, + "BackdropImageTags": [ + "b5b4ab3a04d43f9dad650208c956ee56" + ], + "LockedFields": [], + "LockData": false + }, + { + "Name": "综艺", + "ServerId": "54068b26c84949dc809677381d1267a9", + "Id": "14508", + "Guid": "22376d03fca73f102a0de4d40ed3cd41", + "Etag": "f0a63776848cf0513618763c2e49df1e", + "DateCreated": "2023-12-07T05:38:21.0000000Z", + "CanDelete": false, + "CanDownload": false, + "PresentationUniqueKey": "22376d03fca73f102a0de4d40ed3cd41", + "SortName": "综艺", + "ForcedSortName": "综艺", + "ExternalUrls": [], + "Path": "/var/packages/EmbyServer/var/root/default/综艺", + "Taglines": [], + "FileName": "综艺", + "PlayAccess": "Full", + "RemoteTrailers": [], + "ProviderIds": {}, + "IsFolder": true, + "ParentId": "1", + "Type": "CollectionFolder", + "UserData": { + "PlaybackPositionTicks": 0, + "PlayCount": 0, + "IsFavorite": false, + "Played": false + }, + "ChildCount": 4, + "DisplayPreferencesId": "22376d03fca73f102a0de4d40ed3cd41", + "PrimaryImageAspectRatio": 1.7777777777777777, + "CollectionType": "tvshows", + "ImageTags": { + "Primary": "faf947548d07f9e99462e33fbf38a593" + }, + "BackdropImageTags": [], + "LockedFields": [], + "LockData": false + }, + { + "Name": "音乐", + "ServerId": "54068b26c84949dc809677381d1267a9", + "Id": "4", + "Guid": "28e9960207c978c0d9aaefc8ae2d3a79", + "Etag": "1805ebfb97d3af5c940a5d654007f81e", + "DateCreated": "2023-11-26T06:22:14.0000000Z", + "CanDelete": false, + "CanDownload": false, + "PresentationUniqueKey": "28e9960207c978c0d9aaefc8ae2d3a79", + "SortName": "音乐", + "ForcedSortName": "音乐", + "ExternalUrls": [], + "Path": "/var/packages/EmbyServer/var/root/default/音乐", + "Taglines": [], + "FileName": "音乐", + "PlayAccess": "Full", + "RemoteTrailers": [], + "ProviderIds": {}, + "IsFolder": true, + "ParentId": "1", + "Type": "CollectionFolder", + "UserData": { + "PlaybackPositionTicks": 0, + "PlayCount": 0, + "IsFavorite": false, + "Played": false + }, + "ChildCount": 6, + "DisplayPreferencesId": "28e9960207c978c0d9aaefc8ae2d3a79", + "PrimaryImageAspectRatio": 1.7777777777777777, + "CollectionType": "music", + "ImageTags": { + "Primary": "f7c61a28148d5a2fad6ca0bda48ebf49" + }, + "BackdropImageTags": [], + "LockedFields": [], + "LockData": false + }, + { + "Name": "合集", + "ServerId": "54068b26c84949dc809677381d1267a9", + "Id": "30885", + "Guid": "e7bfe656953e3fad0bdf034be7969a97", + "Etag": "4cc2e1798871df8cf8649fb49a223731", + "DateCreated": "2023-12-07T20:39:49.0000000Z", + "CanDelete": false, + "CanDownload": false, + "PresentationUniqueKey": "e7bfe656953e3fad0bdf034be7969a97", + "SortName": "合集", + "ForcedSortName": "合集", + "ExternalUrls": [], + "Path": "/var/packages/EmbyServer/var/root/default/合集", + "Taglines": [], + "FileName": "合集", + "PlayAccess": "Full", + "RemoteTrailers": [], + "ProviderIds": {}, + "IsFolder": true, + "ParentId": "1", + "Type": "CollectionFolder", + "UserData": { + "PlaybackPositionTicks": 0, + "PlayCount": 0, + "IsFavorite": false, + "Played": false + }, + "ChildCount": 3, + "DisplayPreferencesId": "e7bfe656953e3fad0bdf034be7969a97", + "PrimaryImageAspectRatio": 1.7777777777777777, + "CollectionType": "boxsets", + "ImageTags": { + "Primary": "30a549922a74abc9f506e7b363ccfc65" + }, + "BackdropImageTags": [], + "LockedFields": [], + "LockData": false + }, + { + "Name": "短视频", + "ServerId": "54068b26c84949dc809677381d1267a9", + "Id": "31373", + "Guid": "9548a2410227b737d377fefbd086a046", + "Etag": "b262db8903af6dc3c585931b679cf387", + "DateCreated": "2023-12-14T03:01:56.0000000Z", + "CanDelete": false, + "CanDownload": false, + "PresentationUniqueKey": "9548a2410227b737d377fefbd086a046", + "SortName": "短视频", + "ForcedSortName": "短视频", + "ExternalUrls": [], + "Path": "/var/packages/EmbyServer/var/root/default/混合内容", + "Taglines": [], + "FileName": "混合内容", + "PlayAccess": "Full", + "RemoteTrailers": [], + "ProviderIds": {}, + "IsFolder": true, + "ParentId": "1", + "Type": "CollectionFolder", + "UserData": { + "PlaybackPositionTicks": 0, + "PlayCount": 0, + "IsFavorite": false, + "Played": false + }, + "ChildCount": 9, + "DisplayPreferencesId": "9548a2410227b737d377fefbd086a046", + "PrimaryImageAspectRatio": 1.7777777777777777, + "ImageTags": { + "Primary": "604035e56178ae7a63e5039e405b7c69" + }, + "BackdropImageTags": [], + "LockedFields": [], + "LockData": false + }, + { + "Name": "纪录片", + "ServerId": "54068b26c84949dc809677381d1267a9", + "Id": "64855", + "Guid": "1c474c9c4d728d1a2f621adad0f221ae", + "Etag": "57b2fc16eb7cf0f78b1bf89743af526c", + "DateCreated": "2024-02-28T13:12:33.0000000Z", + "CanDelete": false, + "CanDownload": false, + "PresentationUniqueKey": "1c474c9c4d728d1a2f621adad0f221ae", + "SortName": "纪录片", + "ForcedSortName": "纪录片", + "ExternalUrls": [], + "Path": "/var/packages/EmbyServer/var/root/default/纪录片", + "Taglines": [], + "FileName": "纪录片", + "PlayAccess": "Full", + "RemoteTrailers": [], + "ProviderIds": {}, + "IsFolder": true, + "ParentId": "1", + "Type": "CollectionFolder", + "UserData": { + "PlaybackPositionTicks": 0, + "PlayCount": 0, + "IsFavorite": false, + "Played": false + }, + "ChildCount": 6, + "DisplayPreferencesId": "1c474c9c4d728d1a2f621adad0f221ae", + "PrimaryImageAspectRatio": 0.6688963210702341, + "CollectionType": "tvshows", + "ImageTags": { + "Primary": "a3a250e5e6f81e1c058d335d63b31786" + }, + "BackdropImageTags": [ + "4876668c3b323fa676ca5ff5b5a2fd3f" + ], + "LockedFields": [], + "LockData": false + } + ], + "TotalRecordCount": 7 +} +``` + +### Items 获取歌单列表 + +GET: `[host]/Users/[UserId]/Items` + +query: + +| 参数名 | 备注 | +| --- | --- | +| SortBy | 排序方式列表,可选值: `SortName`| +| SortOrder | 排序,可选值: `Ascending`, `Descending` | +| ParentId | 视图ID,由上一步获取,筛选 `CollectionType = music` 的视图 | +| GenreIds | 类型ID列表,可选 | +| Recursive | 是否递归查询 | +| includeItemTypes | 项目类型,可选值:`Playlist` | +| Fields | 包含的字段列表,可选值:`SortName`, `CanDelete`, `PrimaryImageAspectRatio`, `BasicSyncInfo`, `Container`, `ProductionYear`, `Status`, `EndDate`, `Prefix` | +| EnableImageTypes | 图像类型列表,可选值:`Primary`, `Backdrop`, `Thumb` | +| StartIndex | 起始行数 | +| Limit | 最大数量,可选 | + +response: + +```json +{ + "Items": [ + { + "Name": "电影列表", + "ServerId": "54068b26c84949dc809677381d1267a9", + "Id": "66945", + "Prefix": "电", + "CanDelete": true, + "SupportsSync": true, + "RunTimeTicks": 87574387960, + "IsFolder": true, + "Type": "Playlist", + "UserData": { + "PlaybackPositionTicks": 0, + "PlayCount": 0, + "IsFavorite": false, + "Played": false + }, + "PrimaryImageAspectRatio": 1, + "ImageTags": { + "Primary": "7a76da4af13b608e8498635c878e5752" + }, + "BackdropImageTags": [] + }, + { + "Name": "纯歌曲", + "ServerId": "54068b26c84949dc809677381d1267a9", + "Id": "66946", + "Prefix": "纯", + "CanDelete": true, + "SupportsSync": true, + "RunTimeTicks": 4177020410, + "IsFolder": true, + "Type": "Playlist", + "UserData": { + "PlaybackPositionTicks": 0, + "PlayCount": 0, + "IsFavorite": false, + "Played": false + }, + "PrimaryImageAspectRatio": 1, + "ImageTags": { + "Primary": "ea67ae6c9fb3232d03d03d18e4ec2ef0" + }, + "BackdropImageTags": [] + } + ], + "TotalRecordCount": 2 +} +``` + +### Info 获取服务器信息 + +参见 Jellyfin 的 [Info](./jellyfin#info-获取服务器信息) 接口。 + +### ScheduledTasks 获取扫描状态 + +参见 Jellyfin 的 [ScheduledTasks](jellyfin#scheduledtasks-获取扫描状态) 接口。 + +### PlayedItems/[id] 滚动播放记录 + +**播放开始** + +POST:`[host]/Sessions/Playing` + +body: + +| 参数名 | 备注 | +| --- | --- | +| ItemId | 歌曲ID | +| PlaySessionId | 会话ID | +| PositionTicks | 播放进度 | +| IsPaused | 是否暂停 | +| PlaybackRate | 播放速度 | +| PlayMethod | 播放方式,可选值:`Transcode`, `DirectPlay`, `DirectStream` | + +**播放进度** + +POST:`[host]/Sessions/Playing/Progress` + +body: + +| 参数名 | 备注 | +| --- | --- | +| ItemId | 歌曲ID | +| PlaySessionId | 会话ID | +| PositionTicks | 播放进度 | +| IsPaused | 是否暂停 | +| PlaybackRate | 播放速度 | +| PlayMethod | 播放方式,可选值:`Transcode`, `DirectPlay`, `DirectStream` | + +Emby 文档中对播放进度的报告时机说明如下: + +- 每 10 秒自动报告一次(用于校准服务器上的自动进度增量) +- 在用户于播放器进行任何交互后立即进行 + +报告进度的原因如下,音流仅会对勾选的事件报告进度: + +- [x] TimeUpdate 时间更新 +- [x] Pause 暂停 +- [x] Unpause 取消暂停 +- [ ] VolumeChange 音量变化 +- [ ] RepeatModeChange 重复模式更改 +- [ ] AudioTrackChange 音轨更改 +- [ ] SubtitleTrackChange 字幕更改 +- [ ] PlaylistItemMove 播放列表项移动 +- [ ] PlaylistItemRemove 播放列表项删除 +- [ ] PlaylistItemAdd 播放列表项添加 +- [ ] QualityChange 质量变革 +- [ ] SubtitleOffsetChange 字幕偏移 +- [x] PlaybackRateChange 播放速度改变 + +**标记为已播放** + +> 向 Emby 上报**播放开始**状态时,播放次数已经自动+1,因此无需使用此接口。 + +POST: `[host]/Users/[UserId]/PlayedItems/[id]` + +query: + +| 参数名 | 备注 | +| --- | --- | +| datePlayed | 播放时间,yyyyMMddHHmmss | + +Last.fm 开发者文档中对 scrobble 的条件有以下要求: + +- 曲目时长必须超过 30 秒 +- 曲目已经播放了 4 分钟或总时长的一半(以较早发生者为准) + +满足这些条件后,随时可以发送 scrobble 请求,通常最方便的做法是在曲目播放完毕后发送。 + +:::info + +在 1.3.0 及之前版本,音流会在歌曲播放到 1/4 时上报播放记录。 + +在 1.3.1 及之后版本,音流将遵循上述文档中建议的做法来上报播放记录。 + +::: + +### Similar 获取相似歌曲 + +GET: `[host]/Items/[id]/Similar` + +query: + +| 参数名 | 备注 | +| --- | --- | +| Limit | 结果数量 | +| Fields | 包含的字段列表,可选值:`AudioInfo`, `SortName`, `MediaSources`, `DateCreated`, `ProductionYear` | +| userId | 用户ID | + +response: + +```json +{ + "Items": [ + { + "Name": "狼爱上羊", + "ServerId": "54068b26c84949dc809677381d1267a9", + "Id": "45898", + "DateCreated": "2023-07-28T11:12:03.0000000Z", + "Container": "ogg", + "SortName": "狼爱上羊", + "MediaSources": [ + { + "Protocol": "File", + "Id": "32fa0822ae32197cce9c1861acced72e", + "Path": "/volume1/music/刀郎/歌曲合辑/刀郎 - 狼爱上羊.ogg", + "Type": "Default", + "Container": "ogg", + "Size": 5938532, + "Name": "刀郎 - 狼爱上羊", + "IsRemote": false, + "RunTimeTicks": 4400587760, + "SupportsTranscoding": true, + "SupportsDirectStream": true, + "SupportsDirectPlay": true, + "IsInfiniteStream": false, + "RequiresOpening": false, + "RequiresClosing": false, + "RequiresLooping": false, + "SupportsProbing": false, + "MediaStreams": [ + { + "Codec": "vorbis", + "TimeBase": "1/44100", + "Title": "狼爱上羊", + "DisplayTitle": "Und VORBIS stereo", + "IsInterlaced": false, + "ChannelLayout": "stereo", + "BitRate": 96000, + "Channels": 2, + "SampleRate": 44100, + "IsDefault": false, + "IsForced": false, + "Type": "Audio", + "Index": 0, + "IsExternal": false, + "IsTextSubtitleStream": false, + "SupportsExternalStream": false, + "Protocol": "File", + "AttachmentSize": 0 + }, + { + "Codec": "mjpeg", + "ColorSpace": "bt470bg", + "Comment": "Cover (front)", + "TimeBase": "1/90000", + "IsInterlaced": false, + "BitDepth": 8, + "RefFrames": 1, + "IsDefault": false, + "IsForced": false, + "Height": 750, + "Width": 750, + "RealFrameRate": 90000, + "Profile": "Baseline", + "Type": "EmbeddedImage", + "AspectRatio": "1:1", + "Index": 1, + "IsExternal": false, + "IsTextSubtitleStream": false, + "SupportsExternalStream": false, + "Protocol": "File", + "PixelFormat": "yuvj420p", + "Level": -99, + "IsAnamorphic": false, + "AttachmentSize": 0 + }, + { + "Codec": "text", + "Title": "Lyrics", + "Extradata": "[00:00.00]作词 : 汤潮 演唱:汤潮\n[00:28.62]北风呼呼的刮\n[00:33.35]雪花飘飘洒洒\n[00:38.70]突然传来了一声枪响\n[00:42.64]这匹狼他受了重伤\n[00:47.30]但他侥幸逃脱了\n[00:51.84]救它的是一只羊\n[00:55.89]从此它们约定三生\n[01:00.51]苦诉着衷肠\n[01:04.41]\n[01:05.26]狼说亲爱的\n[01:10.17]谢谢你为我疗伤\n[01:14.93]不管未来有多少的风雨\n[01:19.48]我都为你去抗\n[01:24.15]羊说不要客气\n[01:28.65]谁让我爱上了你\n[01:33.38]在你身边有多么的危险\n[01:37.97]我都会陪伴你\n[01:42.16]就这样 他们快乐的流浪\n[01:49.39]就这样 他们为爱歌唱\n[01:55.50]\n[01:56.45]狼爱上羊啊爱的疯狂\n[02:01.60]谁让他们真爱了一场\n[02:05.60]狼爱上羊啊并不荒唐\n[02:10.30]他们说有爱就有方向\n[02:14.85]狼爱上羊啊爱的风光\n[02:19.51]他们穿破世俗的城墙\n[02:24.30]狼爱上羊啊爱的疯狂\n[02:28.67]他们相互搀扶去远方\n[02:32.63]\n[02:45.63]\n[02:58.82]狼说亲爱的\n[03:03.28]谢谢你为我疗伤\n[03:07.72]不管未来有多少的风雨\n[03:12.58]我都为你去抗\n[03:17.16]羊说不要客气\n[03:21.61]谁让我爱上了你\n[03:26.32]在你身边有多么的危险\n[03:31.10]我都会陪伴你\n[03:35.17]就这样 他们快乐的流浪\n[03:42.50]就这样 他们为爱歌唱\n[03:48.59]\n[03:49.63]狼爱上羊啊爱的疯狂\n[03:54.70]谁让他们真爱了一场\n[03:58.78]狼爱上羊啊并不荒唐\n[04:03.40]他们说有爱就有方向\n[04:07.81]狼爱上羊啊爱的风光\n[04:12.54]他们穿破世俗的城墙\n[04:17.10]狼爱上羊啊爱的疯狂\n[04:21.82]他们相互搀扶去远方\n[04:27.42]", + "DisplayTitle": "Und (TEXT)", + "IsInterlaced": false, + "IsDefault": false, + "IsForced": false, + "Type": "Subtitle", + "Index": 2, + "IsExternal": false, + "IsTextSubtitleStream": true, + "SupportsExternalStream": true, + "Protocol": "File", + "AttachmentSize": 0, + "SubtitleLocationType": "InternalStream" + } + ], + "Formats": [], + "Bitrate": 107958, + "RequiredHttpHeaders": {}, + "ReadAtNativeFramerate": false, + "DefaultAudioStreamIndex": 0, + "DefaultSubtitleStreamIndex": 2 + } + ], + "RunTimeTicks": 4400587760, + "Size": 5938532, + "Bitrate": 107958, + "ProductionYear": 2005, + "IndexNumber": 2, + "IsFolder": false, + "Type": "Audio", + "ParentBackdropItemId": "10126", + "ParentBackdropImageTags": [ + "401cc5f81954801ecbcc6235ac18705b" + ], + "UserData": { + "PlaybackPositionTicks": 0, + "PlayCount": 0, + "IsFavorite": false, + "Played": false + }, + "Artists": [ + "刀郎" + ], + "ArtistItems": [ + { + "Name": "刀郎", + "Id": "10126" + } + ], + "Composers": [], + "Album": "歌曲合辑", + "AlbumId": "10130", + "AlbumPrimaryImageTag": "b2b7edb1ed1d0fee7ede1a1b6ed6c341", + "AlbumArtist": "刀郎", + "AlbumArtists": [ + { + "Name": "刀郎", + "Id": "10126" + } + ], + "ImageTags": { + "Primary": "96bc3d572a66e162f9b1a8304edb4364" + }, + "BackdropImageTags": [], + "MediaType": "Audio" + } + ], + "TotalRecordCount": 1 +} +``` + +### Items 获取歌曲列表 + +GET: `[host]/Users/[UserId]/Items` + +query: + +| 参数名 | 备注 | +| --- | --- | +| SortBy | 排序方式列表,可选值: `Random`, `DateCreated`, `PremiereDate`, `PlayCount`, `DatePlayed`, `SortName`, `CommunityRating` | +| SortOrder | 排序,可选值: `Ascending`, `Descending` | +| IncludeItemTypes | 包含的项目类型,可选值:`Audio` | +| Recursive | 是否递归查询 | +| Fields | 包含的字段列表,可选值:`SortName`, `MediaSources`, `AudioInfo`, `DateCreated`, `ProductionYear` | +| ImageTypeLimit | 返回的每个图片类型图片数量 | +| EnableImageTypes | 图片类型列表,可选值:`Primary` | +| StartIndex | 起始行数 | +| Limit | 最大数量,可选 | +| ParentId | 父视图ID,可选 | +| GenreIds | 类型ID列表,可选 | +| isFavorite | 收藏状态,可选 | +| SearchTerm | 搜索词,可选 | + +response: + +```json +{ + "Items": [ + { + "Name": "9420", + "ServerId": "54068b26c84949dc809677381d1267a9", + "Id": "50985", + "DateCreated": "2021-11-27T08:31:24.0000000Z", + "Container": "mp3", + "SortName": "9420", + "MediaSources": [ + { + "Protocol": "File", + "Id": "74ec19f32687018d22e64b9a41eb6336", + "Path": "/volume1/music/麦小兜/9420/9420.mp3", + "Type": "Default", + "Container": "mp3", + "Size": 9225558, + "Name": "9420", + "IsRemote": false, + "RunTimeTicks": 2291983670, + "SupportsTranscoding": true, + "SupportsDirectStream": true, + "SupportsDirectPlay": true, + "IsInfiniteStream": false, + "RequiresOpening": false, + "RequiresClosing": false, + "RequiresLooping": false, + "SupportsProbing": false, + "MediaStreams": [ + { + "Codec": "mp3", + "TimeBase": "1/14112000", + "DisplayTitle": "Und MP3 stereo", + "IsInterlaced": false, + "ChannelLayout": "stereo", + "BitRate": 320000, + "Channels": 2, + "SampleRate": 44100, + "IsDefault": false, + "IsForced": false, + "Type": "Audio", + "Index": 0, + "IsExternal": false, + "IsTextSubtitleStream": false, + "SupportsExternalStream": false, + "Protocol": "File", + "AttachmentSize": 0 + }, + { + "Codec": "mjpeg", + "ColorSpace": "bt470bg", + "Comment": "Cover (front)", + "TimeBase": "1/90000", + "IsInterlaced": false, + "BitDepth": 8, + "RefFrames": 1, + "IsDefault": false, + "IsForced": false, + "Height": 600, + "Width": 600, + "RealFrameRate": 90000, + "Profile": "Baseline", + "Type": "EmbeddedImage", + "AspectRatio": "1:1", + "Index": 1, + "IsExternal": false, + "IsTextSubtitleStream": false, + "SupportsExternalStream": false, + "Protocol": "File", + "PixelFormat": "yuvj420p", + "Level": -99, + "IsAnamorphic": false, + "AttachmentSize": 0 + }, + { + "Codec": "text", + "Title": "Lyrics", + "Extradata": "[ti:9420]\n[ar:麦小兜]\n[al:9420]\n[00:00.00]9420 - 麦小兜\n[00:05.05]词:可泽\n[00:10.10]曲:可泽\n[00:15.15]编曲:杨栋梁\n[00:20.21]制作公司:Hikoon Music\n[00:25.26]手牵手一起走在幸福的大街\n[00:28.21]微风缓缓的吹来你我相依偎\n[00:31.15]爱的目光如此的热烈\n[00:34.97]\n[00:36.94]这份爱就像是在燃烧的火堆\n[00:39.95]炽热的火焰如同盛开的玫瑰\n[00:42.85]不管白天黑夜继续的沉醉\n[00:46.30]\n[00:47.59]整个世界弥漫\n[00:48.97]\n[00:49.47]薄荷般的气味\n[00:51.45]耳边\n[00:52.34]你的呢喃不停吹\n[00:54.20]所有\n[00:55.14]孤单寂寞 都被悄悄震碎\n[00:59.39]你的眼神就像\n[01:00.49]\n[01:01.08]流淌着的河水\n[01:02.94]流进\n[01:03.42]\n[01:04.01]我的身体润心扉\n[01:05.98]洗涤\n[01:06.95]所有悲伤烦恼带来安慰\n[01:11.08]关于我们\n[01:13.84]我只想说\n[01:16.68]简单一句\n[01:19.53]就是爱你\n[01:22.52]比翼双飞\n[01:25.41]金蝉做媒\n[01:28.26]天造地设的一对\n[01:33.61]\n[01:58.93]手牵手一起走在幸福的大街\n[02:01.91]微风缓缓的吹来你我相依偎\n[02:04.79]爱的目光如此的热烈\n[02:08.76]\n[02:10.76]这份爱就像是在燃烧的火堆\n[02:13.50]炽热的火焰如同盛开的玫瑰\n[02:16.51]不管白天黑夜继续的沉醉\n[02:20.08]\n[02:21.22]整个世界弥漫\n[02:23.12]薄荷般的气味\n[02:25.04]耳边\n[02:25.98]你的呢喃不停吹\n[02:27.90]所有\n[02:28.82]孤单寂寞都被悄悄震碎\n[02:32.96]你的眼神就像\n[02:34.71]流淌着的河水\n[02:36.69]流进\n[02:37.66]我的身体润心扉\n[02:39.57]洗涤\n[02:40.52]所有悲伤烦恼带来安慰\n[02:44.81]关于我们\n[02:47.45]我只想说\n[02:50.28]简单一句\n[02:53.16]就是爱你\n[02:56.07]比翼双飞\n[02:59.08]金蝉做媒\n[03:01.95]天造地设的一对\n[03:07.33]\n[03:08.08]关于我们\n[03:10.90]我只想说\n[03:13.80]简单一句\n[03:16.61]就是爱你\n[03:19.58]比翼双飞\n[03:22.56]金蝉做媒\n[03:25.48]天造地设的一对", + "DisplayTitle": "Und (TEXT)", + "IsInterlaced": false, + "IsDefault": false, + "IsForced": false, + "Type": "Subtitle", + "Index": 2, + "IsExternal": false, + "IsTextSubtitleStream": true, + "SupportsExternalStream": true, + "Protocol": "File", + "AttachmentSize": 0, + "SubtitleLocationType": "InternalStream" + } + ], + "Formats": [], + "Bitrate": 322011, + "RequiredHttpHeaders": {}, + "ReadAtNativeFramerate": false, + "DefaultAudioStreamIndex": 0, + "DefaultSubtitleStreamIndex": 2 + } + ], + "RunTimeTicks": 2291983670, + "Size": 9225558, + "Bitrate": 322011, + "ProductionYear": 2017, + "IndexNumber": 1, + "IsFolder": false, + "Type": "Audio", + "UserData": { + "PlaybackPositionTicks": 0, + "PlayCount": 0, + "IsFavorite": false, + "Played": false + }, + "Artists": [ + "麦小兜" + ], + "ArtistItems": [ + { + "Name": "麦小兜", + "Id": "7855" + } + ], + "Composers": [], + "Album": "9420", + "AlbumId": "7856", + "AlbumPrimaryImageTag": "605950b8b45c1dffe57a5c3d3870916a", + "AlbumArtist": "麦小兜", + "AlbumArtists": [ + { + "Name": "麦小兜", + "Id": "7855" + } + ], + "ImageTags": { + "Primary": "c36a54ac99a7ef10be9fc0626a1d502b" + }, + "MediaType": "Audio" + } + ], + "TotalRecordCount": 19408 +} +``` + +### Items/[id] 获取歌曲信息 + +GET: `[host]/Users/[UserId]/Items/[id]` + +response: + +```json +{ + "Name": "艾里甫与赛乃姆", + "ServerId": "54068b26c84949dc809677381d1267a9", + "Id": "45902", + "Etag": "06ffa98cf3145f04578fddcff6ee66f8", + "DateCreated": "2023-07-28T11:12:06.0000000Z", + "CanDelete": true, + "CanDownload": true, + "PresentationUniqueKey": "2578eaf86b158fd6b3a97034431faf41", + "SupportsSync": true, + "Container": "flac", + "SortName": "艾里甫与赛乃姆", + "ForcedSortName": "艾里甫与赛乃姆", + "ExternalUrls": [], + "MediaSources": [ + { + "Protocol": "File", + "Id": "2578eaf86b158fd6b3a97034431faf41", + "Path": "/volume1/music/刀郎/2002年的第一场雪/刀郎 - 艾里甫与赛乃姆.flac", + "Type": "Default", + "Container": "flac", + "Size": 43685853, + "Name": "刀郎 - 艾里甫与赛乃姆", + "IsRemote": false, + "RunTimeTicks": 3525466670, + "SupportsTranscoding": true, + "SupportsDirectStream": true, + "SupportsDirectPlay": true, + "IsInfiniteStream": false, + "RequiresOpening": false, + "RequiresClosing": false, + "RequiresLooping": false, + "SupportsProbing": false, + "MediaStreams": [ + { + "Codec": "flac", + "TimeBase": "1/44100", + "DisplayTitle": "Und FLAC stereo", + "IsInterlaced": false, + "ChannelLayout": "stereo", + "BitRate": 991320, + "BitDepth": 16, + "Channels": 2, + "SampleRate": 44100, + "IsDefault": false, + "IsForced": false, + "Type": "Audio", + "Index": 0, + "IsExternal": false, + "IsTextSubtitleStream": false, + "SupportsExternalStream": false, + "Protocol": "File", + "AttachmentSize": 0 + }, + { + "Codec": "mjpeg", + "ColorSpace": "bt470bg", + "Comment": "Other", + "TimeBase": "1/90000", + "IsInterlaced": false, + "BitDepth": 8, + "RefFrames": 1, + "IsDefault": false, + "IsForced": false, + "Height": 500, + "Width": 500, + "RealFrameRate": 90000, + "Profile": "Baseline", + "Type": "EmbeddedImage", + "AspectRatio": "1:1", + "Index": 1, + "IsExternal": false, + "IsTextSubtitleStream": false, + "SupportsExternalStream": false, + "Protocol": "File", + "PixelFormat": "yuvj444p", + "Level": -99, + "IsAnamorphic": false, + "AttachmentSize": 0 + }, + { + "Codec": "text", + "Title": "Lyrics", + "Extradata": "[ti:艾里甫与赛乃姆]\n[ar:刀郎]\n[al:2002年的第一场雪]\n[00:00.00]艾里甫与赛乃姆 - 刀郎\n[00:09.00]词:刀郎\n[00:18.00]曲:刀郎\n[00:27.01]从小和你青梅竹马相约在天山下\n[00:33.93]\n[00:37.12]我们本来是天底下最幸福的人啊\n[00:45.74]\n[00:47.01]赛乃姆你是花丛中最美的石榴花\n[00:55.17]\n[00:57.08]艾里甫我却是巴格达上孤独的阿卡\n[01:04.33]\n[01:09.60]夜莺歌声在每个夜晚都会陪伴她\n[01:16.54]\n[01:19.55]我的琴声却飘荡在遥远的巴格达\n[01:27.79]\n[01:29.68]为了爱情我被放逐在天涯\n[01:37.66]\n[01:39.60]莫非今生和你厮守变成了神话\n[01:46.73]\n[01:49.59]我寻遍天山南北我要找到你赛乃姆\n[01:58.17]\n[01:59.58]不管是跋山涉水历尽千辛万苦\n[02:08.10]\n[02:09.53]花园里种不出天山上的雪莲花\n[02:17.68]\n[02:19.56]不历经磨难我找不到今生的幸福\n[02:28.19]\n[02:55.80]夜莺歌声在每个夜晚都会陪伴她\n[03:02.66]\n[03:05.76]我的琴声却飘荡在遥远的巴格达\n[03:13.91]\n[03:15.86]为了爱情我被放逐在天涯\n[03:23.96]\n[03:25.90]莫非今生和你厮守变成了神话\n[03:33.10]\n[03:35.88]我寻遍天山南北我要找到你赛乃姆\n[03:44.28]\n[03:45.83]不管是跋山涉水历尽千辛万苦\n[03:54.33]\n[03:55.89]花园里种不出天山上的雪莲花\n[04:03.91]\n[04:05.87]不历经磨难我找不到今生的幸福\n[04:14.35]\n[04:17.24]我寻遍天山南北我要找到你赛乃姆\n[04:25.50]\n[04:26.91]不管是跋山涉水历尽千辛万苦\n[04:35.63]\n[04:37.16]花园里种不出天山上的雪莲花\n[04:45.17]\n[04:46.96]不历经磨难我找不到今生的幸福", + "DisplayTitle": "Und (TEXT)", + "IsInterlaced": false, + "IsDefault": false, + "IsForced": false, + "Type": "Subtitle", + "Index": 2, + "IsExternal": false, + "IsTextSubtitleStream": true, + "SupportsExternalStream": true, + "Protocol": "File", + "AttachmentSize": 0, + "SubtitleLocationType": "InternalStream" + } + ], + "Formats": [], + "Bitrate": 991320, + "RequiredHttpHeaders": {}, + "ReadAtNativeFramerate": false, + "DefaultAudioStreamIndex": 0, + "DefaultSubtitleStreamIndex": 2 + } + ], + "Path": "/volume1/music/刀郎/2002年的第一场雪/刀郎 - 艾里甫与赛乃姆.flac", + "Taglines": [], + "Genres": [], + "RunTimeTicks": 3525466670, + "Size": 43685853, + "FileName": "刀郎 - 艾里甫与赛乃姆.flac", + "Bitrate": 991320, + "PlayAccess": "Full", + "ProductionYear": 2004, + "IndexNumber": 5, + "ParentIndexNumber": 1, + "RemoteTrailers": [], + "ProviderIds": {}, + "IsFolder": false, + "ParentId": "45874", + "Type": "Audio", + "Studios": [], + "GenreItems": [], + "TagItems": [], + "ParentBackdropItemId": "10126", + "ParentBackdropImageTags": [ + "401cc5f81954801ecbcc6235ac18705b" + ], + "UserData": { + "PlaybackPositionTicks": 0, + "PlayCount": 1, + "IsFavorite": false, + "LastPlayedDate": "2024-04-19T14:29:46.0000000Z", + "Played": true + }, + "DisplayPreferencesId": "61bba315f137702baa296a1c417faada", + "PrimaryImageAspectRatio": 1, + "Artists": [ + "刀郎" + ], + "ArtistItems": [ + { + "Name": "刀郎", + "Id": "10126" + } + ], + "Composers": [], + "Album": "2002年的第一场雪", + "AlbumId": "10139", + "AlbumPrimaryImageTag": "0775ee34b85f766a6fc47fdf647b4bc4", + "AlbumArtist": "刀郎", + "AlbumArtists": [ + { + "Name": "刀郎", + "Id": "10126" + } + ], + "MediaStreams": [ + { + "Codec": "flac", + "TimeBase": "1/44100", + "DisplayTitle": "Und FLAC stereo", + "IsInterlaced": false, + "ChannelLayout": "stereo", + "BitRate": 991320, + "BitDepth": 16, + "Channels": 2, + "SampleRate": 44100, + "IsDefault": false, + "IsForced": false, + "Type": "Audio", + "Index": 0, + "IsExternal": false, + "IsTextSubtitleStream": false, + "SupportsExternalStream": false, + "Protocol": "File", + "AttachmentSize": 0 + }, + { + "Codec": "mjpeg", + "ColorSpace": "bt470bg", + "Comment": "Other", + "TimeBase": "1/90000", + "IsInterlaced": false, + "BitDepth": 8, + "RefFrames": 1, + "IsDefault": false, + "IsForced": false, + "Height": 500, + "Width": 500, + "RealFrameRate": 90000, + "Profile": "Baseline", + "Type": "EmbeddedImage", + "AspectRatio": "1:1", + "Index": 1, + "IsExternal": false, + "IsTextSubtitleStream": false, + "SupportsExternalStream": false, + "Protocol": "File", + "PixelFormat": "yuvj444p", + "Level": -99, + "IsAnamorphic": false, + "AttachmentSize": 0 + }, + { + "Codec": "text", + "Title": "Lyrics", + "Extradata": "[ti:艾里甫与赛乃姆]\n[ar:刀郎]\n[al:2002年的第一场雪]\n[00:00.00]艾里甫与赛乃姆 - 刀郎\n[00:09.00]词:刀郎\n[00:18.00]曲:刀郎\n[00:27.01]从小和你青梅竹马相约在天山下\n[00:33.93]\n[00:37.12]我们本来是天底下最幸福的人啊\n[00:45.74]\n[00:47.01]赛乃姆你是花丛中最美的石榴花\n[00:55.17]\n[00:57.08]艾里甫我却是巴格达上孤独的阿卡\n[01:04.33]\n[01:09.60]夜莺歌声在每个夜晚都会陪伴她\n[01:16.54]\n[01:19.55]我的琴声却飘荡在遥远的巴格达\n[01:27.79]\n[01:29.68]为了爱情我被放逐在天涯\n[01:37.66]\n[01:39.60]莫非今生和你厮守变成了神话\n[01:46.73]\n[01:49.59]我寻遍天山南北我要找到你赛乃姆\n[01:58.17]\n[01:59.58]不管是跋山涉水历尽千辛万苦\n[02:08.10]\n[02:09.53]花园里种不出天山上的雪莲花\n[02:17.68]\n[02:19.56]不历经磨难我找不到今生的幸福\n[02:28.19]\n[02:55.80]夜莺歌声在每个夜晚都会陪伴她\n[03:02.66]\n[03:05.76]我的琴声却飘荡在遥远的巴格达\n[03:13.91]\n[03:15.86]为了爱情我被放逐在天涯\n[03:23.96]\n[03:25.90]莫非今生和你厮守变成了神话\n[03:33.10]\n[03:35.88]我寻遍天山南北我要找到你赛乃姆\n[03:44.28]\n[03:45.83]不管是跋山涉水历尽千辛万苦\n[03:54.33]\n[03:55.89]花园里种不出天山上的雪莲花\n[04:03.91]\n[04:05.87]不历经磨难我找不到今生的幸福\n[04:14.35]\n[04:17.24]我寻遍天山南北我要找到你赛乃姆\n[04:25.50]\n[04:26.91]不管是跋山涉水历尽千辛万苦\n[04:35.63]\n[04:37.16]花园里种不出天山上的雪莲花\n[04:45.17]\n[04:46.96]不历经磨难我找不到今生的幸福", + "DisplayTitle": "Und (TEXT)", + "IsInterlaced": false, + "IsDefault": false, + "IsForced": false, + "Type": "Subtitle", + "Index": 2, + "IsExternal": false, + "IsTextSubtitleStream": true, + "SupportsExternalStream": true, + "Protocol": "File", + "AttachmentSize": 0, + "SubtitleLocationType": "InternalStream" + } + ], + "ImageTags": { + "Primary": "d236a8e730c9c4b54e2a9c4a4c7be5df" + }, + "BackdropImageTags": [], + "MediaType": "Audio", + "LockedFields": [], + "LockData": false +} +``` + +### Items 获取专辑中的歌曲 + +参见 Jellyfin 的 [Items](jellyfin#items-获取专辑中的歌曲) 接口。 + +### Items 获取歌手名下歌曲 + +参见 Jellyfin 的 [Items](jellyfin#items-获取歌手名下歌曲) 接口。 + +### Items 获取歌单中的歌曲 + +GET: `[host]/Users/[UserId]/Items` + +query: + +| 参数名 | 备注 | +| --- | --- | +| SortBy | 排序方式列表,可选值: `ListItemOrder`, `SortName`, `Album,ParentIndexNumber,IndexNumber`, `Artist,Album,ParentIndexNumber,IndexNumber,SortName` | +| SortOrder | 排序,可选值: `Ascending`, `Descending` | +| Fields | 包含的字段列表,可选值:`PrimaryImageAspectRatio`, `MediaSources`, `AudioInfo`, `DateCreated`, `ProductionYear` | +| ImageTypeLimit | 返回的每个图片类型图片数量 | +| StartIndex | 起始行数 | +| Limit | 最大数量,可选 | +| ParentId | 歌单ID | + +response: + +```json +{ + "Items": [ + { + "Name": "9420", + "ServerId": "54068b26c84949dc809677381d1267a9", + "Id": "50985", + "DateCreated": "2021-11-27T08:31:24.0000000Z", + "Container": "mp3", + "SortName": "9420", + "MediaSources": [ + { + "Protocol": "File", + "Id": "74ec19f32687018d22e64b9a41eb6336", + "Path": "/volume1/music/麦小兜/9420/9420.mp3", + "Type": "Default", + "Container": "mp3", + "Size": 9225558, + "Name": "9420", + "IsRemote": false, + "RunTimeTicks": 2291983670, + "SupportsTranscoding": true, + "SupportsDirectStream": true, + "SupportsDirectPlay": true, + "IsInfiniteStream": false, + "RequiresOpening": false, + "RequiresClosing": false, + "RequiresLooping": false, + "SupportsProbing": false, + "MediaStreams": [ + { + "Codec": "mp3", + "TimeBase": "1/14112000", + "DisplayTitle": "Und MP3 stereo", + "IsInterlaced": false, + "ChannelLayout": "stereo", + "BitRate": 320000, + "Channels": 2, + "SampleRate": 44100, + "IsDefault": false, + "IsForced": false, + "Type": "Audio", + "Index": 0, + "IsExternal": false, + "IsTextSubtitleStream": false, + "SupportsExternalStream": false, + "Protocol": "File", + "AttachmentSize": 0 + }, + { + "Codec": "mjpeg", + "ColorSpace": "bt470bg", + "Comment": "Cover (front)", + "TimeBase": "1/90000", + "IsInterlaced": false, + "BitDepth": 8, + "RefFrames": 1, + "IsDefault": false, + "IsForced": false, + "Height": 600, + "Width": 600, + "RealFrameRate": 90000, + "Profile": "Baseline", + "Type": "EmbeddedImage", + "AspectRatio": "1:1", + "Index": 1, + "IsExternal": false, + "IsTextSubtitleStream": false, + "SupportsExternalStream": false, + "Protocol": "File", + "PixelFormat": "yuvj420p", + "Level": -99, + "IsAnamorphic": false, + "AttachmentSize": 0 + }, + { + "Codec": "text", + "Title": "Lyrics", + "Extradata": "[ti:9420]\n[ar:麦小兜]\n[al:9420]\n[00:00.00]9420 - 麦小兜\n[00:05.05]词:可泽\n[00:10.10]曲:可泽\n[00:15.15]编曲:杨栋梁\n[00:20.21]制作公司:Hikoon Music\n[00:25.26]手牵手一起走在幸福的大街\n[00:28.21]微风缓缓的吹来你我相依偎\n[00:31.15]爱的目光如此的热烈\n[00:34.97]\n[00:36.94]这份爱就像是在燃烧的火堆\n[00:39.95]炽热的火焰如同盛开的玫瑰\n[00:42.85]不管白天黑夜继续的沉醉\n[00:46.30]\n[00:47.59]整个世界弥漫\n[00:48.97]\n[00:49.47]薄荷般的气味\n[00:51.45]耳边\n[00:52.34]你的呢喃不停吹\n[00:54.20]所有\n[00:55.14]孤单寂寞 都被悄悄震碎\n[00:59.39]你的眼神就像\n[01:00.49]\n[01:01.08]流淌着的河水\n[01:02.94]流进\n[01:03.42]\n[01:04.01]我的身体润心扉\n[01:05.98]洗涤\n[01:06.95]所有悲伤烦恼带来安慰\n[01:11.08]关于我们\n[01:13.84]我只想说\n[01:16.68]简单一句\n[01:19.53]就是爱你\n[01:22.52]比翼双飞\n[01:25.41]金蝉做媒\n[01:28.26]天造地设的一对\n[01:33.61]\n[01:58.93]手牵手一起走在幸福的大街\n[02:01.91]微风缓缓的吹来你我相依偎\n[02:04.79]爱的目光如此的热烈\n[02:08.76]\n[02:10.76]这份爱就像是在燃烧的火堆\n[02:13.50]炽热的火焰如同盛开的玫瑰\n[02:16.51]不管白天黑夜继续的沉醉\n[02:20.08]\n[02:21.22]整个世界弥漫\n[02:23.12]薄荷般的气味\n[02:25.04]耳边\n[02:25.98]你的呢喃不停吹\n[02:27.90]所有\n[02:28.82]孤单寂寞都被悄悄震碎\n[02:32.96]你的眼神就像\n[02:34.71]流淌着的河水\n[02:36.69]流进\n[02:37.66]我的身体润心扉\n[02:39.57]洗涤\n[02:40.52]所有悲伤烦恼带来安慰\n[02:44.81]关于我们\n[02:47.45]我只想说\n[02:50.28]简单一句\n[02:53.16]就是爱你\n[02:56.07]比翼双飞\n[02:59.08]金蝉做媒\n[03:01.95]天造地设的一对\n[03:07.33]\n[03:08.08]关于我们\n[03:10.90]我只想说\n[03:13.80]简单一句\n[03:16.61]就是爱你\n[03:19.58]比翼双飞\n[03:22.56]金蝉做媒\n[03:25.48]天造地设的一对", + "DisplayTitle": "Und (TEXT)", + "IsInterlaced": false, + "IsDefault": false, + "IsForced": false, + "Type": "Subtitle", + "Index": 2, + "IsExternal": false, + "IsTextSubtitleStream": true, + "SupportsExternalStream": true, + "Protocol": "File", + "AttachmentSize": 0, + "SubtitleLocationType": "InternalStream" + } + ], + "Formats": [], + "Bitrate": 322011, + "RequiredHttpHeaders": {}, + "ReadAtNativeFramerate": false, + "DefaultAudioStreamIndex": 0, + "DefaultSubtitleStreamIndex": 2 + } + ], + "RunTimeTicks": 2291983670, + "Size": 9225558, + "Bitrate": 322011, + "ProductionYear": 2017, + "IndexNumber": 1, + "IsFolder": false, + "Type": "Audio", + "UserData": { + "PlaybackPositionTicks": 0, + "PlayCount": 0, + "IsFavorite": false, + "Played": false + }, + "Artists": [ + "麦小兜" + ], + "ArtistItems": [ + { + "Name": "麦小兜", + "Id": "7855" + } + ], + "Composers": [], + "Album": "9420", + "AlbumId": "7856", + "AlbumPrimaryImageTag": "605950b8b45c1dffe57a5c3d3870916a", + "AlbumArtist": "麦小兜", + "AlbumArtists": [ + { + "Name": "麦小兜", + "Id": "7855" + } + ], + "ImageTags": { + "Primary": "c36a54ac99a7ef10be9fc0626a1d502b" + }, + "MediaType": "Audio" + } + ], + "TotalRecordCount": 19408 +} +``` + +### FavoriteItems/[id] 收藏歌曲/专辑/歌手 + +POST: `[host]/Users/[UserId]/FavoriteItems/[id]` + +### stream 歌曲播放链接 + +GET: `[host]/Audio/[id]/stream` + +query: + +| 参数名 | 备注 | +| --- | --- | +| static | 是否转码,不转码时填写 | +| audioCodec | 格式,可选值:`mp3`, `aac` | +| audioBitRate | 比特率,转码时填写 | + +:::note + +请求头需要携带授权信息。 + +::: + +### FavoriteItems[id] 取消收藏 + +DELETE: `[host]/Users/[UserId]/FavoriteItems/[id]` + +### Items 添加歌曲到歌单 + +POST: `[host]/Playlists/[id]/Items` + +query: + +| 参数名 | 备注 | +| --- | --- | +| Ids | 歌曲ID列表 | +| UserId | 用户ID | + +response code: 204 + +### Items 从歌单中移除歌曲 + +DELETE: `[host]/Playlists/[id]/Items` + +query: + +| 参数名 | 备注 | +| --- | --- | +| entryIds | 歌曲ID列表 | + +response code: 204 + +### Items/[id] 修改歌单信息 + +POST: `[host]/Items/[id]` + +body(`application/json`): + +| 参数名 | 备注 | +| --- | --- | +| Name | 歌单名 | +| Id | 歌单ID | + +response code: 204 \ No newline at end of file diff --git a/i18n/en/docusaurus-plugin-content-docs/current/notes/services/jellyfin.md b/i18n/en/docusaurus-plugin-content-docs/current/notes/services/jellyfin.md new file mode 100644 index 0000000..9a92283 --- /dev/null +++ b/i18n/en/docusaurus-plugin-content-docs/current/notes/services/jellyfin.md @@ -0,0 +1,2113 @@ +--- +sidebar_position: 3 +--- + +# Jellyfin 接口文档 + +:::note 参考文档: + +- [Jellyfin API](https://api.jellyfin.org/) + +::: + +## 认证 + +### AuthenticateByName 登录 + +POST: `[host]/Users/AuthenticateByName` + +body(`application/json`): + +| 参数名 | 是否必填 | 默认值 | 备注 | +| --- | --- | --- | --- | +| Username | Y | | 用户名 | +| Pw | Y | | 密码 | + +response: + +```json +{ + "User": { + "Name": "username", + "ServerId": "68ca97fexxxf41a08axxxf3d057a5f75", + "Id": "cb70exxx1bxxx96ab14xxx6151ba2250", + "HasPassword": true, + "HasConfiguredPassword": true, + "HasConfiguredEasyPassword": false, + "EnableAutoLogin": false, + "LastLoginDate": "2024-04-19T02:51:02.6245939Z", + "LastActivityDate": "2024-04-19T02:51:02.6245939Z", + "Configuration": { + "PlayDefaultAudioTrack": true, + "SubtitleLanguagePreference": "", + "DisplayMissingEpisodes": false, + "GroupedFolders": [], + "SubtitleMode": "Default", + "DisplayCollectionsView": false, + "EnableLocalPassword": false, + "OrderedViews": [], + "LatestItemsExcludes": [], + "MyMediaExcludes": [], + "HidePlayedInLatest": true, + "RememberAudioSelections": true, + "RememberSubtitleSelections": true, + "EnableNextEpisodeAutoPlay": true + }, + "Policy": { + "IsAdministrator": true, + "IsHidden": true, + "IsDisabled": false, + "BlockedTags": [], + "EnableUserPreferenceAccess": true, + "AccessSchedules": [], + "BlockUnratedItems": [], + "EnableRemoteControlOfOtherUsers": true, + "EnableSharedDeviceControl": true, + "EnableRemoteAccess": true, + "EnableLiveTvManagement": true, + "EnableLiveTvAccess": true, + "EnableMediaPlayback": true, + "EnableAudioPlaybackTranscoding": true, + "EnableVideoPlaybackTranscoding": true, + "EnablePlaybackRemuxing": true, + "ForceRemoteSourceTranscoding": false, + "EnableContentDeletion": true, + "EnableContentDeletionFromFolders": [], + "EnableContentDownloading": true, + "EnableSyncTranscoding": true, + "EnableMediaConversion": true, + "EnabledDevices": [], + "EnableAllDevices": true, + "EnabledChannels": [], + "EnableAllChannels": true, + "EnabledFolders": [], + "EnableAllFolders": true, + "InvalidLoginAttemptCount": 0, + "LoginAttemptsBeforeLockout": -1, + "MaxActiveSessions": 0, + "EnablePublicSharing": true, + "BlockedMediaFolders": [], + "BlockedChannels": [], + "RemoteClientBitrateLimit": 0, + "AuthenticationProviderId": "Jellyfin.Server.Implementations.Users.DefaultAuthenticationProvider", + "PasswordResetProviderId": "Jellyfin.Server.Implementations.Users.DefaultPasswordResetProvider", + "SyncPlayAccess": "CreateAndJoinGroups" + } + }, + "SessionInfo": { + "PlayState": { + "CanSeek": false, + "IsPaused": false, + "IsMuted": false, + "RepeatMode": "RepeatNone" + }, + "AdditionalUsers": [], + "Capabilities": { + "PlayableMediaTypes": [], + "SupportedCommands": [], + "SupportsMediaControl": false, + "SupportsContentUploading": false, + "SupportsPersistentIdentifier": true, + "SupportsSync": false + }, + "RemoteEndPoint": "172.27.0.1", + "PlayableMediaTypes": [], + "Id": "15fffxxxd3fxxx30cca43f59fbe23e5b", + "UserId": "cb70eae21b42496ab14xxx6151xxx250", + "UserName": "username", + "Client": "StreamMusicTest", + "LastActivityDate": "2024-04-19T02:51:02.8736846Z", + "LastPlaybackCheckIn": "0001-01-01T00:00:00.0000000Z", + "DeviceName": "MBP", + "DeviceId": "6tgrf7a0qwcfrxxx3bxka0vb", + "ApplicationVersion": "1.2.6", + "IsActive": true, + "SupportsMediaControl": false, + "SupportsRemoteControl": false, + "NowPlayingQueue": [], + "NowPlayingQueueFullItems": [], + "HasCustomDeviceName": false, + "ServerId": "68ca97fe91ff41xxxaa2xxxd057a5f75", + "SupportedCommands": [] + }, + "AccessToken": "602f3582cxxx4d2xxx83d920ad606e22", + "ServerId": "68ca97fe91fxxxx08aaxxx3d057a5f75" +} +``` + +在后续的请求中,需要携带以下参数: + +| 参数名 | 备注 | +| --- | --- | +| MediaBrowser Client | 客户端名称 | +| Device | 设备名 | +| DeviceId | 设备Id | +| Version | 客户端版本号 | +| Token | 访问令牌,登录后从响应中的 `AccessToken` 字段获取 | + +最后,将以上参数组装起来放在请求头中: + +`Authorization: MediaBrowser Client="$clientName", Device="$deviceName", DeviceId="$deviceId", Version="$clientVersion"` + +:::tip + +响应中的 `User.Id` 在后续很多请求中都有用到,建议登录后保存一下。 + +::: + +### Ping 测试服务器连通性 + +GET: `[host]/System/Ping` + +## 请求节点 + +### Items 获取专辑列表 + +GET: `[host]/Users/[UserId]/Items` + +query: + +| 参数名 | 备注 | +| --- | --- | +| GenreIds | 类型ID,可选 | +| SortBy | 排序方式列表,可选值: `Random`, `DateCreated`, `PremiereDate`, `PlayCount`, `DatePlayed`, `SortName`, `CommunityRating` | +| SortOrder | 排序,可选值: `Ascending`, `Descending` | +| IncludeItemTypes | 包含的项目类型,可选值:`MusicAlbum` | +| Recursive | 是否递归查询 | +| Fields | 包含的字段列表,可选值:`SortName`, `BasicSyncInfo`, `ChildCount`, `DateCreated` | +| ImageTypeLimit | 返回的每个图片类型图片数量 | +| EnableImageTypes | 图片类型列表,可选值:`Primary`, `Backdrop`, `Banner`, `Thumb` | +| StartIndex | 起始行数 | +| Limit | 最大数量,可选 | +| artistIds | 歌手 id 列表,可选 | +| SearchTerm | 搜索词,可选 | + +response: + +```json +{ + "Items": [ + { + "Name": "飞驰于你", + "ServerId": "68ca97fe91ff41a08aa2ff3d057a5f75", + "Id": "bb725906dce0e1d5dcbb5e2aab50f590", + "DateCreated": "2024-03-25T09:34:11.7249608Z", + "SortName": "飞驰于你", + "PremiereDate": "2018-01-01T00:00:00.0000000Z", + "ChannelId": null, + "RunTimeTicks": 2441376768, + "ProductionYear": 2018, + "IsFolder": true, + "Type": "MusicAlbum", + "ParentBackdropItemId": "9cb24036603a4bbdaf15891d3f8215b0", + "ParentBackdropImageTags": [ + "17c8f1f8c484ca3f6f9dee5102176e1e" + ], + "UserData": { + "PlaybackPositionTicks": 0, + "PlayCount": 0, + "IsFavorite": false, + "Played": false, + "Key": "bb725906-dce0-e1d5-dcbb-5e2aab50f590" + }, + "ChildCount": 1, + "Artists": [ + "许嵩" + ], + "ArtistItems": [ + { + "Name": "许嵩", + "Id": "9cb24036603a4bbdaf15891d3f8215b0" + } + ], + "AlbumArtist": "许嵩", + "AlbumArtists": [ + { + "Name": "许嵩", + "Id": "9cb24036603a4bbdaf15891d3f8215b0" + } + ], + "ImageTags": { + "Primary": "b9a921b8a933b371c1075b3c9d046352" + }, + "BackdropImageTags": [], + "ImageBlurHashes": { + "Primary": { + "b9a921b8a933b371c1075b3c9d046352": "eH9QXCRj0K%M?HD%oe-;RkIUE2of$*NFNHt7ayR*ofs.%2ayIoofxa" + }, + "Backdrop": { + "17c8f1f8c484ca3f6f9dee5102176e1e": "WTHetT_3.9?bjFt8%hRPofWBRit70L9FMwRjozM{-;IUjZayWXt7" + } + }, + "LocationType": "FileSystem" + } + ], + "TotalRecordCount": 42, + "StartIndex": 0 +} +``` + +### Items/[id] 获取专辑信息 + +GET: `[host]/Users/[UserId]/Items/[id]` + +response: + +```json +{ + "Name": "飞驰于你", + "ServerId": "68ca97fe91ff41a08aa2ff3d057a5f75", + "Id": "bb725906dce0e1d5dcbb5e2aab50f590", + "Etag": "2d38f05eeaa5ac3b0afb97617bc7f30d", + "DateCreated": "2024-03-25T09:34:11.7249608Z", + "CanDelete": true, + "CanDownload": false, + "SortName": "飞驰于你", + "PremiereDate": "2018-01-01T00:00:00.0000000Z", + "ExternalUrls": [], + "Path": "/media/许嵩/飞驰于你", + "EnableMediaSourceDisplay": true, + "ChannelId": null, + "Taglines": [], + "Genres": [], + "CumulativeRunTimeTicks": 2441376768, + "RunTimeTicks": 2441376768, + "PlayAccess": "Full", + "ProductionYear": 2018, + "RemoteTrailers": [], + "ProviderIds": {}, + "IsFolder": true, + "ParentId": "e630c2b62934f5dbab323976e997f90c", + "Type": "MusicAlbum", + "People": [], + "Studios": [], + "GenreItems": [], + "ParentBackdropItemId": "9cb24036603a4bbdaf15891d3f8215b0", + "ParentBackdropImageTags": [ + "17c8f1f8c484ca3f6f9dee5102176e1e" + ], + "LocalTrailerCount": 0, + "UserData": { + "PlaybackPositionTicks": 0, + "PlayCount": 0, + "IsFavorite": false, + "Played": false, + "Key": "bb725906-dce0-e1d5-dcbb-5e2aab50f590" + }, + "RecursiveItemCount": 1, + "ChildCount": 1, + "SpecialFeatureCount": 0, + "DisplayPreferencesId": "f13d7f51d4f1f8b6fcd620855eb88c1e", + "Tags": [], + "PrimaryImageAspectRatio": 1, + "Artists": [ + "许嵩" + ], + "ArtistItems": [ + { + "Name": "许嵩", + "Id": "9cb24036603a4bbdaf15891d3f8215b0" + } + ], + "AlbumArtist": "许嵩", + "AlbumArtists": [ + { + "Name": "许嵩", + "Id": "9cb24036603a4bbdaf15891d3f8215b0" + } + ], + "ImageTags": { + "Primary": "b9a921b8a933b371c1075b3c9d046352" + }, + "BackdropImageTags": [], + "ImageBlurHashes": { + "Primary": { + "b9a921b8a933b371c1075b3c9d046352": "eH9QXCRj0K%M?HD%oe-;RkIUE2of$*NFNHt7ayR*ofs.%2ayIoofxa" + }, + "Backdrop": { + "17c8f1f8c484ca3f6f9dee5102176e1e": "WTHetT_3.9?bjFt8%hRPofWBRit70L9FMwRjozM{-;IUjZayWXt7" + } + }, + "LocationType": "FileSystem", + "LockedFields": [], + "LockData": false +} +``` + +### AlbumArtists 获取专辑艺术家列表 + +GET: `[host]/Artists/AlbumArtists` + +其他格式与歌手列表一致。 + +### Artists 获取歌手列表 + +GET: `[host]/Artists` + +query: + +| 参数名 | 备注 | +| --- | --- | +| SortBy | 排序方式列表,可选值: `Random`, `DateCreated`, `PremiereDate`, `PlayCount`, `DatePlayed`, `SortName`, `CommunityRating` | +| SortOrder | 排序,可选值: `Ascending`, `Descending` | +| Recursive | 是否递归查询 | +| Fields | 包含的字段列表,可选值:`SortName`, `BasicSyncInfo`, `PrimaryImageAspectRatio` | +| ImageTypeLimit | 返回的每个图片类型图片数量 | +| EnableImageTypes | 图片类型列表,可选值:`Primary`, `Backdrop`, `Banner`, `Thumb` | +| StartIndex | 起始行数 | +| Limit | 最大数量,可选 | +| userId | 用户ID | +| SearchTerm | 搜索词,可选 | + +response: + +```json +{ + "Items": [ + { + "Name": "许嵩", + "ServerId": "68ca97fe91ff41a08aa2ff3d057a5f75", + "Id": "9cb24036603a4bbdaf15891d3f8215b0", + "SortName": "许嵩", + "ChannelId": null, + "RunTimeTicks": 47480836096, + "Type": "MusicArtist", + "UserData": { + "PlaybackPositionTicks": 0, + "PlayCount": 0, + "IsFavorite": true, + "Played": false, + "Key": "Artist-Musicbrainz-ca084e75-0f3e-4c05-9f4d-b2714a8c8bb5" + }, + "PrimaryImageAspectRatio": 1, + "ImageTags": { + "Primary": "cd6cc0deefafccfc624ae98786cbcf89" + }, + "BackdropImageTags": [ + "17c8f1f8c484ca3f6f9dee5102176e1e" + ], + "ImageBlurHashes": { + "Backdrop": { + "17c8f1f8c484ca3f6f9dee5102176e1e": "WTHetT_3.9?bjFt8%hRPofWBRit70L9FMwRjozM{-;IUjZayWXt7" + }, + "Primary": { + "cd6cc0deefafccfc624ae98786cbcf89": "eIFE.a-:^6IAxZ56E1.7-;t7snV@RkbHX8~CtRV[RPE1NGaekCxtt7" + } + }, + "LocationType": "FileSystem" + } + ], + "TotalRecordCount": 1, + "StartIndex": 0 +} +``` + +### Items/[id] 获取歌手信息 + +GET: `[host]/Users/[UserId]/Items/[id]` + +response: + +```json +{ + "Name": "许嵩", + "ServerId": "68ca97fe91ff41a08aa2ff3d057a5f75", + "Id": "9cb24036603a4bbdaf15891d3f8215b0", + "Etag": "5c8277637eb996c93c816e3b637b92f2", + "DateCreated": "2024-03-31T00:53:11.0325164Z", + "CanDelete": false, + "CanDownload": false, + "SortName": "许嵩", + "ExternalUrls": [ + { + "Name": "MusicBrainz", + "Url": "https://musicbrainz.org/artist/ca084e75-0f3e-4c05-9f4d-b2714a8c8bb5" + } + ], + "Path": "/config/metadata/artists/许嵩", + "EnableMediaSourceDisplay": true, + "ChannelId": null, + "Taglines": [], + "Genres": [], + "RunTimeTicks": 47480834304, + "PlayAccess": "Full", + "RemoteTrailers": [], + "ProviderIds": { + "MusicBrainzArtist": "ca084e75-0f3e-4c05-9f4d-b2714a8c8bb5" + }, + "ParentId": null, + "Type": "MusicArtist", + "People": [], + "Studios": [], + "GenreItems": [], + "LocalTrailerCount": 0, + "UserData": { + "PlaybackPositionTicks": 0, + "PlayCount": 0, + "IsFavorite": true, + "Played": false, + "Key": "Artist-Musicbrainz-ca084e75-0f3e-4c05-9f4d-b2714a8c8bb5" + }, + "ChildCount": 174, + "SpecialFeatureCount": 0, + "DisplayPreferencesId": "184fdd0115d1192d2c763ca9df5e4ccc", + "Tags": [], + "PrimaryImageAspectRatio": 1, + "ImageTags": { + "Primary": "cd6cc0deefafccfc624ae98786cbcf89" + }, + "BackdropImageTags": [ + "17c8f1f8c484ca3f6f9dee5102176e1e" + ], + "ImageBlurHashes": { + "Backdrop": { + "17c8f1f8c484ca3f6f9dee5102176e1e": "WTHetT_3.9?bjFt8%hRPofWBRit70L9FMwRjozM{-;IUjZayWXt7" + }, + "Primary": { + "cd6cc0deefafccfc624ae98786cbcf89": "eIFE.a-:^6IAxZ56E1.7-;t7snV@RkbHX8~CtRV[RPE1NGaekCxtt7" + } + }, + "LocationType": "FileSystem", + "LockedFields": [], + "SongCount": 132, + "AlbumCount": 42, + "MusicVideoCount": 0, + "LockData": false +} +``` + +### Similar 获取相似歌手 + +GET: `[host]/Artists/[id]/Similar` + +query: + +| 参数名 | 备注 | +| --- | --- | +| limit | 结果数量 | +| Fields | 包含的字段列表,可选值:`SortName`, `BasicSyncInfo`, `PrimaryImageAspectRatio`, `Overview` | + +response: + +```json +{ + "Items": [], + "TotalRecordCount": 0, + "StartIndex": 0 +} +``` + +### Images 图片链接 + +GET: `[host]/Items/[id]/Primary?fillHeight=600&fillWidth=600` + +:::note + +请求头需要添加授权信息。 + +::: + +### Playlists 创建歌单 + +POST: `[host]/Playlists` + +body(`application/json`): + +| 参数名 | 备注 | +| --- | --- | +| Name | 歌单名 | +| Ids | 包含的歌曲ID列表 | +| UserId | 用户名 | +| MediaType | 媒体类型,可选值:`Audio` | + +response: + +```json +{ + "Id": "bea612652ad4b3578c76fa87a13e7fa7" +} +``` + +### Items/[id] 删除歌单 + +DELETE: `[host]/Items/[id]` + +response code: 204 + +### Items 从服务器删除歌曲 + +DELETE: `[host]/Items` + +query: + +| 参数名 | 备注 | +| --- | --- | +| Ids | 包含的歌曲ID列表 | + +:::caution + +危险操作,此接口需要管理员权限。 + +::: + +### Genres 获取全部类型 + +GET: `[host]/Genres` + +query: + +| 参数名 | 备注 | +| --- | --- | +| ParentId | 28e9960207c978c0d9aaefc8ae2d3a79 | +| Recursive | 是否递归查询 | +| userId | 用户ID | +| StartIndex | 起始行数 | +| SortBy | 固定值,`SortName` | +| SortOrder | 固定值,`Ascending` | +| Fields | 结果内容,`PrimaryImageAspectRatio,ItemCounts` | + +response: + +```json +{ + "Items": [ + { + "Name": "Blues", + "ServerId": "9c37aa3250ca4fdcaefd7c5d527c4dc9", + "Id": "df8727bc10d9300f2539c1ade11bca86", + "ChannelId": null, + "Type": "MusicGenre", + "PrimaryImageAspectRatio": 1, + "ImageTags": { + "Primary": "a9cce81ed92265900007e40efc6f103f" + }, + "BackdropImageTags": [], + "ImageBlurHashes": { + "Primary": { + "a9cce81ed92265900007e40efc6f103f": "eDB|4@_2%MWs0L?G?axut8-;s;ogfQRjRP4oNGWBax%M9aWBj[xu-o" + } + }, + "LocationType": "FileSystem", + "MediaType": "Unknown" + }, + { + "Name": "General Chinese Pop", + "ServerId": "9c37aa3250ca4fdcaefd7c5d527c4dc9", + "Id": "db26524cecf61e7877ca36a7ce56e977", + "ChannelId": null, + "Type": "MusicGenre", + "PrimaryImageAspectRatio": 1, + "ImageTags": { + "Primary": "76ead409c6e0e1a6b4aca1b68c3a9f97" + }, + "BackdropImageTags": [], + "ImageBlurHashes": { + "Primary": { + "76ead409c6e0e1a6b4aca1b68c3a9f97": "euH_f9RjfQM{of?HayfQWBoffQfQfQfQfQ~WWBfQRkt7%MoffQt7j[" + } + }, + "LocationType": "FileSystem", + "MediaType": "Unknown" + }, + { + "Name": "国语流行", + "ServerId": "9c37aa3250ca4fdcaefd7c5d527c4dc9", + "Id": "eb1c5035435d9cdae1b1e707b5b10d3e", + "ChannelId": null, + "Type": "MusicGenre", + "PrimaryImageAspectRatio": 1, + "ImageTags": { + "Primary": "b7f4de3edc193f2ed8828cb8723207ae" + }, + "BackdropImageTags": [], + "ImageBlurHashes": { + "Primary": { + "b7f4de3edc193f2ed8828cb8723207ae": "eMP72#D*fQ01~pj[azfQj[RjfQfQfQfQfQWBfQfQofM{odt7fQxuoy" + } + }, + "LocationType": "FileSystem", + "MediaType": "Unknown" + }, + { + "Name": "Pop", + "ServerId": "9c37aa3250ca4fdcaefd7c5d527c4dc9", + "Id": "f1f202f389018ad2c0766af4b0fcb155", + "ChannelId": null, + "Type": "MusicGenre", + "PrimaryImageAspectRatio": 1, + "ImageTags": { + "Primary": "b1b7f3253c1eb7961d690e50119fffe2" + }, + "BackdropImageTags": [], + "ImageBlurHashes": { + "Primary": { + "b1b7f3253c1eb7961d690e50119fffe2": "ecJi^ENHfQEM0g=|oefQt6EL9bt6fQ\u001a|k7azfQafn+WCayfQafxa" + } + }, + "LocationType": "FileSystem", + "MediaType": "Unknown" + }, + { + "Name": "R&B", + "ServerId": "9c37aa3250ca4fdcaefd7c5d527c4dc9", + "Id": "d2b41a81a1122b14418c423b2f3616e9", + "ChannelId": null, + "Type": "MusicGenre", + "PrimaryImageAspectRatio": 1, + "ImageTags": { + "Primary": "8b1af59966f74172e8c53872d75450f2" + }, + "BackdropImageTags": [], + "ImageBlurHashes": { + "Primary": { + "8b1af59966f74172e8c53872d75450f2": "eCDc8ZI[%2x]0fxCxWE1R*IotRx]-;-:xuD*s:IU-oof%gt8t7%gxD" + } + }, + "LocationType": "FileSystem", + "MediaType": "Unknown" + } + ], + "TotalRecordCount": 5, + "StartIndex": 0 +} +``` + +### Lyrics 获取歌词 + +GET: `[host]/Audio/[id]/Lyrics` + +response: + +```json +{ + "Metadata": {}, + "Lyrics": [ + { + "Text": "作词 : G.E.M.邓紫棋", + "Start": 0 + }, + { + "Text": "作曲 : DEE.P/JOHNSON REBECCA ROSE/TE DI/SOL", + "Start": 10000000 + }, + { + "Text": "编曲:Lupo Groinig", + "Start": 43100000 + }, + { + "Text": "监制:Lupo Groinig", + "Start": 53100000 + }, + { + "Text": "说不出说不出一句话", + "Start": 137300000 + }, + { + "Text": "连我自己都很惊讶", + "Start": 163500000 + }, + { + "Text": "这死去的爱", + "Start": 2629600000 + } + ] +} +``` + +### Items 获取歌单列表 + +GET: `[host]/Users/[UserId]/Items` + +query: + +| 参数名 | 备注 | +| --- | --- | +| SortBy | 排序方式列表,可选值: `DateCreated`, `SortName`| +| SortOrder | 排序,可选值: `Ascending`, `Descending` | +| Recursive | 是否递归查询 | +| includeItemTypes | 项目类型,可选值:`Playlist` | +| Fields | 包含的字段列表,可选值:`SortName`, `CanDelete`, `PrimaryImageAspectRatio` | +| mediaTypes | 媒体类型,可选值:`Audio` | +| StartIndex | 起始行数 | +| Limit | 最大数量,可选 | + +response: + +```json +{ + "Items": [ + { + "Name": "test", + "ServerId": "68ca97fe91ff41a08aa2ff3d057a5f75", + "Id": "8fda11ea1088480124aef0e80277054e", + "CanDelete": true, + "SortName": "test", + "ChannelId": null, + "RunTimeTicks": 326515949568, + "IsFolder": true, + "Type": "Playlist", + "UserData": { + "PlaybackPositionTicks": 0, + "PlayCount": 0, + "IsFavorite": false, + "Played": false, + "Key": "8fda11ea-1088-4801-24ae-f0e80277054e" + }, + "ChildCount": 132, + "PrimaryImageAspectRatio": 1, + "ImageTags": { + "Primary": "f775f0319d617c09251a65e80da362ef" + }, + "BackdropImageTags": [], + "ImageBlurHashes": { + "Primary": { + "f775f0319d617c09251a65e80da362ef": "eI9jfgWBfQNG0KM{j[fQj[xufQfQfQfQfQE0j[fQof%MELoLfQs:xZ" + } + }, + "LocationType": "FileSystem", + "MediaType": "Audio" + }, + { + "Name": "信息学3", + "ServerId": "68ca97fe91ff41a08aa2ff3d057a5f75", + "Id": "bea612652ad4b3578c76fa87a13e7fa7", + "CanDelete": true, + "SortName": "信息学0000000003", + "ChannelId": null, + "RunTimeTicks": 0, + "IsFolder": true, + "Type": "Playlist", + "UserData": { + "PlaybackPositionTicks": 0, + "PlayCount": 0, + "IsFavorite": false, + "Played": false, + "Key": "bea61265-2ad4-b357-8c76-fa87a13e7fa7" + }, + "ImageTags": {}, + "BackdropImageTags": [], + "ImageBlurHashes": {}, + "LocationType": "FileSystem", + "MediaType": "Audio" + } + ], + "TotalRecordCount": 2, + "StartIndex": 0 +} +``` + +### Info 获取服务器信息 + +GET: `[host]/System/Info` + +response: + +```json +{ + "OperatingSystemDisplayName": "Linux", + "HasPendingRestart": false, + "IsShuttingDown": false, + "SupportsLibraryMonitor": true, + "WebSocketPortNumber": 8096, + "CompletedInstallations": [], + "CanSelfRestart": false, + "CanLaunchWebBrowser": false, + "ProgramDataPath": "/config", + "WebPath": "/jellyfin/jellyfin-web", + "ItemsByNamePath": "/config/metadata", + "CachePath": "/cache", + "LogPath": "/config/log", + "InternalMetadataPath": "/config/metadata", + "TranscodingTempPath": "/config/transcodes", + "HasUpdateAvailable": false, + "EncoderLocation": "NotFound", + "SystemArchitecture": "X64", + "LocalAddress": "http://172.27.0.2:8096", + "ServerName": "e4941b681250", + "Version": "10.8.13", + "OperatingSystem": "Linux", + "Id": "68ca97fe91ff41a08aa2ff3d057a5f75" +} +``` + +### ScheduledTasks 获取扫描状态 + +GET: `[host]/ScheduledTasks` + +response: + +```json +[ + { + "Name": "Refresh Guide", + "State": "Idle", + "Id": "3339d81856535fbbee58d6d99ec83461", + "LastExecutionResult": { + "StartTimeUtc": "2024-04-18T16:00:54.8238573Z", + "EndTimeUtc": "2024-04-18T16:00:57.8271289Z", + "Status": "Completed", + "Name": "Refresh Guide", + "Key": "RefreshGuide", + "Id": "3339d81856535fbbee58d6d99ec83461" + }, + "Triggers": [ + { + "Type": "IntervalTrigger", + "IntervalTicks": 864000000000 + } + ], + "Description": "Downloads channel information from live tv services.", + "Category": "Live TV", + "IsHidden": true, + "Key": "RefreshGuide" + }, + { + "Name": "TasksRefreshChannels", + "State": "Idle", + "Id": "a27c54259520b2e5c509def68c8f4c45", + "LastExecutionResult": { + "StartTimeUtc": "2024-04-18T16:00:54.7149059Z", + "EndTimeUtc": "2024-04-18T16:00:54.7455027Z", + "Status": "Completed", + "Name": "TasksRefreshChannels", + "Key": "RefreshInternetChannels", + "Id": "a27c54259520b2e5c509def68c8f4c45" + }, + "Triggers": [ + { + "Type": "IntervalTrigger", + "IntervalTicks": 864000000000 + } + ], + "Description": "TasksRefreshChannelsDescription", + "Category": "互联网频道", + "IsHidden": true, + "Key": "RefreshInternetChannels" + }, + { + "Name": "下载缺少的字幕", + "State": "Idle", + "Id": "2c66a88bca43e565d7f8099f825478f1", + "LastExecutionResult": { + "StartTimeUtc": "2024-04-18T15:00:56.4262005Z", + "EndTimeUtc": "2024-04-18T15:00:56.4262914Z", + "Status": "Completed", + "Name": "下载缺少的字幕", + "Key": "DownloadSubtitles", + "Id": "2c66a88bca43e565d7f8099f825478f1" + }, + "Triggers": [ + { + "Type": "IntervalTrigger", + "IntervalTicks": 864000000000 + } + ], + "Description": "根据元数据设置在互联网上搜索缺少的字幕。", + "Category": "媒体库", + "IsHidden": false, + "Key": "DownloadSubtitles" + }, + { + "Name": "优化数据库", + "State": "Idle", + "Id": "31de9ce83b9223d338c77b1a635e144b", + "LastExecutionResult": { + "StartTimeUtc": "2024-04-18T16:01:01.5741968Z", + "EndTimeUtc": "2024-04-18T16:01:01.9621083Z", + "Status": "Completed", + "Name": "优化数据库", + "Key": "OptimizeDatabaseTask", + "Id": "31de9ce83b9223d338c77b1a635e144b" + }, + "Triggers": [ + { + "Type": "IntervalTrigger", + "IntervalTicks": 864000000000 + } + ], + "Description": "压缩数据库并优化可用空间,在扫描库或执行其他数据库修改后运行此任务可能会提高性能。", + "Category": "维护", + "IsHidden": false, + "Key": "OptimizeDatabaseTask" + }, + { + "Name": "关键帧提取器", + "State": "Idle", + "Id": "f302d80f31bcacf76f979d277d448581", + "Triggers": [], + "Description": "从视频文件中提取关键帧以创建更准确的HLS播放列表。这项任务可能需要很长时间。", + "Category": "媒体库", + "IsHidden": false, + "Key": "KeyframeExtraction" + }, + { + "Name": "刷新人员", + "State": "Idle", + "Id": "866456ed0d44e15468124ce33d85961e", + "LastExecutionResult": { + "StartTimeUtc": "2024-04-12T16:00:55.7846583Z", + "EndTimeUtc": "2024-04-12T16:00:55.7856805Z", + "Status": "Completed", + "Name": "刷新人员", + "Key": "RefreshPeople", + "Id": "866456ed0d44e15468124ce33d85961e" + }, + "Triggers": [ + { + "Type": "IntervalTrigger", + "IntervalTicks": 6048000000000 + } + ], + "Description": "更新媒体库中演员和导演的元数据。", + "Category": "媒体库", + "IsHidden": false, + "Key": "RefreshPeople" + }, + { + "Name": "扫描媒体库", + "State": "Idle", + "Id": "7738148ffcd07979c7ceb148e06b3aed", + "LastExecutionResult": { + "StartTimeUtc": "2024-04-18T21:01:51.1025101Z", + "EndTimeUtc": "2024-04-18T21:01:52.1775033Z", + "Status": "Completed", + "Name": "扫描媒体库", + "Key": "RefreshLibrary", + "Id": "7738148ffcd07979c7ceb148e06b3aed" + }, + "Triggers": [ + { + "Type": "IntervalTrigger", + "IntervalTicks": 432000000000 + } + ], + "Description": "扫描你的媒体库以获取新文件并刷新元数据。", + "Category": "媒体库", + "IsHidden": false, + "Key": "RefreshLibrary" + }, + { + "Name": "提取章节图片", + "State": "Idle", + "Id": "4e6637c832ed644d1af3370a2506e80a", + "LastExecutionResult": { + "StartTimeUtc": "2024-04-19T02:00:00.0269008Z", + "EndTimeUtc": "2024-04-19T02:00:00.1078383Z", + "Status": "Completed", + "Name": "提取章节图片", + "Key": "RefreshChapterImages", + "Id": "4e6637c832ed644d1af3370a2506e80a" + }, + "Triggers": [ + { + "Type": "DailyTrigger", + "TimeOfDayTicks": 72000000000, + "MaxRuntimeTicks": 144000000000 + } + ], + "Description": "为包含章节的视频提取缩略图。", + "Category": "媒体库", + "IsHidden": false, + "Key": "RefreshChapterImages" + }, + { + "Name": "更新插件", + "State": "Idle", + "Id": "f9b057c054e9e6daee4a88ffd146a403", + "LastExecutionResult": { + "StartTimeUtc": "2024-04-18T15:00:53.891681Z", + "EndTimeUtc": "2024-04-18T15:00:57.2248519Z", + "Status": "Completed", + "Name": "更新插件", + "Key": "PluginUpdates", + "Id": "f9b057c054e9e6daee4a88ffd146a403" + }, + "Triggers": [ + { + "Type": "StartupTrigger" + }, + { + "Type": "IntervalTrigger", + "IntervalTicks": 864000000000 + } + ], + "Description": "为已设置为自动更新的插件下载和安装更新。", + "Category": "应用程序", + "IsHidden": false, + "Key": "PluginUpdates" + }, + { + "Name": "清理日志目录", + "State": "Idle", + "Id": "1c8ede62c521bea0bf851344f5b8ca40", + "LastExecutionResult": { + "StartTimeUtc": "2024-04-18T15:00:55.6453189Z", + "EndTimeUtc": "2024-04-18T15:00:55.6581013Z", + "Status": "Completed", + "Name": "清理日志目录", + "Key": "CleanLogFiles", + "Id": "1c8ede62c521bea0bf851344f5b8ca40" + }, + "Triggers": [ + { + "Type": "IntervalTrigger", + "IntervalTicks": 864000000000 + } + ], + "Description": "删除存在超过 3 天的的日志文件。", + "Category": "维护", + "IsHidden": false, + "Key": "CleanLogFiles" + }, + { + "Name": "清理程序日志", + "State": "Idle", + "Id": "b461ef918ab28520928183e794350e3c", + "Triggers": [], + "Description": "删除早于设置时间的活动日志条目。", + "Category": "维护", + "IsHidden": false, + "Key": "CleanActivityLog" + }, + { + "Name": "清理缓存目录", + "State": "Idle", + "Id": "241d4fcb19a1d557ee62428e411da609", + "LastExecutionResult": { + "StartTimeUtc": "2024-04-18T15:00:59.0776557Z", + "EndTimeUtc": "2024-04-18T15:00:59.2224953Z", + "Status": "Completed", + "Name": "清理缓存目录", + "Key": "DeleteCacheFiles", + "Id": "241d4fcb19a1d557ee62428e411da609" + }, + "Triggers": [ + { + "Type": "IntervalTrigger", + "IntervalTicks": 864000000000 + } + ], + "Description": "删除系统不再需要的缓存文件。", + "Category": "维护", + "IsHidden": false, + "Key": "DeleteCacheFiles" + }, + { + "Name": "清理转码目录", + "State": "Idle", + "Id": "7d8088c10902f1bf4072ded42437bcfb", + "LastExecutionResult": { + "StartTimeUtc": "2024-04-18T15:00:55.9292122Z", + "EndTimeUtc": "2024-04-18T15:00:55.9294865Z", + "Status": "Completed", + "Name": "清理转码目录", + "Key": "DeleteTranscodeFiles", + "Id": "7d8088c10902f1bf4072ded42437bcfb" + }, + "Triggers": [ + { + "Type": "IntervalTrigger", + "IntervalTicks": 864000000000 + } + ], + "Description": "删除存在超过 1 天的转码文件。", + "Category": "维护", + "IsHidden": false, + "Key": "DeleteTranscodeFiles" + } +] +``` + +:::caution + +此接口需要管理员权限才能访问! + +::: + +### PlayedItems/[id] 滚动播放记录 + +**播放开始** + +POST:`[host]/Sessions/Playing` + +body: + +| 参数名 | 备注 | +| --- | --- | +| ItemId | 歌曲ID | +| PlaySessionId | 会话ID | +| PositionTicks | 播放进度 | +| IsPaused | 是否暂停 | +| PlaybackRate | 播放速度 | +| PlayMethod | 播放方式,可选值:`Transcode`, `DirectPlay`, `DirectStream` | + +**播放进度** + +POST:`[host]/Sessions/Playing/Progress` + +body: + +| 参数名 | 备注 | +| --- | --- | +| ItemId | 歌曲ID | +| PlaySessionId | 会话ID | +| PositionTicks | 播放进度 | +| IsPaused | 是否暂停 | +| PlaybackRate | 播放速度 | +| PlayMethod | 播放方式,可选值:`Transcode`, `DirectPlay`, `DirectStream` | + +Emby 文档中对播放进度的报告时机说明如下: + +- 每 10 秒自动报告一次(用于校准服务器上的自动进度增量) +- 在用户于播放器进行任何交互后立即进行 + +报告进度的原因如下,音流仅会对勾选的事件报告进度: + +- [x] TimeUpdate 时间更新 +- [x] Pause 暂停 +- [x] Unpause 取消暂停 +- [ ] VolumeChange 音量变化 +- [ ] RepeatModeChange 重复模式更改 +- [ ] AudioTrackChange 音轨更改 +- [ ] SubtitleTrackChange 字幕更改 +- [ ] PlaylistItemMove 播放列表项移动 +- [ ] PlaylistItemRemove 播放列表项删除 +- [ ] PlaylistItemAdd 播放列表项添加 +- [ ] QualityChange 质量变革 +- [ ] SubtitleOffsetChange 字幕偏移 +- [x] PlaybackRateChange 播放速度改变 + +**标记为已播放** + +> 向 Emby 上报**播放开始**状态时,播放次数已经自动+1,因此无需使用此接口。 + +POST: `[host]/Users/[UserId]/PlayedItems/[id]` + +query: + +| 参数名 | 备注 | +| --- | --- | +| datePlayed | 播放时间,ISO8601 | + +### Similar 获取相似歌曲 + +GET: `[host]/Items/[id]/Similar` + +query: + +| 参数名 | 备注 | +| --- | --- | +| Limit | 结果数量 | +| Fields | 包含的字段列表,可选值:`AudioInfo`, `SortName`, `MediaSources`, `DateCreated` | +| userId | 用户ID | + +response: + +```json +{ + "Items": [ + { + "Name": "半城烟沙", + "ServerId": "68ca97fe91ff41a08aa2ff3d057a5f75", + "Id": "a46bd635fb5f64d007180788b0e36cd6", + "DateCreated": "2024-03-21T10:52:08.2380753Z", + "SortName": "0001 - 半城烟沙", + "PremiereDate": "2010-01-01T00:00:00.0000000Z", + "MediaSources": [ + { + "Protocol": "File", + "Id": "a46bd635fb5f64d007180788b0e36cd6", + "Path": "/media/许嵩/半城烟沙/半城烟沙 - 许嵩.mp3", + "Type": "Default", + "Container": "mp3", + "Size": 11818653, + "Name": "半城烟沙 - 许嵩", + "IsRemote": false, + "ETag": "a112f50492ca04c9115eceb9ae498883", + "RunTimeTicks": 2946351104, + "ReadAtNativeFramerate": false, + "IgnoreDts": false, + "IgnoreIndex": false, + "GenPtsInput": false, + "SupportsTranscoding": true, + "SupportsDirectStream": true, + "SupportsDirectPlay": true, + "IsInfiniteStream": false, + "RequiresOpening": false, + "RequiresClosing": false, + "RequiresLooping": false, + "SupportsProbing": true, + "MediaStreams": [ + { + "Codec": "mp3", + "TimeBase": "1/14112000", + "DisplayTitle": "MP3 - Stereo", + "IsInterlaced": false, + "ChannelLayout": "stereo", + "BitRate": 320000, + "Channels": 2, + "SampleRate": 44100, + "IsDefault": false, + "IsForced": false, + "Type": "Audio", + "Index": 0, + "IsExternal": false, + "IsTextSubtitleStream": false, + "SupportsExternalStream": false, + "Level": 0 + }, + { + "Codec": "mjpeg", + "ColorSpace": "bt470bg", + "Comment": "Cover (front)", + "TimeBase": "1/90000", + "IsInterlaced": false, + "BitDepth": 8, + "RefFrames": 1, + "IsDefault": false, + "IsForced": false, + "Height": 500, + "Width": 500, + "RealFrameRate": 90000, + "Profile": "Baseline", + "Type": "EmbeddedImage", + "AspectRatio": "1:1", + "Index": 1, + "IsExternal": false, + "IsTextSubtitleStream": false, + "SupportsExternalStream": false, + "PixelFormat": "yuvj420p", + "Level": -99 + } + ], + "MediaAttachments": [], + "Formats": [], + "Bitrate": 320902, + "RequiredHttpHeaders": {}, + "DefaultAudioStreamIndex": 0 + } + ], + "ChannelId": null, + "RunTimeTicks": 2946351104, + "ProductionYear": 2010, + "IndexNumber": 1, + "IsFolder": false, + "Type": "Audio", + "ParentBackdropItemId": "9cb24036603a4bbdaf15891d3f8215b0", + "ParentBackdropImageTags": [ + "17c8f1f8c484ca3f6f9dee5102176e1e" + ], + "UserData": { + "PlaybackPositionTicks": 0, + "PlayCount": 0, + "IsFavorite": false, + "Played": false, + "Key": "半城烟沙 - 许嵩" + }, + "Artists": [ + "许嵩" + ], + "ArtistItems": [ + { + "Name": "许嵩", + "Id": "9cb24036603a4bbdaf15891d3f8215b0" + } + ], + "Album": "半城烟沙", + "AlbumId": "2d5cef1257c1614eeeef82c35debb3af", + "AlbumPrimaryImageTag": "bb5b481820e918f1bbaa89f963028b3d", + "AlbumArtist": "许嵩", + "AlbumArtists": [ + { + "Name": "许嵩", + "Id": "9cb24036603a4bbdaf15891d3f8215b0" + } + ], + "ImageTags": { + "Primary": "f4a103853e2272c22c8bdea78b154f25" + }, + "BackdropImageTags": [], + "ImageBlurHashes": { + "Primary": { + "f4a103853e2272c22c8bdea78b154f25": "eH9QXCRj0K%M?HD%oe-;RkIUE2of$*NFNHt7ayR*ofs.%2ayIoofxa", + "bb5b481820e918f1bbaa89f963028b3d": "eH9QXCRj0K%M?HD%oe-;RkIUE2of$*NFNHt7ayR*ofs.%2ayIoofxa" + }, + "Backdrop": { + "17c8f1f8c484ca3f6f9dee5102176e1e": "WTHetT_3.9?bjFt8%hRPofWBRit70L9FMwRjozM{-;IUjZayWXt7" + } + }, + "LocationType": "FileSystem", + "MediaType": "Audio" + } + ], + "TotalRecordCount": 1, + "StartIndex": 0 +} +``` + +### Items 获取歌曲列表 + +GET: `[host]/Users/[UserId]/Items` + +query: + +| 参数名 | 备注 | +| --- | --- | +| SortBy | 排序方式列表,可选值: `Random`, `DateCreated`, `PremiereDate`, `PlayCount`, `DatePlayed`, `SortName`, `CommunityRating` | +| SortOrder | 排序,可选值: `Ascending`, `Descending` | +| IncludeItemTypes | 包含的项目类型,可选值:`Audio` | +| Recursive | 是否递归查询 | +| Fields | 包含的字段列表,可选值:`SortName`, `MediaSources`, `AudioInfo`, `DateCreated` | +| ImageTypeLimit | 返回的每个图片类型图片数量 | +| EnableImageTypes | 图片类型列表,可选值:`Primary` | +| StartIndex | 起始行数 | +| Limit | 最大数量,可选 | +| isFavorite | 收藏状态,可选 | +| SearchTerm | 搜索词,可选 | +| Years | 发行年份列表,可选 | + +response: + +```json +{ + "Items": [ + { + "Name": "Play With Style", + "ServerId": "68ca97fe91ff41a08aa2ff3d057a5f75", + "Id": "39de54ec99682f60e77133bd51759673", + "DateCreated": "2024-03-21T10:50:57.4570251Z", + "SortName": "0001 - 0007 - Play With Style", + "PremiereDate": "2012-01-01T00:00:00.0000000Z", + "MediaSources": [ + { + "Protocol": "File", + "Id": "39de54ec99682f60e77133bd51759673", + "Path": "/media/许嵩/梦游计/Play With Style - 许嵩.flac", + "Type": "Default", + "Container": "flac", + "Size": 19795534, + "Name": "Play With Style - 许嵩", + "IsRemote": false, + "ETag": "6054c45b3db258b551c8bc6f300bc5ba", + "RunTimeTicks": 1750671232, + "ReadAtNativeFramerate": false, + "IgnoreDts": false, + "IgnoreIndex": false, + "GenPtsInput": false, + "SupportsTranscoding": true, + "SupportsDirectStream": true, + "SupportsDirectPlay": true, + "IsInfiniteStream": false, + "RequiresOpening": false, + "RequiresClosing": false, + "RequiresLooping": false, + "SupportsProbing": true, + "MediaStreams": [ + { + "Codec": "flac", + "TimeBase": "1/44100", + "DisplayTitle": "FLAC - Stereo", + "IsInterlaced": false, + "ChannelLayout": "stereo", + "BitRate": 904591, + "BitDepth": 16, + "Channels": 2, + "SampleRate": 44100, + "IsDefault": false, + "IsForced": false, + "Type": "Audio", + "Index": 0, + "IsExternal": false, + "IsTextSubtitleStream": false, + "SupportsExternalStream": false, + "Level": 0 + }, + { + "Codec": "mjpeg", + "ColorSpace": "bt470bg", + "Comment": "Other", + "TimeBase": "1/90000", + "IsInterlaced": false, + "BitDepth": 8, + "RefFrames": 1, + "IsDefault": false, + "IsForced": false, + "Height": 500, + "Width": 500, + "RealFrameRate": 90000, + "Profile": "Baseline", + "Type": "EmbeddedImage", + "AspectRatio": "1:1", + "Index": 1, + "IsExternal": false, + "IsTextSubtitleStream": false, + "SupportsExternalStream": false, + "PixelFormat": "yuvj420p", + "Level": -99 + } + ], + "MediaAttachments": [], + "Formats": [], + "Bitrate": 904591, + "RequiredHttpHeaders": {}, + "DefaultAudioStreamIndex": 0 + } + ], + "ChannelId": null, + "RunTimeTicks": 1750671232, + "ProductionYear": 2012, + "IndexNumber": 7, + "ParentIndexNumber": 1, + "IsFolder": false, + "Type": "Audio", + "UserData": { + "PlaybackPositionTicks": 0, + "PlayCount": 0, + "IsFavorite": false, + "Played": false, + "Key": "许嵩-梦游计-0001-0007Play With Style" + }, + "Artists": [ + "许嵩" + ], + "ArtistItems": [ + { + "Name": "许嵩", + "Id": "9cb24036603a4bbdaf15891d3f8215b0" + } + ], + "Album": "梦游计", + "AlbumId": "c1037c12ede81c855bbf38a4846248f9", + "AlbumPrimaryImageTag": "ac75515d40f6197331cc7a62f990c255", + "AlbumArtist": "许嵩", + "AlbumArtists": [ + { + "Name": "许嵩", + "Id": "9cb24036603a4bbdaf15891d3f8215b0" + } + ], + "ImageTags": { + "Primary": "3efca2f6e537a66851e786453858e744" + }, + "ImageBlurHashes": { + "Primary": { + "3efca2f6e537a66851e786453858e744": "eH9QXCRj0K%M?HD%oe-;RkIUE2of$*NFNHt7ayR*ofs.%2ayIoofxa", + "ac75515d40f6197331cc7a62f990c255": "eH9QXCRj0K%M?HD%oe-;RkIUE2of$*NFNHt7ayR*ofs.%2ayIoofxa" + } + }, + "LocationType": "FileSystem", + "MediaType": "Audio" + } + ], + "TotalRecordCount": 132, + "StartIndex": 0 +} +``` + +### Items/[id] 获取歌曲信息 + +GET: `[host]/Users/[UserId]/Items/[id]` + +response: + +```json +{ + "Name": "Play With Style", + "ServerId": "68ca97fe91ff41a08aa2ff3d057a5f75", + "Id": "39de54ec99682f60e77133bd51759673", + "Etag": "87d4beea009b1b89fed5089433b47e5c", + "DateCreated": "2024-03-21T10:50:57.4570251Z", + "CanDelete": true, + "CanDownload": true, + "SortName": "0001 - 0007 - Play With Style", + "PremiereDate": "2012-01-01T00:00:00.0000000Z", + "ExternalUrls": [], + "MediaSources": [ + { + "Protocol": "File", + "Id": "39de54ec99682f60e77133bd51759673", + "Path": "/media/许嵩/梦游计/Play With Style - 许嵩.flac", + "Type": "Default", + "Container": "flac", + "Size": 19795534, + "Name": "Play With Style - 许嵩", + "IsRemote": false, + "ETag": "6054c45b3db258b551c8bc6f300bc5ba", + "RunTimeTicks": 1750671232, + "ReadAtNativeFramerate": false, + "IgnoreDts": false, + "IgnoreIndex": false, + "GenPtsInput": false, + "SupportsTranscoding": true, + "SupportsDirectStream": true, + "SupportsDirectPlay": true, + "IsInfiniteStream": false, + "RequiresOpening": false, + "RequiresClosing": false, + "RequiresLooping": false, + "SupportsProbing": true, + "MediaStreams": [ + { + "Codec": "flac", + "TimeBase": "1/44100", + "DisplayTitle": "FLAC - Stereo", + "IsInterlaced": false, + "ChannelLayout": "stereo", + "BitRate": 904591, + "BitDepth": 16, + "Channels": 2, + "SampleRate": 44100, + "IsDefault": false, + "IsForced": false, + "Type": "Audio", + "Index": 0, + "IsExternal": false, + "IsTextSubtitleStream": false, + "SupportsExternalStream": false, + "Level": 0 + }, + { + "Codec": "mjpeg", + "ColorSpace": "bt470bg", + "Comment": "Other", + "TimeBase": "1/90000", + "IsInterlaced": false, + "BitDepth": 8, + "RefFrames": 1, + "IsDefault": false, + "IsForced": false, + "Height": 500, + "Width": 500, + "RealFrameRate": 90000, + "Profile": "Baseline", + "Type": "EmbeddedImage", + "AspectRatio": "1:1", + "Index": 1, + "IsExternal": false, + "IsTextSubtitleStream": false, + "SupportsExternalStream": false, + "PixelFormat": "yuvj420p", + "Level": -99 + } + ], + "MediaAttachments": [], + "Formats": [], + "Bitrate": 904591, + "RequiredHttpHeaders": {}, + "DefaultAudioStreamIndex": 0 + } + ], + "Path": "/media/许嵩/梦游计/Play With Style - 许嵩.flac", + "EnableMediaSourceDisplay": true, + "ChannelId": null, + "Taglines": [], + "Genres": [], + "RunTimeTicks": 1750671232, + "PlayAccess": "Full", + "ProductionYear": 2012, + "IndexNumber": 7, + "ParentIndexNumber": 1, + "RemoteTrailers": [], + "ProviderIds": {}, + "IsFolder": false, + "ParentId": "c1037c12ede81c855bbf38a4846248f9", + "Type": "Audio", + "People": [], + "Studios": [], + "GenreItems": [], + "ParentBackdropItemId": "9cb24036603a4bbdaf15891d3f8215b0", + "ParentBackdropImageTags": [ + "17c8f1f8c484ca3f6f9dee5102176e1e" + ], + "LocalTrailerCount": 0, + "UserData": { + "PlaybackPositionTicks": 0, + "PlayCount": 0, + "IsFavorite": false, + "Played": false, + "Key": "许嵩-梦游计-0001-0007Play With Style" + }, + "SpecialFeatureCount": 0, + "DisplayPreferencesId": "61bba315f137702baa296a1c417faada", + "Tags": [], + "PrimaryImageAspectRatio": 1, + "Artists": [ + "许嵩" + ], + "ArtistItems": [ + { + "Name": "许嵩", + "Id": "9cb24036603a4bbdaf15891d3f8215b0" + } + ], + "Album": "梦游计", + "AlbumId": "c1037c12ede81c855bbf38a4846248f9", + "AlbumPrimaryImageTag": "ac75515d40f6197331cc7a62f990c255", + "AlbumArtist": "许嵩", + "AlbumArtists": [ + { + "Name": "许嵩", + "Id": "9cb24036603a4bbdaf15891d3f8215b0" + } + ], + "MediaStreams": [ + { + "Codec": "flac", + "TimeBase": "1/44100", + "DisplayTitle": "FLAC - Stereo", + "IsInterlaced": false, + "ChannelLayout": "stereo", + "BitRate": 904591, + "BitDepth": 16, + "Channels": 2, + "SampleRate": 44100, + "IsDefault": false, + "IsForced": false, + "Type": "Audio", + "Index": 0, + "IsExternal": false, + "IsTextSubtitleStream": false, + "SupportsExternalStream": false, + "Level": 0 + }, + { + "Codec": "mjpeg", + "ColorSpace": "bt470bg", + "Comment": "Other", + "TimeBase": "1/90000", + "IsInterlaced": false, + "BitDepth": 8, + "RefFrames": 1, + "IsDefault": false, + "IsForced": false, + "Height": 500, + "Width": 500, + "RealFrameRate": 90000, + "Profile": "Baseline", + "Type": "EmbeddedImage", + "AspectRatio": "1:1", + "Index": 1, + "IsExternal": false, + "IsTextSubtitleStream": false, + "SupportsExternalStream": false, + "PixelFormat": "yuvj420p", + "Level": -99 + } + ], + "ImageTags": { + "Primary": "3efca2f6e537a66851e786453858e744" + }, + "BackdropImageTags": [], + "ImageBlurHashes": { + "Primary": { + "3efca2f6e537a66851e786453858e744": "eH9QXCRj0K%M?HD%oe-;RkIUE2of$*NFNHt7ayR*ofs.%2ayIoofxa", + "ac75515d40f6197331cc7a62f990c255": "eH9QXCRj0K%M?HD%oe-;RkIUE2of$*NFNHt7ayR*ofs.%2ayIoofxa" + }, + "Backdrop": { + "17c8f1f8c484ca3f6f9dee5102176e1e": "WTHetT_3.9?bjFt8%hRPofWBRit70L9FMwRjozM{-;IUjZayWXt7" + } + }, + "LocationType": "FileSystem", + "MediaType": "Audio", + "LockedFields": [], + "LockData": false +} +``` + +### Items 获取专辑中的歌曲 + +GET: `[host]/Users/[UserId]/Items` + +query: + +| 参数名 | 备注 | +| --- | --- | +| SortBy | 排序方式列表,可选值: `ParentIndexNumber`, `IndexNumber`, `SortName` | +| Fields | 包含的字段列表,可选值:`ItemCounts`, `PrimaryImageAspectRatio`, `BasicSyncInfo`, `CanDelete`, `MediaSourceCount`, `MediaSources`, `ProductionYear` | +| ParentId | 专辑ID | + +response: + +```json +{ + "Items": [ + { + "Name": "飞驰于你", + "ServerId": "68ca97fe91ff41a08aa2ff3d057a5f75", + "Id": "487267c2b069ccb9b5a345d93bb82fe3", + "CanDelete": true, + "PremiereDate": "2018-01-01T00:00:00.0000000Z", + "MediaSources": [ + { + "Protocol": "File", + "Id": "487267c2b069ccb9b5a345d93bb82fe3", + "Path": "/media/许嵩/飞驰于你/飞驰于你 - 许嵩.flac", + "Type": "Default", + "Container": "flac", + "Size": 31228248, + "Name": "飞驰于你 - 许嵩", + "IsRemote": false, + "ETag": "5c149de4705594742c9784ae3aa52785", + "RunTimeTicks": 2441376768, + "ReadAtNativeFramerate": false, + "IgnoreDts": false, + "IgnoreIndex": false, + "GenPtsInput": false, + "SupportsTranscoding": true, + "SupportsDirectStream": true, + "SupportsDirectPlay": true, + "IsInfiniteStream": false, + "RequiresOpening": false, + "RequiresClosing": false, + "RequiresLooping": false, + "SupportsProbing": true, + "MediaStreams": [ + { + "Codec": "flac", + "TimeBase": "1/44100", + "DisplayTitle": "FLAC - Stereo", + "IsInterlaced": false, + "ChannelLayout": "stereo", + "BitRate": 1023299, + "BitDepth": 16, + "Channels": 2, + "SampleRate": 44100, + "IsDefault": false, + "IsForced": false, + "Type": "Audio", + "Index": 0, + "IsExternal": false, + "IsTextSubtitleStream": false, + "SupportsExternalStream": false, + "Level": 0 + }, + { + "Codec": "mjpeg", + "ColorSpace": "bt470bg", + "Comment": "Other", + "TimeBase": "1/90000", + "IsInterlaced": false, + "BitDepth": 8, + "RefFrames": 1, + "IsDefault": false, + "IsForced": false, + "Height": 500, + "Width": 500, + "RealFrameRate": 90000, + "Profile": "Baseline", + "Type": "EmbeddedImage", + "AspectRatio": "1:1", + "Index": 1, + "IsExternal": false, + "IsTextSubtitleStream": false, + "SupportsExternalStream": false, + "PixelFormat": "yuvj420p", + "Level": -99 + } + ], + "MediaAttachments": [], + "Formats": [], + "Bitrate": 1023299, + "RequiredHttpHeaders": {}, + "DefaultAudioStreamIndex": 0 + } + ], + "ChannelId": null, + "RunTimeTicks": 2441376768, + "ProductionYear": 2018, + "IndexNumber": 1, + "ParentIndexNumber": 1, + "IsFolder": false, + "Type": "Audio", + "ParentBackdropItemId": "9cb24036603a4bbdaf15891d3f8215b0", + "ParentBackdropImageTags": [ + "17c8f1f8c484ca3f6f9dee5102176e1e" + ], + "UserData": { + "PlaybackPositionTicks": 0, + "PlayCount": 0, + "IsFavorite": false, + "Played": false, + "Key": "许嵩-飞驰于你-0001-0001飞驰于你" + }, + "PrimaryImageAspectRatio": 1, + "Artists": [ + "许嵩" + ], + "ArtistItems": [ + { + "Name": "许嵩", + "Id": "9cb24036603a4bbdaf15891d3f8215b0" + } + ], + "Album": "飞驰于你", + "AlbumId": "bb725906dce0e1d5dcbb5e2aab50f590", + "AlbumPrimaryImageTag": "b9a921b8a933b371c1075b3c9d046352", + "AlbumArtist": "许嵩", + "AlbumArtists": [ + { + "Name": "许嵩", + "Id": "9cb24036603a4bbdaf15891d3f8215b0" + } + ], + "ImageTags": { + "Primary": "c1a904073873e3cdc4e8c0fcfc91a933" + }, + "BackdropImageTags": [], + "ImageBlurHashes": { + "Primary": { + "c1a904073873e3cdc4e8c0fcfc91a933": "eH9QXCRj0K%M?HD%oe-;RkIUE2of$*NFNHt7ayR*ofs.%2ayIoofxa", + "b9a921b8a933b371c1075b3c9d046352": "eH9QXCRj0K%M?HD%oe-;RkIUE2of$*NFNHt7ayR*ofs.%2ayIoofxa" + }, + "Backdrop": { + "17c8f1f8c484ca3f6f9dee5102176e1e": "WTHetT_3.9?bjFt8%hRPofWBRit70L9FMwRjozM{-;IUjZayWXt7" + } + }, + "LocationType": "FileSystem", + "MediaType": "Audio" + } + ], + "TotalRecordCount": 1, + "StartIndex": 0 +} +``` + +### Items 获取歌手名下歌曲 + +GET: `[host]/Users/[UserId]/Items` + +| 参数名 | 备注 | +| --- | --- | +| SortBy | 排序方式列表,可选值: `CommunityRating`, `SortName` | +| SortOrder | 排序,可选值: `Ascending`, `Descending` | +| IncludeItemTypes | 包含的项目类型,可选值:`Audio` | +| Recursive | 是否递归查询 | +| Fields | 包含的字段列表,可选值:`SortName`, `MediaSources`, `PrimaryImageAspectRatio`, `BasicSyncInfo`, `ProductionYear` | +| ImageTypeLimit | 返回的每个图片类型图片数量 | +| EnableImageTypes | 图片类型列表,可选值:`Primary` | +| StartIndex | 起始行数 | +| Limit | 最大数量,可选 | +| artists | 歌手名列表 | + +### Items 获取歌单中的歌曲 + +GET: `[host]/Playlists/[id]/Items` + +query: + +| 参数名 | 备注 | +| --- | --- | +| fields | 包含的属性列表,可选值: `SortName`, `CanDelete`, `MediaSources`, `DateCreated`, `ProductionYear` | +| userId | 用户ID | + +response: + +```json +{ + "Items": [ + { + "Name": "飞驰于你", + "ServerId": "68ca97fe91ff41a08aa2ff3d057a5f75", + "Id": "487267c2b069ccb9b5a345d93bb82fe3", + "PlaylistItemId": "857c1587c25342499d4723c72ca2b759", + "DateCreated": "2024-03-21T10:54:53.3621923Z", + "CanDelete": true, + "SortName": "0001 - 0001 - 飞驰于你", + "PremiereDate": "2018-01-01T00:00:00.0000000Z", + "MediaSources": [ + { + "Protocol": "File", + "Id": "487267c2b069ccb9b5a345d93bb82fe3", + "Path": "/media/许嵩/飞驰于你/飞驰于你 - 许嵩.flac", + "Type": "Default", + "Container": "flac", + "Size": 31228248, + "Name": "飞驰于你 - 许嵩", + "IsRemote": false, + "ETag": "5c149de4705594742c9784ae3aa52785", + "RunTimeTicks": 2441376768, + "ReadAtNativeFramerate": false, + "IgnoreDts": false, + "IgnoreIndex": false, + "GenPtsInput": false, + "SupportsTranscoding": true, + "SupportsDirectStream": true, + "SupportsDirectPlay": true, + "IsInfiniteStream": false, + "RequiresOpening": false, + "RequiresClosing": false, + "RequiresLooping": false, + "SupportsProbing": true, + "MediaStreams": [ + { + "Codec": "flac", + "TimeBase": "1/44100", + "DisplayTitle": "FLAC - Stereo", + "IsInterlaced": false, + "ChannelLayout": "stereo", + "BitRate": 1023299, + "BitDepth": 16, + "Channels": 2, + "SampleRate": 44100, + "IsDefault": false, + "IsForced": false, + "Type": "Audio", + "Index": 0, + "IsExternal": false, + "IsTextSubtitleStream": false, + "SupportsExternalStream": false, + "Level": 0 + }, + { + "Codec": "mjpeg", + "ColorSpace": "bt470bg", + "Comment": "Other", + "TimeBase": "1/90000", + "IsInterlaced": false, + "BitDepth": 8, + "RefFrames": 1, + "IsDefault": false, + "IsForced": false, + "Height": 500, + "Width": 500, + "RealFrameRate": 90000, + "Profile": "Baseline", + "Type": "EmbeddedImage", + "AspectRatio": "1:1", + "Index": 1, + "IsExternal": false, + "IsTextSubtitleStream": false, + "SupportsExternalStream": false, + "PixelFormat": "yuvj420p", + "Level": -99 + } + ], + "MediaAttachments": [], + "Formats": [], + "Bitrate": 1023299, + "RequiredHttpHeaders": {}, + "DefaultAudioStreamIndex": 0 + } + ], + "ChannelId": null, + "RunTimeTicks": 2441376768, + "ProductionYear": 2018, + "IndexNumber": 1, + "ParentIndexNumber": 1, + "IsFolder": false, + "Type": "Audio", + "ParentBackdropItemId": "9cb24036603a4bbdaf15891d3f8215b0", + "ParentBackdropImageTags": [ + "17c8f1f8c484ca3f6f9dee5102176e1e" + ], + "UserData": { + "PlaybackPositionTicks": 0, + "PlayCount": 0, + "IsFavorite": false, + "Played": false, + "Key": "许嵩-飞驰于你-0001-0001飞驰于你" + }, + "Artists": [ + "许嵩" + ], + "ArtistItems": [ + { + "Name": "许嵩", + "Id": "9cb24036603a4bbdaf15891d3f8215b0" + } + ], + "Album": "飞驰于你", + "AlbumId": "bb725906dce0e1d5dcbb5e2aab50f590", + "AlbumPrimaryImageTag": "b9a921b8a933b371c1075b3c9d046352", + "AlbumArtist": "许嵩", + "AlbumArtists": [ + { + "Name": "许嵩", + "Id": "9cb24036603a4bbdaf15891d3f8215b0" + } + ], + "ImageTags": { + "Primary": "c1a904073873e3cdc4e8c0fcfc91a933" + }, + "BackdropImageTags": [], + "ImageBlurHashes": { + "Primary": { + "c1a904073873e3cdc4e8c0fcfc91a933": "eH9QXCRj0K%M?HD%oe-;RkIUE2of$*NFNHt7ayR*ofs.%2ayIoofxa", + "b9a921b8a933b371c1075b3c9d046352": "eH9QXCRj0K%M?HD%oe-;RkIUE2of$*NFNHt7ayR*ofs.%2ayIoofxa" + }, + "Backdrop": { + "17c8f1f8c484ca3f6f9dee5102176e1e": "WTHetT_3.9?bjFt8%hRPofWBRit70L9FMwRjozM{-;IUjZayWXt7" + } + }, + "LocationType": "FileSystem", + "MediaType": "Audio" + } + ], + "TotalRecordCount": 1, + "StartIndex": 0 +} +``` + +### FavoriteItems/[id] 收藏歌曲/专辑/歌手 + +POST: `[host]/Users/[UserId]/FavoriteItems/[id]` + +### stream 歌曲播放链接 + +GET: `[host]/Audio/[id]/stream` + +query: + +| 参数名 | 备注 | +| --- | --- | +| static | 是否转码,不转码时填写 | +| audioCodec | 格式,可选值:`mp3`, `aac` | +| audioBitRate | 比特率,转码时填写 | + +:::note + +请求头需要携带授权信息。 + +::: + +### FavoriteItems[id] 取消收藏 + +DELETE: `[host]/Users/[UserId]/FavoriteItems/[id]` + +### Items 添加歌曲到歌单 + +POST: `[host]/Playlists/[id]/Items` + +query: + +| 参数名 | 备注 | +| --- | --- | +| Ids | 歌曲ID列表 | +| UserId | 用户ID | + +response code: 204 + +### Items 从歌单中移除歌曲 + +DELETE: `[host]/Playlists/[id]/Items` + +query: + +| 参数名 | 备注 | +| --- | --- | +| entryIds | 歌曲ID列表 | + +response code: 204 + +### Items/[id] 修改歌单信息 + +POST: `[host]/Items/[id]` + +body(`application/json`): + +| 参数名 | 备注 | +| --- | --- | +| Name | 歌单名 | +| Id | 歌单ID | + +response code: 204 \ No newline at end of file diff --git a/i18n/en/docusaurus-plugin-content-docs/current/notes/services/navidrome.md b/i18n/en/docusaurus-plugin-content-docs/current/notes/services/navidrome.md new file mode 100644 index 0000000..7c8621e --- /dev/null +++ b/i18n/en/docusaurus-plugin-content-docs/current/notes/services/navidrome.md @@ -0,0 +1,568 @@ +--- +sidebar_position: 2 +--- + +# Navidrome 接口文档 + +:::note + +Navidrome 目前还没有官方的接口文档,以下均为抓包所得。 + +0.55.0 版本对原生接口进行了重大重构,以下内容部分已经过时。 + +::: + +## 认证 + +### login 登录 + +POST:`[host]/auth/login` + +body(`application/json`): + +| 参数名 | 是否必填 | 默认值 | 备注 | +| --- | --- | --- | --- | +| username | Y | | 用户名 | +| password | Y | | 密码 | + +响应内容: + +```json +{ + "id": "34c4xxxx-xxxx-xxxx-xxxx-6442b9a3xxxx", + "isAdmin": true, + "lastFMApiKey": "xxx", + "name": "username", + "subsonicSalt": "xxx", + "subsonicToken": "xxxxx", + "token": "xxxx", + "username": "username" +} +``` + +在之后的请求中,需要在请求头中携带以下信息: + +- `x-nd-authorization`: 'Bearer `token`' +- `x-nd-client-unique-id`: `id` + +### keepalive 登录状态保活 + +GET: `[host]/api/keepalive/keepalive` + +## 请求节点 + +### album 获取专辑列表 + +GET: `[host]/api/album` + +query: + +| 参数名 | 备注 | +| --- | --- | +| _start | 起始行数,0开始 | +| _end | 结束行数 | +| _order | 排序,可选值 `ASC`, `DESC` | +| _sort | 排序方式,可选值 `random`, `createdAt`, `min_year`, `play_count`, `play_date`, `name`, `albumArtist`, `rating`, 可用`,`分割多个排序方式,如 `min_year asc,date asc` | +| artist_id | 歌手id,可选 | +| rating | 评分,可选 | +| starred | 收藏状态(`true/false`),可选 | +| name | 专辑名(like 查询),可选 | + +:::note 待补充: + +1. 如何查询在某个区间的年代,自测 `min_year` 和 `max_year` 只有相等的判断,同时传也不会判断区间。 +2. 如何查询歌曲数量大于某个值的专辑 +3. 专辑名的筛选是否可以添加多个关键词筛选 + +::: + +响应内容: + +```json +[ + { + "playCount": 14, + "playDate": "2024-04-18T07:37:46.658+08:00", + "rating": 0, + "starred": false, + "starredAt": null, + "id": "2e7e41725443fa5e8a637a4668e63e98", + "name": "放下", + "embedArtPath": "/volume1/music/胡夏/放下/胡夏-放下.flac", + "artistId": "6d095fad618e04185bd66998450041da", + "artist": "胡夏", + "albumArtistId": "6d095fad618e04185bd66998450041da", + "albumArtist": "胡夏", + "allArtistIds": "6d095fad618e04185bd66998450041da", + "maxYear": 2013, + "minYear": 2013, + "date": "2013", + "maxOriginalYear": 0, + "minOriginalYear": 0, + "releases": 1, + "compilation": false, + "songCount": 1, + "duration": 337.08, + "size": 35163552, + "genre": "", + "genres": null, + "fullText": " 放下 胡夏", + "orderAlbumName": "放下", + "orderAlbumArtistName": "胡夏", + "paths": "/volume1/music/胡夏/放下", + "externalInfoUpdatedAt": null, + "createdAt": "2024-03-02T01:04:46.424670828+08:00", + "updatedAt": "2024-03-21T20:41:28.340360006+08:00" + } +] +``` + +响应头: + +- `x-total-count`: 总行数 + +### album/[id] 获取专辑信息 + +GET: `[host]/api/album/[id]` + +响应内容: + +```json +{ + "playCount": 14, + "playDate": "2024-04-18T07:37:46.658+08:00", + "rating": 0, + "starred": false, + "starredAt": null, + "id": "2e7e41725443fa5e8a637a4668e63e98", + "name": "放下", + "embedArtPath": "/volume1/music/胡夏/放下/胡夏-放下.flac", + "artistId": "6d095fad618e04185bd66998450041da", + "artist": "胡夏", + "albumArtistId": "6d095fad618e04185bd66998450041da", + "albumArtist": "胡夏", + "allArtistIds": "6d095fad618e04185bd66998450041da", + "maxYear": 2013, + "minYear": 2013, + "date": "2013", + "maxOriginalYear": 0, + "minOriginalYear": 0, + "releases": 1, + "compilation": false, + "songCount": 1, + "duration": 337.08, + "size": 35163552, + "genre": "", + "genres": null, + "fullText": " 放下 胡夏", + "orderAlbumName": "放下", + "orderAlbumArtistName": "胡夏", + "paths": "/volume1/music/胡夏/放下", + "externalInfoUpdatedAt": null, + "createdAt": "2024-03-02T01:04:46.424670828+08:00", + "updatedAt": "2024-03-21T20:41:28.340360006+08:00" +} +``` + +### artist/[id] 获取歌手信息 + +GET: `[host]/api/artist/[id]` + +响应内容: + +```json +{ + "playCount": 42, + "playDate": "2024-04-18T08:27:04.036+08:00", + "rating": 0, + "starred": false, + "starredAt": null, + "id": "6d095fad618e04185bd66998450041da", + "name": "胡夏", + "albumCount": 19, + "songCount": 60, + "genres": null, + "fullText": " 胡夏", + "orderArtistName": "胡夏", + "size": 1714942860, + "externalUrl": "https://www.last.fm/music/%E8%83%A1%E5%A4%8F", + "externalInfoUpdatedAt": "0001-01-01T00:00:00Z" +} +``` + +### getArtistInfo 获取歌手简介及相似歌手 + +参见 Subsonic 的 [getArtistInfo](subsonic#getartistinfo2-获取歌手简介和相似歌手列表) 接口。 + +### artist 获取歌手列表 + +GET: `[host]/api/artist` + +query: + +| 参数名 | 备注 | +| --- | --- | +| _start | 起始行数,0开始 | +| _end | 结束行数,_end 和 _start 都等于 0 时可以查询全部 | +| _order | 排序,可选值 `ASC`, `DESC` | +| _sort | 排序方式,可选值 `random`, `play_count`, `play_date`, `name`, `rating`, 可用`,`分割多个排序方式,如 `min_year asc,date asc` | +| rating | 评分,可选 | +| starred | 收藏状态(`true/false`),可选 | +| name | 歌手名(like 查询),可选 | +| role | 角色,可选,0.55.0 及以上版本可用,以下版本添加此参数会出错 | + +响应内容: + +```json +[ + { + "playCount": 42, + "playDate": "2024-04-18T08:27:04.036+08:00", + "rating": 0, + "starred": false, + "starredAt": null, + "id": "6d095fad618e04185bd66998450041da", + "name": "胡夏", + "albumCount": 19, + "songCount": 60, + "genres": null, + "fullText": " 胡夏", + "orderArtistName": "胡夏", + "size": 1714942860, + "mbzArtistId": "2ccbc670-5fe9-450a-a4ab-4d87131214b3", + "smallImageUrl": "https://i.scdn.co/image/ab67616d0000485129c9ac0b10fb4f016de87b11", + "mediumImageUrl": "https://i.scdn.co/image/ab67616d00001e0229c9ac0b10fb4f016de87b11", + "largeImageUrl": "https://i.scdn.co/image/ab67616d0000b27329c9ac0b10fb4f016de87b11", + "externalUrl": "https://www.last.fm/music/%E8%83%A1%E5%A4%8F", + "externalInfoUpdatedAt": "2024-04-18T16:10:03.997920721+08:00" + } +] +``` + +响应头: + +- `x-total-count`: 总行数 + +### getCoverArt 封面图片 + +参见 Subsonic 的 [getCoverArt](./subsonic#getcoverart-封面图片) + +### playlist 创建歌单 + +POST: `[host]/api/playlist` + +body(`application/json`): + +| 参数名 | 备注 | +| --- | --- | +| name | 歌单名 | +| comment | 评论 | +| public | 其他人是否可见 | + +响应内容: + +```json +{ + "id": "775cca70-cd09-4029-9858-583098bef519" +} +``` + +### playlist/[id] 删除歌单 + +DELETE: `[host]/api/playlist/[id]` + +响应内容: + +```json +{} +``` + +### song/[id] 获取歌曲信息 + +GET: `[host]/api/song/[id]` + +### playlist 获取歌单列表 + +GET: `[host]/api/playlist` + +query: + +| 参数名 | 备注 | +| --- | --- | +| _start | 起始行数,0开始 | +| _end | 结束行数,_end 和 _start 都等于 0 时可以查询全部 | +| _order | 排序,可选值 `ASC`, `DESC` | +| _sort | 排序方式,可选值 `random`, `name`, 可用`,`分割多个排序方式,如 `max_year asc,date asc` | + +响应内容: + +```json +[ + { + "id": "355c94bd-4bef-485a-8945-d6d9d02191cd", + "name": "1999年老歌by慕星人", + "comment": "", + "duration": 8280.36, + "size": 951975104, + "songCount": 31, + "ownerName": "userA", + "ownerId": "34c42e25-70f8-42d3-83ff-6442b9a341a4", + "public": false, + "path": "", + "sync": false, + "createdAt": "2024-04-09T15:28:28.917434949+08:00", + "updatedAt": "2024-04-17T22:32:12.507824702+08:00", + "rules": null, + "evaluatedAt": null + }, + { + "id": "bd200cad-839e-4818-85af-7e438fb6aef0", + "name": "15515", + "comment": "", + "duration": 170.67, + "size": 19324940, + "songCount": 1, + "ownerName": "userB", + "ownerId": "631ec3f3-0d00-4648-b404-ce4e95c305f2", + "public": false, + "path": "", + "sync": false, + "createdAt": "2024-03-17T18:55:34.432021369+08:00", + "updatedAt": "2024-03-17T18:55:47.208985366+08:00", + "rules": null, + "evaluatedAt": null + }, +] +``` + +响应头: + +- `x-total-count`: 总行数 + +### setRating 评分 + +参见 Subsonic 的 [setRating](./subsonic#setrating-评分) 接口。 + +### getScanStatus 获取扫描状态 + +参见 Subsonic 的 [getScanStatus](./subsonic#getscanstatus-获取扫描状态) 接口。 + +:::tip + +这个接口获取到的歌曲数量可能与实际不符,建议从歌曲列表接口的响应头中获取歌曲总数。 + +::: + +### scrobble 滚动播放记录 + +参见 Subsonic 的 [scrobble](./subsonic#scrobble-滚动播放记录) 接口。 + +### search2 搜索歌曲/专辑/歌手 + +参见 Subsonic 的 [search2](./subsonic#search2-搜索歌曲专辑歌手) 接口。 + +### getSimilarSongs 获取相似歌曲 + +参见 Subsonic 的 [getSimilarSongs](./subsonic#getsimilarsongs-获取相似歌曲) 接口。 + +### song 获取歌曲列表 + +GET: `[host]/api/song` + +query: + +| 参数名 | 备注 | +| --- | --- | +| _start | 起始行数,0开始 | +| _end | 结束行数,_end 和 _start 都等于 0 时可以查询全部歌手 | +| _order | 排序,可选值 `ASC`, `DESC` | +| _sort | 排序方式,可选值 `random`, `createdAt`, `max_year`, `play_count`, `play_date`, `title`, `album`, `rating`, 可用`,`分割多个排序方式,如 `max_year asc,date asc` | +| album_id | 专辑id | +| starred | 收藏状态 | +| title | 标题 | + +响应内容: + +```json +[ + { + "playCount": 14, + "playDate": "2024-04-18T07:37:46.658+08:00", + "rating": 4, + "starred": true, + "starredAt": "2024-03-02T01:06:30.37479538+08:00", + "bookmarkPosition": 0, + "id": "be7daa6fc04bbbed2e0aaf15fc0b48df", + "path": "/volume1/music/胡夏/放下/胡夏-放下.flac", + "title": "放下", + "album": "放下", + "artistId": "6d095fad618e04185bd66998450041da", + "artist": "胡夏", + "albumArtistId": "6d095fad618e04185bd66998450041da", + "albumArtist": "胡夏", + "albumId": "2e7e41725443fa5e8a637a4668e63e98", + "hasCoverArt": true, + "trackNumber": 1, + "discNumber": 0, + "year": 2013, + "date": "2013", + "originalYear": 0, + "releaseYear": 0, + "size": 35163552, + "suffix": "flac", + "duration": 337.08, + "bitRate": 822, + "channels": 2, + "genre": "", + "genres": null, + "fullText": " 放下 胡夏", + "orderTitle": "放下", + "orderAlbumName": "放下", + "orderArtistName": "胡夏", + "orderAlbumArtistName": "胡夏", + "compilation": false, + "lyrics": "[{\"lang\":\"xxx\",\"line\":[{\"start\":0,\"value\":\"作词 : 丁丁张\"},{\"start\":370,\"value\":\"作曲 : 陈嵩\"},{\"start\":750,\"value\":\"风吹凉一杯茶\"},{\"start\":5800,\"value\":\"夕阳跑赢了老马\"},{\"start\":10400,\"value\":\"回头看雪染白长头发\"},{\"start\":19450,\"value\":\"少年被风催大\"},{\"start\":24350,\"value\":\"容颜未改心有疤\"},{\"start\":28500,\"value\":\"我爱你爱让我放下\"},{\"start\":38370,\"value\":\"放下\"},{\"start\":42920,\"value\":\"一只手握不住流沙\"},{\"start\":49220,\"value\":\"两双眼留不住落花\"},{\"start\":55480,\"value\":\"风吹草云落下你心如野马\"},{\"start\":62280,\"value\":\"等下时光请等一下\"},{\"start\":68440,\"value\":\"千只雀追不上流霞\"},{\"start\":74440,\"value\":\"万只蝶抵不过霜打\"},{\"start\":81540,\"value\":\"水滴石风在刮我声音沙哑\"},{\"start\":87500,\"value\":\"放下容我将你放下\"},{\"start\":94100,\"value\":\"天地江湖日月\"},{\"start\":95550,\"value\":\"不留不念不说话\"},{\"start\":100450,\"value\":\"繁华世界弱水\"},{\"start\":101850,\"value\":\"三千一瓢怎盛下\"},{\"start\":106100,\"value\":\"风吹凉一杯茶\"},{\"start\":109250,\"value\":\"夕阳跑赢了老马\"},{\"start\":112350,\"value\":\"回头看雪染白长头发\"},{\"start\":118600,\"value\":\"少年被风催大\"},{\"start\":121850,\"value\":\"容颜未改心有疤\"},{\"start\":125060,\"value\":\"我爱你爱让我放下\"},{\"start\":147120,\"value\":\"一个人走不到天涯\"},{\"start\":153420,\"value\":\"两场雪封不住嫩芽\"},{\"start\":159720,\"value\":\"月升起云落下你笑颜如花\"},{\"start\":166370,\"value\":\"等下时光请等一下\"},{\"start\":172370,\"value\":\"千个字说不出情话\"},{\"start\":178670,\"value\":\"万封信写不完牵挂\"},{\"start\":185310,\"value\":\"山走远风在刮我心乱如麻\"},{\"start\":191710,\"value\":\"放下容我将你放下\"},{\"start\":198460,\"value\":\"天地江湖日月\"},{\"start\":199860,\"value\":\"不留不念不说话\"},{\"start\":204710,\"value\":\"繁华世界弱水\"},{\"start\":206110,\"value\":\"三千一瓢怎盛下\"},{\"start\":210260,\"value\":\"风吹凉一杯茶\"},{\"start\":213410,\"value\":\"夕阳跑赢了老马\"},{\"start\":216560,\"value\":\"回头看雪染白长头发\"},{\"start\":222910,\"value\":\"少年被风催大\"},{\"start\":226670,\"value\":\"容颜未改心有疤\"},{\"start\":229210,\"value\":\"我爱你爱让我放下\"},{\"start\":235510,\"value\":\"风吹凉一杯茶\"},{\"start\":238660,\"value\":\"夕阳跑赢了老马\"},{\"start\":241810,\"value\":\"回头看雪染白长头发\"},{\"start\":248690,\"value\":\"少年被风催大\"},{\"start\":251210,\"value\":\"容颜未改心有疤\"},{\"start\":258830,\"value\":\"我爱你爱让我放下\"},{\"start\":267250,\"value\":\"忘了你爱让我放下\"}],\"synced\":true}]", + "rgAlbumGain": -6.4, + "rgAlbumPeak": 0.810242, + "rgTrackGain": -6.4, + "rgTrackPeak": 0.810242, + "createdAt": "2024-03-02T01:04:46.424670828+08:00", + "updatedAt": "2024-03-21T20:41:28.340360006+08:00" + } +] +``` + +响应头: + +- `x-total-count`: 总行数 + +:::caution + +当 `_sort` = `createdAt` 时,最多可以获取到 5w 条记录,再往后服务端会报错。 + +::: + +### getTopSongs 获取歌手的热门歌曲 + +参见 Subsonic 的 [getTopSongs](./subsonic#gettopsongs-获取歌手的热门歌曲) 接口。 + +### playlist/tracks 获取歌单中的歌曲列表 + +GET: `[host]/api/playlist/[id]/tracks` + +query: + +| 参数名 | 备注 | +| --- | --- | +| _start | 起始行数,0开始 | +| _end | 结束行数,_end 和 _start 都等于 0 时可以查询全部 | +| _order | 排序,可选值 `ASC`, `DESC` | +| _sort | 排序方式,可选值 `id`, `createdAt`, 可用`,`分割多个排序方式,如 `max_year asc,date asc` | +| playlist_id | 歌单id | + +响应内容: + +```json +[ + { + "id": "1", + "mediaFileId": "45bbcdc311aeb7c4cfd4ab5fb8db3b68", + "playlistId": "355c94bd-4bef-485a-8945-d6d9d02191cd", + "playCount": 5, + "playDate": "2024-03-23T15:52:34.334+08:00", + "rating": 0, + "starred": true, + "starredAt": "2024-02-05T14:07:28.428223292+08:00", + "bookmarkPosition": 0, + "path": "/volume1/music/张宇/雨一直下/张宇-雨一直下.flac", + "title": "雨一直下", + "album": "雨一直下", + "artistId": "1fed6de3753b8cf668a4eb4c485a70cb", + "artist": "张宇", + "albumArtistId": "1fed6de3753b8cf668a4eb4c485a70cb", + "albumArtist": "张宇", + "albumId": "986a826c9d21925f2be641d370d81e32", + "hasCoverArt": true, + "trackNumber": 1, + "discNumber": 0, + "year": 1999, + "date": "1999", + "originalYear": 0, + "releaseYear": 0, + "size": 37831941, + "suffix": "flac", + "duration": 290.31, + "bitRate": 1035, + "channels": 2, + "genre": "", + "genres": null, + "fullText": " 张宇 雨一直下", + "orderTitle": "雨一直下", + "orderAlbumName": "雨一直下", + "orderArtistName": "张宇", + "orderAlbumArtistName": "张宇", + "compilation": false, + "lyrics": "[{\"lang\":\"xxx\",\"line\":[{\"start\":0,\"value\":\"作词 : 十一郎\"},{\"start\":810,\"value\":\"作曲 : 张宇\"},{\"start\":1630,\"value\":\"编曲 : Michael Thompson\"},{\"start\":2450,\"value\":\"\"},{\"start\":40130,\"value\":\"雨一直下\"},{\"start\":43500,\"value\":\"气氛不算融洽\"},{\"start\":48150,\"value\":\"在同个屋檐下\"},{\"start\":49790,\"value\":\"你渐渐感到心在变化\"},{\"start\":53760,\"value\":\"你爱着他\"},{\"start\":57010,\"value\":\"也许也带着恨吧\"},{\"start\":61540,\"value\":\"青春耗了一大半\"},{\"start\":63380,\"value\":\"原来只是陪他玩耍\"},{\"start\":67330,\"value\":\"正想离开他\"},{\"start\":70830,\"value\":\"他却拿着鲜花\"},{\"start\":75390,\"value\":\"说不着边的话\"},{\"start\":77090,\"value\":\"让整个场面更加尴尬\"},{\"start\":80980,\"value\":\"不可思议吧\"},{\"start\":84580,\"value\":\"梦在瞬间崩塌\"},{\"start\":88920,\"value\":\"为何当初那么傻\"},{\"start\":90770,\"value\":\"还一心想要嫁给他\"},{\"start\":95340,\"value\":\"就是爱到深处 才怨他\"},{\"start\":99620,\"value\":\"舍不舍得 都断了吧\"},{\"start\":102560,\"value\":\"那是从来\"},{\"start\":103670,\"value\":\"都没有后路的悬崖\"},{\"start\":109030,\"value\":\"就是爱到深处 才由他\"},{\"start\":113200,\"value\":\"碎了心 也要放得下\"},{\"start\":116300,\"value\":\"难道忘了那爱他的伤\"},{\"start\":119070,\"value\":\"已密密麻麻\"},{\"start\":122380,\"value\":\"雨一直下\"},{\"start\":125740,\"value\":\"气氛不算融洽\"},{\"start\":130210,\"value\":\"在同个屋檐下\"},{\"start\":131950,\"value\":\"你渐渐感到心在变化\"},{\"start\":135890,\"value\":\"不可思议吧\"},{\"start\":139390,\"value\":\"梦在瞬间崩塌\"},{\"start\":143700,\"value\":\"为何当初那么傻\"},{\"start\":145580,\"value\":\"还一心想要嫁给他\"},{\"start\":150120,\"value\":\"就是爱到深处 才怨他\"},{\"start\":154360,\"value\":\"舍不舍得 都断了吧\"},{\"start\":157440,\"value\":\"那是从来\"},{\"start\":158480,\"value\":\"都没有后路的悬崖\"},{\"start\":163810,\"value\":\"就是爱到深处 才由他\"},{\"start\":168080,\"value\":\"碎了心 也要放得下\"},{\"start\":171150,\"value\":\"难道忘了那爱他的伤\"},{\"start\":173920,\"value\":\"已密密麻麻\"},{\"start\":177090,\"value\":\"不要再为了他挣扎\"},{\"start\":180270,\"value\":\"不要再为他左牵右挂\"},{\"start\":183760,\"value\":\"今后不管他爱不爱谁\"},{\"start\":186910,\"value\":\"快乐吗 都随他\"},{\"start\":204950,\"value\":\"就是爱到深处 才由他\"},{\"start\":209130,\"value\":\"碎了心 也要放得下\"},{\"start\":212180,\"value\":\"难道忘了那爱他的伤\"},{\"start\":215010,\"value\":\"已密密麻麻\"},{\"start\":218590,\"value\":\"就是爱到深处 才怨他\"},{\"start\":222760,\"value\":\"舍不舍得都断了吧\"},{\"start\":225920,\"value\":\"那是从来\"},{\"start\":227000,\"value\":\"都没有后路的悬崖\"},{\"start\":232340,\"value\":\"就是爱到深处 才由他\"},{\"start\":236570,\"value\":\"碎了心 也要放得下\"},{\"start\":239640,\"value\":\"难道忘了那爱他的伤\"},{\"start\":242430,\"value\":\"已密密麻麻\"},{\"start\":246940,\"value\":\"\"},{\"start\":247360,\"value\":\"Arranged By – Michael Thompson\"},{\"start\":247590,\"value\":\"Acoustic Guitar – Michael Thompson\"},{\"start\":247760,\"value\":\"Electric Guitar – Michael Thompson\"},{\"start\":247940,\"value\":\"Guitar [Wah Wah] – Michael Thompson\"},{\"start\":248130,\"value\":\"Soloist – Michael Thompson\"},{\"start\":248330,\"value\":\"Bass – Randy Jackson\"},{\"start\":248510,\"value\":\"Drums – Vinnie Colaiuta\"},{\"start\":248690,\"value\":\"Engineer – Bill Drescher\"},{\"start\":248880,\"value\":\"Engineer – David N. Cole\"},{\"start\":249050,\"value\":\"Keyboards – Jeffrey \\\"CJ\\\" Vanston\"},{\"start\":249230,\"value\":\"Programmed By – Jeffrey \\\"CJ\\\" Vanston\"},{\"start\":249400,\"value\":\"Synth – Jeffrey \\\"CJ\\\" Vanston\"},{\"start\":249590,\"value\":\"Lyrics By – 萧慧文\"},{\"start\":249780,\"value\":\"Music By – 张宇\"},{\"start\":249970,\"value\":\"Producer – David N. Cole, Phil Chang\"},{\"start\":250140,\"value\":\"Mixed By – David N. Cole\"},{\"start\":250500,\"value\":\"Recorded By – Bill Schnee, Greg Droman, Richard Flack\"}],\"synced\":true}]", + "rgAlbumGain": -8.76, + "rgAlbumPeak": 0.998901, + "rgTrackGain": -9.65, + "rgTrackPeak": 0.988953, + "createdAt": "2024-02-04T00:11:12.548089502+08:00", + "updatedAt": "2024-03-21T21:42:11.165237602+08:00" + } +] +``` + +响应头: + +- `x-total-count`: 总行数 + +:::tip + +`id` 用于从歌单中删除歌曲,从 1 开始计数。 + +::: + +### star 收藏歌曲/专辑/歌手 + +参见 Subsonic 的 [star](./subsonic#star-收藏歌曲专辑歌手) 接口。 + +### stream 歌曲播放链接 + +参见 Subsonic 的 [stream](./subsonic#stream-歌曲播放链接) 接口。 + +### unstar 取消收藏 + +参见 Subsonic 的 [unstar](./subsonic#unstar-取消收藏) 接口。 + +### playlist/[id] 更新歌单 + +PUT: `[host]/api/playlist/[id]` + +body(`application/json`) + +| 参数名 | 备注 | +| --- | --- | +| name | 歌单名 | +| comment | 评论 | +| public | 其他人是否可见 | + +### playlist/tracks 添加歌曲到歌单 + +POST: `[host]/api/playlist/[id]/tracks` + +body(`application/json`): + +| 参数名 | 备注 | +| --- | --- | +| ids | 待添加的歌曲id列表, `,`分割 | + +### playlist/tracks 从歌单中移除歌曲 + +DELETE: `[host]/api/playlist/[id]/tracks` + +query: + +| 参数名 | 备注 | +| --- | --- | +| id | 待移除的歌曲id,多个id则以 `id=2&id=3` 的形式发起请求 | \ No newline at end of file diff --git a/i18n/en/docusaurus-plugin-content-docs/current/notes/services/plex.md b/i18n/en/docusaurus-plugin-content-docs/current/notes/services/plex.md new file mode 100644 index 0000000..42e992b --- /dev/null +++ b/i18n/en/docusaurus-plugin-content-docs/current/notes/services/plex.md @@ -0,0 +1,824 @@ +--- +sidebar_position: 6 +--- + +# Plex 接口文档 + +:::note 参考文档: + +- [Plex API(非官方)](https://plexapi.dev/) +- [python-plexapi](https://github.com/pkkid/python-plexapi) +- [Plex Audio Transcoder File Extension](https://www.reddit.com/r/PleX/comments/189gtej/plex_audio_transcoder_file_extension/) + +::: + +## 认证 + +若无特殊说明,下文所有请求均需携带以下请求头。 + +| 参数名 | 备注 | +| --- | --- | +| X-Plex-Client-Identifier | 设备 ID | +| X-Plex-Platform | 操作系统,可选值:`Web`, `Android`, `iOS`, `MacOSX`, `Windows`, `Linux` | +| X-Plex-Provides | 应用类型,可选值:`player` | +| X-Plex-Product | 应用名称 | +| X-Plex-Version | 应用版本号 | +| X-Plex-Device | 设备型号 | +| X-Plex-Device-Name | 设备名 | +| X-Plex-Token | 访问令牌,登录成功后必填 | + + +### signin 登录 + +POST: `https://plex.tv/api/v2/users/signin` + +body(`application/json`): + +| 参数名 | 备注 | +| --- | --- | +| login | 用户名 | +| password | 密码 | +| verificationCode | OTP 验证码,可选 | +| rememberMe | 记住登录状态,可选值:`true`, `false` | + +401 response: + +```xml + + + + +``` + +response: + +```xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +``` + +### resources 获取资源列表 + +GET: `https://plex.tv/api/v2/resources?includeHttps=1&includeRelay=1` + +response: + +```xml + + + + + + + + + +``` + +### 测试服务器连通性 + +GET: `[host]` + +response: + +```xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + +``` + +## 请求节点 + +### agents 获取代理(插件)列表 + +GET: `[host]/system/agents/com.plexapp.agents.none/config/8?X-Plex-Token=[token]` + +结尾的 `8` 表示查询艺术家相关的代理。 + +response: + +```xml + + + + + + + + + + +``` + +### sections 获取资料库列表 + +GET: `[host]/library/sections` + +```xml + + + + +``` + +在返回的列表中筛选 `type="artist"`的资料库,记录一下 `key` 值。 + +### sections 获取专辑列表 + +GET: `[host]/library/sections/[sectionKey]/all` + +query: + +| 参数名 | 备注 | +| --- | --- | +| type | 类型,固定为 `9` | +| sort | 排序方式,默认按专辑艺术家排序,可选值 `addedAt:desc` | +| title | 标题,可选 | +| includeAdvanced | `1` | +| includeCollections | `1` | +| includeExternalMedia | `1` | +| X-Plex-Container-Start | 起始行数 | +| X-Plex-Container-Size | 结果数量 | + +response: + +```xml + + + + + +``` +### metadata 获取专辑信息 + +GET: `[host]/library/metadata/[id]` + +query: + +| 参数名 | 备注 | +| --- | --- | +| includeConcerts | `1` | +| includeExtras | `1` | +| includeOnDeck | `1` | +| includePopularLeaves | `1` | +| includePreferences | `1` | +| includeChapters | `1` | +| includeStations | `1` | +| includeMarkers | `1` | +| includeExternalMedia | `1` | +| asyncAugmentMetadata | `1` | +| includeRelated | `1` | +| checkFiles | `1` | +| asyncRefreshAnalysis | `1` | +| asyncRefreshLocalMediaAgent | `1` | + +response: + +```xml + + + + + + + + + +``` + +### children 获取歌手名下的专辑 + +GET: `[host]/library/metadata/[id]/children` + +query: + +| 参数名 | 备注 | +| --- | --- | +| excludeAllLeaves | `1` | + +### sections 获取歌手列表 + +GET: `[host]/library/sections/[sectionKey]/all` + +query: + +| 参数名 | 备注 | +| --- | --- | +| type | 类型,固定为 `8` | +| includeAdvanced | `1` | +| includeCollections | `1` | +| includeExternalMedia | `1` | +| X-Plex-Container-Start | 起始行数 | +| X-Plex-Container-Size | 结果数量 | + +response: + +```xml + + + + + +``` + +### metadata 获取歌手信息 + +GET: `[host]/library/metadata/[id]` + +query: + +| 参数名 | 备注 | +| --- | --- | +| includeConcerts | `1` | +| includeExtras | `1` | +| includeOnDeck | `1` | +| includePopularLeaves | `1` | +| includePreferences | `1` | +| includeChapters | `1` | +| includeStations | `1` | +| includeMarkers | `1` | +| includeExternalMedia | `1` | +| asyncAugmentMetadata | `1` | +| includeRelated | `1` | +| checkFiles | `1` | +| asyncRefreshAnalysis | `1` | +| asyncRefreshLocalMediaAgent | `1` | + +response: + +```xml + + + + + + + + + + + + + + + + + + + + + + +``` + +### photo 图片链接 + +GET: `[host]/photo/:/transcode` + +query: + +| 参数名 | 备注 | +| --- | --- | +| width | 宽度 | +| height | 高度 | +| minSize | `1` | +| upscale | `1` | +| url | `[thumb]?X-Plex-Token=[token]` | +| X-Plex-Token | 访问令牌 | + +### playlists 创建歌单 + +GET: `[host]/playlists` + +首先需要通过 [测试服务器连通性](#测试服务器连通性) 的响应拿到 `machineIdentifier` + +query: + +| 参数名 | 备注 | +| --- | --- | +| type | 类型,固定为 `audio` | +| title | 歌单名 | +| smart | 是否智能歌单,可选值:`1`, `0` | +| uri | `server://[machineIdentifier]/com.plexapp.plugins.library/library/metadata/[歌曲ID列表]` | + +response: + +```xml + + + + +``` + +### playlists 删除歌单 + +DELETE: `[host]/playlists/[id]` + +### metadata 删除歌曲 + +DELETE: `[host]/library/metadata/[id]` + +### folder 获取目录列表 + +GET: `/library/sections/[sectionId]/folder` + +query: + +| 参数名 | 备注 | +| --- | --- | +| X-Plex-Container-Start | 行数偏移 | +| X-Plex-Container-Size | 结果数量 | +| includeCollections | `1` | +| includeExternalMedia | `1` | +| includeAdvanced | `1` | +| includeMeta | `1` | +| parent | 目录ID,不传时查询根目录 | + +### genre 获取类型列表 + +GET: `[host]/library/sections/[sectionId]/genre` + +query: + +| 参数名 | 备注 | +| --- | --- | +| type | `9` | + +response: + +```xml + + + + + + + + +``` + +### streams 获取歌词 + +GET: `[host]/library/streams/[id]` + +query: + +| 参数名 | 备注 | +| --- | --- | +| format | 格式,可选值:`xml` | + +response: + +```xml + + + + + + + + +``` + +### matches 获取歌手匹配结果 + +GET: `[host]/library/metadata/[id]/matches` + +query: + +| 参数名 | 备注 | +| --- | --- | +| manual | 是否手动,固定为 `0` | +| agent | 代理ID | + +response: + +```xml + + + + + + + +``` + +### match 确认歌手匹配结果 + +PUT: `[host]/library/metadata/[id]/match` + +query: + +| 参数名 | 备注 | +| --- | --- | +| name | 歌手名 | +| agent | | + +### playlists 获取歌单列表 + +GET: `[host]/playlists` + +query: + +| 参数名 | 备注 | +| --- | --- | +| playlistType | `audio` | + +response: + +```xml + + + + +``` + +### rate 评分歌曲/专辑/歌手 + +PUT: `[host]/:/rate` + +query: + +| 参数名 | 备注 | +| --- | --- | +| key | 歌曲/专辑/歌手ID | +| identifier | `com.plexapp.plugins.library` | +| rating | 评分,0-10之间 | + +### scrobble 滚动播放记录 + +**更新播放进度** + +GET: `[host]/:/timeline` + +query: + +| 参数名 | 备注 | +| --- | --- | +| ratingKey | 歌曲ID | +| time | 播放进度,单位:ms | +| state | 播放状态,可选值:`playing`, `paused` | + +> 此接口不会将歌曲播放次数+1,需要手动调用下面的接口。 + +**将歌曲标记为已播放** + +GET: `[host]/:/scrobble` + +query: + +| 参数名 | 备注 | +| --- | --- | +| key | 歌曲ID | +| identifier | `com.plexapp.plugins.library` | + +### sections 获取歌曲列表 + +GET: `[host]/library/sections/[sectionKey]/all` + +query: + +| 参数名 | 备注 | +| --- | --- | +| type | 类型,固定为 `10` | +| sort | 排序方式,可选值:`random`, `addedAt`, `year`, `lastViewedAt`, `titleSort`, `album.titleSort`, `ratingCount`,若要倒序,则使用 `addedAt:desc` 这种格式。 | +| includeCollections | `1` | +| includeExternalMedia | `1` | +| artist.id | 歌手ID,可选 | +| userRating | 评分,可选 | +| title | 标题,可选 | +| X-Plex-Container-Start | 起始行数 | +| X-Plex-Container-Size | 结果数量 | + +response: + +```xml + + + + + + + + +``` + +### metadata 获取歌曲信息 + +GET: `[host]/library/metadata/[id]` + +query: + +| 参数名 | 备注 | +| --- | --- | +| includeConcerts | `1` | +| includeExtras | `1` | +| includeOnDeck | `1` | +| includePopularLeaves | `1` | +| includePreferences | `1` | +| includeChapters | `1` | +| includeStations | `1` | +| includeMarkers | `1` | +| includeExternalMedia | `1` | +| asyncAugmentMetadata | `1` | +| includeRelated | `1` | +| checkFiles | `1` | + +response: + +```xml + + + + + + + + + + + + + +``` + +### children 获取专辑中的歌曲 + +GET: `[host]/library/metadata/[id]/children` + +query: + +| 参数名 | 备注 | +| --- | --- | +| excludeAllLeaves | `1` | + +### items 获取歌单中的歌曲 + +GET: `[host]/playlists/[id]/items` + +query: + +| 参数名 | 备注 | +| --- | --- | +| type | `track` | + +response: + +```xml + + + + + + + + + + + + + +``` + +### 收藏歌曲/专辑/歌手 + +Plex 没有收藏功能,建议筛选评分为 10 的作为收藏项目。 + +### transcode 歌曲播放链接 + +**不转码** + +首先需要获取歌曲信息中的 `Track.Media.Part.key` + +GET: `[host]/[partKey]` + +query: + +| 参数名 | 备注 | +| --- | --- | +| X-Plex-Token | 访问令牌 | +| X-Plex-Platform | 操作系统,可选值:`Web`, `Android`, `iOS`, `MacOSX`, `Windows`, `Linux` | + +**转码** + +GET: `[host]/music/:/transcode/universal/start` + +query: + +| 参数名 | 备注 | +| --- | --- | +| path | `/library/metadata/[id]` | +| directPlay | `0` | +| musicBitrate | 比特率 | +| session | 会话ID,客户端随机生成 | +| X-Plex-Token | 访问令牌 | +| X-Plex-Platform | 操作系统,可选值:`Web`, `iOS` | + +:::note + +转码还有更详细的配置,这里记录是直接返回转码后的文件的链接,若需 hls 等其他协议,请参考顶部列出的参考文档。 + +::: + +### items 添加歌曲到歌单 + +PUT: `[host]/playlists/[id]/items` + +query: + +| 参数名 | 备注 | +| --- | --- | +| uri | `server://[machineIdentifier]/com.plexapp.plugins.library/library/metadata/[歌曲ID列表]` | + +### items 从歌单中移除歌曲 + +DELETE: `[host]/playlists/[id]/items/[songId]` + +:::note + +此接口只能单个移除。 + +::: + +### playlists 更新歌单信息 + +PUT: `[host]/playlists/[id]` + +query: + +| 参数名 | 备注 | +| --- | --- | +| title | 歌单名 | +| summary | 评论,可选 | \ No newline at end of file diff --git a/i18n/en/docusaurus-plugin-content-docs/current/notes/services/subsonic.md b/i18n/en/docusaurus-plugin-content-docs/current/notes/services/subsonic.md new file mode 100644 index 0000000..97696e7 --- /dev/null +++ b/i18n/en/docusaurus-plugin-content-docs/current/notes/services/subsonic.md @@ -0,0 +1,632 @@ +--- +sidebar_position: 1 +--- + +# Subsonic 接口文档 + +:::note 参考文档: + +- [Subsonic](https://www.subsonic.org/pages/api.jsp) +- ~~[OpenSubsonic](https://opensubsonic.netlify.app/docs/)~~ + +::: + +## 认证 + +所有请求均需携带以下参数: + +| 参数名 | 是否必填 | 默认值 | 备注 | +| --- | --- | --- | --- | +| u | Y | | 用户名 | +| t | Y | | 以md5(密码+盐)计算出的身份验证令牌 | +| s | Y | | 盐(随机生成,至少6位) | +| v | Y | | 客户端实现的协议版本 | +| c | Y | | 客户端名称 | +| f | N | xml | 返回格式,可选值: `xml`, `json`, `jsonp`。 | + +示例: +```js +password = 'sesame' +salt = 'c19b2d' +token = md5('sesamec19b2d') = '26719a1196d2a940705a59634eb18eab' +url = 'http://your-server/rest/ping.view?u=joe&t=26719a1196d2a940705a59634eb18eab&s=c19b2d&v=1.12.0&c=myapp' +``` + +## 错误代码 + +| 代码 | 备注 | +| --- | --- | +| 0 | 一般性错误 | +| 10 | 缺少参数 | +| 20 | 客户端版本过低 | +| 30 | 服务端版本过低 | +| 40 | 用户名或密码错误 | +| 50 | 无权限 | +| 60 | Subsonic 试用期已结束 | +| 70 | 未找到请求的数据 | + +示例: + +```xml + + + + +``` + +## 请求节点 + +以下节点中,凡是允许填写多个值的,均为以`,`分割的。 + +:::tip + +因 URL 长度限制,批量处理时最好分批请求。 + +::: + +### getAlbumList2 获取专辑列表 + +请求地址:`http://your-server/rest/getAlbumList2` + +最低版本:1.8.0 + +| 参数名 | 是否必填 | 默认值 | 备注 | +| --- | --- | --- | --- | +| type | Y | | 排序方式,可选值: `random`, `newest`, `frequent`, `recent`, `starred`, `alphabeticalByName`, `alphabeticalByArtist`. 特殊值: `byYear`, `byGenre` | +| size | N | 10 | 返回结果数量,最大值 500 | +| offset | N | 0 | 偏移数量,用于分页获取数据 | +| fromYear | Y(type = byYear) | | 年代最小值,若 最小值大于最大值,将倒序返回结果 | +| toYear | Y(type = byYear) | | 年代最大值 | +| genre | Y(type = byGenre) | | 流派 | +| musicFolderId | N | | | + +示例: + +```xml + + + + + + + + + + + + + + +``` + +### getAlbum 获取专辑信息及音轨列表 + +请求地址:`http://your-server/rest/getAlbum` + +最低版本:1.8.0 + +| 参数名 | 是否必填 | 默认值 | 备注 | +| --- | --- | --- | --- | +| id | Y | | 专辑id | + +示例: + +```xml + + + + + + + + + + + + +``` + +### getArtist 获取歌手信息及专辑列表 + +请求地址:`http://your-server/rest/getArtist` + +最低版本:1.8.0 + +| 参数名 | 是否必填 | 默认值 | 备注 | +| --- | --- | --- | --- | +| id | Y | | 歌手id | + +示例: + +```xml + + + + + + + + + + + + + + + + + + + +``` + +### getArtistInfo2 获取歌手简介和相似歌手列表 + +请求地址:`http://your-server/rest/getArtistInfo2` + +最低版本:1.11.0 + +| 参数名 | 是否必填 | 默认值 | 备注 | +| --- | --- | --- | --- | +| id | Y | | 歌手id | +| count | N | | 相似歌手数量 | +| includeNotPresent | N | | 是否返回媒体库不存在的歌手 | + +示例: + +```xml + + + Black Sabbath is an English + band that formed in 1968 in Birmingham, West Midlands, England, United Kingdom, originally comprising + Ozzy Osbourne (vocals), + Tony Iommi (guitar), + Geezer Butler (bass), and + Bill Ward (drums). In the early + , they were the first to pair heavily distorted, sonically dissonant + at slow speeds with lyrics about drugs, mental pain and abominations of war, thus giving birth to generations of metal bands that followed in their wake. + Read more about Black Sabbath on Last.fm. + + 5182c1d9-c7d2-4dad-afa0-ccfeada921a8 + http://www.last.fm/music/Black+Sabbath + http://userserve-ak.last.fm/serve/64/27904353.jpg + http://userserve-ak.last.fm/serve/126/27904353.jpg + http://userserve-ak.last.fm/serve/_/27904353/Black+Sabbath+sabbath+1970.jpg + + + + + + + + + + +``` + +### getIndexes 获取歌手列表 + +请求地址:`http://your-server/rest/getIndexes` + +最低版本:1.0.0 + +| 参数名 | 是否必填 | 默认值 | 备注 | +| --- | --- | --- | --- | +| musicFolderId | N | | | +| ifModifiedSince | N | | 返回给定时间之后发生修改的歌手列表,单位:毫秒,自1970.1.1之后的差值 | + +示例: + +```xml + + + + + + + + + + + + + + + + +``` + +### getCoverArt 封面图片 + +请求地址:`http://your-server/rest/getCoverArt` + +最低版本:1.0.0 + +| 参数名 | 是否必填 | 默认值 | 备注 | +| --- | --- | --- | --- | +| id | Y | | 歌曲/专辑/歌手id,其中专辑id应添加前缀 `al-`,歌手id应添加前缀 `ar-` | +| size | N | | 图片大小,示例:`600` | + +### createPlaylist 创建或更新歌单名 + +请求地址:`http://your-server/rest/createPlaylist` + +最低版本:1.2.0 + +| 参数名 | 是否必填 | 默认值 | 备注 | +| --- | --- | --- | --- | +| playlistId | Y(更新时填写) | | 歌单id | +| name | Y(创建时填写) | | 歌单名 | + +早期版本返回空的 ` `, 自 1.14.0 之后会返回创建/更新后的歌单。 + +### deletePlaylist 删除歌单 + +请求地址:`http://your-server/rest/createPlaylist` + +最低版本:1.2.0 + +| 参数名 | 是否必填 | 默认值 | 备注 | +| --- | --- | --- | --- | +| id | Y | | 歌单id | + +### ping 测试服务连通性或登录 + +请求地址:`http://your-server/rest/ping` + +最低版本:1.0.0 + +### getGenres 获取全部类型 + +请求地址:`http://your-server/rest/getGenres` + +最低版本:1.9.0 + +示例: + +```xml + + + Electronic + Hard Rock + R&B + Blues + Podcast + Brit Pop + Live + + +``` + +### getLyrics 获取歌词 + +请求地址:`http://your-server/rest/getLyrics` + +最低版本:1.2.0 + +| 参数名 | 是否必填 | 默认值 | 备注 | +| --- | --- | --- | --- | +| artist | N | | 歌手名 | +| title | N | | 歌曲名 | + +示例: + +```xml + + It's bugging me Grating me And twisting me around Yeah I'm endlessly Caving in And turning inside out Cause I want it now I want it now Give me your heart and your soul And I'm breaking out I'm breaking out That's when she'll lose control It's holding me Morphing me And forcing me to strive To be endlessly Cold within And dreaming I'm alive Cause I want it now I want it now Give me your heart and your soul I'm not breaking down I'm breaking out That's when she'll lose control And I want you now I want you now I'll feel my heart implode And I'm breaking out Escaping now Feeling my faith erode + +``` + +### getPlaylists 获取歌单列表 + +请求地址:`http://your-server/rest/getPlaylists` + +最低版本:1.0.0 + +| 参数名 | 是否必填 | 默认值 | 备注 | +| --- | --- | --- | --- | +| username | N | | 获取某个用户名下的歌单列表,使用此参数的用户必须有管理员权限 | + +示例: + +```xml + + + + sindre + john + + + + +``` + +### setRating 评分 + +请求地址:`http://your-server/rest/setRating` + +最低版本:1.6.0 + +| 参数名 | 是否必填 | 默认值 | 备注 | +| --- | --- | --- | --- | +| id | Y | | 歌曲/专辑/歌手id | +| rating | Y | | 分数,范围:0-5 | + +### getScanStatus 获取扫描状态 + +请求地址:`http://your-server/rest/getScanStatus` + +最低版本:1.15.0 + +示例: + +```xml + + + +``` + +### scrobble 滚动播放记录 + +请求地址:`http://your-server/rest/scrobble` + +最低版本:1.5.0 + +| 参数名 | 是否必填 | 默认值 | 备注 | +| --- | --- | --- | --- | +| id | Y | | 歌曲id | +| time | N | | 播放时间,自1970以来的毫秒数 | +| submission | N | True | 是否提交,不提交则表示正在播放 | + +### search2 搜索歌曲/专辑/歌手 + +请求地址:`http://your-server/rest/search2` + +最低版本:1.4.0 + +| 参数名 | 是否必填 | 默认值 | 备注 | +| --- | --- | --- | --- | +| query | Y | | 关键词,传 `""` 时可用于获取全部数据 | +| artistCount | N | 20 | 返回的最大歌手数量 | +| artistOffset | N | 0 | 歌手列表偏移量 | +| albumCount | N | 20 | 返回的最大专辑数量 | +| albumOffset | N | 0 | 专辑列表偏移量 | +| songCount | N | 20 | 返回的最大歌曲数量 | +| songOffset | N | 0 | 歌曲列表偏移量 | +| musicFolderId | N | | | + +示例: + +```xml + + + + + + + + +``` + +### getSongsByGenre 获取某个类型的歌曲列表 + +请求地址:`http://your-server/rest/getSongsByGenre` + +最低版本:1.9.0 + +| 参数名 | 是否必填 | 默认值 | 备注 | +| --- | --- | --- | --- | +| genre | Y | | 类型名 | +| count | N | 10 | 返回的歌曲数量,最大 500 | +| offset | N | 0 | 起始行数,用于分页 | + +示例: + +```xml + + + + + + + + +``` + +### getSimilarSongs 获取相似歌曲 + +请求地址:`http://your-server/rest/getSimilarSongs` + +最低版本:1.11.0 + +| 参数名 | 是否必填 | 默认值 | 备注 | +| --- | --- | --- | --- | +| id | Y | | 歌手/专辑/歌曲id | +| count | N | 50 | 返回的歌曲数量 | + +示例: + +```xml + + + + + + + + + + + + + + +``` + +### getRandomSongs 随机获取歌曲 + +请求地址:`http://your-server/rest/getRandomSongs` + +最低版本:1.2.0 + +| 参数名 | 是否必填 | 默认值 | 备注 | +| --- | --- | --- | --- | +| size | N | 10 | 结果数量,最大500条 | + +示例: + +```xml + + + + + + + + + + +``` + +### getStarred 获取收藏歌曲/专辑/歌手 + +请求地址:`http://your-server/rest/getStarred` + +最低版本:1.8.0 + +### getSong 获取歌曲信息 + +请求地址:`http://your-server/rest/getSong` + +最低版本:1.8.0 + +| 参数名 | 是否必填 | 默认值 | 备注 | +| --- | --- | --- | --- | +| id | Y | | 歌曲id | + +示例: + +```xml + + + +``` + +### getTopSongs 获取歌手的热门歌曲 + +请求地址:`http://your-server/rest/getTopSongs` + +最低版本:1.13.0 + +| 参数名 | 是否必填 | 默认值 | 备注 | +| --- | --- | --- | --- | +| artist | Y | | 歌手名 | +| count | N | 50 | 返回的歌曲数量 | + +示例: + +```xml + + + + + + + +``` + +### getPlaylist 获取歌单中的歌曲 + +请求地址:`http://your-server/rest/getPlaylist` + +最低版本:1.0.0 + +| 参数名 | 是否必填 | 默认值 | 备注 | +| --- | --- | --- | --- | +| id | Y | | 歌单id | + +示例: + +```xml + +