🐛 Stream codec is now honored when picking track file extension (#336)

This commit is contained in:
Francesco
2026-04-25 00:35:45 +02:00
committed by GitHub
parent 4b6b23225a
commit 658e4a81ab
2 changed files with 47 additions and 8 deletions
+3 -1
View File
@@ -141,7 +141,9 @@ class Downloader:
)
return None, False
urls, _ = parse_track_stream(stream)
urls, actual_ext = parse_track_stream(stream)
if filename.suffix.lower() != actual_ext:
filename = filename.with_suffix(actual_ext)
download_path = self.download_path / filename
quality = track_qualities_color[stream.audioQuality]
+44 -7
View File
@@ -2,9 +2,18 @@ import subprocess
from pathlib import Path
def run(cmd: list[str]):
"""Run process without printing to terminal"""
subprocess.run(cmd, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
class FFmpegError(RuntimeError):
pass
def run(cmd: list[str]) -> subprocess.CompletedProcess:
"""Run a process; raise `FFmpegError` on non-zero exit with stderr."""
r = subprocess.run(cmd, capture_output=True, text=True)
if r.returncode != 0:
raise FFmpegError(
f"{cmd[0]} failed (rc={r.returncode}): {r.stderr.strip()}"
)
return r
def is_ffmpeg_installed() -> bool:
@@ -13,10 +22,25 @@ def is_ffmpeg_installed() -> bool:
try:
run(["ffmpeg", "-version"])
return True
except FileNotFoundError:
except (FileNotFoundError, FFmpegError):
return False
def _probe_audio_codec(source: Path) -> str:
"""Return first audio stream's codec_name, or "" if ffprobe is unavailable."""
try:
r = run([
"ffprobe", "-v", "error",
"-select_streams", "a:0",
"-show_entries", "stream=codec_name",
"-of", "default=noprint_wrappers=1:nokey=1",
str(source),
])
return r.stdout.strip()
except (FileNotFoundError, FFmpegError):
return ""
def convert_to_mp4(source: Path) -> Path:
output_path = source.with_suffix(".mp4")
@@ -29,13 +53,26 @@ def convert_to_mp4(source: Path) -> Path:
def extract_flac(source: Path) -> Path:
"""
Extracts flac audio from mp4 container
Extract FLAC audio from an MP4 container.
Tidal can serve AAC-in-MP4 for tracks without a lossless master, so the
input may not actually contain FLAC.
"""
codec = _probe_audio_codec(source)
if codec and codec != "flac":
target = source.with_suffix(".m4a")
if target != source:
source.replace(target)
return target
target = source.with_suffix(".flac")
tmp = source.with_suffix(".tmp.flac")
run(["ffmpeg", "-y", "-i", str(source), "-c", "copy", str(tmp)])
tmp.replace(source.with_suffix(".flac"))
tmp.replace(target)
if source != target and source.exists():
source.unlink()
return source.with_suffix(".flac")
return target