aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--MediaBrowser.Api/Library/LibraryService.cs10
-rw-r--r--MediaBrowser.Api/LibraryService.cs17
-rw-r--r--MediaBrowser.Controller/Entities/AdultVideo.cs4
-rw-r--r--MediaBrowser.Controller/Entities/AggregateFolder.cs65
-rw-r--r--MediaBrowser.Controller/Entities/BaseItem.cs558
-rw-r--r--MediaBrowser.Controller/Entities/Book.cs19
-rw-r--r--MediaBrowser.Controller/Entities/CollectionFolder.cs66
-rw-r--r--MediaBrowser.Controller/Entities/Folder.cs63
-rw-r--r--MediaBrowser.Controller/Entities/Game.cs22
-rw-r--r--MediaBrowser.Controller/Entities/IHasImages.cs6
-rw-r--r--MediaBrowser.Controller/Entities/Movies/Movie.cs81
-rw-r--r--MediaBrowser.Controller/Entities/TV/Episode.cs22
-rw-r--r--MediaBrowser.Controller/Entities/TV/Season.cs28
-rw-r--r--MediaBrowser.Controller/Entities/TV/Series.cs14
-rw-r--r--MediaBrowser.Controller/Entities/Trailer.cs27
-rw-r--r--MediaBrowser.Controller/Entities/Video.cs106
-rw-r--r--MediaBrowser.Controller/Library/ItemResolveArgs.cs91
-rw-r--r--MediaBrowser.Controller/LiveTv/ILiveTvRecording.cs2
-rw-r--r--MediaBrowser.Controller/Providers/BaseItemXmlParser.cs8
-rw-r--r--MediaBrowser.Controller/Providers/BaseMetadataProvider.cs189
-rw-r--r--MediaBrowser.Controller/Providers/IHasMetadata.cs6
-rw-r--r--MediaBrowser.Controller/Providers/ILocalMetadataProvider.cs17
-rw-r--r--MediaBrowser.Controller/Providers/IProviderManager.cs12
-rw-r--r--MediaBrowser.Controller/Providers/MetadataRefreshOptions.cs11
-rw-r--r--MediaBrowser.Model/Dto/BaseItemDto.cs6
-rw-r--r--MediaBrowser.Model/Querying/ItemFields.cs5
-rw-r--r--MediaBrowser.Providers/AdultVideos/AdultVideoXmlProvider.cs38
-rw-r--r--MediaBrowser.Providers/All/LocalImageProvider.cs2
-rw-r--r--MediaBrowser.Providers/BaseXmlProvider.cs63
-rw-r--r--MediaBrowser.Providers/BoxSets/BoxSetXmlProvider.cs39
-rw-r--r--MediaBrowser.Providers/CollectionFolderImageProvider.cs56
-rw-r--r--MediaBrowser.Providers/FolderProviderFromXml.cs93
-rw-r--r--MediaBrowser.Providers/Folders/FolderMetadataService.cs52
-rw-r--r--MediaBrowser.Providers/Folders/FolderXmlProvider.cs33
-rw-r--r--MediaBrowser.Providers/Folders/UserRootFolderNameProvider.cs (renamed from MediaBrowser.Providers/UserRootFolderNameProvider.cs)2
-rw-r--r--MediaBrowser.Providers/Games/GameSystemXmlProvider.cs39
-rw-r--r--MediaBrowser.Providers/Games/GameXmlProvider.cs45
-rw-r--r--MediaBrowser.Providers/ImageFromMediaLocationProvider.cs637
-rw-r--r--MediaBrowser.Providers/ImagesByNameProvider.cs100
-rw-r--r--MediaBrowser.Providers/LiveTv/ChannelXmlProvider.cs39
-rw-r--r--MediaBrowser.Providers/Manager/ImageSaver.cs21
-rw-r--r--MediaBrowser.Providers/Manager/ItemImageProvider.cs4
-rw-r--r--MediaBrowser.Providers/Manager/MetadataService.cs16
-rw-r--r--MediaBrowser.Providers/Manager/ProviderManager.cs196
-rw-r--r--MediaBrowser.Providers/MediaBrowser.Providers.csproj30
-rw-r--r--MediaBrowser.Providers/MediaInfo/AudioImageProvider.cs207
-rw-r--r--MediaBrowser.Providers/MediaInfo/BaseFFProbeProvider.cs145
-rw-r--r--MediaBrowser.Providers/MediaInfo/FFProbeAudioInfo.cs (renamed from MediaBrowser.Providers/MediaInfo/FFProbeAudioInfoProvider.cs)45
-rw-r--r--MediaBrowser.Providers/MediaInfo/FFProbeProvider.cs48
-rw-r--r--MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs126
-rw-r--r--MediaBrowser.Providers/MediaInfo/FFProbeVideoInfoProvider.cs686
-rw-r--r--MediaBrowser.Providers/MediaInfo/VideoImageProvider.cs57
-rw-r--r--MediaBrowser.Providers/Movies/FanArtMovieProvider.cs356
-rw-r--r--MediaBrowser.Providers/Movies/FanArtMovieUpdatesPrescanTask.cs4
-rw-r--r--MediaBrowser.Providers/Movies/FanartMovieImageProvider.cs (renamed from MediaBrowser.Providers/Movies/ManualFanartMovieImageProvider.cs)101
-rw-r--r--MediaBrowser.Providers/Movies/GenericMovieDbInfo.cs245
-rw-r--r--MediaBrowser.Providers/Movies/MovieDbImageProvider.cs (renamed from MediaBrowser.Providers/Movies/ManualMovieDbImageProvider.cs)16
-rw-r--r--MediaBrowser.Providers/Movies/MovieDbImagesProvider.cs247
-rw-r--r--MediaBrowser.Providers/Movies/MovieDbProvider.cs510
-rw-r--r--MediaBrowser.Providers/Movies/MovieMetadataService.cs43
-rw-r--r--MediaBrowser.Providers/Movies/MovieProviderFromXml.cs112
-rw-r--r--MediaBrowser.Providers/Movies/MovieXmlProvider.cs48
-rw-r--r--MediaBrowser.Providers/Movies/OpenMovieDatabaseProvider.cs285
-rw-r--r--MediaBrowser.Providers/Movies/TmdbSettings.cs17
-rw-r--r--MediaBrowser.Providers/Movies/TrailerMetadataService.cs43
-rw-r--r--MediaBrowser.Providers/Movies/TrailerXmlProvider.cs30
-rw-r--r--MediaBrowser.Providers/Music/AlbumXmlProvider.cs39
-rw-r--r--MediaBrowser.Providers/Music/ArtistXmlProvider.cs39
-rw-r--r--MediaBrowser.Providers/Music/AudioMetadataService.cs53
-rw-r--r--MediaBrowser.Providers/Music/MusicVideoXmlProvider.cs39
-rw-r--r--MediaBrowser.Providers/People/PersonXmlProvider.cs39
-rw-r--r--MediaBrowser.Providers/ProviderUtils.cs85
-rw-r--r--MediaBrowser.Providers/RefreshIntrosTask.cs8
-rw-r--r--MediaBrowser.Providers/Savers/GameXmlSaver.cs2
-rw-r--r--MediaBrowser.Providers/Savers/MovieXmlSaver.cs11
-rw-r--r--MediaBrowser.Providers/TV/EpisodeMetadataService.cs9
-rw-r--r--MediaBrowser.Providers/TV/EpisodeXmlProvider.cs40
-rw-r--r--MediaBrowser.Providers/TV/SeasonXmlProvider.cs39
-rw-r--r--MediaBrowser.Providers/TV/SeriesPostScanTask.cs15
-rw-r--r--MediaBrowser.Providers/TV/SeriesXmlProvider.cs39
-rw-r--r--MediaBrowser.Providers/TV/TvdbEpisodeProvider.cs9
-rw-r--r--MediaBrowser.Providers/Videos/VideoMetadataService.cs52
-rw-r--r--MediaBrowser.Providers/VirtualItemImageValidator.cs57
-rw-r--r--MediaBrowser.Providers/Years/YearMetadataService.cs43
-rw-r--r--MediaBrowser.Server.Implementations/EntryPoints/LibraryChangedNotifier.cs17
-rw-r--r--MediaBrowser.Server.Implementations/IO/LibraryMonitor.cs13
-rw-r--r--MediaBrowser.Server.Implementations/Library/LibraryManager.cs18
-rw-r--r--MediaBrowser.Server.Implementations/Library/ResolverHelper.cs2
-rw-r--r--MediaBrowser.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs2
-rw-r--r--MediaBrowser.Server.Implementations/Library/Resolvers/TV/SeasonResolver.cs12
-rw-r--r--MediaBrowser.Server.Implementations/Library/Resolvers/TV/SeriesResolver.cs2
-rw-r--r--MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs12
-rw-r--r--MediaBrowser.ServerApplication/ApplicationHost.cs4
-rw-r--r--MediaBrowser.sln3
94 files changed, 1671 insertions, 5363 deletions
diff --git a/MediaBrowser.Api/Library/LibraryService.cs b/MediaBrowser.Api/Library/LibraryService.cs
index 79b2651d5..17520ba1c 100644
--- a/MediaBrowser.Api/Library/LibraryService.cs
+++ b/MediaBrowser.Api/Library/LibraryService.cs
@@ -75,15 +75,7 @@ namespace MediaBrowser.Api.Library
if (locationType != LocationType.Remote && locationType != LocationType.Virtual)
{
- try
- {
- return c.PhysicalLocations;
- }
- catch (Exception ex)
- {
- Logger.ErrorException("Error getting ResolveArgs for {0}", ex, c.Path);
- }
-
+ return c.PhysicalLocations;
}
return new List<string>();
diff --git a/MediaBrowser.Api/LibraryService.cs b/MediaBrowser.Api/LibraryService.cs
index 584bbf9a1..a7a58dbfb 100644
--- a/MediaBrowser.Api/LibraryService.cs
+++ b/MediaBrowser.Api/LibraryService.cs
@@ -331,21 +331,8 @@ namespace MediaBrowser.Api
{
if (item.Parent is AggregateFolder)
{
- return user.RootFolder.GetChildren(user, true).FirstOrDefault(i =>
- {
-
- try
- {
- return i.LocationType == LocationType.FileSystem &&
- i.PhysicalLocations.Contains(item.Path);
- }
- catch (Exception ex)
- {
- Logger.ErrorException("Error getting ResolveArgs for {0}", ex, i.Path);
- return false;
- }
-
- });
+ return user.RootFolder.GetChildren(user, true).FirstOrDefault(i => i.LocationType == LocationType.FileSystem &&
+ i.PhysicalLocations.Contains(item.Path));
}
return item;
diff --git a/MediaBrowser.Controller/Entities/AdultVideo.cs b/MediaBrowser.Controller/Entities/AdultVideo.cs
index f81cfa1f6..475d7bc54 100644
--- a/MediaBrowser.Controller/Entities/AdultVideo.cs
+++ b/MediaBrowser.Controller/Entities/AdultVideo.cs
@@ -3,6 +3,10 @@ namespace MediaBrowser.Controller.Entities
{
public class AdultVideo : Video, IHasPreferredMetadataLanguage
{
+ /// <summary>
+ /// Gets or sets the preferred metadata language.
+ /// </summary>
+ /// <value>The preferred metadata language.</value>
public string PreferredMetadataLanguage { get; set; }
/// <summary>
diff --git a/MediaBrowser.Controller/Entities/AggregateFolder.cs b/MediaBrowser.Controller/Entities/AggregateFolder.cs
index 302842e7e..ef455846e 100644
--- a/MediaBrowser.Controller/Entities/AggregateFolder.cs
+++ b/MediaBrowser.Controller/Entities/AggregateFolder.cs
@@ -1,7 +1,11 @@
-using System;
+using MediaBrowser.Controller.IO;
+using MediaBrowser.Controller.Library;
+using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
+using System.IO;
using System.Linq;
+using System.Runtime.Serialization;
namespace MediaBrowser.Controller.Entities
{
@@ -11,6 +15,11 @@ namespace MediaBrowser.Controller.Entities
/// </summary>
public class AggregateFolder : Folder
{
+ public AggregateFolder()
+ {
+ PhysicalLocationsList = new List<string>();
+ }
+
/// <summary>
/// We don't support manual shortcuts
/// </summary>
@@ -36,6 +45,60 @@ namespace MediaBrowser.Controller.Entities
get { return _virtualChildren; }
}
+ [IgnoreDataMember]
+ public override IEnumerable<string> PhysicalLocations
+ {
+ get
+ {
+ return PhysicalLocationsList;
+ }
+ }
+
+ public List<string> PhysicalLocationsList { get; set; }
+
+ protected override IEnumerable<FileSystemInfo> GetFileSystemChildren()
+ {
+ return CreateResolveArgs().FileSystemChildren;
+ }
+
+ private ItemResolveArgs CreateResolveArgs()
+ {
+ var path = ContainingFolderPath;
+
+ var args = new ItemResolveArgs(ConfigurationManager.ApplicationPaths, LibraryManager)
+ {
+ FileInfo = new DirectoryInfo(path),
+ Path = path,
+ Parent = Parent
+ };
+
+ // Gather child folder and files
+ if (args.IsDirectory)
+ {
+ var isPhysicalRoot = args.IsPhysicalRoot;
+
+ // When resolving the root, we need it's grandchildren (children of user views)
+ var flattenFolderDepth = isPhysicalRoot ? 2 : 0;
+
+ var fileSystemDictionary = FileData.GetFilteredFileSystemEntries(args.Path, FileSystem, Logger, args, flattenFolderDepth: flattenFolderDepth, resolveShortcuts: isPhysicalRoot || args.IsVf);
+
+ // Need to remove subpaths that may have been resolved from shortcuts
+ // Example: if \\server\movies exists, then strip out \\server\movies\action
+ if (isPhysicalRoot)
+ {
+ var paths = LibraryManager.NormalizeRootPathList(fileSystemDictionary.Keys);
+
+ fileSystemDictionary = paths.Select(i => (FileSystemInfo)new DirectoryInfo(i)).ToDictionary(i => i.FullName);
+ }
+
+ args.FileSystemDictionary = fileSystemDictionary;
+ }
+
+ PhysicalLocationsList = args.PhysicalLocations.ToList();
+
+ return args;
+ }
+
/// <summary>
/// Adds the virtual child.
/// </summary>
diff --git a/MediaBrowser.Controller/Entities/BaseItem.cs b/MediaBrowser.Controller/Entities/BaseItem.cs
index 650a9bad0..de5516e29 100644
--- a/MediaBrowser.Controller/Entities/BaseItem.cs
+++ b/MediaBrowser.Controller/Entities/BaseItem.cs
@@ -1,12 +1,10 @@
using MediaBrowser.Common.Extensions;
using MediaBrowser.Common.IO;
using MediaBrowser.Controller.Configuration;
-using MediaBrowser.Controller.IO;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Localization;
using MediaBrowser.Controller.Persistence;
using MediaBrowser.Controller.Providers;
-using MediaBrowser.Controller.Resolvers;
using MediaBrowser.Model.Configuration;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Logging;
@@ -103,6 +101,35 @@ namespace MediaBrowser.Controller.Entities
protected internal bool IsOffline { get; set; }
/// <summary>
+ /// Returns the folder containing the item.
+ /// If the item is a folder, it returns the folder itself
+ /// </summary>
+ [IgnoreDataMember]
+ public virtual string ContainingFolderPath
+ {
+ get
+ {
+ if (IsFolder)
+ {
+ return Path;
+ }
+
+ return System.IO.Path.GetDirectoryName(Path);
+ }
+ }
+
+ [IgnoreDataMember]
+ public bool IsOwnedItem
+ {
+ get
+ {
+ // Local trailer, special feature, theme video, etc.
+ // An item that belongs to another item but is not part of the Parent-Child tree
+ return !IsFolder && Parent == null;
+ }
+ }
+
+ /// <summary>
/// Gets or sets the type of the location.
/// </summary>
/// <value>The type of the location.</value>
@@ -190,19 +217,6 @@ namespace MediaBrowser.Controller.Entities
public List<MetadataFields> LockedFields { get; set; }
/// <summary>
- /// Should be overridden to return the proper folder where metadata lives
- /// </summary>
- /// <value>The meta location.</value>
- [IgnoreDataMember]
- public virtual string MetaLocation
- {
- get
- {
- return Path ?? "";
- }
- }
-
- /// <summary>
/// Gets the type of the media.
/// </summary>
/// <value>The type of the media.</value>
@@ -215,160 +229,19 @@ namespace MediaBrowser.Controller.Entities
}
}
- /// <summary>
- /// The _resolve args
- /// </summary>
- private ItemResolveArgs _resolveArgs;
- /// <summary>
- /// We attach these to the item so that we only ever have to hit the file system once
- /// (this includes the children of the containing folder)
- /// </summary>
- /// <value>The resolve args.</value>
[IgnoreDataMember]
- public ItemResolveArgs ResolveArgs
+ public virtual IEnumerable<string> PhysicalLocations
{
get
{
- if (_resolveArgs == null)
- {
- try
- {
- _resolveArgs = CreateResolveArgs();
- }
- catch (IOException ex)
- {
- Logger.ErrorException("Error creating resolve args for {0}", ex, Path);
-
- IsOffline = true;
+ var locationType = LocationType;
- throw;
- }
- }
-
- return _resolveArgs;
- }
- set
- {
- _resolveArgs = value;
- }
- }
-
- [IgnoreDataMember]
- public IEnumerable<string> PhysicalLocations
- {
- get
- {
- return ResolveArgs.PhysicalLocations;
- }
- }
-
- /// <summary>
- /// Resets the resolve args.
- /// </summary>
- /// <param name="pathInfo">The path info.</param>
- public void ResetResolveArgs(FileSystemInfo pathInfo)
- {
- ResetResolveArgs(CreateResolveArgs(pathInfo));
- }
-
- /// <summary>
- /// Resets the resolve args.
- /// </summary>
- public void ResetResolveArgs()
- {
- _resolveArgs = null;
- }
-
- /// <summary>
- /// Resets the resolve args.
- /// </summary>
- /// <param name="args">The args.</param>
- public void ResetResolveArgs(ItemResolveArgs args)
- {
- _resolveArgs = args;
- }
-
- /// <summary>
- /// Creates ResolveArgs on demand
- /// </summary>
- /// <param name="pathInfo">The path info.</param>
- /// <returns>ItemResolveArgs.</returns>
- /// <exception cref="System.IO.IOException">Unable to retrieve file system info for + path</exception>
- protected internal virtual ItemResolveArgs CreateResolveArgs(FileSystemInfo pathInfo = null)
- {
- var path = Path;
-
- var locationType = LocationType;
-
- if (locationType == LocationType.Remote ||
- locationType == LocationType.Virtual)
- {
- return new ItemResolveArgs(ConfigurationManager.ApplicationPaths, LibraryManager);
- }
-
- var isDirectory = false;
-
- if (UseParentPathToCreateResolveArgs)
- {
- path = System.IO.Path.GetDirectoryName(path);
- isDirectory = true;
- }
-
- pathInfo = pathInfo ?? (isDirectory ? new DirectoryInfo(path) : FileSystem.GetFileSystemInfo(path));
-
- if (pathInfo == null || !pathInfo.Exists)
- {
- throw new IOException("Unable to retrieve file system info for " + path);
- }
-
- var args = new ItemResolveArgs(ConfigurationManager.ApplicationPaths, LibraryManager)
- {
- FileInfo = pathInfo,
- Path = path,
- Parent = Parent
- };
-
- // Gather child folder and files
- if (args.IsDirectory)
- {
- var isPhysicalRoot = args.IsPhysicalRoot;
-
- // When resolving the root, we need it's grandchildren (children of user views)
- var flattenFolderDepth = isPhysicalRoot ? 2 : 0;
-
- var fileSystemDictionary = FileData.GetFilteredFileSystemEntries(args.Path, FileSystem, Logger, args, flattenFolderDepth: flattenFolderDepth, resolveShortcuts: isPhysicalRoot || args.IsVf);
-
- // Need to remove subpaths that may have been resolved from shortcuts
- // Example: if \\server\movies exists, then strip out \\server\movies\action
- if (isPhysicalRoot)
+ if (locationType != LocationType.Remote && locationType != LocationType.Virtual)
{
- var paths = LibraryManager.NormalizeRootPathList(fileSystemDictionary.Keys);
-
- fileSystemDictionary = paths.Select(i => (FileSystemInfo)new DirectoryInfo(i)).ToDictionary(i => i.FullName);
+ return new string[] { };
}
- args.FileSystemDictionary = fileSystemDictionary;
- }
-
- //update our dates
- EntityResolutionHelper.EnsureDates(FileSystem, this, args, false);
-
- IsOffline = false;
-
- return args;
- }
-
- /// <summary>
- /// Some subclasses will stop resolving at a directory and point their Path to a file within. This will help ensure the on-demand resolve args are identical to the
- /// original ones.
- /// </summary>
- /// <value><c>true</c> if [use parent path to create resolve args]; otherwise, <c>false</c>.</value>
- [IgnoreDataMember]
- protected virtual bool UseParentPathToCreateResolveArgs
- {
- get
- {
- return false;
+ return new[] { Path };
}
}
@@ -583,122 +456,93 @@ namespace MediaBrowser.Controller.Entities
/// Loads local trailers from the file system
/// </summary>
/// <returns>List{Video}.</returns>
- private IEnumerable<Trailer> LoadLocalTrailers()
- {
- ItemResolveArgs resolveArgs;
-
- try
- {
- resolveArgs = ResolveArgs;
-
- if (!resolveArgs.IsDirectory)
- {
- return new List<Trailer>();
- }
- }
- catch (IOException ex)
- {
- Logger.ErrorException("Error getting ResolveArgs for {0}", ex, Path);
- return new List<Trailer>();
- }
-
- var files = new List<FileSystemInfo>();
-
- var folder = resolveArgs.GetFileSystemEntryByName(TrailerFolderName);
-
- // Path doesn't exist. No biggie
- if (folder != null)
- {
- try
- {
- files.AddRange(new DirectoryInfo(folder.FullName).EnumerateFiles());
- }
- catch (IOException ex)
- {
- Logger.ErrorException("Error loading trailers for {0}", ex, Name);
- }
- }
-
- // Support xbmc trailers (-trailer suffix on video file names)
- files.AddRange(resolveArgs.FileSystemChildren.Where(i =>
- {
- try
- {
- if ((i.Attributes & FileAttributes.Directory) != FileAttributes.Directory)
- {
- if (System.IO.Path.GetFileNameWithoutExtension(i.Name).EndsWith(XbmcTrailerFileSuffix, StringComparison.OrdinalIgnoreCase) && !string.Equals(Path, i.FullName, StringComparison.OrdinalIgnoreCase))
- {
- return true;
- }
- }
- }
- catch (IOException ex)
- {
- Logger.ErrorException("Error accessing path {0}", ex, i.FullName);
- }
-
- return false;
- }));
-
- return LibraryManager.ResolvePaths<Trailer>(files, null).Select(video =>
- {
- // Try to retrieve it from the db. If we don't find it, use the resolved version
- var dbItem = LibraryManager.GetItemById(video.Id) as Trailer;
-
- if (dbItem != null)
- {
- dbItem.ResetResolveArgs(video.ResolveArgs);
- video = dbItem;
- }
-
- return video;
-
- }).ToList();
+ private IEnumerable<Trailer> LoadLocalTrailers(List<FileSystemInfo> fileSystemChildren)
+ {
+ return new List<Trailer>();
+ //ItemResolveArgs resolveArgs;
+
+ //try
+ //{
+ // resolveArgs = ResolveArgs;
+
+ // if (!resolveArgs.IsDirectory)
+ // {
+ // return new List<Trailer>();
+ // }
+ //}
+ //catch (IOException ex)
+ //{
+ // Logger.ErrorException("Error getting ResolveArgs for {0}", ex, Path);
+ // return new List<Trailer>();
+ //}
+
+ //var files = new List<FileSystemInfo>();
+
+ //var folder = resolveArgs.GetFileSystemEntryByName(TrailerFolderName);
+
+ //// Path doesn't exist. No biggie
+ //if (folder != null)
+ //{
+ // try
+ // {
+ // files.AddRange(new DirectoryInfo(folder.FullName).EnumerateFiles());
+ // }
+ // catch (IOException ex)
+ // {
+ // Logger.ErrorException("Error loading trailers for {0}", ex, Name);
+ // }
+ //}
+
+ //// Support xbmc trailers (-trailer suffix on video file names)
+ //files.AddRange(resolveArgs.FileSystemChildren.Where(i =>
+ //{
+ // try
+ // {
+ // if ((i.Attributes & FileAttributes.Directory) != FileAttributes.Directory)
+ // {
+ // if (System.IO.Path.GetFileNameWithoutExtension(i.Name).EndsWith(XbmcTrailerFileSuffix, StringComparison.OrdinalIgnoreCase) && !string.Equals(Path, i.FullName, StringComparison.OrdinalIgnoreCase))
+ // {
+ // return true;
+ // }
+ // }
+ // }
+ // catch (IOException ex)
+ // {
+ // Logger.ErrorException("Error accessing path {0}", ex, i.FullName);
+ // }
+
+ // return false;
+ //}));
+
+ //return LibraryManager.ResolvePaths<Trailer>(files, null).Select(video =>
+ //{
+ // // Try to retrieve it from the db. If we don't find it, use the resolved version
+ // var dbItem = LibraryManager.GetItemById(video.Id) as Trailer;
+
+ // if (dbItem != null)
+ // {
+ // video = dbItem;
+ // }
+
+ // return video;
+
+ //}).ToList();
}
/// <summary>
/// Loads the theme songs.
/// </summary>
/// <returns>List{Audio.Audio}.</returns>
- private IEnumerable<Audio.Audio> LoadThemeSongs()
+ private IEnumerable<Audio.Audio> LoadThemeSongs(List<FileSystemInfo> fileSystemChildren)
{
- ItemResolveArgs resolveArgs;
-
- try
- {
- resolveArgs = ResolveArgs;
-
- if (!resolveArgs.IsDirectory)
- {
- return new List<Audio.Audio>();
- }
- }
- catch (IOException ex)
- {
- Logger.ErrorException("Error getting ResolveArgs for {0}", ex, Path);
- return new List<Audio.Audio>();
- }
-
- var files = new List<FileSystemInfo>();
-
- var folder = resolveArgs.GetFileSystemEntryByName(ThemeSongsFolderName);
-
- // Path doesn't exist. No biggie
- if (folder != null)
- {
- try
- {
- files.AddRange(new DirectoryInfo(folder.FullName).EnumerateFiles());
- }
- catch (IOException ex)
- {
- Logger.ErrorException("Error loading theme songs for {0}", ex, Name);
- }
- }
+ var files = fileSystemChildren.OfType<DirectoryInfo>()
+ .Where(i => string.Equals(i.Name, ThemeSongsFolderName, StringComparison.OrdinalIgnoreCase))
+ .SelectMany(i => i.EnumerateFiles("*", SearchOption.TopDirectoryOnly))
+ .ToList();
// Support plex/xbmc convention
- files.AddRange(resolveArgs.FileSystemChildren
- .Where(i => string.Equals(System.IO.Path.GetFileNameWithoutExtension(i.Name), ThemeSongFilename, StringComparison.OrdinalIgnoreCase) && EntityResolutionHelper.IsAudioFile(i.Name))
+ files.AddRange(fileSystemChildren.OfType<FileInfo>()
+ .Where(i => string.Equals(System.IO.Path.GetFileNameWithoutExtension(i.Name), ThemeSongFilename, StringComparison.OrdinalIgnoreCase))
);
return LibraryManager.ResolvePaths<Audio.Audio>(files, null).Select(audio =>
@@ -708,7 +552,6 @@ namespace MediaBrowser.Controller.Entities
if (dbItem != null)
{
- dbItem.ResetResolveArgs(audio.ResolveArgs);
audio = dbItem;
}
@@ -720,44 +563,11 @@ namespace MediaBrowser.Controller.Entities
/// Loads the video backdrops.
/// </summary>
/// <returns>List{Video}.</returns>
- private IEnumerable<Video> LoadThemeVideos()
+ private IEnumerable<Video> LoadThemeVideos(IEnumerable<FileSystemInfo> fileSystemChildren)
{
- ItemResolveArgs resolveArgs;
-
- try
- {
- resolveArgs = ResolveArgs;
-
- if (!resolveArgs.IsDirectory)
- {
- return new List<Video>();
- }
- }
- catch (IOException ex)
- {
- Logger.ErrorException("Error getting ResolveArgs for {0}", ex, Path);
- return new List<Video>();
- }
-
- var folder = resolveArgs.GetFileSystemEntryByName(ThemeVideosFolderName);
-
- // Path doesn't exist. No biggie
- if (folder == null)
- {
- return new List<Video>();
- }
-
- IEnumerable<FileSystemInfo> files;
-
- try
- {
- files = new DirectoryInfo(folder.FullName).EnumerateFiles();
- }
- catch (IOException ex)
- {
- Logger.ErrorException("Error loading video backdrops for {0}", ex, Name);
- return new List<Video>();
- }
+ var files = fileSystemChildren.OfType<DirectoryInfo>()
+ .Where(i => string.Equals(i.Name, ThemeVideosFolderName, StringComparison.OrdinalIgnoreCase))
+ .SelectMany(i => i.EnumerateFiles("*", SearchOption.TopDirectoryOnly));
return LibraryManager.ResolvePaths<Video>(files, null).Select(item =>
{
@@ -766,7 +576,6 @@ namespace MediaBrowser.Controller.Entities
if (dbItem != null)
{
- dbItem.ResetResolveArgs(item.ResolveArgs);
item = dbItem;
}
@@ -774,9 +583,9 @@ namespace MediaBrowser.Controller.Entities
}).ToList();
}
- public Task<bool> RefreshMetadata(CancellationToken cancellationToken, bool resetResolveArgs = true)
+ public Task RefreshMetadata(CancellationToken cancellationToken)
{
- return RefreshMetadata(new MetadataRefreshOptions { ResetResolveArgs = resetResolveArgs }, cancellationToken);
+ return RefreshMetadata(new MetadataRefreshOptions(), cancellationToken);
}
/// <summary>
@@ -785,35 +594,24 @@ namespace MediaBrowser.Controller.Entities
/// <param name="options">The options.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>true if a provider reports we changed</returns>
- public async Task<bool> RefreshMetadata(MetadataRefreshOptions options, CancellationToken cancellationToken)
+ public async Task RefreshMetadata(MetadataRefreshOptions options, CancellationToken cancellationToken)
{
- if (options.ResetResolveArgs)
+ var locationType = LocationType;
+
+ if (IsFolder || Parent != null)
{
- // Reload this
- ResetResolveArgs();
- }
+ var files = locationType == LocationType.FileSystem || locationType == LocationType.Offline ?
+ GetFileSystemChildren().ToList() :
+ new List<FileSystemInfo>();
- await BeforeRefreshMetadata(options, cancellationToken).ConfigureAwait(false);
+ await BeforeRefreshMetadata(options, files, cancellationToken).ConfigureAwait(false);
+ }
await ProviderManager.RefreshMetadata(this, options, cancellationToken).ConfigureAwait(false);
-
- return false;
}
- private readonly Task _cachedTask = Task.FromResult(true);
- protected virtual Task BeforeRefreshMetadata(MetadataRefreshOptions options, CancellationToken cancellationToken)
+ protected virtual async Task BeforeRefreshMetadata(MetadataRefreshOptions options, List<FileSystemInfo> fileSystemChildren, CancellationToken cancellationToken)
{
- return _cachedTask;
- }
-
- [Obsolete]
- public virtual async Task<bool> RefreshMetadataDirect(CancellationToken cancellationToken, bool forceSave = false, bool forceRefresh = false)
- {
- // Refresh for the item
- var itemRefreshTask = ProviderManager.ExecuteMetadataProviders(this, cancellationToken, forceRefresh);
-
- cancellationToken.ThrowIfCancellationRequested();
-
var themeSongsChanged = false;
var themeVideosChanged = false;
@@ -825,102 +623,83 @@ namespace MediaBrowser.Controller.Entities
var hasThemeMedia = this as IHasThemeMedia;
if (hasThemeMedia != null)
{
- themeSongsChanged = await RefreshThemeSongs(hasThemeMedia, cancellationToken, forceSave, forceRefresh).ConfigureAwait(false);
+ if (!IsInMixedFolder)
+ {
+ themeSongsChanged = await RefreshThemeSongs(hasThemeMedia, options, fileSystemChildren, cancellationToken).ConfigureAwait(false);
- themeVideosChanged = await RefreshThemeVideos(hasThemeMedia, cancellationToken, forceSave, forceRefresh).ConfigureAwait(false);
+ themeVideosChanged = await RefreshThemeVideos(hasThemeMedia, options, fileSystemChildren, cancellationToken).ConfigureAwait(false);
+ }
}
var hasTrailers = this as IHasTrailers;
if (hasTrailers != null)
{
- localTrailersChanged = await RefreshLocalTrailers(hasTrailers, cancellationToken, forceSave, forceRefresh).ConfigureAwait(false);
+ localTrailersChanged = await RefreshLocalTrailers(hasTrailers, options, fileSystemChildren, cancellationToken).ConfigureAwait(false);
}
}
-
- cancellationToken.ThrowIfCancellationRequested();
-
- // Get the result from the item task
- var updateReason = await itemRefreshTask.ConfigureAwait(false);
-
- var changed = updateReason.HasValue;
-
- if (changed || forceSave || themeSongsChanged || themeVideosChanged || localTrailersChanged)
+
+ if (themeSongsChanged || themeVideosChanged || localTrailersChanged)
{
- cancellationToken.ThrowIfCancellationRequested();
-
- await LibraryManager.UpdateItem(this, updateReason ?? ItemUpdateType.Unspecified, cancellationToken).ConfigureAwait(false);
+ options.ForceSave = true;
}
+ }
- return changed;
+ protected virtual IEnumerable<FileSystemInfo> GetFileSystemChildren()
+ {
+ var path = ContainingFolderPath;
+
+ return new DirectoryInfo(path).EnumerateFileSystemInfos("*", SearchOption.TopDirectoryOnly);
}
- private async Task<bool> RefreshLocalTrailers(IHasTrailers item, CancellationToken cancellationToken, bool forceSave = false, bool forceRefresh = false)
+ private async Task<bool> RefreshLocalTrailers(IHasTrailers item, MetadataRefreshOptions options, List<FileSystemInfo> fileSystemChildren, CancellationToken cancellationToken)
{
- var newItems = LoadLocalTrailers().ToList();
+ var newItems = LoadLocalTrailers(fileSystemChildren).ToList();
var newItemIds = newItems.Select(i => i.Id).ToList();
var itemsChanged = !item.LocalTrailerIds.SequenceEqual(newItemIds);
- var tasks = newItems.Select(i => i.RefreshMetadata(new MetadataRefreshOptions
- {
- ForceSave = forceSave,
- ReplaceAllMetadata = forceRefresh,
- ResetResolveArgs = false
+ var tasks = newItems.Select(i => i.RefreshMetadata(options, cancellationToken));
- }, cancellationToken));
-
- var results = await Task.WhenAll(tasks).ConfigureAwait(false);
+ await Task.WhenAll(tasks).ConfigureAwait(false);
item.LocalTrailerIds = newItemIds;
- return itemsChanged || results.Contains(true);
+ return itemsChanged;
}
- private async Task<bool> RefreshThemeVideos(IHasThemeMedia item, CancellationToken cancellationToken, bool forceSave = false, bool forceRefresh = false)
+ private async Task<bool> RefreshThemeVideos(IHasThemeMedia item, MetadataRefreshOptions options, IEnumerable<FileSystemInfo> fileSystemChildren, CancellationToken cancellationToken)
{
- var newThemeVideos = LoadThemeVideos().ToList();
+ var newThemeVideos = LoadThemeVideos(fileSystemChildren).ToList();
var newThemeVideoIds = newThemeVideos.Select(i => i.Id).ToList();
var themeVideosChanged = !item.ThemeVideoIds.SequenceEqual(newThemeVideoIds);
- var tasks = newThemeVideos.Select(i => i.RefreshMetadata(new MetadataRefreshOptions
- {
- ForceSave = forceSave,
- ReplaceAllMetadata = forceRefresh,
- ResetResolveArgs = false
-
- }, cancellationToken));
+ var tasks = newThemeVideos.Select(i => i.RefreshMetadata(options, cancellationToken));
- var results = await Task.WhenAll(tasks).ConfigureAwait(false);
+ await Task.WhenAll(tasks).ConfigureAwait(false);
item.ThemeVideoIds = newThemeVideoIds;
- return themeVideosChanged || results.Contains(true);
+ return themeVideosChanged;
}
/// <summary>
/// Refreshes the theme songs.
/// </summary>
- private async Task<bool> RefreshThemeSongs(IHasThemeMedia item, CancellationToken cancellationToken, bool forceSave = false, bool forceRefresh = false)
+ private async Task<bool> RefreshThemeSongs(IHasThemeMedia item, MetadataRefreshOptions options, List<FileSystemInfo> fileSystemChildren, CancellationToken cancellationToken)
{
- var newThemeSongs = LoadThemeSongs().ToList();
+ var newThemeSongs = LoadThemeSongs(fileSystemChildren).ToList();
var newThemeSongIds = newThemeSongs.Select(i => i.Id).ToList();
var themeSongsChanged = !item.ThemeSongIds.SequenceEqual(newThemeSongIds);
- var tasks = newThemeSongs.Select(i => i.RefreshMetadata(new MetadataRefreshOptions
- {
- ForceSave = forceSave,
- ReplaceAllMetadata = forceRefresh,
- ResetResolveArgs = false
-
- }, cancellationToken));
+ var tasks = newThemeSongs.Select(i => i.RefreshMetadata(options, cancellationToken));
- var results = await Task.WhenAll(tasks).ConfigureAwait(false);
+ await Task.WhenAll(tasks).ConfigureAwait(false);
item.ThemeSongIds = newThemeSongIds;
- return themeSongsChanged || results.Contains(true);
+ return themeSongsChanged;
}
/// <summary>
@@ -1655,27 +1434,8 @@ namespace MediaBrowser.Controller.Entities
throw new ArgumentNullException("imagePath");
}
- var locationType = LocationType;
-
- if (locationType == LocationType.Remote ||
- locationType == LocationType.Virtual)
- {
- return FileSystem.GetLastWriteTimeUtc(imagePath);
- }
-
- var metaFileEntry = ResolveArgs.GetMetaFileByPath(imagePath);
-
- // If we didn't the metafile entry, check the Season
- if (metaFileEntry == null)
- {
- if (Parent != null)
- {
- metaFileEntry = Parent.ResolveArgs.GetMetaFileByPath(imagePath);
- }
- }
-
// See if we can avoid a file system lookup by looking for the file in ResolveArgs
- return metaFileEntry == null ? FileSystem.GetLastWriteTimeUtc(imagePath) : FileSystem.GetLastWriteTimeUtc(metaFileEntry);
+ return FileSystem.GetLastWriteTimeUtc(imagePath);
}
/// <summary>
diff --git a/MediaBrowser.Controller/Entities/Book.cs b/MediaBrowser.Controller/Entities/Book.cs
index 298941378..28ccf687c 100644
--- a/MediaBrowser.Controller/Entities/Book.cs
+++ b/MediaBrowser.Controller/Entities/Book.cs
@@ -29,25 +29,6 @@ namespace MediaBrowser.Controller.Entities
/// <value>The preferred metadata country code.</value>
public string PreferredMetadataCountryCode { get; set; }
- /// <summary>
- ///
- /// </summary>
- public override string MetaLocation
- {
- get
- {
- return System.IO.Path.GetDirectoryName(Path);
- }
- }
-
- protected override bool UseParentPathToCreateResolveArgs
- {
- get
- {
- return !IsInMixedFolder;
- }
- }
-
public Book()
{
Tags = new List<string>();
diff --git a/MediaBrowser.Controller/Entities/CollectionFolder.cs b/MediaBrowser.Controller/Entities/CollectionFolder.cs
index 6220bc4d5..9c6b60969 100644
--- a/MediaBrowser.Controller/Entities/CollectionFolder.cs
+++ b/MediaBrowser.Controller/Entities/CollectionFolder.cs
@@ -1,4 +1,6 @@
-using System;
+using MediaBrowser.Controller.IO;
+using MediaBrowser.Controller.Library;
+using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
@@ -14,6 +16,11 @@ namespace MediaBrowser.Controller.Entities
/// </summary>
public class CollectionFolder : Folder, ICollectionFolder
{
+ public CollectionFolder()
+ {
+ PhysicalLocationsList = new List<string>();
+ }
+
/// <summary>
/// Gets a value indicating whether this instance is virtual folder.
/// </summary>
@@ -42,6 +49,60 @@ namespace MediaBrowser.Controller.Entities
}
}
+ [IgnoreDataMember]
+ public override IEnumerable<string> PhysicalLocations
+ {
+ get
+ {
+ return PhysicalLocationsList;
+ }
+ }
+
+ public List<string> PhysicalLocationsList { get; set; }
+
+ protected override IEnumerable<FileSystemInfo> GetFileSystemChildren()
+ {
+ return CreateResolveArgs().FileSystemChildren;
+ }
+
+ private ItemResolveArgs CreateResolveArgs()
+ {
+ var path = ContainingFolderPath;
+
+ var args = new ItemResolveArgs(ConfigurationManager.ApplicationPaths, LibraryManager)
+ {
+ FileInfo = new DirectoryInfo(path),
+ Path = path,
+ Parent = Parent
+ };
+
+ // Gather child folder and files
+ if (args.IsDirectory)
+ {
+ var isPhysicalRoot = args.IsPhysicalRoot;
+
+ // When resolving the root, we need it's grandchildren (children of user views)
+ var flattenFolderDepth = isPhysicalRoot ? 2 : 0;
+
+ var fileSystemDictionary = FileData.GetFilteredFileSystemEntries(args.Path, FileSystem, Logger, args, flattenFolderDepth: flattenFolderDepth, resolveShortcuts: isPhysicalRoot || args.IsVf);
+
+ // Need to remove subpaths that may have been resolved from shortcuts
+ // Example: if \\server\movies exists, then strip out \\server\movies\action
+ if (isPhysicalRoot)
+ {
+ var paths = LibraryManager.NormalizeRootPathList(fileSystemDictionary.Keys);
+
+ fileSystemDictionary = paths.Select(i => (FileSystemInfo)new DirectoryInfo(i)).ToDictionary(i => i.FullName);
+ }
+
+ args.FileSystemDictionary = fileSystemDictionary;
+ }
+
+ PhysicalLocationsList = args.PhysicalLocations.ToList();
+
+ return args;
+ }
+
// Cache this since it will be used a lot
/// <summary>
/// The null task result
@@ -59,13 +120,14 @@ namespace MediaBrowser.Controller.Entities
/// <returns>Task.</returns>
protected override Task ValidateChildrenInternal(IProgress<double> progress, CancellationToken cancellationToken, bool? recursive = null, bool forceRefreshMetadata = false)
{
+ CreateResolveArgs();
ResetDynamicChildren();
return NullTaskResult;
}
private List<LinkedChild> _linkedChildren;
-
+
/// <summary>
/// Our children are actually just references to the ones in the physical root...
/// </summary>
diff --git a/MediaBrowser.Controller/Entities/Folder.cs b/MediaBrowser.Controller/Entities/Folder.cs
index 02da2fe61..63a1c2bab 100644
--- a/MediaBrowser.Controller/Entities/Folder.cs
+++ b/MediaBrowser.Controller/Entities/Folder.cs
@@ -1,6 +1,7 @@
using MediaBrowser.Common.Extensions;
using MediaBrowser.Common.Progress;
using MediaBrowser.Controller.Entities.TV;
+using MediaBrowser.Controller.IO;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Localization;
using MediaBrowser.Controller.Providers;
@@ -27,7 +28,7 @@ namespace MediaBrowser.Controller.Entities
public List<Guid> ThemeSongIds { get; set; }
public List<Guid> ThemeVideoIds { get; set; }
-
+
public Folder()
{
LinkedChildren = new List<LinkedChild>();
@@ -379,7 +380,7 @@ namespace MediaBrowser.Controller.Entities
}
catch (IOException ex)
{
- nonCachedChildren = new BaseItem[] {};
+ nonCachedChildren = new BaseItem[] { };
Logger.ErrorException("Error getting file system entries for {0}", ex, Path);
}
@@ -402,8 +403,6 @@ namespace MediaBrowser.Controller.Entities
if (currentChildren.TryGetValue(child.Id, out currentChild))
{
- currentChild.ResetResolveArgs(child.ResolveArgs);
-
//existing item - check if it has changed
if (currentChild.HasChanged(child))
{
@@ -411,7 +410,7 @@ namespace MediaBrowser.Controller.Entities
if (currentChildLocationType != LocationType.Remote &&
currentChildLocationType != LocationType.Virtual)
{
- EntityResolutionHelper.EnsureDates(FileSystem, currentChild, child.ResolveArgs, false);
+ currentChild.DateModified = child.DateModified;
}
currentChild.IsInMixedFolder = child.IsInMixedFolder;
@@ -539,8 +538,7 @@ namespace MediaBrowser.Controller.Entities
await child.RefreshMetadata(new MetadataRefreshOptions
{
ForceSave = currentTuple.Item2,
- ReplaceAllMetadata = forceRefreshMetadata,
- ResetResolveArgs = false
+ ReplaceAllMetadata = forceRefreshMetadata
}, cancellationToken).ConfigureAwait(false);
}
@@ -581,16 +579,6 @@ namespace MediaBrowser.Controller.Entities
});
await ((Folder)child).ValidateChildren(innerProgress, cancellationToken, recursive, forceRefreshMetadata).ConfigureAwait(false);
-
- try
- {
- // Some folder providers are unable to refresh until children have been refreshed.
- await child.RefreshMetadata(cancellationToken, resetResolveArgs: false).ConfigureAwait(false);
- }
- catch (IOException ex)
- {
- Logger.ErrorException("Error refreshing {0}", ex, child.Path ?? child.Name);
- }
}
else
{
@@ -661,14 +649,7 @@ namespace MediaBrowser.Controller.Entities
/// <returns>IEnumerable{BaseItem}.</returns>
protected virtual IEnumerable<BaseItem> GetNonCachedChildren()
{
- var resolveArgs = ResolveArgs;
-
- if (resolveArgs == null || resolveArgs.FileSystemDictionary == null)
- {
- Logger.Error("ResolveArgs null for {0}", Path);
- }
-
- return LibraryManager.ResolvePaths<BaseItem>(resolveArgs.FileSystemChildren, this);
+ return LibraryManager.ResolvePaths<BaseItem>(GetFileSystemChildren(), this);
}
/// <summary>
@@ -914,43 +895,29 @@ namespace MediaBrowser.Controller.Entities
return item;
}
- protected override Task BeforeRefreshMetadata(MetadataRefreshOptions options, CancellationToken cancellationToken)
+ protected override Task BeforeRefreshMetadata(MetadataRefreshOptions options, List<FileSystemInfo> fileSystemChildren, CancellationToken cancellationToken)
{
if (SupportsShortcutChildren && LocationType == LocationType.FileSystem)
{
- RefreshLinkedChildren();
+ if (RefreshLinkedChildren(fileSystemChildren))
+ {
+ options.ForceSave = true;
+ }
}
- return base.BeforeRefreshMetadata(options, cancellationToken);
+ return base.BeforeRefreshMetadata(options, fileSystemChildren, cancellationToken);
}
/// <summary>
/// Refreshes the linked children.
/// </summary>
/// <returns><c>true</c> if XXXX, <c>false</c> otherwise</returns>
- private bool RefreshLinkedChildren()
+ private bool RefreshLinkedChildren(IEnumerable<FileSystemInfo> fileSystemChildren)
{
- ItemResolveArgs resolveArgs;
-
- try
- {
- resolveArgs = ResolveArgs;
-
- if (!resolveArgs.IsDirectory)
- {
- return false;
- }
- }
- catch (IOException ex)
- {
- Logger.ErrorException("Error getting ResolveArgs for {0}", ex, Path);
- return false;
- }
-
var currentManualLinks = LinkedChildren.Where(i => i.Type == LinkedChildType.Manual).ToList();
var currentShortcutLinks = LinkedChildren.Where(i => i.Type == LinkedChildType.Shortcut).ToList();
- var newShortcutLinks = resolveArgs.FileSystemChildren
+ var newShortcutLinks = fileSystemChildren
.Where(i => (i.Attributes & FileAttributes.Directory) != FileAttributes.Directory && FileSystem.IsShortcut(i.FullName))
.Select(i =>
{
@@ -1058,7 +1025,7 @@ namespace MediaBrowser.Controller.Entities
return this;
}
- if (locationType != LocationType.Virtual && ResolveArgs.PhysicalLocations.Contains(path, StringComparer.OrdinalIgnoreCase))
+ if (locationType != LocationType.Virtual && PhysicalLocations.Contains(path, StringComparer.OrdinalIgnoreCase))
{
return this;
}
diff --git a/MediaBrowser.Controller/Entities/Game.cs b/MediaBrowser.Controller/Entities/Game.cs
index da95b7c44..1b5176362 100644
--- a/MediaBrowser.Controller/Entities/Game.cs
+++ b/MediaBrowser.Controller/Entities/Game.cs
@@ -80,17 +80,6 @@ namespace MediaBrowser.Controller.Entities
public string GameSystem { get; set; }
/// <summary>
- ///
- /// </summary>
- public override string MetaLocation
- {
- get
- {
- return System.IO.Path.GetDirectoryName(Path);
- }
- }
-
- /// <summary>
/// Gets or sets a value indicating whether this instance is multi part.
/// </summary>
/// <value><c>true</c> if this instance is multi part; otherwise, <c>false</c>.</value>
@@ -101,17 +90,6 @@ namespace MediaBrowser.Controller.Entities
/// </summary>
public List<string> MultiPartGameFiles { get; set; }
- /// <summary>
- ///
- /// </summary>
- protected override bool UseParentPathToCreateResolveArgs
- {
- get
- {
- return !IsInMixedFolder;
- }
- }
-
public override string GetUserDataKey()
{
var id = this.GetProviderId(MetadataProviders.Gamesdb);
diff --git a/MediaBrowser.Controller/Entities/IHasImages.cs b/MediaBrowser.Controller/Entities/IHasImages.cs
index 1aa299c2a..51f25979c 100644
--- a/MediaBrowser.Controller/Entities/IHasImages.cs
+++ b/MediaBrowser.Controller/Entities/IHasImages.cs
@@ -99,6 +99,12 @@ namespace MediaBrowser.Controller.Entities
/// </summary>
/// <value>The backdrop image paths.</value>
List<string> BackdropImagePaths { get; set; }
+
+ /// <summary>
+ /// Gets a value indicating whether this instance is owned item.
+ /// </summary>
+ /// <value><c>true</c> if this instance is owned item; otherwise, <c>false</c>.</value>
+ bool IsOwnedItem { get; }
}
public static class HasImagesExtensions
diff --git a/MediaBrowser.Controller/Entities/Movies/Movie.cs b/MediaBrowser.Controller/Entities/Movies/Movie.cs
index a4e885337..41a1969d6 100644
--- a/MediaBrowser.Controller/Entities/Movies/Movie.cs
+++ b/MediaBrowser.Controller/Entities/Movies/Movie.cs
@@ -21,7 +21,7 @@ namespace MediaBrowser.Controller.Entities.Movies
public List<Guid> ThemeSongIds { get; set; }
public List<Guid> ThemeVideoIds { get; set; }
-
+
/// <summary>
/// Gets or sets the preferred metadata country code.
/// </summary>
@@ -29,7 +29,7 @@ namespace MediaBrowser.Controller.Entities.Movies
public string PreferredMetadataCountryCode { get; set; }
public string PreferredMetadataLanguage { get; set; }
-
+
public Movie()
{
SpecialFeatureIds = new List<Guid>();
@@ -49,7 +49,7 @@ namespace MediaBrowser.Controller.Entities.Movies
public List<Guid> LocalTrailerIds { get; set; }
public List<string> Keywords { get; set; }
-
+
public List<MediaUrl> RemoteTrailers { get; set; }
/// <summary>
@@ -103,88 +103,48 @@ namespace MediaBrowser.Controller.Entities.Movies
return this.GetProviderId(MetadataProviders.Tmdb) ?? this.GetProviderId(MetadataProviders.Imdb) ?? base.GetUserDataKey();
}
- /// <summary>
- /// Overrides the base implementation to refresh metadata for special features
- /// </summary>
- /// <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>
- /// <returns>Task{System.Boolean}.</returns>
- public override async Task<bool> RefreshMetadataDirect(CancellationToken cancellationToken, bool forceSave = false, bool forceRefresh = false)
+ protected override async Task BeforeRefreshMetadata(MetadataRefreshOptions options, List<FileSystemInfo> fileSystemChildren, CancellationToken cancellationToken)
{
- // Kick off a task to refresh the main item
- var result = await base.RefreshMetadataDirect(cancellationToken, forceSave, forceRefresh).ConfigureAwait(false);
-
- var specialFeaturesChanged = false;
+ await base.BeforeRefreshMetadata(options, fileSystemChildren, cancellationToken).ConfigureAwait(false);
// Must have a parent to have special features
// In other words, it must be part of the Parent/Child tree
if (LocationType == LocationType.FileSystem && Parent != null && !IsInMixedFolder)
{
- specialFeaturesChanged = await RefreshSpecialFeatures(cancellationToken, forceSave, forceRefresh).ConfigureAwait(false);
- }
+ var specialFeaturesChanged = await RefreshSpecialFeatures(options, fileSystemChildren, cancellationToken).ConfigureAwait(false);
- return specialFeaturesChanged || result;
+ if (specialFeaturesChanged)
+ {
+ options.ForceSave = true;
+ }
+ }
}
- private async Task<bool> RefreshSpecialFeatures(CancellationToken cancellationToken, bool forceSave = false, bool forceRefresh = false, bool allowSlowProviders = true)
+ private async Task<bool> RefreshSpecialFeatures(MetadataRefreshOptions options, List<FileSystemInfo> fileSystemChildren, CancellationToken cancellationToken)
{
- var newItems = LoadSpecialFeatures().ToList();
+ var newItems = LoadSpecialFeatures(fileSystemChildren).ToList();
var newItemIds = newItems.Select(i => i.Id).ToList();
var itemsChanged = !SpecialFeatureIds.SequenceEqual(newItemIds);
- var tasks = newItems.Select(i => i.RefreshMetadata(new MetadataRefreshOptions
- {
- ForceSave = forceSave,
- ReplaceAllMetadata = forceRefresh,
- ResetResolveArgs = false
-
- }, cancellationToken));
+ var tasks = newItems.Select(i => i.RefreshMetadata(options, cancellationToken));
- var results = await Task.WhenAll(tasks).ConfigureAwait(false);
+ await Task.WhenAll(tasks).ConfigureAwait(false);
SpecialFeatureIds = newItemIds;
- return itemsChanged || results.Contains(true);
+ return itemsChanged;
}
/// <summary>
/// Loads the special features.
/// </summary>
/// <returns>IEnumerable{Video}.</returns>
- private IEnumerable<Video> LoadSpecialFeatures()
+ private IEnumerable<Video> LoadSpecialFeatures(IEnumerable<FileSystemInfo> fileSystemChildren)
{
- FileSystemInfo folder;
-
- try
- {
- folder = ResolveArgs.GetFileSystemEntryByName("extras") ??
- ResolveArgs.GetFileSystemEntryByName("specials");
- }
- catch (IOException ex)
- {
- Logger.ErrorException("Error getting ResolveArgs for {0}", ex, Path);
- return new List<Video>();
- }
-
- // Path doesn't exist. No biggie
- if (folder == null)
- {
- return new List<Video>();
- }
-
- IEnumerable<FileSystemInfo> files;
-
- try
- {
- files = new DirectoryInfo(folder.FullName).EnumerateFiles();
- }
- catch (IOException ex)
- {
- Logger.ErrorException("Error loading special features for {0}", ex, Name);
- return new List<Video>();
- }
+ var files = fileSystemChildren.OfType<DirectoryInfo>()
+ .Where(i => string.Equals(i.Name, "extras", StringComparison.OrdinalIgnoreCase) || string.Equals(i.Name, "specials", StringComparison.OrdinalIgnoreCase))
+ .SelectMany(i => i.EnumerateFiles("*", SearchOption.TopDirectoryOnly));
return LibraryManager.ResolvePaths<Video>(files, null).Select(video =>
{
@@ -193,7 +153,6 @@ namespace MediaBrowser.Controller.Entities.Movies
if (dbItem != null)
{
- dbItem.ResetResolveArgs(video.ResolveArgs);
video = dbItem;
}
diff --git a/MediaBrowser.Controller/Entities/TV/Episode.cs b/MediaBrowser.Controller/Entities/TV/Episode.cs
index 3bdfc3c44..73726a4e2 100644
--- a/MediaBrowser.Controller/Entities/TV/Episode.cs
+++ b/MediaBrowser.Controller/Entities/TV/Episode.cs
@@ -12,28 +12,6 @@ namespace MediaBrowser.Controller.Entities.TV
public class Episode : Video
{
/// <summary>
- /// Episodes have a special Metadata folder
- /// </summary>
- /// <value>The meta location.</value>
- [IgnoreDataMember]
- public override string MetaLocation
- {
- get
- {
- return System.IO.Path.Combine(Parent.Path, "metadata");
- }
- }
-
- [IgnoreDataMember]
- protected override bool UseParentPathToCreateResolveArgs
- {
- get
- {
- return false;
- }
- }
-
- /// <summary>
/// Gets the season in which it aired.
/// </summary>
/// <value>The aired season.</value>
diff --git a/MediaBrowser.Controller/Entities/TV/Season.cs b/MediaBrowser.Controller/Entities/TV/Season.cs
index 2d781118e..744416560 100644
--- a/MediaBrowser.Controller/Entities/TV/Season.cs
+++ b/MediaBrowser.Controller/Entities/TV/Season.cs
@@ -132,34 +132,6 @@ namespace MediaBrowser.Controller.Entities.TV
}
/// <summary>
- /// Add files from the metadata folder to ResolveArgs
- /// </summary>
- /// <param name="args">The args.</param>
- public static void AddMetadataFiles(ItemResolveArgs args)
- {
- var folder = args.GetFileSystemEntryByName("metadata");
-
- if (folder != null)
- {
- args.AddMetadataFiles(new DirectoryInfo(folder.FullName).EnumerateFiles());
- }
- }
-
- /// <summary>
- /// Creates ResolveArgs on demand
- /// </summary>
- /// <param name="pathInfo">The path info.</param>
- /// <returns>ItemResolveArgs.</returns>
- protected internal override ItemResolveArgs CreateResolveArgs(FileSystemInfo pathInfo = null)
- {
- var args = base.CreateResolveArgs(pathInfo);
-
- AddMetadataFiles(args);
-
- return args;
- }
-
- /// <summary>
/// Creates the name of the sort.
/// </summary>
/// <returns>System.String.</returns>
diff --git a/MediaBrowser.Controller/Entities/TV/Series.cs b/MediaBrowser.Controller/Entities/TV/Series.cs
index 552a517de..efb3c393b 100644
--- a/MediaBrowser.Controller/Entities/TV/Series.cs
+++ b/MediaBrowser.Controller/Entities/TV/Series.cs
@@ -111,20 +111,6 @@ namespace MediaBrowser.Controller.Entities.TV
};
}
- /// <summary>
- /// Creates ResolveArgs on demand
- /// </summary>
- /// <param name="pathInfo">The path info.</param>
- /// <returns>ItemResolveArgs.</returns>
- protected internal override ItemResolveArgs CreateResolveArgs(FileSystemInfo pathInfo = null)
- {
- var args = base.CreateResolveArgs(pathInfo);
-
- Season.AddMetadataFiles(args);
-
- return args;
- }
-
[IgnoreDataMember]
public bool ContainsEpisodesWithoutSeasonFolders
{
diff --git a/MediaBrowser.Controller/Entities/Trailer.cs b/MediaBrowser.Controller/Entities/Trailer.cs
index f429a2677..d6d193442 100644
--- a/MediaBrowser.Controller/Entities/Trailer.cs
+++ b/MediaBrowser.Controller/Entities/Trailer.cs
@@ -89,33 +89,6 @@ namespace MediaBrowser.Controller.Entities
}
}
- /// <summary>
- /// Should be overridden to return the proper folder where metadata lives
- /// </summary>
- /// <value>The meta location.</value>
- [IgnoreDataMember]
- public override string MetaLocation
- {
- get
- {
- if (!IsLocalTrailer)
- {
- return System.IO.Path.GetDirectoryName(Path);
- }
-
- return base.MetaLocation;
- }
- }
-
- /// <summary>
- /// Needed because the resolver stops at the trailer folder and we find the video inside.
- /// </summary>
- /// <value><c>true</c> if [use parent path to create resolve args]; otherwise, <c>false</c>.</value>
- protected override bool UseParentPathToCreateResolveArgs
- {
- get { return !IsLocalTrailer; }
- }
-
public override string GetUserDataKey()
{
var key = this.GetProviderId(MetadataProviders.Tmdb) ?? this.GetProviderId(MetadataProviders.Tvdb) ?? this.GetProviderId(MetadataProviders.Imdb) ?? this.GetProviderId(MetadataProviders.Tvcom);
diff --git a/MediaBrowser.Controller/Entities/Video.cs b/MediaBrowser.Controller/Entities/Video.cs
index 9c9466766..de78068b3 100644
--- a/MediaBrowser.Controller/Entities/Video.cs
+++ b/MediaBrowser.Controller/Entities/Video.cs
@@ -84,33 +84,23 @@ namespace MediaBrowser.Controller.Entities
/// <value>The aspect ratio.</value>
public string AspectRatio { get; set; }
- /// <summary>
- /// Should be overridden to return the proper folder where metadata lives
- /// </summary>
- /// <value>The meta location.</value>
[IgnoreDataMember]
- public override string MetaLocation
+ public override string ContainingFolderPath
{
get
{
- return VideoType == VideoType.VideoFile || VideoType == VideoType.Iso || IsMultiPart ? System.IO.Path.GetDirectoryName(Path) : Path;
- }
- }
+ if (IsMultiPart)
+ {
+ return System.IO.Path.GetDirectoryName(Path);
+ }
- /// <summary>
- /// Needed because the resolver stops at the movie folder and we find the video inside.
- /// </summary>
- /// <value><c>true</c> if [use parent path to create resolve args]; otherwise, <c>false</c>.</value>
- protected override bool UseParentPathToCreateResolveArgs
- {
- get
- {
- if (IsInMixedFolder)
+ if (VideoType == VideoType.BluRay || VideoType == VideoType.Dvd ||
+ VideoType == VideoType.HdDvd)
{
- return false;
+ return Path;
}
- return VideoType == VideoType.VideoFile || VideoType == VideoType.Iso || IsMultiPart;
+ return base.ContainingFolderPath;
}
}
@@ -159,106 +149,73 @@ namespace MediaBrowser.Controller.Entities
}
}
- /// <summary>
- /// Overrides the base implementation to refresh metadata for local trailers
- /// </summary>
- /// <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>
- /// <returns>true if a provider reports we changed</returns>
- public override async Task<bool> RefreshMetadataDirect(CancellationToken cancellationToken, bool forceSave = false, bool forceRefresh = false)
+ protected override async Task BeforeRefreshMetadata(MetadataRefreshOptions options, List<FileSystemInfo> fileSystemChildren, CancellationToken cancellationToken)
{
- // Kick off a task to refresh the main item
- var result = await base.RefreshMetadataDirect(cancellationToken, forceSave, forceRefresh).ConfigureAwait(false);
-
- var additionalPartsChanged = false;
+ await base.BeforeRefreshMetadata(options, fileSystemChildren, cancellationToken).ConfigureAwait(false);
// Must have a parent to have additional parts
// In other words, it must be part of the Parent/Child tree
// The additional parts won't have additional parts themselves
if (IsMultiPart && LocationType == LocationType.FileSystem && Parent != null)
{
- try
- {
- additionalPartsChanged = await RefreshAdditionalParts(cancellationToken, forceSave, forceRefresh).ConfigureAwait(false);
- }
- catch (IOException ex)
+ var additionalPartsChanged = await RefreshAdditionalParts(options, fileSystemChildren, cancellationToken).ConfigureAwait(false);
+
+ if (additionalPartsChanged)
{
- Logger.ErrorException("Error loading additional parts for {0}.", ex, Name);
+ options.ForceSave = true;
}
}
-
- return additionalPartsChanged || result;
}
/// <summary>
/// Refreshes the additional parts.
/// </summary>
+ /// <param name="options">The options.</param>
+ /// <param name="fileSystemChildren">The file system children.</param>
/// <param name="cancellationToken">The cancellation token.</param>
- /// <param name="forceSave">if set to <c>true</c> [force save].</param>
- /// <param name="forceRefresh">if set to <c>true</c> [force refresh].</param>
- /// <param name="allowSlowProviders">if set to <c>true</c> [allow slow providers].</param>
/// <returns>Task{System.Boolean}.</returns>
- private async Task<bool> RefreshAdditionalParts(CancellationToken cancellationToken, bool forceSave = false, bool forceRefresh = false, bool allowSlowProviders = true)
+ private async Task<bool> RefreshAdditionalParts(MetadataRefreshOptions options, List<FileSystemInfo> fileSystemChildren, CancellationToken cancellationToken)
{
- var newItems = LoadAdditionalParts().ToList();
+ var newItems = LoadAdditionalParts(fileSystemChildren).ToList();
var newItemIds = newItems.Select(i => i.Id).ToList();
var itemsChanged = !AdditionalPartIds.SequenceEqual(newItemIds);
- var tasks = newItems.Select(i => i.RefreshMetadata(new MetadataRefreshOptions
- {
- ForceSave = forceSave,
- ReplaceAllMetadata = forceRefresh
+ var tasks = newItems.Select(i => i.RefreshMetadata(options, cancellationToken));
- }, cancellationToken));
-
- var results = await Task.WhenAll(tasks).ConfigureAwait(false);
+ await Task.WhenAll(tasks).ConfigureAwait(false);
AdditionalPartIds = newItemIds;
- return itemsChanged || results.Contains(true);
+ return itemsChanged;
}
/// <summary>
/// Loads the additional parts.
/// </summary>
/// <returns>IEnumerable{Video}.</returns>
- private IEnumerable<Video> LoadAdditionalParts()
+ private IEnumerable<Video> LoadAdditionalParts(IEnumerable<FileSystemInfo> fileSystemChildren)
{
IEnumerable<FileSystemInfo> files;
var path = Path;
- if (string.IsNullOrEmpty(path))
- {
- throw new ApplicationException(string.Format("Item {0} has a null path.", Name ?? Id.ToString()));
- }
-
if (VideoType == VideoType.BluRay || VideoType == VideoType.Dvd)
{
- var parentPath = System.IO.Path.GetDirectoryName(path);
-
- if (string.IsNullOrEmpty(parentPath))
+ files = fileSystemChildren.Where(i =>
{
- throw new IOException("Unable to get parent path info from " + path);
- }
+ if ((i.Attributes & FileAttributes.Directory) == FileAttributes.Directory)
+ {
+ return !string.Equals(i.FullName, path, StringComparison.OrdinalIgnoreCase) && EntityResolutionHelper.IsVideoFile(i.FullName) && EntityResolutionHelper.IsMultiPartFile(i.Name);
+ }
- files = new DirectoryInfo(parentPath)
- .EnumerateDirectories()
- .Where(i => !string.Equals(i.FullName, path, StringComparison.OrdinalIgnoreCase) && EntityResolutionHelper.IsMultiPartFile(i.Name));
+ return false;
+ });
}
else
{
- var resolveArgs = ResolveArgs;
-
- if (resolveArgs == null)
- {
- throw new IOException("ResolveArgs are null for " + path);
- }
-
- files = resolveArgs.FileSystemChildren.Where(i =>
+ files = fileSystemChildren.Where(i =>
{
if ((i.Attributes & FileAttributes.Directory) == FileAttributes.Directory)
{
@@ -276,7 +233,6 @@ namespace MediaBrowser.Controller.Entities
if (dbItem != null)
{
- dbItem.ResetResolveArgs(video.ResolveArgs);
video = dbItem;
}
diff --git a/MediaBrowser.Controller/Library/ItemResolveArgs.cs b/MediaBrowser.Controller/Library/ItemResolveArgs.cs
index c45c14239..5d6d850f0 100644
--- a/MediaBrowser.Controller/Library/ItemResolveArgs.cs
+++ b/MediaBrowser.Controller/Library/ItemResolveArgs.cs
@@ -196,77 +196,6 @@ namespace MediaBrowser.Controller.Library
}
/// <summary>
- /// Store these to reduce disk access in Resolvers
- /// </summary>
- /// <value>The metadata file dictionary.</value>
- private Dictionary<string, FileSystemInfo> MetadataFileDictionary { get; set; }
-
- /// <summary>
- /// Gets the metadata files.
- /// </summary>
- /// <value>The metadata files.</value>
- public IEnumerable<FileSystemInfo> MetadataFiles
- {
- get
- {
- if (MetadataFileDictionary != null)
- {
- return MetadataFileDictionary.Values;
- }
-
- return new FileSystemInfo[] { };
- }
- }
-
- /// <summary>
- /// Adds the metadata file.
- /// </summary>
- /// <param name="path">The path.</param>
- /// <exception cref="System.IO.FileNotFoundException"></exception>
- public void AddMetadataFile(string path)
- {
- var file = new FileInfo(path);
-
- if (!file.Exists)
- {
- throw new FileNotFoundException(path);
- }
-
- AddMetadataFile(file);
- }
-
- /// <summary>
- /// Adds the metadata file.
- /// </summary>
- /// <param name="fileInfo">The file info.</param>
- public void AddMetadataFile(FileSystemInfo fileInfo)
- {
- AddMetadataFiles(new[] { fileInfo });
- }
-
- /// <summary>
- /// Adds the metadata files.
- /// </summary>
- /// <param name="files">The files.</param>
- /// <exception cref="System.ArgumentNullException"></exception>
- public void AddMetadataFiles(IEnumerable<FileSystemInfo> files)
- {
- if (files == null)
- {
- throw new ArgumentNullException();
- }
-
- if (MetadataFileDictionary == null)
- {
- MetadataFileDictionary = new Dictionary<string, FileSystemInfo>(StringComparer.OrdinalIgnoreCase);
- }
- foreach (var file in files)
- {
- MetadataFileDictionary[file.Name] = file;
- }
- }
-
- /// <summary>
/// Gets the name of the file system entry by.
/// </summary>
/// <param name="name">The name.</param>
@@ -320,16 +249,6 @@ namespace MediaBrowser.Controller.Library
{
throw new ArgumentNullException();
}
-
- if (MetadataFileDictionary != null)
- {
- FileSystemInfo entry;
-
- if (MetadataFileDictionary.TryGetValue(System.IO.Path.GetFileName(path), out entry))
- {
- return entry;
- }
- }
return GetFileSystemEntryByPath(path);
}
@@ -346,16 +265,6 @@ namespace MediaBrowser.Controller.Library
{
throw new ArgumentNullException();
}
-
- if (MetadataFileDictionary != null)
- {
- FileSystemInfo entry;
-
- if (MetadataFileDictionary.TryGetValue(name, out entry))
- {
- return entry;
- }
- }
return GetFileSystemEntryByName(name);
}
diff --git a/MediaBrowser.Controller/LiveTv/ILiveTvRecording.cs b/MediaBrowser.Controller/LiveTv/ILiveTvRecording.cs
index 1bff5647b..668c3a414 100644
--- a/MediaBrowser.Controller/LiveTv/ILiveTvRecording.cs
+++ b/MediaBrowser.Controller/LiveTv/ILiveTvRecording.cs
@@ -19,6 +19,6 @@ namespace MediaBrowser.Controller.LiveTv
bool IsParentalAllowed(User user);
- Task<bool> RefreshMetadata(MetadataRefreshOptions options, CancellationToken cancellationToken);
+ Task RefreshMetadata(MetadataRefreshOptions options, CancellationToken cancellationToken);
}
}
diff --git a/MediaBrowser.Controller/Providers/BaseItemXmlParser.cs b/MediaBrowser.Controller/Providers/BaseItemXmlParser.cs
index 7bd6d824a..5d3d9dbca 100644
--- a/MediaBrowser.Controller/Providers/BaseItemXmlParser.cs
+++ b/MediaBrowser.Controller/Providers/BaseItemXmlParser.cs
@@ -20,7 +20,7 @@ namespace MediaBrowser.Controller.Providers
/// </summary>
/// <typeparam name="T"></typeparam>
public class BaseItemXmlParser<T>
- where T : BaseItem, new()
+ where T : BaseItem
{
/// <summary>
/// The logger
@@ -422,11 +422,7 @@ namespace MediaBrowser.Controller.Providers
int runtime;
if (int.TryParse(text.Split(' ')[0], NumberStyles.Integer, _usCulture, out runtime))
{
- // For audio and video don't replace ffmpeg data
- if (!(item is Video || item is Audio))
- {
- item.RunTimeTicks = TimeSpan.FromMinutes(runtime).Ticks;
- }
+ item.RunTimeTicks = TimeSpan.FromMinutes(runtime).Ticks;
}
}
break;
diff --git a/MediaBrowser.Controller/Providers/BaseMetadataProvider.cs b/MediaBrowser.Controller/Providers/BaseMetadataProvider.cs
index 3b499b487..6818fa52b 100644
--- a/MediaBrowser.Controller/Providers/BaseMetadataProvider.cs
+++ b/MediaBrowser.Controller/Providers/BaseMetadataProvider.cs
@@ -2,13 +2,8 @@
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Library;
-using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Logging;
using System;
-using System.Collections.Generic;
-using System.IO;
-using System.Linq;
-using System.Text;
using System.Threading;
using System.Threading.Tasks;
@@ -146,19 +141,6 @@ namespace MediaBrowser.Controller.Providers
providerInfo.LastRefreshed = value;
providerInfo.LastRefreshStatus = status;
providerInfo.ProviderVersion = providerVersion;
-
- // Save the file system stamp for future comparisons
- if (RefreshOnFileSystemStampChange && item.LocationType == LocationType.FileSystem)
- {
- try
- {
- providerInfo.FileStamp = GetCurrentFileSystemStamp(item);
- }
- catch (IOException ex)
- {
- Logger.ErrorException("Error getting file stamp for {0}", ex, item.Path);
- }
- }
}
/// <summary>
@@ -233,12 +215,6 @@ namespace MediaBrowser.Controller.Providers
return true;
}
- if (RefreshOnFileSystemStampChange && item.LocationType == LocationType.FileSystem &&
- HasFileSystemStampChanged(item, providerInfo))
- {
- return true;
- }
-
if (RefreshOnVersionChange && !String.Equals(ProviderVersion, providerInfo.ProviderVersion))
{
return true;
@@ -264,17 +240,6 @@ namespace MediaBrowser.Controller.Providers
}
/// <summary>
- /// Determines if the item's file system stamp has changed from the last time the provider refreshed
- /// </summary>
- /// <param name="item">The item.</param>
- /// <param name="providerInfo">The provider info.</param>
- /// <returns><c>true</c> if [has file system stamp changed] [the specified item]; otherwise, <c>false</c>.</returns>
- protected bool HasFileSystemStampChanged(BaseItem item, BaseProviderInfo providerInfo)
- {
- return GetCurrentFileSystemStamp(item) != providerInfo.FileStamp;
- }
-
- /// <summary>
/// Override this to return the date that should be compared to the last refresh date
/// to determine if this provider should be re-fetched.
/// </summary>
@@ -301,159 +266,5 @@ namespace MediaBrowser.Controller.Providers
/// </summary>
/// <value>The priority.</value>
public abstract MetadataProviderPriority Priority { get; }
-
- /// <summary>
- /// Returns true or false indicating if the provider should refresh when the contents of it's directory changes
- /// </summary>
- /// <value><c>true</c> if [refresh on file system stamp change]; otherwise, <c>false</c>.</value>
- protected virtual bool RefreshOnFileSystemStampChange
- {
- get
- {
- return false;
- }
- }
-
- protected virtual string[] FilestampExtensions
- {
- get { return new string[] { }; }
- }
-
- /// <summary>
- /// Determines if the parent's file system stamp should be used for comparison
- /// </summary>
- /// <param name="item">The item.</param>
- /// <returns><c>true</c> if XXXX, <c>false</c> otherwise</returns>
- protected virtual bool UseParentFileSystemStamp(BaseItem item)
- {
- // True when the current item is just a file
- return !item.ResolveArgs.IsDirectory;
- }
-
- protected virtual IEnumerable<BaseItem> GetItemsForFileStampComparison(BaseItem item)
- {
- if (UseParentFileSystemStamp(item) && item.Parent != null)
- {
- return new[] { item.Parent };
- }
-
- return new[] { item };
- }
-
- /// <summary>
- /// Gets the item's current file system stamp
- /// </summary>
- /// <param name="item">The item.</param>
- /// <returns>Guid.</returns>
- private Guid GetCurrentFileSystemStamp(BaseItem item)
- {
- return GetFileSystemStamp(GetItemsForFileStampComparison(item));
- }
-
- private Dictionary<string, string> _fileStampExtensionsDictionary;
-
- private Dictionary<string, string> FileStampExtensionsDictionary
- {
- get
- {
- return _fileStampExtensionsDictionary ??
- (_fileStampExtensionsDictionary =
- FilestampExtensions.ToDictionary(i => i, StringComparer.OrdinalIgnoreCase));
- }
- }
-
- /// <summary>
- /// Gets the file system stamp.
- /// </summary>
- /// <param name="items">The items.</param>
- /// <returns>Guid.</returns>
- protected virtual Guid GetFileSystemStamp(IEnumerable<BaseItem> items)
- {
- var sb = new StringBuilder();
-
- var extensions = FileStampExtensionsDictionary;
- var numExtensions = FilestampExtensions.Length;
-
- foreach (var item in items)
- {
- // If there's no path or the item is a file, there's nothing to do
- if (item.LocationType == LocationType.FileSystem)
- {
- var resolveArgs = item.ResolveArgs;
-
- if (resolveArgs.IsDirectory)
- {
- // Record the name of each file
- // Need to sort these because accoring to msdn docs, our i/o methods are not guaranteed in any order
- AddFiles(sb, resolveArgs.FileSystemChildren, extensions, numExtensions);
- AddFiles(sb, resolveArgs.MetadataFiles, extensions, numExtensions);
- }
- }
- }
-
- var stamp = sb.ToString();
-
- if (string.IsNullOrEmpty(stamp))
- {
- return Guid.Empty;
- }
-
- return stamp.GetMD5();
- }
-
- private static readonly Dictionary<string, string> FoldersToMonitor = new[] { "extrafanart", "extrathumbs" }
- .ToDictionary(i => i, StringComparer.OrdinalIgnoreCase);
-
- protected Guid GetFileSystemStamp(IEnumerable<FileSystemInfo> files)
- {
- var sb = new StringBuilder();
-
- var extensions = FileStampExtensionsDictionary;
- var numExtensions = FilestampExtensions.Length;
-
- // Record the name of each file
- // Need to sort these because accoring to msdn docs, our i/o methods are not guaranteed in any order
- AddFiles(sb, files, extensions, numExtensions);
-
- return sb.ToString().GetMD5();
- }
-
- /// <summary>
- /// Adds the files.
- /// </summary>
- /// <param name="sb">The sb.</param>
- /// <param name="files">The files.</param>
- /// <param name="extensions">The extensions.</param>
- /// <param name="numExtensions">The num extensions.</param>
- private void AddFiles(StringBuilder sb, IEnumerable<FileSystemInfo> files, Dictionary<string, string> extensions, int numExtensions)
- {
- foreach (var file in files
- .OrderBy(f => f.Name))
- {
- try
- {
- if ((file.Attributes & FileAttributes.Directory) == FileAttributes.Directory)
- {
- if (FoldersToMonitor.ContainsKey(file.Name))
- {
- sb.Append(file.Name);
-
- var children = ((DirectoryInfo)file).EnumerateFiles("*", SearchOption.TopDirectoryOnly).ToList();
- AddFiles(sb, children, extensions, numExtensions);
- }
- }
-
- // It's a file
- else if (numExtensions == 0 || extensions.ContainsKey(file.Extension))
- {
- sb.Append(file.Name);
- }
- }
- catch (IOException ex)
- {
- Logger.ErrorException("Error accessing file attributes for {0}", ex, file.FullName);
- }
- }
- }
}
}
diff --git a/MediaBrowser.Controller/Providers/IHasMetadata.cs b/MediaBrowser.Controller/Providers/IHasMetadata.cs
index 3fba73a28..19ab00e68 100644
--- a/MediaBrowser.Controller/Providers/IHasMetadata.cs
+++ b/MediaBrowser.Controller/Providers/IHasMetadata.cs
@@ -39,5 +39,11 @@ namespace MediaBrowser.Controller.Providers
/// </summary>
/// <returns><c>true</c> if [is save local metadata enabled]; otherwise, <c>false</c>.</returns>
bool IsSaveLocalMetadataEnabled();
+
+ /// <summary>
+ /// Gets a value indicating whether this instance is in mixed folder.
+ /// </summary>
+ /// <value><c>true</c> if this instance is in mixed folder; otherwise, <c>false</c>.</value>
+ bool IsInMixedFolder { get; }
}
}
diff --git a/MediaBrowser.Controller/Providers/ILocalMetadataProvider.cs b/MediaBrowser.Controller/Providers/ILocalMetadataProvider.cs
index 62b208b59..91ef22b2c 100644
--- a/MediaBrowser.Controller/Providers/ILocalMetadataProvider.cs
+++ b/MediaBrowser.Controller/Providers/ILocalMetadataProvider.cs
@@ -5,12 +5,6 @@ namespace MediaBrowser.Controller.Providers
{
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 ILocalMetadataProvider<TItemType> : IMetadataProvider<TItemType>, ILocalMetadataProvider
@@ -19,9 +13,16 @@ namespace MediaBrowser.Controller.Providers
/// <summary>
/// Gets the metadata.
/// </summary>
- /// <param name="path">The path.</param>
+ /// <param name="info">The information.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Task{MetadataResult{`0}}.</returns>
- Task<MetadataResult<TItemType>> GetMetadata(string path, CancellationToken cancellationToken);
+ Task<MetadataResult<TItemType>> GetMetadata(ItemInfo info, CancellationToken cancellationToken);
+ }
+
+ public class ItemInfo
+ {
+ public string Path { get; set; }
+
+ public bool IsInMixedFolder { get; set; }
}
}
diff --git a/MediaBrowser.Controller/Providers/IProviderManager.cs b/MediaBrowser.Controller/Providers/IProviderManager.cs
index 7019623ee..369b58256 100644
--- a/MediaBrowser.Controller/Providers/IProviderManager.cs
+++ b/MediaBrowser.Controller/Providers/IProviderManager.cs
@@ -25,15 +25,6 @@ namespace MediaBrowser.Controller.Providers
Task RefreshMetadata(IHasMetadata item, MetadataRefreshOptions options, CancellationToken cancellationToken);
/// <summary>
- /// Executes the metadata providers.
- /// </summary>
- /// <param name="item">The item.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <param name="force">if set to <c>true</c> [force].</param>
- /// <returns>Task{System.Boolean}.</returns>
- Task<ItemUpdateType?> ExecuteMetadataProviders(BaseItem item, CancellationToken cancellationToken, bool force = false);
-
- /// <summary>
/// Saves the image.
/// </summary>
/// <param name="item">The item.</param>
@@ -60,12 +51,11 @@ namespace MediaBrowser.Controller.Providers
/// <summary>
/// Adds the metadata providers.
/// </summary>
- /// <param name="providers">The providers.</param>
/// <param name="imageProviders">The image providers.</param>
/// <param name="metadataServices">The metadata services.</param>
/// <param name="metadataProviders">The metadata providers.</param>
/// <param name="savers">The savers.</param>
- void AddParts(IEnumerable<BaseMetadataProvider> providers, IEnumerable<IImageProvider> imageProviders, IEnumerable<IMetadataService> metadataServices, IEnumerable<IMetadataProvider> metadataProviders,
+ void AddParts(IEnumerable<IImageProvider> imageProviders, IEnumerable<IMetadataService> metadataServices, IEnumerable<IMetadataProvider> metadataProviders,
IEnumerable<IMetadataSaver> savers);
/// <summary>
diff --git a/MediaBrowser.Controller/Providers/MetadataRefreshOptions.cs b/MediaBrowser.Controller/Providers/MetadataRefreshOptions.cs
index 27de50ef8..83f1a12d9 100644
--- a/MediaBrowser.Controller/Providers/MetadataRefreshOptions.cs
+++ b/MediaBrowser.Controller/Providers/MetadataRefreshOptions.cs
@@ -16,17 +16,6 @@ namespace MediaBrowser.Controller.Providers
/// </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 MetadataRefreshOptions()
- {
- ResetResolveArgs = true;
- }
}
public class ImageRefreshOptions
diff --git a/MediaBrowser.Model/Dto/BaseItemDto.cs b/MediaBrowser.Model/Dto/BaseItemDto.cs
index 4d409ff7d..af3c62396 100644
--- a/MediaBrowser.Model/Dto/BaseItemDto.cs
+++ b/MediaBrowser.Model/Dto/BaseItemDto.cs
@@ -153,12 +153,6 @@ namespace MediaBrowser.Model.Dto
public int? VoteCount { get; set; }
/// <summary>
- /// Gets or sets the original run time ticks.
- /// </summary>
- /// <value>The original run time ticks.</value>
- public long? OriginalRunTimeTicks { get; set; }
-
- /// <summary>
/// Gets or sets the cumulative run time ticks.
/// </summary>
/// <value>The cumulative run time ticks.</value>
diff --git a/MediaBrowser.Model/Querying/ItemFields.cs b/MediaBrowser.Model/Querying/ItemFields.cs
index 406957daa..e16da857b 100644
--- a/MediaBrowser.Model/Querying/ItemFields.cs
+++ b/MediaBrowser.Model/Querying/ItemFields.cs
@@ -72,11 +72,6 @@ namespace MediaBrowser.Model.Querying
Settings,
/// <summary>
- /// The original run time ticks
- /// </summary>
- OriginalRunTimeTicks,
-
- /// <summary>
/// The item overview
/// </summary>
Overview,
diff --git a/MediaBrowser.Providers/AdultVideos/AdultVideoXmlProvider.cs b/MediaBrowser.Providers/AdultVideos/AdultVideoXmlProvider.cs
index 3b6439b4b..40f9fded5 100644
--- a/MediaBrowser.Providers/AdultVideos/AdultVideoXmlProvider.cs
+++ b/MediaBrowser.Providers/AdultVideos/AdultVideoXmlProvider.cs
@@ -5,11 +5,10 @@ using MediaBrowser.Model.Logging;
using MediaBrowser.Providers.Movies;
using System.IO;
using System.Threading;
-using System.Threading.Tasks;
namespace MediaBrowser.Providers.AdultVideos
{
- class AdultVideoXmlProvider : BaseXmlProvider, ILocalMetadataProvider<AdultVideo>
+ class AdultVideoXmlProvider : BaseXmlProvider<AdultVideo>
{
private readonly ILogger _logger;
@@ -19,41 +18,14 @@ namespace MediaBrowser.Providers.AdultVideos
_logger = logger;
}
- public async Task<MetadataResult<AdultVideo>> GetMetadata(string path, CancellationToken cancellationToken)
+ protected override void Fetch(AdultVideo item, string path, CancellationToken cancellationToken)
{
- path = GetXmlFile(path).FullName;
-
- var result = new MetadataResult<AdultVideo>();
-
- await XmlParsingResourcePool.WaitAsync(cancellationToken).ConfigureAwait(false);
-
- try
- {
- result.Item = new AdultVideo();
-
- new MovieXmlParser(_logger).Fetch(result.Item, path, cancellationToken);
- result.HasMetadata = true;
- }
- catch (FileNotFoundException)
- {
- result.HasMetadata = false;
- }
- finally
- {
- XmlParsingResourcePool.Release();
- }
-
- return result;
- }
-
- public string Name
- {
- get { return "Media Browser Xml"; }
+ new MovieXmlParser(_logger).Fetch(item, path, cancellationToken);
}
- protected override FileInfo GetXmlFile(string path)
+ protected override FileInfo GetXmlFile(ItemInfo info)
{
- return MovieXmlProvider.GetXmlFileInfo(path, FileSystem);
+ return MovieXmlProvider.GetXmlFileInfo(info, FileSystem);
}
}
}
diff --git a/MediaBrowser.Providers/All/LocalImageProvider.cs b/MediaBrowser.Providers/All/LocalImageProvider.cs
index 089cfe549..daae58ff6 100644
--- a/MediaBrowser.Providers/All/LocalImageProvider.cs
+++ b/MediaBrowser.Providers/All/LocalImageProvider.cs
@@ -39,7 +39,7 @@ namespace MediaBrowser.Providers.All
if (locationType == LocationType.FileSystem)
{
// Episode has it's own provider
- if (item is Episode || item is Audio)
+ if (item.IsOwnedItem || item is Episode || item is Audio)
{
return false;
}
diff --git a/MediaBrowser.Providers/BaseXmlProvider.cs b/MediaBrowser.Providers/BaseXmlProvider.cs
index 68b003480..521198e96 100644
--- a/MediaBrowser.Providers/BaseXmlProvider.cs
+++ b/MediaBrowser.Providers/BaseXmlProvider.cs
@@ -3,32 +3,81 @@ using MediaBrowser.Controller.Providers;
using System;
using System.IO;
using System.Threading;
+using System.Threading.Tasks;
namespace MediaBrowser.Providers
{
- public abstract class BaseXmlProvider: IHasChangeMonitor
+ public abstract class BaseXmlProvider<T> : ILocalMetadataProvider<T>, IHasChangeMonitor
+ where T : IHasMetadata, new()
{
- protected static readonly SemaphoreSlim XmlParsingResourcePool = new SemaphoreSlim(4, 4);
-
protected IFileSystem FileSystem;
+ public async Task<MetadataResult<T>> GetMetadata(ItemInfo info, CancellationToken cancellationToken)
+ {
+ var result = new MetadataResult<T>();
+
+ var file = GetXmlFile(info);
+
+ if (file == null)
+ {
+ return result;
+ }
+
+ var path = file.FullName;
+
+ await XmlProviderUtils.XmlParsingResourcePool.WaitAsync(cancellationToken).ConfigureAwait(false);
+
+ try
+ {
+ result.Item = new T();
+
+ Fetch(result.Item, path, cancellationToken);
+ result.HasMetadata = true;
+ }
+ catch (FileNotFoundException)
+ {
+ result.HasMetadata = false;
+ }
+ finally
+ {
+ XmlProviderUtils.XmlParsingResourcePool.Release();
+ }
+
+ return result;
+ }
+
+ protected abstract void Fetch(T item, string path, CancellationToken cancellationToken);
+
protected BaseXmlProvider(IFileSystem fileSystem)
{
FileSystem = fileSystem;
}
- protected abstract FileInfo GetXmlFile(string path);
+ protected abstract FileInfo GetXmlFile(ItemInfo info);
public bool HasChanged(IHasMetadata item, DateTime date)
{
- var file = GetXmlFile(item.Path);
+ var file = GetXmlFile(new ItemInfo { IsInMixedFolder = item.IsInMixedFolder, Path = item.Path });
+
+ if (file == null)
+ {
+ return false;
+ }
return FileSystem.GetLastWriteTimeUtc(file) > date;
}
- public bool HasLocalMetadata(IHasMetadata item)
+ public string Name
{
- return GetXmlFile(item.Path).Exists;
+ get
+ {
+ return "Media Browser Xml";
+ }
}
}
+
+ static class XmlProviderUtils
+ {
+ internal static readonly SemaphoreSlim XmlParsingResourcePool = new SemaphoreSlim(4, 4);
+ }
}
diff --git a/MediaBrowser.Providers/BoxSets/BoxSetXmlProvider.cs b/MediaBrowser.Providers/BoxSets/BoxSetXmlProvider.cs
index eee6f3b48..a214dff8c 100644
--- a/MediaBrowser.Providers/BoxSets/BoxSetXmlProvider.cs
+++ b/MediaBrowser.Providers/BoxSets/BoxSetXmlProvider.cs
@@ -4,14 +4,13 @@ using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Logging;
using System.IO;
using System.Threading;
-using System.Threading.Tasks;
namespace MediaBrowser.Providers.BoxSets
{
/// <summary>
/// Class BoxSetXmlProvider.
/// </summary>
- public class BoxSetXmlProvider : BaseXmlProvider, ILocalMetadataProvider<BoxSet>
+ public class BoxSetXmlProvider : BaseXmlProvider<BoxSet>
{
private readonly ILogger _logger;
@@ -21,42 +20,14 @@ namespace MediaBrowser.Providers.BoxSets
_logger = logger;
}
- public async Task<MetadataResult<BoxSet>> GetMetadata(string path, CancellationToken cancellationToken)
+ protected override void Fetch(BoxSet item, string path, CancellationToken cancellationToken)
{
- path = GetXmlFile(path).FullName;
-
- var result = new MetadataResult<BoxSet>();
-
- await XmlParsingResourcePool.WaitAsync(cancellationToken).ConfigureAwait(false);
-
- try
- {
- var item = new BoxSet();
-
- new BaseItemXmlParser<BoxSet>(_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"; }
+ new BaseItemXmlParser<BoxSet>(_logger).Fetch(item, path, cancellationToken);
}
- protected override FileInfo GetXmlFile(string path)
+ protected override FileInfo GetXmlFile(ItemInfo info)
{
- return new FileInfo(Path.Combine(path, "collection.xml"));
+ return new FileInfo(Path.Combine(info.Path, "collection.xml"));
}
}
}
diff --git a/MediaBrowser.Providers/CollectionFolderImageProvider.cs b/MediaBrowser.Providers/CollectionFolderImageProvider.cs
deleted file mode 100644
index e4ea36dd1..000000000
--- a/MediaBrowser.Providers/CollectionFolderImageProvider.cs
+++ /dev/null
@@ -1,56 +0,0 @@
-using MediaBrowser.Common.IO;
-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.IO;
-using System.Linq;
-
-namespace MediaBrowser.Providers
-{
- public class CollectionFolderImageProvider : ImageFromMediaLocationProvider
- {
- public CollectionFolderImageProvider(ILogManager logManager, IServerConfigurationManager configurationManager, IFileSystem fileSystem)
- : base(logManager, configurationManager, fileSystem)
- {
- }
-
- public override bool Supports(BaseItem item)
- {
- return item is CollectionFolder && item.LocationType == LocationType.FileSystem;
- }
-
- public override MetadataProviderPriority Priority
- {
- get { return MetadataProviderPriority.Second; }
- }
-
- protected override FileSystemInfo GetImage(BaseItem item, ItemResolveArgs args, string filenameWithoutExtension)
- {
- return item.PhysicalLocations
- .Select(i => GetImageFromLocation(i, filenameWithoutExtension))
- .FirstOrDefault(i => i != null);
- }
-
- protected override Guid GetFileSystemStamp(IEnumerable<BaseItem> items)
- {
- var files = items.SelectMany(i => i.PhysicalLocations)
- .Select(i => new DirectoryInfo(i))
- .SelectMany(i => i.EnumerateFiles("*", SearchOption.TopDirectoryOnly))
- .Where(i =>
- {
- var ext = i.Extension;
-
- return !string.IsNullOrEmpty(ext) &&
- BaseItem.SupportedImageExtensions.Contains(ext, StringComparer.OrdinalIgnoreCase);
- })
- .ToList();
-
- return GetFileSystemStamp(files);
- }
- }
-}
diff --git a/MediaBrowser.Providers/FolderProviderFromXml.cs b/MediaBrowser.Providers/FolderProviderFromXml.cs
deleted file mode 100644
index 31e4bc8bc..000000000
--- a/MediaBrowser.Providers/FolderProviderFromXml.cs
+++ /dev/null
@@ -1,93 +0,0 @@
-using MediaBrowser.Common.IO;
-using MediaBrowser.Controller.Configuration;
-using MediaBrowser.Controller.Entities;
-using MediaBrowser.Controller.Providers;
-using MediaBrowser.Model.Entities;
-using MediaBrowser.Model.Logging;
-using System;
-using System.IO;
-using System.Threading;
-using System.Threading.Tasks;
-
-namespace MediaBrowser.Providers
-{
- /// <summary>
- /// Provides metadata for Folders and all subclasses by parsing folder.xml
- /// </summary>
- public class FolderProviderFromXml : BaseMetadataProvider
- {
- private readonly IFileSystem _fileSystem;
-
- public FolderProviderFromXml(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.IsFolder && item.LocationType == LocationType.FileSystem;
- }
-
- /// <summary>
- /// Gets the priority.
- /// </summary>
- /// <value>The priority.</value>
- public override MetadataProviderPriority Priority
- {
- get { return MetadataProviderPriority.First; }
- }
-
- private const string XmlFileName = "folder.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<Folder>(Logger).Fetch((Folder)item, path, cancellationToken);
- }
- finally
- {
- XmlParsingResourcePool.Release();
- }
- }
-
- SetLastRefreshed(item, DateTime.UtcNow, providerInfo);
- return true;
- }
- }
-}
diff --git a/MediaBrowser.Providers/Folders/FolderMetadataService.cs b/MediaBrowser.Providers/Folders/FolderMetadataService.cs
new file mode 100644
index 000000000..cc688d91a
--- /dev/null
+++ b/MediaBrowser.Providers/Folders/FolderMetadataService.cs
@@ -0,0 +1,52 @@
+using MediaBrowser.Common.IO;
+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.Folders
+{
+ public class FolderMetadataService : MetadataService<Folder, ItemId>
+ {
+ private readonly ILibraryManager _libraryManager;
+
+ public FolderMetadataService(IServerConfigurationManager serverConfigurationManager, ILogger logger, IProviderManager providerManager, IProviderRepository providerRepo, IFileSystem fileSystem, ILibraryManager libraryManager)
+ : base(serverConfigurationManager, logger, providerManager, providerRepo, fileSystem)
+ {
+ _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(Folder source, Folder target, List<MetadataFields> lockedFields, bool replaceData, bool mergeMetadataSettings)
+ {
+ ProviderUtils.MergeBaseItemData(source, target, lockedFields, replaceData, mergeMetadataSettings);
+ }
+
+ protected override Task SaveItem(Folder item, ItemUpdateType reason, CancellationToken cancellationToken)
+ {
+ return _libraryManager.UpdateItem(item, reason, cancellationToken);
+ }
+
+ public override int Order
+ {
+ get
+ {
+ // Make sure the type-specific services get picked first
+ return 10;
+ }
+ }
+ }
+}
diff --git a/MediaBrowser.Providers/Folders/FolderXmlProvider.cs b/MediaBrowser.Providers/Folders/FolderXmlProvider.cs
new file mode 100644
index 000000000..2fc6a8290
--- /dev/null
+++ b/MediaBrowser.Providers/Folders/FolderXmlProvider.cs
@@ -0,0 +1,33 @@
+using MediaBrowser.Common.IO;
+using MediaBrowser.Controller.Entities;
+using MediaBrowser.Controller.Providers;
+using MediaBrowser.Model.Logging;
+using System.IO;
+using System.Threading;
+
+namespace MediaBrowser.Providers.Folders
+{
+ /// <summary>
+ /// Provides metadata for Folders and all subclasses by parsing folder.xml
+ /// </summary>
+ public class FolderXmlProvider : BaseXmlProvider<Folder>
+ {
+ private readonly ILogger _logger;
+
+ public FolderXmlProvider(IFileSystem fileSystem, ILogger logger)
+ : base(fileSystem)
+ {
+ _logger = logger;
+ }
+
+ protected override void Fetch(Folder item, string path, CancellationToken cancellationToken)
+ {
+ new BaseItemXmlParser<Folder>(_logger).Fetch(item, path, cancellationToken);
+ }
+
+ protected override FileInfo GetXmlFile(ItemInfo info)
+ {
+ return new FileInfo(Path.Combine(info.Path, "folder.xml"));
+ }
+ }
+}
diff --git a/MediaBrowser.Providers/UserRootFolderNameProvider.cs b/MediaBrowser.Providers/Folders/UserRootFolderNameProvider.cs
index 551dd4257..043e32d11 100644
--- a/MediaBrowser.Providers/UserRootFolderNameProvider.cs
+++ b/MediaBrowser.Providers/Folders/UserRootFolderNameProvider.cs
@@ -7,7 +7,7 @@ using System.IO;
using System.Threading;
using System.Threading.Tasks;
-namespace MediaBrowser.Providers
+namespace MediaBrowser.Providers.Folders
{
public class UserRootFolderNameProvider : BaseMetadataProvider
{
diff --git a/MediaBrowser.Providers/Games/GameSystemXmlProvider.cs b/MediaBrowser.Providers/Games/GameSystemXmlProvider.cs
index 61fb791e6..3e74f4c32 100644
--- a/MediaBrowser.Providers/Games/GameSystemXmlProvider.cs
+++ b/MediaBrowser.Providers/Games/GameSystemXmlProvider.cs
@@ -4,11 +4,10 @@ using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Logging;
using System.IO;
using System.Threading;
-using System.Threading.Tasks;
namespace MediaBrowser.Providers.Games
{
- public class GameSystemXmlProvider : BaseXmlProvider, ILocalMetadataProvider<GameSystem>
+ public class GameSystemXmlProvider : BaseXmlProvider<GameSystem>
{
private readonly ILogger _logger;
@@ -18,42 +17,14 @@ namespace MediaBrowser.Providers.Games
_logger = logger;
}
- public async Task<MetadataResult<GameSystem>> GetMetadata(string path, CancellationToken cancellationToken)
+ protected override void Fetch(GameSystem item, string path, CancellationToken cancellationToken)
{
- path = GetXmlFile(path).FullName;
-
- var result = new MetadataResult<GameSystem>();
-
- await XmlParsingResourcePool.WaitAsync(cancellationToken).ConfigureAwait(false);
-
- try
- {
- var item = new GameSystem();
-
- new GameSystemXmlParser(_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"; }
+ new GameSystemXmlParser(_logger).Fetch(item, path, cancellationToken);
}
- protected override FileInfo GetXmlFile(string path)
+ protected override FileInfo GetXmlFile(ItemInfo info)
{
- return new FileInfo(Path.Combine(path, "gamesystem.xml"));
+ return new FileInfo(Path.Combine(info.Path, "gamesystem.xml"));
}
}
}
diff --git a/MediaBrowser.Providers/Games/GameXmlProvider.cs b/MediaBrowser.Providers/Games/GameXmlProvider.cs
index e2a67de8d..644fe3e42 100644
--- a/MediaBrowser.Providers/Games/GameXmlProvider.cs
+++ b/MediaBrowser.Providers/Games/GameXmlProvider.cs
@@ -4,11 +4,10 @@ using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Logging;
using System.IO;
using System.Threading;
-using System.Threading.Tasks;
namespace MediaBrowser.Providers.Games
{
- public class GameXmlProvider : BaseXmlProvider, ILocalMetadataProvider<Game>
+ public class GameXmlProvider : BaseXmlProvider<Game>
{
private readonly ILogger _logger;
@@ -18,57 +17,29 @@ namespace MediaBrowser.Providers.Games
_logger = logger;
}
- public async Task<MetadataResult<Game>> GetMetadata(string path, CancellationToken cancellationToken)
+ protected override void Fetch(Game item, string path, CancellationToken cancellationToken)
{
- path = GetXmlFile(path).FullName;
-
- var result = new MetadataResult<Game>();
-
- await XmlParsingResourcePool.WaitAsync(cancellationToken).ConfigureAwait(false);
-
- try
- {
- var item = new Game();
-
- new GameXmlParser(_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"; }
+ new GameXmlParser(_logger).Fetch(item, path, cancellationToken);
}
- protected override FileInfo GetXmlFile(string path)
+ protected override FileInfo GetXmlFile(ItemInfo info)
{
- var fileInfo = FileSystem.GetFileSystemInfo(path);
+ var fileInfo = FileSystem.GetFileSystemInfo(info.Path);
var directoryInfo = fileInfo as DirectoryInfo;
if (directoryInfo == null)
{
- directoryInfo = new DirectoryInfo(Path.GetDirectoryName(path));
+ directoryInfo = new DirectoryInfo(Path.GetDirectoryName(info.Path));
}
var directoryPath = directoryInfo.FullName;
- var specificFile = Path.Combine(directoryPath, Path.GetFileNameWithoutExtension(path) + ".xml");
+ var specificFile = Path.Combine(directoryPath, Path.GetFileNameWithoutExtension(info.Path) + ".xml");
var file = new FileInfo(specificFile);
- return file.Exists ? file : new FileInfo(Path.Combine(directoryPath, "game.xml"));
+ return info.IsInMixedFolder || file.Exists ? file : new FileInfo(Path.Combine(directoryPath, "game.xml"));
}
}
}
diff --git a/MediaBrowser.Providers/ImageFromMediaLocationProvider.cs b/MediaBrowser.Providers/ImageFromMediaLocationProvider.cs
deleted file mode 100644
index bc58f3178..000000000
--- a/MediaBrowser.Providers/ImageFromMediaLocationProvider.cs
+++ /dev/null
@@ -1,637 +0,0 @@
-using MediaBrowser.Common.IO;
-using MediaBrowser.Controller.Configuration;
-using MediaBrowser.Controller.Entities;
-using MediaBrowser.Controller.Entities.Movies;
-using MediaBrowser.Controller.Entities.TV;
-using MediaBrowser.Controller.Library;
-using MediaBrowser.Controller.Providers;
-using MediaBrowser.Model.Entities;
-using MediaBrowser.Model.Logging;
-using System;
-using System.Collections.Generic;
-using System.Globalization;
-using System.IO;
-using System.Linq;
-using System.Threading;
-using System.Threading.Tasks;
-
-namespace MediaBrowser.Providers
-{
- /// <summary>
- /// Provides images for all types by looking for standard images - folder, backdrop, logo, etc.
- /// </summary>
- public class ImageFromMediaLocationProvider : BaseMetadataProvider
- {
- protected readonly IFileSystem FileSystem;
-
- public ImageFromMediaLocationProvider(ILogManager logManager, IServerConfigurationManager configurationManager, IFileSystem fileSystem)
- : base(logManager, configurationManager)
- {
- FileSystem = fileSystem;
- }
-
- public override ItemUpdateType ItemUpdateType
- {
- get
- {
- return ItemUpdateType.ImageUpdate;
- }
- }
-
- /// <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)
- {
- if (item.LocationType == LocationType.FileSystem)
- {
- if (item.ResolveArgs.IsDirectory)
- {
- return true;
- }
-
- return item.IsInMixedFolder && item.Parent != null && !(item is Episode);
- }
- if (item.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;
- }
-
- protected override IEnumerable<BaseItem> GetItemsForFileStampComparison(BaseItem item)
- {
- var season = item as Season;
- if (season != null)
- {
- var list = new List<BaseItem>();
-
- if (season.LocationType == LocationType.FileSystem)
- {
- list.Add(season);
- }
-
- var series = season.Series;
- if (series != null && series.LocationType == LocationType.FileSystem)
- {
- list.Add(series);
- }
-
- return list;
- }
-
- return base.GetItemsForFileStampComparison(item);
- }
-
- /// <summary>
- /// Gets the priority.
- /// </summary>
- /// <value>The priority.</value>
- public override MetadataProviderPriority Priority
- {
- get { return MetadataProviderPriority.First; }
- }
-
- /// <summary>
- /// Returns true or false indicating if the provider should refresh when the contents of it's directory changes
- /// </summary>
- /// <value><c>true</c> if [refresh on file system stamp change]; otherwise, <c>false</c>.</value>
- protected override bool RefreshOnFileSystemStampChange
- {
- get
- {
- return true;
- }
- }
-
- /// <summary>
- /// Gets the filestamp extensions.
- /// </summary>
- /// <value>The filestamp extensions.</value>
- protected override string[] FilestampExtensions
- {
- get
- {
- return BaseItem.SupportedImageExtensions;
- }
- }
-
- /// <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 Task<bool> FetchAsync(BaseItem item, bool force, BaseProviderInfo providerInfo, CancellationToken cancellationToken)
- {
- cancellationToken.ThrowIfCancellationRequested();
-
- // Make sure current image paths still exist
- item.ValidateImages();
-
- cancellationToken.ThrowIfCancellationRequested();
-
- var args = GetResolveArgsContainingImages(item);
-
- PopulateBaseItemImages(item, args);
-
- SetLastRefreshed(item, DateTime.UtcNow, providerInfo);
- return TrueTaskResult;
- }
-
- private ItemResolveArgs GetResolveArgsContainingImages(BaseItem item)
- {
- if (item.LocationType != LocationType.FileSystem)
- {
- return null;
- }
-
- if (item.IsInMixedFolder)
- {
- if (item.Parent == null)
- {
- return item.ResolveArgs;
- }
- return item.Parent.ResolveArgs;
- }
-
- return item.ResolveArgs;
- }
-
- /// <summary>
- /// Gets the image.
- /// </summary>
- /// <param name="item">The item.</param>
- /// <param name="args">The args.</param>
- /// <param name="filenameWithoutExtension">The filename without extension.</param>
- /// <returns>FileSystemInfo.</returns>
- protected virtual FileSystemInfo GetImage(BaseItem item, ItemResolveArgs args, string filenameWithoutExtension)
- {
- if (string.IsNullOrEmpty(item.MetaLocation))
- {
- return null;
- }
-
- return BaseItem.SupportedImageExtensions
- .Select(i => args.GetMetaFileByPath(GetFullImagePath(item, args, filenameWithoutExtension, i)))
- .FirstOrDefault(i => i != null);
- }
-
- protected virtual string GetFullImagePath(BaseItem item, ItemResolveArgs args, string filenameWithoutExtension, string extension)
- {
- var path = item.MetaLocation;
-
- if (item.IsInMixedFolder)
- {
- var pathFilenameWithoutExtension = Path.GetFileNameWithoutExtension(item.Path);
-
- // If the image filename and path file name match, just look for an image using the same full path as the item
- if (string.Equals(pathFilenameWithoutExtension, filenameWithoutExtension))
- {
- return Path.ChangeExtension(item.Path, extension);
- }
-
- return Path.Combine(path, pathFilenameWithoutExtension + "-" + filenameWithoutExtension + extension);
- }
-
- return Path.Combine(path, filenameWithoutExtension + extension);
- }
-
- private readonly CultureInfo _usCulture = new CultureInfo("en-US");
-
- /// <summary>
- /// Fills in image paths based on files win the folder
- /// </summary>
- /// <param name="item">The item.</param>
- /// <param name="args">The args.</param>
- private void PopulateBaseItemImages(BaseItem item, ItemResolveArgs args)
- {
- PopulatePrimaryImage(item, args);
-
- // Logo Image
- var image = GetImage(item, args, "logo");
-
- if (image != null)
- {
- item.SetImagePath(ImageType.Logo, image.FullName);
- }
-
- // Clearart
- image = GetImage(item, args, "clearart");
-
- if (image != null)
- {
- item.SetImagePath(ImageType.Art, image.FullName);
- }
-
- // Disc
- image = GetImage(item, args, "disc") ??
- GetImage(item, args, "cdart");
-
- if (image != null)
- {
- item.SetImagePath(ImageType.Disc, image.FullName);
- }
-
- // Box Image
- image = GetImage(item, args, "box");
-
- if (image != null)
- {
- item.SetImagePath(ImageType.Box, image.FullName);
- }
-
- // BoxRear Image
- image = GetImage(item, args, "boxrear");
-
- if (image != null)
- {
- item.SetImagePath(ImageType.BoxRear, image.FullName);
- }
-
- // Thumbnail Image
- image = GetImage(item, args, "menu");
-
- if (image != null)
- {
- item.SetImagePath(ImageType.Menu, image.FullName);
- }
-
- PopulateBanner(item, args);
- PopulateThumb(item, args);
-
- // Backdrop Image
- PopulateBackdrops(item, args);
- PopulateScreenshots(item, args);
- }
-
- private void PopulatePrimaryImage(BaseItem item, ItemResolveArgs args)
- {
- // Primary Image
- var image = GetImage(item, args, "folder") ??
- GetImage(item, args, "poster") ??
- GetImage(item, args, "cover") ??
- GetImage(item, args, "default");
-
- // Support plex/xbmc convention
- if (image == null && item is Series)
- {
- image = GetImage(item, args, "show");
- }
-
- // Support plex/xbmc convention
- if (image == null)
- {
- // Supprt xbmc conventions
- var season = item as Season;
- if (season != null && item.IndexNumber.HasValue && season.Series.LocationType == LocationType.FileSystem)
- {
- image = GetSeasonImageFromSeriesFolder(season, "-poster");
- }
- }
-
- // Support plex/xbmc convention
- if (image == null && (item is Movie || item is MusicVideo || item is AdultVideo))
- {
- image = GetImage(item, args, "movie");
- }
-
- // Look for a file with the same name as the item
- if (image == null && !string.IsNullOrEmpty(item.Path))
- {
- var name = Path.GetFileNameWithoutExtension(item.Path);
-
- if (!string.IsNullOrEmpty(name))
- {
- image = GetImage(item, args, name) ??
- GetImage(item, args, name + "-poster");
- }
- }
-
- if (image != null)
- {
- item.SetImagePath(ImageType.Primary, image.FullName);
- }
- }
-
- /// <summary>
- /// Populates the banner.
- /// </summary>
- /// <param name="item">The item.</param>
- /// <param name="args">The args.</param>
- private void PopulateBanner(BaseItem item, ItemResolveArgs args)
- {
- // Banner Image
- var image = GetImage(item, args, "banner");
-
- if (image == null)
- {
- // Supprt xbmc conventions
- var season = item as Season;
- if (season != null && item.IndexNumber.HasValue && season.Series.LocationType == LocationType.FileSystem)
- {
- image = GetSeasonImageFromSeriesFolder(season, "-banner");
- }
- }
-
- if (image != null)
- {
- item.SetImagePath(ImageType.Banner, image.FullName);
- }
- }
-
- /// <summary>
- /// Populates the thumb.
- /// </summary>
- /// <param name="item">The item.</param>
- /// <param name="args">The args.</param>
- private void PopulateThumb(BaseItem item, ItemResolveArgs args)
- {
- // Thumbnail Image
- var image = GetImage(item, args, "thumb") ??
- GetImage(item, args, "landscape");
-
- if (image == null)
- {
- // Supprt xbmc conventions
- var season = item as Season;
- if (season != null && item.IndexNumber.HasValue && season.Series.LocationType == LocationType.FileSystem)
- {
- image = GetSeasonImageFromSeriesFolder(season, "-landscape");
- }
- }
-
- if (image != null)
- {
- item.SetImagePath(ImageType.Thumb, image.FullName);
- }
-
- }
-
- /// <summary>
- /// Populates the backdrops.
- /// </summary>
- /// <param name="item">The item.</param>
- /// <param name="args">The args.</param>
- private void PopulateBackdrops(BaseItem item, ItemResolveArgs args)
- {
- var backdropFiles = new List<string>();
-
- PopulateBackdrops(item, args, backdropFiles, "backdrop", "backdrop");
-
- // Support {name}-fanart.ext
- if (!string.IsNullOrEmpty(item.Path))
- {
- var name = Path.GetFileNameWithoutExtension(item.Path);
-
- if (!string.IsNullOrEmpty(name))
- {
- var image = GetImage(item, args, name + "-fanart");
-
- if (image != null)
- {
- backdropFiles.Add(image.FullName);
- }
- }
- }
-
- // Support plex/xbmc conventions
- PopulateBackdrops(item, args, backdropFiles, "fanart", "fanart-");
- PopulateBackdrops(item, args, backdropFiles, "background", "background-");
- PopulateBackdrops(item, args, backdropFiles, "art", "art-");
-
- var season = item as Season;
- if (season != null && item.IndexNumber.HasValue && season.Series.LocationType == LocationType.FileSystem)
- {
- var image = GetSeasonImageFromSeriesFolder(season, "-fanart");
-
- if (image != null)
- {
- backdropFiles.Add(image.FullName);
- }
- }
-
- if (item.LocationType == LocationType.FileSystem)
- {
- PopulateBackdropsFromExtraFanart(args, backdropFiles);
- }
-
- if (backdropFiles.Count > 0)
- {
- item.BackdropImagePaths = backdropFiles;
- }
- }
-
- private FileSystemInfo GetSeasonImageFromSeriesFolder(Season season, string imageSuffix)
- {
- var series = season.Series;
- var seriesFolderArgs = series.ResolveArgs;
-
- var seasonNumber = season.IndexNumber;
-
- string filename = null;
- FileSystemInfo image;
-
- if (seasonNumber.HasValue)
- {
- 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
- filename = "season" + seasonMarker + imageSuffix;
-
- image = GetImage(series, seriesFolderArgs, filename);
-
- if (image != null && image.Exists)
- {
- return image;
- }
- }
-
- var previousFilename = filename;
-
- // Try using the season name
- filename = season.Name.ToLower().Replace(" ", string.Empty) + imageSuffix;
-
- if (!string.Equals(previousFilename, filename))
- {
- image = GetImage(series, seriesFolderArgs, filename);
-
- if (image != null && image.Exists)
- {
- return image;
- }
- }
-
- return null;
- }
-
- /// <summary>
- /// Populates the backdrops from extra fanart.
- /// </summary>
- /// <param name="args">The args.</param>
- /// <param name="backdrops">The backdrops.</param>
- private void PopulateBackdropsFromExtraFanart(ItemResolveArgs args, List<string> backdrops)
- {
- if (!args.IsDirectory)
- {
- return;
- }
-
- if (args.ContainsFileSystemEntryByName("extrafanart"))
- {
- var path = Path.Combine(args.Path, "extrafanart");
-
- 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);
- })
- .ToList();
-
- backdrops.AddRange(imageFiles);
- }
- }
-
- /// <summary>
- /// Populates the backdrops.
- /// </summary>
- /// <param name="item">The item.</param>
- /// <param name="args">The args.</param>
- /// <param name="backdropFiles">The backdrop files.</param>
- /// <param name="filename">The filename.</param>
- /// <param name="numberedSuffix">The numbered suffix.</param>
- private void PopulateBackdrops(BaseItem item, ItemResolveArgs args, List<string> backdropFiles, string filename, string numberedSuffix)
- {
- var image = GetImage(item, args, filename);
-
- if (image != null)
- {
- backdropFiles.Add(image.FullName);
- }
-
- var unfound = 0;
- for (var i = 1; i <= 20; i++)
- {
- // Backdrop Image
- image = GetImage(item, args, numberedSuffix + i);
-
- if (image != null)
- {
- backdropFiles.Add(image.FullName);
- }
- else
- {
- unfound++;
-
- if (unfound >= 3)
- {
- break;
- }
- }
- }
- }
-
- /// <summary>
- /// Populates the screenshots.
- /// </summary>
- /// <param name="item">The item.</param>
- /// <param name="args">The args.</param>
- private void PopulateScreenshots(BaseItem item, ItemResolveArgs args)
- {
- // Screenshot Image
- var image = GetImage(item, args, "screenshot");
-
- var screenshotFiles = new List<string>();
-
- if (image != null)
- {
- screenshotFiles.Add(image.FullName);
- }
-
- var unfound = 0;
- for (var i = 1; i <= 20; i++)
- {
- // Screenshot Image
- image = GetImage(item, args, "screenshot" + i);
-
- if (image != null)
- {
- screenshotFiles.Add(image.FullName);
- }
- else
- {
- unfound++;
-
- if (unfound >= 3)
- {
- break;
- }
- }
- }
-
- if (screenshotFiles.Count > 0)
- {
- var hasScreenshots = item as IHasScreenshots;
- if (hasScreenshots != null)
- {
- hasScreenshots.ScreenshotImagePaths = screenshotFiles;
- }
- }
- }
-
- protected FileSystemInfo GetImageFromLocation(string path, string filenameWithoutExtension)
- {
- try
- {
- var files = new DirectoryInfo(path)
- .EnumerateFiles()
- .Where(i =>
- {
- var fileName = Path.GetFileNameWithoutExtension(i.FullName);
-
- if (!string.Equals(fileName, filenameWithoutExtension, StringComparison.OrdinalIgnoreCase))
- {
- return false;
- }
-
- var ext = i.Extension;
-
- return !string.IsNullOrEmpty(ext) &&
- BaseItem.SupportedImageExtensions.Contains(ext, StringComparer.OrdinalIgnoreCase);
- })
- .ToList();
-
- return BaseItem.SupportedImageExtensions
- .Select(ext => files.FirstOrDefault(i => string.Equals(ext, i.Extension, StringComparison.OrdinalIgnoreCase)))
- .FirstOrDefault(file => file != null);
- }
- catch (DirectoryNotFoundException)
- {
- return null;
- }
- }
- }
-}
diff --git a/MediaBrowser.Providers/ImagesByNameProvider.cs b/MediaBrowser.Providers/ImagesByNameProvider.cs
deleted file mode 100644
index f63417026..000000000
--- a/MediaBrowser.Providers/ImagesByNameProvider.cs
+++ /dev/null
@@ -1,100 +0,0 @@
-using System.Collections.Generic;
-using MediaBrowser.Common.IO;
-using MediaBrowser.Controller.Configuration;
-using MediaBrowser.Controller.Entities;
-using MediaBrowser.Controller.Library;
-using MediaBrowser.Controller.Providers;
-using MediaBrowser.Model.Logging;
-using System;
-using System.IO;
-using System.Linq;
-
-namespace MediaBrowser.Providers
-{
- /// <summary>
- /// Provides images for generic types by looking for standard images in the IBN
- /// </summary>
- public class ImagesByNameProvider : ImageFromMediaLocationProvider
- {
- public ImagesByNameProvider(ILogManager logManager, IServerConfigurationManager configurationManager, IFileSystem fileSystem)
- : base(logManager, configurationManager, 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)
- {
- // Only run for these generic types since we are expensive in file i/o
- return item is ICollectionFolder;
- }
-
- /// <summary>
- /// Gets the priority.
- /// </summary>
- /// <value>The priority.</value>
- public override MetadataProviderPriority Priority
- {
- get
- {
- return MetadataProviderPriority.Last;
- }
- }
-
- /// <summary>
- /// Gets the location.
- /// </summary>
- /// <param name="item">The item.</param>
- /// <returns>System.String.</returns>
- protected string GetLocation(BaseItem item)
- {
- var name = FileSystem.GetValidFilename(item.Name);
-
- return Path.Combine(ConfigurationManager.ApplicationPaths.GeneralPath, name);
- }
-
- /// <summary>
- /// Gets the image.
- /// </summary>
- /// <param name="item">The item.</param>
- /// <param name="args">The args.</param>
- /// <param name="filenameWithoutExtension">The filename without extension.</param>
- /// <returns>FileSystemInfo.</returns>
- protected override FileSystemInfo GetImage(BaseItem item, ItemResolveArgs args, string filenameWithoutExtension)
- {
- var location = GetLocation(item);
-
- return GetImageFromLocation(location, filenameWithoutExtension);
- }
-
- protected override Guid GetFileSystemStamp(IEnumerable<BaseItem> items)
- {
- var location = GetLocation(items.First());
-
- try
- {
- var files = new DirectoryInfo(location)
- .EnumerateFiles("*", SearchOption.TopDirectoryOnly)
- .Where(i =>
- {
- var ext = i.Extension;
-
- return !string.IsNullOrEmpty(ext) &&
- BaseItem.SupportedImageExtensions.Contains(ext, StringComparer.OrdinalIgnoreCase);
- })
- .ToList();
-
- return GetFileSystemStamp(files);
- }
- catch (DirectoryNotFoundException)
- {
- // User doesn't have the folder. No need to log or blow up
-
- return Guid.Empty;
- }
- }
- }
-}
diff --git a/MediaBrowser.Providers/LiveTv/ChannelXmlProvider.cs b/MediaBrowser.Providers/LiveTv/ChannelXmlProvider.cs
index 701cc9f85..af3de824d 100644
--- a/MediaBrowser.Providers/LiveTv/ChannelXmlProvider.cs
+++ b/MediaBrowser.Providers/LiveTv/ChannelXmlProvider.cs
@@ -4,11 +4,10 @@ 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>
+ public class ChannelXmlProvider : BaseXmlProvider<LiveTvChannel>
{
private readonly ILogger _logger;
@@ -18,42 +17,14 @@ namespace MediaBrowser.Providers.LiveTv
_logger = logger;
}
- public async Task<MetadataResult<LiveTvChannel>> GetMetadata(string path, CancellationToken cancellationToken)
+ protected override void Fetch(LiveTvChannel item, string path, CancellationToken cancellationToken)
{
- path = GetXmlFile(path).FullName;
-
- 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"; }
+ new BaseItemXmlParser<LiveTvChannel>(_logger).Fetch(item, path, cancellationToken);
}
- protected override FileInfo GetXmlFile(string path)
+ protected override FileInfo GetXmlFile(ItemInfo info)
{
- return new FileInfo(Path.Combine(path, "channel.xml"));
+ return new FileInfo(Path.Combine(info.Path, "channel.xml"));
}
}
}
diff --git a/MediaBrowser.Providers/Manager/ImageSaver.cs b/MediaBrowser.Providers/Manager/ImageSaver.cs
index 52aaf412a..2a4ef7597 100644
--- a/MediaBrowser.Providers/Manager/ImageSaver.cs
+++ b/MediaBrowser.Providers/Manager/ImageSaver.cs
@@ -379,14 +379,19 @@ namespace MediaBrowser.Providers.Manager
if (saveLocally)
{
- if (item.IsInMixedFolder && !(item is Episode))
+ if (item is Episode)
+ {
+ path = Path.Combine(Path.GetDirectoryName(item.Path), "metadata", filename + extension);
+ }
+
+ else if (item.IsInMixedFolder)
{
path = GetSavePathForItemInMixedFolder(item, type, filename, extension);
}
if (string.IsNullOrEmpty(path))
{
- path = Path.Combine(item.MetaLocation, filename + extension);
+ path = Path.Combine(item.ContainingFolderPath, filename + extension);
}
}
@@ -468,7 +473,7 @@ namespace MediaBrowser.Providers.Manager
return new[]
{
- Path.Combine(item.MetaLocation, "fanart" + extension)
+ Path.Combine(item.ContainingFolderPath, "fanart" + extension)
};
}
@@ -483,8 +488,8 @@ namespace MediaBrowser.Providers.Manager
return new[]
{
- Path.Combine(item.MetaLocation, "extrafanart", extraFanartFilename + extension),
- Path.Combine(item.MetaLocation, "extrathumbs", "thumb" + outputIndex.ToString(UsCulture) + extension)
+ Path.Combine(item.ContainingFolderPath, "extrafanart", extraFanartFilename + extension),
+ Path.Combine(item.ContainingFolderPath, "extrathumbs", "thumb" + outputIndex.ToString(UsCulture) + extension)
};
}
@@ -519,10 +524,10 @@ namespace MediaBrowser.Providers.Manager
if (item is MusicAlbum || item is MusicArtist)
{
- return new[] { Path.Combine(item.MetaLocation, "folder" + extension) };
+ return new[] { Path.Combine(item.ContainingFolderPath, "folder" + extension) };
}
- return new[] { Path.Combine(item.MetaLocation, "poster" + extension) };
+ return new[] { Path.Combine(item.ContainingFolderPath, "poster" + extension) };
}
if (type == ImageType.Banner)
@@ -561,7 +566,7 @@ namespace MediaBrowser.Providers.Manager
return new[] { GetSavePathForItemInMixedFolder(item, type, "landscape", extension) };
}
- return new[] { Path.Combine(item.MetaLocation, "landscape" + extension) };
+ return new[] { Path.Combine(item.ContainingFolderPath, "landscape" + extension) };
}
// All other paths are the same
diff --git a/MediaBrowser.Providers/Manager/ItemImageProvider.cs b/MediaBrowser.Providers/Manager/ItemImageProvider.cs
index 3fb1e7074..f2fa4dc29 100644
--- a/MediaBrowser.Providers/Manager/ItemImageProvider.cs
+++ b/MediaBrowser.Providers/Manager/ItemImageProvider.cs
@@ -93,8 +93,6 @@ namespace MediaBrowser.Providers.Manager
/// <returns>Task.</returns>
private async Task RefreshFromProvider(IHasImages item, IDynamicImageProvider provider, MetadataOptions savedOptions, RefreshResult result, CancellationToken cancellationToken)
{
- _logger.Debug("Running {0} for {1}", provider.GetType().Name, item.Path ?? item.Name);
-
try
{
var images = provider.GetSupportedImages(item);
@@ -103,6 +101,8 @@ namespace MediaBrowser.Providers.Manager
{
if (!item.HasImage(imageType) && savedOptions.IsEnabled(imageType))
{
+ _logger.Debug("Running {0} for {1}", provider.GetType().Name, item.Path ?? item.Name);
+
var response = await provider.GetImage(item, imageType, cancellationToken).ConfigureAwait(false);
if (response.HasImage)
diff --git a/MediaBrowser.Providers/Manager/MetadataService.cs b/MediaBrowser.Providers/Manager/MetadataService.cs
index c817180c5..e0272bc7b 100644
--- a/MediaBrowser.Providers/Manager/MetadataService.cs
+++ b/MediaBrowser.Providers/Manager/MetadataService.cs
@@ -88,12 +88,16 @@ namespace MediaBrowser.Providers.Manager
// Next run metadata providers
if (refreshOptions.MetadataRefreshMode != MetadataRefreshMode.None)
{
- updateType = updateType | BeforeMetadataRefresh(itemOfType);
-
var providers = GetProviders(item, refreshResult.DateLastMetadataRefresh.HasValue, refreshOptions).ToList();
+ if (providers.Count > 0 || !refreshResult.DateLastMetadataRefresh.HasValue)
+ {
+ updateType = updateType | BeforeMetadataRefresh(itemOfType);
+ }
+
if (providers.Count > 0)
{
+
var result = await RefreshWithProviders(itemOfType, refreshOptions, providers, cancellationToken).ConfigureAwait(false);
updateType = updateType | result.UpdateType;
@@ -145,7 +149,7 @@ namespace MediaBrowser.Providers.Manager
{
var type = item.GetType().Name;
return ServerConfigurationManager.Configuration.MetadataOptions
- .FirstOrDefault(i => string.Equals(i.ItemType, type, StringComparison.OrdinalIgnoreCase)) ??
+ .FirstOrDefault(i => string.Equals(i.ItemType, type, StringComparison.OrdinalIgnoreCase)) ??
_defaultOptions;
}
@@ -278,9 +282,11 @@ namespace MediaBrowser.Providers.Manager
{
Logger.Debug("Running {0} for {1}", provider.GetType().Name, item.Path ?? item.Name);
+ var itemInfo = new ItemInfo { Path = item.Path, IsInMixedFolder = item.IsInMixedFolder };
+
try
{
- var localItem = await provider.GetMetadata(item.Path, cancellationToken).ConfigureAwait(false);
+ var localItem = await provider.GetMetadata(itemInfo, cancellationToken).ConfigureAwait(false);
if (localItem.HasMetadata)
{
@@ -327,7 +333,7 @@ namespace MediaBrowser.Providers.Manager
{
await RunCustomProvider(provider, item, refreshResult, cancellationToken).ConfigureAwait(false);
}
-
+
return refreshResult;
}
diff --git a/MediaBrowser.Providers/Manager/ProviderManager.cs b/MediaBrowser.Providers/Manager/ProviderManager.cs
index dbfc97d51..030b3cbd9 100644
--- a/MediaBrowser.Providers/Manager/ProviderManager.cs
+++ b/MediaBrowser.Providers/Manager/ProviderManager.cs
@@ -48,12 +48,6 @@ namespace MediaBrowser.Providers.Manager
/// <value>The configuration manager.</value>
private IServerConfigurationManager ConfigurationManager { get; set; }
- /// <summary>
- /// Gets the list of currently registered metadata prvoiders
- /// </summary>
- /// <value>The metadata providers enumerable.</value>
- private BaseMetadataProvider[] MetadataProviders { get; set; }
-
private IImageProvider[] ImageProviders { get; set; }
private readonly IFileSystem _fileSystem;
@@ -86,15 +80,12 @@ namespace MediaBrowser.Providers.Manager
/// <summary>
/// Adds the metadata providers.
/// </summary>
- /// <param name="providers">The providers.</param>
/// <param name="imageProviders">The image providers.</param>
/// <param name="metadataServices">The metadata services.</param>
/// <param name="metadataProviders">The metadata providers.</param>
/// <param name="metadataSavers">The metadata savers.</param>
- public void AddParts(IEnumerable<BaseMetadataProvider> providers, IEnumerable<IImageProvider> imageProviders, IEnumerable<IMetadataService> metadataServices, IEnumerable<IMetadataProvider> metadataProviders, IEnumerable<IMetadataSaver> metadataSavers)
+ public void AddParts(IEnumerable<IImageProvider> imageProviders, IEnumerable<IMetadataService> metadataServices, IEnumerable<IMetadataProvider> metadataProviders, IEnumerable<IMetadataSaver> metadataSavers)
{
- MetadataProviders = providers.OrderBy(e => e.Priority).ToArray();
-
ImageProviders = imageProviders.ToArray();
_metadataServices = metadataServices.OrderBy(i => i.Order).ToArray();
@@ -111,174 +102,8 @@ namespace MediaBrowser.Providers.Manager
return service.RefreshMetadata(item, options, cancellationToken);
}
- return ((BaseItem)item).RefreshMetadataDirect(cancellationToken, options.ForceSave, options.ReplaceAllMetadata);
- }
-
- /// <summary>
- /// Runs all metadata providers for an entity, and returns true or false indicating if at least one was refreshed and requires persistence
- /// </summary>
- /// <param name="item">The item.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <param name="force">if set to <c>true</c> [force].</param>
- /// <returns>Task{System.Boolean}.</returns>
- /// <exception cref="System.ArgumentNullException">item</exception>
- public async Task<ItemUpdateType?> ExecuteMetadataProviders(BaseItem item, CancellationToken cancellationToken, bool force = false)
- {
- if (item == null)
- {
- throw new ArgumentNullException("item");
- }
-
- ItemUpdateType? result = null;
-
- cancellationToken.ThrowIfCancellationRequested();
-
- var enableInternetProviders = ConfigurationManager.Configuration.EnableInternetProviders;
-
- var providerHistories = item.DateLastSaved == default(DateTime) ?
- new List<BaseProviderInfo>() :
- _providerRepo.GetProviderHistory(item.Id).ToList();
-
- // Run the normal providers sequentially in order of priority
- foreach (var provider in MetadataProviders)
- {
- cancellationToken.ThrowIfCancellationRequested();
-
- if (!ProviderSupportsItem(provider, item))
- {
- continue;
- }
-
- // Skip if internet providers are currently disabled
- if (provider.RequiresInternet && !enableInternetProviders)
- {
- continue;
- }
-
- // Put this check below the await because the needs refresh of the next tier of providers may depend on the previous ones running
- // This is the case for the fan art provider which depends on the movie and tv providers having run before them
- if (provider.RequiresInternet && item.DontFetchMeta && provider.EnforceDontFetchMetadata)
- {
- continue;
- }
-
- var providerInfo = providerHistories.FirstOrDefault(i => i.ProviderId == provider.Id);
-
- if (providerInfo == null)
- {
- providerInfo = new BaseProviderInfo
- {
- ProviderId = provider.Id
- };
- providerHistories.Add(providerInfo);
- }
-
- try
- {
- if (!force && !provider.NeedsRefresh(item, providerInfo))
- {
- continue;
- }
- }
- catch (Exception ex)
- {
- _logger.Error("Error determining NeedsRefresh for {0}", ex, item.Path);
- }
-
- var updateType = await FetchAsync(provider, item, providerInfo, force, cancellationToken).ConfigureAwait(false);
-
- if (updateType.HasValue)
- {
- if (result.HasValue)
- {
- result = result.Value | updateType.Value;
- }
- else
- {
- result = updateType;
- }
- }
- }
-
- if (result.HasValue || force)
- {
- await _providerRepo.SaveProviderHistory(item.Id, providerHistories, cancellationToken);
- }
-
- return result;
- }
-
- /// <summary>
- /// Providers the supports item.
- /// </summary>
- /// <param name="provider">The provider.</param>
- /// <param name="item">The item.</param>
- /// <returns><c>true</c> if XXXX, <c>false</c> otherwise</returns>
- private bool ProviderSupportsItem(BaseMetadataProvider provider, BaseItem item)
- {
- try
- {
- return provider.Supports(item);
- }
- catch (Exception ex)
- {
- _logger.ErrorException("{0} failed in Supports for type {1}", ex, provider.GetType().Name, item.GetType().Name);
- return false;
- }
- }
-
- /// <summary>
- /// Fetches metadata and returns true or false indicating if any work that requires persistence was done
- /// </summary>
- /// <param name="provider">The provider.</param>
- /// <param name="item">The item.</param>
- /// <param name="providerInfo">The provider information.</param>
- /// <param name="force">if set to <c>true</c> [force].</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task{System.Boolean}.</returns>
- /// <exception cref="System.ArgumentNullException">item</exception>
- private async Task<ItemUpdateType?> FetchAsync(BaseMetadataProvider provider, BaseItem item, BaseProviderInfo providerInfo, bool force, CancellationToken cancellationToken)
- {
- if (item == null)
- {
- throw new ArgumentNullException("item");
- }
-
- cancellationToken.ThrowIfCancellationRequested();
-
- _logger.Debug("Running {0} for {1}", provider.GetType().Name, item.Path ?? item.Name ?? "--Unknown--");
-
- try
- {
- var changed = await provider.FetchAsync(item, force, providerInfo, cancellationToken).ConfigureAwait(false);
-
- if (changed)
- {
- return provider.ItemUpdateType;
- }
-
- return null;
- }
- catch (OperationCanceledException ex)
- {
- _logger.Debug("{0} canceled for {1}", provider.GetType().Name, item.Name);
-
- // If the outer cancellation token is the one that caused the cancellation, throw it
- if (cancellationToken.IsCancellationRequested && ex.CancellationToken == cancellationToken)
- {
- throw;
- }
-
- return null;
- }
- catch (Exception ex)
- {
- _logger.ErrorException("{0} failed refreshing {1} {2}", ex, provider.GetType().Name, item.Name, item.Path ?? string.Empty);
-
- provider.SetLastRefreshed(item, DateTime.UtcNow, providerInfo, ProviderRefreshStatus.Failure);
-
- return ItemUpdateType.Unspecified;
- }
+ _logger.Error("Unable to find a metadata service for item of type " + item.GetType().Name);
+ return Task.FromResult(true);
}
/// <summary>
@@ -328,9 +153,6 @@ namespace MediaBrowser.Providers.Manager
await dataToSave.CopyToAsync(fs, StreamDefaults.DefaultCopyToBufferSize, cancellationToken).ConfigureAwait(false);
}
}
-
- // If this is ever used for something other than metadata we can add a file type param
- item.ResolveArgs.AddMetadataFile(path);
}
finally
{
@@ -517,6 +339,15 @@ namespace MediaBrowser.Providers.Manager
return false;
}
+ // If this restriction is ever lifted, movie xml providers will have to be updated to prevent owned items like trailers from reading those files
+ if (item.IsOwnedItem)
+ {
+ if (provider is ILocalMetadataProvider || provider is IRemoteMetadataProvider)
+ {
+ return false;
+ }
+ }
+
return true;
}
@@ -581,6 +412,7 @@ namespace MediaBrowser.Providers.Manager
list.Add(GetPluginSummary<AdultVideo>());
list.Add(GetPluginSummary<MusicVideo>());
+ list.Add(GetPluginSummary<Video>());
list.Add(GetPluginSummary<LiveTvChannel>());
list.Add(GetPluginSummary<LiveTvProgram>());
@@ -678,6 +510,8 @@ namespace MediaBrowser.Providers.Manager
{
foreach (var saver in _savers.Where(i => i.IsEnabledFor(item, updateType)))
{
+ _logger.Debug("Saving {0} to {1}.", item.Path ?? item.Name, saver.Name);
+
var fileSaver = saver as IMetadataFileSaver;
if (fileSaver != null)
diff --git a/MediaBrowser.Providers/MediaBrowser.Providers.csproj b/MediaBrowser.Providers/MediaBrowser.Providers.csproj
index ad8465009..2c710693e 100644
--- a/MediaBrowser.Providers/MediaBrowser.Providers.csproj
+++ b/MediaBrowser.Providers/MediaBrowser.Providers.csproj
@@ -65,11 +65,13 @@
</ItemGroup>
<ItemGroup>
<Compile Include="AdultVideos\AdultVideoMetadataService.cs" />
+ <Compile Include="AdultVideos\AdultVideoXmlProvider.cs" />
<Compile Include="All\LocalImageProvider.cs" />
<Compile Include="Books\BookMetadataService.cs" />
<Compile Include="BoxSets\BoxSetMetadataService.cs" />
<Compile Include="BoxSets\MovieDbBoxSetImageProvider.cs" />
<Compile Include="BoxSets\MovieDbBoxSetProvider.cs" />
+ <Compile Include="Folders\FolderMetadataService.cs" />
<Compile Include="GameGenres\GameGenreMetadataService.cs" />
<Compile Include="Games\GameMetadataService.cs" />
<Compile Include="Games\GameSystemMetadataService.cs" />
@@ -83,33 +85,34 @@
<Compile Include="Manager\ProviderManager.cs" />
<Compile Include="Manager\MetadataService.cs" />
<Compile Include="BaseXmlProvider.cs" />
- <Compile Include="CollectionFolderImageProvider.cs" />
- <Compile Include="FolderProviderFromXml.cs" />
+ <Compile Include="Folders\FolderXmlProvider.cs" />
<Compile Include="Games\GameXmlParser.cs" />
<Compile Include="Games\GameXmlProvider.cs" />
<Compile Include="Games\GameSystemXmlProvider.cs" />
- <Compile Include="ImageFromMediaLocationProvider.cs" />
- <Compile Include="ImagesByNameProvider.cs" />
+ <Compile Include="MediaInfo\FFProbeAudioInfo.cs" />
<Compile Include="MediaInfo\FFProbeHelpers.cs" />
<Compile Include="MediaInfo\FFProbeProvider.cs" />
<Compile Include="MediaInfo\FFProbeVideoInfo.cs" />
+ <Compile Include="Movies\TrailerMetadataService.cs" />
+ <Compile Include="Movies\GenericMovieDbInfo.cs" />
<Compile Include="Movies\MovieDbSearch.cs" />
+ <Compile Include="Movies\MovieMetadataService.cs" />
<Compile Include="Movies\MovieXmlProvider.cs" />
+ <Compile Include="Movies\TmdbSettings.cs" />
+ <Compile Include="Movies\TrailerXmlProvider.cs" />
<Compile Include="MusicGenres\MusicGenreImageProvider.cs" />
<Compile Include="GameGenres\GameGenreImageProvider.cs" />
<Compile Include="Genres\GenreImageProvider.cs" />
<Compile Include="ImagesByName\ImageUtils.cs" />
<Compile Include="MediaInfo\AudioImageProvider.cs" />
- <Compile Include="MediaInfo\BaseFFProbeProvider.cs" />
- <Compile Include="MediaInfo\FFProbeAudioInfoProvider.cs" />
- <Compile Include="MediaInfo\FFProbeVideoInfoProvider.cs" />
<Compile Include="MediaInfo\VideoImageProvider.cs" />
<Compile Include="BoxSets\BoxSetXmlProvider.cs" />
- <Compile Include="Movies\ManualMovieDbImageProvider.cs" />
- <Compile Include="Movies\ManualFanartMovieImageProvider.cs" />
+ <Compile Include="Movies\MovieDbImageProvider.cs" />
+ <Compile Include="Movies\FanartMovieImageProvider.cs" />
<Compile Include="MusicGenres\MusicGenreMetadataService.cs" />
<Compile Include="Music\AlbumMetadataService.cs" />
<Compile Include="Music\ArtistMetadataService.cs" />
+ <Compile Include="Music\AudioMetadataService.cs" />
<Compile Include="Music\LastfmArtistProvider.cs" />
<Compile Include="Music\MusicBrainzArtistProvider.cs" />
<Compile Include="Music\MusicVideoMetadataService.cs" />
@@ -119,12 +122,8 @@
<Compile Include="People\MovieDbPersonImageProvider.cs" />
<Compile Include="Movies\MovieUpdatesPrescanTask.cs" />
<Compile Include="Movies\MovieXmlParser.cs" />
- <Compile Include="Movies\FanArtMovieProvider.cs" />
<Compile Include="Movies\FanArtMovieUpdatesPrescanTask.cs" />
- <Compile Include="Movies\MovieDbImagesProvider.cs" />
<Compile Include="Movies\MovieDbProvider.cs" />
- <Compile Include="Movies\MovieProviderFromXml.cs" />
- <Compile Include="Movies\OpenMovieDatabaseProvider.cs" />
<Compile Include="Music\AlbumXmlProvider.cs" />
<Compile Include="Music\ArtistXmlProvider.cs" />
<Compile Include="Music\FanArtUpdatesPrescanTask.cs" />
@@ -177,9 +176,10 @@
<Compile Include="TV\SeriesXmlProvider.cs" />
<Compile Include="TV\SeriesXmlParser.cs" />
<Compile Include="TV\TvdbPrescanTask.cs" />
- <Compile Include="UserRootFolderNameProvider.cs" />
+ <Compile Include="Folders\UserRootFolderNameProvider.cs" />
<Compile Include="Users\UserMetadataService.cs" />
- <Compile Include="VirtualItemImageValidator.cs" />
+ <Compile Include="Videos\VideoMetadataService.cs" />
+ <Compile Include="Years\YearMetadataService.cs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\MediaBrowser.Common\MediaBrowser.Common.csproj">
diff --git a/MediaBrowser.Providers/MediaInfo/AudioImageProvider.cs b/MediaBrowser.Providers/MediaInfo/AudioImageProvider.cs
index ad3211650..20ce952db 100644
--- a/MediaBrowser.Providers/MediaInfo/AudioImageProvider.cs
+++ b/MediaBrowser.Providers/MediaInfo/AudioImageProvider.cs
@@ -1,16 +1,13 @@
-using MediaBrowser.Common.Extensions;
-using MediaBrowser.Controller.Configuration;
+using MediaBrowser.Controller.Configuration;
+using MediaBrowser.Controller.Drawing;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.Audio;
-using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.MediaInfo;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Entities;
-using MediaBrowser.Model.Logging;
+using MediaBrowser.Model.IO;
using System;
-using System.Collections.Concurrent;
-using System.IO;
-using System.Linq;
+using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
@@ -19,207 +16,83 @@ namespace MediaBrowser.Providers.MediaInfo
/// <summary>
/// Uses ffmpeg to create video images
/// </summary>
- public class AudioImageProvider : BaseMetadataProvider
+ public class AudioImageProvider : IDynamicImageProvider, IHasChangeMonitor
{
- /// <summary>
- /// The _locks
- /// </summary>
- private readonly ConcurrentDictionary<string, SemaphoreSlim> _locks = new ConcurrentDictionary<string, SemaphoreSlim>();
-
- /// <summary>
- /// The _media encoder
- /// </summary>
+ private readonly IIsoManager _isoManager;
private readonly IMediaEncoder _mediaEncoder;
+ private readonly IServerConfigurationManager _config;
- /// <summary>
- /// Initializes a new instance of the <see cref="BaseMetadataProvider" /> class.
- /// </summary>
- /// <param name="logManager">The log manager.</param>
- /// <param name="configurationManager">The configuration manager.</param>
- /// <param name="mediaEncoder">The media encoder.</param>
- public AudioImageProvider(ILogManager logManager, IServerConfigurationManager configurationManager, IMediaEncoder mediaEncoder)
- : base(logManager, configurationManager)
+ public AudioImageProvider(IIsoManager isoManager, IMediaEncoder mediaEncoder, IServerConfigurationManager config)
{
+ _isoManager = isoManager;
_mediaEncoder = mediaEncoder;
+ _config = config;
}
/// <summary>
- /// Gets a value indicating whether [refresh on version change].
+ /// The null mount task result
/// </summary>
- /// <value><c>true</c> if [refresh on version change]; otherwise, <c>false</c>.</value>
- protected override bool RefreshOnVersionChange
- {
- get
- {
- return true;
- }
- }
+ protected readonly Task<IIsoMount> NullMountTaskResult = Task.FromResult<IIsoMount>(null);
/// <summary>
- /// Gets the provider version.
+ /// Mounts the iso if needed.
/// </summary>
- /// <value>The provider version.</value>
- protected override string ProviderVersion
+ /// <param name="item">The item.</param>
+ /// <param name="cancellationToken">The cancellation token.</param>
+ /// <returns>Task{IIsoMount}.</returns>
+ protected Task<IIsoMount> MountIsoIfNeeded(Video item, CancellationToken cancellationToken)
{
- get
+ if (item.VideoType == VideoType.Iso)
{
- return "1";
+ return _isoManager.Mount(item.Path, cancellationToken);
}
- }
- /// <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.LocationType == LocationType.FileSystem && item is Audio;
+ return NullMountTaskResult;
}
- /// <summary>
- /// Override this to return the date that should be compared to the last refresh date
- /// to determine if this provider should be re-fetched.
- /// </summary>
- /// <param name="item">The item.</param>
- /// <returns>DateTime.</returns>
- protected override DateTime CompareDate(BaseItem item)
+ public IEnumerable<ImageType> GetSupportedImages(IHasImages item)
{
- return item.DateModified;
- }
-
- /// <summary>
- /// Gets the priority.
- /// </summary>
- /// <value>The priority.</value>
- public override MetadataProviderPriority Priority
- {
- get { return MetadataProviderPriority.Last; }
- }
-
- public override ItemUpdateType ItemUpdateType
- {
- get
- {
- return ItemUpdateType.ImageUpdate;
- }
+ return new List<ImageType> { ImageType.Primary };
}
- /// <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)
+ public Task<DynamicImageResponse> GetImage(IHasImages item, ImageType type, CancellationToken cancellationToken)
{
- item.ValidateImages();
-
var audio = (Audio)item;
- if (string.IsNullOrEmpty(audio.PrimaryImagePath) && audio.HasEmbeddedImage)
+ // Can't extract if we didn't find a video stream in the file
+ if (!audio.HasEmbeddedImage)
{
- try
- {
- await CreateImagesForSong(audio, cancellationToken).ConfigureAwait(false);
- }
- catch (Exception ex)
- {
- Logger.ErrorException("Error extracting image for {0}", ex, item.Name);
- }
+ return Task.FromResult(new DynamicImageResponse { HasImage = false });
}
- SetLastRefreshed(item, DateTime.UtcNow, providerInfo);
- return true;
+ return GetVideoImage((Audio)item, cancellationToken);
}
- /// <summary>
- /// Creates the images for song.
- /// </summary>
- /// <param name="item">The item.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task.</returns>
- /// <exception cref="System.InvalidOperationException">Can't extract an image unless the audio file has an embedded image.</exception>
- private async Task CreateImagesForSong(Audio item, CancellationToken cancellationToken)
+ public async Task<DynamicImageResponse> GetVideoImage(Audio item, CancellationToken cancellationToken)
{
- cancellationToken.ThrowIfCancellationRequested();
+ var stream = await _mediaEncoder.ExtractImage(new[] { item.Path }, InputType.File, true, null, null, cancellationToken).ConfigureAwait(false);
- var path = GetAudioImagePath(item);
-
- if (!File.Exists(path))
+ return new DynamicImageResponse
{
- var semaphore = GetLock(path);
-
- // Acquire a lock
- await semaphore.WaitAsync(cancellationToken).ConfigureAwait(false);
-
- // Check again
- if (!File.Exists(path))
- {
- try
- {
- var parentPath = Path.GetDirectoryName(path);
-
- Directory.CreateDirectory(parentPath);
-
- await _mediaEncoder.ExtractImage(new[] { item.Path }, InputType.File, true, null, null, path, cancellationToken).ConfigureAwait(false);
- }
- finally
- {
- semaphore.Release();
- }
- }
- else
- {
- semaphore.Release();
- }
- }
-
- // Image is already in the cache
- item.SetImagePath(ImageType.Primary, path);
+ Format = ImageFormat.Jpg,
+ HasImage = true,
+ Stream = stream
+ };
}
- /// <summary>
- /// Gets the audio image path.
- /// </summary>
- /// <param name="item">The item.</param>
- /// <returns>System.String.</returns>
- private string GetAudioImagePath(Audio item)
+ public string Name
{
- var album = item.Parent as MusicAlbum;
-
- var filename = item.Album ?? string.Empty;
- filename += item.Artists.FirstOrDefault() ?? string.Empty;
- filename += album == null ? item.Id.ToString("N") + "_primary" + item.DateModified.Ticks : album.Id.ToString("N") + album.DateModified.Ticks + "_primary";
-
- filename = filename.GetMD5() + ".jpg";
-
- var prefix = filename.Substring(0, 1);
-
- return Path.Combine(AudioImagesPath, prefix, filename);
+ get { return "Embedded Image"; }
}
- /// <summary>
- /// Gets the audio images data path.
- /// </summary>
- /// <value>The audio images data path.</value>
- public string AudioImagesPath
+ public bool Supports(IHasImages item)
{
- get
- {
- return Path.Combine(ConfigurationManager.ApplicationPaths.DataPath, "extracted-audio-images");
- }
+ return item.LocationType == LocationType.FileSystem && item is Audio;
}
- /// <summary>
- /// Gets the lock.
- /// </summary>
- /// <param name="filename">The filename.</param>
- /// <returns>SemaphoreSlim.</returns>
- private SemaphoreSlim GetLock(string filename)
+ public bool HasChanged(IHasMetadata item, DateTime date)
{
- return _locks.GetOrAdd(filename, key => new SemaphoreSlim(1, 1));
+ return item.DateModified > date;
}
}
}
diff --git a/MediaBrowser.Providers/MediaInfo/BaseFFProbeProvider.cs b/MediaBrowser.Providers/MediaInfo/BaseFFProbeProvider.cs
deleted file mode 100644
index ad4630dcf..000000000
--- a/MediaBrowser.Providers/MediaInfo/BaseFFProbeProvider.cs
+++ /dev/null
@@ -1,145 +0,0 @@
-using MediaBrowser.Controller.Configuration;
-using MediaBrowser.Controller.Entities;
-using MediaBrowser.Controller.Entities.Audio;
-using MediaBrowser.Controller.MediaInfo;
-using MediaBrowser.Controller.Providers;
-using MediaBrowser.Model.Entities;
-using MediaBrowser.Model.IO;
-using MediaBrowser.Model.Logging;
-using MediaBrowser.Model.Serialization;
-using System;
-using System.Collections.Generic;
-using System.Globalization;
-using System.Linq;
-using System.Threading;
-using System.Threading.Tasks;
-
-namespace MediaBrowser.Providers.MediaInfo
-{
- /// <summary>
- /// Provides a base class for extracting media information through ffprobe
- /// </summary>
- /// <typeparam name="T"></typeparam>
- public abstract class BaseFFProbeProvider<T> : BaseMetadataProvider
- where T : BaseItem, IHasMediaStreams
- {
- protected BaseFFProbeProvider(ILogManager logManager, IServerConfigurationManager configurationManager, IMediaEncoder mediaEncoder, IJsonSerializer jsonSerializer)
- : base(logManager, configurationManager)
- {
- JsonSerializer = jsonSerializer;
- MediaEncoder = mediaEncoder;
- }
-
- protected readonly IMediaEncoder MediaEncoder;
- protected readonly IJsonSerializer JsonSerializer;
-
- /// <summary>
- /// Gets the priority.
- /// </summary>
- /// <value>The priority.</value>
- public override MetadataProviderPriority Priority
- {
- get { return MetadataProviderPriority.First; }
- }
-
- protected readonly CultureInfo UsCulture = new CultureInfo("en-US");
-
- /// <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.LocationType == LocationType.FileSystem && item is T;
- }
-
- /// <summary>
- /// Override this to return the date that should be compared to the last refresh date
- /// to determine if this provider should be re-fetched.
- /// </summary>
- /// <param name="item">The item.</param>
- /// <returns>DateTime.</returns>
- protected override DateTime CompareDate(BaseItem item)
- {
- return item.DateModified;
- }
-
- /// <summary>
- /// The null mount task result
- /// </summary>
- protected readonly Task<IIsoMount> NullMountTaskResult = Task.FromResult<IIsoMount>(null);
-
- /// <summary>
- /// Gets the provider version.
- /// </summary>
- /// <value>The provider version.</value>
- protected override string ProviderVersion
- {
- get
- {
- return "ffmpeg20131209";
- }
- }
-
- /// <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 media info.
- /// </summary>
- /// <param name="item">The item.</param>
- /// <param name="isoMount">The iso mount.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task{MediaInfoResult}.</returns>
- /// <exception cref="System.ArgumentNullException">inputPath
- /// or
- /// cache</exception>
- protected async Task<InternalMediaInfoResult> GetMediaInfo(BaseItem item, IIsoMount isoMount, CancellationToken cancellationToken)
- {
- cancellationToken.ThrowIfCancellationRequested();
-
- var type = InputType.File;
- var inputPath = isoMount == null ? new[] { item.Path } : new[] { isoMount.MountedPath };
-
- var video = item as Video;
-
- if (video != null)
- {
- inputPath = MediaEncoderHelpers.GetInputArgument(video.Path, video.LocationType == LocationType.Remote, video.VideoType, video.IsoType, isoMount, video.PlayableStreamFileNames, out type);
- }
-
- return await MediaEncoder.GetMediaInfo(inputPath, type, item is Audio, cancellationToken).ConfigureAwait(false);
- }
-
- /// <summary>
- /// Mounts the iso if needed.
- /// </summary>
- /// <param name="item">The item.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>IsoMount.</returns>
- protected virtual Task<IIsoMount> MountIsoIfNeeded(T item, CancellationToken cancellationToken)
- {
- return NullMountTaskResult;
- }
-
- /// <summary>
- /// Called when [pre fetch].
- /// </summary>
- /// <param name="item">The item.</param>
- /// <param name="mount">The mount.</param>
- protected virtual void OnPreFetch(T item, IIsoMount mount)
- {
-
- }
- }
-}
diff --git a/MediaBrowser.Providers/MediaInfo/FFProbeAudioInfoProvider.cs b/MediaBrowser.Providers/MediaInfo/FFProbeAudioInfo.cs
index bae719eea..4fc92ddeb 100644
--- a/MediaBrowser.Providers/MediaInfo/FFProbeAudioInfoProvider.cs
+++ b/MediaBrowser.Providers/MediaInfo/FFProbeAudioInfo.cs
@@ -1,41 +1,36 @@
using MediaBrowser.Common.Extensions;
-using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.Audio;
+using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.MediaInfo;
using MediaBrowser.Controller.Persistence;
-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.Linq;
using System.Threading;
using System.Threading.Tasks;
namespace MediaBrowser.Providers.MediaInfo
{
- /// <summary>
- /// Extracts audio information using ffprobe
- /// </summary>
- public class FFProbeAudioInfoProvider : BaseFFProbeProvider<Audio>
+ class FFProbeAudioInfo
{
+ private readonly IMediaEncoder _mediaEncoder;
private readonly IItemRepository _itemRepo;
- public FFProbeAudioInfoProvider(ILogManager logManager, IServerConfigurationManager configurationManager, IMediaEncoder mediaEncoder, IJsonSerializer jsonSerializer, IItemRepository itemRepo)
- : base(logManager, configurationManager, mediaEncoder, jsonSerializer)
+ private readonly CultureInfo _usCulture = new CultureInfo("en-US");
+
+ public FFProbeAudioInfo(IMediaEncoder mediaEncoder, IItemRepository itemRepo)
{
+ _mediaEncoder = mediaEncoder;
_itemRepo = itemRepo;
}
- public override async Task<bool> FetchAsync(BaseItem item, bool force, BaseProviderInfo providerInfo, CancellationToken cancellationToken)
+ public async Task<ItemUpdateType> Probe<T>(T item, CancellationToken cancellationToken)
+ where T : Audio
{
- var myItem = (Audio)item;
-
- OnPreFetch(myItem, null);
-
- var result = await GetMediaInfo(item, null, cancellationToken).ConfigureAwait(false);
+ var result = await GetMediaInfo(item, cancellationToken).ConfigureAwait(false);
cancellationToken.ThrowIfCancellationRequested();
@@ -43,11 +38,19 @@ namespace MediaBrowser.Providers.MediaInfo
cancellationToken.ThrowIfCancellationRequested();
- await Fetch(myItem, cancellationToken, result).ConfigureAwait(false);
+ await Fetch(item, cancellationToken, result).ConfigureAwait(false);
- SetLastRefreshed(item, DateTime.UtcNow, providerInfo);
+ return ItemUpdateType.MetadataImport;
+ }
- return true;
+ private async Task<InternalMediaInfoResult> GetMediaInfo(BaseItem item, CancellationToken cancellationToken)
+ {
+ cancellationToken.ThrowIfCancellationRequested();
+
+ const InputType type = InputType.File;
+ var inputPath = new[] { item.Path };
+
+ return await _mediaEncoder.GetMediaInfo(inputPath, type, false, cancellationToken).ConfigureAwait(false);
}
/// <summary>
@@ -82,7 +85,7 @@ namespace MediaBrowser.Providers.MediaInfo
// If we got something, parse it
if (!string.IsNullOrEmpty(duration))
{
- audio.RunTimeTicks = TimeSpan.FromSeconds(double.Parse(duration, UsCulture)).Ticks;
+ audio.RunTimeTicks = TimeSpan.FromSeconds(double.Parse(duration, _usCulture)).Ticks;
}
}
}
@@ -277,6 +280,6 @@ namespace MediaBrowser.Providers.MediaInfo
return null;
}
- }
+ }
}
diff --git a/MediaBrowser.Providers/MediaInfo/FFProbeProvider.cs b/MediaBrowser.Providers/MediaInfo/FFProbeProvider.cs
index 1f3723653..d55a42d11 100644
--- a/MediaBrowser.Providers/MediaInfo/FFProbeProvider.cs
+++ b/MediaBrowser.Providers/MediaInfo/FFProbeProvider.cs
@@ -1,4 +1,5 @@
using MediaBrowser.Controller.Entities;
+using MediaBrowser.Controller.Entities.Audio;
using MediaBrowser.Controller.Entities.Movies;
using MediaBrowser.Controller.Entities.TV;
using MediaBrowser.Controller.Library;
@@ -18,10 +19,14 @@ using System.Threading.Tasks;
namespace MediaBrowser.Providers.MediaInfo
{
public class FFProbeProvider : ICustomMetadataProvider<Episode>,
- ICustomMetadataProvider<MusicVideo>,
- ICustomMetadataProvider<Movie>,
- ICustomMetadataProvider<AdultVideo>,
- ICustomMetadataProvider<LiveTvVideoRecording>,
+ ICustomMetadataProvider<MusicVideo>,
+ ICustomMetadataProvider<Movie>,
+ ICustomMetadataProvider<AdultVideo>,
+ ICustomMetadataProvider<LiveTvVideoRecording>,
+ ICustomMetadataProvider<LiveTvAudioRecording>,
+ ICustomMetadataProvider<Trailer>,
+ ICustomMetadataProvider<Video>,
+ ICustomMetadataProvider<Audio>,
IHasChangeMonitor
{
private readonly ILogger _logger;
@@ -30,7 +35,7 @@ namespace MediaBrowser.Providers.MediaInfo
private readonly IItemRepository _itemRepo;
private readonly IBlurayExaminer _blurayExaminer;
private readonly ILocalizationManager _localization;
-
+
public string Name
{
get { return "ffprobe"; }
@@ -61,6 +66,26 @@ namespace MediaBrowser.Providers.MediaInfo
return FetchVideoInfo(item, cancellationToken);
}
+ public Task<ItemUpdateType> FetchAsync(Trailer item, CancellationToken cancellationToken)
+ {
+ return FetchVideoInfo(item, cancellationToken);
+ }
+
+ public Task<ItemUpdateType> FetchAsync(Video item, CancellationToken cancellationToken)
+ {
+ return FetchVideoInfo(item, cancellationToken);
+ }
+
+ public Task<ItemUpdateType> FetchAsync(Audio item, CancellationToken cancellationToken)
+ {
+ return FetchAudioInfo(item, cancellationToken);
+ }
+
+ public Task<ItemUpdateType> FetchAsync(LiveTvAudioRecording item, CancellationToken cancellationToken)
+ {
+ return FetchAudioInfo(item, cancellationToken);
+ }
+
public FFProbeProvider(ILogger logger, IIsoManager isoManager, IMediaEncoder mediaEncoder, IItemRepository itemRepo, IBlurayExaminer blurayExaminer, ILocalizationManager localization)
{
_logger = logger;
@@ -95,6 +120,19 @@ namespace MediaBrowser.Providers.MediaInfo
return prober.ProbeVideo(item, cancellationToken);
}
+ public Task<ItemUpdateType> FetchAudioInfo<T>(T item, CancellationToken cancellationToken)
+ where T : Audio
+ {
+ if (item.LocationType != LocationType.FileSystem)
+ {
+ return _cachedTask;
+ }
+
+ var prober = new FFProbeAudioInfo(_mediaEncoder, _itemRepo);
+
+ return prober.Probe(item, cancellationToken);
+ }
+
public bool HasChanged(IHasMetadata item, DateTime date)
{
return item.DateModified > date;
diff --git a/MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs b/MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs
index 8adb75839..9073441e4 100644
--- a/MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs
+++ b/MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs
@@ -321,69 +321,69 @@ namespace MediaBrowser.Providers.MediaInfo
/// <param name="currentStreams">The current streams.</param>
private void AddExternalSubtitles(Video video, List<MediaStream> currentStreams)
{
- var useParent = !video.ResolveArgs.IsDirectory;
-
- if (useParent && video.Parent == null)
- {
- return;
- }
-
- var fileSystemChildren = useParent
- ? video.Parent.ResolveArgs.FileSystemChildren
- : video.ResolveArgs.FileSystemChildren;
-
- var startIndex = currentStreams.Count;
- var streams = new List<MediaStream>();
-
- var videoFileNameWithoutExtension = Path.GetFileNameWithoutExtension(video.Path);
-
- foreach (var file in fileSystemChildren
- .Where(f => !f.Attributes.HasFlag(FileAttributes.Directory) && SubtitleExtensions.Contains(Path.GetExtension(f.FullName), StringComparer.OrdinalIgnoreCase)))
- {
- var fullName = file.FullName;
-
- var fileNameWithoutExtension = Path.GetFileNameWithoutExtension(fullName);
-
- // If the subtitle file matches the video file name
- if (string.Equals(videoFileNameWithoutExtension, fileNameWithoutExtension, StringComparison.OrdinalIgnoreCase))
- {
- streams.Add(new MediaStream
- {
- Index = startIndex++,
- Type = MediaStreamType.Subtitle,
- IsExternal = true,
- Path = fullName,
- Codec = Path.GetExtension(fullName).ToLower().TrimStart('.')
- });
- }
- else if (fileNameWithoutExtension.StartsWith(videoFileNameWithoutExtension + ".", StringComparison.OrdinalIgnoreCase))
- {
- // Support xbmc naming conventions - 300.spanish.srt
- var language = fileNameWithoutExtension.Split('.').LastOrDefault();
-
- // Try to translate to three character code
- // Be flexible and check against both the full and three character versions
- var culture = _localization.GetCultures()
- .FirstOrDefault(i => string.Equals(i.DisplayName, language, StringComparison.OrdinalIgnoreCase) || string.Equals(i.Name, language, StringComparison.OrdinalIgnoreCase) || string.Equals(i.ThreeLetterISOLanguageName, language, StringComparison.OrdinalIgnoreCase) || string.Equals(i.TwoLetterISOLanguageName, language, StringComparison.OrdinalIgnoreCase));
-
- if (culture != null)
- {
- language = culture.ThreeLetterISOLanguageName;
- }
-
- streams.Add(new MediaStream
- {
- Index = startIndex++,
- Type = MediaStreamType.Subtitle,
- IsExternal = true,
- Path = fullName,
- Codec = Path.GetExtension(fullName).ToLower().TrimStart('.'),
- Language = language
- });
- }
- }
-
- currentStreams.AddRange(streams);
+ //var useParent = !video.ResolveArgs.IsDirectory;
+
+ //if (useParent && video.Parent == null)
+ //{
+ // return;
+ //}
+
+ //var fileSystemChildren = useParent
+ // ? video.Parent.ResolveArgs.FileSystemChildren
+ // : video.ResolveArgs.FileSystemChildren;
+
+ //var startIndex = currentStreams.Count;
+ //var streams = new List<MediaStream>();
+
+ //var videoFileNameWithoutExtension = Path.GetFileNameWithoutExtension(video.Path);
+
+ //foreach (var file in fileSystemChildren
+ // .Where(f => !f.Attributes.HasFlag(FileAttributes.Directory) && SubtitleExtensions.Contains(Path.GetExtension(f.FullName), StringComparer.OrdinalIgnoreCase)))
+ //{
+ // var fullName = file.FullName;
+
+ // var fileNameWithoutExtension = Path.GetFileNameWithoutExtension(fullName);
+
+ // // If the subtitle file matches the video file name
+ // if (string.Equals(videoFileNameWithoutExtension, fileNameWithoutExtension, StringComparison.OrdinalIgnoreCase))
+ // {
+ // streams.Add(new MediaStream
+ // {
+ // Index = startIndex++,
+ // Type = MediaStreamType.Subtitle,
+ // IsExternal = true,
+ // Path = fullName,
+ // Codec = Path.GetExtension(fullName).ToLower().TrimStart('.')
+ // });
+ // }
+ // else if (fileNameWithoutExtension.StartsWith(videoFileNameWithoutExtension + ".", StringComparison.OrdinalIgnoreCase))
+ // {
+ // // Support xbmc naming conventions - 300.spanish.srt
+ // var language = fileNameWithoutExtension.Split('.').LastOrDefault();
+
+ // // Try to translate to three character code
+ // // Be flexible and check against both the full and three character versions
+ // var culture = _localization.GetCultures()
+ // .FirstOrDefault(i => string.Equals(i.DisplayName, language, StringComparison.OrdinalIgnoreCase) || string.Equals(i.Name, language, StringComparison.OrdinalIgnoreCase) || string.Equals(i.ThreeLetterISOLanguageName, language, StringComparison.OrdinalIgnoreCase) || string.Equals(i.TwoLetterISOLanguageName, language, StringComparison.OrdinalIgnoreCase));
+
+ // if (culture != null)
+ // {
+ // language = culture.ThreeLetterISOLanguageName;
+ // }
+
+ // streams.Add(new MediaStream
+ // {
+ // Index = startIndex++,
+ // Type = MediaStreamType.Subtitle,
+ // IsExternal = true,
+ // Path = fullName,
+ // Codec = Path.GetExtension(fullName).ToLower().TrimStart('.'),
+ // Language = language
+ // });
+ // }
+ //}
+
+ //currentStreams.AddRange(streams);
}
/// <summary>
diff --git a/MediaBrowser.Providers/MediaInfo/FFProbeVideoInfoProvider.cs b/MediaBrowser.Providers/MediaInfo/FFProbeVideoInfoProvider.cs
deleted file mode 100644
index 8d69e6ba2..000000000
--- a/MediaBrowser.Providers/MediaInfo/FFProbeVideoInfoProvider.cs
+++ /dev/null
@@ -1,686 +0,0 @@
-using DvdLib.Ifo;
-using MediaBrowser.Controller.Configuration;
-using MediaBrowser.Controller.Entities;
-using MediaBrowser.Controller.Localization;
-using MediaBrowser.Controller.MediaInfo;
-using MediaBrowser.Controller.Persistence;
-using MediaBrowser.Controller.Providers;
-using MediaBrowser.Model.Entities;
-using MediaBrowser.Model.IO;
-using MediaBrowser.Model.Logging;
-using MediaBrowser.Model.MediaInfo;
-using MediaBrowser.Model.Serialization;
-using System;
-using System.Collections.Generic;
-using System.Globalization;
-using System.IO;
-using System.Linq;
-using System.Threading;
-using System.Threading.Tasks;
-
-namespace MediaBrowser.Providers.MediaInfo
-{
- /// <summary>
- /// Extracts video information using ffprobe
- /// </summary>
- public class FFProbeVideoInfoProvider : BaseFFProbeProvider<Video>
- {
- private readonly IItemRepository _itemRepo;
-
- public FFProbeVideoInfoProvider(IIsoManager isoManager, IBlurayExaminer blurayExaminer, IJsonSerializer jsonSerializer, ILogManager logManager, IServerConfigurationManager configurationManager, IMediaEncoder mediaEncoder, ILocalizationManager localization, IItemRepository itemRepo)
- : base(logManager, configurationManager, mediaEncoder, jsonSerializer)
- {
- if (isoManager == null)
- {
- throw new ArgumentNullException("isoManager");
- }
- if (blurayExaminer == null)
- {
- throw new ArgumentNullException("blurayExaminer");
- }
-
- _blurayExaminer = blurayExaminer;
- _localization = localization;
- _itemRepo = itemRepo;
- _isoManager = isoManager;
- }
-
- /// <summary>
- /// Gets or sets the bluray examiner.
- /// </summary>
- /// <value>The bluray examiner.</value>
- private readonly IBlurayExaminer _blurayExaminer;
-
- /// <summary>
- /// The _iso manager
- /// </summary>
- private readonly IIsoManager _isoManager;
-
- private readonly ILocalizationManager _localization;
-
- /// <summary>
- /// Returns true or false indicating if the provider should refresh when the contents of it's directory changes
- /// </summary>
- /// <value><c>true</c> if [refresh on file system stamp change]; otherwise, <c>false</c>.</value>
- protected override bool RefreshOnFileSystemStampChange
- {
- get
- {
- // Need this in case external subtitle files change
- return true;
- }
- }
-
- /// <summary>
- /// Gets the filestamp extensions.
- /// </summary>
- /// <value>The filestamp extensions.</value>
- protected override string[] FilestampExtensions
- {
- get
- {
- return new[] { ".srt", ".ssa", ".ass" };
- }
- }
-
- public override MetadataProviderPriority Priority
- {
- get
- {
- return MetadataProviderPriority.Second;
- }
- }
-
- /// <summary>
- /// Supports video files and dvd structures
- /// </summary>
- /// <param name="item">The item.</param>
- /// <returns><c>true</c> if XXXX, <c>false</c> otherwise</returns>
- public override bool Supports(BaseItem item)
- {
- if (item.LocationType != LocationType.FileSystem)
- {
- return false;
- }
-
- var video = item as Video;
-
- if (video != null)
- {
- if (video.VideoType == VideoType.Iso)
- {
- return _isoManager.CanMount(item.Path);
- }
-
- return video.VideoType == VideoType.VideoFile || video.VideoType == VideoType.Dvd || video.VideoType == VideoType.BluRay;
- }
-
- return false;
- }
-
- /// <summary>
- /// Called when [pre fetch].
- /// </summary>
- /// <param name="item">The item.</param>
- /// <param name="mount">The mount.</param>
- protected override void OnPreFetch(Video item, IIsoMount mount)
- {
- if (item.VideoType == VideoType.Iso)
- {
- item.IsoType = DetermineIsoType(mount);
- }
-
- if (item.VideoType == VideoType.Dvd || (item.IsoType.HasValue && item.IsoType == IsoType.Dvd))
- {
- FetchFromDvdLib(item, mount);
- }
-
- base.OnPreFetch(item, mount);
- }
-
- private void FetchFromDvdLib(Video item, IIsoMount mount)
- {
- var path = mount == null ? item.Path : mount.MountedPath;
- var dvd = new Dvd(path);
-
- item.RunTimeTicks = dvd.Titles.Select(GetRuntime).Max();
-
- var primaryTitle = dvd.Titles.OrderByDescending(GetRuntime).FirstOrDefault();
-
- uint? titleNumber = null;
-
- if (primaryTitle != null)
- {
- titleNumber = primaryTitle.TitleNumber;
- }
-
- item.PlayableStreamFileNames = GetPrimaryPlaylistVobFiles(item, mount, titleNumber)
- .Select(Path.GetFileName)
- .ToList();
- }
-
- private long GetRuntime(Title title)
- {
- return title.ProgramChains
- .Select(i => (TimeSpan)i.PlaybackTime)
- .Select(i => i.Ticks)
- .Sum();
- }
-
- public override async Task<bool> FetchAsync(BaseItem item, bool force, BaseProviderInfo providerInfo, CancellationToken cancellationToken)
- {
- var video = (Video)item;
-
- var isoMount = await MountIsoIfNeeded(video, cancellationToken).ConfigureAwait(false);
-
- try
- {
- OnPreFetch(video, isoMount);
-
- // If we didn't find any satisfying the min length, just take them all
- if (video.VideoType == VideoType.Dvd || (video.IsoType.HasValue && video.IsoType == IsoType.Dvd))
- {
- if (video.PlayableStreamFileNames.Count == 0)
- {
- Logger.Error("No playable vobs found in dvd structure, skipping ffprobe.");
- SetLastRefreshed(item, DateTime.UtcNow, providerInfo);
- return true;
- }
- }
-
- var result = await GetMediaInfo(item, isoMount, cancellationToken).ConfigureAwait(false);
-
- cancellationToken.ThrowIfCancellationRequested();
-
- FFProbeHelpers.NormalizeFFProbeResult(result);
-
- cancellationToken.ThrowIfCancellationRequested();
-
- await Fetch(video, force, providerInfo, cancellationToken, result, isoMount).ConfigureAwait(false);
-
- }
- finally
- {
- if (isoMount != null)
- {
- isoMount.Dispose();
- }
- }
-
- SetLastRefreshed(item, DateTime.UtcNow, providerInfo);
- return true;
- }
-
- /// <summary>
- /// Mounts the iso if needed.
- /// </summary>
- /// <param name="item">The item.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>IsoMount.</returns>
- protected override Task<IIsoMount> MountIsoIfNeeded(Video item, CancellationToken cancellationToken)
- {
- if (item.VideoType == VideoType.Iso)
- {
- return _isoManager.Mount(item.Path, cancellationToken);
- }
-
- return base.MountIsoIfNeeded(item, cancellationToken);
- }
-
- /// <summary>
- /// Determines the type of the iso.
- /// </summary>
- /// <param name="isoMount">The iso mount.</param>
- /// <returns>System.Nullable{IsoType}.</returns>
- private IsoType? DetermineIsoType(IIsoMount isoMount)
- {
- var folders = Directory.EnumerateDirectories(isoMount.MountedPath).Select(Path.GetFileName).ToList();
-
- if (folders.Contains("video_ts", StringComparer.OrdinalIgnoreCase))
- {
- return IsoType.Dvd;
- }
- if (folders.Contains("bdmv", StringComparer.OrdinalIgnoreCase))
- {
- return IsoType.BluRay;
- }
-
- return null;
- }
-
- private IEnumerable<string> GetPrimaryPlaylistVobFiles(Video video, IIsoMount isoMount, uint? titleNumber)
- {
- // min size 300 mb
- const long minPlayableSize = 314572800;
-
- var root = isoMount != null ? isoMount.MountedPath : video.Path;
-
- // Try to eliminate menus and intros by skipping all files at the front of the list that are less than the minimum size
- // Once we reach a file that is at least the minimum, return all subsequent ones
- var allVobs = Directory.EnumerateFiles(root, "*", SearchOption.AllDirectories)
- .Where(file => string.Equals(Path.GetExtension(file), ".vob", StringComparison.OrdinalIgnoreCase))
- .ToList();
-
- // If we didn't find any satisfying the min length, just take them all
- if (allVobs.Count == 0)
- {
- Logger.Error("No vobs found in dvd structure.");
- return new List<string>();
- }
-
- if (titleNumber.HasValue)
- {
- var prefix = string.Format("VTS_0{0}_", titleNumber.Value.ToString(UsCulture));
- var vobs = allVobs.Where(i => Path.GetFileName(i).StartsWith(prefix, StringComparison.OrdinalIgnoreCase)).ToList();
-
- if (vobs.Count > 0)
- {
- return vobs;
- }
-
- Logger.Debug("Could not determine vob file list for {0} using DvdLib. Will scan using file sizes.", video.Path);
- }
-
- var files = allVobs
- .SkipWhile(f => new FileInfo(f).Length < minPlayableSize)
- .ToList();
-
- // If we didn't find any satisfying the min length, just take them all
- if (files.Count == 0)
- {
- Logger.Warn("Vob size filter resulted in zero matches. Taking all vobs.");
- files = allVobs;
- }
-
- // Assuming they're named "vts_05_01", take all files whose second part matches that of the first file
- if (files.Count > 0)
- {
- var parts = Path.GetFileNameWithoutExtension(files[0]).Split('_');
-
- if (parts.Length == 3)
- {
- var title = parts[1];
-
- files = files.TakeWhile(f =>
- {
- var fileParts = Path.GetFileNameWithoutExtension(f).Split('_');
-
- return fileParts.Length == 3 && string.Equals(title, fileParts[1], StringComparison.OrdinalIgnoreCase);
-
- }).ToList();
-
- // If this resulted in not getting any vobs, just take them all
- if (files.Count == 0)
- {
- Logger.Warn("Vob filename filter resulted in zero matches. Taking all vobs.");
- files = allVobs;
- }
- }
- }
-
- return files;
- }
-
- /// <summary>
- /// Fetches the specified video.
- /// </summary>
- /// <param name="video">The video.</param>
- /// <param name="force">if set to <c>true</c> [force].</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <param name="data">The data.</param>
- /// <param name="isoMount">The iso mount.</param>
- /// <returns>Task.</returns>
- protected async Task Fetch(Video video, bool force, BaseProviderInfo providerInfo, CancellationToken cancellationToken, InternalMediaInfoResult data, IIsoMount isoMount)
- {
- if (data.format != null)
- {
- // For dvd's this may not always be accurate, so don't set the runtime if the item already has one
- var needToSetRuntime = video.VideoType != VideoType.Dvd || video.RunTimeTicks == null || video.RunTimeTicks.Value == 0;
-
- if (needToSetRuntime && !string.IsNullOrEmpty(data.format.duration))
- {
- video.RunTimeTicks = TimeSpan.FromSeconds(double.Parse(data.format.duration, UsCulture)).Ticks;
- }
- }
-
- var mediaStreams = MediaEncoderHelpers.GetMediaInfo(data).MediaStreams;
-
- var chapters = data.Chapters ?? new List<ChapterInfo>();
-
- if (video.VideoType == VideoType.BluRay || (video.IsoType.HasValue && video.IsoType.Value == IsoType.BluRay))
- {
- var inputPath = isoMount != null ? isoMount.MountedPath : video.Path;
- FetchBdInfo(video, chapters, mediaStreams, inputPath, cancellationToken);
- }
-
- AddExternalSubtitles(video, mediaStreams);
-
- FetchWtvInfo(video, force, data);
-
- video.IsHD = mediaStreams.Any(i => i.Type == MediaStreamType.Video && i.Width.HasValue && i.Width.Value >= 1270);
-
- if (chapters.Count == 0 && mediaStreams.Any(i => i.Type == MediaStreamType.Video))
- {
- AddDummyChapters(video, chapters);
- }
-
- var videoStream = mediaStreams.FirstOrDefault(i => i.Type == MediaStreamType.Video);
-
- video.VideoBitRate = videoStream == null ? null : videoStream.BitRate;
- video.DefaultVideoStreamIndex = videoStream == null ? (int?)null : videoStream.Index;
-
- video.HasSubtitles = mediaStreams.Any(i => i.Type == MediaStreamType.Subtitle);
-
- await FFMpegManager.Instance.PopulateChapterImages(video, chapters, false, false, cancellationToken).ConfigureAwait(false);
-
- var videoFileChanged = CompareDate(video) > providerInfo.LastRefreshed;
-
- await _itemRepo.SaveMediaStreams(video.Id, mediaStreams, cancellationToken).ConfigureAwait(false);
-
- // Only save chapters if forcing, if the video changed, or if there are not already any saved ones
- if (force || videoFileChanged || _itemRepo.GetChapter(video.Id, 0) == null)
- {
- await _itemRepo.SaveChapters(video.Id, chapters, cancellationToken).ConfigureAwait(false);
- }
- }
-
- /// <summary>
- /// Fetches the WTV info.
- /// </summary>
- /// <param name="video">The video.</param>
- /// <param name="force">if set to <c>true</c> [force].</param>
- /// <param name="data">The data.</param>
- private void FetchWtvInfo(Video video, bool force, InternalMediaInfoResult data)
- {
- if (data.format == null || data.format.tags == null)
- {
- return;
- }
-
- if (force || video.Genres.Count == 0)
- {
- if (!video.LockedFields.Contains(MetadataFields.Genres))
- {
- var genres = FFProbeHelpers.GetDictionaryValue(data.format.tags, "genre");
-
- if (!string.IsNullOrEmpty(genres))
- {
- video.Genres = genres.Split(new[] { ';', '/', ',' }, StringSplitOptions.RemoveEmptyEntries)
- .Where(i => !string.IsNullOrWhiteSpace(i))
- .Select(i => i.Trim())
- .ToList();
- }
- }
- }
-
- if (force || string.IsNullOrEmpty(video.Overview))
- {
- if (!video.LockedFields.Contains(MetadataFields.Overview))
- {
- var overview = FFProbeHelpers.GetDictionaryValue(data.format.tags, "WM/SubTitleDescription");
-
- if (!string.IsNullOrWhiteSpace(overview))
- {
- video.Overview = overview;
- }
- }
- }
-
- if (force || string.IsNullOrEmpty(video.OfficialRating))
- {
- var officialRating = FFProbeHelpers.GetDictionaryValue(data.format.tags, "WM/ParentalRating");
-
- if (!string.IsNullOrWhiteSpace(officialRating))
- {
- if (!video.LockedFields.Contains(MetadataFields.OfficialRating))
- {
- video.OfficialRating = officialRating;
- }
- }
- }
-
- if (force || video.People.Count == 0)
- {
- if (!video.LockedFields.Contains(MetadataFields.Cast))
- {
- var people = FFProbeHelpers.GetDictionaryValue(data.format.tags, "WM/MediaCredits");
-
- if (!string.IsNullOrEmpty(people))
- {
- video.People = people.Split(new[] { ';', '/' }, StringSplitOptions.RemoveEmptyEntries)
- .Where(i => !string.IsNullOrWhiteSpace(i))
- .Select(i => new PersonInfo { Name = i.Trim(), Type = PersonType.Actor })
- .ToList();
- }
- }
- }
-
- if (force || !video.ProductionYear.HasValue)
- {
- var year = FFProbeHelpers.GetDictionaryValue(data.format.tags, "WM/OriginalReleaseTime");
-
- if (!string.IsNullOrWhiteSpace(year))
- {
- int val;
-
- if (int.TryParse(year, NumberStyles.Integer, UsCulture, out val))
- {
- video.ProductionYear = val;
- }
- }
- }
- }
-
- /// <summary>
- /// Adds the external subtitles.
- /// </summary>
- /// <param name="video">The video.</param>
- /// <param name="currentStreams">The current streams.</param>
- private void AddExternalSubtitles(Video video, List<MediaStream> currentStreams)
- {
- var useParent = !video.ResolveArgs.IsDirectory;
-
- if (useParent && video.Parent == null)
- {
- return;
- }
-
- var fileSystemChildren = useParent
- ? video.Parent.ResolveArgs.FileSystemChildren
- : video.ResolveArgs.FileSystemChildren;
-
- var startIndex = currentStreams.Count;
- var streams = new List<MediaStream>();
-
- var videoFileNameWithoutExtension = Path.GetFileNameWithoutExtension(video.Path);
-
- foreach (var file in fileSystemChildren
- .Where(f => !f.Attributes.HasFlag(FileAttributes.Directory) && FilestampExtensions.Contains(Path.GetExtension(f.FullName), StringComparer.OrdinalIgnoreCase)))
- {
- var fullName = file.FullName;
-
- var fileNameWithoutExtension = Path.GetFileNameWithoutExtension(fullName);
-
- // If the subtitle file matches the video file name
- if (string.Equals(videoFileNameWithoutExtension, fileNameWithoutExtension, StringComparison.OrdinalIgnoreCase))
- {
- streams.Add(new MediaStream
- {
- Index = startIndex++,
- Type = MediaStreamType.Subtitle,
- IsExternal = true,
- Path = fullName,
- Codec = Path.GetExtension(fullName).ToLower().TrimStart('.')
- });
- }
- else if (fileNameWithoutExtension.StartsWith(videoFileNameWithoutExtension + ".", StringComparison.OrdinalIgnoreCase))
- {
- // Support xbmc naming conventions - 300.spanish.srt
- var language = fileNameWithoutExtension.Split('.').LastOrDefault();
-
- // Try to translate to three character code
- // Be flexible and check against both the full and three character versions
- var culture = _localization.GetCultures()
- .FirstOrDefault(i => string.Equals(i.DisplayName, language, StringComparison.OrdinalIgnoreCase) || string.Equals(i.Name, language, StringComparison.OrdinalIgnoreCase) || string.Equals(i.ThreeLetterISOLanguageName, language, StringComparison.OrdinalIgnoreCase) || string.Equals(i.TwoLetterISOLanguageName, language, StringComparison.OrdinalIgnoreCase));
-
- if (culture != null)
- {
- language = culture.ThreeLetterISOLanguageName;
- }
-
- streams.Add(new MediaStream
- {
- Index = startIndex++,
- Type = MediaStreamType.Subtitle,
- IsExternal = true,
- Path = fullName,
- Codec = Path.GetExtension(fullName).ToLower().TrimStart('.'),
- Language = language
- });
- }
- }
-
- currentStreams.AddRange(streams);
- }
-
- /// <summary>
- /// The dummy chapter duration
- /// </summary>
- private readonly long _dummyChapterDuration = TimeSpan.FromMinutes(5).Ticks;
-
- /// <summary>
- /// Adds the dummy chapters.
- /// </summary>
- /// <param name="video">The video.</param>
- /// <param name="chapters">The chapters.</param>
- private void AddDummyChapters(Video video, List<ChapterInfo> chapters)
- {
- var runtime = video.RunTimeTicks ?? 0;
-
- if (runtime < 0)
- {
- throw new ArgumentException(string.Format("{0} has invalid runtime of {1}", video.Name, runtime));
- }
-
- if (runtime < _dummyChapterDuration)
- {
- return;
- }
-
- long currentChapterTicks = 0;
- var index = 1;
-
- // Limit to 100 chapters just in case there's some incorrect metadata here
- while (currentChapterTicks < runtime && index < 100)
- {
- chapters.Add(new ChapterInfo
- {
- Name = "Chapter " + index,
- StartPositionTicks = currentChapterTicks
- });
-
- index++;
- currentChapterTicks += _dummyChapterDuration;
- }
- }
-
- /// <summary>
- /// Fetches the bd info.
- /// </summary>
- /// <param name="item">The item.</param>
- /// <param name="chapters">The chapters.</param>
- /// <param name="mediaStreams">The media streams.</param>
- /// <param name="inputPath">The input path.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- private void FetchBdInfo(BaseItem item, List<ChapterInfo> chapters, List<MediaStream> mediaStreams, string inputPath, CancellationToken cancellationToken)
- {
- var video = (Video)item;
-
- var result = GetBDInfo(inputPath);
-
- cancellationToken.ThrowIfCancellationRequested();
-
- int? currentHeight = null;
- int? currentWidth = null;
- int? currentBitRate = null;
-
- var videoStream = mediaStreams.FirstOrDefault(s => s.Type == MediaStreamType.Video);
-
- // Grab the values that ffprobe recorded
- if (videoStream != null)
- {
- currentBitRate = videoStream.BitRate;
- currentWidth = videoStream.Width;
- currentHeight = videoStream.Height;
- }
-
- // Fill video properties from the BDInfo result
- Fetch(video, mediaStreams, result, chapters);
-
- videoStream = mediaStreams.FirstOrDefault(s => s.Type == MediaStreamType.Video);
-
- // Use the ffprobe values if these are empty
- if (videoStream != null)
- {
- videoStream.BitRate = IsEmpty(videoStream.BitRate) ? currentBitRate : videoStream.BitRate;
- videoStream.Width = IsEmpty(videoStream.Width) ? currentWidth : videoStream.Width;
- videoStream.Height = IsEmpty(videoStream.Height) ? currentHeight : videoStream.Height;
- }
- }
-
- /// <summary>
- /// Determines whether the specified num is empty.
- /// </summary>
- /// <param name="num">The num.</param>
- /// <returns><c>true</c> if the specified num is empty; otherwise, <c>false</c>.</returns>
- private bool IsEmpty(int? num)
- {
- return !num.HasValue || num.Value == 0;
- }
-
- /// <summary>
- /// Fills video properties from the VideoStream of the largest playlist
- /// </summary>
- /// <param name="video">The video.</param>
- /// <param name="mediaStreams">The media streams.</param>
- /// <param name="stream">The stream.</param>
- /// <param name="chapters">The chapters.</param>
- private void Fetch(Video video, List<MediaStream> mediaStreams, BlurayDiscInfo stream, List<ChapterInfo> chapters)
- {
- // Check all input for null/empty/zero
-
- mediaStreams.Clear();
- mediaStreams.AddRange(stream.MediaStreams);
-
- video.MainFeaturePlaylistName = stream.PlaylistName;
-
- if (stream.RunTimeTicks.HasValue && stream.RunTimeTicks.Value > 0)
- {
- video.RunTimeTicks = stream.RunTimeTicks;
- }
-
- video.PlayableStreamFileNames = stream.Files.ToList();
-
- if (stream.Chapters != null)
- {
- chapters.Clear();
-
- chapters.AddRange(stream.Chapters.Select(c => new ChapterInfo
- {
- StartPositionTicks = TimeSpan.FromSeconds(c).Ticks
-
- }));
- }
- }
-
- /// <summary>
- /// Gets information about the longest playlist on a bdrom
- /// </summary>
- /// <param name="path">The path.</param>
- /// <returns>VideoStream.</returns>
- private BlurayDiscInfo GetBDInfo(string path)
- {
- return _blurayExaminer.GetDiscInfo(path);
- }
- }
-}
diff --git a/MediaBrowser.Providers/MediaInfo/VideoImageProvider.cs b/MediaBrowser.Providers/MediaInfo/VideoImageProvider.cs
index 70b849e1c..31d44f4ec 100644
--- a/MediaBrowser.Providers/MediaInfo/VideoImageProvider.cs
+++ b/MediaBrowser.Providers/MediaInfo/VideoImageProvider.cs
@@ -12,7 +12,7 @@ using System.Threading.Tasks;
namespace MediaBrowser.Providers.MediaInfo
{
- public class VideoImageProvider : IDynamicImageProvider
+ public class VideoImageProvider : IDynamicImageProvider, IHasChangeMonitor
{
private readonly IIsoManager _isoManager;
private readonly IMediaEncoder _mediaEncoder;
@@ -26,34 +26,6 @@ namespace MediaBrowser.Providers.MediaInfo
}
/// <summary>
- /// Qualifieses for extraction.
- /// </summary>
- /// <param name="item">The item.</param>
- /// <returns><c>true</c> if XXXX, <c>false</c> otherwise</returns>
- private bool QualifiesForExtraction(Video item)
- {
- // No support for this
- if (item.VideoType == VideoType.HdDvd)
- {
- return false;
- }
-
- // Can't extract from iso's if we weren't unable to determine iso type
- if (item.VideoType == VideoType.Iso && !item.IsoType.HasValue)
- {
- return false;
- }
-
- // Can't extract if we didn't find a video stream in the file
- if (!item.DefaultVideoStreamIndex.HasValue)
- {
- return false;
- }
-
- return true;
- }
-
- /// <summary>
/// The null mount task result
/// </summary>
protected readonly Task<IIsoMount> NullMountTaskResult = Task.FromResult<IIsoMount>(null);
@@ -81,7 +53,27 @@ namespace MediaBrowser.Providers.MediaInfo
public Task<DynamicImageResponse> GetImage(IHasImages item, ImageType type, CancellationToken cancellationToken)
{
- return GetVideoImage((Video)item, cancellationToken);
+ var video = (Video)item;
+
+ // No support for this
+ if (video.VideoType == VideoType.HdDvd)
+ {
+ return Task.FromResult(new DynamicImageResponse { HasImage = false });
+ }
+
+ // Can't extract from iso's if we weren't unable to determine iso type
+ if (video.VideoType == VideoType.Iso && !video.IsoType.HasValue)
+ {
+ return Task.FromResult(new DynamicImageResponse { HasImage = false });
+ }
+
+ // Can't extract if we didn't find a video stream in the file
+ if (!video.DefaultVideoStreamIndex.HasValue)
+ {
+ return Task.FromResult(new DynamicImageResponse { HasImage = false });
+ }
+
+ return GetVideoImage(video, cancellationToken);
}
public async Task<DynamicImageResponse> GetVideoImage(Video item, CancellationToken cancellationToken)
@@ -133,5 +125,10 @@ namespace MediaBrowser.Providers.MediaInfo
return item.LocationType == LocationType.FileSystem && item is Video;
}
+
+ public bool HasChanged(IHasMetadata item, DateTime date)
+ {
+ return item.DateModified > date;
+ }
}
}
diff --git a/MediaBrowser.Providers/Movies/FanArtMovieProvider.cs b/MediaBrowser.Providers/Movies/FanArtMovieProvider.cs
deleted file mode 100644
index 9fc57c542..000000000
--- a/MediaBrowser.Providers/Movies/FanArtMovieProvider.cs
+++ /dev/null
@@ -1,356 +0,0 @@
-using MediaBrowser.Common.Configuration;
-using MediaBrowser.Common.IO;
-using MediaBrowser.Common.Net;
-using MediaBrowser.Controller.Configuration;
-using MediaBrowser.Controller.Entities;
-using MediaBrowser.Controller.Entities.Movies;
-using MediaBrowser.Controller.Library;
-using MediaBrowser.Controller.Providers;
-using MediaBrowser.Model.Configuration;
-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;
-using MediaBrowser.Model.Net;
-using System.Net;
-using MediaBrowser.Providers.Music;
-
-namespace MediaBrowser.Providers.Movies
-{
- /// <summary>
- /// Class FanArtMovieProvider
- /// </summary>
- class FanartMovieProvider : BaseMetadataProvider
- {
- /// <summary>
- /// Gets the HTTP client.
- /// </summary>
- /// <value>The HTTP client.</value>
- protected IHttpClient HttpClient { get; private set; }
-
- /// <summary>
- /// The _provider manager
- /// </summary>
- private readonly IProviderManager _providerManager;
-
- internal static FanartMovieProvider Current { get; private set; }
- private readonly IFileSystem _fileSystem;
-
- /// <summary>
- /// Initializes a new instance of the <see cref="FanartMovieProvider" /> class.
- /// </summary>
- /// <param name="httpClient">The HTTP client.</param>
- /// <param name="logManager">The log manager.</param>
- /// <param name="configurationManager">The configuration manager.</param>
- /// <param name="providerManager">The provider manager.</param>
- /// <exception cref="System.ArgumentNullException">httpClient</exception>
- public FanartMovieProvider(IHttpClient httpClient, ILogManager logManager, IServerConfigurationManager configurationManager, IProviderManager providerManager, IFileSystem fileSystem)
- : base(logManager, configurationManager)
- {
- if (httpClient == null)
- {
- throw new ArgumentNullException("httpClient");
- }
- HttpClient = httpClient;
- _providerManager = providerManager;
- _fileSystem = fileSystem;
- Current = this;
- }
-
- public override ItemUpdateType ItemUpdateType
- {
- get
- {
- return ItemUpdateType.ImageUpdate;
- }
- }
-
- /// <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 "13";
- }
- }
-
- public override MetadataProviderPriority Priority
- {
- get
- {
- return MetadataProviderPriority.Fifth;
- }
- }
-
- /// <summary>
- /// The fan art base URL
- /// </summary>
- protected string FanArtBaseUrl = "http://api.fanart.tv/webservice/movie/{0}/{1}/xml/all/1/1";
-
- /// <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)
- {
- var trailer = item as Trailer;
-
- if (trailer != null)
- {
- return !trailer.IsLocalTrailer;
- }
-
- return item is Movie || item is MusicVideo;
- }
-
- /// <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;
- }
-
- return base.NeedsRefreshInternal(item, providerInfo);
- }
-
- protected override bool NeedsRefreshBasedOnCompareDate(BaseItem item, BaseProviderInfo providerInfo)
- {
- var id = item.GetProviderId(MetadataProviders.Tmdb);
-
- if (!string.IsNullOrEmpty(id))
- {
- // Process images
- var xmlPath = GetFanartXmlPath(id);
-
- var fileInfo = new FileInfo(xmlPath);
-
- return !fileInfo.Exists || _fileSystem.GetLastWriteTimeUtc(fileInfo) > providerInfo.LastRefreshed;
- }
-
- return base.NeedsRefreshBasedOnCompareDate(item, providerInfo);
- }
-
- /// <summary>
- /// Gets the movie data path.
- /// </summary>
- /// <param name="appPaths">The app paths.</param>
- /// <param name="tmdbId">The TMDB id.</param>
- /// <returns>System.String.</returns>
- internal static string GetMovieDataPath(IApplicationPaths appPaths, string tmdbId)
- {
- var dataPath = Path.Combine(GetMoviesDataPath(appPaths), tmdbId);
-
- return dataPath;
- }
-
- /// <summary>
- /// Gets the movie data path.
- /// </summary>
- /// <param name="appPaths">The app paths.</param>
- /// <returns>System.String.</returns>
- internal static string GetMoviesDataPath(IApplicationPaths appPaths)
- {
- var dataPath = Path.Combine(appPaths.DataPath, "fanart-movies");
-
- return dataPath;
- }
-
- /// <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 movieId = item.GetProviderId(MetadataProviders.Tmdb);
-
- if (!string.IsNullOrEmpty(movieId))
- {
- var xmlPath = GetFanartXmlPath(movieId);
-
- // Only download the xml if it doesn't already exist. The prescan task will take care of getting updates
- if (!File.Exists(xmlPath))
- {
- await DownloadMovieXml(movieId, cancellationToken).ConfigureAwait(false);
- }
-
- var images = await _providerManager.GetAvailableRemoteImages(item, cancellationToken, ManualFanartMovieImageProvider.ProviderName).ConfigureAwait(false);
-
- await FetchImages(item, images.ToList(), cancellationToken).ConfigureAwait(false);
- }
-
- SetLastRefreshed(item, DateTime.UtcNow, providerInfo);
- return true;
- }
-
- public string GetFanartXmlPath(string tmdbId)
- {
- var movieDataPath = GetMovieDataPath(ConfigurationManager.ApplicationPaths, tmdbId);
- return Path.Combine(movieDataPath, "fanart.xml");
- }
-
- /// <summary>
- /// Downloads the movie XML.
- /// </summary>
- /// <param name="tmdbId">The TMDB id.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task.</returns>
- internal async Task DownloadMovieXml(string tmdbId, CancellationToken cancellationToken)
- {
- cancellationToken.ThrowIfCancellationRequested();
-
- var url = string.Format(FanArtBaseUrl, FanartArtistProvider.ApiKey, tmdbId);
-
- var xmlPath = GetFanartXmlPath(tmdbId);
-
- Directory.CreateDirectory(Path.GetDirectoryName(xmlPath));
-
- using (var response = await HttpClient.Get(new HttpRequestOptions
- {
- Url = url,
- ResourcePool = FanartArtistProvider.FanArtResourcePool,
- CancellationToken = cancellationToken
-
- }).ConfigureAwait(false))
- {
- using (var xmlFileStream = _fileSystem.GetFileStream(xmlPath, FileMode.Create, FileAccess.Write, FileShare.Read, true))
- {
- await response.CopyToAsync(xmlFileStream).ConfigureAwait(false);
- }
- }
- }
-
- private readonly Task _cachedTask = Task.FromResult(true);
- internal Task EnsureMovieXml(string tmdbId, CancellationToken cancellationToken)
- {
- var path = GetFanartXmlPath(tmdbId);
-
- var fileInfo = _fileSystem.GetFileSystemInfo(path);
-
- if (fileInfo.Exists)
- {
- if (ConfigurationManager.Configuration.EnableFanArtUpdates || (DateTime.UtcNow - _fileSystem.GetLastWriteTimeUtc(fileInfo)).TotalDays <= 7)
- {
- return _cachedTask;
- }
- }
-
- return DownloadMovieXml(tmdbId, cancellationToken);
- }
-
- private async Task FetchImages(BaseItem item, List<RemoteImageInfo> images, CancellationToken cancellationToken)
- {
- cancellationToken.ThrowIfCancellationRequested();
-
- var options = ConfigurationManager.Configuration.GetMetadataOptions("Movie") ?? new MetadataOptions();
-
- if (options.IsEnabled(ImageType.Primary) && !item.HasImage(ImageType.Primary))
- {
- await SaveImage(item, images, ImageType.Primary, cancellationToken).ConfigureAwait(false);
- }
-
- cancellationToken.ThrowIfCancellationRequested();
-
- if (options.IsEnabled(ImageType.Logo) && !item.HasImage(ImageType.Logo))
- {
- await SaveImage(item, images, ImageType.Logo, cancellationToken).ConfigureAwait(false);
- }
-
- cancellationToken.ThrowIfCancellationRequested();
-
- if (options.IsEnabled(ImageType.Art) && !item.HasImage(ImageType.Art))
- {
- await SaveImage(item, images, ImageType.Art, cancellationToken).ConfigureAwait(false);
- }
-
- cancellationToken.ThrowIfCancellationRequested();
-
- if (options.IsEnabled(ImageType.Disc) && !item.HasImage(ImageType.Disc))
- {
- await SaveImage(item, images, ImageType.Disc, cancellationToken).ConfigureAwait(false);
- }
-
- cancellationToken.ThrowIfCancellationRequested();
-
- if (options.IsEnabled(ImageType.Banner) && !item.HasImage(ImageType.Banner))
- {
- await SaveImage(item, images, ImageType.Banner, cancellationToken).ConfigureAwait(false);
- }
-
- cancellationToken.ThrowIfCancellationRequested();
-
- if (options.IsEnabled(ImageType.Thumb) && !item.HasImage(ImageType.Thumb))
- {
- await SaveImage(item, images, ImageType.Thumb, cancellationToken).ConfigureAwait(false);
- }
-
- cancellationToken.ThrowIfCancellationRequested();
-
- var backdropLimit = options.GetLimit(ImageType.Backdrop);
-
- if (backdropLimit > 0 &&
- item.BackdropImagePaths.Count < backdropLimit)
- {
- foreach (var image in images.Where(i => i.Type == ImageType.Backdrop))
- {
- await _providerManager.SaveImage(item, image.Url, FanartArtistProvider.FanArtResourcePool, ImageType.Backdrop, null, cancellationToken)
- .ConfigureAwait(false);
-
- if (item.BackdropImagePaths.Count >= backdropLimit) break;
- }
- }
- }
-
- private async Task SaveImage(BaseItem item, List<RemoteImageInfo> images, ImageType type, CancellationToken cancellationToken)
- {
- foreach (var image in images.Where(i => i.Type == type))
- {
- try
- {
- await _providerManager.SaveImage(item, image.Url, FanartArtistProvider.FanArtResourcePool, 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;
- }
- }
- }
- }
-}
diff --git a/MediaBrowser.Providers/Movies/FanArtMovieUpdatesPrescanTask.cs b/MediaBrowser.Providers/Movies/FanArtMovieUpdatesPrescanTask.cs
index 47948455b..600ba1925 100644
--- a/MediaBrowser.Providers/Movies/FanArtMovieUpdatesPrescanTask.cs
+++ b/MediaBrowser.Providers/Movies/FanArtMovieUpdatesPrescanTask.cs
@@ -60,7 +60,7 @@ namespace MediaBrowser.Providers.Movies
return;
}
- var path = FanartMovieProvider.GetMoviesDataPath(_config.CommonApplicationPaths);
+ var path = FanartMovieImageProvider.GetMoviesDataPath(_config.CommonApplicationPaths);
Directory.CreateDirectory(path);
@@ -136,7 +136,7 @@ namespace MediaBrowser.Providers.Movies
{
_logger.Info("Updating movie " + id);
- await FanartMovieProvider.Current.DownloadMovieXml(id, cancellationToken).ConfigureAwait(false);
+ await FanartMovieImageProvider.Current.DownloadMovieXml(id, cancellationToken).ConfigureAwait(false);
numComplete++;
double percent = numComplete;
diff --git a/MediaBrowser.Providers/Movies/ManualFanartMovieImageProvider.cs b/MediaBrowser.Providers/Movies/FanartMovieImageProvider.cs
index 70aaec526..e0ed0ec25 100644
--- a/MediaBrowser.Providers/Movies/ManualFanartMovieImageProvider.cs
+++ b/MediaBrowser.Providers/Movies/FanartMovieImageProvider.cs
@@ -1,4 +1,5 @@
-using MediaBrowser.Common.IO;
+using MediaBrowser.Common.Configuration;
+using MediaBrowser.Common.IO;
using MediaBrowser.Common.Net;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Entities;
@@ -7,6 +8,7 @@ using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Providers;
+using MediaBrowser.Providers.Music;
using System;
using System.Collections.Generic;
using System.Globalization;
@@ -16,22 +18,27 @@ using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Xml;
-using MediaBrowser.Providers.Music;
namespace MediaBrowser.Providers.Movies
{
- public class ManualFanartMovieImageProvider : IRemoteImageProvider, IHasChangeMonitor, IHasOrder
+ public class FanartMovieImageProvider : IRemoteImageProvider, IHasChangeMonitor, IHasOrder
{
private readonly CultureInfo _usCulture = new CultureInfo("en-US");
private readonly IServerConfigurationManager _config;
private readonly IHttpClient _httpClient;
private readonly IFileSystem _fileSystem;
- public ManualFanartMovieImageProvider(IServerConfigurationManager config, IHttpClient httpClient, IFileSystem fileSystem)
+ private const string FanArtBaseUrl = "http://api.fanart.tv/webservice/movie/{0}/{1}/xml/all/1/1";
+
+ internal static FanartMovieImageProvider Current;
+
+ public FanartMovieImageProvider(IServerConfigurationManager config, IHttpClient httpClient, IFileSystem fileSystem)
{
_config = config;
_httpClient = httpClient;
_fileSystem = fileSystem;
+
+ Current = this;
}
public string Name
@@ -86,9 +93,9 @@ namespace MediaBrowser.Providers.Movies
if (!string.IsNullOrEmpty(movieId))
{
- await FanartMovieProvider.Current.EnsureMovieXml(movieId, cancellationToken).ConfigureAwait(false);
+ await EnsureMovieXml(movieId, cancellationToken).ConfigureAwait(false);
- var xmlPath = FanartMovieProvider.Current.GetFanartXmlPath(movieId);
+ var xmlPath = GetFanartXmlPath(movieId);
try
{
@@ -344,7 +351,7 @@ namespace MediaBrowser.Providers.Movies
if (!string.IsNullOrEmpty(id))
{
// Process images
- var xmlPath = FanartMovieProvider.Current.GetFanartXmlPath(id);
+ var xmlPath = GetFanartXmlPath(id);
var fileInfo = new FileInfo(xmlPath);
@@ -353,5 +360,85 @@ namespace MediaBrowser.Providers.Movies
return false;
}
+
+ /// <summary>
+ /// Gets the movie data path.
+ /// </summary>
+ /// <param name="appPaths">The app paths.</param>
+ /// <param name="tmdbId">The TMDB id.</param>
+ /// <returns>System.String.</returns>
+ internal static string GetMovieDataPath(IApplicationPaths appPaths, string tmdbId)
+ {
+ var dataPath = Path.Combine(GetMoviesDataPath(appPaths), tmdbId);
+
+ return dataPath;
+ }
+
+ /// <summary>
+ /// Gets the movie data path.
+ /// </summary>
+ /// <param name="appPaths">The app paths.</param>
+ /// <returns>System.String.</returns>
+ internal static string GetMoviesDataPath(IApplicationPaths appPaths)
+ {
+ var dataPath = Path.Combine(appPaths.DataPath, "fanart-movies");
+
+ return dataPath;
+ }
+
+ public string GetFanartXmlPath(string tmdbId)
+ {
+ var movieDataPath = GetMovieDataPath(_config.ApplicationPaths, tmdbId);
+ return Path.Combine(movieDataPath, "fanart.xml");
+ }
+
+ /// <summary>
+ /// Downloads the movie XML.
+ /// </summary>
+ /// <param name="tmdbId">The TMDB id.</param>
+ /// <param name="cancellationToken">The cancellation token.</param>
+ /// <returns>Task.</returns>
+ internal async Task DownloadMovieXml(string tmdbId, CancellationToken cancellationToken)
+ {
+ cancellationToken.ThrowIfCancellationRequested();
+
+ var url = string.Format(FanArtBaseUrl, FanartArtistProvider.ApiKey, tmdbId);
+
+ var xmlPath = GetFanartXmlPath(tmdbId);
+
+ Directory.CreateDirectory(Path.GetDirectoryName(xmlPath));
+
+ using (var response = await _httpClient.Get(new HttpRequestOptions
+ {
+ Url = url,
+ ResourcePool = FanartArtistProvider.FanArtResourcePool,
+ CancellationToken = cancellationToken
+
+ }).ConfigureAwait(false))
+ {
+ using (var xmlFileStream = _fileSystem.GetFileStream(xmlPath, FileMode.Create, FileAccess.Write, FileShare.Read, true))
+ {
+ await response.CopyToAsync(xmlFileStream).ConfigureAwait(false);
+ }
+ }
+ }
+
+ private readonly Task _cachedTask = Task.FromResult(true);
+ internal Task EnsureMovieXml(string tmdbId, CancellationToken cancellationToken)
+ {
+ var path = GetFanartXmlPath(tmdbId);
+
+ var fileInfo = _fileSystem.GetFileSystemInfo(path);
+
+ if (fileInfo.Exists)
+ {
+ if (_config.Configuration.EnableFanArtUpdates || (DateTime.UtcNow - _fileSystem.GetLastWriteTimeUtc(fileInfo)).TotalDays <= 7)
+ {
+ return _cachedTask;
+ }
+ }
+
+ return DownloadMovieXml(tmdbId, cancellationToken);
+ }
}
}
diff --git a/MediaBrowser.Providers/Movies/GenericMovieDbInfo.cs b/MediaBrowser.Providers/Movies/GenericMovieDbInfo.cs
new file mode 100644
index 000000000..d06d12a49
--- /dev/null
+++ b/MediaBrowser.Providers/Movies/GenericMovieDbInfo.cs
@@ -0,0 +1,245 @@
+using MediaBrowser.Controller.Entities;
+using MediaBrowser.Controller.Entities.Movies;
+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.Linq;
+using System.Net;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace MediaBrowser.Providers.Movies
+{
+ public class GenericMovieDbInfo<T>
+ where T : Video, new()
+ {
+ private readonly ILogger _logger;
+ private readonly IJsonSerializer _jsonSerializer;
+
+ private readonly CultureInfo _usCulture = new CultureInfo("en-US");
+
+ public GenericMovieDbInfo(ILogger logger, IJsonSerializer jsonSerializer)
+ {
+ _logger = logger;
+ _jsonSerializer = jsonSerializer;
+ }
+
+ public async Task<MetadataResult<T>> GetMetadata(ItemId itemId, CancellationToken cancellationToken)
+ {
+ var result = new MetadataResult<T>();
+
+ var tmdbId = itemId.GetProviderId(MetadataProviders.Tmdb);
+ var imdbId = itemId.GetProviderId(MetadataProviders.Imdb);
+
+ // Don't search for music video id's because it is very easy to misidentify.
+ if (string.IsNullOrEmpty(tmdbId) && string.IsNullOrEmpty(imdbId) && typeof(T) != typeof(MusicVideo))
+ {
+ tmdbId = await new MovieDbSearch(_logger, _jsonSerializer)
+ .FindMovieId(itemId, cancellationToken).ConfigureAwait(false);
+ }
+
+ if (!string.IsNullOrEmpty(tmdbId) || !string.IsNullOrEmpty(imdbId))
+ {
+ cancellationToken.ThrowIfCancellationRequested();
+
+ result.Item = await FetchMovieData(tmdbId, imdbId, itemId.MetadataLanguage, itemId.MetadataCountryCode, cancellationToken).ConfigureAwait(false);
+
+ result.HasMetadata = result.Item != null;
+ }
+
+ return result;
+ }
+
+ /// <summary>
+ /// Fetches the movie data.
+ /// </summary>
+ /// <param name="tmdbId">The TMDB identifier.</param>
+ /// <param name="imdbId">The imdb identifier.</param>
+ /// <param name="language">The language.</param>
+ /// <param name="preferredCountryCode">The preferred country code.</param>
+ /// <param name="cancellationToken">The cancellation token.</param>
+ /// <returns>Task{`0}.</returns>
+ private async Task<T> FetchMovieData(string tmdbId, string imdbId, string language, string preferredCountryCode, CancellationToken cancellationToken)
+ {
+ string dataFilePath = null;
+ MovieDbProvider.CompleteMovieData movieInfo = null;
+
+ // Id could be ImdbId or TmdbId
+ if (string.IsNullOrEmpty(tmdbId))
+ {
+ movieInfo = await MovieDbProvider.Current.FetchMainResult(imdbId, language, cancellationToken).ConfigureAwait(false);
+ if (movieInfo == null) return null;
+
+ tmdbId = movieInfo.id.ToString(_usCulture);
+
+ dataFilePath = MovieDbProvider.Current.GetDataFilePath(tmdbId, language);
+ Directory.CreateDirectory(Path.GetDirectoryName(dataFilePath));
+ _jsonSerializer.SerializeToFile(movieInfo, dataFilePath);
+ }
+
+ await MovieDbProvider.Current.EnsureMovieInfo(tmdbId, language, cancellationToken).ConfigureAwait(false);
+
+ dataFilePath = dataFilePath ?? MovieDbProvider.Current.GetDataFilePath(tmdbId, language);
+ movieInfo = movieInfo ?? _jsonSerializer.DeserializeFromFile<MovieDbProvider.CompleteMovieData>(dataFilePath);
+
+ var item = new T();
+
+ ProcessMainInfo(item, preferredCountryCode, movieInfo);
+
+ return item;
+ }
+
+ /// <summary>
+ /// Processes the main info.
+ /// </summary>
+ /// <param name="movie">The movie.</param>
+ /// <param name="preferredCountryCode">The preferred country code.</param>
+ /// <param name="movieData">The movie data.</param>
+ private void ProcessMainInfo(T movie, string preferredCountryCode, MovieDbProvider.CompleteMovieData movieData)
+ {
+ movie.Name = movieData.title ?? movieData.original_title ?? movieData.name ?? movie.Name;
+
+ // 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.HomePageUrl = movieData.homepage;
+
+ var hasBudget = movie as IHasBudget;
+ if (hasBudget != null)
+ {
+ hasBudget.Budget = movieData.budget;
+ hasBudget.Revenue = movieData.revenue;
+ }
+
+ if (!string.IsNullOrEmpty(movieData.tagline))
+ {
+ var hasTagline = movie as IHasTaglines;
+ if (hasTagline != null)
+ {
+ hasTagline.Taglines.Clear();
+ hasTagline.AddTagline(movieData.tagline);
+ }
+ }
+
+ movie.SetProviderId(MetadataProviders.Tmdb, movieData.id.ToString(_usCulture));
+ movie.SetProviderId(MetadataProviders.Imdb, movieData.imdb_id);
+
+ if (movieData.belongs_to_collection != null)
+ {
+ movie.SetProviderId(MetadataProviders.TmdbCollection,
+ movieData.belongs_to_collection.id.ToString(CultureInfo.InvariantCulture));
+
+ var movieItem = movie as Movie;
+
+ if (movieItem != null)
+ {
+ movieItem.TmdbCollectionName = movieData.belongs_to_collection.name;
+ }
+ }
+ else
+ {
+ movie.SetProviderId(MetadataProviders.TmdbCollection, null); // clear out any old entry
+ }
+
+ float rating;
+ string voteAvg = movieData.vote_average.ToString(CultureInfo.InvariantCulture);
+
+ if (float.TryParse(voteAvg, NumberStyles.AllowDecimalPoint, CultureInfo.InvariantCulture, out rating))
+ {
+ movie.CommunityRating = rating;
+ }
+
+ movie.VoteCount = movieData.vote_count;
+
+ //release date and certification are retrieved based on configured country and we fall back on US if not there and to minimun release date if still no match
+ if (movieData.releases != null && movieData.releases.countries != null)
+ {
+ var ourRelease = movieData.releases.countries.FirstOrDefault(c => c.iso_3166_1.Equals(preferredCountryCode, StringComparison.OrdinalIgnoreCase)) ?? new MovieDbProvider.Country();
+ var usRelease = movieData.releases.countries.FirstOrDefault(c => c.iso_3166_1.Equals("US", StringComparison.OrdinalIgnoreCase)) ?? new MovieDbProvider.Country();
+ var minimunRelease = movieData.releases.countries.OrderBy(c => c.release_date).FirstOrDefault() ?? new MovieDbProvider.Country();
+
+ var ratingPrefix = string.Equals(preferredCountryCode, "us", StringComparison.OrdinalIgnoreCase) ? "" : preferredCountryCode + "-";
+ movie.OfficialRating = !string.IsNullOrEmpty(ourRelease.certification)
+ ? ratingPrefix + ourRelease.certification
+ : !string.IsNullOrEmpty(usRelease.certification)
+ ? usRelease.certification
+ : !string.IsNullOrEmpty(minimunRelease.certification)
+ ? minimunRelease.iso_3166_1 + "-" + minimunRelease.certification
+ : null;
+ }
+
+ if (movieData.release_date.Year != 1)
+ {
+ //no specific country release info at all
+ movie.PremiereDate = movieData.release_date.ToUniversalTime();
+ movie.ProductionYear = movieData.release_date.Year;
+ }
+
+ //studios
+ if (movieData.production_companies != null)
+ {
+ movie.Studios.Clear();
+
+ foreach (var studio in movieData.production_companies.Select(c => c.name))
+ {
+ movie.AddStudio(studio);
+ }
+ }
+
+ // genres
+ // Movies get this from imdb
+ var genres = movieData.genres ?? new List<MovieDbProvider.GenreItem>();
+
+ foreach (var genre in genres.Select(g => g.name))
+ {
+ movie.AddGenre(genre);
+ }
+
+ //Actors, Directors, Writers - all in People
+ //actors come from cast
+ if (movieData.casts != null && movieData.casts.cast != null)
+ {
+ foreach (var actor in movieData.casts.cast.OrderBy(a => a.order)) movie.AddPerson(new PersonInfo { Name = actor.name.Trim(), Role = actor.character, Type = PersonType.Actor, SortOrder = actor.order });
+ }
+
+ //and the rest from crew
+ if (movieData.casts != null && movieData.casts.crew != null)
+ {
+ foreach (var person in movieData.casts.crew) movie.AddPerson(new PersonInfo { Name = person.name.Trim(), Role = person.job, Type = person.department });
+ }
+
+ if (movieData.keywords != null && movieData.keywords.keywords != null)
+ {
+ var hasTags = movie as IHasKeywords;
+ if (hasTags != null)
+ {
+ hasTags.Keywords = movieData.keywords.keywords.Select(i => i.name).ToList();
+ }
+ }
+
+ if (movieData.trailers != null && movieData.trailers.youtube != null &&
+ movieData.trailers.youtube.Count > 0)
+ {
+ var hasTrailers = movie as IHasTrailers;
+ if (hasTrailers != null)
+ {
+ hasTrailers.RemoteTrailers = movieData.trailers.youtube.Select(i => new MediaUrl
+ {
+ Url = string.Format("http://www.youtube.com/watch?v={0}", i.source),
+ IsDirectLink = false,
+ Name = i.name,
+ VideoSize = string.Equals("hd", i.size, StringComparison.OrdinalIgnoreCase) ? VideoSize.HighDefinition : VideoSize.StandardDefinition
+
+ }).ToList();
+ }
+ }
+ }
+
+ }
+}
diff --git a/MediaBrowser.Providers/Movies/ManualMovieDbImageProvider.cs b/MediaBrowser.Providers/Movies/MovieDbImageProvider.cs
index bc4652e2c..be2f7ad61 100644
--- a/MediaBrowser.Providers/Movies/ManualMovieDbImageProvider.cs
+++ b/MediaBrowser.Providers/Movies/MovieDbImageProvider.cs
@@ -15,12 +15,12 @@ using System.Threading.Tasks;
namespace MediaBrowser.Providers.Movies
{
- class ManualMovieDbImageProvider : IRemoteImageProvider, IHasOrder
+ class MovieDbImageProvider : IRemoteImageProvider, IHasOrder
{
private readonly IJsonSerializer _jsonSerializer;
private readonly IHttpClient _httpClient;
- public ManualMovieDbImageProvider(IJsonSerializer jsonSerializer, IHttpClient httpClient)
+ public MovieDbImageProvider(IJsonSerializer jsonSerializer, IHttpClient httpClient)
{
_jsonSerializer = jsonSerializer;
_httpClient = httpClient;
@@ -168,9 +168,17 @@ namespace MediaBrowser.Providers.Movies
private async Task<MovieDbProvider.Images> FetchImages(BaseItem item, IJsonSerializer jsonSerializer,
CancellationToken cancellationToken)
{
- await MovieDbProvider.Current.EnsureMovieInfo(item, cancellationToken).ConfigureAwait(false);
+ var tmdbId = item.GetProviderId(MetadataProviders.Tmdb);
+ var language = item.GetPreferredMetadataLanguage();
+
+ if (string.IsNullOrEmpty(tmdbId))
+ {
+ return null;
+ }
+
+ await MovieDbProvider.Current.EnsureMovieInfo(tmdbId, language, cancellationToken).ConfigureAwait(false);
- var path = MovieDbProvider.Current.GetDataFilePath(item);
+ var path = MovieDbProvider.Current.GetDataFilePath(tmdbId, language);
if (!string.IsNullOrEmpty(path))
{
diff --git a/MediaBrowser.Providers/Movies/MovieDbImagesProvider.cs b/MediaBrowser.Providers/Movies/MovieDbImagesProvider.cs
deleted file mode 100644
index 9ef2e1bca..000000000
--- a/MediaBrowser.Providers/Movies/MovieDbImagesProvider.cs
+++ /dev/null
@@ -1,247 +0,0 @@
-using MediaBrowser.Common.IO;
-using MediaBrowser.Common.Net;
-using MediaBrowser.Controller.Configuration;
-using MediaBrowser.Controller.Entities;
-using MediaBrowser.Controller.Entities.Movies;
-using MediaBrowser.Controller.Library;
-using MediaBrowser.Controller.Providers;
-using MediaBrowser.Model.Configuration;
-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 MovieDbImagesProvider
- /// </summary>
- public class MovieDbImagesProvider : BaseMetadataProvider
- {
- /// <summary>
- /// The _provider manager
- /// </summary>
- private readonly IProviderManager _providerManager;
-
- private readonly IFileSystem _fileSystem;
-
- /// <summary>
- /// Initializes a new instance of the <see cref="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 MovieDbImagesProvider(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.Fourth; }
- }
-
- /// <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)
- {
- var trailer = item as Trailer;
-
- if (trailer != null)
- {
- return !trailer.IsLocalTrailer;
- }
-
- // Don't support local trailers
- return item is Movie || item is MusicVideo;
- }
-
- 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;
- }
-
- var options = ConfigurationManager.Configuration.GetMetadataOptions("Movie") ?? new MetadataOptions();
-
- // Don't refresh if we already have both poster and backdrop and we're not refreshing images
- if (item.HasImage(ImageType.Primary) &&
- item.BackdropImagePaths.Count >= options.GetLimit(ImageType.Backdrop) &&
- !item.LockedFields.Contains(MetadataFields.Images))
- {
- return false;
- }
-
- return base.NeedsRefreshInternal(item, providerInfo);
- }
-
- protected override bool NeedsRefreshBasedOnCompareDate(BaseItem item, BaseProviderInfo providerInfo)
- {
- var path = MovieDbProvider.Current.GetDataFilePath(item);
-
- if (!string.IsNullOrEmpty(path))
- {
- var fileInfo = new FileInfo(path);
-
- if (fileInfo.Exists)
- {
- return _fileSystem.GetLastWriteTimeUtc(fileInfo) > providerInfo.LastRefreshed;
- }
- }
-
- 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, ManualMovieDbImageProvider.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, cancellationToken)
- .ConfigureAwait(false);
- }
-
- cancellationToken.ThrowIfCancellationRequested();
-
- var options = ConfigurationManager.Configuration.GetMetadataOptions("Movie") ?? new MetadataOptions();
-
- var eligibleBackdrops = images
- .Where(i => i.Type == ImageType.Backdrop && i.Width.HasValue && i.Width.Value >= options.GetMinWidth(ImageType.Backdrop))
- .ToList();
-
- var backdropLimit = options.GetLimit(ImageType.Backdrop);
-
- // backdrops - only download if earlier providers didn't find any (fanart)
- if (eligibleBackdrops.Count > 0 &&
- options.IsEnabled(ImageType.Backdrop) &&
- item.BackdropImagePaths.Count < backdropLimit &&
- !item.LockedFields.Contains(MetadataFields.Backdrops))
- {
- for (var i = 0; i < eligibleBackdrops.Count; i++)
- {
- var url = eligibleBackdrops[i].Url;
-
- var img = await MovieDbProvider.Current.GetMovieDbResponse(new HttpRequestOptions
- {
- Url = url,
- CancellationToken = cancellationToken
-
- }).ConfigureAwait(false);
-
- await _providerManager.SaveImage(item, img, MimeTypes.GetMimeType(url), ImageType.Backdrop, null, cancellationToken)
- .ConfigureAwait(false);
-
- if (item.BackdropImagePaths.Count >= backdropLimit)
- {
- break;
- }
- }
- }
- }
- }
-}
diff --git a/MediaBrowser.Providers/Movies/MovieDbProvider.cs b/MediaBrowser.Providers/Movies/MovieDbProvider.cs
index 3adc682fc..47b92235b 100644
--- a/MediaBrowser.Providers/Movies/MovieDbProvider.cs
+++ b/MediaBrowser.Providers/Movies/MovieDbProvider.cs
@@ -5,16 +5,11 @@ using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.Movies;
using MediaBrowser.Controller.Providers;
-using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Logging;
using MediaBrowser.Model.Serialization;
-using MediaBrowser.Providers.Savers;
using System;
using System.Collections.Generic;
-using System.Globalization;
using System.IO;
-using System.Linq;
-using System.Net;
using System.Threading;
using System.Threading.Tasks;
@@ -23,114 +18,55 @@ namespace MediaBrowser.Providers.Movies
/// <summary>
/// Class MovieDbProvider
/// </summary>
- public class MovieDbProvider : BaseMetadataProvider, IDisposable
+ public class MovieDbProvider : IRemoteMetadataProvider<Movie>, IDisposable
{
- protected static CultureInfo EnUs = new CultureInfo("en-US");
-
- protected readonly IProviderManager ProviderManager;
-
- /// <summary>
- /// The movie db
- /// </summary>
internal readonly SemaphoreSlim MovieDbResourcePool = new SemaphoreSlim(1, 1);
internal static MovieDbProvider Current { get; private set; }
- /// <summary>
- /// Gets the json serializer.
- /// </summary>
- /// <value>The json serializer.</value>
- protected IJsonSerializer JsonSerializer { get; private set; }
-
- /// <summary>
- /// Gets the HTTP client.
- /// </summary>
- /// <value>The HTTP client.</value>
- protected IHttpClient HttpClient { get; private set; }
+ private readonly IJsonSerializer _jsonSerializer;
+ private readonly IHttpClient _httpClient;
private readonly IFileSystem _fileSystem;
+ private readonly IServerConfigurationManager _configurationManager;
+ private readonly ILogger _logger;
- /// <summary>
- /// Initializes a new instance of the <see cref="MovieDbProvider" /> class.
- /// </summary>
- /// <param name="logManager">The log manager.</param>
- /// <param name="configurationManager">The configuration manager.</param>
- /// <param name="jsonSerializer">The json serializer.</param>
- /// <param name="httpClient">The HTTP client.</param>
- /// <param name="providerManager">The provider manager.</param>
- public MovieDbProvider(ILogManager logManager, IServerConfigurationManager configurationManager, IJsonSerializer jsonSerializer, IHttpClient httpClient, IProviderManager providerManager, IFileSystem fileSystem)
- : base(logManager, configurationManager)
- {
- JsonSerializer = jsonSerializer;
- HttpClient = httpClient;
- ProviderManager = providerManager;
+ public MovieDbProvider(IJsonSerializer jsonSerializer, IHttpClient httpClient, IFileSystem fileSystem, IServerConfigurationManager configurationManager, ILogger logger)
+ {
+ _jsonSerializer = jsonSerializer;
+ _httpClient = httpClient;
_fileSystem = fileSystem;
+ _configurationManager = configurationManager;
+ _logger = logger;
Current = this;
}
- /// <summary>
- /// Releases unmanaged and - optionally - managed resources.
- /// </summary>
- /// <param name="dispose"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param>
- protected virtual void Dispose(bool dispose)
+ public Task<MetadataResult<Movie>> GetMetadata(ItemId id, CancellationToken cancellationToken)
{
- if (dispose)
- {
- MovieDbResourcePool.Dispose();
- }
+ return GetItemMetadata<Movie>(id, cancellationToken);
}
- /// <summary>
- /// Gets the priority.
- /// </summary>
- /// <value>The priority.</value>
- public override MetadataProviderPriority Priority
+ public Task<MetadataResult<T>> GetItemMetadata<T>(ItemId id, CancellationToken cancellationToken)
+ where T : Video, new ()
{
- get { return MetadataProviderPriority.Third; }
+ var movieDb = new GenericMovieDbInfo<T>(_logger, _jsonSerializer);
+
+ return movieDb.GetMetadata(id, cancellationToken);
}
- /// <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)
+ public string Name
{
- var trailer = item as Trailer;
-
- if (trailer != null)
- {
- return !trailer.IsLocalTrailer;
- }
-
- // Don't support local trailers
- return item is Movie || item is MusicVideo;
+ get { return "TheMovieDb"; }
}
/// <summary>
- /// Gets a value indicating whether [requires internet].
+ /// Releases unmanaged and - optionally - managed resources.
/// </summary>
- /// <value><c>true</c> if [requires internet]; otherwise, <c>false</c>.</value>
- public override bool RequiresInternet
- {
- get
- {
- return true;
- }
- }
-
- protected override bool RefreshOnVersionChange
- {
- get
- {
- return true;
- }
- }
-
- protected override string ProviderVersion
+ /// <param name="dispose"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param>
+ protected virtual void Dispose(bool dispose)
{
- get
+ if (dispose)
{
- return "3";
+ MovieDbResourcePool.Dispose();
}
}
@@ -170,7 +106,7 @@ namespace MediaBrowser.Providers.Movies
}).ConfigureAwait(false))
{
- _tmdbSettings = JsonSerializer.DeserializeFromStream<TmdbSettingsResult>(json);
+ _tmdbSettings = _jsonSerializer.DeserializeFromStream<TmdbSettingsResult>(json);
return _tmdbSettings;
}
@@ -187,30 +123,6 @@ namespace MediaBrowser.Providers.Movies
internal static string ApiKey = "f6bd687ffa63cd282b6ff2c6877f2669";
internal static string AcceptHeader = "application/json,image/*";
- protected override bool NeedsRefreshInternal(BaseItem item, BaseProviderInfo providerInfo)
- {
- if (string.IsNullOrEmpty(item.GetProviderId(MetadataProviders.Tmdb)))
- {
- return true;
- }
-
- return base.NeedsRefreshInternal(item, providerInfo);
- }
-
- protected override bool NeedsRefreshBasedOnCompareDate(BaseItem item, BaseProviderInfo providerInfo)
- {
- var path = GetDataFilePath(item);
-
- if (!string.IsNullOrEmpty(path))
- {
- var fileInfo = new FileInfo(path);
-
- return !fileInfo.Exists || _fileSystem.GetLastWriteTimeUtc(fileInfo) > providerInfo.LastRefreshed;
- }
-
- return base.NeedsRefreshBasedOnCompareDate(item, providerInfo);
- }
-
/// <summary>
/// Gets the movie data path.
/// </summary>
@@ -232,121 +144,6 @@ namespace MediaBrowser.Providers.Movies
}
/// <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 id = item.GetProviderId(MetadataProviders.Tmdb);
-
- if (string.IsNullOrEmpty(id))
- {
- id = item.GetProviderId(MetadataProviders.Imdb);
- }
-
- // Don't search for music video id's because it is very easy to misidentify.
- if (string.IsNullOrEmpty(id) && !(item is MusicVideo))
- {
- id = await new MovieDbSearch(Logger, JsonSerializer)
- .FindMovieId(GetId(item), cancellationToken).ConfigureAwait(false);
- }
-
- if (!string.IsNullOrEmpty(id))
- {
- cancellationToken.ThrowIfCancellationRequested();
-
- await FetchMovieData(item, id, force, cancellationToken).ConfigureAwait(false);
- }
-
- SetLastRefreshed(item, DateTime.UtcNow, providerInfo);
- return true;
- }
-
- private ItemId GetId(IHasMetadata item)
- {
- return new ItemId
- {
- MetadataCountryCode = item.GetPreferredMetadataCountryCode(),
- MetadataLanguage = item.GetPreferredMetadataLanguage(),
- Name = item.Name,
- ProviderIds = item.ProviderIds
- };
- }
-
- /// <summary>
- /// Determines whether [has alt meta] [the specified item].
- /// </summary>
- /// <param name="item">The item.</param>
- /// <returns><c>true</c> if [has alt meta] [the specified item]; otherwise, <c>false</c>.</returns>
- internal static bool HasAltMeta(BaseItem item)
- {
- var path = MovieXmlSaver.GetMovieSavePath((Video)item);
-
- if (item.LocationType == LocationType.FileSystem)
- {
- // If mixed with multiple movies in one folder, resolve args won't have the file system children
- return item.ResolveArgs.ContainsMetaFileByName(Path.GetFileName(path)) || File.Exists(path);
- }
-
- return false;
- }
-
- private readonly CultureInfo _usCulture = new CultureInfo("en-US");
-
- /// <summary>
- /// Fetches the movie data.
- /// </summary>
- /// <param name="item">The item.</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 FetchMovieData(BaseItem item, string id, bool isForcedRefresh, CancellationToken cancellationToken)
- {
- // Id could be ImdbId or TmdbId
-
- var language = item.GetPreferredMetadataLanguage();
-
- var dataFilePath = GetDataFilePath(item);
-
- var tmdbId = item.GetProviderId(MetadataProviders.Tmdb);
-
- if (string.IsNullOrEmpty(dataFilePath) || !File.Exists(dataFilePath))
- {
- var mainResult = await FetchMainResult(id, language, cancellationToken).ConfigureAwait(false);
-
- if (mainResult == null) return;
-
- tmdbId = mainResult.id.ToString(_usCulture);
-
- dataFilePath = GetDataFilePath(tmdbId, language);
-
- var directory = Path.GetDirectoryName(dataFilePath);
-
- Directory.CreateDirectory(directory);
-
- JsonSerializer.SerializeToFile(mainResult, dataFilePath);
- }
-
- if (isForcedRefresh || ConfigurationManager.Configuration.EnableTmdbUpdates || !HasAltMeta(item))
- {
- dataFilePath = GetDataFilePath(tmdbId, language);
-
- if (!string.IsNullOrEmpty(dataFilePath))
- {
- var mainResult = JsonSerializer.DeserializeFromFile<CompleteMovieData>(dataFilePath);
-
- ProcessMainInfo(item, mainResult);
- }
- }
- }
-
- /// <summary>
/// Downloads the movie info.
/// </summary>
/// <param name="id">The id.</param>
@@ -363,60 +160,49 @@ namespace MediaBrowser.Providers.Movies
Directory.CreateDirectory(Path.GetDirectoryName(dataFilePath));
- JsonSerializer.SerializeToFile(mainResult, dataFilePath);
+ _jsonSerializer.SerializeToFile(mainResult, dataFilePath);
}
private readonly Task _cachedTask = Task.FromResult(true);
- internal Task EnsureMovieInfo(BaseItem item, CancellationToken cancellationToken)
+ internal Task EnsureMovieInfo(string tmdbId, string language, CancellationToken cancellationToken)
{
- var path = GetDataFilePath(item);
-
- if (string.IsNullOrEmpty(path))
+ if (string.IsNullOrEmpty(tmdbId))
+ {
+ throw new ArgumentNullException("tmdbId");
+ }
+ if (string.IsNullOrEmpty(language))
{
- return _cachedTask;
+ throw new ArgumentNullException("language");
}
-
+
+ var path = GetDataFilePath(tmdbId, language);
+
var fileInfo = _fileSystem.GetFileSystemInfo(path);
if (fileInfo.Exists)
{
// If it's recent or automatic updates are enabled, don't re-download
- if ((ConfigurationManager.Configuration.EnableTmdbUpdates) || (DateTime.UtcNow - _fileSystem.GetLastWriteTimeUtc(fileInfo)).TotalDays <= 7)
+ if ((_configurationManager.Configuration.EnableTmdbUpdates) || (DateTime.UtcNow - _fileSystem.GetLastWriteTimeUtc(fileInfo)).TotalDays <= 7)
{
return _cachedTask;
}
}
- var id = item.GetProviderId(MetadataProviders.Tmdb);
-
- if (string.IsNullOrEmpty(id))
- {
- return _cachedTask;
- }
-
- return DownloadMovieInfo(id, item.GetPreferredMetadataLanguage(), cancellationToken);
+ return DownloadMovieInfo(tmdbId, language, cancellationToken);
}
- /// <summary>
- /// Gets the data file path.
- /// </summary>
- /// <param name="item">The item.</param>
- /// <returns>System.String.</returns>
- internal string GetDataFilePath(BaseItem item)
+ internal string GetDataFilePath(string tmdbId, string preferredLanguage)
{
- var id = item.GetProviderId(MetadataProviders.Tmdb);
-
- if (string.IsNullOrEmpty(id))
+ if (string.IsNullOrEmpty(tmdbId))
{
- return null;
+ throw new ArgumentNullException("tmdbId");
+ }
+ if (string.IsNullOrEmpty(preferredLanguage))
+ {
+ throw new ArgumentNullException("preferredLanguage");
}
- return GetDataFilePath(id, item.GetPreferredMetadataLanguage());
- }
-
- private string GetDataFilePath(string tmdbId, string preferredLanguage)
- {
- var path = GetMovieDataPath(ConfigurationManager.ApplicationPaths, tmdbId);
+ var path = GetMovieDataPath(_configurationManager.ApplicationPaths, tmdbId);
var filename = string.Format("all-{0}.json",
preferredLanguage ?? string.Empty);
@@ -431,7 +217,7 @@ namespace MediaBrowser.Providers.Movies
/// <param name="language">The language.</param>
/// <param name="cancellationToken">The cancellation token</param>
/// <returns>Task{CompleteMovieData}.</returns>
- private async Task<CompleteMovieData> FetchMainResult(string id, string language, CancellationToken cancellationToken)
+ internal async Task<CompleteMovieData> FetchMainResult(string id, string language, CancellationToken cancellationToken)
{
var url = string.Format(GetMovieInfo3, id, ApiKey);
@@ -461,7 +247,7 @@ namespace MediaBrowser.Providers.Movies
}).ConfigureAwait(false))
{
- mainResult = JsonSerializer.DeserializeFromStream<CompleteMovieData>(json);
+ mainResult = _jsonSerializer.DeserializeFromStream<CompleteMovieData>(json);
}
cancellationToken.ThrowIfCancellationRequested();
@@ -470,7 +256,7 @@ namespace MediaBrowser.Providers.Movies
{
if (!string.IsNullOrEmpty(language) && !string.Equals(language, "en", StringComparison.OrdinalIgnoreCase))
{
- Logger.Info("MovieDbProvider couldn't find meta for language " + language + ". Trying English...");
+ _logger.Info("MovieDbProvider couldn't find meta for language " + language + ". Trying English...");
url = string.Format(GetMovieInfo3, id, ApiKey) + "&include_image_language=en,null&language=en";
@@ -482,12 +268,12 @@ namespace MediaBrowser.Providers.Movies
}).ConfigureAwait(false))
{
- mainResult = JsonSerializer.DeserializeFromStream<CompleteMovieData>(json);
+ mainResult = _jsonSerializer.DeserializeFromStream<CompleteMovieData>(json);
}
if (String.IsNullOrEmpty(mainResult.overview))
{
- Logger.Error("MovieDbProvider - Unable to find information for (id:" + id + ")");
+ _logger.Error("MovieDbProvider - Unable to find information for (id:" + id + ")");
return null;
}
}
@@ -495,183 +281,6 @@ namespace MediaBrowser.Providers.Movies
return mainResult;
}
- /// <summary>
- /// Processes the main info.
- /// </summary>
- /// <param name="movie">The movie.</param>
- /// <param name="movieData">The movie data.</param>
- private void ProcessMainInfo(BaseItem movie, CompleteMovieData movieData)
- {
- if (!movie.LockedFields.Contains(MetadataFields.Name))
- {
- movie.Name = movieData.title ?? movieData.original_title ?? movieData.name ?? movie.Name;
- }
- if (!movie.LockedFields.Contains(MetadataFields.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.HomePageUrl = movieData.homepage;
-
- var hasBudget = movie as IHasBudget;
- if (hasBudget != null)
- {
- hasBudget.Budget = movieData.budget;
- hasBudget.Revenue = movieData.revenue;
- }
-
- if (!string.IsNullOrEmpty(movieData.tagline))
- {
- var hasTagline = movie as IHasTaglines;
- if (hasTagline != null)
- {
- hasTagline.Taglines.Clear();
- hasTagline.AddTagline(movieData.tagline);
- }
- }
-
- movie.SetProviderId(MetadataProviders.Tmdb, movieData.id.ToString(_usCulture));
- movie.SetProviderId(MetadataProviders.Imdb, movieData.imdb_id);
-
- if (movieData.belongs_to_collection != null)
- {
- movie.SetProviderId(MetadataProviders.TmdbCollection,
- movieData.belongs_to_collection.id.ToString(CultureInfo.InvariantCulture));
-
- var movieItem = movie as Movie;
-
- if (movieItem != null)
- {
- movieItem.TmdbCollectionName = movieData.belongs_to_collection.name;
- }
- }
- else
- {
- movie.SetProviderId(MetadataProviders.TmdbCollection, null); // clear out any old entry
- }
-
- float rating;
- string voteAvg = movieData.vote_average.ToString(CultureInfo.InvariantCulture);
-
- // tmdb appears to have unified their numbers to always report "7.3" regardless of country
- // so I removed the culture-specific processing here because it was not working for other countries -ebr
- // Movies get this from imdb
- if (!(movie is Movie) && float.TryParse(voteAvg, NumberStyles.AllowDecimalPoint, CultureInfo.InvariantCulture, out rating))
- {
- movie.CommunityRating = rating;
- }
-
- // Movies get this from imdb
- if (!(movie is Movie))
- {
- movie.VoteCount = movieData.vote_count;
- }
-
- var preferredCountryCode = movie.GetPreferredMetadataCountryCode();
-
- //release date and certification are retrieved based on configured country and we fall back on US if not there and to minimun release date if still no match
- if (movieData.releases != null && movieData.releases.countries != null)
- {
- var ourRelease = movieData.releases.countries.FirstOrDefault(c => c.iso_3166_1.Equals(preferredCountryCode, StringComparison.OrdinalIgnoreCase)) ?? new Country();
- var usRelease = movieData.releases.countries.FirstOrDefault(c => c.iso_3166_1.Equals("US", StringComparison.OrdinalIgnoreCase)) ?? new Country();
- var minimunRelease = movieData.releases.countries.OrderBy(c => c.release_date).FirstOrDefault() ?? new Country();
-
- if (!movie.LockedFields.Contains(MetadataFields.OfficialRating))
- {
- var ratingPrefix = string.Equals(preferredCountryCode, "us", StringComparison.OrdinalIgnoreCase) ? "" : preferredCountryCode + "-";
- movie.OfficialRating = !string.IsNullOrEmpty(ourRelease.certification)
- ? ratingPrefix + ourRelease.certification
- : !string.IsNullOrEmpty(usRelease.certification)
- ? usRelease.certification
- : !string.IsNullOrEmpty(minimunRelease.certification)
- ? minimunRelease.iso_3166_1 + "-" + minimunRelease.certification
- : null;
- }
- }
-
- if (movieData.release_date.Year != 1)
- {
- //no specific country release info at all
- movie.PremiereDate = movieData.release_date.ToUniversalTime();
- movie.ProductionYear = movieData.release_date.Year;
- }
-
- //studios
- if (movieData.production_companies != null && !movie.LockedFields.Contains(MetadataFields.Studios))
- {
- movie.Studios.Clear();
-
- foreach (var studio in movieData.production_companies.Select(c => c.name))
- {
- movie.AddStudio(studio);
- }
- }
-
- // genres
- // Movies get this from imdb
- var genres = movieData.genres ?? new List<GenreItem>();
- if (!movie.LockedFields.Contains(MetadataFields.Genres))
- {
- // Only grab them if a boxset or there are no genres.
- // For movies and trailers we'll use imdb via omdb
- // But omdb data is for english users only so fetch if language is not english
- if (!(movie is Movie) || movie.Genres.Count == 0 || !string.Equals(movie.GetPreferredMetadataLanguage(), "en", StringComparison.OrdinalIgnoreCase))
- {
- movie.Genres.Clear();
-
- foreach (var genre in genres.Select(g => g.name))
- {
- movie.AddGenre(genre);
- }
- }
- }
-
- if (!movie.LockedFields.Contains(MetadataFields.Cast))
- {
- movie.People.Clear();
-
- //Actors, Directors, Writers - all in People
- //actors come from cast
- if (movieData.casts != null && movieData.casts.cast != null)
- {
- foreach (var actor in movieData.casts.cast.OrderBy(a => a.order)) movie.AddPerson(new PersonInfo { Name = actor.name.Trim(), Role = actor.character, Type = PersonType.Actor, SortOrder = actor.order });
- }
-
- //and the rest from crew
- if (movieData.casts != null && movieData.casts.crew != null)
- {
- foreach (var person in movieData.casts.crew) movie.AddPerson(new PersonInfo { Name = person.name.Trim(), Role = person.job, Type = person.department });
- }
- }
-
- if (movieData.keywords != null && movieData.keywords.keywords != null && !movie.LockedFields.Contains(MetadataFields.Keywords))
- {
- var hasTags = movie as IHasKeywords;
- if (hasTags != null)
- {
- hasTags.Keywords = movieData.keywords.keywords.Select(i => i.name).ToList();
- }
- }
-
- if (movieData.trailers != null && movieData.trailers.youtube != null &&
- movieData.trailers.youtube.Count > 0)
- {
- var hasTrailers = movie as IHasTrailers;
- if (hasTrailers != null)
- {
- hasTrailers.RemoteTrailers = movieData.trailers.youtube.Select(i => new MediaUrl
- {
- Url = string.Format("http://www.youtube.com/watch?v={0}", i.source),
- IsDirectLink = false,
- Name = i.name,
- VideoSize = string.Equals("hd", i.size, StringComparison.OrdinalIgnoreCase) ? VideoSize.HighDefinition : VideoSize.StandardDefinition
-
- }).ToList();
- }
- }
- }
-
private DateTime _lastRequestDate = DateTime.MinValue;
/// <summary>
@@ -695,7 +304,7 @@ namespace MediaBrowser.Providers.Movies
_lastRequestDate = DateTime.Now;
- return await HttpClient.Get(options).ConfigureAwait(false);
+ return await _httpClient.Get(options).ConfigureAwait(false);
}
finally
{
@@ -897,18 +506,5 @@ namespace MediaBrowser.Providers.Movies
public Keywords keywords { get; set; }
public Trailers trailers { get; set; }
}
-
- internal class TmdbImageSettings
- {
- public List<string> backdrop_sizes { get; set; }
- public string base_url { get; set; }
- public List<string> poster_sizes { get; set; }
- public List<string> profile_sizes { get; set; }
- }
-
- internal class TmdbSettingsResult
- {
- public TmdbImageSettings images { get; set; }
- }
}
}
diff --git a/MediaBrowser.Providers/Movies/MovieMetadataService.cs b/MediaBrowser.Providers/Movies/MovieMetadataService.cs
new file mode 100644
index 000000000..cc513b395
--- /dev/null
+++ b/MediaBrowser.Providers/Movies/MovieMetadataService.cs
@@ -0,0 +1,43 @@
+using MediaBrowser.Common.IO;
+using MediaBrowser.Controller.Configuration;
+using MediaBrowser.Controller.Entities.Movies;
+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.Movies
+{
+ public class MovieMetadataService : MetadataService<Movie, ItemId>
+ {
+ private readonly ILibraryManager _libraryManager;
+
+ public MovieMetadataService(IServerConfigurationManager serverConfigurationManager, ILogger logger, IProviderManager providerManager, IProviderRepository providerRepo, IFileSystem fileSystem, ILibraryManager libraryManager)
+ : base(serverConfigurationManager, logger, providerManager, providerRepo, fileSystem)
+ {
+ _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(Movie source, Movie target, List<MetadataFields> lockedFields, bool replaceData, bool mergeMetadataSettings)
+ {
+ ProviderUtils.MergeBaseItemData(source, target, lockedFields, replaceData, mergeMetadataSettings);
+ }
+
+ protected override Task SaveItem(Movie item, ItemUpdateType reason, CancellationToken cancellationToken)
+ {
+ return _libraryManager.UpdateItem(item, reason, cancellationToken);
+ }
+ }
+}
diff --git a/MediaBrowser.Providers/Movies/MovieProviderFromXml.cs b/MediaBrowser.Providers/Movies/MovieProviderFromXml.cs
deleted file mode 100644
index 7ae6dffc7..000000000
--- a/MediaBrowser.Providers/Movies/MovieProviderFromXml.cs
+++ /dev/null
@@ -1,112 +0,0 @@
-using MediaBrowser.Common.IO;
-using MediaBrowser.Controller.Configuration;
-using MediaBrowser.Controller.Entities;
-using MediaBrowser.Controller.Entities.TV;
-using MediaBrowser.Controller.Persistence;
-using MediaBrowser.Controller.Providers;
-using MediaBrowser.Model.Entities;
-using MediaBrowser.Model.Logging;
-using MediaBrowser.Providers.Savers;
-using System;
-using System.IO;
-using System.Threading;
-using System.Threading.Tasks;
-
-namespace MediaBrowser.Providers.Movies
-{
- /// <summary>
- /// Class MovieProviderFromXml
- /// </summary>
- public class MovieProviderFromXml : BaseMetadataProvider
- {
- private readonly IItemRepository _itemRepo;
- private readonly IFileSystem _fileSystem;
-
- public MovieProviderFromXml(ILogManager logManager, IServerConfigurationManager configurationManager, IItemRepository itemRepo, IFileSystem fileSystem)
- : base(logManager, configurationManager)
- {
- _itemRepo = itemRepo;
- _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)
- {
- if (item.LocationType != LocationType.FileSystem)
- {
- return false;
- }
-
- var trailer = item as Trailer;
-
- if (trailer != null)
- {
- return !trailer.IsLocalTrailer;
- }
-
- // Check parent for null to avoid running this against things like video backdrops
- return item is Video && !(item is Episode) && item.Parent != null;
- }
-
- /// <summary>
- /// Gets the priority.
- /// </summary>
- /// <value>The priority.</value>
- public override MetadataProviderPriority Priority
- {
- get { return MetadataProviderPriority.First; }
- }
-
- protected override bool NeedsRefreshBasedOnCompareDate(BaseItem item, BaseProviderInfo providerInfo)
- {
- var savePath = MovieXmlSaver.GetMovieSavePath((Video)item);
-
- var xml = item.ResolveArgs.GetMetaFileByPath(savePath) ?? new FileInfo(savePath);
-
- if (!xml.Exists)
- {
- 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 video = (Video)item;
-
- var path = MovieXmlSaver.GetMovieSavePath(video);
-
- if (File.Exists(path))
- {
- await XmlParsingResourcePool.WaitAsync(cancellationToken).ConfigureAwait(false);
-
- try
- {
- new MovieXmlParser(Logger).FetchAsync(video, path, cancellationToken);
- }
- finally
- {
- XmlParsingResourcePool.Release();
- }
- }
-
- SetLastRefreshed(item, DateTime.UtcNow, providerInfo);
-
- return true;
- }
- }
-}
diff --git a/MediaBrowser.Providers/Movies/MovieXmlProvider.cs b/MediaBrowser.Providers/Movies/MovieXmlProvider.cs
index 82250f2b5..8eabc0a2d 100644
--- a/MediaBrowser.Providers/Movies/MovieXmlProvider.cs
+++ b/MediaBrowser.Providers/Movies/MovieXmlProvider.cs
@@ -4,11 +4,10 @@ using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Logging;
using System.IO;
using System.Threading;
-using System.Threading.Tasks;
namespace MediaBrowser.Providers.Movies
{
- public class MovieXmlProvider : BaseXmlProvider, ILocalMetadataProvider<Movie>
+ public class MovieXmlProvider : BaseXmlProvider<Movie>
{
private readonly ILogger _logger;
@@ -18,61 +17,34 @@ namespace MediaBrowser.Providers.Movies
_logger = logger;
}
- public async Task<MetadataResult<Movie>> GetMetadata(string path, CancellationToken cancellationToken)
+ protected override void Fetch(Movie item, string path, CancellationToken cancellationToken)
{
- path = GetXmlFile(path).FullName;
-
- var result = new MetadataResult<Movie>();
-
- await XmlParsingResourcePool.WaitAsync(cancellationToken).ConfigureAwait(false);
-
- try
- {
- result.Item = new Movie();
-
- new MovieXmlParser(_logger).Fetch(result.Item, path, cancellationToken);
- result.HasMetadata = true;
- }
- catch (FileNotFoundException)
- {
- result.HasMetadata = false;
- }
- finally
- {
- XmlParsingResourcePool.Release();
- }
-
- return result;
- }
-
- public string Name
- {
- get { return "Media Browser Xml"; }
+ new MovieXmlParser(_logger).Fetch(item, path, cancellationToken);
}
- protected override FileInfo GetXmlFile(string path)
+ protected override FileInfo GetXmlFile(ItemInfo info)
{
- return GetXmlFileInfo(path, FileSystem);
+ return GetXmlFileInfo(info, FileSystem);
}
- public static FileInfo GetXmlFileInfo(string path, IFileSystem _fileSystem)
+ public static FileInfo GetXmlFileInfo(ItemInfo info, IFileSystem fileSystem)
{
- var fileInfo = _fileSystem.GetFileSystemInfo(path);
+ var fileInfo = fileSystem.GetFileSystemInfo(info.Path);
var directoryInfo = fileInfo as DirectoryInfo;
if (directoryInfo == null)
{
- directoryInfo = new DirectoryInfo(Path.GetDirectoryName(path));
+ directoryInfo = new DirectoryInfo(Path.GetDirectoryName(info.Path));
}
var directoryPath = directoryInfo.FullName;
- var specificFile = Path.Combine(directoryPath, Path.GetFileNameWithoutExtension(path) + ".xml");
+ var specificFile = Path.Combine(directoryPath, Path.GetFileNameWithoutExtension(info.Path) + ".xml");
var file = new FileInfo(specificFile);
- return file.Exists ? file : new FileInfo(Path.Combine(directoryPath, "movie.xml"));
+ return info.IsInMixedFolder || file.Exists ? file : new FileInfo(Path.Combine(directoryPath, "movie.xml"));
}
}
}
diff --git a/MediaBrowser.Providers/Movies/OpenMovieDatabaseProvider.cs b/MediaBrowser.Providers/Movies/OpenMovieDatabaseProvider.cs
deleted file mode 100644
index 9b4f17a86..000000000
--- a/MediaBrowser.Providers/Movies/OpenMovieDatabaseProvider.cs
+++ /dev/null
@@ -1,285 +0,0 @@
-using MediaBrowser.Common.Net;
-using MediaBrowser.Controller.Configuration;
-using MediaBrowser.Controller.Entities;
-using MediaBrowser.Controller.Entities.Movies;
-using MediaBrowser.Controller.Entities.TV;
-using MediaBrowser.Controller.Providers;
-using MediaBrowser.Model.Entities;
-using MediaBrowser.Model.Logging;
-using MediaBrowser.Model.Serialization;
-using System;
-using System.Globalization;
-using System.Linq;
-using System.Net;
-using System.Threading;
-using System.Threading.Tasks;
-
-namespace MediaBrowser.Providers.Movies
-{
- public class OpenMovieDatabaseProvider : BaseMetadataProvider
- {
- private readonly SemaphoreSlim _resourcePool = new SemaphoreSlim(1, 1);
-
- /// <summary>
- /// Gets the json serializer.
- /// </summary>
- /// <value>The json serializer.</value>
- protected IJsonSerializer JsonSerializer { get; private set; }
-
- /// <summary>
- /// Gets the HTTP client.
- /// </summary>
- /// <value>The HTTP client.</value>
- protected IHttpClient HttpClient { get; private set; }
-
- public OpenMovieDatabaseProvider(ILogManager logManager, IServerConfigurationManager configurationManager, IJsonSerializer jsonSerializer, IHttpClient httpClient)
- : base(logManager, configurationManager)
- {
- JsonSerializer = jsonSerializer;
- HttpClient = httpClient;
- }
-
- /// <summary>
- /// Gets the provider version.
- /// </summary>
- /// <value>The provider version.</value>
- protected override string ProviderVersion
- {
- get
- {
- return "13";
- }
- }
-
- /// <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>
- /// 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)
- {
- var trailer = item as Trailer;
-
- // Don't support local trailers
- if (trailer != null)
- {
- return !trailer.IsLocalTrailer;
- }
-
- return item is Movie || item is MusicVideo || item is Series;
- }
-
- /// <summary>
- /// Gets the priority.
- /// </summary>
- /// <value>The priority.</value>
- public override MetadataProviderPriority Priority
- {
- get
- {
- // Run after moviedb and xml providers
- return MetadataProviderPriority.Fifth;
- }
- }
-
- protected readonly CultureInfo UsCulture = new CultureInfo("en-US");
-
- public override async Task<bool> FetchAsync(BaseItem item, bool force, BaseProviderInfo providerInfo, CancellationToken cancellationToken)
- {
- var imdbId = item.GetProviderId(MetadataProviders.Imdb);
-
- if (string.IsNullOrEmpty(imdbId))
- {
- SetLastRefreshed(item, DateTime.UtcNow, providerInfo);
- return true;
- }
-
- var imdbParam = imdbId.StartsWith("tt", StringComparison.OrdinalIgnoreCase) ? imdbId : "tt" + imdbId;
-
- var url = string.Format("http://www.omdbapi.com/?i={0}&tomatoes=true", imdbParam);
-
- using (var stream = await HttpClient.Get(new HttpRequestOptions
- {
- Url = url,
- ResourcePool = _resourcePool,
- CancellationToken = cancellationToken
-
- }).ConfigureAwait(false))
- {
- var result = JsonSerializer.DeserializeFromStream<RootObject>(stream);
-
- var hasCriticRating = item as IHasCriticRating;
- if (hasCriticRating != null)
- {
- // Seeing some bogus RT data on omdb for series, so filter it out here
- // RT doesn't even have tv series
- int tomatoMeter;
-
- if (!string.IsNullOrEmpty(result.tomatoMeter)
- && int.TryParse(result.tomatoMeter, NumberStyles.Integer, UsCulture, out tomatoMeter)
- && tomatoMeter >= 0)
- {
- hasCriticRating.CriticRating = tomatoMeter;
- }
-
- if (!string.IsNullOrEmpty(result.tomatoConsensus)
- && !string.Equals(result.tomatoConsensus, "n/a", StringComparison.OrdinalIgnoreCase)
- && !string.Equals(result.tomatoConsensus, "No consensus yet.", StringComparison.OrdinalIgnoreCase))
- {
- hasCriticRating.CriticRatingSummary = WebUtility.HtmlDecode(result.tomatoConsensus);
- }
- }
-
- int voteCount;
-
- if (!string.IsNullOrEmpty(result.imdbVotes)
- && int.TryParse(result.imdbVotes, NumberStyles.Number, UsCulture, out voteCount)
- && voteCount >= 0)
- {
- item.VoteCount = voteCount;
- }
-
- float imdbRating;
-
- if (!string.IsNullOrEmpty(result.imdbRating)
- && float.TryParse(result.imdbRating, NumberStyles.Any, UsCulture, out imdbRating)
- && imdbRating >= 0)
- {
- item.CommunityRating = imdbRating;
- }
-
- if (!string.IsNullOrEmpty(result.Website)
- && !string.Equals(result.Website, "n/a", StringComparison.OrdinalIgnoreCase))
- {
- item.HomePageUrl = result.Website;
- }
-
- ParseAdditionalMetadata(item, result);
- }
-
- SetLastRefreshed(item, DateTime.UtcNow, providerInfo);
- return true;
- }
-
- private void ParseAdditionalMetadata(BaseItem item, RootObject result)
- {
- // Grab series genres because imdb data is better than tvdb. Leave movies alone
- // But only do it if english is the preferred language because this data will not be localized
- if (!item.LockedFields.Contains(MetadataFields.Genres) &&
- ShouldFetchGenres(item) &&
- !string.IsNullOrWhiteSpace(result.Genre) &&
- !string.Equals(result.Genre, "n/a", StringComparison.OrdinalIgnoreCase))
- {
- item.Genres.Clear();
-
- foreach (var genre in result.Genre
- .Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries)
- .Select(i => i.Trim())
- .Where(i => !string.IsNullOrWhiteSpace(i)))
- {
- item.AddGenre(genre);
- }
- }
-
- var hasMetascore = item as IHasMetascore;
- if (hasMetascore != null)
- {
- float metascore;
-
- if (!string.IsNullOrEmpty(result.Metascore) && float.TryParse(result.Metascore, NumberStyles.Any, UsCulture, out metascore) && metascore >= 0)
- {
- hasMetascore.Metascore = metascore;
- }
- }
-
- var hasAwards = item as IHasAwards;
- if (hasAwards != null && !string.IsNullOrEmpty(result.Awards) &&
- !string.Equals(result.Awards, "n/a", StringComparison.OrdinalIgnoreCase))
- {
- hasAwards.AwardSummary = WebUtility.HtmlDecode(result.Awards);
- }
- }
-
- private bool ShouldFetchGenres(BaseItem item)
- {
- var lang = item.GetPreferredMetadataLanguage();
-
- // The data isn't localized and so can only be used for english users
- if (!string.Equals(lang, "en", StringComparison.OrdinalIgnoreCase))
- {
- return false;
- }
-
- // Only fetch if other providers didn't get anything
- if (item is Trailer)
- {
- return item.Genres.Count == 0;
- }
-
- return item is Series || item is Movie;
- }
-
- protected class RootObject
- {
- public string Title { get; set; }
- public string Year { get; set; }
- public string Rated { get; set; }
- public string Released { get; set; }
- public string Runtime { get; set; }
- public string Genre { get; set; }
- public string Director { get; set; }
- public string Writer { get; set; }
- public string Actors { get; set; }
- public string Plot { get; set; }
- public string Poster { get; set; }
- public string imdbRating { get; set; }
- public string imdbVotes { get; set; }
- public string imdbID { get; set; }
- public string Type { get; set; }
- public string tomatoMeter { get; set; }
- public string tomatoImage { get; set; }
- public string tomatoRating { get; set; }
- public string tomatoReviews { get; set; }
- public string tomatoFresh { get; set; }
- public string tomatoRotten { get; set; }
- public string tomatoConsensus { get; set; }
- public string tomatoUserMeter { get; set; }
- public string tomatoUserRating { get; set; }
- public string tomatoUserReviews { get; set; }
- public string DVD { get; set; }
- public string BoxOffice { get; set; }
- public string Production { get; set; }
- public string Website { get; set; }
- public string Response { get; set; }
-
- public string Language { get; set; }
- public string Country { get; set; }
- public string Awards { get; set; }
- public string Metascore { get; set; }
- }
- }
-}
diff --git a/MediaBrowser.Providers/Movies/TmdbSettings.cs b/MediaBrowser.Providers/Movies/TmdbSettings.cs
new file mode 100644
index 000000000..59e8f7cef
--- /dev/null
+++ b/MediaBrowser.Providers/Movies/TmdbSettings.cs
@@ -0,0 +1,17 @@
+using System.Collections.Generic;
+
+namespace MediaBrowser.Providers.Movies
+{
+ internal class TmdbImageSettings
+ {
+ public List<string> backdrop_sizes { get; set; }
+ public string base_url { get; set; }
+ public List<string> poster_sizes { get; set; }
+ public List<string> profile_sizes { get; set; }
+ }
+
+ internal class TmdbSettingsResult
+ {
+ public TmdbImageSettings images { get; set; }
+ }
+}
diff --git a/MediaBrowser.Providers/Movies/TrailerMetadataService.cs b/MediaBrowser.Providers/Movies/TrailerMetadataService.cs
new file mode 100644
index 000000000..ad9cf5231
--- /dev/null
+++ b/MediaBrowser.Providers/Movies/TrailerMetadataService.cs
@@ -0,0 +1,43 @@
+using MediaBrowser.Common.IO;
+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.Movies
+{
+ public class TrailerMetadataService : MetadataService<Trailer, ItemId>
+ {
+ private readonly ILibraryManager _libraryManager;
+
+ public TrailerMetadataService(IServerConfigurationManager serverConfigurationManager, ILogger logger, IProviderManager providerManager, IProviderRepository providerRepo, IFileSystem fileSystem, ILibraryManager libraryManager)
+ : base(serverConfigurationManager, logger, providerManager, providerRepo, fileSystem)
+ {
+ _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(Trailer source, Trailer target, List<MetadataFields> lockedFields, bool replaceData, bool mergeMetadataSettings)
+ {
+ ProviderUtils.MergeBaseItemData(source, target, lockedFields, replaceData, mergeMetadataSettings);
+ }
+
+ protected override Task SaveItem(Trailer item, ItemUpdateType reason, CancellationToken cancellationToken)
+ {
+ return _libraryManager.UpdateItem(item, reason, cancellationToken);
+ }
+ }
+}
diff --git a/MediaBrowser.Providers/Movies/TrailerXmlProvider.cs b/MediaBrowser.Providers/Movies/TrailerXmlProvider.cs
new file mode 100644
index 000000000..52704b151
--- /dev/null
+++ b/MediaBrowser.Providers/Movies/TrailerXmlProvider.cs
@@ -0,0 +1,30 @@
+using MediaBrowser.Common.IO;
+using MediaBrowser.Controller.Entities;
+using MediaBrowser.Controller.Providers;
+using MediaBrowser.Model.Logging;
+using System.IO;
+using System.Threading;
+
+namespace MediaBrowser.Providers.Movies
+{
+ public class TrailerXmlProvider : BaseXmlProvider<Trailer>
+ {
+ private readonly ILogger _logger;
+
+ public TrailerXmlProvider(IFileSystem fileSystem, ILogger logger)
+ : base(fileSystem)
+ {
+ _logger = logger;
+ }
+
+ protected override void Fetch(Trailer item, string path, CancellationToken cancellationToken)
+ {
+ new MovieXmlParser(_logger).Fetch(item, path, cancellationToken);
+ }
+
+ protected override FileInfo GetXmlFile(ItemInfo info)
+ {
+ return MovieXmlProvider.GetXmlFileInfo(info, FileSystem);
+ }
+ }
+}
diff --git a/MediaBrowser.Providers/Music/AlbumXmlProvider.cs b/MediaBrowser.Providers/Music/AlbumXmlProvider.cs
index 7c7de6182..06a1ed121 100644
--- a/MediaBrowser.Providers/Music/AlbumXmlProvider.cs
+++ b/MediaBrowser.Providers/Music/AlbumXmlProvider.cs
@@ -4,11 +4,10 @@ using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Logging;
using System.IO;
using System.Threading;
-using System.Threading.Tasks;
namespace MediaBrowser.Providers.Music
{
- class AlbumXmlProvider : BaseXmlProvider, ILocalMetadataProvider<MusicAlbum>
+ class AlbumXmlProvider : BaseXmlProvider<MusicAlbum>
{
private readonly ILogger _logger;
@@ -18,42 +17,14 @@ namespace MediaBrowser.Providers.Music
_logger = logger;
}
- public async Task<MetadataResult<MusicAlbum>> GetMetadata(string path, CancellationToken cancellationToken)
+ protected override void Fetch(MusicAlbum item, string path, CancellationToken cancellationToken)
{
- path = GetXmlFile(path).FullName;
-
- var result = new MetadataResult<MusicAlbum>();
-
- await XmlParsingResourcePool.WaitAsync(cancellationToken).ConfigureAwait(false);
-
- try
- {
- var item = new MusicAlbum();
-
- new BaseItemXmlParser<MusicAlbum>(_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"; }
+ new BaseItemXmlParser<MusicAlbum>(_logger).Fetch(item, path, cancellationToken);
}
- protected override FileInfo GetXmlFile(string path)
+ protected override FileInfo GetXmlFile(ItemInfo info)
{
- return new FileInfo(Path.Combine(path, "album.xml"));
+ return new FileInfo(Path.Combine(info.Path, "album.xml"));
}
}
}
diff --git a/MediaBrowser.Providers/Music/ArtistXmlProvider.cs b/MediaBrowser.Providers/Music/ArtistXmlProvider.cs
index 30c38bdaa..921cbc8b9 100644
--- a/MediaBrowser.Providers/Music/ArtistXmlProvider.cs
+++ b/MediaBrowser.Providers/Music/ArtistXmlProvider.cs
@@ -4,11 +4,10 @@ using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Logging;
using System.IO;
using System.Threading;
-using System.Threading.Tasks;
namespace MediaBrowser.Providers.Music
{
- class ArtistXmlProvider : BaseXmlProvider, ILocalMetadataProvider<MusicArtist>
+ class ArtistXmlProvider : BaseXmlProvider<MusicArtist>
{
private readonly ILogger _logger;
@@ -18,42 +17,14 @@ namespace MediaBrowser.Providers.Music
_logger = logger;
}
- public async Task<MetadataResult<MusicArtist>> GetMetadata(string path, CancellationToken cancellationToken)
+ protected override void Fetch(MusicArtist item, string path, CancellationToken cancellationToken)
{
- path = GetXmlFile(path).FullName;
-
- var result = new MetadataResult<MusicArtist>();
-
- await XmlParsingResourcePool.WaitAsync(cancellationToken).ConfigureAwait(false);
-
- try
- {
- var item = new MusicArtist();
-
- new BaseItemXmlParser<MusicArtist>(_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"; }
+ new BaseItemXmlParser<MusicArtist>(_logger).Fetch(item, path, cancellationToken);
}
- protected override FileInfo GetXmlFile(string path)
+ protected override FileInfo GetXmlFile(ItemInfo info)
{
- return new FileInfo(Path.Combine(path, "artist.xml"));
+ return new FileInfo(Path.Combine(info.Path, "artist.xml"));
}
}
}
diff --git a/MediaBrowser.Providers/Music/AudioMetadataService.cs b/MediaBrowser.Providers/Music/AudioMetadataService.cs
new file mode 100644
index 000000000..c056fdabf
--- /dev/null
+++ b/MediaBrowser.Providers/Music/AudioMetadataService.cs
@@ -0,0 +1,53 @@
+using MediaBrowser.Common.IO;
+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.Music
+{
+ public class AudioMetadataService : MetadataService<Audio, ItemId>
+ {
+ private readonly ILibraryManager _libraryManager;
+
+ public AudioMetadataService(IServerConfigurationManager serverConfigurationManager, ILogger logger, IProviderManager providerManager, IProviderRepository providerRepo, IFileSystem fileSystem, ILibraryManager libraryManager)
+ : base(serverConfigurationManager, logger, providerManager, providerRepo, fileSystem)
+ {
+ _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(Audio source, Audio target, List<MetadataFields> lockedFields, bool replaceData, bool mergeMetadataSettings)
+ {
+ ProviderUtils.MergeBaseItemData(source, target, lockedFields, replaceData, mergeMetadataSettings);
+
+ if (replaceData || target.Artists.Count == 0)
+ {
+ target.Artists = source.Artists;
+ }
+
+ if (replaceData || string.IsNullOrEmpty(target.Album))
+ {
+ target.Album = source.Album;
+ }
+ }
+
+ protected override Task SaveItem(Audio item, ItemUpdateType reason, CancellationToken cancellationToken)
+ {
+ return _libraryManager.UpdateItem(item, reason, cancellationToken);
+ }
+ }
+}
diff --git a/MediaBrowser.Providers/Music/MusicVideoXmlProvider.cs b/MediaBrowser.Providers/Music/MusicVideoXmlProvider.cs
index dcfe0d89e..5bdc2cdd0 100644
--- a/MediaBrowser.Providers/Music/MusicVideoXmlProvider.cs
+++ b/MediaBrowser.Providers/Music/MusicVideoXmlProvider.cs
@@ -5,11 +5,10 @@ using MediaBrowser.Model.Logging;
using MediaBrowser.Providers.Movies;
using System.IO;
using System.Threading;
-using System.Threading.Tasks;
namespace MediaBrowser.Providers.Music
{
- class MusicVideoXmlProvider : BaseXmlProvider, ILocalMetadataProvider<MusicVideo>
+ class MusicVideoXmlProvider : BaseXmlProvider<MusicVideo>
{
private readonly ILogger _logger;
@@ -19,42 +18,14 @@ namespace MediaBrowser.Providers.Music
_logger = logger;
}
- public async Task<MetadataResult<MusicVideo>> GetMetadata(string path, CancellationToken cancellationToken)
+ protected override void Fetch(MusicVideo item, string path, CancellationToken cancellationToken)
{
- path = GetXmlFile(path).FullName;
-
- var result = new MetadataResult<MusicVideo>();
-
- await XmlParsingResourcePool.WaitAsync(cancellationToken).ConfigureAwait(false);
-
- try
- {
- var item = new MusicVideo();
-
- new MusicVideoXmlParser(_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"; }
+ new MusicVideoXmlParser(_logger).Fetch(item, path, cancellationToken);
}
- protected override FileInfo GetXmlFile(string path)
+ protected override FileInfo GetXmlFile(ItemInfo info)
{
- return MovieXmlProvider.GetXmlFileInfo(path, FileSystem);
+ return MovieXmlProvider.GetXmlFileInfo(info, FileSystem);
}
}
}
diff --git a/MediaBrowser.Providers/People/PersonXmlProvider.cs b/MediaBrowser.Providers/People/PersonXmlProvider.cs
index c23458f68..0996615d3 100644
--- a/MediaBrowser.Providers/People/PersonXmlProvider.cs
+++ b/MediaBrowser.Providers/People/PersonXmlProvider.cs
@@ -4,11 +4,10 @@ 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>
+ public class PersonXmlProvider : BaseXmlProvider<Person>
{
private readonly ILogger _logger;
@@ -18,42 +17,14 @@ namespace MediaBrowser.Providers.People
_logger = logger;
}
- public async Task<MetadataResult<Person>> GetMetadata(string path, CancellationToken cancellationToken)
+ protected override void Fetch(Person item, string path, CancellationToken cancellationToken)
{
- path = GetXmlFile(path).FullName;
-
- 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"; }
+ new BaseItemXmlParser<Person>(_logger).Fetch(item, path, cancellationToken);
}
- protected override FileInfo GetXmlFile(string path)
+ protected override FileInfo GetXmlFile(ItemInfo info)
{
- return new FileInfo(Path.Combine(path, "person.xml"));
+ return new FileInfo(Path.Combine(info.Path, "person.xml"));
}
}
}
diff --git a/MediaBrowser.Providers/ProviderUtils.cs b/MediaBrowser.Providers/ProviderUtils.cs
index 61fe19a61..ecefb72c4 100644
--- a/MediaBrowser.Providers/ProviderUtils.cs
+++ b/MediaBrowser.Providers/ProviderUtils.cs
@@ -93,7 +93,10 @@ namespace MediaBrowser.Providers
{
if (replaceData || !target.RunTimeTicks.HasValue)
{
- target.RunTimeTicks = source.RunTimeTicks;
+ if (!(target is Audio) && !(target is Video))
+ {
+ target.RunTimeTicks = source.RunTimeTicks;
+ }
}
}
@@ -159,6 +162,11 @@ namespace MediaBrowser.Providers
MergeAlbumArtist(source, target, lockedFields, replaceData);
MergeBudget(source, target, lockedFields, replaceData);
+ MergeMetascore(source, target, lockedFields, replaceData);
+ MergeCriticRating(source, target, lockedFields, replaceData);
+ MergeAwards(source, target, lockedFields, replaceData);
+ MergeTaglines(source, target, lockedFields, replaceData);
+ MergeTrailers(source, target, lockedFields, replaceData);
if (mergeMetadataSettings)
{
@@ -218,5 +226,80 @@ namespace MediaBrowser.Providers
}
}
}
+
+ private static void MergeMetascore(BaseItem source, BaseItem target, List<MetadataFields> lockedFields, bool replaceData)
+ {
+ var sourceCast = source as IHasMetascore;
+ var targetCast = target as IHasMetascore;
+
+ if (sourceCast != null && targetCast != null)
+ {
+ if (replaceData || !targetCast.Metascore.HasValue)
+ {
+ targetCast.Metascore = sourceCast.Metascore;
+ }
+ }
+ }
+
+ private static void MergeAwards(BaseItem source, BaseItem target, List<MetadataFields> lockedFields, bool replaceData)
+ {
+ var sourceCast = source as IHasAwards;
+ var targetCast = target as IHasAwards;
+
+ if (sourceCast != null && targetCast != null)
+ {
+ if (replaceData || string.IsNullOrEmpty(targetCast.AwardSummary))
+ {
+ targetCast.AwardSummary = sourceCast.AwardSummary;
+ }
+ }
+ }
+
+ private static void MergeCriticRating(BaseItem source, BaseItem target, List<MetadataFields> lockedFields, bool replaceData)
+ {
+ var sourceCast = source as IHasCriticRating;
+ var targetCast = target as IHasCriticRating;
+
+ if (sourceCast != null && targetCast != null)
+ {
+ if (replaceData || !targetCast.CriticRating.HasValue)
+ {
+ targetCast.CriticRating = sourceCast.CriticRating;
+ }
+
+ if (replaceData || string.IsNullOrEmpty(targetCast.CriticRatingSummary))
+ {
+ targetCast.CriticRatingSummary = sourceCast.CriticRatingSummary;
+ }
+ }
+ }
+
+ private static void MergeTaglines(BaseItem source, BaseItem target, List<MetadataFields> lockedFields, bool replaceData)
+ {
+ var sourceCast = source as IHasTaglines;
+ var targetCast = target as IHasTaglines;
+
+ if (sourceCast != null && targetCast != null)
+ {
+ if (replaceData || targetCast.Taglines.Count == 0)
+ {
+ targetCast.Taglines = sourceCast.Taglines;
+ }
+ }
+ }
+
+ private static void MergeTrailers(BaseItem source, BaseItem target, List<MetadataFields> lockedFields, bool replaceData)
+ {
+ var sourceCast = source as IHasTrailers;
+ var targetCast = target as IHasTrailers;
+
+ if (sourceCast != null && targetCast != null)
+ {
+ if (replaceData || targetCast.RemoteTrailers.Count == 0)
+ {
+ targetCast.RemoteTrailers = sourceCast.RemoteTrailers;
+ }
+ }
+ }
}
}
diff --git a/MediaBrowser.Providers/RefreshIntrosTask.cs b/MediaBrowser.Providers/RefreshIntrosTask.cs
index b20a6e331..bfe7e7609 100644
--- a/MediaBrowser.Providers/RefreshIntrosTask.cs
+++ b/MediaBrowser.Providers/RefreshIntrosTask.cs
@@ -90,20 +90,14 @@ namespace MediaBrowser.Providers
}
var dbItem = _libraryManager.GetItemById(item.Id);
- var isNewItem = false;
if (dbItem != null)
{
- dbItem.ResetResolveArgs(item.ResolveArgs);
item = dbItem;
}
- else
- {
- isNewItem = true;
- }
// Force the save if it's a new item
- await item.RefreshMetadata(cancellationToken, isNewItem).ConfigureAwait(false);
+ await item.RefreshMetadata(cancellationToken).ConfigureAwait(false);
}
}
}
diff --git a/MediaBrowser.Providers/Savers/GameXmlSaver.cs b/MediaBrowser.Providers/Savers/GameXmlSaver.cs
index a6225b58c..613819517 100644
--- a/MediaBrowser.Providers/Savers/GameXmlSaver.cs
+++ b/MediaBrowser.Providers/Savers/GameXmlSaver.cs
@@ -119,7 +119,7 @@ namespace MediaBrowser.Providers.Savers
return Path.ChangeExtension(item.Path, ".xml");
}
- return Path.Combine(item.MetaLocation, "game.xml");
+ return Path.Combine(item.ContainingFolderPath, "game.xml");
}
}
}
diff --git a/MediaBrowser.Providers/Savers/MovieXmlSaver.cs b/MediaBrowser.Providers/Savers/MovieXmlSaver.cs
index e5ba1aefd..15fdc6752 100644
--- a/MediaBrowser.Providers/Savers/MovieXmlSaver.cs
+++ b/MediaBrowser.Providers/Savers/MovieXmlSaver.cs
@@ -50,16 +50,9 @@ namespace MediaBrowser.Providers.Savers
// If new metadata has been downloaded and save local is on
if (item.IsSaveLocalMetadataEnabled() && (wasMetadataEdited || wasMetadataDownloaded))
{
- var trailer = item as Trailer;
-
- // Don't support local trailers
- if (trailer != null)
- {
- return !trailer.IsLocalTrailer;
- }
var video = item as Video;
// Check parent for null to avoid running this against things like video backdrops
- return video != null && !(item is Episode) && video.Parent != null;
+ return video != null && !(item is Episode) && !video.IsOwnedItem;
}
return false;
@@ -145,7 +138,7 @@ namespace MediaBrowser.Providers.Savers
return Path.ChangeExtension(item.Path, ".xml");
}
- return Path.Combine(item.MetaLocation, "movie.xml");
+ return Path.Combine(item.ContainingFolderPath, "movie.xml");
}
}
}
diff --git a/MediaBrowser.Providers/TV/EpisodeMetadataService.cs b/MediaBrowser.Providers/TV/EpisodeMetadataService.cs
index 2b6edaf08..ddb89bd42 100644
--- a/MediaBrowser.Providers/TV/EpisodeMetadataService.cs
+++ b/MediaBrowser.Providers/TV/EpisodeMetadataService.cs
@@ -1,4 +1,5 @@
-using MediaBrowser.Common.IO;
+using System.IO;
+using MediaBrowser.Common.IO;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Entities.TV;
using MediaBrowser.Controller.Library;
@@ -102,8 +103,10 @@ namespace MediaBrowser.Providers.TV
var currentIndexNumberEnd = item.IndexNumberEnd;
var currentParentIndexNumber = item.ParentIndexNumber;
- item.IndexNumber = item.IndexNumber ?? TVUtils.GetEpisodeNumberFromFile(item.Path, item.Parent is Season);
- item.IndexNumberEnd = item.IndexNumberEnd ?? TVUtils.GetEndingEpisodeNumberFromFile(item.Path);
+ var filename = Path.GetFileName(item.Path);
+
+ item.IndexNumber = item.IndexNumber ?? TVUtils.GetEpisodeNumberFromFile(filename, item.Parent is Season);
+ item.IndexNumberEnd = item.IndexNumberEnd ?? TVUtils.GetEndingEpisodeNumberFromFile(filename);
if (!item.ParentIndexNumber.HasValue)
{
diff --git a/MediaBrowser.Providers/TV/EpisodeXmlProvider.cs b/MediaBrowser.Providers/TV/EpisodeXmlProvider.cs
index b8d88f5e6..b1f8ef976 100644
--- a/MediaBrowser.Providers/TV/EpisodeXmlProvider.cs
+++ b/MediaBrowser.Providers/TV/EpisodeXmlProvider.cs
@@ -4,11 +4,10 @@ using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Logging;
using System.IO;
using System.Threading;
-using System.Threading.Tasks;
namespace MediaBrowser.Providers.TV
{
- public class EpisodeXmlProvider : BaseXmlProvider, ILocalMetadataProvider<Episode>
+ public class EpisodeXmlProvider : BaseXmlProvider<Episode>
{
private readonly ILogger _logger;
@@ -18,43 +17,16 @@ namespace MediaBrowser.Providers.TV
_logger = logger;
}
- public async Task<MetadataResult<Episode>> GetMetadata(string path, CancellationToken cancellationToken)
+ protected override void Fetch(Episode item, string path, CancellationToken cancellationToken)
{
- path = GetXmlFile(path).FullName;
-
- var result = new MetadataResult<Episode>();
-
- await XmlParsingResourcePool.WaitAsync(cancellationToken).ConfigureAwait(false);
-
- try
- {
- result.Item = new Episode();
-
- new EpisodeXmlParser(_logger).Fetch(result.Item, path, cancellationToken);
- result.HasMetadata = true;
- }
- catch (FileNotFoundException)
- {
- result.HasMetadata = false;
- }
- finally
- {
- XmlParsingResourcePool.Release();
- }
-
- return result;
- }
-
- public string Name
- {
- get { return "Media Browser Xml"; }
+ new EpisodeXmlParser(_logger).Fetch(item, path, cancellationToken);
}
- protected override FileInfo GetXmlFile(string path)
+ protected override FileInfo GetXmlFile(ItemInfo info)
{
- var metadataPath = Path.GetDirectoryName(path);
+ var metadataPath = Path.GetDirectoryName(info.Path);
metadataPath = Path.Combine(metadataPath, "metadata");
- var metadataFile = Path.Combine(metadataPath, Path.ChangeExtension(Path.GetFileName(path), ".xml"));
+ var metadataFile = Path.Combine(metadataPath, Path.ChangeExtension(Path.GetFileName(info.Path), ".xml"));
return new FileInfo(metadataFile);
}
diff --git a/MediaBrowser.Providers/TV/SeasonXmlProvider.cs b/MediaBrowser.Providers/TV/SeasonXmlProvider.cs
index 9dcc9fe4f..f9fe45120 100644
--- a/MediaBrowser.Providers/TV/SeasonXmlProvider.cs
+++ b/MediaBrowser.Providers/TV/SeasonXmlProvider.cs
@@ -4,14 +4,13 @@ using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Logging;
using System.IO;
using System.Threading;
-using System.Threading.Tasks;
namespace MediaBrowser.Providers.TV
{
/// <summary>
/// Class SeriesProviderFromXml
/// </summary>
- public class SeasonXmlProvider : BaseXmlProvider, ILocalMetadataProvider<Season>
+ public class SeasonXmlProvider : BaseXmlProvider<Season>
{
private readonly ILogger _logger;
@@ -21,42 +20,14 @@ namespace MediaBrowser.Providers.TV
_logger = logger;
}
- public async Task<MetadataResult<Season>> GetMetadata(string path, CancellationToken cancellationToken)
+ protected override void Fetch(Season item, string path, CancellationToken cancellationToken)
{
- path = GetXmlFile(path).FullName;
-
- var result = new MetadataResult<Season>();
-
- await XmlParsingResourcePool.WaitAsync(cancellationToken).ConfigureAwait(false);
-
- try
- {
- var person = new Season();
-
- new BaseItemXmlParser<Season>(_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"; }
+ new BaseItemXmlParser<Season>(_logger).Fetch(item, path, cancellationToken);
}
- protected override FileInfo GetXmlFile(string path)
+ protected override FileInfo GetXmlFile(ItemInfo info)
{
- return new FileInfo(Path.Combine(path, "season.xml"));
+ return new FileInfo(Path.Combine(info.Path, "season.xml"));
}
}
}
diff --git a/MediaBrowser.Providers/TV/SeriesPostScanTask.cs b/MediaBrowser.Providers/TV/SeriesPostScanTask.cs
index 6d2ece19c..dc06857ce 100644
--- a/MediaBrowser.Providers/TV/SeriesPostScanTask.cs
+++ b/MediaBrowser.Providers/TV/SeriesPostScanTask.cs
@@ -2,6 +2,7 @@
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Entities.TV;
using MediaBrowser.Controller.Library;
+using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Logging;
using System;
@@ -53,7 +54,7 @@ namespace MediaBrowser.Providers.TV
await new MissingEpisodeProvider(_logger, _config).Run(seriesGroups, cancellationToken).ConfigureAwait(false);
var numComplete = 0;
-
+
foreach (var series in seriesList)
{
cancellationToken.ThrowIfCancellationRequested();
@@ -171,7 +172,9 @@ namespace MediaBrowser.Providers.TV
{
foreach (var series in group)
{
- await series.RefreshMetadata(cancellationToken, true)
+ await series.RefreshMetadata(new MetadataRefreshOptions
+ {
+ }, cancellationToken)
.ConfigureAwait(false);
await series.ValidateChildren(new Progress<double>(), cancellationToken, true)
@@ -438,7 +441,9 @@ namespace MediaBrowser.Providers.TV
await season.AddChild(episode, cancellationToken).ConfigureAwait(false);
- await episode.RefreshMetadata(cancellationToken).ConfigureAwait(false);
+ await episode.RefreshMetadata(new MetadataRefreshOptions
+ {
+ }, cancellationToken).ConfigureAwait(false);
}
/// <summary>
@@ -464,7 +469,9 @@ namespace MediaBrowser.Providers.TV
};
await series.AddChild(season, cancellationToken).ConfigureAwait(false);
- await season.RefreshMetadata(cancellationToken).ConfigureAwait(false);
+ await season.RefreshMetadata(new MetadataRefreshOptions
+ {
+ }, cancellationToken).ConfigureAwait(false);
return season;
}
diff --git a/MediaBrowser.Providers/TV/SeriesXmlProvider.cs b/MediaBrowser.Providers/TV/SeriesXmlProvider.cs
index 8f0c63136..4dfaa3925 100644
--- a/MediaBrowser.Providers/TV/SeriesXmlProvider.cs
+++ b/MediaBrowser.Providers/TV/SeriesXmlProvider.cs
@@ -4,14 +4,13 @@ using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Logging;
using System.IO;
using System.Threading;
-using System.Threading.Tasks;
namespace MediaBrowser.Providers.TV
{
/// <summary>
/// Class SeriesProviderFromXml
/// </summary>
- public class SeriesXmlProvider : BaseXmlProvider, ILocalMetadataProvider<Series>
+ public class SeriesXmlProvider : BaseXmlProvider<Series>
{
private readonly ILogger _logger;
@@ -21,42 +20,14 @@ namespace MediaBrowser.Providers.TV
_logger = logger;
}
- public async Task<MetadataResult<Series>> GetMetadata(string path, CancellationToken cancellationToken)
+ protected override void Fetch(Series item, string path, CancellationToken cancellationToken)
{
- path = GetXmlFile(path).FullName;
-
- var result = new MetadataResult<Series>();
-
- await XmlParsingResourcePool.WaitAsync(cancellationToken).ConfigureAwait(false);
-
- try
- {
- var person = new Series();
-
- new SeriesXmlParser(_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"; }
+ new SeriesXmlParser(_logger).Fetch(item, path, cancellationToken);
}
- protected override FileInfo GetXmlFile(string path)
+ protected override FileInfo GetXmlFile(ItemInfo info)
{
- return new FileInfo(Path.Combine(path, "series.xml"));
+ return new FileInfo(Path.Combine(info.Path, "series.xml"));
}
}
}
diff --git a/MediaBrowser.Providers/TV/TvdbEpisodeProvider.cs b/MediaBrowser.Providers/TV/TvdbEpisodeProvider.cs
index 5523b8ab3..178c7a265 100644
--- a/MediaBrowser.Providers/TV/TvdbEpisodeProvider.cs
+++ b/MediaBrowser.Providers/TV/TvdbEpisodeProvider.cs
@@ -185,7 +185,14 @@ namespace MediaBrowser.Providers.TV
var file = Path.Combine(seriesDataPath, string.Format("episode-{0}-{1}.xml", seasonNumber.Value, episodeNumber));
var success = false;
var usingAbsoluteData = false;
- var episode = new Episode();
+
+ var episode = new Episode
+ {
+ IndexNumber = id.IndexNumber,
+ ParentIndexNumber = id.ParentIndexNumber,
+ IndexNumberEnd = id.IndexNumberEnd
+ };
+
try
{
FetchMainEpisodeInfo(episode, file, cancellationToken);
diff --git a/MediaBrowser.Providers/Videos/VideoMetadataService.cs b/MediaBrowser.Providers/Videos/VideoMetadataService.cs
new file mode 100644
index 000000000..9aa4ba138
--- /dev/null
+++ b/MediaBrowser.Providers/Videos/VideoMetadataService.cs
@@ -0,0 +1,52 @@
+using MediaBrowser.Common.IO;
+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.Videos
+{
+ public class VideoMetadataService : MetadataService<Video, ItemId>
+ {
+ private readonly ILibraryManager _libraryManager;
+
+ public VideoMetadataService(IServerConfigurationManager serverConfigurationManager, ILogger logger, IProviderManager providerManager, IProviderRepository providerRepo, IFileSystem fileSystem, ILibraryManager libraryManager)
+ : base(serverConfigurationManager, logger, providerManager, providerRepo, fileSystem)
+ {
+ _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(Video source, Video target, List<MetadataFields> lockedFields, bool replaceData, bool mergeMetadataSettings)
+ {
+ ProviderUtils.MergeBaseItemData(source, target, lockedFields, replaceData, mergeMetadataSettings);
+ }
+
+ protected override Task SaveItem(Video item, ItemUpdateType reason, CancellationToken cancellationToken)
+ {
+ return _libraryManager.UpdateItem(item, reason, cancellationToken);
+ }
+
+ public override int Order
+ {
+ get
+ {
+ // Make sure the type-specific services get picked first
+ return 10;
+ }
+ }
+ }
+}
diff --git a/MediaBrowser.Providers/VirtualItemImageValidator.cs b/MediaBrowser.Providers/VirtualItemImageValidator.cs
deleted file mode 100644
index 892275d38..000000000
--- a/MediaBrowser.Providers/VirtualItemImageValidator.cs
+++ /dev/null
@@ -1,57 +0,0 @@
-using MediaBrowser.Controller.Configuration;
-using MediaBrowser.Controller.Entities;
-using MediaBrowser.Controller.Entities.TV;
-using MediaBrowser.Controller.Providers;
-using MediaBrowser.Model.Entities;
-using MediaBrowser.Model.Logging;
-using System;
-using System.Threading;
-using System.Threading.Tasks;
-
-namespace MediaBrowser.Providers
-{
- public class VirtualItemImageValidator : BaseMetadataProvider
- {
- public VirtualItemImageValidator(ILogManager logManager, IServerConfigurationManager configurationManager)
- : base(logManager, configurationManager)
- {
- }
-
- public override bool Supports(BaseItem item)
- {
- var locationType = item.LocationType;
-
- // The regular provider will get virtual seasons
- if (item.LocationType == LocationType.Virtual)
- {
- var season = item as Season;
-
- if (season != null)
- {
- var series = season.Series;
-
- if (series != null && series.LocationType == LocationType.FileSystem)
- {
- return false;
- }
- }
- }
-
- return locationType == LocationType.Virtual ||
- locationType == LocationType.Remote;
- }
-
- public override Task<bool> FetchAsync(BaseItem item, bool force, BaseProviderInfo providerInfo, CancellationToken cancellationToken)
- {
- item.ValidateImages();
-
- SetLastRefreshed(item, DateTime.UtcNow, providerInfo);
- return TrueTaskResult;
- }
-
- public override MetadataProviderPriority Priority
- {
- get { return MetadataProviderPriority.First; }
- }
- }
-}
diff --git a/MediaBrowser.Providers/Years/YearMetadataService.cs b/MediaBrowser.Providers/Years/YearMetadataService.cs
new file mode 100644
index 000000000..01e511177
--- /dev/null
+++ b/MediaBrowser.Providers/Years/YearMetadataService.cs
@@ -0,0 +1,43 @@
+using MediaBrowser.Common.IO;
+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.Years
+{
+ public class YearMetadataService : MetadataService<Year, ItemId>
+ {
+ private readonly ILibraryManager _libraryManager;
+
+ public YearMetadataService(IServerConfigurationManager serverConfigurationManager, ILogger logger, IProviderManager providerManager, IProviderRepository providerRepo, IFileSystem fileSystem, ILibraryManager libraryManager)
+ : base(serverConfigurationManager, logger, providerManager, providerRepo, fileSystem)
+ {
+ _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(Year source, Year target, List<MetadataFields> lockedFields, bool replaceData, bool mergeMetadataSettings)
+ {
+ ProviderUtils.MergeBaseItemData(source, target, lockedFields, replaceData, mergeMetadataSettings);
+ }
+
+ protected override Task SaveItem(Year item, ItemUpdateType reason, CancellationToken cancellationToken)
+ {
+ return _libraryManager.UpdateItem(item, reason, cancellationToken);
+ }
+ }
+}
diff --git a/MediaBrowser.Server.Implementations/EntryPoints/LibraryChangedNotifier.cs b/MediaBrowser.Server.Implementations/EntryPoints/LibraryChangedNotifier.cs
index 32d4c7708..7dacfacc2 100644
--- a/MediaBrowser.Server.Implementations/EntryPoints/LibraryChangedNotifier.cs
+++ b/MediaBrowser.Server.Implementations/EntryPoints/LibraryChangedNotifier.cs
@@ -273,21 +273,8 @@ namespace MediaBrowser.Server.Implementations.EntryPoints
{
if (item.LocationType == LocationType.FileSystem)
{
- return collections.Where(i =>
- {
-
- try
- {
- return i.LocationType == LocationType.FileSystem &&
- i.PhysicalLocations.Contains(item.Path);
- }
- catch (Exception ex)
- {
- _logger.ErrorException("Error getting ResolveArgs for {0}", ex, i.Path);
- return false;
- }
-
- }).Cast<T>();
+ return collections.Where(i => i.LocationType == LocationType.FileSystem &&
+ i.PhysicalLocations.Contains(item.Path)).Cast<T>();
}
}
diff --git a/MediaBrowser.Server.Implementations/IO/LibraryMonitor.cs b/MediaBrowser.Server.Implementations/IO/LibraryMonitor.cs
index 38f5cb62a..f1f9048a9 100644
--- a/MediaBrowser.Server.Implementations/IO/LibraryMonitor.cs
+++ b/MediaBrowser.Server.Implementations/IO/LibraryMonitor.cs
@@ -162,18 +162,7 @@ namespace MediaBrowser.Server.Implementations.IO
.Children
.OfType<Folder>()
.Where(i => i.LocationType != LocationType.Remote && i.LocationType != LocationType.Virtual)
- .SelectMany(f =>
- {
- try
- {
- return f.PhysicalLocations;
- }
- catch (IOException)
- {
- return new string[] { };
- }
-
- })
+ .SelectMany(f => f.PhysicalLocations)
.Distinct(StringComparer.OrdinalIgnoreCase)
.OrderBy(i => i)
.ToList();
diff --git a/MediaBrowser.Server.Implementations/Library/LibraryManager.cs b/MediaBrowser.Server.Implementations/Library/LibraryManager.cs
index 8294bfed6..813d279ab 100644
--- a/MediaBrowser.Server.Implementations/Library/LibraryManager.cs
+++ b/MediaBrowser.Server.Implementations/Library/LibraryManager.cs
@@ -833,9 +833,6 @@ namespace MediaBrowser.Server.Implementations.Library
(item as MusicArtist).IsAccessedByName = true;
}
- // Set this now so we don't cause additional file system access during provider executions
- item.ResetResolveArgs(fileInfo);
-
return new Tuple<bool, T>(isNew, item);
}
@@ -1113,6 +1110,7 @@ namespace MediaBrowser.Server.Implementations.Library
cancellationToken.ThrowIfCancellationRequested();
await userRootFolder.ValidateChildren(new Progress<double>(), cancellationToken, recursive: false).ConfigureAwait(false);
+ var b = true;
}
/// <summary>
@@ -1244,7 +1242,6 @@ namespace MediaBrowser.Server.Implementations.Library
if (dbItem != null)
{
- dbItem.ResetResolveArgs(video.ResolveArgs);
video = dbItem;
}
}
@@ -1383,6 +1380,8 @@ namespace MediaBrowser.Server.Implementations.Library
item.DateLastSaved = DateTime.UtcNow;
+ _logger.Debug("Saving {0} to database.", item.Path ?? item.Name);
+
await ItemRepository.SaveItem(item, cancellationToken).ConfigureAwait(false);
UpdateItemInLibraryCache(item);
@@ -1479,16 +1478,7 @@ namespace MediaBrowser.Server.Implementations.Library
return true;
}
- try
- {
-
- return i.PhysicalLocations.Contains(item.Path);
- }
- catch (IOException ex)
- {
- _logger.ErrorException("Error getting resolve args for {0}", ex, i.Path);
- return false;
- }
+ return i.PhysicalLocations.Contains(item.Path);
})
.Select(i => i.CollectionType)
.Where(i => !string.IsNullOrEmpty(i))
diff --git a/MediaBrowser.Server.Implementations/Library/ResolverHelper.cs b/MediaBrowser.Server.Implementations/Library/ResolverHelper.cs
index 4ce5f11d4..ad2db5abb 100644
--- a/MediaBrowser.Server.Implementations/Library/ResolverHelper.cs
+++ b/MediaBrowser.Server.Implementations/Library/ResolverHelper.cs
@@ -23,8 +23,6 @@ namespace MediaBrowser.Server.Implementations.Library
/// <param name="fileSystem">The file system.</param>
public static void SetInitialItemValues(BaseItem item, ItemResolveArgs args, IFileSystem fileSystem)
{
- item.ResetResolveArgs(args);
-
// If the resolver didn't specify this
if (string.IsNullOrEmpty(item.Path))
{
diff --git a/MediaBrowser.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs b/MediaBrowser.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs
index 3d6f7e66a..998895cbf 100644
--- a/MediaBrowser.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs
+++ b/MediaBrowser.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs
@@ -184,7 +184,7 @@ namespace MediaBrowser.Server.Implementations.Library.Resolvers.Movies
private void SetProviderIdFromPath(Video item)
{
//we need to only look at the name of this actual item (not parents)
- var justName = item.IsInMixedFolder ? Path.GetFileName(item.Path) : Path.GetFileName(item.MetaLocation);
+ var justName = item.IsInMixedFolder ? Path.GetFileName(item.Path) : Path.GetFileName(item.ContainingFolderPath);
var id = justName.GetAttributeValue("tmdbid");
diff --git a/MediaBrowser.Server.Implementations/Library/Resolvers/TV/SeasonResolver.cs b/MediaBrowser.Server.Implementations/Library/Resolvers/TV/SeasonResolver.cs
index c426fed25..70280d8d3 100644
--- a/MediaBrowser.Server.Implementations/Library/Resolvers/TV/SeasonResolver.cs
+++ b/MediaBrowser.Server.Implementations/Library/Resolvers/TV/SeasonResolver.cs
@@ -47,17 +47,5 @@ namespace MediaBrowser.Server.Implementations.Library.Resolvers.TV
return null;
}
-
- /// <summary>
- /// Sets the initial item values.
- /// </summary>
- /// <param name="item">The item.</param>
- /// <param name="args">The args.</param>
- protected override void SetInitialItemValues(Season item, ItemResolveArgs args)
- {
- base.SetInitialItemValues(item, args);
-
- Season.AddMetadataFiles(args);
- }
}
}
diff --git a/MediaBrowser.Server.Implementations/Library/Resolvers/TV/SeriesResolver.cs b/MediaBrowser.Server.Implementations/Library/Resolvers/TV/SeriesResolver.cs
index 1bd975944..625274da9 100644
--- a/MediaBrowser.Server.Implementations/Library/Resolvers/TV/SeriesResolver.cs
+++ b/MediaBrowser.Server.Implementations/Library/Resolvers/TV/SeriesResolver.cs
@@ -92,8 +92,6 @@ namespace MediaBrowser.Server.Implementations.Library.Resolvers.TV
{
base.SetInitialItemValues(item, args);
- Season.AddMetadataFiles(args);
-
SetProviderIdFromPath(item, args.Path);
}
diff --git a/MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs b/MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs
index 9501d2d12..94b6cdd9e 100644
--- a/MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs
+++ b/MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs
@@ -326,13 +326,9 @@ namespace MediaBrowser.Server.Implementations.LiveTv
item.Name = channelInfo.Name;
}
- // Set this now so we don't cause additional file system access during provider executions
- item.ResetResolveArgs(fileInfo);
-
await item.RefreshMetadata(new MetadataRefreshOptions
{
- ForceSave = isNew,
- ResetResolveArgs = false
+ ForceSave = isNew
}, cancellationToken);
@@ -391,8 +387,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv
await item.RefreshMetadata(new MetadataRefreshOptions
{
- ForceSave = isNew,
- ResetResolveArgs = false
+ ForceSave = isNew
}, cancellationToken);
@@ -448,8 +443,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv
await item.RefreshMetadata(new MetadataRefreshOptions
{
- ForceSave = isNew,
- ResetResolveArgs = false
+ ForceSave = isNew
}, cancellationToken);
diff --git a/MediaBrowser.ServerApplication/ApplicationHost.cs b/MediaBrowser.ServerApplication/ApplicationHost.cs
index 323a63674..d7858222e 100644
--- a/MediaBrowser.ServerApplication/ApplicationHost.cs
+++ b/MediaBrowser.ServerApplication/ApplicationHost.cs
@@ -501,7 +501,7 @@ namespace MediaBrowser.ServerApplication
GetExports<ILibraryPostScanTask>(),
GetExports<IPeoplePrescanTask>());
- ProviderManager.AddParts(GetExports<BaseMetadataProvider>(), GetExports<IImageProvider>(), GetExports<IMetadataService>(), GetExports<IMetadataProvider>(),
+ ProviderManager.AddParts(GetExports<IImageProvider>(), GetExports<IMetadataService>(), GetExports<IMetadataProvider>(),
GetExports<IMetadataSaver>());
ImageProcessor.AddParts(GetExports<IImageEnhancer>());
@@ -627,7 +627,7 @@ namespace MediaBrowser.ServerApplication
list.Add(typeof(IServerApplicationHost).Assembly);
// Include composable parts in the Providers assembly
- list.Add(typeof(ImagesByNameProvider).Assembly);
+ list.Add(typeof(ProviderUtils).Assembly);
// Common implementations
list.Add(typeof(TaskManager).Assembly);
diff --git a/MediaBrowser.sln b/MediaBrowser.sln
index 744debbcd..0c5360b49 100644
--- a/MediaBrowser.sln
+++ b/MediaBrowser.sln
@@ -237,4 +237,7 @@ Global
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
+ GlobalSection(Performance) = preSolution
+ HasPerformanceSessions = true
+ EndGlobalSection
EndGlobal