FEATURE: codecs/containers/file_extensions inconsistency fixed

This commit is contained in:
2026-08-21 15:59:05 +05:00
parent 7af2600ed8
commit 262d458ce4
4 changed files with 209 additions and 91 deletions
+79 -53
View File
@@ -3,8 +3,7 @@
from typing import Dict, Optional
from .constants import FORMAT_INFO, CODEC_TO_CONTAINER_MAP, CONTAINER_INFO
from .constants import FORMAT_INFO, CODEC_TO_CONTAINER_MAP, CONTAINER_INFO, CONTAINER_VIDEO_CODEC_SUPPORT, CODEC_TYPE_MAP
def determine_default_format(container: Optional[str], codec: Optional[str]) -> Optional[str]:
"""
@@ -35,60 +34,10 @@ def determine_default_format(container: Optional[str], codec: Optional[str]) ->
return None
def determine_output_format(stream_info: Dict, user_format: Optional[str],
transcode_audio: Optional[str], input_file: Optional[str] = None) -> str:
"""
Decide which container format to use.
If user_format is provided, use it.
Else, try to detect the input file's container and codec, and use the recommended format.
If detection fails or format is not supported, fallback to:
- MKV if video/subtitles exist
- MP3 if the audio codec is MP3
- MP4 (M4A) otherwise
Args:
stream_info: Dict from get_stream_info().
user_format: Userrequested format (or None).
transcode_audio: Audio codec to transcode to (or None) (unused in this function).
input_file: Path to the input file (optional, used to detect container and codec).
Returns:
A format name that exists in FORMAT_INFO.
"""
if user_format:
return user_format
# If input_file is provided, try to detect container and codec
if input_file:
try:
from .ffmpeg import get_container_format, get_audio_codec
container = get_container_format(input_file)
codec = get_audio_codec(input_file)
fmt = determine_default_format(container, codec)
if fmt in FORMAT_INFO:
return fmt
except Exception:
# If detection fails, fall through to legacy logic
pass
# Fallback: legacy behavior
if stream_info.get('has_video') or stream_info.get('has_subtitle'):
return 'matroska'
audio_codec = stream_info.get('audio_codec', '')
if audio_codec == 'mp3':
return 'mp3'
else:
return 'mp4' # .m4a
def validate_format_compatibility(format_name: str, stream_info: Dict,
drop_video: bool, drop_subs: bool) -> None:
drop_video: bool, drop_subs: bool, input_file: Optional[str] = None) -> None:
"""
Ensure the chosen container can accommodate the streams we intend to keep.
Raises:
ValueError: If the format is incompatible with the intended streams.
"""
info = FORMAT_INFO.get(format_name)
if not info:
@@ -106,3 +55,80 @@ def validate_format_compatibility(format_name: str, stream_info: Dict,
f"Format '{format_name}' does not support subtitle streams. "
"Please use --drop-subs or choose a container that supports subtitles."
)
else:
# If video is present and not dropped, check if the container supports the video codec
if stream_info.get('has_video') and not drop_video and input_file:
from .ffmpeg import get_video_codec
video_codec = get_video_codec(input_file)
if video_codec and not is_video_codec_supported(format_name, video_codec):
raise ValueError(
f"Container '{format_name}' does not support video codec '{video_codec}'. "
"Please use --drop-video or choose a container that supports this video codec (e.g., MKV)."
)
def is_video_codec_supported(container_name: str, video_codec: str) -> bool:
"""
Check if a given container supports a specific video codec.
Args:
container_name: Name of the container (e.g., 'mkv', 'ogg')
video_codec: Video codec name (e.g., 'theora', 'h264', 'png')
Returns:
True if supported, False otherwise.
"""
support = CONTAINER_VIDEO_CODEC_SUPPORT.get(container_name, [])
if not support:
return False
if '*' in support:
return True
return video_codec in support
def determine_output_format(stream_info: Dict, user_format: Optional[str],
transcode_audio: Optional[str], input_file: Optional[str] = None) -> str:
"""
Decide which container format to use.
If user_format is provided, use it.
Else, try to detect the input file's container and codec, and use the recommended format.
If detection fails or format is not supported, fallback to:
- MKV if video/subtitles exist and the video codec is not supported by the recommended container
- MP3 if the audio codec is MP3
- MP4 (M4A) otherwise
"""
if user_format:
return user_format
# Try to detect container and codec from input file
if input_file:
try:
from .ffmpeg import get_container_format, get_audio_codec, get_video_codec
container = get_container_format(input_file)
audio_codec = get_audio_codec(input_file)
video_codec = get_video_codec(input_file) # NEW: get video codec
# Determine recommended format based on audio codec
fmt = determine_default_format(container, audio_codec)
# If video is present and not dropped, check if the recommended container supports the video codec
if stream_info.get('has_video') and not transcode_audio: # transcode_audio doesn't affect video
# If the recommended container doesn't support the video codec, fallback to MKV
if fmt and not is_video_codec_supported(fmt, video_codec):
fmt = 'matroska' # MKV supports virtually all video codecs
# Optionally log a warning
# print(f"Warning: Container '{fmt}' does not support video codec '{video_codec}'. Falling back to MKV.")
if fmt in FORMAT_INFO:
return fmt
except Exception:
pass
# Fallback: legacy behavior
if stream_info.get('has_video') or stream_info.get('has_subtitle'):
return 'matroska'
audio_codec = stream_info.get('audio_codec', '')
if audio_codec == 'mp3':
return 'mp3'
else:
return 'mp4' # .m4a