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:
parent
bd0b3eabee
commit
115e795835
0
lib/__init__.py
Normal file
0
lib/__init__.py
Normal file
0
lib/svtplay/__init__.py
Normal file
0
lib/svtplay/__init__.py
Normal file
0
lib/svtplay/fetcher/__init__.py
Normal file
0
lib/svtplay/fetcher/__init__.py
Normal file
256
lib/svtplay/hds.py
Normal file
256
lib/svtplay/hds.py
Normal 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
5
lib/svtplay/log.py
Normal 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
38
lib/svtplay/output.py
Normal 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))
|
||||
|
2
lib/svtplay/service/__init__.py
Normal file
2
lib/svtplay/service/__init__.py
Normal file
@ -0,0 +1,2 @@
|
||||
class Service(object):
|
||||
pass
|
42
lib/svtplay/service/aftonbladet.py
Normal file
42
lib/svtplay/service/aftonbladet.py
Normal 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
21
lib/svtplay/service/dr.py
Normal 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)
|
||||
|
34
lib/svtplay/service/expressen.py
Normal file
34
lib/svtplay/service/expressen.py
Normal 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)
|
||||
|
37
lib/svtplay/service/hbo.py
Normal file
37
lib/svtplay/service/hbo.py
Normal 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"])
|
||||
|
49
lib/svtplay/service/justin.py
Normal file
49
lib/svtplay/service/justin.py
Normal 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"])
|
||||
|
28
lib/svtplay/service/kanal5.py
Normal file
28
lib/svtplay/service/kanal5.py
Normal 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)
|
||||
|
42
lib/svtplay/service/kanal9.py
Normal file
42
lib/svtplay/service/kanal9.py
Normal 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))
|
||||
|
15
lib/svtplay/service/nrk.py
Normal file
15
lib/svtplay/service/nrk.py
Normal 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)
|
||||
|
64
lib/svtplay/service/qbrick.py
Normal file
64
lib/svtplay/service/qbrick.py
Normal 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)
|
||||
|
15
lib/svtplay/service/ruv.py
Normal file
15
lib/svtplay/service/ruv.py
Normal 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
22
lib/svtplay/service/sr.py
Normal 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)
|
||||
|
74
lib/svtplay/service/svtplay.py
Normal file
74
lib/svtplay/service/svtplay.py
Normal 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("&", "&").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"])
|
||||
|
59
lib/svtplay/service/tv4play.py
Normal file
59
lib/svtplay/service/tv4play.py
Normal 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)
|
||||
|
12
lib/svtplay/service/urplay.py
Normal file
12
lib/svtplay/service/urplay.py
Normal 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/")
|
||||
|
24
lib/svtplay/service/viaplay.py
Normal file
24
lib/svtplay/service/viaplay.py
Normal 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
71
lib/svtplay/utils.py
Normal 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
|
||||
|
856
svtplay_dl.py
856
svtplay_dl.py
@ -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("&", "&").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()]
|
||||
|
Loading…
Reference in New Issue
Block a user