From 316b4708c698c75b4eb8c8e4b254d297b96af1a5 Mon Sep 17 00:00:00 2001 From: Keith Edmunds Date: Sun, 17 Aug 2025 18:26:53 +0100 Subject: [PATCH] Check import filetype; install black --- app/file_importer.py | 32 ++++++++++++++++++++++++++++---- app/helpers.py | 14 +++++++++++++- pyproject.toml | 4 ++++ uv.lock | 2 ++ 4 files changed, 47 insertions(+), 5 deletions(-) diff --git a/app/file_importer.py b/app/file_importer.py index 4b261d3..da7778d 100644 --- a/app/file_importer.py +++ b/app/file_importer.py @@ -35,6 +35,7 @@ from classes import ( ) from config import Config from helpers import ( + audio_file_extension, file_is_unreadable, get_tags, show_OK, @@ -202,8 +203,9 @@ class FileImporter: self.sort_track_match_data(tfd) selection = self.get_user_choices(tfd) if self.process_selection(tfd, selection): - if self.validate_file_data(tfd): - tfd.import_this_file = True + if self.extension_check(tfd): + if self.validate_file_data(tfd): + tfd.import_this_file = True return tfd @@ -237,6 +239,26 @@ class FileImporter: return True + def extension_check(self, tfd: TrackFileData) -> bool: + """ + If we are replacing an existing file, check that the correct file + extension of the replacement file matches the existing file + extension and return True if it does (or if there is no exsting + file), else False. + """ + + if not tfd.file_path_to_remove: + return True + + if tfd.file_path_to_remove.endswith(audio_file_extension(tfd.source_path)): + return True + + tfd.error = ( + f"Existing file ({tfd.file_path_to_remove}) has a different " + f"extension to replacement file ({tfd.source_path})" + ) + return False + def find_similar(self, tfd: TrackFileData) -> None: """ - Search title in existing tracks @@ -445,7 +467,8 @@ class FileImporter: if tfd.track_id == 0 and tfd.destination_path != tfd.file_path_to_remove: while os.path.exists(tfd.destination_path): msg = ( - f"New import requested but default destination path ({tfd.destination_path})" + "New import requested but default destination path" + f" ({tfd.destination_path})" " already exists. Click OK and choose where to save this track" ) show_OK(title="Desintation path exists", msg=msg, parent=None) @@ -627,7 +650,8 @@ class DoTrackImport(QThread): f"Importing {os.path.basename(self.import_file_path)}", 5000 ) - # Get audio metadata in this thread rather than calling function to save interactive time + # Get audio metadata in this thread rather than calling + # function to save interactive time self.audio_metadata = helpers.get_audio_metadata(self.import_file_path) # Remove old file if so requested diff --git a/app/helpers.py b/app/helpers.py index 2971c08..c710554 100644 --- a/app/helpers.py +++ b/app/helpers.py @@ -13,6 +13,7 @@ import tempfile from PyQt6.QtWidgets import QInputDialog, QMainWindow, QMessageBox, QWidget # Third party imports +import filetype from mutagen.flac import FLAC # type: ignore from mutagen.mp3 import MP3 # type: ignore from pydub import AudioSegment, effects @@ -50,6 +51,14 @@ def ask_yes_no( return button == QMessageBox.StandardButton.Yes +def audio_file_extension(fpath: str) -> str | None: + """ + Return the correct extension for this type of file. + """ + + return filetype.guess(fpath).extension + + def fade_point( audio_segment: AudioSegment, fade_threshold: float = 0.0, @@ -72,7 +81,7 @@ def fade_point( fade_threshold = max_vol while ( - audio_segment[trim_ms : trim_ms + chunk_size].dBFS < fade_threshold + audio_segment[trim_ms: trim_ms + chunk_size].dBFS < fade_threshold and trim_ms > 0 ): # noqa W503 trim_ms -= chunk_size @@ -94,6 +103,9 @@ def file_is_unreadable(path: Optional[str]) -> bool: def get_audio_segment(path: str) -> Optional[AudioSegment]: + if not path.endswith(audio_file_extension(path)): + return None + try: if path.endswith(".mp3"): return AudioSegment.from_mp3(path) diff --git a/pyproject.toml b/pyproject.toml index 9a0907a..a26b38f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -34,6 +34,7 @@ dependencies = [ "dogpile-cache>=1.3.4", "pdbpp>=0.10.3", "filetype>=1.2.0", + "black>=25.1.0", ] [dependency-groups] @@ -64,6 +65,9 @@ python_version = 3.11 warn_unused_configs = true disallow_incomplete_defs = true +[tool.pylsp.plugins.pycodestyle] +maxLineLength = 88 + [tool.pytest.ini_options] addopts = "--exitfirst --showlocals --capture=no" pythonpath = [".", "app"] diff --git a/uv.lock b/uv.lock index 25114cd..b6ce2e8 100644 --- a/uv.lock +++ b/uv.lock @@ -492,6 +492,7 @@ dependencies = [ { name = "alchemical" }, { name = "alembic" }, { name = "audioop-lts" }, + { name = "black" }, { name = "colorlog" }, { name = "dogpile-cache" }, { name = "filetype" }, @@ -538,6 +539,7 @@ requires-dist = [ { name = "alchemical", specifier = ">=1.0.2" }, { name = "alembic", specifier = ">=1.14.0" }, { name = "audioop-lts", specifier = ">=0.2.1" }, + { name = "black", specifier = ">=25.1.0" }, { name = "colorlog", specifier = ">=6.9.0" }, { name = "dogpile-cache", specifier = ">=1.3.4" }, { name = "filetype", specifier = ">=1.2.0" },