Merge pull request #13847 from Shadowghost/rework-chapter-management

Rework chapter management
This commit is contained in:
Tim Eisele 2025-04-26 14:01:12 +02:00 committed by GitHub
parent f35b8dd33d
commit df5671263f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
15 changed files with 447 additions and 395 deletions

View File

@ -15,6 +15,7 @@ using System.Security.Cryptography.X509Certificates;
using System.Threading.Tasks; using System.Threading.Tasks;
using Emby.Naming.Common; using Emby.Naming.Common;
using Emby.Photos; using Emby.Photos;
using Emby.Server.Implementations.Chapters;
using Emby.Server.Implementations.Collections; using Emby.Server.Implementations.Collections;
using Emby.Server.Implementations.Configuration; using Emby.Server.Implementations.Configuration;
using Emby.Server.Implementations.Cryptography; using Emby.Server.Implementations.Cryptography;
@ -552,7 +553,7 @@ namespace Emby.Server.Implementations
serviceCollection.AddSingleton<IUserViewManager, UserViewManager>(); serviceCollection.AddSingleton<IUserViewManager, UserViewManager>();
serviceCollection.AddSingleton<IEncodingManager, MediaEncoder.EncodingManager>(); serviceCollection.AddSingleton<IChapterManager, ChapterManager>();
serviceCollection.AddSingleton<IAuthService, AuthService>(); serviceCollection.AddSingleton<IAuthService, AuthService>();
serviceCollection.AddSingleton<IQuickConnect, QuickConnectManager>(); serviceCollection.AddSingleton<IQuickConnect, QuickConnectManager>();
@ -647,7 +648,7 @@ namespace Emby.Server.Implementations
BaseItem.ProviderManager = Resolve<IProviderManager>(); BaseItem.ProviderManager = Resolve<IProviderManager>();
BaseItem.LocalizationManager = Resolve<ILocalizationManager>(); BaseItem.LocalizationManager = Resolve<ILocalizationManager>();
BaseItem.ItemRepository = Resolve<IItemRepository>(); BaseItem.ItemRepository = Resolve<IItemRepository>();
BaseItem.ChapterRepository = Resolve<IChapterRepository>(); BaseItem.ChapterManager = Resolve<IChapterManager>();
BaseItem.FileSystem = Resolve<IFileSystem>(); BaseItem.FileSystem = Resolve<IFileSystem>();
BaseItem.UserDataManager = Resolve<IUserDataManager>(); BaseItem.UserDataManager = Resolve<IUserDataManager>();
BaseItem.ChannelManager = Resolve<IChannelManager>(); BaseItem.ChannelManager = Resolve<IChannelManager>();

View File

@ -0,0 +1,313 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Jellyfin.Extensions;
using MediaBrowser.Controller.Chapters;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.IO;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.MediaEncoding;
using MediaBrowser.Controller.Persistence;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Configuration;
using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.IO;
using MediaBrowser.Model.MediaInfo;
using Microsoft.Extensions.Logging;
namespace Emby.Server.Implementations.Chapters;
/// <summary>
/// The chapter manager.
/// </summary>
public class ChapterManager : IChapterManager
{
private readonly IFileSystem _fileSystem;
private readonly ILogger<ChapterManager> _logger;
private readonly IMediaEncoder _encoder;
private readonly IChapterRepository _chapterRepository;
private readonly ILibraryManager _libraryManager;
private readonly IPathManager _pathManager;
/// <summary>
/// The first chapter ticks.
/// </summary>
private static readonly long _firstChapterTicks = TimeSpan.FromSeconds(15).Ticks;
/// <summary>
/// Initializes a new instance of the <see cref="ChapterManager"/> class.
/// </summary>
/// <param name="logger">The <see cref="ILogger{ChapterManager}"/>.</param>
/// <param name="fileSystem">The <see cref="IFileSystem"/>.</param>
/// <param name="encoder">The <see cref="IMediaEncoder"/>.</param>
/// <param name="chapterRepository">The <see cref="IChapterRepository"/>.</param>
/// <param name="libraryManager">The <see cref="ILibraryManager"/>.</param>
/// <param name="pathManager">The <see cref="IPathManager"/>.</param>
public ChapterManager(
ILogger<ChapterManager> logger,
IFileSystem fileSystem,
IMediaEncoder encoder,
IChapterRepository chapterRepository,
ILibraryManager libraryManager,
IPathManager pathManager)
{
_logger = logger;
_fileSystem = fileSystem;
_encoder = encoder;
_chapterRepository = chapterRepository;
_libraryManager = libraryManager;
_pathManager = pathManager;
}
/// <summary>
/// Determines whether [is eligible for chapter image extraction] [the specified video].
/// </summary>
/// <param name="video">The video.</param>
/// <param name="libraryOptions">The library options for the video.</param>
/// <returns><c>true</c> if [is eligible for chapter image extraction] [the specified video]; otherwise, <c>false</c>.</returns>
private bool IsEligibleForChapterImageExtraction(Video video, LibraryOptions libraryOptions)
{
if (video.IsPlaceHolder)
{
return false;
}
if (libraryOptions is null || !libraryOptions.EnableChapterImageExtraction)
{
return false;
}
if (video.IsShortcut)
{
return false;
}
if (!video.IsCompleteMedia)
{
return false;
}
// Can't extract images if there are no video streams
return video.DefaultVideoStreamIndex.HasValue;
}
private long GetAverageDurationBetweenChapters(IReadOnlyList<ChapterInfo> chapters)
{
if (chapters.Count < 2)
{
return 0;
}
long sum = 0;
for (int i = 1; i < chapters.Count; i++)
{
sum += chapters[i].StartPositionTicks - chapters[i - 1].StartPositionTicks;
}
return sum / chapters.Count;
}
/// <inheritdoc />
public async Task<bool> RefreshChapterImages(Video video, IDirectoryService directoryService, IReadOnlyList<ChapterInfo> chapters, bool extractImages, bool saveChapters, CancellationToken cancellationToken)
{
if (chapters.Count == 0)
{
return true;
}
var libraryOptions = _libraryManager.GetLibraryOptions(video);
if (!IsEligibleForChapterImageExtraction(video, libraryOptions))
{
extractImages = false;
}
var averageChapterDuration = GetAverageDurationBetweenChapters(chapters);
var threshold = TimeSpan.FromSeconds(1).Ticks;
if (averageChapterDuration < threshold)
{
_logger.LogInformation("Skipping chapter image extraction for {Video} as the average chapter duration {AverageDuration} was lower than the minimum threshold {Threshold}", video.Name, averageChapterDuration, threshold);
extractImages = false;
}
var success = true;
var changesMade = false;
var runtimeTicks = video.RunTimeTicks ?? 0;
var currentImages = GetSavedChapterImages(video, directoryService);
foreach (var chapter in chapters)
{
if (chapter.StartPositionTicks >= runtimeTicks)
{
_logger.LogInformation("Stopping chapter extraction for {0} because a chapter was found with a position greater than the runtime.", video.Name);
break;
}
var path = _pathManager.GetChapterImagePath(video, chapter.StartPositionTicks);
if (!currentImages.Contains(path, StringComparison.OrdinalIgnoreCase))
{
if (extractImages)
{
cancellationToken.ThrowIfCancellationRequested();
try
{
// Add some time for the first chapter to make sure we don't end up with a black image
var time = chapter.StartPositionTicks == 0 ? TimeSpan.FromTicks(Math.Min(_firstChapterTicks, video.RunTimeTicks ?? 0)) : TimeSpan.FromTicks(chapter.StartPositionTicks);
var inputPath = video.Path;
var directoryPath = Path.GetDirectoryName(path);
if (!string.IsNullOrEmpty(directoryPath))
{
Directory.CreateDirectory(directoryPath);
}
var container = video.Container;
var mediaSource = new MediaSourceInfo
{
VideoType = video.VideoType,
IsoType = video.IsoType,
Protocol = video.PathProtocol ?? MediaProtocol.File,
};
_logger.LogInformation("Extracting chapter image for {Name} at {Path}", video.Name, inputPath);
var tempFile = await _encoder.ExtractVideoImage(inputPath, container, mediaSource, video.GetDefaultVideoStream(), video.Video3DFormat, time, cancellationToken).ConfigureAwait(false);
File.Copy(tempFile, path, true);
try
{
_fileSystem.DeleteFile(tempFile);
}
catch (IOException ex)
{
_logger.LogError(ex, "Error deleting temporary chapter image encoding file {Path}", tempFile);
}
chapter.ImagePath = path;
chapter.ImageDateModified = _fileSystem.GetLastWriteTimeUtc(path);
changesMade = true;
}
catch (Exception ex)
{
_logger.LogError(ex, "Error extracting chapter images for {0}", string.Join(',', video.Path));
success = false;
break;
}
}
else if (!string.IsNullOrEmpty(chapter.ImagePath))
{
chapter.ImagePath = null;
changesMade = true;
}
}
else if (!string.Equals(path, chapter.ImagePath, StringComparison.OrdinalIgnoreCase))
{
chapter.ImagePath = path;
chapter.ImageDateModified = _fileSystem.GetLastWriteTimeUtc(path);
changesMade = true;
}
else if (libraryOptions?.EnableChapterImageExtraction != true)
{
// We have an image for the current chapter but the user has disabled chapter image extraction -> delete this chapter's image
chapter.ImagePath = null;
changesMade = true;
}
}
if (saveChapters && changesMade)
{
_chapterRepository.SaveChapters(video.Id, chapters);
}
DeleteDeadImages(currentImages, chapters);
return success;
}
/// <inheritdoc />
public void SaveChapters(Video video, IReadOnlyList<ChapterInfo> chapters)
{
_chapterRepository.SaveChapters(video.Id, chapters);
}
/// <inheritdoc />
public ChapterInfo? GetChapter(Guid baseItemId, int index)
{
return _chapterRepository.GetChapter(baseItemId, index);
}
/// <inheritdoc />
public IReadOnlyList<ChapterInfo> GetChapters(Guid baseItemId)
{
return _chapterRepository.GetChapters(baseItemId);
}
/// <inheritdoc />
public void DeleteChapterImages(Video video)
{
var path = _pathManager.GetChapterImageFolderPath(video);
try
{
if (Directory.Exists(path))
{
_logger.LogInformation("Removing chapter images for {Name} [{Id}]", video.Name, video.Id);
Directory.Delete(path, true);
}
}
catch (Exception ex)
{
_logger.LogWarning("Failed to remove chapter image folder for {Item}: {Exception}", video.Id, ex);
}
_chapterRepository.DeleteChapters(video.Id);
}
private IReadOnlyList<string> GetSavedChapterImages(Video video, IDirectoryService directoryService)
{
var path = _pathManager.GetChapterImageFolderPath(video);
if (!Directory.Exists(path))
{
return [];
}
try
{
return directoryService.GetFilePaths(path);
}
catch (IOException)
{
return [];
}
}
private void DeleteDeadImages(IEnumerable<string> images, IEnumerable<ChapterInfo> chapters)
{
var existingImages = chapters.Select(i => i.ImagePath).Where(i => !string.IsNullOrEmpty(i));
var deadImages = images
.Except(existingImages, StringComparer.OrdinalIgnoreCase)
.Where(i => BaseItem.SupportedImageExtensions.Contains(Path.GetExtension(i.AsSpan()), StringComparison.OrdinalIgnoreCase))
.ToList();
foreach (var image in deadImages)
{
_logger.LogDebug("Deleting dead chapter image {Path}", image);
try
{
_fileSystem.DeleteFile(image!);
}
catch (IOException ex)
{
_logger.LogError(ex, "Error deleting {Path}.", image);
}
}
}
}

View File

