from __future__ import annotations from pathlib import Path from typing import Iterator from .constants import MUSIC_EXTENSIONS from .db import connect from .hashing import fast_hash from .metadata import read_metadata def iter_music(root: Path) -> Iterator[Path]: for path in root.rglob("*"): if not path.is_file() or path.is_symlink() or path.name.startswith(".") or ".dedupe" in path.parts: continue if path.suffix.lower() in MUSIC_EXTENSIONS: yield path def index_file(conn, path: Path) -> None: st = path.stat() fh = fast_hash(path) meta = read_metadata(path) flags = ",".join(meta.get("version_flags", [])) conn.execute(""" INSERT INTO audio_tracks(file_path,file_size,fast_hash,duration,artist,title,album,format,bitrate,sample_rate,bit_depth,version_flags,inode,device,status) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,'active') ON CONFLICT(file_path) DO UPDATE SET file_size=excluded.file_size, fast_hash=excluded.fast_hash, duration=excluded.duration,artist=excluded.artist,title=excluded.title,album=excluded.album,format=excluded.format, bitrate=excluded.bitrate,sample_rate=excluded.sample_rate,bit_depth=excluded.bit_depth,version_flags=excluded.version_flags, inode=excluded.inode,device=excluded.device,updated_at=CURRENT_TIMESTAMP,status='active' """, (str(path),st.st_size,fh,meta.get("duration"),meta.get("artist"),meta.get("title"),meta.get("album"),meta.get("format"),meta.get("bitrate"),meta.get("sample_rate"),meta.get("bit_depth"),flags,st.st_ino,st.st_dev)) def scan(root: Path, db_path: Path) -> int: count = 0 with connect(db_path) as conn: for path in iter_music(root): index_file(conn, path); count += 1 return count