1
0
mirror of https://github.com/spaam/svtplay-dl.git synced 2024-11-27 21:54:17 +01:00

Initial work on splitting script to modules

Does not work reliably (downloading SVTPlay videos with HDS may work
if you're lucky).
This commit is contained in:
Olof Johansson 2013-01-17 00:21:47 +01:00
parent bd0b3eabee
commit 115e795835
24 changed files with 929 additions and 837 deletions

0
lib/__init__.py Normal file
View File

0
lib/svtplay/__init__.py Normal file
View File

View File

256
lib/svtplay/hds.py Normal file
View File

@ -0,0 +1,256 @@
import sys
import base64
import re
import struct
import logging
import binascii
import time
from datetime import timedelta
import xml.etree.ElementTree as ET
from lib.svtplay.output import progressbar
from lib.svtplay.utils import get_http_data, select_quality
log = logging.getLogger('svtplay_dl')
progress_stream = sys.stderr
def download_hds(options, url, swf=None):
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 options.output != "-":
extension = re.search("(\.[a-z0-9]+)$", options.output)
if not extension:
options.output = "%s.flv" % options.output
log.info("Outfile: %s", options.output)
file_d = open(options.output, "wb")
else:
file_d = sys.stdout
file_d.write(binascii.a2b_hex(b"464c56010500000009000000001200010c00000000000000"))
file_d.write(base64.b64decode(test["metadata"]))
file_d.write(binascii.a2b_hex(b"00000000"))
total = antal[1]["total"]
start = time.time()
estimated = ""
while i <= total:
url = "%s/%sSeg1-Frag%s" % (baseurl, test["url"], i)
if options.output != "-":
progressbar(total, i, estimated)
data = get_http_data(url)
number = decode_f4f(i, data)
file_d.write(data[number:])
now = time.time()
dt = now - start
et = dt / (i + 1) * total
rt = et - dt
td = timedelta(seconds = int(rt))
estimated = "Estimated Remaining: " + str(td)
i += 1
if options.output != "-":
file_d.close()
progress_stream.write('\n')
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 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

5
lib/svtplay/log.py Normal file
View File

@ -0,0 +1,5 @@
import sys
import logger
log = logging.getLogger('svtplay_dl')
progress_stream = sys.stderr

38
lib/svtplay/output.py Normal file
View File

@ -0,0 +1,38 @@
import sys
from svtplay.log import log
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))

View File

@ -0,0 +1,2 @@
class Service(object):
pass

View File

@ -0,0 +1,42 @@
import sys
import re
from urlparse import urlparse
import xml.etree.ElementTree as ET
from lib.svtplay.utils import get_http_data
class Aftonbladet():
def handle(self, url):
return "aftonbladet.se" in url
def get(self, options, url, start):
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)
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"]
options.other = "-y %s" % path
if start > 0:
options.other = "%s -A %s" % (options.other, str(start))
if url == None:
log.error("Can't find any video on that page")
sys.exit(3)
if url[0:4] == "rtmp":
download_rtmp(options, url)
else:
filename = url + path
download_http(options, filename)

21
lib/svtplay/service/dr.py Normal file
View File

@ -0,0 +1,21 @@
class Dr(object):
def handle(self, url):
return "dr.dk" in url
def get(self, options, url):
data = get_http_data(url)
match = re.search(r'resource:[ ]*"([^"]*)",', data)
resource_url = match.group(1)
resource_data = get_http_data(resource_url)
resource = json.loads(resource_data)
streams = {}
for stream in resource['links']:
streams[stream['bitrateKbps']] = stream['uri']
if len(streams) == 1:
uri = streams[list(streams.keys())[0]]
else:
uri = select_quality(options, streams)
# need -v ?
options.other = "-v -y '" + uri.replace("rtmp://vod.dr.dk/cms/", "") + "'"
download_rtmp(options, uri)

View File

@ -0,0 +1,34 @@
class Expressen():
def handle(self, url):
return "expressen.se" in url
def get(self, options, 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))
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(options, streams)
filename = test
match = re.search("rtmp://([0-9a-z\.]+/[0-9]+/)(.*).flv", filename)
filename = "rtmp://%s" % match.group(1)
options.other = "-y %s" % match.group(2)
download_rtmp(options, filename)

View File

@ -0,0 +1,37 @@
class Hbo():
def handle(self, url):
return "hbo.com" in url
def get(self, 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/%s.xml" % match.group(1)
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(options, streams)
download_rtmp(options, test["path"])

View File

@ -0,0 +1,49 @@
class Justin():
def handle(self, url):
return ("twitch.tv" in url) or ("justin.tv" in url)
def get(self, options, 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)
data = get_http_data(url)
xml = ET.XML(data)
url = xml.find("archive").find("video_file_url").text
download_http(options, 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.")
options.other = match.group(1)
url = "http://usher.justin.tv/find/%s.xml?type=any&p=2321" % user
options.live = True
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"] = "%s/%s" % (i.find("connect").text, i.find("play").text)
streams[int(i.find("video_height").text)] = stream
except AttributeError:
pass
test = select_quality(options, streams)
options.other = "-j '%s' -W %s" % (test["token"], options.other)
options.resume = False
download_rtmp(options, test["url"])

View File

@ -0,0 +1,28 @@
class Kanal5():
def handle(self, url):
return "kanal5play.se" in url
def get(self, options, 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)
data = json.loads(get_http_data(url))
options.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(options, streams)
filename = test["source"]
match = re.search("^(.*):", filename)
options.output = "%s.%s" % (options.output, match.group(1))
options.other = "-W %s -y %s " % ("http://www.kanal5play.se/flash/StandardPlayer.swf", filename)
download_rtmp(options, steambaseurl)

View File

@ -0,0 +1,42 @@
class Kanal9():
def handle(self, url):
return ("kanal9play.se" in url) or ("kanal5.se" in url)
def get(self, options, url):
data = get_http_data(url)
match = re.search("@videoPlayer\" value=\"(.*)\"", data)
if not match:
match = re.search("videoId=(\d+)&player", data)
if not match:
log.error("Can't find video file")
sys.exit(2)
try:
from pyamf import remoting
except ImportError:
log.error("You need to install pyamf to download content from kanal5 and kanal9")
log.error("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, match.group(1), publisher_id], envelope=env)))
env = str(remoting.encode(env).read())
url = "http://c.brightcove.com/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(options, streams)
filename = test["uri"]
match = re.search("(rtmp[e]{0,1}://.*)\&(.*)$", filename)
options.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(options, match.group(1))

View File

@ -0,0 +1,15 @@
class Nrk(object):
def handle(self, url):
return "nrk.no" in url
def get(self, options, url):
data = get_http_data(url)
match = re.search(r'data-media="(.*manifest.f4m)"', data)
manifest_url = match.group(1)
if options.hls:
manifest_url = manifest_url.replace("/z/", "/i/").replace("manifest.f4m", "master.m3u8")
download_hls(options, manifest_url)
else:
manifest_url = "%s?hdcore=2.8.0&g=hejsan" % manifest_url
download_hds(options, manifest_url)

View File

@ -0,0 +1,64 @@
class Qbrick():
def handle(self, url):
return ("dn.se" in url) or ("di.se" in url) or ("svd.se" in url)
def get(self, options, url):
if 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 = "%sDE1BA107" % match.group(1)
else:
mcid = match.group(1)
host = "http://vms.api.qbrick.com/rest/v3/getsingleplayer/%s" % mcid
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)
host = "http://vms.api.qbrick.com/rest/v3/getplayer/%s" % match.group(1)
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=%s" % 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)
host = "http://vms.api.qbrick.com/rest/v3/getsingleplayer/%s" % match.group(1)
else:
log.error("Can't find site")
sys.exit(2)
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)
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(options, streams)
options.other = "-y %s" % path
download_rtmp(options, server)

View File

@ -0,0 +1,15 @@
class Ruv(object):
def handle(self, url):
return "ruv.is" in url
def get(self, options, url):
data = get_http_data(url)
match = re.search(r'(http://load.cache.is/vodruv.*)"', data)
js_url = match.group(1)
js = get_http_data(js_url)
tengipunktur = js.split('"')[1]
match = re.search(r"http.*tengipunktur [+] '([:]1935.*)'", data)
m3u8_url = "http://" + tengipunktur + match.group(1)
base_url = m3u8_url.rsplit("/", 1)[0]
download_hls(options, m3u8_url, base_url)

22
lib/svtplay/service/sr.py Normal file
View File

@ -0,0 +1,22 @@
class Sr():
def handle(self, url):
return "sverigesradio.se" in url
def get(self, options, url):
data = get_http_data(url)
parse = urlparse(url)
try:
metafile = parse_qs(parse[4])["metafile"][0]
options.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)
options.other = unquote_plus(match.group(1))
url = "http://sverigesradio.se%s" % options.other
data = get_http_data(url)
xml = ET.XML(data)
url = xml.find("entry").find("ref").attrib["href"]
download_http(options, url)

