diff --git a/.gitignore b/.gitignore
index af1db6a..fb8905f 100644
--- a/.gitignore
+++ b/.gitignore
@@ -47,5 +47,4 @@ venv.bak/
Video
note.txt
list_proxy.txt
-cmd.txt
-downloaded_files
\ No newline at end of file
+cmd.txt
\ No newline at end of file
diff --git a/README.md b/README.md
index 8cbfa2c..adcf1b2 100644
--- a/README.md
+++ b/README.md
@@ -9,9 +9,6 @@
-
-
-
@@ -21,17 +18,11 @@
+
+
+
-
-
-
-
-
-
-
-
-
-
+
@@ -477,10 +468,10 @@ The `run-container` command mounts also the `config.json` file, so any change to
| Website | Status |
|:-------------------|:------:|
| [1337xx](https://1337xx.to/) | ✅ |
-| [Altadefinizione](https://altadefinizione.florist/) | ✅ |
+| [AltadefinizioneGratis](https://altadefinizionegratis.info/) | ✅ |
| [AnimeUnity](https://animeunity.so/) | ✅ |
| [Ilcorsaronero](https://ilcorsaronero.link/) | ✅ |
-| [CB01New](https://cb01new.lol/) | ✅ |
+| [CB01New](https://cb01new.video/) | ✅ |
| [DDLStreamItaly](https://ddlstreamitaly.co/) | ✅ |
| [GuardaSerie](https://guardaserie.academy/) | ✅ |
| [MostraGuarda](https://mostraguarda.stream/) | ✅ |
diff --git a/StreamingCommunity/Api/Site/altadefinizione/__init__.py b/StreamingCommunity/Api/Site/altadefinizionegratis/__init__.py
similarity index 100%
rename from StreamingCommunity/Api/Site/altadefinizione/__init__.py
rename to StreamingCommunity/Api/Site/altadefinizionegratis/__init__.py
diff --git a/StreamingCommunity/Api/Site/altadefinizione/costant.py b/StreamingCommunity/Api/Site/altadefinizionegratis/costant.py
similarity index 100%
rename from StreamingCommunity/Api/Site/altadefinizione/costant.py
rename to StreamingCommunity/Api/Site/altadefinizionegratis/costant.py
diff --git a/StreamingCommunity/Api/Site/altadefinizione/film.py b/StreamingCommunity/Api/Site/altadefinizionegratis/film.py
similarity index 100%
rename from StreamingCommunity/Api/Site/altadefinizione/film.py
rename to StreamingCommunity/Api/Site/altadefinizionegratis/film.py
diff --git a/StreamingCommunity/Api/Site/altadefinizione/site.py b/StreamingCommunity/Api/Site/altadefinizionegratis/site.py
similarity index 100%
rename from StreamingCommunity/Api/Site/altadefinizione/site.py
rename to StreamingCommunity/Api/Site/altadefinizionegratis/site.py
diff --git a/StreamingCommunity/Api/Site/streamingcommunity/series.py b/StreamingCommunity/Api/Site/streamingcommunity/series.py
index 11755de..afb2211 100644
--- a/StreamingCommunity/Api/Site/streamingcommunity/series.py
+++ b/StreamingCommunity/Api/Site/streamingcommunity/series.py
@@ -199,7 +199,7 @@ def display_episodes_list(scrape_serie) -> str:
# Run the table and handle user input
last_command = table_show_manager.run()
- if last_command == "q":
+ if last_command == "q" or last_command == "quit":
console.print("\n[red]Quit [white]...")
sys.exit(0)
diff --git a/StreamingCommunity/Api/Site/streamingcommunity/site.py b/StreamingCommunity/Api/Site/streamingcommunity/site.py
index ba75c2a..4ca9fc6 100644
--- a/StreamingCommunity/Api/Site/streamingcommunity/site.py
+++ b/StreamingCommunity/Api/Site/streamingcommunity/site.py
@@ -1,6 +1,5 @@
# 10.12.23
-import sys
import json
import logging
import secrets
@@ -83,8 +82,12 @@ def get_version_and_domain():
if not disable_searchDomain:
domain_to_use, base_url = search_domain(SITE_NAME, f"https://{SITE_NAME}.{DOMAIN_NOW}")
- version = get_version(domain_to_use)
-
+ try:
+ version = get_version(domain_to_use)
+ except:
+ console.print("[green]Auto generate version ...")
+ version = secrets.token_hex(32 // 2)
+
return version, domain_to_use
diff --git a/StreamingCommunity/Api/Template/Util/get_domain.py b/StreamingCommunity/Api/Template/Util/get_domain.py
index ad5bd58..bfa86ee 100644
--- a/StreamingCommunity/Api/Template/Util/get_domain.py
+++ b/StreamingCommunity/Api/Template/Util/get_domain.py
@@ -2,7 +2,6 @@
import ssl
import time
-import certifi
from urllib.parse import urlparse, unquote
@@ -20,14 +19,14 @@ from StreamingCommunity.Util._jsonConfig import config_manager
base_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',
- 'cache-control': 'max-age=0',
'dnt': '1',
'priority': 'u=0, i',
+ 'referer': '',
'sec-ch-ua-mobile': '?0',
'sec-ch-ua-platform': '"Windows"',
'sec-fetch-dest': 'document',
'sec-fetch-mode': 'navigate',
- 'sec-fetch-site': 'none',
+ 'sec-fetch-site': 'same-origin',
'sec-fetch-user': '?1',
'upgrade-insecure-requests': '1',
'user-agent': ''
@@ -84,6 +83,8 @@ def validate_url(url, base_url, max_timeout, max_retries=3, sleep=3):
# Verify URL structure matches base_url structure
base_domain = get_base_domain(base_url)
url_domain = get_base_domain(url)
+
+ base_headers['referer'] = url
base_headers['user-agent'] = get_headers()
if base_domain != url_domain:
@@ -98,7 +99,7 @@ def validate_url(url, base_url, max_timeout, max_retries=3, sleep=3):
return False, None
client = httpx.Client(
- verify=certifi.where(),
+ verify=False,
headers=base_headers,
timeout=max_timeout
)
diff --git a/StreamingCommunity/Api/Template/site.py b/StreamingCommunity/Api/Template/site.py
index e4121c2..30aa5d9 100644
--- a/StreamingCommunity/Api/Template/site.py
+++ b/StreamingCommunity/Api/Template/site.py
@@ -74,7 +74,7 @@ def get_select_title(table_show_manager, media_search_manager):
table_show_manager.clear()
# Handle user's quit command
- if last_command == "q":
+ if last_command == "q" or last_command == "quit":
console.print("\n[red]Quit [white]...")
sys.exit(0)
diff --git a/StreamingCommunity/Lib/TMBD/tmdb.py b/StreamingCommunity/Lib/TMBD/tmdb.py
index 271bcc3..9539aa3 100644
--- a/StreamingCommunity/Lib/TMBD/tmdb.py
+++ b/StreamingCommunity/Lib/TMBD/tmdb.py
@@ -75,7 +75,7 @@ def get_select_title(table_show_manager, generic_obj):
table_show_manager.clear()
# Handle user's quit command
- if last_command == "q":
+ if last_command == "q" or last_command == "quit":
Console.print("\n[red]Quit [white]...")
sys.exit(0)
diff --git a/StreamingCommunity/Upload/version.py b/StreamingCommunity/Upload/version.py
index 956341d..e8f4c86 100644
--- a/StreamingCommunity/Upload/version.py
+++ b/StreamingCommunity/Upload/version.py
@@ -1,5 +1,5 @@
__title__ = 'StreamingCommunity'
-__version__ = '2.3.0'
+__version__ = '2.4.0'
__author__ = 'Lovi-0'
__description__ = 'A command-line program to download film'
__copyright__ = 'Copyright 2024'
diff --git a/StreamingCommunity/Util/ffmpeg_installer.py b/StreamingCommunity/Util/ffmpeg_installer.py
index 1f2986c..bc34db4 100644
--- a/StreamingCommunity/Util/ffmpeg_installer.py
+++ b/StreamingCommunity/Util/ffmpeg_installer.py
@@ -19,6 +19,8 @@ from rich.progress import Progress, SpinnerColumn, BarColumn, TextColumn, TimeRe
# Variable
console = Console()
+
+# https://github.com/eugeneware/ffmpeg-static/releases
FFMPEG_CONFIGURATION = {
'windows': {
'base_dir': lambda home: os.path.join(os.path.splitdrive(home)[0] + os.path.sep, 'binary'),
@@ -28,13 +30,13 @@ FFMPEG_CONFIGURATION = {
},
'darwin': {
'base_dir': lambda home: os.path.join(home, 'Applications', 'binary'),
- 'download_url': 'https://evermeet.cx/ffmpeg/ffmpeg-{version}.zip',
+ 'download_url': 'https://github.com/eugeneware/ffmpeg-static/releases/download/b{version}/ffmpeg-macOS-{arch}.zip',
'file_extension': '.zip',
'executables': ['ffmpeg', 'ffprobe', 'ffplay']
},
'linux': {
'base_dir': lambda home: os.path.join(home, '.local', 'bin', 'binary'),
- 'download_url': 'https://johnvansickle.com/ffmpeg/releases/ffmpeg-release-{arch}-static.tar.xz',
+ 'download_url': 'https://github.com/eugeneware/ffmpeg-static/releases/download/b{version}/ffmpeg-linux-{arch}.tar.xz',
'file_extension': '.tar.xz',
'executables': ['ffmpeg', 'ffprobe', 'ffplay']
}
@@ -150,19 +152,26 @@ class FFMPEGDownloader:
def _get_latest_version(self) -> Optional[str]:
"""
- Get the latest FFmpeg version from the official website.
+ Get the latest FFmpeg version from the GitHub releases page.
Returns:
- Optional[str]: The latest version string, or None if retrieval fails
+ Optional[str]: The latest version string, or None if retrieval fails.
Raises:
- requests.exceptions.RequestException: If there are network-related errors
+ requests.exceptions.RequestException: If there are network-related errors.
"""
try:
- version_url = 'https://www.gyan.dev/ffmpeg/builds/release-version'
- return requests.get(version_url).text.strip()
+ # Use GitHub API to fetch the latest release
+ response = requests.get(
+ 'https://api.github.com/repos/eugeneware/ffmpeg-static/releases/latest'
+ )
+ response.raise_for_status()
+ latest_release = response.json()
+
+ # Extract the tag name or version from the release
+ return latest_release.get('tag_name')
except Exception as e:
- logging.error(f"Unable to get version: {e}")
+ logging.error(f"Unable to get version from GitHub: {e}")
return None
def _download_file(self, url: str, destination: str) -> bool:
diff --git a/StreamingCommunity/Util/logger.py b/StreamingCommunity/Util/logger.py
index 0e5f3d0..35c13eb 100644
--- a/StreamingCommunity/Util/logger.py
+++ b/StreamingCommunity/Util/logger.py
@@ -1,5 +1,6 @@
# 26.03.24
+import os
import logging
from logging.handlers import RotatingFileHandler
@@ -26,6 +27,7 @@ class Logger:
# Configure file logging if debug mode and logging to file are both enabled
if self.log_to_file:
+ self.remove_existing_log_file()
self.configure_file_logging()
else:
@@ -51,3 +53,10 @@ class Logger:
formatter = logging.Formatter('[%(filename)s:%(lineno)s - %(funcName)20s() ] %(asctime)s - %(levelname)s - %(message)s')
file_handler.setFormatter(formatter)
logging.getLogger('').addHandler(file_handler)
+
+ def remove_existing_log_file(self):
+ """
+ Remove the log file if it already exists.
+ """
+ if os.path.exists(self.log_file):
+ os.remove(self.log_file)
\ No newline at end of file
diff --git a/StreamingCommunity/Util/os.py b/StreamingCommunity/Util/os.py
index 46281a9..70c88eb 100644
--- a/StreamingCommunity/Util/os.py
+++ b/StreamingCommunity/Util/os.py
@@ -8,16 +8,16 @@ import shutil
import hashlib
import logging
import platform
-import unidecode
import subprocess
import contextlib
-import pathvalidate
import urllib.request
import importlib.metadata
# External library
import httpx
+from unidecode import unidecode
+from pathvalidate import sanitize_filename, sanitize_filepath
# Internal utilities
@@ -25,172 +25,133 @@ from .ffmpeg_installer import check_ffmpeg
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': []
- }
-}
-
-
class OsManager:
def __init__(self):
self.system = self._detect_system()
- self.config = OS_CONFIGURATIONS.get(self.system, {})
+ self.max_length = self._get_max_length()
def _detect_system(self) -> str:
"""Detect and normalize operating system name."""
system = platform.system().lower()
+ if system not in ['windows', 'darwin', 'linux']:
+ raise ValueError(f"Unsupported operating system: {system}")
+ return system
- if system in OS_CONFIGURATIONS:
- return system
-
- raise ValueError(f"Unsupported operating system: {system}")
+ def _get_max_length(self) -> int:
+ """Get max filename length based on OS."""
+ return 255 if self.system == 'windows' else 4096
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':
+ """Normalize Windows paths."""
+ if not path or self.system != 'windows':
return path
-
- # Check if path starts with a drive letter
+
+ # Preserve network paths (UNC and IP-based)
+ if path.startswith('\\\\') or path.startswith('//'):
+ return path.replace('/', '\\')
+
+ # Handle drive letters
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.
-
- Args:
- filename (str): Original filename.
-
- Returns:
- str: Processed filename.
- """
- name, ext = os.path.splitext(filename)
-
- # Handle length restrictions
- if len(name) > self.config['max_length']:
- name = self._truncate_filename(name)
-
- # Reconstruct filename
- processed_filename = name + ext
-
- return processed_filename
+ rest = path[2:].replace('/', '\\').lstrip('\\')
+ return f"{drive}\\{rest}"
- def _truncate_filename(self, name: str) -> str:
- """
- Truncate filename based on OS-specific rules.
-
- Args:
- name (str): Original filename.
-
- Returns:
- str: Truncated filename.
- """
- logging.info("_truncate_filename: ", name)
+ return path.replace('/', '\\')
- if self.system == 'windows':
- return name[:self.config['max_length'] - 3] + '___'
- elif self.system == 'darwin':
- return name[:self.config['max_length']]
- elif self.system == 'linux':
- return name[:self.config['max_length'] - 2] + '___'
+ def _normalize_mac_path(self, path: str) -> str:
+ """Normalize macOS paths."""
+ if not path or self.system != 'darwin':
+ return path
+
+ # Convert Windows separators to Unix
+ normalized = path.replace('\\', '/')
+
+ # Ensure absolute paths start with /
+ if normalized.startswith('/'):
+ return os.path.normpath(normalized)
+
+ return normalized
def get_sanitize_file(self, filename: str) -> str:
- """
- Sanitize filename using pathvalidate with unidecode.
-
- Args:
- filename (str): Original filename.
-
- Returns:
- str: Sanitized filename.
- """
+ """Sanitize filename."""
+ if not filename:
+ return filename
- # Decode unicode characters and sanitize
- decoded_filename = unidecode.unidecode(filename)
- sanitized_filename = pathvalidate.sanitize_filename(decoded_filename)
+ # Decode and sanitize
+ decoded = unidecode(filename)
+ sanitized = sanitize_filename(decoded)
- # Truncate if necessary based on OS configuration
- name, ext = os.path.splitext(sanitized_filename)
- if len(name) > self.config['max_length']:
- name = self._truncate_filename(name)
+ # Split name and extension
+ name, ext = os.path.splitext(sanitized)
- result = name + ext
- return result
+ # Calculate available length for name considering the '...' and extension
+ max_name_length = self.max_length - len('...') - len(ext)
+
+ # Truncate name if it exceeds the max name length
+ if len(name) > max_name_length:
+ name = name[:max_name_length] + '...'
+
+ # Ensure the final file name includes the extension
+ return name + ext
def get_sanitize_path(self, path: str) -> str:
- """
- Sanitize folder path using pathvalidate with unidecode.
-
- Args:
- path (str): Original folder path.
-
- Returns:
- str: Sanitized folder path.
- """
+ """Sanitize complete path."""
+ if not path:
+ return path
- # Normalize path for Windows drive letters first
- path = self._normalize_windows_path(path)
-
- # Decode unicode characters and sanitize
- decoded_path = unidecode.unidecode(path)
- sanitized_path = pathvalidate.sanitize_filepath(decoded_path)
+ # Decode unicode characters
+ decoded = unidecode(path)
- # Split path and process each component
- path_components = os.path.normpath(sanitized_path).split(os.sep)
-
- # 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]
+ # Basic path sanitization
+ sanitized = sanitize_filepath(decoded)
+ if self.system == 'windows':
+ # Handle network paths (UNC or IP-based)
+ if path.startswith('\\\\') or path.startswith('//'):
+ parts = path.replace('/', '\\').split('\\')
+ # Keep server/IP and share name as is
+ sanitized_parts = parts[:4]
+ # Sanitize remaining parts
+ if len(parts) > 4:
+ sanitized_parts.extend([
+ self.get_sanitize_file(part)
+ for part in parts[4:]
+ if part
+ ])
+ return '\\'.join(sanitized_parts)
+
+ # Handle drive letters
+ elif len(path) >= 2 and path[1] == ':':
+ drive = path[:2]
+ rest = path[2:].lstrip('\\').lstrip('/')
+ path_parts = [drive] + [
+ self.get_sanitize_file(part)
+ for part in rest.replace('/', '\\').split('\\')
+ if part
+ ]
+ return '\\'.join(path_parts)
+
+ # Regular path
+ else:
+ parts = path.replace('/', '\\').split('\\')
+ return '\\'.join(p for p in parts if p)
else:
- processed_components = []
+ # Handle Unix-like paths (Linux and macOS)
+ is_absolute = path.startswith('/')
+ parts = path.replace('\\', '/').split('/')
+ sanitized_parts = [
+ self.get_sanitize_file(part)
+ for part in parts
+ if part
+ ]
+
+ result = '/'.join(sanitized_parts)
+ if is_absolute:
+ result = '/' + result
+
+ return result
- # 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:
"""
Create directory path with specified permissions.
@@ -272,6 +233,7 @@ class OsManager:
logging.error(f"An error occurred while checking file existence: {e}")
return False
+
class InternManager():
def format_file_size(self, size_bytes: float) -> str:
diff --git a/StreamingCommunity/Util/table.py b/StreamingCommunity/Util/table.py
index 53a9001..65190c2 100644
--- a/StreamingCommunity/Util/table.py
+++ b/StreamingCommunity/Util/table.py
@@ -174,12 +174,12 @@ class TVShowManager:
else:
choices = [str(i) for i in range(0, max_int_input)]
- choices.extend(["q", "", "back"])
+ choices.extend(["q", "quit", "b", "back"])
key = Prompt.ask("[cyan]Insert media [red]index", choices=choices, show_choices=False)
last_command = key
- if key.lower() == "q":
+ if key.lower() == "q" or key.lower() == "quit":
break
elif key == "":
@@ -188,7 +188,7 @@ class TVShowManager:
if self.slice_end > total_items:
self.slice_end = total_items
- elif key.lower() == "back" and research_func:
+ elif (key.lower() == "b" or key.lower() == "back") and research_func:
self.run_back_command(research_func)
else:
@@ -205,19 +205,19 @@ class TVShowManager:
else:
choices = [str(i) for i in range(0, max_int_input)]
- choices.extend(["q", "", "back"])
+ choices.extend(["q", "quit", "b", "back"])
key = Prompt.ask("[cyan]Insert media [red]index", choices=choices, show_choices=False)
last_command = key
- if key.lower() == "q":
+ if key.lower() == "q" or key.lower() == "quit":
break
elif key == "":
self.slice_start = 0
self.slice_end = self.step
- elif key.lower() == "back" and research_func:
+ elif (key.lower() == "b" or key.lower() == "back") and research_func:
self.run_back_command(research_func)
else:
diff --git a/StreamingCommunity/run.py b/StreamingCommunity/run.py
index 8e30799..f1a616e 100644
--- a/StreamingCommunity/run.py
+++ b/StreamingCommunity/run.py
@@ -125,7 +125,6 @@ def initialize():
def main():
-
start = time.time()
# Create logger
@@ -136,9 +135,39 @@ def main():
search_functions = load_search_functions()
logging.info(f"Load module in: {time.time() - start} s")
- # Create dynamic argument parser
- parser = argparse.ArgumentParser(description='Script to download film and series from the internet.')
+ # Create argument parser
+ parser = argparse.ArgumentParser(
+ description='Script to download movies and series from the internet. Use these commands to configure the script and control its behavior.'
+ )
+ # Add arguments for the main configuration parameters
+ parser.add_argument(
+ '--add_siteName', type=bool, help='Enable or disable adding the site name to the file name (e.g., true/false).'
+ )
+ parser.add_argument(
+ '--disable_searchDomain', type=bool, help='Enable or disable searching in configured domains (e.g., true/false).'
+ )
+ parser.add_argument(
+ '--not_close', type=bool, help='If set to true, the script will not close the console after execution (e.g., true/false).'
+ )
+
+ # Add arguments for M3U8 configuration
+ parser.add_argument(
+ '--default_video_worker', type=int, help='Number of workers for video during M3U8 download (default: 12).'
+ )
+ parser.add_argument(
+ '--default_audio_worker', type=int, help='Number of workers for audio during M3U8 download (default: 12).'
+ )
+
+ # Add options for audio and subtitles
+ parser.add_argument(
+ '--specific_list_audio', type=str, help='Comma-separated list of specific audio languages to download (e.g., ita,eng).'
+ )
+ parser.add_argument(
+ '--specific_list_subtitles', type=str, help='Comma-separated list of specific subtitle languages to download (e.g., eng,spa).'
+ )
+
+ # Add arguments for search functions
color_map = {
"anime": "red",
"film_serie": "yellow",
@@ -153,10 +182,35 @@ def main():
long_option = alias
parser.add_argument(f'-{short_option}', f'--{long_option}', action='store_true', help=f'Search for {alias.split("_")[0]} on streaming platforms.')
- # Parse command line arguments
+ # Parse command-line arguments
args = parser.parse_args()
- # Mapping command-line arguments to functions
+ # Map command-line arguments to the config values
+ config_updates = {}
+
+ if args.add_siteName is not None:
+ config_updates['DEFAULT.add_siteName'] = args.add_siteName
+ if args.disable_searchDomain is not None:
+ config_updates['DEFAULT.disable_searchDomain'] = args.disable_searchDomain
+ if args.not_close is not None:
+ config_updates['DEFAULT.not_close'] = args.not_close
+ if args.default_video_worker is not None:
+ config_updates['M3U8_DOWNLOAD.default_video_worker'] = args.default_video_worker
+ if args.default_audio_worker is not None:
+ config_updates['M3U8_DOWNLOAD.default_audio_worker'] = args.default_audio_worker
+ if args.specific_list_audio is not None:
+ config_updates['M3U8_DOWNLOAD.specific_list_audio'] = args.specific_list_audio.split(',')
+ if args.specific_list_subtitles is not None:
+ config_updates['M3U8_DOWNLOAD.specific_list_subtitles'] = args.specific_list_subtitles.split(',')
+
+ # Apply the updates to the config file
+ for key, value in config_updates.items():
+ section, option = key.split('.')
+ config_manager.set_key(section, option, value)
+
+ config_manager.write_config()
+
+ # Map command-line arguments to functions
arg_to_function = {alias: func for alias, (func, _) in search_functions.items()}
# Check which argument is provided and run the corresponding function
@@ -188,4 +242,4 @@ def main():
run_function(input_to_function[category])
else:
console.print("[red]Invalid category.")
- sys.exit(0)
+ sys.exit(0)
\ No newline at end of file
diff --git a/Test/Util/oss.py b/Test/Util/oss.py
new file mode 100644
index 0000000..df5318e
--- /dev/null
+++ b/Test/Util/oss.py
@@ -0,0 +1,146 @@
+# 22.01.25
+
+# Fix import
+import sys
+import os
+src_path = os.path.abspath(os.path.join(os.path.dirname(__file__), '..', '..'))
+sys.path.append(src_path)
+
+
+
+# Import
+import unittest
+from unittest.mock import patch
+from StreamingCommunity.Util.os import OsManager
+
+class TestOsManager(unittest.TestCase):
+ def setUp(self):
+ self.test_paths = {
+ 'windows': {
+ 'network': [
+ (r'\\server\share\folder\file.txt', r'\\server\share\folder\file.txt'),
+ (r'\\192.168.1.100\share\folder\file.txt', r'\\192.168.1.100\share\folder\file.txt'),
+ (r'\\server\share', r'\\server\share'),
+ (r'\\server\share\\folder//subfolder\file.txt', r'\\server\share\folder\subfolder\file.txt')
+ ],
+ 'drive': [
+ ('C:\\folder\\file.txt', 'C:\\folder\\file.txt'),
+ ('C:/folder/file.txt', 'C:\\folder\\file.txt'),
+ ('D:\\Test\\file.txt', 'D:\\Test\\file.txt'),
+ ('D:/Test/file.txt', 'D:\\Test\\file.txt')
+ ],
+ 'relative': [
+ ('folder\\file.txt', 'folder\\file.txt'),
+ ('folder/file.txt', 'folder\\file.txt'),
+ ('.\\folder\\file.txt', 'folder\\file.txt')
+ ]
+ },
+ 'darwin': {
+ 'absolute': [
+ ('/media/TV/show.mp4', '/media/TV/show.mp4'),
+ ('/Users/name/Documents/file.txt', '/Users/name/Documents/file.txt'),
+ ('/media/TV/show.mp4', '/media/TV/show.mp4')
+ ],
+ 'relative': [
+ ('folder/file.txt', 'folder/file.txt'),
+ ('folder/file.txt', 'folder/file.txt')
+ ]
+ },
+ 'linux': {
+ 'absolute': [
+ ('/home/user/file.txt', '/home/user/file.txt'),
+ ('/mnt/data/file.txt', '/mnt/data/file.txt'),
+ ('/home/user/file.txt', '/home/user/file.txt')
+ ],
+ 'relative': [
+ ('folder/file.txt', 'folder/file.txt'),
+ ('folder/file.txt', 'folder/file.txt')
+ ]
+ }
+ }
+
+ def test_sanitize_file(self):
+ with patch('platform.system', return_value='Windows'):
+ manager = OsManager()
+ test_cases = [
+ ('file.txt', 'file.txt'),
+ ('filéš.txt', 'files.txt')
+ ]
+ for input_name, expected in test_cases:
+ with self.subTest(input_name=input_name):
+ result = manager.get_sanitize_file(input_name)
+ self.assertEqual(result, expected)
+
+ def test_windows_paths(self):
+ with patch('platform.system', return_value='Windows'):
+ manager = OsManager()
+
+ # Test network paths (including IP)
+ for input_path, expected in self.test_paths['windows']['network']:
+ with self.subTest(input_path=input_path):
+ result = manager.get_sanitize_path(input_path)
+ self.assertEqual(result, expected)
+
+ # Test drive paths
+ for input_path, expected in self.test_paths['windows']['drive']:
+ with self.subTest(input_path=input_path):
+ result = manager.get_sanitize_path(input_path)
+ self.assertEqual(result, expected)
+
+ def test_macos_paths(self):
+ with patch('platform.system', return_value='Darwin'):
+ manager = OsManager()
+
+ # Test absolute paths
+ for input_path, expected in self.test_paths['darwin']['absolute']:
+ with self.subTest(input_path=input_path):
+ result = manager.get_sanitize_path(input_path)
+ self.assertEqual(result, expected)
+
+ # Test relative paths
+ for input_path, expected in self.test_paths['darwin']['relative']:
+ with self.subTest(input_path=input_path):
+ result = manager.get_sanitize_path(input_path)
+ self.assertEqual(result, expected)
+
+ def test_linux_paths(self):
+ with patch('platform.system', return_value='Linux'):
+ manager = OsManager()
+
+ for input_path, expected in self.test_paths['linux']['absolute']:
+ with self.subTest(input_path=input_path):
+ result = manager.get_sanitize_path(input_path)
+ self.assertEqual(result, expected)
+
+ def test_special_characters(self):
+ with patch('platform.system', return_value='Windows'):
+ manager = OsManager()
+ special_cases = [
+ ('\\\\server\\share\\àèìòù\\file.txt', '\\\\server\\share\\aeiou\\file.txt'),
+ ('D:\\Test\\åäö\\file.txt', 'D:\\Test\\aao\\file.txt'),
+ ('\\\\192.168.1.100\\share\\tést\\file.txt', '\\\\192.168.1.100\\share\\test\\file.txt')
+ ]
+
+ for input_path, expected in special_cases:
+ with self.subTest(input_path=input_path):
+ result = manager.get_sanitize_path(input_path)
+ self.assertEqual(result, expected)
+
+ def test_network_paths_with_ip(self):
+ with patch('platform.system', return_value='Windows'):
+ manager = OsManager()
+ ip_paths = [
+ ('\\\\192.168.1.100\\share\\folder', '\\\\192.168.1.100\\share\\folder'),
+ ('\\\\10.0.0.50\\public\\data.txt', '\\\\10.0.0.50\\public\\data.txt'),
+ ('\\\\172.16.254.1\\backup\\test.txt', '\\\\172.16.254.1\\backup\\test.txt'),
+ ('\\\\192.168.1.100\\share\\folder\\sub dir\\file.txt',
+ '\\\\192.168.1.100\\share\\folder\\sub dir\\file.txt'),
+ ]
+
+ for input_path, expected in ip_paths:
+ with self.subTest(input_path=input_path):
+ result = manager.get_sanitize_path(input_path)
+ self.assertEqual(result, expected)
+
+if __name__ == '__main__':
+ unittest.main()
\ No newline at end of file
diff --git a/Test/call_updateDomain.py b/Test/call_updateDomain.py
index 0fb5d6d..68d84b9 100644
--- a/Test/call_updateDomain.py
+++ b/Test/call_updateDomain.py
@@ -122,5 +122,5 @@ if __name__ == "__main__":
domain_to_use, _ = search_domain(site_name=site_name, base_url=f"https://{site_name}.{original_domain}", get_first=True)
update_readme(alias, domain_to_use)
- print("------------------------------------")
- time.sleep(2)
\ No newline at end of file
+ print("\n------------------------------------")
+ time.sleep(1)
\ No newline at end of file
diff --git a/config.json b/config.json
index f05892a..62208e6 100644
--- a/config.json
+++ b/config.json
@@ -61,8 +61,8 @@
"streamingcommunity": {
"domain": "ooo"
},
- "altadefinizione": {
- "domain": "florist"
+ "altadefinizionegratis": {
+ "domain": "info"
},
"guardaserie": {
"domain": "academy"
@@ -82,7 +82,7 @@
"domain": "so"
},
"cb01new": {
- "domain": "lol"
+ "domain": "video"
},
"1337xx": {
"domain": "to"