aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CONTRIBUTORS.md1
-rw-r--r--Dockerfile11
-rw-r--r--Emby.Server.Implementations/Images/BaseDynamicImageProvider.cs2
-rw-r--r--Emby.Server.Implementations/Images/DynamicImageProvider.cs10
-rw-r--r--Emby.Server.Implementations/Localization/Core/en-US.json4
-rw-r--r--Emby.Server.Implementations/Session/SessionManager.cs7
-rw-r--r--Jellyfin.Api/Controllers/DisplayPreferencesController.cs6
-rw-r--r--Jellyfin.Api/Helpers/TranscodingJobHelper.cs16
-rw-r--r--MediaBrowser.Controller/Entities/Folder.cs12
-rw-r--r--MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs38
-rw-r--r--MediaBrowser.Controller/MediaEncoding/IAttachmentExtractor.cs6
-rw-r--r--MediaBrowser.Controller/Session/SessionInfo.cs6
-rw-r--r--MediaBrowser.MediaEncoding/Attachments/AttachmentExtractor.cs124
-rw-r--r--MediaBrowser.MediaEncoding/Probing/MediaChapter.cs2
-rw-r--r--src/Jellyfin.MediaEncoding.Hls/ScheduledTasks/KeyframeExtractionScheduledTask.cs4
15 files changed, 222 insertions, 27 deletions
diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md
index 1b601df2c..76b3c1dcb 100644
--- a/CONTRIBUTORS.md
+++ b/CONTRIBUTORS.md
@@ -77,6 +77,7 @@
- [mitchfizz05](https://github.com/mitchfizz05)
- [MrTimscampi](https://github.com/MrTimscampi)
- [n8225](https://github.com/n8225)
+ - [Nalsai](https://github.com/Nalsai)
- [Narfinger](https://github.com/Narfinger)
- [NathanPickard](https://github.com/NathanPickard)
- [neilsb](https://github.com/neilsb)
diff --git a/Dockerfile b/Dockerfile
index f0090889f..c3038b1d2 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -22,10 +22,10 @@ ARG APT_KEY_DONT_WARN_ON_DANGEROUS_USAGE=DontWarn
ENV NVIDIA_DRIVER_CAPABILITIES="compute,video,utility"
# https://github.com/intel/compute-runtime/releases
-ARG GMMLIB_VERSION=21.2.1
-ARG IGC_VERSION=1.0.8517
-ARG NEO_VERSION=21.35.20826
-ARG LEVEL_ZERO_VERSION=1.2.20826
+ARG GMMLIB_VERSION=22.0.2
+ARG IGC_VERSION=1.0.10395
+ARG NEO_VERSION=22.08.22549
+ARG LEVEL_ZERO_VERSION=1.3.22549
# Install dependencies:
# mesa-va-drivers: needed for AMD VAAPI. Mesa >= 20.1 is required for HEVC transcoding.
@@ -48,8 +48,7 @@ RUN apt-get update \
&& wget https://github.com/intel/compute-runtime/releases/download/${NEO_VERSION}/intel-gmmlib_${GMMLIB_VERSION}_amd64.deb \
&& wget https://github.com/intel/intel-graphics-compiler/releases/download/igc-${IGC_VERSION}/intel-igc-core_${IGC_VERSION}_amd64.deb \
&& wget https://github.com/intel/intel-graphics-compiler/releases/download/igc-${IGC_VERSION}/intel-igc-opencl_${IGC_VERSION}_amd64.deb \
- && wget https://github.com/intel/compute-runtime/releases/download/${NEO_VERSION}/intel-opencl_${NEO_VERSION}_amd64.deb \
- && wget https://github.com/intel/compute-runtime/releases/download/${NEO_VERSION}/intel-ocloc_${NEO_VERSION}_amd64.deb \
+ && wget https://github.com/intel/compute-runtime/releases/download/${NEO_VERSION}/intel-opencl-icd_${NEO_VERSION}_amd64.deb \
&& wget https://github.com/intel/compute-runtime/releases/download/${NEO_VERSION}/intel-level-zero-gpu_${LEVEL_ZERO_VERSION}_amd64.deb \
&& dpkg -i *.deb \
&& cd .. \
diff --git a/Emby.Server.Implementations/Images/BaseDynamicImageProvider.cs b/Emby.Server.Implementations/Images/BaseDynamicImageProvider.cs
index 758986945..57c2f1a5e 100644
--- a/Emby.Server.Implementations/Images/BaseDynamicImageProvider.cs
+++ b/Emby.Server.Implementations/Images/BaseDynamicImageProvider.cs
@@ -135,7 +135,7 @@ namespace Emby.Server.Implementations.Images
protected virtual IEnumerable<string> GetStripCollageImagePaths(BaseItem primaryItem, IEnumerable<BaseItem> items)
{
- var useBackdrop = primaryItem is CollectionFolder;
+ var useBackdrop = primaryItem is CollectionFolder || primaryItem is UserView;
return items
.Select(i =>
{
diff --git a/Emby.Server.Implementations/Images/DynamicImageProvider.cs b/Emby.Server.Implementations/Images/DynamicImageProvider.cs
index 575680653..9f9a4902a 100644
--- a/Emby.Server.Implementations/Images/DynamicImageProvider.cs
+++ b/Emby.Server.Implementations/Images/DynamicImageProvider.cs
@@ -84,16 +84,20 @@ namespace Emby.Server.Implementations.Images
}).GroupBy(x => x.Id)
.Select(x => x.First());
+ List<BaseItem> returnItems;
if (isUsingCollectionStrip)
{
- return items
+ returnItems = items
.Where(i => i.HasImage(ImageType.Primary) || i.HasImage(ImageType.Thumb))
.ToList();
+ returnItems.Shuffle();
+ return returnItems;
}
-
- return items
+ returnItems = items
.Where(i => i.HasImage(ImageType.Primary))
.ToList();
+ returnItems.Shuffle();
+ return returnItems;
}
protected override bool Supports(BaseItem item)
diff --git a/Emby.Server.Implementations/Localization/Core/en-US.json b/Emby.Server.Implementations/Localization/Core/en-US.json
index 568a8e447..e06f8e6fe 100644
--- a/Emby.Server.Implementations/Localization/Core/en-US.json
+++ b/Emby.Server.Implementations/Localization/Core/en-US.json
@@ -119,5 +119,7 @@
"TaskDownloadMissingSubtitles": "Download missing subtitles",
"TaskDownloadMissingSubtitlesDescription": "Searches the internet for missing subtitles based on metadata configuration.",
"TaskOptimizeDatabase": "Optimize database",
- "TaskOptimizeDatabaseDescription": "Compacts database and truncates free space. Running this task after scanning the library or doing other changes that imply database modifications might improve performance."
+ "TaskOptimizeDatabaseDescription": "Compacts database and truncates free space. Running this task after scanning the library or doing other changes that imply database modifications might improve performance.",
+ "TaskKeyframeExtractor": "Keyframe Extractor",
+ "TaskKeyframeExtractorDescription": "Extracts keyframes from video files to create more precise HLS playlists. This task may run for a long time."
}
diff --git a/Emby.Server.Implementations/Session/SessionManager.cs b/Emby.Server.Implementations/Session/SessionManager.cs
index 6c679ea20..ab860ef67 100644
--- a/Emby.Server.Implementations/Session/SessionManager.cs
+++ b/Emby.Server.Implementations/Session/SessionManager.cs
@@ -424,9 +424,14 @@ namespace Emby.Server.Implementations.Session
var nowPlayingQueue = info.NowPlayingQueue;
- if (nowPlayingQueue != null)
+ if (nowPlayingQueue?.Length > 0)
{
session.NowPlayingQueue = nowPlayingQueue;
+
+ var itemIds = nowPlayingQueue.Select(queue => queue.Id).ToArray();
+ session.NowPlayingQueueFullItems = _dtoService.GetBaseItemDtos(
+ _libraryManager.GetItemList(new InternalItemsQuery { ItemIds = itemIds }),
+ new DtoOptions(true));
}
}
diff --git a/Jellyfin.Api/Controllers/DisplayPreferencesController.cs b/Jellyfin.Api/Controllers/DisplayPreferencesController.cs
index 0b2604640..27eb22339 100644
--- a/Jellyfin.Api/Controllers/DisplayPreferencesController.cs
+++ b/Jellyfin.Api/Controllers/DisplayPreferencesController.cs
@@ -126,9 +126,11 @@ namespace Jellyfin.Api.Controllers
HomeSectionType.SmallLibraryTiles,
HomeSectionType.Resume,
HomeSectionType.ResumeAudio,
+ HomeSectionType.ResumeBook,
HomeSectionType.LiveTv,
HomeSectionType.NextUp,
- HomeSectionType.LatestMedia, HomeSectionType.None,
+ HomeSectionType.LatestMedia,
+ HomeSectionType.None,
};
if (!Guid.TryParse(displayPreferencesId, out var itemId))
@@ -182,7 +184,7 @@ namespace Jellyfin.Api.Controllers
var order = int.Parse(key.AsSpan().Slice("homesection".Length));
if (!Enum.TryParse<HomeSectionType>(displayPreferences.CustomPrefs[key], true, out var type))
{
- type = order < 7 ? defaults[order] : HomeSectionType.None;
+ type = order < 8 ? defaults[order] : HomeSectionType.None;
}
displayPreferences.CustomPrefs.Remove(key);
diff --git a/Jellyfin.Api/Helpers/TranscodingJobHelper.cs b/Jellyfin.Api/Helpers/TranscodingJobHelper.cs
index 56a54fb1d..e70b79837 100644
--- a/Jellyfin.Api/Helpers/TranscodingJobHelper.cs
+++ b/Jellyfin.Api/Helpers/TranscodingJobHelper.cs
@@ -18,6 +18,7 @@ using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.MediaEncoding;
using MediaBrowser.Controller.Net;
using MediaBrowser.Controller.Session;
+using MediaBrowser.Model.Dlna;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.IO;
using MediaBrowser.Model.MediaInfo;
@@ -42,6 +43,8 @@ namespace Jellyfin.Api.Helpers
/// </summary>
private static readonly Dictionary<string, SemaphoreSlim> _transcodingLocks = new Dictionary<string, SemaphoreSlim>();
+ private readonly IAttachmentExtractor _attachmentExtractor;
+ private readonly IApplicationPaths _appPaths;
private readonly IAuthorizationContext _authorizationContext;
private readonly EncodingHelper _encodingHelper;
private readonly IFileSystem _fileSystem;
@@ -55,6 +58,8 @@ namespace Jellyfin.Api.Helpers
/// <summary>
/// Initializes a new instance of the <see cref="TranscodingJobHelper"/> class.
/// </summary>
+ /// <param name="attachmentExtractor">Instance of the <see cref="IAttachmentExtractor"/> interface.</param>
+ /// <param name="appPaths">Instance of the <see cref="IApplicationPaths"/> interface.</param>
/// <param name="logger">Instance of the <see cref="ILogger{TranscodingJobHelpers}"/> interface.</param>
/// <param name="mediaSourceManager">Instance of the <see cref="IMediaSourceManager"/> interface.</param>
/// <param name="fileSystem">Instance of the <see cref="IFileSystem"/> interface.</param>
@@ -65,6 +70,8 @@ namespace Jellyfin.Api.Helpers
/// <param name="encodingHelper">Instance of <see cref="EncodingHelper"/>.</param>
/// <param name="loggerFactory">Instance of the <see cref="ILoggerFactory"/> interface.</param>
public TranscodingJobHelper(
+ IAttachmentExtractor attachmentExtractor,
+ IApplicationPaths appPaths,
ILogger<TranscodingJobHelper> logger,
IMediaSourceManager mediaSourceManager,
IFileSystem fileSystem,
@@ -75,6 +82,8 @@ namespace Jellyfin.Api.Helpers
EncodingHelper encodingHelper,
ILoggerFactory loggerFactory)
{
+ _attachmentExtractor = attachmentExtractor;
+ _appPaths = appPaths;
_logger = logger;
_mediaSourceManager = mediaSourceManager;
_fileSystem = fileSystem;
@@ -513,6 +522,13 @@ namespace Jellyfin.Api.Helpers
throw new ArgumentException("FFmpeg path not set.");
}
+ // If subtitles get burned in fonts may need to be extracted from the media file
+ if (state.SubtitleStream != null && state.SubtitleDeliveryMethod == SubtitleDeliveryMethod.Encode)
+ {
+ var attachmentPath = Path.Combine(_appPaths.CachePath, "attachments", state.MediaSource.Id);
+ await _attachmentExtractor.ExtractAllAttachments(state.MediaPath, state.MediaSource, attachmentPath, CancellationToken.None).ConfigureAwait(false);
+ }
+
var process = new Process
{
StartInfo = new ProcessStartInfo
diff --git a/MediaBrowser.Controller/Entities/Folder.cs b/MediaBrowser.Controller/Entities/Folder.cs
index 4d9aac6f9..cb5ff6eec 100644
--- a/MediaBrowser.Controller/Entities/Folder.cs
+++ b/MediaBrowser.Controller/Entities/Folder.cs
@@ -848,6 +848,18 @@ namespace MediaBrowser.Controller.Entities
return true;
}
+ if (query.HasThemeSong.HasValue)
+ {
+ Logger.LogDebug("Query requires post-filtering due to HasThemeSong");
+ return true;
+ }
+
+ if (query.HasThemeVideo.HasValue)
+ {
+ Logger.LogDebug("Query requires post-filtering due to HasThemeVideo");
+ return true;
+ }
+
// Filter by VideoType
if (query.VideoTypes.Length > 0)
{
diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs
index cdfde97f6..931337bf4 100644
--- a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs
+++ b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs
@@ -12,6 +12,7 @@ using System.Text.RegularExpressions;
using System.Threading;
using Jellyfin.Data.Enums;
using Jellyfin.Extensions;
+using MediaBrowser.Common.Configuration;
using MediaBrowser.Model.Configuration;
using MediaBrowser.Model.Dlna;
using MediaBrowser.Model.Dto;
@@ -28,7 +29,7 @@ namespace MediaBrowser.Controller.MediaEncoding
private const string VideotoolboxAlias = "vt";
private const string OpenclAlias = "ocl";
private const string CudaAlias = "cu";
-
+ private readonly IApplicationPaths _appPaths;
private readonly IMediaEncoder _mediaEncoder;
private readonly ISubtitleEncoder _subtitleEncoder;
@@ -51,9 +52,11 @@ namespace MediaBrowser.Controller.MediaEncoding
};
public EncodingHelper(
+ IApplicationPaths appPaths,
IMediaEncoder mediaEncoder,
ISubtitleEncoder subtitleEncoder)
{
+ _appPaths = appPaths;
_mediaEncoder = mediaEncoder;
_subtitleEncoder = subtitleEncoder;
}
@@ -1080,6 +1083,12 @@ namespace MediaBrowser.Controller.MediaEncoding
var alphaParam = enableAlpha ? ":alpha=1" : string.Empty;
var sub2videoParam = enableSub2video ? ":sub2video=1" : string.Empty;
+ var fontPath = Path.Combine(_appPaths.CachePath, "attachments", state.MediaSource.Id);
+ var fontParam = string.Format(
+ CultureInfo.InvariantCulture,
+ ":fontsdir={0}",
+ _mediaEncoder.EscapeSubtitleFilterPath(fontPath));
+
// TODO
// var fallbackFontPath = Path.Combine(_appPaths.ProgramDataPath, "fonts", "DroidSansFallback.ttf");
// string fallbackFontParam = string.Empty;
@@ -1120,11 +1129,12 @@ namespace MediaBrowser.Controller.MediaEncoding
// TODO: Perhaps also use original_size=1920x800 ??
return string.Format(
CultureInfo.InvariantCulture,
- "subtitles=f='{0}'{1}{2}{3}{4}",
+ "subtitles=f='{0}'{1}{2}{3}{4}{5}",
_mediaEncoder.EscapeSubtitleFilterPath(subtitlePath),
charsetParam,
alphaParam,
sub2videoParam,
+ fontParam,
// fallbackFontParam,
setPtsParam);
}
@@ -1133,11 +1143,12 @@ namespace MediaBrowser.Controller.MediaEncoding
return string.Format(
CultureInfo.InvariantCulture,
- "subtitles='{0}:si={1}{2}{3}'{4}",
+ "subtitles='{0}:si={1}{2}{3}{4}'{5}",
_mediaEncoder.EscapeSubtitleFilterPath(mediaPath),
state.InternalSubtitleStreamOffset.ToString(CultureInfo.InvariantCulture),
alphaParam,
sub2videoParam,
+ fontParam,
// fallbackFontParam,
setPtsParam);
}
@@ -1338,29 +1349,37 @@ namespace MediaBrowser.Controller.MediaEncoding
switch (encodingOptions.EncoderPreset)
{
case "veryslow":
-
- param += " -preset slow"; // lossless is only supported on maxwell and newer(2014+)
+ param += " -preset p7";
break;
case "slow":
+ param += " -preset p6";
+ break;
+
case "slower":
- param += " -preset slow";
+ param += " -preset p5";
break;
case "medium":
- param += " -preset medium";
+ param += " -preset p4";
break;
case "fast":
+ param += " -preset p3";
+ break;
+
case "faster":
+ param += " -preset p2";
+ break;
+
case "veryfast":
case "superfast":
case "ultrafast":
- param += " -preset fast";
+ param += " -preset p1";
break;
default:
- param += " -preset default";
+ param += " -preset p4";
break;
}
}
@@ -2162,6 +2181,7 @@ namespace MediaBrowser.Controller.MediaEncoding
// Important: If this is ever re-enabled, make sure not to use it with wtv because it breaks seeking
if (!string.Equals(state.InputContainer, "wtv", StringComparison.OrdinalIgnoreCase)
&& state.TranscodingType != TranscodingJobType.Progressive
+ && state.TranscodingType != TranscodingJobType.Hls
&& !state.EnableBreakOnNonKeyFrames(outputVideoCodec)
&& (state.BaseRequest.StartTimeTicks ?? 0) > 0)
{
diff --git a/MediaBrowser.Controller/MediaEncoding/IAttachmentExtractor.cs b/MediaBrowser.Controller/MediaEncoding/IAttachmentExtractor.cs
index 4e7e26624..a2b6be1e6 100644
--- a/MediaBrowser.Controller/MediaEncoding/IAttachmentExtractor.cs
+++ b/MediaBrowser.Controller/MediaEncoding/IAttachmentExtractor.cs
@@ -6,6 +6,7 @@ using System.IO;
using System.Threading;
using System.Threading.Tasks;
using MediaBrowser.Controller.Entities;
+using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Entities;
namespace MediaBrowser.Controller.MediaEncoding
@@ -17,5 +18,10 @@ namespace MediaBrowser.Controller.MediaEncoding
string mediaSourceId,
int attachmentStreamIndex,
CancellationToken cancellationToken);
+ Task ExtractAllAttachments(
+ string inputFile,
+ MediaSourceInfo mediaSource,
+ string outputPath,
+ CancellationToken cancellationToken);
}
}
diff --git a/MediaBrowser.Controller/Session/SessionInfo.cs b/MediaBrowser.Controller/Session/SessionInfo.cs
index 6134c0cf3..c2ca23386 100644
--- a/MediaBrowser.Controller/Session/SessionInfo.cs
+++ b/MediaBrowser.Controller/Session/SessionInfo.cs
@@ -39,6 +39,8 @@ namespace MediaBrowser.Controller.Session
AdditionalUsers = Array.Empty<SessionUserInfo>();
PlayState = new PlayerStateInfo();
SessionControllers = Array.Empty<ISessionController>();
+ NowPlayingQueue = Array.Empty<QueueItem>();
+ NowPlayingQueueFullItems = Array.Empty<BaseItemDto>();
}
public PlayerStateInfo PlayState { get; set; }
@@ -219,7 +221,9 @@ namespace MediaBrowser.Controller.Session
}
}
- public QueueItem[] NowPlayingQueue { get; set; }
+ public IReadOnlyList<QueueItem> NowPlayingQueue { get; set; }
+
+ public IReadOnlyList<BaseItemDto> NowPlayingQueueFullItems { get; set; }
public bool HasCustomDeviceName { get; set; }
diff --git a/MediaBrowser.MediaEncoding/Attachments/AttachmentExtractor.cs b/MediaBrowser.MediaEncoding/Attachments/AttachmentExtractor.cs
index 3fd4cd731..06d20d90e 100644
--- a/MediaBrowser.MediaEncoding/Attachments/AttachmentExtractor.cs
+++ b/MediaBrowser.MediaEncoding/Attachments/AttachmentExtractor.cs
@@ -83,6 +83,130 @@ namespace MediaBrowser.MediaEncoding.Attachments
return (mediaAttachment, attachmentStream);
}
+ public async Task ExtractAllAttachments(
+ string inputFile,
+ MediaSourceInfo mediaSource,
+ string outputPath,
+ CancellationToken cancellationToken)
+ {
+ var semaphore = _semaphoreLocks.GetOrAdd(outputPath, key => new SemaphoreSlim(1, 1));
+
+ await semaphore.WaitAsync(cancellationToken).ConfigureAwait(false);
+
+ try
+ {
+ if (!Directory.Exists(outputPath))
+ {
+ await ExtractAllAttachmentsInternal(
+ _mediaEncoder.GetInputArgument(inputFile, mediaSource),
+ outputPath,
+ cancellationToken).ConfigureAwait(false);
+ }
+ }
+ finally
+ {
+ semaphore.Release();
+ }
+ }
+
+ private async Task ExtractAllAttachmentsInternal(
+ string inputPath,
+ string outputPath,
+ CancellationToken cancellationToken)
+ {
+ if (string.IsNullOrEmpty(inputPath))
+ {
+ throw new ArgumentNullException(nameof(inputPath));
+ }
+
+ if (string.IsNullOrEmpty(outputPath))
+ {
+ throw new ArgumentNullException(nameof(outputPath));
+ }
+
+ Directory.CreateDirectory(outputPath);
+
+ var processArgs = string.Format(
+ CultureInfo.InvariantCulture,
+ "-dump_attachment:t \"\" -i {0} -t 0 -f null null",
+ inputPath);
+
+ int exitCode;
+
+ using (var process = new Process
+ {
+ StartInfo = new ProcessStartInfo
+ {
+ Arguments = processArgs,
+ FileName = _mediaEncoder.EncoderPath,
+ UseShellExecute = false,
+ CreateNoWindow = true,
+ WindowStyle = ProcessWindowStyle.Hidden,
+ WorkingDirectory = outputPath,
+ ErrorDialog = false
+ },
+ EnableRaisingEvents = true
+ })
+ {
+ _logger.LogInformation("{File} {Arguments}", process.StartInfo.FileName, process.StartInfo.Arguments);
+
+ process.Start();
+
+ var ranToCompletion = await ProcessExtensions.WaitForExitAsync(process, cancellationToken).ConfigureAwait(false);
+
+ if (!ranToCompletion)
+ {
+ try
+ {
+ _logger.LogWarning("Killing ffmpeg attachment extraction process");
+ process.Kill();
+ }
+ catch (Exception ex)
+ {
+ _logger.LogError(ex, "Error killing attachment extraction process");
+ }
+ }
+
+ exitCode = ranToCompletion ? process.ExitCode : -1;
+ }
+
+ var failed = false;
+
+ if (exitCode != 0)
+ {
+ failed = true;
+
+ _logger.LogWarning("Deleting extracted attachments {Path} due to failure: {ExitCode}", outputPath, exitCode);
+ try
+ {
+ if (Directory.Exists(outputPath))
+ {
+ Directory.Delete(outputPath);
+ }
+ }
+ catch (IOException ex)
+ {
+ _logger.LogError(ex, "Error deleting extracted attachments {Path}", outputPath);
+ }
+ }
+ else if (!Directory.Exists(outputPath))
+ {
+ failed = true;
+ }
+
+ if (failed)
+ {
+ _logger.LogError("ffmpeg attachment extraction failed for {InputPath} to {OutputPath}", inputPath, outputPath);
+
+ throw new InvalidOperationException(
+ string.Format(CultureInfo.InvariantCulture, "ffmpeg attachment extraction failed for {0} to {1}", inputPath, outputPath));
+ }
+ else
+ {
+ _logger.LogInformation("ffmpeg attachment extraction completed for {Path} to {Path}", inputPath, outputPath);
+ }
+ }
+
private async Task<Stream> GetAttachmentStream(
MediaSourceInfo mediaSource,
MediaAttachment mediaAttachment,
diff --git a/MediaBrowser.MediaEncoding/Probing/MediaChapter.cs b/MediaBrowser.MediaEncoding/Probing/MediaChapter.cs
index a1cef7a9f..c9e948780 100644
--- a/MediaBrowser.MediaEncoding/Probing/MediaChapter.cs
+++ b/MediaBrowser.MediaEncoding/Probing/MediaChapter.cs
@@ -12,7 +12,7 @@ namespace MediaBrowser.MediaEncoding.Probing
public class MediaChapter
{
[JsonPropertyName("id")]
- public int Id { get; set; }
+ public long Id { get; set; }
[JsonPropertyName("time_base")]
public string TimeBase { get; set; }
diff --git a/src/Jellyfin.MediaEncoding.Hls/ScheduledTasks/KeyframeExtractionScheduledTask.cs b/src/Jellyfin.MediaEncoding.Hls/ScheduledTasks/KeyframeExtractionScheduledTask.cs
index d80925fc9..a7d52184b 100644
--- a/src/Jellyfin.MediaEncoding.Hls/ScheduledTasks/KeyframeExtractionScheduledTask.cs
+++ b/src/Jellyfin.MediaEncoding.Hls/ScheduledTasks/KeyframeExtractionScheduledTask.cs
@@ -39,13 +39,13 @@ public class KeyframeExtractionScheduledTask : IScheduledTask
}
/// <inheritdoc />
- public string Name => "Keyframe Extractor";
+ public string Name => _localizationManager.GetLocalizedString("TaskKeyframeExtractor");
/// <inheritdoc />
public string Key => "KeyframeExtraction";
/// <inheritdoc />
- public string Description => "Extracts keyframes from video files to create more precise HLS playlists. This task may run for a long time.";
+ public string Description => _localizationManager.GetLocalizedString("TaskKeyframeExtractorDescription");
/// <inheritdoc />
public string Category => _localizationManager.GetLocalizedString("TasksLibraryCategory");