Refactor downloader and file manager for improved rclone integration and add healthcheck and smoke test options
- Renamed download flags in ContentDownloader for clarity. - Enhanced FileManager with methods to build upload paths and verify existing files for rclone uploads. - Updated StreamProcessor to return success status for stream processing. - Added rclone smoke test and healthcheck functions to validate configuration and tool availability. - Improved environment variable handling with a utility function. - Updated TwitchArchive to incorporate new rclone verification and processing logic. - Added unit tests for new functionality and refactored existing tests for clarity and coverage. Co-authored-by: Copilot <copilot@github.com>
This commit is contained in:
parent
e92f36474a
commit
f97e0200d6
23 changed files with 1013 additions and 289 deletions
|
|
@ -37,37 +37,78 @@ class StreamProcessor:
|
|||
os_type
|
||||
)
|
||||
|
||||
def process_raw_stream(self, raw_path: str, output_path: str) -> None:
|
||||
def process_raw_stream(self, raw_path: str, output_path: str) -> bool:
|
||||
"""
|
||||
Process raw .ts file into mp4/mp3 using ffmpeg.
|
||||
|
||||
Args:
|
||||
raw_path: Path to the raw .ts file
|
||||
output_path: Path for the processed output file
|
||||
|
||||
Returns:
|
||||
bool: True when conversion succeeded, False otherwise
|
||||
"""
|
||||
if not os.path.exists(raw_path):
|
||||
print(f'{Fore.YELLOW}⚠ Raw file not found, skipping processing{Style.RESET_ALL}')
|
||||
return
|
||||
return False
|
||||
|
||||
if self.only_raw:
|
||||
print(f'{Fore.CYAN}Keeping raw .ts file (onlyRaw mode){Style.RESET_ALL}')
|
||||
return
|
||||
return False
|
||||
|
||||
print(f'{Fore.YELLOW}Processing raw stream file...{Style.RESET_ALL}')
|
||||
|
||||
# Build ffmpeg command based on quality
|
||||
if self.quality == 'audio_only':
|
||||
self._process_audio(raw_path, output_path)
|
||||
result = self._process_audio(raw_path, output_path)
|
||||
else:
|
||||
self._process_video(raw_path, output_path)
|
||||
result = self._process_video(raw_path, output_path)
|
||||
|
||||
print(f'{Fore.GREEN}✓ Stream processed successfully{Style.RESET_ALL}')
|
||||
if result:
|
||||
print(f'{Fore.GREEN}✓ Stream processed successfully{Style.RESET_ALL}')
|
||||
else:
|
||||
print(f'{Fore.RED}✗ Stream processing failed{Style.RESET_ALL}')
|
||||
|
||||
return result
|
||||
|
||||
def _process_audio(self, raw_path: str, output_path: str) -> None:
|
||||
def _run_ffmpeg_command(self, cmd: list, output_path: str) -> bool:
|
||||
"""Run FFmpeg while streaming its output to the terminal."""
|
||||
print(f'{Fore.CYAN}Running FFmpeg: {' '.join(cmd)}{Style.RESET_ALL}')
|
||||
|
||||
process = subprocess.Popen(
|
||||
cmd,
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.STDOUT,
|
||||
text=True,
|
||||
encoding='utf-8',
|
||||
errors='replace'
|
||||
)
|
||||
|
||||
if process.stdout:
|
||||
for line in process.stdout:
|
||||
print(line, end='')
|
||||
|
||||
result = process.wait()
|
||||
if result != 0:
|
||||
print(f'{Fore.RED}✗ FFmpeg exited with code: {result}{Style.RESET_ALL}')
|
||||
return False
|
||||
|
||||
if not os.path.exists(output_path):
|
||||
print(f'{Fore.RED}✗ FFmpeg did not create output: {output_path}{Style.RESET_ALL}')
|
||||
return False
|
||||
|
||||
if os.path.getsize(output_path) == 0:
|
||||
print(f'{Fore.RED}✗ FFmpeg created an empty output file: {output_path}{Style.RESET_ALL}')
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
def _process_audio(self, raw_path: str, output_path: str) -> bool:
|
||||
"""Process audio-only stream."""
|
||||
# Audio-only conversion with modern AAC encoding
|
||||
cmd = [
|
||||
self.ffmpeg_path,
|
||||
'-y',
|
||||
'-i', raw_path,
|
||||
'-vn', # No video
|
||||
'-c:a', self.ffmpeg_audio_codec,
|
||||
|
|
@ -85,14 +126,9 @@ class StreamProcessor:
|
|||
cmd.extend(['-movflags', '+faststart'])
|
||||
|
||||
cmd.append(output_path)
|
||||
|
||||
# Run FFmpeg
|
||||
if self.ffmpeg_progress:
|
||||
subprocess.call(cmd)
|
||||
else:
|
||||
subprocess.call(cmd, stdout=subprocess.DEVNULL, stderr=subprocess.STDOUT)
|
||||
return self._run_ffmpeg_command(cmd, output_path)
|
||||
|
||||
def _process_video(self, raw_path: str, output_path: str) -> None:
|
||||
def _process_video(self, raw_path: str, output_path: str) -> bool:
|
||||
"""Process video stream."""
|
||||
cmd = [
|
||||
self.ffmpeg_path,
|
||||
|
|
@ -135,12 +171,7 @@ class StreamProcessor:
|
|||
cmd.extend(['-movflags', '+faststart'])
|
||||
|
||||
cmd.append(output_path)
|
||||
|
||||
# Run FFmpeg
|
||||
if self.ffmpeg_progress:
|
||||
subprocess.call(cmd)
|
||||
else:
|
||||
subprocess.call(cmd, stdout=subprocess.DEVNULL, stderr=subprocess.STDOUT)
|
||||
return self._run_ffmpeg_command(cmd, output_path)
|
||||
|
||||
def build_chat_output_args(self) -> str:
|
||||
"""
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue