"use strict"; // ==UserScript== // @name 网易云音乐拾遗 // @description 本脚本可以帮你收集任意歌单里涉及歌手的热门歌曲到一份新的歌单中 // @namespace lonr.github.io // @version 0.0.4 // @author lonr // @grant GM_addStyle // @match *://music.163.com/#/* // @include http://music.163.com/* // @noframes // @compatible firefox, chrome // @incompatible edge // @supportURL http://music.163.com/#/playlist?id=746621854 // @license GPL // @downloadURL https://update.greasyfork.icu/scripts/30234/%E7%BD%91%E6%98%93%E4%BA%91%E9%9F%B3%E4%B9%90%E6%8B%BE%E9%81%97.user.js // @updateURL https://update.greasyfork.icu/scripts/30234/%E7%BD%91%E6%98%93%E4%BA%91%E9%9F%B3%E4%B9%90%E6%8B%BE%E9%81%97.meta.js // ==/UserScript== var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; var __generator = (this && this.__generator) || function (thisArg, body) { var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g; return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; function verb(n) { return function (v) { return step([n, v]); }; } function step(op) { if (f) throw new TypeError("Generator is already executing."); while (_) try { if (f = 1, y && (t = y[op[0] & 2 ? "return" : op[0] ? "throw" : "next"]) && !(t = t.call(y, op[1])).done) return t; if (y = 0, t) op = [0, t.value]; switch (op[0]) { case 0: case 1: t = op; break; case 4: _.label++; return { value: op[1], done: false }; case 5: _.label++; y = op[1]; op = [0]; continue; case 7: op = _.ops.pop(); _.trys.pop(); continue; default: if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } if (t[2]) _.ops.pop(); _.trys.pop(); continue; } op = body.call(thisArg, _); } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; } }; var Picker = /** @class */ (function () { function Picker(window) { var _this = this; this.window = window; this.artistsMap = []; this.artists = []; this.isListPage = false; this.isRunning = false; this.document = window.document; // iframe window 是不变的,切换页面后 document 会变!!! this.innerWindow = window.document.getElementById('g_iframe').contentWindow; this.innerDocument = this.innerWindow.document; this.addUI(); this.initUI(); this.innerWindow.frameElement.addEventListener('load', function () { _this.initUI(); }); } Object.defineProperty(Picker.prototype, "isAbleToStart", { get: function () { return this.isListPage && !this.isRunning; }, enumerable: true, configurable: true }); /** * 添加UI,绑定了 start 按钮的事件!!! */ Picker.prototype.addUI = function () { var _this = this; var container = this.UIEle = this.document.createElement('aside'); container.className = 'jl-UI'; container.innerHTML = "\n
\n \u672C\u811A\u672C\u80FD\u5E2E\u4F60\u6536\u96C6\u4EFB\u610F\u6B4C\u5355\u91CC\u6D89\u53CA\u6B4C\u624B\u7684\u70ED\u95E8\u6B4C\u66F2\u5230\u4E00\u4EFD\u65B0\u7684\u6B4C\u5355\u4E2D\n
\n\n \u8BF7\u767B\u5F55\u5E76\u901A\u8FC7\u201C\u6211\u7684\u4E3B\u9875\u201D\u6216\u8005\u201C\u53D1\u73B0\u97F3\u4E50-\u6B4C\u5355\u201D\u6253\u5F00\u4EFB\u4E00\u6B4C\u5355\n
\n \n\u5982\u9700\u5E2E\u52A9\u53EF\u4EE5\u90AE\u4EF6\u6211
\n "; this.document.body.appendChild(container); GM_addStyle("\n .jl-UI {\n position: absolute;\n right: 2em;\n top: 100px;\n width: 15em;\n padding: 1em;\n font-size: 14px;\n font-family: sans-serif;\n background: #fff;\n }\n .jl-UI h1 {\n text-align: center;\n margin-bottom: 1em;\n }\n .jl-UI p:first-of-type {\n margin-bottom: 0.5em;\n }\n .jl-UI a {\n color: #C10D0C;\n }\n .jl-options {\n margin-top: 1em;\n margin-bottom: 1em;\n }\n .jl-options p {\n text-align: center;\n margin: 0.5em 0;\n }\n .jl-UI input, .jl-UI button {\n font-size: 14px;\n }\n .jl-nevermore input, .jl-allNew input {\n width: 2em;\n }\n .jl-limit input {\n margin-right: 0.5em;\n width: 3em;\n }\n .jl-start {\n margin: 0.5em 0;\n border: solid 1px #C10D0C !important;\n padding: 0.2em 0.2em;\n color: #C10D0C;\n background: #fff;\n }\n .jl-start[disabled] {\n border-color: grey !important;\n color: grey; \n cursor: no-drop;\n }\n .jl-log {\n white-space: pre-line;\n }\n "); this.loggerEle = this.document.querySelector('.jl-log'); this.startEle = this.document.querySelector('.jl-start'); this.startEle.addEventListener('click', function () { _this.setStartBtn(false); _this.setOptions(); _this.start().catch(_this.log); }); }; Picker.prototype.setStartBtn = function (active) { if (active) { this.startEle.disabled = false; this.startEle.innerText = '点击这里开始'; } else { this.startEle.disabled = true; this.startEle.innerText = '不在歌单页面或者正在运行中'; } }; /** * 设置按钮状态 */ Picker.prototype.initUI = function () { this.isListPage = this.window.document.location.hash.includes('#/playlist'); if (this.isAbleToStart) { // this.innerWindow = (window.document.getElementById('g_iframe') as HTMLIFrameElement).contentWindow; this.innerDocument = this.innerWindow.document; this.setStartBtn(true); this.getArtistsList(); } else { this.setStartBtn(false); } }; Picker.prototype.start = function () { return __awaiter(this, void 0, void 0, function () { return __generator(this, function (_a) { switch (_a.label) { case 0: this.isRunning = true; return [4 /*yield*/, this.setSongs()]; case 1: _a.sent(); this.log('创建歌单完成,上传并等待服务器处理中'); return [4 /*yield*/, this.postList()]; case 2: _a.sent(); this.isRunning = false; return [2 /*return*/]; } }); }); }; Picker.prototype.setOptions = function () { var nevermoreTrueRadio = this.UIEle.querySelector('.jl-nevermore #nevermore-true'); Picker.options.nevermore = nevermoreTrueRadio.checked; var allNewTrueRadio = this.UIEle.querySelector('.jl-allNew #allNew-true'); Picker.options.allNew = allNewTrueRadio.checked; Picker.options.limit = Number(this.UIEle.querySelector('#limit').value); // let listName = this.innerDocument.querySelector('.f-ff2.f-brk')!.textContent; // Picker.options.listName = (this.UIEle!.querySelector('#listName') as HTMLInputElement).value.replace('原歌单名', listName!); }; Picker.prototype.getArtistsList = function () { this.artists = []; this.artistsMap = []; var songList = this.innerDocument.querySelector('.m-table tbody').children; for (var _i = 0, _a = Array.from(songList); _i < _a.length; _i++) { var song = _a[_i]; var songEle = song.querySelector('.f-cb a b'); var songName = songEle.title.replace(/\s/g, ' '); var songHref = songEle.getAttribute('href') || ''; var artistEle = song.querySelector('td:nth-of-type(4) > div > span'); var name_1 = artistEle.title.replace(/\s/g, ' '); // FIXME:多作者歌曲、无详情页作者 // 2018年2月5日:就不! var anchor = artistEle.querySelector('a'); var homepage = anchor ? (anchor.getAttribute('href') || '') : ''; var index = this.artistsMap.indexOf(name_1); if (index < 0) { this.artistsMap.push(name_1); this.artists.push({ name: name_1, homepage: homepage, songs: [], topSongs: [] }); index = this.artistsMap.length - 1; } this.artists[index].songs.push({ songName: songName, songHref: songHref, }); } this.log("\u6B64\u6B4C\u5355\u5171\u6709 " + this.artists.length + " \u4F4D\u6B4C\u624B"); // \n将消耗至少 ${Math.floor(this.artists.length * Picker.options.delay / 1000)}s }; /** * 获取并设置一个歌手的热门歌曲 * @param artist */ Picker.prototype.setTopSongs = function (artist) { return fetch(artist.homepage).then(function (response) { if (response.status !== 200) { return Promise.reject(response.status); } return response.text(); }).then(function (data) { var result = /