YTDC

This tool is able to decipher YouTube signatures.

Ce script ne devrait pas être installé directement. C'est une librairie créée pour d'autres scripts. Elle doit être inclus avec la commande // @require https://update.greasyfork.org/scripts/423445/936595/YTDC.js

// ==UserScript==
// @name			YTDC
// @namespace		https://greasyfork.org/users/723559-valen-h
// @homepage		https://greasyfork.org/scripts/423445-ytdc/
// @updateURL		https://greasyfork.org/scripts/423445-ytdc/code/YTDC.js
// @installURL		https://greasyfork.org/scripts/423445-ytdc/code/YTDC.js
// @downloadURL		https://greasyfork.org/scripts/423445-ytdc/code/YTDC.js
// @version			0.2
// @description		This tool is able to decipher YouTube signatures.
// @author			V. H.
// @license			AFL-3.0
// @grant			none
// ==/UserScript==
/**
 * @since 17/3/2021
 * @file ytd-cracker.js
 */

"use strict";

class _YTD {
	
	_xml = new XMLHttpRequest();
	_base = "";
	_decipher = null;
	static decreg = /\w{1,3}?\s*?=\s*?\w{1,3}?\.split\(\s*?["']{2}?\s*?\)\W*?(?:\w{1,3}?\.\w{1,3}?\(\s*?\w{1,3}?\s*?,\s*?\d+?\s*?\).*?\W*?)*?.*?\w{1,3}?\.join\(\s*?["']{2}?\s*?\)/m;
	static nsreg = "var\\s@1@\\s*?=\\s*?\\{.*?\\};";
	
	constructor(...a) {
		this._xml.overrideMimeType("text/javascript");
		this._xml.onloadend = e => {
			if (this._xml.responseText)
				this._base = this._xml.responseText;
		};
		
		if (a.length) this.init(...a);
	} //ctor
	
	static _init(...a) {
		return new _YTD(...a);
	} //_init
	
	async init(a, b) {
		await this.getPlayer(a);
		this.parse(b);
		
		return this;
	} //init
	
	async getPlayer(scr = Array.from(document.scripts).find(scr => scr.src && scr.src.endsWith("base.js")) || "https://www.youtube.com/s/player/223a7479/player_ias.vflset/en_US/base.js") {
		if (!scr) throw "Bad Source";
		
		if (scr instanceof HTMLScriptElement) scr = scr.src;
		
		this._xml.open("GET", scr, false);
		this._xml.send();
		
		return this._xml.responseText;
	} //getPlayer
	
	parse(base = this._base) {
		if (!base) throw "Bad Source";
		
		const decstr = (base.trim().match(_YTD.decreg) || [ "'not-found';" ])[0].trim(),
			comms = decstr.split(/[;\n]/gmi).map(a => a.trim()),
			ns = comms.length > 2 ? comms[1].split('.')[0].trim() : "not-found",
			nsreg = new RegExp(_YTD.nsreg.replace(/@1@/g, ns), "ms"),
			nsstr = (base.trim().match(nsreg) || [ "'not-found';" ])[0].replace(/\n/g, '').trim(),
			dec = `${nsstr}${decstr}`;
		
		this._decipher = new Function(comms[0].split('=')[0].trim(), dec);
		
		return { decstr, comms, ns, nsreg, nsstr, dec };
	} //parse
	
	decipher(sig) {
		if (!sig) throw "Bad Sig";
		else if (!this._decipher) throw "Bad Decipher";
		
		return this._decipher(sig);
	} //decipher
	
	process(cip) {
		if (!cip) cip = ytInitialPlayerResponse ? ytInitialPlayerResponse.streamingData ? ytInitialPlayerResponse.streamingData.formats[ytInitialPlayerResponse.streamingData.formats.length - 1].signatureCipher : "" : "";
		
		cip = cip.split('&').map(decodeURIComponent);
		
		return `${cip[2].slice(4)}&${cip[1].slice(3)}=${this.decipher(cip[0].slice(2))}`;
	} //process
	
};

if (typeof unsafeWindow != "undefined") unsafeWindow._ytd = _YTD._init();
if (typeof window != "undefined") window._ytd = _YTD._init();