diff --git a/README.md b/README.md index fd3ee7f..c181d5b 100644 --- a/README.md +++ b/README.md @@ -73,9 +73,6 @@ You can change some behaviors by tweaking the configuration file. * tqdm_show_progress: Whether to show progress during downloads or not. - Default Value: true -* fake_proxy: Speed up download for streaming film and series. **Dont work for anime, need to set to FALSE, only streamingcommunity** - - Default Value: false - * create_report: When enabled, this option saves the name of the series or movie being downloaded along with the date and file size in a CSV file, providing a log of downloaded content. - Default Value: false diff --git a/Src/Api/Altadefinizione/costant.py b/Src/Api/Altadefinizione/costant.py new file mode 100644 index 0000000..378c5c3 --- /dev/null +++ b/Src/Api/Altadefinizione/costant.py @@ -0,0 +1,4 @@ +# 26.05.24 + +STREAMING_FOLDER = "altadefinizione" +MOVIE_FOLDER = "Movie" \ No newline at end of file diff --git a/Src/Api/Altadefinizione/film.py b/Src/Api/Altadefinizione/film.py index e35648a..f6510ff 100644 --- a/Src/Api/Altadefinizione/film.py +++ b/Src/Api/Altadefinizione/film.py @@ -18,8 +18,7 @@ from .Core.Player.supervideo import VideoSource # Config ROOT_PATH = config_manager.get('DEFAULT', 'root_path') -STREAMING_FOLDER = "altadefinizione" -MOVIE_FOLDER = "Movie" +from .costant import STREAMING_FOLDER, MOVIE_FOLDER # Variable diff --git a/Src/Api/Animeunity/anime.py b/Src/Api/Animeunity/anime.py index 936b2fa..75dcb73 100644 --- a/Src/Api/Animeunity/anime.py +++ b/Src/Api/Animeunity/anime.py @@ -18,9 +18,7 @@ from .Core.Util import manage_selection # Config ROOT_PATH = config_manager.get('DEFAULT', 'root_path') -ANIME_FOLDER = "animeunity" -SERIES_FOLDER= "Serie" -MOVIE_FOLDER = "Movie" +from .costant import ANIME_FOLDER, SERIES_FOLDER, MOVIE_FOLDER # Variable diff --git a/Src/Api/Animeunity/costant.py b/Src/Api/Animeunity/costant.py new file mode 100644 index 0000000..68ec402 --- /dev/null +++ b/Src/Api/Animeunity/costant.py @@ -0,0 +1,5 @@ +# 26.05.24 + +ANIME_FOLDER = "animeunity" +SERIES_FOLDER= "Serie" +MOVIE_FOLDER = "Movie" \ No newline at end of file diff --git a/Src/Api/Streamingcommunity/costant.py b/Src/Api/Streamingcommunity/costant.py new file mode 100644 index 0000000..3c62330 --- /dev/null +++ b/Src/Api/Streamingcommunity/costant.py @@ -0,0 +1,7 @@ +# 26.05.24 + +STREAMING_FOLDER = "streamingcommunity" +MOVIE_FOLDER = "Movie" +SERIES_FOLDER = "Serie" + +SERVER_IP = ["57.129.7.85","57.129.7.188","57.129.7.174","57.129.4.77","57.129.16.196","57.129.16.156","57.129.16.139","57.129.16.135","57.129.13.175","51.38.112.237","51.195.107.7","51.195.107.230"] \ No newline at end of file diff --git a/Src/Api/Streamingcommunity/film.py b/Src/Api/Streamingcommunity/film.py index f9b971d..47b0e90 100644 --- a/Src/Api/Streamingcommunity/film.py +++ b/Src/Api/Streamingcommunity/film.py @@ -18,8 +18,7 @@ from .Core.Vix_player.player import VideoSource # Config ROOT_PATH = config_manager.get('DEFAULT', 'root_path') -STREAMING_FOLDER = "streamingcommunity" -MOVIE_FOLDER = "Movie" +from .costant import STREAMING_FOLDER, MOVIE_FOLDER, SERVER_IP # Variable @@ -60,4 +59,4 @@ def download_film(id_film: str, title_name: str, domain: str): Downloader( m3u8_playlist = master_playlist, output_filename = os.path.join(mp4_path, mp4_format) - ).start() \ No newline at end of file + ).start(SERVER_IP) \ No newline at end of file diff --git a/Src/Api/Streamingcommunity/series.py b/Src/Api/Streamingcommunity/series.py index 5040798..f10be43 100644 --- a/Src/Api/Streamingcommunity/series.py +++ b/Src/Api/Streamingcommunity/series.py @@ -20,8 +20,7 @@ from .Core.Util import manage_selection, map_episode_title # Config ROOT_PATH = config_manager.get('DEFAULT', 'root_path') -STREAMING_FOLDER = "streamingcommunity" -SERIES_FOLDER = "Serie" +from .costant import STREAMING_FOLDER, SERIES_FOLDER, SERVER_IP # Variable @@ -97,7 +96,7 @@ def donwload_video(tv_name: str, index_season_selected: int, index_episode_selec Downloader( m3u8_playlist = master_playlist, output_filename = os.path.join(mp4_path, mp4_name) - ).start() + ).start(SERVER_IP) def donwload_episode(tv_name: str, index_season_selected: int, donwload_all: bool = False) -> None: diff --git a/Src/Lib/FFmpeg/__init__.py b/Src/Lib/FFmpeg/__init__.py index 0346677..a36b247 100644 --- a/Src/Lib/FFmpeg/__init__.py +++ b/Src/Lib/FFmpeg/__init__.py @@ -3,7 +3,7 @@ from .command import ( join_video, join_audios, - join_subtitle + join_subtitle, ) from .util import print_duration_table from .installer import check_ffmpeg diff --git a/Src/Lib/FFmpeg/command.py b/Src/Lib/FFmpeg/command.py index 5841996..a518ad3 100644 --- a/Src/Lib/FFmpeg/command.py +++ b/Src/Lib/FFmpeg/command.py @@ -16,9 +16,9 @@ except: pass # Internal utilities -from Src.Util.os import check_file_existence from Src.Util._jsonConfig import config_manager -from .util import has_audio_stream +from Src.Util.os import check_file_existence +from .util import has_audio_stream, need_to_force_to_ts from .capture import capture_ffmpeg_real_time @@ -257,7 +257,7 @@ def __transcode_with_subtitles(video: str, subtitles_list: List[Dict[str, str]], # --> v 1.1 (new) -def join_video(video_path: str, out_path: str, vcodec: str = None, acodec: str = None, bitrate: str = None, force_ts = False): +def join_video(video_path: str, out_path: str, vcodec: str = None, acodec: str = None, bitrate: str = None): """ Joins single ts video file to mp4 @@ -281,8 +281,9 @@ def join_video(video_path: str, out_path: str, vcodec: str = None, acodec: str = ffmpeg_cmd.extend(['-hwaccel', 'cuda', '-hwaccel_output_format', 'cuda']) # Add mpegts to force to detect input file as ts file - if force_ts: + if need_to_force_to_ts(video_path): ffmpeg_cmd.extend(['-f', 'mpegts']) + vcodec = "libx264" # Insert input video path ffmpeg_cmd.extend(['-i', video_path]) diff --git a/Src/Lib/FFmpeg/util.py b/Src/Lib/FFmpeg/util.py index db9a30a..c186c66 100644 --- a/Src/Lib/FFmpeg/util.py +++ b/Src/Lib/FFmpeg/util.py @@ -105,3 +105,66 @@ def print_duration_table(file_path: str) -> None: if video_duration is not None: hours, minutes, seconds = format_duration(video_duration) console.log(f"[cyan]Duration for [white]([green]{os.path.basename(file_path)}[white]): [yellow]{int(hours)}[red]h [yellow]{int(minutes)}[red]m [yellow]{int(seconds)}[red]s") + + +def get_ffprobe_info(file_path): + """ + Get format and codec information for a media file using ffprobe. + + Args: + file_path (str): Path to the media file. + + Returns: + dict: A dictionary containing the format name and a list of codec names. + """ + try: + result = subprocess.run( + ['ffprobe', '-v', 'error', '-show_format', '-show_streams', '-print_format', 'json', file_path], + stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True, check=True + ) + output = result.stdout + info = json.loads(output) + + format_name = info['format']['format_name'] if 'format' in info else None + codec_names = [stream['codec_name'] for stream in info['streams']] if 'streams' in info else [] + + return { + 'format_name': format_name, + 'codec_names': codec_names + } + + except subprocess.CalledProcessError as e: + logging.error(f"ffprobe failed for file {file_path}: {e}") + return None + + except json.JSONDecodeError as e: + logging.error(f"Failed to parse JSON output from ffprobe for file {file_path}: {e}") + return None + +def is_png_format_or_codec(file_info): + """ + Check if the format is 'png_pipe' or if any codec is 'png'. + + Args: + file_info (dict): The dictionary containing file information. + + Returns: + bool: True if the format is 'png_pipe' or any codec is 'png', otherwise False. + """ + if not file_info: + return False + return file_info['format_name'] == 'png_pipe' or 'png' in file_info['codec_names'] + +def need_to_force_to_ts(file_path): + """ + Get if a file to TS format if it is in PNG format or contains a PNG codec. + + Args: + file_path (str): Path to the input media file. + """ + logging.info(f"Processing file: {file_path}") + file_info = get_ffprobe_info(file_path) + + if is_png_format_or_codec(file_info): + return True + return False diff --git a/Src/Lib/Hls/downloader.py b/Src/Lib/Hls/downloader.py index 6392a5b..5803afd 100644 --- a/Src/Lib/Hls/downloader.py +++ b/Src/Lib/Hls/downloader.py @@ -46,7 +46,6 @@ from ..E_Table import report_table DOWNLOAD_SPECIFIC_AUDIO = config_manager.get_list('M3U8_FILTER', 'specific_list_audio') DOWNLOAD_SPECIFIC_SUBTITLE = config_manager.get_list('M3U8_FILTER', 'specific_list_subtitles') REMOVE_SEGMENTS_FOLDER = config_manager.get_bool('M3U8_FILTER', 'cleanup_tmp_folder') -FORCE_TS = config_manager.get_dict('M3U8_FILTER', 'force_ts') FILTER_CUSTOM_REOLUTION = config_manager.get_int('M3U8_PARSER', 'force_resolution') CREATE_REPORT = config_manager.get_bool('M3U8_DOWNLOAD', 'create_report') @@ -80,13 +79,14 @@ class Downloader(): else: folder, base_name = os.path.split(self.output_filename) # Split file_folder output - base_name = reduce_base_name(remove_special_characters(transliterate(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.") sys.exit(0) self.output_filename = os.path.join(folder, base_name) + self.output_filename = transliterate(self.output_filename) logging.info(f"Output filename: {self.output_filename}") @@ -222,12 +222,15 @@ class Downloader(): if self.codec is not None: console.log(f"[cyan]Find codec [white]=> ([green]'v'[white]: [yellow]{self.codec.video_codec_name}[white], [green]'a'[white]: [yellow]{self.codec.audio_codec_name}[white], [green]'b'[white]: [yellow]{self.codec.bandwidth})") - def __donwload_video__(self): + def __donwload_video__(self, server_ip: list = None): """ Downloads and manages video segments. This method downloads video segments if necessary and updates the list of downloaded video segments. + + Args: + - server_ip (list): A list of IP addresses to use in requests. """ # Construct full path for the video segment directory @@ -243,6 +246,7 @@ class Downloader(): # Create an instance of M3U8_Segments to handle video segments video_m3u8 = M3U8_Segments(self.m3u8_index, full_path_video) + video_m3u8.add_server_ip(server_ip) # Get information about the video segments video_m3u8.get_info() @@ -253,12 +257,15 @@ class Downloader(): else: console.log("[cyan]Video [red]already exists.") - def __donwload_audio__(self): + def __donwload_audio__(self, server_ip: list = None): """ Downloads and manages audio segments. This method iterates over available audio tracks, downloads them if necessary, and updates the list of downloaded audio tracks. + + Args: + - server_ip (list): A list of IP addresses to use in requests. """ # Iterate over each available audio track @@ -285,6 +292,7 @@ class Downloader(): # If the audio segment directory doesn't exist, download audio segments audio_m3u8 = M3U8_Segments(obj_audio.get('uri'), full_path_audio) + audio_m3u8.add_server_ip(server_ip) # Get information about the audio segments audio_m3u8.get_info() @@ -361,7 +369,7 @@ class Downloader(): for future in futures: future.result() - def __join_video__(self, force_ts = False, vcodec = 'copy') -> str: + def __join_video__(self, vcodec = 'copy') -> str: """ Join downloaded video segments into a single video file. @@ -378,7 +386,6 @@ class Downloader(): join_video( video_path = self.downloaded_video[0].get('path'), out_path = path_join_video, - force_ts = force_ts, vcodec = vcodec ) @@ -459,9 +466,12 @@ class Downloader(): else: logging.info("Video file converted already exist.") - def start(self) -> None: + def start(self, server_ip: list = None) -> None: """ Start the process of fetching, downloading, joining, and cleaning up the video. + + Args: + - server_ip (list): A list of IP addresses to use in requests. """ # Check if file already exist @@ -492,8 +502,8 @@ class Downloader(): self.__manage_playlist__(m3u8_playlist_text) # Start all download ... - self.__donwload_video__() - self.__donwload_audio__() + self.__donwload_video__(server_ip) + self.__donwload_audio__(server_ip) self.__download_subtitle__() # Check file to convert @@ -529,10 +539,7 @@ class Downloader(): self.__donwload_video__() # Convert video - if FORCE_TS: - converted_out_path = self.__join_video__(force_ts=True, vcodec="libx264") - else: - converted_out_path = self.__join_video__() + converted_out_path = self.__join_video__() # Clean all tmp file self.__clean__(converted_out_path) diff --git a/Src/Lib/Hls/segments.py b/Src/Lib/Hls/segments.py index 507041a..18075fe 100644 --- a/Src/Lib/Hls/segments.py +++ b/Src/Lib/Hls/segments.py @@ -35,8 +35,6 @@ from ..M3U8 import ( # Config TQDM_MAX_WORKER = config_manager.get_int('M3U8_DOWNLOAD', 'tdqm_workers') TQDM_SHOW_PROGRESS = config_manager.get_int('M3U8_DOWNLOAD', 'tqdm_show_progress') -FAKE_PROXY = config_manager.get_float('M3U8_DOWNLOAD', 'fake_proxy') -FAKE_PROXY_IP = config_manager.get_list('M3U8_DOWNLOAD', 'fake_proxy_ip') REQUEST_TIMEOUT = config_manager.get_int('M3U8_REQUESTS', 'timeout') REQUEST_VERIFY_SSL = config_manager.get_bool('M3U8_REQUESTS', 'verify_ssl') REQUEST_DISABLE_ERROR = config_manager.get_bool('M3U8_REQUESTS', 'disable_error') @@ -67,8 +65,20 @@ class M3U8_Segments: self.ctrl_c_detected = False # Global variable to track Ctrl+C detection os.makedirs(self.tmp_folder, exist_ok=True) # Create the temporary folder if it does not exist - self.class_ts_estimator = M3U8_Ts_Estimator(TQDM_MAX_WORKER) + self.class_ts_estimator = M3U8_Ts_Estimator(TQDM_MAX_WORKER, 0) self.class_url_fixer = M3U8_UrlFix(url) + self.fake_proxy = False + + def add_server_ip(self, list_ip): + """ + Add server IP addresses + + Args: + list_ip (list): A list of IP addresses to be added. + """ + if list_ip is not None: + self.fake_proxy = True + self.fake_proxy_ip = list_ip def __get_key__(self, m3u8_parser: M3U8_Parser) -> bytes: """ @@ -143,7 +153,7 @@ class M3U8_Segments: logging.info(f"Generated new URL: {self.segments[i]}, from: {segment_url}") # Change IP address of server - if FAKE_PROXY: + if self.fake_proxy: for i in range(len(self.segments)): segment_url = self.segments[i] @@ -155,6 +165,9 @@ class M3U8_Segments: for item in self.segments: file.write(f"{item}\n") + # Update segments for estimator + self.class_ts_estimator.total_segments = len(self.segments) + def get_info(self) -> None: """ Makes a request to the index M3U8 file to get information about segments. @@ -183,42 +196,13 @@ class M3U8_Segments: Returns: str: The modified URL with the new IP address. """ - new_ip_address = FAKE_PROXY_IP[url_index % len(FAKE_PROXY_IP)] + new_ip_address = self.fake_proxy_ip[url_index % len(self.fake_proxy_ip)] # Parse the original URL and replace the hostname with the new IP address parsed_url = urlparse(url)._replace(netloc=new_ip_address) return urlunparse(parsed_url) - def update_progress_bar(self, segment_content: bytes, duration: float, progress_counter: tqdm) -> None: - """ - Updates the progress bar with information about the TS segment download. - - Args: - segment_content (bytes): The content of the downloaded TS segment. - duration (float): The duration of the segment download in seconds. - progress_counter (tqdm): The tqdm object representing the progress bar. - """ - if TQDM_SHOW_PROGRESS: - total_downloaded = len(segment_content) - - # Add the size of the downloaded segment to the estimator - self.class_ts_estimator.add_ts_file(total_downloaded * len(self.segments), total_downloaded, duration) - - # Get downloaded size and total estimated size - downloaded_file_size_str = self.class_ts_estimator.get_downloaded_size().split(' ')[0] - file_total_size = self.class_ts_estimator.calculate_total_size() - number_file_total_size = file_total_size.split(' ')[0] - units_file_total_size = file_total_size.split(' ')[1] - - average_internet_speed = self.class_ts_estimator.get_average_speed() - - # Update the progress bar's postfix - progress_counter.set_postfix_str( - f"{Colors.WHITE}[ {Colors.GREEN}{downloaded_file_size_str} {Colors.WHITE}< {Colors.GREEN}{number_file_total_size} {Colors.RED}{units_file_total_size} " - f"{Colors.WHITE}| {Colors.CYAN}{average_internet_speed:.2f} {Colors.RED}MB/s" - ) - def make_requests_stream(self, ts_url: str, index: int, stop_event: threading.Event, progress_bar: tqdm) -> None: """ Downloads a TS segment and adds it to the segment queue. @@ -248,7 +232,8 @@ class M3U8_Segments: # Get the content of the segment segment_content = response.content - self.update_progress_bar(segment_content, duration, progress_bar) + if TQDM_SHOW_PROGRESS: + self.class_ts_estimator.update_progress_bar(segment_content, duration, progress_bar) # Decrypt the segment content if decryption is needed if self.decryption is not None: @@ -315,8 +300,9 @@ class M3U8_Segments: total=len(self.segments), unit='s', ascii=' #', - bar_format=f"{Colors.YELLOW}Downloading {Colors.WHITE}({add_desc}{Colors.WHITE}): {Colors.RED}{{percentage:.0f}}% {Colors.MAGENTA}{{bar}} {Colors.YELLOW}{{elapsed}} {Colors.WHITE}< {Colors.CYAN}{{remaining}}{{postfix}} {Colors.WHITE}]", - dynamic_ncols=True + bar_format=f"{Colors.YELLOW}Downloading {Colors.WHITE}({add_desc}{Colors.WHITE}): {Colors.RED}{{percentage:.2f}}% {Colors.MAGENTA}{{bar}} {Colors.YELLOW}{{elapsed}} {Colors.WHITE}< {Colors.CYAN}{{remaining}}{{postfix}} {Colors.WHITE}]", + dynamic_ncols=True, + mininterval=0.01 ) def signal_handler(sig, frame): @@ -342,7 +328,7 @@ class M3U8_Segments: time.sleep(0.025) if self.ctrl_c_detected: - console.log("[red]1. Ctrl+C detected. Stopping further downloads.") + console.log("[red]Ctrl+C detected. Stopping further downloads.") stop_event.set() with self.condition: diff --git a/Src/Lib/M3U8/estimator.py b/Src/Lib/M3U8/estimator.py index 429bc85..682fbdf 100644 --- a/Src/Lib/M3U8/estimator.py +++ b/Src/Lib/M3U8/estimator.py @@ -2,17 +2,24 @@ from collections import deque + +# External libraries +from tqdm import tqdm + + # Internal utilities +from Src.Util.color import Colors from Src.Util.os import format_size class M3U8_Ts_Estimator: - def __init__(self, workers: int): + def __init__(self, workers: int, total_segments: int): """ Initialize the TSFileSizeCalculator object. Args: - workers (int): The number of workers using with ThreadPool. + - total_segments (int): Len of total segments to download """ self.ts_file_sizes = [] self.now_downloaded_size = 0 @@ -20,6 +27,7 @@ class M3U8_Ts_Estimator: self.list_speeds = deque(maxlen=self.average_over) self.smoothed_speeds = [] self.tqdm_workers = workers + self.total_segments = total_segments def add_ts_file(self, size: int, size_download: int, duration: float): """ @@ -33,6 +41,10 @@ class M3U8_Ts_Estimator: self.ts_file_sizes.append(size) self.now_downloaded_size += size_download + # Only for the start + if len(self.smoothed_speeds) <= 3: + size_download = size_download / self.tqdm_workers + # Calculate mbps speed_mbps = (size_download * 8) / (duration * 1_000_000) * self.tqdm_workers self.list_speeds.append(speed_mbps) @@ -78,4 +90,33 @@ class M3U8_Ts_Estimator: Returns: str: The total downloaded size as a human-readable string. """ - return format_size(self.now_downloaded_size) \ No newline at end of file + return format_size(self.now_downloaded_size) + + def update_progress_bar(self, segment_content: bytes, duration: float, progress_counter: tqdm) -> None: + """ + Updates the progress bar with information about the TS segment download. + + Args: + segment_content (bytes): The content of the downloaded TS segment. + duration (float): The duration of the segment download in seconds. + progress_counter (tqdm): The tqdm object representing the progress bar. + """ + total_downloaded = len(segment_content) + + # Add the size of the downloaded segment to the estimator + self.add_ts_file(total_downloaded * self.total_segments, total_downloaded, duration) + + # Get downloaded size and total estimated size + downloaded_file_size_str = self.get_downloaded_size().split(' ')[0] + file_total_size = self.calculate_total_size() + + # Fix parameter for prefix + number_file_total_size = file_total_size.split(' ')[0] + units_file_total_size = file_total_size.split(' ')[1] + average_internet_speed = self.get_average_speed() + + # Update the progress bar's postfix + progress_counter.set_postfix_str( + f"{Colors.WHITE}[ {Colors.GREEN}{downloaded_file_size_str} {Colors.WHITE}< {Colors.GREEN}{number_file_total_size} {Colors.RED}{units_file_total_size} " + f"{Colors.WHITE}| {Colors.CYAN}{average_internet_speed:.2f} {Colors.RED}MB/s" + ) \ No newline at end of file diff --git a/Src/Lib/Unidecode/__init__.py b/Src/Lib/Unidecode/__init__.py index 9993746..0d7ee8f 100644 --- a/Src/Lib/Unidecode/__init__.py +++ b/Src/Lib/Unidecode/__init__.py @@ -1,5 +1,7 @@ # 16.05.24 +from unidecode import unidecode + # Internal utilities from .alphabet import alpha_mappings @@ -31,5 +33,5 @@ all_mappings = { def transliterate(text): translated_text = ''.join(all_mappings.get(c, c) for c in text) - return translated_text - + transliterated_text = unidecode(translated_text) + return transliterated_text \ No newline at end of file diff --git a/Src/Lib/Unidecode/alphabet.py b/Src/Lib/Unidecode/alphabet.py index cefe61f..3f11b9b 100644 --- a/Src/Lib/Unidecode/alphabet.py +++ b/Src/Lib/Unidecode/alphabet.py @@ -4,26 +4,49 @@ alpha_mappings = { # Latin Alphabet 'À': 'A', 'Á': 'A', 'Â': 'A', 'Ã': 'A', 'Ä': 'A', 'Å': 'A', 'Æ': 'AE', - 'Ç': 'C', 'È': 'E', 'É': 'E', 'Ê': 'E', 'Ë': 'E', 'Ì': 'I', 'Í': 'I', 'Î': 'I', 'Ï': 'I', - 'Ð': 'D', 'Ñ': 'N', 'Ò': 'O', 'Ó': 'O', 'Ô': 'O', 'Õ': 'O', 'Ö': 'O', 'Ø': 'O', - 'Ù': 'U', 'Ú': 'U', 'Û': 'U', 'Ü': 'U', 'Ý': 'Y', 'Þ': 'Th', 'ß': 'ss', + 'Ç': 'C', 'È': 'E', 'É': 'E', 'Ê': 'E', 'Ë': 'E', + 'Ì': 'I', 'Í': 'I', 'Î': 'I', 'Ï': 'I', + 'Ð': 'D', 'Ñ': 'N', + 'Ò': 'O', 'Ó': 'O', 'Ô': 'O', 'Õ': 'O', 'Ö': 'O', 'Ø': 'O', + 'Ù': 'U', 'Ú': 'U', 'Û': 'U', 'Ü': 'U', + 'Ý': 'Y', + 'Þ': 'TH', + 'ß': 'ss', 'à': 'a', 'á': 'a', 'â': 'a', 'ã': 'a', 'ä': 'a', 'å': 'a', 'æ': 'ae', - 'ç': 'c', 'è': 'e', 'é': 'e', 'ê': 'e', 'ë': 'e', 'ì': 'i', 'í': 'i', 'î': 'i', 'ï': 'i', - 'ð': 'd', 'ñ': 'n', 'ò': 'o', 'ó': 'o', 'ô': 'o', 'õ': 'o', 'ö': 'o', 'ø': 'o', - 'ù': 'u', 'ú': 'u', 'û': 'u', 'ü': 'u', 'ý': 'y', 'þ': 'th', 'ÿ': 'y', - 'Ā': 'A', 'ā': 'a', 'Ă': 'A', 'ă': 'a', 'Ą': 'A', 'ą': 'a', 'Ć': 'C', 'ć': 'c', - 'Ĉ': 'C', 'ĉ': 'c', 'Ċ': 'C', 'ċ': 'c', 'Č': 'C', 'č': 'c', 'Ď': 'D', 'ď': 'd', 'Đ': 'D', 'đ': 'd', + 'ç': 'c', 'è': 'e', 'é': 'e', 'ê': 'e', 'ë': 'e', + 'ì': 'i', 'í': 'i', 'î': 'i', 'ï': 'i', + 'ð': 'd', 'ñ': 'n', + 'ò': 'o', 'ó': 'o', 'ô': 'o', 'õ': 'o', 'ö': 'o', 'ø': 'o', + 'ù': 'u', 'ú': 'u', 'û': 'u', 'ü': 'u', + 'ý': 'y', 'þ': 'th', 'ÿ': 'y', + 'Ā': 'A', 'ā': 'a', 'Ă': 'A', 'ă': 'a', 'Ą': 'A', 'ą': 'a', + 'Ć': 'C', 'ć': 'c', 'Ĉ': 'C', 'ĉ': 'c', 'Ċ': 'C', 'ċ': 'c', 'Č': 'C', 'č': 'c', + 'Ď': 'D', 'ď': 'd', 'Đ': 'D', 'đ': 'd', 'Ē': 'E', 'ē': 'e', 'Ĕ': 'E', 'ĕ': 'e', 'Ė': 'E', 'ė': 'e', 'Ę': 'E', 'ę': 'e', 'Ě': 'E', 'ě': 'e', - 'Ĝ': 'G', 'ĝ': 'g', 'Ğ': 'G', 'ğ': 'g', 'Ġ': 'G', 'ġ': 'g', 'Ģ': 'G', 'ģ': 'g', 'Ĥ': 'H', 'ĥ': 'h', - 'Ħ': 'H', 'ħ': 'h', 'Ĩ': 'I', 'ĩ': 'i', 'Ī': 'I', 'ī': 'i', 'Ĭ': 'I', 'ĭ': 'i', 'Į': 'I', 'į': 'i', - 'İ': 'I', 'ı': 'i', 'IJ': 'IJ', 'ij': 'ij', 'Ĵ': 'J', 'ĵ': 'j', 'Ķ': 'K', 'ķ': 'k', 'ĸ': 'k', 'Ĺ': 'L', - 'ĺ': 'l', 'Ļ': 'L', 'ļ': 'l', 'Ľ': 'L', 'ľ': 'l', 'Ŀ': 'L', 'ŀ': 'l', 'Ł': 'L', 'ł': 'l', 'Ń': 'N', - 'ń': 'n', 'Ņ': 'N', 'ņ': 'n', 'Ň': 'N', 'ň': 'n', 'ʼn': 'n', 'Ō': 'O', 'ō': 'o', 'Ŏ': 'O', 'ŏ': 'o', - 'Ő': 'O', 'ő': 'o', 'Ŕ': 'R', 'ŕ': 'r', 'Ŗ': 'R', 'ŗ': 'r', 'Ř': 'R', 'ř': 'r', 'Ś': 'S', 'ś': 's', - 'Ŝ': 'S', 'ŝ': 's', 'Ş': 'S', 'ş': 's', 'Š': 'S', 'š': 's', 'Ţ': 'T', 'ţ': 't', 'Ť': 'T', 'ť': 't', - 'Ŧ': 'T', 'ŧ': 't', 'Ũ': 'U', 'ũ': 'u', 'Ū': 'U', 'ū': 'u', 'Ŭ': 'U', 'ŭ': 'u', 'Ů': 'U', 'ů': 'u', - 'Ű': 'U', 'ű': 'u', 'Ų': 'U', 'ų': 'u', 'Ŵ': 'W', 'ŵ': 'w', 'Ŷ': 'Y', 'ŷ': 'y', 'Ÿ': 'Y', 'Ź': 'Z', - 'ź': 'z', 'Ż': 'Z', 'ż': 'z', 'Ž': 'Z', 'ž': 'z', + 'Ĝ': 'G', 'ĝ': 'g', 'Ğ': 'G', 'ğ': 'g', 'Ġ': 'G', 'ġ': 'g', 'Ģ': 'G', 'ģ': 'g', + 'Ĥ': 'H', 'ĥ': 'h', 'Ħ': 'H', 'ħ': 'h', + 'Ĩ': 'I', 'ĩ': 'i', 'Ī': 'I', 'ī': 'i', 'Ĭ': 'I', 'ĭ': 'i', 'Į': 'I', 'į': 'i', + 'İ': 'I', 'ı': 'i', + 'IJ': 'IJ', 'ij': 'ij', + 'Ĵ': 'J', 'ĵ': 'j', + 'Ķ': 'K', 'ķ': 'k', 'ĸ': 'k', + 'Ĺ': 'L', 'ĺ': 'l', 'Ļ': 'L', 'ļ': 'l', 'Ľ': 'L', 'ľ': 'l', 'Ŀ': 'L', 'ŀ': 'l', 'Ł': 'L', 'ł': 'l', + 'Ń': 'N', 'ń': 'n', 'Ņ': 'N', 'ņ': 'n', 'Ň': 'N', 'ň': 'n', 'ʼn': 'n', 'Ŋ': 'N', 'ŋ': 'n', + 'Ō': 'O', 'ō': 'o', 'Ŏ': 'O', 'ŏ': 'o', 'Ő': 'O', 'ő': 'o', + 'Œ': 'OE', 'œ': 'oe', + 'Ŕ': 'R', 'ŕ': 'r', 'Ŗ': 'R', 'ŗ': 'r', 'Ř': 'R', 'ř': 'r', + 'Ś': 'S', 'ś': 's', 'Ŝ': 'S', 'ŝ': 's', 'Ş': 'S', 'ş': 's', 'Š': 'S', 'š': 's', + 'Ţ': 'T', 'ţ': 't', 'Ť': 'T', 'ť': 't', 'Ŧ': 'T', 'ŧ': 't', + 'Ũ': 'U', 'ũ': 'u', 'Ū': 'U', 'ū': 'u', 'Ŭ': 'U', 'ŭ': 'u', 'Ů': 'U', 'ů': 'u', + 'Ű': 'U', 'ű': 'u', 'Ų': 'U', 'ų': 'u', + 'Ŵ': 'W', 'ŵ': 'w', + 'Ŷ': 'Y', 'ŷ': 'y', 'Ÿ': 'Y', + 'Ź': 'Z', 'ź': 'z', 'Ż': 'Z', 'ż': 'z', 'Ž': 'Z', 'ž': 'z', + 'ƒ': 'f', + 'Ơ': 'O', 'ơ': 'o', 'Ư': 'U', 'ư': 'u', + 'Ǎ': 'A', 'ǎ': 'a', 'Ǐ': 'I', 'ǐ': 'i', 'Ǒ': 'O', 'ǒ': 'o', 'Ǔ': 'U', 'ǔ': 'u', + 'Ǖ': 'U', 'ǖ': 'u', 'Ǘ': 'U', 'ǘ': 'u', 'Ǚ': 'U', 'ǚ': 'u', 'Ǜ': 'U', 'ǜ': 'u', + 'Ǻ': 'A', 'ǻ': 'a', 'Ǽ': 'AE', 'ǽ': 'ae', 'Ǿ': 'O', 'ǿ': 'o', # Cyrillic Alphabet 'А': 'A', 'Б': 'B', 'В': 'V', 'Г': 'G', 'Д': 'D', 'Е': 'E', 'Ё': 'E', 'Ж': 'Zh', 'З': 'Z', 'И': 'I', @@ -109,4 +132,10 @@ alpha_mappings = { 'ù': 'u', 'ú': 'u', 'ũ': 'u', 'ụ': 'u', 'ủ': 'u', 'ứ': 'u', 'ừ': 'u', 'ử': 'u', 'ữ': 'u', 'ự': 'u', 'ỳ': 'y', 'ý': 'y', 'ỹ': 'y', 'ỷ': 'y', 'ỵ': 'y', + + # Devanagari + 'अ': 'a', 'आ': 'aa', 'इ': 'i', 'ई': 'ii', 'उ': 'u', 'ऊ': 'uu', 'ए': 'e', 'ऐ': 'ai', 'ओ': 'o', 'औ': 'au', 'क': 'ka', 'ख': 'kha', 'ग': 'ga', 'घ': 'gha', 'ङ': 'nga', 'च': 'cha', + 'छ': 'chha', 'ज': 'ja', 'झ': 'jha', 'ञ': 'nya', 'ट': 'ta', 'ठ': 'tha', 'ड': 'da', 'ढ': 'dha', 'ण': 'na', 'त': 'ta', 'थ': 'tha', 'द': 'da', 'ध': 'dha', 'न': 'na', 'प': 'pa', + 'फ': 'pha', 'ब': 'ba', 'भ': 'bha', 'म': 'ma', 'य': 'ya', 'र': 'ra', 'ल': 'la', 'व': 'va', 'श': 'sha', 'ष': 'ssha', 'स': 'sa', 'ह': 'ha', 'ा': 'a', + } diff --git a/Src/Lib/Unidecode/units_of_measurement.py b/Src/Lib/Unidecode/units_of_measurement.py index 37d0744..b42489f 100644 --- a/Src/Lib/Unidecode/units_of_measurement.py +++ b/Src/Lib/Unidecode/units_of_measurement.py @@ -13,6 +13,4 @@ units_of_measurement_mapping = { 'mm³': 'cubic millimeters', 'km²': 'square kilometers', 'km³': 'cubic kilometers', - 'ha': 'hectares', - 'ha': 'hectares', } \ No newline at end of file diff --git a/config.json b/config.json index daa1ff5..f19a900 100644 --- a/config.json +++ b/config.json @@ -18,14 +18,11 @@ "M3U8_DOWNLOAD": { "tdqm_workers": 30, "tqdm_show_progress": true, - "fake_proxy": false, - "fake_proxy_ip": ["57.129.7.85","57.129.7.188","57.129.7.174","57.129.4.77","57.129.16.196","57.129.16.156","57.129.16.139","57.129.16.135","57.129.13.175","57.129.13.157","51.38.112.237","51.195.107.7","51.195.107.230"], "create_report": false }, "M3U8_FILTER": { "use_codec": false, "use_gpu": false, - "force_ts": false, "default_preset": "ultrafast", "cleanup_tmp_folder": true, "specific_list_audio": ["ita"],