Last active
June 18, 2026 06:01
-
-
Save kyohsuke/eac69ddef61d4cd8d165c8b2e59ff0ef to your computer and use it in GitHub Desktop.
This file contains hidden or 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
| // ==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