Add check for cpython, requirements

This commit is contained in:
Lovi 2024-11-28 16:30:04 +01:00
parent a0b8234433
commit 4aa103b4ad
4 changed files with 114 additions and 46 deletions

View File

@ -55,7 +55,7 @@ max_timeout = config_manager.get_int("REQUESTS", "timeout")
class M3U8_Segments:
def __init__(self, url: str, tmp_folder: str, is_index_url: bool = True):
def __init__(self, url: str, tmp_folder: str, is_index_url: bool = True, base_timeout=1.0, max_timeout=5.0):
"""
Initializes the M3U8_Segments object.
@ -81,6 +81,9 @@ class M3U8_Segments:
self.queue = PriorityQueue()
self.stop_event = threading.Event()
self.downloaded_segments = set()
self.base_timeout = base_timeout
self.max_timeout = max_timeout
self.current_timeout = base_timeout
# Stopping
self.interrupt_flag = threading.Event()
@ -226,13 +229,7 @@ class M3U8_Segments:
- progress_bar (tqdm): Progress counter for tracking download progress.
- retries (int): The number of times to retry on failure (default is 3).
- backoff_factor (float): The backoff factor for exponential backoff (default is 1.5 seconds).
"""
if self.interrupt_flag.is_set():
return
need_verify = REQUEST_VERIFY
min_segment_size = 100 # Minimum acceptable size for a TS segment in bytes
"""
for attempt in range(retries):
if self.interrupt_flag.is_set():
return
@ -247,7 +244,7 @@ class M3U8_Segments:
proxy = self.valid_proxy[index % len(self.valid_proxy)]
logging.info(f"Use proxy: {proxy}")
with httpx.Client(proxies=proxy, verify=need_verify) as client:
with httpx.Client(proxies=proxy, verify=REQUEST_VERIFY) as client:
if 'key_base_url' in self.__dict__:
response = client.get(
url=ts_url,
@ -265,7 +262,7 @@ class M3U8_Segments:
)
else:
with httpx.Client(verify=need_verify) as client_2:
with httpx.Client(verify=REQUEST_VERIFY) as client_2:
if 'key_base_url' in self.__dict__:
response = client_2.get(
url=ts_url,
@ -286,19 +283,12 @@ class M3U8_Segments:
response.raise_for_status()
segment_content = response.content
content_size = len(segment_content)
# Check if segment is too small (possibly corrupted or empty)
if content_size < min_segment_size:
raise httpx.RequestError(f"Segment {index} too small ({content_size} bytes)")
duration = time.time() - start_time
# Decrypt if needed and verify decrypted content
if self.decryption is not None:
try:
segment_content = self.decryption.decrypt(segment_content)
if len(segment_content) < min_segment_size:
raise Exception(f"Decrypted segment {index} too small ({len(segment_content)} bytes)")
except Exception as e:
logging.error(f"Decryption failed for segment {index}: {str(e)}")
@ -334,19 +324,20 @@ class M3U8_Segments:
"""
Writes segments to file with additional verification.
"""
buffer = {}
expected_index = 0
segments_written = set()
with open(self.tmp_file_path, 'wb') as f:
expected_index = 0
buffer = {}
total_written = 0
segments_written = set()
while not self.stop_event.is_set() or not self.queue.empty():
if self.interrupt_flag.is_set():
break
try:
index, segment_content = self.queue.get(timeout=1)
index, segment_content = self.queue.get(timeout=self.current_timeout)
# Successful queue retrieval: reduce timeout
self.current_timeout = max(self.base_timeout, self.current_timeout / 2)
# Handle failed segments
if segment_content is None:
@ -357,7 +348,6 @@ class M3U8_Segments:
# Write segment if it's the next expected one
if index == expected_index:
f.write(segment_content)
total_written += len(segment_content)
segments_written.add(index)
f.flush()
expected_index += 1
@ -365,26 +355,25 @@ class M3U8_Segments:
# Write any buffered segments that are now in order
while expected_index in buffer:
next_segment = buffer.pop(expected_index)
if next_segment is not None:
f.write(next_segment)
total_written += len(next_segment)
segments_written.add(expected_index)
f.flush()
expected_index += 1
else:
buffer[index] = segment_content
except queue.Empty:
self.current_timeout = min(self.max_timeout, self.current_timeout * 1.5)
if self.stop_event.is_set():
break
continue
except Exception as e:
logging.error(f"Error writing segment {index}: {str(e)}")
continue
# Final verification
if total_written == 0:
raise Exception("No data written to file")
def download_streams(self, add_desc):
"""

View File

@ -65,18 +65,21 @@ class M3U8_Ts_Estimator:
try:
io_counters = psutil.net_io_counters()
return io_counters
except Exception as e:
logging.warning(f"Unable to access network I/O counters: {e}")
return None
while True:
old_value = get_network_io()
if old_value is None: # If psutil is not available, continue with default values
time.sleep(interval)
continue
time.sleep(interval)
new_value = get_network_io()
if new_value is None: # Handle again if psutil fails in the next call
time.sleep(interval)
continue

View File

