Spotify to Genius (Improved)

Button Genius dengan performa optimal dan fitur lengkap

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Greasemonkey 油猴子Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Userscripts ,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name        Spotify to Genius (Improved)
// @namespace   Violentmonkey Scripts
// @match       https://open.spotify.com/*
// @grant       none
// @version     11.0
// @author      Improved Version
// @description Button Genius dengan performa optimal dan fitur lengkap
// ==/UserScript==

(function() {
    'use strict';

    // Cache untuk hasil pencarian
    const searchCache = new Map();
    const CACHE_LIMIT = 50;

    // Cleanup functions
    let cleanupFunctions = [];
    let hoverTimeout;

    // State management
    let lastUrl = location.href;
    let currentButton = null;

    /**
     * Mendapatkan informasi lagu yang sedang diputar
     */
    function getNowPlaying() {
        // Prioritas 1: Media Session API (lebih akurat)
        if (navigator.mediaSession?.metadata) {
            const meta = navigator.mediaSession.metadata;
            const title = meta.title?.trim();
            const artist = meta.artist?.trim();
            if (title && artist) {
                return `${title} ${artist}`;
            }
        }

        // Prioritas 2: Document Title
        let cleanTitle = document.title
            .replace(/^Spotify\s*[\u2013\u2014\-–—]\s*/i, "")
            .replace(/\s*[\|\-–—]\s*Spotify\s*$/i, "")
            .trim();

        // Validasi format
        if (cleanTitle && 
            cleanTitle !== "Spotify" && 
            cleanTitle !== "Web Player" &&
            cleanTitle.includes('-')) {
            return cleanTitle;
        }

        return null;
    }

    /**
     * Pencarian Genius dengan caching
     */
    async function searchGenius(track) {
        // Cek cache
        if (searchCache.has(track)) {
            return searchCache.get(track);
        }

        const searchUrl = `https://genius.com/api/search/multi?q=${encodeURIComponent(track)}`;
        
        try {
            const response = await fetch(searchUrl);
            if (!response.ok) throw new Error('API request failed');
            
            const data = await response.json();
            
            // Simpan ke cache
            if (searchCache.size >= CACHE_LIMIT) {
                const firstKey = searchCache.keys().next().value;
                searchCache.delete(firstKey);
            }
            searchCache.set(track, data);
            
            return data;
        } catch (error) {
            console.error('Genius API error:', error);
            throw error;
        }
    }

    /**
     * Ekstrak URL lagu dari response Genius
     */
    function extractSongUrl(data) {
        const sections = data.response.sections;
        
        for (const section of sections) {
            if (section.type === 'top_hit' || section.type === 'song') {
                if (section.hits && section.hits.length > 0) {
                    return section.hits[0].result.url;
                }
            }
        }
        
        return null;
    }

    /**
     * Handle klik tombol Genius
     */
    async function handleGeniusClick(btn, track) {
        const gIcon = btn.querySelector('span:first-child');
        const originalText = gIcon.innerText;
        
        // Animasi loading
        gIcon.innerText = "⏳";
        btn.style.transform = 'scale(0.95)';
        btn.style.pointerEvents = 'none';

        try {
            const data = await searchGenius(track);
            const songUrl = extractSongUrl(data);
            
            if (songUrl) {
                window.open(songUrl, '_blank');
            } else {
                // Fallback ke halaman pencarian
                window.open(`https://genius.com/search?q=${encodeURIComponent(track)}`, '_blank');
            }
        } catch (error) {
            // Error fallback
            window.open(`https://genius.com/search?q=${encodeURIComponent(track)}`, '_blank');
        } finally {
            // Reset button state
            setTimeout(() => {
                gIcon.innerText = originalText;
                btn.style.transform = 'scale(1)';
                btn.style.pointerEvents = 'auto';
            }, 200);
        }
    }

    /**
     * Membuat floating button
     */
    function createFloatingButton() {
        // Hapus button lama jika ada
        if (currentButton) {
            currentButton.remove();
        }

        const btn = document.createElement('div');
        btn.id = 'genius-pro-btn';
        btn.setAttribute('role', 'button');
        btn.setAttribute('aria-label', 'Buka lirik di Genius');
        btn.setAttribute('tabindex', '0');
        
        btn.innerHTML = `
            <div style="display:flex; align-items:center; gap:10px; padding: 0 15px;">
                <span style="font-weight:900; font-size:20px; flex-shrink:0;">G</span>
                <span id="genius-track-display" style="font-size:12px; white-space:nowrap; overflow:hidden; text-overflow:ellipsis; display:none; font-weight:bold; max-width:250px;"></span>
            </div>
            <div id="genius-tooltip" style="
                position: absolute;
                bottom: 60px;
                right: 0;
                background: rgba(0,0,0,0.9);
                color: #fff;
                padding: 6px 12px;
                border-radius: 6px;
                font-size: 11px;
                opacity: 0;
                pointer-events: none;
                transition: opacity 0.2s ease;
                white-space: nowrap;
                font-family: sans-serif;
            ">Klik untuk buka di Genius</div>
        `;

        btn.style.cssText = `
            position: fixed;
            bottom: 110px;
            right: 30px;
            z-index: 999999;
            background-color: #f7f219;
            color: #000;
            height: 50px;
            width: 50px;
            border-radius: 50px;
            cursor: pointer;
            box-shadow: 0 8px 25px rgba(0,0,0,0.5);
            font-family: sans-serif;
            transition: all 0.4s cubic-bezier(0.19, 1, 0.22, 1), transform 0.2s ease;
            display: flex;
            align-items: center;
            overflow: hidden;
        `;

        const trackText = btn.querySelector('#genius-track-display');
        const tooltip = btn.querySelector('#genius-tooltip');

        // Hover effect dengan debounce
        btn.onmouseenter = () => {
            clearTimeout(hoverTimeout);
            hoverTimeout = setTimeout(() => {
                const track = getNowPlaying();
                if (track) {
                    trackText.innerText = track;
                    trackText.style.display = 'block';
                    btn.style.width = 'auto';
                    btn.style.borderRadius = '15px';
                    tooltip.style.opacity = '1';
                }
            }, 100);
        };

        btn.onmouseleave = () => {
            clearTimeout(hoverTimeout);
            trackText.style.display = 'none';
            btn.style.width = '50px';
            btn.style.borderRadius = '50px';
            tooltip.style.opacity = '0';
        };

        // Click handler
        btn.onclick = () => {
            const track = getNowPlaying();
            if (!track) {
                tooltip.innerText = 'Tidak ada lagu yang diputar';
                tooltip.style.opacity = '1';
                tooltip.style.background = 'rgba(220, 38, 38, 0.9)';
                setTimeout(() => {
                    tooltip.style.opacity = '0';
                    tooltip.style.background = 'rgba(0,0,0,0.9)';
                    tooltip.innerText = 'Klik untuk buka di Genius';
                }, 2000);
                return;
            }
            handleGeniusClick(btn, track);
        };

        // Keyboard support
        btn.onkeypress = (e) => {
            if (e.key === 'Enter' || e.key === ' ') {
                e.preventDefault();
                btn.onclick();
            }
        };

        document.body.appendChild(btn);
        currentButton = btn;
    }

    /**
     * Toggle button berdasarkan URL
     */
    function toggleButtonBasedOnURL() {
        const isLyricsPage = window.location.href.includes('/lyrics');
        let btn = document.getElementById('genius-pro-btn');

        if (isLyricsPage) {
            if (!btn) {
                createFloatingButton();
            } else {
                btn.style.display = 'flex';
            }
        } else {
            if (btn) {
                btn.style.display = 'none';
            }
        }
    }

    /**
     * Monitor URL changes dengan MutationObserver
     */
    function observeUrlChanges() {
        const observer = new MutationObserver(() => {
            const url = location.href;
            if (url !== lastUrl) {
                lastUrl = url;
                toggleButtonBasedOnURL();
            }
        });

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

        cleanupFunctions.push(() => observer.disconnect());
    }

    /**
     * Listen popstate untuk navigation
     */
    function setupPopstateListener() {
        const handler = () => {
            lastUrl = location.href;
            toggleButtonBasedOnURL();
        };
        
        window.addEventListener('popstate', handler);
        cleanupFunctions.push(() => window.removeEventListener('popstate', handler));
    }

    /**
     * Cleanup saat page unload
     */
    function setupCleanup() {
        window.addEventListener('beforeunload', () => {
            cleanupFunctions.forEach(fn => fn());
            cleanupFunctions = [];
            if (currentButton) {
                currentButton.remove();
            }
        });
    }

    /**
     * Inisialisasi script
     */
    function init() {
        // Initial check
        toggleButtonBasedOnURL();
        
        // Setup observers dan listeners
        observeUrlChanges();
        setupPopstateListener();
        setupCleanup();
        
        console.log('✅ Spotify to Genius (Improved) loaded successfully');
    }

    // Tunggu DOM ready
    if (document.readyState === 'loading') {
        document.addEventListener('DOMContentLoaded', init);
    } else {
        init();
    }

})();