aboutsummaryrefslogtreecommitdiff
path: root/MediaBrowser.Controller/Resolvers
diff options
context:
space:
mode:
authorLukePulverenti <luke.pulverenti@gmail.com>2013-02-20 20:33:05 -0500
committerLukePulverenti <luke.pulverenti@gmail.com>2013-02-20 20:33:05 -0500
commit767cdc1f6f6a63ce997fc9476911e2c361f9d402 (patch)
tree49add55976f895441167c66cfa95e5c7688d18ce /MediaBrowser.Controller/Resolvers
parent845554722efaed872948a9e0f7202e3ef52f1b6e (diff)
Pushing missing changes
Diffstat (limited to 'MediaBrowser.Controller/Resolvers')
-rw-r--r--MediaBrowser.Controller/Resolvers/Audio/AudioResolver.cs29
-rw-r--r--MediaBrowser.Controller/Resolvers/Audio/MusicAlbumResolver.cs27
-rw-r--r--MediaBrowser.Controller/Resolvers/Audio/MusicArtistResolver.cs29
-rw-r--r--MediaBrowser.Controller/Resolvers/AudioResolver.cs54
-rw-r--r--MediaBrowser.Controller/Resolvers/BaseItemResolver.cs291
-rw-r--r--MediaBrowser.Controller/Resolvers/BaseResolutionIgnoreRule.cs12
-rw-r--r--MediaBrowser.Controller/Resolvers/CoreResolutionIgnoreRule.cs52
-rw-r--r--MediaBrowser.Controller/Resolvers/EntityResolutionHelper.cs281
-rw-r--r--MediaBrowser.Controller/Resolvers/FolderResolver.cs107
-rw-r--r--MediaBrowser.Controller/Resolvers/LocalTrailerResolver.cs40
-rw-r--r--MediaBrowser.Controller/Resolvers/Movies/BoxSetResolver.cs71
-rw-r--r--MediaBrowser.Controller/Resolvers/Movies/MovieResolver.cs326
-rw-r--r--MediaBrowser.Controller/Resolvers/TV/EpisodeResolver.cs86
-rw-r--r--MediaBrowser.Controller/Resolvers/TV/SeasonResolver.cs59
-rw-r--r--MediaBrowser.Controller/Resolvers/TV/SeriesResolver.cs164
-rw-r--r--MediaBrowser.Controller/Resolvers/TV/TVUtils.cs415
-rw-r--r--MediaBrowser.Controller/Resolvers/VideoResolver.cs173
17 files changed, 1412 insertions, 804 deletions
diff --git a/MediaBrowser.Controller/Resolvers/Audio/AudioResolver.cs b/MediaBrowser.Controller/Resolvers/Audio/AudioResolver.cs
new file mode 100644
index 0000000000..f827bf0477
--- /dev/null
+++ b/MediaBrowser.Controller/Resolvers/Audio/AudioResolver.cs
@@ -0,0 +1,29 @@
+using MediaBrowser.Controller.Library;
+using System.ComponentModel.Composition;
+
+namespace MediaBrowser.Controller.Resolvers.Audio
+{
+ [Export(typeof(IBaseItemResolver))]
+ public class AudioResolver : BaseItemResolver<Entities.Audio.Audio>
+ {
+ public override ResolverPriority Priority
+ {
+ get { return ResolverPriority.Last; }
+ }
+
+ protected override Entities.Audio.Audio Resolve(ItemResolveArgs args)
+ {
+ // Return audio if the path is a file and has a matching extension
+
+ if (!args.IsDirectory)
+ {
+ if (EntityResolutionHelper.IsAudioFile(args))
+ {
+ return new Entities.Audio.Audio();
+ }
+ }
+
+ return null;
+ }
+ }
+}
diff --git a/MediaBrowser.Controller/Resolvers/Audio/MusicAlbumResolver.cs b/MediaBrowser.Controller/Resolvers/Audio/MusicAlbumResolver.cs
new file mode 100644
index 0000000000..8b2e49f313
--- /dev/null
+++ b/MediaBrowser.Controller/Resolvers/Audio/MusicAlbumResolver.cs
@@ -0,0 +1,27 @@
+using MediaBrowser.Controller.Entities.Audio;
+using MediaBrowser.Controller.Library;
+using System.ComponentModel.Composition;
+
+namespace MediaBrowser.Controller.Resolvers.Audio
+{
+ [Export(typeof(IBaseItemResolver))]
+ public class MusicAlbumResolver : BaseItemResolver<MusicAlbum>
+ {
+ public override ResolverPriority Priority
+ {
+ get { return ResolverPriority.Third; } // we need to be ahead of the generic folder resolver but behind the movie one
+ }
+
+ 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 EntityResolutionHelper.IsMusicAlbum(args) ? new MusicAlbum() : null;
+ }
+
+ }
+}
diff --git a/MediaBrowser.Controller/Resolvers/Audio/MusicArtistResolver.cs b/MediaBrowser.Controller/Resolvers/Audio/MusicArtistResolver.cs
new file mode 100644
index 0000000000..8060e8d334
--- /dev/null
+++ b/MediaBrowser.Controller/Resolvers/Audio/MusicArtistResolver.cs
@@ -0,0 +1,29 @@
+using MediaBrowser.Controller.Entities.Audio;
+using MediaBrowser.Controller.Library;
+using System.ComponentModel.Composition;
+using System.Linq;
+
+namespace MediaBrowser.Controller.Resolvers.Audio
+{
+ [Export(typeof(IBaseItemResolver))]
+ public class MusicArtistResolver : BaseItemResolver<MusicArtist>
+ {
+ public override ResolverPriority Priority
+ {
+ get { return ResolverPriority.Third; } // we need to be ahead of the generic folder resolver but behind the movie one
+ }
+
+ 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(EntityResolutionHelper.IsMusicAlbum) ? new MusicArtist() : null;
+ }
+
+ }
+}
diff --git a/MediaBrowser.Controller/Resolvers/AudioResolver.cs b/MediaBrowser.Controller/Resolvers/AudioResolver.cs
deleted file mode 100644
index 8f10e45e50..0000000000
--- a/MediaBrowser.Controller/Resolvers/AudioResolver.cs
+++ /dev/null
@@ -1,54 +0,0 @@
-using MediaBrowser.Controller.Entities;
-using MediaBrowser.Controller.Library;
-using System.ComponentModel.Composition;
-using System.IO;
-
-namespace MediaBrowser.Controller.Resolvers
-{
- [Export(typeof(IBaseItemResolver))]
- public class AudioResolver : BaseItemResolver<Audio>
- {
- public override ResolverPriority Priority
- {
- get { return ResolverPriority.Last; }
- }
-
- protected override Audio Resolve(ItemResolveEventArgs args)
- {
- // Return audio if the path is a file and has a matching extension
-
- if (!args.IsDirectory)
- {
- if (IsAudioFile(args.Path))
- {
- return new Audio();
- }
- }
-
- return null;
- }
-
- private static bool IsAudioFile(string path)
- {
- string extension = Path.GetExtension(path).ToLower();
-
- switch (extension)
- {
- case ".mp3":
- case ".wma":
- case ".aac":
- case ".acc":
- case ".flac":
- case ".m4a":
- case ".m4b":
- case ".wav":
- case ".ape":
- return true;
-
- default:
- return false;
- }
-
- }
- }
-}
diff --git a/MediaBrowser.Controller/Resolvers/BaseItemResolver.cs b/MediaBrowser.Controller/Resolvers/BaseItemResolver.cs
index 7c9677e4e4..8e43a791fc 100644
--- a/MediaBrowser.Controller/Resolvers/BaseItemResolver.cs
+++ b/MediaBrowser.Controller/Resolvers/BaseItemResolver.cs
@@ -1,126 +1,165 @@
-using MediaBrowser.Controller.Entities;
-using MediaBrowser.Controller.IO;
-using MediaBrowser.Controller.Library;
-using MediaBrowser.Common.Extensions;
-using System;
-using System.IO;
-
-namespace MediaBrowser.Controller.Resolvers
-{
- public abstract class BaseItemResolver<T> : IBaseItemResolver
- where T : BaseItem, new()
- {
- protected virtual T Resolve(ItemResolveEventArgs args)
- {
- return null;
- }
-
- public virtual ResolverPriority Priority
- {
- get
- {
- return ResolverPriority.First;
- }
- }
-
- /// <summary>
- /// Sets initial values on the newly resolved item
- /// </summary>
- protected virtual void SetInitialItemValues(T item, ItemResolveEventArgs args)
- {
- // If the subclass didn't specify this
- if (string.IsNullOrEmpty(item.Path))
- {
- item.Path = args.Path;
- }
-
- // If the subclass didn't specify this
- if (args.Parent != null)
- {
- item.Parent = args.Parent;
- }
-
- item.Id = (item.GetType().FullName + item.Path).GetMD5();
- }
-
- public BaseItem ResolvePath(ItemResolveEventArgs args)
- {
- T item = Resolve(args);
-
- if (item != null)
- {
- // Set initial values on the newly resolved item
- SetInitialItemValues(item, args);
-
- // Make sure the item has a name
- EnsureName(item);
-
- // Make sure DateCreated and DateModified have values
- EnsureDates(item, args);
- }
-
- return item;
- }
-
- private void EnsureName(T item)
- {
- // If the subclass didn't supply a name, add it here
- if (string.IsNullOrEmpty(item.Name))
- {
- item.Name = Path.GetFileNameWithoutExtension(item.Path);
- }
-
- }
-
- /// <summary>
- /// Ensures DateCreated and DateModified have values
- /// </summary>
- private void EnsureDates(T item, ItemResolveEventArgs args)
- {
- if (!Path.IsPathRooted(item.Path))
- {
- return;
- }
-
- // See if a different path came out of the resolver than what went in
- if (!args.Path.Equals(item.Path, StringComparison.OrdinalIgnoreCase))
- {
- WIN32_FIND_DATA? childData = args.GetFileSystemEntry(item.Path);
-
- if (childData != null)
- {
- item.DateCreated = childData.Value.CreationTimeUtc;
- item.DateModified = childData.Value.LastWriteTimeUtc;
- }
- else
- {
- WIN32_FIND_DATA fileData = FileData.GetFileData(item.Path);
- item.DateCreated = fileData.CreationTimeUtc;
- item.DateModified = fileData.LastWriteTimeUtc;
- }
- }
- else
- {
- item.DateCreated = args.FileInfo.CreationTimeUtc;
- item.DateModified = args.FileInfo.LastWriteTimeUtc;
- }
- }
- }
-
- /// <summary>
- /// Weed this to keep a list of resolvers, since Resolvers are built with generics
- /// </summary>
- public interface IBaseItemResolver
- {
- BaseItem ResolvePath(ItemResolveEventArgs args);
- ResolverPriority Priority { get; }
- }
-
- public enum ResolverPriority
- {
- First = 1,
- Second = 2,
- Third = 3,
- Last = 4
- }
-}
+using MediaBrowser.Common.Extensions;
+using MediaBrowser.Controller.Entities;
+using MediaBrowser.Controller.Library;
+using System.IO;
+using System.Text.RegularExpressions;
+
+namespace MediaBrowser.Controller.Resolvers
+{
+ /// <summary>
+ /// Class BaseItemResolver
+ /// </summary>
+ /// <typeparam name="T"></typeparam>
+ public abstract class BaseItemResolver<T> : IBaseItemResolver
+ 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)
+ {
+ // If the subclass didn't specify this
+ if (string.IsNullOrEmpty(item.Path))
+ {
+ item.Path = args.Path;
+ }
+
+ // If the subclass didn't specify this
+ if (args.Parent != null)
+ {
+ item.Parent = args.Parent;
+ }
+
+ item.Id = item.Path.GetMBId(item.GetType());
+ item.DisplayMediaType = item.GetType().Name;
+ }
+
+ /// <summary>
+ /// Resolves the path.
+ /// </summary>
+ /// <param name="args">The args.</param>
+ /// <returns>BaseItem.</returns>
+ public BaseItem ResolvePath(ItemResolveArgs args)
+ {
+ T item = Resolve(args);
+
+ if (item != null)
+ {
+ // Set the args on the item
+ item.ResolveArgs = args;
+
+ // Set initial values on the newly resolved item
+ SetInitialItemValues(item, args);
+
+ // Make sure the item has a name
+ EnsureName(item);
+
+ // Make sure DateCreated and DateModified have values
+ EntityResolutionHelper.EnsureDates(item, args);
+ }
+
+ return item;
+ }
+
+ /// <summary>
+ /// Ensures the name.
+ /// </summary>
+ /// <param name="item">The item.</param>
+ private void EnsureName(T 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;
+ }
+ }
+
+ /// <summary>
+ /// Weed this to keep a list of resolvers, since Resolvers are built with generics
+ /// </summary>
+ public interface IBaseItemResolver
+ {
+ /// <summary>
+ /// Resolves the path.
+ /// </summary>
+ /// <param name="args">The args.</param>
+ /// <returns>BaseItem.</returns>
+ BaseItem ResolvePath(ItemResolveArgs args);
+ /// <summary>
+ /// Gets the priority.
+ /// </summary>
+ /// <value>The priority.</value>
+ ResolverPriority Priority { get; }
+ }
+
+ /// <summary>
+ /// Enum ResolverPriority
+ /// </summary>
+ public enum ResolverPriority
+ {
+ /// <summary>
+ /// The first
+ /// </summary>
+ First = 1,
+ /// <summary>
+ /// The second
+ /// </summary>
+ Second = 2,
+ /// <summary>
+ /// The third
+ /// </summary>
+ Third = 3,
+ /// <summary>
+ /// The last
+ /// </summary>
+ Last = 4
+ }
+}
diff --git a/MediaBrowser.Controller/Resolvers/BaseResolutionIgnoreRule.cs b/MediaBrowser.Controller/Resolvers/BaseResolutionIgnoreRule.cs
new file mode 100644
index 0000000000..45effc4da1
--- /dev/null
+++ b/MediaBrowser.Controller/Resolvers/BaseResolutionIgnoreRule.cs
@@ -0,0 +1,12 @@
+using MediaBrowser.Controller.Library;
+
+namespace MediaBrowser.Controller.Resolvers
+{
+ /// <summary>
+ /// Provides a base "rule" that anyone can use to have paths ignored by the resolver
+ /// </summary>
+ public abstract class BaseResolutionIgnoreRule
+ {
+ public abstract bool ShouldIgnore(ItemResolveArgs args);
+ }
+}
diff --git a/MediaBrowser.Controller/Resolvers/CoreResolutionIgnoreRule.cs b/MediaBrowser.Controller/Resolvers/CoreResolutionIgnoreRule.cs
new file mode 100644
index 0000000000..2d69f8deff
--- /dev/null
+++ b/MediaBrowser.Controller/Resolvers/CoreResolutionIgnoreRule.cs
@@ -0,0 +1,52 @@
+using MediaBrowser.Controller.Library;
+using System;
+using System.Collections.Generic;
+using System.ComponentModel.Composition;
+using System.Linq;
+
+namespace MediaBrowser.Controller.Resolvers
+{
+ /// <summary>
+ /// Provides the core resolver ignore rules
+ /// </summary>
+ [Export(typeof(BaseResolutionIgnoreRule))]
+ public class CoreResolutionIgnoreRule : BaseResolutionIgnoreRule
+ {
+ /// <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"
+ };
+
+ public override 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.Controller/Resolvers/EntityResolutionHelper.cs b/MediaBrowser.Controller/Resolvers/EntityResolutionHelper.cs
index b821f88018..75e1305268 100644
--- a/MediaBrowser.Controller/Resolvers/EntityResolutionHelper.cs
+++ b/MediaBrowser.Controller/Resolvers/EntityResolutionHelper.cs
@@ -1,70 +1,211 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
-using System.IO;
-using MediaBrowser.Controller.IO;
-using MediaBrowser.Controller.Library;
-using MediaBrowser.Controller.Entities.TV;
-
-namespace MediaBrowser.Controller.Resolvers
-{
- public static class EntityResolutionHelper
- {
- /// <summary>
- /// Any folder named in this list will be ignored - can be added to at runtime for extensibility
- /// </summary>
- public static List<string> IgnoreFolders = new List<string>()
- {
- "trailers",
- "metadata",
- "bdmv",
- "certificate",
- "backup",
- "video_ts",
- "audio_ts",
- "ps3_update",
- "ps3_vprm",
- "adv_obj",
- "hvdvd_ts"
- };
- /// <summary>
- /// Determines whether a path should be resolved or ignored entirely - called before we even look at the contents
- /// </summary>
- /// <param name="path"></param>
- /// <returns>false if the path should be ignored</returns>
- public static bool ShouldResolvePath(WIN32_FIND_DATA path)
- {
- bool resolve = true;
- // Ignore hidden files and folders
- if (path.IsHidden || path.IsSystemFile)
- {
- resolve = false;
- }
-
- // Ignore any folders in our list
- else if (path.IsDirectory && IgnoreFolders.Contains(Path.GetFileName(path.Path), StringComparer.OrdinalIgnoreCase))
- {
- resolve = false;
- }
-
- return resolve;
- }
-
- /// <summary>
- /// Determines whether a path should be ignored based on its contents - called after the contents have been read
- /// </summary>
- public static bool ShouldResolvePathContents(ItemResolveEventArgs args)
- {
- bool resolve = true;
- if (args.ContainsFile(".ignore"))
- {
- // Ignore any folders containing a file called .ignore
- resolve = false;
- }
-
- return resolve;
- }
- }
-}
+using MediaBrowser.Common.IO;
+using MediaBrowser.Common.Win32;
+using MediaBrowser.Controller.Entities;
+using MediaBrowser.Controller.Library;
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+
+namespace MediaBrowser.Controller.Resolvers
+{
+ /// <summary>
+ /// Class EntityResolutionHelper
+ /// </summary>
+ public static class EntityResolutionHelper
+ {
+ /// <summary>
+ /// Any extension in this list is considered a metadata file - can be added to at runtime for extensibility
+ /// </summary>
+ public static List<string> MetaExtensions = new List<string>
+ {
+ ".xml",
+ ".jpg",
+ ".png",
+ ".json",
+ ".data"
+ };
+ /// <summary>
+ /// Any extension in this list is considered a video file - can be added to at runtime for extensibility
+ /// </summary>
+ public static List<string> VideoFileExtensions = new List<string>
+ {
+ ".mkv",
+ ".m2t",
+ ".m2ts",
+ ".img",
+ ".iso",
+ ".ts",
+ ".rmvb",
+ ".mov",
+ ".avi",
+ ".mpg",
+ ".mpeg",
+ ".wmv",
+ ".mp4",
+ ".divx",
+ ".dvr-ms",
+ ".wtv",
+ ".ogm",
+ ".ogv",
+ ".asf",
+ ".m4v",
+ ".flv",
+ ".f4v",
+ ".3gp",
+ ".webm"
+ };
+
+ /// <summary>
+ /// Determines whether [is video file] [the specified path].
+ /// </summary>
+ /// <param name="path">The path.</param>
+ /// <returns><c>true</c> if [is video file] [the specified path]; otherwise, <c>false</c>.</returns>
+ public static bool IsVideoFile(string path)
+ {
+ var extension = Path.GetExtension(path) ?? string.Empty;
+ return VideoFileExtensions.Contains(extension, StringComparer.OrdinalIgnoreCase);
+ }
+
+ /// <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);
+ }
+
+ /// <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 (IsAudioFile(file)) foundAudio++;
+ if (foundAudio >= 2)
+ {
+ return true;
+ }
+ if (IsVideoFile(file.Path)) foundVideo++;
+ }
+
+ // or a single audio file and no video files
+ if (foundAudio > 0 && foundVideo == 0) return true;
+ return false;
+ }
+
+ /// <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>
+ public static bool ShouldResolvePathContents(ItemResolveArgs args)
+ {
+ // Ignore any folders containing a file called .ignore
+ return !args.ContainsFileSystemEntryByName(".ignore");
+ }
+
+ /// <summary>
+ /// Ensures DateCreated and DateModified have values
+ /// </summary>
+ /// <param name="item">The item.</param>
+ /// <param name="args">The args.</param>
+ public static void EnsureDates(BaseItem item, ItemResolveArgs args)
+ {
+ if (!Path.IsPathRooted(item.Path))
+ {
+ return;
+ }
+
+ // See if a different path came out of the resolver than what went in
+ if (!args.Path.Equals(item.Path, StringComparison.OrdinalIgnoreCase))
+ {
+ var childData = args.IsDirectory ? args.GetFileSystemEntryByPath(item.Path) : null;
+
+ if (childData.HasValue)
+ {
+ item.DateCreated = childData.Value.CreationTimeUtc;
+ item.DateModified = childData.Value.LastWriteTimeUtc;
+ }
+ else
+ {
+ var fileData = FileSystem.GetFileData(item.Path);
+
+ if (fileData.HasValue)
+ {
+ item.DateCreated = fileData.Value.CreationTimeUtc;
+ item.DateModified = fileData.Value.LastWriteTimeUtc;
+ }
+ }
+ }
+ else
+ {
+ item.DateCreated = args.FileInfo.CreationTimeUtc;
+ item.DateModified = args.FileInfo.LastWriteTimeUtc;
+ }
+ }
+ }
+}
diff --git a/MediaBrowser.Controller/Resolvers/FolderResolver.cs b/MediaBrowser.Controller/Resolvers/FolderResolver.cs
index 028c85f862..e37c18692d 100644
--- a/MediaBrowser.Controller/Resolvers/FolderResolver.cs
+++ b/MediaBrowser.Controller/Resolvers/FolderResolver.cs
@@ -1,36 +1,71 @@
-using MediaBrowser.Controller.Entities;
-using MediaBrowser.Controller.Library;
-using System.ComponentModel.Composition;
-
-namespace MediaBrowser.Controller.Resolvers
-{
- [Export(typeof(IBaseItemResolver))]
- public class FolderResolver : BaseFolderResolver<Folder>
- {
- public override ResolverPriority Priority
- {
- get { return ResolverPriority.Last; }
- }
-
- protected override Folder Resolve(ItemResolveEventArgs args)
- {
- if (args.IsDirectory)
- {
- return new Folder();
- }
-
- return null;
- }
- }
-
- public abstract class BaseFolderResolver<TItemType> : BaseItemResolver<TItemType>
- where TItemType : Folder, new()
- {
- protected override void SetInitialItemValues(TItemType item, ItemResolveEventArgs args)
- {
- base.SetInitialItemValues(item, args);
-
- item.IsRoot = args.Parent == null;
- }
- }
-}
+using MediaBrowser.Controller.Entities;
+using MediaBrowser.Controller.Library;
+using System.ComponentModel.Composition;
+
+namespace MediaBrowser.Controller.Resolvers
+{
+ /// <summary>
+ /// Class FolderResolver
+ /// </summary>
+ [Export(typeof(IBaseItemResolver))]
+ public class FolderResolver : BaseFolderResolver<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 BaseFolderResolver
+ /// </summary>
+ /// <typeparam name="TItemType">The type of the T item type.</typeparam>
+ public abstract class BaseFolderResolver<TItemType> : BaseItemResolver<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.Controller/Resolvers/LocalTrailerResolver.cs b/MediaBrowser.Controller/Resolvers/LocalTrailerResolver.cs
new file mode 100644
index 0000000000..c26b0ce37e
--- /dev/null
+++ b/MediaBrowser.Controller/Resolvers/LocalTrailerResolver.cs
@@ -0,0 +1,40 @@
+using MediaBrowser.Controller.Entities;
+using MediaBrowser.Controller.Library;
+using System;
+using System.ComponentModel.Composition;
+using System.IO;
+
+namespace MediaBrowser.Controller.Resolvers
+{
+ /// <summary>
+ /// Class LocalTrailerResolver
+ /// </summary>
+ [Export(typeof(IBaseItemResolver))]
+ public class LocalTrailerResolver : BaseVideoResolver<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.Controller/Resolvers/Movies/BoxSetResolver.cs b/MediaBrowser.Controller/Resolvers/Movies/BoxSetResolver.cs
index 069068067f..ccca0cfabb 100644
--- a/MediaBrowser.Controller/Resolvers/Movies/BoxSetResolver.cs
+++ b/MediaBrowser.Controller/Resolvers/Movies/BoxSetResolver.cs
@@ -1,28 +1,43 @@
-using MediaBrowser.Controller.Entities.Movies;
-using MediaBrowser.Controller.Library;
-using System;
-using System.ComponentModel.Composition;
-using System.IO;
-
-namespace MediaBrowser.Controller.Resolvers.Movies
-{
- [Export(typeof(IBaseItemResolver))]
- public class BoxSetResolver : BaseFolderResolver<BoxSet>
- {
- protected override BoxSet Resolve(ItemResolveEventArgs args)
- {
- // It's a boxset if all of the following conditions are met:
- // Is a Directory
- // Contains [boxset] in the path
- if (args.IsDirectory)
- {
- if (Path.GetFileName(args.Path).IndexOf("[boxset]", StringComparison.OrdinalIgnoreCase) != -1)
- {
- return new BoxSet();
- }
- }
-
- return null;
- }
- }
-}
+using MediaBrowser.Controller.Entities.Movies;
+using MediaBrowser.Controller.Library;
+using System;
+using System.ComponentModel.Composition;
+using System.IO;
+
+namespace MediaBrowser.Controller.Resolvers.Movies
+{
+ /// <summary>
+ /// Class BoxSetResolver
+ /// </summary>
+ [Export(typeof(IBaseItemResolver))]
+ public class BoxSetResolver : BaseFolderResolver<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.Controller/Resolvers/Movies/MovieResolver.cs b/MediaBrowser.Controller/Resolvers/Movies/MovieResolver.cs
index 825850b20c..14f6357478 100644
--- a/MediaBrowser.Controller/Resolvers/Movies/MovieResolver.cs
+++ b/MediaBrowser.Controller/Resolvers/Movies/MovieResolver.cs
@@ -1,116 +1,210 @@
-using MediaBrowser.Controller.Entities;
-using MediaBrowser.Controller.Entities.Movies;
-using MediaBrowser.Controller.IO;
-using MediaBrowser.Controller.Library;
-using MediaBrowser.Model.Entities;
-using System.ComponentModel.Composition;
-using System.Collections.Generic;
-
-namespace MediaBrowser.Controller.Resolvers.Movies
-{
- [Export(typeof(IBaseItemResolver))]
- public class MovieResolver : BaseVideoResolver<Movie>
- {
- protected override Movie Resolve(ItemResolveEventArgs args)
- {
- // Must be a directory and under a 'Movies' VF
- if (args.IsDirectory)
- {
- // If the parent is not a boxset, the only other allowed parent type is Folder
- if (!(args.Parent is BoxSet))
- {
- if (args.Parent != null && args.Parent.GetType() != typeof(Folder))
- {
- return null;
- }
- }
-
- // Optimization to avoid running all these tests against VF's
- if (args.Parent != null && args.Parent.IsRoot)
- {
- return null;
- }
-
- // Return a movie if the video resolver finds something in the folder
- return GetMovie(args);
- }
-
- return null;
- }
-
- protected override void SetInitialItemValues(Movie item, ItemResolveEventArgs args)
- {
- base.SetInitialItemValues(item, args);
-
- SetProviderIdFromPath(item);
- }
-
- private void SetProviderIdFromPath(Movie item)
- {
- const string srch = "[tmdbid=";
- int index = item.Path.IndexOf(srch, System.StringComparison.OrdinalIgnoreCase);
-
- if (index != -1)
- {
- string id = item.Path.Substring(index + srch.Length);
-
- id = id.Substring(0, id.IndexOf(']'));
-
- item.SetProviderId(MetadataProviders.Tmdb, id);
- }
- }
-
- private Movie GetMovie(ItemResolveEventArgs args)
- {
- //first see if the discovery process has already determined we are a DVD or BD
- if (args.IsDVDFolder)
- {
- return new Movie()
- {
- Path = args.Path,
- VideoType = VideoType.Dvd
- };
- }
- else if (args.IsBDFolder)
- {
- return new Movie()
- {
- Path = args.Path,
- VideoType = VideoType.BluRay
- };
- }
- else if (args.IsHDDVDFolder)
- {
- return new Movie()
- {
- Path = args.Path,
- VideoType = VideoType.HdDvd
- };
- }
-
- // Loop through each child file/folder and see if we find a video
- foreach (var child in args.FileSystemChildren)
- {
- var childArgs = new ItemResolveEventArgs
- {
- FileInfo = child,
- FileSystemChildren = new WIN32_FIND_DATA[] { },
- Path = child.Path
- };
-
- var item = base.Resolve(childArgs);
-
- if (item != null)
- {
- return new Movie
- {
- Path = item.Path,
- VideoType = item.VideoType
- };
- }
- }
-
- return null;
- }
- }
-}
+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.ComponentModel.Composition;
+using System.IO;
+
+namespace MediaBrowser.Controller.Resolvers.Movies
+{
+ /// <summary>
+ /// Class MovieResolver
+ /// </summary>
+ [Export(typeof(IBaseItemResolver))]
+ public class MovieResolver : BaseVideoResolver<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 = item.Path.Substring(item.Path.LastIndexOf(Path.DirectorySeparatorChar));
+
+ 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.Controller/Resolvers/TV/EpisodeResolver.cs b/MediaBrowser.Controller/Resolvers/TV/EpisodeResolver.cs
index 0961edd1ac..f2f3ce122d 100644
--- a/MediaBrowser.Controller/Resolvers/TV/EpisodeResolver.cs
+++ b/MediaBrowser.Controller/Resolvers/TV/EpisodeResolver.cs
@@ -1,21 +1,65 @@
-using MediaBrowser.Controller.Entities.TV;
-using MediaBrowser.Controller.Library;
-using System.ComponentModel.Composition;
-
-namespace MediaBrowser.Controller.Resolvers.TV
-{
- [Export(typeof(IBaseItemResolver))]
- public class EpisodeResolver : BaseVideoResolver<Episode>
- {
- protected override Episode Resolve(ItemResolveEventArgs 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)
- {
- return base.Resolve(args);
- }
-
- return null;
- }
- }
-}
+using System;
+using MediaBrowser.Controller.Entities.TV;
+using MediaBrowser.Controller.Library;
+using System.ComponentModel.Composition;
+using MediaBrowser.Model.Entities;
+
+namespace MediaBrowser.Controller.Resolvers.TV
+{
+ [Export(typeof(IBaseItemResolver))]
+ public class EpisodeResolver : BaseVideoResolver<Episode>
+ {
+ 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;
+ }
+
+ 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.Controller/Resolvers/TV/SeasonResolver.cs b/MediaBrowser.Controller/Resolvers/TV/SeasonResolver.cs
index 0ad0782e07..6569c85bc8 100644
--- a/MediaBrowser.Controller/Resolvers/TV/SeasonResolver.cs
+++ b/MediaBrowser.Controller/Resolvers/TV/SeasonResolver.cs
@@ -1,25 +1,34 @@
-using MediaBrowser.Controller.Entities.TV;
-using MediaBrowser.Controller.Library;
-using System.ComponentModel.Composition;
-using System.IO;
-
-namespace MediaBrowser.Controller.Resolvers.TV
-{
- [Export(typeof(IBaseItemResolver))]
- public class SeasonResolver : BaseFolderResolver<Season>
- {
- protected override Season Resolve(ItemResolveEventArgs args)
- {
- if (args.Parent is Series && args.IsDirectory)
- {
- var season = new Season { };
-
- season.IndexNumber = TVUtils.GetSeasonNumberFromPath(args.Path);
-
- return season;
- }
-
- return null;
- }
- }
-}
+using MediaBrowser.Controller.Entities.TV;
+using MediaBrowser.Controller.Library;
+using System;
+using System.ComponentModel.Composition;
+
+namespace MediaBrowser.Controller.Resolvers.TV
+{
+ [Export(typeof(IBaseItemResolver))]
+ public class SeasonResolver : BaseFolderResolver<Season>
+ {
+ protected override Season Resolve(ItemResolveArgs args)
+ {
+ if (args.Parent is Series && args.IsDirectory)
+ {
+ return new Season
+ {
+ IndexNumber = TVUtils.GetSeasonNumberFromPath(args.Path)
+ };
+ }
+
+ return null;
+ }
+
+ 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.Controller/Resolvers/TV/SeriesResolver.cs b/MediaBrowser.Controller/Resolvers/TV/SeriesResolver.cs
index b8ff2c37be..7c0bc3df14 100644
--- a/MediaBrowser.Controller/Resolvers/TV/SeriesResolver.cs
+++ b/MediaBrowser.Controller/Resolvers/TV/SeriesResolver.cs
@@ -1,64 +1,100 @@
-using MediaBrowser.Controller.Entities.TV;
-using MediaBrowser.Controller.Library;
-using MediaBrowser.Model.Entities;
-using System;
-using System.ComponentModel.Composition;
-using System.IO;
-
-namespace MediaBrowser.Controller.Resolvers.TV
-{
- [Export(typeof(IBaseItemResolver))]
- public class SeriesResolver : BaseFolderResolver<Series>
- {
- protected override Series Resolve(ItemResolveEventArgs args)
- {
- if (args.IsDirectory)
- {
- // Optimization to avoid running all these tests against VF's
- 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
- if (args.ContainsFile("series.xml") || Path.GetFileName(args.Path).IndexOf("[tvdbid=", StringComparison.OrdinalIgnoreCase) != -1 || TVUtils.IsSeriesFolder(args.Path, args.FileSystemChildren))
- {
- return new Series();
- }
- }
-
- return null;
- }
-
- protected override void SetInitialItemValues(Series item, ItemResolveEventArgs args)
- {
- base.SetInitialItemValues(item, args);
-
- SetProviderIdFromPath(item);
- }
-
- private void SetProviderIdFromPath(Series item)
- {
- const string srch = "[tvdbid=";
- int index = item.Path.IndexOf(srch, StringComparison.OrdinalIgnoreCase);
-
- if (index != -1)
- {
- string id = item.Path.Substring(index + srch.Length);
-
- id = id.Substring(0, id.IndexOf(']'));
-
- item.SetProviderId(MetadataProviders.Tvdb, id);
- }
- }
- }
-}
+using MediaBrowser.Common.Extensions;
+using MediaBrowser.Controller.Entities.TV;
+using MediaBrowser.Controller.Library;
+using MediaBrowser.Model.Entities;
+using System;
+using System.ComponentModel.Composition;
+using System.IO;
+
+namespace MediaBrowser.Controller.Resolvers.TV
+{
+ /// <summary>
+ /// Class SeriesResolver
+ /// </summary>
+ [Export(typeof(IBaseItemResolver))]
+ public class SeriesResolver : BaseFolderResolver<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);
+ }
+
+ /// <summary>
+ /// Sets the provider id from path.
+ /// </summary>
+ /// <param name="item">The item.</param>
+ private void SetProviderIdFromPath(Series item)
+ {
+ var justName = item.Path.Substring(item.Path.LastIndexOf(Path.DirectorySeparatorChar));
+
+ var id = justName.GetAttributeValue("tvdbid");
+
+ if (!string.IsNullOrEmpty(id))
+ {
+ item.SetProviderId(MetadataProviders.Tvdb, id);
+ }
+ }
+ }
+}
diff --git a/MediaBrowser.Controller/Resolvers/TV/TVUtils.cs b/MediaBrowser.Controller/Resolvers/TV/TVUtils.cs
index ec3305e167..3e7421a397 100644
--- a/MediaBrowser.Controller/Resolvers/TV/TVUtils.cs
+++ b/MediaBrowser.Controller/Resolvers/TV/TVUtils.cs
@@ -1,164 +1,251 @@
-using MediaBrowser.Controller.IO;
-using System;
-using System.Text.RegularExpressions;
-
-namespace MediaBrowser.Controller.Resolvers.TV
-{
- public static class TVUtils
- {
- /// <summary>
- /// A season folder must contain one of these somewhere in the name
- /// </summary>
- private static readonly string[] SeasonFolderNames = new string[] {
- "season",
- "sæson",
- "temporada",
- "saison",
- "staffel"
- };
-
- /// <summary>
- /// Used to detect paths that represent episodes, need to make sure they don't also
- /// match movie titles like "2001 A Space..."
- /// Currently we limit the numbers here to 2 digits to try and avoid this
- /// </summary>
- /// <remarks>
- /// The order here is important, if the order is changed some of the later
- /// ones might incorrectly match things that higher ones would have caught.
- /// The most restrictive expressions should appear first
- /// </remarks>
- private static readonly Regex[] episodeExpressions = new Regex[] {
- new Regex(@".*\\[s|S]?(?<seasonnumber>\d{1,2})[x|X](?<epnumber>\d{1,3})[^\\]*$", RegexOptions.Compiled), // 01x02 blah.avi S01x01 balh.avi
- new Regex(@".*\\[s|S](?<seasonnumber>\d{1,2})x?[e|E](?<epnumber>\d{1,3})[^\\]*$", RegexOptions.Compiled), // S01E02 blah.avi, S01xE01 blah.avi
- new Regex(@".*\\(?<seriesname>[^\\]*)[s|S]?(?<seasonnumber>\d{1,2})[x|X](?<epnumber>\d{1,3})[^\\]*$", RegexOptions.Compiled), // 01x02 blah.avi S01x01 balh.avi
- new Regex(@".*\\(?<seriesname>[^\\]*)[s|S](?<seasonnumber>\d{1,2})[x|X|\.]?[e|E](?<epnumber>\d{1,3})[^\\]*$", RegexOptions.Compiled) // S01E02 blah.avi, S01xE01 blah.avi
- };
- /// <summary>
- /// To avoid the following matching movies they are only valid when contained in a folder which has been matched as a being season
- /// </summary>
- private static readonly Regex[] episodeExpressionsInASeasonFolder = new Regex[] {
- new Regex(@".*\\(?<epnumber>\d{1,2})\s?-\s?[^\\]*$", RegexOptions.Compiled), // 01 - blah.avi, 01-blah.avi
- new Regex(@".*\\(?<epnumber>\d{1,2})[^\d\\]*[^\\]*$", RegexOptions.Compiled), // 01.avi, 01.blah.avi "01 - 22 blah.avi"
- new Regex(@".*\\(?<seasonnumber>\d)(?<epnumber>\d{1,2})[^\d\\]+[^\\]*$", RegexOptions.Compiled), // 01.avi, 01.blah.avi
- new Regex(@".*\\\D*\d+(?<epnumber>\d{2})", RegexOptions.Compiled) // hell0 - 101 - hello.avi
-
- };
-
- public static int? GetSeasonNumberFromPath(string path)
- {
- // Look for one of the season folder names
- foreach (string name in SeasonFolderNames)
- {
- int index = path.IndexOf(name, StringComparison.OrdinalIgnoreCase);
-
- if (index != -1)
- {
- return GetSeasonNumberFromPathSubstring(path.Substring(index + name.Length));
- }
- }
-
- return null;
- }
-
- /// <summary>
- /// Extracts the season number from the second half of the Season folder name (everything after "Season", or "Staffel")
- /// </summary>
- private static int? GetSeasonNumberFromPathSubstring(string path)
- {
- int numericStart = -1;
- int length = 0;
-
- // Find out where the numbers start, and then keep going until they end
- for (int i = 0; i < path.Length; i++)
- {
- if (char.IsNumber(path, i))
- {
- if (numericStart == -1)
- {
- numericStart = i;
- }
- length++;
- }
- else if (numericStart != -1)
- {
- break;
- }
- }
-
- if (numericStart == -1)
- {
- return null;
- }
-
- return int.Parse(path.Substring(numericStart, length));
- }
-
- public static bool IsSeasonFolder(string path)
- {
- return GetSeasonNumberFromPath(path) != null;
- }
-
- public static bool IsSeriesFolder(string path, WIN32_FIND_DATA[] fileSystemChildren)
- {
- // A folder with more than 3 non-season folders in will not becounted as a series
- int nonSeriesFolders = 0;
-
- for (int i = 0; i < fileSystemChildren.Length; i++)
- {
- var child = fileSystemChildren[i];
-
- if (child.IsHidden || child.IsSystemFile)
- {
- continue;
- }
-
- if (child.IsDirectory)
- {
- if (IsSeasonFolder(child.Path))
- {
- return true;
- }
-
- nonSeriesFolders++;
-
- if (nonSeriesFolders >= 3)
- {
- return false;
- }
- }
- else
- {
- if (FileSystemHelper.IsVideoFile(child.Path) && !string.IsNullOrEmpty(EpisodeNumberFromFile(child.Path, false)))
- {
- return true;
- }
- }
- }
-
- return false;
- }
-
- public static string EpisodeNumberFromFile(string fullPath, bool isInSeason)
- {
- string fl = fullPath.ToLower();
- foreach (Regex r in episodeExpressions)
- {
- Match m = r.Match(fl);
- if (m.Success)
- return m.Groups["epnumber"].Value;
- }
- if (isInSeason)
- {
- foreach (Regex r in episodeExpressionsInASeasonFolder)
- {
- Match m = r.Match(fl);
- if (m.Success)
- return m.Groups["epnumber"].Value;
- }
-
- }
-
- return null;
- }
- }
-}
+using MediaBrowser.Common.Logging;
+using MediaBrowser.Common.Win32;
+using System;
+using System.Collections.Generic;
+using System.Text.RegularExpressions;
+using System.Linq;
+
+namespace MediaBrowser.Controller.Resolvers.TV
+{
+ public static class TVUtils
+ {
+ public static readonly string TVDBApiKey = "B89CE93890E9419B";
+ public static readonly string BannerUrl = "http://www.thetvdb.com/banners/";
+
+ /// <summary>
+ /// A season folder must contain one of these somewhere in the name
+ /// </summary>
+ private static readonly string[] SeasonFolderNames = new[]
+ {
+ "season",
+ "sæson",
+ "temporada",
+ "saison",
+ "staffel"
+ };
+
+ /// <summary>
+ /// Used to detect paths that represent episodes, need to make sure they don't also
+ /// match movie titles like "2001 A Space..."
+ /// Currently we limit the numbers here to 2 digits to try and avoid this
+ /// </summary>
+ /// <remarks>
+ /// The order here is important, if the order is changed some of the later
+ /// ones might incorrectly match things that higher ones would have caught.
+ /// The most restrictive expressions should appear first
+ /// </remarks>
+ private static readonly Regex[] EpisodeExpressions = new[]
+ {
+ new Regex(
+ @".*\\[s|S]?(?<seasonnumber>\d{1,2})[x|X](?<epnumber>\d{1,3})[^\\]*$",
+ RegexOptions.Compiled),
+ // 01x02 blah.avi S01x01 balh.avi
+ new Regex(
+ @".*\\[s|S](?<seasonnumber>\d{1,2})x?[e|E](?<epnumber>\d{1,3})[^\\]*$",
+ RegexOptions.Compiled),
+ // S01E02 blah.avi, S01xE01 blah.avi
+ new Regex(
+ @".*\\(?<seriesname>[^\\]*)[s|S]?(?<seasonnumber>\d{1,2})[x|X](?<epnumber>\d{1,3})[^\\]*$",
+ RegexOptions.Compiled),
+ // 01x02 blah.avi S01x01 balh.avi
+ new Regex(
+ @".*\\(?<seriesname>[^\\]*)[s|S](?<seasonnumber>\d{1,2})[x|X|\.]?[e|E](?<epnumber>\d{1,3})[^\\]*$",
+ RegexOptions.Compiled)
+ // S01E02 blah.avi, S01xE01 blah.avi
+ };
+
+ /// <summary>
+ /// To avoid the following matching movies they are only valid when contained in a folder which has been matched as a being season
+ /// </summary>
+ private static readonly Regex[] EpisodeExpressionsInASeasonFolder = new[]
+ {
+ new Regex(
+ @".*\\(?<epnumber>\d{1,2})\s?-\s?[^\\]*$",
+ RegexOptions.Compiled),
+ // 01 - blah.avi, 01-blah.avi
+ new Regex(
+ @".*\\(?<epnumber>\d{1,2})[^\d\\]*[^\\]*$",
+ RegexOptions.Compiled),
+ // 01.avi, 01.blah.avi "01 - 22 blah.avi"
+ new Regex(
+ @".*\\(?<seasonnumber>\d)(?<epnumber>\d{1,2})[^\d\\]+[^\\]*$",
+ RegexOptions.Compiled),
+ // 01.avi, 01.blah.avi
+ new Regex(
+ @".*\\\D*\d+(?<epnumber>\d{2})",
+ RegexOptions.Compiled)
+ // hell0 - 101 - hello.avi
+
+ };
+
+ public static int? GetSeasonNumberFromPath(string path)
+ {
+ // Look for one of the season folder names
+ foreach (var name in SeasonFolderNames)
+ {
+ int index = path.IndexOf(name, StringComparison.OrdinalIgnoreCase);
+
+ if (index != -1)
+ {
+ return GetSeasonNumberFromPathSubstring(path.Substring(index + name.Length));
+ }
+ }
+
+ return null;
+ }
+
+ /// <summary>
+ /// Extracts the season number from the second half of the Season folder name (everything after "Season", or "Staffel")
+ /// </summary>
+ private static int? GetSeasonNumberFromPathSubstring(string path)
+ {
+ int numericStart = -1;
+ int length = 0;
+
+ // Find out where the numbers start, and then keep going until they end
+ for (int i = 0; i < path.Length; i++)
+ {
+ if (char.IsNumber(path, i))
+ {
+ if (numericStart == -1)
+ {
+ numericStart = i;
+ }
+ length++;
+ }
+ else if (numericStart != -1)
+ {
+ break;
+ }
+ }
+
+ if (numericStart == -1)
+ {
+ return null;
+ }
+
+ return int.Parse(path.Substring(numericStart, length));
+ }
+
+ public static bool IsSeasonFolder(string path)
+ {
+ return GetSeasonNumberFromPath(path) != null;
+ }
+
+ public static bool IsSeriesFolder(string path, IEnumerable<WIN32_FIND_DATA> fileSystemChildren)
+ {
+ // A folder with more than 3 non-season folders in will not becounted as a series
+ var nonSeriesFolders = 0;
+
+ foreach (var child in fileSystemChildren)
+ {
+ if (child.IsHidden || child.IsSystemFile)
+ {
+ continue;
+ }
+
+ if (child.IsDirectory)
+ {
+ if (IsSeasonFolder(child.Path))
+ {
+ return true;
+ }
+
+ nonSeriesFolders++;
+
+ if (nonSeriesFolders >= 3)
+ {
+ return false;
+ }
+ }
+ else
+ {
+ if (EntityResolutionHelper.IsVideoFile(child.Path) &&
+ !string.IsNullOrEmpty(EpisodeNumberFromFile(child.Path, false)))
+ {
+ return true;
+ }
+ }
+ }
+
+ return false;
+ }
+
+ public static string EpisodeNumberFromFile(string fullPath, bool isInSeason)
+ {
+ string fl = fullPath.ToLower();
+ foreach (var r in EpisodeExpressions)
+ {
+ Match m = r.Match(fl);
+ if (m.Success)
+ return m.Groups["epnumber"].Value;
+ }
+ if (isInSeason)
+ {
+ var match = EpisodeExpressionsInASeasonFolder.Select(r => r.Match(fl))
+ .FirstOrDefault(m => m.Success);
+
+ if (match != null)
+ {
+ return match.Value;
+ }
+ }
+
+ return null;
+ }
+
+ public static string SeasonNumberFromEpisodeFile(string fullPath)
+ {
+ string fl = fullPath.ToLower();
+ foreach (var r in EpisodeExpressions)
+ {
+ Match m = r.Match(fl);
+ if (m.Success)
+ {
+ Group g = m.Groups["seasonnumber"];
+ if (g != null)
+ return g.Value;
+ return null;
+ }
+ }
+ return null;
+ }
+
+ public static List<DayOfWeek> GetAirDays(string day)
+ {
+ if (!string.IsNullOrWhiteSpace(day))
+ {
+ if (day.Equals("Daily", StringComparison.OrdinalIgnoreCase))
+ {
+ return new List<DayOfWeek>
+ {
+ DayOfWeek.Sunday,
+ DayOfWeek.Monday,
+ DayOfWeek.Tuesday,
+ DayOfWeek.Wednesday,
+ DayOfWeek.Thursday,
+ DayOfWeek.Friday,
+ DayOfWeek.Saturday
+ };
+ }
+
+ DayOfWeek value;
+
+ if (Enum.TryParse(day, true, out value))
+ {
+ return new List<DayOfWeek>
+ {
+ value
+ };
+ }
+
+ Logger.LogWarning("Invalid value passed into GetAirDays: {0}", day);
+
+ return new List<DayOfWeek>
+ {
+ };
+ }
+ return null;
+ }
+ }
+}
diff --git a/MediaBrowser.Controller/Resolvers/VideoResolver.cs b/MediaBrowser.Controller/Resolvers/VideoResolver.cs
index bc3be5e434..bfb364349b 100644
--- a/MediaBrowser.Controller/Resolvers/VideoResolver.cs
+++ b/MediaBrowser.Controller/Resolvers/VideoResolver.cs
@@ -1,100 +1,73 @@
-using MediaBrowser.Controller.Entities;
-using MediaBrowser.Controller.Library;
-using MediaBrowser.Model.Entities;
-using MediaBrowser.Controller.IO;
-using System.ComponentModel.Composition;
-using System.IO;
-
-namespace MediaBrowser.Controller.Resolvers
-{
- /// <summary>
- /// Resolves a Path into a Video
- /// </summary>
- [Export(typeof(IBaseItemResolver))]
- public class VideoResolver : BaseVideoResolver<Video>
- {
- public override ResolverPriority Priority
- {
- get { return ResolverPriority.Last; }
- }
- }
-
- /// <summary>
- /// Resolves a Path into a Video or Video subclass
- /// </summary>
- public abstract class BaseVideoResolver<T> : BaseItemResolver<T>
- where T : Video, new()
- {
- protected override T Resolve(ItemResolveEventArgs args)
- {
- // If the path is a file check for a matching extensions
- if (!args.IsDirectory)
- {
- if (FileSystemHelper.IsVideoFile(args.Path))
- {
- VideoType type = Path.GetExtension(args.Path).EndsWith("iso", System.StringComparison.OrdinalIgnoreCase) ? VideoType.Iso : VideoType.VideoFile;
-
- return new T
- {
- VideoType = type,
- Path = args.Path
- };
- }
- }
-
- else
- {
- // If the path is a folder, check if it's bluray or dvd
- T item = ResolveFromFolderName(args.Path);
-
- if (item != null)
- {
- return item;
- }
-
- // Also check the subfolders for bluray or dvd
- for (int i = 0; i < args.FileSystemChildren.Length; i++)
- {
- var folder = args.FileSystemChildren[i];
-
- if (!folder.IsDirectory)
- {
- continue;
- }
-
- item = ResolveFromFolderName(folder.Path);
-
- if (item != null)
- {
- return item;
- }
- }
- }
-
- return null;
- }
-
- private T ResolveFromFolderName(string folder)
- {
- if (folder.IndexOf("video_ts", System.StringComparison.OrdinalIgnoreCase) != -1)
- {
- return new T
- {
- VideoType = VideoType.Dvd,
- Path = Path.GetDirectoryName(folder)
- };
- }
- if (folder.IndexOf("bdmv", System.StringComparison.OrdinalIgnoreCase) != -1)
- {
- return new T
- {
- VideoType = VideoType.BluRay,
- Path = Path.GetDirectoryName(folder)
- };
- }
-
- return null;
- }
-
- }
-}
+using MediaBrowser.Controller.Entities;
+using MediaBrowser.Controller.Library;
+using MediaBrowser.Model.Entities;
+using System;
+using System.ComponentModel.Composition;
+using System.IO;
+
+namespace MediaBrowser.Controller.Resolvers
+{
+ /// <summary>
+ /// Resolves a Path into a Video
+ /// </summary>
+ [Export(typeof(IBaseItemResolver))]
+ public class VideoResolver : BaseVideoResolver<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 BaseVideoResolver<T> : BaseItemResolver<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;
+ }
+ }
+}