View File

@ -0,0 +1,74 @@
import sys
import re
import json
from lib.svtplay.service import Service
from lib.svtplay.hds import download_hds
from lib.svtplay.hds import download_hds
from lib.svtplay.utils import get_http_data, select_quality
from lib.svtplay.log import log
class Svtplay(Service):
def handle(self, url):
return ("svtplay.se" in url) or ("svt.se" in url)
def get(self, options, url):
if re.findall("svt.se", url):
data = get_http_data(url)
match = re.search("data-json-href=\"(.*)\"", data)
if match:
filename = match.group(1).replace("&amp;", "&").replace("&format=json", "")
url = "http://www.svt.se%s" % filename
else:
log.error("Can't find video file")
sys.exit(2)
url = "%s?type=embed" % url
data = get_http_data(url)
match = re.search("value=\"(/(public)?(statiskt)?/swf/video/svtplayer-[0-9\.]+swf)\"", data)
swf = "http://www.svtplay.se%s" % match.group(1)
options.other = "-W %s" % swf
url = "%s&output=json&format=json" % url
data = json.loads(get_http_data(url))
options.live = data["video"]["live"]
streams = {}
streams2 = {} #hack..
for i in data["video"]["videoReferences"]:
if options.hls and i["playerType"] == "ios":
stream = {}
stream["url"] = i["url"]
streams[int(i["bitrate"])] = stream
elif not options.hls and i["playerType"] == "flash":
stream = {}
stream["url"] = i["url"]
streams[int(i["bitrate"])] = stream
if options.hls and i["playerType"] == "flash":
stream = {}
stream["url"] = i["url"]
streams2[int(i["bitrate"])] = stream
if len(streams) == 0 and options.hls:
test = streams2[list(streams.keys())[0]]
test["url"] = test["url"].replace("/z/", "/i/").replace("manifest.f4m", "master.m3u8")
elif len(streams) == 0:
log.error("Can't find any streams.")
sys.exit(2)
elif len(streams) == 1:
test = streams[list(streams.keys())[0]]
else:
test = select_quality(options, streams)
if test["url"][0:4] == "rtmp":
download_rtmp(options, test["url"])
elif options.hls:
download_hls(options, test["url"])
elif test["url"][len(test["url"])-3:len(test["url"])] == "f4m":
match = re.search("\/se\/secure\/", test["url"])
if match:
log.error("This stream is encrypted. Use --hls option")
sys.exit(2)
manifest = "%s?hdcore=2.8.0&g=hejsan" % test["url"]
download_hds(options, manifest, swf)
else:
download_http(options, test["url"])

View File

