Refactor code structure for improved readability and maintainability

This commit is contained in:
MaddoScientisto 2026-02-09 23:46:11 +01:00
commit e078cada3b
11 changed files with 1640 additions and 1184 deletions

105
modules/recorder.py Normal file
View file

@ -0,0 +1,105 @@
"""
Live stream recording functionality for Twitch Archive.
"""
import os
import subprocess
from typing import Dict, Any, Optional
from colorama import Fore, Style
class StreamRecorder:
"""Handles live stream recording using streamlink."""
def __init__(self, username: str, quality: str, refresh: float,
hls_segments: int, streamlink_ttvlol: bool, shutdown_callback=None):
"""
Initialize the stream recorder.
Args:
username: Twitch username to record
quality: Stream quality (e.g., 'best', '1080p60', 'audio_only')
refresh: Retry interval in seconds
hls_segments: Number of parallel HLS segments to download
streamlink_ttvlol: Enable ttv-lol proxy (deprecated)
shutdown_callback: Callable to check if shutdown was requested
"""
self.username = username
self.quality = quality
self.refresh = refresh
self.hls_segments = hls_segments
self.streamlink_ttvlol = streamlink_ttvlol
self.shutdown_callback = shutdown_callback
self.current_process = None
def record(self, stream_info: Dict[str, Any], output_path: str) -> bool:
"""
Record a live Twitch stream using streamlink.
Args:
stream_info: Stream metadata from Twitch API
output_path: Path where the raw .ts file will be saved
Returns:
bool: True if recording completed normally, False if interrupted
"""
print(f'\n{Fore.CYAN}{"=" * 60}{Style.RESET_ALL}')
print(f'{Fore.GREEN}🔴 STREAM STARTED: {stream_info["title"]}{Style.RESET_ALL}')
print(f'{Fore.CYAN}{"=" * 60}{Style.RESET_ALL}\n')
# Build streamlink command
cmd = [
'streamlink',
f'twitch.tv/{self.username}',
self.quality,
'--hls-live-restart',
'--retry-streams', str(int(self.refresh)),
'--force',
'-o', output_path
]
# Add segment threads for faster downloads (requires streamlink 5.0+)
if self.hls_segments > 1:
cmd.extend(['--stream-segment-threads', str(self.hls_segments)])
# Add ad-blocking if enabled (deprecated warning)
if self.streamlink_ttvlol:
print(f'{Fore.YELLOW}⚠ Warning: ttv-lol proxy option is deprecated in newer streamlink versions{Style.RESET_ALL}')
print(f'{Fore.YELLOW} Consider disabling streamlink_ttvlol in config or using alternative methods{Style.RESET_ALL}')
# Add authentication if available
oauth_token = os.getenv("OAUTH-PRIVATE-TOKEN", "")
if oauth_token and oauth_token != "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx":
cmd.extend(['--twitch-api-header', f'Authorization=OAuth {oauth_token}'])
# Show command being executed (hide OAuth token for security)
cmd_display = [c if 'OAuth' not in str(c) else 'Authorization=OAuth [HIDDEN]' for c in cmd]
print(f'{Fore.CYAN}Command: {" ".join(cmd_display)}{Style.RESET_ALL}')
# Record the stream (this blocks until stream ends)
print(f'{Fore.YELLOW}Recording stream...{Style.RESET_ALL}')
try:
self.current_process = subprocess.Popen(cmd)
return_code = self.current_process.wait()
self.current_process = None
if self.shutdown_callback and self.shutdown_callback():
print(f'{Fore.YELLOW}✓ Recording stopped by user{Style.RESET_ALL}')
# Return True so processing continues - we still want to process what was recorded
return True
print(f'{Fore.GREEN}✓ Stream recording complete{Style.RESET_ALL}')
return True
except Exception as e:
self.current_process = None
print(f'{Fore.RED}✗ Recording error: {str(e)}{Style.RESET_ALL}')
return False
def stop(self) -> None:
"""Stop the current recording process."""
if self.current_process:
try:
self.current_process.terminate()
print(f'{Fore.YELLOW}Stopping recording process...{Style.RESET_ALL}')
except Exception:
pass