]> code.delx.au - youtube-cgi/blobdiff - youtube.cgi
Fix for Google changes
[youtube-cgi] / youtube.cgi
index c88ad8e11d4995ef6a7a8505dda6ef3bebd04669..33bb29a105d46ae8499b15a23203709816062411 100755 (executable)
@@ -148,7 +148,7 @@ def extract_js(script):
 
     return script[len(PREFIX):-len(SUFFIX)]
 
-def find_func_name(script):
+def find_cipher_func(script):
     FUNC_NAME = R"([a-zA-Z0-9$]+)"
     DECODE_URI_COMPONENT = R"(\(decodeURIComponent)?"
     FUNC_PARAMS = R"(\([a-zA-Z,\.]+\.s\))"
@@ -159,16 +159,33 @@ def find_func_name(script):
     func_name = match.groups()[0]
     return func_name
 
-def decode_signature(js_url, signature):
+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 decode_cipher_url(js_url, cipher):
+    cipher = urllib.parse.parse_qs(cipher)
+    args = [
+        cipher["url"][0],
+        cipher["sp"][0],
+        cipher["s"][0],
+    ]
+
     f = urlopen(js_url)
     script = f.read().decode("utf-8")
     f.close()
 
-    func_name = find_func_name(script)
+    cipher_func_name = find_cipher_func(script)
+    url_func_name = find_url_func(script)
 
     params = {
-        "func_name": func_name,
-        "signature": json.dumps(signature),
+        "cipher_func_name": cipher_func_name,
+        "url_func_name": url_func_name,
+        "args": json.dumps(args),
         "code": json.dumps(extract_js(script)),
     }
     p = subprocess.Popen(
@@ -181,43 +198,47 @@ def decode_signature(js_url, signature):
     js_decode_script = ("""
         const vm = require('vm');
 
-        const sandbox = {
-            location: {
-                hash: '',
-                href: '',
-                protocol: 'http:'
-            },
-            history: {
-                pushState: function(){}
-            },
-            document: {},
-            navigator: {
-                userAgent: ''
-            },
-            XMLHttpRequest: class XMLHttpRequest {},
-            matchMedia: () => ({matches: () => {}, media: ''}),
-            signature: %(signature)s,
-            transformed_signature: null,
-            g: function(){} // this is _yt_player
+        const fakeGlobal = {};
+        fakeGlobal.window = fakeGlobal;
+        fakeGlobal.location = {
+            hash: '',
+            host: 'www.youtube.com',
+            hostname: 'www.youtube.com',
+            href: 'https://www.youtube.com',
+            origin: 'https://www.youtube.com',
+            pathname: '/',
+            protocol: 'https:'
+        };
+        fakeGlobal.history = {
+            pushState: function(){}
+        };
+        fakeGlobal.document = {
+            location: fakeGlobal.location
+        };
+        fakeGlobal.document = {};
+        fakeGlobal.navigator = {
+            userAgent: ''
         };
-        sandbox.window = sandbox;
+        fakeGlobal.XMLHttpRequest = class XMLHttpRequest {};
+        fakeGlobal.matchMedia = () => ({matches: () => {}, media: ''});
+        fakeGlobal.result_url = null;
+        fakeGlobal.g = function(){}; // this is _yt_player
 
         const code_string = %(code)s + ';';
-        const exec_string = 'transformed_signature = %(func_name)s(signature);';
-        vm.runInNewContext(code_string + exec_string, sandbox);
+        const exec_string = 'result_url = %(url_func_name)s(%(cipher_func_name)s(...%(args)s));';
+        vm.runInNewContext(code_string + exec_string, fakeGlobal);
 
-        console.log(sandbox.transformed_signature);
+        console.log(fakeGlobal.result_url);
     """ % params)
 
     p.stdin.write(js_decode_script.encode("utf-8"))
     p.stdin.close()
 
-    transformed_signature = p.stdout.read().decode("utf-8").strip()
-    transformed_signature = urllib.parse.unquote(transformed_signature)
+    result_url = p.stdout.read().decode("utf-8").strip()
     if p.wait() != 0:
         raise Exception("js failed to execute: %d" % p.returncode)
 
-    return transformed_signature
+    return result_url
 
 def get_best_video(player_config):
     js_url = player_config["assets"]["js"]
@@ -245,14 +266,7 @@ def get_best_video(player_config):
             continue
 
         if "signatureCipher" in format_data:
-            cipher = urllib.parse.parse_qs(format_data["signatureCipher"])
-            video_url = cipher["url"][0]
-            if "sig" in cipher:
-                signature = cipher["sig"][0]
-            elif "s" in cipher:
-                signature = decode_signature(js_url, cipher["s"][0])
-            sp = cipher.get("sp", ["signature"])[0]
-            video_url = append_to_qs(video_url, {sp: signature})
+            video_url = decode_cipher_url(js_url, format_data["signatureCipher"])
         else:
             video_url = format_data["url"]