diff options
| author | Luke Pulverenti <luke.pulverenti@gmail.com> | 2013-05-03 11:08:02 -0400 |
|---|---|---|
| committer | Luke Pulverenti <luke.pulverenti@gmail.com> | 2013-05-03 11:08:02 -0400 |
| commit | 43845b40523f91e7816f6ea9e250a93c6f648f4f (patch) | |
| tree | 0e22c2a18e468f1d9aa45ae60e6b375bc4ee0e3f /MediaBrowser.Server.Implementations/ScheduledTasks/VideoImagesTask.cs | |
| parent | ceaae430c62c37996de131733b60cfd3f14fe31b (diff) | |
extract images for small numbers of items on discovery
Diffstat (limited to 'MediaBrowser.Server.Implementations/ScheduledTasks/VideoImagesTask.cs')
| -rw-r--r-- | MediaBrowser.Server.Implementations/ScheduledTasks/VideoImagesTask.cs | 184 |
1 files changed, 138 insertions, 46 deletions
diff --git a/MediaBrowser.Server.Implementations/ScheduledTasks/VideoImagesTask.cs b/MediaBrowser.Server.Implementations/ScheduledTasks/VideoImagesTask.cs index c1f3173ee..d7863c0ba 100644 --- a/MediaBrowser.Server.Implementations/ScheduledTasks/VideoImagesTask.cs +++ b/MediaBrowser.Server.Implementations/ScheduledTasks/VideoImagesTask.cs @@ -13,6 +13,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 { @@ -41,27 +43,97 @@ namespace MediaBrowser.Server.Implementations.ScheduledTasks /// </summary> private readonly IIsoManager _isoManager; + private readonly ILogger _logger; + /// <summary> /// The _locks /// </summary> private readonly ConcurrentDictionary<string, SemaphoreSlim> _locks = new ConcurrentDictionary<string, SemaphoreSlim>(); + private readonly List<BaseItem> _newlyAddedItems = new List<BaseItem>(); + + 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="logManager">The log manager.</param> /// <param name="mediaEncoder">The media encoder.</param> /// <param name="isoManager">The iso manager.</param> - public VideoImagesTask(ILibraryManager libraryManager, IMediaEncoder mediaEncoder, IIsoManager isoManager) + public VideoImagesTask(ILibraryManager libraryManager, ILogManager logManager, IMediaEncoder mediaEncoder, IIsoManager isoManager) { _libraryManager = libraryManager; _mediaEncoder = mediaEncoder; _isoManager = isoManager; + _logger = logManager.GetLogger(GetType().Name); ImageCache = new FileSystemRepository(Kernel.Instance.FFMpegManager.VideoImagesDataPath); + + 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) + { + lock (_newlyAddedItems) + { + _newlyAddedItems.Add(e.Item); + + 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<BaseItem> newItems; + + // Lock the list and release all resources + lock (_newlyAddedItems) + { + newItems = _newlyAddedItems.DistinctBy(i => i.Id).ToList(); + _newlyAddedItems.Clear(); + + NewItemTimer.Dispose(); + NewItemTimer = null; + } + + foreach (var item in GetItemsForExtraction(newItems.Take(5))) + { + try + { + await ExtractImage(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> @@ -96,7 +168,42 @@ namespace MediaBrowser.Server.Implementations.ScheduledTasks /// <returns>Task.</returns> public async Task Execute(CancellationToken cancellationToken, IProgress<double> progress) { - var allItems = _libraryManager.RootFolder.RecursiveChildren.ToList(); + var items = GetItemsForExtraction(_libraryManager.RootFolder.RecursiveChildren).ToList(); + + progress.Report(0); + + var numComplete = 0; + + foreach (var item in items) + { + try + { + await ExtractImage(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> + /// Gets the items for extraction. + /// </summary> + /// <param name="sourceItems">The source items.</param> + /// <returns>IEnumerable{BaseItem}.</returns> + private IEnumerable<Video> GetItemsForExtraction(IEnumerable<BaseItem> sourceItems) + { + var allItems = sourceItems.ToList(); var localTrailers = allItems.SelectMany(i => i.LocalTrailers); var themeVideos = allItems.SelectMany(i => i.ThemeVideos); @@ -108,7 +215,7 @@ namespace MediaBrowser.Server.Implementations.ScheduledTasks items.AddRange(themeVideos); items.AddRange(videos.OfType<Movie>().SelectMany(i => i.SpecialFeatures).ToList()); - items = items.Where(i => + return items.Where(i => { if (!string.IsNullOrEmpty(i.PrimaryImagePath)) { @@ -131,69 +238,54 @@ namespace MediaBrowser.Server.Implementations.ScheduledTasks } return i.MediaStreams != null && i.MediaStreams.Any(m => m.Type == MediaStreamType.Video); - }).ToList(); - - progress.Report(0); + }); + } - var numComplete = 0; + /// <summary> + /// Extracts the image. + /// </summary> + /// <param name="item">The item.</param> + /// <param name="cancellationToken">The cancellation token.</param> + /// <returns>Task.</returns> + private async Task ExtractImage(Video item, CancellationToken cancellationToken) + { + cancellationToken.ThrowIfCancellationRequested(); - foreach (var item in items) - { - cancellationToken.ThrowIfCancellationRequested(); + var filename = item.Id + "_" + item.DateModified.Ticks + "_primary"; - var filename = item.Id + "_" + item.DateModified.Ticks + "_primary"; + var path = ImageCache.GetResourcePath(filename, ".jpg"); - var path = ImageCache.GetResourcePath(filename, ".jpg"); + if (!ImageCache.ContainsFilePath(path)) + { + var semaphore = GetLock(path); - var success = true; + // 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 ExtractImage(item, path, cancellationToken).ConfigureAwait(false); - } - catch - { - success = false; - } - finally - { - semaphore.Release(); - } + await ExtractImageInternal(item, 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> /// Extracts the image. /// </summary> @@ -201,7 +293,7 @@ namespace MediaBrowser.Server.Implementations.ScheduledTasks /// <param name="path">The path.</param> /// <param name="cancellationToken">The cancellation token.</param> /// <returns>Task.</returns> - private async Task ExtractImage(Video video, string path, CancellationToken cancellationToken) + private async Task ExtractImageInternal(Video video, string path, CancellationToken cancellationToken) { var isoMount = await MountIsoIfNeeded(video, cancellationToken).ConfigureAwait(false); |
