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.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using System.Timers;
using ConfusedPolarBear.Plugin.IntroSkipper.Configuration;
using ConfusedPolarBear.Plugin.IntroSkipper.Data;
using MediaBrowser.Common.Extensions;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Session;
@ -21,32 +21,25 @@ namespace ConfusedPolarBear.Plugin.IntroSkipper;
/// Automatically skip past introduction sequences.
/// Commands clients to seek to the end of the intro as soon as they start playing it.
/// </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 ILogger<AutoSkip> _logger;
private IUserDataManager _userDataManager;
private ISessionManager _sessionManager;
private ILogger<AutoSkip> _logger = logger;
private IUserDataManager _userDataManager = userDataManager;
private ISessionManager _sessionManager = sessionManager;
private Timer _playbackTimer = new(1000);
private Dictionary<string, bool> _sentSeekCommand;
/// <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 Dictionary<string, bool> _sentSeekCommand = [];
private HashSet<string> _clientList = [];
private void AutoSkipChanged(object? sender, BasePluginConfiguration e)
{
@ -54,6 +47,7 @@ public class AutoSkip : IHostedService, IDisposable
var newState = configuration.AutoSkip;
_logger.LogDebug("Setting playback timer enabled to {NewState}", newState);
_playbackTimer.Enabled = newState;
_clientList = [.. configuration.ClientList.Split(',', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries)];
}
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)
{
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 itemId = session.NowPlayingItem.Id;
var position = session.PlayState.PositionTicks / TimeSpan.TicksPerSecond;

View File

@ -1,10 +1,10 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using System.Timers;
using ConfusedPolarBear.Plugin.IntroSkipper.Configuration;
using ConfusedPolarBear.Plugin.IntroSkipper.Data;
using MediaBrowser.Common.Extensions;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Session;
@ -21,32 +21,25 @@ namespace ConfusedPolarBear.Plugin.IntroSkipper;
/// Automatically skip past credit sequences.
/// Commands clients to seek to the end of the credits as soon as they start playing it.
/// </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 ILogger<AutoSkipCredits> _logger;
private IUserDataManager _userDataManager;
private ISessionManager _sessionManager;
private ILogger<AutoSkipCredits> _logger = logger;
private IUserDataManager _userDataManager = userDataManager;
private ISessionManager _sessionManager = sessionManager;
private Timer _playbackTimer = new(1000);
private Dictionary<string, bool> _sentSeekCommand;
/// <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 Dictionary<string, bool> _sentSeekCommand = [];
private HashSet<string> _clientList = [];
private void AutoSkipCreditChanged(object? sender, BasePluginConfiguration e)
{
@ -54,6 +47,7 @@ public class AutoSkipCredits : IHostedService, IDisposable
var newState = configuration.AutoSkipCredits;
_logger.LogDebug("Setting playback timer enabled to {NewState}", newState);
_playbackTimer.Enabled = newState;
_clientList = [.. configuration.ClientList.Split(',', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries)];
}
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)
{
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 itemId = session.NowPlayingItem.Id;
var position = session.PlayState.PositionTicks / TimeSpan.TicksPerSecond;

View File

@ -29,6 +29,11 @@ public class PluginConfiguration : BasePluginConfiguration
/// </summary>
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>
/// Gets or sets a value indicating whether to scan for intros during a scheduled task.
/// </summary>

View File

@ -398,6 +398,14 @@
<br />
</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">
<label class="emby-checkbox-label">
<input id="SkipButtonVisible" type="checkbox" is="emby-checkbox" />
@ -699,6 +707,7 @@
// analysis
"MaxParallelism",
"SelectedLibraries",
"ClientList",
"AnalysisPercent",
"AnalysisLengthLimit",
"MinimumIntroDuration",
@ -758,12 +767,13 @@
var autoSkip = document.querySelector("input#AutoSkip");
var skipFirstEpisode = document.querySelector("div#divSkipFirstEpisode");
var secondsOfIntroStartToPlay = document.querySelector("div#divSecondsOfIntroStartToPlay");
var autoSkipClientList = document.getElementById("AutoSkipClientList");
var secondsOfCreditsStartToPlay = document.querySelector("div#divSecondsOfCreditsStartToPlay");
var autoSkipNotificationText = document.querySelector("div#divAutoSkipNotificationText");
var autoSkipCredits = document.querySelector("input#AutoSkipCredits");
var autoSkipCreditsNotificationText = document.querySelector("div#divAutoSkipCreditsNotificationText");
async function autoSkipChanged() {
function autoSkipChanged() {
if (autoSkip.checked) {
skipFirstEpisode.style.display = 'unset';
autoSkipNotificationText.style.display = 'unset';
@ -773,11 +783,12 @@
autoSkipNotificationText.style.display = 'none';
secondsOfIntroStartToPlay.style.display = 'none';
}
clientListVisible();
}
autoSkip.addEventListener("change", autoSkipChanged);
async function autoSkipCreditsChanged() {
function autoSkipCreditsChanged() {
if (autoSkipCredits.checked) {
autoSkipCreditsNotificationText.style.display = 'unset';
secondsOfCreditsStartToPlay.style.display = 'unset';
@ -785,10 +796,57 @@
autoSkipCreditsNotificationText.style.display = 'none';
secondsOfCreditsStartToPlay.style.display = 'none';
}
clientListVisible();
}
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 showAdjustment = document.querySelector("div#divShowPromptAdjustment");
var hideAdjustment = document.querySelector("div#divHidePromptAdjustment");
@ -976,7 +1034,6 @@
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));
}
// adds an item to a dropdown
@ -1143,6 +1200,7 @@
autoSkipChanged();
autoSkipCreditsChanged();
persistSkipChanged();
generateAutoSkipClientList();
Dashboard.hideLoadingMsg();
});