FFMpegAudioImageProvider.cs 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128
  1. using System.Collections.Concurrent;
  2. using MediaBrowser.Controller.Configuration;
  3. using MediaBrowser.Controller.Entities;
  4. using MediaBrowser.Controller.Entities.Audio;
  5. using MediaBrowser.Model.Entities;
  6. using MediaBrowser.Model.Logging;
  7. using System;
  8. using System.Linq;
  9. using System.Threading;
  10. using System.Threading.Tasks;
  11. namespace MediaBrowser.Controller.Providers.MediaInfo
  12. {
  13. /// <summary>
  14. /// Uses ffmpeg to create video images
  15. /// </summary>
  16. public class FFMpegAudioImageProvider : BaseFFMpegProvider<Audio>
  17. {
  18. public FFMpegAudioImageProvider(ILogManager logManager, IServerConfigurationManager configurationManager)
  19. : base(logManager, configurationManager)
  20. {
  21. }
  22. /// <summary>
  23. /// The true task result
  24. /// </summary>
  25. protected static readonly Task<bool> TrueTaskResult = Task.FromResult(true);
  26. /// <summary>
  27. /// Gets the priority.
  28. /// </summary>
  29. /// <value>The priority.</value>
  30. public override MetadataProviderPriority Priority
  31. {
  32. get { return MetadataProviderPriority.Last; }
  33. }
  34. /// <summary>
  35. /// The _locks
  36. /// </summary>
  37. private readonly ConcurrentDictionary<string, SemaphoreSlim> _locks = new ConcurrentDictionary<string, SemaphoreSlim>();
  38. /// <summary>
  39. /// Gets the lock.
  40. /// </summary>
  41. /// <param name="filename">The filename.</param>
  42. /// <returns>System.Object.</returns>
  43. private SemaphoreSlim GetLock(string filename)
  44. {
  45. return _locks.GetOrAdd(filename, key => new SemaphoreSlim(1, 1));
  46. }
  47. /// <summary>
  48. /// Fetches metadata and returns true or false indicating if any work that requires persistence was done
  49. /// </summary>
  50. /// <param name="item">The item.</param>
  51. /// <param name="force">if set to <c>true</c> [force].</param>
  52. /// <param name="cancellationToken">The cancellation token.</param>
  53. /// <returns>Task{System.Boolean}.</returns>
  54. public override async Task<bool> FetchAsync(BaseItem item, bool force, CancellationToken cancellationToken)
  55. {
  56. var success = ProviderRefreshStatus.Success;
  57. if (force || string.IsNullOrEmpty(item.PrimaryImagePath))
  58. {
  59. var album = item.ResolveArgs.Parent as MusicAlbum;
  60. if (album != null)
  61. {
  62. // First try to use the parent's image
  63. item.PrimaryImagePath = item.ResolveArgs.Parent.PrimaryImagePath;
  64. }
  65. // If it's still empty see if there's an embedded image
  66. if (force || string.IsNullOrEmpty(item.PrimaryImagePath))
  67. {
  68. var audio = (Audio)item;
  69. if (audio.MediaStreams != null && audio.MediaStreams.Any(s => s.Type == MediaStreamType.Video))
  70. {
  71. var filename = album != null && string.IsNullOrEmpty(audio.Album + album.DateModified.Ticks) ? (audio.Id.ToString() + audio.DateModified.Ticks) : audio.Album;
  72. var path = Kernel.Instance.FFMpegManager.AudioImageCache.GetResourcePath(filename + "_primary", ".jpg");
  73. if (!Kernel.Instance.FFMpegManager.AudioImageCache.ContainsFilePath(path))
  74. {
  75. var semaphore = GetLock(path);
  76. // Acquire a lock
  77. await semaphore.WaitAsync(cancellationToken).ConfigureAwait(false);
  78. // Check again
  79. if (!Kernel.Instance.FFMpegManager.AudioImageCache.ContainsFilePath(path))
  80. {
  81. try
  82. {
  83. var imageSucceeded = await Kernel.Instance.FFMpegManager.ExtractAudioImage(audio.Path, path, cancellationToken).ConfigureAwait(false);
  84. if (!imageSucceeded)
  85. {
  86. success = ProviderRefreshStatus.Failure;
  87. }
  88. }
  89. finally
  90. {
  91. semaphore.Release();
  92. }
  93. }
  94. else
  95. {
  96. semaphore.Release();
  97. }
  98. }
  99. if (success == ProviderRefreshStatus.Success)
  100. {
  101. // Image is already in the cache
  102. audio.PrimaryImagePath = path;
  103. }
  104. }
  105. }
  106. }
  107. SetLastRefreshed(item, DateTime.UtcNow, success);
  108. return true;
  109. }
  110. }
  111. }