X-Git-Url: https://code.delx.au/webdl/blobdiff_plain/4ea7cd459da04dbde5f5cc1a26b3b343ab35cdce..7d9926f2b1884467ece60492f8d49b757ad6a82a:/common.py diff --git a/common.py b/common.py index f51ec53..accd6dd 100644 --- a/common.py +++ b/common.py @@ -166,39 +166,47 @@ def exec_subprocess(cmd): return False -def convert_flv_mp4(orig_filename): - basename = os.path.splitext(orig_filename)[0] - flv_filename = basename + ".flv" - mp4_filename = basename + ".mp4" - if orig_filename != flv_filename: - os.rename(orig_filename, flv_filename) - print "Converting %s to mp4" % flv_filename +def avconv_remux(infile, outfile): + print "Converting %s to mp4" % infile cmd = [ "avconv", - "-i", flv_filename, + "-i", infile, "-acodec", "copy", "-vcodec", "copy", - mp4_filename, + outfile, ] if not exec_subprocess(cmd): - return + # failed, error has already been logged + return False try: - flv_size = os.stat(flv_filename).st_size - mp4_size = os.stat(mp4_filename).st_size - if abs(flv_size - mp4_size) < 0.05 * flv_size: - os.unlink(flv_filename) + flv_size = os.stat(infile).st_size + mp4_size = os.stat(outfile).st_size + if abs(flv_size - mp4_size) < 0.1 * flv_size: + os.unlink(infile) + return True else: - print >>sys.stderr, "The size of", mp4_filename, "is suspicious, did avconv fail?" + print >>sys.stderr, "The size of", outfile, "is suspicious, did avconv fail?" + return False except Exception, e: - print "Conversion failed", e + print >>sys.stderr, "Conversion failed", e + return False -def convert_filename(filename): - if os.path.splitext(filename.lower())[1] in (".mp4", ".flv"): - f = open(filename) +def convert_to_mp4(filename): + with open(filename) as f: fourcc = f.read(4) - f.close() - if fourcc == "FLV\x01": - convert_flv_mp4(filename) + basename, ext = os.path.splitext(filename) + + if ext == ".mp4" and fourcc == "FLV\x01": + os.rename(filename, basename + ".flv") + ext = ".flv" + filename = basename + ext + + if ext in (".flv", ".ts"): + filename_mp4 = basename + ".mp4" + return avconv_remux(filename, filename_mp4) + + return ext == ".mp4" + def download_rtmp(filename, vbase, vpath, hash_url=None): filename = sanify_filename(filename) @@ -214,8 +222,7 @@ def download_rtmp(filename, vbase, vpath, hash_url=None): if hash_url is not None: cmd += ["--swfVfy", hash_url] if exec_subprocess(cmd): - convert_filename(filename) - return True + return convert_to_mp4(filename) else: return False @@ -246,8 +253,91 @@ def download_urllib(filename, url, referrer=None): except: pass - convert_filename(filename) - return True + return convert_to_mp4(filename) + +def download_hls_get_stream(url): + def parse_bandwidth(line): + params = line.split(":", 1)[1].split(",") + for kv in params: + k, v = kv.split("=", 1) + if k == "BANDWIDTH": + return int(v) + return 0 + + m3u8 = grab_text(url, 0) + best_bandwidth = None + best_url = None + for line in m3u8.split("\n"): + if line.startswith("#EXT-X-STREAM-INF:"): + bandwidth = parse_bandwidth(line) + if best_bandwidth is None or bandwidth > best_bandwidth: + best_bandwidth = bandwidth + best_url = None + elif not line.startswith("#"): + if best_url is None: + best_url = line.strip() + + if not best_url: + raise Exception("Failed to find best stream for HLS: " + url) + + return best_url + +def download_hls_segments(outf, url): + m3u8 = grab_text(url, 0) + + fail_if_not_last_segment = None + for line in m3u8.split("\n"): + if not line.strip() or line.startswith("#"): + continue + + if fail_if_not_last_segment: + raise e + + try: + download_hls_fetch_segment(outf, line) + except urllib2.HTTPError, e: + fail_if_not_last_segment = e + continue + sys.stdout.write(".") + sys.stdout.flush() + + sys.stdout.write("\n") + +def download_hls_fetch_segment(outf, segment_url): + try: + src = _urlopen(segment_url) + shutil.copyfileobj(src, outf) + except: + raise + finally: + try: + src.close() + except: + pass + +def download_hls(filename, m3u8_master_url, hack_url_func=None): + if hack_url_func is None: + hack_url_func = lambda url: url + + tmpdir = tempfile.mkdtemp(prefix="webdl-hls") + + print "Downloading: %s" % filename + + try: + best_stream_url = download_hls_get_stream(hack_url_func(m3u8_master_url)) + ts_file = open(filename, "w") + download_hls_segments(ts_file, hack_url_func(best_stream_url)) + except KeyboardInterrupt: + print "\nCancelled", m3u8_master_url + return False + finally: + shutil.rmtree(tmpdir) + try: + ts_file.close() + except: + pass + + return convert_to_mp4(filename) def natural_sort(l, key=None): ignore_list = ["a", "the"]