Merge p1-w2-tb-web-cli: design majors Web.Services + Web.Client + CLI
This commit is contained in:
@@ -47,7 +47,7 @@ builder.Services.AddSingleton<FileDatabase>(provider =>
|
|||||||
|
|
||||||
// Add services
|
// Add services
|
||||||
builder.Services.AddScoped<TrackRepository>();
|
builder.Services.AddScoped<TrackRepository>();
|
||||||
builder.Services.AddScoped<DeepDrftWeb.Services.TrackService>();
|
builder.Services.AddScoped<DeepDrftWeb.Services.ITrackService, DeepDrftWeb.Services.TrackService>();
|
||||||
builder.Services.AddScoped<AudioProcessor>();
|
builder.Services.AddScoped<AudioProcessor>();
|
||||||
builder.Services.AddScoped<DeepDrftContent.Services.TrackService>();
|
builder.Services.AddScoped<DeepDrftContent.Services.TrackService>();
|
||||||
builder.Services.AddScoped<CliService>();
|
builder.Services.AddScoped<CliService>();
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
using DeepDrftWeb.Services.Repositories;
|
|
||||||
using DeepDrftContent.Services;
|
using DeepDrftContent.Services;
|
||||||
using DeepDrftModels.Entities;
|
using DeepDrftModels.Entities;
|
||||||
using NetBlocks.Models;
|
using NetBlocks.Models;
|
||||||
|
using DeepDrftCli.Utils;
|
||||||
|
|
||||||
namespace DeepDrftCli.Services;
|
namespace DeepDrftCli.Services;
|
||||||
|
|
||||||
@@ -13,18 +13,15 @@ namespace DeepDrftCli.Services;
|
|||||||
public class CliService
|
public class CliService
|
||||||
{
|
{
|
||||||
private readonly ILogger<CliService> _logger;
|
private readonly ILogger<CliService> _logger;
|
||||||
private readonly TrackRepository _trackRepository;
|
private readonly DeepDrftWeb.Services.ITrackService _webTrackService;
|
||||||
private readonly DeepDrftWeb.Services.TrackService _webTrackService;
|
|
||||||
private readonly DeepDrftContent.Services.TrackService _contentTrackService;
|
private readonly DeepDrftContent.Services.TrackService _contentTrackService;
|
||||||
|
|
||||||
public CliService(
|
public CliService(
|
||||||
ILogger<CliService> logger,
|
ILogger<CliService> logger,
|
||||||
TrackRepository trackRepository,
|
DeepDrftWeb.Services.ITrackService webTrackService,
|
||||||
DeepDrftWeb.Services.TrackService webTrackService,
|
|
||||||
DeepDrftContent.Services.TrackService contentTrackService)
|
DeepDrftContent.Services.TrackService contentTrackService)
|
||||||
{
|
{
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
_trackRepository = trackRepository;
|
|
||||||
_webTrackService = webTrackService;
|
_webTrackService = webTrackService;
|
||||||
_contentTrackService = contentTrackService;
|
_contentTrackService = contentTrackService;
|
||||||
}
|
}
|
||||||
@@ -232,7 +229,7 @@ public class CliService
|
|||||||
|
|
||||||
foreach (var track in tracks)
|
foreach (var track in tracks)
|
||||||
{
|
{
|
||||||
Console.WriteLine($"{track.Id,-5} {TruncateString(track.TrackName, 25),-25} {TruncateString(track.Artist, 20),-20} {TruncateString(track.Album ?? "", 15),-15} {TruncateString(track.Genre ?? "", 10),-10}");
|
Console.WriteLine($"{track.Id,-5} {CliUtils.TruncateString(track.TrackName, 25),-25} {CliUtils.TruncateString(track.Artist, 20),-20} {CliUtils.TruncateString(track.Album ?? "", 15),-15} {CliUtils.TruncateString(track.Genre ?? "", 10),-10}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
@@ -285,17 +282,6 @@ public class CliService
|
|||||||
Console.WriteLine(" - Use * to indicate required fields in interactive mode");
|
Console.WriteLine(" - Use * to indicate required fields in interactive mode");
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Truncates a string to fit within specified length
|
|
||||||
/// </summary>
|
|
||||||
private string TruncateString(string input, int maxLength)
|
|
||||||
{
|
|
||||||
if (string.IsNullOrEmpty(input))
|
|
||||||
return string.Empty;
|
|
||||||
|
|
||||||
return input.Length <= maxLength ? input : input.Substring(0, maxLength - 3) + "...";
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Prompts user for track metadata interactively
|
/// Prompts user for track metadata interactively
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
using Terminal.Gui;
|
using Terminal.Gui;
|
||||||
using DeepDrftModels.Entities;
|
using DeepDrftModels.Entities;
|
||||||
|
using DeepDrftCli.Utils;
|
||||||
|
|
||||||
namespace DeepDrftCli.Services;
|
namespace DeepDrftCli.Services;
|
||||||
|
|
||||||
@@ -10,7 +11,7 @@ namespace DeepDrftCli.Services;
|
|||||||
public class GuiService
|
public class GuiService
|
||||||
{
|
{
|
||||||
private readonly ILogger<GuiService> _logger;
|
private readonly ILogger<GuiService> _logger;
|
||||||
private readonly DeepDrftWeb.Services.TrackService _webTrackService;
|
private readonly DeepDrftWeb.Services.ITrackService _webTrackService;
|
||||||
private readonly DeepDrftContent.Services.TrackService _contentTrackService;
|
private readonly DeepDrftContent.Services.TrackService _contentTrackService;
|
||||||
|
|
||||||
// GUI Components
|
// GUI Components
|
||||||
@@ -23,7 +24,7 @@ public class GuiService
|
|||||||
|
|
||||||
public GuiService(
|
public GuiService(
|
||||||
ILogger<GuiService> logger,
|
ILogger<GuiService> logger,
|
||||||
DeepDrftWeb.Services.TrackService webTrackService,
|
DeepDrftWeb.Services.ITrackService webTrackService,
|
||||||
DeepDrftContent.Services.TrackService contentTrackService)
|
DeepDrftContent.Services.TrackService contentTrackService)
|
||||||
{
|
{
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
@@ -785,7 +786,7 @@ public class GuiService
|
|||||||
|
|
||||||
// Create display items for the list view
|
// Create display items for the list view
|
||||||
var displayItems = _tracks.Select(t =>
|
var displayItems = _tracks.Select(t =>
|
||||||
$"{t.Id,4} │ {TruncateString(t.TrackName, 25),25} │ {TruncateString(t.Artist, 20),20} │ {TruncateString(t.Album ?? "", 15),15} │ {TruncateString(t.Genre ?? "", 10),10}"
|
$"{t.Id,4} │ {CliUtils.TruncateString(t.TrackName, 25),25} │ {CliUtils.TruncateString(t.Artist, 20),20} │ {CliUtils.TruncateString(t.Album ?? "", 15),15} │ {CliUtils.TruncateString(t.Genre ?? "", 10),10}"
|
||||||
).ToArray();
|
).ToArray();
|
||||||
|
|
||||||
_trackListView?.SetSource(displayItems);
|
_trackListView?.SetSource(displayItems);
|
||||||
@@ -894,14 +895,4 @@ public class GuiService
|
|||||||
UpdateStatus("Ready");
|
UpdateStatus("Ready");
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Truncate string to fit display width
|
|
||||||
/// </summary>
|
|
||||||
private string TruncateString(string input, int maxLength)
|
|
||||||
{
|
|
||||||
if (string.IsNullOrEmpty(input))
|
|
||||||
return string.Empty;
|
|
||||||
|
|
||||||
return input.Length <= maxLength ? input : input.Substring(0, maxLength - 3) + "...";
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
namespace DeepDrftCli.Utils;
|
||||||
|
|
||||||
|
internal static class CliUtils
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Truncates a string to fit within the specified column width,
|
||||||
|
/// appending "..." when the string is longer.
|
||||||
|
/// </summary>
|
||||||
|
internal static string TruncateString(string input, int maxLength)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(input))
|
||||||
|
return string.Empty;
|
||||||
|
|
||||||
|
return input.Length <= maxLength ? input : input.Substring(0, maxLength - 3) + "...";
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,5 +1,4 @@
|
|||||||
using DeepDrftContent.Services.Audio;
|
using DeepDrftContent.Services.Audio;
|
||||||
using DeepDrftContent.Services.Constants;
|
|
||||||
using DeepDrftContent.Services.FileDatabase.Models;
|
using DeepDrftContent.Services.FileDatabase.Models;
|
||||||
using DeepDrftContent.Middleware;
|
using DeepDrftContent.Middleware;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
@@ -10,15 +9,23 @@ namespace DeepDrftContent.Controllers;
|
|||||||
[Route("api/[controller]")]
|
[Route("api/[controller]")]
|
||||||
public class TrackController : ControllerBase
|
public class TrackController : ControllerBase
|
||||||
{
|
{
|
||||||
private readonly DeepDrftContent.Services.FileDatabase.Services.FileDatabase _fileDatabase;
|
private readonly DeepDrftContent.Services.TrackService _trackService;
|
||||||
private readonly WavOffsetService _wavOffsetService;
|
private readonly WavOffsetService _wavOffsetService;
|
||||||
private readonly ILogger<TrackController> _logger;
|
private readonly ILogger<TrackController> _logger;
|
||||||
|
|
||||||
|
// FileDatabase is injected directly for PutTrack because that endpoint receives a pre-processed
|
||||||
|
// AudioBinaryDto over the wire, not a WAV file path. TrackService.AddTrackFromWavAsync is
|
||||||
|
// file-path-oriented and not applicable here. If a file-upload flow is added in future,
|
||||||
|
// route it through TrackService instead.
|
||||||
|
private readonly DeepDrftContent.Services.FileDatabase.Services.FileDatabase _fileDatabase;
|
||||||
|
|
||||||
public TrackController(
|
public TrackController(
|
||||||
|
DeepDrftContent.Services.TrackService trackService,
|
||||||
DeepDrftContent.Services.FileDatabase.Services.FileDatabase fileDatabase,
|
DeepDrftContent.Services.FileDatabase.Services.FileDatabase fileDatabase,
|
||||||
WavOffsetService wavOffsetService,
|
WavOffsetService wavOffsetService,
|
||||||
ILogger<TrackController> logger)
|
ILogger<TrackController> logger)
|
||||||
{
|
{
|
||||||
|
_trackService = trackService;
|
||||||
_fileDatabase = fileDatabase;
|
_fileDatabase = fileDatabase;
|
||||||
_wavOffsetService = wavOffsetService;
|
_wavOffsetService = wavOffsetService;
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
@@ -31,7 +38,7 @@ public class TrackController : ControllerBase
|
|||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var file = await _fileDatabase.LoadResourceAsync<AudioBinary>(VaultConstants.Tracks, trackId);
|
var file = await _trackService.GetAudioBinaryAsync(trackId);
|
||||||
if (file == null)
|
if (file == null)
|
||||||
{
|
{
|
||||||
_logger.LogWarning("Track not found: {TrackId}", trackId);
|
_logger.LogWarning("Track not found: {TrackId}", trackId);
|
||||||
@@ -72,7 +79,10 @@ public class TrackController : ControllerBase
|
|||||||
{
|
{
|
||||||
_logger.LogInformation("PutTrack called with trackId: {TrackId}", trackId);
|
_logger.LogInformation("PutTrack called with trackId: {TrackId}", trackId);
|
||||||
var audioBinary = AudioBinary.From(track);
|
var audioBinary = AudioBinary.From(track);
|
||||||
var success = await _fileDatabase.RegisterResourceAsync(VaultConstants.Tracks, trackId, audioBinary);
|
// Direct FileDatabase write: this endpoint receives an already-processed AudioBinaryDto,
|
||||||
|
// not a WAV file, so TrackService.AddTrackFromWavAsync does not apply. See constructor comment.
|
||||||
|
var success = await _fileDatabase.RegisterResourceAsync(
|
||||||
|
DeepDrftContent.Services.Constants.VaultConstants.Tracks, trackId, audioBinary);
|
||||||
return success ? Ok() : BadRequest("Failed to store audio track");
|
return success ? Ok() : BadRequest("Failed to store audio track");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,7 +1,9 @@
|
|||||||
|
using DeepDrftContent.Services;
|
||||||
using DeepDrftContent.Services.Audio;
|
using DeepDrftContent.Services.Audio;
|
||||||
using DeepDrftContent.Services.Constants;
|
using DeepDrftContent.Services.Constants;
|
||||||
using DeepDrftContent.Services.FileDatabase.Models;
|
using DeepDrftContent.Services.FileDatabase.Models;
|
||||||
using DeepDrftContent.Services.FileDatabase.Services;
|
using DeepDrftContent.Services.FileDatabase.Services;
|
||||||
|
using DeepDrftContent.Services.Processors;
|
||||||
using DeepDrftContent.Models;
|
using DeepDrftContent.Models;
|
||||||
|
|
||||||
namespace DeepDrftContent
|
namespace DeepDrftContent
|
||||||
@@ -12,6 +14,8 @@ namespace DeepDrftContent
|
|||||||
{
|
{
|
||||||
// Audio services
|
// Audio services
|
||||||
builder.Services.AddSingleton<WavOffsetService>();
|
builder.Services.AddSingleton<WavOffsetService>();
|
||||||
|
builder.Services.AddSingleton<AudioProcessor>();
|
||||||
|
builder.Services.AddSingleton<TrackService>();
|
||||||
|
|
||||||
// File Database
|
// File Database
|
||||||
builder.Configuration.AddJsonFile("environment/filedatabase.json", optional: false, reloadOnChange: true);
|
builder.Configuration.AddJsonFile("environment/filedatabase.json", optional: false, reloadOnChange: true);
|
||||||
|
|||||||
@@ -4,8 +4,6 @@ namespace DeepDrftWeb.Client.Common;
|
|||||||
|
|
||||||
public class DarkModeSettings()
|
public class DarkModeSettings()
|
||||||
{
|
{
|
||||||
// public EventCallback<bool> IsDarkModeChanged { get; set; }
|
|
||||||
|
|
||||||
[PersistentState]
|
[PersistentState]
|
||||||
public bool IsDarkMode
|
public bool IsDarkMode
|
||||||
{
|
{
|
||||||
@@ -14,7 +12,6 @@ public class DarkModeSettings()
|
|||||||
{
|
{
|
||||||
if (value == field) return;
|
if (value == field) return;
|
||||||
field = value;
|
field = value;
|
||||||
// IsDarkModeChanged.InvokeAsync(value);
|
|
||||||
}
|
}
|
||||||
} = false;
|
} = false;
|
||||||
}
|
}
|
||||||
@@ -225,6 +225,9 @@ public class AudioInteropService : IAsyncDisposable
|
|||||||
return await InvokeJsAsync<AudioOperationResult>("DeepDrftAudio.disposePlayer", playerId);
|
return await InvokeJsAsync<AudioOperationResult>("DeepDrftAudio.disposePlayer", playerId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: The typeof(T) switch below requires updating whenever a new result type is added.
|
||||||
|
// Consider introducing a shared marker interface (e.g. IAudioResult with a static factory
|
||||||
|
// method) so InvokeJsAsync can construct the failure result generically without a type switch.
|
||||||
private async Task<T> InvokeJsAsync<T>(string identifier, params object[] args)
|
private async Task<T> InvokeJsAsync<T>(string identifier, params object[] args)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ using DeepDrftModels.Entities;
|
|||||||
using DeepDrftWeb.Client.Clients;
|
using DeepDrftWeb.Client.Clients;
|
||||||
using Microsoft.AspNetCore.Components;
|
using Microsoft.AspNetCore.Components;
|
||||||
using NetBlocks.Models;
|
using NetBlocks.Models;
|
||||||
|
using System.Buffers;
|
||||||
|
|
||||||
namespace DeepDrftWeb.Client.Services;
|
namespace DeepDrftWeb.Client.Services;
|
||||||
|
|
||||||
@@ -135,29 +136,25 @@ public abstract class AudioPlayerService : IPlayerService, IAsyncDisposable
|
|||||||
|
|
||||||
private async Task StreamAudio(TrackMediaResponse audio)
|
private async Task StreamAudio(TrackMediaResponse audio)
|
||||||
{
|
{
|
||||||
|
const int bufferSize = 32 * 1024;
|
||||||
|
var rentedBuffer = ArrayPool<byte>.Shared.Rent(bufferSize);
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
const int bufferSize = 32 * 1024;
|
|
||||||
long totalBytesRead = 0;
|
long totalBytesRead = 0;
|
||||||
int currentBytes;
|
int currentBytes;
|
||||||
|
|
||||||
do
|
do
|
||||||
{
|
{
|
||||||
var buffer = new byte[bufferSize];
|
currentBytes = await audio.Stream.ReadAsync(rentedBuffer, 0, bufferSize);
|
||||||
currentBytes = await audio.Stream.ReadAsync(buffer, 0, buffer.Length);
|
|
||||||
|
|
||||||
if (currentBytes > 0)
|
if (currentBytes > 0)
|
||||||
{
|
{
|
||||||
totalBytesRead += currentBytes;
|
totalBytesRead += currentBytes;
|
||||||
|
|
||||||
if (currentBytes < bufferSize)
|
// Slice to actual bytes read before sending to interop
|
||||||
{
|
var chunk = rentedBuffer[..currentBytes];
|
||||||
var trimmedBuffer = new byte[currentBytes];
|
|
||||||
Array.Copy(buffer, trimmedBuffer, currentBytes);
|
|
||||||
buffer = trimmedBuffer;
|
|
||||||
}
|
|
||||||
|
|
||||||
var appendResult = await _audioInterop.AppendAudioBlockAsync(PlayerId, buffer);
|
var appendResult = await _audioInterop.AppendAudioBlockAsync(PlayerId, chunk);
|
||||||
if (!appendResult.Success)
|
if (!appendResult.Success)
|
||||||
{
|
{
|
||||||
throw new Exception($"Failed to append audio block: {appendResult.Error}");
|
throw new Exception($"Failed to append audio block: {appendResult.Error}");
|
||||||
@@ -191,6 +188,10 @@ public abstract class AudioPlayerService : IPlayerService, IAsyncDisposable
|
|||||||
await NotifyStateChanged();
|
await NotifyStateChanged();
|
||||||
throw;
|
throw;
|
||||||
}
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
ArrayPool<byte>.Shared.Return(rentedBuffer);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task TogglePlayPause()
|
public async Task TogglePlayPause()
|
||||||
|
|||||||
@@ -53,8 +53,6 @@ public class StreamingAudioPlayerService : AudioPlayerService, IStreamingPlayerS
|
|||||||
// Resume AudioContext immediately on track selection (user interaction) to avoid clicks later
|
// Resume AudioContext immediately on track selection (user interaction) to avoid clicks later
|
||||||
await _audioInterop.EnsureAudioContextReady(PlayerId);
|
await _audioInterop.EnsureAudioContextReady(PlayerId);
|
||||||
|
|
||||||
// NotifyStateChanged();
|
|
||||||
|
|
||||||
await NotifyTrackSelected();
|
await NotifyTrackSelected();
|
||||||
|
|
||||||
await LoadTrackStreaming(track);
|
await LoadTrackStreaming(track);
|
||||||
|
|||||||
@@ -0,0 +1,15 @@
|
|||||||
|
using DeepDrftModels.Entities;
|
||||||
|
using DeepDrftModels.Models;
|
||||||
|
using NetBlocks.Models;
|
||||||
|
|
||||||
|
namespace DeepDrftWeb.Services;
|
||||||
|
|
||||||
|
public interface ITrackService
|
||||||
|
{
|
||||||
|
Task<ResultContainer<TrackEntity?>> GetById(long id);
|
||||||
|
Task<ResultContainer<List<TrackEntity>>> GetAll();
|
||||||
|
Task<ResultContainer<PagedResult<TrackEntity>>> GetPaged(int pageNumber, int pageSize, string? sortColumn, bool sortDescending);
|
||||||
|
Task<ResultContainer<TrackEntity>> Create(TrackEntity newTrack);
|
||||||
|
Task<ResultContainer<TrackEntity>> Update(TrackEntity track);
|
||||||
|
Task<Result> Delete(long id);
|
||||||
|
}
|
||||||
@@ -26,11 +26,18 @@ public class TrackRepository
|
|||||||
|
|
||||||
public async Task<PagedResult<TrackEntity>> GetPage(PagingParameters<TrackEntity> pageParameters)
|
public async Task<PagedResult<TrackEntity>> GetPage(PagingParameters<TrackEntity> pageParameters)
|
||||||
{
|
{
|
||||||
|
// Two separate queries with no transaction: count and page can be momentarily inconsistent
|
||||||
|
// under concurrent writes. Acceptable — SQLite concurrency is low and the UI is read-only.
|
||||||
|
// If filtering is added, the count query must be updated to apply the same filter.
|
||||||
var count = await _db.Tracks.CountAsync();
|
var count = await _db.Tracks.CountAsync();
|
||||||
|
|
||||||
var page = await _db.Tracks
|
var orderBy = pageParameters.OrderBy ?? (t => t.Id);
|
||||||
.OrderBy(pageParameters.OrderBy ?? (t => t.Id))
|
var ordered = pageParameters.IsDescending
|
||||||
.Skip((pageParameters.Page - 1) * pageParameters.PageSize)
|
? _db.Tracks.OrderByDescending(orderBy)
|
||||||
|
: _db.Tracks.OrderBy(orderBy);
|
||||||
|
|
||||||
|
var page = await ordered
|
||||||
|
.Skip(pageParameters.Skip)
|
||||||
.Take(pageParameters.PageSize)
|
.Take(pageParameters.PageSize)
|
||||||
.ToListAsync();
|
.ToListAsync();
|
||||||
|
|
||||||
|
|||||||
@@ -6,10 +6,8 @@ using NetBlocks.Models;
|
|||||||
|
|
||||||
namespace DeepDrftWeb.Services;
|
namespace DeepDrftWeb.Services;
|
||||||
|
|
||||||
public class TrackService
|
public class TrackService : ITrackService
|
||||||
{
|
{
|
||||||
private readonly string _sortLastAscending = Enumerable.Repeat(char.MaxValue, 64).Aggregate(string.Empty, (a, b) => a + b);
|
|
||||||
private readonly string _sortLastDescending = Enumerable.Repeat(char.MinValue.ToString(), 64).Aggregate(string.Empty, (a, b) => a + b);
|
|
||||||
private readonly TrackRepository _repository;
|
private readonly TrackRepository _repository;
|
||||||
|
|
||||||
public TrackService(TrackRepository repository)
|
public TrackService(TrackRepository repository)
|
||||||
@@ -65,13 +63,13 @@ public class TrackService
|
|||||||
parameters.OrderBy = entity => entity.Artist;
|
parameters.OrderBy = entity => entity.Artist;
|
||||||
break;
|
break;
|
||||||
case "Album":
|
case "Album":
|
||||||
parameters.OrderBy = entity => entity.Album ?? _sortLastAscending;
|
parameters.OrderBy = entity => entity.Album ?? "";
|
||||||
break;
|
break;
|
||||||
case "ReleaseDate":
|
case "ReleaseDate":
|
||||||
parameters.OrderBy = entity => entity.ReleaseDate ?? DateOnly.MaxValue;
|
parameters.OrderBy = entity => entity.ReleaseDate ?? DateOnly.MaxValue;
|
||||||
break;
|
break;
|
||||||
case "Genre":
|
case "Genre":
|
||||||
parameters.OrderBy = entity => entity.Genre ?? _sortLastAscending;
|
parameters.OrderBy = entity => entity.Genre ?? "";
|
||||||
break;
|
break;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,9 +10,9 @@ namespace DeepDrftWeb.Controllers;
|
|||||||
[Route("api/[controller]")]
|
[Route("api/[controller]")]
|
||||||
public class TrackController : ControllerBase
|
public class TrackController : ControllerBase
|
||||||
{
|
{
|
||||||
private readonly TrackService _trackService;
|
private readonly ITrackService _trackService;
|
||||||
|
|
||||||
public TrackController(TrackService trackService)
|
public TrackController(ITrackService trackService)
|
||||||
{
|
{
|
||||||
_trackService = trackService;
|
_trackService = trackService;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ public static class Startup
|
|||||||
// Add Track services
|
// Add Track services
|
||||||
builder.Services
|
builder.Services
|
||||||
.AddScoped<TrackRepository>()
|
.AddScoped<TrackRepository>()
|
||||||
.AddScoped<TrackService>();
|
.AddScoped<ITrackService, TrackService>();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static string GetKestrelUrl(this WebApplicationBuilder builder)
|
public static string GetKestrelUrl(this WebApplicationBuilder builder)
|
||||||
|
|||||||
Reference in New Issue
Block a user