]> code.delx.au - youtube-cgi/commitdiff
Fixed for latest changes master
authorJames Bunton <jamesbunton@delx.net.au>
Tue, 9 Feb 2021 11:32:55 +0000 (22:32 +1100)
committerJames Bunton <jamesbunton@delx.net.au>
Tue, 9 Feb 2021 11:32:55 +0000 (22:32 +1100)
youtube.cgi

index 212ceae83234d81ccc0c63b7681f00d9fadbd92f..b0771af44fd95ffe910a361f608f6bf3493829a3 100755 (executable)
@@ -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"