100 lines
3.6 KiB
C#
100 lines
3.6 KiB
C#
using System;
|
|
using System.Collections.Concurrent;
|
|
using System.IO;
|
|
using System.Text.Json;
|
|
using System.Threading;
|
|
using System.Threading.Tasks;
|
|
using Jellyfin.Plugin.JRay.Models;
|
|
using Jellyfin.Plugin.JRay.Services.Interfaces;
|
|
using MediaBrowser.Controller.Library;
|
|
using Microsoft.Extensions.Logging;
|
|
|
|
namespace Jellyfin.Plugin.JRay.Services;
|
|
|
|
/// <summary>
|
|
/// Loads scene-actor-extraction truth files from disk, alongside each media
|
|
/// item's source file, and caches the parsed result for a configurable
|
|
/// duration.
|
|
/// </summary>
|
|
public sealed class TruthDataService : ITruthDataService
|
|
{
|
|
private static readonly JsonSerializerOptions JsonOptions = new(JsonSerializerDefaults.Web);
|
|
|
|
private readonly ILibraryManager _libraryManager;
|
|
private readonly IManagedTruthStore _managedTruthStore;
|
|
private readonly ILogger<TruthDataService> _logger;
|
|
private readonly ConcurrentDictionary<Guid, CacheEntry> _cache = new();
|
|
|
|
/// <summary>
|
|
/// Initializes a new instance of the <see cref="TruthDataService"/> class.
|
|
/// </summary>
|
|
/// <param name="libraryManager">The Jellyfin library manager.</param>
|
|
/// <param name="managedTruthStore">The managed truth store.</param>
|
|
/// <param name="logger">The logger.</param>
|
|
public TruthDataService(ILibraryManager libraryManager, IManagedTruthStore managedTruthStore, ILogger<TruthDataService> logger)
|
|
{
|
|
_libraryManager = libraryManager;
|
|
_managedTruthStore = managedTruthStore;
|
|
_logger = logger;
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
public async Task<TruthFile?> GetTruthAsync(Guid itemId, CancellationToken cancellationToken)
|
|
{
|
|
var cacheDuration = TimeSpan.FromMinutes(Math.Max(0, Plugin.Instance?.Configuration.CacheDurationMinutes ?? 0));
|
|
|
|
if (_cache.TryGetValue(itemId, out var cached) && DateTime.UtcNow - cached.LoadedAt < cacheDuration)
|
|
{
|
|
return cached.Truth;
|
|
}
|
|
|
|
var managed = await _managedTruthStore.LoadAsync(itemId, cancellationToken).ConfigureAwait(false);
|
|
if (managed is not null)
|
|
{
|
|
_cache[itemId] = new CacheEntry(managed, DateTime.UtcNow);
|
|
return managed;
|
|
}
|
|
|
|
var item = _libraryManager.GetItemById(itemId);
|
|
if (item is null || string.IsNullOrEmpty(item.Path))
|
|
{
|
|
_logger.LogDebug("JRay: item {ItemId} not found or has no path", itemId);
|
|
return null;
|
|
}
|
|
|
|
var suffix = Plugin.Instance?.Configuration.TruthFileSuffix ?? ".jray.json";
|
|
var truthPath = Path.Combine(
|
|
Path.GetDirectoryName(item.Path) ?? string.Empty,
|
|
Path.GetFileNameWithoutExtension(item.Path) + suffix);
|
|
|
|
if (!File.Exists(truthPath))
|
|
{
|
|
_logger.LogDebug("JRay: no truth file at {TruthPath}", truthPath);
|
|
_cache[itemId] = new CacheEntry(null, DateTime.UtcNow);
|
|
return null;
|
|
}
|
|
|
|
try
|
|
{
|
|
using var stream = File.OpenRead(truthPath);
|
|
var truth = await JsonSerializer.DeserializeAsync<TruthFile>(stream, JsonOptions, cancellationToken)
|
|
.ConfigureAwait(false);
|
|
_cache[itemId] = new CacheEntry(truth, DateTime.UtcNow);
|
|
return truth;
|
|
}
|
|
catch (Exception ex) when (ex is IOException or JsonException)
|
|
{
|
|
_logger.LogWarning(ex, "JRay: failed to read truth file {TruthPath}", truthPath);
|
|
return null;
|
|
}
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
public void Invalidate(Guid itemId)
|
|
{
|
|
_cache.TryRemove(itemId, out _);
|
|
}
|
|
|
|
private sealed record CacheEntry(TruthFile? Truth, DateTime LoadedAt);
|
|
}
|