175 lines
5.9 KiB
C#
175 lines
5.9 KiB
C#
using System.IO;
|
|
using System.Reflection;
|
|
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;
|
|
|
|
/// <summary>
|
|
/// Controller for managing sport livestream recordings.
|
|
/// </summary>
|
|
[ApiController]
|
|
[Route("Plugins/SRFPlay/Recording")]
|
|
[Authorize]
|
|
public class RecordingController : ControllerBase
|
|
{
|
|
private readonly ILogger<RecordingController> _logger;
|
|
private readonly IRecordingService _recordingService;
|
|
|
|
/// <summary>
|
|
/// Initializes a new instance of the <see cref="RecordingController"/> class.
|
|
/// </summary>
|
|
/// <param name="logger">The logger.</param>
|
|
/// <param name="recordingService">The recording service.</param>
|
|
public RecordingController(
|
|
ILogger<RecordingController> logger,
|
|
IRecordingService recordingService)
|
|
{
|
|
_logger = logger;
|
|
_recordingService = recordingService;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Serves the recording manager page accessible to any authenticated user.
|
|
/// </summary>
|
|
/// <returns>The recording manager HTML page.</returns>
|
|
[HttpGet("Page")]
|
|
[ProducesResponseType(StatusCodes.Status200OK)]
|
|
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
|
public IActionResult GetRecordingPage()
|
|
{
|
|
var assembly = Assembly.GetExecutingAssembly();
|
|
var resourceStream = assembly.GetManifestResourceStream("Jellyfin.Plugin.SRFPlay.Configuration.recordingPage.html");
|
|
|
|
if (resourceStream == null)
|
|
{
|
|
return NotFound("Recording page not found");
|
|
}
|
|
|
|
return File(resourceStream, "text/html");
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets upcoming sport livestreams available for recording.
|
|
/// </summary>
|
|
/// <param name="cancellationToken">The cancellation token.</param>
|
|
/// <returns>List of upcoming livestreams.</returns>
|
|
[HttpGet("Schedule")]
|
|
[ProducesResponseType(StatusCodes.Status200OK)]
|
|
public async Task<IActionResult> GetSchedule(CancellationToken cancellationToken)
|
|
{
|
|
var schedule = await _recordingService.GetUpcomingScheduleAsync(cancellationToken).ConfigureAwait(false);
|
|
return Ok(schedule);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Schedules a livestream for recording by URN.
|
|
/// </summary>
|
|
/// <param name="urn">The SRF URN.</param>
|
|
/// <param name="cancellationToken">The cancellation token.</param>
|
|
/// <returns>The created recording entry.</returns>
|
|
[HttpPost("Schedule/{urn}")]
|
|
[ProducesResponseType(StatusCodes.Status200OK)]
|
|
[ProducesResponseType(StatusCodes.Status400BadRequest)]
|
|
public async Task<IActionResult> 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);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Cancels a scheduled recording.
|
|
/// </summary>
|
|
/// <param name="id">The recording ID.</param>
|
|
/// <returns>OK or NotFound.</returns>
|
|
[HttpDelete("Schedule/{id}")]
|
|
[ProducesResponseType(StatusCodes.Status200OK)]
|
|
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
|
public IActionResult CancelRecording([FromRoute] string id)
|
|
{
|
|
return _recordingService.CancelRecording(id) ? Ok() : NotFound();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets currently active recordings.
|
|
/// </summary>
|
|
/// <returns>List of active recordings.</returns>
|
|
[HttpGet("Active")]
|
|
[ProducesResponseType(StatusCodes.Status200OK)]
|
|
public IActionResult GetActiveRecordings()
|
|
{
|
|
var active = _recordingService.GetRecordings(RecordingState.Recording);
|
|
return Ok(active);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Stops an active recording.
|
|
/// </summary>
|
|
/// <param name="id">The recording ID.</param>
|
|
/// <returns>OK or NotFound.</returns>
|
|
[HttpPost("Active/{id}/Stop")]
|
|
[ProducesResponseType(StatusCodes.Status200OK)]
|
|
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
|
public IActionResult StopRecording([FromRoute] string id)
|
|
{
|
|
return _recordingService.StopRecording(id) ? Ok() : NotFound();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets completed recordings.
|
|
/// </summary>
|
|
/// <returns>List of completed recordings.</returns>
|
|
[HttpGet("Completed")]
|
|
[ProducesResponseType(StatusCodes.Status200OK)]
|
|
public IActionResult GetCompletedRecordings()
|
|
{
|
|
var completed = _recordingService.GetRecordings(RecordingState.Completed);
|
|
return Ok(completed);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets all recordings (all states).
|
|
/// </summary>
|
|
/// <returns>List of all recordings.</returns>
|
|
[HttpGet("All")]
|
|
[ProducesResponseType(StatusCodes.Status200OK)]
|
|
public IActionResult GetAllRecordings()
|
|
{
|
|
var all = _recordingService.GetRecordings();
|
|
return Ok(all);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Deletes a completed recording and its file.
|
|
/// </summary>
|
|
/// <param name="id">The recording ID.</param>
|
|
/// <param name="deleteFile">Whether to delete the file too.</param>
|
|
/// <returns>OK or NotFound.</returns>
|
|
[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();
|
|
}
|
|
}
|