diff --git a/src/Jellyfin.MediaEncoding.Hls/Cache/CacheDecorator.cs b/src/Jellyfin.MediaEncoding.Hls/Cache/CacheDecorator.cs index 5348360f..42bdedd2 100644 --- a/src/Jellyfin.MediaEncoding.Hls/Cache/CacheDecorator.cs +++ b/src/Jellyfin.MediaEncoding.Hls/Cache/CacheDecorator.cs @@ -16,7 +16,7 @@ using Microsoft.Extensions.Logging; namespace Jellyfin.MediaEncoding.Hls.Cache; /// -public class CacheDecorator : IKeyframeExtractor +public partial class CacheDecorator : IKeyframeExtractor { private readonly IKeyframeRepository _keyframeRepository; private readonly IKeyframeExtractor _keyframeExtractor; @@ -58,11 +58,11 @@ public class CacheDecorator : IKeyframeExtractor { if (!_keyframeExtractor.TryExtractKeyframes(itemId, filePath, out var result)) { - _logger.LogDebug("Failed to extract keyframes using {ExtractorName}", _keyframeExtractorName); + LogKeyframeExtractionFailed(_logger, _keyframeExtractorName); return false; } - _logger.LogDebug("Successfully extracted keyframes using {ExtractorName}", _keyframeExtractorName); + LogKeyframeExtractionSucceeded(_logger, _keyframeExtractorName); keyframeData = result; _keyframeRepository.SaveKeyframeDataAsync(itemId, keyframeData, CancellationToken.None) .GetAwaiter() @@ -71,4 +71,10 @@ public class CacheDecorator : IKeyframeExtractor return true; } + + [LoggerMessage(Level = LogLevel.Debug, Message = "Failed to extract keyframes using {ExtractorName}")] + private static partial void LogKeyframeExtractionFailed(ILogger logger, string extractorName); + + [LoggerMessage(Level = LogLevel.Debug, Message = "Successfully extracted keyframes using {ExtractorName}")] + private static partial void LogKeyframeExtractionSucceeded(ILogger logger, string extractorName); } diff --git a/src/Jellyfin.MediaEncoding.Hls/Extractors/FfProbeKeyframeExtractor.cs b/src/Jellyfin.MediaEncoding.Hls/Extractors/FfProbeKeyframeExtractor.cs index 47cef8d2..cb0b3316 100644 --- a/src/Jellyfin.MediaEncoding.Hls/Extractors/FfProbeKeyframeExtractor.cs +++ b/src/Jellyfin.MediaEncoding.Hls/Extractors/FfProbeKeyframeExtractor.cs @@ -15,7 +15,7 @@ using Microsoft.Extensions.Logging; using Extractor = Jellyfin.MediaEncoding.Keyframes.FfProbe.FfProbeKeyframeExtractor; /// -public class FfProbeKeyframeExtractor : IKeyframeExtractor +public partial class FfProbeKeyframeExtractor : IKeyframeExtractor { private readonly IMediaEncoder _mediaEncoder; private readonly NamingOptions _namingOptions; @@ -51,12 +51,27 @@ public class FfProbeKeyframeExtractor : IKeyframeExtractor keyframeData = Extractor.GetKeyframeData(_mediaEncoder.ProbePath, filePath); return keyframeData.KeyframeTicks.Count > 0; } - catch (Exception ex) + catch (FileNotFoundException ex) { - _logger.LogError(ex, "Extracting keyframes from {FilePath} using ffprobe failed", filePath); + LogKeyframeExtractionError(_logger, filePath, ex); + } + catch (UnauthorizedAccessException ex) + { + LogKeyframeExtractionError(_logger, filePath, ex); + } + catch (IOException ex) + { + LogKeyframeExtractionError(_logger, filePath, ex); + } + catch (InvalidDataException ex) + { + LogKeyframeExtractionError(_logger, filePath, ex); } keyframeData = null; return false; } + + [LoggerMessage(Level = LogLevel.Error, Message = "Extracting keyframes from {FilePath} using ffprobe failed")] + private static partial void LogKeyframeExtractionError(ILogger logger, string filePath, Exception exception); } diff --git a/src/Jellyfin.MediaEncoding.Hls/Extractors/MatroskaKeyframeExtractor.cs b/src/Jellyfin.MediaEncoding.Hls/Extractors/MatroskaKeyframeExtractor.cs index 0726aa99..515ee344 100644 --- a/src/Jellyfin.MediaEncoding.Hls/Extractors/MatroskaKeyframeExtractor.cs +++ b/src/Jellyfin.MediaEncoding.Hls/Extractors/MatroskaKeyframeExtractor.cs @@ -6,12 +6,13 @@ namespace Jellyfin.MediaEncoding.Hls.Extractors; using System; using System.Diagnostics.CodeAnalysis; +using System.IO; using Jellyfin.MediaEncoding.Keyframes; using Microsoft.Extensions.Logging; using Extractor = Jellyfin.MediaEncoding.Keyframes.Matroska.MatroskaKeyframeExtractor; /// -public class MatroskaKeyframeExtractor : IKeyframeExtractor +public partial class MatroskaKeyframeExtractor : IKeyframeExtractor { private readonly ILogger _logger; @@ -41,12 +42,27 @@ public class MatroskaKeyframeExtractor : IKeyframeExtractor keyframeData = Extractor.GetKeyframeData(filePath); return keyframeData.KeyframeTicks.Count > 0; } - catch (Exception ex) + catch (FileNotFoundException ex) { - _logger.LogError(ex, "Extracting keyframes from {FilePath} using matroska metadata failed", filePath); + LogKeyframeExtractionError(_logger, filePath, ex); + } + catch (UnauthorizedAccessException ex) + { + LogKeyframeExtractionError(_logger, filePath, ex); + } + catch (IOException ex) + { + LogKeyframeExtractionError(_logger, filePath, ex); + } + catch (InvalidDataException ex) + { + LogKeyframeExtractionError(_logger, filePath, ex); } keyframeData = null; return false; } + + [LoggerMessage(Level = LogLevel.Error, Message = "Extracting keyframes from {FilePath} using matroska metadata failed")] + private static partial void LogKeyframeExtractionError(ILogger logger, string filePath, Exception exception); } diff --git a/src/Jellyfin.MediaEncoding.Hls/Playlist/DynamicHlsPlaylistGenerator.cs b/src/Jellyfin.MediaEncoding.Hls/Playlist/DynamicHlsPlaylistGenerator.cs index 67ab3cd6..00d691a4 100644 --- a/src/Jellyfin.MediaEncoding.Hls/Playlist/DynamicHlsPlaylistGenerator.cs +++ b/src/Jellyfin.MediaEncoding.Hls/Playlist/DynamicHlsPlaylistGenerator.cs @@ -37,6 +37,8 @@ public class DynamicHlsPlaylistGenerator : IDynamicHlsPlaylistGenerator /// public string CreateMainPlaylist(CreateMainPlaylistRequest request) { + ArgumentNullException.ThrowIfNull(request); + IReadOnlyList segments; // For video transcodes it is sufficient with equal length segments as ffmpeg will create new keyframes if (request.IsRemuxingVideo diff --git a/src/Jellyfin.MediaEncoding.Hls/ScheduledTasks/KeyframeExtractionScheduledTask.cs b/src/Jellyfin.MediaEncoding.Hls/ScheduledTasks/KeyframeExtractionScheduledTask.cs index 68fab1da..027a818f 100644 --- a/src/Jellyfin.MediaEncoding.Hls/ScheduledTasks/KeyframeExtractionScheduledTask.cs +++ b/src/Jellyfin.MediaEncoding.Hls/ScheduledTasks/KeyframeExtractionScheduledTask.cs @@ -56,6 +56,8 @@ public class KeyframeExtractionScheduledTask : IScheduledTask /// public Task ExecuteAsync(IProgress progress, CancellationToken cancellationToken) { + ArgumentNullException.ThrowIfNull(progress); + var query = new InternalItemsQuery { MediaTypes = [MediaType.Video], diff --git a/src/Jellyfin.MediaEncoding.Keyframes/FfProbe/FfProbeKeyframeExtractor.cs b/src/Jellyfin.MediaEncoding.Keyframes/FfProbe/FfProbeKeyframeExtractor.cs index 5975aebd..6c2cffbf 100644 --- a/src/Jellyfin.MediaEncoding.Keyframes/FfProbe/FfProbeKeyframeExtractor.cs +++ b/src/Jellyfin.MediaEncoding.Keyframes/FfProbe/FfProbeKeyframeExtractor.cs @@ -50,10 +50,13 @@ public static class FfProbeKeyframeExtractor { process.PriorityClass = ProcessPriorityClass.BelowNormal; } - catch + catch (InvalidOperationException) { - // We do not care if process priority setting fails - // Ideally log a warning but this does not have a logger available + // Process may have already exited - ignore + } + catch (System.ComponentModel.Win32Exception) + { + // Priority setting may not be supported on this platform - ignore } return ParseStream(process.StandardOutput); @@ -67,9 +70,13 @@ public static class FfProbeKeyframeExtractor process.Kill(); } } - catch + catch (InvalidOperationException) { - // We do not care if this fails + // Process may have already exited - ignore + } + catch (System.ComponentModel.Win32Exception) + { + // Process termination may fail - ignore } throw;