FIX: codec/container/file_extension autodetection when default values are used
This commit is contained in:
+23
-15
@@ -6,7 +6,7 @@ import subprocess
|
||||
import sys
|
||||
from typing import List, Dict, Any
|
||||
|
||||
from .constants import FORMAT_INFO
|
||||
from .constants import FORMAT_INFO, CODEC_TO_EXTENSION_MAP
|
||||
from .ffmpeg import (
|
||||
get_audio_duration,
|
||||
get_stream_info,
|
||||
@@ -34,10 +34,11 @@ def split_audio(input_file: str, output_directory: str, tracks: List[Dict[str, A
|
||||
output_directory: Directory where output files will be saved.
|
||||
tracks: List of dicts, each containing parsed fields (ts, tn, an, ...).
|
||||
args: Parsed command‑line arguments (namespace) with attributes:
|
||||
- container: output container name
|
||||
- container: output container name (or None for auto-detect)
|
||||
- audio_codec: audio codec (copy or encoder)
|
||||
- video_codec: video codec (copy or encoder)
|
||||
- subtitle_codec: subtitle codec (copy or encoder)
|
||||
- video_quality: integer or None
|
||||
- drop_video: bool
|
||||
- drop_subs: bool
|
||||
- number_tracks: bool
|
||||
@@ -77,7 +78,6 @@ def split_audio(input_file: str, output_directory: str, tracks: List[Dict[str, A
|
||||
# --------------------------------------------------------------------------
|
||||
# 2. Determine output container
|
||||
# --------------------------------------------------------------------------
|
||||
# Use args.container if provided, otherwise auto-detect
|
||||
user_container = getattr(args, 'container', None)
|
||||
output_container = determine_output_format(
|
||||
stream_info,
|
||||
@@ -104,13 +104,30 @@ def split_audio(input_file: str, output_directory: str, tracks: List[Dict[str, A
|
||||
raise RuntimeError(f"Compatibility error: {e}")
|
||||
|
||||
# --------------------------------------------------------------------------
|
||||
# 4. Parse timestamps
|
||||
# 4. Determine output audio codec and extension
|
||||
# --------------------------------------------------------------------------
|
||||
# Determine the audio codec that will be used in the output
|
||||
if args.audio_codec != 'copy':
|
||||
output_audio_codec = args.audio_codec
|
||||
else:
|
||||
output_audio_codec = stream_info.get('audio_codec', '')
|
||||
|
||||
# Choose extension based on audio codec if possible, otherwise fallback to container default
|
||||
if output_audio_codec in CODEC_TO_EXTENSION_MAP:
|
||||
extension = CODEC_TO_EXTENSION_MAP[output_audio_codec]
|
||||
else:
|
||||
extension = FORMAT_INFO.get(output_container, {}).get('ext', '.mkv')
|
||||
|
||||
print(f"Output extension: {extension}")
|
||||
|
||||
# --------------------------------------------------------------------------
|
||||
# 5. Parse timestamps
|
||||
# --------------------------------------------------------------------------
|
||||
track_times = parse_track_timestamps(tracks)
|
||||
resolved_times = resolve_end_times(track_times, total_duration)
|
||||
|
||||
# --------------------------------------------------------------------------
|
||||
# 5. Fetch original metadata (for fallbacks)
|
||||
# 6. Fetch original metadata (for fallbacks)
|
||||
# --------------------------------------------------------------------------
|
||||
input_metadata = get_metadata(input_file)
|
||||
original_album = input_metadata.get('album')
|
||||
@@ -127,13 +144,12 @@ def split_audio(input_file: str, output_directory: str, tracks: List[Dict[str, A
|
||||
print(f" Stream {idx}: '{comment}'")
|
||||
|
||||
# --------------------------------------------------------------------------
|
||||
# 6. Handle attached picture (cover art)
|
||||
# 7. Handle attached picture (cover art)
|
||||
# --------------------------------------------------------------------------
|
||||
cover_image_path = getattr(args, 'cover_image', None)
|
||||
if cover_image_path and not os.path.exists(cover_image_path):
|
||||
cover_image_path = None
|
||||
|
||||
# If not provided via args (CLI case), try to detect and extract
|
||||
if not cover_image_path and not args.drop_video and stream_info.get('has_video'):
|
||||
if is_attached_picture(input_file):
|
||||
cover_image_path = os.path.join(output_directory, 'cover.png')
|
||||
@@ -142,12 +158,6 @@ def split_audio(input_file: str, output_directory: str, tracks: List[Dict[str, A
|
||||
else:
|
||||
cover_image_path = None
|
||||
|
||||
# --------------------------------------------------------------------------
|
||||
# 7. Determine file extension
|
||||
# --------------------------------------------------------------------------
|
||||
extension_info = FORMAT_INFO.get(output_container, {})
|
||||
extension = extension_info.get('ext', '.mkv')
|
||||
|
||||
# --------------------------------------------------------------------------
|
||||
# 8. Process each track
|
||||
# --------------------------------------------------------------------------
|
||||
@@ -199,7 +209,6 @@ def split_audio(input_file: str, output_directory: str, tracks: List[Dict[str, A
|
||||
drop_subs=args.drop_subs,
|
||||
)
|
||||
|
||||
print(cmd)
|
||||
print(f"Extracting track {idx}: {track_name} "
|
||||
f"({format_time(start_seconds)} - {format_time(end_seconds)})")
|
||||
|
||||
@@ -208,7 +217,6 @@ def split_audio(input_file: str, output_directory: str, tracks: List[Dict[str, A
|
||||
if result.returncode != 0:
|
||||
print(f"ERROR extracting track {idx}:")
|
||||
print(result.stderr)
|
||||
# Optionally stop on first error? We'll continue.
|
||||
else:
|
||||
print(f" -> Saved to: {output_path}")
|
||||
|
||||
|
||||
+109
-41
@@ -31,7 +31,6 @@ from .defaults import (
|
||||
DEFAULT_REPLACE_BAD_CHARS,
|
||||
DEFAULT_SKIP_EXISTING,
|
||||
DEFAULT_DELETE_ORIGINAL,
|
||||
DEFAULT_TRANSCODE_TO,
|
||||
)
|
||||
from .core import split_audio
|
||||
from .tracklist import read_tracklist, parse_format
|
||||
@@ -48,18 +47,26 @@ def main():
|
||||
parser.add_argument('tracklist_file', help='Tracklist file')
|
||||
|
||||
# Container and codec options
|
||||
parser.add_argument('--container', default=DEFAULT_FORMAT,
|
||||
help="Output container format (default: %(default)s)")
|
||||
parser.add_argument('--audio-codec', default='copy',
|
||||
help="Audio codec (copy or encoder name, e.g., libopus)")
|
||||
parser.add_argument('--video-codec', default='copy',
|
||||
help="Video codec (copy or encoder name, e.g., libx264)")
|
||||
parser.add_argument('--subtitle-codec', default='copy',
|
||||
help="Subtitle codec (copy or encoder name, e.g., srt)")
|
||||
parser.add_argument('--drop-video', action='store_true', default=DEFAULT_DROP_VIDEO,
|
||||
help="Drop video streams")
|
||||
parser.add_argument('--drop-subs', action='store_true', default=DEFAULT_DROP_SUBS,
|
||||
help="Drop subtitle streams")
|
||||
parser.add_argument(
|
||||
'--container',
|
||||
default=None,
|
||||
help="Output container format (auto-detect if not specified)"
|
||||
)
|
||||
parser.add_argument(
|
||||
'--audio-codec',
|
||||
default='copy',
|
||||
help="Audio codec (copy or encoder name, e.g., libopus)"
|
||||
)
|
||||
parser.add_argument(
|
||||
'--video-codec',
|
||||
default='copy',
|
||||
help="Video codec (copy or encoder name, e.g., libx264)"
|
||||
)
|
||||
parser.add_argument(
|
||||
'--subtitle-codec',
|
||||
default='copy',
|
||||
help="Subtitle codec (copy or encoder name, e.g., srt)"
|
||||
)
|
||||
parser.add_argument(
|
||||
'--video-quality', '-vq',
|
||||
type=int,
|
||||
@@ -67,44 +74,105 @@ def main():
|
||||
help="Video quality (integer, encoder-specific; usually 1-31, lower=better). "
|
||||
"If omitted, FFmpeg default is used."
|
||||
)
|
||||
parser.add_argument(
|
||||
'--drop-video',
|
||||
action='store_true',
|
||||
default=DEFAULT_DROP_VIDEO,
|
||||
help="Drop video streams"
|
||||
)
|
||||
parser.add_argument(
|
||||
'--drop-subs',
|
||||
action='store_true',
|
||||
default=DEFAULT_DROP_SUBS,
|
||||
help="Drop subtitle streams"
|
||||
)
|
||||
|
||||
# Filename options
|
||||
parser.add_argument('--number-tracks', action='store_true', default=DEFAULT_NUMBER_TRACKS,
|
||||
help="Prepend track numbers")
|
||||
parser.add_argument('--output-template', default=DEFAULT_OUTPUT_TEMPLATE,
|
||||
help="Output filename template (default: %(default)s)")
|
||||
parser.add_argument('--replace-bad-chars', action='store_true', default=DEFAULT_REPLACE_BAD_CHARS,
|
||||
help="Replace bad characters")
|
||||
parser.add_argument('--replacement-char', default=DEFAULT_REPLACEMENT_CHAR,
|
||||
help="Replacement character (default: %(default)s)")
|
||||
parser.add_argument('--bad-chars', default=DEFAULT_BAD_CHARS,
|
||||
help="Bad characters to replace (default: %(default)s)")
|
||||
parser.add_argument('--skip-existing', action='store_true', default=DEFAULT_SKIP_EXISTING,
|
||||
help="Skip existing output files")
|
||||
parser.add_argument(
|
||||
'--number-tracks',
|
||||
action='store_true',
|
||||
default=DEFAULT_NUMBER_TRACKS,
|
||||
help="Prepend track numbers"
|
||||
)
|
||||
parser.add_argument(
|
||||
'--output-template',
|
||||
default=DEFAULT_OUTPUT_TEMPLATE,
|
||||
help="Output filename template (default: %(default)s)"
|
||||
)
|
||||
parser.add_argument(
|
||||
'--replace-bad-chars',
|
||||
action='store_true',
|
||||
default=DEFAULT_REPLACE_BAD_CHARS,
|
||||
help="Replace bad characters"
|
||||
)
|
||||
parser.add_argument(
|
||||
'--replacement-char',
|
||||
default=DEFAULT_REPLACEMENT_CHAR,
|
||||
help="Replacement character (default: %(default)s)"
|
||||
)
|
||||
parser.add_argument(
|
||||
'--bad-chars',
|
||||
default=DEFAULT_BAD_CHARS,
|
||||
help="Bad characters to replace (default: %(default)s)"
|
||||
)
|
||||
parser.add_argument(
|
||||
'--skip-existing',
|
||||
action='store_true',
|
||||
default=DEFAULT_SKIP_EXISTING,
|
||||
help="Skip existing output files"
|
||||
)
|
||||
|
||||
# Metadata
|
||||
parser.add_argument('--album', default=DEFAULT_ALBUM, help="Album name")
|
||||
parser.add_argument('--comment', default=DEFAULT_COMMENT, help="Comment")
|
||||
parser.add_argument('--no-comment', action='store_true', default=DEFAULT_NO_COMMENT,
|
||||
help="Ignore comment")
|
||||
parser.add_argument('--comment-stream', type=int, default=DEFAULT_COMMENT_STREAM,
|
||||
help="Comment stream index")
|
||||
parser.add_argument('--merge-comments', action='store_true', default=DEFAULT_MERGE_COMMENTS,
|
||||
help="Merge all comments")
|
||||
parser.add_argument('--comment-separator', default=DEFAULT_COMMENT_SEPARATOR,
|
||||
help="Separator for merged comments (default: %(default)s)")
|
||||
parser.add_argument(
|
||||
'--no-comment',
|
||||
action='store_true',
|
||||
default=DEFAULT_NO_COMMENT,
|
||||
help="Ignore comment"
|
||||
)
|
||||
parser.add_argument(
|
||||
'--comment-stream',
|
||||
type=int,
|
||||
default=DEFAULT_COMMENT_STREAM,
|
||||
help="Comment stream index"
|
||||
)
|
||||
parser.add_argument(
|
||||
'--merge-comments',
|
||||
action='store_true',
|
||||
default=DEFAULT_MERGE_COMMENTS,
|
||||
help="Merge all comments"
|
||||
)
|
||||
parser.add_argument(
|
||||
'--comment-separator',
|
||||
default=DEFAULT_COMMENT_SEPARATOR,
|
||||
help="Separator for merged comments (default: %(default)s)"
|
||||
)
|
||||
|
||||
# Tracklist format
|
||||
parser.add_argument('--tracklist-format', default=DEFAULT_TRACKLIST_FORMAT,
|
||||
help="Tracklist format (default: %(default)s)")
|
||||
parser.add_argument(
|
||||
'--tracklist-format',
|
||||
default=DEFAULT_TRACKLIST_FORMAT,
|
||||
help="Tracklist format (default: %(default)s)"
|
||||
)
|
||||
|
||||
# Other
|
||||
parser.add_argument('--delete-original', action='store_true', default=DEFAULT_DELETE_ORIGINAL,
|
||||
help="Delete original file after split")
|
||||
parser.add_argument('--dry-run', action='store_true',
|
||||
help="Parse and display tracklist without splitting")
|
||||
parser.add_argument('--output-dir', '-o', default=None,
|
||||
help="Output directory (default: <input_basename>_splits)")
|
||||
parser.add_argument(
|
||||
'--delete-original',
|
||||
action='store_true',
|
||||
default=DEFAULT_DELETE_ORIGINAL,
|
||||
help="Delete original file after split"
|
||||
)
|
||||
parser.add_argument(
|
||||
'--dry-run',
|
||||
action='store_true',
|
||||
help="Parse and display tracklist without splitting"
|
||||
)
|
||||
parser.add_argument(
|
||||
'--output-dir', '-o',
|
||||
default=None,
|
||||
help="Output directory (default: <input_basename>_splits)"
|
||||
)
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
|
||||
Reference in New Issue
Block a user