From 001c3f09e8bfb0a80f01e79c794d6fc6355b990d Mon Sep 17 00:00:00 2001 From: Lovi <62809003+Lovi-0@users.noreply.github.com> Date: Fri, 27 Dec 2024 12:20:59 +0100 Subject: [PATCH] v2.0.0 - 2 --- .gitignore | 15 +- unix_install.sh => Installer/unix_install.sh | 3 + win_install.bat => Installer/win_install.bat | 4 + README.md | 99 ++++++++----- .../Api/Site/mostraguarda/film.py | 4 + .../Api/Site/streamingcommunity/site.py | 15 +- .../Api/Template/Util/get_domain.py | 76 +++++----- StreamingCommunity/Lib/FFmpeg/command.py | 2 +- StreamingCommunity/Lib/FFmpeg/util.py | 4 +- StreamingCommunity/Lib/M3U8/decryptor.py | 2 +- StreamingCommunity/Upload/version.py | 2 +- StreamingCommunity/Util/message.py | 66 +++++---- StreamingCommunity/Util/os.py | 127 ++++++++++------- StreamingCommunity/Util/table.py | 130 ++++++++---------- StreamingCommunity/__init__.py | 0 config.json | 8 +- 16 files changed, 306 insertions(+), 251 deletions(-) rename unix_install.sh => Installer/unix_install.sh (98%) rename win_install.bat => Installer/win_install.bat (97%) create mode 100644 StreamingCommunity/__init__.py diff --git a/.gitignore b/.gitignore index be7663d..af1db6a 100644 --- a/.gitignore +++ b/.gitignore @@ -3,9 +3,6 @@ __pycache__/ *.py[cod] *$py.class -# C extensions -*.so - # Distribution / packaging .Python build/ @@ -22,11 +19,12 @@ share/python-wheels/ *.egg-info/ .installed.cfg *.egg -MANIFEST # PyInstaller *.manifest *.spec +setup.py +MANIFEST.in # Installer logs pip-log.txt @@ -36,15 +34,6 @@ pip-delete-this-directory.txt *.mo *.pot -# Django stuff: -*.log -local_settings.py -db.sqlite3 -db.sqlite3-journal - -# Jupyter Notebook -.ipynb_checkpoints - # Environments .env .venv diff --git a/unix_install.sh b/Installer/unix_install.sh similarity index 98% rename from unix_install.sh rename to Installer/unix_install.sh index 8382b51..405d702 100644 --- a/unix_install.sh +++ b/Installer/unix_install.sh @@ -1,5 +1,8 @@ #!/bin/sh +# Spostarsi nella directory superiore rispetto a quella corrente +cd "$(dirname "$0")/.." || exit 1 + # Function to check if a command exists command_exists() { command -v "$1" > /dev/null 2>&1 diff --git a/win_install.bat b/Installer/win_install.bat similarity index 97% rename from win_install.bat rename to Installer/win_install.bat index acc4d5a..1e96c70 100644 --- a/win_install.bat +++ b/Installer/win_install.bat @@ -1,4 +1,8 @@ @echo off + +:: Spostarsi nella directory superiore rispetto a quella corrente +cd .. + :: Check if the script is running as administrator net session >nul 2>&1 if %errorlevel% neq 0 ( diff --git a/README.md b/README.md index 774a20f..523366c 100644 --- a/README.md +++ b/README.md @@ -1,35 +1,64 @@ -# StreamingCommunity Downloader +

+ Project Logo +

