Spotify Infinite Scroller

Infinite podcast scrolling! Never click that pesky load more episodes button again!

За да инсталирате този скрипт, трябва да имате инсталирано разширение като Tampermonkey, Greasemonkey или Violentmonkey.

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

За да инсталирате този скрипт, трябва да имате инсталирано разширение като Tampermonkey или Violentmonkey.

За да инсталирате този скрипт, трябва да имате инсталирано разширение като Tampermonkey или Userscripts.

За да инсталирате скрипта, трябва да инсталирате разширение като Tampermonkey.

За да инсталирате този скрипт, трябва да имате инсталиран скриптов мениджър.

(Вече имам скриптов мениджър, искам да го инсталирам!)

За да инсталирате този стил, трябва да инсталирате разширение като Stylus.

За да инсталирате този стил, трябва да инсталирате разширение като Stylus.

За да инсталирате този стил, трябва да инсталирате разширение като Stylus.

За да инсталирате този стил, трябва да имате инсталиран мениджър на потребителски стилове.

За да инсталирате този стил, трябва да имате инсталиран мениджър на потребителски стилове.

За да инсталирате този стил, трябва да имате инсталиран мениджър на потребителски стилове.

(Вече имам инсталиран мениджър на стиловете, искам да го инсталирам!)

// ==UserScript==

// @name         Spotify Infinite Scroller
// @namespace    http://tampermonkey.net/
// @version      1.9.7
// @description  Infinite podcast scrolling! Never click that pesky load more episodes button again!
// @author       TheCodingChihuahua
// @match        https://open.spotify.com/*
// @icon         https://www.google.com/s2/favicons?sz=64&domain=spotify.com
// @grant        none
// @license      Apache-2.0 license
// @run-at       document-idle

// ==/UserScript==

var version = '1.9.7';

// Changelog
/*
1.9.7 (hotfix)
added logging in debug for when you toggle autoload

1.9.5 (hotfix)
I cant remember what I added in this update :P

1.9.0 (hotfix)
Moved the toggle button from top right to bottom right, and added a more dynamic debug system, now when you hover over the button the pointer changes

1.8.0 (hotfix)
Fixed the button not being clicked sometimes

1.7.0 (update)
Added a button to toggle!

1.0.5 (hotfix)
Slightly smoother (changed rootMargin to 1000 instead of 600)

1.0.0
Super simple, just clicks the button
*/

(function () {
    'use strict';
    let clicking = false;
    let enabled = true;
    let toggleButton = null;
    const clickIt = (btn) => {
        if (clicking || !enabled) return;
        clicking = true;
        const old = btn.style.outline;
        btn.style.outline = '5px solid #1ed760';
        btn.click();
        console.log('Spotify Infinite Scroll v'+version+': Clicked Load more episodes');
        setTimeout(() => btn.style.outline = old, 600);
        setTimeout(() => clicking = false, 1400);
    };
    const observer = new IntersectionObserver((entries) => {
        entries.forEach(entry => {
            if (entry.isIntersecting) clickIt(entry.target);
        });
    }, { rootMargin: '1000px', threshold: 0.0 });
    const findButton = () => {
        let btn = document.querySelector('button[data-testid="show-more-button-inline"]');
        if (!btn) {
            for (const b of document.querySelectorAll('button')) {
                const txt = (b.innerText || b.textContent || '').toLowerCase();
                if (txt.includes('load more episodes') || txt.includes('show more episodes')) {
                    btn = b; break;
                }
            }
        }
        if (btn && !btn._watched) {
            btn._watched = true;
            observer.observe(btn);
            console.log('Spotify Infinite Scroll v'+version+': Watching Load more button');
            // Manual check if already in expanded view (fixes no-trigger on short pages)
            setTimeout(() => {
                if (!btn) return; // Safety
                const rect = btn.getBoundingClientRect();
                const margin = 1000; // Matches rootMargin
                const expandedTop = -margin;
                const expandedBottom = window.innerHeight + margin;
                const expandedLeft = -margin;
                const expandedRight = window.innerWidth + margin;
                const isIntersecting = !(rect.bottom < expandedTop || rect.top > expandedBottom || rect.right < expandedLeft || rect.left > expandedRight);
                if (isIntersecting) {
                    console.log('Spotify Infinite Scroll v'+version+': Button already in view – manual trigger');
                    clickIt(btn);
                }
            }, 0);
        }
    };
    // Create the toggle button
    const createToggleButton = () => {
        if (toggleButton) return;
        toggleButton = document.createElement('button');
        toggleButton.innerHTML = 'Auto-Load: <strong>ON</strong>';
        toggleButton.style.cssText = `
            position: fixed !important;
            top: 810px !important;
            right: 15px !important;
            z-index: 99999 !important;
            padding: 12px 18px !important;
            border-radius: 25px !important;
            border: 2px solid #1ed760 !important;
            background: #1ed760 !important;
            color: #000 !important;
            font-weight: bold !important;
            box-shadow: 0 4px 12px rgba(0,0,0,0.3) !important;
            transition: all 0.3s ease !important;
            min-width: 140px !important;
            text-align: center !important;
            cursor: pointer !important;
        `;
        toggleButton.onclick = () => {
            enabled = !enabled;
            const status = enabled ? 'ON' : 'OFF';
            toggleButton.innerHTML = `Auto-Load: <strong>${status}</strong>`;
            toggleButton.style.background = enabled ? '#1ed760' : '#ff4d4d';
            toggleButton.style.borderColor = enabled ? '#1ed760' : '#ff4d4d';
            toggleButton.style.color = enabled ? '#000' : '#fff';
            console.log('Spotify Infinite Scroll v'+version+': Autoload toggled '+enabled);
        };
        document.body.appendChild(toggleButton);
    };
    // Detect when Now Playing bar is open and move button to the left
    const updateButtonPosition = () => {
        if (!toggleButton) return;
        // Spotify's Now Playing bar has this attribute when expanded
        const nowPlayingBar = document.querySelector('[data-testid="now-playing-bar"]');
        const isExpanded = nowPlayingBar && window.getComputedStyle(nowPlayingBar).transform.includes('translateX(0');
        if (isExpanded) {
            // Move to left side
            toggleButton.style.right = 'auto';
            toggleButton.style.left = '15px';
        } else {
            // Back to right side
            toggleButton.style.left = 'auto';
            toggleButton.style.right = '15px';
        }
    };
    // Watch for Now Playing bar changes
    const npObserver = new MutationObserver(updateButtonPosition);
    const startObservers = () => {
        const npBar = document.querySelector('[data-testid="now-playing-bar"]');
        if (npBar) npObserver.observe(npBar.parentElement, { attributes: true, subtree: true });
        updateButtonPosition();
    };
    // Page navigation handling
    let lastPath = location.pathname;
    const checkPage = () => {
        if (location.pathname !== lastPath) {
            lastPath = location.pathname;
            if (lastPath.startsWith('/show/')) {
                setTimeout(() => {
                    createToggleButton();
                    findButton();
                    startObservers();
                }, 1000);
            } else {
                if (toggleButton) toggleButton.remove();
                toggleButton = null;
            }
        }
    };
    // Start everything
    new MutationObserver(findButton).observe(document.body, { childList: true, subtree: true });
    setInterval(findButton, 3000);
    setInterval(checkPage, 300);
    setInterval(updateButtonPosition, 100); // Fallback safety
    checkPage();
    setTimeout(findButton, 100);
    console.log('Spotify Infinite Scroll v'+version+': Loaded');
})();