Skip to content

Commit

Permalink
Feat: Add a polling task to check for update via the update api endpo…
Browse files Browse the repository at this point in the history
…int (#124)
  • Loading branch information
scampower3 authored Mar 24, 2024
1 parent 42338a1 commit 18b1e47
Show file tree
Hide file tree
Showing 4 changed files with 175 additions and 0 deletions.
10 changes: 10 additions & 0 deletions Jellyfin.Plugin.Tvdb/Configuration/PluginConfiguration.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ public class PluginConfiguration : BasePluginConfiguration
public const string ProjectApiKey = "7f7eed88-2530-4f84-8ee7-f154471b8f87";
private int _cacheDurationInHours = 1;
private int _cacheDurationInDays = 7;
private int _metadataUpdateInHours = 2;

/// <summary>
/// Gets or sets the tvdb api key for user.
Expand All @@ -39,5 +40,14 @@ public int CacheDurationInDays
get => _cacheDurationInDays;
set => _cacheDurationInDays = value < 1 ? 7 : value;
}

/// <summary>
/// Gets or sets the metadata update in hours.
/// </summary>
public int MetadataUpdateInHours
{
get => _metadataUpdateInHours;
set => _metadataUpdateInHours = value < 1 ? 1 : value;
}
}
}
9 changes: 9 additions & 0 deletions Jellyfin.Plugin.Tvdb/Configuration/config.html
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,13 @@ <h2 class="sectionTitle">TheTVDB Settings:</h2>
The cache time in days for Languages and Activity Type metadata.
</div>
</div>
<div class="inputContainer">
<label for="metadataUpdateInHours">Metadata Update In Hours:</label>
<input type="number" id="metadataUpdateInHours" required="required" is="emby-input" />
<div class="fieldDescription">
How many hours ago should the metadata be last updated on TheTvdb. Should be greater than the value of cache time in hours.
</div>
</div>
<br />
<div>
<button is="emby-button" type="submit" data-theme="b" class="raised button-submit block">
Expand All @@ -56,6 +63,7 @@ <h2 class="sectionTitle">TheTVDB Settings:</h2>
document.getElementById('txtTvdbSubscriberPIN').value = config.SubscriberPIN || '';
document.getElementById('cacheDurationInHours').value = config.CacheDurationInHours;
document.getElementById('cacheDurationInDays').value = config.CacheDurationInDays;
document.getElementById('metadataUpdateInHours').value = config.MetadataUpdateInHours;
Dashboard.hideLoadingMsg();
});
},
Expand All @@ -67,6 +75,7 @@ <h2 class="sectionTitle">TheTVDB Settings:</h2>
config.SubscriberPIN = document.getElementById('txtTvdbSubscriberPIN').value;
config.CacheDurationInHours = document.getElementById('cacheDurationInHours').value;
config.CacheDurationInDays = document.getElementById('cacheDurationInDays').value;
config.MetadataUpdateInHours = document.getElementById('metadataUpdateInHours').value;
ApiClient.updatePluginConfiguration(TvdbPluginConfiguration.uniquePluginId, config).then(function (result) {
Dashboard.processPluginConfigurationUpdateResult(result);
});
Expand Down
132 changes: 132 additions & 0 deletions Jellyfin.Plugin.Tvdb/ScheduledTasks/UpdateTask.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.TV;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.IO;
using MediaBrowser.Model.Tasks;
using Microsoft.Extensions.Logging;
using Tvdb.Sdk;

using Action = Tvdb.Sdk.Action;
using Type = Tvdb.Sdk.Type;

