Add NVIDIA support for FFmpeg in Docker and enhance chat rendering functionality
All checks were successful
Publish Twitch Archive Container / publish (push) Successful in 7m36s
All checks were successful
Publish Twitch Archive Container / publish (push) Successful in 7m36s
- Introduced a new docker-compose.nvidia.yml for NVIDIA GPU support. - Updated dockerstart.bat to allow optional NVIDIA runtime. - Enhanced ContentDownloader to manage chat rendering status and font settings. - Improved hardware acceleration detection in utils.py. - Added tests for hardware acceleration and chat rendering behavior. Co-authored-by: Copilot <copilot@github.com>
This commit is contained in:
parent
f97e0200d6
commit
ec44981a9d
8 changed files with 226 additions and 18 deletions
|
|
@ -4,6 +4,7 @@ Includes fallback support for chat_downloader when VOD-based methods fail.
|
|||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
import subprocess
|
||||
import json
|
||||
import threading
|
||||
|
|
@ -45,6 +46,10 @@ class ContentDownloader:
|
|||
self.download_live_chat_enabled = config.get('downloadLiveCHAT', True)
|
||||
self.use_chat_downloader_primary = config.get('useChatDownloaderPrimary', False)
|
||||
self.use_chat_downloader_fallback = config.get('useChatDownloaderFallback', True)
|
||||
default_chat_font = 'Arial' if sys.platform.startswith('win') else 'DejaVu Sans'
|
||||
self.chat_render_font = config.get('chat_render_font', default_chat_font)
|
||||
self.last_chat_render_attempted = False
|
||||
self.last_chat_render_succeeded = False
|
||||
|
||||
# Initialize chat_downloader if available
|
||||
self.chat_downloader = None
|
||||
|
|
@ -61,6 +66,11 @@ class ContentDownloader:
|
|||
self.chat_thread = None
|
||||
self.chat_thread_success = False
|
||||
self.chat_thread_error = None
|
||||
|
||||
def reset_chat_render_status(self) -> None:
|
||||
"""Reset chat render tracking before a processing pass."""
|
||||
self.last_chat_render_attempted = False
|
||||
self.last_chat_render_succeeded = False
|
||||
|
||||
def download_vod(self, vod_info: Dict[str, Any], output_path: str) -> bool:
|
||||
"""
|
||||
|
|
@ -190,7 +200,7 @@ class ContentDownloader:
|
|||
'-h', '1080',
|
||||
'--framerate', '30',
|
||||
'--outline',
|
||||
'-f', 'Arial',
|
||||
'-f', self.chat_render_font,
|
||||
'--font-size', '22',
|
||||
'--update-rate', '1.0',
|
||||
'--offline',
|
||||
|
|
@ -215,6 +225,9 @@ class ContentDownloader:
|
|||
|
||||
try:
|
||||
print(f'{Fore.YELLOW}Rendering chat video...{Style.RESET_ALL}')
|
||||
print(f'{Fore.CYAN}Using chat font: {self.chat_render_font}{Style.RESET_ALL}')
|
||||
self.last_chat_render_attempted = True
|
||||
self.last_chat_render_succeeded = False
|
||||
|
||||
# Build complete command
|
||||
full_cmd = [self.twitch_downloader_path, 'chatrender', '-i', json_path, '-o', video_path] + chat_settings
|
||||
|
|
@ -249,6 +262,7 @@ class ContentDownloader:
|
|||
print(f'{Fore.RED}✗ Chat video file is too small ({file_size} bytes){Style.RESET_ALL}')
|
||||
return False
|
||||
|
||||
self.last_chat_render_succeeded = True
|
||||
print(f'{Fore.GREEN}✓ Chat rendered ({file_size:,} bytes){Style.RESET_ALL}')
|
||||
return True
|
||||
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ import os
|
|||
import subprocess
|
||||
from colorama import Fore, Style
|
||||
|
||||
from .utils import detect_hardware_acceleration, get_hwaccel_encoder
|
||||
from .utils import detect_hardware_acceleration, get_hwaccel_encoder, resolve_hwaccel_type
|
||||
|
||||
|
||||
class StreamProcessor:
|
||||
|
|
@ -36,6 +36,7 @@ class StreamProcessor:
|
|||
config.get('ffmpeg_hwaccel', 'auto'),
|
||||
os_type
|
||||
)
|
||||
self.hwaccel_type = resolve_hwaccel_type(self.hwaccel_type, os_type)
|
||||
|
||||
def process_raw_stream(self, raw_path: str, output_path: str) -> bool:
|
||||
"""
|
||||
|
|
|
|||
|
|
@ -269,20 +269,57 @@ def detect_hardware_acceleration(hwaccel_config: str, os_type: str) -> Optional[
|
|||
if hwaccel_config in ['nvenc', 'qsv', 'amf', 'vaapi']:
|
||||
return hwaccel_config
|
||||
|
||||
# Auto-detect: try to determine available hardware
|
||||
# Auto-detect: choose only hardware we can reasonably prove is present.
|
||||
if hwaccel_config == 'auto':
|
||||
# On Windows, NVIDIA is most common
|
||||
if os_type == 'windows':
|
||||
# Could check for nvidia-smi, but just return 'auto' for ffmpeg to decide
|
||||
return 'auto'
|
||||
else:
|
||||
# On Linux, VAAPI is common for Intel/AMD, or NVENC for NVIDIA
|
||||
# Let ffmpeg auto-detect
|
||||
return 'auto'
|
||||
if is_nvidia_runtime_available():
|
||||
return 'nvenc'
|
||||
if is_vaapi_runtime_available():
|
||||
return 'vaapi'
|
||||
return 'none'
|
||||
|
||||
return None
|
||||
|
||||
|
||||
def is_nvidia_runtime_available() -> bool:
|
||||
"""Return True when the current runtime appears to expose an NVIDIA GPU."""
|
||||
visible_devices = os.getenv('NVIDIA_VISIBLE_DEVICES', '').strip().lower()
|
||||
if visible_devices in {'void', 'none'}:
|
||||
return False
|
||||
if visible_devices and visible_devices != 'all':
|
||||
return True
|
||||
|
||||
if shutil.which('nvidia-smi'):
|
||||
return True
|
||||
|
||||
return any(
|
||||
os.path.exists(device_path)
|
||||
for device_path in ('/dev/nvidiactl', '/dev/nvidia0', '/dev/nvidia-modeset')
|
||||
)
|
||||
|
||||
|
||||
def is_vaapi_runtime_available() -> bool:
|
||||
"""Return True when Linux VAAPI render nodes are present."""
|
||||
return any(
|
||||
os.path.exists(device_path)
|
||||
for device_path in ('/dev/dri/renderD128', '/dev/dri/card0')
|
||||
)
|
||||
|
||||
|
||||
def resolve_hwaccel_type(hwaccel_type: Optional[str], os_type: str) -> Optional[str]:
|
||||
"""Return a safe hardware acceleration choice for the current runtime."""
|
||||
if hwaccel_type in (None, 'none'):
|
||||
return 'none'
|
||||
|
||||
if hwaccel_type == 'nvenc':
|
||||
return 'nvenc' if is_nvidia_runtime_available() else 'none'
|
||||
|
||||
if hwaccel_type == 'vaapi':
|
||||
return 'vaapi' if is_vaapi_runtime_available() else 'none'
|
||||
|
||||
# Leave explicit QSV/AMF unchanged for non-container users; container auto-detect no longer picks them blindly.
|
||||
return hwaccel_type
|
||||
|
||||
|
||||
def get_hwaccel_encoder(hwaccel_type: str) -> str:
|
||||
"""
|
||||
Get the appropriate hardware-accelerated encoder for the given acceleration type.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue