Switch default output to MP3, fix AAC for iPod Nano 7G, auto-remove gray source rows after convert
- Separate AAC and MP3 ffmpeg paths: remove +global_header and +genpts from MP3 - Clamp AAC bitrate to 160 kbps (iPod Nano 7G max); warn on override - Make mp3 the default format in UI, converter, and CLI - Auto-remove converted source tracks from library UI (keep files on disk) - Bulk delete_tracks with single scan+sync (progress support) - Async duplicate removal with progress via WorkerThread
This commit is contained in:
parent
63d72adae6
commit
340b0dea9e
@ -42,7 +42,7 @@ class AudioConverter:
|
|||||||
def convert_to_ipod_format(self,
|
def convert_to_ipod_format(self,
|
||||||
track_info: TrackInfo,
|
track_info: TrackInfo,
|
||||||
input_file: str,
|
input_file: str,
|
||||||
format: str = "m4a",
|
format: str = "mp3",
|
||||||
audio_bitrate: int = 256,
|
audio_bitrate: int = 256,
|
||||||
overwrite: bool = False) -> str:
|
overwrite: bool = False) -> str:
|
||||||
"""
|
"""
|
||||||
@ -90,24 +90,27 @@ class AudioConverter:
|
|||||||
logger.warning(f"Existing file is too small ({file_size} bytes), re-converting")
|
logger.warning(f"Existing file is too small ({file_size} bytes), re-converting")
|
||||||
os.remove(output_file)
|
os.remove(output_file)
|
||||||
|
|
||||||
# iPod Nano 7 is strict about AAC parameters:
|
if format == "m4a":
|
||||||
|
# iPod Nano 7 AAC requirements:
|
||||||
# - AAC-LC profile only (HE-AAC causes playback issues/crashes)
|
# - AAC-LC profile only (HE-AAC causes playback issues/crashes)
|
||||||
# - 44100 Hz sample rate (firmware expects this exactly)
|
# - 44100 Hz sample rate (firmware expects this exactly)
|
||||||
|
# - Max 160 kbps for stereo (higher may cause truncation/crackle)
|
||||||
# - +global_header: required for iPod AAC decoder init
|
# - +global_header: required for iPod AAC decoder init
|
||||||
# - +genpts: fix timestamp gaps from web sources
|
# - +genpts: fix timestamp gaps from web sources
|
||||||
codec = "aac" if format == "m4a" else "libmp3lame"
|
m4a_bitrate = min(audio_bitrate, 160)
|
||||||
|
if audio_bitrate > 160:
|
||||||
|
logger.warning(
|
||||||
|
"Bitrate %d kbps exceeds iPod Nano 7 max (160 kbps), clamping to 160",
|
||||||
|
audio_bitrate,
|
||||||
|
)
|
||||||
cmd = [
|
cmd = [
|
||||||
"ffmpeg", "-y", "-fflags", "+genpts", "-i", input_file,
|
"ffmpeg", "-y", "-fflags", "+genpts", "-i", input_file,
|
||||||
"-c:a", codec,
|
"-c:a", "aac",
|
||||||
"-b:a", f"{audio_bitrate}k",
|
"-b:a", f"{m4a_bitrate}k",
|
||||||
"-ar", "44100",
|
"-ar", "44100",
|
||||||
"-ac", "2",
|
"-ac", "2",
|
||||||
"-flags", "+global_header",
|
"-flags", "+global_header",
|
||||||
]
|
]
|
||||||
|
|
||||||
# For M4A/AAC: force LC profile and use libfdk_aac if available (best quality),
|
|
||||||
# otherwise fall back to built-in aac with profile flag
|
|
||||||
if format == "m4a":
|
|
||||||
# Try libfdk_aac first (best compatibility)
|
# Try libfdk_aac first (best compatibility)
|
||||||
fdkaac_cmd = cmd + [
|
fdkaac_cmd = cmd + [
|
||||||
"-map", "0:a",
|
"-map", "0:a",
|
||||||
@ -129,7 +132,6 @@ class AudioConverter:
|
|||||||
if "libfdk_aac" in test.stdout:
|
if "libfdk_aac" in test.stdout:
|
||||||
cmd = fdkaac_cmd
|
cmd = fdkaac_cmd
|
||||||
else:
|
else:
|
||||||
# Fallback: built-in aac with LC profile
|
|
||||||
cmd += [
|
cmd += [
|
||||||
"-map", "0:a",
|
"-map", "0:a",
|
||||||
"-map", "0:v?",
|
"-map", "0:v?",
|
||||||
@ -154,7 +156,15 @@ class AudioConverter:
|
|||||||
output_file,
|
output_file,
|
||||||
]
|
]
|
||||||
else:
|
else:
|
||||||
cmd += [
|
# MP3 for iPod Nano 7G: reliable, no special decoder flags needed.
|
||||||
|
# libmp3lame is mature and produces streams safe for hardware decoders.
|
||||||
|
# No +global_header — that flag is AAC-only and confuses MP3 playback.
|
||||||
|
cmd = [
|
||||||
|
"ffmpeg", "-y", "-i", input_file,
|
||||||
|
"-c:a", "libmp3lame",
|
||||||
|
"-b:a", f"{audio_bitrate}k",
|
||||||
|
"-ar", "44100",
|
||||||
|
"-ac", "2",
|
||||||
"-map", "0:a",
|
"-map", "0:a",
|
||||||
"-map", "0:t?",
|
"-map", "0:t?",
|
||||||
"-id3v2_version", "3",
|
"-id3v2_version", "3",
|
||||||
|
|||||||
@ -155,7 +155,7 @@ class YouTubeToIPodCLI:
|
|||||||
def convert(self,
|
def convert(self,
|
||||||
tracks: List[TrackInfo],
|
tracks: List[TrackInfo],
|
||||||
output_dir: str,
|
output_dir: str,
|
||||||
format: str = "m4a",
|
format: str = "mp3",
|
||||||
quality: int = 256,
|
quality: int = 256,
|
||||||
video: bool = False,
|
video: bool = False,
|
||||||
resolution: str = "640x480") -> List[Tuple[TrackInfo, str]]:
|
resolution: str = "640x480") -> List[Tuple[TrackInfo, str]]:
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user