select autoskip clients (#277)

Co-authored-by: rlauu <46294892+rlauu@users.noreply.github.com>
This commit is contained in:
rlauuzo 2024-09-09 20:52:54 +02:00 committed by GitHub
parent 899d5e1914
commit d428efb1f2
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 102 additions and 73 deletions

View File

@ -1,10 +1,10 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.Timers; using System.Timers;
using ConfusedPolarBear.Plugin.IntroSkipper.Configuration; using ConfusedPolarBear.Plugin.IntroSkipper.Configuration;
using ConfusedPolarBear.Plugin.IntroSkipper.Data;
using MediaBrowser.Common.Extensions; using MediaBrowser.Common.Extensions;
using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Session; using MediaBrowser.Controller.Session;
@ -21,32 +21,25 @@ namespace ConfusedPolarBear.Plugin.IntroSkipper;
/// Automatically skip past introduction sequences. /// Automatically skip past introduction sequences.
/// Commands clients to seek to the end of the intro as soon as they start playing it. /// Commands clients to seek to the end of the intro as soon as they start playing it.
/// </summary> /// </summary>
public class AutoSkip : IHostedService, IDisposable /// <remarks>
/// Initializes a new instance of the <see cref="AutoSkip"/> class.
/// </remarks>
/// <param name="userDataManager">User data manager.</param>
/// <param name="sessionManager">Session manager.</param>
/// <param name="logger">Logger.</param>
public class AutoSkip(
IUserDataManager userDataManager,
ISessionManager sessionManager,
ILogger<AutoSkip> logger) : IHostedService, IDisposable
{ {
private readonly object _sentSeekCommandLock = new(); private readonly object _sentSeekCommandLock = new();
private ILogger<AutoSkip> _logger; private ILogger<AutoSkip> _logger = logger;
private IUserDataManager _userDataManager; private IUserDataManager _userDataManager = userDataManager;
private ISessionManager _sessionManager; private ISessionManager _sessionManager = sessionManager;
private Timer _playbackTimer = new(1000); private Timer _playbackTimer = new(1000);
private Dictionary<string, bool> _sentSeekCommand; private Dictionary<string, bool> _sentSeekCommand = [];
private HashSet<string> _clientList = [];
/// <summary>
/// Initializes a new instance of the <see cref="AutoSkip"/> class.
/// </summary>
/// <param name="userDataManager">User data manager.</param>
/// <param name="sessionManager">Session manager.</param>
/// <param name="logger">Logger.</param>
public AutoSkip(
IUserDataManager userDataManager,
ISessionManager sessionManager,
ILogger<AutoSkip> logger)
{
_userDataManager = userDataManager;
_sessionManager = sessionManager;
_logger = logger;
_sentSeekCommand = new Dictionary<string, bool>();
}
private void AutoSkipChanged(object? sender, BasePluginConfiguration e) private void AutoSkipChanged(object? sender, BasePluginConfiguration e)
{ {
@ -54,6 +47,7 @@ public class AutoSkip : IHostedService, IDisposable
var newState = configuration.AutoSkip; var newState = configuration.AutoSkip;
_logger.LogDebug("Setting playback timer enabled to {NewState}", newState); _logger.LogDebug("Setting playback timer enabled to {NewState}", newState);
_playbackTimer.Enabled = newState; _playbackTimer.Enabled = newState;
_clientList = [.. configuration.ClientList.Split(',', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries)];
} }
private void UserDataManager_UserDataSaved(object? sender, UserDataSaveEventArgs e) private void UserDataManager_UserDataSaved(object? sender, UserDataSaveEventArgs e)
@ -111,19 +105,8 @@ public class AutoSkip : IHostedService, IDisposable
private void PlaybackTimer_Elapsed(object? sender, ElapsedEventArgs e) private void PlaybackTimer_Elapsed(object? sender, ElapsedEventArgs e)
{ {
foreach (var session in _sessionManager.Sessions) foreach (var session in _sessionManager.Sessions.Where(s => _clientList.Contains(s.Client, StringComparer.OrdinalIgnoreCase)))
{ {
if (WarningManager.HasFlag(PluginWarning.UnableToAddSkipButton))
{
_logger.LogTrace("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;

View File

@ -1,10 +1,10 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.Timers; using System.Timers;
using ConfusedPolarBear.Plugin.IntroSkipper.Configuration; using ConfusedPolarBear.Plugin.IntroSkipper.Configuration;
using ConfusedPolarBear.Plugin.IntroSkipper.Data;
using MediaBrowser.Common.Extensions; using MediaBrowser.Common.Extensions;
using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Session; using MediaBrowser.Controller.Session;
@ -21,32 +21,25 @@ namespace ConfusedPolarBear.Plugin.IntroSkipper;
/// Automatically skip past credit sequences. /// Automatically skip past credit sequences.
/// Commands clients to seek to the end of the credits as soon as they start playing it. /// Commands clients to seek to the end of the credits as soon as they start playing it.
/// </summary> /// </summary>
public class AutoSkipCredits : IHostedService, IDisposable /// <remarks>
/// Initializes a new instance of the <see cref="AutoSkipCredits"/> class.
/// </remarks>
/// <param name="userDataManager">User data manager.</param>
/// <param name="sessionManager">Session manager.</param>
/// <param name="logger">Logger.</param>
public class AutoSkipCredits(
IUserDataManager userDataManager,
ISessionManager sessionManager,
ILogger<AutoSkipCredits> logger) : IHostedService, IDisposable
{ {
private readonly object _sentSeekCommandLock = new(); private readonly object _sentSeekCommandLock = new();
private ILogger<AutoSkipCredits> _logger; private ILogger<AutoSkipCredits> _logger = logger;
private IUserDataManager _userDataManager; private IUserDataManager _userDataManager = userDataManager;
private ISessionManager _sessionManager; private ISessionManager _sessionManager = sessionManager;
private Timer _playbackTimer = new(1000); private Timer _playbackTimer = new(1000);
private Dictionary<string, bool> _sentSeekCommand; private Dictionary<string, bool> _sentSeekCommand = [];
private HashSet<string> _clientList = [];
/// <summary>
/// Initializes a new instance of the <see cref="AutoSkipCredits"/> class.
/// </summary>
/// <param name="userDataManager">User data manager.</param>
/// <param name="sessionManager">Session manager.</param>
/// <param name="logger">Logger.</param>
public AutoSkipCredits(
IUserDataManager userDataManager,
ISessionManager sessionManager,
ILogger<AutoSkipCredits> logger)
{
_userDataManager = userDataManager;
_sessionManager = sessionManager;
_logger = logger;
_sentSeekCommand = new Dictionary<string, bool>();
}
private void AutoSkipCreditChanged(object? sender, BasePluginConfiguration e) private void AutoSkipCreditChanged(object? sender, BasePluginConfiguration e)
{ {
@ -54,6 +47,7 @@ public class AutoSkipCredits : IHostedService, IDisposable
var newState = configuration.AutoSkipCredits; var newState = configuration.AutoSkipCredits;
_logger.LogDebug("Setting playback timer enabled to {NewState}", newState); _logger.LogDebug("Setting playback timer enabled to {NewState}", newState);
_playbackTimer.Enabled = newState; _playbackTimer.Enabled = newState;
_clientList = [.. configuration.ClientList.Split(',', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries)];
} }
private void UserDataManager_UserDataSaved(object? sender, UserDataSaveEventArgs e) private void UserDataManager_UserDataSaved(object? sender, UserDataSaveEventArgs e)
@ -111,19 +105,8 @@ public class AutoSkipCredits : IHostedService, IDisposable
private void PlaybackTimer_Elapsed(object? sender, ElapsedEventArgs e) private void PlaybackTimer_Elapsed(object? sender, ElapsedEventArgs e)
{ {
foreach (var session in _sessionManager.Sessions) foreach (var session in _sessionManager.Sessions.Where(s => _clientList.Contains(s.Client, StringComparer.OrdinalIgnoreCase)))
{ {
if (WarningManager.HasFlag(PluginWarning.UnableToAddSkipButton))
{
_logger.LogTrace("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;

View File

@ -29,6 +29,11 @@ public class PluginConfiguration : BasePluginConfiguration
/// </summary> /// </summary>
public string SelectedLibraries { get; set; } = string.Empty; public string SelectedLibraries { get; set; } = string.Empty;
/// <summary>
/// Gets or sets the list of client to auto skip for.
/// </summary>
public string ClientList { get; set; } = "Android TV, Kodi";
/// <summary> /// <summary>
/// Gets or sets a value indicating whether to scan for intros during a scheduled task. /// Gets or sets a value indicating whether to scan for intros during a scheduled task.
/// </summary> /// </summary>

View File

@ -398,6 +398,14 @@
<br /> <br />
</div> </div>
<details id="AutoSkipClientList" style="padding-bottom: 1em;">
<summary>Auto Skip Client List</summary>
<br />
<div class="checkboxList paperList" style="padding:.5em 1em"></div>
<label class="inputLabel" for="ClientList"></label>
<input id="ClientList" type="hidden" is="emby-input" />
</details>
<div class="checkboxContainer checkboxContainer-withDescription"> <div class="checkboxContainer checkboxContainer-withDescription">
<label class="emby-checkbox-label"> <label class="emby-checkbox-label">
<input id="SkipButtonVisible" type="checkbox" is="emby-checkbox" /> <input id="SkipButtonVisible" type="checkbox" is="emby-checkbox" />
@ -699,6 +707,7 @@
// analysis // analysis
"MaxParallelism", "MaxParallelism",
"SelectedLibraries", "SelectedLibraries",
"ClientList",
"AnalysisPercent", "AnalysisPercent",
"AnalysisLengthLimit", "AnalysisLengthLimit",
"MinimumIntroDuration", "MinimumIntroDuration",
@ -758,12 +767,13 @@
var autoSkip = document.querySelector("input#AutoSkip"); var autoSkip = document.querySelector("input#AutoSkip");
var skipFirstEpisode = document.querySelector("div#divSkipFirstEpisode"); var skipFirstEpisode = document.querySelector("div#divSkipFirstEpisode");
var secondsOfIntroStartToPlay = document.querySelector("div#divSecondsOfIntroStartToPlay"); var secondsOfIntroStartToPlay = document.querySelector("div#divSecondsOfIntroStartToPlay");
var autoSkipClientList = document.getElementById("AutoSkipClientList");
var secondsOfCreditsStartToPlay = document.querySelector("div#divSecondsOfCreditsStartToPlay"); var secondsOfCreditsStartToPlay = document.querySelector("div#divSecondsOfCreditsStartToPlay");
var autoSkipNotificationText = document.querySelector("div#divAutoSkipNotificationText"); var autoSkipNotificationText = document.querySelector("div#divAutoSkipNotificationText");
var autoSkipCredits = document.querySelector("input#AutoSkipCredits"); var autoSkipCredits = document.querySelector("input#AutoSkipCredits");
var autoSkipCreditsNotificationText = document.querySelector("div#divAutoSkipCreditsNotificationText"); var autoSkipCreditsNotificationText = document.querySelector("div#divAutoSkipCreditsNotificationText");
async function autoSkipChanged() { function autoSkipChanged() {
if (autoSkip.checked) { if (autoSkip.checked) {
skipFirstEpisode.style.display = 'unset'; skipFirstEpisode.style.display = 'unset';
autoSkipNotificationText.style.display = 'unset'; autoSkipNotificationText.style.display = 'unset';
@ -773,11 +783,12 @@
autoSkipNotificationText.style.display = 'none'; autoSkipNotificationText.style.display = 'none';
secondsOfIntroStartToPlay.style.display = 'none'; secondsOfIntroStartToPlay.style.display = 'none';
} }
clientListVisible();
} }
autoSkip.addEventListener("change", autoSkipChanged); autoSkip.addEventListener("change", autoSkipChanged);
async function autoSkipCreditsChanged() { function autoSkipCreditsChanged() {
if (autoSkipCredits.checked) { if (autoSkipCredits.checked) {
autoSkipCreditsNotificationText.style.display = 'unset'; autoSkipCreditsNotificationText.style.display = 'unset';
secondsOfCreditsStartToPlay.style.display = 'unset'; secondsOfCreditsStartToPlay.style.display = 'unset';
@ -785,10 +796,57 @@
autoSkipCreditsNotificationText.style.display = 'none'; autoSkipCreditsNotificationText.style.display = 'none';
secondsOfCreditsStartToPlay.style.display = 'none'; secondsOfCreditsStartToPlay.style.display = 'none';
} }
clientListVisible();
} }
autoSkipCredits.addEventListener("change", autoSkipCreditsChanged); autoSkipCredits.addEventListener("change", autoSkipCreditsChanged);
function clientListVisible() {
if (autoSkip.checked || autoSkipCredits.checked) {
autoSkipClientList.style.display = 'unset';
autoSkipClientList.style.width = '100%';
} else {
autoSkipClientList.style.display = 'none';
}
}
async function getDeviceList() {
const response = await getJson("Devices");
const devices = [...new Set(response.Items.map(item => item.AppName))];
return devices;
}
function updateClientList() {
document.getElementById('ClientList').value = Array.from(
autoSkipClientList.querySelectorAll('input[type="checkbox"]:checked')
).map(checkbox => checkbox.nextElementSibling.textContent).join(', ');
}
async function generateAutoSkipClientList() {
var devices = await getDeviceList();
var deviceList = document.getElementById('ClientList').value;
var checkedDevices = deviceList ? deviceList.split(', ') : [];
var checkboxListHtml = devices.map(function(device) {
var id = 'chk' + device.replace(/\s+/g, '');
var isChecked = checkedDevices.includes(device) ? 'checked' : '';
return '<label class="emby-checkbox-label">' +
'<input type="checkbox" is="emby-checkbox" id="' + id + '" ' + isChecked + '>' +
'<span class="checkboxLabel">' + device + '</span>' +
'</label>';
}).join('');
var checkboxList = autoSkipClientList.querySelector('.checkboxList.paperList');
checkboxList.innerHTML = checkboxListHtml;
var checkboxes = checkboxList.querySelectorAll('input[type="checkbox"]');
for (var i = 0; i < checkboxes.length; i++) {
checkboxes[i].addEventListener('change', function() {
updateClientList();
});
}
}
var persistSkip = document.querySelector("input#PersistSkipButton"); var persistSkip = document.querySelector("input#PersistSkipButton");
var showAdjustment = document.querySelector("div#divShowPromptAdjustment"); var showAdjustment = document.querySelector("div#divShowPromptAdjustment");
var hideAdjustment = document.querySelector("div#divHidePromptAdjustment"); var hideAdjustment = document.querySelector("div#divHidePromptAdjustment");
@ -976,7 +1034,6 @@
document.querySelector("#editRightIntroEpisodeEnd").value = setTime(Math.round(rightEpisodeJson.Introduction.IntroEnd)); document.querySelector("#editRightIntroEpisodeEnd").value = setTime(Math.round(rightEpisodeJson.Introduction.IntroEnd));
document.querySelector("#editRightCreditEpisodeStart").value = setTime(Math.round(rightEpisodeJson.Credits.IntroStart)); document.querySelector("#editRightCreditEpisodeStart").value = setTime(Math.round(rightEpisodeJson.Credits.IntroStart));
document.querySelector("#editRightCreditEpisodeEnd").value = setTime(Math.round(rightEpisodeJson.Credits.IntroEnd)); document.querySelector("#editRightCreditEpisodeEnd").value = setTime(Math.round(rightEpisodeJson.Credits.IntroEnd));
} }
// adds an item to a dropdown // adds an item to a dropdown
@ -1143,6 +1200,7 @@
autoSkipChanged(); autoSkipChanged();
autoSkipCreditsChanged(); autoSkipCreditsChanged();
persistSkipChanged(); persistSkipChanged();
generateAutoSkipClientList();
Dashboard.hideLoadingMsg(); Dashboard.hideLoadingMsg();
}); });