Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Select an option

  • Save anphetamina/2fc2bdc13f1c44235bdad37273c19960 to your computer and use it in GitHub Desktop.

Select an option

Save anphetamina/2fc2bdc13f1c44235bdad37273c19960 to your computer and use it in GitHub Desktop.
YouTube Playback Error Auto-Refresh script for tampermonkey (Firefox)
// ==UserScript==
// @name YouTube Playback Error Auto-Refresh
// @namespace https://local.userscripts/youtube-error-refresh
// @version 3.0.0
// @description When YouTube's "An error occurred, please try again later" playback overlay appears, clears the per-tab sessionStorage (mimicking a fresh tab) and reloads the SAME tab, until the video is playable. Language-independent. Does NOT autoplay. No new tabs.
// @author Antonio Santoro
// @match https://www.youtube.com/*
// @match https://m.youtube.com/*
// @run-at document-start
// @grant none
// @noframes
// ==/UserScript==
(function () {
'use strict';
// ------------------------- Config (tweak freely) -------------------------
const MAX_ATTEMPTS = 6; // Stop after this many resets for the same video (prevents infinite loops).
const CONFIRM_MS = 1200; // Error must stay visible this long before acting (ignores a flicker).
const RELOAD_DELAY_MS = 300; // Small pause before reloading.
const POLL_MS = 1000; // How often we check while watching.
const STABLE_MS = 10000; // Go idle / reset counter after this much error-free, loaded time.
const MAX_WATCH_MS = 90000; // Absolute safety cutoff for polling after (re)start.
const CLEAR_SESSION = true; // The key trick: wipe sessionStorage before reloading (emulates a fresh tab).
const VERBOSE = true; // Set false to silence routine logs.
// -------------------------------------------------------------------------
// The transient playback error overlay — same class in every language.
// The "video unavailable / private" screen is a DIFFERENT element, not matched here.
const ERROR_SELECTOR = '.ytp-error';
const log = (...a) => { if (VERBOSE) console.log('[YT auto-refresh]', ...a); };
let confirmTimer = null;
let reloading = false;
let pollId = null;
let watchStart = 0;
let cleanSince = 0;
function videoKey() {
try {
const u = new URL(location.href);
return u.searchParams.get('v') || location.pathname;
} catch (e) {
return location.pathname;
}
}
// Counter lives in localStorage so wiping sessionStorage doesn't erase it.
const key = () => 'ytErrRefresh:' + videoKey();
const getCount = () => { try { return parseInt(localStorage.getItem(key()) || '0', 10) || 0; } catch (e) { return 0; } };
const setCount = (n) => { try { localStorage.setItem(key(), String(n)); } catch (e) {} };
const clearCount = () => { try { localStorage.removeItem(key()); } catch (e) {} };
function isVisible(el) {
if (!el) return false;
const cs = getComputedStyle(el);
if (cs.display === 'none' || cs.visibility === 'hidden' || cs.opacity === '0') return false;
return el.getClientRects().length > 0;
}
const errorShowing = () => isVisible(document.querySelector(ERROR_SELECTOR));
function playerLooksLoaded() {
const v = document.querySelector('video.html5-main-video') || document.querySelector('video');
return !!v && (v.readyState >= 1 || v.duration > 0);
}
function startPolling() {
watchStart = Date.now();
cleanSince = 0;
if (pollId == null) pollId = setInterval(check, POLL_MS);
}
function stopPolling(reason) {
if (pollId != null) { clearInterval(pollId); pollId = null; log('idle —', reason); }
}
function cancelPending() {
if (confirmTimer) { clearTimeout(confirmTimer); confirmTimer = null; }
}
function scheduleReload() {
if (reloading || confirmTimer) return;
log('error overlay detected — confirming for ' + CONFIRM_MS + 'ms...');
confirmTimer = setTimeout(() => {
confirmTimer = null;
if (!errorShowing()) { log('overlay cleared on its own; not reloading'); return; }
const count = getCount();
if (count >= MAX_ATTEMPTS) {
console.warn('[YT auto-refresh] Giving up after ' + count + ' resets for this video.');
stopPolling('max attempts reached');
return;
}
setCount(count + 1);
reloading = true;
if (CLEAR_SESSION) {
try { sessionStorage.clear(); log('sessionStorage cleared (fresh-tab emulation)'); }
catch (e) { log('could not clear sessionStorage:', e); }
}
log('reloading (attempt ' + (count + 1) + '/' + MAX_ATTEMPTS +
') — enable "Persist Logs" to keep this visible across the reload');
setTimeout(() => location.reload(), RELOAD_DELAY_MS);
}, CONFIRM_MS);
}
function check() {
if (reloading) return;
if (errorShowing()) {
cleanSince = 0;
watchStart = Date.now();
scheduleReload();
return;
}
cancelPending();
if (playerLooksLoaded()) {
if (!cleanSince) cleanSince = Date.now();
if (Date.now() - cleanSince > STABLE_MS) { clearCount(); stopPolling('video stable'); }
} else {
cleanSince = 0;
if (Date.now() - watchStart > MAX_WATCH_MS) stopPolling('watch window elapsed');
}
}
log('active on', location.href);
startPolling();
document.addEventListener('DOMContentLoaded', check);
window.addEventListener('load', check);
window.addEventListener('yt-navigate-finish', () => { reloading = false; startPolling(); check(); });
})();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment