v2.0.0 - 2

This commit is contained in:
Lovi 2024-12-27 12:20:59 +01:00
parent 983089c321
commit 001c3f09e8
16 changed files with 306 additions and 251 deletions

15
.gitignore vendored
View File

@ -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

View File

@ -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

View File

@ -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 (

View File

@ -1,35 +1,64 @@
# StreamingCommunity Downloader
<p align="center">
<img src="https://i.ibb.co/PFnjvBc/immagine-2024-12-26-180318047.png" alt="Project Logo" width="700"/>
</p>
![Project Logo](https://i.ibb.co/f4h5Y2m/min-logo.png)
<p align="center">
<a href="https://pypi.org/project/streamingcommunity">
<img src="https://img.shields.io/pypi/v/streamingcommunity?logo=pypi&labelColor=555555&style=for-the-badge" alt="PyPI"/>
</a>
<a href="https://www.python.org">
<img src="https://img.shields.io/badge/Python->=3.8-3776AB?style=for-the-badge&logo=python&logoColor=white" alt="Python"/>
</a>
<a href="https://www.paypal.com/donate/?hosted_button_id=UXTWMT8P6HE2C">
<img src="https://img.shields.io/badge/_-Donate-red.svg?logo=githubsponsors&labelColor=555555&style=for-the-badge" alt="Donate"/>
</a>
<a href="https://github.com/Lovi-0/StreamingCommunity/blob/main/LICENSE">
<img src="https://img.shields.io/badge/License-GPL_3.0-blue.svg?style=for-the-badge" alt="License"/>
</a>
<a href="https://github.com/Lovi-0/StreamingCommunity/commits">
<img src="https://img.shields.io/github/commit-activity/m/Lovi-0/StreamingCommunity?label=commits&style=for-the-badge" alt="Commits"/>
</a>
<a href="https://github.com/Lovi-0/StreamingCommunity/commits">
<img src="https://img.shields.io/github/last-commit/Lovi-0/StreamingCommunity/main?label=&style=for-the-badge&display_timestamp=committer" alt="Last Commit"/>
</a>
</p>
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)
<p align="center">
<a href="https://pypi.org/project/streamingcommunity">
<img src="https://img.shields.io/pypi/dm/streamingcommunity?style=for-the-badge" alt="PyPI Downloads"/>
</a>
<a href="https://github.com/Lovi-0/StreamingCommunity/network/members">
<img src="https://img.shields.io/github/forks/Lovi-0/StreamingCommunity?style=for-the-badge" alt="Forks"/>
</a>
<a href="https://github.com/Lovi-0/StreamingCommunity">
<img src="https://img.shields.io/github/languages/code-size/Lovi-0/StreamingCommunity?style=for-the-badge" alt="Code Size"/>
</a>
<a href="https://github.com/Lovi-0/StreamingCommunity">
<img src="https://img.shields.io/github/repo-size/Lovi-0/StreamingCommunity?style=for-the-badge" alt="Repo Size"/>
</a>
</p>
# 📋 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
<a href="https://github.com/Lovi-0/StreamingCommunity/graphs/contributors" alt="View Contributors">
<img src="https://contrib.rocks/image?repo=Lovi-0/StreamingCommunity&max=1000&columns=10" alt="Contributors" />
</a>

View File

@ -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")

View File

@ -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)

View File

@ -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]

View File

@ -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"

View File

@ -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']

View File

@ -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

View File

@ -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'

View File

@ -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")

View File

@ -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):
"""

View File

@ -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

View File

View File

@ -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"