automatic skip only for Apps without Skip Button (#208)

This commit is contained in:
Kilian von Pflugk 2024-08-31 16:48:31 +00:00 committed by GitHub
parent 1a13ef1a37
commit 88003edb21
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
11 changed files with 177 additions and 139 deletions

View File

@ -17,6 +17,7 @@ public class TestFlags
WarningManager.Clear();
WarningManager.SetFlag(PluginWarning.UnableToAddSkipButton);
Assert.Equal("UnableToAddSkipButton", WarningManager.GetWarnings());
Assert.True(WarningManager.HasFlag(PluginWarning.UnableToAddSkipButton));
}
[Fact]
@ -26,9 +27,22 @@ public class TestFlags
WarningManager.SetFlag(PluginWarning.UnableToAddSkipButton);
WarningManager.SetFlag(PluginWarning.InvalidChromaprintFingerprint);
WarningManager.SetFlag(PluginWarning.InvalidChromaprintFingerprint);
Assert.True(WarningManager.HasFlag(PluginWarning.UnableToAddSkipButton) && WarningManager.HasFlag(PluginWarning.InvalidChromaprintFingerprint));
Assert.Equal(
"UnableToAddSkipButton, InvalidChromaprintFingerprint",
WarningManager.GetWarnings());
}
[Fact]
public void TestHasFlag()
{
WarningManager.Clear();
Assert.True(WarningManager.HasFlag(PluginWarning.None));
Assert.False(WarningManager.HasFlag(PluginWarning.UnableToAddSkipButton) && WarningManager.HasFlag(PluginWarning.InvalidChromaprintFingerprint));
WarningManager.SetFlag(PluginWarning.UnableToAddSkipButton);
WarningManager.SetFlag(PluginWarning.InvalidChromaprintFingerprint);
Assert.True(WarningManager.HasFlag(PluginWarning.UnableToAddSkipButton) && WarningManager.HasFlag(PluginWarning.InvalidChromaprintFingerprint));
Assert.False(WarningManager.HasFlag(PluginWarning.IncompatibleFFmpegBuild));
Assert.True(WarningManager.HasFlag(PluginWarning.None));
}
}

View File

@ -112,6 +112,17 @@ public class AutoSkip : IHostedService, IDisposable
{
foreach (var session in _sessionManager.Sessions)
{
if (WarningManager.HasFlag(PluginWarning.UnableToAddSkipButton))
{
_logger.LogInformation("using autoskip to skip the intro because the injection of the skip button failed");
}
// only need for official Android TV App and jellyfin-kodi
else if (session.Client != "Android TV" || session.Client != "Kodi")
{
continue;
}
var deviceId = session.DeviceId;
var itemId = session.NowPlayingItem.Id;
var position = session.PlayState.PositionTicks / TimeSpan.TicksPerSecond;

View File

@ -112,6 +112,17 @@ public class AutoSkipCredits : IHostedService, IDisposable
{
foreach (var session in _sessionManager.Sessions)
{
if (WarningManager.HasFlag(PluginWarning.UnableToAddSkipButton))
{
_logger.LogInformation("using autoskip to skip the credits because the injection of the skip button failed");
}
// only need for official Android TV App and jellyfin-kodi
else if (session.Client != "Android TV" || session.Client != "Kodi")
{
continue;
}
var deviceId = session.DeviceId;
var itemId = session.NowPlayingItem.Id;
var position = session.PlayState.PositionTicks / TimeSpan.TicksPerSecond;

View File

@ -347,9 +347,8 @@
</label>
<div class="fieldDescription">
If checked, intros will be automatically skipped. If you access Jellyfin through a
reverse proxy, it must be configured to proxy web
sockets.<br />
If checked, credits will be automatically skipped for Apps without Skip Button.
If you access Jellyfin through a reverse proxy, it must be configured to proxy websockets.<br />
</div>
</div>
@ -383,9 +382,8 @@
</label>
<div class="fieldDescription">
If checked, credits will be automatically skipped. If you access Jellyfin through a
reverse proxy, it must be configured to proxy web
sockets.<br />
If checked, credits will be automatically skipped for Apps without Skip Button.
If you access Jellyfin through a reverse proxy, it must be configured to proxy websockets.<br />
</div>
</div>

View File

@ -106,42 +106,3 @@ public class Intro
return string.Format(CultureInfo.InvariantCulture, "{0} {1} {2}", start, end, (int)action);
}
}
/// <summary>
/// An Intro class with episode metadata. Only used in end to end testing programs.
/// </summary>
public class IntroWithMetadata : Intro
{
/// <summary>
/// Initializes a new instance of the <see cref="IntroWithMetadata"/> class.
/// </summary>
/// <param name="series">Series name.</param>
/// <param name="season">Season number.</param>
/// <param name="title">Episode title.</param>
/// <param name="intro">Intro timestamps.</param>
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;
}
/// <summary>
/// Gets or sets the series name of the TV episode associated with this intro.
/// </summary>
public string Series { get; set; }
/// <summary>
/// Gets or sets the season number of the TV episode associated with this intro.
/// </summary>
public int Season { get; set; }
/// <summary>
/// Gets or sets the title of the TV episode associated with this intro.
/// </summary>
public string Title { get; set; }
}

View File

@ -0,0 +1,40 @@
namespace ConfusedPolarBear.Plugin.IntroSkipper;
/// <summary>
/// An Intro class with episode metadata. Only used in end to end testing programs.
/// </summary>
public class IntroWithMetadata : Intro
{
/// <summary>
/// Initializes a new instance of the <see cref="IntroWithMetadata"/> class.
/// </summary>
/// <param name="series">Series name.</param>
/// <param name="season">Season number.</param>
/// <param name="title">Episode title.</param>
/// <param name="intro">Intro timestamps.</param>
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;
}
/// <summary>
/// Gets or sets the series name of the TV episode associated with this intro.
/// </summary>
public string Series { get; set; }
/// <summary>
/// Gets or sets the season number of the TV episode associated with this intro.
/// </summary>
public int Season { get; set; }
/// <summary>
/// Gets or sets the title of the TV episode associated with this intro.
/// </summary>
public string Title { get; set; }
}

View File

@ -28,37 +28,3 @@ public enum PluginWarning
/// </summary>
IncompatibleFFmpegBuild = 4,
}
/// <summary>
/// Warning manager.
/// </summary>
public static class WarningManager
{
private static PluginWarning warnings;
/// <summary>
/// Set warning.
/// </summary>
/// <param name="warning">Warning.</param>
public static void SetFlag(PluginWarning warning)
{
warnings |= warning;
}
/// <summary>
/// Clear warnings.
/// </summary>
public static void Clear()
{
warnings = PluginWarning.None;
}
/// <summary>
/// Get warnings.
/// </summary>
/// <returns>Warnings.</returns>
public static string GetWarnings()
{
return warnings.ToString();
}
}

View File

@ -1,5 +1,4 @@
using System;
using System.Collections.Generic;
namespace ConfusedPolarBear.Plugin.IntroSkipper;
@ -82,51 +81,3 @@ public class TimeRange : IComparable
(Start < tr.End && tr.End < End);
}
}
#pragma warning restore CA1036
/// <summary>
/// Time range helpers.
/// </summary>
public static class TimeRangeHelpers
{
/// <summary>
/// Finds the longest contiguous time range.
/// </summary>
/// <param name="times">Sorted timestamps to search.</param>
/// <param name="maximumDistance">Maximum distance permitted between contiguous timestamps.</param>
/// <returns>The longest contiguous time range (if one was found), or null (if none was found).</returns>
public static TimeRange? FindContiguous(double[] times, double maximumDistance)
{
if (times.Length == 0)
{
return null;
}
Array.Sort(times);
var ranges = new List<TimeRange>();
var currentRange = new TimeRange(times[0], times[0]);
// For all provided timestamps, check if it is contiguous with its neighbor.
for (var i = 0; i < times.Length - 1; i++)
{
var current = times[i];
var next = times[i + 1];
if (next - current <= maximumDistance)
{
currentRange.End = next;
continue;
}
ranges.Add(new TimeRange(currentRange));
currentRange = new TimeRange(next, next);
}
// Find and return the longest contiguous range.
ranges.Sort();
return (ranges.Count > 0) ? ranges[0] : null;
}
}

