Last active
May 7, 2024 17:51
-
-
Save adokce/3e697fecaab7546c8306feb64cbc9643 to your computer and use it in GitHub Desktop.
Pomodoro for YouTube (+YouTube Music) - Run in the browser tab's console to start/stop music in pomodoro interval
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* scriptYoutubePomodoro.js | |
* | |
* Overview: | |
* This script implements a Pomodoro timer that controls music playback on YouTube AND Youtube Music | |
* directly from the browser's console. It is designed to help manage work and break intervals | |
* by automatically playing and pausing music, along with playing distinct tones to indicate | |
* the transition between work and break periods. | |
* | |
* Features: | |
* - Toggle music play/pause automatically at the start and end of work/break intervals. | |
* - Play a distinct tone at the beginning of each work and break period. | |
* - Easily adjustable settings for work and break durations, tone durations, and volumes. | |
* - Simple console commands to start, stop, and reset the Pomodoro timer. | |
* | |
* Usage: | |
* 1. Open YouTube Music in a browser tab and start playing something. | |
* 2. Open the browser's developer console (usually F12 or right-click -> Inspect -> Console). | |
* 3. Copy and paste the entire script into the console and press Enter to load it. | |
* 4. The Pomodoro timer will automatically start upon executing the script. Below are additional functions to manually control the timer: | |
* - `startPomodoro()`: Starts the Pomodoro timer with music control. | |
* - `stopPomodoro()`: Stops the current Pomodoro timer. | |
* - `resetPomodoro()`: Resets and restarts the Pomodoro timer from the beginning of a work period. | |
* | |
* Example: | |
* To start the Pomodoro timer, simply type `startPomodoro()` in the console and press Enter. | |
* To stop the timer at any time, type `stopPomodoro()`. | |
* To reset and restart the timer, type `resetPomodoro()`. | |
* | |
* Note: | |
* This script is intended for use with YouTube Music and has been tested under typical usage conditions. | |
* Ensure that your browser allows scripts to control audio playback and simulate keyboard events. | |
*/ | |
/* | |
* You can also use this as a bookmarklet via `https://caiorss.github.io/bookmarklet-maker/` | |
*/ | |
// Adjustable Variables | |
let workDuration = 25/*minutes*/ * 60; // Work duration in seconds | |
let breakDuration = 5/*minutes*/ * 60; // Break duration in seconds | |
let workToneDuration = 1200; // Tone duration for work transition in milliseconds | |
let breakToneDuration = 300; // Tone duration for break transition in milliseconds | |
let workToneVolume = 0.3; // Volume for work tone | |
let breakToneVolume = 0.15; // Volume for break tone | |
let updateTimerDisplay; | |
let isWorkTime = true; | |
let pomodoroTimer; | |
function simulateKey(key) { | |
let event = new KeyboardEvent("keydown", { | |
key: key, | |
bubbles: true | |
}); | |
document.body.dispatchEvent(event); | |
} | |
function playTone(duration, volume, callback) { | |
let audioContext = new AudioContext(); | |
let oscillator1 = audioContext.createOscillator(); | |
let oscillator2 = audioContext.createOscillator(); | |
let gainNode = audioContext.createGain(); | |
oscillator1.type = 'sine'; | |
oscillator2.type = 'sine'; | |
oscillator1.frequency.value = 830; // Higher tone | |
oscillator2.frequency.value = 660; // Lower tone | |
oscillator1.connect(gainNode); | |
oscillator2.connect(gainNode); | |
gainNode.connect(audioContext.destination); | |
gainNode.gain.setValueAtTime(volume, audioContext.currentTime); | |
gainNode.gain.setValueAtTime(volume, audioContext.currentTime + duration / 2000); | |
gainNode.gain.exponentialRampToValueAtTime(volume * 0.01, audioContext.currentTime + duration / 1000); | |
oscillator1.start(audioContext.currentTime); | |
oscillator1.stop(audioContext.currentTime + duration / 2000); | |
oscillator2.start(audioContext.currentTime + duration / 2000); | |
oscillator2.stop(audioContext.currentTime + duration / 1000); | |
oscillator2.onended = function() { | |
audioContext.close(); | |
if (callback) callback(); | |
}; | |
} | |
function togglePlayPause() { | |
simulateKey(';'); | |
} | |
function isMusicPlaying() { | |
const media = document.querySelectorAll('audio, video'); | |
return Array.from(media).some(media => !media.paused); | |
} | |
function formatTime(seconds) { | |
let min = Math.floor(seconds / 60); | |
let sec = seconds % 60; | |
return min + ':' + (sec < 10 ? '0' + sec : sec); | |
} | |
// Create and style the timer display element | |
function createTimerDisplay() { | |
const timerDisplay = document.createElement('div'); | |
timerDisplay.id = 'pomodoroTimerDisplay'; | |
timerDisplay.style.position = 'fixed'; | |
timerDisplay.style.top = '50px'; | |
timerDisplay.style.left = '50%'; | |
timerDisplay.style.transform = 'translateX(-50%)'; | |
timerDisplay.style.backgroundColor = 'rgba(0, 0, 0, 0.7)'; | |
timerDisplay.style.color = 'white'; | |
timerDisplay.style.padding = '10px'; | |
timerDisplay.style.borderRadius = '5px'; | |
timerDisplay.style.zIndex = '10000'; | |
timerDisplay.style.fontSize = '16px'; | |
timerDisplay.style.fontFamily = 'Arial, sans-serif'; | |
timerDisplay.style.textAlign = 'center'; | |
document.body.appendChild(timerDisplay); | |
} | |
// Update the timer display element | |
function updateTimeLeftDisplay(timeLeft) { | |
const timerDisplay = document.getElementById('pomodoroTimerDisplay'); | |
timerDisplay.textContent = `${isWorkTime ? 'Work' : 'Break'} time left: ${formatTime(timeLeft)}`; | |
} | |
// Initialize the timer display when the script loads | |
createTimerDisplay(); | |
// Modify the startPomodoro function to update the HTML element | |
function startPomodoro() { | |
let timeLeft = isWorkTime ? workDuration : breakDuration; | |
clearInterval(updateTimerDisplay); | |
updateTimerDisplay = setInterval(() => { | |
updateTimeLeftDisplay(timeLeft); | |
timeLeft--; | |
if (timeLeft < 0) { | |
clearInterval(updateTimerDisplay); | |
} | |
}, 1000); | |
if (isWorkTime) { | |
if (!isMusicPlaying()) { | |
togglePlayPause(); | |
} | |
playTone(workToneDuration, workToneVolume, () => { | |
pomodoroTimer = setTimeout(() => { | |
isWorkTime = false; | |
startPomodoro(); | |
}, workDuration * 1000); | |
}); | |
} else { | |
togglePlayPause(); | |
playTone(breakToneDuration, breakToneVolume, () => { | |
pomodoroTimer = setTimeout(() => { | |
togglePlayPause(); | |
isWorkTime = true; | |
startPomodoro(); | |
}, breakDuration * 1000); | |
}); | |
} | |
} | |
function stopPomodoro() { | |
clearTimeout(pomodoroTimer); | |
clearInterval(updateTimerDisplay); | |
const timerDisplay = document.getElementById('pomodoroTimerDisplay'); | |
timerDisplay.textContent = 'Pomodoro stopped'; | |
} | |
function resetPomodoro() { | |
clearTimeout(pomodoroTimer); | |
clearInterval(updateTimerDisplay); | |
isWorkTime = true; | |
const timerDisplay = document.getElementById('pomodoroTimerDisplay'); | |
timerDisplay.textContent = 'Pomodoro reset and restarting...'; | |
startPomodoro(); | |
} | |
if (isMusicPlaying()) { | |
console.log("Detected music playing at start. Adjusting timer."); | |
startPomodoro(); | |
} else { | |
togglePlayPause(); | |
startPomodoro(); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment