SP2netease Custom

Click a floating button to get the last played song from Last.fm and search it on Netease Music

您需要先安裝使用者腳本管理器擴展,如 TampermonkeyGreasemonkeyViolentmonkey 之後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyUserscripts 後才能安裝該腳本。

你需要先安裝一款使用者腳本管理器擴展,比如 Tampermonkey,才能安裝此腳本

您需要先安裝使用者腳本管理器擴充功能後才能安裝該腳本。

(我已經安裝了使用者腳本管理器,讓我安裝!)

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

(我已經安裝了使用者樣式管理器,讓我安裝!)

// ==UserScript==
// @name         SP2netease Custom
// @namespace    http://tampermonkey.net/
// @version      1.1
// @description  Click a floating button to get the last played song from Last.fm and search it on Netease Music
// @author       You
// @match        *://*/*
// @grant        GM_addStyle
// @grant        GM_xmlhttpRequest
// @grant        GM_getValue
// @grant        GM_setValue
// @connect      last.fm
// ==/UserScript==

(function() {
    'use strict';

    // 存储工具函数
    const storage = {
        // 设置存储
        setItem: function(key, value) {
            try {
                // 首先尝试使用油猴存储API(跨域)
                if (typeof GM_setValue !== 'undefined') {
                    GM_setValue(key, value);
                    console.log('使用GM_setValue保存:', key, value);
                    return;
                }
            } catch (e) {
                console.log('GM_setValue不可用:', e);
            }
            
            try {
                // 然后尝试使用localStorage
                localStorage.setItem(key, value);
                console.log('使用localStorage保存:', key, value);
            } catch (e) {
                // localStorage不可用时使用cookie
                console.log('localStorage不可用,使用cookie保存:', key, value);
                this.setCookie(key, value, 365);
            }
        },
        
        // 获取存储
        getItem: function(key) {
            try {
                // 首先尝试使用油猴存储API(跨域)
                if (typeof GM_getValue !== 'undefined') {
                    const value = GM_getValue(key, null);
                    console.log('从GM_getValue读取:', key, value);
                    return value;
                }
            } catch (e) {
                console.log('GM_getValue不可用:', e);
            }
            
            try {
                // 然后尝试使用localStorage
                const value = localStorage.getItem(key);
                console.log('从localStorage读取:', key, value);
                return value;
            } catch (e) {
                // localStorage不可用时使用cookie
                console.log('localStorage不可用,从cookie读取:', key);
                return this.getCookie(key);
            }
        },
        
        // 设置cookie
        setCookie: function(name, value, days) {
            let expires = "";
            if (days) {
                const date = new Date();
                date.setTime(date.getTime() + (days * 24 * 60 * 60 * 1000));
                expires = "; expires=" + date.toUTCString();
            }
            // 设置cookie为跨域共享
            document.cookie = name + "=" + (value || "") + expires + "; path=/; domain=." + window.location.hostname.split('.').slice(-2).join('.');
        },
        
        // 获取cookie
        getCookie: function(name) {
            const nameEQ = name + "=";
            const ca = document.cookie.split(';');
            for (let i = 0; i < ca.length; i++) {
                let c = ca[i];
                while (c.charAt(0) === ' ') c = c.substring(1, c.length);
                if (c.indexOf(nameEQ) === 0) return c.substring(nameEQ.length, c.length);
            }
            return null;
        }
    };
    
    // 创建设置面板
    const createSettingsPanel = () => {
        // 创建面板元素
        const panel = document.createElement('div');
        panel.id = 'netease-settings-panel';
        
        // 添加面板内容
        panel.innerHTML = `
            <div class="netease-settings-header">
                <h3>Last.fm 用户名设置</h3>
                <button id="close-settings">×</button>
            </div>
            <div class="netease-settings-content">
                <label for="lastfm-username">用户名:</label>
                <input type="text" id="lastfm-username" placeholder="请输入Last.fm用户名">
                <button id="save-settings">保存</button>
            </div>
        `;
        
        // 设置输入框的默认值
        const usernameInput = panel.querySelector('#lastfm-username');
        const savedUsername = storage.getItem('lastfmUsername');
        
        // 调试信息
        console.log('从存储中读取到的用户名:', savedUsername);
        
        if (savedUsername !== null && savedUsername !== '') {
            usernameInput.value = savedUsername;
            console.log('设置输入框默认值为:', savedUsername);
        } else {
            console.log('未设置输入框默认值');
        }
        
        // 设置面板为浮动窗口样式
        panel.style.cssText = `
            position: fixed;
            top: 50%;
            left: 50%;
            transform: translate(-50%, -50%);
            width: 300px;
            background: white;
            border: 1px solid #ccc;
            border-radius: 8px;
            box-shadow: 0 4px 12px rgba(0,0,0,0.3);
            z-index: 10002;
            padding: 20px;
        `;
        
        // 设置面板头部样式
        const header = panel.querySelector('.netease-settings-header');
        header.style.cssText = `
            display: flex;
            justify-content: space-between;
            align-items: center;
            margin-bottom: 15px;
        `;
        
        // 设置关闭按钮样式
        const closeButton = panel.querySelector('#close-settings');
        closeButton.style.cssText = `
            background: none;
            border: none;
            font-size: 20px;
            cursor: pointer;
            padding: 0;
            width: 24px;
            height: 24px;
            display: flex;
            align-items: center;
            justify-content: center;
        `;
        
        // 设置内容区域样式
        const content = panel.querySelector('.netease-settings-content');
        content.style.cssText = `
            display: flex;
            flex-direction: column;
            gap: 10px;
        `;
        
        // 设置标签样式
        const label = panel.querySelector('label');
        label.style.cssText = `
            font-weight: bold;
        `;
        
        // 设置输入框样式
        const input = panel.querySelector('input');
        input.style.cssText = `
            padding: 8px;
            border: 1px solid #ccc;
            border-radius: 4px;
        `;
        
        // 设置保存按钮样式
        const saveButton = panel.querySelector('#save-settings');
        saveButton.style.cssText = `
            padding: 10px;
            background-color: #E20000;
            color: white;
            border: none;
            border-radius: 4px;
            cursor: pointer;
            font-size: 14px;
        `;
        
        // 将面板添加到页面
        document.body.appendChild(panel);
        
        // 添加事件监听器
        document.getElementById('close-settings').addEventListener('click', () => {
            panel.remove();
        });
        
        document.getElementById('save-settings').addEventListener('click', () => {
            const username = document.getElementById('lastfm-username').value.trim();
            if (username) {
                try {
                    storage.setItem('lastfmUsername', username);
                    alert('设置已保存');
                    panel.remove();
                } catch (e) {
                    alert('保存失败: ' + e.message);
                    // 保存失败时不移除面板,让用户重新尝试
                }
            } else {
                alert('请输入有效的用户名');
                // 不移除面板,让用户重新输入
            }
        });
        
        // 点击面板外部关闭面板
        panel.addEventListener('click', (e) => {
            if (e.target === panel) {
                panel.remove();
            }
        });
        
        // 阻止事件冒泡到面板外部
        const contentElement = panel.querySelector('.netease-settings-content');
        contentElement.addEventListener('click', (e) => {
            e.stopPropagation();
        });
    };
    
    // 创建浮动按钮
    const createFloatingButton = () => {
        // 创建按钮元素
        const button = document.createElement('div');
        button.id = 'netease-search-button';
        
        // 添加SVG图标
        button.innerHTML = '<svg t="1754156884017" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="3730" width="32" height="32"><path d="M512 32c265.12 0 480 214.88 480 480 0 265.088-214.88 480-480 480-265.088 0-480-214.912-480-480C32 246.848 246.912 32 512 32z m82.08 173.568c-51.84 17.152-76.736 65.44-61.856 120.32l5.344 20.192c-11.2 2.432-22.208 5.696-32.928 9.824-50.656 19.52-90.848 62.816-104.896 113.024a153.696 153.696 0 0 0-5.088 55.04 137.92 137.92 0 0 0 57.536 100.576 121.504 121.504 0 0 0 103.264 17.6 120.96 120.96 0 0 0 63.68-42.56c24.736-32.288 32.096-74.752 20.8-119.52-4.16-16.32-9.344-34.368-14.4-51.904l-5.376-18.976c21.44 5.376 41.184 16.032 57.44 31.04 56 52.288 66.784 142.368 25.088 209.536-36.608 59.008-107.936 97.088-181.664 97.088a225.76 225.76 0 0 1-225.536-225.472c0-28.992 5.696-57.696 16.832-84.448a221.664 221.664 0 0 1 41.856-65.6 223.712 223.712 0 0 1 81.408-56.416A31.712 31.712 0 0 0 412.064 256a299.52 299.52 0 0 0-25.696 11.808 289.504 289.504 0 0 0-100 86.432 286.56 286.56 0 0 0-54.304 167.136c0 159.296 129.6 288.896 288.96 288.896 95.2 0 187.648-49.856 235.552-127.072 58.304-93.888 43.232-215.584-35.712-289.344-32.32-30.208-74.368-47.872-118.784-51.776-2.304-8.96-5.888-22.592-8.64-32.832-2.08-7.584-3.104-16.16-0.864-23.808a31.808 31.808 0 0 1 38.4-21.44c4.416 1.184 8.608 3.264 12.256 6.048 3.84 2.88 6.848 6.624 10.368 9.888a31.744 31.744 0 0 0 49.856-37.664l-0.64-1.056a65.312 65.312 0 0 0-14.4-16.448 100.48 100.48 0 0 0-53.376-23.392 96.128 96.128 0 0 0-40.96 4.224z m-40.224 201.92c3.36 12.416 7.04 25.344 10.752 38.176 4.928 17.28 9.888 34.432 13.824 49.92 4.576 18.176 6.624 44.192-9.664 65.472a57.056 57.056 0 0 1-30.24 19.936 58.464 58.464 0 0 1-50.144-8.544 73.92 73.92 0 0 1-30.528-53.952 89.6 89.6 0 0 1 2.944-32.416c8.8-31.36 34.304-58.528 66.624-71.008 8.672-3.36 17.536-5.856 26.432-7.584z" fill="#FFFFFF" p-id="3731"></path></svg>';
        
        // 添加按钮样式
        GM_addStyle(`
            #netease-search-button {
                position: fixed;
                width: 32px;
                height: 32px;
                border-radius: 50%;
                background-color: #E20000; /* 红色背景 */
                cursor: pointer;
                z-index: 10000;
                box-shadow: 0 2px 10px rgba(0,0,0,0.3);
                transition: opacity 0.3s;
                display: flex;
                justify-content: center;
                align-items: center;
            }
            
            #netease-search-button:hover {
                opacity: 0.8;
            }
            
            #netease-search-button svg {
                width: 100%;
                height: 100%;
            }
        `);
        
        // 设置按钮初始位置
        // 从存储中读取保存的位置,如果没有则使用默认位置
        const savedPosition = storage.getItem('neteaseButtonPosition');
        if (savedPosition) {
            const position = JSON.parse(savedPosition);
            button.style.left = position.x + 'px';
            button.style.top = position.y + 'px';
        } else {
            button.style.left = '20px';
            button.style.top = '20px';
        }
        
        // 添加拖动功能
        let isDragging = false;
        let offsetX, offsetY;
        
        button.addEventListener('mousedown', (e) => {
            isDragging = true;
            offsetX = e.clientX - button.getBoundingClientRect().left;
            offsetY = e.clientY - button.getBoundingClientRect().top;
            button.style.cursor = 'grabbing';
            e.preventDefault();
        });
        
        document.addEventListener('mousemove', (e) => {
            if (!isDragging) return;
            button.style.left = (e.clientX - offsetX) + 'px';
            button.style.top = (e.clientY - offsetY) + 'px';
        });
        
        document.addEventListener('mouseup', () => {
            isDragging = false;
            button.style.cursor = 'pointer';
            
            // 保存按钮位置到存储
            const position = {
                x: button.offsetLeft,
                y: button.offsetTop
            };
            storage.setItem('neteaseButtonPosition', JSON.stringify(position));
        });
        
        // 添加右键点击功能打开设置
        button.addEventListener('contextmenu', (e) => {
            e.preventDefault();
            createSettingsPanel();
        });
    
    // 添加油猴脚本管理页面的设置入口
    if (window.location.href.startsWith('chrome-extension://') || window.location.href.startsWith('moz-extension://')) {
        // 创建设置按钮
        const settingsButton = document.createElement('button');
        settingsButton.textContent = 'SP2netease 设置';
        settingsButton.style.cssText = `
            position: fixed;
            top: 10px;
            right: 10px;
            z-index: 10001;
            padding: 10px;
            background-color: #E20000;
            color: white;
            border: none;
            border-radius: 4px;
            cursor: pointer;
            font-size: 14px;
        `;
        
        settingsButton.addEventListener('click', (e) => {
            e.preventDefault();
            createSettingsPanel();
        });
        
        document.body.appendChild(settingsButton);
    }
        
        // 添加左键点击功能
        button.addEventListener('click', () => {
            if (!isDragging) {
                // 直接搜索最近播放的歌曲
                searchDirectly();
            }
        });
        
        // 将按钮添加到页面
        document.body.appendChild(button);
    };
    
    // 直接搜索功能 - 在网易云音乐中搜索
    const searchDirectly = () => {
        // 从存储中读取保存的Last.fm用户名
        const lastfmUsername = storage.getItem('lastfmUsername');
        
        // 调试信息
        console.log('读取到的Last.fm用户名:', lastfmUsername);
        
        // 检查用户名是否存在
        if (!lastfmUsername || lastfmUsername.trim() === '') {
            alert('请先设置Last.fm用户名');
            createSettingsPanel();
            return;
        }
        
        // 自动获取Last.fm最近播放的歌曲信息
        GM_xmlhttpRequest({
            method: 'GET',
            url: `https://www.last.fm/zh/user/${lastfmUsername}`,
            onload: function(response) {
                const parser = new DOMParser();
                const doc = parser.parseFromString(response.responseText, 'text/html');
                
                // 获取最近播放的歌曲信息
                const recentTracks = doc.querySelector('.chartlist tbody tr');
                
                if (!recentTracks) {
                    alert('无法找到最近播放的歌曲信息');
                    return;
                }
                
                const songNameElement = recentTracks.querySelector('.chartlist-name a');
                const artistElement = recentTracks.querySelector('.chartlist-artist a');
                
                if (!songNameElement || !artistElement) {
                    alert('无法获取歌曲信息');
                    return;
                }
                
                const songName = songNameElement.textContent.trim();
                const artist = artistElement.textContent.trim();
                
                // 构建搜索URL
                const searchQuery = encodeURIComponent(`${songName} ${artist}`);
                const searchUrl = `https://music.163.com/#/search/m/?s=${searchQuery}`;
                
                // 在新窗口打开搜索页面
                window.open(searchUrl, '_blank');
            },
            onerror: function(error) {
                alert('获取歌曲信息失败: ' + error.statusText);
            }
        });
    };
    
    // 页面加载完成后创建按钮
    if (document.readyState === 'loading') {
        document.addEventListener('DOMContentLoaded', createFloatingButton);
    } else {
        createFloatingButton();
    }
})();