Initial commit. Basic featues are implemented
This commit is contained in:
@@ -0,0 +1,130 @@
|
||||
"""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}")
|
||||
Reference in New Issue
Block a user