@ -0,0 +1,59 @@
class Tv4play():
def handle(self, url):
return ("tv4play.se" in url) or ("tv4.se" in url)
def get(self, options, url):
parse = urlparse(url)
if "tv4play.se" in url:
try:
vid = parse_qs(parse[4])["video_id"][0]
except KeyError:
log.error("Can't find video file")
sys.exit(2)
else:
match = re.search("-(\d+)$", url)
if match:
vid = match.group(1)
else:
log.error("Can't find video file")
sys.exit(2)
url = "http://premium.tv4play.se/api/web/asset/%s/play" % vid
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"))
if xml.find("live").text:
if xml.find("live").text != "false":
options.live = True
streams = {}
for i in sa:
if i.find("mediaFormat").text != "smi":
stream = {}
stream["uri"] = i.find("base").text
stream["path"] = i.find("url").text
streams[int(i.find("bitrate").text)] = stream
if len(streams) == 1:
test = streams[list(streams.keys())[0]]
else:
test = select_quality(options, streams)
swf = "http://www.tv4play.se/flash/tv4playflashlets.swf"
options.other = "-W %s -y %s" % (swf, test["path"])
if test["uri"][0:4] == "rtmp":
download_rtmp(options, test["uri"])
elif test["uri"][len(test["uri"])-3:len(test["uri"])] == "f4m":
match = re.search("\/se\/secure\/", test["uri"])
if match:
log.error("This stream is encrypted. Use --hls option")
sys.exit(2)
manifest = "%s?hdcore=2.8.0&g=hejsan" % test["path"]
download_hds(options, manifest, swf)

View File

@ -0,0 +1,12 @@
class Urplay():
def handle(self, url):
return "urplay.se" in url
def get(self, options, url):
data = get_http_data(url)
match = re.search('file=(.*)\&plugins', data)
if match:
path = "mp%s:%s" % (match.group(1)[-1], match.group(1))
options.other = "-a ondemand -y %s" % path
download_rtmp(options, "rtmp://streaming.ur.se/")

View File

@ -0,0 +1,24 @@
class Viaplay():
def handle(self, url):
return ("tv3play.se" in url) or ("tv6play.se" in url) or ("tv8play.se" in url)
def get(self, options, 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)
options.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
options.other = "-W http://flvplayer.viastream.viasat.tv/play/swf/player110516.swf?rnd=1315434062"
download_rtmp(options, filename)

71
lib/svtplay/utils.py Normal file
View File

@ -0,0 +1,71 @@
import sys
import logging
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
from io import BytesIO as StringIO
else:
from urllib2 import Request, urlopen, HTTPError, URLError
from urlparse import urlparse, parse_qs
from urllib import unquote_plus, quote_plus
from StringIO import StringIO
log = logging.getLogger('svtplay_dl')
progress_stream = sys.stderr
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:
log.error("Something wrong with that url")
log.error("Error code: %s" % e.code)
sys.exit(5)
except URLError as e:
log.error("Something wrong with that url")
log.error("Error code: %s" % e.reason)
sys.exit(5)
except ValueError as e:
log.error("Try adding http:// before the url")
sys.exit(5)
if sys.version_info > (3, 0):
data = response.read()
try:
data = data.decode("utf-8")
except UnicodeDecodeError:
pass
else:
try:
data = response.read()
except socket.error as e:
log.error("Lost the connection to the server")
sys.exit(5)
response.close()
return data
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):
log.error("Can't find that quality. (Try one of: %s)",
", ".join(map(str, sort)))
sys.exit(4)
return selected

View File

