199 lines
6.7 KiB
C#
Raw Normal View History

2022-05-30 02:23:36 -05:00
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Globalization;
using System.Net.Mime;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
2022-06-29 20:52:16 -05:00
using Microsoft.Extensions.Logging;
2022-05-30 02:23:36 -05:00
namespace ConfusedPolarBear.Plugin.IntroSkipper.Controllers;
/// <summary>
2022-05-31 16:18:17 -05:00
/// Audio fingerprint visualization controller. Allows browsing fingerprints on a per episode basis.
2022-05-30 02:23:36 -05:00
/// </summary>
[Authorize(Policy = "RequiresElevation")]
2022-05-30 02:23:36 -05:00
[ApiController]
[Produces(MediaTypeNames.Application.Json)]
[Route("Intros")]
2022-05-31 16:18:17 -05:00
public class VisualizationController : ControllerBase
2022-05-30 02:23:36 -05:00
{
2022-06-29 20:52:16 -05:00
private readonly ILogger<VisualizationController> _logger;
2022-05-30 02:23:36 -05:00
/// <summary>
2022-05-31 16:18:17 -05:00
/// Initializes a new instance of the <see cref="VisualizationController"/> class.
2022-05-30 02:23:36 -05:00
/// </summary>
2022-06-29 20:52:16 -05:00
/// <param name="logger">Logger.</param>
public VisualizationController(ILogger<VisualizationController> logger)
2022-05-30 02:23:36 -05:00
{
2022-06-29 20:52:16 -05:00
_logger = logger;
2022-05-30 02:23:36 -05:00
}
/// <summary>
/// Returns all show names and seasons.
/// </summary>
/// <returns>Dictionary of show names to a list of season names.</returns>
[HttpGet("Shows")]
public ActionResult<Dictionary<string, HashSet<string>>> GetShowSeasons()
{
2022-06-29 20:52:16 -05:00
_logger.LogDebug("Returning season names by series");
2022-05-30 02:23:36 -05:00
var showSeasons = new Dictionary<string, HashSet<string>>();
2022-06-29 20:52:16 -05:00
// Loop through all seasons in the analysis queue
foreach (var kvp in Plugin.Instance!.AnalysisQueue)
2022-05-30 02:23:36 -05:00
{
2022-06-29 20:52:16 -05:00
// Check that this season contains at least one episode.
var episodes = kvp.Value;
if (episodes is null || episodes.Count == 0)
2022-05-30 02:23:36 -05:00
{
2022-06-29 20:52:16 -05:00
_logger.LogDebug("Skipping season {Id} (null or empty)", kvp.Key);
continue;
}
2022-05-30 02:23:36 -05:00
2022-06-29 20:52:16 -05:00
// Peek at the top episode from this season and store the series name and season number.
var first = episodes[0];
var series = first.SeriesName;
var season = GetSeasonName(first);
2022-05-30 02:23:36 -05:00
2022-06-29 20:52:16 -05:00
// Validate the series and season before attempting to store it.
if (string.IsNullOrWhiteSpace(series) || string.IsNullOrWhiteSpace(season))
{
_logger.LogDebug("Skipping season {Id} (no name or number)", kvp.Key);
continue;
2022-05-30 02:23:36 -05:00
}
2022-06-29 20:52:16 -05:00
// TryAdd is used when adding the HashSet since it is a no-op if one was already created for this series.
showSeasons.TryAdd(series, new HashSet<string>());
showSeasons[series].Add(season);
2022-05-30 02:23:36 -05:00
}
return showSeasons;
}
/// <summary>
/// Returns the names and unique identifiers of all episodes in the provided season.
/// </summary>
/// <param name="series">Show name.</param>
/// <param name="season">Season name.</param>
/// <returns>List of episode titles.</returns>
[HttpGet("Show/{Series}/{Season}")]
2022-05-31 16:18:17 -05:00
public ActionResult<List<EpisodeVisualization>> GetSeasonEpisodes(
2022-05-30 02:23:36 -05:00
[FromRoute] string series,
[FromRoute] string season)
{
var visualEpisodes = new List<EpisodeVisualization>();
2022-05-30 02:23:36 -05:00
if (!LookupSeasonByName(series, season, out var episodes))
2022-05-30 02:23:36 -05:00
{
return NotFound();
}
2022-05-30 02:23:36 -05:00
foreach (var e in episodes)
{
visualEpisodes.Add(new EpisodeVisualization(e.EpisodeId, e.Name));
2022-05-30 02:23:36 -05:00
}
return visualEpisodes;
2022-05-30 02:23:36 -05:00
}
/// <summary>
/// Fingerprint the provided episode and returns the uncompressed fingerprint data points.
/// </summary>
/// <param name="id">Episode id.</param>
/// <returns>Read only collection of fingerprint points.</returns>
[HttpGet("Fingerprint/{Id}")]
public ActionResult<uint[]> GetEpisodeFingerprint([FromRoute] Guid id)
2022-05-30 02:23:36 -05:00
{
var queue = Plugin.Instance!.AnalysisQueue;
// Search through all queued episodes to find the requested id
foreach (var season in queue)
{
foreach (var needle in season.Value)
{
if (needle.EpisodeId == id)
{
2022-06-09 17:33:39 -05:00
return Chromaprint.Fingerprint(needle);
2022-05-30 02:23:36 -05:00
}
}
}
return NotFound();
}
2022-07-03 01:20:33 -05:00
/// <summary>
/// Erases all timestamps for the provided season.
/// </summary>
/// <param name="series">Show name.</param>
/// <param name="season">Season name.</param>
/// <response code="204">Season timestamps erased.</response>
/// <response code="404">Unable to find season in provided series.</response>
/// <returns>No content.</returns>
[HttpDelete("Show/{Series}/{Season}")]
public ActionResult EraseSeason([FromRoute] string series, [FromRoute] string season)
{
if (!LookupSeasonByName(series, season, out var episodes))
{
return NotFound();
}
_logger.LogInformation("Erasing timestamps for {Series} {Season} at user request", series, season);
foreach (var e in episodes)
{
Plugin.Instance!.Intros.Remove(e.EpisodeId);
}
Plugin.Instance!.SaveTimestamps();
return NoContent();
}
2022-07-04 02:03:10 -05:00
/// <summary>
/// Returns the statistics for the most recent analysis.
/// </summary>
/// <response code="200">Analysis statistics.</response>
/// <returns>AnalysisStatistics.</returns>
[HttpGet("Statistics")]
public ActionResult<AnalysisStatistics> GetAnalysisStatistics()
{
return Plugin.Instance!.AnalysisStatistics;
}
2022-05-30 02:23:36 -05:00
private string GetSeasonName(QueuedEpisode episode)
{
return "Season " + episode.SeasonNumber.ToString(CultureInfo.InvariantCulture);
}
/// <summary>
/// Lookup a named season of a series and return all queued episodes.
/// </summary>
/// <param name="series">Series name.</param>
/// <param name="season">Season name.</param>
/// <param name="episodes">Episodes.</param>
/// <returns>Boolean indicating if the requested season was found.</returns>
private bool LookupSeasonByName(string series, string season, out List<QueuedEpisode> episodes)
{
foreach (var queuedEpisodes in Plugin.Instance!.AnalysisQueue)
{
var first = queuedEpisodes.Value[0];
var firstSeasonName = GetSeasonName(first);
// Assert that the queued episode series and season are equal to what was requested
if (
!string.Equals(first.SeriesName, series, StringComparison.OrdinalIgnoreCase) ||
!string.Equals(firstSeasonName, season, StringComparison.OrdinalIgnoreCase))
{
continue;
}
episodes = queuedEpisodes.Value;
return true;
}
episodes = new List<QueuedEpisode>();
return false;
}
2022-05-30 02:23:36 -05:00
}