/// Local file-system node returned by the native `listAudioChildren` /// channel call (Design "Interfaces / Contracts"): either a subfolder or an /// audio file, one SAF tree level deep. Pure DTO — no behavior, so no unit /// tests are warranted for it on its own (exercised indirectly through its /// consumers, e.g. `ConstructorArbolAuto.itemsLocales`). class NodoLocal { const NodoLocal({ required this.documentId, required this.nombre, required this.esDirectorio, }); /// Opaque SAF document id, unique within the picked tree. May itself /// contain `:`/`/` (Design "Prefix stripped by length"), so callers must /// never split/parse it — only wrap it verbatim in a media id. final String documentId; /// Raw on-device filename (or folder name), NOT yet title-stripped. final String nombre; /// Whether this node is a browsable subfolder (`true`) or a playable /// audio file (`false`). final bool esDirectorio; } /// Playable local track (Phase 2 extends the Phase 1 minimal shape with /// resolved embedded metadata — Design "Data Flow"). Pure DTO — no /// behavior, so no unit tests are warranted for it on its own beyond /// construction (`pista_local_test.dart`). class PistaLocal { const PistaLocal({ required this.documentId, required this.titulo, required this.contentUri, this.artista, this.embeddedArtUri, this.bitrate, this.sampleRate, }); /// Opaque SAF document id for this track. final String documentId; /// Display title, already computed by the caller (filename minus /// extension, or a derived fallback — see `navegacion_auto.dart`). final String titulo; /// Playable `content://` URI resolved via /// `FuenteMusicaLocalAuto.uriContenidoDePista`. final String contentUri; /// Resolved embedded artist metadata, when available (Design "Interfaces /// / Contracts" — `readAudioMetadataBatch`). `null` when unresolved or /// unavailable. final String? artista; /// Resolved embedded-art `content://` URI served via the app's /// `FileProvider` cache (Design ADR-1). `null` when there is no embedded /// picture or it could not be resolved. final String? embeddedArtUri; /// Resolved bitrate in bits per second, when available. final int? bitrate; /// Resolved sample rate in Hz, only available on API 31+ (Design ADR-5). /// `null` on older API levels or when unresolved. final int? sampleRate; } /// Resolved embedded metadata for a single local track (Design "Interfaces /// / Contracts"): one entry per requested `documentId`, all fields /// individually nullable (per-field parse failures degrade gracefully /// instead of dropping the whole entry). Pure DTO — no behavior, so no /// unit tests are warranted for it on its own beyond construction /// (`pista_local_test.dart`). class MetadatosPista { const MetadatosPista({ this.titulo, this.artista, this.artUri, this.bitrate, this.sampleRate, }); /// Resolved embedded title, or `null` when absent/unparseable. final String? titulo; /// Resolved embedded artist, or `null` when absent/unparseable. final String? artista; /// Resolved embedded-art `content://` URI (Design ADR-1), or `null` when /// there is no embedded picture or it could not be resolved/served. final String? artUri; /// Resolved bitrate in bits per second, or `null` when unknown. final int? bitrate; /// Resolved sample rate in Hz (API 31+ only, Design ADR-5), or `null` /// when unknown/unsupported on this API level. final int? sampleRate; }