Skip to content

Instantly share code, notes, and snippets.

@adokce
Last active May 7, 2024 17:51
Show Gist options
  • Save adokce/3e697fecaab7546c8306feb64cbc9643 to your computer and use it in GitHub Desktop.
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
/**
* 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