Files
audio_splitter/audio_splitter/core.py
T
max 02b730b082
Build and Push Docker Image / build-and-push-backend (pull_request) Failing after 19m21s
Build and Push Docker Image / build-and-push-frontend (pull_request) Successful in 54s
Build and Push Docker Image / notify-deployment-server (pull_request) Successful in 11s
CHANGE: maps codec/container/file_extension more accurate
2026-08-19 11:12:48 +05:00

131 lines
5.6 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""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, input_file=input_file)
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}")