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; using Microsoft.Extensions.Logging; namespace ConfusedPolarBear.Plugin.IntroSkipper.Controllers; /// /// Audio fingerprint visualization controller. Allows browsing fingerprints on a per episode basis. /// [Authorize(Policy = "RequiresElevation")] [ApiController] [Produces(MediaTypeNames.Application.Json)] [Route("Intros")] public class VisualizationController : ControllerBase { private readonly ILogger _logger; /// /// Initializes a new instance of the class. /// /// Logger. public VisualizationController(ILogger logger) { _logger = logger; } /// /// Returns all show names and seasons. /// /// Dictionary of show names to a list of season names. [HttpGet("Shows")] public ActionResult>> GetShowSeasons() { _logger.LogDebug("Returning season names by series"); var showSeasons = new Dictionary>(); // Loop through all seasons in the analysis queue foreach (var kvp in Plugin.Instance!.AnalysisQueue) { // Check that this season contains at least one episode. var episodes = kvp.Value; if (episodes is null || episodes.Count == 0) { _logger.LogDebug("Skipping season {Id} (null or empty)", kvp.Key); continue; } // 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); // 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; } // 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()); showSeasons[series].Add(season); } return showSeasons; } /// /// Returns the names and unique identifiers of all episodes in the provided season. /// /// Show name. /// Season name. /// List of episode titles. [HttpGet("Show/{Series}/{Season}")] public ActionResult> GetSeasonEpisodes( [FromRoute] string series, [FromRoute] string season) { var episodes = new List(); 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; } foreach (var queuedEpisode in queuedEpisodes.Value) { episodes.Add(new EpisodeVisualization(queuedEpisode.EpisodeId, queuedEpisode.Name)); } } return episodes; } /// /// Fingerprint the provided episode and returns the uncompressed fingerprint data points. /// /// Episode id. /// Read only collection of fingerprint points. [HttpGet("Fingerprint/{Id}")] public ActionResult> GetEpisodeFingerprint([FromRoute] Guid id) { 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) { return Chromaprint.Fingerprint(needle); } } } return NotFound(); } private string GetSeasonName(QueuedEpisode episode) { return "Season " + episode.SeasonNumber.ToString(CultureInfo.InvariantCulture); } }