refact (core): split large code blocks, remove useless files/code #12
+31
-2
@@ -23,6 +23,7 @@ from .formats import (
|
||||
determine_output_format,
|
||||
validate_format_compatibility,
|
||||
)
|
||||
from .handlers import get_handler, needs_drop_video
|
||||
from .timestamp import parse_track_timestamps, resolve_end_times
|
||||
from .filename import build_filename
|
||||
from .metadata import build_metadata_dict
|
||||
@@ -162,6 +163,14 @@ def split_audio(input_file: str, output_directory: str, tracks: List[Dict[str, A
|
||||
else:
|
||||
cover_image_path = None
|
||||
|
||||
# Check if we need special handling (e.g., Opus files need opustags)
|
||||
input_audio_codec = stream_info.get('audio_codec')
|
||||
cover_handler = get_handler(output_container, input_audio_codec)
|
||||
needs_drop = needs_drop_video(output_container, input_audio_codec)
|
||||
if cover_image_path and needs_drop:
|
||||
print(f"Using special handler for {output_container} + {input_audio_codec}")
|
||||
print("Temporarily dropping video for opustags post-processing.")
|
||||
|
||||
# --------------------------------------------------------------------------
|
||||
# 8. Process each track
|
||||
# --------------------------------------------------------------------------
|
||||
@@ -208,6 +217,11 @@ def split_audio(input_file: str, output_directory: str, tracks: List[Dict[str, A
|
||||
subtitle_enc = CODEC_NAME_TO_FFMPEG.get(subtitle_enc, subtitle_enc)
|
||||
|
||||
# Then build command with these mapped encoders
|
||||
# Use temporary drop_video for handlers that need it
|
||||
effective_drop_video = args.drop_video or (cover_image_path and needs_drop)
|
||||
# For handlers that need opustags post-processing, don't pass cover_image_path
|
||||
# to ffmpeg - it will process audio-only, then handler adds cover afterward
|
||||
ffmpeg_cover_path = None if (cover_image_path and needs_drop) else cover_image_path
|
||||
cmd = build_ffmpeg_command(
|
||||
input_file=input_file,
|
||||
start_seconds=start_seconds,
|
||||
@@ -219,9 +233,9 @@ def split_audio(input_file: str, output_directory: str, tracks: List[Dict[str, A
|
||||
video_codec=video_enc,
|
||||
subtitle_codec=subtitle_enc,
|
||||
metadata=metadata,
|
||||
cover_image_path=cover_image_path,
|
||||
cover_image_path=ffmpeg_cover_path,
|
||||
video_quality=getattr(args, 'video_quality', None),
|
||||
drop_video=args.drop_video,
|
||||
drop_video=effective_drop_video,
|
||||
drop_subs=args.drop_subs,
|
||||
)
|
||||
|
||||
@@ -235,6 +249,21 @@ def split_audio(input_file: str, output_directory: str, tracks: List[Dict[str, A
|
||||
print(result.stderr)
|
||||
else:
|
||||
print(f" -> Saved to: {output_path}")
|
||||
# Apply cover image handler if needed
|
||||
if cover_image_path and not args.drop_video and needs_drop:
|
||||
print(f"Applying cover image via {output_container} handler...")
|
||||
if not cover_handler(
|
||||
input_file=input_file,
|
||||
cover_image_path=cover_image_path,
|
||||
output_path=output_path,
|
||||
stream_info=stream_info,
|
||||
audio_codec=audio_enc,
|
||||
video_codec=video_enc,
|
||||
video_quality=getattr(args, 'video_quality', None),
|
||||
drop_video=args.drop_video,
|
||||
drop_subs=args.drop_subs,
|
||||
):
|
||||
print(f"Warning: Failed to attach cover image to {output_path}")
|
||||
|
||||
# --------------------------------------------------------------------------
|
||||
# 9. Delete original file if requested
|
||||
|
||||
@@ -310,6 +310,9 @@ def _build_cover_image_mapping(
|
||||
else:
|
||||
cmd.extend(['-c:a', 'copy'])
|
||||
|
||||
# Preserve attached_pic disposition for cover image
|
||||
cmd.extend(['-disposition', 'attached_pic'])
|
||||
|
||||
cmd.append('-sn')
|
||||
|
||||
if video_quality is not None:
|
||||
|
||||
@@ -0,0 +1,167 @@
|
||||
# audio_splitter/handlers.py
|
||||
"""Handler system for cover image attachment.
|
||||
|
||||
This module provides a registry of handlers for different
|
||||
codec + image combinations. Each handler is responsible for
|
||||
attaching the cover image to the output file in the appropriate way.
|
||||
"""
|
||||
|
||||
import os
|
||||
import subprocess
|
||||
from typing import Dict, Optional
|
||||
|
||||
|
||||
def _run_opustags(input_file: str, cover_image_path: str, output_path: Optional[str] = None) -> bool:
|
||||
"""
|
||||
Run opustags to attach a cover image to an Opus file.
|
||||
|
||||
Args:
|
||||
input_file: Path to the input Opus file.
|
||||
cover_image_path: Path to the cover image.
|
||||
output_path: Optional output path (if None, overwrites input).
|
||||
|
||||
Returns:
|
||||
True if successful, False otherwise.
|
||||
"""
|
||||
cmd = ['opustags', '--set-cover', cover_image_path, input_file]
|
||||
|
||||
if output_path:
|
||||
cmd.extend(['-o', output_path, '-y'])
|
||||
else:
|
||||
cmd.extend(['-i'])
|
||||
|
||||
result = subprocess.run(cmd, capture_output=True, text=True, check=False)
|
||||
if result.returncode != 0:
|
||||
print(f"opustags failed: {result.stderr}")
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
def _handler_default(
|
||||
input_file: str,
|
||||
cover_image_path: str,
|
||||
output_path: str,
|
||||
stream_info: Dict,
|
||||
audio_codec: str,
|
||||
video_codec: str,
|
||||
video_quality: Optional[int],
|
||||
drop_video: bool,
|
||||
drop_subs: bool,
|
||||
) -> bool:
|
||||
"""
|
||||
Default handler: use ffmpeg with -disposition attached_pic.
|
||||
|
||||
This is the standard approach for formats like MP3, FLAC, etc.
|
||||
"""
|
||||
# This handler doesn't do anything - the cover is already handled
|
||||
# by the ffmpeg command in core.py via build_ffmpeg_command()
|
||||
# We just return True to indicate success.
|
||||
return True
|
||||
|
||||
|
||||
def _handler_opus_with_cover(
|
||||
input_file: str,
|
||||
cover_image_path: str,
|
||||
output_path: str,
|
||||
stream_info: Dict,
|
||||
audio_codec: str,
|
||||
video_codec: str,
|
||||
video_quality: Optional[int],
|
||||
drop_video: bool,
|
||||
drop_subs: bool,
|
||||
) -> bool:
|
||||
"""
|
||||
Handler for Opus files with cover image.
|
||||
|
||||
Uses opustags to attach the cover image after the file is processed.
|
||||
"""
|
||||
return _run_opustags(output_path, cover_image_path)
|
||||
|
||||
|
||||
def _handler_mp3_with_cover(
|
||||
input_file: str,
|
||||
cover_image_path: str,
|
||||
output_path: str,
|
||||
stream_info: Dict,
|
||||
audio_codec: str,
|
||||
video_codec: str,
|
||||
video_quality: Optional[int],
|
||||
drop_video: bool,
|
||||
drop_subs: bool,
|
||||
) -> bool:
|
||||
"""
|
||||
Handler for MP3 files with cover image.
|
||||
|
||||
Uses ffmpeg with -disposition attached_pic.
|
||||
"""
|
||||
# MP3 handler - the cover is already attached by ffmpeg in core.py
|
||||
return True
|
||||
|
||||
|
||||
def _handler_flac_with_cover(
|
||||
input_file: str,
|
||||
cover_image_path: str,
|
||||
output_path: str,
|
||||
stream_info: Dict,
|
||||
audio_codec: str,
|
||||
video_codec: str,
|
||||
video_quality: Optional[int],
|
||||
drop_video: bool,
|
||||
drop_subs: bool,
|
||||
) -> bool:
|
||||
"""
|
||||
Handler for FLAC files with cover image.
|
||||
|
||||
Uses ffmpeg with -disposition attached_pic.
|
||||
"""
|
||||
# FLAC handler - the cover is already attached by ffmpeg in core.py
|
||||
return True
|
||||
|
||||
|
||||
# Handler registry: maps (output_container, input_codec) to handler function
|
||||
HANDLER_REGISTRY: Dict[tuple, callable] = {
|
||||
('opus', 'opus'): _handler_opus_with_cover,
|
||||
('ogg', 'opus'): _handler_opus_with_cover,
|
||||
('mp3', 'mp3'): _handler_mp3_with_cover,
|
||||
('flac', 'flac'): _handler_flac_with_cover,
|
||||
}
|
||||
|
||||
|
||||
def get_handler(output_container: str, input_audio_codec: Optional[str] = None):
|
||||
"""
|
||||
Get the appropriate handler for the given container and input codec.
|
||||
|
||||
Args:
|
||||
output_container: Output container format (e.g., 'opus', 'mp3', 'flac').
|
||||
input_audio_codec: Input audio codec (e.g., 'opus', 'mp3', 'flac').
|
||||
|
||||
Returns:
|
||||
Handler function, or the default handler if no specific handler is found.
|
||||
"""
|
||||
if input_audio_codec:
|
||||
key = (output_container, input_audio_codec)
|
||||
if key in HANDLER_REGISTRY:
|
||||
return HANDLER_REGISTRY[key]
|
||||
|
||||
# Fall back to container-only key
|
||||
if (output_container, None) in HANDLER_REGISTRY:
|
||||
return HANDLER_REGISTRY[(output_container, None)]
|
||||
|
||||
# Default handler
|
||||
return _handler_default
|
||||
|
||||
|
||||
def needs_drop_video(output_container: str, input_audio_codec: Optional[str] = None) -> bool:
|
||||
"""
|
||||
Check if the handler requires dropping video even if user didn't specify --drop-video.
|
||||
|
||||
Args:
|
||||
output_container: Output container format.
|
||||
input_audio_codec: Input audio codec.
|
||||
|
||||
Returns:
|
||||
True if video should be dropped, False otherwise.
|
||||
"""
|
||||
handler = get_handler(output_container, input_audio_codec)
|
||||
# The opus handler requires dropping video
|
||||
return handler == _handler_opus_with_cover
|
||||
Reference in New Issue
Block a user