Skip to content

Instantly share code, notes, and snippets.

@leafney
Created February 8, 2023 01:56
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save leafney/f14edffbc05902b75df7ddb4926b2777 to your computer and use it in GitHub Desktop.
Save leafney/f14edffbc05902b75df7ddb4926b2777 to your computer and use it in GitHub Desktop.
Cloud music player implemented by Node-Red and HomeAssistant
[{"id":"0d1d65f7ba1553ca","type":"tab","label":"云音乐播放器","disabled":false,"info":"","env":[]},{"id":"33759b693780be63","type":"inject","z":"0d1d65f7ba1553ca","name":"手动启动/停止","props":[],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","x":250,"y":240,"wires":[["dbc207429b6f6dc0"]]},{"id":"e9211b0583476c26","type":"function","z":"0d1d65f7ba1553ca","name":"设置音乐类别","func":"// 发起请求\nmsg.headers = {\n \"Content-Type\": \"application/json\"\n};\n\n// 随机类别 热歌榜|新歌榜|飙升榜|抖音榜|电音榜\nvar arr = ['热歌榜','热歌榜','新歌榜','飙升榜','抖音榜']\n\n// 随机获取一项\nvar random_type = arr[Math.floor(Math.random() * arr.length)];\n\nmsg.payload = {\n // \"sort\":\"热歌榜\",\n // 'mid':1, // 歌单ID\n \"sort\":random_type,\n \"format\":\"json\"\n}\n\n// 显示类别\nnode.status({fill:\"blue\",shape:\"dot\",text:random_type});\n\n\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":760,"y":340,"wires":[["722779911f306151"]]},{"id":"722779911f306151","type":"http request","z":"0d1d65f7ba1553ca","name":"请求音乐信息","method":"GET","ret":"obj","paytoqs":"query","url":"https://api.uomg.com/api/rand.music","tls":"","persist":false,"proxy":"","insecureHTTPParser":false,"authType":"","senderr":false,"headers":[],"x":760,"y":400,"wires":[["73636bcf6853a1f4"]]},{"id":"18b5f3a109361730","type":"api-call-service","z":"0d1d65f7ba1553ca","name":"播放音乐","server":"3cd209f0e02bc9b8","version":5,"debugenabled":false,"domain":"media_player","service":"play_media","areaId":[],"deviceId":[],"entityId":["media_player.mopidy"],"data":"msg.payload","dataType":"jsonata","mergeContext":"","mustacheAltTags":false,"outputProperties":[],"queue":"none","x":980,"y":620,"wires":[["33472c39781a68a1","c838eb7ba78b9a48"]]},{"id":"68fa5a5589d0413e","type":"function","z":"0d1d65f7ba1553ca","name":"解析音乐信息","func":"// 解析\nvar data = msg.data_info;\nvar url = msg.responseUrl;\n\n// 显示播放歌曲名称\nnode.status({fill:\"green\",shape:\"dot\",text:`${data.name}-${data.artistsname}`});\n\n// 设置播放器参数\nmsg.payload={\n \"media_content_id\":url,\n \"media_content_type\":\"music\",\n \"announce\":\"true\"\n}\n\n// 当前歌曲正常播放标识\nflow.set('cloud_music_played',true);\n\n// 等待一下,防止冲突\nsetTimeout(function(){\n // 设置启动标识 (用于切歌模式下,停止播放)\n global.set('cloud_music_start',true,'file');\n},2000);\n\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":760,"y":560,"wires":[["18b5f3a109361730","e5bd51033ff6c35e"]]},{"id":"55c7de619e4bcbf3","type":"trigger-state","z":"0d1d65f7ba1553ca","name":"上一曲播放完毕","server":"3cd209f0e02bc9b8","version":2,"exposeToHomeAssistant":false,"haConfig":[{"property":"name","value":""},{"property":"icon","value":""}],"entityid":"media_player.mopidy","entityidfiltertype":"exact","debugenabled":false,"constraints":[{"targetType":"this_entity","targetValue":"","propertyType":"previous_state","propertyValue":"old_state.state","comparatorType":"is","comparatorValueDatatype":"str","comparatorValue":"playing"},{"targetType":"this_entity","targetValue":"","propertyType":"current_state","propertyValue":"new_state.state","comparatorType":"is","comparatorValueDatatype":"str","comparatorValue":"off"}],"inputs":0,"outputs":2,"customoutputs":[],"outputinitially":false,"state_type":"str","enableInput":false,"x":260,"y":500,"wires":[["f6b40acd0cd79ecd"],[]]},{"id":"858eab131136b164","type":"api-call-service","z":"0d1d65f7ba1553ca","name":"停止播放","server":"3cd209f0e02bc9b8","version":5,"debugenabled":false,"domain":"media_player","service":"media_stop","areaId":[],"deviceId":[],"entityId":["media_player.mopidy"],"data":"","dataType":"jsonata","mergeContext":"","mustacheAltTags":false,"outputProperties":[],"queue":"none","x":980,"y":300,"wires":[["3be1104b41134ac0"]]},{"id":"c6b61ba2983f9cf0","type":"inject","z":"0d1d65f7ba1553ca","name":"手动切歌","props":[{"p":"payload"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"","payloadType":"str","x":240,"y":360,"wires":[["bba82e0449fc619d"]]},{"id":"f6b40acd0cd79ecd","type":"delay","z":"0d1d65f7ba1553ca","name":"限制频率","pauseType":"rate","timeout":"5","timeoutUnits":"seconds","rate":"1","nbRateUnits":"10","rateUnits":"second","randomFirst":"1","randomLast":"5","randomUnits":"seconds","drop":true,"allowrate":false,"outputs":1,"x":480,"y":460,"wires":[["35238c82ee9ad38b"]]},{"id":"7027b91f3df00544","type":"server-state-changed","z":"0d1d65f7ba1553ca","name":"双击启动/停止","server":"3cd209f0e02bc9b8","version":4,"exposeToHomeAssistant":false,"haConfig":[{"property":"name","value":""},{"property":"icon","value":""}],"entityidfilter":"sensor.0x54ef4410004c084d_action","entityidfiltertype":"exact","outputinitially":false,"state_type":"str","haltifstate":"button_6_double","halt_if_type":"str","halt_if_compare":"is","outputs":2,"output_only_on_state_change":true,"for":"0","forType":"num","forUnits":"minutes","ignorePrevStateNull":false,"ignorePrevStateUnknown":false,"ignorePrevStateUnavailable":false,"ignoreCurrentStateUnknown":false,"ignoreCurrentStateUnavailable":false,"outputProperties":[],"x":250,"y":280,"wires":[["dbc207429b6f6dc0"],[]]},{"id":"8560476076d9ec2e","type":"server-state-changed","z":"0d1d65f7ba1553ca","name":"单击切歌","server":"3cd209f0e02bc9b8","version":4,"exposeToHomeAssistant":false,"haConfig":[{"property":"name","value":""},{"property":"icon","value":""}],"entityidfilter":"sensor.0x54ef4410004c084d_action","entityidfiltertype":"exact","outputinitially":false,"state_type":"str","haltifstate":"button_6_single","halt_if_type":"str","halt_if_compare":"is","outputs":2,"output_only_on_state_change":true,"for":"0","forType":"num","forUnits":"minutes","ignorePrevStateNull":false,"ignorePrevStateUnknown":false,"ignorePrevStateUnavailable":false,"ignoreCurrentStateUnknown":false,"ignoreCurrentStateUnavailable":false,"outputProperties":[{"property":"payload","propertyType":"msg","value":"","valueType":"entityState"},{"property":"data","propertyType":"msg","value":"","valueType":"eventData"},{"property":"topic","propertyType":"msg","value":"","valueType":"triggerId"}],"x":240,"y":400,"wires":[["bba82e0449fc619d"],[]]},{"id":"67ed4b93f820eb6b","type":"switch","z":"0d1d65f7ba1553ca","name":"校验音乐链接","property":"responseUrl","propertyType":"msg","rules":[{"t":"regex","v":"^http(.*)?music\\.163\\.com([/#]*)?404$","vt":"str","case":true},{"t":"else"}],"checkall":"true","repair":false,"outputs":2,"x":760,"y":520,"wires":[["ed37f750b32c53d5"],["68fa5a5589d0413e"]]},{"id":"14bb74c3871d2a0d","type":"link out","z":"0d1d65f7ba1553ca","name":"link out 6","mode":"link","links":["6eb4074c79524195"],"x":935,"y":500,"wires":[]},{"id":"6eb4074c79524195","type":"link in","z":"0d1d65f7ba1553ca","name":"link in 5","links":["14bb74c3871d2a0d"],"x":325,"y":460,"wires":[["f6b40acd0cd79ecd"]]},{"id":"73636bcf6853a1f4","type":"function","z":"0d1d65f7ba1553ca","name":"提取音乐链接","func":"// 临时存储音乐信息\nmsg.data_info = msg.payload.data;\n\n// 返回的音乐链接\nvar url = msg.payload.data.url;\n\n// 有些音乐链接请求后返回404,需要先校验一下链接是否有效\n\n// var url = \"http://music.163.com/song/media/outer/url?id=29774171\";\n// var url = \"https://music.163.com/song/media/outer/url?id=1992712131\";\n\nmsg.url = url;\n\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":760,"y":440,"wires":[["d8434f54c95fcfb7"]]},{"id":"d8434f54c95fcfb7","type":"http request","z":"0d1d65f7ba1553ca","name":"请求音乐链接","method":"GET","ret":"txt","paytoqs":"ignore","url":"","tls":"","persist":false,"proxy":"","insecureHTTPParser":false,"authType":"","senderr":false,"headers":[],"x":760,"y":480,"wires":[["67ed4b93f820eb6b"]]},{"id":"33472c39781a68a1","type":"api-call-service","z":"0d1d65f7ba1553ca","name":"播放器音量 40%","server":"3cd209f0e02bc9b8","version":5,"debugenabled":false,"domain":"media_player","service":"volume_set","areaId":[],"deviceId":[],"entityId":["media_player.mopidy"],"data":"{\"volume_level\":0.4}","dataType":"json","mergeContext":"","mustacheAltTags":false,"outputProperties":[],"queue":"none","x":1000,"y":680,"wires":[[]]},{"id":"4eb44e04ca269500","type":"function","z":"0d1d65f7ba1553ca","name":"设置自动续播标识","func":"\n// 设置自动续播标识\nglobal.set('cloud_music_next',true,'file');\n\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":510,"y":340,"wires":[["e9211b0583476c26"]]},{"id":"70f47ee1b26c68c6","type":"function","z":"0d1d65f7ba1553ca","name":"移除云音乐播放标识","func":"\n// 移除自动续播标识\nglobal.set('cloud_music_next',undefined,'file');\n\n// 移除启动标识\nglobal.set('cloud_music_start',undefined,'file');\n\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":780,"y":300,"wires":[["858eab131136b164"]]},{"id":"cdb7084fb8e04e8f","type":"function","z":"0d1d65f7ba1553ca","name":"设置切歌标识","func":"\n// 手动切歌标识\nflow.set('cloud_music_cut',true);\n\n// // 设置启动标识 (用于切歌模式下,停止播放)\n// global.set('cloud_music_start',true,'file');\n\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":500,"y":420,"wires":[["e9211b0583476c26"]]},{"id":"e5bd51033ff6c35e","type":"delay","z":"0d1d65f7ba1553ca","name":"歌曲播放1分钟后","pauseType":"delay","timeout":"1","timeoutUnits":"minutes","rate":"1","nbRateUnits":"1","rateUnits":"minute","randomFirst":"1","randomLast":"5","randomUnits":"seconds","drop":true,"allowrate":false,"outputs":1,"x":1010,"y":540,"wires":[["627c5f4abdd8fe68"]]},{"id":"627c5f4abdd8fe68","type":"function","z":"0d1d65f7ba1553ca","name":"移除切歌标识","func":"\n// 歌曲正常播放一段时间后,移除切歌标识\nflow.set('cloud_music_cut',undefined);\n\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":1000,"y":580,"wires":[[]]},{"id":"35238c82ee9ad38b","type":"function","z":"0d1d65f7ba1553ca","name":"自动播放下一曲","func":"// 判断是否处于自动播放下一曲模式\nvar music_next = global.get('cloud_music_next','file') || false;\n\n// 判断当前播放器暂停状态是否由切歌操作触发\n// (切歌会导致播放器状态变化,从而导致触发下一曲,这里要限制)\nvar music_cut = flow.get('cloud_music_cut') || false;\n\n// 上一曲是否正常播放\nvar music_played = flow.get('cloud_music_played') || false;\n\n// 播放状态\nvar music_start = global.get('cloud_music_start','file') || false;\n\n// 当处于自动下一曲模式时,继续下一曲;\n// 切歌模式下,不触发自动下一曲;但如果播放失败,则触发下一曲\n// if(music_next && !music_cut){\n// node.send(msg);\n// }\n\n// 优化\nif(music_next){\n if(!music_cut){\n node.send(msg);\n }\n}else{\n // 非自动续播状态下,如果上一曲没有正常播放,则再播放一次\n if(!music_played){\n node.send(msg);\n }\n \n // 单曲模式下,播放完毕移除播放标识\n if (music_start){\n global.set('cloud_music_start',false,'file');\n }\n \n}","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":500,"y":500,"wires":[["e9211b0583476c26"]]},{"id":"d5d26d42dbaaeefe","type":"comment","z":"0d1d65f7ba1553ca","name":"当前链接无法播放,自动下一曲","info":"","x":1050,"y":420,"wires":[]},{"id":"05afa3423ece0963","type":"comment","z":"0d1d65f7ba1553ca","name":"云音乐播放器","info":"## 功能说明\n\n- 支持手动触发开启、停止、切歌操作\n- 支持自动触发,例如使用小米无线开关执行\n - 双击 开启或停止\n - 单击 切歌\n- 支持自动连播模式和单曲播放模式\n","x":250,"y":180,"wires":[]},{"id":"bba82e0449fc619d","type":"delay","z":"0d1d65f7ba1553ca","name":"限制频率","pauseType":"rate","timeout":"5","timeoutUnits":"seconds","rate":"1","nbRateUnits":"5","rateUnits":"second","randomFirst":"1","randomLast":"5","randomUnits":"seconds","drop":true,"allowrate":false,"outputs":1,"x":480,"y":380,"wires":[["cdb7084fb8e04e8f"]]},{"id":"dbc207429b6f6dc0","type":"function","z":"0d1d65f7ba1553ca","name":"启动状态","func":"\n// 判断启动状态\nvar music_start = global.get('cloud_music_start','file') || false;\n\nmusic_start = !music_start;\n\n// global.set('cloud_music_start',music_start,'file');\n\nmsg.payload = music_start;\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":480,"y":260,"wires":[["8c539921f7c54d23"]]},{"id":"8c539921f7c54d23","type":"switch","z":"0d1d65f7ba1553ca","name":"判断状态","property":"payload","propertyType":"msg","rules":[{"t":"false"},{"t":"true"}],"checkall":"true","repair":false,"outputs":2,"x":480,"y":300,"wires":[["70f47ee1b26c68c6"],["4eb44e04ca269500"]]},{"id":"ed37f750b32c53d5","type":"change","z":"0d1d65f7ba1553ca","name":"标记播放失败","rules":[{"t":"set","p":"cloud_music_played","pt":"flow","to":"false","tot":"bool"}],"action":"","property":"","from":"","to":"","reg":false,"x":1000,"y":460,"wires":[["14bb74c3871d2a0d","bbec98c169504538"]]},{"id":"3be1104b41134ac0","type":"debug","z":"0d1d65f7ba1553ca","name":"调试1","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","statusVal":"","statusType":"auto","x":1130,"y":300,"wires":[]},{"id":"bbec98c169504538","type":"debug","z":"0d1d65f7ba1553ca","name":"调试2","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","statusVal":"","statusType":"auto","x":1170,"y":460,"wires":[]},{"id":"c838eb7ba78b9a48","type":"debug","z":"0d1d65f7ba1553ca","name":"调试3","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":1130,"y":620,"wires":[]},{"id":"3cd209f0e02bc9b8","type":"server","name":"Home Assistant","version":5,"addon":false,"rejectUnauthorizedCerts":true,"ha_boolean":"y|yes|true|on|home|open","connectionDelay":true,"cacheJson":true,"heartbeat":false,"heartbeatInterval":"30","areaSelector":"friendlyName","deviceSelector":"friendlyName","entitySelector":"friendlyName","statusSeparator":": ","statusYear":"hidden","statusMonth":"short","statusDay":"numeric","statusHourCycle":"default","statusTimeFormat":"h:m","enableGlobalContextStore":false}]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment