您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
All-in-one enhancement: UI tweaks, draggable menu, playlist tools, device detection, lyrics, ratings & more!
// ==UserScript== // @name Spotify Web Enhancer (Full Suite) // @namespace Eliminater74 // @version 1.2.0 // @description All-in-one enhancement: UI tweaks, draggable menu, playlist tools, device detection, lyrics, ratings & more! // @author Eliminater74 // @license MIT // @match https://open.spotify.com/* // @grant GM_addStyle // @grant GM_getValue // @grant GM_setValue // ==/UserScript== (function () { 'use strict'; const save = (k, v) => GM_setValue(k, v); const load = (k, d) => GM_getValue(k, d); const settings = { compactMode: load('compactMode', false), autoResume: load('autoResume', true), lyricsToggle: load('lyricsToggle', true), wideMode: load('wideMode', false), ratings: JSON.parse(load('ratings', '{}')), iconX: load('iconX', '20px'), iconY: load('iconY', '20px') }; const css = ` #swe-gear { position:fixed; width:42px;height:42px; border-radius:50%; background:#1db954;color:#fff; display:flex;align-items:center;justify-content:center; font-size:22px;font-weight:bold; box-shadow:0 0 10px #000;cursor:grab; z-index:99999; left:${settings.iconX};top:${settings.iconY}; user-select:none; } #swe-menu { position:fixed; background:#181818;color:white; border-radius:10px; padding:12px;font-size:13px; z-index:99998;top:70px;left:20px; display:none;box-shadow:0 0 10px black; max-width:250px } #swe-menu label {display:block;margin:5px 0;} #swe-lyrics { position:fixed;bottom:10px;right:10px; width:400px;height:300px; resize:both;overflow:auto; z-index:99997; border:2px solid #555; display:none; } .swe-rating-bar { display:inline-block;margin-left:5px; } .swe-star {cursor:pointer;color:gray;font-size:16px;} .swe-star.active {color:gold;} body.swe-wide .Root__main-view-wrapper { max-width: 100% !important; }`; GM_addStyle(css); const gear = document.createElement('div'); gear.id = 'swe-gear'; gear.innerText = '⚙️'; document.body.appendChild(gear); const menu = document.createElement('div'); menu.id = 'swe-menu'; menu.innerHTML = ` <b>Spotify Enhancer</b><br> <label><input type="checkbox" id="swe-compact"> Compact Mode</label> <label><input type="checkbox" id="swe-resume"> Auto Resume</label> <label><input type="checkbox" id="swe-lyrics"> Lyrics Panel</label> <label><input type="checkbox" id="swe-wide"> Wide Mode</label> <button id="swe-export">Export Playlist</button> <button id="swe-dupes">Remove Duplicates</button> <div id="swe-device">Device: Unknown</div> `; document.body.appendChild(menu); const lyrics = document.createElement('iframe'); lyrics.id = 'swe-lyrics'; document.body.appendChild(lyrics); gear.addEventListener('click', () => menu.style.display = menu.style.display === 'none' ? 'block' : 'none'); // Draggable gear icon gear.onmousedown = function (e) { gear.style.cursor = 'grabbing'; let shiftX = e.clientX - gear.getBoundingClientRect().left; let shiftY = e.clientY - gear.getBoundingClientRect().top; function moveAt(pageX, pageY) { gear.style.left = pageX - shiftX + 'px'; gear.style.top = pageY - shiftY + 'px'; save('iconX', gear.style.left); save('iconY', gear.style.top); } function onMouseMove(e) { moveAt(e.pageX, e.pageY); } document.addEventListener('mousemove', onMouseMove); document.onmouseup = function () { document.removeEventListener('mousemove', onMouseMove); gear.onmouseup = null; gear.style.cursor = 'grab'; }; }; // Sync checkboxes document.getElementById('swe-compact').checked = settings.compactMode; document.getElementById('swe-resume').checked = settings.autoResume; document.getElementById('swe-lyrics').checked = settings.lyricsToggle; document.getElementById('swe-wide').checked = settings.wideMode; document.getElementById('swe-compact').onchange = () => { save('compactMode', event.target.checked); location.reload(); }; document.getElementById('swe-resume').onchange = () => save('autoResume', event.target.checked); document.getElementById('swe-lyrics').onchange = () => { save('lyricsToggle', event.target.checked); lyrics.style.display = event.target.checked ? 'block' : 'none'; }; document.getElementById('swe-wide').onchange = () => { save('wideMode', event.target.checked); document.body.classList.toggle('swe-wide', event.target.checked); }; // Wide mode init if (settings.wideMode) document.body.classList.add('swe-wide'); // Lyrics toggle if (settings.lyricsToggle) lyrics.style.display = 'block'; // Lyrics iframe loader function loadLyrics() { const track = document.querySelector('[data-testid="nowplaying-track-link"]')?.textContent; if (track) lyrics.src = `https://www.musixmatch.com/search/${encodeURIComponent(track)}`; } setInterval(loadLyrics, 15000); // Auto-resume logic if (settings.autoResume && location.pathname === '/') { const lastUri = load('lastUri', ''); if (lastUri) setTimeout(() => location.href = lastUri, 1500); } setInterval(() => { const uri = window.location.pathname.includes('/track/') ? location.href : ''; if (uri) save('lastUri', uri); }, 5000); // Export Playlist document.getElementById('swe-export').onclick = () => { const rows = [...document.querySelectorAll('[data-testid="tracklist-row"]')]; const lines = rows.map(row => { const title = row.querySelector('div span a')?.textContent || 'Unknown'; const artist = row.querySelector('span[class*="artists-albums"]')?.textContent || 'Unknown'; return `${title} - ${artist}`; }); const blob = new Blob([lines.join('\n')], { type: 'text/plain' }); const a = document.createElement('a'); a.href = URL.createObjectURL(blob); a.download = 'playlist.txt'; a.click(); }; // Remove Duplicates document.getElementById('swe-dupes').onclick = () => { const map = new Map(); document.querySelectorAll('[data-testid="tracklist-row"]').forEach(row => { const title = row.querySelector('div span a')?.textContent; const artist = row.querySelector('span[class*="artists-albums"]')?.textContent; const key = `${title}-${artist}`; if (map.has(key)) row.style.display = 'none'; else map.set(key, true); }); alert('Duplicates removed.'); }; // Keyboard controls document.addEventListener('keydown', e => { if (e.target.tagName.match(/INPUT|TEXTAREA/)) return; const play = document.querySelector('[data-testid="control-button-playpause"]'); const next = document.querySelector('[data-testid="control-button-skip-forward"]'); const prev = document.querySelector('[data-testid="control-button-skip-back"]'); const heart = document.querySelector('[data-testid="control-button-heart"]'); switch (e.key.toLowerCase()) { case ' ': play?.click(); e.preventDefault(); break; case 'arrowright': next?.click(); break; case 'arrowleft': prev?.click(); break; case 'l': heart?.click(); break; } }); // Device Detector function updateDeviceName() { const txt = document.querySelector('[data-testid="device-picker"]')?.innerText; if (txt) document.getElementById('swe-device').innerText = 'Device: ' + txt.split('\n')[0]; } setInterval(updateDeviceName, 10000); })();
QingJ © 2025
镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址