@ -17,7 +17,6 @@ using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.Audio; using MediaBrowser.Controller.Entities.Audio;
using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.LiveTv; using MediaBrowser.Controller.LiveTv;
using MediaBrowser.Controller.Persistence;
using MediaBrowser.Controller.Playlists; using MediaBrowser.Controller.Playlists;
using MediaBrowser.Controller.Providers; using MediaBrowser.Controller.Providers;
using MediaBrowser.Controller.Trickplay; using MediaBrowser.Controller.Trickplay;
@ -51,7 +50,7 @@ namespace Emby.Server.Implementations.Dto
private readonly Lazy<ILiveTvManager> _livetvManagerFactory; private readonly Lazy<ILiveTvManager> _livetvManagerFactory;
private readonly ITrickplayManager _trickplayManager; private readonly ITrickplayManager _trickplayManager;
private readonly IChapterRepository _chapterRepository; private readonly IChapterManager _chapterManager;
public DtoService( public DtoService(
ILogger<DtoService> logger, ILogger<DtoService> logger,
@ -64,7 +63,7 @@ namespace Emby.Server.Implementations.Dto
IMediaSourceManager mediaSourceManager, IMediaSourceManager mediaSourceManager,
Lazy<ILiveTvManager> livetvManagerFactory, Lazy<ILiveTvManager> livetvManagerFactory,
ITrickplayManager trickplayManager, ITrickplayManager trickplayManager,
IChapterRepository chapterRepository) IChapterManager chapterManager)
{ {
_logger = logger; _logger = logger;
_libraryManager = libraryManager; _libraryManager = libraryManager;
@ -76,7 +75,7 @@ namespace Emby.Server.Implementations.Dto
_mediaSourceManager = mediaSourceManager; _mediaSourceManager = mediaSourceManager;
_livetvManagerFactory = livetvManagerFactory; _livetvManagerFactory = livetvManagerFactory;
_trickplayManager = trickplayManager; _trickplayManager = trickplayManager;
_chapterRepository = chapterRepository; _chapterManager = chapterManager;
} }
private ILiveTvManager LivetvManager => _livetvManagerFactory.Value; private ILiveTvManager LivetvManager => _livetvManagerFactory.Value;
@ -1061,7 +1060,7 @@ namespace Emby.Server.Implementations.Dto
if (options.ContainsField(ItemFields.Chapters)) if (options.ContainsField(ItemFields.Chapters))
{ {
dto.Chapters = _chapterRepository.GetChapters(item.Id).ToList(); dto.Chapters = _chapterManager.GetChapters(item.Id).ToList();
} }
if (options.ContainsField(ItemFields.Trickplay)) if (options.ContainsField(ItemFields.Trickplay))

View File

@ -29,9 +29,9 @@ public class PathManager : IPathManager
_appPaths = appPaths; _appPaths = appPaths;
} }
private string SubtitleCachePath => Path.Combine(_appPaths.DataPath, "subtitles"); private string SubtitleCachePath => Path.Join(_appPaths.DataPath, "subtitles");
private string AttachmentCachePath => Path.Combine(_appPaths.DataPath, "attachments"); private string AttachmentCachePath => Path.Join(_appPaths.DataPath, "attachments");
/// <inheritdoc /> /// <inheritdoc />
public string GetAttachmentPath(string mediaSourceId, string fileName) public string GetAttachmentPath(string mediaSourceId, string fileName)
@ -67,7 +67,21 @@ public class PathManager : IPathManager
var id = item.Id.ToString("D", CultureInfo.InvariantCulture).AsSpan(); var id = item.Id.ToString("D", CultureInfo.InvariantCulture).AsSpan();
return saveWithMedia return saveWithMedia
? Path.Combine(item.ContainingFolderPath, Path.ChangeExtension(item.Path, ".trickplay")) ? Path.Join(item.ContainingFolderPath, Path.ChangeExtension(item.Path, ".trickplay"))
: Path.Join(_config.ApplicationPaths.TrickplayPath, id[..2], id); : Path.Join(_config.ApplicationPaths.TrickplayPath, id[..2], id);
} }
/// <inheritdoc/>
public string GetChapterImageFolderPath(BaseItem item)
{
return Path.Join(item.GetInternalMetadataPath(), "chapters");
}
/// <inheritdoc/>
public string GetChapterImagePath(BaseItem item, long chapterPositionTicks)
{
var filename = item.DateModified.Ticks.ToString(CultureInfo.InvariantCulture) + "_" + chapterPositionTicks.ToString(CultureInfo.InvariantCulture) + ".jpg";
return Path.Join(GetChapterImageFolderPath(item), filename);
}
} }

View File

