Hoopr Direct MP3 Downloader (Mini-Player Title Fixed)

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 });
})();