< Summary - Jellyfin

Information
Class: Jellyfin.MediaEncoding.Hls.Cache.CacheDecorator
Assembly: Jellyfin.MediaEncoding.Hls
File(s): /srv/git/jellyfin/src/Jellyfin.MediaEncoding.Hls/Cache/CacheDecorator.cs
Line coverage
19%
Covered lines: 9
Uncovered lines: 37
Coverable lines: 46
Total lines: 96
Line coverage: 19.5%
Branch coverage
0%
Covered branches: 0
Total branches: 8
Branch coverage: 0%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Coverage history

Coverage history 0 25 50 75 100

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.cctor()100%210%
.ctor(...)100%11100%
get_IsMetadataBased()100%11100%
TryExtractKeyframes(...)0%2040%
SaveToCache(...)0%620%
GetCachePath(...)100%210%
TryReadFromCache(...)0%620%

File(s)

/srv/git/jellyfin/src/Jellyfin.MediaEncoding.Hls/Cache/CacheDecorator.cs

#LineLine coverage
 1using System;
 2using System.Diagnostics.CodeAnalysis;
 3using System.Globalization;
 4using System.IO;
 5using System.Text.Json;
 6using Jellyfin.Extensions.Json;
 7using Jellyfin.MediaEncoding.Hls.Extractors;
 8using Jellyfin.MediaEncoding.Keyframes;
 9using MediaBrowser.Common.Configuration;
 10using MediaBrowser.Common.Extensions;
 11using Microsoft.Extensions.Logging;
 12
 13namespace Jellyfin.MediaEncoding.Hls.Cache;
 14
 15/// <inheritdoc />
 16public class CacheDecorator : IKeyframeExtractor
 17{
 18    private readonly IKeyframeExtractor _keyframeExtractor;
 19    private readonly ILogger<CacheDecorator> _logger;
 20    private readonly string _keyframeExtractorName;
 021    private static readonly JsonSerializerOptions _jsonOptions = JsonDefaults.Options;
 22    private readonly string _keyframeCachePath;
 23
 24    /// <summary>
 25    /// Initializes a new instance of the <see cref="CacheDecorator"/> class.
 26    /// </summary>
 27    /// <param name="applicationPaths">An instance of the <see cref="IApplicationPaths"/> interface.</param>
 28    /// <param name="keyframeExtractor">An instance of the <see cref="IKeyframeExtractor"/> interface.</param>
 29    /// <param name="logger">An instance of the <see cref="ILogger{CacheDecorator}"/> interface.</param>
 30    public CacheDecorator(IApplicationPaths applicationPaths, IKeyframeExtractor keyframeExtractor, ILogger<CacheDecorat
 4431    {
 4432        ArgumentNullException.ThrowIfNull(applicationPaths);
 4433        ArgumentNullException.ThrowIfNull(keyframeExtractor);
 34
 4435        _keyframeExtractor = keyframeExtractor;
 4436        _logger = logger;
 4437        _keyframeExtractorName = keyframeExtractor.GetType().Name;
 38        // TODO make the dir configurable
 4439        _keyframeCachePath = Path.Combine(applicationPaths.DataPath, "keyframes");
 4440    }
 41
 42    /// <inheritdoc />
 4443    public bool IsMetadataBased => _keyframeExtractor.IsMetadataBased;
 44
 45    /// <inheritdoc />
 46    public bool TryExtractKeyframes(string filePath, [NotNullWhen(true)] out KeyframeData? keyframeData)
 047    {
 048        keyframeData = null;
 049        var cachePath = GetCachePath(_keyframeCachePath, filePath);
 050        if (TryReadFromCache(cachePath, out var cachedResult))
 051        {
 052            keyframeData = cachedResult;
 053            return true;
 54        }
 55
 056        if (!_keyframeExtractor.TryExtractKeyframes(filePath, out var result))
 057        {
 058            _logger.LogDebug("Failed to extract keyframes using {ExtractorName}", _keyframeExtractorName);
 059            return false;
 60        }
 61
 062        _logger.LogDebug("Successfully extracted keyframes using {ExtractorName}", _keyframeExtractorName);
 063        keyframeData = result;
 064        SaveToCache(cachePath, keyframeData);
 065        return true;
 066    }
 67
 68    private static void SaveToCache(string cachePath, KeyframeData keyframeData)
 069    {
 070        var json = JsonSerializer.Serialize(keyframeData, _jsonOptions);
 071        Directory.CreateDirectory(Path.GetDirectoryName(cachePath) ?? throw new ArgumentException($"Provided path ({cach
 072        File.WriteAllText(cachePath, json);
 073    }
 74
 75    private static string GetCachePath(string keyframeCachePath, string filePath)
 076    {
 077        var lastWriteTimeUtc = File.GetLastWriteTimeUtc(filePath);
 078        ReadOnlySpan<char> filename = (filePath + "_" + lastWriteTimeUtc.Ticks.ToString(CultureInfo.InvariantCulture)).G
 079        var prefix = filename[..1];
 80
 081        return Path.Join(keyframeCachePath, prefix, filename);
 082    }
 83
 84    private static bool TryReadFromCache(string cachePath, [NotNullWhen(true)] out KeyframeData? cachedResult)
 085    {
 086        if (File.Exists(cachePath))
 087        {
 088            var bytes = File.ReadAllBytes(cachePath);
 089            cachedResult = JsonSerializer.Deserialize<KeyframeData>(bytes, _jsonOptions);
 090            return cachedResult is not null;
 91        }
 92
 093        cachedResult = null;
 094        return false;
 095    }
 96}