From 8d41a494c995dbf56d3837d8a9b47f8734e70911 Mon Sep 17 00:00:00 2001 From: Puyodead1 <23562356riley@gmail.com> Date: Tue, 18 May 2021 10:15:54 -0400 Subject: [PATCH] semi udemy support --- .gitignore | 2 + dashdownloader.py | 156 --------------------------------- dashdownloader_multisegment.py | 86 +++++++----------- keyfile.example.json | 3 + keyfile.json | 4 - 5 files changed, 39 insertions(+), 212 deletions(-) delete mode 100644 dashdownloader.py create mode 100644 keyfile.example.json delete mode 100644 keyfile.json diff --git a/.gitignore b/.gitignore index 510c73d..d466f04 100644 --- a/.gitignore +++ b/.gitignore @@ -112,3 +112,5 @@ dmypy.json # Pyre type checker .pyre/ +*.mp4 +keyfile.json \ No newline at end of file diff --git a/dashdownloader.py b/dashdownloader.py deleted file mode 100644 index 065b70a..0000000 --- a/dashdownloader.py +++ /dev/null @@ -1,156 +0,0 @@ -#dashdownloader -import os,requests,shutil,json,glob -from mpegdash.parser import MPEGDASHParser -from mpegdash.nodes import Descriptor -from mpegdash.utils import ( - parse_attr_value, parse_child_nodes, parse_node_value, - write_attr_value, write_child_node, write_node_value -) - -#global ids -quality = 1080 #default to 1080p -retry = 3 -download_dir = os.getcwd() # set the folder to output -working_dir = os.getcwd() # set the folder to download ephemeral files -keyfile_path = working_dir + "/keyfile.json" - -#Patching the Mpegdash lib for keyID -def __init__(self): - self.scheme_id_uri = '' # xs:anyURI (required) - self.value = None # xs:string - self.id = None # xs:string - self.key_id = None # xs:string - -def parse(self, xmlnode): - self.scheme_id_uri = parse_attr_value(xmlnode, 'schemeIdUri', str) - self.value = parse_attr_value(xmlnode, 'value', str) - self.id = parse_attr_value(xmlnode, 'id', str) - self.key_id = parse_attr_value(xmlnode, 'ns2:default_KID', str) - if(self.key_id == None): - self.key_id = parse_attr_value(xmlnode, 'cenc:default_KID', str) - -def write(self, xmlnode): - write_attr_value(xmlnode, 'schemeIdUri', self.scheme_id_uri) - write_attr_value(xmlnode, 'value', self.value) - write_attr_value(xmlnode, 'id', self.id) - write_attr_value(xmlnode, 'ns2:default_KID', self.key_id) - if(self.key_id == None): - write_attr_value(xmlnode, 'cenc:default_KID', self.key_id) - -Descriptor.__init__ = __init__ -Descriptor.parse = parse -Descriptor.write = write - -#Get the keys -with open(keyfile_path,'r') as keyfile: - keyfile = keyfile.read() -keyfile = json.loads(keyfile) - -def manifest_parser(mpd_url): - manifest = requests.get(mpd_url).text - with open("manifest.mpd",'w') as manifest_handler: - manifest_handler.write(manifest) - mpd = MPEGDASHParser.parse("./manifest.mpd") - audio = [] - video = [] - for period in mpd.periods: - for adapt_set in period.adaptation_sets: - content_type = adapt_set.content_type - for repr in adapt_set.representations: - base_url = repr.base_urls[0].base_url_value - if(content_type == "audio"): - audio.append(base_url) - elif(content_type == "video" and repr.height == quality): - video.append(base_url) - for prot in adapt_set.content_protections: - if(prot.value == "cenc"): - kId = prot.key_id.replace('-','') - if(content_type == "audio"): - audio.append(kId) - elif(content_type == "video" and repr.height == quality): - video.append(kId) - break - return video + audio - -def download(url,filename,count = 0): - video = requests.get(url, stream=True) - if video.status_code == 200: - video_length = int(video.headers.get('content-length')) - if(os.path.isfile(filename) and os.path.getsize(filename) >= video_length): - print("Video already downloaded.. skipping write to disk..") - else: - try: - with open(filename, 'wb') as video_file: - shutil.copyfileobj(video.raw, video_file) - except: - print(url,filename) - print("Write to disk error: Reattempting download and write..") - if(count <= retry): - count += 1 - download(url,filename,count) - else: - exit("Error Writing Video to Disk. Exiting...") - - if os.path.getsize(filename) >= video_length: - pass - else: - print("Error downloaded video is faulty.. Retrying to download") - if(count <= retry): - count += 1 - download(url,filename,count) - else: - exit("Error Writing Video to Disk. Exiting...") - else: - print("Video file is not accessible",filename,"Retrying...") - if(count <= retry): - count += 1 - download(url,filename,count) - else: - print("Adding Video file not accessible to log") - with open(download_dir + "\video_access_error.txt",'a') as videoaccerr: - videoaccerr.write(filename + " " + url +"\n") - -def decrypt(filename,keyid,video_title): - try: - key = keyfile[keyid] - print(key) - os.system(f"ffmpeg -y -decryption_key {key} -i {filename} -codec copy -metadata title={video_title} dec_{filename}") - except KeyError as error: - print("Key not found") - exit() - -def mux_process(outfile): - command = f"ffmpeg -y -i dec_audio.mp4 -i dec_video.mp4 -acodec copy -vcodec copy -metadata title=\"{video_title}\" {outfile}.mp4" - print(command) - os.system(command) - -def cleanup(path): - leftover_files = glob.glob(path + '/*.mp4', recursive=True) - mpd_files = glob.glob(path + '/*.mpd', recursive=True) - leftover_files = leftover_files + mpd_files - for file_list in leftover_files: - try: - os.remove(file_list) - except OSError: - print(f"Error deleting file: {file_list}") - - -if __name__ == "__main__": - mpd = "https://demo.com/stream.mpd" - base_url = mpd.split("stream.mpd")[0] - os.chdir(working_dir) - video_url,video_keyid,audio_url,audio_keyid = manifest_parser(mpd) - video_url = base_url + video_url - audio_url = base_url + audio_url - audio_filename = "audio.mp4" - video_filename = "video.mp4" - video_title = "Video Title" - audio_title = video_title - download(video_url,video_filename) - download(audio_url,audio_filename) - decrypt(video_filename,video_keyid,video_title) - decrypt(audio_filename,audio_keyid,audio_title) - final_file = download_dir + '/' + video_title - print(final_file) - mux_process(final_file) - cleanup(working_dir) diff --git a/dashdownloader_multisegment.py b/dashdownloader_multisegment.py index 017b8f3..a46b791 100644 --- a/dashdownloader_multisegment.py +++ b/dashdownloader_multisegment.py @@ -11,7 +11,7 @@ from mpegdash.utils import ( retry = 3 download_dir = os.getcwd() # set the folder to output working_dir = os.getcwd() + "\working_dir" # set the folder to download ephemeral files -keyfile_path = download_dir + "\keyfile_test.json" +keyfile_path = download_dir + "\keyfile.json" if not os.path.exists(working_dir): os.makedirs(working_dir) @@ -63,46 +63,35 @@ def durationtoseconds(period): def download_media(filename,url,epoch = 0): if(os.path.isfile(filename)): - media_head = requests.head(url, allow_redirects = True) - if media_head.status_code == 200: - media_length = int(media_head.headers.get("content-length")) - if(os.path.getsize(filename) >= media_length): - print("Video already downloaded.. skipping Downloading..") - else: - print("Redownloading faulty download..") - os.remove(filename) #Improve removing logic - download_media(filename,url) - else: - if (epoch > retry): - exit("Server doesn't support HEAD.") - download_media(filename,url,epoch + 1) + print("Segment already downloaded.. skipping..") else: media = requests.get(url, stream=True) media_length = int(media.headers.get("content-length")) if media.status_code == 200: - if(os.path.isfile(filename) and os.path.getsize(filename) >= video_length): - print("Video already downloaded.. skipping write to disk..") + if(os.path.isfile(filename) and os.path.getsize(filename) >= media_length): + print("Segment already downloaded.. skipping write to disk..") else: try: with open(filename, 'wb') as video_file: shutil.copyfileobj(media.raw, video_file) + print("Segment downloaded: " + filename) return False #Successfully downloaded the file except: - print("Connection error: Reattempting download of video..") + print("Connection error: Reattempting download of segment..") download_media(filename,url, epoch + 1) - if os.path.getsize(filename) >= video_length: + if os.path.getsize(filename) >= media_length: pass else: - print("Error downloaded video is faulty.. Retrying to download") + print("Segment is faulty.. Redownloading...") download_media(filename,url, epoch + 1) elif(media.status_code == 404): print("Probably end hit!\n",url) return True #Probably hit the last of the file else: if (epoch > retry): - exit("Error Video fetching exceeded retry times.") - print("Error fetching video file.. Retrying to download") + exit("Error fetching segment, exceeded retry times.") + print("Error fetching segment file.. Redownloading...") download_media(filename,url, epoch + 1) def cleanup(path): @@ -117,14 +106,14 @@ def cleanup(path): def mux_process(video_title,outfile): if os.name == "nt": - command = f"ffmpeg -y -i decrypted_audio.mp4 -i decrypted_video.mp4 -acodec copy -vcodec copy -fflags +bitexact -map_metadata -1 -metadata title=\"{video_title}\" -metadata creation_time=2020-00-00T70:05:30.000000Z {outfile}.mp4" + command = f"ffmpeg -y -i decrypted_audio.mp4 -i decrypted_video.mp4 -acodec copy -vcodec copy -fflags +bitexact -map_metadata -1 -metadata title=\"{video_title}\" -metadata creation_time=2020-00-00T70:05:30.000000Z \"{outfile}.mp4\"" else: command = f"nice -n 7 ffmpeg -y -i decrypted_audio.mp4 -i decrypted_video.mp4 -acodec copy -vcodec copy -fflags +bitexact -map_metadata -1 -metadata title=\"{video_title}\" -metadata creation_time=2020-00-00T70:05:30.000000Z {outfile}.mp4" os.system(command) -def decrypt(kid,filename): +def decrypt(filename): try: - key = keyfile[kid.lower()] + key = keyfile["0"] except KeyError as error: exit("Key not found") if(os.name == "nt"): @@ -134,31 +123,29 @@ def decrypt(kid,filename): def handle_irregular_segments(media_info,video_title,output_path): - no_segment,video_url,video_kid,video_extension,no_segment,audio_url,audio_kid,audio_extension = media_info - video_url = base_url + video_url - audio_url = base_url + audio_url - video_init = video_url.replace("$Number$","init") - audio_init = audio_url.replace("$Number$","init") - download_media("video_0.mp4",video_init) - download_media("audio_0.mp4",audio_init) + no_segment,video_url,video_init,video_extension,no_segment,audio_url,audio_init,audio_extension = media_info + download_media("video_0.seg.mp4",video_init) + download_media("audio_0.seg.mp4",audio_init) for count in range(1,no_segment): video_segment_url = video_url.replace("$Number$",str(count)) audio_segment_url = audio_url.replace("$Number$",str(count)) - video_status = download_media(f"video_{str(count)}.{video_extension}",video_segment_url) - audio_status = download_media(f"audio_{str(count)}.{audio_extension}",audio_segment_url) + video_status = download_media(f"video_{str(count)}.seg.{video_extension}",video_segment_url) + audio_status = download_media(f"audio_{str(count)}.seg.{audio_extension}",audio_segment_url) if(video_status): if os.name == "nt": - video_concat_command = "copy /b " + "+".join([f"video_{i}.{video_extension}" for i in range(0,count)]) + " encrypted_video.mp4" - audio_concat_command = "copy /b " + "+".join([f"audio_{i}.{audio_extension}" for i in range(0,count)]) + " encrypted_audio.mp4" + video_concat_command = "copy /b " + "+".join([f"video_{i}.seg.{video_extension}" for i in range(0,count)]) + " encrypted_video.mp4" + audio_concat_command = "copy /b " + "+".join([f"audio_{i}.seg.{audio_extension}" for i in range(0,count)]) + " encrypted_audio.mp4" else: - video_concat_command = "cat " + " ".join([f"video_{i}.{video_extension}" for i in range(0,count)]) + " > encrypted_video.mp4" - audio_concat_command = "cat " + " ".join([f"audio_{i}.{audio_extension}" for i in range(0,count)]) + " > encrypted_audio.mp4" + video_concat_command = "cat " + " ".join([f"video_{i}.seg.{video_extension}" for i in range(0,count)]) + " > encrypted_video.mp4" + audio_concat_command = "cat " + " ".join([f"audio_{i}.seg.{audio_extension}" for i in range(0,count)]) + " > encrypted_audio.mp4" + print(video_concat_command) + print(audio_concat_command) os.system(video_concat_command) os.system(audio_concat_command) + decrypt("video") + decrypt("audio") + mux_process(video_title,output_path) break - decrypt(video_kid,"video") - decrypt(audio_kid,"audio") - mux_process(video_title,output_path) def manifest_parser(mpd_url): @@ -181,36 +168,31 @@ def manifest_parser(mpd_url): total_segments = running_time / segment_time else: print("Media segments are of inequal timeframe") - print(segment.media) - approx_no_segments = int(running_time // 10) # aproximate of 10 sec per segment + + approx_no_segments = round(running_time / 6) + 20 # aproximate of 6 sec per segment print("Expected No of segments:",approx_no_segments) if(content_type == "audio/mp4"): segment_extension = segment.media.split(".")[-1] audio.append(approx_no_segments) audio.append(segment.media) + audio.append(segment.initialization) audio.append(segment_extension) elif(content_type == "video/mp4"): segment_extension = segment.media.split(".")[-1] video.append(approx_no_segments) video.append(segment.media) + video.append(segment.initialization) video.append(segment_extension) - for prot in repr.content_protections: - if(prot.value == "cenc"): - kId = prot.key_id.replace('-','') - if(content_type == "audio/mp4"): - audio.append(kId) - elif(content_type == "video/mp4"): - video.append(kId) return video + audio if __name__ == "__main__": - mpd = "https://www.example.com/index.mpd" + mpd = "https://www.udemy.com/assets/25653992/encrypted-files/out/v1/6d2a767a83064e7fa747bedeb1841f7d/06c8dc12da2745f1b0b4e7c2c032dfef/842d4b8e2e014fbbb87c640ddc89d036/index.mpd?token=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE2MjEzMjc0NzAsInBhdGgiOiJvdXQvdjEvNmQyYTc2N2E4MzA2NGU3ZmE3NDdiZWRlYjE4NDFmN2QvMDZjOGRjMTJkYTI3NDVmMWIwYjRlN2MyYzAzMmRmZWYvODQyZDRiOGUyZTAxNGZiYmI4N2M2NDBkZGM4OWQwMzYvIn0.4NpalcpDz0i5SXl6UYxnxECoacfRkJBHtKx5tWlJMOQ&provider=cloudfront&v=1" base_url = mpd.split("index.mpd")[0] os.chdir(working_dir) media_info = manifest_parser(mpd) - video_title = "Title of the video" - output_path = download_dir + "\output_video_name" + video_title = "171. Editing Collision Meshes" + output_path = download_dir + "\\171. Editing Collision Meshes" handle_irregular_segments(media_info,video_title,output_path) cleanup(working_dir) diff --git a/keyfile.example.json b/keyfile.example.json new file mode 100644 index 0000000..6500cf9 --- /dev/null +++ b/keyfile.example.json @@ -0,0 +1,3 @@ +{ + "KeyID": "key" +} diff --git a/keyfile.json b/keyfile.json deleted file mode 100644 index 658ca7e..0000000 --- a/keyfile.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "KeyID": "key", - "5071422d38cb0bea995d655dfc2e45dd": "23a02b9a0608aachee359af99cdacb87" -} \ No newline at end of file