@ -1,272 +0,0 @@
#nullable disable
#pragma warning disable CS1591
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Jellyfin.Extensions;
using MediaBrowser.Controller.Chapters;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.MediaEncoding;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Configuration;
using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.IO;
using Microsoft.Extensions.Logging;
namespace Emby.Server.Implementations.MediaEncoder
{
public class EncodingManager : IEncodingManager
{
private readonly IFileSystem _fileSystem;
private readonly ILogger<EncodingManager> _logger;
private readonly IMediaEncoder _encoder;
private readonly IChapterRepository _chapterManager;
private readonly ILibraryManager _libraryManager;
/// <summary>
/// The first chapter ticks.
/// </summary>
private static readonly long _firstChapterTicks = TimeSpan.FromSeconds(15).Ticks;
public EncodingManager(
ILogger<EncodingManager> logger,
IFileSystem fileSystem,
IMediaEncoder encoder,
IChapterRepository chapterManager,
ILibraryManager libraryManager)
{
_logger = logger;
_fileSystem = fileSystem;
_encoder = encoder;
_chapterManager = chapterManager;
_libraryManager = libraryManager;
}
/// <summary>
/// Gets the chapter images data path.
/// </summary>
/// <value>The chapter images data path.</value>
private static string GetChapterImagesPath(BaseItem item)
{
return Path.Combine(item.GetInternalMetadataPath(), "chapters");
}
/// <summary>
/// Determines whether [is eligible for chapter image extraction] [the specified video].
/// </summary>
/// <param name="video">The video.</param>
/// <param name="libraryOptions">The library options for the video.</param>
/// <returns><c>true</c> if [is eligible for chapter image extraction] [the specified video]; otherwise, <c>false</c>.</returns>
private bool IsEligibleForChapterImageExtraction(Video video, LibraryOptions libraryOptions)
{
if (video.IsPlaceHolder)
{
return false;
}
if (libraryOptions is null || !libraryOptions.EnableChapterImageExtraction)
{
return false;
}
if (video.IsShortcut)
{
return false;
}
if (!video.IsCompleteMedia)
{
return false;
}
// Can't extract images if there are no video streams
return video.DefaultVideoStreamIndex.HasValue;
}
private long GetAverageDurationBetweenChapters(IReadOnlyList<ChapterInfo> chapters)
{
if (chapters.Count < 2)
{
return 0;
}
long sum = 0;
for (int i = 1; i < chapters.Count; i++)
{
sum += chapters[i].StartPositionTicks - chapters[i - 1].StartPositionTicks;
}
return sum / chapters.Count;
}
public async Task<bool> RefreshChapterImages(Video video, IDirectoryService directoryService, IReadOnlyList<ChapterInfo> chapters, bool extractImages, bool saveChapters, CancellationToken cancellationToken)
{
if (chapters.Count == 0)
{
return true;
}
var libraryOptions = _libraryManager.GetLibraryOptions(video);
if (!IsEligibleForChapterImageExtraction(video, libraryOptions))
{
extractImages = false;
}
var averageChapterDuration = GetAverageDurationBetweenChapters(chapters);
var threshold = TimeSpan.FromSeconds(1).Ticks;
if (averageChapterDuration < threshold)
{
_logger.LogInformation("Skipping chapter image extraction for {Video} as the average chapter duration {AverageDuration} was lower than the minimum threshold {Threshold}", video.Name, averageChapterDuration, threshold);
extractImages = false;
}
var success = true;
var changesMade = false;
var runtimeTicks = video.RunTimeTicks ?? 0;
var currentImages = GetSavedChapterImages(video, directoryService);
foreach (var chapter in chapters)
{
if (chapter.StartPositionTicks >= runtimeTicks)
{
_logger.LogInformation("Stopping chapter extraction for {0} because a chapter was found with a position greater than the runtime.", video.Name);
break;
}
var path = GetChapterImagePath(video, chapter.StartPositionTicks);
if (!currentImages.Contains(path, StringComparison.OrdinalIgnoreCase))
{
if (extractImages)
{
cancellationToken.ThrowIfCancellationRequested();
try
{
// Add some time for the first chapter to make sure we don't end up with a black image
var time = chapter.StartPositionTicks == 0 ? TimeSpan.FromTicks(Math.Min(_firstChapterTicks, video.RunTimeTicks ?? 0)) : TimeSpan.FromTicks(chapter.StartPositionTicks);
var inputPath = video.Path;
Directory.CreateDirectory(Path.GetDirectoryName(path));
var container = video.Container;
var mediaSource = new MediaSourceInfo
{
VideoType = video.VideoType,
IsoType = video.IsoType,
Protocol = video.PathProtocol.Value,
};
var tempFile = await _encoder.ExtractVideoImage(inputPath, container, mediaSource, video.GetDefaultVideoStream(), video.Video3DFormat, time, cancellationToken).ConfigureAwait(false);
File.Copy(tempFile, path, true);
try
{
_fileSystem.DeleteFile(tempFile);
}
catch (IOException ex)
{
_logger.LogError(ex, "Error deleting temporary chapter image encoding file {Path}", tempFile);
}
chapter.ImagePath = path;
chapter.ImageDateModified = _fileSystem.GetLastWriteTimeUtc(path);
changesMade = true;
}
catch (Exception ex)
{
_logger.LogError(ex, "Error extracting chapter images for {0}", string.Join(',', video.Path));
success = false;
break;
}
}
else if (!string.IsNullOrEmpty(chapter.ImagePath))
{
chapter.ImagePath = null;
changesMade = true;
}
}
else if (!string.Equals(path, chapter.ImagePath, StringComparison.OrdinalIgnoreCase))
{
chapter.ImagePath = path;
chapter.ImageDateModified = _fileSystem.GetLastWriteTimeUtc(path);
changesMade = true;
}
else if (libraryOptions?.EnableChapterImageExtraction != true)
{
// We have an image for the current chapter but the user has disabled chapter image extraction -> delete this chapter's image
chapter.ImagePath = null;
changesMade = true;
}
}
if (saveChapters && changesMade)
{
_chapterManager.SaveChapters(video.Id, chapters);
}
DeleteDeadImages(currentImages, chapters);
return success;
}
private string GetChapterImagePath(Video video, long chapterPositionTicks)
{
var filename = video.DateModified.Ticks.ToString(CultureInfo.InvariantCulture) + "_" + chapterPositionTicks.ToString(CultureInfo.InvariantCulture) + ".jpg";
return Path.Combine(GetChapterImagesPath(video), filename);
}
private static IReadOnlyList<string> GetSavedChapterImages(Video video, IDirectoryService directoryService)
{
var path = GetChapterImagesPath(video);
if (!Directory.Exists(path))
{
return Array.Empty<string>();
}
try
{
return directoryService.GetFilePaths(path);
}
catch (IOException)
{
return Array.Empty<string>();
}
}
private void DeleteDeadImages(IEnumerable<string> images, IEnumerable<ChapterInfo> chapters)
{
var deadImages = images
.Except(chapters.Select(i => i.ImagePath).Where(i => !string.IsNullOrEmpty(i)), StringComparer.OrdinalIgnoreCase)
.Where(i => BaseItem.SupportedImageExtensions.Contains(Path.GetExtension(i.AsSpan()), StringComparison.OrdinalIgnoreCase))
.ToList();
foreach (var image in deadImages)
{
_logger.LogDebug("Deleting dead chapter image {Path}", image);
try
{
_fileSystem.DeleteFile(image);
}
catch (IOException ex)
{
_logger.LogError(ex, "Error deleting {Path}.", image);
}
}
}
}
}

View File

