(编辑:jimmy 日期: 2024/12/23 浏览:2)
{ "code": 200, "privileges": [{...}], "songs": [{...}]}
(function () { window.hooker = new Hooker(); function Hooker() { const H = this; const makeid = idmaker(); const map = H.map = {}; H.hook = hook; H.unhook = unhook; function hook(base, path, log=false, apply_debugger=false, hook_return=false) { // target path = arrPath(path); let parent = base; for (let i = 0; i < path.length - 1; i++) { const prop = path[i]; parent = parent[prop]; } const prop = path[path.length-1]; const target = parent[prop]; // Only hook functions if (typeof target !== 'function') { throw new TypeError('hooker.hook: Hook functions only'); } // Check args valid if (hook_return) { if (typeof hook_return !== 'object' || hook_return === null) { throw new TypeError('hooker.hook: Argument hook_return should be false or an object'); } if (!hook_return.hasOwnProperty('value') && typeof hook_return.dealer !== 'function') { throw new TypeError('hooker.hook: Argument hook_return should contain one of following properties: value, dealer'); } if (hook_return.hasOwnProperty('value') && typeof hook_return.dealer === 'function') { throw new TypeError('hooker.hook: Argument hook_return should not contain both of following properties: value, dealer'); } } // hooker function const hooker = function hooker() { let _this = this === H ? null : this; let args = Array.from(arguments); const config = map[id].config; const hook_return = config.hook_return; // hook functions config.log && console.log([base, path.join('.')], _this, args); if (config.apply_debugger) {debugger;} if (hook_return && typeof hook_return.dealer === 'function') { [_this, args] = hook_return.dealer(_this, args); } // continue stack return hook_return && hook_return.hasOwnProperty('value') ? hook_return.value : target.apply(_this, args); } parent[prop] = hooker; // Id const id = makeid(); map[id] = { id: id, prop: prop, parent: parent, target: target, hooker: hooker, config: { log: log, apply_debugger: apply_debugger, hook_return: hook_return } }; return map[id]; } function unhook(id) { // unhook try { const hookObj = map[id]; hookObj.parent[hookObj.prop] = hookObj.target; delete map[id]; } catch(err) { console.error(err); DoLog(LogLevel.Error, 'unhook error'); } } function arrPath(path) { return Array.isArray(path) ? path : path.split('.') } function idmaker() { let i = 0; return function() { return i++; } } }}) ();
hooker.hook(window.XMLHttpRequest.prototype, 'open', true, true);hooker.hook(window.XMLHttpRequest.prototype, 'send', true, true);
// hook top windowhooker.hook(window.XMLHttpRequest.prototype, 'open', true, true);hooker.hook(window.XMLHttpRequest.prototype, 'send', true, true);// iframe.contentWindowhooker.hook(document.querySelector('#g_iframe').contentWindow.XMLHttpRequest.prototype, 'open', true, true);hooker.hook(document.querySelector('#g_iframe').contentWindow.XMLHttpRequest.prototype, 'send', true, true);
(function() { let xhr; const AEL = getPureAEL(); // hook top window hooker.hook(window.XMLHttpRequest.prototype, 'open', false, false, { dealer: onOpen }); hooker.hook(window.XMLHttpRequest.prototype, 'send', false, false, { dealer: onSend }); // iframe.contentWindow hooker.hook(document.querySelector('#g_iframe').contentWindow.XMLHttpRequest.prototype, 'open', false, false, { dealer: onOpen }); hooker.hook(document.querySelector('#g_iframe').contentWindow.XMLHttpRequest.prototype, 'send', false, false, { dealer: onSend }); function onOpen(_this, args) { if (args[1].includes('/weapi/v3/song/detail')) { // 记录detail api的xhr xhr = _this; } return [_this, args]; } function onSend(_this, args) { if (xhr === _this) { AEL.call(_this, 'load', function(e) { debugger; }); } return [_this, args]; } // Get unpolluted addEventListener function getPureAEL(parentDocument=document) { const ifr = makeIfr(parentDocument); const oWin = ifr.contentWindow; const oDoc = ifr.contentDocument; const AEL = oWin.XMLHttpRequest.prototype.addEventListener; return AEL; } // Get unpolluted removeEventListener function getPureREL(parentDocument=document) { const ifr = makeIfr(parentDocument); const oWin = ifr.contentWindow; const oDoc = ifr.contentDocument; const REL = oWin.XMLHttpRequest.prototype.removeEventListener; return REL; } function makeIfr(parentDocument=document) { const ifr = parentDocument.createElement('iframe'); ifr.srcdoc = '<html></html>'; ifr.style.width = ifr.style.height = ifr.style.border = ifr.style.padding = ifr.style.margin = '0'; parentDocument.body.appendChild(ifr); return ifr; }}) ();
function onSend(_this, args) { if (xhr === _this) { AEL.call(_this, 'load', function(e) { debugger; }); debugger; } return [_this, args];}
function onSend(_this, args) { if (xhr === _this) { AEL.call(_this, 'readystatechange', function(e) { _onreadystatechange = _this.onreadystatechange; _this.onreadystatechange = onProgress; debugger; }, {once: true}); // once参数指定事件监听器仅仅触发一次 } return [_this, args];}function onProgress(e) { debugger;}
function onProgress(e) { const xhr = e.target; if (xhr.readyState === 4) { const RATES = { 'none': 0, 'standard': 128000, 'exhigh': 320000,'lossless': 999000, }; // br-level 对照表,可从普通单曲的请求中收集到 const json = JSON.parse(xhr.response); const privilege = json['privileges'][0]; dlLevel = privilege['downloadMaxBrLevel']; dlRate = RATES[dlLevel]; plLevel = privilege['playMaxBrLevel']; plRate = RATES[plLevel]; privilege['dlLevel'] = dlLevel; // Download privilege['dl'] = dlRate; // Download privilege['plLevel'] = plLevel; // Play privilege['pl'] = plRate; // Play const response = JSON.stringify(json) const propDesc = { value: response, writable: false, configurable: false, enumerable: true } Object.defineProperties(xhr, { 'response': propDesc, 'responseText': propDesc }); } // 别忘了继续执行页面前端js _onreadystatechange(e);}
GM_xmlhttpRequest({ method: "GET", url: api_got_url, // 第三方接口返回的url onprogress: function(e) { json.data[0].url = e.finalUrl; // 填入其他属性 onreadystatechange.apply(_this, args); // 继续执行页面前端代码 }})