--- title: 如何添加 网易云音乐playlist --- # 如何添加 网易云音乐playlist 利用 NeteaseCloudMusicApi 获取歌单和播放地址 我在你基础上改了一下,但是我的逻辑不合理 https://github.com/dissipator/xiaomusic ## 评论 ### 评论 1 - hanxi 建议通过插件实现或者新增一个页面工具把歌单导出 json 。 歌单的 json 格式见 /issues/78.html --- ### 评论 2 - qiujie8092916 > 利用 NeteaseCloudMusicApi 获取歌单和播放地址 我在你基础上改了一下,但是我的逻辑不合理 [dissipator/xiaomusic](https://github.com/dissipator/xiaomusic) 老哥在实现插件了吗?我也急需播放歌单 我诉求是:我用网易云音乐单独新建了一个歌单,我往里面扔歌曲,以更新歌单。希望创建一个自定义的语音命令,让小爱同学随机播放这个歌单里的音乐。然后我通过在米家里执行,比如触发了「我回来了」的智能场景时,就让小爱音箱执行这个自定义的语音命令,就会自动播放我新建的这个歌单里的音乐了。 现在的小爱音箱虽然能勉强实现,但是很垃圾。随机播放的随机性有问题,并且只能选择歌单里的 30 首歌。 (老哥如果没实现的话,我可以尝试搞搞) --- ### 评论 3 - dissipator 我都能直接利用这个直接播放NeteaseCloudMusicApi这上的歌了。在配合unblk,无敌。我就是没设备,还在路上 通过插件实现,非常好,就是不知道怎么开发,有文档我可以尝试一下。 --- ### 评论 4 - dissipator > 建议通过插件实现或者新增一个页面工具把歌单导出 json 。 歌单的 json 格式见 #78 配置处直接填了api的接口 http://127.0.0.1:3000/playlist/detail?id=12758992226 我是直接再你歌单保存上强改的。 ```python async def downloadjson(data: UrlInfo, Verifcation=Depends(verification)): log.info(data) url = data.url content = "[{" host = f"{url.split('/')[0]}//{url.split('/')[2]}" try: ret = "OK" jsons = await downloadfile(url,"json") # print(jsons) list_name = jsons['playlist']['name'] content += '"name":"'+list_name+'","musics":[' for song in jsons['playlist']['tracks']: content += f"{{\"name\":\"{song['name']}\",\"url\": \"{host}/song/url?br=999000&proxy=http:%2F%2F127.0.0.1:8080&realIP=211.161.244.70&id={song['id']}\"}}" except Exception as e: log.exception(f"Execption {e}") ret = "Download JSON file failed." content = content[:-1] + "]}]" ``` 照着你的说明。而且能成功播发 ```python @app.get("/musicinfo") async def musicinfo( name: str, musictag: bool = False, Verifcation=Depends(verification) ): url = xiaomusic.get_music_url(name) if("song/url" in url): jsons = await downloadfile(url,"json") url = jsons['data'][0]['url'] ``` 播放处加了一个判断 --- ### 评论 5 - hanxi > > 建议通过插件实现或者新增一个页面工具把歌单导出 json 。 歌单的 json 格式见 #78 你的修改我看了,不太通用。生成json,再用现有的接口提交json更通用。 --- ### 评论 6 - dissipator 是的,不通用。最好是用插件实现。 1. 就是不知道你插件的逻辑。 2. 如果用插件就考虑直接读取网易账号下所有歌单。然后选择一个导入,或者全部导入。 3. 等你完善文档后我可以尝试写一个。同理,qq等其他平台的歌单也就都可以弄了 --- ### 评论 7 - hanxi 等有空我写个修改歌单内容的插件示例吧。 --- ### 评论 8 - dissipator ``` import requests def getmy_playlist(type="netease",api_host="http://127.0.0.1/api", playlist_id=None,uid=None): """ Purpose: """ global log, xiaomusic if type == "netease": if uid: api_url = f"{api_host}/user/playlist?uid={uid}" # 发起请求 response = requests.get(api_url, timeout=5) # 增加超时以避免长时间挂起 response.raise_for_status() # 如果响应不是200,引发HTTPError异常 # log.info(f"getmy_playlist url:{api_url} response:{response.text}") music_list = response.json() for item in music_list['playlist']: list_name = item.get("name") log.info(f"getmy_playlist name:{list_name}") # if item.get("id") in [12709941656,]: songs_url = f"{api_host}/playlist/detail?id={item['id']}" # 发起请求 response = requests.get(songs_url, timeout=5) # 增加超时以避免长时间挂起 response.raise_for_status() # 如果响应不是200,引发HTTPError异常 # log.info(f"getmy_playlist url:{api_url} response:{response.text}") musics = response.json() one_music_list = [] for music in musics['playlist']['tracks']: if (not music): continue # try: name = music['name'] picUrl = music['al']['picUrl'] artist = music['ar'][0]['name'] album = music['al']['name'] name = music.get("name") url = f"{api_host}/song/url?id={music['id']}&br=350000&realIP=211.161.244.70&proxy=HTTP:%2F%2F127.0.0.1:8080" if (not name) or (not url): continue xiaomusic.all_music[name] = url xiaomusic.all_music_tags[name] = { "title": name, "artist": artist, "album": album, "year": "", "genre": "", "picture": picUrl, "lyrics": "" } one_music_list.append(name) log.debug(f"getmy_playlist name:{list_name}") log.debug(one_music_list) # 歌曲名字相同会覆盖 xiaomusic.music_list[list_name] = one_music_list xiaomusic.try_save_tag_cache() log.debug(xiaomusic.all_music) log.debug(xiaomusic.music_list) return if playlist_id: songs_url = f"{api_host}/playlist/detail?id={playlist_id}" # 发起请求 response = requests.get(songs_url, timeout=5) # 增加超时以避免长时间挂起 response.raise_for_status() # 如果响应不是200,引发HTTPError异常 # log.info(f"getmy_playlist url:{api_url} response:{response.text}") musics = response.json() list_name = musics['playlist']['name'] one_music_list = [] for music in musics['playlist']['tracks']: if (not music): continue # try: name = music['name'] picUrl = music['al']['picUrl'] artist = music['ar'][0]['name'] album = music['al']['name'] name = music.get("name") url = f"{api_host}/song/url?id={music['id']}&br=350000&realIP=211.161.244.70&proxy=HTTP:%2F%2F127.0.0.1:8080" if (not name) or (not url): continue xiaomusic.all_music[name] = url xiaomusic.all_music_tags[name] = { "title": name, "artist": artist, "album": album, "year": "", "genre": "", "picture": picUrl, "lyrics": "" } one_music_list.append(name) log.debug(f"getmy_playlist name:{list_name}") log.debug(one_music_list) # 歌曲名字相同会覆盖 xiaomusic.music_list[list_name] = one_music_list xiaomusic.try_save_tag_cache() log.debug(xiaomusic.all_music) log.debug(xiaomusic.music_list) return else: log.error(f"getmy_playlist type:{type} not support") ``` --- ### 评论 9 - dissipator > 等有空我写个修改歌单内容的插件示例吧。 不用拉,插件我已经写出来了 ![image](https://github.com/user-attachments/assets/53e593e7-1439-4968-9549-8c84b2fee42c) --- ### 评论 10 - hanxi 发下你的 setting.json 配置吧,方便他人知道怎么配。 --- ### 评论 11 - dissipator 我还在测试,设备到了能用了再发吧 --- ### 评论 12 - guitarbug 也需要网易歌单功能, 坐等教程 --- ### 评论 13 - dissipator # 成功了 ![image](https://github.com/user-attachments/assets/33e22665-b1cc-4069-9e8c-46e466679b30) # 最好是在setting.json里去配置,我测试老是不匹配,在setting.json里去配置终于成功。可以直接调用 [NeteaseCloudMusicApi](http://localhost:3000/docs/#/?id=neteasecloudmusicapi) 接口,甚至使用UnblockNeteaseMusic 解锁灰色,但是代码需要小调整。 setting.json ```json { "account": "", "password": "", "mi_did": "", "miio_tts_command": "", "cookie": "", "verbose": false, "music_path": "music", "download_path": "music/download", "conf_path": "conf", "cache_dir": "cache", "hostname": "192.168.2.5", "port": 8090, "public_port": 0, "proxy": "", "search_prefix": "bilisearch:", "ffmpeg_location": "./ffmpeg/bin", "active_cmd": "play,set_random_play,playlocal,play_music_list,play_music_list_index,stop_after_minute,stop,获取歌单", "exclude_dirs": "@eaDir,tmp", "music_path_depth": 10, "disable_httpauth": true, "httpauth_username": "", "httpauth_password": "", "music_list_url": "", "music_list_json": "", "custom_play_list_json": "", "disable_download": false, "key_word_dict": { "播放歌曲": "play", "播放本地歌曲": "playlocal", "关机": "stop", "下一首": "play_next", "上一首": "play_prev", "单曲循环": "set_play_type_one", "全部循环": "set_play_type_all", "随机播放": "set_random_play", "分钟后关机": "stop_after_minute", "播放列表": "play_music_list", "刷新列表": "gen_music_list", "加入收藏": "add_to_favorites", "收藏歌曲": "add_to_favorites", "取消收藏": "del_from_favorites", "播放列表第": "play_music_list_index", "本地播放歌曲": "playlocal", "放歌曲": "play", "暂停": "stop", "停止": "stop", "停止播放": "stop", "播放歌单": "play_music_list", "测试自定义口令": "exec#code1(\"hello\")", "测试链接": "exec#httpget(\"https://github.com/hanxi/xiaomusic\")", "获取歌单": "exec#getmy_playlist(playlist_id=12758992225)" }, "key_match_order": [ "分钟后关机", "播放歌曲", "下一首", "上一首", "单曲循环", "全部循环", "随机播放", "关机", "刷新列表", "播放列表第", "播放列表", "加入收藏", "收藏歌曲", "取消收藏", "播放本地歌曲", "本地播放歌曲", "放歌曲", "暂停", "停止", "停止播放", "播放歌单", "测试自定义口令", "测试链接", "获取歌单" ], "use_music_api": false, "use_music_audio_id": "1582971365183456177", "use_music_id": "355454500", "log_file": "/tmp/xiaomusic.txt", "fuzzy_match_cutoff": 0.6, "enable_fuzzy_match": true, "stop_tts_msg": "收到,再见", "enable_config_example": false, "keywords_playlocal": "播放本地歌曲,本地播放歌曲", "keywords_play": "播放歌曲,放歌曲", "keywords_stop": "关机,暂停,停止,停止播放", "keywords_playlist": "播放列表,播放歌单", "user_key_word_dict": { "测试自定义口令": "exec#code1(\"hello\")", "测试链接": "exec#httpget(\"https://github.com/hanxi/xiaomusic\")", "获取歌单": "exec#getmy_playlist(playlist_id=12758992225)" }, "enable_force_stop": false, "devices": { " ": { "did": " ", "device_id": " -17c6-4204- - ", "hardware": "L05C", "name": "小黑你好", "play_type": "", "cur_music": "", "cur_playlist": "" } }, "group_list": "", "remove_id3tag": false, "convert_to_mp3": false, "delay_sec": 3, "continue_play": false, "pull_ask_sec": 1, "crontab_json": "", "enable_yt_dlp_cookies": false, "get_ask_by_mina": false } ``` getmy_playlist.py ```python import requests async def getmy_playlist(type="netease",api_host="http://127.0.0.1/api", playlist_id=None,uid=None): """ Purpose: """ global log, xiaomusic if type == "netease": if uid: api_url = f"{api_host}/user/playlist?uid={uid}" # 发起请求 response = requests.get(api_url, timeout=5) # 增加超时以避免长时间挂起 response.raise_for_status() # 如果响应不是200,引发HTTPError异常 # log.info(f"getmy_playlist url:{api_url} response:{response.text}") music_list = response.json() for item in music_list['playlist']: list_name = item.get("name") log.info(f"getmy_playlist name:{list_name}") # if item.get("id") in [12709941656,]: songs_url = f"{api_host}/playlist/detail?id={item['id']}" # 发起请求 response = requests.get(songs_url, timeout=5) # 增加超时以避免长时间挂起 response.raise_for_status() # 如果响应不是200,引发HTTPError异常 # log.info(f"getmy_playlist url:{api_url} response:{response.text}") musics = response.json() one_music_list = [] for music in musics['playlist']['tracks']: if (not music): continue # try: name = music['name'] picUrl = music['al']['picUrl'] artist = music['ar'][0]['name'] album = music['al']['name'] name = music.get("name") url = f"{api_host}/song/url?id={music['id']}&br=350000&realIP=211.161.244.70&proxy=HTTP:%2F%2F127.0.0.1:8080" if (not name) or (not url): continue xiaomusic.all_music[name] = url xiaomusic.all_music_tags[name] = { "title": name, "artist": artist, "album": album, "year": "", "genre": "", "picture": picUrl, "lyrics": "" } one_music_list.append(name) log.debug(f"getmy_playlist name:{list_name}") log.debug(one_music_list) # 歌曲名字相同会覆盖 xiaomusic.music_list[list_name] = one_music_list xiaomusic.try_save_tag_cache() log.debug(xiaomusic.all_music) log.debug(xiaomusic.music_list) return if playlist_id: songs_url = f"{api_host}/playlist/detail?id={playlist_id}" # 发起请求 response = requests.get(songs_url, timeout=5) # 增加超时以避免长时间挂起 response.raise_for_status() # 如果响应不是200,引发HTTPError异常 musics = response.json() list_name = musics['playlist']['name'] log.info(f"getmy_playlist list_name:{list_name} ") one_music_list = [] for music in musics['playlist']['tracks']: if (not music): continue # try: name = music['name'] picUrl = music['al']['picUrl'] artist = music['ar'][0]['name'] album = music['al']['name'] name = music.get("name") url = f"{api_host}/song/url?id={music['id']}&br=350000&proxy=HTTP:%2F%2F127.0.0.1:8080" if (not name) or (not url): continue xiaomusic.all_music[name] = url xiaomusic.all_music_tags[name] = { "title": name, "artist": artist, "album": album, "year": "", "genre": "", "picture": picUrl, "lyrics": "" } one_music_list.append(name) log.debug(f"getmy_playlist name:{list_name}") log.debug(one_music_list) # 歌曲名字相同会覆盖 xiaomusic.music_list[list_name] = one_music_list xiaomusic.try_save_tag_cache() return else: log.error(f"getmy_playlist type:{type} not support") ``` --- ### 评论 14 - dissipator ![image](https://github.com/user-attachments/assets/7412eec4-f7d3-4a86-b186-0118d6f331ff) ![image](https://github.com/user-attachments/assets/2215e520-0d40-4c2c-8d46-d3106d65fc51) --- ### 评论 15 - dludream NeteaseCloudMusicApi似乎获取不到播放地址?其他信息倒是有。 ![2024-11-28_151952](https://github.com/user-attachments/assets/c7e7a748-aa82-47f0-8784-f6469cc3e99b) --- ### 评论 16 - hanxi 看代码是另一个接口获取url的: `url = f"{api_host}/song/url?id={music['id']}&br=350000&realIP=211.161.244.70&proxy=HTTP:%2F%2F127.0.0.1:8080"` --- ### 评论 17 - dludream > 看代码是另一个接口获取url的: `url = f"{api_host}/song/url?id={music['id']}&br=350000&realIP=211.161.244.70&proxy=HTTP:%2F%2F127.0.0.1:8080"` 是的,我用的是这个接口,https://registry.hub.docker.com/r/gnehs/neteasecloudmusicapi-docker/ 可能网易修改了api,这些获取不了播放地址了。 我倒不是要这个功能,只是感兴趣看看。我不获取,我直接用yt把歌全下载下来播放。 --- ### 评论 18 - dissipator 这个不是网易的接口,是[NeteaseCloudMusicApi](http://localhost:3000/docs/#/?id=neteasecloudmusicapi) 接口。proxy=HTTP:%2F%2F127.0.0.1:8080"是UnblockNeteaseMusic 解锁灰色; 完整的使用方式和docker 可以到 https://github.com/dissipator/xiaomusic/tree/dev 看README.md;目前没有教程。本人就在群2,有问题可以找我。 --- [链接到 GitHub Issue](https://github.com/hanxi/xiaomusic/issues/269)