1
0
mirror of https://github.com/spaam/svtplay-dl.git synced 2024-11-24 12:15:40 +01:00
svtplay-dl/svtplay_dl.py
Markus Helgesson 0f71269e65 Restructure so the script to be included and used from other Python scripts.
* Renamed 'svtplay-dl' to 'svtplay_dl.py' which conforms with the Python
  guidelines and can be imported from other scripts (names with '-' cannot).
* Created the wrapper 'svtplay-dl' for backward compatability.
* Broke out the major parts of main() into get_media()
* New Options class corresponding to the command line parameters.
* Modified how the log used and progress information written so they can be
  easily configured by the caller.
2012-12-23 16:52:07 +01:00

1256 lines
40 KiB
Python
Executable File

#!/usr/bin/env python
import sys
if sys.version_info > (3, 0):
from urllib.request import Request, urlopen
from urllib.error import HTTPError, URLError
from urllib.parse import urlparse, parse_qs, unquote_plus, quote_plus
else:
from urllib2 import Request, urlopen, HTTPError, URLError
from urlparse import urlparse, parse_qs
from urllib import unquote_plus, quote_plus
import re
import os
import subprocess
from optparse import OptionParser
import xml.etree.ElementTree as ET
import shlex
import json
import time
import logging
import base64
import struct
import binascii
import StringIO
import hashlib
__version__ = "0.8.2012.12.23"
class Options:
"""
Options used when invoking the script from another Python script.
Simple container class used when calling get_media() from another Python
script. The variables corresponds to the command line parameters parsed
in main() when the script is called directly.
When called from a script there are a few more things to consider:
* Logging is done to 'log'. main() calls setup_log() which sets the
logging to either stdout or stderr depending on the silent level.
A user calling get_media() directly can either also use setup_log()
or configure the log manually.
* Progress information is printed to 'progress_stream' which defaults to
sys.stderr but can be changed to any stream.
* Many errors results in calls to system.exit() so catch 'SystemExit'-
Exceptions to prevent the entire application from exiting if that happens.
"""
def __init__(self):
self.output = None
self.resume = False
self.live = False
self.silent = False
self.quality = None
self.hls = False
log = logging.getLogger('svtplay_dl')
progress_stream = sys.stderr
def readbyte(data, pos):
return struct.unpack("B", data[pos])[0]
def read16(data, pos):
endpos = pos + 2
return struct.unpack(">H", data[pos:endpos])[0]
def read24(data, pos):
end = pos + 3
return struct.unpack(">L", "\x00" + data[pos:end])[0]
def read32(data, pos):
end = pos + 4
return struct.unpack(">i", data[pos:end])[0]
def read64(data, pos):
end = pos + 8
return struct.unpack(">Q", data[pos:end])[0]
def readstring(data, pos):
length = 0
while (data[pos + length] != "\x00"):
length += 1
endpos = pos + length
string = data[pos:endpos]
pos += length + 1
return pos, string
def readboxtype(data, pos):
boxsize = read32(data, pos)
tpos = pos + 4
endpos = tpos + 4
boxtype = data[tpos:endpos]
if boxsize > 1:
boxsize -= 8
pos += 8
return pos, boxsize, boxtype
def readbox(data, pos):
version = readbyte(data, pos)
pos += 1
flags = read24(data, pos)
pos += 3
bootstrapversion = read32(data, pos)
pos += 4
byte = readbyte(data, pos)
pos += 1
profile = (byte & 0xC0) >> 6
live = (byte & 0x20) >> 5
update = (byte & 0x10) >> 4
timescale = read32(data, pos)
pos += 4
currentmediatime = read64(data, pos)
pos += 8
smptetimecodeoffset = read64(data, pos)
pos += 8
temp = readstring(data, pos)
movieidentifier = temp[1]
pos = temp[0]
serverentrycount = readbyte(data, pos)
pos += 1
serverentrytable = []
i = 0
while i < serverentrycount:
temp = readstring(data, pos)
serverentrytable.append(temp[1])
pos = temp[0]
i += 1
qualityentrycount = readbyte(data, pos)
pos += 1
qualityentrytable = []
i = 0
while i < qualityentrycount:
temp = readstring(data, pos)
qualityentrytable.append(temp[1])
pos = temp[0]
i += 1
tmp = readstring(data, pos)
drm = tmp[1]
pos = tmp[0]
tmp = readstring(data, pos)
metadata = tmp[1]
pos = tmp[0]
segmentruntable = readbyte(data, pos)
pos += 1
if segmentruntable > 0:
tmp = readboxtype(data, pos)
boxtype = tmp[2]
boxsize = tmp[1]
pos = tmp[0]
if boxtype == "asrt":
antal = readasrtbox(data, pos)
pos += boxsize
fragRunTableCount = readbyte(data, pos)
pos += 1
i = 0
while i < fragRunTableCount:
tmp = readboxtype(data, pos)
boxtype = tmp[2]
boxsize = tmp[1]
pos = tmp[0]
if boxtype == "afrt":
readafrtbox(data, pos)
pos += boxsize
i += 1
return antal
def readafrtbox(data, pos):
version = readbyte(data, pos)
pos += 1
flags = read24(data, pos)
pos += 3
timescale = read32(data, pos)
pos += 4
qualityentry = readbyte(data, pos)
pos += 1
i = 0
while i < qualityentry:
temp = readstring(data, pos)
qualitysegmulti = temp[1]
pos = temp[0]
i += 1
fragrunentrycount = read32(data, pos)
pos += 4
i = 0
while i < fragrunentrycount:
firstfragment = read32(data, pos)
pos += 4
timestamp = read64(data, pos)
pos += 8
duration = read32(data, pos)
pos += 4
i += 1
def readasrtbox(data, pos):
version = readbyte(data, pos)
pos += 1
flags = read24(data, pos)
pos += 3
qualityentrycount = readbyte(data, pos)
pos += 1
qualitysegmentmodifers = []
i = 0
while i < qualityentrycount:
temp = readstring(data, pos)
qualitysegmentmodifers.append(temp[1])
pos = temp[0]
i += 1
seqCount = read32(data, pos)
pos += 4
ret = {}
i = 0
while i < seqCount:
firstseg = read32(data, pos)
pos += 4
fragPerSeg = read32(data, pos)
pos += 4
tmp = i + 1
ret[tmp] = {"first": firstseg, "total": fragPerSeg}
i += 1
return ret
def parsem3u(data):
if not data.startswith("#EXTM3U"):
raise ValueError("Does not apprear to be a ext m3u file")
files=[]
streaminfo={}
globdata={}
data=data.replace("\r","\n")
for l in data.split("\n")[1:]:
if not l:
continue
if l.startswith("#EXT-X-STREAM-INF:"):
#not a proper parser
info = [x.strip().split("=",1) for x in l[18:].split(",")]
streaminfo.update({info[1][0]: info[1][1]})
elif l.startswith("#EXT-X-ENDLIST"):
break
elif l.startswith("#EXT-X-"):
globdata.update(dict([l[7:].strip().split(":",1)]))
elif l.startswith("#EXTINF:"):
dur, title=l[8:].strip().split(",",1)
streaminfo['duration'] = dur
streaminfo['title'] = title
elif l[0]=='#':
pass
else:
files.append((l, streaminfo))
streaminfo={}
return globdata,files
def decode_f4f(fragID, fragData ):
start = fragData.find( "mdat" ) + 4
if (fragID > 1):
for dummy in range( 2 ):
tagLen, = struct.unpack_from( ">L", fragData, start )
tagLen &= 0x00ffffff
start += tagLen + 11 + 4
return start
def get_http_data(url, method="GET", header="", data=""):
""" Get the page to parse it for streams """
request = Request(url)
request.add_header('User-Agent', 'Mozilla/5.0 (Windows; U; Windows NT 5.1; en-GB; rv:1.9.0.3) Gecko/2008092417 Firefox/3.0.3')
if len(header) > 0:
request.add_header('Content-Type', header)
if len(data) > 0:
request.add_data(data)
try:
response = urlopen(request)
except HTTPError as e:
logging.error("Something wrong with that url")
logging.error("Error code: %s" % e.code)
sys.exit(5)
except URLError as e:
logging.error("Something wrong with that url")
logging.error("Error code: %s" % e.reason)
sys.exit(5)
except ValueError as e:
logging.error("Try adding http:// before the url")
sys.exit(5)
if sys.version_info > (3, 0):
data = response.read().decode('utf-8')
else:
try:
data = response.read()
except socket.error as e:
logging.error("Lost the connection to the server")
sys.exit(5)
response.close()
return data
def progress(byte, total):
""" Print some info about how much we have downloaded """
ratio = float(byte) / total
percent = round(ratio*100, 2)
tlen = str(len(str(total)))
fmt = "Downloaded %"+tlen+"dkB of %dkB bytes (% 3.2f%%)"
progresstr = fmt % (byte >> 10, total >> 10, percent)
columns = int(os.getenv("COLUMNS", "80"))
if len(progresstr) < columns - 13:
p = int((columns - len(progresstr) - 3) * ratio)
q = int((columns - len(progresstr) - 3) * (1 - ratio))
progresstr = "[" + ("#" * p) + (" " * q) + "] " + progresstr
progress_stream.write(progresstr + '\r')
if byte >= total:
progress_stream.write('\n')
progress_stream.flush()
def download_hds(options, url, output, swf):
data = get_http_data(url)
streams = {}
bootstrap = {}
xml = ET.XML(data)
prefix = xml.find("{http://ns.adobe.com/f4m/1.0}id").text
if sys.version_info < (2, 7):
bootstrapIter = xml.getiterator("{http://ns.adobe.com/f4m/1.0}bootstrapInfo")
mediaIter = xml.getiterator("{http://ns.adobe.com/f4m/1.0}media")
else:
bootstrapIter = xml.iter("{http://ns.adobe.com/f4m/1.0}bootstrapInfo")
mediaIter = xml.iter("{http://ns.adobe.com/f4m/1.0}media")
for i in bootstrapIter:
bootstrap[i.attrib["id"]] = i.text
for i in mediaIter:
streams[int(i.attrib["bitrate"])] = {"url": i.attrib["url"], "bootstrapInfoId": i.attrib["bootstrapInfoId"], "metadata": i.find("{http://ns.adobe.com/f4m/1.0}metadata").text}
test = select_quality(options, streams)
bootstrap = base64.b64decode(bootstrap[test["bootstrapInfoId"]])
box = readboxtype(bootstrap, 0)
if box[2] == "abst":
antal = readbox(bootstrap, box[0])
baseurl = url[0:url.rfind("/")]
i = 1
if output != "-":
extension = re.search("(\.[a-z0-9]+)$", output)
if not extension:
output = output + ".flv"
file_d = open(output, "wb")
else:
file_d = sys.stdout
file_d.write(binascii.a2b_hex( "464c56010500000009000000001200010c00000000000000" ) )
file_d.write(base64.b64decode(test["metadata"]))
file_d.write(binascii.a2b_hex("00000000"))
while i <= antal[1]["total"]:
url = "%s/%sSeg1-Frag%s" % (baseurl, test["url"], i)
if output != "-":
progressbar(antal[1]["total"], i)
data = get_http_data(url)
number = decode_f4f(i, data)
file_d.write(data[number:])
i += 1
if output != "-":
file_d.close()
progress_stream.write('\n')
def download_hls(options, url, output, live, other):
data = get_http_data(url)
globaldata, files = parsem3u(data)
streams ={}
for i in files:
streams[int(i[1]["BANDWIDTH"])] = i[0]
test = select_quality(options, streams)
m3u8 = get_http_data(test)
globaldata, files = parsem3u(m3u8)
encrypted = False
key = None
try:
keydata = globaldata["KEY"]
encrypted = True
match = re.search("URI=\"(http://.*)\"", keydata)
key = get_http_data(match.group(1))
rand = os.urandom(16)
except:
pass
try:
from Crypto.Cipher import AES
decryptor = AES.new(key, AES.MODE_CBC, rand)
except ImportError:
logging.error("You need to install pycrypto to download encrypted HLS streams")
sys.exit(2)
n=1
if output != "-":
file_d = open(output + ".ts", "wb")
else:
file_d = sys.stdout
for i in files:
if output != "-":
progressbar(len(files), n)
data = get_http_data(i[0])
if encrypted:
lots = StringIO.StringIO(data)
plain = ""
crypt = lots.read(1024)
decrypted = decryptor.decrypt(crypt)
while decrypted:
plain += decrypted
crypt = lots.read(1024)
decrypted = decryptor.decrypt(crypt)
data = plain
file_d.write(data)
n += 1
if output != "-":
file_d.close()
progress_stream.write('\n')
def download_http(url, output):
""" Get the stream from HTTP """
response = urlopen(url)
total_size = response.info()['Content-Length']
total_size = int(total_size)
bytes_so_far = 0
if output != "-":
extension = re.search("(\.[a-z0-9]+)$", url)
if extension:
output = output + extension.group(1)
file_d = open(output,"wb")
else:
file_d = sys.stdout
lastprogress = 0
while 1:
chunk = response.read(8192)
bytes_so_far += len(chunk)
if not chunk:
break
file_d.write(chunk)
if output != "-":
now = time.time()
if lastprogress + 1 < now:
lastprogress = now
progress(bytes_so_far, total_size)
if output != "-":
file_d.close()
def download_rtmp(options, url, output, live, extra_args, resume):
""" Get the stream from RTMP """
args = []
if live:
args.append("-v")
if resume:
args.append("-e")
extension = re.search("(\.[a-z0-9]+)$", url)
if output != "-":
if not extension:
extension = re.search("-y (.+):[-_a-z0-9\/]", extra_args)
if not extension:
output = output + ".flv"
else:
output = output + "." + extension.group(1)
else:
output = output + extension.group(1)
args += ["-o", output]
if options.silent or output == "-":
args.append("-q")
if extra_args:
args += shlex.split(extra_args)
command = ["rtmpdump", "-r", url] + args
try:
subprocess.call(command)
except OSError as e:
logging.error("Could not execute rtmpdump: " + e.strerror)
def select_quality(options, streams):
sort = sorted(streams.keys(), key=int)
if options.quality:
quality = options.quality
else:
quality = sort.pop()
try:
selected = streams[int(quality)]
except (KeyError, ValueError):
logging.error("Can't find that quality. (Try one of: %s)",
", ".join(map(str, sort)))
sys.exit(4)
return selected
class Justin():
def __init__(self, options, output, live, resume):
self.options = options
self.output = output
self.live = live
self.resume = resume
def get(self, url):
other = "-a ondemand"
data = get_http_data(url)
data = re.sub("<(\d+)", "<_\g<1>", data)
data = re.sub("</(\d+)", "</_\g<1>", data)
xml = ET.XML(data)
if sys.version_info < (2, 7):
sa = list(xml)
else:
sa = list(xml)
streams = {}
for i in sa:
if i.tag[1:][:-1] != "iv":
try:
stream = {}
stream["token"] = i.find("token").text
stream["url"] = i.find("connect").text + "/" + i.find("play").text
streams[int(i.find("video_height").text)] = stream
except AttributeError:
None
test = select_quality(self.options, streams)
other = "-j '%s' -W %s" % (test["token"], self.resume)
self.resume = False
download_rtmp(self.options, test["url"], self.output, self.live, other, self.resume)
class Justin2():
def __init__(self, options, output, live, resume):
self.options = options
self.output = output
self.live = live
self.resume = resume
def get(self, url):
other = "-a ondemand"
data = get_http_data(url)
xml = ET.XML(data)
url = xml.find("archive").find("video_file_url").text
if self.output != '-':
self.output = self.output + url[-4:]
logging.info("Outfile: %s", self.output)
download_http(url, self.output)
class Hbo():
def __init__(self, options, output, live, resume):
self.options = options
self.output = output
self.live = live
self.resume = resume
def get(self, url):
data = get_http_data(url)
xml = ET.XML(data)
videoid = xml.find("content")[1].find("videoId").text
url = "http://render.cdn.hbo.com/data/content/global/videos/data/%s.xml" % videoid
data = get_http_data(url)
xml = ET.XML(data)
ss = xml.find("videos")
if sys.version_info < (2, 7):
sa = list(ss.getiterator("size"))
else:
sa = list(ss.iter("size"))
streams = {}
for i in sa:
stream = {}
stream["path"] = i.find("tv14").find("path").text
streams[int(i.attrib["width"])] = stream
test = select_quality(self.options, streams)
if not self.output:
self.output = os.path.basename(test["path"])
logging.info("Outfile: %s", self.output)
download_rtmp(self.options, test["path"], self.output, self.live, "", self.resume)
class Sr():
def __init__(self, options, output, live, other, resume):
self.options = options
self.output = output
self.live = live
self.other = other
self.resume = resume
def get(self, url):
url = url + self.other
data = get_http_data(url)
xml = ET.XML(data)
url = xml.find("entry").find("ref").attrib["href"]
if not self.output:
self.output = os.path.basename(url)
logging.info("Outfile: %s", self.output)
download_http(url, self.output)
class Urplay():
def __init__(self, options, output, live, resume):
self.options = options
self.output = output
self.live = live
self.resume = resume
def get(self, url):
other = "-a ondemand"
data = get_http_data(url)
match = re.search('file=(.*)\&plugins', data)
if match:
path = "mp" + match.group(1)[-1] + ":" + match.group(1)
other = "-a ondemand -y %s" % path
if not self.output:
self.output = os.path.basename(path)
logging.info("Outfile: %s", self.output)
download_rtmp(self.options, "rtmp://streaming.ur.se/", self.output, self.live, other, self.resume)
class Qbrick():
def __init__(self, options, output, live, resume):
self.options = options
self.output = output
self.live = live
self.resume = resume
def get(self, url):
data = get_http_data(url)
xml = ET.XML(data)
server = xml.find("head").find("meta").attrib["base"]
streams = xml.find("body").find("switch")
if sys.version_info < (2, 7):
sa = list(streams.getiterator("video"))
else:
sa = list(streams.iter("video"))
streams = {}
for i in sa:
streams[int(i.attrib["system-bitrate"])] = i.attrib["src"]
path = select_quality(self.options, streams)
if not self.output:
self.output = os.path.basename(path)
logging.info("Outfile: %s", self.output)
other = "-y %s" % path
download_rtmp(self.options, server, self.output, self.live, other, self.resume)
class Kanal5():
def __init__(self, options, output, live, resume):
self.options = options
self.output = output
self.live = live
self.resume = resume
def get(self, url):
data = json.loads(get_http_data(url))
self.live = data["isLive"]
steambaseurl = data["streamBaseUrl"]
streams = {}
for i in data["streams"]:
stream = {}
stream["source"] = i["source"]
streams[int(i["bitrate"])] = stream
test = select_quality(self.options, streams)
if not self.output:
self.output = os.path.basename(test["source"])
logging.info("Outfile: %s", self.output)
filename = test["source"]
match = re.search("^(.*):", filename)
self.output = "%s.%s" % (self.output, match.group(1))
other = "-W %s -y %s " % ("http://www.kanal5play.se/flash/StandardPlayer.swf", filename)
download_rtmp(self.options, steambaseurl, self.output, self.live, other, self.resume)
class Kanal9():
def __init__(self, options, output, live, other, resume):
self.options = options
self.output = output
self.live = live
self.other = other
self.resume = resume
def get(self, url):
try:
from pyamf import remoting
except ImportError:
logging.error("You need to install pyamf to download content from kanal5 and kanal9")
logging.info("In debian the package is called python-pyamf")
sys.exit(2)
player_id = 811317479001
publisher_id = 22710239001
const = "9f79dd85c3703b8674de883265d8c9e606360c2e"
env = remoting.Envelope(amfVersion=3)
env.bodies.append(("/1", remoting.Request(target="com.brightcove.player.runtime.PlayerMediaFacade.findMediaById", body=[const, player_id, self.other, publisher_id], envelope=env)))
env = str(remoting.encode(env).read())
url = "http://" + url + "/services/messagebroker/amf?playerKey=AQ~~,AAAABUmivxk~,SnCsFJuhbr0vfwrPJJSL03znlhz-e9bk"
header = "application/x-amf"
data = get_http_data(url, "POST", header, env)
streams = {}
for i in remoting.decode(data).bodies[0][1].body['renditions']:
stream = {}
stream["uri"] = i["defaultURL"]
streams[i["encodingRate"]] = stream
test = select_quality(self.options, streams)
if not self.output:
self.output = os.path.basename(test["uri"])
logging.info("Outfile: %s", self.output)
filename = test["uri"]
match = re.search("(rtmp[e]{0,1}://.*)\&(.*)$", filename)
other = "-W %s -y %s " % ("http://admin.brightcove.com/viewer/us1.25.04.01.2011-05-24182704/connection/ExternalConnection_2.swf", match.group(2))
download_rtmp(self.options, match.group(1), self.output, self.live, other, self.resume)
class Expressen():
def __init__(self, options, output, live, resume):
self.options = options
self.output = output
self.live = live
self.resume = resume
def get(self, url):
other = ""
data = get_http_data(url)
xml = ET.XML(data)
ss = xml.find("vurls")
if sys.version_info < (2, 7):
sa = list(ss.getiterator("vurl"))
else:
sa = list(ss.iter("vurl"))
streams = {}
for i in sa:
streams[int(i.attrib["bitrate"])] = i.text
test = select_quality(self.options, streams)
if not self.output:
self.output = os.path.basename(test)
logging.info("Outfile: %s", self.output)
filename = test
match = re.search("rtmp://([0-9a-z\.]+/[0-9]+/)(.*).flv", filename)
filename = "rtmp://%s" % match.group(1)
other = "-y %s" % match.group(2)
download_rtmp(self.options, filename, self.output, self.live, other, self.resume)
class Aftonbladet():
def __init__(self, options, output, live, resume):
self.options = options
self.output = output
self.live = live
self.resume = resume
def get(self, url, start):
data = get_http_data(url)
xml = ET.XML(data)
url = xml.find("articleElement").find("mediaElement").find("baseUrl").text
path = xml.find("articleElement").find("mediaElement").find("media").attrib["url"]
other = "-y %s" % path
if start > 0:
other = other + " -A %s" % str(start)
if url == None:
logging.error("Can't find any video on that page")
sys.exit(3)
if not self.output:
self.output = os.path.basename(path)
logging.info("Outfile: %s", self.output)
if url[0:4] == "rtmp":
download_rtmp(self.options, url, self.output, self.live, other, self.resume)
else:
filename = url + path
download_http(filename, self.output)
class Viaplay():
def __init__(self, options, output, live, resume):
self.options = options
self.output = output
self.live = live
self.resume = resume
def get(self, url):
other = ""
data = get_http_data(url)
xml = ET.XML(data)
filename = xml.find("Product").find("Videos").find("Video").find("Url").text
if filename[:4] == "http":
data = get_http_data(filename)
xml = ET.XML(data)
filename = xml.find("Url").text
if not self.output:
self.output = os.path.basename(filename)
logging.info("Outfile: %s", self.output)
other = "-W http://flvplayer.viastream.viasat.tv/play/swf/player110516.swf?rnd=1315434062"
download_rtmp(self.options, filename, self.output, self.live, other, self.resume)
class Tv4play():
def __init__(self, options, output, live, resume):
self.options = options
self.output = output
self.live = live
self.resume = resume
def get(self, url):
data = get_http_data(url)
xml = ET.XML(data)
ss = xml.find("items")
if sys.version_info < (2, 7):
sa = list(ss.getiterator("item"))
else:
sa = list(ss.iter("item"))
streams = {}
sa.pop(len(sa)-1)
for i in sa:
stream = {}
stream["uri"] = i.find("base").text
stream["path"] = i.find("url").text
streams[int(i.find("bitrate").text)] = stream
test = select_quality(self.options, streams)
other = "-W http://www.tv4play.se/flash/tv4playflashlets.swf -y %s" % test["path"]
if not self.output:
self.output = os.path.basename(test["path"])
logging.info("Outfile: %s", self.output)
download_rtmp(self.options, test["uri"], self.output, self.live, other, self.resume)
class Svtplay():
def __init__(self, options, output, live, resume):
self.options = options
self.output = output
self.live = live
self.resume = resume
def get(self, url):
url = url + "?type=embed"
data = get_http_data(url)
match = re.search("value=\"(/public/swf/video/svtplayer-[0-9\.]+swf)\"", data)
swf = "http://www.svtplay.se" + match.group(1)
other = "-W " + swf
url = url + "&output=json"
data = json.loads(get_http_data(url))
self.live = data["video"]["live"]
streams = {}
for i in data["video"]["videoReferences"]:
if self.options.hls and i["playerType"] == "ios":
stream = {}
stream["url"] = i["url"]
streams[int(i["bitrate"])] = stream
elif not self.options.hls and i["playerType"] == "flash":
stream = {}
stream["url"] = i["url"]
streams[int(i["bitrate"])] = stream
if len(streams) == 1:
test = streams[streams.keys()[0]]
else:
test = select_quality(self.options, streams)
if test["url"][0:4] == "rtmp":
download_rtmp(self.options, test["url"], self.output, self.live, other, self.resume)
elif self.options.hls:
download_hls(self.options, test["url"], self.output, self.live, other)
elif test["url"][len(test["url"])-3:len(test["url"])] == "f4m":
match = re.search("\/se\/secure\/", test["url"])
if match:
logging.error("This stream is encrypted. Use --hls option")
sys.exit(2)
manifest = "%s?hdcore=2.8.0&g=hejsan" % test["url"]
download_hds(self.options, manifest, self.output, swf)
else:
download_http(test["url"], self.output)
class Svt():
def __init__(self, options, output, live, resume):
self.options = options
self.output = output
self.live = live
self.resume = resume
def get(self, url):
other = "-W http://svtplay.se/flash/svtplayer-2012.28.swf"
data = get_http_data(url)
match = re.search('SgFlashVideoMeta (.*)\" href=\"http\:\/', data)
if match:
new = match.group(1)
tmp = new.split("|")
streams = {}
for f in tmp:
match = re.search('url:(.*)\,bitrate:([0-9]+)', f)
streams[int(match.group(2))] = match.group(1)
filename = select_quality(self.options, streams)
else:
match = re.search('pathflv=(.*)\&amp\;background', data)
if not match:
logging.error("Can't find streams")
sys.exit(3)
filename = match.group(1)
if not self.output:
self.output = os.path.basename(filename)
logging.info("Outfile: %s", self.output)
if filename[0:4] == "rtmp":
download_rtmp(self.options, filename, self.output, self.live, other, self.resume)
else:
download_http(filename, self.output)
def progressbar(total, pos, msg=""):
"""
Given a total and a progress position, output a progress bar
to stderr. It is important to not output anything else while
using this, as it relies soley on the behavior of carriage
return (\\r).
Can also take an optioal message to add after the
progressbar. It must not contain newliens.
The progress bar will look something like this:
[099/500][=========...............................] ETA: 13:36:59
Of course, the ETA part should be supplied be the calling
function.
"""
width = 50 # TODO hardcoded progressbar width
rel_pos = int(float(pos)/total*width)
bar = str()
# FIXME ugly generation of bar
for i in range(0, rel_pos):
bar += "="
for i in range(rel_pos, width):
bar += "."
# Determine how many digits in total (base 10)
digits_total = len(str(total))
fmt_width = "%0" + str(digits_total) + "d"
fmt = "\r[" + fmt_width + "/" + fmt_width + "][%s] %s"
progress_stream.write(fmt % (pos, total, bar, msg))
def get_media(url, options):
output = options.output
live = options.live
resume = options.resume
silent = options.silent
hls = options.hls
if not output:
data = get_http_data(url)
match = re.search("(?i)<title>\s*(.*?)\s*</title>", data)
if match:
if sys.version_info > (3, 0):
title = re.sub('[^\w\s-]', '', match.group(1)).strip().lower()
output = re.sub('[-\s]+', '-', title)
else:
title = unicode(re.sub('[^\w\s-]', '', match.group(1)).strip().lower())
output = unicode(re.sub('[-\s]+', '-', title))
if re.findall("(twitch|justin).tv", url):
parse = urlparse(url)
match = re.search("/b/(\d+)", parse.path)
if match:
url = "http://api.justin.tv/api/broadcast/by_archive/%s.xml?onsite=true" % match.group(1)
justin = Justin2(options, output, live, resume)
justin.get(url)
else:
match = re.search("/(.*)", parse.path)
if match:
user = match.group(1)
data = get_http_data(url)
match = re.search("embedSWF\(\"(.*)\", \"live", data)
if not match:
log.error("Can't find swf file.")
other = match.group(1)
url = "http://usher.justin.tv/find/%s.xml?type=any&p=2321" % user
live = True
justin = Justin(options, output, live, other)
justin.get(url)
if re.findall("hbo.com", url):
parse = urlparse(url)
try:
other = parse[5]
except KeyError:
log.error("Something wrong with that url")
sys.exit(2)
match = re.search("^/(.*).html", other)
if not match:
log.error("Cant find video file")
sys.exit(2)
url = "http://www.hbo.com/data/content/" + match.group(1) + ".xml"
hbo = Hbo(options, output, live, resume)
hbo.get(url)
if re.findall("tv4play", url):
parse = urlparse(url)
try:
vid = parse_qs(parse[4])["video_id"][0]
except KeyError:
log.error("Can't find video file")
sys.exit(2)
url = "http://premium.tv4play.se/api/web/asset/%s/play" % vid
tv4play = Tv4play(options, output, live, resume)
tv4play.get(url)
elif re.findall("(tv3play|tv6play|tv8play)", url):
parse = urlparse(url)
match = re.search('\/play\/(.*)/?', parse.path)
if not match:
log.error("Cant find video file")
sys.exit(2)
url = "http://viastream.viasat.tv/PlayProduct/%s" % match.group(1)
viaplay = Viaplay(options, output, live, resume)
viaplay.get(url)
elif re.findall("viaplay", url):
parse = urlparse(url)
match = re.search('\/Tv/channels\/[a-zA-Z0-9-]+\/[a-zA-Z0-9-]+\/[a-zA-Z0-9-]+\/(.*)/', parse.path)
if not match:
log.error("Can't find video file")
sys.exit(2)
url = "http://viasat.web.entriq.net/nw/article/view/%s/?tf=players/TV6.tpl" % match.group(1)
data = get_http_data(url)
match = re.search("id:'(.*)'", data)
if not match:
log.error("Can't find video file")
sys.exit(2)
url = "http://viastream.viasat.tv/PlayProduct/%s" % match.group(1)
viaplay = Viaplay(options, output, live, resume)
viaplay.get(url)
elif re.findall("aftonbladet", url):
parse = urlparse(url)
data = get_http_data(url)
match = re.search("abTvArticlePlayer-player-(.*)-[0-9]+-[0-9]+-clickOverlay", data)
if not match:
log.error("Can't find video file")
sys.exit(2)
try:
start = parse_qs(parse[4])["start"][0]
except KeyError:
start = 0
url = "http://www.aftonbladet.se/resource/webbtv/article/%s/player" % match.group(1)
aftonbladet = Aftonbladet(options, output, live, resume)
aftonbladet.get(url, start)
elif re.findall("expressen", url):
parse = urlparse(url)
match = re.search("/(.*[\/\+].*)/", unquote_plus(parse.path))
if not match:
log.error("Can't find video file")
sys.exit(2)
url = "http://tv.expressen.se/%s/?standAlone=true&output=xml" % quote_plus(match.group(1))
expressen = Expressen(options, output, live, resume)
expressen.get(url)
elif re.findall("kanal5play", url):
match = re.search(".*video/([0-9]+)", url)
if not match:
log.error("Can't find video file")
sys.exit(2)
url = "http://www.kanal5play.se/api/getVideo?format=FLASH&videoId=%s" % match.group(1)
kanal5 = Kanal5(options, output, live, resume)
kanal5.get(url)
elif re.findall("kanal9play", url):
data = get_http_data(url)
match = re.search("@videoPlayer\" value=\"(.*)\"", data)
if not match:
log.error("Can't find video file")
sys.exit(2)
kanal9 = Kanal9(options, output, live, match.group(1), resume)
kanal9.get("c.brightcove.com")
elif re.findall("dn.se", url):
data = get_http_data(url)
match = re.search("data-qbrick-mcid=\"([0-9A-F]+)\"", data)
if not match:
match = re.search("mediaId = \'([0-9A-F]+)\';", data)
if not match:
log.error("Can't find video file")
sys.exit(2)
mcid = match.group(1) + "DE1BA107"
else:
mcid = match.group(1)
qbrick = Qbrick(options, output, live, resume)
host = "http://vms.api.qbrick.com/rest/v3/getsingleplayer/" + mcid
data = get_http_data(host)
xml = ET.XML(data)
try:
url = xml.find("media").find("item").find("playlist").find("stream").find("format").find("substream").text
except AttributeError:
log.error("Can't find video file")
sys.exit(2)
qbrick.get(url)
elif re.findall("di.se", url):
data = get_http_data(url)
match = re.search("ccid: \"(.*)\"\,", data)
if not match:
log.error("Can't find video file")
sys.exit(2)
qbrick = Qbrick(options, output, live, resume)
host = "http://vms.api.qbrick.com/rest/v3/getplayer/" + match.group(1)
data = get_http_data(host)
xml = ET.XML(data)
try:
host = xml.find("media").find("item").find("playlist").find("stream").find("format").find("substream").text
except AttributeError:
log.error("Can't find stream")
sys.exit(2)
qbrick.get(host)
elif re.findall("svd.se", url):
match = re.search("_([0-9]+)\.svd", url)
if not match:
log.error("Can't find video file")
sys.exit(2)
data = get_http_data("http://www.svd.se/?service=ajax&type=webTvClip&articleId=" + match.group(1))
match = re.search("mcid=([A-F0-9]+)\&width=", data)
if not match:
log.error("Can't find video file")
sys.exit(2)
qbrick = Qbrick(options, output, live, resume)
host = "http://vms.api.qbrick.com/rest/v3/getsingleplayer/" + match.group(1)
data = get_http_data(host)
xml = ET.XML(data)
try:
host = xml.find("media").find("item").find("playlist").find("stream").find("format").find("substream").text
except AttributeError:
log.error("Can't find video file")
sys.exit(2)
qbrick.get(host)
elif re.findall("urplay.se", url):
urplay = Urplay(options, output, live, resume)
urplay.get(url)
elif re.findall("sverigesradio", url):
data = get_http_data(url)
parse = urlparse(url)
try:
metafile = parse_qs(parse[4])["metafile"][0]
other = "%s?%s" % (parse[2], parse[4])
except KeyError:
match = re.search("linkUrl=(.*)\;isButton=", data)
if not match:
log.error("Can't find video file")
sys.exit(2)
other = unquote_plus(match.group(1))
sr = Sr(options, output, live, other, resume)
sr.get("http://sverigesradio.se")
elif re.findall("svt.se", url):
svt = Svt(options, output, live, resume)
svt.get(url)
elif re.findall("beta.svtplay.se", url):
svtplay = Svtplay(options, output, live, resume)
svtplay.get(url)
elif re.findall("svtplay.se", url):
svtplay = Svtplay(options, output, live, resume)
svtplay.get(url)
else:
log.error("That site is not supported. Make a ticket or send a message")
sys.exit(2)
def setup_log(silent):
if silent:
stream = sys.stderr
level = logging.WARNING
else:
stream = sys.stdout
level = logging.INFO
fmt = logging.Formatter('%(levelname)s %(message)s')
hdlr = logging.StreamHandler(stream)
hdlr.setFormatter(fmt)
log.addHandler(hdlr)
log.setLevel(level)
def main():
""" Main program """
usage = "usage: %prog [options] url"
parser = OptionParser(usage=usage, version=__version__)
parser.add_option("-o", "--output",
metavar="OUTPUT", help="Outputs to the given filename.")
parser.add_option("-r", "--resume",
action="store_true", dest="resume", default=False,
help="Resume a download")
parser.add_option("-l", "--live",
action="store_true", dest="live", default=False,
help="Enable for live streams")
parser.add_option("-s", "--silent",
action="store_true", dest="silent", default=False)
parser.add_option("-q", "--quality",
metavar="quality", help="Choose what format to download.\nIt will download the best format by default")
parser.add_option("-H", "--hls",
action="store_true", dest="hls", default=False)
(options, args) = parser.parse_args()
if len(args) != 1:
parser.error("incorrect number of arguments")
setup_log(options.silent)
url = args[0]
get_media(url, options)
if __name__ == "__main__":
main()