X-Git-Url: https://git.frykholm.com/svtplaydump.git/blobdiff_plain/2301fe1403fe109e85b66e4c6f5cbe6da8521b5f..8cf70eae2b3cda25f903d9e0de716e8024473709:/svtplaydump.py diff --git a/svtplaydump.py b/svtplaydump.py index 54d7ac0..8b4a3c2 100755 --- a/svtplaydump.py +++ b/svtplaydump.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python3 +#!/usr/bin/env python3.4 # -*- coding: utf-8 -*- # # (C) Copyright 2010 Mikael Frykholm @@ -33,6 +33,8 @@ import sys, os import socket import feedparser from datetime import datetime, timezone +from pathlib import Path + class Video(dict): def __init__(self, *args, **kwargs): self.update(dict(*args, **kwargs)) # use the free update to set keys @@ -62,7 +64,7 @@ def scrape_player_page(video): else: flashvars = requests.get("http://www.svtplay.se/%s"%video_player.attrs['data-json-href']+"?output=json").json() video['duration'] = video_player.attrs.get('data-length',0) - if not video['title']: + if not 'title' in video: video['title'] = soup.find('meta',{'property':'og:title'}).attrs['content'].replace('|','_').replace('/','_') if not 'genre' in video: if soup.find(text='Kategori:'): @@ -71,32 +73,61 @@ def scrape_player_page(video): video['genre'] = 'Ingen Genre' if 'dynamicStreams' in flashvars: video['url'] = flashvars['dynamicStreams'][0].split('url:')[1].split('.mp4,')[0] +'.mp4' - filename = video['title']+".mp4" + filename = Path(video['title']).with_suffix(".mp4") print(Popen(["rtmpdump","-o"+filename,"-r", url], stdout=PIPE).communicate()[0]) if 'pathflv' in flashvars: rtmp = flashvars['pathflv'][0] - filename = video['title']+".flv" + filename = Path(video['title']).with_suffix(".flv") print(Popen(["mplayer","-dumpstream","-dumpfile",filename, rtmp], stdout=PIPE).communicate()[0]) if not 'timestamp' in video: if soup.find_all(datetime=True): xmldate_str = soup.find_all(datetime=True)[0].attrs['datetime'] - video['timestamp'] = datetime(*feedparser._parse_date_w3dtf(xmldate_str)[:6]) #naive in utc - video['timestamp'] = video['timestamp'].replace(tzinfo=timezone.utc).astimezone(tz=None) #convert to local time + if xmldate_str: + video['timestamp'] = datetime(*feedparser._parse_date_w3dtf(xmldate_str)[:6]) #naive in utc + video['timestamp'] = video['timestamp'].replace(tzinfo=timezone.utc).astimezone(tz=None) #convert to local time if 'video' in flashvars: for reference in flashvars['video']['videoReferences']: if 'm3u8' in reference['url']: video['url']=reference['url'] - video['filename'] = video['title']+'.ts' + video['filename'] = Path(video['title']).with_suffix('.ts') if 'statistics' in flashvars: video['category'] = flashvars['statistics']['category'] - download_from_playlist(video) + if not download_from_playlist(video): + return False if not 'url' in video: print("Could not find any streams") return False return video def download_from_playlist(video): - playlist = parse_playlist(requests.get(video['url']).text) + params = requests.utils.urlparse(video['url']).query + print(params) + if 'cc1=' in params: #'cc1=name=Svenska~default=yes~forced=no~uri=http://media.svt.se/download/mcc/wp3/undertexter-wsrt/1134047/1134047-025A/C(sv)/index.m3u8~lang=sv' + video['subs'] = [dict([k.split('=') for k in params.split('cc1=')[1].split('~')])] #make a dict from the paramstring + try: + req = requests.get(video['url']).text + except: + print("Error reading, skipping file") + print(sys.exc_info()[1]) + return False + if 'subs' in video: + try: + segments = [item for item in requests.get(video['subs'][0]['uri']).text.split('\n') if 'vtt' in item] + except: + print("Error reading, skipping subtitle") + print(sys.exc_info()[1]) + segments = [] #ugly FIXME + video['subs'][0]['download'] = [] + for segment in segments: + if not segment.startswith('http'): + segment = "{}/{}".format(os.path.dirname(video['subs'][0]['uri']), segment) + try: + video['subs'][0]['download'].append(requests.get(segment).text) + except: + print("Error reading, skipping subtitle") + print(sys.exc_info()[1]) + break + playlist = parse_playlist(req) if not playlist: return videourl = sorted(playlist, key=lambda k: int(k['BANDWIDTH']))[-1]['url'] @@ -104,27 +135,42 @@ def download_from_playlist(video): videourl = "{}/{}".format(os.path.dirname(video['url']), videourl) segments, metadata = parse_segment_playlist(videourl) if "EXT-X-KEY" in metadata: - key = requests.get(metadata["EXT-X-KEY"]['URI'].strip('"')).text + try: + key = requests.get(metadata["EXT-X-KEY"]['URI'].strip('"')).text + except: + print("Error reading, skipping file") + print(sys.exc_info()[1]) + return False decrypt=True else: decrypt=False - with open("%s"%video['filename'],"wb") as ofile: + with video['filename'].open("wb") as ofile: segment=0 size = 0 for url in segments: - ufile = requests.get(url, stream=True).raw - print("\r{0:.2f} MB".format(size/1024/1024),end="") + try: + ufile = requests.get(url, stream=True).raw + except: + print("Error reading, skipping file") + print(sys.exc_info()[1]) + return False + print("\r{0:.2f} MB".format(size/1024/1024), end="") sys.stdout.flush() if decrypt: iv=struct.pack("IIII",segment,0,0,0) - decryptor = AES.new(key, AES.MODE_CBC, iv) + try: + decryptor = AES.new(key, AES.MODE_CBC, iv) #ValueError: AES key must be either 16, 24, or 32 bytes long + except(ValueError) as e: + print("Error using decryption key. Skipping") + print(e) + return False while(True): try: buf = ufile.read(4096) - except (socket.error, TypeError) as e: - print("Error reading, skipping file") - print(e) - return + except: + print("Error reading, skipping file") #FIXME mark file as failed + print(sys.exc_info()[1]) + return False if not buf: break if decrypt: @@ -134,7 +180,13 @@ def download_from_playlist(video): segment += 1 if 'thumb-url' in video: - video['thumb'] = requests.get(video['thumb-url'],stream=True).raw + try: + video['thumb'] = requests.get(video['thumb-url'],stream=True).raw + except: + print("Error reading thumbnail") #FIXME mark file as failed + print(sys.exc_info()[1]) + + return True def parse_playlist(playlist): if not playlist.startswith("#EXTM3U"): @@ -200,38 +252,44 @@ def parse_videolist(): page_num += 1 def remux(video, xml=None): - basename = video['filename'].split('.ts')[0] if 'genre' in video: if not os.path.exists(video['genre']): os.mkdir(video['genre']) - video['path'] = os.path.join(video['genre'],basename+'.mkv') + video['path'] = Path(video['genre'] / video['filename']).with_suffix('.mkv') else: - video['path'] = basename+'.mkv' - command = ["mkvmerge","-o",video['path'], '--title',video['title']] + video['path'] = video['filename'].with_suffix('.mkv') + command = ["mkvmerge","-o",str(video['path']), '--title',video['title']] if xml: - with open(basename+'.xml','w') as f: + with video['filename'].with_suffix('.xml').open('w') as f: f.write(xml) - command.extend(['--global-tags',basename+'.xml']) + command.extend(['--global-tags',str(video['filename'].with_suffix('.xml'))]) if 'thumb' in video: with open('thumbnail.jpg','wb') as f: #FIXME use title instead for many downloaders f.write(video['thumb'].read()) command.extend(['--attachment-description', "Thumbnail", '--attachment-mime-type', 'image/jpeg', '--attach-file', 'thumbnail.jpg']) - command.append(video['filename']) + # if 'subs' in video: + # for sub in video['subs']: + # if 'download' in sub: + # with open("{}.vtt".format(sub['lang']),'wb') as f: + # f.write(bytes("".join(sub['download']),'utf-8')) #FIXME + # command.extend(['--language 0:{} {}.vtt'.format(sub['lang'],sub['lang'])]) + + command.append(str(video['filename'])) print(Popen(command, stdout=PIPE).communicate()[0]) - for fname in (video['filename'], basename+'.xml','thumbnail.jpg'): + for fname in (video['filename'], video['filename'].with_suffix('.xml'),Path('thumbnail.jpg')): try: - os.unlink(fname) + fname.unlink() except: pass if 'timestamp' in video: try: - os.utime(video['path'], times=(video['timestamp'].timestamp(),video['timestamp'].timestamp())) + os.utime(str(video['path']), times=(video['timestamp'].timestamp(),video['timestamp'].timestamp())) except FileNotFoundError as e: print(e) - + def mkv_metadata(video): root = BeautifulSoup(features='xml') @@ -295,7 +353,13 @@ if __name__ == "__main__": if args.no_act: continue open(os.path.join('.seen',video['title']),'w').close() #touch - video = scrape_player_page(video) + ret = scrape_player_page(video) + if not ret: + if not os.path.exists('.failed'): + os.mkdir('.failed') + open(os.path.join('.failed',video['title']),'w').close() #touch + continue + video = ret if args.no_remux: continue xml = mkv_metadata(video) @@ -305,5 +369,5 @@ if __name__ == "__main__": if not args.no_act: video = scrape_player_page({'url':args.url}) if not args.no_remux: - remux({'title':e.title}) + remux(video) print(("Downloaded {}".format(args.url)))