Spotify to YouTube Redirector

Redirects Spotify track links to YouTube, using the omijn/yt2spotify converter endpoint.

您需要先安装一个扩展,例如 篡改猴Greasemonkey暴力猴,之后才能安装此脚本。

You will need to install an extension such as Tampermonkey to install this script.

您需要先安装一个扩展,例如 篡改猴暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴Userscripts ,之后才能安装此脚本。

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

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

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

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

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

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

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

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

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

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

// ==UserScript==
// @name         Spotify to YouTube Redirector
// @namespace    http://tampermonkey.net/
// @version      1.3
// @description  Redirects Spotify track links to YouTube, using the omijn/yt2spotify converter endpoint.
// @author       CL Using backend from ytm2spotify by omijn
// @match        *://*/*
// @grant        GM_xmlhttpRequest
// @grant        GM_getValue
// @grant        GM_setValue
// @grant        GM_registerMenuCommand
// @connect      *
// @license MIT
// ==/UserScript==

(function() {
    'use strict';

    // --- Configuration ---
    // Default API URL for the yt2spotify converter service.
    // This points to the '/convert' endpoint.
    const DEFAULT_CONVERTER_API_URL = 'https://ytm2spotify.com//convert'; // Updated API URL to include /convert

    let converterApiUrl = GM_getValue('converterApiUrl', DEFAULT_CONVERTER_API_URL);

    // --- Helper Functions ---

    function showNotification(message, isError = false) {
        const notificationId = 'spotify-yt-redirector-notification';
        let notificationElement = document.getElementById(notificationId);

        if (!notificationElement) {
            notificationElement = document.createElement('div');
            notificationElement.id = notificationId;
            Object.assign(notificationElement.style, {
                position: 'fixed',
                top: '20px',
                right: '20px',
                padding: '15px 20px',
                borderRadius: '8px',
                color: 'white',
                zIndex: '99999',
                fontSize: '16px',
                boxShadow: '0 4px 12px rgba(0,0,0,0.2)',
                opacity: '0',
                transition: 'opacity 0.5s ease-in-out, transform 0.3s ease-in-out',
                transform: 'translateX(100%)',
                fontFamily: 'Arial, sans-serif'
            });
            document.body.appendChild(notificationElement);
        }

        notificationElement.textContent = message;
        notificationElement.style.backgroundColor = isError ? '#e74c3c' : '#2ecc71'; // Red for error, Green for success

        // Animate in
        setTimeout(() => {
            notificationElement.style.opacity = '1';
            notificationElement.style.transform = 'translateX(0)';
        }, 50);

        // Automatically hide after some time
        setTimeout(() => {
            notificationElement.style.opacity = '0';
            notificationElement.style.transform = 'translateX(100%)';
        }, isError ? 7000 : 4000);
    }


    function getYouTubeLink(spotifyUrl, callback) {
        if (!converterApiUrl) {
            showNotification('Converter API URL is not configured. Please set it via the script menu if the default is incorrect.', true);
            console.error('Spotify to YouTube Redirector: Converter API URL not configured or empty.');
            callback(null, 'Configuration error: Converter API URL not set.');
            return;
        }

        showNotification('Converting Spotify link to YouTube...');
        console.log(`Attempting to convert Spotify URL: ${spotifyUrl} using API: ${converterApiUrl}`);

        // Construct the API request URL
        // The backend expects 'to_service=youtube_ytm' for YouTube conversion.
        const apiUrl = `${converterApiUrl}?url=${encodeURIComponent(spotifyUrl)}&to_service=youtube_ytm`;

        GM_xmlhttpRequest({
            method: 'GET',
            url: apiUrl,
            timeout: 15000,
            onload: function(response) {
                try {
                    if (response.status >= 200 && response.status < 300) {
                        const data = JSON.parse(response.responseText);
                        if (data && data.results && data.results.length > 0 && data.results[0].url) {
                            console.log('Conversion successful. YouTube URL:', data.results[0].url);
                            callback(data.results[0].url, null);
                        } else if (data && data.manual_search_link) {
                            console.log('Direct link not found, using manual search link:', data.manual_search_link);
                            callback(data.manual_search_link, null);
                        }
                        else {
                            console.error('Conversion failed: Invalid response structure from API. Expected "results[0].url" or "manual_search_link". Response:', data);
                            callback(null, 'Invalid response structure from converter API.');
                        }
                    } else {
                        console.error(`Conversion failed: API request error. Status: ${response.status}`, response.responseText);
                        callback(null, `Converter API request failed (Status: ${response.status}). Check API endpoint.`);
                    }
                } catch (e) {
                    console.error('Conversion failed: Error parsing API response. Is it valid JSON?', e, response.responseText);
                    callback(null, 'Error parsing converter API response.');
                }
            },
            onerror: function(error) {
                console.error('Conversion failed: Network error or CORS issue with API. Ensure the API URL is correct and allows cross-origin requests if necessary.', error);
                callback(null, 'Network error or CORS issue with converter API.');
            },
            ontimeout: function() {
                console.error('Conversion failed: API request timed out.');
                callback(null, 'Converter API request timed out.');
            }
        });
    }

    // --- Event Listener ---
    document.addEventListener('click', function(event) {
        let targetElement = event.target;
        while (targetElement && targetElement.tagName !== 'A') {
            targetElement = targetElement.parentElement;
        }

        if (targetElement && targetElement.href) {
            const url = targetElement.href;
            // Regex to identify Spotify track links
            // Example: https://open.spotify.com/track/TRACK_ID_HERE
            const spotifyTrackRegex = /^https?:\/\/open\.spotify\.com\/(?:[a-zA-Z]{2}-[a-zA-Z]{2}\/)?track\/([a-zA-Z0-9]+)/;

            if (spotifyTrackRegex.test(url)) {
                event.preventDefault();
                event.stopPropagation();

                console.log('Spotify track link clicked:', url);

                getYouTubeLink(url, function(youtubeLink, error) {
                    if (youtubeLink) {
                        showNotification(`Redirecting to YouTube: ${youtubeLink}`);
                        window.location.href = youtubeLink;
                    } else {
                        showNotification(`Error: ${error || 'Could not convert link.'} Opening original Spotify link.`, true);
                        setTimeout(() => {
                             window.open(url, '_blank');
                        }, 2000);
                    }
                });
            }
        }
    }, true); // Use capture phase

    // --- Configuration Menu ---
    GM_registerMenuCommand('Set Converter API URL', function() {
        const newUrl = prompt('Enter the full URL for your converter API (e.g., https://ytm2spotify.com//convert):', GM_getValue('converterApiUrl', DEFAULT_CONVERTER_API_URL));
        if (newUrl === null) return; // User cancelled

        if (newUrl.trim() === '') {
            GM_setValue('converterApiUrl', DEFAULT_CONVERTER_API_URL);
            converterApiUrl = DEFAULT_CONVERTER_API_URL;
            showNotification(`Converter API URL reset to default: ${DEFAULT_CONVERTER_API_URL}`);
        } else {
            converterApiUrl = newUrl.trim();
            GM_setValue('converterApiUrl', converterApiUrl);
            showNotification(`Converter API URL updated to: ${converterApiUrl}`);
        }
    });

    // Initial check and notification
    console.log('Spotify to YouTube Redirector script loaded (v1.3).');
    if (GM_getValue('converterApiUrl', DEFAULT_CONVERTER_API_URL) === 'http://localhost/your_converter_path/convert') {
         showNotification('Spotify Redirector: API URL might be an old placeholder. Current default is for yt2spotify. Configure via script menu if needed.', true);
    } else {
         showNotification('Spotify to YouTube Redirector active.');
    }

})();