Greasy Fork

Beatport - useful functions

stop autoplay, auto-jump over initial X seconds of track, and more

// ==UserScript==
// @name         Beatport - useful functions
// @namespace    http://tampermonkey.net/
// @version      0.15
// @description  stop autoplay, auto-jump over initial X seconds of track, and more
// @author       https://puvox.software
// @match        https://*.beatport.com/*
// @icon         https://www.google.com/s2/favicons?domain=beatport.com
// @grant        GM_registerMenuCommand
// @grant        GM_setValue
// @grant        GM_getValue
// @license      MIT
// ==/UserScript==

(function() {
    'use strict';
    function hmsToSecondsOnly(str) {
        var p = str.split(':'),
            s = 0, m = 1;
        while (p.length > 0) {
            s += m * parseInt(p.pop(), 10);
            m *= 60;
        }
        return s;
    }



    // ############################### STOP AUTOPLAY ############################### //
    {
        const stopWithinSecondsLast = 2;

        let optName = 'stopautoplay3';
        const currentValue = GM_getValue(optName, 1);
        GM_registerMenuCommand("Disable autoplay", () => {
            const newValue = prompt("After current track ends, next track will not start [0/1]", GM_getValue(optName, 1));
            if (newValue !== null) {
                GM_setValue(optName, newValue);
                if (newValue) startInterval();
                else              endInterval();
            }
        });

        function checkStopNeeded() {
            // if (!currentValue) return;
            const playState = document.querySelector('[data-testid="player-control-pause_track"]');
            if (playState) {
                const currentTimeElement = document.querySelector('[data-testid="player-clock-played_time"]');
                const currentTimeSeconds = hmsToSecondsOnly(currentTimeElement.textContent);
                const fullTimeSeconds = hmsToSecondsOnly(currentTimeElement.nextSibling.textContent);
                if (fullTimeSeconds-currentTimeSeconds < stopWithinSecondsLast) {
                    try { playState.parentNode.click(); } catch(e){}
                }
            }
        }

        let intervalCheck;
        function startInterval () {
            intervalCheck = setInterval ( checkStopNeeded, 1000);
        }

        function endInterval () {
            clearInterval (intervalCheck);
        }

        if (currentValue > 0) {
            startInterval();
        }

    }
    // ################################################################# //



    function checkFlag (uniqueFlagName, callback) {
        uniqueFlagName = 'bpt_flag_' + uniqueFlagName;
        const table = document.querySelector('[class*="Table-style__TableData"]');
        if (table) {
            const flagDiv = table.querySelector("." + uniqueFlagName);
            if (!flagDiv) {
                const newDiv = document.createElement("div");
                newDiv.classList.add(uniqueFlagName);
                table.appendChild(newDiv);
                callback();
            }
        }
    }


    // ############################# AUTO JUMP from start ############################# //
    {
        function attachPlayEvent() {
            document.querySelectorAll('[data-testid="play-button"]').forEach(e=> e.addEventListener("click", function(x) {
                setTimeout ( execJump, 2000);
            }));
        }

        let playerElement = undefined;
        function execJump () {
            // console.log("b1");
            const currentTimeElement = document.querySelector('[data-testid="player-clock-played_time"]');
            const fullTimeSeconds = hmsToSecondsOnly(currentTimeElement.nextSibling.textContent);
            const jumpSeconds = (fullTimeSeconds/100) * currentValue;
            playerElement = document.querySelector("#bp-player canvas");
            // console.log("b2: " + jumpSeconds);
            for (let i=0; i<jumpSeconds; i++) {
                simulateKeyEvent(playerElement, 'keydown', '.');
            }
        }

        function simulateKeyEvent(playerElement, type, key) {
            const event = new KeyboardEvent(type, {
                key: key,
                code: key,
                which: key.charCodeAt(0),
                keyCode: key.charCodeAt(0),
                charCode: key.charCodeAt(0),
                bubbles: true,
                composed: true,
                cancelable: true,
            });
            playerElement.dispatchEvent(event);
        }

        // option
        let optName = 'autojumpPercentage';
        const currentValue = GM_getValue(optName, 30);
        GM_registerMenuCommand("Autojump seconds from start", () => {
            const newValue = prompt("When tracks start playing, automatically skip first initial seconds (because start period of tracks can often be meaningless). Here you can set value from 0 to 100, which is percentage period of track to be skipped from the start", GM_getValue(optName, 30));
            if (newValue !== null) {
                GM_setValue(optName, newValue);
                if (newValue) startInterval();
                else          endInterval();
            }
        });


        let intervalCheck;
        function startInterval() {
            intervalCheck = setInterval(()=> checkFlag('autostart', attachPlayEvent), 4000);
        }

        function endInterval() {
            clearInterval(intervalCheck);
        }
        // document.addEventListener('readystatechange', event => { if (event.target.readyState === "complete") { // interactive
        if (currentValue > 0) {
            startInterval();
        }


    }
    // ################################################################# //



    // ####################### shadow-color beatport remixes ######################
    {
        function checkCurrentTracks()
        {
            if ( location.href.includes('/artist') && location.href.includes('/tracks') )
            {
                let authorName = document.querySelectorAll('h1')[0].textContent; //location.href.match(/artist\/(.*?)\//)[1].replace(/&/g, '-').replace(/-/g, ' ');
                for( var x of document.querySelectorAll('[data-testid="tracks-table-row"] .cell.title .container > div') )
                {
                    if ( x && x.textContent && !x.textContent.includes(authorName) )
                    {
                        x.parentNode.parentNode.parentNode.style.background = "#000000";
                        x.parentNode.parentNode.parentNode.style.opacity = "1";
                    }
                }
            }
        }

        // option
        let optName = 'shadowbox_remixes';
        const currentValue = GM_getValue(optName, 1);
        GM_registerMenuCommand("Shadow color the remixes (on tracks page)", () => {
            const newValue = prompt("Shadow rows on tracks page, which are remixes of the artist (useful if you don't want to emphasize on that author's remixes) [0/1]", GM_getValue(optName, 1));
            if (newValue !== null) {
                GM_setValue(optName, newValue);
                if (newValue) startInterval();
                else          endInterval();
            }
        });

        const intervalSeconds=3;
        let intervalCheck;
        function startInterval() {
            intervalCheck = setInterval(()=> checkFlag('shadowremixes', checkCurrentTracks), intervalSeconds * 1000 );
        }

        function endInterval() {
            clearInterval(intervalCheck);
        }

        if (currentValue > 0) {
            startInterval();
        }
    }
    // ######################################################################### //







    // ####################### sremove duplicate tracks from "Tracks" page######################
    {
        const trackRowSelector = '[data-testid="tracks-table-row"]';
        function checkIfHideNeeded () {
            const randomTrack = document.querySelector(trackRowSelector);
            if (randomTrack) {
                hideDuplicateTracks();
            }
        }

		function getCurrentTracksOnPage(){
			const matches = location.href.match('per_page=(.*)?|$');
			const amountString = matches[1] || '25';
			return parseInt(amountString);
		}

		function hideDuplicateTracks () {
			const all = document.querySelectorAll(trackRowSelector + ' .cell.title');
			const values = Object.values(all).reverse();
			const allTitles = [];
			for(const el of values) {
				const title = el.textContent;
				if (!allTitles.includes(title)) {
					allTitles.push(title);
				} else {
					// delete el.parentNode.parentNode.removeChild(el.parentNode); // causes exception on beatport client-side
					// el.parentNode.style.background = 'red';
					el.parentNode.style.display = 'none';
				}
			}
		}

        // option
        let optName = 'shadowbox_remixes';
        const currentValue = GM_getValue(optName, 1);
        GM_registerMenuCommand("Hide duplicate tracks (on tracks page)", () => {
            const newValue = prompt("Duplicate tracks take unnecessary waste of your vision, you can hide them [0/1]", GM_getValue(optName, 1));
            if (newValue !== null) {
                GM_setValue(optName, newValue);
                if (newValue) startInterval();
                else          endInterval();
            }
        });

        const intervalSeconds = 3;

        let intervalCheck;
        function startInterval() {
            intervalCheck = setInterval(()=> checkFlag('remove_duplicate_tracks', checkIfHideNeeded), intervalSeconds * 1000 );
        }

        function endInterval() {
            clearInterval(intervalCheck);
        }

        if (currentValue > 0) {
            startInterval();
        }
    }
    // ######################################################################### //

})();