namespace Jellyfin.Plugin.Tvdb.ScheduledTasks
{
/// <summary>
/// Task to poll TheTVDB for updates.
/// </summary>
public class UpdateTask : IScheduledTask
{
private readonly ILibraryManager _libraryManager;
private readonly IProviderManager _providerManager;
private readonly IFileSystem _fileSystem;
private readonly ILogger<UpdateTask> _logger;
private readonly TvdbClientManager _tvdbClientManager;

/// <summary>
/// Initializes a new instance of the <see cref="UpdateTask"/> class.
/// </summary>
/// <param name="tvdbClientManager">TheTvdb Client Manager.</param>
/// <param name="libraryManager">Library Manager.</param>
/// <param name="providerManager">Provider Manager.</param>
/// <param name="fileSystem">File System.</param>
/// <param name="logger">Logger.</param>
public UpdateTask(TvdbClientManager tvdbClientManager, ILibraryManager libraryManager, IProviderManager providerManager, IFileSystem fileSystem, ILogger<UpdateTask> logger)
{
_libraryManager = libraryManager;
_providerManager = providerManager;
_fileSystem = fileSystem;
_logger = logger;
_tvdbClientManager = tvdbClientManager;
}

/// <inheritdoc/>
public string Name => "Check for metadata updates.";

/// <inheritdoc/>
public string Key => "CheckForMetadataUpdatesTask";

/// <inheritdoc/>
public string Description => "Checks TheTvdb's API Update endpoint for updates periodically and updates metadata accordingly.";

/// <inheritdoc/>
public string Category => "TheTVDB";

private static int MetadataUpdateInHours => TvdbPlugin.Instance?.Configuration.MetadataUpdateInHours * -1 ?? -1;

/// <inheritdoc/>
public async Task ExecuteAsync(IProgress<double> progress, CancellationToken cancellationToken)
{
progress.Report(0);
_logger.LogInformation("Checking for metadata updates.");
var toUpdateItems = await GetItemsUpdated(cancellationToken).ConfigureAwait(false);
_logger.LogInformation("Found {0} items to update.", toUpdateItems.Count);
progress.Report(10);
MetadataRefreshOptions refreshOptions = new MetadataRefreshOptions(new DirectoryService(_fileSystem))
{
MetadataRefreshMode = MetadataRefreshMode.FullRefresh
};
double increment = 90.0 / toUpdateItems.Count;
double currentProgress = 10;
foreach (BaseItem item in toUpdateItems)
{
_logger.LogInformation("Refreshing metadata for TvdbId {Tvdbid}:{Name}", item.GetTvdbId(), item.Name);
await _providerManager.RefreshSingleItem(
item,
refreshOptions,
cancellationToken).ConfigureAwait(false);
currentProgress += increment;
progress.Report(currentProgress);
}

progress.Report(100);
}

/// <inheritdoc/>
public IEnumerable<TaskTriggerInfo> GetDefaultTriggers()
{
return Enumerable.Empty<TaskTriggerInfo>();
}

/// <summary>
/// Gets all items that have been updated.
/// </summary>
/// <returns>List of items that have been updated.</returns>
private async Task<List<BaseItem>> GetItemsUpdated(CancellationToken cancellationToken)
{
double fromTime = DateTimeOffset.UtcNow.AddHours(MetadataUpdateInHours).ToUnixTimeSeconds();
IReadOnlyList<EntityUpdate> episodeUpdates = await _tvdbClientManager.GetUpdates(fromTime, cancellationToken, Type.Episodes, Action.Update).ConfigureAwait(false);
IReadOnlyList<EntityUpdate> seriesUpdates = await _tvdbClientManager.GetUpdates(fromTime, cancellationToken, Type.Series, Action.Update).ConfigureAwait(false);

var allUpdates = episodeUpdates.Concat(seriesUpdates);

string providerId = MetadataProvider.Tvdb.ToString();

List<BaseItem> toUpdateItems = new List<BaseItem>();

Dictionary<string, string> providerIdPair = new Dictionary<string, string>() { { providerId, string.Empty } };
InternalItemsQuery query = new InternalItemsQuery();

foreach (EntityUpdate update in allUpdates)
{
providerIdPair[providerId] = update.RecordId!.Value.ToString(CultureInfo.InvariantCulture);
query.HasAnyProviderId = providerIdPair;
List<BaseItem> itemList = _libraryManager.GetItemList(query);
if (itemList.Count > 0 && !toUpdateItems.Contains(itemList[0]))
{
toUpdateItems.Add(itemList[0]);
}
}

return toUpdateItems;
}
}
}
24 changes: 24 additions & 0 deletions Jellyfin.Plugin.Tvdb/TvdbClientManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@
using Microsoft.Extensions.DependencyInjection;
using Tvdb.Sdk;

using Action = Tvdb.Sdk.Action;
using Type = Tvdb.Sdk.Type;

namespace Jellyfin.Plugin.Tvdb;

/// <summary>
Expand Down Expand Up @@ -510,6 +513,26 @@ public async Task<IReadOnlyList<ArtworkType>> GetArtworkTypeAsync(CancellationTo
}
}

/// <summary>
/// Gets updates from tvdb since a given time. No caching.
/// </summary>
/// <param name="fromTime">From time in unix timestamp.</param>
/// <param name="cancellationToken">Cancellation token.</param>
/// <param name="type"> Type of data.</param>
/// <param name="action">Delete, update or null.</param>
/// <returns>A list of updates.</returns>
public async Task<IReadOnlyList<EntityUpdate>> GetUpdates(
double fromTime,
CancellationToken cancellationToken,
Type? type = null,
Action? action = null)
{
var updatesClient = _serviceProvider.GetRequiredService<IUpdatesClient>();
await LoginAsync().ConfigureAwait(false);
var updatesResult = await updatesClient.UpdatesAsync(since: fromTime, type: type, action: action, cancellationToken: cancellationToken).ConfigureAwait(false);
return updatesResult.Data;
}

/// <summary>
/// Purge the cache.
/// </summary>
Expand Down Expand Up @@ -568,6 +591,7 @@ private ServiceProvider ConfigureService(IApplicationHost applicationHost)
services.AddTransient<IArtworkClient>(_ => new ArtworkClient(_sdkClientSettings, _httpClientFactory.CreateClient(TvdbHttpClient)));
services.AddTransient<IArtwork_TypesClient>(_ => new Artwork_TypesClient(_sdkClientSettings, _httpClientFactory.CreateClient(TvdbHttpClient)));
services.AddTransient<ILanguagesClient>(_ => new LanguagesClient(_sdkClientSettings, _httpClientFactory.CreateClient(TvdbHttpClient)));
services.AddTransient<IUpdatesClient>(_ => new UpdatesClient(_sdkClientSettings, _httpClientFactory.CreateClient(TvdbHttpClient)));

return services.BuildServiceProvider();
}
Expand Down

0 comments on commit 18b1e47

Please sign in to comment.