您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
解锁MyFreeMP3的QQ音乐、酷狗音乐、酷我音乐,过广告拦截器检测,所有下载全部转为页面内直链下载
当前为
// ==UserScript== // @name My Free MP3+ // @namespace http://tampermonkey.net/My Free MP3 Plus // @version 0.2.5 // @description 解锁MyFreeMP3的QQ音乐、酷狗音乐、酷我音乐,过广告拦截器检测,所有下载全部转为页面内直链下载 // @author PY-DNG // @require https://gf.qytechs.cn/scripts/456034-basic-functions-for-userscripts/code/script.js?version=1226884 // @match http*://tool.liumingye.cn/music_old/* // @match http*://tools.liumingye.cn/music_old/* // @match http*://tool.liumingye.cn/music/* // @match http*://tools.liumingye.cn/music/* // @connect kugou.com // @connect * // @grant GM_xmlhttpRequest // @grant GM_download // @run-at document-start // ==/UserScript== /* global LogLevel DoLog Err $ $All $CrE $AEL $$CrE addStyle detectDom destroyEvent copyProp copyProps parseArgs escJsStr replaceText getUrlArgv dl_browser dl_GM AsyncManager */ /* global pop */ (function() { 'use strict'; // Main loader main(); function main() { // Collect all funcs from page objs const pages = [music, music_old].map(f => f()); const func_immediate = [], func_load = []; for (const page of pages) { page.regurl.test(location.href) && page.funcs.forEach(funcobj => (funcobj.onload ? func_load : func_immediate).push(funcobj.func)); } // Exec const exec = funcs => funcs.forEach(func => func()); exec(func_immediate); $AEL(window, 'load', exec.bind(null, func_load)); } // 新版页面 function music() { return { regurl: /^https?:\/\/tools?\.liumingye\.cn\/music\//, funcs: [{ func: downloadInPage, onload: false }] } function downloadInPage() { const hooker = new Hooker(); const xhrs = []; const hookedURLs = ['https://api.liumingye.cn/m/api/search', 'https://api.liumingye.cn/m/api/home/recommend', 'https://api.liumingye.cn/m/api/top/song']; const openHooerId = hooker.hook(XMLHttpRequest.prototype, 'open', false, false, { dealer(_this, args) { if (hookedURLs.some(url => args[1].includes(url))) { xhrs.push(_this); } return [_this, args]; } }); const sendHooerId = hooker.hook(XMLHttpRequest.prototype, 'send', false, false, { dealer(_this, args) { if (xhrs.includes(_this)) { const callbackName = 'onloadend' in _this ? 'onloadend' : 'onreadystatechange'; const callback = _this[callbackName]; _this[callbackName] = function() { const json = JSON.parse(this.response); json.data.list.forEach(song => song.quality.forEach((q, i) => typeof q !== 'number' && (song.quality[i] = parseInt(q.name, 10)))); rewriteResponse(this, json); callback.apply(this, arguments); } xhrs.splice(xhrs.indexOf(_this), 1); } return [_this, args]; } }); DoLog(`XMLHttpRequest Hooked: ${openHooerId}, ${sendHooerId}`); } } // 旧版页面 function music_old() { return { regurl: /^https?:\/\/tools?\.liumingye\.cn\/music_old\//, funcs: [{ func: unlockTencent, onload: true }, { func: downloadInPage, onload: true }, { func: bypassAdkillerDetector, onload: false }] }; // 解锁QQ音乐、酷狗音乐、酷我音乐函数 function unlockTencent() { // 模拟双击 const search_title = $('#search .home-title'); const eDblclick = new Event('dblclick'); search_title.dispatchEvent(eDblclick); // 去除双击事件 const p = search_title.parentElement; const new_search_title = $CrE('div'); new_search_title.className = search_title.className; new_search_title.innerHTML = search_title.innerHTML; p.removeChild(search_title); p.insertBefore(new_search_title, p.children[0]); } // Hook掉下载按钮实现全部下载均采用页面内下载方式(重写下载逻辑) function downloadInPage() { $AEL(document.body, 'click', onclick, {capture: true}); function onclick(e) { const elm = e.target; const parent = elm ? elm.parentElement : null; match(elm); match(parent); function match(elm) { const tag = elm.tagName.toUpperCase(); const clList = [...elm.classList]; if (tag === 'A' && clList.includes('download') || clList.includes('pic_download')) { e.stopPropagation(); e.preventDefault();; download(elm); } } } function download(a) { const elm_data = a.parentElement.previousElementSibling; const url = elm_data.value; const name = $("#name").value; const pop_id = pop.download(name, 'download'); GM_xmlhttpRequest({ method: 'GET', url: url, responseType: 'blob', onprogress: function(e) { e.lengthComputable /*&& c*/ && (pop.size(pop_id, bytesToSize(e.loaded) + " / " + bytesToSize(e.total)), pop.percent(pop_id, 100 * (e.loaded / e.total) >> 0)) }, onerror: function(e) { console.log(e); window.open(url); }, onload: function(response) { const blob = response.response; const dataUrl = URL.createObjectURL(blob); const ext = getExtname(elm_data.id, blob.type.split(';')[0]); saveFile(dataUrl, `${name}.${ext}`); setTimeout(URL.revokeObjectURL.bind(URL, dataUrl), 1000); pop.finished(pop_id); setTimeout(pop.close.bind(pop, pop_id), 2000); } }); function getExtname(...args) { const map = { url_dsd: "flac", url_flac: "flac", url_ape: "ape", url_320: "mp3", url_128: "mp3", url_m4a: "m4a", url_lrc: "lrc", 'image/png': 'png', 'image/jpg': 'jpg', 'image/gif': 'gif', 'image/bmp': 'bmp', 'image/jpeg': 'jpeg', 'image/webp': 'webp', 'image/tiff': 'tiff', 'image/vnd.microsoft.icon': 'ico', }; return map[args.find(a => map[a])]; } function bytesToSize(a) { if (0 === a) { return "0 B"; } var b = 1024 , c = ["B", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"] , d = Math.floor(Math.log(a) / Math.log(b)); return (a / Math.pow(b, d)).toFixed(2) + " " + c[d] } } } // 过广告拦截器检测 function bypassAdkillerDetector() { /* // 拦截广告拦截检测器的setTimeout延迟启动器 // 优点:不用考虑#music_tool是否存在,不用反复执行;缺点:需要在setTimeout启动器注册(不可用)前执行,如果脚本加载缓慢,就来不及了 const setTimeout = unsafeWindow.setTimeout; unsafeWindow.setTimeout = function(func, time) { if (func && func.toString().includes('$("#music_tool").html()')) { func = function() {}; } setTimeout.call(this, func, time); } */ /* // 拦截广告拦截检测器的innerHTML检测 // 优点:对浏览器API没有影响,对DOM影响极小,在检测前执行即可;缺点:需要#music_tool存在,需要反复检测执行,影响性能,稳定性差 const bypasser = () => { const elm = $('#music_tool'); elm && Object.defineProperty($('#music_tool'), 'innerHTML', {get: () => '<iframe></iframe>'}); }; setTimeout(bypasser, 2000); bypasser(); */ // 在页面添加干扰元素 // 优点:对浏览器API没有影响,对DOM几乎没有影响,在检测前执行即可,不用考虑#music_tool是否存在,不用反复执行;缺点:可能影响广告功能(乐 document.body.firstChild.insertAdjacentHTML('beforebegin', '<ins id="music_tool" style="display: none !important;">sometext</ins>'); } } // Save dataURL to file function saveFile(dataURL, filename) { const a = $CrE('a'); a.href = dataURL; a.download = filename; a.click(); } 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++; } } } function rewriteResponse(xhr, json) { const response = JSON.stringify(json); const propDesc = { value: response, writable: false, configurable: true, enumerable: true }; Object.defineProperties(xhr, { 'response': propDesc, 'responseText': propDesc }); } })();
QingJ © 2025
镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址