X-Git-Url: https://git.frykholm.com/svtplaydump.git/blobdiff_plain/89a00fa0fe4fbb8047e21230249070ef5177535b..a750237037e1bb3920eab83b594a39758efe662e:/svtplaydump.py diff --git a/svtplaydump.py b/svtplaydump.py index c61c36e..066ed41 100755 --- a/svtplaydump.py +++ b/svtplaydump.py @@ -1,4 +1,5 @@ #!/usr/bin/env python +# -*- coding: utf-8 -*- # # (C) Copyright 2010 Mikael Frykholm # @@ -16,6 +17,7 @@ # along with this program. If not, see # # Changelog: +# 0.3 added apple streaming playlist parsing and decryption # 0.2 added python 2.4 urlparse compatibility # 0.1 initial release @@ -23,6 +25,8 @@ from BeautifulSoup import BeautifulSoup from subprocess import * import re import json +from Crypto.Cipher import AES +import struct try: import urlparse except ImportError: @@ -37,13 +41,12 @@ import sys def main(argv=None): if argv is None: argv=sys.argv - videoid = re.findall("/video/(.*)[/]*",argv[1])[0] - - soup = BeautifulSoup(urllib2.urlopen("http://www.svtplay.se/video/%s/?type=embed"%videoid).read()) - - flashvars = json.loads(soup.find("param", {"name":"flashvars",'value':True})['value'][5:]) + page = urllib2.urlopen(argv[1]).read() + soup = BeautifulSoup(page,convertEntities=BeautifulSoup.HTML_ENTITIES) + videoid = re.findall("svt_article_id=(.*)[&]*",page)[0] + flashvars = json.loads(urllib2.urlopen("http://www.svt.se/wd?widgetId=248134§ionId=1024&articleId=%s&position=0&format=json&type=embed&contextSectionId=1024"%videoid).read()) try: - title = flashvars['statistics']['title'] + title = soup.find('meta',{'property':'og:title'}).attrMap['content'] except: title = "unnamed" if 'dynamicStreams' in flashvars: @@ -55,13 +58,74 @@ def main(argv=None): filename = title+".flv" print Popen(["mplayer","-dumpstream","-dumpfile",filename, rtmp], stdout=PIPE).communicate()[0] if 'video' in flashvars: - url = sorted(flashvars['video']['videoReferences'], key=lambda k: k['bitrate'])[-1]['url'] - filename = title+".mp4" - print Popen(["rtmpdump",u"-o"+filename,"-r", url], stdout=PIPE).communicate()[0] - + for reference in flashvars['video']['videoReferences']: + if reference['url'].endswith("m3u8"): + url=reference['url'] + download_from_playlist(url, title+'.ts') else: print "Could not find any streams" return +def download_from_playlist(url, title): + playlist = parse_playlist(urllib2.urlopen(url).read()) + videourl = sorted(playlist, key=lambda k: int(k['BANDWIDTH']))[-1]['url'] + segments, metadata = parse_segment_playlist(urllib2.urlopen(videourl).read()) + if "EXT-X-KEY" in metadata: + key = urllib2.urlopen(metadata["EXT-X-KEY"]['URI'].strip('"')).read() + decrypt=True + else: + decrypt=False + with open("%s"%title,"w") as ofile: + segment=0 + for url in segments: + print "Downloading: %s"%(url) + ufile = urllib2.urlopen(url) + if decrypt: + iv=struct.pack("IIII",segment,0,0,0) + decryptor = AES.new(key, AES.MODE_CBC, iv) + while(True): + buf = ufile.read(1024) + if buf: + if decrypt: + buf = decryptor.decrypt(buf) + ofile.write(buf) + else: + ufile.close() + break + segment += 1 + +def parse_playlist(playlist): + assert playlist.startswith("#EXTM3U") + playlist = playlist.splitlines()[1:] + items=[] + for (metadata_string,url) in zip(playlist[0::2], playlist[1::2]): + md = dict() + assert 'EXT-X-STREAM-INF' in metadata_string.split(':')[0] + for item in metadata_string.split(':')[1].split(','): + if '=' in item: + md.update([item.split('='),]) + md['url']=url + items.append(md) + return items + +def parse_segment_playlist(playlist): + assert playlist.startswith("#EXTM3U") + PATTERN = re.compile(r'''((?:[^,"']|"[^"]*"|'[^']*')+)''') + segments = [] + next_is_url=False + metadata = {} + for row in playlist.splitlines(): + if next_is_url: + segments.append(row) + next_is_url=False + continue + if 'EXTINF' in row: + next_is_url=True + if "EXT-X-KEY" in row: + row = row.split(':',1)[1] #skip first part + parts = PATTERN.split(row)[1:-1] #do magic re split and keep quoting + metadata["EXT-X-KEY"] = dict([part.split('=',1) for part in parts if '=' in part]) #throw away the commas and make dict of the pairs + return(segments, metadata) + if __name__ == "__main__": sys.exit(main())