Initial commit. Basic featues are implemented

This commit is contained in:
2026-07-28 16:25:11 +05:00
commit 842e3bf1b0
12 changed files with 1190 additions and 0 deletions
+130
View File
@@ -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 commandline 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}")