aboutsummaryrefslogtreecommitdiff
path: root/MediaBrowser.Providers
diff options
context:
space:
mode:
authorOrry Verducci <orry@orryverducci.co.uk>2021-12-01 22:13:52 +0000
committerGitHub <noreply@github.com>2021-12-01 22:13:52 +0000
commite446e9fde935ad5744500e6efaab8fcacf89b600 (patch)
tree9012e91423660bf4bc9992f06cf26f53e826fb65 /MediaBrowser.Providers
parent9abe9e7e54cc454667ba2128b5d321631b5ece51 (diff)
parentf6d8c19a7ac41c6c7c217d9e9ccbf98f78122327 (diff)
Merge branch 'master' into mbaff-interlace-detection
Diffstat (limited to 'MediaBrowser.Providers')
-rw-r--r--MediaBrowser.Providers/Manager/ImageSaver.cs3
-rw-r--r--MediaBrowser.Providers/Manager/ItemImageProvider.cs152
-rw-r--r--MediaBrowser.Providers/Manager/MetadataService.cs6
-rw-r--r--MediaBrowser.Providers/MediaBrowser.Providers.csproj6
-rw-r--r--MediaBrowser.Providers/MediaInfo/AudioImageProvider.cs40
-rw-r--r--MediaBrowser.Providers/MediaInfo/EmbeddedImageProvider.cs52
-rw-r--r--MediaBrowser.Providers/MediaInfo/SubtitleResolver.cs54
-rw-r--r--MediaBrowser.Providers/MediaInfo/VideoImageProvider.cs19
-rw-r--r--MediaBrowser.Providers/Plugins/MusicBrainz/MusicBrainzAlbumProvider.cs28
-rw-r--r--MediaBrowser.Providers/Plugins/Omdb/OmdbEpisodeProvider.cs37
-rw-r--r--MediaBrowser.Providers/Plugins/Omdb/OmdbImageProvider.cs55
-rw-r--r--MediaBrowser.Providers/Plugins/Omdb/OmdbItemProvider.cs219
-rw-r--r--MediaBrowser.Providers/Plugins/Omdb/OmdbProvider.cs132
-rw-r--r--MediaBrowser.Providers/Plugins/Tmdb/BoxSets/TmdbBoxSetImageProvider.cs39
-rw-r--r--MediaBrowser.Providers/Plugins/Tmdb/Configuration/PluginConfiguration.cs15
-rw-r--r--MediaBrowser.Providers/Plugins/Tmdb/Configuration/config.html31
-rw-r--r--MediaBrowser.Providers/Plugins/Tmdb/Movies/TmdbMovieImageProvider.cs39
-rw-r--r--MediaBrowser.Providers/Plugins/Tmdb/Movies/TmdbMovieProvider.cs3
-rw-r--r--MediaBrowser.Providers/Plugins/Tmdb/People/TmdbPersonImageProvider.cs17
-rw-r--r--MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbEpisodeImageProvider.cs21
-rw-r--r--MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbEpisodeProvider.cs4
-rw-r--r--MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeasonImageProvider.cs21
-rw-r--r--MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeasonProvider.cs2
-rw-r--r--MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeriesImageProvider.cs38
-rw-r--r--MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeriesProvider.cs2
-rw-r--r--MediaBrowser.Providers/Plugins/Tmdb/TmdbClientManager.cs125
-rw-r--r--MediaBrowser.Providers/Plugins/Tmdb/TmdbUtils.cs5
-rw-r--r--MediaBrowser.Providers/TV/SeriesMetadataService.cs7
28 files changed, 588 insertions, 584 deletions
diff --git a/MediaBrowser.Providers/Manager/ImageSaver.cs b/MediaBrowser.Providers/Manager/ImageSaver.cs
index 8ded2d1446..d2a3344bef 100644
--- a/MediaBrowser.Providers/Manager/ImageSaver.cs
+++ b/MediaBrowser.Providers/Manager/ImageSaver.cs
@@ -439,9 +439,6 @@ namespace MediaBrowser.Providers.Manager
case ImageType.Backdrop:
filename = GetBackdropSaveFilename(item.GetImages(type), "backdrop", "backdrop", imageIndex);
break;
- case ImageType.Screenshot:
- filename = GetBackdropSaveFilename(item.GetImages(type), "screenshot", "screenshot", imageIndex);
- break;
default:
filename = type.ToString().ToLowerInvariant();
break;
diff --git a/MediaBrowser.Providers/Manager/ItemImageProvider.cs b/MediaBrowser.Providers/Manager/ItemImageProvider.cs
index 39372acb94..b1d73c4c41 100644
--- a/MediaBrowser.Providers/Manager/ItemImageProvider.cs
+++ b/MediaBrowser.Providers/Manager/ItemImageProvider.cs
@@ -1,7 +1,5 @@
#nullable disable
-#pragma warning disable CA1002, CS1591
-
using System;
using System.Collections.Generic;
using System.IO;
@@ -25,6 +23,9 @@ using Microsoft.Extensions.Logging;
namespace MediaBrowser.Providers.Manager
{
+ /// <summary>
+ /// Utilities for managing images attached to items.
+ /// </summary>
public class ItemImageProvider
{
private readonly ILogger _logger;
@@ -47,6 +48,12 @@ namespace MediaBrowser.Providers.Manager
ImageType.Thumb
};
+ /// <summary>
+ /// Initializes a new instance of the <see cref="ItemImageProvider"/> class.
+ /// </summary>
+ /// <param name="logger">The logger.</param>
+ /// <param name="providerManager">The provider manager for interacting with provider image references.</param>
+ /// <param name="fileSystem">The filesystem.</param>
public ItemImageProvider(ILogger logger, IProviderManager providerManager, IFileSystem fileSystem)
{
_logger = logger;
@@ -54,6 +61,13 @@ namespace MediaBrowser.Providers.Manager
_fileSystem = fileSystem;
}
+ /// <summary>
+ /// Verifies existing images have valid paths and adds any new local images provided.
+ /// </summary>
+ /// <param name="item">The <see cref="BaseItem"/> to validate images for.</param>
+ /// <param name="providers">The providers to use, must include <see cref="ILocalImageProvider"/>(s) for local scanning.</param>
+ /// <param name="directoryService">The directory service for <see cref="ILocalImageProvider"/>s to use.</param>
+ /// <returns><c>true</c> if changes were made to the item; otherwise <c>false</c>.</returns>
public bool ValidateImages(BaseItem item, IEnumerable<IImageProvider> providers, IDirectoryService directoryService)
{
var hasChanges = false;
@@ -73,6 +87,15 @@ namespace MediaBrowser.Providers.Manager
return hasChanges;
}
+ /// <summary>
+ /// Refreshes from the providers according to the given options.
+ /// </summary>
+ /// <param name="item">The <see cref="BaseItem"/> to gather images for.</param>
+ /// <param name="libraryOptions">The library options.</param>
+ /// <param name="providers">The providers to query for images.</param>
+ /// <param name="refreshOptions">The refresh options.</param>
+ /// <param name="cancellationToken">The cancellation token.</param>
+ /// <returns>The refresh result.</returns>
public async Task<RefreshResult> RefreshImages(
BaseItem item,
LibraryOptions libraryOptions,
@@ -80,14 +103,10 @@ namespace MediaBrowser.Providers.Manager
ImageRefreshOptions refreshOptions,
CancellationToken cancellationToken)
{
+ var oldBackdropImages = Array.Empty<ItemImageInfo>();
if (refreshOptions.IsReplacingImage(ImageType.Backdrop))
{
- ClearImages(item, ImageType.Backdrop);
- }
-
- if (refreshOptions.IsReplacingImage(ImageType.Screenshot))
- {
- ClearImages(item, ImageType.Screenshot);
+ oldBackdropImages = item.GetImages(ImageType.Backdrop).ToArray();
}
var result = new RefreshResult { UpdateType = ItemUpdateType.None };
@@ -95,16 +114,15 @@ namespace MediaBrowser.Providers.Manager
var typeName = item.GetType().Name;
var typeOptions = libraryOptions.GetTypeOptions(typeName) ?? new TypeOptions { Type = typeName };
- // In order to avoid duplicates, only download these if there are none already
- var backdropLimit = typeOptions.GetLimit(ImageType.Backdrop);
- var screenshotLimit = typeOptions.GetLimit(ImageType.Screenshot);
+ // track library limits, adding buffer to allow lazy replacing of current images
+ var backdropLimit = typeOptions.GetLimit(ImageType.Backdrop) + oldBackdropImages.Length;
var downloadedImages = new List<ImageType>();
foreach (var provider in providers)
{
if (provider is IRemoteImageProvider remoteProvider)
{
- await RefreshFromProvider(item, remoteProvider, refreshOptions, typeOptions, backdropLimit, screenshotLimit, downloadedImages, result, cancellationToken).ConfigureAwait(false);
+ await RefreshFromProvider(item, remoteProvider, refreshOptions, typeOptions, backdropLimit, downloadedImages, result, cancellationToken).ConfigureAwait(false);
continue;
}
@@ -114,11 +132,17 @@ namespace MediaBrowser.Providers.Manager
}
}
+ // only delete existing multi-images if new ones were added
+ if (oldBackdropImages.Length > 0 && oldBackdropImages.Length < item.GetImages(ImageType.Backdrop).Count())
+ {
+ PruneImages(item, oldBackdropImages);
+ }
+
return result;
}
/// <summary>
- /// Refreshes from provider.
+ /// Refreshes from a dynamic provider.
/// </summary>
private async Task RefreshFromProvider(
BaseItem item,
@@ -153,13 +177,14 @@ namespace MediaBrowser.Providers.Manager
if (response.Protocol == MediaProtocol.Http)
{
_logger.LogDebug("Setting image url into item {0}", item.Id);
+ var index = item.AllowsMultipleImages(imageType) ? item.GetImages(imageType).Count() : 0;
item.SetImage(
new ItemImageInfo
{
Path = response.Path,
Type = imageType
},
- 0);
+ index);
}
else
{
@@ -190,7 +215,7 @@ namespace MediaBrowser.Providers.Manager
catch (Exception ex)
{
result.ErrorMessage = ex.Message;
- _logger.LogError(ex, "Error in {provider}", provider.Name);
+ _logger.LogError(ex, "Error in {Provider}", provider.Name);
}
}
@@ -206,9 +231,8 @@ namespace MediaBrowser.Providers.Manager
/// <param name="images">The images.</param>
/// <param name="savedOptions">The saved options.</param>
/// <param name="backdropLimit">The backdrop limit.</param>
- /// <param name="screenshotLimit">The screenshot limit.</param>
/// <returns><c>true</c> if the specified item contains images; otherwise, <c>false</c>.</returns>
- private bool ContainsImages(BaseItem item, List<ImageType> images, TypeOptions savedOptions, int backdropLimit, int screenshotLimit)
+ private bool ContainsImages(BaseItem item, List<ImageType> images, TypeOptions savedOptions, int backdropLimit)
{
// Using .Any causes the creation of a DisplayClass aka. variable capture
for (var i = 0; i < _singularImages.Length; i++)
@@ -225,23 +249,17 @@ namespace MediaBrowser.Providers.Manager
return false;
}
- if (images.Contains(ImageType.Screenshot) && item.GetImages(ImageType.Screenshot).Count() < screenshotLimit)
- {
- return false;
- }
-
return true;
}
/// <summary>
- /// Refreshes from provider.
+ /// Refreshes from a remote provider.
/// </summary>
/// <param name="item">The item.</param>
/// <param name="provider">The provider.</param>
/// <param name="refreshOptions">The refresh options.</param>
/// <param name="savedOptions">The saved options.</param>
/// <param name="backdropLimit">The backdrop limit.</param>
- /// <param name="screenshotLimit">The screenshot limit.</param>
/// <param name="downloadedImages">The downloaded images.</param>
/// <param name="result">The result.</param>
/// <param name="cancellationToken">The cancellation token.</param>
@@ -252,7 +270,6 @@ namespace MediaBrowser.Providers.Manager
ImageRefreshOptions refreshOptions,
TypeOptions savedOptions,
int backdropLimit,
- int screenshotLimit,
ICollection<ImageType> downloadedImages,
RefreshResult result,
CancellationToken cancellationToken)
@@ -266,7 +283,7 @@ namespace MediaBrowser.Providers.Manager
if (!refreshOptions.ReplaceAllImages &&
refreshOptions.ReplaceImages.Length == 0 &&
- ContainsImages(item, provider.GetSupportedImages(item).ToList(), savedOptions, backdropLimit, screenshotLimit))
+ ContainsImages(item, provider.GetSupportedImages(item).ToList(), savedOptions, backdropLimit))
{
return;
}
@@ -305,13 +322,7 @@ namespace MediaBrowser.Providers.Manager
}
minWidth = savedOptions.GetMinWidth(ImageType.Backdrop);
- await DownloadBackdrops(item, ImageType.Backdrop, backdropLimit, provider, result, list, minWidth, cancellationToken).ConfigureAwait(false);
-
- if (item is IHasScreenshots hasScreenshots)
- {
- minWidth = savedOptions.GetMinWidth(ImageType.Screenshot);
- await DownloadBackdrops(item, ImageType.Screenshot, screenshotLimit, provider, result, list, minWidth, cancellationToken).ConfigureAwait(false);
- }
+ await DownloadMultiImages(item, ImageType.Backdrop, refreshOptions, backdropLimit, provider, result, list, minWidth, cancellationToken).ConfigureAwait(false);
}
catch (OperationCanceledException)
{
@@ -320,7 +331,7 @@ namespace MediaBrowser.Providers.Manager
catch (Exception ex)
{
result.ErrorMessage = ex.Message;
- _logger.LogError(ex, "Error in {provider}", provider.Name);
+ _logger.LogError(ex, "Error in {Provider}", provider.Name);
}
}
@@ -329,40 +340,36 @@ namespace MediaBrowser.Providers.Manager
return options.IsEnabled(type);
}
- private void ClearImages(BaseItem item, ImageType type)
+ private void PruneImages(BaseItem item, ItemImageInfo[] images)
{
- var deleted = false;
- var deletedImages = new List<ItemImageInfo>();
-
- foreach (var image in item.GetImages(type))
+ for (var i = 0; i < images.Length; i++)
{
- if (!image.IsLocalFile)
- {
- deletedImages.Add(image);
- continue;
- }
+ var image = images[i];
- try
- {
- _fileSystem.DeleteFile(image.Path);
- deleted = true;
- }
- catch (FileNotFoundException)
+ if (image.IsLocalFile)
{
+ try
+ {
+ _fileSystem.DeleteFile(image.Path);
+ }
+ catch (FileNotFoundException)
+ {
+ }
}
}
- item.RemoveImages(deletedImages);
-
- if (deleted)
- {
- item.ValidateImages(new DirectoryService(_fileSystem));
- }
+ item.RemoveImages(images);
}
+ /// <summary>
+ /// Merges a list of images into the provided item, validating existing images and replacing them or adding new images as necessary.
+ /// </summary>
+ /// <param name="item">The <see cref="BaseItem"/> to modify.</param>
+ /// <param name="images">The new images to place in <c>item</c>.</param>
+ /// <returns><c>true</c> if changes were made to the item; otherwise <c>false</c>.</returns>
public bool MergeImages(BaseItem item, IReadOnlyList<LocalImageInfo> images)
{
- var changed = false;
+ var changed = item.ValidateImages(new DirectoryService(_fileSystem));
for (var i = 0; i < _singularImages.Length; i++)
{
@@ -398,18 +405,6 @@ namespace MediaBrowser.Providers.Manager
currentImage.DateModified = newDateModified;
}
}
- else
- {
- var existing = item.GetImageInfo(type, 0);
- if (existing != null)
- {
- if (existing.IsLocalFile && !File.Exists(existing.Path))
- {
- item.RemoveImage(existing);
- changed = true;
- }
- }
- }
}
if (UpdateMultiImages(item, images, ImageType.Backdrop))
@@ -417,15 +412,6 @@ namespace MediaBrowser.Providers.Manager
changed = true;
}
- var hasScreenshots = item as IHasScreenshots;
- if (hasScreenshots != null)
- {
- if (UpdateMultiImages(item, images, ImageType.Screenshot))
- {
- changed = true;
- }
- }
-
return changed;
}
@@ -471,7 +457,7 @@ namespace MediaBrowser.Providers.Manager
CancellationToken cancellationToken)
{
var eligibleImages = images
- .Where(i => i.Type == type && i.Width >= minWidth)
+ .Where(i => i.Type == type && (i.Width == null || i.Width >= minWidth))
.ToList();
if (EnableImageStub(item) && eligibleImages.Count > 0)
@@ -536,7 +522,7 @@ namespace MediaBrowser.Providers.Manager
return true;
}
- if (item is IItemByName && item is not MusicArtist)
+ if (item is IItemByName and not MusicArtist)
{
var hasDualAccess = item as IHasDualAccess;
if (hasDualAccess == null || hasDualAccess.IsAccessedByName)
@@ -569,7 +555,7 @@ namespace MediaBrowser.Providers.Manager
newIndex);
}
- private async Task DownloadBackdrops(BaseItem item, ImageType imageType, int limit, IRemoteImageProvider provider, RefreshResult result, IEnumerable<RemoteImageInfo> images, int minWidth, CancellationToken cancellationToken)
+ private async Task DownloadMultiImages(BaseItem item, ImageType imageType, ImageRefreshOptions refreshOptions, int limit, IRemoteImageProvider provider, RefreshResult result, IEnumerable<RemoteImageInfo> images, int minWidth, CancellationToken cancellationToken)
{
foreach (var image in images.Where(i => i.Type == imageType))
{
@@ -609,8 +595,8 @@ namespace MediaBrowser.Providers.Manager
break;
}
- // If there's already an image of the same size, skip it
- if (response.Content.Headers.ContentLength.HasValue)
+ // If there's already an image of the same file size, skip it unless doing a full refresh
+ if (response.Content.Headers.ContentLength.HasValue && !refreshOptions.IsReplacingImage(imageType))
{
try
{
diff --git a/MediaBrowser.Providers/Manager/MetadataService.cs b/MediaBrowser.Providers/Manager/MetadataService.cs
index ffb3baeb1f..90d14a9731 100644
--- a/MediaBrowser.Providers/Manager/MetadataService.cs
+++ b/MediaBrowser.Providers/Manager/MetadataService.cs
@@ -713,7 +713,7 @@ namespace MediaBrowser.Providers.Manager
}
catch (Exception ex)
{
- Logger.LogError(ex, "Error in {provider}", provider.Name);
+ Logger.LogError(ex, "Error in {Provider}", provider.Name);
// If a local provider fails, consider that a failure
refreshResult.ErrorMessage = ex.Message;
@@ -785,7 +785,7 @@ namespace MediaBrowser.Providers.Manager
catch (Exception ex)
{
refreshResult.ErrorMessage = ex.Message;
- Logger.LogError(ex, "Error in {provider}", provider.Name);
+ Logger.LogError(ex, "Error in {Provider}", provider.Name);
}
}
@@ -837,7 +837,7 @@ namespace MediaBrowser.Providers.Manager
{
refreshResult.Failures++;
refreshResult.ErrorMessage = ex.Message;
- Logger.LogError(ex, "Error in {provider}", provider.Name);
+ Logger.LogError(ex, "Error in {Provider}", provider.Name);
}
}
diff --git a/MediaBrowser.Providers/MediaBrowser.Providers.csproj b/MediaBrowser.Providers/MediaBrowser.Providers.csproj
index 9d0a6944bb..b421121111 100644
--- a/MediaBrowser.Providers/MediaBrowser.Providers.csproj
+++ b/MediaBrowser.Providers/MediaBrowser.Providers.csproj
@@ -16,9 +16,9 @@
</ItemGroup>
<ItemGroup>
- <PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="6.0.0-rc.2*" />
- <PackageReference Include="Microsoft.Extensions.Caching.Abstractions" Version="6.0.0-rc.2*" />
- <PackageReference Include="Microsoft.Extensions.Http" Version="6.0.0-rc.2*" />
+ <PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="6.0.0" />
+ <PackageReference Include="Microsoft.Extensions.Caching.Abstractions" Version="6.0.0" />
+ <PackageReference Include="Microsoft.Extensions.Http" Version="6.0.0" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
<PackageReference Include="OptimizedPriorityQueue" Version="5.0.0" />
<PackageReference Include="PlaylistsNET" Version="1.1.3" />
diff --git a/MediaBrowser.Providers/MediaInfo/AudioImageProvider.cs b/MediaBrowser.Providers/MediaInfo/AudioImageProvider.cs
index 8c81b08db7..b4b1895f51 100644
--- a/MediaBrowser.Providers/MediaInfo/AudioImageProvider.cs
+++ b/MediaBrowser.Providers/MediaInfo/AudioImageProvider.cs
@@ -1,7 +1,5 @@
#nullable disable
-#pragma warning disable CA1002, CS1591
-
using System;
using System.Collections.Generic;
using System.Globalization;
@@ -13,7 +11,9 @@ using MediaBrowser.Common.Extensions;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.Audio;
+using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.MediaEncoding;
+using MediaBrowser.Controller.Persistence;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.IO;
@@ -21,38 +21,49 @@ using MediaBrowser.Model.IO;
namespace MediaBrowser.Providers.MediaInfo
{
/// <summary>
- /// Uses ffmpeg to create video images.
+ /// Uses <see cref="IMediaEncoder"/> to extract embedded images.
/// </summary>
public class AudioImageProvider : IDynamicImageProvider
{
+ private readonly IMediaSourceManager _mediaSourceManager;
private readonly IMediaEncoder _mediaEncoder;
private readonly IServerConfigurationManager _config;
private readonly IFileSystem _fileSystem;
- public AudioImageProvider(IMediaEncoder mediaEncoder, IServerConfigurationManager config, IFileSystem fileSystem)
+ /// <summary>
+ /// Initializes a new instance of the <see cref="AudioImageProvider"/> class.
+ /// </summary>
+ /// <param name="mediaSourceManager">The media source manager for fetching item streams.</param>
+ /// <param name="mediaEncoder">The media encoder for extracting embedded images.</param>
+ /// <param name="config">The server configuration manager for getting image paths.</param>
+ /// <param name="fileSystem">The filesystem.</param>
+ public AudioImageProvider(IMediaSourceManager mediaSourceManager, IMediaEncoder mediaEncoder, IServerConfigurationManager config, IFileSystem fileSystem)
{
+ _mediaSourceManager = mediaSourceManager;
_mediaEncoder = mediaEncoder;
_config = config;
_fileSystem = fileSystem;
}
- public string AudioImagesPath => Path.Combine(_config.ApplicationPaths.CachePath, "extracted-audio-images");
+ private string AudioImagesPath => Path.Combine(_config.ApplicationPaths.CachePath, "extracted-audio-images");
+ /// <inheritdoc />
public string Name => "Image Extractor";
+ /// <inheritdoc />
public IEnumerable<ImageType> GetSupportedImages(BaseItem item)
{
- return new List<ImageType> { ImageType.Primary };
+ return new[] { ImageType.Primary };
}
+ /// <inheritdoc />
public Task<DynamicImageResponse> GetImage(BaseItem item, ImageType type, CancellationToken cancellationToken)
{
- var audio = (Audio)item;
-
- var imageStreams =
- audio.GetMediaStreams(MediaStreamType.EmbeddedImage)
- .Where(i => i.Type == MediaStreamType.EmbeddedImage)
- .ToList();
+ var imageStreams = _mediaSourceManager.GetMediaStreams(new MediaStreamQuery
+ {
+ ItemId = item.Id,
+ Type = MediaStreamType.EmbeddedImage
+ });
// Can't extract if we didn't find a video stream in the file
if (imageStreams.Count == 0)
@@ -63,7 +74,7 @@ namespace MediaBrowser.Providers.MediaInfo
return GetImage((Audio)item, imageStreams, cancellationToken);
}
- public async Task<DynamicImageResponse> GetImage(Audio item, List<MediaStream> imageStreams, CancellationToken cancellationToken)
+ private async Task<DynamicImageResponse> GetImage(Audio item, List<MediaStream> imageStreams, CancellationToken cancellationToken)
{
var path = GetAudioImagePath(item);
@@ -75,7 +86,7 @@ namespace MediaBrowser.Providers.MediaInfo
imageStreams.FirstOrDefault(i => (i.Comment ?? string.Empty).IndexOf("cover", StringComparison.OrdinalIgnoreCase) != -1) ??
imageStreams.FirstOrDefault();
- var imageStreamIndex = imageStream == null ? (int?)null : imageStream.Index;
+ var imageStreamIndex = imageStream?.Index;
var tempFile = await _mediaEncoder.ExtractAudioImage(item.Path, imageStreamIndex, cancellationToken).ConfigureAwait(false);
@@ -127,6 +138,7 @@ namespace MediaBrowser.Providers.MediaInfo
return Path.Join(AudioImagesPath, prefix, filename);
}
+ /// <inheritdoc />
public bool Supports(BaseItem item)
{
if (item.IsShortcut)
diff --git a/MediaBrowser.Providers/MediaInfo/EmbeddedImageProvider.cs b/MediaBrowser.Providers/MediaInfo/EmbeddedImageProvider.cs
index c41d36d763..96d7d139a4 100644
--- a/MediaBrowser.Providers/MediaInfo/EmbeddedImageProvider.cs
+++ b/MediaBrowser.Providers/MediaInfo/EmbeddedImageProvider.cs
@@ -8,13 +8,16 @@ using System.Threading;
using System.Threading.Tasks;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.TV;
+using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.MediaEncoding;
+using MediaBrowser.Controller.Persistence;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Drawing;
using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.MediaInfo;
using MediaBrowser.Model.Net;
+using Microsoft.Extensions.Logging;
namespace MediaBrowser.Providers.MediaInfo
{
@@ -44,15 +47,21 @@ namespace MediaBrowser.Providers.MediaInfo
"logo",
};
+ private readonly IMediaSourceManager _mediaSourceManager;
private readonly IMediaEncoder _mediaEncoder;
+ private readonly ILogger<EmbeddedImageProvider> _logger;
/// <summary>
/// Initializes a new instance of the <see cref="EmbeddedImageProvider"/> class.
/// </summary>
+ /// <param name="mediaSourceManager">The media source manager for fetching item streams and attachments.</param>
/// <param name="mediaEncoder">The media encoder for extracting attached/embedded images.</param>
- public EmbeddedImageProvider(IMediaEncoder mediaEncoder)
+ /// <param name="logger">The logger.</param>
+ public EmbeddedImageProvider(IMediaSourceManager mediaSourceManager, IMediaEncoder mediaEncoder, ILogger<EmbeddedImageProvider> logger)
{
+ _mediaSourceManager = mediaSourceManager;
_mediaEncoder = mediaEncoder;
+ _logger = logger;
}
/// <inheritdoc />
@@ -114,22 +123,31 @@ namespace MediaBrowser.Providers.MediaInfo
ImageType.Primary => _primaryImageFileNames,
ImageType.Backdrop => _backdropImageFileNames,
ImageType.Logo => _logoImageFileNames,
- _ => _primaryImageFileNames
+ _ => Array.Empty<string>()
};
+ if (imageFileNames.Length == 0)
+ {
+ _logger.LogWarning("Attempted to load unexpected image type: {Type}", type);
+ return new DynamicImageResponse { HasImage = false };
+ }
+
// Try attachments first
- var attachmentStream = item.GetMediaSources(false)
- .SelectMany(source => source.MediaAttachments)
+ var attachmentStream = _mediaSourceManager.GetMediaAttachments(item.Id)
.FirstOrDefault(attachment => !string.IsNullOrEmpty(attachment.FileName)
&& imageFileNames.Any(name => attachment.FileName.Contains(name, StringComparison.OrdinalIgnoreCase)));
if (attachmentStream != null)
{
- return await ExtractAttachment(item, cancellationToken, attachmentStream, mediaSource);
+ return await ExtractAttachment(item, attachmentStream, mediaSource, cancellationToken);
}
// Fall back to EmbeddedImage streams
- var imageStreams = item.GetMediaStreams().FindAll(i => i.Type == MediaStreamType.EmbeddedImage);
+ var imageStreams = _mediaSourceManager.GetMediaStreams(new MediaStreamQuery
+ {
+ ItemId = item.Id,
+ Type = MediaStreamType.EmbeddedImage
+ });
if (imageStreams.Count == 0)
{
@@ -156,20 +174,28 @@ namespace MediaBrowser.Providers.MediaInfo
}
}
+ var format = imageStream.Codec switch
+ {
+ "mjpeg" => ImageFormat.Jpg,
+ "png" => ImageFormat.Png,
+ "gif" => ImageFormat.Gif,
+ _ => ImageFormat.Jpg
+ };
+
string extractedImagePath =
- await _mediaEncoder.ExtractVideoImage(item.Path, item.Container, mediaSource, imageStream, imageStream.Index, ".jpg", cancellationToken)
+ await _mediaEncoder.ExtractVideoImage(item.Path, item.Container, mediaSource, imageStream, imageStream.Index, format, cancellationToken)
.ConfigureAwait(false);
return new DynamicImageResponse
{
- Format = ImageFormat.Jpg,
+ Format = format,
HasImage = true,
Path = extractedImagePath,
Protocol = MediaProtocol.File
};
}
- private async Task<DynamicImageResponse> ExtractAttachment(Video item, CancellationToken cancellationToken, MediaAttachment attachmentStream, MediaSourceInfo mediaSource)
+ private async Task<DynamicImageResponse> ExtractAttachment(Video item, MediaAttachment attachmentStream, MediaSourceInfo mediaSource, CancellationToken cancellationToken)
{
var extension = string.IsNullOrEmpty(attachmentStream.MimeType)
? Path.GetExtension(attachmentStream.FileName)
@@ -180,10 +206,6 @@ namespace MediaBrowser.Providers.MediaInfo
extension = ".jpg";
}
- string extractedAttachmentPath =
- await _mediaEncoder.ExtractVideoImage(item.Path, item.Container, mediaSource, null, attachmentStream.Index, extension, cancellationToken)
- .ConfigureAwait(false);
-
ImageFormat format = extension switch
{
".bmp" => ImageFormat.Bmp,
@@ -194,6 +216,10 @@ namespace MediaBrowser.Providers.MediaInfo
_ => ImageFormat.Jpg
};
+ string extractedAttachmentPath =
+ await _mediaEncoder.ExtractVideoImage(item.Path, item.Container, mediaSource, null, attachmentStream.Index, format, cancellationToken)
+ .ConfigureAwait(false);
+
return new DynamicImageResponse
{
Format = format,
diff --git a/MediaBrowser.Providers/MediaInfo/SubtitleResolver.cs b/MediaBrowser.Providers/MediaInfo/SubtitleResolver.cs
index dd4a5f061c..ba284187ed 100644
--- a/MediaBrowser.Providers/MediaInfo/SubtitleResolver.cs
+++ b/MediaBrowser.Providers/MediaInfo/SubtitleResolver.cs
@@ -1,7 +1,3 @@
-#nullable disable
-
-#pragma warning disable CA1002, CS1591
-
using System;
using System.Collections.Generic;
using System.IO;
@@ -12,15 +8,30 @@ using MediaBrowser.Model.Globalization;
namespace MediaBrowser.Providers.MediaInfo
{
+ /// <summary>
+ /// Resolves external subtitles for videos.
+ /// </summary>
public class SubtitleResolver
{
private readonly ILocalizationManager _localization;
+ /// <summary>
+ /// Initializes a new instance of the <see cref="SubtitleResolver"/> class.
+ /// </summary>
+ /// <param name="localization">The localization manager.</param>
public SubtitleResolver(ILocalizationManager localization)
{
_localization = localization;
}
+ /// <summary>
+ /// Retrieves the external subtitle streams for the provided video.
+ /// </summary>
+ /// <param name="video">The video to search from.</param>
+ /// <param name="startIndex">The stream index to start adding subtitle streams at.</param>
+ /// <param name="directoryService">The directory service to search for files.</param>
+ /// <param name="clearCache">True if the directory service cache should be cleared before searching.</param>
+ /// <returns>The external subtitle streams located.</returns>
public List<MediaStream> GetExternalSubtitleStreams(
Video video,
int startIndex,
@@ -56,6 +67,13 @@ namespace MediaBrowser.Providers.MediaInfo
return streams;
}
+ /// <summary>
+ /// Locates the external subtitle files for the provided video.
+ /// </summary>
+ /// <param name="video">The video to search from.</param>
+ /// <param name="directoryService">The directory service to search for files.</param>
+ /// <param name="clearCache">True if the directory service cache should be cleared before searching.</param>
+ /// <returns>The external subtitle file paths located.</returns>
public IEnumerable<string> GetExternalSubtitleFiles(
Video video,
IDirectoryService directoryService,
@@ -74,6 +92,13 @@ namespace MediaBrowser.Providers.MediaInfo
}
}
+ /// <summary>
+ /// Extracts the subtitle files from the provided list and adds them to the list of streams.
+ /// </summary>
+ /// <param name="streams">The list of streams to add external subtitles to.</param>
+ /// <param name="videoPath">The path to the video file.</param>
+ /// <param name="startIndex">The stream index to start adding subtitle streams at.</param>
+ /// <param name="files">The files to add if they are subtitles.</param>
public void AddExternalSubtitleStreams(
List<MediaStream> streams,
string videoPath,
@@ -120,6 +145,12 @@ namespace MediaBrowser.Providers.MediaInfo
while (languageSpan.Length > 0)
{
var lastDot = languageSpan.LastIndexOf('.');
+ if (lastDot < videoFileNameWithoutExtension.Length)
+ {
+ languageSpan = ReadOnlySpan<char>.Empty;
+ break;
+ }
+
var currentSlice = languageSpan[lastDot..];
if (currentSlice.Equals(".default", StringComparison.OrdinalIgnoreCase)
|| currentSlice.Equals(".forced", StringComparison.OrdinalIgnoreCase)
@@ -133,12 +164,19 @@ namespace MediaBrowser.Providers.MediaInfo
break;
}
- // Try to translate to three character code
- // Be flexible and check against both the full and three character versions
var language = languageSpan.ToString();
- var culture = _localization.FindLanguageInfo(language);
+ if (string.IsNullOrWhiteSpace(language))
+ {
+ language = null;
+ }
+ else
+ {
+ // Try to translate to three character code
+ // Be flexible and check against both the full and three character versions
+ var culture = _localization.FindLanguageInfo(language);
- language = culture == null ? language : culture.ThreeLetterISOLanguageName;
+ language = culture == null ? language : culture.ThreeLetterISOLanguageName;
+ }
mediaStream = new MediaStream
{
diff --git a/MediaBrowser.Providers/MediaInfo/VideoImageProvider.cs b/MediaBrowser.Providers/MediaInfo/VideoImageProvider.cs
index d226182c09..d4bf629702 100644
--- a/MediaBrowser.Providers/MediaInfo/VideoImageProvider.cs
+++ b/MediaBrowser.Providers/MediaInfo/VideoImageProvider.cs
@@ -4,7 +4,9 @@ using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using MediaBrowser.Controller.Entities;
+using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.MediaEncoding;
+using MediaBrowser.Controller.Persistence;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Drawing;
using MediaBrowser.Model.Dto;
@@ -19,16 +21,19 @@ namespace MediaBrowser.Providers.MediaInfo
/// </summary>
public class VideoImageProvider : IDynamicImageProvider, IHasOrder
{
+ private readonly IMediaSourceManager _mediaSourceManager;
private readonly IMediaEncoder _mediaEncoder;
private readonly ILogger<VideoImageProvider> _logger;
/// <summary>
/// Initializes a new instance of the <see cref="VideoImageProvider"/> class.
/// </summary>
+ /// <param name="mediaSourceManager">The media source manager for fetching item streams.</param>
/// <param name="mediaEncoder">The media encoder for capturing images.</param>
/// <param name="logger">The logger.</param>
- public VideoImageProvider(IMediaEncoder mediaEncoder, ILogger<VideoImageProvider> logger)
+ public VideoImageProvider(IMediaSourceManager mediaSourceManager, IMediaEncoder mediaEncoder, ILogger<VideoImageProvider> logger)
{
+ _mediaSourceManager = mediaSourceManager;
_mediaEncoder = mediaEncoder;
_logger = logger;
}
@@ -78,12 +83,18 @@ namespace MediaBrowser.Providers.MediaInfo
// If we know the duration, grab it from 10% into the video. Otherwise just 10 seconds in.
// Always use 10 seconds for dvd because our duration could be out of whack
- var imageOffset = item.VideoType != VideoType.Dvd && item.RunTimeTicks.HasValue &&
- item.RunTimeTicks.Value > 0
+ var imageOffset = item.VideoType != VideoType.Dvd && item.RunTimeTicks > 0
? TimeSpan.FromTicks(item.RunTimeTicks.Value / 10)
: TimeSpan.FromSeconds(10);
- var videoStream = item.GetDefaultVideoStream() ?? item.GetMediaStreams().FirstOrDefault(i => i.Type == MediaStreamType.Video);
+ var query = new MediaStreamQuery { ItemId = item.Id, Index = item.DefaultVideoStreamIndex };
+ var videoStream = _mediaSourceManager.GetMediaStreams(query).FirstOrDefault();
+ if (videoStream == null)
+ {
+ query.Type = MediaStreamType.Video;
+ query.Index = null;
+ videoStream = _mediaSourceManager.GetMediaStreams(query).FirstOrDefault();
+ }
if (videoStream == null)
{
diff --git a/MediaBrowser.Providers/Plugins/MusicBrainz/MusicBrainzAlbumProvider.cs b/MediaBrowser.Providers/Plugins/MusicBrainz/MusicBrainzAlbumProvider.cs
index 5559b9db6a..8a32cb07ce 100644
--- a/MediaBrowser.Providers/Plugins/MusicBrainz/MusicBrainzAlbumProvider.cs
+++ b/MediaBrowser.Providers/Plugins/MusicBrainz/MusicBrainzAlbumProvider.cs
@@ -319,6 +319,12 @@ namespace MediaBrowser.Providers.Music
{
case "name-credit":
{
+ if (reader.IsEmptyElement)
+ {
+ reader.Read();
+ break;
+ }
+
using var subReader = reader.ReadSubtree();
return ParseArtistNameCredit(subReader);
}
@@ -355,6 +361,12 @@ namespace MediaBrowser.Providers.Music
{
case "artist":
{
+ if (reader.IsEmptyElement)
+ {
+ reader.Read();
+ break;
+ }
+
var id = reader.GetAttribute("id");
using var subReader = reader.ReadSubtree();
return ParseArtistArtistCredit(subReader, id);
@@ -457,8 +469,8 @@ namespace MediaBrowser.Providers.Music
};
using var reader = XmlReader.Create(oReader, settings);
- reader.MoveToContent();
- reader.Read();
+ await reader.MoveToContentAsync().ConfigureAwait(false);
+ await reader.ReadAsync().ConfigureAwait(false);
// Loop through each element
while (!reader.EOF && reader.ReadState == ReadState.Interactive)
@@ -471,7 +483,7 @@ namespace MediaBrowser.Providers.Music
{
if (reader.IsEmptyElement)
{
- reader.Read();
+ await reader.ReadAsync().ConfigureAwait(false);
continue;
}
@@ -481,14 +493,14 @@ namespace MediaBrowser.Providers.Music
default:
{
- reader.Skip();
+ await reader.SkipAsync().ConfigureAwait(false);
break;
}
}
}
else
{
- reader.Read();
+ await reader.ReadAsync().ConfigureAwait(false);
}
}
@@ -755,6 +767,12 @@ namespace MediaBrowser.Providers.Music
case "artist-credit":
{
+ if (reader.IsEmptyElement)
+ {
+ reader.Read();
+ break;
+ }
+
using var subReader = reader.ReadSubtree();
var artist = ParseArtistCredit(subReader);
diff --git a/MediaBrowser.Providers/Plugins/Omdb/OmdbEpisodeProvider.cs b/MediaBrowser.Providers/Plugins/Omdb/OmdbEpisodeProvider.cs
index f67ac6ede4..d8b33a799f 100644
--- a/MediaBrowser.Providers/Plugins/Omdb/OmdbEpisodeProvider.cs
+++ b/MediaBrowser.Providers/Plugins/Omdb/OmdbEpisodeProvider.cs
@@ -4,7 +4,6 @@ using System.Collections.Generic;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
-using MediaBrowser.Common;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Entities.TV;
using MediaBrowser.Controller.Library;
@@ -17,24 +16,17 @@ namespace MediaBrowser.Providers.Plugins.Omdb
{
public class OmdbEpisodeProvider : IRemoteMetadataProvider<Episode, EpisodeInfo>, IHasOrder
{
- private readonly IHttpClientFactory _httpClientFactory;
private readonly OmdbItemProvider _itemProvider;
- private readonly IFileSystem _fileSystem;
- private readonly IServerConfigurationManager _configurationManager;
- private readonly IApplicationHost _appHost;
+ private readonly OmdbProvider _omdbProvider;
public OmdbEpisodeProvider(
- IApplicationHost appHost,
IHttpClientFactory httpClientFactory,
ILibraryManager libraryManager,
IFileSystem fileSystem,
IServerConfigurationManager configurationManager)
{
- _httpClientFactory = httpClientFactory;
- _fileSystem = fileSystem;
- _configurationManager = configurationManager;
- _appHost = appHost;
- _itemProvider = new OmdbItemProvider(_appHost, httpClientFactory, libraryManager, fileSystem, configurationManager);
+ _itemProvider = new OmdbItemProvider(httpClientFactory, libraryManager, fileSystem, configurationManager);
+ _omdbProvider = new OmdbProvider(httpClientFactory, fileSystem, configurationManager);
}
// After TheTvDb
@@ -44,12 +36,12 @@ namespace MediaBrowser.Providers.Plugins.Omdb
public Task<IEnumerable<RemoteSearchResult>> GetSearchResults(EpisodeInfo searchInfo, CancellationToken cancellationToken)
{
- return _itemProvider.GetSearchResults(searchInfo, "episode", cancellationToken);
+ return _itemProvider.GetSearchResults(searchInfo, cancellationToken);
}
public async Task<MetadataResult<Episode>> GetMetadata(EpisodeInfo info, CancellationToken cancellationToken)
{
- var result = new MetadataResult<Episode>()
+ var result = new MetadataResult<Episode>
{
Item = new Episode(),
QueriedById = true
@@ -61,13 +53,20 @@ namespace MediaBrowser.Providers.Plugins.Omdb
return result;
}
- if (info.SeriesProviderIds.TryGetValue(MetadataProvider.Imdb.ToString(), out string? seriesImdbId) && !string.IsNullOrEmpty(seriesImdbId))
+ if (info.SeriesProviderIds.TryGetValue(MetadataProvider.Imdb.ToString(), out string? seriesImdbId)
+ && !string.IsNullOrEmpty(seriesImdbId)
+ && info.IndexNumber.HasValue
+ && info.ParentIndexNumber.HasValue)
{
- if (info.IndexNumber.HasValue && info.ParentIndexNumber.HasValue)
- {
- result.HasMetadata = await new OmdbProvider(_httpClientFactory, _fileSystem, _appHost, _configurationManager)
- .FetchEpisodeData(result, info.IndexNumber.Value, info.ParentIndexNumber.Value, info.GetProviderId(MetadataProvider.Imdb), seriesImdbId, info.MetadataLanguage, info.MetadataCountryCode, cancellationToken).ConfigureAwait(false);
- }
+ result.HasMetadata = await _omdbProvider.FetchEpisodeData(
+ result,
+ info.IndexNumber.Value,
+ info.ParentIndexNumber.Value,
+ info.GetProviderId(MetadataProvider.Imdb),
+ seriesImdbId,
+ info.MetadataLanguage,
+ info.MetadataCountryCode,
+ cancellationToken).ConfigureAwait(false);
}
return result;
diff --git a/MediaBrowser.Providers/Plugins/Omdb/OmdbImageProvider.cs b/MediaBrowser.Providers/Plugins/Omdb/OmdbImageProvider.cs
index fa82089c86..4c3fc23b2b 100644
--- a/MediaBrowser.Providers/Plugins/Omdb/OmdbImageProvider.cs
+++ b/MediaBrowser.Providers/Plugins/Omdb/OmdbImageProvider.cs
@@ -2,12 +2,12 @@
#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;
using MediaBrowser.Common.Net;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Entities;
@@ -23,16 +23,12 @@ namespace MediaBrowser.Providers.Plugins.Omdb
public class OmdbImageProvider : IRemoteImageProvider, IHasOrder
{
private readonly IHttpClientFactory _httpClientFactory;
- private readonly IFileSystem _fileSystem;
- private readonly IServerConfigurationManager _configurationManager;
- private readonly IApplicationHost _appHost;
+ private readonly OmdbProvider _omdbProvider;
- public OmdbImageProvider(IApplicationHost appHost, IHttpClientFactory httpClientFactory, IFileSystem fileSystem, IServerConfigurationManager configurationManager)
+ public OmdbImageProvider(IHttpClientFactory httpClientFactory, IFileSystem fileSystem, IServerConfigurationManager configurationManager)
{
_httpClientFactory = httpClientFactory;
- _fileSystem = fileSystem;
- _configurationManager = configurationManager;
- _appHost = appHost;
+ _omdbProvider = new OmdbProvider(_httpClientFactory, fileSystem, configurationManager);
}
public string Name => "The Open Movie Database";
@@ -52,38 +48,27 @@ namespace MediaBrowser.Providers.Plugins.Omdb
public async Task<IEnumerable<RemoteImageInfo>> GetImages(BaseItem item, CancellationToken cancellationToken)
{
var imdbId = item.GetProviderId(MetadataProvider.Imdb);
+ if (string.IsNullOrWhiteSpace(imdbId))
+ {
+ return Enumerable.Empty<RemoteImageInfo>();
+ }
- var list = new List<RemoteImageInfo>();
-
- var provider = new OmdbProvider(_httpClientFactory, _fileSystem, _appHost, _configurationManager);
+ var rootObject = await _omdbProvider.GetRootObject(imdbId, cancellationToken).ConfigureAwait(false);
- if (!string.IsNullOrWhiteSpace(imdbId))
+ if (string.IsNullOrEmpty(rootObject.Poster))
{
- var rootObject = await provider.GetRootObject(imdbId, cancellationToken).ConfigureAwait(false);
+ return Enumerable.Empty<RemoteImageInfo>();
+ }
- if (!string.IsNullOrEmpty(rootObject.Poster))
+ // the poster url is sometimes higher quality than the poster api
+ return new[]
+ {
+ new RemoteImageInfo
{
- if (item is Episode)
- {
- // img.omdbapi.com is returning 404's
- list.Add(new RemoteImageInfo
- {
- ProviderName = Name,
- Url = rootObject.Poster
- });
- }
- else
- {
- list.Add(new RemoteImageInfo
- {
- ProviderName = Name,
- Url = string.Format(CultureInfo.InvariantCulture, "https://img.omdbapi.com/?i={0}&apikey=2c9d9507", imdbId)
- });
- }
+ ProviderName = Name,
+ Url = rootObject.Poster
}
- }
-
- return list;
+ };
}
public Task<HttpResponseMessage> GetImageResponse(string url, CancellationToken cancellationToken)
diff --git a/MediaBrowser.Providers/Plugins/Omdb/OmdbItemProvider.cs b/MediaBrowser.Providers/Plugins/Omdb/OmdbItemProvider.cs
index 2409993a29..e5753b2b59 100644
--- a/MediaBrowser.Providers/Plugins/Omdb/OmdbItemProvider.cs
+++ b/MediaBrowser.Providers/Plugins/Omdb/OmdbItemProvider.cs
@@ -8,11 +8,11 @@ using System.Globalization;
using System.Linq;
using System.Net;
using System.Net.Http;
+using System.Text;
using System.Text.Json;
using System.Threading;
using System.Threading.Tasks;
using Jellyfin.Extensions.Json;
-using MediaBrowser.Common;
using MediaBrowser.Common.Net;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Entities;
@@ -31,13 +31,10 @@ namespace MediaBrowser.Providers.Plugins.Omdb
{
private readonly IHttpClientFactory _httpClientFactory;
private readonly ILibraryManager _libraryManager;
- private readonly IFileSystem _fileSystem;
- private readonly IServerConfigurationManager _configurationManager;
- private readonly IApplicationHost _appHost;
private readonly JsonSerializerOptions _jsonOptions;
+ private readonly OmdbProvider _omdbProvider;
public OmdbItemProvider(
- IApplicationHost appHost,
IHttpClientFactory httpClientFactory,
ILibraryManager libraryManager,
IFileSystem fileSystem,
@@ -45,9 +42,7 @@ namespace MediaBrowser.Providers.Plugins.Omdb
{
_httpClientFactory = httpClientFactory;
_libraryManager = libraryManager;
- _fileSystem = fileSystem;
- _configurationManager = configurationManager;
- _appHost = appHost;
+ _omdbProvider = new OmdbProvider(_httpClientFactory, fileSystem, configurationManager);
_jsonOptions = new JsonSerializerOptions(JsonDefaults.Options);
_jsonOptions.Converters.Add(new JsonOmdbNotAvailableStringConverter());
@@ -59,185 +54,166 @@ namespace MediaBrowser.Providers.Plugins.Omdb
// After primary option
public int Order => 2;
+ public Task<IEnumerable<RemoteSearchResult>> GetSearchResults(TrailerInfo searchInfo, CancellationToken cancellationToken)
+ {
+ return GetSearchResultsInternal(searchInfo, true, cancellationToken);
+ }
+
public Task<IEnumerable<RemoteSearchResult>> GetSearchResults(SeriesInfo searchInfo, CancellationToken cancellationToken)
{
- return GetSearchResults(searchInfo, "series", cancellationToken);
+ return GetSearchResultsInternal(searchInfo, true, cancellationToken);
}
public Task<IEnumerable<RemoteSearchResult>> GetSearchResults(MovieInfo searchInfo, CancellationToken cancellationToken)
{
- return GetSearchResults(searchInfo, "movie", cancellationToken);
+ return GetSearchResultsInternal(searchInfo, true, cancellationToken);
}
- public Task<IEnumerable<RemoteSearchResult>> GetSearchResults(ItemLookupInfo searchInfo, string type, CancellationToken cancellationToken)
+ public Task<IEnumerable<RemoteSearchResult>> GetSearchResults(EpisodeInfo searchInfo, CancellationToken cancellationToken)
{
- return GetSearchResultsInternal(searchInfo, type, true, cancellationToken);
+ return GetSearchResultsInternal(searchInfo, true, cancellationToken);
}
- private async Task<IEnumerable<RemoteSearchResult>> GetSearchResultsInternal(ItemLookupInfo searchInfo, string type, bool isSearch, CancellationToken cancellationToken)
+ private async Task<IEnumerable<RemoteSearchResult>> GetSearchResultsInternal(ItemLookupInfo searchInfo, bool isSearch, CancellationToken cancellationToken)
{
+ var type = searchInfo switch
+ {
+ EpisodeInfo => "episode",
+ SeriesInfo => "series",
+ _ => "movie"
+ };
+
+ // This is a bit hacky?
var episodeSearchInfo = searchInfo as EpisodeInfo;
+ var indexNumberEnd = episodeSearchInfo?.IndexNumberEnd;
var imdbId = searchInfo.GetProviderId(MetadataProvider.Imdb);
- var urlQuery = "plot=full&r=json";
- if (type == "episode" && episodeSearchInfo != null)
+ var urlQuery = new StringBuilder("plot=full&r=json");
+ if (episodeSearchInfo != null)
{
episodeSearchInfo.SeriesProviderIds.TryGetValue(MetadataProvider.Imdb.ToString(), out imdbId);
- }
-
- var name = searchInfo.Name;
- var year = searchInfo.Year;
+ if (searchInfo.IndexNumber.HasValue)
+ {
+ urlQuery.Append("&Episode=").Append(searchInfo.IndexNumber.Value);
+ }
- if (!string.IsNullOrWhiteSpace(name))
- {
- var parsedName = _libraryManager.ParseName(name);
- var yearInName = parsedName.Year;
- name = parsedName.Name;
- year ??= yearInName;
+ if (searchInfo.ParentIndexNumber.HasValue)
+ {
+ urlQuery.Append("&Season=").Append(searchInfo.ParentIndexNumber.Value);
+ }
}
if (string.IsNullOrWhiteSpace(imdbId))
{
- if (year.HasValue)
+ var name = searchInfo.Name;
+ var year = searchInfo.Year;
+ if (!string.IsNullOrWhiteSpace(name))
{
- urlQuery += "&y=" + year.Value.ToString(CultureInfo.InvariantCulture);
+ var parsedName = _libraryManager.ParseName(name);
+ var yearInName = parsedName.Year;
+ name = parsedName.Name;
+ year ??= yearInName;
}
- // &s means search and returns a list of results as opposed to t
- if (isSearch)
- {
- urlQuery += "&s=" + WebUtility.UrlEncode(name);
- }
- else
+ if (year.HasValue)
{
- urlQuery += "&t=" + WebUtility.UrlEncode(name);
+ urlQuery.Append("&y=").Append(year);
}
- urlQuery += "&type=" + type;
+ // &s means search and returns a list of results as opposed to t
+ urlQuery.Append(isSearch ? "&s=" : "&t=");
+ urlQuery.Append(WebUtility.UrlEncode(name));
+ urlQuery.Append("&type=")
+ .Append(type);
}
else
{
- urlQuery += "&i=" + imdbId;
+ urlQuery.Append("&i=")
+ .Append(imdbId);
isSearch = false;
}
- if (type == "episode")
- {
- if (searchInfo.IndexNumber.HasValue)
- {
- 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);
+ var url = OmdbProvider.GetOmdbUrl(urlQuery.ToString());
- using var response = await OmdbProvider.GetOmdbResponse(_httpClientFactory.CreateClient(NamedClient.Default), url, cancellationToken).ConfigureAwait(false);
+ using var response = await _httpClientFactory.CreateClient(NamedClient.Default).GetAsync(url, cancellationToken).ConfigureAwait(false);
await using var stream = await response.Content.ReadAsStreamAsync(cancellationToken).ConfigureAwait(false);
- var resultList = new List<SearchResult>();
if (isSearch)
{
var searchResultList = await JsonSerializer.DeserializeAsync<SearchResultList>(stream, _jsonOptions, cancellationToken).ConfigureAwait(false);
- if (searchResultList != null && searchResultList.Search != null)
+ if (searchResultList?.Search != null)
{
- resultList.AddRange(searchResultList.Search);
+ var resultCount = searchResultList.Search.Count;
+ var result = new RemoteSearchResult[resultCount];
+ for (var i = 0; i < resultCount; i++)
+ {
+ result[i] = ResultToMetadataResult(searchResultList.Search[i], searchInfo, indexNumberEnd);
+ }
+
+ return result;
}
}
else
{
var result = await JsonSerializer.DeserializeAsync<SearchResult>(stream, _jsonOptions, cancellationToken).ConfigureAwait(false);
- if (string.Equals(result.Response, "true", StringComparison.OrdinalIgnoreCase))
+ if (string.Equals(result?.Response, "true", StringComparison.OrdinalIgnoreCase))
{
- resultList.Add(result);
+ return new[] { ResultToMetadataResult(result, searchInfo, indexNumberEnd) };
}
}
- 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;
- });
+ return Enumerable.Empty<RemoteSearchResult>();
}
public Task<MetadataResult<Trailer>> GetMetadata(TrailerInfo info, CancellationToken cancellationToken)
{
- return GetMovieResult<Trailer>(info, cancellationToken);
+ return GetResult<Trailer>(info, cancellationToken);
}
- public Task<IEnumerable<RemoteSearchResult>> GetSearchResults(TrailerInfo searchInfo, CancellationToken cancellationToken)
+ public Task<MetadataResult<Series>> GetMetadata(SeriesInfo info, CancellationToken cancellationToken)
{
- return GetSearchResults(searchInfo, "movie", cancellationToken);
+ return GetResult<Series>(info, cancellationToken);
}
- public async Task<MetadataResult<Series>> GetMetadata(SeriesInfo info, CancellationToken cancellationToken)
+ public Task<MetadataResult<Movie>> GetMetadata(MovieInfo info, CancellationToken cancellationToken)
{
- var result = new MetadataResult<Series>
+ return GetResult<Movie>(info, cancellationToken);
+ }
+
+ private RemoteSearchResult ResultToMetadataResult(SearchResult result, ItemLookupInfo searchInfo, int? indexNumberEnd)
+ {
+ var item = new RemoteSearchResult
{
- Item = new Series(),
- QueriedById = true
+ IndexNumber = searchInfo.IndexNumber,
+ Name = result.Title,
+ ParentIndexNumber = searchInfo.ParentIndexNumber,
+ SearchProviderName = Name,
+ IndexNumberEnd = indexNumberEnd
};
- var imdbId = info.GetProviderId(MetadataProvider.Imdb);
- if (string.IsNullOrWhiteSpace(imdbId))
+ item.SetProviderId(MetadataProvider.Imdb, result.imdbID);
+
+ if (OmdbProvider.TryParseYear(result.Year, out var parsedYear))
{
- imdbId = await GetSeriesImdbId(info, cancellationToken).ConfigureAwait(false);
- result.QueriedById = false;
+ item.ProductionYear = parsedYear;
}
- if (!string.IsNullOrEmpty(imdbId))
+ if (!string.IsNullOrEmpty(result.Released)
+ && DateTime.TryParse(result.Released, CultureInfo.InvariantCulture, DateTimeStyles.AllowWhiteSpaces, out var released))
{
- result.Item.SetProviderId(MetadataProvider.Imdb, imdbId);
- result.HasMetadata = true;
-
- await new OmdbProvider(_httpClientFactory, _fileSystem, _appHost, _configurationManager).Fetch(result, imdbId, info.MetadataLanguage, info.MetadataCountryCode, cancellationToken).ConfigureAwait(false);
+ item.PremiereDate = released;
}
- return result;
- }
+ if (!string.IsNullOrWhiteSpace(result.Poster))
+ {
+ item.ImageUrl = result.Poster;
+ }
- public Task<MetadataResult<Movie>> GetMetadata(MovieInfo info, CancellationToken cancellationToken)
- {
- return GetMovieResult<Movie>(info, cancellationToken);
+ return item;
}
- private async Task<MetadataResult<T>> GetMovieResult<T>(ItemLookupInfo info, CancellationToken cancellationToken)
+ private async Task<MetadataResult<T>> GetResult<T>(ItemLookupInfo info, CancellationToken cancellationToken)
where T : BaseItem, new()
{
var result = new MetadataResult<T>
@@ -249,7 +225,7 @@ namespace MediaBrowser.Providers.Plugins.Omdb
var imdbId = info.GetProviderId(MetadataProvider.Imdb);
if (string.IsNullOrWhiteSpace(imdbId))
{
- imdbId = await GetMovieImdbId(info, cancellationToken).ConfigureAwait(false);
+ imdbId = await GetImdbId(info, cancellationToken).ConfigureAwait(false);
result.QueriedById = false;
}
@@ -258,22 +234,15 @@ namespace MediaBrowser.Providers.Plugins.Omdb
result.Item.SetProviderId(MetadataProvider.Imdb, imdbId);
result.HasMetadata = true;
- await new OmdbProvider(_httpClientFactory, _fileSystem, _appHost, _configurationManager).Fetch(result, imdbId, info.MetadataLanguage, info.MetadataCountryCode, cancellationToken).ConfigureAwait(false);
+ await _omdbProvider.Fetch(result, imdbId, info.MetadataLanguage, info.MetadataCountryCode, cancellationToken).ConfigureAwait(false);
}
return result;
}
- private async Task<string> GetMovieImdbId(ItemLookupInfo info, CancellationToken cancellationToken)
- {
- var results = await GetSearchResultsInternal(info, "movie", false, cancellationToken).ConfigureAwait(false);
- var first = results.FirstOrDefault();
- return first?.GetProviderId(MetadataProvider.Imdb);
- }
-
- private async Task<string> GetSeriesImdbId(SeriesInfo info, CancellationToken cancellationToken)
+ private async Task<string> GetImdbId(ItemLookupInfo info, CancellationToken cancellationToken)
{
- var results = await GetSearchResultsInternal(info, "series", false, cancellationToken).ConfigureAwait(false);
+ var results = await GetSearchResultsInternal(info, false, cancellationToken).ConfigureAwait(false);
var first = results.FirstOrDefault();
return first?.GetProviderId(MetadataProvider.Imdb);
}
diff --git a/MediaBrowser.Providers/Plugins/Omdb/OmdbProvider.cs b/MediaBrowser.Providers/Plugins/Omdb/OmdbProvider.cs
index 816a882b46..12ea2d55b9 100644
--- a/MediaBrowser.Providers/Plugins/Omdb/OmdbProvider.cs
+++ b/MediaBrowser.Providers/Plugins/Omdb/OmdbProvider.cs
@@ -4,15 +4,16 @@
using System;
using System.Collections.Generic;
+using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Net.Http;
+using System.Net.Http.Json;
using System.Text.Json;
using System.Threading;
using System.Threading.Tasks;
using Jellyfin.Extensions.Json;
-using MediaBrowser.Common;
using MediaBrowser.Common.Net;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Entities;
@@ -28,24 +29,22 @@ namespace MediaBrowser.Providers.Plugins.Omdb
private readonly IFileSystem _fileSystem;
private readonly IServerConfigurationManager _configurationManager;
private readonly IHttpClientFactory _httpClientFactory;
- private readonly IApplicationHost _appHost;
private readonly JsonSerializerOptions _jsonOptions;
/// <summary>Initializes a new instance of the <see cref="OmdbProvider"/> class.</summary>
/// <param name="httpClientFactory">HttpClientFactory to use for calls to OMDB service.</param>
/// <param name="fileSystem">IFileSystem to use for store OMDB data.</param>
- /// <param name="appHost">IApplicationHost to use.</param>
/// <param name="configurationManager">IServerConfigurationManager to use.</param>
- public OmdbProvider(IHttpClientFactory httpClientFactory, IFileSystem fileSystem, IApplicationHost appHost, IServerConfigurationManager configurationManager)
+ public OmdbProvider(IHttpClientFactory httpClientFactory, IFileSystem fileSystem, IServerConfigurationManager configurationManager)
{
_httpClientFactory = httpClientFactory;
_fileSystem = fileSystem;
_configurationManager = configurationManager;
- _appHost = appHost;
_jsonOptions = new JsonSerializerOptions(JsonDefaults.Options);
- _jsonOptions.Converters.Add(new JsonOmdbNotAvailableStringConverter());
- _jsonOptions.Converters.Add(new JsonOmdbNotAvailableInt32Converter());
+ // These converters need to take priority
+ _jsonOptions.Converters.Insert(0, new JsonOmdbNotAvailableStringConverter());
+ _jsonOptions.Converters.Insert(0, new JsonOmdbNotAvailableInt32Converter());
}
/// <summary>Fetches data from OMDB service.</summary>
@@ -68,8 +67,9 @@ namespace MediaBrowser.Providers.Plugins.Omdb
var result = await GetRootObject(imdbId, cancellationToken).ConfigureAwait(false);
+ var isEnglishRequested = IsConfiguredForEnglish(item, language);
// Only take the name and rating if the user's language is set to English, since Omdb has no localization
- if (string.Equals(language, "en", StringComparison.OrdinalIgnoreCase) || _configurationManager.Configuration.EnableNewOmdbSupport)
+ if (isEnglishRequested)
{
item.Name = result.Title;
@@ -79,9 +79,7 @@ namespace MediaBrowser.Providers.Plugins.Omdb
}
}
- if (!string.IsNullOrEmpty(result.Year) && result.Year.Length >= 4
- && int.TryParse(result.Year.AsSpan().Slice(0, 4), NumberStyles.Number, CultureInfo.InvariantCulture, out var year)
- && year >= 0)
+ if (TryParseYear(result.Year, out var year))
{
item.ProductionYear = year;
}
@@ -117,7 +115,7 @@ namespace MediaBrowser.Providers.Plugins.Omdb
item.SetProviderId(MetadataProvider.Imdb, result.imdbID);
}
- ParseAdditionalMetadata(itemResult, result);
+ ParseAdditionalMetadata(itemResult, result, isEnglishRequested);
}
/// <summary>Gets data about an episode.</summary>
@@ -180,8 +178,9 @@ namespace MediaBrowser.Providers.Plugins.Omdb
return false;
}
+ var isEnglishRequested = IsConfiguredForEnglish(item, language);
// Only take the name and rating if the user's language is set to English, since Omdb has no localization
- if (string.Equals(language, "en", StringComparison.OrdinalIgnoreCase) || _configurationManager.Configuration.EnableNewOmdbSupport)
+ if (isEnglishRequested)
{
item.Name = result.Title;
@@ -191,9 +190,7 @@ namespace MediaBrowser.Providers.Plugins.Omdb
}
}
- if (!string.IsNullOrEmpty(result.Year) && result.Year.Length >= 4
- && int.TryParse(result.Year.AsSpan().Slice(0, 4), NumberStyles.Number, CultureInfo.InvariantCulture, out var year)
- && year >= 0)
+ if (TryParseYear(result.Year, out var year))
{
item.ProductionYear = year;
}
@@ -229,7 +226,7 @@ namespace MediaBrowser.Providers.Plugins.Omdb
item.SetProviderId(MetadataProvider.Imdb, result.imdbID);
}
- ParseAdditionalMetadata(itemResult, result);
+ ParseAdditionalMetadata(itemResult, result, isEnglishRequested);
return true;
}
@@ -263,6 +260,30 @@ namespace MediaBrowser.Providers.Plugins.Omdb
return Url + "&" + query;
}
+ /// <summary>
+ /// Extract the year from a string.
+ /// </summary>
+ /// <param name="input">The input string.</param>
+ /// <param name="year">The year.</param>
+ /// <returns>A value indicating whether the input could successfully be parsed as a year.</returns>
+ public static bool TryParseYear(string input, [NotNullWhen(true)] out int? year)
+ {
+ if (string.IsNullOrEmpty(input))
+ {
+ year = 0;
+ return false;
+ }
+
+ if (int.TryParse(input.AsSpan(0, 4), NumberStyles.Number, CultureInfo.InvariantCulture, out var result))
+ {
+ year = result;
+ return true;
+ }
+
+ year = 0;
+ return false;
+ }
+
private async Task<string> EnsureItemInfo(string imdbId, CancellationToken cancellationToken)
{
if (string.IsNullOrWhiteSpace(imdbId))
@@ -295,7 +316,7 @@ namespace MediaBrowser.Providers.Plugins.Omdb
"i={0}&plot=short&tomatoes=true&r=json",
imdbParam));
- var rootObject = await GetDeserializedOmdbResponse<RootObject>(_httpClientFactory.CreateClient(NamedClient.Default), url, cancellationToken).ConfigureAwait(false);
+ var rootObject = await _httpClientFactory.CreateClient(NamedClient.Default).GetFromJsonAsync<RootObject>(url, _jsonOptions, cancellationToken).ConfigureAwait(false);
await using FileStream jsonFileStream = new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.None, IODefaults.FileStreamBufferSize, FileOptions.Asynchronous);
await JsonSerializer.SerializeAsync(jsonFileStream, rootObject, _jsonOptions, cancellationToken).ConfigureAwait(false);
@@ -335,37 +356,13 @@ namespace MediaBrowser.Providers.Plugins.Omdb
imdbParam,
seasonId));
- var rootObject = await GetDeserializedOmdbResponse<SeasonRootObject>(_httpClientFactory.CreateClient(NamedClient.Default), url, cancellationToken).ConfigureAwait(false);
+ var rootObject = await _httpClientFactory.CreateClient(NamedClient.Default).GetFromJsonAsync<SeasonRootObject>(url, _jsonOptions, cancellationToken).ConfigureAwait(false);
await using FileStream jsonFileStream = new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.None, IODefaults.FileStreamBufferSize, FileOptions.Asynchronous);
await JsonSerializer.SerializeAsync(jsonFileStream, rootObject, _jsonOptions, cancellationToken).ConfigureAwait(false);
return path;
}
- /// <summary>Gets response from OMDB service as type T.</summary>
- /// <param name="httpClient">HttpClient instance to use for service call.</param>
- /// <param name="url">Http URL to use for service call.</param>
- /// <param name="cancellationToken">CancellationToken to use for service call.</param>
- /// <typeparam name="T">The first generic type parameter.</typeparam>
- /// <returns>OMDB service response as type T.</returns>
- public async Task<T> GetDeserializedOmdbResponse<T>(HttpClient httpClient, string url, CancellationToken cancellationToken)
- {
- using var response = await GetOmdbResponse(httpClient, url, cancellationToken).ConfigureAwait(false);
- await using Stream content = await response.Content.ReadAsStreamAsync(cancellationToken).ConfigureAwait(false);
-
- return await JsonSerializer.DeserializeAsync<T>(content, _jsonOptions, cancellationToken).ConfigureAwait(false);
- }
-
- /// <summary>Gets response from OMDB service.</summary>
- /// <param name="httpClient">HttpClient instance to use for service call.</param>
- /// <param name="url">Http URL to use for service call.</param>
- /// <param name="cancellationToken">CancellationToken to use for service call.</param>
- /// <returns>OMDB service response as HttpResponseMessage.</returns>
- public static Task<HttpResponseMessage> GetOmdbResponse(HttpClient httpClient, string url, CancellationToken cancellationToken)
- {
- return httpClient.GetAsync(url, cancellationToken);
- }
-
internal string GetDataFilePath(string imdbId)
{
if (string.IsNullOrEmpty(imdbId))
@@ -394,31 +391,25 @@ namespace MediaBrowser.Providers.Plugins.Omdb
return Path.Combine(dataPath, filename);
}
- private void ParseAdditionalMetadata<T>(MetadataResult<T> itemResult, RootObject result)
+ private static void ParseAdditionalMetadata<T>(MetadataResult<T> itemResult, RootObject result, bool isEnglishRequested)
where T : BaseItem
{
var item = itemResult.Item;
- var isConfiguredForEnglish = IsConfiguredForEnglish(item) || _configurationManager.Configuration.EnableNewOmdbSupport;
-
// 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))
+ if (isEnglishRequested && !string.IsNullOrWhiteSpace(result.Genre))
{
item.Genres = Array.Empty<string>();
- foreach (var genre in result.Genre
- .Split(',', StringSplitOptions.RemoveEmptyEntries)
- .Select(i => i.Trim())
- .Where(i => !string.IsNullOrWhiteSpace(i)))
+ foreach (var genre in result.Genre.Split(',', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries))
{
item.AddGenre(genre);
}
}
- if (isConfiguredForEnglish)
+ if (isEnglishRequested)
{
- // Omdb is currently English only, so for other languages skip this and let secondary providers fill it in
item.Overview = result.Plot;
}
@@ -431,7 +422,7 @@ namespace MediaBrowser.Providers.Plugins.Omdb
{
var person = new PersonInfo
{
- Name = result.Director.Trim(),
+ Name = result.Director,
Type = PersonType.Director
};
@@ -442,7 +433,7 @@ namespace MediaBrowser.Providers.Plugins.Omdb
{
var person = new PersonInfo
{
- Name = result.Writer.Trim(),
+ Name = result.Writer,
Type = PersonType.Writer
};
@@ -451,29 +442,34 @@ namespace MediaBrowser.Providers.Plugins.Omdb
if (!string.IsNullOrWhiteSpace(result.Actors))
{
- var actorList = result.Actors.Split(',');
+ var actorList = result.Actors.Split(',', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries);
foreach (var actor in actorList)
{
- if (!string.IsNullOrWhiteSpace(actor))
+ if (string.IsNullOrWhiteSpace(actor))
{
- var person = new PersonInfo
- {
- Name = actor.Trim(),
- Type = PersonType.Actor
- };
-
- itemResult.AddPerson(person);
+ continue;
}
+
+ var person = new PersonInfo
+ {
+ Name = actor,
+ Type = PersonType.Actor
+ };
+
+ itemResult.AddPerson(person);
}
}
}
- private bool IsConfiguredForEnglish(BaseItem item)
+ private static bool IsConfiguredForEnglish(BaseItem item, string language)
{
- var lang = item.GetPreferredMetadataLanguage();
+ if (string.IsNullOrEmpty(language))
+ {
+ language = item.GetPreferredMetadataLanguage();
+ }
// The data isn't localized and so can only be used for English users
- return string.Equals(lang, "en", StringComparison.OrdinalIgnoreCase);
+ return string.Equals(language, "en", StringComparison.OrdinalIgnoreCase);
}
internal class SeasonRootObject
@@ -550,7 +546,7 @@ namespace MediaBrowser.Providers.Plugins.Omdb
if (Ratings != null)
{
var rating = Ratings.FirstOrDefault(i => string.Equals(i.Source, "Rotten Tomatoes", StringComparison.OrdinalIgnoreCase));
- if (rating != null && rating.Value != null)
+ if (rating?.Value != null)
{
var value = rating.Value.TrimEnd('%');
if (float.TryParse(value, NumberStyles.Any, CultureInfo.InvariantCulture, out var score))
diff --git a/MediaBrowser.Providers/Plugins/Tmdb/BoxSets/TmdbBoxSetImageProvider.cs b/MediaBrowser.Providers/Plugins/Tmdb/BoxSets/TmdbBoxSetImageProvider.cs
index 35dc36811e..29a557c315 100644
--- a/MediaBrowser.Providers/Plugins/Tmdb/BoxSets/TmdbBoxSetImageProvider.cs
+++ b/MediaBrowser.Providers/Plugins/Tmdb/BoxSets/TmdbBoxSetImageProvider.cs
@@ -13,7 +13,6 @@ using MediaBrowser.Common.Net;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.Movies;
using MediaBrowser.Controller.Providers;
-using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Providers;
@@ -67,40 +66,12 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.BoxSets
return Enumerable.Empty<RemoteImageInfo>();
}
- var remoteImages = new List<RemoteImageInfo>();
+ var posters = collection.Images.Posters;
+ var backdrops = collection.Images.Backdrops;
+ var remoteImages = new List<RemoteImageInfo>(posters.Count + backdrops.Count);
- for (var i = 0; i < collection.Images.Posters.Count; i++)
- {
- var poster = collection.Images.Posters[i];
- remoteImages.Add(new RemoteImageInfo
- {
- Url = _tmdbClientManager.GetPosterUrl(poster.FilePath),
- CommunityRating = poster.VoteAverage,
- VoteCount = poster.VoteCount,
- Width = poster.Width,
- Height = poster.Height,
- Language = TmdbUtils.AdjustImageLanguage(poster.Iso_639_1, language),
- ProviderName = Name,
- Type = ImageType.Primary,
- RatingType = RatingType.Score
- });
- }
-
- for (var i = 0; i < collection.Images.Backdrops.Count; i++)
- {
- var backdrop = collection.Images.Backdrops[i];
- remoteImages.Add(new RemoteImageInfo
- {
- Url = _tmdbClientManager.GetBackdropUrl(backdrop.FilePath),
- CommunityRating = backdrop.VoteAverage,
- VoteCount = backdrop.VoteCount,
- Width = backdrop.Width,
- Height = backdrop.Height,
- ProviderName = Name,
- Type = ImageType.Backdrop,
- RatingType = RatingType.Score
- });
- }
+ _tmdbClientManager.ConvertPostersToRemoteImageInfo(posters, language, remoteImages);
+ _tmdbClientManager.ConvertBackdropsToRemoteImageInfo(backdrops, language, remoteImages);
return remoteImages;
}
diff --git a/MediaBrowser.Providers/Plugins/Tmdb/Configuration/PluginConfiguration.cs b/MediaBrowser.Providers/Plugins/Tmdb/Configuration/PluginConfiguration.cs
index 907f0160d6..9a78a75362 100644
--- a/MediaBrowser.Providers/Plugins/Tmdb/Configuration/PluginConfiguration.cs
+++ b/MediaBrowser.Providers/Plugins/Tmdb/Configuration/PluginConfiguration.cs
@@ -11,5 +11,20 @@ namespace MediaBrowser.Providers.Plugins.Tmdb
/// Gets or sets a value indicating whether include adult content when searching with TMDb.
/// </summary>
public bool IncludeAdult { get; set; }
+
+ /// <summary>
+ /// Gets or sets a value indicating whether tags should be imported for series from TMDb.
+ /// </summary>
+ public bool ExcludeTagsSeries { get; set; }
+
+ /// <summary>
+ /// Gets or sets a value indicating whether tags should be imported for movies from TMDb.
+ /// </summary>
+ public bool ExcludeTagsMovies { get; set; }
+
+ /// <summary>
+ /// Gets or sets a value indicating the maximum number of cast members to fetch for an item.
+ /// </summary>
+ public int MaxCastMembers { get; set; } = 15;
}
}
diff --git a/MediaBrowser.Providers/Plugins/Tmdb/Configuration/config.html b/MediaBrowser.Providers/Plugins/Tmdb/Configuration/config.html
index 6f42549d7d..12b4c7ca4e 100644
--- a/MediaBrowser.Providers/Plugins/Tmdb/Configuration/config.html
+++ b/MediaBrowser.Providers/Plugins/Tmdb/Configuration/config.html
@@ -12,6 +12,18 @@
<input is="emby-checkbox" type="checkbox" id="includeAdult" />
<span>Include adult content in search results.</span>
</label>
+ <label class="checkboxContainer">
+ <input is="emby-checkbox" type="checkbox" id="excludeTagsSeries" />
+ <span>Exclude tags/keywords from metadata fetched for series.</span>
+ </label>
+ <label class="checkboxContainer">
+ <input is="emby-checkbox" type="checkbox" id="excludeTagsMovies" />
+ <span>Exclude tags/keywords from metadata fetched for movies.</span>
+ </label>
+ <div class="inputContainer">
+ <input is="emby-input" type="number" id="maxCastMembers" pattern="[0-9]*" required min="0" max="1000" label="Max Cast Members" />
+ <div class="fieldDescription">The maximum number of cast members to fetch for an item.</div>
+ </div>
<br />
<div>
<button is="emby-button" type="submit" class="raised button-submit block"><span>Save</span></button>
@@ -29,20 +41,33 @@
Dashboard.showLoadingMsg();
ApiClient.getPluginConfiguration(PluginConfig.pluginId).then(function (config) {
document.querySelector('#includeAdult').checked = config.IncludeAdult;
+ document.querySelector('#excludeTagsSeries').checked = config.ExcludeTagsSeries;
+ document.querySelector('#excludeTagsMovies').checked = config.ExcludeTagsMovies;
+
+ var maxCastMembers = document.querySelector('#maxCastMembers');
+ maxCastMembers.value = config.MaxCastMembers;
+ maxCastMembers.dispatchEvent(new Event('change', {
+ bubbles: true,
+ cancelable: false
+ }));
+
Dashboard.hideLoadingMsg();
});
});
-
+
document.querySelector('.configForm')
.addEventListener('submit', function (e) {
Dashboard.showLoadingMsg();
-
+
ApiClient.getPluginConfiguration(PluginConfig.pluginId).then(function (config) {
config.IncludeAdult = document.querySelector('#includeAdult').checked;
+ config.ExcludeTagsSeries = document.querySelector('#excludeTagsSeries').checked;
+ config.ExcludeTagsMovies = document.querySelector('#excludeTagsMovies').checked;
+ config.MaxCastMembers = document.querySelector('#maxCastMembers').value;
ApiClient.updatePluginConfiguration(PluginConfig.pluginId, config).then(Dashboard.processPluginConfigurationUpdateResult);
});
-
+
e.preventDefault();
return false;
});
diff --git a/MediaBrowser.Providers/Plugins/Tmdb/Movies/TmdbMovieImageProvider.cs b/MediaBrowser.Providers/Plugins/Tmdb/Movies/TmdbMovieImageProvider.cs
index 015eddc1ac..f71f7bd10d 100644
--- a/MediaBrowser.Providers/Plugins/Tmdb/Movies/TmdbMovieImageProvider.cs
+++ b/MediaBrowser.Providers/Plugins/Tmdb/Movies/TmdbMovieImageProvider.cs
@@ -13,7 +13,6 @@ using MediaBrowser.Common.Net;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.Movies;
using MediaBrowser.Controller.Providers;
-using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Providers;
using TMDbLib.Objects.Find;
@@ -84,40 +83,12 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.Movies
return Enumerable.Empty<RemoteImageInfo>();
}
- var remoteImages = new List<RemoteImageInfo>();
+ var posters = movie.Images.Posters;
+ var backdrops = movie.Images.Backdrops;
+ var remoteImages = new List<RemoteImageInfo>(posters.Count + backdrops.Count);
- for (var i = 0; i < movie.Images.Posters.Count; i++)
- {
- var poster = movie.Images.Posters[i];
- remoteImages.Add(new RemoteImageInfo
- {
- Url = _tmdbClientManager.GetPosterUrl(poster.FilePath),
- CommunityRating = poster.VoteAverage,
- VoteCount = poster.VoteCount,
- Width = poster.Width,
- Height = poster.Height,
- Language = TmdbUtils.AdjustImageLanguage(poster.Iso_639_1, language),
- ProviderName = Name,
- Type = ImageType.Primary,
- RatingType = RatingType.Score
- });
- }
-
- for (var i = 0; i < movie.Images.Backdrops.Count; i++)
- {
- var backdrop = movie.Images.Backdrops[i];
- remoteImages.Add(new RemoteImageInfo
- {
- Url = _tmdbClientManager.GetPosterUrl(backdrop.FilePath),
- CommunityRating = backdrop.VoteAverage,
- VoteCount = backdrop.VoteCount,
- Width = backdrop.Width,
- Height = backdrop.Height,
- ProviderName = Name,
- Type = ImageType.Backdrop,
- RatingType = RatingType.Score
- });
- }
+ _tmdbClientManager.ConvertPostersToRemoteImageInfo(posters, language, remoteImages);
+ _tmdbClientManager.ConvertBackdropsToRemoteImageInfo(backdrops, language, remoteImages);
return remoteImages;
}
diff --git a/MediaBrowser.Providers/Plugins/Tmdb/Movies/TmdbMovieProvider.cs b/MediaBrowser.Providers/Plugins/Tmdb/Movies/TmdbMovieProvider.cs
index 9dd0678561..fcaacc90d1 100644
--- a/MediaBrowser.Providers/Plugins/Tmdb/Movies/TmdbMovieProvider.cs
+++ b/MediaBrowser.Providers/Plugins/Tmdb/Movies/TmdbMovieProvider.cs
@@ -241,8 +241,7 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.Movies
if (movieResult.Credits?.Cast != null)
{
- // TODO configurable
- foreach (var actor in movieResult.Credits.Cast.OrderBy(a => a.Order).Take(TmdbUtils.MaxCastMembers))
+ foreach (var actor in movieResult.Credits.Cast.OrderBy(a => a.Order).Take(Plugin.Instance.Configuration.MaxCastMembers))
{
var personInfo = new PersonInfo
{
diff --git a/MediaBrowser.Providers/Plugins/Tmdb/People/TmdbPersonImageProvider.cs b/MediaBrowser.Providers/Plugins/Tmdb/People/TmdbPersonImageProvider.cs
index 1fc5ccba59..7ce4cfe676 100644
--- a/MediaBrowser.Providers/Plugins/Tmdb/People/TmdbPersonImageProvider.cs
+++ b/MediaBrowser.Providers/Plugins/Tmdb/People/TmdbPersonImageProvider.cs
@@ -60,21 +60,10 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.People
return Enumerable.Empty<RemoteImageInfo>();
}
- var remoteImages = new RemoteImageInfo[personResult.Images.Profiles.Count];
+ var profiles = personResult.Images.Profiles;
+ var remoteImages = new List<RemoteImageInfo>(profiles.Count);
- for (var i = 0; i < personResult.Images.Profiles.Count; i++)
- {
- var image = personResult.Images.Profiles[i];
- remoteImages[i] = new RemoteImageInfo
- {
- ProviderName = Name,
- Type = ImageType.Primary,
- Width = image.Width,
- Height = image.Height,
- Language = TmdbUtils.AdjustImageLanguage(image.Iso_639_1, language),
- Url = _tmdbClientManager.GetProfileUrl(image.FilePath)
- };
- }
+ _tmdbClientManager.ConvertProfilesToRemoteImageInfo(profiles, language, remoteImages);
return remoteImages;
}
diff --git a/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbEpisodeImageProvider.cs b/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbEpisodeImageProvider.cs
index eb75e94050..5eec776b5b 100644
--- a/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbEpisodeImageProvider.cs
+++ b/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbEpisodeImageProvider.cs
@@ -12,7 +12,6 @@ using System.Threading.Tasks;
using MediaBrowser.Common.Net;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Providers;
-using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Providers;
@@ -75,23 +74,9 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.TV
return Enumerable.Empty<RemoteImageInfo>();
}
- var remoteImages = new RemoteImageInfo[stills.Count];
- for (var i = 0; i < stills.Count; i++)
- {
- var image = stills[i];
- remoteImages[i] = new RemoteImageInfo
- {
- Url = _tmdbClientManager.GetStillUrl(image.FilePath),
- CommunityRating = image.VoteAverage,
- VoteCount = image.VoteCount,
- Width = image.Width,
- Height = image.Height,
- Language = TmdbUtils.AdjustImageLanguage(image.Iso_639_1, language),
- ProviderName = Name,
- Type = ImageType.Primary,
- RatingType = RatingType.Score
- };
- }
+ var remoteImages = new List<RemoteImageInfo>(stills.Count);
+
+ _tmdbClientManager.ConvertStillsToRemoteImageInfo(stills, language, remoteImages);
return remoteImages;
}
diff --git a/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbEpisodeProvider.cs b/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbEpisodeProvider.cs
index 3f826843a6..8ac9d0cab1 100644
--- a/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbEpisodeProvider.cs
+++ b/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbEpisodeProvider.cs
@@ -154,7 +154,7 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.TV
if (credits?.Cast != null)
{
- foreach (var actor in credits.Cast.OrderBy(a => a.Order).Take(TmdbUtils.MaxCastMembers))
+ foreach (var actor in credits.Cast.OrderBy(a => a.Order).Take(Plugin.Instance.Configuration.MaxCastMembers))
{
metadataResult.AddPerson(new PersonInfo
{
@@ -168,7 +168,7 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.TV
if (credits?.GuestStars != null)
{
- foreach (var guest in credits.GuestStars.OrderBy(a => a.Order).Take(TmdbUtils.MaxCastMembers))
+ foreach (var guest in credits.GuestStars.OrderBy(a => a.Order).Take(Plugin.Instance.Configuration.MaxCastMembers))
{
metadataResult.AddPerson(new PersonInfo
{
diff --git a/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeasonImageProvider.cs b/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeasonImageProvider.cs
index ca44c9bbc3..4446fa9665 100644
--- a/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeasonImageProvider.cs
+++ b/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeasonImageProvider.cs
@@ -11,7 +11,6 @@ using MediaBrowser.Common.Net;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.TV;
using MediaBrowser.Controller.Providers;
-using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Providers;
@@ -62,23 +61,9 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.TV
return Enumerable.Empty<RemoteImageInfo>();
}
- var remoteImages = new RemoteImageInfo[posters.Count];
- for (var i = 0; i < posters.Count; i++)
- {
- var image = posters[i];
- remoteImages[i] = new RemoteImageInfo
- {
- Url = _tmdbClientManager.GetPosterUrl(image.FilePath),
- CommunityRating = image.VoteAverage,
- VoteCount = image.VoteCount,
- Width = image.Width,
- Height = image.Height,
- Language = TmdbUtils.AdjustImageLanguage(image.Iso_639_1, language),
- ProviderName = Name,
- Type = ImageType.Primary,
- RatingType = RatingType.Score
- };
- }
+ var remoteImages = new List<RemoteImageInfo>(posters.Count);
+
+ _tmdbClientManager.ConvertPostersToRemoteImageInfo(posters, language, remoteImages);
return remoteImages;
}
diff --git a/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeasonProvider.cs b/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeasonProvider.cs
index 4ac8896801..7afaddc245 100644
--- a/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeasonProvider.cs
+++ b/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeasonProvider.cs
@@ -67,7 +67,7 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.TV
var credits = seasonResult.Credits;
if (credits?.Cast != null)
{
- var cast = credits.Cast.OrderBy(c => c.Order).Take(TmdbUtils.MaxCastMembers).ToList();
+ var cast = credits.Cast.OrderBy(c => c.Order).Take(Plugin.Instance.Configuration.MaxCastMembers).ToList();
for (var i = 0; i < cast.Count; i++)
{
result.AddPerson(new PersonInfo
diff --git a/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeriesImageProvider.cs b/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeriesImageProvider.cs
index f3f3403789..5ef3736c4f 100644
--- a/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeriesImageProvider.cs
+++ b/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeriesImageProvider.cs
@@ -11,7 +11,6 @@ using MediaBrowser.Common.Net;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.TV;
using MediaBrowser.Controller.Providers;
-using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Providers;
@@ -70,41 +69,10 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.TV
var posters = series.Images.Posters;
var backdrops = series.Images.Backdrops;
+ var remoteImages = new List<RemoteImageInfo>(posters.Count + backdrops.Count);
- var remoteImages = new RemoteImageInfo[posters.Count + backdrops.Count];
-
- for (var i = 0; i < posters.Count; i++)
- {
- var poster = posters[i];
- remoteImages[i] = new RemoteImageInfo
- {
- Url = _tmdbClientManager.GetPosterUrl(poster.FilePath),
- CommunityRating = poster.VoteAverage,
- VoteCount = poster.VoteCount,
- Width = poster.Width,
- Height = poster.Height,
- Language = TmdbUtils.AdjustImageLanguage(poster.Iso_639_1, language),
- ProviderName = Name,
- Type = ImageType.Primary,
- RatingType = RatingType.Score
- };
- }
-
- for (var i = 0; i < backdrops.Count; i++)
- {
- var backdrop = series.Images.Backdrops[i];
- remoteImages[posters.Count + i] = new RemoteImageInfo
- {
- Url = _tmdbClientManager.GetBackdropUrl(backdrop.FilePath),
- CommunityRating = backdrop.VoteAverage,
- VoteCount = backdrop.VoteCount,
- Width = backdrop.Width,
- Height = backdrop.Height,
- ProviderName = Name,
- Type = ImageType.Backdrop,
- RatingType = RatingType.Score
- };
- }
+ _tmdbClientManager.ConvertPostersToRemoteImageInfo(posters, language, remoteImages);
+ _tmdbClientManager.ConvertBackdropsToRemoteImageInfo(backdrops, language, remoteImages);
return remoteImages;
}
diff --git a/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeriesProvider.cs b/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeriesProvider.cs
index feda15cf75..77e22ffbf0 100644
--- a/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeriesProvider.cs
+++ b/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeriesProvider.cs
@@ -331,7 +331,7 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.TV
{
if (seriesResult.Credits?.Cast != null)
{
- foreach (var actor in seriesResult.Credits.Cast.OrderBy(a => a.Order).Take(TmdbUtils.MaxCastMembers))
+ foreach (var actor in seriesResult.Credits.Cast.OrderBy(a => a.Order).Take(Plugin.Instance.Configuration.MaxCastMembers))
{
var personInfo = new PersonInfo
{
diff --git a/MediaBrowser.Providers/Plugins/Tmdb/TmdbClientManager.cs b/MediaBrowser.Providers/Plugins/Tmdb/TmdbClientManager.cs
index 74be4c7933..4d3bc61e58 100644
--- a/MediaBrowser.Providers/Plugins/Tmdb/TmdbClientManager.cs
+++ b/MediaBrowser.Providers/Plugins/Tmdb/TmdbClientManager.cs
@@ -1,10 +1,13 @@
-#nullable disable
+#nullable disable
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Threading;
using System.Threading.Tasks;
+using MediaBrowser.Model.Dto;
+using MediaBrowser.Model.Entities;
+using MediaBrowser.Model.Providers;
using Microsoft.Extensions.Caching.Memory;
using TMDbLib.Client;
using TMDbLib.Objects.Collections;
@@ -57,11 +60,17 @@ namespace MediaBrowser.Providers.Plugins.Tmdb
await EnsureClientConfigAsync().ConfigureAwait(false);
+ var extraMethods = MovieMethods.Credits | MovieMethods.Releases | MovieMethods.Images | MovieMethods.Videos;
+ if (!(Plugin.Instance?.Configuration.ExcludeTagsMovies).GetValueOrDefault())
+ {
+ extraMethods |= MovieMethods.Keywords;
+ }
+
movie = await _tmDbClient.GetMovieAsync(
tmdbId,
TmdbUtils.NormalizeLanguage(language),
imageLanguages,
- MovieMethods.Credits | MovieMethods.Releases | MovieMethods.Images | MovieMethods.Keywords | MovieMethods.Videos,
+ extraMethods,
cancellationToken).ConfigureAwait(false);
if (movie != null)
@@ -123,11 +132,17 @@ namespace MediaBrowser.Providers.Plugins.Tmdb
await EnsureClientConfigAsync().ConfigureAwait(false);
+ var extraMethods = TvShowMethods.Credits | TvShowMethods.Images | TvShowMethods.ExternalIds | TvShowMethods.Videos | TvShowMethods.ContentRatings | TvShowMethods.EpisodeGroups;
+ if (!(Plugin.Instance?.Configuration.ExcludeTagsSeries).GetValueOrDefault())
+ {
+ extraMethods |= TvShowMethods.Keywords;
+ }
+
series = await _tmDbClient.GetTvShowAsync(
tmdbId,
language: TmdbUtils.NormalizeLanguage(language),
includeImageLanguage: imageLanguages,
- extraMethods: TvShowMethods.Credits | TvShowMethods.Images | TvShowMethods.Keywords | TvShowMethods.ExternalIds | TvShowMethods.Videos | TvShowMethods.ContentRatings | TvShowMethods.EpisodeGroups,
+ extraMethods: extraMethods,
cancellationToken: cancellationToken).ConfigureAwait(false);
if (series != null)
@@ -471,33 +486,29 @@ namespace MediaBrowser.Providers.Plugins.Tmdb
}
/// <summary>
- /// Gets the absolute URL of the poster.
+ /// Handles bad path checking and builds the absolute url.
/// </summary>
- /// <param name="posterPath">The relative URL of the poster.</param>
+ /// <param name="size">The image size to fetch.</param>
+ /// <param name="path">The relative URL of the image.</param>
/// <returns>The absolute URL.</returns>
- public string GetPosterUrl(string posterPath)
+ private string GetUrl(string size, string path)
{
- if (string.IsNullOrEmpty(posterPath))
+ if (string.IsNullOrEmpty(path))
{
return null;
}
- return _tmDbClient.GetImageUrl(_tmDbClient.Config.Images.PosterSizes[^1], posterPath).ToString();
+ return _tmDbClient.GetImageUrl(size, path, true).ToString();
}
/// <summary>
- /// Gets the absolute URL of the backdrop image.
+ /// Gets the absolute URL of the poster.
/// </summary>
- /// <param name="posterPath">The relative URL of the backdrop image.</param>
+ /// <param name="posterPath">The relative URL of the poster.</param>
/// <returns>The absolute URL.</returns>
- public string GetBackdropUrl(string posterPath)
+ public string GetPosterUrl(string posterPath)
{
- if (string.IsNullOrEmpty(posterPath))
- {
- return null;
- }
-
- return _tmDbClient.GetImageUrl(_tmDbClient.Config.Images.BackdropSizes[^1], posterPath).ToString();
+ return GetUrl(_tmDbClient.Config.Images.PosterSizes[^1], posterPath);
}
/// <summary>
@@ -507,27 +518,79 @@ namespace MediaBrowser.Providers.Plugins.Tmdb
/// <returns>The absolute URL.</returns>
public string GetProfileUrl(string actorProfilePath)
{
- if (string.IsNullOrEmpty(actorProfilePath))
- {
- return null;
- }
+ return GetUrl(_tmDbClient.Config.Images.ProfileSizes[^1], actorProfilePath);
+ }
- return _tmDbClient.GetImageUrl(_tmDbClient.Config.Images.ProfileSizes[^1], actorProfilePath).ToString();
+ /// <summary>
+ /// Converts poster <see cref="ImageData"/>s into <see cref="RemoteImageInfo"/>s.
+ /// </summary>
+ /// <param name="images">The input images.</param>
+ /// <param name="requestLanguage">The requested language.</param>
+ /// <param name="results">The collection to add the remote images into.</param>
+ public void ConvertPostersToRemoteImageInfo(List<ImageData> images, string requestLanguage, List<RemoteImageInfo> results)
+ {
+ ConvertToRemoteImageInfo(images, _tmDbClient.Config.Images.PosterSizes[^1], ImageType.Primary, requestLanguage, results);
}
/// <summary>
- /// Gets the absolute URL of the still image.
+ /// Converts backdrop <see cref="ImageData"/>s into <see cref="RemoteImageInfo"/>s.
/// </summary>
- /// <param name="filePath">The relative URL of the still image.</param>
- /// <returns>The absolute URL.</returns>
- public string GetStillUrl(string filePath)
+ /// <param name="images">The input images.</param>
+ /// <param name="requestLanguage">The requested language.</param>
+ /// <param name="results">The collection to add the remote images into.</param>
+ public void ConvertBackdropsToRemoteImageInfo(List<ImageData> images, string requestLanguage, List<RemoteImageInfo> results)
{
- if (string.IsNullOrEmpty(filePath))
+ ConvertToRemoteImageInfo(images, _tmDbClient.Config.Images.BackdropSizes[^1], ImageType.Backdrop, requestLanguage, results);
+ }
+
+ /// <summary>
+ /// Converts profile <see cref="ImageData"/>s into <see cref="RemoteImageInfo"/>s.
+ /// </summary>
+ /// <param name="images">The input images.</param>
+ /// <param name="requestLanguage">The requested language.</param>
+ /// <param name="results">The collection to add the remote images into.</param>
+ public void ConvertProfilesToRemoteImageInfo(List<ImageData> images, string requestLanguage, List<RemoteImageInfo> results)
+ {
+ ConvertToRemoteImageInfo(images, _tmDbClient.Config.Images.ProfileSizes[^1], ImageType.Primary, requestLanguage, results);
+ }
+
+ /// <summary>
+ /// Converts still <see cref="ImageData"/>s into <see cref="RemoteImageInfo"/>s.
+ /// </summary>
+ /// <param name="images">The input images.</param>
+ /// <param name="requestLanguage">The requested language.</param>
+ /// <param name="results">The collection to add the remote images into.</param>
+ public void ConvertStillsToRemoteImageInfo(List<ImageData> images, string requestLanguage, List<RemoteImageInfo> results)
+ {
+ ConvertToRemoteImageInfo(images, _tmDbClient.Config.Images.StillSizes[^1], ImageType.Primary, requestLanguage, results);
+ }
+
+ /// <summary>
+ /// Converts <see cref="ImageData"/>s into <see cref="RemoteImageInfo"/>s.
+ /// </summary>
+ /// <param name="images">The input images.</param>
+ /// <param name="size">The size of the image to fetch.</param>
+ /// <param name="type">The type of the image.</param>
+ /// <param name="requestLanguage">The requested language.</param>
+ /// <param name="results">The collection to add the remote images into.</param>
+ private void ConvertToRemoteImageInfo(List<ImageData> images, string size, ImageType type, string requestLanguage, List<RemoteImageInfo> results)
+ {
+ for (var i = 0; i < images.Count; i++)
{
- return null;
+ var image = images[i];
+ results.Add(new RemoteImageInfo
+ {
+ Url = GetUrl(size, image.FilePath),
+ CommunityRating = image.VoteAverage,
+ VoteCount = image.VoteCount,
+ Width = image.Width,
+ Height = image.Height,
+ Language = TmdbUtils.AdjustImageLanguage(image.Iso_639_1, requestLanguage),
+ ProviderName = TmdbUtils.ProviderName,
+ Type = type,
+ RatingType = RatingType.Score
+ });
}
-
- return _tmDbClient.GetImageUrl(_tmDbClient.Config.Images.StillSizes[^1], filePath).ToString();
}
private Task EnsureClientConfigAsync()
@@ -542,7 +605,7 @@ namespace MediaBrowser.Providers.Plugins.Tmdb
GC.SuppressFinalize(this);
}
-/// <summary>
+ /// <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>
diff --git a/MediaBrowser.Providers/Plugins/Tmdb/TmdbUtils.cs b/MediaBrowser.Providers/Plugins/Tmdb/TmdbUtils.cs
index 58ab9f5473..a3a78103ea 100644
--- a/MediaBrowser.Providers/Plugins/Tmdb/TmdbUtils.cs
+++ b/MediaBrowser.Providers/Plugins/Tmdb/TmdbUtils.cs
@@ -29,11 +29,6 @@ namespace MediaBrowser.Providers.Plugins.Tmdb
public const string ApiKey = "4219e299c89411838049ab0dab19ebd5";
/// <summary>
- /// Maximum number of cast members to pull.
- /// </summary>
- public const int MaxCastMembers = 15;
-
- /// <summary>
/// The crew types to keep.
/// </summary>
public static readonly string[] WantedCrewTypes =
diff --git a/MediaBrowser.Providers/TV/SeriesMetadataService.cs b/MediaBrowser.Providers/TV/SeriesMetadataService.cs
index 9d223b4b66..770dc3e003 100644
--- a/MediaBrowser.Providers/TV/SeriesMetadataService.cs
+++ b/MediaBrowser.Providers/TV/SeriesMetadataService.cs
@@ -130,11 +130,12 @@ namespace MediaBrowser.Providers.TV
/// <returns>The async task.</returns>
private async Task FillInMissingSeasonsAsync(Series series, CancellationToken cancellationToken)
{
- var episodesInSeriesFolder = series.GetRecursiveChildren(i => i is Episode)
- .Cast<Episode>()
+ var seriesChildren = series.GetRecursiveChildren(i => i is Episode || i is Season);
+ var episodesInSeriesFolder = seriesChildren
+ .OfType<Episode>()
.Where(i => !i.IsInSeasonFolder);
- List<Season> seasons = series.Children.OfType<Season>().ToList();
+ List<Season> seasons = seriesChildren.OfType<Season>().ToList();
// Loop through the unique season numbers
foreach (var episode in episodesInSeriesFolder)