This commit is contained in:
Eric Reed 2014-01-29 12:22:53 -05:00
commit ab5145bcd7
104 changed files with 3884 additions and 2333 deletions

View File

@ -799,7 +799,12 @@ namespace MediaBrowser.Api.Images
await _providerManager.SaveImage(entity, memoryStream, mimeType, imageType, null, null, CancellationToken.None).ConfigureAwait(false); await _providerManager.SaveImage(entity, memoryStream, mimeType, imageType, null, null, CancellationToken.None).ConfigureAwait(false);
await entity.RefreshMetadata(CancellationToken.None, forceRefresh: true, forceSave: true, allowSlowProviders: false).ConfigureAwait(false); await entity.RefreshMetadata(new MetadataRefreshOptions
{
ImageRefreshMode = MetadataRefreshMode.None,
ForceSave = true
}, CancellationToken.None).ConfigureAwait(false);
} }
} }
} }

View File

@ -9,13 +9,13 @@ using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Entities; using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Providers; using MediaBrowser.Model.Providers;
using ServiceStack; using ServiceStack;
using ServiceStack.Text.Controller;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using ServiceStack.Text.Controller;
namespace MediaBrowser.Api.Images namespace MediaBrowser.Api.Images
{ {
@ -193,12 +193,7 @@ namespace MediaBrowser.Api.Images
private List<ImageProviderInfo> GetImageProviders(BaseItem item) private List<ImageProviderInfo> GetImageProviders(BaseItem item)
{ {
return _providerManager.GetImageProviders(item).Select(i => new ImageProviderInfo return _providerManager.GetImageProviderInfo(item).ToList();
{
Name = i.Name,
Priority = i.Priority
}).ToList();
} }
public object Get(GetRemoteImages request) public object Get(GetRemoteImages request)
@ -229,7 +224,9 @@ namespace MediaBrowser.Api.Images
var result = new RemoteImageResult var result = new RemoteImageResult
{ {
TotalRecordCount = imagesList.Count, TotalRecordCount = imagesList.Count,
Providers = _providerManager.GetImageProviders(item).Select(i => i.Name).ToList() Providers = images.Select(i => i.ProviderName)
.Distinct(StringComparer.OrdinalIgnoreCase)
.ToList()
}; };
if (request.StartIndex.HasValue) if (request.StartIndex.HasValue)
@ -284,8 +281,13 @@ namespace MediaBrowser.Api.Images
{ {
await _providerManager.SaveImage(item, request.ImageUrl, null, request.Type, null, CancellationToken.None).ConfigureAwait(false); await _providerManager.SaveImage(item, request.ImageUrl, null, request.Type, null, CancellationToken.None).ConfigureAwait(false);
await item.RefreshMetadata(CancellationToken.None, forceSave: true, allowSlowProviders: false) await item.RefreshMetadata(new MetadataRefreshOptions
.ConfigureAwait(false); {
ForceSave = true,
ImageRefreshMode = MetadataRefreshMode.None,
MetadataRefreshMode = MetadataRefreshMode.None
}, CancellationToken.None).ConfigureAwait(false);
} }
/// <summary> /// <summary>

View File

@ -2,6 +2,7 @@
using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.Audio; using MediaBrowser.Controller.Entities.Audio;
using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Providers;
using ServiceStack; using ServiceStack;
using System; using System;
using System.Linq; using System.Linq;
@ -131,7 +132,11 @@ namespace MediaBrowser.Api
try try
{ {
await item.RefreshMetadata(cancellationToken, forceRefresh: request.Forced).ConfigureAwait(false); await item.RefreshMetadata(new MetadataRefreshOptions
{
ReplaceAllMetadata = request.Forced,
}, CancellationToken.None).ConfigureAwait(false);
} }
catch (Exception ex) catch (Exception ex)
{ {
@ -152,7 +157,11 @@ namespace MediaBrowser.Api
try try
{ {
await item.RefreshMetadata(CancellationToken.None, forceRefresh: request.Forced).ConfigureAwait(false); await item.RefreshMetadata(new MetadataRefreshOptions
{
ReplaceAllMetadata = request.Forced,
}, CancellationToken.None).ConfigureAwait(false);
} }
catch (Exception ex) catch (Exception ex)
{ {
@ -173,7 +182,11 @@ namespace MediaBrowser.Api
try try
{ {
await item.RefreshMetadata(CancellationToken.None, forceRefresh: request.Forced).ConfigureAwait(false); await item.RefreshMetadata(new MetadataRefreshOptions
{
ReplaceAllMetadata = request.Forced,
}, CancellationToken.None).ConfigureAwait(false);
} }
catch (Exception ex) catch (Exception ex)
{ {
@ -194,7 +207,11 @@ namespace MediaBrowser.Api
try try
{ {
await item.RefreshMetadata(CancellationToken.None, forceRefresh: request.Forced).ConfigureAwait(false); await item.RefreshMetadata(new MetadataRefreshOptions
{
ReplaceAllMetadata = request.Forced,
}, CancellationToken.None).ConfigureAwait(false);
} }
catch (Exception ex) catch (Exception ex)
{ {
@ -215,7 +232,11 @@ namespace MediaBrowser.Api
try try
{ {
await item.RefreshMetadata(CancellationToken.None, forceRefresh: request.Forced).ConfigureAwait(false); await item.RefreshMetadata(new MetadataRefreshOptions
{
ReplaceAllMetadata = request.Forced,
}, CancellationToken.None).ConfigureAwait(false);
} }
catch (Exception ex) catch (Exception ex)
{ {
@ -236,7 +257,11 @@ namespace MediaBrowser.Api
try try
{ {
await item.RefreshMetadata(CancellationToken.None, forceRefresh: request.Forced).ConfigureAwait(false); await item.RefreshMetadata(new MetadataRefreshOptions
{
ReplaceAllMetadata = request.Forced,
}, CancellationToken.None).ConfigureAwait(false);
} }
catch (Exception ex) catch (Exception ex)
{ {
@ -266,7 +291,11 @@ namespace MediaBrowser.Api
try try
{ {
await item.RefreshMetadata(CancellationToken.None, forceRefresh: request.Forced).ConfigureAwait(false); await item.RefreshMetadata(new MetadataRefreshOptions
{
ReplaceAllMetadata = request.Forced,
}, CancellationToken.None).ConfigureAwait(false);
if (item.IsFolder) if (item.IsFolder)
{ {
@ -301,7 +330,11 @@ namespace MediaBrowser.Api
{ {
foreach (var child in collectionFolder.Children.ToList()) foreach (var child in collectionFolder.Children.ToList())
{ {
await child.RefreshMetadata(CancellationToken.None, forceRefresh: request.Forced).ConfigureAwait(false); await child.RefreshMetadata(new MetadataRefreshOptions
{
ReplaceAllMetadata = request.Forced,
}, CancellationToken.None).ConfigureAwait(false);
if (child.IsFolder) if (child.IsFolder)
{ {

View File

@ -167,6 +167,17 @@ namespace MediaBrowser.Api.Library
public bool RefreshLibrary { get; set; } public bool RefreshLibrary { get; set; }
} }
[Route("/Library/Changes/Path", "POST")]
public class ReportChangedPath : IReturnVoid
{
/// <summary>
/// Gets or sets the name.
/// </summary>
/// <value>The name.</value>
[ApiMember(Name = "Path", Description = "The path that was changed.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "POST")]
public string Path { get; set; }
}
/// <summary> /// <summary>
/// Class LibraryStructureService /// Class LibraryStructureService
/// </summary> /// </summary>
@ -187,7 +198,7 @@ namespace MediaBrowser.Api.Library
/// </summary> /// </summary>
private readonly ILibraryManager _libraryManager; private readonly ILibraryManager _libraryManager;
private readonly IDirectoryWatchers _directoryWatchers; private readonly ILibraryMonitor _libraryMonitor;
private readonly IFileSystem _fileSystem; private readonly IFileSystem _fileSystem;
private readonly ILogger _logger; private readonly ILogger _logger;
@ -199,7 +210,7 @@ namespace MediaBrowser.Api.Library
/// <param name="userManager">The user manager.</param> /// <param name="userManager">The user manager.</param>
/// <param name="libraryManager">The library manager.</param> /// <param name="libraryManager">The library manager.</param>
/// <exception cref="System.ArgumentNullException">appPaths</exception> /// <exception cref="System.ArgumentNullException">appPaths</exception>
public LibraryStructureService(IServerApplicationPaths appPaths, IUserManager userManager, ILibraryManager libraryManager, IDirectoryWatchers directoryWatchers, IFileSystem fileSystem, ILogger logger) public LibraryStructureService(IServerApplicationPaths appPaths, IUserManager userManager, ILibraryManager libraryManager, ILibraryMonitor libraryMonitor, IFileSystem fileSystem, ILogger logger)
{ {
if (appPaths == null) if (appPaths == null)
{ {
@ -209,11 +220,26 @@ namespace MediaBrowser.Api.Library
_userManager = userManager; _userManager = userManager;
_appPaths = appPaths; _appPaths = appPaths;
_libraryManager = libraryManager; _libraryManager = libraryManager;
_directoryWatchers = directoryWatchers; _libraryMonitor = libraryMonitor;
_fileSystem = fileSystem; _fileSystem = fileSystem;
_logger = logger; _logger = logger;
} }
/// <summary>
/// Posts the specified request.
/// </summary>
/// <param name="request">The request.</param>
/// <exception cref="System.ArgumentException">Please supply a Path</exception>
public void Post(ReportChangedPath request)
{
if (string.IsNullOrEmpty(request.Path))
{
throw new ArgumentException("Please supply a Path");
}
_libraryMonitor.ReportFileSystemChanged(request.Path);
}
/// <summary> /// <summary>
/// Gets the specified request. /// Gets the specified request.
/// </summary> /// </summary>
@ -270,8 +296,7 @@ namespace MediaBrowser.Api.Library
throw new ArgumentException("There is already a media collection with the name " + name + "."); throw new ArgumentException("There is already a media collection with the name " + name + ".");
} }
_directoryWatchers.Stop(); _libraryMonitor.Stop();
_directoryWatchers.TemporarilyIgnore(virtualFolderPath);
try try
{ {
@ -294,10 +319,8 @@ namespace MediaBrowser.Api.Library
// No need to start if scanning the library because it will handle it // No need to start if scanning the library because it will handle it
if (!request.RefreshLibrary) if (!request.RefreshLibrary)
{ {
_directoryWatchers.Start(); _libraryMonitor.Start();
} }
_directoryWatchers.RemoveTempIgnore(virtualFolderPath);
} }
if (request.RefreshLibrary) if (request.RefreshLibrary)
@ -348,9 +371,7 @@ namespace MediaBrowser.Api.Library
throw new ArgumentException("There is already a media collection with the name " + newPath + "."); throw new ArgumentException("There is already a media collection with the name " + newPath + ".");
} }
_directoryWatchers.Stop(); _libraryMonitor.Stop();
_directoryWatchers.TemporarilyIgnore(currentPath);
_directoryWatchers.TemporarilyIgnore(newPath);
try try
{ {
@ -376,11 +397,8 @@ namespace MediaBrowser.Api.Library
// No need to start if scanning the library because it will handle it // No need to start if scanning the library because it will handle it
if (!request.RefreshLibrary) if (!request.RefreshLibrary)
{ {
_directoryWatchers.Start(); _libraryMonitor.Start();
} }
_directoryWatchers.RemoveTempIgnore(currentPath);
_directoryWatchers.RemoveTempIgnore(newPath);
} }
if (request.RefreshLibrary) if (request.RefreshLibrary)
@ -420,8 +438,7 @@ namespace MediaBrowser.Api.Library
throw new DirectoryNotFoundException("The media folder does not exist"); throw new DirectoryNotFoundException("The media folder does not exist");
} }
_directoryWatchers.Stop(); _libraryMonitor.Stop();
_directoryWatchers.TemporarilyIgnore(path);
try try
{ {
@ -437,10 +454,8 @@ namespace MediaBrowser.Api.Library
// No need to start if scanning the library because it will handle it // No need to start if scanning the library because it will handle it
if (!request.RefreshLibrary) if (!request.RefreshLibrary)
{ {
_directoryWatchers.Start(); _libraryMonitor.Start();
} }
_directoryWatchers.RemoveTempIgnore(path);
} }
if (request.RefreshLibrary) if (request.RefreshLibrary)
@ -460,7 +475,7 @@ namespace MediaBrowser.Api.Library
throw new ArgumentNullException("request"); throw new ArgumentNullException("request");
} }
_directoryWatchers.Stop(); _libraryMonitor.Stop();
try try
{ {
@ -485,7 +500,7 @@ namespace MediaBrowser.Api.Library
// No need to start if scanning the library because it will handle it // No need to start if scanning the library because it will handle it
if (!request.RefreshLibrary) if (!request.RefreshLibrary)
{ {
_directoryWatchers.Start(); _libraryMonitor.Start();
} }
} }
@ -506,7 +521,7 @@ namespace MediaBrowser.Api.Library
throw new ArgumentNullException("request"); throw new ArgumentNullException("request");
} }
_directoryWatchers.Stop(); _libraryMonitor.Stop();
try try
{ {
@ -531,7 +546,7 @@ namespace MediaBrowser.Api.Library
// No need to start if scanning the library because it will handle it // No need to start if scanning the library because it will handle it
if (!request.RefreshLibrary) if (!request.RefreshLibrary)
{ {
_directoryWatchers.Start(); _libraryMonitor.Start();
} }
} }

View File

@ -18,17 +18,17 @@ namespace MediaBrowser.Controller.Drawing
/// <param name="image">The image.</param> /// <param name="image">The image.</param>
/// <param name="toStream">To stream.</param> /// <param name="toStream">To stream.</param>
/// <param name="quality">The quality.</param> /// <param name="quality">The quality.</param>
public static void Save(this Image image, ImageFormat outputFormat, Stream toStream, int quality) public static void Save(this Image image, System.Drawing.Imaging.ImageFormat outputFormat, Stream toStream, int quality)
{ {
// Use special save methods for jpeg and png that will result in a much higher quality image // Use special save methods for jpeg and png that will result in a much higher quality image
// All other formats use the generic Image.Save // All other formats use the generic Image.Save
if (ImageFormat.Jpeg.Equals(outputFormat)) if (System.Drawing.Imaging.ImageFormat.Jpeg.Equals(outputFormat))
{ {
SaveAsJpeg(image, toStream, quality); SaveAsJpeg(image, toStream, quality);
} }
else if (ImageFormat.Png.Equals(outputFormat)) else if (System.Drawing.Imaging.ImageFormat.Png.Equals(outputFormat))
{ {
image.Save(toStream, ImageFormat.Png); image.Save(toStream, System.Drawing.Imaging.ImageFormat.Png);
} }
else else
{ {

View File

@ -0,0 +1,11 @@

namespace MediaBrowser.Controller.Drawing
{
public enum ImageFormat
{
Jpg,
Png,
Gif,
Bmp
}
}

View File

@ -23,7 +23,7 @@ namespace MediaBrowser.Controller.Entities
/// <summary> /// <summary>
/// Class BaseItem /// Class BaseItem
/// </summary> /// </summary>
public abstract class BaseItem : IHasProviderIds, ILibraryItem, IHasImages, IHasUserData public abstract class BaseItem : IHasProviderIds, ILibraryItem, IHasImages, IHasUserData, IHasMetadata
{ {
protected BaseItem() protected BaseItem()
{ {
@ -364,11 +364,16 @@ namespace MediaBrowser.Controller.Entities
} }
} }
private string _forcedSortName;
/// <summary> /// <summary>
/// Gets or sets the name of the forced sort. /// Gets or sets the name of the forced sort.
/// </summary> /// </summary>
/// <value>The name of the forced sort.</value> /// <value>The name of the forced sort.</value>
public string ForcedSortName { get; set; } public string ForcedSortName
{
get { return _forcedSortName; }
set { _forcedSortName = value; _sortName = null; }
}
private string _sortName; private string _sortName;
/// <summary> /// <summary>
@ -767,25 +772,35 @@ namespace MediaBrowser.Controller.Entities
}).ToList(); }).ToList();
} }
public Task<bool> RefreshMetadata(CancellationToken cancellationToken, bool resetResolveArgs = true)
{
return RefreshMetadata(new MetadataRefreshOptions { ResetResolveArgs = resetResolveArgs }, cancellationToken);
}
/// <summary> /// <summary>
/// Overrides the base implementation to refresh metadata for local trailers /// Overrides the base implementation to refresh metadata for local trailers
/// </summary> /// </summary>
/// <param name="options">The options.</param>
/// <param name="cancellationToken">The cancellation token.</param> /// <param name="cancellationToken">The cancellation token.</param>
/// <param name="forceSave">if set to <c>true</c> [is new item].</param>
/// <param name="forceRefresh">if set to <c>true</c> [force].</param>
/// <param name="allowSlowProviders">if set to <c>true</c> [allow slow providers].</param>
/// <param name="resetResolveArgs">if set to <c>true</c> [reset resolve args].</param>
/// <returns>true if a provider reports we changed</returns> /// <returns>true if a provider reports we changed</returns>
public virtual async Task<bool> RefreshMetadata(CancellationToken cancellationToken, bool forceSave = false, bool forceRefresh = false, bool allowSlowProviders = true, bool resetResolveArgs = true) public async Task<bool> RefreshMetadata(MetadataRefreshOptions options, CancellationToken cancellationToken)
{ {
if (resetResolveArgs) if (options.ResetResolveArgs)
{ {
// Reload this // Reload this
ResetResolveArgs(); ResetResolveArgs();
} }
await ProviderManager.RefreshMetadata(this, options, cancellationToken).ConfigureAwait(false);
return false;
}
[Obsolete]
public virtual async Task<bool> RefreshMetadataDirect(CancellationToken cancellationToken, bool forceSave = false, bool forceRefresh = false)
{
// Refresh for the item // Refresh for the item
var itemRefreshTask = ProviderManager.ExecuteMetadataProviders(this, cancellationToken, forceRefresh, allowSlowProviders); var itemRefreshTask = ProviderManager.ExecuteMetadataProviders(this, cancellationToken, forceRefresh);
cancellationToken.ThrowIfCancellationRequested(); cancellationToken.ThrowIfCancellationRequested();
@ -800,15 +815,15 @@ namespace MediaBrowser.Controller.Entities
var hasThemeMedia = this as IHasThemeMedia; var hasThemeMedia = this as IHasThemeMedia;
if (hasThemeMedia != null) if (hasThemeMedia != null)
{ {
themeSongsChanged = await RefreshThemeSongs(hasThemeMedia, cancellationToken, forceSave, forceRefresh, allowSlowProviders).ConfigureAwait(false); themeSongsChanged = await RefreshThemeSongs(hasThemeMedia, cancellationToken, forceSave, forceRefresh).ConfigureAwait(false);
themeVideosChanged = await RefreshThemeVideos(hasThemeMedia, cancellationToken, forceSave, forceRefresh, allowSlowProviders).ConfigureAwait(false); themeVideosChanged = await RefreshThemeVideos(hasThemeMedia, cancellationToken, forceSave, forceRefresh).ConfigureAwait(false);
} }
var hasTrailers = this as IHasTrailers; var hasTrailers = this as IHasTrailers;
if (hasTrailers != null) if (hasTrailers != null)
{ {
localTrailersChanged = await RefreshLocalTrailers(hasTrailers, cancellationToken, forceSave, forceRefresh, allowSlowProviders).ConfigureAwait(false); localTrailersChanged = await RefreshLocalTrailers(hasTrailers, cancellationToken, forceSave, forceRefresh).ConfigureAwait(false);
} }
} }
@ -829,14 +844,20 @@ namespace MediaBrowser.Controller.Entities
return changed; return changed;
} }
private async Task<bool> RefreshLocalTrailers(IHasTrailers item, CancellationToken cancellationToken, bool forceSave = false, bool forceRefresh = false, bool allowSlowProviders = true) private async Task<bool> RefreshLocalTrailers(IHasTrailers item, CancellationToken cancellationToken, bool forceSave = false, bool forceRefresh = false)
{ {
var newItems = LoadLocalTrailers().ToList(); var newItems = LoadLocalTrailers().ToList();
var newItemIds = newItems.Select(i => i.Id).ToList(); var newItemIds = newItems.Select(i => i.Id).ToList();
var itemsChanged = !item.LocalTrailerIds.SequenceEqual(newItemIds); var itemsChanged = !item.LocalTrailerIds.SequenceEqual(newItemIds);
var tasks = newItems.Select(i => i.RefreshMetadata(cancellationToken, forceSave, forceRefresh, allowSlowProviders, resetResolveArgs: false)); var tasks = newItems.Select(i => i.RefreshMetadata(new MetadataRefreshOptions
{
ForceSave = forceSave,
ReplaceAllMetadata = forceRefresh,
ResetResolveArgs = false
}, cancellationToken));
var results = await Task.WhenAll(tasks).ConfigureAwait(false); var results = await Task.WhenAll(tasks).ConfigureAwait(false);
@ -845,14 +866,20 @@ namespace MediaBrowser.Controller.Entities
return itemsChanged || results.Contains(true); return itemsChanged || results.Contains(true);
} }
private async Task<bool> RefreshThemeVideos(IHasThemeMedia item, CancellationToken cancellationToken, bool forceSave = false, bool forceRefresh = false, bool allowSlowProviders = true) private async Task<bool> RefreshThemeVideos(IHasThemeMedia item, CancellationToken cancellationToken, bool forceSave = false, bool forceRefresh = false)
{ {
var newThemeVideos = LoadThemeVideos().ToList(); var newThemeVideos = LoadThemeVideos().ToList();
var newThemeVideoIds = newThemeVideos.Select(i => i.Id).ToList(); var newThemeVideoIds = newThemeVideos.Select(i => i.Id).ToList();
var themeVideosChanged = !item.ThemeVideoIds.SequenceEqual(newThemeVideoIds); var themeVideosChanged = !item.ThemeVideoIds.SequenceEqual(newThemeVideoIds);
var tasks = newThemeVideos.Select(i => i.RefreshMetadata(cancellationToken, forceSave, forceRefresh, allowSlowProviders, resetResolveArgs: false)); var tasks = newThemeVideos.Select(i => i.RefreshMetadata(new MetadataRefreshOptions
{
ForceSave = forceSave,
ReplaceAllMetadata = forceRefresh,
ResetResolveArgs = false
}, cancellationToken));
var results = await Task.WhenAll(tasks).ConfigureAwait(false); var results = await Task.WhenAll(tasks).ConfigureAwait(false);
@ -864,14 +891,20 @@ namespace MediaBrowser.Controller.Entities
/// <summary> /// <summary>
/// Refreshes the theme songs. /// Refreshes the theme songs.
/// </summary> /// </summary>
private async Task<bool> RefreshThemeSongs(IHasThemeMedia item, CancellationToken cancellationToken, bool forceSave = false, bool forceRefresh = false, bool allowSlowProviders = true) private async Task<bool> RefreshThemeSongs(IHasThemeMedia item, CancellationToken cancellationToken, bool forceSave = false, bool forceRefresh = false)
{ {
var newThemeSongs = LoadThemeSongs().ToList(); var newThemeSongs = LoadThemeSongs().ToList();
var newThemeSongIds = newThemeSongs.Select(i => i.Id).ToList(); var newThemeSongIds = newThemeSongs.Select(i => i.Id).ToList();
var themeSongsChanged = !item.ThemeSongIds.SequenceEqual(newThemeSongIds); var themeSongsChanged = !item.ThemeSongIds.SequenceEqual(newThemeSongIds);
var tasks = newThemeSongs.Select(i => i.RefreshMetadata(cancellationToken, forceSave, forceRefresh, allowSlowProviders, resetResolveArgs: false)); var tasks = newThemeSongs.Select(i => i.RefreshMetadata(new MetadataRefreshOptions
{
ForceSave = forceSave,
ReplaceAllMetadata = forceRefresh,
ResetResolveArgs = false
}, cancellationToken));
var results = await Task.WhenAll(tasks).ConfigureAwait(false); var results = await Task.WhenAll(tasks).ConfigureAwait(false);
@ -1456,7 +1489,13 @@ namespace MediaBrowser.Controller.Entities
// Refresh metadata // Refresh metadata
// Need to disable slow providers or the image might get re-downloaded // Need to disable slow providers or the image might get re-downloaded
return RefreshMetadata(CancellationToken.None, forceSave: true, allowSlowProviders: false); return RefreshMetadata(new MetadataRefreshOptions
{
ForceSave = true,
ImageRefreshMode = MetadataRefreshMode.None,
MetadataRefreshMode = MetadataRefreshMode.None
}, CancellationToken.None);
} }
/// <summary> /// <summary>
@ -1482,8 +1521,10 @@ namespace MediaBrowser.Controller.Entities
/// <summary> /// <summary>
/// Validates that images within the item are still on the file system /// Validates that images within the item are still on the file system
/// </summary> /// </summary>
public void ValidateImages() public bool ValidateImages()
{ {
var changed = false;
// Only validate paths from the same directory - need to copy to a list because we are going to potentially modify the collection below // Only validate paths from the same directory - need to copy to a list because we are going to potentially modify the collection below
var deletedKeys = Images var deletedKeys = Images
.Where(image => !File.Exists(image.Value)) .Where(image => !File.Exists(image.Value))
@ -1494,14 +1535,28 @@ namespace MediaBrowser.Controller.Entities
foreach (var key in deletedKeys) foreach (var key in deletedKeys)
{ {
Images.Remove(key); Images.Remove(key);
changed = true;
} }
if (ValidateBackdrops())
{
changed = true;
}
if (ValidateScreenshots())
{
changed = true;
}
return changed;
} }
/// <summary> /// <summary>
/// Validates that backdrops within the item are still on the file system /// Validates that backdrops within the item are still on the file system
/// </summary> /// </summary>
public void ValidateBackdrops() private bool ValidateBackdrops()
{ {
var changed = false;
// Only validate paths from the same directory - need to copy to a list because we are going to potentially modify the collection below // Only validate paths from the same directory - need to copy to a list because we are going to potentially modify the collection below
var deletedImages = BackdropImagePaths var deletedImages = BackdropImagePaths
.Where(path => !File.Exists(path)) .Where(path => !File.Exists(path))
@ -1513,7 +1568,11 @@ namespace MediaBrowser.Controller.Entities
BackdropImagePaths.Remove(path); BackdropImagePaths.Remove(path);
RemoveImageSourceForPath(path); RemoveImageSourceForPath(path);
changed = true;
} }
return changed;
} }
/// <summary> /// <summary>
@ -1593,9 +1652,16 @@ namespace MediaBrowser.Controller.Entities
/// <summary> /// <summary>
/// Validates the screenshots. /// Validates the screenshots.
/// </summary> /// </summary>
public void ValidateScreenshots() private bool ValidateScreenshots()
{ {
var hasScreenshots = (IHasScreenshots)this; var changed = false;
var hasScreenshots = this as IHasScreenshots;
if (hasScreenshots == null)
{
return changed;
}
// Only validate paths from the same directory - need to copy to a list because we are going to potentially modify the collection below // Only validate paths from the same directory - need to copy to a list because we are going to potentially modify the collection below
var deletedImages = hasScreenshots.ScreenshotImagePaths var deletedImages = hasScreenshots.ScreenshotImagePaths
@ -1606,7 +1672,10 @@ namespace MediaBrowser.Controller.Entities
foreach (var path in deletedImages) foreach (var path in deletedImages)
{ {
hasScreenshots.ScreenshotImagePaths.Remove(path); hasScreenshots.ScreenshotImagePaths.Remove(path);
changed = true;
} }
return changed;
} }
/// <summary> /// <summary>
@ -1699,7 +1768,12 @@ namespace MediaBrowser.Controller.Entities
FileSystem.SwapFiles(file1, file2); FileSystem.SwapFiles(file1, file2);
// Directory watchers should repeat this, but do a quick refresh first // Directory watchers should repeat this, but do a quick refresh first
return RefreshMetadata(CancellationToken.None, forceSave: true, allowSlowProviders: false); return RefreshMetadata(new MetadataRefreshOptions
{
ForceSave = true,
MetadataRefreshMode = MetadataRefreshMode.None
}, CancellationToken.None);
} }
public virtual bool IsPlayed(User user) public virtual bool IsPlayed(User user)

View File

@ -3,6 +3,7 @@ using MediaBrowser.Common.Progress;
using MediaBrowser.Controller.Entities.TV; using MediaBrowser.Controller.Entities.TV;
using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Localization; using MediaBrowser.Controller.Localization;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Controller.Resolvers; using MediaBrowser.Controller.Resolvers;
using MediaBrowser.Model.Entities; using MediaBrowser.Model.Entities;
using MoreLinq; using MoreLinq;
@ -535,7 +536,13 @@ namespace MediaBrowser.Controller.Entities
try try
{ {
//refresh it //refresh it
await child.RefreshMetadata(cancellationToken, forceSave: currentTuple.Item2, forceRefresh: forceRefreshMetadata, resetResolveArgs: false).ConfigureAwait(false); await child.RefreshMetadata(new MetadataRefreshOptions
{
ForceSave = currentTuple.Item2,
ReplaceAllMetadata = forceRefreshMetadata,
ResetResolveArgs = false
}, cancellationToken).ConfigureAwait(false);
} }
catch (IOException ex) catch (IOException ex)
{ {
@ -907,9 +914,9 @@ namespace MediaBrowser.Controller.Entities
return item; return item;
} }
public override async Task<bool> RefreshMetadata(CancellationToken cancellationToken, bool forceSave = false, bool forceRefresh = false, bool allowSlowProviders = true, bool resetResolveArgs = true) public override async Task<bool> RefreshMetadataDirect(CancellationToken cancellationToken, bool forceSave = false, bool forceRefresh = false)
{ {
var changed = await base.RefreshMetadata(cancellationToken, forceSave, forceRefresh, allowSlowProviders, resetResolveArgs).ConfigureAwait(false); var changed = await base.RefreshMetadataDirect(cancellationToken, forceSave, forceRefresh).ConfigureAwait(false);
return (SupportsShortcutChildren && LocationType == LocationType.FileSystem && RefreshLinkedChildren()) || changed; return (SupportsShortcutChildren && LocationType == LocationType.FileSystem && RefreshLinkedChildren()) || changed;
} }

View File

@ -1,5 +1,6 @@
using MediaBrowser.Model.Entities; using MediaBrowser.Model.Entities;
using System; using System;
using System.Collections.Generic;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace MediaBrowser.Controller.Entities namespace MediaBrowser.Controller.Entities
@ -10,7 +11,7 @@ namespace MediaBrowser.Controller.Entities
/// Gets the name. /// Gets the name.
/// </summary> /// </summary>
/// <value>The name.</value> /// <value>The name.</value>
string Name { get; } string Name { get; set; }
/// <summary> /// <summary>
/// Gets the path. /// Gets the path.
@ -24,6 +25,12 @@ namespace MediaBrowser.Controller.Entities
/// <value>The identifier.</value> /// <value>The identifier.</value>
Guid Id { get; } Guid Id { get; }
/// <summary>
/// Gets the type of the location.
/// </summary>
/// <value>The type of the location.</value>
LocationType LocationType { get; }
/// <summary> /// <summary>
/// Gets the image path. /// Gets the image path.
/// </summary> /// </summary>
@ -81,6 +88,24 @@ namespace MediaBrowser.Controller.Entities
/// </summary> /// </summary>
/// <returns>System.String.</returns> /// <returns>System.String.</returns>
string GetPreferredMetadataLanguage(); string GetPreferredMetadataLanguage();
/// <summary>
/// Validates the images and returns true or false indicating if any were removed.
/// </summary>
bool ValidateImages();
/// <summary>
/// Gets or sets the backdrop image paths.
/// </summary>
/// <value>The backdrop image paths.</value>
List<string> BackdropImagePaths { get; set; }
/// <summary>
/// Determines whether [contains image with source URL] [the specified URL].
/// </summary>
/// <param name="url">The URL.</param>
/// <returns><c>true</c> if [contains image with source URL] [the specified URL]; otherwise, <c>false</c>.</returns>
bool ContainsImageWithSourceUrl(string url);
} }
public static class HasImagesExtensions public static class HasImagesExtensions

View File

@ -14,8 +14,10 @@ namespace MediaBrowser.Controller.Entities
List<string> ScreenshotImagePaths { get; set; } List<string> ScreenshotImagePaths { get; set; }
/// <summary> /// <summary>
/// Validates the screenshots. /// Determines whether [contains image with source URL] [the specified URL].
/// </summary> /// </summary>
void ValidateScreenshots(); /// <param name="url">The URL.</param>
/// <returns><c>true</c> if [contains image with source URL] [the specified URL]; otherwise, <c>false</c>.</returns>
bool ContainsImageWithSourceUrl(string url);
} }
} }

View File

@ -1,4 +1,5 @@
using MediaBrowser.Model.Configuration; using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Configuration;
using MediaBrowser.Model.Entities; using MediaBrowser.Model.Entities;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
@ -108,13 +109,11 @@ namespace MediaBrowser.Controller.Entities.Movies
/// <param name="cancellationToken">The cancellation token.</param> /// <param name="cancellationToken">The cancellation token.</param>
/// <param name="forceSave">if set to <c>true</c> [is new item].</param> /// <param name="forceSave">if set to <c>true</c> [is new item].</param>
/// <param name="forceRefresh">if set to <c>true</c> [force].</param> /// <param name="forceRefresh">if set to <c>true</c> [force].</param>
/// <param name="allowSlowProviders">if set to <c>true</c> [allow slow providers].</param>
/// <param name="resetResolveArgs">The reset resolve args.</param>
/// <returns>Task{System.Boolean}.</returns> /// <returns>Task{System.Boolean}.</returns>
public override async Task<bool> RefreshMetadata(CancellationToken cancellationToken, bool forceSave = false, bool forceRefresh = false, bool allowSlowProviders = true, bool resetResolveArgs = true) public override async Task<bool> RefreshMetadataDirect(CancellationToken cancellationToken, bool forceSave = false, bool forceRefresh = false)
{ {
// Kick off a task to refresh the main item // Kick off a task to refresh the main item
var result = await base.RefreshMetadata(cancellationToken, forceSave, forceRefresh, allowSlowProviders, resetResolveArgs).ConfigureAwait(false); var result = await base.RefreshMetadataDirect(cancellationToken, forceSave, forceRefresh).ConfigureAwait(false);
var specialFeaturesChanged = false; var specialFeaturesChanged = false;
@ -122,7 +121,7 @@ namespace MediaBrowser.Controller.Entities.Movies
// In other words, it must be part of the Parent/Child tree // In other words, it must be part of the Parent/Child tree
if (LocationType == LocationType.FileSystem && Parent != null && !IsInMixedFolder) if (LocationType == LocationType.FileSystem && Parent != null && !IsInMixedFolder)
{ {
specialFeaturesChanged = await RefreshSpecialFeatures(cancellationToken, forceSave, forceRefresh, allowSlowProviders).ConfigureAwait(false); specialFeaturesChanged = await RefreshSpecialFeatures(cancellationToken, forceSave, forceRefresh).ConfigureAwait(false);
} }
return specialFeaturesChanged || result; return specialFeaturesChanged || result;
@ -135,7 +134,13 @@ namespace MediaBrowser.Controller.Entities.Movies
var itemsChanged = !SpecialFeatureIds.SequenceEqual(newItemIds); var itemsChanged = !SpecialFeatureIds.SequenceEqual(newItemIds);
var tasks = newItems.Select(i => i.RefreshMetadata(cancellationToken, forceSave, forceRefresh, allowSlowProviders, resetResolveArgs: false)); var tasks = newItems.Select(i => i.RefreshMetadata(new MetadataRefreshOptions
{
ForceSave = forceSave,
ReplaceAllMetadata = forceRefresh,
ResetResolveArgs = false
}, cancellationToken));
var results = await Task.WhenAll(tasks).ConfigureAwait(false); var results = await Task.WhenAll(tasks).ConfigureAwait(false);

View File

@ -1,5 +1,6 @@
using MediaBrowser.Common.Configuration; using MediaBrowser.Common.Configuration;
using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Configuration; using MediaBrowser.Model.Configuration;
using MediaBrowser.Model.Serialization; using MediaBrowser.Model.Serialization;
using System; using System;
@ -212,7 +213,12 @@ namespace MediaBrowser.Controller.Entities
// Kick off a task to validate the media library // Kick off a task to validate the media library
Task.Run(() => ValidateMediaLibrary(new Progress<double>(), CancellationToken.None)); Task.Run(() => ValidateMediaLibrary(new Progress<double>(), CancellationToken.None));
return RefreshMetadata(CancellationToken.None, forceSave: true, forceRefresh: true); return RefreshMetadata(new MetadataRefreshOptions
{
ForceSave = true,
ReplaceAllMetadata = true
}, CancellationToken.None);
} }
/// <summary> /// <summary>
@ -275,17 +281,13 @@ namespace MediaBrowser.Controller.Entities
/// <param name="cancellationToken">The cancellation token.</param> /// <param name="cancellationToken">The cancellation token.</param>
/// <param name="forceSave">if set to <c>true</c> [is new item].</param> /// <param name="forceSave">if set to <c>true</c> [is new item].</param>
/// <param name="forceRefresh">if set to <c>true</c> [force].</param> /// <param name="forceRefresh">if set to <c>true</c> [force].</param>
/// <param name="allowSlowProviders">if set to <c>true</c> [allow slow providers].</param>
/// <returns>true if a provider reports we changed</returns> /// <returns>true if a provider reports we changed</returns>
public override async Task<bool> RefreshMetadata(CancellationToken cancellationToken, bool forceSave = false, bool forceRefresh = false, bool allowSlowProviders = true, bool resetResolveArgs = true) public override async Task<bool> RefreshMetadataDirect(CancellationToken cancellationToken, bool forceSave = false, bool forceRefresh = false)
{
if (resetResolveArgs)
{ {
// Reload this // Reload this
ResetResolveArgs(); ResetResolveArgs();
}
var updateReason = await ProviderManager.ExecuteMetadataProviders(this, cancellationToken, forceRefresh, allowSlowProviders).ConfigureAwait(false); var updateReason = await ProviderManager.ExecuteMetadataProviders(this, cancellationToken, forceRefresh).ConfigureAwait(false);
var changed = updateReason.HasValue; var changed = updateReason.HasValue;

View File

@ -1,4 +1,5 @@
using MediaBrowser.Controller.Persistence; using MediaBrowser.Controller.Persistence;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Controller.Resolvers; using MediaBrowser.Controller.Resolvers;
using MediaBrowser.Model.Entities; using MediaBrowser.Model.Entities;
using System; using System;
@ -164,13 +165,11 @@ namespace MediaBrowser.Controller.Entities
/// <param name="cancellationToken">The cancellation token.</param> /// <param name="cancellationToken">The cancellation token.</param>
/// <param name="forceSave">if set to <c>true</c> [is new item].</param> /// <param name="forceSave">if set to <c>true</c> [is new item].</param>
/// <param name="forceRefresh">if set to <c>true</c> [force].</param> /// <param name="forceRefresh">if set to <c>true</c> [force].</param>
/// <param name="allowSlowProviders">if set to <c>true</c> [allow slow providers].</param>
/// <param name="resetResolveArgs">The reset resolve args.</param>
/// <returns>true if a provider reports we changed</returns> /// <returns>true if a provider reports we changed</returns>
public override async Task<bool> RefreshMetadata(CancellationToken cancellationToken, bool forceSave = false, bool forceRefresh = false, bool allowSlowProviders = true, bool resetResolveArgs = true) public override async Task<bool> RefreshMetadataDirect(CancellationToken cancellationToken, bool forceSave = false, bool forceRefresh = false)
{ {
// Kick off a task to refresh the main item // Kick off a task to refresh the main item
var result = await base.RefreshMetadata(cancellationToken, forceSave, forceRefresh, allowSlowProviders, resetResolveArgs).ConfigureAwait(false); var result = await base.RefreshMetadataDirect(cancellationToken, forceSave, forceRefresh).ConfigureAwait(false);
var additionalPartsChanged = false; var additionalPartsChanged = false;
@ -181,7 +180,7 @@ namespace MediaBrowser.Controller.Entities
{ {
try try
{ {
additionalPartsChanged = await RefreshAdditionalParts(cancellationToken, forceSave, forceRefresh, allowSlowProviders).ConfigureAwait(false); additionalPartsChanged = await RefreshAdditionalParts(cancellationToken, forceSave, forceRefresh).ConfigureAwait(false);
} }
catch (IOException ex) catch (IOException ex)
{ {
@ -208,7 +207,12 @@ namespace MediaBrowser.Controller.Entities
var itemsChanged = !AdditionalPartIds.SequenceEqual(newItemIds); var itemsChanged = !AdditionalPartIds.SequenceEqual(newItemIds);
var tasks = newItems.Select(i => i.RefreshMetadata(cancellationToken, forceSave, forceRefresh, allowSlowProviders)); var tasks = newItems.Select(i => i.RefreshMetadata(new MetadataRefreshOptions
{
ForceSave = forceSave,
ReplaceAllMetadata = forceRefresh
}, cancellationToken));
var results = await Task.WhenAll(tasks).ConfigureAwait(false); var results = await Task.WhenAll(tasks).ConfigureAwait(false);

View File

@ -1,29 +0,0 @@
using System;
namespace MediaBrowser.Controller.IO
{
public interface IDirectoryWatchers : IDisposable
{
/// <summary>
/// Add the path to our temporary ignore list. Use when writing to a path within our listening scope.
/// </summary>
/// <param name="path">The path.</param>
void TemporarilyIgnore(string path);
/// <summary>
/// Removes the temp ignore.
/// </summary>
/// <param name="path">The path.</param>
void RemoveTempIgnore(string path);
/// <summary>
/// Starts this instance.
/// </summary>
void Start();
/// <summary>
/// Stops this instance.
/// </summary>
void Stop();
}
}

View File

@ -0,0 +1,36 @@
using System;
namespace MediaBrowser.Controller.Library
{
public interface ILibraryMonitor : IDisposable
{
/// <summary>
/// Starts this instance.
/// </summary>
void Start();
/// <summary>
/// Stops this instance.
/// </summary>
void Stop();
/// <summary>
/// Reports the file system change beginning.
/// </summary>
/// <param name="path">The path.</param>
void ReportFileSystemChangeBeginning(string path);
/// <summary>
/// Reports the file system change complete.
/// </summary>
/// <param name="path">The path.</param>
/// <param name="refreshPath">if set to <c>true</c> [refresh path].</param>
void ReportFileSystemChangeComplete(string path, bool refreshPath);
/// <summary>
/// Reports the file system changed.
/// </summary>
/// <param name="path">The path.</param>
void ReportFileSystemChanged(string path);
}
}

View File

@ -1,4 +1,5 @@
using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Entities; using MediaBrowser.Model.Entities;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
@ -11,8 +12,6 @@ namespace MediaBrowser.Controller.LiveTv
string MediaType { get; } string MediaType { get; }
LocationType LocationType { get; }
RecordingInfo RecordingInfo { get; set; } RecordingInfo RecordingInfo { get; set; }
string GetClientTypeName(); string GetClientTypeName();
@ -21,6 +20,6 @@ namespace MediaBrowser.Controller.LiveTv
bool IsParentalAllowed(User user); bool IsParentalAllowed(User user);
Task<bool> RefreshMetadata(CancellationToken cancellationToken, bool forceSave = false, bool forceRefresh = false, bool allowSlowProviders = true, bool resetResolveArgs = true); Task<bool> RefreshMetadata(MetadataRefreshOptions options, CancellationToken cancellationToken);
} }
} }

View File

@ -1,4 +1,5 @@
using System.IO; using MediaBrowser.Controller.Drawing;
using System.IO;
namespace MediaBrowser.Controller.LiveTv namespace MediaBrowser.Controller.LiveTv
{ {
@ -14,6 +15,6 @@ namespace MediaBrowser.Controller.LiveTv
/// Gets or sets the type of the MIME. /// Gets or sets the type of the MIME.
/// </summary> /// </summary>
/// <value>The type of the MIME.</value> /// <value>The type of the MIME.</value>
public string MimeType { get; set; } public ImageFormat Format { get; set; }
} }
} }

View File

@ -69,6 +69,7 @@
<Link>Properties\SharedVersion.cs</Link> <Link>Properties\SharedVersion.cs</Link>
</Compile> </Compile>
<Compile Include="Drawing\IImageProcessor.cs" /> <Compile Include="Drawing\IImageProcessor.cs" />
<Compile Include="Drawing\ImageFormat.cs" />
<Compile Include="Drawing\ImageProcessingOptions.cs" /> <Compile Include="Drawing\ImageProcessingOptions.cs" />
<Compile Include="Dto\IDtoService.cs" /> <Compile Include="Dto\IDtoService.cs" />
<Compile Include="Entities\AdultVideo.cs" /> <Compile Include="Entities\AdultVideo.cs" />
@ -143,8 +144,17 @@
<Compile Include="Persistence\IFileOrganizationRepository.cs" /> <Compile Include="Persistence\IFileOrganizationRepository.cs" />
<Compile Include="Persistence\MediaStreamQuery.cs" /> <Compile Include="Persistence\MediaStreamQuery.cs" />
<Compile Include="Providers\IDynamicInfoProvider.cs" /> <Compile Include="Providers\IDynamicInfoProvider.cs" />
<Compile Include="Providers\IHasMetadata.cs" />
<Compile Include="Providers\IImageProvider.cs" /> <Compile Include="Providers\IImageProvider.cs" />
<Compile Include="Providers\IProviderRepository.cs" />
<Compile Include="Providers\IRemoteImageProvider.cs" />
<Compile Include="Providers\ILocalImageProvider.cs" />
<Compile Include="Providers\IMetadataProvider.cs" />
<Compile Include="Providers\IMetadataService.cs" />
<Compile Include="Providers\ItemId.cs" />
<Compile Include="Providers\MetadataRefreshOptions.cs" />
<Compile Include="Providers\NameParser.cs" /> <Compile Include="Providers\NameParser.cs" />
<Compile Include="Providers\MetadataStatus.cs" />
<Compile Include="Session\ISessionManager.cs" /> <Compile Include="Session\ISessionManager.cs" />
<Compile Include="Drawing\ImageExtensions.cs" /> <Compile Include="Drawing\ImageExtensions.cs" />
<Compile Include="Entities\AggregateFolder.cs" /> <Compile Include="Entities\AggregateFolder.cs" />
@ -174,7 +184,7 @@
<Compile Include="Entities\Video.cs" /> <Compile Include="Entities\Video.cs" />
<Compile Include="Entities\CollectionFolder.cs" /> <Compile Include="Entities\CollectionFolder.cs" />
<Compile Include="Entities\Year.cs" /> <Compile Include="Entities\Year.cs" />
<Compile Include="IO\IDirectoryWatchers.cs" /> <Compile Include="Library\ILibraryMonitor.cs" />
<Compile Include="IServerApplicationHost.cs" /> <Compile Include="IServerApplicationHost.cs" />
<Compile Include="IServerApplicationPaths.cs" /> <Compile Include="IServerApplicationPaths.cs" />
<Compile Include="Library\SearchHintInfo.cs" /> <Compile Include="Library\SearchHintInfo.cs" />

View File

@ -1,5 +1,4 @@
using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Entities; using MediaBrowser.Model.Entities;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
@ -112,22 +111,6 @@ namespace MediaBrowser.Controller.Persistence
/// <param name="cancellationToken">The cancellation token.</param> /// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Task.</returns> /// <returns>Task.</returns>
Task SaveMediaStreams(Guid id, IEnumerable<MediaStream> streams, CancellationToken cancellationToken); Task SaveMediaStreams(Guid id, IEnumerable<MediaStream> streams, CancellationToken cancellationToken);
/// <summary>
/// Gets the provider history.
/// </summary>
/// <param name="itemId">The item identifier.</param>
/// <returns>IEnumerable{BaseProviderInfo}.</returns>
IEnumerable<BaseProviderInfo> GetProviderHistory(Guid itemId);
/// <summary>
/// Saves the provider history.
/// </summary>
/// <param name="id">The identifier.</param>
/// <param name="history">The history.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Task.</returns>
Task SaveProviderHistory(Guid id, IEnumerable<BaseProviderInfo> history, CancellationToken cancellationToken);
} }
} }

View File

@ -0,0 +1,31 @@
using MediaBrowser.Controller.Entities;
using MediaBrowser.Model.Entities;
using System;
using System.Collections.Generic;
namespace MediaBrowser.Controller.Providers
{
/// <summary>
/// Interface IHasMetadata
/// </summary>
public interface IHasMetadata : IHasImages, IHasProviderIds
{
/// <summary>
/// Gets the preferred metadata country code.
/// </summary>
/// <returns>System.String.</returns>
string GetPreferredMetadataCountryCode();
/// <summary>
/// Gets the locked fields.
/// </summary>
/// <value>The locked fields.</value>
List<MetadataFields> LockedFields { get; }
/// <summary>
/// Gets or sets the date last saved.
/// </summary>
/// <value>The date last saved.</value>
DateTime DateLastSaved { get; set; }
}
}

View File

@ -1,9 +1,4 @@
using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Providers;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
namespace MediaBrowser.Controller.Providers namespace MediaBrowser.Controller.Providers
{ {
@ -26,26 +21,9 @@ namespace MediaBrowser.Controller.Providers
bool Supports(IHasImages item); bool Supports(IHasImages item);
/// <summary> /// <summary>
/// Gets the images. /// Gets the order.
/// </summary> /// </summary>
/// <param name="item">The item.</param> /// <value>The order.</value>
/// <param name="imageType">Type of the image.</param> int Order { get; }
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Task{IEnumerable{RemoteImageInfo}}.</returns>
Task<IEnumerable<RemoteImageInfo>> GetImages(IHasImages item, ImageType imageType, CancellationToken cancellationToken);
/// <summary>
/// Gets the images.
/// </summary>
/// <param name="item">The item.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Task{IEnumerable{RemoteImageInfo}}.</returns>
Task<IEnumerable<RemoteImageInfo>> GetAllImages(IHasImages item, CancellationToken cancellationToken);
/// <summary>
/// Gets the priority.
/// </summary>
/// <value>The priority.</value>
int Priority { get; }
} }
} }

View File

@ -0,0 +1,66 @@
using MediaBrowser.Controller.Drawing;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Model.Entities;
using System.Collections.Generic;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
namespace MediaBrowser.Controller.Providers
{
/// <summary>
/// This is just a marker interface
/// </summary>
public interface ILocalImageProvider : IImageProvider
{
}
public interface IImageFileProvider : ILocalImageProvider
{
List<LocalImageInfo> GetImages(IHasImages item);
}
public class LocalImageInfo
{
public string Path { get; set; }
public ImageType Type { get; set; }
}
public interface IDynamicImageProvider : ILocalImageProvider
{
/// <summary>
/// Gets the supported images.
/// </summary>
/// <param name="item">The item.</param>
/// <returns>IEnumerable{ImageType}.</returns>
IEnumerable<ImageType> GetSupportedImages(IHasImages item);
/// <summary>
/// Gets the image.
/// </summary>
/// <param name="item">The item.</param>
/// <param name="type">The type.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Task{DynamicImageResponse}.</returns>
Task<DynamicImageResponse> GetImage(IHasImages item, ImageType type, CancellationToken cancellationToken);
}
public class DynamicImageInfo
{
public string ImageId { get; set; }
public ImageType Type { get; set; }
}
public class DynamicImageResponse
{
public string Path { get; set; }
public Stream Stream { get; set; }
public ImageFormat Format { get; set; }
public bool HasImage { get; set; }
public void SetFormatFromMimeType(string mimeType)
{
}
}
}

View File

@ -0,0 +1,68 @@
using System;
using System.Threading;
using System.Threading.Tasks;
namespace MediaBrowser.Controller.Providers
{
/// <summary>
/// Marker interface
/// </summary>
public interface IMetadataProvider
{
/// <summary>
/// Gets the name.
/// </summary>
/// <value>The name.</value>
string Name { get; }
}
public interface IMetadataProvider<TItemType> : IMetadataProvider
where TItemType : IHasMetadata
{
}
public interface ILocalMetadataProvider : IMetadataProvider
{
/// <summary>
/// Determines whether [has local metadata] [the specified item].
/// </summary>
/// <param name="item">The item.</param>
/// <returns><c>true</c> if [has local metadata] [the specified item]; otherwise, <c>false</c>.</returns>
bool HasLocalMetadata(IHasMetadata item);
}
public interface IRemoteMetadataProvider : IMetadataProvider
{
}
public interface IRemoteMetadataProvider<TItemType> : IMetadataProvider<TItemType>, IRemoteMetadataProvider
where TItemType : IHasMetadata
{
Task<MetadataResult<TItemType>> GetMetadata(ItemId id, CancellationToken cancellationToken);
}
public interface ILocalMetadataProvider<TItemType> : IMetadataProvider<TItemType>, ILocalMetadataProvider
where TItemType : IHasMetadata
{
Task<MetadataResult<TItemType>> GetMetadata(string path, CancellationToken cancellationToken);
}
public interface IHasChangeMonitor
{
/// <summary>
/// Determines whether the specified item has changed.
/// </summary>
/// <param name="item">The item.</param>
/// <param name="date">The date.</param>
/// <returns><c>true</c> if the specified item has changed; otherwise, <c>false</c>.</returns>
bool HasChanged(IHasMetadata item, DateTime date);
}
public class MetadataResult<T>
where T : IHasMetadata
{
public bool HasMetadata { get; set; }
public T Item { get; set; }
}
}

View File

@ -0,0 +1,38 @@
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
namespace MediaBrowser.Controller.Providers
{
public interface IMetadataService
{
/// <summary>
/// Adds the parts.
/// </summary>
/// <param name="providers">The providers.</param>
/// <param name="imageProviders">The image providers.</param>
void AddParts(IEnumerable<IMetadataProvider> providers, IEnumerable<IImageProvider> imageProviders);
/// <summary>
/// Determines whether this instance can refresh the specified item.
/// </summary>
/// <param name="item">The item.</param>
/// <returns><c>true</c> if this instance can refresh the specified item; otherwise, <c>false</c>.</returns>
bool CanRefresh(IHasMetadata item);
/// <summary>
/// Refreshes the metadata.
/// </summary>
/// <param name="item">The item.</param>
/// <param name="options">The options.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Task.</returns>
Task RefreshMetadata(IHasMetadata item, MetadataRefreshOptions options, CancellationToken cancellationToken);
/// <summary>
/// Gets the order.
/// </summary>
/// <value>The order.</value>
int Order { get; }
}
}

View File

@ -14,15 +14,23 @@ namespace MediaBrowser.Controller.Providers
/// </summary> /// </summary>
public interface IProviderManager public interface IProviderManager
{ {
/// <summary>
/// Refreshes the metadata.
/// </summary>
/// <param name="item">The item.</param>
/// <param name="options">The options.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Task.</returns>
Task RefreshMetadata(IHasMetadata item, MetadataRefreshOptions options, CancellationToken cancellationToken);
/// <summary> /// <summary>
/// Executes the metadata providers. /// Executes the metadata providers.
/// </summary> /// </summary>
/// <param name="item">The item.</param> /// <param name="item">The item.</param>
/// <param name="cancellationToken">The cancellation token.</param> /// <param name="cancellationToken">The cancellation token.</param>
/// <param name="force">if set to <c>true</c> [force].</param> /// <param name="force">if set to <c>true</c> [force].</param>
/// <param name="allowSlowProviders">if set to <c>true</c> [allow slow providers].</param>
/// <returns>Task{System.Boolean}.</returns> /// <returns>Task{System.Boolean}.</returns>
Task<ItemUpdateType?> ExecuteMetadataProviders(BaseItem item, CancellationToken cancellationToken, bool force = false, bool allowSlowProviders = true); Task<ItemUpdateType?> ExecuteMetadataProviders(BaseItem item, CancellationToken cancellationToken, bool force = false);
/// <summary> /// <summary>
/// Saves the image. /// Saves the image.
@ -54,7 +62,9 @@ namespace MediaBrowser.Controller.Providers
/// </summary> /// </summary>
/// <param name="providers">The providers.</param> /// <param name="providers">The providers.</param>
/// <param name="imageProviders">The image providers.</param> /// <param name="imageProviders">The image providers.</param>
void AddParts(IEnumerable<BaseMetadataProvider> providers, IEnumerable<IImageProvider> imageProviders); /// <param name="metadataServices">The metadata services.</param>
/// <param name="metadataProviders">The metadata providers.</param>
void AddParts(IEnumerable<BaseMetadataProvider> providers, IEnumerable<IImageProvider> imageProviders, IEnumerable<IMetadataService> metadataServices, IEnumerable<IMetadataProvider> metadataProviders);
/// <summary> /// <summary>
/// Gets the available remote images. /// Gets the available remote images.
@ -70,7 +80,7 @@ namespace MediaBrowser.Controller.Providers
/// Gets the image providers. /// Gets the image providers.
/// </summary> /// </summary>
/// <param name="item">The item.</param> /// <param name="item">The item.</param>
/// <returns>IEnumerable{IImageProvider}.</returns> /// <returns>IEnumerable{ImageProviderInfo}.</returns>
IEnumerable<IImageProvider> GetImageProviders(BaseItem item); IEnumerable<ImageProviderInfo> GetImageProviderInfo(BaseItem item);
} }
} }

View File

@ -0,0 +1,48 @@
using MediaBrowser.Controller.Persistence;
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
namespace MediaBrowser.Controller.Providers
{
public interface IProviderRepository : IRepository
{
/// <summary>
/// Gets the provider history.
/// </summary>
/// <param name="itemId">The item identifier.</param>
/// <returns>IEnumerable{BaseProviderInfo}.</returns>
IEnumerable<BaseProviderInfo> GetProviderHistory(Guid itemId);
/// <summary>
/// Saves the provider history.
/// </summary>
/// <param name="id">The identifier.</param>
/// <param name="history">The history.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Task.</returns>
Task SaveProviderHistory(Guid id, IEnumerable<BaseProviderInfo> history, CancellationToken cancellationToken);
/// <summary>
/// Gets the metadata status.
/// </summary>
/// <param name="itemId">The item identifier.</param>
/// <returns>MetadataStatus.</returns>
MetadataStatus GetMetadataStatus(Guid itemId);
/// <summary>
/// Saves the metadata status.
/// </summary>
/// <param name="status">The status.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Task.</returns>
Task SaveMetadataStatus(MetadataStatus status, CancellationToken cancellationToken);
/// <summary>
/// Initializes this instance.
/// </summary>
/// <returns>Task.</returns>
Task Initialize();
}
}

View File

@ -0,0 +1,48 @@
using MediaBrowser.Common.Net;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Providers;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
namespace MediaBrowser.Controller.Providers
{
/// <summary>
/// Interface IImageProvider
/// </summary>
public interface IRemoteImageProvider : IImageProvider
{
/// <summary>
/// Gets the supported images.
/// </summary>
/// <param name="item">The item.</param>
/// <returns>IEnumerable{ImageType}.</returns>
IEnumerable<ImageType> GetSupportedImages(IHasImages item);
/// <summary>
/// Gets the images.
/// </summary>
/// <param name="item">The item.</param>
/// <param name="imageType">Type of the image.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Task{IEnumerable{RemoteImageInfo}}.</returns>
Task<IEnumerable<RemoteImageInfo>> GetImages(IHasImages item, ImageType imageType, CancellationToken cancellationToken);
/// <summary>
/// Gets the images.
/// </summary>
/// <param name="item">The item.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Task{IEnumerable{RemoteImageInfo}}.</returns>
Task<IEnumerable<RemoteImageInfo>> GetAllImages(IHasImages item, CancellationToken cancellationToken);
/// <summary>
/// Gets the image response.
/// </summary>
/// <param name="url">The URL.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Task{HttpResponseInfo}.</returns>
Task<HttpResponseInfo> GetImageResponse(string url, CancellationToken cancellationToken);
}
}

View File

@ -0,0 +1,35 @@
using MediaBrowser.Model.Entities;
using System;
using System.Collections.Generic;
namespace MediaBrowser.Controller.Providers
{
public class ItemId : IHasProviderIds
{
/// <summary>
/// Gets or sets the name.
/// </summary>
/// <value>The name.</value>
public string Name { get; set; }
/// <summary>
/// Gets or sets the metadata language.
/// </summary>
/// <value>The metadata language.</value>
public string MetadataLanguage { get; set; }
/// <summary>
/// Gets or sets the metadata country code.
/// </summary>
/// <value>The metadata country code.</value>
public string MetadataCountryCode { get; set; }
/// <summary>
/// Gets or sets the provider ids.
/// </summary>
/// <value>The provider ids.</value>
public Dictionary<string, string> ProviderIds { get; set; }
public ItemId()
{
ProviderIds = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
}
}
}

View File

@ -0,0 +1,49 @@
using System;
namespace MediaBrowser.Controller.Providers
{
public class MetadataRefreshOptions : ImageRefreshOptions
{
/// <summary>
/// When paired with MetadataRefreshMode=FullRefresh, all existing data will be overwritten with new data from the providers.
/// </summary>
public bool ReplaceAllMetadata { get; set; }
public MetadataRefreshMode MetadataRefreshMode { get; set; }
/// <summary>
/// TODO: deprecate. Keeping this for now, for api compatibility
/// </summary>
[Obsolete]
public bool ForceSave { get; set; }
/// <summary>
/// TODO: deprecate. Keeping this for now, for api compatibility
/// </summary>
[Obsolete]
public bool ResetResolveArgs { get; set; }
}
public class ImageRefreshOptions
{
public MetadataRefreshMode ImageRefreshMode { get; set; }
}
public enum MetadataRefreshMode
{
/// <summary>
/// Providers will be executed based on default rules
/// </summary>
EnsureMetadata,
/// <summary>
/// No providers will be executed
/// </summary>
None,
/// <summary>
/// All providers will be executed to search for new metadata
/// </summary>
FullRefresh
}
}

View File

@ -0,0 +1,122 @@
using System;
using System.Collections.Generic;
using System.Linq;
using MediaBrowser.Common.Extensions;
namespace MediaBrowser.Controller.Providers
{
public class MetadataStatus
{
/// <summary>
/// Gets or sets the item identifier.
/// </summary>
/// <value>The item identifier.</value>
public Guid ItemId { get; set; }
/// <summary>
/// Gets or sets the date last metadata refresh.
/// </summary>
/// <value>The date last metadata refresh.</value>
public DateTime? DateLastMetadataRefresh { get; set; }
/// <summary>
/// Gets or sets the date last images refresh.
/// </summary>
/// <value>The date last images refresh.</value>
public DateTime? DateLastImagesRefresh { get; set; }
/// <summary>
/// Gets or sets the last result.
/// </summary>
/// <value>The last result.</value>
public ProviderRefreshStatus LastStatus { get; set; }
/// <summary>
/// Gets or sets the last result error message.
/// </summary>
/// <value>The last result error message.</value>
public string LastErrorMessage { get; set; }
/// <summary>
/// Gets or sets the providers refreshed.
/// </summary>
/// <value>The providers refreshed.</value>
public List<Guid> MetadataProvidersRefreshed { get; set; }
public List<Guid> ImageProvidersRefreshed { get; set; }
public void AddStatus(ProviderRefreshStatus status, string errorMessage)
{
if (LastStatus != status)
{
IsDirty = true;
}
if (string.IsNullOrEmpty(LastErrorMessage))
{
LastErrorMessage = errorMessage;
}
if (LastStatus == ProviderRefreshStatus.Success)
{
LastStatus = status;
}
}
public MetadataStatus()
{
LastStatus = ProviderRefreshStatus.Success;
MetadataProvidersRefreshed = new List<Guid>();
ImageProvidersRefreshed = new List<Guid>();
}
public bool IsDirty { get; private set; }
public void SetDateLastMetadataRefresh(DateTime date)
{
if (date != (DateLastMetadataRefresh ?? DateTime.MinValue))
{
IsDirty = true;
}
DateLastMetadataRefresh = date;
}
public void SetDateLastImagesRefresh(DateTime date)
{
if (date != (DateLastImagesRefresh ?? DateTime.MinValue))
{
IsDirty = true;
}
DateLastImagesRefresh = date;
}
public void AddImageProvidersRefreshed(List<Guid> providerIds)
{
var count = ImageProvidersRefreshed.Count;
providerIds.AddRange(ImageProvidersRefreshed);
ImageProvidersRefreshed = providerIds.Distinct().ToList();
if (ImageProvidersRefreshed.Count != count)
{
IsDirty = true;
}
}
public void AddMetadataProvidersRefreshed(List<Guid> providerIds)
{
var count = MetadataProvidersRefreshed.Count;
providerIds.AddRange(MetadataProvidersRefreshed);
MetadataProvidersRefreshed = providerIds.Distinct().ToList();
if (MetadataProvidersRefreshed.Count != count)
{
IsDirty = true;
}
}
}
}

View File

@ -165,7 +165,7 @@ namespace MediaBrowser.Model.Configuration
/// different directories and files. /// different directories and files.
/// </summary> /// </summary>
/// <value>The file watcher delay.</value> /// <value>The file watcher delay.</value>
public int FileWatcherDelay { get; set; } public int RealtimeWatcherDelay { get; set; }
/// <summary> /// <summary>
/// Gets or sets a value indicating whether [enable dashboard response caching]. /// Gets or sets a value indicating whether [enable dashboard response caching].
@ -250,7 +250,7 @@ namespace MediaBrowser.Model.Configuration
MaxResumePct = 90; MaxResumePct = 90;
MinResumeDurationSeconds = Convert.ToInt32(TimeSpan.FromMinutes(5).TotalSeconds); MinResumeDurationSeconds = Convert.ToInt32(TimeSpan.FromMinutes(5).TotalSeconds);
FileWatcherDelay = 8; RealtimeWatcherDelay = 20;
RecentItemDays = 10; RecentItemDays = 10;

View File

@ -116,6 +116,12 @@ namespace MediaBrowser.Model.Dto
/// <value>The overview.</value> /// <value>The overview.</value>
public string Overview { get; set; } public string Overview { get; set; }
/// <summary>
/// Gets or sets the name of the TMDB collection.
/// </summary>
/// <value>The name of the TMDB collection.</value>
public string TmdbCollectionName { get; set; }
/// <summary> /// <summary>
/// Gets or sets the taglines. /// Gets or sets the taglines.
/// </summary> /// </summary>

View File

@ -20,6 +20,17 @@ namespace MediaBrowser.Model.Entities
/// </summary> /// </summary>
public static class ProviderIdsExtensions public static class ProviderIdsExtensions
{ {
/// <summary>
/// Determines whether [has provider identifier] [the specified instance].
/// </summary>
/// <param name="instance">The instance.</param>
/// <param name="provider">The provider.</param>
/// <returns><c>true</c> if [has provider identifier] [the specified instance]; otherwise, <c>false</c>.</returns>
public static bool HasProviderId(this IHasProviderIds instance, MetadataProviders provider)
{
return !string.IsNullOrEmpty(instance.GetProviderId(provider.ToString()));
}
/// <summary> /// <summary>
/// Gets a provider id /// Gets a provider id
/// </summary> /// </summary>

View File

@ -12,9 +12,9 @@
public string Name { get; set; } public string Name { get; set; }
/// <summary> /// <summary>
/// Gets or sets the priority. /// Gets or sets the order.
/// </summary> /// </summary>
/// <value>The priority.</value> /// <value>The order.</value>
public int Priority { get; set; } public int Order { get; set; }
} }
} }

View File

@ -151,6 +151,11 @@ namespace MediaBrowser.Model.Querying
/// </summary> /// </summary>
Tags, Tags,
/// <summary>
/// The TMDB collection name
/// </summary>
TmdbCollectionName,
/// <summary> /// <summary>
/// The trailer url of the item /// The trailer url of the item
/// </summary> /// </summary>

View File

@ -0,0 +1,327 @@
using MediaBrowser.Common.IO;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.Movies;
using MediaBrowser.Controller.Entities.TV;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Entities;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
namespace MediaBrowser.Providers.All
{
public class LocalImageProvider : IImageFileProvider
{
private readonly IFileSystem _fileSystem;
public LocalImageProvider(IFileSystem fileSystem)
{
_fileSystem = fileSystem;
}
public string Name
{
get { return "Local Images"; }
}
public int Order
{
get { return 0; }
}
public bool Supports(IHasImages item)
{
var locationType = item.LocationType;
if (locationType == LocationType.FileSystem)
{
// Episode has it's own provider
if (item is Episode)
{
return false;
}
return true;
}
if (locationType == LocationType.Virtual)
{
var season = item as Season;
if (season != null)
{
var series = season.Series;
if (series != null && series.LocationType == LocationType.FileSystem)
{
return true;
}
}
}
return false;
}
private IEnumerable<string> GetFiles(IHasImages item, bool includeDirectories)
{
if (item.LocationType != LocationType.FileSystem)
{
return new List<string>();
}
var path = item.Path;
var fileInfo = _fileSystem.GetFileSystemInfo(path) as DirectoryInfo;
if (fileInfo == null)
{
path = Path.GetDirectoryName(path);
}
if (includeDirectories)
{
return Directory.EnumerateFileSystemEntries(path, "*", SearchOption.TopDirectoryOnly);
}
return Directory.EnumerateFiles(path, "*", SearchOption.TopDirectoryOnly);
}
public List<LocalImageInfo> GetImages(IHasImages item)
{
var files = GetFileDictionary(GetFiles(item, true));
var list = new List<LocalImageInfo>();
PopulateImages(item, list, files);
return list;
}
private void PopulateImages(IHasImages item, List<LocalImageInfo> images, Dictionary<string, string> files)
{
var imagePrefix = string.Empty;
var baseItem = item as BaseItem;
if (baseItem != null && baseItem.IsInMixedFolder)
{
imagePrefix = Path.GetFileNameWithoutExtension(item.Path) + "-";
}
PopulatePrimaryImages(item, images, files, imagePrefix);
PopulateBackdrops(item, images, files, imagePrefix);
PopulateScreenshots(images, files, imagePrefix);
AddImage(files, images, imagePrefix + "logo", ImageType.Logo);
AddImage(files, images, imagePrefix + "clearart", ImageType.Art);
AddImage(files, images, imagePrefix + "disc", ImageType.Disc);
AddImage(files, images, imagePrefix + "cdart", ImageType.Disc);
AddImage(files, images, imagePrefix + "box", ImageType.Box);
AddImage(files, images, imagePrefix + "back", ImageType.BoxRear);
AddImage(files, images, imagePrefix + "boxrear", ImageType.BoxRear);
AddImage(files, images, imagePrefix + "menu", ImageType.Menu);
// Banner
AddImage(files, images, imagePrefix + "banner", ImageType.Banner);
// Thumb
AddImage(files, images, imagePrefix + "thumb", ImageType.Thumb);
AddImage(files, images, imagePrefix + "landscape", ImageType.Thumb);
var season = item as Season;
if (season != null)
{
PopulateSeasonImagesFromSeriesFolder(season, images);
}
}
private void PopulatePrimaryImages(IHasImages item, List<LocalImageInfo> images, Dictionary<string, string> files, string imagePrefix)
{
AddImage(files, images, imagePrefix + "folder", ImageType.Primary);
AddImage(files, images, imagePrefix + "cover", ImageType.Primary);
AddImage(files, images, imagePrefix + "poster", ImageType.Primary);
AddImage(files, images, imagePrefix + "default", ImageType.Primary);
// Support plex/xbmc convention
if (item is Series)
{
AddImage(files, images, imagePrefix + "show", ImageType.Primary);
}
// Support plex/xbmc convention
if (item is Movie || item is MusicVideo || item is AdultVideo)
{
AddImage(files, images, imagePrefix + "movie", ImageType.Primary);
}
if (string.IsNullOrEmpty(item.Path))
{
var name = Path.GetFileNameWithoutExtension(item.Path);
if (!string.IsNullOrEmpty(name))
{
AddImage(files, images, name, ImageType.Primary);
AddImage(files, images, name + "-poster", ImageType.Primary);
}
}
}
private void PopulateBackdrops(IHasImages item, List<LocalImageInfo> images, Dictionary<string, string> files, string imagePrefix)
{
PopulateBackdrops(images, files, imagePrefix, "backdrop", "backdrop", ImageType.Backdrop);
if (string.IsNullOrEmpty(item.Path))
{
var name = Path.GetFileNameWithoutExtension(item.Path);
if (!string.IsNullOrEmpty(name))
{
AddImage(files, images, imagePrefix + name + "-fanart", ImageType.Backdrop);
}
}
PopulateBackdrops(images, files, imagePrefix, "fanart", "fanart-", ImageType.Backdrop);
PopulateBackdrops(images, files, imagePrefix, "background", "background-", ImageType.Backdrop);
PopulateBackdrops(images, files, imagePrefix, "art", "art-", ImageType.Backdrop);
string extraFanartFolder;
if (files.TryGetValue("extrafanart", out extraFanartFolder))
{
PopulateBackdropsFromExtraFanart(extraFanartFolder, images);
}
}
private void PopulateBackdropsFromExtraFanart(string path, List<LocalImageInfo> images)
{
var imageFiles = Directory.EnumerateFiles(path, "*", SearchOption.TopDirectoryOnly)
.Where(i =>
{
var extension = Path.GetExtension(i);
if (string.IsNullOrEmpty(extension))
{
return false;
}
return BaseItem.SupportedImageExtensions.Contains(extension, StringComparer.OrdinalIgnoreCase);
});
images.AddRange(imageFiles.Select(i => new LocalImageInfo
{
Path = i,
Type = ImageType.Backdrop
}));
}
private void PopulateScreenshots(List<LocalImageInfo> images, Dictionary<string, string> files, string imagePrefix)
{
PopulateBackdrops(images, files, imagePrefix, "screenshot", "screenshot", ImageType.Screenshot);
}
private void PopulateBackdrops(List<LocalImageInfo> images, Dictionary<string, string> files, string imagePrefix, string firstFileName, string subsequentFileNamePrefix, ImageType type)
{
AddImage(files, images, imagePrefix + firstFileName, type);
var unfound = 0;
for (var i = 1; i <= 20; i++)
{
// Screenshot Image
var found = AddImage(files, images, imagePrefix + subsequentFileNamePrefix + i, type);
if (!found)
{
unfound++;
if (unfound >= 3)
{
break;
}
}
}
}
private readonly CultureInfo _usCulture = new CultureInfo("en-US");
private void PopulateSeasonImagesFromSeriesFolder(Season season, List<LocalImageInfo> images)
{
var seasonNumber = season.IndexNumber;
var series = season.Series;
if (!seasonNumber.HasValue || series.LocationType != LocationType.FileSystem)
{
return;
}
var files = GetFileDictionary(GetFiles(series, false));
// Try using the season name
var prefix = season.Name.ToLower().Replace(" ", string.Empty);
var filenamePrefixes = new List<string> { prefix };
var seasonMarker = seasonNumber.Value == 0
? "-specials"
: seasonNumber.Value.ToString("00", _usCulture);
// Get this one directly from the file system since we have to go up a level
if (!string.Equals(prefix, seasonMarker, StringComparison.OrdinalIgnoreCase))
{
filenamePrefixes.Add("season" + seasonMarker);
}
foreach (var filename in filenamePrefixes)
{
AddImage(files, images, filename + "-poster", ImageType.Primary);
AddImage(files, images, filename + "-fanart", ImageType.Backdrop);
AddImage(files, images, filename + "-banner", ImageType.Banner);
AddImage(files, images, filename + "-landscape", ImageType.Thumb);
}
}
private Dictionary<string, string> GetFileDictionary(IEnumerable<string> paths)
{
var dict = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
foreach (var path in paths)
{
var filename = Path.GetFileName(path);
if (!string.IsNullOrEmpty(filename))
{
dict[filename] = path;
}
}
return dict;
}
private bool AddImage(Dictionary<string, string> dict, List<LocalImageInfo> images, string name, ImageType type)
{
var image = GetImage(dict, name);
if (image != null)
{
images.Add(new LocalImageInfo
{
Path = image,
Type = type
});
return true;
}
return false;
}
private string GetImage(Dictionary<string, string> dict, string name)
{
return BaseItem.SupportedImageExtensions
.Select(i =>
{
var filename = name + i;
string path;
return dict.TryGetValue(filename, out path) ? path : null;
})
.FirstOrDefault(i => i != null);
}
}
}

View File

@ -0,0 +1,34 @@
using MediaBrowser.Common.IO;
using MediaBrowser.Controller.Providers;
using System;
using System.IO;
using System.Threading;
namespace MediaBrowser.Providers
{
public abstract class BaseXmlProvider: IHasChangeMonitor
{
protected static readonly SemaphoreSlim XmlParsingResourcePool = new SemaphoreSlim(4, 4);
protected IFileSystem FileSystem;
protected BaseXmlProvider(IFileSystem fileSystem)
{
FileSystem = fileSystem;
}
protected abstract string GetXmlPath(string path);
public bool HasChanged(IHasMetadata item, DateTime date)
{
var path = GetXmlPath(item.Path);
return FileSystem.GetLastWriteTimeUtc(path) > date;
}
public bool HasLocalMetadata(IHasMetadata item)
{
return File.Exists(GetXmlPath(item.Path));
}
}
}

View File

@ -1,5 +1,4 @@
using System.Collections.Generic; using MediaBrowser.Common.IO;
using MediaBrowser.Common.IO;
using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Library;
@ -7,6 +6,7 @@ using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Entities; using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Logging; using MediaBrowser.Model.Logging;
using System; using System;
using System.Collections.Generic;
using System.IO; using System.IO;
using System.Linq; using System.Linq;

View File

@ -5,15 +5,17 @@ using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Providers; using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Entities; using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Providers; using MediaBrowser.Model.Providers;
using MediaBrowser.Providers.Genres;
using MediaBrowser.Providers.ImagesByName;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace MediaBrowser.Providers.ImagesByName namespace MediaBrowser.Providers.GameGenres
{ {
public class GameGenresManualImageProvider : IImageProvider public class GameGenreImageProvider : IRemoteImageProvider
{ {
private readonly IServerConfigurationManager _config; private readonly IServerConfigurationManager _config;
private readonly IHttpClient _httpClient; private readonly IHttpClient _httpClient;
@ -21,7 +23,7 @@ namespace MediaBrowser.Providers.ImagesByName
private readonly SemaphoreSlim _listResourcePool = new SemaphoreSlim(1, 1); private readonly SemaphoreSlim _listResourcePool = new SemaphoreSlim(1, 1);
public GameGenresManualImageProvider(IServerConfigurationManager config, IHttpClient httpClient, IFileSystem fileSystem) public GameGenreImageProvider(IServerConfigurationManager config, IHttpClient httpClient, IFileSystem fileSystem)
{ {
_config = config; _config = config;
_httpClient = httpClient; _httpClient = httpClient;
@ -43,6 +45,15 @@ namespace MediaBrowser.Providers.ImagesByName
return item is GameGenre; return item is GameGenre;
} }
public IEnumerable<ImageType> GetSupportedImages(IHasImages item)
{
return new List<ImageType>
{
ImageType.Primary,
ImageType.Thumb
};
}
public Task<IEnumerable<RemoteImageInfo>> GetImages(IHasImages item, ImageType imageType, CancellationToken cancellationToken) public Task<IEnumerable<RemoteImageInfo>> GetImages(IHasImages item, ImageType imageType, CancellationToken cancellationToken)
{ {
return GetImages(item, imageType == ImageType.Primary, imageType == ImageType.Thumb, cancellationToken); return GetImages(item, imageType == ImageType.Primary, imageType == ImageType.Thumb, cancellationToken);
@ -120,9 +131,19 @@ namespace MediaBrowser.Providers.ImagesByName
return ImageUtils.EnsureList(url, file, _httpClient, _fileSystem, _listResourcePool, cancellationToken); return ImageUtils.EnsureList(url, file, _httpClient, _fileSystem, _listResourcePool, cancellationToken);
} }
public int Priority public int Order
{ {
get { return 0; } get { return 0; }
} }
public Task<HttpResponseInfo> GetImageResponse(string url, CancellationToken cancellationToken)
{
return _httpClient.GetResponse(new HttpRequestOptions
{
CancellationToken = cancellationToken,
Url = url,
ResourcePool = GenreImageProvider.ImageDownloadResourcePool
});
}
} }
} }

View File

@ -0,0 +1,42 @@
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Logging;
using MediaBrowser.Providers.Manager;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
namespace MediaBrowser.Providers.GameGenres
{
public class GameGenreMetadataService : MetadataService<GameGenre>
{
private readonly ILibraryManager _libraryManager;
public GameGenreMetadataService(IServerConfigurationManager serverConfigurationManager, ILogger logger, IProviderManager providerManager, IProviderRepository providerRepo, ILibraryManager libraryManager)
: base(serverConfigurationManager, logger, providerManager, providerRepo)
{
_libraryManager = libraryManager;
}
/// <summary>
/// Merges the specified source.
/// </summary>
/// <param name="source">The source.</param>
/// <param name="target">The target.</param>
/// <param name="lockedFields">The locked fields.</param>
/// <param name="replaceData">if set to <c>true</c> [replace data].</param>
/// <param name="mergeMetadataSettings">if set to <c>true</c> [merge metadata settings].</param>
protected override void MergeData(GameGenre source, GameGenre target, List<MetadataFields> lockedFields, bool replaceData, bool mergeMetadataSettings)
{
ProviderUtils.MergeBaseItemData(source, target, lockedFields, replaceData, mergeMetadataSettings);
}
protected override Task SaveItem(GameGenre item, ItemUpdateType reason, CancellationToken cancellationToken)
{
return _libraryManager.UpdateItem(item, reason, cancellationToken);
}
}
}

View File

@ -5,15 +5,16 @@ using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Providers; using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Entities; using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Providers; using MediaBrowser.Model.Providers;
using MediaBrowser.Providers.ImagesByName;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace MediaBrowser.Providers.ImagesByName namespace MediaBrowser.Providers.Genres
{ {
public class GenresManualImageProvider : IImageProvider public class GenreImageProvider : IRemoteImageProvider
{ {
private readonly IServerConfigurationManager _config; private readonly IServerConfigurationManager _config;
private readonly IHttpClient _httpClient; private readonly IHttpClient _httpClient;
@ -21,7 +22,9 @@ namespace MediaBrowser.Providers.ImagesByName
private readonly SemaphoreSlim _listResourcePool = new SemaphoreSlim(1, 1); private readonly SemaphoreSlim _listResourcePool = new SemaphoreSlim(1, 1);
public GenresManualImageProvider(IServerConfigurationManager config, IHttpClient httpClient, IFileSystem fileSystem) public static SemaphoreSlim ImageDownloadResourcePool = new SemaphoreSlim(5, 5);
public GenreImageProvider(IServerConfigurationManager config, IHttpClient httpClient, IFileSystem fileSystem)
{ {
_config = config; _config = config;
_httpClient = httpClient; _httpClient = httpClient;
@ -43,6 +46,15 @@ namespace MediaBrowser.Providers.ImagesByName
return item is Genre; return item is Genre;
} }
public IEnumerable<ImageType> GetSupportedImages(IHasImages item)
{
return new List<ImageType>
{
ImageType.Primary,
ImageType.Thumb
};
}
public Task<IEnumerable<RemoteImageInfo>> GetImages(IHasImages item, ImageType imageType, CancellationToken cancellationToken) public Task<IEnumerable<RemoteImageInfo>> GetImages(IHasImages item, ImageType imageType, CancellationToken cancellationToken)
{ {
return GetImages(item, imageType == ImageType.Primary, imageType == ImageType.Thumb, cancellationToken); return GetImages(item, imageType == ImageType.Primary, imageType == ImageType.Thumb, cancellationToken);
@ -120,9 +132,19 @@ namespace MediaBrowser.Providers.ImagesByName
return ImageUtils.EnsureList(url, file, _httpClient, _fileSystem, _listResourcePool, cancellationToken); return ImageUtils.EnsureList(url, file, _httpClient, _fileSystem, _listResourcePool, cancellationToken);
} }
public int Priority public int Order
{ {
get { return 0; } get { return 0; }
} }
public Task<HttpResponseInfo> GetImageResponse(string url, CancellationToken cancellationToken)
{
return _httpClient.GetResponse(new HttpRequestOptions
{
CancellationToken = cancellationToken,
Url = url,
ResourcePool = ImageDownloadResourcePool
});
}
} }
} }

View File

@ -0,0 +1,42 @@
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Logging;
using MediaBrowser.Providers.Manager;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
namespace MediaBrowser.Providers.Genres
{
public class GenreMetadataService : MetadataService<Genre>
{
private readonly ILibraryManager _libraryManager;
public GenreMetadataService(IServerConfigurationManager serverConfigurationManager, ILogger logger, IProviderManager providerManager, IProviderRepository providerRepo, ILibraryManager libraryManager)
: base(serverConfigurationManager, logger, providerManager, providerRepo)
{
_libraryManager = libraryManager;
}
/// <summary>
/// Merges the specified source.
/// </summary>
/// <param name="source">The source.</param>
/// <param name="target">The target.</param>
/// <param name="lockedFields">The locked fields.</param>
/// <param name="replaceData">if set to <c>true</c> [replace data].</param>
/// <param name="mergeMetadataSettings">if set to <c>true</c> [merge metadata settings].</param>
protected override void MergeData(Genre source, Genre target, List<MetadataFields> lockedFields, bool replaceData, bool mergeMetadataSettings)
{
ProviderUtils.MergeBaseItemData(source, target, lockedFields, replaceData, mergeMetadataSettings);
}
protected override Task SaveItem(Genre item, ItemUpdateType reason, CancellationToken cancellationToken)
{
return _libraryManager.UpdateItem(item, reason, cancellationToken);
}
}
}

View File

@ -145,17 +145,6 @@ namespace MediaBrowser.Providers
cancellationToken.ThrowIfCancellationRequested(); cancellationToken.ThrowIfCancellationRequested();
// Make sure current backdrop paths still exist
item.ValidateBackdrops();
var hasScreenshots = item as IHasScreenshots;
if (hasScreenshots != null)
{
hasScreenshots.ValidateScreenshots();
}
cancellationToken.ThrowIfCancellationRequested();
var args = GetResolveArgsContainingImages(item); var args = GetResolveArgsContainingImages(item);
PopulateBaseItemImages(item, args); PopulateBaseItemImages(item, args);

View File

@ -1,160 +0,0 @@
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Logging;
using MediaBrowser.Model.Net;
using MediaBrowser.Model.Providers;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Threading;
using System.Threading.Tasks;
namespace MediaBrowser.Providers.ImagesByName
{
public class GameGenreImageProvider : BaseMetadataProvider
{
private readonly IProviderManager _providerManager;
private readonly SemaphoreSlim _resourcePool = new SemaphoreSlim(5, 5);
public GameGenreImageProvider(ILogManager logManager, IServerConfigurationManager configurationManager, IProviderManager providerManager)
: base(logManager, configurationManager)
{
_providerManager = providerManager;
}
public override bool Supports(BaseItem item)
{
return item is GameGenre;
}
public override bool RequiresInternet
{
get
{
return true;
}
}
public override ItemUpdateType ItemUpdateType
{
get
{
return ItemUpdateType.ImageUpdate;
}
}
protected override bool NeedsRefreshInternal(BaseItem item, BaseProviderInfo providerInfo)
{
if (item.HasImage(ImageType.Primary) && item.HasImage(ImageType.Thumb))
{
return false;
}
// Try again periodically in case new images were added
if ((DateTime.UtcNow - providerInfo.LastRefreshed).TotalDays > 7)
{
return true;
}
return base.NeedsRefreshInternal(item, providerInfo);
}
protected override bool RefreshOnVersionChange
{
get
{
return true;
}
}
protected override string ProviderVersion
{
get
{
return "8";
}
}
public override async Task<bool> FetchAsync(BaseItem item, bool force, BaseProviderInfo providerInfo, CancellationToken cancellationToken)
{
if (item.HasImage(ImageType.Primary) && item.HasImage(ImageType.Thumb))
{
SetLastRefreshed(item, DateTime.UtcNow, providerInfo);
return true;
}
var images = await _providerManager.GetAvailableRemoteImages(item, cancellationToken, GameGenresManualImageProvider.ProviderName).ConfigureAwait(false);
await DownloadImages(item, images.ToList(), cancellationToken).ConfigureAwait(false);
SetLastRefreshed(item, DateTime.UtcNow, providerInfo);
return true;
}
private async Task DownloadImages(BaseItem item, List<RemoteImageInfo> images, CancellationToken cancellationToken)
{
if (!item.LockedFields.Contains(MetadataFields.Images))
{
cancellationToken.ThrowIfCancellationRequested();
if (!item.HasImage(ImageType.Primary))
{
await SaveImage(item, images, ImageType.Primary, cancellationToken).ConfigureAwait(false);
}
cancellationToken.ThrowIfCancellationRequested();
if (!item.HasImage(ImageType.Thumb))
{
await SaveImage(item, images, ImageType.Thumb, cancellationToken).ConfigureAwait(false);
}
}
if (!item.LockedFields.Contains(MetadataFields.Backdrops))
{
cancellationToken.ThrowIfCancellationRequested();
if (item.BackdropImagePaths.Count == 0)
{
foreach (var image in images.Where(i => i.Type == ImageType.Backdrop))
{
await _providerManager.SaveImage(item, image.Url, _resourcePool, ImageType.Backdrop, null, cancellationToken)
.ConfigureAwait(false);
break;
}
}
}
}
private async Task SaveImage(BaseItem item, IEnumerable<RemoteImageInfo> images, ImageType type, CancellationToken cancellationToken)
{
foreach (var image in images.Where(i => i.Type == type))
{
try
{
await _providerManager.SaveImage(item, image.Url, _resourcePool, type, null, cancellationToken).ConfigureAwait(false);
break;
}
catch (HttpException ex)
{
// Sometimes fanart has bad url's in their xml
if (ex.StatusCode.HasValue && ex.StatusCode.Value == HttpStatusCode.NotFound)
{
continue;
}
break;
}
}
}
public override MetadataProviderPriority Priority
{
get { return MetadataProviderPriority.Third; }
}
}
}

View File

@ -1,160 +0,0 @@
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Logging;
using MediaBrowser.Model.Net;
using MediaBrowser.Model.Providers;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Threading;
using System.Threading.Tasks;
namespace MediaBrowser.Providers.ImagesByName
{
public class GenreImageProvider : BaseMetadataProvider
{
private readonly IProviderManager _providerManager;
private readonly SemaphoreSlim _resourcePool = new SemaphoreSlim(5, 5);
public GenreImageProvider(ILogManager logManager, IServerConfigurationManager configurationManager, IProviderManager providerManager)
: base(logManager, configurationManager)
{
_providerManager = providerManager;
}
public override bool Supports(BaseItem item)
{
return item is Genre;
}
public override bool RequiresInternet
{
get
{
return true;
}
}
public override ItemUpdateType ItemUpdateType
{
get
{
return ItemUpdateType.ImageUpdate;
}
}
protected override bool NeedsRefreshInternal(BaseItem item, BaseProviderInfo providerInfo)
{
if (item.HasImage(ImageType.Primary) && item.HasImage(ImageType.Thumb))
{
return false;
}
// Try again periodically in case new images were added
if ((DateTime.UtcNow - providerInfo.LastRefreshed).TotalDays > 7)
{
return true;
}
return base.NeedsRefreshInternal(item, providerInfo);
}
protected override bool RefreshOnVersionChange
{
get
{
return true;
}
}
protected override string ProviderVersion
{
get
{
return "8";
}
}
public override async Task<bool> FetchAsync(BaseItem item, bool force, BaseProviderInfo providerInfo, CancellationToken cancellationToken)
{
if (item.HasImage(ImageType.Primary) && item.HasImage(ImageType.Thumb))
{
SetLastRefreshed(item, DateTime.UtcNow, providerInfo);
return true;
}
var images = await _providerManager.GetAvailableRemoteImages(item, cancellationToken, GenresManualImageProvider.ProviderName).ConfigureAwait(false);
await DownloadImages(item, images.ToList(), cancellationToken).ConfigureAwait(false);
SetLastRefreshed(item, DateTime.UtcNow, providerInfo);
return true;
}
private async Task DownloadImages(BaseItem item, List<RemoteImageInfo> images, CancellationToken cancellationToken)
{
if (!item.LockedFields.Contains(MetadataFields.Images))
{
cancellationToken.ThrowIfCancellationRequested();
if (!item.HasImage(ImageType.Primary))
{
await SaveImage(item, images, ImageType.Primary, cancellationToken).ConfigureAwait(false);
}
cancellationToken.ThrowIfCancellationRequested();
if (!item.HasImage(ImageType.Thumb))
{
await SaveImage(item, images, ImageType.Thumb, cancellationToken).ConfigureAwait(false);
}
}
if (!item.LockedFields.Contains(MetadataFields.Backdrops))
{
cancellationToken.ThrowIfCancellationRequested();
if (item.BackdropImagePaths.Count == 0)
{
foreach (var image in images.Where(i => i.Type == ImageType.Backdrop))
{
await _providerManager.SaveImage(item, image.Url, _resourcePool, ImageType.Backdrop, null, cancellationToken)
.ConfigureAwait(false);
break;
}
}
}
}
private async Task SaveImage(BaseItem item, IEnumerable<RemoteImageInfo> images, ImageType type, CancellationToken cancellationToken)
{
foreach (var image in images.Where(i => i.Type == type))
{
try
{
await _providerManager.SaveImage(item, image.Url, _resourcePool, type, null, cancellationToken).ConfigureAwait(false);
break;
}
catch (HttpException ex)
{
// Sometimes fanart has bad url's in their xml
if (ex.StatusCode.HasValue && ex.StatusCode.Value == HttpStatusCode.NotFound)
{
continue;
}
break;
}
}
}
public override MetadataProviderPriority Priority
{
get { return MetadataProviderPriority.Third; }
}
}
}

View File

@ -1,161 +0,0 @@
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.Audio;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Logging;
using MediaBrowser.Model.Net;
using MediaBrowser.Model.Providers;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Threading;
using System.Threading.Tasks;
namespace MediaBrowser.Providers.ImagesByName
{
public class MusicGenreImageProvider : BaseMetadataProvider
{
private readonly IProviderManager _providerManager;
private readonly SemaphoreSlim _resourcePool = new SemaphoreSlim(5, 5);
public MusicGenreImageProvider(ILogManager logManager, IServerConfigurationManager configurationManager, IProviderManager providerManager)
: base(logManager, configurationManager)
{
_providerManager = providerManager;
}
public override bool Supports(BaseItem item)
{
return item is MusicGenre;
}
public override bool RequiresInternet
{
get
{
return true;
}
}
public override ItemUpdateType ItemUpdateType
{
get
{
return ItemUpdateType.ImageUpdate;
}
}
protected override bool NeedsRefreshInternal(BaseItem item, BaseProviderInfo providerInfo)
{
if (item.HasImage(ImageType.Primary) && item.HasImage(ImageType.Thumb))
{
return false;
}
// Try again periodically in case new images were added
if ((DateTime.UtcNow - providerInfo.LastRefreshed).TotalDays > 7)
{
return true;
}
return base.NeedsRefreshInternal(item, providerInfo);
}
protected override bool RefreshOnVersionChange
{
get
{
return true;
}
}
protected override string ProviderVersion
{
get
{
return "8";
}
}
public override async Task<bool> FetchAsync(BaseItem item, bool force, BaseProviderInfo providerInfo, CancellationToken cancellationToken)
{
if (item.HasImage(ImageType.Primary) && item.HasImage(ImageType.Thumb))
{
SetLastRefreshed(item, DateTime.UtcNow, providerInfo);
return true;
}
var images = await _providerManager.GetAvailableRemoteImages(item, cancellationToken, MusicGenresManualImageProvider.ProviderName).ConfigureAwait(false);
await DownloadImages(item, images.ToList(), cancellationToken).ConfigureAwait(false);
SetLastRefreshed(item, DateTime.UtcNow, providerInfo);
return true;
}
private async Task DownloadImages(BaseItem item, List<RemoteImageInfo> images, CancellationToken cancellationToken)
{
if (!item.LockedFields.Contains(MetadataFields.Images))
{
cancellationToken.ThrowIfCancellationRequested();
if (!item.HasImage(ImageType.Primary))
{
await SaveImage(item, images, ImageType.Primary, cancellationToken).ConfigureAwait(false);
}
cancellationToken.ThrowIfCancellationRequested();
if (!item.HasImage(ImageType.Thumb))
{
await SaveImage(item, images, ImageType.Thumb, cancellationToken).ConfigureAwait(false);
}
}
if (!item.LockedFields.Contains(MetadataFields.Backdrops))
{
cancellationToken.ThrowIfCancellationRequested();
if (item.BackdropImagePaths.Count == 0)
{
foreach (var image in images.Where(i => i.Type == ImageType.Backdrop))
{
await _providerManager.SaveImage(item, image.Url, _resourcePool, ImageType.Backdrop, null, cancellationToken)
.ConfigureAwait(false);
break;
}
}
}
}
private async Task SaveImage(BaseItem item, IEnumerable<RemoteImageInfo> images, ImageType type, CancellationToken cancellationToken)
{
foreach (var image in images.Where(i => i.Type == type))
{
try
{
await _providerManager.SaveImage(item, image.Url, _resourcePool, type, null, cancellationToken).ConfigureAwait(false);
break;
}
catch (HttpException ex)
{
// Sometimes fanart has bad url's in their xml
if (ex.StatusCode.HasValue && ex.StatusCode.Value == HttpStatusCode.NotFound)
{
continue;
}
break;
}
}
}
public override MetadataProviderPriority Priority
{
get { return MetadataProviderPriority.Third; }
}
}
}

View File

@ -1,160 +0,0 @@
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Logging;
using MediaBrowser.Model.Net;
using MediaBrowser.Model.Providers;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Threading;
using System.Threading.Tasks;
namespace MediaBrowser.Providers.ImagesByName
{
public class StudioImageProvider : BaseMetadataProvider
{
private readonly IProviderManager _providerManager;
private readonly SemaphoreSlim _resourcePool = new SemaphoreSlim(5, 5);
public StudioImageProvider(ILogManager logManager, IServerConfigurationManager configurationManager, IProviderManager providerManager)
: base(logManager, configurationManager)
{
_providerManager = providerManager;
}
public override bool Supports(BaseItem item)
{
return item is Studio;
}
public override bool RequiresInternet
{
get
{
return true;
}
}
public override ItemUpdateType ItemUpdateType
{
get
{
return ItemUpdateType.ImageUpdate;
}
}
protected override bool NeedsRefreshInternal(BaseItem item, BaseProviderInfo providerInfo)
{
if (item.HasImage(ImageType.Primary) && item.HasImage(ImageType.Thumb))
{
return false;
}
// Try again periodically in case new images were added
if ((DateTime.UtcNow - providerInfo.LastRefreshed).TotalDays > 7)
{
return true;
}
return base.NeedsRefreshInternal(item, providerInfo);
}
protected override bool RefreshOnVersionChange
{
get
{
return true;
}
}
protected override string ProviderVersion
{
get
{
return "6";
}
}
public override async Task<bool> FetchAsync(BaseItem item, bool force, BaseProviderInfo providerInfo, CancellationToken cancellationToken)
{
if (item.HasImage(ImageType.Primary) && item.HasImage(ImageType.Thumb))
{
SetLastRefreshed(item, DateTime.UtcNow, providerInfo);
return true;
}
var images = await _providerManager.GetAvailableRemoteImages(item, cancellationToken, StudiosManualImageProvider.ProviderName).ConfigureAwait(false);
await DownloadImages(item, images.ToList(), cancellationToken).ConfigureAwait(false);
SetLastRefreshed(item, DateTime.UtcNow, providerInfo);
return true;
}
private async Task DownloadImages(BaseItem item, List<RemoteImageInfo> images, CancellationToken cancellationToken)
{
if (!item.LockedFields.Contains(MetadataFields.Images))
{
cancellationToken.ThrowIfCancellationRequested();
if (!item.HasImage(ImageType.Primary))
{
await SaveImage(item, images, ImageType.Primary, cancellationToken).ConfigureAwait(false);
}
cancellationToken.ThrowIfCancellationRequested();
if (!item.HasImage(ImageType.Thumb))
{
await SaveImage(item, images, ImageType.Thumb, cancellationToken).ConfigureAwait(false);
}
}
if (!item.LockedFields.Contains(MetadataFields.Backdrops))
{
cancellationToken.ThrowIfCancellationRequested();
if (item.BackdropImagePaths.Count == 0)
{
foreach (var image in images.Where(i => i.Type == ImageType.Backdrop))
{
await _providerManager.SaveImage(item, image.Url, _resourcePool, ImageType.Backdrop, null, cancellationToken)
.ConfigureAwait(false);
break;
}
}
}
}
private async Task SaveImage(BaseItem item, IEnumerable<RemoteImageInfo> images, ImageType type, CancellationToken cancellationToken)
{
foreach (var image in images.Where(i => i.Type == type))
{
try
{
await _providerManager.SaveImage(item, image.Url, _resourcePool, type, null, cancellationToken).ConfigureAwait(false);
break;
}
catch (HttpException ex)
{
// Sometimes fanart has bad url's in their xml
if (ex.StatusCode.HasValue && ex.StatusCode.Value == HttpStatusCode.NotFound)
{
continue;
}
break;
}
}
}
public override MetadataProviderPriority Priority
{
get { return MetadataProviderPriority.Third; }
}
}
}

View File

@ -0,0 +1,37 @@
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.LiveTv;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Logging;
using MediaBrowser.Providers.Manager;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
namespace MediaBrowser.Providers.LiveTv
{
public class ChannelMetadataService : MetadataService<LiveTvChannel>
{
private readonly ILibraryManager _libraryManager;
public ChannelMetadataService(IServerConfigurationManager serverConfigurationManager, ILogger logger, IProviderManager providerManager, IProviderRepository providerRepo, ILibraryManager libraryManager)
: base(serverConfigurationManager, logger, providerManager, providerRepo)
{
_libraryManager = libraryManager;
}
/// <summary>
/// Merges the specified source.
/// </summary>
protected override void MergeData(LiveTvChannel source, LiveTvChannel target, List<MetadataFields> lockedFields, bool replaceData, bool mergeMetadataSettings)
{
ProviderUtils.MergeBaseItemData(source, target, lockedFields, replaceData, mergeMetadataSettings);
}
protected override Task SaveItem(LiveTvChannel item, ItemUpdateType reason, CancellationToken cancellationToken)
{
return _libraryManager.UpdateItem(item, reason, cancellationToken);
}
}
}

View File

@ -1,91 +0,0 @@
using MediaBrowser.Common.IO;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.LiveTv;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Logging;
using System;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
namespace MediaBrowser.Providers.LiveTv
{
class ChannelProviderFromXml : BaseMetadataProvider
{
private readonly IFileSystem _fileSystem;
public ChannelProviderFromXml(ILogManager logManager, IServerConfigurationManager configurationManager, IFileSystem fileSystem)
: base(logManager, configurationManager)
{
_fileSystem = fileSystem;
}
/// <summary>
/// Supportses the specified item.
/// </summary>
/// <param name="item">The item.</param>
/// <returns><c>true</c> if XXXX, <c>false</c> otherwise</returns>
public override bool Supports(BaseItem item)
{
return item is LiveTvChannel;
}
/// <summary>
/// Gets the priority.
/// </summary>
/// <value>The priority.</value>
public override MetadataProviderPriority Priority
{
get { return MetadataProviderPriority.Second; }
}
private const string XmlFileName = "channel.xml";
protected override bool NeedsRefreshBasedOnCompareDate(BaseItem item, BaseProviderInfo providerInfo)
{
var xml = item.ResolveArgs.GetMetaFileByPath(Path.Combine(item.MetaLocation, XmlFileName));
if (xml == null)
{
return false;
}
return _fileSystem.GetLastWriteTimeUtc(xml) > item.DateLastSaved;
}
/// <summary>
/// Fetches metadata and returns true or false indicating if any work that requires persistence was done
/// </summary>
/// <param name="item">The item.</param>
/// <param name="force">if set to <c>true</c> [force].</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Task{System.Boolean}.</returns>
public override async Task<bool> FetchAsync(BaseItem item, bool force, BaseProviderInfo providerInfo, CancellationToken cancellationToken)
{
cancellationToken.ThrowIfCancellationRequested();
var metadataFile = item.ResolveArgs.GetMetaFileByPath(Path.Combine(item.MetaLocation, XmlFileName));
if (metadataFile != null)
{
var path = metadataFile.FullName;
await XmlParsingResourcePool.WaitAsync(cancellationToken).ConfigureAwait(false);
try
{
new BaseItemXmlParser<LiveTvChannel>(Logger).Fetch((LiveTvChannel)item, path, cancellationToken);
}
finally
{
XmlParsingResourcePool.Release();
}
SetLastRefreshed(item, DateTime.UtcNow, providerInfo);
return true;
}
return false;
}
}
}

View File

@ -0,0 +1,59 @@
using MediaBrowser.Common.IO;
using MediaBrowser.Controller.LiveTv;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Logging;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
namespace MediaBrowser.Providers.LiveTv
{
public class ChannelXmlProvider : BaseXmlProvider, ILocalMetadataProvider<LiveTvChannel>
{
private readonly ILogger _logger;
public ChannelXmlProvider(IFileSystem fileSystem, ILogger logger)
: base(fileSystem)
{
_logger = logger;
}
public async Task<MetadataResult<LiveTvChannel>> GetMetadata(string path, CancellationToken cancellationToken)
{
path = GetXmlPath(path);
var result = new MetadataResult<LiveTvChannel>();
await XmlParsingResourcePool.WaitAsync(cancellationToken).ConfigureAwait(false);
try
{
var item = new LiveTvChannel();
new BaseItemXmlParser<LiveTvChannel>(_logger).Fetch(item, path, cancellationToken);
result.HasMetadata = true;
result.Item = item;
}
catch (FileNotFoundException)
{
result.HasMetadata = false;
}
finally
{
XmlParsingResourcePool.Release();
}
return result;
}
public string Name
{
get { return "Media Browser Xml"; }
}
protected override string GetXmlPath(string path)
{
return Path.Combine(path, "channel.xml");
}
}
}

View File

@ -0,0 +1,41 @@
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.LiveTv;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Logging;
using MediaBrowser.Providers.Manager;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
namespace MediaBrowser.Providers.LiveTv
{
public class ProgramMetadataService : MetadataService<LiveTvProgram>
{
private readonly ILibraryManager _libraryManager;
public ProgramMetadataService(IServerConfigurationManager serverConfigurationManager, ILogger logger, IProviderManager providerManager, IProviderRepository providerRepo, ILibraryManager libraryManager)
: base(serverConfigurationManager, logger, providerManager, providerRepo)
{
_libraryManager = libraryManager;
}
/// <summary>
/// Merges the specified source.
/// </summary>
/// <param name="source">The source.</param>
/// <param name="target">The target.</param>
/// <param name="lockedFields">The locked fields.</param>
/// <param name="replaceData">if set to <c>true</c> [replace data].</param>
protected override void MergeData(LiveTvProgram source, LiveTvProgram target, List<MetadataFields> lockedFields, bool replaceData, bool mergeMetadataSettings)
{
ProviderUtils.MergeBaseItemData(source, target, lockedFields, replaceData, mergeMetadataSettings);
}
protected override Task SaveItem(LiveTvProgram item, ItemUpdateType reason, CancellationToken cancellationToken)
{
return _libraryManager.UpdateItem(item, reason, cancellationToken);
}
}
}

View File

@ -3,7 +3,7 @@ using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.Audio; using MediaBrowser.Controller.Entities.Audio;
using MediaBrowser.Controller.Entities.TV; using MediaBrowser.Controller.Entities.TV;
using MediaBrowser.Controller.IO; using MediaBrowser.Controller.Library;
using MediaBrowser.Model.Configuration; using MediaBrowser.Model.Configuration;
using MediaBrowser.Model.Entities; using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Logging; using MediaBrowser.Model.Logging;
@ -15,7 +15,7 @@ using System.Linq;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace MediaBrowser.Server.Implementations.Providers namespace MediaBrowser.Providers.Manager
{ {
/// <summary> /// <summary>
/// Class ImageSaver /// Class ImageSaver
@ -36,7 +36,7 @@ namespace MediaBrowser.Server.Implementations.Providers
/// <summary> /// <summary>
/// The _directory watchers /// The _directory watchers
/// </summary> /// </summary>
private readonly IDirectoryWatchers _directoryWatchers; private readonly ILibraryMonitor _libraryMonitor;
private readonly IFileSystem _fileSystem; private readonly IFileSystem _fileSystem;
private readonly ILogger _logger; private readonly ILogger _logger;
@ -44,11 +44,11 @@ namespace MediaBrowser.Server.Implementations.Providers
/// Initializes a new instance of the <see cref="ImageSaver"/> class. /// Initializes a new instance of the <see cref="ImageSaver"/> class.
/// </summary> /// </summary>
/// <param name="config">The config.</param> /// <param name="config">The config.</param>
/// <param name="directoryWatchers">The directory watchers.</param> /// <param name="libraryMonitor">The directory watchers.</param>
public ImageSaver(IServerConfigurationManager config, IDirectoryWatchers directoryWatchers, IFileSystem fileSystem, ILogger logger) public ImageSaver(IServerConfigurationManager config, ILibraryMonitor libraryMonitor, IFileSystem fileSystem, ILogger logger)
{ {
_config = config; _config = config;
_directoryWatchers = directoryWatchers; _libraryMonitor = libraryMonitor;
_fileSystem = fileSystem; _fileSystem = fileSystem;
_logger = logger; _logger = logger;
_remoteImageCache = new FileSystemRepository(config.ApplicationPaths.DownloadedImagesDataPath); _remoteImageCache = new FileSystemRepository(config.ApplicationPaths.DownloadedImagesDataPath);
@ -160,7 +160,7 @@ namespace MediaBrowser.Server.Implementations.Providers
// Delete the current path // Delete the current path
if (!string.IsNullOrEmpty(currentPath) && !paths.Contains(currentPath, StringComparer.OrdinalIgnoreCase)) if (!string.IsNullOrEmpty(currentPath) && !paths.Contains(currentPath, StringComparer.OrdinalIgnoreCase))
{ {
_directoryWatchers.TemporarilyIgnore(currentPath); _libraryMonitor.ReportFileSystemChangeBeginning(currentPath);
try try
{ {
@ -179,7 +179,7 @@ namespace MediaBrowser.Server.Implementations.Providers
} }
finally finally
{ {
_directoryWatchers.RemoveTempIgnore(currentPath); _libraryMonitor.ReportFileSystemChangeComplete(currentPath, false);
} }
} }
} }
@ -197,8 +197,8 @@ namespace MediaBrowser.Server.Implementations.Providers
var parentFolder = Path.GetDirectoryName(path); var parentFolder = Path.GetDirectoryName(path);
_directoryWatchers.TemporarilyIgnore(path); _libraryMonitor.ReportFileSystemChangeBeginning(path);
_directoryWatchers.TemporarilyIgnore(parentFolder); _libraryMonitor.ReportFileSystemChangeBeginning(parentFolder);
try try
{ {
@ -223,8 +223,8 @@ namespace MediaBrowser.Server.Implementations.Providers
} }
finally finally
{ {
_directoryWatchers.RemoveTempIgnore(path); _libraryMonitor.ReportFileSystemChangeComplete(path, false);
_directoryWatchers.RemoveTempIgnore(parentFolder); _libraryMonitor.ReportFileSystemChangeComplete(parentFolder, false);
} }
} }
@ -348,6 +348,9 @@ namespace MediaBrowser.Server.Implementations.Providers
case ImageType.Art: case ImageType.Art:
filename = "clearart"; filename = "clearart";
break; break;
case ImageType.BoxRear:
filename = "back";
break;
case ImageType.Disc: case ImageType.Disc:
filename = item is MusicAlbum ? "cdart" : "disc"; filename = item is MusicAlbum ? "cdart" : "disc";
break; break;

View File

@ -0,0 +1,435 @@
using MediaBrowser.Common.Extensions;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Logging;
using MediaBrowser.Model.Net;
using MediaBrowser.Model.Providers;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Threading;
using System.Threading.Tasks;
namespace MediaBrowser.Providers.Manager
{
public class ItemImageProvider
{
private readonly ILogger _logger;
private readonly IProviderManager _providerManager;
private readonly IServerConfigurationManager _config;
public ItemImageProvider(ILogger logger, IProviderManager providerManager, IServerConfigurationManager config)
{
_logger = logger;
_providerManager = providerManager;
_config = config;
}
public bool ValidateImages(IHasImages item, IEnumerable<IImageProvider> providers)
{
var hasChanges = item.ValidateImages();
foreach (var provider in providers.OfType<IImageFileProvider>())
{
var images = provider.GetImages(item);
if (MergeImages(item, images))
{
hasChanges = true;
}
}
return hasChanges;
}
public async Task<RefreshResult> RefreshImages(IHasImages item, IEnumerable<IImageProvider> imageProviders, ImageRefreshOptions options, CancellationToken cancellationToken)
{
var result = new RefreshResult { UpdateType = ItemUpdateType.Unspecified };
var providers = GetImageProviders(item, imageProviders).ToList();
var providerIds = new List<Guid>();
foreach (var provider in providers.OfType<IRemoteImageProvider>())
{
await RefreshFromProvider(item, provider, options, result, cancellationToken).ConfigureAwait(false);
providerIds.Add(provider.GetType().FullName.GetMD5());
}
foreach (var provider in providers.OfType<IDynamicImageProvider>())
{
await RefreshFromProvider(item, provider, result, cancellationToken).ConfigureAwait(false);
providerIds.Add(provider.GetType().FullName.GetMD5());
}
result.Providers = providerIds;
return result;
}
/// <summary>
/// Refreshes from provider.
/// </summary>
/// <param name="item">The item.</param>
/// <param name="provider">The provider.</param>
/// <param name="result">The result.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Task.</returns>
private async Task RefreshFromProvider(IHasImages item, IDynamicImageProvider provider, RefreshResult result, CancellationToken cancellationToken)
{
_logger.Debug("Running {0} for {1}", provider.GetType().Name, item.Path ?? item.Name);
try
{
var images = provider.GetSupportedImages(item);
foreach (var imageType in images)
{
if (!item.HasImage(imageType))
{
var response = await provider.GetImage(item, imageType, cancellationToken).ConfigureAwait(false);
if (response.HasImage)
{
var mimeType = "image/" + response.Format.ToString().ToLower();
await _providerManager.SaveImage((BaseItem)item, response.Stream, mimeType, imageType, null, Guid.NewGuid().ToString(), cancellationToken).ConfigureAwait(false);
result.UpdateType = result.UpdateType | ItemUpdateType.ImageUpdate;
}
}
}
}
catch (OperationCanceledException)
{
throw;
}
catch (Exception ex)
{
result.ErrorMessage = ex.Message;
result.Status = ProviderRefreshStatus.CompletedWithErrors;
_logger.ErrorException("Error in {0}", ex, provider.Name);
}
}
/// <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
};
/// <summary>
/// Determines if an item already contains the given images
/// </summary>
/// <param name="item"></param>
/// <param name="images"></param>
/// <returns></returns>
private bool ContainsImages(IHasImages item, List<ImageType> images)
{
if (_singularImages.Any(i => images.Contains(i) && !item.HasImage(i)))
{
return false;
}
if (images.Contains(ImageType.Backdrop) && item.BackdropImagePaths.Count < GetMaxBackdropCount(item))
{
return false;
}
if (images.Contains(ImageType.Screenshot))
{
var hasScreenshots = item as IHasScreenshots;
if (hasScreenshots != null)
{
if (hasScreenshots.ScreenshotImagePaths.Count < GetMaxBackdropCount(item))
{
return false;
}
}
}
return true;
}
/// <summary>
/// Refreshes from provider.
/// </summary>
/// <param name="item">The item.</param>
/// <param name="provider">The provider.</param>
/// <param name="options">The options.</param>
/// <param name="result">The result.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Task.</returns>
private async Task RefreshFromProvider(IHasImages item, IRemoteImageProvider provider, ImageRefreshOptions options, RefreshResult result, CancellationToken cancellationToken)
{
try
{
// TODO: Also factor in IsConfiguredToDownloadImage
if (ContainsImages(item, provider.GetSupportedImages(item).ToList()))
{
return;
}
_logger.Debug("Running {0} for {1}", provider.GetType().Name, item.Path ?? item.Name);
var images = await provider.GetAllImages(item, cancellationToken).ConfigureAwait(false);
var list = images.ToList();
foreach (var type in _singularImages)
{
if (IsConfiguredToDownloadImage(item, type) && !item.HasImage(type))
{
await DownloadImage(item, provider, result, list, type, cancellationToken).ConfigureAwait(false);
}
}
await DownloadBackdrops(item, provider, result, list, cancellationToken).ConfigureAwait(false);
var hasScreenshots = item as IHasScreenshots;
if (hasScreenshots != null)
{
await DownloadScreenshots(hasScreenshots, provider, result, list, cancellationToken).ConfigureAwait(false);
}
}
catch (OperationCanceledException)
{
throw;
}
catch (Exception ex)
{
result.ErrorMessage = ex.Message;
result.Status = ProviderRefreshStatus.CompletedWithErrors;
_logger.ErrorException("Error in {0}", ex, provider.Name);
}
}
/// <summary>
/// Gets the image providers.
/// </summary>
/// <param name="item">The item.</param>
/// <param name="imageProviders">The image providers.</param>
/// <returns>IEnumerable{IImageProvider}.</returns>
private IEnumerable<IImageProvider> GetImageProviders(IHasImages item, IEnumerable<IImageProvider> imageProviders)
{
var providers = imageProviders.Where(i =>
{
try
{
return i.Supports(item);
}
catch (Exception ex)
{
_logger.ErrorException("Error in ImageProvider.Supports", ex, i.Name);
return false;
}
});
if (!_config.Configuration.EnableInternetProviders)
{
providers = providers.Where(i => !(i is IRemoteImageProvider));
}
return providers.OrderBy(i => i.Order);
}
private bool MergeImages(IHasImages item, List<LocalImageInfo> images)
{
var changed = false;
foreach (var type in _singularImages)
{
var image = images.FirstOrDefault(i => i.Type == type);
if (image != null)
{
var oldPath = item.GetImagePath(type);
item.SetImagePath(type, image.Path);
if (!string.Equals(oldPath, image.Path, StringComparison.OrdinalIgnoreCase))
{
changed = true;
}
}
}
// The change reporting will only be accurate at the count level
// Improve this if/when needed
var backdrops = images.Where(i => i.Type == ImageType.Backdrop).ToList();
if (backdrops.Count > 0)
{
var oldCount = item.BackdropImagePaths.Count;
item.BackdropImagePaths = item.BackdropImagePaths
.Concat(backdrops.Select(i => i.Path))
.Distinct(StringComparer.OrdinalIgnoreCase)
.ToList();
if (oldCount != item.BackdropImagePaths.Count)
{
changed = true;
}
}
var hasScreenshots = item as IHasScreenshots;
if (hasScreenshots != null)
{
var screenshots = images.Where(i => i.Type == ImageType.Screenshot).ToList();
if (screenshots.Count > 0)
{
var oldCount = hasScreenshots.ScreenshotImagePaths.Count;
hasScreenshots.ScreenshotImagePaths = hasScreenshots.ScreenshotImagePaths
.Concat(screenshots.Select(i => i.Path))
.Distinct(StringComparer.OrdinalIgnoreCase)
.ToList();
if (oldCount != hasScreenshots.ScreenshotImagePaths.Count)
{
changed = true;
}
}
}
return changed;
}
private async Task DownloadImage(IHasImages item, IRemoteImageProvider provider, RefreshResult result, IEnumerable<RemoteImageInfo> images, ImageType type, CancellationToken cancellationToken)
{
foreach (var image in images.Where(i => i.Type == type))
{
var url = image.Url;
try
{
var response = await provider.GetImageResponse(url, cancellationToken).ConfigureAwait(false);
await _providerManager.SaveImage((BaseItem)item, response.Content, response.ContentType, type, null, url, cancellationToken).ConfigureAwait(false);
result.UpdateType = result.UpdateType | ItemUpdateType.ImageUpdate;
break;
}
catch (HttpException ex)
{
// Sometimes providers send back bad url's. Just move onto the next image
if (ex.StatusCode.HasValue && ex.StatusCode.Value == HttpStatusCode.NotFound)
{
continue;
}
break;
}
}
}
private async Task DownloadBackdrops(IHasImages item, IRemoteImageProvider provider, RefreshResult result, IEnumerable<RemoteImageInfo> images, CancellationToken cancellationToken)
{
const ImageType imageType = ImageType.Backdrop;
var maxCount = GetMaxBackdropCount(item);
foreach (var image in images.Where(i => i.Type == imageType))
{
if (item.BackdropImagePaths.Count >= maxCount)
{
break;
}
var url = image.Url;
if (item.ContainsImageWithSourceUrl(url))
{
continue;
}
try
{
var response = await provider.GetImageResponse(url, cancellationToken).ConfigureAwait(false);
await _providerManager.SaveImage((BaseItem)item, response.Content, response.ContentType, imageType, null, url, cancellationToken).ConfigureAwait(false);
result.UpdateType = result.UpdateType | ItemUpdateType.ImageUpdate;
break;
}
catch (HttpException ex)
{
// Sometimes providers send back bad url's. Just move onto the next image
if (ex.StatusCode.HasValue && ex.StatusCode.Value == HttpStatusCode.NotFound)
{
continue;
}
break;
}
}
}
private async Task DownloadScreenshots(IHasScreenshots item, IRemoteImageProvider provider, RefreshResult result, IEnumerable<RemoteImageInfo> images, CancellationToken cancellationToken)
{
const ImageType imageType = ImageType.Screenshot;
var maxCount = GetMaxScreenshotCount(item);
foreach (var image in images.Where(i => i.Type == imageType))
{
if (item.ScreenshotImagePaths.Count >= maxCount)
{
break;
}
var url = image.Url;
if (item.ContainsImageWithSourceUrl(url))
{
continue;
}
try
{
var response = await provider.GetImageResponse(url, cancellationToken).ConfigureAwait(false);
await _providerManager.SaveImage((BaseItem)item, response.Content, response.ContentType, imageType, null, url, cancellationToken).ConfigureAwait(false);
result.UpdateType = result.UpdateType | ItemUpdateType.ImageUpdate;
break;
}
catch (HttpException ex)
{
// Sometimes providers send back bad url's. Just move onto the next image
if (ex.StatusCode.HasValue && ex.StatusCode.Value == HttpStatusCode.NotFound)
{
continue;
}
break;
}
}
}
private bool IsConfiguredToDownloadImage(IHasImages item, ImageType type)
{
return true;
}
private int GetMaxBackdropCount(IHasImages item)
{
return 1;
}
private int GetMaxScreenshotCount(IHasScreenshots item)
{
return 1;
}
}
}

View File

@ -0,0 +1,358 @@
using MediaBrowser.Common.Extensions;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Logging;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
namespace MediaBrowser.Providers.Manager
{
public abstract class MetadataService<TItemType> : IMetadataService
where TItemType : IHasMetadata, new()
{
protected readonly IServerConfigurationManager ServerConfigurationManager;
protected readonly ILogger Logger;
protected readonly IProviderManager ProviderManager;
private readonly IProviderRepository _providerRepo;
private IMetadataProvider<TItemType>[] _providers = { };
private IImageProvider[] _imageProviders = { };
protected MetadataService(IServerConfigurationManager serverConfigurationManager, ILogger logger, IProviderManager providerManager, IProviderRepository providerRepo)
{
ServerConfigurationManager = serverConfigurationManager;
Logger = logger;
ProviderManager = providerManager;
_providerRepo = providerRepo;
}
/// <summary>
/// Adds the parts.
/// </summary>
/// <param name="providers">The providers.</param>
/// <param name="imageProviders">The image providers.</param>
public void AddParts(IEnumerable<IMetadataProvider> providers, IEnumerable<IImageProvider> imageProviders)
{
_providers = providers.OfType<IMetadataProvider<TItemType>>()
.ToArray();
_imageProviders = imageProviders.OrderBy(i => i.Order).ToArray();
}
/// <summary>
/// Saves the provider result.
/// </summary>
/// <param name="result">The result.</param>
/// <returns>Task.</returns>
protected Task SaveProviderResult(MetadataStatus result)
{
return _providerRepo.SaveMetadataStatus(result, CancellationToken.None);
}
/// <summary>
/// Gets the last result.
/// </summary>
/// <param name="itemId">The item identifier.</param>
/// <returns>ProviderResult.</returns>
protected MetadataStatus GetLastResult(Guid itemId)
{
return _providerRepo.GetMetadataStatus(itemId) ?? new MetadataStatus { ItemId = itemId };
}
public async Task RefreshMetadata(IHasMetadata item, MetadataRefreshOptions options, CancellationToken cancellationToken)
{
var itemOfType = (TItemType)item;
var updateType = ItemUpdateType.Unspecified;
var lastResult = GetLastResult(item.Id);
var refreshResult = lastResult;
refreshResult.LastErrorMessage = string.Empty;
refreshResult.LastStatus = ProviderRefreshStatus.Success;
var imageProviders = GetImageProviders(item).ToList();
var itemImageProvider = new ItemImageProvider(Logger, ProviderManager, ServerConfigurationManager);
var localImagesFailed = false;
// Start by validating images
try
{
// Always validate images and check for new locally stored ones.
if (itemImageProvider.ValidateImages(item, imageProviders))
{
updateType = updateType | ItemUpdateType.ImageUpdate;
}
}
catch (Exception ex)
{
localImagesFailed = true;
Logger.ErrorException("Error validating images for {0}", ex, item.Path ?? item.Name);
refreshResult.AddStatus(ProviderRefreshStatus.Failure, ex.Message);
}
// Next run metadata providers
if (options.MetadataRefreshMode != MetadataRefreshMode.None)
{
var providers = GetProviders(item, lastResult.DateLastMetadataRefresh.HasValue, options).ToList();
if (providers.Count > 0)
{
var result = await RefreshWithProviders(itemOfType, options, providers, cancellationToken).ConfigureAwait(false);
updateType = updateType | result.UpdateType;
refreshResult.AddStatus(result.Status, result.ErrorMessage);
refreshResult.SetDateLastMetadataRefresh(DateTime.UtcNow);
refreshResult.AddImageProvidersRefreshed(result.Providers);
}
}
// Next run remote image providers, but only if local image providers didn't throw an exception
if (!localImagesFailed)
{
if ((options.ImageRefreshMode == MetadataRefreshMode.EnsureMetadata && !lastResult.DateLastImagesRefresh.HasValue) ||
options.ImageRefreshMode == MetadataRefreshMode.FullRefresh)
{
var result = await itemImageProvider.RefreshImages(itemOfType, imageProviders, options, cancellationToken).ConfigureAwait(false);
updateType = updateType | result.UpdateType;
refreshResult.AddStatus(result.Status, result.ErrorMessage);
refreshResult.SetDateLastImagesRefresh(DateTime.UtcNow);
refreshResult.AddImageProvidersRefreshed(result.Providers);
}
}
var providersHadChanges = updateType > ItemUpdateType.Unspecified;
if (options.ForceSave || providersHadChanges)
{
if (string.IsNullOrEmpty(item.Name))
{
throw new InvalidOperationException("Item has no name");
}
// Save to database
await SaveItem(itemOfType, updateType, cancellationToken);
}
if (providersHadChanges || refreshResult.IsDirty)
{
await SaveProviderResult(refreshResult).ConfigureAwait(false);
}
}
/// <summary>
/// Gets the providers.
/// </summary>
/// <param name="item">The item.</param>
/// <param name="hasRefreshedMetadata">if set to <c>true</c> [has refreshed metadata].</param>
/// <param name="options">The options.</param>
/// <returns>IEnumerable{`0}.</returns>
protected virtual IEnumerable<IMetadataProvider> GetProviders(IHasMetadata item, bool hasRefreshedMetadata, MetadataRefreshOptions options)
{
// Get providers to refresh
var providers = _providers.Where(i => CanRefresh(i, item)).ToList();
// Run all if either of these flags are true
var runAllProviders = options.ReplaceAllMetadata || options.MetadataRefreshMode == MetadataRefreshMode.FullRefresh || !hasRefreshedMetadata;
if (!runAllProviders)
{
// Avoid implicitly captured closure
var currentItem = item;
var providersWithChanges = providers.OfType<IHasChangeMonitor>()
.Where(i => i.HasChanged(currentItem, currentItem.DateLastSaved))
.ToList();
// If local providers are the only ones with changes, then just run those
if (providersWithChanges.All(i => i is ILocalMetadataProvider))
{
providers = providers.Where(i => i is ILocalMetadataProvider).ToList();
}
}
return providers;
}
/// <summary>
/// Determines whether this instance can refresh the specified provider.
/// </summary>
/// <param name="provider">The provider.</param>
/// <param name="item">The item.</param>
/// <returns><c>true</c> if this instance can refresh the specified provider; otherwise, <c>false</c>.</returns>
protected bool CanRefresh(IMetadataProvider provider, IHasMetadata item)
{
if (!ServerConfigurationManager.Configuration.EnableInternetProviders && provider is IRemoteMetadataProvider)
{
return false;
}
if (item.LocationType != LocationType.FileSystem && provider is ILocalMetadataProvider)
{
return false;
}
return true;
}
protected abstract Task SaveItem(TItemType item, ItemUpdateType reason, CancellationToken cancellationToken);
protected virtual ItemId GetId(IHasMetadata item)
{
return new ItemId
{
MetadataCountryCode = item.GetPreferredMetadataCountryCode(),
MetadataLanguage = item.GetPreferredMetadataLanguage(),
Name = item.Name,
ProviderIds = item.ProviderIds
};
}
public bool CanRefresh(IHasMetadata item)
{
return item is TItemType;
}
protected virtual async Task<RefreshResult> RefreshWithProviders(TItemType item, MetadataRefreshOptions options, List<IMetadataProvider> providers, CancellationToken cancellationToken)
{
var refreshResult = new RefreshResult
{
UpdateType = ItemUpdateType.Unspecified,
Providers = providers.Select(i => i.GetType().FullName.GetMD5()).ToList()
};
var temp = new TItemType();
// If replacing all metadata, run internet providers first
if (options.ReplaceAllMetadata)
{
await ExecuteRemoteProviders(item, temp, providers.OfType<IRemoteMetadataProvider<TItemType>>(), refreshResult, cancellationToken).ConfigureAwait(false);
}
var hasLocalMetadata = false;
foreach (var provider in providers.OfType<ILocalMetadataProvider<TItemType>>())
{
Logger.Debug("Running {0} for {1}", provider.GetType().Name, item.Path ?? item.Name);
try
{
var localItem = await provider.GetMetadata(item.Path, cancellationToken).ConfigureAwait(false);
if (localItem.HasMetadata)
{
MergeData(localItem.Item, temp, new List<MetadataFields>(), false, true);
refreshResult.UpdateType = refreshResult.UpdateType | ItemUpdateType.MetadataImport;
// Only one local provider allowed per item
hasLocalMetadata = true;
break;
}
}
catch (OperationCanceledException)
{
throw;
}
catch (Exception ex)
{
// If a local provider fails, consider that a failure
refreshResult.Status = ProviderRefreshStatus.Failure;
refreshResult.ErrorMessage = ex.Message;
Logger.ErrorException("Error in {0}", ex, provider.Name);
// If the local provider fails don't continue with remote providers because the user's saved metadata could be lost
return refreshResult;
}
}
if (!options.ReplaceAllMetadata && !hasLocalMetadata)
{
await ExecuteRemoteProviders(item, temp, providers.OfType<IRemoteMetadataProvider<TItemType>>(), refreshResult, cancellationToken).ConfigureAwait(false);
}
MergeData(temp, item, item.LockedFields, true, true);
return refreshResult;
}
private async Task ExecuteRemoteProviders(TItemType item, TItemType temp, IEnumerable<IRemoteMetadataProvider<TItemType>> providers, RefreshResult refreshResult, CancellationToken cancellationToken)
{
var id = GetId(item);
foreach (var provider in providers)
{
Logger.Debug("Running {0} for {1}", provider.GetType().Name, item.Path ?? item.Name);
try
{
var result = await provider.GetMetadata(id, cancellationToken).ConfigureAwait(false);
if (result.HasMetadata)
{
MergeData(result.Item, temp, new List<MetadataFields>(), false, false);
refreshResult.UpdateType = refreshResult.UpdateType | ItemUpdateType.MetadataDownload;
}
}
catch (OperationCanceledException)
{
throw;
}
catch (Exception ex)
{
refreshResult.Status = ProviderRefreshStatus.CompletedWithErrors;
refreshResult.ErrorMessage = ex.Message;
Logger.ErrorException("Error in {0}", ex, provider.Name);
}
}
}
protected abstract void MergeData(TItemType source, TItemType target, List<MetadataFields> lockedFields, bool replaceData, bool mergeMetadataSettings);
public virtual int Order
{
get
{
return 0;
}
}
private IEnumerable<IImageProvider> GetImageProviders(IHasImages item)
{
var providers = _imageProviders.Where(i =>
{
try
{
return i.Supports(item);
}
catch (Exception ex)
{
Logger.ErrorException("Error in ImageProvider.Supports", ex, i.Name);
return false;
}
});
if (!ServerConfigurationManager.Configuration.EnableInternetProviders)
{
providers = providers.Where(i => !(i is IRemoteImageProvider));
}
return providers.OrderBy(i => i.Order);
}
}
public class RefreshResult
{
public ItemUpdateType UpdateType { get; set; }
public ProviderRefreshStatus Status { get; set; }
public string ErrorMessage { get; set; }
public List<Guid> Providers { get; set; }
}
}

View File

@ -2,7 +2,6 @@
using MediaBrowser.Common.Net; using MediaBrowser.Common.Net;
using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.Movies;
using MediaBrowser.Controller.IO; using MediaBrowser.Controller.IO;
using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Persistence; using MediaBrowser.Controller.Persistence;
@ -17,7 +16,7 @@ using System.Linq;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace MediaBrowser.Server.Implementations.Providers namespace MediaBrowser.Providers.Manager
{ {
/// <summary> /// <summary>
/// Class ProviderManager /// Class ProviderManager
@ -37,7 +36,7 @@ namespace MediaBrowser.Server.Implementations.Providers
/// <summary> /// <summary>
/// The _directory watchers /// The _directory watchers
/// </summary> /// </summary>
private readonly IDirectoryWatchers _directoryWatchers; private readonly ILibraryMonitor _libraryMonitor;
/// <summary> /// <summary>
/// Gets or sets the configuration manager. /// Gets or sets the configuration manager.
@ -51,26 +50,32 @@ namespace MediaBrowser.Server.Implementations.Providers
/// <value>The metadata providers enumerable.</value> /// <value>The metadata providers enumerable.</value>
private BaseMetadataProvider[] MetadataProviders { get; set; } private BaseMetadataProvider[] MetadataProviders { get; set; }
private IRemoteImageProvider[] RemoteImageProviders { get; set; }
private IImageProvider[] ImageProviders { get; set; } private IImageProvider[] ImageProviders { get; set; }
private readonly IFileSystem _fileSystem; private readonly IFileSystem _fileSystem;
private readonly IItemRepository _itemRepo; private readonly IProviderRepository _providerRepo;
private IMetadataService[] _metadataServices = { };
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="ProviderManager" /> class. /// Initializes a new instance of the <see cref="ProviderManager" /> class.
/// </summary> /// </summary>
/// <param name="httpClient">The HTTP client.</param> /// <param name="httpClient">The HTTP client.</param>
/// <param name="configurationManager">The configuration manager.</param> /// <param name="configurationManager">The configuration manager.</param>
/// <param name="directoryWatchers">The directory watchers.</param> /// <param name="libraryMonitor">The directory watchers.</param>
/// <param name="logManager">The log manager.</param> /// <param name="logManager">The log manager.</param>
public ProviderManager(IHttpClient httpClient, IServerConfigurationManager configurationManager, IDirectoryWatchers directoryWatchers, ILogManager logManager, IFileSystem fileSystem, IItemRepository itemRepo) /// <param name="fileSystem">The file system.</param>
/// <param name="providerRepo">The provider repo.</param>
public ProviderManager(IHttpClient httpClient, IServerConfigurationManager configurationManager, ILibraryMonitor libraryMonitor, ILogManager logManager, IFileSystem fileSystem, IProviderRepository providerRepo)
{ {
_logger = logManager.GetLogger("ProviderManager"); _logger = logManager.GetLogger("ProviderManager");
_httpClient = httpClient; _httpClient = httpClient;
ConfigurationManager = configurationManager; ConfigurationManager = configurationManager;
_directoryWatchers = directoryWatchers; _libraryMonitor = libraryMonitor;
_fileSystem = fileSystem; _fileSystem = fileSystem;
_itemRepo = itemRepo; _providerRepo = providerRepo;
} }
/// <summary> /// <summary>
@ -78,11 +83,34 @@ namespace MediaBrowser.Server.Implementations.Providers
/// </summary> /// </summary>
/// <param name="providers">The providers.</param> /// <param name="providers">The providers.</param>
/// <param name="imageProviders">The image providers.</param> /// <param name="imageProviders">The image providers.</param>
public void AddParts(IEnumerable<BaseMetadataProvider> providers, IEnumerable<IImageProvider> imageProviders) /// <param name="metadataServices">The metadata services.</param>
/// <param name="metadataProviders">The metadata providers.</param>
public void AddParts(IEnumerable<BaseMetadataProvider> providers, IEnumerable<IImageProvider> imageProviders, IEnumerable<IMetadataService> metadataServices, IEnumerable<IMetadataProvider> metadataProviders)
{ {
MetadataProviders = providers.OrderBy(e => e.Priority).ToArray(); MetadataProviders = providers.OrderBy(e => e.Priority).ToArray();
ImageProviders = imageProviders.OrderByDescending(i => i.Priority).ToArray(); ImageProviders = imageProviders.OrderBy(i => i.Order).ToArray();
RemoteImageProviders = ImageProviders.OfType<IRemoteImageProvider>().ToArray();
_metadataServices = metadataServices.OrderBy(i => i.Order).ToArray();
var providerList = metadataProviders.ToList();
foreach (var service in _metadataServices)
{
service.AddParts(providerList, ImageProviders);
}
}
public Task RefreshMetadata(IHasMetadata item, MetadataRefreshOptions options, CancellationToken cancellationToken)
{
var service = _metadataServices.FirstOrDefault(i => i.CanRefresh(item));
if (service != null)
{
return service.RefreshMetadata(item, options, cancellationToken);
}
return ((BaseItem)item).RefreshMetadataDirect(cancellationToken, options.ForceSave, options.ReplaceAllMetadata);
} }
/// <summary> /// <summary>
@ -91,9 +119,9 @@ namespace MediaBrowser.Server.Implementations.Providers
/// <param name="item">The item.</param> /// <param name="item">The item.</param>
/// <param name="cancellationToken">The cancellation token.</param> /// <param name="cancellationToken">The cancellation token.</param>
/// <param name="force">if set to <c>true</c> [force].</param> /// <param name="force">if set to <c>true</c> [force].</param>
/// <param name="allowSlowProviders">if set to <c>true</c> [allow slow providers].</param>
/// <returns>Task{System.Boolean}.</returns> /// <returns>Task{System.Boolean}.</returns>
public async Task<ItemUpdateType?> ExecuteMetadataProviders(BaseItem item, CancellationToken cancellationToken, bool force = false, bool allowSlowProviders = true) /// <exception cref="System.ArgumentNullException">item</exception>
public async Task<ItemUpdateType?> ExecuteMetadataProviders(BaseItem item, CancellationToken cancellationToken, bool force = false)
{ {
if (item == null) if (item == null)
{ {
@ -108,7 +136,7 @@ namespace MediaBrowser.Server.Implementations.Providers
var providerHistories = item.DateLastSaved == default(DateTime) ? var providerHistories = item.DateLastSaved == default(DateTime) ?
new List<BaseProviderInfo>() : new List<BaseProviderInfo>() :
_itemRepo.GetProviderHistory(item.Id).ToList(); _providerRepo.GetProviderHistory(item.Id).ToList();
// Run the normal providers sequentially in order of priority // Run the normal providers sequentially in order of priority
foreach (var provider in MetadataProviders) foreach (var provider in MetadataProviders)
@ -126,12 +154,6 @@ namespace MediaBrowser.Server.Implementations.Providers
continue; continue;
} }
// Skip if is slow and we aren't allowing slow ones
if (provider.IsSlow && !allowSlowProviders)
{
continue;
}
// Put this check below the await because the needs refresh of the next tier of providers may depend on the previous ones running // Put this check below the await because the needs refresh of the next tier of providers may depend on the previous ones running
// This is the case for the fan art provider which depends on the movie and tv providers having run before them // This is the case for the fan art provider which depends on the movie and tv providers having run before them
if (provider.RequiresInternet && item.DontFetchMeta && provider.EnforceDontFetchMetadata) if (provider.RequiresInternet && item.DontFetchMeta && provider.EnforceDontFetchMetadata)
@ -179,7 +201,7 @@ namespace MediaBrowser.Server.Implementations.Providers
if (result.HasValue || force) if (result.HasValue || force)
{ {
await _itemRepo.SaveProviderHistory(item.Id, providerHistories, cancellationToken); await _providerRepo.SaveProviderHistory(item.Id, providerHistories, cancellationToken);
} }
return result; return result;
@ -293,7 +315,7 @@ namespace MediaBrowser.Server.Implementations.Providers
} }
//Tell the watchers to ignore //Tell the watchers to ignore
_directoryWatchers.TemporarilyIgnore(path); _libraryMonitor.ReportFileSystemChangeBeginning(path);
if (dataToSave.CanSeek) if (dataToSave.CanSeek)
{ {
@ -316,7 +338,7 @@ namespace MediaBrowser.Server.Implementations.Providers
finally finally
{ {
//Remove the ignore //Remove the ignore
_directoryWatchers.RemoveTempIgnore(path); _libraryMonitor.ReportFileSystemChangeComplete(path, false);
} }
} }
@ -358,7 +380,7 @@ namespace MediaBrowser.Server.Implementations.Providers
/// <returns>Task.</returns> /// <returns>Task.</returns>
public Task SaveImage(BaseItem item, Stream source, string mimeType, ImageType type, int? imageIndex, string sourceUrl, CancellationToken cancellationToken) public Task SaveImage(BaseItem item, Stream source, string mimeType, ImageType type, int? imageIndex, string sourceUrl, CancellationToken cancellationToken)
{ {
return new ImageSaver(ConfigurationManager, _directoryWatchers, _fileSystem, _logger).SaveImage(item, source, mimeType, type, imageIndex, sourceUrl, cancellationToken); return new ImageSaver(ConfigurationManager, _libraryMonitor, _fileSystem, _logger).SaveImage(item, source, mimeType, type, imageIndex, sourceUrl, cancellationToken);
} }
/// <summary> /// <summary>
@ -371,7 +393,7 @@ namespace MediaBrowser.Server.Implementations.Providers
/// <returns>Task{IEnumerable{RemoteImageInfo}}.</returns> /// <returns>Task{IEnumerable{RemoteImageInfo}}.</returns>
public async Task<IEnumerable<RemoteImageInfo>> GetAvailableRemoteImages(BaseItem item, CancellationToken cancellationToken, string providerName = null, ImageType? type = null) public async Task<IEnumerable<RemoteImageInfo>> GetAvailableRemoteImages(BaseItem item, CancellationToken cancellationToken, string providerName = null, ImageType? type = null)
{ {
var providers = GetImageProviders(item); var providers = GetRemoteImageProviders(item);
if (!string.IsNullOrEmpty(providerName)) if (!string.IsNullOrEmpty(providerName))
{ {
@ -396,7 +418,7 @@ namespace MediaBrowser.Server.Implementations.Providers
/// <param name="preferredLanguage">The preferred language.</param> /// <param name="preferredLanguage">The preferred language.</param>
/// <param name="type">The type.</param> /// <param name="type">The type.</param>
/// <returns>Task{IEnumerable{RemoteImageInfo}}.</returns> /// <returns>Task{IEnumerable{RemoteImageInfo}}.</returns>
private async Task<IEnumerable<RemoteImageInfo>> GetImages(BaseItem item, CancellationToken cancellationToken, IImageProvider i, string preferredLanguage, ImageType? type = null) private async Task<IEnumerable<RemoteImageInfo>> GetImages(BaseItem item, CancellationToken cancellationToken, IRemoteImageProvider i, string preferredLanguage, ImageType? type = null)
{ {
try try
{ {
@ -414,7 +436,7 @@ namespace MediaBrowser.Server.Implementations.Providers
} }
catch (Exception ex) catch (Exception ex)
{ {
_logger.ErrorException("{0} failed in GetImages for type {1}", ex, i.GetType().Name, item.GetType().Name); _logger.ErrorException("{0} failed in GetImageInfos for type {1}", ex, i.GetType().Name, item.GetType().Name);
return new List<RemoteImageInfo>(); return new List<RemoteImageInfo>();
} }
} }
@ -430,14 +452,9 @@ namespace MediaBrowser.Server.Implementations.Providers
return images; return images;
} }
/// <summary> private IEnumerable<IRemoteImageProvider> GetRemoteImageProviders(BaseItem item)
/// Gets the supported image providers.
/// </summary>
/// <param name="item">The item.</param>
/// <returns>IEnumerable{IImageProvider}.</returns>
public IEnumerable<IImageProvider> GetImageProviders(BaseItem item)
{ {
return ImageProviders.Where(i => return RemoteImageProviders.Where(i =>
{ {
try try
{ {
@ -448,6 +465,22 @@ namespace MediaBrowser.Server.Implementations.Providers
_logger.ErrorException("{0} failed in Supports for type {1}", ex, i.GetType().Name, item.GetType().Name); _logger.ErrorException("{0} failed in Supports for type {1}", ex, i.GetType().Name, item.GetType().Name);
return false; return false;
} }
});
}
/// <summary>
/// Gets the supported image providers.
/// </summary>
/// <param name="item">The item.</param>
/// <returns>IEnumerable{IImageProvider}.</returns>
public IEnumerable<ImageProviderInfo> GetImageProviderInfo(BaseItem item)
{
return GetRemoteImageProviders(item).Select(i => new ImageProviderInfo
{
Name = i.Name,
Order = i.Order
}); });
} }
} }

View File

@ -64,6 +64,17 @@
</Reference> </Reference>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Compile Include="All\LocalImageProvider.cs" />
<Compile Include="GameGenres\GameGenreMetadataService.cs" />
<Compile Include="Genres\GenreMetadataService.cs" />
<Compile Include="LiveTv\ChannelMetadataService.cs" />
<Compile Include="LiveTv\ChannelXmlProvider.cs" />
<Compile Include="LiveTv\ProgramMetadataService.cs" />
<Compile Include="Manager\ImageSaver.cs" />
<Compile Include="Manager\ItemImageProvider.cs" />
<Compile Include="Manager\ProviderManager.cs" />
<Compile Include="Manager\MetadataService.cs" />
<Compile Include="BaseXmlProvider.cs" />
<Compile Include="CollectionFolderImageProvider.cs" /> <Compile Include="CollectionFolderImageProvider.cs" />
<Compile Include="FanartBaseProvider.cs" /> <Compile Include="FanartBaseProvider.cs" />
<Compile Include="FolderProviderFromXml.cs" /> <Compile Include="FolderProviderFromXml.cs" />
@ -72,14 +83,10 @@
<Compile Include="Games\GameSystemProviderFromXml.cs" /> <Compile Include="Games\GameSystemProviderFromXml.cs" />
<Compile Include="ImageFromMediaLocationProvider.cs" /> <Compile Include="ImageFromMediaLocationProvider.cs" />
<Compile Include="ImagesByNameProvider.cs" /> <Compile Include="ImagesByNameProvider.cs" />
<Compile Include="ImagesByName\MusicGenreImageProvider.cs" /> <Compile Include="MusicGenres\MusicGenreImageProvider.cs" />
<Compile Include="ImagesByName\MusicGenresManualImageProvider.cs" /> <Compile Include="GameGenres\GameGenreImageProvider.cs" />
<Compile Include="ImagesByName\GameGenreImageProvider.cs" /> <Compile Include="Genres\GenreImageProvider.cs" />
<Compile Include="ImagesByName\GameGenresManualImageProvider.cs" />
<Compile Include="ImagesByName\GenreImageProvider.cs" />
<Compile Include="ImagesByName\GenresManualImageProvider.cs" />
<Compile Include="ImagesByName\ImageUtils.cs" /> <Compile Include="ImagesByName\ImageUtils.cs" />
<Compile Include="LiveTv\ChannelProviderFromXml.cs" />
<Compile Include="MediaInfo\AudioImageProvider.cs" /> <Compile Include="MediaInfo\AudioImageProvider.cs" />
<Compile Include="MediaInfo\BaseFFProbeProvider.cs" /> <Compile Include="MediaInfo\BaseFFProbeProvider.cs" />
<Compile Include="MediaInfo\FFProbeAudioInfoProvider.cs" /> <Compile Include="MediaInfo\FFProbeAudioInfoProvider.cs" />
@ -88,8 +95,8 @@
<Compile Include="Movies\BoxSetProviderFromXml.cs" /> <Compile Include="Movies\BoxSetProviderFromXml.cs" />
<Compile Include="Movies\ManualMovieDbImageProvider.cs" /> <Compile Include="Movies\ManualMovieDbImageProvider.cs" />
<Compile Include="Movies\ManualFanartMovieImageProvider.cs" /> <Compile Include="Movies\ManualFanartMovieImageProvider.cs" />
<Compile Include="Movies\ManualMovieDbPersonImageProvider.cs" /> <Compile Include="MusicGenres\MusicGenreMetadataService.cs" />
<Compile Include="Movies\MovieDbPersonImageProvider.cs" /> <Compile Include="People\MovieDbPersonImageProvider.cs" />
<Compile Include="Movies\MovieUpdatesPrescanTask.cs" /> <Compile Include="Movies\MovieUpdatesPrescanTask.cs" />
<Compile Include="Movies\MovieXmlParser.cs" /> <Compile Include="Movies\MovieXmlParser.cs" />
<Compile Include="Movies\FanArtMovieProvider.cs" /> <Compile Include="Movies\FanArtMovieProvider.cs" />
@ -98,8 +105,6 @@
<Compile Include="Movies\MovieDbProvider.cs" /> <Compile Include="Movies\MovieDbProvider.cs" />
<Compile Include="Movies\MovieProviderFromXml.cs" /> <Compile Include="Movies\MovieProviderFromXml.cs" />
<Compile Include="Movies\OpenMovieDatabaseProvider.cs" /> <Compile Include="Movies\OpenMovieDatabaseProvider.cs" />
<Compile Include="Movies\PersonProviderFromXml.cs" />
<Compile Include="Movies\MovieDbPersonProvider.cs" />
<Compile Include="Music\AlbumInfoFromSongProvider.cs" /> <Compile Include="Music\AlbumInfoFromSongProvider.cs" />
<Compile Include="Music\AlbumProviderFromXml.cs" /> <Compile Include="Music\AlbumProviderFromXml.cs" />
<Compile Include="Music\ArtistInfoFromSongProvider.cs" /> <Compile Include="Music\ArtistInfoFromSongProvider.cs" />
@ -118,7 +123,11 @@
<Compile Include="Music\MusicBrainzAlbumProvider.cs" /> <Compile Include="Music\MusicBrainzAlbumProvider.cs" />
<Compile Include="Music\MusicVideoXmlParser.cs" /> <Compile Include="Music\MusicVideoXmlParser.cs" />
<Compile Include="Music\SoundtrackPostScanTask.cs" /> <Compile Include="Music\SoundtrackPostScanTask.cs" />
<Compile Include="People\PersonMetadataService.cs" />
<Compile Include="People\PersonXmlProvider.cs" />
<Compile Include="People\MovieDbPersonProvider.cs" />
<Compile Include="Properties\AssemblyInfo.cs" /> <Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="ProviderUtils.cs" />
<Compile Include="RefreshIntrosTask.cs" /> <Compile Include="RefreshIntrosTask.cs" />
<Compile Include="Savers\AlbumXmlSaver.cs" /> <Compile Include="Savers\AlbumXmlSaver.cs" />
<Compile Include="Savers\ArtistXmlSaver.cs" /> <Compile Include="Savers\ArtistXmlSaver.cs" />
@ -133,8 +142,8 @@
<Compile Include="Savers\SeasonXmlSaver.cs" /> <Compile Include="Savers\SeasonXmlSaver.cs" />
<Compile Include="Savers\SeriesXmlSaver.cs" /> <Compile Include="Savers\SeriesXmlSaver.cs" />
<Compile Include="Savers\XmlSaverHelpers.cs" /> <Compile Include="Savers\XmlSaverHelpers.cs" />
<Compile Include="ImagesByName\StudioImageProvider.cs" /> <Compile Include="Studios\StudiosImageProvider.cs" />
<Compile Include="ImagesByName\StudiosManualImageProvider.cs" /> <Compile Include="Studios\StudioMetadataService.cs" />
<Compile Include="TV\EpisodeImageFromMediaLocationProvider.cs" /> <Compile Include="TV\EpisodeImageFromMediaLocationProvider.cs" />
<Compile Include="TV\EpisodeIndexNumberProvider.cs" /> <Compile Include="TV\EpisodeIndexNumberProvider.cs" />
<Compile Include="TV\EpisodeProviderFromXml.cs" /> <Compile Include="TV\EpisodeProviderFromXml.cs" />
@ -145,7 +154,7 @@
<Compile Include="TV\ManualFanartSeasonProvider.cs" /> <Compile Include="TV\ManualFanartSeasonProvider.cs" />
<Compile Include="TV\ManualFanartSeriesProvider.cs" /> <Compile Include="TV\ManualFanartSeriesProvider.cs" />
<Compile Include="TV\ManualTvdbEpisodeImageProvider.cs" /> <Compile Include="TV\ManualTvdbEpisodeImageProvider.cs" />
<Compile Include="TV\ManualTvdbPersonImageProvider.cs" /> <Compile Include="People\TvdbPersonImageProvider.cs" />
<Compile Include="TV\ManualTvdbSeasonImageProvider.cs" /> <Compile Include="TV\ManualTvdbSeasonImageProvider.cs" />
<Compile Include="TV\ManualTvdbSeriesImageProvider.cs" /> <Compile Include="TV\ManualTvdbSeriesImageProvider.cs" />
<Compile Include="TV\SeasonIndexNumberProvider.cs" /> <Compile Include="TV\SeasonIndexNumberProvider.cs" />
@ -157,7 +166,6 @@
<Compile Include="TV\SeriesPostScanTask.cs" /> <Compile Include="TV\SeriesPostScanTask.cs" />
<Compile Include="TV\SeriesProviderFromXml.cs" /> <Compile Include="TV\SeriesProviderFromXml.cs" />
<Compile Include="TV\SeriesXmlParser.cs" /> <Compile Include="TV\SeriesXmlParser.cs" />
<Compile Include="TV\TvdbPersonImageProvider.cs" />
<Compile Include="TV\TvdbPrescanTask.cs" /> <Compile Include="TV\TvdbPrescanTask.cs" />
<Compile Include="TV\TvdbSeriesImageProvider.cs" /> <Compile Include="TV\TvdbSeriesImageProvider.cs" />
<Compile Include="UserRootFolderNameProvider.cs" /> <Compile Include="UserRootFolderNameProvider.cs" />
@ -180,6 +188,7 @@
<ItemGroup> <ItemGroup>
<None Include="packages.config" /> <None Include="packages.config" />
</ItemGroup> </ItemGroup>
<ItemGroup />
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<Import Project="$(SolutionDir)\.nuget\NuGet.targets" Condition=" '$(ConfigurationName)' != 'Release Mono' " /> <Import Project="$(SolutionDir)\.nuget\NuGet.targets" Condition=" '$(ConfigurationName)' != 'Release Mono' " />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it. <!-- To modify your build process, add your task inside one of the targets below and uncomment it.

View File

@ -1,4 +1,5 @@
using MediaBrowser.Controller.Configuration; using MediaBrowser.Common.Net;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Providers; using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Dto; using MediaBrowser.Model.Dto;
@ -16,14 +17,16 @@ using System.Xml;
namespace MediaBrowser.Providers.Movies namespace MediaBrowser.Providers.Movies
{ {
public class ManualFanartMovieImageProvider : IImageProvider public class ManualFanartMovieImageProvider : IRemoteImageProvider
{ {
private readonly CultureInfo _usCulture = new CultureInfo("en-US"); private readonly CultureInfo _usCulture = new CultureInfo("en-US");
private readonly IServerConfigurationManager _config; private readonly IServerConfigurationManager _config;
private readonly IHttpClient _httpClient;
public ManualFanartMovieImageProvider(IServerConfigurationManager config) public ManualFanartMovieImageProvider(IServerConfigurationManager config, IHttpClient httpClient)
{ {
_config = config; _config = config;
_httpClient = httpClient;
} }
public string Name public string Name
@ -41,6 +44,20 @@ namespace MediaBrowser.Providers.Movies
return FanArtMovieProvider.SupportsItem(item); return FanArtMovieProvider.SupportsItem(item);
} }
public IEnumerable<ImageType> GetSupportedImages(IHasImages item)
{
return new List<ImageType>
{
ImageType.Primary,
ImageType.Thumb,
ImageType.Art,
ImageType.Logo,
ImageType.Disc,
ImageType.Banner,
ImageType.Backdrop
};
}
public async Task<IEnumerable<RemoteImageInfo>> GetImages(IHasImages item, ImageType imageType, CancellationToken cancellationToken) public async Task<IEnumerable<RemoteImageInfo>> GetImages(IHasImages item, ImageType imageType, CancellationToken cancellationToken)
{ {
var images = await GetAllImages(item, cancellationToken).ConfigureAwait(false); var images = await GetAllImages(item, cancellationToken).ConfigureAwait(false);
@ -294,9 +311,19 @@ namespace MediaBrowser.Providers.Movies
} }
} }
public int Priority public int Order
{ {
get { return 1; } get { return 1; }
} }
public Task<HttpResponseInfo> GetImageResponse(string url, CancellationToken cancellationToken)
{
return _httpClient.GetResponse(new HttpRequestOptions
{
CancellationToken = cancellationToken,
Url = url,
ResourcePool = FanartBaseProvider.FanArtResourcePool
});
}
} }
} }

View File

@ -1,4 +1,5 @@
using MediaBrowser.Controller.Configuration; using MediaBrowser.Common.Net;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Providers; using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Dto; using MediaBrowser.Model.Dto;
@ -14,15 +15,17 @@ using System.Threading.Tasks;
namespace MediaBrowser.Providers.Movies namespace MediaBrowser.Providers.Movies
{ {
class ManualMovieDbImageProvider : IImageProvider class ManualMovieDbImageProvider : IRemoteImageProvider
{ {
private readonly IJsonSerializer _jsonSerializer; private readonly IJsonSerializer _jsonSerializer;
private readonly IServerConfigurationManager _config; private readonly IServerConfigurationManager _config;
private readonly IHttpClient _httpClient;
public ManualMovieDbImageProvider(IJsonSerializer jsonSerializer, IServerConfigurationManager config) public ManualMovieDbImageProvider(IJsonSerializer jsonSerializer, IServerConfigurationManager config, IHttpClient httpClient)
{ {
_jsonSerializer = jsonSerializer; _jsonSerializer = jsonSerializer;
_config = config; _config = config;
_httpClient = httpClient;
} }
public string Name public string Name
@ -40,6 +43,15 @@ namespace MediaBrowser.Providers.Movies
return MovieDbImagesProvider.SupportsItem(item); return MovieDbImagesProvider.SupportsItem(item);
} }
public IEnumerable<ImageType> GetSupportedImages(IHasImages item)
{
return new List<ImageType>
{
ImageType.Primary,
ImageType.Backdrop
};
}
public async Task<IEnumerable<RemoteImageInfo>> GetImages(IHasImages item, ImageType imageType, CancellationToken cancellationToken) public async Task<IEnumerable<RemoteImageInfo>> GetImages(IHasImages item, ImageType imageType, CancellationToken cancellationToken)
{ {
var images = await GetAllImages(item, cancellationToken).ConfigureAwait(false); var images = await GetAllImages(item, cancellationToken).ConfigureAwait(false);
@ -167,9 +179,19 @@ namespace MediaBrowser.Providers.Movies
return null; return null;
} }
public int Priority public int Order
{ {
get { return 2; } get { return 0; }
}
public Task<HttpResponseInfo> GetImageResponse(string url, CancellationToken cancellationToken)
{
return _httpClient.GetResponse(new HttpRequestOptions
{
CancellationToken = cancellationToken,
Url = url,
ResourcePool = MovieDbProvider.Current.MovieDbResourcePool
});
} }
} }
} }

View File

@ -1,207 +0,0 @@
using MediaBrowser.Common.IO;
using MediaBrowser.Common.Net;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Logging;
using MediaBrowser.Model.Providers;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
namespace MediaBrowser.Providers.Movies
{
/// <summary>
/// Class MovieDbPersonImageProvider.
/// </summary>
public class MovieDbPersonImageProvider : BaseMetadataProvider
{
/// <summary>
/// The _provider manager
/// </summary>
private readonly IProviderManager _providerManager;
private readonly IFileSystem _fileSystem;
/// <summary>
/// Initializes a new instance of the <see cref="MediaBrowser.Providers.Movies.MovieDbImagesProvider"/> class.
/// </summary>
/// <param name="logManager">The log manager.</param>
/// <param name="configurationManager">The configuration manager.</param>
/// <param name="providerManager">The provider manager.</param>
public MovieDbPersonImageProvider(ILogManager logManager, IServerConfigurationManager configurationManager, IProviderManager providerManager, IFileSystem fileSystem)
: base(logManager, configurationManager)
{
_providerManager = providerManager;
_fileSystem = fileSystem;
}
/// <summary>
/// Gets the priority.
/// </summary>
/// <value>The priority.</value>
public override MetadataProviderPriority Priority
{
get { return MetadataProviderPriority.Third; }
}
/// <summary>
/// Supports the specified item.
/// </summary>
/// <param name="item">The item.</param>
/// <returns><c>true</c> if XXXX, <c>false</c> otherwise</returns>
public override bool Supports(BaseItem item)
{
return item is Person;
}
public override ItemUpdateType ItemUpdateType
{
get
{
return ItemUpdateType.ImageUpdate;
}
}
/// <summary>
/// Gets a value indicating whether [requires internet].
/// </summary>
/// <value><c>true</c> if [requires internet]; otherwise, <c>false</c>.</value>
public override bool RequiresInternet
{
get
{
return true;
}
}
/// <summary>
/// Gets a value indicating whether [refresh on version change].
/// </summary>
/// <value><c>true</c> if [refresh on version change]; otherwise, <c>false</c>.</value>
protected override bool RefreshOnVersionChange
{
get
{
return true;
}
}
/// <summary>
/// Gets the provider version.
/// </summary>
/// <value>The provider version.</value>
protected override string ProviderVersion
{
get
{
return "3";
}
}
/// <summary>
/// Needses the refresh internal.
/// </summary>
/// <param name="item">The item.</param>
/// <param name="providerInfo">The provider info.</param>
/// <returns><c>true</c> if XXXX, <c>false</c> otherwise</returns>
protected override bool NeedsRefreshInternal(BaseItem item, BaseProviderInfo providerInfo)
{
if (string.IsNullOrEmpty(item.GetProviderId(MetadataProviders.Tmdb)))
{
return false;
}
// Don't refresh if we already have both poster and backdrop and we're not refreshing images
if (item.HasImage(ImageType.Primary))
{
return false;
}
return base.NeedsRefreshInternal(item, providerInfo);
}
/// <summary>
/// Needses the refresh based on compare date.
/// </summary>
/// <param name="item">The item.</param>
/// <param name="providerInfo">The provider info.</param>
/// <returns><c>true</c> if XXXX, <c>false</c> otherwise</returns>
protected override bool NeedsRefreshBasedOnCompareDate(BaseItem item, BaseProviderInfo providerInfo)
{
var provderId = item.GetProviderId(MetadataProviders.Tmdb);
if (!string.IsNullOrEmpty(provderId))
{
// Process images
var path = MovieDbPersonProvider.GetPersonDataFilePath(ConfigurationManager.ApplicationPaths, provderId);
var fileInfo = new FileInfo(path);
if (fileInfo.Exists)
{
return _fileSystem.GetLastWriteTimeUtc(fileInfo) > providerInfo.LastRefreshed;
}
return false;
}
return false;
}
/// <summary>
/// Fetches metadata and returns true or false indicating if any work that requires persistence was done
/// </summary>
/// <param name="item">The item.</param>
/// <param name="force">if set to <c>true</c> [force].</param>
/// <param name="cancellationToken">The cancellation token</param>
/// <returns>Task{System.Boolean}.</returns>
public override async Task<bool> FetchAsync(BaseItem item, bool force, BaseProviderInfo providerInfo, CancellationToken cancellationToken)
{
var images = await _providerManager.GetAvailableRemoteImages(item, cancellationToken, ManualMovieDbPersonImageProvider.ProviderName).ConfigureAwait(false);
await ProcessImages(item, images.ToList(), cancellationToken).ConfigureAwait(false);
SetLastRefreshed(item, DateTime.UtcNow, providerInfo);
return true;
}
/// <summary>
/// Processes the images.
/// </summary>
/// <param name="item">The item.</param>
/// <param name="images">The images.</param>
/// <param name="cancellationToken">The cancellation token</param>
/// <returns>Task.</returns>
private async Task ProcessImages(BaseItem item, List<RemoteImageInfo> images, CancellationToken cancellationToken)
{
cancellationToken.ThrowIfCancellationRequested();
var eligiblePosters = images
.Where(i => i.Type == ImageType.Primary)
.ToList();
// poster
if (eligiblePosters.Count > 0 && !item.HasImage(ImageType.Primary) && !item.LockedFields.Contains(MetadataFields.Images))
{
var poster = eligiblePosters[0];
var url = poster.Url;
var img = await MovieDbProvider.Current.GetMovieDbResponse(new HttpRequestOptions
{
Url = url,
CancellationToken = cancellationToken
}).ConfigureAwait(false);
await _providerManager.SaveImage(item, img, MimeTypes.GetMimeType(url), ImageType.Primary, null, url, cancellationToken)
.ConfigureAwait(false);
}
}
}
}

View File

@ -1,440 +0,0 @@
using MediaBrowser.Common.Configuration;
using MediaBrowser.Common.Extensions;
using MediaBrowser.Common.IO;
using MediaBrowser.Common.Net;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Logging;
using MediaBrowser.Model.Serialization;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Net;
using System.Threading;
using System.Threading.Tasks;
namespace MediaBrowser.Providers.Movies
{
/// <summary>
/// Class TmdbPersonProvider
/// </summary>
public class MovieDbPersonProvider : BaseMetadataProvider
{
protected readonly IProviderManager ProviderManager;
internal static MovieDbPersonProvider Current { get; private set; }
const string DataFileName = "info.json";
private readonly IFileSystem _fileSystem;
public MovieDbPersonProvider(IJsonSerializer jsonSerializer, ILogManager logManager, IServerConfigurationManager configurationManager, IProviderManager providerManager, IFileSystem fileSystem)
: base(logManager, configurationManager)
{
if (jsonSerializer == null)
{
throw new ArgumentNullException("jsonSerializer");
}
JsonSerializer = jsonSerializer;
ProviderManager = providerManager;
_fileSystem = fileSystem;
Current = this;
}
/// <summary>
/// Gets the json serializer.
/// </summary>
/// <value>The json serializer.</value>
protected IJsonSerializer JsonSerializer { get; private set; }
/// <summary>
/// Supportses the specified item.
/// </summary>
/// <param name="item">The item.</param>
/// <returns><c>true</c> if XXXX, <c>false</c> otherwise</returns>
public override bool Supports(BaseItem item)
{
return item is Person;
}
protected override bool RefreshOnVersionChange
{
get
{
return true;
}
}
protected override string ProviderVersion
{
get
{
return "3";
}
}
public override ItemUpdateType ItemUpdateType
{
get
{
return ItemUpdateType.MetadataDownload;
}
}
protected override bool NeedsRefreshInternal(BaseItem item, BaseProviderInfo providerInfo)
{
if (HasAltMeta(item))
return false;
return base.NeedsRefreshInternal(item, providerInfo);
}
protected override bool NeedsRefreshBasedOnCompareDate(BaseItem item, BaseProviderInfo providerInfo)
{
var provderId = item.GetProviderId(MetadataProviders.Tmdb);
if (!string.IsNullOrEmpty(provderId))
{
// Process images
var path = GetPersonDataPath(ConfigurationManager.ApplicationPaths, provderId);
var file = Path.Combine(path, DataFileName);
var fileInfo = new FileInfo(file);
if (fileInfo.Exists)
{
return _fileSystem.GetLastWriteTimeUtc(fileInfo) > providerInfo.LastRefreshed;
}
return true;
}
return base.NeedsRefreshBasedOnCompareDate(item, providerInfo);
}
internal static string GetPersonDataPath(IApplicationPaths appPaths, string tmdbId)
{
var letter = tmdbId.GetMD5().ToString().Substring(0, 1);
var seriesDataPath = Path.Combine(GetPersonsDataPath(appPaths), letter, tmdbId);
return seriesDataPath;
}
internal static string GetPersonDataFilePath(IApplicationPaths appPaths, string tmdbId)
{
var letter = tmdbId.GetMD5().ToString().Substring(0, 1);
var seriesDataPath = Path.Combine(GetPersonsDataPath(appPaths), letter, tmdbId);
return Path.Combine(seriesDataPath, DataFileName);
}
internal static string GetPersonsDataPath(IApplicationPaths appPaths)
{
var dataPath = Path.Combine(appPaths.DataPath, "tmdb-people");
return dataPath;
}
private bool HasAltMeta(BaseItem item)
{
return item.LocationType == LocationType.FileSystem && item.ResolveArgs.ContainsMetaFileByName("person.xml");
}
/// <summary>
/// Fetches metadata and returns true or false indicating if any work that requires persistence was done
/// </summary>
/// <param name="item">The item.</param>
/// <param name="force">if set to <c>true</c> [force].</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Task{System.Boolean}.</returns>
public override async Task<bool> FetchAsync(BaseItem item, bool force, BaseProviderInfo providerInfo, CancellationToken cancellationToken)
{
cancellationToken.ThrowIfCancellationRequested();
var person = (Person)item;
var id = person.GetProviderId(MetadataProviders.Tmdb);
// We don't already have an Id, need to fetch it
if (string.IsNullOrEmpty(id))
{
id = await GetTmdbId(item, cancellationToken).ConfigureAwait(false);
}
cancellationToken.ThrowIfCancellationRequested();
if (!string.IsNullOrEmpty(id))
{
await FetchInfo(person, id, force, cancellationToken).ConfigureAwait(false);
}
SetLastRefreshed(item, DateTime.UtcNow, providerInfo);
return true;
}
/// <summary>
/// Gets the priority.
/// </summary>
/// <value>The priority.</value>
public override MetadataProviderPriority Priority
{
get { return MetadataProviderPriority.Second; }
}
/// <summary>
/// Gets a value indicating whether [requires internet].
/// </summary>
/// <value><c>true</c> if [requires internet]; otherwise, <c>false</c>.</value>
public override bool RequiresInternet
{
get
{
return true;
}
}
private readonly CultureInfo _usCulture = new CultureInfo("en-US");
/// <summary>
/// Gets the TMDB id.
/// </summary>
/// <param name="person">The person.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Task{System.String}.</returns>
private async Task<string> GetTmdbId(BaseItem person, CancellationToken cancellationToken)
{
string url = string.Format(@"http://api.themoviedb.org/3/search/person?api_key={1}&query={0}", WebUtility.UrlEncode(person.Name), MovieDbProvider.ApiKey);
PersonSearchResults searchResult = null;
using (var json = await MovieDbProvider.Current.GetMovieDbResponse(new HttpRequestOptions
{
Url = url,
CancellationToken = cancellationToken,
AcceptHeader = MovieDbProvider.AcceptHeader
}).ConfigureAwait(false))
{
searchResult = JsonSerializer.DeserializeFromStream<PersonSearchResults>(json);
}
return searchResult != null && searchResult.Total_Results > 0 ? searchResult.Results[0].Id.ToString(_usCulture) : null;
}
/// <summary>
/// Fetches the info.
/// </summary>
/// <param name="person">The person.</param>
/// <param name="id">The id.</param>
/// <param name="isForcedRefresh">if set to <c>true</c> [is forced refresh].</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Task.</returns>
private async Task FetchInfo(Person person, string id, bool isForcedRefresh, CancellationToken cancellationToken)
{
await EnsurePersonInfo(id, cancellationToken).ConfigureAwait(false);
if (isForcedRefresh || !HasAltMeta(person))
{
var dataFilePath = GetPersonDataFilePath(ConfigurationManager.ApplicationPaths, id);
var info = JsonSerializer.DeserializeFromFile<PersonResult>(dataFilePath);
cancellationToken.ThrowIfCancellationRequested();
ProcessInfo(person, info);
}
}
internal async Task EnsurePersonInfo(string id, CancellationToken cancellationToken)
{
var personDataPath = GetPersonDataPath(ConfigurationManager.ApplicationPaths, id);
var fileInfo = _fileSystem.GetFileSystemInfo(personDataPath);
if (fileInfo.Exists && (DateTime.UtcNow - _fileSystem.GetLastWriteTimeUtc(fileInfo)).TotalDays <= 7)
{
return;
}
var url = string.Format(@"http://api.themoviedb.org/3/person/{1}?api_key={0}&append_to_response=credits,images", MovieDbProvider.ApiKey, id);
using (var json = await MovieDbProvider.Current.GetMovieDbResponse(new HttpRequestOptions
{
Url = url,
CancellationToken = cancellationToken,
AcceptHeader = MovieDbProvider.AcceptHeader
}).ConfigureAwait(false))
{
Directory.CreateDirectory(personDataPath);
using (var fs = _fileSystem.GetFileStream(Path.Combine(personDataPath, DataFileName), FileMode.Create, FileAccess.Write, FileShare.Read, true))
{
await json.CopyToAsync(fs).ConfigureAwait(false);
}
}
}
/// <summary>
/// Processes the info.
/// </summary>
/// <param name="person">The person.</param>
/// <param name="searchResult">The search result.</param>
protected void ProcessInfo(Person person, PersonResult searchResult)
{
if (!person.LockedFields.Contains(MetadataFields.Overview))
{
person.Overview = searchResult.biography;
}
DateTime date;
if (DateTime.TryParseExact(searchResult.birthday, "yyyy-MM-dd", new CultureInfo("en-US"), DateTimeStyles.None, out date))
{
person.PremiereDate = date.ToUniversalTime();
}
if (DateTime.TryParseExact(searchResult.deathday, "yyyy-MM-dd", new CultureInfo("en-US"), DateTimeStyles.None, out date))
{
person.EndDate = date.ToUniversalTime();
}
if (!string.IsNullOrEmpty(searchResult.homepage))
{
person.HomePageUrl = searchResult.homepage;
}
if (!person.LockedFields.Contains(MetadataFields.ProductionLocations))
{
if (!string.IsNullOrEmpty(searchResult.place_of_birth))
{
person.PlaceOfBirth = searchResult.place_of_birth;
}
}
person.SetProviderId(MetadataProviders.Tmdb, searchResult.id.ToString(_usCulture));
}
#region Result Objects
/// <summary>
/// Class PersonSearchResult
/// </summary>
public class PersonSearchResult
{
/// <summary>
/// Gets or sets a value indicating whether this <see cref="PersonSearchResult" /> is adult.
/// </summary>
/// <value><c>true</c> if adult; otherwise, <c>false</c>.</value>
public bool Adult { get; set; }
/// <summary>
/// Gets or sets the id.
/// </summary>
/// <value>The id.</value>
public int Id { get; set; }
/// <summary>
/// Gets or sets the name.
/// </summary>
/// <value>The name.</value>
public string Name { get; set; }
/// <summary>
/// Gets or sets the profile_ path.
/// </summary>
/// <value>The profile_ path.</value>
public string Profile_Path { get; set; }
}
/// <summary>
/// Class PersonSearchResults
/// </summary>
public class PersonSearchResults
{
/// <summary>
/// Gets or sets the page.
/// </summary>
/// <value>The page.</value>
public int Page { get; set; }
/// <summary>
/// Gets or sets the results.
/// </summary>
/// <value>The results.</value>
public List<PersonSearchResult> Results { get; set; }
/// <summary>
/// Gets or sets the total_ pages.
/// </summary>
/// <value>The total_ pages.</value>
public int Total_Pages { get; set; }
/// <summary>
/// Gets or sets the total_ results.
/// </summary>
/// <value>The total_ results.</value>
public int Total_Results { get; set; }
}
public class Cast
{
public int id { get; set; }
public string title { get; set; }
public string character { get; set; }
public string original_title { get; set; }
public string poster_path { get; set; }
public string release_date { get; set; }
public bool adult { get; set; }
}
public class Crew
{
public int id { get; set; }
public string title { get; set; }
public string original_title { get; set; }
public string department { get; set; }
public string job { get; set; }
public string poster_path { get; set; }
public string release_date { get; set; }
public bool adult { get; set; }
}
public class Credits
{
public List<Cast> cast { get; set; }
public List<Crew> crew { get; set; }
}
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; }
}
public class Images
{
public List<Profile> profiles { get; set; }
}
public class PersonResult
{
public bool adult { get; set; }
public List<object> also_known_as { get; set; }
public string biography { get; set; }
public string birthday { get; set; }
public string deathday { get; set; }
public string homepage { get; set; }
public int id { get; set; }
public string imdb_id { get; set; }
public string name { get; set; }
public string place_of_birth { get; set; }
public double popularity { get; set; }
public string profile_path { get; set; }
public Credits credits { get; set; }
public Images images { get; set; }
}
#endregion
}
}

View File

@ -697,7 +697,8 @@ namespace MediaBrowser.Providers.Movies
} }
if (!movie.LockedFields.Contains(MetadataFields.Overview)) if (!movie.LockedFields.Contains(MetadataFields.Overview))
{ {
movie.Overview = WebUtility.HtmlDecode(movieData.overview); // Bug in Mono: WebUtility.HtmlDecode should return null if the string is null but in Mono it generate an System.ArgumentNullException.
movie.Overview = movieData.overview != null ? WebUtility.HtmlDecode(movieData.overview) : null;
movie.Overview = movie.Overview != null ? movie.Overview.Replace("\n\n", "\n") : null; movie.Overview = movie.Overview != null ? movie.Overview.Replace("\n\n", "\n") : null;
} }
movie.HomePageUrl = movieData.homepage; movie.HomePageUrl = movieData.homepage;

View File

@ -1,89 +0,0 @@
using MediaBrowser.Common.IO;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Logging;
using System;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
namespace MediaBrowser.Providers.Movies
{
class PersonProviderFromXml : BaseMetadataProvider
{
private readonly IFileSystem _fileSystem;
public PersonProviderFromXml(ILogManager logManager, IServerConfigurationManager configurationManager, IFileSystem fileSystem)
: base(logManager, configurationManager)
{
_fileSystem = fileSystem;
}
/// <summary>
/// Supportses the specified item.
/// </summary>
/// <param name="item">The item.</param>
/// <returns><c>true</c> if XXXX, <c>false</c> otherwise</returns>
public override bool Supports(BaseItem item)
{
return item is Person;
}
/// <summary>
/// Gets the priority.
/// </summary>
/// <value>The priority.</value>
public override MetadataProviderPriority Priority
{
get { return MetadataProviderPriority.Second; }
}
private const string XmlFileName = "person.xml";
protected override bool NeedsRefreshBasedOnCompareDate(BaseItem item, BaseProviderInfo providerInfo)
{
var xml = item.ResolveArgs.GetMetaFileByPath(Path.Combine(item.MetaLocation, XmlFileName));
if (xml == null)
{
return false;
}
return _fileSystem.GetLastWriteTimeUtc(xml) > item.DateLastSaved;
}
/// <summary>
/// Fetches metadata and returns true or false indicating if any work that requires persistence was done
/// </summary>
/// <param name="item">The item.</param>
/// <param name="force">if set to <c>true</c> [force].</param>
/// <param name="providerInfo">The provider information.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Task{System.Boolean}.</returns>
public override async Task<bool> FetchAsync(BaseItem item, bool force, BaseProviderInfo providerInfo, CancellationToken cancellationToken)
{
cancellationToken.ThrowIfCancellationRequested();
var metadataFile = item.ResolveArgs.GetMetaFileByPath(Path.Combine(item.MetaLocation, XmlFileName));
if (metadataFile != null)
{
var path = metadataFile.FullName;
await XmlParsingResourcePool.WaitAsync(cancellationToken).ConfigureAwait(false);
try
{
new BaseItemXmlParser<Person>(Logger).Fetch((Person)item, path, cancellationToken);
}
finally
{
XmlParsingResourcePool.Release();
}
}
SetLastRefreshed(item, DateTime.UtcNow, providerInfo);
return true;
}
}
}

View File

@ -1,4 +1,5 @@
using MediaBrowser.Controller.Configuration; using MediaBrowser.Common.Net;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.Audio; using MediaBrowser.Controller.Entities.Audio;
using MediaBrowser.Controller.Providers; using MediaBrowser.Controller.Providers;
@ -17,14 +18,16 @@ using System.Xml;
namespace MediaBrowser.Providers.Music namespace MediaBrowser.Providers.Music
{ {
public class ManualFanartAlbumProvider : IImageProvider public class ManualFanartAlbumProvider : IRemoteImageProvider
{ {
private readonly CultureInfo _usCulture = new CultureInfo("en-US"); private readonly CultureInfo _usCulture = new CultureInfo("en-US");
private readonly IServerConfigurationManager _config; private readonly IServerConfigurationManager _config;
private readonly IHttpClient _httpClient;
public ManualFanartAlbumProvider(IServerConfigurationManager config) public ManualFanartAlbumProvider(IServerConfigurationManager config, IHttpClient httpClient)
{ {
_config = config; _config = config;
_httpClient = httpClient;
} }
public string Name public string Name
@ -42,6 +45,15 @@ namespace MediaBrowser.Providers.Music
return item is MusicAlbum; return item is MusicAlbum;
} }
public IEnumerable<ImageType> GetSupportedImages(IHasImages item)
{
return new List<ImageType>
{
ImageType.Primary,
ImageType.Disc
};
}
public async Task<IEnumerable<RemoteImageInfo>> GetImages(IHasImages item, ImageType imageType, CancellationToken cancellationToken) public async Task<IEnumerable<RemoteImageInfo>> GetImages(IHasImages item, ImageType imageType, CancellationToken cancellationToken)
{ {
var images = await GetAllImages(item, cancellationToken).ConfigureAwait(false); var images = await GetAllImages(item, cancellationToken).ConfigureAwait(false);
@ -325,9 +337,19 @@ namespace MediaBrowser.Providers.Music
list.Add(info); list.Add(info);
} }
public int Priority public int Order
{ {
get { return 1; } get { return 0; }
}
public Task<HttpResponseInfo> GetImageResponse(string url, CancellationToken cancellationToken)
{
return _httpClient.GetResponse(new HttpRequestOptions
{
CancellationToken = cancellationToken,
Url = url,
ResourcePool = FanartBaseProvider.FanArtResourcePool
});
} }
} }
} }

View File

@ -1,4 +1,5 @@
using MediaBrowser.Controller.Configuration; using MediaBrowser.Common.Net;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.Audio; using MediaBrowser.Controller.Entities.Audio;
using MediaBrowser.Controller.Providers; using MediaBrowser.Controller.Providers;
@ -17,14 +18,16 @@ using System.Xml;
namespace MediaBrowser.Providers.Music namespace MediaBrowser.Providers.Music
{ {
public class ManualFanartArtistProvider : IImageProvider public class ManualFanartArtistProvider : IRemoteImageProvider
{ {
private readonly CultureInfo _usCulture = new CultureInfo("en-US"); private readonly CultureInfo _usCulture = new CultureInfo("en-US");
private readonly IServerConfigurationManager _config; private readonly IServerConfigurationManager _config;
private readonly IHttpClient _httpClient;
public ManualFanartArtistProvider(IServerConfigurationManager config) public ManualFanartArtistProvider(IServerConfigurationManager config, IHttpClient httpClient)
{ {
_config = config; _config = config;
_httpClient = httpClient;
} }
public string Name public string Name
@ -42,6 +45,18 @@ namespace MediaBrowser.Providers.Music
return item is MusicArtist; return item is MusicArtist;
} }
public IEnumerable<ImageType> GetSupportedImages(IHasImages item)
{
return new List<ImageType>
{
ImageType.Primary,
ImageType.Logo,
ImageType.Art,
ImageType.Banner,
ImageType.Backdrop
};
}
public async Task<IEnumerable<RemoteImageInfo>> GetImages(IHasImages item, ImageType imageType, CancellationToken cancellationToken) public async Task<IEnumerable<RemoteImageInfo>> GetImages(IHasImages item, ImageType imageType, CancellationToken cancellationToken)
{ {
var images = await GetAllImages(item, cancellationToken).ConfigureAwait(false); var images = await GetAllImages(item, cancellationToken).ConfigureAwait(false);
@ -334,9 +349,19 @@ namespace MediaBrowser.Providers.Music
list.Add(info); list.Add(info);
} }
public int Priority public int Order
{ {
get { return 1; } get { return 0; }
}
public Task<HttpResponseInfo> GetImageResponse(string url, CancellationToken cancellationToken)
{
return _httpClient.GetResponse(new HttpRequestOptions
{
CancellationToken = cancellationToken,
Url = url,
ResourcePool = FanartBaseProvider.FanArtResourcePool
});
} }
} }
} }

View File

@ -1,4 +1,5 @@
using MediaBrowser.Controller.Entities; using MediaBrowser.Common.Net;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.Audio; using MediaBrowser.Controller.Entities.Audio;
using MediaBrowser.Controller.Providers; using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Entities; using MediaBrowser.Model.Entities;
@ -11,8 +12,15 @@ using System.Threading.Tasks;
namespace MediaBrowser.Providers.Music namespace MediaBrowser.Providers.Music
{ {
public class ManualLastFmImageProvider : IImageProvider public class ManualLastFmImageProvider : IRemoteImageProvider
{ {
private readonly IHttpClient _httpClient;
public ManualLastFmImageProvider(IHttpClient httpClient)
{
_httpClient = httpClient;
}
public string Name public string Name
{ {
get { return ProviderName; } get { return ProviderName; }
@ -28,6 +36,14 @@ namespace MediaBrowser.Providers.Music
return item is MusicAlbum || item is MusicArtist; return item is MusicAlbum || item is MusicArtist;
} }
public IEnumerable<ImageType> GetSupportedImages(IHasImages item)
{
return new List<ImageType>
{
ImageType.Primary
};
}
public async Task<IEnumerable<RemoteImageInfo>> GetImages(IHasImages item, ImageType imageType, CancellationToken cancellationToken) public async Task<IEnumerable<RemoteImageInfo>> GetImages(IHasImages item, ImageType imageType, CancellationToken cancellationToken)
{ {
var images = await GetAllImages(item, cancellationToken).ConfigureAwait(false); var images = await GetAllImages(item, cancellationToken).ConfigureAwait(false);
@ -72,7 +88,8 @@ namespace MediaBrowser.Providers.Music
var info = new RemoteImageInfo var info = new RemoteImageInfo
{ {
ProviderName = Name, ProviderName = Name,
Url = url Url = url,
Type = ImageType.Primary
}; };
if (string.Equals(size, "mega", StringComparison.OrdinalIgnoreCase)) if (string.Equals(size, "mega", StringComparison.OrdinalIgnoreCase))
@ -95,9 +112,19 @@ namespace MediaBrowser.Providers.Music
return info; return info;
} }
public int Priority public int Order
{ {
get { return 0; } get { return 1; }
}
public Task<HttpResponseInfo> GetImageResponse(string url, CancellationToken cancellationToken)
{
return _httpClient.GetResponse(new HttpRequestOptions
{
CancellationToken = cancellationToken,
Url = url,
ResourcePool = LastfmBaseProvider.LastfmResourcePool
});
} }
} }
} }

View File

@ -1,4 +1,5 @@
using MediaBrowser.Common.Net; using MediaBrowser.Common;
using MediaBrowser.Common.Net;
using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.Audio; using MediaBrowser.Controller.Entities.Audio;
@ -20,11 +21,13 @@ namespace MediaBrowser.Providers.Music
internal static MusicBrainzAlbumProvider Current; internal static MusicBrainzAlbumProvider Current;
private readonly IHttpClient _httpClient; private readonly IHttpClient _httpClient;
private readonly IApplicationHost _appHost;
public MusicBrainzAlbumProvider(ILogManager logManager, IServerConfigurationManager configurationManager, IHttpClient httpClient) public MusicBrainzAlbumProvider(ILogManager logManager, IServerConfigurationManager configurationManager, IHttpClient httpClient, IApplicationHost appHost)
: base(logManager, configurationManager) : base(logManager, configurationManager)
{ {
_httpClient = httpClient; _httpClient = httpClient;
_appHost = appHost;
Current = this; Current = this;
} }
@ -83,7 +86,7 @@ namespace MediaBrowser.Providers.Music
private async Task<ReleaseResult> GetReleaseResult(string albumName, string artistId, CancellationToken cancellationToken) private async Task<ReleaseResult> GetReleaseResult(string albumName, string artistId, CancellationToken cancellationToken)
{ {
var url = string.Format("http://www.musicbrainz.org/ws/2/release/?query=\"{0}\" and arid:{1}", var url = string.Format("http://www.musicbrainz.org/ws/2/release/?query=\"{0}\" AND arid:{1}",
WebUtility.UrlEncode(albumName), WebUtility.UrlEncode(albumName),
artistId); artistId);
@ -94,7 +97,7 @@ namespace MediaBrowser.Providers.Music
private async Task<ReleaseResult> GetReleaseResultByArtistName(string albumName, string artistName, CancellationToken cancellationToken) private async Task<ReleaseResult> GetReleaseResultByArtistName(string albumName, string artistName, CancellationToken cancellationToken)
{ {
var url = string.Format("http://www.musicbrainz.org/ws/2/release/?query=\"{0}\" and artist:\"{1}\"", var url = string.Format("http://www.musicbrainz.org/ws/2/release/?query=\"{0}\" AND artist:\"{1}\"",
WebUtility.UrlEncode(albumName), WebUtility.UrlEncode(albumName),
WebUtility.UrlEncode(artistName)); WebUtility.UrlEncode(artistName));
@ -189,11 +192,13 @@ namespace MediaBrowser.Providers.Music
var doc = new XmlDocument(); var doc = new XmlDocument();
var userAgent = _appHost.Name + "/" + _appHost.ApplicationVersion;
using (var xml = await _httpClient.Get(new HttpRequestOptions using (var xml = await _httpClient.Get(new HttpRequestOptions
{ {
Url = url, Url = url,
CancellationToken = cancellationToken, CancellationToken = cancellationToken,
UserAgent = Environment.MachineName UserAgent = userAgent
}).ConfigureAwait(false)) }).ConfigureAwait(false))
{ {

View File

@ -6,15 +6,17 @@ using MediaBrowser.Controller.Entities.Audio;
using MediaBrowser.Controller.Providers; using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Entities; using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Providers; using MediaBrowser.Model.Providers;
using MediaBrowser.Providers.Genres;
using MediaBrowser.Providers.ImagesByName;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace MediaBrowser.Providers.ImagesByName namespace MediaBrowser.Providers.MusicGenres
{ {
public class MusicGenresManualImageProvider : IImageProvider public class MusicGenreImageProvider : IRemoteImageProvider
{ {
private readonly IServerConfigurationManager _config; private readonly IServerConfigurationManager _config;
private readonly IHttpClient _httpClient; private readonly IHttpClient _httpClient;
@ -22,7 +24,7 @@ namespace MediaBrowser.Providers.ImagesByName
private readonly SemaphoreSlim _listResourcePool = new SemaphoreSlim(1, 1); private readonly SemaphoreSlim _listResourcePool = new SemaphoreSlim(1, 1);
public MusicGenresManualImageProvider(IServerConfigurationManager config, IHttpClient httpClient, IFileSystem fileSystem) public MusicGenreImageProvider(IServerConfigurationManager config, IHttpClient httpClient, IFileSystem fileSystem)
{ {
_config = config; _config = config;
_httpClient = httpClient; _httpClient = httpClient;
@ -44,6 +46,15 @@ namespace MediaBrowser.Providers.ImagesByName
return item is MusicGenre; return item is MusicGenre;
} }
public IEnumerable<ImageType> GetSupportedImages(IHasImages item)
{
return new List<ImageType>
{
ImageType.Primary,
ImageType.Thumb
};
}
public Task<IEnumerable<RemoteImageInfo>> GetImages(IHasImages item, ImageType imageType, CancellationToken cancellationToken) public Task<IEnumerable<RemoteImageInfo>> GetImages(IHasImages item, ImageType imageType, CancellationToken cancellationToken)
{ {
return GetImages(item, imageType == ImageType.Primary, imageType == ImageType.Thumb, cancellationToken); return GetImages(item, imageType == ImageType.Primary, imageType == ImageType.Thumb, cancellationToken);
@ -121,9 +132,19 @@ namespace MediaBrowser.Providers.ImagesByName
return ImageUtils.EnsureList(url, file, _httpClient, _fileSystem, _listResourcePool, cancellationToken); return ImageUtils.EnsureList(url, file, _httpClient, _fileSystem, _listResourcePool, cancellationToken);
} }
public int Priority public int Order
{ {
get { return 0; } get { return 0; }
} }
public Task<HttpResponseInfo> GetImageResponse(string url, CancellationToken cancellationToken)
{
return _httpClient.GetResponse(new HttpRequestOptions
{
CancellationToken = cancellationToken,
Url = url,
ResourcePool = GenreImageProvider.ImageDownloadResourcePool
});
}
} }
} }

View File

@ -0,0 +1,42 @@
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Entities.Audio;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Logging;
using MediaBrowser.Providers.Manager;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
namespace MediaBrowser.Providers.MusicGenres
{
public class MusicGenreMetadataService : MetadataService<MusicGenre>
{
private readonly ILibraryManager _libraryManager;
public MusicGenreMetadataService(IServerConfigurationManager serverConfigurationManager, ILogger logger, IProviderManager providerManager, IProviderRepository providerRepo, ILibraryManager libraryManager)
: base(serverConfigurationManager, logger, providerManager, providerRepo)
{
_libraryManager = libraryManager;
}
/// <summary>
/// Merges the specified source.
/// </summary>
/// <param name="source">The source.</param>
/// <param name="target">The target.</param>
/// <param name="lockedFields">The locked fields.</param>
/// <param name="replaceData">if set to <c>true</c> [replace data].</param>
/// <param name="mergeMetadataSettings">if set to <c>true</c> [merge metadata settings].</param>
protected override void MergeData(MusicGenre source, MusicGenre target, List<MetadataFields> lockedFields, bool replaceData, bool mergeMetadataSettings)
{
ProviderUtils.MergeBaseItemData(source, target, lockedFields, replaceData, mergeMetadataSettings);
}
protected override Task SaveItem(MusicGenre item, ItemUpdateType reason, CancellationToken cancellationToken)
{
return _libraryManager.UpdateItem(item, reason, cancellationToken);
}
}
}

View File

@ -1,26 +1,30 @@
using MediaBrowser.Controller.Configuration; using MediaBrowser.Common.Net;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Providers; using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Entities; using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Providers; using MediaBrowser.Model.Providers;
using MediaBrowser.Model.Serialization; using MediaBrowser.Model.Serialization;
using MediaBrowser.Providers.Movies;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace MediaBrowser.Providers.Movies namespace MediaBrowser.Providers.People
{ {
public class ManualMovieDbPersonImageProvider : IImageProvider public class MovieDbPersonImageProvider : IRemoteImageProvider
{ {
private readonly IServerConfigurationManager _config; private readonly IServerConfigurationManager _config;
private readonly IJsonSerializer _jsonSerializer; private readonly IJsonSerializer _jsonSerializer;
private readonly IHttpClient _httpClient;
public ManualMovieDbPersonImageProvider(IServerConfigurationManager config, IJsonSerializer jsonSerializer) public MovieDbPersonImageProvider(IServerConfigurationManager config, IJsonSerializer jsonSerializer, IHttpClient httpClient)
{ {
_config = config; _config = config;
_jsonSerializer = jsonSerializer; _jsonSerializer = jsonSerializer;
_httpClient = httpClient;
} }
public string Name public string Name
@ -38,6 +42,14 @@ namespace MediaBrowser.Providers.Movies
return item is Person; return item is Person;
} }
public IEnumerable<ImageType> GetSupportedImages(IHasImages item)
{
return new List<ImageType>
{
ImageType.Primary
};
}
public async Task<IEnumerable<RemoteImageInfo>> GetImages(IHasImages item, ImageType imageType, CancellationToken cancellationToken) public async Task<IEnumerable<RemoteImageInfo>> GetImages(IHasImages item, ImageType imageType, CancellationToken cancellationToken)
{ {
var images = await GetAllImages(item, cancellationToken).ConfigureAwait(false); var images = await GetAllImages(item, cancellationToken).ConfigureAwait(false);
@ -120,9 +132,19 @@ namespace MediaBrowser.Providers.Movies
return profile.iso_639_1 == null ? null : profile.iso_639_1.ToString(); return profile.iso_639_1 == null ? null : profile.iso_639_1.ToString();
} }
public int Priority public int Order
{ {
get { return 0; } get { return 0; }
} }
public Task<HttpResponseInfo> GetImageResponse(string url, CancellationToken cancellationToken)
{
return _httpClient.GetResponse(new HttpRequestOptions
{
CancellationToken = cancellationToken,
Url = url,
ResourcePool = MovieDbProvider.Current.MovieDbResourcePool
});
}
} }
} }

View File

@ -0,0 +1,289 @@
using MediaBrowser.Common.Configuration;
using MediaBrowser.Common.Extensions;
using MediaBrowser.Common.IO;
using MediaBrowser.Common.Net;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Serialization;
using MediaBrowser.Providers.Movies;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Net;
using System.Threading;
using System.Threading.Tasks;
namespace MediaBrowser.Providers.People
{
public class MovieDbPersonProvider : IRemoteMetadataProvider<Person>
{
const string DataFileName = "info.json";
internal static MovieDbPersonProvider Current { get; private set; }
private readonly IJsonSerializer _jsonSerializer;
private readonly IFileSystem _fileSystem;
private readonly IServerConfigurationManager _configurationManager;
public MovieDbPersonProvider(IFileSystem fileSystem, IServerConfigurationManager configurationManager, IJsonSerializer jsonSerializer)
{
_fileSystem = fileSystem;
_configurationManager = configurationManager;
_jsonSerializer = jsonSerializer;
Current = this;
}
public string Name
{
get { return "TheMovieDb"; }
}
public async Task<MetadataResult<Person>> GetMetadata(ItemId id, CancellationToken cancellationToken)
{
var tmdbId = id.GetProviderId(MetadataProviders.Tmdb);
// We don't already have an Id, need to fetch it
if (string.IsNullOrEmpty(tmdbId))
{
tmdbId = await GetTmdbId(id.Name, cancellationToken).ConfigureAwait(false);
}
var result = new MetadataResult<Person>();
if (!string.IsNullOrEmpty(tmdbId))
{
await EnsurePersonInfo(tmdbId, cancellationToken).ConfigureAwait(false);
var dataFilePath = GetPersonDataFilePath(_configurationManager.ApplicationPaths, tmdbId);
var info = _jsonSerializer.DeserializeFromFile<PersonResult>(dataFilePath);
var item = new Person();
result.HasMetadata = true;
item.Name = info.name;
item.HomePageUrl = info.homepage;
item.PlaceOfBirth = info.place_of_birth;
item.Overview = info.biography;
DateTime date;
if (DateTime.TryParseExact(info.birthday, "yyyy-MM-dd", new CultureInfo("en-US"), DateTimeStyles.None, out date))
{
item.PremiereDate = date.ToUniversalTime();
}
if (DateTime.TryParseExact(info.deathday, "yyyy-MM-dd", new CultureInfo("en-US"), DateTimeStyles.None, out date))
{
item.EndDate = date.ToUniversalTime();
}
item.SetProviderId(MetadataProviders.Tmdb, info.id.ToString(_usCulture));
if (!string.IsNullOrEmpty(info.imdb_id))
{
item.SetProviderId(MetadataProviders.Imdb, info.imdb_id);
}
result.Item = item;
}
return result;
}
private readonly CultureInfo _usCulture = new CultureInfo("en-US");
/// <summary>
/// Gets the TMDB id.
/// </summary>
/// <param name="name">The name.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Task{System.String}.</returns>
private async Task<string> GetTmdbId(string name, CancellationToken cancellationToken)
{
string url = string.Format(@"http://api.themoviedb.org/3/search/person?api_key={1}&query={0}", WebUtility.UrlEncode(name), MovieDbProvider.ApiKey);
PersonSearchResults searchResult = null;
using (var json = await MovieDbProvider.Current.GetMovieDbResponse(new HttpRequestOptions
{
Url = url,
CancellationToken = cancellationToken,
AcceptHeader = MovieDbProvider.AcceptHeader
}).ConfigureAwait(false))
{
searchResult = _jsonSerializer.DeserializeFromStream<PersonSearchResults>(json);
}
return searchResult != null && searchResult.Total_Results > 0 ? searchResult.Results[0].Id.ToString(_usCulture) : null;
}
internal async Task EnsurePersonInfo(string id, CancellationToken cancellationToken)
{
var dataFilePath = GetPersonDataFilePath(_configurationManager.ApplicationPaths, id);
var fileInfo = _fileSystem.GetFileSystemInfo(dataFilePath);
if (fileInfo.Exists && (DateTime.UtcNow - _fileSystem.GetLastWriteTimeUtc(fileInfo)).TotalDays <= 7)
{
return;
}
var url = string.Format(@"http://api.themoviedb.org/3/person/{1}?api_key={0}&append_to_response=credits,images", MovieDbProvider.ApiKey, id);
using (var json = await MovieDbProvider.Current.GetMovieDbResponse(new HttpRequestOptions
{
Url = url,
CancellationToken = cancellationToken,
AcceptHeader = MovieDbProvider.AcceptHeader
}).ConfigureAwait(false))
{
Directory.CreateDirectory(Path.GetDirectoryName(dataFilePath));
using (var fs = _fileSystem.GetFileStream(dataFilePath, FileMode.Create, FileAccess.Write, FileShare.Read, true))
{
await json.CopyToAsync(fs).ConfigureAwait(false);
}
}
}
private static string GetPersonDataPath(IApplicationPaths appPaths, string tmdbId)
{
var letter = tmdbId.GetMD5().ToString().Substring(0, 1);
return Path.Combine(GetPersonsDataPath(appPaths), letter, tmdbId);
}
internal static string GetPersonDataFilePath(IApplicationPaths appPaths, string tmdbId)
{
return Path.Combine(GetPersonDataPath(appPaths, tmdbId), DataFileName);
}
private static string GetPersonsDataPath(IApplicationPaths appPaths)
{
return Path.Combine(appPaths.DataPath, "tmdb-people");
}
#region Result Objects
/// <summary>
/// Class PersonSearchResult
/// </summary>
public class PersonSearchResult
{
/// <summary>
/// Gets or sets a value indicating whether this <see cref="MovieDbPersonProvider.PersonSearchResult" /> is adult.
/// </summary>
/// <value><c>true</c> if adult; otherwise, <c>false</c>.</value>
public bool Adult { get; set; }
/// <summary>
/// Gets or sets the id.
/// </summary>
/// <value>The id.</value>
public int Id { get; set; }
/// <summary>
/// Gets or sets the name.
/// </summary>
/// <value>The name.</value>
public string Name { get; set; }
/// <summary>
/// Gets or sets the profile_ path.
/// </summary>
/// <value>The profile_ path.</value>
public string Profile_Path { get; set; }
}
/// <summary>
/// Class PersonSearchResults
/// </summary>
public class PersonSearchResults
{
/// <summary>
/// Gets or sets the page.
/// </summary>
/// <value>The page.</value>
public int Page { get; set; }
/// <summary>
/// Gets or sets the results.
/// </summary>
/// <value>The results.</value>
public List<MovieDbPersonProvider.PersonSearchResult> Results { get; set; }
/// <summary>
/// Gets or sets the total_ pages.
/// </summary>
/// <value>The total_ pages.</value>
public int Total_Pages { get; set; }
/// <summary>
/// Gets or sets the total_ results.
/// </summary>
/// <value>The total_ results.</value>
public int Total_Results { get; set; }
}
public class Cast
{
public int id { get; set; }
public string title { get; set; }
public string character { get; set; }
public string original_title { get; set; }
public string poster_path { get; set; }
public string release_date { get; set; }
public bool adult { get; set; }
}
public class Crew
{
public int id { get; set; }
public string title { get; set; }
public string original_title { get; set; }
public string department { get; set; }
public string job { get; set; }
public string poster_path { get; set; }
public string release_date { get; set; }
public bool adult { get; set; }
}
public class Credits
{
public List<Cast> cast { get; set; }
public List<Crew> crew { get; set; }
}
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; }
}
public class Images
{
public List<Profile> profiles { get; set; }
}
public class PersonResult
{
public bool adult { get; set; }
public List<object> also_known_as { get; set; }
public string biography { get; set; }
public string birthday { get; set; }
public string deathday { get; set; }
public string homepage { get; set; }
public int id { get; set; }
public string imdb_id { get; set; }
public string name { get; set; }
public string place_of_birth { get; set; }
public double popularity { get; set; }
public string profile_path { get; set; }
public Credits credits { get; set; }
public Images images { get; set; }
}
#endregion
}
}

View File

@ -0,0 +1,47 @@
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Logging;
using MediaBrowser.Providers.Manager;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
namespace MediaBrowser.Providers.People
{
public class PersonMetadataService : MetadataService<Person>
{
private readonly ILibraryManager _libraryManager;
public PersonMetadataService(IServerConfigurationManager serverConfigurationManager, ILogger logger, IProviderManager providerManager, IProviderRepository providerRepo, ILibraryManager libraryManager)
: base(serverConfigurationManager, logger, providerManager, providerRepo)
{
_libraryManager = libraryManager;
}
/// <summary>
/// Merges the specified source.
/// </summary>
/// <param name="source">The source.</param>
/// <param name="target">The target.</param>
/// <param name="lockedFields">The locked fields.</param>
/// <param name="replaceData">if set to <c>true</c> [replace data].</param>
/// <param name="mergeMetadataSettings">if set to <c>true</c> [merge metadata settings].</param>
protected override void MergeData(Person source, Person target, List<MetadataFields> lockedFields, bool replaceData, bool mergeMetadataSettings)
{
ProviderUtils.MergeBaseItemData(source, target, lockedFields, replaceData, mergeMetadataSettings);
if (replaceData || string.IsNullOrEmpty(target.PlaceOfBirth))
{
target.PlaceOfBirth = source.PlaceOfBirth;
}
}
protected override Task SaveItem(Person item, ItemUpdateType reason, CancellationToken cancellationToken)
{
return _libraryManager.UpdateItem(item, reason, cancellationToken);
}
}
}

View File

@ -0,0 +1,59 @@
using MediaBrowser.Common.IO;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Logging;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
namespace MediaBrowser.Providers.People
{
public class PersonXmlProvider : BaseXmlProvider, ILocalMetadataProvider<Person>
{
private readonly ILogger _logger;
public PersonXmlProvider(IFileSystem fileSystem, ILogger logger)
: base(fileSystem)
{
_logger = logger;
}
public async Task<MetadataResult<Person>> GetMetadata(string path, CancellationToken cancellationToken)
{
path = GetXmlPath(path);
var result = new MetadataResult<Person>();
await XmlParsingResourcePool.WaitAsync(cancellationToken).ConfigureAwait(false);
try
{
var person = new Person();
new BaseItemXmlParser<Person>(_logger).Fetch(person, path, cancellationToken);
result.HasMetadata = true;
result.Item = person;
}
catch (FileNotFoundException)
{
result.HasMetadata = false;
}
finally
{
XmlParsingResourcePool.Release();
}
return result;
}
public string Name
{
get { return "Media Browser Xml"; }
}
protected override string GetXmlPath(string path)
{
return Path.Combine(path, "person.xml");
}
}
}

View File

@ -1,10 +1,12 @@
using MediaBrowser.Controller.Configuration; using MediaBrowser.Common.Net;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.TV; using MediaBrowser.Controller.Entities.TV;
using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Providers; using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Entities; using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Providers; using MediaBrowser.Model.Providers;
using MediaBrowser.Providers.TV;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
@ -14,17 +16,19 @@ using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.Xml; using System.Xml;
namespace MediaBrowser.Providers.TV namespace MediaBrowser.Providers.People
{ {
public class ManualTvdbPersonImageProvider : IImageProvider public class TvdbPersonImageProvider : IRemoteImageProvider
{ {
private readonly IServerConfigurationManager _config; private readonly IServerConfigurationManager _config;
private readonly ILibraryManager _library; private readonly ILibraryManager _library;
private readonly IHttpClient _httpClient;
public ManualTvdbPersonImageProvider(IServerConfigurationManager config, ILibraryManager library) public TvdbPersonImageProvider(IServerConfigurationManager config, ILibraryManager library, IHttpClient httpClient)
{ {
_config = config; _config = config;
_library = library; _library = library;
_httpClient = httpClient;
} }
public string Name public string Name
@ -42,6 +46,14 @@ namespace MediaBrowser.Providers.TV
return item is Person; return item is Person;
} }
public IEnumerable<ImageType> GetSupportedImages(IHasImages item)
{
return new List<ImageType>
{
ImageType.Primary
};
}
public async Task<IEnumerable<RemoteImageInfo>> GetImages(IHasImages item, ImageType imageType, CancellationToken cancellationToken) public async Task<IEnumerable<RemoteImageInfo>> GetImages(IHasImages item, ImageType imageType, CancellationToken cancellationToken)
{ {
var images = await GetAllImages(item, cancellationToken).ConfigureAwait(false); var images = await GetAllImages(item, cancellationToken).ConfigureAwait(false);
@ -184,9 +196,19 @@ namespace MediaBrowser.Providers.TV
return null; return null;
} }
public int Priority public int Order
{ {
get { return 0; } get { return 1; }
}
public Task<HttpResponseInfo> GetImageResponse(string url, CancellationToken cancellationToken)
{
return _httpClient.GetResponse(new HttpRequestOptions
{
CancellationToken = cancellationToken,
Url = url,
ResourcePool = TvdbSeriesProvider.Current.TvDbResourcePool
});
} }
} }
} }

View File

@ -0,0 +1,126 @@
using MediaBrowser.Controller.Entities;
using MediaBrowser.Model.Entities;
using System.Collections.Generic;
namespace MediaBrowser.Providers
{
public static class ProviderUtils
{
public static void MergeBaseItemData(BaseItem source, BaseItem target, List<MetadataFields> lockedFields, bool replaceData, bool mergeMetadataSettings)
{
if (!lockedFields.Contains(MetadataFields.Name))
{
if (replaceData || string.IsNullOrEmpty(target.Name))
{
target.Name = source.Name;
}
}
if (replaceData || !target.CommunityRating.HasValue)
{
target.CommunityRating = source.CommunityRating;
}
if (replaceData || !target.EndDate.HasValue)
{
target.EndDate = source.EndDate;
}
if (!lockedFields.Contains(MetadataFields.Genres))
{
if (replaceData || target.Genres.Count == 0)
{
target.Genres = source.Genres;
}
}
if (replaceData || string.IsNullOrEmpty(target.HomePageUrl))
{
target.HomePageUrl = source.HomePageUrl;
}
if (replaceData || !target.IndexNumber.HasValue)
{
target.IndexNumber = source.IndexNumber;
}
if (!lockedFields.Contains(MetadataFields.OfficialRating))
{
if (replaceData || string.IsNullOrEmpty(target.OfficialRating))
{
target.OfficialRating = source.OfficialRating;
}
}
if (replaceData || string.IsNullOrEmpty(target.OfficialRatingDescription))
{
target.OfficialRatingDescription = source.OfficialRatingDescription;
}
if (!lockedFields.Contains(MetadataFields.Overview))
{
if (replaceData || string.IsNullOrEmpty(target.Overview))
{
target.Overview = source.Overview;
}
}
if (replaceData || !target.ParentIndexNumber.HasValue)
{
target.ParentIndexNumber = source.ParentIndexNumber;
}
if (!lockedFields.Contains(MetadataFields.Cast))
{
if (replaceData || target.People.Count == 0)
{
target.People = source.People;
}
}
if (replaceData || !target.PremiereDate.HasValue)
{
target.PremiereDate = source.PremiereDate;
}
if (replaceData || !target.ProductionYear.HasValue)
{
target.ProductionYear = source.ProductionYear;
}
if (!lockedFields.Contains(MetadataFields.Runtime))
{
if (replaceData || !target.RunTimeTicks.HasValue)
{
target.RunTimeTicks = source.RunTimeTicks;
}
}
if (!lockedFields.Contains(MetadataFields.Studios))
{
if (replaceData || target.Studios.Count == 0)
{
target.Studios = source.Studios;
}
}
if (replaceData || !target.VoteCount.HasValue)
{
target.VoteCount = source.VoteCount;
}
foreach (var id in source.ProviderIds)
{
target.ProviderIds[id.Key] = id.Value;
}
if (mergeMetadataSettings)
{
target.ForcedSortName = source.ForcedSortName;
target.LockedFields = source.LockedFields;
target.DontFetchMeta = source.DontFetchMeta;
target.DisplayMediaType = source.DisplayMediaType;
}
}
}
}

View File

@ -0,0 +1,41 @@
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Logging;
using MediaBrowser.Providers.Manager;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
namespace MediaBrowser.Providers.Studios
{
public class StudioMetadataService : MetadataService<Studio>
{
private readonly ILibraryManager _libraryManager;
public StudioMetadataService(IServerConfigurationManager serverConfigurationManager, ILogger logger, IProviderManager providerManager, IProviderRepository providerRepo, ILibraryManager libraryManager)
: base(serverConfigurationManager, logger, providerManager, providerRepo)
{
_libraryManager = libraryManager;
}
/// <summary>
/// Merges the specified source.
/// </summary>
/// <param name="source">The source.</param>
/// <param name="target">The target.</param>
/// <param name="lockedFields">The locked fields.</param>
/// <param name="replaceData">if set to <c>true</c> [replace data].</param>
protected override void MergeData(Studio source, Studio target, List<MetadataFields> lockedFields, bool replaceData, bool mergeMetadataSettings)
{
ProviderUtils.MergeBaseItemData(source, target, lockedFields, replaceData, mergeMetadataSettings);
}
protected override Task SaveItem(Studio item, ItemUpdateType reason, CancellationToken cancellationToken)
{
return _libraryManager.UpdateItem(item, reason, cancellationToken);
}
}
}

View File

@ -5,15 +5,17 @@ using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Providers; using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Entities; using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Providers; using MediaBrowser.Model.Providers;
using MediaBrowser.Providers.Genres;
using MediaBrowser.Providers.ImagesByName;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace MediaBrowser.Providers.ImagesByName namespace MediaBrowser.Providers.Studios
{ {
public class StudiosManualImageProvider : IImageProvider public class StudiosImageProvider : IRemoteImageProvider
{ {
private readonly IServerConfigurationManager _config; private readonly IServerConfigurationManager _config;
private readonly IHttpClient _httpClient; private readonly IHttpClient _httpClient;
@ -21,7 +23,7 @@ namespace MediaBrowser.Providers.ImagesByName
private readonly SemaphoreSlim _listResourcePool = new SemaphoreSlim(1, 1); private readonly SemaphoreSlim _listResourcePool = new SemaphoreSlim(1, 1);
public StudiosManualImageProvider(IServerConfigurationManager config, IHttpClient httpClient, IFileSystem fileSystem) public StudiosImageProvider(IServerConfigurationManager config, IHttpClient httpClient, IFileSystem fileSystem)
{ {
_config = config; _config = config;
_httpClient = httpClient; _httpClient = httpClient;
@ -43,6 +45,15 @@ namespace MediaBrowser.Providers.ImagesByName
return item is Studio; return item is Studio;
} }
public IEnumerable<ImageType> GetSupportedImages(IHasImages item)
{
return new List<ImageType>
{
ImageType.Primary,
ImageType.Thumb
};
}
public Task<IEnumerable<RemoteImageInfo>> GetImages(IHasImages item, ImageType imageType, CancellationToken cancellationToken) public Task<IEnumerable<RemoteImageInfo>> GetImages(IHasImages item, ImageType imageType, CancellationToken cancellationToken)
{ {
return GetImages(item, imageType == ImageType.Primary, imageType == ImageType.Thumb, cancellationToken); return GetImages(item, imageType == ImageType.Primary, imageType == ImageType.Thumb, cancellationToken);
@ -120,9 +131,19 @@ namespace MediaBrowser.Providers.ImagesByName
return ImageUtils.EnsureList(url, file, _httpClient, _fileSystem, _listResourcePool, cancellationToken); return ImageUtils.EnsureList(url, file, _httpClient, _fileSystem, _listResourcePool, cancellationToken);
} }
public int Priority public int Order
{ {
get { return 0; } get { return 0; }
} }
public Task<HttpResponseInfo> GetImageResponse(string url, CancellationToken cancellationToken)
{
return _httpClient.GetResponse(new HttpRequestOptions
{
CancellationToken = cancellationToken,
Url = url,
ResourcePool = GenreImageProvider.ImageDownloadResourcePool
});
}
} }
} }

View File

@ -1,4 +1,5 @@
using MediaBrowser.Controller.Configuration; using MediaBrowser.Common.Net;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.TV; using MediaBrowser.Controller.Entities.TV;
using MediaBrowser.Controller.Providers; using MediaBrowser.Controller.Providers;
@ -17,14 +18,16 @@ using System.Xml;
namespace MediaBrowser.Providers.TV namespace MediaBrowser.Providers.TV
{ {
public class ManualFanartSeasonImageProvider : IImageProvider public class ManualFanartSeasonImageProvider : IRemoteImageProvider
{ {
private readonly CultureInfo _usCulture = new CultureInfo("en-US"); private readonly CultureInfo _usCulture = new CultureInfo("en-US");
private readonly IServerConfigurationManager _config; private readonly IServerConfigurationManager _config;
private readonly IHttpClient _httpClient;
public ManualFanartSeasonImageProvider(IServerConfigurationManager config) public ManualFanartSeasonImageProvider(IServerConfigurationManager config, IHttpClient httpClient)
{ {
_config = config; _config = config;
_httpClient = httpClient;
} }
public string Name public string Name
@ -42,6 +45,15 @@ namespace MediaBrowser.Providers.TV
return item is Season; return item is Season;
} }
public IEnumerable<ImageType> GetSupportedImages(IHasImages item)
{
return new List<ImageType>
{
ImageType.Backdrop,
ImageType.Thumb
};
}
public async Task<IEnumerable<RemoteImageInfo>> GetImages(IHasImages item, ImageType imageType, CancellationToken cancellationToken) public async Task<IEnumerable<RemoteImageInfo>> GetImages(IHasImages item, ImageType imageType, CancellationToken cancellationToken)
{ {
var images = await GetAllImages(item, cancellationToken).ConfigureAwait(false); var images = await GetAllImages(item, cancellationToken).ConfigureAwait(false);
@ -245,9 +257,19 @@ namespace MediaBrowser.Providers.TV
} }
} }
public int Priority public int Order
{ {
get { return 0; } get { return 1; }
}
public Task<HttpResponseInfo> GetImageResponse(string url, CancellationToken cancellationToken)
{
return _httpClient.GetResponse(new HttpRequestOptions
{
CancellationToken = cancellationToken,
Url = url,
ResourcePool = FanartBaseProvider.FanArtResourcePool
});
} }
} }
} }

View File

@ -1,4 +1,5 @@
using MediaBrowser.Controller.Configuration; using MediaBrowser.Common.Net;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.TV; using MediaBrowser.Controller.Entities.TV;
using MediaBrowser.Controller.Providers; using MediaBrowser.Controller.Providers;
@ -17,14 +18,16 @@ using System.Xml;
namespace MediaBrowser.Providers.TV namespace MediaBrowser.Providers.TV
{ {
public class ManualFanartSeriesImageProvider : IImageProvider public class ManualFanartSeriesImageProvider : IRemoteImageProvider
{ {
private readonly CultureInfo _usCulture = new CultureInfo("en-US"); private readonly CultureInfo _usCulture = new CultureInfo("en-US");
private readonly IServerConfigurationManager _config; private readonly IServerConfigurationManager _config;
private readonly IHttpClient _httpClient;
public ManualFanartSeriesImageProvider(IServerConfigurationManager config) public ManualFanartSeriesImageProvider(IServerConfigurationManager config, IHttpClient httpClient)
{ {
_config = config; _config = config;
_httpClient = httpClient;
} }
public string Name public string Name
@ -42,6 +45,19 @@ namespace MediaBrowser.Providers.TV
return item is Series; return item is Series;
} }
public IEnumerable<ImageType> GetSupportedImages(IHasImages item)
{
return new List<ImageType>
{
ImageType.Primary,
ImageType.Thumb,
ImageType.Art,
ImageType.Logo,
ImageType.Backdrop,
ImageType.Banner
};
}
public async Task<IEnumerable<RemoteImageInfo>> GetImages(IHasImages item, ImageType imageType, CancellationToken cancellationToken) public async Task<IEnumerable<RemoteImageInfo>> GetImages(IHasImages item, ImageType imageType, CancellationToken cancellationToken)
{ {
var images = await GetAllImages(item, cancellationToken).ConfigureAwait(false); var images = await GetAllImages(item, cancellationToken).ConfigureAwait(false);
@ -302,9 +318,19 @@ namespace MediaBrowser.Providers.TV
} }
} }
public int Priority public int Order
{ {
get { return 0; } get { return 1; }
}
public Task<HttpResponseInfo> GetImageResponse(string url, CancellationToken cancellationToken)
{
return _httpClient.GetResponse(new HttpRequestOptions
{
CancellationToken = cancellationToken,
Url = url,
ResourcePool = FanartBaseProvider.FanArtResourcePool
});
} }
} }
} }

View File

@ -1,4 +1,5 @@
using MediaBrowser.Controller.Configuration; using MediaBrowser.Common.Net;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.TV; using MediaBrowser.Controller.Entities.TV;
using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Library;
@ -16,14 +17,16 @@ using System.Xml;
namespace MediaBrowser.Providers.TV namespace MediaBrowser.Providers.TV
{ {
public class ManualTvdbEpisodeImageProvider : IImageProvider public class ManualTvdbEpisodeImageProvider : IRemoteImageProvider
{ {
private readonly IServerConfigurationManager _config; private readonly IServerConfigurationManager _config;
private readonly CultureInfo _usCulture = new CultureInfo("en-US"); private readonly CultureInfo _usCulture = new CultureInfo("en-US");
private readonly IHttpClient _httpClient;
public ManualTvdbEpisodeImageProvider(IServerConfigurationManager config) public ManualTvdbEpisodeImageProvider(IServerConfigurationManager config, IHttpClient httpClient)
{ {
_config = config; _config = config;
_httpClient = httpClient;
} }
public string Name public string Name
@ -36,6 +39,14 @@ namespace MediaBrowser.Providers.TV
return item is Episode; return item is Episode;
} }
public IEnumerable<ImageType> GetSupportedImages(IHasImages item)
{
return new List<ImageType>
{
ImageType.Primary
};
}
public async Task<IEnumerable<RemoteImageInfo>> GetImages(IHasImages item, ImageType imageType, CancellationToken cancellationToken) public async Task<IEnumerable<RemoteImageInfo>> GetImages(IHasImages item, ImageType imageType, CancellationToken cancellationToken)
{ {
var images = await GetAllImages(item, cancellationToken).ConfigureAwait(false); var images = await GetAllImages(item, cancellationToken).ConfigureAwait(false);
@ -161,9 +172,19 @@ namespace MediaBrowser.Providers.TV
}; };
} }
public int Priority public int Order
{ {
get { return 0; } get { return 0; }
} }
public Task<HttpResponseInfo> GetImageResponse(string url, CancellationToken cancellationToken)
{
return _httpClient.GetResponse(new HttpRequestOptions
{
CancellationToken = cancellationToken,
Url = url,
ResourcePool = TvdbSeriesProvider.Current.TvDbResourcePool
});
}
} }
} }

View File

@ -1,4 +1,5 @@
using MediaBrowser.Controller.Configuration; using MediaBrowser.Common.Net;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.TV; using MediaBrowser.Controller.Entities.TV;
using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Library;
@ -18,14 +19,16 @@ using System.Xml;
namespace MediaBrowser.Providers.TV namespace MediaBrowser.Providers.TV
{ {
public class ManualTvdbSeasonImageProvider : IImageProvider public class ManualTvdbSeasonImageProvider : IRemoteImageProvider
{ {
private readonly IServerConfigurationManager _config; private readonly IServerConfigurationManager _config;
private readonly CultureInfo _usCulture = new CultureInfo("en-US"); private readonly CultureInfo _usCulture = new CultureInfo("en-US");
private readonly IHttpClient _httpClient;
public ManualTvdbSeasonImageProvider(IServerConfigurationManager config) public ManualTvdbSeasonImageProvider(IServerConfigurationManager config, IHttpClient httpClient)
{ {
_config = config; _config = config;
_httpClient = httpClient;
} }
public string Name public string Name
@ -43,6 +46,16 @@ namespace MediaBrowser.Providers.TV
return item is Season; return item is Season;
} }
public IEnumerable<ImageType> GetSupportedImages(IHasImages item)
{
return new List<ImageType>
{
ImageType.Primary,
ImageType.Banner,
ImageType.Backdrop
};
}
public async Task<IEnumerable<RemoteImageInfo>> GetImages(IHasImages item, ImageType imageType, CancellationToken cancellationToken) public async Task<IEnumerable<RemoteImageInfo>> GetImages(IHasImages item, ImageType imageType, CancellationToken cancellationToken)
{ {
var images = await GetAllImages(item, cancellationToken).ConfigureAwait(false); var images = await GetAllImages(item, cancellationToken).ConfigureAwait(false);
@ -308,9 +321,19 @@ namespace MediaBrowser.Providers.TV
} }
public int Priority public int Order
{ {
get { return 1; } get { return 0; }
}
public Task<HttpResponseInfo> GetImageResponse(string url, CancellationToken cancellationToken)
{
return _httpClient.GetResponse(new HttpRequestOptions
{
CancellationToken = cancellationToken,
Url = url,
ResourcePool = TvdbSeriesProvider.Current.TvDbResourcePool
});
} }
} }
} }

View File

@ -1,4 +1,5 @@
using MediaBrowser.Controller.Configuration; using MediaBrowser.Common.Net;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.TV; using MediaBrowser.Controller.Entities.TV;
using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Library;
@ -18,14 +19,16 @@ using System.Xml;
namespace MediaBrowser.Providers.TV namespace MediaBrowser.Providers.TV
{ {
public class ManualTvdbSeriesImageProvider : IImageProvider public class ManualTvdbSeriesImageProvider : IRemoteImageProvider
{ {
private readonly IServerConfigurationManager _config; private readonly IServerConfigurationManager _config;
private readonly IHttpClient _httpClient;
private readonly CultureInfo _usCulture = new CultureInfo("en-US"); private readonly CultureInfo _usCulture = new CultureInfo("en-US");
public ManualTvdbSeriesImageProvider(IServerConfigurationManager config) public ManualTvdbSeriesImageProvider(IServerConfigurationManager config, IHttpClient httpClient)
{ {
_config = config; _config = config;
_httpClient = httpClient;
} }
public string Name public string Name
@ -43,6 +46,16 @@ namespace MediaBrowser.Providers.TV
return item is Series; return item is Series;
} }
public IEnumerable<ImageType> GetSupportedImages(IHasImages item)
{
return new List<ImageType>
{
ImageType.Primary,
ImageType.Banner,
ImageType.Backdrop
};
}
public async Task<IEnumerable<RemoteImageInfo>> GetImages(IHasImages item, ImageType imageType, CancellationToken cancellationToken) public async Task<IEnumerable<RemoteImageInfo>> GetImages(IHasImages item, ImageType imageType, CancellationToken cancellationToken)
{ {
var images = await GetAllImages(item, cancellationToken).ConfigureAwait(false); var images = await GetAllImages(item, cancellationToken).ConfigureAwait(false);
@ -304,9 +317,19 @@ namespace MediaBrowser.Providers.TV
} }
public int Priority public int Order
{ {
get { return 1; } get { return 0; }
}
public Task<HttpResponseInfo> GetImageResponse(string url, CancellationToken cancellationToken)
{
return _httpClient.GetResponse(new HttpRequestOptions
{
CancellationToken = cancellationToken,
Url = url,
ResourcePool = TvdbSeriesProvider.Current.TvDbResourcePool
});
} }
} }
} }

View File

@ -1,98 +0,0 @@
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Logging;
using MediaBrowser.Model.Providers;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
namespace MediaBrowser.Providers.TV
{
public class TvdbPersonImageProvider : BaseMetadataProvider
{
private readonly IProviderManager _providerManager;
public TvdbPersonImageProvider(ILogManager logManager, IServerConfigurationManager configurationManager, IProviderManager providerManager)
: base(logManager, configurationManager)
{
_providerManager = providerManager;
}
protected override bool RefreshOnVersionChange
{
get
{
return true;
}
}
protected override string ProviderVersion
{
get
{
return "2";
}
}
public override bool RequiresInternet
{
get
{
return true;
}
}
public override bool Supports(BaseItem item)
{
return item is Person;
}
/// <summary>
/// Fetches metadata and returns true or false indicating if any work that requires persistence was done
/// </summary>
/// <param name="item">The item.</param>
/// <param name="force">if set to <c>true</c> [force].</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Task{System.Boolean}.</returns>
public override async Task<bool> FetchAsync(BaseItem item, bool force, BaseProviderInfo providerInfo, CancellationToken cancellationToken)
{
if (string.IsNullOrEmpty(item.PrimaryImagePath))
{
cancellationToken.ThrowIfCancellationRequested();
var images = await _providerManager.GetAvailableRemoteImages(item, cancellationToken, ManualTvdbPersonImageProvider.ProviderName).ConfigureAwait(false);
await DownloadImages(item, images.ToList(), cancellationToken).ConfigureAwait(false);
SetLastRefreshed(item, DateTime.UtcNow, providerInfo);
return true;
}
SetLastRefreshed(item, DateTime.UtcNow, providerInfo);
return true;
}
private async Task DownloadImages(BaseItem item, List<RemoteImageInfo> images, CancellationToken cancellationToken)
{
if (!item.HasImage(ImageType.Primary) && !item.LockedFields.Contains(MetadataFields.Images))
{
var image = images.FirstOrDefault(i => i.Type == ImageType.Primary);
if (image != null)
{
await _providerManager.SaveImage(item, image.Url, TvdbSeriesProvider.Current.TvDbResourcePool, ImageType.Primary, null, cancellationToken)
.ConfigureAwait(false);
}
}
}
public override MetadataProviderPriority Priority
{
get { return MetadataProviderPriority.Fourth; }
}
}
}

View File

@ -44,14 +44,6 @@ namespace MediaBrowser.Providers
public override Task<bool> FetchAsync(BaseItem item, bool force, BaseProviderInfo providerInfo, CancellationToken cancellationToken) public override Task<bool> FetchAsync(BaseItem item, bool force, BaseProviderInfo providerInfo, CancellationToken cancellationToken)
{ {
item.ValidateImages(); item.ValidateImages();
item.ValidateBackdrops();
var hasScreenshots = item as IHasScreenshots;
if (hasScreenshots != null)
{
hasScreenshots.ValidateScreenshots();
}
SetLastRefreshed(item, DateTime.UtcNow, providerInfo); SetLastRefreshed(item, DateTime.UtcNow, providerInfo);
return TrueTaskResult; return TrueTaskResult;

View File

@ -388,18 +388,18 @@ namespace MediaBrowser.Server.Implementations.Drawing
/// <param name="image">The image.</param> /// <param name="image">The image.</param>
/// <param name="outputFormat">The output format.</param> /// <param name="outputFormat">The output format.</param>
/// <returns>ImageFormat.</returns> /// <returns>ImageFormat.</returns>
private ImageFormat GetOutputFormat(Image image, ImageOutputFormat outputFormat) private System.Drawing.Imaging.ImageFormat GetOutputFormat(Image image, ImageOutputFormat outputFormat)
{ {
switch (outputFormat) switch (outputFormat)
{ {
case ImageOutputFormat.Bmp: case ImageOutputFormat.Bmp:
return ImageFormat.Bmp; return System.Drawing.Imaging.ImageFormat.Bmp;
case ImageOutputFormat.Gif: case ImageOutputFormat.Gif:
return ImageFormat.Gif; return System.Drawing.Imaging.ImageFormat.Gif;
case ImageOutputFormat.Jpg: case ImageOutputFormat.Jpg:
return ImageFormat.Jpeg; return System.Drawing.Imaging.ImageFormat.Jpeg;
case ImageOutputFormat.Png: case ImageOutputFormat.Png:
return ImageFormat.Png; return System.Drawing.Imaging.ImageFormat.Png;
default: default:
return image.RawFormat; return image.RawFormat;
} }
@ -787,7 +787,7 @@ namespace MediaBrowser.Server.Implementations.Drawing
//And then save it in the cache //And then save it in the cache
using (var outputStream = _fileSystem.GetFileStream(enhancedImagePath, FileMode.Create, FileAccess.Write, FileShare.Read, false)) using (var outputStream = _fileSystem.GetFileStream(enhancedImagePath, FileMode.Create, FileAccess.Write, FileShare.Read, false))
{ {
newImage.Save(ImageFormat.Png, outputStream, 100); newImage.Save(System.Drawing.Imaging.ImageFormat.Png, outputStream, 100);
} }
} }
} }

View File

@ -1024,6 +1024,11 @@ namespace MediaBrowser.Server.Implementations.Dto
{ {
dto.SpecialFeatureCount = specialFeatureCount; dto.SpecialFeatureCount = specialFeatureCount;
} }
if (fields.Contains(ItemFields.TmdbCollectionName))
{
dto.TmdbCollectionName = movie.TmdbCollectionName;
}
} }
// Add EpisodeInfo // Add EpisodeInfo

View File

@ -22,7 +22,7 @@ namespace MediaBrowser.Server.Implementations.FileOrganization
{ {
public class EpisodeFileOrganizer public class EpisodeFileOrganizer
{ {
private readonly IDirectoryWatchers _directoryWatchers; private readonly ILibraryMonitor _libraryMonitor;
private readonly ILibraryManager _libraryManager; private readonly ILibraryManager _libraryManager;
private readonly ILogger _logger; private readonly ILogger _logger;
private readonly IFileSystem _fileSystem; private readonly IFileSystem _fileSystem;
@ -31,14 +31,14 @@ namespace MediaBrowser.Server.Implementations.FileOrganization
private readonly CultureInfo _usCulture = new CultureInfo("en-US"); private readonly CultureInfo _usCulture = new CultureInfo("en-US");
public EpisodeFileOrganizer(IFileOrganizationService organizationService, IServerConfigurationManager config, IFileSystem fileSystem, ILogger logger, ILibraryManager libraryManager, IDirectoryWatchers directoryWatchers) public EpisodeFileOrganizer(IFileOrganizationService organizationService, IServerConfigurationManager config, IFileSystem fileSystem, ILogger logger, ILibraryManager libraryManager, ILibraryMonitor libraryMonitor)
{ {
_organizationService = organizationService; _organizationService = organizationService;
_config = config; _config = config;
_fileSystem = fileSystem; _fileSystem = fileSystem;
_logger = logger; _logger = logger;
_libraryManager = libraryManager; _libraryManager = libraryManager;
_directoryWatchers = directoryWatchers; _libraryMonitor = libraryMonitor;
} }
public async Task<FileOrganizationResult> OrganizeEpisodeFile(string path, TvFileOrganizationOptions options, bool overwriteExisting) public async Task<FileOrganizationResult> OrganizeEpisodeFile(string path, TvFileOrganizationOptions options, bool overwriteExisting)
@ -174,6 +174,8 @@ namespace MediaBrowser.Server.Implementations.FileOrganization
{ {
_logger.Debug("Removing duplicate episode {0}", path); _logger.Debug("Removing duplicate episode {0}", path);
_libraryMonitor.ReportFileSystemChangeBeginning(path);
try try
{ {
File.Delete(path); File.Delete(path);
@ -182,6 +184,10 @@ namespace MediaBrowser.Server.Implementations.FileOrganization
{ {
_logger.ErrorException("Error removing duplicate episode", ex, path); _logger.ErrorException("Error removing duplicate episode", ex, path);
} }
finally
{
_libraryMonitor.ReportFileSystemChangeComplete(path, true);
}
} }
} }
} }
@ -232,7 +238,7 @@ namespace MediaBrowser.Server.Implementations.FileOrganization
private void PerformFileSorting(TvFileOrganizationOptions options, FileOrganizationResult result) private void PerformFileSorting(TvFileOrganizationOptions options, FileOrganizationResult result)
{ {
_directoryWatchers.TemporarilyIgnore(result.TargetPath); _libraryMonitor.ReportFileSystemChangeBeginning(result.TargetPath);
Directory.CreateDirectory(Path.GetDirectoryName(result.TargetPath)); Directory.CreateDirectory(Path.GetDirectoryName(result.TargetPath));
@ -264,7 +270,7 @@ namespace MediaBrowser.Server.Implementations.FileOrganization
} }
finally finally
{ {
_directoryWatchers.RemoveTempIgnore(result.TargetPath); _libraryMonitor.ReportFileSystemChangeComplete(result.TargetPath, true);
} }
if (copy) if (copy)
@ -376,8 +382,8 @@ namespace MediaBrowser.Server.Implementations.FileOrganization
private string GetEpisodeFileName(string sourcePath, string seriesName, int seasonNumber, int episodeNumber, int? endingEpisodeNumber, string episodeTitle, TvFileOrganizationOptions options) private string GetEpisodeFileName(string sourcePath, string seriesName, int seasonNumber, int episodeNumber, int? endingEpisodeNumber, string episodeTitle, TvFileOrganizationOptions options)
{ {
seriesName = _fileSystem.GetValidFilename(seriesName); seriesName = _fileSystem.GetValidFilename(seriesName).Trim();
episodeTitle = _fileSystem.GetValidFilename(episodeTitle); episodeTitle = _fileSystem.GetValidFilename(episodeTitle).Trim();
var sourceExtension = (Path.GetExtension(sourcePath) ?? string.Empty).TrimStart('.'); var sourceExtension = (Path.GetExtension(sourcePath) ?? string.Empty).TrimStart('.');

View File

@ -21,17 +21,17 @@ namespace MediaBrowser.Server.Implementations.FileOrganization
private readonly ITaskManager _taskManager; private readonly ITaskManager _taskManager;
private readonly IFileOrganizationRepository _repo; private readonly IFileOrganizationRepository _repo;
private readonly ILogger _logger; private readonly ILogger _logger;
private readonly IDirectoryWatchers _directoryWatchers; private readonly ILibraryMonitor _libraryMonitor;
private readonly ILibraryManager _libraryManager; private readonly ILibraryManager _libraryManager;
private readonly IServerConfigurationManager _config; private readonly IServerConfigurationManager _config;
private readonly IFileSystem _fileSystem; private readonly IFileSystem _fileSystem;
public FileOrganizationService(ITaskManager taskManager, IFileOrganizationRepository repo, ILogger logger, IDirectoryWatchers directoryWatchers, ILibraryManager libraryManager, IServerConfigurationManager config, IFileSystem fileSystem) public FileOrganizationService(ITaskManager taskManager, IFileOrganizationRepository repo, ILogger logger, ILibraryMonitor libraryMonitor, ILibraryManager libraryManager, IServerConfigurationManager config, IFileSystem fileSystem)
{ {
_taskManager = taskManager; _taskManager = taskManager;
_repo = repo; _repo = repo;
_logger = logger; _logger = logger;
_directoryWatchers = directoryWatchers; _libraryMonitor = libraryMonitor;
_libraryManager = libraryManager; _libraryManager = libraryManager;
_config = config; _config = config;
_fileSystem = fileSystem; _fileSystem = fileSystem;
@ -91,13 +91,10 @@ namespace MediaBrowser.Server.Implementations.FileOrganization
} }
var organizer = new EpisodeFileOrganizer(this, _config, _fileSystem, _logger, _libraryManager, var organizer = new EpisodeFileOrganizer(this, _config, _fileSystem, _logger, _libraryManager,
_directoryWatchers); _libraryMonitor);
await organizer.OrganizeEpisodeFile(result.OriginalPath, _config.Configuration.TvFileOrganizationOptions, true) await organizer.OrganizeEpisodeFile(result.OriginalPath, _config.Configuration.TvFileOrganizationOptions, true)
.ConfigureAwait(false); .ConfigureAwait(false);
await _libraryManager.ValidateMediaLibrary(new Progress<double>(), CancellationToken.None)
.ConfigureAwait(false);
} }
public Task ClearLog() public Task ClearLog()
@ -108,12 +105,9 @@ namespace MediaBrowser.Server.Implementations.FileOrganization
public async Task PerformEpisodeOrganization(EpisodeFileOrganizationRequest request) public async Task PerformEpisodeOrganization(EpisodeFileOrganizationRequest request)
{ {
var organizer = new EpisodeFileOrganizer(this, _config, _fileSystem, _logger, _libraryManager, var organizer = new EpisodeFileOrganizer(this, _config, _fileSystem, _logger, _libraryManager,
_directoryWatchers); _libraryMonitor);
await organizer.OrganizeWithCorrection(request, _config.Configuration.TvFileOrganizationOptions).ConfigureAwait(false); await organizer.OrganizeWithCorrection(request, _config.Configuration.TvFileOrganizationOptions).ConfigureAwait(false);
await _libraryManager.ValidateMediaLibrary(new Progress<double>(), CancellationToken.None)
.ConfigureAwait(false);
} }
} }
} }

View File

