using MediaBrowser.Common.IO; using MediaBrowser.Common.MediaInfo; using MediaBrowser.Common.ScheduledTasks; using MediaBrowser.Controller; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities.Movies; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Persistence; using MediaBrowser.Controller.Providers.MediaInfo; using MediaBrowser.Model.Entities; using MediaBrowser.Model.Logging; using MoreLinq; using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.IO; using System.Linq; using System.Threading; using System.Threading.Tasks; namespace MediaBrowser.Server.Implementations.ScheduledTasks { /// /// Class VideoImagesTask /// public class VideoImagesTask : IScheduledTask { /// /// Gets or sets the image cache. /// /// The image cache. public FileSystemRepository ImageCache { get; set; } /// /// The _library manager /// private readonly ILibraryManager _libraryManager; /// /// The _media encoder /// private readonly IMediaEncoder _mediaEncoder; /// /// The _iso manager /// private readonly IIsoManager _isoManager; private readonly IItemRepository _itemRepo; private readonly ILogger _logger; /// /// The _locks /// private readonly ConcurrentDictionary _locks = new ConcurrentDictionary(); private readonly List _newlyAddedItems = new List(); private const int NewItemDelay = 60000; /// /// The current new item timer /// /// The new item timer. private Timer NewItemTimer { get; set; } /// /// Initializes a new instance of the class. /// /// The library manager. /// The log manager. /// The media encoder. /// The iso manager. public VideoImagesTask(ILibraryManager libraryManager, ILogManager logManager, IMediaEncoder mediaEncoder, IIsoManager isoManager, IItemRepository itemRepo) { _libraryManager = libraryManager; _mediaEncoder = mediaEncoder; _isoManager = isoManager; _itemRepo = itemRepo; _logger = logManager.GetLogger(GetType().Name); ImageCache = new FileSystemRepository(Kernel.Instance.FFMpegManager.VideoImagesDataPath); libraryManager.ItemAdded += libraryManager_ItemAdded; libraryManager.ItemUpdated += libraryManager_ItemAdded; } /// /// Handles the ItemAdded event of the libraryManager control. /// /// The source of the event. /// The instance containing the event data. 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); } } } /// /// News the item timer callback. /// /// The state. private async void NewItemTimerCallback(object state) { List 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(3))) { try { await ExtractImage(item, CancellationToken.None).ConfigureAwait(false); } catch (Exception ex) { _logger.ErrorException("Error creating image for {0}", ex, item.Name); } } } /// /// Gets the name of the task /// /// The name. public string Name { get { return "Video image extraction"; } } /// /// Gets the description. /// /// The description. public string Description { get { return "Extracts images from video files that do not have external images."; } } /// /// Gets the category. /// /// The category. public string Category { get { return "Library"; } } /// /// Executes the task /// /// The cancellation token. /// The progress. /// Task. public async Task Execute(CancellationToken cancellationToken, IProgress progress) { 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); } /// /// Gets the items for extraction. /// /// The source items. /// IEnumerable{BaseItem}. private IEnumerable