using System.ComponentModel; using System.ComponentModel.DataAnnotations; using ErsatzTV.Application.Images; using ErsatzTV.Controllers.Api.Requests; using ErsatzTV.Core; using ErsatzTV.Core.Api.Images; using ErsatzTV.Extensions; using MediatR; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; namespace ErsatzTV.Controllers.Api; [ApiController] public class ImagesController(IMediator mediator) : ControllerBase { [HttpGet("/api/v1/images/folders", Name = "GetImageFolders")] [Tags("Images")] [EndpointSummary("List image library folders")] [EndpointDescription("Omit parentId for the top-level folders; pass a folder id to list that folder's children.")] [EndpointGroupName("general")] [ProducesResponseType(typeof(List), StatusCodes.Status200OK)] public async Task> GetFolders( [FromQuery] [Description("Parent image library-folder id; omit for the top-level folders")] int? parentId = null, CancellationToken cancellationToken = default) { Option maybeParentId = parentId ?? Option.None; List folders = await mediator.Send(new GetImageFolders(maybeParentId), cancellationToken); return folders.Map(Project).ToList(); } [HttpPut("/api/v1/images/folders/{id:int}/duration", Name = "UpdateImageFolderDuration")] [Tags("Images")] [EndpointSummary("Set or clear an image folder's playout duration")] [EndpointDescription( "Pass a positive durationSeconds to set the per-image duration for this folder (cascades to descendant " + "images that don't override it); pass null to clear it and inherit from an ancestor.")] [EndpointGroupName("general")] [ProducesResponseType(typeof(UpdateImageFolderDurationResponseModel), StatusCodes.Status200OK)] [ProducesResponseType(typeof(ProblemDetails), StatusCodes.Status400BadRequest)] [ProducesResponseType(typeof(ProblemDetails), StatusCodes.Status404NotFound)] public async Task UpdateDuration( int id, [Required][FromBody] UpdateImageFolderDurationRequest request, CancellationToken cancellationToken) { if (request.DurationSeconds is <= 0) { return BadRequest( new ProblemDetails { Status = StatusCodes.Status400BadRequest, Title = "Validation failed", Detail = "durationSeconds must be greater than zero, or null to clear" }); } bool exists = await mediator.Send(new ImageFolderExists(id), cancellationToken); if (!exists) { return ApiResults.NotFoundProblem("Image folder not found"); } double? duration = await mediator.Send( new UpdateImageFolderDuration(id, request.DurationSeconds), cancellationToken); return Ok(new UpdateImageFolderDurationResponseModel(duration)); } private static ImageFolderResponseModel Project(ImageFolderViewModel vm) => new( vm.LibraryFolderId, vm.Name, vm.FullPath, vm.SubfolderCount, vm.ImageCount, vm.DurationSeconds.ToNullable()); }