// ==UserScript== // @name 网易云网页音频下载 // @namespace http://tampermonkey.net/ // @version 2025-04-18 // @description 从网易云网页上下载m4a音频文件,但音质较差。在点击播放后右侧会出现一个按钮,点击进入新页面下载。 // @author https://github.com/madderscientist // @match https://*/* // @icon https://www.google.com/s2/favicons?sz=64&domain=163.com // @grant none // @run-at document-start // @license MIT // @downloadURL https://update.greasyfork.icu/scripts/533227/%E7%BD%91%E6%98%93%E4%BA%91%E7%BD%91%E9%A1%B5%E9%9F%B3%E9%A2%91%E4%B8%8B%E8%BD%BD.user.js // @updateURL https://update.greasyfork.icu/scripts/533227/%E7%BD%91%E6%98%93%E4%BA%91%E7%BD%91%E9%A1%B5%E9%9F%B3%E9%A2%91%E4%B8%8B%E8%BD%BD.meta.js // ==/UserScript== (function () { 'use strict'; // 检查当前网页的 URL 是否包含 daolnwod name 和 src 参数 const urlParams = new URLSearchParams(window.location.search); if (urlParams.has('daolnwod')) { let name = urlParams.get('name'); let src = urlParams.get('src'); if (!(name && src)) throw new Error("缺少name和src属性"); // 对内容解码,比如空格的 替换为' ' const decodeDiv = document.createElement('div'); const decodeHTML = (str) => { decodeDiv.innerHTML = str; let decodedString = decodeDiv.textContent || decodeDiv.innerText; return decodedString; } name = decodeHTML(name); src = decodeHTML(src); document.body.innerHTML = `

下载${name}中,请稍等……

`; // 用fetch才能修改文件名。直接点击的话文件名不对的 fetch(src).then(response => { if (!response.ok) throw new Error('Network response was not ok'); return response.blob(); }).then(blob => { const url = URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; a.download = name; document.body.appendChild(a); a.click(); document.body.removeChild(a); URL.revokeObjectURL(url); }).catch(error => { alert("下载失败", error); }); return; // 直接返回,不再注入后面的代码 } // 判断当前网页是否是 music.163.com if (!window.location.hostname.includes('music.163.com')) return; // 经测试网易云音乐使用的是document.createElement("audio"),且创建于网页加载时【另外一种方法是new Audio()】 // 下面对其进行注入,以获取audio对象 为了赶在最前面执行,所以使用document-start console.log("网易云音乐注入"); function getAudioName(mode = 0b11) { // 获取网易云音乐的音频名称 const as = document.querySelector('.play').querySelectorAll('a'); switch (mode) { case 0b11: return `${as[1].innerText} - ${as[0].innerText}`; case 0b10: return as[1].innerText; case 0b01: return as[0].innerText; default: return "download" } } let btn = null; function createAudioBtn(HTMLaudio) { if (btn) return; btn = document.createElement('button'); btn.innerText = '下载音频'; btn.id = 'hacker'; document.body.appendChild(btn); btn.audio = HTMLaudio; btn.addEventListener('click', function () { const audioSrc = this.audio.src; if (audioSrc) { // 从音频源中提取文件后缀 const audioExtension = audioSrc.split('.').pop().split('?')[0]; if (!audioExtension || audioExtension.length > 4) audioExtension = 'mp3'; const filename = `${getAudioName()}.${audioExtension}`; console.log("音频地址:", audioSrc, "音频名称:", filename); // 获取根域名 一般含有music,所以匹配的域名加入了music const urlObj = new URL(audioSrc); const rootDomainWithProtocol = `${urlObj.protocol}//${urlObj.hostname}`; // 在新标签页中访问根域名,并传参 用daolnwod标识 const newTabUrl = `${rootDomainWithProtocol}?daolnwod&name=${encodeURIComponent(filename)}&src=${encodeURIComponent(audioSrc)}`; window.open(newTabUrl, '_blank'); } else { alert('音频源未找到'); } }); } // 保存原始的 document.createElement 方法 const originalCreateElement = document.createElement; // 钩子函数 document.createElement = function (tagName, ...args) { // 调用原始的 createElement 方法 const element = originalCreateElement.call(document, tagName, ...args); // 如果创建的是