automatic skip only for Apps without Skip Button (#208)
This commit is contained in:
parent
1a13ef1a37
commit
88003edb21
@ -17,6 +17,7 @@ public class TestFlags
|
|||||||
WarningManager.Clear();
|
WarningManager.Clear();
|
||||||
WarningManager.SetFlag(PluginWarning.UnableToAddSkipButton);
|
WarningManager.SetFlag(PluginWarning.UnableToAddSkipButton);
|
||||||
Assert.Equal("UnableToAddSkipButton", WarningManager.GetWarnings());
|
Assert.Equal("UnableToAddSkipButton", WarningManager.GetWarnings());
|
||||||
|
Assert.True(WarningManager.HasFlag(PluginWarning.UnableToAddSkipButton));
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
@ -26,9 +27,22 @@ public class TestFlags
|
|||||||
WarningManager.SetFlag(PluginWarning.UnableToAddSkipButton);
|
WarningManager.SetFlag(PluginWarning.UnableToAddSkipButton);
|
||||||
WarningManager.SetFlag(PluginWarning.InvalidChromaprintFingerprint);
|
WarningManager.SetFlag(PluginWarning.InvalidChromaprintFingerprint);
|
||||||
WarningManager.SetFlag(PluginWarning.InvalidChromaprintFingerprint);
|
WarningManager.SetFlag(PluginWarning.InvalidChromaprintFingerprint);
|
||||||
|
Assert.True(WarningManager.HasFlag(PluginWarning.UnableToAddSkipButton) && WarningManager.HasFlag(PluginWarning.InvalidChromaprintFingerprint));
|
||||||
Assert.Equal(
|
Assert.Equal(
|
||||||
"UnableToAddSkipButton, InvalidChromaprintFingerprint",
|
"UnableToAddSkipButton, InvalidChromaprintFingerprint",
|
||||||
WarningManager.GetWarnings());
|
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));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -112,6 +112,17 @@ public class AutoSkip : IHostedService, IDisposable
|
|||||||
{
|
{
|
||||||
foreach (var session in _sessionManager.Sessions)
|
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 deviceId = session.DeviceId;
|
||||||
var itemId = session.NowPlayingItem.Id;
|
var itemId = session.NowPlayingItem.Id;
|
||||||
var position = session.PlayState.PositionTicks / TimeSpan.TicksPerSecond;
|
var position = session.PlayState.PositionTicks / TimeSpan.TicksPerSecond;
|
||||||
|
@ -112,6 +112,17 @@ public class AutoSkipCredits : IHostedService, IDisposable
|
|||||||
{
|
{
|
||||||
foreach (var session in _sessionManager.Sessions)
|
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 deviceId = session.DeviceId;
|
||||||
var itemId = session.NowPlayingItem.Id;
|
var itemId = session.NowPlayingItem.Id;
|
||||||
var position = session.PlayState.PositionTicks / TimeSpan.TicksPerSecond;
|
var position = session.PlayState.PositionTicks / TimeSpan.TicksPerSecond;
|
||||||
|
@ -347,9 +347,8 @@
|
|||||||
</label>
|
</label>
|
||||||
|
|
||||||
<div class="fieldDescription">
|
<div class="fieldDescription">
|
||||||
If checked, intros will be automatically skipped. If you access Jellyfin through a
|
If checked, credits will be automatically skipped for Apps without Skip Button.
|
||||||
reverse proxy, it must be configured to proxy web
|
If you access Jellyfin through a reverse proxy, it must be configured to proxy websockets.<br />
|
||||||
sockets.<br />
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -383,9 +382,8 @@
|
|||||||
</label>
|
</label>
|
||||||
|
|
||||||
<div class="fieldDescription">
|
<div class="fieldDescription">
|
||||||
If checked, credits will be automatically skipped. If you access Jellyfin through a
|
If checked, credits will be automatically skipped for Apps without Skip Button.
|
||||||
reverse proxy, it must be configured to proxy web
|
If you access Jellyfin through a reverse proxy, it must be configured to proxy websockets.<br />
|
||||||
sockets.<br />
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -106,42 +106,3 @@ public class Intro
|
|||||||
return string.Format(CultureInfo.InvariantCulture, "{0} {1} {2}", start, end, (int)action);
|
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; }
|
|
||||||
}
|
|
||||||
|
@ -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; }
|
||||||
|
}
|
@ -28,37 +28,3 @@ public enum PluginWarning
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
IncompatibleFFmpegBuild = 4,
|
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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
|
||||||
|
|
||||||
namespace ConfusedPolarBear.Plugin.IntroSkipper;
|
namespace ConfusedPolarBear.Plugin.IntroSkipper;
|
||||||
|
|
||||||
@ -82,51 +81,3 @@ public class TimeRange : IComparable
|
|||||||
(Start < tr.End && tr.End < End);
|
(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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
45
ConfusedPolarBear.Plugin.IntroSkipper/Data/WarningManager.cs
Normal file
45
ConfusedPolarBear.Plugin.IntroSkipper/Data/WarningManager.cs
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
@ -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")]
|
|
Loading…
x
Reference in New Issue
Block a user