From 132ffe86ba56c6695f2ae4ccc4b361789871eeb6 Mon Sep 17 00:00:00 2001 From: anon Date: Tue, 15 Oct 2024 14:56:50 +0000 Subject: [PATCH] added seek bar functionality --- script.js | 166 ++++++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 125 insertions(+), 41 deletions(-) diff --git a/script.js b/script.js index 2cbf03d..1926f02 100644 --- a/script.js +++ b/script.js @@ -1,8 +1,8 @@ // ==UserScript== // @name antisocial.moe - youtube music scrobbler // @namespace http://tampermonkey.net/ -// @version 2.0 -// @author +// @version 2.1 +// @description Scrobbles songs from YouTube Music to antisocial.moe, with progress bar monitoring // @match https://music.youtube.com/* // @grant GM_xmlhttpRequest // @grant GM_setValue @@ -186,6 +186,90 @@ // Timer for scrobbling after 10 seconds of playback let playbackTimer = null; + /** + * ============================ + * Progress Monitoring Logic + * ============================ + */ + + /** + * Gets the current progress percentage of the song. + * @returns {number|null} Progress percentage (0-100) or null if not available. + */ + function getProgressPercentage() { + const progressBar = querySelectorDeep('#progress-bar'); + if (progressBar) { + const valueNow = parseInt(progressBar.getAttribute('aria-valuenow'), 10); + const valueMax = parseInt(progressBar.getAttribute('aria-valuemax'), 10); + if (!isNaN(valueNow) && !isNaN(valueMax) && valueMax > 0) { + return (valueNow / valueMax) * 100; + } + } + return null; + } + + // Variable to keep track of last progress percentage + let lastProgressPercentage = null; + + /** + * Handles progress changes and detects jumps from >95% to <5%. + * @param {number} newPercentage - The new progress percentage. + */ + function handleProgressChange(newPercentage) { + if (lastProgressPercentage === null) { + lastProgressPercentage = newPercentage; + return; + } + if (lastProgressPercentage > 95 && newPercentage < 5) { + console.log('Progress jumped from >95% to <5%. Sending new scrobble.'); + const songInfo = getSongInfo(); + if (songInfo) { + scrobble(songInfo.title, songInfo.author, songInfo.album); + } else { + console.warn('Cannot scrobble: song information not found.'); + } + } + lastProgressPercentage = newPercentage; + } + + /** + * Initializes a MutationObserver to monitor the progress bar's aria-valuenow. + */ + function initProgressObserver() { + const progressBar = querySelectorDeep('#progress-bar'); + if (!progressBar) { + console.error('Progress bar not found. Progress monitoring not initialized.'); + return; + } + + // Initialize lastProgressPercentage + lastProgressPercentage = getProgressPercentage(); + console.debug('Initial progress percentage:', lastProgressPercentage); + + // Create observer + const progressObserver = new MutationObserver((mutations) => { + for (let mutation of mutations) { + if (mutation.type === 'attributes' && mutation.attributeName === 'aria-valuenow') { + const newPercentage = getProgressPercentage(); + if (newPercentage !== null) { + console.debug(`Progress updated: ${newPercentage.toFixed(2)}%`); + handleProgressChange(newPercentage); + } + } + } + }); + + // Observe aria-valuenow + progressObserver.observe(progressBar, { attributes: true, attributeFilter: ['aria-valuenow'] }); + console.log('Progress observer initialized.'); + } + + /** + * ============================ + * Scrobbling Logic (continued) + * ============================ + */ + /** * Handles the scrobbling logic when a song change or playback state change is detected. * Scrobbles the song only if it has been playing for more than 10 seconds and is currently playing. @@ -208,31 +292,33 @@ if (!areSongsEqual(lastScrobbledSong, songInfo)) { console.log(`New song detected: "${songInfo.title}" by ${songInfo.author} from "${songInfo.album}"`); - // Update lastScrobbledSong to prevent repeated attempts - // Note: We update it here to prevent multiple timers for the same song - lastScrobbledSong = songInfo; - - // Clear any existing timer - if (playbackTimer) { - clearTimeout(playbackTimer); - playbackTimer = null; - } - // Check if the song is currently playing - if (isPlaying()) { - if (!scrobbleScheduled) { - scrobbleScheduled = true; - playbackTimer = setTimeout(() => { - if (isPlaying()) { - scrobble(songInfo.title, songInfo.author, songInfo.album); - playbackTimer = null; - } else { - console.log('Playback stopped before scrobble threshold.'); - scrobbleScheduled = false; - } - }, 10000); // 10 seconds - console.log('Started 10-second timer for scrobble.'); - } + if (isPlaying() && !scrobbleScheduled) { + scrobbleScheduled = true; + + // Update lastScrobbledSong to prevent repeated attempts + // Note: We update it here to prevent multiple timers for the same song + lastScrobbledSong = songInfo; + + // Clear any existing timer + if (playbackTimer) { + clearTimeout(playbackTimer); + playbackTimer = null; + scrobbleScheduled = false; + } + + playbackTimer = setTimeout(() => { + if (isPlaying()) { + scrobble(songInfo.title, songInfo.author, songInfo.album); + playbackTimer = null; + } else { + console.log('Playback stopped before scrobble threshold.'); + scrobbleScheduled = false; + } + }, 10000); // 10 seconds + console.log('Started 10-second timer for scrobble.'); + } else { + console.debug(`isPlaying: ${isPlaying()} scrobbleScheduled: ${scrobbleScheduled}`) } } else { console.debug('Same song, no action needed.'); @@ -298,6 +384,12 @@ console.log('YouTube Music Scrobbler with Conditional Scrobbling is now active.'); } + /** + * ============================ + * Initialization + * ============================ + */ + /** * Waits for the player bar to be available in the DOM before initializing the observer. */ @@ -309,6 +401,9 @@ initObserver(); // Initial scrobble handling handleScrobble(); + + // Initialize progress observer + initProgressObserver(); } }, 1000); // Check every second @@ -319,12 +414,6 @@ // }, 30000); // 30 seconds } - /** - * ============================ - * UI for Managing AUTH_KEY - * ============================ - */ - /** * Prompts the user to enter their AUTH_KEY and stores it using GM_setValue. */ @@ -350,15 +439,8 @@ } /** - * ============================ - * Initialization - * ============================ + * Optionally, prompt the user to set the AUTH_KEY if it's not already set. */ - - // Register the menu command for setting the AUTH_KEY - registerMenuCommands(); - - // Optionally, prompt the user to set the AUTH_KEY if it's not already set function promptForAuthKeyIfNeeded() { const authKey = GM_getValue('AUTH_KEY', ''); if (!authKey) { @@ -366,6 +448,9 @@ } } + // Register the menu command for setting the AUTH_KEY + registerMenuCommands(); + // Start the script by waiting for the player bar to load waitForPlayerBar(); @@ -373,4 +458,3 @@ promptForAuthKeyIfNeeded(); })(); -