// ==UserScript== // @name 爬取全民K歌指定用户的全部歌曲 // @namespace https://kg.qq.com/ // @version 1.3.0 // @description 一键下载全民K歌用户的所有歌曲(MP4/M4A) // @author aspen138 // @match https://kg.qq.com/* // @match https://static-play.kg.qq.com/* // @match https://node.kg.qq.com/* // @grant GM_xmlhttpRequest // @grant GM_download // @grant GM_setValue // @grant GM_getValue // @grant GM_addStyle // @connect kg.qq.com // @connect node.kg.qq.com // @connect static-play.kg.qq.com // @connect * // @run-at document-idle // @license MIT // @icon data:image/x-icon;base64,AAABAAEAGBgAAAEAIACICQAAFgAAACgAAAAYAAAAMAAAAAEAIAAAAAAAAAkAAAAAAAAAAAAAAAAAAAAAAAD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8AAgH/AAIB/wAAAv8AQw7+AIs1+QCKNPwAnjr8Apc5+z2ZOvyKnDn7v5s6+9+XOfvvkzf72o42+7KKNvx6gjL9KnYu/ABfJv0AUSDBAAAAAAB2CUELiQdfC////wD///8AAQD/AAEA/wAAAP8AQw3/AIgz+QCQNvsxozv7xqE8+v+cOfr/ljf7/480+/+LNPv/iTP7/4g0+/+HNfr/hjX7/n8z+59nKfwZWCDNACwCExWHC0vniwxTu////wD///8AAAD/AAAA/wAAAP8ANQD/AJ88+oKhOvr5mjn6/5M0+/+MM/v/iDH7/4Iw+/9+Lvz/ey78/3kt+/93Lfz/dy77/3sw+/97MPvxeSjkWaMFRbuQDU//lwhKsv///wD///8AAAu/AAALvwAAB8UAdSr4iaU7+/+XN/v/jTP7/4cw+/+BLvz/fCz8/3cr/P9yKfz/cCf8/24o/P9rJ/3/ayj8/2sp/f9vK/3/di72/4kYgP6QBzr9fgRWOv///wD///8AZCfhAGEm4QB3LuNfpz33+480+/+KMfv/gjD8/30u/P92Kfz/cSn8/24m/P9oJvz/ZiT8/2Ik/P9hI/v/XSP//1Un//9WKf//YCn//2ou//+IEWOzVQpGAP///wD///8AXhv/AGMe/winOf/ykTb8/4cx+/+ALvz/eSv8/3In/P9sJvz/aCT8/2Qi/v9eIv7/XCD+/1kg/v9XHf//Uh///3YWm/93Fpn/XCH2/1co//9mJ/2/ayT2AP///wD///8AaCb7AH8w/IWVN/v/hjD7/30s/P92Kfz/byf8/2kk/v9kIv7/XiD+/1ke/v9WHv7/VBz+/1Ec/v9MG///eROF/5UFKP+UBCz/hQ5g/2Ed4P9bJf/9UyX/S////wD///8AhTX6AJk5+/eFMfv/fSz8/3Qr/P9uKPz/ZiT+/2Ag/f9aHv7/Vh7+/1Qc/v9PGf7/TBn//0oZ/v9GGf//lAcx/4sNUP+NC07/kwc2/3AWp/9UHv7/VB7+ov///wD///8AhjH8QIgz+/9+Lvv/dSv8/2wo/P9mJP7/XiL8/1ge/v9UHP7/TRT+/zoA/P8sAPv/NgD9/0UW//8+Fv//exJ+/5kGKP+XBjD/kgg9/1sU1/9OGf7/UBz+2P///wD///8AiDT7cYAw+/92K/z/bij8/2Uk/P9eIP7/WB7+/1Qe/v87APz/LQD7/1g2/v9tVf//YUb//0IR//9AFv//Pxb//28Ul/93E4T/XRbE/z8Y//9IF/7/Thz++v///wD///8AgjH7jnou/P9wKfz/ZyT8/14i/v9YHv7/Uhz+/zoA/P9XNv//wrn///j4////////tqz//z0O/vpAFP6IPRP/5ikl//8vJ///LCr//zwl//9AIv7/RiD+/////wD///8AfTD8jnQr/P9qJ/3/YSL+/1kg/v9SHP7/SxT+/1Uz/v/u6///////////////////koP//0AU/sVCFP4APhf/AEEo/6o3Nv//Nzf9/zk1/f88K/3/QSb9/////wD///8Adi35cW8r/P9lJvz/XSD9/1Ue/f9OHP7/SyD+/720////////////////////////mo3//0EN/49BHv4ANyL/KkoZ/ys4P/3TNUL9/zY//f85Nf3/PSj9+f///wD///8AbSn/QGwp/f9gJPz/WCD8/1Ae/f9KGf7/UzH+//j3////////////////////////4Nz//xkA++w9JP/aOzD+/zku/qI1MP03Mk78/zZD/f84Of3/Oyv92P///wD///8ASB3iAGco+vdeIvz/VR79/04c/f9IGf7/WD7//////////////////////////////////4d+//8FAPv/Mi/+/zZD/ts2Nf0DNE781jZG/P83O/3/PC3+ov///wD///8AWSLsAGkq/4NfJP7/VB79/0sc/f9GHP7/VTr//////////////////9DK//+Qgf//6uf///v7//+Hiv//HCf7/zBH/Ow4Q/sYMkL9gjJL/P8zRvz9Pjv8Sf///wD///8AThj3AEgU8AhXIvnwVB7+/0sc/f9GHP7/SSv+/+Lg/////////////3Vg//8AAPj/wLj/////////////5eT//0Bh/egvTP0YMFH8Xy9b+/8tQ/3NRCH7AP///wD///8ARwzzAEcM8wBVG/qDVR7+/0we/v9FG///QBr+/5CF///+/v///////+7s//+7tP//9/b/////////////6+///x9i+8wtUP0DMVz+cCFk+/+GDf8YhwD/AP///wD///8AaCf/AGgn/wBhJvyKVSL+/0si/v9EJP7/PyT//ykA/f+opf/////////////////////////////T3f//Kmj8/0BQ/HxQSf0ANm78pD0+/VZoAP8AZwD/AP///wD///8AVy/7AFcv+wBbMvuNVTD8/0k0/P9ANv3/Ozj9/zA0/f8LAfv/aHH//6yy///GzP//u8X//4Of//8VYPv/EWn82ExD/wB1HP8AS07+JEUt/QBQAP8AUAD/AP///wD///8AUj/9AFI//QBZQPxzWEf+/0ZM+/49U/z9NFj8/y9a+/8sW/r/ADn6/wAw+v8EQPv/AEf5/wBS+f8ZbvvIK2D8FR9b/wB4Dv8AUTz/AEIz/QBVAP8AVQD/AP///wD///8AUkX5AFJF+QBXRvo9UUryc0FV/iQ7X/wbM2L8Mi1u+1sqcvySKXX8xClw++MmbvvhJHL8tjJr/mEtZPsHK177ACpX/wB2GP8ATkP/AEIz/QBVAP8AVQD/AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A/gB5APwAEQD4AAEA8AABAOAAAwDAAAMAwAABAMAAAQCAAAEAgAABAIAAAQCAAYEAgAEBAIAAAQDAAAEAwAABAMAAAwDgAAMA4AAnAOAAbwDgAH8A4AD/AP///wA= // ==/UserScript== // @credit Ported from https://www.manshaoco.com/1354.html Python code: Auto-fetches UID, grabs all songs, and downloads them. (function () { 'use strict'; // ─── Helpers ──────────────────────────────────────────────────────────────── function getCookies() { return document.cookie; } function parseCookies(cookieStr) { const dict = {}; cookieStr.split(';').forEach(pair => { const [k, ...rest] = pair.trim().split('='); if (k) dict[k.trim()] = rest.join('=').trim(); }); return dict; } function cleanFilename(name) { return name.replace(/[\\/:*?"<>|\r\n"]/g, '').trim(); } function uidFromURL() { try { const url = new URL(window.location.href); return url.searchParams.get('uid') || ''; } catch { return ''; } } function gmFetch(url, opts = {}) { return new Promise((resolve, reject) => { GM_xmlhttpRequest({ method: opts.method || 'GET', url, headers: opts.headers || {}, responseType: opts.responseType || 'text', onload: r => resolve(r), onerror: e => reject(e), }); }); } function extractJSON(text) { // Generic fallback: find outermost {...} and parse const s = text.indexOf('{'); const e = text.lastIndexOf('}'); if (s === -1 || e === -1) return null; try { return JSON.parse(text.slice(s, e + 1)); } catch { return null; } } /** * Extract a window.VARNAME = {...}; assignment from raw HTML. * Uses brace-balancing so large nested JSON is handled correctly. */ function extractVar(html, varName) { const marker = `window.${varName}`; const markerIdx = html.indexOf(marker); if (markerIdx === -1) return null; const eqIdx = html.indexOf('=', markerIdx); if (eqIdx === -1) return null; const start = html.indexOf('{', eqIdx); if (start === -1) return null; let depth = 0; for (let i = start; i < html.length; i++) { if (html[i] === '{') depth++; else if (html[i] === '}') { depth--; if (depth === 0) { try { return JSON.parse(html.slice(start, i + 1)); } catch { return null; } } } } return null; } /** * Try both page formats and return a unified object with a .data field. * Old kg.qq.com/node/personal → window.__DATA__ → .data * New static-play React app → window.__FETCH_RES__ → .userInfoRes.data * * For the play detail page: * Old → window.__DATA__ → .detail * New → window.__FETCH_RES__ → .detailRes.data (or similar) */ function extractPageData(html) { // ── New React page (__FETCH_RES__) ── const fetchRes = extractVar(html, '__FETCH_RES__'); if (fetchRes) { const data = fetchRes.userInfoRes && fetchRes.userInfoRes.data; const detail = fetchRes.ugcDetailRes && fetchRes.ugcDetailRes.data; if (data || detail) return { source: 'FETCH_RES', data, detail }; } // ── Old SSR page (__DATA__) ── const dataObj = extractVar(html, '__DATA__'); if (dataObj) return { source: '__DATA__', data: dataObj.data, detail: dataObj.detail }; return null; } function sleep(ms) { return new Promise(r => setTimeout(r, ms)); } // ─── UI ───────────────────────────────────────────────────────────────────── GM_addStyle(` #kgdl-panel *{box-sizing:border-box;font-family:'PingFang SC','Microsoft YaHei',sans-serif} #kgdl-panel{ position:fixed;bottom:24px;right:24px;z-index:2147483647; width:340px;background:#0d0d0d;border:1px solid #222; border-radius:16px;box-shadow:0 8px 40px #000a; overflow:hidden;transition:height .3s ease; } #kgdl-header{ display:flex;align-items:center;justify-content:space-between; padding:14px 18px;background:#161616;cursor:pointer; border-bottom:1px solid #222;user-select:none; } #kgdl-header .kgdl-title{ font-size:14px;font-weight:700;letter-spacing:.04em; color:#fff;display:flex;align-items:center;gap:8px; } #kgdl-header .kgdl-title span.dot{ width:8px;height:8px;border-radius:50%;background:#ff4d4d; display:inline-block;animation:kgdl-pulse 1.4s infinite; } @keyframes kgdl-pulse{0%,100%{opacity:1;transform:scale(1)}50%{opacity:.5;transform:scale(.85)}} #kgdl-toggle{color:#555;font-size:18px;line-height:1;transition:transform .3s} #kgdl-body{padding:16px 18px;display:flex;flex-direction:column;gap:10px} .kgdl-label{font-size:11px;color:#555;letter-spacing:.06em;text-transform:uppercase;margin-bottom:2px} .kgdl-input{ width:100%;background:#1c1c1c;border:1px solid #2a2a2a;border-radius:8px; color:#e0e0e0;font-size:13px;padding:8px 12px;outline:none; transition:border .2s; } .kgdl-input:focus{border-color:#ff4d4d} .kgdl-input::placeholder{color:#3a3a3a} .kgdl-row{display:flex;gap:8px} .kgdl-btn{ flex:1;padding:9px 0;border:none;border-radius:9px; font-size:13px;font-weight:600;cursor:pointer; transition:opacity .2s,transform .1s; } .kgdl-btn:active{transform:scale(.97)} .kgdl-btn:disabled{opacity:.35;cursor:not-allowed} .kgdl-btn.primary{background:#ff4d4d;color:#fff} .kgdl-btn.secondary{background:#222;color:#aaa;border:1px solid #2a2a2a} #kgdl-log{ background:#111;border:1px solid #1e1e1e;border-radius:10px; font-size:11.5px;color:#5a5a5a;padding:10px 12px; height:120px;overflow-y:auto;line-height:1.7; font-family:'Menlo','Consolas',monospace; } #kgdl-log .ok{color:#3d9e6a} #kgdl-log .err{color:#c44} #kgdl-log .info{color:#5a8fc4} #kgdl-log .hi{color:#e0e0e0} #kgdl-progress-wrap{display:none;flex-direction:column;gap:4px} #kgdl-progress-label{font-size:11px;color:#666} #kgdl-progress-bar-bg{background:#1e1e1e;border-radius:99px;height:5px;overflow:hidden} #kgdl-progress-bar{height:5px;border-radius:99px;background:#ff4d4d;width:0%;transition:width .3s ease} `); const panel = document.createElement('div'); panel.id = 'kgdl-panel'; panel.innerHTML = `