// ==UserScript== // @name my-xmly // @version 0.0.2 // @description 登录后支持VIP音频下载,支持专辑批量下载,支持失败后自动重试,支持添加编号。(基于“黑客, uid:219866”,"Ming"代码) // @author Ming // @match *://www.ximalaya.com/* // @grant GM_xmlhttpRequest // @grant GM_setValue // @grant GM_getValue // @grant GM_addStyle // @grant GM_setClipboard // @grant GM_download // @icon https://www.ximalaya.com/favicon.ico // @require https://registry.npmmirror.com/crypto-js/4.1.1/files/crypto-js.js // @license MIT // @namespace https://greasyfork.org/users/1438860 // @downloadURL https://update.greasyfork.icu/scripts/535106/my-xmly.user.js // @updateURL https://update.greasyfork.icu/scripts/535106/my-xmly.meta.js // ==/UserScript== (function() { 'use strict'; function showAlert(message, type = 'info') { const alertDiv = document.createElement('div'); alertDiv.style.cssText = ` position: fixed; top: 20px; left: 50%; transform: translateX(-50%); padding: 10px 20px; background: ${type === 'error' ? '#fff2f0' : '#f6ffed'}; border: 1px solid ${type === 'error' ? '#ffccc7' : '#b7eb8f'}; color: ${type === 'error' ? '#cf1322' : '#389e0d'}; border-radius: 4px; z-index: 2147483647; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif; box-shadow: 0 2px 8px rgba(0,0,0,0.15); `; alertDiv.textContent = message; document.body.appendChild(alertDiv); setTimeout(() => { alertDiv.style.transition = 'opacity 0.3s'; alertDiv.style.opacity = '0'; setTimeout(() => alertDiv.remove(), 300); }, 3000); } function initSetting() { var setting; if (!GM_getValue('priate_script_xmly_data')) { GM_setValue('priate_script_xmly_data', { left: 20, top: 100, manualMusicURL: null, quality: 1, showNumber: true, numberOffset: 1 }) } setting = GM_getValue('priate_script_xmly_data') if (!setting.quality) setting.quality = 1; setting.quality = 1; // Always high quality if (setting.showNumber === null) setting.showNumber = true; if (!setting.numberOffset) setting.numberOffset = 1; GM_setValue('priate_script_xmly_data', setting) } // 手动获取音频地址功能 function manualGetMusicURL() { let windowID = getRandStr("1234567890qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM", 100) function getRandStr(chs, len) { let str = ""; while (len--) { str += chs[parseInt(Math.random() * chs.length)]; } return str; } (function() { let playOriginal = HTMLAudioElement.prototype.play; function play() { let link = this.src; window.top.postMessage(Array("audioVideoCapturer", link, windowID, "link"), "*"); return playOriginal.call(this); } HTMLAudioElement.prototype.play = play; HTMLAudioElement.prototype.play.toString = HTMLAudioElement.prototype.play.toString.bind(playOriginal); })(); if (window.top == window) { window.addEventListener("message", function(event) { if (event.data[0] == "audioVideoCapturer") { var setting = GM_getValue('priate_script_xmly_data') setting.manualMusicURL = event.data[1] GM_setValue('priate_script_xmly_data', setting) } }); } } manualGetMusicURL() function injectDiv() { try { // Check if div already exists if (document.getElementById('priate_script_div')) { return; } var priate_script_div = document.createElement("div"); priate_script_div.innerHTML = `
`; document.body.appendChild(priate_script_div); // Set initial position const setting = GM_getValue('priate_script_xmly_data'); const div = document.getElementById('priate_script_div'); if (div) { div.style.left = (setting.left || 20) + "px"; div.style.top = (setting.top || 100) + "px"; } } catch (error) { console.error('Failed to inject div:', error); } } function dragFunc(id) { var Drag = document.getElementById(id); var setting = GM_getValue('priate_script_xmly_data') Drag.onmousedown = function(event) { var ev = event || window.event; event.stopPropagation(); var disX = ev.clientX - Drag.offsetLeft; var disY = ev.clientY - Drag.offsetTop; document.onmousemove = function(event) { var ev = event || window.event; setting.left = ev.clientX - disX Drag.style.left = setting.left + "px"; setting.top = ev.clientY - disY Drag.style.top = setting.top + "px"; Drag.style.cursor = "move"; GM_setValue('priate_script_xmly_data', setting) }; }; Drag.onmouseup = function() { document.onmousemove = null; this.style.cursor = "default"; }; }; // 第一种获取musicURL的方式,任意用户均可获得,不可获得VIP音频 async function getSimpleMusicURL1(item) { var res = null; if (item.url) { res = item.url; } else { const timestamp = Date.parse(new Date()); var url = `https://mobwsa.ximalaya.com/mobile-playpage/playpage/tabs/${item.id}/${timestamp}`; try { const response = await fetch(url); const resp = await response.json(); if (resp.ret === 0) { const setting = GM_getValue('priate_script_xmly_data'); const trackInfo = resp.data.playpage.trackInfo; if (setting.quality == 0) { res = trackInfo.playUrl32; } else if (setting.quality == 1) { res = trackInfo.playUrl64; } } } catch (error) { console.error('Error fetching URL:', error); } } return res; } // 第二种获取musicURL的方式,任意用户均可获得,不可获得VIP音频 async function getSimpleMusicURL2(item) { var res = null; if (item.url) { res = item.url; } else { var url = `https://www.ximalaya.com/revision/play/v1/audio?id=${item.id}&ptype=1`; try { const response = await fetch(url); const resp = await response.json(); if (resp.ret == 200) { res = resp.data.src; } } catch (error) { console.error('Error fetching URL:', error); } } return res; } //获取任意音频方法 async function getAllMusicURL1(item) { var res = null var setting; if (item.url) { res = item.url } else { const all_li = document.querySelectorAll('.sound-list>ul li'); for (var num = 0; num < all_li.length; num++) { var li = all_li[num] const item_a = li.querySelector('a'); const id = item_a.href.split('/')[item_a.href.split('/').length - 1] if (id == item.id) { li.querySelector('div.all-icon').click() while (!res) { await Sleep(1) setting = GM_getValue('priate_script_xmly_data') res = setting.manualMusicURL } setting.manualMusicURL = null GM_setValue('priate_script_xmly_data', setting) li.querySelector('div.all-icon').click() break } } } if (!res && item.isSingle) { document.querySelector('div.play-btn').click() while (!res) { await Sleep(1) setting = GM_getValue('priate_script_xmly_data') res = setting.manualMusicURL } setting.manualMusicURL = null GM_setValue('priate_script_xmly_data', setting) document.querySelector('div.play-btn').click() } return res } // 通过解密数据的方式获取 URL async function getAllMusicURL2(item) { function decrypt(t) { return CryptoJS.AES.decrypt({ ciphertext: CryptoJS.enc.Base64url.parse(t) }, CryptoJS.enc.Hex.parse('aaad3e4fd540b0f79dca95606e72bf93'), { mode: CryptoJS.mode.ECB, padding: CryptoJS.pad.Pkcs7 }).toString(CryptoJS.enc.Utf8) } var res = null; if (item.url) { res = item.url; } else { const timestamp = Date.parse(new Date()); var url = `https://www.ximalaya.com/mobile-playpage/track/v3/baseInfo/${timestamp}?device=web&trackId=${item.id}`; try { const response = await fetch(url); const resp = await response.json(); try { res = decrypt(resp.trackInfo.playUrlList[0].url); } catch (e) { console.log("解密错误"); res = null; } } catch (error) { console.error('Error fetching URL:', error); } } return res; } class XimalayaDownloader { constructor() { this.setting = GM_getValue('priate_script_xmly_data'); this.data = []; this.musicList = []; this.isDownloading = false; this.cancelDownloadObj = null; this.stopDownload = false; this.autoDownloadEnabled = false; this.autoDownloadPaused = false; this.createButtons(); this.initElements(); this.initEventListeners(); this.updateUI(); } createButtons() { // Create auto download button only const autoDownloadBtn = document.createElement('button'); autoDownloadBtn.id = 'autoDownloadBtn'; autoDownloadBtn.textContent = '自动下载'; autoDownloadBtn.style.display = 'none'; const downloadBtn = document.getElementById('downloadBtn'); if (downloadBtn) { downloadBtn.parentNode.insertBefore(autoDownloadBtn, downloadBtn.nextSibling); } } initElements() { try { this.elements = { qualityBtn: document.getElementById('qualityBtn'), numberToggleBtn: document.getElementById('numberToggleBtn'), numberOffsetBtn: document.getElementById('numberOffsetBtn'), numberOffsetSpan: document.getElementById('numberOffsetSpan'), loadBtn: document.getElementById('loadBtn'), downloadBtn: document.getElementById('downloadBtn'), exportBtn: document.getElementById('exportBtn'), cancelBtn: document.getElementById('cancelBtn'), selectAllBtn: document.getElementById('selectAllBtn'), musicTable: document.getElementById('musicTable'), musicTableBody: document.getElementById('musicTableBody'), autoDownloadBtn: document.getElementById('autoDownloadBtn') }; // Verify all elements were found for (let key in this.elements) { if (!this.elements[key]) { throw new Error(`Element ${key} not found`); } } } catch (error) { console.error('Failed to initialize elements:', error); throw error; } } initEventListeners() { this.elements.qualityBtn.onclick = () => this.changeQuality(); this.elements.numberToggleBtn.onclick = () => this.switchShowNumber(); this.elements.numberOffsetBtn.onclick = () => this.addNumberOffset(); this.elements.numberOffsetBtn.oncontextmenu = (e) => { e.preventDefault(); this.subNumberOffset(); }; this.elements.loadBtn.onclick = () => this.loadMusic(); this.elements.downloadBtn.onclick = () => this.downloadAllMusics(); this.elements.exportBtn.onclick = () => this.exportAllMusicURL(); this.elements.cancelBtn.onclick = () => this.cancelDownload(); this.elements.selectAllBtn.onclick = () => this.selectAllMusic(); this.elements.autoDownloadBtn.onclick = () => this.toggleAutoDownload(); } updateUI() { // Update quality button const qualityColors = ["#946C00", "#55ACEE", "#00947e", "#337ab7"]; const qualityTexts = ["标准", "高清", "超高", "未知"]; const quality = (this.setting.quality >= 0 && this.setting.quality <= 2) ? this.setting.quality : 3; this.elements.qualityBtn.style.color = qualityColors[quality]; this.elements.qualityBtn.textContent = qualityTexts[quality]; // Update number toggle this.elements.numberToggleBtn.style.color = this.setting.showNumber ? "#00947e" : "#CC0F35"; this.elements.numberToggleBtn.textContent = this.setting.showNumber ? "开启" : "关闭"; this.elements.numberOffsetSpan.style.display = this.setting.showNumber ? "inline" : "none"; this.elements.numberOffsetBtn.textContent = this.setting.numberOffset; // Update buttons this.elements.loadBtn.textContent = this.data.length > 0 ? "重载" : "加载"; this.elements.downloadBtn.style.display = (!this.isDownloading && this.musicList.length > 0) ? "inline" : "none"; this.elements.exportBtn.style.display = (!this.isDownloading && this.musicList.length > 0) ? "inline" : "none"; this.elements.cancelBtn.style.display = this.isDownloading ? "inline" : "none"; this.elements.musicTable.style.display = this.data.length > 0 ? "block" : "none"; this.elements.autoDownloadBtn.style.display = (!this.isDownloading && this.musicList.length > 0) ? "inline" : "none"; this.elements.autoDownloadBtn.textContent = this.autoDownloadEnabled ? "停止自动" : "自动下载"; this.renderMusicList(); } renderMusicList() { this.elements.musicTableBody.innerHTML = ''; const dataToRender = this.isDownloading ? this.musicList : this.data; dataToRender.forEach(item => { const tr = document.createElement('tr'); tr.innerHTML = `