- Create shared/media/format_utils.py with format detection and validation - Create shared/media/video_encoder.py with FFmpegEncoder and PILEncoder classes - Extract validate_video_for_discord to shared utility - Extract ffmpeg detection to shared detect_ffmpeg() function - Extract Discord video optimization to shared optimize_video_for_discord() - Remove duplicate code from video_node.py (-109 lines) Phase 3 summary: - video_node.py: 1201 -> 1092 lines (9% reduction this phase) - Total reduction from original: 1562 -> 1092 lines (30% reduction) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
138 lines
3.6 KiB
Python
138 lines
3.6 KiB
Python
"""
|
|
Video format utilities for ComfyUI-DiscordSend
|
|
|
|
Provides format detection, extension mapping, and validation.
|
|
"""
|
|
|
|
import os
|
|
from typing import Tuple, Optional
|
|
|
|
|
|
def parse_format_string(format_str: str) -> Tuple[str, str]:
|
|
"""
|
|
Parse a format string into type and extension.
|
|
|
|
Args:
|
|
format_str: Format string like "video/h264-mp4" or "image/gif"
|
|
|
|
Returns:
|
|
Tuple of (format_type, format_extension)
|
|
"""
|
|
if "/" in format_str:
|
|
format_type, format_ext = format_str.split("/", 1)
|
|
else:
|
|
format_type = "video"
|
|
format_ext = format_str
|
|
|
|
return format_type, format_ext
|
|
|
|
|
|
def normalize_video_extension(format_str: str) -> str:
|
|
"""
|
|
Normalize a format string to a file extension.
|
|
|
|
Args:
|
|
format_str: Format string like "video/h264-mp4"
|
|
|
|
Returns:
|
|
Normalized extension (e.g., "mp4", "webm", "gif")
|
|
"""
|
|
_, format_ext = parse_format_string(format_str)
|
|
|
|
# Map format strings to extensions
|
|
extension_map = {
|
|
"h264-mp4": "mp4",
|
|
"h265-mp4": "mp4",
|
|
"vp9-webm": "webm",
|
|
"prores": "mov",
|
|
}
|
|
|
|
return extension_map.get(format_ext, format_ext)
|
|
|
|
|
|
def get_mime_type(extension: str) -> str:
|
|
"""
|
|
Get MIME type for a video extension.
|
|
|
|
Args:
|
|
extension: File extension (without dot)
|
|
|
|
Returns:
|
|
MIME type string
|
|
"""
|
|
mime_types = {
|
|
"mp4": "video/mp4",
|
|
"webm": "video/webm",
|
|
"gif": "image/gif",
|
|
"mov": "video/quicktime",
|
|
"avi": "video/x-msvideo",
|
|
"mkv": "video/x-matroska",
|
|
}
|
|
return mime_types.get(extension.lower(), "application/octet-stream")
|
|
|
|
|
|
def validate_video_for_discord(file_path: str, max_size_mb: int = 25) -> Tuple[bool, str]:
|
|
"""
|
|
Validate that a video file is compatible with Discord.
|
|
|
|
Args:
|
|
file_path: Path to the video file
|
|
max_size_mb: Maximum file size in megabytes (default 25MB for Discord)
|
|
|
|
Returns:
|
|
Tuple of (is_valid, message)
|
|
"""
|
|
if not os.path.exists(file_path):
|
|
return False, f"File does not exist: {file_path}"
|
|
|
|
file_size = os.path.getsize(file_path)
|
|
|
|
if file_size == 0:
|
|
return False, "File is empty"
|
|
|
|
if file_size < 1024:
|
|
return False, f"File is suspiciously small: {file_size} bytes"
|
|
|
|
max_size = max_size_mb * 1024 * 1024
|
|
if file_size > max_size:
|
|
return False, f"File exceeds Discord's size limit of {max_size_mb}MB ({file_size / (1024*1024):.2f}MB)"
|
|
|
|
ext = os.path.splitext(file_path)[1].lower().lstrip('.')
|
|
|
|
if ext in ['mp4', 'webm', 'gif']:
|
|
return True, "Valid"
|
|
elif ext in ['mov']:
|
|
return False, "MOV files may need conversion for Discord compatibility"
|
|
elif ext in ['png', 'apng']:
|
|
return False, "PNG/APNG sequence may need compilation for Discord"
|
|
else:
|
|
return False, f"Unknown format '{ext}' - may not be compatible with Discord"
|
|
|
|
|
|
def is_animated_format(extension: str) -> bool:
|
|
"""
|
|
Check if a format supports animation.
|
|
|
|
Args:
|
|
extension: File extension (without dot)
|
|
|
|
Returns:
|
|
True if the format supports animation
|
|
"""
|
|
animated_formats = {'gif', 'webp', 'mp4', 'webm', 'mov', 'avi', 'mkv', 'apng'}
|
|
return extension.lower() in animated_formats
|
|
|
|
|
|
def supports_alpha(extension: str) -> bool:
|
|
"""
|
|
Check if a format supports alpha channel (transparency).
|
|
|
|
Args:
|
|
extension: File extension (without dot)
|
|
|
|
Returns:
|
|
True if the format supports alpha
|
|
"""
|
|
alpha_formats = {'webm', 'gif', 'webp', 'png', 'apng', 'mov'}
|
|
return extension.lower() in alpha_formats
|