From 951ac748ed3acb62408260ada464dffee87942ad Mon Sep 17 00:00:00 2001
From: David <59258980+zerodytrash@users.noreply.github.com>
Date: Thu, 30 Dec 2021 00:41:07 +0100
Subject: [PATCH] =?UTF-8?q?New=20Version=20=F0=9F=8E=89?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
...ple-YouTube-Age-Restriction-Bypass.user.js | 501 +++++++++++-------
package.json | 2 +-
2 files changed, 304 insertions(+), 199 deletions(-)
diff --git a/dist/Simple-YouTube-Age-Restriction-Bypass.user.js b/dist/Simple-YouTube-Age-Restriction-Bypass.user.js
index 8ed4a44..df4d9b3 100644
--- a/dist/Simple-YouTube-Age-Restriction-Bypass.user.js
+++ b/dist/Simple-YouTube-Age-Restriction-Bypass.user.js
@@ -5,7 +5,7 @@
// @description:de Schaue YouTube Videos mit Altersbeschränkungen ohne Anmeldung und ohne dein Alter zu bestätigen :)
// @description:fr Regardez des vidéos YouTube avec des restrictions d'âge sans vous inscrire et sans confirmer votre âge :)
// @description:it Guarda i video con restrizioni di età su YouTube senza login e senza verifica dell'età :)
-// @version 2.2.2
+// @version 2.3.1
// @author Zerody (https://github.com/zerodytrash)
// @namespace https://github.com/zerodytrash/Simple-YouTube-Age-Restriction-Bypass/
// @supportURL https://github.com/zerodytrash/Simple-YouTube-Age-Restriction-Bypass/issues
@@ -44,6 +44,19 @@
const ACCOUNT_PROXY_SERVER_HOST = 'https://youtube-proxy.zerody.one';
const VIDEO_PROXY_SERVER_HOST = 'https://phx.4everproxy.com';
+ // Whether a thumbnail is blurred can be detected by the following "sqp" parameter values in the thumbnail URL.
+ // Seems to be base64 encoded protobuf objects, see https://stackoverflow.com/a/51203860
+ const THUMBNAIL_BLURRED_SQPS = [
+ '-oaymwEpCOADEI4CSFryq4qpAxsIARUAAAAAGAElAADIQj0AgKJDeAHtAZmZGUI=', // Desktop 480x270
+ '-oaymwEiCOADEI4CSFXyq4qpAxQIARUAAIhCGAFwAcABBu0BmZkZQg==', // Desktop 480x270
+ '-oaymwEiCOgCEMoBSFXyq4qpAxQIARUAAIhCGAFwAcABBu0BZmbmQQ==', // Desktop 360x202
+ '-oaymwEiCNAFEJQDSFXyq4qpAxQIARUAAIhCGAFwAcABBu0BZmZmQg==', // Desktop 720x404
+ '-oaymwEdCNAFEJQDSFryq4qpAw8IARUAAIhCGAHtAWZmZkI=', // Desktop 720x404
+ '-oaymwEdCNACELwBSFryq4qpAw8IARUAAIhCGAHtAT0K10E=', // Desktop 336x188
+ '-oaymwESCMACELQB8quKqQMG7QHMzMxB', // Mobile 320x180
+ '-oaymwESCOADEOgC8quKqQMG7QGZmRlC' // Mobile 480x360
+ ];
+
const isDesktop = window.location.host !== 'm.youtube.com';
const isEmbed = window.location.pathname.includes('/embed/');
@@ -69,6 +82,24 @@
return obj !== null && typeof obj === 'object';
}
+ function findNestedObjectsByAttributeNames(object, attributeNames) {
+ var results = [];
+
+ // Does the current object match the attribute conditions?
+ if (attributeNames.every((key) => typeof object[key] !== 'undefined')) {
+ results.push(object);
+ }
+
+ // Diggin' deeper for each nested object (recursive)
+ Object.keys(object).forEach((key) => {
+ if (object[key] && typeof object[key] === 'object') {
+ results.push(...findNestedObjectsByAttributeNames(object[key], attributeNames));
+ }
+ });
+
+ return results;
+ }
+
function getCookie(name) {
const value = `; ${document.cookie}`;
const parts = value.split(`; ${name}=`);
@@ -194,6 +225,25 @@
return (cvt_hex(H0) + cvt_hex(H1) + cvt_hex(H2) + cvt_hex(H3) + cvt_hex(H4)).toLowerCase();
}
+ let pageLoadedAndVisible = (() => {
+ const pageLoadEventName = isDesktop ? 'yt-navigate-finish' : 'state-navigateend';
+
+ window.addEventListener(pageLoadEventName, () => {
+ if (document.visibilityState === 'hidden') {
+ document.addEventListener('visibilitychange', ready, { once: true });
+ } else {
+ ready();
+ }
+ });
+
+ function ready() {
+ pageLoadedAndVisible.resolve();
+ pageLoadedAndVisible = new Deferred();
+ }
+
+ return new Deferred();
+ })();
+
const nativeJSONParse = window.JSON.parse;
const nativeXMLHttpRequestOpen = XMLHttpRequest.prototype.open;
@@ -218,6 +268,96 @@
return native;
})();
+ function getYtcfgValue(value) {var _window$ytcfg;
+ return (_window$ytcfg = window.ytcfg) === null || _window$ytcfg === void 0 ? void 0 : _window$ytcfg.get(value);
+ }
+
+ function isUserLoggedIn() {
+ // Session Cookie exists?
+ if (!getSidCookie()) return false;
+
+ // LOGGED_IN doesn't exist on embedded page, use DELEGATED_SESSION_ID as fallback
+ if (typeof getYtcfgValue('LOGGED_IN') === 'boolean') return getYtcfgValue('LOGGED_IN');
+ if (typeof getYtcfgValue('DELEGATED_SESSION_ID') === 'string') return true;
+
+ return false;
+ }
+
+ function getPlayer$1(payload, requiresAuth) {
+ return sendInnertubeRequest('v1/player', payload, requiresAuth);
+ }
+
+ function getNext(payload) {
+ return sendInnertubeRequest('v1/next', payload, false);
+ }
+
+ function getSignatureTimestamp() {
+ return (
+ getYtcfgValue('STS') ||
+ (() => {var _document$querySelect;
+ // STS is missing on embedded player. Retrieve from player base script as fallback...
+ const playerBaseJsPath = (_document$querySelect = document.querySelector('script[src*="/base.js"]')) === null || _document$querySelect === void 0 ? void 0 : _document$querySelect.src;
+
+ if (!playerBaseJsPath) return;
+
+ const xmlhttp = new XMLHttpRequest();
+ xmlhttp.open('GET', playerBaseJsPath, false);
+ xmlhttp.send(null);
+
+ return parseInt(xmlhttp.responseText.match(/signatureTimestamp:([0-9]*)/)[1]);
+ })());
+
+ }
+
+ function sendInnertubeRequest(endpoint, payload, useAuth) {
+ const xmlhttp = new XMLHttpRequest();
+ xmlhttp.open('POST', `/youtubei/${endpoint}?key=${getYtcfgValue('INNERTUBE_API_KEY')}`, false);
+ if (useAuth && isUserLoggedIn()) {
+ xmlhttp.withCredentials = true;
+ xmlhttp.setRequestHeader('Authorization', generateSidBasedAuth());
+ }
+ xmlhttp.send(JSON.stringify(payload));
+ return nativeJSONParse(xmlhttp.responseText);
+ }
+
+ function getSidCookie() {
+ return getCookie('SAPISID') || getCookie('__Secure-3PAPISID');
+ }
+
+ function generateSidBasedAuth() {
+ const sid = getSidCookie();
+ const timestamp = Math.floor(new Date().getTime() / 1000);
+ const input = timestamp + ' ' + sid + ' ' + location.origin;
+ const hash = generateSha1Hash(input);
+ return `SAPISIDHASH ${timestamp}_${hash}`;
+ }
+
+ const logPrefix = '%cSimple-YouTube-Age-Restriction-Bypass:';
+ const logPrefixStyle = 'background-color: #1e5c85; color: #fff; font-size: 1.2em;';
+ const logSuffix = '\uD83D\uDC1E You can report bugs at: https://github.com/zerodytrash/Simple-YouTube-Age-Restriction-Bypass/issues';
+
+ function error(err, msg) {
+ console.error(logPrefix, logPrefixStyle, msg, err, getYtcfgDebugString(), '\n\n', logSuffix);
+ }
+
+ function info(msg) {
+ console.info(logPrefix, logPrefixStyle, msg);
+ }
+
+ function getYtcfgDebugString() {
+ try {
+ return (
+ `InnertubeConfig: ` +
+ `innertubeApiKey: ${getYtcfgValue('INNERTUBE_API_KEY')} ` +
+ `innertubeClientName: ${getYtcfgValue('INNERTUBE_CLIENT_NAME')} ` +
+ `innertubeClientVersion: ${getYtcfgValue('INNERTUBE_CLIENT_VERSION')} ` +
+ `loggedIn: ${getYtcfgValue('LOGGED_IN')} `);
+
+ } catch (err) {
+ return `Failed to access config: ${err}`;
+ }
+ }
+
let wrappedPlayerResponse;
let wrappedNextResponse;
@@ -228,7 +368,7 @@
// Just for compatibility: Intercept (re-)definitions on YouTube's initial player response property to chain setter/getter from other extensions by hijacking the Object.defineProperty function
Object.defineProperty = (obj, prop, descriptor) => {
if (obj === window && PLAYER_RESPONSE_ALIASES.includes(prop)) {
- console.info("Another extension tries to redefine '" + prop + "' (probably an AdBlock extension). Chain it...");
+ info("Another extension tries to redefine '" + prop + "' (probably an AdBlock extension). Chain it...");
if (descriptor !== null && descriptor !== void 0 && descriptor.set) chainedPlayerSetter = descriptor.set;
if (descriptor !== null && descriptor !== void 0 && descriptor.get) chainedPlayerGetter = descriptor.get;
@@ -341,244 +481,171 @@
return hasGcrFlag && wasUnlockedByAccountProxy;
}
- function getYtcfgValue(value) {var _window$ytcfg;
- return (_window$ytcfg = window.ytcfg) === null || _window$ytcfg === void 0 ? void 0 : _window$ytcfg.get(value);
- }
-
- function isUserLoggedIn() {
- // Session Cookie exists?
- if (!getSidCookie()) return false;
-
- // LOGGED_IN doesn't exist on embedded page, use DELEGATED_SESSION_ID as fallback
- if (typeof getYtcfgValue('LOGGED_IN') === 'boolean') return getYtcfgValue('LOGGED_IN');
- if (typeof getYtcfgValue('DELEGATED_SESSION_ID') === 'string') return true;
-
- return false;
- }
-
- function getPlayer$1(videoId, clientConfig, useAuth) {
- const payload = getInnertubeEmbedPayload(videoId, clientConfig);
- return sendInnertubeRequest('v1/player', payload, useAuth);
- }
-
- function getNext(videoId, clientConfig, playlistId, playlistIndex) {
- const payload = getInnertubeEmbedPayload(videoId, clientConfig, playlistId, playlistIndex);
- return sendInnertubeRequest('v1/next', payload, false);
+ function isSearchResult(parsedData) {var _parsedData$contents6, _parsedData$contents7, _parsedData$contents8, _parsedData$onRespons, _parsedData$onRespons2, _parsedData$onRespons3;
+ return (
+ typeof (parsedData === null || parsedData === void 0 ? void 0 : (_parsedData$contents6 = parsedData.contents) === null || _parsedData$contents6 === void 0 ? void 0 : _parsedData$contents6.twoColumnSearchResultsRenderer) === 'object' || // Desktop initial results
+ (parsedData === null || parsedData === void 0 ? void 0 : (_parsedData$contents7 = parsedData.contents) === null || _parsedData$contents7 === void 0 ? void 0 : (_parsedData$contents8 = _parsedData$contents7.sectionListRenderer) === null || _parsedData$contents8 === void 0 ? void 0 : _parsedData$contents8.targetId) === 'search-feed' || // Mobile initial results
+ (parsedData === null || parsedData === void 0 ? void 0 : (_parsedData$onRespons = parsedData.onResponseReceivedCommands) === null || _parsedData$onRespons === void 0 ? void 0 : (_parsedData$onRespons2 = _parsedData$onRespons.find((x) => x.appendContinuationItemsAction)) === null || _parsedData$onRespons2 === void 0 ? void 0 : (_parsedData$onRespons3 = _parsedData$onRespons2.appendContinuationItemsAction) === null || _parsedData$onRespons3 === void 0 ? void 0 : _parsedData$onRespons3.targetId) === 'search-feed' // Desktop & Mobile scroll continuation
+ );
}
- function getMainPageClientName() {
- // replace embedded client with YouTube's main page client (e.g. WEB_EMBEDDED_PLAYER => WEB)
- return getYtcfgValue('INNERTUBE_CLIENT_NAME').replace('_EMBEDDED_PLAYER', '');
+ function getGoogleVideoUrl(originalUrl) {
+ return VIDEO_PROXY_SERVER_HOST + '/direct/' + btoa(originalUrl);
}
- function getSignatureTimestamp() {
- return (
- getYtcfgValue('STS') ||
- (() => {var _document$querySelect;
- // STS is missing on embedded player. Retrieve from player base script as fallback...
- const playerBaseJsPath = (_document$querySelect = document.querySelector('script[src*="/base.js"]')) === null || _document$querySelect === void 0 ? void 0 : _document$querySelect.src;
+ function getPlayer(payload) {
+ const queryParams = new URLSearchParams(payload).toString();
- if (!playerBaseJsPath) return;
+ const proxyUrl = ACCOUNT_PROXY_SERVER_HOST + '/getPlayer?' + queryParams;
- const xmlhttp = new XMLHttpRequest();
- xmlhttp.open('GET', playerBaseJsPath, false);
- xmlhttp.send(null);
+ try {
+ const xmlhttp = new XMLHttpRequest();
+ xmlhttp.open('GET', proxyUrl, false);
+ xmlhttp.send(null);
- return parseInt(xmlhttp.responseText.match(/signatureTimestamp:([0-9]*)/)[1]);
- })());
+ const playerResponse = nativeJSONParse(xmlhttp.responseText);
- }
+ // mark request as 'proxied'
+ playerResponse.proxied = true;
- function sendInnertubeRequest(endpoint, payload, useAuth) {
- const xmlhttp = new XMLHttpRequest();
- xmlhttp.open('POST', `/youtubei/${endpoint}?key=${getYtcfgValue('INNERTUBE_API_KEY')}`, false);
- if (useAuth && isUserLoggedIn()) {
- xmlhttp.withCredentials = true;
- xmlhttp.setRequestHeader('Authorization', generateSidBasedAuth());
+ return playerResponse;
+ } catch (err) {
+ error(err);
+ return { errorMessage: 'Proxy Connection failed' };
}
- xmlhttp.send(JSON.stringify(payload));
- return nativeJSONParse(xmlhttp.responseText);
}
- function getInnertubeEmbedPayload(videoId, clientConfig, playlistId, playlistIndex) {
- return {
- context: {
- client: {
- ...getYtcfgValue('INNERTUBE_CONTEXT').client,
- ...{ clientName: getMainPageClientName() },
- ...(clientConfig || {}) },
-
- thirdParty: {
- embedUrl: 'https://www.youtube.com/' } },
-
-
- playbackContext: {
- contentPlaybackContext: {
- signatureTimestamp: getSignatureTimestamp() } },
+ var tDesktop = "\r\n";
+ var tMobile = "\r\n \r\n \r\n \r\n\r\n";
- videoId,
- playlistId,
- playlistIndex };
-
- }
-
- function getSidCookie() {
- return getCookie('SAPISID') || getCookie('__Secure-3PAPISID');
- }
-
- function generateSidBasedAuth() {
- const sid = getSidCookie();
- const timestamp = Math.floor(new Date().getTime() / 1000);
- const input = timestamp + ' ' + sid + ' ' + location.origin;
- const hash = generateSha1Hash(input);
- return `SAPISIDHASH ${timestamp}_${hash}`;
- }
+ const template = isDesktop ? tDesktop : tMobile;
- const logPrefix = 'Simple-YouTube-Age-Restriction-Bypass:';
- const logSuffix = 'You can report bugs at: https://github.com/zerodytrash/Simple-YouTube-Age-Restriction-Bypass/issues';
+ const nToastContainer = createElement('div', { id: 'toast-container', innerHTML: template });
+ const nToast = nToastContainer.querySelector(':scope > *');
- function error(err, msg) {
- console.error(logPrefix, msg, err, getYtcfgDebugString(), logSuffix);
- }
+ document.documentElement.append(nToastContainer);
- function info(msg) {
- console.info(logPrefix, msg);
+ if (!isDesktop) {
+ nToast.nMessage = nToast.querySelector('.notification-action-response-text');
+ nToast.show = (message) => {
+ nToast.nMessage.innerText = message;
+ nToast.setAttribute('dir', 'in');
+ setTimeout(() => {
+ nToast.setAttribute('dir', 'out');
+ }, nToast.duration + 225);
+ };
}
- function getYtcfgDebugString() {
- try {
- return (
- `InnertubeConfig: ` +
- `innertubeApiKey: ${getYtcfgValue('INNERTUBE_API_KEY')} ` +
- `innertubeClientName: ${getYtcfgValue('INNERTUBE_CLIENT_NAME')} ` +
- `innertubeClientVersion: ${getYtcfgValue('INNERTUBE_CLIENT_VERSION')} ` +
- `loggedIn: ${getYtcfgValue('LOGGED_IN')} `);
+ async function show(message) {let duration = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 5;
- } catch (err) {
- return `Failed to access config: ${err}`;
- }
- }
+ await pageLoadedAndVisible;
- function getGoogleVideoUrl(originalUrl, proxyHost) {
- return proxyHost + '/direct/' + btoa(originalUrl);
+ nToast.duration = duration * 1000;
+ nToast.show(message);
}
- function getPlayer(videoId, reason) {
- const queryParams = new URLSearchParams({
- videoId,
- reason,
- clientName: getMainPageClientName(),
- clientVersion: getYtcfgValue('INNERTUBE_CLIENT_VERSION'),
- signatureTimestamp: getSignatureTimestamp(),
- isEmbed: +isEmbed }).
- toString();
-
- const proxyUrl = ACCOUNT_PROXY_SERVER_HOST + '/getPlayer?' + queryParams;
-
- const xmlhttp = new XMLHttpRequest();
- xmlhttp.open('GET', proxyUrl, false);
- xmlhttp.send(null);
+ var Toast = { show };
- const playerResponse = nativeJSONParse(xmlhttp.responseText);
+ const messagesMap = {
+ success: 'Age-restricted video successfully unlocked!',
+ fail: 'Unable to unlock this video 🙁 - More information in the developer console' };
- // mark request as 'proxied'
- playerResponse.proxied = true;
- return playerResponse;
- }
+ let lastProxiedGoogleVideoUrlParams;
+ let responseCache = {};
- var tDesktop = "\n";
+ function getUnlockStrategies(playerResponse) {var _playerResponse$video, _playerResponse$playa, _playerResponse$previ;
+ const videoId = ((_playerResponse$video = playerResponse.videoDetails) === null || _playerResponse$video === void 0 ? void 0 : _playerResponse$video.videoId) || getYtcfgValue('PLAYER_VARS').video_id;
+ const reason = ((_playerResponse$playa = playerResponse.playabilityStatus) === null || _playerResponse$playa === void 0 ? void 0 : _playerResponse$playa.status) || ((_playerResponse$previ = playerResponse.previewPlayabilityStatus) === null || _playerResponse$previ === void 0 ? void 0 : _playerResponse$previ.status);
+ const clientName = isEmbed || isDesktop ? 'WEB' : 'MWEB';
+ const clientVersion = getYtcfgValue('INNERTUBE_CLIENT_VERSION');
+ const signatureTimestamp = getSignatureTimestamp();
- var tMobile = "\n \n \n \n\n";
+ return [
+ // Strategy 1: Retrieve the video info by using a age-gate bypass for the innertube API
+ // Source: https://github.com/yt-dlp/yt-dlp/issues/574#issuecomment-887171136
+ {
+ name: 'Embed',
+ requiresAuth: false,
+ payload: {
+ context: {
+ client: {
+ clientName,
+ clientVersion,
+ clientScreen: 'EMBED' },
- const pageLoad = new Deferred();
- const pageLoadEventName = isDesktop ? 'yt-navigate-finish' : 'state-navigateend';
+ thirdParty: {
+ embedUrl: 'https://www.youtube.com/' } },
- const template = isDesktop ? tDesktop : tMobile;
- const nNotificationWrapper = createElement('div', { id: 'notification-wrapper', innerHTML: template });
- const nNotification = nNotificationWrapper.querySelector(':scope > *');
- const nMobileText = !isDesktop && nNotification.querySelector('.notification-action-response-text');
+ playbackContext: {
+ contentPlaybackContext: {
+ signatureTimestamp } },
- window.addEventListener(pageLoadEventName, init, { once: true });
- function init() {
- document.body.append(nNotificationWrapper);
- pageLoad.resolve();
- }
+ videoId },
- function show(message, duration = 5) {
+ getPlayer: getPlayer$1 },
- pageLoad.then(_show);
+ // Strategy 2: Retrieve the video info by using the WEB_CREATOR client in combination with user authentication
+ // See https://github.com/yt-dlp/yt-dlp/pull/600
+ {
+ name: 'Creator + Auth',
+ requiresAuth: true,
+ payload: {
+ context: {
+ client: {
+ clientName: 'WEB_CREATOR',
+ clientVersion: '1.20210909.07.00',
+ thirdParty: {
+ embedUrl: 'https://www.youtube.com/' } } },
- function _show() {
- const _duration = duration * 1000;
- if (isDesktop) {
- nNotification.duration = _duration;
- nNotification.show(message);
- } else {
- nMobileText.innerText = message;
- nNotification.setAttribute('dir', 'in');
- setTimeout(() => {
- nNotification.setAttribute('dir', 'out');
- }, _duration + 225);
- }
- }
- }
- var Notification = { show };
- const messagesMap = {
- success: 'Age-restricted video successfully unlocked!',
- fail: 'Unable to unlock this video 🙁 - More information in the developer console' };
+ playbackContext: {
+ contentPlaybackContext: {
+ signatureTimestamp } },
- const unlockStrategies = [
- // Strategy 1: Retrieve the video info by using a age-gate bypass for the innertube API
- // Source: https://github.com/yt-dlp/yt-dlp/issues/574#issuecomment-887171136
- {
- name: 'Innertube Embed',
- requireAuth: false,
- fn: (videoId) => getPlayer$1(videoId, { clientScreen: 'EMBED' }, false) },
+ videoId },
- // Strategy 2: Retrieve the video info by using the WEB_CREATOR client in combination with user authentication
- // See https://github.com/yt-dlp/yt-dlp/pull/600
- {
- name: 'Innertube Creator + Auth',
- requireAuth: true,
- fn: (videoId) => getPlayer$1(videoId, { clientName: 'WEB_CREATOR', clientVersion: '1.20210909.07.00' }, true) },
+ getPlayer: getPlayer$1 },
- // Strategy 3: Retrieve the video info from an account proxy server.
- // See https://github.com/zerodytrash/Simple-YouTube-Age-Restriction-Bypass/tree/main/account-proxy
- {
- name: 'Account Proxy',
- requireAuth: false,
- fn: (videoId, reason) => getPlayer(videoId, reason) }];
+ // Strategy 3: Retrieve the video info from an account proxy server.
+ // See https://github.com/zerodytrash/Simple-YouTube-Age-Restriction-Bypass/tree/main/account-proxy
+ {
+ name: 'Account Proxy',
+ requireAuth: false,
+ payload: {
+ videoId,
+ reason,
+ clientName,
+ clientVersion,
+ signatureTimestamp,
+ isEmbed: +isEmbed },
+ getPlayer: getPlayer }];
- let lastProxiedGoogleVideoUrlParams;
- let responseCache = {};
+ }
function getLastProxiedGoogleVideoId() {var _lastProxiedGoogleVid;
return (_lastProxiedGoogleVid = lastProxiedGoogleVideoUrlParams) === null || _lastProxiedGoogleVid === void 0 ? void 0 : _lastProxiedGoogleVid.get('id');
}
- function unlockPlayerResponse(playerResponse) {var _playerResponse$video, _playerResponse$playa, _playerResponse$previ, _unlockedPlayerRespon, _unlockedPlayerRespon3;
- const videoId = ((_playerResponse$video = playerResponse.videoDetails) === null || _playerResponse$video === void 0 ? void 0 : _playerResponse$video.videoId) || getYtcfgValue('PLAYER_VARS').video_id;
- const reason = ((_playerResponse$playa = playerResponse.playabilityStatus) === null || _playerResponse$playa === void 0 ? void 0 : _playerResponse$playa.status) || ((_playerResponse$previ = playerResponse.previewPlayabilityStatus) === null || _playerResponse$previ === void 0 ? void 0 : _playerResponse$previ.status);
- const unlockedPlayerResponse = getUnlockedPlayerResponse(videoId, reason);
+ function unlockPlayerResponse(playerResponse) {var _unlockedPlayerRespon, _unlockedPlayerRespon3;
+ const unlockedPlayerResponse = getUnlockedPlayerResponse(playerResponse);
// account proxy error?
if (unlockedPlayerResponse.errorMessage) {
- Notification.show(`${messagesMap.fail} (ProxyError)`, 10);
+ Toast.show(`${messagesMap.fail} (ProxyError)`, 10);
throw new Error(`Player Unlock Failed, Proxy Error Message: ${unlockedPlayerResponse.errorMessage}`);
}
// check if the unlocked response isn't playable
if (((_unlockedPlayerRespon = unlockedPlayerResponse.playabilityStatus) === null || _unlockedPlayerRespon === void 0 ? void 0 : _unlockedPlayerRespon.status) !== 'OK') {var _unlockedPlayerRespon2;
- Notification.show(`${messagesMap.fail} (PlayabilityError)`, 10);
+ Toast.show(`${messagesMap.fail} (PlayabilityError)`, 10);
throw new Error(`Player Unlock Failed, playabilityStatus: ${(_unlockedPlayerRespon2 = unlockedPlayerResponse.playabilityStatus) === null || _unlockedPlayerRespon2 === void 0 ? void 0 : _unlockedPlayerRespon2.status}`);
}
@@ -598,35 +665,54 @@
// Transfer all unlocked properties to the original player response
Object.assign(playerResponse, unlockedPlayerResponse);
- Notification.show(messagesMap.success);
+ Toast.show(messagesMap.success);
}
- function getUnlockedPlayerResponse(videoId, reason) {
+ function getUnlockedPlayerResponse(playerResponse) {var _playerResponse$video2;
+ const videoId = ((_playerResponse$video2 = playerResponse.videoDetails) === null || _playerResponse$video2 === void 0 ? void 0 : _playerResponse$video2.videoId) || getYtcfgValue('PLAYER_VARS').video_id;
+
// Check if response is cached
- if (responseCache.videoId === videoId) return responseCache.playerResponse;
+ if (responseCache.videoId === videoId) return responseCache.unlockedPlayerResponse;
- let playerResponse;
+ const unlockStrategies = getUnlockStrategies(playerResponse);
- unlockStrategies.every((strategy, index) => {var _playerResponse, _playerResponse$playa2;
- if (strategy.requireAuth && !isUserLoggedIn()) return true;
+ let unlockedPlayerResponse;
+
+ // Try every strategy until one of them works
+ unlockStrategies.every((strategy, index) => {var _unlockedPlayerRespon6, _unlockedPlayerRespon7;
+ // Skip strategy if authentication is required and the user is not logged in
+ if (strategy.requiresAuth && !isUserLoggedIn()) return true;
info(`Trying Unlock Method #${index + 1} (${strategy.name})`);
- playerResponse = strategy.fn(videoId, reason);
- return ((_playerResponse = playerResponse) === null || _playerResponse === void 0 ? void 0 : (_playerResponse$playa2 = _playerResponse.playabilityStatus) === null || _playerResponse$playa2 === void 0 ? void 0 : _playerResponse$playa2.status) !== 'OK';
+ unlockedPlayerResponse = strategy.getPlayer(strategy.payload, strategy.requiresAuth);
+
+ return ((_unlockedPlayerRespon6 = unlockedPlayerResponse) === null || _unlockedPlayerRespon6 === void 0 ? void 0 : (_unlockedPlayerRespon7 = _unlockedPlayerRespon6.playabilityStatus) === null || _unlockedPlayerRespon7 === void 0 ? void 0 : _unlockedPlayerRespon7.status) !== 'OK';
});
- // Cache response
- responseCache = { videoId, playerResponse };
+ // Cache response to prevent a flood of requests in case youtube processes a blocked response mutiple times.
+ responseCache = { videoId, unlockedPlayerResponse };
- return playerResponse;
+ return unlockedPlayerResponse;
}
function unlockNextResponse(originalNextResponse) {
- info('Trying Sidebar Unlock Method (Innertube Embed)');
+ info('Trying sidebar unlock');
+
+ const { videoId } = originalNextResponse.currentVideoEndpoint.watchEndpoint;
+ const { clientName, clientVersion } = getYtcfgValue('INNERTUBE_CONTEXT').client;
+ const payload = {
+ context: {
+ client: {
+ clientName,
+ clientVersion,
+ clientScreen: 'EMBED' } },
- const { videoId, playlistId, index: playlistIndex } = originalNextResponse.currentVideoEndpoint.watchEndpoint;
- const unlockedNextResponse = getNext(videoId, { clientScreen: 'EMBED' }, playlistId, playlistIndex);
+
+ videoId };
+
+
+ const unlockedNextResponse = getNext(payload);
// check if the sidebar of the unlocked response is still empty
if (isWatchNextSidebarEmpty(unlockedNextResponse)) {
@@ -674,10 +760,20 @@
originalStructuredDescriptionContentRenderer.expandableVideoDescriptionBodyRenderer = unlockedStructuredDescriptionContentRenderer.expandableVideoDescriptionBodyRenderer;
}
+ function processThumbnails(responseObject) {
+ const thumbnails = findNestedObjectsByAttributeNames(responseObject, ['url', 'height']).filter((x) => typeof x.url === 'string' && x.url.indexOf('https://i.ytimg.com/') === 0);
+ const blurredThumbnails = thumbnails.filter((thumbnail) => THUMBNAIL_BLURRED_SQPS.some((sqp) => thumbnail.url.includes(sqp)));
+
+ // Simply remove all URL parameters to eliminate the blur effect.
+ blurredThumbnails.forEach((x) => x.url = x.url.split('?')[0]);
+
+ info(blurredThumbnails.length + '/' + thumbnails.length + ' thumbnails detected as blurred.');
+ }
+
try {
- attachInitialDataInterceptor(checkAndUnlock);
attachJsonInterceptor(checkAndUnlock);
attachXhrOpenInterceptor(onXhrOpenCalled);
+ attachInitialDataInterceptor(checkAndUnlock);
} catch (err) {
error(err, 'Error while attaching data interceptors');
}
@@ -708,6 +804,15 @@
error(err, 'Video or sidebar unlock failed');
}
+ try {
+ // Unlock blurry video thumbnails
+ if (isSearchResult(ytData) || isSearchResult(ytData.response)) {
+ processThumbnails(ytData);
+ }
+ } catch (err) {
+ error(err, 'Thumbnail unlock failed');
+ }
+
return ytData;
}
@@ -726,7 +831,7 @@
get: () => false });
- return getGoogleVideoUrl(url.toString(), VIDEO_PROXY_SERVER_HOST);
+ return getGoogleVideoUrl(url.toString());
}
}
diff --git a/package.json b/package.json
index 76111cc..6c8ac20 100644
--- a/package.json
+++ b/package.json
@@ -2,7 +2,7 @@
"type": "module",
"name": "simple-youtube-age-restriction-bypass",
"description": "A simple userscript to bypass YouTube's age verification and watch age restricted videos without having to sign in.",
- "version": "2.2.2",
+ "version": "2.3.1",
"repository": {
"type": "git",
"url": "git+https://github.com/zerodytrash/Simple-YouTube-Age-Restriction-Bypass.git"