SunoEz

Adds download songs and copy lyrics buttons to Suno song pages

// ==UserScript==
// @name         SunoEz
// @namespace    http://tampermonkey.net/
// @version      0.3
// @description  Adds download songs and copy lyrics buttons to Suno song pages
// @author       yanyaoli
// @match        https://suno.com/song/*
// @grant        none
// ==/UserScript==

(function() {
    'use strict';

    let btnContainerAdded = false;
    function main() {
        if (btnContainerAdded) return;
        function getSongIdFromUrl() {
            const url = window.location.href;
            const regex = /\/song\/([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})/i;
            const match = url.match(regex);
            return match ? match[1] : null;
        }

        function showToast(message, duration = 3000) {
            const toast = document.createElement('div');
            toast.textContent = message;
            toast.style.position = 'fixed';
            toast.style.top = '20px';
            toast.style.right = '20px';
            toast.style.padding = '10px 15px';
            toast.style.borderRadius = '5px';
            toast.style.background = 'rgba(44, 62, 80, 0.7)';
            toast.style.color = 'white';
            toast.style.zIndex = '10000';

            document.body.appendChild(toast);

            setTimeout(() => {
                toast.remove();
            }, duration);
        }

        const container = document.querySelector('body > div.css-fhtuey > div.css-lb6gzl > div > div > div > div.css-1t2smg3 > div.css-1xwn4k3');

        if (!container) return;

        const btnContainer = document.createElement('div')
        btnContainer.className = 'chakra-stack css-1h50ovt'

        const downloadBtn = document.createElement('button');
        downloadBtn.className = 'chakra-button css-apxhpy';
        downloadBtn.textContent = 'Download';
        downloadBtn.style.marginLeft = '10px';

        const copyBtn = document.createElement('button');
        copyBtn.className = 'chakra-button css-apxhpy';
        copyBtn.textContent = 'Copy Lyrics';
        copyBtn.style.marginLeft = '10px';

        function downloadSong() {
            const songId = getSongIdFromUrl();
            const downloadUrl = `https://cdn1.suno.ai/${songId}.mp3`;
            const link = document.createElement('a');
            link.href = downloadUrl;
            link.download = songId + '.mp3';
            link.target = '_blank';
            document.body.appendChild(link);
            link.click();
            document.body.removeChild(link);
        }

        function copyLyrics() {
            const pElement = document.querySelector('body > div.css-fhtuey > div.css-lb6gzl > div > div > div > div.css-1t2smg3 > div.css-1dt9bqw > div.css-78su93 > p');
            const textToCopy = pElement ? pElement.textContent : '';

            navigator.clipboard.writeText(textToCopy).then(() => {
                showToast('Lyrics copied to clipboard!');
            }, (err) => {
                console.error('Could not copy text: ', err);
            });
        }

        downloadBtn.addEventListener('click', downloadSong)
        copyBtn.addEventListener('click', copyLyrics);

        btnContainer.appendChild(downloadBtn);
        btnContainer.appendChild(copyBtn);

        container.appendChild(btnContainer);

        btnContainerAdded = true;
    }

    main();

    const observer = new MutationObserver((mutationsList, observer) => {
        for (let mutation of mutationsList) {
            if (mutation.type === 'childList' && mutation.target.nodeName === 'HEAD') {
                main();
                break;
            }
        }
    });

    observer.observe(document.documentElement, { childList: true, subtree: true });
})();