diff --git a/NzbDrone.Core.Test/Files/RSS/nzbmatrix.xml b/NzbDrone.Core.Test/Files/RSS/nzbmatrix.xml
index 4c422f780..71872272e 100644
--- a/NzbDrone.Core.Test/Files/RSS/nzbmatrix.xml
+++ b/NzbDrone.Core.Test/Files/RSS/nzbmatrix.xml
@@ -148,16 +148,6 @@
6
-
- Fussball Bundesliga 2010 2011 30 Spieltag FC Bayern Muenchen vs Bayer 04 Leverkusen German WS dTV XviD WoGS
- http://nzbmatrix.com/nzb-details.php?id=914423&hit=1
- http://nzbmatrix.com/nzb-details.php?id=914423&hit=1
- Name: Fussball Bundesliga 2010 2011 30 Spieltag FC Bayern Muenchen vs Bayer 04 Leverkusen German WS dTV XviD WoGS Category: TV: Divx/Xvid Size: 1.28 GB Added: 2011-04-25 11:41:35 Group: alt.binaries.multimedia NFO:View NFO ]]>
- TV: Divx/Xvid
- tv.divx/xvid
- 6
-
- How I Met Your Mother S06E20 The Exploding Meatball Sub German Custom Subbed WS HDTV XviD iNTERNAL BaCKToRGhttp://nzbmatrix.com/nzb-details.php?id=914420&hit=1
diff --git a/NzbDrone.Core.Test/ParserTest.cs b/NzbDrone.Core.Test/ParserTest.cs
index 3704a7263..1ca1b1ab9 100644
--- a/NzbDrone.Core.Test/ParserTest.cs
+++ b/NzbDrone.Core.Test/ParserTest.cs
@@ -1,10 +1,12 @@
// ReSharper disable RedundantUsingDirective
using System;
+using System.Linq;
using FluentAssertions;
using NUnit.Framework;
using NzbDrone.Core.Model;
using NzbDrone.Core.Repository.Quality;
using NzbDrone.Core.Test.Framework;
+using NzbDrone.Test.Common;
namespace NzbDrone.Core.Test
{
@@ -52,10 +54,10 @@ namespace NzbDrone.Core.Test
public void ParseTitle_single(string postTitle, string title, int seasonNumber, int episodeNumber)
{
var result = Parser.ParseTitle(postTitle);
+ result.EpisodeNumbers.Should().HaveCount(1);
result.SeasonNumber.Should().Be(seasonNumber);
- result.EpisodeNumbers[0].Should().Be(episodeNumber);
+ result.EpisodeNumbers.First().Should().Be(episodeNumber);
result.CleanTitle.Should().Be(Parser.NormalizeTitle(title));
- result.EpisodeNumbers.Count.Should().Be(1);
}
[Test]
@@ -68,12 +70,15 @@ namespace NzbDrone.Core.Test
[TestCase(@"P:\TV Shows\House\Season 6\S06E13 - 5 to 9 - 720p BluRay.mkv", 6, 13)]
[TestCase(@"S:\TV Drop\House - 10x11 - Title [SDTV]\1011 - Title.avi", 10, 11)]
[TestCase(@"S:\TV Drop\King of the Hill - 10x12 - 24 Hour Propane People [SDTV]\1012 - 24 Hour Propane People.avi", 10, 12)]
+ [TestCase(@"S:\TV Drop\King of the Hill - 10x12 - 24 Hour Propane People [SDTV]\Hour Propane People.avi", 10, 12)]
public void PathParse_tests(string path, int season, int episode)
{
var result = Parser.ParsePath(path);
result.EpisodeNumbers.Should().HaveCount(1);
result.SeasonNumber.Should().Be(season);
result.EpisodeNumbers[0].Should().Be(episode);
+
+ ExceptionVerification.IgnoreWarns();
}
[TestCase("WEEDS.S03E01-06.DUAL.BDRip.XviD.AC3.-HELLYWOOD", QualityTypes.DVD)]
@@ -167,7 +172,7 @@ namespace NzbDrone.Core.Test
[TestCase("2011.01.10 - Denis Leary - HD TV.mkv", "", 2011, 1, 10)]
[TestCase("2011.03.13 - Denis Leary - HD TV.mkv", "", 2011, 3, 13)]
[TestCase("The Tonight Show with Jay Leno - 2011-06-16 - Larry David, \"Bachelorette\" Ashley Hebert, Pitbull with Ne-Yo", "The Tonight Show with Jay Leno", 2011, 6, 16)]
- public void episode_daily_parse(string postTitle, string title, int year, int month, int day)
+ public void parse_daily_episodes(string postTitle, string title, int year, int month, int day)
{
var result = Parser.ParseTitle(postTitle);
var airDate = new DateTime(year, month, day);
@@ -176,6 +181,16 @@ namespace NzbDrone.Core.Test
Assert.IsNull(result.EpisodeNumbers);
}
+ [Test]
+ public void parse_daily_should_fail_if_episode_is_far_in_future()
+ {
+ var title = string.Format("{0}.{1}.{2} - Denis Leary - HD TV.mkv", DateTime.Now.Year, DateTime.Now.Month, DateTime.Now.AddDays(2).Day);
+
+ Parser.ParseTitle(title).Should().BeNull();
+
+ ExceptionVerification.ExcpectedWarns(1);
+ }
+
[TestCase("30.Rock.Season.04.HDTV.XviD-DIMENSION", "30.Rock", 4)]
[TestCase("Parks.and.Recreation.S02.720p.x264-DIMENSION", "Parks.and.Recreation", 2)]
@@ -345,6 +360,7 @@ namespace NzbDrone.Core.Test
var result = Parser.ParseTitle(postTitle);
result.Should().BeNull();
+ ExceptionVerification.ExcpectedWarns(1);
}
[TestCase("Lie.to.Me.S03.SUBPACK.DVDRip.XviD-REWARD")]
@@ -355,6 +371,16 @@ namespace NzbDrone.Core.Test
var result = Parser.ParseTitle(postTitle);
result.Should().BeNull();
+
+ ExceptionVerification.ExcpectedWarns(1);
+ }
+
+ [TestCase("Fussball Bundesliga 2010 2011 30 Spieltag FC Bayern Muenchen vs Bayer 04 Leverkusen German WS dTV XviD WoGS")]
+ public void unparsable_should_log_error_but_not_throw(string title)
+ {
+ Parser.ParseTitle(title);
+ ExceptionVerification.IgnoreWarns();
+ ExceptionVerification.ExcpectedErrors(1);
}
}
}
diff --git a/NzbDrone.Core.Test/ProviderTests/DiskScanProviderTest_ImportFile.cs b/NzbDrone.Core.Test/ProviderTests/DiskScanProviderTest_ImportFile.cs
index 57cf7ad38..f3db56dba 100644
--- a/NzbDrone.Core.Test/ProviderTests/DiskScanProviderTest_ImportFile.cs
+++ b/NzbDrone.Core.Test/ProviderTests/DiskScanProviderTest_ImportFile.cs
@@ -137,7 +137,7 @@ namespace NzbDrone.Core.Test.ProviderTests
//Assert
VerifySkipImport(result, mocker);
- ExceptionVerification.ExcpectedWarns(1);
+ ExceptionVerification.ExcpectedWarns(2);
}
[Test]
diff --git a/NzbDrone.Core.Test/ProviderTests/LogProviderTests/LogProviderFixture.cs b/NzbDrone.Core.Test/ProviderTests/LogProviderTests/LogProviderFixture.cs
index 34d00f77d..fbd9616f2 100644
--- a/NzbDrone.Core.Test/ProviderTests/LogProviderTests/LogProviderFixture.cs
+++ b/NzbDrone.Core.Test/ProviderTests/LogProviderTests/LogProviderFixture.cs
@@ -155,7 +155,7 @@ namespace NzbDrone.Core.Test.ProviderTests.LogProviderTests
//Assert
var result = Db.Fetch();
- result.Should().HaveCount(21);
+ result.Should().HaveCount(20);
result.Should().OnlyContain(s => s.Time > DateTime.Now.AddDays(-30));
}
diff --git a/NzbDrone.Core/Instrumentation/DatabaseTarget.cs b/NzbDrone.Core/Instrumentation/DatabaseTarget.cs
index 41c23ef2f..2e895825b 100644
--- a/NzbDrone.Core/Instrumentation/DatabaseTarget.cs
+++ b/NzbDrone.Core/Instrumentation/DatabaseTarget.cs
@@ -24,7 +24,7 @@ namespace NzbDrone.Core.Instrumentation
public void Register()
{
LogManager.Configuration.AddTarget("DbLogger", this);
- LogManager.Configuration.LoggingRules.Add(new LoggingRule("*", LogLevel.Debug, this));
+ LogManager.Configuration.LoggingRules.Add(new LoggingRule("*", LogLevel.Info, this));
LogManager.ConfigurationReloaded += (sender, args) => Register();
LogConfiguration.Reload();
diff --git a/NzbDrone.Core/Parser.cs b/NzbDrone.Core/Parser.cs
index 126784e38..a729b4255 100644
--- a/NzbDrone.Core/Parser.cs
+++ b/NzbDrone.Core/Parser.cs
@@ -62,116 +62,127 @@ namespace NzbDrone.Core
RegexOptions.IgnoreCase | RegexOptions.Compiled);
- ///
- /// Parses a file path into list of episodes it contains
- ///
- /// Path of the file to parse
- /// List of episodes contained in the file
internal static EpisodeParseResult ParsePath(string path)
{
var fileInfo = new FileInfo(path);
- return ParseTitle(fileInfo.Name);
+
+ var result = ParseTitle(fileInfo.Name);
+
+ if (result == null)
+ {
+ Logger.Trace("Attempting to parse episode info using full path. {0}", fileInfo.FullName);
+ result = ParseTitle(fileInfo.FullName);
+ }
+
+ return result;
}
- ///
- /// Parses a post title into list of episodes it contains
- ///
- /// Title of the report
- /// List of episodes contained in the post
+
internal static EpisodeParseResult ParseTitle(string title)
{
- Logger.Trace("Parsing string '{0}'", title);
- var simpleTitle = SimpleTitleRegex.Replace(title, String.Empty);
-
- foreach (var regex in ReportTitleRegex)
+ try
{
- //Use only the filename, not the entire path
- var match = regex.Matches(simpleTitle);
+ Logger.Trace("Parsing string '{0}'", title);
+ var simpleTitle = SimpleTitleRegex.Replace(title, String.Empty);
- if (match.Count != 0)
+ foreach (var regex in ReportTitleRegex)
{
- var seriesName = NormalizeTitle(match[0].Groups["title"].Value);
-
- int airyear;
- Int32.TryParse(match[0].Groups["airyear"].Value, out airyear);
+ var match = regex.Matches(simpleTitle);
- EpisodeParseResult parsedEpisode;
-
- if (airyear < 1)
+ if (match.Count != 0)
{
- var seasons = new List();
-
- foreach (Capture seasonCapture in match[0].Groups["season"].Captures)
+ var result = ParseMatchCollection(match);
+ if (result != null)
{
- int s;
- if (Int32.TryParse(seasonCapture.Value, out s))
- seasons.Add(s);
+ //Check if episode is in the future (most likley a parse error)
+ if (result.AirDate > DateTime.Now.AddDays(1).Date)
+ break;
+
+ result.Language = ParseLanguage(title);
+ result.Quality = ParseQuality(title);
+ return result;
}
+ }
+ }
+ Logger.Warn("Unable to parse episode info. {0}", title);
+ }
+ catch (Exception e)
+ {
+ Logger.Error("An error has occurred while trying to parse '{0}'", title);
+ }
+ return null;
+ }
- //If more than 1 season was parsed go to the next REGEX (A multi-season release is unlikely)
- if (seasons.Distinct().Count() != 1)
- continue;
+ private static EpisodeParseResult ParseMatchCollection(MatchCollection matchCollection)
+ {
+ var seriesName = NormalizeTitle(matchCollection[0].Groups["title"].Value);
- var season = seasons[0];
+ int airyear;
+ Int32.TryParse(matchCollection[0].Groups["airyear"].Value, out airyear);
- parsedEpisode = new EpisodeParseResult
- {
- CleanTitle = seriesName,
- SeasonNumber = season,
- EpisodeNumbers = new List()
- };
+ EpisodeParseResult parsedEpisode;
- foreach (Match matchGroup in match)
- {
- var count = matchGroup.Groups["episode"].Captures.Count;
+ if (airyear < 1900)
+ {
+ var seasons = new List();
- //Allows use to return a list of 0 episodes (We can handle that as a full season release)
- if (count > 0)
- {
- var first = Convert.ToInt32(matchGroup.Groups["episode"].Captures[0].Value);
- var last = Convert.ToInt32(matchGroup.Groups["episode"].Captures[count - 1].Value);
+ foreach (Capture seasonCapture in matchCollection[0].Groups["season"].Captures)
+ {
+ int parsedSeason;
+ if (Int32.TryParse(seasonCapture.Value, out parsedSeason))
+ seasons.Add(parsedSeason);
+ }
- for (int i = first; i <= last; i++)
- {
- parsedEpisode.EpisodeNumbers.Add(i);
- }
- }
+ //If more than 1 season was parsed go to the next REGEX (A multi-season release is unlikely)
+ if (seasons.Distinct().Count() != 1)
+ return null;
- else
- {
- //Check to see if this is an "Extras" or "SUBPACK" release, if it is, return NULL
- //Todo: Set a "Extras" flag in EpisodeParseResult if we want to download them ever
- if (!String.IsNullOrEmpty(match[0].Groups["extras"].Value))
- return null;
+ parsedEpisode = new EpisodeParseResult
+ {
+ SeasonNumber = seasons.First(),
+ EpisodeNumbers = new List()
+ };
- parsedEpisode.FullSeason = true;
- }
- }
- }
+ foreach (Match matchGroup in matchCollection)
+ {
+ var episodeCaptures = matchGroup.Groups["episode"].Captures.Cast().ToList();
+ //Allows use to return a list of 0 episodes (We can handle that as a full season release)
+ if (episodeCaptures.Any())
+ {
+ var first = Convert.ToInt32(episodeCaptures.First().Value);
+ var last = Convert.ToInt32(episodeCaptures.Last().Value);
+ parsedEpisode.EpisodeNumbers = Enumerable.Range(first, last - first + 1).ToList();
+ }
else
{
- //Try to Parse as a daily show
- var airmonth = Convert.ToInt32(match[0].Groups["airmonth"].Value);
- var airday = Convert.ToInt32(match[0].Groups["airday"].Value);
+ //Check to see if this is an "Extras" or "SUBPACK" release, if it is, return NULL
+ //Todo: Set a "Extras" flag in EpisodeParseResult if we want to download them ever
+ if (!String.IsNullOrWhiteSpace(matchCollection[0].Groups["extras"].Value))
+ return null;
- parsedEpisode = new EpisodeParseResult
- {
- CleanTitle = seriesName,
- AirDate = new DateTime(airyear, airmonth, airday),
- Language = ParseLanguage(simpleTitle)
- };
+ parsedEpisode.FullSeason = true;
}
+ }
+ }
- parsedEpisode.Quality = ParseQuality(title);
-
- Logger.Trace("Episode Parsed. {0}", parsedEpisode);
+ else
+ {
+ //Try to Parse as a daily show
+ var airmonth = Convert.ToInt32(matchCollection[0].Groups["airmonth"].Value);
+ var airday = Convert.ToInt32(matchCollection[0].Groups["airday"].Value);
- return parsedEpisode;
- }
+ parsedEpisode = new EpisodeParseResult
+ {
+ AirDate = new DateTime(airyear, airmonth, airday).Date,
+ };
}
- Logger.Warn("Unable to parse episode info. {0}", title);
- return null;
+
+ parsedEpisode.CleanTitle = seriesName;
+
+ Logger.Trace("Episode Parsed. {0}", parsedEpisode);
+
+ return parsedEpisode;
}
///
@@ -294,7 +305,7 @@ namespace NzbDrone.Core
return result;
}
- public static LanguageType ParseLanguage(string title)
+ internal static LanguageType ParseLanguage(string title)
{
var lowerTitle = title.ToLower();
diff --git a/NzbDrone.Core/Providers/Jobs/JobProvider.cs b/NzbDrone.Core/Providers/Jobs/JobProvider.cs
index 0ff1b4642..3db6d4ea1 100644
--- a/NzbDrone.Core/Providers/Jobs/JobProvider.cs
+++ b/NzbDrone.Core/Providers/Jobs/JobProvider.cs
@@ -208,8 +208,8 @@ namespace NzbDrone.Core.Providers.Jobs
if (Queue.Count != 0)
{
job = Queue.First();
+ logger.Trace("Popping {0} from the queue.", job);
Queue.Remove(job);
- logger.Debug("Popping {0} from the queue.", job);
}
}
diff --git a/NzbDrone.Web/App_Start/EntityFramework.SqlServerCompact.cs b/NzbDrone.Web/App_Start/EntityFramework.SqlServerCompact.cs
new file mode 100644
index 000000000..a579f7479
--- /dev/null
+++ b/NzbDrone.Web/App_Start/EntityFramework.SqlServerCompact.cs
@@ -0,0 +1,12 @@
+using System.Data.Entity;
+using System.Data.Entity.Infrastructure;
+
+[assembly: WebActivator.PreApplicationStartMethod(typeof(NzbDrone.Web.App_Start.EntityFramework_SqlServerCompact), "Start")]
+
+namespace NzbDrone.Web.App_Start {
+ public static class EntityFramework_SqlServerCompact {
+ public static void Start() {
+ Database.DefaultConnectionFactory = new SqlCeConnectionFactory("System.Data.SqlServerCe.4.0");
+ }
+ }
+}
diff --git a/NzbDrone.Web/App_Start/MiniProfiler.cs b/NzbDrone.Web/App_Start/MiniProfiler.cs
new file mode 100644
index 000000000..11f7c4410
--- /dev/null
+++ b/NzbDrone.Web/App_Start/MiniProfiler.cs
@@ -0,0 +1,95 @@
+using System.Web;
+using System.Web.Mvc;
+using System.Linq;
+using MvcMiniProfiler;
+using MvcMiniProfiler.MVCHelpers;
+using Microsoft.Web.Infrastructure.DynamicModuleHelper;
+//using System.Data;
+//using System.Data.Entity;
+//using System.Data.Entity.Infrastructure;
+
+//using MvcMiniProfiler.Data.Linq2Sql;
+
+[assembly: WebActivator.PreApplicationStartMethod(
+ typeof(NzbDrone.Web.App_Start.MiniProfilerPackage), "PreStart")]
+
+[assembly: WebActivator.PostApplicationStartMethod(
+ typeof(NzbDrone.Web.App_Start.MiniProfilerPackage), "PostStart")]
+
+
+namespace NzbDrone.Web.App_Start
+{
+ public static class MiniProfilerPackage
+ {
+ public static void PreStart()
+ {
+
+ // Be sure to restart you ASP.NET Developement server, this code will not run until you do that.
+
+ //TODO: See - _MINIPROFILER UPDATED Layout.cshtml
+ // For profiling to display in the UI you will have to include the line @MvcMiniProfiler.MiniProfiler.RenderIncludes()
+ // in your master layout
+
+ //TODO: Non SQL Server based installs can use other formatters like: new MvcMiniProfiler.SqlFormatters.InlineFormatter()
+ MiniProfiler.Settings.SqlFormatter = new MvcMiniProfiler.SqlFormatters.SqlServerFormatter();
+
+ //TODO: To profile a standard DbConnection:
+ // var profiled = new ProfiledDbConnection(cnn, MiniProfiler.Current);
+
+ //TODO: If you are profiling EF code first try:
+ // MiniProfilerEF.Initialize();
+
+ //Make sure the MiniProfiler handles BeginRequest and EndRequest
+ DynamicModuleUtility.RegisterModule(typeof(MiniProfilerStartupModule));
+
+ //Setup profiler for Controllers via a Global ActionFilter
+ GlobalFilters.Filters.Add(new ProfilingActionFilter());
+ }
+
+ public static void PostStart()
+ {
+ // Intercept ViewEngines to profile all partial views and regular views.
+ // If you prefer to insert your profiling blocks manually you can comment this out
+ var copy = ViewEngines.Engines.ToList();
+ ViewEngines.Engines.Clear();
+ foreach (var item in copy)
+ {
+ ViewEngines.Engines.Add(new ProfilingViewEngine(item));
+ }
+ }
+ }
+
+ public class MiniProfilerStartupModule : IHttpModule
+ {
+ public void Init(HttpApplication context)
+ {
+ context.BeginRequest += (sender, e) =>
+ {
+ var request = ((HttpApplication)sender).Request;
+ //TODO: By default only local requests are profiled, optionally you can set it up
+ // so authenticated users are always profiled
+ if (request.IsLocal) { MiniProfiler.Start(); }
+ };
+
+
+ // TODO: You can control who sees the profiling information
+ /*
+ context.AuthenticateRequest += (sender, e) =>
+ {
+ if (!CurrentUserIsAllowedToSeeProfiler())
+ {
+ MvcMiniProfiler.MiniProfiler.Stop(discardResults: true);
+ }
+ };
+ */
+
+ context.EndRequest += (sender, e) =>
+ {
+ MiniProfiler.Stop();
+ };
+ }
+
+ public void Dispose() { }
+ }
+}
+
diff --git a/NzbDrone.Web/Global.asax.cs b/NzbDrone.Web/Global.asax.cs
index b1919b223..b8c7a200e 100644
--- a/NzbDrone.Web/Global.asax.cs
+++ b/NzbDrone.Web/Global.asax.cs
@@ -4,18 +4,14 @@ using System.Linq;
using System.Reflection;
using System.Threading;
using System.Web;
-using System.Web.Caching;
using System.Web.Mvc;
using System.Web.Routing;
-using MvcMiniProfiler;
using NLog.Config;
using Ninject;
using Ninject.Web.Mvc;
using NLog;
using NzbDrone.Common;
using NzbDrone.Core;
-using NzbDrone.Core.Instrumentation;
-using Telerik.Web.Mvc;
namespace NzbDrone.Web
{
@@ -99,13 +95,11 @@ namespace NzbDrone.Web
protected void Application_BeginRequest()
{
- Thread.CurrentThread.Name = "UI";
- MiniProfiler.Start();
+ Thread.CurrentThread.Name = "WEB_THREAD";
}
protected void Application_EndRequest()
{
- MiniProfiler.Stop();
}
}
}
\ No newline at end of file
diff --git a/NzbDrone.Web/NzbDrone.Web.csproj b/NzbDrone.Web/NzbDrone.Web.csproj
index 86c876b5b..be31737e8 100644
--- a/NzbDrone.Web/NzbDrone.Web.csproj
+++ b/NzbDrone.Web/NzbDrone.Web.csproj
@@ -46,23 +46,20 @@
- False..\packages\EntityFramework.4.2.0.0\lib\net40\EntityFramework.dll
+
- False
- ..\packages\Microsoft.Web.Infrastructure.1.0.0.0\lib\net40\Microsoft.Web.Infrastructure.dllTrue
+ ..\packages\Microsoft.Web.Infrastructure.1.0.0.0\lib\net40\Microsoft.Web.Infrastructure.dll
-
- False..\packages\MiniProfiler.1.9\lib\net40\MvcMiniProfiler.dll..\packages\Ninject.2.2.1.4\lib\net40-Full\Ninject.dll
-
+ ..\packages\Ninject.MVC3.2.2.2.0\lib\net40-Full\Ninject.Web.Mvc.dll
@@ -118,6 +115,10 @@
False..\Libraries\TvdbLib.dll
+
+ False
+ ..\packages\WebActivator.1.5\lib\net40\WebActivator.dll
+
@@ -174,6 +175,8 @@
TrueUploadLocalization.en-US.resx
+
+
@@ -486,9 +489,6 @@
-
-
-
diff --git a/NzbDrone.Web/Views/Log/Index.cshtml b/NzbDrone.Web/Views/Log/Index.cshtml
index 2e3f5b9b1..dcae34133 100644
--- a/NzbDrone.Web/Views/Log/Index.cshtml
+++ b/NzbDrone.Web/Views/Log/Index.cshtml
@@ -24,7 +24,11 @@
Logs
}
@section ActionMenu{
- @{Html.RenderPartial("SubMenu");}
+