Skip to content

Instantly share code, notes, and snippets.

@kyohsuke
Last active June 18, 2026 06:01
Show Gist options
  • Select an option

  • Save kyohsuke/eac69ddef61d4cd8d165c8b2e59ff0ef to your computer and use it in GitHub Desktop.

Select an option

Save kyohsuke/eac69ddef61d4cd8d165c8b2e59ff0ef to your computer and use it in GitHub Desktop.
// ==UserScript==
// @name Auto Refresh YouTube Live
// @description Automatically tracks live head for YouTube Live
// @match https://www.youtube.com/*
// @version 1.0.7
// @homepageURL https://gist.github.com/kyohsuke/eac69ddef61d4cd8d165c8b2e59ff0ef
// @updateURL https://gist.githubusercontent.com/kyohsuke/eac69ddef61d4cd8d165c8b2e59ff0ef/raw/youtubeLiveObserver.user.js
// @downloadURL https://gist.githubusercontent.com/kyohsuke/eac69ddef61d4cd8d165c8b2e59ff0ef/raw/youtubeLiveObserver.user.js
// @icon https://www.google.com/s2/favicons?sz=64&domain=youtube.com
// @grant none
// ==/UserScript==
(function(){
function liveTracker() {
console.log("Init Live Tracker");
const initialInterval = 3;
let observer = null;
let intervalResetTimerID = null;
let liveInterval = initialInterval;
let currentURL = "";
function isObject(value) {
return value !== null && typeof value === 'object'
}
function liveButton() {
return document.getElementsByClassName("ytp-live-badge")[0];
}
function videoTag() {
return document.getElementsByTagName("video")[0]?.src;
}
function isLive(btn) {
return (btn != undefined && window.getComputedStyle(btn).display != "none");
}
function addObserver() {
let liveBtn = liveButton();
const handleMutation = (mutationsList, observer) => {
mutationsList.forEach(mutation => {
// 変更に対する処理
let onLive = false;
mutation.target.classList.forEach(className => {
if(className == "ytp-live-badge-is-livehead") {
onLive = true;
}
});
if(isLive(liveBtn) && !onLive) {
let nextDelay = 1000 * liveInterval;
console.log("Move ahead after", liveInterval, "seconds");
if(liveInterval < 10) {
liveInterval += 1;
}
if(intervalResetTimerID) {
clearTimeout(intervalResetTimerID);
}
intervalResetTimerID = setTimeout(function(){
liveInterval = initialInterval;
console.log("Timer Reset");
}, 1000 * 60);
setTimeout(function(){
if(videoTag()) {
liveBtn.click();
}
}, nextDelay);
}
});
};
if(observer != null) {
observer.disconnect();
}
if(isObject(liveBtn)) {
console.log("Put Observer");
observer = new MutationObserver(handleMutation);
observer.observe(liveBtn, {
attributeFilter: ["class"],
});
}
}
function putEvents() {
liveInterval = initialInterval;
addObserver();
}
setInterval(function(){
let pickURL = location.href;
if(currentURL != pickURL) {
currentURL = pickURL;
setTimeout(putEvents, 1000);
}
}, 1000);
}
liveTracker();
})();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment