diff options
| author | LukePulverenti <luke.pulverenti@gmail.com> | 2013-02-20 20:33:05 -0500 |
|---|---|---|
| committer | LukePulverenti <luke.pulverenti@gmail.com> | 2013-02-20 20:33:05 -0500 |
| commit | 767cdc1f6f6a63ce997fc9476911e2c361f9d402 (patch) | |
| tree | 49add55976f895441167c66cfa95e5c7688d18ce /MediaBrowser.Controller/Resolvers | |
| parent | 845554722efaed872948a9e0f7202e3ef52f1b6e (diff) | |
Pushing missing changes
Diffstat (limited to 'MediaBrowser.Controller/Resolvers')
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; + } + } +} |