@ -15,15 +15,29 @@ 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
from datetime import timedelta
from svtplay.log import log
from svtplay.utils import get_http_data, select_quality
from svtplay.service.aftonbladet import Aftonbladet
from svtplay.service.dr import Dr
from svtplay.service.expressen import Expressen
from svtplay.service.hbo import Hbo
from svtplay.service.justin import Justin
from svtplay.service.kanal5 import Kanal5
from svtplay.service.kanal9 import Kanal9
from svtplay.service.nrk import Nrk
from svtplay.service.qbrick import Qbrick
from svtplay.service.ruv import Ruv
from svtplay.service.sr import Sr
from svtplay.service.svtplay import Svtplay
from svtplay.service.tv4play import Tv4play
from svtplay.service.urplay import Urplay
from svtplay.service.viaplay import Viaplay
__version__ = "0.8.2013.01.15"
@ -59,174 +73,6 @@ class Options:
self.hls = False
self.other = None
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")
@ -259,52 +105,6 @@ def parsem3u(data):
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:
log.error("Something wrong with that url")
log.error("Error code: %s" % e.code)
sys.exit(5)
except URLError as e:
log.error("Something wrong with that url")
log.error("Error code: %s" % e.reason)
sys.exit(5)
except ValueError as e:
log.error("Try adding http:// before the url")
sys.exit(5)
if sys.version_info > (3, 0):
data = response.read()
try:
data = data.decode("utf-8")
except UnicodeDecodeError:
pass
else:
try:
data = response.read()
except socket.error as e:
log.error("Lost the connection to the server")
sys.exit(5)
response.close()
return data
def progress(byte, total, extra = ""):
""" Print some info about how much we have downloaded """
ratio = float(byte) / total
@ -325,70 +125,6 @@ def progress(byte, total, extra = ""):
progress_stream.flush()
def download_hds(options, url, swf=None):
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 options.output != "-":
extension = re.search("(\.[a-z0-9]+)$", options.output)
if not extension:
options.output = "%s.flv" % options.output
log.info("Outfile: %s", options.output)
file_d = open(options.output, "wb")
else:
file_d = sys.stdout
file_d.write(binascii.a2b_hex(b"464c56010500000009000000001200010c00000000000000"))
file_d.write(base64.b64decode(test["metadata"]))
file_d.write(binascii.a2b_hex(b"00000000"))
total = antal[1]["total"]
start = time.time()
estimated = ""
while i <= total:
url = "%s/%sSeg1-Frag%s" % (baseurl, test["url"], i)
if options.output != "-":
progressbar(total, i, estimated)
data = get_http_data(url)
number = decode_f4f(i, data)
file_d.write(data[number:])
now = time.time()
dt = now - start
et = dt / (i + 1) * total
rt = et - dt
td = timedelta(seconds = int(rt))
estimated = "Estimated Remaining: " + str(td)
i += 1
if options.output != "-":
file_d.close()
progress_stream.write('\n')
def download_hls(options, url, baseurl=None):
data = get_http_data(url)
globaldata, files = parsem3u(data)
@ -542,560 +278,6 @@ def select_quality(options, streams):
return selected
class Justin():
def handle(self, url):
return ("twitch.tv" in url) or ("justin.tv" in url)
def get(self, options, 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)
data = get_http_data(url)
xml = ET.XML(data)
url = xml.find("archive").find("video_file_url").text
download_http(options, 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.")
options.other = match.group(1)
url = "http://usher.justin.tv/find/%s.xml?type=any&p=2321" % user
options.live = True
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"] = "%s/%s" % (i.find("connect").text, i.find("play").text)
streams[int(i.find("video_height").text)] = stream
except AttributeError:
pass
test = select_quality(options, streams)
options.other = "-j '%s' -W %s" % (test["token"], options.other)
options.resume = False
download_rtmp(options, test["url"])
class Hbo():
def handle(self, url):
return "hbo.com" in url
def get(self, 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/%s.xml" % match.group(1)
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(options, streams)
download_rtmp(options, test["path"])
class Sr():
def handle(self, url):
return "sverigesradio.se" in url
def get(self, options, url):
data = get_http_data(url)
parse = urlparse(url)
try:
metafile = parse_qs(parse[4])["metafile"][0]
options.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)
options.other = unquote_plus(match.group(1))
url = "http://sverigesradio.se%s" % options.other
data = get_http_data(url)
xml = ET.XML(data)
url = xml.find("entry").find("ref").attrib["href"]
download_http(options, url)
class Urplay():
def handle(self, url):
return "urplay.se" in url
def get(self, options, url):
data = get_http_data(url)
match = re.search('file=(.*)\&plugins', data)
if match:
path = "mp%s:%s" % (match.group(1)[-1], match.group(1))
options.other = "-a ondemand -y %s" % path
download_rtmp(options, "rtmp://streaming.ur.se/")
class Qbrick():
def handle(self, url):
return ("dn.se" in url) or ("di.se" in url) or ("svd.se" in url)
def get(self, options, url):
if 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 = "%sDE1BA107" % match.group(1)
else:
mcid = match.group(1)
host = "http://vms.api.qbrick.com/rest/v3/getsingleplayer/%s" % mcid
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)
host = "http://vms.api.qbrick.com/rest/v3/getplayer/%s" % match.group(1)
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=%s" % 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)
host = "http://vms.api.qbrick.com/rest/v3/getsingleplayer/%s" % match.group(1)
else:
log.error("Can't find site")
sys.exit(2)
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)
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(options, streams)
options.other = "-y %s" % path
download_rtmp(options, server)
class Kanal5():
def handle(self, url):
return "kanal5play.se" in url
def get(self, options, 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)
data = json.loads(get_http_data(url))
options.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(options, streams)
filename = test["source"]
match = re.search("^(.*):", filename)
options.output = "%s.%s" % (options.output, match.group(1))
options.other = "-W %s -y %s " % ("http://www.kanal5play.se/flash/StandardPlayer.swf", filename)
download_rtmp(options, steambaseurl)
class Kanal9():
def handle(self, url):
return ("kanal9play.se" in url) or ("kanal5.se" in url)
def get(self, options, url):
data = get_http_data(url)
match = re.search("@videoPlayer\" value=\"(.*)\"", data)
if not match:
match = re.search("videoId=(\d+)&player", data)
if not match:
log.error("Can't find video file")
sys.exit(2)
try:
from pyamf import remoting
except ImportError:
log.error("You need to install pyamf to download content from kanal5 and kanal9")
log.error("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, match.group(1), publisher_id], envelope=env)))
env = str(remoting.encode(env).read())
url = "http://c.brightcove.com/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(options, streams)
filename = test["uri"]
match = re.search("(rtmp[e]{0,1}://.*)\&(.*)$", filename)
options.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(options, match.group(1))
class Expressen():
def handle(self, url):
return "expressen.se" in url
def get(self, options, 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))
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(options, streams)
filename = test
match = re.search("rtmp://([0-9a-z\.]+/[0-9]+/)(.*).flv", filename)
filename = "rtmp://%s" % match.group(1)
options.other = "-y %s" % match.group(2)
download_rtmp(options, filename)
class Aftonbladet():
def handle(self, url):
return "aftonbladet.se" in url
def get(self, options, url, start):
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)
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"]
options.other = "-y %s" % path
if start > 0:
options.other = "%s -A %s" % (options.other, str(start))
if url == None:
log.error("Can't find any video on that page")
sys.exit(3)
if url[0:4] == "rtmp":
download_rtmp(options, url)
else:
filename = url + path
download_http(options, filename)
class Viaplay():
def handle(self, url):
return ("tv3play.se" in url) or ("tv6play.se" in url) or ("tv8play.se" in url)
def get(self, options, 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)
options.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
options.other = "-W http://flvplayer.viastream.viasat.tv/play/swf/player110516.swf?rnd=1315434062"
download_rtmp(options, filename)
class Tv4play():
def handle(self, url):
return ("tv4play.se" in url) or ("tv4.se" in url)
def get(self, options, url):
parse = urlparse(url)
if "tv4play.se" in url:
try:
vid = parse_qs(parse[4])["video_id"][0]
except KeyError:
log.error("Can't find video file")
sys.exit(2)
else:
match = re.search("-(\d+)$", url)
if match:
vid = match.group(1)
else:
log.error("Can't find video file")
sys.exit(2)
url = "http://premium.tv4play.se/api/web/asset/%s/play" % vid
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"))
if xml.find("live").text:
if xml.find("live").text != "false":
options.live = True
streams = {}
for i in sa:
if i.find("mediaFormat").text != "smi":
stream = {}
stream["uri"] = i.find("base").text
stream["path"] = i.find("url").text
streams[int(i.find("bitrate").text)] = stream
if len(streams) == 1:
test = streams[list(streams.keys())[0]]
else:
test = select_quality(options, streams)
swf = "http://www.tv4play.se/flash/tv4playflashlets.swf"
options.other = "-W %s -y %s" % (swf, test["path"])
if test["uri"][0:4] == "rtmp":
download_rtmp(options, test["uri"])
elif test["uri"][len(test["uri"])-3:len(test["uri"])] == "f4m":
match = re.search("\/se\/secure\/", test["uri"])
if match:
log.error("This stream is encrypted. Use --hls option")
sys.exit(2)
manifest = "%s?hdcore=2.8.0&g=hejsan" % test["path"]
download_hds(options, manifest, swf)
class Svtplay():
def handle(self, url):
return ("svtplay.se" in url) or ("svt.se" in url)
def get(self, options, url):
if re.findall("svt.se", url):
data = get_http_data(url)
match = re.search("data-json-href=\"(.*)\"", data)
if match:
filename = match.group(1).replace("&amp;", "&").replace("&format=json", "")
url = "http://www.svt.se%s" % filename
else:
log.error("Can't find video file")
sys.exit(2)
url = "%s?type=embed" % url
data = get_http_data(url)
match = re.search("value=\"(/(public)?(statiskt)?/swf/video/svtplayer-[0-9\.]+swf)\"", data)
swf = "http://www.svtplay.se%s" % match.group(1)
options.other = "-W %s" % swf
url = "%s&output=json&format=json" % url
data = json.loads(get_http_data(url))
options.live = data["video"]["live"]
streams = {}
streams2 = {} #hack..
for i in data["video"]["videoReferences"]:
if options.hls and i["playerType"] == "ios":
stream = {}
stream["url"] = i["url"]
streams[int(i["bitrate"])] = stream
elif not options.hls and i["playerType"] == "flash":
stream = {}
stream["url"] = i["url"]
streams[int(i["bitrate"])] = stream
if options.hls and i["playerType"] == "flash":
stream = {}
stream["url"] = i["url"]
streams2[int(i["bitrate"])] = stream
if len(streams) == 0 and options.hls:
test = streams2[list(streams.keys())[0]]
test["url"] = test["url"].replace("/z/", "/i/").replace("manifest.f4m", "master.m3u8")
elif len(streams) == 0:
log.error("Can't find any streams.")
sys.exit(2)
elif len(streams) == 1:
test = streams[list(streams.keys())[0]]
else:
test = select_quality(options, streams)
if test["url"][0:4] == "rtmp":
download_rtmp(options, test["url"])
elif options.hls:
download_hls(options, test["url"])
elif test["url"][len(test["url"])-3:len(test["url"])] == "f4m":
match = re.search("\/se\/secure\/", test["url"])
if match:
log.error("This stream is encrypted. Use --hls option")
sys.exit(2)
manifest = "%s?hdcore=2.8.0&g=hejsan" % test["url"]
download_hds(options, manifest, swf)
else:
download_http(options, test["url"])
class Nrk(object):
def handle(self, url):
return "nrk.no" in url
def get(self, options, url):
data = get_http_data(url)
match = re.search(r'data-media="(.*manifest.f4m)"', data)
manifest_url = match.group(1)
if options.hls:
manifest_url = manifest_url.replace("/z/", "/i/").replace("manifest.f4m", "master.m3u8")
download_hls(options, manifest_url)
else:
manifest_url = "%s?hdcore=2.8.0&g=hejsan" % manifest_url
download_hds(options, manifest_url)
class Dr(object):
def handle(self, url):
return "dr.dk" in url
def get(self, options, url):
data = get_http_data(url)
match = re.search(r'resource:[ ]*"([^"]*)",', data)
resource_url = match.group(1)
resource_data = get_http_data(resource_url)
resource = json.loads(resource_data)
streams = {}
for stream in resource['links']:
streams[stream['bitrateKbps']] = stream['uri']
if len(streams) == 1:
uri = streams[list(streams.keys())[0]]
else:
uri = select_quality(options, streams)
# need -v ?
options.other = "-v -y '" + uri.replace("rtmp://vod.dr.dk/cms/", "") + "'"
download_rtmp(options, uri)
class Ruv(object):
def handle(self, url):
return "ruv.is" in url
def get(self, options, url):
data = get_http_data(url)
match = re.search(r'(http://load.cache.is/vodruv.*)"', data)
js_url = match.group(1)
js = get_http_data(js_url)
tengipunktur = js.split('"')[1]
match = re.search(r"http.*tengipunktur [+] '([:]1935.*)'", data)
m3u8_url = "http://" + tengipunktur + match.group(1)
base_url = m3u8_url.rsplit("/", 1)[0]
download_hls(options, m3u8_url, base_url)
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):
sites = [Aftonbladet(), Dr(), Expressen(), Hbo(), Justin(), Kanal5(), Kanal9(),
Nrk(), Qbrick(), Ruv(), Sr(), Svtplay(), Tv4play(), Urplay(), Viaplay()]