Speed up music scan by deferring tag reads to import time

Reading embedded ID3/MP4 tags for every file during the scan itself was
taking minutes even for a small (146-track) library, since each mutagen
open is a slow round trip over the CIFS-mounted NAS share. Scan now uses
fast filename/path parsing only; tag reads happen once, only for the
tracks actually selected for import.
This commit is contained in:
Myron Blair
2026-07-26 20:38:09 -05:00
parent 514cbad4ab
commit cf31a14ae1
2 changed files with 24 additions and 17 deletions
+18 -14
View File
@@ -132,7 +132,10 @@ def _read_tags(path: Path) -> Dict:
def scan_music() -> List[Dict]:
"""Music/<Artist>/<Album>/<NN Title.ext>. Prefers embedded tags, falls back to path/filename."""
"""Music/<Artist>/<Album>/<NN Title.ext>. Filename/path-derived only — no file reads, so this
stays fast even over a network share with a large library. Embedded tags (better titles, real
duration) are read later, only for the tracks actually selected for import — see tags_for_file().
"""
if not MUSIC_DIR.is_dir():
return []
out = []
@@ -145,23 +148,24 @@ def scan_music() -> List[Dict]:
for f in sorted(album_dir.iterdir()):
if not f.is_file() or f.suffix.lower() not in AUDIO_EXTS:
continue
tags = _read_tags(f)
track_number = tags["track_number"]
title = tags["title"]
if not title:
fm = _TRACK_NUM_RE.match(f.stem)
if fm:
track_number = track_number or int(fm.group(1))
title = fm.group(2)
else:
title = f.stem
track_number = None
title = f.stem
fm = _TRACK_NUM_RE.match(f.stem)
if fm:
track_number = int(fm.group(1))
title = fm.group(2)
out.append({
"artist": tags["artist"] or artist_dir.name,
"album": tags["album"] or album_dir.name,
"artist": artist_dir.name,
"album": album_dir.name,
"title": _clean_title(title) or title,
"track_number": track_number,
"duration_seconds": round(tags["duration"], 1),
"duration_seconds": 0,
"storage_path": str(f),
"size_bytes": f.stat().st_size,
})
return out
def tags_for_file(path_str: str) -> Dict:
"""Embedded-tag lookup for a single file — used at import time, not during scan."""
return _read_tags(Path(path_str))