KeyframeExtractionScheduledTask.cs 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111
  1. using System;
  2. using System.Collections.Generic;
  3. using System.IO;
  4. using System.Linq;
  5. using System.Threading;
  6. using System.Threading.Tasks;
  7. using Jellyfin.Data.Enums;
  8. using Jellyfin.MediaEncoding.Hls.Extractors;
  9. using MediaBrowser.Controller.Dto;
  10. using MediaBrowser.Controller.Entities;
  11. using MediaBrowser.Controller.Library;
  12. using MediaBrowser.Model.Entities;
  13. using MediaBrowser.Model.Globalization;
  14. using MediaBrowser.Model.Tasks;
  15. namespace Jellyfin.MediaEncoding.Hls.ScheduledTasks;
  16. /// <inheritdoc />
  17. public class KeyframeExtractionScheduledTask : IScheduledTask
  18. {
  19. private const int Pagesize = 1000;
  20. private readonly ILocalizationManager _localizationManager;
  21. private readonly ILibraryManager _libraryManager;
  22. private readonly IKeyframeExtractor[] _keyframeExtractors;
  23. private static readonly BaseItemKind[] _itemTypes = { BaseItemKind.Episode, BaseItemKind.Movie };
  24. /// <summary>
  25. /// Initializes a new instance of the <see cref="KeyframeExtractionScheduledTask"/> class.
  26. /// </summary>
  27. /// <param name="localizationManager">An instance of the <see cref="ILocalizationManager"/> interface.</param>
  28. /// <param name="libraryManager">An instance of the <see cref="ILibraryManager"/> interface.</param>
  29. /// <param name="keyframeExtractors">The keyframe extractors.</param>
  30. public KeyframeExtractionScheduledTask(ILocalizationManager localizationManager, ILibraryManager libraryManager, IEnumerable<IKeyframeExtractor> keyframeExtractors)
  31. {
  32. _localizationManager = localizationManager;
  33. _libraryManager = libraryManager;
  34. _keyframeExtractors = keyframeExtractors.OrderByDescending(e => e.IsMetadataBased).ToArray();
  35. }
  36. /// <inheritdoc />
  37. public string Name => "Keyframe Extractor";
  38. /// <inheritdoc />
  39. public string Key => "KeyframeExtraction";
  40. /// <inheritdoc />
  41. public string Description => "Extracts keyframes from video files to create more precise HLS playlists. This task may run for a long time.";
  42. /// <inheritdoc />
  43. public string Category => _localizationManager.GetLocalizedString("TasksLibraryCategory");
  44. /// <inheritdoc />
  45. public Task Execute(CancellationToken cancellationToken, IProgress<double> progress)
  46. {
  47. var query = new InternalItemsQuery
  48. {
  49. MediaTypes = new[] { MediaType.Video },
  50. IsVirtualItem = false,
  51. IncludeItemTypes = _itemTypes,
  52. DtoOptions = new DtoOptions(true),
  53. SourceTypes = new[] { SourceType.Library },
  54. Recursive = true,
  55. Limit = Pagesize
  56. };
  57. var numberOfVideos = _libraryManager.GetCount(query);
  58. var startIndex = 0;
  59. var numComplete = 0;
  60. while (startIndex < numberOfVideos)
  61. {
  62. query.StartIndex = startIndex;
  63. var videos = _libraryManager.GetItemList(query);
  64. var currentPageCount = videos.Count;
  65. // TODO parallelize with Parallel.ForEach?
  66. for (var i = 0; i < currentPageCount; i++)
  67. {
  68. var video = videos[i];
  69. // Only local files supported
  70. if (video.IsFileProtocol && File.Exists(video.Path))
  71. {
  72. for (var j = 0; j < _keyframeExtractors.Length; j++)
  73. {
  74. var extractor = _keyframeExtractors[j];
  75. // The cache decorator will make sure to save them in the data dir
  76. if (extractor.TryExtractKeyframes(video.Path, out _))
  77. {
  78. break;
  79. }
  80. }
  81. }
  82. // Update progress
  83. numComplete++;
  84. double percent = (double)numComplete / numberOfVideos;
  85. progress.Report(100 * percent);
  86. }
  87. startIndex += Pagesize;
  88. }
  89. progress.Report(100);
  90. return Task.CompletedTask;
  91. }
  92. /// <inheritdoc />
  93. public IEnumerable<TaskTriggerInfo> GetDefaultTriggers() => Enumerable.Empty<TaskTriggerInfo>();
  94. }