aboutsummaryrefslogtreecommitdiff
path: root/MediaBrowser.Server.Implementations/Library
diff options
context:
space:
mode:
authorLukePulverenti <luke.pulverenti@gmail.com>2013-03-03 01:58:04 -0500
committerLukePulverenti <luke.pulverenti@gmail.com>2013-03-03 01:58:04 -0500
commitac3a94f5a1dbb94b374e0160c344fcf99af9b696 (patch)
treef8b109c3bb5ebce964363e9df874bf3235259616 /MediaBrowser.Server.Implementations/Library
parent627b8370a89cbf9826898c2edfc46767dfb5272a (diff)
moved resolvers to implementations, trimmed nuget package a bit
Diffstat (limited to 'MediaBrowser.Server.Implementations/Library')
-rw-r--r--MediaBrowser.Server.Implementations/Library/CoreResolutionIgnoreRule.cs55
-rw-r--r--MediaBrowser.Server.Implementations/Library/LibraryManager.cs30
-rw-r--r--MediaBrowser.Server.Implementations/Library/ResolverHelper.cs82
-rw-r--r--MediaBrowser.Server.Implementations/Library/Resolvers/Audio/AudioResolver.cs80
-rw-r--r--MediaBrowser.Server.Implementations/Library/Resolvers/Audio/MusicAlbumResolver.cs92
-rw-r--r--MediaBrowser.Server.Implementations/Library/Resolvers/Audio/MusicArtistResolver.cs39
-rw-r--r--MediaBrowser.Server.Implementations/Library/Resolvers/BaseItemResolver.cs61
-rw-r--r--MediaBrowser.Server.Implementations/Library/Resolvers/FolderResolver.cs69
-rw-r--r--MediaBrowser.Server.Implementations/Library/Resolvers/LocalTrailerResolver.cs38
-rw-r--r--MediaBrowser.Server.Implementations/Library/Resolvers/Movies/BoxSetResolver.cs41
-rw-r--r--MediaBrowser.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs208
-rw-r--r--MediaBrowser.Server.Implementations/Library/Resolvers/TV/EpisodeResolver.cs76
-rw-r--r--MediaBrowser.Server.Implementations/Library/Resolvers/TV/SeasonResolver.cs45
-rw-r--r--MediaBrowser.Server.Implementations/Library/Resolvers/TV/SeriesResolver.cs98
-rw-r--r--MediaBrowser.Server.Implementations/Library/Resolvers/VideoResolver.cs71
15 files changed, 1079 insertions, 6 deletions
diff --git a/MediaBrowser.Server.Implementations/Library/CoreResolutionIgnoreRule.cs b/MediaBrowser.Server.Implementations/Library/CoreResolutionIgnoreRule.cs
new file mode 100644
index 000000000..98b8bea11
--- /dev/null
+++ b/MediaBrowser.Server.Implementations/Library/CoreResolutionIgnoreRule.cs
@@ -0,0 +1,55 @@
+using MediaBrowser.Controller.Library;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+
+namespace MediaBrowser.Server.Implementations.Library
+{
+ /// <summary>
+ /// Provides the core resolver ignore rules
+ /// </summary>
+ public class CoreResolutionIgnoreRule : IResolverIgnoreRule
+ {
+ /// <summary>
+ /// Any folder named in this list will be ignored - can be added to at runtime for extensibility
+ /// </summary>
+ private static readonly List<string> IgnoreFolders = new List<string>
+ {
+ "trailers",
+ "metadata",
+ "certificate",
+ "backup",
+ "ps3_update",
+ "ps3_vprm",
+ "adv_obj",
+ "extrafanart"
+ };
+
+ /// <summary>
+ /// Shoulds the ignore.
+ /// </summary>
+ /// <param name="args">The args.</param>
+ /// <returns><c>true</c> if XXXX, <c>false</c> otherwise</returns>
+ public bool ShouldIgnore(ItemResolveArgs args)
+ {
+ // Ignore hidden files and folders
+ if (args.IsHidden)
+ {
+ return true;
+ }
+
+ if (args.IsDirectory)
+ {
+ var filename = args.FileInfo.cFileName;
+
+ // Ignore any folders in our list
+ if (IgnoreFolders.Contains(filename, StringComparer.OrdinalIgnoreCase))
+ {
+ return true;
+ }
+ }
+
+ return false;
+ }
+ }
+}
diff --git a/MediaBrowser.Server.Implementations/Library/LibraryManager.cs b/MediaBrowser.Server.Implementations/Library/LibraryManager.cs
index ba54b5d2b..e778b35df 100644
--- a/MediaBrowser.Server.Implementations/Library/LibraryManager.cs
+++ b/MediaBrowser.Server.Implementations/Library/LibraryManager.cs
@@ -6,10 +6,10 @@ using MediaBrowser.Controller;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.IO;
using MediaBrowser.Controller.Library;
-using MediaBrowser.Controller.Resolvers;
using MediaBrowser.Controller.ScheduledTasks;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Logging;
+using MediaBrowser.Server.Implementations.Library.Resolvers;
using MoreLinq;
using System;
using System.Collections.Concurrent;
@@ -37,7 +37,7 @@ namespace MediaBrowser.Server.Implementations.Library
/// Gets the list of entity resolution ignore rules
/// </summary>
/// <value>The entity resolution ignore rules.</value>
- private IEnumerable<IResolutionIgnoreRule> EntityResolutionIgnoreRules { get; set; }
+ private IEnumerable<IResolverIgnoreRule> EntityResolutionIgnoreRules { get; set; }
/// <summary>
/// Gets the list of BasePluginFolders added by plugins
@@ -49,7 +49,7 @@ namespace MediaBrowser.Server.Implementations.Library
/// Gets the list of currently registered entity resolvers
/// </summary>
/// <value>The entity resolvers enumerable.</value>
- private IEnumerable<IBaseItemResolver> EntityResolvers { get; set; }
+ private IEnumerable<IItemResolver> EntityResolvers { get; set; }
#region LibraryChanged Event
/// <summary>
@@ -113,7 +113,7 @@ namespace MediaBrowser.Server.Implementations.Library
/// <param name="pluginFolders">The plugin folders.</param>
/// <param name="resolvers">The resolvers.</param>
/// <param name="introProviders">The intro providers.</param>
- public void AddParts(IEnumerable<IResolutionIgnoreRule> rules, IEnumerable<IVirtualFolderCreator> pluginFolders, IEnumerable<IBaseItemResolver> resolvers, IEnumerable<IIntroProvider> introProviders)
+ public void AddParts(IEnumerable<IResolverIgnoreRule> rules, IEnumerable<IVirtualFolderCreator> pluginFolders, IEnumerable<IItemResolver> resolvers, IEnumerable<IIntroProvider> introProviders)
{
EntityResolutionIgnoreRules = rules;
PluginFolderCreators = pluginFolders;
@@ -189,7 +189,14 @@ namespace MediaBrowser.Server.Implementations.Library
/// <returns>BaseItem.</returns>
public BaseItem ResolveItem(ItemResolveArgs args)
{
- return EntityResolvers.Select(r => r.ResolvePath(args)).FirstOrDefault(i => i != null);
+ var item = EntityResolvers.Select(r => r.ResolvePath(args)).FirstOrDefault(i => i != null);
+
+ if (item != null)
+ {
+ ResolverHelper.SetInitialItemValues(item, args);
+ }
+
+ return item;
}
/// <summary>
@@ -237,7 +244,7 @@ namespace MediaBrowser.Server.Implementations.Library
}
// Check to see if we should resolve based on our contents
- if (args.IsDirectory && !EntityResolutionHelper.ShouldResolvePathContents(args))
+ if (args.IsDirectory && !ShouldResolvePathContents(args))
{
return null;
}
@@ -246,6 +253,17 @@ namespace MediaBrowser.Server.Implementations.Library
}
/// <summary>
+ /// Determines whether a path should be ignored based on its contents - called after the contents have been read
+ /// </summary>
+ /// <param name="args">The args.</param>
+ /// <returns><c>true</c> if XXXX, <c>false</c> otherwise</returns>
+ private static bool ShouldResolvePathContents(ItemResolveArgs args)
+ {
+ // Ignore any folders containing a file called .ignore
+ return !args.ContainsFileSystemEntryByName(".ignore");
+ }
+
+ /// <summary>
/// Resolves a set of files into a list of BaseItem
/// </summary>
/// <typeparam name="T"></typeparam>
diff --git a/MediaBrowser.Server.Implementations/Library/ResolverHelper.cs b/MediaBrowser.Server.Implementations/Library/ResolverHelper.cs
new file mode 100644
index 000000000..aea34b0be
--- /dev/null
+++ b/MediaBrowser.Server.Implementations/Library/ResolverHelper.cs
@@ -0,0 +1,82 @@
+using MediaBrowser.Common.Extensions;
+using MediaBrowser.Controller.Entities;
+using MediaBrowser.Controller.Library;
+using System.IO;
+using System.Text.RegularExpressions;
+
+namespace MediaBrowser.Server.Implementations.Library
+{
+ /// <summary>
+ /// Class ResolverHelper
+ /// </summary>
+ public static class ResolverHelper
+ {
+ /// <summary>
+ /// Sets the initial item values.
+ /// </summary>
+ /// <param name="item">The item.</param>
+ /// <param name="args">The args.</param>
+ public static void SetInitialItemValues(BaseItem item, ItemResolveArgs args)
+ {
+ item.ResolveArgs = args;
+
+ // If the resolver didn't specify this
+ if (string.IsNullOrEmpty(item.Path))
+ {
+ item.Path = args.Path;
+ }
+
+ // If the resolver didn't specify this
+ if (args.Parent != null)
+ {
+ item.Parent = args.Parent;
+ }
+
+ item.Id = item.Path.GetMBId(item.GetType());
+ item.DisplayMediaType = item.GetType().Name;
+
+ // Make sure the item has a name
+ EnsureName(item);
+
+ // Make sure DateCreated and DateModified have values
+ EntityResolutionHelper.EnsureDates(item, args);
+ }
+
+ /// <summary>
+ /// Ensures the name.
+ /// </summary>
+ /// <param name="item">The item.</param>
+ private static void EnsureName(BaseItem item)
+ {
+ // If the subclass didn't supply a name, add it here
+ if (string.IsNullOrEmpty(item.Name) && !string.IsNullOrEmpty(item.Path))
+ {
+ //we use our resolve args name here to get the name of the containg folder, not actual video file
+ item.Name = GetMBName(item.ResolveArgs.FileInfo.cFileName, item.ResolveArgs.FileInfo.IsDirectory);
+ }
+ }
+
+ /// <summary>
+ /// The MB name regex
+ /// </summary>
+ private static readonly Regex MBNameRegex = new Regex("(\\[.*\\])", RegexOptions.Compiled);
+
+ /// <summary>
+ /// Strip out attribute items and return just the name we will use for items
+ /// </summary>
+ /// <param name="path">Assumed to be a file or directory path</param>
+ /// <param name="isDirectory">if set to <c>true</c> [is directory].</param>
+ /// <returns>The cleaned name</returns>
+ private static string GetMBName(string path, bool isDirectory)
+ {
+ //first just get the file or directory name
+ var fn = isDirectory ? Path.GetFileName(path) : Path.GetFileNameWithoutExtension(path);
+
+ //now - strip out anything inside brackets
+ fn = MBNameRegex.Replace(fn, string.Empty);
+
+ return fn;
+ }
+
+ }
+}
diff --git a/MediaBrowser.Server.Implementations/Library/Resolvers/Audio/AudioResolver.cs b/MediaBrowser.Server.Implementations/Library/Resolvers/Audio/AudioResolver.cs
new file mode 100644
index 000000000..f8e0acb5a
--- /dev/null
+++ b/MediaBrowser.Server.Implementations/Library/Resolvers/Audio/AudioResolver.cs
@@ -0,0 +1,80 @@
+using MediaBrowser.Controller.IO;
+using MediaBrowser.Controller.Library;
+using System;
+using System.IO;
+using System.Linq;
+
+namespace MediaBrowser.Server.Implementations.Library.Resolvers.Audio
+{
+ /// <summary>
+ /// Class AudioResolver
+ /// </summary>
+ public class AudioResolver : ItemResolver<Controller.Entities.Audio.Audio>
+ {
+ /// <summary>
+ /// Gets the priority.
+ /// </summary>
+ /// <value>The priority.</value>
+ public override ResolverPriority Priority
+ {
+ get { return ResolverPriority.Last; }
+ }
+
+ /// <summary>
+ /// Resolves the specified args.
+ /// </summary>
+ /// <param name="args">The args.</param>
+ /// <returns>Entities.Audio.Audio.</returns>
+ protected override Controller.Entities.Audio.Audio Resolve(ItemResolveArgs args)
+ {
+ // Return audio if the path is a file and has a matching extension
+
+ if (!args.IsDirectory)
+ {
+ if (IsAudioFile(args))
+ {
+ return new Controller.Entities.Audio.Audio();
+ }
+ }
+
+ return null;
+ }
+
+ /// <summary>
+ /// The audio file extensions
+ /// </summary>
+ public static readonly string[] AudioFileExtensions = new[] {
+ ".mp3",
+ ".flac",
+ ".wma",
+ ".aac",
+ ".acc",
+ ".m4a",
+ ".m4b",
+ ".wav",
+ ".ape",
+ ".ogg",
+ ".oga"
+ };
+
+ /// <summary>
+ /// Determines whether [is audio file] [the specified args].
+ /// </summary>
+ /// <param name="args">The args.</param>
+ /// <returns><c>true</c> if [is audio file] [the specified args]; otherwise, <c>false</c>.</returns>
+ public static bool IsAudioFile(ItemResolveArgs args)
+ {
+ return AudioFileExtensions.Contains(Path.GetExtension(args.Path), StringComparer.OrdinalIgnoreCase);
+ }
+
+ /// <summary>
+ /// Determines whether [is audio file] [the specified file].
+ /// </summary>
+ /// <param name="file">The file.</param>
+ /// <returns><c>true</c> if [is audio file] [the specified file]; otherwise, <c>false</c>.</returns>
+ public static bool IsAudioFile(WIN32_FIND_DATA file)
+ {
+ return AudioFileExtensions.Contains(Path.GetExtension(file.Path), StringComparer.OrdinalIgnoreCase);
+ }
+ }
+}
diff --git a/MediaBrowser.Server.Implementations/Library/Resolvers/Audio/MusicAlbumResolver.cs b/MediaBrowser.Server.Implementations/Library/Resolvers/Audio/MusicAlbumResolver.cs
new file mode 100644
index 000000000..31016e2fc
--- /dev/null
+++ b/MediaBrowser.Server.Implementations/Library/Resolvers/Audio/MusicAlbumResolver.cs
@@ -0,0 +1,92 @@
+using System.Collections.Generic;
+using MediaBrowser.Controller.Entities.Audio;
+using MediaBrowser.Controller.IO;
+using MediaBrowser.Controller.Library;
+
+namespace MediaBrowser.Server.Implementations.Library.Resolvers.Audio
+{
+ /// <summary>
+ /// Class MusicAlbumResolver
+ /// </summary>
+ public class MusicAlbumResolver : ItemResolver<MusicAlbum>
+ {
+ /// <summary>
+ /// Gets the priority.
+ /// </summary>
+ /// <value>The priority.</value>
+ public override ResolverPriority Priority
+ {
+ get { return ResolverPriority.Third; } // we need to be ahead of the generic folder resolver but behind the movie one
+ }
+
+ /// <summary>
+ /// Resolves the specified args.
+ /// </summary>
+ /// <param name="args">The args.</param>
+ /// <returns>MusicAlbum.</returns>
+ protected override MusicAlbum Resolve(ItemResolveArgs args)
+ {
+ if (!args.IsDirectory) return null;
+
+ //Avoid mis-identifying top folders
+ if (args.Parent == null) return null;
+ if (args.Parent.IsRoot) return null;
+
+ return IsMusicAlbum(args) ? new MusicAlbum() : null;
+ }
+
+
+ /// <summary>
+ /// Determine if the supplied file data points to a music album
+ /// </summary>
+ /// <param name="data">The data.</param>
+ /// <returns><c>true</c> if [is music album] [the specified data]; otherwise, <c>false</c>.</returns>
+ public static bool IsMusicAlbum(WIN32_FIND_DATA data)
+ {
+ return ContainsMusic(FileSystem.GetFiles(data.Path));
+ }
+
+ /// <summary>
+ /// Determine if the supplied reslove args should be considered a music album
+ /// </summary>
+ /// <param name="args">The args.</param>
+ /// <returns><c>true</c> if [is music album] [the specified args]; otherwise, <c>false</c>.</returns>
+ public static bool IsMusicAlbum(ItemResolveArgs args)
+ {
+ // Args points to an album if parent is an Artist folder or it directly contains music
+ if (args.IsDirectory)
+ {
+ //if (args.Parent is MusicArtist) return true; //saves us from testing children twice
+ if (ContainsMusic(args.FileSystemChildren)) return true;
+ }
+
+
+ return false;
+ }
+
+ /// <summary>
+ /// Determine if the supplied list contains what we should consider music
+ /// </summary>
+ /// <param name="list">The list.</param>
+ /// <returns><c>true</c> if the specified list contains music; otherwise, <c>false</c>.</returns>
+ public static bool ContainsMusic(IEnumerable<WIN32_FIND_DATA> list)
+ {
+ // If list contains at least 2 audio files or at least one and no video files consider it to contain music
+ var foundAudio = 0;
+ var foundVideo = 0;
+ foreach (var file in list)
+ {
+ if (AudioResolver.IsAudioFile(file)) foundAudio++;
+ if (foundAudio >= 2)
+ {
+ return true;
+ }
+ if (EntityResolutionHelper.IsVideoFile(file.Path)) foundVideo++;
+ }
+
+ // or a single audio file and no video files
+ if (foundAudio > 0 && foundVideo == 0) return true;
+ return false;
+ }
+ }
+}
diff --git a/MediaBrowser.Server.Implementations/Library/Resolvers/Audio/MusicArtistResolver.cs b/MediaBrowser.Server.Implementations/Library/Resolvers/Audio/MusicArtistResolver.cs
new file mode 100644
index 000000000..e48ad96c6
--- /dev/null
+++ b/MediaBrowser.Server.Implementations/Library/Resolvers/Audio/MusicArtistResolver.cs
@@ -0,0 +1,39 @@
+using MediaBrowser.Controller.Entities.Audio;
+using MediaBrowser.Controller.Library;
+using System.Linq;
+
+namespace MediaBrowser.Server.Implementations.Library.Resolvers.Audio
+{
+ /// <summary>
+ /// Class MusicArtistResolver
+ /// </summary>
+ public class MusicArtistResolver : ItemResolver<MusicArtist>
+ {
+ /// <summary>
+ /// Gets the priority.
+ /// </summary>
+ /// <value>The priority.</value>
+ public override ResolverPriority Priority
+ {
+ get { return ResolverPriority.Third; } // we need to be ahead of the generic folder resolver but behind the movie one
+ }
+
+ /// <summary>
+ /// Resolves the specified args.
+ /// </summary>
+ /// <param name="args">The args.</param>
+ /// <returns>MusicArtist.</returns>
+ protected override MusicArtist Resolve(ItemResolveArgs args)
+ {
+ if (!args.IsDirectory) return null;
+
+ //Avoid mis-identifying top folders
+ if (args.Parent == null) return null;
+ if (args.Parent.IsRoot) return null;
+
+ // If we contain an album assume we are an artist folder
+ return args.FileSystemChildren.Any(MusicAlbumResolver.IsMusicAlbum) ? new MusicArtist() : null;
+ }
+
+ }
+}
diff --git a/MediaBrowser.Server.Implementations/Library/Resolvers/BaseItemResolver.cs b/MediaBrowser.Server.Implementations/Library/Resolvers/BaseItemResolver.cs
new file mode 100644
index 000000000..a80a48290
--- /dev/null
+++ b/MediaBrowser.Server.Implementations/Library/Resolvers/BaseItemResolver.cs
@@ -0,0 +1,61 @@
+using MediaBrowser.Controller.Entities;
+using MediaBrowser.Controller.Library;
+
+namespace MediaBrowser.Server.Implementations.Library.Resolvers
+{
+ /// <summary>
+ /// Class ItemResolver
+ /// </summary>
+ /// <typeparam name="T"></typeparam>
+ public abstract class ItemResolver<T> : IItemResolver
+ where T : BaseItem, new()
+ {
+ /// <summary>
+ /// Resolves the specified args.
+ /// </summary>
+ /// <param name="args">The args.</param>
+ /// <returns>`0.</returns>
+ protected virtual T Resolve(ItemResolveArgs args)
+ {
+ return null;
+ }
+
+ /// <summary>
+ /// Gets the priority.
+ /// </summary>
+ /// <value>The priority.</value>
+ public virtual ResolverPriority Priority
+ {
+ get
+ {
+ return ResolverPriority.First;
+ }
+ }
+
+ /// <summary>
+ /// Sets initial values on the newly resolved item
+ /// </summary>
+ /// <param name="item">The item.</param>
+ /// <param name="args">The args.</param>
+ protected virtual void SetInitialItemValues(T item, ItemResolveArgs args)
+ {
+ }
+
+ /// <summary>
+ /// Resolves the path.
+ /// </summary>
+ /// <param name="args">The args.</param>
+ /// <returns>BaseItem.</returns>
+ BaseItem IItemResolver.ResolvePath(ItemResolveArgs args)
+ {
+ var item = Resolve(args);
+
+ if (item != null)
+ {
+ SetInitialItemValues(item, args);
+ }
+
+ return item;
+ }
+ }
+}
diff --git a/MediaBrowser.Server.Implementations/Library/Resolvers/FolderResolver.cs b/MediaBrowser.Server.Implementations/Library/Resolvers/FolderResolver.cs
new file mode 100644
index 000000000..c680af7d6
--- /dev/null
+++ b/MediaBrowser.Server.Implementations/Library/Resolvers/FolderResolver.cs
@@ -0,0 +1,69 @@
+using MediaBrowser.Controller.Entities;
+using MediaBrowser.Controller.Library;
+
+namespace MediaBrowser.Server.Implementations.Library.Resolvers
+{
+ /// <summary>
+ /// Class FolderResolver
+ /// </summary>
+ public class FolderResolver : FolderResolver<Folder>
+ {
+ /// <summary>
+ /// Gets the priority.
+ /// </summary>
+ /// <value>The priority.</value>
+ public override ResolverPriority Priority
+ {
+ get { return ResolverPriority.Last; }
+ }
+
+ /// <summary>
+ /// Resolves the specified args.
+ /// </summary>
+ /// <param name="args">The args.</param>
+ /// <returns>Folder.</returns>
+ protected override Folder Resolve(ItemResolveArgs args)
+ {
+ if (args.IsDirectory)
+ {
+ if (args.IsPhysicalRoot)
+ {
+ return new AggregateFolder();
+ }
+ if (args.IsRoot)
+ {
+ return new UserRootFolder(); //if we got here and still a root - must be user root
+ }
+ if (args.IsVf)
+ {
+ return new CollectionFolder();
+ }
+
+ return new Folder();
+ }
+
+ return null;
+ }
+ }
+
+ /// <summary>
+ /// Class FolderResolver
+ /// </summary>
+ /// <typeparam name="TItemType">The type of the T item type.</typeparam>
+ public abstract class FolderResolver<TItemType> : ItemResolver<TItemType>
+ where TItemType : Folder, new()
+ {
+ /// <summary>
+ /// Sets the initial item values.
+ /// </summary>
+ /// <param name="item">The item.</param>
+ /// <param name="args">The args.</param>
+ protected override void SetInitialItemValues(TItemType item, ItemResolveArgs args)
+ {
+ base.SetInitialItemValues(item, args);
+
+ item.IsRoot = args.Parent == null;
+ item.IsPhysicalRoot = args.IsPhysicalRoot;
+ }
+ }
+}
diff --git a/MediaBrowser.Server.Implementations/Library/Resolvers/LocalTrailerResolver.cs b/MediaBrowser.Server.Implementations/Library/Resolvers/LocalTrailerResolver.cs
new file mode 100644
index 000000000..d0a0cd5c7
--- /dev/null
+++ b/MediaBrowser.Server.Implementations/Library/Resolvers/LocalTrailerResolver.cs
@@ -0,0 +1,38 @@
+using MediaBrowser.Controller.Entities;
+using MediaBrowser.Controller.Library;
+using System;
+using System.IO;
+
+namespace MediaBrowser.Server.Implementations.Library.Resolvers
+{
+ /// <summary>
+ /// Class LocalTrailerResolver
+ /// </summary>
+ public class LocalTrailerResolver : VideoResolver<Trailer>
+ {
+ /// <summary>
+ /// Resolves the specified args.
+ /// </summary>
+ /// <param name="args">The args.</param>
+ /// <returns>Trailer.</returns>
+ protected override Trailer Resolve(ItemResolveArgs args)
+ {
+ // Trailers are not Children, therefore this can never happen
+ if (args.Parent != null)
+ {
+ return null;
+ }
+
+ // If the file is within a trailers folder, see if the VideoResolver returns something
+ if (!args.IsDirectory)
+ {
+ if (string.Equals(Path.GetFileName(Path.GetDirectoryName(args.Path)), BaseItem.TrailerFolderName, StringComparison.OrdinalIgnoreCase))
+ {
+ return base.Resolve(args);
+ }
+ }
+
+ return null;
+ }
+ }
+}
diff --git a/MediaBrowser.Server.Implementations/Library/Resolvers/Movies/BoxSetResolver.cs b/MediaBrowser.Server.Implementations/Library/Resolvers/Movies/BoxSetResolver.cs
new file mode 100644
index 000000000..2f810d2a1
--- /dev/null
+++ b/MediaBrowser.Server.Implementations/Library/Resolvers/Movies/BoxSetResolver.cs
@@ -0,0 +1,41 @@
+using MediaBrowser.Controller.Entities.Movies;
+using MediaBrowser.Controller.Library;
+using System;
+using System.IO;
+
+namespace MediaBrowser.Server.Implementations.Library.Resolvers.Movies
+{
+ /// <summary>
+ /// Class BoxSetResolver
+ /// </summary>
+ public class BoxSetResolver : FolderResolver<BoxSet>
+ {
+ /// <summary>
+ /// Resolves the specified args.
+ /// </summary>
+ /// <param name="args">The args.</param>
+ /// <returns>BoxSet.</returns>
+ protected override BoxSet Resolve(ItemResolveArgs args)
+ {
+ // It's a boxset if all of the following conditions are met:
+ // Is a Directory
+ // Contains [boxset] in the path
+ if (args.IsDirectory)
+ {
+ var filename = Path.GetFileName(args.Path);
+
+ if (string.IsNullOrEmpty(filename))
+ {
+ return null;
+ }
+
+ if (filename.IndexOf("[boxset]", StringComparison.OrdinalIgnoreCase) != -1)
+ {
+ return new BoxSet();
+ }
+ }
+
+ return null;
+ }
+ }
+}
diff --git a/MediaBrowser.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs b/MediaBrowser.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs
new file mode 100644
index 000000000..601ab6661
--- /dev/null
+++ b/MediaBrowser.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs
@@ -0,0 +1,208 @@
+using MediaBrowser.Common.Extensions;
+using MediaBrowser.Controller.Entities;
+using MediaBrowser.Controller.Entities.Movies;
+using MediaBrowser.Controller.Library;
+using MediaBrowser.Controller.Providers.Movies;
+using MediaBrowser.Model.Entities;
+using System;
+using System.Collections.Generic;
+using System.IO;
+
+namespace MediaBrowser.Server.Implementations.Library.Resolvers.Movies
+{
+ /// <summary>
+ /// Class MovieResolver
+ /// </summary>
+ public class MovieResolver : VideoResolver<Movie>
+ {
+ /// <summary>
+ /// Gets the priority.
+ /// </summary>
+ /// <value>The priority.</value>
+ public override ResolverPriority Priority
+ {
+ get
+ {
+ // Give plugins a chance to catch iso's first
+ // Also since we have to loop through child files looking for videos,
+ // see if we can avoid some of that by letting other resolvers claim folders first
+ return ResolverPriority.Second;
+ }
+ }
+
+ /// <summary>
+ /// Resolves the specified args.
+ /// </summary>
+ /// <param name="args">The args.</param>
+ /// <returns>Movie.</returns>
+ protected override Movie Resolve(ItemResolveArgs args)
+ {
+ // Must be a directory and under a 'Movies' VF
+ if (args.IsDirectory)
+ {
+ // Avoid expensive tests against VF's and all their children by not allowing this
+ if (args.Parent == null || args.Parent.IsRoot)
+ {
+ return null;
+ }
+
+ // If the parent is not a boxset, the only other allowed parent type is Folder
+ if (!(args.Parent is BoxSet))
+ {
+ if (args.Parent.GetType() != typeof(Folder))
+ {
+ return null;
+ }
+ }
+
+ // Optimization to avoid running all these tests against Top folders
+ if (args.Parent != null && args.Parent.IsRoot)
+ {
+ return null;
+ }
+
+ // The movie must be a video file
+ return FindMovie(args);
+ }
+
+ 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(Movie item, ItemResolveArgs args)
+ {
+ base.SetInitialItemValues(item, args);
+
+ SetProviderIdFromPath(item);
+ }
+
+ /// <summary>
+ /// Sets the provider id from path.
+ /// </summary>
+ /// <param name="item">The item.</param>
+ private void SetProviderIdFromPath(Movie item)
+ {
+ //we need to only look at the name of this actual item (not parents)
+ var justName = Path.GetFileName(item.Path);
+
+ var id = justName.GetAttributeValue("tmdbid");
+
+ if (!string.IsNullOrEmpty(id))
+ {
+ item.SetProviderId(MetadataProviders.Tmdb, id);
+ }
+ }
+
+ /// <summary>
+ /// Finds a movie based on a child file system entries
+ /// </summary>
+ /// <param name="args">The args.</param>
+ /// <returns>Movie.</returns>
+ private Movie FindMovie(ItemResolveArgs args)
+ {
+ // Since the looping is expensive, this is an optimization to help us avoid it
+ if (args.ContainsMetaFileByName("series.xml") || args.Path.IndexOf("[tvdbid", StringComparison.OrdinalIgnoreCase) != -1)
+ {
+ return null;
+ }
+
+ // Optimization to avoid having to resolve every file
+ bool? isKnownMovie = null;
+
+ var movies = new List<Movie>();
+
+ // Loop through each child file/folder and see if we find a video
+ foreach (var child in args.FileSystemChildren)
+ {
+ if (child.IsDirectory)
+ {
+ if (IsDvdDirectory(child.cFileName))
+ {
+ return new Movie
+ {
+ Path = args.Path,
+ VideoType = VideoType.Dvd
+ };
+ }
+ if (IsBluRayDirectory(child.cFileName))
+ {
+ return new Movie
+ {
+ Path = args.Path,
+ VideoType = VideoType.BluRay
+ };
+ }
+ if (IsHdDvdDirectory(child.cFileName))
+ {
+ return new Movie
+ {
+ Path = args.Path,
+ VideoType = VideoType.HdDvd
+ };
+ }
+
+ continue;
+ }
+
+ var childArgs = new ItemResolveArgs
+ {
+ FileInfo = child,
+ Path = child.Path
+ };
+
+ var item = base.Resolve(childArgs);
+
+ if (item != null)
+ {
+ // If we already know it's a movie, we can stop looping
+ if (!isKnownMovie.HasValue)
+ {
+ isKnownMovie = args.ContainsMetaFileByName("movie.xml") || args.ContainsMetaFileByName(MovieDbProvider.LOCAL_META_FILE_NAME) || args.Path.IndexOf("[tmdbid", StringComparison.OrdinalIgnoreCase) != -1;
+ }
+
+ if (isKnownMovie.Value)
+ {
+ return item;
+ }
+
+ movies.Add(item);
+ }
+ }
+
+ // If there are multiple video files, return null, and let the VideoResolver catch them later as plain videos
+ return movies.Count == 1 ? movies[0] : null;
+ }
+
+ /// <summary>
+ /// Determines whether [is DVD directory] [the specified directory name].
+ /// </summary>
+ /// <param name="directoryName">Name of the directory.</param>
+ /// <returns><c>true</c> if [is DVD directory] [the specified directory name]; otherwise, <c>false</c>.</returns>
+ private bool IsDvdDirectory(string directoryName)
+ {
+ return directoryName.Equals("video_ts", StringComparison.OrdinalIgnoreCase);
+ }
+ /// <summary>
+ /// Determines whether [is hd DVD directory] [the specified directory name].
+ /// </summary>
+ /// <param name="directoryName">Name of the directory.</param>
+ /// <returns><c>true</c> if [is hd DVD directory] [the specified directory name]; otherwise, <c>false</c>.</returns>
+ private bool IsHdDvdDirectory(string directoryName)
+ {
+ return directoryName.Equals("hvdvd_ts", StringComparison.OrdinalIgnoreCase);
+ }
+ /// <summary>
+ /// Determines whether [is blu ray directory] [the specified directory name].
+ /// </summary>
+ /// <param name="directoryName">Name of the directory.</param>
+ /// <returns><c>true</c> if [is blu ray directory] [the specified directory name]; otherwise, <c>false</c>.</returns>
+ private bool IsBluRayDirectory(string directoryName)
+ {
+ return directoryName.Equals("bdmv", StringComparison.OrdinalIgnoreCase);
+ }
+ }
+}
diff --git a/MediaBrowser.Server.Implementations/Library/Resolvers/TV/EpisodeResolver.cs b/MediaBrowser.Server.Implementations/Library/Resolvers/TV/EpisodeResolver.cs
new file mode 100644
index 000000000..6fa9711cc
--- /dev/null
+++ b/MediaBrowser.Server.Implementations/Library/Resolvers/TV/EpisodeResolver.cs
@@ -0,0 +1,76 @@
+using MediaBrowser.Controller.Entities.TV;
+using MediaBrowser.Controller.Library;
+using MediaBrowser.Model.Entities;
+using System;
+
+namespace MediaBrowser.Server.Implementations.Library.Resolvers.TV
+{
+ /// <summary>
+ /// Class EpisodeResolver
+ /// </summary>
+ public class EpisodeResolver : VideoResolver<Episode>
+ {
+ /// <summary>
+ /// Resolves the specified args.
+ /// </summary>
+ /// <param name="args">The args.</param>
+ /// <returns>Episode.</returns>
+ protected override Episode Resolve(ItemResolveArgs args)
+ {
+ // If the parent is a Season or Series, then this is an Episode if the VideoResolver returns something
+ if (args.Parent is Season || args.Parent is Series)
+ {
+ if (args.IsDirectory)
+ {
+ if (args.ContainsFileSystemEntryByName("video_ts"))
+ {
+ return new Episode
+ {
+ Path = args.Path,
+ VideoType = VideoType.Dvd
+ };
+ }
+ if (args.ContainsFileSystemEntryByName("bdmv"))
+ {
+ return new Episode
+ {
+ Path = args.Path,
+ VideoType = VideoType.BluRay
+ };
+ }
+ }
+
+ return base.Resolve(args);
+ }
+
+ 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(Episode item, ItemResolveArgs args)
+ {
+ base.SetInitialItemValues(item, args);
+
+ //fill in our season and series ids
+ var season = args.Parent as Season;
+ if (season != null)
+ {
+ item.SeasonItemId = season.Id;
+ var series = season.Parent as Series;
+ if (series != null)
+ {
+ item.SeriesItemId = series.Id;
+ }
+ }
+ else
+ {
+ var series = args.Parent as Series;
+ item.SeriesItemId = series != null ? series.Id : Guid.Empty;
+ }
+ }
+ }
+}
diff --git a/MediaBrowser.Server.Implementations/Library/Resolvers/TV/SeasonResolver.cs b/MediaBrowser.Server.Implementations/Library/Resolvers/TV/SeasonResolver.cs
new file mode 100644
index 000000000..07fb2f486
--- /dev/null
+++ b/MediaBrowser.Server.Implementations/Library/Resolvers/TV/SeasonResolver.cs
@@ -0,0 +1,45 @@
+using MediaBrowser.Controller.Entities.TV;
+using MediaBrowser.Controller.Library;
+using System;
+
+namespace MediaBrowser.Server.Implementations.Library.Resolvers.TV
+{
+ /// <summary>
+ /// Class SeasonResolver
+ /// </summary>
+ public class SeasonResolver : FolderResolver<Season>
+ {
+ /// <summary>
+ /// Resolves the specified args.
+ /// </summary>
+ /// <param name="args">The args.</param>
+ /// <returns>Season.</returns>
+ protected override Season Resolve(ItemResolveArgs args)
+ {
+ if (args.Parent is Series && args.IsDirectory)
+ {
+ return new Season
+ {
+ IndexNumber = TVUtils.GetSeasonNumberFromPath(args.Path)
+ };
+ }
+
+ 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);
+
+ var series = args.Parent as Series;
+ item.SeriesItemId = series != null ? series.Id : Guid.Empty;
+
+ Season.AddMetadataFiles(args);
+ }
+ }
+}
diff --git a/MediaBrowser.Server.Implementations/Library/Resolvers/TV/SeriesResolver.cs b/MediaBrowser.Server.Implementations/Library/Resolvers/TV/SeriesResolver.cs
new file mode 100644
index 000000000..642e52363
--- /dev/null
+++ b/MediaBrowser.Server.Implementations/Library/Resolvers/TV/SeriesResolver.cs
@@ -0,0 +1,98 @@
+using MediaBrowser.Common.Extensions;
+using MediaBrowser.Controller.Entities.TV;
+using MediaBrowser.Controller.Library;
+using MediaBrowser.Model.Entities;
+using System;
+using System.IO;
+
+namespace MediaBrowser.Server.Implementations.Library.Resolvers.TV
+{
+ /// <summary>
+ /// Class SeriesResolver
+ /// </summary>
+ public class SeriesResolver : FolderResolver<Series>
+ {
+ /// <summary>
+ /// Gets the priority.
+ /// </summary>
+ /// <value>The priority.</value>
+ public override ResolverPriority Priority
+ {
+ get
+ {
+ return ResolverPriority.Second;
+ }
+ }
+
+ /// <summary>
+ /// Resolves the specified args.
+ /// </summary>
+ /// <param name="args">The args.</param>
+ /// <returns>Series.</returns>
+ protected override Series Resolve(ItemResolveArgs args)
+ {
+ if (args.IsDirectory)
+ {
+ // Avoid expensive tests against VF's and all their children by not allowing this
+ if (args.Parent == null || args.Parent.IsRoot)
+ {
+ return null;
+ }
+
+ // Optimization to avoid running these tests against Seasons
+ if (args.Parent is Series)
+ {
+ return null;
+ }
+
+ // It's a Series if any of the following conditions are met:
+ // series.xml exists
+ // [tvdbid= is present in the path
+ // TVUtils.IsSeriesFolder returns true
+ var filename = Path.GetFileName(args.Path);
+
+ if (string.IsNullOrEmpty(filename))
+ {
+ return null;
+ }
+
+ if (args.ContainsMetaFileByName("series.xml") || filename.IndexOf("[tvdbid=", StringComparison.OrdinalIgnoreCase) != -1 || TVUtils.IsSeriesFolder(args.Path, args.FileSystemChildren))
+ {
+ return new Series();
+ }
+ }
+
+ 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(Series item, ItemResolveArgs args)
+ {
+ base.SetInitialItemValues(item, args);
+
+ Season.AddMetadataFiles(args);
+
+ SetProviderIdFromPath(item, args.Path);
+ }
+
+ /// <summary>
+ /// Sets the provider id from path.
+ /// </summary>
+ /// <param name="item">The item.</param>
+ private void SetProviderIdFromPath(Series item, string path)
+ {
+ var justName = Path.GetFileName(path);
+
+ var id = justName.GetAttributeValue("tvdbid");
+
+ if (!string.IsNullOrEmpty(id))
+ {
+ item.SetProviderId(MetadataProviders.Tvdb, id);
+ }
+ }
+ }
+}
diff --git a/MediaBrowser.Server.Implementations/Library/Resolvers/VideoResolver.cs b/MediaBrowser.Server.Implementations/Library/Resolvers/VideoResolver.cs
new file mode 100644
index 000000000..5a47f38cd
--- /dev/null
+++ b/MediaBrowser.Server.Implementations/Library/Resolvers/VideoResolver.cs
@@ -0,0 +1,71 @@
+using MediaBrowser.Controller.Entities;
+using MediaBrowser.Controller.Library;
+using MediaBrowser.Model.Entities;
+using System;
+using System.IO;
+
+namespace MediaBrowser.Server.Implementations.Library.Resolvers
+{
+ /// <summary>
+ /// Resolves a Path into a Video
+ /// </summary>
+ public class VideoResolver : VideoResolver<Video>
+ {
+ /// <summary>
+ /// Gets the priority.
+ /// </summary>
+ /// <value>The priority.</value>
+ public override ResolverPriority Priority
+ {
+ get { return ResolverPriority.Last; }
+ }
+ }
+
+ /// <summary>
+ /// Resolves a Path into a Video or Video subclass
+ /// </summary>
+ /// <typeparam name="T"></typeparam>
+ public abstract class VideoResolver<T> : ItemResolver<T>
+ where T : Video, new()
+ {
+ /// <summary>
+ /// Resolves the specified args.
+ /// </summary>
+ /// <param name="args">The args.</param>
+ /// <returns>`0.</returns>
+ protected override T Resolve(ItemResolveArgs args)
+ {
+ // If the path is a file check for a matching extensions
+ if (!args.IsDirectory)
+ {
+ if (EntityResolutionHelper.IsVideoFile(args.Path))
+ {
+ var extension = Path.GetExtension(args.Path);
+
+ var type = string.Equals(extension, ".iso", StringComparison.OrdinalIgnoreCase) || string.Equals(extension, ".img", StringComparison.OrdinalIgnoreCase) ?
+ VideoType.Iso : VideoType.VideoFile;
+
+ return new T
+ {
+ VideoType = type,
+ Path = args.Path
+ };
+ }
+ }
+
+ 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(T item, ItemResolveArgs args)
+ {
+ base.SetInitialItemValues(item, args);
+
+ item.VideoFormat = item.Path.IndexOf("[3d]", StringComparison.OrdinalIgnoreCase) != -1 ? VideoFormat.Digital3D : item.Path.IndexOf("[sbs3d]", StringComparison.OrdinalIgnoreCase) != -1 ? VideoFormat.Sbs3D : VideoFormat.Standard;
+ }
+ }
+}