// ==UserScript==
// @name Simple YouTube Age Restriction Bypass
// @description Watch age-restricted YouTube videos without login or age verification 😎
// @version 2.6.0
// @author Zerody (Optimized by Cody)
// @namespace https://github.com/zerodytrash/Simple-YouTube-Age-Restriction-Bypass/
// @supportURL https://github.com/zerodytrash/Simple-YouTube-Age-Restriction-Bypass/issues
// @license MIT
// @match https://www.youtube.com/*
// @match https://www.youtube-nocookie.com/*
// @match https://m.youtube.com/*
// @match https://music.youtube.com/*
// @grant none
// @run-at document-start
// @compatible chrome
// @compatible firefox
// @compatible opera
// @compatible edge
// @compatible safari
// ==/UserScript==
(function () {
'use strict';
// Configuration constants
const UNLOCKABLE_PLAYABILITY_STATUSES = ['AGE_VERIFICATION_REQUIRED', 'AGE_CHECK_REQUIRED', 'CONTENT_CHECK_REQUIRED', 'LOGIN_REQUIRED'];
const VALID_PLAYABILITY_STATUSES = ['OK', 'LIVE_STREAM_OFFLINE'];
const ACCOUNT_PROXY_SERVER = 'https://youtube-proxy.zerody.one';
const VIDEO_PROXY_SERVER = 'https://ny.4everproxy.com';
const ENABLE_UNLOCK_NOTIFICATION = true;
const ENABLE_UNLOCK_CONFIRMATION_EMBED = true;
const GOOGLE_AUTH_HEADERS = ['Authorization', 'X-Goog-AuthUser', 'X-Origin'];
const BLURRED_THUMBNAIL_LENGTHS = [32, 48, 56, 68, 72, 84, 88];
const LOG_PREFIX = '%cYouTube Age Bypass:';
const LOG_STYLE = 'color: white; background-color: #007BFF; padding: 3px;';
const logger = {
info: (msg) => console.info(LOG_PREFIX, LOG_STYLE, msg),
error: (err, msg) => console.error(LOG_PREFIX, LOG_STYLE, msg, err),
};
// Helper to debounce functions
const debounce = (fn, delay) => {
let timeout;
return (...args) => {
clearTimeout(timeout);
timeout = setTimeout(() => fn.apply(this, args), delay);
};
};
// Element observer with timeout
function waitForElement(selector, timeout = 5000) {
return new Promise((resolve, reject) => {
const observer = new MutationObserver((mutations, obs) => {
const elem = document.querySelector(selector);
if (elem) {
obs.disconnect();
resolve(elem);
}
});
observer.observe(document.body, { childList: true, subtree: true });
setTimeout(() => {
observer.disconnect();
reject(new Error('Element not found: ' + selector));
}, timeout);
});
}
// Check if video is restricted
function isAgeRestricted(status) {
return UNLOCKABLE_PLAYABILITY_STATUSES.includes(status?.status);
}
// Deep copy object
function deepCopy(obj) {
return JSON.parse(JSON.stringify(obj));
}
// Unlock video response
function unlockResponse(response) {
if (isAgeRestricted(response.playabilityStatus)) {
logger.info('Unlocking video...');
const unlockedResponse = fetchUnlockedResponse(response.videoDetails.videoId);
if (unlockedResponse.errorMessage) {
logger.error(null, `Unlock failed: ${unlockedResponse.errorMessage}`);
return;
}
// Replace response content with unlocked data
Object.assign(response, unlockedResponse);
response.unlocked = true;
logger.info('Video unlocked successfully.');
}
}
// Fetch unlocked video response
function fetchUnlockedResponse(videoId) {
const payload = {
context: {
client: {
clientName: 'WEB',
clientVersion: '2.20220203.04.00',
},
},
videoId,
racyCheckOk: true,
contentCheckOk: true,
};
try {
const xhr = new XMLHttpRequest();
xhr.open('POST', `${ACCOUNT_PROXY_SERVER}/youtubei/v1/player`, false);
xhr.send(JSON.stringify(payload));
return JSON.parse(xhr.responseText);
} catch (err) {
logger.error(err, 'Failed to fetch unlocked response');
return { errorMessage: 'Unlock failed' };
}
}
// Hook into JSON.parse to intercept video data
const nativeJSONParse = JSON.parse;
JSON.parse = function (text) {
const data = nativeJSONParse.call(this, text);
if (data && data.playabilityStatus) {
try {
unlockResponse(data);
} catch (err) {
logger.error(err, 'Error unlocking response');
}
}
return data;
};
// Hook into XMLHttpRequest.open for unlocking
const nativeXHROpen = XMLHttpRequest.prototype.open;
XMLHttpRequest.prototype.open = function (method, url, ...args) {
if (url.includes('/youtubei/v1/player')) {
this.addEventListener('readystatechange', function () {
if (this.readyState === 4 && this.status === 200) {
try {
const response = JSON.parse(this.responseText);
unlockResponse(response);
this.responseText = JSON.stringify(response);
} catch (err) {
logger.error(err, 'Failed to intercept response');
}
}
});
}
nativeXHROpen.call(this, method, url, ...args);
};
logger.info('Script initialized. Age restriction bypass enabled.');
})();