Task A (#126): new non-polymorphic ScheduleItemResponseModel / ScheduleItemsResponseModel in Core/Api/Scheduling, plus shared NamedIdResponseModel. ScheduleItemResponseMapper flattens the One/Flood/Multiple/Duration VM hierarchy. ScheduleController GET/POST/PUT items now return the flat DTOs. Task B: GET /api/languages (LanguagesController + LanguageCodeResponseModel); GET /api/channels/music-video-credits-templates and GET /api/channels/stream-selectors; FillerKind added to FillerPresetResponseModel with optional ?fillerKind= filter on GET /api/filler-presets. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -3,6 +3,7 @@ using System.Threading.Channels;
|
||||
using ErsatzTV.Application;
|
||||
using ErsatzTV.Application.Channels;
|
||||
using ErsatzTV.Application.Playouts;
|
||||
using ErsatzTV.Application.Templates;
|
||||
using ErsatzTV.Controllers.Api.Requests;
|
||||
using ErsatzTV.Core;
|
||||
using ErsatzTV.Core.Api.Channels;
|
||||
@@ -43,6 +44,22 @@ public class ChannelController(ChannelWriter<IBackgroundServiceRequest> workerCh
|
||||
CancellationToken cancellationToken) =>
|
||||
await mediator.Send(new GetChannelGuideData(start, end), cancellationToken);
|
||||
|
||||
[HttpGet("/api/channels/music-video-credits-templates", Name = "GetMusicVideoCreditsTemplates")]
|
||||
[Tags("Channels")]
|
||||
[EndpointSummary("Get available music video credits template names")]
|
||||
[EndpointGroupName("general")]
|
||||
[ProducesResponseType(typeof(List<string>), StatusCodes.Status200OK)]
|
||||
public async Task<List<string>> GetMusicVideoCreditsTemplates(CancellationToken cancellationToken) =>
|
||||
await mediator.Send(new GetMusicVideoCreditTemplates(), cancellationToken);
|
||||
|
||||
[HttpGet("/api/channels/stream-selectors", Name = "GetChannelStreamSelectors")]
|
||||
[Tags("Channels")]
|
||||
[EndpointSummary("Get available channel stream selector names")]
|
||||
[EndpointGroupName("general")]
|
||||
[ProducesResponseType(typeof(List<string>), StatusCodes.Status200OK)]
|
||||
public async Task<List<string>> GetStreamSelectors(CancellationToken cancellationToken) =>
|
||||
await mediator.Send(new GetChannelStreamSelectors(), cancellationToken);
|
||||
|
||||
[HttpGet("/api/channels/{id:int}", Name = "GetChannelById")]
|
||||
[Tags("Channels")]
|
||||
[EndpointSummary("Get a channel by id")]
|
||||
|
||||
@@ -3,6 +3,7 @@ using ErsatzTV.Application.Filler;
|
||||
using ErsatzTV.Controllers.Api.Requests;
|
||||
using ErsatzTV.Core;
|
||||
using ErsatzTV.Core.Api.Filler;
|
||||
using ErsatzTV.Core.Domain.Filler;
|
||||
using ErsatzTV.Extensions;
|
||||
using MediatR;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
@@ -16,10 +17,13 @@ public class FillerPresetController(IMediator mediator) : ControllerBase
|
||||
[HttpGet("/api/filler-presets", Name = "GetFillerPresets")]
|
||||
[Tags("Filler Presets")]
|
||||
[EndpointSummary("Get all filler presets")]
|
||||
[EndpointDescription("Optionally filter to a single filler kind via the fillerKind query parameter.")]
|
||||
[EndpointGroupName("general")]
|
||||
[ProducesResponseType(typeof(List<FillerPresetResponseModel>), StatusCodes.Status200OK)]
|
||||
public async Task<List<FillerPresetResponseModel>> GetAll(CancellationToken cancellationToken) =>
|
||||
await mediator.Send(new GetAllFillerPresetsForApi(), cancellationToken);
|
||||
public async Task<List<FillerPresetResponseModel>> GetAll(
|
||||
[FromQuery] FillerKind? fillerKind,
|
||||
CancellationToken cancellationToken) =>
|
||||
await mediator.Send(new GetAllFillerPresetsForApi(fillerKind), cancellationToken);
|
||||
|
||||
[HttpGet("/api/filler-presets/{id:int}", Name = "GetFillerPresetById")]
|
||||
[Tags("Filler Presets")]
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
using System.Linq;
|
||||
using ErsatzTV.Application.MediaItems;
|
||||
using ErsatzTV.Core.Api.MediaItems;
|
||||
using MediatR;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace ErsatzTV.Controllers.Api;
|
||||
|
||||
[ApiController]
|
||||
public class LanguagesController(IMediator mediator) : ControllerBase
|
||||
{
|
||||
[HttpGet("/api/languages", Name = "GetLanguages")]
|
||||
[Tags("Languages")]
|
||||
[EndpointSummary("Get all available language codes")]
|
||||
[EndpointGroupName("general")]
|
||||
[ProducesResponseType(typeof(List<LanguageCodeResponseModel>), StatusCodes.Status200OK)]
|
||||
public async Task<List<LanguageCodeResponseModel>> GetLanguages(CancellationToken cancellationToken)
|
||||
{
|
||||
List<LanguageCodeViewModel> languages = await mediator.Send(new GetAllLanguageCodes(), cancellationToken);
|
||||
return languages
|
||||
.Select(l => new LanguageCodeResponseModel(l.ThreeLetterISOLanguageName, l.EnglishName))
|
||||
.ToList();
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,9 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Linq;
|
||||
using ErsatzTV.Application.ProgramSchedules;
|
||||
using ErsatzTV.Controllers.Api.Requests;
|
||||
using ErsatzTV.Core;
|
||||
using ErsatzTV.Core.Api.Scheduling;
|
||||
using ErsatzTV.Extensions;
|
||||
using MediatR;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
@@ -104,7 +106,7 @@ public class ScheduleController(IMediator mediator) : ControllerBase
|
||||
"nullable durationEstimate and the envelope carries a nullable totalDurationEstimate. Estimates are " +
|
||||
"derived from referenced collection/media runtimes and are null when unbounded or unknown.")]
|
||||
[EndpointGroupName("general")]
|
||||
[ProducesResponseType(typeof(ProgramScheduleItemsWithDurationViewModel), StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(typeof(ScheduleItemsResponseModel), StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(typeof(ProblemDetails), StatusCodes.Status404NotFound)]
|
||||
public async Task<IActionResult> GetItems(int id, CancellationToken cancellationToken)
|
||||
{
|
||||
@@ -116,14 +118,14 @@ public class ScheduleController(IMediator mediator) : ControllerBase
|
||||
|
||||
ProgramScheduleItemsWithDurationViewModel items =
|
||||
await mediator.Send(new GetProgramScheduleItemsWithDurations(id), cancellationToken);
|
||||
return new OkObjectResult(items);
|
||||
return new OkObjectResult(ScheduleItemResponseMapper.ProjectToResponseModel(items));
|
||||
}
|
||||
|
||||
[HttpPost("/api/schedules/{id:int}/items")]
|
||||
[Tags("Schedules")]
|
||||
[EndpointSummary("Add a schedule item")]
|
||||
[EndpointGroupName("general")]
|
||||
[ProducesResponseType(typeof(ProgramScheduleItemViewModel), StatusCodes.Status201Created)]
|
||||
[ProducesResponseType(typeof(ScheduleItemResponseModel), StatusCodes.Status201Created)]
|
||||
[ProducesResponseType(typeof(ProblemDetails), StatusCodes.Status404NotFound)]
|
||||
[ProducesResponseType(typeof(ProblemDetails), StatusCodes.Status422UnprocessableEntity)]
|
||||
public async Task<IActionResult> AddItem(
|
||||
@@ -133,14 +135,16 @@ public class ScheduleController(IMediator mediator) : ControllerBase
|
||||
{
|
||||
Either<BaseError, ProgramScheduleItemViewModel> result =
|
||||
await mediator.Send(request.ToAddCommand(id), cancellationToken);
|
||||
return result.ToCreatedResult(item => $"/api/schedules/{id}/items/{item.Id}", item => item);
|
||||
return result.ToCreatedResult(
|
||||
item => $"/api/schedules/{id}/items/{item.Id}",
|
||||
item => ScheduleItemResponseMapper.ProjectToResponseModel(item));
|
||||
}
|
||||
|
||||
[HttpPut("/api/schedules/{id:int}/items")]
|
||||
[Tags("Schedules")]
|
||||
[EndpointSummary("Replace schedule items")]
|
||||
[EndpointGroupName("general")]
|
||||
[ProducesResponseType(typeof(IEnumerable<ProgramScheduleItemViewModel>), StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(typeof(List<ScheduleItemResponseModel>), StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(typeof(ProblemDetails), StatusCodes.Status404NotFound)]
|
||||
[ProducesResponseType(typeof(ProblemDetails), StatusCodes.Status422UnprocessableEntity)]
|
||||
public async Task<IActionResult> ReplaceItems(
|
||||
@@ -150,7 +154,9 @@ public class ScheduleController(IMediator mediator) : ControllerBase
|
||||
{
|
||||
Either<BaseError, IEnumerable<ProgramScheduleItemViewModel>> result =
|
||||
await mediator.Send(request.ToCommand(id), cancellationToken);
|
||||
return result.ToUpdatedResult();
|
||||
return result
|
||||
.Map(items => items.Select(ScheduleItemResponseMapper.ProjectToResponseModel).ToList())
|
||||
.ToUpdatedResult();
|
||||
}
|
||||
|
||||
[HttpDelete("/api/schedules/{id:int}/items/{itemId:int}")]
|
||||
|
||||
Reference in New Issue
Block a user