using DeepDrftModels.DTOs; using Models.Common; using NetBlocks.Models; namespace DeepDrftData; /// /// SQL-side track service. Repository outputs entities; this service outputs DTOs via /// TrackConverter. In-process consumers (UnifiedTrackService, CLI, DeepDrftPublic) all /// receive DTOs. /// public interface ITrackService { Task> GetById(long id); Task> GetByEntryKey(string entryKey); /// /// Returns a single track chosen uniformly at random, or null when the library is empty /// (a valid state, not a failure). Backs the public "Stream Now" instant-play feature. /// Task> GetRandom(CancellationToken cancellationToken = default); Task>> GetAll(); Task>> GetPaged(int pageNumber, int pageSize, string? sortColumn, bool sortDescending, TrackFilter? filter = null, CancellationToken cancellationToken = default); /// All releases, title-ascending, each carrying its non-deleted track count. Task>> GetReleases(CancellationToken cancellationToken = default); /// Distinct non-null genres with track counts, genre-ascending. Task>> GetDistinctGenres(CancellationToken cancellationToken = default); /// /// Aggregate figures behind the public home hero stat row: Cut track count + per-ReleaseType Cut /// release breakdown, Mix release count, and total Mix runtime in seconds. One read for all three cards. /// Task> GetHomeStats(CancellationToken cancellationToken = default); /// /// Non-deleted tracks whose SQL duration is still null — the work list for the one-time duration /// backfill. The backfill reads each track's stored duration from the vault and writes it via /// . /// Task>> GetTracksMissingDuration(CancellationToken cancellationToken = default); /// /// Set the SQL duration for one track. Idempotent: a track whose duration is already set is left /// untouched. Backs the duration backfill. Returns the number of rows updated (0 or 1). /// Task> UpdateDuration(long id, double durationSeconds, CancellationToken cancellationToken = default); /// /// Unconditionally overwrite the SQL duration for one track. Unlike , /// this carries no null guard — it is for the replace-audio path where the track already has a /// non-null duration that must be overwritten with the new audio's value. Returns a fail Result /// when zero rows are affected (track removed between lookup and write). /// Task> SetDuration(long id, double durationSeconds, CancellationToken cancellationToken = default); /// /// Resolve the release matching + , creating /// one from when none exists. Backs the upload flow's FK /// resolution so a track lands on a shared release rather than duplicating release-cardinal data. /// Task> FindOrCreateRelease( string title, string artist, ReleaseDto releaseData, CancellationToken cancellationToken = default); /// /// Read-only peek for an existing release by its natural key, or null when none exists — a find /// with no create side-effect. Backs the upload cardinality pre-check, which must read a release's /// medium and live-track count before deciding whether to admit an upload, without creating a /// release for an upload it may reject. The returned DTO carries TrackCount. /// Task> GetReleaseByTitleAndArtist( string title, string artist, CancellationToken cancellationToken = default); Task> Create(TrackDto newTrack); Task> Update(TrackDto track); Task Delete(long id); /// Soft-delete a release row by id. Idempotent — a missing or already-deleted row is a no-op. Task DeleteRelease(long id, CancellationToken cancellationToken = default); /// /// Count of non-deleted tracks on a release. Backs the delete-cascade decision: when a track /// delete leaves a release with zero live tracks, the release is soft-deleted too. /// Task> CountLiveTracksByRelease(long releaseId, CancellationToken cancellationToken = default); }