Greasy Fork is available in English.

网易云下载

歌单里好多歌用以前的脚本都404了,于是简单修改了下。

  1. // ==UserScript==
  2. // @name 网易云下载
  3. // @description 歌单里好多歌用以前的脚本都404了,于是简单修改了下。
  4. // @version 20180616
  5. // @author 糖果君
  6. // @include http*://music.163.com/*
  7. // @grant unsafeWindow
  8. // @grant GM_xmlhttpRequest
  9. // @grant GM.xmlHttpRequest
  10. // @require https://greasyfork.org/scripts/26727-网易云音乐歌曲封面下载/code/网易云音乐歌曲封面下载.user.js
  11. // @require https://greasyfork.org/scripts/34555-greasemonkey-4-polyfills/code/Greasemonkey%204%20Polyfills.js
  12.  
  13. // @namespace
  14. // ==/UserScript==
  15. //参考 https://greasyfork.org/zh-CN/scripts/10548-网易云音乐下载
  16. //以及更早的 https://greasyfork.org/zh-CN/scripts/1099-netease-music-download
  17. var window=unsafeWindow||window.wrappedJSObject;
  18. var api = {
  19. //参考 https://greasyfork.org/zh-CN/scripts/10582-网易云音乐高音质支持
  20. getTrackURL: function(dfsId) {
  21. var byte1 = '3go8&$8*3*3h0k(2)2';
  22. var byte2 = dfsId + '';
  23. var byte3 = [];
  24. for (var i = 0; i < byte2.length; i++) {
  25. byte3[i] = byte2.charCodeAt(i) ^ byte1.charCodeAt(i % byte1.length);
  26. }
  27. byte3 = byte3.map(function(i) {
  28. return String.fromCharCode(i);
  29. }).join('');
  30. var results = window.CryptoJS.MD5(byte3).toString(window.CryptoJS.enc.Base64).replace(/\//g, '_').replace(/\+/g, '-');
  31. var url = 'http://p2.music.126.net/' + results + '/' + byte2;
  32. return url;
  33. },
  34. autoSign: function (type, cookie) {
  35. if (document.cookie.indexOf(cookie) == - 1) {
  36. api.sign(type, function (result) {
  37. if (result.code == - 2 || result.code == 200) {
  38. var cookieime = new Date();
  39. cookieime.setTime(24 * 60 * 60 * 1000 + new Date(new Date() .toDateString()) .getTime() - 1);
  40. document.cookie = cookie+ ';expires=' + cookieime.toGMTString();
  41. }
  42. });
  43. };
  44. },
  45. encrypt_request: function(callback, url, data) {
  46. // 这个好像不重要。var token = window.document.cookie.split('__csrf') [1].split(';') [0].substring(1);
  47. var token = '';
  48. data.csrf_token = token;
  49. var req = new XMLHttpRequest();
  50. req.open('POST', window.location.origin+url+ token, true);
  51. req.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
  52. req.setRequestHeader('X-Real-IP', '27.38.4.87');
  53. //解决海外问题。
  54. req.onload = function() {
  55. callback(JSON.parse(this.responseText));
  56. };
  57. //参考 https://github.com/darknessomi/musicbox/wiki/网易云音乐新版WebAPI分析。
  58. var pubKey = '010001';
  59. var modulus = '00e0b509f6259df8642dbc35662901477df22677ec152b5ff68ace615bb7b725152b3ab17a876aea8a5aa76d2e417629ec4ee341f56135fccf695280104e0312ecbda92557c93870114af6c9d05c4f7f0c3685b7a46bee255932575cce10b424d813cfe4875d3e82047b97ddef52741d546b8e289dc6935b3ece0462db0a22b8e7';
  60. var nonce = '0CoJUm6Qyw8W8jud';
  61. var result = window.asrsea(JSON.stringify(data), pubKey, modulus, nonce);
  62. req.send('params=' + encodeURIComponent(result.encText) + '&encSecKey=' + encodeURIComponent(result.encSecKey));
  63. },
  64. detail: function(songId, callback) {
  65. var url = '/weapi/v3/song/detail';
  66. var data = {
  67. c:
  68. JSON.stringify([{
  69. id: songId
  70. }
  71. ])
  72. };
  73. this.encrypt_request(callback, url, data);
  74. },
  75. album: function (albumId, callback) {
  76. var url = '/api/album/' + albumId;
  77. var data = {
  78. };
  79. this.encrypt_request(callback, url, data);
  80. },
  81. lrc: function(songId, callback) {
  82. var url = '/weapi/song/lyric';
  83. var data = {
  84. id: songId,
  85. lv: -1,
  86. tv: -1
  87. };
  88. this.encrypt_request(callback, url, data);
  89. },
  90. newsong: function(songId, callback) {
  91. var url = '/weapi/song/enhance/player/url';
  92. var data = {
  93. ids: [songId],
  94. br: 999000,
  95. };
  96. this.encrypt_request(callback, url, data);
  97. },
  98. mv: function(mvId, callback) {
  99. var url = '/weapi/mv/detail/';
  100. var data = {
  101. id: mvId,
  102. };
  103. this.encrypt_request(callback, url, data);
  104. },
  105. search: function(songinfo, callback) {
  106. var url = '/weapi/search/pc';
  107. var data = {
  108. s: songinfo,
  109. limit: 1,
  110. type: 1,
  111. offset: 0,
  112. };
  113. this.encrypt_request(callback, url, data);
  114. },
  115. sign: function (type, callback) {
  116. var url = '/weapi/point/dailyTask';
  117. var data = {
  118. type: type
  119. };
  120. this.encrypt_request(callback, url, data);
  121. },
  122. xiamisearch: function (key, callback) {
  123. GM.xmlHttpRequest({
  124. method: 'GET',
  125. url: 'http://music-api-jwzcyzizya.now.sh/api/search/song/xiami?&limit=1&page=1&key=' + key,
  126. onload: function (response) {
  127. callback(JSON.parse(response.responseText));
  128. }
  129. });
  130. },
  131. };
  132. var innerFrame = document.querySelector('iframe');
  133. var pages = [
  134. {
  135. url: 'music.163.com/#/song?id=',
  136. handler: function() {
  137. var songId = location.href.match(/id=([0-9]+)/)[1];
  138. var downloadLine = this.createDownloadLine(songId);
  139. var innerFrameDoc = innerFrame.contentWindow.document;
  140. var albumNode = innerFrameDoc.querySelectorAll('p.des.s-fc4')[1];
  141. var parentNode = albumNode.parentNode;
  142. parentNode.insertBefore(downloadLine, albumNode.nextElementSibling);
  143. },
  144. createDownloadLine: function(songId) {
  145. var disableStyle = function(link) {
  146. link.text += '(无)';
  147. link.style.color = 'gray';
  148. link.style.textDecoration = 'none';
  149. link.style.cursor = 'auto';
  150. };
  151. var setUrlAndSize = function (mp3Link, Music, albumId,songinfo) {
  152. if (Music) {
  153. var href = Music.url;
  154. if (href) {
  155. mp3Link.href = href;
  156. mp3Link.text += (Music.size / 1024 / 1024) .toFixed(1) + 'M';
  157. return ;
  158. }
  159. else {/*其实旧接口还是能用,带.mp3等后缀名会直接403 去掉后缀名就可以
  160. api.album(albumId, function (result) {
  161. if (result.album && result.album.songs)
  162. {
  163. var songs = result.album.songs;
  164. for (var i = 0; i < songs.length; i++)
  165. {
  166. if (songs[i].id == songId) {
  167. var song = songs[i];
  168. var music = song.hMusic || song.mMusic || song.lMusic || song.bMusic;
  169. var mp3url;
  170. var dfsId = music.dfsId_str || music.dfsId;
  171. if (music && dfsId != 0) {
  172. mp3url = api.getTrackURL(dfsId);
  173. mp3Link.text += (music.size / 1024 / 1024) .toFixed(1) + 'M(旧)';
  174. } else if (!song.mp3Url.endsWith('==/0')) {
  175. mp3url = song.mp3Url;
  176. }
  177. if (mp3url) {
  178. mp3Link.href = mp3url;
  179. }
  180. else {*/
  181. api.xiamisearch(songinfo, function (result) {
  182. if (result.songList) {
  183. console.log(result);
  184. mp3Link.href = result.songList[0].file;
  185. mp3Link.text += '(虾米)';
  186. } else {
  187. disableStyle(mp3Link);
  188. }
  189. });/*
  190. }
  191. }
  192. }
  193. }
  194.  
  195. });
  196. */}
  197. }
  198. };
  199. var setLyric = function(LycLink, result) {
  200. var LrC = '';
  201. var lrc = result.lrc;
  202. var tlrc = result.tlyric;
  203. var num = 0;
  204. if (lrc && lrc.lyric) {
  205. LrC += lrc.lyric + '\n';
  206. num+=1;}
  207. if (tlrc && tlrc.lyric) {
  208. LrC += tlrc.lyric;
  209. num+=2;}
  210.  
  211.  
  212. if (num != 0) {
  213. var html = '';
  214. switch (num) {
  215. case 1:html="(原)";break;
  216. case 2:html="(译)";break;
  217. case 3:html="(合)";break;
  218. }
  219. LycLink.href = 'data:text/plain;charset=utf-8,' + encodeURIComponent(LrC);
  220. LycLink.innerHTML += html;
  221. } else {
  222. disableStyle(LycLink);
  223. }
  224. };
  225.  
  226. var newMp3Link = this.createLink('歌曲');
  227. var lyricLink = this.createLink('歌词');
  228. var mvLink = this.createLink('mv');
  229. var picLink = this.createLink('封面');
  230.  
  231. api.detail(songId, function (result) {
  232. var song = result.songs[0];
  233. if (song.mv) {
  234. api.mv(song.mv, function (result) {
  235. var mv = result.data.brs;
  236. mvLink.href = mv[720] || mv[480] || mv[240];
  237. });
  238. }
  239. else {
  240. disableStyle(mvLink);
  241. };
  242.  
  243. if (song.al.pic_str || song.al.pic) {
  244. var img = innerFrame.contentWindow.document.querySelector(".j-img");
  245. picLink.href = img.dataset["src"] = api.getTrackURL(song.al.pic_str || song.al.pic);
  246. img.src = img.dataset["src"] + "?param=130y130";
  247. } else {
  248. disableStyle(picLink);
  249. };
  250. var alia = song.alia[0] ? '(' + song.alia + ')' : '';
  251. var ars = '-';
  252. for (var i = 0; i < song.ar.length; i++) {
  253. ars += (song.ar) [i].name + '/';
  254. }
  255. var songinfo = song.name + alia + ars;
  256. var alid = song.al.id;
  257. api.newsong(songId, function (result) {
  258. var song = result.data[0];
  259. setUrlAndSize(newMp3Link, song, alid,songinfo);
  260. });
  261. });
  262. api.lrc(songId, function (result) {
  263. setLyric(lyricLink, result);
  264. });
  265. var container = this.createLineContainer('下载');
  266. container.appendChild(newMp3Link);
  267. container.appendChild(lyricLink);
  268. container.appendChild(mvLink);
  269. container.appendChild(picLink);
  270.  
  271. return container;
  272. },
  273. createLink: function(label) {
  274. var link = document.createElement('a');
  275. link.innerHTML = label;
  276. link.className = 's-fc7';
  277. link.style.marginRight = '10px';
  278. link.href = 'javascript:void(0);';
  279. link.target = '_blank';
  280. return link;
  281. },
  282. createLineContainer: function(label) {
  283. var container = document.createElement('p');
  284. container.className = 'desc s-fc4';
  285. container.innerHTML = label + ':';
  286. container.style.margin = '10px 0';
  287. return container;
  288. },
  289. },
  290. ];
  291. if (innerFrame) {
  292. innerFrame.addEventListener('load', function () {
  293. var i, page;
  294. for (i = 0; i < pages.length; i += 1) {
  295. page = pages[i];
  296. if (location.href.indexOf(page.url) != -1) {
  297. page.handler();
  298. }
  299. }
  300. });
  301. }
  302. document.cookie = 'os=ios';
  303. api.autoSign(0,'appsign=true');
  304. api.autoSign(1,'websign=true');