// ==UserScript== // @name 网易云音乐高音质支持 // @version 1.0 // @description 去除网页版网易云音乐仅可播放低音质(96Kbps)的限制,强制播放高音质版本 // @match *://music.163.com/* // @include *://music.163.com/* // @author 864907600cc // @icon https://secure.gravatar.com/avatar/147834caf9ccb0a66b2505c753747867 // @run-at document-end // @grant none // @noframes // @namespace http://ext.ccloli.com // @downloadURL none // ==/UserScript== // getTrackURL 源码来自 Chrome 扩展程序 网易云音乐增强器(Netease Music Enhancement) by wanmingtom@gmail.com // 菊苣这个加密算法你是怎么知道的 _(:3 var getTrackURL = function getTrackURL (dfsId) { var byte1 = '3' + 'g' + 'o' + '8' + '&' + '$' + '8' + '*' + '3' + '*' + '3' + 'h' + '0' + 'k' + '(' + '2' + ')' + '2'; var byte1Length = byte1.length; var byte2 = dfsId + ''; var byte2Length = byte2.length; var byte3 = []; for (var i = 0; i < byte2Length; i++) { byte3[i] = byte2.charCodeAt(i) ^ byte1.charCodeAt(i % byte1Length); }; byte3 = byte3.map(function(i) { return String.fromCharCode(i) }).join(''); results = CryptoJS.MD5(byte3).toString(CryptoJS.enc.Base64); results = results.replace(/\//g, '_').replace(/\+/g, '-'); var url = 'http://m1.music.126.net/' + results + '/' + byte2 + '.mp3'; return url; } // 由于黄易修改了相关变量, 网易云音乐增强器 里对播放器的 hack 基本失效,故关于切换音质的部分均重写,找变量找的我心好累 _(:3 var magicIt = function magicIt(){ console.log('施放魔法!变变变!'); // 黄易将播放列表存在了 localStorage 里,通过遍历 + getTrackURL 就可以将其的 mp3 地址替换为音质尽可能高的 mp3 地址 var data = JSON.parse(localStorage.getItem('track-queue')); data.map(function(elem){ // 部分音乐没有高音质 elem.mp3Url = getTrackURL(elem.hMusic ? elem.hMusic.dfsId : elem.mMusic ? elem.mMusic.dfsId : elem.lMusic.dfsId); }); localStorage.setItem('track-queue', JSON.stringify(data)); // 由于直接改 localStorage 只能刷新后生效,看了两天的源码,终于找出了黄易将播放列表存在哪个变量里了 _(:3 // 卧槽这些变量名只有两个字符,基本看不出什么端倪,我还差点以为我不会 js 了,结果找了好久终于试出来了,闲的无聊的可以看我 QQ 空间里这两天的吐槽 _(:3 // 不过相比某度,感觉黄易的代码写得真(ta)是(ma)高(kan)大(bu)上(dong)啊 _(:3」∠∠∠∠∠∠∠∠∠∠)__________ NEJ.P("nm.d").fW.bL().bA['track-queue'] = data; } // 但是直接改了播放列表也不会对当前播放的曲目产生任何变化,player.seek() 只是重新播放而已,并没有重新向服务器发起请求 _(:3 // 尝试 hack 原函数,实现自动转换播放列表的音质,不过看来我小看了闭包的作用 _(:3 /* 略 */ // 这个 bL.WG 可以实现强制重新载入播放列表,但是在随机播放模式下,其播放的第一曲始终是播放列表的第一曲,故废弃 // 话说黄易每次在歌单点击播放时都会 bL.WG(undefined, 0) 清空播放列表再 bL.WG(newPlaylist, 1) (伪代码)添加播放列表,感觉有点多此一举,还是因为我没理解 bL.WG 的含义 _(:3 /* NEJ.P("nm.d").fW.bL().WG(data, 1); */ // 由于无法直接 hack 原函数,onstorage 事件不会在当前标签页触发,所以监听播放器的播放列表曲目数的变化来转换播放列表,我真是太机智了 _(:3 document.getElementsByClassName('icn-list')[0].addEventListener('DOMSubtreeModified', function(){ console.log('播放列表发生了变化,当前曲目数为 ' + this.textContent); // 如前,黄易在播放一个新的歌单时会清空播放列表,此时其值为 0,故此时不做处理 if (this.textContent == '0') return; // 莫名其妙地会出现播放最后一曲的情况,加个定时器去除冲突 setTimeout(function(){ magicIt(); // 对于当前曲目的处理方法……先播放上一曲再播放下一曲不就好了?我真是天(chun)才 _(:3 document.getElementsByClassName('prv')[0].click(); document.getElementsByClassName('nxt')[0].click(); }, 10); });