Fix signal handler mp4

This commit is contained in:
Lovi 2025-02-13 10:41:59 +01:00
parent 2f8d706b59
commit 8ce68479ae
2 changed files with 45 additions and 39 deletions

View File

@ -3,6 +3,7 @@
import os import os
import re import re
import sys import sys
import time
import signal import signal
import logging import logging
from functools import partial from functools import partial
@ -39,21 +40,38 @@ TELEGRAM_BOT = config_manager.get_bool('DEFAULT', 'telegram_bot')
def signal_handler(signum, frame, kill_handler): class InterruptHandler:
"""Signal handler for graceful interruption""" def __init__(self):
kill_handler[0] = True self.interrupt_count = 0
print("\nReceived interrupt signal. Completing current download...") self.last_interrupt_time = 0
self.kill_download = False
self.force_quit = False
def signal_handler(signum, frame, interrupt_handler, original_handler):
"""Enhanced signal handler for multiple interrupt scenarios"""
current_time = time.time()
# Reset counter if more than 2 seconds have passed since last interrupt
if current_time - interrupt_handler.last_interrupt_time > 2:
interrupt_handler.interrupt_count = 0
interrupt_handler.interrupt_count += 1
interrupt_handler.last_interrupt_time = current_time
if interrupt_handler.interrupt_count == 1:
interrupt_handler.kill_download = True
console.print("\n[bold yellow]First interrupt received. Download will complete and save. Press Ctrl+C three times quickly to force quit.[/bold yellow]")
elif interrupt_handler.interrupt_count >= 3:
interrupt_handler.force_quit = True
console.print("\n[bold red]Force quit activated. Saving partial download...[/bold red]")
signal.signal(signum, original_handler)
def MP4_downloader(url: str, path: str, referer: str = None, headers_: dict = None): def MP4_downloader(url: str, path: str, referer: str = None, headers_: dict = None):
""" """
Downloads an MP4 video from a given URL with robust error handling and SSL bypass. Downloads an MP4 video with enhanced interrupt handling.
- Single Ctrl+C: Completes download gracefully
Parameters: - Triple Ctrl+C: Saves partial download and exits
- url (str): The URL of the MP4 video to download.
- path (str): The local path where the downloaded MP4 file will be saved.
- referer (str, optional): The referer header value.
- headers_ (dict, optional): Custom headers for the request.
""" """
if TELEGRAM_BOT: if TELEGRAM_BOT:
bot = get_bot_instance() bot = get_bot_instance()
@ -65,23 +83,19 @@ def MP4_downloader(url: str, path: str, referer: str = None, headers_: dict = No
bot.send_message(f"Contenuto già scaricato!", None) bot.send_message(f"Contenuto già scaricato!", None)
return 400 return 400
# Early return for link-only mode
if GET_ONLY_LINK: if GET_ONLY_LINK:
return {'path': path, 'url': url} return {'path': path, 'url': url}
# Validate URL
if not (url.lower().startswith('http://') or url.lower().startswith('https://')): if not (url.lower().startswith('http://') or url.lower().startswith('https://')):
logging.error(f"Invalid URL: {url}") logging.error(f"Invalid URL: {url}")
console.print(f"[bold red]Invalid URL: {url}[/bold red]") console.print(f"[bold red]Invalid URL: {url}[/bold red]")
return None return None
# Prepare headers
try: try:
headers = {} headers = {}
if referer: if referer:
headers['Referer'] = referer headers['Referer'] = referer
# Use custom headers if provided, otherwise use default user agent
if headers_: if headers_:
headers.update(headers_) headers.update(headers_)
else: else:
@ -93,17 +107,12 @@ def MP4_downloader(url: str, path: str, referer: str = None, headers_: dict = No
return None return None
temp_path = f"{path}.temp" temp_path = f"{path}.temp"
kill_handler = [False] # Using list for mutable state interrupt_handler = InterruptHandler()
original_handler = signal.signal(signal.SIGINT, partial(signal_handler, kill_handler=kill_handler)) original_handler = signal.signal(signal.SIGINT, partial(signal_handler, interrupt_handler=interrupt_handler, original_handler=signal.getsignal(signal.SIGINT)))
try: try:
# Create a custom transport that bypasses SSL verification transport = httpx.HTTPTransport(verify=False, http2=True)
transport = httpx.HTTPTransport(
verify=False,
http2=True
)
# Download with streaming and progress tracking
with httpx.Client(transport=transport, timeout=httpx.Timeout(60)) as client: with httpx.Client(transport=transport, timeout=httpx.Timeout(60)) as client:
with client.stream("GET", url, headers=headers, timeout=REQUEST_TIMEOUT) as response: with client.stream("GET", url, headers=headers, timeout=REQUEST_TIMEOUT) as response:
response.raise_for_status() response.raise_for_status()
@ -125,16 +134,16 @@ def MP4_downloader(url: str, path: str, referer: str = None, headers_: dict = No
unit_scale=True, unit_scale=True,
desc='Downloading', desc='Downloading',
mininterval=0.05, mininterval=0.05,
file=sys.stdout, # Using file=sys.stdout to force in-place updates because sys.stderr may not support carriage returns in this environment. file=sys.stdout # Using file=sys.stdout to force in-place updates because sys.stderr may not support carriage returns in this environment.
) )
downloaded = 0 downloaded = 0
with open(temp_path, 'wb') as file, progress_bar as bar: with open(temp_path, 'wb') as file, progress_bar as bar:
try: try:
for chunk in response.iter_bytes(chunk_size=1024): for chunk in response.iter_bytes(chunk_size=1024):
if kill_handler[0]: if interrupt_handler.force_quit:
console.print("\n[bold yellow]Interrupting download...[/bold yellow]") console.print("\n[bold red]Force quitting... Saving partial download.[/bold red]")
return None, True break
if chunk: if chunk:
size = file.write(chunk) size = file.write(chunk)
@ -142,18 +151,15 @@ def MP4_downloader(url: str, path: str, referer: str = None, headers_: dict = No
bar.update(size) bar.update(size)
except KeyboardInterrupt: except KeyboardInterrupt:
console.print("\n[bold red]Download interrupted by user.[/bold red]") if not interrupt_handler.force_quit:
if os.path.exists(temp_path): interrupt_handler.kill_download = True
os.remove(temp_path)
return None, True
# Rename temp file to final file
if os.path.exists(temp_path): if os.path.exists(temp_path):
os.rename(temp_path, path) os.rename(temp_path, path)
if os.path.exists(path): if os.path.exists(path):
console.print(Panel( console.print(Panel(
f"[bold green]Download completed![/bold green]\n" f"[bold green]Download completed{' (Partial)' if interrupt_handler.force_quit else ''}![/bold green]\n"
f"[cyan]File size: [bold red]{internet_manager.format_file_size(os.path.getsize(path))}[/bold red]\n" f"[cyan]File size: [bold red]{internet_manager.format_file_size(os.path.getsize(path))}[/bold red]\n"
f"[cyan]Duration: [bold]{print_duration_table(path, description=False, return_string=True)}[/bold]", f"[cyan]Duration: [bold]{print_duration_table(path, description=False, return_string=True)}[/bold]",
title=f"{os.path.basename(path.replace('.mp4', ''))}", title=f"{os.path.basename(path.replace('.mp4', ''))}",
@ -161,22 +167,22 @@ def MP4_downloader(url: str, path: str, referer: str = None, headers_: dict = No
)) ))
if TELEGRAM_BOT: if TELEGRAM_BOT:
message = f"Download completato\nDimensione: {internet_manager.format_file_size(os.path.getsize(path))}\nDurata: {print_duration_table(path, description=False, return_string=True)}\nTitolo: {os.path.basename(path.replace('.mp4', ''))}" message = f"Download completato{'(Parziale)' if interrupt_handler.force_quit else ''}\nDimensione: {internet_manager.format_file_size(os.path.getsize(path))}\nDurata: {print_duration_table(path, description=False, return_string=True)}\nTitolo: {os.path.basename(path.replace('.mp4', ''))}"
clean_message = re.sub(r'\[[a-zA-Z]+\]', '', message) clean_message = re.sub(r'\[[a-zA-Z]+\]', '', message)
bot.send_message(clean_message, None) bot.send_message(clean_message, None)
return path, kill_handler[0] return path, interrupt_handler.kill_download
else: else:
console.print("[bold red]Download failed or file is empty.[/bold red]") console.print("[bold red]Download failed or file is empty.[/bold red]")
return None, kill_handler[0] return None, interrupt_handler.kill_download
except Exception as e: except Exception as e:
logging.error(f"Unexpected error: {e}") logging.error(f"Unexpected error: {e}")
console.print(f"[bold red]Unexpected Error: {e}[/bold red]") console.print(f"[bold red]Unexpected Error: {e}[/bold red]")
if os.path.exists(temp_path): if os.path.exists(temp_path):
os.remove(temp_path) os.remove(temp_path)
return None, kill_handler[0] return None, interrupt_handler.kill_download
finally: finally:
# Restore original signal handler
signal.signal(signal.SIGINT, original_handler) signal.signal(signal.SIGINT, original_handler)

View File

@ -10,7 +10,7 @@ with open("requirements.txt", "r", encoding="utf-8-sig") as f:
setup( setup(
name="StreamingCommunity", name="StreamingCommunity",
version="2.5.7", version="2.5.8",
long_description=read_readme(), long_description=read_readme(),
long_description_content_type="text/markdown", long_description_content_type="text/markdown",
author="Lovi-0", author="Lovi-0",