189 lines
6.4 KiB
C#
Raw Normal View History

2022-05-01 00:33:22 -05:00
using System;
2022-06-13 01:52:41 -05:00
using System.Collections.Generic;
2022-05-01 00:33:22 -05:00
using System.Net.Mime;
using ConfusedPolarBear.Plugin.IntroSkipper.Configuration;
2022-07-29 03:34:55 -05:00
using MediaBrowser.Controller.Entities.TV;
2022-05-01 00:33:22 -05:00
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
namespace ConfusedPolarBear.Plugin.IntroSkipper.Controllers;
/// <summary>
/// Skip intro controller.
/// </summary>
[Authorize]
[ApiController]
[Produces(MediaTypeNames.Application.Json)]
public class SkipIntroController : ControllerBase
{
/// <summary>
/// Initializes a new instance of the <see cref="SkipIntroController"/> class.
2022-05-01 00:33:22 -05:00
/// </summary>
public SkipIntroController()
{
}
/// <summary>
/// Returns the timestamps of the introduction in a television episode. Responses are in API version 1 format.
2022-05-01 00:33:22 -05:00
/// </summary>
2022-05-01 01:24:57 -05:00
/// <param name="id">ID of the episode. Required.</param>
2022-11-25 00:40:02 -06:00
/// <param name="mode">Timestamps to return. Optional. Defaults to Introduction for backwards compatibility.</param>
2022-05-01 00:33:22 -05:00
/// <response code="200">Episode contains an intro.</response>
/// <response code="404">Failed to find an intro in the provided episode.</response>
/// <returns>Detected intro.</returns>
2022-05-01 00:33:22 -05:00
[HttpGet("Episode/{id}/IntroTimestamps")]
[HttpGet("Episode/{id}/IntroTimestamps/v1")]
2022-11-25 00:40:02 -06:00
public ActionResult<Intro> GetIntroTimestamps(
[FromRoute] Guid id,
[FromQuery] AnalysisMode mode = AnalysisMode.Introduction)
2022-05-01 00:33:22 -05:00
{
2022-11-25 00:40:02 -06:00
var intro = GetIntro(id, mode);
2022-05-01 00:33:22 -05:00
if (intro is null || !intro.Valid)
2022-05-01 00:33:22 -05:00
{
return NotFound();
}
return intro;
}
/// <summary>
/// Gets a dictionary of all skippable segments.
/// </summary>
/// <param name="id">Media ID.</param>
/// <response code="200">Skippable segments dictionary.</response>
/// <returns>Dictionary of skippable segments.</returns>
[HttpGet("Episode/{id}/IntroSkipperSegments")]
public ActionResult<Dictionary<AnalysisMode, Intro>> GetSkippableSegments([FromRoute] Guid id)
{
var segments = new Dictionary<AnalysisMode, Intro>();
if (GetIntro(id, AnalysisMode.Introduction) is Intro intro)
{
segments[AnalysisMode.Introduction] = intro;
}
if (GetIntro(id, AnalysisMode.Credits) is Intro credits)
{
segments[AnalysisMode.Credits] = credits;
}
return segments;
}
2022-11-25 00:40:02 -06:00
/// <summary>Lookup and return the skippable timestamps for the provided item.</summary>
/// <param name="id">Unique identifier of this episode.</param>
2022-11-25 00:40:02 -06:00
/// <param name="mode">Mode.</param>
/// <returns>Intro object if the provided item has an intro, null otherwise.</returns>
2022-11-25 00:40:02 -06:00
private Intro? GetIntro(Guid id, AnalysisMode mode)
{
2022-11-25 00:40:02 -06:00
try
{
var timestamp = mode == AnalysisMode.Introduction ?
Plugin.Instance!.Intros[id] :
Plugin.Instance!.Credits[id];
// Operate on a copy to avoid mutating the original Intro object stored in the dictionary.
var segment = new Intro(timestamp);
var config = Plugin.Instance!.Configuration;
2024-03-02 14:01:34 -05:00
segment.IntroEnd -= config.SecondsOfIntroToPlay;
2024-03-01 09:19:12 -05:00
if (config.PersistSkipButton)
2024-03-01 18:29:37 +01:00
{
segment.ShowSkipPromptAt = segment.IntroStart;
segment.HideSkipPromptAt = segment.IntroEnd;
}
else
2024-03-01 09:19:12 -05:00
{
segment.ShowSkipPromptAt = Math.Max(0, segment.IntroStart - config.ShowPromptAdjustment);
segment.HideSkipPromptAt = Math.Min(
segment.IntroStart + config.HidePromptAdjustment,
segment.IntroEnd);
}
return segment;
2022-11-25 00:40:02 -06:00
}
catch (KeyNotFoundException)
{
return null;
}
}
/// <summary>
/// Erases all previously discovered introduction timestamps.
/// </summary>
/// <param name="mode">Mode.</param>
/// <response code="204">Operation successful.</response>
/// <returns>No content.</returns>
[Authorize(Policy = "RequiresElevation")]
[HttpPost("Intros/EraseTimestamps")]
public ActionResult ResetIntroTimestamps([FromQuery] AnalysisMode mode)
{
if (mode == AnalysisMode.Introduction)
{
Plugin.Instance!.Intros.Clear();
}
else if (mode == AnalysisMode.Credits)
{
Plugin.Instance!.Credits.Clear();
}
Plugin.Instance!.SaveTimestamps();
return NoContent();
}
2022-06-13 01:52:41 -05:00
/// <summary>
/// Get all introductions or credits. Only used by the end to end testing script.
2022-06-13 01:52:41 -05:00
/// </summary>
/// <param name="mode">Mode.</param>
/// <response code="200">All timestamps have been returned.</response>
2022-07-29 03:34:55 -05:00
/// <returns>List of IntroWithMetadata objects.</returns>
2022-06-13 01:52:41 -05:00
[Authorize(Policy = "RequiresElevation")]
[HttpGet("Intros/All")]
public ActionResult<List<IntroWithMetadata>> GetAllTimestamps(
[FromQuery] AnalysisMode mode = AnalysisMode.Introduction)
2022-06-13 01:52:41 -05:00
{
2022-07-29 03:34:55 -05:00
List<IntroWithMetadata> intros = new();
var timestamps = mode == AnalysisMode.Introduction ?
Plugin.Instance!.Intros :
Plugin.Instance!.Credits;
2022-07-29 03:34:55 -05:00
// Get metadata for all intros
foreach (var intro in timestamps)
2022-07-29 03:34:55 -05:00
{
// Get the details of the item from Jellyfin
var rawItem = Plugin.Instance!.GetItem(intro.Key);
if (rawItem is not Episode episode)
{
throw new InvalidCastException("Unable to cast item id " + intro.Key + " to an Episode");
}
// Associate the metadata with the intro
intros.Add(
new IntroWithMetadata(
episode.SeriesName,
episode.AiredSeasonNumber ?? 0,
episode.Name,
intro.Value));
}
return intros;
2022-06-13 01:52:41 -05:00
}
/// <summary>
/// Gets the user interface configuration.
/// </summary>
/// <response code="200">UserInterfaceConfiguration returned.</response>
/// <returns>UserInterfaceConfiguration.</returns>
[Route("Intros/UserInterfaceConfiguration")]
public ActionResult<UserInterfaceConfiguration> GetUserInterfaceConfiguration()
{
var config = Plugin.Instance!.Configuration;
return new UserInterfaceConfiguration(
config.SkipButtonVisible,
config.SkipButtonIntroText,
config.SkipButtonEndCreditsText);
}
2022-05-01 00:33:22 -05:00
}