mirror of
https://github.com/spaam/svtplay-dl.git
synced 2024-11-27 21:54:17 +01:00
HTTP Live Streaming support.
If the user install pycrypto it will work with encrypted streams from svtplay for example.
This commit is contained in:
parent
882b5dd810
commit
2b3d7119bd
107
svtplay-dl
107
svtplay-dl
@ -24,7 +24,7 @@ import binascii
|
||||
import StringIO
|
||||
import hashlib
|
||||
|
||||
__version__ = "0.8.2012.10.26"
|
||||
__version__ = "0.8.2012.11.08"
|
||||
|
||||
def readbyte(data, pos):
|
||||
return struct.unpack("B", data[pos])[0]
|
||||
@ -191,6 +191,38 @@ def readasrtbox(data, pos):
|
||||
i += 1
|
||||
return ret
|
||||
|
||||
def parsem3u(data):
|
||||
if not data.startswith("#EXTM3U"):
|
||||
raise ValueError("Does not apprear to be a ext m3u file")
|
||||
|
||||
files=[]
|
||||
streaminfo={}
|
||||
globdata={}
|
||||
|
||||
data=data.replace("\r","\n")
|
||||
for l in data.split("\n")[1:]:
|
||||
if not l:
|
||||
continue
|
||||
if l.startswith("#EXT-X-STREAM-INF:"):
|
||||
#not a proper parser
|
||||
kalle = [x.strip().split("=",1) for x in l[18:].split(",")]
|
||||
streaminfo.update({kalle[1][0]: kalle[1][1]})
|
||||
elif l.startswith("#EXT-X-ENDLIST"):
|
||||
break
|
||||
elif l.startswith("#EXT-X-"):
|
||||
globdata.update(dict([l[7:].strip().split(":",1)]))
|
||||
elif l.startswith("#EXTINF:"):
|
||||
dur, title=l[8:].strip().split(",",1)
|
||||
streaminfo['duration'] = dur
|
||||
streaminfo['title'] = title
|
||||
elif l[0]=='#':
|
||||
pass
|
||||
else:
|
||||
files.append((l, streaminfo))
|
||||
streaminfo={}
|
||||
|
||||
return globdata,files
|
||||
|
||||
def decode_f4f(fragID, fragData ):
|
||||
start = fragData.find( "mdat" ) + 4
|
||||
if (fragID > 1):
|
||||
@ -309,6 +341,63 @@ def download_hds(options, url, output, swf):
|
||||
file_d.close()
|
||||
sys.stderr.write('\n')
|
||||
|
||||
def download_hls(options, url, output, live, other):
|
||||
data = get_http_data(url)
|
||||
globaldata, files = parsem3u(data)
|
||||
streams ={}
|
||||
for i in files:
|
||||
streams[int(i[1]["BANDWIDTH"])] = i[0]
|
||||
|
||||
test = select_quality(options, streams)
|
||||
m3u8 = get_http_data(test)
|
||||
globaldata, files = parsem3u(m3u8)
|
||||
encrypted = False
|
||||
key = None
|
||||
try:
|
||||
keydata = globaldata["KEY"]
|
||||
encrypted = True
|
||||
match = re.search("URI=\"(http://.*)\"", keydata)
|
||||
key = get_http_data(match.group(1))
|
||||
rand = os.urandom(16)
|
||||
except:
|
||||
pass
|
||||
|
||||
try:
|
||||
from Crypto.Cipher import AES
|
||||
decryptor = AES.new(key, AES.MODE_CBC, rand)
|
||||
except ImportError:
|
||||
logging.error("You need to install pycrypto to download encrypted HLS streams")
|
||||
sys.exit(2)
|
||||
n=1
|
||||
if output != "-":
|
||||
file_d = open(output + ".ts", "wb")
|
||||
else:
|
||||
file_d = sys.stdout
|
||||
|
||||
for i in files:
|
||||
if output != "-":
|
||||
progresstr = "Downloading %s/%s fragments" % (n, len(files))
|
||||
sys.stderr.write(progresstr + '\r')
|
||||
data = get_http_data(i[0])
|
||||
if encrypted:
|
||||
lots = StringIO.StringIO(data)
|
||||
|
||||
plain = ""
|
||||
crypt = lots.read(1024)
|
||||
decrypted = decryptor.decrypt(crypt)
|
||||
while decrypted:
|
||||
plain += decrypted
|
||||
crypt = lots.read(1024)
|
||||
decrypted = decryptor.decrypt(crypt)
|
||||
data = plain
|
||||
|
||||
file_d.write(data)
|
||||
n += 1
|
||||
|
||||
if output != "-":
|
||||
file_d.close()
|
||||
sys.stderr.write('\n')
|
||||
|
||||
def download_http(url, output):
|
||||
""" Get the stream from HTTP """
|
||||
response = urlopen(url)
|
||||
@ -758,7 +847,11 @@ class Svtplay():
|
||||
streams = {}
|
||||
|
||||
for i in data["video"]["videoReferences"]:
|
||||
if i["playerType"] == "flash":
|
||||
if self.options.hls and i["playerType"] == "ios":
|
||||
stream = {}
|
||||
stream["url"] = i["url"]
|
||||
streams[int(i["bitrate"])] = stream
|
||||
elif not self.options.hls and i["playerType"] == "flash":
|
||||
stream = {}
|
||||
stream["url"] = i["url"]
|
||||
streams[int(i["bitrate"])] = stream
|
||||
@ -770,7 +863,13 @@ class Svtplay():
|
||||
|
||||
if test["url"][0:4] == "rtmp":
|
||||
download_rtmp(self.options, test["url"], self.output, self.live, other, self.resume)
|
||||
elif self.options.hls:
|
||||
download_hls(self.options, test["url"], self.output, self.live, other)
|
||||
elif test["url"][len(test["url"])-3:len(test["url"])] == "f4m":
|
||||
match = re.search("\/se\/secure\/", test["url"])
|
||||
if match:
|
||||
logging.error("This stream is encrypted. Use --hls option")
|
||||
sys.exit(2)
|
||||
manifest = "%s?hdcore=2.8.0&g=hejsan" % test["url"]
|
||||
download_hds(self.options, manifest, self.output, swf)
|
||||
else:
|
||||
@ -828,6 +927,8 @@ def main():
|
||||
action="store_true", dest="silent", default=False)
|
||||
parser.add_option("-q", "--quality",
|
||||
metavar="quality", help="Choose what format to download.\nIt will download the best format by default")
|
||||
parser.add_option("-H", "--hls",
|
||||
action="store_true", dest="hls", default=False)
|
||||
(options, args) = parser.parse_args()
|
||||
if len(args) != 1:
|
||||
parser.error("incorrect number of arguments")
|
||||
@ -835,6 +936,8 @@ def main():
|
||||
live = options.live
|
||||
resume = options.resume
|
||||
silent = options.silent
|
||||
hls = options.hls
|
||||
|
||||
if silent:
|
||||
logging.basicConfig(format='%(levelname)s %(message)s', level=logging.WARNING, stream=sys.stderr)
|
||||
else:
|
||||
|
Loading…
Reference in New Issue
Block a user