您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Floating MP3 download button for Hoopr with correct title from mini-player
// ==UserScript== // @name Hoopr Direct MP3 Downloader (Mini-Player Title Fixed) // @namespace http://tampermonkey.net/ // @version 1.3.3 // @license shivafx55 // @description Floating MP3 download button for Hoopr with correct title from mini-player // @match *://*.hoopr.ai/* // @grant GM_download // ==/UserScript== (function () { 'use strict'; let button; function getMiniPlayerTitle() { let titleElem = document.querySelector('#nameOfSong'); if (titleElem) { let text = titleElem.innerText.trim(); return text.replace(/[<>:\"/\\|?*]+/g, '') || "Hoopr_Track"; } return "Hoopr_Track"; } function createDownloadButton() { if (button) return; button = document.createElement("button"); button.innerText = "⬇ Direct MP3"; Object.assign(button.style, { position: "fixed", bottom: "20px", right: "20px", background: "#ff3366", color: "white", padding: "10px 15px", borderRadius: "8px", border: "none", cursor: "pointer", zIndex: 999999, fontSize: "14px" }); button.onclick = async () => { let audio = document.querySelector("audio"); if (!audio) { alert("No audio playing!"); return; } let src = audio.src || audio.currentSrc; if (!src) { alert("No audio source found!"); return; } let trackName = getMiniPlayerTitle(); if (src.startsWith("blob:")) { try { let response = await fetch(src); let blob = await response.blob(); let url = URL.createObjectURL(blob); GM_download(url, trackName + ".mp3"); } catch (err) { alert("Failed to fetch blob audio: " + err); } } else { GM_download(src, trackName + ".mp3"); } }; document.body.appendChild(button); } // Watch for audio elements being added const observer = new MutationObserver(() => { if (document.querySelector("audio")) { createDownloadButton(); } }); observer.observe(document.body, { childList: true, subtree: true }); })();