@ -14,16 +14,16 @@ namespace MediaBrowser.Server.Implementations.FileOrganization
{ {
public class OrganizerScheduledTask : IScheduledTask, IConfigurableScheduledTask public class OrganizerScheduledTask : IScheduledTask, IConfigurableScheduledTask
{ {
private readonly IDirectoryWatchers _directoryWatchers; private readonly ILibraryMonitor _libraryMonitor;
private readonly ILibraryManager _libraryManager; private readonly ILibraryManager _libraryManager;
private readonly ILogger _logger; private readonly ILogger _logger;
private readonly IFileSystem _fileSystem; private readonly IFileSystem _fileSystem;
private readonly IServerConfigurationManager _config; private readonly IServerConfigurationManager _config;
private readonly IFileOrganizationService _organizationService; private readonly IFileOrganizationService _organizationService;
public OrganizerScheduledTask(IDirectoryWatchers directoryWatchers, ILibraryManager libraryManager, ILogger logger, IFileSystem fileSystem, IServerConfigurationManager config, IFileOrganizationService organizationService) public OrganizerScheduledTask(ILibraryMonitor libraryMonitor, ILibraryManager libraryManager, ILogger logger, IFileSystem fileSystem, IServerConfigurationManager config, IFileOrganizationService organizationService)
{ {
_directoryWatchers = directoryWatchers; _libraryMonitor = libraryMonitor;
_libraryManager = libraryManager; _libraryManager = libraryManager;
_logger = logger; _logger = logger;
_fileSystem = fileSystem; _fileSystem = fileSystem;
@ -48,7 +48,7 @@ namespace MediaBrowser.Server.Implementations.FileOrganization
public Task Execute(CancellationToken cancellationToken, IProgress<double> progress) public Task Execute(CancellationToken cancellationToken, IProgress<double> progress)
{ {
return new TvFolderOrganizer(_libraryManager, _logger, _fileSystem, _directoryWatchers, _organizationService, _config) return new TvFolderOrganizer(_libraryManager, _logger, _fileSystem, _libraryMonitor, _organizationService, _config)
.Organize(_config.Configuration.TvFileOrganizationOptions, cancellationToken, progress); .Organize(_config.Configuration.TvFileOrganizationOptions, cancellationToken, progress);
} }

View File

@ -18,19 +18,19 @@ namespace MediaBrowser.Server.Implementations.FileOrganization
{ {
public class TvFolderOrganizer public class TvFolderOrganizer
{ {
private readonly IDirectoryWatchers _directoryWatchers; private readonly ILibraryMonitor _libraryMonitor;
private readonly ILibraryManager _libraryManager; private readonly ILibraryManager _libraryManager;
private readonly ILogger _logger; private readonly ILogger _logger;
private readonly IFileSystem _fileSystem; private readonly IFileSystem _fileSystem;
private readonly IFileOrganizationService _organizationService; private readonly IFileOrganizationService _organizationService;
private readonly IServerConfigurationManager _config; private readonly IServerConfigurationManager _config;
public TvFolderOrganizer(ILibraryManager libraryManager, ILogger logger, IFileSystem fileSystem, IDirectoryWatchers directoryWatchers, IFileOrganizationService organizationService, IServerConfigurationManager config) public TvFolderOrganizer(ILibraryManager libraryManager, ILogger logger, IFileSystem fileSystem, ILibraryMonitor libraryMonitor, IFileOrganizationService organizationService, IServerConfigurationManager config)
{ {
_libraryManager = libraryManager; _libraryManager = libraryManager;
_logger = logger; _logger = logger;
_fileSystem = fileSystem; _fileSystem = fileSystem;
_directoryWatchers = directoryWatchers; _libraryMonitor = libraryMonitor;
_organizationService = organizationService; _organizationService = organizationService;
_config = config; _config = config;
} }
@ -57,7 +57,7 @@ namespace MediaBrowser.Server.Implementations.FileOrganization
foreach (var file in eligibleFiles) foreach (var file in eligibleFiles)
{ {
var organizer = new EpisodeFileOrganizer(_organizationService, _config, _fileSystem, _logger, _libraryManager, var organizer = new EpisodeFileOrganizer(_organizationService, _config, _fileSystem, _logger, _libraryManager,
_directoryWatchers); _libraryMonitor);
var result = await organizer.OrganizeEpisodeFile(file.FullName, options, false).ConfigureAwait(false); var result = await organizer.OrganizeEpisodeFile(file.FullName, options, false).ConfigureAwait(false);

View File

@ -2,7 +2,6 @@
using MediaBrowser.Common.ScheduledTasks; using MediaBrowser.Common.ScheduledTasks;
using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.IO;
using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Library;
using MediaBrowser.Model.Entities; using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Logging; using MediaBrowser.Model.Logging;
@ -18,10 +17,7 @@ using System.Threading.Tasks;
namespace MediaBrowser.Server.Implementations.IO namespace MediaBrowser.Server.Implementations.IO
{ {
/// <summary> public class LibraryMonitor : ILibraryMonitor
/// Class DirectoryWatchers
/// </summary>
public class DirectoryWatchers : IDirectoryWatchers
{ {
/// <summary> /// <summary>
/// The file system watchers /// The file system watchers
@ -55,17 +51,28 @@ namespace MediaBrowser.Server.Implementations.IO
/// Add the path to our temporary ignore list. Use when writing to a path within our listening scope. /// Add the path to our temporary ignore list. Use when writing to a path within our listening scope.
/// </summary> /// </summary>
/// <param name="path">The path.</param> /// <param name="path">The path.</param>
public void TemporarilyIgnore(string path) private void TemporarilyIgnore(string path)
{ {
_tempIgnoredPaths[path] = path; _tempIgnoredPaths[path] = path;
} }
/// <summary> public void ReportFileSystemChangeBeginning(string path)
/// Removes the temp ignore.
/// </summary>
/// <param name="path">The path.</param>
public async void RemoveTempIgnore(string path)
{ {
if (string.IsNullOrEmpty(path))
{
throw new ArgumentNullException("path");
}
TemporarilyIgnore(path);
}
public async void ReportFileSystemChangeComplete(string path, bool refreshPath)
{
if (string.IsNullOrEmpty(path))
{
throw new ArgumentNullException("path");
}
// This is an arbitraty amount of time, but delay it because file system writes often trigger events after RemoveTempIgnore has been called. // This is an arbitraty amount of time, but delay it because file system writes often trigger events after RemoveTempIgnore has been called.
// Seeing long delays in some situations, especially over the network. // Seeing long delays in some situations, especially over the network.
// Seeing delays up to 40 seconds, but not going to ignore changes for that long. // Seeing delays up to 40 seconds, but not going to ignore changes for that long.
@ -73,6 +80,11 @@ namespace MediaBrowser.Server.Implementations.IO
string val; string val;
_tempIgnoredPaths.TryRemove(path, out val); _tempIgnoredPaths.TryRemove(path, out val);
if (refreshPath)
{
ReportFileSystemChanged(path);
}
} }
/// <summary> /// <summary>
@ -93,9 +105,9 @@ namespace MediaBrowser.Server.Implementations.IO
private readonly IFileSystem _fileSystem; private readonly IFileSystem _fileSystem;
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="DirectoryWatchers" /> class. /// Initializes a new instance of the <see cref="LibraryMonitor" /> class.
/// </summary> /// </summary>
public DirectoryWatchers(ILogManager logManager, ITaskManager taskManager, ILibraryManager libraryManager, IServerConfigurationManager configurationManager, IFileSystem fileSystem) public LibraryMonitor(ILogManager logManager, ITaskManager taskManager, ILibraryManager libraryManager, IServerConfigurationManager configurationManager, IFileSystem fileSystem)
{ {
if (taskManager == null) if (taskManager == null)
{ {
@ -104,7 +116,7 @@ namespace MediaBrowser.Server.Implementations.IO
LibraryManager = libraryManager; LibraryManager = libraryManager;
TaskManager = taskManager; TaskManager = taskManager;
Logger = logManager.GetLogger("DirectoryWatchers"); Logger = logManager.GetLogger(GetType().Name);
ConfigurationManager = configurationManager; ConfigurationManager = configurationManager;
_fileSystem = fileSystem; _fileSystem = fileSystem;
@ -328,31 +340,30 @@ namespace MediaBrowser.Server.Implementations.IO
{ {
OnWatcherChanged(e); OnWatcherChanged(e);
} }
catch (IOException ex) catch (Exception ex)
{ {
Logger.ErrorException("IOException in watcher changed. Path: {0}", ex, e.FullPath); Logger.ErrorException("Exception in watcher changed. Path: {0}", ex, e.FullPath);
} }
} }
private void OnWatcherChanged(FileSystemEventArgs e) private void OnWatcherChanged(FileSystemEventArgs e)
{ {
var name = e.Name; Logger.Debug("Watcher sees change of type " + e.ChangeType + " to " + e.FullPath);
// Ignore certain files ReportFileSystemChanged(e.FullPath);
if (_alwaysIgnoreFiles.Contains(name, StringComparer.OrdinalIgnoreCase))
{
return;
} }
var nameFromFullPath = Path.GetFileName(e.FullPath); public void ReportFileSystemChanged(string path)
// Ignore certain files
if (!string.IsNullOrEmpty(nameFromFullPath) && _alwaysIgnoreFiles.Contains(nameFromFullPath, StringComparer.OrdinalIgnoreCase))
{ {
return; if (string.IsNullOrEmpty(path))
{
throw new ArgumentNullException("path");
} }
// Ignore when someone manually creates a new folder var filename = Path.GetFileName(path);
if (e.ChangeType == WatcherChangeTypes.Created && name == "New folder")
// Ignore certain files
if (!string.IsNullOrEmpty(filename) && _alwaysIgnoreFiles.Contains(filename, StringComparer.OrdinalIgnoreCase))
{ {
return; return;
} }
@ -362,38 +373,37 @@ namespace MediaBrowser.Server.Implementations.IO
// If the parent of an ignored path has a change event, ignore that too // If the parent of an ignored path has a change event, ignore that too
if (tempIgnorePaths.Any(i => if (tempIgnorePaths.Any(i =>
{ {
if (string.Equals(i, e.FullPath, StringComparison.OrdinalIgnoreCase)) if (string.Equals(i, path, StringComparison.OrdinalIgnoreCase))
{ {
Logger.Debug("Watcher ignoring change to {0}", e.FullPath); Logger.Debug("Ignoring change to {0}", path);
return true;
}
if (_fileSystem.ContainsSubPath(i, path))
{
Logger.Debug("Ignoring change to {0}", path);
return true; return true;
} }
// Go up a level // Go up a level
var parent = Path.GetDirectoryName(i); var parent = Path.GetDirectoryName(i);
if (string.Equals(parent, e.FullPath, StringComparison.OrdinalIgnoreCase)) if (!string.IsNullOrEmpty(parent))
{ {
Logger.Debug("Watcher ignoring change to {0}", e.FullPath); if (string.Equals(parent, path, StringComparison.OrdinalIgnoreCase))
{
Logger.Debug("Ignoring change to {0}", path);
return true; return true;
} }
// Go up another level // Go up another level
if (!string.IsNullOrEmpty(parent))
{
parent = Path.GetDirectoryName(i); parent = Path.GetDirectoryName(i);
if (string.Equals(parent, e.FullPath, StringComparison.OrdinalIgnoreCase)) if (string.Equals(parent, path, StringComparison.OrdinalIgnoreCase))
{ {
Logger.Debug("Watcher ignoring change to {0}", e.FullPath); Logger.Debug("Ignoring change to {0}", path);
return true; return true;
} }
} }
if (i.StartsWith(e.FullPath, StringComparison.OrdinalIgnoreCase) ||
e.FullPath.StartsWith(i, StringComparison.OrdinalIgnoreCase))
{
Logger.Debug("Watcher ignoring change to {0}", e.FullPath);
return true;
}
return false; return false;
})) }))
@ -401,22 +411,19 @@ namespace MediaBrowser.Server.Implementations.IO
return; return;
} }
Logger.Info("Watcher sees change of type " + e.ChangeType + " to " + e.FullPath); // Avoid implicitly captured closure
var affectedPath = path;
//Since we're watching created, deleted and renamed we always want the parent of the item to be the affected path _affectedPaths.AddOrUpdate(path, path, (key, oldValue) => affectedPath);
var affectedPath = e.FullPath;
_affectedPaths.AddOrUpdate(affectedPath, affectedPath, (key, oldValue) => affectedPath);
lock (_timerLock) lock (_timerLock)
{ {
if (_updateTimer == null) if (_updateTimer == null)
{ {
_updateTimer = new Timer(TimerStopped, null, TimeSpan.FromSeconds(ConfigurationManager.Configuration.FileWatcherDelay), TimeSpan.FromMilliseconds(-1)); _updateTimer = new Timer(TimerStopped, null, TimeSpan.FromSeconds(ConfigurationManager.Configuration.RealtimeWatcherDelay), TimeSpan.FromMilliseconds(-1));
} }
else else
{ {
_updateTimer.Change(TimeSpan.FromSeconds(ConfigurationManager.Configuration.FileWatcherDelay), TimeSpan.FromMilliseconds(-1)); _updateTimer.Change(TimeSpan.FromSeconds(ConfigurationManager.Configuration.RealtimeWatcherDelay), TimeSpan.FromMilliseconds(-1));
} }
} }
} }
@ -427,24 +434,9 @@ namespace MediaBrowser.Server.Implementations.IO
/// <param name="stateInfo">The state info.</param> /// <param name="stateInfo">The state info.</param>
private async void TimerStopped(object stateInfo) private async void TimerStopped(object stateInfo)
{ {
lock (_timerLock) Logger.Debug("Timer stopped.");
{
// Extend the timer as long as any of the paths are still being written to.
if (_affectedPaths.Any(p => IsFileLocked(p.Key)))
{
Logger.Info("Timer extended.");
_updateTimer.Change(TimeSpan.FromSeconds(ConfigurationManager.Configuration.FileWatcherDelay), TimeSpan.FromMilliseconds(-1));
return;
}
Logger.Info("Timer stopped."); DisposeTimer();
if (_updateTimer != null)
{
_updateTimer.Dispose();
_updateTimer = null;
}
}
var paths = _affectedPaths.Keys.ToList(); var paths = _affectedPaths.Keys.ToList();
_affectedPaths.Clear(); _affectedPaths.Clear();
@ -452,59 +444,16 @@ namespace MediaBrowser.Server.Implementations.IO
await ProcessPathChanges(paths).ConfigureAwait(false); await ProcessPathChanges(paths).ConfigureAwait(false);
} }
/// <summary> private void DisposeTimer()
/// Try and determine if a file is locked
/// This is not perfect, and is subject to race conditions, so I'd rather not make this a re-usable library method.
/// </summary>
/// <param name="path">The path.</param>
/// <returns><c>true</c> if [is file locked] [the specified path]; otherwise, <c>false</c>.</returns>
private bool IsFileLocked(string path)
{ {
try lock (_timerLock)
{ {
var data = _fileSystem.GetFileSystemInfo(path); if (_updateTimer != null)
if (!data.Exists
|| data.Attributes.HasFlag(FileAttributes.Directory)
|| data.Attributes.HasFlag(FileAttributes.ReadOnly))
{ {
return false; _updateTimer.Dispose();
_updateTimer = null;
} }
} }
catch (IOException)
{
return false;
}
try
{
using (_fileSystem.GetFileStream(path, FileMode.Open, FileAccess.ReadWrite, FileShare.ReadWrite))
{
//file is not locked
return false;
}
}
catch (DirectoryNotFoundException)
{
return false;
}
catch (FileNotFoundException)
{
return false;
}
catch (IOException)
{
//the file is unavailable because it is:
//still being written to
//or being processed by another thread
//or does not exist (has already been processed)
Logger.Debug("{0} is locked.", path);
return true;
}
catch
{
return false;
}
} }
/// <summary> /// <summary>
@ -599,14 +548,7 @@ namespace MediaBrowser.Server.Implementations.IO
watcher.Dispose(); watcher.Dispose();
} }
lock (_timerLock) DisposeTimer();
{
if (_updateTimer != null)
{
_updateTimer.Dispose();
_updateTimer = null;
}
}
_fileSystemWatchers.Clear(); _fileSystemWatchers.Clear();
_affectedPaths.Clear(); _affectedPaths.Clear();

View File

@ -137,7 +137,7 @@ namespace MediaBrowser.Server.Implementations.Library
private IEnumerable<IMetadataSaver> _savers; private IEnumerable<IMetadataSaver> _savers;
private readonly Func<IDirectoryWatchers> _directoryWatchersFactory; private readonly Func<ILibraryMonitor> _libraryMonitorFactory;
/// <summary> /// <summary>
/// The _library items cache /// The _library items cache
@ -180,14 +180,14 @@ namespace MediaBrowser.Server.Implementations.Library
/// <param name="userManager">The user manager.</param> /// <param name="userManager">The user manager.</param>
/// <param name="configurationManager">The configuration manager.</param> /// <param name="configurationManager">The configuration manager.</param>
/// <param name="userDataRepository">The user data repository.</param> /// <param name="userDataRepository">The user data repository.</param>
public LibraryManager(ILogger logger, ITaskManager taskManager, IUserManager userManager, IServerConfigurationManager configurationManager, IUserDataManager userDataRepository, Func<IDirectoryWatchers> directoryWatchersFactory, IFileSystem fileSystem) public LibraryManager(ILogger logger, ITaskManager taskManager, IUserManager userManager, IServerConfigurationManager configurationManager, IUserDataManager userDataRepository, Func<ILibraryMonitor> libraryMonitorFactory, IFileSystem fileSystem)
{ {
_logger = logger; _logger = logger;
_taskManager = taskManager; _taskManager = taskManager;
_userManager = userManager; _userManager = userManager;
ConfigurationManager = configurationManager; ConfigurationManager = configurationManager;
_userDataRepository = userDataRepository; _userDataRepository = userDataRepository;
_directoryWatchersFactory = directoryWatchersFactory; _libraryMonitorFactory = libraryMonitorFactory;
_fileSystem = fileSystem; _fileSystem = fileSystem;
ByReferenceItems = new ConcurrentDictionary<Guid, BaseItem>(); ByReferenceItems = new ConcurrentDictionary<Guid, BaseItem>();
@ -934,7 +934,7 @@ namespace MediaBrowser.Server.Implementations.Library
/// <returns>Task.</returns> /// <returns>Task.</returns>
public async Task ValidateMediaLibraryInternal(IProgress<double> progress, CancellationToken cancellationToken) public async Task ValidateMediaLibraryInternal(IProgress<double> progress, CancellationToken cancellationToken)
{ {
_directoryWatchersFactory().Stop(); _libraryMonitorFactory().Stop();
try try
{ {
@ -942,7 +942,7 @@ namespace MediaBrowser.Server.Implementations.Library
} }
finally finally
{ {
_directoryWatchersFactory().Start(); _libraryMonitorFactory().Start();
} }
} }
@ -1462,13 +1462,13 @@ namespace MediaBrowser.Server.Implementations.Library
var semaphore = _fileLocks.GetOrAdd(path, key => new SemaphoreSlim(1, 1)); var semaphore = _fileLocks.GetOrAdd(path, key => new SemaphoreSlim(1, 1));
var directoryWatchers = _directoryWatchersFactory(); var libraryMonitor = _libraryMonitorFactory();
await semaphore.WaitAsync().ConfigureAwait(false); await semaphore.WaitAsync().ConfigureAwait(false);
try try
{ {
directoryWatchers.TemporarilyIgnore(path); libraryMonitor.ReportFileSystemChangeBeginning(path);
saver.Save(item, CancellationToken.None); saver.Save(item, CancellationToken.None);
} }
catch (Exception ex) catch (Exception ex)
@ -1477,7 +1477,7 @@ namespace MediaBrowser.Server.Implementations.Library
} }
finally finally
{ {
directoryWatchers.RemoveTempIgnore(path); libraryMonitor.ReportFileSystemChangeComplete(path, false);
semaphore.Release(); semaphore.Release();
} }
} }

View File

@ -46,7 +46,7 @@ namespace MediaBrowser.Server.Implementations.Library
} }
// Make sure the item has a name // Make sure the item has a name
EnsureName(item); EnsureName(item, args);
item.DontFetchMeta = item.Path.IndexOf("[dontfetchmeta]", StringComparison.OrdinalIgnoreCase) != -1 || item.DontFetchMeta = item.Path.IndexOf("[dontfetchmeta]", StringComparison.OrdinalIgnoreCase) != -1 ||
item.Parents.Any(i => i.DontFetchMeta); item.Parents.Any(i => i.DontFetchMeta);
@ -59,13 +59,13 @@ namespace MediaBrowser.Server.Implementations.Library
/// Ensures the name. /// Ensures the name.
/// </summary> /// </summary>
/// <param name="item">The item.</param> /// <param name="item">The item.</param>
private static void EnsureName(BaseItem item) private static void EnsureName(BaseItem item, ItemResolveArgs args)
{ {
// If the subclass didn't supply a name, add it here // If the subclass didn't supply a name, add it here
if (string.IsNullOrEmpty(item.Name) && !string.IsNullOrEmpty(item.Path)) if (string.IsNullOrEmpty(item.Name) && !string.IsNullOrEmpty(item.Path))
{ {
//we use our resolve args name here to get the name of the containg folder, not actual video file //we use our resolve args name here to get the name of the containg folder, not actual video file
item.Name = GetMBName(item.ResolveArgs.FileInfo.Name, (item.ResolveArgs.FileInfo.Attributes & FileAttributes.Directory) == FileAttributes.Directory); item.Name = GetMBName(args.FileInfo.Name, (args.FileInfo.Attributes & FileAttributes.Directory) == FileAttributes.Directory);
} }
} }

View File

@ -4,6 +4,7 @@ using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Persistence; using MediaBrowser.Controller.Persistence;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Logging; using MediaBrowser.Model.Logging;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
@ -192,7 +193,11 @@ namespace MediaBrowser.Server.Implementations.Library
/// <returns>Task.</returns> /// <returns>Task.</returns>
public Task RefreshUsersMetadata(CancellationToken cancellationToken, bool force = false) public Task RefreshUsersMetadata(CancellationToken cancellationToken, bool force = false)
{ {
var tasks = Users.Select(user => user.RefreshMetadata(cancellationToken, forceRefresh: force)).ToList(); var tasks = Users.Select(user => user.RefreshMetadata(new MetadataRefreshOptions
{
ReplaceAllMetadata = force
}, cancellationToken)).ToList();
return Task.WhenAll(tasks); return Task.WhenAll(tasks);
} }

View File

@ -16,7 +16,6 @@ namespace MediaBrowser.Server.Implementations.Library.Validators
/// Initializes a new instance of the <see cref="ArtistsPostScanTask" /> class. /// Initializes a new instance of the <see cref="ArtistsPostScanTask" /> class.
/// </summary> /// </summary>
/// <param name="libraryManager">The library manager.</param> /// <param name="libraryManager">The library manager.</param>
/// <param name="userManager">The user manager.</param>
public GenresPostScanTask(ILibraryManager libraryManager) public GenresPostScanTask(ILibraryManager libraryManager)
{ {
_libraryManager = libraryManager; _libraryManager = libraryManager;

View File

@ -1,5 +1,6 @@
using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Logging; using MediaBrowser.Model.Logging;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
@ -88,7 +89,14 @@ namespace MediaBrowser.Server.Implementations.Library.Validators
var itemByName = _libraryManager.GetPerson(name); var itemByName = _libraryManager.GetPerson(name);
await itemByName.RefreshMetadata(cancellationToken, allowSlowProviders: false).ConfigureAwait(false); // The only purpose here is to be able to react to image changes without running the people task.
// All other metadata can wait for that.
await itemByName.RefreshMetadata(new MetadataRefreshOptions
{
ImageRefreshMode = MetadataRefreshMode.None,
MetadataRefreshMode = MetadataRefreshMode.None
}, cancellationToken).ConfigureAwait(false);
foreach (var libraryId in counts.Keys) foreach (var libraryId in counts.Keys)
{ {

View File

@ -1,154 +1,111 @@
using MediaBrowser.Common.IO; using MediaBrowser.Common.Net;
using MediaBrowser.Common.Net;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.LiveTv; using MediaBrowser.Controller.LiveTv;
using MediaBrowser.Controller.Providers; using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Entities; using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Logging; using MediaBrowser.Model.Logging;
using MediaBrowser.Model.Net;
using System; using System;
using System.IO; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Net;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace MediaBrowser.Server.Implementations.LiveTv namespace MediaBrowser.Server.Implementations.LiveTv
{ {
public class ChannelImageProvider : BaseMetadataProvider public class ChannelImageProvider : IDynamicImageProvider, IHasChangeMonitor
{ {
private readonly ILiveTvManager _liveTvManager; private readonly ILiveTvManager _liveTvManager;
private readonly IProviderManager _providerManager;
private readonly IFileSystem _fileSystem;
private readonly IHttpClient _httpClient; private readonly IHttpClient _httpClient;
private readonly ILogger _logger;
public ChannelImageProvider(ILogManager logManager, IServerConfigurationManager configurationManager, ILiveTvManager liveTvManager, IProviderManager providerManager, IFileSystem fileSystem, IHttpClient httpClient) public ChannelImageProvider(ILiveTvManager liveTvManager, IHttpClient httpClient, ILogger logger)
: base(logManager, configurationManager)
{ {
_liveTvManager = liveTvManager; _liveTvManager = liveTvManager;
_providerManager = providerManager;
_fileSystem = fileSystem;
_httpClient = httpClient; _httpClient = httpClient;
_logger = logger;
} }
public override bool Supports(BaseItem item) public IEnumerable<ImageType> GetSupportedImages(IHasImages item)
{ {
return item is LiveTvChannel; return new[] { ImageType.Primary };
} }
protected override bool NeedsRefreshInternal(BaseItem item, BaseProviderInfo providerInfo) public async Task<DynamicImageResponse> GetImage(IHasImages item, ImageType type, CancellationToken cancellationToken)
{ {
return !item.HasImage(ImageType.Primary); var liveTvItem = (LiveTvChannel)item;
}
public override async Task<bool> FetchAsync(BaseItem item, bool force, BaseProviderInfo providerInfo, CancellationToken cancellationToken) var imageResponse = new DynamicImageResponse();
{
if (item.HasImage(ImageType.Primary))
{
SetLastRefreshed(item, DateTime.UtcNow, providerInfo);
return true;
}
var changed = true; if (!string.IsNullOrEmpty(liveTvItem.ProviderImagePath))
try
{ {
changed = await DownloadImage((LiveTvChannel)item, cancellationToken).ConfigureAwait(false); imageResponse.Path = liveTvItem.ProviderImagePath;
imageResponse.HasImage = true;
} }
catch (HttpException ex) else if (!string.IsNullOrEmpty(liveTvItem.ProviderImageUrl))
{
// Don't fail the provider on a 404
if (!ex.StatusCode.HasValue || ex.StatusCode.Value != HttpStatusCode.NotFound)
{
throw;
}
}
if (changed)
{
SetLastRefreshed(item, DateTime.UtcNow, providerInfo);
}
return changed;
}
private async Task<bool> DownloadImage(LiveTvChannel item, CancellationToken cancellationToken)
{
Stream imageStream = null;
string contentType = null;
if (!string.IsNullOrEmpty(item.ProviderImagePath))
{
contentType = "image/" + Path.GetExtension(item.ProviderImagePath).ToLower();
imageStream = _fileSystem.GetFileStream(item.ProviderImagePath, FileMode.Open, FileAccess.Read, FileShare.Read, true);
}
else if (!string.IsNullOrEmpty(item.ProviderImageUrl))
{ {
var options = new HttpRequestOptions var options = new HttpRequestOptions
{ {
CancellationToken = cancellationToken, CancellationToken = cancellationToken,
Url = item.ProviderImageUrl Url = liveTvItem.ProviderImageUrl
}; };
var response = await _httpClient.GetResponse(options).ConfigureAwait(false); var response = await _httpClient.GetResponse(options).ConfigureAwait(false);
if (!response.ContentType.StartsWith("image/", StringComparison.OrdinalIgnoreCase)) if (response.ContentType.StartsWith("image/", StringComparison.OrdinalIgnoreCase))
{ {
Logger.Error("Provider did not return an image content type."); imageResponse.HasImage = true;
return false; imageResponse.Stream = response.Content;
imageResponse.SetFormatFromMimeType(response.ContentType);
} }
else
imageStream = response.Content;
contentType = response.ContentType;
}
else if (item.HasProviderImage ?? true)
{ {
var service = _liveTvManager.Services.FirstOrDefault(i => string.Equals(i.Name, item.ServiceName, StringComparison.OrdinalIgnoreCase)); _logger.Error("Provider did not return an image content type.");
}
}
else if (liveTvItem.HasProviderImage ?? true)
{
var service = _liveTvManager.Services.FirstOrDefault(i => string.Equals(i.Name, liveTvItem.ServiceName, StringComparison.OrdinalIgnoreCase));
if (service != null) if (service != null)
{ {
try try
{ {
var response = await service.GetChannelImageAsync(item.ExternalId, cancellationToken).ConfigureAwait(false); var response = await service.GetChannelImageAsync(liveTvItem.ExternalId, cancellationToken).ConfigureAwait(false);
if (response != null) if (response != null)
{ {
imageStream = response.Stream; imageResponse.HasImage = true;
contentType = response.MimeType; imageResponse.Stream = response.Stream;
imageResponse.Format = response.Format;
} }
} }
catch (NotImplementedException) catch (NotImplementedException)
{ {
return false;
} }
} }
} }
if (imageStream != null) return imageResponse;
}
public string Name
{ {
// Dummy up the original url get { return "Live TV Service Provider"; }
var url = item.ServiceName + item.ExternalId;
await _providerManager.SaveImage(item, imageStream, contentType, ImageType.Primary, null, url, cancellationToken).ConfigureAwait(false);
return true;
} }
return false; public bool Supports(IHasImages item)
}
public override MetadataProviderPriority Priority
{ {
get { return MetadataProviderPriority.Second; } return item is LiveTvChannel;
} }
public override ItemUpdateType ItemUpdateType public int Order
{ {
get get { return 0; }
{
return ItemUpdateType.ImageUpdate;
} }
public bool HasChanged(IHasMetadata item, DateTime date)
{
return !item.HasImage(ImageType.Primary) && (DateTime.UtcNow - date).TotalDays >= 1;
} }
} }
} }

View File

@ -9,6 +9,7 @@ using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.LiveTv; using MediaBrowser.Controller.LiveTv;
using MediaBrowser.Controller.MediaInfo; using MediaBrowser.Controller.MediaInfo;
using MediaBrowser.Controller.Persistence; using MediaBrowser.Controller.Persistence;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Controller.Sorting; using MediaBrowser.Controller.Sorting;
using MediaBrowser.Model.Entities; using MediaBrowser.Model.Entities;
using MediaBrowser.Model.LiveTv; using MediaBrowser.Model.LiveTv;
@ -328,7 +329,12 @@ namespace MediaBrowser.Server.Implementations.LiveTv
// Set this now so we don't cause additional file system access during provider executions // Set this now so we don't cause additional file system access during provider executions
item.ResetResolveArgs(fileInfo); item.ResetResolveArgs(fileInfo);
await item.RefreshMetadata(cancellationToken, forceSave: isNew, resetResolveArgs: false); await item.RefreshMetadata(new MetadataRefreshOptions
{
ForceSave = isNew,
ResetResolveArgs = false
}, cancellationToken);
return item; return item;
} }
@ -383,7 +389,12 @@ namespace MediaBrowser.Server.Implementations.LiveTv
item.RunTimeTicks = (info.EndDate - info.StartDate).Ticks; item.RunTimeTicks = (info.EndDate - info.StartDate).Ticks;
item.StartDate = info.StartDate; item.StartDate = info.StartDate;
await item.RefreshMetadata(cancellationToken, forceSave: isNew, resetResolveArgs: false); await item.RefreshMetadata(new MetadataRefreshOptions
{
ForceSave = isNew,
ResetResolveArgs = false
}, cancellationToken);
return item; return item;
} }
@ -435,7 +446,12 @@ namespace MediaBrowser.Server.Implementations.LiveTv
item.RecordingInfo = info; item.RecordingInfo = info;
item.ServiceName = serviceName; item.ServiceName = serviceName;
await item.RefreshMetadata(cancellationToken, forceSave: isNew, resetResolveArgs: false); await item.RefreshMetadata(new MetadataRefreshOptions
{
ForceSave = isNew,
ResetResolveArgs = false
}, cancellationToken);
_libraryManager.RegisterItem((BaseItem)item); _libraryManager.RegisterItem((BaseItem)item);

View File

@ -1,154 +1,111 @@
using MediaBrowser.Common.IO; using MediaBrowser.Common.Net;
using MediaBrowser.Common.Net;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.LiveTv; using MediaBrowser.Controller.LiveTv;
using MediaBrowser.Controller.Providers; using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Entities; using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Logging; using MediaBrowser.Model.Logging;
using MediaBrowser.Model.Net;
using System; using System;
using System.IO; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Net;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace MediaBrowser.Server.Implementations.LiveTv namespace MediaBrowser.Server.Implementations.LiveTv
{ {
public class ProgramImageProvider : BaseMetadataProvider public class ProgramImageProvider : IDynamicImageProvider, IHasChangeMonitor
{ {
private readonly ILiveTvManager _liveTvManager; private readonly ILiveTvManager _liveTvManager;
private readonly IProviderManager _providerManager;
private readonly IFileSystem _fileSystem;
private readonly IHttpClient _httpClient; private readonly IHttpClient _httpClient;
private readonly ILogger _logger;
public ProgramImageProvider(ILogManager logManager, IServerConfigurationManager configurationManager, ILiveTvManager liveTvManager, IProviderManager providerManager, IFileSystem fileSystem, IHttpClient httpClient) public ProgramImageProvider(ILiveTvManager liveTvManager, IHttpClient httpClient, ILogger logger)
: base(logManager, configurationManager)
{ {
_liveTvManager = liveTvManager; _liveTvManager = liveTvManager;
_providerManager = providerManager;
_fileSystem = fileSystem;
_httpClient = httpClient; _httpClient = httpClient;
_logger = logger;
} }
public override bool Supports(BaseItem item) public IEnumerable<ImageType> GetSupportedImages(IHasImages item)
{ {
return item is LiveTvProgram; return new[] { ImageType.Primary };
} }
protected override bool NeedsRefreshInternal(BaseItem item, BaseProviderInfo providerInfo) public async Task<DynamicImageResponse> GetImage(IHasImages item, ImageType type, CancellationToken cancellationToken)
{ {
return !item.HasImage(ImageType.Primary); var liveTvItem = (LiveTvProgram)item;
}
public override async Task<bool> FetchAsync(BaseItem item, bool force, BaseProviderInfo providerInfo, CancellationToken cancellationToken) var imageResponse = new DynamicImageResponse();
{
if (item.HasImage(ImageType.Primary))
{
SetLastRefreshed(item, DateTime.UtcNow, providerInfo);
return true;
}
var changed = true; if (!string.IsNullOrEmpty(liveTvItem.ProviderImagePath))
try
{ {
changed = await DownloadImage((LiveTvProgram)item, cancellationToken).ConfigureAwait(false); imageResponse.Path = liveTvItem.ProviderImagePath;
imageResponse.HasImage = true;
} }
catch (HttpException ex) else if (!string.IsNullOrEmpty(liveTvItem.ProviderImageUrl))
{
// Don't fail the provider on a 404
if (!ex.StatusCode.HasValue || ex.StatusCode.Value != HttpStatusCode.NotFound)
{
throw;
}
}
if (changed)
{
SetLastRefreshed(item, DateTime.UtcNow, providerInfo);
}
return changed;
}
private async Task<bool> DownloadImage(LiveTvProgram item, CancellationToken cancellationToken)
{
Stream imageStream = null;
string contentType = null;
if (!string.IsNullOrEmpty(item.ProviderImagePath))
{
contentType = "image/" + Path.GetExtension(item.ProviderImagePath).ToLower();
imageStream = _fileSystem.GetFileStream(item.ProviderImagePath, FileMode.Open, FileAccess.Read, FileShare.Read, true);
}
else if (!string.IsNullOrEmpty(item.ProviderImageUrl))
{ {
var options = new HttpRequestOptions var options = new HttpRequestOptions
{ {
CancellationToken = cancellationToken, CancellationToken = cancellationToken,
Url = item.ProviderImageUrl Url = liveTvItem.ProviderImageUrl
}; };
var response = await _httpClient.GetResponse(options).ConfigureAwait(false); var response = await _httpClient.GetResponse(options).ConfigureAwait(false);
if (!response.ContentType.StartsWith("image/", StringComparison.OrdinalIgnoreCase)) if (response.ContentType.StartsWith("image/", StringComparison.OrdinalIgnoreCase))
{ {
Logger.Error("Provider did not return an image content type."); imageResponse.HasImage = true;
return false; imageResponse.Stream = response.Content;
imageResponse.SetFormatFromMimeType(response.ContentType);
} }
else
imageStream = response.Content;
contentType = response.ContentType;
}
else if (item.HasProviderImage ?? true)
{ {
var service = _liveTvManager.Services.FirstOrDefault(i => string.Equals(i.Name, item.ServiceName, StringComparison.OrdinalIgnoreCase)); _logger.Error("Provider did not return an image content type.");
}
}
else if (liveTvItem.HasProviderImage ?? true)
{
var service = _liveTvManager.Services.FirstOrDefault(i => string.Equals(i.Name, liveTvItem.ServiceName, StringComparison.OrdinalIgnoreCase));
if (service != null) if (service != null)
{ {
try try
{ {
var response = await service.GetProgramImageAsync(item.ExternalId, item.ExternalChannelId, cancellationToken).ConfigureAwait(false); var response = await service.GetProgramImageAsync(liveTvItem.ExternalId, liveTvItem.ExternalChannelId, cancellationToken).ConfigureAwait(false);
if (response != null) if (response != null)
{ {
imageStream = response.Stream; imageResponse.HasImage = true;
contentType = response.MimeType; imageResponse.Stream = response.Stream;
imageResponse.Format = response.Format;
} }
} }
catch (NotImplementedException) catch (NotImplementedException)
{ {
return false;
} }
} }
} }
if (imageStream != null) return imageResponse;
}
public string Name
{ {
// Dummy up the original url get { return "Live TV Service Provider"; }
var url = item.ServiceName + item.ExternalId;
await _providerManager.SaveImage(item, imageStream, contentType, ImageType.Primary, null, url, cancellationToken).ConfigureAwait(false);
return true;
} }
return false; public bool Supports(IHasImages item)
}
public override MetadataProviderPriority Priority
{ {
get { return MetadataProviderPriority.Second; } return item is LiveTvProgram;
} }
public override ItemUpdateType ItemUpdateType public int Order
{ {
get get { return 0; }
{
return ItemUpdateType.ImageUpdate;
} }
public bool HasChanged(IHasMetadata item, DateTime date)
{
return !item.HasImage(ImageType.Primary) && (DateTime.UtcNow - date).TotalHours >= 12;
} }
} }
} }

View File

@ -118,7 +118,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv
if (response != null) if (response != null)
{ {
imageStream = response.Stream; imageStream = response.Stream;
contentType = response.MimeType; contentType = "image/" + response.Format.ToString().ToLower();
} }
} }
catch (NotImplementedException) catch (NotImplementedException)

View File

@ -137,7 +137,7 @@
<Compile Include="HttpServer\StreamWriter.cs" /> <Compile Include="HttpServer\StreamWriter.cs" />
<Compile Include="HttpServer\SwaggerService.cs" /> <Compile Include="HttpServer\SwaggerService.cs" />
<Compile Include="Drawing\ImageProcessor.cs" /> <Compile Include="Drawing\ImageProcessor.cs" />
<Compile Include="IO\DirectoryWatchers.cs" /> <Compile Include="IO\LibraryMonitor.cs" />
<Compile Include="Library\CoreResolutionIgnoreRule.cs" /> <Compile Include="Library\CoreResolutionIgnoreRule.cs" />
<Compile Include="Library\LibraryManager.cs" /> <Compile Include="Library\LibraryManager.cs" />
<Compile Include="Library\SearchEngine.cs" /> <Compile Include="Library\SearchEngine.cs" />
@ -189,8 +189,6 @@
<Compile Include="Persistence\SqliteShrinkMemoryTimer.cs" /> <Compile Include="Persistence\SqliteShrinkMemoryTimer.cs" />
<Compile Include="Persistence\TypeMapper.cs" /> <Compile Include="Persistence\TypeMapper.cs" />
<Compile Include="Properties\AssemblyInfo.cs" /> <Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Providers\ImageSaver.cs" />
<Compile Include="Providers\ProviderManager.cs" />
<Compile Include="Roku\RokuControllerFactory.cs" /> <Compile Include="Roku\RokuControllerFactory.cs" />
<Compile Include="ScheduledTasks\PeopleValidationTask.cs" /> <Compile Include="ScheduledTasks\PeopleValidationTask.cs" />
<Compile Include="ScheduledTasks\ChapterImagesTask.cs" /> <Compile Include="ScheduledTasks\ChapterImagesTask.cs" />

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