// ==UserScript== // @name 歌曲宝全能下载助手 (终极优化版) // @namespace http://tampermonkey.net/ // @version 5.0 // @description 在歌曲宝搜索结果页面添加直链下载,拦截弹窗,下载歌词并嵌入封面图 // @author You // @match https://www.gequbao.com/s/* // @match https://www.gequbao.com/music/* // @grant GM_setValue // @grant GM_getValue // @grant GM_xmlhttpRequest // @grant GM_notification // @grant GM_download // @require https://cdn.jsdelivr.net/npm/jquery@3.6.0/dist/jquery.min.js // @require https://cdn.jsdelivr.net/npm/jsmediatags@3.9.7/dist/jsmediatags.min.js // ==/UserScript== (function() { 'use strict'; // 配置 const CONFIG = { defaultDownloadType: GM_getValue('download_type', 'direct'), downloadLrc: GM_getValue('download_lrc', false), embedCover: GM_getValue('embed_cover', true) // 新增:嵌入封面开关 }; // 主初始化函数 function init() { if (window.location.pathname.startsWith('/s/')) { // 搜索结果页面:添加下载控制面板和链接 addDownloadControlPanel(); addDirectDownloadLinks(); } else if (window.location.pathname.startsWith('/music/')) { // 歌曲详情页面:增强下载按钮功能 enhanceMusicPage(); } } // === 功能1: 为搜索结果页面添加控制面板 === function addDownloadControlPanel() { if ($('#download-control-panel').length > 0) return; const panelHtml = `
`; $('#search-line-form').after(panelHtml); // 绑定切换事件 $('input[name="download-type"]').change(function() { const type = $(this).val(); GM_setValue('download_type', type); CONFIG.defaultDownloadType = type; refreshAllDownloadLinks(); }); $('#download-lrc-switch').change(function() { const enabled = $(this).is(':checked'); GM_setValue('download_lrc', enabled); CONFIG.downloadLrc = enabled; }); $('#embed-cover-switch').change(function() { const enabled = $(this).is(':checked'); GM_setValue('embed_cover', enabled); CONFIG.embedCover = enabled; }); } // === 功能2: 为搜索结果添加下载链接 === function addDirectDownloadLinks() { $('.row.mb-3').nextAll('.row').each(function() { const $row = $(this); const $actionCol = $row.find('.col-4.text-right'); const $playLink = $actionCol.find('a[href*="/music/"]'); if ($actionCol.find('.direct-download').length > 0) return; const musicLink = $playLink.attr('href'); const musicId = musicLink.match(/\/music\/(\d+)/)[1]; const $downloadContainer = $('', { class: 'download-container', css: { 'margin-left': '10px' } }); const $songDownload = $('', { href: 'javascript:void(0)', html: '下载歌曲', css: { 'color': CONFIG.defaultDownloadType === 'direct' ? '#dc3545' : '#28a745', 'cursor': 'pointer', 'font-weight': 'bold', 'margin-right': '8px' }, addClass: 'direct-download', click: function(e) { e.preventDefault(); fetchDownloadUrl(musicId, $(this), 'song'); } }); const $lrcDownload = $('', { href: 'javascript:void(0)', html: '下载歌词', css: { 'color': '#6f42c1', 'cursor': 'pointer', 'font-weight': 'bold' }, addClass: 'lrc-download', click: function(e) { e.preventDefault(); fetchDownloadUrl(musicId, $(this), 'lrc'); } }); $downloadContainer.append($songDownload).append($lrcDownload); $actionCol.append($downloadContainer); }); } function refreshAllDownloadLinks() { $('.direct-download').css('color', CONFIG.defaultDownloadType === 'direct' ? '#dc3545' : '#28a745'); } // === 核心下载逻辑 === async function fetchDownloadUrl(musicId, $link, type = 'song') { const originalText = $link.html(); $link.html('获取中...').css('color', '#ffc107'); try { const response = await fetch(`/music/${musicId}`); const html = await response.text(); const appData = parseAppDataFromHTML(html); if (!appData) throw new Error('无法解析歌曲数据'); const lrcContent = parseLrcFromHTML(html); const coverUrl = parseCoverFromHTML(html); $link.data('musicId', musicId); $link.data('cachedData', appData); $link.data('lrcContent', lrcContent); $link.data('coverUrl', coverUrl); if (type === 'song') { if (CONFIG.defaultDownloadType === 'direct') { await fetchDirectDownloadUrl(appData, musicId, $link, lrcContent, coverUrl); } else { fetchPanDownloadUrl(appData, musicId, $link, lrcContent); } } else { downloadLrcFile(appData, lrcContent, $link); } } catch (error) { console.error('获取下载链接失败:', error); handleDownloadError($link, originalText, type); } } // === 功能3: 拦截弹窗并强制下载 === async function fetchDirectDownloadUrl(appData, musicId, $link, lrcContent, coverUrl) { if (appData.mp3_extra_urls && appData.mp3_extra_urls.length > 0) { for (let extraUrl of appData.mp3_extra_urls) { if (extraUrl.share_link && extraUrl.share_link.match(/\.mp3($|\?)/)) { applyDownloadUrl($link, musicId, appData); await triggerDirectDownload(extraUrl.share_link, appData, lrcContent, coverUrl); return; } } } if (appData.play_id) { await fetchPlayUrl(appData.play_id, musicId, $link, appData, lrcContent, coverUrl); } else { fetchPanDownloadUrl(appData, musicId, $link, lrcContent); } } function fetchPlayUrl(playId, musicId, $link, appData, lrcContent, coverUrl) { return new Promise((resolve, reject) => { GM_xmlhttpRequest({ method: 'POST', url: 'https://www.gequbao.com/api/play-url', headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, data: `id=${encodeURIComponent(playId)}`, onload: function(response) { try { const result = JSON.parse(response.responseText); if (result.code === 1 && result.data && result.data.url) { let downloadUrl = result.data.url; if (downloadUrl.includes('antiserver.kuwo.cn')) { convertKuwoUrl(downloadUrl, musicId, $link, appData, lrcContent, coverUrl).then(resolve).catch(reject); } else { applyDownloadUrl($link, musicId, appData); triggerDirectDownload(downloadUrl, appData, lrcContent, coverUrl).then(resolve).catch(reject); } } else { reject(new Error(result.msg || '获取播放链接失败')); } } catch (e) { reject(e); } }, onerror: reject }); }); } // === 功能4: 使用页面“下载歌词”按钮的逻辑 === function enhanceMusicPage() { // 重写原页面的歌词下载逻辑,使其也使用GM_download const originalLrcBtn = document.getElementById('btn-download-lrc'); if (originalLrcBtn) { originalLrcBtn.addEventListener('click', function(e) { e.preventDefault(); downloadLrcFromMusicPage(); }); } } function downloadLrcFromMusicPage() { // 从歌曲详情页获取歌词内容 const lrcElement = document.getElementById('content-lrc'); if (lrcElement) { const lrcContent = lrcElement.innerText; if (lrcContent && !lrcContent.includes('该歌曲暂无歌词')) { // 从页面获取歌曲信息 const musicTitleElement = document.querySelector('#music-title h1'); let songName = '未知歌曲'; if (musicTitleElement) { songName = musicTitleElement.textContent.split(' - ')[0].trim(); } const lrcFileName = sanitizeFilename(`${songName}.lrc`); const blob = new Blob([lrcContent], { type: 'text/plain' }); const url = URL.createObjectURL(blob); GM_download({ url: url, name: lrcFileName, onload: function() { URL.revokeObjectURL(url); GM_notification({ text: `歌词已下载: ${songName}`, title: '歌曲宝下载助手', timeout: 2000 }); }, onerror: function(error) { URL.revokeObjectURL(url); console.error('歌词下载失败:', error); } }); } else { alert('当前歌曲暂无歌词'); } } } function downloadLrcFile(appData, lrcContent, $link = null) { if (!lrcContent || lrcContent.includes('该歌曲暂无歌词')) { if ($link) { $link.html('无歌词').css('color', '#6c757d'); setTimeout(() => { $link.html('下载歌词').css('color', '#6f42c1'); }, 2000); } return; } const lrcFileName = sanitizeFilename(`${appData.mp3_title} - ${appData.mp3_author}.lrc`); const blob = new Blob([lrcContent], { type: 'text/plain' }); const url = URL.createObjectURL(blob); GM_download({ url: url, name: lrcFileName, onload: function() { URL.revokeObjectURL(url); if ($link) $link.html('下载歌词').css('color', '#6f42c1'); GM_notification({ text: `歌词已下载: ${appData.mp3_title}`, title: '歌曲宝下载助手', timeout: 2000 }); } }); } // === 功能5: 嵌入封面图片到MP3文件 === async function triggerDirectDownload(mp3Url, appData, lrcContent, coverUrl) { const fileName = sanitizeFilename(`${appData.mp3_title} - ${appData.mp3_author}.mp3`); if (CONFIG.embedCover && coverUrl) { // 如果开启了嵌入封面功能且有封面URL,则先下载图片和音频,然后合并 await downloadAndEmbedCover(mp3Url, fileName, coverUrl, appData); } else { // 否则直接下载MP3 GM_download({ url: mp3Url, name: fileName, onload: function() { GM_notification({ text: `下载完成: ${appData.mp3_title}`, title: '歌曲宝下载助手', timeout: 3000 }); } }); } // 如果设置了同时下载歌词,下载歌词 if (CONFIG.downloadLrc && lrcContent) { downloadLrcFile(appData, lrcContent); } } async function downloadAndEmbedCover(mp3Url, fileName, coverUrl, appData) { try { GM_notification({ text: `正在下载并处理: ${appData.mp3_title}`, title: '歌曲宝下载助手', timeout: 2000 }); // 并行下载MP3和封面图片 const [mp3Response, coverResponse] = await Promise.all([ fetch(mp3Url), fetch(coverUrl) ]); const [mp3Buffer, coverBuffer] = await Promise.all([ mp3Response.arrayBuffer(), coverResponse.arrayBuffer() ]); // 将封面图片转换为Base64 const coverBase64 = arrayBufferToBase64(coverBuffer); const mimeType = getImageMimeType(coverUrl); // 使用jsmediatags写入封面到MP3 jsmediatags.write(mp3Buffer, { tags: { title: appData.mp3_title, artist: appData.mp3_author, picture: { data: coverBase64, format: mimeType } } }, function(result) { if (result.error) { console.error('嵌入封面失败:', result.error); // 如果失败,回退到直接下载 fallbackDirectDownload(mp3Url, fileName, appData); return; } // 创建Blob并下载 const finalBlob = new Blob([result.buffer], { type: 'audio/mpeg' }); const finalUrl = URL.createObjectURL(finalBlob); GM_download({ url: finalUrl, name: fileName, onload: function() { URL.revokeObjectURL(finalUrl); GM_notification({ text: `下载完成(含封面): ${appData.mp3_title}`, title: '歌曲宝下载助手', timeout: 3000 }); } }); }); } catch (error) { console.error('处理文件失败:', error); // 出错时回退到直接下载 fallbackDirectDownload(mp3Url, fileName, appData); } } function fallbackDirectDownload(mp3Url, fileName, appData) { GM_download({ url: mp3Url, name: fileName, onload: function() { GM_notification({ text: `下载完成(无封面): ${appData.mp3_title}`, title: '歌曲宝下载助手', timeout: 3000 }); } }); } // === 工具函数 === function parseAppDataFromHTML(html) { const match = html.match(/window\.appData\s*=\s*({[^;]+});/); if (match && match[1]) { try { return JSON.parse(match[1]); } catch (e) { console.error('解析appData失败:', e); } } return null; } function parseLrcFromHTML(html) { const lrcMatch = html.match(/
]*>([\s\S]*?)<\/div>/); if (lrcMatch && lrcMatch[1]) { return lrcMatch[1].replace(//gi, '\n').replace(/<[^>]+>/g, '').trim(); } return null; } function parseCoverFromHTML(html) { // 从HTML中提取封面图片URL const coverMatch = html.match(/]*src="([^"]*)"[^>]*style="[^"]*background-image[^"]*"/); if (coverMatch && coverMatch[1]) { return coverMatch[1]; } // 备用方法:从aplayer-pic的背景图片中提取 const bgMatch = html.match(/background-image:\s*url\("([^&]*)"\)/); return bgMatch ? bgMatch[1] : null; } function arrayBufferToBase64(buffer) { let binary = ''; const bytes = new Uint8Array(buffer); for (let i = 0; i < bytes.byteLength; i++) { binary += String.fromCharCode(bytes[i]); } return window.btoa(binary); } function getImageMimeType(url) { if (url.includes('.jpg') || url.includes('.jpeg')) return 'image/jpeg'; if (url.includes('.png')) return 'image/png'; if (url.includes('.gif')) return 'image/gif'; return 'image/jpeg'; // 默认 } function sanitizeFilename(filename) { return filename.replace(/[<>:"/\\|?*]/g, '_').replace(/\s+/g, ' ').trim(); } function applyDownloadUrl($link, musicId, appData) { $link.html('下载歌曲').css('color', CONFIG.defaultDownloadType === 'direct' ? '#dc3545' : '#28a745'); } function handleDownloadError($link, originalText, type) { $link.html(originalText).css('color', type === 'song' ? (CONFIG.defaultDownloadType === 'direct' ? '#dc3545' : '#28a745') : '#6f42c1'); GM_notification({ text: '获取下载链接失败,请重试', title: '歌曲宝下载助手', timeout: 3000 }); } // 初始化 window.addEventListener('load', function() { setTimeout(init, 1000); }); // 监听页面变化 const observer = new MutationObserver(function(mutations) { let shouldUpdate = false; mutations.forEach(function(mutation) { if (mutation.addedNodes.length > 0) shouldUpdate = true; }); if (shouldUpdate) setTimeout(init, 500); }); observer.observe(document.body, { childList: true, subtree: true }); })();