diff options
Diffstat (limited to 'MediaBrowser.Server.Implementations/ScheduledTasks/AudioImagesTask.cs')
| -rw-r--r-- | MediaBrowser.Server.Implementations/ScheduledTasks/AudioImagesTask.cs | 172 |
1 files changed, 133 insertions, 39 deletions
diff --git a/MediaBrowser.Server.Implementations/ScheduledTasks/AudioImagesTask.cs b/MediaBrowser.Server.Implementations/ScheduledTasks/AudioImagesTask.cs index 2d10917d3..b9a51ab78 100644 --- a/MediaBrowser.Server.Implementations/ScheduledTasks/AudioImagesTask.cs +++ b/MediaBrowser.Server.Implementations/ScheduledTasks/AudioImagesTask.cs @@ -11,6 +11,8 @@ using System.Collections.Generic; using System.Linq; using System.Threading; using System.Threading.Tasks; +using MediaBrowser.Model.Logging; +using MoreLinq; namespace MediaBrowser.Server.Implementations.ScheduledTasks { @@ -34,25 +36,102 @@ namespace MediaBrowser.Server.Implementations.ScheduledTasks /// </summary> private readonly IMediaEncoder _mediaEncoder; + private readonly ILogger _logger; + + /// <summary> /// The _locks /// </summary> private readonly ConcurrentDictionary<string, SemaphoreSlim> _locks = new ConcurrentDictionary<string, SemaphoreSlim>(); + private readonly List<Audio> _newlyAddedItems = new List<Audio>(); + + private const int NewItemDelay = 300000; + + /// <summary> + /// The current new item timer + /// </summary> + /// <value>The new item timer.</value> + private Timer NewItemTimer { get; set; } + /// <summary> /// Initializes a new instance of the <see cref="AudioImagesTask" /> class. /// </summary> /// <param name="libraryManager">The library manager.</param> /// <param name="mediaEncoder">The media encoder.</param> - public AudioImagesTask(ILibraryManager libraryManager, IMediaEncoder mediaEncoder) + public AudioImagesTask(ILibraryManager libraryManager, IMediaEncoder mediaEncoder, ILogManager logManager) { _libraryManager = libraryManager; _mediaEncoder = mediaEncoder; + _logger = logManager.GetLogger(GetType().Name); ImageCache = new FileSystemRepository(Kernel.Instance.FFMpegManager.AudioImagesDataPath); + + libraryManager.ItemAdded += libraryManager_ItemAdded; + libraryManager.ItemUpdated += libraryManager_ItemAdded; } /// <summary> + /// Handles the ItemAdded event of the libraryManager control. + /// </summary> + /// <param name="sender">The source of the event.</param> + /// <param name="e">The <see cref="ItemChangeEventArgs"/> instance containing the event data.</param> + void libraryManager_ItemAdded(object sender, ItemChangeEventArgs e) + { + var audio = e.Item as Audio; + + if (audio != null) + { + lock (_newlyAddedItems) + { + _newlyAddedItems.Add(audio); + + if (NewItemTimer == null) + { + NewItemTimer = new Timer(NewItemTimerCallback, null, NewItemDelay, Timeout.Infinite); + } + else + { + NewItemTimer.Change(NewItemDelay, Timeout.Infinite); + } + } + } + } + + /// <summary> + /// News the item timer callback. + /// </summary> + /// <param name="state">The state.</param> + private async void NewItemTimerCallback(object state) + { + List<Audio> newSongs; + + // Lock the list and release all resources + lock (_newlyAddedItems) + { + newSongs = _newlyAddedItems.DistinctBy(i => i.Id).ToList(); + _newlyAddedItems.Clear(); + + NewItemTimer.Dispose(); + NewItemTimer = null; + } + + foreach (var item in newSongs + .Where(i => i.LocationType == LocationType.FileSystem && string.IsNullOrEmpty(i.PrimaryImagePath) && i.MediaStreams.Any(m => m.Type == MediaStreamType.Video)) + .Take(20)) + { + try + { + await CreateImagesForSong(item, CancellationToken.None).ConfigureAwait(false); + } + catch (Exception ex) + { + _logger.ErrorException("Error creating image for {0}", ex, item.Name); + } + } + } + + /// <summary> /// Gets the name of the task /// </summary> /// <value>The name.</value> @@ -89,7 +168,7 @@ namespace MediaBrowser.Server.Implementations.ScheduledTasks { var items = _libraryManager.RootFolder.RecursiveChildren .OfType<Audio>() - .Where(i => i.LocationType == LocationType.FileSystem && string.IsNullOrEmpty(i.PrimaryImagePath) && i.MediaStreams != null && i.MediaStreams.Any(m => m.Type == MediaStreamType.Video)) + .Where(i => i.LocationType == LocationType.FileSystem && string.IsNullOrEmpty(i.PrimaryImagePath) && i.MediaStreams.Any(m => m.Type == MediaStreamType.Video)) .ToList(); progress.Report(0); @@ -98,63 +177,78 @@ namespace MediaBrowser.Server.Implementations.ScheduledTasks foreach (var item in items) { - cancellationToken.ThrowIfCancellationRequested(); + try + { + await CreateImagesForSong(item, cancellationToken).ConfigureAwait(false); + } + catch + { + // Already logged at lower levels. + // Just don't let the task fail + } + + numComplete++; + double percent = numComplete; + percent /= items.Count; + + progress.Report(100 * percent); + } + + progress.Report(100); + } + + /// <summary> + /// Creates the images for song. + /// </summary> + /// <param name="item">The item.</param> + /// <param name="cancellationToken">The cancellation token.</param> + /// <returns>Task.</returns> + private async Task CreateImagesForSong(Audio item, CancellationToken cancellationToken) + { + cancellationToken.ThrowIfCancellationRequested(); + + if (item.MediaStreams.All(i => i.Type != MediaStreamType.Video)) + { + throw new InvalidOperationException("Can't extract an image unless the audio file has an embedded image."); + } - var album = item.Parent as MusicAlbum; + var album = item.Parent as MusicAlbum; - var filename = item.Album ?? string.Empty; + var filename = item.Album ?? string.Empty; - filename += album == null ? item.Id.ToString() + item.DateModified.Ticks : album.Id.ToString() + album.DateModified.Ticks; + filename += album == null ? item.Id.ToString() + item.DateModified.Ticks : album.Id.ToString() + album.DateModified.Ticks; - var path = ImageCache.GetResourcePath(filename + "_primary", ".jpg"); + var path = ImageCache.GetResourcePath(filename + "_primary", ".jpg"); - var success = true; + if (!ImageCache.ContainsFilePath(path)) + { + var semaphore = GetLock(path); + // Acquire a lock + await semaphore.WaitAsync(cancellationToken).ConfigureAwait(false); + + // Check again if (!ImageCache.ContainsFilePath(path)) { - var semaphore = GetLock(path); - - // Acquire a lock - await semaphore.WaitAsync(cancellationToken).ConfigureAwait(false); - - // Check again - if (!ImageCache.ContainsFilePath(path)) + try { - try - { - await _mediaEncoder.ExtractImage(new[] { item.Path }, InputType.AudioFile, null, path, cancellationToken).ConfigureAwait(false); - } - catch - { - success = false; - } - finally - { - semaphore.Release(); - } + await _mediaEncoder.ExtractImage(new[] {item.Path}, InputType.AudioFile, null, path, cancellationToken).ConfigureAwait(false); } - else + finally { semaphore.Release(); } - } - numComplete++; - double percent = numComplete; - percent /= items.Count; - - progress.Report(100 * percent); - - if (success) - { // Image is already in the cache item.PrimaryImagePath = path; await _libraryManager.UpdateItem(item, cancellationToken).ConfigureAwait(false); } + else + { + semaphore.Release(); + } } - - progress.Report(100); } /// <summary> |
