mirror of
https://github.com/myronblair/kino-app
synced 2026-07-28 13:33:49 -05:00
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:
+15
-11
@@ -132,7 +132,10 @@ def _read_tags(path: Path) -> Dict:
|
|||||||
|
|
||||||
|
|
||||||
def scan_music() -> List[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():
|
if not MUSIC_DIR.is_dir():
|
||||||
return []
|
return []
|
||||||
out = []
|
out = []
|
||||||
@@ -145,23 +148,24 @@ def scan_music() -> List[Dict]:
|
|||||||
for f in sorted(album_dir.iterdir()):
|
for f in sorted(album_dir.iterdir()):
|
||||||
if not f.is_file() or f.suffix.lower() not in AUDIO_EXTS:
|
if not f.is_file() or f.suffix.lower() not in AUDIO_EXTS:
|
||||||
continue
|
continue
|
||||||
tags = _read_tags(f)
|
track_number = None
|
||||||
track_number = tags["track_number"]
|
title = f.stem
|
||||||
title = tags["title"]
|
|
||||||
if not title:
|
|
||||||
fm = _TRACK_NUM_RE.match(f.stem)
|
fm = _TRACK_NUM_RE.match(f.stem)
|
||||||
if fm:
|
if fm:
|
||||||
track_number = track_number or int(fm.group(1))
|
track_number = int(fm.group(1))
|
||||||
title = fm.group(2)
|
title = fm.group(2)
|
||||||
else:
|
|
||||||
title = f.stem
|
|
||||||
out.append({
|
out.append({
|
||||||
"artist": tags["artist"] or artist_dir.name,
|
"artist": artist_dir.name,
|
||||||
"album": tags["album"] or album_dir.name,
|
"album": album_dir.name,
|
||||||
"title": _clean_title(title) or title,
|
"title": _clean_title(title) or title,
|
||||||
"track_number": track_number,
|
"track_number": track_number,
|
||||||
"duration_seconds": round(tags["duration"], 1),
|
"duration_seconds": 0,
|
||||||
"storage_path": str(f),
|
"storage_path": str(f),
|
||||||
"size_bytes": f.stat().st_size,
|
"size_bytes": f.stat().st_size,
|
||||||
})
|
})
|
||||||
return out
|
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))
|
||||||
|
|||||||
+6
-3
@@ -1431,10 +1431,13 @@ async def scan_import(payload: dict, user: dict = Depends(require_admin)):
|
|||||||
for it in items:
|
for it in items:
|
||||||
if await db.tracks.find_one({"storage_path": it["storage_path"]}, {"_id": 1}):
|
if await db.tracks.find_one({"storage_path": it["storage_path"]}, {"_id": 1}):
|
||||||
continue
|
continue
|
||||||
|
tags = await asyncio.to_thread(library_scan.tags_for_file, it["storage_path"])
|
||||||
track = Track(
|
track = Track(
|
||||||
title=it.get("title") or "Untitled", artist=it.get("artist") or "Unknown Artist",
|
title=tags.get("title") or it.get("title") or "Untitled",
|
||||||
album=it.get("album") or "Unknown Album", track_number=it.get("track_number"),
|
artist=tags.get("artist") or it.get("artist") or "Unknown Artist",
|
||||||
duration_seconds=it.get("duration_seconds") or 0, storage_path=it["storage_path"],
|
album=tags.get("album") or it.get("album") or "Unknown Album",
|
||||||
|
track_number=tags.get("track_number") or it.get("track_number"),
|
||||||
|
duration_seconds=round(tags.get("duration") or 0, 1), storage_path=it["storage_path"],
|
||||||
).model_dump()
|
).model_dump()
|
||||||
await db.tracks.insert_one(track)
|
await db.tracks.insert_one(track)
|
||||||
created += 1
|
created += 1
|
||||||
|
|||||||
Reference in New Issue
Block a user