This commit is contained in:
Lovi 2025-01-23 10:50:43 +01:00
parent fd0add424d
commit 3b8af89c35
20 changed files with 367 additions and 193 deletions

3
.gitignore vendored
View File

@ -47,5 +47,4 @@ venv.bak/
Video
note.txt
list_proxy.txt
cmd.txt
downloaded_files
cmd.txt

View File

@ -9,9 +9,6 @@
<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>
@ -21,17 +18,11 @@
</p>
<p align="center">
<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://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"/>
<img src="https://img.shields.io/pypi/dw/streamingcommunity?style=for-the-badge" alt="PyPI Downloads"/>
</a>
</p>
@ -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/) | ✅ |

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

146
Test/Util/oss.py Normal file
View File

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

View File

@ -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)
print("\n------------------------------------")
time.sleep(1)

View File

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