1
0
mirror of https://github.com/spaam/svtplay-dl.git synced 2024-11-25 12:45:42 +01:00
svtplay-dl/lib/svtplay/hds.py
Johan Andersson 3fdc183572 download_hds: fix some decoding problems
Saw some issues while downloading streams from urplay.
2013-03-12 13:19:11 +01:00

261 lines
6.9 KiB
Python

# ex:ts=4:sw=4:sts=4:et
# -*- tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*-
from __future__ import absolute_import
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 svtplay.output import progressbar, progress_stream
from svtplay.utils import get_http_data, select_quality
log = logging.getLogger('svtplay_dl')
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
metasize = struct.pack(">L", len(base64.b64decode(test["metadata"])))[1:]
file_d.write(binascii.a2b_hex(b"464c560105000000090000000012"))
file_d.write(metasize)
file_d.write(binascii.a2b_hex(b"00000000000000"))
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):
tagLen, = struct.unpack_from(">L", fragData, start)
tagLen &= 0x00ffffff
start += tagLen + 11 + 4
return start