diff options
| author | crobibero <cody@robibe.ro> | 2020-09-11 15:53:04 -0600 |
|---|---|---|
| committer | crobibero <cody@robibe.ro> | 2020-09-11 15:53:04 -0600 |
| commit | f13b87afa3e81e7fa2710caec58a7d6cb20f7635 (patch) | |
| tree | 0f269ac5baa27b334c5377d9dec4010aedf25183 /MediaBrowser.Providers | |
| parent | 2363ad544979adf32207fa927f106fadb784f1fb (diff) | |
| parent | 6bf0acb854683377bebad3ca27de17706519c420 (diff) | |
Merge remote-tracking branch 'upstream/master' into api-upload-subtitle
Diffstat (limited to 'MediaBrowser.Providers')
166 files changed, 3468 insertions, 2438 deletions
diff --git a/MediaBrowser.Providers/Books/AudioBookMetadataService.cs b/MediaBrowser.Providers/Books/AudioBookMetadataService.cs index 8eaeeea080..eabc66c6b8 100644 --- a/MediaBrowser.Providers/Books/AudioBookMetadataService.cs +++ b/MediaBrowser.Providers/Books/AudioBookMetadataService.cs @@ -1,3 +1,5 @@ +#pragma warning disable CS1591 + using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Library; @@ -25,7 +27,7 @@ namespace MediaBrowser.Providers.Books protected override void MergeData( MetadataResult<AudioBook> source, MetadataResult<AudioBook> target, - MetadataFields[] lockedFields, + MetadataField[] lockedFields, bool replaceData, bool mergeMetadataSettings) { diff --git a/MediaBrowser.Providers/Books/BookMetadataService.cs b/MediaBrowser.Providers/Books/BookMetadataService.cs index 3406417113..3f3782dfb9 100644 --- a/MediaBrowser.Providers/Books/BookMetadataService.cs +++ b/MediaBrowser.Providers/Books/BookMetadataService.cs @@ -1,3 +1,5 @@ +#pragma warning disable CS1591 + using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Library; @@ -22,7 +24,7 @@ namespace MediaBrowser.Providers.Books } /// <inheritdoc /> - protected override void MergeData(MetadataResult<Book> source, MetadataResult<Book> target, MetadataFields[] lockedFields, bool replaceData, bool mergeMetadataSettings) + protected override void MergeData(MetadataResult<Book> source, MetadataResult<Book> target, MetadataField[] lockedFields, bool replaceData, bool mergeMetadataSettings) { ProviderUtils.MergeBaseItemData(source, target, lockedFields, replaceData, mergeMetadataSettings); diff --git a/MediaBrowser.Providers/BoxSets/BoxSetMetadataService.cs b/MediaBrowser.Providers/BoxSets/BoxSetMetadataService.cs index 3c9760ea7a..e5326da71c 100644 --- a/MediaBrowser.Providers/BoxSets/BoxSetMetadataService.cs +++ b/MediaBrowser.Providers/BoxSets/BoxSetMetadataService.cs @@ -1,3 +1,5 @@ +#pragma warning disable CS1591 + using System.Collections.Generic; using System.Linq; using MediaBrowser.Controller.Configuration; @@ -43,7 +45,7 @@ namespace MediaBrowser.Providers.BoxSets } /// <inheritdoc /> - protected override void MergeData(MetadataResult<BoxSet> source, MetadataResult<BoxSet> target, MetadataFields[] lockedFields, bool replaceData, bool mergeMetadataSettings) + protected override void MergeData(MetadataResult<BoxSet> source, MetadataResult<BoxSet> target, MetadataField[] lockedFields, bool replaceData, bool mergeMetadataSettings) { ProviderUtils.MergeBaseItemData(source, target, lockedFields, replaceData, mergeMetadataSettings); diff --git a/MediaBrowser.Providers/Channels/ChannelMetadataService.cs b/MediaBrowser.Providers/Channels/ChannelMetadataService.cs index 9afa823197..db2213bad4 100644 --- a/MediaBrowser.Providers/Channels/ChannelMetadataService.cs +++ b/MediaBrowser.Providers/Channels/ChannelMetadataService.cs @@ -1,3 +1,5 @@ +#pragma warning disable CS1591 + using MediaBrowser.Controller.Channels; using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Library; @@ -22,7 +24,7 @@ namespace MediaBrowser.Providers.Channels } /// <inheritdoc /> - protected override void MergeData(MetadataResult<Channel> source, MetadataResult<Channel> target, MetadataFields[] lockedFields, bool replaceData, bool mergeMetadataSettings) + protected override void MergeData(MetadataResult<Channel> source, MetadataResult<Channel> target, MetadataField[] lockedFields, bool replaceData, bool mergeMetadataSettings) { ProviderUtils.MergeBaseItemData(source, target, lockedFields, replaceData, mergeMetadataSettings); } diff --git a/MediaBrowser.Providers/Folders/CollectionFolderMetadataService.cs b/MediaBrowser.Providers/Folders/CollectionFolderMetadataService.cs index 921222543c..e0f3131fdb 100644 --- a/MediaBrowser.Providers/Folders/CollectionFolderMetadataService.cs +++ b/MediaBrowser.Providers/Folders/CollectionFolderMetadataService.cs @@ -1,3 +1,4 @@ +#pragma warning disable CS1591 using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Entities; @@ -23,7 +24,7 @@ namespace MediaBrowser.Providers.Folders } /// <inheritdoc /> - protected override void MergeData(MetadataResult<CollectionFolder> source, MetadataResult<CollectionFolder> target, MetadataFields[] lockedFields, bool replaceData, bool mergeMetadataSettings) + protected override void MergeData(MetadataResult<CollectionFolder> source, MetadataResult<CollectionFolder> target, MetadataField[] lockedFields, bool replaceData, bool mergeMetadataSettings) { ProviderUtils.MergeBaseItemData(source, target, lockedFields, replaceData, mergeMetadataSettings); } diff --git a/MediaBrowser.Providers/Folders/FolderMetadataService.cs b/MediaBrowser.Providers/Folders/FolderMetadataService.cs index b6bd2515dd..998bf4c6a3 100644 --- a/MediaBrowser.Providers/Folders/FolderMetadataService.cs +++ b/MediaBrowser.Providers/Folders/FolderMetadataService.cs @@ -1,3 +1,5 @@ +#pragma warning disable CS1591 + using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Library; @@ -26,7 +28,7 @@ namespace MediaBrowser.Providers.Folders public override int Order => 10; /// <inheritdoc /> - protected override void MergeData(MetadataResult<Folder> source, MetadataResult<Folder> target, MetadataFields[] lockedFields, bool replaceData, bool mergeMetadataSettings) + protected override void MergeData(MetadataResult<Folder> source, MetadataResult<Folder> target, MetadataField[] lockedFields, bool replaceData, bool mergeMetadataSettings) { ProviderUtils.MergeBaseItemData(source, target, lockedFields, replaceData, mergeMetadataSettings); } diff --git a/MediaBrowser.Providers/Folders/UserViewMetadataService.cs b/MediaBrowser.Providers/Folders/UserViewMetadataService.cs index 60ee811149..2d536f12ec 100644 --- a/MediaBrowser.Providers/Folders/UserViewMetadataService.cs +++ b/MediaBrowser.Providers/Folders/UserViewMetadataService.cs @@ -1,3 +1,5 @@ +#pragma warning disable CS1591 + using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Library; @@ -22,7 +24,7 @@ namespace MediaBrowser.Providers.Folders } /// <inheritdoc /> - protected override void MergeData(MetadataResult<UserView> source, MetadataResult<UserView> target, MetadataFields[] lockedFields, bool replaceData, bool mergeMetadataSettings) + protected override void MergeData(MetadataResult<UserView> source, MetadataResult<UserView> target, MetadataField[] lockedFields, bool replaceData, bool mergeMetadataSettings) { ProviderUtils.MergeBaseItemData(source, target, lockedFields, replaceData, mergeMetadataSettings); } diff --git a/MediaBrowser.Providers/Genres/GenreMetadataService.cs b/MediaBrowser.Providers/Genres/GenreMetadataService.cs index f3406c1abc..f7ea767e7c 100644 --- a/MediaBrowser.Providers/Genres/GenreMetadataService.cs +++ b/MediaBrowser.Providers/Genres/GenreMetadataService.cs @@ -1,3 +1,5 @@ +#pragma warning disable CS1591 + using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Library; @@ -22,7 +24,7 @@ namespace MediaBrowser.Providers.Genres } /// <inheritdoc /> - protected override void MergeData(MetadataResult<Genre> source, MetadataResult<Genre> target, MetadataFields[] lockedFields, bool replaceData, bool mergeMetadataSettings) + protected override void MergeData(MetadataResult<Genre> source, MetadataResult<Genre> target, MetadataField[] lockedFields, bool replaceData, bool mergeMetadataSettings) { ProviderUtils.MergeBaseItemData(source, target, lockedFields, replaceData, mergeMetadataSettings); } diff --git a/MediaBrowser.Providers/LiveTv/ProgramMetadataService.cs b/MediaBrowser.Providers/LiveTv/LiveTvMetadataService.cs index 7dd49c71a9..2e6cf45302 100644 --- a/MediaBrowser.Providers/LiveTv/ProgramMetadataService.cs +++ b/MediaBrowser.Providers/LiveTv/LiveTvMetadataService.cs @@ -1,3 +1,5 @@ +#pragma warning disable CS1591 + using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.LiveTv; @@ -22,7 +24,7 @@ namespace MediaBrowser.Providers.LiveTv } /// <inheritdoc /> - protected override void MergeData(MetadataResult<LiveTvChannel> source, MetadataResult<LiveTvChannel> target, MetadataFields[] lockedFields, bool replaceData, bool mergeMetadataSettings) + protected override void MergeData(MetadataResult<LiveTvChannel> source, MetadataResult<LiveTvChannel> target, MetadataField[] lockedFields, bool replaceData, bool mergeMetadataSettings) { ProviderUtils.MergeBaseItemData(source, target, lockedFields, replaceData, mergeMetadataSettings); } diff --git a/MediaBrowser.Providers/Manager/ImageSaver.cs b/MediaBrowser.Providers/Manager/ImageSaver.cs index 3ab621ba41..19a42d506c 100644 --- a/MediaBrowser.Providers/Manager/ImageSaver.cs +++ b/MediaBrowser.Providers/Manager/ImageSaver.cs @@ -1,3 +1,5 @@ +#pragma warning disable CS1591 + using System; using System.Collections.Generic; using System.Globalization; @@ -9,30 +11,33 @@ using MediaBrowser.Common.Configuration; using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities.Audio; -using MediaBrowser.Controller.Entities.TV; using MediaBrowser.Controller.Library; using MediaBrowser.Model.Configuration; using MediaBrowser.Model.Entities; using MediaBrowser.Model.IO; using MediaBrowser.Model.Net; using Microsoft.Extensions.Logging; +using Episode = MediaBrowser.Controller.Entities.TV.Episode; +using MusicAlbum = MediaBrowser.Controller.Entities.Audio.MusicAlbum; +using Person = MediaBrowser.Controller.Entities.Person; +using Season = MediaBrowser.Controller.Entities.TV.Season; namespace MediaBrowser.Providers.Manager { /// <summary> - /// Class ImageSaver + /// Class ImageSaver. /// </summary> public class ImageSaver { private static readonly CultureInfo UsCulture = new CultureInfo("en-US"); /// <summary> - /// The _config + /// The _config. /// </summary> private readonly IServerConfigurationManager _config; /// <summary> - /// The _directory watchers + /// The _directory watchers. /// </summary> private readonly ILibraryMonitor _libraryMonitor; private readonly IFileSystem _fileSystem; @@ -53,6 +58,16 @@ namespace MediaBrowser.Providers.Manager _logger = logger; } + private bool EnableExtraThumbsDuplication + { + get + { + var config = _config.GetConfiguration<XbmcMetadataOptions>("xbmcmetadata"); + + return config.EnableExtraThumbsDuplication; + } + } + /// <summary> /// Saves the image. /// </summary> @@ -63,7 +78,7 @@ namespace MediaBrowser.Providers.Manager /// <param name="imageIndex">Index of the image.</param> /// <param name="cancellationToken">The cancellation token.</param> /// <returns>Task.</returns> - /// <exception cref="ArgumentNullException">mimeType</exception> + /// <exception cref="ArgumentNullException">mimeType.</exception> public Task SaveImage(BaseItem item, Stream source, string mimeType, ImageType type, int? imageIndex, CancellationToken cancellationToken) { return SaveImage(item, source, mimeType, type, imageIndex, null, cancellationToken); @@ -78,11 +93,6 @@ namespace MediaBrowser.Providers.Manager var saveLocally = item.SupportsLocalMetadata && item.IsSaveLocalMetadataEnabled() && !item.ExtraType.HasValue && !(item is Audio); - if (item is User) - { - saveLocally = true; - } - if (type != ImageType.Primary && item is Episode) { saveLocally = false; @@ -105,6 +115,7 @@ namespace MediaBrowser.Providers.Manager } } } + if (saveLocallyWithMedia.HasValue && !saveLocallyWithMedia.Value) { saveLocally = saveLocallyWithMedia.Value; @@ -122,35 +133,40 @@ namespace MediaBrowser.Providers.Manager var retryPaths = GetSavePaths(item, type, imageIndex, mimeType, false); // If there are more than one output paths, the stream will need to be seekable - var memoryStream = new MemoryStream(); - using (source) + if (paths.Length > 1 && !source.CanSeek) { - await source.CopyToAsync(memoryStream).ConfigureAwait(false); - } + var memoryStream = new MemoryStream(); + await using (source.ConfigureAwait(false)) + { + await source.CopyToAsync(memoryStream).ConfigureAwait(false); + } - source = memoryStream; + source = memoryStream; + } var currentImage = GetCurrentImage(item, type, index); var currentImageIsLocalFile = currentImage != null && currentImage.IsLocalFile; - var currentImagePath = currentImage == null ? null : currentImage.Path; + var currentImagePath = currentImage?.Path; var savedPaths = new List<string>(); - using (source) + await using (source.ConfigureAwait(false)) { - var currentPathIndex = 0; - - foreach (var path in paths) + for (int i = 0; i < paths.Length; i++) { - source.Position = 0; + if (i != 0) + { + source.Position = 0; + } + string retryPath = null; if (paths.Length == retryPaths.Length) { - retryPath = retryPaths[currentPathIndex]; + retryPath = retryPaths[i]; } - var savedPath = await SaveImageToLocation(source, path, retryPath, cancellationToken).ConfigureAwait(false); + + var savedPath = await SaveImageToLocation(source, paths[i], retryPath, cancellationToken).ConfigureAwait(false); savedPaths.Add(savedPath); - currentPathIndex++; } } @@ -172,7 +188,6 @@ namespace MediaBrowser.Providers.Manager } catch (FileNotFoundException) { - } finally { @@ -181,6 +196,11 @@ namespace MediaBrowser.Providers.Manager } } + public async Task SaveImage(Stream source, string path) + { + await SaveImageToLocation(source, path, path, CancellationToken.None).ConfigureAwait(false); + } + private async Task<string> SaveImageToLocation(Stream source, string path, string retryPath, CancellationToken cancellationToken) { try @@ -217,7 +237,6 @@ namespace MediaBrowser.Providers.Manager } } - source.Position = 0; await SaveImageToLocation(source, retryPath, cancellationToken).ConfigureAwait(false); return retryPath; } @@ -244,9 +263,9 @@ namespace MediaBrowser.Providers.Manager _fileSystem.SetAttributes(path, false, false); - using (var fs = new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.Read, IODefaults.FileStreamBufferSize, FileOptions.Asynchronous)) + await using (var fs = new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.Read, IODefaults.FileStreamBufferSize, FileOptions.Asynchronous)) { - await source.CopyToAsync(fs, IODefaults.CopyToBufferSize, cancellationToken).ConfigureAwait(false); + await source.CopyToAsync(fs, cancellationToken).ConfigureAwait(false); } if (_config.Configuration.SaveMetadataHidden) @@ -302,7 +321,7 @@ namespace MediaBrowser.Providers.Manager /// <exception cref="ArgumentNullException"> /// imageIndex /// or - /// imageIndex + /// imageIndex. /// </exception> private ItemImageInfo GetCurrentImage(BaseItem item, ImageType type, int imageIndex) { @@ -318,7 +337,8 @@ namespace MediaBrowser.Providers.Manager /// <param name="path">The path.</param> /// <exception cref="ArgumentNullException">imageIndex /// or - /// imageIndex</exception> + /// imageIndex. + /// </exception> private void SetImagePath(BaseItem item, ImageType type, int? imageIndex, string path) { item.SetImagePath(type, imageIndex ?? 0, _fileSystem.GetFileInfo(path)); @@ -336,7 +356,7 @@ namespace MediaBrowser.Providers.Manager /// <exception cref="ArgumentNullException"> /// imageIndex /// or - /// imageIndex + /// imageIndex. /// </exception> private string GetStandardSavePath(BaseItem item, ImageType type, int? imageIndex, string mimeType, bool saveLocally) { @@ -345,7 +365,7 @@ namespace MediaBrowser.Providers.Manager if (string.IsNullOrWhiteSpace(extension)) { - throw new ArgumentException(string.Format("Unable to determine image file extension from mime type {0}", mimeType)); + throw new ArgumentException(string.Format(CultureInfo.InvariantCulture, "Unable to determine image file extension from mime type {0}", mimeType)); } if (type == ImageType.Thumb && saveLocally) @@ -439,7 +459,6 @@ namespace MediaBrowser.Providers.Manager { path = Path.Combine(Path.GetDirectoryName(item.Path), "metadata", filename + extension); } - else if (item.IsInMixedFolder) { path = GetSavePathForItemInMixedFolder(item, type, filename, extension); @@ -458,6 +477,7 @@ namespace MediaBrowser.Providers.Manager { filename = folderName; } + path = Path.Combine(item.GetInternalMetadataPath(), filename + extension); } @@ -490,7 +510,7 @@ namespace MediaBrowser.Providers.Manager /// <param name="imageIndex">Index of the image.</param> /// <param name="mimeType">Type of the MIME.</param> /// <returns>IEnumerable{System.String}.</returns> - /// <exception cref="ArgumentNullException">imageIndex</exception> + /// <exception cref="ArgumentNullException">imageIndex.</exception> private string[] GetCompatibleSavePaths(BaseItem item, ImageType type, int? imageIndex, string mimeType) { var season = item as Season; @@ -549,6 +569,7 @@ namespace MediaBrowser.Providers.Manager { list.Add(Path.Combine(item.ContainingFolderPath, "extrathumbs", "thumb" + outputIndex.ToString(UsCulture) + extension)); } + return list.ToArray(); } @@ -593,16 +614,6 @@ namespace MediaBrowser.Providers.Manager return new[] { GetStandardSavePath(item, type, imageIndex, mimeType, true) }; } - private bool EnableExtraThumbsDuplication - { - get - { - var config = _config.GetConfiguration<XbmcMetadataOptions>("xbmcmetadata"); - - return config.EnableExtraThumbsDuplication; - } - } - /// <summary> /// Gets the save path for item in mixed folder. /// </summary> @@ -617,6 +628,7 @@ namespace MediaBrowser.Providers.Manager { imageFilename = "poster"; } + var folder = Path.GetDirectoryName(item.Path); return Path.Combine(folder, Path.GetFileNameWithoutExtension(item.Path) + "-" + imageFilename + extension); diff --git a/MediaBrowser.Providers/Manager/ItemImageProvider.cs b/MediaBrowser.Providers/Manager/ItemImageProvider.cs index 6ef0e44a2b..d0bdbd7c95 100644 --- a/MediaBrowser.Providers/Manager/ItemImageProvider.cs +++ b/MediaBrowser.Providers/Manager/ItemImageProvider.cs @@ -1,3 +1,5 @@ +#pragma warning disable CS1591 + using System; using System.Collections.Generic; using System.IO; @@ -26,6 +28,22 @@ namespace MediaBrowser.Providers.Manager private readonly IProviderManager _providerManager; private readonly IFileSystem _fileSystem; + /// <summary> + /// Image types that are only one per item. + /// </summary> + private readonly ImageType[] _singularImages = + { + ImageType.Primary, + ImageType.Art, + ImageType.Banner, + ImageType.Box, + ImageType.BoxRear, + ImageType.Disc, + ImageType.Logo, + ImageType.Menu, + ImageType.Thumb + }; + public ItemImageProvider(ILogger logger, IProviderManager providerManager, IFileSystem fileSystem) { _logger = logger; @@ -52,12 +70,18 @@ namespace MediaBrowser.Providers.Manager return hasChanges; } - public async Task<RefreshResult> RefreshImages(BaseItem item, LibraryOptions libraryOptions, List<IImageProvider> providers, ImageRefreshOptions refreshOptions, MetadataOptions savedOptions, CancellationToken cancellationToken) + public async Task<RefreshResult> RefreshImages( + BaseItem item, + LibraryOptions libraryOptions, + List<IImageProvider> providers, + ImageRefreshOptions refreshOptions, + CancellationToken cancellationToken) { if (refreshOptions.IsReplacingImage(ImageType.Backdrop)) { ClearImages(item, ImageType.Backdrop); } + if (refreshOptions.IsReplacingImage(ImageType.Screenshot)) { ClearImages(item, ImageType.Screenshot); @@ -75,19 +99,15 @@ namespace MediaBrowser.Providers.Manager foreach (var provider in providers) { - var remoteProvider = provider as IRemoteImageProvider; - - if (remoteProvider != null) + if (provider is IRemoteImageProvider remoteProvider) { await RefreshFromProvider(item, libraryOptions, remoteProvider, refreshOptions, typeOptions, backdropLimit, screenshotLimit, downloadedImages, result, cancellationToken).ConfigureAwait(false); continue; } - var dynamicImageProvider = provider as IDynamicImageProvider; - - if (dynamicImageProvider != null) + if (provider is IDynamicImageProvider dynamicImageProvider) { - await RefreshFromProvider(item, dynamicImageProvider, refreshOptions, typeOptions, libraryOptions, downloadedImages, result, cancellationToken).ConfigureAwait(false); + await RefreshFromProvider(item, dynamicImageProvider, refreshOptions, typeOptions, downloadedImages, result, cancellationToken).ConfigureAwait(false); } } @@ -97,11 +117,11 @@ namespace MediaBrowser.Providers.Manager /// <summary> /// Refreshes from provider. /// </summary> - private async Task RefreshFromProvider(BaseItem item, + private async Task RefreshFromProvider( + BaseItem item, IDynamicImageProvider provider, ImageRefreshOptions refreshOptions, TypeOptions savedOptions, - LibraryOptions libraryOptions, ICollection<ImageType> downloadedImages, RefreshResult result, CancellationToken cancellationToken) @@ -112,7 +132,10 @@ namespace MediaBrowser.Providers.Manager foreach (var imageType in images) { - if (!IsEnabled(savedOptions, imageType, item)) continue; + if (!IsEnabled(savedOptions, imageType)) + { + continue; + } if (!HasImage(item, imageType) || (refreshOptions.IsReplacingImage(imageType) && !downloadedImages.Contains(imageType))) { @@ -127,12 +150,13 @@ namespace MediaBrowser.Providers.Manager if (response.Protocol == MediaProtocol.Http) { _logger.LogDebug("Setting image url into item {0}", item.Id); - item.SetImage(new ItemImageInfo - { - Path = response.Path, - Type = imageType - - }, 0); + item.SetImage( + new ItemImageInfo + { + Path = response.Path, + Type = imageType + }, + 0); } else { @@ -151,7 +175,7 @@ namespace MediaBrowser.Providers.Manager } downloadedImages.Add(imageType); - result.UpdateType = result.UpdateType | ItemUpdateType.ImageUpdate; + result.UpdateType |= ItemUpdateType.ImageUpdate; } } } @@ -167,29 +191,13 @@ namespace MediaBrowser.Providers.Manager } } - /// <summary> - /// Image types that are only one per item - /// </summary> - private readonly ImageType[] _singularImages = - { - ImageType.Primary, - ImageType.Art, - ImageType.Banner, - ImageType.Box, - ImageType.BoxRear, - ImageType.Disc, - ImageType.Logo, - ImageType.Menu, - ImageType.Thumb - }; - private bool HasImage(BaseItem item, ImageType type) { return item.HasImage(type); } /// <summary> - /// Determines if an item already contains the given images + /// Determines if an item already contains the given images. /// </summary> /// <param name="item">The item.</param> /// <param name="images">The images.</param> @@ -221,6 +229,7 @@ namespace MediaBrowser.Providers.Manager /// Refreshes from provider. /// </summary> /// <param name="item">The item.</param> + /// <param name="libraryOptions">The library options.</param> /// <param name="provider">The provider.</param> /// <param name="refreshOptions">The refresh options.</param> /// <param name="savedOptions">The saved options.</param> @@ -230,7 +239,9 @@ namespace MediaBrowser.Providers.Manager /// <param name="result">The result.</param> /// <param name="cancellationToken">The cancellation token.</param> /// <returns>Task.</returns> - private async Task RefreshFromProvider(BaseItem item, LibraryOptions libraryOptions, + private async Task RefreshFromProvider( + BaseItem item, + LibraryOptions libraryOptions, IRemoteImageProvider provider, ImageRefreshOptions refreshOptions, TypeOptions savedOptions, @@ -256,20 +267,24 @@ namespace MediaBrowser.Providers.Manager _logger.LogDebug("Running {0} for {1}", provider.GetType().Name, item.Path ?? item.Name); - var images = await _providerManager.GetAvailableRemoteImages(item, new RemoteImageQuery - { - ProviderName = provider.Name, - IncludeAllLanguages = false, - IncludeDisabledProviders = false, - - }, cancellationToken).ConfigureAwait(false); + var images = await _providerManager.GetAvailableRemoteImages( + item, + new RemoteImageQuery(provider.Name) + { + IncludeAllLanguages = false, + IncludeDisabledProviders = false, + }, + cancellationToken).ConfigureAwait(false); var list = images.ToList(); int minWidth; foreach (var imageType in _singularImages) { - if (!IsEnabled(savedOptions, imageType, item)) continue; + if (!IsEnabled(savedOptions, imageType)) + { + continue; + } if (!HasImage(item, imageType) || (refreshOptions.IsReplacingImage(imageType) && !downloadedImages.Contains(imageType))) { @@ -286,8 +301,7 @@ namespace MediaBrowser.Providers.Manager minWidth = savedOptions.GetMinWidth(ImageType.Backdrop); await DownloadBackdrops(item, libraryOptions, ImageType.Backdrop, backdropLimit, provider, result, list, minWidth, cancellationToken).ConfigureAwait(false); - var hasScreenshots = item as IHasScreenshots; - if (hasScreenshots != null) + if (item is IHasScreenshots hasScreenshots) { minWidth = savedOptions.GetMinWidth(ImageType.Screenshot); await DownloadBackdrops(item, libraryOptions, ImageType.Screenshot, screenshotLimit, provider, result, list, minWidth, cancellationToken).ConfigureAwait(false); @@ -304,7 +318,7 @@ namespace MediaBrowser.Providers.Manager } } - private bool IsEnabled(TypeOptions options, ImageType type, BaseItem item) + private bool IsEnabled(TypeOptions options, ImageType type) { return options.IsEnabled(type); } @@ -329,7 +343,6 @@ namespace MediaBrowser.Providers.Manager } catch (FileNotFoundException) { - } } @@ -365,7 +378,6 @@ namespace MediaBrowser.Providers.Manager } else { - var newDateModified = _fileSystem.GetLastWriteTimeUtc(image.FileInfo); // If date changed then we need to reset saved image dimensions @@ -428,7 +440,9 @@ namespace MediaBrowser.Providers.Manager return changed; } - private async Task<bool> DownloadImage(BaseItem item, LibraryOptions libraryOptions, + private async Task<bool> DownloadImage( + BaseItem item, + LibraryOptions libraryOptions, IRemoteImageProvider provider, RefreshResult result, IEnumerable<RemoteImageInfo> images, @@ -440,10 +454,10 @@ namespace MediaBrowser.Providers.Manager .Where(i => i.Type == type && !(i.Width.HasValue && i.Width.Value < minWidth)) .ToList(); - if (EnableImageStub(item, type, libraryOptions) && eligibleImages.Count > 0) + if (EnableImageStub(item, libraryOptions) && eligibleImages.Count > 0) { SaveImageStub(item, type, eligibleImages.Select(i => i.Url)); - result.UpdateType = result.UpdateType | ItemUpdateType.ImageUpdate; + result.UpdateType |= ItemUpdateType.ImageUpdate; return true; } @@ -453,20 +467,29 @@ namespace MediaBrowser.Providers.Manager try { - var response = await provider.GetImageResponse(url, cancellationToken).ConfigureAwait(false); - - await _providerManager.SaveImage(item, response.Content, response.ContentType, type, null, cancellationToken).ConfigureAwait(false); - - result.UpdateType = result.UpdateType | ItemUpdateType.ImageUpdate; + using var response = await provider.GetImageResponse(url, cancellationToken).ConfigureAwait(false); + await using var stream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false); + + await _providerManager.SaveImage( + item, + stream, + response.Content.Headers.ContentType.MediaType, + type, + null, + cancellationToken).ConfigureAwait(false); + + result.UpdateType |= ItemUpdateType.ImageUpdate; return true; } catch (HttpException ex) { // Sometimes providers send back bad url's. Just move to the next image - if (ex.StatusCode.HasValue && ex.StatusCode.Value == HttpStatusCode.NotFound) + if (ex.StatusCode.HasValue + && (ex.StatusCode.Value == HttpStatusCode.NotFound || ex.StatusCode.Value == HttpStatusCode.Forbidden)) { continue; } + break; } } @@ -474,7 +497,7 @@ namespace MediaBrowser.Providers.Manager return false; } - private bool EnableImageStub(BaseItem item, ImageType type, LibraryOptions libraryOptions) + private bool EnableImageStub(BaseItem item, LibraryOptions libraryOptions) { if (item is LiveTvProgram) { @@ -500,11 +523,6 @@ namespace MediaBrowser.Providers.Manager return false; } - //if (!item.IsSaveLocalMetadataEnabled()) - //{ - // return true; - //} - return true; } @@ -517,14 +535,15 @@ namespace MediaBrowser.Providers.Manager private void SaveImageStub(BaseItem item, ImageType imageType, IEnumerable<string> urls, int newIndex) { - var path = string.Join("|", urls.Take(1).ToArray()); - - item.SetImage(new ItemImageInfo - { - Path = path, - Type = imageType + var path = string.Join('|', urls.Take(1)); - }, newIndex); + item.SetImage( + new ItemImageInfo + { + Path = path, + Type = imageType + }, + newIndex); } private async Task DownloadBackdrops(BaseItem item, LibraryOptions libraryOptions, ImageType imageType, int limit, IRemoteImageProvider provider, RefreshResult result, IEnumerable<RemoteImageInfo> images, int minWidth, CancellationToken cancellationToken) @@ -543,23 +562,23 @@ namespace MediaBrowser.Providers.Manager var url = image.Url; - if (EnableImageStub(item, imageType, libraryOptions)) + if (EnableImageStub(item, libraryOptions)) { SaveImageStub(item, imageType, new[] { url }); - result.UpdateType = result.UpdateType | ItemUpdateType.ImageUpdate; + result.UpdateType |= ItemUpdateType.ImageUpdate; continue; } try { - var response = await provider.GetImageResponse(url, cancellationToken).ConfigureAwait(false); + using var response = await provider.GetImageResponse(url, cancellationToken).ConfigureAwait(false); // If there's already an image of the same size, skip it - if (response.ContentLength.HasValue) + if (response.Content.Headers.ContentLength.HasValue) { try { - if (item.GetImages(imageType).Any(i => _fileSystem.GetFileInfo(i.Path).Length == response.ContentLength.Value)) + if (item.GetImages(imageType).Any(i => _fileSystem.GetFileInfo(i.Path).Length == response.Content.Headers.ContentLength.Value)) { response.Content.Dispose(); continue; @@ -571,16 +590,25 @@ namespace MediaBrowser.Providers.Manager } } - await _providerManager.SaveImage(item, response.Content, response.ContentType, imageType, null, cancellationToken).ConfigureAwait(false); + await using var stream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false); + await _providerManager.SaveImage( + item, + stream, + response.Content.Headers.ContentType.MediaType, + imageType, + null, + cancellationToken).ConfigureAwait(false); result.UpdateType = result.UpdateType | ItemUpdateType.ImageUpdate; } catch (HttpException ex) { // Sometimes providers send back bad urls. Just move onto the next image - if (ex.StatusCode.HasValue && ex.StatusCode.Value == HttpStatusCode.NotFound) + if (ex.StatusCode.HasValue + && (ex.StatusCode.Value == HttpStatusCode.NotFound || ex.StatusCode.Value == HttpStatusCode.Forbidden)) { continue; } + break; } } diff --git a/MediaBrowser.Providers/Manager/MetadataService.cs b/MediaBrowser.Providers/Manager/MetadataService.cs index c49aa407aa..42785b0574 100644 --- a/MediaBrowser.Providers/Manager/MetadataService.cs +++ b/MediaBrowser.Providers/Manager/MetadataService.cs @@ -1,3 +1,5 @@ +#pragma warning disable CS1591 + using System; using System.Collections.Generic; using System.Linq; @@ -19,13 +21,7 @@ namespace MediaBrowser.Providers.Manager where TItemType : BaseItem, IHasLookupInfo<TIdType>, new() where TIdType : ItemLookupInfo, new() { - protected readonly IServerConfigurationManager ServerConfigurationManager; - protected readonly ILogger Logger; - protected readonly IProviderManager ProviderManager; - protected readonly IFileSystem FileSystem; - protected readonly ILibraryManager LibraryManager; - - protected MetadataService(IServerConfigurationManager serverConfigurationManager, ILogger logger, IProviderManager providerManager, IFileSystem fileSystem, ILibraryManager libraryManager) + protected MetadataService(IServerConfigurationManager serverConfigurationManager, ILogger<MetadataService<TItemType, TIdType>> logger, IProviderManager providerManager, IFileSystem fileSystem, ILibraryManager libraryManager) { ServerConfigurationManager = serverConfigurationManager; Logger = logger; @@ -34,6 +30,26 @@ namespace MediaBrowser.Providers.Manager LibraryManager = libraryManager; } + protected IServerConfigurationManager ServerConfigurationManager { get; } + + protected ILogger<MetadataService<TItemType, TIdType>> Logger { get; } + + protected IProviderManager ProviderManager { get; } + + protected IFileSystem FileSystem { get; } + + protected ILibraryManager LibraryManager { get; } + + protected virtual bool EnableUpdatingPremiereDateFromChildren => false; + + protected virtual bool EnableUpdatingGenresFromChildren => false; + + protected virtual bool EnableUpdatingStudiosFromChildren => false; + + protected virtual bool EnableUpdatingOfficialRatingFromChildren => false; + + public virtual int Order => 0; + private FileSystemMetadata TryGetFile(string path, IDirectoryService directoryService) { try @@ -50,7 +66,6 @@ namespace MediaBrowser.Providers.Manager public async Task<ItemUpdateType> RefreshMetadata(BaseItem item, MetadataRefreshOptions refreshOptions, CancellationToken cancellationToken) { var itemOfType = (TItemType)item; - var config = ProviderManager.GetMetadataOptions(item); var updateType = ItemUpdateType.None; var requiresRefresh = false; @@ -84,7 +99,7 @@ namespace MediaBrowser.Providers.Manager // Always validate images and check for new locally stored ones. if (itemImageProvider.ValidateImages(item, allImageProviders.OfType<ILocalImageProvider>(), refreshOptions.DirectoryService)) { - updateType = updateType | ItemUpdateType.ImageUpdate; + updateType |= ItemUpdateType.ImageUpdate; } } catch (Exception ex) @@ -100,7 +115,7 @@ namespace MediaBrowser.Providers.Manager bool hasRefreshedMetadata = true; bool hasRefreshedImages = true; - var isFirstRefresh = item.DateLastRefreshed == default(DateTime); + var isFirstRefresh = item.DateLastRefreshed == default; // Next run metadata providers if (refreshOptions.MetadataRefreshMode != MetadataRefreshMode.None) @@ -112,7 +127,7 @@ namespace MediaBrowser.Providers.Manager { if (item.BeforeMetadataRefresh(refreshOptions.ReplaceAllMetadata)) { - updateType = updateType | ItemUpdateType.MetadataImport; + updateType |= ItemUpdateType.MetadataImport; } } @@ -125,12 +140,12 @@ namespace MediaBrowser.Providers.Manager ApplySearchResult(id, refreshOptions.SearchResult); } - //await FindIdentities(id, cancellationToken).ConfigureAwait(false); + // await FindIdentities(id, cancellationToken).ConfigureAwait(false); id.IsAutomated = refreshOptions.IsAutomated; var result = await RefreshWithProviders(metadataResult, id, refreshOptions, providers, itemImageProvider, cancellationToken).ConfigureAwait(false); - updateType = updateType | result.UpdateType; + updateType |= result.UpdateType; if (result.Failures > 0) { hasRefreshedMetadata = false; @@ -145,9 +160,9 @@ namespace MediaBrowser.Providers.Manager if (providers.Count > 0) { - var result = await itemImageProvider.RefreshImages(itemOfType, libraryOptions, providers, refreshOptions, config, cancellationToken).ConfigureAwait(false); + var result = await itemImageProvider.RefreshImages(itemOfType, libraryOptions, providers, refreshOptions, cancellationToken).ConfigureAwait(false); - updateType = updateType | result.UpdateType; + updateType |= result.UpdateType; if (result.Failures > 0) { hasRefreshedImages = false; @@ -156,7 +171,7 @@ namespace MediaBrowser.Providers.Manager } var beforeSaveResult = BeforeSave(itemOfType, isFirstRefresh || refreshOptions.ReplaceAllMetadata || refreshOptions.MetadataRefreshMode == MetadataRefreshMode.FullRefresh || requiresRefresh || refreshOptions.ForceSave, updateType); - updateType = updateType | beforeSaveResult; + updateType |= beforeSaveResult; // Save if changes were made, or it's never been saved before if (refreshOptions.ForceSave || updateType > ItemUpdateType.None || isFirstRefresh || refreshOptions.ReplaceAllMetadata || requiresRefresh) @@ -173,7 +188,7 @@ namespace MediaBrowser.Providers.Manager // If any of these properties are set then make sure the updateType is not None, just to force everything to save if (refreshOptions.ForceSave || refreshOptions.ReplaceAllMetadata) { - updateType = updateType | ItemUpdateType.MetadataDownload; + updateType |= ItemUpdateType.MetadataDownload; } if (hasRefreshedMetadata && hasRefreshedImages) @@ -182,11 +197,11 @@ namespace MediaBrowser.Providers.Manager } else { - item.DateLastRefreshed = default(DateTime); + item.DateLastRefreshed = default; } // Save to database - SaveItem(metadataResult, libraryOptions, updateType, cancellationToken); + await SaveItemAsync(metadataResult, libraryOptions, updateType, cancellationToken).ConfigureAwait(false); } await AfterMetadataRefresh(itemOfType, refreshOptions, cancellationToken).ConfigureAwait(false); @@ -201,25 +216,26 @@ namespace MediaBrowser.Providers.Manager lookupInfo.Year = result.ProductionYear; } - protected void SaveItem(MetadataResult<TItemType> result, LibraryOptions libraryOptions, ItemUpdateType reason, CancellationToken cancellationToken) + protected async Task SaveItemAsync(MetadataResult<TItemType> result, LibraryOptions libraryOptions, ItemUpdateType reason, CancellationToken cancellationToken) { if (result.Item.SupportsPeople && result.People != null) { var baseItem = result.Item; LibraryManager.UpdatePeople(baseItem, result.People); - SavePeopleMetadata(result.People, libraryOptions, cancellationToken); + await SavePeopleMetadataAsync(result.People, libraryOptions, cancellationToken).ConfigureAwait(false); } - result.Item.UpdateToRepository(reason, cancellationToken); + + await result.Item.UpdateToRepositoryAsync(reason, cancellationToken).ConfigureAwait(false); } - private void SavePeopleMetadata(List<PersonInfo> people, LibraryOptions libraryOptions, CancellationToken cancellationToken) + private async Task SavePeopleMetadataAsync(List<PersonInfo> people, LibraryOptions libraryOptions, CancellationToken cancellationToken) { foreach (var person in people) { cancellationToken.ThrowIfCancellationRequested(); - if (person.ProviderIds.Any() || !string.IsNullOrWhiteSpace(person.ImageUrl)) + if (person.ProviderIds.Count > 0 || !string.IsNullOrWhiteSpace(person.ImageUrl)) { var updateType = ItemUpdateType.MetadataDownload; @@ -236,40 +252,42 @@ namespace MediaBrowser.Providers.Manager if (!string.IsNullOrWhiteSpace(person.ImageUrl) && !personEntity.HasImage(ImageType.Primary)) { - AddPersonImage(personEntity, libraryOptions, person.ImageUrl, cancellationToken); + await AddPersonImageAsync(personEntity, libraryOptions, person.ImageUrl, cancellationToken).ConfigureAwait(false); saveEntity = true; - updateType = updateType | ItemUpdateType.ImageUpdate; + updateType |= ItemUpdateType.ImageUpdate; } if (saveEntity) { - personEntity.UpdateToRepository(updateType, cancellationToken); + await personEntity.UpdateToRepositoryAsync(updateType, cancellationToken).ConfigureAwait(false); } } } } - private void AddPersonImage(Person personEntity, LibraryOptions libraryOptions, string imageUrl, CancellationToken cancellationToken) + private async Task AddPersonImageAsync(Person personEntity, LibraryOptions libraryOptions, string imageUrl, CancellationToken cancellationToken) { - //if (libraryOptions.DownloadImagesInAdvance) - //{ - // try - // { - // await ProviderManager.SaveImage(personEntity, imageUrl, ImageType.Primary, null, cancellationToken).ConfigureAwait(false); - // return; - // } - // catch (Exception ex) - // { - // Logger.LogError(ex, "Error in AddPersonImage"); - // } - //} - - personEntity.SetImage(new ItemImageInfo - { - Path = imageUrl, - Type = ImageType.Primary - }, 0); + if (libraryOptions.DownloadImagesInAdvance) + { + try + { + await ProviderManager.SaveImage(personEntity, imageUrl, ImageType.Primary, null, cancellationToken).ConfigureAwait(false); + return; + } + catch (Exception ex) + { + Logger.LogError(ex, "Error in AddPersonImage"); + } + } + + personEntity.SetImage( + new ItemImageInfo + { + Path = imageUrl, + Type = ImageType.Primary + }, + 0); } protected virtual Task AfterMetadataRefresh(TItemType item, MetadataRefreshOptions refreshOptions, CancellationToken cancellationToken) @@ -324,6 +342,7 @@ namespace MediaBrowser.Providers.Manager { return true; } + var folder = item as Folder; if (folder != null) { @@ -389,7 +408,7 @@ namespace MediaBrowser.Providers.Manager { if (!child.IsFolder) { - ticks += (child.RunTimeTicks ?? 0); + ticks += child.RunTimeTicks ?? 0; } } @@ -422,6 +441,7 @@ namespace MediaBrowser.Providers.Manager { dateLastMediaAdded = childDateCreated; } + any = true; } } @@ -436,14 +456,6 @@ namespace MediaBrowser.Providers.Manager return updateType; } - protected virtual bool EnableUpdatingPremiereDateFromChildren => false; - - protected virtual bool EnableUpdatingGenresFromChildren => false; - - protected virtual bool EnableUpdatingStudiosFromChildren => false; - - protected virtual bool EnableUpdatingOfficialRatingFromChildren => false; - private ItemUpdateType UpdatePremiereDate(TItemType item, IList<BaseItem> children) { var updateType = ItemUpdateType.None; @@ -486,7 +498,7 @@ namespace MediaBrowser.Providers.Manager { var updateType = ItemUpdateType.None; - if (!item.LockedFields.Contains(MetadataFields.Genres)) + if (!item.LockedFields.Contains(MetadataField.Genres)) { var currentList = item.Genres; @@ -507,7 +519,7 @@ namespace MediaBrowser.Providers.Manager { var updateType = ItemUpdateType.None; - if (!item.LockedFields.Contains(MetadataFields.Studios)) + if (!item.LockedFields.Contains(MetadataField.Studios)) { var currentList = item.Studios; @@ -528,7 +540,7 @@ namespace MediaBrowser.Providers.Manager { var updateType = ItemUpdateType.None; - if (!item.LockedFields.Contains(MetadataFields.OfficialRating)) + if (!item.LockedFields.Contains(MetadataField.OfficialRating)) { if (item.UpdateRatingToItems(children)) { @@ -652,7 +664,8 @@ namespace MediaBrowser.Providers.Manager return type == typeof(TItemType); } - protected virtual async Task<RefreshResult> RefreshWithProviders(MetadataResult<TItemType> metadata, + protected virtual async Task<RefreshResult> RefreshWithProviders( + MetadataResult<TItemType> metadata, TIdType id, MetadataRefreshOptions options, List<IMetadataProvider> providers, @@ -718,7 +731,7 @@ namespace MediaBrowser.Providers.Manager userDataList.AddRange(localItem.UserDataList); } - MergeData(localItem, temp, new MetadataFields[] { }, !options.ReplaceAllMetadata, true); + MergeData(localItem, temp, Array.Empty<MetadataField>(), !options.ReplaceAllMetadata, true); refreshResult.UpdateType = refreshResult.UpdateType | ItemUpdateType.MetadataImport; // Only one local provider allowed per item @@ -726,6 +739,7 @@ namespace MediaBrowser.Providers.Manager { hasLocalMetadata = true; } + break; } @@ -766,20 +780,20 @@ namespace MediaBrowser.Providers.Manager else { // TODO: If the new metadata from above has some blank data, this can cause old data to get filled into those empty fields - MergeData(metadata, temp, new MetadataFields[] { }, false, false); + MergeData(metadata, temp, Array.Empty<MetadataField>(), false, false); MergeData(temp, metadata, item.LockedFields, true, false); } } } - //var isUnidentified = failedProviderCount > 0 && successfulProviderCount == 0; + // var isUnidentified = failedProviderCount > 0 && successfulProviderCount == 0; foreach (var provider in customProviders.Where(i => !(i is IPreRefreshProvider))) { await RunCustomProvider(provider, item, logName, options, refreshResult, cancellationToken).ConfigureAwait(false); } - //ImportUserData(item, userDataList, cancellationToken); + // ImportUserData(item, userDataList, cancellationToken); return refreshResult; } @@ -843,7 +857,7 @@ namespace MediaBrowser.Providers.Manager { result.Provider = provider.Name; - MergeData(result, temp, new MetadataFields[] { }, false, false); + MergeData(result, temp, Array.Empty<MetadataField>(), false, false); MergeNewData(temp.Item, id); refreshResult.UpdateType = refreshResult.UpdateType | ItemUpdateType.MetadataDownload; @@ -874,6 +888,7 @@ namespace MediaBrowser.Providers.Manager { return "en"; } + return language; } @@ -892,24 +907,23 @@ namespace MediaBrowser.Providers.Manager } } - protected abstract void MergeData(MetadataResult<TItemType> source, + protected abstract void MergeData( + MetadataResult<TItemType> source, MetadataResult<TItemType> target, - MetadataFields[] lockedFields, + MetadataField[] lockedFields, bool replaceData, bool mergeMetadataSettings); - public virtual int Order => 0; - private bool HasChanged(BaseItem item, IHasItemChangeMonitor changeMonitor, IDirectoryService directoryService) { try { var hasChanged = changeMonitor.HasChanged(item, directoryService); - //if (hasChanged) - //{ - // logger.LogDebug("{0} reports change to {1}", changeMonitor.GetType().Name, item.Path ?? item.Name); - //} + if (hasChanged) + { + Logger.LogDebug("{0} reports change to {1}", changeMonitor.GetType().Name, item.Path ?? item.Name); + } return hasChanged; } @@ -920,11 +934,4 @@ namespace MediaBrowser.Providers.Manager } } } - - public class RefreshResult - { - public ItemUpdateType UpdateType { get; set; } - public string ErrorMessage { get; set; } - public int Failures { get; set; } - } } diff --git a/MediaBrowser.Providers/Manager/ProviderManager.cs b/MediaBrowser.Providers/Manager/ProviderManager.cs index cfff897672..b6fb4267f4 100644 --- a/MediaBrowser.Providers/Manager/ProviderManager.cs +++ b/MediaBrowser.Providers/Manager/ProviderManager.cs @@ -1,13 +1,15 @@ -#pragma warning disable CS1591 - using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Globalization; using System.IO; using System.Linq; +using System.Net; +using System.Net.Http; +using System.Net.Mime; using System.Threading; using System.Threading.Tasks; +using Jellyfin.Data.Events; using MediaBrowser.Common.Net; using MediaBrowser.Common.Progress; using MediaBrowser.Controller; @@ -16,109 +18,118 @@ using MediaBrowser.Controller.Dto; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities.Audio; using MediaBrowser.Controller.Entities.Movies; -using MediaBrowser.Controller.Entities.TV; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Providers; using MediaBrowser.Controller.Subtitles; using MediaBrowser.Model.Configuration; using MediaBrowser.Model.Entities; -using MediaBrowser.Model.Events; using MediaBrowser.Model.IO; +using MediaBrowser.Model.Net; using MediaBrowser.Model.Providers; -using MediaBrowser.Model.Serialization; using Microsoft.Extensions.Logging; using Priority_Queue; +using Book = MediaBrowser.Controller.Entities.Book; +using Episode = MediaBrowser.Controller.Entities.TV.Episode; +using Movie = MediaBrowser.Controller.Entities.Movies.Movie; +using MusicAlbum = MediaBrowser.Controller.Entities.Audio.MusicAlbum; +using Season = MediaBrowser.Controller.Entities.TV.Season; +using Series = MediaBrowser.Controller.Entities.TV.Series; namespace MediaBrowser.Providers.Manager { /// <summary> - /// Class ProviderManager + /// Class ProviderManager. /// </summary> public class ProviderManager : IProviderManager, IDisposable { - private readonly ILogger _logger; - private readonly IHttpClient _httpClient; + private readonly object _refreshQueueLock = new object(); + private readonly ILogger<ProviderManager> _logger; + private readonly IHttpClientFactory _httpClientFactory; private readonly ILibraryMonitor _libraryMonitor; private readonly IFileSystem _fileSystem; private readonly IServerApplicationPaths _appPaths; - private readonly IJsonSerializer _json; private readonly ILibraryManager _libraryManager; private readonly ISubtitleManager _subtitleManager; private readonly IServerConfigurationManager _configurationManager; + private readonly ConcurrentDictionary<Guid, double> _activeRefreshes = new ConcurrentDictionary<Guid, double>(); + private readonly CancellationTokenSource _disposeCancellationTokenSource = new CancellationTokenSource(); + private readonly SimplePriorityQueue<Tuple<Guid, MetadataRefreshOptions>> _refreshQueue = + new SimplePriorityQueue<Tuple<Guid, MetadataRefreshOptions>>(); - private IImageProvider[] ImageProviders { get; set; } - - private IMetadataService[] _metadataServices = { }; - private IMetadataProvider[] _metadataProviders = { }; + private IMetadataService[] _metadataServices = Array.Empty<IMetadataService>(); + private IMetadataProvider[] _metadataProviders = Array.Empty<IMetadataProvider>(); private IEnumerable<IMetadataSaver> _savers; - private IExternalId[] _externalIds; - - private CancellationTokenSource _disposeCancellationTokenSource = new CancellationTokenSource(); - - public event EventHandler<GenericEventArgs<BaseItem>> RefreshStarted; - public event EventHandler<GenericEventArgs<BaseItem>> RefreshCompleted; - public event EventHandler<GenericEventArgs<Tuple<BaseItem, double>>> RefreshProgress; + private bool _isProcessingRefreshQueue; + private bool _disposed; /// <summary> - /// Initializes a new instance of the <see cref="ProviderManager" /> class. + /// Initializes a new instance of the <see cref="ProviderManager"/> class. /// </summary> + /// <param name="httpClientFactory">The Http client factory.</param> + /// <param name="subtitleManager">The subtitle manager.</param> + /// <param name="configurationManager">The configuration manager.</param> + /// <param name="libraryMonitor">The library monitor.</param> + /// <param name="logger">The logger.</param> + /// <param name="fileSystem">The filesystem.</param> + /// <param name="appPaths">The server application paths.</param> + /// <param name="libraryManager">The library manager.</param> public ProviderManager( - IHttpClient httpClient, + IHttpClientFactory httpClientFactory, ISubtitleManager subtitleManager, IServerConfigurationManager configurationManager, ILibraryMonitor libraryMonitor, ILogger<ProviderManager> logger, IFileSystem fileSystem, IServerApplicationPaths appPaths, - ILibraryManager libraryManager, - IJsonSerializer json) + ILibraryManager libraryManager) { _logger = logger; - _httpClient = httpClient; + _httpClientFactory = httpClientFactory; _configurationManager = configurationManager; _libraryMonitor = libraryMonitor; _fileSystem = fileSystem; _appPaths = appPaths; _libraryManager = libraryManager; - _json = json; _subtitleManager = subtitleManager; } - /// <summary> - /// Adds the metadata providers. - /// </summary> - public void AddParts(IEnumerable<IImageProvider> imageProviders, IEnumerable<IMetadataService> metadataServices, - IEnumerable<IMetadataProvider> metadataProviders, IEnumerable<IMetadataSaver> metadataSavers, - IEnumerable<IExternalId> externalIds) + /// <inheritdoc/> + public event EventHandler<GenericEventArgs<BaseItem>> RefreshStarted; + + /// <inheritdoc/> + public event EventHandler<GenericEventArgs<BaseItem>> RefreshCompleted; + + /// <inheritdoc/> + public event EventHandler<GenericEventArgs<Tuple<BaseItem, double>>> RefreshProgress; + + private IImageProvider[] ImageProviders { get; set; } + + /// <inheritdoc/> + public void AddParts( + IEnumerable<IImageProvider> imageProviders, + IEnumerable<IMetadataService> metadataServices, + IEnumerable<IMetadataProvider> metadataProviders, + IEnumerable<IMetadataSaver> metadataSavers, + IEnumerable<IExternalId> externalIds) { ImageProviders = imageProviders.ToArray(); _metadataServices = metadataServices.OrderBy(i => i.Order).ToArray(); _metadataProviders = metadataProviders.ToArray(); - _externalIds = externalIds.OrderBy(i => i.Name).ToArray(); - - _savers = metadataSavers.Where(i => - { - var configurable = i as IConfigurableProvider; + _externalIds = externalIds.OrderBy(i => i.ProviderName).ToArray(); - return configurable == null || configurable.IsEnabled; - }).ToArray(); + _savers = metadataSavers + .Where(i => !(i is IConfigurableProvider configurable) || configurable.IsEnabled) + .ToArray(); } + /// <inheritdoc/> public Task<ItemUpdateType> RefreshSingleItem(BaseItem item, MetadataRefreshOptions options, CancellationToken cancellationToken) { - IMetadataService service = null; var type = item.GetType(); - foreach (var current in _metadataServices) - { - if (current.CanRefreshPrimary(type)) - { - service = current; - break; - } - } + var service = _metadataServices.FirstOrDefault(current => current.CanRefreshPrimary(type)); if (service == null) { @@ -141,35 +152,50 @@ namespace MediaBrowser.Providers.Manager return Task.FromResult(ItemUpdateType.None); } + /// <inheritdoc/> public async Task SaveImage(BaseItem item, string url, ImageType type, int? imageIndex, CancellationToken cancellationToken) { - using (var response = await _httpClient.GetResponse(new HttpRequestOptions - { - CancellationToken = cancellationToken, - Url = url, - BufferContent = false + var httpClient = _httpClientFactory.CreateClient(NamedClient.Default); + using var response = await httpClient.GetAsync(url, cancellationToken).ConfigureAwait(false); + + var contentType = response.Content.Headers.ContentType.MediaType; - }).ConfigureAwait(false)) + // Workaround for tvheadend channel icons + // TODO: Isolate this hack into the tvh plugin + if (string.IsNullOrEmpty(contentType)) { - // Workaround for tvheadend channel icons - // TODO: Isolate this hack into the tvh plugin - if (string.IsNullOrEmpty(response.ContentType)) + if (url.IndexOf("/imagecache/", StringComparison.OrdinalIgnoreCase) != -1) { - if (url.IndexOf("/imagecache/", StringComparison.OrdinalIgnoreCase) != -1) - { - response.ContentType = "image/png"; - } + contentType = "image/png"; } + } - await SaveImage(item, response.Content, response.ContentType, type, imageIndex, cancellationToken).ConfigureAwait(false); + // thetvdb will sometimes serve a rubbish 404 html page with a 200 OK code, because reasons... + if (contentType.Equals(MediaTypeNames.Text.Html, StringComparison.OrdinalIgnoreCase)) + { + throw new HttpException("Invalid image received.") + { + StatusCode = HttpStatusCode.NotFound + }; } + + await using var stream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false); + await SaveImage( + item, + stream, + contentType, + type, + imageIndex, + cancellationToken).ConfigureAwait(false); } + /// <inheritdoc/> public Task SaveImage(BaseItem item, Stream source, string mimeType, ImageType type, int? imageIndex, CancellationToken cancellationToken) { return new ImageSaver(_configurationManager, _libraryMonitor, _fileSystem, _logger).SaveImage(item, source, mimeType, type, imageIndex, cancellationToken); } + /// <inheritdoc/> public Task SaveImage(BaseItem item, string source, string mimeType, ImageType type, int? imageIndex, bool? saveLocallyWithMedia, CancellationToken cancellationToken) { if (string.IsNullOrWhiteSpace(source)) @@ -182,6 +208,14 @@ namespace MediaBrowser.Providers.Manager return new ImageSaver(_configurationManager, _libraryMonitor, _fileSystem, _logger).SaveImage(item, fileStream, mimeType, type, imageIndex, saveLocallyWithMedia, cancellationToken); } + /// <inheritdoc/> + public Task SaveImage(Stream source, string mimeType, string path) + { + return new ImageSaver(_configurationManager, _libraryMonitor, _fileSystem, _logger) + .SaveImage(source, path); + } + + /// <inheritdoc/> public async Task<IEnumerable<RemoteImageInfo>> GetAvailableRemoteImages(BaseItem item, RemoteImageQuery query, CancellationToken cancellationToken) { var providers = GetRemoteImageProviders(item, query.IncludeDisabledProviders); @@ -201,7 +235,7 @@ namespace MediaBrowser.Providers.Manager languages.Add(preferredLanguage); } - var tasks = providers.Select(i => GetImages(item, cancellationToken, i, languages, query.ImageType)); + var tasks = providers.Select(i => GetImages(item, i, languages, cancellationToken, query.ImageType)); var results = await Task.WhenAll(tasks).ConfigureAwait(false); @@ -212,12 +246,17 @@ namespace MediaBrowser.Providers.Manager /// Gets the images. /// </summary> /// <param name="item">The item.</param> - /// <param name="cancellationToken">The cancellation token.</param> /// <param name="provider">The provider.</param> /// <param name="preferredLanguages">The preferred languages.</param> + /// <param name="cancellationToken">The cancellation token.</param> /// <param name="type">The type.</param> /// <returns>Task{IEnumerable{RemoteImageInfo}}.</returns> - private async Task<IEnumerable<RemoteImageInfo>> GetImages(BaseItem item, CancellationToken cancellationToken, IRemoteImageProvider provider, List<string> preferredLanguages, ImageType? type = null) + private async Task<IEnumerable<RemoteImageInfo>> GetImages( + BaseItem item, + IRemoteImageProvider provider, + IReadOnlyCollection<string> preferredLanguages, + CancellationToken cancellationToken, + ImageType? type = null) { try { @@ -243,25 +282,23 @@ namespace MediaBrowser.Providers.Manager } catch (Exception ex) { - _logger.LogError(ex, "{0} failed in GetImageInfos for type {1}", provider.GetType().Name, item.GetType().Name); + _logger.LogError(ex, "{ProviderName} failed in GetImageInfos for type {ItemType} at {ItemPath}", provider.GetType().Name, item.GetType().Name, item.Path); return new List<RemoteImageInfo>(); } } - /// <summary> - /// Gets the supported image providers. - /// </summary> - /// <param name="item">The item.</param> - /// <returns>IEnumerable{IImageProvider}.</returns> + /// <inheritdoc/> public IEnumerable<ImageProviderInfo> GetRemoteImageProviderInfo(BaseItem item) { - return GetRemoteImageProviders(item, true).Select(i => new ImageProviderInfo - { - Name = i.Name, - SupportedImages = i.GetSupportedImages(item).ToArray() - }); + return GetRemoteImageProviders(item, true).Select(i => new ImageProviderInfo(i.Name, i.GetSupportedImages(item).ToArray())); } + /// <summary> + /// Gets the image providers for the provided item. + /// </summary> + /// <param name="item">The item.</param> + /// <param name="refreshOptions">The image refresh options.</param> + /// <returns>The image providers for the item.</returns> public IEnumerable<IImageProvider> GetImageProviders(BaseItem item, ImageRefreshOptions refreshOptions) { return GetImageProviders(item, _libraryManager.GetLibraryOptions(item), GetMetadataOptions(item), refreshOptions, false); @@ -275,7 +312,7 @@ namespace MediaBrowser.Providers.Manager var typeOptions = libraryOptions.GetTypeOptions(item.GetType().Name); var typeFetcherOrder = typeOptions?.ImageFetcherOrder; - return ImageProviders.Where(i => CanRefresh(i, item, libraryOptions, options, refreshOptions, includeDisabled)) + return ImageProviders.Where(i => CanRefresh(i, item, libraryOptions, refreshOptions, includeDisabled)) .OrderBy(i => { // See if there's a user-defined order @@ -296,6 +333,13 @@ namespace MediaBrowser.Providers.Manager .ThenBy(GetOrder); } + /// <summary> + /// Gets the metadata providers for the provided item. + /// </summary> + /// <param name="item">The item.</param> + /// <param name="libraryOptions">The library options.</param> + /// <typeparam name="T">The type of metadata provider.</typeparam> + /// <returns>The metadata providers.</returns> public IEnumerable<IMetadataProvider<T>> GetMetadataProviders<T>(BaseItem item, LibraryOptions libraryOptions) where T : BaseItem { @@ -311,7 +355,7 @@ namespace MediaBrowser.Providers.Manager var currentOptions = globalMetadataOptions; return _metadataProviders.OfType<IMetadataProvider<T>>() - .Where(i => CanRefresh(i, item, libraryOptions, currentOptions, includeDisabled, forceEnableInternetMetadata)) + .Where(i => CanRefresh(i, item, libraryOptions, includeDisabled, forceEnableInternetMetadata)) .OrderBy(i => GetConfiguredOrder(item, i, libraryOptions, globalMetadataOptions)) .ThenBy(GetDefaultOrder); } @@ -321,14 +365,20 @@ namespace MediaBrowser.Providers.Manager var options = GetMetadataOptions(item); var libraryOptions = _libraryManager.GetLibraryOptions(item); - return GetImageProviders(item, libraryOptions, options, - new ImageRefreshOptions( - new DirectoryService(_fileSystem)), - includeDisabled) - .OfType<IRemoteImageProvider>(); + return GetImageProviders( + item, + libraryOptions, + options, + new ImageRefreshOptions(new DirectoryService(_fileSystem)), + includeDisabled).OfType<IRemoteImageProvider>(); } - private bool CanRefresh(IMetadataProvider provider, BaseItem item, LibraryOptions libraryOptions, MetadataOptions options, bool includeDisabled, bool forceEnableInternetMetadata) + private bool CanRefresh( + IMetadataProvider provider, + BaseItem item, + LibraryOptions libraryOptions, + bool includeDisabled, + bool forceEnableInternetMetadata) { if (!includeDisabled) { @@ -364,7 +414,12 @@ namespace MediaBrowser.Providers.Manager return true; } - private bool CanRefresh(IImageProvider provider, BaseItem item, LibraryOptions libraryOptions, MetadataOptions options, ImageRefreshOptions refreshOptions, bool includeDisabled) + private bool CanRefresh( + IImageProvider provider, + BaseItem item, + LibraryOptions libraryOptions, + ImageRefreshOptions refreshOptions, + bool includeDisabled) { if (!includeDisabled) { @@ -392,7 +447,7 @@ namespace MediaBrowser.Providers.Manager } catch (Exception ex) { - _logger.LogError(ex, "{0} failed in Supports for type {1}", provider.GetType().Name, item.GetType().Name); + _logger.LogError(ex, "{ProviderName} failed in Supports for type {ItemType} at {ItemPath}", provider.GetType().Name, item.GetType().Name, item.Path); return false; } } @@ -404,9 +459,7 @@ namespace MediaBrowser.Providers.Manager /// <returns>System.Int32.</returns> private int GetOrder(IImageProvider provider) { - var hasOrder = provider as IHasOrder; - - if (hasOrder == null) + if (!(provider is IHasOrder hasOrder)) { return 0; } @@ -433,7 +486,7 @@ namespace MediaBrowser.Providers.Manager if (provider is IRemoteMetadataProvider) { var typeOptions = libraryOptions.GetTypeOptions(item.GetType().Name); - var typeFetcherOrder = typeOptions == null ? null : typeOptions.MetadataFetcherOrder; + var typeFetcherOrder = typeOptions?.MetadataFetcherOrder; var fetcherOrder = typeFetcherOrder ?? globalMetadataOptions.MetadataFetcherOrder; @@ -451,9 +504,7 @@ namespace MediaBrowser.Providers.Manager private int GetDefaultOrder(IMetadataProvider provider) { - var hasOrder = provider as IHasOrder; - - if (hasOrder != null) + if (provider is IHasOrder hasOrder) { return hasOrder.Order; } @@ -461,9 +512,10 @@ namespace MediaBrowser.Providers.Manager return 0; } + /// <inheritdoc/> public MetadataPluginSummary[] GetAllMetadataPlugins() { - return new MetadataPluginSummary[] + return new[] { GetPluginSummary<Movie>(), GetPluginSummary<BoxSet>(), @@ -485,7 +537,7 @@ namespace MediaBrowser.Providers.Manager where T : BaseItem, new() { // Give it a dummy path just so that it looks like a file system item - var dummy = new T() + var dummy = new T { Path = Path.Combine(_appPaths.InternalMetadataPath, "dummy"), ParentId = Guid.NewGuid() @@ -500,16 +552,17 @@ namespace MediaBrowser.Providers.Manager var libraryOptions = new LibraryOptions(); - var imageProviders = GetImageProviders(dummy, libraryOptions, options, - new ImageRefreshOptions( - new DirectoryService(_fileSystem)), - true) - .ToList(); + var imageProviders = GetImageProviders( + dummy, + libraryOptions, + options, + new ImageRefreshOptions(new DirectoryService(_fileSystem)), + true).ToList(); var pluginList = summary.Plugins.ToList(); AddMetadataPlugins(pluginList, dummy, libraryOptions, options); - AddImagePlugins(pluginList, dummy, imageProviders); + AddImagePlugins(pluginList, imageProviders); var subtitleProviders = _subtitleManager.GetSupportedProviders(dummy); @@ -540,14 +593,14 @@ namespace MediaBrowser.Providers.Manager var providers = GetMetadataProvidersInternal<T>(item, libraryOptions, options, true, true).ToList(); // Locals - list.AddRange(providers.Where(i => (i is ILocalMetadataProvider)).Select(i => new MetadataPlugin + list.AddRange(providers.Where(i => i is ILocalMetadataProvider).Select(i => new MetadataPlugin { Name = i.Name, Type = MetadataPluginType.LocalMetadataProvider })); // Fetchers - list.AddRange(providers.Where(i => (i is IRemoteMetadataProvider)).Select(i => new MetadataPlugin + list.AddRange(providers.Where(i => i is IRemoteMetadataProvider).Select(i => new MetadataPlugin { Name = i.Name, Type = MetadataPluginType.MetadataFetcher @@ -561,12 +614,10 @@ namespace MediaBrowser.Providers.Manager })); } - private void AddImagePlugins<T>(List<MetadataPlugin> list, T item, List<IImageProvider> imageProviders) - where T : BaseItem + private void AddImagePlugins(List<MetadataPlugin> list, List<IImageProvider> imageProviders) { - // Locals - list.AddRange(imageProviders.Where(i => (i is ILocalImageProvider)).Select(i => new MetadataPlugin + list.AddRange(imageProviders.Where(i => i is ILocalImageProvider).Select(i => new MetadataPlugin { Name = i.Name, Type = MetadataPluginType.LocalImageProvider @@ -580,6 +631,7 @@ namespace MediaBrowser.Providers.Manager })); } + /// <inheritdoc/> public MetadataOptions GetMetadataOptions(BaseItem item) { var type = item.GetType().Name; @@ -589,17 +641,13 @@ namespace MediaBrowser.Providers.Manager new MetadataOptions(); } - /// <summary> - /// Saves the metadata. - /// </summary> + /// <inheritdoc/> public void SaveMetadata(BaseItem item, ItemUpdateType updateType) { SaveMetadata(item, updateType, _savers); } - /// <summary> - /// Saves the metadata. - /// </summary> + /// <inheritdoc/> public void SaveMetadata(BaseItem item, ItemUpdateType updateType, IEnumerable<string> savers) { SaveMetadata(item, updateType, _savers.Where(i => savers.Contains(i.Name, StringComparer.OrdinalIgnoreCase))); @@ -611,7 +659,6 @@ namespace MediaBrowser.Providers.Manager /// <param name="item">The item.</param> /// <param name="updateType">Type of the update.</param> /// <param name="savers">The savers.</param> - /// <returns>Task.</returns> private void SaveMetadata(BaseItem item, ItemUpdateType updateType, IEnumerable<IMetadataSaver> savers) { var libraryOptions = _libraryManager.GetLibraryOptions(item); @@ -620,11 +667,9 @@ namespace MediaBrowser.Providers.Manager { _logger.LogDebug("Saving {0} to {1}.", item.Path ?? item.Name, saver.Name); - var fileSaver = saver as IMetadataFileSaver; - - if (fileSaver != null) + if (saver is IMetadataFileSaver fileSaver) { - string path = null; + string path; try { @@ -691,11 +736,9 @@ namespace MediaBrowser.Providers.Manager { if (updateType >= ItemUpdateType.MetadataEdit) { - var fileSaver = saver as IMetadataFileSaver; - // Manual edit occurred // Even if save local is off, save locally anyway if the metadata file already exists - if (fileSaver == null || !File.Exists(fileSaver.GetSavePath(item))) + if (!(saver is IMetadataFileSaver fileSaver) || !File.Exists(fileSaver.GetSavePath(item))) { return false; } @@ -726,6 +769,7 @@ namespace MediaBrowser.Providers.Manager } } + /// <inheritdoc/> public Task<IEnumerable<RemoteSearchResult>> GetRemoteSearchResults<TItemType, TLookupType>(RemoteSearchQuery<TLookupType> searchInfo, CancellationToken cancellationToken) where TItemType : BaseItem, new() where TLookupType : ItemLookupInfo @@ -740,7 +784,7 @@ namespace MediaBrowser.Providers.Manager return GetRemoteSearchResults<TItemType, TLookupType>(searchInfo, referenceItem, cancellationToken); } - public async Task<IEnumerable<RemoteSearchResult>> GetRemoteSearchResults<TItemType, TLookupType>(RemoteSearchQuery<TLookupType> searchInfo, BaseItem referenceItem, CancellationToken cancellationToken) + private async Task<IEnumerable<RemoteSearchResult>> GetRemoteSearchResults<TItemType, TLookupType>(RemoteSearchQuery<TLookupType> searchInfo, BaseItem referenceItem, CancellationToken cancellationToken) where TItemType : BaseItem, new() where TLookupType : ItemLookupInfo { @@ -779,6 +823,7 @@ namespace MediaBrowser.Providers.Manager { searchInfo.SearchInfo.MetadataLanguage = _configurationManager.Configuration.PreferredMetadataLanguage; } + if (string.IsNullOrWhiteSpace(searchInfo.SearchInfo.MetadataCountryCode)) { searchInfo.SearchInfo.MetadataCountryCode = _configurationManager.Configuration.MetadataCountryCode; @@ -823,12 +868,14 @@ namespace MediaBrowser.Providers.Manager } } - //_logger.LogDebug("Returning search results {0}", _json.SerializeToString(resultList)); + // _logger.LogDebug("Returning search results {0}", _json.SerializeToString(resultList)); return resultList; } - private async Task<IEnumerable<RemoteSearchResult>> GetSearchResults<TLookupType>(IRemoteSearchProvider<TLookupType> provider, TLookupType searchInfo, + private async Task<IEnumerable<RemoteSearchResult>> GetSearchResults<TLookupType>( + IRemoteSearchProvider<TLookupType> provider, + TLookupType searchInfo, CancellationToken cancellationToken) where TLookupType : ItemLookupInfo { @@ -844,7 +891,8 @@ namespace MediaBrowser.Providers.Manager return list; } - public Task<HttpResponseInfo> GetSearchImage(string providerName, string url, CancellationToken cancellationToken) + /// <inheritdoc/> + public Task<HttpResponseMessage> GetSearchImage(string providerName, string url, CancellationToken cancellationToken) { var provider = _metadataProviders.OfType<IRemoteSearchProvider>().FirstOrDefault(i => string.Equals(i.Name, providerName, StringComparison.OrdinalIgnoreCase)); @@ -856,7 +904,7 @@ namespace MediaBrowser.Providers.Manager return provider.GetImageResponse(url, cancellationToken); } - public IEnumerable<IExternalId> GetExternalIds(IHasProviderIds item) + private IEnumerable<IExternalId> GetExternalIds(IHasProviderIds item) { return _externalIds.Where(i => { @@ -872,6 +920,7 @@ namespace MediaBrowser.Providers.Manager }); } + /// <inheritdoc/> public IEnumerable<ExternalUrl> GetExternalUrls(BaseItem item) { return GetExternalIds(item) @@ -891,29 +940,29 @@ namespace MediaBrowser.Providers.Manager return new ExternalUrl { - Name = i.Name, + Name = i.ProviderName, Url = string.Format( CultureInfo.InvariantCulture, i.UrlFormatString, value) }; - }).Where(i => i != null).Concat(item.GetRelatedUrls()); } + /// <inheritdoc/> public IEnumerable<ExternalIdInfo> GetExternalIdInfos(IHasProviderIds item) { return GetExternalIds(item) .Select(i => new ExternalIdInfo { - Name = i.Name, + Name = i.ProviderName, Key = i.Key, + Type = i.Type, UrlFormatString = i.UrlFormatString }); } - private ConcurrentDictionary<Guid, double> _activeRefreshes = new ConcurrentDictionary<Guid, double>(); - + /// <inheritdoc/> public Dictionary<Guid, Guid> GetRefreshQueue() { lock (_refreshQueueLock) @@ -929,22 +978,25 @@ namespace MediaBrowser.Providers.Manager } } + /// <inheritdoc/> public void OnRefreshStart(BaseItem item) { - _logger.LogInformation("OnRefreshStart {0}", item.Id.ToString("N", CultureInfo.InvariantCulture)); + _logger.LogDebug("OnRefreshStart {0}", item.Id.ToString("N", CultureInfo.InvariantCulture)); _activeRefreshes[item.Id] = 0; RefreshStarted?.Invoke(this, new GenericEventArgs<BaseItem>(item)); } + /// <inheritdoc/> public void OnRefreshComplete(BaseItem item) { - _logger.LogInformation("OnRefreshComplete {0}", item.Id.ToString("N", CultureInfo.InvariantCulture)); + _logger.LogDebug("OnRefreshComplete {0}", item.Id.ToString("N", CultureInfo.InvariantCulture)); _activeRefreshes.Remove(item.Id, out _); RefreshCompleted?.Invoke(this, new GenericEventArgs<BaseItem>(item)); } + /// <inheritdoc/> public double? GetRefreshProgress(Guid id) { if (_activeRefreshes.TryGetValue(id, out double value)) @@ -955,6 +1007,7 @@ namespace MediaBrowser.Providers.Manager return null; } + /// <inheritdoc/> public void OnRefreshProgress(BaseItem item, double progress) { var id = item.Id; @@ -974,12 +1027,7 @@ namespace MediaBrowser.Providers.Manager RefreshProgress?.Invoke(this, new GenericEventArgs<Tuple<BaseItem, double>>(new Tuple<BaseItem, double>(item, progress))); } - private readonly SimplePriorityQueue<Tuple<Guid, MetadataRefreshOptions>> _refreshQueue = - new SimplePriorityQueue<Tuple<Guid, MetadataRefreshOptions>>(); - - private readonly object _refreshQueueLock = new object(); - private bool _isProcessingRefreshQueue; - + /// <inheritdoc/> public void QueueRefresh(Guid id, MetadataRefreshOptions options, RefreshPriority priority) { if (_disposed) @@ -1023,7 +1071,7 @@ namespace MediaBrowser.Providers.Manager if (item != null) { // Try to throttle this a little bit. - await Task.Delay(100).ConfigureAwait(false); + await Task.Delay(100, cancellationToken).ConfigureAwait(false); var task = item is MusicArtist artist ? RefreshArtist(artist, refreshItem.Item2, cancellationToken) @@ -1053,17 +1101,14 @@ namespace MediaBrowser.Providers.Manager await item.RefreshMetadata(options, cancellationToken).ConfigureAwait(false); // Collection folders don't validate their children so we'll have to simulate that here - - if (item is CollectionFolder collectionFolder) + switch (item) { - await RefreshCollectionFolderChildren(options, collectionFolder, cancellationToken).ConfigureAwait(false); - } - else - { - if (item is Folder folder) - { + case CollectionFolder collectionFolder: + await RefreshCollectionFolderChildren(options, collectionFolder, cancellationToken).ConfigureAwait(false); + break; + case Folder folder: await folder.ValidateChildren(new SimpleProgress<double>(), cancellationToken, options).ConfigureAwait(false); - } + break; } } @@ -1073,7 +1118,7 @@ namespace MediaBrowser.Providers.Manager { await child.RefreshMetadata(options, cancellationToken).ConfigureAwait(false); - await child.ValidateChildren(new SimpleProgress<double>(), cancellationToken, options, true).ConfigureAwait(false); + await child.ValidateChildren(new SimpleProgress<double>(), cancellationToken, options).ConfigureAwait(false); } } @@ -1109,20 +1154,41 @@ namespace MediaBrowser.Providers.Manager } } + /// <inheritdoc/> public Task RefreshFullItem(BaseItem item, MetadataRefreshOptions options, CancellationToken cancellationToken) { return RefreshItem(item, options, cancellationToken); } - private bool _disposed; + /// <inheritdoc/> public void Dispose() { - _disposed = true; + Dispose(true); + GC.SuppressFinalize(this); + } + + /// <summary> + /// Releases unmanaged and optionally managed resources. + /// </summary> + /// <param name="disposing"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param> + protected virtual void Dispose(bool disposing) + { + if (_disposed) + { + return; + } if (!_disposeCancellationTokenSource.IsCancellationRequested) { _disposeCancellationTokenSource.Cancel(); } + + if (disposing) + { + _disposeCancellationTokenSource.Dispose(); + } + + _disposed = true; } } } diff --git a/MediaBrowser.Providers/Manager/ProviderUtils.cs b/MediaBrowser.Providers/Manager/ProviderUtils.cs index 8d1588c4eb..70a5a6ac13 100644 --- a/MediaBrowser.Providers/Manager/ProviderUtils.cs +++ b/MediaBrowser.Providers/Manager/ProviderUtils.cs @@ -1,3 +1,5 @@ +#pragma warning disable CS1591 + using System; using System.Collections.Generic; using System.Linq; @@ -14,7 +16,7 @@ namespace MediaBrowser.Providers.Manager public static void MergeBaseItemData<T>( MetadataResult<T> sourceResult, MetadataResult<T> targetResult, - MetadataFields[] lockedFields, + MetadataField[] lockedFields, bool replaceData, bool mergeMetadataSettings) where T : BaseItem @@ -24,14 +26,15 @@ namespace MediaBrowser.Providers.Manager if (source == null) { - throw new ArgumentNullException(nameof(source)); + throw new ArgumentException("Item cannot be null.", nameof(sourceResult)); } + if (target == null) { - throw new ArgumentNullException(nameof(target)); + throw new ArgumentException("Item cannot be null.", nameof(targetResult)); } - if (!lockedFields.Contains(MetadataFields.Name)) + if (!lockedFields.Contains(MetadataField.Name)) { if (replaceData || string.IsNullOrEmpty(target.Name)) { @@ -62,7 +65,7 @@ namespace MediaBrowser.Providers.Manager target.EndDate = source.EndDate; } - if (!lockedFields.Contains(MetadataFields.Genres)) + if (!lockedFields.Contains(MetadataField.Genres)) { if (replaceData || target.Genres.Length == 0) { @@ -75,7 +78,7 @@ namespace MediaBrowser.Providers.Manager target.IndexNumber = source.IndexNumber; } - if (!lockedFields.Contains(MetadataFields.OfficialRating)) + if (!lockedFields.Contains(MetadataField.OfficialRating)) { if (replaceData || string.IsNullOrEmpty(target.OfficialRating)) { @@ -93,7 +96,7 @@ namespace MediaBrowser.Providers.Manager target.Tagline = source.Tagline; } - if (!lockedFields.Contains(MetadataFields.Overview)) + if (!lockedFields.Contains(MetadataField.Overview)) { if (replaceData || string.IsNullOrEmpty(target.Overview)) { @@ -106,12 +109,11 @@ namespace MediaBrowser.Providers.Manager target.ParentIndexNumber = source.ParentIndexNumber; } - if (!lockedFields.Contains(MetadataFields.Cast)) + if (!lockedFields.Contains(MetadataField.Cast)) { if (replaceData || targetResult.People == null || targetResult.People.Count == 0) { targetResult.People = sourceResult.People; - } else if (targetResult.People != null && sourceResult.People != null) { @@ -129,7 +131,7 @@ namespace MediaBrowser.Providers.Manager target.ProductionYear = source.ProductionYear; } - if (!lockedFields.Contains(MetadataFields.Runtime)) + if (!lockedFields.Contains(MetadataField.Runtime)) { if (replaceData || !target.RunTimeTicks.HasValue) { @@ -140,7 +142,7 @@ namespace MediaBrowser.Providers.Manager } } - if (!lockedFields.Contains(MetadataFields.Studios)) + if (!lockedFields.Contains(MetadataField.Studios)) { if (replaceData || target.Studios.Length == 0) { @@ -148,7 +150,7 @@ namespace MediaBrowser.Providers.Manager } } - if (!lockedFields.Contains(MetadataFields.Tags)) + if (!lockedFields.Contains(MetadataField.Tags)) { if (replaceData || target.Tags.Length == 0) { @@ -156,7 +158,7 @@ namespace MediaBrowser.Providers.Manager } } - if (!lockedFields.Contains(MetadataFields.ProductionLocations)) + if (!lockedFields.Contains(MetadataField.ProductionLocations)) { if (replaceData || target.ProductionLocations.Length == 0) { diff --git a/MediaBrowser.Providers/Manager/RefreshResult.cs b/MediaBrowser.Providers/Manager/RefreshResult.cs new file mode 100644 index 0000000000..72fc61e424 --- /dev/null +++ b/MediaBrowser.Providers/Manager/RefreshResult.cs @@ -0,0 +1,15 @@ +#pragma warning disable CS1591 + +using MediaBrowser.Controller.Library; + +namespace MediaBrowser.Providers.Manager +{ + public class RefreshResult + { + public ItemUpdateType UpdateType { get; set; } + + public string ErrorMessage { get; set; } + + public int Failures { get; set; } + } +} diff --git a/MediaBrowser.Providers/MediaBrowser.Providers.csproj b/MediaBrowser.Providers/MediaBrowser.Providers.csproj index 1b3df63b63..39f93c479b 100644 --- a/MediaBrowser.Providers/MediaBrowser.Providers.csproj +++ b/MediaBrowser.Providers/MediaBrowser.Providers.csproj @@ -16,17 +16,19 @@ </ItemGroup> <ItemGroup> - <PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="3.1.3" /> - <PackageReference Include="Microsoft.Extensions.Caching.Abstractions" Version="3.1.3" /> + <PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="3.1.7" /> + <PackageReference Include="Microsoft.Extensions.Caching.Abstractions" Version="3.1.7" /> + <PackageReference Include="Microsoft.Extensions.Http" Version="3.1.7" /> <PackageReference Include="OptimizedPriorityQueue" Version="4.2.0" /> - <PackageReference Include="PlaylistsNET" Version="1.0.4" /> - <PackageReference Include="TvDbSharper" Version="3.0.1" /> + <PackageReference Include="PlaylistsNET" Version="1.1.2" /> + <PackageReference Include="TvDbSharper" Version="3.2.1" /> </ItemGroup> <PropertyGroup> <TargetFramework>netstandard2.1</TargetFramework> <GenerateAssemblyInfo>false</GenerateAssemblyInfo> <GenerateDocumentationFile>true</GenerateDocumentationFile> + <TreatWarningsAsErrors Condition=" '$(Configuration)' == 'Release'">true</TreatWarningsAsErrors> </PropertyGroup> <!-- Code Analyzers--> @@ -44,11 +46,9 @@ <ItemGroup> <None Remove="Plugins\AudioDb\Configuration\config.html" /> <EmbeddedResource Include="Plugins\AudioDb\Configuration\config.html" /> - </ItemGroup> - - <ItemGroup> + <None Remove="Plugins\Omdb\Configuration\config.html" /> + <EmbeddedResource Include="Plugins\Omdb\Configuration\config.html" /> <None Remove="Plugins\MusicBrainz\Configuration\config.html" /> <EmbeddedResource Include="Plugins\MusicBrainz\Configuration\config.html" /> </ItemGroup> - </Project> diff --git a/MediaBrowser.Providers/MediaInfo/AudioImageProvider.cs b/MediaBrowser.Providers/MediaInfo/AudioImageProvider.cs index 7023ef706b..64ad1bddfe 100644 --- a/MediaBrowser.Providers/MediaInfo/AudioImageProvider.cs +++ b/MediaBrowser.Providers/MediaInfo/AudioImageProvider.cs @@ -1,3 +1,5 @@ +#pragma warning disable CS1591 + using System; using System.Collections.Generic; using System.Globalization; @@ -17,7 +19,7 @@ using MediaBrowser.Model.IO; namespace MediaBrowser.Providers.MediaInfo { /// <summary> - /// Uses ffmpeg to create video images + /// Uses ffmpeg to create video images. /// </summary> public class AudioImageProvider : IDynamicImageProvider { @@ -32,6 +34,10 @@ namespace MediaBrowser.Providers.MediaInfo _fileSystem = fileSystem; } + public string AudioImagesPath => Path.Combine(_config.ApplicationPaths.CachePath, "extracted-audio-images"); + + public string Name => "Image Extractor"; + public IEnumerable<ImageType> GetSupportedImages(BaseItem item) { return new List<ImageType> { ImageType.Primary }; @@ -79,7 +85,6 @@ namespace MediaBrowser.Providers.MediaInfo } catch { - } } @@ -92,15 +97,15 @@ namespace MediaBrowser.Providers.MediaInfo private string GetAudioImagePath(Audio item) { - string filename = null; + string filename; if (item.GetType() == typeof(Audio)) { - var albumArtist = item.AlbumArtists.FirstOrDefault(); - - if (!string.IsNullOrWhiteSpace(item.Album) && !string.IsNullOrWhiteSpace(albumArtist)) + if (item.AlbumArtists.Count > 0 + && !string.IsNullOrWhiteSpace(item.Album) + && !string.IsNullOrWhiteSpace(item.AlbumArtists[0])) { - filename = (item.Album + "-" + albumArtist).GetMD5().ToString("N", CultureInfo.InvariantCulture); + filename = (item.Album + "-" + item.AlbumArtists[0]).GetMD5().ToString("N", CultureInfo.InvariantCulture); } else { @@ -115,21 +120,18 @@ namespace MediaBrowser.Providers.MediaInfo filename = item.Id.ToString("N", CultureInfo.InvariantCulture) + ".jpg"; } - var prefix = filename.Substring(0, 1); + var prefix = filename.AsSpan().Slice(0, 1); - return Path.Combine(AudioImagesPath, prefix, filename); + return Path.Join(AudioImagesPath, prefix, filename); } - public string AudioImagesPath => Path.Combine(_config.ApplicationPaths.CachePath, "extracted-audio-images"); - - public string Name => "Image Extractor"; - public bool Supports(BaseItem item) { if (item.IsShortcut) { return false; } + if (!item.IsFileProtocol) { return false; diff --git a/MediaBrowser.Providers/MediaInfo/FFProbeAudioInfo.cs b/MediaBrowser.Providers/MediaInfo/FFProbeAudioInfo.cs index 207d755249..9454636669 100644 --- a/MediaBrowser.Providers/MediaInfo/FFProbeAudioInfo.cs +++ b/MediaBrowser.Providers/MediaInfo/FFProbeAudioInfo.cs @@ -1,10 +1,10 @@ +#pragma warning disable CS1591 + using System; using System.Collections.Generic; -using System.Globalization; using System.Linq; using System.Threading; using System.Threading.Tasks; -using MediaBrowser.Common.Configuration; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities.Audio; using MediaBrowser.Controller.Library; @@ -15,32 +15,31 @@ using MediaBrowser.Model.Dlna; using MediaBrowser.Model.Dto; using MediaBrowser.Model.Entities; using MediaBrowser.Model.MediaInfo; -using MediaBrowser.Model.Serialization; namespace MediaBrowser.Providers.MediaInfo { - class FFProbeAudioInfo + public class FFProbeAudioInfo { private readonly IMediaEncoder _mediaEncoder; private readonly IItemRepository _itemRepo; - private readonly IApplicationPaths _appPaths; - private readonly IJsonSerializer _json; private readonly ILibraryManager _libraryManager; private readonly IMediaSourceManager _mediaSourceManager; - private readonly CultureInfo _usCulture = new CultureInfo("en-US"); - - public FFProbeAudioInfo(IMediaSourceManager mediaSourceManager, IMediaEncoder mediaEncoder, IItemRepository itemRepo, IApplicationPaths appPaths, IJsonSerializer json, ILibraryManager libraryManager) + public FFProbeAudioInfo( + IMediaSourceManager mediaSourceManager, + IMediaEncoder mediaEncoder, + IItemRepository itemRepo, + ILibraryManager libraryManager) { _mediaEncoder = mediaEncoder; _itemRepo = itemRepo; - _appPaths = appPaths; - _json = json; _libraryManager = libraryManager; _mediaSourceManager = mediaSourceManager; } - public async Task<ItemUpdateType> Probe<T>(T item, MetadataRefreshOptions options, + public async Task<ItemUpdateType> Probe<T>( + T item, + MetadataRefreshOptions options, CancellationToken cancellationToken) where T : Audio { @@ -55,20 +54,21 @@ namespace MediaBrowser.Providers.MediaInfo protocol = _mediaSourceManager.GetPathProtocol(path); } - var result = await _mediaEncoder.GetMediaInfo(new MediaInfoRequest - { - MediaType = DlnaProfileType.Audio, - MediaSource = new MediaSourceInfo + var result = await _mediaEncoder.GetMediaInfo( + new MediaInfoRequest { - Path = path, - Protocol = protocol - } - - }, cancellationToken).ConfigureAwait(false); + MediaType = DlnaProfileType.Audio, + MediaSource = new MediaSourceInfo + { + Path = path, + Protocol = protocol + } + }, + cancellationToken).ConfigureAwait(false); cancellationToken.ThrowIfCancellationRequested(); - Fetch(item, cancellationToken, result); + Fetch(item, result, cancellationToken); } return ItemUpdateType.MetadataImport; @@ -78,10 +78,9 @@ namespace MediaBrowser.Providers.MediaInfo /// Fetches the specified audio. /// </summary> /// <param name="audio">The audio.</param> - /// <param name="cancellationToken">The cancellation token.</param> /// <param name="mediaInfo">The media information.</param> - /// <returns>Task.</returns> - protected void Fetch(Audio audio, CancellationToken cancellationToken, Model.MediaInfo.MediaInfo mediaInfo) + /// <param name="cancellationToken">The cancellation token.</param> + protected void Fetch(Audio audio, Model.MediaInfo.MediaInfo mediaInfo, CancellationToken cancellationToken) { var mediaStreams = mediaInfo.MediaStreams; @@ -91,8 +90,8 @@ namespace MediaBrowser.Providers.MediaInfo audio.RunTimeTicks = mediaInfo.RunTimeTicks; audio.Size = mediaInfo.Size; - //var extension = (Path.GetExtension(audio.Path) ?? string.Empty).TrimStart('.'); - //audio.Container = extension; + // var extension = (Path.GetExtension(audio.Path) ?? string.Empty).TrimStart('.'); + // audio.Container = extension; FetchDataFromTags(audio, mediaInfo); @@ -100,7 +99,7 @@ namespace MediaBrowser.Providers.MediaInfo } /// <summary> - /// Fetches data from the tags dictionary + /// Fetches data from the tags dictionary. /// </summary> /// <param name="audio">The audio.</param> /// <param name="data">The data.</param> @@ -112,7 +111,7 @@ namespace MediaBrowser.Providers.MediaInfo audio.Name = data.Name; } - if (audio.SupportsPeople && !audio.LockedFields.Contains(MetadataFields.Cast)) + if (audio.SupportsPeople && !audio.LockedFields.Contains(MetadataField.Cast)) { var people = new List<PersonInfo>(); @@ -143,7 +142,7 @@ namespace MediaBrowser.Providers.MediaInfo audio.ProductionYear = audio.PremiereDate.Value.ToLocalTime().Year; } - if (!audio.LockedFields.Contains(MetadataFields.Genres)) + if (!audio.LockedFields.Contains(MetadataField.Genres)) { audio.Genres = Array.Empty<string>(); @@ -153,16 +152,16 @@ namespace MediaBrowser.Providers.MediaInfo } } - if (!audio.LockedFields.Contains(MetadataFields.Studios)) + if (!audio.LockedFields.Contains(MetadataField.Studios)) { audio.SetStudios(data.Studios); } - audio.SetProviderId(MetadataProviders.MusicBrainzAlbumArtist, data.GetProviderId(MetadataProviders.MusicBrainzAlbumArtist)); - audio.SetProviderId(MetadataProviders.MusicBrainzArtist, data.GetProviderId(MetadataProviders.MusicBrainzArtist)); - audio.SetProviderId(MetadataProviders.MusicBrainzAlbum, data.GetProviderId(MetadataProviders.MusicBrainzAlbum)); - audio.SetProviderId(MetadataProviders.MusicBrainzReleaseGroup, data.GetProviderId(MetadataProviders.MusicBrainzReleaseGroup)); - audio.SetProviderId(MetadataProviders.MusicBrainzTrack, data.GetProviderId(MetadataProviders.MusicBrainzTrack)); + audio.SetProviderId(MetadataProvider.MusicBrainzAlbumArtist, data.GetProviderId(MetadataProvider.MusicBrainzAlbumArtist)); + audio.SetProviderId(MetadataProvider.MusicBrainzArtist, data.GetProviderId(MetadataProvider.MusicBrainzArtist)); + audio.SetProviderId(MetadataProvider.MusicBrainzAlbum, data.GetProviderId(MetadataProvider.MusicBrainzAlbum)); + audio.SetProviderId(MetadataProvider.MusicBrainzReleaseGroup, data.GetProviderId(MetadataProvider.MusicBrainzReleaseGroup)); + audio.SetProviderId(MetadataProvider.MusicBrainzTrack, data.GetProviderId(MetadataProvider.MusicBrainzTrack)); } } } diff --git a/MediaBrowser.Providers/MediaInfo/FFProbeProvider.cs b/MediaBrowser.Providers/MediaInfo/FFProbeProvider.cs index 6982568eb2..c61187fdf6 100644 --- a/MediaBrowser.Providers/MediaInfo/FFProbeProvider.cs +++ b/MediaBrowser.Providers/MediaInfo/FFProbeProvider.cs @@ -1,10 +1,10 @@ +#pragma warning disable CS1591 + using System; using System.IO; using System.Linq; using System.Threading; using System.Threading.Tasks; -using MediaBrowser.Common.Configuration; -using MediaBrowser.Controller.Channels; using MediaBrowser.Controller.Chapters; using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Entities; @@ -18,9 +18,7 @@ using MediaBrowser.Controller.Providers; using MediaBrowser.Controller.Subtitles; using MediaBrowser.Model.Entities; using MediaBrowser.Model.Globalization; -using MediaBrowser.Model.IO; using MediaBrowser.Model.MediaInfo; -using MediaBrowser.Model.Serialization; using Microsoft.Extensions.Logging; namespace MediaBrowser.Providers.MediaInfo @@ -37,24 +35,54 @@ namespace MediaBrowser.Providers.MediaInfo IPreRefreshProvider, IHasItemChangeMonitor { - private readonly ILogger _logger; - private readonly IIsoManager _isoManager; + private readonly ILogger<FFProbeProvider> _logger; private readonly IMediaEncoder _mediaEncoder; private readonly IItemRepository _itemRepo; private readonly IBlurayExaminer _blurayExaminer; private readonly ILocalizationManager _localization; - private readonly IApplicationPaths _appPaths; - private readonly IJsonSerializer _json; private readonly IEncodingManager _encodingManager; private readonly IServerConfigurationManager _config; private readonly ISubtitleManager _subtitleManager; private readonly IChapterManager _chapterManager; private readonly ILibraryManager _libraryManager; - private readonly IChannelManager _channelManager; private readonly IMediaSourceManager _mediaSourceManager; + private readonly SubtitleResolver _subtitleResolver; + + private readonly Task<ItemUpdateType> _cachedTask = Task.FromResult(ItemUpdateType.None); + + public FFProbeProvider( + ILogger<FFProbeProvider> logger, + IMediaSourceManager mediaSourceManager, + IMediaEncoder mediaEncoder, + IItemRepository itemRepo, + IBlurayExaminer blurayExaminer, + ILocalizationManager localization, + IEncodingManager encodingManager, + IServerConfigurationManager config, + ISubtitleManager subtitleManager, + IChapterManager chapterManager, + ILibraryManager libraryManager) + { + _logger = logger; + _mediaEncoder = mediaEncoder; + _itemRepo = itemRepo; + _blurayExaminer = blurayExaminer; + _localization = localization; + _encodingManager = encodingManager; + _config = config; + _subtitleManager = subtitleManager; + _chapterManager = chapterManager; + _libraryManager = libraryManager; + _mediaSourceManager = mediaSourceManager; + + _subtitleResolver = new SubtitleResolver(BaseItem.LocalizationManager); + } public string Name => "ffprobe"; + // Run last + public int Order => 100; + public bool HasChanged(BaseItem item, IDirectoryService directoryService) { var video = item as Video; @@ -119,45 +147,6 @@ namespace MediaBrowser.Providers.MediaInfo return FetchAudioInfo(item, options, cancellationToken); } - private SubtitleResolver _subtitleResolver; - - public FFProbeProvider( - ILogger<FFProbeProvider> logger, - IMediaSourceManager mediaSourceManager, - IChannelManager channelManager, - IIsoManager isoManager, - IMediaEncoder mediaEncoder, - IItemRepository itemRepo, - IBlurayExaminer blurayExaminer, - ILocalizationManager localization, - IApplicationPaths appPaths, - IJsonSerializer json, - IEncodingManager encodingManager, - IServerConfigurationManager config, - ISubtitleManager subtitleManager, - IChapterManager chapterManager, - ILibraryManager libraryManager) - { - _logger = logger; - _isoManager = isoManager; - _mediaEncoder = mediaEncoder; - _itemRepo = itemRepo; - _blurayExaminer = blurayExaminer; - _localization = localization; - _appPaths = appPaths; - _json = json; - _encodingManager = encodingManager; - _config = config; - _subtitleManager = subtitleManager; - _chapterManager = chapterManager; - _libraryManager = libraryManager; - _channelManager = channelManager; - _mediaSourceManager = mediaSourceManager; - - _subtitleResolver = new SubtitleResolver(BaseItem.LocalizationManager); - } - - private readonly Task<ItemUpdateType> _cachedTask = Task.FromResult(ItemUpdateType.None); public Task<ItemUpdateType> FetchVideoInfo<T>(T item, MetadataRefreshOptions options, CancellationToken cancellationToken) where T : Video { @@ -209,9 +198,9 @@ namespace MediaBrowser.Providers.MediaInfo private string NormalizeStrmLine(string line) { - return line.Replace("\t", string.Empty) - .Replace("\r", string.Empty) - .Replace("\n", string.Empty) + return line.Replace("\t", string.Empty, StringComparison.Ordinal) + .Replace("\r", string.Empty, StringComparison.Ordinal) + .Replace("\n", string.Empty, StringComparison.Ordinal) .Trim(); } @@ -240,11 +229,9 @@ namespace MediaBrowser.Providers.MediaInfo FetchShortcutInfo(item); } - var prober = new FFProbeAudioInfo(_mediaSourceManager, _mediaEncoder, _itemRepo, _appPaths, _json, _libraryManager); + var prober = new FFProbeAudioInfo(_mediaSourceManager, _mediaEncoder, _itemRepo, _libraryManager); return prober.Probe(item, options, cancellationToken); } - // Run last - public int Order => 100; } } diff --git a/MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs b/MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs index 89496622fc..776dee7802 100644 --- a/MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs +++ b/MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs @@ -24,7 +24,6 @@ using MediaBrowser.Model.Dlna; using MediaBrowser.Model.Dto; using MediaBrowser.Model.Entities; using MediaBrowser.Model.Globalization; -using MediaBrowser.Model.IO; using MediaBrowser.Model.MediaInfo; using MediaBrowser.Model.Providers; using Microsoft.Extensions.Logging; @@ -176,7 +175,7 @@ namespace MediaBrowser.Providers.MediaInfo mediaAttachments = mediaInfo.MediaAttachments; video.TotalBitrate = mediaInfo.Bitrate; - //video.FormatName = (mediaInfo.Container ?? string.Empty) + // video.FormatName = (mediaInfo.Container ?? string.Empty) // .Replace("matroska", "mkv", StringComparison.OrdinalIgnoreCase); // For dvd's this may not always be accurate, so don't set the runtime if the item already has one @@ -283,7 +282,7 @@ namespace MediaBrowser.Providers.MediaInfo { var video = (Video)item; - //video.PlayableStreamFileNames = blurayInfo.Files.ToList(); + // video.PlayableStreamFileNames = blurayInfo.Files.ToList(); // Use BD Info if it has multiple m2ts. Otherwise, treat it like a video file and rely more on ffprobe output if (blurayInfo.Files.Length > 1) @@ -342,7 +341,7 @@ namespace MediaBrowser.Providers.MediaInfo } /// <summary> - /// Gets information about the longest playlist on a bdrom + /// Gets information about the longest playlist on a bdrom. /// </summary> /// <param name="path">The path.</param> /// <returns>VideoStream.</returns> @@ -368,7 +367,7 @@ namespace MediaBrowser.Providers.MediaInfo { var isFullRefresh = refreshOptions.MetadataRefreshMode == MetadataRefreshMode.FullRefresh; - if (!video.IsLocked && !video.LockedFields.Contains(MetadataFields.OfficialRating)) + if (!video.IsLocked && !video.LockedFields.Contains(MetadataField.OfficialRating)) { if (!string.IsNullOrWhiteSpace(data.OfficialRating) || isFullRefresh) { @@ -376,7 +375,7 @@ namespace MediaBrowser.Providers.MediaInfo } } - if (!video.IsLocked && !video.LockedFields.Contains(MetadataFields.Genres)) + if (!video.IsLocked && !video.LockedFields.Contains(MetadataField.Genres)) { if (video.Genres.Length == 0 || isFullRefresh) { @@ -389,7 +388,7 @@ namespace MediaBrowser.Providers.MediaInfo } } - if (!video.IsLocked && !video.LockedFields.Contains(MetadataFields.Studios)) + if (!video.IsLocked && !video.LockedFields.Contains(MetadataField.Studios)) { if (video.Studios.Length == 0 || isFullRefresh) { @@ -404,6 +403,7 @@ namespace MediaBrowser.Providers.MediaInfo video.ProductionYear = data.ProductionYear; } } + if (data.PremiereDate.HasValue) { if (!video.PremiereDate.HasValue || isFullRefresh) @@ -411,6 +411,7 @@ namespace MediaBrowser.Providers.MediaInfo video.PremiereDate = data.PremiereDate; } } + if (data.IndexNumber.HasValue) { if (!video.IndexNumber.HasValue || isFullRefresh) @@ -418,6 +419,7 @@ namespace MediaBrowser.Providers.MediaInfo video.IndexNumber = data.IndexNumber; } } + if (data.ParentIndexNumber.HasValue) { if (!video.ParentIndexNumber.HasValue || isFullRefresh) @@ -426,7 +428,7 @@ namespace MediaBrowser.Providers.MediaInfo } } - if (!video.IsLocked && !video.LockedFields.Contains(MetadataFields.Name)) + if (!video.IsLocked && !video.LockedFields.Contains(MetadataField.Name)) { if (!string.IsNullOrWhiteSpace(data.Name) && libraryOptions.EnableEmbeddedTitles) { @@ -444,7 +446,7 @@ namespace MediaBrowser.Providers.MediaInfo video.ProductionYear = video.PremiereDate.Value.ToLocalTime().Year; } - if (!video.IsLocked && !video.LockedFields.Contains(MetadataFields.Overview)) + if (!video.IsLocked && !video.LockedFields.Contains(MetadataField.Overview)) { if (string.IsNullOrWhiteSpace(video.Overview) || isFullRefresh) { @@ -457,7 +459,7 @@ namespace MediaBrowser.Providers.MediaInfo { var isFullRefresh = options.MetadataRefreshMode == MetadataRefreshMode.FullRefresh; - if (!video.IsLocked && !video.LockedFields.Contains(MetadataFields.Cast)) + if (!video.IsLocked && !video.LockedFields.Contains(MetadataField.Cast)) { if (isFullRefresh || _libraryManager.GetPeople(video).Count == 0) { @@ -537,17 +539,18 @@ namespace MediaBrowser.Providers.MediaInfo if (enableSubtitleDownloading && enabled) { - var downloadedLanguages = await new SubtitleDownloader(_logger, - _subtitleManager) - .DownloadSubtitles(video, - currentStreams.Concat(externalSubtitleStreams).ToList(), - skipIfEmbeddedSubtitlesPresent, - skipIfAudioTrackMatches, - requirePerfectMatch, - subtitleDownloadLanguages, - libraryOptions.DisabledSubtitleFetchers, - libraryOptions.SubtitleFetcherOrder, - cancellationToken).ConfigureAwait(false); + var downloadedLanguages = await new SubtitleDownloader( + _logger, + _subtitleManager).DownloadSubtitles( + video, + currentStreams.Concat(externalSubtitleStreams).ToList(), + skipIfEmbeddedSubtitlesPresent, + skipIfAudioTrackMatches, + requirePerfectMatch, + subtitleDownloadLanguages, + libraryOptions.DisabledSubtitleFetchers, + libraryOptions.SubtitleFetcherOrder, + cancellationToken).ConfigureAwait(false); // Rescan if (downloadedLanguages.Count > 0) @@ -565,7 +568,7 @@ namespace MediaBrowser.Providers.MediaInfo /// Creates dummy chapters. /// </summary> /// <param name="video">The video.</param> - /// <return>An array of dummy chapters.</returns> + /// <returns>An array of dummy chapters.</returns> private ChapterInfo[] CreateDummyChapters(Video video) { var runtime = video.RunTimeTicks ?? 0; diff --git a/MediaBrowser.Providers/MediaInfo/SubtitleDownloader.cs b/MediaBrowser.Providers/MediaInfo/SubtitleDownloader.cs index 77c0e9b4ef..912aedb0db 100644 --- a/MediaBrowser.Providers/MediaInfo/SubtitleDownloader.cs +++ b/MediaBrowser.Providers/MediaInfo/SubtitleDownloader.cs @@ -1,3 +1,5 @@ +#pragma warning disable CS1591 + using System; using System.Collections.Generic; using System.Linq; @@ -25,7 +27,8 @@ namespace MediaBrowser.Providers.MediaInfo _subtitleManager = subtitleManager; } - public async Task<List<string>> DownloadSubtitles(Video video, + public async Task<List<string>> DownloadSubtitles( + Video video, List<MediaStream> mediaStreams, bool skipIfEmbeddedSubtitlesPresent, bool skipIfAudioTrackMatches, @@ -39,8 +42,16 @@ namespace MediaBrowser.Providers.MediaInfo foreach (var lang in languages) { - var downloaded = await DownloadSubtitles(video, mediaStreams, skipIfEmbeddedSubtitlesPresent, - skipIfAudioTrackMatches, requirePerfectMatch, lang, disabledSubtitleFetchers, subtitleFetcherOrder, cancellationToken).ConfigureAwait(false); + var downloaded = await DownloadSubtitles( + video, + mediaStreams, + skipIfEmbeddedSubtitlesPresent, + skipIfAudioTrackMatches, + requirePerfectMatch, + lang, + disabledSubtitleFetchers, + subtitleFetcherOrder, + cancellationToken).ConfigureAwait(false); if (downloaded) { @@ -51,7 +62,8 @@ namespace MediaBrowser.Providers.MediaInfo return downloadedLanguages; } - public Task<bool> DownloadSubtitles(Video video, + public Task<bool> DownloadSubtitles( + Video video, List<MediaStream> mediaStreams, bool skipIfEmbeddedSubtitlesPresent, bool skipIfAudioTrackMatches, @@ -87,11 +99,21 @@ namespace MediaBrowser.Providers.MediaInfo return Task.FromResult(false); } - return DownloadSubtitles(video, mediaStreams, skipIfEmbeddedSubtitlesPresent, skipIfAudioTrackMatches, - requirePerfectMatch, lang, disabledSubtitleFetchers, subtitleFetcherOrder, mediaType, cancellationToken); + return DownloadSubtitles( + video, + mediaStreams, + skipIfEmbeddedSubtitlesPresent, + skipIfAudioTrackMatches, + requirePerfectMatch, + lang, + disabledSubtitleFetchers, + subtitleFetcherOrder, + mediaType, + cancellationToken); } - private async Task<bool> DownloadSubtitles(Video video, + private async Task<bool> DownloadSubtitles( + Video video, List<MediaStream> mediaStreams, bool skipIfEmbeddedSubtitlesPresent, bool skipIfAudioTrackMatches, diff --git a/MediaBrowser.Providers/MediaInfo/SubtitleResolver.cs b/MediaBrowser.Providers/MediaInfo/SubtitleResolver.cs index 2bbe8a968d..e9f999c6d0 100644 --- a/MediaBrowser.Providers/MediaInfo/SubtitleResolver.cs +++ b/MediaBrowser.Providers/MediaInfo/SubtitleResolver.cs @@ -1,3 +1,5 @@ +#pragma warning disable CS1591 + using System; using System.Collections.Generic; using System.IO; @@ -6,7 +8,6 @@ using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Providers; using MediaBrowser.Model.Entities; using MediaBrowser.Model.Globalization; -using MediaBrowser.Model.IO; namespace MediaBrowser.Providers.MediaInfo { @@ -60,15 +61,15 @@ namespace MediaBrowser.Providers.MediaInfo } catch (IOException) { - } return streams; } - public List<string> GetExternalSubtitleFiles(Video video, - IDirectoryService directoryService, - bool clearCache) + public List<string> GetExternalSubtitleFiles( + Video video, + IDirectoryService directoryService, + bool clearCache) { var list = new List<string>(); @@ -87,7 +88,9 @@ namespace MediaBrowser.Providers.MediaInfo return list; } - private void AddExternalSubtitleStreams(List<MediaStream> streams, string folder, + private void AddExternalSubtitleStreams( + List<MediaStream> streams, + string folder, string videoPath, int startIndex, IDirectoryService directoryService, @@ -98,7 +101,8 @@ namespace MediaBrowser.Providers.MediaInfo AddExternalSubtitleStreams(streams, videoPath, startIndex, files); } - public void AddExternalSubtitleStreams(List<MediaStream> streams, + public void AddExternalSubtitleStreams( + List<MediaStream> streams, string videoPath, int startIndex, string[] files) @@ -185,13 +189,13 @@ namespace MediaBrowser.Providers.MediaInfo private string NormalizeFilenameForSubtitleComparison(string filename) { // Try to account for sloppy file naming - filename = filename.Replace("_", string.Empty); - filename = filename.Replace(" ", string.Empty); + filename = filename.Replace("_", string.Empty, StringComparison.Ordinal); + filename = filename.Replace(" ", string.Empty, StringComparison.Ordinal); // can't normalize this due to languages such as pt-br - //filename = filename.Replace("-", string.Empty); + // filename = filename.Replace("-", string.Empty); - //filename = filename.Replace(".", string.Empty); + // filename = filename.Replace(".", string.Empty); return filename; } diff --git a/MediaBrowser.Providers/MediaInfo/SubtitleScheduledTask.cs b/MediaBrowser.Providers/MediaInfo/SubtitleScheduledTask.cs index 2615f2dbbb..d231bfa2fc 100644 --- a/MediaBrowser.Providers/MediaInfo/SubtitleScheduledTask.cs +++ b/MediaBrowser.Providers/MediaInfo/SubtitleScheduledTask.cs @@ -1,3 +1,5 @@ +#pragma warning disable CS1591 + using System; using System.Collections.Generic; using System.Linq; @@ -10,11 +12,10 @@ using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Subtitles; using MediaBrowser.Model.Entities; +using MediaBrowser.Model.Globalization; using MediaBrowser.Model.Providers; -using MediaBrowser.Model.Serialization; using MediaBrowser.Model.Tasks; using Microsoft.Extensions.Logging; -using MediaBrowser.Model.Globalization; namespace MediaBrowser.Providers.MediaInfo { @@ -23,29 +24,37 @@ namespace MediaBrowser.Providers.MediaInfo private readonly ILibraryManager _libraryManager; private readonly IServerConfigurationManager _config; private readonly ISubtitleManager _subtitleManager; - private readonly IMediaSourceManager _mediaSourceManager; - private readonly ILogger _logger; - private readonly IJsonSerializer _json; + private readonly ILogger<SubtitleScheduledTask> _logger; private readonly ILocalizationManager _localization; public SubtitleScheduledTask( ILibraryManager libraryManager, - IJsonSerializer json, IServerConfigurationManager config, ISubtitleManager subtitleManager, ILogger<SubtitleScheduledTask> logger, - IMediaSourceManager mediaSourceManager, ILocalizationManager localization) { _libraryManager = libraryManager; _config = config; _subtitleManager = subtitleManager; _logger = logger; - _mediaSourceManager = mediaSourceManager; - _json = json; _localization = localization; } + public string Name => _localization.GetLocalizedString("TaskDownloadMissingSubtitles"); + + public string Description => _localization.GetLocalizedString("TaskDownloadMissingSubtitlesDescription"); + + public string Category => _localization.GetLocalizedString("TasksLibraryCategory"); + + public string Key => "DownloadSubtitles"; + + public bool IsHidden => false; + + public bool IsEnabled => true; + + public bool IsLogged => true; + private SubtitleOptions GetOptions() { return _config.GetConfiguration<SubtitleOptions>("subtitles"); @@ -64,23 +73,23 @@ namespace MediaBrowser.Providers.MediaInfo var libraryOptions = _libraryManager.GetLibraryOptions(library); string[] subtitleDownloadLanguages; - bool SkipIfEmbeddedSubtitlesPresent; - bool SkipIfAudioTrackMatches; - bool RequirePerfectMatch; + bool skipIfEmbeddedSubtitlesPresent; + bool skipIfAudioTrackMatches; + bool requirePerfectMatch; if (libraryOptions.SubtitleDownloadLanguages == null) { subtitleDownloadLanguages = options.DownloadLanguages; - SkipIfEmbeddedSubtitlesPresent = options.SkipIfEmbeddedSubtitlesPresent; - SkipIfAudioTrackMatches = options.SkipIfAudioTrackMatches; - RequirePerfectMatch = options.RequirePerfectMatch; + skipIfEmbeddedSubtitlesPresent = options.SkipIfEmbeddedSubtitlesPresent; + skipIfAudioTrackMatches = options.SkipIfAudioTrackMatches; + requirePerfectMatch = options.RequirePerfectMatch; } else { subtitleDownloadLanguages = libraryOptions.SubtitleDownloadLanguages; - SkipIfEmbeddedSubtitlesPresent = libraryOptions.SkipSubtitlesIfEmbeddedSubtitlesPresent; - SkipIfAudioTrackMatches = libraryOptions.SkipSubtitlesIfAudioTrackMatches; - RequirePerfectMatch = libraryOptions.RequirePerfectSubtitleMatch; + skipIfEmbeddedSubtitlesPresent = libraryOptions.SkipSubtitlesIfEmbeddedSubtitlesPresent; + skipIfAudioTrackMatches = libraryOptions.SkipSubtitlesIfAudioTrackMatches; + requirePerfectMatch = libraryOptions.RequirePerfectSubtitleMatch; } foreach (var lang in subtitleDownloadLanguages) @@ -96,12 +105,12 @@ namespace MediaBrowser.Providers.MediaInfo Recursive = true }; - if (SkipIfAudioTrackMatches) + if (skipIfAudioTrackMatches) { query.HasNoAudioTrackWithLanguage = lang; } - if (SkipIfEmbeddedSubtitlesPresent) + if (skipIfEmbeddedSubtitlesPresent) { // Exclude if it already has any subtitles of the same language query.HasNoSubtitleTrackWithLanguage = lang; @@ -158,36 +167,37 @@ namespace MediaBrowser.Providers.MediaInfo var libraryOptions = _libraryManager.GetLibraryOptions(video); string[] subtitleDownloadLanguages; - bool SkipIfEmbeddedSubtitlesPresent; - bool SkipIfAudioTrackMatches; - bool RequirePerfectMatch; + bool skipIfEmbeddedSubtitlesPresent; + bool skipIfAudioTrackMatches; + bool requirePerfectMatch; if (libraryOptions.SubtitleDownloadLanguages == null) { subtitleDownloadLanguages = options.DownloadLanguages; - SkipIfEmbeddedSubtitlesPresent = options.SkipIfEmbeddedSubtitlesPresent; - SkipIfAudioTrackMatches = options.SkipIfAudioTrackMatches; - RequirePerfectMatch = options.RequirePerfectMatch; + skipIfEmbeddedSubtitlesPresent = options.SkipIfEmbeddedSubtitlesPresent; + skipIfAudioTrackMatches = options.SkipIfAudioTrackMatches; + requirePerfectMatch = options.RequirePerfectMatch; } else { subtitleDownloadLanguages = libraryOptions.SubtitleDownloadLanguages; - SkipIfEmbeddedSubtitlesPresent = libraryOptions.SkipSubtitlesIfEmbeddedSubtitlesPresent; - SkipIfAudioTrackMatches = libraryOptions.SkipSubtitlesIfAudioTrackMatches; - RequirePerfectMatch = libraryOptions.RequirePerfectSubtitleMatch; + skipIfEmbeddedSubtitlesPresent = libraryOptions.SkipSubtitlesIfEmbeddedSubtitlesPresent; + skipIfAudioTrackMatches = libraryOptions.SkipSubtitlesIfAudioTrackMatches; + requirePerfectMatch = libraryOptions.RequirePerfectSubtitleMatch; } - var downloadedLanguages = await new SubtitleDownloader(_logger, - _subtitleManager) - .DownloadSubtitles(video, - mediaStreams, - SkipIfEmbeddedSubtitlesPresent, - SkipIfAudioTrackMatches, - RequirePerfectMatch, - subtitleDownloadLanguages, - libraryOptions.DisabledSubtitleFetchers, - libraryOptions.SubtitleFetcherOrder, - cancellationToken).ConfigureAwait(false); + var downloadedLanguages = await new SubtitleDownloader( + _logger, + _subtitleManager).DownloadSubtitles( + video, + mediaStreams, + skipIfEmbeddedSubtitlesPresent, + skipIfAudioTrackMatches, + requirePerfectMatch, + subtitleDownloadLanguages, + libraryOptions.DisabledSubtitleFetchers, + libraryOptions.SubtitleFetcherOrder, + cancellationToken).ConfigureAwait(false); // Rescan if (downloadedLanguages.Count > 0) @@ -201,25 +211,11 @@ namespace MediaBrowser.Providers.MediaInfo public IEnumerable<TaskTriggerInfo> GetDefaultTriggers() { - return new[] { - + return new[] + { // Every so often new TaskTriggerInfo { Type = TaskTriggerInfo.TriggerInterval, IntervalTicks = TimeSpan.FromHours(24).Ticks} }; } - - public string Name => _localization.GetLocalizedString("TaskDownloadMissingSubtitles"); - - public string Description => _localization.GetLocalizedString("TaskDownloadMissingSubtitlesDescription"); - - public string Category => _localization.GetLocalizedString("TasksLibraryCategory"); - - public string Key => "DownloadSubtitles"; - - public bool IsHidden => false; - - public bool IsEnabled => true; - - public bool IsLogged => true; } } diff --git a/MediaBrowser.Providers/MediaInfo/VideoImageProvider.cs b/MediaBrowser.Providers/MediaInfo/VideoImageProvider.cs index f405700407..fc38d3832a 100644 --- a/MediaBrowser.Providers/MediaInfo/VideoImageProvider.cs +++ b/MediaBrowser.Providers/MediaInfo/VideoImageProvider.cs @@ -1,3 +1,5 @@ +#pragma warning disable CS1591 + using System; using System.Collections.Generic; using System.Linq; @@ -17,7 +19,7 @@ namespace MediaBrowser.Providers.MediaInfo public class VideoImageProvider : IDynamicImageProvider, IHasOrder { private readonly IMediaEncoder _mediaEncoder; - private readonly ILogger _logger; + private readonly ILogger<VideoImageProvider> _logger; private readonly IFileSystem _fileSystem; public VideoImageProvider(IMediaEncoder mediaEncoder, ILogger<VideoImageProvider> logger, IFileSystem fileSystem) @@ -27,6 +29,11 @@ namespace MediaBrowser.Providers.MediaInfo _fileSystem = fileSystem; } + public string Name => "Screen Grabber"; + + // Make sure this comes after internet image providers + public int Order => 100; + public IEnumerable<ImageType> GetSupportedImages(BaseItem item) { return new List<ImageType> { ImageType.Primary }; @@ -93,6 +100,7 @@ namespace MediaBrowser.Providers.MediaInfo { videoIndex++; } + if (mediaStream == imageStream) { break; @@ -124,14 +132,13 @@ namespace MediaBrowser.Providers.MediaInfo }; } - public string Name => "Screen Grabber"; - public bool Supports(BaseItem item) { if (item.IsShortcut) { return false; } + if (!item.IsFileProtocol) { return false; @@ -146,7 +153,5 @@ namespace MediaBrowser.Providers.MediaInfo return false; } - // Make sure this comes after internet image providers - public int Order => 100; } } diff --git a/MediaBrowser.Providers/Movies/MovieExternalIds.cs b/MediaBrowser.Providers/Movies/ImdbExternalId.cs index 55810b1ed8..a8d74aa0b5 100644 --- a/MediaBrowser.Providers/Movies/MovieExternalIds.cs +++ b/MediaBrowser.Providers/Movies/ImdbExternalId.cs @@ -1,19 +1,25 @@ +#pragma warning disable CS1591 + using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities.Movies; using MediaBrowser.Controller.Entities.TV; using MediaBrowser.Controller.LiveTv; using MediaBrowser.Controller.Providers; using MediaBrowser.Model.Entities; +using MediaBrowser.Model.Providers; namespace MediaBrowser.Providers.Movies { public class ImdbExternalId : IExternalId { /// <inheritdoc /> - public string Name => "IMDb"; + public string ProviderName => "IMDb"; + + /// <inheritdoc /> + public string Key => MetadataProvider.Imdb.ToString(); /// <inheritdoc /> - public string Key => MetadataProviders.Imdb.ToString(); + public ExternalIdMediaType? Type => null; /// <inheritdoc /> public string UrlFormatString => "https://www.imdb.com/title/{0}"; @@ -30,19 +36,4 @@ namespace MediaBrowser.Providers.Movies return item is Movie || item is MusicVideo || item is Series || item is Episode || item is Trailer; } } - - public class ImdbPersonExternalId : IExternalId - { - /// <inheritdoc /> - public string Name => "IMDb"; - - /// <inheritdoc /> - public string Key => MetadataProviders.Imdb.ToString(); - - /// <inheritdoc /> - public string UrlFormatString => "https://www.imdb.com/name/{0}"; - - /// <inheritdoc /> - public bool Supports(IHasProviderIds item) => item is Person; - } } diff --git a/MediaBrowser.Providers/Movies/ImdbPersonExternalId.cs b/MediaBrowser.Providers/Movies/ImdbPersonExternalId.cs new file mode 100644 index 0000000000..8151ab4715 --- /dev/null +++ b/MediaBrowser.Providers/Movies/ImdbPersonExternalId.cs @@ -0,0 +1,27 @@ +#pragma warning disable CS1591 + +using MediaBrowser.Controller.Entities; +using MediaBrowser.Controller.Providers; +using MediaBrowser.Model.Entities; +using MediaBrowser.Model.Providers; + +namespace MediaBrowser.Providers.Movies +{ + public class ImdbPersonExternalId : IExternalId + { + /// <inheritdoc /> + public string ProviderName => "IMDb"; + + /// <inheritdoc /> + public string Key => MetadataProvider.Imdb.ToString(); + + /// <inheritdoc /> + public ExternalIdMediaType? Type => ExternalIdMediaType.Person; + + /// <inheritdoc /> + public string UrlFormatString => "https://www.imdb.com/name/{0}"; + + /// <inheritdoc /> + public bool Supports(IHasProviderIds item) => item is Person; + } +} diff --git a/MediaBrowser.Providers/Movies/MovieMetadataService.cs b/MediaBrowser.Providers/Movies/MovieMetadataService.cs index 1e2c325d99..c477fb70f6 100644 --- a/MediaBrowser.Providers/Movies/MovieMetadataService.cs +++ b/MediaBrowser.Providers/Movies/MovieMetadataService.cs @@ -1,3 +1,5 @@ +#pragma warning disable CS1591 + using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Entities.Movies; using MediaBrowser.Controller.Library; @@ -28,15 +30,17 @@ namespace MediaBrowser.Providers.Movies { return false; } + if (!item.ProductionYear.HasValue) { return false; } + return base.IsFullLocalMetadata(item); } /// <inheritdoc /> - protected override void MergeData(MetadataResult<Movie> source, MetadataResult<Movie> target, MetadataFields[] lockedFields, bool replaceData, bool mergeMetadataSettings) + protected override void MergeData(MetadataResult<Movie> source, MetadataResult<Movie> target, MetadataField[] lockedFields, bool replaceData, bool mergeMetadataSettings) { ProviderUtils.MergeBaseItemData(source, target, lockedFields, replaceData, mergeMetadataSettings); diff --git a/MediaBrowser.Providers/Movies/TrailerMetadataService.cs b/MediaBrowser.Providers/Movies/TrailerMetadataService.cs index 2e6f762b8e..f32d9ec0a3 100644 --- a/MediaBrowser.Providers/Movies/TrailerMetadataService.cs +++ b/MediaBrowser.Providers/Movies/TrailerMetadataService.cs @@ -1,3 +1,5 @@ +#pragma warning disable CS1591 + using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Library; @@ -28,15 +30,17 @@ namespace MediaBrowser.Providers.Movies { return false; } + if (!item.ProductionYear.HasValue) { return false; } + return base.IsFullLocalMetadata(item); } /// <inheritdoc /> - protected override void MergeData(MetadataResult<Trailer> source, MetadataResult<Trailer> target, MetadataFields[] lockedFields, bool replaceData, bool mergeMetadataSettings) + protected override void MergeData(MetadataResult<Trailer> source, MetadataResult<Trailer> target, MetadataField[] lockedFields, bool replaceData, bool mergeMetadataSettings) { ProviderUtils.MergeBaseItemData(source, target, lockedFields, replaceData, mergeMetadataSettings); diff --git a/MediaBrowser.Providers/Music/Extensions.cs b/MediaBrowser.Providers/Music/AlbumInfoExtensions.cs index ea1efe266b..dddfd02e4f 100644 --- a/MediaBrowser.Providers/Music/Extensions.cs +++ b/MediaBrowser.Providers/Music/AlbumInfoExtensions.cs @@ -1,10 +1,12 @@ +#pragma warning disable CS1591 + using System.Linq; using MediaBrowser.Controller.Providers; using MediaBrowser.Model.Entities; namespace MediaBrowser.Providers.Music { - public static class Extensions + public static class AlbumInfoExtensions { public static string GetAlbumArtist(this AlbumInfo info) { @@ -16,16 +18,16 @@ namespace MediaBrowser.Providers.Music return id; } - return info.AlbumArtists.FirstOrDefault(); + return info.AlbumArtists.Count > 0 ? info.AlbumArtists[0] : default; } public static string GetReleaseGroupId(this AlbumInfo info) { - var id = info.GetProviderId(MetadataProviders.MusicBrainzReleaseGroup); + var id = info.GetProviderId(MetadataProvider.MusicBrainzReleaseGroup); if (string.IsNullOrEmpty(id)) { - return info.SongInfos.Select(i => i.GetProviderId(MetadataProviders.MusicBrainzReleaseGroup)) + return info.SongInfos.Select(i => i.GetProviderId(MetadataProvider.MusicBrainzReleaseGroup)) .FirstOrDefault(i => !string.IsNullOrEmpty(i)); } @@ -34,11 +36,11 @@ namespace MediaBrowser.Providers.Music public static string GetReleaseId(this AlbumInfo info) { - var id = info.GetProviderId(MetadataProviders.MusicBrainzAlbum); + var id = info.GetProviderId(MetadataProvider.MusicBrainzAlbum); if (string.IsNullOrEmpty(id)) { - return info.SongInfos.Select(i => i.GetProviderId(MetadataProviders.MusicBrainzAlbum)) + return info.SongInfos.Select(i => i.GetProviderId(MetadataProvider.MusicBrainzAlbum)) .FirstOrDefault(i => !string.IsNullOrEmpty(i)); } @@ -47,16 +49,16 @@ namespace MediaBrowser.Providers.Music public static string GetMusicBrainzArtistId(this AlbumInfo info) { - info.ProviderIds.TryGetValue(MetadataProviders.MusicBrainzAlbumArtist.ToString(), out string id); + info.ProviderIds.TryGetValue(MetadataProvider.MusicBrainzAlbumArtist.ToString(), out string id); if (string.IsNullOrEmpty(id)) { - info.ArtistProviderIds.TryGetValue(MetadataProviders.MusicBrainzArtist.ToString(), out id); + info.ArtistProviderIds.TryGetValue(MetadataProvider.MusicBrainzArtist.ToString(), out id); } if (string.IsNullOrEmpty(id)) { - return info.SongInfos.Select(i => i.GetProviderId(MetadataProviders.MusicBrainzAlbumArtist)) + return info.SongInfos.Select(i => i.GetProviderId(MetadataProvider.MusicBrainzAlbumArtist)) .FirstOrDefault(i => !string.IsNullOrEmpty(i)); } @@ -65,11 +67,11 @@ namespace MediaBrowser.Providers.Music public static string GetMusicBrainzArtistId(this ArtistInfo info) { - info.ProviderIds.TryGetValue(MetadataProviders.MusicBrainzArtist.ToString(), out var id); + info.ProviderIds.TryGetValue(MetadataProvider.MusicBrainzArtist.ToString(), out var id); if (string.IsNullOrEmpty(id)) { - return info.SongInfos.Select(i => i.GetProviderId(MetadataProviders.MusicBrainzAlbumArtist)) + return info.SongInfos.Select(i => i.GetProviderId(MetadataProvider.MusicBrainzAlbumArtist)) .FirstOrDefault(i => !string.IsNullOrEmpty(i)); } diff --git a/MediaBrowser.Providers/Music/AlbumMetadataService.cs b/MediaBrowser.Providers/Music/AlbumMetadataService.cs index ed6c01968d..8c9a1f59b1 100644 --- a/MediaBrowser.Providers/Music/AlbumMetadataService.cs +++ b/MediaBrowser.Providers/Music/AlbumMetadataService.cs @@ -1,3 +1,5 @@ +#pragma warning disable CS1591 + using System; using System.Collections.Generic; using System.Linq; @@ -45,7 +47,7 @@ namespace MediaBrowser.Providers.Music if (isFullRefresh || currentUpdateType > ItemUpdateType.None) { - if (!item.LockedFields.Contains(MetadataFields.Name)) + if (!item.LockedFields.Contains(MetadataField.Name)) { var name = children.Select(i => i.Album).FirstOrDefault(i => !string.IsNullOrEmpty(i)); @@ -108,7 +110,7 @@ namespace MediaBrowser.Providers.Music protected override void MergeData( MetadataResult<MusicAlbum> source, MetadataResult<MusicAlbum> target, - MetadataFields[] lockedFields, + MetadataField[] lockedFields, bool replaceData, bool mergeMetadataSettings) { diff --git a/MediaBrowser.Providers/Music/ArtistMetadataService.cs b/MediaBrowser.Providers/Music/ArtistMetadataService.cs index 5a30260a52..e29475dd73 100644 --- a/MediaBrowser.Providers/Music/ArtistMetadataService.cs +++ b/MediaBrowser.Providers/Music/ArtistMetadataService.cs @@ -1,3 +1,5 @@ +#pragma warning disable CS1591 + using System.Collections.Generic; using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Entities; @@ -39,7 +41,7 @@ namespace MediaBrowser.Providers.Music } /// <inheritdoc /> - protected override void MergeData(MetadataResult<MusicArtist> source, MetadataResult<MusicArtist> target, MetadataFields[] lockedFields, bool replaceData, bool mergeMetadataSettings) + protected override void MergeData(MetadataResult<MusicArtist> source, MetadataResult<MusicArtist> target, MetadataField[] lockedFields, bool replaceData, bool mergeMetadataSettings) { ProviderUtils.MergeBaseItemData(source, target, lockedFields, replaceData, mergeMetadataSettings); } diff --git a/MediaBrowser.Providers/Music/AudioMetadataService.cs b/MediaBrowser.Providers/Music/AudioMetadataService.cs index e726fa1e2c..8b9fc8a08d 100644 --- a/MediaBrowser.Providers/Music/AudioMetadataService.cs +++ b/MediaBrowser.Providers/Music/AudioMetadataService.cs @@ -1,3 +1,5 @@ +#pragma warning disable CS1591 + using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Entities.Audio; using MediaBrowser.Controller.Library; @@ -22,7 +24,7 @@ namespace MediaBrowser.Providers.Music } /// <inheritdoc /> - protected override void MergeData(MetadataResult<Audio> source, MetadataResult<Audio> target, MetadataFields[] lockedFields, bool replaceData, bool mergeMetadataSettings) + protected override void MergeData(MetadataResult<Audio> source, MetadataResult<Audio> target, MetadataField[] lockedFields, bool replaceData, bool mergeMetadataSettings) { ProviderUtils.MergeBaseItemData(source, target, lockedFields, replaceData, mergeMetadataSettings); diff --git a/MediaBrowser.Providers/Music/MusicExternalIds.cs b/MediaBrowser.Providers/Music/ImvdbId.cs index 628b9a9a10..a1726b9965 100644 --- a/MediaBrowser.Providers/Music/MusicExternalIds.cs +++ b/MediaBrowser.Providers/Music/ImvdbId.cs @@ -1,18 +1,24 @@ +#pragma warning disable CS1591 + using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Providers; using MediaBrowser.Model.Entities; +using MediaBrowser.Model.Providers; namespace MediaBrowser.Providers.Music { public class ImvdbId : IExternalId { /// <inheritdoc /> - public string Name => "IMVDb"; + public string ProviderName => "IMVDb"; /// <inheritdoc /> public string Key => "IMVDb"; /// <inheritdoc /> + public ExternalIdMediaType? Type => null; + + /// <inheritdoc /> public string UrlFormatString => null; /// <inheritdoc /> diff --git a/MediaBrowser.Providers/Music/MusicVideoMetadataService.cs b/MediaBrowser.Providers/Music/MusicVideoMetadataService.cs index d653e10638..1d611a7466 100644 --- a/MediaBrowser.Providers/Music/MusicVideoMetadataService.cs +++ b/MediaBrowser.Providers/Music/MusicVideoMetadataService.cs @@ -1,3 +1,5 @@ +#pragma warning disable CS1591 + using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Library; @@ -25,7 +27,7 @@ namespace MediaBrowser.Providers.Music protected override void MergeData( MetadataResult<MusicVideo> source, MetadataResult<MusicVideo> target, - MetadataFields[] lockedFields, + MetadataField[] lockedFields, bool replaceData, bool mergeMetadataSettings) { diff --git a/MediaBrowser.Providers/MusicGenres/MusicGenreMetadataService.cs b/MediaBrowser.Providers/MusicGenres/MusicGenreMetadataService.cs index bb47de40bc..7dda7e9bf3 100644 --- a/MediaBrowser.Providers/MusicGenres/MusicGenreMetadataService.cs +++ b/MediaBrowser.Providers/MusicGenres/MusicGenreMetadataService.cs @@ -1,3 +1,5 @@ +#pragma warning disable CS1591 + using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Entities.Audio; using MediaBrowser.Controller.Library; @@ -22,7 +24,7 @@ namespace MediaBrowser.Providers.MusicGenres } /// <inheritdoc /> - protected override void MergeData(MetadataResult<MusicGenre> source, MetadataResult<MusicGenre> target, MetadataFields[] lockedFields, bool replaceData, bool mergeMetadataSettings) + protected override void MergeData(MetadataResult<MusicGenre> source, MetadataResult<MusicGenre> target, MetadataField[] lockedFields, bool replaceData, bool mergeMetadataSettings) { ProviderUtils.MergeBaseItemData(source, target, lockedFields, replaceData, mergeMetadataSettings); } diff --git a/MediaBrowser.Providers/People/PersonMetadataService.cs b/MediaBrowser.Providers/People/PersonMetadataService.cs index 804f3f3e32..fe6d1d4d30 100644 --- a/MediaBrowser.Providers/People/PersonMetadataService.cs +++ b/MediaBrowser.Providers/People/PersonMetadataService.cs @@ -1,3 +1,5 @@ +#pragma warning disable CS1591 + using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Library; @@ -22,7 +24,7 @@ namespace MediaBrowser.Providers.People } /// <inheritdoc /> - protected override void MergeData(MetadataResult<Person> source, MetadataResult<Person> target, MetadataFields[] lockedFields, bool replaceData, bool mergeMetadataSettings) + protected override void MergeData(MetadataResult<Person> source, MetadataResult<Person> target, MetadataField[] lockedFields, bool replaceData, bool mergeMetadataSettings) { ProviderUtils.MergeBaseItemData(source, target, lockedFields, replaceData, mergeMetadataSettings); } diff --git a/MediaBrowser.Providers/Photos/PhotoAlbumMetadataService.cs b/MediaBrowser.Providers/Photos/PhotoAlbumMetadataService.cs index af8f7a2622..60ed964524 100644 --- a/MediaBrowser.Providers/Photos/PhotoAlbumMetadataService.cs +++ b/MediaBrowser.Providers/Photos/PhotoAlbumMetadataService.cs @@ -1,3 +1,5 @@ +#pragma warning disable CS1591 + using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Library; @@ -22,7 +24,7 @@ namespace MediaBrowser.Providers.Photos } /// <inheritdoc /> - protected override void MergeData(MetadataResult<PhotoAlbum> source, MetadataResult<PhotoAlbum> target, MetadataFields[] lockedFields, bool replaceData, bool mergeMetadataSettings) + protected override void MergeData(MetadataResult<PhotoAlbum> source, MetadataResult<PhotoAlbum> target, MetadataField[] lockedFields, bool replaceData, bool mergeMetadataSettings) { ProviderUtils.MergeBaseItemData(source, target, lockedFields, replaceData, mergeMetadataSettings); } diff --git a/MediaBrowser.Providers/Photos/PhotoMetadataService.cs b/MediaBrowser.Providers/Photos/PhotoMetadataService.cs index 579b5a4d0f..cbbb433c09 100644 --- a/MediaBrowser.Providers/Photos/PhotoMetadataService.cs +++ b/MediaBrowser.Providers/Photos/PhotoMetadataService.cs @@ -1,3 +1,5 @@ +#pragma warning disable CS1591 + using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Library; @@ -22,7 +24,7 @@ namespace MediaBrowser.Providers.Photos } /// <inheritdoc /> - protected override void MergeData(MetadataResult<Photo> source, MetadataResult<Photo> target, MetadataFields[] lockedFields, bool replaceData, bool mergeMetadataSettings) + protected override void MergeData(MetadataResult<Photo> source, MetadataResult<Photo> target, MetadataField[] lockedFields, bool replaceData, bool mergeMetadataSettings) { ProviderUtils.MergeBaseItemData(source, target, lockedFields, replaceData, mergeMetadataSettings); } diff --git a/MediaBrowser.Providers/Playlists/PlaylistItemsProvider.cs b/MediaBrowser.Providers/Playlists/PlaylistItemsProvider.cs index ae837c591a..067d585cba 100644 --- a/MediaBrowser.Providers/Playlists/PlaylistItemsProvider.cs +++ b/MediaBrowser.Providers/Playlists/PlaylistItemsProvider.cs @@ -1,3 +1,5 @@ +#pragma warning disable CS1591 + using System; using System.Collections.Generic; using System.IO; @@ -8,7 +10,6 @@ using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Playlists; using MediaBrowser.Controller.Providers; -using MediaBrowser.Model.IO; using Microsoft.Extensions.Logging; using PlaylistsNET.Content; @@ -20,17 +21,18 @@ namespace MediaBrowser.Providers.Playlists IPreRefreshProvider, IHasItemChangeMonitor { - private ILogger _logger; - private IFileSystem _fileSystem; + private readonly ILogger<PlaylistItemsProvider> _logger; - public PlaylistItemsProvider(IFileSystem fileSystem, ILogger<PlaylistItemsProvider> logger) + public PlaylistItemsProvider(ILogger<PlaylistItemsProvider> logger) { - _fileSystem = fileSystem; _logger = logger; } public string Name => "Playlist Reader"; + // Run last + public int Order => 100; + public Task<ItemUpdateType> FetchAsync(Playlist item, MetadataRefreshOptions options, CancellationToken cancellationToken) { var path = item.Path; @@ -61,18 +63,22 @@ namespace MediaBrowser.Providers.Playlists { return GetWplItems(stream); } + if (string.Equals(".zpl", extension, StringComparison.OrdinalIgnoreCase)) { return GetZplItems(stream); } + if (string.Equals(".m3u", extension, StringComparison.OrdinalIgnoreCase)) { return GetM3uItems(stream); } + if (string.Equals(".m3u8", extension, StringComparison.OrdinalIgnoreCase)) { return GetM3u8Items(stream); } + if (string.Equals(".pls", extension, StringComparison.OrdinalIgnoreCase)) { return GetPlsItems(stream); @@ -95,7 +101,7 @@ namespace MediaBrowser.Providers.Playlists private IEnumerable<LinkedChild> GetM3u8Items(Stream stream) { - var content = new M3u8Content(); + var content = new M3uContent(); var playlist = content.GetFromStream(stream); return playlist.PlaylistEntries.Select(i => new LinkedChild @@ -157,7 +163,5 @@ namespace MediaBrowser.Providers.Playlists return false; } - // Run last - public int Order => 100; } } diff --git a/MediaBrowser.Providers/Playlists/PlaylistMetadataService.cs b/MediaBrowser.Providers/Playlists/PlaylistMetadataService.cs index a41362ea3e..5262919d56 100644 --- a/MediaBrowser.Providers/Playlists/PlaylistMetadataService.cs +++ b/MediaBrowser.Providers/Playlists/PlaylistMetadataService.cs @@ -1,3 +1,5 @@ +#pragma warning disable CS1591 + using System.Collections.Generic; using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Entities; @@ -37,7 +39,7 @@ namespace MediaBrowser.Providers.Playlists => item.GetLinkedChildren(); /// <inheritdoc /> - protected override void MergeData(MetadataResult<Playlist> source, MetadataResult<Playlist> target, MetadataFields[] lockedFields, bool replaceData, bool mergeMetadataSettings) + protected override void MergeData(MetadataResult<Playlist> source, MetadataResult<Playlist> target, MetadataField[] lockedFields, bool replaceData, bool mergeMetadataSettings) { ProviderUtils.MergeBaseItemData(source, target, lockedFields, replaceData, mergeMetadataSettings); diff --git a/MediaBrowser.Providers/Plugins/AudioDb/AlbumImageProvider.cs b/MediaBrowser.Providers/Plugins/AudioDb/AlbumImageProvider.cs index dee2d59f0f..e3a1decb88 100644 --- a/MediaBrowser.Providers/Plugins/AudioDb/AlbumImageProvider.cs +++ b/MediaBrowser.Providers/Plugins/AudioDb/AlbumImageProvider.cs @@ -1,4 +1,7 @@ +#pragma warning disable CS1591 + using System.Collections.Generic; +using System.Net.Http; using System.Threading; using System.Threading.Tasks; using MediaBrowser.Common.Net; @@ -15,13 +18,13 @@ namespace MediaBrowser.Providers.Plugins.AudioDb public class AudioDbAlbumImageProvider : IRemoteImageProvider, IHasOrder { private readonly IServerConfigurationManager _config; - private readonly IHttpClient _httpClient; + private readonly IHttpClientFactory _httpClientFactory; private readonly IJsonSerializer _json; - public AudioDbAlbumImageProvider(IServerConfigurationManager config, IHttpClient httpClient, IJsonSerializer json) + public AudioDbAlbumImageProvider(IServerConfigurationManager config, IHttpClientFactory httpClientFactory, IJsonSerializer json) { _config = config; - _httpClient = httpClient; + _httpClientFactory = httpClientFactory; _json = json; } @@ -45,7 +48,7 @@ namespace MediaBrowser.Providers.Plugins.AudioDb /// <inheritdoc /> public async Task<IEnumerable<RemoteImageInfo>> GetImages(BaseItem item, CancellationToken cancellationToken) { - var id = item.GetProviderId(MetadataProviders.MusicBrainzReleaseGroup); + var id = item.GetProviderId(MetadataProvider.MusicBrainzReleaseGroup); if (!string.IsNullOrWhiteSpace(id)) { @@ -92,13 +95,10 @@ namespace MediaBrowser.Providers.Plugins.AudioDb } /// <inheritdoc /> - public Task<HttpResponseInfo> GetImageResponse(string url, CancellationToken cancellationToken) + public Task<HttpResponseMessage> GetImageResponse(string url, CancellationToken cancellationToken) { - return _httpClient.GetResponse(new HttpRequestOptions - { - CancellationToken = cancellationToken, - Url = url - }); + var httpClient = _httpClientFactory.CreateClient(NamedClient.Default); + return httpClient.GetAsync(url, cancellationToken); } /// <inheritdoc /> diff --git a/MediaBrowser.Providers/Plugins/AudioDb/AlbumProvider.cs b/MediaBrowser.Providers/Plugins/AudioDb/AlbumProvider.cs index 1a0e878719..e6d89e6880 100644 --- a/MediaBrowser.Providers/Plugins/AudioDb/AlbumProvider.cs +++ b/MediaBrowser.Providers/Plugins/AudioDb/AlbumProvider.cs @@ -1,3 +1,5 @@ +#pragma warning disable CS1591 + using System; using System.Collections.Generic; using System.Globalization; @@ -24,16 +26,16 @@ namespace MediaBrowser.Providers.Plugins.AudioDb { private readonly IServerConfigurationManager _config; private readonly IFileSystem _fileSystem; - private readonly IHttpClient _httpClient; + private readonly IHttpClientFactory _httpClientFactory; private readonly IJsonSerializer _json; public static AudioDbAlbumProvider Current; - public AudioDbAlbumProvider(IServerConfigurationManager config, IFileSystem fileSystem, IHttpClient httpClient, IJsonSerializer json) + public AudioDbAlbumProvider(IServerConfigurationManager config, IFileSystem fileSystem, IHttpClientFactory httpClientFactory, IJsonSerializer json) { _config = config; _fileSystem = fileSystem; - _httpClient = httpClient; + _httpClientFactory = httpClientFactory; _json = json; Current = this; @@ -104,11 +106,11 @@ namespace MediaBrowser.Providers.Plugins.AudioDb item.Genres = new[] { result.strGenre }; } - item.SetProviderId(MetadataProviders.AudioDbArtist, result.idArtist); - item.SetProviderId(MetadataProviders.AudioDbAlbum, result.idAlbum); + item.SetProviderId(MetadataProvider.AudioDbArtist, result.idArtist); + item.SetProviderId(MetadataProvider.AudioDbAlbum, result.idAlbum); - item.SetProviderId(MetadataProviders.MusicBrainzAlbumArtist, result.strMusicBrainzArtistID); - item.SetProviderId(MetadataProviders.MusicBrainzReleaseGroup, result.strMusicBrainzID); + item.SetProviderId(MetadataProvider.MusicBrainzAlbumArtist, result.strMusicBrainzArtistID); + item.SetProviderId(MetadataProvider.MusicBrainzReleaseGroup, result.strMusicBrainzID); string overview = null; @@ -172,18 +174,10 @@ namespace MediaBrowser.Providers.Plugins.AudioDb Directory.CreateDirectory(Path.GetDirectoryName(path)); - using (var httpResponse = await _httpClient.SendAsync( - new HttpRequestOptions - { - Url = url, - CancellationToken = cancellationToken - }, - HttpMethod.Get).ConfigureAwait(false)) - using (var response = httpResponse.Content) - using (var xmlFileStream = new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.Read, IODefaults.FileStreamBufferSize, true)) - { - await response.CopyToAsync(xmlFileStream).ConfigureAwait(false); - } + using var response = await _httpClientFactory.CreateClient(NamedClient.Default).GetAsync(url, cancellationToken).ConfigureAwait(false); + await using var stream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false); + await using var xmlFileStream = new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.Read, IODefaults.FileStreamBufferSize, true); + await stream.CopyToAsync(xmlFileStream, cancellationToken).ConfigureAwait(false); } private static string GetAlbumDataPath(IApplicationPaths appPaths, string musicBrainzReleaseGroupId) @@ -210,42 +204,79 @@ namespace MediaBrowser.Providers.Plugins.AudioDb public class Album { public string idAlbum { get; set; } + public string idArtist { get; set; } + public string strAlbum { get; set; } + public string strArtist { get; set; } + public string intYearReleased { get; set; } + public string strGenre { get; set; } + public string strSubGenre { get; set; } + public string strReleaseFormat { get; set; } + public string intSales { get; set; } + public string strAlbumThumb { get; set; } + public string strAlbumCDart { get; set; } + public string strDescriptionEN { get; set; } + public string strDescriptionDE { get; set; } + public string strDescriptionFR { get; set; } + public string strDescriptionCN { get; set; } + public string strDescriptionIT { get; set; } + public string strDescriptionJP { get; set; } + public string strDescriptionRU { get; set; } + public string strDescriptionES { get; set; } + public string strDescriptionPT { get; set; } + public string strDescriptionSE { get; set; } + public string strDescriptionNL { get; set; } + public string strDescriptionHU { get; set; } + public string strDescriptionNO { get; set; } + public string strDescriptionIL { get; set; } + public string strDescriptionPL { get; set; } + public object intLoved { get; set; } + public object intScore { get; set; } + public string strReview { get; set; } + public object strMood { get; set; } + public object strTheme { get; set; } + public object strSpeed { get; set; } + public object strLocation { get; set; } + public string strMusicBrainzID { get; set; } + public string strMusicBrainzArtistID { get; set; } + public object strItunesID { get; set; } + public object strAmazonID { get; set; } + public string strLocked { get; set; } } @@ -255,7 +286,7 @@ namespace MediaBrowser.Providers.Plugins.AudioDb } /// <inheritdoc /> - public Task<HttpResponseInfo> GetImageResponse(string url, CancellationToken cancellationToken) + public Task<HttpResponseMessage> GetImageResponse(string url, CancellationToken cancellationToken) { throw new NotImplementedException(); } diff --git a/MediaBrowser.Providers/Plugins/AudioDb/ArtistImageProvider.cs b/MediaBrowser.Providers/Plugins/AudioDb/ArtistImageProvider.cs index 18afd5dd5c..54851c4d07 100644 --- a/MediaBrowser.Providers/Plugins/AudioDb/ArtistImageProvider.cs +++ b/MediaBrowser.Providers/Plugins/AudioDb/ArtistImageProvider.cs @@ -1,4 +1,7 @@ +#pragma warning disable CS1591 + using System.Collections.Generic; +using System.Net.Http; using System.Threading; using System.Threading.Tasks; using MediaBrowser.Common.Net; @@ -15,14 +18,14 @@ namespace MediaBrowser.Providers.Plugins.AudioDb public class AudioDbArtistImageProvider : IRemoteImageProvider, IHasOrder { private readonly IServerConfigurationManager _config; - private readonly IHttpClient _httpClient; + private readonly IHttpClientFactory _httpClientFactory; private readonly IJsonSerializer _json; - public AudioDbArtistImageProvider(IServerConfigurationManager config, IJsonSerializer json, IHttpClient httpClient) + public AudioDbArtistImageProvider(IServerConfigurationManager config, IJsonSerializer json, IHttpClientFactory httpClientFactory) { _config = config; _json = json; - _httpClient = httpClient; + _httpClientFactory = httpClientFactory; } /// <inheritdoc /> @@ -47,7 +50,7 @@ namespace MediaBrowser.Providers.Plugins.AudioDb /// <inheritdoc /> public async Task<IEnumerable<RemoteImageInfo>> GetImages(BaseItem item, CancellationToken cancellationToken) { - var id = item.GetProviderId(MetadataProviders.MusicBrainzArtist); + var id = item.GetProviderId(MetadataProvider.MusicBrainzArtist); if (!string.IsNullOrWhiteSpace(id)) { @@ -133,13 +136,10 @@ namespace MediaBrowser.Providers.Plugins.AudioDb return list; } - public Task<HttpResponseInfo> GetImageResponse(string url, CancellationToken cancellationToken) + public Task<HttpResponseMessage> GetImageResponse(string url, CancellationToken cancellationToken) { - return _httpClient.GetResponse(new HttpRequestOptions - { - CancellationToken = cancellationToken, - Url = url - }); + var httpClient = _httpClientFactory.CreateClient(NamedClient.Default); + return httpClient.GetAsync(url, cancellationToken); } /// <inheritdoc /> diff --git a/MediaBrowser.Providers/Plugins/AudioDb/ArtistProvider.cs b/MediaBrowser.Providers/Plugins/AudioDb/ArtistProvider.cs index df0f3df8fa..72dad8a25a 100644 --- a/MediaBrowser.Providers/Plugins/AudioDb/ArtistProvider.cs +++ b/MediaBrowser.Providers/Plugins/AudioDb/ArtistProvider.cs @@ -1,3 +1,5 @@ +#pragma warning disable CS1591 + using System; using System.Collections.Generic; using System.IO; @@ -21,25 +23,25 @@ namespace MediaBrowser.Providers.Plugins.AudioDb { public class AudioDbArtistProvider : IRemoteMetadataProvider<MusicArtist, ArtistInfo>, IHasOrder { + private const string ApiKey = "195003"; + public const string BaseUrl = "https://www.theaudiodb.com/api/v1/json/" + ApiKey; + private readonly IServerConfigurationManager _config; private readonly IFileSystem _fileSystem; - private readonly IHttpClient _httpClient; + private readonly IHttpClientFactory _httpClientFactory; private readonly IJsonSerializer _json; - public static AudioDbArtistProvider Current; - - private const string ApiKey = "195003"; - public const string BaseUrl = "https://www.theaudiodb.com/api/v1/json/" + ApiKey; - - public AudioDbArtistProvider(IServerConfigurationManager config, IFileSystem fileSystem, IHttpClient httpClient, IJsonSerializer json) + public AudioDbArtistProvider(IServerConfigurationManager config, IFileSystem fileSystem, IHttpClientFactory httpClientFactory, IJsonSerializer json) { _config = config; _fileSystem = fileSystem; - _httpClient = httpClient; + _httpClientFactory = httpClientFactory; _json = json; Current = this; } + public static AudioDbArtistProvider Current { get; private set; } + /// <inheritdoc /> public string Name => "TheAudioDB"; @@ -85,15 +87,15 @@ namespace MediaBrowser.Providers.Plugins.AudioDb private void ProcessResult(MusicArtist item, Artist result, string preferredLanguage) { - //item.HomePageUrl = result.strWebsite; + // item.HomePageUrl = result.strWebsite; if (!string.IsNullOrEmpty(result.strGenre)) { item.Genres = new[] { result.strGenre }; } - item.SetProviderId(MetadataProviders.AudioDbArtist, result.idArtist); - item.SetProviderId(MetadataProviders.MusicBrainzArtist, result.strMusicBrainzID); + item.SetProviderId(MetadataProvider.AudioDbArtist, result.idArtist); + item.SetProviderId(MetadataProvider.MusicBrainzArtist, result.strMusicBrainzID); string overview = null; @@ -153,23 +155,13 @@ namespace MediaBrowser.Providers.Plugins.AudioDb var path = GetArtistInfoPath(_config.ApplicationPaths, musicBrainzId); - using (var httpResponse = await _httpClient.SendAsync( - new HttpRequestOptions - { - Url = url, - CancellationToken = cancellationToken, - BufferContent = true - }, - HttpMethod.Get).ConfigureAwait(false)) - using (var response = httpResponse.Content) - { - Directory.CreateDirectory(Path.GetDirectoryName(path)); + using var response = await _httpClientFactory.CreateClient(NamedClient.Default).GetAsync(url, cancellationToken).ConfigureAwait(false); + await using var stream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false); - using (var xmlFileStream = new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.Read, IODefaults.FileStreamBufferSize, true)) - { - await response.CopyToAsync(xmlFileStream).ConfigureAwait(false); - } - } + Directory.CreateDirectory(Path.GetDirectoryName(path)); + + await using var xmlFileStream = new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.Read, IODefaults.FileStreamBufferSize, true); + await stream.CopyToAsync(xmlFileStream, cancellationToken).ConfigureAwait(false); } /// <summary> @@ -199,45 +191,85 @@ namespace MediaBrowser.Providers.Plugins.AudioDb public class Artist { public string idArtist { get; set; } + public string strArtist { get; set; } + public string strArtistAlternate { get; set; } + public object idLabel { get; set; } + public string intFormedYear { get; set; } + public string intBornYear { get; set; } + public object intDiedYear { get; set; } + public object strDisbanded { get; set; } + public string strGenre { get; set; } + public string strSubGenre { get; set; } + public string strWebsite { get; set; } + public string strFacebook { get; set; } + public string strTwitter { get; set; } + public string strBiographyEN { get; set; } + public string strBiographyDE { get; set; } + public string strBiographyFR { get; set; } + public string strBiographyCN { get; set; } + public string strBiographyIT { get; set; } + public string strBiographyJP { get; set; } + public string strBiographyRU { get; set; } + public string strBiographyES { get; set; } + public string strBiographyPT { get; set; } + public string strBiographySE { get; set; } + public string strBiographyNL { get; set; } + public string strBiographyHU { get; set; } + public string strBiographyNO { get; set; } + public string strBiographyIL { get; set; } + public string strBiographyPL { get; set; } + public string strGender { get; set; } + public string intMembers { get; set; } + public string strCountry { get; set; } + public string strCountryCode { get; set; } + public string strArtistThumb { get; set; } + public string strArtistLogo { get; set; } + public string strArtistFanart { get; set; } + public string strArtistFanart2 { get; set; } + public string strArtistFanart3 { get; set; } + public string strArtistBanner { get; set; } + public string strMusicBrainzID { get; set; } + public object strLastFMChart { get; set; } + public string strLocked { get; set; } } @@ -247,7 +279,7 @@ namespace MediaBrowser.Providers.Plugins.AudioDb } /// <inheritdoc /> - public Task<HttpResponseInfo> GetImageResponse(string url, CancellationToken cancellationToken) + public Task<HttpResponseMessage> GetImageResponse(string url, CancellationToken cancellationToken) { throw new NotImplementedException(); } diff --git a/MediaBrowser.Providers/Plugins/AudioDb/AudioDbAlbumExternalId.cs b/MediaBrowser.Providers/Plugins/AudioDb/AudioDbAlbumExternalId.cs new file mode 100644 index 0000000000..138cfef19a --- /dev/null +++ b/MediaBrowser.Providers/Plugins/AudioDb/AudioDbAlbumExternalId.cs @@ -0,0 +1,27 @@ +#pragma warning disable CS1591 + +using MediaBrowser.Controller.Entities.Audio; +using MediaBrowser.Controller.Providers; +using MediaBrowser.Model.Entities; +using MediaBrowser.Model.Providers; + +namespace MediaBrowser.Providers.Plugins.AudioDb +{ + public class AudioDbAlbumExternalId : IExternalId + { + /// <inheritdoc /> + public string ProviderName => "TheAudioDb"; + + /// <inheritdoc /> + public string Key => MetadataProvider.AudioDbAlbum.ToString(); + + /// <inheritdoc /> + public ExternalIdMediaType? Type => null; + + /// <inheritdoc /> + public string UrlFormatString => "https://www.theaudiodb.com/album/{0}"; + + /// <inheritdoc /> + public bool Supports(IHasProviderIds item) => item is MusicAlbum; + } +} diff --git a/MediaBrowser.Providers/Plugins/AudioDb/AudioDbArtistExternalId.cs b/MediaBrowser.Providers/Plugins/AudioDb/AudioDbArtistExternalId.cs new file mode 100644 index 0000000000..8aceb48c0c --- /dev/null +++ b/MediaBrowser.Providers/Plugins/AudioDb/AudioDbArtistExternalId.cs @@ -0,0 +1,27 @@ +#pragma warning disable CS1591 + +using MediaBrowser.Controller.Entities.Audio; +using MediaBrowser.Controller.Providers; +using MediaBrowser.Model.Entities; +using MediaBrowser.Model.Providers; + +namespace MediaBrowser.Providers.Plugins.AudioDb +{ + public class AudioDbArtistExternalId : IExternalId + { + /// <inheritdoc /> + public string ProviderName => "TheAudioDb"; + + /// <inheritdoc /> + public string Key => MetadataProvider.AudioDbArtist.ToString(); + + /// <inheritdoc /> + public ExternalIdMediaType? Type => ExternalIdMediaType.Artist; + + /// <inheritdoc /> + public string UrlFormatString => "https://www.theaudiodb.com/artist/{0}"; + + /// <inheritdoc /> + public bool Supports(IHasProviderIds item) => item is MusicArtist; + } +} diff --git a/MediaBrowser.Providers/Plugins/AudioDb/AudioDbOtherAlbumExternalId.cs b/MediaBrowser.Providers/Plugins/AudioDb/AudioDbOtherAlbumExternalId.cs new file mode 100644 index 0000000000..014481da24 --- /dev/null +++ b/MediaBrowser.Providers/Plugins/AudioDb/AudioDbOtherAlbumExternalId.cs @@ -0,0 +1,27 @@ +#pragma warning disable CS1591 + +using MediaBrowser.Controller.Entities.Audio; +using MediaBrowser.Controller.Providers; +using MediaBrowser.Model.Entities; +using MediaBrowser.Model.Providers; + +namespace MediaBrowser.Providers.Plugins.AudioDb +{ + public class AudioDbOtherAlbumExternalId : IExternalId + { + /// <inheritdoc /> + public string ProviderName => "TheAudioDb"; + + /// <inheritdoc /> + public string Key => MetadataProvider.AudioDbAlbum.ToString(); + + /// <inheritdoc /> + public ExternalIdMediaType? Type => ExternalIdMediaType.Album; + + /// <inheritdoc /> + public string UrlFormatString => "https://www.theaudiodb.com/album/{0}"; + + /// <inheritdoc /> + public bool Supports(IHasProviderIds item) => item is Audio; + } +} diff --git a/MediaBrowser.Providers/Plugins/AudioDb/AudioDbOtherArtistExternalId.cs b/MediaBrowser.Providers/Plugins/AudioDb/AudioDbOtherArtistExternalId.cs new file mode 100644 index 0000000000..7875391043 --- /dev/null +++ b/MediaBrowser.Providers/Plugins/AudioDb/AudioDbOtherArtistExternalId.cs @@ -0,0 +1,27 @@ +#pragma warning disable CS1591 + +using MediaBrowser.Controller.Entities.Audio; +using MediaBrowser.Controller.Providers; +using MediaBrowser.Model.Entities; +using MediaBrowser.Model.Providers; + +namespace MediaBrowser.Providers.Plugins.AudioDb +{ + public class AudioDbOtherArtistExternalId : IExternalId + { + /// <inheritdoc /> + public string ProviderName => "TheAudioDb"; + + /// <inheritdoc /> + public string Key => MetadataProvider.AudioDbArtist.ToString(); + + /// <inheritdoc /> + public ExternalIdMediaType? Type => ExternalIdMediaType.OtherArtist; + + /// <inheritdoc /> + public string UrlFormatString => "https://www.theaudiodb.com/artist/{0}"; + + /// <inheritdoc /> + public bool Supports(IHasProviderIds item) => item is Audio || item is MusicAlbum; + } +} diff --git a/MediaBrowser.Providers/Plugins/AudioDb/Configuration/PluginConfiguration.cs b/MediaBrowser.Providers/Plugins/AudioDb/Configuration/PluginConfiguration.cs index ad3c7eb4bd..9657a290fc 100644 --- a/MediaBrowser.Providers/Plugins/AudioDb/Configuration/PluginConfiguration.cs +++ b/MediaBrowser.Providers/Plugins/AudioDb/Configuration/PluginConfiguration.cs @@ -1,4 +1,6 @@ -using MediaBrowser.Model.Plugins; +#pragma warning disable CS1591 + +using MediaBrowser.Model.Plugins; namespace MediaBrowser.Providers.Plugins.AudioDb { diff --git a/MediaBrowser.Providers/Plugins/AudioDb/Configuration/config.html b/MediaBrowser.Providers/Plugins/AudioDb/Configuration/config.html index 34494644d4..82f26a8f26 100644 --- a/MediaBrowser.Providers/Plugins/AudioDb/Configuration/config.html +++ b/MediaBrowser.Providers/Plugins/AudioDb/Configuration/config.html @@ -28,29 +28,31 @@ pluginId: "a629c0da-fac5-4c7e-931a-7174223f14c8" }; - $('.configPage').on('pageshow', function () { - Dashboard.showLoadingMsg(); - ApiClient.getPluginConfiguration(PluginConfig.pluginId).then(function (config) { - $('#enable').checked(config.Enable); - $('#replaceAlbumName').checked(config.ReplaceAlbumName); - - Dashboard.hideLoadingMsg(); + document.querySelector('.configPage') + .addEventListener('pageshow', function () { + Dashboard.showLoadingMsg(); + ApiClient.getPluginConfiguration(PluginConfig.pluginId).then(function (config) { + document.querySelector('#enable').checked = config.Enable; + document.querySelector('#replaceAlbumName').checked = config.ReplaceAlbumName; + + Dashboard.hideLoadingMsg(); + }); }); - }); - - $('.configForm').on('submit', function (e) { - Dashboard.showLoadingMsg(); - var form = this; - ApiClient.getPluginConfiguration(PluginConfig.pluginId).then(function (config) { - config.Enable = $('#enable', form).checked(); - config.ReplaceAlbumName = $('#replaceAlbumName', form).checked(); - - ApiClient.updatePluginConfiguration(PluginConfig.pluginId, config).then(Dashboard.processPluginConfigurationUpdateResult); + document.querySelector('.configForm') + .addEventListener('submit', function (e) { + Dashboard.showLoadingMsg(); + + ApiClient.getPluginConfiguration(PluginConfig.pluginId).then(function (config) { + config.Enable = document.querySelector('#enable').checked; + config.ReplaceAlbumName = document.querySelector('#replaceAlbumName').checked; + + ApiClient.updatePluginConfiguration(PluginConfig.pluginId, config).then(Dashboard.processPluginConfigurationUpdateResult); + }); + + e.preventDefault(); + return false; }); - - return false; - }); </script> </div> </body> diff --git a/MediaBrowser.Providers/Plugins/AudioDb/ExternalIds.cs b/MediaBrowser.Providers/Plugins/AudioDb/ExternalIds.cs deleted file mode 100644 index 2d8cb431ca..0000000000 --- a/MediaBrowser.Providers/Plugins/AudioDb/ExternalIds.cs +++ /dev/null @@ -1,66 +0,0 @@ -using MediaBrowser.Controller.Entities.Audio; -using MediaBrowser.Controller.Providers; -using MediaBrowser.Model.Entities; - -namespace MediaBrowser.Providers.Plugins.AudioDb -{ - public class AudioDbAlbumExternalId : IExternalId - { - /// <inheritdoc /> - public string Name => "TheAudioDb"; - - /// <inheritdoc /> - public string Key => MetadataProviders.AudioDbAlbum.ToString(); - - /// <inheritdoc /> - public string UrlFormatString => "https://www.theaudiodb.com/album/{0}"; - - /// <inheritdoc /> - public bool Supports(IHasProviderIds item) => item is MusicAlbum; - } - - public class AudioDbOtherAlbumExternalId : IExternalId - { - /// <inheritdoc /> - public string Name => "TheAudioDb Album"; - - /// <inheritdoc /> - public string Key => MetadataProviders.AudioDbAlbum.ToString(); - - /// <inheritdoc /> - public string UrlFormatString => "https://www.theaudiodb.com/album/{0}"; - - /// <inheritdoc /> - public bool Supports(IHasProviderIds item) => item is Audio; - } - - public class AudioDbArtistExternalId : IExternalId - { - /// <inheritdoc /> - public string Name => "TheAudioDb"; - - /// <inheritdoc /> - public string Key => MetadataProviders.AudioDbArtist.ToString(); - - /// <inheritdoc /> - public string UrlFormatString => "https://www.theaudiodb.com/artist/{0}"; - - /// <inheritdoc /> - public bool Supports(IHasProviderIds item) => item is MusicArtist; - } - - public class AudioDbOtherArtistExternalId : IExternalId - { - /// <inheritdoc /> - public string Name => "TheAudioDb Artist"; - - /// <inheritdoc /> - public string Key => MetadataProviders.AudioDbArtist.ToString(); - - /// <inheritdoc /> - public string UrlFormatString => "https://www.theaudiodb.com/artist/{0}"; - - /// <inheritdoc /> - public bool Supports(IHasProviderIds item) => item is Audio || item is MusicAlbum; - } -} diff --git a/MediaBrowser.Providers/Plugins/AudioDb/Plugin.cs b/MediaBrowser.Providers/Plugins/AudioDb/Plugin.cs index 8532c4df3a..b5bd72ff0d 100644 --- a/MediaBrowser.Providers/Plugins/AudioDb/Plugin.cs +++ b/MediaBrowser.Providers/Plugins/AudioDb/Plugin.cs @@ -1,4 +1,6 @@ -using System; +#pragma warning disable CS1591 + +using System; using System.Collections.Generic; using MediaBrowser.Common.Configuration; using MediaBrowser.Common.Plugins; @@ -9,6 +11,12 @@ namespace MediaBrowser.Providers.Plugins.AudioDb { public class Plugin : BasePlugin<PluginConfiguration>, IHasWebPages { + public Plugin(IApplicationPaths applicationPaths, IXmlSerializer xmlSerializer) + : base(applicationPaths, xmlSerializer) + { + Instance = this; + } + public static Plugin Instance { get; private set; } public override Guid Id => new Guid("a629c0da-fac5-4c7e-931a-7174223f14c8"); @@ -17,11 +25,8 @@ namespace MediaBrowser.Providers.Plugins.AudioDb public override string Description => "Get artist and album metadata or images from AudioDB."; - public Plugin(IApplicationPaths applicationPaths, IXmlSerializer xmlSerializer) - : base(applicationPaths, xmlSerializer) - { - Instance = this; - } + // TODO remove when plugin removed from server. + public override string ConfigurationFileName => "Jellyfin.Plugin.AudioDb.xml"; public IEnumerable<PluginPageInfo> GetPages() { diff --git a/MediaBrowser.Providers/Plugins/MusicBrainz/ArtistProvider.cs b/MediaBrowser.Providers/Plugins/MusicBrainz/ArtistProvider.cs index 260a3b6e7b..781b716406 100644 --- a/MediaBrowser.Providers/Plugins/MusicBrainz/ArtistProvider.cs +++ b/MediaBrowser.Providers/Plugins/MusicBrainz/ArtistProvider.cs @@ -1,14 +1,16 @@ +#pragma warning disable CS1591 + using System; using System.Collections.Generic; using System.Globalization; using System.IO; using System.Linq; using System.Net; +using System.Net.Http; using System.Text; using System.Threading; using System.Threading.Tasks; using System.Xml; -using MediaBrowser.Common.Net; using MediaBrowser.Controller.Entities.Audio; using MediaBrowser.Controller.Extensions; using MediaBrowser.Controller.Providers; @@ -35,21 +37,19 @@ namespace MediaBrowser.Providers.Music { var url = "/ws/2/artist/?query=arid:{0}" + musicBrainzId.ToString(CultureInfo.InvariantCulture); - using (var response = await MusicBrainzAlbumProvider.Current.GetMusicBrainzResponse(url, cancellationToken).ConfigureAwait(false)) - using (var stream = response.Content) - { - return GetResultsFromResponse(stream); - } + using var response = await MusicBrainzAlbumProvider.Current.GetMusicBrainzResponse(url, cancellationToken).ConfigureAwait(false); + await using var stream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false); + return GetResultsFromResponse(stream); } else { // They seem to throw bad request failures on any term with a slash var nameToSearch = searchInfo.Name.Replace('/', ' '); - var url = string.Format("/ws/2/artist/?query=\"{0}\"&dismax=true", UrlEncode(nameToSearch)); + var url = string.Format(CultureInfo.InvariantCulture, "/ws/2/artist/?query=\"{0}\"&dismax=true", UrlEncode(nameToSearch)); using (var response = await MusicBrainzAlbumProvider.Current.GetMusicBrainzResponse(url, cancellationToken).ConfigureAwait(false)) - using (var stream = response.Content) + await using (var stream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false)) { var results = GetResultsFromResponse(stream).ToList(); @@ -62,15 +62,11 @@ namespace MediaBrowser.Providers.Music if (HasDiacritics(searchInfo.Name)) { // Try again using the search with accent characters url - url = string.Format("/ws/2/artist/?query=artistaccent:\"{0}\"", UrlEncode(nameToSearch)); + url = string.Format(CultureInfo.InvariantCulture, "/ws/2/artist/?query=artistaccent:\"{0}\"", UrlEncode(nameToSearch)); - using (var response = await MusicBrainzAlbumProvider.Current.GetMusicBrainzResponse(url, cancellationToken).ConfigureAwait(false)) - { - using (var stream = response.Content) - { - return GetResultsFromResponse(stream); - } - } + using var response = await MusicBrainzAlbumProvider.Current.GetMusicBrainzResponse(url, cancellationToken).ConfigureAwait(false); + await using var stream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false); + return GetResultsFromResponse(stream); } } @@ -108,11 +104,13 @@ namespace MediaBrowser.Providers.Music reader.Read(); continue; } + using (var subReader = reader.ReadSubtree()) { return ParseArtistList(subReader).ToList(); } } + default: { reader.Skip(); @@ -150,6 +148,7 @@ namespace MediaBrowser.Providers.Music reader.Read(); continue; } + var mbzId = reader.GetAttribute("id"); using (var subReader = reader.ReadSubtree()) @@ -160,8 +159,10 @@ namespace MediaBrowser.Providers.Music yield return artist; } } + break; } + default: { reader.Skip(); @@ -202,6 +203,7 @@ namespace MediaBrowser.Providers.Music result.Overview = reader.ReadElementContentAsString(); break; } + default: { // there is sort-name if ever needed @@ -216,7 +218,7 @@ namespace MediaBrowser.Providers.Music } } - result.SetProviderId(MetadataProviders.MusicBrainzArtist, artistId); + result.SetProviderId(MetadataProvider.MusicBrainzArtist, artistId); if (string.IsNullOrWhiteSpace(artistId) || string.IsNullOrWhiteSpace(result.Name)) { @@ -249,7 +251,7 @@ namespace MediaBrowser.Providers.Music if (singleResult != null) { - musicBrainzId = singleResult.GetProviderId(MetadataProviders.MusicBrainzArtist); + musicBrainzId = singleResult.GetProviderId(MetadataProvider.MusicBrainzArtist); result.Item.Overview = singleResult.Overview; if (Plugin.Instance.Configuration.ReplaceArtistName) @@ -262,7 +264,7 @@ namespace MediaBrowser.Providers.Music if (!string.IsNullOrWhiteSpace(musicBrainzId)) { result.HasMetadata = true; - result.Item.SetProviderId(MetadataProviders.MusicBrainzArtist, musicBrainzId); + result.Item.SetProviderId(MetadataProvider.MusicBrainzArtist, musicBrainzId); } return result; @@ -290,7 +292,7 @@ namespace MediaBrowser.Providers.Music public string Name => "MusicBrainz"; - public Task<HttpResponseInfo> GetImageResponse(string url, CancellationToken cancellationToken) + public Task<HttpResponseMessage> GetImageResponse(string url, CancellationToken cancellationToken) { throw new NotImplementedException(); } diff --git a/MediaBrowser.Providers/Plugins/MusicBrainz/Configuration/PluginConfiguration.cs b/MediaBrowser.Providers/Plugins/MusicBrainz/Configuration/PluginConfiguration.cs index 5843b0c7d9..980da9a010 100644 --- a/MediaBrowser.Providers/Plugins/MusicBrainz/Configuration/PluginConfiguration.cs +++ b/MediaBrowser.Providers/Plugins/MusicBrainz/Configuration/PluginConfiguration.cs @@ -1,4 +1,6 @@ -using MediaBrowser.Model.Plugins; +#pragma warning disable CS1591 + +using MediaBrowser.Model.Plugins; namespace MediaBrowser.Providers.Plugins.MusicBrainz { diff --git a/MediaBrowser.Providers/Plugins/MusicBrainz/Configuration/config.html b/MediaBrowser.Providers/Plugins/MusicBrainz/Configuration/config.html index 1f02461da2..1945e6cb49 100644 --- a/MediaBrowser.Providers/Plugins/MusicBrainz/Configuration/config.html +++ b/MediaBrowser.Providers/Plugins/MusicBrainz/Configuration/config.html @@ -36,33 +36,47 @@ uniquePluginId: "8c95c4d2-e50c-4fb0-a4f3-6c06ff0f9a1a" }; - $('.musicBrainzConfigPage').on('pageshow', function () { - Dashboard.showLoadingMsg(); - ApiClient.getPluginConfiguration(MusicBrainzPluginConfig.uniquePluginId).then(function (config) { - $('#server').val(config.Server).change(); - $('#rateLimit').val(config.RateLimit).change(); - $('#enable').checked(config.Enable); - $('#replaceArtistName').checked(config.ReplaceArtistName); + document.querySelector('.musicBrainzConfigPage') + .addEventListener('pageshow', function () { + Dashboard.showLoadingMsg(); + ApiClient.getPluginConfiguration(MusicBrainzPluginConfig.uniquePluginId).then(function (config) { + var server = document.querySelector('#server'); + server.value = config.Server; + server.dispatchEvent(new Event('change', { + bubbles: true, + cancelable: false + })); + + var rateLimit = document.querySelector('#rateLimit'); + rateLimit.value = config.RateLimit; + rateLimit.dispatchEvent(new Event('change', { + bubbles: true, + cancelable: false + })); + + document.querySelector('#enable').checked = config.Enable; + document.querySelector('#replaceArtistName').checked = config.ReplaceArtistName; - Dashboard.hideLoadingMsg(); + Dashboard.hideLoadingMsg(); + }); }); - }); - - $('.musicBrainzConfigForm').on('submit', function (e) { - Dashboard.showLoadingMsg(); - - var form = this; - ApiClient.getPluginConfiguration(MusicBrainzPluginConfig.uniquePluginId).then(function (config) { - config.Server = $('#server', form).val(); - config.RateLimit = $('#rateLimit', form).val(); - config.Enable = $('#enable', form).checked(); - config.ReplaceArtistName = $('#replaceArtistName', form).checked(); - - ApiClient.updatePluginConfiguration(MusicBrainzPluginConfig.uniquePluginId, config).then(Dashboard.processPluginConfigurationUpdateResult); + + document.querySelector('.musicBrainzConfigForm') + .addEventListener('submit', function (e) { + Dashboard.showLoadingMsg(); + + ApiClient.getPluginConfiguration(MusicBrainzPluginConfig.uniquePluginId).then(function (config) { + config.Server = document.querySelector('#server').value; + config.RateLimit = document.querySelector('#rateLimit').value; + config.Enable = document.querySelector('#enable').checked; + config.ReplaceArtistName = document.querySelector('#replaceArtistName').checked; + + ApiClient.updatePluginConfiguration(MusicBrainzPluginConfig.uniquePluginId, config).then(Dashboard.processPluginConfigurationUpdateResult); + }); + + e.preventDefault(); + return false; }); - - return false; - }); </script> </div> </body> diff --git a/MediaBrowser.Providers/Plugins/MusicBrainz/ExternalIds.cs b/MediaBrowser.Providers/Plugins/MusicBrainz/ExternalIds.cs index 03565a34c4..5600c389c0 100644 --- a/MediaBrowser.Providers/Plugins/MusicBrainz/ExternalIds.cs +++ b/MediaBrowser.Providers/Plugins/MusicBrainz/ExternalIds.cs @@ -1,6 +1,9 @@ +#pragma warning disable CS1591 + using MediaBrowser.Controller.Entities.Audio; using MediaBrowser.Controller.Providers; using MediaBrowser.Model.Entities; +using MediaBrowser.Model.Providers; using MediaBrowser.Providers.Plugins.MusicBrainz; namespace MediaBrowser.Providers.Music @@ -8,10 +11,13 @@ namespace MediaBrowser.Providers.Music public class MusicBrainzReleaseGroupExternalId : IExternalId { /// <inheritdoc /> - public string Name => "MusicBrainz Release Group"; + public string ProviderName => "MusicBrainz"; + + /// <inheritdoc /> + public string Key => MetadataProvider.MusicBrainzReleaseGroup.ToString(); /// <inheritdoc /> - public string Key => MetadataProviders.MusicBrainzReleaseGroup.ToString(); + public ExternalIdMediaType? Type => ExternalIdMediaType.ReleaseGroup; /// <inheritdoc /> public string UrlFormatString => Plugin.Instance.Configuration.Server + "/release-group/{0}"; @@ -23,10 +29,13 @@ namespace MediaBrowser.Providers.Music public class MusicBrainzAlbumArtistExternalId : IExternalId { /// <inheritdoc /> - public string Name => "MusicBrainz Album Artist"; + public string ProviderName => "MusicBrainz"; /// <inheritdoc /> - public string Key => MetadataProviders.MusicBrainzAlbumArtist.ToString(); + public string Key => MetadataProvider.MusicBrainzAlbumArtist.ToString(); + + /// <inheritdoc /> + public ExternalIdMediaType? Type => ExternalIdMediaType.AlbumArtist; /// <inheritdoc /> public string UrlFormatString => Plugin.Instance.Configuration.Server + "/artist/{0}"; @@ -38,10 +47,13 @@ namespace MediaBrowser.Providers.Music public class MusicBrainzAlbumExternalId : IExternalId { /// <inheritdoc /> - public string Name => "MusicBrainz Album"; + public string ProviderName => "MusicBrainz"; + + /// <inheritdoc /> + public string Key => MetadataProvider.MusicBrainzAlbum.ToString(); /// <inheritdoc /> - public string Key => MetadataProviders.MusicBrainzAlbum.ToString(); + public ExternalIdMediaType? Type => ExternalIdMediaType.Album; /// <inheritdoc /> public string UrlFormatString => Plugin.Instance.Configuration.Server + "/release/{0}"; @@ -53,10 +65,13 @@ namespace MediaBrowser.Providers.Music public class MusicBrainzArtistExternalId : IExternalId { /// <inheritdoc /> - public string Name => "MusicBrainz"; + public string ProviderName => "MusicBrainz"; /// <inheritdoc /> - public string Key => MetadataProviders.MusicBrainzArtist.ToString(); + public string Key => MetadataProvider.MusicBrainzArtist.ToString(); + + /// <inheritdoc /> + public ExternalIdMediaType? Type => ExternalIdMediaType.Artist; /// <inheritdoc /> public string UrlFormatString => Plugin.Instance.Configuration.Server + "/artist/{0}"; @@ -68,11 +83,14 @@ namespace MediaBrowser.Providers.Music public class MusicBrainzOtherArtistExternalId : IExternalId { /// <inheritdoc /> - public string Name => "MusicBrainz Artist"; + public string ProviderName => "MusicBrainz"; /// <inheritdoc /> - public string Key => MetadataProviders.MusicBrainzArtist.ToString(); + public string Key => MetadataProvider.MusicBrainzArtist.ToString(); + + /// <inheritdoc /> + public ExternalIdMediaType? Type => ExternalIdMediaType.OtherArtist; /// <inheritdoc /> public string UrlFormatString => Plugin.Instance.Configuration.Server + "/artist/{0}"; @@ -84,10 +102,13 @@ namespace MediaBrowser.Providers.Music public class MusicBrainzTrackId : IExternalId { /// <inheritdoc /> - public string Name => "MusicBrainz Track"; + public string ProviderName => "MusicBrainz"; + + /// <inheritdoc /> + public string Key => MetadataProvider.MusicBrainzTrack.ToString(); /// <inheritdoc /> - public string Key => MetadataProviders.MusicBrainzTrack.ToString(); + public ExternalIdMediaType? Type => ExternalIdMediaType.Track; /// <inheritdoc /> public string UrlFormatString => Plugin.Instance.Configuration.Server + "/track/{0}"; diff --git a/MediaBrowser.Providers/Plugins/MusicBrainz/AlbumProvider.cs b/MediaBrowser.Providers/Plugins/MusicBrainz/MusicBrainzAlbumProvider.cs index 31cdaf616a..46f8988f27 100644 --- a/MediaBrowser.Providers/Plugins/MusicBrainz/AlbumProvider.cs +++ b/MediaBrowser.Providers/Plugins/MusicBrainz/MusicBrainzAlbumProvider.cs @@ -1,3 +1,5 @@ +#pragma warning disable CS1591 + using System; using System.Collections.Generic; using System.Diagnostics; @@ -24,36 +26,34 @@ namespace MediaBrowser.Providers.Music public class MusicBrainzAlbumProvider : IRemoteMetadataProvider<MusicAlbum, AlbumInfo>, IHasOrder { /// <summary> - /// The Jellyfin user-agent is unrestricted but source IP must not exceed - /// one request per second, therefore we rate limit to avoid throttling. - /// Be prudent, use a value slightly above the minimun required. - /// https://musicbrainz.org/doc/XML_Web_Service/Rate_Limiting - /// </summary> - private readonly long _musicBrainzQueryIntervalMs; - - /// <summary> /// For each single MB lookup/search, this is the maximum number of /// attempts that shall be made whilst receiving a 503 Server /// Unavailable (indicating throttled) response. /// </summary> private const uint MusicBrainzQueryAttempts = 5u; - internal static MusicBrainzAlbumProvider Current; + /// <summary> + /// The Jellyfin user-agent is unrestricted but source IP must not exceed + /// one request per second, therefore we rate limit to avoid throttling. + /// Be prudent, use a value slightly above the minimun required. + /// https://musicbrainz.org/doc/XML_Web_Service/Rate_Limiting + /// </summary> + private readonly long _musicBrainzQueryIntervalMs; - private readonly IHttpClient _httpClient; + private readonly IHttpClientFactory _httpClientFactory; private readonly IApplicationHost _appHost; - private readonly ILogger _logger; + private readonly ILogger<MusicBrainzAlbumProvider> _logger; private readonly string _musicBrainzBaseUrl; private Stopwatch _stopWatchMusicBrainz = new Stopwatch(); public MusicBrainzAlbumProvider( - IHttpClient httpClient, + IHttpClientFactory httpClientFactory, IApplicationHost appHost, ILogger<MusicBrainzAlbumProvider> logger) { - _httpClient = httpClient; + _httpClientFactory = httpClientFactory; _appHost = appHost; _logger = logger; @@ -66,6 +66,8 @@ namespace MediaBrowser.Providers.Music Current = this; } + internal static MusicBrainzAlbumProvider Current { get; private set; } + /// <inheritdoc /> public string Name => "MusicBrainz"; @@ -109,7 +111,7 @@ namespace MediaBrowser.Providers.Music else { // I'm sure there is a better way but for now it resolves search for 12" Mixes - var queryName = searchInfo.Name.Replace("\"", string.Empty); + var queryName = searchInfo.Name.Replace("\"", string.Empty, StringComparison.Ordinal); url = string.Format( CultureInfo.InvariantCulture, @@ -121,11 +123,9 @@ namespace MediaBrowser.Providers.Music if (!string.IsNullOrWhiteSpace(url)) { - using (var response = await GetMusicBrainzResponse(url, cancellationToken).ConfigureAwait(false)) - using (var stream = response.Content) - { - return GetResultsFromResponse(stream); - } + using var response = await GetMusicBrainzResponse(url, cancellationToken).ConfigureAwait(false); + await using var stream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false); + return GetResultsFromResponse(stream); } return Enumerable.Empty<RemoteSearchResult>(); @@ -163,17 +163,17 @@ namespace MediaBrowser.Providers.Music Name = i.Artists[0].Item1 }; - result.AlbumArtist.SetProviderId(MetadataProviders.MusicBrainzArtist, i.Artists[0].Item2); + result.AlbumArtist.SetProviderId(MetadataProvider.MusicBrainzArtist, i.Artists[0].Item2); } if (!string.IsNullOrWhiteSpace(i.ReleaseId)) { - result.SetProviderId(MetadataProviders.MusicBrainzAlbum, i.ReleaseId); + result.SetProviderId(MetadataProvider.MusicBrainzAlbum, i.ReleaseId); } if (!string.IsNullOrWhiteSpace(i.ReleaseGroupId)) { - result.SetProviderId(MetadataProviders.MusicBrainzReleaseGroup, i.ReleaseGroupId); + result.SetProviderId(MetadataProvider.MusicBrainzReleaseGroup, i.ReleaseGroupId); } return result; @@ -247,12 +247,12 @@ namespace MediaBrowser.Providers.Music { if (!string.IsNullOrEmpty(releaseId)) { - result.Item.SetProviderId(MetadataProviders.MusicBrainzAlbum, releaseId); + result.Item.SetProviderId(MetadataProvider.MusicBrainzAlbum, releaseId); } if (!string.IsNullOrEmpty(releaseGroupId)) { - result.Item.SetProviderId(MetadataProviders.MusicBrainzReleaseGroup, releaseGroupId); + result.Item.SetProviderId(MetadataProvider.MusicBrainzReleaseGroup, releaseGroupId); } } @@ -276,27 +276,25 @@ namespace MediaBrowser.Providers.Music private async Task<ReleaseResult> GetReleaseResult(string albumName, string artistId, CancellationToken cancellationToken) { - var url = string.Format("/ws/2/release/?query=\"{0}\" AND arid:{1}", + var url = string.Format( + CultureInfo.InvariantCulture, + "/ws/2/release/?query=\"{0}\" AND arid:{1}", WebUtility.UrlEncode(albumName), artistId); - using (var response = await GetMusicBrainzResponse(url, cancellationToken).ConfigureAwait(false)) - using (var stream = response.Content) - using (var oReader = new StreamReader(stream, Encoding.UTF8)) + using var response = await GetMusicBrainzResponse(url, cancellationToken).ConfigureAwait(false); + await using var stream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false); + using var oReader = new StreamReader(stream, Encoding.UTF8); + var settings = new XmlReaderSettings { - var settings = new XmlReaderSettings() - { - ValidationType = ValidationType.None, - CheckCharacters = false, - IgnoreProcessingInstructions = true, - IgnoreComments = true - }; + ValidationType = ValidationType.None, + CheckCharacters = false, + IgnoreProcessingInstructions = true, + IgnoreComments = true + }; - using (var reader = XmlReader.Create(oReader, settings)) - { - return ReleaseResult.Parse(reader).FirstOrDefault(); - } - } + using var reader = XmlReader.Create(oReader, settings); + return ReleaseResult.Parse(reader).FirstOrDefault(); } private async Task<ReleaseResult> GetReleaseResultByArtistName(string albumName, string artistName, CancellationToken cancellationToken) @@ -307,23 +305,19 @@ namespace MediaBrowser.Providers.Music WebUtility.UrlEncode(albumName), WebUtility.UrlEncode(artistName)); - using (var response = await GetMusicBrainzResponse(url, cancellationToken).ConfigureAwait(false)) - using (var stream = response.Content) - using (var oReader = new StreamReader(stream, Encoding.UTF8)) + using var response = await GetMusicBrainzResponse(url, cancellationToken).ConfigureAwait(false); + await using var stream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false); + using var oReader = new StreamReader(stream, Encoding.UTF8); + var settings = new XmlReaderSettings() { - var settings = new XmlReaderSettings() - { - ValidationType = ValidationType.None, - CheckCharacters = false, - IgnoreProcessingInstructions = true, - IgnoreComments = true - }; + ValidationType = ValidationType.None, + CheckCharacters = false, + IgnoreProcessingInstructions = true, + IgnoreComments = true + }; - using (var reader = XmlReader.Create(oReader, settings)) - { - return ReleaseResult.Parse(reader).FirstOrDefault(); - } - } + using var reader = XmlReader.Create(oReader, settings); + return ReleaseResult.Parse(reader).FirstOrDefault(); } private class ReleaseResult @@ -361,6 +355,7 @@ namespace MediaBrowser.Providers.Music return ParseReleaseList(subReader).ToList(); } } + default: { reader.Skip(); @@ -396,6 +391,7 @@ namespace MediaBrowser.Providers.Music reader.Read(); continue; } + var releaseId = reader.GetAttribute("id"); using (var subReader = reader.ReadSubtree()) @@ -406,8 +402,10 @@ namespace MediaBrowser.Providers.Music yield return release; } } + break; } + default: { reader.Skip(); @@ -453,6 +451,7 @@ namespace MediaBrowser.Providers.Music { result.Year = date.Year; } + break; } case "annotation": @@ -480,6 +479,7 @@ namespace MediaBrowser.Providers.Music break; } + default: { reader.Skip(); @@ -497,7 +497,7 @@ namespace MediaBrowser.Providers.Music } } - private static ValueTuple<string, string> ParseArtistCredit(XmlReader reader) + private static (string, string) ParseArtistCredit(XmlReader reader) { reader.MoveToContent(); reader.Read(); @@ -518,6 +518,7 @@ namespace MediaBrowser.Providers.Music return ParseArtistNameCredit(subReader); } } + default: { reader.Skip(); @@ -531,7 +532,7 @@ namespace MediaBrowser.Providers.Music } } - return new ValueTuple<string, string>(); + return default; } private static (string, string) ParseArtistNameCredit(XmlReader reader) @@ -556,6 +557,7 @@ namespace MediaBrowser.Providers.Music return ParseArtistArtistCredit(subReader, id); } } + default: { reader.Skip(); @@ -593,6 +595,7 @@ namespace MediaBrowser.Providers.Music name = reader.ReadElementContentAsString(); break; } + default: { reader.Skip(); @@ -613,30 +616,21 @@ namespace MediaBrowser.Providers.Music { var url = "/ws/2/release?release-group=" + releaseGroupId.ToString(CultureInfo.InvariantCulture); - using (var response = await GetMusicBrainzResponse(url, cancellationToken).ConfigureAwait(false)) - using (var stream = response.Content) - using (var oReader = new StreamReader(stream, Encoding.UTF8)) + using var response = await GetMusicBrainzResponse(url, cancellationToken).ConfigureAwait(false); + await using var stream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false); + using var oReader = new StreamReader(stream, Encoding.UTF8); + var settings = new XmlReaderSettings { - var settings = new XmlReaderSettings() - { - ValidationType = ValidationType.None, - CheckCharacters = false, - IgnoreProcessingInstructions = true, - IgnoreComments = true - }; - - using (var reader = XmlReader.Create(oReader, settings)) - { - var result = ReleaseResult.Parse(reader).FirstOrDefault(); + ValidationType = ValidationType.None, + CheckCharacters = false, + IgnoreProcessingInstructions = true, + IgnoreComments = true + }; - if (result != null) - { - return result.ReleaseId; - } - } - } + using var reader = XmlReader.Create(oReader, settings); + var result = ReleaseResult.Parse(reader).FirstOrDefault(); - return null; + return result?.ReleaseId; } /// <summary> @@ -649,57 +643,57 @@ namespace MediaBrowser.Providers.Music { var url = "/ws/2/release-group/?query=reid:" + releaseEntryId.ToString(CultureInfo.InvariantCulture); - using (var response = await GetMusicBrainzResponse(url, cancellationToken).ConfigureAwait(false)) - using (var stream = response.Content) - using (var oReader = new StreamReader(stream, Encoding.UTF8)) + using var response = await GetMusicBrainzResponse(url, cancellationToken).ConfigureAwait(false); + await using var stream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false); + using var oReader = new StreamReader(stream, Encoding.UTF8); + var settings = new XmlReaderSettings { - var settings = new XmlReaderSettings() - { - ValidationType = ValidationType.None, - CheckCharacters = false, - IgnoreProcessingInstructions = true, - IgnoreComments = true - }; + ValidationType = ValidationType.None, + CheckCharacters = false, + IgnoreProcessingInstructions = true, + IgnoreComments = true + }; - using (var reader = XmlReader.Create(oReader, settings)) - { - reader.MoveToContent(); - reader.Read(); + using (var reader = XmlReader.Create(oReader, settings)) + { + reader.MoveToContent(); + reader.Read(); - // Loop through each element - while (!reader.EOF && reader.ReadState == ReadState.Interactive) + // Loop through each element + while (!reader.EOF && reader.ReadState == ReadState.Interactive) + { + if (reader.NodeType == XmlNodeType.Element) { - if (reader.NodeType == XmlNodeType.Element) + switch (reader.Name) { - switch (reader.Name) + case "release-group-list": { - case "release-group-list": - { - if (reader.IsEmptyElement) - { - reader.Read(); - continue; - } - using (var subReader = reader.ReadSubtree()) - { - return GetFirstReleaseGroupId(subReader); - } - } - default: - { - reader.Skip(); - break; - } + if (reader.IsEmptyElement) + { + reader.Read(); + continue; + } + + using (var subReader = reader.ReadSubtree()) + { + return GetFirstReleaseGroupId(subReader); + } + } + + default: + { + reader.Skip(); + break; } - } - else - { - reader.Read(); } } - - return null; + else + { + reader.Read(); + } } + + return null; } } @@ -719,6 +713,7 @@ namespace MediaBrowser.Providers.Music { return reader.GetAttribute("id"); } + default: { reader.Skip(); @@ -741,23 +736,19 @@ namespace MediaBrowser.Providers.Music /// A number of retries shall be made in order to try and satisfy the request before /// giving up and returning null. /// </summary> - internal async Task<HttpResponseInfo> GetMusicBrainzResponse(string url, CancellationToken cancellationToken) + internal async Task<HttpResponseMessage> GetMusicBrainzResponse(string url, CancellationToken cancellationToken) { - var options = new HttpRequestOptions - { - Url = _musicBrainzBaseUrl.TrimEnd('/') + url, - CancellationToken = cancellationToken, - // MusicBrainz request a contact email address is supplied, as comment, in user agent field: - // https://musicbrainz.org/doc/XML_Web_Service/Rate_Limiting#User-Agent - UserAgent = string.Format( - CultureInfo.InvariantCulture, - "{0} ( {1} )", - _appHost.ApplicationUserAgent, - _appHost.ApplicationUserAgentAddress), - BufferContent = false - }; + using var options = new HttpRequestMessage(HttpMethod.Get, _musicBrainzBaseUrl.TrimEnd('/') + url); + + // MusicBrainz request a contact email address is supplied, as comment, in user agent field: + // https://musicbrainz.org/doc/XML_Web_Service/Rate_Limiting#User-Agent + options.Headers.UserAgent.ParseAdd(string.Format( + CultureInfo.InvariantCulture, + "{0} ( {1} )", + _appHost.ApplicationUserAgent, + _appHost.ApplicationUserAgentAddress)); - HttpResponseInfo response; + HttpResponseMessage response; var attempts = 0u; do @@ -776,7 +767,7 @@ namespace MediaBrowser.Providers.Music _logger.LogDebug("GetMusicBrainzResponse: Time since previous request: {0} ms", _stopWatchMusicBrainz.ElapsedMilliseconds); _stopWatchMusicBrainz.Restart(); - response = await _httpClient.SendAsync(options, HttpMethod.Get).ConfigureAwait(false); + response = await _httpClientFactory.CreateClient(NamedClient.Default).SendAsync(options).ConfigureAwait(false); // We retry a finite number of times, and only whilst MB is indicating 503 (throttling) } @@ -785,14 +776,14 @@ namespace MediaBrowser.Providers.Music // Log error if unable to query MB database due to throttling if (attempts == MusicBrainzQueryAttempts && response.StatusCode == HttpStatusCode.ServiceUnavailable) { - _logger.LogError("GetMusicBrainzResponse: 503 Service Unavailable (throttled) response received {0} times whilst requesting {1}", attempts, options.Url); + _logger.LogError("GetMusicBrainzResponse: 503 Service Unavailable (throttled) response received {0} times whilst requesting {1}", attempts, options.RequestUri); } return response; } /// <inheritdoc /> - public Task<HttpResponseInfo> GetImageResponse(string url, CancellationToken cancellationToken) + public Task<HttpResponseMessage> GetImageResponse(string url, CancellationToken cancellationToken) { throw new NotImplementedException(); } diff --git a/MediaBrowser.Providers/Plugins/MusicBrainz/Plugin.cs b/MediaBrowser.Providers/Plugins/MusicBrainz/Plugin.cs index 8e1b3ea376..90266e4409 100644 --- a/MediaBrowser.Providers/Plugins/MusicBrainz/Plugin.cs +++ b/MediaBrowser.Providers/Plugins/MusicBrainz/Plugin.cs @@ -1,4 +1,6 @@ -using System; +#pragma warning disable CS1591 + +using System; using System.Collections.Generic; using MediaBrowser.Common.Configuration; using MediaBrowser.Common.Plugins; @@ -21,6 +23,9 @@ namespace MediaBrowser.Providers.Plugins.MusicBrainz public const long DefaultRateLimit = 2000u; + // TODO remove when plugin removed from server. + public override string ConfigurationFileName => "Jellyfin.Plugin.MusicBrainz.xml"; + public Plugin(IApplicationPaths applicationPaths, IXmlSerializer xmlSerializer) : base(applicationPaths, xmlSerializer) { diff --git a/MediaBrowser.Providers/Plugins/Omdb/Configuration/PluginConfiguration.cs b/MediaBrowser.Providers/Plugins/Omdb/Configuration/PluginConfiguration.cs new file mode 100644 index 0000000000..196f14e7c8 --- /dev/null +++ b/MediaBrowser.Providers/Plugins/Omdb/Configuration/PluginConfiguration.cs @@ -0,0 +1,11 @@ +#pragma warning disable CS1591 + +using MediaBrowser.Model.Plugins; + +namespace MediaBrowser.Providers.Plugins.Omdb +{ + public class PluginConfiguration : BasePluginConfiguration + { + public bool CastAndCrew { get; set; } + } +} diff --git a/MediaBrowser.Providers/Plugins/Omdb/Configuration/config.html b/MediaBrowser.Providers/Plugins/Omdb/Configuration/config.html new file mode 100644 index 0000000000..f4375b3cb5 --- /dev/null +++ b/MediaBrowser.Providers/Plugins/Omdb/Configuration/config.html @@ -0,0 +1,52 @@ +<!DOCTYPE html> +<html> +<head> + <title>OMDb</title> +</head> +<body> + <div data-role="page" class="page type-interior pluginConfigurationPage configPage" data-require="emby-input,emby-button,emby-checkbox"> + <div data-role="content"> + <div class="content-primary"> + <form class="configForm"> + <label class="checkboxContainer"> + <input is="emby-checkbox" type="checkbox" id="castAndCrew" /> + <span>Collect information about the cast and other crew members from OMDb.</span> + </label> + <br /> + <div> + <button is="emby-button" type="submit" class="raised button-submit block"><span>Save</span></button> + </div> + </form> + </div> + </div> + <script type="text/javascript"> + var PluginConfig = { + pluginId: "a628c0da-fac5-4c7e-9d1a-7134223f14c8" + }; + + document.querySelector('.configPage') + .addEventListener('pageshow', function () { + Dashboard.showLoadingMsg(); + ApiClient.getPluginConfiguration(PluginConfig.pluginId).then(function (config) { + document.querySelector('#castAndCrew').checked = config.CastAndCrew; + Dashboard.hideLoadingMsg(); + }); + }); + + + document.querySelector('.configForm') + .addEventListener('submit', function (e) { + Dashboard.showLoadingMsg(); + + ApiClient.getPluginConfiguration(PluginConfig.pluginId).then(function (config) { + config.CastAndCrew = document.querySelector('#castAndCrew').checked; + ApiClient.updatePluginConfiguration(PluginConfig.pluginId, config).then(Dashboard.processPluginConfigurationUpdateResult); + }); + + e.preventDefault(); + return false; + }); + </script> + </div> +</body> +</html> diff --git a/MediaBrowser.Providers/Plugins/Omdb/OmdbEpisodeProvider.cs b/MediaBrowser.Providers/Plugins/Omdb/OmdbEpisodeProvider.cs index 37160dd2c0..bfc840ea59 100644 --- a/MediaBrowser.Providers/Plugins/Omdb/OmdbEpisodeProvider.cs +++ b/MediaBrowser.Providers/Plugins/Omdb/OmdbEpisodeProvider.cs @@ -1,8 +1,10 @@ +#pragma warning disable CS1591 + using System.Collections.Generic; +using System.Net.Http; using System.Threading; using System.Threading.Tasks; using MediaBrowser.Common; -using MediaBrowser.Common.Net; using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Entities.TV; using MediaBrowser.Controller.Library; @@ -11,31 +13,39 @@ using MediaBrowser.Model.Entities; using MediaBrowser.Model.IO; using MediaBrowser.Model.Providers; using MediaBrowser.Model.Serialization; -using Microsoft.Extensions.Logging; namespace MediaBrowser.Providers.Plugins.Omdb { - public class OmdbEpisodeProvider : - IRemoteMetadataProvider<Episode, EpisodeInfo>, - IHasOrder + public class OmdbEpisodeProvider : IRemoteMetadataProvider<Episode, EpisodeInfo>, IHasOrder { private readonly IJsonSerializer _jsonSerializer; - private readonly IHttpClient _httpClient; + private readonly IHttpClientFactory _httpClientFactory; private readonly OmdbItemProvider _itemProvider; private readonly IFileSystem _fileSystem; private readonly IServerConfigurationManager _configurationManager; private readonly IApplicationHost _appHost; - public OmdbEpisodeProvider(IJsonSerializer jsonSerializer, IApplicationHost appHost, IHttpClient httpClient, ILogger logger, ILibraryManager libraryManager, IFileSystem fileSystem, IServerConfigurationManager configurationManager) + public OmdbEpisodeProvider( + IJsonSerializer jsonSerializer, + IApplicationHost appHost, + IHttpClientFactory httpClientFactory, + ILibraryManager libraryManager, + IFileSystem fileSystem, + IServerConfigurationManager configurationManager) { _jsonSerializer = jsonSerializer; - _httpClient = httpClient; + _httpClientFactory = httpClientFactory; _fileSystem = fileSystem; _configurationManager = configurationManager; _appHost = appHost; - _itemProvider = new OmdbItemProvider(jsonSerializer, _appHost, httpClient, logger, libraryManager, fileSystem, configurationManager); + _itemProvider = new OmdbItemProvider(jsonSerializer, _appHost, httpClientFactory, libraryManager, fileSystem, configurationManager); } + // After TheTvDb + public int Order => 1; + + public string Name => "The Open Movie Database"; + public Task<IEnumerable<RemoteSearchResult>> GetSearchResults(EpisodeInfo searchInfo, CancellationToken cancellationToken) { return _itemProvider.GetSearchResults(searchInfo, "episode", cancellationToken); @@ -55,23 +65,19 @@ namespace MediaBrowser.Providers.Plugins.Omdb return result; } - if (info.SeriesProviderIds.TryGetValue(MetadataProviders.Imdb.ToString(), out string seriesImdbId) && !string.IsNullOrEmpty(seriesImdbId)) + if (info.SeriesProviderIds.TryGetValue(MetadataProvider.Imdb.ToString(), out string seriesImdbId) && !string.IsNullOrEmpty(seriesImdbId)) { if (info.IndexNumber.HasValue && info.ParentIndexNumber.HasValue) { - result.HasMetadata = await new OmdbProvider(_jsonSerializer, _httpClient, _fileSystem, _appHost, _configurationManager) - .FetchEpisodeData(result, info.IndexNumber.Value, info.ParentIndexNumber.Value, info.GetProviderId(MetadataProviders.Imdb), seriesImdbId, info.MetadataLanguage, info.MetadataCountryCode, cancellationToken).ConfigureAwait(false); + result.HasMetadata = await new OmdbProvider(_jsonSerializer, _httpClientFactory, _fileSystem, _appHost, _configurationManager) + .FetchEpisodeData(result, info.IndexNumber.Value, info.ParentIndexNumber.Value, info.GetProviderId(MetadataProvider.Imdb), seriesImdbId, info.MetadataLanguage, info.MetadataCountryCode, cancellationToken).ConfigureAwait(false); } } return result; } - // After TheTvDb - public int Order => 1; - - public string Name => "The Open Movie Database"; - public Task<HttpResponseInfo> GetImageResponse(string url, CancellationToken cancellationToken) + public Task<HttpResponseMessage> GetImageResponse(string url, CancellationToken cancellationToken) { return _itemProvider.GetImageResponse(url, cancellationToken); } diff --git a/MediaBrowser.Providers/Plugins/Omdb/OmdbImageProvider.cs b/MediaBrowser.Providers/Plugins/Omdb/OmdbImageProvider.cs index a450c2a6db..8f4240dc11 100644 --- a/MediaBrowser.Providers/Plugins/Omdb/OmdbImageProvider.cs +++ b/MediaBrowser.Providers/Plugins/Omdb/OmdbImageProvider.cs @@ -1,4 +1,8 @@ +#pragma warning disable CS1591 + using System.Collections.Generic; +using System.Net.Http; +using System.Globalization; using System.Threading; using System.Threading.Tasks; using MediaBrowser.Common; @@ -17,21 +21,27 @@ namespace MediaBrowser.Providers.Plugins.Omdb { public class OmdbImageProvider : IRemoteImageProvider, IHasOrder { - private readonly IHttpClient _httpClient; + private readonly IHttpClientFactory _httpClientFactory; private readonly IJsonSerializer _jsonSerializer; private readonly IFileSystem _fileSystem; private readonly IServerConfigurationManager _configurationManager; private readonly IApplicationHost _appHost; - public OmdbImageProvider(IJsonSerializer jsonSerializer, IApplicationHost appHost, IHttpClient httpClient, IFileSystem fileSystem, IServerConfigurationManager configurationManager) + public OmdbImageProvider(IJsonSerializer jsonSerializer, IApplicationHost appHost, IHttpClientFactory httpClientFactory, IFileSystem fileSystem, IServerConfigurationManager configurationManager) { _jsonSerializer = jsonSerializer; - _httpClient = httpClient; + _httpClientFactory = httpClientFactory; _fileSystem = fileSystem; _configurationManager = configurationManager; _appHost = appHost; } + public string Name => "The Open Movie Database"; + + // After other internet providers, because they're better + // But before fallback providers like screengrab + public int Order => 90; + public IEnumerable<ImageType> GetSupportedImages(BaseItem item) { return new List<ImageType> @@ -42,11 +52,11 @@ namespace MediaBrowser.Providers.Plugins.Omdb public async Task<IEnumerable<RemoteImageInfo>> GetImages(BaseItem item, CancellationToken cancellationToken) { - var imdbId = item.GetProviderId(MetadataProviders.Imdb); + var imdbId = item.GetProviderId(MetadataProvider.Imdb); var list = new List<RemoteImageInfo>(); - var provider = new OmdbProvider(_jsonSerializer, _httpClient, _fileSystem, _appHost, _configurationManager); + var provider = new OmdbProvider(_jsonSerializer, _httpClientFactory, _fileSystem, _appHost, _configurationManager); if (!string.IsNullOrWhiteSpace(imdbId)) { @@ -68,7 +78,7 @@ namespace MediaBrowser.Providers.Plugins.Omdb list.Add(new RemoteImageInfo { ProviderName = Name, - Url = string.Format("https://img.omdbapi.com/?i={0}&apikey=2c9d9507", imdbId) + Url = string.Format(CultureInfo.InvariantCulture, "https://img.omdbapi.com/?i={0}&apikey=2c9d9507", imdbId) }); } } @@ -77,23 +87,14 @@ namespace MediaBrowser.Providers.Plugins.Omdb return list; } - public Task<HttpResponseInfo> GetImageResponse(string url, CancellationToken cancellationToken) + public Task<HttpResponseMessage> GetImageResponse(string url, CancellationToken cancellationToken) { - return _httpClient.GetResponse(new HttpRequestOptions - { - CancellationToken = cancellationToken, - Url = url - }); + return _httpClientFactory.CreateClient(NamedClient.Default).GetAsync(url, cancellationToken); } - public string Name => "The Open Movie Database"; - public bool Supports(BaseItem item) { return item is Movie || item is Trailer || item is Episode; } - // After other internet providers, because they're better - // But before fallback providers like screengrab - public int Order => 90; } } diff --git a/MediaBrowser.Providers/Plugins/Omdb/OmdbItemProvider.cs b/MediaBrowser.Providers/Plugins/Omdb/OmdbItemProvider.cs index 3aadda5d07..705359d2c7 100644 --- a/MediaBrowser.Providers/Plugins/Omdb/OmdbItemProvider.cs +++ b/MediaBrowser.Providers/Plugins/Omdb/OmdbItemProvider.cs @@ -1,8 +1,11 @@ +#pragma warning disable CS1591 + using System; using System.Collections.Generic; using System.Globalization; using System.Linq; using System.Net; +using System.Net.Http; using System.Threading; using System.Threading.Tasks; using MediaBrowser.Common; @@ -17,7 +20,6 @@ using MediaBrowser.Model.Entities; using MediaBrowser.Model.IO; using MediaBrowser.Model.Providers; using MediaBrowser.Model.Serialization; -using Microsoft.Extensions.Logging; namespace MediaBrowser.Providers.Plugins.Omdb { @@ -25,23 +27,30 @@ namespace MediaBrowser.Providers.Plugins.Omdb IRemoteMetadataProvider<Movie, MovieInfo>, IRemoteMetadataProvider<Trailer, TrailerInfo>, IHasOrder { private readonly IJsonSerializer _jsonSerializer; - private readonly IHttpClient _httpClient; - private readonly ILogger _logger; + private readonly IHttpClientFactory _httpClientFactory; private readonly ILibraryManager _libraryManager; private readonly IFileSystem _fileSystem; private readonly IServerConfigurationManager _configurationManager; private readonly IApplicationHost _appHost; - public OmdbItemProvider(IJsonSerializer jsonSerializer, IApplicationHost appHost, IHttpClient httpClient, ILogger logger, ILibraryManager libraryManager, IFileSystem fileSystem, IServerConfigurationManager configurationManager) + public OmdbItemProvider( + IJsonSerializer jsonSerializer, + IApplicationHost appHost, + IHttpClientFactory httpClientFactory, + ILibraryManager libraryManager, + IFileSystem fileSystem, + IServerConfigurationManager configurationManager) { _jsonSerializer = jsonSerializer; - _httpClient = httpClient; - _logger = logger; + _httpClientFactory = httpClientFactory; _libraryManager = libraryManager; _fileSystem = fileSystem; _configurationManager = configurationManager; _appHost = appHost; } + + public string Name => "The Open Movie Database"; + // After primary option public int Order => 2; @@ -64,12 +73,12 @@ namespace MediaBrowser.Providers.Plugins.Omdb { var episodeSearchInfo = searchInfo as EpisodeInfo; - var imdbId = searchInfo.GetProviderId(MetadataProviders.Imdb); + var imdbId = searchInfo.GetProviderId(MetadataProvider.Imdb); var urlQuery = "plot=full&r=json"; if (type == "episode" && episodeSearchInfo != null) { - episodeSearchInfo.SeriesProviderIds.TryGetValue(MetadataProviders.Imdb.ToString(), out imdbId); + episodeSearchInfo.SeriesProviderIds.TryGetValue(MetadataProvider.Imdb.ToString(), out imdbId); } var name = searchInfo.Name; @@ -80,7 +89,7 @@ namespace MediaBrowser.Providers.Plugins.Omdb var parsedName = _libraryManager.ParseName(name); var yearInName = parsedName.Year; name = parsedName.Name; - year = year ?? yearInName; + year ??= yearInName; } if (string.IsNullOrWhiteSpace(imdbId)) @@ -99,6 +108,7 @@ namespace MediaBrowser.Providers.Plugins.Omdb { urlQuery += "&t=" + WebUtility.UrlEncode(name); } + urlQuery += "&type=" + type; } else @@ -113,75 +123,72 @@ namespace MediaBrowser.Providers.Plugins.Omdb { urlQuery += string.Format(CultureInfo.InvariantCulture, "&Episode={0}", searchInfo.IndexNumber); } + if (searchInfo.ParentIndexNumber.HasValue) { urlQuery += string.Format(CultureInfo.InvariantCulture, "&Season={0}", searchInfo.ParentIndexNumber); } } - var url = OmdbProvider.GetOmdbUrl(urlQuery, _appHost, cancellationToken); + var url = OmdbProvider.GetOmdbUrl(urlQuery); - using (var response = await OmdbProvider.GetOmdbResponse(_httpClient, url, cancellationToken).ConfigureAwait(false)) + using var response = await OmdbProvider.GetOmdbResponse(_httpClientFactory.CreateClient(NamedClient.Default), url, cancellationToken).ConfigureAwait(false); + await using var stream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false); + var resultList = new List<SearchResult>(); + + if (isSearch) { - using (var stream = response.Content) + var searchResultList = await _jsonSerializer.DeserializeFromStreamAsync<SearchResultList>(stream).ConfigureAwait(false); + if (searchResultList != null && searchResultList.Search != null) { - var resultList = new List<SearchResult>(); - - if (isSearch) - { - var searchResultList = await _jsonSerializer.DeserializeFromStreamAsync<SearchResultList>(stream).ConfigureAwait(false); - if (searchResultList != null && searchResultList.Search != null) - { - resultList.AddRange(searchResultList.Search); - } - } - else - { - var result = await _jsonSerializer.DeserializeFromStreamAsync<SearchResult>(stream).ConfigureAwait(false); - if (string.Equals(result.Response, "true", StringComparison.OrdinalIgnoreCase)) - { - resultList.Add(result); - } - } - - return resultList.Select(result => - { - var item = new RemoteSearchResult - { - IndexNumber = searchInfo.IndexNumber, - Name = result.Title, - ParentIndexNumber = searchInfo.ParentIndexNumber, - SearchProviderName = Name - }; - - if (episodeSearchInfo != null && episodeSearchInfo.IndexNumberEnd.HasValue) - { - item.IndexNumberEnd = episodeSearchInfo.IndexNumberEnd.Value; - } - - item.SetProviderId(MetadataProviders.Imdb, result.imdbID); - - if (result.Year.Length > 0 - && int.TryParse(result.Year.Substring(0, Math.Min(result.Year.Length, 4)), NumberStyles.Integer, CultureInfo.InvariantCulture, out var parsedYear)) - { - item.ProductionYear = parsedYear; - } - - if (!string.IsNullOrEmpty(result.Released) - && DateTime.TryParse(result.Released, CultureInfo.InvariantCulture, DateTimeStyles.AllowWhiteSpaces, out var released)) - { - item.PremiereDate = released; - } - - if (!string.IsNullOrWhiteSpace(result.Poster) && !string.Equals(result.Poster, "N/A", StringComparison.OrdinalIgnoreCase)) - { - item.ImageUrl = result.Poster; - } - - return item; - }); + resultList.AddRange(searchResultList.Search); } } + else + { + var result = await _jsonSerializer.DeserializeFromStreamAsync<SearchResult>(stream).ConfigureAwait(false); + if (string.Equals(result.Response, "true", StringComparison.OrdinalIgnoreCase)) + { + resultList.Add(result); + } + } + + return resultList.Select(result => + { + var item = new RemoteSearchResult + { + IndexNumber = searchInfo.IndexNumber, + Name = result.Title, + ParentIndexNumber = searchInfo.ParentIndexNumber, + SearchProviderName = Name + }; + + if (episodeSearchInfo != null && episodeSearchInfo.IndexNumberEnd.HasValue) + { + item.IndexNumberEnd = episodeSearchInfo.IndexNumberEnd.Value; + } + + item.SetProviderId(MetadataProvider.Imdb, result.imdbID); + + if (result.Year.Length > 0 + && int.TryParse(result.Year.AsSpan().Slice(0, Math.Min(result.Year.Length, 4)), NumberStyles.Integer, CultureInfo.InvariantCulture, out var parsedYear)) + { + item.ProductionYear = parsedYear; + } + + if (!string.IsNullOrEmpty(result.Released) + && DateTime.TryParse(result.Released, CultureInfo.InvariantCulture, DateTimeStyles.AllowWhiteSpaces, out var released)) + { + item.PremiereDate = released; + } + + if (!string.IsNullOrWhiteSpace(result.Poster) && !string.Equals(result.Poster, "N/A", StringComparison.OrdinalIgnoreCase)) + { + item.ImageUrl = result.Poster; + } + + return item; + }); } public Task<MetadataResult<Trailer>> GetMetadata(TrailerInfo info, CancellationToken cancellationToken) @@ -194,8 +201,6 @@ namespace MediaBrowser.Providers.Plugins.Omdb return GetSearchResults(searchInfo, "movie", cancellationToken); } - public string Name => "The Open Movie Database"; - public async Task<MetadataResult<Series>> GetMetadata(SeriesInfo info, CancellationToken cancellationToken) { var result = new MetadataResult<Series> @@ -204,7 +209,7 @@ namespace MediaBrowser.Providers.Plugins.Omdb QueriedById = true }; - var imdbId = info.GetProviderId(MetadataProviders.Imdb); + var imdbId = info.GetProviderId(MetadataProvider.Imdb); if (string.IsNullOrWhiteSpace(imdbId)) { imdbId = await GetSeriesImdbId(info, cancellationToken).ConfigureAwait(false); @@ -213,10 +218,10 @@ namespace MediaBrowser.Providers.Plugins.Omdb if (!string.IsNullOrEmpty(imdbId)) { - result.Item.SetProviderId(MetadataProviders.Imdb, imdbId); + result.Item.SetProviderId(MetadataProvider.Imdb, imdbId); result.HasMetadata = true; - await new OmdbProvider(_jsonSerializer, _httpClient, _fileSystem, _appHost, _configurationManager).Fetch(result, imdbId, info.MetadataLanguage, info.MetadataCountryCode, cancellationToken).ConfigureAwait(false); + await new OmdbProvider(_jsonSerializer, _httpClientFactory, _fileSystem, _appHost, _configurationManager).Fetch(result, imdbId, info.MetadataLanguage, info.MetadataCountryCode, cancellationToken).ConfigureAwait(false); } return result; @@ -236,7 +241,7 @@ namespace MediaBrowser.Providers.Plugins.Omdb QueriedById = true }; - var imdbId = info.GetProviderId(MetadataProviders.Imdb); + var imdbId = info.GetProviderId(MetadataProvider.Imdb); if (string.IsNullOrWhiteSpace(imdbId)) { imdbId = await GetMovieImdbId(info, cancellationToken).ConfigureAwait(false); @@ -245,10 +250,10 @@ namespace MediaBrowser.Providers.Plugins.Omdb if (!string.IsNullOrEmpty(imdbId)) { - result.Item.SetProviderId(MetadataProviders.Imdb, imdbId); + result.Item.SetProviderId(MetadataProvider.Imdb, imdbId); result.HasMetadata = true; - await new OmdbProvider(_jsonSerializer, _httpClient, _fileSystem, _appHost, _configurationManager).Fetch(result, imdbId, info.MetadataLanguage, info.MetadataCountryCode, cancellationToken).ConfigureAwait(false); + await new OmdbProvider(_jsonSerializer, _httpClientFactory, _fileSystem, _appHost, _configurationManager).Fetch(result, imdbId, info.MetadataLanguage, info.MetadataCountryCode, cancellationToken).ConfigureAwait(false); } return result; @@ -258,49 +263,67 @@ namespace MediaBrowser.Providers.Plugins.Omdb { var results = await GetSearchResultsInternal(info, "movie", false, cancellationToken).ConfigureAwait(false); var first = results.FirstOrDefault(); - return first == null ? null : first.GetProviderId(MetadataProviders.Imdb); + return first?.GetProviderId(MetadataProvider.Imdb); } private async Task<string> GetSeriesImdbId(SeriesInfo info, CancellationToken cancellationToken) { var results = await GetSearchResultsInternal(info, "series", false, cancellationToken).ConfigureAwait(false); var first = results.FirstOrDefault(); - return first == null ? null : first.GetProviderId(MetadataProviders.Imdb); + return first?.GetProviderId(MetadataProvider.Imdb); } - public Task<HttpResponseInfo> GetImageResponse(string url, CancellationToken cancellationToken) + public Task<HttpResponseMessage> GetImageResponse(string url, CancellationToken cancellationToken) { - return _httpClient.GetResponse(new HttpRequestOptions - { - CancellationToken = cancellationToken, - Url = url - }); + return _httpClientFactory.CreateClient(NamedClient.Default).GetAsync(url, cancellationToken); } - class SearchResult + private class SearchResult { public string Title { get; set; } + public string Year { get; set; } + public string Rated { get; set; } + public string Released { get; set; } + public string Season { get; set; } + public string Episode { get; set; } + public string Runtime { get; set; } + public string Genre { get; set; } + public string Director { get; set; } + public string Writer { get; set; } + public string Actors { get; set; } + public string Plot { get; set; } + public string Language { get; set; } + public string Country { get; set; } + public string Awards { get; set; } + public string Poster { get; set; } + public string Metascore { get; set; } + public string imdbRating { get; set; } + public string imdbVotes { get; set; } + public string imdbID { get; set; } + public string seriesID { get; set; } + public string Type { get; set; } + public string Response { get; set; } } @@ -312,6 +335,5 @@ namespace MediaBrowser.Providers.Plugins.Omdb /// <value>The results.</value> public List<SearchResult> Search { get; set; } } - } } diff --git a/MediaBrowser.Providers/Plugins/Omdb/OmdbProvider.cs b/MediaBrowser.Providers/Plugins/Omdb/OmdbProvider.cs index fbdd293edf..32dab60a6b 100644 --- a/MediaBrowser.Providers/Plugins/Omdb/OmdbProvider.cs +++ b/MediaBrowser.Providers/Plugins/Omdb/OmdbProvider.cs @@ -1,3 +1,5 @@ +#pragma warning disable CS1591 + using System; using System.Collections.Generic; using System.Globalization; @@ -23,14 +25,14 @@ namespace MediaBrowser.Providers.Plugins.Omdb private readonly IJsonSerializer _jsonSerializer; private readonly IFileSystem _fileSystem; private readonly IServerConfigurationManager _configurationManager; - private readonly IHttpClient _httpClient; + private readonly IHttpClientFactory _httpClientFactory; private readonly CultureInfo _usCulture = new CultureInfo("en-US"); private readonly IApplicationHost _appHost; - public OmdbProvider(IJsonSerializer jsonSerializer, IHttpClient httpClient, IFileSystem fileSystem, IApplicationHost appHost, IServerConfigurationManager configurationManager) + public OmdbProvider(IJsonSerializer jsonSerializer, IHttpClientFactory httpClientFactory, IFileSystem fileSystem, IApplicationHost appHost, IServerConfigurationManager configurationManager) { _jsonSerializer = jsonSerializer; - _httpClient = httpClient; + _httpClientFactory = httpClientFactory; _fileSystem = fileSystem; _configurationManager = configurationManager; _appHost = appHost; @@ -60,7 +62,7 @@ namespace MediaBrowser.Providers.Plugins.Omdb } if (!string.IsNullOrEmpty(result.Year) && result.Year.Length >= 4 - && int.TryParse(result.Year.Substring(0, 4), NumberStyles.Number, _usCulture, out var year) + && int.TryParse(result.Year.AsSpan().Slice(0, 4), NumberStyles.Number, _usCulture, out var year) && year >= 0) { item.ProductionYear = year; @@ -77,7 +79,7 @@ namespace MediaBrowser.Providers.Plugins.Omdb && int.TryParse(result.imdbVotes, NumberStyles.Number, _usCulture, out var voteCount) && voteCount >= 0) { - //item.VoteCount = voteCount; + // item.VoteCount = voteCount; } if (!string.IsNullOrEmpty(result.imdbRating) @@ -87,14 +89,14 @@ namespace MediaBrowser.Providers.Plugins.Omdb item.CommunityRating = imdbRating; } - //if (!string.IsNullOrEmpty(result.Website)) - //{ - // item.HomePageUrl = result.Website; - //} + if (!string.IsNullOrEmpty(result.Website)) + { + item.HomePageUrl = result.Website; + } if (!string.IsNullOrWhiteSpace(result.imdbID)) { - item.SetProviderId(MetadataProviders.Imdb, result.imdbID); + item.SetProviderId(MetadataProvider.Imdb, result.imdbID); } ParseAdditionalMetadata(itemResult, result); @@ -121,7 +123,7 @@ namespace MediaBrowser.Providers.Plugins.Omdb if (!string.IsNullOrWhiteSpace(episodeImdbId)) { - foreach (var episode in (seasonResult.Episodes ?? new RootObject[] { })) + foreach (var episode in seasonResult.Episodes) { if (string.Equals(episodeImdbId, episode.imdbID, StringComparison.OrdinalIgnoreCase)) { @@ -134,7 +136,7 @@ namespace MediaBrowser.Providers.Plugins.Omdb // finally, search by numbers if (result == null) { - foreach (var episode in (seasonResult.Episodes ?? new RootObject[] { })) + foreach (var episode in seasonResult.Episodes) { if (episode.Episode == episodeNumber) { @@ -161,7 +163,7 @@ namespace MediaBrowser.Providers.Plugins.Omdb } if (!string.IsNullOrEmpty(result.Year) && result.Year.Length >= 4 - && int.TryParse(result.Year.Substring(0, 4), NumberStyles.Number, _usCulture, out var year) + && int.TryParse(result.Year.AsSpan().Slice(0, 4), NumberStyles.Number, _usCulture, out var year) && year >= 0) { item.ProductionYear = year; @@ -178,7 +180,7 @@ namespace MediaBrowser.Providers.Plugins.Omdb && int.TryParse(result.imdbVotes, NumberStyles.Number, _usCulture, out var voteCount) && voteCount >= 0) { - //item.VoteCount = voteCount; + // item.VoteCount = voteCount; } if (!string.IsNullOrEmpty(result.imdbRating) @@ -188,14 +190,14 @@ namespace MediaBrowser.Providers.Plugins.Omdb item.CommunityRating = imdbRating; } - //if (!string.IsNullOrEmpty(result.Website)) - //{ - // item.HomePageUrl = result.Website; - //} + if (!string.IsNullOrEmpty(result.Website)) + { + item.HomePageUrl = result.Website; + } if (!string.IsNullOrWhiteSpace(result.imdbID)) { - item.SetProviderId(MetadataProviders.Imdb, result.imdbID); + item.SetProviderId(MetadataProvider.Imdb, result.imdbID); } ParseAdditionalMetadata(itemResult, result); @@ -243,7 +245,7 @@ namespace MediaBrowser.Providers.Plugins.Omdb internal static bool IsValidSeries(Dictionary<string, string> seriesProviderIds) { - if (seriesProviderIds.TryGetValue(MetadataProviders.Imdb.ToString(), out string id) && !string.IsNullOrEmpty(id)) + if (seriesProviderIds.TryGetValue(MetadataProvider.Imdb.ToString(), out string id) && !string.IsNullOrEmpty(id)) { // This check should ideally never be necessary but we're seeing some cases of this and haven't tracked them down yet. if (!string.IsNullOrWhiteSpace(id)) @@ -255,15 +257,16 @@ namespace MediaBrowser.Providers.Plugins.Omdb return false; } - public static string GetOmdbUrl(string query, IApplicationHost appHost, CancellationToken cancellationToken) + public static string GetOmdbUrl(string query) { - const string url = "https://www.omdbapi.com?apikey=2c9d9507"; + const string Url = "https://www.omdbapi.com?apikey=2c9d9507"; if (string.IsNullOrWhiteSpace(query)) { - return url; + return Url; } - return url + "&" + query; + + return Url + "&" + query; } private async Task<string> EnsureItemInfo(string imdbId, CancellationToken cancellationToken) @@ -288,17 +291,17 @@ namespace MediaBrowser.Providers.Plugins.Omdb } } - var url = GetOmdbUrl(string.Format("i={0}&plot=short&tomatoes=true&r=json", imdbParam), _appHost, cancellationToken); + var url = GetOmdbUrl( + string.Format( + CultureInfo.InvariantCulture, + "i={0}&plot=short&tomatoes=true&r=json", + imdbParam)); - using (var response = await GetOmdbResponse(_httpClient, url, cancellationToken).ConfigureAwait(false)) - { - using (var stream = response.Content) - { - var rootObject = await _jsonSerializer.DeserializeFromStreamAsync<RootObject>(stream).ConfigureAwait(false); - Directory.CreateDirectory(Path.GetDirectoryName(path)); - _jsonSerializer.SerializeToFile(rootObject, path); - } - } + using var response = await GetOmdbResponse(_httpClientFactory.CreateClient(NamedClient.Default), url, cancellationToken).ConfigureAwait(false); + await using var stream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false); + var rootObject = await _jsonSerializer.DeserializeFromStreamAsync<RootObject>(stream).ConfigureAwait(false); + Directory.CreateDirectory(Path.GetDirectoryName(path)); + _jsonSerializer.SerializeToFile(rootObject, path); return path; } @@ -325,30 +328,25 @@ namespace MediaBrowser.Providers.Plugins.Omdb } } - var url = GetOmdbUrl(string.Format("i={0}&season={1}&detail=full", imdbParam, seasonId), _appHost, cancellationToken); + var url = GetOmdbUrl( + string.Format( + CultureInfo.InvariantCulture, + "i={0}&season={1}&detail=full", + imdbParam, + seasonId)); - using (var response = await GetOmdbResponse(_httpClient, url, cancellationToken).ConfigureAwait(false)) - { - using (var stream = response.Content) - { - var rootObject = await _jsonSerializer.DeserializeFromStreamAsync<SeasonRootObject>(stream).ConfigureAwait(false); - Directory.CreateDirectory(Path.GetDirectoryName(path)); - _jsonSerializer.SerializeToFile(rootObject, path); - } - } + using var response = await GetOmdbResponse(_httpClientFactory.CreateClient(NamedClient.Default), url, cancellationToken).ConfigureAwait(false); + await using var stream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false); + var rootObject = await _jsonSerializer.DeserializeFromStreamAsync<SeasonRootObject>(stream).ConfigureAwait(false); + Directory.CreateDirectory(Path.GetDirectoryName(path)); + _jsonSerializer.SerializeToFile(rootObject, path); return path; } - public static Task<HttpResponseInfo> GetOmdbResponse(IHttpClient httpClient, string url, CancellationToken cancellationToken) + public static Task<HttpResponseMessage> GetOmdbResponse(HttpClient httpClient, string url, CancellationToken cancellationToken) { - return httpClient.SendAsync(new HttpRequestOptions - { - Url = url, - CancellationToken = cancellationToken, - BufferContent = true, - EnableDefaultUserAgent = true - }, HttpMethod.Get); + return httpClient.GetAsync(url, cancellationToken); } internal string GetDataFilePath(string imdbId) @@ -360,7 +358,7 @@ namespace MediaBrowser.Providers.Plugins.Omdb var dataPath = Path.Combine(_configurationManager.ApplicationPaths.CachePath, "omdb"); - var filename = string.Format("{0}.json", imdbId); + var filename = string.Format(CultureInfo.InvariantCulture, "{0}.json", imdbId); return Path.Combine(dataPath, filename); } @@ -374,7 +372,7 @@ namespace MediaBrowser.Providers.Plugins.Omdb var dataPath = Path.Combine(_configurationManager.ApplicationPaths.CachePath, "omdb"); - var filename = string.Format("{0}_season_{1}.json", imdbId, seasonId); + var filename = string.Format(CultureInfo.InvariantCulture, "{0}_season_{1}.json", imdbId, seasonId); return Path.Combine(dataPath, filename); } @@ -386,7 +384,7 @@ namespace MediaBrowser.Providers.Plugins.Omdb var isConfiguredForEnglish = IsConfiguredForEnglish(item) || _configurationManager.Configuration.EnableNewOmdbSupport; - // Grab series genres because imdb data is better than tvdb. Leave movies alone + // Grab series genres because IMDb data is better than TVDB. Leave movies alone // But only do it if english is the preferred language because this data will not be localized if (isConfiguredForEnglish && !string.IsNullOrWhiteSpace(result.Genre)) { @@ -407,45 +405,50 @@ namespace MediaBrowser.Providers.Plugins.Omdb item.Overview = result.Plot; } - //if (!string.IsNullOrWhiteSpace(result.Director)) - //{ - // var person = new PersonInfo - // { - // Name = result.Director.Trim(), - // Type = PersonType.Director - // }; - - // itemResult.AddPerson(person); - //} - - //if (!string.IsNullOrWhiteSpace(result.Writer)) - //{ - // var person = new PersonInfo - // { - // Name = result.Director.Trim(), - // Type = PersonType.Writer - // }; - - // itemResult.AddPerson(person); - //} - - //if (!string.IsNullOrWhiteSpace(result.Actors)) - //{ - // var actorList = result.Actors.Split(','); - // foreach (var actor in actorList) - // { - // if (!string.IsNullOrWhiteSpace(actor)) - // { - // var person = new PersonInfo - // { - // Name = actor.Trim(), - // Type = PersonType.Actor - // }; - - // itemResult.AddPerson(person); - // } - // } - //} + if (!Plugin.Instance.Configuration.CastAndCrew) + { + return; + } + + if (!string.IsNullOrWhiteSpace(result.Director)) + { + var person = new PersonInfo + { + Name = result.Director.Trim(), + Type = PersonType.Director + }; + + itemResult.AddPerson(person); + } + + if (!string.IsNullOrWhiteSpace(result.Writer)) + { + var person = new PersonInfo + { + Name = result.Director.Trim(), + Type = PersonType.Writer + }; + + itemResult.AddPerson(person); + } + + if (!string.IsNullOrWhiteSpace(result.Actors)) + { + var actorList = result.Actors.Split(','); + foreach (var actor in actorList) + { + if (!string.IsNullOrWhiteSpace(actor)) + { + var person = new PersonInfo + { + Name = actor.Trim(), + Type = PersonType.Actor + }; + + itemResult.AddPerson(person); + } + } + } } private bool IsConfiguredForEnglish(BaseItem item) @@ -459,40 +462,70 @@ namespace MediaBrowser.Providers.Plugins.Omdb internal class SeasonRootObject { public string Title { get; set; } + public string seriesID { get; set; } + public int Season { get; set; } + public int? totalSeasons { get; set; } + public RootObject[] Episodes { get; set; } + public string Response { get; set; } } internal class RootObject { public string Title { get; set; } + public string Year { get; set; } + public string Rated { get; set; } + public string Released { get; set; } + public string Runtime { get; set; } + public string Genre { get; set; } + public string Director { get; set; } + public string Writer { get; set; } + public string Actors { get; set; } + public string Plot { get; set; } + public string Language { get; set; } + public string Country { get; set; } + public string Awards { get; set; } + public string Poster { get; set; } + public List<OmdbRating> Ratings { get; set; } + public string Metascore { get; set; } + public string imdbRating { get; set; } + public string imdbVotes { get; set; } + public string imdbID { get; set; } + public string Type { get; set; } + public string DVD { get; set; } + public string BoxOffice { get; set; } + public string Production { get; set; } + public string Website { get; set; } + public string Response { get; set; } + public int Episode { get; set; } public float? GetRottenTomatoScore() @@ -509,12 +542,15 @@ namespace MediaBrowser.Providers.Plugins.Omdb } } } + return null; } } + public class OmdbRating { public string Source { get; set; } + public string Value { get; set; } } } diff --git a/MediaBrowser.Providers/Plugins/Omdb/Plugin.cs b/MediaBrowser.Providers/Plugins/Omdb/Plugin.cs new file mode 100644 index 0000000000..41ca561643 --- /dev/null +++ b/MediaBrowser.Providers/Plugins/Omdb/Plugin.cs @@ -0,0 +1,40 @@ +#pragma warning disable CS1591 + +using System; +using System.Collections.Generic; +using MediaBrowser.Common.Configuration; +using MediaBrowser.Common.Plugins; +using MediaBrowser.Model.Plugins; +using MediaBrowser.Model.Serialization; + +namespace MediaBrowser.Providers.Plugins.Omdb +{ + public class Plugin : BasePlugin<PluginConfiguration>, IHasWebPages + { + public static Plugin Instance { get; private set; } + + public override Guid Id => new Guid("a628c0da-fac5-4c7e-9d1a-7134223f14c8"); + + public override string Name => "OMDb"; + + public override string Description => "Get metadata for movies and other video content from OMDb."; + + // TODO remove when plugin removed from server. + public override string ConfigurationFileName => "Jellyfin.Plugin.Omdb.xml"; + + public Plugin(IApplicationPaths applicationPaths, IXmlSerializer xmlSerializer) + : base(applicationPaths, xmlSerializer) + { + Instance = this; + } + + public IEnumerable<PluginPageInfo> GetPages() + { + yield return new PluginPageInfo + { + Name = Name, + EmbeddedResourcePath = GetType().Namespace + ".Configuration.config.html" + }; + } + } +} diff --git a/MediaBrowser.Providers/Plugins/TheTvdb/Configuration/PluginConfiguration.cs b/MediaBrowser.Providers/Plugins/TheTvdb/Configuration/PluginConfiguration.cs new file mode 100644 index 0000000000..690a52c4d3 --- /dev/null +++ b/MediaBrowser.Providers/Plugins/TheTvdb/Configuration/PluginConfiguration.cs @@ -0,0 +1,10 @@ +#pragma warning disable CS1591 + +using MediaBrowser.Model.Plugins; + +namespace MediaBrowser.Providers.Plugins.TheTvdb +{ + public class PluginConfiguration : BasePluginConfiguration + { + } +} diff --git a/MediaBrowser.Providers/Plugins/TheTvdb/Plugin.cs b/MediaBrowser.Providers/Plugins/TheTvdb/Plugin.cs new file mode 100644 index 0000000000..e7079ed3cd --- /dev/null +++ b/MediaBrowser.Providers/Plugins/TheTvdb/Plugin.cs @@ -0,0 +1,29 @@ +#pragma warning disable CS1591 + +using System; +using MediaBrowser.Common.Configuration; +using MediaBrowser.Common.Plugins; +using MediaBrowser.Model.Serialization; + +namespace MediaBrowser.Providers.Plugins.TheTvdb +{ + public class Plugin : BasePlugin<PluginConfiguration> + { + public static Plugin Instance { get; private set; } + + public override Guid Id => new Guid("a677c0da-fac5-4cde-941a-7134223f14c8"); + + public override string Name => "TheTVDB"; + + public override string Description => "Get metadata for movies and other video content from TheTVDB."; + + // TODO remove when plugin removed from server. + public override string ConfigurationFileName => "Jellyfin.Plugin.TheTvdb.xml"; + + public Plugin(IApplicationPaths applicationPaths, IXmlSerializer xmlSerializer) + : base(applicationPaths, xmlSerializer) + { + Instance = this; + } + } +} diff --git a/MediaBrowser.Providers/Plugins/TheTvdb/TvdbClientManager.cs b/MediaBrowser.Providers/Plugins/TheTvdb/TvdbClientManager.cs index b73834155c..f22d484abc 100644 --- a/MediaBrowser.Providers/Plugins/TheTvdb/TvdbClientManager.cs +++ b/MediaBrowser.Providers/Plugins/TheTvdb/TvdbClientManager.cs @@ -1,7 +1,11 @@ +#pragma warning disable CS1591 + using System; using System.Collections.Generic; +using System.Globalization; using System.Linq; using System.Reflection; +using System.Runtime.CompilerServices; using System.Threading; using System.Threading.Tasks; using MediaBrowser.Controller.Providers; @@ -16,7 +20,6 @@ namespace MediaBrowser.Providers.Plugins.TheTvdb { private const string DefaultLanguage = "en"; - private readonly SemaphoreSlim _cacheWriteLock = new SemaphoreSlim(1, 1); private readonly IMemoryCache _cache; private readonly TvDbClient _tvDbClient; private DateTime _tokenCreatedAt; @@ -120,6 +123,7 @@ namespace MediaBrowser.Providers.Plugins.TheTvdb var cacheKey = GenerateKey("series", zap2ItId, language); return TryGetValue(cacheKey, language, () => TvDbClient.Search.SearchSeriesByZap2ItIdAsync(zap2ItId, cancellationToken)); } + public Task<TvDbResponse<Actor[]>> GetActorsAsync( int tvdbId, string language, @@ -172,7 +176,7 @@ namespace MediaBrowser.Providers.Plugins.TheTvdb string language, CancellationToken cancellationToken) { - searchInfo.SeriesProviderIds.TryGetValue(MetadataProviders.Tvdb.ToString(), + searchInfo.SeriesProviderIds.TryGetValue(nameof(MetadataProvider.Tvdb), out var seriesTvdbId); var episodeQuery = new EpisodeQuery(); @@ -190,7 +194,7 @@ namespace MediaBrowser.Providers.Plugins.TheTvdb episodeQuery.AbsoluteNumber = searchInfo.IndexNumber.Value; break; default: - //aired order + // aired order episodeQuery.AiredEpisode = searchInfo.IndexNumber.Value; episodeQuery.AiredSeason = searchInfo.ParentIndexNumber.Value; break; @@ -199,10 +203,10 @@ namespace MediaBrowser.Providers.Plugins.TheTvdb else if (searchInfo.PremiereDate.HasValue) { // tvdb expects yyyy-mm-dd format - episodeQuery.FirstAired = searchInfo.PremiereDate.Value.ToString("yyyy-MM-dd"); + episodeQuery.FirstAired = searchInfo.PremiereDate.Value.ToString("yyyy-MM-dd", CultureInfo.InvariantCulture); } - return GetEpisodeTvdbId(Convert.ToInt32(seriesTvdbId), episodeQuery, language, cancellationToken); + return GetEpisodeTvdbId(Convert.ToInt32(seriesTvdbId, CultureInfo.InvariantCulture), episodeQuery, language, cancellationToken); } public async Task<string> GetEpisodeTvdbId( @@ -214,7 +218,7 @@ namespace MediaBrowser.Providers.Plugins.TheTvdb var episodePage = await GetEpisodesPageAsync(Convert.ToInt32(seriesTvdbId), episodeQuery, language, cancellationToken) .ConfigureAwait(false); - return episodePage.Data.FirstOrDefault()?.Id.ToString(); + return episodePage.Data.FirstOrDefault()?.Id.ToString(CultureInfo.InvariantCulture); } public Task<TvDbResponse<EpisodeRecord[]>> GetEpisodesPageAsync( @@ -226,30 +230,56 @@ namespace MediaBrowser.Providers.Plugins.TheTvdb return GetEpisodesPageAsync(tvdbId, 1, episodeQuery, language, cancellationToken); } - private async Task<T> TryGetValue<T>(string key, string language, Func<Task<T>> resultFactory) + public async IAsyncEnumerable<KeyType> GetImageKeyTypesForSeriesAsync(int tvdbId, string language, [EnumeratorCancellation] CancellationToken cancellationToken) { - if (_cache.TryGetValue(key, out T cachedValue)) + var cacheKey = GenerateKey(nameof(TvDbClient.Series.GetImagesSummaryAsync), tvdbId); + var imagesSummary = await TryGetValue(cacheKey, language, () => TvDbClient.Series.GetImagesSummaryAsync(tvdbId, cancellationToken)).ConfigureAwait(false); + + if (imagesSummary.Data.Fanart > 0) { - return cachedValue; + yield return KeyType.Fanart; } - await _cacheWriteLock.WaitAsync().ConfigureAwait(false); - try + if (imagesSummary.Data.Series > 0) { - if (_cache.TryGetValue(key, out cachedValue)) - { - return cachedValue; - } + yield return KeyType.Series; + } - _tvDbClient.AcceptedLanguage = TvdbUtils.NormalizeLanguage(language) ?? DefaultLanguage; - var result = await resultFactory.Invoke().ConfigureAwait(false); - _cache.Set(key, result, TimeSpan.FromHours(1)); - return result; + if (imagesSummary.Data.Poster > 0) + { + yield return KeyType.Poster; } - finally + } + + public async IAsyncEnumerable<KeyType> GetImageKeyTypesForSeasonAsync(int tvdbId, string language, [EnumeratorCancellation] CancellationToken cancellationToken) + { + var cacheKey = GenerateKey(nameof(TvDbClient.Series.GetImagesSummaryAsync), tvdbId); + var imagesSummary = await TryGetValue(cacheKey, language, () => TvDbClient.Series.GetImagesSummaryAsync(tvdbId, cancellationToken)).ConfigureAwait(false); + + if (imagesSummary.Data.Season > 0) { - _cacheWriteLock.Release(); + yield return KeyType.Season; } + + if (imagesSummary.Data.Fanart > 0) + { + yield return KeyType.Fanart; + } + + // TODO seasonwide is not supported in TvDbSharper + } + + private async Task<T> TryGetValue<T>(string key, string language, Func<Task<T>> resultFactory) + { + if (_cache.TryGetValue(key, out T cachedValue)) + { + return cachedValue; + } + + _tvDbClient.AcceptedLanguage = TvdbUtils.NormalizeLanguage(language) ?? DefaultLanguage; + var result = await resultFactory.Invoke().ConfigureAwait(false); + _cache.Set(key, result, TimeSpan.FromHours(1)); + return result; } private static string GenerateKey(params object[] objects) diff --git a/MediaBrowser.Providers/Plugins/TheTvdb/TvdbEpisodeImageProvider.cs b/MediaBrowser.Providers/Plugins/TheTvdb/TvdbEpisodeImageProvider.cs index 6118a9c53e..de2f6875f8 100644 --- a/MediaBrowser.Providers/Plugins/TheTvdb/TvdbEpisodeImageProvider.cs +++ b/MediaBrowser.Providers/Plugins/TheTvdb/TvdbEpisodeImageProvider.cs @@ -1,5 +1,9 @@ +#pragma warning disable CS1591 + using System; using System.Collections.Generic; +using System.Net.Http; +using System.Globalization; using System.Threading; using System.Threading.Tasks; using MediaBrowser.Common.Net; @@ -16,13 +20,13 @@ namespace MediaBrowser.Providers.Plugins.TheTvdb { public class TvdbEpisodeImageProvider : IRemoteImageProvider { - private readonly IHttpClient _httpClient; - private readonly ILogger _logger; + private readonly IHttpClientFactory _httpClientFactory; + private readonly ILogger<TvdbEpisodeImageProvider> _logger; private readonly TvdbClientManager _tvdbClientManager; - public TvdbEpisodeImageProvider(IHttpClient httpClient, ILogger<TvdbEpisodeImageProvider> logger, TvdbClientManager tvdbClientManager) + public TvdbEpisodeImageProvider(IHttpClientFactory httpClientFactory, ILogger<TvdbEpisodeImageProvider> logger, TvdbClientManager tvdbClientManager) { - _httpClient = httpClient; + _httpClientFactory = httpClientFactory; _logger = logger; _tvdbClientManager = tvdbClientManager; } @@ -68,13 +72,13 @@ namespace MediaBrowser.Providers.Plugins.TheTvdb "Episode {SeasonNumber}x{EpisodeNumber} not found for series {SeriesTvdbId}", episodeInfo.ParentIndexNumber, episodeInfo.IndexNumber, - series.GetProviderId(MetadataProviders.Tvdb)); + series.GetProviderId(MetadataProvider.Tvdb)); return imageResult; } var episodeResult = await _tvdbClientManager - .GetEpisodesAsync(Convert.ToInt32(episodeTvdbId), language, cancellationToken) + .GetEpisodesAsync(Convert.ToInt32(episodeTvdbId, CultureInfo.InvariantCulture), language, cancellationToken) .ConfigureAwait(false); var image = GetImageInfo(episodeResult.Data); @@ -85,7 +89,7 @@ namespace MediaBrowser.Providers.Plugins.TheTvdb } catch (TvDbServerException e) { - _logger.LogError(e, "Failed to retrieve episode images for series {TvDbId}", series.GetProviderId(MetadataProviders.Tvdb)); + _logger.LogError(e, "Failed to retrieve episode images for series {TvDbId}", series.GetProviderId(MetadataProvider.Tvdb)); } } @@ -101,8 +105,8 @@ namespace MediaBrowser.Providers.Plugins.TheTvdb return new RemoteImageInfo { - Width = Convert.ToInt32(episode.ThumbWidth), - Height = Convert.ToInt32(episode.ThumbHeight), + Width = Convert.ToInt32(episode.ThumbWidth, CultureInfo.InvariantCulture), + Height = Convert.ToInt32(episode.ThumbHeight, CultureInfo.InvariantCulture), ProviderName = Name, Url = TvdbUtils.BannerUrl + episode.Filename, Type = ImageType.Primary @@ -111,13 +115,9 @@ namespace MediaBrowser.Providers.Plugins.TheTvdb public int Order => 0; - public Task<HttpResponseInfo> GetImageResponse(string url, CancellationToken cancellationToken) + public Task<HttpResponseMessage> GetImageResponse(string url, CancellationToken cancellationToken) { - return _httpClient.GetResponse(new HttpRequestOptions - { - CancellationToken = cancellationToken, - Url = url - }); + return _httpClientFactory.CreateClient(NamedClient.Default).GetAsync(url, cancellationToken); } } } diff --git a/MediaBrowser.Providers/Plugins/TheTvdb/TvdbEpisodeProvider.cs b/MediaBrowser.Providers/Plugins/TheTvdb/TvdbEpisodeProvider.cs index 08c2a74d2c..c088d8cec1 100644 --- a/MediaBrowser.Providers/Plugins/TheTvdb/TvdbEpisodeProvider.cs +++ b/MediaBrowser.Providers/Plugins/TheTvdb/TvdbEpisodeProvider.cs @@ -1,5 +1,8 @@ +#pragma warning disable CS1591 + using System; using System.Collections.Generic; +using System.Net.Http; using System.Threading; using System.Threading.Tasks; using MediaBrowser.Common.Net; @@ -14,19 +17,18 @@ using TvDbSharper.Dto; namespace MediaBrowser.Providers.Plugins.TheTvdb { - /// <summary> - /// Class RemoteEpisodeProvider + /// Class RemoteEpisodeProvider. /// </summary> public class TvdbEpisodeProvider : IRemoteMetadataProvider<Episode, EpisodeInfo>, IHasOrder { - private readonly IHttpClient _httpClient; - private readonly ILogger _logger; + private readonly IHttpClientFactory _httpClientFactory; + private readonly ILogger<TvdbEpisodeProvider> _logger; private readonly TvdbClientManager _tvdbClientManager; - public TvdbEpisodeProvider(IHttpClient httpClient, ILogger<TvdbEpisodeProvider> logger, TvdbClientManager tvdbClientManager) + public TvdbEpisodeProvider(IHttpClientFactory httpClientFactory, ILogger<TvdbEpisodeProvider> logger, TvdbClientManager tvdbClientManager) { - _httpClient = httpClient; + _httpClientFactory = httpClientFactory; _logger = logger; _tvdbClientManager = tvdbClientManager; } @@ -95,7 +97,7 @@ namespace MediaBrowser.Providers.Plugins.TheTvdb QueriedById = true }; - string seriesTvdbId = searchInfo.GetProviderId(MetadataProviders.Tvdb); + string seriesTvdbId = searchInfo.GetProviderId(MetadataProvider.Tvdb); string episodeTvdbId = null; try { @@ -139,20 +141,26 @@ namespace MediaBrowser.Providers.Plugins.TheTvdb Name = episode.EpisodeName, Overview = episode.Overview, CommunityRating = (float?)episode.SiteRating, - } }; result.ResetPeople(); var item = result.Item; - item.SetProviderId(MetadataProviders.Tvdb, episode.Id.ToString()); - item.SetProviderId(MetadataProviders.Imdb, episode.ImdbId); + item.SetProviderId(MetadataProvider.Tvdb, episode.Id.ToString()); + item.SetProviderId(MetadataProvider.Imdb, episode.ImdbId); if (string.Equals(id.SeriesDisplayOrder, "dvd", StringComparison.OrdinalIgnoreCase)) { item.IndexNumber = Convert.ToInt32(episode.DvdEpisodeNumber ?? episode.AiredEpisodeNumber); item.ParentIndexNumber = episode.DvdSeason ?? episode.AiredSeason; } + else if (string.Equals(id.SeriesDisplayOrder, "absolute", StringComparison.OrdinalIgnoreCase)) + { + if (episode.AbsoluteNumber.GetValueOrDefault() != 0) + { + item.IndexNumber = episode.AbsoluteNumber; + } + } else if (episode.AiredEpisodeNumber.HasValue) { item.IndexNumber = episode.AiredEpisodeNumber; @@ -188,7 +196,7 @@ namespace MediaBrowser.Providers.Plugins.TheTvdb for (var i = 0; i < episode.GuestStars.Length; ++i) { var currentActor = episode.GuestStars[i]; - var roleStartIndex = currentActor.IndexOf('('); + var roleStartIndex = currentActor.IndexOf('(', StringComparison.Ordinal); if (roleStartIndex == -1) { @@ -207,7 +215,7 @@ namespace MediaBrowser.Providers.Plugins.TheTvdb for (var j = i + 1; j < episode.GuestStars.Length; ++j) { var currentRole = episode.GuestStars[j]; - var roleEndIndex = currentRole.IndexOf(')'); + var roleEndIndex = currentRole.IndexOf(')', StringComparison.Ordinal); if (roleEndIndex == -1) { @@ -242,13 +250,9 @@ namespace MediaBrowser.Providers.Plugins.TheTvdb return result; } - public Task<HttpResponseInfo> GetImageResponse(string url, CancellationToken cancellationToken) + public Task<HttpResponseMessage> GetImageResponse(string url, CancellationToken cancellationToken) { - return _httpClient.GetResponse(new HttpRequestOptions - { - CancellationToken = cancellationToken, - Url = url - }); + return _httpClientFactory.CreateClient(NamedClient.Default).GetAsync(url, cancellationToken); } public int Order => 0; diff --git a/MediaBrowser.Providers/Plugins/TheTvdb/TvdbPersonImageProvider.cs b/MediaBrowser.Providers/Plugins/TheTvdb/TvdbPersonImageProvider.cs index c1cdc90e90..dc3c60dee0 100644 --- a/MediaBrowser.Providers/Plugins/TheTvdb/TvdbPersonImageProvider.cs +++ b/MediaBrowser.Providers/Plugins/TheTvdb/TvdbPersonImageProvider.cs @@ -1,6 +1,9 @@ +#pragma warning disable CS1591 + using System; using System.Collections.Generic; using System.Linq; +using System.Net.Http; using System.Threading; using System.Threading.Tasks; using MediaBrowser.Common.Net; @@ -18,15 +21,15 @@ namespace MediaBrowser.Providers.Plugins.TheTvdb { public class TvdbPersonImageProvider : IRemoteImageProvider, IHasOrder { - private readonly IHttpClient _httpClient; - private readonly ILogger _logger; + private readonly IHttpClientFactory _httpClientFactory; + private readonly ILogger<TvdbPersonImageProvider> _logger; private readonly ILibraryManager _libraryManager; private readonly TvdbClientManager _tvdbClientManager; - public TvdbPersonImageProvider(ILibraryManager libraryManager, IHttpClient httpClient, ILogger<TvdbPersonImageProvider> logger, TvdbClientManager tvdbClientManager) + public TvdbPersonImageProvider(ILibraryManager libraryManager, IHttpClientFactory httpClientFactory, ILogger<TvdbPersonImageProvider> logger, TvdbClientManager tvdbClientManager) { _libraryManager = libraryManager; - _httpClient = httpClient; + _httpClientFactory = httpClientFactory; _logger = logger; _tvdbClientManager = tvdbClientManager; } @@ -57,7 +60,6 @@ namespace MediaBrowser.Providers.Plugins.TheTvdb { EnableImages = false } - }).Cast<Series>() .Where(i => TvdbSeriesProvider.IsValidSeries(i.ProviderIds)) .ToList(); @@ -73,7 +75,7 @@ namespace MediaBrowser.Providers.Plugins.TheTvdb private async Task<RemoteImageInfo> GetImageFromSeriesData(Series series, string personName, CancellationToken cancellationToken) { - var tvdbId = Convert.ToInt32(series.GetProviderId(MetadataProviders.Tvdb)); + var tvdbId = Convert.ToInt32(series.GetProviderId(MetadataProvider.Tvdb)); try { @@ -103,13 +105,9 @@ namespace MediaBrowser.Providers.Plugins.TheTvdb } /// <inheritdoc /> - public Task<HttpResponseInfo> GetImageResponse(string url, CancellationToken cancellationToken) + public Task<HttpResponseMessage> GetImageResponse(string url, CancellationToken cancellationToken) { - return _httpClient.GetResponse(new HttpRequestOptions - { - CancellationToken = cancellationToken, - Url = url - }); + return _httpClientFactory.CreateClient(NamedClient.Default).GetAsync(url, cancellationToken); } } } diff --git a/MediaBrowser.Providers/Plugins/TheTvdb/TvdbSeasonImageProvider.cs b/MediaBrowser.Providers/Plugins/TheTvdb/TvdbSeasonImageProvider.cs index a5d183df77..49576d488d 100644 --- a/MediaBrowser.Providers/Plugins/TheTvdb/TvdbSeasonImageProvider.cs +++ b/MediaBrowser.Providers/Plugins/TheTvdb/TvdbSeasonImageProvider.cs @@ -1,6 +1,9 @@ +#pragma warning disable CS1591 + using System; using System.Collections.Generic; using System.Linq; +using System.Net.Http; using System.Threading; using System.Threading.Tasks; using MediaBrowser.Common.Net; @@ -18,13 +21,13 @@ namespace MediaBrowser.Providers.Plugins.TheTvdb { public class TvdbSeasonImageProvider : IRemoteImageProvider, IHasOrder { - private readonly IHttpClient _httpClient; - private readonly ILogger _logger; + private readonly IHttpClientFactory _httpClientFactory; + private readonly ILogger<TvdbSeasonImageProvider> _logger; private readonly TvdbClientManager _tvdbClientManager; - public TvdbSeasonImageProvider(IHttpClient httpClient, ILogger<TvdbSeasonImageProvider> logger, TvdbClientManager tvdbClientManager) + public TvdbSeasonImageProvider(IHttpClientFactory httpClientFactory, ILogger<TvdbSeasonImageProvider> logger, TvdbClientManager tvdbClientManager) { - _httpClient = httpClient; + _httpClientFactory = httpClientFactory; _logger = logger; _tvdbClientManager = tvdbClientManager; } @@ -55,16 +58,16 @@ namespace MediaBrowser.Providers.Plugins.TheTvdb if (series == null || !season.IndexNumber.HasValue || !TvdbSeriesProvider.IsValidSeries(series.ProviderIds)) { - return new RemoteImageInfo[] { }; + return Array.Empty<RemoteImageInfo>(); } - var tvdbId = Convert.ToInt32(series.GetProviderId(MetadataProviders.Tvdb)); + var tvdbId = Convert.ToInt32(series.GetProviderId(MetadataProvider.Tvdb)); var seasonNumber = season.IndexNumber.Value; var language = item.GetPreferredMetadataLanguage(); var remoteImages = new List<RemoteImageInfo>(); - var keyTypes = new[] { KeyType.Season, KeyType.Seasonwide, KeyType.Fanart }; - foreach (var keyType in keyTypes) + var keyTypes = _tvdbClientManager.GetImageKeyTypesForSeasonAsync(tvdbId, language, cancellationToken).ConfigureAwait(false); + await foreach (var keyType in keyTypes) { var imageQuery = new ImagesQuery { @@ -89,7 +92,8 @@ namespace MediaBrowser.Providers.Plugins.TheTvdb private IEnumerable<RemoteImageInfo> GetImages(Image[] images, string preferredLanguage) { var list = new List<RemoteImageInfo>(); - var languages = _tvdbClientManager.GetLanguagesAsync(CancellationToken.None).Result.Data; + // any languages with null ids are ignored + var languages = _tvdbClientManager.GetLanguagesAsync(CancellationToken.None).Result.Data.Where(x => x.Id.HasValue); foreach (Image image in images) { var imageInfo = new RemoteImageInfo @@ -113,8 +117,8 @@ namespace MediaBrowser.Providers.Plugins.TheTvdb imageInfo.Type = TvdbUtils.GetImageTypeFromKeyType(image.KeyType); list.Add(imageInfo); } - var isLanguageEn = string.Equals(preferredLanguage, "en", StringComparison.OrdinalIgnoreCase); + var isLanguageEn = string.Equals(preferredLanguage, "en", StringComparison.OrdinalIgnoreCase); return list.OrderByDescending(i => { if (string.Equals(preferredLanguage, i.Language, StringComparison.OrdinalIgnoreCase)) @@ -143,13 +147,9 @@ namespace MediaBrowser.Providers.Plugins.TheTvdb public int Order => 0; - public Task<HttpResponseInfo> GetImageResponse(string url, CancellationToken cancellationToken) + public Task<HttpResponseMessage> GetImageResponse(string url, CancellationToken cancellationToken) { - return _httpClient.GetResponse(new HttpRequestOptions - { - CancellationToken = cancellationToken, - Url = url - }); + return _httpClientFactory.CreateClient(NamedClient.Default).GetAsync(url, cancellationToken); } } } diff --git a/MediaBrowser.Providers/Plugins/TheTvdb/TvdbSeriesImageProvider.cs b/MediaBrowser.Providers/Plugins/TheTvdb/TvdbSeriesImageProvider.cs index 1bad607565..d96840e51c 100644 --- a/MediaBrowser.Providers/Plugins/TheTvdb/TvdbSeriesImageProvider.cs +++ b/MediaBrowser.Providers/Plugins/TheTvdb/TvdbSeriesImageProvider.cs @@ -1,6 +1,9 @@ +#pragma warning disable CS1591 + using System; using System.Collections.Generic; using System.Linq; +using System.Net.Http; using System.Threading; using System.Threading.Tasks; using MediaBrowser.Common.Net; @@ -18,13 +21,13 @@ namespace MediaBrowser.Providers.Plugins.TheTvdb { public class TvdbSeriesImageProvider : IRemoteImageProvider, IHasOrder { - private readonly IHttpClient _httpClient; - private readonly ILogger _logger; + private readonly IHttpClientFactory _httpClientFactory; + private readonly ILogger<TvdbSeriesImageProvider> _logger; private readonly TvdbClientManager _tvdbClientManager; - public TvdbSeriesImageProvider(IHttpClient httpClient, ILogger<TvdbSeriesImageProvider> logger, TvdbClientManager tvdbClientManager) + public TvdbSeriesImageProvider(IHttpClientFactory httpClientFactory, ILogger<TvdbSeriesImageProvider> logger, TvdbClientManager tvdbClientManager) { - _httpClient = httpClient; + _httpClientFactory = httpClientFactory; _logger = logger; _tvdbClientManager = tvdbClientManager; } @@ -57,9 +60,10 @@ namespace MediaBrowser.Providers.Plugins.TheTvdb var language = item.GetPreferredMetadataLanguage(); var remoteImages = new List<RemoteImageInfo>(); - var keyTypes = new[] { KeyType.Poster, KeyType.Series, KeyType.Fanart }; - var tvdbId = Convert.ToInt32(item.GetProviderId(MetadataProviders.Tvdb)); - foreach (KeyType keyType in keyTypes) + var tvdbId = Convert.ToInt32(item.GetProviderId(MetadataProvider.Tvdb)); + var allowedKeyTypes = _tvdbClientManager.GetImageKeyTypesForSeriesAsync(tvdbId, language, cancellationToken) + .ConfigureAwait(false); + await foreach (KeyType keyType in allowedKeyTypes) { var imageQuery = new ImagesQuery { @@ -79,6 +83,7 @@ namespace MediaBrowser.Providers.Plugins.TheTvdb tvdbId); } } + return remoteImages; } @@ -110,8 +115,8 @@ namespace MediaBrowser.Providers.Plugins.TheTvdb imageInfo.Type = TvdbUtils.GetImageTypeFromKeyType(image.KeyType); list.Add(imageInfo); } - var isLanguageEn = string.Equals(preferredLanguage, "en", StringComparison.OrdinalIgnoreCase); + var isLanguageEn = string.Equals(preferredLanguage, "en", StringComparison.OrdinalIgnoreCase); return list.OrderByDescending(i => { if (string.Equals(preferredLanguage, i.Language, StringComparison.OrdinalIgnoreCase)) @@ -140,13 +145,9 @@ namespace MediaBrowser.Providers.Plugins.TheTvdb public int Order => 0; - public Task<HttpResponseInfo> GetImageResponse(string url, CancellationToken cancellationToken) + public Task<HttpResponseMessage> GetImageResponse(string url, CancellationToken cancellationToken) { - return _httpClient.GetResponse(new HttpRequestOptions - { - CancellationToken = cancellationToken, - Url = url - }); + return _httpClientFactory.CreateClient(NamedClient.Default).GetAsync(url, cancellationToken); } } } diff --git a/MediaBrowser.Providers/Plugins/TheTvdb/TvdbSeriesProvider.cs b/MediaBrowser.Providers/Plugins/TheTvdb/TvdbSeriesProvider.cs index f6cd249f51..ca9b1d738f 100644 --- a/MediaBrowser.Providers/Plugins/TheTvdb/TvdbSeriesProvider.cs +++ b/MediaBrowser.Providers/Plugins/TheTvdb/TvdbSeriesProvider.cs @@ -1,6 +1,9 @@ +#pragma warning disable CS1591 + using System; using System.Collections.Generic; using System.Linq; +using System.Net.Http; using System.Text; using System.Text.RegularExpressions; using System.Threading; @@ -22,15 +25,16 @@ namespace MediaBrowser.Providers.Plugins.TheTvdb public class TvdbSeriesProvider : IRemoteMetadataProvider<Series, SeriesInfo>, IHasOrder { internal static TvdbSeriesProvider Current { get; private set; } - private readonly IHttpClient _httpClient; - private readonly ILogger _logger; + + private readonly IHttpClientFactory _httpClientFactory; + private readonly ILogger<TvdbSeriesProvider> _logger; private readonly ILibraryManager _libraryManager; private readonly ILocalizationManager _localizationManager; private readonly TvdbClientManager _tvdbClientManager; - public TvdbSeriesProvider(IHttpClient httpClient, ILogger<TvdbSeriesProvider> logger, ILibraryManager libraryManager, ILocalizationManager localizationManager, TvdbClientManager tvdbClientManager) + public TvdbSeriesProvider(IHttpClientFactory httpClientFactory, ILogger<TvdbSeriesProvider> logger, ILibraryManager libraryManager, ILocalizationManager localizationManager, TvdbClientManager tvdbClientManager) { - _httpClient = httpClient; + _httpClientFactory = httpClientFactory; _logger = logger; _libraryManager = libraryManager; _localizationManager = localizationManager; @@ -94,22 +98,22 @@ namespace MediaBrowser.Providers.Plugins.TheTvdb { var series = result.Item; - if (seriesProviderIds.TryGetValue(MetadataProviders.Tvdb.ToString(), out var tvdbId) && !string.IsNullOrEmpty(tvdbId)) + if (seriesProviderIds.TryGetValue(MetadataProvider.Tvdb.ToString(), out var tvdbId) && !string.IsNullOrEmpty(tvdbId)) { - series.SetProviderId(MetadataProviders.Tvdb, tvdbId); + series.SetProviderId(MetadataProvider.Tvdb, tvdbId); } - if (seriesProviderIds.TryGetValue(MetadataProviders.Imdb.ToString(), out var imdbId) && !string.IsNullOrEmpty(imdbId)) + if (seriesProviderIds.TryGetValue(MetadataProvider.Imdb.ToString(), out var imdbId) && !string.IsNullOrEmpty(imdbId)) { - series.SetProviderId(MetadataProviders.Imdb, imdbId); - tvdbId = await GetSeriesByRemoteId(imdbId, MetadataProviders.Imdb.ToString(), metadataLanguage, + series.SetProviderId(MetadataProvider.Imdb, imdbId); + tvdbId = await GetSeriesByRemoteId(imdbId, MetadataProvider.Imdb.ToString(), metadataLanguage, cancellationToken).ConfigureAwait(false); } - if (seriesProviderIds.TryGetValue(MetadataProviders.Zap2It.ToString(), out var zap2It) && !string.IsNullOrEmpty(zap2It)) + if (seriesProviderIds.TryGetValue(MetadataProvider.Zap2It.ToString(), out var zap2It) && !string.IsNullOrEmpty(zap2It)) { - series.SetProviderId(MetadataProviders.Zap2It, zap2It); - tvdbId = await GetSeriesByRemoteId(zap2It, MetadataProviders.Zap2It.ToString(), metadataLanguage, + series.SetProviderId(MetadataProvider.Zap2It, zap2It); + tvdbId = await GetSeriesByRemoteId(zap2It, MetadataProvider.Zap2It.ToString(), metadataLanguage, cancellationToken).ConfigureAwait(false); } @@ -145,12 +149,11 @@ namespace MediaBrowser.Providers.Plugins.TheTvdb private async Task<string> GetSeriesByRemoteId(string id, string idType, string language, CancellationToken cancellationToken) { - TvDbResponse<SeriesSearchResult[]> result = null; try { - if (string.Equals(idType, MetadataProviders.Zap2It.ToString(), StringComparison.OrdinalIgnoreCase)) + if (string.Equals(idType, MetadataProvider.Zap2It.ToString(), StringComparison.OrdinalIgnoreCase)) { result = await _tvdbClientManager.GetSeriesByZap2ItIdAsync(id, language, cancellationToken) .ConfigureAwait(false); @@ -176,9 +179,9 @@ namespace MediaBrowser.Providers.Plugins.TheTvdb /// <returns>True, if the dictionary contains a valid TV provider ID, otherwise false.</returns> internal static bool IsValidSeries(Dictionary<string, string> seriesProviderIds) { - return seriesProviderIds.ContainsKey(MetadataProviders.Tvdb.ToString()) || - seriesProviderIds.ContainsKey(MetadataProviders.Imdb.ToString()) || - seriesProviderIds.ContainsKey(MetadataProviders.Zap2It.ToString()); + return seriesProviderIds.ContainsKey(MetadataProvider.Tvdb.ToString()) || + seriesProviderIds.ContainsKey(MetadataProvider.Imdb.ToString()) || + seriesProviderIds.ContainsKey(MetadataProvider.Zap2It.ToString()); } /// <summary> @@ -245,24 +248,29 @@ namespace MediaBrowser.Providers.Plugins.TheTvdb { Name = tvdbTitles.FirstOrDefault(), ProductionYear = firstAired.Year, - SearchProviderName = Name, - ImageUrl = TvdbUtils.BannerUrl + seriesSearchResult.Banner - + SearchProviderName = Name }; + + if (!string.IsNullOrEmpty(seriesSearchResult.Banner)) + { + // Results from their Search endpoints already include the /banners/ part in the url, because reasons... + remoteSearchResult.ImageUrl = TvdbUtils.TvdbImageBaseUrl + seriesSearchResult.Banner; + } + try { var seriesSesult = await _tvdbClientManager.GetSeriesByIdAsync(seriesSearchResult.Id, language, cancellationToken) .ConfigureAwait(false); - remoteSearchResult.SetProviderId(MetadataProviders.Imdb, seriesSesult.Data.ImdbId); - remoteSearchResult.SetProviderId(MetadataProviders.Zap2It, seriesSesult.Data.Zap2itId); + remoteSearchResult.SetProviderId(MetadataProvider.Imdb, seriesSesult.Data.ImdbId); + remoteSearchResult.SetProviderId(MetadataProvider.Zap2It, seriesSesult.Data.Zap2itId); } catch (TvDbServerException e) { _logger.LogError(e, "Unable to retrieve series with id {TvdbId}", seriesSearchResult.Id); } - remoteSearchResult.SetProviderId(MetadataProviders.Tvdb, seriesSearchResult.Id.ToString()); + remoteSearchResult.SetProviderId(MetadataProvider.Tvdb, seriesSearchResult.Id.ToString()); list.Add(new Tuple<List<string>, RemoteSearchResult>(tvdbTitles, remoteSearchResult)); } @@ -274,15 +282,6 @@ namespace MediaBrowser.Providers.Plugins.TheTvdb } /// <summary> - /// The remove - /// </summary> - const string remove = "\"'!`?"; - /// <summary> - /// The spacers - /// </summary> - const string spacers = "/,.:;\\(){}[]+-_=–*"; // (there are two types of dashes, short and long) - - /// <summary> /// Gets the name of the comparable. /// </summary> /// <param name="name">The name.</param> @@ -291,47 +290,25 @@ namespace MediaBrowser.Providers.Plugins.TheTvdb { name = name.ToLowerInvariant(); name = name.Normalize(NormalizationForm.FormKD); - var sb = new StringBuilder(); - foreach (var c in name) - { - if (c >= 0x2B0 && c <= 0x0333) - { - // skip char modifier and diacritics - } - else if (remove.IndexOf(c) > -1) - { - // skip chars we are removing - } - else if (spacers.IndexOf(c) > -1) - { - sb.Append(" "); - } - else if (c == '&') - { - sb.Append(" and "); - } - else - { - sb.Append(c); - } - } - sb.Replace(", the", string.Empty).Replace("the ", " ").Replace(" the ", " "); - - return Regex.Replace(sb.ToString().Trim(), @"\s+", " "); + name = name.Replace(", the", string.Empty).Replace("the ", " ").Replace(" the ", " "); + name = name.Replace("&", " and " ); + name = Regex.Replace(name, @"[\p{Lm}\p{Mn}]", string.Empty); // Remove diacritics, etc + name = Regex.Replace(name, @"[\W\p{Pc}]+", " "); // Replace sequences of non-word characters and _ with " " + return name.Trim(); } private void MapSeriesToResult(MetadataResult<Series> result, TvDbSharper.Dto.Series tvdbSeries, string metadataLanguage) { Series series = result.Item; - series.SetProviderId(MetadataProviders.Tvdb, tvdbSeries.Id.ToString()); + series.SetProviderId(MetadataProvider.Tvdb, tvdbSeries.Id.ToString()); series.Name = tvdbSeries.SeriesName; series.Overview = (tvdbSeries.Overview ?? string.Empty).Trim(); result.ResultLanguage = metadataLanguage; series.AirDays = TVUtils.GetAirDays(tvdbSeries.AirsDayOfWeek); series.AirTime = tvdbSeries.AirsTime; series.CommunityRating = (float?)tvdbSeries.SiteRating; - series.SetProviderId(MetadataProviders.Imdb, tvdbSeries.ImdbId); - series.SetProviderId(MetadataProviders.Zap2It, tvdbSeries.Zap2itId); + series.SetProviderId(MetadataProvider.Imdb, tvdbSeries.ImdbId); + series.SetProviderId(MetadataProvider.Zap2It, tvdbSeries.Zap2itId); if (Enum.TryParse(tvdbSeries.Status, true, out SeriesStatus seriesStatus)) { series.Status = seriesStatus; @@ -394,10 +371,14 @@ namespace MediaBrowser.Providers.Plugins.TheTvdb Type = PersonType.Actor, Name = (actor.Name ?? string.Empty).Trim(), Role = actor.Role, - ImageUrl = TvdbUtils.BannerUrl + actor.Image, SortOrder = actor.SortOrder }; + if (!string.IsNullOrEmpty(actor.Image)) + { + personInfo.ImageUrl = TvdbUtils.BannerUrl + actor.Image; + } + if (!string.IsNullOrWhiteSpace(personInfo.Name)) { result.AddPerson(personInfo); @@ -409,7 +390,7 @@ namespace MediaBrowser.Providers.Plugins.TheTvdb public async Task Identify(SeriesInfo info) { - if (!string.IsNullOrWhiteSpace(info.GetProviderId(MetadataProviders.Tvdb))) + if (!string.IsNullOrWhiteSpace(info.GetProviderId(MetadataProvider.Tvdb))) { return; } @@ -421,21 +402,16 @@ namespace MediaBrowser.Providers.Plugins.TheTvdb if (entry != null) { - var id = entry.GetProviderId(MetadataProviders.Tvdb); - info.SetProviderId(MetadataProviders.Tvdb, id); + var id = entry.GetProviderId(MetadataProvider.Tvdb); + info.SetProviderId(MetadataProvider.Tvdb, id); } } public int Order => 0; - public Task<HttpResponseInfo> GetImageResponse(string url, CancellationToken cancellationToken) + public Task<HttpResponseMessage> GetImageResponse(string url, CancellationToken cancellationToken) { - return _httpClient.GetResponse(new HttpRequestOptions - { - CancellationToken = cancellationToken, - Url = url, - BufferContent = false - }); + return _httpClientFactory.CreateClient(NamedClient.Default).GetAsync(url, cancellationToken); } } } diff --git a/MediaBrowser.Providers/Plugins/TheTvdb/TvdbUtils.cs b/MediaBrowser.Providers/Plugins/TheTvdb/TvdbUtils.cs index 79d879aa1b..37a8d04a6f 100644 --- a/MediaBrowser.Providers/Plugins/TheTvdb/TvdbUtils.cs +++ b/MediaBrowser.Providers/Plugins/TheTvdb/TvdbUtils.cs @@ -1,3 +1,5 @@ +#pragma warning disable CS1591 + using System; using MediaBrowser.Model.Entities; @@ -7,7 +9,8 @@ namespace MediaBrowser.Providers.Plugins.TheTvdb { public const string TvdbApiKey = "OG4V3YJ3FAP7FP2K"; public const string TvdbBaseUrl = "https://www.thetvdb.com/"; - public const string BannerUrl = TvdbBaseUrl + "banners/"; + public const string TvdbImageBaseUrl = "https://www.thetvdb.com"; + public const string BannerUrl = TvdbImageBaseUrl + "/banners/"; public static ImageType GetImageTypeFromKeyType(string keyType) { diff --git a/MediaBrowser.Providers/Tmdb/BoxSets/TmdbBoxSetExternalId.cs b/MediaBrowser.Providers/Plugins/Tmdb/BoxSets/TmdbBoxSetExternalId.cs index 187295e1e4..1f7ec64338 100644 --- a/MediaBrowser.Providers/Tmdb/BoxSets/TmdbBoxSetExternalId.cs +++ b/MediaBrowser.Providers/Plugins/Tmdb/BoxSets/TmdbBoxSetExternalId.cs @@ -2,16 +2,23 @@ using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities.Movies; using MediaBrowser.Controller.Providers; using MediaBrowser.Model.Entities; +using MediaBrowser.Model.Providers; -namespace MediaBrowser.Providers.Tmdb.BoxSets +namespace MediaBrowser.Providers.Plugins.Tmdb.BoxSets { + /// <summary> + /// External ID for a TMDB box set. + /// </summary> public class TmdbBoxSetExternalId : IExternalId { /// <inheritdoc /> - public string Name => TmdbUtils.ProviderName; + public string ProviderName => TmdbUtils.ProviderName; /// <inheritdoc /> - public string Key => MetadataProviders.TmdbCollection.ToString(); + public string Key => MetadataProvider.TmdbCollection.ToString(); + + /// <inheritdoc /> + public ExternalIdMediaType? Type => ExternalIdMediaType.BoxSet; /// <inheritdoc /> public string UrlFormatString => TmdbUtils.BaseTmdbUrl + "collection/{0}"; diff --git a/MediaBrowser.Providers/Tmdb/BoxSets/TmdbBoxSetImageProvider.cs b/MediaBrowser.Providers/Plugins/Tmdb/BoxSets/TmdbBoxSetImageProvider.cs index 0bdf2bce1e..f6592afe46 100644 --- a/MediaBrowser.Providers/Tmdb/BoxSets/TmdbBoxSetImageProvider.cs +++ b/MediaBrowser.Providers/Plugins/Tmdb/BoxSets/TmdbBoxSetImageProvider.cs @@ -1,6 +1,9 @@ +#pragma warning disable CS1591 + using System; using System.Collections.Generic; using System.Linq; +using System.Net.Http; using System.Threading; using System.Threading.Tasks; using MediaBrowser.Common.Net; @@ -10,19 +13,19 @@ using MediaBrowser.Controller.Providers; using MediaBrowser.Model.Dto; using MediaBrowser.Model.Entities; using MediaBrowser.Model.Providers; -using MediaBrowser.Providers.Tmdb.Models.Collections; -using MediaBrowser.Providers.Tmdb.Models.General; -using MediaBrowser.Providers.Tmdb.Movies; +using MediaBrowser.Providers.Plugins.Tmdb.Models.Collections; +using MediaBrowser.Providers.Plugins.Tmdb.Models.General; +using MediaBrowser.Providers.Plugins.Tmdb.Movies; -namespace MediaBrowser.Providers.Tmdb.BoxSets +namespace MediaBrowser.Providers.Plugins.Tmdb.BoxSets { public class TmdbBoxSetImageProvider : IRemoteImageProvider, IHasOrder { - private readonly IHttpClient _httpClient; + private readonly IHttpClientFactory _httpClientFactory; - public TmdbBoxSetImageProvider(IHttpClient httpClient) + public TmdbBoxSetImageProvider(IHttpClientFactory httpClientFactory) { - _httpClient = httpClient; + _httpClientFactory = httpClientFactory; } public string Name => ProviderName; @@ -45,7 +48,7 @@ namespace MediaBrowser.Providers.Tmdb.BoxSets public async Task<IEnumerable<RemoteImageInfo>> GetImages(BaseItem item, CancellationToken cancellationToken) { - var tmdbId = item.GetProviderId(MetadataProviders.Tmdb); + var tmdbId = item.GetProviderId(MetadataProvider.Tmdb); if (!string.IsNullOrEmpty(tmdbId)) { @@ -105,6 +108,7 @@ namespace MediaBrowser.Providers.Tmdb.BoxSets { return 3; } + if (!isLanguageEn) { if (string.Equals("en", i.Language, StringComparison.OrdinalIgnoreCase)) @@ -112,10 +116,12 @@ namespace MediaBrowser.Providers.Tmdb.BoxSets return 2; } } + if (string.IsNullOrEmpty(i.Language)) { return isLanguageEn ? 3 : 2; } + return 0; }) .ThenByDescending(i => i.CommunityRating ?? 0) @@ -148,13 +154,9 @@ namespace MediaBrowser.Providers.Tmdb.BoxSets public int Order => 0; - public Task<HttpResponseInfo> GetImageResponse(string url, CancellationToken cancellationToken) + public Task<HttpResponseMessage> GetImageResponse(string url, CancellationToken cancellationToken) { - return _httpClient.GetResponse(new HttpRequestOptions - { - CancellationToken = cancellationToken, - Url = url - }); + return _httpClientFactory.CreateClient(NamedClient.Default).GetAsync(url, cancellationToken); } } } diff --git a/MediaBrowser.Providers/Tmdb/BoxSets/TmdbBoxSetProvider.cs b/MediaBrowser.Providers/Plugins/Tmdb/BoxSets/TmdbBoxSetProvider.cs index dd3783ffbf..e7328b5535 100644 --- a/MediaBrowser.Providers/Tmdb/BoxSets/TmdbBoxSetProvider.cs +++ b/MediaBrowser.Providers/Plugins/Tmdb/BoxSets/TmdbBoxSetProvider.cs @@ -1,8 +1,12 @@ +#pragma warning disable CS1591 + using System; using System.Collections.Generic; using System.Globalization; using System.IO; using System.Linq; +using System.Net.Http; +using System.Net.Http.Headers; using System.Threading; using System.Threading.Tasks; using MediaBrowser.Common.Configuration; @@ -16,12 +20,12 @@ using MediaBrowser.Model.Globalization; using MediaBrowser.Model.IO; using MediaBrowser.Model.Providers; using MediaBrowser.Model.Serialization; -using MediaBrowser.Providers.Tmdb.Models.Collections; -using MediaBrowser.Providers.Tmdb.Models.General; -using MediaBrowser.Providers.Tmdb.Movies; +using MediaBrowser.Providers.Plugins.Tmdb.Models.Collections; +using MediaBrowser.Providers.Plugins.Tmdb.Models.General; +using MediaBrowser.Providers.Plugins.Tmdb.Movies; using Microsoft.Extensions.Logging; -namespace MediaBrowser.Providers.Tmdb.BoxSets +namespace MediaBrowser.Providers.Plugins.Tmdb.BoxSets { public class TmdbBoxSetProvider : IRemoteMetadataProvider<BoxSet, BoxSetInfo> { @@ -29,12 +33,11 @@ namespace MediaBrowser.Providers.Tmdb.BoxSets internal static TmdbBoxSetProvider Current; - private readonly ILogger _logger; + private readonly ILogger<TmdbBoxSetProvider> _logger; private readonly IJsonSerializer _json; private readonly IServerConfigurationManager _config; private readonly IFileSystem _fileSystem; - private readonly ILocalizationManager _localization; - private readonly IHttpClient _httpClient; + private readonly IHttpClientFactory _httpClientFactory; private readonly ILibraryManager _libraryManager; public TmdbBoxSetProvider( @@ -42,16 +45,14 @@ namespace MediaBrowser.Providers.Tmdb.BoxSets IJsonSerializer json, IServerConfigurationManager config, IFileSystem fileSystem, - ILocalizationManager localization, - IHttpClient httpClient, + IHttpClientFactory httpClientFactory, ILibraryManager libraryManager) { _logger = logger; _json = json; _config = config; _fileSystem = fileSystem; - _localization = localization; - _httpClient = httpClient; + _httpClientFactory = httpClientFactory; _libraryManager = libraryManager; Current = this; } @@ -60,7 +61,7 @@ namespace MediaBrowser.Providers.Tmdb.BoxSets public async Task<IEnumerable<RemoteSearchResult>> GetSearchResults(BoxSetInfo searchInfo, CancellationToken cancellationToken) { - var tmdbId = searchInfo.GetProviderId(MetadataProviders.Tmdb); + var tmdbId = searchInfo.GetProviderId(MetadataProvider.Tmdb); if (!string.IsNullOrEmpty(tmdbId)) { @@ -78,13 +79,11 @@ namespace MediaBrowser.Providers.Tmdb.BoxSets var result = new RemoteSearchResult { Name = info.Name, - SearchProviderName = Name, - ImageUrl = images.Count == 0 ? null : (tmdbImageUrl + images[0].File_Path) }; - result.SetProviderId(MetadataProviders.Tmdb, info.Id.ToString(_usCulture)); + result.SetProviderId(MetadataProvider.Tmdb, info.Id.ToString(_usCulture)); return new[] { result }; } @@ -94,7 +93,7 @@ namespace MediaBrowser.Providers.Tmdb.BoxSets public async Task<MetadataResult<BoxSet>> GetMetadata(BoxSetInfo id, CancellationToken cancellationToken) { - var tmdbId = id.GetProviderId(MetadataProviders.Tmdb); + var tmdbId = id.GetProviderId(MetadataProvider.Tmdb); // We don't already have an Id, need to fetch it if (string.IsNullOrEmpty(tmdbId)) @@ -105,7 +104,7 @@ namespace MediaBrowser.Providers.Tmdb.BoxSets if (searchResult != null) { - tmdbId = searchResult.GetProviderId(MetadataProviders.Tmdb); + tmdbId = searchResult.GetProviderId(MetadataProvider.Tmdb); } } @@ -152,7 +151,7 @@ namespace MediaBrowser.Providers.Tmdb.BoxSets Overview = obj.Overview }; - item.SetProviderId(MetadataProviders.Tmdb, obj.Id.ToString(_usCulture)); + item.SetProviderId(MetadataProvider.Tmdb, obj.Id.ToString(_usCulture)); return item; } @@ -161,7 +160,10 @@ namespace MediaBrowser.Providers.Tmdb.BoxSets { var mainResult = await FetchMainResult(tmdbId, preferredMetadataLanguage, cancellationToken).ConfigureAwait(false); - if (mainResult == null) return; + if (mainResult == null) + { + return; + } var dataFilePath = GetDataFilePath(_config.ApplicationPaths, tmdbId, preferredMetadataLanguage); @@ -172,11 +174,11 @@ namespace MediaBrowser.Providers.Tmdb.BoxSets private async Task<CollectionResult> FetchMainResult(string id, string language, CancellationToken cancellationToken) { - var url = string.Format(GetCollectionInfo3, id, TmdbUtils.ApiKey); + var url = string.Format(CultureInfo.InvariantCulture, GetCollectionInfo3, id, TmdbUtils.ApiKey); if (!string.IsNullOrEmpty(language)) { - url += string.Format("&language={0}", TmdbMovieProvider.NormalizeLanguage(language)); + url += string.Format(CultureInfo.InvariantCulture, "&language={0}", TmdbMovieProvider.NormalizeLanguage(language)); // Get images in english and with no language url += "&include_image_language=" + TmdbMovieProvider.GetImageLanguagesParam(language); @@ -184,29 +186,23 @@ namespace MediaBrowser.Providers.Tmdb.BoxSets cancellationToken.ThrowIfCancellationRequested(); - CollectionResult mainResult; - - using (var response = await TmdbMovieProvider.Current.GetMovieDbResponse(new HttpRequestOptions - { - Url = url, - CancellationToken = cancellationToken, - AcceptHeader = TmdbUtils.AcceptHeader - - }).ConfigureAwait(false)) + using var requestMessage = new HttpRequestMessage(HttpMethod.Get, url); + foreach (var header in TmdbUtils.AcceptHeaders) { - using (var json = response.Content) - { - mainResult = await _json.DeserializeFromStreamAsync<CollectionResult>(json).ConfigureAwait(false); - } + requestMessage.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue(header)); } + using var mainResponse = await TmdbMovieProvider.Current.GetMovieDbResponse(requestMessage, cancellationToken).ConfigureAwait(false); + await using var stream = await mainResponse.Content.ReadAsStreamAsync().ConfigureAwait(false); + var mainResult = await _json.DeserializeFromStreamAsync<CollectionResult>(stream).ConfigureAwait(false); + cancellationToken.ThrowIfCancellationRequested(); if (mainResult != null && string.IsNullOrEmpty(mainResult.Name)) { if (!string.IsNullOrEmpty(language) && !string.Equals(language, "en", StringComparison.OrdinalIgnoreCase)) { - url = string.Format(GetCollectionInfo3, id, TmdbUtils.ApiKey) + "&language=en"; + url = string.Format(CultureInfo.InvariantCulture, GetCollectionInfo3, id, TmdbUtils.ApiKey) + "&language=en"; if (!string.IsNullOrEmpty(language)) { @@ -214,21 +210,17 @@ namespace MediaBrowser.Providers.Tmdb.BoxSets url += "&include_image_language=" + TmdbMovieProvider.GetImageLanguagesParam(language); } - using (var response = await TmdbMovieProvider.Current.GetMovieDbResponse(new HttpRequestOptions - { - Url = url, - CancellationToken = cancellationToken, - AcceptHeader = TmdbUtils.AcceptHeader - - }).ConfigureAwait(false)) + using var langRequestMessage = new HttpRequestMessage(HttpMethod.Get, url); + foreach (var header in TmdbUtils.AcceptHeaders) { - using (var json = response.Content) - { - mainResult = await _json.DeserializeFromStreamAsync<CollectionResult>(json).ConfigureAwait(false); - } + langRequestMessage.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue(header)); } + + await using var langStream = await mainResponse.Content.ReadAsStreamAsync().ConfigureAwait(false); + mainResult = await _json.DeserializeFromStreamAsync<CollectionResult>(langStream).ConfigureAwait(false); } } + return mainResult; } @@ -256,7 +248,7 @@ namespace MediaBrowser.Providers.Tmdb.BoxSets { var path = GetDataPath(appPaths, tmdbId); - var filename = string.Format("all-{0}.json", preferredLanguage ?? string.Empty); + var filename = string.Format(CultureInfo.InvariantCulture, "all-{0}.json", preferredLanguage ?? string.Empty); return Path.Combine(path, filename); } @@ -275,13 +267,9 @@ namespace MediaBrowser.Providers.Tmdb.BoxSets return dataPath; } - public Task<HttpResponseInfo> GetImageResponse(string url, CancellationToken cancellationToken) + public Task<HttpResponseMessage> GetImageResponse(string url, CancellationToken cancellationToken) { - return _httpClient.GetResponse(new HttpRequestOptions - { - CancellationToken = cancellationToken, - Url = url - }); + return _httpClientFactory.CreateClient(NamedClient.Default).GetAsync(url, cancellationToken); } } } diff --git a/MediaBrowser.Providers/Tmdb/Models/Collections/CollectionImages.cs b/MediaBrowser.Providers/Plugins/Tmdb/Models/Collections/CollectionImages.cs index 18f26c3977..0a8994d540 100644 --- a/MediaBrowser.Providers/Tmdb/Models/Collections/CollectionImages.cs +++ b/MediaBrowser.Providers/Plugins/Tmdb/Models/Collections/CollectionImages.cs @@ -1,11 +1,14 @@ +#pragma warning disable CS1591 + using System.Collections.Generic; -using MediaBrowser.Providers.Tmdb.Models.General; +using MediaBrowser.Providers.Plugins.Tmdb.Models.General; -namespace MediaBrowser.Providers.Tmdb.Models.Collections +namespace MediaBrowser.Providers.Plugins.Tmdb.Models.Collections { public class CollectionImages { public List<Backdrop> Backdrops { get; set; } + public List<Poster> Posters { get; set; } } } diff --git a/MediaBrowser.Providers/Tmdb/Models/Collections/CollectionResult.cs b/MediaBrowser.Providers/Plugins/Tmdb/Models/Collections/CollectionResult.cs index 53d2599f84..c6b851c237 100644 --- a/MediaBrowser.Providers/Tmdb/Models/Collections/CollectionResult.cs +++ b/MediaBrowser.Providers/Plugins/Tmdb/Models/Collections/CollectionResult.cs @@ -1,15 +1,23 @@ +#pragma warning disable CS1591 + using System.Collections.Generic; -namespace MediaBrowser.Providers.Tmdb.Models.Collections +namespace MediaBrowser.Providers.Plugins.Tmdb.Models.Collections { public class CollectionResult { public int Id { get; set; } + public string Name { get; set; } + public string Overview { get; set; } + public string Poster_Path { get; set; } + public string Backdrop_Path { get; set; } + public List<Part> Parts { get; set; } + public CollectionImages Images { get; set; } } } diff --git a/MediaBrowser.Providers/Tmdb/Models/Collections/Part.cs b/MediaBrowser.Providers/Plugins/Tmdb/Models/Collections/Part.cs index ff19291c74..a48124b3e1 100644 --- a/MediaBrowser.Providers/Tmdb/Models/Collections/Part.cs +++ b/MediaBrowser.Providers/Plugins/Tmdb/Models/Collections/Part.cs @@ -1,11 +1,17 @@ -namespace MediaBrowser.Providers.Tmdb.Models.Collections +#pragma warning disable CS1591 + +namespace MediaBrowser.Providers.Plugins.Tmdb.Models.Collections { public class Part { public string Title { get; set; } + public int Id { get; set; } + public string Release_Date { get; set; } + public string Poster_Path { get; set; } + public string Backdrop_Path { get; set; } } } diff --git a/MediaBrowser.Providers/Tmdb/Models/General/Backdrop.cs b/MediaBrowser.Providers/Plugins/Tmdb/Models/General/Backdrop.cs index db4cd66816..5b7627f6e8 100644 --- a/MediaBrowser.Providers/Tmdb/Models/General/Backdrop.cs +++ b/MediaBrowser.Providers/Plugins/Tmdb/Models/General/Backdrop.cs @@ -1,13 +1,21 @@ -namespace MediaBrowser.Providers.Tmdb.Models.General +#pragma warning disable CS1591 + +namespace MediaBrowser.Providers.Plugins.Tmdb.Models.General { public class Backdrop { public double Aspect_Ratio { get; set; } + public string File_Path { get; set; } + public int Height { get; set; } + public string Iso_639_1 { get; set; } + public double Vote_Average { get; set; } + public int Vote_Count { get; set; } + public int Width { get; set; } } } diff --git a/MediaBrowser.Providers/Tmdb/Models/General/Crew.cs b/MediaBrowser.Providers/Plugins/Tmdb/Models/General/Crew.cs index 47b9854030..339ecb6285 100644 --- a/MediaBrowser.Providers/Tmdb/Models/General/Crew.cs +++ b/MediaBrowser.Providers/Plugins/Tmdb/Models/General/Crew.cs @@ -1,12 +1,19 @@ -namespace MediaBrowser.Providers.Tmdb.Models.General +#pragma warning disable CS1591 + +namespace MediaBrowser.Providers.Plugins.Tmdb.Models.General { public class Crew { public int Id { get; set; } + public string Credit_Id { get; set; } + public string Name { get; set; } + public string Department { get; set; } + public string Job { get; set; } + public string Profile_Path { get; set; } } } diff --git a/MediaBrowser.Providers/Tmdb/Models/General/ExternalIds.cs b/MediaBrowser.Providers/Plugins/Tmdb/Models/General/ExternalIds.cs index 37e37b0bed..aac4420e8b 100644 --- a/MediaBrowser.Providers/Tmdb/Models/General/ExternalIds.cs +++ b/MediaBrowser.Providers/Plugins/Tmdb/Models/General/ExternalIds.cs @@ -1,11 +1,17 @@ -namespace MediaBrowser.Providers.Tmdb.Models.General +#pragma warning disable CS1591 + +namespace MediaBrowser.Providers.Plugins.Tmdb.Models.General { public class ExternalIds { public string Imdb_Id { get; set; } + public object Freebase_Id { get; set; } + public string Freebase_Mid { get; set; } - public int Tvdb_Id { get; set; } - public int Tvrage_Id { get; set; } + + public int? Tvdb_Id { get; set; } + + public int? Tvrage_Id { get; set; } } } diff --git a/MediaBrowser.Providers/Tmdb/Models/General/Genre.cs b/MediaBrowser.Providers/Plugins/Tmdb/Models/General/Genre.cs index 9a6686d50d..9ba1c15c65 100644 --- a/MediaBrowser.Providers/Tmdb/Models/General/Genre.cs +++ b/MediaBrowser.Providers/Plugins/Tmdb/Models/General/Genre.cs @@ -1,8 +1,11 @@ -namespace MediaBrowser.Providers.Tmdb.Models.General +#pragma warning disable CS1591 + +namespace MediaBrowser.Providers.Plugins.Tmdb.Models.General { public class Genre { public int Id { get; set; } + public string Name { get; set; } } } diff --git a/MediaBrowser.Providers/Tmdb/Models/General/Images.cs b/MediaBrowser.Providers/Plugins/Tmdb/Models/General/Images.cs index f1c99537d1..0538cf174d 100644 --- a/MediaBrowser.Providers/Tmdb/Models/General/Images.cs +++ b/MediaBrowser.Providers/Plugins/Tmdb/Models/General/Images.cs @@ -1,10 +1,13 @@ +#pragma warning disable CS1591 + using System.Collections.Generic; -namespace MediaBrowser.Providers.Tmdb.Models.General +namespace MediaBrowser.Providers.Plugins.Tmdb.Models.General { public class Images { public List<Backdrop> Backdrops { get; set; } + public List<Poster> Posters { get; set; } } } diff --git a/MediaBrowser.Providers/Tmdb/Models/General/Keyword.cs b/MediaBrowser.Providers/Plugins/Tmdb/Models/General/Keyword.cs index 4e30113497..fff86931be 100644 --- a/MediaBrowser.Providers/Tmdb/Models/General/Keyword.cs +++ b/MediaBrowser.Providers/Plugins/Tmdb/Models/General/Keyword.cs @@ -1,8 +1,11 @@ -namespace MediaBrowser.Providers.Tmdb.Models.General +#pragma warning disable CS1591 + +namespace MediaBrowser.Providers.Plugins.Tmdb.Models.General { public class Keyword { public int Id { get; set; } + public string Name { get; set; } } } diff --git a/MediaBrowser.Providers/Tmdb/Models/General/Keywords.cs b/MediaBrowser.Providers/Plugins/Tmdb/Models/General/Keywords.cs index 1950a51b3f..235ecb5682 100644 --- a/MediaBrowser.Providers/Tmdb/Models/General/Keywords.cs +++ b/MediaBrowser.Providers/Plugins/Tmdb/Models/General/Keywords.cs @@ -1,6 +1,8 @@ +#pragma warning disable CS1591 + using System.Collections.Generic; -namespace MediaBrowser.Providers.Tmdb.Models.General +namespace MediaBrowser.Providers.Plugins.Tmdb.Models.General { public class Keywords { diff --git a/MediaBrowser.Providers/Tmdb/Models/General/Poster.cs b/MediaBrowser.Providers/Plugins/Tmdb/Models/General/Poster.cs index 33401b15dc..4f61e978be 100644 --- a/MediaBrowser.Providers/Tmdb/Models/General/Poster.cs +++ b/MediaBrowser.Providers/Plugins/Tmdb/Models/General/Poster.cs @@ -1,13 +1,21 @@ -namespace MediaBrowser.Providers.Tmdb.Models.General +#pragma warning disable CS1591 + +namespace MediaBrowser.Providers.Plugins.Tmdb.Models.General { public class Poster { public double Aspect_Ratio { get; set; } + public string File_Path { get; set; } + public int Height { get; set; } + public string Iso_639_1 { get; set; } + public double Vote_Average { get; set; } + public int Vote_Count { get; set; } + public int Width { get; set; } } } diff --git a/MediaBrowser.Providers/Tmdb/Models/General/Profile.cs b/MediaBrowser.Providers/Plugins/Tmdb/Models/General/Profile.cs index f87d14850c..0a1f8843eb 100644 --- a/MediaBrowser.Providers/Tmdb/Models/General/Profile.cs +++ b/MediaBrowser.Providers/Plugins/Tmdb/Models/General/Profile.cs @@ -1,11 +1,17 @@ -namespace MediaBrowser.Providers.Tmdb.Models.General +#pragma warning disable CS1591 + +namespace MediaBrowser.Providers.Plugins.Tmdb.Models.General { public class Profile { public string File_Path { get; set; } + public int Width { get; set; } + public int Height { get; set; } + public object Iso_639_1 { get; set; } + public double Aspect_Ratio { get; set; } } } diff --git a/MediaBrowser.Providers/Tmdb/Models/General/Still.cs b/MediaBrowser.Providers/Plugins/Tmdb/Models/General/Still.cs index 15ff4a0991..61de819b93 100644 --- a/MediaBrowser.Providers/Tmdb/Models/General/Still.cs +++ b/MediaBrowser.Providers/Plugins/Tmdb/Models/General/Still.cs @@ -1,14 +1,23 @@ -namespace MediaBrowser.Providers.Tmdb.Models.General +#pragma warning disable CS1591 + +namespace MediaBrowser.Providers.Plugins.Tmdb.Models.General { public class Still { public double Aspect_Ratio { get; set; } + public string File_Path { get; set; } + public int Height { get; set; } + public string Id { get; set; } + public string Iso_639_1 { get; set; } + public double Vote_Average { get; set; } + public int Vote_Count { get; set; } + public int Width { get; set; } } } diff --git a/MediaBrowser.Providers/Tmdb/Models/General/StillImages.cs b/MediaBrowser.Providers/Plugins/Tmdb/Models/General/StillImages.cs index 266965c47e..59ab18b7bf 100644 --- a/MediaBrowser.Providers/Tmdb/Models/General/StillImages.cs +++ b/MediaBrowser.Providers/Plugins/Tmdb/Models/General/StillImages.cs @@ -1,6 +1,8 @@ +#pragma warning disable CS1591 + using System.Collections.Generic; -namespace MediaBrowser.Providers.Tmdb.Models.General +namespace MediaBrowser.Providers.Plugins.Tmdb.Models.General { public class StillImages { diff --git a/MediaBrowser.Providers/Tmdb/Models/General/Video.cs b/MediaBrowser.Providers/Plugins/Tmdb/Models/General/Video.cs index fb69e77674..ebd5c7acee 100644 --- a/MediaBrowser.Providers/Tmdb/Models/General/Video.cs +++ b/MediaBrowser.Providers/Plugins/Tmdb/Models/General/Video.cs @@ -1,14 +1,23 @@ -namespace MediaBrowser.Providers.Tmdb.Models.General +#pragma warning disable CS1591 + +namespace MediaBrowser.Providers.Plugins.Tmdb.Models.General { public class Video { public string Id { get; set; } + public string Iso_639_1 { get; set; } + public string Iso_3166_1 { get; set; } + public string Key { get; set; } + public string Name { get; set; } + public string Site { get; set; } + public string Size { get; set; } + public string Type { get; set; } } } diff --git a/MediaBrowser.Providers/Tmdb/Models/General/Videos.cs b/MediaBrowser.Providers/Plugins/Tmdb/Models/General/Videos.cs index 26812780da..241dcab4de 100644 --- a/MediaBrowser.Providers/Tmdb/Models/General/Videos.cs +++ b/MediaBrowser.Providers/Plugins/Tmdb/Models/General/Videos.cs @@ -1,6 +1,8 @@ +#pragma warning disable CS1591 + using System.Collections.Generic; -namespace MediaBrowser.Providers.Tmdb.Models.General +namespace MediaBrowser.Providers.Plugins.Tmdb.Models.General { public class Videos { diff --git a/MediaBrowser.Providers/Tmdb/Models/Movies/BelongsToCollection.cs b/MediaBrowser.Providers/Plugins/Tmdb/Models/Movies/BelongsToCollection.cs index ac673df619..e8745be140 100644 --- a/MediaBrowser.Providers/Tmdb/Models/Movies/BelongsToCollection.cs +++ b/MediaBrowser.Providers/Plugins/Tmdb/Models/Movies/BelongsToCollection.cs @@ -1,10 +1,15 @@ -namespace MediaBrowser.Providers.Tmdb.Models.Movies +#pragma warning disable CS1591 + +namespace MediaBrowser.Providers.Plugins.Tmdb.Models.Movies { public class BelongsToCollection { public int Id { get; set; } + public string Name { get; set; } + public string Poster_Path { get; set; } + public string Backdrop_Path { get; set; } } } diff --git a/MediaBrowser.Providers/Tmdb/Models/Movies/Cast.cs b/MediaBrowser.Providers/Plugins/Tmdb/Models/Movies/Cast.cs index 44af9e5681..937cfb8f6b 100644 --- a/MediaBrowser.Providers/Tmdb/Models/Movies/Cast.cs +++ b/MediaBrowser.Providers/Plugins/Tmdb/Models/Movies/Cast.cs @@ -1,12 +1,19 @@ -namespace MediaBrowser.Providers.Tmdb.Models.Movies +#pragma warning disable CS1591 + +namespace MediaBrowser.Providers.Plugins.Tmdb.Models.Movies { public class Cast { public int Id { get; set; } + public string Name { get; set; } + public string Character { get; set; } + public int Order { get; set; } + public int Cast_Id { get; set; } + public string Profile_Path { get; set; } } } diff --git a/MediaBrowser.Providers/Tmdb/Models/Movies/Casts.cs b/MediaBrowser.Providers/Plugins/Tmdb/Models/Movies/Casts.cs index 7b5094fa3e..37547640f5 100644 --- a/MediaBrowser.Providers/Tmdb/Models/Movies/Casts.cs +++ b/MediaBrowser.Providers/Plugins/Tmdb/Models/Movies/Casts.cs @@ -1,11 +1,14 @@ +#pragma warning disable CS1591 + using System.Collections.Generic; -using MediaBrowser.Providers.Tmdb.Models.General; +using MediaBrowser.Providers.Plugins.Tmdb.Models.General; -namespace MediaBrowser.Providers.Tmdb.Models.Movies +namespace MediaBrowser.Providers.Plugins.Tmdb.Models.Movies { public class Casts { public List<Cast> Cast { get; set; } + public List<Crew> Crew { get; set; } } } diff --git a/MediaBrowser.Providers/Tmdb/Models/Movies/Country.cs b/MediaBrowser.Providers/Plugins/Tmdb/Models/Movies/Country.cs index 6f843adddb..edd656a461 100644 --- a/MediaBrowser.Providers/Tmdb/Models/Movies/Country.cs +++ b/MediaBrowser.Providers/Plugins/Tmdb/Models/Movies/Country.cs @@ -1,11 +1,15 @@ +#pragma warning disable CS1591 + using System; -namespace MediaBrowser.Providers.Tmdb.Models.Movies +namespace MediaBrowser.Providers.Plugins.Tmdb.Models.Movies { public class Country { public string Iso_3166_1 { get; set; } + public string Certification { get; set; } + public DateTime Release_Date { get; set; } } } diff --git a/MediaBrowser.Providers/Tmdb/Models/Movies/MovieResult.cs b/MediaBrowser.Providers/Plugins/Tmdb/Models/Movies/MovieResult.cs index 1b262946fb..704ebcd5a1 100644 --- a/MediaBrowser.Providers/Tmdb/Models/Movies/MovieResult.cs +++ b/MediaBrowser.Providers/Plugins/Tmdb/Models/Movies/MovieResult.cs @@ -1,39 +1,70 @@ +#pragma warning disable CS1591 + using System.Collections.Generic; -using MediaBrowser.Providers.Tmdb.Models.General; +using MediaBrowser.Providers.Plugins.Tmdb.Models.General; -namespace MediaBrowser.Providers.Tmdb.Models.Movies +namespace MediaBrowser.Providers.Plugins.Tmdb.Models.Movies { public class MovieResult { public bool Adult { get; set; } + public string Backdrop_Path { get; set; } + public BelongsToCollection Belongs_To_Collection { get; set; } - public int Budget { get; set; } + + public long Budget { get; set; } + public List<Genre> Genres { get; set; } + public string Homepage { get; set; } + public int Id { get; set; } + public string Imdb_Id { get; set; } + public string Original_Title { get; set; } + public string Original_Name { get; set; } + public string Overview { get; set; } + public double Popularity { get; set; } + public string Poster_Path { get; set; } + public List<ProductionCompany> Production_Companies { get; set; } + public List<ProductionCountry> Production_Countries { get; set; } + public string Release_Date { get; set; } - public int Revenue { get; set; } + + public long Revenue { get; set; } + public int Runtime { get; set; } + public List<SpokenLanguage> Spoken_Languages { get; set; } + public string Status { get; set; } + public string Tagline { get; set; } + public string Title { get; set; } + public string Name { get; set; } + public double Vote_Average { get; set; } + public int Vote_Count { get; set; } + public Casts Casts { get; set; } + public Releases Releases { get; set; } + public Images Images { get; set; } + public Keywords Keywords { get; set; } + public Trailers Trailers { get; set; } public string GetOriginalTitle() diff --git a/MediaBrowser.Providers/Tmdb/Models/Movies/ProductionCompany.cs b/MediaBrowser.Providers/Plugins/Tmdb/Models/Movies/ProductionCompany.cs index c3382f3057..2788731b2e 100644 --- a/MediaBrowser.Providers/Tmdb/Models/Movies/ProductionCompany.cs +++ b/MediaBrowser.Providers/Plugins/Tmdb/Models/Movies/ProductionCompany.cs @@ -1,8 +1,11 @@ -namespace MediaBrowser.Providers.Tmdb.Models.Movies +#pragma warning disable CS1591 + +namespace MediaBrowser.Providers.Plugins.Tmdb.Models.Movies { public class ProductionCompany { public string Name { get; set; } + public int Id { get; set; } } } diff --git a/MediaBrowser.Providers/Tmdb/Models/Movies/ProductionCountry.cs b/MediaBrowser.Providers/Plugins/Tmdb/Models/Movies/ProductionCountry.cs index 78112c915c..1b6f2cc67a 100644 --- a/MediaBrowser.Providers/Tmdb/Models/Movies/ProductionCountry.cs +++ b/MediaBrowser.Providers/Plugins/Tmdb/Models/Movies/ProductionCountry.cs @@ -1,8 +1,11 @@ -namespace MediaBrowser.Providers.Tmdb.Models.Movies +#pragma warning disable CS1591 + +namespace MediaBrowser.Providers.Plugins.Tmdb.Models.Movies { public class ProductionCountry { public string Iso_3166_1 { get; set; } + public string Name { get; set; } } } diff --git a/MediaBrowser.Providers/Tmdb/Models/Movies/Releases.cs b/MediaBrowser.Providers/Plugins/Tmdb/Models/Movies/Releases.cs index c44f31e462..276fbaaf55 100644 --- a/MediaBrowser.Providers/Tmdb/Models/Movies/Releases.cs +++ b/MediaBrowser.Providers/Plugins/Tmdb/Models/Movies/Releases.cs @@ -1,6 +1,8 @@ +#pragma warning disable CS1591 + using System.Collections.Generic; -namespace MediaBrowser.Providers.Tmdb.Models.Movies +namespace MediaBrowser.Providers.Plugins.Tmdb.Models.Movies { public class Releases { diff --git a/MediaBrowser.Providers/Tmdb/Models/Movies/SpokenLanguage.cs b/MediaBrowser.Providers/Plugins/Tmdb/Models/Movies/SpokenLanguage.cs index 4bc5cfa483..67231d219d 100644 --- a/MediaBrowser.Providers/Tmdb/Models/Movies/SpokenLanguage.cs +++ b/MediaBrowser.Providers/Plugins/Tmdb/Models/Movies/SpokenLanguage.cs @@ -1,8 +1,11 @@ -namespace MediaBrowser.Providers.Tmdb.Models.Movies +#pragma warning disable CS1591 + +namespace MediaBrowser.Providers.Plugins.Tmdb.Models.Movies { public class SpokenLanguage { public string Iso_639_1 { get; set; } + public string Name { get; set; } } } diff --git a/MediaBrowser.Providers/Tmdb/Models/Movies/Trailers.cs b/MediaBrowser.Providers/Plugins/Tmdb/Models/Movies/Trailers.cs index 4bfa02f062..166860f517 100644 --- a/MediaBrowser.Providers/Tmdb/Models/Movies/Trailers.cs +++ b/MediaBrowser.Providers/Plugins/Tmdb/Models/Movies/Trailers.cs @@ -1,6 +1,8 @@ +#pragma warning disable CS1591 + using System.Collections.Generic; -namespace MediaBrowser.Providers.Tmdb.Models.Movies +namespace MediaBrowser.Providers.Plugins.Tmdb.Models.Movies { public class Trailers { diff --git a/MediaBrowser.Providers/Tmdb/Models/Movies/Youtube.cs b/MediaBrowser.Providers/Plugins/Tmdb/Models/Movies/Youtube.cs index 069572824c..6885b7dab5 100644 --- a/MediaBrowser.Providers/Tmdb/Models/Movies/Youtube.cs +++ b/MediaBrowser.Providers/Plugins/Tmdb/Models/Movies/Youtube.cs @@ -1,9 +1,13 @@ -namespace MediaBrowser.Providers.Tmdb.Models.Movies +#pragma warning disable CS1591 + +namespace MediaBrowser.Providers.Plugins.Tmdb.Models.Movies { public class Youtube { public string Name { get; set; } + public string Size { get; set; } + public string Source { get; set; } } } diff --git a/MediaBrowser.Providers/Plugins/Tmdb/Models/People/PersonImages.cs b/MediaBrowser.Providers/Plugins/Tmdb/Models/People/PersonImages.cs new file mode 100644 index 0000000000..3ea12334e8 --- /dev/null +++ b/MediaBrowser.Providers/Plugins/Tmdb/Models/People/PersonImages.cs @@ -0,0 +1,12 @@ +#pragma warning disable CS1591 + +using System.Collections.Generic; +using MediaBrowser.Providers.Plugins.Tmdb.Models.General; + +namespace MediaBrowser.Providers.Plugins.Tmdb.Models.People +{ + public class PersonImages + { + public List<Profile> Profiles { get; set; } + } +} diff --git a/MediaBrowser.Providers/Tmdb/Models/People/PersonResult.cs b/MediaBrowser.Providers/Plugins/Tmdb/Models/People/PersonResult.cs index 6e997050fe..460ced49a0 100644 --- a/MediaBrowser.Providers/Tmdb/Models/People/PersonResult.cs +++ b/MediaBrowser.Providers/Plugins/Tmdb/Models/People/PersonResult.cs @@ -1,23 +1,38 @@ +#pragma warning disable CS1591 + using System.Collections.Generic; -using MediaBrowser.Providers.Tmdb.Models.General; +using MediaBrowser.Providers.Plugins.Tmdb.Models.General; -namespace MediaBrowser.Providers.Tmdb.Models.People +namespace MediaBrowser.Providers.Plugins.Tmdb.Models.People { public class PersonResult { public bool Adult { get; set; } + public List<string> Also_Known_As { get; set; } + public string Biography { get; set; } + public string Birthday { get; set; } + public string Deathday { get; set; } + public string Homepage { get; set; } + public int Id { get; set; } + public string Imdb_Id { get; set; } + public string Name { get; set; } + public string Place_Of_Birth { get; set; } + public double Popularity { get; set; } + public string Profile_Path { get; set; } + public PersonImages Images { get; set; } + public ExternalIds External_Ids { get; set; } } } diff --git a/MediaBrowser.Providers/Tmdb/Models/Search/ExternalIdLookupResult.cs b/MediaBrowser.Providers/Plugins/Tmdb/Models/Search/ExternalIdLookupResult.cs index d19f4e8cbd..87c2a723d1 100644 --- a/MediaBrowser.Providers/Tmdb/Models/Search/ExternalIdLookupResult.cs +++ b/MediaBrowser.Providers/Plugins/Tmdb/Models/Search/ExternalIdLookupResult.cs @@ -1,6 +1,8 @@ +#pragma warning disable CS1591 + using System.Collections.Generic; -namespace MediaBrowser.Providers.Tmdb.Models.Search +namespace MediaBrowser.Providers.Plugins.Tmdb.Models.Search { public class ExternalIdLookupResult { diff --git a/MediaBrowser.Providers/Tmdb/Models/Search/MovieResult.cs b/MediaBrowser.Providers/Plugins/Tmdb/Models/Search/MovieResult.cs index 245162728b..401c75c319 100644 --- a/MediaBrowser.Providers/Tmdb/Models/Search/MovieResult.cs +++ b/MediaBrowser.Providers/Plugins/Tmdb/Models/Search/MovieResult.cs @@ -1,4 +1,6 @@ -namespace MediaBrowser.Providers.Tmdb.Models.Search +#pragma warning disable CS1591 + +namespace MediaBrowser.Providers.Plugins.Tmdb.Models.Search { public class MovieResult { @@ -7,55 +9,66 @@ namespace MediaBrowser.Providers.Tmdb.Models.Search /// </summary> /// <value><c>true</c> if adult; otherwise, <c>false</c>.</value> public bool Adult { get; set; } + /// <summary> /// Gets or sets the backdrop_path. /// </summary> /// <value>The backdrop_path.</value> public string Backdrop_Path { get; set; } + /// <summary> /// Gets or sets the id. /// </summary> /// <value>The id.</value> public int Id { get; set; } + /// <summary> /// Gets or sets the original_title. /// </summary> /// <value>The original_title.</value> public string Original_Title { get; set; } + /// <summary> /// Gets or sets the original_name. /// </summary> /// <value>The original_name.</value> public string Original_Name { get; set; } + /// <summary> /// Gets or sets the release_date. /// </summary> /// <value>The release_date.</value> public string Release_Date { get; set; } + /// <summary> /// Gets or sets the poster_path. /// </summary> /// <value>The poster_path.</value> public string Poster_Path { get; set; } + /// <summary> /// Gets or sets the popularity. /// </summary> /// <value>The popularity.</value> public double Popularity { get; set; } + /// <summary> /// Gets or sets the title. /// </summary> /// <value>The title.</value> public string Title { get; set; } + /// <summary> /// Gets or sets the vote_average. /// </summary> /// <value>The vote_average.</value> public double Vote_Average { get; set; } + /// <summary> - /// For collection search results + /// For collection search results. /// </summary> public string Name { get; set; } + /// <summary> /// Gets or sets the vote_count. /// </summary> diff --git a/MediaBrowser.Providers/Tmdb/Models/Search/PersonSearchResult.cs b/MediaBrowser.Providers/Plugins/Tmdb/Models/Search/PersonSearchResult.cs index 93916068f4..4cff45ca64 100644 --- a/MediaBrowser.Providers/Tmdb/Models/Search/PersonSearchResult.cs +++ b/MediaBrowser.Providers/Plugins/Tmdb/Models/Search/PersonSearchResult.cs @@ -1,4 +1,6 @@ -namespace MediaBrowser.Providers.Tmdb.Models.Search +#pragma warning disable CS1591 + +namespace MediaBrowser.Providers.Plugins.Tmdb.Models.Search { public class PersonSearchResult { diff --git a/MediaBrowser.Providers/Tmdb/Models/Search/TmdbSearchResult.cs b/MediaBrowser.Providers/Plugins/Tmdb/Models/Search/TmdbSearchResult.cs index a9f888e759..3b9257b623 100644 --- a/MediaBrowser.Providers/Tmdb/Models/Search/TmdbSearchResult.cs +++ b/MediaBrowser.Providers/Plugins/Tmdb/Models/Search/TmdbSearchResult.cs @@ -1,6 +1,8 @@ +#pragma warning disable CS1591 + using System.Collections.Generic; -namespace MediaBrowser.Providers.Tmdb.Models.Search +namespace MediaBrowser.Providers.Plugins.Tmdb.Models.Search { public class TmdbSearchResult<T> { diff --git a/MediaBrowser.Providers/Tmdb/Models/Search/TvResult.cs b/MediaBrowser.Providers/Plugins/Tmdb/Models/Search/TvResult.cs index ed140bedd0..b2bb068b58 100644 --- a/MediaBrowser.Providers/Tmdb/Models/Search/TvResult.cs +++ b/MediaBrowser.Providers/Plugins/Tmdb/Models/Search/TvResult.cs @@ -1,15 +1,25 @@ -namespace MediaBrowser.Providers.Tmdb.Models.Search +#pragma warning disable CS1591 + +namespace MediaBrowser.Providers.Plugins.Tmdb.Models.Search { public class TvResult { public string Backdrop_Path { get; set; } + public string First_Air_Date { get; set; } + public int Id { get; set; } + public string Original_Name { get; set; } + public string Poster_Path { get; set; } + public double Popularity { get; set; } + public string Name { get; set; } + public double Vote_Average { get; set; } + public int Vote_Count { get; set; } } } diff --git a/MediaBrowser.Providers/Tmdb/Models/TV/Cast.cs b/MediaBrowser.Providers/Plugins/Tmdb/Models/TV/Cast.cs index c659df9ac8..4ce26c65ee 100644 --- a/MediaBrowser.Providers/Tmdb/Models/TV/Cast.cs +++ b/MediaBrowser.Providers/Plugins/Tmdb/Models/TV/Cast.cs @@ -1,12 +1,19 @@ -namespace MediaBrowser.Providers.Tmdb.Models.TV +#pragma warning disable CS1591 + +namespace MediaBrowser.Providers.Plugins.Tmdb.Models.TV { public class Cast { public string Character { get; set; } + public string Credit_Id { get; set; } + public int Id { get; set; } + public string Name { get; set; } + public string Profile_Path { get; set; } + public int Order { get; set; } } } diff --git a/MediaBrowser.Providers/Tmdb/Models/TV/ContentRating.cs b/MediaBrowser.Providers/Plugins/Tmdb/Models/TV/ContentRating.cs index 3177cd71b0..aef4e28632 100644 --- a/MediaBrowser.Providers/Tmdb/Models/TV/ContentRating.cs +++ b/MediaBrowser.Providers/Plugins/Tmdb/Models/TV/ContentRating.cs @@ -1,8 +1,11 @@ -namespace MediaBrowser.Providers.Tmdb.Models.TV +#pragma warning disable CS1591 + +namespace MediaBrowser.Providers.Plugins.Tmdb.Models.TV { public class ContentRating { public string Iso_3166_1 { get; set; } + public string Rating { get; set; } } } diff --git a/MediaBrowser.Providers/Tmdb/Models/TV/ContentRatings.cs b/MediaBrowser.Providers/Plugins/Tmdb/Models/TV/ContentRatings.cs index 883e605c96..ae1b5668d3 100644 --- a/MediaBrowser.Providers/Tmdb/Models/TV/ContentRatings.cs +++ b/MediaBrowser.Providers/Plugins/Tmdb/Models/TV/ContentRatings.cs @@ -1,6 +1,8 @@ +#pragma warning disable CS1591 + using System.Collections.Generic; -namespace MediaBrowser.Providers.Tmdb.Models.TV +namespace MediaBrowser.Providers.Plugins.Tmdb.Models.TV { public class ContentRatings { diff --git a/MediaBrowser.Providers/Tmdb/Models/TV/CreatedBy.cs b/MediaBrowser.Providers/Plugins/Tmdb/Models/TV/CreatedBy.cs index 21588d8977..ba36632e04 100644 --- a/MediaBrowser.Providers/Tmdb/Models/TV/CreatedBy.cs +++ b/MediaBrowser.Providers/Plugins/Tmdb/Models/TV/CreatedBy.cs @@ -1,9 +1,13 @@ -namespace MediaBrowser.Providers.Tmdb.Models.TV +#pragma warning disable CS1591 + +namespace MediaBrowser.Providers.Plugins.Tmdb.Models.TV { public class CreatedBy { public int Id { get; set; } + public string Name { get; set; } + public string Profile_Path { get; set; } } } diff --git a/MediaBrowser.Providers/Tmdb/Models/TV/Credits.cs b/MediaBrowser.Providers/Plugins/Tmdb/Models/TV/Credits.cs index b62b5f605c..47205d8751 100644 --- a/MediaBrowser.Providers/Tmdb/Models/TV/Credits.cs +++ b/MediaBrowser.Providers/Plugins/Tmdb/Models/TV/Credits.cs @@ -1,11 +1,14 @@ +#pragma warning disable CS1591 + using System.Collections.Generic; -using MediaBrowser.Providers.Tmdb.Models.General; +using MediaBrowser.Providers.Plugins.Tmdb.Models.General; -namespace MediaBrowser.Providers.Tmdb.Models.TV +namespace MediaBrowser.Providers.Plugins.Tmdb.Models.TV { public class Credits { public List<Cast> Cast { get; set; } + public List<Crew> Crew { get; set; } } } diff --git a/MediaBrowser.Providers/Tmdb/Models/TV/Episode.cs b/MediaBrowser.Providers/Plugins/Tmdb/Models/TV/Episode.cs index ab11a6cd2f..53e3c26959 100644 --- a/MediaBrowser.Providers/Tmdb/Models/TV/Episode.cs +++ b/MediaBrowser.Providers/Plugins/Tmdb/Models/TV/Episode.cs @@ -1,14 +1,23 @@ -namespace MediaBrowser.Providers.Tmdb.Models.TV +#pragma warning disable CS1591 + +namespace MediaBrowser.Providers.Plugins.Tmdb.Models.TV { public class Episode { public string Air_Date { get; set; } + public int Episode_Number { get; set; } + public int Id { get; set; } + public string Name { get; set; } + public string Overview { get; set; } + public string Still_Path { get; set; } + public double Vote_Average { get; set; } + public int Vote_Count { get; set; } } } diff --git a/MediaBrowser.Providers/Tmdb/Models/TV/EpisodeCredits.cs b/MediaBrowser.Providers/Plugins/Tmdb/Models/TV/EpisodeCredits.cs index 1c86be0f41..9707e4bf49 100644 --- a/MediaBrowser.Providers/Tmdb/Models/TV/EpisodeCredits.cs +++ b/MediaBrowser.Providers/Plugins/Tmdb/Models/TV/EpisodeCredits.cs @@ -1,12 +1,16 @@ +#pragma warning disable CS1591 + using System.Collections.Generic; -using MediaBrowser.Providers.Tmdb.Models.General; +using MediaBrowser.Providers.Plugins.Tmdb.Models.General; -namespace MediaBrowser.Providers.Tmdb.Models.TV +namespace MediaBrowser.Providers.Plugins.Tmdb.Models.TV { public class EpisodeCredits { public List<Cast> Cast { get; set; } + public List<Crew> Crew { get; set; } + public List<GuestStar> Guest_Stars { get; set; } } } diff --git a/MediaBrowser.Providers/Tmdb/Models/TV/EpisodeResult.cs b/MediaBrowser.Providers/Plugins/Tmdb/Models/TV/EpisodeResult.cs index 0513ce7e25..4458bad367 100644 --- a/MediaBrowser.Providers/Tmdb/Models/TV/EpisodeResult.cs +++ b/MediaBrowser.Providers/Plugins/Tmdb/Models/TV/EpisodeResult.cs @@ -1,23 +1,38 @@ +#pragma warning disable CS1591 + using System; -using MediaBrowser.Providers.Tmdb.Models.General; +using MediaBrowser.Providers.Plugins.Tmdb.Models.General; -namespace MediaBrowser.Providers.Tmdb.Models.TV +namespace MediaBrowser.Providers.Plugins.Tmdb.Models.TV { public class EpisodeResult { public DateTime Air_Date { get; set; } + public int Episode_Number { get; set; } + public string Name { get; set; } + public string Overview { get; set; } + public int Id { get; set; } + public object Production_Code { get; set; } + public int Season_Number { get; set; } + public string Still_Path { get; set; } + public double Vote_Average { get; set; } + public int Vote_Count { get; set; } + public StillImages Images { get; set; } + public ExternalIds External_Ids { get; set; } + public EpisodeCredits Credits { get; set; } + public Tmdb.Models.General.Videos Videos { get; set; } } } diff --git a/MediaBrowser.Providers/Tmdb/Models/TV/GuestStar.cs b/MediaBrowser.Providers/Plugins/Tmdb/Models/TV/GuestStar.cs index 2dfe7a8625..8f39886418 100644 --- a/MediaBrowser.Providers/Tmdb/Models/TV/GuestStar.cs +++ b/MediaBrowser.Providers/Plugins/Tmdb/Models/TV/GuestStar.cs @@ -1,12 +1,19 @@ -namespace MediaBrowser.Providers.Tmdb.Models.TV +#pragma warning disable CS1591 + +namespace MediaBrowser.Providers.Plugins.Tmdb.Models.TV { public class GuestStar { public int Id { get; set; } + public string Name { get; set; } + public string Credit_Id { get; set; } + public string Character { get; set; } + public int Order { get; set; } + public string Profile_Path { get; set; } } } diff --git a/MediaBrowser.Providers/Tmdb/Models/TV/Network.cs b/MediaBrowser.Providers/Plugins/Tmdb/Models/TV/Network.cs index f982682d12..3dc310d330 100644 --- a/MediaBrowser.Providers/Tmdb/Models/TV/Network.cs +++ b/MediaBrowser.Providers/Plugins/Tmdb/Models/TV/Network.cs @@ -1,8 +1,11 @@ -namespace MediaBrowser.Providers.Tmdb.Models.TV +#pragma warning disable CS1591 + +namespace MediaBrowser.Providers.Plugins.Tmdb.Models.TV { public class Network { public int Id { get; set; } + public string Name { get; set; } } } diff --git a/MediaBrowser.Providers/Tmdb/Models/TV/Season.cs b/MediaBrowser.Providers/Plugins/Tmdb/Models/TV/Season.cs index 976e3c97e2..9cbd283a95 100644 --- a/MediaBrowser.Providers/Tmdb/Models/TV/Season.cs +++ b/MediaBrowser.Providers/Plugins/Tmdb/Models/TV/Season.cs @@ -1,11 +1,17 @@ -namespace MediaBrowser.Providers.Tmdb.Models.TV +#pragma warning disable CS1591 + +namespace MediaBrowser.Providers.Plugins.Tmdb.Models.TV { public class Season { public string Air_Date { get; set; } + public int Episode_Count { get; set; } + public int Id { get; set; } + public string Poster_Path { get; set; } + public int Season_Number { get; set; } } } diff --git a/MediaBrowser.Providers/Plugins/Tmdb/Models/TV/SeasonImages.cs b/MediaBrowser.Providers/Plugins/Tmdb/Models/TV/SeasonImages.cs new file mode 100644 index 0000000000..f364d4921b --- /dev/null +++ b/MediaBrowser.Providers/Plugins/Tmdb/Models/TV/SeasonImages.cs @@ -0,0 +1,12 @@ +#pragma warning disable CS1591 + +using System.Collections.Generic; +using MediaBrowser.Providers.Plugins.Tmdb.Models.General; + +namespace MediaBrowser.Providers.Plugins.Tmdb.Models.TV +{ + public class SeasonImages + { + public List<Poster> Posters { get; set; } + } +} diff --git a/MediaBrowser.Providers/Tmdb/Models/TV/SeasonResult.cs b/MediaBrowser.Providers/Plugins/Tmdb/Models/TV/SeasonResult.cs index bc9213c04d..e98048eacc 100644 --- a/MediaBrowser.Providers/Tmdb/Models/TV/SeasonResult.cs +++ b/MediaBrowser.Providers/Plugins/Tmdb/Models/TV/SeasonResult.cs @@ -1,21 +1,33 @@ +#pragma warning disable CS1591 + using System; using System.Collections.Generic; -using MediaBrowser.Providers.Tmdb.Models.General; +using MediaBrowser.Providers.Plugins.Tmdb.Models.General; -namespace MediaBrowser.Providers.Tmdb.Models.TV +namespace MediaBrowser.Providers.Plugins.Tmdb.Models.TV { public class SeasonResult { public DateTime Air_Date { get; set; } + public List<Episode> Episodes { get; set; } + public string Name { get; set; } + public string Overview { get; set; } + public int Id { get; set; } + public string Poster_Path { get; set; } + public int Season_Number { get; set; } + public Credits Credits { get; set; } + public SeasonImages Images { get; set; } + public ExternalIds External_Ids { get; set; } + public General.Videos Videos { get; set; } } } diff --git a/MediaBrowser.Providers/Tmdb/Models/TV/SeriesResult.cs b/MediaBrowser.Providers/Plugins/Tmdb/Models/TV/SeriesResult.cs index ad95e502e4..331cd59fa6 100644 --- a/MediaBrowser.Providers/Tmdb/Models/TV/SeriesResult.cs +++ b/MediaBrowser.Providers/Plugins/Tmdb/Models/TV/SeriesResult.cs @@ -1,40 +1,71 @@ +#pragma warning disable CS1591 + using System; using System.Collections.Generic; -using MediaBrowser.Providers.Tmdb.Models.General; +using MediaBrowser.Providers.Plugins.Tmdb.Models.General; -namespace MediaBrowser.Providers.Tmdb.Models.TV +namespace MediaBrowser.Providers.Plugins.Tmdb.Models.TV { public class SeriesResult { public string Backdrop_Path { get; set; } + public List<CreatedBy> Created_By { get; set; } + public List<int> Episode_Run_Time { get; set; } + public DateTime First_Air_Date { get; set; } + public List<Genre> Genres { get; set; } + public string Homepage { get; set; } + public int Id { get; set; } + public bool In_Production { get; set; } + public List<string> Languages { get; set; } + public DateTime Last_Air_Date { get; set; } + public string Name { get; set; } + public List<Network> Networks { get; set; } + public int Number_Of_Episodes { get; set; } + public int Number_Of_Seasons { get; set; } + public string Original_Name { get; set; } + public List<string> Origin_Country { get; set; } + public string Overview { get; set; } + public string Popularity { get; set; } + public string Poster_Path { get; set; } + public List<Season> Seasons { get; set; } + public string Status { get; set; } + public double Vote_Average { get; set; } + public int Vote_Count { get; set; } + public Credits Credits { get; set; } + public Images Images { get; set; } + public Keywords Keywords { get; set; } + public ExternalIds External_Ids { get; set; } + public General.Videos Videos { get; set; } + public ContentRatings Content_Ratings { get; set; } + public string ResultLanguage { get; set; } } } diff --git a/MediaBrowser.Providers/Tmdb/Movies/GenericTmdbMovieInfo.cs b/MediaBrowser.Providers/Plugins/Tmdb/Movies/GenericTmdbMovieInfo.cs index ad42b564c6..01a887eed5 100644 --- a/MediaBrowser.Providers/Tmdb/Movies/GenericTmdbMovieInfo.cs +++ b/MediaBrowser.Providers/Plugins/Tmdb/Movies/GenericTmdbMovieInfo.cs @@ -1,3 +1,5 @@ +#pragma warning disable CS1591 + using System; using System.Collections.Generic; using System.Globalization; @@ -13,10 +15,10 @@ using MediaBrowser.Controller.Providers; using MediaBrowser.Model.Entities; using MediaBrowser.Model.IO; using MediaBrowser.Model.Serialization; -using MediaBrowser.Providers.Tmdb.Models.Movies; +using MediaBrowser.Providers.Plugins.Tmdb.Models.Movies; using Microsoft.Extensions.Logging; -namespace MediaBrowser.Providers.Tmdb.Movies +namespace MediaBrowser.Providers.Plugins.Tmdb.Movies { public class GenericTmdbMovieInfo<T> where T : BaseItem, new() @@ -38,8 +40,8 @@ namespace MediaBrowser.Providers.Tmdb.Movies public async Task<MetadataResult<T>> GetMetadata(ItemLookupInfo itemId, CancellationToken cancellationToken) { - var tmdbId = itemId.GetProviderId(MetadataProviders.Tmdb); - var imdbId = itemId.GetProviderId(MetadataProviders.Imdb); + var tmdbId = itemId.GetProviderId(MetadataProvider.Tmdb); + var imdbId = itemId.GetProviderId(MetadataProvider.Imdb); // Don't search for music video id's because it is very easy to misidentify. if (string.IsNullOrEmpty(tmdbId) && string.IsNullOrEmpty(imdbId) && typeof(T) != typeof(MusicVideo)) @@ -50,7 +52,7 @@ namespace MediaBrowser.Providers.Tmdb.Movies if (searchResult != null) { - tmdbId = searchResult.GetProviderId(MetadataProviders.Tmdb); + tmdbId = searchResult.GetProviderId(MetadataProvider.Tmdb); } } @@ -131,7 +133,7 @@ namespace MediaBrowser.Providers.Tmdb.Movies movie.Overview = string.IsNullOrWhiteSpace(movieData.Overview) ? null : WebUtility.HtmlDecode(movieData.Overview); movie.Overview = movie.Overview != null ? movie.Overview.Replace("\n\n", "\n") : null; - //movie.HomePageUrl = movieData.homepage; + // movie.HomePageUrl = movieData.homepage; if (!string.IsNullOrEmpty(movieData.Tagline)) { @@ -146,12 +148,12 @@ namespace MediaBrowser.Providers.Tmdb.Movies .ToArray(); } - movie.SetProviderId(MetadataProviders.Tmdb, movieData.Id.ToString(_usCulture)); - movie.SetProviderId(MetadataProviders.Imdb, movieData.Imdb_Id); + movie.SetProviderId(MetadataProvider.Tmdb, movieData.Id.ToString(_usCulture)); + movie.SetProviderId(MetadataProvider.Imdb, movieData.Imdb_Id); if (movieData.Belongs_To_Collection != null) { - movie.SetProviderId(MetadataProviders.TmdbCollection, + movie.SetProviderId(MetadataProvider.TmdbCollection, movieData.Belongs_To_Collection.Id.ToString(CultureInfo.InvariantCulture)); if (movie is Movie movieItem) @@ -167,7 +169,7 @@ namespace MediaBrowser.Providers.Tmdb.Movies movie.CommunityRating = rating; } - //movie.VoteCount = movieData.vote_count; + // movie.VoteCount = movieData.vote_count; if (movieData.Releases != null && movieData.Releases.Countries != null) { @@ -201,7 +203,7 @@ namespace MediaBrowser.Providers.Tmdb.Movies } } - //studios + // studios if (movieData.Production_Companies != null) { movie.SetStudios(movieData.Production_Companies.Select(c => c.Name)); @@ -219,8 +221,8 @@ namespace MediaBrowser.Providers.Tmdb.Movies resultItem.ResetPeople(); var tmdbImageUrl = settings.images.GetImageUrl("original"); - //Actors, Directors, Writers - all in People - //actors come from cast + // Actors, Directors, Writers - all in People + // actors come from cast if (movieData.Casts != null && movieData.Casts.Cast != null) { foreach (var actor in movieData.Casts.Cast.OrderBy(a => a.Order)) @@ -240,14 +242,14 @@ namespace MediaBrowser.Providers.Tmdb.Movies if (actor.Id > 0) { - personInfo.SetProviderId(MetadataProviders.Tmdb, actor.Id.ToString(CultureInfo.InvariantCulture)); + personInfo.SetProviderId(MetadataProvider.Tmdb, actor.Id.ToString(CultureInfo.InvariantCulture)); } resultItem.AddPerson(personInfo); } } - //and the rest from crew + // and the rest from crew if (movieData.Casts?.Crew != null) { var keepTypes = new[] @@ -282,14 +284,14 @@ namespace MediaBrowser.Providers.Tmdb.Movies if (person.Id > 0) { - personInfo.SetProviderId(MetadataProviders.Tmdb, person.Id.ToString(CultureInfo.InvariantCulture)); + personInfo.SetProviderId(MetadataProvider.Tmdb, person.Id.ToString(CultureInfo.InvariantCulture)); } resultItem.AddPerson(personInfo); } } - //if (movieData.keywords != null && movieData.keywords.keywords != null) + // if (movieData.keywords != null && movieData.keywords.keywords != null) //{ // movie.Keywords = movieData.keywords.keywords.Select(i => i.name).ToList(); //} @@ -298,12 +300,11 @@ namespace MediaBrowser.Providers.Tmdb.Movies { movie.RemoteTrailers = movieData.Trailers.Youtube.Select(i => new MediaUrl { - Url = string.Format("https://www.youtube.com/watch?v={0}", i.Source), + Url = string.Format(CultureInfo.InvariantCulture, "https://www.youtube.com/watch?v={0}", i.Source), Name = i.Name }).ToArray(); } } - } } diff --git a/MediaBrowser.Providers/Tmdb/Movies/TmdbImageProvider.cs b/MediaBrowser.Providers/Plugins/Tmdb/Movies/TmdbImageProvider.cs index 039a49728b..a975fb8f6a 100644 --- a/MediaBrowser.Providers/Tmdb/Movies/TmdbImageProvider.cs +++ b/MediaBrowser.Providers/Plugins/Tmdb/Movies/TmdbImageProvider.cs @@ -1,7 +1,10 @@ +#pragma warning disable CS1591 + using System; using System.Collections.Generic; using System.Globalization; using System.Linq; +using System.Net.Http; using System.Threading; using System.Threading.Tasks; using MediaBrowser.Common.Net; @@ -13,21 +16,21 @@ using MediaBrowser.Model.Entities; using MediaBrowser.Model.IO; using MediaBrowser.Model.Providers; using MediaBrowser.Model.Serialization; -using MediaBrowser.Providers.Tmdb.Models.General; -using MediaBrowser.Providers.Tmdb.Models.Movies; +using MediaBrowser.Providers.Plugins.Tmdb.Models.General; +using MediaBrowser.Providers.Plugins.Tmdb.Models.Movies; -namespace MediaBrowser.Providers.Tmdb.Movies +namespace MediaBrowser.Providers.Plugins.Tmdb.Movies { public class TmdbImageProvider : IRemoteImageProvider, IHasOrder { private readonly IJsonSerializer _jsonSerializer; - private readonly IHttpClient _httpClient; + private readonly IHttpClientFactory _httpClientFactory; private readonly IFileSystem _fileSystem; - public TmdbImageProvider(IJsonSerializer jsonSerializer, IHttpClient httpClient, IFileSystem fileSystem) + public TmdbImageProvider(IJsonSerializer jsonSerializer, IHttpClientFactory httpClientFactory, IFileSystem fileSystem) { _jsonSerializer = jsonSerializer; - _httpClient = httpClient; + _httpClientFactory = httpClientFactory; _fileSystem = fileSystem; } @@ -107,6 +110,7 @@ namespace MediaBrowser.Providers.Tmdb.Movies { return 3; } + if (!isLanguageEn) { if (string.Equals("en", i.Language, StringComparison.OrdinalIgnoreCase)) @@ -114,10 +118,12 @@ namespace MediaBrowser.Providers.Tmdb.Movies return 2; } } + if (string.IsNullOrEmpty(i.Language)) { return isLanguageEn ? 3 : 2; } + return 0; }) .ThenByDescending(i => i.CommunityRating ?? 0) @@ -158,11 +164,11 @@ namespace MediaBrowser.Providers.Tmdb.Movies /// <returns>Task{MovieImages}.</returns> private async Task<Images> FetchImages(BaseItem item, string language, IJsonSerializer jsonSerializer, CancellationToken cancellationToken) { - var tmdbId = item.GetProviderId(MetadataProviders.Tmdb); + var tmdbId = item.GetProviderId(MetadataProvider.Tmdb); if (string.IsNullOrWhiteSpace(tmdbId)) { - var imdbId = item.GetProviderId(MetadataProviders.Imdb); + var imdbId = item.GetProviderId(MetadataProvider.Imdb); if (!string.IsNullOrWhiteSpace(imdbId)) { var movieInfo = await TmdbMovieProvider.Current.FetchMainResult(imdbId, false, language, cancellationToken).ConfigureAwait(false); @@ -197,13 +203,9 @@ namespace MediaBrowser.Providers.Tmdb.Movies public int Order => 0; - public Task<HttpResponseInfo> GetImageResponse(string url, CancellationToken cancellationToken) + public Task<HttpResponseMessage> GetImageResponse(string url, CancellationToken cancellationToken) { - return _httpClient.GetResponse(new HttpRequestOptions - { - CancellationToken = cancellationToken, - Url = url - }); + return _httpClientFactory.CreateClient(NamedClient.Default).GetAsync(url, cancellationToken); } } } diff --git a/MediaBrowser.Providers/Tmdb/Movies/TmdbMovieExternalId.cs b/MediaBrowser.Providers/Plugins/Tmdb/Movies/TmdbMovieExternalId.cs index fc7a4583fd..9610e40585 100644 --- a/MediaBrowser.Providers/Tmdb/Movies/TmdbMovieExternalId.cs +++ b/MediaBrowser.Providers/Plugins/Tmdb/Movies/TmdbMovieExternalId.cs @@ -3,16 +3,23 @@ using MediaBrowser.Controller.Entities.Movies; using MediaBrowser.Controller.LiveTv; using MediaBrowser.Controller.Providers; using MediaBrowser.Model.Entities; +using MediaBrowser.Model.Providers; -namespace MediaBrowser.Providers.Tmdb.Movies +namespace MediaBrowser.Providers.Plugins.Tmdb.Movies { + /// <summary> + /// External ID for a TMBD movie. + /// </summary> public class TmdbMovieExternalId : IExternalId { /// <inheritdoc /> - public string Name => TmdbUtils.ProviderName; + public string ProviderName => TmdbUtils.ProviderName; /// <inheritdoc /> - public string Key => MetadataProviders.Tmdb.ToString(); + public string Key => MetadataProvider.Tmdb.ToString(); + + /// <inheritdoc /> + public ExternalIdMediaType? Type => ExternalIdMediaType.Movie; /// <inheritdoc /> public string UrlFormatString => TmdbUtils.BaseTmdbUrl + "movie/{0}"; diff --git a/MediaBrowser.Providers/Tmdb/Movies/TmdbMovieProvider.cs b/MediaBrowser.Providers/Plugins/Tmdb/Movies/TmdbMovieProvider.cs index e2fd5b9e30..5d383722a0 100644 --- a/MediaBrowser.Providers/Tmdb/Movies/TmdbMovieProvider.cs +++ b/MediaBrowser.Providers/Plugins/Tmdb/Movies/TmdbMovieProvider.cs @@ -1,9 +1,12 @@ +#pragma warning disable CS1591 + using System; using System.Collections.Generic; using System.Globalization; using System.IO; using System.Net; using System.Net.Http; +using System.Net.Http.Headers; using System.Threading; using System.Threading.Tasks; using MediaBrowser.Common; @@ -15,28 +18,29 @@ using MediaBrowser.Controller.Entities.Movies; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Providers; using MediaBrowser.Model.Entities; -using MediaBrowser.Model.Globalization; using MediaBrowser.Model.IO; -using MediaBrowser.Model.Net; using MediaBrowser.Model.Providers; using MediaBrowser.Model.Serialization; -using MediaBrowser.Providers.Tmdb.Models.Movies; +using MediaBrowser.Providers.Plugins.Tmdb.Models.Movies; using Microsoft.Extensions.Logging; -namespace MediaBrowser.Providers.Tmdb.Movies +namespace MediaBrowser.Providers.Plugins.Tmdb.Movies { /// <summary> - /// Class MovieDbProvider + /// Class MovieDbProvider. /// </summary> public class TmdbMovieProvider : IRemoteMetadataProvider<Movie, MovieInfo>, IHasOrder { + private const string TmdbConfigUrl = TmdbUtils.BaseTmdbApiUrl + "3/configuration?api_key={0}"; + private const string GetMovieInfo3 = TmdbUtils.BaseTmdbApiUrl + @"3/movie/{0}?api_key={1}&append_to_response=casts,releases,images,keywords,trailers"; + internal static TmdbMovieProvider Current { get; private set; } private readonly IJsonSerializer _jsonSerializer; - private readonly IHttpClient _httpClient; + private readonly IHttpClientFactory _httpClientFactory; private readonly IFileSystem _fileSystem; private readonly IServerConfigurationManager _configurationManager; - private readonly ILogger _logger; + private readonly ILogger<TmdbMovieProvider> _logger; private readonly ILibraryManager _libraryManager; private readonly IApplicationHost _appHost; @@ -44,7 +48,7 @@ namespace MediaBrowser.Providers.Tmdb.Movies public TmdbMovieProvider( IJsonSerializer jsonSerializer, - IHttpClient httpClient, + IHttpClientFactory httpClientFactory, IFileSystem fileSystem, IServerConfigurationManager configurationManager, ILogger<TmdbMovieProvider> logger, @@ -52,7 +56,7 @@ namespace MediaBrowser.Providers.Tmdb.Movies IApplicationHost appHost) { _jsonSerializer = jsonSerializer; - _httpClient = httpClient; + _httpClientFactory = httpClientFactory; _fileSystem = fileSystem; _configurationManager = configurationManager; _logger = logger; @@ -68,7 +72,7 @@ namespace MediaBrowser.Providers.Tmdb.Movies public async Task<IEnumerable<RemoteSearchResult>> GetMovieSearchResults(ItemLookupInfo searchInfo, CancellationToken cancellationToken) { - var tmdbId = searchInfo.GetProviderId(MetadataProviders.Tmdb); + var tmdbId = searchInfo.GetProviderId(MetadataProvider.Tmdb); if (!string.IsNullOrEmpty(tmdbId)) { @@ -101,11 +105,11 @@ namespace MediaBrowser.Providers.Tmdb.Movies } } - remoteResult.SetProviderId(MetadataProviders.Tmdb, obj.Id.ToString(_usCulture)); + remoteResult.SetProviderId(MetadataProvider.Tmdb, obj.Id.ToString(_usCulture)); if (!string.IsNullOrWhiteSpace(obj.Imdb_Id)) { - remoteResult.SetProviderId(MetadataProviders.Imdb, obj.Imdb_Id); + remoteResult.SetProviderId(MetadataProvider.Imdb, obj.Imdb_Id); } return new[] { remoteResult }; @@ -130,7 +134,7 @@ namespace MediaBrowser.Providers.Tmdb.Movies public string Name => TmdbUtils.ProviderName; /// <summary> - /// The _TMDB settings task + /// The _TMDB settings task. /// </summary> private TmdbSettingsResult _tmdbSettings; @@ -145,25 +149,17 @@ namespace MediaBrowser.Providers.Tmdb.Movies return _tmdbSettings; } - using (HttpResponseInfo response = await GetMovieDbResponse(new HttpRequestOptions - { - Url = string.Format(TmdbConfigUrl, TmdbUtils.ApiKey), - CancellationToken = cancellationToken, - AcceptHeader = TmdbUtils.AcceptHeader - - }).ConfigureAwait(false)) + using var requestMessage = new HttpRequestMessage(HttpMethod.Get, string.Format(CultureInfo.InvariantCulture, TmdbConfigUrl, TmdbUtils.ApiKey)); + foreach (var header in TmdbUtils.AcceptHeaders) { - using (Stream json = response.Content) - { - _tmdbSettings = await _jsonSerializer.DeserializeFromStreamAsync<TmdbSettingsResult>(json).ConfigureAwait(false); - - return _tmdbSettings; - } + requestMessage.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue(header)); } - } - private const string TmdbConfigUrl = TmdbUtils.BaseTmdbApiUrl + "3/configuration?api_key={0}"; - private const string GetMovieInfo3 = TmdbUtils.BaseTmdbApiUrl + @"3/movie/{0}?api_key={1}&append_to_response=casts,releases,images,keywords,trailers"; + using var response = await GetMovieDbResponse(requestMessage, cancellationToken).ConfigureAwait(false); + await using var stream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false); + _tmdbSettings = await _jsonSerializer.DeserializeFromStreamAsync<TmdbSettingsResult>(stream).ConfigureAwait(false); + return _tmdbSettings; + } /// <summary> /// Gets the movie data path. @@ -196,7 +192,10 @@ namespace MediaBrowser.Providers.Tmdb.Movies { var mainResult = await FetchMainResult(id, true, preferredMetadataLanguage, cancellationToken).ConfigureAwait(false); - if (mainResult == null) return; + if (mainResult == null) + { + return; + } var dataFilePath = GetDataFilePath(id, preferredMetadataLanguage); @@ -242,7 +241,7 @@ namespace MediaBrowser.Providers.Tmdb.Movies preferredLanguage = "alllang"; } - var filename = string.Format("all-{0}.json", preferredLanguage); + var filename = string.Format(CultureInfo.InvariantCulture, "all-{0}.json", preferredLanguage); return Path.Combine(path, filename); } @@ -273,7 +272,7 @@ namespace MediaBrowser.Providers.Tmdb.Movies languages.Add("en"); } - return string.Join(",", languages.ToArray()); + return string.Join(",", languages); } public static string NormalizeLanguage(string language) @@ -314,57 +313,37 @@ namespace MediaBrowser.Providers.Tmdb.Movies /// <param name="id">The id.</param> /// <param name="isTmdbId">if set to <c>true</c> [is TMDB identifier].</param> /// <param name="language">The language.</param> - /// <param name="cancellationToken">The cancellation token</param> + /// <param name="cancellationToken">The cancellation token.</param> /// <returns>Task{CompleteMovieData}.</returns> internal async Task<MovieResult> FetchMainResult(string id, bool isTmdbId, string language, CancellationToken cancellationToken) { - var url = string.Format(GetMovieInfo3, id, TmdbUtils.ApiKey); + var url = string.Format(CultureInfo.InvariantCulture, GetMovieInfo3, id, TmdbUtils.ApiKey); if (!string.IsNullOrEmpty(language)) { - url += string.Format("&language={0}", NormalizeLanguage(language)); + url += string.Format(CultureInfo.InvariantCulture, "&language={0}", NormalizeLanguage(language)); // Get images in english and with no language url += "&include_image_language=" + GetImageLanguagesParam(language); } - MovieResult mainResult; - cancellationToken.ThrowIfCancellationRequested(); - // Cache if not using a tmdbId because we won't have the tmdb cache directory structure. So use the lower level cache. - var cacheMode = isTmdbId ? CacheMode.None : CacheMode.Unconditional; - var cacheLength = TimeSpan.FromDays(3); - - try + using var requestMessage = new HttpRequestMessage(HttpMethod.Get, url); + foreach (var header in TmdbUtils.AcceptHeaders) { - using (var response = await GetMovieDbResponse(new HttpRequestOptions - { - Url = url, - CancellationToken = cancellationToken, - AcceptHeader = TmdbUtils.AcceptHeader, - CacheMode = cacheMode, - CacheLength = cacheLength - - }).ConfigureAwait(false)) - { - using (var json = response.Content) - { - mainResult = await _jsonSerializer.DeserializeFromStreamAsync<MovieResult>(json).ConfigureAwait(false); - } - } + requestMessage.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue(header)); } - catch (HttpException ex) - { - // Return null so that callers know there is no metadata for this id - if (ex.StatusCode.HasValue && ex.StatusCode.Value == HttpStatusCode.NotFound) - { - return null; - } - throw; + using var mainResponse = await GetMovieDbResponse(requestMessage, cancellationToken).ConfigureAwait(false); + if (mainResponse.StatusCode == HttpStatusCode.NotFound) + { + return null; } + await using var stream = await mainResponse.Content.ReadAsStreamAsync().ConfigureAwait(false); + var mainResult = await _jsonSerializer.DeserializeFromStreamAsync<MovieResult>(stream).ConfigureAwait(false); + cancellationToken.ThrowIfCancellationRequested(); // If the language preference isn't english, then have the overview fallback to english if it's blank @@ -375,7 +354,7 @@ namespace MediaBrowser.Providers.Tmdb.Movies { _logger.LogInformation("MovieDbProvider couldn't find meta for language " + language + ". Trying English..."); - url = string.Format(GetMovieInfo3, id, TmdbUtils.ApiKey) + "&language=en"; + url = string.Format(CultureInfo.InvariantCulture, GetMovieInfo3, id, TmdbUtils.ApiKey) + "&language=en"; if (!string.IsNullOrEmpty(language)) { @@ -383,65 +362,38 @@ namespace MediaBrowser.Providers.Tmdb.Movies url += "&include_image_language=" + GetImageLanguagesParam(language); } - using (var response = await GetMovieDbResponse(new HttpRequestOptions + using var langRequestMessage = new HttpRequestMessage(HttpMethod.Get, url); + foreach (var header in TmdbUtils.AcceptHeaders) { - Url = url, - CancellationToken = cancellationToken, - AcceptHeader = TmdbUtils.AcceptHeader, - CacheMode = cacheMode, - CacheLength = cacheLength + langRequestMessage.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue(header)); + } - }).ConfigureAwait(false)) - { - using (var json = response.Content) - { - var englishResult = await _jsonSerializer.DeserializeFromStreamAsync<MovieResult>(json).ConfigureAwait(false); + using var langResponse = await GetMovieDbResponse(langRequestMessage, cancellationToken).ConfigureAwait(false); - mainResult.Overview = englishResult.Overview; - } - } + await using var langStream = await langResponse.Content.ReadAsStreamAsync().ConfigureAwait(false); + var langResult = await _jsonSerializer.DeserializeFromStreamAsync<MovieResult>(stream).ConfigureAwait(false); + mainResult.Overview = langResult.Overview; } return mainResult; } - private static long _lastRequestTicks; - // The limit is 40 requests per 10 seconds - private const int RequestIntervalMs = 300; - /// <summary> /// Gets the movie db response. /// </summary> - internal async Task<HttpResponseInfo> GetMovieDbResponse(HttpRequestOptions options) + internal Task<HttpResponseMessage> GetMovieDbResponse(HttpRequestMessage message, CancellationToken cancellationToken = default) { - var delayTicks = (RequestIntervalMs * 10000) - (DateTime.UtcNow.Ticks - _lastRequestTicks); - var delayMs = Math.Min(delayTicks / 10000, RequestIntervalMs); - - if (delayMs > 0) - { - _logger.LogDebug("Throttling Tmdb by {0} ms", delayMs); - await Task.Delay(Convert.ToInt32(delayMs)).ConfigureAwait(false); - } - - _lastRequestTicks = DateTime.UtcNow.Ticks; - - options.BufferContent = true; - options.UserAgent = _appHost.ApplicationUserAgent; - - return await _httpClient.SendAsync(options, HttpMethod.Get).ConfigureAwait(false); + message.Headers.UserAgent.ParseAdd(_appHost.ApplicationUserAgent); + return _httpClientFactory.CreateClient(NamedClient.Default).SendAsync(message, cancellationToken); } /// <inheritdoc /> public int Order => 1; /// <inheritdoc /> - public Task<HttpResponseInfo> GetImageResponse(string url, CancellationToken cancellationToken) + public Task<HttpResponseMessage> GetImageResponse(string url, CancellationToken cancellationToken) { - return _httpClient.GetResponse(new HttpRequestOptions - { - CancellationToken = cancellationToken, - Url = url - }); + return _httpClientFactory.CreateClient(NamedClient.Default).GetAsync(url, cancellationToken); } } } diff --git a/MediaBrowser.Providers/Plugins/Tmdb/Movies/TmdbSearch.cs b/MediaBrowser.Providers/Plugins/Tmdb/Movies/TmdbSearch.cs new file mode 100644 index 0000000000..d885cd90b3 --- /dev/null +++ b/MediaBrowser.Providers/Plugins/Tmdb/Movies/TmdbSearch.cs @@ -0,0 +1,297 @@ +#pragma warning disable CS1591 + +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; +using System.Net; +using System.Net.Http; +using System.Net.Http.Headers; +using System.Text.RegularExpressions; +using System.Threading; +using System.Threading.Tasks; +using MediaBrowser.Controller.Library; +using MediaBrowser.Controller.Providers; +using MediaBrowser.Model.Entities; +using MediaBrowser.Model.Providers; +using MediaBrowser.Model.Serialization; +using MediaBrowser.Providers.Plugins.Tmdb.Models.Search; +using Microsoft.Extensions.Logging; + +namespace MediaBrowser.Providers.Plugins.Tmdb.Movies +{ + public class TmdbSearch + { + private const string SearchUrl = TmdbUtils.BaseTmdbApiUrl + @"3/search/{3}?api_key={1}&query={0}&language={2}"; + private const string SearchUrlTvWithYear = TmdbUtils.BaseTmdbApiUrl + @"3/search/tv?api_key={1}&query={0}&language={2}&first_air_date_year={3}"; + private const string SearchUrlMovieWithYear = TmdbUtils.BaseTmdbApiUrl + @"3/search/movie?api_key={1}&query={0}&language={2}&primary_release_year={3}"; + + private static readonly CultureInfo _usCulture = new CultureInfo("en-US"); + + private static readonly Regex _cleanEnclosed = new Regex(@"\p{Ps}.*\p{Pe}", RegexOptions.Compiled); + private static readonly Regex _cleanNonWord = new Regex(@"[\W_]+", RegexOptions.Compiled); + private static readonly Regex _cleanStopWords = new Regex( + @"\b( # Start at word boundary + 19[0-9]{2}|20[0-9]{2}| # 1900-2099 + S[0-9]{2}| # Season + E[0-9]{2}| # Episode + (2160|1080|720|576|480)[ip]?| # Resolution + [xh]?264| # Encoding + (web|dvd|bd|hdtv|hd)rip| # *Rip + web|hdtv|mp4|bluray|ktr|dl|single|imageset|internal|doku|dubbed|retail|xxx|flac + ).* # Match rest of string", + RegexOptions.Compiled | RegexOptions.IgnorePatternWhitespace | RegexOptions.IgnoreCase); + + private readonly ILogger _logger; + private readonly IJsonSerializer _json; + private readonly ILibraryManager _libraryManager; + + public TmdbSearch(ILogger logger, IJsonSerializer json, ILibraryManager libraryManager) + { + _logger = logger; + _json = json; + _libraryManager = libraryManager; + } + + public Task<IEnumerable<RemoteSearchResult>> GetSearchResults(SeriesInfo idInfo, CancellationToken cancellationToken) + { + return GetSearchResults(idInfo, "tv", cancellationToken); + } + + public Task<IEnumerable<RemoteSearchResult>> GetMovieSearchResults(ItemLookupInfo idInfo, CancellationToken cancellationToken) + { + return GetSearchResults(idInfo, "movie", cancellationToken); + } + + public Task<IEnumerable<RemoteSearchResult>> GetSearchResults(BoxSetInfo idInfo, CancellationToken cancellationToken) + { + return GetSearchResults(idInfo, "collection", cancellationToken); + } + + private async Task<IEnumerable<RemoteSearchResult>> GetSearchResults(ItemLookupInfo idInfo, string searchType, CancellationToken cancellationToken) + { + var name = idInfo.Name; + var year = idInfo.Year; + + if (string.IsNullOrWhiteSpace(name)) + { + return new List<RemoteSearchResult>(); + } + + var tmdbSettings = await TmdbMovieProvider.Current.GetTmdbSettings(cancellationToken).ConfigureAwait(false); + + var tmdbImageUrl = tmdbSettings.images.GetImageUrl("original"); + + // ParseName is required here. + // Caller provides the filename with extension stripped and NOT the parsed filename + var parsedName = _libraryManager.ParseName(name); + var yearInName = parsedName.Year; + name = parsedName.Name; + year ??= yearInName; + + var language = idInfo.MetadataLanguage.ToLowerInvariant(); + + // Replace sequences of non-word characters with space + // TMDB expects a space separated list of words make sure that is the case + name = _cleanNonWord.Replace(name, " ").Trim(); + + _logger.LogInformation("TmdbSearch: Finding id for item: {0} ({1})", name, year); + var results = await GetSearchResults(name, searchType, year, language, tmdbImageUrl, cancellationToken).ConfigureAwait(false); + + if (results.Count == 0) + { + // try in english if wasn't before + if (!string.Equals(language, "en", StringComparison.OrdinalIgnoreCase)) + { + results = await GetSearchResults(name, searchType, year, "en", tmdbImageUrl, cancellationToken).ConfigureAwait(false); + } + } + + // TODO: retrying alternatives should be done outside the search + // provider so that the retry logic can be common for all search + // providers + if (results.Count == 0) + { + var name2 = parsedName.Name; + + // Remove things enclosed in []{}() etc + name2 = _cleanEnclosed.Replace(name2, string.Empty); + + // Replace sequences of non-word characters with space + name2 = _cleanNonWord.Replace(name2, " "); + + // Clean based on common stop words / tokens + name2 = _cleanStopWords.Replace(name2, string.Empty); + + // Trim whitespace + name2 = name2.Trim(); + + // Search again if the new name is different + if (!string.Equals(name2, name, StringComparison.Ordinal) && !string.IsNullOrWhiteSpace(name2)) + { + _logger.LogInformation("TmdbSearch: Finding id for item: {0} ({1})", name2, year); + results = await GetSearchResults(name2, searchType, year, language, tmdbImageUrl, cancellationToken).ConfigureAwait(false); + + if (results.Count == 0 && !string.Equals(language, "en", StringComparison.OrdinalIgnoreCase)) + { + // one more time, in english + results = await GetSearchResults(name2, searchType, year, "en", tmdbImageUrl, cancellationToken).ConfigureAwait(false); + } + } + } + + return results.Where(i => + { + if (year.HasValue && i.ProductionYear.HasValue) + { + // Allow one year tolerance + return Math.Abs(year.Value - i.ProductionYear.Value) <= 1; + } + + return true; + }); + } + + private Task<List<RemoteSearchResult>> GetSearchResults(string name, string type, int? year, string language, string baseImageUrl, CancellationToken cancellationToken) + { + switch (type) + { + case "tv": + return GetSearchResultsTv(name, year, language, baseImageUrl, cancellationToken); + default: + return GetSearchResultsGeneric(name, type, year, language, baseImageUrl, cancellationToken); + } + } + + private async Task<List<RemoteSearchResult>> GetSearchResultsGeneric(string name, string type, int? year, string language, string baseImageUrl, CancellationToken cancellationToken) + { + if (string.IsNullOrWhiteSpace(name)) + { + throw new ArgumentException("String can't be null or empty.", nameof(name)); + } + + string url3; + if (year != null && string.Equals(type, "movie", StringComparison.OrdinalIgnoreCase)) + { + url3 = string.Format( + CultureInfo.InvariantCulture, + SearchUrlMovieWithYear, + WebUtility.UrlEncode(name), + TmdbUtils.ApiKey, + language, + year); + } + else + { + url3 = string.Format( + CultureInfo.InvariantCulture, + SearchUrl, + WebUtility.UrlEncode(name), + TmdbUtils.ApiKey, + language, + type); + } + + using var requestMessage = new HttpRequestMessage(HttpMethod.Get, url3); + foreach (var header in TmdbUtils.AcceptHeaders) + { + requestMessage.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue(header)); + } + + using var response = await TmdbMovieProvider.Current.GetMovieDbResponse(requestMessage, cancellationToken).ConfigureAwait(false); + await using var stream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false); + var searchResults = await _json.DeserializeFromStreamAsync<TmdbSearchResult<MovieResult>>(stream).ConfigureAwait(false); + + var results = searchResults.Results ?? new List<MovieResult>(); + + return results + .Select(i => + { + var remoteResult = new RemoteSearchResult {SearchProviderName = TmdbMovieProvider.Current.Name, Name = i.Title ?? i.Name ?? i.Original_Title, ImageUrl = string.IsNullOrWhiteSpace(i.Poster_Path) ? null : baseImageUrl + i.Poster_Path}; + + if (!string.IsNullOrWhiteSpace(i.Release_Date)) + { + // These dates are always in this exact format + if (DateTime.TryParseExact(i.Release_Date, "yyyy-MM-dd", _usCulture, DateTimeStyles.None, out var r)) + { + remoteResult.PremiereDate = r.ToUniversalTime(); + remoteResult.ProductionYear = remoteResult.PremiereDate.Value.Year; + } + } + + remoteResult.SetProviderId(MetadataProvider.Tmdb, i.Id.ToString(_usCulture)); + + return remoteResult; + }) + .ToList(); + } + + private async Task<List<RemoteSearchResult>> GetSearchResultsTv(string name, int? year, string language, string baseImageUrl, CancellationToken cancellationToken) + { + if (string.IsNullOrWhiteSpace(name)) + { + throw new ArgumentException("String can't be null or empty.", nameof(name)); + } + + string url3; + if (year == null) + { + url3 = string.Format( + CultureInfo.InvariantCulture, + SearchUrl, + WebUtility.UrlEncode(name), + TmdbUtils.ApiKey, + language, + "tv"); + } + else + { + url3 = string.Format( + CultureInfo.InvariantCulture, + SearchUrlTvWithYear, + WebUtility.UrlEncode(name), + TmdbUtils.ApiKey, + language, + year); + } + + using var requestMessage = new HttpRequestMessage(HttpMethod.Get, url3); + foreach (var header in TmdbUtils.AcceptHeaders) + { + requestMessage.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue(header)); + } + + using var response = await TmdbMovieProvider.Current.GetMovieDbResponse(requestMessage, cancellationToken).ConfigureAwait(false); + await using var stream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false); + var searchResults = await _json.DeserializeFromStreamAsync<TmdbSearchResult<TvResult>>(stream).ConfigureAwait(false); + + var results = searchResults.Results ?? new List<TvResult>(); + + return results + .Select(i => + { + var remoteResult = new RemoteSearchResult + { + SearchProviderName = TmdbMovieProvider.Current.Name, + Name = i.Name ?? i.Original_Name, + ImageUrl = string.IsNullOrWhiteSpace(i.Poster_Path) ? null : baseImageUrl + i.Poster_Path + }; + + if (!string.IsNullOrWhiteSpace(i.First_Air_Date)) + { + // These dates are always in this exact format + if (DateTime.TryParseExact(i.First_Air_Date, "yyyy-MM-dd", _usCulture, DateTimeStyles.None, out var r)) + { + remoteResult.PremiereDate = r.ToUniversalTime(); + remoteResult.ProductionYear = remoteResult.PremiereDate.Value.Year; + } + } + + remoteResult.SetProviderId(MetadataProvider.Tmdb, i.Id.ToString(_usCulture)); + + return remoteResult; + }) + .ToList(); + } + } +} diff --git a/MediaBrowser.Providers/Tmdb/Movies/TmdbSettings.cs b/MediaBrowser.Providers/Plugins/Tmdb/Movies/TmdbSettings.cs index dca406b99a..128258ab36 100644 --- a/MediaBrowser.Providers/Tmdb/Movies/TmdbSettings.cs +++ b/MediaBrowser.Providers/Plugins/Tmdb/Movies/TmdbSettings.cs @@ -1,12 +1,17 @@ +#pragma warning disable CS1591 + using System.Collections.Generic; -namespace MediaBrowser.Providers.Tmdb.Movies +namespace MediaBrowser.Providers.Plugins.Tmdb.Movies { internal class TmdbImageSettings { public List<string> backdrop_sizes { get; set; } + public string secure_base_url { get; set; } + public List<string> poster_sizes { get; set; } + public List<string> profile_sizes { get; set; } public string GetImageUrl(string image) diff --git a/MediaBrowser.Providers/Tmdb/Music/TmdbMusicVideoProvider.cs b/MediaBrowser.Providers/Plugins/Tmdb/Music/TmdbMusicVideoProvider.cs index 81909fa38f..73e49ba5bf 100644 --- a/MediaBrowser.Providers/Tmdb/Music/TmdbMusicVideoProvider.cs +++ b/MediaBrowser.Providers/Plugins/Tmdb/Music/TmdbMusicVideoProvider.cs @@ -1,14 +1,16 @@ +#pragma warning disable CS1591 + using System; using System.Collections.Generic; +using System.Net.Http; using System.Threading; using System.Threading.Tasks; -using MediaBrowser.Common.Net; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Providers; using MediaBrowser.Model.Providers; -using MediaBrowser.Providers.Tmdb.Movies; +using MediaBrowser.Providers.Plugins.Tmdb.Movies; -namespace MediaBrowser.Providers.Tmdb.Music +namespace MediaBrowser.Providers.Plugins.Tmdb.Music { public class TmdbMusicVideoProvider : IRemoteMetadataProvider<MusicVideo, MusicVideoInfo> { @@ -24,7 +26,7 @@ namespace MediaBrowser.Providers.Tmdb.Music public string Name => TmdbMovieProvider.Current.Name; - public Task<HttpResponseInfo> GetImageResponse(string url, CancellationToken cancellationToken) + public Task<HttpResponseMessage> GetImageResponse(string url, CancellationToken cancellationToken) { throw new NotImplementedException(); } diff --git a/MediaBrowser.Providers/Tmdb/People/TmdbPersonExternalId.cs b/MediaBrowser.Providers/Plugins/Tmdb/People/TmdbPersonExternalId.cs index 2c61bc70aa..de74a7a4c7 100644 --- a/MediaBrowser.Providers/Tmdb/People/TmdbPersonExternalId.cs +++ b/MediaBrowser.Providers/Plugins/Tmdb/People/TmdbPersonExternalId.cs @@ -1,16 +1,23 @@ using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Providers; using MediaBrowser.Model.Entities; +using MediaBrowser.Model.Providers; -namespace MediaBrowser.Providers.Tmdb.People +namespace MediaBrowser.Providers.Plugins.Tmdb.People { + /// <summary> + /// External ID for a TMDB person. + /// </summary> public class TmdbPersonExternalId : IExternalId { /// <inheritdoc /> - public string Name => TmdbUtils.ProviderName; + public string ProviderName => TmdbUtils.ProviderName; /// <inheritdoc /> - public string Key => MetadataProviders.Tmdb.ToString(); + public string Key => MetadataProvider.Tmdb.ToString(); + + /// <inheritdoc /> + public ExternalIdMediaType? Type => ExternalIdMediaType.Person; /// <inheritdoc /> public string UrlFormatString => TmdbUtils.BaseTmdbUrl + "person/{0}"; diff --git a/MediaBrowser.Providers/Tmdb/People/TmdbPersonImageProvider.cs b/MediaBrowser.Providers/Plugins/Tmdb/People/TmdbPersonImageProvider.cs index e205d796ae..f2d2c8120e 100644 --- a/MediaBrowser.Providers/Tmdb/People/TmdbPersonImageProvider.cs +++ b/MediaBrowser.Providers/Plugins/Tmdb/People/TmdbPersonImageProvider.cs @@ -1,6 +1,9 @@ +#pragma warning disable CS1591 + using System; using System.Collections.Generic; using System.Linq; +using System.Net.Http; using System.Threading; using System.Threading.Tasks; using MediaBrowser.Common.Net; @@ -10,28 +13,32 @@ using MediaBrowser.Controller.Providers; using MediaBrowser.Model.Entities; using MediaBrowser.Model.Providers; using MediaBrowser.Model.Serialization; -using MediaBrowser.Providers.Tmdb.Models.General; -using MediaBrowser.Providers.Tmdb.Models.People; -using MediaBrowser.Providers.Tmdb.Movies; +using MediaBrowser.Providers.Plugins.Tmdb.Models.General; +using MediaBrowser.Providers.Plugins.Tmdb.Models.People; +using MediaBrowser.Providers.Plugins.Tmdb.Movies; -namespace MediaBrowser.Providers.Tmdb.People +namespace MediaBrowser.Providers.Plugins.Tmdb.People { public class TmdbPersonImageProvider : IRemoteImageProvider, IHasOrder { private readonly IServerConfigurationManager _config; private readonly IJsonSerializer _jsonSerializer; - private readonly IHttpClient _httpClient; + private readonly IHttpClientFactory _httpClientFactory; - public TmdbPersonImageProvider(IServerConfigurationManager config, IJsonSerializer jsonSerializer, IHttpClient httpClient) + public TmdbPersonImageProvider(IServerConfigurationManager config, IJsonSerializer jsonSerializer, IHttpClientFactory httpClientFactory) { _config = config; _jsonSerializer = jsonSerializer; - _httpClient = httpClient; + _httpClientFactory = httpClientFactory; } + public static string ProviderName => TmdbUtils.ProviderName; + + /// <inheritdoc /> public string Name => ProviderName; - public static string ProviderName => TmdbUtils.ProviderName; + /// <inheritdoc /> + public int Order => 0; public bool Supports(BaseItem item) { @@ -49,7 +56,7 @@ namespace MediaBrowser.Providers.Tmdb.People public async Task<IEnumerable<RemoteImageInfo>> GetImages(BaseItem item, CancellationToken cancellationToken) { var person = (Person)item; - var id = person.GetProviderId(MetadataProviders.Tmdb); + var id = person.GetProviderId(MetadataProvider.Tmdb); if (!string.IsNullOrEmpty(id)) { @@ -98,6 +105,7 @@ namespace MediaBrowser.Providers.Tmdb.People { return 3; } + if (!isLanguageEn) { if (string.Equals("en", i.Language, StringComparison.OrdinalIgnoreCase)) @@ -105,10 +113,12 @@ namespace MediaBrowser.Providers.Tmdb.People return 2; } } + if (string.IsNullOrEmpty(i.Language)) { return isLanguageEn ? 3 : 2; } + return 0; }) .ThenByDescending(i => i.CommunityRating ?? 0) @@ -120,15 +130,9 @@ namespace MediaBrowser.Providers.Tmdb.People return profile.Iso_639_1?.ToString(); } - public int Order => 0; - - public Task<HttpResponseInfo> GetImageResponse(string url, CancellationToken cancellationToken) + public Task<HttpResponseMessage> GetImageResponse(string url, CancellationToken cancellationToken) { - return _httpClient.GetResponse(new HttpRequestOptions - { - CancellationToken = cancellationToken, - Url = url - }); + return _httpClientFactory.CreateClient(NamedClient.Default).GetAsync(url, cancellationToken); } } } diff --git a/MediaBrowser.Providers/Tmdb/People/TmdbPersonProvider.cs b/MediaBrowser.Providers/Plugins/Tmdb/People/TmdbPersonProvider.cs index 5880011691..8d1a854d6c 100644 --- a/MediaBrowser.Providers/Tmdb/People/TmdbPersonProvider.cs +++ b/MediaBrowser.Providers/Plugins/Tmdb/People/TmdbPersonProvider.cs @@ -1,9 +1,13 @@ +#pragma warning disable CS1591 + using System; using System.Collections.Generic; using System.Globalization; using System.IO; using System.Linq; using System.Net; +using System.Net.Http; +using System.Net.Http.Headers; using System.Threading; using System.Threading.Tasks; using MediaBrowser.Common.Configuration; @@ -17,46 +21,48 @@ using MediaBrowser.Model.IO; using MediaBrowser.Model.Net; using MediaBrowser.Model.Providers; using MediaBrowser.Model.Serialization; -using MediaBrowser.Providers.Tmdb.Models.General; -using MediaBrowser.Providers.Tmdb.Models.People; -using MediaBrowser.Providers.Tmdb.Models.Search; -using MediaBrowser.Providers.Tmdb.Movies; +using MediaBrowser.Providers.Plugins.Tmdb.Models.General; +using MediaBrowser.Providers.Plugins.Tmdb.Models.People; +using MediaBrowser.Providers.Plugins.Tmdb.Models.Search; +using MediaBrowser.Providers.Plugins.Tmdb.Movies; using Microsoft.Extensions.Logging; -namespace MediaBrowser.Providers.Tmdb.People +namespace MediaBrowser.Providers.Plugins.Tmdb.People { public class TmdbPersonProvider : IRemoteMetadataProvider<Person, PersonLookupInfo> { const string DataFileName = "info.json"; - internal static TmdbPersonProvider Current { get; private set; } + private readonly CultureInfo _usCulture = new CultureInfo("en-US"); private readonly IJsonSerializer _jsonSerializer; private readonly IFileSystem _fileSystem; private readonly IServerConfigurationManager _configurationManager; - private readonly IHttpClient _httpClient; - private readonly ILogger _logger; + private readonly IHttpClientFactory _httpClientFactory; + private readonly ILogger<TmdbPersonProvider> _logger; public TmdbPersonProvider( IFileSystem fileSystem, IServerConfigurationManager configurationManager, IJsonSerializer jsonSerializer, - IHttpClient httpClient, + IHttpClientFactory httpClientFactory, ILogger<TmdbPersonProvider> logger) { _fileSystem = fileSystem; _configurationManager = configurationManager; _jsonSerializer = jsonSerializer; - _httpClient = httpClient; + _httpClientFactory = httpClientFactory; _logger = logger; Current = this; } + internal static TmdbPersonProvider Current { get; private set; } + public string Name => TmdbUtils.ProviderName; public async Task<IEnumerable<RemoteSearchResult>> GetSearchResults(PersonLookupInfo searchInfo, CancellationToken cancellationToken) { - var tmdbId = searchInfo.GetProviderId(MetadataProviders.Tmdb); + var tmdbId = searchInfo.GetProviderId(MetadataProvider.Tmdb); var tmdbSettings = await TmdbMovieProvider.Current.GetTmdbSettings(cancellationToken).ConfigureAwait(false); @@ -80,8 +86,8 @@ namespace MediaBrowser.Providers.Tmdb.People ImageUrl = images.Count == 0 ? null : (tmdbImageUrl + images[0].File_Path) }; - result.SetProviderId(MetadataProviders.Tmdb, info.Id.ToString(_usCulture)); - result.SetProviderId(MetadataProviders.Imdb, info.Imdb_Id); + result.SetProviderId(MetadataProvider.Tmdb, info.Id.ToString(_usCulture)); + result.SetProviderId(MetadataProvider.Imdb, info.Imdb_Id); return new[] { result }; } @@ -92,24 +98,25 @@ namespace MediaBrowser.Providers.Tmdb.People return new List<RemoteSearchResult>(); } - var url = string.Format(TmdbUtils.BaseTmdbApiUrl + @"3/search/person?api_key={1}&query={0}", WebUtility.UrlEncode(searchInfo.Name), TmdbUtils.ApiKey); + var url = string.Format( + CultureInfo.InvariantCulture, + TmdbUtils.BaseTmdbApiUrl + @"3/search/person?api_key={1}&query={0}", + WebUtility.UrlEncode(searchInfo.Name), + TmdbUtils.ApiKey); - using (var response = await TmdbMovieProvider.Current.GetMovieDbResponse(new HttpRequestOptions + using var requestMessage = new HttpRequestMessage(HttpMethod.Get, url); + foreach (var header in TmdbUtils.AcceptHeaders) { - Url = url, - CancellationToken = cancellationToken, - AcceptHeader = TmdbUtils.AcceptHeader + requestMessage.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue(header)); + } - }).ConfigureAwait(false)) - { - using (var json = response.Content) - { - var result = await _jsonSerializer.DeserializeFromStreamAsync<TmdbSearchResult<PersonSearchResult>>(json).ConfigureAwait(false) ?? - new TmdbSearchResult<PersonSearchResult>(); + var response = await TmdbMovieProvider.Current.GetMovieDbResponse(requestMessage, cancellationToken).ConfigureAwait(false); + await using var stream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false); - return result.Results.Select(i => GetSearchResult(i, tmdbImageUrl)); - } - } + var result2 = await _jsonSerializer.DeserializeFromStreamAsync<TmdbSearchResult<PersonSearchResult>>(stream).ConfigureAwait(false) + ?? new TmdbSearchResult<PersonSearchResult>(); + + return result2.Results.Select(i => GetSearchResult(i, tmdbImageUrl)); } private RemoteSearchResult GetSearchResult(PersonSearchResult i, string baseImageUrl) @@ -123,14 +130,14 @@ namespace MediaBrowser.Providers.Tmdb.People ImageUrl = string.IsNullOrEmpty(i.Profile_Path) ? null : baseImageUrl + i.Profile_Path }; - result.SetProviderId(MetadataProviders.Tmdb, i.Id.ToString(_usCulture)); + result.SetProviderId(MetadataProvider.Tmdb, i.Id.ToString(_usCulture)); return result; } public async Task<MetadataResult<Person>> GetMetadata(PersonLookupInfo id, CancellationToken cancellationToken) { - var tmdbId = id.GetProviderId(MetadataProviders.Tmdb); + var tmdbId = id.GetProviderId(MetadataProvider.Tmdb); // We don't already have an Id, need to fetch it if (string.IsNullOrEmpty(tmdbId)) @@ -167,12 +174,13 @@ namespace MediaBrowser.Providers.Tmdb.People // TODO: This should go in PersonMetadataService, not each person provider item.Name = id.Name; - //item.HomePageUrl = info.homepage; + // item.HomePageUrl = info.homepage; if (!string.IsNullOrWhiteSpace(info.Place_Of_Birth)) { item.ProductionLocations = new string[] { info.Place_Of_Birth }; } + item.Overview = info.Biography; if (DateTime.TryParseExact(info.Birthday, "yyyy-MM-dd", new CultureInfo("en-US"), DateTimeStyles.None, out var date)) @@ -185,11 +193,11 @@ namespace MediaBrowser.Providers.Tmdb.People item.EndDate = date.ToUniversalTime(); } - item.SetProviderId(MetadataProviders.Tmdb, info.Id.ToString(_usCulture)); + item.SetProviderId(MetadataProvider.Tmdb, info.Id.ToString(_usCulture)); if (!string.IsNullOrEmpty(info.Imdb_Id)) { - item.SetProviderId(MetadataProviders.Imdb, info.Imdb_Id); + item.SetProviderId(MetadataProvider.Imdb, info.Imdb_Id); } result.HasMetadata = true; @@ -199,8 +207,6 @@ namespace MediaBrowser.Providers.Tmdb.People return result; } - private readonly CultureInfo _usCulture = new CultureInfo("en-US"); - /// <summary> /// Gets the TMDB id. /// </summary> @@ -211,7 +217,7 @@ namespace MediaBrowser.Providers.Tmdb.People { var results = await GetSearchResults(info, cancellationToken).ConfigureAwait(false); - return results.Select(i => i.GetProviderId(MetadataProviders.Tmdb)).FirstOrDefault(); + return results.Select(i => i.GetProviderId(MetadataProvider.Tmdb)).FirstOrDefault(); } internal async Task EnsurePersonInfo(string id, CancellationToken cancellationToken) @@ -225,33 +231,29 @@ namespace MediaBrowser.Providers.Tmdb.People return; } - var url = string.Format(TmdbUtils.BaseTmdbApiUrl + @"3/person/{1}?api_key={0}&append_to_response=credits,images,external_ids", TmdbUtils.ApiKey, id); - - using (var response = await TmdbMovieProvider.Current.GetMovieDbResponse(new HttpRequestOptions - { - Url = url, - CancellationToken = cancellationToken, - AcceptHeader = TmdbUtils.AcceptHeader + var url = string.Format( + CultureInfo.InvariantCulture, + TmdbUtils.BaseTmdbApiUrl + @"3/person/{1}?api_key={0}&append_to_response=credits,images,external_ids", + TmdbUtils.ApiKey, + id); - }).ConfigureAwait(false)) + using var requestMessage = new HttpRequestMessage(HttpMethod.Get, url); + foreach (var header in TmdbUtils.AcceptHeaders) { - using (var json = response.Content) - { - Directory.CreateDirectory(Path.GetDirectoryName(dataFilePath)); - - using (var fs = new FileStream(dataFilePath, FileMode.Create, FileAccess.Write, FileShare.Read, IODefaults.FileStreamBufferSize, true)) - { - await json.CopyToAsync(fs).ConfigureAwait(false); - } - } + requestMessage.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue(header)); } + + using var response = await TmdbMovieProvider.Current.GetMovieDbResponse(requestMessage, cancellationToken).ConfigureAwait(false); + Directory.CreateDirectory(Path.GetDirectoryName(dataFilePath)); + await using var fs = new FileStream(dataFilePath, FileMode.Create, FileAccess.Write, FileShare.Read, IODefaults.FileStreamBufferSize, true); + await response.Content.CopyToAsync(fs).ConfigureAwait(false); } private static string GetPersonDataPath(IApplicationPaths appPaths, string tmdbId) { - var letter = tmdbId.GetMD5().ToString().Substring(0, 1); + var letter = tmdbId.GetMD5().ToString().AsSpan().Slice(0, 1); - return Path.Combine(GetPersonsDataPath(appPaths), letter, tmdbId); + return Path.Join(GetPersonsDataPath(appPaths), letter, tmdbId); } internal static string GetPersonDataFilePath(IApplicationPaths appPaths, string tmdbId) @@ -264,13 +266,9 @@ namespace MediaBrowser.Providers.Tmdb.People return Path.Combine(appPaths.CachePath, "tmdb-people"); } - public Task<HttpResponseInfo> GetImageResponse(string url, CancellationToken cancellationToken) + public Task<HttpResponseMessage> GetImageResponse(string url, CancellationToken cancellationToken) { - return _httpClient.GetResponse(new HttpRequestOptions - { - CancellationToken = cancellationToken, - Url = url - }); + return _httpClientFactory.CreateClient(NamedClient.Default).GetAsync(url, cancellationToken); } } } diff --git a/MediaBrowser.Providers/Tmdb/TV/TmdbEpisodeImageProvider.cs b/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbEpisodeImageProvider.cs index 558c8149e5..eebecdac67 100644 --- a/MediaBrowser.Providers/Tmdb/TV/TmdbEpisodeImageProvider.cs +++ b/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbEpisodeImageProvider.cs @@ -1,9 +1,11 @@ +#pragma warning disable CS1591 + using System; using System.Collections.Generic; using System.Linq; +using System.Net.Http; using System.Threading; using System.Threading.Tasks; -using MediaBrowser.Common.Net; using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Providers; @@ -13,19 +15,19 @@ using MediaBrowser.Model.Globalization; using MediaBrowser.Model.IO; using MediaBrowser.Model.Providers; using MediaBrowser.Model.Serialization; -using MediaBrowser.Providers.Tmdb.Models.General; -using MediaBrowser.Providers.Tmdb.Movies; +using MediaBrowser.Providers.Plugins.Tmdb.Models.General; +using MediaBrowser.Providers.Plugins.Tmdb.Movies; using Microsoft.Extensions.Logging; -namespace MediaBrowser.Providers.Tmdb.TV +namespace MediaBrowser.Providers.Plugins.Tmdb.TV { public class TmdbEpisodeImageProvider : TmdbEpisodeProviderBase, IRemoteImageProvider, IHasOrder { - public TmdbEpisodeImageProvider(IHttpClient httpClient, IServerConfigurationManager configurationManager, IJsonSerializer jsonSerializer, IFileSystem fileSystem, ILocalizationManager localization, ILoggerFactory loggerFactory) - : base(httpClient, configurationManager, jsonSerializer, fileSystem, localization, loggerFactory) + public TmdbEpisodeImageProvider(IHttpClientFactory httpClientFactory, IServerConfigurationManager configurationManager, IJsonSerializer jsonSerializer, IFileSystem fileSystem, ILocalizationManager localization, ILoggerFactory loggerFactory) + : base(httpClientFactory, configurationManager, jsonSerializer, fileSystem, localization, loggerFactory) { } public IEnumerable<ImageType> GetSupportedImages(BaseItem item) @@ -41,7 +43,7 @@ namespace MediaBrowser.Providers.Tmdb.TV var episode = (Controller.Entities.TV.Episode)item; var series = episode.Series; - var seriesId = series != null ? series.GetProviderId(MetadataProviders.Tmdb) : null; + var seriesId = series != null ? series.GetProviderId(MetadataProvider.Tmdb) : null; var list = new List<RemoteImageInfo>(); @@ -80,7 +82,6 @@ namespace MediaBrowser.Providers.Tmdb.TV RatingType = RatingType.Score })); - var isLanguageEn = string.Equals(language, "en", StringComparison.OrdinalIgnoreCase); return list.OrderByDescending(i => @@ -89,6 +90,7 @@ namespace MediaBrowser.Providers.Tmdb.TV { return 3; } + if (!isLanguageEn) { if (string.Equals("en", i.Language, StringComparison.OrdinalIgnoreCase)) @@ -96,15 +98,16 @@ namespace MediaBrowser.Providers.Tmdb.TV return 2; } } + if (string.IsNullOrEmpty(i.Language)) { return isLanguageEn ? 3 : 2; } + return 0; }) .ThenByDescending(i => i.CommunityRating ?? 0) .ThenByDescending(i => i.VoteCount ?? 0); - } private IEnumerable<Still> GetPosters(StillImages images) @@ -112,8 +115,7 @@ namespace MediaBrowser.Providers.Tmdb.TV return images.Stills ?? new List<Still>(); } - - public Task<HttpResponseInfo> GetImageResponse(string url, CancellationToken cancellationToken) + public Task<HttpResponseMessage> GetImageResponse(string url, CancellationToken cancellationToken) { return GetResponse(url, cancellationToken); } diff --git a/MediaBrowser.Providers/Tmdb/TV/TmdbEpisodeProvider.cs b/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbEpisodeProvider.cs index a17f5d17a2..ed4739acbf 100644 --- a/MediaBrowser.Providers/Tmdb/TV/TmdbEpisodeProvider.cs +++ b/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbEpisodeProvider.cs @@ -1,11 +1,13 @@ +#pragma warning disable CS1591 + using System; using System.Collections.Generic; using System.Globalization; using System.Linq; using System.Net; +using System.Net.Http; using System.Threading; using System.Threading.Tasks; -using MediaBrowser.Common.Net; using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities.TV; @@ -18,15 +20,15 @@ using MediaBrowser.Model.Providers; using MediaBrowser.Model.Serialization; using Microsoft.Extensions.Logging; -namespace MediaBrowser.Providers.Tmdb.TV +namespace MediaBrowser.Providers.Plugins.Tmdb.TV { public class TmdbEpisodeProvider : TmdbEpisodeProviderBase, IRemoteMetadataProvider<Episode, EpisodeInfo>, IHasOrder { - public TmdbEpisodeProvider(IHttpClient httpClient, IServerConfigurationManager configurationManager, IJsonSerializer jsonSerializer, IFileSystem fileSystem, ILocalizationManager localization, ILoggerFactory loggerFactory) - : base(httpClient, configurationManager, jsonSerializer, fileSystem, localization, loggerFactory) + public TmdbEpisodeProvider(IHttpClientFactory httpClientFactory, IServerConfigurationManager configurationManager, IJsonSerializer jsonSerializer, IFileSystem fileSystem, ILocalizationManager localization, ILoggerFactory loggerFactory) + : base(httpClientFactory, configurationManager, jsonSerializer, fileSystem, localization, loggerFactory) { } public async Task<IEnumerable<RemoteSearchResult>> GetSearchResults(EpisodeInfo searchInfo, CancellationToken cancellationToken) @@ -71,7 +73,7 @@ namespace MediaBrowser.Providers.Tmdb.TV return result; } - info.SeriesProviderIds.TryGetValue(MetadataProviders.Tmdb.ToString(), out string seriesTmdbId); + info.SeriesProviderIds.TryGetValue(MetadataProvider.Tmdb.ToString(), out string seriesTmdbId); if (string.IsNullOrEmpty(seriesTmdbId)) { @@ -107,9 +109,9 @@ namespace MediaBrowser.Providers.Tmdb.TV item.ParentIndexNumber = info.ParentIndexNumber; item.IndexNumberEnd = info.IndexNumberEnd; - if (response.External_Ids.Tvdb_Id > 0) + if (response.External_Ids != null && response.External_Ids.Tvdb_Id > 0) { - item.SetProviderId(MetadataProviders.Tvdb, response.External_Ids.Tvdb_Id.ToString(CultureInfo.InvariantCulture)); + item.SetProviderId(MetadataProvider.Tvdb, response.External_Ids.Tvdb_Id.Value.ToString(CultureInfo.InvariantCulture)); } item.PremiereDate = response.Air_Date; @@ -129,7 +131,7 @@ namespace MediaBrowser.Providers.Tmdb.TV { if (video.Site.Equals("youtube", System.StringComparison.OrdinalIgnoreCase)) { - var videoUrl = string.Format("http://www.youtube.com/watch?v={0}", video.Key); + var videoUrl = string.Format(CultureInfo.InvariantCulture, "http://www.youtube.com/watch?v={0}", video.Key); item.AddTrailerUrl(videoUrl); } } @@ -141,8 +143,8 @@ namespace MediaBrowser.Providers.Tmdb.TV var credits = response.Credits; if (credits != null) { - //Actors, Directors, Writers - all in People - //actors come from cast + // Actors, Directors, Writers - all in People + // actors come from cast if (credits.Cast != null) { foreach (var actor in credits.Cast.OrderBy(a => a.Order)) @@ -160,7 +162,7 @@ namespace MediaBrowser.Providers.Tmdb.TV } } - //and the rest from crew + // and the rest from crew if (credits.Crew != null) { var keepTypes = new[] @@ -199,10 +201,11 @@ namespace MediaBrowser.Providers.Tmdb.TV return result; } - public Task<HttpResponseInfo> GetImageResponse(string url, CancellationToken cancellationToken) + public Task<HttpResponseMessage> GetImageResponse(string url, CancellationToken cancellationToken) { return GetResponse(url, cancellationToken); } + // After TheTvDb public int Order => 1; diff --git a/MediaBrowser.Providers/Tmdb/TV/TmdbEpisodeProviderBase.cs b/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbEpisodeProviderBase.cs index e87fe9332f..55b0f0409e 100644 --- a/MediaBrowser.Providers/Tmdb/TV/TmdbEpisodeProviderBase.cs +++ b/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbEpisodeProviderBase.cs @@ -1,6 +1,10 @@ +#pragma warning disable CS1591 + using System; using System.Globalization; using System.IO; +using System.Net.Http; +using System.Net.Http.Headers; using System.Threading; using System.Threading.Tasks; using MediaBrowser.Common.Net; @@ -8,30 +12,30 @@ using MediaBrowser.Controller.Configuration; using MediaBrowser.Model.Globalization; using MediaBrowser.Model.IO; using MediaBrowser.Model.Serialization; -using MediaBrowser.Providers.Tmdb.Models.TV; -using MediaBrowser.Providers.Tmdb.Movies; +using MediaBrowser.Providers.Plugins.Tmdb.Models.TV; +using MediaBrowser.Providers.Plugins.Tmdb.Movies; using Microsoft.Extensions.Logging; -namespace MediaBrowser.Providers.Tmdb.TV +namespace MediaBrowser.Providers.Plugins.Tmdb.TV { public abstract class TmdbEpisodeProviderBase { private const string EpisodeUrlPattern = TmdbUtils.BaseTmdbApiUrl + @"3/tv/{0}/season/{1}/episode/{2}?api_key={3}&append_to_response=images,external_ids,credits,videos"; - private readonly IHttpClient _httpClient; + private readonly IHttpClientFactory _httpClientFactory; private readonly IServerConfigurationManager _configurationManager; private readonly IJsonSerializer _jsonSerializer; private readonly IFileSystem _fileSystem; private readonly ILocalizationManager _localization; - private readonly ILogger _logger; + private readonly ILogger<TmdbEpisodeProviderBase> _logger; - protected TmdbEpisodeProviderBase(IHttpClient httpClient, IServerConfigurationManager configurationManager, IJsonSerializer jsonSerializer, IFileSystem fileSystem, ILocalizationManager localization, ILoggerFactory loggerFactory) + protected TmdbEpisodeProviderBase(IHttpClientFactory httpClientFactory, IServerConfigurationManager configurationManager, IJsonSerializer jsonSerializer, IFileSystem fileSystem, ILocalizationManager localization, ILoggerFactory loggerFactory) { - _httpClient = httpClient; + _httpClientFactory = httpClientFactory; _configurationManager = configurationManager; _jsonSerializer = jsonSerializer; _fileSystem = fileSystem; _localization = localization; - _logger = loggerFactory.CreateLogger(GetType().Name); + _logger = loggerFactory.CreateLogger<TmdbEpisodeProviderBase>(); } protected ILogger Logger => _logger; @@ -53,6 +57,7 @@ namespace MediaBrowser.Providers.Tmdb.TV { throw new ArgumentNullException(nameof(tmdbId)); } + if (string.IsNullOrEmpty(language)) { throw new ArgumentNullException(nameof(language)); @@ -80,6 +85,7 @@ namespace MediaBrowser.Providers.Tmdb.TV { throw new ArgumentNullException(nameof(tmdbId)); } + if (string.IsNullOrEmpty(preferredLanguage)) { throw new ArgumentNullException(nameof(preferredLanguage)); @@ -87,7 +93,7 @@ namespace MediaBrowser.Providers.Tmdb.TV var path = TmdbSeriesProvider.GetSeriesDataPath(_configurationManager.ApplicationPaths, tmdbId); - var filename = string.Format("season-{0}-episode-{1}-{2}.json", + var filename = string.Format(CultureInfo.InvariantCulture, "season-{0}-episode-{1}-{2}.json", seasonNumber.ToString(CultureInfo.InvariantCulture), episodeNumber.ToString(CultureInfo.InvariantCulture), preferredLanguage); @@ -107,11 +113,17 @@ namespace MediaBrowser.Providers.Tmdb.TV internal async Task<EpisodeResult> FetchMainResult(string urlPattern, string id, int seasonNumber, int episodeNumber, string language, CancellationToken cancellationToken) { - var url = string.Format(urlPattern, id, seasonNumber.ToString(CultureInfo.InvariantCulture), episodeNumber, TmdbUtils.ApiKey); + var url = string.Format( + CultureInfo.InvariantCulture, + urlPattern, + id, + seasonNumber.ToString(CultureInfo.InvariantCulture), + episodeNumber, + TmdbUtils.ApiKey); if (!string.IsNullOrEmpty(language)) { - url += string.Format("&language={0}", language); + url += string.Format(CultureInfo.InvariantCulture, "&language={0}", language); } var includeImageLanguageParam = TmdbMovieProvider.GetImageLanguagesParam(language); @@ -120,28 +132,20 @@ namespace MediaBrowser.Providers.Tmdb.TV cancellationToken.ThrowIfCancellationRequested(); - using (var response = await TmdbMovieProvider.Current.GetMovieDbResponse(new HttpRequestOptions - { - Url = url, - CancellationToken = cancellationToken, - AcceptHeader = TmdbUtils.AcceptHeader - - }).ConfigureAwait(false)) + using var requestMessage = new HttpRequestMessage(HttpMethod.Get, url); + foreach (var header in TmdbUtils.AcceptHeaders) { - using (var json = response.Content) - { - return await _jsonSerializer.DeserializeFromStreamAsync<EpisodeResult>(json).ConfigureAwait(false); - } + requestMessage.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue(header)); } + + using var response = await TmdbMovieProvider.Current.GetMovieDbResponse(requestMessage, cancellationToken).ConfigureAwait(false); + await using var stream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false); + return await _jsonSerializer.DeserializeFromStreamAsync<EpisodeResult>(stream).ConfigureAwait(false); } - protected Task<HttpResponseInfo> GetResponse(string url, CancellationToken cancellationToken) + protected Task<HttpResponseMessage> GetResponse(string url, CancellationToken cancellationToken) { - return _httpClient.GetResponse(new HttpRequestOptions - { - CancellationToken = cancellationToken, - Url = url - }); + return _httpClientFactory.CreateClient(NamedClient.Default).GetAsync(url, cancellationToken); } } } diff --git a/MediaBrowser.Providers/Tmdb/TV/TmdbSeasonImageProvider.cs b/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeasonImageProvider.cs index 698a436046..e7e2fd05b8 100644 --- a/MediaBrowser.Providers/Tmdb/TV/TmdbSeasonImageProvider.cs +++ b/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeasonImageProvider.cs @@ -1,7 +1,10 @@ +#pragma warning disable CS1591 + using System; using System.Collections.Generic; using System.IO; using System.Linq; +using System.Net.Http; using System.Threading; using System.Threading.Tasks; using MediaBrowser.Common.Net; @@ -12,20 +15,20 @@ using MediaBrowser.Model.Dto; using MediaBrowser.Model.Entities; using MediaBrowser.Model.Providers; using MediaBrowser.Model.Serialization; -using MediaBrowser.Providers.Tmdb.Models.General; -using MediaBrowser.Providers.Tmdb.Movies; +using MediaBrowser.Providers.Plugins.Tmdb.Models.General; +using MediaBrowser.Providers.Plugins.Tmdb.Movies; -namespace MediaBrowser.Providers.Tmdb.TV +namespace MediaBrowser.Providers.Plugins.Tmdb.TV { public class TmdbSeasonImageProvider : IRemoteImageProvider, IHasOrder { private readonly IJsonSerializer _jsonSerializer; - private readonly IHttpClient _httpClient; + private readonly IHttpClientFactory _httpClientFactory; - public TmdbSeasonImageProvider(IJsonSerializer jsonSerializer, IHttpClient httpClient) + public TmdbSeasonImageProvider(IJsonSerializer jsonSerializer, IHttpClientFactory httpClientFactory) { _jsonSerializer = jsonSerializer; - _httpClient = httpClient; + _httpClientFactory = httpClientFactory; } public int Order => 1; @@ -34,13 +37,9 @@ namespace MediaBrowser.Providers.Tmdb.TV public static string ProviderName => TmdbUtils.ProviderName; - public Task<HttpResponseInfo> GetImageResponse(string url, CancellationToken cancellationToken) + public Task<HttpResponseMessage> GetImageResponse(string url, CancellationToken cancellationToken) { - return _httpClient.GetResponse(new HttpRequestOptions - { - CancellationToken = cancellationToken, - Url = url - }); + return _httpClientFactory.CreateClient(NamedClient.Default).GetAsync(url, cancellationToken); } public async Task<IEnumerable<RemoteImageInfo>> GetImages(BaseItem item, CancellationToken cancellationToken) @@ -48,7 +47,7 @@ namespace MediaBrowser.Providers.Tmdb.TV var season = (Season)item; var series = season.Series; - var seriesId = series?.GetProviderId(MetadataProviders.Tmdb); + var seriesId = series?.GetProviderId(MetadataProvider.Tmdb); if (string.IsNullOrEmpty(seriesId)) { diff --git a/MediaBrowser.Providers/Tmdb/TV/TmdbSeasonProvider.cs b/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeasonProvider.cs index 5ad3319717..40f1c4e69f 100644 --- a/MediaBrowser.Providers/Tmdb/TV/TmdbSeasonProvider.cs +++ b/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeasonProvider.cs @@ -1,8 +1,12 @@ +#pragma warning disable CS1591 + using System; using System.Collections.Generic; using System.Globalization; using System.IO; using System.Net; +using System.Net.Http; +using System.Net.Http.Headers; using System.Threading; using System.Threading.Tasks; using MediaBrowser.Common.Net; @@ -14,33 +18,33 @@ using MediaBrowser.Model.IO; using MediaBrowser.Model.Net; using MediaBrowser.Model.Providers; using MediaBrowser.Model.Serialization; -using MediaBrowser.Providers.Tmdb.Models.TV; -using MediaBrowser.Providers.Tmdb.Movies; +using MediaBrowser.Providers.Plugins.Tmdb.Models.TV; +using MediaBrowser.Providers.Plugins.Tmdb.Movies; using Microsoft.Extensions.Logging; using Season = MediaBrowser.Controller.Entities.TV.Season; -namespace MediaBrowser.Providers.Tmdb.TV +namespace MediaBrowser.Providers.Plugins.Tmdb.TV { public class TmdbSeasonProvider : IRemoteMetadataProvider<Season, SeasonInfo> { private const string GetTvInfo3 = TmdbUtils.BaseTmdbApiUrl + @"3/tv/{0}/season/{1}?api_key={2}&append_to_response=images,keywords,external_ids,credits,videos"; - private readonly IHttpClient _httpClient; + private readonly IHttpClientFactory _httpClientFactory; private readonly IServerConfigurationManager _configurationManager; private readonly IJsonSerializer _jsonSerializer; private readonly IFileSystem _fileSystem; private readonly ILocalizationManager _localization; - private readonly ILogger _logger; + private readonly ILogger<TmdbSeasonProvider> _logger; internal static TmdbSeasonProvider Current { get; private set; } - public TmdbSeasonProvider(IHttpClient httpClient, IServerConfigurationManager configurationManager, IFileSystem fileSystem, ILocalizationManager localization, IJsonSerializer jsonSerializer, ILoggerFactory loggerFactory) + public TmdbSeasonProvider(IHttpClientFactory httpClientFactory, IServerConfigurationManager configurationManager, IFileSystem fileSystem, ILocalizationManager localization, IJsonSerializer jsonSerializer, ILogger<TmdbSeasonProvider> logger) { - _httpClient = httpClient; + _httpClientFactory = httpClientFactory; _configurationManager = configurationManager; _fileSystem = fileSystem; _localization = localization; _jsonSerializer = jsonSerializer; - _logger = loggerFactory.CreateLogger(GetType().Name); + _logger = logger; Current = this; } @@ -48,7 +52,7 @@ namespace MediaBrowser.Providers.Tmdb.TV { var result = new MetadataResult<Season>(); - info.SeriesProviderIds.TryGetValue(MetadataProviders.Tmdb.ToString(), out string seriesTmdbId); + info.SeriesProviderIds.TryGetValue(MetadataProvider.Tmdb.ToString(), out string seriesTmdbId); var seasonNumber = info.IndexNumber; @@ -63,7 +67,7 @@ namespace MediaBrowser.Providers.Tmdb.TV result.Item = new Season(); // Don't use moviedb season names for now until if/when we have field-level configuration - //result.Item.Name = seasonInfo.name; + // result.Item.Name = seasonInfo.name; result.Item.Name = info.Name; @@ -71,25 +75,25 @@ namespace MediaBrowser.Providers.Tmdb.TV result.Item.Overview = seasonInfo.Overview; - if (seasonInfo.External_Ids.Tvdb_Id > 0) + if (seasonInfo.External_Ids != null && seasonInfo.External_Ids.Tvdb_Id > 0) { - result.Item.SetProviderId(MetadataProviders.Tvdb, seasonInfo.External_Ids.Tvdb_Id.ToString(CultureInfo.InvariantCulture)); + result.Item.SetProviderId(MetadataProvider.Tvdb, seasonInfo.External_Ids.Tvdb_Id.Value.ToString(CultureInfo.InvariantCulture)); } var credits = seasonInfo.Credits; if (credits != null) { - //Actors, Directors, Writers - all in People - //actors come from cast + // Actors, Directors, Writers - all in People + // actors come from cast if (credits.Cast != null) { - //foreach (var actor in credits.cast.OrderBy(a => a.order)) result.Item.AddPerson(new PersonInfo { Name = actor.name.Trim(), Role = actor.character, Type = PersonType.Actor, SortOrder = actor.order }); + // foreach (var actor in credits.cast.OrderBy(a => a.order)) result.Item.AddPerson(new PersonInfo { Name = actor.name.Trim(), Role = actor.character, Type = PersonType.Actor, SortOrder = actor.order }); } - //and the rest from crew + // and the rest from crew if (credits.Crew != null) { - //foreach (var person in credits.crew) result.Item.AddPerson(new PersonInfo { Name = person.name.Trim(), Role = person.job, Type = person.department }); + // foreach (var person in credits.crew) result.Item.AddPerson(new PersonInfo { Name = person.name.Trim(), Role = person.job, Type = person.department }); } } @@ -119,13 +123,9 @@ namespace MediaBrowser.Providers.Tmdb.TV return Task.FromResult<IEnumerable<RemoteSearchResult>>(new List<RemoteSearchResult>()); } - public Task<HttpResponseInfo> GetImageResponse(string url, CancellationToken cancellationToken) + public Task<HttpResponseMessage> GetImageResponse(string url, CancellationToken cancellationToken) { - return _httpClient.GetResponse(new HttpRequestOptions - { - CancellationToken = cancellationToken, - Url = url - }); + return _httpClientFactory.CreateClient(NamedClient.Default).GetAsync(url, cancellationToken); } private async Task<SeasonResult> GetSeasonInfo(string seriesTmdbId, int season, string preferredMetadataLanguage, @@ -145,6 +145,7 @@ namespace MediaBrowser.Providers.Tmdb.TV { throw new ArgumentNullException(nameof(tmdbId)); } + if (string.IsNullOrEmpty(language)) { throw new ArgumentNullException(nameof(language)); @@ -172,6 +173,7 @@ namespace MediaBrowser.Providers.Tmdb.TV { throw new ArgumentNullException(nameof(tmdbId)); } + if (string.IsNullOrEmpty(preferredLanguage)) { throw new ArgumentNullException(nameof(preferredLanguage)); @@ -179,7 +181,7 @@ namespace MediaBrowser.Providers.Tmdb.TV var path = TmdbSeriesProvider.GetSeriesDataPath(_configurationManager.ApplicationPaths, tmdbId); - var filename = string.Format("season-{0}-{1}.json", + var filename = string.Format(CultureInfo.InvariantCulture, "season-{0}-{1}.json", seasonNumber.ToString(CultureInfo.InvariantCulture), preferredLanguage); @@ -198,11 +200,16 @@ namespace MediaBrowser.Providers.Tmdb.TV internal async Task<SeasonResult> FetchMainResult(string id, int seasonNumber, string language, CancellationToken cancellationToken) { - var url = string.Format(GetTvInfo3, id, seasonNumber.ToString(CultureInfo.InvariantCulture), TmdbUtils.ApiKey); + var url = string.Format( + CultureInfo.InvariantCulture, + GetTvInfo3, + id, + seasonNumber.ToString(CultureInfo.InvariantCulture), + TmdbUtils.ApiKey); if (!string.IsNullOrEmpty(language)) { - url += string.Format("&language={0}", TmdbMovieProvider.NormalizeLanguage(language)); + url += string.Format(CultureInfo.InvariantCulture, "&language={0}", TmdbMovieProvider.NormalizeLanguage(language)); } var includeImageLanguageParam = TmdbMovieProvider.GetImageLanguagesParam(language); @@ -211,19 +218,15 @@ namespace MediaBrowser.Providers.Tmdb.TV cancellationToken.ThrowIfCancellationRequested(); - using (var response = await TmdbMovieProvider.Current.GetMovieDbResponse(new HttpRequestOptions + using var requestMessage = new HttpRequestMessage(HttpMethod.Get, url); + foreach (var header in TmdbUtils.AcceptHeaders) { - Url = url, - CancellationToken = cancellationToken, - AcceptHeader = TmdbUtils.AcceptHeader - - }).ConfigureAwait(false)) - { - using (var json = response.Content) - { - return await _jsonSerializer.DeserializeFromStreamAsync<SeasonResult>(json).ConfigureAwait(false); - } + requestMessage.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue(header)); } + + using var response = await TmdbMovieProvider.Current.GetMovieDbResponse(requestMessage, cancellationToken).ConfigureAwait(false); + await using var stream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false); + return await _jsonSerializer.DeserializeFromStreamAsync<SeasonResult>(stream).ConfigureAwait(false); } } } diff --git a/MediaBrowser.Providers/Tmdb/TV/TmdbSeriesExternalId.cs b/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeriesExternalId.cs index 524a3b05e2..6ecc055d71 100644 --- a/MediaBrowser.Providers/Tmdb/TV/TmdbSeriesExternalId.cs +++ b/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeriesExternalId.cs @@ -1,16 +1,23 @@ using MediaBrowser.Controller.Entities.TV; using MediaBrowser.Controller.Providers; using MediaBrowser.Model.Entities; +using MediaBrowser.Model.Providers; -namespace MediaBrowser.Providers.Tmdb.TV +namespace MediaBrowser.Providers.Plugins.Tmdb.TV { + /// <summary> + /// External ID for a TMDB series. + /// </summary> public class TmdbSeriesExternalId : IExternalId { /// <inheritdoc /> - public string Name => TmdbUtils.ProviderName; + public string ProviderName => TmdbUtils.ProviderName; /// <inheritdoc /> - public string Key => MetadataProviders.Tmdb.ToString(); + public string Key => MetadataProvider.Tmdb.ToString(); + + /// <inheritdoc /> + public ExternalIdMediaType? Type => ExternalIdMediaType.Series; /// <inheritdoc /> public string UrlFormatString => TmdbUtils.BaseTmdbUrl + "tv/{0}"; diff --git a/MediaBrowser.Providers/Tmdb/TV/TmdbSeriesImageProvider.cs b/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeriesImageProvider.cs index 0460fe9940..125560175e 100644 --- a/MediaBrowser.Providers/Tmdb/TV/TmdbSeriesImageProvider.cs +++ b/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeriesImageProvider.cs @@ -1,6 +1,9 @@ +#pragma warning disable CS1591 + using System; using System.Collections.Generic; using System.Linq; +using System.Net.Http; using System.Threading; using System.Threading.Tasks; using MediaBrowser.Common.Net; @@ -12,22 +15,22 @@ using MediaBrowser.Model.Entities; using MediaBrowser.Model.IO; using MediaBrowser.Model.Providers; using MediaBrowser.Model.Serialization; -using MediaBrowser.Providers.Tmdb.Models.General; -using MediaBrowser.Providers.Tmdb.Models.TV; -using MediaBrowser.Providers.Tmdb.Movies; +using MediaBrowser.Providers.Plugins.Tmdb.Models.General; +using MediaBrowser.Providers.Plugins.Tmdb.Models.TV; +using MediaBrowser.Providers.Plugins.Tmdb.Movies; -namespace MediaBrowser.Providers.Tmdb.TV +namespace MediaBrowser.Providers.Plugins.Tmdb.TV { public class TmdbSeriesImageProvider : IRemoteImageProvider, IHasOrder { private readonly IJsonSerializer _jsonSerializer; - private readonly IHttpClient _httpClient; + private readonly IHttpClientFactory _httpClientFactory; private readonly IFileSystem _fileSystem; - public TmdbSeriesImageProvider(IJsonSerializer jsonSerializer, IHttpClient httpClient, IFileSystem fileSystem) + public TmdbSeriesImageProvider(IJsonSerializer jsonSerializer, IHttpClientFactory httpClientFactory, IFileSystem fileSystem) { _jsonSerializer = jsonSerializer; - _httpClient = httpClient; + _httpClientFactory = httpClientFactory; _fileSystem = fileSystem; } @@ -99,6 +102,7 @@ namespace MediaBrowser.Providers.Tmdb.TV { return 3; } + if (!isLanguageEn) { if (string.Equals("en", i.Language, StringComparison.OrdinalIgnoreCase)) @@ -106,10 +110,12 @@ namespace MediaBrowser.Providers.Tmdb.TV return 2; } } + if (string.IsNullOrEmpty(i.Language)) { return isLanguageEn ? 3 : 2; } + return 0; }) .ThenByDescending(i => i.CommunityRating ?? 0) @@ -148,7 +154,7 @@ namespace MediaBrowser.Providers.Tmdb.TV private async Task<Images> FetchImages(BaseItem item, string language, IJsonSerializer jsonSerializer, CancellationToken cancellationToken) { - var tmdbId = item.GetProviderId(MetadataProviders.Tmdb); + var tmdbId = item.GetProviderId(MetadataProvider.Tmdb); if (string.IsNullOrEmpty(tmdbId)) { @@ -171,16 +177,13 @@ namespace MediaBrowser.Providers.Tmdb.TV return null; } + // After tvdb and fanart public int Order => 2; - public Task<HttpResponseInfo> GetImageResponse(string url, CancellationToken cancellationToken) + public Task<HttpResponseMessage> GetImageResponse(string url, CancellationToken cancellationToken) { - return _httpClient.GetResponse(new HttpRequestOptions - { - CancellationToken = cancellationToken, - Url = url - }); + return _httpClientFactory.CreateClient(NamedClient.Default).GetAsync(url, cancellationToken); } } } diff --git a/MediaBrowser.Providers/Tmdb/TV/TmdbSeriesProvider.cs b/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeriesProvider.cs index 7195dc42a7..fa0873b9de 100644 --- a/MediaBrowser.Providers/Tmdb/TV/TmdbSeriesProvider.cs +++ b/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeriesProvider.cs @@ -1,8 +1,12 @@ +#pragma warning disable CS1591 + using System; using System.Collections.Generic; using System.Globalization; using System.IO; using System.Linq; +using System.Net.Http; +using System.Net.Http.Headers; using System.Threading; using System.Threading.Tasks; using MediaBrowser.Common.Configuration; @@ -17,35 +21,36 @@ using MediaBrowser.Model.Globalization; using MediaBrowser.Model.IO; using MediaBrowser.Model.Providers; using MediaBrowser.Model.Serialization; -using MediaBrowser.Providers.Tmdb.Models.Search; -using MediaBrowser.Providers.Tmdb.Models.TV; -using MediaBrowser.Providers.Tmdb.Movies; +using MediaBrowser.Providers.Plugins.Tmdb.Models.Search; +using MediaBrowser.Providers.Plugins.Tmdb.Models.TV; +using MediaBrowser.Providers.Plugins.Tmdb.Movies; using Microsoft.Extensions.Logging; -namespace MediaBrowser.Providers.Tmdb.TV +namespace MediaBrowser.Providers.Plugins.Tmdb.TV { public class TmdbSeriesProvider : IRemoteMetadataProvider<Series, SeriesInfo>, IHasOrder { private const string GetTvInfo3 = TmdbUtils.BaseTmdbApiUrl + @"3/tv/{0}?api_key={1}&append_to_response=credits,images,keywords,external_ids,videos,content_ratings"; - private readonly CultureInfo _usCulture = new CultureInfo("en-US"); - - internal static TmdbSeriesProvider Current { get; private set; } private readonly IJsonSerializer _jsonSerializer; private readonly IFileSystem _fileSystem; private readonly IServerConfigurationManager _configurationManager; - private readonly ILogger _logger; + private readonly ILogger<TmdbSeriesProvider> _logger; private readonly ILocalizationManager _localization; - private readonly IHttpClient _httpClient; + private readonly IHttpClientFactory _httpClientFactory; private readonly ILibraryManager _libraryManager; + private readonly CultureInfo _usCulture = new CultureInfo("en-US"); + + internal static TmdbSeriesProvider Current { get; private set; } + public TmdbSeriesProvider( IJsonSerializer jsonSerializer, IFileSystem fileSystem, IServerConfigurationManager configurationManager, ILogger<TmdbSeriesProvider> logger, ILocalizationManager localization, - IHttpClient httpClient, + IHttpClientFactory httpClientFactory, ILibraryManager libraryManager) { _jsonSerializer = jsonSerializer; @@ -53,7 +58,7 @@ namespace MediaBrowser.Providers.Tmdb.TV _configurationManager = configurationManager; _logger = logger; _localization = localization; - _httpClient = httpClient; + _httpClientFactory = httpClientFactory; _libraryManager = libraryManager; Current = this; } @@ -62,7 +67,7 @@ namespace MediaBrowser.Providers.Tmdb.TV public async Task<IEnumerable<RemoteSearchResult>> GetSearchResults(SeriesInfo searchInfo, CancellationToken cancellationToken) { - var tmdbId = searchInfo.GetProviderId(MetadataProviders.Tmdb); + var tmdbId = searchInfo.GetProviderId(MetadataProvider.Tmdb); if (!string.IsNullOrEmpty(tmdbId)) { @@ -84,18 +89,18 @@ namespace MediaBrowser.Providers.Tmdb.TV ImageUrl = string.IsNullOrWhiteSpace(obj.Poster_Path) ? null : tmdbImageUrl + obj.Poster_Path }; - remoteResult.SetProviderId(MetadataProviders.Tmdb, obj.Id.ToString(_usCulture)); - remoteResult.SetProviderId(MetadataProviders.Imdb, obj.External_Ids.Imdb_Id); + remoteResult.SetProviderId(MetadataProvider.Tmdb, obj.Id.ToString(_usCulture)); + remoteResult.SetProviderId(MetadataProvider.Imdb, obj.External_Ids.Imdb_Id); - if (obj.External_Ids.Tvdb_Id > 0) + if (obj.External_Ids != null && obj.External_Ids.Tvdb_Id > 0) { - remoteResult.SetProviderId(MetadataProviders.Tvdb, obj.External_Ids.Tvdb_Id.ToString(_usCulture)); + remoteResult.SetProviderId(MetadataProvider.Tvdb, obj.External_Ids.Tvdb_Id.Value.ToString(_usCulture)); } return new[] { remoteResult }; } - var imdbId = searchInfo.GetProviderId(MetadataProviders.Imdb); + var imdbId = searchInfo.GetProviderId(MetadataProvider.Imdb); if (!string.IsNullOrEmpty(imdbId)) { @@ -107,7 +112,7 @@ namespace MediaBrowser.Providers.Tmdb.TV } } - var tvdbId = searchInfo.GetProviderId(MetadataProviders.Tvdb); + var tvdbId = searchInfo.GetProviderId(MetadataProvider.Tvdb); if (!string.IsNullOrEmpty(tvdbId)) { @@ -127,11 +132,11 @@ namespace MediaBrowser.Providers.Tmdb.TV var result = new MetadataResult<Series>(); result.QueriedById = true; - var tmdbId = info.GetProviderId(MetadataProviders.Tmdb); + var tmdbId = info.GetProviderId(MetadataProvider.Tmdb); if (string.IsNullOrEmpty(tmdbId)) { - var imdbId = info.GetProviderId(MetadataProviders.Imdb); + var imdbId = info.GetProviderId(MetadataProvider.Imdb); if (!string.IsNullOrEmpty(imdbId)) { @@ -139,14 +144,14 @@ namespace MediaBrowser.Providers.Tmdb.TV if (searchResult != null) { - tmdbId = searchResult.GetProviderId(MetadataProviders.Tmdb); + tmdbId = searchResult.GetProviderId(MetadataProvider.Tmdb); } } } if (string.IsNullOrEmpty(tmdbId)) { - var tvdbId = info.GetProviderId(MetadataProviders.Tvdb); + var tvdbId = info.GetProviderId(MetadataProvider.Tvdb); if (!string.IsNullOrEmpty(tvdbId)) { @@ -154,7 +159,7 @@ namespace MediaBrowser.Providers.Tmdb.TV if (searchResult != null) { - tmdbId = searchResult.GetProviderId(MetadataProviders.Tmdb); + tmdbId = searchResult.GetProviderId(MetadataProvider.Tmdb); } } } @@ -168,7 +173,7 @@ namespace MediaBrowser.Providers.Tmdb.TV if (searchResult != null) { - tmdbId = searchResult.GetProviderId(MetadataProviders.Tmdb); + tmdbId = searchResult.GetProviderId(MetadataProvider.Tmdb); } } @@ -217,9 +222,8 @@ namespace MediaBrowser.Providers.Tmdb.TV var series = seriesResult.Item; series.Name = seriesInfo.Name; - series.SetProviderId(MetadataProviders.Tmdb, seriesInfo.Id.ToString(_usCulture)); - - //series.VoteCount = seriesInfo.vote_count; + series.OriginalTitle = seriesInfo.Original_Name; + series.SetProviderId(MetadataProvider.Tmdb, seriesInfo.Id.ToString(_usCulture)); string voteAvg = seriesInfo.Vote_Average.ToString(CultureInfo.InvariantCulture); @@ -240,7 +244,7 @@ namespace MediaBrowser.Providers.Tmdb.TV series.Genres = seriesInfo.Genres.Select(i => i.Name).ToArray(); } - //series.HomePageUrl = seriesInfo.homepage; + series.HomePageUrl = seriesInfo.Homepage; series.RunTimeTicks = seriesInfo.Episode_Run_Time.Select(i => TimeSpan.FromMinutes(i).Ticks).FirstOrDefault(); @@ -261,15 +265,17 @@ namespace MediaBrowser.Providers.Tmdb.TV { if (!string.IsNullOrWhiteSpace(ids.Imdb_Id)) { - series.SetProviderId(MetadataProviders.Imdb, ids.Imdb_Id); + series.SetProviderId(MetadataProvider.Imdb, ids.Imdb_Id); } + if (ids.Tvrage_Id > 0) { - series.SetProviderId(MetadataProviders.TvRage, ids.Tvrage_Id.ToString(_usCulture)); + series.SetProviderId(MetadataProvider.TvRage, ids.Tvrage_Id.Value.ToString(_usCulture)); } + if (ids.Tvdb_Id > 0) { - series.SetProviderId(MetadataProviders.Tvdb, ids.Tvdb_Id.ToString(_usCulture)); + series.SetProviderId(MetadataProvider.Tvdb, ids.Tvdb_Id.Value.ToString(_usCulture)); } } @@ -308,29 +314,61 @@ namespace MediaBrowser.Providers.Tmdb.TV seriesResult.ResetPeople(); var tmdbImageUrl = settings.images.GetImageUrl("original"); - if (seriesInfo.Credits != null && seriesInfo.Credits.Cast != null) + if (seriesInfo.Credits != null) { - foreach (var actor in seriesInfo.Credits.Cast.OrderBy(a => a.Order)) + if (seriesInfo.Credits.Cast != null) { - var personInfo = new PersonInfo + foreach (var actor in seriesInfo.Credits.Cast.OrderBy(a => a.Order)) { - Name = actor.Name.Trim(), - Role = actor.Character, - Type = PersonType.Actor, - SortOrder = actor.Order - }; + var personInfo = new PersonInfo + { + Name = actor.Name.Trim(), + Role = actor.Character, + Type = PersonType.Actor, + SortOrder = actor.Order + }; - if (!string.IsNullOrWhiteSpace(actor.Profile_Path)) - { - personInfo.ImageUrl = tmdbImageUrl + actor.Profile_Path; + if (!string.IsNullOrWhiteSpace(actor.Profile_Path)) + { + personInfo.ImageUrl = tmdbImageUrl + actor.Profile_Path; + } + + if (actor.Id > 0) + { + personInfo.SetProviderId(MetadataProvider.Tmdb, actor.Id.ToString(CultureInfo.InvariantCulture)); + } + + seriesResult.AddPerson(personInfo); } + } - if (actor.Id > 0) + if (seriesInfo.Credits.Crew != null) + { + var keepTypes = new[] { - personInfo.SetProviderId(MetadataProviders.Tmdb, actor.Id.ToString(CultureInfo.InvariantCulture)); - } + PersonType.Director, + PersonType.Writer, + PersonType.Producer + }; + + foreach (var person in seriesInfo.Credits.Crew) + { + // Normalize this + var type = TmdbUtils.MapCrewToPersonType(person); + + if (!keepTypes.Contains(type, StringComparer.OrdinalIgnoreCase) + && !keepTypes.Contains(person.Job ?? string.Empty, StringComparer.OrdinalIgnoreCase)) + { + continue; + } - seriesResult.AddPerson(personInfo); + seriesResult.AddPerson(new PersonInfo + { + Name = person.Name.Trim(), + Role = person.Job, + Type = type + }); + } } } } @@ -367,7 +405,7 @@ namespace MediaBrowser.Providers.Tmdb.TV internal async Task<SeriesResult> FetchMainResult(string id, string language, CancellationToken cancellationToken) { - var url = string.Format(GetTvInfo3, id, TmdbUtils.ApiKey); + var url = string.Format(CultureInfo.InvariantCulture, GetTvInfo3, id, TmdbUtils.ApiKey); if (!string.IsNullOrEmpty(language)) { @@ -377,25 +415,19 @@ namespace MediaBrowser.Providers.Tmdb.TV cancellationToken.ThrowIfCancellationRequested(); - SeriesResult mainResult; - - using (var response = await TmdbMovieProvider.Current.GetMovieDbResponse(new HttpRequestOptions + using var mainRequestMessage = new HttpRequestMessage(HttpMethod.Get, url); + foreach (var header in TmdbUtils.AcceptHeaders) { - Url = url, - CancellationToken = cancellationToken, - AcceptHeader = TmdbUtils.AcceptHeader + mainRequestMessage.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue(header)); + } - }).ConfigureAwait(false)) - { - using (var json = response.Content) - { - mainResult = await _jsonSerializer.DeserializeFromStreamAsync<SeriesResult>(json).ConfigureAwait(false); + using var mainResponse = await TmdbMovieProvider.Current.GetMovieDbResponse(mainRequestMessage, cancellationToken).ConfigureAwait(false); + await using var mainStream = await mainResponse.Content.ReadAsStreamAsync().ConfigureAwait(false); + var mainResult = await _jsonSerializer.DeserializeFromStreamAsync<SeriesResult>(mainStream).ConfigureAwait(false); - if (!string.IsNullOrEmpty(language)) - { - mainResult.ResultLanguage = language; - } - } + if (!string.IsNullOrEmpty(language)) + { + mainResult.ResultLanguage = language; } cancellationToken.ThrowIfCancellationRequested(); @@ -408,7 +440,7 @@ namespace MediaBrowser.Providers.Tmdb.TV { _logger.LogInformation("MovieDbSeriesProvider couldn't find meta for language {Language}. Trying English...", language); - url = string.Format(GetTvInfo3, id, TmdbUtils.ApiKey) + "&language=en"; + url = string.Format(CultureInfo.InvariantCulture, GetTvInfo3, id, TmdbUtils.ApiKey) + "&language=en"; if (!string.IsNullOrEmpty(language)) { @@ -416,22 +448,18 @@ namespace MediaBrowser.Providers.Tmdb.TV url += "&include_image_language=" + TmdbMovieProvider.GetImageLanguagesParam(language); } - using (var response = await TmdbMovieProvider.Current.GetMovieDbResponse(new HttpRequestOptions + using var requestMessage = new HttpRequestMessage(HttpMethod.Get, url); + foreach (var header in TmdbUtils.AcceptHeaders) { - Url = url, - CancellationToken = cancellationToken, - AcceptHeader = TmdbUtils.AcceptHeader + mainRequestMessage.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue(header)); + } - }).ConfigureAwait(false)) - { - using (var json = response.Content) - { - var englishResult = await _jsonSerializer.DeserializeFromStreamAsync<SeriesResult>(json).ConfigureAwait(false); + using var response = await TmdbMovieProvider.Current.GetMovieDbResponse(requestMessage, cancellationToken).ConfigureAwait(false); + await using var stream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false); + var englishResult = await _jsonSerializer.DeserializeFromStreamAsync<SeriesResult>(stream).ConfigureAwait(false); - mainResult.Overview = englishResult.Overview; - mainResult.ResultLanguage = "en"; - } - } + mainResult.Overview = englishResult.Overview; + mainResult.ResultLanguage = "en"; } return mainResult; @@ -469,51 +497,52 @@ namespace MediaBrowser.Providers.Tmdb.TV var path = GetSeriesDataPath(_configurationManager.ApplicationPaths, tmdbId); - var filename = string.Format("series-{0}.json", preferredLanguage ?? string.Empty); + var filename = string.Format(CultureInfo.InvariantCulture, "series-{0}.json", preferredLanguage ?? string.Empty); return Path.Combine(path, filename); } private async Task<RemoteSearchResult> FindByExternalId(string id, string externalSource, CancellationToken cancellationToken) { - var url = string.Format(TmdbUtils.BaseTmdbApiUrl + @"3/find/{0}?api_key={1}&external_source={2}", + var url = string.Format( + CultureInfo.InvariantCulture, + TmdbUtils.BaseTmdbApiUrl + @"3/find/{0}?api_key={1}&external_source={2}", id, TmdbUtils.ApiKey, externalSource); - using (var response = await TmdbMovieProvider.Current.GetMovieDbResponse(new HttpRequestOptions + using var requestMessage = new HttpRequestMessage(HttpMethod.Get, url); + foreach (var header in TmdbUtils.AcceptHeaders) { - Url = url, - CancellationToken = cancellationToken, - AcceptHeader = TmdbUtils.AcceptHeader + requestMessage.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue(header)); + } - }).ConfigureAwait(false)) + using var response = await TmdbMovieProvider.Current.GetMovieDbResponse(requestMessage, cancellationToken).ConfigureAwait(false); + await using var stream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false); + + var result = await _jsonSerializer.DeserializeFromStreamAsync<ExternalIdLookupResult>(stream).ConfigureAwait(false); + + if (result != null && result.Tv_Results != null) { - using (var json = response.Content) + var tv = result.Tv_Results.FirstOrDefault(); + + if (tv != null) { - var result = await _jsonSerializer.DeserializeFromStreamAsync<ExternalIdLookupResult>(json).ConfigureAwait(false); + var tmdbSettings = await TmdbMovieProvider.Current.GetTmdbSettings(cancellationToken).ConfigureAwait(false); + var tmdbImageUrl = tmdbSettings.images.GetImageUrl("original"); - if (result != null && result.Tv_Results != null) + var remoteResult = new RemoteSearchResult { - var tv = result.Tv_Results.FirstOrDefault(); - - if (tv != null) - { - var tmdbSettings = await TmdbMovieProvider.Current.GetTmdbSettings(cancellationToken).ConfigureAwait(false); - var tmdbImageUrl = tmdbSettings.images.GetImageUrl("original"); - - var remoteResult = new RemoteSearchResult - { - Name = tv.Name, - SearchProviderName = Name, - ImageUrl = string.IsNullOrWhiteSpace(tv.Poster_Path) ? null : tmdbImageUrl + tv.Poster_Path - }; + Name = tv.Name, + SearchProviderName = Name, + ImageUrl = string.IsNullOrWhiteSpace(tv.Poster_Path) + ? null + : tmdbImageUrl + tv.Poster_Path + }; - remoteResult.SetProviderId(MetadataProviders.Tmdb, tv.Id.ToString(_usCulture)); + remoteResult.SetProviderId(MetadataProvider.Tmdb, tv.Id.ToString(_usCulture)); - return remoteResult; - } - } + return remoteResult; } } @@ -523,13 +552,9 @@ namespace MediaBrowser.Providers.Tmdb.TV // After TheTVDB public int Order => 1; - public Task<HttpResponseInfo> GetImageResponse(string url, CancellationToken cancellationToken) + public Task<HttpResponseMessage> GetImageResponse(string url, CancellationToken cancellationToken) { - return _httpClient.GetResponse(new HttpRequestOptions - { - CancellationToken = cancellationToken, - Url = url - }); + return _httpClientFactory.CreateClient(NamedClient.Default).GetAsync(url, cancellationToken); } } } diff --git a/MediaBrowser.Providers/Plugins/Tmdb/TmdbUtils.cs b/MediaBrowser.Providers/Plugins/Tmdb/TmdbUtils.cs new file mode 100644 index 0000000000..1415d69761 --- /dev/null +++ b/MediaBrowser.Providers/Plugins/Tmdb/TmdbUtils.cs @@ -0,0 +1,65 @@ +using System; +using System.Net.Mime; +using MediaBrowser.Model.Entities; +using MediaBrowser.Providers.Plugins.Tmdb.Models.General; + +namespace MediaBrowser.Providers.Plugins.Tmdb +{ + /// <summary> + /// Utilities for the TMDb provider. + /// </summary> + public static class TmdbUtils + { + /// <summary> + /// URL of the TMDB instance to use. + /// </summary> + public const string BaseTmdbUrl = "https://www.themoviedb.org/"; + + /// <summary> + /// URL of the TMDB API instance to use. + /// </summary> + public const string BaseTmdbApiUrl = "https://api.themoviedb.org/"; + + /// <summary> + /// Name of the provider. + /// </summary> + public const string ProviderName = "TheMovieDb"; + + /// <summary> + /// API key to use when performing an API call. + /// </summary> + public const string ApiKey = "4219e299c89411838049ab0dab19ebd5"; + + /// <summary> + /// Value of the Accept header for requests to the provider. + /// </summary> + public static readonly string[] AcceptHeaders = { MediaTypeNames.Application.Json, "image/*" }; + + /// <summary> + /// Maps the TMDB provided roles for crew members to Jellyfin roles. + /// </summary> + /// <param name="crew">Crew member to map against the Jellyfin person types.</param> + /// <returns>The Jellyfin person type.</returns> + public static string MapCrewToPersonType(Crew crew) + { + if (crew.Department.Equals("production", StringComparison.InvariantCultureIgnoreCase) + && crew.Job.Contains("director", StringComparison.InvariantCultureIgnoreCase)) + { + return PersonType.Director; + } + + if (crew.Department.Equals("production", StringComparison.InvariantCultureIgnoreCase) + && crew.Job.Contains("producer", StringComparison.InvariantCultureIgnoreCase)) + { + return PersonType.Producer; + } + + if (crew.Department.Equals("writing", StringComparison.InvariantCultureIgnoreCase)) + { + return PersonType.Writer; + } + + return null; + } + } +} diff --git a/MediaBrowser.Providers/Tmdb/Trailers/TmdbTrailerProvider.cs b/MediaBrowser.Providers/Plugins/Tmdb/Trailers/TmdbTrailerProvider.cs index b15de01255..25296387ba 100644 --- a/MediaBrowser.Providers/Tmdb/Trailers/TmdbTrailerProvider.cs +++ b/MediaBrowser.Providers/Plugins/Tmdb/Trailers/TmdbTrailerProvider.cs @@ -1,21 +1,24 @@ +#pragma warning disable CS1591 + using System.Collections.Generic; +using System.Net.Http; using System.Threading; using System.Threading.Tasks; using MediaBrowser.Common.Net; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Providers; using MediaBrowser.Model.Providers; -using MediaBrowser.Providers.Tmdb.Movies; +using MediaBrowser.Providers.Plugins.Tmdb.Movies; -namespace MediaBrowser.Providers.Tmdb.Trailers +namespace MediaBrowser.Providers.Plugins.Tmdb.Trailers { public class TmdbTrailerProvider : IHasOrder, IRemoteMetadataProvider<Trailer, TrailerInfo> { - private readonly IHttpClient _httpClient; + private readonly IHttpClientFactory _httpClientFactory; - public TmdbTrailerProvider(IHttpClient httpClient) + public TmdbTrailerProvider(IHttpClientFactory httpClientFactory) { - _httpClient = httpClient; + _httpClientFactory = httpClientFactory; } public Task<IEnumerable<RemoteSearchResult>> GetSearchResults(TrailerInfo searchInfo, CancellationToken cancellationToken) @@ -32,13 +35,9 @@ namespace MediaBrowser.Providers.Tmdb.Trailers public int Order => 0; - public Task<HttpResponseInfo> GetImageResponse(string url, CancellationToken cancellationToken) + public Task<HttpResponseMessage> GetImageResponse(string url, CancellationToken cancellationToken) { - return _httpClient.GetResponse(new HttpRequestOptions - { - CancellationToken = cancellationToken, - Url = url - }); + return _httpClientFactory.CreateClient(NamedClient.Default).GetAsync(url, cancellationToken); } } } diff --git a/MediaBrowser.Providers/Studios/StudioMetadataService.cs b/MediaBrowser.Providers/Studios/StudioMetadataService.cs index 847e47f1b1..78042b40de 100644 --- a/MediaBrowser.Providers/Studios/StudioMetadataService.cs +++ b/MediaBrowser.Providers/Studios/StudioMetadataService.cs @@ -1,3 +1,5 @@ +#pragma warning disable CS1591 + using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Library; @@ -21,7 +23,7 @@ namespace MediaBrowser.Providers.Studios } /// <inheritdoc /> - protected override void MergeData(MetadataResult<Studio> source, MetadataResult<Studio> target, MetadataFields[] lockedFields, bool replaceData, bool mergeMetadataSettings) + protected override void MergeData(MetadataResult<Studio> source, MetadataResult<Studio> target, MetadataField[] lockedFields, bool replaceData, bool mergeMetadataSettings) { ProviderUtils.MergeBaseItemData(source, target, lockedFields, replaceData, mergeMetadataSettings); } diff --git a/MediaBrowser.Providers/Studios/StudiosImageProvider.cs b/MediaBrowser.Providers/Studios/StudiosImageProvider.cs index cbef27a091..76dc7df7f7 100644 --- a/MediaBrowser.Providers/Studios/StudiosImageProvider.cs +++ b/MediaBrowser.Providers/Studios/StudiosImageProvider.cs @@ -1,5 +1,8 @@ +#pragma warning disable CS1591 + using System; using System.Collections.Generic; +using System.Globalization; using System.IO; using System.Linq; using System.Net.Http; @@ -18,13 +21,13 @@ namespace MediaBrowser.Providers.Studios public class StudiosImageProvider : IRemoteImageProvider { private readonly IServerConfigurationManager _config; - private readonly IHttpClient _httpClient; + private readonly IHttpClientFactory _httpClientFactory; private readonly IFileSystem _fileSystem; - public StudiosImageProvider(IServerConfigurationManager config, IHttpClient httpClient, IFileSystem fileSystem) + public StudiosImageProvider(IServerConfigurationManager config, IHttpClientFactory httpClientFactory, IFileSystem fileSystem) { _config = config; - _httpClient = httpClient; + _httpClientFactory = httpClientFactory; _fileSystem = fileSystem; } @@ -99,33 +102,29 @@ namespace MediaBrowser.Providers.Studios private string GetUrl(string image, string filename) { - return string.Format("https://raw.github.com/MediaBrowser/MediaBrowser.Resources/master/images/imagesbyname/studios/{0}/{1}.jpg", image, filename); + return string.Format(CultureInfo.InvariantCulture, "https://raw.github.com/MediaBrowser/MediaBrowser.Resources/master/images/imagesbyname/studios/{0}/{1}.jpg", image, filename); } private Task<string> EnsureThumbsList(string file, CancellationToken cancellationToken) { const string url = "https://raw.github.com/MediaBrowser/MediaBrowser.Resources/master/images/imagesbyname/studiothumbs.txt"; - return EnsureList(url, file, _httpClient, _fileSystem, cancellationToken); + return EnsureList(url, file, _fileSystem, cancellationToken); } private Task<string> EnsurePosterList(string file, CancellationToken cancellationToken) { const string url = "https://raw.github.com/MediaBrowser/MediaBrowser.Resources/master/images/imagesbyname/studioposters.txt"; - return EnsureList(url, file, _httpClient, _fileSystem, cancellationToken); + return EnsureList(url, file, _fileSystem, cancellationToken); } public int Order => 0; - public Task<HttpResponseInfo> GetImageResponse(string url, CancellationToken cancellationToken) + public Task<HttpResponseMessage> GetImageResponse(string url, CancellationToken cancellationToken) { - return _httpClient.GetResponse(new HttpRequestOptions - { - CancellationToken = cancellationToken, - Url = url, - BufferContent = false - }); + var httpClient = _httpClientFactory.CreateClient(NamedClient.Default); + return httpClient.GetAsync(url, cancellationToken); } /// <summary> @@ -133,30 +132,21 @@ namespace MediaBrowser.Providers.Studios /// </summary> /// <param name="url">The URL.</param> /// <param name="file">The file.</param> - /// <param name="httpClient">The HTTP client.</param> /// <param name="fileSystem">The file system.</param> /// <param name="cancellationToken">The cancellation token.</param> /// <returns>Task.</returns> - public async Task<string> EnsureList(string url, string file, IHttpClient httpClient, IFileSystem fileSystem, CancellationToken cancellationToken) + public async Task<string> EnsureList(string url, string file, IFileSystem fileSystem, CancellationToken cancellationToken) { var fileInfo = fileSystem.GetFileInfo(file); if (!fileInfo.Exists || (DateTime.UtcNow - fileSystem.GetLastWriteTimeUtc(fileInfo)).TotalDays > 1) { - Directory.CreateDirectory(Path.GetDirectoryName(file)); + var httpClient = _httpClientFactory.CreateClient(NamedClient.Default); - using (var res = await httpClient.SendAsync( - new HttpRequestOptions - { - CancellationToken = cancellationToken, - Url = url - }, - HttpMethod.Get).ConfigureAwait(false)) - using (var content = res.Content) - using (var fileStream = new FileStream(file, FileMode.Create)) - { - await content.CopyToAsync(fileStream).ConfigureAwait(false); - } + Directory.CreateDirectory(Path.GetDirectoryName(file)); + await using var response = await httpClient.GetStreamAsync(url).ConfigureAwait(false); + await using var fileStream = new FileStream(file, FileMode.Create); + await response.CopyToAsync(fileStream, cancellationToken).ConfigureAwait(false); } return file; @@ -201,6 +191,5 @@ namespace MediaBrowser.Providers.Studios } } } - } } diff --git a/MediaBrowser.Providers/Subtitles/SubtitleManager.cs b/MediaBrowser.Providers/Subtitles/SubtitleManager.cs index 2b55618a8e..30f390dcf5 100644 --- a/MediaBrowser.Providers/Subtitles/SubtitleManager.cs +++ b/MediaBrowser.Providers/Subtitles/SubtitleManager.cs @@ -1,3 +1,5 @@ +#pragma warning disable CS1591 + using System; using System.Collections.Generic; using System.Globalization; @@ -25,7 +27,7 @@ namespace MediaBrowser.Providers.Subtitles { public class SubtitleManager : ISubtitleManager { - private readonly ILogger _logger; + private readonly ILogger<SubtitleManager> _logger; private readonly IFileSystem _fileSystem; private readonly ILibraryMonitor _monitor; private readonly IMediaSourceManager _mediaSourceManager; @@ -104,6 +106,7 @@ namespace MediaBrowser.Providers.Subtitles _logger.LogError(ex, "Error downloading subtitles from {Provider}", provider.Name); } } + return Array.Empty<RemoteSubtitleInfo>(); } @@ -145,7 +148,7 @@ namespace MediaBrowser.Providers.Subtitles CancellationToken cancellationToken) { var parts = subtitleId.Split(new[] { '_' }, 2); - var provider = GetProvider(parts.First()); + var provider = GetProvider(parts[0]); try { @@ -327,7 +330,6 @@ namespace MediaBrowser.Providers.Subtitles Index = index, ItemId = item.Id, Type = MediaStreamType.Subtitle - }).First(); var path = stream.Path; @@ -381,9 +383,7 @@ namespace MediaBrowser.Providers.Subtitles { Name = i.Name, Id = GetProviderId(i.Name) - }).ToArray(); } - } } diff --git a/MediaBrowser.Providers/TV/DummySeasonProvider.cs b/MediaBrowser.Providers/TV/DummySeasonProvider.cs index 6a1e6df8fd..a0f7f6cfd7 100644 --- a/MediaBrowser.Providers/TV/DummySeasonProvider.cs +++ b/MediaBrowser.Providers/TV/DummySeasonProvider.cs @@ -1,3 +1,5 @@ +#pragma warning disable CS1591 + using System.Collections.Generic; using System.Globalization; using System.Linq; @@ -40,11 +42,11 @@ namespace MediaBrowser.Providers.TV if (hasNewSeasons) { - //var directoryService = new DirectoryService(_fileSystem); + // var directoryService = new DirectoryService(_fileSystem); - //await series.RefreshMetadata(new MetadataRefreshOptions(directoryService), cancellationToken).ConfigureAwait(false); + // await series.RefreshMetadata(new MetadataRefreshOptions(directoryService), cancellationToken).ConfigureAwait(false); - //await series.ValidateChildren(new SimpleProgress<double>(), cancellationToken, new MetadataRefreshOptions(directoryService)) + // await series.ValidateChildren(new SimpleProgress<double>(), cancellationToken, new MetadataRefreshOptions(directoryService)) // .ConfigureAwait(false); } @@ -72,6 +74,7 @@ namespace MediaBrowser.Providers.TV { seasons = series.Children.OfType<Season>().ToList(); } + var existingSeason = seasons .FirstOrDefault(i => i.IndexNumber.HasValue && i.IndexNumber.Value == seasonNumber); @@ -84,7 +87,7 @@ namespace MediaBrowser.Providers.TV else if (existingSeason.IsVirtualItem) { existingSeason.IsVirtualItem = false; - existingSeason.UpdateToRepository(ItemUpdateType.MetadataEdit, cancellationToken); + await existingSeason.UpdateToRepositoryAsync(ItemUpdateType.MetadataEdit, cancellationToken).ConfigureAwait(false); seasons = null; } } @@ -110,7 +113,7 @@ namespace MediaBrowser.Providers.TV else if (existingSeason.IsVirtualItem) { existingSeason.IsVirtualItem = false; - existingSeason.UpdateToRepository(ItemUpdateType.MetadataEdit, cancellationToken); + await existingSeason.UpdateToRepositoryAsync(ItemUpdateType.MetadataEdit, cancellationToken).ConfigureAwait(false); seasons = null; } } @@ -121,7 +124,8 @@ namespace MediaBrowser.Providers.TV /// <summary> /// Adds the season. /// </summary> - public async Task<Season> AddSeason(Series series, + public async Task<Season> AddSeason( + Series series, int? seasonNumber, bool isVirtualItem, CancellationToken cancellationToken) @@ -208,11 +212,14 @@ namespace MediaBrowser.Providers.TV { _logger.LogInformation("Removing virtual season {0} {1}", series.Name, seasonToRemove.IndexNumber); - _libraryManager.DeleteItem(seasonToRemove, new DeleteOptions - { - DeleteFileLocation = true + _libraryManager.DeleteItem( + seasonToRemove, + new DeleteOptions + { + DeleteFileLocation = true - }, false); + }, + false); hasChanges = true; } diff --git a/MediaBrowser.Providers/TV/EpisodeMetadataService.cs b/MediaBrowser.Providers/TV/EpisodeMetadataService.cs index 758c47ba05..170f1bdd8c 100644 --- a/MediaBrowser.Providers/TV/EpisodeMetadataService.cs +++ b/MediaBrowser.Providers/TV/EpisodeMetadataService.cs @@ -1,3 +1,5 @@ +#pragma warning disable CS1591 + using System; using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Entities.TV; @@ -66,7 +68,7 @@ namespace MediaBrowser.Providers.TV } /// <inheritdoc /> - protected override void MergeData(MetadataResult<Episode> source, MetadataResult<Episode> target, MetadataFields[] lockedFields, bool replaceData, bool mergeMetadataSettings) + protected override void MergeData(MetadataResult<Episode> source, MetadataResult<Episode> target, MetadataField[] lockedFields, bool replaceData, bool mergeMetadataSettings) { ProviderUtils.MergeBaseItemData(source, target, lockedFields, replaceData, mergeMetadataSettings); diff --git a/MediaBrowser.Providers/TV/MissingEpisodeProvider.cs b/MediaBrowser.Providers/TV/MissingEpisodeProvider.cs index 0721c4bb40..616c61ec0a 100644 --- a/MediaBrowser.Providers/TV/MissingEpisodeProvider.cs +++ b/MediaBrowser.Providers/TV/MissingEpisodeProvider.cs @@ -1,3 +1,5 @@ +#pragma warning disable CS1591 + using System; using System.Collections.Generic; using System.Globalization; @@ -46,7 +48,7 @@ namespace MediaBrowser.Providers.TV public async Task<bool> Run(Series series, bool addNewItems, CancellationToken cancellationToken) { - var tvdbId = series.GetProviderId(MetadataProviders.Tvdb); + var tvdbId = series.GetProviderId(MetadataProvider.Tvdb); if (string.IsNullOrEmpty(tvdbId)) { return false; @@ -108,7 +110,7 @@ namespace MediaBrowser.Providers.TV /// <summary> /// Returns true if a series has any seasons or episodes without season or episode numbers - /// If this data is missing no virtual items will be added in order to prevent possible duplicates + /// If this data is missing no virtual items will be added in order to prevent possible duplicates. /// </summary> private bool HasInvalidContent(IList<BaseItem> allItems) { @@ -157,7 +159,7 @@ namespace MediaBrowser.Providers.TV var now = DateTime.UtcNow.AddDays(-UnairedEpisodeThresholdDays); - if (airDate < now && addMissingEpisodes || airDate > now) + if ((airDate < now && addMissingEpisodes) || airDate > now) { // tvdb has a lot of nearly blank episodes _logger.LogInformation("Creating virtual missing/unaired episode {0} {1}x{2}", series.Name, tuple.seasonNumber, tuple.episodenumber); @@ -171,7 +173,7 @@ namespace MediaBrowser.Providers.TV } /// <summary> - /// Removes the virtual entry after a corresponding physical version has been added + /// Removes the virtual entry after a corresponding physical version has been added. /// </summary> private bool RemoveObsoleteOrMissingEpisodes( IEnumerable<BaseItem> allRecursiveChildren, @@ -230,10 +232,13 @@ namespace MediaBrowser.Providers.TV foreach (var episodeToRemove in episodesToRemove) { - _libraryManager.DeleteItem(episodeToRemove, new DeleteOptions - { - DeleteFileLocation = true - }, false); + _libraryManager.DeleteItem( + episodeToRemove, + new DeleteOptions + { + DeleteFileLocation = true + }, + false); hasChanges = true; } @@ -244,7 +249,7 @@ namespace MediaBrowser.Providers.TV /// <summary> /// Removes the obsolete or missing seasons. /// </summary> - /// <param name="allRecursiveChildren"></param> + /// <param name="allRecursiveChildren">All recursive children.</param> /// <param name="episodeLookup">The episode lookup.</param> /// <returns><see cref="bool" />.</returns> private bool RemoveObsoleteOrMissingSeasons( @@ -295,10 +300,13 @@ namespace MediaBrowser.Providers.TV foreach (var seasonToRemove in seasonsToRemove) { - _libraryManager.DeleteItem(seasonToRemove, new DeleteOptions - { - DeleteFileLocation = true - }, false); + _libraryManager.DeleteItem( + seasonToRemove, + new DeleteOptions + { + DeleteFileLocation = true + }, + false); hasChanges = true; } @@ -352,7 +360,10 @@ namespace MediaBrowser.Providers.TV /// <param name="seasonCounts"></param> /// <param name="episodeTuple"></param> /// <returns>Episode.</returns> - private Episode GetExistingEpisode(IEnumerable<Episode> existingEpisodes, IReadOnlyDictionary<int, int> seasonCounts, (int seasonNumber, int episodeNumber, DateTime firstAired) episodeTuple) + private Episode GetExistingEpisode( + IEnumerable<Episode> existingEpisodes, + IReadOnlyDictionary<int, int> seasonCounts, + (int seasonNumber, int episodeNumber, DateTime firstAired) episodeTuple) { var seasonNumber = episodeTuple.seasonNumber; var episodeNumber = episodeTuple.episodeNumber; diff --git a/MediaBrowser.Providers/TV/SeasonMetadataService.cs b/MediaBrowser.Providers/TV/SeasonMetadataService.cs index eb8032e0e1..5431de623e 100644 --- a/MediaBrowser.Providers/TV/SeasonMetadataService.cs +++ b/MediaBrowser.Providers/TV/SeasonMetadataService.cs @@ -1,3 +1,5 @@ +#pragma warning disable CS1591 + using System; using System.Collections.Generic; using System.Linq; @@ -86,7 +88,7 @@ namespace MediaBrowser.Providers.TV } /// <inheritdoc /> - protected override void MergeData(MetadataResult<Season> source, MetadataResult<Season> target, MetadataFields[] lockedFields, bool replaceData, bool mergeMetadataSettings) + protected override void MergeData(MetadataResult<Season> source, MetadataResult<Season> target, MetadataField[] lockedFields, bool replaceData, bool mergeMetadataSettings) { ProviderUtils.MergeBaseItemData(source, target, lockedFields, replaceData, mergeMetadataSettings); } diff --git a/MediaBrowser.Providers/TV/SeriesMetadataService.cs b/MediaBrowser.Providers/TV/SeriesMetadataService.cs index 5e75a8125d..a2c0e62c19 100644 --- a/MediaBrowser.Providers/TV/SeriesMetadataService.cs +++ b/MediaBrowser.Providers/TV/SeriesMetadataService.cs @@ -1,3 +1,5 @@ +#pragma warning disable CS1591 + using System; using System.Threading; using System.Threading.Tasks; @@ -42,7 +44,8 @@ namespace MediaBrowser.Providers.TV await seasonProvider.Run(item, cancellationToken).ConfigureAwait(false); // TODO why does it not register this itself omg - var provider = new MissingEpisodeProvider(Logger, + var provider = new MissingEpisodeProvider( + Logger, ServerConfigurationManager, LibraryManager, _localization, @@ -55,7 +58,7 @@ namespace MediaBrowser.Providers.TV } catch (Exception ex) { - Logger.LogError(ex, "Error in DummySeasonProvider"); + Logger.LogError(ex, "Error in DummySeasonProvider for {ItemPath}", item.Path); } } @@ -66,15 +69,17 @@ namespace MediaBrowser.Providers.TV { return false; } + if (!item.ProductionYear.HasValue) { return false; } + return base.IsFullLocalMetadata(item); } /// <inheritdoc /> - protected override void MergeData(MetadataResult<Series> source, MetadataResult<Series> target, MetadataFields[] lockedFields, bool replaceData, bool mergeMetadataSettings) + protected override void MergeData(MetadataResult<Series> source, MetadataResult<Series> target, MetadataField[] lockedFields, bool replaceData, bool mergeMetadataSettings) { ProviderUtils.MergeBaseItemData(source, target, lockedFields, replaceData, mergeMetadataSettings); diff --git a/MediaBrowser.Providers/TV/TvExternalIds.cs b/MediaBrowser.Providers/TV/TvExternalIds.cs deleted file mode 100644 index baf8542851..0000000000 --- a/MediaBrowser.Providers/TV/TvExternalIds.cs +++ /dev/null @@ -1,68 +0,0 @@ -using MediaBrowser.Controller.Entities.TV; -using MediaBrowser.Controller.Providers; -using MediaBrowser.Model.Entities; -using MediaBrowser.Providers.Plugins.TheTvdb; - -namespace MediaBrowser.Providers.TV -{ - public class Zap2ItExternalId : IExternalId - { - /// <inheritdoc /> - public string Name => "Zap2It"; - - /// <inheritdoc /> - public string Key => MetadataProviders.Zap2It.ToString(); - - /// <inheritdoc /> - public string UrlFormatString => "http://tvlistings.zap2it.com/overview.html?programSeriesId={0}"; - - /// <inheritdoc /> - public bool Supports(IHasProviderIds item) => item is Series; - } - - public class TvdbExternalId : IExternalId - { - /// <inheritdoc /> - public string Name => "TheTVDB"; - - /// <inheritdoc /> - public string Key => MetadataProviders.Tvdb.ToString(); - - /// <inheritdoc /> - public string UrlFormatString => TvdbUtils.TvdbBaseUrl + "?tab=series&id={0}"; - - /// <inheritdoc /> - public bool Supports(IHasProviderIds item) => item is Series; - - } - - public class TvdbSeasonExternalId : IExternalId - { - /// <inheritdoc /> - public string Name => "TheTVDB"; - - /// <inheritdoc /> - public string Key => MetadataProviders.Tvdb.ToString(); - - /// <inheritdoc /> - public string UrlFormatString => null; - - /// <inheritdoc /> - public bool Supports(IHasProviderIds item) => item is Season; - } - - public class TvdbEpisodeExternalId : IExternalId - { - /// <inheritdoc /> - public string Name => "TheTVDB"; - - /// <inheritdoc /> - public string Key => MetadataProviders.Tvdb.ToString(); - - /// <inheritdoc /> - public string UrlFormatString => TvdbUtils.TvdbBaseUrl + "?tab=episode&id={0}"; - - /// <inheritdoc /> - public bool Supports(IHasProviderIds item) => item is Episode; - } -} diff --git a/MediaBrowser.Providers/TV/TvdbEpisodeExternalId.cs b/MediaBrowser.Providers/TV/TvdbEpisodeExternalId.cs new file mode 100644 index 0000000000..40c5f2d785 --- /dev/null +++ b/MediaBrowser.Providers/TV/TvdbEpisodeExternalId.cs @@ -0,0 +1,28 @@ +#pragma warning disable CS1591 + +using MediaBrowser.Controller.Entities.TV; +using MediaBrowser.Controller.Providers; +using MediaBrowser.Model.Entities; +using MediaBrowser.Model.Providers; +using MediaBrowser.Providers.Plugins.TheTvdb; + +namespace MediaBrowser.Providers.TV +{ + public class TvdbEpisodeExternalId : IExternalId + { + /// <inheritdoc /> + public string ProviderName => "TheTVDB"; + + /// <inheritdoc /> + public string Key => MetadataProvider.Tvdb.ToString(); + + /// <inheritdoc /> + public ExternalIdMediaType? Type => ExternalIdMediaType.Episode; + + /// <inheritdoc /> + public string UrlFormatString => TvdbUtils.TvdbBaseUrl + "?tab=episode&id={0}"; + + /// <inheritdoc /> + public bool Supports(IHasProviderIds item) => item is Episode; + } +} diff --git a/MediaBrowser.Providers/TV/TvdbExternalId.cs b/MediaBrowser.Providers/TV/TvdbExternalId.cs new file mode 100644 index 0000000000..4c54de9f82 --- /dev/null +++ b/MediaBrowser.Providers/TV/TvdbExternalId.cs @@ -0,0 +1,28 @@ +#pragma warning disable CS1591 + +using MediaBrowser.Controller.Entities.TV; +using MediaBrowser.Controller.Providers; +using MediaBrowser.Model.Entities; +using MediaBrowser.Model.Providers; +using MediaBrowser.Providers.Plugins.TheTvdb; + +namespace MediaBrowser.Providers.TV +{ + public class TvdbExternalId : IExternalId + { + /// <inheritdoc /> + public string ProviderName => "TheTVDB"; + + /// <inheritdoc /> + public string Key => MetadataProvider.Tvdb.ToString(); + + /// <inheritdoc /> + public ExternalIdMediaType? Type => null; + + /// <inheritdoc /> + public string UrlFormatString => TvdbUtils.TvdbBaseUrl + "?tab=series&id={0}"; + + /// <inheritdoc /> + public bool Supports(IHasProviderIds item) => item is Series; + } +} diff --git a/MediaBrowser.Providers/TV/TvdbSeasonExternalId.cs b/MediaBrowser.Providers/TV/TvdbSeasonExternalId.cs new file mode 100644 index 0000000000..807ebb3eee --- /dev/null +++ b/MediaBrowser.Providers/TV/TvdbSeasonExternalId.cs @@ -0,0 +1,28 @@ +#pragma warning disable CS1591 + +using MediaBrowser.Controller.Entities.TV; +using MediaBrowser.Controller.Providers; +using MediaBrowser.Model.Entities; +using MediaBrowser.Model.Providers; +using MediaBrowser.Providers.Plugins.TheTvdb; + +namespace MediaBrowser.Providers.TV +{ + public class TvdbSeasonExternalId : IExternalId + { + /// <inheritdoc /> + public string ProviderName => "TheTVDB"; + + /// <inheritdoc /> + public string Key => MetadataProvider.Tvdb.ToString(); + + /// <inheritdoc /> + public ExternalIdMediaType? Type => ExternalIdMediaType.Season; + + /// <inheritdoc /> + public string UrlFormatString => null; + + /// <inheritdoc /> + public bool Supports(IHasProviderIds item) => item is Season; + } +} diff --git a/MediaBrowser.Providers/TV/Zap2ItExternalId.cs b/MediaBrowser.Providers/TV/Zap2ItExternalId.cs new file mode 100644 index 0000000000..c9f314af94 --- /dev/null +++ b/MediaBrowser.Providers/TV/Zap2ItExternalId.cs @@ -0,0 +1,28 @@ +#pragma warning disable CS1591 + +using MediaBrowser.Controller.Entities.TV; +using MediaBrowser.Controller.Providers; +using MediaBrowser.Model.Entities; +using MediaBrowser.Model.Providers; +using MediaBrowser.Providers.Plugins.TheTvdb; + +namespace MediaBrowser.Providers.TV +{ + public class Zap2ItExternalId : IExternalId + { + /// <inheritdoc /> + public string ProviderName => "Zap2It"; + + /// <inheritdoc /> + public string Key => MetadataProvider.Zap2It.ToString(); + + /// <inheritdoc /> + public ExternalIdMediaType? Type => null; + + /// <inheritdoc /> + public string UrlFormatString => "http://tvlistings.zap2it.com/overview.html?programSeriesId={0}"; + + /// <inheritdoc /> + public bool Supports(IHasProviderIds item) => item is Series; + } +} diff --git a/MediaBrowser.Providers/Tmdb/Models/People/PersonImages.cs b/MediaBrowser.Providers/Tmdb/Models/People/PersonImages.cs deleted file mode 100644 index 113f410b23..0000000000 --- a/MediaBrowser.Providers/Tmdb/Models/People/PersonImages.cs +++ /dev/null @@ -1,10 +0,0 @@ -using System.Collections.Generic; -using MediaBrowser.Providers.Tmdb.Models.General; - -namespace MediaBrowser.Providers.Tmdb.Models.People -{ - public class PersonImages - { - public List<Profile> Profiles { get; set; } - } -} diff --git a/MediaBrowser.Providers/Tmdb/Models/TV/SeasonImages.cs b/MediaBrowser.Providers/Tmdb/Models/TV/SeasonImages.cs deleted file mode 100644 index 9a93dd6ae8..0000000000 --- a/MediaBrowser.Providers/Tmdb/Models/TV/SeasonImages.cs +++ /dev/null @@ -1,10 +0,0 @@ -using System.Collections.Generic; -using MediaBrowser.Providers.Tmdb.Models.General; - -namespace MediaBrowser.Providers.Tmdb.Models.TV -{ - public class SeasonImages - { - public List<Poster> Posters { get; set; } - } -} diff --git a/MediaBrowser.Providers/Tmdb/Movies/TmdbSearch.cs b/MediaBrowser.Providers/Tmdb/Movies/TmdbSearch.cs deleted file mode 100644 index 223cef086b..0000000000 --- a/MediaBrowser.Providers/Tmdb/Movies/TmdbSearch.cs +++ /dev/null @@ -1,252 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Globalization; -using System.Linq; -using System.Net; -using System.Threading; -using System.Threading.Tasks; -using MediaBrowser.Common.Net; -using MediaBrowser.Controller.Library; -using MediaBrowser.Controller.Providers; -using MediaBrowser.Model.Entities; -using MediaBrowser.Model.Providers; -using MediaBrowser.Model.Serialization; -using MediaBrowser.Providers.Tmdb.Models.Search; -using Microsoft.Extensions.Logging; - -namespace MediaBrowser.Providers.Tmdb.Movies -{ - public class TmdbSearch - { - private static readonly CultureInfo EnUs = new CultureInfo("en-US"); - private const string Search3 = TmdbUtils.BaseTmdbApiUrl + @"3/search/{3}?api_key={1}&query={0}&language={2}"; - - private readonly ILogger _logger; - private readonly IJsonSerializer _json; - private readonly ILibraryManager _libraryManager; - - public TmdbSearch(ILogger logger, IJsonSerializer json, ILibraryManager libraryManager) - { - _logger = logger; - _json = json; - _libraryManager = libraryManager; - } - - public Task<IEnumerable<RemoteSearchResult>> GetSearchResults(SeriesInfo idInfo, CancellationToken cancellationToken) - { - return GetSearchResults(idInfo, "tv", cancellationToken); - } - - public Task<IEnumerable<RemoteSearchResult>> GetMovieSearchResults(ItemLookupInfo idInfo, CancellationToken cancellationToken) - { - return GetSearchResults(idInfo, "movie", cancellationToken); - } - - public Task<IEnumerable<RemoteSearchResult>> GetSearchResults(BoxSetInfo idInfo, CancellationToken cancellationToken) - { - return GetSearchResults(idInfo, "collection", cancellationToken); - } - - private async Task<IEnumerable<RemoteSearchResult>> GetSearchResults(ItemLookupInfo idInfo, string searchType, CancellationToken cancellationToken) - { - var name = idInfo.Name; - var year = idInfo.Year; - - if (string.IsNullOrWhiteSpace(name)) - { - return new List<RemoteSearchResult>(); - } - - var tmdbSettings = await TmdbMovieProvider.Current.GetTmdbSettings(cancellationToken).ConfigureAwait(false); - - var tmdbImageUrl = tmdbSettings.images.GetImageUrl("original"); - - if (!string.IsNullOrWhiteSpace(name)) - { - var parsedName = _libraryManager.ParseName(name); - var yearInName = parsedName.Year; - name = parsedName.Name; - year = year ?? yearInName; - } - - _logger.LogInformation("MovieDbProvider: Finding id for item: " + name); - var language = idInfo.MetadataLanguage.ToLowerInvariant(); - - //nope - search for it - //var searchType = item is BoxSet ? "collection" : "movie"; - - var results = await GetSearchResults(name, searchType, year, language, tmdbImageUrl, cancellationToken).ConfigureAwait(false); - - if (results.Count == 0) - { - //try in english if wasn't before - if (!string.Equals(language, "en", StringComparison.OrdinalIgnoreCase)) - { - results = await GetSearchResults(name, searchType, year, "en", tmdbImageUrl, cancellationToken).ConfigureAwait(false); - } - } - - if (results.Count == 0) - { - // try with dot and _ turned to space - var originalName = name; - - name = name.Replace(",", " "); - name = name.Replace(".", " "); - name = name.Replace("_", " "); - name = name.Replace("-", " "); - name = name.Replace("!", " "); - name = name.Replace("?", " "); - - var parenthIndex = name.IndexOf('('); - if (parenthIndex != -1) - { - name = name.Substring(0, parenthIndex); - } - - name = name.Trim(); - - // Search again if the new name is different - if (!string.Equals(name, originalName)) - { - results = await GetSearchResults(name, searchType, year, language, tmdbImageUrl, cancellationToken).ConfigureAwait(false); - - if (results.Count == 0 && !string.Equals(language, "en", StringComparison.OrdinalIgnoreCase)) - { - //one more time, in english - results = await GetSearchResults(name, searchType, year, "en", tmdbImageUrl, cancellationToken).ConfigureAwait(false); - - } - } - } - - return results.Where(i => - { - if (year.HasValue && i.ProductionYear.HasValue) - { - // Allow one year tolerance - return Math.Abs(year.Value - i.ProductionYear.Value) <= 1; - } - - return true; - }); - } - - private Task<List<RemoteSearchResult>> GetSearchResults(string name, string type, int? year, string language, string baseImageUrl, CancellationToken cancellationToken) - { - switch (type) - { - case "tv": - return GetSearchResultsTv(name, year, language, baseImageUrl, cancellationToken); - default: - return GetSearchResultsGeneric(name, type, year, language, baseImageUrl, cancellationToken); - } - } - - private async Task<List<RemoteSearchResult>> GetSearchResultsGeneric(string name, string type, int? year, string language, string baseImageUrl, CancellationToken cancellationToken) - { - if (string.IsNullOrWhiteSpace(name)) - { - throw new ArgumentException("name"); - } - - var url3 = string.Format(Search3, WebUtility.UrlEncode(name), TmdbUtils.ApiKey, language, type); - - using (var response = await TmdbMovieProvider.Current.GetMovieDbResponse(new HttpRequestOptions - { - Url = url3, - CancellationToken = cancellationToken, - AcceptHeader = TmdbUtils.AcceptHeader - - }).ConfigureAwait(false)) - { - using (var json = response.Content) - { - var searchResults = await _json.DeserializeFromStreamAsync<TmdbSearchResult<MovieResult>>(json).ConfigureAwait(false); - - var results = searchResults.Results ?? new List<MovieResult>(); - - return results - .Select(i => - { - var remoteResult = new RemoteSearchResult - { - SearchProviderName = TmdbMovieProvider.Current.Name, - Name = i.Title ?? i.Name ?? i.Original_Title, - ImageUrl = string.IsNullOrWhiteSpace(i.Poster_Path) ? null : baseImageUrl + i.Poster_Path - }; - - if (!string.IsNullOrWhiteSpace(i.Release_Date)) - { - // These dates are always in this exact format - if (DateTime.TryParseExact(i.Release_Date, "yyyy-MM-dd", EnUs, DateTimeStyles.None, out var r)) - { - remoteResult.PremiereDate = r.ToUniversalTime(); - remoteResult.ProductionYear = remoteResult.PremiereDate.Value.Year; - } - } - - remoteResult.SetProviderId(MetadataProviders.Tmdb, i.Id.ToString(EnUs)); - - return remoteResult; - - }) - .ToList(); - } - } - } - - private async Task<List<RemoteSearchResult>> GetSearchResultsTv(string name, int? year, string language, string baseImageUrl, CancellationToken cancellationToken) - { - if (string.IsNullOrWhiteSpace(name)) - { - throw new ArgumentException("name"); - } - - var url3 = string.Format(Search3, WebUtility.UrlEncode(name), TmdbUtils.ApiKey, language, "tv"); - - using (var response = await TmdbMovieProvider.Current.GetMovieDbResponse(new HttpRequestOptions - { - Url = url3, - CancellationToken = cancellationToken, - AcceptHeader = TmdbUtils.AcceptHeader - - }).ConfigureAwait(false)) - { - using (var json = response.Content) - { - var searchResults = await _json.DeserializeFromStreamAsync<TmdbSearchResult<TvResult>>(json).ConfigureAwait(false); - - var results = searchResults.Results ?? new List<TvResult>(); - - return results - .Select(i => - { - var remoteResult = new RemoteSearchResult - { - SearchProviderName = TmdbMovieProvider.Current.Name, - Name = i.Name ?? i.Original_Name, - ImageUrl = string.IsNullOrWhiteSpace(i.Poster_Path) ? null : baseImageUrl + i.Poster_Path - }; - - if (!string.IsNullOrWhiteSpace(i.First_Air_Date)) - { - // These dates are always in this exact format - if (DateTime.TryParseExact(i.First_Air_Date, "yyyy-MM-dd", EnUs, DateTimeStyles.None, out var r)) - { - remoteResult.PremiereDate = r.ToUniversalTime(); - remoteResult.ProductionYear = remoteResult.PremiereDate.Value.Year; - } - } - - remoteResult.SetProviderId(MetadataProviders.Tmdb, i.Id.ToString(EnUs)); - - return remoteResult; - - }) - .ToList(); - } - } - } - } -} diff --git a/MediaBrowser.Providers/Tmdb/TmdbUtils.cs b/MediaBrowser.Providers/Tmdb/TmdbUtils.cs deleted file mode 100644 index 035b99c1a6..0000000000 --- a/MediaBrowser.Providers/Tmdb/TmdbUtils.cs +++ /dev/null @@ -1,31 +0,0 @@ -using System; -using MediaBrowser.Model.Entities; -using MediaBrowser.Providers.Tmdb.Models.General; - -namespace MediaBrowser.Providers.Tmdb -{ - public static class TmdbUtils - { - public const string BaseTmdbUrl = "https://www.themoviedb.org/"; - public const string BaseTmdbApiUrl = "https://api.themoviedb.org/"; - public const string ProviderName = "TheMovieDb"; - public const string ApiKey = "4219e299c89411838049ab0dab19ebd5"; - public const string AcceptHeader = "application/json,image/*"; - - public static string MapCrewToPersonType(Crew crew) - { - if (crew.Department.Equals("production", StringComparison.InvariantCultureIgnoreCase) - && crew.Job.IndexOf("producer", StringComparison.InvariantCultureIgnoreCase) != -1) - { - return PersonType.Producer; - } - - if (crew.Department.Equals("writing", StringComparison.InvariantCultureIgnoreCase)) - { - return PersonType.Writer; - } - - return null; - } - } -} diff --git a/MediaBrowser.Providers/Users/UserMetadataService.cs b/MediaBrowser.Providers/Users/UserMetadataService.cs deleted file mode 100644 index fb6c91b6c0..0000000000 --- a/MediaBrowser.Providers/Users/UserMetadataService.cs +++ /dev/null @@ -1,30 +0,0 @@ -using MediaBrowser.Controller.Configuration; -using MediaBrowser.Controller.Entities; -using MediaBrowser.Controller.Library; -using MediaBrowser.Controller.Providers; -using MediaBrowser.Model.Entities; -using MediaBrowser.Model.IO; -using MediaBrowser.Providers.Manager; -using Microsoft.Extensions.Logging; - -namespace MediaBrowser.Providers.Users -{ - public class UserMetadataService : MetadataService<User, ItemLookupInfo> - { - public UserMetadataService( - IServerConfigurationManager serverConfigurationManager, - ILogger<UserMetadataService> logger, - IProviderManager providerManager, - IFileSystem fileSystem, - ILibraryManager libraryManager) - : base(serverConfigurationManager, logger, providerManager, fileSystem, libraryManager) - { - } - - /// <inheritdoc /> - protected override void MergeData(MetadataResult<User> source, MetadataResult<User> target, MetadataFields[] lockedFields, bool replaceData, bool mergeMetadataSettings) - { - ProviderUtils.MergeBaseItemData(source, target, lockedFields, replaceData, mergeMetadataSettings); - } - } -} diff --git a/MediaBrowser.Providers/Videos/VideoMetadataService.cs b/MediaBrowser.Providers/Videos/VideoMetadataService.cs index 21378ada0d..31c7eaac4b 100644 --- a/MediaBrowser.Providers/Videos/VideoMetadataService.cs +++ b/MediaBrowser.Providers/Videos/VideoMetadataService.cs @@ -1,3 +1,5 @@ +#pragma warning disable CS1591 + using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Library; @@ -26,7 +28,7 @@ namespace MediaBrowser.Providers.Videos public override int Order => 10; /// <inheritdoc /> - protected override void MergeData(MetadataResult<Video> source, MetadataResult<Video> target, MetadataFields[] lockedFields, bool replaceData, bool mergeMetadataSettings) + protected override void MergeData(MetadataResult<Video> source, MetadataResult<Video> target, MetadataField[] lockedFields, bool replaceData, bool mergeMetadataSettings) { ProviderUtils.MergeBaseItemData(source, target, lockedFields, replaceData, mergeMetadataSettings); } diff --git a/MediaBrowser.Providers/Years/YearMetadataService.cs b/MediaBrowser.Providers/Years/YearMetadataService.cs index 2a0fa19ea3..6151d12e9b 100644 --- a/MediaBrowser.Providers/Years/YearMetadataService.cs +++ b/MediaBrowser.Providers/Years/YearMetadataService.cs @@ -1,3 +1,5 @@ +#pragma warning disable CS1591 + using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Library; @@ -22,7 +24,7 @@ namespace MediaBrowser.Providers.Years } /// <inheritdoc /> - protected override void MergeData(MetadataResult<Year> source, MetadataResult<Year> target, MetadataFields[] lockedFields, bool replaceData, bool mergeMetadataSettings) + protected override void MergeData(MetadataResult<Year> source, MetadataResult<Year> target, MetadataField[] lockedFields, bool replaceData, bool mergeMetadataSettings) { ProviderUtils.MergeBaseItemData(source, target, lockedFields, replaceData, mergeMetadataSettings); } |
