From e34bce85ed23df6b964de83b01734da35d3d41bb Mon Sep 17 00:00:00 2001 From: Lovi-0 Date: Tue, 25 Jun 2024 22:39:57 +0200 Subject: [PATCH] - Fix join multiple ep animeunity - Add _failed to file with missing ts files. - Fix requirement --- .gitignore | 3 +- Src/Api/animeunity/Core/Class/EpisodeType.py | 13 +++++-- Src/Api/animeunity/Core/Player/vixcloud.py | 6 +-- Src/Api/animeunity/anime.py | 41 +++++++++++--------- Src/Lib/Downloader/HLS/downloader.py | 14 +++++-- Src/Lib/Downloader/HLS/segments.py | 1 + Src/Lib/M3U8/parser.py | 26 +++++++++++-- Test/t_m3u8_parser.py | 5 +-- requirements.txt | 3 +- 9 files changed, 75 insertions(+), 37 deletions(-) diff --git a/.gitignore b/.gitignore index 00eda45..b3cc9d0 100644 --- a/.gitignore +++ b/.gitignore @@ -57,5 +57,6 @@ venv.bak/ # Other Video +note.txt list_proxy.txt -note \ No newline at end of file +config.json \ No newline at end of file diff --git a/Src/Api/animeunity/Core/Class/EpisodeType.py b/Src/Api/animeunity/Core/Class/EpisodeType.py index 9f19cac..bdac48c 100644 --- a/Src/Api/animeunity/Core/Class/EpisodeType.py +++ b/Src/Api/animeunity/Core/Class/EpisodeType.py @@ -34,17 +34,22 @@ class EpisodeManager: episode = Episode(episode_data) self.episodes.append(episode) - def get_episode_by_index(self, index: int) -> Episode: + def find_episode_by_id(self, episode_id: int) -> Episode: """ - Get an episode by its index. + Get an episode by its id. Args: - - index (int): Index of the episode to retrieve. + - episode_id (int): Index of the episode to retrieve. Returns: Episode: The episode object. """ - return self.episodes[index] + + for episode in self.episodes: + if episode.id == episode_id: + return episode + + return None def get_length(self) -> int: """ diff --git a/Src/Api/animeunity/Core/Player/vixcloud.py b/Src/Api/animeunity/Core/Player/vixcloud.py index d0766c5..d3dfe0e 100644 --- a/Src/Api/animeunity/Core/Player/vixcloud.py +++ b/Src/Api/animeunity/Core/Player/vixcloud.py @@ -62,7 +62,7 @@ class VideoSource: """ try: - response = httpx.get(f"https://www.{self.base_name}.{self.domain}/info_api/{self.media_id}/") + response = httpx.get(f"https://www.{self.base_name}.{self.domain}/info_api/{self.media_id}/", headers=self.headers) response.raise_for_status() # Parse JSON response and return episode count @@ -89,7 +89,7 @@ class VideoSource: "end_range": index_ep + 1 } - response = httpx.get(f"https://www.{self.base_name}.{self.domain}/info_api/{self.media_id}/{index_ep}", params = params) + response = httpx.get(f"https://www.{self.base_name}.{self.domain}/info_api/{self.media_id}/{index_ep}", headers=self.headers, params=params, timeout=5) response.raise_for_status() # Return information about the episode @@ -112,7 +112,7 @@ class VideoSource: """ try: - response = httpx.get(f"https://www.{self.base_name}.{self.domain}/embed-url/{episode_id}") + response = httpx.get(f"https://www.{self.base_name}.{self.domain}/embed-url/{episode_id}", headers=self.headers) response.raise_for_status() # Extract and clean embed URL diff --git a/Src/Api/animeunity/anime.py b/Src/Api/animeunity/anime.py index 01e9d47..37fedd8 100644 --- a/Src/Api/animeunity/anime.py +++ b/Src/Api/animeunity/anime.py @@ -32,28 +32,33 @@ def download_episode(index_select: int): # Get information about the selected episode obj_episode = video_source.get_info_episode(index_select) - start_message() - console.print(f"[yellow]Download: [red]EP_{obj_episode.number} \n") + if obj_episode is not None: - # Get the embed URL for the episode - embed_url = video_source.get_embed(obj_episode.id) + start_message() + console.print(f"[yellow]Download: [red]EP_{obj_episode.number} \n") - # Parse parameter in embed text - video_source.parse_script(embed_url) + # Get the embed URL for the episode + embed_url = video_source.get_embed(obj_episode.id) + + # Parse parameter in embed text + video_source.parse_script(embed_url) + + # Create output path + mp4_path = None + mp4_name = f"{obj_episode.number}.mp4" + if video_source.is_series: + mp4_path = os.path.join(ROOT_PATH, SITE_NAME, SERIES_FOLDER, video_source.series_name) + else: + mp4_path = os.path.join(ROOT_PATH, SITE_NAME, MOVIE_FOLDER, video_source.series_name) + + # Start downloading + HLS_Downloader( + m3u8_playlist = video_source.get_playlist(), + output_filename = os.path.join(mp4_path, mp4_name) + ).start() - # Create output path - mp4_path = None - mp4_name = f"{index_select + 1}.mp4" - if video_source.is_series: - mp4_path = os.path.join(ROOT_PATH, SITE_NAME, SERIES_FOLDER, video_source.series_name) else: - mp4_path = os.path.join(ROOT_PATH, SITE_NAME, MOVIE_FOLDER, video_source.series_name) - - # Start downloading - HLS_Downloader( - m3u8_playlist = video_source.get_playlist(), - output_filename = os.path.join(mp4_path, mp4_name) - ).start() + logging.error(f"Skip index: {index_select} cant find info with api.") def donwload_series(tv_id: int, tv_name: str): diff --git a/Src/Lib/Downloader/HLS/downloader.py b/Src/Lib/Downloader/HLS/downloader.py index b52832f..554c878 100644 --- a/Src/Lib/Downloader/HLS/downloader.py +++ b/Src/Lib/Downloader/HLS/downloader.py @@ -88,7 +88,7 @@ class HLS_Downloader(): # For missing output_filename folder, base_name = os.path.split(self.output_filename) # Split file_folder output - base_name = reduce_base_name(remove_special_characters(base_name)) # Remove special char + base_name = reduce_base_name(remove_special_characters(base_name)) # Remove special char create_folder(folder) # Create folder and check if exist if not can_create_file(base_name): # Check if folder file name can be create logging.error("Invalid mp4 name.") @@ -158,6 +158,7 @@ class HLS_Downloader(): # Create an instance of the M3U8_Parser class obj_parse = M3U8_Parser() + self.main_obj_parser = obj_parse # Extract information about the M3U8 playlist obj_parse.parse_data( @@ -227,7 +228,6 @@ class HLS_Downloader(): if self.codec is not None: console.print(f"[cyan]Codec [white]=> ([green]'v'[white]: [yellow]{self.codec.video_codec_name}[white] ([green]b[white]: [yellow]{self.codec.video_bitrate // 1000}k[white]), [green]'a'[white]: [yellow]{self.codec.audio_codec_name}[white] ([green]b[white]: [yellow]{self.codec.audio_bitrate // 1000}k[white]))") - def __donwload_video__(self): """ Downloads and manages video segments. @@ -254,9 +254,12 @@ class HLS_Downloader(): video_m3u8.get_info() # Download the video segments - video_m3u8.download_streams(f"{Colors.MAGENTA}video") self.expected_real_time = video_m3u8.expected_real_time + list_available_resolution_size = self.main_obj_parser._video.get_list_resolution_and_size(video_m3u8.expected_real_time_s) + #console.print(f"[cyan]Estimate size [white]=> [red]{sorted(list_available_resolution_size, reverse=True)}") + video_m3u8.download_streams(f"{Colors.MAGENTA}video") + # Get time of output file print_duration_table(os.path.join(full_path_video, "0.ts")) @@ -496,7 +499,10 @@ class HLS_Downloader(): )) # Delete all files except the output file - delete_files_except_one(self.base_path, os.path.basename(self.output_filename)) + if not missing_ts: + delete_files_except_one(self.base_path, os.path.basename(self.output_filename)) + else: + delete_files_except_one(self.base_path, os.path.basename(self.output_filename.replace(".mp4", "_failed.mp4"))) # Remove the base folder if REMOVE_SEGMENTS_FOLDER: diff --git a/Src/Lib/Downloader/HLS/segments.py b/Src/Lib/Downloader/HLS/segments.py index 3e6c45c..5a6bfeb 100644 --- a/Src/Lib/Downloader/HLS/segments.py +++ b/Src/Lib/Downloader/HLS/segments.py @@ -127,6 +127,7 @@ class M3U8_Segments: #console.log(f"[red]Expected duration after download: {m3u8_parser.get_duration()}") #console.log(f"[red]There is key: [yellow]{m3u8_parser.keys is not None}") self.expected_real_time = m3u8_parser.get_duration(return_string=False) + self.expected_real_time_s = m3u8_parser.duration # Check if there is an encryption key in the playlis if m3u8_parser.keys is not None: diff --git a/Src/Lib/M3U8/parser.py b/Src/Lib/M3U8/parser.py index 4d13e95..fa977a4 100644 --- a/Src/Lib/M3U8/parser.py +++ b/Src/Lib/M3U8/parser.py @@ -6,6 +6,7 @@ import logging # Internal utilities from m3u8 import loads +from Src.Util.os import format_file_size # External libraries @@ -228,6 +229,24 @@ class M3U8_Video: list: A list of resolutions extracted from the video playlist. """ return [video['resolution'] for video in self.video_playlist] + + def get_list_resolution_and_size(self, duration): + """ + Retrieve a list of resolutions and size from the video playlist. + + Args: + - duration (int): Total duration of the video in 's'. + + Returns: + list: A list of resolutions extracted from the video playlist. + """ + result = [] + + for video in self.video_playlist: + video_size = format_file_size((video['bandwidth'] * duration) / 8) + result.append((video_size)) + + return result class M3U8_Audio: @@ -474,7 +493,8 @@ class M3U8_Parser: self.video_playlist.append({ "uri": playlist.uri, - "resolution": playlist.stream_info.resolution + "resolution": playlist.stream_info.resolution, + "bandwidth": playlist.stream_info.bandwidth }) if there_is_codec: @@ -485,7 +505,8 @@ class M3U8_Parser: self.video_playlist.append({ "uri": playlist.uri, - "resolution": M3U8_Parser.extract_resolution(playlist.uri) + "resolution": M3U8_Parser.extract_resolution(playlist.uri), + "bandwidth": playlist.stream_info.bandwidth }) if there_is_codec: @@ -609,7 +630,6 @@ class M3U8_Parser: minutes, seconds = divmod(remainder, 60) - # Format the duration string with colors if return_string: return f"[yellow]{int(hours)}[red]h [yellow]{int(minutes)}[red]m [yellow]{int(seconds)}[red]s" diff --git a/Test/t_m3u8_parser.py b/Test/t_m3u8_parser.py index e57010e..c2ae48b 100644 --- a/Test/t_m3u8_parser.py +++ b/Test/t_m3u8_parser.py @@ -14,7 +14,6 @@ def read_file(file_path): # Import -from Src.Lib.M3U8.parser import M3U8 from Src.Lib.M3U8 import M3U8_Parser @@ -31,5 +30,5 @@ playlist = read_file(os.path.join(base_path_file, "playlist.m3u8")) # Test class -obj_m3u8_parser.parse_data("http", index_audio) -print(f"Duration : {obj_m3u8_parser.get_duration()}") +obj_m3u8_parser.parse_data("", playlist) +print(obj_m3u8_parser._video.get_list_resolution_and_size(50000)) \ No newline at end of file diff --git a/requirements.txt b/requirements.txt index 35a5e13..8b98cc7 100644 --- a/requirements.txt +++ b/requirements.txt @@ -8,4 +8,5 @@ m3u8 psutil unidecode fake-useragent -qbittorrent-api \ No newline at end of file +qbittorrent-api +python-qbittorrent \ No newline at end of file