using System;
using System.Threading;
using System.Threading.Tasks;
using Jellyfin.Plugin.JRay.Models;
using Jellyfin.Plugin.JRay.Services;
using Jellyfin.Plugin.JRay.Services.Interfaces;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
namespace Jellyfin.Plugin.JRay.Controllers;
///
/// Exposes scene-actor-extraction "truth" data: which actors are present in
/// the scene at a given timestamp.
///
///
/// Presence is scene-scoped, not instantaneous: a window is a claim about
/// scene membership, not a recognition event, so an actor who is off-camera
/// during a reverse shot is still present. Windows are served exactly as stored
/// — never merged, split or trimmed.
///
[ApiController]
[Route("Plugins/JRay/Items/{itemId}")]
[Authorize]
// TRACES: JR-004, JR-005, JR-012, JR-013, JR-014 | SR-002
public class ActorsController : ControllerBase
{
private readonly ITruthDataService _truthDataService;
///
/// Initializes a new instance of the class.
///
/// The truth data service.
public ActorsController(ITruthDataService truthDataService)
{
_truthDataService = truthDataService;
}
///
/// Gets the full actor timeline (every actor with their scene-presence windows) for a movie.
///
/// The Jellyfin item id.
/// Cancellation token.
/// The truth file contents, or 404 if no truth data exists for this item.
[HttpGet("Timeline")]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
public async Task> GetTimeline(Guid itemId, CancellationToken cancellationToken)
{
var truth = await _truthDataService.GetTruthAsync(itemId, cancellationToken).ConfigureAwait(false);
if (truth is null)
{
return NotFound();
}
return Ok(truth);
}
///
/// Gets the JRay context (currently: the actors in the scene) at a given timestamp.
/// This is an extensible envelope — future fields (locations, trivia, etc.)
/// will be added here without changing the route.
///
/// The Jellyfin item id.
/// The timestamp, in seconds from the start of the movie.
/// Cancellation token.
/// The JRay context at , or 404 if no truth data exists for this item.
[HttpGet("jray")]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
public async Task> GetContext(Guid itemId, [FromQuery] double t, CancellationToken cancellationToken)
{
var truth = await _truthDataService.GetTruthAsync(itemId, cancellationToken).ConfigureAwait(false);
if (truth is null)
{
return NotFound();
}
var context = new JRayContext();
foreach (var actor in PresenceLookup.ActorsPresentAt(truth, t))
{
context.Actors.Add(new ActorInScene
{
Name = actor.Name,
ImdbId = actor.ImdbId,
TmdbId = actor.TmdbId,
JellyfinId = actor.JellyfinId
});
}
return Ok(context);
}
}