mirror of
https://github.com/spaam/svtplay-dl.git
synced 2024-11-30 23:24:16 +01:00
256 lines
6.7 KiB
Python
256 lines
6.7 KiB
Python
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, progress_stream
|
|
from lib.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
|
|
|
|
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
|
|
|