Simple YouTube Age Restriction Bypass

Watch age-restricted YouTube videos without login or age verification 😎

ของเมื่อวันที่ 18-12-2024 ดู เวอร์ชันล่าสุด

คุณจะต้องติดตั้งส่วนขยาย เช่น Tampermonkey, Greasemonkey หรือ Violentmonkey เพื่อติดตั้งสคริปต์นี้

คุณจะต้องติดตั้งส่วนขยาย เช่น Tampermonkey หรือ Violentmonkey เพื่อติดตั้งสคริปต์นี้

คุณจะต้องติดตั้งส่วนขยาย เช่น Tampermonkey หรือ Violentmonkey เพื่อติดตั้งสคริปต์นี้

You will need to install an extension such as Tampermonkey or Userscripts to install this script.

You will need to install an extension such as Tampermonkey to install this script.

You will need to install a user script manager extension to install this script.

(I already have a user script manager, let me install it!)

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

(I already have a user style manager, let me install it!)

// ==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.');
})();