View File

@ -0,0 +1,51 @@
using System;
using System.Collections.Generic;
namespace ConfusedPolarBear.Plugin.IntroSkipper;
#pragma warning restore CA1036
/// <summary>
/// Time range helpers.
/// </summary>
public static class TimeRangeHelpers
{
/// <summary>
/// Finds the longest contiguous time range.
/// </summary>
/// <param name="times">Sorted timestamps to search.</param>
/// <param name="maximumDistance">Maximum distance permitted between contiguous timestamps.</param>
/// <returns>The longest contiguous time range (if one was found), or null (if none was found).</returns>
public static TimeRange? FindContiguous(double[] times, double maximumDistance)
{
if (times.Length == 0)
{
return null;
}
Array.Sort(times);
var ranges = new List<TimeRange>();
var currentRange = new TimeRange(times[0], times[0]);
// For all provided timestamps, check if it is contiguous with its neighbor.
for (var i = 0; i < times.Length - 1; i++)
{
var current = times[i];
var next = times[i + 1];
if (next - current <= maximumDistance)
{
currentRange.End = next;
continue;
}
ranges.Add(new TimeRange(currentRange));
currentRange = new TimeRange(next, next);
}
// Find and return the longest contiguous range.
ranges.Sort();
return (ranges.Count > 0) ? ranges[0] : null;
}
}

View File

@ -0,0 +1,45 @@
namespace ConfusedPolarBear.Plugin.IntroSkipper;
/// <summary>
/// Warning manager.
/// </summary>
public static class WarningManager
{
private static PluginWarning warnings;
/// <summary>
/// Set warning.
/// </summary>
/// <param name="warning">Warning.</param>
public static void SetFlag(PluginWarning warning)
{
warnings |= warning;
}
/// <summary>
/// Clear warnings.
/// </summary>
public static void Clear()
{
warnings = PluginWarning.None;
}
/// <summary>
/// Get warnings.
/// </summary>
/// <returns>Warnings.</returns>
public static string GetWarnings()
{
return warnings.ToString();
}
/// <summary>
/// Check if a specific warning flag is set.
/// </summary>
/// <param name="warning">Warning flag to check.</param>
/// <returns>True if the flag is set, otherwise false.</returns>
public static bool HasFlag(PluginWarning warning)
{
return (warnings & warning) == warning;
}
}

View File

@ -1,10 +0,0 @@
// This file is used by Code Analysis to maintain SuppressMessage
// attributes that are applied to this project.
// Project-level suppressions either have no target or are given
// a specific target and scoped to a namespace, type, member, etc.
using System.Diagnostics.CodeAnalysis;
[assembly: SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1649:File name should match first type name", Justification = "Legacy TODO", Scope = "type", Target = "~T:ConfusedPolarBear.Plugin.IntroSkipper.WarningManager")]
[assembly: SuppressMessage("StyleCop.CSharp.MaintainabilityRules", "SA1402:File may only contain a single type", Justification = "Legacy TODO", Scope = "type", Target = "~T:ConfusedPolarBear.Plugin.IntroSkipper.IntroWithMetadata")]
[assembly: SuppressMessage("StyleCop.CSharp.MaintainabilityRules", "SA1402:File may only contain a single type", Justification = "Legacy TODO", Scope = "type", Target = "~T:ConfusedPolarBear.Plugin.IntroSkipper.TimeRangeHelpers")]