"""Core logic: orchestrates the splitting process.""" import os import subprocess from .constants import FORMAT_INFO from .ffmpeg import get_audio_duration, get_stream_info, get_metadata, build_ffmpeg_command from .formats import determine_output_format, validate_format_compatibility from .timestamp import parse_track_timestamps, resolve_end_times from .filename import build_filename from .metadata import build_metadata_dict from .utils import format_time def split_audio(input_file, output_directory, tracks, args): """ Main orchestration function: split the audio file into tracks. Args: input_file: Path to the input media file. output_directory: Directory where output files will be saved. tracks: List of dicts, each containing parsed fields. args: Parsed command‑line arguments (namespace). Raises: RuntimeError: If no audio stream is found. ValueError: If timestamp parsing or format compatibility fails. """ # -------------------------------------------------------------------------- # 1. Setup # -------------------------------------------------------------------------- os.makedirs(output_directory, exist_ok=True) total_duration = get_audio_duration(input_file) stream_info = get_stream_info(input_file) print(f"Detected streams: audio={stream_info['has_audio']}, " f"video={stream_info['has_video']}, subs={stream_info['has_subtitle']}") print(f"Audio codec: {stream_info.get('audio_codec', 'unknown')}") if not stream_info['has_audio']: raise RuntimeError("No audio stream found in input file.") # -------------------------------------------------------------------------- # 2. Format decision # -------------------------------------------------------------------------- output_format = determine_output_format(stream_info, args.format, args.transcode_to) print(f"Output container: {output_format}") validate_format_compatibility(output_format, stream_info, args.drop_video, args.drop_subs) # Determine file extension. extension_info = FORMAT_INFO.get(output_format, {}) extension = extension_info.get('ext', '.mkv') # -------------------------------------------------------------------------- # 3. Parse timestamps # -------------------------------------------------------------------------- track_times = parse_track_timestamps(tracks) resolved_times = resolve_end_times(track_times, total_duration) # -------------------------------------------------------------------------- # 4. Fetch original metadata (for fallbacks) # -------------------------------------------------------------------------- input_metadata = get_metadata(input_file) original_album = input_metadata.get('album') original_title = input_metadata.get('title') original_comments = input_metadata.get('comments', []) if original_album: print(f"Original album: '{original_album}'") if original_title: print(f"Original title: '{original_title}'") if original_comments: print(f"Found {len(original_comments)} comment(s) in input file.") for idx, comment in original_comments: print(f" Stream {idx}: '{comment}'") # -------------------------------------------------------------------------- # 5. Process each track # -------------------------------------------------------------------------- for idx, track in enumerate(tracks, start=1): start_seconds, end_seconds = resolved_times[idx - 1] duration_seconds = end_seconds - start_seconds if duration_seconds <= 0: print(f"Warning: Track {idx} has zero or negative duration, skipping.") continue track_name = track.get('tn', 'Unknown') # ---------------------------------------------------------------------- # 5a. Build filename # ---------------------------------------------------------------------- clean_filename = build_filename(track, idx, extension, args) output_path = os.path.join(output_directory, clean_filename) # ---------------------------------------------------------------------- # 5b. Handle existing files # ---------------------------------------------------------------------- if args.skip_existing and os.path.exists(output_path): print(f"Skipping track {idx}: {output_path} already exists.") continue # ---------------------------------------------------------------------- # 5c. Build metadata # ---------------------------------------------------------------------- metadata = build_metadata_dict(track, idx, input_metadata, args) # ---------------------------------------------------------------------- # 5d. Build and execute FFmpeg command # ---------------------------------------------------------------------- cmd = build_ffmpeg_command( input_file, start_seconds, duration_seconds, output_path, stream_info, output_format, args.transcode_to, args.drop_video, args.drop_subs, metadata=metadata ) print(f"Extracting track {idx}: {track_name} " f"({format_time(start_seconds)} - {format_time(end_seconds)})") result = subprocess.run(cmd, capture_output=True, text=True, check=False) if result.returncode != 0: print(f"ERROR extracting track {idx}:") print(result.stderr) else: print(f" -> Saved to: {output_path}")