FFProbeProvider.cs 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232
  1. #nullable disable
  2. #pragma warning disable CS1591
  3. using System;
  4. using System.IO;
  5. using System.Linq;
  6. using System.Threading;
  7. using System.Threading.Tasks;
  8. using Emby.Naming.Common;
  9. using MediaBrowser.Controller.Chapters;
  10. using MediaBrowser.Controller.Configuration;
  11. using MediaBrowser.Controller.Entities;
  12. using MediaBrowser.Controller.Entities.Audio;
  13. using MediaBrowser.Controller.Entities.Movies;
  14. using MediaBrowser.Controller.Entities.TV;
  15. using MediaBrowser.Controller.Library;
  16. using MediaBrowser.Controller.MediaEncoding;
  17. using MediaBrowser.Controller.Persistence;
  18. using MediaBrowser.Controller.Providers;
  19. using MediaBrowser.Controller.Subtitles;
  20. using MediaBrowser.Model.Entities;
  21. using MediaBrowser.Model.Globalization;
  22. using MediaBrowser.Model.IO;
  23. using MediaBrowser.Model.MediaInfo;
  24. using Microsoft.Extensions.Logging;
  25. namespace MediaBrowser.Providers.MediaInfo
  26. {
  27. public class FFProbeProvider : ICustomMetadataProvider<Episode>,
  28. ICustomMetadataProvider<MusicVideo>,
  29. ICustomMetadataProvider<Movie>,
  30. ICustomMetadataProvider<Trailer>,
  31. ICustomMetadataProvider<Video>,
  32. ICustomMetadataProvider<Audio>,
  33. ICustomMetadataProvider<AudioBook>,
  34. IHasOrder,
  35. IForcedProvider,
  36. IPreRefreshProvider,
  37. IHasItemChangeMonitor
  38. {
  39. private readonly ILogger<FFProbeProvider> _logger;
  40. private readonly AudioResolver _audioResolver;
  41. private readonly SubtitleResolver _subtitleResolver;
  42. private readonly FFProbeVideoInfo _videoProber;
  43. private readonly FFProbeAudioInfo _audioProber;
  44. private readonly Task<ItemUpdateType> _cachedTask = Task.FromResult(ItemUpdateType.None);
  45. public FFProbeProvider(
  46. ILogger<FFProbeProvider> logger,
  47. IMediaSourceManager mediaSourceManager,
  48. IMediaEncoder mediaEncoder,
  49. IItemRepository itemRepo,
  50. IBlurayExaminer blurayExaminer,
  51. ILocalizationManager localization,
  52. IEncodingManager encodingManager,
  53. IServerConfigurationManager config,
  54. ISubtitleManager subtitleManager,
  55. IChapterManager chapterManager,
  56. ILibraryManager libraryManager,
  57. IFileSystem fileSystem,
  58. NamingOptions namingOptions)
  59. {
  60. _logger = logger;
  61. _audioResolver = new AudioResolver(localization, mediaEncoder, fileSystem, namingOptions);
  62. _subtitleResolver = new SubtitleResolver(localization, mediaEncoder, fileSystem, namingOptions);
  63. _videoProber = new FFProbeVideoInfo(
  64. _logger,
  65. mediaSourceManager,
  66. mediaEncoder,
  67. itemRepo,
  68. blurayExaminer,
  69. localization,
  70. encodingManager,
  71. config,
  72. subtitleManager,
  73. chapterManager,
  74. libraryManager,
  75. _audioResolver,
  76. _subtitleResolver);
  77. _audioProber = new FFProbeAudioInfo(mediaSourceManager, mediaEncoder, itemRepo, libraryManager);
  78. }
  79. public string Name => "ffprobe";
  80. // Run last
  81. public int Order => 100;
  82. public bool HasChanged(BaseItem item, IDirectoryService directoryService)
  83. {
  84. var video = item as Video;
  85. if (video == null || video.VideoType == VideoType.VideoFile || video.VideoType == VideoType.Iso)
  86. {
  87. var path = item.Path;
  88. if (!string.IsNullOrWhiteSpace(path) && item.IsFileProtocol)
  89. {
  90. var file = directoryService.GetFile(path);
  91. if (file != null && file.LastWriteTimeUtc != item.DateModified)
  92. {
  93. _logger.LogDebug("Refreshing {ItemPath} due to date modified timestamp change.", path);
  94. return true;
  95. }
  96. }
  97. }
  98. if (item.SupportsLocalMetadata && video != null && !video.IsPlaceHolder
  99. && !video.SubtitleFiles.SequenceEqual(
  100. _subtitleResolver.GetExternalFiles(video, directoryService, false)
  101. .Select(info => info.Path).ToList(),
  102. StringComparer.Ordinal))
  103. {
  104. _logger.LogDebug("Refreshing {ItemPath} due to external subtitles change.", item.Path);
  105. return true;
  106. }
  107. if (item.SupportsLocalMetadata && video != null && !video.IsPlaceHolder
  108. && !video.AudioFiles.SequenceEqual(
  109. _audioResolver.GetExternalFiles(video, directoryService, false)
  110. .Select(info => info.Path).ToList(),
  111. StringComparer.Ordinal))
  112. {
  113. _logger.LogDebug("Refreshing {ItemPath} due to external audio change.", item.Path);
  114. return true;
  115. }
  116. return false;
  117. }
  118. public Task<ItemUpdateType> FetchAsync(Episode item, MetadataRefreshOptions options, CancellationToken cancellationToken)
  119. {
  120. return FetchVideoInfo(item, options, cancellationToken);
  121. }
  122. public Task<ItemUpdateType> FetchAsync(MusicVideo item, MetadataRefreshOptions options, CancellationToken cancellationToken)
  123. {
  124. return FetchVideoInfo(item, options, cancellationToken);
  125. }
  126. public Task<ItemUpdateType> FetchAsync(Movie item, MetadataRefreshOptions options, CancellationToken cancellationToken)
  127. {
  128. return FetchVideoInfo(item, options, cancellationToken);
  129. }
  130. public Task<ItemUpdateType> FetchAsync(Trailer item, MetadataRefreshOptions options, CancellationToken cancellationToken)
  131. {
  132. return FetchVideoInfo(item, options, cancellationToken);
  133. }
  134. public Task<ItemUpdateType> FetchAsync(Video item, MetadataRefreshOptions options, CancellationToken cancellationToken)
  135. {
  136. return FetchVideoInfo(item, options, cancellationToken);
  137. }
  138. public Task<ItemUpdateType> FetchAsync(Audio item, MetadataRefreshOptions options, CancellationToken cancellationToken)
  139. {
  140. return FetchAudioInfo(item, options, cancellationToken);
  141. }
  142. public Task<ItemUpdateType> FetchAsync(AudioBook item, MetadataRefreshOptions options, CancellationToken cancellationToken)
  143. {
  144. return FetchAudioInfo(item, options, cancellationToken);
  145. }
  146. public Task<ItemUpdateType> FetchVideoInfo<T>(T item, MetadataRefreshOptions options, CancellationToken cancellationToken)
  147. where T : Video
  148. {
  149. if (item.IsPlaceHolder)
  150. {
  151. return _cachedTask;
  152. }
  153. if (!item.IsCompleteMedia)
  154. {
  155. return _cachedTask;
  156. }
  157. if (item.IsVirtualItem)
  158. {
  159. return _cachedTask;
  160. }
  161. if (!options.EnableRemoteContentProbe && !item.IsFileProtocol)
  162. {
  163. return _cachedTask;
  164. }
  165. if (item.IsShortcut)
  166. {
  167. FetchShortcutInfo(item);
  168. }
  169. return _videoProber.ProbeVideo(item, options, cancellationToken);
  170. }
  171. private string NormalizeStrmLine(string line)
  172. {
  173. return line.Replace("\t", string.Empty, StringComparison.Ordinal)
  174. .Replace("\r", string.Empty, StringComparison.Ordinal)
  175. .Replace("\n", string.Empty, StringComparison.Ordinal)
  176. .Trim();
  177. }
  178. private void FetchShortcutInfo(BaseItem item)
  179. {
  180. item.ShortcutPath = File.ReadAllLines(item.Path)
  181. .Select(NormalizeStrmLine)
  182. .FirstOrDefault(i => !string.IsNullOrWhiteSpace(i) && !i.StartsWith('#'));
  183. }
  184. public Task<ItemUpdateType> FetchAudioInfo<T>(T item, MetadataRefreshOptions options, CancellationToken cancellationToken)
  185. where T : Audio
  186. {
  187. if (item.IsVirtualItem)
  188. {
  189. return _cachedTask;
  190. }
  191. if (!options.EnableRemoteContentProbe && !item.IsFileProtocol)
  192. {
  193. return _cachedTask;
  194. }
  195. if (item.IsShortcut)
  196. {
  197. FetchShortcutInfo(item);
  198. }
  199. return _audioProber.Probe(item, options, cancellationToken);
  200. }
  201. }
  202. }