mirror of
				https://github.com/jellyfin/jellyfin.git
				synced 2025-10-30 18:22:48 -04:00 
			
		
		
		
	Merge pull request #13847 from Shadowghost/rework-chapter-management
Rework chapter management
This commit is contained in:
		
							parent
							
								
									f35b8dd33d
								
							
						
					
					
						commit
						df5671263f
					
				| @ -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>(); | ||||||
|  | |||||||
							
								
								
									
										313
									
								
								Emby.Server.Implementations/Chapters/ChapterManager.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										313
									
								
								Emby.Server.Implementations/Chapters/ChapterManager.cs
									
									
									
									
									
										Normal 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); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
| @ -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)) | ||||||
|  | |||||||
| @ -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); | ||||||
|  |     } | ||||||
| } | } | ||||||
|  | |||||||
| @ -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); |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| @ -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) | ||||||
|                     { |                     { | ||||||
|  | |||||||
| @ -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() | ||||||
|  | |||||||
							
								
								
									
										55
									
								
								MediaBrowser.Controller/Chapters/IChapterManager.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										55
									
								
								MediaBrowser.Controller/Chapters/IChapterManager.cs
									
									
									
									
									
										Normal 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); | ||||||
|  | } | ||||||
| @ -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) | ||||||
|  | |||||||
| @ -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); | ||||||
| } | } | ||||||
|  | |||||||
| @ -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); |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| @ -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> | ||||||
| @ -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; | ||||||
|  | |||||||
| @ -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); | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -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, | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user