"""Source-backend value objects — framework-free. A *source* is a place tracks come from (a mounted folder, YouTube, an upload). Backends are driven adapters (``app.infrastructure.sources``); these are the shapes they speak in, and the ports they satisfy live in ``app.domain.ports``. The first backend, ``local``, is *indexable*: it enumerates files already on disk. Concrete metadata (artist/album/tags) is intentionally **not** resolved here — a source yields a file plus a minimal title; enrichment (plan §6.2) fills the rest later, so this stays a thin discovery layer (CLAUDE.md: no duplicated business logic).""" from dataclasses import dataclass from pathlib import Path @dataclass(frozen=True, slots=True) class SourceInfo: """Describes a registered source for enumeration / health (UI, admin).""" name: str label: str kind: str # "indexable" (more kinds — search/download — arrive with youtube) available: bool @dataclass(frozen=True, slots=True) class SourceFile: """A single importable file discovered by an indexable source. ``source_id`` is stable per source (the local backend uses the path relative to its root) so re-scans are idempotent — already-imported files are skipped. """ source_id: str path: Path suggested_title: str file_format: str file_size: int