@ -23,7 +23,7 @@ import httpx
# Internal utilities
from StreamingCommunity.Src.Util.console import console
from StreamingCommunity.Src.Util.console import console, msg
# Variable
@ -318,9 +318,6 @@ class OsSummary():
Returns:
str: The version string of the executable.
Raises:
SystemExit: If the command is not found or fails to execute.
"""
try:
@ -328,7 +325,7 @@ class OsSummary():
return version_output.split(" ")[2]
except (FileNotFoundError, subprocess.CalledProcessError):
print(f"{command[0]} not found")
console.print(f"{command[0]} not found", style="bold red")
sys.exit(0)
def get_library_version(self, lib_name: str):
@ -341,7 +338,6 @@ class OsSummary():
Returns:
str: The library name followed by its version, or `-not installed` if not found.
"""
try:
version = importlib.metadata.version(lib_name)
return f"{lib_name}-{version}"
@ -349,7 +345,62 @@ class OsSummary():
except importlib.metadata.PackageNotFoundError:
return f"{lib_name}-not installed"
def get_system_summary(self):
async def download_requirements(self, url: str, filename: str):
"""
Download the requirements.txt file from the specified URL if not found locally using httpx.
Args:
url (str): The URL to download the requirements file from.
filename (str): The local filename to save the requirements file as.
"""
try:
async with httpx.AsyncClient() as client:
console.print(f"{filename} not found locally. Downloading from {url}...", style="bold yellow")
response = await client.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)
except Exception as e:
console.print(f"Failed to download {filename}: {e}", style="bold red")
sys.exit(1)
def install_library(self, lib_name: str):
"""
Install a Python library using pip.
Args:
lib_name (str): The name of the library to install.
"""
try:
console.print(f"Installing {lib_name}...", style="bold yellow")
subprocess.check_call([sys.executable, "-m", "pip", "install", lib_name])
console.print(f"{lib_name} installed successfully!", style="bold green")
except subprocess.CalledProcessError as e:
console.print(f"Failed to install {lib_name}: {e}", style="bold red")
sys.exit(1)
def check_python_version(self):
"""
Check if the installed Python is the official CPython distribution.
Exits with a message if not the official version.
"""
python_implementation = platform.python_implementation()
if python_implementation != "CPython":
console.print(f"[bold red]Warning: You are using a non-official Python distribution: {python_implementation}.[/bold red]")
console.print("Please install the official Python from [bold blue]https://www.python.org[/bold blue] and try again.", style="bold yellow")
sys.exit(0)
async def get_system_summary(self):
"""
Generate a summary of the system environment.
@ -360,6 +411,9 @@ class OsSummary():
- Installed Python libraries as listed in `requirements.txt`.
"""
# Check if Python is the official CPython
self.check_python_version()
# Check internet connectivity
InternManager().check_internet()
console.print("[bold blue]System Summary[/bold blue][white]:")
@ -381,12 +435,32 @@ class OsSummary():
console.print(f"[cyan]Exe versions[white]: [bold red]ffmpeg {ffmpeg_version}, ffprobe {ffprobe_version}[/bold red]")
logging.info(f"Dependencies: ffmpeg {ffmpeg_version}, ffprobe {ffprobe_version}")
# Optional libraries versions
"""optional_libraries = [line.strip() for line in open('requirements.txt', 'r', encoding='utf-8-sig')]
optional_libs_versions = [self.get_library_version(lib) for lib in optional_libraries]
# Check if requirements.txt exists, if not download it
requirements_file = 'requirements.txt'
if not os.path.exists(requirements_file):
await self.download_requirements(
'https://raw.githubusercontent.com/Lovi-0/StreamingCommunity/refs/heads/main/requirements.txt',
requirements_file
)
# Read the optional libraries from the requirements file
optional_libraries = [line.strip() for line in open(requirements_file, 'r', encoding='utf-8-sig')]
console.print(f"[cyan]Libraries[white]: [bold red]{', '.join(optional_libs_versions)}[/bold red]\n")
logging.info(f"Libraries: {', '.join(optional_libs_versions)}")"""
# Check if libraries are installed and prompt to install missing ones
for lib in optional_libraries:
installed_version = self.get_library_version(lib)
if 'not installed' in installed_version:
# Prompt user to install missing library using Prompt.ask()
user_response = msg.ask(f"{lib} is not installed. Do you want to install it? (yes/no)", default="y")
if user_response.lower().strip() in ["yes", "y"]:
self.install_library(lib)
else:
#console.print(f"[cyan]Library[white]: [bold red]{installed_version}[/bold red]")
logging.info(f"Library: {installed_version}")
console.print(f"[cyan]Libraries[white]: [bold red]{', '.join([self.get_library_version(lib) for lib in optional_libraries])}[/bold red]\n")
logging.info(f"Libraries: {', '.join([self.get_library_version(lib) for lib in optional_libraries])}")

View File

@ -4,6 +4,7 @@ import os
import sys
import time
import glob
import asyncio
import logging
import platform
import argparse
@ -16,7 +17,7 @@ from StreamingCommunity.Src.Util.message import start_message
from StreamingCommunity.Src.Util.console import console, msg
from StreamingCommunity.Src.Util._jsonConfig import config_manager
from StreamingCommunity.Src.Upload.update import update as git_update
from StreamingCommunity.Src.Util.os import os_summary
from StreamingCommunity.Src.Util.os import OsSummary
from StreamingCommunity.Src.Lib.TMBD import tmdb
from StreamingCommunity.Src.Util.logger import Logger
@ -103,7 +104,8 @@ def initialize():
start_message()
# Get system info
os_summary.get_system_summary()
os_summary = OsSummary()
asyncio.run(os_summary.get_system_summary())
# Set terminal size for win 7
if platform.system() == "Windows" and "7" in platform.version():