aboutsummaryrefslogtreecommitdiff
path: root/MediaBrowser.Controller/Resolvers
diff options
context:
space:
mode:
authorLukePulverenti Luke Pulverenti luke pulverenti <LukePulverenti Luke Pulverenti luke.pulverenti@gmail.com>2012-09-08 15:05:18 -0400
committerLukePulverenti Luke Pulverenti luke pulverenti <LukePulverenti Luke Pulverenti luke.pulverenti@gmail.com>2012-09-08 15:05:18 -0400
commit8b39ed2f63a08597e1faf368c3c0506dbe960127 (patch)
tree1fa7e8b98e7e25323a74d486c8bf8746081e648d /MediaBrowser.Controller/Resolvers
parent2884df296c35d615065e6c6ce4685197a424c707 (diff)
Moved TV into the main project and added Series properties to DTOBaseItem
Diffstat (limited to 'MediaBrowser.Controller/Resolvers')
-rw-r--r--MediaBrowser.Controller/Resolvers/TV/EpisodeResolver.cs21
-rw-r--r--MediaBrowser.Controller/Resolvers/TV/SeasonResolver.cs35
-rw-r--r--MediaBrowser.Controller/Resolvers/TV/SeriesResolver.cs64
-rw-r--r--MediaBrowser.Controller/Resolvers/TV/TVUtils.cs166
4 files changed, 286 insertions, 0 deletions
diff --git a/MediaBrowser.Controller/Resolvers/TV/EpisodeResolver.cs b/MediaBrowser.Controller/Resolvers/TV/EpisodeResolver.cs
new file mode 100644
index 0000000000..6d1261dfb0
--- /dev/null
+++ b/MediaBrowser.Controller/Resolvers/TV/EpisodeResolver.cs
@@ -0,0 +1,21 @@
+using MediaBrowser.Controller.Library;
+using MediaBrowser.Model.Entities.TV;
+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;
+ }
+ }
+}
diff --git a/MediaBrowser.Controller/Resolvers/TV/SeasonResolver.cs b/MediaBrowser.Controller/Resolvers/TV/SeasonResolver.cs
new file mode 100644
index 0000000000..0bb880b787
--- /dev/null
+++ b/MediaBrowser.Controller/Resolvers/TV/SeasonResolver.cs
@@ -0,0 +1,35 @@
+using MediaBrowser.Controller.Library;
+using MediaBrowser.Model.Entities.TV;
+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)
+ {
+ Season season = new Season();
+
+ season.IndexNumber = TVUtils.GetSeasonNumberFromPath(args.Path);
+
+ // Gather these now so that the episode provider classes can utilize them instead of having to make their own file system calls
+ if (args.ContainsFolder("metadata"))
+ {
+ season.MetadataFiles = Directory.GetFiles(Path.Combine(args.Path, "metadata"), "*", SearchOption.TopDirectoryOnly);
+ }
+ else
+ {
+ season.MetadataFiles = new string[] { };
+ }
+
+ return season;
+ }
+
+ return null;
+ }
+ }
+}
diff --git a/MediaBrowser.Controller/Resolvers/TV/SeriesResolver.cs b/MediaBrowser.Controller/Resolvers/TV/SeriesResolver.cs
new file mode 100644
index 0000000000..dd82b14484
--- /dev/null
+++ b/MediaBrowser.Controller/Resolvers/TV/SeriesResolver.cs
@@ -0,0 +1,64 @@
+using MediaBrowser.Controller.Library;
+using MediaBrowser.Model.Entities;
+using MediaBrowser.Model.Entities.TV;
+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)
+ {
+ string srch = "[tvdbid=";
+ 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.Tvdb, id);
+ }
+ }
+ }
+}
diff --git a/MediaBrowser.Controller/Resolvers/TV/TVUtils.cs b/MediaBrowser.Controller/Resolvers/TV/TVUtils.cs
new file mode 100644
index 0000000000..ebfcda6028
--- /dev/null
+++ b/MediaBrowser.Controller/Resolvers/TV/TVUtils.cs
@@ -0,0 +1,166 @@
+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 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;
+ }
+ else
+ {
+ nonSeriesFolders++;
+
+ if (nonSeriesFolders >= 3)
+ {
+ return false;
+ }
+ }
+ }
+ else
+ {
+ if (!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;
+ }
+ }
+}