mirror of
https://github.com/Arrowar/StreamingCommunity.git
synced 2025-06-05 02:55:25 +00:00
Add check for cpython, requirements
This commit is contained in:
parent
a0b8234433
commit
4aa103b4ad
@ -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):
|
||||
"""
|
||||
|
@ -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
|
||||
|
@ -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])}")
|
||||
|
||||
|
||||
|
||||
|
@ -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():
|
||||
|
Loading…
x
Reference in New Issue
Block a user