From dc2263f008ce4fef67825a2cf7e66315aa37efcf Mon Sep 17 00:00:00 2001 From: Graziano Nobile <119879688+Giann8@users.noreply.github.com> Date: Sat, 1 Feb 2025 15:33:34 +0100 Subject: [PATCH] Stop handling for MP$ and HLS formats (#238) * Added MP4 stop signal handler for series * Upgraded stopped option handling for HLS * Upgraded stop handling for HLS and MP4 --- .../Api/Site/altadefinizionegratis/film.py | 6 ++- .../Api/Site/animeunity/film_serie.py | 24 ++++++++--- StreamingCommunity/Api/Site/cb01new/film.py | 5 +++ .../Api/Site/ddlstreamitaly/series.py | 17 ++++++-- .../Api/Site/guardaserie/series.py | 15 +++++-- .../Api/Site/mostraguarda/film.py | 6 ++- .../Api/Site/streamingcommunity/film.py | 5 +++ .../Api/Site/streamingcommunity/series.py | 22 +++++++--- .../Lib/Downloader/HLS/downloader.py | 14 ++++-- .../Lib/Downloader/HLS/segments.py | 4 +- .../Lib/Downloader/MP4/downloader.py | 43 +++++++++++++++---- 11 files changed, 127 insertions(+), 34 deletions(-) diff --git a/StreamingCommunity/Api/Site/altadefinizionegratis/film.py b/StreamingCommunity/Api/Site/altadefinizionegratis/film.py index 609802d..2e857c8 100644 --- a/StreamingCommunity/Api/Site/altadefinizionegratis/film.py +++ b/StreamingCommunity/Api/Site/altadefinizionegratis/film.py @@ -40,7 +40,8 @@ def download_film(select_title: MediaItem) -> str: # Start message and display film information start_message() console.print(f"[yellow]Download: [red]{select_title.name} \n") - + console.print(f"[cyan]You can safely stop the download with [bold]Ctrl+c[bold] [cyan] \n") + # Set domain and media ID for the video source video_source = VideoSource(select_title.url) @@ -64,6 +65,9 @@ def download_film(select_title: MediaItem) -> str: if msg.ask("[green]Do you want to continue [white]([red]y[white])[green] or return at home[white]([red]n[white]) ", choices=['y', 'n'], default='y', show_choices=True) == "n": frames = get_call_stack() execute_search(frames[-4])""" + if r_proc == None: + if os.path.exists(os.path.join(mp4_path, title_name)): + os.remove(os.path.join(mp4_path, title_name)) if r_proc != None: console.print("[green]Result: ") diff --git a/StreamingCommunity/Api/Site/animeunity/film_serie.py b/StreamingCommunity/Api/Site/animeunity/film_serie.py index c7442bb..d099219 100644 --- a/StreamingCommunity/Api/Site/animeunity/film_serie.py +++ b/StreamingCommunity/Api/Site/animeunity/film_serie.py @@ -24,10 +24,10 @@ from StreamingCommunity.Api.Player.vixcloud import VideoSourceAnime # Variable from .costant import SITE_NAME, ANIME_FOLDER, MOVIE_FOLDER +KILL_HANDLER = bool(False) - -def download_episode(index_select: int, scrape_serie: ScrapeSerieAnime, video_source: VideoSourceAnime) -> str: +def download_episode(index_select: int, scrape_serie: ScrapeSerieAnime, video_source: VideoSourceAnime) -> tuple[str,bool]: """ Downloads the selected episode. @@ -36,6 +36,7 @@ def download_episode(index_select: int, scrape_serie: ScrapeSerieAnime, video_so Return: - str: output path + - bool: kill handler status """ # Get information about the selected episode @@ -45,7 +46,8 @@ def download_episode(index_select: int, scrape_serie: ScrapeSerieAnime, video_so start_message() console.print(f"[yellow]Download: [red]EP_{obj_episode.number} \n") - + console.print("[cyan]You can safely stop the download with [bold]Ctrl+c[bold] [cyan] \n") + # Collect mp4 url video_source.get_embed(obj_episode.id) @@ -65,11 +67,18 @@ def download_episode(index_select: int, scrape_serie: ScrapeSerieAnime, video_so os_manager.create_path(mp4_path) # Start downloading + r_proc = MP4_downloader( url=str(video_source.src_mp4).strip(), path=os.path.join(mp4_path, title_name) ) - + + # If download fails do not create the file + if r_proc == None: + if os.path.exists(os.path.join(mp4_path, title_name)): + os.remove(os.path.join(mp4_path, title_name)) + return "",True + if r_proc != None: console.print("[green]Result: ") console.print(r_proc) @@ -106,12 +115,15 @@ def download_series(select_title: MediaItem): # Download selected episodes if len(list_episode_select) == 1 and last_command != "*": - download_episode(list_episode_select[0]-1, scrape_serie, video_source) + download_episode(list_episode_select[0]-1, scrape_serie, video_source)[0] # Download all other episodes selecter else: + kill_handler=bool(False) for i_episode in list_episode_select: - download_episode(i_episode-1, scrape_serie, video_source) + if kill_handler: + break + kill_handler= download_episode(i_episode-1, scrape_serie, video_source)[1] def download_film(select_title: MediaItem): diff --git a/StreamingCommunity/Api/Site/cb01new/film.py b/StreamingCommunity/Api/Site/cb01new/film.py index aa98e13..5969350 100644 --- a/StreamingCommunity/Api/Site/cb01new/film.py +++ b/StreamingCommunity/Api/Site/cb01new/film.py @@ -38,6 +38,7 @@ def download_film(select_title: MediaItem) -> str: # Start message and display film information start_message() console.print(f"[yellow]Download: [red]{select_title.name} \n") + console.print(f"[cyan]You can safely stop the download with [bold]Ctrl+c[bold] [cyan] \n") # Setup api manger print(select_title.url) @@ -64,6 +65,10 @@ def download_film(select_title: MediaItem) -> str: frames = get_call_stack() execute_search(frames[-4])""" + if r_proc == None: + if os.path.exists(os.path.join(mp4_path, title_name)): + os.remove(os.path.join(mp4_path, title_name)) + if r_proc != None: console.print("[green]Result: ") console.print(r_proc) diff --git a/StreamingCommunity/Api/Site/ddlstreamitaly/series.py b/StreamingCommunity/Api/Site/ddlstreamitaly/series.py index ec18bf1..e55a154 100644 --- a/StreamingCommunity/Api/Site/ddlstreamitaly/series.py +++ b/StreamingCommunity/Api/Site/ddlstreamitaly/series.py @@ -28,7 +28,7 @@ from .costant import SERIES_FOLDER -def download_video(index_episode_selected: int, scape_info_serie: GetSerieInfo, video_source: VideoSource) -> str: +def download_video(index_episode_selected: int, scape_info_serie: GetSerieInfo, video_source: VideoSource) -> tuple[str,bool]: """ Download a single episode video. @@ -38,13 +38,14 @@ def download_video(index_episode_selected: int, scape_info_serie: GetSerieInfo, Return: - str: output path + - bool: kill handler status """ start_message() # Get info about episode obj_episode = scape_info_serie.list_episodes[index_episode_selected - 1] - console.print(f"[yellow]Download: [red]{obj_episode.get('name')}") - print() + console.print(f"[yellow]Download: [red]{obj_episode.get('name')}\n") + console.print(f"[cyan]You can safely stop the download with [bold]Ctrl+c[bold] [cyan] \n") # Define filename and path for the downloaded video title_name = os_manager.get_sanitize_file( @@ -70,6 +71,11 @@ def download_video(index_episode_selected: int, scape_info_serie: GetSerieInfo, path=os.path.join(mp4_path, title_name), referer=f"{parsed_url.scheme}://{parsed_url.netloc}/", ) + + if r_proc == None: + if os.path.exists(os.path.join(mp4_path, title_name)): + os.remove(os.path.join(mp4_path, title_name)) + return "",True if r_proc != None: console.print("[green]Result: ") @@ -105,8 +111,11 @@ def download_thread(dict_serie: MediaItem): return # Download selected episodes + kill_handler = bool(False) for i_episode in list_episode_select: - download_video(i_episode, scape_info_serie, video_source) + if kill_handler: + break + kill_handler = download_video(i_episode, scape_info_serie, video_source)[1] def display_episodes_list(obj_episode_manager) -> str: diff --git a/StreamingCommunity/Api/Site/guardaserie/series.py b/StreamingCommunity/Api/Site/guardaserie/series.py index 70d8fa0..757e445 100644 --- a/StreamingCommunity/Api/Site/guardaserie/series.py +++ b/StreamingCommunity/Api/Site/guardaserie/series.py @@ -44,9 +44,8 @@ def download_video(index_season_selected: int, index_episode_selected: int, scap # Get info about episode obj_episode = scape_info_serie.list_episodes[index_episode_selected - 1] - console.print(f"[yellow]Download: [red]{index_season_selected}:{index_episode_selected} {obj_episode.get('name')}") - print() - + console.print(f"[yellow]Download: [red]{index_season_selected}:{index_episode_selected} {obj_episode.get('name')}\n") + console.print(f"[cyan]You can safely stop the download with [bold]Ctrl+c[bold] [cyan] \n") # Define filename and path for the downloaded video mp4_name = f"{map_episode_title(scape_info_serie.tv_name, index_season_selected, index_episode_selected, obj_episode.get('name'))}.mp4" mp4_path = os.path.join(SERIES_FOLDER, scape_info_serie.tv_name, f"S{index_season_selected}") @@ -70,6 +69,13 @@ def download_video(index_season_selected: int, index_episode_selected: int, scap if msg.ask("[green]Do you want to continue [white]([red]y[white])[green] or return at home[white]([red]n[white]) ", choices=['y', 'n'], default='y', show_choices=True) == "n": frames = get_call_stack() execute_search(frames[-4])""" + + # Removes file not completed and stops other downloads + if r_proc == None: + if os.path.exists(os.path.join(mp4_path, mp4_name)): + os.remove(os.path.join(mp4_path, mp4_name)) + return "",True + if r_proc != None: console.print("[green]Result: ") @@ -113,7 +119,10 @@ def download_episode(scape_info_serie: GetSerieInfo, index_season_selected: int, return # Download selected episodes + stopped = bool(False) for i_episode in list_episode_select: + if stopped: + break download_video(index_season_selected, i_episode, scape_info_serie) diff --git a/StreamingCommunity/Api/Site/mostraguarda/film.py b/StreamingCommunity/Api/Site/mostraguarda/film.py index b7889d9..7c2a859 100644 --- a/StreamingCommunity/Api/Site/mostraguarda/film.py +++ b/StreamingCommunity/Api/Site/mostraguarda/film.py @@ -50,7 +50,7 @@ def download_film(movie_details: Json_film) -> str: # Start message and display film information start_message() console.print(f"[yellow]Download: [red]{movie_details.title} \n") - + console.print(f"[cyan]You can safely stop the download with [bold]Ctrl+c[bold] [cyan] \n") # Make request to main site try: url = f"https://{SITE_NAME}.{DOMAIN_NOW}/set-movie-a/{movie_details.imdb_id}" @@ -94,6 +94,10 @@ def download_film(movie_details: Json_film) -> str: frames = get_call_stack() execute_search(frames[-4])""" + if r_proc == None: + if os.path.exists(os.path.join(mp4_path, title_name)): + os.remove(os.path.join(mp4_path, title_name)) + if r_proc != None: console.print("[green]Result: ") console.print(r_proc) diff --git a/StreamingCommunity/Api/Site/streamingcommunity/film.py b/StreamingCommunity/Api/Site/streamingcommunity/film.py index 436cca6..627afee 100644 --- a/StreamingCommunity/Api/Site/streamingcommunity/film.py +++ b/StreamingCommunity/Api/Site/streamingcommunity/film.py @@ -40,6 +40,7 @@ def download_film(select_title: MediaItem) -> str: # Start message and display film information start_message() console.print(f"[yellow]Download: [red]{select_title.name} \n") + console.print(f"[cyan]You can safely stop the download with [bold]Ctrl+c[bold] [cyan] \n") # Init class video_source = VideoSource(SITE_NAME, False) @@ -68,6 +69,10 @@ def download_film(select_title: MediaItem) -> str: frames = get_call_stack() execute_search(frames[-4])""" + if r_proc == None: + if os.path.exists(os.path.join(mp4_path, title_name)): + os.remove(os.path.join(mp4_path, title_name)) + if r_proc != None: console.print("[green]Result: ") console.print(r_proc) diff --git a/StreamingCommunity/Api/Site/streamingcommunity/series.py b/StreamingCommunity/Api/Site/streamingcommunity/series.py index 36da453..4a9415d 100644 --- a/StreamingCommunity/Api/Site/streamingcommunity/series.py +++ b/StreamingCommunity/Api/Site/streamingcommunity/series.py @@ -28,7 +28,7 @@ from .costant import SITE_NAME, SERIES_FOLDER -def download_video(index_season_selected: int, index_episode_selected: int, scrape_serie: ScrapeSerie, video_source: VideoSource) -> str: +def download_video(index_season_selected: int, index_episode_selected: int, scrape_serie: ScrapeSerie, video_source: VideoSource) -> tuple[str,bool]: """ Download a single episode video. @@ -38,15 +38,16 @@ def download_video(index_season_selected: int, index_episode_selected: int, scra Return: - str: output path + - bool: kill handler status """ start_message() index_season_selected = dynamic_format_number(index_season_selected) # Get info about episode obj_episode = scrape_serie.episode_manager.get(index_episode_selected - 1) - console.print(f"[yellow]Download: [red]{index_season_selected}:{index_episode_selected} {obj_episode.name}") - print() - + console.print(f"[yellow]Download: [red]{index_season_selected}:{index_episode_selected} {obj_episode.name}\n") + console.print(f"[cyan]You can safely stop the download with [bold]Ctrl+c[bold] [cyan] \n") + # Define filename and path for the downloaded video mp4_name = f"{map_episode_title(scrape_serie.series_name, index_season_selected, index_episode_selected, obj_episode.name)}.mp4" mp4_path = os.path.join(SERIES_FOLDER, scrape_serie.series_name, f"S{index_season_selected}") @@ -70,6 +71,12 @@ def download_video(index_season_selected: int, index_episode_selected: int, scra frames = get_call_stack() execute_search(frames[-4])""" + if r_proc == None: + if os.path.exists(os.path.join(mp4_path, mp4_name)): + os.remove(os.path.join(mp4_path, mp4_name)) + return "",True + + if r_proc != None: console.print("[green]Result: ") console.print(r_proc) @@ -112,9 +119,12 @@ def download_episode(index_season_selected: int, scrape_serie: ScrapeSerie, vide console.print(f"[red]{str(e)}") return - # Download selected episodes + # Download selected episodes if not stopped + stopped = bool(False) for i_episode in list_episode_select: - download_video(index_season_selected, i_episode, scrape_serie, video_source) + if stopped: + break + stopped=download_video(index_season_selected, i_episode, scrape_serie, video_source)[1] def download_series(select_season: MediaItem, version: str) -> None: """ diff --git a/StreamingCommunity/Lib/Downloader/HLS/downloader.py b/StreamingCommunity/Lib/Downloader/HLS/downloader.py index 4a004b9..2ab36fe 100644 --- a/StreamingCommunity/Lib/Downloader/HLS/downloader.py +++ b/StreamingCommunity/Lib/Downloader/HLS/downloader.py @@ -416,7 +416,7 @@ class ContentDownloader: # Download the video streams and print status info_dw = video_m3u8.download_streams(f"{Colors.MAGENTA}video", "video") list_MissingTs.append(info_dw) - + self.stopped=list_MissingTs.pop() # Print duration information of the downloaded video #print_duration_table(downloaded_video[0].get('path')) @@ -447,7 +447,7 @@ class ContentDownloader: # Download the audio segments and print status info_dw = audio_m3u8.download_streams(f"{Colors.MAGENTA}audio {Colors.RED}{obj_audio.get('language')}", f"audio_{obj_audio.get('language')}") list_MissingTs.append(info_dw) - + self.stopped=list_MissingTs.pop() # Print duration information of the downloaded audio #print_duration_table(obj_audio.get('path')) @@ -710,6 +710,8 @@ class ContentJoiner: class HLS_Downloader: + stopped = bool(False) + def __init__(self, output_filename: str=None, m3u8_playlist: str=None, m3u8_index: str=None, is_playlist_url: bool=True, is_index_url: bool=True): """ Initializes the HLS_Downloader class. @@ -820,9 +822,11 @@ class HLS_Downloader: return None else: + if self.stopped: + return self.stopped return { 'path': self.output_filename, - 'url': self.m3u8_playlist + 'url': self.m3u8_playlist, } else: @@ -847,9 +851,11 @@ class HLS_Downloader: return None else: + if self.stopped: + return None return { 'path': self.output_filename, - 'url': self.m3u8_index + 'url': self.m3u8_index, } else: diff --git a/StreamingCommunity/Lib/Downloader/HLS/segments.py b/StreamingCommunity/Lib/Downloader/HLS/segments.py index d33d7f1..4229738 100644 --- a/StreamingCommunity/Lib/Downloader/HLS/segments.py +++ b/StreamingCommunity/Lib/Downloader/HLS/segments.py @@ -570,4 +570,6 @@ class M3U8_Segments: console.print("[yellow]⚠ Warning:[/yellow] Too many retries detected! Consider reducing the number of [cyan]workers[/cyan] in the [magenta]config.json[/magenta] file. This will impact [bold]performance[/bold]. \n") # Info to return - return {'type': type, 'nFailed': self.info_nFailed} \ No newline at end of file + if self.download_interrupted: + return {'type': type, 'nFailed': self.info_nFailed, 'stopped': bool(True)} + return {'type': type, 'nFailed': self.info_nFailed, 'stopped': bool(False)} \ No newline at end of file diff --git a/StreamingCommunity/Lib/Downloader/MP4/downloader.py b/StreamingCommunity/Lib/Downloader/MP4/downloader.py index 51c1be7..fa09dda 100644 --- a/StreamingCommunity/Lib/Downloader/MP4/downloader.py +++ b/StreamingCommunity/Lib/Downloader/MP4/downloader.py @@ -1,11 +1,12 @@ # 09.06.24 import os +import signal import sys import ssl import certifi import logging - +import atexit # External libraries import httpx @@ -34,7 +35,9 @@ GET_ONLY_LINK = config_manager.get_bool('M3U8_PARSER', 'get_only_link') TQDM_USE_LARGE_BAR = config_manager.get_int('M3U8_DOWNLOAD', 'tqdm_use_large_bar') REQUEST_TIMEOUT = config_manager.get_float('REQUESTS', 'timeout') - +#Ending constant +KILL_HANDLER = bool(False) + def MP4_downloader(url: str, path: str, referer: str = None, headers_: dict = None): """ @@ -46,6 +49,7 @@ def MP4_downloader(url: str, path: str, referer: str = None, headers_: dict = No - referer (str, optional): The referer header value. - headers_ (dict, optional): Custom headers for the request. """ + # Early return for link-only mode if GET_ONLY_LINK: return {'path': path, 'url': url} @@ -111,15 +115,34 @@ def MP4_downloader(url: str, path: str, referer: str = None, headers_: dict = No # Ensure directory exists os.makedirs(os.path.dirname(path), exist_ok=True) + + def signal_handler(*args): + """ + Signal handler for SIGINT + + Parameters: + - args (tuple): The signal arguments (to prevent errors). + """ + + if(downloaded MAX_DOWNLOAD_SIZE: # break @@ -133,7 +156,7 @@ def MP4_downloader(url: str, path: str, referer: str = None, headers_: dict = No title=f"{os.path.basename(path.replace('.mp4', ''))}", border_style="green" )) - return path + return path,KILL_HANDLER else: console.print("[bold red]Download failed or file is empty.[/bold red]") @@ -153,3 +176,7 @@ def MP4_downloader(url: str, path: str, referer: str = None, headers_: dict = No logging.error(f"Unexpected error during download: {e}") console.print(f"[bold red]Unexpected Error: {e}[/bold red]") return None + + except KeyboardInterrupt: + console.print("[bold red]Download stopped by user.[/bold red]") + return None \ No newline at end of file