-![Project Logo](https://i.ibb.co/f4h5Y2m/min-logo.png) +

+ + PyPI + + + Python + + + Donate + + + License + + + Commits + + + Last Commit + +

-A versatile script designed to download films and series from various supported streaming platforms. - -# 🤝 Join our Community - -Chat, contribute, and have fun in our **Git_StreamingCommunity** Discord [Server](https://discord.com/invite/8vV68UGRc7) +

+ + PyPI Downloads + + + Forks + + + Code Size + + + Repo Size + +

# 📋 Table of Contents -- [Website available](#website-status) -- [Installation](#installation) - - [PyPI Installation](#1-pypi-installation) - - [Automatic Installation](#2-automatic-installation) - - [Manual Installation](#3-manual-installation) - - [Win 7](https://github.com/Ghost6446/StreamingCommunity_api/wiki/Installation#win-7) - - [Termux](https://github.com/Ghost6446/StreamingCommunity_api/wiki/Termux) -- [Configuration](#configuration) - - [Default](#default-settings) - - [Request](#requests-settings) - - [Download](#m3u8_download-settings) - - [Parser](#m3u8_parser-settings) -- [Docker](#docker) -- [Tutorial](#tutorials) -- [To Do](#to-do) -- [Support](#support) -- [Contribute](#contributing) -- [Disclamer](#disclaimer) - - +- 🌐 [Website available](#website-status) +- 🛠️ [Installation](#installation) + - 📦 [PyPI Installation](#1-pypi-installation) + - 🔄 [Automatic Installation](#2-automatic-installation) + - 📝 [Manual Installation](#3-manual-installation) + - 💻 [Win 7](https://github.com/Ghost6446/StreamingCommunity_api/wiki/Installation#win-7) + - 📱 [Termux](https://github.com/Ghost6446/StreamingCommunity_api/wiki/Termux) +- ⚙️ [Configuration](#configuration) + - 🔧 [Default](#default-settings) + - 📩 [Request](#requests-settings) + - 📥 [Download](#m3u8_download-settings) + - 🔍 [Parser](#m3u8_parser-settings) +- 🐳 [Docker](#docker) +- 🎓 [Tutorial](#tutorials) +- 📝 [To do](#to-do) +- 💬 [Support](#support) +- 🤝 [Contribute](#contributing) +- ⚠️ [Disclaimer](#disclaimer) +- ⚡ [Contributors](#contributors) # Installation @@ -83,13 +112,13 @@ pip install --upgrade StreamingCommunity #### On Windows: ```powershell -.\win_install.bat +.\Installer\win_install.bat ``` #### On Linux/MacOS/BSD: ```bash -sudo chmod +x unix_install.sh && ./unix_install.sh +sudo chmod +x Installer/unix_install.sh && ./Installer/unix_install.sh ``` ### Usage @@ -378,12 +407,6 @@ 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 - -If you'd like to support this project, consider making a donation! - -[![Donate](https://www.paypalobjects.com/en_US/i/btn/btn_donate_LG.gif)](https://www.paypal.com/donate/?hosted_button_id=UXTWMT8P6HE2C) - # Contributing Contributions are welcome! Steps: @@ -397,3 +420,9 @@ Contributions are welcome! Steps: # Disclaimer This software is provided "as is", without warranty of any kind, express or implied, including but not limited to the warranties of merchantability, fitness for a particular purpose, and noninfringement. In no event shall the authors or copyright holders be liable for any claim, damages, or other liability, whether in an action of contract, tort, or otherwise, arising from, out of, or in connection with the software or the use or other dealings in the software. + +## Contributors + + + Contributors + diff --git a/StreamingCommunity/Api/Site/mostraguarda/film.py b/StreamingCommunity/Api/Site/mostraguarda/film.py index 224069c..d878445 100644 --- a/StreamingCommunity/Api/Site/mostraguarda/film.py +++ b/StreamingCommunity/Api/Site/mostraguarda/film.py @@ -61,6 +61,10 @@ def download_film(movie_details: Json_film) -> str: logging.error(f"Not found in the server. Dict: {movie_details}") raise + if "not found" in str(response.text): + logging.error(f"Cant find in the server, Element: {movie_details}") + raise + # Extract supervideo url soup = BeautifulSoup(response.text, "html.parser") player_links = soup.find("ul", class_ = "_player-mirrors").find_all("li") diff --git a/StreamingCommunity/Api/Site/streamingcommunity/site.py b/StreamingCommunity/Api/Site/streamingcommunity/site.py index 1b8c79e..ecb549e 100644 --- a/StreamingCommunity/Api/Site/streamingcommunity/site.py +++ b/StreamingCommunity/Api/Site/streamingcommunity/site.py @@ -76,13 +76,14 @@ def get_version_and_domain(): # Extract version from the response try: - version = get_version(httpx.get( - url=base_url, - headers={ - 'user-agent': get_headers() - }, - timeout=max_timeout - ).text) + version = get_version( + httpx.get( + url=base_url, + headers={'User-Agent': get_headers()}, + timeout=max_timeout + ).text + ) + except: console.print("[green]Auto generate version ...") version = secrets.token_hex(32 // 2) diff --git a/StreamingCommunity/Api/Template/Util/get_domain.py b/StreamingCommunity/Api/Template/Util/get_domain.py index 9c66ab5..205e306 100644 --- a/StreamingCommunity/Api/Template/Util/get_domain.py +++ b/StreamingCommunity/Api/Template/Util/get_domain.py @@ -87,7 +87,6 @@ def search_domain(site_name: str, base_url: str): Returns: tuple: The found domain and the complete URL. """ - # Extract config domain max_timeout = config_manager.get_int("REQUESTS", "timeout") domain = str(config_manager.get_dict("SITE", site_name)['domain']) @@ -102,7 +101,6 @@ def search_domain(site_name: str, base_url: str): }, follow_redirects=True, timeout=max_timeout - ) as client: response_follow = client.get(f"{base_url}.{domain}") response_follow.raise_for_status() @@ -111,51 +109,57 @@ def search_domain(site_name: str, base_url: str): query = base_url.split("/")[-1] # Perform a Google search with multiple results - search_results = list(search(query, num_results=5)) - #console.print(f"[green]Google search results[white]: {search_results}") + search_results = list(search(query, num_results=10, lang="it")) + console.print(f"\nGoogle search results: {search_results}") + def normalize_for_comparison(url): + """Normalize URL by removing protocol, www, and trailing slashes""" + url = url.lower() + url = url.replace("https://", "").replace("http://", "") + url = url.replace("www.", "") + return url.rstrip("/") + + # Normalize the base_url we're looking for + target_url = normalize_for_comparison(base_url) + # Iterate through search results for first_url in search_results: console.print(f"[green]Checking url[white]: [red]{first_url}") - # Check if the base URL matches the Google search result - parsed_first_url = urlparse(first_url) - - # Compare base url from google search with base url from config.json - if parsed_first_url.netloc.split(".")[0] == base_url: - console.print(f"[red]URL does not match base URL. Skipping.[/red]") - continue - - try: - final_url = get_final_redirect_url(first_url, max_timeout) - - if final_url is not None: - - def extract_domain(url): - parsed_url = urlparse(url) - domain = parsed_url.netloc - return domain.split(".")[-1] - - new_domain_extract = extract_domain(str(final_url)) - - if msg.ask(f"[cyan]\nDo you want to auto site[white]: [red]{site_name}[cyan] with domain[white]: [red]{new_domain_extract}", choices=["y", "n"], default="y").lower() == "y": - - # Update domain in config.json - config_manager.config['SITE'][site_name]['domain'] = new_domain_extract - config_manager.write_config() - - # Return config domain - return new_domain_extract, f"{base_url}.{new_domain_extract}" + # Get just the domain part of the search result + parsed_result = urlparse(first_url) + result_domain = normalize_for_comparison(parsed_result.netloc) - except Exception as redirect_error: - console.print(f"[red]Error following redirect for {first_url}: {redirect_error}") - continue + # Compare with our target URL (without the protocol part) + if result_domain.startswith(target_url.split("/")[-1]): + try: + final_url = get_final_redirect_url(first_url, max_timeout) + + if final_url is not None: + def extract_domain(url): + parsed_url = urlparse(url) + domain = parsed_url.netloc + return domain.split(".")[-1] + + new_domain_extract = extract_domain(str(final_url)) + + if msg.ask(f"\n[cyan]Do you want to auto update site[white] [red]'{site_name}'[cyan] with domain[white] [red]'{new_domain_extract}'.", choices=["y", "n"], default="y").lower() == "y": + + # Update domain in config.json + config_manager.config['SITE'][site_name]['domain'] = new_domain_extract + config_manager.write_config() + + return new_domain_extract, f"{base_url}.{new_domain_extract}" + + except Exception as redirect_error: + console.print(f"[red]Error following redirect for {first_url}: {redirect_error}") + continue # If no matching URL is found console.print("[bold red]No valid URL found matching the base URL.[/bold red]") raise Exception("No matching domain found") - # Ensure the URL is in string format before parsing + # Handle successful initial domain check parsed_url = urlparse(str(response_follow.url)) parse_domain = parsed_url.netloc tld = parse_domain.split('.')[-1] diff --git a/StreamingCommunity/Lib/FFmpeg/command.py b/StreamingCommunity/Lib/FFmpeg/command.py index 916bb0d..51a8a83 100644 --- a/StreamingCommunity/Lib/FFmpeg/command.py +++ b/StreamingCommunity/Lib/FFmpeg/command.py @@ -62,7 +62,7 @@ def join_video(video_path: str, out_path: str, codec: M3U8_Codec = None): # Add mpegts to force to detect input file as ts file if need_to_force_to_ts(video_path): - console.log("[red]Force input file to 'mpegts'.") + #console.log("[red]Force input file to 'mpegts'.") ffmpeg_cmd.extend(['-f', 'mpegts']) vcodec = "libx264" diff --git a/StreamingCommunity/Lib/FFmpeg/util.py b/StreamingCommunity/Lib/FFmpeg/util.py index 7c7828e..41b7c43 100644 --- a/StreamingCommunity/Lib/FFmpeg/util.py +++ b/StreamingCommunity/Lib/FFmpeg/util.py @@ -74,8 +74,8 @@ def get_video_duration(file_path: str) -> float: # Extract duration from the video information try: return float(probe_result['format']['duration']) + except: - logging.error("Cant get duration.") return 1 except Exception as e: @@ -207,6 +207,8 @@ def is_png_format_or_codec(file_info): """ if not file_info: return False + + console.print(f"[yellow][FFmpeg] [cyan]Avaiable codec[white]: [red]{file_info['codec_names']}") return file_info['format_name'] == 'png_pipe' or 'png' in file_info['codec_names'] diff --git a/StreamingCommunity/Lib/M3U8/decryptor.py b/StreamingCommunity/Lib/M3U8/decryptor.py index 8480bbf..1b5f8a6 100644 --- a/StreamingCommunity/Lib/M3U8/decryptor.py +++ b/StreamingCommunity/Lib/M3U8/decryptor.py @@ -17,7 +17,7 @@ crypto_installed = crypto_spec is not None if crypto_installed: - console.print("[cyan]Decrypy use: Cryptodome") + console.print("[cyan]Decrypy use: Cryptodomex") from Cryptodome.Cipher import AES from Cryptodome.Util.Padding import unpad diff --git a/StreamingCommunity/Upload/version.py b/StreamingCommunity/Upload/version.py index 3a15edf..60b8242 100644 --- a/StreamingCommunity/Upload/version.py +++ b/StreamingCommunity/Upload/version.py @@ -1,5 +1,5 @@ __title__ = 'StreamingCommunity' -__version__ = '1.9.8' +__version__ = '2.0.0' __author__ = 'Lovi-0' __description__ = 'A command-line program to download film' __copyright__ = 'Copyright 2024' diff --git a/StreamingCommunity/Util/message.py b/StreamingCommunity/Util/message.py index 8a7144f..ad541ad 100644 --- a/StreamingCommunity/Util/message.py +++ b/StreamingCommunity/Util/message.py @@ -3,44 +3,62 @@ import os import platform - # Internal utilities from StreamingCommunity.Util.console import console from StreamingCommunity.Util._jsonConfig import config_manager - # Variable CLEAN = config_manager.get_bool('DEFAULT', 'clean_console') SHOW = config_manager.get_bool('DEFAULT', 'show_message') +def create_italian_flag_colored_text(text: str) -> str: + """Create text divided into three sections with Italian flag colors, splitting each line at two spaces.""" + + # Split the text into lines + lines = text.splitlines() + + colored_lines = [] + + for line in lines: + # Split each line into parts using two spaces as a delimiter + parts = line.split(" ") + + # Ensure there are exactly 3 parts (add empty strings if necessary) + parts += [''] * (4 - len(parts)) + + # Apply flag colors to the parts + green_part = f"[green]{parts[0]}[/]" + white_part = f"[white]{parts[1]}[/]" + red_part = f"[red]{parts[2]}[/]" + + # Reassemble the colored line + colored_line = green_part + white_part + red_part + colored_lines.append(colored_line) + + # Join all colored lines back into a single string + return "\n".join(colored_lines) + def start_message(): - """ - Display a start message. - """ - + """Display a stylized start message in the console.""" + msg = r''' + ██╗ ██████╗ ██╗ ██╗██╗ ██╗ ██╗ ███████╗████████╗██████╗ ███████╗ █████╗ ███╗ ███╗██╗███╗ ██╗ ██████╗ + ██║ ██╔═══██╗██║ ██║██║ ╚██╗██╔╝ ██╔════╝╚══██╔══╝██╔══██╗██╔════╝██╔══██╗████╗ ████║██║████╗ ██║██╔════╝ + ██║ ██║ ██║██║ ██║██║ ╚███╔╝ ███████╗ ██║ ██████╔╝█████╗ ███████║██╔████╔██║██║██╔██╗ ██║██║ ███╗ + ██║ ██║ ██║╚██╗ ██╔╝██║ ██╔██╗ ╚════██║ ██║ ██╔══██╗██╔══╝ ██╔══██║██║╚██╔╝██║██║██║╚██╗██║██║ ██║ + ███████╗╚██████╔╝ ╚████╔╝ ██║ ██╔╝ ██╗ ███████║ ██║ ██║ ██║███████╗██║ ██║██║ ╚═╝ ██║██║██║ ╚████║╚██████╔╝ + ╚══════╝ ╚═════╝ ╚═══╝ ╚═╝ ╚═╝ ╚═╝ ╚══════╝ ╚═╝ ╚═╝ ╚═╝╚══════╝╚═╝ ╚═╝╚═╝ ╚═╝╚═╝╚═╝ ╚═══╝ ╚═════╝ + '''.rstrip() - _____ _ _ _____ _ _ - / ____| | (_) / ____| (_) | - | (___ | |_ _ __ ___ __ _ _ __ ___ _ _ __ __ _ | | ___ _ __ ___ _ __ ___ _ _ _ __ _| |_ _ _ - \___ \| __| '__/ _ \/ _` | '_ ` _ \| | '_ \ / _` | | | / _ \| '_ ` _ \| '_ ` _ \| | | | '_ \| | __| | | | - ____) | |_| | | __/ (_| | | | | | | | | | | (_| | | |___| (_) | | | | | | | | | | | |_| | | | | | |_| |_| | - |_____/ \__|_| \___|\__,_|_| |_| |_|_|_| |_|\__, | \_____\___/|_| |_| |_|_| |_| |_|\__,_|_| |_|_|\__|\__, | - __/ | __/ | - |___/ |___/ - - ''' + colored_msg = create_italian_flag_colored_text(msg) if CLEAN: - if platform.system() == 'Windows': - os.system("cls") - else: - os.system("clear") + os.system("cls" if platform.system() == 'Windows' else "clear") if SHOW: - console.print(f"[bold yellow]{msg}") - console.print(f"[magenta]Created by: Lovi\n") + console.print(colored_msg) - row = "-" * console.width - console.print(f"[yellow]{row} \n") + # Print a decorative separator line using asterisks + separator = "_" * (console.width - 2) # Ridotto di 2 per il padding + console.print(f"[yellow]{separator}[/yellow]\n") diff --git a/StreamingCommunity/Util/os.py b/StreamingCommunity/Util/os.py index bcd2167..d67f6e7 100644 --- a/StreamingCommunity/Util/os.py +++ b/StreamingCommunity/Util/os.py @@ -27,28 +27,28 @@ from StreamingCommunity.Util.console import console, msg # Variable OS_CONFIGURATIONS = { - 'windows': { - 'max_length': 255, - 'invalid_chars': '<>:"/\\|?*', - 'reserved_names': [ - "CON", "PRN", "AUX", "NUL", - "COM1", "COM2", "COM3", "COM4", "COM5", "COM6", "COM7", "COM8", "COM9", - "LPT1", "LPT2", "LPT3", "LPT4", "LPT5", "LPT6", "LPT7", "LPT8", "LPT9" - ], - 'max_path': 255 - }, - 'darwin': { - 'max_length': 4096, - 'invalid_chars': '/:', - 'reserved_names': [], - 'hidden_file_restriction': True - }, - 'linux': { - 'max_length': 4096, - 'invalid_chars': '/\0', - 'reserved_names': [] - } + 'windows': { + 'max_length': 255, + 'invalid_chars': '<>:"/\\|?*', + 'reserved_names': [ + "CON", "PRN", "AUX", "NUL", + "COM1", "COM2", "COM3", "COM4", "COM5", "COM6", "COM7", "COM8", "COM9", + "LPT1", "LPT2", "LPT3", "LPT4", "LPT5", "LPT6", "LPT7", "LPT8", "LPT9" + ], + 'max_path': 255 + }, + 'darwin': { + 'max_length': 4096, + 'invalid_chars': '/:', + 'reserved_names': [], + 'hidden_file_restriction': True + }, + 'linux': { + 'max_length': 4096, + 'invalid_chars': '/\0', + 'reserved_names': [] } +} @@ -66,6 +66,27 @@ class OsManager: raise ValueError(f"Unsupported operating system: {system}") + def _normalize_windows_path(self, path: str) -> str: + """ + Normalize Windows paths to handle drive letters correctly. + + Args: + path (str): Original path that might contain a drive letter. + + Returns: + str: Properly normalized absolute path. + """ + if self.system != 'windows': + return path + + # Check if path starts with a drive letter + if len(path) >= 2 and path[1] == ':': + drive = path[0:2] + rest = path[2:].lstrip(os.sep) + # Ensure proper absolute path format + return os.path.join(drive + os.sep, rest) + return path + def _process_filename(self, filename: str) -> str: """ Comprehensively process filename with cross-platform considerations. @@ -76,8 +97,6 @@ class OsManager: Returns: str: Processed filename. """ - # Preserve file extension - logging.info("_process_filename: ", filename) name, ext = os.path.splitext(filename) # Handle length restrictions @@ -118,7 +137,6 @@ class OsManager: Returns: str: Sanitized filename. """ - logging.info("get_sanitize_file: ", filename) # Decode unicode characters and sanitize decoded_filename = unidecode.unidecode(filename) @@ -129,8 +147,8 @@ class OsManager: if len(name) > self.config['max_length']: name = self._truncate_filename(name) - logging.info("return :", name + ext) - return name + ext + result = name + ext + return result def get_sanitize_path(self, path: str) -> str: """ @@ -142,7 +160,9 @@ class OsManager: Returns: str: Sanitized folder path. """ - logging.info("get_sanitize_file: ", path) + + # Normalize path for Windows drive letters first + path = self._normalize_windows_path(path) # Decode unicode characters and sanitize decoded_path = unidecode.unidecode(path) @@ -150,16 +170,26 @@ class OsManager: # Split path and process each component path_components = os.path.normpath(sanitized_path).split(os.sep) - processed_components = [] - for component in path_components: - # Truncate component if necessary - if len(component) > self.config['max_length']: - component = self._truncate_filename(component) - processed_components.append(component) + # Handle Windows drive letter specially + if self.system == 'windows' and len(path_components[0]) == 2 and path_components[0][1] == ':': + drive = path_components.pop(0) + processed_components = [drive + os.sep] - logging.info("return :", os.path.join(*processed_components)) - return os.path.join(*processed_components) + else: + processed_components = [] + + # Process remaining components + for component in path_components: + if component: # Skip empty components + if len(component) > self.config['max_length']: + component = self._truncate_filename(component) + + processed_components.append(component) + + # Join with proper separator and normalize + result = os.path.normpath(os.path.join(*processed_components)) + return result def create_path(self, path: str, mode: int = 0o755) -> bool: """ @@ -173,10 +203,7 @@ class OsManager: bool: True if path created successfully, False otherwise. """ try: - # Sanitize path first sanitized_path = self.get_sanitize_path(path) - - # Create directory with recursive option os.makedirs(sanitized_path, mode=mode, exist_ok=True) return True @@ -197,6 +224,7 @@ class OsManager: try: shutil.rmtree(folder_path) return True + except OSError as e: logging.error(f"Folder removal error: {e}") return False @@ -238,18 +266,12 @@ class OsManager: """ try: logging.info(f"Check if file exists: {file_path}") - if os.path.exists(file_path): - logging.info(f"The file '{file_path}' exists.") - return True - - else: - return False + return os.path.exists(file_path) except Exception as e: logging.error(f"An error occurred while checking file existence: {e}") return False - class InternManager(): def format_file_size(self, size_bytes: float) -> str: @@ -296,7 +318,7 @@ class InternManager(): while True: try: httpx.get("https://www.google.com") - console.log("[bold green]Internet is available![/bold green]") + #console.log("[bold green]Internet is available![/bold green]") break except urllib.error.URLError: @@ -374,21 +396,20 @@ class OsSummary: try: import requests - console.print(f"{filename} not found locally. Downloading from {url}...", style="bold yellow") + logging.info(f"{filename} not found locally. Downloading from {url}...") response = requests.get(url) if response.status_code == 200: with open(filename, 'wb') as f: f.write(response.content) - console.print(f"{filename} successfully downloaded.", style="bold green") - + else: - console.print(f"Failed to download {filename}. HTTP Status code: {response.status_code}", style="bold red") - sys.exit(1) + logging.error(f"Failed to download {filename}. HTTP Status code: {response.status_code}") + sys.exit(0) except Exception as e: - console.print(f"Failed to download {filename}: {e}", style="bold red") - sys.exit(1) + logging.error(f"Failed to download {filename}: {e}") + sys.exit(0) def install_library(self, lib_name: str): """ diff --git a/StreamingCommunity/Util/table.py b/StreamingCommunity/Util/table.py index 6e94090..53a9001 100644 --- a/StreamingCommunity/Util/table.py +++ b/StreamingCommunity/Util/table.py @@ -83,7 +83,57 @@ class TVShowManager: row_data = [entry.get(col_name, '') for col_name in self.column_info.keys()] table.add_row(*row_data) - self.console.print(table) # Use self.console.print instead of print + self.console.print(table) + + def run_back_command(self, research_func: dict): + """ + Executes a back-end search command by dynamically importing a module and invoking its search function. + + Args: + research_func (dict): A dictionary containing: + - 'folder' (str): The absolute path to the directory containing the module to be executed. + """ + try: + + # Get site name from folder + site_name = (os.path.basename(research_func['folder'])) + + # Find the project root directory + current_path = research_func['folder'] + while not os.path.exists(os.path.join(current_path, 'StreamingCommunity')): + current_path = os.path.dirname(current_path) + + # Add project root to Python path + project_root = current_path + #print(f"[DEBUG] Project Root: {project_root}") + + if project_root not in sys.path: + sys.path.insert(0, project_root) + + # Import using full absolute import + module_path = f'StreamingCommunity.Api.Site.{site_name}' + #print(f"[DEBUG] Importing module: {module_path}") + + # Import the module + module = importlib.import_module(module_path) + + # Get the search function + search_func = getattr(module, 'search') + + # Call the search function with the search string + search_func(None) + + except Exception as e: + self.console.print(f"[red]Error during search: {e}") + + # Print detailed traceback + import traceback + traceback.print_exc() + + # Optionally remove the path if you want to clean up + if project_root in sys.path: + sys.path.remove(project_root) + def run(self, force_int_input: bool = False, max_int_input: int = 0) -> str: """ @@ -114,7 +164,7 @@ class TVShowManager: # Handling user input for loading more items or quitting if self.slice_end < total_items: - self.console.print(f"\n\n[yellow][INFO] [green]Press [red]Enter [green]for next page, [red]'q' [green]to quit, or [red]'back' [green]to search.") + self.console.print(f"\n[green]Press [red]Enter [green]for next page, [red]'q' [green]to quit, or [red]'back' [green]to search.") if not force_int_input: key = Prompt.ask( @@ -139,49 +189,14 @@ class TVShowManager: self.slice_end = total_items elif key.lower() == "back" and research_func: - try: - # Find the project root directory - current_path = research_func['folder'] - while not os.path.exists(os.path.join(current_path, 'StreamingCommunity')): - current_path = os.path.dirname(current_path) - - # Add project root to Python path - project_root = current_path - #print(f"[DEBUG] Project Root: {project_root}") - - if project_root not in sys.path: - sys.path.insert(0, project_root) - - # Import using full absolute import - module_path = 'StreamingCommunity.Api.Site.streamingcommunity' - #print(f"[DEBUG] Importing module: {module_path}") - - # Import the module - module = importlib.import_module(module_path) - - # Get the search function - search_func = getattr(module, 'media_search_manager') - - # Call the search function with the search string - search_func(None) - - except Exception as e: - self.console.print(f"[red]Error during search: {e}") - - # Print detailed traceback - import traceback - traceback.print_exc() - - # Optionally remove the path if you want to clean up - if project_root in sys.path: - sys.path.remove(project_root) + self.run_back_command(research_func) else: break else: # Last slice, ensure all remaining items are shown - self.console.print(f"\n\n[yellow][INFO] [green]You've reached the end. [red]Enter [green]for first page, [red]'q' [green]to quit, or [red]'back' [green]to search.") + self.console.print(f"\n [green]You've reached the end. [red]Enter [green]for first page, [red]'q' [green]to quit, or [red]'back' [green]to search.") if not force_int_input: key = Prompt.ask( "\n[cyan]Insert media index [yellow](e.g., 1), [red]* [cyan]to download all media, " @@ -203,42 +218,7 @@ class TVShowManager: self.slice_end = self.step elif key.lower() == "back" and research_func: - try: - # Find the project root directory - current_path = research_func['folder'] - while not os.path.exists(os.path.join(current_path, 'StreamingCommunity')): - current_path = os.path.dirname(current_path) - - # Add project root to Python path - project_root = current_path - #print(f"[DEBUG] Project Root: {project_root}") - - if project_root not in sys.path: - sys.path.insert(0, project_root) - - # Import using full absolute import - module_path = 'StreamingCommunity.Api.Site.streamingcommunity' - #print(f"[DEBUG] Importing module: {module_path}") - - # Import the module - module = importlib.import_module(module_path) - - # Get the search function - search_func = getattr(module, 'search') - - # Call the search function with the search string - search_func(None) - - except Exception as e: - self.console.print(f"[red]Error during search: {e}") - - # Print detailed traceback - import traceback - traceback.print_exc() - - # Optionally remove the path if you want to clean up - if project_root in sys.path: - sys.path.remove(project_root) + self.run_back_command(research_func) else: break diff --git a/StreamingCommunity/__init__.py b/StreamingCommunity/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/config.json b/config.json index ac1090b..1d66c03 100644 --- a/config.json +++ b/config.json @@ -26,11 +26,11 @@ "M3U8_DOWNLOAD": { "tqdm_delay": 0.01, "tqdm_use_large_bar": true, + "default_video_workser": 12, + "default_audio_workser": 12, "download_video": true, "download_audio": true, "merge_audio": true, - "default_video_workser": 12, - "default_audio_workser": 12, "specific_list_audio": [ "ita" ], @@ -56,10 +56,10 @@ }, "SITE": { "streamingcommunity": { - "domain": "family" + "domain": "prof" }, "altadefinizione": { - "domain": "now" + "domain": "deal" }, "guardaserie": { "domain": "academy"