__author_info = "Written by Antonio Ospite http://ao2.it"
import os
+import re
import sys
import xml.etree.ElementTree as etree
import urllib2
import struct
import tempfile
from optparse import OptionParser
+from urlparse import urlparse, urlunparse
def get_chunk_data(data):
out_file.write(struct.pack('<L', data_len))
-def get_manifest(base_url, dest_dir=tempfile.gettempdir(),
- manifest_file='Manifest'):
+def get_manifest(url, dest_dir=tempfile.gettempdir()):
"""Returns the manifest and the new URL if this is changed"""
if os.path.exists(dest_dir) == False:
os.mkdir(dest_dir, 0755)
- if base_url.startswith('http://'):
+ # Remove the querystring if present
+ manifest_url = urlunparse(urlparse(url)._replace(query=''))
- manifest_url = base_url
- if not manifest_url.lower().endswith(('/manifest', '.ismc', '.csm')):
- manifest_url += '/Manifest'
+ if not manifest_url.lower().endswith(('/manifest', '.ismc', '.csm')):
+ manifest_url += '/Manifest'
+
+ if manifest_url.startswith('http://'):
response = urllib2.urlopen(manifest_url)
data = response.read()
- manifest_path = os.path.join(dest_dir, manifest_file)
- f = open(manifest_path, "w")
+ local_manifest_path = os.path.join(dest_dir, 'Manifest')
+
+ f = open(local_manifest_path, "w")
f.write(data)
f.close()
else:
- manifest_path = base_url
+ local_manifest_path = url
- manifest = etree.parse(manifest_path)
+ manifest = etree.parse(local_manifest_path)
version = manifest.getroot().attrib['MajorVersion']
if version != "2":
try:
# if some intermediate client Manifest is used, like in Rai Replay
clip = manifest.find("Clip")
- actual_manifest_url = clip.attrib["Url"]
- base_url = actual_manifest_url.lower().replace("/manifest", "")
+ manifest_url = clip.attrib["Url"]
except:
pass
+ manifest_pattern = re.compile("/manifest$", re.IGNORECASE)
+ base_url = manifest_pattern.sub("", manifest_url)
+
return (manifest, base_url)
def print_manifest_info(manifest):
- streams = manifest.findall('//StreamIndex')
+ streams = manifest.findall('.//StreamIndex')
for i, s in enumerate(streams):
stream_type = s.attrib["Type"]
print
-def download_chunks(base_url, manifest, stream_index, quality_level, dest_dir):
-
- if os.path.exists(dest_dir) == False:
- os.mkdir(dest_dir, 0755)
-
- stream = manifest.findall('//StreamIndex')[stream_index]
-
+def get_chunk_quality_string(stream, quality_level):
quality = stream.findall("QualityLevel")[quality_level]
bitrate = quality.attrib["Bitrate"]
+ quality_attributes = quality.findall("CustomAttributes/Attribute")
+ custom_attributes = ""
+ for i in quality_attributes:
+ custom_attributes += "%s=%s," % (i.attrib["Name"], i.attrib["Value"])
+ custom_attributes = custom_attributes.rstrip(',')
+
# Assume URLs are in this form:
# Url="QualityLevels({bitrate})/Fragments(video={start time})"
+ # or
+ # Url="QualityLevels({bitrate},{CustomAttributes})/Fragments(video={start time})"
url = stream.attrib["Url"]
chunks_quality = url.split('/')[0].replace("{bitrate}", bitrate)
+ chunks_quality = chunks_quality.replace("{CustomAttributes}", custom_attributes)
+
+ return chunks_quality
+
+
+def get_chunk_name_string(stream, chunk):
+ t = chunk.attrib["t"]
+ url = stream.attrib["Url"]
+ chunk_name = url.split('/')[1].replace("{start time}", t)
+
+ return chunk_name
+
+
+def download_chunks(base_url, manifest, stream_index, quality_level, dest_dir):
+
+ if os.path.exists(dest_dir) == False:
+ os.mkdir(dest_dir, 0755)
+
+ stream = manifest.findall('.//StreamIndex')[stream_index]
+
+ chunks_quality = get_chunk_quality_string(stream, quality_level)
+
chunks_dest_dir = os.path.join(dest_dir, chunks_quality)
if os.path.exists(chunks_dest_dir) == False:
os.mkdir(chunks_dest_dir, 0755)
print "\tChunks %10d/%-10d" % (0, len(chunks)), "\r",
sys.stdout.flush()
for i, c in enumerate(chunks):
- t = c.attrib["t"]
- chunk_name = url.split('/')[1].replace("{start time}", t)
+ chunk_name = get_chunk_name_string(stream, c)
chunk_file = os.path.join(dest_dir, chunks_quality, chunk_name)
if os.path.exists(chunk_file) == False:
chunk_url = base_url + '/' + chunks_quality + '/' + chunk_name
- response = urllib2.urlopen(chunk_url)
- data = response.read()
+ try:
+ response = urllib2.urlopen(chunk_url)
+ data = response.read()
+
+ f = open(chunk_file, "wb")
+ f.write(data)
+ f.close()
+ except Exception as e:
+ print e
- f = open(chunk_file, "wb")
- f.write(data)
- f.close()
else:
f = open(chunk_file, "rb")
data = f.read()
if final_dest_file == None:
final_dest_file = dest_file_name
- stream = manifest.findall('//StreamIndex')[stream_index]
-
- quality = stream.findall("QualityLevel")[quality_level]
- bitrate = quality.attrib["Bitrate"]
+ stream = manifest.findall('.//StreamIndex')[stream_index]
- # Assume URLs are in this form:
- # Url="QualityLevels({bitrate})/Fragments(video={start time})"
- url = stream.attrib["Url"]
+ chunks_quality = get_chunk_quality_string(stream, quality_level)
- chunks_quality = url.split('/')[0].replace("{bitrate}", bitrate)
chunks_src_dir = os.path.join(src_dir, chunks_quality)
dest_file = open(dest_file_name, "wb")
print "\tChunks %10d/%-10d" % (0, len(chunks)), "\r",
sys.stdout.flush()
for i, c in enumerate(chunks):
- t = c.attrib["t"]
- chunk_name = url.split('/')[1].replace("{start time}", t)
+ chunk_name = get_chunk_name_string(stream, c)
chunk_file = os.path.join(chunks_src_dir, chunk_name)
f = open(chunk_file, "rb")
# Add a nice WAV header
if stream.attrib['Type'] == "audio":
+ quality = stream.findall("QualityLevel")[quality_level]
codec_private_data = quality.attrib['CodecPrivateData']
fmt = {}
def calc_tracks_delay(manifest, stream1_index, stream2_index):
- streams = manifest.findall('//StreamIndex')
+ streams = manifest.findall('.//StreamIndex')
s1 = streams[stream1_index]
s2 = streams[stream2_index]