using System.Threading; using System.Threading.Tasks; using Jellyfin.Plugin.SRFPlay.Api.Models; using Jellyfin.Plugin.SRFPlay.Services.Interfaces; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Logging; namespace Jellyfin.Plugin.SRFPlay.Controllers; /// /// Controller for managing sport livestream recordings. /// [ApiController] [Route("Plugins/SRFPlay/Recording")] [Authorize] public class RecordingController : ControllerBase { private readonly ILogger _logger; private readonly IRecordingService _recordingService; /// /// Initializes a new instance of the class. /// /// The logger. /// The recording service. public RecordingController( ILogger logger, IRecordingService recordingService) { _logger = logger; _recordingService = recordingService; } /// /// Gets upcoming sport livestreams available for recording. /// /// The cancellation token. /// List of upcoming livestreams. [HttpGet("Schedule")] [ProducesResponseType(StatusCodes.Status200OK)] public async Task GetSchedule(CancellationToken cancellationToken) { var schedule = await _recordingService.GetUpcomingScheduleAsync(cancellationToken).ConfigureAwait(false); return Ok(schedule); } /// /// Schedules a livestream for recording by URN. /// /// The SRF URN. /// The cancellation token. /// The created recording entry. [HttpPost("Schedule/{urn}")] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status400BadRequest)] public async Task ScheduleRecording( [FromRoute] string urn, CancellationToken cancellationToken) { if (string.IsNullOrEmpty(urn)) { return BadRequest("URN is required"); } // URN comes URL-encoded with colons, decode it urn = System.Net.WebUtility.UrlDecode(urn); _logger.LogInformation("Scheduling recording for URN: {Urn}", urn); var entry = await _recordingService.ScheduleRecordingAsync(urn, cancellationToken).ConfigureAwait(false); return Ok(entry); } /// /// Cancels a scheduled recording. /// /// The recording ID. /// OK or NotFound. [HttpDelete("Schedule/{id}")] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status404NotFound)] public IActionResult CancelRecording([FromRoute] string id) { return _recordingService.CancelRecording(id) ? Ok() : NotFound(); } /// /// Gets currently active recordings. /// /// List of active recordings. [HttpGet("Active")] [ProducesResponseType(StatusCodes.Status200OK)] public IActionResult GetActiveRecordings() { var active = _recordingService.GetRecordings(RecordingState.Recording); return Ok(active); } /// /// Stops an active recording. /// /// The recording ID. /// OK or NotFound. [HttpPost("Active/{id}/Stop")] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status404NotFound)] public IActionResult StopRecording([FromRoute] string id) { return _recordingService.StopRecording(id) ? Ok() : NotFound(); } /// /// Gets completed recordings. /// /// List of completed recordings. [HttpGet("Completed")] [ProducesResponseType(StatusCodes.Status200OK)] public IActionResult GetCompletedRecordings() { var completed = _recordingService.GetRecordings(RecordingState.Completed); return Ok(completed); } /// /// Gets all recordings (all states). /// /// List of all recordings. [HttpGet("All")] [ProducesResponseType(StatusCodes.Status200OK)] public IActionResult GetAllRecordings() { var all = _recordingService.GetRecordings(); return Ok(all); } /// /// Deletes a completed recording and its file. /// /// The recording ID. /// Whether to delete the file too. /// OK or NotFound. [HttpDelete("Completed/{id}")] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status404NotFound)] public IActionResult DeleteRecording( [FromRoute] string id, [FromQuery] bool deleteFile = true) { return _recordingService.DeleteRecording(id, deleteFile) ? Ok() : NotFound(); } }