using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Providers; using MediaBrowser.Model.Entities; using MediaBrowser.Model.Logging; using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Threading; using System.Threading.Tasks; namespace MediaBrowser.Providers { /// /// Provides images for all types by looking for standard images - folder, backdrop, logo, etc. /// public class ImageFromMediaLocationProvider : BaseMetadataProvider { public ImageFromMediaLocationProvider(ILogManager logManager, IServerConfigurationManager configurationManager) : base(logManager, configurationManager) { } public override ItemUpdateType ItemUpdateType { get { return ItemUpdateType.ImageUpdate; } } /// /// Supportses the specified item. /// /// The item. /// true if XXXX, false otherwise public override bool Supports(BaseItem item) { return item.LocationType == LocationType.FileSystem && item.ResolveArgs.IsDirectory; } /// /// Gets the priority. /// /// The priority. public override MetadataProviderPriority Priority { get { return MetadataProviderPriority.First; } } /// /// Returns true or false indicating if the provider should refresh when the contents of it's directory changes /// /// true if [refresh on file system stamp change]; otherwise, false. protected override bool RefreshOnFileSystemStampChange { get { return true; } } /// /// Gets the filestamp extensions. /// /// The filestamp extensions. protected override string[] FilestampExtensions { get { return BaseItem.SupportedImageExtensions; } } /// /// Fetches metadata and returns true or false indicating if any work that requires persistence was done /// /// The item. /// if set to true [force]. /// The cancellation token. /// Task{System.Boolean}. public override Task FetchAsync(BaseItem item, bool force, CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested(); // Make sure current image paths still exist ValidateImages(item); cancellationToken.ThrowIfCancellationRequested(); // Make sure current backdrop paths still exist ValidateBackdrops(item); cancellationToken.ThrowIfCancellationRequested(); PopulateBaseItemImages(item); SetLastRefreshed(item, DateTime.UtcNow); return TrueTaskResult; } /// /// Validates that images within the item are still on the file system /// /// The item. private void ValidateImages(BaseItem item) { // Only validate paths from the same directory - need to copy to a list because we are going to potentially modify the collection below var deletedKeys = item.Images.ToList().Where(image => { var path = image.Value; return IsInMetaLocation(item, path) && item.ResolveArgs.GetMetaFileByPath(path) == null; }).Select(i => i.Key).ToList(); // Now remove them from the dictionary foreach (var key in deletedKeys) { item.Images.Remove(key); } } /// /// Validates that backdrops within the item are still on the file system /// /// The item. private void ValidateBackdrops(BaseItem item) { if (item.BackdropImagePaths == null) { return; } // Only validate paths from the same directory - need to copy to a list because we are going to potentially modify the collection below var deletedImages = item.BackdropImagePaths.Where(path => IsInMetaLocation(item, path) && item.ResolveArgs.GetMetaFileByPath(path) == null).ToList(); // Now remove them from the dictionary foreach (var path in deletedImages) { item.BackdropImagePaths.Remove(path); } } /// /// Determines whether [is in same directory] [the specified item]. /// /// The item. /// The path. /// true if [is in same directory] [the specified item]; otherwise, false. private bool IsInMetaLocation(BaseItem item, string path) { return string.Equals(Path.GetDirectoryName(path), item.MetaLocation, StringComparison.OrdinalIgnoreCase); } /// /// Gets the image. /// /// The item. /// The filename without extension. /// FileSystemInfo. protected virtual FileSystemInfo GetImage(BaseItem item, string filenameWithoutExtension) { return BaseItem.SupportedImageExtensions.Select(i => item.ResolveArgs.GetMetaFileByPath(Path.Combine(item.ResolveArgs.Path, filenameWithoutExtension + i))).FirstOrDefault(i => i != null); } /// /// Fills in image paths based on files win the folder /// /// The item. private void PopulateBaseItemImages(BaseItem item) { // Primary Image var image = GetImage(item, "folder") ?? GetImage(item, "poster") ?? GetImage(item, "cover") ?? GetImage(item, "default"); // Look for a file with the same name as the item if (image == null) { var name = Path.GetFileNameWithoutExtension(item.Path); if (!string.IsNullOrEmpty(name)) { image = GetImage(item, name); } } if (image != null) { item.SetImage(ImageType.Primary, image.FullName); } // Logo Image image = GetImage(item, "logo"); if (image != null) { item.SetImage(ImageType.Logo, image.FullName); } // Banner Image image = GetImage(item, "banner"); if (image != null) { item.SetImage(ImageType.Banner, image.FullName); } // Clearart image = GetImage(item, "clearart"); if (image != null) { item.SetImage(ImageType.Art, image.FullName); } // Thumbnail Image image = GetImage(item, "thumb"); if (image != null) { item.SetImage(ImageType.Thumb, image.FullName); } // Box Image image = GetImage(item, "box"); if (image != null) { item.SetImage(ImageType.Box, image.FullName); } // BoxRear Image image = GetImage(item, "boxrear"); if (image != null) { item.SetImage(ImageType.BoxRear, image.FullName); } // Thumbnail Image image = GetImage(item, "menu"); if (image != null) { item.SetImage(ImageType.Menu, image.FullName); } // Backdrop Image PopulateBackdrops(item); // Screenshot Image image = GetImage(item, "screenshot"); var screenshotFiles = new List(); if (image != null) { screenshotFiles.Add(image.FullName); } var unfound = 0; for (var i = 1; i <= 20; i++) { // Screenshot Image image = GetImage(item, "screenshot" + i); if (image != null) { screenshotFiles.Add(image.FullName); } else { unfound++; if (unfound >= 3) { break; } } } if (screenshotFiles.Count > 0) { item.ScreenshotImagePaths = screenshotFiles; } } /// /// Populates the backdrops. /// /// The item. private void PopulateBackdrops(BaseItem item) { var backdropFiles = new List(); PopulateBackdrops(item, backdropFiles, "backdrop", "backdrop"); // Support plex/xbmc conventions PopulateBackdrops(item, backdropFiles, "fanart", "fanart-"); PopulateBackdrops(item, backdropFiles, "background", "background-"); if (backdropFiles.Count > 0) { item.BackdropImagePaths = backdropFiles; } } /// /// Populates the backdrops. /// /// The item. /// The backdrop files. /// The filename. /// The numbered suffix. private void PopulateBackdrops(BaseItem item, List backdropFiles, string filename, string numberedSuffix) { var image = GetImage(item, filename); if (image != null) { backdropFiles.Add(image.FullName); } var unfound = 0; for (var i = 1; i <= 20; i++) { // Backdrop Image image = GetImage(item, numberedSuffix + i); if (image != null) { backdropFiles.Add(image.FullName); } else { unfound++; if (unfound >= 3) { break; } } } } } }