您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
记录猫耳FM / missevan 的 标题和网址,可导出为 JSON 方便后期处理其他任务。脚本支持下载当前页面弹幕 xml 或记录中的所有页面对应 xml(只能逐个下载)
// ==UserScript== // @name 猫耳FM记录与XML弹幕下载 // @namespace https://github.com/fishhh666 // @version 1.0 // @description 记录猫耳FM / missevan 的 标题和网址,可导出为 JSON 方便后期处理其他任务。脚本支持下载当前页面弹幕 xml 或记录中的所有页面对应 xml(只能逐个下载) // @author fishhh666 // @match https://www.missevan.com/sound/player?id=* // @grant GM_registerMenuCommand // @grant GM_unregisterMenuCommand // @grant GM_setValue // @grant GM_getValue // @grant GM_xmlhttpRequest // @require https://cdnjs.cloudflare.com/ajax/libs/FileSaver.js/2.0.5/FileSaver.min.js // ==/UserScript== (function() { 'use strict'; let records = GM_getValue("records", []); let recording = GM_getValue("recording", false); let lastUrl = location.href; let menuIds = []; // 保存记录 function saveRecords() { GM_setValue("records", records); updateMenu(); } // 添加记录(去重) function addRecord(url, title) { if (!/^https:\/\/www\.missevan\.com\/sound\/player\?id=\d+$/.test(url)) return; if (!records.some(r => r.url === url)) { records.push({ url, title }); saveRecords(); console.log("记录已添加:", title, url); } } // 安全添加记录(延迟+轮询确认标题更新) function safeAddRecord(url) { let attempts = 0; let oldTitle = document.title.trim(); let check = () => { let nowTitle = document.title.trim(); if (nowTitle !== oldTitle || attempts >= 5) { addRecord(url, nowTitle); } else { attempts++; setTimeout(check, 300); // 每300ms检测一次,最多尝试5次,1.5秒 } }; setTimeout(check, 500); // 首次延迟0.5秒,避免太早取旧标题 } // 切换记录状态 function toggleRecording() { recording = !recording; GM_setValue("recording", recording); if (recording) { safeAddRecord(location.href); alert("开始记录,当前页面已记录!网址变化时也会自动添加。"); } else { alert("已暂停记录。"); } updateMenu(); } // 清空记录 function clearRecords() { if (records.length === 0) { alert("记录是空的,不需要清空。"); return; } if (confirm("确认要清空所有记录吗?此操作不可恢复!")) { records = []; saveRecords(); console.log("记录已清空"); } } // 查看记录 function viewRecords() { if (records.length === 0) { alert("还没有任何记录。"); } else { let str = records.map((r, i) => `${i + 1}. ${r.title}:${r.url}`).join("\n"); alert(str); } } // 下载记录到 link.json function downloadRecords() { if (records.length === 0) return alert("记录为空,无法导出。"); let jsonArr = records.map(r => ({ title: r.title, link: r.url })); let jsonStr = JSON.stringify(jsonArr, null, 2); // 美化缩进 let blob = new Blob([jsonStr], { type: "application/json;charset=utf-8" }); window.saveAs(blob, "link.json"); } // 获取 soundid function getIdFromUrl(url) { let m = url.match(/id=(\d+)/); return m ? m[1] : null; } // 下载 XML function downloadXml(apiUrl, filename) { return new Promise((resolve) => { GM_xmlhttpRequest({ method: "GET", url: apiUrl, responseType: "text", onload: function(res) { if (res.status === 200) { let blob = new Blob([res.responseText], { type: "application/xml" }); window.saveAs(blob, filename); } else { console.error("请求失败:", apiUrl, res.status); } resolve(); }, onerror: function(err) { console.error("请求错误:", apiUrl, err); resolve(); } }); }); } // 下载当前页面弹幕 async function downloadCurrent() { const url = location.href; const id = getIdFromUrl(url); if (!id) return alert("未获取到 soundid。"); const apiUrl = `https://www.missevan.com/sound/getdm?soundid=${id}`; const title = document.title.trim().replace(/[\/\\:*?"<>|]/g, "_"); await downloadXml(apiUrl, `${title}.xml`); } // 下载所有记录弹幕 async function downloadAll() { if (records.length === 0) return alert("没有记录可下载。"); for (let r of records) { let id = getIdFromUrl(r.url); if (!id) { console.warn("未找到id:", r.url); continue; } const apiUrl = `https://www.missevan.com/sound/getdm?soundid=${id}`; let safeTitle = r.title.trim().replace(/[\/\\:*?"<>|]/g, "_"); console.log("请求弹幕:", apiUrl); await downloadXml(apiUrl, `${safeTitle}.xml`); } } // 更新菜单 function updateMenu() { for (let id of menuIds) GM_unregisterMenuCommand(id); menuIds = []; menuIds.push(GM_registerMenuCommand("① " + (recording ? "暂停记录" : "开始记录"), toggleRecording)); menuIds.push(GM_registerMenuCommand("② 清空记录 (" + records.length + ")", clearRecords)); menuIds.push(GM_registerMenuCommand("③ 查看记录 (" + records.length + ")", viewRecords)); menuIds.push(GM_registerMenuCommand("④ 下载记录 (link.json)", downloadRecords)); menuIds.push(GM_registerMenuCommand("⑤ 下载当前弹幕", downloadCurrent)); menuIds.push(GM_registerMenuCommand("⑥ 下载所有弹幕", downloadAll)); } // 页面加载时:如果正在记录,先安全写一次 if (recording) { safeAddRecord(location.href); } // 定时检测 url 变化(处理SPA路由切换) setInterval(() => { if (!recording) return; if (location.href !== lastUrl) { let newUrl = location.href; lastUrl = newUrl; safeAddRecord(newUrl); } }, 1000); updateMenu(); })();