Switch from watched status synchronization task to an event based solution

This commit is contained in:
DarkFighterLuke
2024-09-11 16:19:18 +02:00
parent 52342b3dc3
commit c9cc4f150e
4 changed files with 31 additions and 189 deletions
@@ -41,7 +41,6 @@ namespace Jellyfin.Plugin.TubeArchivistMetadata.Configuration
TAJFSync = false;
_jfUsernamesTo = new HashSet<string>();
TAJFTaskInterval = 1;
JFTAWatchedTaskInterval = 1;
}
/// <summary>
@@ -136,12 +135,6 @@ namespace Jellyfin.Plugin.TubeArchivistMetadata.Configuration
/// </summary>
public int TAJFTaskInterval { get; set; }
/// <summary>
/// Gets or sets the interval in seconds at which the Jellyfin to TubeArchivist watched statuses synchronization task should run.
/// It requires Jellyfin server restart to take effect.
/// </summary>
public int JFTAWatchedTaskInterval { get; set; }
/// <summary>
/// Gets the playback progress owners Jellyfin usernames to synchronize data from TubeArchivist.
/// </summary>
@@ -58,13 +58,6 @@
<div class="fieldDescription">This is the Jellyfin username to synchronize data from on
TubeArchivist</div>
</div>
<div class="inputContainer">
<label class="inputLabel inputLabelUnfocused" for="JFTAWatchedTaskInterval">Jellyfin to TubeArchivist
watched statuses synchronization interval</label>
<input id="JFTAWatchedTaskInterval" name="JFTAWatchedTaskInterval" type="number" is="emby-input" min="1" />
<div class="fieldDescription">This is the Jellyfin to TubeArchivist watched statuses
synchronization interval in seconds (Jellyfin restart required).</div>
</div>
<div class="checkboxContainer checkboxContainer-withDescription">
<label class="emby-checkbox-label" for="TAJFSync">
<input id="TAJFSync" name="TAJFSync" type="checkbox" is="emby-checkbox" />
@@ -112,7 +105,6 @@
document.querySelector('#TAJFSync').checked = config.TAJFSync;
document.querySelector('#JFUsernameFrom').value = config.JFUsernameFrom;
document.querySelector('#TAJFTaskInterval').value = config.TAJFTaskInterval;
document.querySelector('#JFTAWatchedTaskInterval').value = config.JFTAWatchedTaskInterval;
Dashboard.hideLoadingMsg();
});
});
@@ -130,7 +122,6 @@
config.TAJFSync = document.querySelector('#TAJFSync').checked;
config.JFUsernameFrom = document.querySelector('#JFUsernameFrom').value;
config.TAJFTaskInterval = document.querySelector('#TAJFTaskInterval').value;
config.JFTAWatchedTaskInterval = document.querySelector('#JFTAWatchedTaskInterval').value;
ApiClient.updatePluginConfiguration(TAMetadataConfig.pluginUniqueId, config).then(function (result) {
Dashboard.processPluginConfigurationUpdateResult(result);
});
@@ -62,10 +62,10 @@ namespace Jellyfin.Plugin.TubeArchivistMetadata
sessionManager.PlaybackProgress += OnPlaybackProgress;
LibraryManager = libraryManager;
_userManager = userManager;
userDataManager.UserDataSaved += OnWatchedStatusChange;
var taToJellyfinProgressSyncTask = new TAToJellyfinProgressSyncTask(logger, libraryManager, userManager, userDataManager);
var jfToTubearchivistProgressSyncTask = new JFToTubearchivistProgressSyncTask(logger, libraryManager, userManager, userDataManager);
var jfToTubearchivistWatchedSyncTask = new JFToTubearchivistWatchedSyncTask(logger, libraryManager, userManager, userDataManager);
var isTAJFTaskPresent = taskManager.ScheduledTasks.Any(t => t.Name.Equals(taToJellyfinProgressSyncTask.Name, StringComparison.Ordinal));
if (Instance!.Configuration.TAJFSync && !isTAJFTaskPresent)
{
@@ -78,10 +78,8 @@ namespace Jellyfin.Plugin.TubeArchivistMetadata
if (Instance!.Configuration.JFTASync && !isJFTATaskPresent)
{
logger.LogInformation("Queueing task {TaskName}.", jfToTubearchivistProgressSyncTask.Name);
logger.LogInformation("Queueing task {TaskName}.", jfToTubearchivistWatchedSyncTask.Name);
taskManager.AddTasks([jfToTubearchivistProgressSyncTask, jfToTubearchivistWatchedSyncTask]);
taskManager.AddTasks([jfToTubearchivistProgressSyncTask]);
taskManager.Execute<JFToTubearchivistProgressSyncTask>();
taskManager.Execute<JFToTubearchivistWatchedSyncTask>();
}
logger.LogInformation("{Message}", "Collection display name: " + Instance?.Configuration.CollectionTitle);
@@ -171,5 +169,34 @@ namespace Jellyfin.Plugin.TubeArchivistMetadata
}
}
}
private async void OnWatchedStatusChange(object? sender, UserDataSaveEventArgs eventArgs)
{
var user = _userManager.GetUserById(eventArgs.UserId);
if (user != null && Configuration.GetJFUsernamesToArray().Contains(user!.Username))
{
var isPlayed = eventArgs.Item.IsPlayed(user);
Logger.LogInformation("User {UserId} changed watched status to {Status} for the item {ItemName}", eventArgs.UserId, isPlayed, eventArgs.Item.Name);
string itemYTId;
if (eventArgs.Item is Series)
{
itemYTId = Utils.GetChannelNameFromPath(eventArgs.Item.Path);
}
else if (eventArgs.Item is Episode)
{
itemYTId = Utils.GetVideoNameFromPath(eventArgs.Item.Path);
}
else
{
return;
}
var statusCode = await TubeArchivistApi.GetInstance().SetWatchedStatus(itemYTId, isPlayed).ConfigureAwait(true);
if (statusCode != System.Net.HttpStatusCode.OK)
{
Logger.LogInformation("POST /watched returned {StatusCode} for item {ItemName} ({VideoYTId}) with watched status {IsPlayed}", statusCode, eventArgs.Item.Name, itemYTId, isPlayed);
}
}
}
}
}
@@ -1,169 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Jellyfin.Data.Enums;
using Jellyfin.Plugin.TubeArchivistMetadata.TubeArchivist;
using Jellyfin.Plugin.TubeArchivistMetadata.Utilities;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.TV;
using MediaBrowser.Controller.Library;
using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Tasks;
using Microsoft.Extensions.Logging;
namespace Jellyfin.Plugin.TubeArchivistMetadata.Tasks
{
/// <summary>
/// Task to sync Jellyfin playback progresses to TubeArchivist.
/// </summary>
public class JFToTubearchivistWatchedSyncTask : IScheduledTask
{
private readonly ILogger<Plugin> _logger;
private readonly ILibraryManager _libraryManager;
private readonly IUserManager _userManager;
private readonly IUserDataManager _userDataManager;
/// <summary>
/// Initializes a new instance of the <see cref="JFToTubearchivistWatchedSyncTask"/> class.
/// </summary>
/// <param name="logger">Logger.</param>
/// <param name="libraryManager">Library manager.</param>
/// <param name="userManager">User manager.</param>
/// <param name="userDataManager">User data manager.</param>
public JFToTubearchivistWatchedSyncTask(ILogger<Plugin> logger, ILibraryManager libraryManager, IUserManager userManager, IUserDataManager userDataManager)
{
_logger = logger;
_libraryManager = libraryManager;
_userManager = userManager;
_userDataManager = userDataManager;
}
/// <inheritdoc/>
public string Name => "JFToTubeArchivistWatchedSyncTask";
/// <inheritdoc/>
public string Description => "This tasks syncs TubeArchivist watched statuses to Jellyfin";
/// <inheritdoc/>
public string Category => "TubeArchivistMetadata";
/// <inheritdoc/>
public string Key => "JFToTubeArchivistWatchedSyncTask";
/// <inheritdoc/>
public async Task ExecuteAsync(IProgress<double> progress, CancellationToken cancellationToken)
{
if (Plugin.Instance!.Configuration.JFTASync)
{
var start = DateTime.Now;
_logger.LogInformation("Starting Jellyfin->TubeArchivist watched statuses synchronization.");
var taApi = TubeArchivistApi.GetInstance();
foreach (var jfUsername in Plugin.Instance!.Configuration.GetJFUsernamesToArray())
{
var user = _userManager.GetUserByName(jfUsername);
if (user == null)
{
_logger.LogInformation("{Message}", $"Jellyfin user with username {jfUsername} not found");
continue;
}
var collectionItem = _libraryManager.GetItemList(new InternalItemsQuery
{
Name = Plugin.Instance?.Configuration.CollectionTitle,
IncludeItemTypes = new[] { BaseItemKind.CollectionFolder }
}).FirstOrDefault();
if (collectionItem == null)
{
var message = $"Collection '{Plugin.Instance?.Configuration.CollectionTitle}' not found.";
_logger.LogCritical("{Message}", message);
}
else
{
var collection = (CollectionFolder)collectionItem;
var channels = collection.GetChildren(user, false, new InternalItemsQuery
{
IncludeItemTypes = new[] { BaseItemKind.Series }
});
_logger.LogInformation("Analyzing collection {Id} with name {Name}", collectionItem.Id, collectionItem.Name);
_logger.LogInformation("Found {Message} channels", channels.Count);
foreach (Series channel in channels)
{
var channelYTId = Utils.GetChannelNameFromPath(channel.Path);
var isChannelWatched = false;
var isChannelCheckedForWatched = false;
var years = channel.GetChildren(user, false, new InternalItemsQuery
{
IncludeItemTypes = new[] { BaseItemKind.Season }
});
_logger.LogInformation("Found {Years} years in channel {ChannelName}", years.Count, channel.Name);
foreach (Season year in years)
{
var videos = year.GetChildren(user, false, new InternalItemsQuery
{
IncludeItemTypes = new[] { BaseItemKind.Episode }
});
_logger.LogInformation("Found {Videos} videos in year {YearName} of the channel {ChannelName}", videos.Count, year.Name, channel.Name);
foreach (Episode video in videos)
{
var videoYTId = Utils.GetVideoNameFromPath(video.Path);
if (!isChannelCheckedForWatched && channel.IsPlayed(user))
{
var isChannelPlayed = channel.IsPlayed(user);
var statusCode = await taApi.SetWatchedStatus(channelYTId, isChannelPlayed).ConfigureAwait(true);
if (statusCode != System.Net.HttpStatusCode.OK)
{
_logger.LogInformation("{Message}", $"POST /watched returned {statusCode} for channel {channel.Name} ({channelYTId}) with wacthed status {isChannelPlayed}");
}
else
{
isChannelWatched = true;
}
isChannelCheckedForWatched = true;
}
if (!isChannelWatched)
{
var isVideoPlayed = video.IsPlayed(user);
var statusCode = await taApi.SetWatchedStatus(videoYTId, isVideoPlayed).ConfigureAwait(true);
if (statusCode != System.Net.HttpStatusCode.OK)
{
_logger.LogInformation("{Message}", $"POST /watched returned {statusCode} for video {video.Name} ({videoYTId}) with watched status {isVideoPlayed}");
}
}
}
}
}
}
}
_logger.LogInformation("Time elapsed: {Time}", DateTime.Now - start);
}
else
{
_logger.LogInformation("Jellyfin->TubeArchivist watched status is currently disabled.");
}
}
/// <inheritdoc/>
public IEnumerable<TaskTriggerInfo> GetDefaultTriggers()
{
return
[
new TaskTriggerInfo
{
Type = TaskTriggerInfo.TriggerInterval,
IntervalTicks = TimeSpan.FromSeconds(Plugin.Instance!.Configuration.JFTAWatchedTaskInterval).Ticks
},
];
}
}
}