X-Git-Url: https://code.delx.au/youtube-cgi/blobdiff_plain/0872bf6ff47e5817a748dfcf4c8acaa077f29801..d88238ba4df834947fb3681c854093358696daaf:/youtube.cgi diff --git a/youtube.cgi b/youtube.cgi index 8b103be..45c8574 100755 --- a/youtube.cgi +++ b/youtube.cgi @@ -4,7 +4,6 @@ from __future__ import division import cookielib import cgi -import itertools import json from lxml import html import os @@ -134,6 +133,11 @@ def get_player_config(doc): p2 = line.rfind(";") if p1 >= 0 and p2 > 0: return json.loads(line[p1+1:p2]) + if "ytplayer.config =" in line: + p1 = line.find("ytplayer.config =") + p2 = line.rfind(";") + if p1 >= 0 and p2 > 0: + return json.loads(line[p1+18:p2]) if "'PLAYER_CONFIG': " in line: p1 = line.find(":") if p1 >= 0: @@ -141,29 +145,63 @@ def get_player_config(doc): convert_from_old_itag(player_config) return player_config -def get_best_video(player_config): - url_data = urlparse.parse_qs(player_config["args"]["url_encoded_fmt_stream_map"]) - url_data = itertools.izip_longest( - url_data["url"], - url_data["type"], - url_data["quality"], - url_data.get("sig", []), +def decode_signature(js_url, s): + script = urlopen(js_url).read() + func_name = re.search(R"\b([a-z]+)\([a-z]+\.s\);", script).groups()[0] + p1 = script.find("function " + func_name) + p2 = script.find("}", p1) + func_block = script[p1:p2+1] + + p = subprocess.Popen( + "js", + shell=True, + close_fds=True, + stdin=subprocess.PIPE, + stdout=subprocess.PIPE ) + p.stdin.write(func_block + "\n") + p.stdin.write("console.log(%s('%s'));\n" % (func_name, s)) + p.stdin.close() + + signature = p.stdout.read().strip() + if p.wait() != 0: + raise Exception("js failed to execute: %d" % p.returncode) + + return signature + +def get_best_video(player_config): + url_data_list = player_config["args"]["url_encoded_fmt_stream_map"].split(",") + js_url = player_config["assets"]["js"] + best_url = None best_quality = None best_extension = None - for video_url, mimetype, quality, signature in url_data: - mimetype = mimetype.split(";")[0] + for url_data in url_data_list: + url_data = urlparse.parse_qs(url_data) + mimetype = url_data["type"][0].split(";")[0] + quality = url_data["quality"][0] + + if quality not in QUALITIES: + continue if mimetype not in MIMETYPES: continue + extension = MIMETYPES[mimetype] - quality = QUALITIES.get(quality.split(",")[0], -1) - if best_quality is None or quality > best_quality: - if signature: - video_url = append_to_qs(video_url, {"signature": signature}) - best_url = video_url - best_quality = quality - best_extension = extension + quality = QUALITIES.get(quality, -1) + + if best_quality is not None and quality < best_quality: + continue + + video_url = url_data["url"][0] + if "sig" in url_data: + signature = url_data["sig"][0] + else: + signature = decode_signature(js_url, url_data["s"][0]) + video_url = append_to_qs(video_url, {"signature": signature}) + + best_url = video_url + best_quality = quality + best_extension = extension return best_url, best_extension @@ -238,27 +276,32 @@ def pp_size(size): def copy_with_progress(content_length, infile, outfile): def print_status(): + rate = 0 + if now != last_ts: + rate = last_bytes_read / (now - last_ts) sys.stdout.write("\33[2K\r") sys.stdout.write("%s / %s (%s/sec)" % ( pp_size(bytes_read), pp_size(content_length), - pp_size(bytes_read / (now - start_ts)), + pp_size(rate), )) sys.stdout.flush() - start_ts = time.time() last_ts = 0 + last_bytes_read = 0 bytes_read = 0 while True: now = time.time() if now - last_ts > 0.5: - last_ts = now print_status() + last_ts = now + last_bytes_read = 0 buf = infile.read(32768) if not buf: break outfile.write(buf) + last_bytes_read += len(buf) bytes_read += len(buf) # Newline at the end @@ -315,7 +358,7 @@ def main(): if __name__ == "__main__": - resource.setrlimit(resource.RLIMIT_AS, (MAX_MEMORY_BYTES, MAX_MEMORY_BYTES)) +### resource.setrlimit(resource.RLIMIT_AS, (MAX_MEMORY_BYTES, MAX_MEMORY_BYTES)) if os.environ.has_key("SCRIPT_NAME"): cgimain() else: