lol politique
- 14 098 réponses
- 171 participants
- 681 446 vues
- 132 followers
Will Zégal
75788
Will Zégal
Membre depuis 22 ans
Sujet de la discussion Posté le 24/02/2017 à 13:11:54lol politique
En cette période électorale française est entrée en vigueur la Charte du pub spéciale élections.
Rappel du principe : on se bouffe de la politique et des politiciens partout à longueur de journée. Pour éviter de casser les noix sur AF avec ceux qui s'en tapent ou qui saturent, tout ce qui est lié à la politique est prohibé en dehors des sujets dédiés.
Donc, voici un sujet pour rigoler sur la politique ou les politiques : images, citations, etc.
Par contre, on ne discute pas de politique ici. Pour ça, il y a le café du commerce.
P.S. : si quelqu'un a une idée de titre moins pourri pour ce sujet...
P.P.S : le "P.S." si dessus signifie évidemment "Post Scriptum"
Rappel du principe : on se bouffe de la politique et des politiciens partout à longueur de journée. Pour éviter de casser les noix sur AF avec ceux qui s'en tapent ou qui saturent, tout ce qui est lié à la politique est prohibé en dehors des sujets dédiés.
Donc, voici un sujet pour rigoler sur la politique ou les politiques : images, citations, etc.
Par contre, on ne discute pas de politique ici. Pour ça, il y a le café du commerce.
P.S. : si quelqu'un a une idée de titre moins pourri pour ce sujet...
P.P.S : le "P.S." si dessus signifie évidemment "Post Scriptum"
Anonyme
12661 Posté le 14/02/2022 à 16:08:32
Qui a dit : Segolene n'a qu'un seul défaut, c'est son ex mari ?
j-master
33965
Vie après AF ?
Membre depuis 19 ans
12662 Posté le 14/02/2022 à 16:21:00
Arnaud Montebourg
"L'Homme est la nature prenant conscience d'elle même." - Elisée Reclus
darinze
5134
Je poste, donc je suis
Membre depuis 3 ans
12663 Posté le 14/02/2022 à 16:25:40
Citation de Will :
Voilà, ça c'est lol Je trouve moins drôle quand on passe X posts à débattre de si une image / blague / remarque a sa place ou pas dans un topic ou est drôle ou pas.
T'es pas obligé d'écouter toutes les conneries de DaRinze.
BusterK
13302
Drogué·e à l’AFéine
Membre depuis 16 ans
12664 Posté le 14/02/2022 à 16:31:44
Dis donc dada, tu n’aurais pas la pudibonderie flexible ?
"And in the end, the love you take is equal to the love you make"
Dr Pouet
52037
Membre d’honneur
Membre depuis 20 ans
12666 Posté le 14/02/2022 à 21:28:21
BusterK
13302
Drogué·e à l’AFéine
Membre depuis 16 ans
12667 Posté le 14/02/2022 à 22:24:12
Reviens Darinze, je ne voulais pas te froisser
"And in the end, the love you take is equal to the love you make"
darinze
5134
Je poste, donc je suis
Membre depuis 3 ans
12668 Posté le 14/02/2022 à 22:44:51
T'inquiète Polaroil, je suis pas froissé, je suis plat comme un limande
T'es pas obligé d'écouter toutes les conneries de DaRinze.
[ Dernière édition du message le 14/02/2022 à 22:45:10 ]
El Migo
24005
Vie après AF ?
Membre depuis 17 ans
12669 Posté le 16/02/2022 à 13:18:21
VOTEZ C'EST IMPORTANT
En cadeau, un script pour bypasser les contrôles de limite d'âge sur YT, à intégrer à Tampermonkey (par exemple)
En cadeau, un script pour bypasser les contrôles de limite d'âge sur YT, à intégrer à Tampermonkey (par exemple)
Spoiler - Cliquer ici pour lire la suite
// ==UserScript==
// @name Simple YouTube Age Restriction Bypass
// @description Watch age restricted videos on YouTube without login and without age verification
// @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.1.4
// @author Zerody (https://github.com/zerodytrash)
// @namespace https://github.com/zerodytrash/Simple-YouTube-Age-Restriction-Bypass/
// @updateURL https://github.com/zerodytrash/Simple-YouTube-Age-Restriction-Bypass/raw/main/Simple-YouTube-Age-Restriction-Bypass.user.js
// @downloadURL https://github.com/zerodytrash/Simple-YouTube-Age-Restriction-Bypass/raw/main/Simple-YouTube-Age-Restriction-Bypass.user.js
// @supportURL https://github.com/zerodytrash/Simple-YouTube-Age-Restriction-Bypass/issues
// @license MIT
// @match https://www.youtube.com/*
// @match https://m.youtube.com/*
// @grant none
// @run-at document-start
// @compatible chrome Chrome + Tampermonkey or Violentmonkey
// @compatible firefox Firefox + Greasemonkey or Tampermonkey or Violentmonkey
// @compatible opera Opera + Tampermonkey or Violentmonkey
// @compatible edge Edge + Tampermonkey or Violentmonkey
// @compatible safari Safari + Tampermonkey or Violentmonkey
// ==/UserScript==
const initUnlocker = () => {
const UNLOCKABLE_PLAYER_STATES = ["AGE_VERIFICATION_REQUIRED", "AGE_CHECK_REQUIRED", "LOGIN_REQUIRED"];
const PLAYER_RESPONSE_ALIASES = ["ytInitialPlayerResponse", "playerResponse"];
// YouTube API config (Innertube).
// The actual values will be determined later from the global ytcfg variable => setInnertubeConfigFromYtcfg()
const INNERTUBE_CONFIG = {
INNERTUBE_API_KEY: "AIzaSyAO_FJ2SlqU8Q4STEHLGCilw_Y9_11qcW8",
INNERTUBE_CLIENT_NAME: "WEB",
INNERTUBE_CLIENT_VERSION: "2.20210721.00.00",
INNERTUBE_CONTEXT: {},
STS: 18834, // signatureTimestamp (relevant for the cipher functions)
LOGGED_IN: false,
};
// The following proxies are currently used as fallback if the innertube age-gate bypass doesn't work...
// You can host your own account proxy instance. See https://github.com/zerodytrash/Simple-YouTube-Age-Restriction-Bypass/tree/main/account-proxy
const ACCOUNT_PROXY_SERVER_HOST = "https://youtube-proxy.zerody.one";
const VIDEO_PROXY_SERVER_HOST = "https://phx.4everproxy.com";
const ENABLE_UNLOCK_NOTIFICATION = true;
const nativeParse = window.JSON.parse; // Backup the original parse function
const nativeDefineProperty = getNativeDefineProperty(); // Backup the original defineProperty function to intercept setter & getter on the ytInitialPlayerResponse
const nativeXmlHttpOpen = XMLHttpRequest.prototype.open;
// Just for compatibility: Backup original getter/setter for 'ytInitialPlayerResponse', defined by other extensions like AdBlock
let { get: chainedPlayerGetter, set: chainedPlayerSetter } = Object.getOwnPropertyDescriptor(window, "ytInitialPlayerResponse") || {};
let wrappedPlayerResponse;
let wrappedNextResponse;
let lastProxiedGoogleVideoUrlParams;
let responseCache = {};
const Deferred = function () {
return Object.assign(new Promise((resolve, reject) => {
this.resolve = resolve;
this.reject = reject;
}), this);
};
const Notification = (() => {
const isPolymer = !!window.Polymer;
const pageLoad = new Deferred();
const pageLoadEventName = isPolymer ? 'yt-navigate-finish' : 'state-navigateend';
const node = isPolymer
? createElement('tp-yt-paper-toast')
: createElement('c3-toast', { innerHTML: '<ytm-notification-action-renderer><div class="notification-action-response-text"></div></ytm-notification-action-renderer>' });
const nMobileText = !isPolymer && node.querySelector('.notification-action-response-text');
window.addEventListener(pageLoadEventName, init, { once: true });
function init() {
document.body.append(node);
pageLoad.resolve();
}
function show(message, duration = 5) {
if (!ENABLE_UNLOCK_NOTIFICATION) return;
pageLoad.then(_show);
function _show() {
if (isPolymer) {
node.duration = duration * 1000;
node.show(message);
} else {
nMobileText.innerHTML = message;
node.setAttribute('dir', 'in');
setTimeout(() => {
node.setAttribute('dir', 'out');
}, duration * 1000 + 225);
}
}
}
return { show };
})();
// 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...");
if (descriptor?.set) chainedPlayerSetter = descriptor.set;
if (descriptor?.get) chainedPlayerGetter = descriptor.get;
} else {
nativeDefineProperty(obj, prop, descriptor);
}
};
// Redefine 'ytInitialPlayerResponse' to inspect and modify the initial player response as soon as the variable is set on page load
nativeDefineProperty(window, "ytInitialPlayerResponse", {
set: (playerResponse) => {
// prevent recursive setter calls by ignoring unchanged data (this fixes a problem caused by Brave browser shield)
if (playerResponse === wrappedPlayerResponse) return;
wrappedPlayerResponse = inspectJsonData(playerResponse);
if (typeof chainedPlayerSetter === "function") chainedPlayerSetter(wrappedPlayerResponse);
},
get: () => {
if (typeof chainedPlayerGetter === "function") try { return chainedPlayerGetter() } catch (err) { };
return wrappedPlayerResponse || {};
},
configurable: true,
});
// Also redefine 'ytInitialData' for the initial next/sidebar response
nativeDefineProperty(window, "ytInitialData", {
set: (nextResponse) => { wrappedNextResponse = inspectJsonData(nextResponse); },
get: () => wrappedNextResponse,
configurable: true,
});
// Intercept XMLHttpRequest.open to rewrite video URL's (sometimes required)
XMLHttpRequest.prototype.open = function () {
if (arguments.length > 1 && typeof arguments[1] === "string" && arguments[1].indexOf("https://") === 0) {
const method = arguments[0];
const url = new URL(arguments[1]);
const urlParams = new URLSearchParams(url.search);
// If the account proxy was used to retieve the video info, the following applies:
// some video files (mostly music videos) can only be accessed from IPs in the same country as the innertube api request (/youtubei/v1/player) was made.
// to get around this, the googlevideo URL will be replaced with a web-proxy URL in the same country (US).
// this is only required if the "gcr=[countrycode]" flag is set in the googlevideo-url...
function isGoogleVideo() {
return method === "GET" && url.host.indexOf(".googlevideo.com") > 0;
}
function hasGcrFlag() {
return urlParams.get("gcr") !== null;
}
function isUnlockedByAccountProxy() {
return urlParams.get("id") !== null && urlParams.get("id") === lastProxiedGoogleVideoUrlParams?.get("id");
}
if (VIDEO_PROXY_SERVER_HOST && isGoogleVideo() && hasGcrFlag() && isUnlockedByAccountProxy()) {
// rewrite request URL
arguments[1] = VIDEO_PROXY_SERVER_HOST + "/direct/" + btoa(arguments[1]);
// solve CORS errors by preventing YouTube from enabling the "withCredentials" option (not required for the proxy)
nativeDefineProperty(this, "withCredentials", {
set: () => { },
get: () => false,
});
}
}
return nativeXmlHttpOpen.apply(this, arguments);
};
// Intercept, inspect and modify JSON-based communication to unlock player responses by hijacking the JSON.parse function
window.JSON.parse = (text, reviver) => inspectJsonData(nativeParse(text, reviver));
function inspectJsonData(parsedData) {
// If YouTube does JSON.parse(null) or similar weird things
if (typeof parsedData !== "object" || parsedData === null) return parsedData;
try {
// Unlock #1: Array based in "&pbj=1" AJAX response on any navigation (does not seem to be used anymore)
if (Array.isArray(parsedData)) {
const { playerResponse } = parsedData.find(e => typeof e.playerResponse === "object") || {};
if (playerResponse && isAgeRestricted(playerResponse.playabilityStatus)) {
playerResponseArrayItem.playerResponse = unlockPlayerResponse(playerResponse);
const { response: nextResponse } = parsedData.find(e => typeof e.response === "object") || {};
if (isWatchNextObject(nextResponse) && !isLoggedIn() && isWatchNextSidebarEmpty(nextResponse.contents)) {
nextResponseArrayItem.response = unlockNextResponse(nextResponse);
}
}
}
// Unlock #2: Another JSON-Object containing the 'playerResponse' (seems to be used by m.youtube.com with &pbj=1)
if (parsedData.playerResponse?.playabilityStatus && parsedData.playerResponse?.videoDetails && isAgeRestricted(parsedData.playerResponse.playabilityStatus)) {
parsedData.playerResponse = unlockPlayerResponse(parsedData.playerResponse);
}
// Unlock #3: Initial page data structure and response from the '/youtubei/v1/player' endpoint
if (parsedData.playabilityStatus && parsedData.videoDetails && isAgeRestricted(parsedData.playabilityStatus)) {
parsedData = unlockPlayerResponse(parsedData);
}
// Equivelant of unlock #2 for sidebar/next response
if (isWatchNextObject(parsedData.response) && !isLoggedIn() && isWatchNextSidebarEmpty(parsedData.response.contents)) {
parsedData.response = unlockNextResponse(parsedData.response);
}
// Equivelant of unlock #3 for sidebar/next response
if (isWatchNextObject(parsedData) && !isLoggedIn() && isWatchNextSidebarEmpty(parsedData.contents)) {
parsedData = unlockNextResponse(parsedData)
}
} catch (err) {
console.error("Simple-YouTube-Age-Restriction-Bypass-Error:", err, "You can report bugs at: https://github.com/zerodytrash/Simple-YouTube-Age-Restriction-Bypass/issues");
}
return parsedData;
}
function isAgeRestricted(playabilityStatus) {
if (!playabilityStatus?.status) return false;
return !!playabilityStatus.desktopLegacyAgeGateReason || UNLOCKABLE_PLAYER_STATES.includes(playabilityStatus.status);
}
function isWatchNextObject(parsedData) {
if (!parsedData?.contents || !parsedData?.currentVideoEndpoint?.watchEndpoint?.videoId) return false;
return !!parsedData.contents.twoColumnWatchNextResults || !!parsedData.contents.singleColumnWatchNextResults;
}
function isWatchNextSidebarEmpty(contents) {
const secondaryResults = contents.twoColumnWatchNextResults?.secondaryResults?.secondaryResults;
if (secondaryResults?.results) return false;
// MWEB response layout
const singleColumnWatchNextContents = contents.singleColumnWatchNextResults?.results?.results?.contents;
if (!singleColumnWatchNextContents) return true;
const { itemSectionRenderer } = singleColumnWatchNextContents.find(e => e.itemSectionRenderer?.targetId === "watch-next-feed") || {};
return !!itemSectionRenderer;
}
function isLoggedIn() {
setInnertubeConfigFromYtcfg();
return INNERTUBE_CONFIG.LOGGED_IN;
}
function unlockPlayerResponse(playerResponse) {
const videoId = playerResponse.videoDetails.videoId;
const reason = playerResponse.playabilityStatus?.status;
const unlockedPayerResponse = getUnlockedPlayerResponse(videoId, reason);
// account proxy error?
if (unlockedPayerResponse.errorMessage) {
Notification.show("Unable to unlock this video - More information in the developer console (ProxyError)", 10);
throw new Error(`Unlock Failed, errorMessage:${unlockedPayerResponse.errorMessage}; innertubeApiKey:${INNERTUBE_CONFIG.INNERTUBE_API_KEY}; innertubeClientName:${INNERTUBE_CONFIG.INNERTUBE_CLIENT_NAME}; innertubeClientVersion:${INNERTUBE_CONFIG.INNERTUBE_CLIENT_VERSION}`);
}
// check if the unlocked response isn't playable
if (unlockedPayerResponse.playabilityStatus?.status !== "OK") {
Notification.show(`Unable to unlock this video - More information in the developer console (playabilityStatus: ${unlockedPayerResponse.playabilityStatus?.status})`, 10);
throw new Error(`Unlock Failed, playabilityStatus:${unlockedPayerResponse.playabilityStatus?.status}; innertubeApiKey:${INNERTUBE_CONFIG.INNERTUBE_API_KEY}; innertubeClientName:${INNERTUBE_CONFIG.INNERTUBE_CLIENT_NAME}; innertubeClientVersion:${INNERTUBE_CONFIG.INNERTUBE_CLIENT_VERSION}`);
}
// if the video info was retrieved via proxy, store the URL params from the url- or signatureCipher-attribute to detect later if the requested video files are from this unlock.
// => see isUnlockedByAccountProxy()
if (unlockedPayerResponse.proxied && unlockedPayerResponse.streamingData?.adaptiveFormats) {
const cipherText = unlockedPayerResponse.streamingData.adaptiveFormats.find(x => x.signatureCipher)?.signatureCipher;
const videoUrl = cipherText ? new URLSearchParams(cipherText).get("url") : unlockedPayerResponse.streamingData.adaptiveFormats.find(x => x.url)?.url;
lastProxiedGoogleVideoUrlParams = videoUrl ? new URLSearchParams(new URL(videoUrl).search) : null;
}
Notification.show("Video successfully unlocked!");
return unlockedPayerResponse;
}
function unlockNextResponse(nextResponse) {
const { videoId, playlistId, index: playlistIndex } = nextResponse.currentVideoEndpoint.watchEndpoint;
const unlockedNextResponse = getUnlockedNextResponse(videoId, playlistId, playlistIndex);
// check if the unlocked response's sidebar is still empty
if (isWatchNextSidebarEmpty(unlockedNextResponse.contents)) {
throw new Error(`Sidebar Unlock Failed, innertubeApiKey:${INNERTUBE_CONFIG.INNERTUBE_API_KEY}; innertubeClientName:${INNERTUBE_CONFIG.INNERTUBE_CLIENT_NAME}; innertubeClientVersion:${INNERTUBE_CONFIG.INNERTUBE_CLIENT_VERSION}`);
}
// Transfer WatchNextResults to original response
if (nextResponse.contents?.twoColumnWatchNextResults?.secondaryResults) {
nextResponse.contents.twoColumnWatchNextResults.secondaryResults = unlockedNextResponse?.contents?.twoColumnWatchNextResults?.secondaryResults;
}
// Transfer mobile (MWEB) WatchNextResults to original response
if (nextResponse.contents?.singleColumnWatchNextResults?.results?.results?.contents) {
const unlockedWatchNextFeed = unlockedNextResponse?.contents?.singleColumnWatchNextResults?.results?.results?.contents
?.find(x => x.itemSectionRenderer?.targetId === "watch-next-feed");
if (unlockedWatchNextFeed) nextResponse.contents.singleColumnWatchNextResults.results.results.contents.push(unlockedWatchNextFeed);
}
// Transfer video description to original response
const originalVideoSecondaryInfoRenderer = nextResponse.contents?.twoColumnWatchNextResults?.results?.results?.contents
?.find(x => x.videoSecondaryInfoRenderer)?.videoSecondaryInfoRenderer;
const unlockedVideoSecondaryInfoRenderer = unlockedNextResponse.contents?.twoColumnWatchNextResults?.results?.results?.contents
?.find(x => x.videoSecondaryInfoRenderer)?.videoSecondaryInfoRenderer;
if (originalVideoSecondaryInfoRenderer && unlockedVideoSecondaryInfoRenderer?.description)
originalVideoSecondaryInfoRenderer.description = unlockedVideoSecondaryInfoRenderer.description;
// Transfer mobile (MWEB) video description to original response
const originalStructuredDescriptionContentRenderer = nextResponse.engagementPanels
?.find(x => x.engagementPanelSectionListRenderer)?.engagementPanelSectionListRenderer?.content?.structuredDescriptionContentRenderer?.items
?.find(x => x.expandableVideoDescriptionBodyRenderer);
const unlockedStructuredDescriptionContentRenderer = unlockedNextResponse.engagementPanels
?.find(x => x.engagementPanelSectionListRenderer)?.engagementPanelSectionListRenderer?.content?.structuredDescriptionContentRenderer?.items
?.find(x => x.expandableVideoDescriptionBodyRenderer);
if (originalStructuredDescriptionContentRenderer && unlockedStructuredDescriptionContentRenderer?.expandableVideoDescriptionBodyRenderer)
originalStructuredDescriptionContentRenderer.expandableVideoDescriptionBodyRenderer = unlockedStructuredDescriptionContentRenderer.expandableVideoDescriptionBodyRenderer;
return nextResponse;
}
function getUnlockedPlayerResponse(videoId, reason) {
// Check if response is cached
if (responseCache.videoId === videoId) return responseCache.playerResponse;
setInnertubeConfigFromYtcfg();
let playerResponse = useInnertubeEmbed();
if (playerResponse?.playabilityStatus?.status !== "OK") playerResponse = useProxy();
// Cache response for 10 seconds
responseCache = { videoId, playerResponse };
setTimeout(() => { responseCache = {} }, 10000);
return playerResponse;
// 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
function useInnertubeEmbed() {
console.info("Simple-YouTube-Age-Restriction-Bypass: Trying Unlock Method #1 (Innertube Embed)");
const payload = getInnertubeEmbedPayload(videoId);
const xmlhttp = new XMLHttpRequest();
xmlhttp.open("POST", `/youtubei/v1/player?key=${INNERTUBE_CONFIG.INNERTUBE_API_KEY}`, false); // Synchronous!!!
xmlhttp.send(JSON.stringify(payload));
return nativeParse(xmlhttp.responseText);
}
// Strategy 2: Retrieve the video info from an account proxy server.
// See https://github.com/zerodytrash/Simple-YouTube-Age-Restriction-Bypass/tree/main/account-proxy
function useProxy() {
console.info("Simple-YouTube-Age-Restriction-Bypass: Trying Unlock Method #2 (Account Proxy)");
const xmlhttp = new XMLHttpRequest();
xmlhttp.open("GET", ACCOUNT_PROXY_SERVER_HOST + `/getPlayer?videoId=${encodeURIComponent(videoId)}&reason=${encodeURIComponent(reason)}&clientName=${INNERTUBE_CONFIG.INNERTUBE_CLIENT_NAME}&clientVersion=${INNERTUBE_CONFIG.INNERTUBE_CLIENT_VERSION}&signatureTimestamp=${INNERTUBE_CONFIG.STS}`, false); // Synchronous!!!
xmlhttp.send(null);
const playerResponse = nativeParse(xmlhttp.responseText);
playerResponse.proxied = true;
return playerResponse;
}
}
function getUnlockedNextResponse(videoId, playlistId, playlistIndex) {
setInnertubeConfigFromYtcfg();
// Retrieve the video info by using a age-gate bypass for the innertube API
// Source: https://github.com/zerodytrash/Simple-YouTube-Age-Restriction-Bypass/issues/16#issuecomment-889232425
console.info("Simple-YouTube-Age-Restriction-Bypass: Trying Sidebar Unlock Method (Innertube Embed)");
const payload = getInnertubeEmbedPayload(videoId, playlistId, playlistIndex);
const xmlhttp = new XMLHttpRequest();
xmlhttp.open("POST", `/youtubei/v1/next?key=${INNERTUBE_CONFIG.INNERTUBE_API_KEY}`, false); // Synchronous!!!
xmlhttp.send(JSON.stringify(payload));
return nativeParse(xmlhttp.responseText);
}
function getInnertubeEmbedPayload(videoId, playlistId, playlistIndex) {
const data = {
context: {
client: {
clientName: INNERTUBE_CONFIG.INNERTUBE_CLIENT_NAME.replace('_EMBEDDED_PLAYER', ''),
clientVersion: INNERTUBE_CONFIG.INNERTUBE_CLIENT_VERSION,
clientScreen: "EMBED",
},
thirdParty: {
embedUrl: "https://www.youtube.com/",
},
},
playbackContext: {
contentPlaybackContext: {
signatureTimestamp: INNERTUBE_CONFIG.STS,
},
},
videoId,
playlistId,
playlistIndex,
};
// Append client info from INNERTUBE_CONTEXT
if (typeof INNERTUBE_CONFIG.INNERTUBE_CONTEXT?.client === "object") {
data.context.client = { ...data.context.client, ...INNERTUBE_CONFIG.INNERTUBE_CONTEXT.client };
}
return data;
}
// to avoid version conflicts between client and server response, the current YouTube version config will be determined
function setInnertubeConfigFromYtcfg() {
if (!window.ytcfg) {
console.warn("Simple-YouTube-Age-Restriction-Bypass: Unable to retrieve global YouTube configuration (window.ytcfg). Using old values...");
return;
}
for (const key in INNERTUBE_CONFIG) {
const value = window.ytcfg.data_?.[key] ?? window.ytcfg.get?.(key);
if (value !== undefined && value !== null) {
INNERTUBE_CONFIG[key] = value;
} else {
console.warn(`Simple-YouTube-Age-Restriction-Bypass: Unable to retrieve global YouTube configuration variable '${key}'. Using old value...`);
}
}
}
// Some extensions like AdBlock override the Object.defineProperty function to prevent a redefinition of the 'ytInitialPlayerResponse' variable by YouTube.
// But we need to define a custom descriptor to that variable to intercept its value. This behavior causes a race condition depending on the execution order with this script
// This function tries to restore the native Object.defineProperty function...
function getNativeDefineProperty() {
// Check if the Object.defineProperty function is native (original)
if (Object.defineProperty?.toString().indexOf("[native code]") > -1) {
return Object.defineProperty;
}
// if the Object.defineProperty function is already overidden, try to restore the native function from another window...
const tempFrame = createElement("iframe", { style: `display: none;` });
document.documentElement.append(tempFrame);
const nativeDefineProperty = tempFrame?.contentWindow?.Object?.defineProperty;
tempFrame.remove();
if (nativeDefineProperty) {
console.info("Simple-YouTube-Age-Restriction-Bypass: Overidden Object.defineProperty function successfully restored!");
return nativeDefineProperty;
} else {
console.warn("Simple-YouTube-Age-Restriction-Bypass: Unable to restore the original Object.defineProperty function");
return Object.defineProperty;
}
}
function createElement(tagName, options) {
const node = document.createElement(tagName);
options && Object.assign(node, options);
return node;
}
};
// Just a trick to get around the sandbox restrictions in Firefox / Greasemonkey
// Greasemonkey => Inject code into the main window
// Tampermonkey & Violentmonkey => Execute code directly
if (typeof GM_info === "object" && GM_info.scriptHandler === "Greasemonkey") {
window.eval("(" + initUnlocker.toString() + ")();");
} else {
initUnlocker();
}
l
// @name Simple YouTube Age Restriction Bypass
// @description Watch age restricted videos on YouTube without login and without age verification
// @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.1.4
// @author Zerody (https://github.com/zerodytrash)
// @namespace https://github.com/zerodytrash/Simple-YouTube-Age-Restriction-Bypass/
// @updateURL https://github.com/zerodytrash/Simple-YouTube-Age-Restriction-Bypass/raw/main/Simple-YouTube-Age-Restriction-Bypass.user.js
// @downloadURL https://github.com/zerodytrash/Simple-YouTube-Age-Restriction-Bypass/raw/main/Simple-YouTube-Age-Restriction-Bypass.user.js
// @supportURL https://github.com/zerodytrash/Simple-YouTube-Age-Restriction-Bypass/issues
// @license MIT
// @match https://www.youtube.com/*
// @match https://m.youtube.com/*
// @grant none
// @run-at document-start
// @compatible chrome Chrome + Tampermonkey or Violentmonkey
// @compatible firefox Firefox + Greasemonkey or Tampermonkey or Violentmonkey
// @compatible opera Opera + Tampermonkey or Violentmonkey
// @compatible edge Edge + Tampermonkey or Violentmonkey
// @compatible safari Safari + Tampermonkey or Violentmonkey
// ==/UserScript==
const initUnlocker = () => {
const UNLOCKABLE_PLAYER_STATES = ["AGE_VERIFICATION_REQUIRED", "AGE_CHECK_REQUIRED", "LOGIN_REQUIRED"];
const PLAYER_RESPONSE_ALIASES = ["ytInitialPlayerResponse", "playerResponse"];
// YouTube API config (Innertube).
// The actual values will be determined later from the global ytcfg variable => setInnertubeConfigFromYtcfg()
const INNERTUBE_CONFIG = {
INNERTUBE_API_KEY: "AIzaSyAO_FJ2SlqU8Q4STEHLGCilw_Y9_11qcW8",
INNERTUBE_CLIENT_NAME: "WEB",
INNERTUBE_CLIENT_VERSION: "2.20210721.00.00",
INNERTUBE_CONTEXT: {},
STS: 18834, // signatureTimestamp (relevant for the cipher functions)
LOGGED_IN: false,
};
// The following proxies are currently used as fallback if the innertube age-gate bypass doesn't work...
// You can host your own account proxy instance. See https://github.com/zerodytrash/Simple-YouTube-Age-Restriction-Bypass/tree/main/account-proxy
const ACCOUNT_PROXY_SERVER_HOST = "https://youtube-proxy.zerody.one";
const VIDEO_PROXY_SERVER_HOST = "https://phx.4everproxy.com";
const ENABLE_UNLOCK_NOTIFICATION = true;
const nativeParse = window.JSON.parse; // Backup the original parse function
const nativeDefineProperty = getNativeDefineProperty(); // Backup the original defineProperty function to intercept setter & getter on the ytInitialPlayerResponse
const nativeXmlHttpOpen = XMLHttpRequest.prototype.open;
// Just for compatibility: Backup original getter/setter for 'ytInitialPlayerResponse', defined by other extensions like AdBlock
let { get: chainedPlayerGetter, set: chainedPlayerSetter } = Object.getOwnPropertyDescriptor(window, "ytInitialPlayerResponse") || {};
let wrappedPlayerResponse;
let wrappedNextResponse;
let lastProxiedGoogleVideoUrlParams;
let responseCache = {};
const Deferred = function () {
return Object.assign(new Promise((resolve, reject) => {
this.resolve = resolve;
this.reject = reject;
}), this);
};
const Notification = (() => {
const isPolymer = !!window.Polymer;
const pageLoad = new Deferred();
const pageLoadEventName = isPolymer ? 'yt-navigate-finish' : 'state-navigateend';
const node = isPolymer
? createElement('tp-yt-paper-toast')
: createElement('c3-toast', { innerHTML: '<ytm-notification-action-renderer><div class="notification-action-response-text"></div></ytm-notification-action-renderer>' });
const nMobileText = !isPolymer && node.querySelector('.notification-action-response-text');
window.addEventListener(pageLoadEventName, init, { once: true });
function init() {
document.body.append(node);
pageLoad.resolve();
}
function show(message, duration = 5) {
if (!ENABLE_UNLOCK_NOTIFICATION) return;
pageLoad.then(_show);
function _show() {
if (isPolymer) {
node.duration = duration * 1000;
node.show(message);
} else {
nMobileText.innerHTML = message;
node.setAttribute('dir', 'in');
setTimeout(() => {
node.setAttribute('dir', 'out');
}, duration * 1000 + 225);
}
}
}
return { show };
})();
// 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...");
if (descriptor?.set) chainedPlayerSetter = descriptor.set;
if (descriptor?.get) chainedPlayerGetter = descriptor.get;
} else {
nativeDefineProperty(obj, prop, descriptor);
}
};
// Redefine 'ytInitialPlayerResponse' to inspect and modify the initial player response as soon as the variable is set on page load
nativeDefineProperty(window, "ytInitialPlayerResponse", {
set: (playerResponse) => {
// prevent recursive setter calls by ignoring unchanged data (this fixes a problem caused by Brave browser shield)
if (playerResponse === wrappedPlayerResponse) return;
wrappedPlayerResponse = inspectJsonData(playerResponse);
if (typeof chainedPlayerSetter === "function") chainedPlayerSetter(wrappedPlayerResponse);
},
get: () => {
if (typeof chainedPlayerGetter === "function") try { return chainedPlayerGetter() } catch (err) { };
return wrappedPlayerResponse || {};
},
configurable: true,
});
// Also redefine 'ytInitialData' for the initial next/sidebar response
nativeDefineProperty(window, "ytInitialData", {
set: (nextResponse) => { wrappedNextResponse = inspectJsonData(nextResponse); },
get: () => wrappedNextResponse,
configurable: true,
});
// Intercept XMLHttpRequest.open to rewrite video URL's (sometimes required)
XMLHttpRequest.prototype.open = function () {
if (arguments.length > 1 && typeof arguments[1] === "string" && arguments[1].indexOf("https://") === 0) {
const method = arguments[0];
const url = new URL(arguments[1]);
const urlParams = new URLSearchParams(url.search);
// If the account proxy was used to retieve the video info, the following applies:
// some video files (mostly music videos) can only be accessed from IPs in the same country as the innertube api request (/youtubei/v1/player) was made.
// to get around this, the googlevideo URL will be replaced with a web-proxy URL in the same country (US).
// this is only required if the "gcr=[countrycode]" flag is set in the googlevideo-url...
function isGoogleVideo() {
return method === "GET" && url.host.indexOf(".googlevideo.com") > 0;
}
function hasGcrFlag() {
return urlParams.get("gcr") !== null;
}
function isUnlockedByAccountProxy() {
return urlParams.get("id") !== null && urlParams.get("id") === lastProxiedGoogleVideoUrlParams?.get("id");
}
if (VIDEO_PROXY_SERVER_HOST && isGoogleVideo() && hasGcrFlag() && isUnlockedByAccountProxy()) {
// rewrite request URL
arguments[1] = VIDEO_PROXY_SERVER_HOST + "/direct/" + btoa(arguments[1]);
// solve CORS errors by preventing YouTube from enabling the "withCredentials" option (not required for the proxy)
nativeDefineProperty(this, "withCredentials", {
set: () => { },
get: () => false,
});
}
}
return nativeXmlHttpOpen.apply(this, arguments);
};
// Intercept, inspect and modify JSON-based communication to unlock player responses by hijacking the JSON.parse function
window.JSON.parse = (text, reviver) => inspectJsonData(nativeParse(text, reviver));
function inspectJsonData(parsedData) {
// If YouTube does JSON.parse(null) or similar weird things
if (typeof parsedData !== "object" || parsedData === null) return parsedData;
try {
// Unlock #1: Array based in "&pbj=1" AJAX response on any navigation (does not seem to be used anymore)
if (Array.isArray(parsedData)) {
const { playerResponse } = parsedData.find(e => typeof e.playerResponse === "object") || {};
if (playerResponse && isAgeRestricted(playerResponse.playabilityStatus)) {
playerResponseArrayItem.playerResponse = unlockPlayerResponse(playerResponse);
const { response: nextResponse } = parsedData.find(e => typeof e.response === "object") || {};
if (isWatchNextObject(nextResponse) && !isLoggedIn() && isWatchNextSidebarEmpty(nextResponse.contents)) {
nextResponseArrayItem.response = unlockNextResponse(nextResponse);
}
}
}
// Unlock #2: Another JSON-Object containing the 'playerResponse' (seems to be used by m.youtube.com with &pbj=1)
if (parsedData.playerResponse?.playabilityStatus && parsedData.playerResponse?.videoDetails && isAgeRestricted(parsedData.playerResponse.playabilityStatus)) {
parsedData.playerResponse = unlockPlayerResponse(parsedData.playerResponse);
}
// Unlock #3: Initial page data structure and response from the '/youtubei/v1/player' endpoint
if (parsedData.playabilityStatus && parsedData.videoDetails && isAgeRestricted(parsedData.playabilityStatus)) {
parsedData = unlockPlayerResponse(parsedData);
}
// Equivelant of unlock #2 for sidebar/next response
if (isWatchNextObject(parsedData.response) && !isLoggedIn() && isWatchNextSidebarEmpty(parsedData.response.contents)) {
parsedData.response = unlockNextResponse(parsedData.response);
}
// Equivelant of unlock #3 for sidebar/next response
if (isWatchNextObject(parsedData) && !isLoggedIn() && isWatchNextSidebarEmpty(parsedData.contents)) {
parsedData = unlockNextResponse(parsedData)
}
} catch (err) {
console.error("Simple-YouTube-Age-Restriction-Bypass-Error:", err, "You can report bugs at: https://github.com/zerodytrash/Simple-YouTube-Age-Restriction-Bypass/issues");
}
return parsedData;
}
function isAgeRestricted(playabilityStatus) {
if (!playabilityStatus?.status) return false;
return !!playabilityStatus.desktopLegacyAgeGateReason || UNLOCKABLE_PLAYER_STATES.includes(playabilityStatus.status);
}
function isWatchNextObject(parsedData) {
if (!parsedData?.contents || !parsedData?.currentVideoEndpoint?.watchEndpoint?.videoId) return false;
return !!parsedData.contents.twoColumnWatchNextResults || !!parsedData.contents.singleColumnWatchNextResults;
}
function isWatchNextSidebarEmpty(contents) {
const secondaryResults = contents.twoColumnWatchNextResults?.secondaryResults?.secondaryResults;
if (secondaryResults?.results) return false;
// MWEB response layout
const singleColumnWatchNextContents = contents.singleColumnWatchNextResults?.results?.results?.contents;
if (!singleColumnWatchNextContents) return true;
const { itemSectionRenderer } = singleColumnWatchNextContents.find(e => e.itemSectionRenderer?.targetId === "watch-next-feed") || {};
return !!itemSectionRenderer;
}
function isLoggedIn() {
setInnertubeConfigFromYtcfg();
return INNERTUBE_CONFIG.LOGGED_IN;
}
function unlockPlayerResponse(playerResponse) {
const videoId = playerResponse.videoDetails.videoId;
const reason = playerResponse.playabilityStatus?.status;
const unlockedPayerResponse = getUnlockedPlayerResponse(videoId, reason);
// account proxy error?
if (unlockedPayerResponse.errorMessage) {
Notification.show("Unable to unlock this video - More information in the developer console (ProxyError)", 10);
throw new Error(`Unlock Failed, errorMessage:${unlockedPayerResponse.errorMessage}; innertubeApiKey:${INNERTUBE_CONFIG.INNERTUBE_API_KEY}; innertubeClientName:${INNERTUBE_CONFIG.INNERTUBE_CLIENT_NAME}; innertubeClientVersion:${INNERTUBE_CONFIG.INNERTUBE_CLIENT_VERSION}`);
}
// check if the unlocked response isn't playable
if (unlockedPayerResponse.playabilityStatus?.status !== "OK") {
Notification.show(`Unable to unlock this video - More information in the developer console (playabilityStatus: ${unlockedPayerResponse.playabilityStatus?.status})`, 10);
throw new Error(`Unlock Failed, playabilityStatus:${unlockedPayerResponse.playabilityStatus?.status}; innertubeApiKey:${INNERTUBE_CONFIG.INNERTUBE_API_KEY}; innertubeClientName:${INNERTUBE_CONFIG.INNERTUBE_CLIENT_NAME}; innertubeClientVersion:${INNERTUBE_CONFIG.INNERTUBE_CLIENT_VERSION}`);
}
// if the video info was retrieved via proxy, store the URL params from the url- or signatureCipher-attribute to detect later if the requested video files are from this unlock.
// => see isUnlockedByAccountProxy()
if (unlockedPayerResponse.proxied && unlockedPayerResponse.streamingData?.adaptiveFormats) {
const cipherText = unlockedPayerResponse.streamingData.adaptiveFormats.find(x => x.signatureCipher)?.signatureCipher;
const videoUrl = cipherText ? new URLSearchParams(cipherText).get("url") : unlockedPayerResponse.streamingData.adaptiveFormats.find(x => x.url)?.url;
lastProxiedGoogleVideoUrlParams = videoUrl ? new URLSearchParams(new URL(videoUrl).search) : null;
}
Notification.show("Video successfully unlocked!");
return unlockedPayerResponse;
}
function unlockNextResponse(nextResponse) {
const { videoId, playlistId, index: playlistIndex } = nextResponse.currentVideoEndpoint.watchEndpoint;
const unlockedNextResponse = getUnlockedNextResponse(videoId, playlistId, playlistIndex);
// check if the unlocked response's sidebar is still empty
if (isWatchNextSidebarEmpty(unlockedNextResponse.contents)) {
throw new Error(`Sidebar Unlock Failed, innertubeApiKey:${INNERTUBE_CONFIG.INNERTUBE_API_KEY}; innertubeClientName:${INNERTUBE_CONFIG.INNERTUBE_CLIENT_NAME}; innertubeClientVersion:${INNERTUBE_CONFIG.INNERTUBE_CLIENT_VERSION}`);
}
// Transfer WatchNextResults to original response
if (nextResponse.contents?.twoColumnWatchNextResults?.secondaryResults) {
nextResponse.contents.twoColumnWatchNextResults.secondaryResults = unlockedNextResponse?.contents?.twoColumnWatchNextResults?.secondaryResults;
}
// Transfer mobile (MWEB) WatchNextResults to original response
if (nextResponse.contents?.singleColumnWatchNextResults?.results?.results?.contents) {
const unlockedWatchNextFeed = unlockedNextResponse?.contents?.singleColumnWatchNextResults?.results?.results?.contents
?.find(x => x.itemSectionRenderer?.targetId === "watch-next-feed");
if (unlockedWatchNextFeed) nextResponse.contents.singleColumnWatchNextResults.results.results.contents.push(unlockedWatchNextFeed);
}
// Transfer video description to original response
const originalVideoSecondaryInfoRenderer = nextResponse.contents?.twoColumnWatchNextResults?.results?.results?.contents
?.find(x => x.videoSecondaryInfoRenderer)?.videoSecondaryInfoRenderer;
const unlockedVideoSecondaryInfoRenderer = unlockedNextResponse.contents?.twoColumnWatchNextResults?.results?.results?.contents
?.find(x => x.videoSecondaryInfoRenderer)?.videoSecondaryInfoRenderer;
if (originalVideoSecondaryInfoRenderer && unlockedVideoSecondaryInfoRenderer?.description)
originalVideoSecondaryInfoRenderer.description = unlockedVideoSecondaryInfoRenderer.description;
// Transfer mobile (MWEB) video description to original response
const originalStructuredDescriptionContentRenderer = nextResponse.engagementPanels
?.find(x => x.engagementPanelSectionListRenderer)?.engagementPanelSectionListRenderer?.content?.structuredDescriptionContentRenderer?.items
?.find(x => x.expandableVideoDescriptionBodyRenderer);
const unlockedStructuredDescriptionContentRenderer = unlockedNextResponse.engagementPanels
?.find(x => x.engagementPanelSectionListRenderer)?.engagementPanelSectionListRenderer?.content?.structuredDescriptionContentRenderer?.items
?.find(x => x.expandableVideoDescriptionBodyRenderer);
if (originalStructuredDescriptionContentRenderer && unlockedStructuredDescriptionContentRenderer?.expandableVideoDescriptionBodyRenderer)
originalStructuredDescriptionContentRenderer.expandableVideoDescriptionBodyRenderer = unlockedStructuredDescriptionContentRenderer.expandableVideoDescriptionBodyRenderer;
return nextResponse;
}
function getUnlockedPlayerResponse(videoId, reason) {
// Check if response is cached
if (responseCache.videoId === videoId) return responseCache.playerResponse;
setInnertubeConfigFromYtcfg();
let playerResponse = useInnertubeEmbed();
if (playerResponse?.playabilityStatus?.status !== "OK") playerResponse = useProxy();
// Cache response for 10 seconds
responseCache = { videoId, playerResponse };
setTimeout(() => { responseCache = {} }, 10000);
return playerResponse;
// 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
function useInnertubeEmbed() {
console.info("Simple-YouTube-Age-Restriction-Bypass: Trying Unlock Method #1 (Innertube Embed)");
const payload = getInnertubeEmbedPayload(videoId);
const xmlhttp = new XMLHttpRequest();
xmlhttp.open("POST", `/youtubei/v1/player?key=${INNERTUBE_CONFIG.INNERTUBE_API_KEY}`, false); // Synchronous!!!
xmlhttp.send(JSON.stringify(payload));
return nativeParse(xmlhttp.responseText);
}
// Strategy 2: Retrieve the video info from an account proxy server.
// See https://github.com/zerodytrash/Simple-YouTube-Age-Restriction-Bypass/tree/main/account-proxy
function useProxy() {
console.info("Simple-YouTube-Age-Restriction-Bypass: Trying Unlock Method #2 (Account Proxy)");
const xmlhttp = new XMLHttpRequest();
xmlhttp.open("GET", ACCOUNT_PROXY_SERVER_HOST + `/getPlayer?videoId=${encodeURIComponent(videoId)}&reason=${encodeURIComponent(reason)}&clientName=${INNERTUBE_CONFIG.INNERTUBE_CLIENT_NAME}&clientVersion=${INNERTUBE_CONFIG.INNERTUBE_CLIENT_VERSION}&signatureTimestamp=${INNERTUBE_CONFIG.STS}`, false); // Synchronous!!!
xmlhttp.send(null);
const playerResponse = nativeParse(xmlhttp.responseText);
playerResponse.proxied = true;
return playerResponse;
}
}
function getUnlockedNextResponse(videoId, playlistId, playlistIndex) {
setInnertubeConfigFromYtcfg();
// Retrieve the video info by using a age-gate bypass for the innertube API
// Source: https://github.com/zerodytrash/Simple-YouTube-Age-Restriction-Bypass/issues/16#issuecomment-889232425
console.info("Simple-YouTube-Age-Restriction-Bypass: Trying Sidebar Unlock Method (Innertube Embed)");
const payload = getInnertubeEmbedPayload(videoId, playlistId, playlistIndex);
const xmlhttp = new XMLHttpRequest();
xmlhttp.open("POST", `/youtubei/v1/next?key=${INNERTUBE_CONFIG.INNERTUBE_API_KEY}`, false); // Synchronous!!!
xmlhttp.send(JSON.stringify(payload));
return nativeParse(xmlhttp.responseText);
}
function getInnertubeEmbedPayload(videoId, playlistId, playlistIndex) {
const data = {
context: {
client: {
clientName: INNERTUBE_CONFIG.INNERTUBE_CLIENT_NAME.replace('_EMBEDDED_PLAYER', ''),
clientVersion: INNERTUBE_CONFIG.INNERTUBE_CLIENT_VERSION,
clientScreen: "EMBED",
},
thirdParty: {
embedUrl: "https://www.youtube.com/",
},
},
playbackContext: {
contentPlaybackContext: {
signatureTimestamp: INNERTUBE_CONFIG.STS,
},
},
videoId,
playlistId,
playlistIndex,
};
// Append client info from INNERTUBE_CONTEXT
if (typeof INNERTUBE_CONFIG.INNERTUBE_CONTEXT?.client === "object") {
data.context.client = { ...data.context.client, ...INNERTUBE_CONFIG.INNERTUBE_CONTEXT.client };
}
return data;
}
// to avoid version conflicts between client and server response, the current YouTube version config will be determined
function setInnertubeConfigFromYtcfg() {
if (!window.ytcfg) {
console.warn("Simple-YouTube-Age-Restriction-Bypass: Unable to retrieve global YouTube configuration (window.ytcfg). Using old values...");
return;
}
for (const key in INNERTUBE_CONFIG) {
const value = window.ytcfg.data_?.[key] ?? window.ytcfg.get?.(key);
if (value !== undefined && value !== null) {
INNERTUBE_CONFIG[key] = value;
} else {
console.warn(`Simple-YouTube-Age-Restriction-Bypass: Unable to retrieve global YouTube configuration variable '${key}'. Using old value...`);
}
}
}
// Some extensions like AdBlock override the Object.defineProperty function to prevent a redefinition of the 'ytInitialPlayerResponse' variable by YouTube.
// But we need to define a custom descriptor to that variable to intercept its value. This behavior causes a race condition depending on the execution order with this script
// This function tries to restore the native Object.defineProperty function...
function getNativeDefineProperty() {
// Check if the Object.defineProperty function is native (original)
if (Object.defineProperty?.toString().indexOf("[native code]") > -1) {
return Object.defineProperty;
}
// if the Object.defineProperty function is already overidden, try to restore the native function from another window...
const tempFrame = createElement("iframe", { style: `display: none;` });
document.documentElement.append(tempFrame);
const nativeDefineProperty = tempFrame?.contentWindow?.Object?.defineProperty;
tempFrame.remove();
if (nativeDefineProperty) {
console.info("Simple-YouTube-Age-Restriction-Bypass: Overidden Object.defineProperty function successfully restored!");
return nativeDefineProperty;
} else {
console.warn("Simple-YouTube-Age-Restriction-Bypass: Unable to restore the original Object.defineProperty function");
return Object.defineProperty;
}
}
function createElement(tagName, options) {
const node = document.createElement(tagName);
options && Object.assign(node, options);
return node;
}
};
// Just a trick to get around the sandbox restrictions in Firefox / Greasemonkey
// Greasemonkey => Inject code into the main window
// Tampermonkey & Violentmonkey => Execute code directly
if (typeof GM_info === "object" && GM_info.scriptHandler === "Greasemonkey") {
window.eval("(" + initUnlocker.toString() + ")();");
} else {
initUnlocker();
}
l
[ Dernière édition du message le 16/02/2022 à 13:21:48 ]
Anonyme
12670 Posté le 16/02/2022 à 13:21:54
Citation :
J'ai un jeu de mot la dessus. Mais je le ferai pas. Trop lourd.Arnaud Montebourg
- < Liste des sujets
- Charte