xiaomusic 同步网易云歌单实战:从歌单 ID 到小爱音箱语音播放
【免费下载链接】xiaomusic使用小爱音箱播放音乐,音乐使用 yt-dlp 下载。项目地址: https://gitcode.com/GitHub_Trending/xia/xiaomusic
本篇技术指南聚焦 xiaomusic 的“网易云歌单同步”主题:讲解如何借助独立工具netease-playlist通过网易云歌单 ID 下载歌曲或生成 xiaomusic 可识别的歌单配置,并深入剖析官方推荐的替代实现——自定义口令插件方案,让你彻底读懂"歌单 ID → 歌单数据 → 小爱音箱播放"的完整链路。读完本文,你将掌握配置自定义口令、编写 Python 插件拉取歌单、以及调用 docs/issues/269.md 中社区验证过的getmy_playlist插件的完整实战能力。
一、方案概览:三条通往网易云歌单的路径
在 xiaomusic 中接入网易云歌单,社区验证过的主要有下面三种方式:
| 方案 | 实现形态 | 适合场景 |
|---|---|---|
netease-playlist独立工具 | 独立模块,可 Docker 部署 | 一次性/批量下载歌曲,或离线生成歌单配置 |
| 自定义口令插件 | 将插件代码挂在exec#口令下 | 语音实时拉取歌单并播放,可配合米家自动化 |
| 网络歌单 JSON(官方原生) | 在设置页配置歌单 JSON / JSON URL | 电台、m3u8、B 站等在线流媒体歌单 |
三条路径并非互斥:你完全可以用netease-playlist把歌单导出成 自定义网络歌单格式 后填入设置页,也可以用插件方案实现"把歌曲扔进网易云歌单 → 喊一句口令 → 小爱音箱随机播放"的实时同步体验。
二、独立工具 netease-playlist:歌单 ID 驱动下载与配置生成
Issue 312 的核心内容是推广独立模块netease-playlist。它的能力可以用一句话概括:
通过网易云音乐歌单 ID,下载歌曲,或生成 xiaomusic 可用的歌单配置。
2.1 在 xiaomusic 体系中的定位
原文档明确说明了两点定位:
- 独立模块,可 Docker 部署——它不修改 xiaomusic 本体,不依赖 xiaomusic 的插件机制;
- 可作为不用插件的另外实现方式——如果你不想维护插件代码,可以只靠这个工具完成任务。
也就是说,netease-playlist与 xiaomusic 的耦合点是"歌单 ID"与"歌单配置 JSON",前者是输入,后者是输出。
2.2 它生成的歌单配置长什么样
netease-playlist生成的目标格式,就是 xiaomusic 原生支持的自定义网络歌单 JSON。参考 docs/issues/78.md 中官方给出的结构:
[ { "name": "歌单1", "musics": [ { "name": "歌名1", "url": "http://.../index.m3u8", "type": "radio" }, { "name": "歌名2", "url": "https://.../64k.mp3" } ] } ]要点:
- 顶层是歌单数组,每个元素含
name(歌单名)与musics(歌曲数组); - 每首歌含
name与url两个必填字段; type为radio时表示电台流,会一直播放当前源,不切下一首;url可以是直链、m3u8 电台地址,也可以是self:///api/proxy/plugin-url?data=开头的在线音乐插件代理链接(v0.4.15 起支持,需配合 OnlineSearch 的 JS 插件并开启“网络歌曲过代理”开关)。
拿到这份 JSON 后,有两种落地方式:
- 直接粘贴到 xiaomusic 设置页的歌单 JSON 输入框;
- 把 JSON 上传到 gist / GitHub / Gitee 等可直链分享的地址,在JSON URL 输入框里填入 raw 链接,点击“获取”按钮自动填充。
三、配置生成的两种入口:设置页与 config 文件
官方在 docs/issues/105.md 中明确提示:“建议通过插件实现或者新增一个页面工具把歌单导出 json,歌单的 json 格式见 /issues/78”。
对应的配置字段在config-example.json中体现为三个相关项:
"music_list_url": "", "music_list_json": "", "custom_play_list_json": ""其中music_list_json/custom_play_list_json即歌单 JSON 的注入位置,music_list_url则是 JSON 文件的远程地址。设置页上对应的就是“歌单 JSON 输入框”与“JSON URL 输入框”。
需要注意的是,社区在实战中反馈:最好在setting.json里配置,网页端配置有时会出现匹配不上的情况(见 Issue 269 评论 13)。所以一个稳妥的做法是:先用设置页在线验证歌单可用,再把最终 JSON 固化进配置文件。
四、进阶方案:用自定义口令插件实现实时歌单同步
netease-playlist是离线/半离线方案,做不到"往歌单扔歌 → 音箱马上能播"。社区验证过的实时方案是自定义口令插件。这正是 Issue 105 讲解的功能,也是 Issue 312 中"不用插件"的另一面。
4.1 自定义口令的运行原理
从源码看,整个链路是这样的:
- 配置注入:在
config.py的Config模型中,user_key_word_dict与key_word_dict都是独立的配置字段(config.py)。调用init()时,append_user_keyword()会把user_key_word_dict中每个k: v合并进key_word_dict,并追加进key_match_order匹配优先级表(config.py); - 口令解析:
command_handler.py中,无论完全匹配还是模糊匹配,只要opvalue以exec#开头,就把#之后的部分当作可执行代码返回(command_handler.py); - 插件执行:
XiaoMusic.exec()拿到代码后调用plugin_manager.execute_plugin(code)(xiaomusic.py); - 代码校验:
PluginManager先用ast.parse校验代码必须是“直接调用插件函数的表达式”,支持字符串/数字/布尔/None/列表/元组/字典作为参数,不支持关键字参数(plugin.py); - 动态加载:启动时
PluginManager遍历plugins/包内所有模块,动态导入并取出"与文件名同名"的函数注册进_funcs(plugin.py)。因此code1对应 plugins/code1.py,httpget对应 plugins/httpget.py; - 同步/异步兼容:
execute_plugin通过inspect.iscoroutinefunction判断,异步函数await调用,同步函数直接调用(plugin.py)。
4.2 最小可运行配置
以官方插件示例code1为例,配置user_key_word_dict即可(会自动插入key_word_dict):
{ "user_key_word_dict": { "测试自定义口令": "exec#code1(\"hello\")", "测试链接": "exec#httpget(\"https://github.com/hanxi/xiaomusic\")" } }同时必须在active_cmd中配上口令对应的动作值用于唤醒:
"active_cmd": "play,set_random_play,playlocal,play_music_list,play_music_list_index,stop_after_minute,stop,测试自定义口令"active_cmd的作用(见 Issue 105 评论 20):决定在 xiaomusic 未播放时,哪些命令可以唤醒 xiaomusic 接管处理。从 command_handler.py 看,当设备未在播放且非控制面板来源时,若opvalue与opkey都不在active_cmd_arr中,该命令会被忽略。
插件本体放在plugins/目录下,文件内的函数名必须与文件名一致。仓库自带的官方示例 plugins/code1.py 展示了如何拿到用户语音原文并让音箱回读:
async def code1(arg1): global log, xiaomusic log.info(f"code1:{arg1}") did = xiaomusic.get_cur_did() await xiaomusic.do_tts(did, "你好,我是自定义的测试口令") query = xiaomusic.command_handler.last_cmd.strip() await xiaomusic.do_tts(did, f"你说的是: {query}")关键点:
global log, xiaomusic是插件运行时的约定注入变量(由PluginManager._load_plugins注入模块命名空间);xiaomusic.get_cur_did()获取当前说话设备(xiaomusic.py);xiaomusic.command_handler.last_cmd保存了最近一次语音输入原文(command_handler.py),插件内部自行做前缀切割即可实现“口令 + 参数”的玩法;do_tts(did, value)让指定设备说话(xiaomusic.py),早期版本签名是do_tts(value),重构后必须传did(Issue 105 评论 3 正是这个报错)。
httpget则演示了"用语音触发一次 HTTP 请求",可用来对接任意 REST 服务:
import requests def httpget(url): global log response = requests.get(url, timeout=5) # 增加超时以避免长时间挂起 response.raise_for_status() # 如果响应不是200,引发HTTPError异常 log.info(f"httpget url:{url} response:{response.text}")容器部署时需将plugins/目录挂载出来,才能让自定义插件文件生效。
五、社区验证的完整方案:getmy_playlist 插件
Issue 269 的评论 13 给出了经过真机验证的完整方案:一个名为getmy_playlist.py的插件,通过 NeteaseCloudMusicApi 拉取歌单并注入 xiaomusic 内存,让"网易云歌单"直接变为"语音歌单"。
5.1 它的工作方式
插件核心逻辑(要点提炼自 Issue 269 中的完整代码):
- 支持两种入参:
uid(拉取该用户全部歌单)与playlist_id(拉取单个歌单); - 通过
{api_host}/user/playlist?uid={uid}获取用户歌单列表,再对每个歌单调用{api_host}/playlist/detail?id={id}获取曲目; - 对每首歌构造播放地址
{api_host}/song/url?id={id}&br=350000&realIP=...&proxy=...; - 写入三个关键数据结构:
xiaomusic.all_music[name] = url:把歌曲名映射到播放地址;xiaomusic.all_music_tags[name] = {...}:写入标题、歌手、专辑、封面等元数据(picture用music['al']['picUrl']);xiaomusic.music_list[list_name] = one_music_list:把歌单名映射到歌曲名列表;
- 最后调用
xiaomusic.try_save_tag_cache()保存标签缓存。
5.2 关键参数与接线方式
在setting.json中用user_key_word_dict绑定口令(注意 Issue 269 中演示的口令动作值是exec#getmy_playlist(playlist_id=12758992225)):
"user_key_word_dict": { "获取歌单": "exec#getmy_playlist(playlist_id=12758992225)" }同时在active_cmd末尾追加获取歌单(示例完整active_cmd见 Issue 269 评论 13)。部署要点如下:
| 事项 | 说明 |
|---|---|
| API 服务 | 本地/局域网运行 NeteaseCloudMusicApi(可用gnehs/neteasecloudmusicapi-docker镜像部署) |
| 播放地址 | 用song/url接口按歌曲 ID 换直链;proxy=HTTP:%2F%2F127.0.0.1:8080参数配合 UnblockNeteaseMusic 解锁灰色歌曲 |
| 触发方式 | 对小爱音箱说"获取歌单",或将此口令挂到米家"我回来了"智能场景自动触发 |
| 局限性 | 歌曲地址时效性强;网易侧 API 变动可能导致取不到播放地址(见 Issue 269 评论 15-17),此时可退化为"用 yt-dlp 全量下载后本地播放" |
5.3 为什么推荐"生成 JSON 再提交"
Issue 269 的作者 hanxi 在评论 5 中的评价值得注意:直接改接口强改歌单保存逻辑不太通用,更通用的做法是生成 JSON,再用现有接口提交 JSON。这与 Issue 312 中netease-playlist"生成歌单配置"的思路完全一致——把 xiaomusic 当作消费方,而不是去改它的内部逻辑。
六、常见问题与避坑清单
综合 Issue 312 / 269 / 105 的社区反馈,整理以下实战避坑点:
- 口令不触发:检查
active_cmd是否已包含该口令对应的值或口令本身(评论多次确认"需要在 active_cmd 里配一下才能正常唤醒"); - 自定义口令无法覆盖默认口令:
user_key_word_dict只能以插入方式添加,不能删除系统默认口令(如"播放歌曲");如需改动,去网页后台设置页改(Issue 105 评论 14-17); - 插件函数卡死整个服务:插件内不要写阻塞型死循环;长任务用
asyncio.sleep让步,并另写一个口令杀掉对应 task(Issue 105 评论 25-29); do_tts报缺少value参数:旧版本签名问题,升级到修复后的版本即可;- 拿不到播放地址:网易 API 变动会导致
song/url失效,需要更新 API 服务或改用下载方案(Issue 269 评论 15-17); - Docker 部署注意:插件目录要挂载;ARM 设备需确认镜像是否提供对应架构(Issue 269 评论 19-21);
- 歌曲时长获取不到:部分 m4a / 流媒体格式无法解析时长,会影响自动切歌,可配合
remove_id3tag、convert_to_mp3等配置规避(Issue 78 评论 7-13)。
七、总结
围绕"网易云歌单同步"这一需求,xiaomusic 生态给出了从轻到重的完整方案谱系:
- 只想下载/离线播放,用独立工具netease-playlist(Issue 312 推荐);
- 想要实时同步、语音可控,写一个自定义口令插件(Issue 105 + 269 的
getmy_playlist是经过真机验证的参考实现); - 播放电台、m3u8 或在线流媒体,直接用网络歌单 JSON原生能力(Issue 78)。
无论选哪条路,其底层都共享同一套契约:把歌单数据规整为"歌单名 → 歌曲名 → 播放地址"结构,要么通过配置文件、要么通过all_music/music_list等内存结构注入 xiaomusic。理解这条契约,你就能把 QQ 音乐、Apple Music 等任何平台歌单用同样的思路接入小爱音箱。
相关参考:
- 插件运行机制源码
- 口令匹配源码
- 配置合并源码
- 官方示例插件 code1 / httpget
- 歌单 JSON 格式说明
【免费下载链接】xiaomusic使用小爱音箱播放音乐,音乐使用 yt-dlp 下载。项目地址: https://gitcode.com/GitHub_Trending/xia/xiaomusic
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考