feat(api): #288 wrap raw VMs, nullable-honest DTOs, search pageNum

24 #nullable enable flips across ErsatzTV.Core/Api; Collection/Schedule/
SmartCollection/Resolution VMs wrapped in ResponseModels (Version now
header-only, SPA-verified); pageNum threaded into GET /api/search.

Refs #288 #197

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-07-12 02:08:12 +02:00
co-authored by Claude Opus 4.8
parent 9e2d160884
commit fecf16d3d2
38 changed files with 189 additions and 45 deletions
@@ -14,11 +14,19 @@ namespace ErsatzTV.Controllers.Api;
[EndpointGroupName("general")]
public class ResolutionController(IMediator mediator) : ControllerBase
{
[HttpGet("/api/ffmpeg/resolution/by-name/{name}", Name="GetResolutionByName")]
public async Task<ActionResult<ResolutionViewModel>> GetResolutionByName(string name, CancellationToken cancellationToken)
[HttpGet("/api/ffmpeg/resolution/by-name/{name}", Name = "GetResolutionByName")]
[Tags("Settings")]
[EndpointSummary("Get a resolution by name")]
[ProducesResponseType(typeof(ResolutionResponseModel), StatusCodes.Status200OK)]
[ProducesResponseType(typeof(ProblemDetails), StatusCodes.Status404NotFound)]
public async Task<ActionResult<ResolutionResponseModel>> GetResolutionByName(
string name,
CancellationToken cancellationToken)
{
Option<ResolutionViewModel> result = await mediator.Send(new GetResolutionByName(name), cancellationToken);
return result.Match<ActionResult<ResolutionViewModel>>(i => Ok(i), () => NotFound());
return result.Match<ActionResult<ResolutionResponseModel>>(
i => Ok(ProjectToResponseModel(i)),
() => NotFound());
}
[HttpGet("/api/settings/resolutions", Name = "GetResolutions")]