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;