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