diff options
Diffstat (limited to 'MediaBrowser.Server.Implementations')
13 files changed, 51 insertions, 1072 deletions
diff --git a/MediaBrowser.Server.Implementations/Drawing/ImageProcessor.cs b/MediaBrowser.Server.Implementations/Drawing/ImageProcessor.cs index 6378cef52..06a03ba1c 100644 --- a/MediaBrowser.Server.Implementations/Drawing/ImageProcessor.cs +++ b/MediaBrowser.Server.Implementations/Drawing/ImageProcessor.cs @@ -388,18 +388,18 @@ namespace MediaBrowser.Server.Implementations.Drawing /// <param name="image">The image.</param> /// <param name="outputFormat">The output format.</param> /// <returns>ImageFormat.</returns> - private ImageFormat GetOutputFormat(Image image, ImageOutputFormat outputFormat) + private System.Drawing.Imaging.ImageFormat GetOutputFormat(Image image, ImageOutputFormat outputFormat) { switch (outputFormat) { case ImageOutputFormat.Bmp: - return ImageFormat.Bmp; + return System.Drawing.Imaging.ImageFormat.Bmp; case ImageOutputFormat.Gif: - return ImageFormat.Gif; + return System.Drawing.Imaging.ImageFormat.Gif; case ImageOutputFormat.Jpg: - return ImageFormat.Jpeg; + return System.Drawing.Imaging.ImageFormat.Jpeg; case ImageOutputFormat.Png: - return ImageFormat.Png; + return System.Drawing.Imaging.ImageFormat.Png; default: return image.RawFormat; } @@ -787,7 +787,7 @@ namespace MediaBrowser.Server.Implementations.Drawing //And then save it in the cache using (var outputStream = _fileSystem.GetFileStream(enhancedImagePath, FileMode.Create, FileAccess.Write, FileShare.Read, false)) { - newImage.Save(ImageFormat.Png, outputStream, 100); + newImage.Save(System.Drawing.Imaging.ImageFormat.Png, outputStream, 100); } } } diff --git a/MediaBrowser.Server.Implementations/Dto/DtoService.cs b/MediaBrowser.Server.Implementations/Dto/DtoService.cs index 7a9735e0e..b427b0c45 100644 --- a/MediaBrowser.Server.Implementations/Dto/DtoService.cs +++ b/MediaBrowser.Server.Implementations/Dto/DtoService.cs @@ -1024,6 +1024,11 @@ namespace MediaBrowser.Server.Implementations.Dto { dto.SpecialFeatureCount = specialFeatureCount; } + + if (fields.Contains(ItemFields.TmdbCollectionName)) + { + dto.TmdbCollectionName = movie.TmdbCollectionName; + } } // Add EpisodeInfo diff --git a/MediaBrowser.Server.Implementations/Library/ResolverHelper.cs b/MediaBrowser.Server.Implementations/Library/ResolverHelper.cs index e32fcd627..4ce5f11d4 100644 --- a/MediaBrowser.Server.Implementations/Library/ResolverHelper.cs +++ b/MediaBrowser.Server.Implementations/Library/ResolverHelper.cs @@ -46,7 +46,7 @@ namespace MediaBrowser.Server.Implementations.Library } // Make sure the item has a name - EnsureName(item); + EnsureName(item, args); item.DontFetchMeta = item.Path.IndexOf("[dontfetchmeta]", StringComparison.OrdinalIgnoreCase) != -1 || item.Parents.Any(i => i.DontFetchMeta); @@ -59,13 +59,13 @@ namespace MediaBrowser.Server.Implementations.Library /// Ensures the name. /// </summary> /// <param name="item">The item.</param> - private static void EnsureName(BaseItem item) + private static void EnsureName(BaseItem item, ItemResolveArgs args) { // If the subclass didn't supply a name, add it here if (string.IsNullOrEmpty(item.Name) && !string.IsNullOrEmpty(item.Path)) { //we use our resolve args name here to get the name of the containg folder, not actual video file - item.Name = GetMBName(item.ResolveArgs.FileInfo.Name, (item.ResolveArgs.FileInfo.Attributes & FileAttributes.Directory) == FileAttributes.Directory); + item.Name = GetMBName(args.FileInfo.Name, (args.FileInfo.Attributes & FileAttributes.Directory) == FileAttributes.Directory); } } diff --git a/MediaBrowser.Server.Implementations/Library/UserManager.cs b/MediaBrowser.Server.Implementations/Library/UserManager.cs index d4a74f2b6..ce76dd21b 100644 --- a/MediaBrowser.Server.Implementations/Library/UserManager.cs +++ b/MediaBrowser.Server.Implementations/Library/UserManager.cs @@ -4,6 +4,7 @@ using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Persistence; +using MediaBrowser.Controller.Providers; using MediaBrowser.Model.Logging; using System; using System.Collections.Generic; @@ -192,7 +193,11 @@ namespace MediaBrowser.Server.Implementations.Library /// <returns>Task.</returns> public Task RefreshUsersMetadata(CancellationToken cancellationToken, bool force = false) { - var tasks = Users.Select(user => user.RefreshMetadata(cancellationToken, forceRefresh: force)).ToList(); + var tasks = Users.Select(user => user.RefreshMetadata(new MetadataRefreshOptions + { + ReplaceAllMetadata = force + + }, cancellationToken)).ToList(); return Task.WhenAll(tasks); } diff --git a/MediaBrowser.Server.Implementations/Library/Validators/GenresPostScanTask.cs b/MediaBrowser.Server.Implementations/Library/Validators/GenresPostScanTask.cs index b4907a70c..d7add8574 100644 --- a/MediaBrowser.Server.Implementations/Library/Validators/GenresPostScanTask.cs +++ b/MediaBrowser.Server.Implementations/Library/Validators/GenresPostScanTask.cs @@ -16,7 +16,6 @@ namespace MediaBrowser.Server.Implementations.Library.Validators /// Initializes a new instance of the <see cref="ArtistsPostScanTask" /> class. /// </summary> /// <param name="libraryManager">The library manager.</param> - /// <param name="userManager">The user manager.</param> public GenresPostScanTask(ILibraryManager libraryManager) { _libraryManager = libraryManager; diff --git a/MediaBrowser.Server.Implementations/Library/Validators/PeoplePostScanTask.cs b/MediaBrowser.Server.Implementations/Library/Validators/PeoplePostScanTask.cs index 0104b2b7e..c8094302c 100644 --- a/MediaBrowser.Server.Implementations/Library/Validators/PeoplePostScanTask.cs +++ b/MediaBrowser.Server.Implementations/Library/Validators/PeoplePostScanTask.cs @@ -1,5 +1,6 @@ using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Library; +using MediaBrowser.Controller.Providers; using MediaBrowser.Model.Logging; using System; using System.Collections.Generic; @@ -88,7 +89,14 @@ namespace MediaBrowser.Server.Implementations.Library.Validators var itemByName = _libraryManager.GetPerson(name); - await itemByName.RefreshMetadata(cancellationToken, allowSlowProviders: false).ConfigureAwait(false); + // The only purpose here is to be able to react to image changes without running the people task. + // All other metadata can wait for that. + await itemByName.RefreshMetadata(new MetadataRefreshOptions + { + ImageRefreshMode = MetadataRefreshMode.None, + MetadataRefreshMode = MetadataRefreshMode.None + + }, cancellationToken).ConfigureAwait(false); foreach (var libraryId in counts.Keys) { diff --git a/MediaBrowser.Server.Implementations/LiveTv/ChannelImageProvider.cs b/MediaBrowser.Server.Implementations/LiveTv/ChannelImageProvider.cs index f1e10e175..c1d8d796b 100644 --- a/MediaBrowser.Server.Implementations/LiveTv/ChannelImageProvider.cs +++ b/MediaBrowser.Server.Implementations/LiveTv/ChannelImageProvider.cs @@ -116,7 +116,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv if (response != null) { imageStream = response.Stream; - contentType = response.MimeType; + contentType = "image/" + response.Format.ToString().ToLower(); } } catch (NotImplementedException) diff --git a/MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs b/MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs index e256d7da5..9501d2d12 100644 --- a/MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs +++ b/MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs @@ -9,6 +9,7 @@ using MediaBrowser.Controller.Library; using MediaBrowser.Controller.LiveTv; using MediaBrowser.Controller.MediaInfo; using MediaBrowser.Controller.Persistence; +using MediaBrowser.Controller.Providers; using MediaBrowser.Controller.Sorting; using MediaBrowser.Model.Entities; using MediaBrowser.Model.LiveTv; @@ -328,7 +329,12 @@ namespace MediaBrowser.Server.Implementations.LiveTv // Set this now so we don't cause additional file system access during provider executions item.ResetResolveArgs(fileInfo); - await item.RefreshMetadata(cancellationToken, forceSave: isNew, resetResolveArgs: false); + await item.RefreshMetadata(new MetadataRefreshOptions + { + ForceSave = isNew, + ResetResolveArgs = false + + }, cancellationToken); return item; } @@ -383,7 +389,12 @@ namespace MediaBrowser.Server.Implementations.LiveTv item.RunTimeTicks = (info.EndDate - info.StartDate).Ticks; item.StartDate = info.StartDate; - await item.RefreshMetadata(cancellationToken, forceSave: isNew, resetResolveArgs: false); + await item.RefreshMetadata(new MetadataRefreshOptions + { + ForceSave = isNew, + ResetResolveArgs = false + + }, cancellationToken); return item; } @@ -435,7 +446,12 @@ namespace MediaBrowser.Server.Implementations.LiveTv item.RecordingInfo = info; item.ServiceName = serviceName; - await item.RefreshMetadata(cancellationToken, forceSave: isNew, resetResolveArgs: false); + await item.RefreshMetadata(new MetadataRefreshOptions + { + ForceSave = isNew, + ResetResolveArgs = false + + }, cancellationToken); _libraryManager.RegisterItem((BaseItem)item); diff --git a/MediaBrowser.Server.Implementations/LiveTv/ProgramImageProvider.cs b/MediaBrowser.Server.Implementations/LiveTv/ProgramImageProvider.cs index 041925cdd..3bf2712d4 100644 --- a/MediaBrowser.Server.Implementations/LiveTv/ProgramImageProvider.cs +++ b/MediaBrowser.Server.Implementations/LiveTv/ProgramImageProvider.cs @@ -116,7 +116,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv if (response != null) { imageStream = response.Stream; - contentType = response.MimeType; + contentType = "image/" + response.Format.ToString().ToLower(); } } catch (NotImplementedException) diff --git a/MediaBrowser.Server.Implementations/LiveTv/RecordingImageProvider.cs b/MediaBrowser.Server.Implementations/LiveTv/RecordingImageProvider.cs index 9f6ab85a4..ce7c1286b 100644 --- a/MediaBrowser.Server.Implementations/LiveTv/RecordingImageProvider.cs +++ b/MediaBrowser.Server.Implementations/LiveTv/RecordingImageProvider.cs @@ -118,7 +118,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv if (response != null) { imageStream = response.Stream; - contentType = response.MimeType; + contentType = "image/" + response.Format.ToString().ToLower(); } } catch (NotImplementedException) diff --git a/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj b/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj index 314e7a458..a92ec29d5 100644 --- a/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj +++ b/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj @@ -189,8 +189,6 @@ <Compile Include="Persistence\SqliteShrinkMemoryTimer.cs" /> <Compile Include="Persistence\TypeMapper.cs" /> <Compile Include="Properties\AssemblyInfo.cs" /> - <Compile Include="Providers\ImageSaver.cs" /> - <Compile Include="Providers\ProviderManager.cs" /> <Compile Include="Roku\RokuControllerFactory.cs" /> <Compile Include="ScheduledTasks\PeopleValidationTask.cs" /> <Compile Include="ScheduledTasks\ChapterImagesTask.cs" /> diff --git a/MediaBrowser.Server.Implementations/Providers/ImageSaver.cs b/MediaBrowser.Server.Implementations/Providers/ImageSaver.cs deleted file mode 100644 index ec797b688..000000000 --- a/MediaBrowser.Server.Implementations/Providers/ImageSaver.cs +++ /dev/null @@ -1,598 +0,0 @@ -using MediaBrowser.Common.IO; -using MediaBrowser.Controller.Configuration; -using MediaBrowser.Controller.Entities; -using MediaBrowser.Controller.Entities.Audio; -using MediaBrowser.Controller.Entities.TV; -using MediaBrowser.Controller.IO; -using MediaBrowser.Model.Configuration; -using MediaBrowser.Model.Entities; -using MediaBrowser.Model.Logging; -using System; -using System.Collections.Generic; -using System.Globalization; -using System.IO; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; - -namespace MediaBrowser.Server.Implementations.Providers -{ - /// <summary> - /// Class ImageSaver - /// </summary> - public class ImageSaver - { - private static readonly CultureInfo UsCulture = new CultureInfo("en-US"); - - /// <summary> - /// The _config - /// </summary> - private readonly IServerConfigurationManager _config; - - /// <summary> - /// The remote image cache - /// </summary> - private readonly FileSystemRepository _remoteImageCache; - /// <summary> - /// The _directory watchers - /// </summary> - private readonly IDirectoryWatchers _directoryWatchers; - private readonly IFileSystem _fileSystem; - private readonly ILogger _logger; - - /// <summary> - /// Initializes a new instance of the <see cref="ImageSaver"/> class. - /// </summary> - /// <param name="config">The config.</param> - /// <param name="directoryWatchers">The directory watchers.</param> - public ImageSaver(IServerConfigurationManager config, IDirectoryWatchers directoryWatchers, IFileSystem fileSystem, ILogger logger) - { - _config = config; - _directoryWatchers = directoryWatchers; - _fileSystem = fileSystem; - _logger = logger; - _remoteImageCache = new FileSystemRepository(config.ApplicationPaths.DownloadedImagesDataPath); - } - - /// <summary> - /// Saves the image. - /// </summary> - /// <param name="item">The item.</param> - /// <param name="source">The source.</param> - /// <param name="mimeType">Type of the MIME.</param> - /// <param name="type">The type.</param> - /// <param name="imageIndex">Index of the image.</param> - /// <param name="sourceUrl">The source URL.</param> - /// <param name="cancellationToken">The cancellation token.</param> - /// <returns>Task.</returns> - /// <exception cref="System.ArgumentNullException">mimeType</exception> - public async Task SaveImage(BaseItem item, Stream source, string mimeType, ImageType type, int? imageIndex, string sourceUrl, CancellationToken cancellationToken) - { - if (string.IsNullOrEmpty(mimeType)) - { - throw new ArgumentNullException("mimeType"); - } - - var saveLocally = item.IsSaveLocalMetadataEnabled() && item.Parent != null && !(item is Audio); - - if (item is IItemByName || item is User) - { - saveLocally = true; - } - - if (type != ImageType.Primary && item is Episode) - { - saveLocally = false; - } - - var locationType = item.LocationType; - if (locationType == LocationType.Remote || locationType == LocationType.Virtual) - { - saveLocally = false; - - var season = item as Season; - - // If season is virtual under a physical series, save locally if using compatible convention - if (season != null && _config.Configuration.ImageSavingConvention == ImageSavingConvention.Compatible) - { - var series = season.Series; - - if (series != null) - { - var seriesLocationType = series.LocationType; - if (seriesLocationType == LocationType.FileSystem || seriesLocationType == LocationType.Offline) - { - saveLocally = true; - } - } - } - } - - if (type == ImageType.Backdrop && imageIndex == null) - { - imageIndex = item.BackdropImagePaths.Count; - } - else if (type == ImageType.Screenshot && imageIndex == null) - { - var hasScreenshots = (IHasScreenshots)item; - imageIndex = hasScreenshots.ScreenshotImagePaths.Count; - } - - var index = imageIndex ?? 0; - - var paths = GetSavePaths(item, type, imageIndex, mimeType, saveLocally); - - // If there are more than one output paths, the stream will need to be seekable - if (paths.Length > 1 && !source.CanSeek) - { - var memoryStream = new MemoryStream(); - using (source) - { - await source.CopyToAsync(memoryStream).ConfigureAwait(false); - } - memoryStream.Position = 0; - source = memoryStream; - } - - var currentPath = GetCurrentImagePath(item, type, index); - - using (source) - { - var isFirst = true; - - foreach (var path in paths) - { - // Seek back to the beginning - if (!isFirst) - { - source.Position = 0; - } - - await SaveImageToLocation(source, path, cancellationToken).ConfigureAwait(false); - - isFirst = false; - } - } - - // Set the path into the item - SetImagePath(item, type, imageIndex, paths[0], sourceUrl); - - // Delete the current path - if (!string.IsNullOrEmpty(currentPath) && !paths.Contains(currentPath, StringComparer.OrdinalIgnoreCase)) - { - _directoryWatchers.TemporarilyIgnore(currentPath); - - try - { - var currentFile = new FileInfo(currentPath); - - // This will fail if the file is hidden - if (currentFile.Exists) - { - if ((currentFile.Attributes & FileAttributes.Hidden) == FileAttributes.Hidden) - { - currentFile.Attributes &= ~FileAttributes.Hidden; - } - - currentFile.Delete(); - } - } - finally - { - _directoryWatchers.RemoveTempIgnore(currentPath); - } - } - } - - /// <summary> - /// Saves the image to location. - /// </summary> - /// <param name="source">The source.</param> - /// <param name="path">The path.</param> - /// <param name="cancellationToken">The cancellation token.</param> - /// <returns>Task.</returns> - private async Task SaveImageToLocation(Stream source, string path, CancellationToken cancellationToken) - { - _logger.Debug("Saving image to {0}", path); - - var parentFolder = Path.GetDirectoryName(path); - - _directoryWatchers.TemporarilyIgnore(path); - _directoryWatchers.TemporarilyIgnore(parentFolder); - - try - { - Directory.CreateDirectory(Path.GetDirectoryName(path)); - - // If the file is currently hidden we'll have to remove that or the save will fail - var file = new FileInfo(path); - - // This will fail if the file is hidden - if (file.Exists) - { - if ((file.Attributes & FileAttributes.Hidden) == FileAttributes.Hidden) - { - file.Attributes &= ~FileAttributes.Hidden; - } - } - - using (var fs = _fileSystem.GetFileStream(path, FileMode.Create, FileAccess.Write, FileShare.Read, true)) - { - await source.CopyToAsync(fs, StreamDefaults.DefaultCopyToBufferSize, cancellationToken).ConfigureAwait(false); - } - } - finally - { - _directoryWatchers.RemoveTempIgnore(path); - _directoryWatchers.RemoveTempIgnore(parentFolder); - } - } - - /// <summary> - /// Gets the save paths. - /// </summary> - /// <param name="item">The item.</param> - /// <param name="type">The type.</param> - /// <param name="imageIndex">Index of the image.</param> - /// <param name="mimeType">Type of the MIME.</param> - /// <param name="saveLocally">if set to <c>true</c> [save locally].</param> - /// <returns>IEnumerable{System.String}.</returns> - private string[] GetSavePaths(BaseItem item, ImageType type, int? imageIndex, string mimeType, bool saveLocally) - { - if (_config.Configuration.ImageSavingConvention == ImageSavingConvention.Legacy || !saveLocally) - { - return new[] { GetStandardSavePath(item, type, imageIndex, mimeType, saveLocally) }; - } - - return GetCompatibleSavePaths(item, type, imageIndex, mimeType); - } - - /// <summary> - /// Gets the current image path. - /// </summary> - /// <param name="item">The item.</param> - /// <param name="type">The type.</param> - /// <param name="imageIndex">Index of the image.</param> - /// <returns>System.String.</returns> - /// <exception cref="System.ArgumentNullException"> - /// imageIndex - /// or - /// imageIndex - /// </exception> - private string GetCurrentImagePath(IHasImages item, ImageType type, int imageIndex) - { - return item.GetImagePath(type, imageIndex); - } - - /// <summary> - /// Sets the image path. - /// </summary> - /// <param name="item">The item.</param> - /// <param name="type">The type.</param> - /// <param name="imageIndex">Index of the image.</param> - /// <param name="path">The path.</param> - /// <param name="sourceUrl">The source URL.</param> - /// <exception cref="System.ArgumentNullException">imageIndex - /// or - /// imageIndex</exception> - private void SetImagePath(BaseItem item, ImageType type, int? imageIndex, string path, string sourceUrl) - { - switch (type) - { - case ImageType.Screenshot: - - if (!imageIndex.HasValue) - { - throw new ArgumentNullException("imageIndex"); - } - - var hasScreenshots = (IHasScreenshots)item; - if (hasScreenshots.ScreenshotImagePaths.Count > imageIndex.Value) - { - hasScreenshots.ScreenshotImagePaths[imageIndex.Value] = path; - } - else if (!hasScreenshots.ScreenshotImagePaths.Contains(path, StringComparer.OrdinalIgnoreCase)) - { - hasScreenshots.ScreenshotImagePaths.Add(path); - } - break; - case ImageType.Backdrop: - if (!imageIndex.HasValue) - { - throw new ArgumentNullException("imageIndex"); - } - if (item.BackdropImagePaths.Count > imageIndex.Value) - { - item.BackdropImagePaths[imageIndex.Value] = path; - } - else if (!item.BackdropImagePaths.Contains(path, StringComparer.OrdinalIgnoreCase)) - { - item.BackdropImagePaths.Add(path); - } - - if (string.IsNullOrEmpty(sourceUrl)) - { - item.RemoveImageSourceForPath(path); - } - else - { - item.AddImageSource(path, sourceUrl); - } - break; - default: - item.SetImagePath(type, path); - break; - } - } - - /// <summary> - /// Gets the save path. - /// </summary> - /// <param name="item">The item.</param> - /// <param name="type">The type.</param> - /// <param name="imageIndex">Index of the image.</param> - /// <param name="mimeType">Type of the MIME.</param> - /// <param name="saveLocally">if set to <c>true</c> [save locally].</param> - /// <returns>System.String.</returns> - /// <exception cref="System.ArgumentNullException"> - /// imageIndex - /// or - /// imageIndex - /// </exception> - private string GetStandardSavePath(BaseItem item, ImageType type, int? imageIndex, string mimeType, bool saveLocally) - { - string filename; - - switch (type) - { - case ImageType.Art: - filename = "clearart"; - break; - case ImageType.Disc: - filename = item is MusicAlbum ? "cdart" : "disc"; - break; - case ImageType.Primary: - filename = item is Episode ? Path.GetFileNameWithoutExtension(item.Path) : "folder"; - break; - case ImageType.Backdrop: - if (!imageIndex.HasValue) - { - throw new ArgumentNullException("imageIndex"); - } - filename = GetBackdropSaveFilename(item.BackdropImagePaths, "backdrop", "backdrop", imageIndex.Value); - break; - case ImageType.Screenshot: - if (!imageIndex.HasValue) - { - throw new ArgumentNullException("imageIndex"); - } - var hasScreenshots = (IHasScreenshots)item; - filename = GetBackdropSaveFilename(hasScreenshots.ScreenshotImagePaths, "screenshot", "screenshot", imageIndex.Value); - break; - default: - filename = type.ToString().ToLower(); - break; - } - - var extension = mimeType.Split('/').Last(); - - if (string.Equals(extension, "jpeg", StringComparison.OrdinalIgnoreCase)) - { - extension = "jpg"; - } - - extension = "." + extension.ToLower(); - - string path = null; - - if (saveLocally) - { - if (item.IsInMixedFolder && !(item is Episode)) - { - path = GetSavePathForItemInMixedFolder(item, type, filename, extension); - } - - if (string.IsNullOrEmpty(path)) - { - path = Path.Combine(item.MetaLocation, filename + extension); - } - } - - // None of the save local conditions passed, so store it in our internal folders - if (string.IsNullOrEmpty(path)) - { - path = _remoteImageCache.GetResourcePath(item.GetType().FullName + item.Id, filename + extension); - } - - return path; - } - - private string GetBackdropSaveFilename(IEnumerable<string> images, string zeroIndexFilename, string numberedIndexPrefix, int index) - { - if (index == 0) - { - return zeroIndexFilename; - } - - var filenames = images.Select(Path.GetFileNameWithoutExtension).ToList(); - - var current = index; - while (filenames.Contains(numberedIndexPrefix + current.ToString(UsCulture), StringComparer.OrdinalIgnoreCase)) - { - current++; - } - - return numberedIndexPrefix + current.ToString(UsCulture); - } - - /// <summary> - /// Gets the compatible save paths. - /// </summary> - /// <param name="item">The item.</param> - /// <param name="type">The type.</param> - /// <param name="imageIndex">Index of the image.</param> - /// <param name="mimeType">Type of the MIME.</param> - /// <returns>IEnumerable{System.String}.</returns> - /// <exception cref="System.ArgumentNullException">imageIndex</exception> - private string[] GetCompatibleSavePaths(BaseItem item, ImageType type, int? imageIndex, string mimeType) - { - var season = item as Season; - - var extension = mimeType.Split('/').Last(); - - if (string.Equals(extension, "jpeg", StringComparison.OrdinalIgnoreCase)) - { - extension = "jpg"; - } - extension = "." + extension.ToLower(); - - // Backdrop paths - if (type == ImageType.Backdrop) - { - if (!imageIndex.HasValue) - { - throw new ArgumentNullException("imageIndex"); - } - - if (imageIndex.Value == 0) - { - if (item.IsInMixedFolder) - { - return new[] { GetSavePathForItemInMixedFolder(item, type, "fanart", extension) }; - } - - if (season != null && item.IndexNumber.HasValue) - { - var seriesFolder = season.SeriesPath; - - var seasonMarker = item.IndexNumber.Value == 0 - ? "-specials" - : item.IndexNumber.Value.ToString("00", UsCulture); - - var imageFilename = "season" + seasonMarker + "-fanart" + extension; - - return new[] { Path.Combine(seriesFolder, imageFilename) }; - } - - return new[] - { - Path.Combine(item.MetaLocation, "fanart" + extension) - }; - } - - var outputIndex = imageIndex.Value; - - if (item.IsInMixedFolder) - { - return new[] { GetSavePathForItemInMixedFolder(item, type, "fanart" + outputIndex.ToString(UsCulture), extension) }; - } - - var extraFanartFilename = GetBackdropSaveFilename(item.BackdropImagePaths, "fanart", "fanart", outputIndex); - - return new[] - { - Path.Combine(item.MetaLocation, "extrafanart", extraFanartFilename + extension), - Path.Combine(item.MetaLocation, "extrathumbs", "thumb" + outputIndex.ToString(UsCulture) + extension) - }; - } - - if (type == ImageType.Primary) - { - if (season != null && item.IndexNumber.HasValue) - { - var seriesFolder = season.SeriesPath; - - var seasonMarker = item.IndexNumber.Value == 0 - ? "-specials" - : item.IndexNumber.Value.ToString("00", UsCulture); - - var imageFilename = "season" + seasonMarker + "-poster" + extension; - - return new[] { Path.Combine(seriesFolder, imageFilename) }; - } - - if (item is Episode) - { - var seasonFolder = Path.GetDirectoryName(item.Path); - - var imageFilename = Path.GetFileNameWithoutExtension(item.Path) + "-thumb" + extension; - - return new[] { Path.Combine(seasonFolder, imageFilename) }; - } - - if (item.IsInMixedFolder || item is MusicVideo) - { - return new[] { GetSavePathForItemInMixedFolder(item, type, string.Empty, extension) }; - } - - if (item is MusicAlbum || item is MusicArtist) - { - return new[] { Path.Combine(item.MetaLocation, "folder" + extension) }; - } - - return new[] { Path.Combine(item.MetaLocation, "poster" + extension) }; - } - - if (type == ImageType.Banner) - { - if (season != null && item.IndexNumber.HasValue) - { - var seriesFolder = season.SeriesPath; - - var seasonMarker = item.IndexNumber.Value == 0 - ? "-specials" - : item.IndexNumber.Value.ToString("00", UsCulture); - - var imageFilename = "season" + seasonMarker + "-banner" + extension; - - return new[] { Path.Combine(seriesFolder, imageFilename) }; - } - } - - if (type == ImageType.Thumb) - { - if (season != null && item.IndexNumber.HasValue) - { - var seriesFolder = season.SeriesPath; - - var seasonMarker = item.IndexNumber.Value == 0 - ? "-specials" - : item.IndexNumber.Value.ToString("00", UsCulture); - - var imageFilename = "season" + seasonMarker + "-landscape" + extension; - - return new[] { Path.Combine(seriesFolder, imageFilename) }; - } - - if (item.IsInMixedFolder) - { - return new[] { GetSavePathForItemInMixedFolder(item, type, "landscape", extension) }; - } - - return new[] { Path.Combine(item.MetaLocation, "landscape" + extension) }; - } - - // All other paths are the same - return new[] { GetStandardSavePath(item, type, imageIndex, mimeType, true) }; - } - - /// <summary> - /// Gets the save path for item in mixed folder. - /// </summary> - /// <param name="item">The item.</param> - /// <param name="type">The type.</param> - /// <param name="imageFilename">The image filename.</param> - /// <param name="extension">The extension.</param> - /// <returns>System.String.</returns> - private string GetSavePathForItemInMixedFolder(IHasImages item, ImageType type, string imageFilename, string extension) - { - if (type == ImageType.Primary) - { - imageFilename = "poster"; - } - var folder = Path.GetDirectoryName(item.Path); - - return Path.Combine(folder, Path.GetFileNameWithoutExtension(item.Path) + "-" + imageFilename + extension); - } - } -} diff --git a/MediaBrowser.Server.Implementations/Providers/ProviderManager.cs b/MediaBrowser.Server.Implementations/Providers/ProviderManager.cs deleted file mode 100644 index cbfd7d74d..000000000 --- a/MediaBrowser.Server.Implementations/Providers/ProviderManager.cs +++ /dev/null @@ -1,454 +0,0 @@ -using MediaBrowser.Common.IO; -using MediaBrowser.Common.Net; -using MediaBrowser.Controller.Configuration; -using MediaBrowser.Controller.Entities; -using MediaBrowser.Controller.Entities.Movies; -using MediaBrowser.Controller.IO; -using MediaBrowser.Controller.Library; -using MediaBrowser.Controller.Persistence; -using MediaBrowser.Controller.Providers; -using MediaBrowser.Model.Entities; -using MediaBrowser.Model.Logging; -using MediaBrowser.Model.Providers; -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; - -namespace MediaBrowser.Server.Implementations.Providers -{ - /// <summary> - /// Class ProviderManager - /// </summary> - public class ProviderManager : IProviderManager - { - /// <summary> - /// The _logger - /// </summary> - private readonly ILogger _logger; - - /// <summary> - /// The _HTTP client - /// </summary> - private readonly IHttpClient _httpClient; - - /// <summary> - /// The _directory watchers - /// </summary> - private readonly IDirectoryWatchers _directoryWatchers; - - /// <summary> - /// Gets or sets the configuration manager. - /// </summary> - /// <value>The configuration manager.</value> - private IServerConfigurationManager ConfigurationManager { get; set; } - - /// <summary> - /// Gets the list of currently registered metadata prvoiders - /// </summary> - /// <value>The metadata providers enumerable.</value> - private BaseMetadataProvider[] MetadataProviders { get; set; } - - private IImageProvider[] ImageProviders { get; set; } - private readonly IFileSystem _fileSystem; - - private readonly IItemRepository _itemRepo; - - /// <summary> - /// Initializes a new instance of the <see cref="ProviderManager" /> class. - /// </summary> - /// <param name="httpClient">The HTTP client.</param> - /// <param name="configurationManager">The configuration manager.</param> - /// <param name="directoryWatchers">The directory watchers.</param> - /// <param name="logManager">The log manager.</param> - public ProviderManager(IHttpClient httpClient, IServerConfigurationManager configurationManager, IDirectoryWatchers directoryWatchers, ILogManager logManager, IFileSystem fileSystem, IItemRepository itemRepo) - { - _logger = logManager.GetLogger("ProviderManager"); - _httpClient = httpClient; - ConfigurationManager = configurationManager; - _directoryWatchers = directoryWatchers; - _fileSystem = fileSystem; - _itemRepo = itemRepo; - } - - /// <summary> - /// Adds the metadata providers. - /// </summary> - /// <param name="providers">The providers.</param> - /// <param name="imageProviders">The image providers.</param> - public void AddParts(IEnumerable<BaseMetadataProvider> providers, IEnumerable<IImageProvider> imageProviders) - { - MetadataProviders = providers.OrderBy(e => e.Priority).ToArray(); - - ImageProviders = imageProviders.OrderByDescending(i => i.Priority).ToArray(); - } - - /// <summary> - /// Runs all metadata providers for an entity, and returns true or false indicating if at least one was refreshed and requires persistence - /// </summary> - /// <param name="item">The item.</param> - /// <param name="cancellationToken">The cancellation token.</param> - /// <param name="force">if set to <c>true</c> [force].</param> - /// <param name="allowSlowProviders">if set to <c>true</c> [allow slow providers].</param> - /// <returns>Task{System.Boolean}.</returns> - public async Task<ItemUpdateType?> ExecuteMetadataProviders(BaseItem item, CancellationToken cancellationToken, bool force = false, bool allowSlowProviders = true) - { - if (item == null) - { - throw new ArgumentNullException("item"); - } - - ItemUpdateType? result = null; - - cancellationToken.ThrowIfCancellationRequested(); - - var enableInternetProviders = ConfigurationManager.Configuration.EnableInternetProviders; - - var providerHistories = item.DateLastSaved == default(DateTime) ? - new List<BaseProviderInfo>() : - _itemRepo.GetProviderHistory(item.Id).ToList(); - - // Run the normal providers sequentially in order of priority - foreach (var provider in MetadataProviders) - { - cancellationToken.ThrowIfCancellationRequested(); - - if (!ProviderSupportsItem(provider, item)) - { - continue; - } - - // Skip if internet providers are currently disabled - if (provider.RequiresInternet && !enableInternetProviders) - { - continue; - } - - // Skip if is slow and we aren't allowing slow ones - if (provider.IsSlow && !allowSlowProviders) - { - continue; - } - - // Put this check below the await because the needs refresh of the next tier of providers may depend on the previous ones running - // This is the case for the fan art provider which depends on the movie and tv providers having run before them - if (provider.RequiresInternet && item.DontFetchMeta && provider.EnforceDontFetchMetadata) - { - continue; - } - - var providerInfo = providerHistories.FirstOrDefault(i => i.ProviderId == provider.Id); - - if (providerInfo == null) - { - providerInfo = new BaseProviderInfo - { - ProviderId = provider.Id - }; - providerHistories.Add(providerInfo); - } - - try - { - if (!force && !provider.NeedsRefresh(item, providerInfo)) - { - continue; - } - } - catch (Exception ex) - { - _logger.Error("Error determining NeedsRefresh for {0}", ex, item.Path); - } - - var updateType = await FetchAsync(provider, item, providerInfo, force, cancellationToken).ConfigureAwait(false); - - if (updateType.HasValue) - { - if (result.HasValue) - { - result = result.Value | updateType.Value; - } - else - { - result = updateType; - } - } - } - - if (result.HasValue || force) - { - await _itemRepo.SaveProviderHistory(item.Id, providerHistories, cancellationToken); - } - - return result; - } - - /// <summary> - /// Providers the supports item. - /// </summary> - /// <param name="provider">The provider.</param> - /// <param name="item">The item.</param> - /// <returns><c>true</c> if XXXX, <c>false</c> otherwise</returns> - private bool ProviderSupportsItem(BaseMetadataProvider provider, BaseItem item) - { - try - { - return provider.Supports(item); - } - catch (Exception ex) - { - _logger.ErrorException("{0} failed in Supports for type {1}", ex, provider.GetType().Name, item.GetType().Name); - return false; - } - } - - /// <summary> - /// Fetches metadata and returns true or false indicating if any work that requires persistence was done - /// </summary> - /// <param name="provider">The provider.</param> - /// <param name="item">The item.</param> - /// <param name="providerInfo">The provider information.</param> - /// <param name="force">if set to <c>true</c> [force].</param> - /// <param name="cancellationToken">The cancellation token.</param> - /// <returns>Task{System.Boolean}.</returns> - /// <exception cref="System.ArgumentNullException">item</exception> - private async Task<ItemUpdateType?> FetchAsync(BaseMetadataProvider provider, BaseItem item, BaseProviderInfo providerInfo, bool force, CancellationToken cancellationToken) - { - if (item == null) - { - throw new ArgumentNullException("item"); - } - - cancellationToken.ThrowIfCancellationRequested(); - - // Don't clog up the log with these providers - if (!(provider is IDynamicInfoProvider)) - { - _logger.Debug("Running {0} for {1}", provider.GetType().Name, item.Path ?? item.Name ?? "--Unknown--"); - } - - try - { - var changed = await provider.FetchAsync(item, force, providerInfo, cancellationToken).ConfigureAwait(false); - - if (changed) - { - return provider.ItemUpdateType; - } - - return null; - } - catch (OperationCanceledException ex) - { - _logger.Debug("{0} canceled for {1}", provider.GetType().Name, item.Name); - - // If the outer cancellation token is the one that caused the cancellation, throw it - if (cancellationToken.IsCancellationRequested && ex.CancellationToken == cancellationToken) - { - throw; - } - - return null; - } - catch (Exception ex) - { - _logger.ErrorException("{0} failed refreshing {1} {2}", ex, provider.GetType().Name, item.Name, item.Path ?? string.Empty); - - provider.SetLastRefreshed(item, DateTime.UtcNow, providerInfo, ProviderRefreshStatus.Failure); - - return ItemUpdateType.Unspecified; - } - } - - /// <summary> - /// Saves to library filesystem. - /// </summary> - /// <param name="item">The item.</param> - /// <param name="path">The path.</param> - /// <param name="dataToSave">The data to save.</param> - /// <param name="cancellationToken">The cancellation token.</param> - /// <returns>Task.</returns> - /// <exception cref="System.ArgumentNullException"></exception> - public async Task SaveToLibraryFilesystem(BaseItem item, string path, Stream dataToSave, CancellationToken cancellationToken) - { - if (item == null) - { - throw new ArgumentNullException(); - } - if (string.IsNullOrEmpty(path)) - { - throw new ArgumentNullException(); - } - if (dataToSave == null) - { - throw new ArgumentNullException(); - } - - if (cancellationToken.IsCancellationRequested) - { - dataToSave.Dispose(); - cancellationToken.ThrowIfCancellationRequested(); - } - - //Tell the watchers to ignore - _directoryWatchers.TemporarilyIgnore(path); - - if (dataToSave.CanSeek) - { - dataToSave.Position = 0; - } - - try - { - using (dataToSave) - { - using (var fs = _fileSystem.GetFileStream(path, FileMode.Create, FileAccess.Write, FileShare.Read, true)) - { - await dataToSave.CopyToAsync(fs, StreamDefaults.DefaultCopyToBufferSize, cancellationToken).ConfigureAwait(false); - } - } - - // If this is ever used for something other than metadata we can add a file type param - item.ResolveArgs.AddMetadataFile(path); - } - finally - { - //Remove the ignore - _directoryWatchers.RemoveTempIgnore(path); - } - } - - - /// <summary> - /// Saves the image. - /// </summary> - /// <param name="item">The item.</param> - /// <param name="url">The URL.</param> - /// <param name="resourcePool">The resource pool.</param> - /// <param name="type">The type.</param> - /// <param name="imageIndex">Index of the image.</param> - /// <param name="cancellationToken">The cancellation token.</param> - /// <returns>Task.</returns> - public async Task SaveImage(BaseItem item, string url, SemaphoreSlim resourcePool, ImageType type, int? imageIndex, CancellationToken cancellationToken) - { - var response = await _httpClient.GetResponse(new HttpRequestOptions - { - CancellationToken = cancellationToken, - ResourcePool = resourcePool, - Url = url - - }).ConfigureAwait(false); - - await SaveImage(item, response.Content, response.ContentType, type, imageIndex, url, cancellationToken) - .ConfigureAwait(false); - } - - /// <summary> - /// Saves the image. - /// </summary> - /// <param name="item">The item.</param> - /// <param name="source">The source.</param> - /// <param name="mimeType">Type of the MIME.</param> - /// <param name="type">The type.</param> - /// <param name="imageIndex">Index of the image.</param> - /// <param name="sourceUrl">The source URL.</param> - /// <param name="cancellationToken">The cancellation token.</param> - /// <returns>Task.</returns> - public Task SaveImage(BaseItem item, Stream source, string mimeType, ImageType type, int? imageIndex, string sourceUrl, CancellationToken cancellationToken) - { - return new ImageSaver(ConfigurationManager, _directoryWatchers, _fileSystem, _logger).SaveImage(item, source, mimeType, type, imageIndex, sourceUrl, cancellationToken); - } - - /// <summary> - /// Gets the available remote images. - /// </summary> - /// <param name="item">The item.</param> - /// <param name="cancellationToken">The cancellation token.</param> - /// <param name="providerName">Name of the provider.</param> - /// <param name="type">The type.</param> - /// <returns>Task{IEnumerable{RemoteImageInfo}}.</returns> - public async Task<IEnumerable<RemoteImageInfo>> GetAvailableRemoteImages(BaseItem item, CancellationToken cancellationToken, string providerName = null, ImageType? type = null) - { - var providers = GetImageProviders(item); - - if (!string.IsNullOrEmpty(providerName)) - { - providers = providers.Where(i => string.Equals(i.Name, providerName, StringComparison.OrdinalIgnoreCase)); - } - - var preferredLanguage = item.GetPreferredMetadataLanguage(); - - var tasks = providers.Select(i => GetImages(item, cancellationToken, i, preferredLanguage, type)); - - var results = await Task.WhenAll(tasks).ConfigureAwait(false); - - return results.SelectMany(i => i); - } - - /// <summary> - /// Gets the images. - /// </summary> - /// <param name="item">The item.</param> - /// <param name="cancellationToken">The cancellation token.</param> - /// <param name="i">The i.</param> - /// <param name="preferredLanguage">The preferred language.</param> - /// <param name="type">The type.</param> - /// <returns>Task{IEnumerable{RemoteImageInfo}}.</returns> - private async Task<IEnumerable<RemoteImageInfo>> GetImages(BaseItem item, CancellationToken cancellationToken, IImageProvider i, string preferredLanguage, ImageType? type = null) - { - try - { - if (type.HasValue) - { - var result = await i.GetImages(item, type.Value, cancellationToken).ConfigureAwait(false); - - return FilterImages(result, preferredLanguage); - } - else - { - var result = await i.GetAllImages(item, cancellationToken).ConfigureAwait(false); - return FilterImages(result, preferredLanguage); - } - } - catch (Exception ex) - { - _logger.ErrorException("{0} failed in GetImages for type {1}", ex, i.GetType().Name, item.GetType().Name); - return new List<RemoteImageInfo>(); - } - } - - private IEnumerable<RemoteImageInfo> FilterImages(IEnumerable<RemoteImageInfo> images, string preferredLanguage) - { - if (string.Equals(preferredLanguage, "en", StringComparison.OrdinalIgnoreCase)) - { - images = images.Where(i => string.IsNullOrEmpty(i.Language) || - string.Equals(i.Language, "en", StringComparison.OrdinalIgnoreCase)); - } - - return images; - } - - /// <summary> - /// Gets the supported image providers. - /// </summary> - /// <param name="item">The item.</param> - /// <returns>IEnumerable{IImageProvider}.</returns> - public IEnumerable<IImageProvider> GetImageProviders(BaseItem item) - { - return ImageProviders.Where(i => - { - try - { - return i.Supports(item); - } - catch (Exception ex) - { - _logger.ErrorException("{0} failed in Supports for type {1}", ex, i.GetType().Name, item.GetType().Name); - return false; - } - }); - } - } -} |
