fix: ensure distroless compatibility

This commit is contained in:
Xoconoch
2025-08-23 12:01:18 -06:00
parent c72934c224
commit bd393805a8
2 changed files with 56 additions and 25 deletions

View File

@@ -69,12 +69,6 @@ AUDIO_FORMATS = {
}
}
def check_ffmpeg_available():
"""Check if FFmpeg is installed and available."""
if which("ffmpeg") is None:
logger.error("FFmpeg is not installed or not in PATH. Audio conversion is unavailable.")
return False
return True
def get_output_path(input_path, format_name):
"""Get the output path with the new extension based on the format."""
@@ -93,6 +87,7 @@ def get_output_path(input_path, format_name):
return os.path.join(dir_name, new_file_name)
def register_active_download(path):
"""
Register a file as being actively downloaded.
@@ -134,8 +129,14 @@ def convert_audio(input_path, format_name=None, bitrate=None, register_func=None
global unregister_active_download
unregister_active_download = unregister_func
# If no format specified or FFmpeg not available, return the original path
if not format_name or not check_ffmpeg_available():
# If no format specified, return the original path
if not format_name:
return input_path
# Resolve ffmpeg path explicitly (distroless-safe)
ffmpeg_path = which("ffmpeg") or "/usr/local/bin/ffmpeg"
if not os.path.exists(ffmpeg_path):
logger.error(f"FFmpeg is not available (looked for '{ffmpeg_path}'). Audio conversion is unavailable.")
return input_path
# Validate format and get format details
@@ -162,16 +163,9 @@ def convert_audio(input_path, format_name=None, bitrate=None, register_func=None
# Skip conversion if the file is already in the target format and bitrate matches (or not applicable)
if input_path.lower().endswith(format_details["extension"].lower()):
# For lossless, or if effective_bitrate matches (or no specific bitrate needed for format)
if format_details["default_bitrate"] is None: # Lossless
logger.info(f"File {input_path} is already in {format_name_upper} (lossless) format. Skipping conversion.")
return input_path
# For lossy, if no specific bitrate was relevant (already handled by effective_bitrate logic)
# This condition might be redundant if we always convert to ensure bitrate.
# Let's assume for now, if it's already the right extension, we don't re-encode unless bitrate implies so.
# However, the original logic converted if bitrate was specified even for same extension.
# To maintain similar behavior: if a bitrate is effectively set for a lossy format, we proceed.
# If effective_bitrate is None (e.g. for FLAC, WAV), and extension matches, skip.
if not effective_bitrate and format_details["default_bitrate"] is not None:
logger.info(f"File {input_path} is already in {format_name_upper} format with a suitable bitrate. Skipping conversion.")
return input_path
@@ -186,10 +180,10 @@ def convert_audio(input_path, format_name=None, bitrate=None, register_func=None
register_active_download(temp_output)
try:
cmd = ["ffmpeg", "-y", "-hide_banner", "-loglevel", "error", "-i", input_path]
cmd = [ffmpeg_path, "-y", "-hide_banner", "-loglevel", "error", "-i", input_path]
# Add bitrate parameter for lossy formats if an effective_bitrate is set
if effective_bitrate and format_details["bitrates"]: # format_details["bitrates"] implies lossy
if effective_bitrate and format_details["bitrates"]: # lossy
cmd.extend(["-b:a", effective_bitrate])
# Add codec parameter
@@ -234,6 +228,15 @@ def convert_audio(input_path, format_name=None, bitrate=None, register_func=None
logger.info(f"Successfully converted to {format_name_upper}" + (f" at {effective_bitrate}" if effective_bitrate else ""))
return output_path
except FileNotFoundError as fnf:
logger.error(f"FFmpeg executable not found at '{ffmpeg_path}'. Conversion aborted.")
if exists(temp_output):
try:
os.remove(temp_output)
except Exception:
pass
unregister_active_download(temp_output)
return input_path
except Exception as e:
logger.error(f"Error during audio conversion: {str(e)}")
# Clean up temp files

View File

@@ -17,6 +17,8 @@ from os import (
system,
replace as os_replace,
)
import subprocess
import shutil
from deezspot.models.download import (
Track,
Album,
@@ -263,8 +265,23 @@ class EASY_DW:
try:
# Step 1: First convert the OGG file to standard format (copy operation)
# Output is og_song_path_for_ogg_output
ffmpeg_cmd = f'ffmpeg -y -hide_banner -loglevel error -i "{temp_filename}" -c:a copy "{og_song_path_for_ogg_output}"'
system(ffmpeg_cmd) # Creates/overwrites og_song_path_for_ogg_output
# Resolve ffmpeg path explicitly to avoid PATH issues in distroless
ffmpeg_path = shutil.which("ffmpeg") or "/usr/local/bin/ffmpeg"
try:
result = subprocess.run(
[
ffmpeg_path, "-y", "-hide_banner", "-loglevel", "error",
"-i", temp_filename, "-c:a", "copy", og_song_path_for_ogg_output
],
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
text=True,
check=False
)
if result.returncode != 0 or not os.path.exists(og_song_path_for_ogg_output):
raise RuntimeError(f"ffmpeg remux failed (rc={result.returncode}). stderr: {result.stderr.strip()}")
except FileNotFoundError as fnf:
raise RuntimeError(f"ffmpeg not found: attempted '{ffmpeg_path}'. Ensure it is present in PATH.") from fnf
# temp_filename has been processed. Unregister and remove it.
# CURRENT_DOWNLOAD was temp_filename.
@@ -1024,16 +1041,27 @@ def download_cli(preferences: Preferences) -> None:
__not_interface = preferences.not_interface
__quality_download = preferences.quality_download
__recursive_download = preferences.recursive_download
cmd = f"deez-dw.py -so spo -l \"{__link}\" "
# Build argv list instead of shell string (distroless-safe)
argv = ["deez-dw.py", "-so", "spo", "-l", __link]
if __output_dir:
cmd += f"-o {__output_dir} "
argv += ["-o", str(__output_dir)]
if __not_interface:
cmd += f"-g "
argv += ["-g"]
if __quality_download:
cmd += f"-q {__quality_download} "
argv += ["-q", str(__quality_download)]
if __recursive_download:
cmd += f"-rd "
system(cmd)
argv += ["-rd"]
prog = shutil.which(argv[0])
if not prog:
logger.error("deez-dw.py CLI not found in PATH; cannot run download_cli in this environment.")
return
argv[0] = prog
try:
result = subprocess.run(argv, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
if result.returncode != 0:
logger.error(f"deez-dw.py exited with {result.returncode}: {result.stderr.strip()}")
except Exception as e:
logger.error(f"Failed to execute deez-dw.py: {e}")
class DW_TRACK:
def __init__(