refact (core): split large code blocks, remove useless files/code #12
+21
-36
@@ -11,34 +11,36 @@ This file serves as the single source of truth for:
|
||||
Codec != Container != File Extension.
|
||||
Example: Opus (codec) → Ogg (container) → .opus (extension)
|
||||
"""
|
||||
from typing import Dict, Optional
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# Container information
|
||||
# ------------------------------------------------------------------------------
|
||||
CONTAINER_INFO = [
|
||||
# Audio-only containers
|
||||
{'name': 'mp3', 'ffmpeg': 'mp3', 'extension': '.mp3', 'audio_only': False, 'supports_video': True, 'supports_subs': False},
|
||||
{'name': 'm4a', 'ffmpeg': 'mp4', 'extension': '.m4a', 'audio_only': True, 'supports_video': False, 'supports_subs': False},
|
||||
{'name': 'flac', 'ffmpeg': 'flac', 'extension': '.flac', 'audio_only': True, 'supports_video': False, 'supports_subs': False},
|
||||
{'name': 'wav', 'ffmpeg': 'wav', 'extension': '.wav', 'audio_only': True, 'supports_video': False, 'supports_subs': False},
|
||||
{'name': 'aac', 'ffmpeg': 'adts', 'extension': '.aac', 'audio_only': True, 'supports_video': False, 'supports_subs': False},
|
||||
{'name': 'mp3', 'ffmpeg': 'mp3', 'extension': '.mp3', 'supports_video': True, 'supports_subs': False},
|
||||
{'name': 'm4a', 'ffmpeg': 'mp4', 'extension': '.m4a', 'supports_video': False, 'supports_subs': False},
|
||||
{'name': 'flac', 'ffmpeg': 'flac', 'extension': '.flac', 'supports_video': False, 'supports_subs': False},
|
||||
{'name': 'wav', 'ffmpeg': 'wav', 'extension': '.wav', 'supports_video': False, 'supports_subs': False},
|
||||
{'name': 'aac', 'ffmpeg': 'adts', 'extension': '.aac', 'supports_video': False, 'supports_subs': False},
|
||||
# Containers that support video and subtitles
|
||||
{'name': 'mp4', 'ffmpeg': 'mp4', 'extension': '.mp4', 'audio_only': False, 'supports_video': True, 'supports_subs': True},
|
||||
{'name': 'mkv', 'ffmpeg': 'matroska', 'extension': '.mkv', 'audio_only': False, 'supports_video': True, 'supports_subs': True},
|
||||
{'name': 'matroska', 'ffmpeg': 'matroska', 'extension': '.mkv', 'audio_only': False, 'supports_video': True, 'supports_subs': True},
|
||||
{'name': 'ogg', 'ffmpeg': 'ogg', 'extension': '.ogg', 'audio_only': False, 'supports_video': True, 'supports_subs': True},
|
||||
{'name': 'webm', 'ffmpeg': 'webm', 'extension': '.webm', 'audio_only': False, 'supports_video': True, 'supports_subs': False},
|
||||
{'name': 'mp4', 'ffmpeg': 'mp4', 'extension': '.mp4', 'supports_video': True, 'supports_subs': True},
|
||||
{'name': 'mkv', 'ffmpeg': 'matroska', 'extension': '.mkv', 'supports_video': True, 'supports_subs': True},
|
||||
{'name': 'ogg', 'ffmpeg': 'ogg', 'extension': '.ogg', 'supports_video': True, 'supports_subs': True},
|
||||
{'name': 'webm', 'ffmpeg': 'webm', 'extension': '.webm', 'supports_video': True, 'supports_subs': False},
|
||||
]
|
||||
|
||||
# Legacy FORMAT_INFO for backward compatibility
|
||||
FORMAT_INFO = {
|
||||
container['name']: {
|
||||
'ffmpeg': container['ffmpeg'],
|
||||
'ext': container['extension'],
|
||||
'audio_only': container['audio_only'],
|
||||
}
|
||||
for container in CONTAINER_INFO
|
||||
}
|
||||
# Container name lookup set for fast membership checks
|
||||
CONTAINER_NAMES = {container['name'] for container in CONTAINER_INFO}
|
||||
|
||||
|
||||
def get_container_info(container_name: str) -> Optional[Dict]:
|
||||
"""Return container info dict or None if not found."""
|
||||
for container in CONTAINER_INFO:
|
||||
if container['name'] == container_name:
|
||||
return container
|
||||
return None
|
||||
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# Codec information
|
||||
@@ -70,22 +72,6 @@ CODEC_TO_EXTENSION_MAP = {codec['name']: codec['recommended_extension'] for code
|
||||
# Map codec name → FFmpeg encoder name
|
||||
CODEC_NAME_TO_FFMPEG = {codec['name']: codec['ffmpeg'] for codec in CODEC_INFO}
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# Video codec support per container (legacy, will be superseded by compatibility matrix)
|
||||
# ------------------------------------------------------------------------------
|
||||
CONTAINER_VIDEO_CODEC_SUPPORT = {
|
||||
'mp4': ['h264', 'h265', 'vp9', 'av1', 'mpeg4', 'hevc'],
|
||||
'mkv': ['*'],
|
||||
'matroska': ['*'],
|
||||
'ogg': ['theora', 'dirac', 'vp8', 'png', 'mjpeg'],
|
||||
'webm': ['vp8', 'vp9', 'av1'],
|
||||
'mp3': [],
|
||||
'm4a': [],
|
||||
'flac': [],
|
||||
'wav': [],
|
||||
'aac': [],
|
||||
}
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# Compatibility matrix: container → supported audio and video codecs
|
||||
# ------------------------------------------------------------------------------
|
||||
@@ -100,7 +86,6 @@ COMPATIBILITY_MATRIX = {
|
||||
'm4a': {'audio': ['aac', 'alac', 'opus', 'flac'], 'video': []},
|
||||
'mp4': {'audio': ['aac', 'alac', 'opus', 'flac', 'mp3'], 'video': ['h264', 'h265', 'vp9', 'av1']},
|
||||
'mkv': {'audio': None, 'video': None},
|
||||
'matroska': {'audio': None, 'video': None},
|
||||
'ogg': {'audio': ['opus', 'vorbis', 'flac'], 'video': ['theora', 'vp8', 'png']},
|
||||
'webm': {'audio': ['opus', 'vorbis'], 'video': ['vp8', 'vp9', 'av1']},
|
||||
'flac': {'audio': ['flac'], 'video': []},
|
||||
|
||||
@@ -7,9 +7,9 @@ import sys
|
||||
from typing import List, Dict, Any
|
||||
|
||||
from .constants import (
|
||||
FORMAT_INFO,
|
||||
CONTAINER_NAMES,
|
||||
CODEC_TO_EXTENSION_MAP,
|
||||
CODEC_NAME_TO_FFMPEG, # <-- Add this
|
||||
CODEC_NAME_TO_FFMPEG,
|
||||
)
|
||||
from .ffmpeg import (
|
||||
get_audio_duration,
|
||||
@@ -121,7 +121,8 @@ def split_audio(input_file: str, output_directory: str, tracks: List[Dict[str, A
|
||||
if output_audio_codec in CODEC_TO_EXTENSION_MAP:
|
||||
extension = CODEC_TO_EXTENSION_MAP[output_audio_codec]
|
||||
else:
|
||||
extension = FORMAT_INFO.get(output_container, {}).get('ext', '.mkv')
|
||||
container_info = next((c for c in CONTAINER_INFO if c['name'] == output_container), None)
|
||||
extension = container_info['extension'] if container_info else '.mkv'
|
||||
|
||||
print(f"Output extension: {extension}")
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ import json
|
||||
import subprocess
|
||||
from typing import Dict, List, Optional, Tuple
|
||||
|
||||
from .constants import FORMAT_INFO
|
||||
from .constants import CONTAINER_INFO
|
||||
from .utils import format_time
|
||||
|
||||
|
||||
@@ -159,7 +159,7 @@ def get_metadata(input_file: str) -> Dict[str, any]:
|
||||
|
||||
def get_container_format(input_file: str) -> Optional[str]:
|
||||
"""
|
||||
Retrieve the container format name (e.g., 'mp4', 'mp3', 'matroska') from the input file.
|
||||
Retrieve the container format name (e.g., 'mp4', 'mp3', 'mkv') from the input file.
|
||||
|
||||
Args:
|
||||
input_file: Path to the media file.
|
||||
@@ -179,7 +179,7 @@ def get_container_format(input_file: str) -> Optional[str]:
|
||||
format_name = result.stdout.strip().split(',')[0] # take first if multiple
|
||||
if not format_name:
|
||||
return None
|
||||
# Normalize common aliases to names used in FORMAT_INFO
|
||||
# Normalize common aliases to names used in CONTAINER_INFO
|
||||
mapping = {
|
||||
'mpeg': 'mp3',
|
||||
'mp2': 'mp3',
|
||||
@@ -187,8 +187,8 @@ def get_container_format(input_file: str) -> Optional[str]:
|
||||
'm4a': 'mp4',
|
||||
'mov': 'mp4',
|
||||
'3gp': 'mp4',
|
||||
'matroska': 'matroska',
|
||||
'webm': 'matroska',
|
||||
'matroska': 'mkv',
|
||||
'webm': 'mkv',
|
||||
'ogg': 'ogg',
|
||||
'flac': 'flac',
|
||||
'wav': 'wav',
|
||||
@@ -366,7 +366,7 @@ def _build_standard_mapping(
|
||||
def _build_format(cmd: List[str], format_opt: Optional[str]) -> None:
|
||||
"""Add output format option if specified."""
|
||||
if format_opt:
|
||||
ffmpeg_format = FORMAT_INFO.get(format_opt, {}).get('ffmpeg', format_opt)
|
||||
ffmpeg_format = next((c['ffmpeg'] for c in CONTAINER_INFO if c['name'] == format_opt), format_opt)
|
||||
cmd.extend(['-f', ffmpeg_format])
|
||||
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
from typing import Dict, Optional
|
||||
|
||||
from .constants import (
|
||||
FORMAT_INFO,
|
||||
CONTAINER_NAMES,
|
||||
CONTAINER_INFO,
|
||||
CODEC_TO_CONTAINER_MAP,
|
||||
COMPATIBILITY_MATRIX,
|
||||
@@ -22,7 +22,7 @@ def determine_default_format(container: Optional[str], codec: Optional[str]) ->
|
||||
If the codec is not found, it falls back to the container.
|
||||
|
||||
Args:
|
||||
container: Container name (e.g., 'ogg', 'mp4', 'matroska') as returned by get_container_format().
|
||||
container: Container name (e.g., 'ogg', 'mp4', 'mkv') as returned by get_container_format().
|
||||
codec: Audio codec name (e.g., 'opus', 'aac', 'mp3') as returned by get_audio_codec().
|
||||
|
||||
Returns:
|
||||
@@ -33,10 +33,10 @@ def determine_default_format(container: Optional[str], codec: Optional[str]) ->
|
||||
|
||||
if codec and codec in CODEC_TO_CONTAINER_MAP:
|
||||
fmt = CODEC_TO_CONTAINER_MAP[codec]
|
||||
if fmt in FORMAT_INFO:
|
||||
if fmt in CONTAINER_NAMES:
|
||||
return fmt
|
||||
|
||||
if container in FORMAT_INFO:
|
||||
if container in CONTAINER_NAMES:
|
||||
return container
|
||||
|
||||
return None
|
||||
@@ -67,14 +67,14 @@ def determine_output_format(stream_info: Dict, user_format: Optional[str],
|
||||
container = get_container_format(input_file)
|
||||
audio_codec = get_audio_codec(input_file)
|
||||
fmt = determine_default_format(container, audio_codec)
|
||||
if fmt in FORMAT_INFO:
|
||||
if fmt in CONTAINER_NAMES:
|
||||
return fmt
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
# Fallback
|
||||
if stream_info.get('has_video') or stream_info.get('has_subtitle'):
|
||||
return 'matroska'
|
||||
return 'mkv'
|
||||
audio_codec = stream_info.get('audio_codec', '')
|
||||
if audio_codec == 'mp3':
|
||||
return 'mp3'
|
||||
@@ -97,13 +97,13 @@ def validate_format_compatibility(
|
||||
Raises:
|
||||
ValueError: If the combination is incompatible.
|
||||
"""
|
||||
info = FORMAT_INFO.get(container)
|
||||
info = next((c for c in CONTAINER_INFO if c['name'] == container), None)
|
||||
if not info:
|
||||
print(f"Warning: Unknown container '{container}'. Proceeding, but may fail.")
|
||||
return
|
||||
|
||||
# Check if container is audio-only and video is present (unless dropped)
|
||||
if info['audio_only'] and stream_info.get('has_video') and not drop_video:
|
||||
if not info['supports_video'] and stream_info.get('has_video') and not drop_video:
|
||||
raise ValueError(
|
||||
f"Container '{container}' does not support video streams. "
|
||||
"Please use --drop-video or choose a container that supports video (e.g., MKV, MP4)."
|
||||
|
||||
@@ -9,7 +9,7 @@ from backend.services.task_manager import task_manager
|
||||
# Import core functions
|
||||
from audio_splitter.ffmpeg import get_stream_info, get_container_format, get_audio_codec
|
||||
from audio_splitter.formats import determine_default_format
|
||||
from audio_splitter.constants import FORMAT_INFO
|
||||
from audio_splitter.constants import CONTAINER_NAMES
|
||||
from audio_splitter.defaults import DEFAULT_FORMAT
|
||||
|
||||
router = APIRouter(prefix="/api", tags=["info"])
|
||||
@@ -63,7 +63,7 @@ async def get_recommended_format(task_id: str):
|
||||
# Fallback if detection fails or format is unsupported
|
||||
if fmt is None:
|
||||
fmt = DEFAULT_FORMAT
|
||||
if fmt not in FORMAT_INFO:
|
||||
if fmt not in CONTAINER_NAMES:
|
||||
fmt = "mp3" # ultimate fallback
|
||||
|
||||
return {"format": fmt}
|
||||
|
||||
@@ -16,7 +16,7 @@ export function useFormatValidation(
|
||||
) {
|
||||
useEffect(() => {
|
||||
const selectedContainer = containers.find(c => c.name === options.container)
|
||||
if (selectedContainer?.audio_only && hasVideo && !options.drop_video) {
|
||||
if (!selectedContainer?.supports_video && hasVideo && !options.drop_video) {
|
||||
setFormatError(
|
||||
`Container '${options.container}' does not support video streams. Please enable "Drop video" or choose a container that supports video.`
|
||||
)
|
||||
|
||||
@@ -48,7 +48,6 @@ export interface ContainerInfo {
|
||||
name: string
|
||||
ffmpeg: string
|
||||
extension: string
|
||||
audio_only: boolean
|
||||
supports_video: boolean
|
||||
supports_subs: boolean
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user