From 4351302a2581bd2c0f23c264bfc8014e0591d147 Mon Sep 17 00:00:00 2001 From: Daniel Harvey Date: Mon, 25 May 2026 11:35:04 -0400 Subject: [PATCH] Flip ITrackService/TrackManager to DTO output; TrackConverter is the sole entity<->DTO path across all consumers --- DeepDrftAPI/Controllers/TrackController.cs | 4 +- DeepDrftAPI/Services/UnifiedTrackService.cs | 12 +-- DeepDrftCli/Services/CliService.cs | 4 +- DeepDrftCli/Services/GuiService.cs | 15 ++-- DeepDrftData/ITrackService.cs | 17 ++-- DeepDrftData/TrackManager.cs | 78 +++++++++---------- .../Components/Pages/Tracks/TrackEdit.razor | 4 +- .../Components/Pages/Tracks/TrackList.razor | 22 +++--- DeepDrftManager/Components/_Imports.razor | 2 +- DeepDrftManager/Services/CmsTrackService.cs | 60 +++++++------- DeepDrftManager/Services/ICmsTrackService.cs | 10 +-- DeepDrftPublic.Client/Clients/TrackClient.cs | 8 +- .../Pages/TracksView.razor.cs | 8 +- .../Services/AudioPlayerService.cs | 8 +- .../Services/IPlayerService.cs | 8 +- .../Services/ITrackDataService.cs | 4 +- .../Services/StreamingAudioPlayerService.cs | 8 +- .../Services/TrackClientDataService.cs | 4 +- .../ViewModels/TracksViewModel.cs | 6 +- DeepDrftPublic/Controllers/TrackController.cs | 8 +- .../Services/TrackDirectDataService.cs | 6 +- .../Components/TrackCard.razor.cs | 6 +- .../Components/TracksGallery.razor.cs | 10 +-- 23 files changed, 156 insertions(+), 156 deletions(-) diff --git a/DeepDrftAPI/Controllers/TrackController.cs b/DeepDrftAPI/Controllers/TrackController.cs index f468084..6d7ed4e 100644 --- a/DeepDrftAPI/Controllers/TrackController.cs +++ b/DeepDrftAPI/Controllers/TrackController.cs @@ -68,7 +68,7 @@ public class TrackController : ControllerBase return Ok(result.Value); } - // POST api/track/upload: raw WAV in (multipart/form-data) + metadata → persisted TrackEntity out. + // POST api/track/upload: raw WAV in (multipart/form-data) + metadata → persisted TrackDto out. // Used by the CMS upload flow on DeepDrftManager; that host proxies the upload here so it never // touches the vault disk path or SQL directly. UnifiedTrackService owns the two-database write. // @@ -80,7 +80,7 @@ public class TrackController : ControllerBase [HttpPost("upload")] [RequestSizeLimit(1_073_741_824)] [RequestFormLimits(MultipartBodyLengthLimit = 1_073_741_824)] - public async Task> UploadTrack( + public async Task> UploadTrack( [FromForm] IFormFile? wav, [FromForm] string? trackName, [FromForm] string? artist, diff --git a/DeepDrftAPI/Services/UnifiedTrackService.cs b/DeepDrftAPI/Services/UnifiedTrackService.cs index b41da3c..260c9eb 100644 --- a/DeepDrftAPI/Services/UnifiedTrackService.cs +++ b/DeepDrftAPI/Services/UnifiedTrackService.cs @@ -1,7 +1,7 @@ using DeepDrftContent; using DeepDrftContent.Constants; using DeepDrftData; -using DeepDrftModels.Entities; +using DeepDrftModels.DTOs; using NetBlocks.Models; using FileDb = DeepDrftContent.FileDatabase.Services.FileDatabase; @@ -34,10 +34,10 @@ public class UnifiedTrackService /// /// Process a WAV into the vault, then persist its metadata to SQL. On success the returned - /// entity carries the SQL-assigned Id. If the vault write succeeds but the SQL persist fails, + /// DTO carries the SQL-assigned Id. If the vault write succeeds but the SQL persist fails, /// the audio is orphaned under EntryKey — logged loudly so it is recoverable manually. /// - public async Task> UploadAsync( + public async Task> UploadAsync( string tempFilePath, string trackName, string artist, @@ -53,12 +53,12 @@ public class UnifiedTrackService if (unpersisted is null) { _logger.LogWarning("UploadAsync: content TrackContentService returned null for {TrackName}", trackName); - return ResultContainer.CreateFailResult("Failed to process and store WAV."); + return ResultContainer.CreateFailResult("Failed to process and store WAV."); } unpersisted.CreatedByUserId = createdByUserId; - var saveResult = await _sqlTrackService.Create(unpersisted); + var saveResult = await _sqlTrackService.Create(TrackConverter.Convert(unpersisted)); if (!saveResult.Success || saveResult.Value is null) { // Vault write succeeded, SQL persist failed — audio is orphaned in the tracks vault @@ -67,7 +67,7 @@ public class UnifiedTrackService _logger.LogError( "Track persisted to vault but SQL save failed. Orphaned entry: {EntryKey}. Error: {Error}", unpersisted.EntryKey, error); - return ResultContainer.CreateFailResult($"Track was uploaded but could not be saved: {error}"); + return ResultContainer.CreateFailResult($"Track was uploaded but could not be saved: {error}"); } return saveResult; diff --git a/DeepDrftCli/Services/CliService.cs b/DeepDrftCli/Services/CliService.cs index d509eca..9a457f9 100644 --- a/DeepDrftCli/Services/CliService.cs +++ b/DeepDrftCli/Services/CliService.cs @@ -164,8 +164,8 @@ public class CliService return; } - // Add track to SQL database - var result = await _webTrackService.Create(trackEntity); + // Add track to SQL database (service layer is DTO-typed) + var result = await _webTrackService.Create(DeepDrftData.TrackConverter.Convert(trackEntity)); if (result.Success && result.Value != null) { Console.WriteLine($"✓ Track added successfully!"); diff --git a/DeepDrftCli/Services/GuiService.cs b/DeepDrftCli/Services/GuiService.cs index 6af8972..5c433b6 100644 --- a/DeepDrftCli/Services/GuiService.cs +++ b/DeepDrftCli/Services/GuiService.cs @@ -1,6 +1,7 @@ using Microsoft.Extensions.Logging; using Terminal.Gui; -using DeepDrftModels.Entities; +using DeepDrftData; +using DeepDrftModels.DTOs; using DeepDrftCli.Utils; namespace DeepDrftCli.Services; @@ -20,7 +21,7 @@ public class GuiService private ListView? _trackListView; private TextView? _statusView; private FrameView? _legendFrame; - private List _tracks = new(); + private List _tracks = new(); public GuiService( ILogger logger, @@ -541,7 +542,7 @@ public class GuiService /// /// Delete the specified track from the database /// - private async Task DeleteTrackAsync(TrackEntity trackToDelete) + private async Task DeleteTrackAsync(TrackDto trackToDelete) { try { @@ -650,8 +651,8 @@ public class GuiService return false; } - // Add to SQL database - var result = await _webTrackService.Create(trackEntity); + // Add to SQL database (service layer is DTO-typed) + var result = await _webTrackService.Create(TrackConverter.Convert(trackEntity)); if (result.Success && result.Value != null) { UpdateStatus($"✓ Track '{trackName}' by {artist} added successfully!"); @@ -676,7 +677,7 @@ public class GuiService /// /// Validate input and update existing track in database /// - private async Task ValidateAndUpdateTrackAsync(TrackEntity originalTrack, string trackName, + private async Task ValidateAndUpdateTrackAsync(TrackDto originalTrack, string trackName, string artist, string album, string genre, string releaseDate) { try @@ -709,7 +710,7 @@ public class GuiService UpdateStatus("Updating track..."); // Create updated track entity - var updatedTrack = new TrackEntity + var updatedTrack = new TrackDto { Id = originalTrack.Id, EntryKey = originalTrack.EntryKey, // Keep original entry key diff --git a/DeepDrftData/ITrackService.cs b/DeepDrftData/ITrackService.cs index 9b70750..1a13f50 100644 --- a/DeepDrftData/ITrackService.cs +++ b/DeepDrftData/ITrackService.cs @@ -1,15 +1,20 @@ -using DeepDrftModels.Entities; +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>> GetAll(); - Task>> GetPaged(int pageNumber, int pageSize, string? sortColumn, bool sortDescending, CancellationToken cancellationToken = default); - Task> Create(TrackEntity newTrack); - Task> Update(TrackEntity track); + Task> GetById(long id); + Task>> GetAll(); + Task>> GetPaged(int pageNumber, int pageSize, string? sortColumn, bool sortDescending, CancellationToken cancellationToken = default); + Task> Create(TrackDto newTrack); + Task> Update(TrackDto track); Task Delete(long id); } diff --git a/DeepDrftData/TrackManager.cs b/DeepDrftData/TrackManager.cs index bf5984b..9e5ec14 100644 --- a/DeepDrftData/TrackManager.cs +++ b/DeepDrftData/TrackManager.cs @@ -9,14 +9,15 @@ using NetBlocks.Models; namespace DeepDrftData; /// -/// SQL-side track orchestrator built on the BlazorBlocks Manager base. Two surfaces coexist: +/// SQL-side track service built on the BlazorBlocks Manager base. The layer boundary: +/// TrackRepository outputs entities; this service outputs DTOs via TrackConverter — the +/// single authoritative entity↔DTO conversion path. The ITrackService surface is DTO-typed +/// throughout; the entity never escapes the service layer. /// -/// - The DTO-shaped IManager surface (GetById → TrackDto, etc.) inherited from Manager<>. -/// - The entity-shaped ITrackService surface retained for backward compatibility with the -/// web host, CMS, and CLI — all existing controllers and pages inject ITrackService and -/// expect TrackEntity-typed results. The two GetById overloads conflict on signature, so -/// ITrackService.GetById is implemented explicitly; the base Manager.Delete satisfies -/// both interfaces because the signatures align. +/// The base Manager<> surface does not line up with ITrackService by signature (base +/// Add vs Create, base Update→Result vs Update→DTO, base Get/GetPage vs GetAll/GetPaged, +/// base GetById→TDto vs GetById→TDto?), so the query and mutation methods are implemented +/// here over Repository + TrackConverter. Only Delete(long)→Result is inherited unchanged. /// public class TrackManager : Manager, ITrackService @@ -28,39 +29,38 @@ public class TrackManager { } - // --- ITrackService implementation (entity-space; calls Repository directly) --- - - // Explicit interface implementation — IManager.GetById returns ResultContainer - // (inherited from Manager<>), so this entity-typed overload cannot coexist as a public - // member with the same name. Callers always inject ITrackService, so the explicit impl - // resolves correctly at the call site. - async Task> ITrackService.GetById(long id) + // Explicit impl: base GetById returns ResultContainer (fails on miss); the + // service contract is ResultContainer (pass with null on miss). Return types + // differ, so this cannot be a public overload of the inherited member. + async Task> ITrackService.GetById(long id) { try { var entity = await Repository.GetByIdAsync(id); - return ResultContainer.CreatePassResult(entity); + return ResultContainer.CreatePassResult( + entity is null ? null : TrackConverter.Convert(entity)); } catch (Exception e) { - return ResultContainer.CreateFailResult(e.Message); + return ResultContainer.CreateFailResult(e.Message); } } - public async Task>> GetAll() + public async Task>> GetAll() { try { var entities = await Repository.GetAllAsync(); - return ResultContainer>.CreatePassResult(entities.ToList()); + return ResultContainer>.CreatePassResult( + entities.Select(TrackConverter.Convert).ToList()); } catch (Exception e) { - return ResultContainer>.CreateFailResult(e.Message); + return ResultContainer>.CreateFailResult(e.Message); } } - public async Task>> GetPaged( + public async Task>> GetPaged( int pageNumber, int pageSize, string? sortColumn, @@ -86,52 +86,46 @@ public class TrackManager }; var page = await Repository.GetPagedAsync(parameters); - return ResultContainer>.CreatePassResult(page); + var dtoPage = PagedResult.From(page, page.Items.Select(TrackConverter.Convert)); + return ResultContainer>.CreatePassResult(dtoPage); } catch (Exception e) { - return ResultContainer>.CreateFailResult(e.Message); + return ResultContainer>.CreateFailResult(e.Message); } } - public async Task> Create(TrackEntity newTrack) + public async Task> Create(TrackDto newTrack) { try { - var added = await Repository.AddAsync(newTrack); - return ResultContainer.CreatePassResult(added); + var added = await Repository.AddAsync(TrackConverter.Convert(newTrack)); + return ResultContainer.CreatePassResult(TrackConverter.Convert(added)); } catch (Exception e) { - return ResultContainer.CreateFailResult(e.Message); + return ResultContainer.CreateFailResult(e.Message); } } - // Manager<>.Update takes TrackDto and returns Result; this Update keeps the - // entity-typed contract callers expect and returns the post-update entity for the - // existing CMS edit flow that reads back the persisted values. - /// - /// Updates the track's metadata fields and returns the DB-authoritative entity. - /// The caller's object has its UpdatedAt field - /// mutated in place by ; do not reuse it. - /// - public async Task> Update(TrackEntity track) + // Explicit impl: base Update returns Result; the service contract returns the persisted + // DTO so the CMS edit flow reads back DB-authoritative values. + async Task> ITrackService.Update(TrackDto track) { try { - await Repository.UpdateAsync(track); + await Repository.UpdateAsync(TrackConverter.Convert(track)); var updated = await Repository.GetByIdAsync(track.Id); return updated is not null - ? ResultContainer.CreatePassResult(updated) - : ResultContainer.CreateFailResult("Track not found after update."); + ? ResultContainer.CreatePassResult(TrackConverter.Convert(updated)) + : ResultContainer.CreateFailResult("Track not found after update."); } catch (Exception e) { - return ResultContainer.CreateFailResult(e.Message); + return ResultContainer.CreateFailResult(e.Message); } } - // Delete(long) is inherited from Manager<> — its Task signature already - // satisfies ITrackService.Delete, and the base implementation performs the soft delete - // via Repository.DeleteAsync. No override needed. + // Delete(long) → Result is inherited from Manager<> and satisfies ITrackService.Delete + // by signature. No override. } diff --git a/DeepDrftManager/Components/Pages/Tracks/TrackEdit.razor b/DeepDrftManager/Components/Pages/Tracks/TrackEdit.razor index e751844..e5e6ff0 100644 --- a/DeepDrftManager/Components/Pages/Tracks/TrackEdit.razor +++ b/DeepDrftManager/Components/Pages/Tracks/TrackEdit.razor @@ -90,7 +90,7 @@ @code { [Parameter] public long Id { get; set; } - private TrackEntity? _track; + private TrackDto? _track; private TrackEditForm _form = new(); private bool _loading = true; private bool _busy; @@ -199,7 +199,7 @@ public string? Genre { get; set; } public DateTime? ReleaseDate { get; set; } - public static TrackEditForm From(TrackEntity track) => new() + public static TrackEditForm From(TrackDto track) => new() { TrackName = track.TrackName, Artist = track.Artist, diff --git a/DeepDrftManager/Components/Pages/Tracks/TrackList.razor b/DeepDrftManager/Components/Pages/Tracks/TrackList.razor index aa7fbbd..4230130 100644 --- a/DeepDrftManager/Components/Pages/Tracks/TrackList.razor +++ b/DeepDrftManager/Components/Pages/Tracks/TrackList.razor @@ -20,7 +20,7 @@ - Loading tracks… - Track Name - Artist - Album - Genre - Release Date + Track Name + Artist + Album + Genre + Release Date Entry Key Actions @@ -74,9 +74,9 @@ @code { - private MudTable? _table; + private MudTable? _table; - private async Task> LoadServerData(TableState state, CancellationToken cancellationToken) + private async Task> LoadServerData(TableState state, CancellationToken cancellationToken) { var pageNumber = state.Page + 1; // MudTable is 0-based, service is 1-based. var sortColumn = string.IsNullOrEmpty(state.SortLabel) ? "TrackName" : state.SortLabel; @@ -88,18 +88,18 @@ { var errorText = result.Messages.FirstOrDefault()?.Message ?? "Unknown error"; Snackbar.Add($"Failed to load tracks: {errorText}", Severity.Error); - return new TableData { Items = Array.Empty(), TotalItems = 0 }; + return new TableData { Items = Array.Empty(), TotalItems = 0 }; } var page = result.Value; - return new TableData + return new TableData { Items = page.Items, TotalItems = page.TotalCount }; } - private async Task ConfirmAndDelete(TrackEntity track) + private async Task ConfirmAndDelete(TrackDto track) { var confirmed = await DialogService.ShowMessageBox( title: "Delete track", diff --git a/DeepDrftManager/Components/_Imports.razor b/DeepDrftManager/Components/_Imports.razor index 7742bd4..2fce393 100644 --- a/DeepDrftManager/Components/_Imports.razor +++ b/DeepDrftManager/Components/_Imports.razor @@ -11,7 +11,7 @@ @using AuthBlocksWeb.Components @using DeepDrftManager @using DeepDrftManager.Components -@using DeepDrftModels.Entities +@using DeepDrftModels.DTOs @using Models.Common @using AuthBlocksModels.SystemDefinitions @using AuthBlocksWeb.HierarchicalAuthorize diff --git a/DeepDrftManager/Services/CmsTrackService.cs b/DeepDrftManager/Services/CmsTrackService.cs index 0aa1511..58088c1 100644 --- a/DeepDrftManager/Services/CmsTrackService.cs +++ b/DeepDrftManager/Services/CmsTrackService.cs @@ -1,7 +1,7 @@ using System.Net; using System.Net.Http.Headers; using System.Net.Http.Json; -using DeepDrftModels.Entities; +using DeepDrftModels.DTOs; using Models.Common; using NetBlocks.Models; @@ -30,7 +30,7 @@ public class CmsTrackService : ICmsTrackService _logger = logger; } - public async Task> UploadTrackAsync( + public async Task> UploadTrackAsync( Stream wavStream, string fileName, string contentType, @@ -67,7 +67,7 @@ public class CmsTrackService : ICmsTrackService catch (Exception ex) { _logger.LogError(ex, "Content API call failed for upload of {TrackName}", trackName); - return ResultContainer.CreateFailResult("Content API is unreachable."); + return ResultContainer.CreateFailResult("Content API is unreachable."); } using (response) @@ -79,35 +79,35 @@ public class CmsTrackService : ICmsTrackService if (statusCode >= 500) { _logger.LogError("Content API returned {Status} for upload of {TrackName}: {Body}", statusCode, trackName, body); - return ResultContainer.CreateFailResult("Upload failed on the content server. Please try again."); + return ResultContainer.CreateFailResult("Upload failed on the content server. Please try again."); } // 4xx: body is user-friendly validation text from DeepDrftAPI — relay as-is. _logger.LogWarning("Content API rejected upload: {Status} {Body}", statusCode, body); - return ResultContainer.CreateFailResult( + return ResultContainer.CreateFailResult( string.IsNullOrWhiteSpace(body) ? $"Upload rejected ({statusCode})." : body); } // The Content API now owns the dual-database write, so the response is the persisted - // entity (Id > 0) — no SQL roundtrip here. - TrackEntity? persisted; + // track DTO (Id > 0) — no SQL roundtrip here. + TrackDto? persisted; try { - persisted = await response.Content.ReadFromJsonAsync(ct); + persisted = await response.Content.ReadFromJsonAsync(ct); } catch (Exception ex) { - _logger.LogError(ex, "Failed to deserialize TrackEntity from Content API response"); - return ResultContainer.CreateFailResult("Content API returned an unexpected response."); + _logger.LogError(ex, "Failed to deserialize TrackDto from Content API response"); + return ResultContainer.CreateFailResult("Content API returned an unexpected response."); } if (persisted is null) { - _logger.LogError("Content API returned a null TrackEntity"); - return ResultContainer.CreateFailResult("Content API returned an empty response."); + _logger.LogError("Content API returned a null TrackDto"); + return ResultContainer.CreateFailResult("Content API returned an empty response."); } - return ResultContainer.CreatePassResult(persisted); + return ResultContainer.CreatePassResult(persisted); } } @@ -144,7 +144,7 @@ public class CmsTrackService : ICmsTrackService } } - public async Task>> GetPagedAsync( + public async Task>> GetPagedAsync( int page, int pageSize, string? sortColumn, bool sortDescending, CancellationToken ct = default) { @@ -163,7 +163,7 @@ public class CmsTrackService : ICmsTrackService catch (Exception ex) { _logger.LogError(ex, "Content API call failed for track page"); - return ResultContainer>.CreateFailResult("Content API is unreachable."); + return ResultContainer>.CreateFailResult("Content API is unreachable."); } using (response) @@ -171,31 +171,31 @@ public class CmsTrackService : ICmsTrackService if (!response.IsSuccessStatusCode) { _logger.LogError("Content API track page failed: {Status}", (int)response.StatusCode); - return ResultContainer>.CreateFailResult("Failed to load tracks."); + return ResultContainer>.CreateFailResult("Failed to load tracks."); } - PagedResult? paged; + PagedResult? paged; try { - paged = await response.Content.ReadFromJsonAsync>(ct); + paged = await response.Content.ReadFromJsonAsync>(ct); } catch (Exception ex) { _logger.LogError(ex, "Failed to deserialize PagedResult from Content API response"); - return ResultContainer>.CreateFailResult("Content API returned an unexpected response."); + return ResultContainer>.CreateFailResult("Content API returned an unexpected response."); } if (paged is null) { _logger.LogError("Content API returned a null PagedResult"); - return ResultContainer>.CreateFailResult("Content API returned an empty response."); + return ResultContainer>.CreateFailResult("Content API returned an empty response."); } - return ResultContainer>.CreatePassResult(paged); + return ResultContainer>.CreatePassResult(paged); } } - public async Task> GetByIdAsync(long id, CancellationToken ct = default) + public async Task> GetByIdAsync(long id, CancellationToken ct = default) { var client = _httpClientFactory.CreateClient(ContentCmsClientName); @@ -207,34 +207,34 @@ public class CmsTrackService : ICmsTrackService catch (Exception ex) { _logger.LogError(ex, "Content API call failed for track {TrackId}", id); - return ResultContainer.CreateFailResult("Content API is unreachable."); + return ResultContainer.CreateFailResult("Content API is unreachable."); } using (response) { if (response.StatusCode == HttpStatusCode.NotFound) { - return ResultContainer.CreatePassResult(null); + return ResultContainer.CreatePassResult(null); } if (!response.IsSuccessStatusCode) { _logger.LogError("Content API track lookup failed for {TrackId}: {Status}", id, (int)response.StatusCode); - return ResultContainer.CreateFailResult("Failed to load track."); + return ResultContainer.CreateFailResult("Failed to load track."); } - TrackEntity? track; + TrackDto? track; try { - track = await response.Content.ReadFromJsonAsync(ct); + track = await response.Content.ReadFromJsonAsync(ct); } catch (Exception ex) { - _logger.LogError(ex, "Failed to deserialize TrackEntity from Content API response"); - return ResultContainer.CreateFailResult("Content API returned an unexpected response."); + _logger.LogError(ex, "Failed to deserialize TrackDto from Content API response"); + return ResultContainer.CreateFailResult("Content API returned an unexpected response."); } - return ResultContainer.CreatePassResult(track); + return ResultContainer.CreatePassResult(track); } } diff --git a/DeepDrftManager/Services/ICmsTrackService.cs b/DeepDrftManager/Services/ICmsTrackService.cs index 3d17593..dd2f318 100644 --- a/DeepDrftManager/Services/ICmsTrackService.cs +++ b/DeepDrftManager/Services/ICmsTrackService.cs @@ -1,4 +1,4 @@ -using DeepDrftModels.Entities; +using DeepDrftModels.DTOs; using Models.Common; using NetBlocks.Models; @@ -13,10 +13,10 @@ public interface ICmsTrackService { /// /// Proxy a WAV upload to DeepDrftAPI. The Content API owns the dual-database write and - /// returns the persisted entity carrying the SQL-assigned Id. A vault-without-SQL + /// returns the persisted track carrying the SQL-assigned Id. A vault-without-SQL /// orphan is handled and logged server-side; here it surfaces as a failed result. /// - Task> UploadTrackAsync( + Task> UploadTrackAsync( Stream wavStream, string fileName, string contentType, @@ -37,7 +37,7 @@ public interface ICmsTrackService /// /// Fetch a page of track metadata from the Content API's GET api/track/page. /// - Task>> GetPagedAsync( + Task>> GetPagedAsync( int page, int pageSize, string? sortColumn, bool sortDescending, CancellationToken ct = default); @@ -45,7 +45,7 @@ public interface ICmsTrackService /// Fetch a single track's metadata from GET api/track/meta/{id}. A 404 returns a /// passing result with a null value. /// - Task> GetByIdAsync(long id, CancellationToken ct = default); + Task> GetByIdAsync(long id, CancellationToken ct = default); /// /// Update a track's metadata via PUT api/track/meta/{id}. EntryKey is immutable and diff --git a/DeepDrftPublic.Client/Clients/TrackClient.cs b/DeepDrftPublic.Client/Clients/TrackClient.cs index f32455a..3a75d69 100644 --- a/DeepDrftPublic.Client/Clients/TrackClient.cs +++ b/DeepDrftPublic.Client/Clients/TrackClient.cs @@ -1,4 +1,4 @@ -using DeepDrftModels.Entities; +using DeepDrftModels.DTOs; using Models.Common; using NetBlocks.Models; using System.Text.Json; @@ -16,7 +16,7 @@ public class TrackClient _http = httpClientFactory.CreateClient("DeepDrft.API"); } - public async Task>> GetPage( + public async Task>> GetPage( int pageNumber, int pageSize, string? sortColumn = null, @@ -38,11 +38,11 @@ public class TrackClient var response = await _http.GetAsync($"api/track/page{query}"); var json = await response.Content.ReadAsStringAsync(); - var dto = JsonSerializer.Deserialize>>(json, new JsonSerializerOptions + var dto = JsonSerializer.Deserialize>>(json, new JsonSerializerOptions { PropertyNameCaseInsensitive = true }); - return dto?.From() ?? ApiResult>.CreateFailResult("Failed to deserialize response"); + return dto?.From() ?? ApiResult>.CreateFailResult("Failed to deserialize response"); } } \ No newline at end of file diff --git a/DeepDrftPublic.Client/Pages/TracksView.razor.cs b/DeepDrftPublic.Client/Pages/TracksView.razor.cs index b6701ff..95e661b 100644 --- a/DeepDrftPublic.Client/Pages/TracksView.razor.cs +++ b/DeepDrftPublic.Client/Pages/TracksView.razor.cs @@ -1,4 +1,4 @@ -using DeepDrftModels.Entities; +using DeepDrftModels.DTOs; using DeepDrftPublic.Client.Services; using DeepDrftPublic.Client.ViewModels; using Microsoft.AspNetCore.Components; @@ -11,7 +11,7 @@ public partial class TracksView : ComponentBase [Inject] public required TracksViewModel ViewModel { get; set; } [CascadingParameter] public required IPlayerService PlayerService { get; set; } - private TrackEntity? _selectedTrack = null; + private TrackDto? _selectedTrack = null; private int _clickCount = 0; private string _lifecycleStatus = "Not initialized"; @@ -40,14 +40,14 @@ public partial class TracksView : ComponentBase { var result = await ViewModel.TrackData.GetPage(newPage, ViewModel.PageSize, ViewModel.SortBy, ViewModel.IsDescending); - if (result is { Success: true, Value: PagedResult pageResult }) + if (result is { Success: true, Value: PagedResult pageResult }) { ViewModel.Page = pageResult; ViewModel.PageSize = pageResult.PageSize; } } - private async Task PlayTrack(TrackEntity? track) + private async Task PlayTrack(TrackDto? track) { if (track == null && _selectedTrack == null || track?.Id == _selectedTrack?.Id) return; diff --git a/DeepDrftPublic.Client/Services/AudioPlayerService.cs b/DeepDrftPublic.Client/Services/AudioPlayerService.cs index f2eb4f6..4fadea6 100644 --- a/DeepDrftPublic.Client/Services/AudioPlayerService.cs +++ b/DeepDrftPublic.Client/Services/AudioPlayerService.cs @@ -1,4 +1,4 @@ -using DeepDrftModels.Entities; +using DeepDrftModels.DTOs; using DeepDrftPublic.Client.Clients; using Microsoft.AspNetCore.Components; using NetBlocks.Models; @@ -32,7 +32,7 @@ public abstract class AudioPlayerService : IPlayerService, IAsyncDisposable /// / path are responsible for managing /// it themselves. /// - public TrackEntity? CurrentTrack { get; protected set; } + public TrackDto? CurrentTrack { get; protected set; } // Events public EventCallback? OnStateChanged { get; set; } @@ -74,7 +74,7 @@ public abstract class AudioPlayerService : IPlayerService, IAsyncDisposable } } - public virtual async Task SelectTrack(TrackEntity track) + public virtual async Task SelectTrack(TrackDto track) { await EnsureInitializedAsync(); @@ -87,7 +87,7 @@ public abstract class AudioPlayerService : IPlayerService, IAsyncDisposable await NotifyStateChanged(); } - private async Task LoadTrack(TrackEntity track) + private async Task LoadTrack(TrackDto track) { try { diff --git a/DeepDrftPublic.Client/Services/IPlayerService.cs b/DeepDrftPublic.Client/Services/IPlayerService.cs index 2821803..6d71bcb 100644 --- a/DeepDrftPublic.Client/Services/IPlayerService.cs +++ b/DeepDrftPublic.Client/Services/IPlayerService.cs @@ -1,4 +1,4 @@ -using DeepDrftModels.Entities; +using DeepDrftModels.DTOs; using Microsoft.AspNetCore.Components; using NetBlocks.Models; @@ -17,7 +17,7 @@ public interface IPlayerService double Volume { get; } double LoadProgress { get; } string? ErrorMessage { get; } - TrackEntity? CurrentTrack { get; } + TrackDto? CurrentTrack { get; } // Events for UI updates EventCallback? OnStateChanged { get; set; } @@ -25,7 +25,7 @@ public interface IPlayerService // Control methods Task InitializeAsync(); - Task SelectTrack(TrackEntity track); + Task SelectTrack(TrackDto track); Task Stop(); Task Unload(); Task TogglePlayPause(); @@ -43,5 +43,5 @@ public interface IStreamingPlayerService : IPlayerService int BufferedChunks { get; } // Streaming control methods - Task SelectTrackStreaming(TrackEntity track); + Task SelectTrackStreaming(TrackDto track); } \ No newline at end of file diff --git a/DeepDrftPublic.Client/Services/ITrackDataService.cs b/DeepDrftPublic.Client/Services/ITrackDataService.cs index 9c715e4..842ec42 100644 --- a/DeepDrftPublic.Client/Services/ITrackDataService.cs +++ b/DeepDrftPublic.Client/Services/ITrackDataService.cs @@ -1,4 +1,4 @@ -using DeepDrftModels.Entities; +using DeepDrftModels.DTOs; using Models.Common; using NetBlocks.Models; @@ -17,7 +17,7 @@ namespace DeepDrftPublic.Client.Services; /// public interface ITrackDataService { - Task>> GetPage( + Task>> GetPage( int pageNumber, int pageSize, string? sortColumn = null, diff --git a/DeepDrftPublic.Client/Services/StreamingAudioPlayerService.cs b/DeepDrftPublic.Client/Services/StreamingAudioPlayerService.cs index c163992..12a394c 100644 --- a/DeepDrftPublic.Client/Services/StreamingAudioPlayerService.cs +++ b/DeepDrftPublic.Client/Services/StreamingAudioPlayerService.cs @@ -1,4 +1,4 @@ -using DeepDrftModels.Entities; +using DeepDrftModels.DTOs; using DeepDrftPublic.Client.Clients; using System.Buffers; using Microsoft.Extensions.Logging; @@ -41,12 +41,12 @@ public class StreamingAudioPlayerService : AudioPlayerService, IStreamingPlayerS _logger = logger; } - public override async Task SelectTrack(TrackEntity track) + public override async Task SelectTrack(TrackDto track) { await SelectTrackStreaming(track); } - public async Task SelectTrackStreaming(TrackEntity track) + public async Task SelectTrackStreaming(TrackDto track) { await EnsureInitializedAsync(); @@ -59,7 +59,7 @@ public class StreamingAudioPlayerService : AudioPlayerService, IStreamingPlayerS await NotifyStateChanged(); } - private async Task LoadTrackStreaming(TrackEntity track) + private async Task LoadTrackStreaming(TrackDto track) { // Always reset to clean state before loading new track. ResetToIdle // both cancels and awaits any in-flight streaming loop, so by the time diff --git a/DeepDrftPublic.Client/Services/TrackClientDataService.cs b/DeepDrftPublic.Client/Services/TrackClientDataService.cs index 70e1fa2..5f360c4 100644 --- a/DeepDrftPublic.Client/Services/TrackClientDataService.cs +++ b/DeepDrftPublic.Client/Services/TrackClientDataService.cs @@ -1,4 +1,4 @@ -using DeepDrftModels.Entities; +using DeepDrftModels.DTOs; using DeepDrftPublic.Client.Clients; using Models.Common; using NetBlocks.Models; @@ -19,7 +19,7 @@ public class TrackClientDataService : ITrackDataService _trackClient = trackClient; } - public Task>> GetPage( + public Task>> GetPage( int pageNumber, int pageSize, string? sortColumn = null, diff --git a/DeepDrftPublic.Client/ViewModels/TracksViewModel.cs b/DeepDrftPublic.Client/ViewModels/TracksViewModel.cs index a68b46e..a53ca31 100644 --- a/DeepDrftPublic.Client/ViewModels/TracksViewModel.cs +++ b/DeepDrftPublic.Client/ViewModels/TracksViewModel.cs @@ -1,4 +1,4 @@ -using DeepDrftModels.Entities; +using DeepDrftModels.DTOs; using DeepDrftPublic.Client.Services; using Models.Common; @@ -15,7 +15,7 @@ public class TracksViewModel get => Page?.PageSize ?? 15; set { - if (Page == null) throw new Exception(); + if (Page == null) return; if (value != Page.PageSize) { Page.PageSize = value; @@ -24,7 +24,7 @@ public class TracksViewModel } public string SortBy { get; set; } = string.Empty; public bool IsDescending { get; set; } = false; - public PagedResult? Page { get; set; } = null; + public PagedResult? Page { get; set; } = null; public TracksViewModel(ITrackDataService trackData) { diff --git a/DeepDrftPublic/Controllers/TrackController.cs b/DeepDrftPublic/Controllers/TrackController.cs index c68b8a0..f4d1164 100644 --- a/DeepDrftPublic/Controllers/TrackController.cs +++ b/DeepDrftPublic/Controllers/TrackController.cs @@ -1,5 +1,5 @@ using DeepDrftData; -using DeepDrftModels.Entities; +using DeepDrftModels.DTOs; using Microsoft.AspNetCore.Mvc; using Models.Common; using NetBlocks.Models; @@ -18,15 +18,15 @@ public class TrackController : ControllerBase } [HttpGet("page")] - public async Task>>> GetPage( + public async Task>>> GetPage( [FromQuery] int pageNumber, [FromQuery] int pageSize, [FromQuery] string? sortColumn = null, [FromQuery] bool sortDescending = false) { var result = await _trackService.GetPaged(pageNumber, pageSize, sortColumn, sortDescending); - var apiResult = ApiResult>.From(result); - var dto = new ApiResultDto>(apiResult); + var apiResult = ApiResult>.From(result); + var dto = new ApiResultDto>(apiResult); return result.Success ? Ok(dto) : StatusCode(500, dto); } diff --git a/DeepDrftPublic/Services/TrackDirectDataService.cs b/DeepDrftPublic/Services/TrackDirectDataService.cs index e97107e..31676b1 100644 --- a/DeepDrftPublic/Services/TrackDirectDataService.cs +++ b/DeepDrftPublic/Services/TrackDirectDataService.cs @@ -1,5 +1,5 @@ using DeepDrftData; -using DeepDrftModels.Entities; +using DeepDrftModels.DTOs; using DeepDrftPublic.Client.Services; using Models.Common; using NetBlocks.Models; @@ -21,13 +21,13 @@ public class TrackDirectDataService : ITrackDataService _trackService = trackService; } - public async Task>> GetPage( + public async Task>> GetPage( int pageNumber, int pageSize, string? sortColumn = null, bool sortDescending = false) { var result = await _trackService.GetPaged(pageNumber, pageSize, sortColumn, sortDescending); - return ApiResult>.From(result); + return ApiResult>.From(result); } } diff --git a/DeepDrftShared.Client/Components/TrackCard.razor.cs b/DeepDrftShared.Client/Components/TrackCard.razor.cs index ace2198..31af189 100644 --- a/DeepDrftShared.Client/Components/TrackCard.razor.cs +++ b/DeepDrftShared.Client/Components/TrackCard.razor.cs @@ -1,13 +1,13 @@ using Microsoft.AspNetCore.Components; -using DeepDrftModels.Entities; +using DeepDrftModels.DTOs; using MudBlazor; namespace DeepDrftShared.Client.Components; public partial class TrackCard : ComponentBase { - [Parameter] public required TrackEntity TrackModel { get; set; } - [Parameter] public EventCallback OnPlay { get; set; } + [Parameter] public required TrackDto TrackModel { get; set; } + [Parameter] public EventCallback OnPlay { get; set; } [Parameter] public bool IsPlaying { get; set; } = false; private string PlayPauseIcon => IsPlaying ? Icons.Material.Filled.MusicNote : Icons.Material.Filled.PlayArrow; diff --git a/DeepDrftShared.Client/Components/TracksGallery.razor.cs b/DeepDrftShared.Client/Components/TracksGallery.razor.cs index 42b2ec5..882a6e4 100644 --- a/DeepDrftShared.Client/Components/TracksGallery.razor.cs +++ b/DeepDrftShared.Client/Components/TracksGallery.razor.cs @@ -1,15 +1,15 @@ using Microsoft.AspNetCore.Components; -using DeepDrftModels.Entities; +using DeepDrftModels.DTOs; namespace DeepDrftShared.Client.Components; public partial class TracksGallery : ComponentBase { - [Parameter] public IEnumerable Tracks { get; set; } = []; - [Parameter] public TrackEntity? SelectedTrack { get; set; } - [Parameter] public EventCallback SelectedTrackChanged { get; set; } + [Parameter] public IEnumerable Tracks { get; set; } = []; + [Parameter] public TrackDto? SelectedTrack { get; set; } + [Parameter] public EventCallback SelectedTrackChanged { get; set; } - private async Task HandlePlayClick(TrackEntity track) + private async Task HandlePlayClick(TrackDto track) { if (SelectedTrack == track) return; SelectedTrack = track;