From c72934c224a3511f3812f58618adaa39f82f3799 Mon Sep 17 00:00:00 2001 From: Xoconoch Date: Sat, 23 Aug 2025 11:38:25 -0600 Subject: [PATCH] fix: ENOENT file mismatch --- deezspot/libutils/taggers.py | 6 +++ deezspot/libutils/write_tags.py | 65 ++++++++++++++++++++++++++++----- 2 files changed, 62 insertions(+), 9 deletions(-) diff --git a/deezspot/libutils/taggers.py b/deezspot/libutils/taggers.py index d2a2da3..d8a3e00 100644 --- a/deezspot/libutils/taggers.py +++ b/deezspot/libutils/taggers.py @@ -183,6 +183,12 @@ def apply_tags_to_track(track: Track, metadata_dict: Dict[str, Any]) -> None: try: track.tags = metadata_dict + try: + import os + path = getattr(track, 'song_path', None) + logger.debug(f"Pre-tagging: path={repr(path)}, exists={os.path.exists(path) if path else None}") + except Exception: + pass write_tags(track) logger.debug(f"Successfully applied tags to track: {metadata_dict.get('music', 'Unknown')}") except Exception as e: diff --git a/deezspot/libutils/write_tags.py b/deezspot/libutils/write_tags.py index 5395910..2cf6ed4 100644 --- a/deezspot/libutils/write_tags.py +++ b/deezspot/libutils/write_tags.py @@ -16,9 +16,40 @@ import requests import logging import os import traceback +import time +import unicodedata logger = logging.getLogger("deezspot.taggers") +# Helper: wait for a file to appear on disk (and preferably become non-empty) +def _wait_for_file(path: str, timeout: float = 3.0, interval: float = 0.1) -> bool: + end_time = time.time() + timeout + while time.time() < end_time: + try: + if os.path.exists(path): + try: + # Consider file ready if size can be read and > 0 + if os.path.getsize(path) > 0: + return True + except OSError: + # If stat fails intermittently, but path exists, allow another cycle + pass + except Exception: + pass + time.sleep(interval) + # One last check + return os.path.exists(path) + +# Helper: safe directory snapshot for diagnostics +def _safe_dir_snapshot(path: str, limit: int = 25): + try: + dirname = os.path.dirname(path) or "." + entries = os.listdir(dirname) + return entries[:limit] + except Exception as e: + return [f""] + + def request(url): response = requests.get(url) response.raise_for_status() @@ -85,7 +116,7 @@ def __write_mp3(filepath, data): comment_text = data.get('comment', 'Downloaded by DeezSpot') tags.add(COMM(encoding=3, lang='eng', desc='', text=comment_text)) - + if data.get('composer'): tags.add(TCOM(encoding=3, text=str(data['composer']))) if data.get('copyright'): tags.add(TCOP(encoding=3, text=str(data['copyright']))) if data.get('label'): tags.add(TPUB(encoding=3, text=str(data['label']))) # Publisher/Label @@ -182,17 +213,16 @@ def __write_m4a(filepath, data): # --- Vorbis Comments (FLAC, OGG, OPUS) --- def __write_vorbis(filepath, data, audio_format_class): + # Mitigate races with late renames or slow mounts + if not _wait_for_file(filepath, timeout=3.0, interval=0.1): + logger.error(f"Vorbis tagging aborted: file not found after wait: {filepath}") + return try: tags = audio_format_class(filepath) except Exception as e: - logger.warning(f"Could not open {filepath} for Vorbis tagging ({audio_format_class.__name__}), creating new tags: {e}") - try: - instance = audio_format_class() - instance.save(filepath) - tags = audio_format_class(filepath) - except Exception as e_create: - logger.error(f"Failed to create/load {filepath} for Vorbis tagging: {e_create}") - return + logger.warning(f"Could not open {filepath} for Vorbis tagging ({audio_format_class.__name__}): {e}") + # Do not attempt to create a new OGG/Opus/FLAC container via mutagen; abort cleanly. + return tags.delete() # Clear existing tags before adding new ones @@ -270,6 +300,23 @@ def write_tags(media): logger.warning(f"No metadata (tags) found for {filepath}. Skipping tagging.") return + # Diagnostics: probe existence and normalization + try: + dirname = os.path.dirname(filepath) + basename = os.path.basename(filepath) + nfc = unicodedata.normalize('NFC', basename) + nfkc = unicodedata.normalize('NFKC', basename) + logger.debug(f"Tagging probe: path={repr(filepath)}, exists={os.path.exists(filepath)}, dir={repr(dirname)}") + logger.debug(f"Tagging probe: basename={repr(basename)}, NFC==basename? {nfc==basename}, NFKC==basename? {nfkc==basename}") + logger.debug(f"Directory sample: {_safe_dir_snapshot(filepath, limit=25)}") + except Exception: + pass + + # If the file isn't visible yet, wait briefly to avoid races + if not _wait_for_file(filepath, timeout=3.0, interval=0.1): + logger.error(f"write_tags: File not found after wait, skipping tagging: {filepath}") + return + file_ext = getattr(media, 'file_format', None) if not file_ext: logger.warning(f"File format not specified in media object for {filepath}. Attempting to guess from filepath.")