1
0

added seek bar functionality

This commit is contained in:
anon 2024-10-15 14:56:50 +00:00
parent 0b6058965f
commit 132ffe86ba

128
script.js
View File

@ -1,8 +1,8 @@
// ==UserScript== // ==UserScript==
// @name antisocial.moe - youtube music scrobbler // @name antisocial.moe - youtube music scrobbler
// @namespace http://tampermonkey.net/ // @namespace http://tampermonkey.net/
// @version 2.0 // @version 2.1
// @author // @description Scrobbles songs from YouTube Music to antisocial.moe, with progress bar monitoring
// @match https://music.youtube.com/* // @match https://music.youtube.com/*
// @grant GM_xmlhttpRequest // @grant GM_xmlhttpRequest
// @grant GM_setValue // @grant GM_setValue
@ -186,6 +186,90 @@
// Timer for scrobbling after 10 seconds of playback // Timer for scrobbling after 10 seconds of playback
let playbackTimer = null; 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. * 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. * Scrobbles the song only if it has been playing for more than 10 seconds and is currently playing.
@ -208,6 +292,10 @@
if (!areSongsEqual(lastScrobbledSong, songInfo)) { if (!areSongsEqual(lastScrobbledSong, songInfo)) {
console.log(`New song detected: "${songInfo.title}" by ${songInfo.author} from "${songInfo.album}"`); console.log(`New song detected: "${songInfo.title}" by ${songInfo.author} from "${songInfo.album}"`);
// Check if the song is currently playing
if (isPlaying() && !scrobbleScheduled) {
scrobbleScheduled = true;
// Update lastScrobbledSong to prevent repeated attempts // Update lastScrobbledSong to prevent repeated attempts
// Note: We update it here to prevent multiple timers for the same song // Note: We update it here to prevent multiple timers for the same song
lastScrobbledSong = songInfo; lastScrobbledSong = songInfo;
@ -216,12 +304,9 @@
if (playbackTimer) { if (playbackTimer) {
clearTimeout(playbackTimer); clearTimeout(playbackTimer);
playbackTimer = null; playbackTimer = null;
scrobbleScheduled = false;
} }
// Check if the song is currently playing
if (isPlaying()) {
if (!scrobbleScheduled) {
scrobbleScheduled = true;
playbackTimer = setTimeout(() => { playbackTimer = setTimeout(() => {
if (isPlaying()) { if (isPlaying()) {
scrobble(songInfo.title, songInfo.author, songInfo.album); scrobble(songInfo.title, songInfo.author, songInfo.album);
@ -232,7 +317,8 @@
} }
}, 10000); // 10 seconds }, 10000); // 10 seconds
console.log('Started 10-second timer for scrobble.'); console.log('Started 10-second timer for scrobble.');
} } else {
console.debug(`isPlaying: ${isPlaying()} scrobbleScheduled: ${scrobbleScheduled}`)
} }
} else { } else {
console.debug('Same song, no action needed.'); console.debug('Same song, no action needed.');
@ -298,6 +384,12 @@
console.log('YouTube Music Scrobbler with Conditional Scrobbling is now active.'); 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. * Waits for the player bar to be available in the DOM before initializing the observer.
*/ */
@ -309,6 +401,9 @@
initObserver(); initObserver();
// Initial scrobble handling // Initial scrobble handling
handleScrobble(); handleScrobble();
// Initialize progress observer
initProgressObserver();
} }
}, 1000); // Check every second }, 1000); // Check every second
@ -319,12 +414,6 @@
// }, 30000); // 30 seconds // }, 30000); // 30 seconds
} }
/**
* ============================
* UI for Managing AUTH_KEY
* ============================
*/
/** /**
* Prompts the user to enter their AUTH_KEY and stores it using GM_setValue. * Prompts the user to enter their AUTH_KEY and stores it using GM_setValue.
*/ */
@ -350,15 +439,8 @@
} }
/** /**
* ============================ * Optionally, prompt the user to set the AUTH_KEY if it's not already set.
* Initialization
* ============================
*/ */
// 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() { function promptForAuthKeyIfNeeded() {
const authKey = GM_getValue('AUTH_KEY', ''); const authKey = GM_getValue('AUTH_KEY', '');
if (!authKey) { 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 // Start the script by waiting for the player bar to load
waitForPlayerBar(); waitForPlayerBar();
@ -373,4 +458,3 @@
promptForAuthKeyIfNeeded(); promptForAuthKeyIfNeeded();
})(); })();