// ==UserScript==
// @name SpotiKitMobileDesktop - no more updates
// @namespace https://github.com/Myst1cX/SpotiKit
// @version 7.3.1.fork
// @description SpotiKit — Mobile‑like layout for Spotify Web. Floating player, bottom nav, library overlay, and more.
// @author kitbodega, Myst1cX (fork)
// @icon https://i.ibb.co/YF1nLPfK/2eca7229-ca6a-4ad6-8653-b80a6a0f8586.png
// @match https://open.spotify.com/*
// @match https://www.spotify.com/*/account/*
// @match https://www.spotify.com/*/premium/*
// @match https://www.spotify.com/*/duo/*
// @match https://www.spotify.com/*/student/*
// @match https://www.spotify.com/*/family/*
// @match https://payments.spotify.com/*
// @grant GM_addStyle
// @grant GM_registerMenuCommand
// @grant GM_setValue
// @grant GM_getValue
// @run-at document-start
// @homepageURL https://github.com/Myst1cX/SpotiKit
// @supportURL https://github.com/Myst1cX/SpotiKit/issues
// @updateURL https://raw.githubusercontent.com/Myst1cX/SpotiKit/main/SpotiKitMobileDesktop.user.js
// @downloadURL https://raw.githubusercontent.com/Myst1cX/SpotiKit/main/SpotiKitMobileDesktop.user.js
// ==/UserScript==
// THIS WAS THE INITIAL FORK. THERE WILL BE NO MORE UPDATES.
// USE: https://github.com/Myst1cX/spotifuck-userscript/raw/main/spotifuck-mobile.user.js
// RESOLVED (7.3.1.fork, Myst1cX)
// First batch:
// Added a browser-side equivalent of Spotifuck's ForceEnglish
// The userscript version forces English on open.spotify.com by overriding navigator.language/languages,
// stripping a non-English /intl-xx/ locale prefix from the URL if present,
// and flipping the account-level language setting at open.spotify.com/preferences to English via a hidden iframe when needed,
// then verifying it stuck before reloading the page automatically.
// Second batch:
// Restored version 7.3's visual premium spoof & payment page blockers:
// the PINK/GREEN constants, the REPLACE text-swap map, and the run() function - renamed to runPremium() to avoid clashes - that elabels
// "Free" text as "Premium," hides upgrade/install-app prompts, and redirects away from the premium/duo/student/family/payments pages
// Restored the @match lines for www.spotify.com/*/account,premium,duo,student,family/* and payments.spotify.com/*
// Third batch:
// Restricted the mobile bottom nav bar (Home/Search/Library) and its CSS to only load on open.spotify.com,
// so it stops appearing on other spotify.com pages like the account/premium sections.
// Fourth batch:
// a) Fixed forceEnglish() to also redirect www.spotify.com off non-English region path segments (e.g. /mx/ -> /us/),
// matching the fix already present on open.spotify.com. Previously only the open.spotify.com /intl-xx/ prefix was
// stripped, so www.spotify.com account/premium pages could still render in a non-English region path.
// b) Removed the old Spanish-language selectors/regex left over from before forceEnglish reliably forced English:
// the "obtener|conseguir" and "explorar|ver|planes" branches of the button-relabeling regexes, the Spanish
// "Planes Premium" XPath/aria-label selectors, the "abrir en...|instalar|descargar..." branch of the
// desktop-app-hiding regex, and the Spanish "Descarga canciones..." string match.
// c) Reworked runPremium()'s text-swap pass so it no longer does a blind full document.body walk on a timer.
// It's now MutationObserver-driven: scanText()/applyReplacements() only re-scan nodes that actually changed,
// batched and debounced (400ms) via handlePremiumMutations(), falling back to a full-body scan only if more
// than 20 nodes changed in one batch. Every swap is now logged (selector, before/after text, times applied)
// via logChange(), viewable through a new "Show everything replaced so far" userscript menu command.
// d) Added two independent userscript-manager menu toggles (via GM_registerMenuCommand + GM_setValue/GM_getValue),
// since the spoof behaves differently depending on which site it's touching:
// 1. "Visual Premium Spoof (open.spotify.com)" - the in-player text/badge relabeling and account widgets
// that render inside the web player.
// 2. "Visual Premium Spoof (www.spotify.com)" - the account site (spotify.com /premium, /duo, /student,
// /family, purchase pages) and the payments.spotify.com (plan payment blockers/redirects).
// Each toggle is independent, persists via GM storage, and reloads the page to apply. Both are enabled by default.
// Added the matching @grant lines (GM_registerMenuCommand, GM_setValue, GM_getValue) needed for the above.
(function() {
'use strict';
const PINK = '#FFD2D7';
const GREEN = '#1ed760';
// --- Per-site visual premium spoof toggles (Fourth batch) ---
const SPOOF_OPEN_KEY = 'spotikitPremSpoofOpen';
const SPOOF_WWW_KEY = 'spotikitPremSpoofWWW';
const HOST_IS_OPEN = location.hostname === 'open.spotify.com';
const HOST_IS_WWW = location.hostname === 'www.spotify.com' || location.hostname === 'payments.spotify.com';
function getFlag(key) {
try { return typeof GM_getValue === 'function' ? GM_getValue(key, true) : true; }
catch (e) { return true; }
}
function setFlag(key, val) {
try { if (typeof GM_setValue === 'function') GM_setValue(key, val); } catch (e) {}
}
function premiumSpoofEnabledHere() {
if (HOST_IS_OPEN) return getFlag(SPOOF_OPEN_KEY);
if (HOST_IS_WWW) return getFlag(SPOOF_WWW_KEY);
return false;
}
if (typeof GM_registerMenuCommand === 'function') {
GM_registerMenuCommand(
(getFlag(SPOOF_OPEN_KEY) ? '\u2705' : '\u274c') + ' Visual Premium Spoof (open.spotify.com)',
() => { setFlag(SPOOF_OPEN_KEY, !getFlag(SPOOF_OPEN_KEY)); location.reload(); }
);
GM_registerMenuCommand(
(getFlag(SPOOF_WWW_KEY) ? '\u2705' : '\u274c') + ' Visual Premium Spoof (www.spotify.com)',
() => { setFlag(SPOOF_WWW_KEY, !getFlag(SPOOF_WWW_KEY)); location.reload(); }
);
}
function forceEnglish() {
try {
Object.defineProperty(navigator, 'language', { get: () => 'en-US', configurable: true });
Object.defineProperty(navigator, 'languages', { get: () => ['en-US', 'en'], configurable: true });
} catch (e) {}
if (location.hostname === 'www.spotify.com') {
const ENGLISH_REGIONS = ['us', 'gb', 'ca', 'au', 'ie', 'nz'];
const wm = location.pathname.match(/^\/([a-z]{2})(\/.*)?$/i);
if (wm && !ENGLISH_REGIONS.includes(wm[1].toLowerCase())) {
location.replace(location.origin + '/us' + (wm[2] || '/') + location.search + location.hash);
return;
}
}
const m = location.pathname.match(/^\/intl-([a-z]{2})(\/.*)?$/i);
if (m && m[1].toLowerCase() !== 'en') {
location.replace(location.origin + (m[2] || '/') + location.search + location.hash);
return;
}
forceEnglishAccountSetting();
}
function forceEnglishAccountSetting() {
const PENDING_KEY = 'spotikitEnglishFlipPending';
const ATTEMPTS_KEY = 'spotikitEnglishFlipAttempts';
const MAX_ATTEMPTS = 3;
if (window.top !== window.self) return;
const verifying = localStorage.getItem(PENDING_KEY) === 'true';
if (verifying) localStorage.removeItem(PENDING_KEY);
const withPreferencesDoc = (callback) => {
let settled = false;
const fire = (doc, cleanup) => {
if (settled) return;
settled = true;
callback(doc, cleanup);
};
if (location.pathname.startsWith('/preferences')) {
fire(document, () => {});
return;
}
const iframe = document.createElement('iframe');
iframe.style.display = 'none';
iframe.src = 'https://open.spotify.com/preferences';
(document.documentElement || document.body).appendChild(iframe);
let removed = false;
const cleanup = () => {
if (removed) return;
removed = true;
iframe.remove();
};
iframe.addEventListener('load', () => {
try {
fire(iframe.contentDocument, cleanup);
} catch (e) {
cleanup();
fire(null, cleanup);
}
});
setTimeout(() => { cleanup(); fire(null, cleanup); }, 15000);
};
const giveUp = () => {};
const attemptFlip = () => {
withPreferencesDoc((doc, cleanup) => {
if (!doc) { cleanup(); giveUp(); return; }
applyEnglishToLanguageSelect(doc, (result) => {
if (!result.found) {
cleanup();
giveUp();
return;
}
if (!result.changed) {
cleanup();
localStorage.removeItem(ATTEMPTS_KEY);
return;
}
localStorage.setItem(PENDING_KEY, 'true');
setTimeout(() => { cleanup(); location.reload(); }, 1000);
});
});
};
if (!verifying) {
attemptFlip();
return;
}
withPreferencesDoc((doc, cleanup) => {
if (!doc) { cleanup(); giveUp(); return; }
applyEnglishToLanguageSelect(doc, (result) => {
cleanup();
if (result.found && result.value === 'en') {
localStorage.removeItem(ATTEMPTS_KEY);
return;
}
if (!result.found) {
giveUp();
return;
}
const attempts = parseInt(localStorage.getItem(ATTEMPTS_KEY) || '0', 10) + 1;
if (attempts >= MAX_ATTEMPTS) {
giveUp();
return;
}
localStorage.setItem(ATTEMPTS_KEY, String(attempts));
attemptFlip();
}, { readOnly: true });
});
}
function applyEnglishToLanguageSelect(doc, onDone, { readOnly = false } = {}) {
let settled = false;
const resolve = (result) => {
if (settled) return;
settled = true;
onDone(result);
};
const trySelect = () => {
const select = doc.getElementById('desktop.settings.selectLanguage');
if (!select) return false;
if (readOnly || select.value === 'en') {
resolve({ found: true, value: select.value, changed: false });
return true;
}
const win = doc.defaultView || window;
const nativeSetter = Object.getOwnPropertyDescriptor(win.HTMLSelectElement.prototype, 'value').set;
nativeSetter.call(select, 'en');
select.dispatchEvent(new Event('change', { bubbles: true }));
resolve({ found: true, value: 'en', changed: true });
return true;
};
if (trySelect()) return;
const win = doc.defaultView || window;
const startObserving = () => {
if (trySelect()) return;
const observer = new win.MutationObserver(() => {
if (trySelect()) observer.disconnect();
});
observer.observe(doc.body, { childList: true, subtree: true });
setTimeout(() => {
observer.disconnect();
resolve({ found: false, value: null, changed: false });
}, 12000);
};
if (doc.body) {
startObserving();
} else {
doc.addEventListener('DOMContentLoaded', startObserving, { once: true });
}
}
forceEnglish();
let ulFlag = false;
let ffDone = false;
let pfint = null;
let sidebarOverlayActive = false;
const cache = {
leftSidebar: null,
rootContainer: null,
bottomNav: null
};
function getLeftSidebar() {
if (!cache.leftSidebar || !document.contains(cache.leftSidebar)) {
cache.leftSidebar = document.querySelector('#Desktop_LeftSidebar_Id');
}
return cache.leftSidebar;
}
function getRootContainer() {
if (!cache.rootContainer || !document.contains(cache.rootContainer)) {
cache.rootContainer = document.querySelector('.Root__top-container') || document.querySelector('div[data-testid=root]');
}
return cache.rootContainer;
}
window.switchLs = function(forceCollapse = false) {
const leftSidebar = getLeftSidebar();
if (!leftSidebar) return;
const rootContainer = getRootContainer();
if (forceCollapse || sidebarOverlayActive) {
delete leftSidebar.dataset.overlay;
sidebarOverlayActive = false;
if (rootContainer) {
rootContainer.style.removeProperty('--left-sidebar-width');
rootContainer.style.removeProperty('--nav-bar-width');
}
setTimeout(() => {
window.dispatchEvent(new Event('resize'));
}, 50);
} else {
leftSidebar.dataset.overlay = 'true';
sidebarOverlayActive = true;
if (rootContainer) {
rootContainer.style.setProperty('--left-sidebar-width', window.innerWidth + 'px');
rootContainer.style.setProperty('--nav-bar-width', window.innerWidth + 'px');
}
const expandBtn = leftSidebar.querySelector(
'button[aria-label*="Expand Your Library"], ' +
'button[aria-label*="Expandir Tu biblioteca"], ' +
'button[aria-label*="Expandir tu biblioteca"]'
);
if (expandBtn) {
expandBtn.click();
}
const headerH1 = leftSidebar.querySelector('header>div>div:first-child h1');
if (headerH1) {
const lang = document.documentElement.lang || '';
headerH1.textContent = lang.startsWith('es') ? 'Tu biblioteca' : 'Your library';
}
setTimeout(() => {
const list = leftSidebar.querySelector('[role="list"],[role="grid"],div[class*="view-container"]');
if (list) {
list.scrollBy(0, 1);
list.scrollBy(0, -1);
}
window.dispatchEvent(new Event('resize'));
}, 100);
}
updateActiveTab();
};
window.firstFuck = function() {
if (pfint) clearInterval(pfint);
pfint = setInterval(() => {
const playBtn = document.querySelector('aside button[data-testid=control-button-playpause]:not(.fuckd)');
if (playBtn) {
playBtn.classList.add('fuckd');
window.pBtn = playBtn;
if (!ffDone) {
ffDone = true;
clearInterval(pfint);
addCSSJSHack();
}
}
}, 5000);
};
window.addCSSJSHack = function() {
const setupLibraryButton = () => {
const libBtn = document.querySelector('#Desktop_LeftSidebar_Id header button[aria-label*="Your Library"]:not(.fuckd)');
if (libBtn && !libBtn.classList.contains('fuckd')) {
window.lBtn = libBtn;
libBtn.classList.add('fuckd', 'lbtn');
libBtn.style.padding = '0';
libBtn.style.height = '20px';
libBtn.addEventListener('click', function() {
setTimeout(() => switchLs(), 0);
});
if (libBtn.getAttribute('aria-label') === 'Collapse Your Library') {
libBtn.click();
}
}
};
const setupLibraryGrid = () => {
const libGrid = document.querySelector('#Desktop_LeftSidebar_Id div[role=grid]:not(.fuckd)');
if (libGrid) {
libGrid.classList.add('fuckd');
libGrid.addEventListener('click', (event) => {
let target = event.target;
let isFolder = false;
for (let i = 0; i < 5 && target; i++) {
const ariaLabelledBy = target.getAttribute('aria-labelledby');
if (ariaLabelledBy && ariaLabelledBy.includes(':folder:')) { isFolder = true; break; }
const ariaDescribedBy = target.getAttribute('aria-describedby');
if (ariaDescribedBy && ariaDescribedBy.includes(':folder:')) { isFolder = true; break; }
target = target.parentElement;
}
if (!isFolder) {
setTimeout(() => {
switchLs(true);
}, 150);
}
});
}
};
const setupSearchInput = () => {
const searchInput = document.querySelector('input[data-testid=search-input]:not(.fuckd)');
if (searchInput) {
searchInput.classList.add('fuckd');
searchInput.addEventListener('focus', () => {
const npBar = document.querySelector('aside[data-testid=now-playing-bar]');
if (npBar) npBar.style.display = 'none';
});
searchInput.addEventListener('blur', () => {
const npBar = document.querySelector('aside[data-testid=now-playing-bar]');
if (npBar) npBar.style.display = 'flex';
});
}
};
setupLibraryButton();
setupLibraryGrid();
setupSearchInput();
setupPlayerToggle();
setTimeout(() => {
setupLibraryButton();
setupLibraryGrid();
setupSearchInput();
setupPlayerToggle();
}, 2000);
};
function setupPlayerToggle() {
const player = document.querySelector('aside[data-testid=now-playing-bar]:not(.fuckd)');
if (!player || player.querySelector('#sp-player-toggle')) return;
player.classList.add('fuckd');
const btn = document.createElement('button');
btn.id = 'sp-player-toggle';
btn.textContent = '\u25BC';
player.appendChild(btn);
btn.addEventListener('click', function(e) {
e.stopPropagation();
player.classList.toggle('minimized');
btn.textContent = player.classList.contains('minimized') ? '\u25B2' : '\u25BC';
});
}
function injectMobileCSS() {
const style = document.createElement('style');
style.textContent = `
body{min-width:100%!important;min-height:100%!important;padding-bottom:56px!important}
body,div[data-testid=root],.Root__top-container,.Root__now-playing-bar{background:transparent!important}
aside[data-testid=now-playing-bar]>div,.Root__now-playing-bar>div,.Root__now-playing-bar{background:transparent!important}
.os-scrollbar{--os-size:6px!important}
.contentSpacing{padding:0}
div[data-testid=root]{--panel-gap:0!important;--content-spacing:10px}
#main+div,#main+div>div{overflow:hidden!important;width:auto}
#main+div>div>div>div:nth-child(2)>div{width:100vw!important}
div[data-encore-id=banner],
#global-nav-bar>div:first-of-type,
#global-nav-bar a[href="/download"],
button[data-testid=fullscreen-mode-button],
div.main-view-container__mh-footer-container,
button[data-testid=upgrade-button],
a[href="/download"],
button[aria-label="Expandir la vista Estás escuchando"],
button[aria-label="Ocultar la vista Estás escuchando"]
{display:none!important}
#global-nav-bar{display:none!important}
body.sp-search #global-nav-bar{display:flex!important}
#global-nav-bar button[data-testid=home-button],
#global-nav-bar a[aria-label*="Home"],
#global-nav-bar a[aria-label*="Inicio"]{display:none!important}
#sp-bottom-nav{
position:fixed;
bottom:0;
left:0;
right:0;
height:56px;
background:transparent!important;
background-image:linear-gradient(to top,rgba(0,0,0,1) 0%,rgba(0,0,0,0.85) 40%,rgba(0,0,0,0.5) 75%,rgba(0,0,0,0) 100%)!important;
border:none!important;
box-shadow:none!important;
display:flex;
align-items:center;
justify-content:space-around;
z-index:9999;
padding:0 8px;
pointer-events:none;
will-change:transform
}
#sp-bottom-nav button{
flex:1;
display:flex;
flex-direction:column;
align-items:center;
justify-content:center;
gap:2px;
background:none!important;
border:none;
color:#b3b3b3;
cursor:pointer;
padding:4px 0;
transition:color 0.15s;
height:100%;
pointer-events:auto
}
#sp-bottom-nav button.active{color:#fff}
#sp-bottom-nav button svg{width:24px;height:24px;fill:currentColor}
#sp-bottom-nav button span{font-size:10px;letter-spacing:0.5px}
aside[data-testid=now-playing-bar]{
min-width:calc(100% - 16px)!important;
margin:0 8px!important;
position:fixed!important;
box-shadow:0 -4px 30px rgba(0,0,0,0.5)!important;
background:rgba(40,8,8,0.65)!important;
backdrop-filter:blur(20px)!important;
-webkit-backdrop-filter:blur(20px)!important;
border:1px solid rgba(255,255,255,0.06)!important;
bottom:64px!important;
z-index:30!important;
border-radius:16px!important;
transition:transform 0.3s cubic-bezier(0.4,0,0.2,1),height 0.3s cubic-bezier(0.4,0,0.2,1)!important;
overflow:hidden!important;
will-change:transform
}
aside[data-testid=now-playing-bar].minimized{
height:72px!important;
min-height:72px!important;
max-height:72px!important;
border-radius:14px!important;
background:rgba(40,8,8,0.85)!important;
padding:0!important
}
aside[data-testid=now-playing-bar].minimized div[data-testid=general-controls],
aside[data-testid=now-playing-bar].minimized [data-testid=progress-bar],
aside[data-testid=now-playing-bar].minimized [role=progressbar],
aside[data-testid=now-playing-bar].minimized [data-testid=playback-position],
aside[data-testid=now-playing-bar].minimized [data-testid=playback-duration],
aside[data-testid=now-playing-bar].minimized [data-testid=playback-progressbar],
aside[data-testid=now-playing-bar].minimized button[aria-label*="Previous"],
aside[data-testid=now-playing-bar].minimized button[aria-label*="Next"],
aside[data-testid=now-playing-bar].minimized button[aria-label*="Skip"],
aside[data-testid=now-playing-bar].minimized button[aria-label*="Aleatorio"],
aside[data-testid=now-playing-bar].minimized button[aria-label*="repeat"],
aside[data-testid=now-playing-bar].minimized button[aria-label*="Repetir"],
aside[data-testid=now-playing-bar].minimized button[data-testid="lyrics-button"],
aside[data-testid=now-playing-bar].minimized button[data-testid="control-button-queue"],
aside[data-testid=now-playing-bar].minimized button[data-testid="pip-toggle-button"],
aside[data-testid=now-playing-bar].minimized button[data-testid="fullscreen-mode-button"],
aside[data-testid=now-playing-bar].minimized button[aria-label*="Conectar"],
aside[data-testid=now-playing-bar].minimized button[aria-label*="conectar"],
aside[data-testid=now-playing-bar].minimized button[aria-label*="device"],
aside[data-testid=now-playing-bar].minimized [data-testid="volume-bar"],
aside[data-testid=now-playing-bar].minimized div[data-testid=now-playing-widget]>div:last-child,
aside[data-testid=now-playing-bar].minimized>div:first-child>div:last-child{
display:none!important
}
aside[data-testid=now-playing-bar].minimized>div:first-child{
flex-direction:row!important;
align-items:center!important;
gap:10px!important;
padding:8px 44px 8px 8px!important;
height:100%!important
}
aside[data-testid=now-playing-bar].minimized div[data-testid=now-playing-widget]{
flex:1!important;
min-width:0!important;
flex-direction:row!important;
align-items:center!important;
gap:12px!important;
height:100%!important
}
aside[data-testid=now-playing-bar].minimized div[data-testid=now-playing-widget]>div:first-child{
width:56px!important;
height:56px!important;
min-width:56px!important;
border-radius:6px!important;
overflow:hidden!important
}
aside[data-testid=now-playing-bar].minimized div[data-testid=now-playing-widget]>div:first-child img{
width:100%!important;
height:100%!important;
border-radius:6px!important
}
aside[data-testid=now-playing-bar].minimized div[data-testid=now-playing-widget]>div:nth-child(2){
overflow:hidden!important;
max-width:50vw!important;
display:flex!important;
flex-direction:column!important;
justify-content:center!important;
gap:2px!important
}
aside[data-testid=now-playing-bar].minimized div[data-testid=now-playing-widget]>div:nth-child(2) a,
aside[data-testid=now-playing-bar].minimized div[data-testid=now-playing-widget]>div:nth-child(2) span{
white-space:nowrap!important;
overflow:hidden!important;
text-overflow:ellipsis!important;
max-width:100%!important;
font-size:14px!important;
line-height:1.3!important
}
aside[data-testid=now-playing-bar].minimized div[data-testid=now-playing-widget]>div:nth-child(2)>div:last-child span{
font-size:12px!important;
opacity:0.75!important
}
aside[data-testid=now-playing-bar].minimized div[data-testid=player-controls]{
width:auto!important;
margin:0!important;
flex:none!important;
height:100%!important;
display:flex!important;
align-items:center!important
}
aside[data-testid=now-playing-bar].minimized div[data-testid=player-controls]>div{
min-height:0!important;
margin:0!important
}
aside[data-testid=now-playing-bar].minimized div[data-testid=player-controls] button[data-testid=control-button-playpause]{
transform:scale(1.15)!important;
margin:0 6px!important
}
#sp-player-toggle{
position:absolute;
right:10px;
top:50%;
transform:translateY(-50%);
background:rgba(255,255,255,0.1);
border:1px solid rgba(255,255,255,0.08);
color:#fff;
width:28px;
height:28px;
border-radius:50%;
cursor:pointer;
z-index:5;
font-size:11px;
line-height:1;
display:flex;
align-items:center;
justify-content:center;
padding:0;
opacity:0.7;
transition:opacity 0.2s
}
#sp-player-toggle:hover{opacity:1;background:rgba(255,255,255,0.2)}
aside[data-testid=now-playing-bar] button[aria-label*="scroll"],
aside[data-testid=now-playing-bar] button[aria-label*="info"],
aside[data-testid=now-playing-bar] [class*="chevron"],
aside[data-testid=now-playing-bar] [class*="Chevron"],
aside[data-testid=now-playing-bar] button[aria-label*="Play from"],
aside[data-testid=now-playing-bar] button[aria-label*="Queue"]{display:none!important}
aside[data-testid=now-playing-bar]:not(.minimized)>div:first-child{
flex-direction:column!important;
height:auto!important;
padding:8px 8px 6px!important
}
aside[data-testid=now-playing-bar]>div>div{width:100%!important}
aside[data-testid=now-playing-bar]:not(.minimized)>div>div:last-child>div{min-height:28px;margin:3px 6px}
aside[data-testid=now-playing-bar]:not(.minimized)>div>div:last-child button{transform:scale(1.1);margin:0 4px}
aside[data-testid=now-playing-bar]:not(.minimized) div[data-testid=general-controls]{margin:6px 0 8px!important}
aside[data-testid=now-playing-bar]:not(.minimized) div[data-testid=general-controls] button{transform:scale(1.2)!important;margin:0 6px!important}
aside[data-testid=now-playing-bar]:not(.minimized) div[data-testid=player-controls]{margin:2px 0!important}
aside[data-testid=now-playing-bar]:not(.minimized) div[data-testid=now-playing-widget]{justify-content:center;overflow:hidden}
aside[data-testid=now-playing-bar]:not(.minimized) div[data-testid=now-playing-widget]>div:last-child>button{transform:scale(1.15)}
aside[data-testid=now-playing-bar]:not(.minimized) div[data-testid=now-playing-widget]>div:nth-child(2){display:flex!important;overflow:hidden!important}
aside[data-testid=now-playing-bar]:not(.minimized) div[data-testid=now-playing-widget]>div:nth-child(2) span{font-size:13px!important;height:20px!important;margin:0!important}
aside[data-testid=now-playing-bar]:not(.minimized) div[data-testid=now-playing-widget]>div:nth-child(2)>div{min-width:auto;max-width:66%}
aside[data-testid=now-playing-bar]:not(.minimized)>div:first-child>div:last-child{
display:flex!important;
flex-direction:row!important;
align-items:center!important;
justify-content:center!important;
gap:2px!important;
padding:4px 0!important
}
aside[data-testid=now-playing-bar]:not(.minimized)>div:first-child>div:last-child button{transform:scale(0.85)!important}
aside[data-testid=now-playing-bar]:not(.minimized)>div:first-child>div:last-child [data-testid=volume-bar]{max-width:100px!important}
input[data-testid="search-input"],
input[aria-label="¿Qué quieres reproducir?"],
input[aria-label="What do you want to play?"]{display:none!important}
body.sp-search input[data-testid="search-input"],
body.sp-search input[aria-label="¿Qué quieres reproducir?"],
body.sp-search input[aria-label="What do you want to play?"]{display:flex!important}
body.sp-collection main>section>div:first-child{height:auto!important;min-height:auto!important;padding:10px}
form[role=search]{z-index:10;max-width:88%}
#Desktop_LeftSidebar_Id{
width:0!important;
min-width:0!important;
overflow:hidden!important;
display:none!important
}
#Desktop_LeftSidebar_Id[data-overlay="true"]{
display:flex!important;
position:fixed!important;
top:0!important;
left:0!important;
width:100vw!important;
min-width:100vw!important;
max-width:100vw!important;
height:calc(100vh - 56px)!important;
z-index:999!important;
background:#121212!important;
flex-direction:column!important;
overflow:hidden!important
}
#Desktop_LeftSidebar_Id[data-overlay="true"]>*{
width:100vw!important;
min-width:100vw!important;
max-width:100vw!important
}
#Desktop_LeftSidebar_Id[data-overlay="true"] .YourLibraryX,
#Desktop_LeftSidebar_Id[data-overlay="true"] [class*="YourLibraryX"]{
width:100vw!important;
min-width:100vw!important;
max-width:100vw!important;
background:transparent!important;
height:100%!important
}
#Desktop_LeftSidebar_Id[data-overlay="true"] nav{width:100vw!important;max-width:100vw!important}
#Desktop_LeftSidebar_Id[data-overlay="true"] header button[aria-label*="Create"]{
position:absolute!important;
top:14px!important;
right:14px!important;
width:36px!important;
height:36px!important
}
#Desktop_LeftSidebar_Id[data-overlay="true"] button[aria-label*="Collapse Your Library"],
#Desktop_LeftSidebar_Id[data-overlay="true"] button[aria-label*="Expand Your Library"],
#Desktop_LeftSidebar_Id[data-overlay="true"] button[aria-label*="Comprimir"],
#Desktop_LeftSidebar_Id[data-overlay="true"] button[aria-label*="Expandir"]{display:none!important}
#Desktop_LeftSidebar_Id[data-overlay="true"] [data-testid="resize-bar"],
#Desktop_LeftSidebar_Id[data-overlay="true"] [class*="ResizeBar"],
#Desktop_LeftSidebar_Id[data-overlay="true"] [class*="resize-bar"],
#Desktop_LeftSidebar_Id[data-overlay="true"] [class*="Resizer"]{
display:none!important;
width:0!important;
pointer-events:none!important
}
[data-testid="resize-bar"],[class*="ResizeBar"]{display:none!important}
#Desktop_LeftSidebar_Id[data-overlay="true"] [data-overlayscrollbars-viewport],
#Desktop_LeftSidebar_Id[data-overlay="true"] [class*="os-viewport"],
#Desktop_LeftSidebar_Id[data-overlay="true"] div[role="grid"],
#Desktop_LeftSidebar_Id[data-overlay="true"] [data-testid="LibraryRoot"]{padding-bottom:80px!important}
#Desktop_LeftSidebar_Id>nav>div{min-height:48px;border-radius:25px}
.YourLibraryX{background:var(--background-elevated-base)!important}
.YourLibraryX header{padding:14px}
#main-view,div[data-testid=main-view],.Root__main-view,
#main-view+div,#main-view+div>div,#main-view+div>div>div,
div[data-testid=root]>div:first-child>div:first-child{margin-left:0!important;padding-left:0!important}
section[data-testid=artist-page]>div>div:first-child:not([data-encore-id]){height:25vh}
div[data-testid=tracklist-row]{padding:0 10px 0 0;grid-gap:0}
div[data-testid=tracklist-row] button:not([data-testid=add-to-playlist-button]){transform:scale(1.3)!important;opacity:0.6!important}
div[data-testid=tracklist-row] button:hover{color:#2d6!important}
div[data-testid=tracklist-row]>div:first-child>div:first-child{height:24px;min-height:24px;min-width:24px;margin:0 8px!important}
[aria-colcount="3"] div[data-testid=tracklist-row]{grid-template-columns:[index] var(--tracklist-index-column-width,40px) [first] minmax(120px,var(--col1,4fr)) [last] minmax(82px,var(--col2,1fr))!important}
[aria-colcount="4"] div[data-testid=tracklist-row]{grid-template-columns:[index] var(--tracklist-index-column-width,40px) [first] minmax(120px,var(--col1,4fr)) [var1] minmax(120px,var(--col2,2fr)) [last] minmax(82px,var(--col3,1fr))!important}
[aria-colcount="5"] div[data-testid=tracklist-row]{grid-template-columns:[index] var(--tracklist-index-column-width,40px) [first] minmax(120px,var(--col1,6fr)) [var1] minmax(120px,var(--col2,4fr)) [var2] minmax(120px,var(--col3,3fr)) [last] minmax(82px,var(--col4,1fr))!important}
section[data-testid=home-page] .contentSpacing{padding:0 10px!important;overflow:hidden}
div[data-testid=grid-container]{margin-inline:0!important;column-gap:0!important;overflow:hidden!important}
div[data-testid=action-bar-row],div[data-testid=topbar-content]{padding:5px 10px}
div[data-testid=track-list]>div:first-child,div[data-testid=playlist-tracklist]>div:first-child{margin:0!important;padding:0!important}
main>section:not([data-testid=artist-page])>div:first-child{height:auto!important;min-height:auto!important;padding:10px}
main>section h1.encore-text-headline-large{font-size:22px!important}
section[data-testid=artist-page] span.encore-text-headline-large{font-size:26px!important}
section[data-testid=artist-page] div[data-testid=grid-container] h2,section[data-testid=artist-page] section[data-testid=component-shelf]{padding:0 10px}
.Root__top-container{grid-template-columns:auto 1fr auto!important}
#Desktop_PanelContainer_Id{display:flex!important;flex-direction:column!important;overflow-y:auto!important}
div.IPnR0MPdiJw3m3C8.rd25SoWs7Y4T40c7,
button[aria-label="Comprimir Tu biblioteca"],
button[aria-label="Collapse Your library"]{display:none!important}
button[data-testid="npv-artist-bio-button"],.tDBAoTKiCjMk1wxv{display:none!important;height:0px!important}
.qy8cKKS5c5Y24cTG{display:none!important}
.lhB5KQbFP8BJIgvI{flex:1!important;overflow-y:auto!important}
ul.oPf3qKGRkUM3T0bK{display:block!important;overflow-y:auto!important}
`;
document.head.appendChild(style);
}
const FALLBACK_SVGS = {
home: '',
search: '',
library: ''
};
function createBottomNav() {
if (document.getElementById('sp-bottom-nav')) return;
const nav = document.createElement('div');
nav.id = 'sp-bottom-nav';
cache.bottomNav = nav;
const tabs = [
{ name: 'home', label: 'Home' },
{ name: 'search', label: 'Search' },
{ name: 'library', label: 'Library' }
];
const frag = document.createDocumentFragment();
tabs.forEach(({ name, label }) => {
const btn = document.createElement('button');
btn.dataset.tab = name;
btn.innerHTML = `${FALLBACK_SVGS[name]}${label}`;
btn.addEventListener('click', () => handleTabClick(name));
frag.appendChild(btn);
});
nav.appendChild(frag);
document.body.appendChild(nav);
updateActiveTab();
}
function handleTabClick(name) {
const currentPath = location.pathname;
if (name === 'library') {
if (sidebarOverlayActive) {
switchLs(true);
} else {
switchLs();
}
return;
}
if (sidebarOverlayActive) switchLs(true);
if (name === 'search') {
if (!currentPath.startsWith('/search')) {
history.pushState(null, '', '/search');
window.dispatchEvent(new PopStateEvent('popstate'));
}
return;
}
if (name === 'home') {
if (currentPath !== '/') {
history.pushState(null, '', '/');
window.dispatchEvent(new PopStateEvent('popstate'));
}
return;
}
}
let lastActiveTab = null;
function updateActiveTab() {
const path = location.pathname;
let active = null;
if (sidebarOverlayActive) active = 'library';
else if (path === '/' || path === '/home') active = 'home';
else if (path.startsWith('/search')) active = 'search';
else if (path.startsWith('/collection')) active = 'library';
if (active === lastActiveTab) return;
lastActiveTab = active;
const nav = cache.bottomNav || document.getElementById('sp-bottom-nav');
if (!nav) return;
const buttons = nav.children;
for (let i = 0; i < buttons.length; i++) {
const btn = buttons[i];
btn.classList.toggle('active', btn.dataset.tab === active);
}
}
let lastBodyClass = '';
function updateBodyClass() {
const path = location.pathname;
let cls = '';
if (path === '/' || path === '/home') cls = 'sp-home';
else if (path.startsWith('/search')) cls = 'sp-search';
else if (path.startsWith('/collection')) cls = 'sp-collection';
else if (path.startsWith('/playlist')) cls = 'sp-playlist';
else if (path.startsWith('/album')) cls = 'sp-album';
else if (path.startsWith('/artist')) cls = 'sp-artist';
else if (path.startsWith('/track')) cls = 'sp-track';
if (cls === lastBodyClass) return;
if (lastBodyClass) document.body.classList.remove(lastBodyClass);
if (cls) document.body.classList.add(cls);
lastBodyClass = cls;
}
let lastPath = '';
function onLocationChange() {
if (location.pathname === lastPath) return;
lastPath = location.pathname;
updateBodyClass();
updateActiveTab();
}
function hookHistory() {
const origPush = history.pushState;
const origReplace = history.replaceState;
history.pushState = function() {
origPush.apply(this, arguments);
onLocationChange();
};
history.replaceState = function() {
origReplace.apply(this, arguments);
onLocationChange();
};
window.addEventListener('popstate', onLocationChange);
}
const IS_OPEN_SPOTIFY = window.location.hostname === 'open.spotify.com';
if (IS_OPEN_SPOTIFY) {
injectMobileCSS();
const waitForBody = setInterval(() => {
if (document.body) {
clearInterval(waitForBody);
lastPath = location.pathname;
updateBodyClass();
createBottomNav();
firstFuck();
hookHistory();
}
}, 100);
}
GM_addStyle(`
.__sp_curr {
display:inline-block;
background:#535353;
color:#fff;
font-size:11px;
font-weight:700;
padding:3px 8px;
border-radius:3px;
text-transform:uppercase;
letter-spacing:.4px;
}
`);
const REPLACE = {
"Spotify Free": "Premium Individual",
"1 Free account": "1 Premium account",
"1 free account": "1 Premium account",
"Music with ads": "Listen to music ad-free",
"Music listening with ad breaks": "Listen to music ad-free",
"Shuffle play": "Play any song",
"Songs play in shuffle": "Play any song",
"Online only": "Download for offline listening",
"Streaming only": "Download for offline listening",
"No downloads": "Download for offline listening",
"Basic audio quality": "Very high audio quality",
"Normal audio quality": "Very high audio quality",
"Limited skips": "Unlimited skips",
"Free plan": "Premium Individual",
};
const replacementLog = new Map();
function logChange(selector, from, to) {
const key = `${selector}\u0000${from}\u0000${to}`;
const existing = replacementLog.get(key);
if (existing) existing.times_applied++;
else replacementLog.set(key, { selector, old_text: from, new_text: to, times_applied: 1 });
}
function printReplacementLog() {
if (replacementLog.size === 0) {
console.log('[SpotiKit] Nothing has been replaced yet.');
return;
}
console.log(`[SpotiKit] ${replacementLog.size} distinct change(s) made so far:`);
console.table(Array.from(replacementLog.values()));
}
function applyReplacements(node) {
let v = node.nodeValue;
if (v == null) return;
let c = false;
for (const [from, to] of Object.entries(REPLACE)) {
if (v.includes(from)) {
v = v.replaceAll(from, to);
c = true;
logChange('(page text)', from, to);
}
}
if (c) node.nodeValue = v;
}
function scanText(root) {
if (!root) return;
const w = document.createTreeWalker(root, NodeFilter.SHOW_TEXT, null, false);
let n;
while (n = w.nextNode()) applyReplacements(n);
}
function runPremium() {
document.querySelectorAll('.encore-text-title-medium, [class*="title-medium"]').forEach(el => {
if ((el.textContent || '').trim() === 'Premium Individual') {
el.style.color = window.location.href.includes('/subscription/manage/') ? '#000' : PINK;
const parent = el.closest('[class*="Hjkjj"], [class*="hjkjj"]');
if (parent) {
parent.style.background = PINK;
parent.style.color = '#000';
}
}
});
const planCard = document.querySelector('[data-testid="plan-card"]');
if (planCard && !planCard.querySelector('.__sp_logo')) {
planCard.style.position = 'relative';
const logo = document.createElement('span');
logo.className = '__sp_logo';
logo.style.cssText = 'position:absolute;top:8px;right:8px;width:24px;height:24px;z-index:10;pointer-events:none;display:flex;align-items:center;justify-content:center;background:#FFD2D7;border-radius:50%;font-size:12px;font-weight:700;color:#000;';
logo.textContent = 'SP';
planCard.appendChild(logo);
const msg = document.createElement('p');
msg.textContent = 'Your Premium Individual NEVER expires. Dont pay Spotify, fuck their monopoly!';
msg.style.cssText = 'color:#B3B3B3;font-size:14px;margin:8px 0;text-align:left;line-height:1.4;padding:0 4px;';
const btnRow = planCard.querySelector('[class*="dCZPlm"]');
if (btnRow) btnRow.parentNode.insertBefore(msg, btnRow);
}
document.querySelectorAll('h1, h2, h3, h4, strong, span, div[class*="plan"], div[class*="Plan"]').forEach(el => {
const t = (el.textContent || '').trim();
if (t === 'Free' || t === 'Spotify Free' || t === 'Free plan') {
logChange('h1,h2,h3,h4,strong,span,div[class*="plan"]', t, 'Premium Individual');
el.textContent = 'Premium Individual';
el.style.color = PINK;
el.style.fontWeight = '700';
}
});
document.querySelectorAll('a, button, [role="button"]').forEach(el => {
const orig = (el.innerText || el.textContent || '').trim();
const t = orig.toLowerCase();
if (/^(get|buy|join)\s*premium/.test(t)) {
logChange('a, button, [role="button"]', orig, 'DONT JOIN PREMIUM');
el.textContent = 'DONT JOIN PREMIUM';
el.style.cssText += `background:${PINK}!important;color:#000!important;border:none!important;border-radius:20px!important;font-weight:700!important;pointer-events:none!important;cursor:default!important;`;
el.onclick = e => { e.preventDefault(); e.stopPropagation(); };
}
if (/^(explore|view)\s*plans/.test(t)) {
logChange('a, button, [role="button"]', orig, 'Manage plan');
el.textContent = 'Manage plan';
el.style.cssText += `background:transparent!important;color:#fff!important;border:1px solid #727272!important;border-radius:20px!important;font-weight:700!important;pointer-events:none!important;cursor:default!important;`;
el.onclick = e => { e.preventDefault(); e.stopPropagation(); };
}
if (/^try/.test(t) && !el.dataset.spDone) {
logChange('a, button, [role="button"]', orig, '(hidden)');
el.style.display = 'none';
el.dataset.spDone = '1';
}
});
document.querySelectorAll('[class*="badge"], [class*="Badge"]').forEach(el => {
if (/^free$/i.test(el.textContent.trim())) {
el.textContent = 'PREMIUM';
el.style.background = PINK;
el.style.color = '#000';
}
});
document.querySelectorAll('table').forEach(tbl => {
tbl.querySelectorAll('td, th').forEach(cell => {
const t = cell.textContent.trim().toLowerCase();
if (!t || t === '\u2014' || t === '-' || t === 'no' || /free/.test(t)) {
logChange('table td, th', t || '(empty)', '\u2713');
cell.innerHTML = `\u2713`;
}
});
});
document.querySelectorAll('span[data-encore-id="text"]').forEach(el => {
const t = el.textContent.trim();
if (t === 'Download for offline listening') {
logChange('span[data-encore-id="text"]', t, 'Spotify wont fuck you');
el.textContent = 'Spotify wont fuck you';
}
});
const upgradeBtn = document.querySelector('[data-testid="upgrade-button"]:not([data-sp-done])');
if (upgradeBtn) { logChange('[data-testid="upgrade-button"]', upgradeBtn.textContent.trim(), '(hidden)'); upgradeBtn.style.display = 'none'; upgradeBtn.dataset.spDone = '1'; }
const installBtn = document.querySelector('a[href="/download"]:not([data-sp-done])');
if (installBtn) { logChange('a[href="/download"]', 'install app link', '(hidden)'); installBtn.style.display = 'none'; installBtn.dataset.spDone = '1'; }
const premiumMenu = document.querySelector('a[href*="premium/?ref=web_loggedin_upgrade_menu"]:not([data-sp-done])');
if (premiumMenu) { logChange('a[href*="premium/?ref=web_loggedin_upgrade_menu"]', premiumMenu.textContent.trim(), '(hidden)'); premiumMenu.style.display = 'none'; premiumMenu.dataset.spDone = '1'; }
const planesXpath = document.evaluate(
'//a[text()="Premium Plans"] | //span[text()="Premium Plans"] | //div[text()="Premium Plans"]',
document, null, XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE, null
);
for (let i = 0; i < planesXpath.snapshotLength; i++) {
const n = planesXpath.snapshotItem(i);
if (n && n.nodeType === 1 && !n.dataset.spDone) {
logChange('(xpath) Premium Plans text', n.textContent.trim(), '(hidden)');
n.style.display = 'none';
n.dataset.spDone = '1';
}
}
document.querySelectorAll('[aria-label*="Premium Plans"], [data-ga-action="premium"], [data-ga-category="menu"] a, a[href*="/premium/"]').forEach(el => {
if (el.dataset.spDone) return;
const t = el.textContent.trim();
if (t === 'Premium Plans') {
logChange('[aria-label*="Premium Plans"] / [data-ga-action="premium"] / a[href*="/premium/"]', t, '(hidden)');
el.style.display = 'none';
el.dataset.spDone = '1';
}
});
const DESKTOP_SELECTORS = [
'[data-testid="open-in-app-button"]',
'[data-testid="install-app-button"]',
'[data-testid="download-button"]',
'[aria-label*="open in app"]',
'[aria-label*="install app"]',
'[aria-label*="download app"]',
'[class*="open-in-app"]',
'[class*="install-app"]',
'[class*="get-the-app"]',
'[class*="view-in-app"]',
'[class*="desktop-app"]',
];
DESKTOP_SELECTORS.forEach(sel => {
document.querySelectorAll(sel).forEach(el => el.style.display = 'none');
});
document.querySelectorAll('a, button, [role="button"]').forEach(el => {
const t = (el.textContent || '').trim().toLowerCase();
if (/open (in|the) (app|desktop|spotify)|install|download (the )?app|get the app|launch|listen in app|listen on desktop|use (the )?(app|desktop)/.test(t)) {
el.style.display = 'none';
}
});
const premiumBanner = document.querySelector('[data-testid="compact-banner"]');
if (premiumBanner) {
const wrapper = premiumBanner.closest('[class*="dad329a7"]');
if (wrapper) {
wrapper.style.width = '100%';
}
premiumBanner.style.cssText += `
display:flex !important;
flex-direction:row !important;
background:#2A2A2A !important;
cursor:default !important;
padding:0 !important;
border-radius:8px !important;
overflow:hidden !important;
min-width:unset !important;
width:100% !important;
`;
const left = document.createElement('div');
left.style.cssText = 'flex:1;display:flex;flex-direction:column;align-items:center;justify-content:center;row-gap:var(--encore-spacing-tighter-2);padding:var(--encore-spacing-looser) var(--encore-spacing-tighter-2);cursor:pointer;';
const pencilSvg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
pencilSvg.setAttribute('viewBox', '0 0 16 16');
pencilSvg.setAttribute('role', 'img');
pencilSvg.setAttribute('aria-hidden', 'true');
pencilSvg.style.cssText = 'width:var(--encore-graphic-size-decorative-base);height:var(--encore-graphic-size-decorative-base);';
pencilSvg.innerHTML = ``;
const leftText = document.createElement('span');
leftText.className = 'e-10561-text encore-text-body-small-bold';
leftText.style.cssText = 'color:var(--text-base);text-align:center;';
leftText.textContent = 'Edit profile';
left.appendChild(pencilSvg);
left.appendChild(leftText);
left.onclick = e => {
e.stopPropagation();
window.location.href = 'https://www.spotify.com/us/account/profile/';
};
const right = document.createElement('div');
right.style.cssText = 'flex:1;display:flex;flex-direction:column;align-items:center;justify-content:center;row-gap:var(--encore-spacing-tighter-2);padding:var(--encore-spacing-looser) var(--encore-spacing-tighter-2);cursor:pointer;border-left:1px solid #404040;';
const cardSvg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
cardSvg.setAttribute('viewBox', '0 0 16 16');
cardSvg.setAttribute('role', 'img');
cardSvg.setAttribute('aria-hidden', 'true');
cardSvg.style.cssText = 'width:var(--encore-graphic-size-decorative-base);height:var(--encore-graphic-size-decorative-base);';
cardSvg.innerHTML = ``;
const rightText = document.createElement('span');
rightText.className = 'e-10561-text encore-text-body-small-bold';
rightText.style.cssText = 'color:var(--text-base);text-align:center;';
rightText.textContent = 'Payment method';
right.appendChild(cardSvg);
right.appendChild(rightText);
right.onclick = e => {
e.stopPropagation();
window.location.href = 'https://www.spotify.com/us/account/saved-payment-cards/';
};
premiumBanner.innerHTML = '';
premiumBanner.appendChild(left);
premiumBanner.appendChild(right);
}
if (/\/premium\/|\/duo\/|\/student\/|\/family\//.test(window.location.href) && !document.querySelector('.__sp_premium_done')) {
window.location.replace('https://open.spotify.com/');
}
if (window.location.hostname === 'payments.spotify.com' && !document.querySelector('.__sp_pay_done')) {
window.location.replace('https://open.spotify.com/');
}
}
// Single gated entry point: both the timed passes below and the mutation
// observer funnel through this so premiumSpoofEnabledHere() is the one
// switch that turns the whole spoof pass on/off for the current host.
function premiumPass(changedRoot) {
if (!premiumSpoofEnabledHere()) return;
if (changedRoot) scanText(changedRoot);
else scanText(document.body);
runPremium();
}
setTimeout(() => premiumPass(document.body), 300);
setTimeout(() => premiumPass(document.body), 2000);
let premTimer;
let pendingNodes = new Set();
let pendingTextNodes = new Set();
let premiumObserver = null;
function handlePremiumMutations(mutations) {
if (!premiumSpoofEnabledHere()) return;
for (const m of mutations) {
if (m.type === 'childList') {
m.addedNodes.forEach(node => {
if (node.nodeType === 1) pendingNodes.add(node);
});
} else if (m.type === 'characterData') {
pendingTextNodes.add(m.target);
}
}
clearTimeout(premTimer);
premTimer = setTimeout(() => {
if (pendingNodes.size > 0 && pendingNodes.size <= 20) {
pendingNodes.forEach(node => scanText(node));
} else if (pendingNodes.size > 20) {
scanText(document.body);
}
pendingNodes.clear();
pendingTextNodes.forEach(node => applyReplacements(node));
pendingTextNodes.clear();
runPremium();
}, 400);
}
function startPremiumObserver() {
if (premiumObserver) premiumObserver.disconnect();
premiumObserver = new MutationObserver(handlePremiumMutations);
premiumObserver.observe(document.body, {
childList: true,
subtree: true,
characterData: true,
});
}
if (document.body) {
startPremiumObserver();
} else {
document.addEventListener('DOMContentLoaded', startPremiumObserver, { once: true });
}
if (typeof GM_registerMenuCommand === 'function') {
GM_registerMenuCommand('\ud83d\udccb Show everything replaced so far (console)', () => {
printReplacementLog();
alert('Current text replacements have been logged to the console. Open DevTools (Press F12 or Right click and Inspect), then select the Logs tab under Console to view it.');
});
}
})();