Skip to content

Commit ee8b5dd

Browse files
authored
fix: use extension-only check for deleted/moved file cleanup (#1)
The watcher's on_deleted and on_moved handlers called is_audio_file() which checks os.path.isfile() - always False for files that no longer exist on disk. This meant real-time cleanup of output files never triggered when a source was deleted or renamed. Add has_audio_extension() that checks only the file extension without requiring the file to exist, and use it in the watcher handlers for paths that are no longer on disk. The periodic sync (every 5 min) already caught orphans as a fallback, but this fix enables immediate cleanup on deletion events.
1 parent 8f9060d commit ee8b5dd

4 files changed

Lines changed: 49 additions & 6 deletions

File tree

‎pyproject.toml‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
44

55
[project]
66
name = "audio-transcode-watcher"
7-
version = "0.1.2"
7+
version = "0.1.3"
88
description = "Watch a source folder and automatically transcode audio files to multiple formats"
99
readme = "README.md"
1010
license = "GPL-3.0-or-later"

‎src/audio_transcode_watcher/utils.py‎

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,8 +38,17 @@ def nfc_path(p: str) -> str:
3838
return normalized
3939

4040

41+
def has_audio_extension(path: str) -> bool:
42+
"""Check if a path has a recognized audio file extension (no existence check).
43+
44+
Use this instead of is_audio_file() when the file may no longer exist
45+
on disk (e.g. in watchdog on_deleted / on_moved handlers).
46+
"""
47+
return Path(nfc_path(path)).suffix.lower() in AUDIO_EXTENSIONS
48+
49+
4150
def is_audio_file(path: str) -> bool:
42-
"""Check if a file is a recognized audio file."""
51+
"""Check if a file is a recognized audio file (must exist on disk)."""
4352
normalized = nfc_path(path)
4453
if not os.path.isfile(normalized):
4554
return False

‎src/audio_transcode_watcher/watcher.py‎

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99

1010
from .config import Config
1111
from .sync import delete_outputs, process_source_file, safety_guard_active
12-
from .utils import is_audio_file
12+
from .utils import has_audio_extension, is_audio_file
1313

1414

1515
class AudioSyncHandler(FileSystemEventHandler):
@@ -59,8 +59,8 @@ def on_moved(self, event) -> None:
5959
if event.is_directory:
6060
return
6161

62-
# Delete outputs for old path
63-
if is_audio_file(event.src_path):
62+
# Delete outputs for old path (file no longer exists at src_path)
63+
if has_audio_extension(event.src_path):
6464
delete_outputs(event.src_path, self.config)
6565

6666
# Create outputs for new path
@@ -72,7 +72,8 @@ def on_deleted(self, event) -> None:
7272
if event.is_directory:
7373
return
7474

75-
if is_audio_file(event.src_path):
75+
# File no longer exists, so check extension only
76+
if has_audio_extension(event.src_path):
7677
delete_outputs(event.src_path, self.config)
7778

7879

‎tests/test_utils.py‎

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
from audio_transcode_watcher.utils import (
88
appears_empty_dir,
99
get_output_filename,
10+
has_audio_extension,
1011
is_audio_file,
1112
is_lossless,
1213
is_mp3,
@@ -61,6 +62,38 @@ def test_handles_empty_path(self):
6162
assert nfc_path("") == ""
6263

6364

65+
class TestHasAudioExtension:
66+
"""Tests for has_audio_extension function (no file existence check)."""
67+
68+
def test_flac_extension(self):
69+
"""Test that .flac extension is recognized."""
70+
assert has_audio_extension("/music/Artist - Song.flac") is True
71+
72+
def test_mp3_extension(self):
73+
"""Test that .mp3 extension is recognized."""
74+
assert has_audio_extension("/music/Artist - Song.mp3") is True
75+
76+
def test_m4a_extension(self):
77+
"""Test that .m4a extension is recognized."""
78+
assert has_audio_extension("/music/Artist - Song.m4a") is True
79+
80+
def test_nonexistent_file_still_recognized(self):
81+
"""Test that a nonexistent path with audio extension returns True."""
82+
assert has_audio_extension("/nonexistent/path/Song.flac") is True
83+
84+
def test_txt_extension_not_audio(self):
85+
"""Test that .txt extension is not recognized."""
86+
assert has_audio_extension("/music/readme.txt") is False
87+
88+
def test_case_insensitive(self):
89+
"""Test that extension matching is case-insensitive."""
90+
assert has_audio_extension("/music/Song.FLAC") is True
91+
92+
def test_no_extension(self):
93+
"""Test that path without extension is not recognized."""
94+
assert has_audio_extension("/music/noext") is False
95+
96+
6497
class TestIsAudioFile:
6598
"""Tests for is_audio_file function."""
6699

0 commit comments

Comments
 (0)