From 1c5f960b16db50953329f9e4682619db39dc8634 Mon Sep 17 00:00:00 2001 From: Francesco Grazioso <40018163+FrancescoGrazioso@users.noreply.github.com> Date: Sat, 1 Jun 2024 13:13:09 +0200 Subject: [PATCH] Feat/app api and frontend (#140) * minor fixes * created basic django app * add django dependency * created basic search endpoint * created retrieve method for search * remove retrieve * start implementing download endpoint (only movie for now) * start implementing episode info for series * finished get_episodes_info * minor fixes * add download anime episode * start implementing download for tv series * refactor methods * finished download endpoint (will implement possibility to download single episodes of season in tv series) * new domain and black on project * start * add cors * add start gui command * add gui for search * edited .gitignore * create component for media details * better UX/UI * edited anime episode to stream response (better experience) * implemented UI for media details (TODO add download capabilities) * fix poster fetching * minor fixes * fix cors error * start implementing download * fix typing on anime movies * refactor * refactor + add download OVA * add plot for all anime types * add download for all anime episodes * add download all tv series episodes * fix crach if localStorage is undefined * moved download logic in separeted file * fix wrong index passed while downloading tv series * fix style searchbar * add loader to search button and add enter listener while searching * remove dependency from loading episodes to download all in anime series * add function to download selected episodes for anime * add sh command to kill gui * fix messages in kill_gui.sh * start implementing download select episodes for tv series (to be tested) + run black and eslint * start refactoring to version 2.0 * start implementing preview endpoint * finish reimplement version 2.0 --- Src/Api/Animeunity/anime.py | 19 ++++--- Src/Api/Streamingcommunity/series.py | 30 ++++++----- Src/Lib/Hls/segments.py | 6 ++- api/endpoints/urls.py | 4 +- api/endpoints/views.py | 74 +++++++++++----------------- frontend/src/views/Details.vue | 6 +-- 6 files changed, 66 insertions(+), 73 deletions(-) diff --git a/Src/Api/Animeunity/anime.py b/Src/Api/Animeunity/anime.py index 26a6357..e250a89 100644 --- a/Src/Api/Animeunity/anime.py +++ b/Src/Api/Animeunity/anime.py @@ -26,37 +26,40 @@ video_source = VideoSource() -def download_episode(index_select: int): +def download_episode(index_select: int, custom_video_source: VideoSource = None): """ Downloads the selected episode. Args: - index_select (int): Index of the episode to download. """ + + active_video_source = custom_video_source if custom_video_source is not None else video_source + # Get information about the selected episode - obj_episode = video_source.get_info_episode(index_select) + obj_episode = active_video_source.get_info_episode(index_select) start_message() console.print(f"[yellow]Download: [red]EP_{obj_episode.number} \n") # Get the embed URL for the episode - embed_url = video_source.get_embed(obj_episode.id) + embed_url = active_video_source.get_embed(obj_episode.id) # Parse parameter in embed text - video_source.parse_script(embed_url) + active_video_source.parse_script(embed_url) # 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, ANIME_FOLDER, SERIES_FOLDER, video_source.series_name) + if active_video_source.is_series: + mp4_path = os.path.join(ROOT_PATH, ANIME_FOLDER, SERIES_FOLDER, active_video_source.series_name) else: - mp4_path = os.path.join(ROOT_PATH, ANIME_FOLDER, MOVIE_FOLDER, video_source.series_name) + mp4_path = os.path.join(ROOT_PATH, ANIME_FOLDER, MOVIE_FOLDER, active_video_source.series_name) # Crete downloader obj_download = Downloader( - m3u8_playlist = video_source.get_playlist(), + m3u8_playlist = active_video_source.get_playlist(), output_filename = os.path.join(mp4_path, mp4_name) ) diff --git a/Src/Api/Streamingcommunity/series.py b/Src/Api/Streamingcommunity/series.py index f10be43..0046e35 100644 --- a/Src/Api/Streamingcommunity/series.py +++ b/Src/Api/Streamingcommunity/series.py @@ -66,7 +66,7 @@ def display_episodes_list() -> str: return last_command -def donwload_video(tv_name: str, index_season_selected: int, index_episode_selected: int) -> None: +def donwload_video(tv_name: str, index_season_selected: int, index_episode_selected: int, custom_video_source: VideoSource = None) -> None: """ Download a single episode video. @@ -76,10 +76,12 @@ def donwload_video(tv_name: str, index_season_selected: int, index_episode_selec - index_episode_selected (int): Index of the selected episode. """ + active_video_source = custom_video_source if custom_video_source is not None else video_source + start_message() # Get info about episode - obj_episode = video_source.obj_episode_manager.episodes[index_episode_selected - 1] + obj_episode = active_video_source.obj_episode_manager.episodes[index_episode_selected - 1] console.print(f"[yellow]Download: [red]{index_season_selected}:{index_episode_selected} {obj_episode.name}") print() @@ -88,9 +90,9 @@ def donwload_video(tv_name: str, index_season_selected: int, index_episode_selec mp4_path = os.path.join(ROOT_PATH, STREAMING_FOLDER, SERIES_FOLDER, tv_name, f"S{index_season_selected}") # Retrieve scws and if available master playlist - video_source.get_iframe(obj_episode.id) - video_source.get_content() - master_playlist = video_source.get_playlist() + active_video_source.get_iframe(obj_episode.id) + active_video_source.get_content() + master_playlist = active_video_source.get_playlist() # Download the episode Downloader( @@ -99,7 +101,7 @@ def donwload_video(tv_name: str, index_season_selected: int, index_episode_selec ).start(SERVER_IP) -def donwload_episode(tv_name: str, index_season_selected: int, donwload_all: bool = False) -> None: +def donwload_episode(tv_name: str, index_season_selected: int, donwload_all: bool = False, custom_video_source: VideoSource = None) -> None: """ Download all episodes of a season. @@ -109,19 +111,21 @@ def donwload_episode(tv_name: str, index_season_selected: int, donwload_all: boo - donwload_all (bool): Donwload all seasons episodes """ + active_video_source = custom_video_source if custom_video_source is not None else video_source + # Clean memory of all episodes and get the number of the season (some dont follow rule of [1,2,3,4,5] but [1,2,3,145,5,6,7]). - video_source.obj_episode_manager.clear() - season_number = (video_source.obj_title_manager.titles[index_season_selected-1].number) + active_video_source.obj_episode_manager.clear() + season_number = (active_video_source.obj_title_manager.titles[index_season_selected-1].number) # Start message and collect information about episodes start_message() - video_source.collect_title_season(season_number) - episodes_count = video_source.obj_episode_manager.get_length() + active_video_source.collect_title_season(season_number) + episodes_count = active_video_source.obj_episode_manager.get_length() # Download all episodes wihtout ask if donwload_all: for i_episode in range(1, episodes_count+1): - donwload_video(tv_name, index_season_selected, i_episode) + donwload_video(tv_name, index_season_selected, i_episode, active_video_source) console.print(f"\n[red]Download [yellow]season: [red]{index_season_selected}.") @@ -134,12 +138,12 @@ def donwload_episode(tv_name: str, index_season_selected: int, donwload_all: boo # Download selected episodes if len(list_episode_select) == 1 and last_command != "*": - donwload_video(tv_name, index_season_selected, list_episode_select[0]) + donwload_video(tv_name, index_season_selected, list_episode_select[0],active_video_source) # Download all other episodes selecter else: for i_episode in list_episode_select: - donwload_video(tv_name, index_season_selected, i_episode) + donwload_video(tv_name, index_season_selected, i_episode, active_video_source) def download_series(tv_id: str, tv_name: str, version: str, domain: str) -> None: diff --git a/Src/Lib/Hls/segments.py b/Src/Lib/Hls/segments.py index beeb831..e69f28d 100644 --- a/Src/Lib/Hls/segments.py +++ b/Src/Lib/Hls/segments.py @@ -319,7 +319,11 @@ class M3U8_Segments: self.condition.notify_all() # Wake up the writer thread if it's waiting # Register the signal handler for Ctrl+C - signal.signal(signal.SIGINT, signal_handler) + try: + signal.signal(signal.SIGINT, signal_handler) + except Exception as e: + logging.error(f"Failed to register signal handler: {e}") + logging.error("Ctrl+C detection may not work.") with ThreadPoolExecutor(max_workers=TQDM_MAX_WORKER) as executor: diff --git a/api/endpoints/urls.py b/api/endpoints/urls.py index 184bb2f..8d2802c 100644 --- a/api/endpoints/urls.py +++ b/api/endpoints/urls.py @@ -1,10 +1,10 @@ from rest_framework import routers -from .views import SearchView#, DownloadView +from .views import SearchView, DownloadView router = routers.DefaultRouter() router.register(r"search", SearchView, basename="search") -#router.register(r"download", DownloadView, basename="download") +router.register(r"download", DownloadView, basename="download") urlpatterns = router.urls diff --git a/api/endpoints/views.py b/api/endpoints/views.py index 0df7062..8c7df92 100644 --- a/api/endpoints/views.py +++ b/api/endpoints/views.py @@ -7,10 +7,16 @@ from rest_framework import viewsets from rest_framework.decorators import action from rest_framework.response import Response + +from Src.Api.Animeunity.anime import donwload_film +from Src.Api.Animeunity.anime import download_episode as donwload_anime_episode from Src.Api.Animeunity import title_search as anime_search from Src.Api.Animeunity.Core.Vix_player.player import VideoSource as anime_source from Src.Api.Animeunity.site import media_search_manager as anime_media_manager + +from Src.Api.Streamingcommunity.film import download_film +from Src.Api.Streamingcommunity.series import donwload_episode as download_tv_episode from Src.Api.Streamingcommunity import title_search as sc_search, get_version_and_domain from Src.Api.Streamingcommunity.Core.Vix_player.player import VideoSource as film_video_source from Src.Api.Streamingcommunity.site import media_search_manager as film_media_manager @@ -151,7 +157,7 @@ class SearchView(viewsets.ViewSet): return Response({"error": "No media found with that search query"}) -''' + class DownloadView(viewsets.ViewSet): def create(self, request): @@ -171,59 +177,36 @@ class DownloadView(viewsets.ViewSet): case "MOVIE": download_film(self.media_id, self.media_slug, self.domain) case "TV": - video_source = VideoSource() - video_source.set_url_base_name(STREAM_SITE_NAME) - video_source.set_version(self.site_version) - video_source.set_domain(self.domain) - video_source.set_series_name(self.media_slug) - video_source.set_media_id(self.media_id) + video_source = film_video_source() + video_source.setup( + version = self.site_version, + domain = self.domain, + media_id = self.media_id, + series_name = self.media_slug + ) video_source.collect_info_seasons() video_source.obj_episode_manager.clear() - video_source.collect_title_season(self.download_id) - episodes_count = video_source.obj_episode_manager.get_length() - for i_episode in range(1, episodes_count + 1): - episode_id = video_source.obj_episode_manager.episodes[ - i_episode - 1 - ].id + seasons_count = video_source.obj_title_manager.get_length() - # Define filename and path for the downloaded video - mp4_name = remove_special_characters( - f"{map_episode_title(self.media_slug,video_source.obj_episode_manager.episodes[i_episode - 1],self.download_id)}.mp4" - ) - mp4_path = remove_special_characters( - os.path.join( - ROOT_PATH, - SERIES_FOLDER, - self.media_slug, - f"S{self.download_id}", - ) - ) - os.makedirs(mp4_path, exist_ok=True) - - # Get iframe and content for the episode - video_source.get_iframe(episode_id) - video_source.get_content() - video_source.set_url_base_name(STREAM_SITE_NAME) - - # Download the episode - obj_download = Downloader( - m3u8_playlist=video_source.get_playlist(), - key=video_source.get_key(), - output_filename=os.path.join(mp4_path, mp4_name), - ) - - obj_download.download_m3u8() + for i_season in range(1, seasons_count + 1): + download_tv_episode(self.media_slug, i_season, True, video_source) case "TV_ANIME": - episodes_downloader = EpisodeDownloader( - self.media_id, self.media_slug + video_source = anime_source() + video_source.setup( + media_id = self.media_id, + series_name = self.media_slug ) - episodes_downloader.download_episode(self.download_id) + episoded_count = video_source.get_count_episodes() + for i in range(episoded_count): + donwload_anime_episode(i, video_source) case "OVA" | "SPECIAL": - anime_download_film( - id_film=self.media_id, title_name=self.media_slug + donwload_film( + id_film=self.media_id, + title_name=self.media_slug + ) case _: raise Exception("Type media not supported") @@ -238,4 +221,3 @@ class DownloadView(viewsets.ViewSet): } return Response(response_dict) -''' diff --git a/frontend/src/views/Details.vue b/frontend/src/views/Details.vue index 5880730..9e6a95e 100644 --- a/frontend/src/views/Details.vue +++ b/frontend/src/views/Details.vue @@ -148,7 +148,7 @@ const downloadAllItems = async () => { @click.prevent="downloadAllItems"> Scarica {{['TV_ANIME', 'TV'].includes(item.type)? 'tutto' : ''}} -