From 837bcf31106c2fb26c300f84fd8936efea65ce17 Mon Sep 17 00:00:00 2001 From: Lovi <62809003+Lovi-0@users.noreply.github.com> Date: Mon, 16 Dec 2024 22:18:04 +0100 Subject: [PATCH] Ea porsea gha fato i porsei --- README.md | 10 +- .../Api/Site/1337xx/__init__.py | 6 +- StreamingCommunity/Api/Site/1337xx/site.py | 2 + .../Api/Site/altadefinizione/__init__.py | 6 +- .../Api/Site/altadefinizione/film.py | 11 +- .../Api/Site/altadefinizione/site.py | 7 +- .../Api/Site/animeunity/__init__.py | 6 +- .../Api/Site/animeunity/film_serie.py | 11 +- .../Api/Site/animeunity/site.py | 2 + .../Api/Site/bitsearch/__init__.py | 52 --- .../Api/Site/bitsearch/costant.py | 15 - StreamingCommunity/Api/Site/bitsearch/site.py | 84 ----- .../Api/Site/cb01new/__init__.py | 6 +- StreamingCommunity/Api/Site/cb01new/film.py | 14 +- StreamingCommunity/Api/Site/cb01new/site.py | 4 +- .../Api/Site/ddlstreamitaly/__init__.py | 6 +- .../Api/Site/ddlstreamitaly/series.py | 13 +- .../Api/Site/ddlstreamitaly/site.py | 2 + .../Api/Site/guardaserie/__init__.py | 6 +- .../Api/Site/guardaserie/series.py | 10 +- .../Api/Site/guardaserie/site.py | 4 +- .../{piratebays => ilcorsaronero}/__init__.py | 9 +- .../{piratebays => ilcorsaronero}/costant.py | 0 .../Api/Site/ilcorsaronero/site.py | 63 ++++ .../{bitsearch => ilcorsaronero}/title.py | 11 +- .../ilcorsaronero/util/ilCorsarScraper.py | 139 ++++++++ .../Api/Site/mostraguarda/__init__.py | 6 +- .../Api/Site/mostraguarda/film.py | 11 +- .../Api/Site/piratebays/site.py | 89 ----- .../Api/Site/piratebays/title.py | 45 --- .../Api/Site/streamingcommunity/__init__.py | 8 +- .../Api/Site/streamingcommunity/film.py | 11 +- .../Api/Site/streamingcommunity/series.py | 28 +- .../Api/Site/streamingcommunity/site.py | 3 + .../Lib/Downloader/HLS/downloader.py | 8 +- .../Lib/Downloader/HLS/segments.py | 2 +- .../Lib/Downloader/TOR/downloader.py | 310 +++++++++++------- StreamingCommunity/Upload/version.py | 2 +- StreamingCommunity/Util/table.py | 12 +- StreamingCommunity/run.py | 4 +- Test/Download/TOR.py | 2 +- config.json | 11 +- setup.py | 2 +- 43 files changed, 543 insertions(+), 510 deletions(-) delete mode 100644 StreamingCommunity/Api/Site/bitsearch/__init__.py delete mode 100644 StreamingCommunity/Api/Site/bitsearch/costant.py delete mode 100644 StreamingCommunity/Api/Site/bitsearch/site.py rename StreamingCommunity/Api/Site/{piratebays => ilcorsaronero}/__init__.py (87%) rename StreamingCommunity/Api/Site/{piratebays => ilcorsaronero}/costant.py (100%) create mode 100644 StreamingCommunity/Api/Site/ilcorsaronero/site.py rename StreamingCommunity/Api/Site/{bitsearch => ilcorsaronero}/title.py (86%) create mode 100644 StreamingCommunity/Api/Site/ilcorsaronero/util/ilCorsarScraper.py delete mode 100644 StreamingCommunity/Api/Site/piratebays/site.py delete mode 100644 StreamingCommunity/Api/Site/piratebays/title.py diff --git a/README.md b/README.md index d4422c6..3911fd6 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# StreamingCommunity Downloader 🎬 +# StreamingCommunity Downloader ![Project Logo](https://i.ibb.co/f4h5Y2m/min-logo.png) @@ -26,6 +26,9 @@ Chat, contribute, and have fun in our **Git_StreamingCommunity** Discord [Server - [Docker](#docker) - [Tutorial](#tutorials) - [To Do](#to-do) +- [Support](#support) +- [Contribute](#contributing) +- [Disclamer](#disclaimer) @@ -372,12 +375,11 @@ The `run-container` command mounts also the `config.json` file, so any change to | 1337xx | ✅ | | Altadefinizione | ✅ | | AnimeUnity | ✅ | -| BitSearch | ✅ | +| Ilcorsaronero | ✅ | | CB01New | ✅ | | DDLStreamItaly | ✅ | | GuardaSerie | ✅ | | MostraGuarda | ✅ | -| PirateBays | ✅ | | StreamingCommunity | ✅ | # Tutorials @@ -391,7 +393,7 @@ The `run-container` command mounts also the `config.json` file, so any change to - Create website API -> https://github.com/Lovi-0/StreamingCommunity/tree/test_gui_1 -# SUPPORT +# Support If you'd like to support this project, consider making a donation! diff --git a/StreamingCommunity/Api/Site/1337xx/__init__.py b/StreamingCommunity/Api/Site/1337xx/__init__.py index 6e3dcd2..5634497 100644 --- a/StreamingCommunity/Api/Site/1337xx/__init__.py +++ b/StreamingCommunity/Api/Site/1337xx/__init__.py @@ -1,6 +1,6 @@ # 02.07.24 -from unidecode import unidecode +from urllib.parse import quote_plus # Internal utilities @@ -30,7 +30,7 @@ def search(string_to_search: str = None, get_onylDatabase: bool = False): string_to_search = msg.ask(f"\n[purple]Insert word to search in [red]{SITE_NAME}").strip() # Search on database - len_database = title_search(unidecode(string_to_search)) + len_database = title_search(quote_plus(string_to_search)) # Return list of elements if get_onylDatabase: @@ -48,4 +48,4 @@ def search(string_to_search: str = None, get_onylDatabase: bool = False): console.print(f"\n[red]Nothing matching was found for[white]: [purple]{string_to_search}") # Retry - search() + search() \ No newline at end of file diff --git a/StreamingCommunity/Api/Site/1337xx/site.py b/StreamingCommunity/Api/Site/1337xx/site.py index 92f6fd4..31b375b 100644 --- a/StreamingCommunity/Api/Site/1337xx/site.py +++ b/StreamingCommunity/Api/Site/1337xx/site.py @@ -34,6 +34,8 @@ def title_search(word_to_search: str) -> int: Returns: - int: The number of titles found. """ + media_search_manager.clear() + table_show_manager.clear() # Find new domain if prev dont work max_timeout = config_manager.get_int("REQUESTS", "timeout") diff --git a/StreamingCommunity/Api/Site/altadefinizione/__init__.py b/StreamingCommunity/Api/Site/altadefinizione/__init__.py index d46bfb7..83baddc 100644 --- a/StreamingCommunity/Api/Site/altadefinizione/__init__.py +++ b/StreamingCommunity/Api/Site/altadefinizione/__init__.py @@ -1,6 +1,6 @@ # 26.05.24 -from unidecode import unidecode +from urllib.parse import quote_plus # Internal utilities @@ -30,7 +30,7 @@ def search(string_to_search: str = None, get_onylDatabase: bool = False): string_to_search = msg.ask(f"\n[purple]Insert word to search in [red]{SITE_NAME}").strip() # Search on database - len_database = title_search(unidecode(string_to_search)) + len_database = title_search(quote_plus(string_to_search)) # Return list of elements if get_onylDatabase: @@ -48,4 +48,4 @@ def search(string_to_search: str = None, get_onylDatabase: bool = False): console.print(f"\n[red]Nothing matching was found for[white]: [purple]{string_to_search}") # Retry - search() + search() \ No newline at end of file diff --git a/StreamingCommunity/Api/Site/altadefinizione/film.py b/StreamingCommunity/Api/Site/altadefinizione/film.py index 0cbf1aa..1e4b949 100644 --- a/StreamingCommunity/Api/Site/altadefinizione/film.py +++ b/StreamingCommunity/Api/Site/altadefinizione/film.py @@ -25,13 +25,16 @@ from StreamingCommunity.Api.Player.supervideo import VideoSource from .costant import ROOT_PATH, SITE_NAME, MOVIE_FOLDER -def download_film(select_title: MediaItem): +def download_film(select_title: MediaItem) -> str: """ Downloads a film using the provided film ID, title name, and domain. Parameters: - title_name (str): The name of the film title. - url (str): The url of the video + + Return: + - str: output path """ # Start message and display film information @@ -56,14 +59,16 @@ def download_film(select_title: MediaItem): output_filename=os.path.join(mp4_path, title_name) ).start() - if r_proc == 404: + """if r_proc == 404: time.sleep(2) # Re call search function 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]) + execute_search(frames[-4])""" if r_proc != None: console.print("[green]Result: ") console.print(r_proc) + + return os.path.join(mp4_path, title_name) \ No newline at end of file diff --git a/StreamingCommunity/Api/Site/altadefinizione/site.py b/StreamingCommunity/Api/Site/altadefinizione/site.py index 5812f1a..b9042e3 100644 --- a/StreamingCommunity/Api/Site/altadefinizione/site.py +++ b/StreamingCommunity/Api/Site/altadefinizione/site.py @@ -34,13 +34,16 @@ def title_search(title_search: str) -> int: Returns: int: The number of titles found. """ - client = httpx.Client() + media_search_manager.clear() + table_show_manager.clear() # Find new domain if prev dont work max_timeout = config_manager.get_int("REQUESTS", "timeout") domain_to_use, _ = search_domain(SITE_NAME, f"https://{SITE_NAME}") # Send request to search for title + client = httpx.Client() + try: response = client.get( url=f"https://{SITE_NAME}.{domain_to_use}/?story={title_search.replace(' ', '+')}&do=search&subaction=search&titleonly=3", @@ -83,4 +86,4 @@ def run_get_select_title(): """ Display a selection of titles and prompt the user to choose one. """ - return get_select_title(table_show_manager, media_search_manager) + return get_select_title(table_show_manager, media_search_manager) \ No newline at end of file diff --git a/StreamingCommunity/Api/Site/animeunity/__init__.py b/StreamingCommunity/Api/Site/animeunity/__init__.py index 638e203..57a100c 100644 --- a/StreamingCommunity/Api/Site/animeunity/__init__.py +++ b/StreamingCommunity/Api/Site/animeunity/__init__.py @@ -1,6 +1,6 @@ # 21.05.24 -from unidecode import unidecode +from urllib.parse import quote_plus # Internal utilities @@ -27,7 +27,7 @@ def search(string_to_search: str = None, get_onylDatabase: bool = False): string_to_search = msg.ask(f"\n[purple]Insert word to search in [red]{SITE_NAME}").strip() # Search on database - len_database = title_search(unidecode(string_to_search)) + len_database = title_search(quote_plus(string_to_search)) # Return list of elements if get_onylDatabase: @@ -48,4 +48,4 @@ def search(string_to_search: str = None, get_onylDatabase: bool = False): console.print(f"\n[red]Nothing matching was found for[white]: [purple]{string_to_search}") # Retry - search() + search() \ No newline at end of file diff --git a/StreamingCommunity/Api/Site/animeunity/film_serie.py b/StreamingCommunity/Api/Site/animeunity/film_serie.py index 64230db..73d1cfe 100644 --- a/StreamingCommunity/Api/Site/animeunity/film_serie.py +++ b/StreamingCommunity/Api/Site/animeunity/film_serie.py @@ -27,12 +27,15 @@ from .costant import ROOT_PATH, SITE_NAME, SERIES_FOLDER, MOVIE_FOLDER -def download_episode(index_select: int, scrape_serie: ScrapeSerieAnime, video_source: VideoSourceAnime): +def download_episode(index_select: int, scrape_serie: ScrapeSerieAnime, video_source: VideoSourceAnime) -> str: """ Downloads the selected episode. Parameters: - index_select (int): Index of the episode to download. + + Return: + - str: output path """ # Get information about the selected episode @@ -63,14 +66,16 @@ def download_episode(index_select: int, scrape_serie: ScrapeSerieAnime, video_so # Start downloading r_proc = MP4_downloader( - url = str(video_source.src_mp4).strip(), - path = os.path.join(mp4_path, title_name) + url=str(video_source.src_mp4).strip(), + path=os.path.join(mp4_path, title_name) ) if r_proc != None: console.print("[green]Result: ") console.print(r_proc) + return os.path.join(mp4_path, title_name) + else: logging.error(f"Skip index: {index_select} cant find info with api.") diff --git a/StreamingCommunity/Api/Site/animeunity/site.py b/StreamingCommunity/Api/Site/animeunity/site.py index 39b595b..514e455 100644 --- a/StreamingCommunity/Api/Site/animeunity/site.py +++ b/StreamingCommunity/Api/Site/animeunity/site.py @@ -99,6 +99,8 @@ def title_search(title: str) -> int: Returns: - int: A number containing the length of media search manager. """ + media_search_manager.clear() + table_show_manager.clear() # Get token and session value from configuration max_timeout = config_manager.get_int("REQUESTS", "timeout") diff --git a/StreamingCommunity/Api/Site/bitsearch/__init__.py b/StreamingCommunity/Api/Site/bitsearch/__init__.py deleted file mode 100644 index 2e43cd8..0000000 --- a/StreamingCommunity/Api/Site/bitsearch/__init__.py +++ /dev/null @@ -1,52 +0,0 @@ -# 01.07.24 - -from unidecode import unidecode - - -# Internal utilities -from StreamingCommunity.Util.console import console, msg - - -# Logic class -from .site import title_search, run_get_select_title, media_search_manager -from .title import download_title - - -# Variable -indice = 7 -_useFor = "film_serie" -_deprecate = False -_priority = 2 -_engineDownload = "tor" -from .costant import SITE_NAME - - -def search(string_to_search: str = None, get_onylDatabase:bool = False): - """ - Main function of the application for film and series. - """ - - if string_to_search is None: - string_to_search = msg.ask(f"\n[purple]Insert word to search in [red]{SITE_NAME}").strip() - - # Search on database - len_database = title_search(unidecode(string_to_search)) - - # Return list of elements - if get_onylDatabase: - return media_search_manager - - if len_database > 0: - - # Select title from list - select_title = run_get_select_title() - - # Download title - download_title(select_title) - - - else: - console.print(f"\n[red]Nothing matching was found for[white]: [purple]{string_to_search}") - - # Retry - search() diff --git a/StreamingCommunity/Api/Site/bitsearch/costant.py b/StreamingCommunity/Api/Site/bitsearch/costant.py deleted file mode 100644 index 1e55dba..0000000 --- a/StreamingCommunity/Api/Site/bitsearch/costant.py +++ /dev/null @@ -1,15 +0,0 @@ -# 01.07.24 - -import os - - -# Internal utilities -from StreamingCommunity.Util._jsonConfig import config_manager - - -SITE_NAME = os.path.basename(os.path.dirname(os.path.abspath(__file__))) -ROOT_PATH = config_manager.get('DEFAULT', 'root_path') -DOMAIN_NOW = config_manager.get_dict('SITE', SITE_NAME)['domain'] - -SERIES_FOLDER = config_manager.get('DEFAULT', 'serie_folder_name') -MOVIE_FOLDER = config_manager.get('DEFAULT', 'movie_folder_name') diff --git a/StreamingCommunity/Api/Site/bitsearch/site.py b/StreamingCommunity/Api/Site/bitsearch/site.py deleted file mode 100644 index 797058f..0000000 --- a/StreamingCommunity/Api/Site/bitsearch/site.py +++ /dev/null @@ -1,84 +0,0 @@ -# 01.07.24 - -# External libraries -import httpx -from bs4 import BeautifulSoup - - -# Internal utilities -from StreamingCommunity.Util.console import console -from StreamingCommunity.Util._jsonConfig import config_manager -from StreamingCommunity.Util.headers import get_headers -from StreamingCommunity.Util.table import TVShowManager - - -# Logic class -from StreamingCommunity.Api.Template import get_select_title -from StreamingCommunity.Api.Template.Util import search_domain -from StreamingCommunity.Api.Template.Class.SearchType import MediaManager - - -# Variable -from .costant import SITE_NAME -media_search_manager = MediaManager() -table_show_manager = TVShowManager() - - -def title_search(word_to_search: str) -> int: - """ - Search for titles based on a search query. - - Parameters: - - title_search (str): The title to search for. - - Returns: - - int: The number of titles found. - """ - - # Find new domain if prev dont work - max_timeout = config_manager.get_int("REQUESTS", "timeout") - domain_to_use, _ = search_domain(SITE_NAME, f"https://{SITE_NAME}") - - # Construct the full site URL and load the search page - try: - response = httpx.get( - url=f"https://{SITE_NAME}.{domain_to_use}/search?q={word_to_search}&category=1&subcat=2&page=1", - headers={'user-agent': get_headers()}, - timeout=max_timeout - ) - response.raise_for_status() - - except Exception as e: - console.print(f"Site: {SITE_NAME}, request search error: {e}") - - # Create soup and find table - soup = BeautifulSoup(response.text, "html.parser") - - for title_div in soup.find_all("li", class_ = "card"): - try: - div_stats = title_div.find("div", class_ = "stats") - - title_info = { - 'name': title_div.find("a").get_text(strip=True), - 'url': title_div.find_all("a")[-1].get("href"), - #'nDownload': div_stats.find_all("div")[0].get_text(strip=True), - 'size': div_stats.find_all("div")[1].get_text(strip=True), - 'seader': div_stats.find_all("div")[2].get_text(strip=True), - 'leacher': div_stats.find_all("div")[3].get_text(strip=True), - 'date': div_stats.find_all("div")[4].get_text(strip=True) - } - - media_search_manager.add_media(title_info) - - except: - pass - - # Return the number of titles found - return media_search_manager.get_length() - - -def run_get_select_title(): - """ - Display a selection of titles and prompt the user to choose one. - """ - return get_select_title(table_show_manager, media_search_manager) diff --git a/StreamingCommunity/Api/Site/cb01new/__init__.py b/StreamingCommunity/Api/Site/cb01new/__init__.py index ee01ee0..c60c9a4 100644 --- a/StreamingCommunity/Api/Site/cb01new/__init__.py +++ b/StreamingCommunity/Api/Site/cb01new/__init__.py @@ -1,6 +1,6 @@ # 09.06.24 -from unidecode import unidecode +from urllib.parse import quote_plus # Internal utilities @@ -30,7 +30,7 @@ def search(string_to_search: str = None, get_onylDatabase: bool = False): string_to_search = msg.ask(f"\n[purple]Insert word to search in [red]{SITE_NAME}").strip() # Search on database - len_database = title_search(unidecode(string_to_search)) + len_database = title_search(quote_plus(string_to_search)) # Return list of elements if get_onylDatabase: @@ -49,4 +49,4 @@ def search(string_to_search: str = None, get_onylDatabase: bool = False): console.print(f"\n[red]Nothing matching was found for[white]: [purple]{string_to_search}") # Retry - search() + search() \ No newline at end of file diff --git a/StreamingCommunity/Api/Site/cb01new/film.py b/StreamingCommunity/Api/Site/cb01new/film.py index 08e3845..23b1d37 100644 --- a/StreamingCommunity/Api/Site/cb01new/film.py +++ b/StreamingCommunity/Api/Site/cb01new/film.py @@ -1,11 +1,10 @@ # 03.07.24 import os -import time # Internal utilities -from StreamingCommunity.Util.console import console, msg +from StreamingCommunity.Util.console import console from StreamingCommunity.Util.os import os_manager from StreamingCommunity.Util.message import start_message from StreamingCommunity.Util.call_stack import get_call_stack @@ -25,12 +24,15 @@ from StreamingCommunity.Api.Player.maxstream import VideoSource from .costant import ROOT_PATH, SITE_NAME, MOVIE_FOLDER -def download_film(select_title: MediaItem): +def download_film(select_title: MediaItem) -> str: """ Downloads a film using the provided obj. Parameters: - select_title (MediaItem): The media item to be downloaded. This should be an instance of the MediaItem class, containing attributes like `name` and `url`. + + Return: + - str: output path """ # Start message and display film information @@ -56,14 +58,16 @@ def download_film(select_title: MediaItem): output_filename=os.path.join(mp4_path, title_name) ).start() - if r_proc == 404: + """if r_proc == 404: time.sleep(2) # Re call search function 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]) + execute_search(frames[-4])""" if r_proc != None: console.print("[green]Result: ") console.print(r_proc) + + return os.path.join(mp4_path, title_name) \ No newline at end of file diff --git a/StreamingCommunity/Api/Site/cb01new/site.py b/StreamingCommunity/Api/Site/cb01new/site.py index 68842c2..ddaa40a 100644 --- a/StreamingCommunity/Api/Site/cb01new/site.py +++ b/StreamingCommunity/Api/Site/cb01new/site.py @@ -33,6 +33,8 @@ def title_search(word_to_search: str) -> int: Returns: - int: The number of titles found. """ + media_search_manager.clear() + table_show_manager.clear() # Find new domain if prev dont work max_timeout = config_manager.get_int("REQUESTS", "timeout") @@ -71,4 +73,4 @@ def run_get_select_title(): """ Display a selection of titles and prompt the user to choose one. """ - return get_select_title(table_show_manager, media_search_manager) + return get_select_title(table_show_manager, media_search_manager) \ No newline at end of file diff --git a/StreamingCommunity/Api/Site/ddlstreamitaly/__init__.py b/StreamingCommunity/Api/Site/ddlstreamitaly/__init__.py index 2b6e417..553ee1d 100644 --- a/StreamingCommunity/Api/Site/ddlstreamitaly/__init__.py +++ b/StreamingCommunity/Api/Site/ddlstreamitaly/__init__.py @@ -1,7 +1,7 @@ # 09.06.24 import logging -from unidecode import unidecode +from urllib.parse import quote_plus # Internal utilities @@ -33,7 +33,7 @@ def search(string_to_search: str = None, get_onylDatabase: bool = False): string_to_search = msg.ask(f"\n[purple]Insert word to search in [red]{SITE_NAME}").strip() # Search on database - len_database = title_search(unidecode(string_to_search)) + len_database = title_search(quote_plus(string_to_search)) # Return list of elements if get_onylDatabase: @@ -55,4 +55,4 @@ def search(string_to_search: str = None, get_onylDatabase: bool = False): console.print(f"\n[red]Nothing matching was found for[white]: [purple]{string_to_search}") # Retry - search() + search() \ No newline at end of file diff --git a/StreamingCommunity/Api/Site/ddlstreamitaly/series.py b/StreamingCommunity/Api/Site/ddlstreamitaly/series.py index cb74083..d30cb91 100644 --- a/StreamingCommunity/Api/Site/ddlstreamitaly/series.py +++ b/StreamingCommunity/Api/Site/ddlstreamitaly/series.py @@ -29,13 +29,16 @@ table_show_manager = TVShowManager() -def download_video(index_episode_selected: int, scape_info_serie: GetSerieInfo, video_source: VideoSource) -> None: +def download_video(index_episode_selected: int, scape_info_serie: GetSerieInfo, video_source: VideoSource) -> str: """ Download a single episode video. Parameters: - tv_name (str): Name of the TV series. - index_episode_selected (int): Index of the selected episode. + + Return: + - str: output path """ start_message() @@ -65,15 +68,17 @@ def download_video(index_episode_selected: int, scape_info_serie: GetSerieInfo, # Start download r_proc = MP4_downloader( - url = master_playlist, - path = os.path.join(mp4_path, title_name), - referer = f"{parsed_url.scheme}://{parsed_url.netloc}/", + url=master_playlist, + path=os.path.join(mp4_path, title_name), + referer=f"{parsed_url.scheme}://{parsed_url.netloc}/", ) if r_proc != None: console.print("[green]Result: ") console.print(r_proc) + return os.path.join(mp4_path, title_name) + def download_thread(dict_serie: MediaItem): """ diff --git a/StreamingCommunity/Api/Site/ddlstreamitaly/site.py b/StreamingCommunity/Api/Site/ddlstreamitaly/site.py index 1fb38f5..0caaffc 100644 --- a/StreamingCommunity/Api/Site/ddlstreamitaly/site.py +++ b/StreamingCommunity/Api/Site/ddlstreamitaly/site.py @@ -37,6 +37,8 @@ def title_search(word_to_search: str) -> int: Returns: - int: The number of titles found. """ + media_search_manager.clear() + table_show_manager.clear() # Find new domain if prev dont work max_timeout = config_manager.get_int("REQUESTS", "timeout") diff --git a/StreamingCommunity/Api/Site/guardaserie/__init__.py b/StreamingCommunity/Api/Site/guardaserie/__init__.py index 7c404d4..acb4687 100644 --- a/StreamingCommunity/Api/Site/guardaserie/__init__.py +++ b/StreamingCommunity/Api/Site/guardaserie/__init__.py @@ -1,6 +1,6 @@ # 09.06.24 -from unidecode import unidecode +from urllib.parse import quote_plus # Internal utilities @@ -32,7 +32,7 @@ def search(string_to_search: str = None, get_onylDatabase: bool = False): string_to_search = msg.ask(f"\n[purple]Insert word to search in [red]{SITE_NAME}").strip() # Search on database - len_database = title_search(unidecode(string_to_search)) + len_database = title_search(quote_plus(string_to_search)) # Return list of elements if get_onylDatabase: @@ -50,4 +50,4 @@ def search(string_to_search: str = None, get_onylDatabase: bool = False): console.print(f"\n[red]Nothing matching was found for[white]: [purple]{string_to_search}") # Retry - search() + search() \ No newline at end of file diff --git a/StreamingCommunity/Api/Site/guardaserie/series.py b/StreamingCommunity/Api/Site/guardaserie/series.py index b57bae0..6f3d6e9 100644 --- a/StreamingCommunity/Api/Site/guardaserie/series.py +++ b/StreamingCommunity/Api/Site/guardaserie/series.py @@ -29,7 +29,7 @@ table_show_manager = TVShowManager() -def download_video(index_season_selected: int, index_episode_selected: int, scape_info_serie: GetSerieInfo) -> None: +def download_video(index_season_selected: int, index_episode_selected: int, scape_info_serie: GetSerieInfo) -> str: """ Download a single episode video. @@ -37,6 +37,9 @@ def download_video(index_season_selected: int, index_episode_selected: int, scap - tv_name (str): Name of the TV series. - index_season_selected (int): Index of the selected season. - index_episode_selected (int): Index of the selected episode. + + Return: + - str: output path """ start_message() @@ -62,18 +65,19 @@ def download_video(index_season_selected: int, index_episode_selected: int, scap output_filename=os.path.join(mp4_path, mp4_name) ).start() - if r_proc == 404: + """if r_proc == 404: time.sleep(2) # Re call search function 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]) + execute_search(frames[-4])""" if r_proc != None: console.print("[green]Result: ") console.print(r_proc) + return os.path.join(mp4_path, mp4_name) def download_episode(scape_info_serie: GetSerieInfo, index_season_selected: int, download_all: bool = False) -> None: diff --git a/StreamingCommunity/Api/Site/guardaserie/site.py b/StreamingCommunity/Api/Site/guardaserie/site.py index ee5da04..982b4aa 100644 --- a/StreamingCommunity/Api/Site/guardaserie/site.py +++ b/StreamingCommunity/Api/Site/guardaserie/site.py @@ -34,6 +34,8 @@ def title_search(word_to_search: str) -> int: Returns: - int: The number of titles found. """ + media_search_manager.clear() + table_show_manager.clear() # Find new domain if prev dont work max_timeout = config_manager.get_int("REQUESTS", "timeout") @@ -81,4 +83,4 @@ def run_get_select_title(): """ Display a selection of titles and prompt the user to choose one. """ - return get_select_title(table_show_manager, media_search_manager) + return get_select_title(table_show_manager, media_search_manager) \ No newline at end of file diff --git a/StreamingCommunity/Api/Site/piratebays/__init__.py b/StreamingCommunity/Api/Site/ilcorsaronero/__init__.py similarity index 87% rename from StreamingCommunity/Api/Site/piratebays/__init__.py rename to StreamingCommunity/Api/Site/ilcorsaronero/__init__.py index 6e3dcd2..e29a859 100644 --- a/StreamingCommunity/Api/Site/piratebays/__init__.py +++ b/StreamingCommunity/Api/Site/ilcorsaronero/__init__.py @@ -1,6 +1,7 @@ # 02.07.24 -from unidecode import unidecode +import asyncio +from urllib.parse import quote_plus # Internal utilities @@ -13,7 +14,7 @@ from .title import download_title # Variable -indice = 8 +indice = 9 _useFor = "film_serie" _deprecate = False _priority = 2 @@ -30,7 +31,7 @@ def search(string_to_search: str = None, get_onylDatabase: bool = False): string_to_search = msg.ask(f"\n[purple]Insert word to search in [red]{SITE_NAME}").strip() # Search on database - len_database = title_search(unidecode(string_to_search)) + len_database = asyncio.run(title_search(quote_plus(string_to_search))) # Return list of elements if get_onylDatabase: @@ -48,4 +49,4 @@ def search(string_to_search: str = None, get_onylDatabase: bool = False): console.print(f"\n[red]Nothing matching was found for[white]: [purple]{string_to_search}") # Retry - search() + search() \ No newline at end of file diff --git a/StreamingCommunity/Api/Site/piratebays/costant.py b/StreamingCommunity/Api/Site/ilcorsaronero/costant.py similarity index 100% rename from StreamingCommunity/Api/Site/piratebays/costant.py rename to StreamingCommunity/Api/Site/ilcorsaronero/costant.py diff --git a/StreamingCommunity/Api/Site/ilcorsaronero/site.py b/StreamingCommunity/Api/Site/ilcorsaronero/site.py new file mode 100644 index 0000000..210ef91 --- /dev/null +++ b/StreamingCommunity/Api/Site/ilcorsaronero/site.py @@ -0,0 +1,63 @@ +# 02.07.24 + + +# Internal utilities +from StreamingCommunity.Util.table import TVShowManager + + +# Logic class +from StreamingCommunity.Api.Template import get_select_title +from StreamingCommunity.Api.Template.Util import search_domain +from StreamingCommunity.Api.Template.Class.SearchType import MediaManager +from .util.ilCorsarScraper import IlCorsaroNeroScraper + + +# Variable +from .costant import SITE_NAME +media_search_manager = MediaManager() +table_show_manager = TVShowManager() + + +async def title_search(word_to_search: str) -> int: + """ + Search for titles based on a search query. + + Parameters: + - title_search (str): The title to search for. + + Returns: + - int: The number of titles found. + """ + media_search_manager.clear() + table_show_manager.clear() + + # Find new domain if prev dont work + domain_to_use, _ = search_domain(SITE_NAME, f"https://{SITE_NAME}") + + # Create scraper and collect result + print("\n") + scraper = IlCorsaroNeroScraper(f"https://{SITE_NAME}.{domain_to_use}/", 1) + results = await scraper.search(word_to_search) + + # Add all result to media manager + for i, torrent in enumerate(results): + media_search_manager.add_media({ + 'name': torrent['name'], + 'type': torrent['type'], + 'seed': torrent['seed'], + 'leech': torrent['leech'], + 'size': torrent['size'], + 'date': torrent['date'], + 'url': torrent['url'] + }) + + + # Return the number of titles found + return media_search_manager.get_length() + + +def run_get_select_title(): + """ + Display a selection of titles and prompt the user to choose one. + """ + return get_select_title(table_show_manager, media_search_manager) \ No newline at end of file diff --git a/StreamingCommunity/Api/Site/bitsearch/title.py b/StreamingCommunity/Api/Site/ilcorsaronero/title.py similarity index 86% rename from StreamingCommunity/Api/Site/bitsearch/title.py rename to StreamingCommunity/Api/Site/ilcorsaronero/title.py index af5556d..9eea1ab 100644 --- a/StreamingCommunity/Api/Site/bitsearch/title.py +++ b/StreamingCommunity/Api/Site/ilcorsaronero/title.py @@ -1,12 +1,12 @@ -# 01.07.24 +# 02.07.24 import os # Internal utilities from StreamingCommunity.Util.console import console -from StreamingCommunity.Util.message import start_message from StreamingCommunity.Util.os import os_manager +from StreamingCommunity.Util.message import start_message from StreamingCommunity.Lib.Downloader import TOR_downloader @@ -27,18 +27,17 @@ def download_title(select_title: MediaItem): """ start_message() - console.print(f"[yellow]Download: [red]{select_title.name} \n") - print() + print() # Define output path - title_name = os_manager.get_sanitize_file(select_title.name.replace("-", "_") + ".mp4") + title_name = os_manager.get_sanitize_file(select_title.name) mp4_path = os_manager.get_sanitize_path( os.path.join(ROOT_PATH, SITE_NAME, MOVIE_FOLDER, title_name.replace(".mp4", "")) ) # Create output folder - os_manager.create_path(mp4_path) + os_manager.create_path(mp4_path) # Tor manager manager = TOR_downloader() diff --git a/StreamingCommunity/Api/Site/ilcorsaronero/util/ilCorsarScraper.py b/StreamingCommunity/Api/Site/ilcorsaronero/util/ilCorsarScraper.py new file mode 100644 index 0000000..362f10a --- /dev/null +++ b/StreamingCommunity/Api/Site/ilcorsaronero/util/ilCorsarScraper.py @@ -0,0 +1,139 @@ +# 12.14.24 +import logging +import asyncio +from typing import List, Dict, Optional + + +# External libraries +import httpx +from bs4 import BeautifulSoup + + +# Internal utilities +from StreamingCommunity.Util._jsonConfig import config_manager +from StreamingCommunity.Util.console import console + + +# Variable +max_timeout = config_manager.get_int("REQUESTS", "timeout") + + +class IlCorsaroNeroScraper: + def __init__(self, base_url: str, max_page: int = 1): + self.base_url = base_url + self.max_page = max_page + self.headers = { + 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36', + 'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8', + 'Accept-Language': 'en-US,en;q=0.5', + 'Connection': 'keep-alive', + 'Upgrade-Insecure-Requests': '1' + } + + async def fetch_url(self, url: str) -> Optional[str]: + """ + Fetch the HTML content of a given URL. + """ + try: + console.print(f"[cyan]Fetching url[white]: [red]{url}") + async with httpx.AsyncClient(headers=self.headers, follow_redirects=True, timeout=max_timeout) as client: + response = await client.get(url) + + # If the request was successful, return the HTML content + response.raise_for_status() + return response.text + + except Exception as e: + logging.error(f"Error fetching {url}: {e}") + return None + + def parse_torrents(self, html: str) -> List[Dict[str, str]]: + """ + Parse the HTML content and extract torrent details. + """ + torrents = [] + soup = BeautifulSoup(html, "html.parser") + table = soup.find("tbody") + + for row in table.find_all("tr"): + try: + columns = row.find_all("td") + + torrents.append({ + 'type': columns[0].get_text(strip=True), + 'name': row.find("th").find("a").get_text(strip=True), + 'seed': columns[1].get_text(strip=True), + 'leech': columns[2].get_text(strip=True), + 'size': columns[3].get_text(strip=True), + 'date': columns[4].get_text(strip=True), + 'url': "https://ilcorsaronero.link" + row.find("th").find("a").get("href") + }) + + except Exception as e: + logging.error(f"Error parsing row: {e}") + continue + + return torrents + + async def fetch_real_url(self, url: str) -> Optional[str]: + """ + Fetch the real torrent URL from the detailed page. + """ + response_html = await self.fetch_url(url) + if not response_html: + return None + + soup = BeautifulSoup(response_html, "html.parser") + links = soup.find_all("a") + + # Find and return the magnet link + for link in links: + if "magnet" in str(link): + return link.get("href") + + return None + + async def search(self, query: str) -> List[Dict[str, str]]: + """ + Search for torrents based on the query string. + """ + all_torrents = [] + + # Loop through each page + for page in range(self.max_page): + url = f'{self.base_url}search?q={query}&page={page}' + + html = await self.fetch_url(url) + if not html: + console.print(f"[bold red]No HTML content for page {page}[/bold red]") + break + + torrents = self.parse_torrents(html) + if not torrents: + console.print(f"[bold red]No torrents found on page {page}[/bold red]") + break + + # Use asyncio.gather to fetch all real URLs concurrently + tasks = [self.fetch_real_url(result['url']) for result in torrents] + real_urls = await asyncio.gather(*tasks) + + # Attach real URLs to the torrent data + for i, result in enumerate(torrents): + result['url'] = real_urls[i] + + all_torrents.extend(torrents) + + return all_torrents + +async def main(): + scraper = IlCorsaroNeroScraper("https://ilcorsaronero.link/") + results = await scraper.search("cars") + + if results: + for i, torrent in enumerate(results): + console.print(f"[bold green]{i} = {torrent}[/bold green] \n") + else: + console.print("[bold red]No torrents found.[/bold red]") + +if __name__ == '__main__': + asyncio.run(main()) diff --git a/StreamingCommunity/Api/Site/mostraguarda/__init__.py b/StreamingCommunity/Api/Site/mostraguarda/__init__.py index 68e489a..31cd4fe 100644 --- a/StreamingCommunity/Api/Site/mostraguarda/__init__.py +++ b/StreamingCommunity/Api/Site/mostraguarda/__init__.py @@ -1,6 +1,6 @@ # 26.05.24 -from unidecode import unidecode +from urllib.parse import quote_plus # Internal utilities @@ -34,7 +34,7 @@ def search(string_to_search: str = None, get_onylDatabase: bool = False): return 0 # Search on database - movie_id = tmdb.search_movie(unidecode(string_to_search)) + movie_id = tmdb.search_movie(quote_plus(string_to_search)) if movie_id is not None: movie_details: Json_film = tmdb.get_movie_details(tmdb_id=movie_id) @@ -46,4 +46,4 @@ def search(string_to_search: str = None, get_onylDatabase: bool = False): console.print(f"\n[red]Nothing matching was found for[white]: [purple]{string_to_search}") # Retry - search() + search() \ No newline at end of file diff --git a/StreamingCommunity/Api/Site/mostraguarda/film.py b/StreamingCommunity/Api/Site/mostraguarda/film.py index 1615292..fc4d60e 100644 --- a/StreamingCommunity/Api/Site/mostraguarda/film.py +++ b/StreamingCommunity/Api/Site/mostraguarda/film.py @@ -36,12 +36,15 @@ from StreamingCommunity.Lib.TMBD import Json_film from .costant import ROOT_PATH, SITE_NAME, DOMAIN_NOW, MOVIE_FOLDER -def download_film(movie_details: Json_film): +def download_film(movie_details: Json_film) -> str: """ Downloads a film using the provided tmbd id. Parameters: - movie_details (Json_film): Class with info about film title. + + Return: + - str: output path """ # Start message and display film information @@ -81,14 +84,16 @@ def download_film(movie_details: Json_film): output_filename=os.path.join(mp4_path, title_name) ).start() - if r_proc == 404: + """if r_proc == 404: time.sleep(2) # Re call search function 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]) + execute_search(frames[-4])""" if r_proc != None: console.print("[green]Result: ") console.print(r_proc) + + return os.path.join(mp4_path, title_name) \ No newline at end of file diff --git a/StreamingCommunity/Api/Site/piratebays/site.py b/StreamingCommunity/Api/Site/piratebays/site.py deleted file mode 100644 index c1779c7..0000000 --- a/StreamingCommunity/Api/Site/piratebays/site.py +++ /dev/null @@ -1,89 +0,0 @@ -# 02.07.24 - -# External libraries -import httpx -from bs4 import BeautifulSoup - - -# Internal utilities -from StreamingCommunity.Util.console import console -from StreamingCommunity.Util._jsonConfig import config_manager -from StreamingCommunity.Util.headers import get_headers -from StreamingCommunity.Util.table import TVShowManager - - -# Logic class -from StreamingCommunity.Api.Template import get_select_title -from StreamingCommunity.Api.Template.Class.SearchType import MediaManager - - -# Variable -from .costant import SITE_NAME, DOMAIN_NOW -media_search_manager = MediaManager() -table_show_manager = TVShowManager() - - -def title_search(word_to_search: str) -> int: - """ - Search for titles based on a search query. - - Parameters: - - title_search (str): The title to search for. - - Returns: - - int: The number of titles found. - """ - - # Find new domain if prev dont work - max_timeout = config_manager.get_int("REQUESTS", "timeout") - - # Construct the full site URL and load the search page - try: - response = httpx.get( - url=f"https://1.{SITE_NAME}.{DOMAIN_NOW}/s/?q={word_to_search}&video=on", - headers={ - 'accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7', - 'accept-language': 'it-IT,it;q=0.9,en-US;q=0.8,en;q=0.7', - 'referer': 'https://wwv.thepiratebay3.co/', - 'user-agent': get_headers() - }, - follow_redirects=True, - timeout=max_timeout - ) - response.raise_for_status() - - except Exception as e: - console.print(f"Site: {SITE_NAME}, request search error: {e}") - - # Create soup and find table - soup = BeautifulSoup(response.text, "html.parser") - table = soup.find("tbody") - - # Scrape div film in table on single page - for tr in table.find_all('tr'): - try: - - title_info = { - 'name': tr.find_all("a")[1].get_text(strip=True), - 'url': tr.find_all("td")[3].find("a").get("href"), - 'upload': tr.find_all("td")[2].get_text(strip=True), - 'size': tr.find_all("td")[4].get_text(strip=True), - 'seader': tr.find_all("td")[5].get_text(strip=True), - 'leacher': tr.find_all("td")[6].get_text(strip=True), - 'by': tr.find_all("td")[7].get_text(strip=True), - } - - media_search_manager.add_media(title_info) - - except: - continue - - # Return the number of titles found - return media_search_manager.get_length() - - -def run_get_select_title(): - """ - Display a selection of titles and prompt the user to choose one. - """ - return get_select_title(table_show_manager, media_search_manager) \ No newline at end of file diff --git a/StreamingCommunity/Api/Site/piratebays/title.py b/StreamingCommunity/Api/Site/piratebays/title.py deleted file mode 100644 index 94dd377..0000000 --- a/StreamingCommunity/Api/Site/piratebays/title.py +++ /dev/null @@ -1,45 +0,0 @@ -# 02.07.24 - -import os -import sys - - -# Internal utilities -from StreamingCommunity.Util.console import console -from StreamingCommunity.Util.message import start_message -from StreamingCommunity.Util.os import os_manager -from StreamingCommunity.Lib.Downloader import TOR_downloader - - -# Logic class -from StreamingCommunity.Api.Template.Class.SearchType import MediaItem - - -# Config -from .costant import ROOT_PATH, DOMAIN_NOW, SITE_NAME, MOVIE_FOLDER - - -def download_title(select_title: MediaItem): - """ - Downloads a media item and saves it as an MP4 file. - - Parameters: - - select_title (MediaItem): The media item to be downloaded. This should be an instance of the MediaItem class, containing attributes like `name` and `url`. - """ - - start_message() - console.print(f"[yellow]Download: [red]{select_title.name} \n") - print() - - # Define output path - title_name = os_manager.get_sanitize_file(select_title.name.replace("-", "_") + ".mp4") - mp4_path = os.path.join(ROOT_PATH, SITE_NAME, MOVIE_FOLDER, title_name.replace(".mp4", "")) - - # Create output folder - os_manager.create_path(mp4_path) - - # Tor manager - manager = TOR_downloader() - manager.add_magnet_link(select_title.url) - manager.start_download() - manager.move_downloaded_files(mp4_path) diff --git a/StreamingCommunity/Api/Site/streamingcommunity/__init__.py b/StreamingCommunity/Api/Site/streamingcommunity/__init__.py index badc78e..f745340 100644 --- a/StreamingCommunity/Api/Site/streamingcommunity/__init__.py +++ b/StreamingCommunity/Api/Site/streamingcommunity/__init__.py @@ -1,6 +1,6 @@ # 21.05.24 -from unidecode import unidecode +from urllib.parse import quote_plus # Internal utilities @@ -32,12 +32,12 @@ def search(string_to_search: str = None, get_onylDatabase: bool = False): # Get site domain and version and get result of the search site_version, domain = get_version_and_domain() - len_database = title_search(unidecode(string_to_search), domain) + len_database = title_search(quote_plus(string_to_search), domain) # Return list of elements if get_onylDatabase: return media_search_manager - + if len_database > 0: # Select title from list @@ -53,4 +53,4 @@ def search(string_to_search: str = None, get_onylDatabase: bool = False): console.print(f"\n[red]Nothing matching was found for[white]: [purple]{string_to_search}") # Retry - search() + search() \ No newline at end of file diff --git a/StreamingCommunity/Api/Site/streamingcommunity/film.py b/StreamingCommunity/Api/Site/streamingcommunity/film.py index a8db695..8dec240 100644 --- a/StreamingCommunity/Api/Site/streamingcommunity/film.py +++ b/StreamingCommunity/Api/Site/streamingcommunity/film.py @@ -25,13 +25,16 @@ from StreamingCommunity.Api.Player.vixcloud import VideoSource from .costant import ROOT_PATH, SITE_NAME, MOVIE_FOLDER -def download_film(select_title: MediaItem): +def download_film(select_title: MediaItem) -> str: """ Downloads a film using the provided film ID, title name, and domain. Parameters: - domain (str): The domain of the site - version (str): Version of site. + + Return: + - str: output path """ # Start message and display film information @@ -57,14 +60,16 @@ def download_film(select_title: MediaItem): output_filename=os.path.join(mp4_path, title_name) ).start() - if r_proc == 404: + """if r_proc == 404: time.sleep(2) # Re call search function 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]) + execute_search(frames[-4])""" if r_proc != None: console.print("[green]Result: ") console.print(r_proc) + + return os.path.join(mp4_path, title_name) \ No newline at end of file diff --git a/StreamingCommunity/Api/Site/streamingcommunity/series.py b/StreamingCommunity/Api/Site/streamingcommunity/series.py index f32c837..155bca4 100644 --- a/StreamingCommunity/Api/Site/streamingcommunity/series.py +++ b/StreamingCommunity/Api/Site/streamingcommunity/series.py @@ -29,16 +29,17 @@ table_show_manager = TVShowManager() -def download_video(tv_name: str, index_season_selected: int, index_episode_selected: int, scrape_serie: ScrapeSerie, video_source: VideoSource) -> None: +def download_video(index_season_selected: int, index_episode_selected: int, scrape_serie: ScrapeSerie, video_source: VideoSource) -> str: """ Download a single episode video. Parameters: - - tv_name (str): Name of the TV series. - index_season_selected (int): Index of the selected season. - index_episode_selected (int): Index of the selected episode. - """ + Return: + - str: output path + """ start_message() # Get info about episode @@ -47,8 +48,8 @@ def download_video(tv_name: str, index_season_selected: int, index_episode_selec print() # Define filename and path for the downloaded video - mp4_name = f"{map_episode_title(tv_name, index_season_selected, index_episode_selected, obj_episode.name)}.mp4" - mp4_path = os.path.join(ROOT_PATH, SITE_NAME, SERIES_FOLDER, tv_name, f"S{index_season_selected}") + 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(ROOT_PATH, SITE_NAME, SERIES_FOLDER, scrape_serie.series_name, f"S{index_season_selected}") # Retrieve scws and if available master playlist video_source.get_iframe(obj_episode.id) @@ -61,24 +62,25 @@ def download_video(tv_name: str, index_season_selected: int, index_episode_selec output_filename=os.path.join(mp4_path, mp4_name) ).start() - if r_proc == 404: + """if r_proc == 404: time.sleep(2) # Re call search function 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]) + execute_search(frames[-4])""" if r_proc != None: console.print("[green]Result: ") console.print(r_proc) -def download_episode(tv_name: str, index_season_selected: int, scrape_serie: ScrapeSerie, video_source: VideoSource, download_all: bool = False) -> None: + return os.path.join(mp4_path, mp4_name) + +def download_episode(index_season_selected: int, scrape_serie: ScrapeSerie, video_source: VideoSource, download_all: bool = False) -> None: """ Download episodes of a selected season. Parameters: - - tv_name (str): Name of the TV series. - index_season_selected (int): Index of the selected season. - download_all (bool): Download all episodes in the season. """ @@ -95,7 +97,7 @@ def download_episode(tv_name: str, index_season_selected: int, scrape_serie: Scr # Download all episodes without asking for i_episode in range(1, episodes_count + 1): - download_video(tv_name, index_season_selected, i_episode, scrape_serie, video_source) + download_video(index_season_selected, i_episode, scrape_serie, video_source) console.print(f"\n[red]End downloaded [yellow]season: [red]{index_season_selected}.") else: @@ -112,7 +114,7 @@ def download_episode(tv_name: str, index_season_selected: int, scrape_serie: Scr # Download selected episodes for i_episode in list_episode_select: - download_video(tv_name, index_season_selected, i_episode, scrape_serie, video_source) + download_video(index_season_selected, i_episode, scrape_serie, video_source) def download_series(select_season: MediaItem, version: str) -> None: """ @@ -160,11 +162,11 @@ def download_series(select_season: MediaItem, version: str) -> None: if len(list_season_select) > 1 or index_season_selected == "*": # Download all episodes if multiple seasons are selected or if '*' is used - download_episode(select_season.slug, i_season, scrape_serie, video_source, download_all=True) + download_episode(i_season, scrape_serie, video_source, download_all=True) else: # Otherwise, let the user select specific episodes for the single season - download_episode(select_season.slug, i_season, scrape_serie, video_source, download_all=False) + download_episode(i_season, scrape_serie, video_source, download_all=False) def display_episodes_list(scrape_serie) -> str: diff --git a/StreamingCommunity/Api/Site/streamingcommunity/site.py b/StreamingCommunity/Api/Site/streamingcommunity/site.py index cd73bd1..1b8c79e 100644 --- a/StreamingCommunity/Api/Site/streamingcommunity/site.py +++ b/StreamingCommunity/Api/Site/streamingcommunity/site.py @@ -101,6 +101,9 @@ def title_search(title_search: str, domain: str) -> int: Returns: int: The number of titles found. """ + media_search_manager.clear() + table_show_manager.clear() + try: response = httpx.get( url=f"https://{SITE_NAME}.{domain}/api/search?q={title_search.replace(' ', '+')}", diff --git a/StreamingCommunity/Lib/Downloader/HLS/downloader.py b/StreamingCommunity/Lib/Downloader/HLS/downloader.py index 2dd03de..8aa2226 100644 --- a/StreamingCommunity/Lib/Downloader/HLS/downloader.py +++ b/StreamingCommunity/Lib/Downloader/HLS/downloader.py @@ -826,11 +826,6 @@ class HLS_Downloader: Args: out_path (str): The path of the output file to be cleaned up. """ - def dict_to_seconds(d): - """Converts a dictionary of time components to total seconds.""" - if d is not None: - return d['h'] * 3600 + d['m'] * 60 + d['s'] - return 0 # Check if the final output file exists logging.info(f"Check if end file converted exists: {out_path}") @@ -861,7 +856,8 @@ class HLS_Downloader: panel_content = ( f"[bold green]Download completed![/bold green]\n" f"[cyan]File size: [bold red]{formatted_size}[/bold red]\n" - f"[cyan]Duration: [bold]{formatted_duration}[/bold]" + f"[cyan]Duration: [bold]{formatted_duration}[/bold]\n" + f"[cyan]Output: [bold]{self.output_filename}[/bold]" ) if missing_ts: diff --git a/StreamingCommunity/Lib/Downloader/HLS/segments.py b/StreamingCommunity/Lib/Downloader/HLS/segments.py index 338f4dc..9787598 100644 --- a/StreamingCommunity/Lib/Downloader/HLS/segments.py +++ b/StreamingCommunity/Lib/Downloader/HLS/segments.py @@ -558,7 +558,7 @@ 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]." + "This will impact [bold]performance[/bold]. \n" ) # Info to return diff --git a/StreamingCommunity/Lib/Downloader/TOR/downloader.py b/StreamingCommunity/Lib/Downloader/TOR/downloader.py index 37fecdd..49271be 100644 --- a/StreamingCommunity/Lib/Downloader/TOR/downloader.py +++ b/StreamingCommunity/Lib/Downloader/TOR/downloader.py @@ -1,15 +1,18 @@ # 23.06.24 import os +import re import sys import time import shutil +import psutil import logging # Internal utilities from StreamingCommunity.Util.color import Colors from StreamingCommunity.Util.os import internet_manager +from StreamingCommunity.Util.console import console from StreamingCommunity.Util._jsonConfig import config_manager @@ -42,16 +45,18 @@ class TOR_downloader: - username (str): Username for logging into qBittorrent. - password (str): Password for logging into qBittorrent. """ - try: + try: + console.print(f"[cyan]Connect to: [green]{HOST}:{PORT}") self.qb = Client(f'http://{HOST}:{PORT}/') except: logging.error("Start qbitorrent first.") + sys.exit(0) self.username = USERNAME self.password = PASSWORD - self.logged_in = False - self.save_path = None - self.torrent_name = None + self.latest_torrent_hash = None + self.output_file = None + self.file_name = None self.login() @@ -68,155 +73,226 @@ class TOR_downloader: logging.error(f"Failed to log in: {str(e)}") self.logged_in = False + def delete_magnet(self, torrent_info): + + if (int(torrent_info.get('dl_speed')) == 0 and + int(torrent_info.get('peers')) == 0 and + int(torrent_info.get('seeds')) == 0): + + # Elimina il torrent appena aggiunto + console.print(f"[bold red]⚠️ Torrent non scaricabile. Rimozione in corso...[/bold red]") + + try: + # Rimuovi il torrent + self.qb.delete_permanently(torrent_info['hash']) + + except Exception as delete_error: + logging.error(f"Errore durante la rimozione del torrent: {delete_error}") + + # Resetta l'ultimo hash + self.latest_torrent_hash = None + def add_magnet_link(self, magnet_link): """ - Adds a torrent via magnet link to qBittorrent. + Aggiunge un magnet link e recupera le informazioni dettagliate. - Parameters: - - magnet_link (str): Magnet link of the torrent to be added. + Args: + magnet_link (str): Magnet link da aggiungere + + Returns: + dict: Informazioni del torrent aggiunto, o None se fallisce """ - try: - self.qb.download_from_link(magnet_link) - logging.info("Added magnet link to qBittorrent.") - # Get the hash of the latest added torrent - torrents = self.qb.torrents() - if torrents: - self.latest_torrent_hash = torrents[-1]['hash'] - logging.info(f"Latest torrent hash: {self.latest_torrent_hash}") + # Estrai l'hash dal magnet link + magnet_hash_match = re.search(r'urn:btih:([0-9a-fA-F]+)', magnet_link) + + if not magnet_hash_match: + raise ValueError("Hash del magnet link non trovato") + + magnet_hash = magnet_hash_match.group(1).lower() + + # Estrai il nome del file dal magnet link (se presente) + name_match = re.search(r'dn=([^&]+)', magnet_link) + torrent_name = name_match.group(1).replace('+', ' ') if name_match else "Nome non disponibile" + + # Salva il timestamp prima di aggiungere il torrent + before_add_time = time.time() + + # Aggiungi il magnet link + console.print(f"[cyan]Aggiunta magnet link[/cyan]: [red]{magnet_link}") + self.qb.download_from_link(magnet_link) + + # Aspetta un attimo per essere sicuri che il torrent sia stato aggiunto + time.sleep(1) + + # Cerca il torrent + torrents = self.qb.torrents() + matching_torrents = [ + t for t in torrents + if (t['hash'].lower() == magnet_hash) or (t.get('added_on', 0) > before_add_time) + ] + + if not matching_torrents: + raise ValueError("Nessun torrent corrispondente trovato") + + # Prendi il primo torrent corrispondente + torrent_info = matching_torrents[0] + + # Formatta e stampa le informazioni + console.print("\n[bold green]🔗 Dettagli Torrent Aggiunto:[/bold green]") + console.print(f"[yellow]Nome:[/yellow] {torrent_info.get('name', torrent_name)}") + console.print(f"[yellow]Hash:[/yellow] {torrent_info['hash']}") + console.print(f"[yellow]Dimensione:[/yellow] {torrent_info.get('size', 'Non disponibile'):,} bytes") + console.print(f"[yellow]Stato:[/yellow] {torrent_info.get('state', 'Sconosciuto')}") + print() - except Exception as e: - logging.error(f"Failed to add magnet link: {str(e)}") + # Salva l'hash per usi successivi e il path + self.latest_torrent_hash = torrent_info['hash'] + self.output_file = torrent_info['content_path'] + self.file_name = torrent_info['name'] + + # Controlla che sia possibile il download + time.sleep(5) + self.delete_magnet(self.qb.get_torrent(self.latest_torrent_hash)) + + return torrent_info def start_download(self): """ Starts downloading the latest added torrent and monitors progress. """ - try: + if self.latest_torrent_hash is not None: + try: - torrents = self.qb.torrents() - if not torrents: - logging.error("No torrents found.") - return - - # Sleep to load magnet to qbit app - time.sleep(10) - latest_torrent = torrents[-1] - torrent_hash = latest_torrent['hash'] + # Custom bar for mobile and pc + if TQDM_USE_LARGE_BAR: + bar_format = ( + f"{Colors.YELLOW}[TOR] {Colors.WHITE}({Colors.CYAN}video{Colors.WHITE}): " + f"{Colors.RED}{{percentage:.2f}}% {Colors.MAGENTA}{{bar}} {Colors.WHITE}[ " + f"{Colors.YELLOW}{{elapsed}} {Colors.WHITE}< {Colors.CYAN}{{remaining}}{{postfix}} {Colors.WHITE}]" + ) + else: + bar_format = ( + f"{Colors.YELLOW}Proc{Colors.WHITE}: " + f"{Colors.RED}{{percentage:.2f}}% {Colors.WHITE}| " + f"{Colors.CYAN}{{remaining}}{{postfix}} {Colors.WHITE}]" + ) - # Custom bar for mobile and pc - if TQDM_USE_LARGE_BAR: - bar_format = ( - f"{Colors.YELLOW}[TOR] {Colors.WHITE}({Colors.CYAN}video{Colors.WHITE}): " - f"{Colors.RED}{{percentage:.2f}}% {Colors.MAGENTA}{{bar}} {Colors.WHITE}[ " - f"{Colors.YELLOW}{{elapsed}} {Colors.WHITE}< {Colors.CYAN}{{remaining}}{{postfix}} {Colors.WHITE}]" - ) - else: - bar_format = ( - f"{Colors.YELLOW}Proc{Colors.WHITE}: " - f"{Colors.RED}{{percentage:.2f}}% {Colors.WHITE}| " - f"{Colors.CYAN}{{remaining}}{{postfix}} {Colors.WHITE}]" + progress_bar = tqdm( + total=100, + ascii='░▒█', + bar_format=bar_format, + unit_scale=True, + unit_divisor=1024, + mininterval=0.05 ) - progress_bar = tqdm( - total=100, - ascii='░▒█', - bar_format=bar_format, - unit_scale=True, - unit_divisor=1024, - mininterval=0.05 - ) + with progress_bar as pbar: + while True: - with progress_bar as pbar: - while True: + # Get variable from qtorrent + torrent_info = self.qb.get_torrent(self.latest_torrent_hash) + self.save_path = torrent_info['save_path'] + self.torrent_name = torrent_info['name'] - # Get variable from qtorrent - torrent_info = self.qb.get_torrent(torrent_hash) - self.save_path = torrent_info['save_path'] - self.torrent_name = torrent_info['name'] + # Fetch important variable + pieces_have = torrent_info['pieces_have'] + pieces_num = torrent_info['pieces_num'] + progress = (pieces_have / pieces_num) * 100 if pieces_num else 0 + pbar.n = progress - # Fetch important variable - pieces_have = torrent_info['pieces_have'] - pieces_num = torrent_info['pieces_num'] - progress = (pieces_have / pieces_num) * 100 if pieces_num else 0 - pbar.n = progress + download_speed = torrent_info['dl_speed'] + total_size = torrent_info['total_size'] + downloaded_size = torrent_info['total_downloaded'] - download_speed = torrent_info['dl_speed'] - total_size = torrent_info['total_size'] - downloaded_size = torrent_info['total_downloaded'] + # Format variable + downloaded_size_str = internet_manager.format_file_size(downloaded_size) + downloaded_size = downloaded_size_str.split(' ')[0] - # Format variable - downloaded_size_str = internet_manager.format_file_size(downloaded_size) - downloaded_size = downloaded_size_str.split(' ')[0] + total_size_str = internet_manager.format_file_size(total_size) + total_size = total_size_str.split(' ')[0] + total_size_unit = total_size_str.split(' ')[1] - total_size_str = internet_manager.format_file_size(total_size) - total_size = total_size_str.split(' ')[0] - total_size_unit = total_size_str.split(' ')[1] + average_internet_str = internet_manager.format_transfer_speed(download_speed) + average_internet = average_internet_str.split(' ')[0] + average_internet_unit = average_internet_str.split(' ')[1] - average_internet_str = internet_manager.format_transfer_speed(download_speed) - average_internet = average_internet_str.split(' ')[0] - average_internet_unit = average_internet_str.split(' ')[1] + # Update the progress bar's postfix + if TQDM_USE_LARGE_BAR: + pbar.set_postfix_str( + f"{Colors.WHITE}[ {Colors.GREEN}{downloaded_size} {Colors.WHITE}< {Colors.GREEN}{total_size} {Colors.RED}{total_size_unit} " + f"{Colors.WHITE}| {Colors.CYAN}{average_internet} {Colors.RED}{average_internet_unit}" + ) + else: + pbar.set_postfix_str( + f"{Colors.WHITE}[ {Colors.GREEN}{downloaded_size}{Colors.RED} {total_size} " + f"{Colors.WHITE}| {Colors.CYAN}{average_internet} {Colors.RED}{average_internet_unit}" + ) + + pbar.refresh() + time.sleep(0.2) - # Update the progress bar's postfix - if TQDM_USE_LARGE_BAR: - pbar.set_postfix_str( - f"{Colors.WHITE}[ {Colors.GREEN}{downloaded_size} {Colors.WHITE}< {Colors.GREEN}{total_size} {Colors.RED}{total_size_unit} " - f"{Colors.WHITE}| {Colors.CYAN}{average_internet} {Colors.RED}{average_internet_unit}" - ) - else: - pbar.set_postfix_str( - f"{Colors.WHITE}[ {Colors.GREEN}{downloaded_size}{Colors.RED} {total_size} " - f"{Colors.WHITE}| {Colors.CYAN}{average_internet} {Colors.RED}{average_internet_unit}" - ) - - pbar.refresh() - time.sleep(0.2) + # Break at the end + if int(progress) == 100: + break - # Break at the end - if int(progress) == 100: - break + except KeyboardInterrupt: + logging.info("Download process interrupted.") - except KeyboardInterrupt: - logging.info("Download process interrupted.") + def is_file_in_use(self, file_path: str) -> bool: + """Check if a file is in use by any process.""" + for proc in psutil.process_iter(['open_files']): + try: + if any(file_path == f.path for f in proc.info['open_files'] or []): + return True + except (psutil.NoSuchProcess, psutil.AccessDenied): + continue - except Exception as e: - logging.error(f"Download error: {str(e)}") - sys.exit(0) + return False - def move_downloaded_files(self, destination=None): + def move_downloaded_files(self, destination: str): """ Moves downloaded files of the latest torrent to another location. Parameters: - - save_path (str): Current save path (output directory) of the torrent. - - destination (str, optional): Destination directory to move files. If None, moves to current directory. + - destination (str): Destination directory to move files. Returns: - bool: True if files are moved successfully, False otherwise. """ - - video_extensions = {'.mp4', '.mkv', 'avi'} - time.sleep(2) + console.print(f"[cyan]Destination folder: [red]{destination}") - # List directories in the save path - dirs = [d for d in os.listdir(self.save_path) if os.path.isdir(os.path.join(self.save_path, d))] - - for dir_name in dirs: - if self.torrent_name.split(" ")[0] in dir_name: - dir_path = os.path.join(self.save_path, dir_name) + try: - # Ensure destination is set; if not, use current directory - destination = destination or os.getcwd() + # Ensure the file is not in use + timeout = 3 + elapsed = 0 + while self.is_file_in_use(self.output_file) and elapsed < timeout: + time.sleep(1) + elapsed += 1 + + if elapsed == timeout: + raise Exception(f"File '{self.output_file}' is in use and could not be moved.") - # Move only video files - for file_name in os.listdir(dir_path): - file_path = os.path.join(dir_path, file_name) - - # Check if it's a file and if it has a video extension - if os.path.isfile(file_path) and os.path.splitext(file_name)[1] in video_extensions: - shutil.move(file_path, os.path.join(destination, file_name)) - logging.info(f"Moved file {file_name} to {destination}") + # Ensure destination directory exists + os.makedirs(destination, exist_ok=True) - time.sleep(2) - self.qb.delete_permanently(self.qb.torrents()[-1]['hash']) - return True + # Perform the move operation + try: + shutil.move(self.output_file, destination) + + except OSError as e: + if e.errno == 17: # Cross-disk move error + # Perform copy and delete manually + shutil.copy2(self.output_file, destination) + os.remove(self.output_file) + else: + raise + + # Delete the torrent data + #self.qb.delete_permanently(self.qb.torrents()[-1]['hash']) + return True + + except Exception as e: + print(f"Error moving file: {e}") + return False \ No newline at end of file diff --git a/StreamingCommunity/Upload/version.py b/StreamingCommunity/Upload/version.py index 073a917..6ff0845 100644 --- a/StreamingCommunity/Upload/version.py +++ b/StreamingCommunity/Upload/version.py @@ -1,5 +1,5 @@ __title__ = 'StreamingCommunity' -__version__ = '1.9.2' +__version__ = '1.9.4' __author__ = 'Lovi-0' __description__ = 'A command-line program to download film' __copyright__ = 'Copyright 2024' diff --git a/StreamingCommunity/Util/table.py b/StreamingCommunity/Util/table.py index bfc6553..6e94090 100644 --- a/StreamingCommunity/Util/table.py +++ b/StreamingCommunity/Util/table.py @@ -162,11 +162,8 @@ class TVShowManager: # Get the search function search_func = getattr(module, 'media_search_manager') - # Ask for search string - string_to_search = Prompt.ask(f"\n[purple]Insert word to search in [red]{research_func['folder_base']}").strip() - # Call the search function with the search string - search_func(string_to_search) + search_func(None) except Exception as e: self.console.print(f"[red]Error during search: {e}") @@ -227,13 +224,10 @@ class TVShowManager: module = importlib.import_module(module_path) # Get the search function - search_func = getattr(module, 'media_search_manager') - - # Ask for search string - string_to_search = Prompt.ask(f"\n[purple]Insert word to search in [red]{research_func['folder_base']}").strip() + search_func = getattr(module, 'search') # Call the search function with the search string - search_func(string_to_search) + search_func(None) except Exception as e: self.console.print(f"[red]Error during search: {e}") diff --git a/StreamingCommunity/run.py b/StreamingCommunity/run.py index e58a47f..6405c99 100644 --- a/StreamingCommunity/run.py +++ b/StreamingCommunity/run.py @@ -120,11 +120,11 @@ def initialize(): sys.exit(0) # Attempting GitHub update - try: + """try: git_update() print() except: - console.log("[red]Error with loading github.") + console.log("[red]Error with loading github.")""" # Show trending film and series if SHOW_TRENDING: diff --git a/Test/Download/TOR.py b/Test/Download/TOR.py index a822e1b..52a7cdd 100644 --- a/Test/Download/TOR.py +++ b/Test/Download/TOR.py @@ -19,7 +19,7 @@ start_message() logger = Logger() manager = TOR_downloader() -magnet_link = "magnet:?x" +magnet_link = "magnet:?xt=" manager.add_magnet_link(magnet_link) manager.start_download() manager.move_downloaded_files() diff --git a/config.json b/config.json index 75103e9..a8eab9b 100644 --- a/config.json +++ b/config.json @@ -10,7 +10,7 @@ "serie_folder_name": "TV", "map_episode_name": "%(tv_name)_S%(season)E%(episode)_%(episode_name)", "config_qbit_tor": { - "host": "192.168.1.59", + "host": "192.168.1.58", "port": "8080", "user": "admin", "pass": "adminadmin" @@ -85,16 +85,13 @@ "domain": "to" }, "cb01new": { - "domain": "club" - }, - "bitsearch": { - "domain": "to" + "domain": "icu" }, "1337xx": { "domain": "to" }, - "piratebays": { - "domain": "to" + "ilcorsaronero": { + "domain": "link" } } } \ No newline at end of file diff --git a/setup.py b/setup.py index 1381c0d..b613c7d 100644 --- a/setup.py +++ b/setup.py @@ -11,7 +11,7 @@ with open("requirements.txt", "r", encoding="utf-8-sig") as f: setup( name="StreamingCommunity", - version="1.9.2", + version="1.9.4", long_description=read_readme(), long_description_content_type="text/markdown", author="Lovi-0",