From bc8862ee79979c7910b3c5f5b83c871a263c796a Mon Sep 17 00:00:00 2001 From: James Bunton Date: Tue, 9 Feb 2021 22:32:55 +1100 Subject: [PATCH] Fixed for latest changes --- youtube.cgi | 44 ++++++++++++++++++++++++-------------------- 1 file changed, 24 insertions(+), 20 deletions(-) diff --git a/youtube.cgi b/youtube.cgi index 212ceae..b0771af 100755 --- a/youtube.cgi +++ b/youtube.cgi @@ -142,8 +142,8 @@ def append_to_qs(url, params): def get_player_config(scripts): config_strings = [ - ("ytplayer.config = {", 1, "};", 1), ("ytcfg.set({\"", 2, "});", 1), + ("ytInitialPlayerResponse = {\"", 2, "};", 1), ] player_config = {} for script in scripts: @@ -175,13 +175,22 @@ def find_cipher_func(script): func_name = match.groups()[0] return func_name -def find_url_func(script): - FUNC_NAME = R"([a-zA-Z0-9$]+)" - PATTERN = R"this\.url\s*=\s*" + FUNC_NAME + R"\s*\(\s*this\s*\)" - - match = re.search(PATTERN, script) - func_name = match.groups()[0] - return func_name +def construct_url_from_cipher_result(cipher_result): + for k, v in cipher_result.items(): + if isinstance(v, str) and v.startswith("https://"): + temp_url = v + break + else: + raise Exception("Could not find URL-like string in cipher result!") + + for k, v in cipher_result.items(): + if isinstance(v, dict): + params = {} + for k2, v2 in v.items(): + params[k2] = urllib.parse.unquote(v2) + return append_to_qs(temp_url, params) + else: + raise Exception("Could not find params-like structure in cipher result!") def decode_cipher_url(js_url, cipher): cipher = urllib.parse.parse_qs(cipher) @@ -196,11 +205,9 @@ def decode_cipher_url(js_url, cipher): f.close() cipher_func_name = find_cipher_func(script) - url_func_name = find_url_func(script) params = { "cipher_func_name": cipher_func_name, - "url_func_name": url_func_name, "args": json.dumps(args), "code": json.dumps(extract_js(script)), } @@ -237,30 +244,29 @@ def decode_cipher_url(js_url, cipher): }; fakeGlobal.XMLHttpRequest = class XMLHttpRequest {}; fakeGlobal.matchMedia = () => ({matches: () => {}, media: ''}); - fakeGlobal.result_url = null; + fakeGlobal.result = null; fakeGlobal.g = function(){}; // this is _yt_player fakeGlobal.TimeRanges = function(){}; const code_string = %(code)s + ';'; - const exec_string = 'result_url = %(url_func_name)s(%(cipher_func_name)s(...%(args)s));'; + const exec_string = 'result = %(cipher_func_name)s(...%(args)s);'; vm.runInNewContext(code_string + exec_string, fakeGlobal); - console.log(fakeGlobal.result_url); + console.log(JSON.stringify(fakeGlobal.result)); """ % params) p.stdin.write(js_decode_script.encode("utf-8")) p.stdin.close() - result_url = p.stdout.read().decode("utf-8").strip() + result = json.load(p.stdout) if p.wait() != 0: raise Exception("js failed to execute: %d" % p.returncode) + result_url = construct_url_from_cipher_result(result) return result_url def get_best_video(player_config): - player_args = player_config["args"] - player_response = json.loads(player_args["player_response"]) - formats = player_response["streamingData"]["formats"] + formats = player_config["streamingData"]["formats"] best_url = None best_quality = None @@ -309,9 +315,7 @@ def get_video_url(page): if not video_url: return None, None - title = player_config["args"].get("title", None) - if not title: - title = json.loads(player_config["args"]["player_response"])["videoDetails"]["title"] + title = player_config["videoDetails"].get("title", None) if not title: title = "Unknown title" -- 2.39.2