intro-skipper/IntroSkipper/Controllers/VisualizationController.cs

250 lines
8.9 KiB
C#
Raw Normal View History

2024-10-25 14:31:50 -04:00
// Copyright (C) 2024 Intro-Skipper contributors <intro-skipper.org>
// SPDX-License-Identifier: GPL-3.0-only.
2024-10-25 14:15:12 -04:00
2022-05-30 02:23:36 -05:00
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
2022-05-30 02:23:36 -05:00
using System.Net.Mime;
using System.Threading;
using System.Threading.Tasks;
using IntroSkipper.Data;
using IntroSkipper.Db;
using IntroSkipper.Manager;
2024-05-13 23:50:51 +02:00
using MediaBrowser.Common.Api;
2022-05-30 02:23:36 -05:00
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 IntroSkipper.Controllers;
2022-05-30 02:23:36 -05:00
/// <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>
/// <remarks>
/// Initializes a new instance of the <see cref="VisualizationController"/> class.
/// </remarks>
/// <param name="logger">Logger.</param>
/// <param name="mediaSegmentUpdateManager">Media Segment Update Manager.</param>
2024-05-13 23:50:51 +02:00
[Authorize(Policy = Policies.RequiresElevation)]
2022-05-30 02:23:36 -05:00
[ApiController]
[Produces(MediaTypeNames.Application.Json)]
[Route("Intros")]
public class VisualizationController(ILogger<VisualizationController> logger, MediaSegmentUpdateManager mediaSegmentUpdateManager) : ControllerBase
2022-05-30 02:23:36 -05:00
{
2024-10-31 11:58:56 +01:00
private readonly ILogger<VisualizationController> _logger = logger;
private readonly MediaSegmentUpdateManager _mediaSegmentUpdateManager = mediaSegmentUpdateManager;
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<Guid, ShowInfos>> GetShowSeasons()
2022-05-30 02:23:36 -05:00
{
2024-10-31 11:58:56 +01:00
_logger.LogDebug("Returning season IDs by series name");
2022-06-29 20:52:16 -05:00
var showSeasons = new Dictionary<Guid, ShowInfos>();
2022-05-30 02:23:36 -05:00
foreach (var kvp in Plugin.Instance!.QueuedMediaItems)
2022-05-30 02:23:36 -05:00
{
if (kvp.Value.FirstOrDefault() is QueuedEpisode first)
2022-05-30 02:23:36 -05:00
{
var seriesId = first.SeriesId;
var seasonId = kvp.Key;
2022-05-30 02:23:36 -05:00
var seasonNumber = first.SeasonNumber;
if (!showSeasons.TryGetValue(seriesId, out var showInfo))
{
showInfo = new ShowInfos { SeriesName = first.SeriesName, ProductionYear = GetProductionYear(seriesId), LibraryName = GetLibraryName(seriesId), IsMovie = first.IsMovie, Seasons = [] };
showSeasons[seriesId] = showInfo;
}
2022-05-30 02:23:36 -05:00
showInfo.Seasons[seasonId] = seasonNumber;
2022-05-30 02:23:36 -05:00
}
}
// Sort the dictionary by SeriesName and the seasons by SeasonName
var sortedShowSeasons = showSeasons
.OrderBy(kvp => kvp.Value.SeriesName)
.ToDictionary(
kvp => kvp.Key,
kvp => new ShowInfos
{
SeriesName = kvp.Value.SeriesName,
ProductionYear = kvp.Value.ProductionYear,
LibraryName = kvp.Value.LibraryName,
IsMovie = kvp.Value.IsMovie,
Seasons = kvp.Value.Seasons
.OrderBy(s => s.Value)
.ToDictionary(s => s.Key, s => s.Value)
});
return sortedShowSeasons;
2022-05-30 02:23:36 -05:00
}
/// <summary>
/// Returns the analyzer actions for the provided season.
/// </summary>
/// <param name="seasonId">Season ID.</param>
/// <returns>List of episode titles.</returns>
[HttpGet("AnalyzerActions/{SeasonId}")]
public ActionResult<IReadOnlyDictionary<AnalysisMode, AnalyzerAction>> GetAnalyzerAction([FromRoute] Guid seasonId)
{
if (!Plugin.Instance!.QueuedMediaItems.ContainsKey(seasonId))
{
return NotFound();
}
Recaps and Previews Support (#357) * Recaps and Previews Support * Add draft UI of preview / recap edit * remove intro/credit tasks * Update configPage.html * rename task * Reorganize settings by relation * More standardized formatting * Some additional formatting * fix a typo * Update configPage.html * Allow missing recap / prview data * More risk to corrupt than benefit * Update TimeStamps.cs * Update PluginConfiguration.cs * Update configPage.html * Update PluginConfiguration.cs * Add chapter regex to settings * Move all UI into UI section * Move ending seconds with similar * Add default * fixes * Update SkipIntroController.cs * Autoskip all segments * Check if adjacent segment * Update AutoSkip.cs * Update AutoSkip.cs * Settings apply to all segment types * Update SegmentProvider * Update configPage.html Whoops * Update Plugin.cs * Update AutoSkip.cs * Let’s call it missing instead * Update BaseItemAnalyzerTask.cs * Update BaseItemAnalyzerTask.cs * Update BaseItemAnalyzerTask.cs * Move "select" all below list * Clarify button wording * Update configPage.html * Nope, long client list will hide it * Simplify wording * Update QueuedEpisode.cs * fix unit test for ffmpeg7 * Add migration * Restore DataContract * update * Update configPage.html * remove analyzed status * Update AutoSkip.cs * Update configPage.html typo * Store analyzed items in seasoninfo * Update VisualizationController.cs * update * Update IntroSkipperDbContext.cs * Add preview / recap delete * This keeps changing itself * Update SkipIntroController.cs * Rather add it to be removed --------- Co-authored-by: rlauu <46294892+rlauu@users.noreply.github.com> Co-authored-by: TwistedUmbrellaX <1173913+AbandonedCart@users.noreply.github.com> Co-authored-by: Kilian von Pflugk <github@jumoog.io>
2024-11-21 15:42:55 +01:00
var analyzerActions = new Dictionary<AnalysisMode, AnalyzerAction>();
foreach (var mode in Enum.GetValues<AnalysisMode>())
{
analyzerActions[mode] = Plugin.Instance!.GetAnalyzerAction(seasonId, mode);
}
return Ok(analyzerActions);
}
2022-05-30 02:23:36 -05:00
/// <summary>
/// Returns the names and unique identifiers of all episodes in the provided season.
/// </summary>
/// <param name="seriesId">Show ID.</param>
/// <param name="seasonId">Season ID.</param>
2022-05-30 02:23:36 -05:00
/// <returns>List of episode titles.</returns>
[HttpGet("Show/{SeriesId}/{SeasonId}")]
public ActionResult<List<EpisodeVisualization>> GetSeasonEpisodes([FromRoute] Guid seriesId, [FromRoute] Guid seasonId)
2022-05-30 02:23:36 -05:00
{
if (!Plugin.Instance!.QueuedMediaItems.TryGetValue(seasonId, out var episodes))
2022-05-30 02:23:36 -05:00
{
return NotFound();
}
2022-05-30 02:23:36 -05:00
if (!episodes.Any(e => e.SeriesId == seriesId))
{
return NotFound();
2022-05-30 02:23:36 -05:00
}
var showName = episodes.FirstOrDefault()?.SeriesName!;
return episodes.Select(e => new EpisodeVisualization(e.EpisodeId, e.Name)).ToList();
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("Episode/{Id}/Chromaprint")]
public ActionResult<uint[]> GetEpisodeFingerprint([FromRoute] Guid id)
2022-05-30 02:23:36 -05:00
{
// Search through all queued episodes to find the requested id
foreach (var season in Plugin.Instance!.QueuedMediaItems)
2022-05-30 02:23:36 -05:00
{
foreach (var needle in season.Value)
{
if (needle.EpisodeId == id)
{
return FFmpegWrapper.Fingerprint(needle, AnalysisMode.Introduction);
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="seriesId">Show ID.</param>
/// <param name="seasonId">Season ID.</param>
/// <param name="eraseCache">Erase cache.</param>
2024-10-31 11:58:56 +01:00
/// <param name="cancellationToken">Cancellation Token.</param>
2022-07-03 01:20:33 -05:00
/// <response code="204">Season timestamps erased.</response>
/// <response code="404">Unable to find season in provided series.</response>
/// <returns>No content.</returns>
[HttpDelete("Show/{SeriesId}/{SeasonId}")]
2024-10-31 11:58:56 +01:00
public async Task<ActionResult> EraseSeasonAsync([FromRoute] Guid seriesId, [FromRoute] Guid seasonId, [FromQuery] bool eraseCache = false, CancellationToken cancellationToken = default)
2022-07-03 01:20:33 -05:00
{
2024-10-31 11:58:56 +01:00
var episodes = Plugin.Instance!.QueuedMediaItems[seasonId];
if (episodes.Count == 0)
2022-07-03 01:20:33 -05:00
{
return NotFound();
}
2024-10-31 11:58:56 +01:00
_logger.LogInformation("Erasing timestamps for series {SeriesId} season {SeasonId} at user request", seriesId, seasonId);
2022-07-03 01:20:33 -05:00
2024-10-31 11:58:56 +01:00
try
2022-07-03 01:20:33 -05:00
{
using var db = new IntroSkipperDbContext(Plugin.Instance!.DbPath);
2024-10-31 11:58:56 +01:00
foreach (var episode in episodes)
{
2024-10-31 11:58:56 +01:00
cancellationToken.ThrowIfCancellationRequested();
Recaps and Previews Support (#357) * Recaps and Previews Support * Add draft UI of preview / recap edit * remove intro/credit tasks * Update configPage.html * rename task * Reorganize settings by relation * More standardized formatting * Some additional formatting * fix a typo * Update configPage.html * Allow missing recap / prview data * More risk to corrupt than benefit * Update TimeStamps.cs * Update PluginConfiguration.cs * Update configPage.html * Update PluginConfiguration.cs * Add chapter regex to settings * Move all UI into UI section * Move ending seconds with similar * Add default * fixes * Update SkipIntroController.cs * Autoskip all segments * Check if adjacent segment * Update AutoSkip.cs * Update AutoSkip.cs * Settings apply to all segment types * Update SegmentProvider * Update configPage.html Whoops * Update Plugin.cs * Update AutoSkip.cs * Let’s call it missing instead * Update BaseItemAnalyzerTask.cs * Update BaseItemAnalyzerTask.cs * Update BaseItemAnalyzerTask.cs * Move "select" all below list * Clarify button wording * Update configPage.html * Nope, long client list will hide it * Simplify wording * Update QueuedEpisode.cs * fix unit test for ffmpeg7 * Add migration * Restore DataContract * update * Update configPage.html * remove analyzed status * Update AutoSkip.cs * Update configPage.html typo * Store analyzed items in seasoninfo * Update VisualizationController.cs * update * Update IntroSkipperDbContext.cs * Add preview / recap delete * This keeps changing itself * Update SkipIntroController.cs * Rather add it to be removed --------- Co-authored-by: rlauu <46294892+rlauu@users.noreply.github.com> Co-authored-by: TwistedUmbrellaX <1173913+AbandonedCart@users.noreply.github.com> Co-authored-by: Kilian von Pflugk <github@jumoog.io>
2024-11-21 15:42:55 +01:00
var existingSegments = db.DbSegment.Where(s => s.ItemId == episode.EpisodeId);
2024-10-31 11:58:56 +01:00
Recaps and Previews Support (#357) * Recaps and Previews Support * Add draft UI of preview / recap edit * remove intro/credit tasks * Update configPage.html * rename task * Reorganize settings by relation * More standardized formatting * Some additional formatting * fix a typo * Update configPage.html * Allow missing recap / prview data * More risk to corrupt than benefit * Update TimeStamps.cs * Update PluginConfiguration.cs * Update configPage.html * Update PluginConfiguration.cs * Add chapter regex to settings * Move all UI into UI section * Move ending seconds with similar * Add default * fixes * Update SkipIntroController.cs * Autoskip all segments * Check if adjacent segment * Update AutoSkip.cs * Update AutoSkip.cs * Settings apply to all segment types * Update SegmentProvider * Update configPage.html Whoops * Update Plugin.cs * Update AutoSkip.cs * Let’s call it missing instead * Update BaseItemAnalyzerTask.cs * Update BaseItemAnalyzerTask.cs * Update BaseItemAnalyzerTask.cs * Move "select" all below list * Clarify button wording * Update configPage.html * Nope, long client list will hide it * Simplify wording * Update QueuedEpisode.cs * fix unit test for ffmpeg7 * Add migration * Restore DataContract * update * Update configPage.html * remove analyzed status * Update AutoSkip.cs * Update configPage.html typo * Store analyzed items in seasoninfo * Update VisualizationController.cs * update * Update IntroSkipperDbContext.cs * Add preview / recap delete * This keeps changing itself * Update SkipIntroController.cs * Rather add it to be removed --------- Co-authored-by: rlauu <46294892+rlauu@users.noreply.github.com> Co-authored-by: TwistedUmbrellaX <1173913+AbandonedCart@users.noreply.github.com> Co-authored-by: Kilian von Pflugk <github@jumoog.io>
2024-11-21 15:42:55 +01:00
db.DbSegment.RemoveRange(existingSegments);
2024-10-31 11:58:56 +01:00
if (eraseCache)
{
await Task.Run(() => FFmpegWrapper.DeleteEpisodeCache(episode.EpisodeId), cancellationToken).ConfigureAwait(false);
}
}
2022-07-03 01:20:33 -05:00
Recaps and Previews Support (#357) * Recaps and Previews Support * Add draft UI of preview / recap edit * remove intro/credit tasks * Update configPage.html * rename task * Reorganize settings by relation * More standardized formatting * Some additional formatting * fix a typo * Update configPage.html * Allow missing recap / prview data * More risk to corrupt than benefit * Update TimeStamps.cs * Update PluginConfiguration.cs * Update configPage.html * Update PluginConfiguration.cs * Add chapter regex to settings * Move all UI into UI section * Move ending seconds with similar * Add default * fixes * Update SkipIntroController.cs * Autoskip all segments * Check if adjacent segment * Update AutoSkip.cs * Update AutoSkip.cs * Settings apply to all segment types * Update SegmentProvider * Update configPage.html Whoops * Update Plugin.cs * Update AutoSkip.cs * Let’s call it missing instead * Update BaseItemAnalyzerTask.cs * Update BaseItemAnalyzerTask.cs * Update BaseItemAnalyzerTask.cs * Move "select" all below list * Clarify button wording * Update configPage.html * Nope, long client list will hide it * Simplify wording * Update QueuedEpisode.cs * fix unit test for ffmpeg7 * Add migration * Restore DataContract * update * Update configPage.html * remove analyzed status * Update AutoSkip.cs * Update configPage.html typo * Store analyzed items in seasoninfo * Update VisualizationController.cs * update * Update IntroSkipperDbContext.cs * Add preview / recap delete * This keeps changing itself * Update SkipIntroController.cs * Rather add it to be removed --------- Co-authored-by: rlauu <46294892+rlauu@users.noreply.github.com> Co-authored-by: TwistedUmbrellaX <1173913+AbandonedCart@users.noreply.github.com> Co-authored-by: Kilian von Pflugk <github@jumoog.io>
2024-11-21 15:42:55 +01:00
var seasonInfo = db.DbSeasonInfo.Where(s => s.SeasonId == seasonId);
foreach (var info in seasonInfo)
{
db.Entry(info).Property(s => s.EpisodeIds).CurrentValue = [];
}
await db.SaveChangesAsync(cancellationToken).ConfigureAwait(false);
2024-10-31 11:58:56 +01:00
if (Plugin.Instance.Configuration.UpdateMediaSegments)
{
await _mediaSegmentUpdateManager.UpdateMediaSegmentsAsync(episodes, cancellationToken).ConfigureAwait(false);
}
2022-07-03 01:20:33 -05:00
2024-10-31 11:58:56 +01:00
return NoContent();
}
catch (Exception ex)
{
2024-10-31 11:58:56 +01:00
return StatusCode(500, ex.Message);
}
2022-07-03 01:20:33 -05:00
}
/// <summary>
/// Updates the analyzer actions for the provided season.
/// </summary>
/// <param name="request">Update analyzer actions request.</param>
/// <returns>No content.</returns>
[HttpPost("AnalyzerActions/UpdateSeason")]
public async Task<ActionResult> UpdateAnalyzerActions([FromBody] UpdateAnalyzerActionsRequest request)
{
Recaps and Previews Support (#357) * Recaps and Previews Support * Add draft UI of preview / recap edit * remove intro/credit tasks * Update configPage.html * rename task * Reorganize settings by relation * More standardized formatting * Some additional formatting * fix a typo * Update configPage.html * Allow missing recap / prview data * More risk to corrupt than benefit * Update TimeStamps.cs * Update PluginConfiguration.cs * Update configPage.html * Update PluginConfiguration.cs * Add chapter regex to settings * Move all UI into UI section * Move ending seconds with similar * Add default * fixes * Update SkipIntroController.cs * Autoskip all segments * Check if adjacent segment * Update AutoSkip.cs * Update AutoSkip.cs * Settings apply to all segment types * Update SegmentProvider * Update configPage.html Whoops * Update Plugin.cs * Update AutoSkip.cs * Let’s call it missing instead * Update BaseItemAnalyzerTask.cs * Update BaseItemAnalyzerTask.cs * Update BaseItemAnalyzerTask.cs * Move "select" all below list * Clarify button wording * Update configPage.html * Nope, long client list will hide it * Simplify wording * Update QueuedEpisode.cs * fix unit test for ffmpeg7 * Add migration * Restore DataContract * update * Update configPage.html * remove analyzed status * Update AutoSkip.cs * Update configPage.html typo * Store analyzed items in seasoninfo * Update VisualizationController.cs * update * Update IntroSkipperDbContext.cs * Add preview / recap delete * This keeps changing itself * Update SkipIntroController.cs * Rather add it to be removed --------- Co-authored-by: rlauu <46294892+rlauu@users.noreply.github.com> Co-authored-by: TwistedUmbrellaX <1173913+AbandonedCart@users.noreply.github.com> Co-authored-by: Kilian von Pflugk <github@jumoog.io>
2024-11-21 15:42:55 +01:00
await Plugin.Instance!.SetAnalyzerActionAsync(request.Id, request.AnalyzerActions).ConfigureAwait(false);
return NoContent();
}
private static string GetProductionYear(Guid seriesId)
{
return seriesId == Guid.Empty
? "Unknown"
: Plugin.Instance?.GetItem(seriesId)?.ProductionYear?.ToString(CultureInfo.InvariantCulture) ?? "Unknown";
}
private static string GetLibraryName(Guid seriesId)
{
if (seriesId == Guid.Empty)
{
return "Unknown";
}
var collectionFolders = Plugin.Instance?.GetCollectionFolders(seriesId);
return collectionFolders?.Count > 0
? string.Join(", ", collectionFolders.Select(folder => folder.Name))
: "Unknown";
}
2022-05-30 02:23:36 -05:00
}