mirror of
https://github.com/oskvr37/tiddl.git
synced 2026-06-13 04:05:08 +03:00
🐛 Stream codec is now honored when picking track file extension (#336)
This commit is contained in:
@@ -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]
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user