diff --git a/ConfusedPolarBear.Plugin.IntroSkipper.Tests/TestAudioFingerprinting.cs b/ConfusedPolarBear.Plugin.IntroSkipper.Tests/TestAudioFingerprinting.cs index afe614d..cedaa8c 100644 --- a/ConfusedPolarBear.Plugin.IntroSkipper.Tests/TestAudioFingerprinting.cs +++ b/ConfusedPolarBear.Plugin.IntroSkipper.Tests/TestAudioFingerprinting.cs @@ -105,13 +105,13 @@ public class TestAudioFingerprinting rhsFingerprint); Assert.True(lhs.Valid); - Assert.Equal(0, lhs.IntroStart); - Assert.Equal(17.208, lhs.IntroEnd, 3); + Assert.Equal(0, lhs.Start); + Assert.Equal(17.208, lhs.End, 3); Assert.True(rhs.Valid); // because we changed for 0.128 to 0.1238 its 4,952 now but that's too early (<= 5) - Assert.Equal(0, rhs.IntroStart); - Assert.Equal(22.1602, rhs.IntroEnd); + Assert.Equal(0, rhs.Start); + Assert.Equal(22.1602, rhs.End); } /// diff --git a/ConfusedPolarBear.Plugin.IntroSkipper.Tests/TestBlackFrames.cs b/ConfusedPolarBear.Plugin.IntroSkipper.Tests/TestBlackFrames.cs index 7cb86bc..9740f18 100644 --- a/ConfusedPolarBear.Plugin.IntroSkipper.Tests/TestBlackFrames.cs +++ b/ConfusedPolarBear.Plugin.IntroSkipper.Tests/TestBlackFrames.cs @@ -42,7 +42,7 @@ public class TestBlackFrames var result = analyzer.AnalyzeMediaFile(episode, 240, 30, 85); Assert.NotNull(result); - Assert.InRange(result.IntroStart, 300 - range, 300 + range); + Assert.InRange(result.Start, 300 - range, 300 + range); } private QueuedEpisode queueFile(string path) diff --git a/ConfusedPolarBear.Plugin.IntroSkipper.Tests/TestChapterAnalyzer.cs b/ConfusedPolarBear.Plugin.IntroSkipper.Tests/TestChapterAnalyzer.cs index 136f04e..7fd689e 100644 --- a/ConfusedPolarBear.Plugin.IntroSkipper.Tests/TestChapterAnalyzer.cs +++ b/ConfusedPolarBear.Plugin.IntroSkipper.Tests/TestChapterAnalyzer.cs @@ -23,8 +23,8 @@ public class TestChapterAnalyzer var introChapter = FindChapter(chapters, AnalysisMode.Introduction); Assert.NotNull(introChapter); - Assert.Equal(60, introChapter.IntroStart); - Assert.Equal(90, introChapter.IntroEnd); + Assert.Equal(60, introChapter.Start); + Assert.Equal(90, introChapter.End); } [Theory] @@ -39,11 +39,11 @@ public class TestChapterAnalyzer var creditsChapter = FindChapter(chapters, AnalysisMode.Credits); Assert.NotNull(creditsChapter); - Assert.Equal(1890, creditsChapter.IntroStart); - Assert.Equal(2000, creditsChapter.IntroEnd); + Assert.Equal(1890, creditsChapter.Start); + Assert.Equal(2000, creditsChapter.End); } - private Intro? FindChapter(Collection chapters, AnalysisMode mode) + private Segment? FindChapter(Collection chapters, AnalysisMode mode) { var logger = new LoggerFactory().CreateLogger(); var analyzer = new ChapterAnalyzer(logger); diff --git a/ConfusedPolarBear.Plugin.IntroSkipper.Tests/TestEdl.cs b/ConfusedPolarBear.Plugin.IntroSkipper.Tests/TestEdl.cs index c8b1e17..ab1d36d 100644 --- a/ConfusedPolarBear.Plugin.IntroSkipper.Tests/TestEdl.cs +++ b/ConfusedPolarBear.Plugin.IntroSkipper.Tests/TestEdl.cs @@ -38,8 +38,8 @@ public class TestEdl Assert.Equal(edlPath, EdlManager.GetEdlPath(mediaPath)); } - private Intro MakeIntro(double start, double end) + private Segment MakeIntro(double start, double end) { - return new Intro(Guid.Empty, new TimeRange(start, end)); + return new Segment(Guid.Empty, new TimeRange(start, end)); } } diff --git a/ConfusedPolarBear.Plugin.IntroSkipper/Analyzers/AnalyzerHelper.cs b/ConfusedPolarBear.Plugin.IntroSkipper/Analyzers/AnalyzerHelper.cs index be19a88..dc0d88a 100644 --- a/ConfusedPolarBear.Plugin.IntroSkipper/Analyzers/AnalyzerHelper.cs +++ b/ConfusedPolarBear.Plugin.IntroSkipper/Analyzers/AnalyzerHelper.cs @@ -34,12 +34,12 @@ public class AnalyzerHelper /// Original introductions. /// Analysis mode. /// Modified Intro Timestamps. - public Dictionary AdjustIntroTimes( + public Dictionary AdjustIntroTimes( ReadOnlyCollection episodes, - Dictionary originalIntros, + Dictionary originalIntros, AnalysisMode mode) { - var modifiedIntros = new Dictionary(); + var modifiedIntros = new Dictionary(); foreach (var episode in episodes) { @@ -58,15 +58,15 @@ public class AnalyzerHelper return modifiedIntros; } - private Intro AdjustIntroForEpisode(QueuedEpisode episode, Intro originalIntro, AnalysisMode mode) + private Segment AdjustIntroForEpisode(QueuedEpisode episode, Segment originalIntro, AnalysisMode mode) { var chapters = GetChaptersWithVirtualEnd(episode); - var adjustedIntro = new Intro(originalIntro); + var adjustedIntro = new Segment(originalIntro); - var originalIntroStart = new TimeRange(Math.Max(0, (int)originalIntro.IntroStart - 5), (int)originalIntro.IntroStart + 10); - var originalIntroEnd = new TimeRange((int)originalIntro.IntroEnd - 10, Math.Min(episode.Duration, (int)originalIntro.IntroEnd + 5)); + var originalIntroStart = new TimeRange(Math.Max(0, (int)originalIntro.Start - 5), (int)originalIntro.Start + 10); + var originalIntroEnd = new TimeRange((int)originalIntro.End - 10, Math.Min(episode.Duration, (int)originalIntro.End + 5)); - _logger.LogTrace("{Name} original intro: {Start} - {End}", episode.Name, originalIntro.IntroStart, originalIntro.IntroEnd); + _logger.LogTrace("{Name} original intro: {Start} - {End}", episode.Name, originalIntro.Start, originalIntro.End); if (!AdjustIntroBasedOnChapters(episode, chapters, adjustedIntro, originalIntroStart, originalIntroEnd) && mode == AnalysisMode.Introduction) @@ -84,7 +84,7 @@ public class AnalyzerHelper return chapters; } - private bool AdjustIntroBasedOnChapters(QueuedEpisode episode, List chapters, Intro adjustedIntro, TimeRange originalIntroStart, TimeRange originalIntroEnd) + private bool AdjustIntroBasedOnChapters(QueuedEpisode episode, List chapters, Segment adjustedIntro, TimeRange originalIntroStart, TimeRange originalIntroEnd) { foreach (var chapter in chapters) { @@ -92,13 +92,13 @@ public class AnalyzerHelper if (originalIntroStart.Start < chapterStartSeconds && chapterStartSeconds < originalIntroStart.End) { - adjustedIntro.IntroStart = chapterStartSeconds; + adjustedIntro.Start = chapterStartSeconds; _logger.LogTrace("{Name} chapter found close to intro start: {Start}", episode.Name, chapterStartSeconds); } if (originalIntroEnd.Start < chapterStartSeconds && chapterStartSeconds < originalIntroEnd.End) { - adjustedIntro.IntroEnd = chapterStartSeconds; + adjustedIntro.End = chapterStartSeconds; _logger.LogTrace("{Name} chapter found close to intro end: {End}", episode.Name, chapterStartSeconds); return true; } @@ -107,7 +107,7 @@ public class AnalyzerHelper return false; } - private void AdjustIntroBasedOnSilence(QueuedEpisode episode, Intro adjustedIntro, TimeRange originalIntroEnd) + private void AdjustIntroBasedOnSilence(QueuedEpisode episode, Segment adjustedIntro, TimeRange originalIntroEnd) { var silence = FFmpegWrapper.DetectSilence(episode, originalIntroEnd); @@ -117,16 +117,16 @@ public class AnalyzerHelper if (IsValidSilenceForIntroAdjustment(currentRange, originalIntroEnd, adjustedIntro)) { - adjustedIntro.IntroEnd = currentRange.Start; + adjustedIntro.End = currentRange.Start; break; } } } - private bool IsValidSilenceForIntroAdjustment(TimeRange silenceRange, TimeRange originalIntroEnd, Intro adjustedIntro) + private bool IsValidSilenceForIntroAdjustment(TimeRange silenceRange, TimeRange originalIntroEnd, Segment adjustedIntro) { return originalIntroEnd.Intersects(silenceRange) && silenceRange.Duration >= _silenceDetectionMinimumDuration && - silenceRange.Start >= adjustedIntro.IntroStart; + silenceRange.Start >= adjustedIntro.Start; } } diff --git a/ConfusedPolarBear.Plugin.IntroSkipper/Analyzers/BlackFrameAnalyzer.cs b/ConfusedPolarBear.Plugin.IntroSkipper/Analyzers/BlackFrameAnalyzer.cs index 172f851..bd0ed1d 100644 --- a/ConfusedPolarBear.Plugin.IntroSkipper/Analyzers/BlackFrameAnalyzer.cs +++ b/ConfusedPolarBear.Plugin.IntroSkipper/Analyzers/BlackFrameAnalyzer.cs @@ -50,7 +50,7 @@ public class BlackFrameAnalyzer : IMediaFileAnalyzer throw new NotImplementedException("mode must equal Credits"); } - var creditTimes = new Dictionary(); + var creditTimes = new Dictionary(); var episodeAnalysisQueue = new List(analysisQueue); @@ -113,7 +113,7 @@ public class BlackFrameAnalyzer : IMediaFileAnalyzer continue; } - searchStart = episode.Duration - credit.IntroStart + (0.5 * searchDistance); + searchStart = episode.Duration - credit.Start + (0.5 * searchDistance); creditTimes.Add(episode.EpisodeId, credit); episode.State.SetAnalyzed(mode, true); @@ -135,7 +135,7 @@ public class BlackFrameAnalyzer : IMediaFileAnalyzer /// Search Distance. /// Percentage of the frame that must be black. /// Credits timestamp. - public Intro? AnalyzeMediaFile(QueuedEpisode episode, double searchStart, int searchDistance, int minimum) + public Segment? AnalyzeMediaFile(QueuedEpisode episode, double searchStart, int searchDistance, int minimum) { // Start by analyzing the last N minutes of the file. var upperLimit = searchStart; diff --git a/ConfusedPolarBear.Plugin.IntroSkipper/Analyzers/ChapterAnalyzer.cs b/ConfusedPolarBear.Plugin.IntroSkipper/Analyzers/ChapterAnalyzer.cs index a302d57..3a20f31 100644 --- a/ConfusedPolarBear.Plugin.IntroSkipper/Analyzers/ChapterAnalyzer.cs +++ b/ConfusedPolarBear.Plugin.IntroSkipper/Analyzers/ChapterAnalyzer.cs @@ -34,7 +34,7 @@ public class ChapterAnalyzer : IMediaFileAnalyzer AnalysisMode mode, CancellationToken cancellationToken) { - var skippableRanges = new Dictionary(); + var skippableRanges = new Dictionary(); // Episode analysis queue. var episodeAnalysisQueue = new List(analysisQueue); @@ -84,7 +84,7 @@ public class ChapterAnalyzer : IMediaFileAnalyzer /// Regular expression pattern. /// Analysis mode. /// Intro object containing skippable time range, or null if no chapter matched. - public Intro? FindMatchingChapter( + public Segment? FindMatchingChapter( QueuedEpisode episode, Collection chapters, string expression, @@ -165,7 +165,7 @@ public class ChapterAnalyzer : IMediaFileAnalyzer } _logger.LogTrace("{Base}: okay", baseMessage); - return new Intro(episode.EpisodeId, currentRange); + return new Segment(episode.EpisodeId, currentRange); } return null; diff --git a/ConfusedPolarBear.Plugin.IntroSkipper/Analyzers/ChromaprintAnalyzer.cs b/ConfusedPolarBear.Plugin.IntroSkipper/Analyzers/ChromaprintAnalyzer.cs index ba82762..9ed525e 100644 --- a/ConfusedPolarBear.Plugin.IntroSkipper/Analyzers/ChromaprintAnalyzer.cs +++ b/ConfusedPolarBear.Plugin.IntroSkipper/Analyzers/ChromaprintAnalyzer.cs @@ -56,7 +56,7 @@ public class ChromaprintAnalyzer : IMediaFileAnalyzer CancellationToken cancellationToken) { // All intros for this season. - var seasonIntros = new Dictionary(); + var seasonIntros = new Dictionary(); // Cache of all fingerprints for this season. var fingerprintCache = new Dictionary(); @@ -157,14 +157,14 @@ public class ChromaprintAnalyzer : IMediaFileAnalyzer if (_analysisMode == AnalysisMode.Credits) { // Calculate new values for the current intro - double currentOriginalIntroStart = currentIntro.IntroStart; - currentIntro.IntroStart = currentEpisode.Duration - currentIntro.IntroEnd; - currentIntro.IntroEnd = currentEpisode.Duration - currentOriginalIntroStart; + double currentOriginalIntroStart = currentIntro.Start; + currentIntro.Start = currentEpisode.Duration - currentIntro.End; + currentIntro.End = currentEpisode.Duration - currentOriginalIntroStart; // Calculate new values for the remaining intro - double remainingIntroOriginalStart = remainingIntro.IntroStart; - remainingIntro.IntroStart = remainingEpisode.Duration - remainingIntro.IntroEnd; - remainingIntro.IntroEnd = remainingEpisode.Duration - remainingIntroOriginalStart; + double remainingIntroOriginalStart = remainingIntro.Start; + remainingIntro.Start = remainingEpisode.Duration - remainingIntro.End; + remainingIntro.End = remainingEpisode.Duration - remainingIntroOriginalStart; } // Only save the discovered intro if it is: @@ -218,7 +218,7 @@ public class ChromaprintAnalyzer : IMediaFileAnalyzer /// Second episode id. /// Second episode fingerprint points. /// Intros for the first and second episodes. - public (Intro Lhs, Intro Rhs) CompareEpisodes( + public (Segment Lhs, Segment Rhs) CompareEpisodes( Guid lhsId, uint[] lhsPoints, Guid rhsId, @@ -240,7 +240,7 @@ public class ChromaprintAnalyzer : IMediaFileAnalyzer lhsId, rhsId); - return (new Intro(lhsId), new Intro(rhsId)); + return (new Segment(lhsId), new Segment(rhsId)); } /// @@ -251,7 +251,7 @@ public class ChromaprintAnalyzer : IMediaFileAnalyzer /// Second episode id. /// Second episode shared timecodes. /// Intros for the first and second episodes. - private (Intro Lhs, Intro Rhs) GetLongestTimeRange( + private (Segment Lhs, Segment Rhs) GetLongestTimeRange( Guid lhsId, List lhsRanges, Guid rhsId, @@ -276,7 +276,7 @@ public class ChromaprintAnalyzer : IMediaFileAnalyzer } // Create Intro classes for each time range. - return (new Intro(lhsId, lhsIntro), new Intro(rhsId, rhsIntro)); + return (new Segment(lhsId, lhsIntro), new Segment(rhsId, rhsIntro)); } /// diff --git a/ConfusedPolarBear.Plugin.IntroSkipper/AutoSkip.cs b/ConfusedPolarBear.Plugin.IntroSkipper/AutoSkip.cs index adf5334..c3cc3d2 100644 --- a/ConfusedPolarBear.Plugin.IntroSkipper/AutoSkip.cs +++ b/ConfusedPolarBear.Plugin.IntroSkipper/AutoSkip.cs @@ -128,8 +128,8 @@ public class AutoSkip( } // Seek is unreliable if called at the very start of an episode. - var adjustedStart = Math.Max(5, intro.IntroStart + Plugin.Instance.Configuration.SecondsOfIntroStartToPlay); - var adjustedEnd = intro.IntroEnd - Plugin.Instance.Configuration.RemainingSecondsOfIntro; + var adjustedStart = Math.Max(5, intro.Start + Plugin.Instance.Configuration.SecondsOfIntroStartToPlay); + var adjustedEnd = intro.End - Plugin.Instance.Configuration.RemainingSecondsOfIntro; _logger.LogTrace( "Playback position is {Position}, intro runs from {Start} to {End}", diff --git a/ConfusedPolarBear.Plugin.IntroSkipper/AutoSkipCredits.cs b/ConfusedPolarBear.Plugin.IntroSkipper/AutoSkipCredits.cs index a4b1d7d..539e9a2 100644 --- a/ConfusedPolarBear.Plugin.IntroSkipper/AutoSkipCredits.cs +++ b/ConfusedPolarBear.Plugin.IntroSkipper/AutoSkipCredits.cs @@ -128,8 +128,8 @@ public class AutoSkipCredits( } // Seek is unreliable if called at the very end of an episode. - var adjustedStart = credit.IntroStart + Plugin.Instance.Configuration.SecondsOfCreditsStartToPlay; - var adjustedEnd = credit.IntroEnd - Plugin.Instance.Configuration.RemainingSecondsOfIntro; + var adjustedStart = credit.Start + Plugin.Instance.Configuration.SecondsOfCreditsStartToPlay; + var adjustedEnd = credit.End - Plugin.Instance.Configuration.RemainingSecondsOfIntro; _logger.LogTrace( "Playback position is {Position}, credits run from {Start} to {End}", diff --git a/ConfusedPolarBear.Plugin.IntroSkipper/Configuration/configPage.html b/ConfusedPolarBear.Plugin.IntroSkipper/Configuration/configPage.html index 1501fbf..225ba28 100644 --- a/ConfusedPolarBear.Plugin.IntroSkipper/Configuration/configPage.html +++ b/ConfusedPolarBear.Plugin.IntroSkipper/Configuration/configPage.html @@ -1024,16 +1024,16 @@ // Update the editor for the first and second episodes timestampEditor.style.display = "unset"; document.querySelector("#editLeftEpisodeTitle").textContent = leftEpisode.text; - document.querySelector("#editLeftIntroEpisodeStart").value = setTime(Math.round(leftEpisodeJson.Introduction.IntroStart)); - document.querySelector("#editLeftIntroEpisodeEnd").value = setTime(Math.round(leftEpisodeJson.Introduction.IntroEnd)); - document.querySelector("#editLeftCreditEpisodeStart").value = setTime(Math.round(leftEpisodeJson.Credits.IntroStart)); - document.querySelector("#editLeftCreditEpisodeEnd").value = setTime(Math.round(leftEpisodeJson.Credits.IntroEnd)); + document.querySelector("#editLeftIntroEpisodeStart").value = setTime(Math.round(leftEpisodeJson.Introduction.Start)); + document.querySelector("#editLeftIntroEpisodeEnd").value = setTime(Math.round(leftEpisodeJson.Introduction.End)); + document.querySelector("#editLeftCreditEpisodeStart").value = setTime(Math.round(leftEpisodeJson.Credits.Start)); + document.querySelector("#editLeftCreditEpisodeEnd").value = setTime(Math.round(leftEpisodeJson.Credits.End)); document.querySelector("#editRightEpisodeTitle").textContent = rightEpisode.text; - document.querySelector("#editRightIntroEpisodeStart").value = setTime(Math.round(rightEpisodeJson.Introduction.IntroStart)); - document.querySelector("#editRightIntroEpisodeEnd").value = setTime(Math.round(rightEpisodeJson.Introduction.IntroEnd)); - document.querySelector("#editRightCreditEpisodeStart").value = setTime(Math.round(rightEpisodeJson.Credits.IntroStart)); - document.querySelector("#editRightCreditEpisodeEnd").value = setTime(Math.round(rightEpisodeJson.Credits.IntroEnd)); + document.querySelector("#editRightIntroEpisodeStart").value = setTime(Math.round(rightEpisodeJson.Introduction.Start)); + document.querySelector("#editRightIntroEpisodeEnd").value = setTime(Math.round(rightEpisodeJson.Introduction.End)); + document.querySelector("#editRightCreditEpisodeStart").value = setTime(Math.round(rightEpisodeJson.Credits.Start)); + document.querySelector("#editRightCreditEpisodeEnd").value = setTime(Math.round(rightEpisodeJson.Credits.End)); } // adds an item to a dropdown diff --git a/ConfusedPolarBear.Plugin.IntroSkipper/Configuration/inject.js b/ConfusedPolarBear.Plugin.IntroSkipper/Configuration/inject.js index efd6363..3e54431 100644 --- a/ConfusedPolarBear.Plugin.IntroSkipper/Configuration/inject.js +++ b/ConfusedPolarBear.Plugin.IntroSkipper/Configuration/inject.js @@ -364,10 +364,10 @@ const introSkipper = { }, updateSkipperFields(skipperFields) { const { Introduction = {}, Credits = {} } = this.skipperData; - skipperFields.querySelector('#introStartEdit').value = Introduction.IntroStart || 0; - skipperFields.querySelector('#introEndEdit').value = Introduction.IntroEnd || 0; - skipperFields.querySelector('#creditsStartEdit').value = Credits.IntroStart || 0; - skipperFields.querySelector('#creditsEndEdit').value = Credits.IntroEnd || 0; + skipperFields.querySelector('#introStartEdit').value = Introduction.Start || 0; + skipperFields.querySelector('#introEndEdit').value = Introduction.End || 0; + skipperFields.querySelector('#creditsStartEdit').value = Credits.Start || 0; + skipperFields.querySelector('#creditsEndEdit').value = Credits.End || 0; }, attachSaveListener(metadataFormFields) { const saveButton = metadataFormFields.querySelector('.formDialogFooter .btnSave'); @@ -414,19 +414,19 @@ const introSkipper = { async saveSkipperData() { const newTimestamps = { Introduction: { - IntroStart: parseFloat(document.getElementById('introStartEdit').value || 0), - IntroEnd: parseFloat(document.getElementById('introEndEdit').value || 0) + Start: parseFloat(document.getElementById('introStartEdit').value || 0), + End: parseFloat(document.getElementById('introEndEdit').value || 0) }, Credits: { - IntroStart: parseFloat(document.getElementById('creditsStartEdit').value || 0), - IntroEnd: parseFloat(document.getElementById('creditsEndEdit').value || 0) + Start: parseFloat(document.getElementById('creditsStartEdit').value || 0), + End: parseFloat(document.getElementById('creditsEndEdit').value || 0) } }; const { Introduction = {}, Credits = {} } = this.skipperData; - if (newTimestamps.Introduction.IntroStart !== (Introduction.IntroStart || 0) || - newTimestamps.Introduction.IntroEnd !== (Introduction.IntroEnd || 0) || - newTimestamps.Credits.IntroStart !== (Credits.IntroStart || 0) || - newTimestamps.Credits.IntroEnd !== (Credits.IntroEnd || 0)) { + if (newTimestamps.Introduction.Start !== (Introduction.Start || 0) || + newTimestamps.Introduction.End !== (Introduction.End || 0) || + newTimestamps.Credits.Start !== (Credits.Start || 0) || + newTimestamps.Credits.End !== (Credits.End || 0)) { const response = await this.secureFetch(`Episode/${this.currentEpisodeId}/Timestamps`, "POST", JSON.stringify(newTimestamps)); this.d(response.ok ? 'Timestamps updated successfully' : 'Failed to update timestamps:', response.status); } else { diff --git a/ConfusedPolarBear.Plugin.IntroSkipper/Controllers/SkipIntroController.cs b/ConfusedPolarBear.Plugin.IntroSkipper/Controllers/SkipIntroController.cs index 6944988..db3f726 100644 --- a/ConfusedPolarBear.Plugin.IntroSkipper/Controllers/SkipIntroController.cs +++ b/ConfusedPolarBear.Plugin.IntroSkipper/Controllers/SkipIntroController.cs @@ -68,16 +68,16 @@ public class SkipIntroController : ControllerBase return NotFound(); } - if (timestamps?.Introduction.IntroEnd > 0.0) + if (timestamps?.Introduction.End > 0.0) { - var tr = new TimeRange(timestamps.Introduction.IntroStart, timestamps.Introduction.IntroEnd); - Plugin.Instance!.Intros[id] = new Intro(id, tr); + var tr = new TimeRange(timestamps.Introduction.Start, timestamps.Introduction.End); + Plugin.Instance!.Intros[id] = new Segment(id, tr); } - if (timestamps?.Credits.IntroEnd > 0.0) + if (timestamps?.Credits.End > 0.0) { - var cr = new TimeRange(timestamps.Credits.IntroStart, timestamps.Credits.IntroEnd); - Plugin.Instance!.Credits[id] = new Intro(id, cr); + var cr = new TimeRange(timestamps.Credits.Start, timestamps.Credits.End); + Plugin.Instance!.Credits[id] = new Segment(id, cr); } Plugin.Instance!.SaveTimestamps(AnalysisMode.Introduction); @@ -208,45 +208,6 @@ public class SkipIntroController : ControllerBase return NoContent(); } - /// - /// Get all introductions or credits. Only used by the end to end testing script. - /// - /// Mode. - /// All timestamps have been returned. - /// List of IntroWithMetadata objects. - [Authorize(Policy = Policies.RequiresElevation)] - [HttpGet("Intros/All")] - public ActionResult> GetAllTimestamps( - [FromQuery] AnalysisMode mode = AnalysisMode.Introduction) - { - List intros = []; - - var timestamps = mode == AnalysisMode.Introduction ? - Plugin.Instance!.Intros : - Plugin.Instance!.Credits; - - // Get metadata for all intros - foreach (var intro in timestamps) - { - // Get the details of the item from Jellyfin - var rawItem = Plugin.Instance.GetItem(intro.Key); - if (rawItem == null || 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; - } - /// /// Gets the user interface configuration. /// diff --git a/ConfusedPolarBear.Plugin.IntroSkipper/Controllers/VisualizationController.cs b/ConfusedPolarBear.Plugin.IntroSkipper/Controllers/VisualizationController.cs index daf2407..59d000e 100644 --- a/ConfusedPolarBear.Plugin.IntroSkipper/Controllers/VisualizationController.cs +++ b/ConfusedPolarBear.Plugin.IntroSkipper/Controllers/VisualizationController.cs @@ -171,7 +171,7 @@ public class VisualizationController : ControllerBase if (timestamps.IntroEnd > 0.0) { var tr = new TimeRange(timestamps.IntroStart, timestamps.IntroEnd); - Plugin.Instance!.Intros[id] = new Intro(id, tr); + Plugin.Instance!.Intros[id] = new Segment(id, tr); Plugin.Instance.SaveTimestamps(AnalysisMode.Introduction); } diff --git a/ConfusedPolarBear.Plugin.IntroSkipper/Data/Intro.cs b/ConfusedPolarBear.Plugin.IntroSkipper/Data/Intro.cs index fcfa893..912341f 100644 --- a/ConfusedPolarBear.Plugin.IntroSkipper/Data/Intro.cs +++ b/ConfusedPolarBear.Plugin.IntroSkipper/Data/Intro.cs @@ -1,7 +1,5 @@ using System; -using System.Globalization; using System.Runtime.Serialization; -using System.Text.Json.Serialization; namespace ConfusedPolarBear.Plugin.IntroSkipper.Data; @@ -9,55 +7,18 @@ namespace ConfusedPolarBear.Plugin.IntroSkipper.Data; /// Result of fingerprinting and analyzing two episodes in a season. /// All times are measured in seconds relative to the beginning of the media file. /// +/// +/// Initializes a new instance of the class. +/// +/// intro. [DataContract(Namespace = "http://schemas.datacontract.org/2004/07/ConfusedPolarBear.Plugin.IntroSkipper")] -public class Intro +public class Intro(Segment intro) { - /// - /// Initializes a new instance of the class. - /// - /// Episode. - /// Introduction time range. - public Intro(Guid episode, TimeRange intro) - { - EpisodeId = episode; - IntroStart = intro.Start; - IntroEnd = intro.End; - } - - /// - /// Initializes a new instance of the class. - /// - /// Episode. - public Intro(Guid episode) - { - EpisodeId = episode; - IntroStart = 0; - IntroEnd = 0; - } - - /// - /// Initializes a new instance of the class. - /// - /// intro. - public Intro(Intro intro) - { - EpisodeId = intro.EpisodeId; - IntroStart = intro.IntroStart; - IntroEnd = intro.IntroEnd; - } - - /// - /// Initializes a new instance of the class. - /// - public Intro() - { - } - /// /// Gets or sets the Episode ID. /// [DataMember] - public Guid EpisodeId { get; set; } + public Guid EpisodeId { get; set; } = intro.EpisodeId; /// /// Gets a value indicating whether this introduction is valid or not. @@ -65,23 +26,17 @@ public class Intro /// public bool Valid => IntroEnd > 0; - /// - /// Gets the duration of this intro. - /// - [JsonIgnore] - public double Duration => IntroEnd - IntroStart; - /// /// Gets or sets the introduction sequence start time. /// [DataMember] - public double IntroStart { get; set; } + public double IntroStart { get; set; } = intro.Start; /// /// Gets or sets the introduction sequence end time. /// [DataMember] - public double IntroEnd { get; set; } + public double IntroEnd { get; set; } = intro.End; /// /// Gets or sets the recommended time to display the skip intro prompt. @@ -92,22 +47,4 @@ public class Intro /// Gets or sets the recommended time to hide the skip intro prompt. /// public double HideSkipPromptAt { get; set; } - - /// - /// Convert this Intro object to a Kodi compatible EDL entry. - /// - /// User specified configuration EDL action. - /// String. - public string ToEdl(EdlAction action) - { - if (action == EdlAction.None) - { - throw new ArgumentException("Cannot serialize an EdlAction of None"); - } - - var start = Math.Round(IntroStart, 2); - var end = Math.Round(IntroEnd, 2); - - return string.Format(CultureInfo.InvariantCulture, "{0} {1} {2}", start, end, (int)action); - } } diff --git a/ConfusedPolarBear.Plugin.IntroSkipper/Data/IntroWithMetadata.cs b/ConfusedPolarBear.Plugin.IntroSkipper/Data/IntroWithMetadata.cs deleted file mode 100644 index e76e870..0000000 --- a/ConfusedPolarBear.Plugin.IntroSkipper/Data/IntroWithMetadata.cs +++ /dev/null @@ -1,40 +0,0 @@ -namespace ConfusedPolarBear.Plugin.IntroSkipper.Data; - -/// -/// An Intro class with episode metadata. Only used in end to end testing programs. -/// -public class IntroWithMetadata : Intro -{ - /// - /// Initializes a new instance of the class. - /// - /// Series name. - /// Season number. - /// Episode title. - /// Intro timestamps. - public IntroWithMetadata(string series, int season, string title, Intro intro) - { - Series = series; - Season = season; - Title = title; - - EpisodeId = intro.EpisodeId; - IntroStart = intro.IntroStart; - IntroEnd = intro.IntroEnd; - } - - /// - /// Gets or sets the series name of the TV episode associated with this intro. - /// - public string Series { get; set; } - - /// - /// Gets or sets the season number of the TV episode associated with this intro. - /// - public int Season { get; set; } - - /// - /// Gets or sets the title of the TV episode associated with this intro. - /// - public string Title { get; set; } -} diff --git a/ConfusedPolarBear.Plugin.IntroSkipper/Data/Segment.cs b/ConfusedPolarBear.Plugin.IntroSkipper/Data/Segment.cs new file mode 100644 index 0000000..6862862 --- /dev/null +++ b/ConfusedPolarBear.Plugin.IntroSkipper/Data/Segment.cs @@ -0,0 +1,114 @@ +using System; +using System.Globalization; +using System.Runtime.Serialization; +using System.Text.Json.Serialization; + +namespace ConfusedPolarBear.Plugin.IntroSkipper.Data; + +/// +/// Result of fingerprinting and analyzing two episodes in a season. +/// All times are measured in seconds relative to the beginning of the media file. +/// +[DataContract(Namespace = "http://schemas.datacontract.org/2004/07/ConfusedPolarBear.Plugin.IntroSkipper.Segment")] +public class Segment +{ + /// + /// Initializes a new instance of the class. + /// + /// Episode. + /// Introduction time range. + public Segment(Guid episode, TimeRange segment) + { + EpisodeId = episode; + Start = segment.Start; + End = segment.End; + } + + /// + /// Initializes a new instance of the class. + /// + /// Episode. + public Segment(Guid episode) + { + EpisodeId = episode; + Start = 0; + End = 0; + } + + /// + /// Initializes a new instance of the class. + /// + /// intro. + public Segment(Segment intro) + { + EpisodeId = intro.EpisodeId; + Start = intro.Start; + End = intro.End; + } + + /// + /// Initializes a new instance of the class. + /// + /// intro. + public Segment(Intro intro) + { + EpisodeId = intro.EpisodeId; + Start = intro.IntroStart; + End = intro.IntroEnd; + } + + /// + /// Initializes a new instance of the class. + /// + public Segment() + { + } + + /// + /// Gets or sets the Episode ID. + /// + [DataMember] + public Guid EpisodeId { get; set; } + + /// + /// Gets or sets the introduction sequence start time. + /// + [DataMember] + public double Start { get; set; } + + /// + /// Gets or sets the introduction sequence end time. + /// + [DataMember] + public double End { get; set; } + + /// + /// Gets a value indicating whether this introduction is valid or not. + /// Invalid results must not be returned through the API. + /// + public bool Valid => End > 0; + + /// + /// Gets the duration of this intro. + /// + [JsonIgnore] + public double Duration => End - Start; + + /// + /// Convert this Intro object to a Kodi compatible EDL entry. + /// + /// User specified configuration EDL action. + /// String. + public string ToEdl(EdlAction action) + { + if (action == EdlAction.None) + { + throw new ArgumentException("Cannot serialize an EdlAction of None"); + } + + var start = Math.Round(Start, 2); + var end = Math.Round(End, 2); + + return string.Format(CultureInfo.InvariantCulture, "{0} {1} {2}", start, end, (int)action); + } +} diff --git a/ConfusedPolarBear.Plugin.IntroSkipper/Data/TimeStamps.cs b/ConfusedPolarBear.Plugin.IntroSkipper/Data/TimeStamps.cs index 1ff2cb6..6295578 100644 --- a/ConfusedPolarBear.Plugin.IntroSkipper/Data/TimeStamps.cs +++ b/ConfusedPolarBear.Plugin.IntroSkipper/Data/TimeStamps.cs @@ -9,11 +9,11 @@ namespace ConfusedPolarBear.Plugin.IntroSkipper.Data /// /// Gets or sets Introduction. /// - public Intro Introduction { get; set; } = new Intro(); + public Segment Introduction { get; set; } = new Segment(); /// /// Gets or sets Credits. /// - public Intro Credits { get; set; } = new Intro(); + public Segment Credits { get; set; } = new Segment(); } } diff --git a/ConfusedPolarBear.Plugin.IntroSkipper/Plugin.cs b/ConfusedPolarBear.Plugin.IntroSkipper/Plugin.cs index 51896a3..c19bcf7 100644 --- a/ConfusedPolarBear.Plugin.IntroSkipper/Plugin.cs +++ b/ConfusedPolarBear.Plugin.IntroSkipper/Plugin.cs @@ -147,12 +147,12 @@ public partial class Plugin : BasePlugin, IHasWebPages /// /// Gets the results of fingerprinting all episodes. /// - public ConcurrentDictionary Intros { get; } = new(); + public ConcurrentDictionary Intros { get; } = new(); /// /// Gets all discovered ending credits. /// - public ConcurrentDictionary Credits { get; } = new(); + public ConcurrentDictionary Credits { get; } = new(); /// /// Gets the most recent media item queue. @@ -201,7 +201,7 @@ public partial class Plugin : BasePlugin, IHasWebPages /// Mode. public void SaveTimestamps(AnalysisMode mode) { - List introList = []; + List introList = []; var filePath = mode == AnalysisMode.Introduction ? _introPath : _creditsPath; @@ -311,7 +311,7 @@ public partial class Plugin : BasePlugin, IHasWebPages /// Item id. /// Mode. /// Intro. - internal static Intro GetIntroByMode(Guid id, AnalysisMode mode) + internal static Segment GetIntroByMode(Guid id, AnalysisMode mode) { return mode == AnalysisMode.Introduction ? Instance!.Intros[id] @@ -366,7 +366,7 @@ public partial class Plugin : BasePlugin, IHasWebPages /// State of this item. internal EpisodeState GetState(Guid id) => EpisodeStates.GetOrAdd(id, _ => new EpisodeState()); - internal void UpdateTimestamps(Dictionary newTimestamps, AnalysisMode mode) + internal void UpdateTimestamps(Dictionary newTimestamps, AnalysisMode mode) { foreach (var intro in newTimestamps) { diff --git a/ConfusedPolarBear.Plugin.IntroSkipper/XmlSerializationHelper.cs b/ConfusedPolarBear.Plugin.IntroSkipper/XmlSerializationHelper.cs index c8c168c..0388be5 100644 --- a/ConfusedPolarBear.Plugin.IntroSkipper/XmlSerializationHelper.cs +++ b/ConfusedPolarBear.Plugin.IntroSkipper/XmlSerializationHelper.cs @@ -20,9 +20,10 @@ namespace ConfusedPolarBear.Plugin.IntroSkipper serializer.WriteObject(fileStream, obj); } - public static List DeserializeFromXml(string filePath) + public static void MigrateFromIntro(string filePath) { - var result = new List(); + var intros = new List(); + var segments = new List(); try { // Create a FileStream to read the XML file @@ -34,7 +35,39 @@ namespace ConfusedPolarBear.Plugin.IntroSkipper DataContractSerializer serializer = new DataContractSerializer(typeof(List)); // Deserialize the object from the XML - result = serializer.ReadObject(reader) as List; + intros = serializer.ReadObject(reader) as List; + + // Close the reader + reader.Close(); + } + catch (Exception ex) + { + Console.WriteLine($"Error deserializing XML: {ex.Message}"); + } + + ArgumentNullException.ThrowIfNull(intros); + intros.ForEach(delegate(Intro name) + { + segments.Add(new Segment(name)); + }); + SerializeToXml(segments, filePath); + } + + public static List DeserializeFromXml(string filePath) + { + var result = new List(); + try + { + // Create a FileStream to read the XML file + using FileStream fileStream = new FileStream(filePath, FileMode.Open); + // Create an XmlDictionaryReader to read the XML + XmlDictionaryReader reader = XmlDictionaryReader.CreateTextReader(fileStream, new XmlDictionaryReaderQuotas()); + + // Create a DataContractSerializer for type T + DataContractSerializer serializer = new DataContractSerializer(typeof(List)); + + // Deserialize the object from the XML + result = serializer.ReadObject(reader) as List; // Close the reader reader.Close(); @@ -81,6 +114,12 @@ namespace ConfusedPolarBear.Plugin.IntroSkipper // Save the modified XML document xmlDoc.Save(filePath); } + + // intro -> segment migration + if (xmlDoc.DocumentElement.NamespaceURI == "http://schemas.datacontract.org/2004/07/ConfusedPolarBear.Plugin.IntroSkipper") + { + MigrateFromIntro(filePath); + } } catch (XmlException ex) {