Merge remote-tracking branch 'upstream/master' into api-upload-subtitle

This commit is contained in:
crobibero
2020-09-11 15:53:04 -06:00
1587 changed files with 58442 additions and 53570 deletions

View File

@@ -1,3 +1,5 @@
#pragma warning disable CS1591
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Library;
@@ -25,7 +27,7 @@ namespace MediaBrowser.Providers.Books
protected override void MergeData(
MetadataResult<AudioBook> source,
MetadataResult<AudioBook> target,
MetadataFields[] lockedFields,
MetadataField[] lockedFields,
bool replaceData,
bool mergeMetadataSettings)
{

View File

@@ -1,3 +1,5 @@
#pragma warning disable CS1591
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Library;
@@ -22,7 +24,7 @@ namespace MediaBrowser.Providers.Books
}
/// <inheritdoc />
protected override void MergeData(MetadataResult<Book> source, MetadataResult<Book> target, MetadataFields[] lockedFields, bool replaceData, bool mergeMetadataSettings)
protected override void MergeData(MetadataResult<Book> source, MetadataResult<Book> target, MetadataField[] lockedFields, bool replaceData, bool mergeMetadataSettings)
{
ProviderUtils.MergeBaseItemData(source, target, lockedFields, replaceData, mergeMetadataSettings);

View File

@@ -1,3 +1,5 @@
#pragma warning disable CS1591
using System.Collections.Generic;
using System.Linq;
using MediaBrowser.Controller.Configuration;
@@ -43,7 +45,7 @@ namespace MediaBrowser.Providers.BoxSets
}
/// <inheritdoc />
protected override void MergeData(MetadataResult<BoxSet> source, MetadataResult<BoxSet> target, MetadataFields[] lockedFields, bool replaceData, bool mergeMetadataSettings)
protected override void MergeData(MetadataResult<BoxSet> source, MetadataResult<BoxSet> target, MetadataField[] lockedFields, bool replaceData, bool mergeMetadataSettings)
{
ProviderUtils.MergeBaseItemData(source, target, lockedFields, replaceData, mergeMetadataSettings);

View File

@@ -1,3 +1,5 @@
#pragma warning disable CS1591
using MediaBrowser.Controller.Channels;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Library;
@@ -22,7 +24,7 @@ namespace MediaBrowser.Providers.Channels
}
/// <inheritdoc />
protected override void MergeData(MetadataResult<Channel> source, MetadataResult<Channel> target, MetadataFields[] lockedFields, bool replaceData, bool mergeMetadataSettings)
protected override void MergeData(MetadataResult<Channel> source, MetadataResult<Channel> target, MetadataField[] lockedFields, bool replaceData, bool mergeMetadataSettings)
{
ProviderUtils.MergeBaseItemData(source, target, lockedFields, replaceData, mergeMetadataSettings);
}

View File

@@ -1,3 +1,4 @@
#pragma warning disable CS1591
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Entities;
@@ -23,7 +24,7 @@ namespace MediaBrowser.Providers.Folders
}
/// <inheritdoc />
protected override void MergeData(MetadataResult<CollectionFolder> source, MetadataResult<CollectionFolder> target, MetadataFields[] lockedFields, bool replaceData, bool mergeMetadataSettings)
protected override void MergeData(MetadataResult<CollectionFolder> source, MetadataResult<CollectionFolder> target, MetadataField[] lockedFields, bool replaceData, bool mergeMetadataSettings)
{
ProviderUtils.MergeBaseItemData(source, target, lockedFields, replaceData, mergeMetadataSettings);
}

View File

@@ -1,3 +1,5 @@
#pragma warning disable CS1591
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Library;
@@ -26,7 +28,7 @@ namespace MediaBrowser.Providers.Folders
public override int Order => 10;
/// <inheritdoc />
protected override void MergeData(MetadataResult<Folder> source, MetadataResult<Folder> target, MetadataFields[] lockedFields, bool replaceData, bool mergeMetadataSettings)
protected override void MergeData(MetadataResult<Folder> source, MetadataResult<Folder> target, MetadataField[] lockedFields, bool replaceData, bool mergeMetadataSettings)
{
ProviderUtils.MergeBaseItemData(source, target, lockedFields, replaceData, mergeMetadataSettings);
}

View File

@@ -1,3 +1,5 @@
#pragma warning disable CS1591
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Library;
@@ -22,7 +24,7 @@ namespace MediaBrowser.Providers.Folders
}
/// <inheritdoc />
protected override void MergeData(MetadataResult<UserView> source, MetadataResult<UserView> target, MetadataFields[] lockedFields, bool replaceData, bool mergeMetadataSettings)
protected override void MergeData(MetadataResult<UserView> source, MetadataResult<UserView> target, MetadataField[] lockedFields, bool replaceData, bool mergeMetadataSettings)
{
ProviderUtils.MergeBaseItemData(source, target, lockedFields, replaceData, mergeMetadataSettings);
}

View File

@@ -1,3 +1,5 @@
#pragma warning disable CS1591
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Library;
@@ -22,7 +24,7 @@ namespace MediaBrowser.Providers.Genres
}
/// <inheritdoc />
protected override void MergeData(MetadataResult<Genre> source, MetadataResult<Genre> target, MetadataFields[] lockedFields, bool replaceData, bool mergeMetadataSettings)
protected override void MergeData(MetadataResult<Genre> source, MetadataResult<Genre> target, MetadataField[] lockedFields, bool replaceData, bool mergeMetadataSettings)
{
ProviderUtils.MergeBaseItemData(source, target, lockedFields, replaceData, mergeMetadataSettings);
}

View File

@@ -1,3 +1,5 @@
#pragma warning disable CS1591
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.LiveTv;
@@ -22,7 +24,7 @@ namespace MediaBrowser.Providers.LiveTv
}
/// <inheritdoc />
protected override void MergeData(MetadataResult<LiveTvChannel> source, MetadataResult<LiveTvChannel> target, MetadataFields[] lockedFields, bool replaceData, bool mergeMetadataSettings)
protected override void MergeData(MetadataResult<LiveTvChannel> source, MetadataResult<LiveTvChannel> target, MetadataField[] lockedFields, bool replaceData, bool mergeMetadataSettings)
{
ProviderUtils.MergeBaseItemData(source, target, lockedFields, replaceData, mergeMetadataSettings);
}

View File

@@ -1,3 +1,5 @@
#pragma warning disable CS1591
using System;
using System.Collections.Generic;
using System.Globalization;
@@ -9,30 +11,33 @@ using MediaBrowser.Common.Configuration;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.Audio;
using MediaBrowser.Controller.Entities.TV;
using MediaBrowser.Controller.Library;
using MediaBrowser.Model.Configuration;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.IO;
using MediaBrowser.Model.Net;
using Microsoft.Extensions.Logging;
using Episode = MediaBrowser.Controller.Entities.TV.Episode;
using MusicAlbum = MediaBrowser.Controller.Entities.Audio.MusicAlbum;
using Person = MediaBrowser.Controller.Entities.Person;
using Season = MediaBrowser.Controller.Entities.TV.Season;
namespace MediaBrowser.Providers.Manager
{
/// <summary>
/// Class ImageSaver
/// Class ImageSaver.
/// </summary>
public class ImageSaver
{
private static readonly CultureInfo UsCulture = new CultureInfo("en-US");
/// <summary>
/// The _config
/// The _config.
/// </summary>
private readonly IServerConfigurationManager _config;
/// <summary>
/// The _directory watchers
/// The _directory watchers.
/// </summary>
private readonly ILibraryMonitor _libraryMonitor;
private readonly IFileSystem _fileSystem;
@@ -53,6 +58,16 @@ namespace MediaBrowser.Providers.Manager
_logger = logger;
}
private bool EnableExtraThumbsDuplication
{
get
{
var config = _config.GetConfiguration<XbmcMetadataOptions>("xbmcmetadata");
return config.EnableExtraThumbsDuplication;
}
}
/// <summary>
/// Saves the image.
/// </summary>
@@ -63,7 +78,7 @@ namespace MediaBrowser.Providers.Manager
/// <param name="imageIndex">Index of the image.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Task.</returns>
/// <exception cref="ArgumentNullException">mimeType</exception>
/// <exception cref="ArgumentNullException">mimeType.</exception>
public Task SaveImage(BaseItem item, Stream source, string mimeType, ImageType type, int? imageIndex, CancellationToken cancellationToken)
{
return SaveImage(item, source, mimeType, type, imageIndex, null, cancellationToken);
@@ -78,11 +93,6 @@ namespace MediaBrowser.Providers.Manager
var saveLocally = item.SupportsLocalMetadata && item.IsSaveLocalMetadataEnabled() && !item.ExtraType.HasValue && !(item is Audio);
if (item is User)
{
saveLocally = true;
}
if (type != ImageType.Primary && item is Episode)
{
saveLocally = false;
@@ -105,6 +115,7 @@ namespace MediaBrowser.Providers.Manager
}
}
}
if (saveLocallyWithMedia.HasValue && !saveLocallyWithMedia.Value)
{
saveLocally = saveLocallyWithMedia.Value;
@@ -122,35 +133,40 @@ namespace MediaBrowser.Providers.Manager
var retryPaths = GetSavePaths(item, type, imageIndex, mimeType, false);
// If there are more than one output paths, the stream will need to be seekable
var memoryStream = new MemoryStream();
using (source)
if (paths.Length > 1 && !source.CanSeek)
{
await source.CopyToAsync(memoryStream).ConfigureAwait(false);
}
var memoryStream = new MemoryStream();
await using (source.ConfigureAwait(false))
{
await source.CopyToAsync(memoryStream).ConfigureAwait(false);
}
source = memoryStream;
source = memoryStream;
}
var currentImage = GetCurrentImage(item, type, index);
var currentImageIsLocalFile = currentImage != null && currentImage.IsLocalFile;
var currentImagePath = currentImage == null ? null : currentImage.Path;
var currentImagePath = currentImage?.Path;
var savedPaths = new List<string>();
using (source)
await using (source.ConfigureAwait(false))
{
var currentPathIndex = 0;
foreach (var path in paths)
for (int i = 0; i < paths.Length; i++)
{
source.Position = 0;
if (i != 0)
{
source.Position = 0;
}
string retryPath = null;
if (paths.Length == retryPaths.Length)
{
retryPath = retryPaths[currentPathIndex];
retryPath = retryPaths[i];
}
var savedPath = await SaveImageToLocation(source, path, retryPath, cancellationToken).ConfigureAwait(false);
var savedPath = await SaveImageToLocation(source, paths[i], retryPath, cancellationToken).ConfigureAwait(false);
savedPaths.Add(savedPath);
currentPathIndex++;
}
}
@@ -172,7 +188,6 @@ namespace MediaBrowser.Providers.Manager
}
catch (FileNotFoundException)
{
}
finally
{
@@ -181,6 +196,11 @@ namespace MediaBrowser.Providers.Manager
}
}
public async Task SaveImage(Stream source, string path)
{
await SaveImageToLocation(source, path, path, CancellationToken.None).ConfigureAwait(false);
}
private async Task<string> SaveImageToLocation(Stream source, string path, string retryPath, CancellationToken cancellationToken)
{
try
@@ -217,7 +237,6 @@ namespace MediaBrowser.Providers.Manager
}
}
source.Position = 0;
await SaveImageToLocation(source, retryPath, cancellationToken).ConfigureAwait(false);
return retryPath;
}
@@ -244,9 +263,9 @@ namespace MediaBrowser.Providers.Manager
_fileSystem.SetAttributes(path, false, false);
using (var fs = new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.Read, IODefaults.FileStreamBufferSize, FileOptions.Asynchronous))
await using (var fs = new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.Read, IODefaults.FileStreamBufferSize, FileOptions.Asynchronous))
{
await source.CopyToAsync(fs, IODefaults.CopyToBufferSize, cancellationToken).ConfigureAwait(false);
await source.CopyToAsync(fs, cancellationToken).ConfigureAwait(false);
}
if (_config.Configuration.SaveMetadataHidden)
@@ -302,7 +321,7 @@ namespace MediaBrowser.Providers.Manager
/// <exception cref="ArgumentNullException">
/// imageIndex
/// or
/// imageIndex
/// imageIndex.
/// </exception>
private ItemImageInfo GetCurrentImage(BaseItem item, ImageType type, int imageIndex)
{
@@ -318,7 +337,8 @@ namespace MediaBrowser.Providers.Manager
/// <param name="path">The path.</param>
/// <exception cref="ArgumentNullException">imageIndex
/// or
/// imageIndex</exception>
/// imageIndex.
/// </exception>
private void SetImagePath(BaseItem item, ImageType type, int? imageIndex, string path)
{
item.SetImagePath(type, imageIndex ?? 0, _fileSystem.GetFileInfo(path));
@@ -336,7 +356,7 @@ namespace MediaBrowser.Providers.Manager
/// <exception cref="ArgumentNullException">
/// imageIndex
/// or
/// imageIndex
/// imageIndex.
/// </exception>
private string GetStandardSavePath(BaseItem item, ImageType type, int? imageIndex, string mimeType, bool saveLocally)
{
@@ -345,7 +365,7 @@ namespace MediaBrowser.Providers.Manager
if (string.IsNullOrWhiteSpace(extension))
{
throw new ArgumentException(string.Format("Unable to determine image file extension from mime type {0}", mimeType));
throw new ArgumentException(string.Format(CultureInfo.InvariantCulture, "Unable to determine image file extension from mime type {0}", mimeType));
}
if (type == ImageType.Thumb && saveLocally)
@@ -439,7 +459,6 @@ namespace MediaBrowser.Providers.Manager
{
path = Path.Combine(Path.GetDirectoryName(item.Path), "metadata", filename + extension);
}
else if (item.IsInMixedFolder)
{
path = GetSavePathForItemInMixedFolder(item, type, filename, extension);
@@ -458,6 +477,7 @@ namespace MediaBrowser.Providers.Manager
{
filename = folderName;
}
path = Path.Combine(item.GetInternalMetadataPath(), filename + extension);
}
@@ -490,7 +510,7 @@ namespace MediaBrowser.Providers.Manager
/// <param name="imageIndex">Index of the image.</param>
/// <param name="mimeType">Type of the MIME.</param>
/// <returns>IEnumerable{System.String}.</returns>
/// <exception cref="ArgumentNullException">imageIndex</exception>
/// <exception cref="ArgumentNullException">imageIndex.</exception>
private string[] GetCompatibleSavePaths(BaseItem item, ImageType type, int? imageIndex, string mimeType)
{
var season = item as Season;
@@ -549,6 +569,7 @@ namespace MediaBrowser.Providers.Manager
{
list.Add(Path.Combine(item.ContainingFolderPath, "extrathumbs", "thumb" + outputIndex.ToString(UsCulture) + extension));
}
return list.ToArray();
}
@@ -593,16 +614,6 @@ namespace MediaBrowser.Providers.Manager
return new[] { GetStandardSavePath(item, type, imageIndex, mimeType, true) };
}
private bool EnableExtraThumbsDuplication
{
get
{
var config = _config.GetConfiguration<XbmcMetadataOptions>("xbmcmetadata");
return config.EnableExtraThumbsDuplication;
}
}
/// <summary>
/// Gets the save path for item in mixed folder.
/// </summary>
@@ -617,6 +628,7 @@ namespace MediaBrowser.Providers.Manager
{
imageFilename = "poster";
}
var folder = Path.GetDirectoryName(item.Path);
return Path.Combine(folder, Path.GetFileNameWithoutExtension(item.Path) + "-" + imageFilename + extension);

View File

@@ -1,3 +1,5 @@
#pragma warning disable CS1591
using System;
using System.Collections.Generic;
using System.IO;
@@ -26,6 +28,22 @@ namespace MediaBrowser.Providers.Manager
private readonly IProviderManager _providerManager;
private readonly IFileSystem _fileSystem;
/// <summary>
/// Image types that are only one per item.
/// </summary>
private readonly ImageType[] _singularImages =
{
ImageType.Primary,
ImageType.Art,
ImageType.Banner,
ImageType.Box,
ImageType.BoxRear,
ImageType.Disc,
ImageType.Logo,
ImageType.Menu,
ImageType.Thumb
};
public ItemImageProvider(ILogger logger, IProviderManager providerManager, IFileSystem fileSystem)
{
_logger = logger;
@@ -52,12 +70,18 @@ namespace MediaBrowser.Providers.Manager
return hasChanges;
}
public async Task<RefreshResult> RefreshImages(BaseItem item, LibraryOptions libraryOptions, List<IImageProvider> providers, ImageRefreshOptions refreshOptions, MetadataOptions savedOptions, CancellationToken cancellationToken)
public async Task<RefreshResult> RefreshImages(
BaseItem item,
LibraryOptions libraryOptions,
List<IImageProvider> providers,
ImageRefreshOptions refreshOptions,
CancellationToken cancellationToken)
{
if (refreshOptions.IsReplacingImage(ImageType.Backdrop))
{
ClearImages(item, ImageType.Backdrop);
}
if (refreshOptions.IsReplacingImage(ImageType.Screenshot))
{
ClearImages(item, ImageType.Screenshot);
@@ -75,19 +99,15 @@ namespace MediaBrowser.Providers.Manager
foreach (var provider in providers)
{
var remoteProvider = provider as IRemoteImageProvider;
if (remoteProvider != null)
if (provider is IRemoteImageProvider remoteProvider)
{
await RefreshFromProvider(item, libraryOptions, remoteProvider, refreshOptions, typeOptions, backdropLimit, screenshotLimit, downloadedImages, result, cancellationToken).ConfigureAwait(false);
continue;
}
var dynamicImageProvider = provider as IDynamicImageProvider;
if (dynamicImageProvider != null)
if (provider is IDynamicImageProvider dynamicImageProvider)
{
await RefreshFromProvider(item, dynamicImageProvider, refreshOptions, typeOptions, libraryOptions, downloadedImages, result, cancellationToken).ConfigureAwait(false);
await RefreshFromProvider(item, dynamicImageProvider, refreshOptions, typeOptions, downloadedImages, result, cancellationToken).ConfigureAwait(false);
}
}
@@ -97,11 +117,11 @@ namespace MediaBrowser.Providers.Manager
/// <summary>
/// Refreshes from provider.
/// </summary>
private async Task RefreshFromProvider(BaseItem item,
private async Task RefreshFromProvider(
BaseItem item,
IDynamicImageProvider provider,
ImageRefreshOptions refreshOptions,
TypeOptions savedOptions,
LibraryOptions libraryOptions,
ICollection<ImageType> downloadedImages,
RefreshResult result,
CancellationToken cancellationToken)
@@ -112,7 +132,10 @@ namespace MediaBrowser.Providers.Manager
foreach (var imageType in images)
{
if (!IsEnabled(savedOptions, imageType, item)) continue;
if (!IsEnabled(savedOptions, imageType))
{
continue;
}
if (!HasImage(item, imageType) || (refreshOptions.IsReplacingImage(imageType) && !downloadedImages.Contains(imageType)))
{
@@ -127,12 +150,13 @@ namespace MediaBrowser.Providers.Manager
if (response.Protocol == MediaProtocol.Http)
{
_logger.LogDebug("Setting image url into item {0}", item.Id);
item.SetImage(new ItemImageInfo
{
Path = response.Path,
Type = imageType
}, 0);
item.SetImage(
new ItemImageInfo
{
Path = response.Path,
Type = imageType
},
0);
}
else
{
@@ -151,7 +175,7 @@ namespace MediaBrowser.Providers.Manager
}
downloadedImages.Add(imageType);
result.UpdateType = result.UpdateType | ItemUpdateType.ImageUpdate;
result.UpdateType |= ItemUpdateType.ImageUpdate;
}
}
}
@@ -167,29 +191,13 @@ namespace MediaBrowser.Providers.Manager
}
}
/// <summary>
/// Image types that are only one per item
/// </summary>
private readonly ImageType[] _singularImages =
{
ImageType.Primary,
ImageType.Art,
ImageType.Banner,
ImageType.Box,
ImageType.BoxRear,
ImageType.Disc,
ImageType.Logo,
ImageType.Menu,
ImageType.Thumb
};
private bool HasImage(BaseItem item, ImageType type)
{
return item.HasImage(type);
}
/// <summary>
/// Determines if an item already contains the given images
/// Determines if an item already contains the given images.
/// </summary>
/// <param name="item">The item.</param>
/// <param name="images">The images.</param>
@@ -221,6 +229,7 @@ namespace MediaBrowser.Providers.Manager
/// Refreshes from provider.
/// </summary>
/// <param name="item">The item.</param>
/// <param name="libraryOptions">The library options.</param>
/// <param name="provider">The provider.</param>
/// <param name="refreshOptions">The refresh options.</param>
/// <param name="savedOptions">The saved options.</param>
@@ -230,7 +239,9 @@ namespace MediaBrowser.Providers.Manager
/// <param name="result">The result.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Task.</returns>
private async Task RefreshFromProvider(BaseItem item, LibraryOptions libraryOptions,
private async Task RefreshFromProvider(
BaseItem item,
LibraryOptions libraryOptions,
IRemoteImageProvider provider,
ImageRefreshOptions refreshOptions,
TypeOptions savedOptions,
@@ -256,20 +267,24 @@ namespace MediaBrowser.Providers.Manager
_logger.LogDebug("Running {0} for {1}", provider.GetType().Name, item.Path ?? item.Name);
var images = await _providerManager.GetAvailableRemoteImages(item, new RemoteImageQuery
{
ProviderName = provider.Name,
IncludeAllLanguages = false,
IncludeDisabledProviders = false,
}, cancellationToken).ConfigureAwait(false);
var images = await _providerManager.GetAvailableRemoteImages(
item,
new RemoteImageQuery(provider.Name)
{
IncludeAllLanguages = false,
IncludeDisabledProviders = false,
},
cancellationToken).ConfigureAwait(false);
var list = images.ToList();
int minWidth;
foreach (var imageType in _singularImages)
{
if (!IsEnabled(savedOptions, imageType, item)) continue;
if (!IsEnabled(savedOptions, imageType))
{
continue;
}
if (!HasImage(item, imageType) || (refreshOptions.IsReplacingImage(imageType) && !downloadedImages.Contains(imageType)))
{
@@ -286,8 +301,7 @@ namespace MediaBrowser.Providers.Manager
minWidth = savedOptions.GetMinWidth(ImageType.Backdrop);
await DownloadBackdrops(item, libraryOptions, ImageType.Backdrop, backdropLimit, provider, result, list, minWidth, cancellationToken).ConfigureAwait(false);
var hasScreenshots = item as IHasScreenshots;
if (hasScreenshots != null)
if (item is IHasScreenshots hasScreenshots)
{
minWidth = savedOptions.GetMinWidth(ImageType.Screenshot);
await DownloadBackdrops(item, libraryOptions, ImageType.Screenshot, screenshotLimit, provider, result, list, minWidth, cancellationToken).ConfigureAwait(false);
@@ -304,7 +318,7 @@ namespace MediaBrowser.Providers.Manager
}
}
private bool IsEnabled(TypeOptions options, ImageType type, BaseItem item)
private bool IsEnabled(TypeOptions options, ImageType type)
{
return options.IsEnabled(type);
}
@@ -329,7 +343,6 @@ namespace MediaBrowser.Providers.Manager
}
catch (FileNotFoundException)
{
}
}
@@ -365,7 +378,6 @@ namespace MediaBrowser.Providers.Manager
}
else
{
var newDateModified = _fileSystem.GetLastWriteTimeUtc(image.FileInfo);
// If date changed then we need to reset saved image dimensions
@@ -428,7 +440,9 @@ namespace MediaBrowser.Providers.Manager
return changed;
}
private async Task<bool> DownloadImage(BaseItem item, LibraryOptions libraryOptions,
private async Task<bool> DownloadImage(
BaseItem item,
LibraryOptions libraryOptions,
IRemoteImageProvider provider,
RefreshResult result,
IEnumerable<RemoteImageInfo> images,
@@ -440,10 +454,10 @@ namespace MediaBrowser.Providers.Manager
.Where(i => i.Type == type && !(i.Width.HasValue && i.Width.Value < minWidth))
.ToList();
if (EnableImageStub(item, type, libraryOptions) && eligibleImages.Count > 0)
if (EnableImageStub(item, libraryOptions) && eligibleImages.Count > 0)
{
SaveImageStub(item, type, eligibleImages.Select(i => i.Url));
result.UpdateType = result.UpdateType | ItemUpdateType.ImageUpdate;
result.UpdateType |= ItemUpdateType.ImageUpdate;
return true;
}
@@ -453,20 +467,29 @@ namespace MediaBrowser.Providers.Manager
try
{
var response = await provider.GetImageResponse(url, cancellationToken).ConfigureAwait(false);
using var response = await provider.GetImageResponse(url, cancellationToken).ConfigureAwait(false);
await using var stream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false);
await _providerManager.SaveImage(item, response.Content, response.ContentType, type, null, cancellationToken).ConfigureAwait(false);
await _providerManager.SaveImage(
item,
stream,
response.Content.Headers.ContentType.MediaType,
type,
null,
cancellationToken).ConfigureAwait(false);
result.UpdateType = result.UpdateType | ItemUpdateType.ImageUpdate;
result.UpdateType |= ItemUpdateType.ImageUpdate;
return true;
}
catch (HttpException ex)
{
// Sometimes providers send back bad url's. Just move to the next image
if (ex.StatusCode.HasValue && ex.StatusCode.Value == HttpStatusCode.NotFound)
if (ex.StatusCode.HasValue
&& (ex.StatusCode.Value == HttpStatusCode.NotFound || ex.StatusCode.Value == HttpStatusCode.Forbidden))
{
continue;
}
break;
}
}
@@ -474,7 +497,7 @@ namespace MediaBrowser.Providers.Manager
return false;
}
private bool EnableImageStub(BaseItem item, ImageType type, LibraryOptions libraryOptions)
private bool EnableImageStub(BaseItem item, LibraryOptions libraryOptions)
{
if (item is LiveTvProgram)
{
@@ -500,11 +523,6 @@ namespace MediaBrowser.Providers.Manager
return false;
}
//if (!item.IsSaveLocalMetadataEnabled())
//{
// return true;
//}
return true;
}
@@ -517,14 +535,15 @@ namespace MediaBrowser.Providers.Manager
private void SaveImageStub(BaseItem item, ImageType imageType, IEnumerable<string> urls, int newIndex)
{
var path = string.Join("|", urls.Take(1).ToArray());
var path = string.Join('|', urls.Take(1));
item.SetImage(new ItemImageInfo
{
Path = path,
Type = imageType
}, newIndex);
item.SetImage(
new ItemImageInfo
{
Path = path,
Type = imageType
},
newIndex);
}
private async Task DownloadBackdrops(BaseItem item, LibraryOptions libraryOptions, ImageType imageType, int limit, IRemoteImageProvider provider, RefreshResult result, IEnumerable<RemoteImageInfo> images, int minWidth, CancellationToken cancellationToken)
@@ -543,23 +562,23 @@ namespace MediaBrowser.Providers.Manager
var url = image.Url;
if (EnableImageStub(item, imageType, libraryOptions))
if (EnableImageStub(item, libraryOptions))
{
SaveImageStub(item, imageType, new[] { url });
result.UpdateType = result.UpdateType | ItemUpdateType.ImageUpdate;
result.UpdateType |= ItemUpdateType.ImageUpdate;
continue;
}
try
{
var response = await provider.GetImageResponse(url, cancellationToken).ConfigureAwait(false);
using var response = await provider.GetImageResponse(url, cancellationToken).ConfigureAwait(false);
// If there's already an image of the same size, skip it
if (response.ContentLength.HasValue)
if (response.Content.Headers.ContentLength.HasValue)
{
try
{
if (item.GetImages(imageType).Any(i => _fileSystem.GetFileInfo(i.Path).Length == response.ContentLength.Value))
if (item.GetImages(imageType).Any(i => _fileSystem.GetFileInfo(i.Path).Length == response.Content.Headers.ContentLength.Value))
{
response.Content.Dispose();
continue;
@@ -571,16 +590,25 @@ namespace MediaBrowser.Providers.Manager
}
}
await _providerManager.SaveImage(item, response.Content, response.ContentType, imageType, null, cancellationToken).ConfigureAwait(false);
await using var stream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false);
await _providerManager.SaveImage(
item,
stream,
response.Content.Headers.ContentType.MediaType,
imageType,
null,
cancellationToken).ConfigureAwait(false);
result.UpdateType = result.UpdateType | ItemUpdateType.ImageUpdate;
}
catch (HttpException ex)
{
// Sometimes providers send back bad urls. Just move onto the next image
if (ex.StatusCode.HasValue && ex.StatusCode.Value == HttpStatusCode.NotFound)
if (ex.StatusCode.HasValue
&& (ex.StatusCode.Value == HttpStatusCode.NotFound || ex.StatusCode.Value == HttpStatusCode.Forbidden))
{
continue;
}
break;
}
}

View File

@@ -1,3 +1,5 @@
#pragma warning disable CS1591
using System;
using System.Collections.Generic;
using System.Linq;
@@ -19,13 +21,7 @@ namespace MediaBrowser.Providers.Manager
where TItemType : BaseItem, IHasLookupInfo<TIdType>, new()
where TIdType : ItemLookupInfo, new()
{
protected readonly IServerConfigurationManager ServerConfigurationManager;
protected readonly ILogger Logger;
protected readonly IProviderManager ProviderManager;
protected readonly IFileSystem FileSystem;
protected readonly ILibraryManager LibraryManager;
protected MetadataService(IServerConfigurationManager serverConfigurationManager, ILogger logger, IProviderManager providerManager, IFileSystem fileSystem, ILibraryManager libraryManager)
protected MetadataService(IServerConfigurationManager serverConfigurationManager, ILogger<MetadataService<TItemType, TIdType>> logger, IProviderManager providerManager, IFileSystem fileSystem, ILibraryManager libraryManager)
{
ServerConfigurationManager = serverConfigurationManager;
Logger = logger;
@@ -34,6 +30,26 @@ namespace MediaBrowser.Providers.Manager
LibraryManager = libraryManager;
}
protected IServerConfigurationManager ServerConfigurationManager { get; }
protected ILogger<MetadataService<TItemType, TIdType>> Logger { get; }
protected IProviderManager ProviderManager { get; }
protected IFileSystem FileSystem { get; }
protected ILibraryManager LibraryManager { get; }
protected virtual bool EnableUpdatingPremiereDateFromChildren => false;
protected virtual bool EnableUpdatingGenresFromChildren => false;
protected virtual bool EnableUpdatingStudiosFromChildren => false;
protected virtual bool EnableUpdatingOfficialRatingFromChildren => false;
public virtual int Order => 0;
private FileSystemMetadata TryGetFile(string path, IDirectoryService directoryService)
{
try
@@ -50,7 +66,6 @@ namespace MediaBrowser.Providers.Manager
public async Task<ItemUpdateType> RefreshMetadata(BaseItem item, MetadataRefreshOptions refreshOptions, CancellationToken cancellationToken)
{
var itemOfType = (TItemType)item;
var config = ProviderManager.GetMetadataOptions(item);
var updateType = ItemUpdateType.None;
var requiresRefresh = false;
@@ -84,7 +99,7 @@ namespace MediaBrowser.Providers.Manager
// Always validate images and check for new locally stored ones.
if (itemImageProvider.ValidateImages(item, allImageProviders.OfType<ILocalImageProvider>(), refreshOptions.DirectoryService))
{
updateType = updateType | ItemUpdateType.ImageUpdate;
updateType |= ItemUpdateType.ImageUpdate;
}
}
catch (Exception ex)
@@ -100,7 +115,7 @@ namespace MediaBrowser.Providers.Manager
bool hasRefreshedMetadata = true;
bool hasRefreshedImages = true;
var isFirstRefresh = item.DateLastRefreshed == default(DateTime);
var isFirstRefresh = item.DateLastRefreshed == default;
// Next run metadata providers
if (refreshOptions.MetadataRefreshMode != MetadataRefreshMode.None)
@@ -112,7 +127,7 @@ namespace MediaBrowser.Providers.Manager
{
if (item.BeforeMetadataRefresh(refreshOptions.ReplaceAllMetadata))
{
updateType = updateType | ItemUpdateType.MetadataImport;
updateType |= ItemUpdateType.MetadataImport;
}
}
@@ -125,12 +140,12 @@ namespace MediaBrowser.Providers.Manager
ApplySearchResult(id, refreshOptions.SearchResult);
}
//await FindIdentities(id, cancellationToken).ConfigureAwait(false);
// await FindIdentities(id, cancellationToken).ConfigureAwait(false);
id.IsAutomated = refreshOptions.IsAutomated;
var result = await RefreshWithProviders(metadataResult, id, refreshOptions, providers, itemImageProvider, cancellationToken).ConfigureAwait(false);
updateType = updateType | result.UpdateType;
updateType |= result.UpdateType;
if (result.Failures > 0)
{
hasRefreshedMetadata = false;
@@ -145,9 +160,9 @@ namespace MediaBrowser.Providers.Manager
if (providers.Count > 0)
{
var result = await itemImageProvider.RefreshImages(itemOfType, libraryOptions, providers, refreshOptions, config, cancellationToken).ConfigureAwait(false);
var result = await itemImageProvider.RefreshImages(itemOfType, libraryOptions, providers, refreshOptions, cancellationToken).ConfigureAwait(false);
updateType = updateType | result.UpdateType;
updateType |= result.UpdateType;
if (result.Failures > 0)
{
hasRefreshedImages = false;
@@ -156,7 +171,7 @@ namespace MediaBrowser.Providers.Manager
}
var beforeSaveResult = BeforeSave(itemOfType, isFirstRefresh || refreshOptions.ReplaceAllMetadata || refreshOptions.MetadataRefreshMode == MetadataRefreshMode.FullRefresh || requiresRefresh || refreshOptions.ForceSave, updateType);
updateType = updateType | beforeSaveResult;
updateType |= beforeSaveResult;
// Save if changes were made, or it's never been saved before
if (refreshOptions.ForceSave || updateType > ItemUpdateType.None || isFirstRefresh || refreshOptions.ReplaceAllMetadata || requiresRefresh)
@@ -173,7 +188,7 @@ namespace MediaBrowser.Providers.Manager
// If any of these properties are set then make sure the updateType is not None, just to force everything to save
if (refreshOptions.ForceSave || refreshOptions.ReplaceAllMetadata)
{
updateType = updateType | ItemUpdateType.MetadataDownload;
updateType |= ItemUpdateType.MetadataDownload;
}
if (hasRefreshedMetadata && hasRefreshedImages)
@@ -182,11 +197,11 @@ namespace MediaBrowser.Providers.Manager
}
else
{
item.DateLastRefreshed = default(DateTime);
item.DateLastRefreshed = default;
}
// Save to database
SaveItem(metadataResult, libraryOptions, updateType, cancellationToken);
await SaveItemAsync(metadataResult, libraryOptions, updateType, cancellationToken).ConfigureAwait(false);
}
await AfterMetadataRefresh(itemOfType, refreshOptions, cancellationToken).ConfigureAwait(false);
@@ -201,25 +216,26 @@ namespace MediaBrowser.Providers.Manager
lookupInfo.Year = result.ProductionYear;
}
protected void SaveItem(MetadataResult<TItemType> result, LibraryOptions libraryOptions, ItemUpdateType reason, CancellationToken cancellationToken)
protected async Task SaveItemAsync(MetadataResult<TItemType> result, LibraryOptions libraryOptions, ItemUpdateType reason, CancellationToken cancellationToken)
{
if (result.Item.SupportsPeople && result.People != null)
{
var baseItem = result.Item;
LibraryManager.UpdatePeople(baseItem, result.People);
SavePeopleMetadata(result.People, libraryOptions, cancellationToken);
await SavePeopleMetadataAsync(result.People, libraryOptions, cancellationToken).ConfigureAwait(false);
}
result.Item.UpdateToRepository(reason, cancellationToken);
await result.Item.UpdateToRepositoryAsync(reason, cancellationToken).ConfigureAwait(false);
}
private void SavePeopleMetadata(List<PersonInfo> people, LibraryOptions libraryOptions, CancellationToken cancellationToken)
private async Task SavePeopleMetadataAsync(List<PersonInfo> people, LibraryOptions libraryOptions, CancellationToken cancellationToken)
{
foreach (var person in people)
{
cancellationToken.ThrowIfCancellationRequested();
if (person.ProviderIds.Any() || !string.IsNullOrWhiteSpace(person.ImageUrl))
if (person.ProviderIds.Count > 0 || !string.IsNullOrWhiteSpace(person.ImageUrl))
{
var updateType = ItemUpdateType.MetadataDownload;
@@ -236,40 +252,42 @@ namespace MediaBrowser.Providers.Manager
if (!string.IsNullOrWhiteSpace(person.ImageUrl) && !personEntity.HasImage(ImageType.Primary))
{
AddPersonImage(personEntity, libraryOptions, person.ImageUrl, cancellationToken);
await AddPersonImageAsync(personEntity, libraryOptions, person.ImageUrl, cancellationToken).ConfigureAwait(false);
saveEntity = true;
updateType = updateType | ItemUpdateType.ImageUpdate;
updateType |= ItemUpdateType.ImageUpdate;
}
if (saveEntity)
{
personEntity.UpdateToRepository(updateType, cancellationToken);
await personEntity.UpdateToRepositoryAsync(updateType, cancellationToken).ConfigureAwait(false);
}
}
}
}
private void AddPersonImage(Person personEntity, LibraryOptions libraryOptions, string imageUrl, CancellationToken cancellationToken)
private async Task AddPersonImageAsync(Person personEntity, LibraryOptions libraryOptions, string imageUrl, CancellationToken cancellationToken)
{
//if (libraryOptions.DownloadImagesInAdvance)
//{
// try
// {
// await ProviderManager.SaveImage(personEntity, imageUrl, ImageType.Primary, null, cancellationToken).ConfigureAwait(false);
// return;
// }
// catch (Exception ex)
// {
// Logger.LogError(ex, "Error in AddPersonImage");
// }
//}
personEntity.SetImage(new ItemImageInfo
if (libraryOptions.DownloadImagesInAdvance)
{
Path = imageUrl,
Type = ImageType.Primary
}, 0);
try
{
await ProviderManager.SaveImage(personEntity, imageUrl, ImageType.Primary, null, cancellationToken).ConfigureAwait(false);
return;
}
catch (Exception ex)
{
Logger.LogError(ex, "Error in AddPersonImage");
}
}
personEntity.SetImage(
new ItemImageInfo
{
Path = imageUrl,
Type = ImageType.Primary
},
0);
}
protected virtual Task AfterMetadataRefresh(TItemType item, MetadataRefreshOptions refreshOptions, CancellationToken cancellationToken)
@@ -324,6 +342,7 @@ namespace MediaBrowser.Providers.Manager
{
return true;
}
var folder = item as Folder;
if (folder != null)
{
@@ -389,7 +408,7 @@ namespace MediaBrowser.Providers.Manager
{
if (!child.IsFolder)
{
ticks += (child.RunTimeTicks ?? 0);
ticks += child.RunTimeTicks ?? 0;
}
}
@@ -422,6 +441,7 @@ namespace MediaBrowser.Providers.Manager
{
dateLastMediaAdded = childDateCreated;
}
any = true;
}
}
@@ -436,14 +456,6 @@ namespace MediaBrowser.Providers.Manager
return updateType;
}
protected virtual bool EnableUpdatingPremiereDateFromChildren => false;
protected virtual bool EnableUpdatingGenresFromChildren => false;
protected virtual bool EnableUpdatingStudiosFromChildren => false;
protected virtual bool EnableUpdatingOfficialRatingFromChildren => false;
private ItemUpdateType UpdatePremiereDate(TItemType item, IList<BaseItem> children)
{
var updateType = ItemUpdateType.None;
@@ -486,7 +498,7 @@ namespace MediaBrowser.Providers.Manager
{
var updateType = ItemUpdateType.None;
if (!item.LockedFields.Contains(MetadataFields.Genres))
if (!item.LockedFields.Contains(MetadataField.Genres))
{
var currentList = item.Genres;
@@ -507,7 +519,7 @@ namespace MediaBrowser.Providers.Manager
{
var updateType = ItemUpdateType.None;
if (!item.LockedFields.Contains(MetadataFields.Studios))
if (!item.LockedFields.Contains(MetadataField.Studios))
{
var currentList = item.Studios;
@@ -528,7 +540,7 @@ namespace MediaBrowser.Providers.Manager
{
var updateType = ItemUpdateType.None;
if (!item.LockedFields.Contains(MetadataFields.OfficialRating))
if (!item.LockedFields.Contains(MetadataField.OfficialRating))
{
if (item.UpdateRatingToItems(children))
{
@@ -652,7 +664,8 @@ namespace MediaBrowser.Providers.Manager
return type == typeof(TItemType);
}
protected virtual async Task<RefreshResult> RefreshWithProviders(MetadataResult<TItemType> metadata,
protected virtual async Task<RefreshResult> RefreshWithProviders(
MetadataResult<TItemType> metadata,
TIdType id,
MetadataRefreshOptions options,
List<IMetadataProvider> providers,
@@ -718,7 +731,7 @@ namespace MediaBrowser.Providers.Manager
userDataList.AddRange(localItem.UserDataList);
}
MergeData(localItem, temp, new MetadataFields[] { }, !options.ReplaceAllMetadata, true);
MergeData(localItem, temp, Array.Empty<MetadataField>(), !options.ReplaceAllMetadata, true);
refreshResult.UpdateType = refreshResult.UpdateType | ItemUpdateType.MetadataImport;
// Only one local provider allowed per item
@@ -726,6 +739,7 @@ namespace MediaBrowser.Providers.Manager
{
hasLocalMetadata = true;
}
break;
}
@@ -766,20 +780,20 @@ namespace MediaBrowser.Providers.Manager
else
{
// TODO: If the new metadata from above has some blank data, this can cause old data to get filled into those empty fields
MergeData(metadata, temp, new MetadataFields[] { }, false, false);
MergeData(metadata, temp, Array.Empty<MetadataField>(), false, false);
MergeData(temp, metadata, item.LockedFields, true, false);
}
}
}
//var isUnidentified = failedProviderCount > 0 && successfulProviderCount == 0;
// var isUnidentified = failedProviderCount > 0 && successfulProviderCount == 0;
foreach (var provider in customProviders.Where(i => !(i is IPreRefreshProvider)))
{
await RunCustomProvider(provider, item, logName, options, refreshResult, cancellationToken).ConfigureAwait(false);
}
//ImportUserData(item, userDataList, cancellationToken);
// ImportUserData(item, userDataList, cancellationToken);
return refreshResult;
}
@@ -843,7 +857,7 @@ namespace MediaBrowser.Providers.Manager
{
result.Provider = provider.Name;
MergeData(result, temp, new MetadataFields[] { }, false, false);
MergeData(result, temp, Array.Empty<MetadataField>(), false, false);
MergeNewData(temp.Item, id);
refreshResult.UpdateType = refreshResult.UpdateType | ItemUpdateType.MetadataDownload;
@@ -874,6 +888,7 @@ namespace MediaBrowser.Providers.Manager
{
return "en";
}
return language;
}
@@ -892,24 +907,23 @@ namespace MediaBrowser.Providers.Manager
}
}
protected abstract void MergeData(MetadataResult<TItemType> source,
protected abstract void MergeData(
MetadataResult<TItemType> source,
MetadataResult<TItemType> target,
MetadataFields[] lockedFields,
MetadataField[] lockedFields,
bool replaceData,
bool mergeMetadataSettings);
public virtual int Order => 0;
private bool HasChanged(BaseItem item, IHasItemChangeMonitor changeMonitor, IDirectoryService directoryService)
{
try
{
var hasChanged = changeMonitor.HasChanged(item, directoryService);
//if (hasChanged)
//{
// logger.LogDebug("{0} reports change to {1}", changeMonitor.GetType().Name, item.Path ?? item.Name);
//}
if (hasChanged)
{
Logger.LogDebug("{0} reports change to {1}", changeMonitor.GetType().Name, item.Path ?? item.Name);
}
return hasChanged;
}
@@ -920,11 +934,4 @@ namespace MediaBrowser.Providers.Manager
}
}
}
public class RefreshResult
{
public ItemUpdateType UpdateType { get; set; }
public string ErrorMessage { get; set; }
public int Failures { get; set; }
}
}

View File

@@ -1,13 +1,15 @@
#pragma warning disable CS1591
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Net.Mime;
using System.Threading;
using System.Threading.Tasks;
using Jellyfin.Data.Events;
using MediaBrowser.Common.Net;
using MediaBrowser.Common.Progress;
using MediaBrowser.Controller;
@@ -16,109 +18,118 @@ using MediaBrowser.Controller.Dto;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.Audio;
using MediaBrowser.Controller.Entities.Movies;
using MediaBrowser.Controller.Entities.TV;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Controller.Subtitles;
using MediaBrowser.Model.Configuration;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Events;
using MediaBrowser.Model.IO;
using MediaBrowser.Model.Net;
using MediaBrowser.Model.Providers;
using MediaBrowser.Model.Serialization;
using Microsoft.Extensions.Logging;
using Priority_Queue;
using Book = MediaBrowser.Controller.Entities.Book;
using Episode = MediaBrowser.Controller.Entities.TV.Episode;
using Movie = MediaBrowser.Controller.Entities.Movies.Movie;
using MusicAlbum = MediaBrowser.Controller.Entities.Audio.MusicAlbum;
using Season = MediaBrowser.Controller.Entities.TV.Season;
using Series = MediaBrowser.Controller.Entities.TV.Series;
namespace MediaBrowser.Providers.Manager
{
/// <summary>
/// Class ProviderManager
/// Class ProviderManager.
/// </summary>
public class ProviderManager : IProviderManager, IDisposable
{
private readonly ILogger _logger;
private readonly IHttpClient _httpClient;
private readonly object _refreshQueueLock = new object();
private readonly ILogger<ProviderManager> _logger;
private readonly IHttpClientFactory _httpClientFactory;
private readonly ILibraryMonitor _libraryMonitor;
private readonly IFileSystem _fileSystem;
private readonly IServerApplicationPaths _appPaths;
private readonly IJsonSerializer _json;
private readonly ILibraryManager _libraryManager;
private readonly ISubtitleManager _subtitleManager;
private readonly IServerConfigurationManager _configurationManager;
private readonly ConcurrentDictionary<Guid, double> _activeRefreshes = new ConcurrentDictionary<Guid, double>();
private readonly CancellationTokenSource _disposeCancellationTokenSource = new CancellationTokenSource();
private readonly SimplePriorityQueue<Tuple<Guid, MetadataRefreshOptions>> _refreshQueue =
new SimplePriorityQueue<Tuple<Guid, MetadataRefreshOptions>>();
private IImageProvider[] ImageProviders { get; set; }
private IMetadataService[] _metadataServices = { };
private IMetadataProvider[] _metadataProviders = { };
private IMetadataService[] _metadataServices = Array.Empty<IMetadataService>();
private IMetadataProvider[] _metadataProviders = Array.Empty<IMetadataProvider>();
private IEnumerable<IMetadataSaver> _savers;
private IExternalId[] _externalIds;
private CancellationTokenSource _disposeCancellationTokenSource = new CancellationTokenSource();
public event EventHandler<GenericEventArgs<BaseItem>> RefreshStarted;
public event EventHandler<GenericEventArgs<BaseItem>> RefreshCompleted;
public event EventHandler<GenericEventArgs<Tuple<BaseItem, double>>> RefreshProgress;
private bool _isProcessingRefreshQueue;
private bool _disposed;
/// <summary>
/// Initializes a new instance of the <see cref="ProviderManager" /> class.
/// Initializes a new instance of the <see cref="ProviderManager"/> class.
/// </summary>
/// <param name="httpClientFactory">The Http client factory.</param>
/// <param name="subtitleManager">The subtitle manager.</param>
/// <param name="configurationManager">The configuration manager.</param>
/// <param name="libraryMonitor">The library monitor.</param>
/// <param name="logger">The logger.</param>
/// <param name="fileSystem">The filesystem.</param>
/// <param name="appPaths">The server application paths.</param>
/// <param name="libraryManager">The library manager.</param>
public ProviderManager(
IHttpClient httpClient,
IHttpClientFactory httpClientFactory,
ISubtitleManager subtitleManager,
IServerConfigurationManager configurationManager,
ILibraryMonitor libraryMonitor,
ILogger<ProviderManager> logger,
IFileSystem fileSystem,
IServerApplicationPaths appPaths,
ILibraryManager libraryManager,
IJsonSerializer json)
ILibraryManager libraryManager)
{
_logger = logger;
_httpClient = httpClient;
_httpClientFactory = httpClientFactory;
_configurationManager = configurationManager;
_libraryMonitor = libraryMonitor;
_fileSystem = fileSystem;
_appPaths = appPaths;
_libraryManager = libraryManager;
_json = json;
_subtitleManager = subtitleManager;
}
/// <summary>
/// Adds the metadata providers.
/// </summary>
public void AddParts(IEnumerable<IImageProvider> imageProviders, IEnumerable<IMetadataService> metadataServices,
IEnumerable<IMetadataProvider> metadataProviders, IEnumerable<IMetadataSaver> metadataSavers,
IEnumerable<IExternalId> externalIds)
/// <inheritdoc/>
public event EventHandler<GenericEventArgs<BaseItem>> RefreshStarted;
/// <inheritdoc/>
public event EventHandler<GenericEventArgs<BaseItem>> RefreshCompleted;
/// <inheritdoc/>
public event EventHandler<GenericEventArgs<Tuple<BaseItem, double>>> RefreshProgress;
private IImageProvider[] ImageProviders { get; set; }
/// <inheritdoc/>
public void AddParts(
IEnumerable<IImageProvider> imageProviders,
IEnumerable<IMetadataService> metadataServices,
IEnumerable<IMetadataProvider> metadataProviders,
IEnumerable<IMetadataSaver> metadataSavers,
IEnumerable<IExternalId> externalIds)
{
ImageProviders = imageProviders.ToArray();
_metadataServices = metadataServices.OrderBy(i => i.Order).ToArray();
_metadataProviders = metadataProviders.ToArray();
_externalIds = externalIds.OrderBy(i => i.Name).ToArray();
_externalIds = externalIds.OrderBy(i => i.ProviderName).ToArray();
_savers = metadataSavers.Where(i =>
{
var configurable = i as IConfigurableProvider;
return configurable == null || configurable.IsEnabled;
}).ToArray();
_savers = metadataSavers
.Where(i => !(i is IConfigurableProvider configurable) || configurable.IsEnabled)
.ToArray();
}
/// <inheritdoc/>
public Task<ItemUpdateType> RefreshSingleItem(BaseItem item, MetadataRefreshOptions options, CancellationToken cancellationToken)
{
IMetadataService service = null;
var type = item.GetType();
foreach (var current in _metadataServices)
{
if (current.CanRefreshPrimary(type))
{
service = current;
break;
}
}
var service = _metadataServices.FirstOrDefault(current => current.CanRefreshPrimary(type));
if (service == null)
{
@@ -141,35 +152,50 @@ namespace MediaBrowser.Providers.Manager
return Task.FromResult(ItemUpdateType.None);
}
/// <inheritdoc/>
public async Task SaveImage(BaseItem item, string url, ImageType type, int? imageIndex, CancellationToken cancellationToken)
{
using (var response = await _httpClient.GetResponse(new HttpRequestOptions
{
CancellationToken = cancellationToken,
Url = url,
BufferContent = false
var httpClient = _httpClientFactory.CreateClient(NamedClient.Default);
using var response = await httpClient.GetAsync(url, cancellationToken).ConfigureAwait(false);
}).ConfigureAwait(false))
var contentType = response.Content.Headers.ContentType.MediaType;
// Workaround for tvheadend channel icons
// TODO: Isolate this hack into the tvh plugin
if (string.IsNullOrEmpty(contentType))
{
// Workaround for tvheadend channel icons
// TODO: Isolate this hack into the tvh plugin
if (string.IsNullOrEmpty(response.ContentType))
if (url.IndexOf("/imagecache/", StringComparison.OrdinalIgnoreCase) != -1)
{
if (url.IndexOf("/imagecache/", StringComparison.OrdinalIgnoreCase) != -1)
{
response.ContentType = "image/png";
}
contentType = "image/png";
}
await SaveImage(item, response.Content, response.ContentType, type, imageIndex, cancellationToken).ConfigureAwait(false);
}
// thetvdb will sometimes serve a rubbish 404 html page with a 200 OK code, because reasons...
if (contentType.Equals(MediaTypeNames.Text.Html, StringComparison.OrdinalIgnoreCase))
{
throw new HttpException("Invalid image received.")
{
StatusCode = HttpStatusCode.NotFound
};
}
await using var stream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false);
await SaveImage(
item,
stream,
contentType,
type,
imageIndex,
cancellationToken).ConfigureAwait(false);
}
/// <inheritdoc/>
public Task SaveImage(BaseItem item, Stream source, string mimeType, ImageType type, int? imageIndex, CancellationToken cancellationToken)
{
return new ImageSaver(_configurationManager, _libraryMonitor, _fileSystem, _logger).SaveImage(item, source, mimeType, type, imageIndex, cancellationToken);
}
/// <inheritdoc/>
public Task SaveImage(BaseItem item, string source, string mimeType, ImageType type, int? imageIndex, bool? saveLocallyWithMedia, CancellationToken cancellationToken)
{
if (string.IsNullOrWhiteSpace(source))
@@ -182,6 +208,14 @@ namespace MediaBrowser.Providers.Manager
return new ImageSaver(_configurationManager, _libraryMonitor, _fileSystem, _logger).SaveImage(item, fileStream, mimeType, type, imageIndex, saveLocallyWithMedia, cancellationToken);
}
/// <inheritdoc/>
public Task SaveImage(Stream source, string mimeType, string path)
{
return new ImageSaver(_configurationManager, _libraryMonitor, _fileSystem, _logger)
.SaveImage(source, path);
}
/// <inheritdoc/>
public async Task<IEnumerable<RemoteImageInfo>> GetAvailableRemoteImages(BaseItem item, RemoteImageQuery query, CancellationToken cancellationToken)
{
var providers = GetRemoteImageProviders(item, query.IncludeDisabledProviders);
@@ -201,7 +235,7 @@ namespace MediaBrowser.Providers.Manager
languages.Add(preferredLanguage);
}
var tasks = providers.Select(i => GetImages(item, cancellationToken, i, languages, query.ImageType));
var tasks = providers.Select(i => GetImages(item, i, languages, cancellationToken, query.ImageType));
var results = await Task.WhenAll(tasks).ConfigureAwait(false);
@@ -212,12 +246,17 @@ namespace MediaBrowser.Providers.Manager
/// Gets the images.
/// </summary>
/// <param name="item">The item.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <param name="provider">The provider.</param>
/// <param name="preferredLanguages">The preferred languages.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <param name="type">The type.</param>
/// <returns>Task{IEnumerable{RemoteImageInfo}}.</returns>
private async Task<IEnumerable<RemoteImageInfo>> GetImages(BaseItem item, CancellationToken cancellationToken, IRemoteImageProvider provider, List<string> preferredLanguages, ImageType? type = null)
private async Task<IEnumerable<RemoteImageInfo>> GetImages(
BaseItem item,
IRemoteImageProvider provider,
IReadOnlyCollection<string> preferredLanguages,
CancellationToken cancellationToken,
ImageType? type = null)
{
try
{
@@ -243,25 +282,23 @@ namespace MediaBrowser.Providers.Manager
}
catch (Exception ex)
{
_logger.LogError(ex, "{0} failed in GetImageInfos for type {1}", provider.GetType().Name, item.GetType().Name);
_logger.LogError(ex, "{ProviderName} failed in GetImageInfos for type {ItemType} at {ItemPath}", provider.GetType().Name, item.GetType().Name, item.Path);
return new List<RemoteImageInfo>();
}
}
/// <summary>
/// Gets the supported image providers.
/// </summary>
/// <param name="item">The item.</param>
/// <returns>IEnumerable{IImageProvider}.</returns>
/// <inheritdoc/>
public IEnumerable<ImageProviderInfo> GetRemoteImageProviderInfo(BaseItem item)
{
return GetRemoteImageProviders(item, true).Select(i => new ImageProviderInfo
{
Name = i.Name,
SupportedImages = i.GetSupportedImages(item).ToArray()
});
return GetRemoteImageProviders(item, true).Select(i => new ImageProviderInfo(i.Name, i.GetSupportedImages(item).ToArray()));
}
/// <summary>
/// Gets the image providers for the provided item.
/// </summary>
/// <param name="item">The item.</param>
/// <param name="refreshOptions">The image refresh options.</param>
/// <returns>The image providers for the item.</returns>
public IEnumerable<IImageProvider> GetImageProviders(BaseItem item, ImageRefreshOptions refreshOptions)
{
return GetImageProviders(item, _libraryManager.GetLibraryOptions(item), GetMetadataOptions(item), refreshOptions, false);
@@ -275,7 +312,7 @@ namespace MediaBrowser.Providers.Manager
var typeOptions = libraryOptions.GetTypeOptions(item.GetType().Name);
var typeFetcherOrder = typeOptions?.ImageFetcherOrder;
return ImageProviders.Where(i => CanRefresh(i, item, libraryOptions, options, refreshOptions, includeDisabled))
return ImageProviders.Where(i => CanRefresh(i, item, libraryOptions, refreshOptions, includeDisabled))
.OrderBy(i =>
{
// See if there's a user-defined order
@@ -296,6 +333,13 @@ namespace MediaBrowser.Providers.Manager
.ThenBy(GetOrder);
}
/// <summary>
/// Gets the metadata providers for the provided item.
/// </summary>
/// <param name="item">The item.</param>
/// <param name="libraryOptions">The library options.</param>
/// <typeparam name="T">The type of metadata provider.</typeparam>
/// <returns>The metadata providers.</returns>
public IEnumerable<IMetadataProvider<T>> GetMetadataProviders<T>(BaseItem item, LibraryOptions libraryOptions)
where T : BaseItem
{
@@ -311,7 +355,7 @@ namespace MediaBrowser.Providers.Manager
var currentOptions = globalMetadataOptions;
return _metadataProviders.OfType<IMetadataProvider<T>>()
.Where(i => CanRefresh(i, item, libraryOptions, currentOptions, includeDisabled, forceEnableInternetMetadata))
.Where(i => CanRefresh(i, item, libraryOptions, includeDisabled, forceEnableInternetMetadata))
.OrderBy(i => GetConfiguredOrder(item, i, libraryOptions, globalMetadataOptions))
.ThenBy(GetDefaultOrder);
}
@@ -321,14 +365,20 @@ namespace MediaBrowser.Providers.Manager
var options = GetMetadataOptions(item);
var libraryOptions = _libraryManager.GetLibraryOptions(item);
return GetImageProviders(item, libraryOptions, options,
new ImageRefreshOptions(
new DirectoryService(_fileSystem)),
includeDisabled)
.OfType<IRemoteImageProvider>();
return GetImageProviders(
item,
libraryOptions,
options,
new ImageRefreshOptions(new DirectoryService(_fileSystem)),
includeDisabled).OfType<IRemoteImageProvider>();
}
private bool CanRefresh(IMetadataProvider provider, BaseItem item, LibraryOptions libraryOptions, MetadataOptions options, bool includeDisabled, bool forceEnableInternetMetadata)
private bool CanRefresh(
IMetadataProvider provider,
BaseItem item,
LibraryOptions libraryOptions,
bool includeDisabled,
bool forceEnableInternetMetadata)
{
if (!includeDisabled)
{
@@ -364,7 +414,12 @@ namespace MediaBrowser.Providers.Manager
return true;
}
private bool CanRefresh(IImageProvider provider, BaseItem item, LibraryOptions libraryOptions, MetadataOptions options, ImageRefreshOptions refreshOptions, bool includeDisabled)
private bool CanRefresh(
IImageProvider provider,
BaseItem item,
LibraryOptions libraryOptions,
ImageRefreshOptions refreshOptions,
bool includeDisabled)
{
if (!includeDisabled)
{
@@ -392,7 +447,7 @@ namespace MediaBrowser.Providers.Manager
}
catch (Exception ex)
{
_logger.LogError(ex, "{0} failed in Supports for type {1}", provider.GetType().Name, item.GetType().Name);
_logger.LogError(ex, "{ProviderName} failed in Supports for type {ItemType} at {ItemPath}", provider.GetType().Name, item.GetType().Name, item.Path);
return false;
}
}
@@ -404,9 +459,7 @@ namespace MediaBrowser.Providers.Manager
/// <returns>System.Int32.</returns>
private int GetOrder(IImageProvider provider)
{
var hasOrder = provider as IHasOrder;
if (hasOrder == null)
if (!(provider is IHasOrder hasOrder))
{
return 0;
}
@@ -433,7 +486,7 @@ namespace MediaBrowser.Providers.Manager
if (provider is IRemoteMetadataProvider)
{
var typeOptions = libraryOptions.GetTypeOptions(item.GetType().Name);
var typeFetcherOrder = typeOptions == null ? null : typeOptions.MetadataFetcherOrder;
var typeFetcherOrder = typeOptions?.MetadataFetcherOrder;
var fetcherOrder = typeFetcherOrder ?? globalMetadataOptions.MetadataFetcherOrder;
@@ -451,9 +504,7 @@ namespace MediaBrowser.Providers.Manager
private int GetDefaultOrder(IMetadataProvider provider)
{
var hasOrder = provider as IHasOrder;
if (hasOrder != null)
if (provider is IHasOrder hasOrder)
{
return hasOrder.Order;
}
@@ -461,9 +512,10 @@ namespace MediaBrowser.Providers.Manager
return 0;
}
/// <inheritdoc/>
public MetadataPluginSummary[] GetAllMetadataPlugins()
{
return new MetadataPluginSummary[]
return new[]
{
GetPluginSummary<Movie>(),
GetPluginSummary<BoxSet>(),
@@ -485,7 +537,7 @@ namespace MediaBrowser.Providers.Manager
where T : BaseItem, new()
{
// Give it a dummy path just so that it looks like a file system item
var dummy = new T()
var dummy = new T
{
Path = Path.Combine(_appPaths.InternalMetadataPath, "dummy"),
ParentId = Guid.NewGuid()
@@ -500,16 +552,17 @@ namespace MediaBrowser.Providers.Manager
var libraryOptions = new LibraryOptions();
var imageProviders = GetImageProviders(dummy, libraryOptions, options,
new ImageRefreshOptions(
new DirectoryService(_fileSystem)),
true)
.ToList();
var imageProviders = GetImageProviders(
dummy,
libraryOptions,
options,
new ImageRefreshOptions(new DirectoryService(_fileSystem)),
true).ToList();
var pluginList = summary.Plugins.ToList();
AddMetadataPlugins(pluginList, dummy, libraryOptions, options);
AddImagePlugins(pluginList, dummy, imageProviders);
AddImagePlugins(pluginList, imageProviders);
var subtitleProviders = _subtitleManager.GetSupportedProviders(dummy);
@@ -540,14 +593,14 @@ namespace MediaBrowser.Providers.Manager
var providers = GetMetadataProvidersInternal<T>(item, libraryOptions, options, true, true).ToList();
// Locals
list.AddRange(providers.Where(i => (i is ILocalMetadataProvider)).Select(i => new MetadataPlugin
list.AddRange(providers.Where(i => i is ILocalMetadataProvider).Select(i => new MetadataPlugin
{
Name = i.Name,
Type = MetadataPluginType.LocalMetadataProvider
}));
// Fetchers
list.AddRange(providers.Where(i => (i is IRemoteMetadataProvider)).Select(i => new MetadataPlugin
list.AddRange(providers.Where(i => i is IRemoteMetadataProvider).Select(i => new MetadataPlugin
{
Name = i.Name,
Type = MetadataPluginType.MetadataFetcher
@@ -561,12 +614,10 @@ namespace MediaBrowser.Providers.Manager
}));
}
private void AddImagePlugins<T>(List<MetadataPlugin> list, T item, List<IImageProvider> imageProviders)
where T : BaseItem
private void AddImagePlugins(List<MetadataPlugin> list, List<IImageProvider> imageProviders)
{
// Locals
list.AddRange(imageProviders.Where(i => (i is ILocalImageProvider)).Select(i => new MetadataPlugin
list.AddRange(imageProviders.Where(i => i is ILocalImageProvider).Select(i => new MetadataPlugin
{
Name = i.Name,
Type = MetadataPluginType.LocalImageProvider
@@ -580,6 +631,7 @@ namespace MediaBrowser.Providers.Manager
}));
}
/// <inheritdoc/>
public MetadataOptions GetMetadataOptions(BaseItem item)
{
var type = item.GetType().Name;
@@ -589,17 +641,13 @@ namespace MediaBrowser.Providers.Manager
new MetadataOptions();
}
/// <summary>
/// Saves the metadata.
/// </summary>
/// <inheritdoc/>
public void SaveMetadata(BaseItem item, ItemUpdateType updateType)
{
SaveMetadata(item, updateType, _savers);
}
/// <summary>
/// Saves the metadata.
/// </summary>
/// <inheritdoc/>
public void SaveMetadata(BaseItem item, ItemUpdateType updateType, IEnumerable<string> savers)
{
SaveMetadata(item, updateType, _savers.Where(i => savers.Contains(i.Name, StringComparer.OrdinalIgnoreCase)));
@@ -611,7 +659,6 @@ namespace MediaBrowser.Providers.Manager
/// <param name="item">The item.</param>
/// <param name="updateType">Type of the update.</param>
/// <param name="savers">The savers.</param>
/// <returns>Task.</returns>
private void SaveMetadata(BaseItem item, ItemUpdateType updateType, IEnumerable<IMetadataSaver> savers)
{
var libraryOptions = _libraryManager.GetLibraryOptions(item);
@@ -620,11 +667,9 @@ namespace MediaBrowser.Providers.Manager
{
_logger.LogDebug("Saving {0} to {1}.", item.Path ?? item.Name, saver.Name);
var fileSaver = saver as IMetadataFileSaver;
if (fileSaver != null)
if (saver is IMetadataFileSaver fileSaver)
{
string path = null;
string path;
try
{
@@ -691,11 +736,9 @@ namespace MediaBrowser.Providers.Manager
{
if (updateType >= ItemUpdateType.MetadataEdit)
{
var fileSaver = saver as IMetadataFileSaver;
// Manual edit occurred
// Even if save local is off, save locally anyway if the metadata file already exists
if (fileSaver == null || !File.Exists(fileSaver.GetSavePath(item)))
if (!(saver is IMetadataFileSaver fileSaver) || !File.Exists(fileSaver.GetSavePath(item)))
{
return false;
}
@@ -726,6 +769,7 @@ namespace MediaBrowser.Providers.Manager
}
}
/// <inheritdoc/>
public Task<IEnumerable<RemoteSearchResult>> GetRemoteSearchResults<TItemType, TLookupType>(RemoteSearchQuery<TLookupType> searchInfo, CancellationToken cancellationToken)
where TItemType : BaseItem, new()
where TLookupType : ItemLookupInfo
@@ -740,7 +784,7 @@ namespace MediaBrowser.Providers.Manager
return GetRemoteSearchResults<TItemType, TLookupType>(searchInfo, referenceItem, cancellationToken);
}
public async Task<IEnumerable<RemoteSearchResult>> GetRemoteSearchResults<TItemType, TLookupType>(RemoteSearchQuery<TLookupType> searchInfo, BaseItem referenceItem, CancellationToken cancellationToken)
private async Task<IEnumerable<RemoteSearchResult>> GetRemoteSearchResults<TItemType, TLookupType>(RemoteSearchQuery<TLookupType> searchInfo, BaseItem referenceItem, CancellationToken cancellationToken)
where TItemType : BaseItem, new()
where TLookupType : ItemLookupInfo
{
@@ -779,6 +823,7 @@ namespace MediaBrowser.Providers.Manager
{
searchInfo.SearchInfo.MetadataLanguage = _configurationManager.Configuration.PreferredMetadataLanguage;
}
if (string.IsNullOrWhiteSpace(searchInfo.SearchInfo.MetadataCountryCode))
{
searchInfo.SearchInfo.MetadataCountryCode = _configurationManager.Configuration.MetadataCountryCode;
@@ -823,12 +868,14 @@ namespace MediaBrowser.Providers.Manager
}
}
//_logger.LogDebug("Returning search results {0}", _json.SerializeToString(resultList));
// _logger.LogDebug("Returning search results {0}", _json.SerializeToString(resultList));
return resultList;
}
private async Task<IEnumerable<RemoteSearchResult>> GetSearchResults<TLookupType>(IRemoteSearchProvider<TLookupType> provider, TLookupType searchInfo,
private async Task<IEnumerable<RemoteSearchResult>> GetSearchResults<TLookupType>(
IRemoteSearchProvider<TLookupType> provider,
TLookupType searchInfo,
CancellationToken cancellationToken)
where TLookupType : ItemLookupInfo
{
@@ -844,7 +891,8 @@ namespace MediaBrowser.Providers.Manager
return list;
}
public Task<HttpResponseInfo> GetSearchImage(string providerName, string url, CancellationToken cancellationToken)
/// <inheritdoc/>
public Task<HttpResponseMessage> GetSearchImage(string providerName, string url, CancellationToken cancellationToken)
{
var provider = _metadataProviders.OfType<IRemoteSearchProvider>().FirstOrDefault(i => string.Equals(i.Name, providerName, StringComparison.OrdinalIgnoreCase));
@@ -856,7 +904,7 @@ namespace MediaBrowser.Providers.Manager
return provider.GetImageResponse(url, cancellationToken);
}
public IEnumerable<IExternalId> GetExternalIds(IHasProviderIds item)
private IEnumerable<IExternalId> GetExternalIds(IHasProviderIds item)
{
return _externalIds.Where(i =>
{
@@ -872,6 +920,7 @@ namespace MediaBrowser.Providers.Manager
});
}
/// <inheritdoc/>
public IEnumerable<ExternalUrl> GetExternalUrls(BaseItem item)
{
return GetExternalIds(item)
@@ -891,29 +940,29 @@ namespace MediaBrowser.Providers.Manager
return new ExternalUrl
{
Name = i.Name,
Name = i.ProviderName,
Url = string.Format(
CultureInfo.InvariantCulture,
i.UrlFormatString,
value)
};
}).Where(i => i != null).Concat(item.GetRelatedUrls());
}
/// <inheritdoc/>
public IEnumerable<ExternalIdInfo> GetExternalIdInfos(IHasProviderIds item)
{
return GetExternalIds(item)
.Select(i => new ExternalIdInfo
{
Name = i.Name,
Name = i.ProviderName,
Key = i.Key,
Type = i.Type,
UrlFormatString = i.UrlFormatString
});
}
private ConcurrentDictionary<Guid, double> _activeRefreshes = new ConcurrentDictionary<Guid, double>();
/// <inheritdoc/>
public Dictionary<Guid, Guid> GetRefreshQueue()
{
lock (_refreshQueueLock)
@@ -929,22 +978,25 @@ namespace MediaBrowser.Providers.Manager
}
}
/// <inheritdoc/>
public void OnRefreshStart(BaseItem item)
{
_logger.LogInformation("OnRefreshStart {0}", item.Id.ToString("N", CultureInfo.InvariantCulture));
_logger.LogDebug("OnRefreshStart {0}", item.Id.ToString("N", CultureInfo.InvariantCulture));
_activeRefreshes[item.Id] = 0;
RefreshStarted?.Invoke(this, new GenericEventArgs<BaseItem>(item));
}
/// <inheritdoc/>
public void OnRefreshComplete(BaseItem item)
{
_logger.LogInformation("OnRefreshComplete {0}", item.Id.ToString("N", CultureInfo.InvariantCulture));
_logger.LogDebug("OnRefreshComplete {0}", item.Id.ToString("N", CultureInfo.InvariantCulture));
_activeRefreshes.Remove(item.Id, out _);
RefreshCompleted?.Invoke(this, new GenericEventArgs<BaseItem>(item));
}
/// <inheritdoc/>
public double? GetRefreshProgress(Guid id)
{
if (_activeRefreshes.TryGetValue(id, out double value))
@@ -955,6 +1007,7 @@ namespace MediaBrowser.Providers.Manager
return null;
}
/// <inheritdoc/>
public void OnRefreshProgress(BaseItem item, double progress)
{
var id = item.Id;
@@ -974,12 +1027,7 @@ namespace MediaBrowser.Providers.Manager
RefreshProgress?.Invoke(this, new GenericEventArgs<Tuple<BaseItem, double>>(new Tuple<BaseItem, double>(item, progress)));
}
private readonly SimplePriorityQueue<Tuple<Guid, MetadataRefreshOptions>> _refreshQueue =
new SimplePriorityQueue<Tuple<Guid, MetadataRefreshOptions>>();
private readonly object _refreshQueueLock = new object();
private bool _isProcessingRefreshQueue;
/// <inheritdoc/>
public void QueueRefresh(Guid id, MetadataRefreshOptions options, RefreshPriority priority)
{
if (_disposed)
@@ -1023,7 +1071,7 @@ namespace MediaBrowser.Providers.Manager
if (item != null)
{
// Try to throttle this a little bit.
await Task.Delay(100).ConfigureAwait(false);
await Task.Delay(100, cancellationToken).ConfigureAwait(false);
var task = item is MusicArtist artist
? RefreshArtist(artist, refreshItem.Item2, cancellationToken)
@@ -1053,17 +1101,14 @@ namespace MediaBrowser.Providers.Manager
await item.RefreshMetadata(options, cancellationToken).ConfigureAwait(false);
// Collection folders don't validate their children so we'll have to simulate that here
if (item is CollectionFolder collectionFolder)
switch (item)
{
await RefreshCollectionFolderChildren(options, collectionFolder, cancellationToken).ConfigureAwait(false);
}
else
{
if (item is Folder folder)
{
case CollectionFolder collectionFolder:
await RefreshCollectionFolderChildren(options, collectionFolder, cancellationToken).ConfigureAwait(false);
break;
case Folder folder:
await folder.ValidateChildren(new SimpleProgress<double>(), cancellationToken, options).ConfigureAwait(false);
}
break;
}
}
@@ -1073,7 +1118,7 @@ namespace MediaBrowser.Providers.Manager
{
await child.RefreshMetadata(options, cancellationToken).ConfigureAwait(false);
await child.ValidateChildren(new SimpleProgress<double>(), cancellationToken, options, true).ConfigureAwait(false);
await child.ValidateChildren(new SimpleProgress<double>(), cancellationToken, options).ConfigureAwait(false);
}
}
@@ -1109,20 +1154,41 @@ namespace MediaBrowser.Providers.Manager
}
}
/// <inheritdoc/>
public Task RefreshFullItem(BaseItem item, MetadataRefreshOptions options, CancellationToken cancellationToken)
{
return RefreshItem(item, options, cancellationToken);
}
private bool _disposed;
/// <inheritdoc/>
public void Dispose()
{
_disposed = true;
Dispose(true);
GC.SuppressFinalize(this);
}
/// <summary>
/// Releases unmanaged and optionally managed resources.
/// </summary>
/// <param name="disposing"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param>
protected virtual void Dispose(bool disposing)
{
if (_disposed)
{
return;
}
if (!_disposeCancellationTokenSource.IsCancellationRequested)
{
_disposeCancellationTokenSource.Cancel();
}
if (disposing)
{
_disposeCancellationTokenSource.Dispose();
}
_disposed = true;
}
}
}

View File

@@ -1,3 +1,5 @@
#pragma warning disable CS1591
using System;
using System.Collections.Generic;
using System.Linq;
@@ -14,7 +16,7 @@ namespace MediaBrowser.Providers.Manager
public static void MergeBaseItemData<T>(
MetadataResult<T> sourceResult,
MetadataResult<T> targetResult,
MetadataFields[] lockedFields,
MetadataField[] lockedFields,
bool replaceData,
bool mergeMetadataSettings)
where T : BaseItem
@@ -24,14 +26,15 @@ namespace MediaBrowser.Providers.Manager
if (source == null)
{
throw new ArgumentNullException(nameof(source));
}
if (target == null)
{
throw new ArgumentNullException(nameof(target));
throw new ArgumentException("Item cannot be null.", nameof(sourceResult));
}
if (!lockedFields.Contains(MetadataFields.Name))
if (target == null)
{
throw new ArgumentException("Item cannot be null.", nameof(targetResult));
}
if (!lockedFields.Contains(MetadataField.Name))
{
if (replaceData || string.IsNullOrEmpty(target.Name))
{
@@ -62,7 +65,7 @@ namespace MediaBrowser.Providers.Manager
target.EndDate = source.EndDate;
}
if (!lockedFields.Contains(MetadataFields.Genres))
if (!lockedFields.Contains(MetadataField.Genres))
{
if (replaceData || target.Genres.Length == 0)
{
@@ -75,7 +78,7 @@ namespace MediaBrowser.Providers.Manager
target.IndexNumber = source.IndexNumber;
}
if (!lockedFields.Contains(MetadataFields.OfficialRating))
if (!lockedFields.Contains(MetadataField.OfficialRating))
{
if (replaceData || string.IsNullOrEmpty(target.OfficialRating))
{
@@ -93,7 +96,7 @@ namespace MediaBrowser.Providers.Manager
target.Tagline = source.Tagline;
}
if (!lockedFields.Contains(MetadataFields.Overview))
if (!lockedFields.Contains(MetadataField.Overview))
{
if (replaceData || string.IsNullOrEmpty(target.Overview))
{
@@ -106,12 +109,11 @@ namespace MediaBrowser.Providers.Manager
target.ParentIndexNumber = source.ParentIndexNumber;
}
if (!lockedFields.Contains(MetadataFields.Cast))
if (!lockedFields.Contains(MetadataField.Cast))
{
if (replaceData || targetResult.People == null || targetResult.People.Count == 0)
{
targetResult.People = sourceResult.People;
}
else if (targetResult.People != null && sourceResult.People != null)
{
@@ -129,7 +131,7 @@ namespace MediaBrowser.Providers.Manager
target.ProductionYear = source.ProductionYear;
}
if (!lockedFields.Contains(MetadataFields.Runtime))
if (!lockedFields.Contains(MetadataField.Runtime))
{
if (replaceData || !target.RunTimeTicks.HasValue)
{
@@ -140,7 +142,7 @@ namespace MediaBrowser.Providers.Manager
}
}
if (!lockedFields.Contains(MetadataFields.Studios))
if (!lockedFields.Contains(MetadataField.Studios))
{
if (replaceData || target.Studios.Length == 0)
{
@@ -148,7 +150,7 @@ namespace MediaBrowser.Providers.Manager
}
}
if (!lockedFields.Contains(MetadataFields.Tags))
if (!lockedFields.Contains(MetadataField.Tags))
{
if (replaceData || target.Tags.Length == 0)
{
@@ -156,7 +158,7 @@ namespace MediaBrowser.Providers.Manager
}
}
if (!lockedFields.Contains(MetadataFields.ProductionLocations))
if (!lockedFields.Contains(MetadataField.ProductionLocations))
{
if (replaceData || target.ProductionLocations.Length == 0)
{

View File

@@ -0,0 +1,15 @@
#pragma warning disable CS1591
using MediaBrowser.Controller.Library;
namespace MediaBrowser.Providers.Manager
{
public class RefreshResult
{
public ItemUpdateType UpdateType { get; set; }
public string ErrorMessage { get; set; }
public int Failures { get; set; }
}
}

View File

@@ -16,17 +16,19 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="3.1.3" />
<PackageReference Include="Microsoft.Extensions.Caching.Abstractions" Version="3.1.3" />
<PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="3.1.7" />
<PackageReference Include="Microsoft.Extensions.Caching.Abstractions" Version="3.1.7" />
<PackageReference Include="Microsoft.Extensions.Http" Version="3.1.7" />
<PackageReference Include="OptimizedPriorityQueue" Version="4.2.0" />
<PackageReference Include="PlaylistsNET" Version="1.0.4" />
<PackageReference Include="TvDbSharper" Version="3.0.1" />
<PackageReference Include="PlaylistsNET" Version="1.1.2" />
<PackageReference Include="TvDbSharper" Version="3.2.1" />
</ItemGroup>
<PropertyGroup>
<TargetFramework>netstandard2.1</TargetFramework>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<TreatWarningsAsErrors Condition=" '$(Configuration)' == 'Release'">true</TreatWarningsAsErrors>
</PropertyGroup>
<!-- Code Analyzers-->
@@ -44,11 +46,9 @@
<ItemGroup>
<None Remove="Plugins\AudioDb\Configuration\config.html" />
<EmbeddedResource Include="Plugins\AudioDb\Configuration\config.html" />
</ItemGroup>
<ItemGroup>
<None Remove="Plugins\Omdb\Configuration\config.html" />
<EmbeddedResource Include="Plugins\Omdb\Configuration\config.html" />
<None Remove="Plugins\MusicBrainz\Configuration\config.html" />
<EmbeddedResource Include="Plugins\MusicBrainz\Configuration\config.html" />
</ItemGroup>
</Project>

View File

@@ -1,3 +1,5 @@
#pragma warning disable CS1591
using System;
using System.Collections.Generic;
using System.Globalization;
@@ -17,7 +19,7 @@ using MediaBrowser.Model.IO;
namespace MediaBrowser.Providers.MediaInfo
{
/// <summary>
/// Uses ffmpeg to create video images
/// Uses ffmpeg to create video images.
/// </summary>
public class AudioImageProvider : IDynamicImageProvider
{
@@ -32,6 +34,10 @@ namespace MediaBrowser.Providers.MediaInfo
_fileSystem = fileSystem;
}
public string AudioImagesPath => Path.Combine(_config.ApplicationPaths.CachePath, "extracted-audio-images");
public string Name => "Image Extractor";
public IEnumerable<ImageType> GetSupportedImages(BaseItem item)
{
return new List<ImageType> { ImageType.Primary };
@@ -79,7 +85,6 @@ namespace MediaBrowser.Providers.MediaInfo
}
catch
{
}
}
@@ -92,15 +97,15 @@ namespace MediaBrowser.Providers.MediaInfo
private string GetAudioImagePath(Audio item)
{
string filename = null;
string filename;
if (item.GetType() == typeof(Audio))
{
var albumArtist = item.AlbumArtists.FirstOrDefault();
if (!string.IsNullOrWhiteSpace(item.Album) && !string.IsNullOrWhiteSpace(albumArtist))
if (item.AlbumArtists.Count > 0
&& !string.IsNullOrWhiteSpace(item.Album)
&& !string.IsNullOrWhiteSpace(item.AlbumArtists[0]))
{
filename = (item.Album + "-" + albumArtist).GetMD5().ToString("N", CultureInfo.InvariantCulture);
filename = (item.Album + "-" + item.AlbumArtists[0]).GetMD5().ToString("N", CultureInfo.InvariantCulture);
}
else
{
@@ -115,21 +120,18 @@ namespace MediaBrowser.Providers.MediaInfo
filename = item.Id.ToString("N", CultureInfo.InvariantCulture) + ".jpg";
}
var prefix = filename.Substring(0, 1);
var prefix = filename.AsSpan().Slice(0, 1);
return Path.Combine(AudioImagesPath, prefix, filename);
return Path.Join(AudioImagesPath, prefix, filename);
}
public string AudioImagesPath => Path.Combine(_config.ApplicationPaths.CachePath, "extracted-audio-images");
public string Name => "Image Extractor";
public bool Supports(BaseItem item)
{
if (item.IsShortcut)
{
return false;
}
if (!item.IsFileProtocol)
{
return false;

View File

@@ -1,10 +1,10 @@
#pragma warning disable CS1591
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using MediaBrowser.Common.Configuration;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.Audio;
using MediaBrowser.Controller.Library;
@@ -15,32 +15,31 @@ using MediaBrowser.Model.Dlna;
using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.MediaInfo;
using MediaBrowser.Model.Serialization;
namespace MediaBrowser.Providers.MediaInfo
{
class FFProbeAudioInfo
public class FFProbeAudioInfo
{
private readonly IMediaEncoder _mediaEncoder;
private readonly IItemRepository _itemRepo;
private readonly IApplicationPaths _appPaths;
private readonly IJsonSerializer _json;
private readonly ILibraryManager _libraryManager;
private readonly IMediaSourceManager _mediaSourceManager;
private readonly CultureInfo _usCulture = new CultureInfo("en-US");
public FFProbeAudioInfo(IMediaSourceManager mediaSourceManager, IMediaEncoder mediaEncoder, IItemRepository itemRepo, IApplicationPaths appPaths, IJsonSerializer json, ILibraryManager libraryManager)
public FFProbeAudioInfo(
IMediaSourceManager mediaSourceManager,
IMediaEncoder mediaEncoder,
IItemRepository itemRepo,
ILibraryManager libraryManager)
{
_mediaEncoder = mediaEncoder;
_itemRepo = itemRepo;
_appPaths = appPaths;
_json = json;
_libraryManager = libraryManager;
_mediaSourceManager = mediaSourceManager;
}
public async Task<ItemUpdateType> Probe<T>(T item, MetadataRefreshOptions options,
public async Task<ItemUpdateType> Probe<T>(
T item,
MetadataRefreshOptions options,
CancellationToken cancellationToken)
where T : Audio
{
@@ -55,20 +54,21 @@ namespace MediaBrowser.Providers.MediaInfo
protocol = _mediaSourceManager.GetPathProtocol(path);
}
var result = await _mediaEncoder.GetMediaInfo(new MediaInfoRequest
{
MediaType = DlnaProfileType.Audio,
MediaSource = new MediaSourceInfo
var result = await _mediaEncoder.GetMediaInfo(
new MediaInfoRequest
{
Path = path,
Protocol = protocol
}
}, cancellationToken).ConfigureAwait(false);
MediaType = DlnaProfileType.Audio,
MediaSource = new MediaSourceInfo
{
Path = path,
Protocol = protocol
}
},
cancellationToken).ConfigureAwait(false);
cancellationToken.ThrowIfCancellationRequested();
Fetch(item, cancellationToken, result);
Fetch(item, result, cancellationToken);
}
return ItemUpdateType.MetadataImport;
@@ -78,10 +78,9 @@ namespace MediaBrowser.Providers.MediaInfo
/// Fetches the specified audio.
/// </summary>
/// <param name="audio">The audio.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <param name="mediaInfo">The media information.</param>
/// <returns>Task.</returns>
protected void Fetch(Audio audio, CancellationToken cancellationToken, Model.MediaInfo.MediaInfo mediaInfo)
/// <param name="cancellationToken">The cancellation token.</param>
protected void Fetch(Audio audio, Model.MediaInfo.MediaInfo mediaInfo, CancellationToken cancellationToken)
{
var mediaStreams = mediaInfo.MediaStreams;
@@ -91,8 +90,8 @@ namespace MediaBrowser.Providers.MediaInfo
audio.RunTimeTicks = mediaInfo.RunTimeTicks;
audio.Size = mediaInfo.Size;
//var extension = (Path.GetExtension(audio.Path) ?? string.Empty).TrimStart('.');
//audio.Container = extension;
// var extension = (Path.GetExtension(audio.Path) ?? string.Empty).TrimStart('.');
// audio.Container = extension;
FetchDataFromTags(audio, mediaInfo);
@@ -100,7 +99,7 @@ namespace MediaBrowser.Providers.MediaInfo
}
/// <summary>
/// Fetches data from the tags dictionary
/// Fetches data from the tags dictionary.
/// </summary>
/// <param name="audio">The audio.</param>
/// <param name="data">The data.</param>
@@ -112,7 +111,7 @@ namespace MediaBrowser.Providers.MediaInfo
audio.Name = data.Name;
}
if (audio.SupportsPeople && !audio.LockedFields.Contains(MetadataFields.Cast))
if (audio.SupportsPeople && !audio.LockedFields.Contains(MetadataField.Cast))
{
var people = new List<PersonInfo>();
@@ -143,7 +142,7 @@ namespace MediaBrowser.Providers.MediaInfo
audio.ProductionYear = audio.PremiereDate.Value.ToLocalTime().Year;
}
if (!audio.LockedFields.Contains(MetadataFields.Genres))
if (!audio.LockedFields.Contains(MetadataField.Genres))
{
audio.Genres = Array.Empty<string>();
@@ -153,16 +152,16 @@ namespace MediaBrowser.Providers.MediaInfo
}
}
if (!audio.LockedFields.Contains(MetadataFields.Studios))
if (!audio.LockedFields.Contains(MetadataField.Studios))
{
audio.SetStudios(data.Studios);
}
audio.SetProviderId(MetadataProviders.MusicBrainzAlbumArtist, data.GetProviderId(MetadataProviders.MusicBrainzAlbumArtist));
audio.SetProviderId(MetadataProviders.MusicBrainzArtist, data.GetProviderId(MetadataProviders.MusicBrainzArtist));
audio.SetProviderId(MetadataProviders.MusicBrainzAlbum, data.GetProviderId(MetadataProviders.MusicBrainzAlbum));
audio.SetProviderId(MetadataProviders.MusicBrainzReleaseGroup, data.GetProviderId(MetadataProviders.MusicBrainzReleaseGroup));
audio.SetProviderId(MetadataProviders.MusicBrainzTrack, data.GetProviderId(MetadataProviders.MusicBrainzTrack));
audio.SetProviderId(MetadataProvider.MusicBrainzAlbumArtist, data.GetProviderId(MetadataProvider.MusicBrainzAlbumArtist));
audio.SetProviderId(MetadataProvider.MusicBrainzArtist, data.GetProviderId(MetadataProvider.MusicBrainzArtist));
audio.SetProviderId(MetadataProvider.MusicBrainzAlbum, data.GetProviderId(MetadataProvider.MusicBrainzAlbum));
audio.SetProviderId(MetadataProvider.MusicBrainzReleaseGroup, data.GetProviderId(MetadataProvider.MusicBrainzReleaseGroup));
audio.SetProviderId(MetadataProvider.MusicBrainzTrack, data.GetProviderId(MetadataProvider.MusicBrainzTrack));
}
}
}

View File

@@ -1,10 +1,10 @@
#pragma warning disable CS1591
using System;
using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using MediaBrowser.Common.Configuration;
using MediaBrowser.Controller.Channels;
using MediaBrowser.Controller.Chapters;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Entities;
@@ -18,9 +18,7 @@ using MediaBrowser.Controller.Providers;
using MediaBrowser.Controller.Subtitles;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Globalization;
using MediaBrowser.Model.IO;
using MediaBrowser.Model.MediaInfo;
using MediaBrowser.Model.Serialization;
using Microsoft.Extensions.Logging;
namespace MediaBrowser.Providers.MediaInfo
@@ -37,24 +35,54 @@ namespace MediaBrowser.Providers.MediaInfo
IPreRefreshProvider,
IHasItemChangeMonitor
{
private readonly ILogger _logger;
private readonly IIsoManager _isoManager;
private readonly ILogger<FFProbeProvider> _logger;
private readonly IMediaEncoder _mediaEncoder;
private readonly IItemRepository _itemRepo;
private readonly IBlurayExaminer _blurayExaminer;
private readonly ILocalizationManager _localization;
private readonly IApplicationPaths _appPaths;
private readonly IJsonSerializer _json;
private readonly IEncodingManager _encodingManager;
private readonly IServerConfigurationManager _config;
private readonly ISubtitleManager _subtitleManager;
private readonly IChapterManager _chapterManager;
private readonly ILibraryManager _libraryManager;
private readonly IChannelManager _channelManager;
private readonly IMediaSourceManager _mediaSourceManager;
private readonly SubtitleResolver _subtitleResolver;
private readonly Task<ItemUpdateType> _cachedTask = Task.FromResult(ItemUpdateType.None);
public FFProbeProvider(
ILogger<FFProbeProvider> logger,
IMediaSourceManager mediaSourceManager,
IMediaEncoder mediaEncoder,
IItemRepository itemRepo,
IBlurayExaminer blurayExaminer,
ILocalizationManager localization,
IEncodingManager encodingManager,
IServerConfigurationManager config,
ISubtitleManager subtitleManager,
IChapterManager chapterManager,
ILibraryManager libraryManager)
{
_logger = logger;
_mediaEncoder = mediaEncoder;
_itemRepo = itemRepo;
_blurayExaminer = blurayExaminer;
_localization = localization;
_encodingManager = encodingManager;
_config = config;
_subtitleManager = subtitleManager;
_chapterManager = chapterManager;
_libraryManager = libraryManager;
_mediaSourceManager = mediaSourceManager;
_subtitleResolver = new SubtitleResolver(BaseItem.LocalizationManager);
}
public string Name => "ffprobe";
// Run last
public int Order => 100;
public bool HasChanged(BaseItem item, IDirectoryService directoryService)
{
var video = item as Video;
@@ -119,45 +147,6 @@ namespace MediaBrowser.Providers.MediaInfo
return FetchAudioInfo(item, options, cancellationToken);
}
private SubtitleResolver _subtitleResolver;
public FFProbeProvider(
ILogger<FFProbeProvider> logger,
IMediaSourceManager mediaSourceManager,
IChannelManager channelManager,
IIsoManager isoManager,
IMediaEncoder mediaEncoder,
IItemRepository itemRepo,
IBlurayExaminer blurayExaminer,
ILocalizationManager localization,
IApplicationPaths appPaths,
IJsonSerializer json,
IEncodingManager encodingManager,
IServerConfigurationManager config,
ISubtitleManager subtitleManager,
IChapterManager chapterManager,
ILibraryManager libraryManager)
{
_logger = logger;
_isoManager = isoManager;
_mediaEncoder = mediaEncoder;
_itemRepo = itemRepo;
_blurayExaminer = blurayExaminer;
_localization = localization;
_appPaths = appPaths;
_json = json;
_encodingManager = encodingManager;
_config = config;
_subtitleManager = subtitleManager;
_chapterManager = chapterManager;
_libraryManager = libraryManager;
_channelManager = channelManager;
_mediaSourceManager = mediaSourceManager;
_subtitleResolver = new SubtitleResolver(BaseItem.LocalizationManager);
}
private readonly Task<ItemUpdateType> _cachedTask = Task.FromResult(ItemUpdateType.None);
public Task<ItemUpdateType> FetchVideoInfo<T>(T item, MetadataRefreshOptions options, CancellationToken cancellationToken)
where T : Video
{
@@ -209,9 +198,9 @@ namespace MediaBrowser.Providers.MediaInfo
private string NormalizeStrmLine(string line)
{
return line.Replace("\t", string.Empty)
.Replace("\r", string.Empty)
.Replace("\n", string.Empty)
return line.Replace("\t", string.Empty, StringComparison.Ordinal)
.Replace("\r", string.Empty, StringComparison.Ordinal)
.Replace("\n", string.Empty, StringComparison.Ordinal)
.Trim();
}
@@ -240,11 +229,9 @@ namespace MediaBrowser.Providers.MediaInfo
FetchShortcutInfo(item);
}
var prober = new FFProbeAudioInfo(_mediaSourceManager, _mediaEncoder, _itemRepo, _appPaths, _json, _libraryManager);
var prober = new FFProbeAudioInfo(_mediaSourceManager, _mediaEncoder, _itemRepo, _libraryManager);
return prober.Probe(item, options, cancellationToken);
}
// Run last
public int Order => 100;
}
}

View File

@@ -24,7 +24,6 @@ using MediaBrowser.Model.Dlna;
using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Globalization;
using MediaBrowser.Model.IO;
using MediaBrowser.Model.MediaInfo;
using MediaBrowser.Model.Providers;
using Microsoft.Extensions.Logging;
@@ -176,7 +175,7 @@ namespace MediaBrowser.Providers.MediaInfo
mediaAttachments = mediaInfo.MediaAttachments;
video.TotalBitrate = mediaInfo.Bitrate;
//video.FormatName = (mediaInfo.Container ?? string.Empty)
// video.FormatName = (mediaInfo.Container ?? string.Empty)
// .Replace("matroska", "mkv", StringComparison.OrdinalIgnoreCase);
// For dvd's this may not always be accurate, so don't set the runtime if the item already has one
@@ -283,7 +282,7 @@ namespace MediaBrowser.Providers.MediaInfo
{
var video = (Video)item;
//video.PlayableStreamFileNames = blurayInfo.Files.ToList();
// video.PlayableStreamFileNames = blurayInfo.Files.ToList();
// Use BD Info if it has multiple m2ts. Otherwise, treat it like a video file and rely more on ffprobe output
if (blurayInfo.Files.Length > 1)
@@ -342,7 +341,7 @@ namespace MediaBrowser.Providers.MediaInfo
}
/// <summary>
/// Gets information about the longest playlist on a bdrom
/// Gets information about the longest playlist on a bdrom.
/// </summary>
/// <param name="path">The path.</param>
/// <returns>VideoStream.</returns>
@@ -368,7 +367,7 @@ namespace MediaBrowser.Providers.MediaInfo
{
var isFullRefresh = refreshOptions.MetadataRefreshMode == MetadataRefreshMode.FullRefresh;
if (!video.IsLocked && !video.LockedFields.Contains(MetadataFields.OfficialRating))
if (!video.IsLocked && !video.LockedFields.Contains(MetadataField.OfficialRating))
{
if (!string.IsNullOrWhiteSpace(data.OfficialRating) || isFullRefresh)
{
@@ -376,7 +375,7 @@ namespace MediaBrowser.Providers.MediaInfo
}
}
if (!video.IsLocked && !video.LockedFields.Contains(MetadataFields.Genres))
if (!video.IsLocked && !video.LockedFields.Contains(MetadataField.Genres))
{
if (video.Genres.Length == 0 || isFullRefresh)
{
@@ -389,7 +388,7 @@ namespace MediaBrowser.Providers.MediaInfo
}
}
if (!video.IsLocked && !video.LockedFields.Contains(MetadataFields.Studios))
if (!video.IsLocked && !video.LockedFields.Contains(MetadataField.Studios))
{
if (video.Studios.Length == 0 || isFullRefresh)
{
@@ -404,6 +403,7 @@ namespace MediaBrowser.Providers.MediaInfo
video.ProductionYear = data.ProductionYear;
}
}
if (data.PremiereDate.HasValue)
{
if (!video.PremiereDate.HasValue || isFullRefresh)
@@ -411,6 +411,7 @@ namespace MediaBrowser.Providers.MediaInfo
video.PremiereDate = data.PremiereDate;
}
}
if (data.IndexNumber.HasValue)
{
if (!video.IndexNumber.HasValue || isFullRefresh)
@@ -418,6 +419,7 @@ namespace MediaBrowser.Providers.MediaInfo
video.IndexNumber = data.IndexNumber;
}
}
if (data.ParentIndexNumber.HasValue)
{
if (!video.ParentIndexNumber.HasValue || isFullRefresh)
@@ -426,7 +428,7 @@ namespace MediaBrowser.Providers.MediaInfo
}
}
if (!video.IsLocked && !video.LockedFields.Contains(MetadataFields.Name))
if (!video.IsLocked && !video.LockedFields.Contains(MetadataField.Name))
{
if (!string.IsNullOrWhiteSpace(data.Name) && libraryOptions.EnableEmbeddedTitles)
{
@@ -444,7 +446,7 @@ namespace MediaBrowser.Providers.MediaInfo
video.ProductionYear = video.PremiereDate.Value.ToLocalTime().Year;
}
if (!video.IsLocked && !video.LockedFields.Contains(MetadataFields.Overview))
if (!video.IsLocked && !video.LockedFields.Contains(MetadataField.Overview))
{
if (string.IsNullOrWhiteSpace(video.Overview) || isFullRefresh)
{
@@ -457,7 +459,7 @@ namespace MediaBrowser.Providers.MediaInfo
{
var isFullRefresh = options.MetadataRefreshMode == MetadataRefreshMode.FullRefresh;
if (!video.IsLocked && !video.LockedFields.Contains(MetadataFields.Cast))
if (!video.IsLocked && !video.LockedFields.Contains(MetadataField.Cast))
{
if (isFullRefresh || _libraryManager.GetPeople(video).Count == 0)
{
@@ -537,17 +539,18 @@ namespace MediaBrowser.Providers.MediaInfo
if (enableSubtitleDownloading && enabled)
{
var downloadedLanguages = await new SubtitleDownloader(_logger,
_subtitleManager)
.DownloadSubtitles(video,
currentStreams.Concat(externalSubtitleStreams).ToList(),
skipIfEmbeddedSubtitlesPresent,
skipIfAudioTrackMatches,
requirePerfectMatch,
subtitleDownloadLanguages,
libraryOptions.DisabledSubtitleFetchers,
libraryOptions.SubtitleFetcherOrder,
cancellationToken).ConfigureAwait(false);
var downloadedLanguages = await new SubtitleDownloader(
_logger,
_subtitleManager).DownloadSubtitles(
video,
currentStreams.Concat(externalSubtitleStreams).ToList(),
skipIfEmbeddedSubtitlesPresent,
skipIfAudioTrackMatches,
requirePerfectMatch,
subtitleDownloadLanguages,
libraryOptions.DisabledSubtitleFetchers,
libraryOptions.SubtitleFetcherOrder,
cancellationToken).ConfigureAwait(false);
// Rescan
if (downloadedLanguages.Count > 0)
@@ -565,7 +568,7 @@ namespace MediaBrowser.Providers.MediaInfo
/// Creates dummy chapters.
/// </summary>
/// <param name="video">The video.</param>
/// <return>An array of dummy chapters.</returns>
/// <returns>An array of dummy chapters.</returns>
private ChapterInfo[] CreateDummyChapters(Video video)
{
var runtime = video.RunTimeTicks ?? 0;

View File

@@ -1,3 +1,5 @@
#pragma warning disable CS1591
using System;
using System.Collections.Generic;
using System.Linq;
@@ -25,7 +27,8 @@ namespace MediaBrowser.Providers.MediaInfo
_subtitleManager = subtitleManager;
}
public async Task<List<string>> DownloadSubtitles(Video video,
public async Task<List<string>> DownloadSubtitles(
Video video,
List<MediaStream> mediaStreams,
bool skipIfEmbeddedSubtitlesPresent,
bool skipIfAudioTrackMatches,
@@ -39,8 +42,16 @@ namespace MediaBrowser.Providers.MediaInfo
foreach (var lang in languages)
{
var downloaded = await DownloadSubtitles(video, mediaStreams, skipIfEmbeddedSubtitlesPresent,
skipIfAudioTrackMatches, requirePerfectMatch, lang, disabledSubtitleFetchers, subtitleFetcherOrder, cancellationToken).ConfigureAwait(false);
var downloaded = await DownloadSubtitles(
video,
mediaStreams,
skipIfEmbeddedSubtitlesPresent,
skipIfAudioTrackMatches,
requirePerfectMatch,
lang,
disabledSubtitleFetchers,
subtitleFetcherOrder,
cancellationToken).ConfigureAwait(false);
if (downloaded)
{
@@ -51,7 +62,8 @@ namespace MediaBrowser.Providers.MediaInfo
return downloadedLanguages;
}
public Task<bool> DownloadSubtitles(Video video,
public Task<bool> DownloadSubtitles(
Video video,
List<MediaStream> mediaStreams,
bool skipIfEmbeddedSubtitlesPresent,
bool skipIfAudioTrackMatches,
@@ -87,11 +99,21 @@ namespace MediaBrowser.Providers.MediaInfo
return Task.FromResult(false);
}
return DownloadSubtitles(video, mediaStreams, skipIfEmbeddedSubtitlesPresent, skipIfAudioTrackMatches,
requirePerfectMatch, lang, disabledSubtitleFetchers, subtitleFetcherOrder, mediaType, cancellationToken);
return DownloadSubtitles(
video,
mediaStreams,
skipIfEmbeddedSubtitlesPresent,
skipIfAudioTrackMatches,
requirePerfectMatch,
lang,
disabledSubtitleFetchers,
subtitleFetcherOrder,
mediaType,
cancellationToken);
}
private async Task<bool> DownloadSubtitles(Video video,
private async Task<bool> DownloadSubtitles(
Video video,
List<MediaStream> mediaStreams,
bool skipIfEmbeddedSubtitlesPresent,
bool skipIfAudioTrackMatches,

View File

@@ -1,3 +1,5 @@
#pragma warning disable CS1591
using System;
using System.Collections.Generic;
using System.IO;
@@ -6,7 +8,6 @@ using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Globalization;
using MediaBrowser.Model.IO;
namespace MediaBrowser.Providers.MediaInfo
{
@@ -60,15 +61,15 @@ namespace MediaBrowser.Providers.MediaInfo
}
catch (IOException)
{
}
return streams;
}
public List<string> GetExternalSubtitleFiles(Video video,
IDirectoryService directoryService,
bool clearCache)
public List<string> GetExternalSubtitleFiles(
Video video,
IDirectoryService directoryService,
bool clearCache)
{
var list = new List<string>();
@@ -87,7 +88,9 @@ namespace MediaBrowser.Providers.MediaInfo
return list;
}
private void AddExternalSubtitleStreams(List<MediaStream> streams, string folder,
private void AddExternalSubtitleStreams(
List<MediaStream> streams,
string folder,
string videoPath,
int startIndex,
IDirectoryService directoryService,
@@ -98,7 +101,8 @@ namespace MediaBrowser.Providers.MediaInfo
AddExternalSubtitleStreams(streams, videoPath, startIndex, files);
}
public void AddExternalSubtitleStreams(List<MediaStream> streams,
public void AddExternalSubtitleStreams(
List<MediaStream> streams,
string videoPath,
int startIndex,
string[] files)
@@ -185,13 +189,13 @@ namespace MediaBrowser.Providers.MediaInfo
private string NormalizeFilenameForSubtitleComparison(string filename)
{
// Try to account for sloppy file naming
filename = filename.Replace("_", string.Empty);
filename = filename.Replace(" ", string.Empty);
filename = filename.Replace("_", string.Empty, StringComparison.Ordinal);
filename = filename.Replace(" ", string.Empty, StringComparison.Ordinal);
// can't normalize this due to languages such as pt-br
//filename = filename.Replace("-", string.Empty);
// filename = filename.Replace("-", string.Empty);
//filename = filename.Replace(".", string.Empty);
// filename = filename.Replace(".", string.Empty);
return filename;
}

View File

@@ -1,3 +1,5 @@
#pragma warning disable CS1591
using System;
using System.Collections.Generic;
using System.Linq;
@@ -10,11 +12,10 @@ using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Subtitles;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Globalization;
using MediaBrowser.Model.Providers;
using MediaBrowser.Model.Serialization;
using MediaBrowser.Model.Tasks;
using Microsoft.Extensions.Logging;
using MediaBrowser.Model.Globalization;
namespace MediaBrowser.Providers.MediaInfo
{
@@ -23,29 +24,37 @@ namespace MediaBrowser.Providers.MediaInfo
private readonly ILibraryManager _libraryManager;
private readonly IServerConfigurationManager _config;
private readonly ISubtitleManager _subtitleManager;
private readonly IMediaSourceManager _mediaSourceManager;
private readonly ILogger _logger;
private readonly IJsonSerializer _json;
private readonly ILogger<SubtitleScheduledTask> _logger;
private readonly ILocalizationManager _localization;
public SubtitleScheduledTask(
ILibraryManager libraryManager,
IJsonSerializer json,
IServerConfigurationManager config,
ISubtitleManager subtitleManager,
ILogger<SubtitleScheduledTask> logger,
IMediaSourceManager mediaSourceManager,
ILocalizationManager localization)
{
_libraryManager = libraryManager;
_config = config;
_subtitleManager = subtitleManager;
_logger = logger;
_mediaSourceManager = mediaSourceManager;
_json = json;
_localization = localization;
}
public string Name => _localization.GetLocalizedString("TaskDownloadMissingSubtitles");
public string Description => _localization.GetLocalizedString("TaskDownloadMissingSubtitlesDescription");
public string Category => _localization.GetLocalizedString("TasksLibraryCategory");
public string Key => "DownloadSubtitles";
public bool IsHidden => false;
public bool IsEnabled => true;
public bool IsLogged => true;
private SubtitleOptions GetOptions()
{
return _config.GetConfiguration<SubtitleOptions>("subtitles");
@@ -64,23 +73,23 @@ namespace MediaBrowser.Providers.MediaInfo
var libraryOptions = _libraryManager.GetLibraryOptions(library);
string[] subtitleDownloadLanguages;
bool SkipIfEmbeddedSubtitlesPresent;
bool SkipIfAudioTrackMatches;
bool RequirePerfectMatch;
bool skipIfEmbeddedSubtitlesPresent;
bool skipIfAudioTrackMatches;
bool requirePerfectMatch;
if (libraryOptions.SubtitleDownloadLanguages == null)
{
subtitleDownloadLanguages = options.DownloadLanguages;
SkipIfEmbeddedSubtitlesPresent = options.SkipIfEmbeddedSubtitlesPresent;
SkipIfAudioTrackMatches = options.SkipIfAudioTrackMatches;
RequirePerfectMatch = options.RequirePerfectMatch;
skipIfEmbeddedSubtitlesPresent = options.SkipIfEmbeddedSubtitlesPresent;
skipIfAudioTrackMatches = options.SkipIfAudioTrackMatches;
requirePerfectMatch = options.RequirePerfectMatch;
}
else
{
subtitleDownloadLanguages = libraryOptions.SubtitleDownloadLanguages;
SkipIfEmbeddedSubtitlesPresent = libraryOptions.SkipSubtitlesIfEmbeddedSubtitlesPresent;
SkipIfAudioTrackMatches = libraryOptions.SkipSubtitlesIfAudioTrackMatches;
RequirePerfectMatch = libraryOptions.RequirePerfectSubtitleMatch;
skipIfEmbeddedSubtitlesPresent = libraryOptions.SkipSubtitlesIfEmbeddedSubtitlesPresent;
skipIfAudioTrackMatches = libraryOptions.SkipSubtitlesIfAudioTrackMatches;
requirePerfectMatch = libraryOptions.RequirePerfectSubtitleMatch;
}
foreach (var lang in subtitleDownloadLanguages)
@@ -96,12 +105,12 @@ namespace MediaBrowser.Providers.MediaInfo
Recursive = true
};
if (SkipIfAudioTrackMatches)
if (skipIfAudioTrackMatches)
{
query.HasNoAudioTrackWithLanguage = lang;
}
if (SkipIfEmbeddedSubtitlesPresent)
if (skipIfEmbeddedSubtitlesPresent)
{
// Exclude if it already has any subtitles of the same language
query.HasNoSubtitleTrackWithLanguage = lang;
@@ -158,36 +167,37 @@ namespace MediaBrowser.Providers.MediaInfo
var libraryOptions = _libraryManager.GetLibraryOptions(video);
string[] subtitleDownloadLanguages;
bool SkipIfEmbeddedSubtitlesPresent;
bool SkipIfAudioTrackMatches;
bool RequirePerfectMatch;
bool skipIfEmbeddedSubtitlesPresent;
bool skipIfAudioTrackMatches;
bool requirePerfectMatch;
if (libraryOptions.SubtitleDownloadLanguages == null)
{
subtitleDownloadLanguages = options.DownloadLanguages;
SkipIfEmbeddedSubtitlesPresent = options.SkipIfEmbeddedSubtitlesPresent;
SkipIfAudioTrackMatches = options.SkipIfAudioTrackMatches;
RequirePerfectMatch = options.RequirePerfectMatch;
skipIfEmbeddedSubtitlesPresent = options.SkipIfEmbeddedSubtitlesPresent;
skipIfAudioTrackMatches = options.SkipIfAudioTrackMatches;
requirePerfectMatch = options.RequirePerfectMatch;
}
else
{
subtitleDownloadLanguages = libraryOptions.SubtitleDownloadLanguages;
SkipIfEmbeddedSubtitlesPresent = libraryOptions.SkipSubtitlesIfEmbeddedSubtitlesPresent;
SkipIfAudioTrackMatches = libraryOptions.SkipSubtitlesIfAudioTrackMatches;
RequirePerfectMatch = libraryOptions.RequirePerfectSubtitleMatch;
skipIfEmbeddedSubtitlesPresent = libraryOptions.SkipSubtitlesIfEmbeddedSubtitlesPresent;
skipIfAudioTrackMatches = libraryOptions.SkipSubtitlesIfAudioTrackMatches;
requirePerfectMatch = libraryOptions.RequirePerfectSubtitleMatch;
}
var downloadedLanguages = await new SubtitleDownloader(_logger,
_subtitleManager)
.DownloadSubtitles(video,
mediaStreams,
SkipIfEmbeddedSubtitlesPresent,
SkipIfAudioTrackMatches,
RequirePerfectMatch,
subtitleDownloadLanguages,
libraryOptions.DisabledSubtitleFetchers,
libraryOptions.SubtitleFetcherOrder,
cancellationToken).ConfigureAwait(false);
var downloadedLanguages = await new SubtitleDownloader(
_logger,
_subtitleManager).DownloadSubtitles(
video,
mediaStreams,
skipIfEmbeddedSubtitlesPresent,
skipIfAudioTrackMatches,
requirePerfectMatch,
subtitleDownloadLanguages,
libraryOptions.DisabledSubtitleFetchers,
libraryOptions.SubtitleFetcherOrder,
cancellationToken).ConfigureAwait(false);
// Rescan
if (downloadedLanguages.Count > 0)
@@ -201,25 +211,11 @@ namespace MediaBrowser.Providers.MediaInfo
public IEnumerable<TaskTriggerInfo> GetDefaultTriggers()
{
return new[] {
return new[]
{
// Every so often
new TaskTriggerInfo { Type = TaskTriggerInfo.TriggerInterval, IntervalTicks = TimeSpan.FromHours(24).Ticks}
};
}
public string Name => _localization.GetLocalizedString("TaskDownloadMissingSubtitles");
public string Description => _localization.GetLocalizedString("TaskDownloadMissingSubtitlesDescription");
public string Category => _localization.GetLocalizedString("TasksLibraryCategory");
public string Key => "DownloadSubtitles";
public bool IsHidden => false;
public bool IsEnabled => true;
public bool IsLogged => true;
}
}

View File

@@ -1,3 +1,5 @@
#pragma warning disable CS1591
using System;
using System.Collections.Generic;
using System.Linq;
@@ -17,7 +19,7 @@ namespace MediaBrowser.Providers.MediaInfo
public class VideoImageProvider : IDynamicImageProvider, IHasOrder
{
private readonly IMediaEncoder _mediaEncoder;
private readonly ILogger _logger;
private readonly ILogger<VideoImageProvider> _logger;
private readonly IFileSystem _fileSystem;
public VideoImageProvider(IMediaEncoder mediaEncoder, ILogger<VideoImageProvider> logger, IFileSystem fileSystem)
@@ -27,6 +29,11 @@ namespace MediaBrowser.Providers.MediaInfo
_fileSystem = fileSystem;
}
public string Name => "Screen Grabber";
// Make sure this comes after internet image providers
public int Order => 100;
public IEnumerable<ImageType> GetSupportedImages(BaseItem item)
{
return new List<ImageType> { ImageType.Primary };
@@ -93,6 +100,7 @@ namespace MediaBrowser.Providers.MediaInfo
{
videoIndex++;
}
if (mediaStream == imageStream)
{
break;
@@ -124,14 +132,13 @@ namespace MediaBrowser.Providers.MediaInfo
};
}
public string Name => "Screen Grabber";
public bool Supports(BaseItem item)
{
if (item.IsShortcut)
{
return false;
}
if (!item.IsFileProtocol)
{
return false;
@@ -146,7 +153,5 @@ namespace MediaBrowser.Providers.MediaInfo
return false;
}
// Make sure this comes after internet image providers
public int Order => 100;
}
}

View File

@@ -1,19 +1,25 @@
#pragma warning disable CS1591
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.Movies;
using MediaBrowser.Controller.Entities.TV;
using MediaBrowser.Controller.LiveTv;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Providers;
namespace MediaBrowser.Providers.Movies
{
public class ImdbExternalId : IExternalId
{
/// <inheritdoc />
public string Name => "IMDb";
public string ProviderName => "IMDb";
/// <inheritdoc />
public string Key => MetadataProviders.Imdb.ToString();
public string Key => MetadataProvider.Imdb.ToString();
/// <inheritdoc />
public ExternalIdMediaType? Type => null;
/// <inheritdoc />
public string UrlFormatString => "https://www.imdb.com/title/{0}";
@@ -30,19 +36,4 @@ namespace MediaBrowser.Providers.Movies
return item is Movie || item is MusicVideo || item is Series || item is Episode || item is Trailer;
}
}
public class ImdbPersonExternalId : IExternalId
{
/// <inheritdoc />
public string Name => "IMDb";
/// <inheritdoc />
public string Key => MetadataProviders.Imdb.ToString();
/// <inheritdoc />
public string UrlFormatString => "https://www.imdb.com/name/{0}";
/// <inheritdoc />
public bool Supports(IHasProviderIds item) => item is Person;
}
}

View File

@@ -0,0 +1,27 @@
#pragma warning disable CS1591
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Providers;
namespace MediaBrowser.Providers.Movies
{
public class ImdbPersonExternalId : IExternalId
{
/// <inheritdoc />
public string ProviderName => "IMDb";
/// <inheritdoc />
public string Key => MetadataProvider.Imdb.ToString();
/// <inheritdoc />
public ExternalIdMediaType? Type => ExternalIdMediaType.Person;
/// <inheritdoc />
public string UrlFormatString => "https://www.imdb.com/name/{0}";
/// <inheritdoc />
public bool Supports(IHasProviderIds item) => item is Person;
}
}

View File

@@ -1,3 +1,5 @@
#pragma warning disable CS1591
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Entities.Movies;
using MediaBrowser.Controller.Library;
@@ -28,15 +30,17 @@ namespace MediaBrowser.Providers.Movies
{
return false;
}
if (!item.ProductionYear.HasValue)
{
return false;
}
return base.IsFullLocalMetadata(item);
}
/// <inheritdoc />
protected override void MergeData(MetadataResult<Movie> source, MetadataResult<Movie> target, MetadataFields[] lockedFields, bool replaceData, bool mergeMetadataSettings)
protected override void MergeData(MetadataResult<Movie> source, MetadataResult<Movie> target, MetadataField[] lockedFields, bool replaceData, bool mergeMetadataSettings)
{
ProviderUtils.MergeBaseItemData(source, target, lockedFields, replaceData, mergeMetadataSettings);

View File

@@ -1,3 +1,5 @@
#pragma warning disable CS1591
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Library;
@@ -28,15 +30,17 @@ namespace MediaBrowser.Providers.Movies
{
return false;
}
if (!item.ProductionYear.HasValue)
{
return false;
}
return base.IsFullLocalMetadata(item);
}
/// <inheritdoc />
protected override void MergeData(MetadataResult<Trailer> source, MetadataResult<Trailer> target, MetadataFields[] lockedFields, bool replaceData, bool mergeMetadataSettings)
protected override void MergeData(MetadataResult<Trailer> source, MetadataResult<Trailer> target, MetadataField[] lockedFields, bool replaceData, bool mergeMetadataSettings)
{
ProviderUtils.MergeBaseItemData(source, target, lockedFields, replaceData, mergeMetadataSettings);

View File

@@ -1,10 +1,12 @@
#pragma warning disable CS1591
using System.Linq;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Entities;
namespace MediaBrowser.Providers.Music
{
public static class Extensions
public static class AlbumInfoExtensions
{
public static string GetAlbumArtist(this AlbumInfo info)
{
@@ -16,16 +18,16 @@ namespace MediaBrowser.Providers.Music
return id;
}
return info.AlbumArtists.FirstOrDefault();
return info.AlbumArtists.Count > 0 ? info.AlbumArtists[0] : default;
}
public static string GetReleaseGroupId(this AlbumInfo info)
{
var id = info.GetProviderId(MetadataProviders.MusicBrainzReleaseGroup);
var id = info.GetProviderId(MetadataProvider.MusicBrainzReleaseGroup);
if (string.IsNullOrEmpty(id))
{
return info.SongInfos.Select(i => i.GetProviderId(MetadataProviders.MusicBrainzReleaseGroup))
return info.SongInfos.Select(i => i.GetProviderId(MetadataProvider.MusicBrainzReleaseGroup))
.FirstOrDefault(i => !string.IsNullOrEmpty(i));
}
@@ -34,11 +36,11 @@ namespace MediaBrowser.Providers.Music
public static string GetReleaseId(this AlbumInfo info)
{
var id = info.GetProviderId(MetadataProviders.MusicBrainzAlbum);
var id = info.GetProviderId(MetadataProvider.MusicBrainzAlbum);
if (string.IsNullOrEmpty(id))
{
return info.SongInfos.Select(i => i.GetProviderId(MetadataProviders.MusicBrainzAlbum))
return info.SongInfos.Select(i => i.GetProviderId(MetadataProvider.MusicBrainzAlbum))
.FirstOrDefault(i => !string.IsNullOrEmpty(i));
}
@@ -47,16 +49,16 @@ namespace MediaBrowser.Providers.Music
public static string GetMusicBrainzArtistId(this AlbumInfo info)
{
info.ProviderIds.TryGetValue(MetadataProviders.MusicBrainzAlbumArtist.ToString(), out string id);
info.ProviderIds.TryGetValue(MetadataProvider.MusicBrainzAlbumArtist.ToString(), out string id);
if (string.IsNullOrEmpty(id))
{
info.ArtistProviderIds.TryGetValue(MetadataProviders.MusicBrainzArtist.ToString(), out id);
info.ArtistProviderIds.TryGetValue(MetadataProvider.MusicBrainzArtist.ToString(), out id);
}
if (string.IsNullOrEmpty(id))
{
return info.SongInfos.Select(i => i.GetProviderId(MetadataProviders.MusicBrainzAlbumArtist))
return info.SongInfos.Select(i => i.GetProviderId(MetadataProvider.MusicBrainzAlbumArtist))
.FirstOrDefault(i => !string.IsNullOrEmpty(i));
}
@@ -65,11 +67,11 @@ namespace MediaBrowser.Providers.Music
public static string GetMusicBrainzArtistId(this ArtistInfo info)
{
info.ProviderIds.TryGetValue(MetadataProviders.MusicBrainzArtist.ToString(), out var id);
info.ProviderIds.TryGetValue(MetadataProvider.MusicBrainzArtist.ToString(), out var id);
if (string.IsNullOrEmpty(id))
{
return info.SongInfos.Select(i => i.GetProviderId(MetadataProviders.MusicBrainzAlbumArtist))
return info.SongInfos.Select(i => i.GetProviderId(MetadataProvider.MusicBrainzAlbumArtist))
.FirstOrDefault(i => !string.IsNullOrEmpty(i));
}

View File

@@ -1,3 +1,5 @@
#pragma warning disable CS1591
using System;
using System.Collections.Generic;
using System.Linq;
@@ -45,7 +47,7 @@ namespace MediaBrowser.Providers.Music
if (isFullRefresh || currentUpdateType > ItemUpdateType.None)
{
if (!item.LockedFields.Contains(MetadataFields.Name))
if (!item.LockedFields.Contains(MetadataField.Name))
{
var name = children.Select(i => i.Album).FirstOrDefault(i => !string.IsNullOrEmpty(i));
@@ -108,7 +110,7 @@ namespace MediaBrowser.Providers.Music
protected override void MergeData(
MetadataResult<MusicAlbum> source,
MetadataResult<MusicAlbum> target,
MetadataFields[] lockedFields,
MetadataField[] lockedFields,
bool replaceData,
bool mergeMetadataSettings)
{

View File

@@ -1,3 +1,5 @@
#pragma warning disable CS1591
using System.Collections.Generic;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Entities;
@@ -39,7 +41,7 @@ namespace MediaBrowser.Providers.Music
}
/// <inheritdoc />
protected override void MergeData(MetadataResult<MusicArtist> source, MetadataResult<MusicArtist> target, MetadataFields[] lockedFields, bool replaceData, bool mergeMetadataSettings)
protected override void MergeData(MetadataResult<MusicArtist> source, MetadataResult<MusicArtist> target, MetadataField[] lockedFields, bool replaceData, bool mergeMetadataSettings)
{
ProviderUtils.MergeBaseItemData(source, target, lockedFields, replaceData, mergeMetadataSettings);
}

View File

@@ -1,3 +1,5 @@
#pragma warning disable CS1591
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Entities.Audio;
using MediaBrowser.Controller.Library;
@@ -22,7 +24,7 @@ namespace MediaBrowser.Providers.Music
}
/// <inheritdoc />
protected override void MergeData(MetadataResult<Audio> source, MetadataResult<Audio> target, MetadataFields[] lockedFields, bool replaceData, bool mergeMetadataSettings)
protected override void MergeData(MetadataResult<Audio> source, MetadataResult<Audio> target, MetadataField[] lockedFields, bool replaceData, bool mergeMetadataSettings)
{
ProviderUtils.MergeBaseItemData(source, target, lockedFields, replaceData, mergeMetadataSettings);

View File

@@ -1,17 +1,23 @@
#pragma warning disable CS1591
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Providers;
namespace MediaBrowser.Providers.Music
{
public class ImvdbId : IExternalId
{
/// <inheritdoc />
public string Name => "IMVDb";
public string ProviderName => "IMVDb";
/// <inheritdoc />
public string Key => "IMVDb";
/// <inheritdoc />
public ExternalIdMediaType? Type => null;
/// <inheritdoc />
public string UrlFormatString => null;

View File

@@ -1,3 +1,5 @@
#pragma warning disable CS1591
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Library;
@@ -25,7 +27,7 @@ namespace MediaBrowser.Providers.Music
protected override void MergeData(
MetadataResult<MusicVideo> source,
MetadataResult<MusicVideo> target,
MetadataFields[] lockedFields,
MetadataField[] lockedFields,
bool replaceData,
bool mergeMetadataSettings)
{

View File

@@ -1,3 +1,5 @@
#pragma warning disable CS1591
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Entities.Audio;
using MediaBrowser.Controller.Library;
@@ -22,7 +24,7 @@ namespace MediaBrowser.Providers.MusicGenres
}
/// <inheritdoc />
protected override void MergeData(MetadataResult<MusicGenre> source, MetadataResult<MusicGenre> target, MetadataFields[] lockedFields, bool replaceData, bool mergeMetadataSettings)
protected override void MergeData(MetadataResult<MusicGenre> source, MetadataResult<MusicGenre> target, MetadataField[] lockedFields, bool replaceData, bool mergeMetadataSettings)
{
ProviderUtils.MergeBaseItemData(source, target, lockedFields, replaceData, mergeMetadataSettings);
}

View File

@@ -1,3 +1,5 @@
#pragma warning disable CS1591
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Library;
@@ -22,7 +24,7 @@ namespace MediaBrowser.Providers.People
}
/// <inheritdoc />
protected override void MergeData(MetadataResult<Person> source, MetadataResult<Person> target, MetadataFields[] lockedFields, bool replaceData, bool mergeMetadataSettings)
protected override void MergeData(MetadataResult<Person> source, MetadataResult<Person> target, MetadataField[] lockedFields, bool replaceData, bool mergeMetadataSettings)
{
ProviderUtils.MergeBaseItemData(source, target, lockedFields, replaceData, mergeMetadataSettings);
}

View File

@@ -1,3 +1,5 @@
#pragma warning disable CS1591
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Library;
@@ -22,7 +24,7 @@ namespace MediaBrowser.Providers.Photos
}
/// <inheritdoc />
protected override void MergeData(MetadataResult<PhotoAlbum> source, MetadataResult<PhotoAlbum> target, MetadataFields[] lockedFields, bool replaceData, bool mergeMetadataSettings)
protected override void MergeData(MetadataResult<PhotoAlbum> source, MetadataResult<PhotoAlbum> target, MetadataField[] lockedFields, bool replaceData, bool mergeMetadataSettings)
{
ProviderUtils.MergeBaseItemData(source, target, lockedFields, replaceData, mergeMetadataSettings);
}

View File

@@ -1,3 +1,5 @@
#pragma warning disable CS1591
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Library;
@@ -22,7 +24,7 @@ namespace MediaBrowser.Providers.Photos
}
/// <inheritdoc />
protected override void MergeData(MetadataResult<Photo> source, MetadataResult<Photo> target, MetadataFields[] lockedFields, bool replaceData, bool mergeMetadataSettings)
protected override void MergeData(MetadataResult<Photo> source, MetadataResult<Photo> target, MetadataField[] lockedFields, bool replaceData, bool mergeMetadataSettings)
{
ProviderUtils.MergeBaseItemData(source, target, lockedFields, replaceData, mergeMetadataSettings);
}

View File

@@ -1,3 +1,5 @@
#pragma warning disable CS1591
using System;
using System.Collections.Generic;
using System.IO;
@@ -8,7 +10,6 @@ using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Playlists;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.IO;
using Microsoft.Extensions.Logging;
using PlaylistsNET.Content;
@@ -20,17 +21,18 @@ namespace MediaBrowser.Providers.Playlists
IPreRefreshProvider,
IHasItemChangeMonitor
{
private ILogger _logger;
private IFileSystem _fileSystem;
private readonly ILogger<PlaylistItemsProvider> _logger;
public PlaylistItemsProvider(IFileSystem fileSystem, ILogger<PlaylistItemsProvider> logger)
public PlaylistItemsProvider(ILogger<PlaylistItemsProvider> logger)
{
_fileSystem = fileSystem;
_logger = logger;
}
public string Name => "Playlist Reader";
// Run last
public int Order => 100;
public Task<ItemUpdateType> FetchAsync(Playlist item, MetadataRefreshOptions options, CancellationToken cancellationToken)
{
var path = item.Path;
@@ -61,18 +63,22 @@ namespace MediaBrowser.Providers.Playlists
{
return GetWplItems(stream);
}
if (string.Equals(".zpl", extension, StringComparison.OrdinalIgnoreCase))
{
return GetZplItems(stream);
}
if (string.Equals(".m3u", extension, StringComparison.OrdinalIgnoreCase))
{
return GetM3uItems(stream);
}
if (string.Equals(".m3u8", extension, StringComparison.OrdinalIgnoreCase))
{
return GetM3u8Items(stream);
}
if (string.Equals(".pls", extension, StringComparison.OrdinalIgnoreCase))
{
return GetPlsItems(stream);
@@ -95,7 +101,7 @@ namespace MediaBrowser.Providers.Playlists
private IEnumerable<LinkedChild> GetM3u8Items(Stream stream)
{
var content = new M3u8Content();
var content = new M3uContent();
var playlist = content.GetFromStream(stream);
return playlist.PlaylistEntries.Select(i => new LinkedChild
@@ -157,7 +163,5 @@ namespace MediaBrowser.Providers.Playlists
return false;
}
// Run last
public int Order => 100;
}
}

View File

@@ -1,3 +1,5 @@
#pragma warning disable CS1591
using System.Collections.Generic;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Entities;
@@ -37,7 +39,7 @@ namespace MediaBrowser.Providers.Playlists
=> item.GetLinkedChildren();
/// <inheritdoc />
protected override void MergeData(MetadataResult<Playlist> source, MetadataResult<Playlist> target, MetadataFields[] lockedFields, bool replaceData, bool mergeMetadataSettings)
protected override void MergeData(MetadataResult<Playlist> source, MetadataResult<Playlist> target, MetadataField[] lockedFields, bool replaceData, bool mergeMetadataSettings)
{
ProviderUtils.MergeBaseItemData(source, target, lockedFields, replaceData, mergeMetadataSettings);

View File

@@ -1,4 +1,7 @@
#pragma warning disable CS1591
using System.Collections.Generic;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
using MediaBrowser.Common.Net;
@@ -15,13 +18,13 @@ namespace MediaBrowser.Providers.Plugins.AudioDb
public class AudioDbAlbumImageProvider : IRemoteImageProvider, IHasOrder
{
private readonly IServerConfigurationManager _config;
private readonly IHttpClient _httpClient;
private readonly IHttpClientFactory _httpClientFactory;
private readonly IJsonSerializer _json;
public AudioDbAlbumImageProvider(IServerConfigurationManager config, IHttpClient httpClient, IJsonSerializer json)
public AudioDbAlbumImageProvider(IServerConfigurationManager config, IHttpClientFactory httpClientFactory, IJsonSerializer json)
{
_config = config;
_httpClient = httpClient;
_httpClientFactory = httpClientFactory;
_json = json;
}
@@ -45,7 +48,7 @@ namespace MediaBrowser.Providers.Plugins.AudioDb
/// <inheritdoc />
public async Task<IEnumerable<RemoteImageInfo>> GetImages(BaseItem item, CancellationToken cancellationToken)
{
var id = item.GetProviderId(MetadataProviders.MusicBrainzReleaseGroup);
var id = item.GetProviderId(MetadataProvider.MusicBrainzReleaseGroup);
if (!string.IsNullOrWhiteSpace(id))
{
@@ -92,13 +95,10 @@ namespace MediaBrowser.Providers.Plugins.AudioDb
}
/// <inheritdoc />
public Task<HttpResponseInfo> GetImageResponse(string url, CancellationToken cancellationToken)
public Task<HttpResponseMessage> GetImageResponse(string url, CancellationToken cancellationToken)
{
return _httpClient.GetResponse(new HttpRequestOptions
{
CancellationToken = cancellationToken,
Url = url
});
var httpClient = _httpClientFactory.CreateClient(NamedClient.Default);
return httpClient.GetAsync(url, cancellationToken);
}
/// <inheritdoc />

View File

@@ -1,3 +1,5 @@
#pragma warning disable CS1591
using System;
using System.Collections.Generic;
using System.Globalization;
@@ -24,16 +26,16 @@ namespace MediaBrowser.Providers.Plugins.AudioDb
{
private readonly IServerConfigurationManager _config;
private readonly IFileSystem _fileSystem;
private readonly IHttpClient _httpClient;
private readonly IHttpClientFactory _httpClientFactory;
private readonly IJsonSerializer _json;
public static AudioDbAlbumProvider Current;
public AudioDbAlbumProvider(IServerConfigurationManager config, IFileSystem fileSystem, IHttpClient httpClient, IJsonSerializer json)
public AudioDbAlbumProvider(IServerConfigurationManager config, IFileSystem fileSystem, IHttpClientFactory httpClientFactory, IJsonSerializer json)
{
_config = config;
_fileSystem = fileSystem;
_httpClient = httpClient;
_httpClientFactory = httpClientFactory;
_json = json;
Current = this;
@@ -104,11 +106,11 @@ namespace MediaBrowser.Providers.Plugins.AudioDb
item.Genres = new[] { result.strGenre };
}
item.SetProviderId(MetadataProviders.AudioDbArtist, result.idArtist);
item.SetProviderId(MetadataProviders.AudioDbAlbum, result.idAlbum);
item.SetProviderId(MetadataProvider.AudioDbArtist, result.idArtist);
item.SetProviderId(MetadataProvider.AudioDbAlbum, result.idAlbum);
item.SetProviderId(MetadataProviders.MusicBrainzAlbumArtist, result.strMusicBrainzArtistID);
item.SetProviderId(MetadataProviders.MusicBrainzReleaseGroup, result.strMusicBrainzID);
item.SetProviderId(MetadataProvider.MusicBrainzAlbumArtist, result.strMusicBrainzArtistID);
item.SetProviderId(MetadataProvider.MusicBrainzReleaseGroup, result.strMusicBrainzID);
string overview = null;
@@ -172,18 +174,10 @@ namespace MediaBrowser.Providers.Plugins.AudioDb
Directory.CreateDirectory(Path.GetDirectoryName(path));
using (var httpResponse = await _httpClient.SendAsync(
new HttpRequestOptions
{
Url = url,
CancellationToken = cancellationToken
},
HttpMethod.Get).ConfigureAwait(false))
using (var response = httpResponse.Content)
using (var xmlFileStream = new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.Read, IODefaults.FileStreamBufferSize, true))
{
await response.CopyToAsync(xmlFileStream).ConfigureAwait(false);
}
using var response = await _httpClientFactory.CreateClient(NamedClient.Default).GetAsync(url, cancellationToken).ConfigureAwait(false);
await using var stream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false);
await using var xmlFileStream = new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.Read, IODefaults.FileStreamBufferSize, true);
await stream.CopyToAsync(xmlFileStream, cancellationToken).ConfigureAwait(false);
}
private static string GetAlbumDataPath(IApplicationPaths appPaths, string musicBrainzReleaseGroupId)
@@ -210,42 +204,79 @@ namespace MediaBrowser.Providers.Plugins.AudioDb
public class Album
{
public string idAlbum { get; set; }
public string idArtist { get; set; }
public string strAlbum { get; set; }
public string strArtist { get; set; }
public string intYearReleased { get; set; }
public string strGenre { get; set; }
public string strSubGenre { get; set; }
public string strReleaseFormat { get; set; }
public string intSales { get; set; }
public string strAlbumThumb { get; set; }
public string strAlbumCDart { get; set; }
public string strDescriptionEN { get; set; }
public string strDescriptionDE { get; set; }
public string strDescriptionFR { get; set; }
public string strDescriptionCN { get; set; }
public string strDescriptionIT { get; set; }
public string strDescriptionJP { get; set; }
public string strDescriptionRU { get; set; }
public string strDescriptionES { get; set; }
public string strDescriptionPT { get; set; }
public string strDescriptionSE { get; set; }
public string strDescriptionNL { get; set; }
public string strDescriptionHU { get; set; }
public string strDescriptionNO { get; set; }
public string strDescriptionIL { get; set; }
public string strDescriptionPL { get; set; }
public object intLoved { get; set; }
public object intScore { get; set; }
public string strReview { get; set; }
public object strMood { get; set; }
public object strTheme { get; set; }
public object strSpeed { get; set; }
public object strLocation { get; set; }
public string strMusicBrainzID { get; set; }
public string strMusicBrainzArtistID { get; set; }
public object strItunesID { get; set; }
public object strAmazonID { get; set; }
public string strLocked { get; set; }
}
@@ -255,7 +286,7 @@ namespace MediaBrowser.Providers.Plugins.AudioDb
}
/// <inheritdoc />
public Task<HttpResponseInfo> GetImageResponse(string url, CancellationToken cancellationToken)
public Task<HttpResponseMessage> GetImageResponse(string url, CancellationToken cancellationToken)
{
throw new NotImplementedException();
}

View File

@@ -1,4 +1,7 @@
#pragma warning disable CS1591
using System.Collections.Generic;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
using MediaBrowser.Common.Net;
@@ -15,14 +18,14 @@ namespace MediaBrowser.Providers.Plugins.AudioDb
public class AudioDbArtistImageProvider : IRemoteImageProvider, IHasOrder
{
private readonly IServerConfigurationManager _config;
private readonly IHttpClient _httpClient;
private readonly IHttpClientFactory _httpClientFactory;
private readonly IJsonSerializer _json;
public AudioDbArtistImageProvider(IServerConfigurationManager config, IJsonSerializer json, IHttpClient httpClient)
public AudioDbArtistImageProvider(IServerConfigurationManager config, IJsonSerializer json, IHttpClientFactory httpClientFactory)
{
_config = config;
_json = json;
_httpClient = httpClient;
_httpClientFactory = httpClientFactory;
}
/// <inheritdoc />
@@ -47,7 +50,7 @@ namespace MediaBrowser.Providers.Plugins.AudioDb
/// <inheritdoc />
public async Task<IEnumerable<RemoteImageInfo>> GetImages(BaseItem item, CancellationToken cancellationToken)
{
var id = item.GetProviderId(MetadataProviders.MusicBrainzArtist);
var id = item.GetProviderId(MetadataProvider.MusicBrainzArtist);
if (!string.IsNullOrWhiteSpace(id))
{
@@ -133,13 +136,10 @@ namespace MediaBrowser.Providers.Plugins.AudioDb
return list;
}
public Task<HttpResponseInfo> GetImageResponse(string url, CancellationToken cancellationToken)
public Task<HttpResponseMessage> GetImageResponse(string url, CancellationToken cancellationToken)
{
return _httpClient.GetResponse(new HttpRequestOptions
{
CancellationToken = cancellationToken,
Url = url
});
var httpClient = _httpClientFactory.CreateClient(NamedClient.Default);
return httpClient.GetAsync(url, cancellationToken);
}
/// <inheritdoc />

View File

@@ -1,3 +1,5 @@
#pragma warning disable CS1591
using System;
using System.Collections.Generic;
using System.IO;
@@ -21,25 +23,25 @@ namespace MediaBrowser.Providers.Plugins.AudioDb
{
public class AudioDbArtistProvider : IRemoteMetadataProvider<MusicArtist, ArtistInfo>, IHasOrder
{
private readonly IServerConfigurationManager _config;
private readonly IFileSystem _fileSystem;
private readonly IHttpClient _httpClient;
private readonly IJsonSerializer _json;
public static AudioDbArtistProvider Current;
private const string ApiKey = "195003";
public const string BaseUrl = "https://www.theaudiodb.com/api/v1/json/" + ApiKey;
public AudioDbArtistProvider(IServerConfigurationManager config, IFileSystem fileSystem, IHttpClient httpClient, IJsonSerializer json)
private readonly IServerConfigurationManager _config;
private readonly IFileSystem _fileSystem;
private readonly IHttpClientFactory _httpClientFactory;
private readonly IJsonSerializer _json;
public AudioDbArtistProvider(IServerConfigurationManager config, IFileSystem fileSystem, IHttpClientFactory httpClientFactory, IJsonSerializer json)
{
_config = config;
_fileSystem = fileSystem;
_httpClient = httpClient;
_httpClientFactory = httpClientFactory;
_json = json;
Current = this;
}
public static AudioDbArtistProvider Current { get; private set; }
/// <inheritdoc />
public string Name => "TheAudioDB";
@@ -85,15 +87,15 @@ namespace MediaBrowser.Providers.Plugins.AudioDb
private void ProcessResult(MusicArtist item, Artist result, string preferredLanguage)
{
//item.HomePageUrl = result.strWebsite;
// item.HomePageUrl = result.strWebsite;
if (!string.IsNullOrEmpty(result.strGenre))
{
item.Genres = new[] { result.strGenre };
}
item.SetProviderId(MetadataProviders.AudioDbArtist, result.idArtist);
item.SetProviderId(MetadataProviders.MusicBrainzArtist, result.strMusicBrainzID);
item.SetProviderId(MetadataProvider.AudioDbArtist, result.idArtist);
item.SetProviderId(MetadataProvider.MusicBrainzArtist, result.strMusicBrainzID);
string overview = null;
@@ -153,23 +155,13 @@ namespace MediaBrowser.Providers.Plugins.AudioDb
var path = GetArtistInfoPath(_config.ApplicationPaths, musicBrainzId);
using (var httpResponse = await _httpClient.SendAsync(
new HttpRequestOptions
{
Url = url,
CancellationToken = cancellationToken,
BufferContent = true
},
HttpMethod.Get).ConfigureAwait(false))
using (var response = httpResponse.Content)
{
Directory.CreateDirectory(Path.GetDirectoryName(path));
using var response = await _httpClientFactory.CreateClient(NamedClient.Default).GetAsync(url, cancellationToken).ConfigureAwait(false);
await using var stream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false);
using (var xmlFileStream = new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.Read, IODefaults.FileStreamBufferSize, true))
{
await response.CopyToAsync(xmlFileStream).ConfigureAwait(false);
}
}
Directory.CreateDirectory(Path.GetDirectoryName(path));
await using var xmlFileStream = new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.Read, IODefaults.FileStreamBufferSize, true);
await stream.CopyToAsync(xmlFileStream, cancellationToken).ConfigureAwait(false);
}
/// <summary>
@@ -199,45 +191,85 @@ namespace MediaBrowser.Providers.Plugins.AudioDb
public class Artist
{
public string idArtist { get; set; }
public string strArtist { get; set; }
public string strArtistAlternate { get; set; }
public object idLabel { get; set; }
public string intFormedYear { get; set; }
public string intBornYear { get; set; }
public object intDiedYear { get; set; }
public object strDisbanded { get; set; }
public string strGenre { get; set; }
public string strSubGenre { get; set; }
public string strWebsite { get; set; }
public string strFacebook { get; set; }
public string strTwitter { get; set; }
public string strBiographyEN { get; set; }
public string strBiographyDE { get; set; }
public string strBiographyFR { get; set; }
public string strBiographyCN { get; set; }
public string strBiographyIT { get; set; }
public string strBiographyJP { get; set; }
public string strBiographyRU { get; set; }
public string strBiographyES { get; set; }
public string strBiographyPT { get; set; }
public string strBiographySE { get; set; }
public string strBiographyNL { get; set; }
public string strBiographyHU { get; set; }
public string strBiographyNO { get; set; }
public string strBiographyIL { get; set; }
public string strBiographyPL { get; set; }
public string strGender { get; set; }
public string intMembers { get; set; }
public string strCountry { get; set; }
public string strCountryCode { get; set; }
public string strArtistThumb { get; set; }
public string strArtistLogo { get; set; }
public string strArtistFanart { get; set; }
public string strArtistFanart2 { get; set; }
public string strArtistFanart3 { get; set; }
public string strArtistBanner { get; set; }
public string strMusicBrainzID { get; set; }
public object strLastFMChart { get; set; }
public string strLocked { get; set; }
}
@@ -247,7 +279,7 @@ namespace MediaBrowser.Providers.Plugins.AudioDb
}
/// <inheritdoc />
public Task<HttpResponseInfo> GetImageResponse(string url, CancellationToken cancellationToken)
public Task<HttpResponseMessage> GetImageResponse(string url, CancellationToken cancellationToken)
{
throw new NotImplementedException();
}

View File

@@ -0,0 +1,27 @@
#pragma warning disable CS1591
using MediaBrowser.Controller.Entities.Audio;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Providers;
namespace MediaBrowser.Providers.Plugins.AudioDb
{
public class AudioDbAlbumExternalId : IExternalId
{
/// <inheritdoc />
public string ProviderName => "TheAudioDb";
/// <inheritdoc />
public string Key => MetadataProvider.AudioDbAlbum.ToString();
/// <inheritdoc />
public ExternalIdMediaType? Type => null;
/// <inheritdoc />
public string UrlFormatString => "https://www.theaudiodb.com/album/{0}";
/// <inheritdoc />
public bool Supports(IHasProviderIds item) => item is MusicAlbum;
}
}

View File

@@ -0,0 +1,27 @@
#pragma warning disable CS1591
using MediaBrowser.Controller.Entities.Audio;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Providers;
namespace MediaBrowser.Providers.Plugins.AudioDb
{
public class AudioDbArtistExternalId : IExternalId
{
/// <inheritdoc />
public string ProviderName => "TheAudioDb";
/// <inheritdoc />
public string Key => MetadataProvider.AudioDbArtist.ToString();
/// <inheritdoc />
public ExternalIdMediaType? Type => ExternalIdMediaType.Artist;
/// <inheritdoc />
public string UrlFormatString => "https://www.theaudiodb.com/artist/{0}";
/// <inheritdoc />
public bool Supports(IHasProviderIds item) => item is MusicArtist;
}
}

View File

@@ -0,0 +1,27 @@
#pragma warning disable CS1591
using MediaBrowser.Controller.Entities.Audio;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Providers;
namespace MediaBrowser.Providers.Plugins.AudioDb
{
public class AudioDbOtherAlbumExternalId : IExternalId
{
/// <inheritdoc />
public string ProviderName => "TheAudioDb";
/// <inheritdoc />
public string Key => MetadataProvider.AudioDbAlbum.ToString();
/// <inheritdoc />
public ExternalIdMediaType? Type => ExternalIdMediaType.Album;
/// <inheritdoc />
public string UrlFormatString => "https://www.theaudiodb.com/album/{0}";
/// <inheritdoc />
public bool Supports(IHasProviderIds item) => item is Audio;
}
}

View File

@@ -0,0 +1,27 @@
#pragma warning disable CS1591
using MediaBrowser.Controller.Entities.Audio;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Providers;
namespace MediaBrowser.Providers.Plugins.AudioDb
{
public class AudioDbOtherArtistExternalId : IExternalId
{
/// <inheritdoc />
public string ProviderName => "TheAudioDb";
/// <inheritdoc />
public string Key => MetadataProvider.AudioDbArtist.ToString();
/// <inheritdoc />
public ExternalIdMediaType? Type => ExternalIdMediaType.OtherArtist;
/// <inheritdoc />
public string UrlFormatString => "https://www.theaudiodb.com/artist/{0}";
/// <inheritdoc />
public bool Supports(IHasProviderIds item) => item is Audio || item is MusicAlbum;
}
}

View File

@@ -1,4 +1,6 @@
using MediaBrowser.Model.Plugins;
#pragma warning disable CS1591
using MediaBrowser.Model.Plugins;
namespace MediaBrowser.Providers.Plugins.AudioDb
{

View File

@@ -28,29 +28,31 @@
pluginId: "a629c0da-fac5-4c7e-931a-7174223f14c8"
};
$('.configPage').on('pageshow', function () {
Dashboard.showLoadingMsg();
ApiClient.getPluginConfiguration(PluginConfig.pluginId).then(function (config) {
$('#enable').checked(config.Enable);
$('#replaceAlbumName').checked(config.ReplaceAlbumName);
Dashboard.hideLoadingMsg();
});
});
$('.configForm').on('submit', function (e) {
Dashboard.showLoadingMsg();
var form = this;
ApiClient.getPluginConfiguration(PluginConfig.pluginId).then(function (config) {
config.Enable = $('#enable', form).checked();
config.ReplaceAlbumName = $('#replaceAlbumName', form).checked();
ApiClient.updatePluginConfiguration(PluginConfig.pluginId, config).then(Dashboard.processPluginConfigurationUpdateResult);
document.querySelector('.configPage')
.addEventListener('pageshow', function () {
Dashboard.showLoadingMsg();
ApiClient.getPluginConfiguration(PluginConfig.pluginId).then(function (config) {
document.querySelector('#enable').checked = config.Enable;
document.querySelector('#replaceAlbumName').checked = config.ReplaceAlbumName;
Dashboard.hideLoadingMsg();
});
});
return false;
});
document.querySelector('.configForm')
.addEventListener('submit', function (e) {
Dashboard.showLoadingMsg();
ApiClient.getPluginConfiguration(PluginConfig.pluginId).then(function (config) {
config.Enable = document.querySelector('#enable').checked;
config.ReplaceAlbumName = document.querySelector('#replaceAlbumName').checked;
ApiClient.updatePluginConfiguration(PluginConfig.pluginId, config).then(Dashboard.processPluginConfigurationUpdateResult);
});
e.preventDefault();
return false;
});
</script>
</div>
</body>

View File

@@ -1,66 +0,0 @@
using MediaBrowser.Controller.Entities.Audio;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Entities;
namespace MediaBrowser.Providers.Plugins.AudioDb
{
public class AudioDbAlbumExternalId : IExternalId
{
/// <inheritdoc />
public string Name => "TheAudioDb";
/// <inheritdoc />
public string Key => MetadataProviders.AudioDbAlbum.ToString();
/// <inheritdoc />
public string UrlFormatString => "https://www.theaudiodb.com/album/{0}";
/// <inheritdoc />
public bool Supports(IHasProviderIds item) => item is MusicAlbum;
}
public class AudioDbOtherAlbumExternalId : IExternalId
{
/// <inheritdoc />
public string Name => "TheAudioDb Album";
/// <inheritdoc />
public string Key => MetadataProviders.AudioDbAlbum.ToString();
/// <inheritdoc />
public string UrlFormatString => "https://www.theaudiodb.com/album/{0}";
/// <inheritdoc />
public bool Supports(IHasProviderIds item) => item is Audio;
}
public class AudioDbArtistExternalId : IExternalId
{
/// <inheritdoc />
public string Name => "TheAudioDb";
/// <inheritdoc />
public string Key => MetadataProviders.AudioDbArtist.ToString();
/// <inheritdoc />
public string UrlFormatString => "https://www.theaudiodb.com/artist/{0}";
/// <inheritdoc />
public bool Supports(IHasProviderIds item) => item is MusicArtist;
}
public class AudioDbOtherArtistExternalId : IExternalId
{
/// <inheritdoc />
public string Name => "TheAudioDb Artist";
/// <inheritdoc />
public string Key => MetadataProviders.AudioDbArtist.ToString();
/// <inheritdoc />
public string UrlFormatString => "https://www.theaudiodb.com/artist/{0}";
/// <inheritdoc />
public bool Supports(IHasProviderIds item) => item is Audio || item is MusicAlbum;
}
}

View File

@@ -1,4 +1,6 @@
using System;
#pragma warning disable CS1591
using System;
using System.Collections.Generic;
using MediaBrowser.Common.Configuration;
using MediaBrowser.Common.Plugins;
@@ -9,6 +11,12 @@ namespace MediaBrowser.Providers.Plugins.AudioDb
{
public class Plugin : BasePlugin<PluginConfiguration>, IHasWebPages
{
public Plugin(IApplicationPaths applicationPaths, IXmlSerializer xmlSerializer)
: base(applicationPaths, xmlSerializer)
{
Instance = this;
}
public static Plugin Instance { get; private set; }
public override Guid Id => new Guid("a629c0da-fac5-4c7e-931a-7174223f14c8");
@@ -17,11 +25,8 @@ namespace MediaBrowser.Providers.Plugins.AudioDb
public override string Description => "Get artist and album metadata or images from AudioDB.";
public Plugin(IApplicationPaths applicationPaths, IXmlSerializer xmlSerializer)
: base(applicationPaths, xmlSerializer)
{
Instance = this;
}
// TODO remove when plugin removed from server.
public override string ConfigurationFileName => "Jellyfin.Plugin.AudioDb.xml";
public IEnumerable<PluginPageInfo> GetPages()
{

View File

@@ -1,14 +1,16 @@
#pragma warning disable CS1591
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Xml;
using MediaBrowser.Common.Net;
using MediaBrowser.Controller.Entities.Audio;
using MediaBrowser.Controller.Extensions;
using MediaBrowser.Controller.Providers;
@@ -35,21 +37,19 @@ namespace MediaBrowser.Providers.Music
{
var url = "/ws/2/artist/?query=arid:{0}" + musicBrainzId.ToString(CultureInfo.InvariantCulture);
using (var response = await MusicBrainzAlbumProvider.Current.GetMusicBrainzResponse(url, cancellationToken).ConfigureAwait(false))
using (var stream = response.Content)
{
return GetResultsFromResponse(stream);
}
using var response = await MusicBrainzAlbumProvider.Current.GetMusicBrainzResponse(url, cancellationToken).ConfigureAwait(false);
await using var stream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false);
return GetResultsFromResponse(stream);
}
else
{
// They seem to throw bad request failures on any term with a slash
var nameToSearch = searchInfo.Name.Replace('/', ' ');
var url = string.Format("/ws/2/artist/?query=\"{0}\"&dismax=true", UrlEncode(nameToSearch));
var url = string.Format(CultureInfo.InvariantCulture, "/ws/2/artist/?query=\"{0}\"&dismax=true", UrlEncode(nameToSearch));
using (var response = await MusicBrainzAlbumProvider.Current.GetMusicBrainzResponse(url, cancellationToken).ConfigureAwait(false))
using (var stream = response.Content)
await using (var stream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false))
{
var results = GetResultsFromResponse(stream).ToList();
@@ -62,15 +62,11 @@ namespace MediaBrowser.Providers.Music
if (HasDiacritics(searchInfo.Name))
{
// Try again using the search with accent characters url
url = string.Format("/ws/2/artist/?query=artistaccent:\"{0}\"", UrlEncode(nameToSearch));
url = string.Format(CultureInfo.InvariantCulture, "/ws/2/artist/?query=artistaccent:\"{0}\"", UrlEncode(nameToSearch));
using (var response = await MusicBrainzAlbumProvider.Current.GetMusicBrainzResponse(url, cancellationToken).ConfigureAwait(false))
{
using (var stream = response.Content)
{
return GetResultsFromResponse(stream);
}
}
using var response = await MusicBrainzAlbumProvider.Current.GetMusicBrainzResponse(url, cancellationToken).ConfigureAwait(false);
await using var stream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false);
return GetResultsFromResponse(stream);
}
}
@@ -108,11 +104,13 @@ namespace MediaBrowser.Providers.Music
reader.Read();
continue;
}
using (var subReader = reader.ReadSubtree())
{
return ParseArtistList(subReader).ToList();
}
}
default:
{
reader.Skip();
@@ -150,6 +148,7 @@ namespace MediaBrowser.Providers.Music
reader.Read();
continue;
}
var mbzId = reader.GetAttribute("id");
using (var subReader = reader.ReadSubtree())
@@ -160,8 +159,10 @@ namespace MediaBrowser.Providers.Music
yield return artist;
}
}
break;
}
default:
{
reader.Skip();
@@ -202,6 +203,7 @@ namespace MediaBrowser.Providers.Music
result.Overview = reader.ReadElementContentAsString();
break;
}
default:
{
// there is sort-name if ever needed
@@ -216,7 +218,7 @@ namespace MediaBrowser.Providers.Music
}
}
result.SetProviderId(MetadataProviders.MusicBrainzArtist, artistId);
result.SetProviderId(MetadataProvider.MusicBrainzArtist, artistId);
if (string.IsNullOrWhiteSpace(artistId) || string.IsNullOrWhiteSpace(result.Name))
{
@@ -249,7 +251,7 @@ namespace MediaBrowser.Providers.Music
if (singleResult != null)
{
musicBrainzId = singleResult.GetProviderId(MetadataProviders.MusicBrainzArtist);
musicBrainzId = singleResult.GetProviderId(MetadataProvider.MusicBrainzArtist);
result.Item.Overview = singleResult.Overview;
if (Plugin.Instance.Configuration.ReplaceArtistName)
@@ -262,7 +264,7 @@ namespace MediaBrowser.Providers.Music
if (!string.IsNullOrWhiteSpace(musicBrainzId))
{
result.HasMetadata = true;
result.Item.SetProviderId(MetadataProviders.MusicBrainzArtist, musicBrainzId);
result.Item.SetProviderId(MetadataProvider.MusicBrainzArtist, musicBrainzId);
}
return result;
@@ -290,7 +292,7 @@ namespace MediaBrowser.Providers.Music
public string Name => "MusicBrainz";
public Task<HttpResponseInfo> GetImageResponse(string url, CancellationToken cancellationToken)
public Task<HttpResponseMessage> GetImageResponse(string url, CancellationToken cancellationToken)
{
throw new NotImplementedException();
}

View File

@@ -1,4 +1,6 @@
using MediaBrowser.Model.Plugins;
#pragma warning disable CS1591
using MediaBrowser.Model.Plugins;
namespace MediaBrowser.Providers.Plugins.MusicBrainz
{

View File

@@ -36,33 +36,47 @@
uniquePluginId: "8c95c4d2-e50c-4fb0-a4f3-6c06ff0f9a1a"
};
$('.musicBrainzConfigPage').on('pageshow', function () {
Dashboard.showLoadingMsg();
ApiClient.getPluginConfiguration(MusicBrainzPluginConfig.uniquePluginId).then(function (config) {
$('#server').val(config.Server).change();
$('#rateLimit').val(config.RateLimit).change();
$('#enable').checked(config.Enable);
$('#replaceArtistName').checked(config.ReplaceArtistName);
document.querySelector('.musicBrainzConfigPage')
.addEventListener('pageshow', function () {
Dashboard.showLoadingMsg();
ApiClient.getPluginConfiguration(MusicBrainzPluginConfig.uniquePluginId).then(function (config) {
var server = document.querySelector('#server');
server.value = config.Server;
server.dispatchEvent(new Event('change', {
bubbles: true,
cancelable: false
}));
var rateLimit = document.querySelector('#rateLimit');
rateLimit.value = config.RateLimit;
rateLimit.dispatchEvent(new Event('change', {
bubbles: true,
cancelable: false
}));
document.querySelector('#enable').checked = config.Enable;
document.querySelector('#replaceArtistName').checked = config.ReplaceArtistName;
Dashboard.hideLoadingMsg();
Dashboard.hideLoadingMsg();
});
});
});
$('.musicBrainzConfigForm').on('submit', function (e) {
Dashboard.showLoadingMsg();
var form = this;
ApiClient.getPluginConfiguration(MusicBrainzPluginConfig.uniquePluginId).then(function (config) {
config.Server = $('#server', form).val();
config.RateLimit = $('#rateLimit', form).val();
config.Enable = $('#enable', form).checked();
config.ReplaceArtistName = $('#replaceArtistName', form).checked();
ApiClient.updatePluginConfiguration(MusicBrainzPluginConfig.uniquePluginId, config).then(Dashboard.processPluginConfigurationUpdateResult);
document.querySelector('.musicBrainzConfigForm')
.addEventListener('submit', function (e) {
Dashboard.showLoadingMsg();
ApiClient.getPluginConfiguration(MusicBrainzPluginConfig.uniquePluginId).then(function (config) {
config.Server = document.querySelector('#server').value;
config.RateLimit = document.querySelector('#rateLimit').value;
config.Enable = document.querySelector('#enable').checked;
config.ReplaceArtistName = document.querySelector('#replaceArtistName').checked;
ApiClient.updatePluginConfiguration(MusicBrainzPluginConfig.uniquePluginId, config).then(Dashboard.processPluginConfigurationUpdateResult);
});
e.preventDefault();
return false;
});
return false;
});
</script>
</div>
</body>

View File

@@ -1,6 +1,9 @@
#pragma warning disable CS1591
using MediaBrowser.Controller.Entities.Audio;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Providers;
using MediaBrowser.Providers.Plugins.MusicBrainz;
namespace MediaBrowser.Providers.Music
@@ -8,10 +11,13 @@ namespace MediaBrowser.Providers.Music
public class MusicBrainzReleaseGroupExternalId : IExternalId
{
/// <inheritdoc />
public string Name => "MusicBrainz Release Group";
public string ProviderName => "MusicBrainz";
/// <inheritdoc />
public string Key => MetadataProviders.MusicBrainzReleaseGroup.ToString();
public string Key => MetadataProvider.MusicBrainzReleaseGroup.ToString();
/// <inheritdoc />
public ExternalIdMediaType? Type => ExternalIdMediaType.ReleaseGroup;
/// <inheritdoc />
public string UrlFormatString => Plugin.Instance.Configuration.Server + "/release-group/{0}";
@@ -23,10 +29,13 @@ namespace MediaBrowser.Providers.Music
public class MusicBrainzAlbumArtistExternalId : IExternalId
{
/// <inheritdoc />
public string Name => "MusicBrainz Album Artist";
public string ProviderName => "MusicBrainz";
/// <inheritdoc />
public string Key => MetadataProviders.MusicBrainzAlbumArtist.ToString();
public string Key => MetadataProvider.MusicBrainzAlbumArtist.ToString();
/// <inheritdoc />
public ExternalIdMediaType? Type => ExternalIdMediaType.AlbumArtist;
/// <inheritdoc />
public string UrlFormatString => Plugin.Instance.Configuration.Server + "/artist/{0}";
@@ -38,10 +47,13 @@ namespace MediaBrowser.Providers.Music
public class MusicBrainzAlbumExternalId : IExternalId
{
/// <inheritdoc />
public string Name => "MusicBrainz Album";
public string ProviderName => "MusicBrainz";
/// <inheritdoc />
public string Key => MetadataProviders.MusicBrainzAlbum.ToString();
public string Key => MetadataProvider.MusicBrainzAlbum.ToString();
/// <inheritdoc />
public ExternalIdMediaType? Type => ExternalIdMediaType.Album;
/// <inheritdoc />
public string UrlFormatString => Plugin.Instance.Configuration.Server + "/release/{0}";
@@ -53,10 +65,13 @@ namespace MediaBrowser.Providers.Music
public class MusicBrainzArtistExternalId : IExternalId
{
/// <inheritdoc />
public string Name => "MusicBrainz";
public string ProviderName => "MusicBrainz";
/// <inheritdoc />
public string Key => MetadataProviders.MusicBrainzArtist.ToString();
public string Key => MetadataProvider.MusicBrainzArtist.ToString();
/// <inheritdoc />
public ExternalIdMediaType? Type => ExternalIdMediaType.Artist;
/// <inheritdoc />
public string UrlFormatString => Plugin.Instance.Configuration.Server + "/artist/{0}";
@@ -68,11 +83,14 @@ namespace MediaBrowser.Providers.Music
public class MusicBrainzOtherArtistExternalId : IExternalId
{
/// <inheritdoc />
public string Name => "MusicBrainz Artist";
public string ProviderName => "MusicBrainz";
/// <inheritdoc />
public string Key => MetadataProviders.MusicBrainzArtist.ToString();
public string Key => MetadataProvider.MusicBrainzArtist.ToString();
/// <inheritdoc />
public ExternalIdMediaType? Type => ExternalIdMediaType.OtherArtist;
/// <inheritdoc />
public string UrlFormatString => Plugin.Instance.Configuration.Server + "/artist/{0}";
@@ -84,10 +102,13 @@ namespace MediaBrowser.Providers.Music
public class MusicBrainzTrackId : IExternalId
{
/// <inheritdoc />
public string Name => "MusicBrainz Track";
public string ProviderName => "MusicBrainz";
/// <inheritdoc />
public string Key => MetadataProviders.MusicBrainzTrack.ToString();
public string Key => MetadataProvider.MusicBrainzTrack.ToString();
/// <inheritdoc />
public ExternalIdMediaType? Type => ExternalIdMediaType.Track;
/// <inheritdoc />
public string UrlFormatString => Plugin.Instance.Configuration.Server + "/track/{0}";

View File

@@ -1,3 +1,5 @@
#pragma warning disable CS1591
using System;
using System.Collections.Generic;
using System.Diagnostics;
@@ -23,6 +25,13 @@ namespace MediaBrowser.Providers.Music
{
public class MusicBrainzAlbumProvider : IRemoteMetadataProvider<MusicAlbum, AlbumInfo>, IHasOrder
{
/// <summary>
/// For each single MB lookup/search, this is the maximum number of
/// attempts that shall be made whilst receiving a 503 Server
/// Unavailable (indicating throttled) response.
/// </summary>
private const uint MusicBrainzQueryAttempts = 5u;
/// <summary>
/// The Jellyfin user-agent is unrestricted but source IP must not exceed
/// one request per second, therefore we rate limit to avoid throttling.
@@ -31,29 +40,20 @@ namespace MediaBrowser.Providers.Music
/// </summary>
private readonly long _musicBrainzQueryIntervalMs;
/// <summary>
/// For each single MB lookup/search, this is the maximum number of
/// attempts that shall be made whilst receiving a 503 Server
/// Unavailable (indicating throttled) response.
/// </summary>
private const uint MusicBrainzQueryAttempts = 5u;
internal static MusicBrainzAlbumProvider Current;
private readonly IHttpClient _httpClient;
private readonly IHttpClientFactory _httpClientFactory;
private readonly IApplicationHost _appHost;
private readonly ILogger _logger;
private readonly ILogger<MusicBrainzAlbumProvider> _logger;
private readonly string _musicBrainzBaseUrl;
private Stopwatch _stopWatchMusicBrainz = new Stopwatch();
public MusicBrainzAlbumProvider(
IHttpClient httpClient,
IHttpClientFactory httpClientFactory,
IApplicationHost appHost,
ILogger<MusicBrainzAlbumProvider> logger)
{
_httpClient = httpClient;
_httpClientFactory = httpClientFactory;
_appHost = appHost;
_logger = logger;
@@ -66,6 +66,8 @@ namespace MediaBrowser.Providers.Music
Current = this;
}
internal static MusicBrainzAlbumProvider Current { get; private set; }
/// <inheritdoc />
public string Name => "MusicBrainz";
@@ -109,7 +111,7 @@ namespace MediaBrowser.Providers.Music
else
{
// I'm sure there is a better way but for now it resolves search for 12" Mixes
var queryName = searchInfo.Name.Replace("\"", string.Empty);
var queryName = searchInfo.Name.Replace("\"", string.Empty, StringComparison.Ordinal);
url = string.Format(
CultureInfo.InvariantCulture,
@@ -121,11 +123,9 @@ namespace MediaBrowser.Providers.Music
if (!string.IsNullOrWhiteSpace(url))
{
using (var response = await GetMusicBrainzResponse(url, cancellationToken).ConfigureAwait(false))
using (var stream = response.Content)
{
return GetResultsFromResponse(stream);
}
using var response = await GetMusicBrainzResponse(url, cancellationToken).ConfigureAwait(false);
await using var stream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false);
return GetResultsFromResponse(stream);
}
return Enumerable.Empty<RemoteSearchResult>();
@@ -163,17 +163,17 @@ namespace MediaBrowser.Providers.Music
Name = i.Artists[0].Item1
};
result.AlbumArtist.SetProviderId(MetadataProviders.MusicBrainzArtist, i.Artists[0].Item2);
result.AlbumArtist.SetProviderId(MetadataProvider.MusicBrainzArtist, i.Artists[0].Item2);
}
if (!string.IsNullOrWhiteSpace(i.ReleaseId))
{
result.SetProviderId(MetadataProviders.MusicBrainzAlbum, i.ReleaseId);
result.SetProviderId(MetadataProvider.MusicBrainzAlbum, i.ReleaseId);
}
if (!string.IsNullOrWhiteSpace(i.ReleaseGroupId))
{
result.SetProviderId(MetadataProviders.MusicBrainzReleaseGroup, i.ReleaseGroupId);
result.SetProviderId(MetadataProvider.MusicBrainzReleaseGroup, i.ReleaseGroupId);
}
return result;
@@ -247,12 +247,12 @@ namespace MediaBrowser.Providers.Music
{
if (!string.IsNullOrEmpty(releaseId))
{
result.Item.SetProviderId(MetadataProviders.MusicBrainzAlbum, releaseId);
result.Item.SetProviderId(MetadataProvider.MusicBrainzAlbum, releaseId);
}
if (!string.IsNullOrEmpty(releaseGroupId))
{
result.Item.SetProviderId(MetadataProviders.MusicBrainzReleaseGroup, releaseGroupId);
result.Item.SetProviderId(MetadataProvider.MusicBrainzReleaseGroup, releaseGroupId);
}
}
@@ -276,27 +276,25 @@ namespace MediaBrowser.Providers.Music
private async Task<ReleaseResult> GetReleaseResult(string albumName, string artistId, CancellationToken cancellationToken)
{
var url = string.Format("/ws/2/release/?query=\"{0}\" AND arid:{1}",
var url = string.Format(
CultureInfo.InvariantCulture,
"/ws/2/release/?query=\"{0}\" AND arid:{1}",
WebUtility.UrlEncode(albumName),
artistId);
using (var response = await GetMusicBrainzResponse(url, cancellationToken).ConfigureAwait(false))
using (var stream = response.Content)
using (var oReader = new StreamReader(stream, Encoding.UTF8))
using var response = await GetMusicBrainzResponse(url, cancellationToken).ConfigureAwait(false);
await using var stream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false);
using var oReader = new StreamReader(stream, Encoding.UTF8);
var settings = new XmlReaderSettings
{
var settings = new XmlReaderSettings()
{
ValidationType = ValidationType.None,
CheckCharacters = false,
IgnoreProcessingInstructions = true,
IgnoreComments = true
};
ValidationType = ValidationType.None,
CheckCharacters = false,
IgnoreProcessingInstructions = true,
IgnoreComments = true
};
using (var reader = XmlReader.Create(oReader, settings))
{
return ReleaseResult.Parse(reader).FirstOrDefault();
}
}
using var reader = XmlReader.Create(oReader, settings);
return ReleaseResult.Parse(reader).FirstOrDefault();
}
private async Task<ReleaseResult> GetReleaseResultByArtistName(string albumName, string artistName, CancellationToken cancellationToken)
@@ -307,23 +305,19 @@ namespace MediaBrowser.Providers.Music
WebUtility.UrlEncode(albumName),
WebUtility.UrlEncode(artistName));
using (var response = await GetMusicBrainzResponse(url, cancellationToken).ConfigureAwait(false))
using (var stream = response.Content)
using (var oReader = new StreamReader(stream, Encoding.UTF8))
using var response = await GetMusicBrainzResponse(url, cancellationToken).ConfigureAwait(false);
await using var stream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false);
using var oReader = new StreamReader(stream, Encoding.UTF8);
var settings = new XmlReaderSettings()
{
var settings = new XmlReaderSettings()
{
ValidationType = ValidationType.None,
CheckCharacters = false,
IgnoreProcessingInstructions = true,
IgnoreComments = true
};
ValidationType = ValidationType.None,
CheckCharacters = false,
IgnoreProcessingInstructions = true,
IgnoreComments = true
};
using (var reader = XmlReader.Create(oReader, settings))
{
return ReleaseResult.Parse(reader).FirstOrDefault();
}
}
using var reader = XmlReader.Create(oReader, settings);
return ReleaseResult.Parse(reader).FirstOrDefault();
}
private class ReleaseResult
@@ -361,6 +355,7 @@ namespace MediaBrowser.Providers.Music
return ParseReleaseList(subReader).ToList();
}
}
default:
{
reader.Skip();
@@ -396,6 +391,7 @@ namespace MediaBrowser.Providers.Music
reader.Read();
continue;
}
var releaseId = reader.GetAttribute("id");
using (var subReader = reader.ReadSubtree())
@@ -406,8 +402,10 @@ namespace MediaBrowser.Providers.Music
yield return release;
}
}
break;
}
default:
{
reader.Skip();
@@ -453,6 +451,7 @@ namespace MediaBrowser.Providers.Music
{
result.Year = date.Year;
}
break;
}
case "annotation":
@@ -480,6 +479,7 @@ namespace MediaBrowser.Providers.Music
break;
}
default:
{
reader.Skip();
@@ -497,7 +497,7 @@ namespace MediaBrowser.Providers.Music
}
}
private static ValueTuple<string, string> ParseArtistCredit(XmlReader reader)
private static (string, string) ParseArtistCredit(XmlReader reader)
{
reader.MoveToContent();
reader.Read();
@@ -518,6 +518,7 @@ namespace MediaBrowser.Providers.Music
return ParseArtistNameCredit(subReader);
}
}
default:
{
reader.Skip();
@@ -531,7 +532,7 @@ namespace MediaBrowser.Providers.Music
}
}
return new ValueTuple<string, string>();
return default;
}
private static (string, string) ParseArtistNameCredit(XmlReader reader)
@@ -556,6 +557,7 @@ namespace MediaBrowser.Providers.Music
return ParseArtistArtistCredit(subReader, id);
}
}
default:
{
reader.Skip();
@@ -593,6 +595,7 @@ namespace MediaBrowser.Providers.Music
name = reader.ReadElementContentAsString();
break;
}
default:
{
reader.Skip();
@@ -613,30 +616,21 @@ namespace MediaBrowser.Providers.Music
{
var url = "/ws/2/release?release-group=" + releaseGroupId.ToString(CultureInfo.InvariantCulture);
using (var response = await GetMusicBrainzResponse(url, cancellationToken).ConfigureAwait(false))
using (var stream = response.Content)
using (var oReader = new StreamReader(stream, Encoding.UTF8))
using var response = await GetMusicBrainzResponse(url, cancellationToken).ConfigureAwait(false);
await using var stream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false);
using var oReader = new StreamReader(stream, Encoding.UTF8);
var settings = new XmlReaderSettings
{
var settings = new XmlReaderSettings()
{
ValidationType = ValidationType.None,
CheckCharacters = false,
IgnoreProcessingInstructions = true,
IgnoreComments = true
};
ValidationType = ValidationType.None,
CheckCharacters = false,
IgnoreProcessingInstructions = true,
IgnoreComments = true
};
using (var reader = XmlReader.Create(oReader, settings))
{
var result = ReleaseResult.Parse(reader).FirstOrDefault();
using var reader = XmlReader.Create(oReader, settings);
var result = ReleaseResult.Parse(reader).FirstOrDefault();
if (result != null)
{
return result.ReleaseId;
}
}
}
return null;
return result?.ReleaseId;
}
/// <summary>
@@ -649,57 +643,57 @@ namespace MediaBrowser.Providers.Music
{
var url = "/ws/2/release-group/?query=reid:" + releaseEntryId.ToString(CultureInfo.InvariantCulture);
using (var response = await GetMusicBrainzResponse(url, cancellationToken).ConfigureAwait(false))
using (var stream = response.Content)
using (var oReader = new StreamReader(stream, Encoding.UTF8))
using var response = await GetMusicBrainzResponse(url, cancellationToken).ConfigureAwait(false);
await using var stream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false);
using var oReader = new StreamReader(stream, Encoding.UTF8);
var settings = new XmlReaderSettings
{
var settings = new XmlReaderSettings()
{
ValidationType = ValidationType.None,
CheckCharacters = false,
IgnoreProcessingInstructions = true,
IgnoreComments = true
};
ValidationType = ValidationType.None,
CheckCharacters = false,
IgnoreProcessingInstructions = true,
IgnoreComments = true
};
using (var reader = XmlReader.Create(oReader, settings))
{
reader.MoveToContent();
reader.Read();
using (var reader = XmlReader.Create(oReader, settings))
{
reader.MoveToContent();
reader.Read();
// Loop through each element
while (!reader.EOF && reader.ReadState == ReadState.Interactive)
// Loop through each element
while (!reader.EOF && reader.ReadState == ReadState.Interactive)
{
if (reader.NodeType == XmlNodeType.Element)
{
if (reader.NodeType == XmlNodeType.Element)
switch (reader.Name)
{
switch (reader.Name)
case "release-group-list":
{
case "release-group-list":
{
if (reader.IsEmptyElement)
{
reader.Read();
continue;
}
using (var subReader = reader.ReadSubtree())
{
return GetFirstReleaseGroupId(subReader);
}
}
default:
{
reader.Skip();
break;
}
if (reader.IsEmptyElement)
{
reader.Read();
continue;
}
using (var subReader = reader.ReadSubtree())
{
return GetFirstReleaseGroupId(subReader);
}
}
default:
{
reader.Skip();
break;
}
}
else
{
reader.Read();
}
}
return null;
else
{
reader.Read();
}
}
return null;
}
}
@@ -719,6 +713,7 @@ namespace MediaBrowser.Providers.Music
{
return reader.GetAttribute("id");
}
default:
{
reader.Skip();
@@ -741,23 +736,19 @@ namespace MediaBrowser.Providers.Music
/// A number of retries shall be made in order to try and satisfy the request before
/// giving up and returning null.
/// </summary>
internal async Task<HttpResponseInfo> GetMusicBrainzResponse(string url, CancellationToken cancellationToken)
internal async Task<HttpResponseMessage> GetMusicBrainzResponse(string url, CancellationToken cancellationToken)
{
var options = new HttpRequestOptions
{
Url = _musicBrainzBaseUrl.TrimEnd('/') + url,
CancellationToken = cancellationToken,
// MusicBrainz request a contact email address is supplied, as comment, in user agent field:
// https://musicbrainz.org/doc/XML_Web_Service/Rate_Limiting#User-Agent
UserAgent = string.Format(
CultureInfo.InvariantCulture,
"{0} ( {1} )",
_appHost.ApplicationUserAgent,
_appHost.ApplicationUserAgentAddress),
BufferContent = false
};
using var options = new HttpRequestMessage(HttpMethod.Get, _musicBrainzBaseUrl.TrimEnd('/') + url);
HttpResponseInfo response;
// MusicBrainz request a contact email address is supplied, as comment, in user agent field:
// https://musicbrainz.org/doc/XML_Web_Service/Rate_Limiting#User-Agent
options.Headers.UserAgent.ParseAdd(string.Format(
CultureInfo.InvariantCulture,
"{0} ( {1} )",
_appHost.ApplicationUserAgent,
_appHost.ApplicationUserAgentAddress));
HttpResponseMessage response;
var attempts = 0u;
do
@@ -776,7 +767,7 @@ namespace MediaBrowser.Providers.Music
_logger.LogDebug("GetMusicBrainzResponse: Time since previous request: {0} ms", _stopWatchMusicBrainz.ElapsedMilliseconds);
_stopWatchMusicBrainz.Restart();
response = await _httpClient.SendAsync(options, HttpMethod.Get).ConfigureAwait(false);
response = await _httpClientFactory.CreateClient(NamedClient.Default).SendAsync(options).ConfigureAwait(false);
// We retry a finite number of times, and only whilst MB is indicating 503 (throttling)
}
@@ -785,14 +776,14 @@ namespace MediaBrowser.Providers.Music
// Log error if unable to query MB database due to throttling
if (attempts == MusicBrainzQueryAttempts && response.StatusCode == HttpStatusCode.ServiceUnavailable)
{
_logger.LogError("GetMusicBrainzResponse: 503 Service Unavailable (throttled) response received {0} times whilst requesting {1}", attempts, options.Url);
_logger.LogError("GetMusicBrainzResponse: 503 Service Unavailable (throttled) response received {0} times whilst requesting {1}", attempts, options.RequestUri);
}
return response;
}
/// <inheritdoc />
public Task<HttpResponseInfo> GetImageResponse(string url, CancellationToken cancellationToken)
public Task<HttpResponseMessage> GetImageResponse(string url, CancellationToken cancellationToken)
{
throw new NotImplementedException();
}

View File

@@ -1,4 +1,6 @@
using System;
#pragma warning disable CS1591
using System;
using System.Collections.Generic;
using MediaBrowser.Common.Configuration;
using MediaBrowser.Common.Plugins;
@@ -21,6 +23,9 @@ namespace MediaBrowser.Providers.Plugins.MusicBrainz
public const long DefaultRateLimit = 2000u;
// TODO remove when plugin removed from server.
public override string ConfigurationFileName => "Jellyfin.Plugin.MusicBrainz.xml";
public Plugin(IApplicationPaths applicationPaths, IXmlSerializer xmlSerializer)
: base(applicationPaths, xmlSerializer)
{

View File

@@ -0,0 +1,11 @@
#pragma warning disable CS1591
using MediaBrowser.Model.Plugins;
namespace MediaBrowser.Providers.Plugins.Omdb
{
public class PluginConfiguration : BasePluginConfiguration
{
public bool CastAndCrew { get; set; }
}
}

View File

@@ -0,0 +1,52 @@
<!DOCTYPE html>
<html>
<head>
<title>OMDb</title>
</head>
<body>
<div data-role="page" class="page type-interior pluginConfigurationPage configPage" data-require="emby-input,emby-button,emby-checkbox">
<div data-role="content">
<div class="content-primary">
<form class="configForm">
<label class="checkboxContainer">
<input is="emby-checkbox" type="checkbox" id="castAndCrew" />
<span>Collect information about the cast and other crew members from OMDb.</span>
</label>
<br />
<div>
<button is="emby-button" type="submit" class="raised button-submit block"><span>Save</span></button>
</div>
</form>
</div>
</div>
<script type="text/javascript">
var PluginConfig = {
pluginId: "a628c0da-fac5-4c7e-9d1a-7134223f14c8"
};
document.querySelector('.configPage')
.addEventListener('pageshow', function () {
Dashboard.showLoadingMsg();
ApiClient.getPluginConfiguration(PluginConfig.pluginId).then(function (config) {
document.querySelector('#castAndCrew').checked = config.CastAndCrew;
Dashboard.hideLoadingMsg();
});
});
document.querySelector('.configForm')
.addEventListener('submit', function (e) {
Dashboard.showLoadingMsg();
ApiClient.getPluginConfiguration(PluginConfig.pluginId).then(function (config) {
config.CastAndCrew = document.querySelector('#castAndCrew').checked;
ApiClient.updatePluginConfiguration(PluginConfig.pluginId, config).then(Dashboard.processPluginConfigurationUpdateResult);
});
e.preventDefault();
return false;
});
</script>
</div>
</body>
</html>

View File

@@ -1,8 +1,10 @@
#pragma warning disable CS1591
using System.Collections.Generic;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
using MediaBrowser.Common;
using MediaBrowser.Common.Net;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Entities.TV;
using MediaBrowser.Controller.Library;
@@ -11,31 +13,39 @@ using MediaBrowser.Model.Entities;
using MediaBrowser.Model.IO;
using MediaBrowser.Model.Providers;
using MediaBrowser.Model.Serialization;
using Microsoft.Extensions.Logging;
namespace MediaBrowser.Providers.Plugins.Omdb
{
public class OmdbEpisodeProvider :
IRemoteMetadataProvider<Episode, EpisodeInfo>,
IHasOrder
public class OmdbEpisodeProvider : IRemoteMetadataProvider<Episode, EpisodeInfo>, IHasOrder
{
private readonly IJsonSerializer _jsonSerializer;
private readonly IHttpClient _httpClient;
private readonly IHttpClientFactory _httpClientFactory;
private readonly OmdbItemProvider _itemProvider;
private readonly IFileSystem _fileSystem;
private readonly IServerConfigurationManager _configurationManager;
private readonly IApplicationHost _appHost;
public OmdbEpisodeProvider(IJsonSerializer jsonSerializer, IApplicationHost appHost, IHttpClient httpClient, ILogger logger, ILibraryManager libraryManager, IFileSystem fileSystem, IServerConfigurationManager configurationManager)
public OmdbEpisodeProvider(
IJsonSerializer jsonSerializer,
IApplicationHost appHost,
IHttpClientFactory httpClientFactory,
ILibraryManager libraryManager,
IFileSystem fileSystem,
IServerConfigurationManager configurationManager)
{
_jsonSerializer = jsonSerializer;
_httpClient = httpClient;
_httpClientFactory = httpClientFactory;
_fileSystem = fileSystem;
_configurationManager = configurationManager;
_appHost = appHost;
_itemProvider = new OmdbItemProvider(jsonSerializer, _appHost, httpClient, logger, libraryManager, fileSystem, configurationManager);
_itemProvider = new OmdbItemProvider(jsonSerializer, _appHost, httpClientFactory, libraryManager, fileSystem, configurationManager);
}
// After TheTvDb
public int Order => 1;
public string Name => "The Open Movie Database";
public Task<IEnumerable<RemoteSearchResult>> GetSearchResults(EpisodeInfo searchInfo, CancellationToken cancellationToken)
{
return _itemProvider.GetSearchResults(searchInfo, "episode", cancellationToken);
@@ -55,23 +65,19 @@ namespace MediaBrowser.Providers.Plugins.Omdb
return result;
}
if (info.SeriesProviderIds.TryGetValue(MetadataProviders.Imdb.ToString(), out string seriesImdbId) && !string.IsNullOrEmpty(seriesImdbId))
if (info.SeriesProviderIds.TryGetValue(MetadataProvider.Imdb.ToString(), out string seriesImdbId) && !string.IsNullOrEmpty(seriesImdbId))
{
if (info.IndexNumber.HasValue && info.ParentIndexNumber.HasValue)
{
result.HasMetadata = await new OmdbProvider(_jsonSerializer, _httpClient, _fileSystem, _appHost, _configurationManager)
.FetchEpisodeData(result, info.IndexNumber.Value, info.ParentIndexNumber.Value, info.GetProviderId(MetadataProviders.Imdb), seriesImdbId, info.MetadataLanguage, info.MetadataCountryCode, cancellationToken).ConfigureAwait(false);
result.HasMetadata = await new OmdbProvider(_jsonSerializer, _httpClientFactory, _fileSystem, _appHost, _configurationManager)
.FetchEpisodeData(result, info.IndexNumber.Value, info.ParentIndexNumber.Value, info.GetProviderId(MetadataProvider.Imdb), seriesImdbId, info.MetadataLanguage, info.MetadataCountryCode, cancellationToken).ConfigureAwait(false);
}
}
return result;
}
// After TheTvDb
public int Order => 1;
public string Name => "The Open Movie Database";
public Task<HttpResponseInfo> GetImageResponse(string url, CancellationToken cancellationToken)
public Task<HttpResponseMessage> GetImageResponse(string url, CancellationToken cancellationToken)
{
return _itemProvider.GetImageResponse(url, cancellationToken);
}

View File

@@ -1,4 +1,8 @@
#pragma warning disable CS1591
using System.Collections.Generic;
using System.Net.Http;
using System.Globalization;
using System.Threading;
using System.Threading.Tasks;
using MediaBrowser.Common;
@@ -17,21 +21,27 @@ namespace MediaBrowser.Providers.Plugins.Omdb
{
public class OmdbImageProvider : IRemoteImageProvider, IHasOrder
{
private readonly IHttpClient _httpClient;
private readonly IHttpClientFactory _httpClientFactory;
private readonly IJsonSerializer _jsonSerializer;
private readonly IFileSystem _fileSystem;
private readonly IServerConfigurationManager _configurationManager;
private readonly IApplicationHost _appHost;
public OmdbImageProvider(IJsonSerializer jsonSerializer, IApplicationHost appHost, IHttpClient httpClient, IFileSystem fileSystem, IServerConfigurationManager configurationManager)
public OmdbImageProvider(IJsonSerializer jsonSerializer, IApplicationHost appHost, IHttpClientFactory httpClientFactory, IFileSystem fileSystem, IServerConfigurationManager configurationManager)
{
_jsonSerializer = jsonSerializer;
_httpClient = httpClient;
_httpClientFactory = httpClientFactory;
_fileSystem = fileSystem;
_configurationManager = configurationManager;
_appHost = appHost;
}
public string Name => "The Open Movie Database";
// After other internet providers, because they're better
// But before fallback providers like screengrab
public int Order => 90;
public IEnumerable<ImageType> GetSupportedImages(BaseItem item)
{
return new List<ImageType>
@@ -42,11 +52,11 @@ namespace MediaBrowser.Providers.Plugins.Omdb
public async Task<IEnumerable<RemoteImageInfo>> GetImages(BaseItem item, CancellationToken cancellationToken)
{
var imdbId = item.GetProviderId(MetadataProviders.Imdb);
var imdbId = item.GetProviderId(MetadataProvider.Imdb);
var list = new List<RemoteImageInfo>();
var provider = new OmdbProvider(_jsonSerializer, _httpClient, _fileSystem, _appHost, _configurationManager);
var provider = new OmdbProvider(_jsonSerializer, _httpClientFactory, _fileSystem, _appHost, _configurationManager);
if (!string.IsNullOrWhiteSpace(imdbId))
{
@@ -68,7 +78,7 @@ namespace MediaBrowser.Providers.Plugins.Omdb
list.Add(new RemoteImageInfo
{
ProviderName = Name,
Url = string.Format("https://img.omdbapi.com/?i={0}&apikey=2c9d9507", imdbId)
Url = string.Format(CultureInfo.InvariantCulture, "https://img.omdbapi.com/?i={0}&apikey=2c9d9507", imdbId)
});
}
}
@@ -77,23 +87,14 @@ namespace MediaBrowser.Providers.Plugins.Omdb
return list;
}
public Task<HttpResponseInfo> GetImageResponse(string url, CancellationToken cancellationToken)
public Task<HttpResponseMessage> GetImageResponse(string url, CancellationToken cancellationToken)
{
return _httpClient.GetResponse(new HttpRequestOptions
{
CancellationToken = cancellationToken,
Url = url
});
return _httpClientFactory.CreateClient(NamedClient.Default).GetAsync(url, cancellationToken);
}
public string Name => "The Open Movie Database";
public bool Supports(BaseItem item)
{
return item is Movie || item is Trailer || item is Episode;
}
// After other internet providers, because they're better
// But before fallback providers like screengrab
public int Order => 90;
}
}

View File

@@ -1,8 +1,11 @@
#pragma warning disable CS1591
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
using MediaBrowser.Common;
@@ -17,7 +20,6 @@ using MediaBrowser.Model.Entities;
using MediaBrowser.Model.IO;
using MediaBrowser.Model.Providers;
using MediaBrowser.Model.Serialization;
using Microsoft.Extensions.Logging;
namespace MediaBrowser.Providers.Plugins.Omdb
{
@@ -25,23 +27,30 @@ namespace MediaBrowser.Providers.Plugins.Omdb
IRemoteMetadataProvider<Movie, MovieInfo>, IRemoteMetadataProvider<Trailer, TrailerInfo>, IHasOrder
{
private readonly IJsonSerializer _jsonSerializer;
private readonly IHttpClient _httpClient;
private readonly ILogger _logger;
private readonly IHttpClientFactory _httpClientFactory;
private readonly ILibraryManager _libraryManager;
private readonly IFileSystem _fileSystem;
private readonly IServerConfigurationManager _configurationManager;
private readonly IApplicationHost _appHost;
public OmdbItemProvider(IJsonSerializer jsonSerializer, IApplicationHost appHost, IHttpClient httpClient, ILogger logger, ILibraryManager libraryManager, IFileSystem fileSystem, IServerConfigurationManager configurationManager)
public OmdbItemProvider(
IJsonSerializer jsonSerializer,
IApplicationHost appHost,
IHttpClientFactory httpClientFactory,
ILibraryManager libraryManager,
IFileSystem fileSystem,
IServerConfigurationManager configurationManager)
{
_jsonSerializer = jsonSerializer;
_httpClient = httpClient;
_logger = logger;
_httpClientFactory = httpClientFactory;
_libraryManager = libraryManager;
_fileSystem = fileSystem;
_configurationManager = configurationManager;
_appHost = appHost;
}
public string Name => "The Open Movie Database";
// After primary option
public int Order => 2;
@@ -64,12 +73,12 @@ namespace MediaBrowser.Providers.Plugins.Omdb
{
var episodeSearchInfo = searchInfo as EpisodeInfo;
var imdbId = searchInfo.GetProviderId(MetadataProviders.Imdb);
var imdbId = searchInfo.GetProviderId(MetadataProvider.Imdb);
var urlQuery = "plot=full&r=json";
if (type == "episode" && episodeSearchInfo != null)
{
episodeSearchInfo.SeriesProviderIds.TryGetValue(MetadataProviders.Imdb.ToString(), out imdbId);
episodeSearchInfo.SeriesProviderIds.TryGetValue(MetadataProvider.Imdb.ToString(), out imdbId);
}
var name = searchInfo.Name;
@@ -80,7 +89,7 @@ namespace MediaBrowser.Providers.Plugins.Omdb
var parsedName = _libraryManager.ParseName(name);
var yearInName = parsedName.Year;
name = parsedName.Name;
year = year ?? yearInName;
year ??= yearInName;
}
if (string.IsNullOrWhiteSpace(imdbId))
@@ -99,6 +108,7 @@ namespace MediaBrowser.Providers.Plugins.Omdb
{
urlQuery += "&t=" + WebUtility.UrlEncode(name);
}
urlQuery += "&type=" + type;
}
else
@@ -113,75 +123,72 @@ namespace MediaBrowser.Providers.Plugins.Omdb
{
urlQuery += string.Format(CultureInfo.InvariantCulture, "&Episode={0}", searchInfo.IndexNumber);
}
if (searchInfo.ParentIndexNumber.HasValue)
{
urlQuery += string.Format(CultureInfo.InvariantCulture, "&Season={0}", searchInfo.ParentIndexNumber);
}
}
var url = OmdbProvider.GetOmdbUrl(urlQuery, _appHost, cancellationToken);
var url = OmdbProvider.GetOmdbUrl(urlQuery);
using (var response = await OmdbProvider.GetOmdbResponse(_httpClient, url, cancellationToken).ConfigureAwait(false))
using var response = await OmdbProvider.GetOmdbResponse(_httpClientFactory.CreateClient(NamedClient.Default), url, cancellationToken).ConfigureAwait(false);
await using var stream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false);
var resultList = new List<SearchResult>();
if (isSearch)
{
using (var stream = response.Content)
var searchResultList = await _jsonSerializer.DeserializeFromStreamAsync<SearchResultList>(stream).ConfigureAwait(false);
if (searchResultList != null && searchResultList.Search != null)
{
var resultList = new List<SearchResult>();
if (isSearch)
{
var searchResultList = await _jsonSerializer.DeserializeFromStreamAsync<SearchResultList>(stream).ConfigureAwait(false);
if (searchResultList != null && searchResultList.Search != null)
{
resultList.AddRange(searchResultList.Search);
}
}
else
{
var result = await _jsonSerializer.DeserializeFromStreamAsync<SearchResult>(stream).ConfigureAwait(false);
if (string.Equals(result.Response, "true", StringComparison.OrdinalIgnoreCase))
{
resultList.Add(result);
}
}
return resultList.Select(result =>
{
var item = new RemoteSearchResult
{
IndexNumber = searchInfo.IndexNumber,
Name = result.Title,
ParentIndexNumber = searchInfo.ParentIndexNumber,
SearchProviderName = Name
};
if (episodeSearchInfo != null && episodeSearchInfo.IndexNumberEnd.HasValue)
{
item.IndexNumberEnd = episodeSearchInfo.IndexNumberEnd.Value;
}
item.SetProviderId(MetadataProviders.Imdb, result.imdbID);
if (result.Year.Length > 0
&& int.TryParse(result.Year.Substring(0, Math.Min(result.Year.Length, 4)), NumberStyles.Integer, CultureInfo.InvariantCulture, out var parsedYear))
{
item.ProductionYear = parsedYear;
}
if (!string.IsNullOrEmpty(result.Released)
&& DateTime.TryParse(result.Released, CultureInfo.InvariantCulture, DateTimeStyles.AllowWhiteSpaces, out var released))
{
item.PremiereDate = released;
}
if (!string.IsNullOrWhiteSpace(result.Poster) && !string.Equals(result.Poster, "N/A", StringComparison.OrdinalIgnoreCase))
{
item.ImageUrl = result.Poster;
}
return item;
});
resultList.AddRange(searchResultList.Search);
}
}
else
{
var result = await _jsonSerializer.DeserializeFromStreamAsync<SearchResult>(stream).ConfigureAwait(false);
if (string.Equals(result.Response, "true", StringComparison.OrdinalIgnoreCase))
{
resultList.Add(result);
}
}
return resultList.Select(result =>
{
var item = new RemoteSearchResult
{
IndexNumber = searchInfo.IndexNumber,
Name = result.Title,
ParentIndexNumber = searchInfo.ParentIndexNumber,
SearchProviderName = Name
};
if (episodeSearchInfo != null && episodeSearchInfo.IndexNumberEnd.HasValue)
{
item.IndexNumberEnd = episodeSearchInfo.IndexNumberEnd.Value;
}
item.SetProviderId(MetadataProvider.Imdb, result.imdbID);
if (result.Year.Length > 0
&& int.TryParse(result.Year.AsSpan().Slice(0, Math.Min(result.Year.Length, 4)), NumberStyles.Integer, CultureInfo.InvariantCulture, out var parsedYear))
{
item.ProductionYear = parsedYear;
}
if (!string.IsNullOrEmpty(result.Released)
&& DateTime.TryParse(result.Released, CultureInfo.InvariantCulture, DateTimeStyles.AllowWhiteSpaces, out var released))
{
item.PremiereDate = released;
}
if (!string.IsNullOrWhiteSpace(result.Poster) && !string.Equals(result.Poster, "N/A", StringComparison.OrdinalIgnoreCase))
{
item.ImageUrl = result.Poster;
}
return item;
});
}
public Task<MetadataResult<Trailer>> GetMetadata(TrailerInfo info, CancellationToken cancellationToken)
@@ -194,8 +201,6 @@ namespace MediaBrowser.Providers.Plugins.Omdb
return GetSearchResults(searchInfo, "movie", cancellationToken);
}
public string Name => "The Open Movie Database";
public async Task<MetadataResult<Series>> GetMetadata(SeriesInfo info, CancellationToken cancellationToken)
{
var result = new MetadataResult<Series>
@@ -204,7 +209,7 @@ namespace MediaBrowser.Providers.Plugins.Omdb
QueriedById = true
};
var imdbId = info.GetProviderId(MetadataProviders.Imdb);
var imdbId = info.GetProviderId(MetadataProvider.Imdb);
if (string.IsNullOrWhiteSpace(imdbId))
{
imdbId = await GetSeriesImdbId(info, cancellationToken).ConfigureAwait(false);
@@ -213,10 +218,10 @@ namespace MediaBrowser.Providers.Plugins.Omdb
if (!string.IsNullOrEmpty(imdbId))
{
result.Item.SetProviderId(MetadataProviders.Imdb, imdbId);
result.Item.SetProviderId(MetadataProvider.Imdb, imdbId);
result.HasMetadata = true;
await new OmdbProvider(_jsonSerializer, _httpClient, _fileSystem, _appHost, _configurationManager).Fetch(result, imdbId, info.MetadataLanguage, info.MetadataCountryCode, cancellationToken).ConfigureAwait(false);
await new OmdbProvider(_jsonSerializer, _httpClientFactory, _fileSystem, _appHost, _configurationManager).Fetch(result, imdbId, info.MetadataLanguage, info.MetadataCountryCode, cancellationToken).ConfigureAwait(false);
}
return result;
@@ -236,7 +241,7 @@ namespace MediaBrowser.Providers.Plugins.Omdb
QueriedById = true
};
var imdbId = info.GetProviderId(MetadataProviders.Imdb);
var imdbId = info.GetProviderId(MetadataProvider.Imdb);
if (string.IsNullOrWhiteSpace(imdbId))
{
imdbId = await GetMovieImdbId(info, cancellationToken).ConfigureAwait(false);
@@ -245,10 +250,10 @@ namespace MediaBrowser.Providers.Plugins.Omdb
if (!string.IsNullOrEmpty(imdbId))
{
result.Item.SetProviderId(MetadataProviders.Imdb, imdbId);
result.Item.SetProviderId(MetadataProvider.Imdb, imdbId);
result.HasMetadata = true;
await new OmdbProvider(_jsonSerializer, _httpClient, _fileSystem, _appHost, _configurationManager).Fetch(result, imdbId, info.MetadataLanguage, info.MetadataCountryCode, cancellationToken).ConfigureAwait(false);
await new OmdbProvider(_jsonSerializer, _httpClientFactory, _fileSystem, _appHost, _configurationManager).Fetch(result, imdbId, info.MetadataLanguage, info.MetadataCountryCode, cancellationToken).ConfigureAwait(false);
}
return result;
@@ -258,49 +263,67 @@ namespace MediaBrowser.Providers.Plugins.Omdb
{
var results = await GetSearchResultsInternal(info, "movie", false, cancellationToken).ConfigureAwait(false);
var first = results.FirstOrDefault();
return first == null ? null : first.GetProviderId(MetadataProviders.Imdb);
return first?.GetProviderId(MetadataProvider.Imdb);
}
private async Task<string> GetSeriesImdbId(SeriesInfo info, CancellationToken cancellationToken)
{
var results = await GetSearchResultsInternal(info, "series", false, cancellationToken).ConfigureAwait(false);
var first = results.FirstOrDefault();
return first == null ? null : first.GetProviderId(MetadataProviders.Imdb);
return first?.GetProviderId(MetadataProvider.Imdb);
}
public Task<HttpResponseInfo> GetImageResponse(string url, CancellationToken cancellationToken)
public Task<HttpResponseMessage> GetImageResponse(string url, CancellationToken cancellationToken)
{
return _httpClient.GetResponse(new HttpRequestOptions
{
CancellationToken = cancellationToken,
Url = url
});
return _httpClientFactory.CreateClient(NamedClient.Default).GetAsync(url, cancellationToken);
}
class SearchResult
private class SearchResult
{
public string Title { get; set; }
public string Year { get; set; }
public string Rated { get; set; }
public string Released { get; set; }
public string Season { get; set; }
public string Episode { get; set; }
public string Runtime { get; set; }
public string Genre { get; set; }
public string Director { get; set; }
public string Writer { get; set; }
public string Actors { get; set; }
public string Plot { get; set; }
public string Language { get; set; }
public string Country { get; set; }
public string Awards { get; set; }
public string Poster { get; set; }
public string Metascore { get; set; }
public string imdbRating { get; set; }
public string imdbVotes { get; set; }
public string imdbID { get; set; }
public string seriesID { get; set; }
public string Type { get; set; }
public string Response { get; set; }
}
@@ -312,6 +335,5 @@ namespace MediaBrowser.Providers.Plugins.Omdb
/// <value>The results.</value>
public List<SearchResult> Search { get; set; }
}
}
}

View File

@@ -1,3 +1,5 @@
#pragma warning disable CS1591
using System;
using System.Collections.Generic;
using System.Globalization;
@@ -23,14 +25,14 @@ namespace MediaBrowser.Providers.Plugins.Omdb
private readonly IJsonSerializer _jsonSerializer;
private readonly IFileSystem _fileSystem;
private readonly IServerConfigurationManager _configurationManager;
private readonly IHttpClient _httpClient;
private readonly IHttpClientFactory _httpClientFactory;
private readonly CultureInfo _usCulture = new CultureInfo("en-US");
private readonly IApplicationHost _appHost;
public OmdbProvider(IJsonSerializer jsonSerializer, IHttpClient httpClient, IFileSystem fileSystem, IApplicationHost appHost, IServerConfigurationManager configurationManager)
public OmdbProvider(IJsonSerializer jsonSerializer, IHttpClientFactory httpClientFactory, IFileSystem fileSystem, IApplicationHost appHost, IServerConfigurationManager configurationManager)
{
_jsonSerializer = jsonSerializer;
_httpClient = httpClient;
_httpClientFactory = httpClientFactory;
_fileSystem = fileSystem;
_configurationManager = configurationManager;
_appHost = appHost;
@@ -60,7 +62,7 @@ namespace MediaBrowser.Providers.Plugins.Omdb
}
if (!string.IsNullOrEmpty(result.Year) && result.Year.Length >= 4
&& int.TryParse(result.Year.Substring(0, 4), NumberStyles.Number, _usCulture, out var year)
&& int.TryParse(result.Year.AsSpan().Slice(0, 4), NumberStyles.Number, _usCulture, out var year)
&& year >= 0)
{
item.ProductionYear = year;
@@ -77,7 +79,7 @@ namespace MediaBrowser.Providers.Plugins.Omdb
&& int.TryParse(result.imdbVotes, NumberStyles.Number, _usCulture, out var voteCount)
&& voteCount >= 0)
{
//item.VoteCount = voteCount;
// item.VoteCount = voteCount;
}
if (!string.IsNullOrEmpty(result.imdbRating)
@@ -87,14 +89,14 @@ namespace MediaBrowser.Providers.Plugins.Omdb
item.CommunityRating = imdbRating;
}
//if (!string.IsNullOrEmpty(result.Website))
//{
// item.HomePageUrl = result.Website;
//}
if (!string.IsNullOrEmpty(result.Website))
{
item.HomePageUrl = result.Website;
}
if (!string.IsNullOrWhiteSpace(result.imdbID))
{
item.SetProviderId(MetadataProviders.Imdb, result.imdbID);
item.SetProviderId(MetadataProvider.Imdb, result.imdbID);
}
ParseAdditionalMetadata(itemResult, result);
@@ -121,7 +123,7 @@ namespace MediaBrowser.Providers.Plugins.Omdb
if (!string.IsNullOrWhiteSpace(episodeImdbId))
{
foreach (var episode in (seasonResult.Episodes ?? new RootObject[] { }))
foreach (var episode in seasonResult.Episodes)
{
if (string.Equals(episodeImdbId, episode.imdbID, StringComparison.OrdinalIgnoreCase))
{
@@ -134,7 +136,7 @@ namespace MediaBrowser.Providers.Plugins.Omdb
// finally, search by numbers
if (result == null)
{
foreach (var episode in (seasonResult.Episodes ?? new RootObject[] { }))
foreach (var episode in seasonResult.Episodes)
{
if (episode.Episode == episodeNumber)
{
@@ -161,7 +163,7 @@ namespace MediaBrowser.Providers.Plugins.Omdb
}
if (!string.IsNullOrEmpty(result.Year) && result.Year.Length >= 4
&& int.TryParse(result.Year.Substring(0, 4), NumberStyles.Number, _usCulture, out var year)
&& int.TryParse(result.Year.AsSpan().Slice(0, 4), NumberStyles.Number, _usCulture, out var year)
&& year >= 0)
{
item.ProductionYear = year;
@@ -178,7 +180,7 @@ namespace MediaBrowser.Providers.Plugins.Omdb
&& int.TryParse(result.imdbVotes, NumberStyles.Number, _usCulture, out var voteCount)
&& voteCount >= 0)
{
//item.VoteCount = voteCount;
// item.VoteCount = voteCount;
}
if (!string.IsNullOrEmpty(result.imdbRating)
@@ -188,14 +190,14 @@ namespace MediaBrowser.Providers.Plugins.Omdb
item.CommunityRating = imdbRating;
}
//if (!string.IsNullOrEmpty(result.Website))
//{
// item.HomePageUrl = result.Website;
//}
if (!string.IsNullOrEmpty(result.Website))
{
item.HomePageUrl = result.Website;
}
if (!string.IsNullOrWhiteSpace(result.imdbID))
{
item.SetProviderId(MetadataProviders.Imdb, result.imdbID);
item.SetProviderId(MetadataProvider.Imdb, result.imdbID);
}
ParseAdditionalMetadata(itemResult, result);
@@ -243,7 +245,7 @@ namespace MediaBrowser.Providers.Plugins.Omdb
internal static bool IsValidSeries(Dictionary<string, string> seriesProviderIds)
{
if (seriesProviderIds.TryGetValue(MetadataProviders.Imdb.ToString(), out string id) && !string.IsNullOrEmpty(id))
if (seriesProviderIds.TryGetValue(MetadataProvider.Imdb.ToString(), out string id) && !string.IsNullOrEmpty(id))
{
// This check should ideally never be necessary but we're seeing some cases of this and haven't tracked them down yet.
if (!string.IsNullOrWhiteSpace(id))
@@ -255,15 +257,16 @@ namespace MediaBrowser.Providers.Plugins.Omdb
return false;
}
public static string GetOmdbUrl(string query, IApplicationHost appHost, CancellationToken cancellationToken)
public static string GetOmdbUrl(string query)
{
const string url = "https://www.omdbapi.com?apikey=2c9d9507";
const string Url = "https://www.omdbapi.com?apikey=2c9d9507";
if (string.IsNullOrWhiteSpace(query))
{
return url;
return Url;
}
return url + "&" + query;
return Url + "&" + query;
}
private async Task<string> EnsureItemInfo(string imdbId, CancellationToken cancellationToken)
@@ -288,17 +291,17 @@ namespace MediaBrowser.Providers.Plugins.Omdb
}
}
var url = GetOmdbUrl(string.Format("i={0}&plot=short&tomatoes=true&r=json", imdbParam), _appHost, cancellationToken);
var url = GetOmdbUrl(
string.Format(
CultureInfo.InvariantCulture,
"i={0}&plot=short&tomatoes=true&r=json",
imdbParam));
using (var response = await GetOmdbResponse(_httpClient, url, cancellationToken).ConfigureAwait(false))
{
using (var stream = response.Content)
{
var rootObject = await _jsonSerializer.DeserializeFromStreamAsync<RootObject>(stream).ConfigureAwait(false);
Directory.CreateDirectory(Path.GetDirectoryName(path));
_jsonSerializer.SerializeToFile(rootObject, path);
}
}
using var response = await GetOmdbResponse(_httpClientFactory.CreateClient(NamedClient.Default), url, cancellationToken).ConfigureAwait(false);
await using var stream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false);
var rootObject = await _jsonSerializer.DeserializeFromStreamAsync<RootObject>(stream).ConfigureAwait(false);
Directory.CreateDirectory(Path.GetDirectoryName(path));
_jsonSerializer.SerializeToFile(rootObject, path);
return path;
}
@@ -325,30 +328,25 @@ namespace MediaBrowser.Providers.Plugins.Omdb
}
}
var url = GetOmdbUrl(string.Format("i={0}&season={1}&detail=full", imdbParam, seasonId), _appHost, cancellationToken);
var url = GetOmdbUrl(
string.Format(
CultureInfo.InvariantCulture,
"i={0}&season={1}&detail=full",
imdbParam,
seasonId));
using (var response = await GetOmdbResponse(_httpClient, url, cancellationToken).ConfigureAwait(false))
{
using (var stream = response.Content)
{
var rootObject = await _jsonSerializer.DeserializeFromStreamAsync<SeasonRootObject>(stream).ConfigureAwait(false);
Directory.CreateDirectory(Path.GetDirectoryName(path));
_jsonSerializer.SerializeToFile(rootObject, path);
}
}
using var response = await GetOmdbResponse(_httpClientFactory.CreateClient(NamedClient.Default), url, cancellationToken).ConfigureAwait(false);
await using var stream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false);
var rootObject = await _jsonSerializer.DeserializeFromStreamAsync<SeasonRootObject>(stream).ConfigureAwait(false);
Directory.CreateDirectory(Path.GetDirectoryName(path));
_jsonSerializer.SerializeToFile(rootObject, path);
return path;
}
public static Task<HttpResponseInfo> GetOmdbResponse(IHttpClient httpClient, string url, CancellationToken cancellationToken)
public static Task<HttpResponseMessage> GetOmdbResponse(HttpClient httpClient, string url, CancellationToken cancellationToken)
{
return httpClient.SendAsync(new HttpRequestOptions
{
Url = url,
CancellationToken = cancellationToken,
BufferContent = true,
EnableDefaultUserAgent = true
}, HttpMethod.Get);
return httpClient.GetAsync(url, cancellationToken);
}
internal string GetDataFilePath(string imdbId)
@@ -360,7 +358,7 @@ namespace MediaBrowser.Providers.Plugins.Omdb
var dataPath = Path.Combine(_configurationManager.ApplicationPaths.CachePath, "omdb");
var filename = string.Format("{0}.json", imdbId);
var filename = string.Format(CultureInfo.InvariantCulture, "{0}.json", imdbId);
return Path.Combine(dataPath, filename);
}
@@ -374,7 +372,7 @@ namespace MediaBrowser.Providers.Plugins.Omdb
var dataPath = Path.Combine(_configurationManager.ApplicationPaths.CachePath, "omdb");
var filename = string.Format("{0}_season_{1}.json", imdbId, seasonId);
var filename = string.Format(CultureInfo.InvariantCulture, "{0}_season_{1}.json", imdbId, seasonId);
return Path.Combine(dataPath, filename);
}
@@ -386,7 +384,7 @@ namespace MediaBrowser.Providers.Plugins.Omdb
var isConfiguredForEnglish = IsConfiguredForEnglish(item) || _configurationManager.Configuration.EnableNewOmdbSupport;
// Grab series genres because imdb data is better than tvdb. Leave movies alone
// Grab series genres because IMDb data is better than TVDB. Leave movies alone
// But only do it if english is the preferred language because this data will not be localized
if (isConfiguredForEnglish && !string.IsNullOrWhiteSpace(result.Genre))
{
@@ -407,45 +405,50 @@ namespace MediaBrowser.Providers.Plugins.Omdb
item.Overview = result.Plot;
}
//if (!string.IsNullOrWhiteSpace(result.Director))
//{
// var person = new PersonInfo
// {
// Name = result.Director.Trim(),
// Type = PersonType.Director
// };
if (!Plugin.Instance.Configuration.CastAndCrew)
{
return;
}
// itemResult.AddPerson(person);
//}
if (!string.IsNullOrWhiteSpace(result.Director))
{
var person = new PersonInfo
{
Name = result.Director.Trim(),
Type = PersonType.Director
};
//if (!string.IsNullOrWhiteSpace(result.Writer))
//{
// var person = new PersonInfo
// {
// Name = result.Director.Trim(),
// Type = PersonType.Writer
// };
itemResult.AddPerson(person);
}
// itemResult.AddPerson(person);
//}
if (!string.IsNullOrWhiteSpace(result.Writer))
{
var person = new PersonInfo
{
Name = result.Director.Trim(),
Type = PersonType.Writer
};
//if (!string.IsNullOrWhiteSpace(result.Actors))
//{
// var actorList = result.Actors.Split(',');
// foreach (var actor in actorList)
// {
// if (!string.IsNullOrWhiteSpace(actor))
// {
// var person = new PersonInfo
// {
// Name = actor.Trim(),
// Type = PersonType.Actor
// };
itemResult.AddPerson(person);
}
// itemResult.AddPerson(person);
// }
// }
//}
if (!string.IsNullOrWhiteSpace(result.Actors))
{
var actorList = result.Actors.Split(',');
foreach (var actor in actorList)
{
if (!string.IsNullOrWhiteSpace(actor))
{
var person = new PersonInfo
{
Name = actor.Trim(),
Type = PersonType.Actor
};
itemResult.AddPerson(person);
}
}
}
}
private bool IsConfiguredForEnglish(BaseItem item)
@@ -459,40 +462,70 @@ namespace MediaBrowser.Providers.Plugins.Omdb
internal class SeasonRootObject
{
public string Title { get; set; }
public string seriesID { get; set; }
public int Season { get; set; }
public int? totalSeasons { get; set; }
public RootObject[] Episodes { get; set; }
public string Response { get; set; }
}
internal class RootObject
{
public string Title { get; set; }
public string Year { get; set; }
public string Rated { get; set; }
public string Released { get; set; }
public string Runtime { get; set; }
public string Genre { get; set; }
public string Director { get; set; }
public string Writer { get; set; }
public string Actors { get; set; }
public string Plot { get; set; }
public string Language { get; set; }
public string Country { get; set; }
public string Awards { get; set; }
public string Poster { get; set; }
public List<OmdbRating> Ratings { get; set; }
public string Metascore { get; set; }
public string imdbRating { get; set; }
public string imdbVotes { get; set; }
public string imdbID { get; set; }
public string Type { get; set; }
public string DVD { get; set; }
public string BoxOffice { get; set; }
public string Production { get; set; }
public string Website { get; set; }
public string Response { get; set; }
public int Episode { get; set; }
public float? GetRottenTomatoScore()
@@ -509,12 +542,15 @@ namespace MediaBrowser.Providers.Plugins.Omdb
}
}
}
return null;
}
}
public class OmdbRating
{
public string Source { get; set; }
public string Value { get; set; }
}
}

View File

@@ -0,0 +1,40 @@
#pragma warning disable CS1591
using System;
using System.Collections.Generic;
using MediaBrowser.Common.Configuration;
using MediaBrowser.Common.Plugins;
using MediaBrowser.Model.Plugins;
using MediaBrowser.Model.Serialization;
namespace MediaBrowser.Providers.Plugins.Omdb
{
public class Plugin : BasePlugin<PluginConfiguration>, IHasWebPages
{
public static Plugin Instance { get; private set; }
public override Guid Id => new Guid("a628c0da-fac5-4c7e-9d1a-7134223f14c8");
public override string Name => "OMDb";
public override string Description => "Get metadata for movies and other video content from OMDb.";
// TODO remove when plugin removed from server.
public override string ConfigurationFileName => "Jellyfin.Plugin.Omdb.xml";
public Plugin(IApplicationPaths applicationPaths, IXmlSerializer xmlSerializer)
: base(applicationPaths, xmlSerializer)
{
Instance = this;
}
public IEnumerable<PluginPageInfo> GetPages()
{
yield return new PluginPageInfo
{
Name = Name,
EmbeddedResourcePath = GetType().Namespace + ".Configuration.config.html"
};
}
}
}

View File

@@ -0,0 +1,10 @@
#pragma warning disable CS1591
using MediaBrowser.Model.Plugins;
namespace MediaBrowser.Providers.Plugins.TheTvdb
{
public class PluginConfiguration : BasePluginConfiguration
{
}
}

View File

@@ -0,0 +1,29 @@
#pragma warning disable CS1591
using System;
using MediaBrowser.Common.Configuration;
using MediaBrowser.Common.Plugins;
using MediaBrowser.Model.Serialization;
namespace MediaBrowser.Providers.Plugins.TheTvdb
{
public class Plugin : BasePlugin<PluginConfiguration>
{
public static Plugin Instance { get; private set; }
public override Guid Id => new Guid("a677c0da-fac5-4cde-941a-7134223f14c8");
public override string Name => "TheTVDB";
public override string Description => "Get metadata for movies and other video content from TheTVDB.";
// TODO remove when plugin removed from server.
public override string ConfigurationFileName => "Jellyfin.Plugin.TheTvdb.xml";
public Plugin(IApplicationPaths applicationPaths, IXmlSerializer xmlSerializer)
: base(applicationPaths, xmlSerializer)
{
Instance = this;
}
}
}

View File

@@ -1,7 +1,11 @@
#pragma warning disable CS1591
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Threading;
using System.Threading.Tasks;
using MediaBrowser.Controller.Providers;
@@ -16,7 +20,6 @@ namespace MediaBrowser.Providers.Plugins.TheTvdb
{
private const string DefaultLanguage = "en";
private readonly SemaphoreSlim _cacheWriteLock = new SemaphoreSlim(1, 1);
private readonly IMemoryCache _cache;
private readonly TvDbClient _tvDbClient;
private DateTime _tokenCreatedAt;
@@ -120,6 +123,7 @@ namespace MediaBrowser.Providers.Plugins.TheTvdb
var cacheKey = GenerateKey("series", zap2ItId, language);
return TryGetValue(cacheKey, language, () => TvDbClient.Search.SearchSeriesByZap2ItIdAsync(zap2ItId, cancellationToken));
}
public Task<TvDbResponse<Actor[]>> GetActorsAsync(
int tvdbId,
string language,
@@ -172,7 +176,7 @@ namespace MediaBrowser.Providers.Plugins.TheTvdb
string language,
CancellationToken cancellationToken)
{
searchInfo.SeriesProviderIds.TryGetValue(MetadataProviders.Tvdb.ToString(),
searchInfo.SeriesProviderIds.TryGetValue(nameof(MetadataProvider.Tvdb),
out var seriesTvdbId);
var episodeQuery = new EpisodeQuery();
@@ -190,7 +194,7 @@ namespace MediaBrowser.Providers.Plugins.TheTvdb
episodeQuery.AbsoluteNumber = searchInfo.IndexNumber.Value;
break;
default:
//aired order
// aired order
episodeQuery.AiredEpisode = searchInfo.IndexNumber.Value;
episodeQuery.AiredSeason = searchInfo.ParentIndexNumber.Value;
break;
@@ -199,10 +203,10 @@ namespace MediaBrowser.Providers.Plugins.TheTvdb
else if (searchInfo.PremiereDate.HasValue)
{
// tvdb expects yyyy-mm-dd format
episodeQuery.FirstAired = searchInfo.PremiereDate.Value.ToString("yyyy-MM-dd");
episodeQuery.FirstAired = searchInfo.PremiereDate.Value.ToString("yyyy-MM-dd", CultureInfo.InvariantCulture);
}
return GetEpisodeTvdbId(Convert.ToInt32(seriesTvdbId), episodeQuery, language, cancellationToken);
return GetEpisodeTvdbId(Convert.ToInt32(seriesTvdbId, CultureInfo.InvariantCulture), episodeQuery, language, cancellationToken);
}
public async Task<string> GetEpisodeTvdbId(
@@ -214,7 +218,7 @@ namespace MediaBrowser.Providers.Plugins.TheTvdb
var episodePage =
await GetEpisodesPageAsync(Convert.ToInt32(seriesTvdbId), episodeQuery, language, cancellationToken)
.ConfigureAwait(false);
return episodePage.Data.FirstOrDefault()?.Id.ToString();
return episodePage.Data.FirstOrDefault()?.Id.ToString(CultureInfo.InvariantCulture);
}
public Task<TvDbResponse<EpisodeRecord[]>> GetEpisodesPageAsync(
@@ -226,6 +230,45 @@ namespace MediaBrowser.Providers.Plugins.TheTvdb
return GetEpisodesPageAsync(tvdbId, 1, episodeQuery, language, cancellationToken);
}
public async IAsyncEnumerable<KeyType> GetImageKeyTypesForSeriesAsync(int tvdbId, string language, [EnumeratorCancellation] CancellationToken cancellationToken)
{
var cacheKey = GenerateKey(nameof(TvDbClient.Series.GetImagesSummaryAsync), tvdbId);
var imagesSummary = await TryGetValue(cacheKey, language, () => TvDbClient.Series.GetImagesSummaryAsync(tvdbId, cancellationToken)).ConfigureAwait(false);
if (imagesSummary.Data.Fanart > 0)
{
yield return KeyType.Fanart;
}
if (imagesSummary.Data.Series > 0)
{
yield return KeyType.Series;
}
if (imagesSummary.Data.Poster > 0)
{
yield return KeyType.Poster;
}
}
public async IAsyncEnumerable<KeyType> GetImageKeyTypesForSeasonAsync(int tvdbId, string language, [EnumeratorCancellation] CancellationToken cancellationToken)
{
var cacheKey = GenerateKey(nameof(TvDbClient.Series.GetImagesSummaryAsync), tvdbId);
var imagesSummary = await TryGetValue(cacheKey, language, () => TvDbClient.Series.GetImagesSummaryAsync(tvdbId, cancellationToken)).ConfigureAwait(false);
if (imagesSummary.Data.Season > 0)
{
yield return KeyType.Season;
}
if (imagesSummary.Data.Fanart > 0)
{
yield return KeyType.Fanart;
}
// TODO seasonwide is not supported in TvDbSharper
}
private async Task<T> TryGetValue<T>(string key, string language, Func<Task<T>> resultFactory)
{
if (_cache.TryGetValue(key, out T cachedValue))
@@ -233,23 +276,10 @@ namespace MediaBrowser.Providers.Plugins.TheTvdb
return cachedValue;
}
await _cacheWriteLock.WaitAsync().ConfigureAwait(false);
try
{
if (_cache.TryGetValue(key, out cachedValue))
{
return cachedValue;
}
_tvDbClient.AcceptedLanguage = TvdbUtils.NormalizeLanguage(language) ?? DefaultLanguage;
var result = await resultFactory.Invoke().ConfigureAwait(false);
_cache.Set(key, result, TimeSpan.FromHours(1));
return result;
}
finally
{
_cacheWriteLock.Release();
}
_tvDbClient.AcceptedLanguage = TvdbUtils.NormalizeLanguage(language) ?? DefaultLanguage;
var result = await resultFactory.Invoke().ConfigureAwait(false);
_cache.Set(key, result, TimeSpan.FromHours(1));
return result;
}
private static string GenerateKey(params object[] objects)

View File

@@ -1,5 +1,9 @@
#pragma warning disable CS1591
using System;
using System.Collections.Generic;
using System.Net.Http;
using System.Globalization;
using System.Threading;
using System.Threading.Tasks;
using MediaBrowser.Common.Net;
@@ -16,13 +20,13 @@ namespace MediaBrowser.Providers.Plugins.TheTvdb
{
public class TvdbEpisodeImageProvider : IRemoteImageProvider
{
private readonly IHttpClient _httpClient;
private readonly ILogger _logger;
private readonly IHttpClientFactory _httpClientFactory;
private readonly ILogger<TvdbEpisodeImageProvider> _logger;
private readonly TvdbClientManager _tvdbClientManager;
public TvdbEpisodeImageProvider(IHttpClient httpClient, ILogger<TvdbEpisodeImageProvider> logger, TvdbClientManager tvdbClientManager)
public TvdbEpisodeImageProvider(IHttpClientFactory httpClientFactory, ILogger<TvdbEpisodeImageProvider> logger, TvdbClientManager tvdbClientManager)
{
_httpClient = httpClient;
_httpClientFactory = httpClientFactory;
_logger = logger;
_tvdbClientManager = tvdbClientManager;
}
@@ -68,13 +72,13 @@ namespace MediaBrowser.Providers.Plugins.TheTvdb
"Episode {SeasonNumber}x{EpisodeNumber} not found for series {SeriesTvdbId}",
episodeInfo.ParentIndexNumber,
episodeInfo.IndexNumber,
series.GetProviderId(MetadataProviders.Tvdb));
series.GetProviderId(MetadataProvider.Tvdb));
return imageResult;
}
var episodeResult =
await _tvdbClientManager
.GetEpisodesAsync(Convert.ToInt32(episodeTvdbId), language, cancellationToken)
.GetEpisodesAsync(Convert.ToInt32(episodeTvdbId, CultureInfo.InvariantCulture), language, cancellationToken)
.ConfigureAwait(false);
var image = GetImageInfo(episodeResult.Data);
@@ -85,7 +89,7 @@ namespace MediaBrowser.Providers.Plugins.TheTvdb
}
catch (TvDbServerException e)
{
_logger.LogError(e, "Failed to retrieve episode images for series {TvDbId}", series.GetProviderId(MetadataProviders.Tvdb));
_logger.LogError(e, "Failed to retrieve episode images for series {TvDbId}", series.GetProviderId(MetadataProvider.Tvdb));
}
}
@@ -101,8 +105,8 @@ namespace MediaBrowser.Providers.Plugins.TheTvdb
return new RemoteImageInfo
{
Width = Convert.ToInt32(episode.ThumbWidth),
Height = Convert.ToInt32(episode.ThumbHeight),
Width = Convert.ToInt32(episode.ThumbWidth, CultureInfo.InvariantCulture),
Height = Convert.ToInt32(episode.ThumbHeight, CultureInfo.InvariantCulture),
ProviderName = Name,
Url = TvdbUtils.BannerUrl + episode.Filename,
Type = ImageType.Primary
@@ -111,13 +115,9 @@ namespace MediaBrowser.Providers.Plugins.TheTvdb
public int Order => 0;
public Task<HttpResponseInfo> GetImageResponse(string url, CancellationToken cancellationToken)
public Task<HttpResponseMessage> GetImageResponse(string url, CancellationToken cancellationToken)
{
return _httpClient.GetResponse(new HttpRequestOptions
{
CancellationToken = cancellationToken,
Url = url
});
return _httpClientFactory.CreateClient(NamedClient.Default).GetAsync(url, cancellationToken);
}
}
}

View File

@@ -1,5 +1,8 @@
#pragma warning disable CS1591
using System;
using System.Collections.Generic;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
using MediaBrowser.Common.Net;
@@ -14,19 +17,18 @@ using TvDbSharper.Dto;
namespace MediaBrowser.Providers.Plugins.TheTvdb
{
/// <summary>
/// Class RemoteEpisodeProvider
/// Class RemoteEpisodeProvider.
/// </summary>
public class TvdbEpisodeProvider : IRemoteMetadataProvider<Episode, EpisodeInfo>, IHasOrder
{
private readonly IHttpClient _httpClient;
private readonly ILogger _logger;
private readonly IHttpClientFactory _httpClientFactory;
private readonly ILogger<TvdbEpisodeProvider> _logger;
private readonly TvdbClientManager _tvdbClientManager;
public TvdbEpisodeProvider(IHttpClient httpClient, ILogger<TvdbEpisodeProvider> logger, TvdbClientManager tvdbClientManager)
public TvdbEpisodeProvider(IHttpClientFactory httpClientFactory, ILogger<TvdbEpisodeProvider> logger, TvdbClientManager tvdbClientManager)
{
_httpClient = httpClient;
_httpClientFactory = httpClientFactory;
_logger = logger;
_tvdbClientManager = tvdbClientManager;
}
@@ -95,7 +97,7 @@ namespace MediaBrowser.Providers.Plugins.TheTvdb
QueriedById = true
};
string seriesTvdbId = searchInfo.GetProviderId(MetadataProviders.Tvdb);
string seriesTvdbId = searchInfo.GetProviderId(MetadataProvider.Tvdb);
string episodeTvdbId = null;
try
{
@@ -139,20 +141,26 @@ namespace MediaBrowser.Providers.Plugins.TheTvdb
Name = episode.EpisodeName,
Overview = episode.Overview,
CommunityRating = (float?)episode.SiteRating,
}
};
result.ResetPeople();
var item = result.Item;
item.SetProviderId(MetadataProviders.Tvdb, episode.Id.ToString());
item.SetProviderId(MetadataProviders.Imdb, episode.ImdbId);
item.SetProviderId(MetadataProvider.Tvdb, episode.Id.ToString());
item.SetProviderId(MetadataProvider.Imdb, episode.ImdbId);
if (string.Equals(id.SeriesDisplayOrder, "dvd", StringComparison.OrdinalIgnoreCase))
{
item.IndexNumber = Convert.ToInt32(episode.DvdEpisodeNumber ?? episode.AiredEpisodeNumber);
item.ParentIndexNumber = episode.DvdSeason ?? episode.AiredSeason;
}
else if (string.Equals(id.SeriesDisplayOrder, "absolute", StringComparison.OrdinalIgnoreCase))
{
if (episode.AbsoluteNumber.GetValueOrDefault() != 0)
{
item.IndexNumber = episode.AbsoluteNumber;
}
}
else if (episode.AiredEpisodeNumber.HasValue)
{
item.IndexNumber = episode.AiredEpisodeNumber;
@@ -188,7 +196,7 @@ namespace MediaBrowser.Providers.Plugins.TheTvdb
for (var i = 0; i < episode.GuestStars.Length; ++i)
{
var currentActor = episode.GuestStars[i];
var roleStartIndex = currentActor.IndexOf('(');
var roleStartIndex = currentActor.IndexOf('(', StringComparison.Ordinal);
if (roleStartIndex == -1)
{
@@ -207,7 +215,7 @@ namespace MediaBrowser.Providers.Plugins.TheTvdb
for (var j = i + 1; j < episode.GuestStars.Length; ++j)
{
var currentRole = episode.GuestStars[j];
var roleEndIndex = currentRole.IndexOf(')');
var roleEndIndex = currentRole.IndexOf(')', StringComparison.Ordinal);
if (roleEndIndex == -1)
{
@@ -242,13 +250,9 @@ namespace MediaBrowser.Providers.Plugins.TheTvdb
return result;
}
public Task<HttpResponseInfo> GetImageResponse(string url, CancellationToken cancellationToken)
public Task<HttpResponseMessage> GetImageResponse(string url, CancellationToken cancellationToken)
{
return _httpClient.GetResponse(new HttpRequestOptions
{
CancellationToken = cancellationToken,
Url = url
});
return _httpClientFactory.CreateClient(NamedClient.Default).GetAsync(url, cancellationToken);
}
public int Order => 0;

View File

@@ -1,6 +1,9 @@
#pragma warning disable CS1591
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
using MediaBrowser.Common.Net;
@@ -18,15 +21,15 @@ namespace MediaBrowser.Providers.Plugins.TheTvdb
{
public class TvdbPersonImageProvider : IRemoteImageProvider, IHasOrder
{
private readonly IHttpClient _httpClient;
private readonly ILogger _logger;
private readonly IHttpClientFactory _httpClientFactory;
private readonly ILogger<TvdbPersonImageProvider> _logger;
private readonly ILibraryManager _libraryManager;
private readonly TvdbClientManager _tvdbClientManager;
public TvdbPersonImageProvider(ILibraryManager libraryManager, IHttpClient httpClient, ILogger<TvdbPersonImageProvider> logger, TvdbClientManager tvdbClientManager)
public TvdbPersonImageProvider(ILibraryManager libraryManager, IHttpClientFactory httpClientFactory, ILogger<TvdbPersonImageProvider> logger, TvdbClientManager tvdbClientManager)
{
_libraryManager = libraryManager;
_httpClient = httpClient;
_httpClientFactory = httpClientFactory;
_logger = logger;
_tvdbClientManager = tvdbClientManager;
}
@@ -57,7 +60,6 @@ namespace MediaBrowser.Providers.Plugins.TheTvdb
{
EnableImages = false
}
}).Cast<Series>()
.Where(i => TvdbSeriesProvider.IsValidSeries(i.ProviderIds))
.ToList();
@@ -73,7 +75,7 @@ namespace MediaBrowser.Providers.Plugins.TheTvdb
private async Task<RemoteImageInfo> GetImageFromSeriesData(Series series, string personName, CancellationToken cancellationToken)
{
var tvdbId = Convert.ToInt32(series.GetProviderId(MetadataProviders.Tvdb));
var tvdbId = Convert.ToInt32(series.GetProviderId(MetadataProvider.Tvdb));
try
{
@@ -103,13 +105,9 @@ namespace MediaBrowser.Providers.Plugins.TheTvdb
}
/// <inheritdoc />
public Task<HttpResponseInfo> GetImageResponse(string url, CancellationToken cancellationToken)
public Task<HttpResponseMessage> GetImageResponse(string url, CancellationToken cancellationToken)
{
return _httpClient.GetResponse(new HttpRequestOptions
{
CancellationToken = cancellationToken,
Url = url
});
return _httpClientFactory.CreateClient(NamedClient.Default).GetAsync(url, cancellationToken);
}
}
}

View File

@@ -1,6 +1,9 @@
#pragma warning disable CS1591
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
using MediaBrowser.Common.Net;
@@ -18,13 +21,13 @@ namespace MediaBrowser.Providers.Plugins.TheTvdb
{
public class TvdbSeasonImageProvider : IRemoteImageProvider, IHasOrder
{
private readonly IHttpClient _httpClient;
private readonly ILogger _logger;
private readonly IHttpClientFactory _httpClientFactory;
private readonly ILogger<TvdbSeasonImageProvider> _logger;
private readonly TvdbClientManager _tvdbClientManager;
public TvdbSeasonImageProvider(IHttpClient httpClient, ILogger<TvdbSeasonImageProvider> logger, TvdbClientManager tvdbClientManager)
public TvdbSeasonImageProvider(IHttpClientFactory httpClientFactory, ILogger<TvdbSeasonImageProvider> logger, TvdbClientManager tvdbClientManager)
{
_httpClient = httpClient;
_httpClientFactory = httpClientFactory;
_logger = logger;
_tvdbClientManager = tvdbClientManager;
}
@@ -55,16 +58,16 @@ namespace MediaBrowser.Providers.Plugins.TheTvdb
if (series == null || !season.IndexNumber.HasValue || !TvdbSeriesProvider.IsValidSeries(series.ProviderIds))
{
return new RemoteImageInfo[] { };
return Array.Empty<RemoteImageInfo>();
}
var tvdbId = Convert.ToInt32(series.GetProviderId(MetadataProviders.Tvdb));
var tvdbId = Convert.ToInt32(series.GetProviderId(MetadataProvider.Tvdb));
var seasonNumber = season.IndexNumber.Value;
var language = item.GetPreferredMetadataLanguage();
var remoteImages = new List<RemoteImageInfo>();
var keyTypes = new[] { KeyType.Season, KeyType.Seasonwide, KeyType.Fanart };
foreach (var keyType in keyTypes)
var keyTypes = _tvdbClientManager.GetImageKeyTypesForSeasonAsync(tvdbId, language, cancellationToken).ConfigureAwait(false);
await foreach (var keyType in keyTypes)
{
var imageQuery = new ImagesQuery
{
@@ -89,7 +92,8 @@ namespace MediaBrowser.Providers.Plugins.TheTvdb
private IEnumerable<RemoteImageInfo> GetImages(Image[] images, string preferredLanguage)
{
var list = new List<RemoteImageInfo>();
var languages = _tvdbClientManager.GetLanguagesAsync(CancellationToken.None).Result.Data;
// any languages with null ids are ignored
var languages = _tvdbClientManager.GetLanguagesAsync(CancellationToken.None).Result.Data.Where(x => x.Id.HasValue);
foreach (Image image in images)
{
var imageInfo = new RemoteImageInfo
@@ -113,8 +117,8 @@ namespace MediaBrowser.Providers.Plugins.TheTvdb
imageInfo.Type = TvdbUtils.GetImageTypeFromKeyType(image.KeyType);
list.Add(imageInfo);
}
var isLanguageEn = string.Equals(preferredLanguage, "en", StringComparison.OrdinalIgnoreCase);
var isLanguageEn = string.Equals(preferredLanguage, "en", StringComparison.OrdinalIgnoreCase);
return list.OrderByDescending(i =>
{
if (string.Equals(preferredLanguage, i.Language, StringComparison.OrdinalIgnoreCase))
@@ -143,13 +147,9 @@ namespace MediaBrowser.Providers.Plugins.TheTvdb
public int Order => 0;
public Task<HttpResponseInfo> GetImageResponse(string url, CancellationToken cancellationToken)
public Task<HttpResponseMessage> GetImageResponse(string url, CancellationToken cancellationToken)
{
return _httpClient.GetResponse(new HttpRequestOptions
{
CancellationToken = cancellationToken,
Url = url
});
return _httpClientFactory.CreateClient(NamedClient.Default).GetAsync(url, cancellationToken);
}
}
}

View File

@@ -1,6 +1,9 @@
#pragma warning disable CS1591
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
using MediaBrowser.Common.Net;
@@ -18,13 +21,13 @@ namespace MediaBrowser.Providers.Plugins.TheTvdb
{
public class TvdbSeriesImageProvider : IRemoteImageProvider, IHasOrder
{
private readonly IHttpClient _httpClient;
private readonly ILogger _logger;
private readonly IHttpClientFactory _httpClientFactory;
private readonly ILogger<TvdbSeriesImageProvider> _logger;
private readonly TvdbClientManager _tvdbClientManager;
public TvdbSeriesImageProvider(IHttpClient httpClient, ILogger<TvdbSeriesImageProvider> logger, TvdbClientManager tvdbClientManager)
public TvdbSeriesImageProvider(IHttpClientFactory httpClientFactory, ILogger<TvdbSeriesImageProvider> logger, TvdbClientManager tvdbClientManager)
{
_httpClient = httpClient;
_httpClientFactory = httpClientFactory;
_logger = logger;
_tvdbClientManager = tvdbClientManager;
}
@@ -57,9 +60,10 @@ namespace MediaBrowser.Providers.Plugins.TheTvdb
var language = item.GetPreferredMetadataLanguage();
var remoteImages = new List<RemoteImageInfo>();
var keyTypes = new[] { KeyType.Poster, KeyType.Series, KeyType.Fanart };
var tvdbId = Convert.ToInt32(item.GetProviderId(MetadataProviders.Tvdb));
foreach (KeyType keyType in keyTypes)
var tvdbId = Convert.ToInt32(item.GetProviderId(MetadataProvider.Tvdb));
var allowedKeyTypes = _tvdbClientManager.GetImageKeyTypesForSeriesAsync(tvdbId, language, cancellationToken)
.ConfigureAwait(false);
await foreach (KeyType keyType in allowedKeyTypes)
{
var imageQuery = new ImagesQuery
{
@@ -79,6 +83,7 @@ namespace MediaBrowser.Providers.Plugins.TheTvdb
tvdbId);
}
}
return remoteImages;
}
@@ -110,8 +115,8 @@ namespace MediaBrowser.Providers.Plugins.TheTvdb
imageInfo.Type = TvdbUtils.GetImageTypeFromKeyType(image.KeyType);
list.Add(imageInfo);
}
var isLanguageEn = string.Equals(preferredLanguage, "en", StringComparison.OrdinalIgnoreCase);
var isLanguageEn = string.Equals(preferredLanguage, "en", StringComparison.OrdinalIgnoreCase);
return list.OrderByDescending(i =>
{
if (string.Equals(preferredLanguage, i.Language, StringComparison.OrdinalIgnoreCase))
@@ -140,13 +145,9 @@ namespace MediaBrowser.Providers.Plugins.TheTvdb
public int Order => 0;
public Task<HttpResponseInfo> GetImageResponse(string url, CancellationToken cancellationToken)
public Task<HttpResponseMessage> GetImageResponse(string url, CancellationToken cancellationToken)
{
return _httpClient.GetResponse(new HttpRequestOptions
{
CancellationToken = cancellationToken,
Url = url
});
return _httpClientFactory.CreateClient(NamedClient.Default).GetAsync(url, cancellationToken);
}
}
}

View File

@@ -1,6 +1,9 @@
#pragma warning disable CS1591
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
@@ -22,15 +25,16 @@ namespace MediaBrowser.Providers.Plugins.TheTvdb
public class TvdbSeriesProvider : IRemoteMetadataProvider<Series, SeriesInfo>, IHasOrder
{
internal static TvdbSeriesProvider Current { get; private set; }
private readonly IHttpClient _httpClient;
private readonly ILogger _logger;
private readonly IHttpClientFactory _httpClientFactory;
private readonly ILogger<TvdbSeriesProvider> _logger;
private readonly ILibraryManager _libraryManager;
private readonly ILocalizationManager _localizationManager;
private readonly TvdbClientManager _tvdbClientManager;
public TvdbSeriesProvider(IHttpClient httpClient, ILogger<TvdbSeriesProvider> logger, ILibraryManager libraryManager, ILocalizationManager localizationManager, TvdbClientManager tvdbClientManager)
public TvdbSeriesProvider(IHttpClientFactory httpClientFactory, ILogger<TvdbSeriesProvider> logger, ILibraryManager libraryManager, ILocalizationManager localizationManager, TvdbClientManager tvdbClientManager)
{
_httpClient = httpClient;
_httpClientFactory = httpClientFactory;
_logger = logger;
_libraryManager = libraryManager;
_localizationManager = localizationManager;
@@ -94,22 +98,22 @@ namespace MediaBrowser.Providers.Plugins.TheTvdb
{
var series = result.Item;
if (seriesProviderIds.TryGetValue(MetadataProviders.Tvdb.ToString(), out var tvdbId) && !string.IsNullOrEmpty(tvdbId))
if (seriesProviderIds.TryGetValue(MetadataProvider.Tvdb.ToString(), out var tvdbId) && !string.IsNullOrEmpty(tvdbId))
{
series.SetProviderId(MetadataProviders.Tvdb, tvdbId);
series.SetProviderId(MetadataProvider.Tvdb, tvdbId);
}
if (seriesProviderIds.TryGetValue(MetadataProviders.Imdb.ToString(), out var imdbId) && !string.IsNullOrEmpty(imdbId))
if (seriesProviderIds.TryGetValue(MetadataProvider.Imdb.ToString(), out var imdbId) && !string.IsNullOrEmpty(imdbId))
{
series.SetProviderId(MetadataProviders.Imdb, imdbId);
tvdbId = await GetSeriesByRemoteId(imdbId, MetadataProviders.Imdb.ToString(), metadataLanguage,
series.SetProviderId(MetadataProvider.Imdb, imdbId);
tvdbId = await GetSeriesByRemoteId(imdbId, MetadataProvider.Imdb.ToString(), metadataLanguage,
cancellationToken).ConfigureAwait(false);
}
if (seriesProviderIds.TryGetValue(MetadataProviders.Zap2It.ToString(), out var zap2It) && !string.IsNullOrEmpty(zap2It))
if (seriesProviderIds.TryGetValue(MetadataProvider.Zap2It.ToString(), out var zap2It) && !string.IsNullOrEmpty(zap2It))
{
series.SetProviderId(MetadataProviders.Zap2It, zap2It);
tvdbId = await GetSeriesByRemoteId(zap2It, MetadataProviders.Zap2It.ToString(), metadataLanguage,
series.SetProviderId(MetadataProvider.Zap2It, zap2It);
tvdbId = await GetSeriesByRemoteId(zap2It, MetadataProvider.Zap2It.ToString(), metadataLanguage,
cancellationToken).ConfigureAwait(false);
}
@@ -145,12 +149,11 @@ namespace MediaBrowser.Providers.Plugins.TheTvdb
private async Task<string> GetSeriesByRemoteId(string id, string idType, string language, CancellationToken cancellationToken)
{
TvDbResponse<SeriesSearchResult[]> result = null;
try
{
if (string.Equals(idType, MetadataProviders.Zap2It.ToString(), StringComparison.OrdinalIgnoreCase))
if (string.Equals(idType, MetadataProvider.Zap2It.ToString(), StringComparison.OrdinalIgnoreCase))
{
result = await _tvdbClientManager.GetSeriesByZap2ItIdAsync(id, language, cancellationToken)
.ConfigureAwait(false);
@@ -176,9 +179,9 @@ namespace MediaBrowser.Providers.Plugins.TheTvdb
/// <returns>True, if the dictionary contains a valid TV provider ID, otherwise false.</returns>
internal static bool IsValidSeries(Dictionary<string, string> seriesProviderIds)
{
return seriesProviderIds.ContainsKey(MetadataProviders.Tvdb.ToString()) ||
seriesProviderIds.ContainsKey(MetadataProviders.Imdb.ToString()) ||
seriesProviderIds.ContainsKey(MetadataProviders.Zap2It.ToString());
return seriesProviderIds.ContainsKey(MetadataProvider.Tvdb.ToString()) ||
seriesProviderIds.ContainsKey(MetadataProvider.Imdb.ToString()) ||
seriesProviderIds.ContainsKey(MetadataProvider.Zap2It.ToString());
}
/// <summary>
@@ -245,24 +248,29 @@ namespace MediaBrowser.Providers.Plugins.TheTvdb
{
Name = tvdbTitles.FirstOrDefault(),
ProductionYear = firstAired.Year,
SearchProviderName = Name,
ImageUrl = TvdbUtils.BannerUrl + seriesSearchResult.Banner
SearchProviderName = Name
};
if (!string.IsNullOrEmpty(seriesSearchResult.Banner))
{
// Results from their Search endpoints already include the /banners/ part in the url, because reasons...
remoteSearchResult.ImageUrl = TvdbUtils.TvdbImageBaseUrl + seriesSearchResult.Banner;
}
try
{
var seriesSesult =
await _tvdbClientManager.GetSeriesByIdAsync(seriesSearchResult.Id, language, cancellationToken)
.ConfigureAwait(false);
remoteSearchResult.SetProviderId(MetadataProviders.Imdb, seriesSesult.Data.ImdbId);
remoteSearchResult.SetProviderId(MetadataProviders.Zap2It, seriesSesult.Data.Zap2itId);
remoteSearchResult.SetProviderId(MetadataProvider.Imdb, seriesSesult.Data.ImdbId);
remoteSearchResult.SetProviderId(MetadataProvider.Zap2It, seriesSesult.Data.Zap2itId);
}
catch (TvDbServerException e)
{
_logger.LogError(e, "Unable to retrieve series with id {TvdbId}", seriesSearchResult.Id);
}
remoteSearchResult.SetProviderId(MetadataProviders.Tvdb, seriesSearchResult.Id.ToString());
remoteSearchResult.SetProviderId(MetadataProvider.Tvdb, seriesSearchResult.Id.ToString());
list.Add(new Tuple<List<string>, RemoteSearchResult>(tvdbTitles, remoteSearchResult));
}
@@ -273,15 +281,6 @@ namespace MediaBrowser.Providers.Plugins.TheTvdb
.ToList();
}
/// <summary>
/// The remove
/// </summary>
const string remove = "\"'!`?";
/// <summary>
/// The spacers
/// </summary>
const string spacers = "/,.:;\\(){}[]+-_=*"; // (there are two types of dashes, short and long)
/// <summary>
/// Gets the name of the comparable.
/// </summary>
@@ -291,47 +290,25 @@ namespace MediaBrowser.Providers.Plugins.TheTvdb
{
name = name.ToLowerInvariant();
name = name.Normalize(NormalizationForm.FormKD);
var sb = new StringBuilder();
foreach (var c in name)
{
if (c >= 0x2B0 && c <= 0x0333)
{
// skip char modifier and diacritics
}
else if (remove.IndexOf(c) > -1)
{
// skip chars we are removing
}
else if (spacers.IndexOf(c) > -1)
{
sb.Append(" ");
}
else if (c == '&')
{
sb.Append(" and ");
}
else
{
sb.Append(c);
}
}
sb.Replace(", the", string.Empty).Replace("the ", " ").Replace(" the ", " ");
return Regex.Replace(sb.ToString().Trim(), @"\s+", " ");
name = name.Replace(", the", string.Empty).Replace("the ", " ").Replace(" the ", " ");
name = name.Replace("&", " and " );
name = Regex.Replace(name, @"[\p{Lm}\p{Mn}]", string.Empty); // Remove diacritics, etc
name = Regex.Replace(name, @"[\W\p{Pc}]+", " "); // Replace sequences of non-word characters and _ with " "
return name.Trim();
}
private void MapSeriesToResult(MetadataResult<Series> result, TvDbSharper.Dto.Series tvdbSeries, string metadataLanguage)
{
Series series = result.Item;
series.SetProviderId(MetadataProviders.Tvdb, tvdbSeries.Id.ToString());
series.SetProviderId(MetadataProvider.Tvdb, tvdbSeries.Id.ToString());
series.Name = tvdbSeries.SeriesName;
series.Overview = (tvdbSeries.Overview ?? string.Empty).Trim();
result.ResultLanguage = metadataLanguage;
series.AirDays = TVUtils.GetAirDays(tvdbSeries.AirsDayOfWeek);
series.AirTime = tvdbSeries.AirsTime;
series.CommunityRating = (float?)tvdbSeries.SiteRating;
series.SetProviderId(MetadataProviders.Imdb, tvdbSeries.ImdbId);
series.SetProviderId(MetadataProviders.Zap2It, tvdbSeries.Zap2itId);
series.SetProviderId(MetadataProvider.Imdb, tvdbSeries.ImdbId);
series.SetProviderId(MetadataProvider.Zap2It, tvdbSeries.Zap2itId);
if (Enum.TryParse(tvdbSeries.Status, true, out SeriesStatus seriesStatus))
{
series.Status = seriesStatus;
@@ -394,10 +371,14 @@ namespace MediaBrowser.Providers.Plugins.TheTvdb
Type = PersonType.Actor,
Name = (actor.Name ?? string.Empty).Trim(),
Role = actor.Role,
ImageUrl = TvdbUtils.BannerUrl + actor.Image,
SortOrder = actor.SortOrder
};
if (!string.IsNullOrEmpty(actor.Image))
{
personInfo.ImageUrl = TvdbUtils.BannerUrl + actor.Image;
}
if (!string.IsNullOrWhiteSpace(personInfo.Name))
{
result.AddPerson(personInfo);
@@ -409,7 +390,7 @@ namespace MediaBrowser.Providers.Plugins.TheTvdb
public async Task Identify(SeriesInfo info)
{
if (!string.IsNullOrWhiteSpace(info.GetProviderId(MetadataProviders.Tvdb)))
if (!string.IsNullOrWhiteSpace(info.GetProviderId(MetadataProvider.Tvdb)))
{
return;
}
@@ -421,21 +402,16 @@ namespace MediaBrowser.Providers.Plugins.TheTvdb
if (entry != null)
{
var id = entry.GetProviderId(MetadataProviders.Tvdb);
info.SetProviderId(MetadataProviders.Tvdb, id);
var id = entry.GetProviderId(MetadataProvider.Tvdb);
info.SetProviderId(MetadataProvider.Tvdb, id);
}
}
public int Order => 0;
public Task<HttpResponseInfo> GetImageResponse(string url, CancellationToken cancellationToken)
public Task<HttpResponseMessage> GetImageResponse(string url, CancellationToken cancellationToken)
{
return _httpClient.GetResponse(new HttpRequestOptions
{
CancellationToken = cancellationToken,
Url = url,
BufferContent = false
});
return _httpClientFactory.CreateClient(NamedClient.Default).GetAsync(url, cancellationToken);
}
}
}

View File

@@ -1,3 +1,5 @@
#pragma warning disable CS1591
using System;
using MediaBrowser.Model.Entities;
@@ -7,7 +9,8 @@ namespace MediaBrowser.Providers.Plugins.TheTvdb
{
public const string TvdbApiKey = "OG4V3YJ3FAP7FP2K";
public const string TvdbBaseUrl = "https://www.thetvdb.com/";
public const string BannerUrl = TvdbBaseUrl + "banners/";
public const string TvdbImageBaseUrl = "https://www.thetvdb.com";
public const string BannerUrl = TvdbImageBaseUrl + "/banners/";
public static ImageType GetImageTypeFromKeyType(string keyType)
{

View File

@@ -2,16 +2,23 @@ using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.Movies;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Providers;
namespace MediaBrowser.Providers.Tmdb.BoxSets
namespace MediaBrowser.Providers.Plugins.Tmdb.BoxSets
{
/// <summary>
/// External ID for a TMDB box set.
/// </summary>
public class TmdbBoxSetExternalId : IExternalId
{
/// <inheritdoc />
public string Name => TmdbUtils.ProviderName;
public string ProviderName => TmdbUtils.ProviderName;
/// <inheritdoc />
public string Key => MetadataProviders.TmdbCollection.ToString();
public string Key => MetadataProvider.TmdbCollection.ToString();
/// <inheritdoc />
public ExternalIdMediaType? Type => ExternalIdMediaType.BoxSet;
/// <inheritdoc />
public string UrlFormatString => TmdbUtils.BaseTmdbUrl + "collection/{0}";

View File

@@ -1,6 +1,9 @@
#pragma warning disable CS1591
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
using MediaBrowser.Common.Net;
@@ -10,19 +13,19 @@ using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Providers;
using MediaBrowser.Providers.Tmdb.Models.Collections;
using MediaBrowser.Providers.Tmdb.Models.General;
using MediaBrowser.Providers.Tmdb.Movies;
using MediaBrowser.Providers.Plugins.Tmdb.Models.Collections;
using MediaBrowser.Providers.Plugins.Tmdb.Models.General;
using MediaBrowser.Providers.Plugins.Tmdb.Movies;
namespace MediaBrowser.Providers.Tmdb.BoxSets
namespace MediaBrowser.Providers.Plugins.Tmdb.BoxSets
{
public class TmdbBoxSetImageProvider : IRemoteImageProvider, IHasOrder
{
private readonly IHttpClient _httpClient;
private readonly IHttpClientFactory _httpClientFactory;
public TmdbBoxSetImageProvider(IHttpClient httpClient)
public TmdbBoxSetImageProvider(IHttpClientFactory httpClientFactory)
{
_httpClient = httpClient;
_httpClientFactory = httpClientFactory;
}
public string Name => ProviderName;
@@ -45,7 +48,7 @@ namespace MediaBrowser.Providers.Tmdb.BoxSets
public async Task<IEnumerable<RemoteImageInfo>> GetImages(BaseItem item, CancellationToken cancellationToken)
{
var tmdbId = item.GetProviderId(MetadataProviders.Tmdb);
var tmdbId = item.GetProviderId(MetadataProvider.Tmdb);
if (!string.IsNullOrEmpty(tmdbId))
{
@@ -105,6 +108,7 @@ namespace MediaBrowser.Providers.Tmdb.BoxSets
{
return 3;
}
if (!isLanguageEn)
{
if (string.Equals("en", i.Language, StringComparison.OrdinalIgnoreCase))
@@ -112,10 +116,12 @@ namespace MediaBrowser.Providers.Tmdb.BoxSets
return 2;
}
}
if (string.IsNullOrEmpty(i.Language))
{
return isLanguageEn ? 3 : 2;
}
return 0;
})
.ThenByDescending(i => i.CommunityRating ?? 0)
@@ -148,13 +154,9 @@ namespace MediaBrowser.Providers.Tmdb.BoxSets
public int Order => 0;
public Task<HttpResponseInfo> GetImageResponse(string url, CancellationToken cancellationToken)
public Task<HttpResponseMessage> GetImageResponse(string url, CancellationToken cancellationToken)
{
return _httpClient.GetResponse(new HttpRequestOptions
{
CancellationToken = cancellationToken,
Url = url
});
return _httpClientFactory.CreateClient(NamedClient.Default).GetAsync(url, cancellationToken);
}
}
}

View File

@@ -1,8 +1,12 @@
#pragma warning disable CS1591
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Threading;
using System.Threading.Tasks;
using MediaBrowser.Common.Configuration;
@@ -16,12 +20,12 @@ using MediaBrowser.Model.Globalization;
using MediaBrowser.Model.IO;
using MediaBrowser.Model.Providers;
using MediaBrowser.Model.Serialization;
using MediaBrowser.Providers.Tmdb.Models.Collections;
using MediaBrowser.Providers.Tmdb.Models.General;
using MediaBrowser.Providers.Tmdb.Movies;
using MediaBrowser.Providers.Plugins.Tmdb.Models.Collections;
using MediaBrowser.Providers.Plugins.Tmdb.Models.General;
using MediaBrowser.Providers.Plugins.Tmdb.Movies;
using Microsoft.Extensions.Logging;
namespace MediaBrowser.Providers.Tmdb.BoxSets
namespace MediaBrowser.Providers.Plugins.Tmdb.BoxSets
{
public class TmdbBoxSetProvider : IRemoteMetadataProvider<BoxSet, BoxSetInfo>
{
@@ -29,12 +33,11 @@ namespace MediaBrowser.Providers.Tmdb.BoxSets
internal static TmdbBoxSetProvider Current;
private readonly ILogger _logger;
private readonly ILogger<TmdbBoxSetProvider> _logger;
private readonly IJsonSerializer _json;
private readonly IServerConfigurationManager _config;
private readonly IFileSystem _fileSystem;
private readonly ILocalizationManager _localization;
private readonly IHttpClient _httpClient;
private readonly IHttpClientFactory _httpClientFactory;
private readonly ILibraryManager _libraryManager;
public TmdbBoxSetProvider(
@@ -42,16 +45,14 @@ namespace MediaBrowser.Providers.Tmdb.BoxSets
IJsonSerializer json,
IServerConfigurationManager config,
IFileSystem fileSystem,
ILocalizationManager localization,
IHttpClient httpClient,
IHttpClientFactory httpClientFactory,
ILibraryManager libraryManager)
{
_logger = logger;
_json = json;
_config = config;
_fileSystem = fileSystem;
_localization = localization;
_httpClient = httpClient;
_httpClientFactory = httpClientFactory;
_libraryManager = libraryManager;
Current = this;
}
@@ -60,7 +61,7 @@ namespace MediaBrowser.Providers.Tmdb.BoxSets
public async Task<IEnumerable<RemoteSearchResult>> GetSearchResults(BoxSetInfo searchInfo, CancellationToken cancellationToken)
{
var tmdbId = searchInfo.GetProviderId(MetadataProviders.Tmdb);
var tmdbId = searchInfo.GetProviderId(MetadataProvider.Tmdb);
if (!string.IsNullOrEmpty(tmdbId))
{
@@ -78,13 +79,11 @@ namespace MediaBrowser.Providers.Tmdb.BoxSets
var result = new RemoteSearchResult
{
Name = info.Name,
SearchProviderName = Name,
ImageUrl = images.Count == 0 ? null : (tmdbImageUrl + images[0].File_Path)
};
result.SetProviderId(MetadataProviders.Tmdb, info.Id.ToString(_usCulture));
result.SetProviderId(MetadataProvider.Tmdb, info.Id.ToString(_usCulture));
return new[] { result };
}
@@ -94,7 +93,7 @@ namespace MediaBrowser.Providers.Tmdb.BoxSets
public async Task<MetadataResult<BoxSet>> GetMetadata(BoxSetInfo id, CancellationToken cancellationToken)
{
var tmdbId = id.GetProviderId(MetadataProviders.Tmdb);
var tmdbId = id.GetProviderId(MetadataProvider.Tmdb);
// We don't already have an Id, need to fetch it
if (string.IsNullOrEmpty(tmdbId))
@@ -105,7 +104,7 @@ namespace MediaBrowser.Providers.Tmdb.BoxSets
if (searchResult != null)
{
tmdbId = searchResult.GetProviderId(MetadataProviders.Tmdb);
tmdbId = searchResult.GetProviderId(MetadataProvider.Tmdb);
}
}
@@ -152,7 +151,7 @@ namespace MediaBrowser.Providers.Tmdb.BoxSets
Overview = obj.Overview
};
item.SetProviderId(MetadataProviders.Tmdb, obj.Id.ToString(_usCulture));
item.SetProviderId(MetadataProvider.Tmdb, obj.Id.ToString(_usCulture));
return item;
}
@@ -161,7 +160,10 @@ namespace MediaBrowser.Providers.Tmdb.BoxSets
{
var mainResult = await FetchMainResult(tmdbId, preferredMetadataLanguage, cancellationToken).ConfigureAwait(false);
if (mainResult == null) return;
if (mainResult == null)
{
return;
}
var dataFilePath = GetDataFilePath(_config.ApplicationPaths, tmdbId, preferredMetadataLanguage);
@@ -172,11 +174,11 @@ namespace MediaBrowser.Providers.Tmdb.BoxSets
private async Task<CollectionResult> FetchMainResult(string id, string language, CancellationToken cancellationToken)
{
var url = string.Format(GetCollectionInfo3, id, TmdbUtils.ApiKey);
var url = string.Format(CultureInfo.InvariantCulture, GetCollectionInfo3, id, TmdbUtils.ApiKey);
if (!string.IsNullOrEmpty(language))
{
url += string.Format("&language={0}", TmdbMovieProvider.NormalizeLanguage(language));
url += string.Format(CultureInfo.InvariantCulture, "&language={0}", TmdbMovieProvider.NormalizeLanguage(language));
// Get images in english and with no language
url += "&include_image_language=" + TmdbMovieProvider.GetImageLanguagesParam(language);
@@ -184,29 +186,23 @@ namespace MediaBrowser.Providers.Tmdb.BoxSets
cancellationToken.ThrowIfCancellationRequested();
CollectionResult mainResult;
using (var response = await TmdbMovieProvider.Current.GetMovieDbResponse(new HttpRequestOptions
using var requestMessage = new HttpRequestMessage(HttpMethod.Get, url);
foreach (var header in TmdbUtils.AcceptHeaders)
{
Url = url,
CancellationToken = cancellationToken,
AcceptHeader = TmdbUtils.AcceptHeader
}).ConfigureAwait(false))
{
using (var json = response.Content)
{
mainResult = await _json.DeserializeFromStreamAsync<CollectionResult>(json).ConfigureAwait(false);
}
requestMessage.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue(header));
}
using var mainResponse = await TmdbMovieProvider.Current.GetMovieDbResponse(requestMessage, cancellationToken).ConfigureAwait(false);
await using var stream = await mainResponse.Content.ReadAsStreamAsync().ConfigureAwait(false);
var mainResult = await _json.DeserializeFromStreamAsync<CollectionResult>(stream).ConfigureAwait(false);
cancellationToken.ThrowIfCancellationRequested();
if (mainResult != null && string.IsNullOrEmpty(mainResult.Name))
{
if (!string.IsNullOrEmpty(language) && !string.Equals(language, "en", StringComparison.OrdinalIgnoreCase))
{
url = string.Format(GetCollectionInfo3, id, TmdbUtils.ApiKey) + "&language=en";
url = string.Format(CultureInfo.InvariantCulture, GetCollectionInfo3, id, TmdbUtils.ApiKey) + "&language=en";
if (!string.IsNullOrEmpty(language))
{
@@ -214,21 +210,17 @@ namespace MediaBrowser.Providers.Tmdb.BoxSets
url += "&include_image_language=" + TmdbMovieProvider.GetImageLanguagesParam(language);
}
using (var response = await TmdbMovieProvider.Current.GetMovieDbResponse(new HttpRequestOptions
using var langRequestMessage = new HttpRequestMessage(HttpMethod.Get, url);
foreach (var header in TmdbUtils.AcceptHeaders)
{
Url = url,
CancellationToken = cancellationToken,
AcceptHeader = TmdbUtils.AcceptHeader
}).ConfigureAwait(false))
{
using (var json = response.Content)
{
mainResult = await _json.DeserializeFromStreamAsync<CollectionResult>(json).ConfigureAwait(false);
}
langRequestMessage.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue(header));
}
await using var langStream = await mainResponse.Content.ReadAsStreamAsync().ConfigureAwait(false);
mainResult = await _json.DeserializeFromStreamAsync<CollectionResult>(langStream).ConfigureAwait(false);
}
}
return mainResult;
}
@@ -256,7 +248,7 @@ namespace MediaBrowser.Providers.Tmdb.BoxSets
{
var path = GetDataPath(appPaths, tmdbId);
var filename = string.Format("all-{0}.json", preferredLanguage ?? string.Empty);
var filename = string.Format(CultureInfo.InvariantCulture, "all-{0}.json", preferredLanguage ?? string.Empty);
return Path.Combine(path, filename);
}
@@ -275,13 +267,9 @@ namespace MediaBrowser.Providers.Tmdb.BoxSets
return dataPath;
}
public Task<HttpResponseInfo> GetImageResponse(string url, CancellationToken cancellationToken)
public Task<HttpResponseMessage> GetImageResponse(string url, CancellationToken cancellationToken)
{
return _httpClient.GetResponse(new HttpRequestOptions
{
CancellationToken = cancellationToken,
Url = url
});
return _httpClientFactory.CreateClient(NamedClient.Default).GetAsync(url, cancellationToken);
}
}
}

View File

@@ -1,11 +1,14 @@
using System.Collections.Generic;
using MediaBrowser.Providers.Tmdb.Models.General;
#pragma warning disable CS1591
namespace MediaBrowser.Providers.Tmdb.Models.Collections
using System.Collections.Generic;
using MediaBrowser.Providers.Plugins.Tmdb.Models.General;
namespace MediaBrowser.Providers.Plugins.Tmdb.Models.Collections
{
public class CollectionImages
{
public List<Backdrop> Backdrops { get; set; }
public List<Poster> Posters { get; set; }
}
}

View File

@@ -1,15 +1,23 @@
#pragma warning disable CS1591
using System.Collections.Generic;
namespace MediaBrowser.Providers.Tmdb.Models.Collections
namespace MediaBrowser.Providers.Plugins.Tmdb.Models.Collections
{
public class CollectionResult
{
public int Id { get; set; }
public string Name { get; set; }
public string Overview { get; set; }
public string Poster_Path { get; set; }
public string Backdrop_Path { get; set; }
public List<Part> Parts { get; set; }
public CollectionImages Images { get; set; }
}
}

View File

@@ -1,11 +1,17 @@
namespace MediaBrowser.Providers.Tmdb.Models.Collections
#pragma warning disable CS1591
namespace MediaBrowser.Providers.Plugins.Tmdb.Models.Collections
{
public class Part
{
public string Title { get; set; }
public int Id { get; set; }
public string Release_Date { get; set; }
public string Poster_Path { get; set; }
public string Backdrop_Path { get; set; }
}
}

View File

@@ -1,13 +1,21 @@
namespace MediaBrowser.Providers.Tmdb.Models.General
#pragma warning disable CS1591
namespace MediaBrowser.Providers.Plugins.Tmdb.Models.General
{
public class Backdrop
{
public double Aspect_Ratio { get; set; }
public string File_Path { get; set; }
public int Height { get; set; }
public string Iso_639_1 { get; set; }
public double Vote_Average { get; set; }
public int Vote_Count { get; set; }
public int Width { get; set; }
}
}

View File

@@ -1,12 +1,19 @@
namespace MediaBrowser.Providers.Tmdb.Models.General
#pragma warning disable CS1591
namespace MediaBrowser.Providers.Plugins.Tmdb.Models.General
{
public class Crew
{
public int Id { get; set; }
public string Credit_Id { get; set; }
public string Name { get; set; }
public string Department { get; set; }
public string Job { get; set; }
public string Profile_Path { get; set; }
}
}

View File

@@ -1,11 +1,17 @@
namespace MediaBrowser.Providers.Tmdb.Models.General
#pragma warning disable CS1591
namespace MediaBrowser.Providers.Plugins.Tmdb.Models.General
{
public class ExternalIds
{
public string Imdb_Id { get; set; }
public object Freebase_Id { get; set; }
public string Freebase_Mid { get; set; }
public int Tvdb_Id { get; set; }
public int Tvrage_Id { get; set; }
public int? Tvdb_Id { get; set; }
public int? Tvrage_Id { get; set; }
}
}

View File

@@ -1,8 +1,11 @@
namespace MediaBrowser.Providers.Tmdb.Models.General
#pragma warning disable CS1591
namespace MediaBrowser.Providers.Plugins.Tmdb.Models.General
{
public class Genre
{
public int Id { get; set; }
public string Name { get; set; }
}
}

View File

@@ -1,10 +1,13 @@
#pragma warning disable CS1591
using System.Collections.Generic;
namespace MediaBrowser.Providers.Tmdb.Models.General
namespace MediaBrowser.Providers.Plugins.Tmdb.Models.General
{
public class Images
{
public List<Backdrop> Backdrops { get; set; }
public List<Poster> Posters { get; set; }
}
}

View File

@@ -1,8 +1,11 @@
namespace MediaBrowser.Providers.Tmdb.Models.General
#pragma warning disable CS1591
namespace MediaBrowser.Providers.Plugins.Tmdb.Models.General
{
public class Keyword
{
public int Id { get; set; }
public string Name { get; set; }
}
}

View File

@@ -1,6 +1,8 @@
#pragma warning disable CS1591
using System.Collections.Generic;
namespace MediaBrowser.Providers.Tmdb.Models.General
namespace MediaBrowser.Providers.Plugins.Tmdb.Models.General
{
public class Keywords
{

View File

@@ -1,13 +1,21 @@
namespace MediaBrowser.Providers.Tmdb.Models.General
#pragma warning disable CS1591
namespace MediaBrowser.Providers.Plugins.Tmdb.Models.General
{
public class Poster
{
public double Aspect_Ratio { get; set; }
public string File_Path { get; set; }
public int Height { get; set; }
public string Iso_639_1 { get; set; }
public double Vote_Average { get; set; }
public int Vote_Count { get; set; }
public int Width { get; set; }
}
}

View File

@@ -1,11 +1,17 @@
namespace MediaBrowser.Providers.Tmdb.Models.General
#pragma warning disable CS1591
namespace MediaBrowser.Providers.Plugins.Tmdb.Models.General
{
public class Profile
{
public string File_Path { get; set; }
public int Width { get; set; }
public int Height { get; set; }
public object Iso_639_1 { get; set; }
public double Aspect_Ratio { get; set; }
}
}

View File

@@ -1,14 +1,23 @@
namespace MediaBrowser.Providers.Tmdb.Models.General
#pragma warning disable CS1591
namespace MediaBrowser.Providers.Plugins.Tmdb.Models.General
{
public class Still
{
public double Aspect_Ratio { get; set; }
public string File_Path { get; set; }
public int Height { get; set; }
public string Id { get; set; }
public string Iso_639_1 { get; set; }
public double Vote_Average { get; set; }
public int Vote_Count { get; set; }
public int Width { get; set; }
}
}

View File

@@ -1,6 +1,8 @@
#pragma warning disable CS1591
using System.Collections.Generic;
namespace MediaBrowser.Providers.Tmdb.Models.General
namespace MediaBrowser.Providers.Plugins.Tmdb.Models.General
{
public class StillImages
{

View File

@@ -1,14 +1,23 @@
namespace MediaBrowser.Providers.Tmdb.Models.General
#pragma warning disable CS1591
namespace MediaBrowser.Providers.Plugins.Tmdb.Models.General
{
public class Video
{
public string Id { get; set; }
public string Iso_639_1 { get; set; }
public string Iso_3166_1 { get; set; }
public string Key { get; set; }
public string Name { get; set; }
public string Site { get; set; }
public string Size { get; set; }
public string Type { get; set; }
}
}

View File

@@ -1,6 +1,8 @@
#pragma warning disable CS1591
using System.Collections.Generic;
namespace MediaBrowser.Providers.Tmdb.Models.General
namespace MediaBrowser.Providers.Plugins.Tmdb.Models.General
{
public class Videos
{

View File

@@ -1,10 +1,15 @@
namespace MediaBrowser.Providers.Tmdb.Models.Movies
#pragma warning disable CS1591
namespace MediaBrowser.Providers.Plugins.Tmdb.Models.Movies
{
public class BelongsToCollection
{
public int Id { get; set; }
public string Name { get; set; }
public string Poster_Path { get; set; }
public string Backdrop_Path { get; set; }
}
}

View File

@@ -1,12 +1,19 @@
namespace MediaBrowser.Providers.Tmdb.Models.Movies
#pragma warning disable CS1591
namespace MediaBrowser.Providers.Plugins.Tmdb.Models.Movies
{
public class Cast
{
public int Id { get; set; }
public string Name { get; set; }
public string Character { get; set; }
public int Order { get; set; }
public int Cast_Id { get; set; }
public string Profile_Path { get; set; }
}
}

View File

@@ -1,11 +1,14 @@
using System.Collections.Generic;
using MediaBrowser.Providers.Tmdb.Models.General;
#pragma warning disable CS1591
namespace MediaBrowser.Providers.Tmdb.Models.Movies
using System.Collections.Generic;
using MediaBrowser.Providers.Plugins.Tmdb.Models.General;
namespace MediaBrowser.Providers.Plugins.Tmdb.Models.Movies
{
public class Casts
{
public List<Cast> Cast { get; set; }
public List<Crew> Crew { get; set; }
}
}

View File

@@ -1,11 +1,15 @@
#pragma warning disable CS1591
using System;
namespace MediaBrowser.Providers.Tmdb.Models.Movies
namespace MediaBrowser.Providers.Plugins.Tmdb.Models.Movies
{
public class Country
{
public string Iso_3166_1 { get; set; }
public string Certification { get; set; }
public DateTime Release_Date { get; set; }
}
}

View File

@@ -1,39 +1,70 @@
using System.Collections.Generic;
using MediaBrowser.Providers.Tmdb.Models.General;
#pragma warning disable CS1591
namespace MediaBrowser.Providers.Tmdb.Models.Movies
using System.Collections.Generic;
using MediaBrowser.Providers.Plugins.Tmdb.Models.General;
namespace MediaBrowser.Providers.Plugins.Tmdb.Models.Movies
{
public class MovieResult
{
public bool Adult { get; set; }
public string Backdrop_Path { get; set; }
public BelongsToCollection Belongs_To_Collection { get; set; }
public int Budget { get; set; }
public long Budget { get; set; }
public List<Genre> Genres { get; set; }
public string Homepage { get; set; }
public int Id { get; set; }
public string Imdb_Id { get; set; }
public string Original_Title { get; set; }
public string Original_Name { get; set; }
public string Overview { get; set; }
public double Popularity { get; set; }
public string Poster_Path { get; set; }
public List<ProductionCompany> Production_Companies { get; set; }
public List<ProductionCountry> Production_Countries { get; set; }
public string Release_Date { get; set; }
public int Revenue { get; set; }
public long Revenue { get; set; }
public int Runtime { get; set; }
public List<SpokenLanguage> Spoken_Languages { get; set; }
public string Status { get; set; }
public string Tagline { get; set; }
public string Title { get; set; }
public string Name { get; set; }
public double Vote_Average { get; set; }
public int Vote_Count { get; set; }
public Casts Casts { get; set; }
public Releases Releases { get; set; }
public Images Images { get; set; }
public Keywords Keywords { get; set; }
public Trailers Trailers { get; set; }
public string GetOriginalTitle()

View File

@@ -1,8 +1,11 @@
namespace MediaBrowser.Providers.Tmdb.Models.Movies
#pragma warning disable CS1591
namespace MediaBrowser.Providers.Plugins.Tmdb.Models.Movies
{
public class ProductionCompany
{
public string Name { get; set; }
public int Id { get; set; }
}
}

Some files were not shown because too many files have changed in this diff Show More