aboutsummaryrefslogtreecommitdiff
path: root/src/Jellyfin.MediaEncoding.Hls/Cache/CacheDecorator.cs
diff options
context:
space:
mode:
authorTim Eisele <Ghost_of_Stone@web.de>2025-04-03 02:06:40 +0200
committerGitHub <noreply@github.com>2025-04-02 18:06:40 -0600
commit0573999d5ef7526a3bb3e24523ba0e5599816155 (patch)
tree7d35df4c7c8536fc8f2f99d8d05309d97862684d /src/Jellyfin.MediaEncoding.Hls/Cache/CacheDecorator.cs
parent49ac705867234c48e79ceb1cd84bc4394c65313d (diff)
Import Keyframes into database (#13771)
* Migrate keyframe data into database * Clear database table before import to handle failed migrations
Diffstat (limited to 'src/Jellyfin.MediaEncoding.Hls/Cache/CacheDecorator.cs')
-rw-r--r--src/Jellyfin.MediaEncoding.Hls/Cache/CacheDecorator.cs79
1 files changed, 22 insertions, 57 deletions
diff --git a/src/Jellyfin.MediaEncoding.Hls/Cache/CacheDecorator.cs b/src/Jellyfin.MediaEncoding.Hls/Cache/CacheDecorator.cs
index 127f4079c..8ca0e869a 100644
--- a/src/Jellyfin.MediaEncoding.Hls/Cache/CacheDecorator.cs
+++ b/src/Jellyfin.MediaEncoding.Hls/Cache/CacheDecorator.cs
@@ -1,13 +1,12 @@
+#pragma warning disable CA1826 // Do not use Enumerable methods on indexable collections
+
using System;
using System.Diagnostics.CodeAnalysis;
-using System.Globalization;
-using System.IO;
-using System.Text.Json;
-using Jellyfin.Extensions.Json;
+using System.Linq;
+using System.Threading;
using Jellyfin.MediaEncoding.Hls.Extractors;
using Jellyfin.MediaEncoding.Keyframes;
-using MediaBrowser.Common.Configuration;
-using MediaBrowser.Common.Extensions;
+using MediaBrowser.Controller.Persistence;
using Microsoft.Extensions.Logging;
namespace Jellyfin.MediaEncoding.Hls.Cache;
@@ -15,82 +14,48 @@ namespace Jellyfin.MediaEncoding.Hls.Cache;
/// <inheritdoc />
public class CacheDecorator : IKeyframeExtractor
{
+ private readonly IKeyframeRepository _keyframeRepository;
private readonly IKeyframeExtractor _keyframeExtractor;
private readonly ILogger<CacheDecorator> _logger;
private readonly string _keyframeExtractorName;
- private static readonly JsonSerializerOptions _jsonOptions = JsonDefaults.Options;
- private readonly string _keyframeCachePath;
/// <summary>
/// Initializes a new instance of the <see cref="CacheDecorator"/> class.
/// </summary>
- /// <param name="applicationPaths">An instance of the <see cref="IApplicationPaths"/> interface.</param>
+ /// <param name="keyframeRepository">An instance of the <see cref="IKeyframeRepository"/> interface.</param>
/// <param name="keyframeExtractor">An instance of the <see cref="IKeyframeExtractor"/> interface.</param>
/// <param name="logger">An instance of the <see cref="ILogger{CacheDecorator}"/> interface.</param>
- public CacheDecorator(IApplicationPaths applicationPaths, IKeyframeExtractor keyframeExtractor, ILogger<CacheDecorator> logger)
+ public CacheDecorator(IKeyframeRepository keyframeRepository, IKeyframeExtractor keyframeExtractor, ILogger<CacheDecorator> logger)
{
- ArgumentNullException.ThrowIfNull(applicationPaths);
+ ArgumentNullException.ThrowIfNull(keyframeRepository);
ArgumentNullException.ThrowIfNull(keyframeExtractor);
+ _keyframeRepository = keyframeRepository;
_keyframeExtractor = keyframeExtractor;
_logger = logger;
_keyframeExtractorName = keyframeExtractor.GetType().Name;
- // TODO make the dir configurable
- _keyframeCachePath = Path.Combine(applicationPaths.DataPath, "keyframes");
}
/// <inheritdoc />
public bool IsMetadataBased => _keyframeExtractor.IsMetadataBased;
/// <inheritdoc />
- public bool TryExtractKeyframes(string filePath, [NotNullWhen(true)] out KeyframeData? keyframeData)
+ public bool TryExtractKeyframes(Guid itemId, string filePath, [NotNullWhen(true)] out KeyframeData? keyframeData)
{
- keyframeData = null;
- var cachePath = GetCachePath(_keyframeCachePath, filePath);
- if (TryReadFromCache(cachePath, out var cachedResult))
+ keyframeData = _keyframeRepository.GetKeyframeData(itemId).FirstOrDefault();
+ if (keyframeData is null)
{
- keyframeData = cachedResult;
- return true;
+ if (!_keyframeExtractor.TryExtractKeyframes(itemId, filePath, out var result))
+ {
+ _logger.LogDebug("Failed to extract keyframes using {ExtractorName}", _keyframeExtractorName);
+ return false;
+ }
+
+ _logger.LogDebug("Successfully extracted keyframes using {ExtractorName}", _keyframeExtractorName);
+ keyframeData = result;
+ _keyframeRepository.SaveKeyframeDataAsync(itemId, keyframeData, CancellationToken.None).GetAwaiter().GetResult();
}
- if (!_keyframeExtractor.TryExtractKeyframes(filePath, out var result))
- {
- _logger.LogDebug("Failed to extract keyframes using {ExtractorName}", _keyframeExtractorName);
- return false;
- }
-
- _logger.LogDebug("Successfully extracted keyframes using {ExtractorName}", _keyframeExtractorName);
- keyframeData = result;
- SaveToCache(cachePath, keyframeData);
return true;
}
-
- private static void SaveToCache(string cachePath, KeyframeData keyframeData)
- {
- var json = JsonSerializer.Serialize(keyframeData, _jsonOptions);
- Directory.CreateDirectory(Path.GetDirectoryName(cachePath) ?? throw new ArgumentException($"Provided path ({cachePath}) is not valid.", nameof(cachePath)));
- File.WriteAllText(cachePath, json);
- }
-
- private static string GetCachePath(string keyframeCachePath, string filePath)
- {
- var lastWriteTimeUtc = File.GetLastWriteTimeUtc(filePath);
- ReadOnlySpan<char> filename = (filePath + "_" + lastWriteTimeUtc.Ticks.ToString(CultureInfo.InvariantCulture)).GetMD5() + ".json";
- var prefix = filename[..1];
-
- return Path.Join(keyframeCachePath, prefix, filename);
- }
-
- private static bool TryReadFromCache(string cachePath, [NotNullWhen(true)] out KeyframeData? cachedResult)
- {
- if (File.Exists(cachePath))
- {
- var bytes = File.ReadAllBytes(cachePath);
- cachedResult = JsonSerializer.Deserialize<KeyframeData>(bytes, _jsonOptions);
- return cachedResult is not null;
- }
-
- cachedResult = null;
- return false;
- }
}