@ -11,8 +11,6 @@ using MediaBrowser.Controller.Chapters;
using MediaBrowser.Controller.Dto; using MediaBrowser.Controller.Dto;
using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.MediaEncoding;
using MediaBrowser.Controller.Persistence;
using MediaBrowser.Controller.Providers; using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Globalization; using MediaBrowser.Model.Globalization;
using MediaBrowser.Model.IO; using MediaBrowser.Model.IO;
@ -28,42 +26,34 @@ namespace Emby.Server.Implementations.ScheduledTasks.Tasks
{ {
private readonly ILogger<ChapterImagesTask> _logger; private readonly ILogger<ChapterImagesTask> _logger;
private readonly ILibraryManager _libraryManager; private readonly ILibraryManager _libraryManager;
private readonly IItemRepository _itemRepo;
private readonly IApplicationPaths _appPaths; private readonly IApplicationPaths _appPaths;
private readonly IEncodingManager _encodingManager; private readonly IChapterManager _chapterManager;
private readonly IFileSystem _fileSystem; private readonly IFileSystem _fileSystem;
private readonly ILocalizationManager _localization; private readonly ILocalizationManager _localization;
private readonly IChapterRepository _chapterRepository;
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="ChapterImagesTask" /> class. /// Initializes a new instance of the <see cref="ChapterImagesTask" /> class.
/// </summary> /// </summary>
/// <param name="logger">Instance of the <see cref="ILogger"/> interface.</param> /// <param name="logger">Instance of the <see cref="ILogger"/> interface.</param>
/// <param name="libraryManager">Instance of the <see cref="ILibraryManager"/> interface.</param> /// <param name="libraryManager">Instance of the <see cref="ILibraryManager"/> interface.</param>
/// <param name="itemRepo">Instance of the <see cref="IItemRepository"/> interface.</param>
/// <param name="appPaths">Instance of the <see cref="IApplicationPaths"/> interface.</param> /// <param name="appPaths">Instance of the <see cref="IApplicationPaths"/> interface.</param>
/// <param name="encodingManager">Instance of the <see cref="IEncodingManager"/> interface.</param> /// <param name="chapterManager">Instance of the <see cref="IChapterManager"/> interface.</param>
/// <param name="fileSystem">Instance of the <see cref="IFileSystem"/> interface.</param> /// <param name="fileSystem">Instance of the <see cref="IFileSystem"/> interface.</param>
/// <param name="localization">Instance of the <see cref="ILocalizationManager"/> interface.</param> /// <param name="localization">Instance of the <see cref="ILocalizationManager"/> interface.</param>
/// <param name="chapterRepository">Instance of the <see cref="IChapterRepository"/> interface.</param>
public ChapterImagesTask( public ChapterImagesTask(
ILogger<ChapterImagesTask> logger, ILogger<ChapterImagesTask> logger,
ILibraryManager libraryManager, ILibraryManager libraryManager,
IItemRepository itemRepo,
IApplicationPaths appPaths, IApplicationPaths appPaths,
IEncodingManager encodingManager, IChapterManager chapterManager,
IFileSystem fileSystem, IFileSystem fileSystem,
ILocalizationManager localization, ILocalizationManager localization)
IChapterRepository chapterRepository)
{ {
_logger = logger; _logger = logger;
_libraryManager = libraryManager; _libraryManager = libraryManager;
_itemRepo = itemRepo;
_appPaths = appPaths; _appPaths = appPaths;
_encodingManager = encodingManager; _chapterManager = chapterManager;
_fileSystem = fileSystem; _fileSystem = fileSystem;
_localization = localization; _localization = localization;
_chapterRepository = chapterRepository;
} }
/// <inheritdoc /> /// <inheritdoc />
@ -126,12 +116,12 @@ namespace Emby.Server.Implementations.ScheduledTasks.Tasks
} }
catch (IOException) catch (IOException)
{ {
previouslyFailedImages = new List<string>(); previouslyFailedImages = [];
} }
} }
else else
{ {
previouslyFailedImages = new List<string>(); previouslyFailedImages = [];
} }
var directoryService = new DirectoryService(_fileSystem); var directoryService = new DirectoryService(_fileSystem);
@ -146,9 +136,9 @@ namespace Emby.Server.Implementations.ScheduledTasks.Tasks
try try
{ {
var chapters = _chapterRepository.GetChapters(video.Id); var chapters = _chapterManager.GetChapters(video.Id);
var success = await _encodingManager.RefreshChapterImages(video, directoryService, chapters, extract, true, cancellationToken).ConfigureAwait(false); var success = await _chapterManager.RefreshChapterImages(video, directoryService, chapters, extract, true, cancellationToken).ConfigureAwait(false);
if (!success) if (!success)
{ {

View File

@ -1,12 +1,10 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq; using System.Linq;
using Jellyfin.Database.Implementations; using Jellyfin.Database.Implementations;
using Jellyfin.Database.Implementations.Entities; using Jellyfin.Database.Implementations.Entities;
using MediaBrowser.Controller.Chapters;
using MediaBrowser.Controller.Drawing; using MediaBrowser.Controller.Drawing;
using MediaBrowser.Model.Dto; using MediaBrowser.Controller.Persistence;
using MediaBrowser.Model.Entities; using MediaBrowser.Model.Entities;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
@ -31,19 +29,7 @@ public class ChapterRepository : IChapterRepository
_imageProcessor = imageProcessor; _imageProcessor = imageProcessor;
} }
/// <inheritdoc cref="IChapterRepository"/> /// <inheritdoc />
public ChapterInfo? GetChapter(BaseItemDto baseItem, int index)
{
return GetChapter(baseItem.Id, index);
}
/// <inheritdoc cref="IChapterRepository"/>
public IReadOnlyList<ChapterInfo> GetChapters(BaseItemDto baseItem)
{
return GetChapters(baseItem.Id);
}
/// <inheritdoc cref="IChapterRepository"/>
public ChapterInfo? GetChapter(Guid baseItemId, int index) public ChapterInfo? GetChapter(Guid baseItemId, int index)
{ {
using var context = _dbProvider.CreateDbContext(); using var context = _dbProvider.CreateDbContext();
@ -62,7 +48,7 @@ public class ChapterRepository : IChapterRepository
return null; return null;
} }
/// <inheritdoc cref="IChapterRepository"/> /// <inheritdoc />
public IReadOnlyList<ChapterInfo> GetChapters(Guid baseItemId) public IReadOnlyList<ChapterInfo> GetChapters(Guid baseItemId)
{ {
using var context = _dbProvider.CreateDbContext(); using var context = _dbProvider.CreateDbContext();
@ -77,7 +63,7 @@ public class ChapterRepository : IChapterRepository
.ToArray(); .ToArray();
} }
/// <inheritdoc cref="IChapterRepository"/> /// <inheritdoc />
public void SaveChapters(Guid itemId, IReadOnlyList<ChapterInfo> chapters) public void SaveChapters(Guid itemId, IReadOnlyList<ChapterInfo> chapters)
{ {
using var context = _dbProvider.CreateDbContext(); using var context = _dbProvider.CreateDbContext();
@ -95,6 +81,14 @@ public class ChapterRepository : IChapterRepository
} }
} }
/// <inheritdoc />
public void DeleteChapters(Guid itemId)
{
using var context = _dbProvider.CreateDbContext();
context.Chapters.Where(c => c.ItemId.Equals(itemId)).ExecuteDelete();
context.SaveChanges();
}
private Chapter Map(ChapterInfo chapterInfo, int index, Guid itemId) private Chapter Map(ChapterInfo chapterInfo, int index, Guid itemId)
{ {
return new Chapter() return new Chapter()

View File

@ -0,0 +1,55 @@
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Entities;
namespace MediaBrowser.Controller.Chapters;
/// <summary>
/// Interface IChapterManager.
/// </summary>
public interface IChapterManager
{
/// <summary>
/// Saves the chapters.
/// </summary>
/// <param name="video">The video.</param>
/// <param name="chapters">The set of chapters.</param>
void SaveChapters(Video video, IReadOnlyList<ChapterInfo> chapters);
/// <summary>
/// Gets a single chapter of a BaseItem on a specific index.
/// </summary>
/// <param name="baseItemId">The BaseItems id.</param>
/// <param name="index">The index of that chapter.</param>
/// <returns>A chapter instance.</returns>
ChapterInfo? GetChapter(Guid baseItemId, int index);
/// <summary>
/// Gets all chapters associated with the baseItem.
/// </summary>
/// <param name="baseItemId">The BaseItems id.</param>
/// <returns>A readonly list of chapter instances.</returns>
IReadOnlyList<ChapterInfo> GetChapters(Guid baseItemId);
/// <summary>
/// Refreshes the chapter images.
/// </summary>
/// <param name="video">Video to use.</param>
/// <param name="directoryService">Directory service to use.</param>
/// <param name="chapters">Set of chapters to refresh.</param>
/// <param name="extractImages">Option to extract images.</param>
/// <param name="saveChapters">Option to save chapters.</param>
/// <param name="cancellationToken">CancellationToken to use for operation.</param>
/// <returns><c>true</c> if successful, <c>false</c> if not.</returns>
Task<bool> RefreshChapterImages(Video video, IDirectoryService directoryService, IReadOnlyList<ChapterInfo> chapters, bool extractImages, bool saveChapters, CancellationToken cancellationToken);
/// <summary>
/// Deletes the chapter images.
/// </summary>
/// <param name="video">Video to use.</param>
void DeleteChapterImages(Video video);
}

View File

@ -34,7 +34,6 @@ using MediaBrowser.Model.IO;
using MediaBrowser.Model.Library; using MediaBrowser.Model.Library;
using MediaBrowser.Model.LiveTv; using MediaBrowser.Model.LiveTv;
using MediaBrowser.Model.MediaInfo; using MediaBrowser.Model.MediaInfo;
using MediaBrowser.Model.Providers;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
namespace MediaBrowser.Controller.Entities namespace MediaBrowser.Controller.Entities
@ -484,7 +483,7 @@ namespace MediaBrowser.Controller.Entities
public static IItemRepository ItemRepository { get; set; } public static IItemRepository ItemRepository { get; set; }
public static IChapterRepository ChapterRepository { get; set; } public static IChapterManager ChapterManager { get; set; }
public static IFileSystem FileSystem { get; set; } public static IFileSystem FileSystem { get; set; }
@ -2051,7 +2050,7 @@ namespace MediaBrowser.Controller.Entities
{ {
if (imageType == ImageType.Chapter) if (imageType == ImageType.Chapter)
{ {
var chapter = ChapterRepository.GetChapter(this.Id, imageIndex); var chapter = ChapterManager.GetChapter(Id, imageIndex);
if (chapter is null) if (chapter is null)
{ {
@ -2101,7 +2100,7 @@ namespace MediaBrowser.Controller.Entities
if (image.Type == ImageType.Chapter) if (image.Type == ImageType.Chapter)
{ {
var chapters = ChapterRepository.GetChapters(this.Id); var chapters = ChapterManager.GetChapters(Id);
for (var i = 0; i < chapters.Count; i++) for (var i = 0; i < chapters.Count; i++)
{ {
if (chapters[i].ImagePath == image.Path) if (chapters[i].ImagePath == image.Path)

View File

@ -1,5 +1,4 @@
using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities;
using MediaBrowser.Model.Dto;
namespace MediaBrowser.Controller.IO; namespace MediaBrowser.Controller.IO;
@ -46,4 +45,19 @@ public interface IPathManager
/// <param name="mediaSourceId">The media source id.</param> /// <param name="mediaSourceId">The media source id.</param>
/// <returns>The absolute path.</returns> /// <returns>The absolute path.</returns>
public string GetAttachmentFolderPath(string mediaSourceId); public string GetAttachmentFolderPath(string mediaSourceId);
/// <summary>
/// Gets the chapter images data path.
/// </summary>
/// <param name="item">The base item.</param>
/// <returns>The chapter images data path.</returns>
public string GetChapterImageFolderPath(BaseItem item);
/// <summary>
/// Gets the chapter images path.
/// </summary>
/// <param name="item">The base item.</param>
/// <param name="chapterPositionTicks">The chapter position.</param>
/// <returns>The chapter images data path.</returns>
public string GetChapterImagePath(BaseItem item, long chapterPositionTicks);
} }

View File

@ -1,28 +0,0 @@
#nullable disable
#pragma warning disable CS1591
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Entities;
namespace MediaBrowser.Controller.MediaEncoding
{
public interface IEncodingManager
{
/// <summary>
/// Refreshes the chapter images.
/// </summary>
/// <param name="video">Video to use.</param>
/// <param name="directoryService">Directory service to use.</param>
/// <param name="chapters">Set of chapters to refresh.</param>
/// <param name="extractImages">Option to extract images.</param>
/// <param name="saveChapters">Option to save chapters.</param>
/// <param name="cancellationToken">CancellationToken to use for operation.</param>
/// <returns><c>true</c> if successful, <c>false</c> if not.</returns>
Task<bool> RefreshChapterImages(Video video, IDirectoryService directoryService, IReadOnlyList<ChapterInfo> chapters, bool extractImages, bool saveChapters, CancellationToken cancellationToken);
}
}

View File

@ -1,15 +1,20 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Entities; using MediaBrowser.Model.Entities;
namespace MediaBrowser.Controller.Chapters; namespace MediaBrowser.Controller.Persistence;
/// <summary> /// <summary>
/// Interface IChapterManager. /// Interface IChapterRepository.
/// </summary> /// </summary>
public interface IChapterRepository public interface IChapterRepository
{ {
/// <summary>
/// Deletes the chapters.
/// </summary>
/// <param name="itemId">The item.</param>
void DeleteChapters(Guid itemId);
/// <summary> /// <summary>
/// Saves the chapters. /// Saves the chapters.
/// </summary> /// </summary>
@ -17,21 +22,6 @@ public interface IChapterRepository
/// <param name="chapters">The set of chapters.</param> /// <param name="chapters">The set of chapters.</param>
void SaveChapters(Guid itemId, IReadOnlyList<ChapterInfo> chapters); void SaveChapters(Guid itemId, IReadOnlyList<ChapterInfo> chapters);
/// <summary>
/// Gets all chapters associated with the baseItem.
/// </summary>
/// <param name="baseItem">The baseitem.</param>
/// <returns>A readonly list of chapter instances.</returns>
IReadOnlyList<ChapterInfo> GetChapters(BaseItemDto baseItem);
/// <summary>
/// Gets a single chapter of a BaseItem on a specific index.
/// </summary>
/// <param name="baseItem">The baseitem.</param>
/// <param name="index">The index of that chapter.</param>
/// <returns>A chapter instance.</returns>
ChapterInfo? GetChapter(BaseItemDto baseItem, int index);
/// <summary> /// <summary>
/// Gets all chapters associated with the baseItem. /// Gets all chapters associated with the baseItem.
/// </summary> /// </summary>

View File

@ -32,7 +32,6 @@ namespace MediaBrowser.Providers.MediaInfo
private const char InternalValueSeparator = '\u001F'; private const char InternalValueSeparator = '\u001F';
private readonly IMediaEncoder _mediaEncoder; private readonly IMediaEncoder _mediaEncoder;
private readonly IItemRepository _itemRepo;
private readonly ILibraryManager _libraryManager; private readonly ILibraryManager _libraryManager;
private readonly ILogger<AudioFileProber> _logger; private readonly ILogger<AudioFileProber> _logger;
private readonly IMediaSourceManager _mediaSourceManager; private readonly IMediaSourceManager _mediaSourceManager;
@ -46,7 +45,6 @@ namespace MediaBrowser.Providers.MediaInfo
/// <param name="logger">Instance of the <see cref="ILogger"/> interface.</param> /// <param name="logger">Instance of the <see cref="ILogger"/> interface.</param>
/// <param name="mediaSourceManager">Instance of the <see cref="IMediaSourceManager"/> interface.</param> /// <param name="mediaSourceManager">Instance of the <see cref="IMediaSourceManager"/> interface.</param>
/// <param name="mediaEncoder">Instance of the <see cref="IMediaEncoder"/> interface.</param> /// <param name="mediaEncoder">Instance of the <see cref="IMediaEncoder"/> interface.</param>
/// <param name="itemRepo">Instance of the <see cref="IItemRepository"/> interface.</param>
/// <param name="libraryManager">Instance of the <see cref="ILibraryManager"/> interface.</param> /// <param name="libraryManager">Instance of the <see cref="ILibraryManager"/> interface.</param>
/// <param name="lyricResolver">Instance of the <see cref="LyricResolver"/> interface.</param> /// <param name="lyricResolver">Instance of the <see cref="LyricResolver"/> interface.</param>
/// <param name="lyricManager">Instance of the <see cref="ILyricManager"/> interface.</param> /// <param name="lyricManager">Instance of the <see cref="ILyricManager"/> interface.</param>
@ -55,14 +53,12 @@ namespace MediaBrowser.Providers.MediaInfo
ILogger<AudioFileProber> logger, ILogger<AudioFileProber> logger,
IMediaSourceManager mediaSourceManager, IMediaSourceManager mediaSourceManager,
IMediaEncoder mediaEncoder, IMediaEncoder mediaEncoder,
IItemRepository itemRepo,
ILibraryManager libraryManager, ILibraryManager libraryManager,
LyricResolver lyricResolver, LyricResolver lyricResolver,
ILyricManager lyricManager, ILyricManager lyricManager,
IMediaStreamRepository mediaStreamRepository) IMediaStreamRepository mediaStreamRepository)
{ {
_mediaEncoder = mediaEncoder; _mediaEncoder = mediaEncoder;
_itemRepo = itemRepo;
_libraryManager = libraryManager; _libraryManager = libraryManager;
_logger = logger; _logger = logger;
_mediaSourceManager = mediaSourceManager; _mediaSourceManager = mediaSourceManager;

View File

@ -34,13 +34,11 @@ namespace MediaBrowser.Providers.MediaInfo
private readonly ILogger<FFProbeVideoInfo> _logger; private readonly ILogger<FFProbeVideoInfo> _logger;
private readonly IMediaSourceManager _mediaSourceManager; private readonly IMediaSourceManager _mediaSourceManager;
private readonly IMediaEncoder _mediaEncoder; private readonly IMediaEncoder _mediaEncoder;
private readonly IItemRepository _itemRepo;
private readonly IBlurayExaminer _blurayExaminer; private readonly IBlurayExaminer _blurayExaminer;
private readonly ILocalizationManager _localization; private readonly ILocalizationManager _localization;
private readonly IEncodingManager _encodingManager; private readonly IChapterManager _chapterManager;
private readonly IServerConfigurationManager _config; private readonly IServerConfigurationManager _config;
private readonly ISubtitleManager _subtitleManager; private readonly ISubtitleManager _subtitleManager;
private readonly IChapterRepository _chapterManager;
private readonly ILibraryManager _libraryManager; private readonly ILibraryManager _libraryManager;
private readonly AudioResolver _audioResolver; private readonly AudioResolver _audioResolver;
private readonly SubtitleResolver _subtitleResolver; private readonly SubtitleResolver _subtitleResolver;
@ -51,13 +49,11 @@ namespace MediaBrowser.Providers.MediaInfo
ILogger<FFProbeVideoInfo> logger, ILogger<FFProbeVideoInfo> logger,
IMediaSourceManager mediaSourceManager, IMediaSourceManager mediaSourceManager,
IMediaEncoder mediaEncoder, IMediaEncoder mediaEncoder,
IItemRepository itemRepo,
IBlurayExaminer blurayExaminer, IBlurayExaminer blurayExaminer,
ILocalizationManager localization, ILocalizationManager localization,
IEncodingManager encodingManager, IChapterManager chapterManager,
IServerConfigurationManager config, IServerConfigurationManager config,
ISubtitleManager subtitleManager, ISubtitleManager subtitleManager,
IChapterRepository chapterManager,
ILibraryManager libraryManager, ILibraryManager libraryManager,
AudioResolver audioResolver, AudioResolver audioResolver,
SubtitleResolver subtitleResolver, SubtitleResolver subtitleResolver,
@ -67,13 +63,11 @@ namespace MediaBrowser.Providers.MediaInfo
_logger = logger; _logger = logger;
_mediaSourceManager = mediaSourceManager; _mediaSourceManager = mediaSourceManager;
_mediaEncoder = mediaEncoder; _mediaEncoder = mediaEncoder;
_itemRepo = itemRepo;
_blurayExaminer = blurayExaminer; _blurayExaminer = blurayExaminer;
_localization = localization; _localization = localization;
_encodingManager = encodingManager; _chapterManager = chapterManager;
_config = config; _config = config;
_subtitleManager = subtitleManager; _subtitleManager = subtitleManager;
_chapterManager = chapterManager;
_libraryManager = libraryManager; _libraryManager = libraryManager;
_audioResolver = audioResolver; _audioResolver = audioResolver;
_subtitleResolver = subtitleResolver; _subtitleResolver = subtitleResolver;
@ -298,9 +292,9 @@ namespace MediaBrowser.Providers.MediaInfo
extractDuringScan = libraryOptions.ExtractChapterImagesDuringLibraryScan; extractDuringScan = libraryOptions.ExtractChapterImagesDuringLibraryScan;
} }
await _encodingManager.RefreshChapterImages(video, options.DirectoryService, chapters, extractDuringScan, false, cancellationToken).ConfigureAwait(false); await _chapterManager.RefreshChapterImages(video, options.DirectoryService, chapters, extractDuringScan, false, cancellationToken).ConfigureAwait(false);
_chapterManager.SaveChapters(video.Id, chapters); _chapterManager.SaveChapters(video, chapters);
} }
} }

View File

@ -55,13 +55,11 @@ namespace MediaBrowser.Providers.MediaInfo
/// </summary> /// </summary>
/// <param name="mediaSourceManager">Instance of the <see cref="IMediaSourceManager"/> interface.</param> /// <param name="mediaSourceManager">Instance of the <see cref="IMediaSourceManager"/> interface.</param>
/// <param name="mediaEncoder">Instance of the <see cref="IMediaEncoder"/> interface.</param> /// <param name="mediaEncoder">Instance of the <see cref="IMediaEncoder"/> interface.</param>
/// <param name="itemRepo">Instance of the <see cref="IItemRepository"/> interface.</param>
/// <param name="blurayExaminer">Instance of the <see cref="IBlurayExaminer"/> interface.</param> /// <param name="blurayExaminer">Instance of the <see cref="IBlurayExaminer"/> interface.</param>
/// <param name="localization">Instance of the <see cref="ILocalizationManager"/> interface.</param> /// <param name="localization">Instance of the <see cref="ILocalizationManager"/> interface.</param>
/// <param name="encodingManager">Instance of the <see cref="IEncodingManager"/> interface.</param> /// <param name="chapterManager">Instance of the <see cref="IChapterManager"/> interface.</param>
/// <param name="config">Instance of the <see cref="IServerConfigurationManager"/> interface.</param> /// <param name="config">Instance of the <see cref="IServerConfigurationManager"/> interface.</param>
/// <param name="subtitleManager">Instance of the <see cref="ISubtitleManager"/> interface.</param> /// <param name="subtitleManager">Instance of the <see cref="ISubtitleManager"/> interface.</param>
/// <param name="chapterManager">Instance of the <see cref="IChapterRepository"/> interface.</param>
/// <param name="libraryManager">Instance of the <see cref="ILibraryManager"/> interface.</param> /// <param name="libraryManager">Instance of the <see cref="ILibraryManager"/> interface.</param>
/// <param name="loggerFactory">Instance of the <see cref="ILoggerFactory"/>.</param> /// <param name="loggerFactory">Instance of the <see cref="ILoggerFactory"/>.</param>
/// <param name="fileSystem">Instance of the <see cref="IFileSystem"/> interface.</param> /// <param name="fileSystem">Instance of the <see cref="IFileSystem"/> interface.</param>
@ -72,13 +70,11 @@ namespace MediaBrowser.Providers.MediaInfo
public ProbeProvider( public ProbeProvider(
IMediaSourceManager mediaSourceManager, IMediaSourceManager mediaSourceManager,
IMediaEncoder mediaEncoder, IMediaEncoder mediaEncoder,
IItemRepository itemRepo,
IBlurayExaminer blurayExaminer, IBlurayExaminer blurayExaminer,
ILocalizationManager localization, ILocalizationManager localization,
IEncodingManager encodingManager, IChapterManager chapterManager,
IServerConfigurationManager config, IServerConfigurationManager config,
ISubtitleManager subtitleManager, ISubtitleManager subtitleManager,
IChapterRepository chapterManager,
ILibraryManager libraryManager, ILibraryManager libraryManager,
IFileSystem fileSystem, IFileSystem fileSystem,
ILoggerFactory loggerFactory, ILoggerFactory loggerFactory,
@ -96,13 +92,11 @@ namespace MediaBrowser.Providers.MediaInfo
loggerFactory.CreateLogger<FFProbeVideoInfo>(), loggerFactory.CreateLogger<FFProbeVideoInfo>(),
mediaSourceManager, mediaSourceManager,
mediaEncoder, mediaEncoder,
itemRepo,
blurayExaminer, blurayExaminer,
localization, localization,
encodingManager, chapterManager,
config, config,
subtitleManager, subtitleManager,
chapterManager,
libraryManager, libraryManager,
_audioResolver, _audioResolver,
_subtitleResolver, _subtitleResolver,
@ -113,7 +107,6 @@ namespace MediaBrowser.Providers.MediaInfo
loggerFactory.CreateLogger<AudioFileProber>(), loggerFactory.CreateLogger<AudioFileProber>(),
mediaSourceManager, mediaSourceManager,
mediaEncoder, mediaEncoder,
itemRepo,
libraryManager, libraryManager,
_lyricResolver, _lyricResolver,
lyricManager, lyricManager,