Updated folder mapping logic. Added unmapped folder view.

pull/3113/head
Keivan 14 years ago
parent beaf0cf939
commit 9d52229e04

@ -23,5 +23,11 @@ namespace NzbDrone.Core.Test
}
}
[SetUp]
public void Setup()
{
Main.ConfigureNlog();
}
}
}

@ -68,8 +68,47 @@ namespace NzbDrone.Core.Test
moqData.Verify(c => c.Add(It.IsAny<Series>()), Times.Once());
}
[Test]
[Row(0)]
[Row(1)]
[Row(2)]
[Row(3)]
public void register_series_with_match(int matchPosition)
{
TvdbSeries fakeSeries = Builder<TvdbSeries>.CreateNew().With(f => f.SeriesName = "The Simpsons").Build();
var fakeSearch = Builder<TvdbSearchResult>.CreateListOfSize(4).Build();
fakeSearch[matchPosition].Id = fakeSeries.Id;
fakeSearch[matchPosition].SeriesName = fakeSeries.SeriesName;
//Arrange
var moqData = new Mock<IRepository>();
var moqTvdb = new Mock<ITvDbProvider>();
moqData.Setup(f => f.Exists<Series>(c => c.TvdbId == It.IsAny<long>())).Returns(false);
moqTvdb.Setup(f => f.SearchSeries(It.IsAny<String>())).Returns(fakeSearch);
moqTvdb.Setup(f => f.GetSeries(fakeSeries.Id, It.IsAny<TvdbLanguage>())).Returns(fakeSeries);
var kernel = new MockingKernel();
kernel.Bind<IRepository>().ToConstant(moqData.Object);
kernel.Bind<ITvDbProvider>().ToConstant(moqTvdb.Object);
kernel.Bind<ISeriesProvider>().To<SeriesProvider>();
//Act
var seriesController = kernel.Get<ISeriesProvider>();
seriesController.RegisterSeries(@"D:\TV Shows\The Simpsons");
//Assert
//Verify that the show was added to the database only once.
moqData.Verify(c => c.Add(It.Is<Series>(d => d.TvdbId == fakeSeries.Id)), Times.Once());
}
[Test]
[Description("This test confirms that the tvdb id stored in the db is preserved rather than being replaced by an auto incrementing value")]
public void tvdbid_is_preserved([RandomNumbers(Minimum = 100, Maximum = 999, Count = 1)] int tvdbId)
{
@ -86,5 +125,26 @@ namespace NzbDrone.Core.Test
Assert.IsNotEmpty(allSeries);
Assert.AreEqual(tvdbId, allSeries.First().TvdbId);
}
[Test]
[Row(new object[] { "CAPITAL", "capital", true })]
[Row(new object[] { "Something!!", "Something", true })]
[Row(new object[] { "Simpsons 2000", "Simpsons", true })]
[Row(new object[] { "Simp222sons", "Simpsons", true })]
[Row(new object[] { "Simpsons", "The Simpsons", true })]
[Row(new object[] { "Law and order", "Law & order", true })]
[Row(new object[] { "xxAndxx", "xxxx", false })]
[Row(new object[] { "Andxx", "xx", false })]
[Row(new object[] { "xxAnd", "xx", false })]
[Row(new object[] { "Thexx", "xx", false })]
[Row(new object[] { "Thexx", "xx", false })]
[Row(new object[] { "xxThexx", "xxxxx", false })]
[Row(new object[] { "Simpsons The", "Simpsons", true })]
public void Name_match_test(string a, string b, bool match)
{
bool result = SeriesProvider.IsTitleMatch(a, b);
Assert.AreEqual(match, result, "{0} , {1}", a, b);
}
}
}

@ -0,0 +1,432 @@
<?xml version="1.0"?>
<doc>
<assembly>
<name>NLog.Extended</name>
</assembly>
<members>
<member name="T:NLog.LayoutRenderers.AspNetApplicationValueLayoutRenderer">
<summary>
ASP.NET Application variable.
</summary>
<remarks>
Use this layout renderer to insert the value of the specified variable stored
in the ASP.NET Application dictionary.
</remarks>
<example>
<para>You can set the value of an ASP.NET Application variable by using the following code:</para>
<code lang="C#">
<![CDATA[
HttpContext.Current.Application["myvariable"] = 123;
HttpContext.Current.Application["stringvariable"] = "aaa BBB";
HttpContext.Current.Application["anothervariable"] = DateTime.Now;
]]>
</code>
<para>Example usage of ${aspnet-application}:</para>
<code lang="NLog Layout Renderer">
${aspnet-application:variable=myvariable} - produces "123"
${aspnet-application:variable=anothervariable} - produces "01/01/2006 00:00:00"
${aspnet-application:variable=anothervariable:culture=pl-PL} - produces "2006-01-01 00:00:00"
${aspnet-application:variable=myvariable:padding=5} - produces " 123"
${aspnet-application:variable=myvariable:padding=-5} - produces "123 "
${aspnet-application:variable=stringvariable:upperCase=true} - produces "AAA BBB"
</code>
</example>
</member>
<member name="M:NLog.LayoutRenderers.AspNetApplicationValueLayoutRenderer.Append(System.Text.StringBuilder,NLog.LogEventInfo)">
<summary>
Renders the specified ASP.NET Application variable and appends it to the specified <see cref="T:System.Text.StringBuilder"/>.
</summary>
<param name="builder">The <see cref="T:System.Text.StringBuilder"/> to append the rendered data to.</param>
<param name="logEvent">Logging event.</param>
</member>
<member name="P:NLog.LayoutRenderers.AspNetApplicationValueLayoutRenderer.Variable">
<summary>
Gets or sets the variable name.
</summary>
<docgen category='Rendering Options' order='10' />
</member>
<member name="T:NLog.LayoutRenderers.AspNetRequestValueLayoutRenderer">
<summary>
ASP.NET Request variable.
</summary>
<remarks>
Use this layout renderer to insert the value of the specified parameter of the
ASP.NET Request object.
</remarks>
<example>
<para>Example usage of ${aspnet-request}:</para>
<code lang="NLog Layout Renderer">
${aspnet-request:item=v}
${aspnet-request:querystring=v}
${aspnet-request:form=v}
${aspnet-request:cookie=v}
${aspnet-request:serverVariable=v}
</code>
</example>
</member>
<member name="M:NLog.LayoutRenderers.AspNetRequestValueLayoutRenderer.Append(System.Text.StringBuilder,NLog.LogEventInfo)">
<summary>
Renders the specified ASP.NET Request variable and appends it to the specified <see cref="T:System.Text.StringBuilder"/>.
</summary>
<param name="builder">The <see cref="T:System.Text.StringBuilder"/> to append the rendered data to.</param>
<param name="logEvent">Logging event.</param>
</member>
<member name="P:NLog.LayoutRenderers.AspNetRequestValueLayoutRenderer.Item">
<summary>
Gets or sets the item name. The QueryString, Form, Cookies, or ServerVariables collection variables having the specified name are rendered.
</summary>
<docgen category='Rendering Options' order='10' />
</member>
<member name="P:NLog.LayoutRenderers.AspNetRequestValueLayoutRenderer.QueryString">
<summary>
Gets or sets the QueryString variable to be rendered.
</summary>
<docgen category='Rendering Options' order='10' />
</member>
<member name="P:NLog.LayoutRenderers.AspNetRequestValueLayoutRenderer.Form">
<summary>
Gets or sets the form variable to be rendered.
</summary>
<docgen category='Rendering Options' order='10' />
</member>
<member name="P:NLog.LayoutRenderers.AspNetRequestValueLayoutRenderer.Cookie">
<summary>
Gets or sets the cookie to be rendered.
</summary>
<docgen category='Rendering Options' order='10' />
</member>
<member name="P:NLog.LayoutRenderers.AspNetRequestValueLayoutRenderer.ServerVariable">
<summary>
Gets or sets the ServerVariables item to be rendered.
</summary>
<docgen category='Rendering Options' order='10' />
</member>
<member name="T:NLog.LayoutRenderers.AspNetSessionIDLayoutRenderer">
<summary>
ASP.NET Session ID.
</summary>
</member>
<member name="M:NLog.LayoutRenderers.AspNetSessionIDLayoutRenderer.Append(System.Text.StringBuilder,NLog.LogEventInfo)">
<summary>
Renders the ASP.NET Session ID appends it to the specified <see cref="T:System.Text.StringBuilder"/>.
</summary>
<param name="builder">The <see cref="T:System.Text.StringBuilder"/> to append the rendered data to.</param>
<param name="logEvent">Logging event.</param>
</member>
<member name="T:NLog.LayoutRenderers.AspNetSessionValueLayoutRenderer">
<summary>
ASP.NET Session variable.
</summary>
<remarks>
Use this layout renderer to insert the value of the specified variable stored
in the ASP.NET Session dictionary.
</remarks>
<example>
<para>You can set the value of an ASP.NET Session variable by using the following code:</para>
<code lang="C#">
<![CDATA[
HttpContext.Current.Session["myvariable"] = 123;
HttpContext.Current.Session["stringvariable"] = "aaa BBB";
HttpContext.Current.Session["anothervariable"] = DateTime.Now;
]]>
</code>
<para>Example usage of ${aspnet-session}:</para>
<code lang="NLog Layout Renderer">
${aspnet-session:variable=myvariable} - produces "123"
${aspnet-session:variable=anothervariable} - produces "01/01/2006 00:00:00"
${aspnet-session:variable=anothervariable:culture=pl-PL} - produces "2006-01-01 00:00:00"
${aspnet-session:variable=myvariable:padding=5} - produces " 123"
${aspnet-session:variable=myvariable:padding=-5} - produces "123 "
${aspnet-session:variable=stringvariable:upperCase=true} - produces "AAA BBB"
</code>
</example>
</member>
<member name="M:NLog.LayoutRenderers.AspNetSessionValueLayoutRenderer.Append(System.Text.StringBuilder,NLog.LogEventInfo)">
<summary>
Renders the specified ASP.NET Session value and appends it to the specified <see cref="T:System.Text.StringBuilder"/>.
</summary>
<param name="builder">The <see cref="T:System.Text.StringBuilder"/> to append the rendered data to.</param>
<param name="logEvent">Logging event.</param>
</member>
<member name="P:NLog.LayoutRenderers.AspNetSessionValueLayoutRenderer.Variable">
<summary>
Gets or sets the session variable name.
</summary>
<docgen category='Rendering Options' order='10' />
</member>
<member name="T:NLog.LayoutRenderers.AspNetUserAuthTypeLayoutRenderer">
<summary>
ASP.NET User variable.
</summary>
</member>
<member name="M:NLog.LayoutRenderers.AspNetUserAuthTypeLayoutRenderer.Append(System.Text.StringBuilder,NLog.LogEventInfo)">
<summary>
Renders the specified ASP.NET User.Identity.AuthenticationType variable and appends it to the specified <see cref="T:System.Text.StringBuilder"/>.
</summary>
<param name="builder">The <see cref="T:System.Text.StringBuilder"/> to append the rendered data to.</param>
<param name="logEvent">Logging event.</param>
</member>
<member name="T:NLog.LayoutRenderers.AspNetUserIdentityLayoutRenderer">
<summary>
ASP.NET User variable.
</summary>
</member>
<member name="M:NLog.LayoutRenderers.AspNetUserIdentityLayoutRenderer.Append(System.Text.StringBuilder,NLog.LogEventInfo)">
<summary>
Renders the specified ASP.NET User.Identity.Name variable and appends it to the specified <see cref="T:System.Text.StringBuilder"/>.
</summary>
<param name="builder">The <see cref="T:System.Text.StringBuilder"/> to append the rendered data to.</param>
<param name="logEvent">Logging event.</param>
</member>
<member name="T:NLog.Targets.AspNetTraceTarget">
<summary>
Writes log messages to the ASP.NET trace.
</summary>
<seealso href="http://nlog-project.org/wiki/AspNetTrace_target">Documentation on NLog Wiki</seealso>
<remarks>
Log entries can then be viewed by navigating to http://server/path/Trace.axd.
</remarks>
</member>
<member name="M:NLog.Targets.AspNetTraceTarget.Write(NLog.LogEventInfo)">
<summary>
Writes the specified logging event to the ASP.NET Trace facility.
If the log level is greater than or equal to <see cref="F:NLog.LogLevel.Warn"/> it uses the
<see cref="M:System.Web.TraceContext.Warn(System.String,System.String)"/> method, otherwise it uses
<see cref="M:System.Web.TraceContext.Write(System.String,System.String)"/> method.
</summary>
<param name="logEvent">The logging event.</param>
</member>
<member name="T:NLog.Targets.MessageQueueTarget">
<summary>
Writes log message to the specified message queue handled by MSMQ.
</summary>
<seealso href="http://nlog-project.org/wiki/MessageQueue_target">Documentation on NLog Wiki</seealso>
<example>
<p>
To set up the target in the <a href="config.html">configuration file</a>,
use the following syntax:
</p>
<code lang="XML" source="examples/targets/Configuration File/MSMQ/Simple/NLog.config" />
<p>
You can use a single target to write to multiple queues (similar to writing to multiple files with the File target).
</p>
<code lang="XML" source="examples/targets/Configuration File/MSMQ/Multiple/NLog.config" />
<p>
The above examples assume just one target and a single rule.
More configuration options are described <a href="config.html">here</a>.
</p>
<p>
To set up the log target programmatically use code like this:
</p>
<code lang="C#" source="examples/targets/Configuration API/MSMQ/Simple/Example.cs" />
</example>
</member>
<member name="M:NLog.Targets.MessageQueueTarget.#ctor">
<summary>
Initializes a new instance of the <see cref="T:NLog.Targets.MessageQueueTarget"/> class.
</summary>
<remarks>
The default value of the layout is: <code>${longdate}|${level:uppercase=true}|${logger}|${message}</code>
</remarks>
</member>
<member name="M:NLog.Targets.MessageQueueTarget.Write(NLog.LogEventInfo)">
<summary>
Writes the specified logging event to a queue specified in the Queue
parameter.
</summary>
<param name="logEvent">The logging event.</param>
</member>
<member name="M:NLog.Targets.MessageQueueTarget.PrepareMessage(NLog.LogEventInfo)">
<summary>
Prepares a message to be sent to the message queue.
</summary>
<param name="logEvent">The log event to be used when calculating label and text to be written.</param>
<returns>The message to be sent.</returns>
<remarks>
You may override this method in inheriting classes
to provide services like encryption or message
authentication.
</remarks>
</member>
<member name="P:NLog.Targets.MessageQueueTarget.Queue">
<summary>
Gets or sets the name of the queue to write to.
</summary>
<remarks>
To write to a private queue on a local machine use <c>.\private$\QueueName</c>.
For other available queue names, consult MSMQ documentation.
</remarks>
<docgen category='Queue Options' order='10' />
</member>
<member name="P:NLog.Targets.MessageQueueTarget.Label">
<summary>
Gets or sets the label to associate with each message.
</summary>
<remarks>
By default no label is associated.
</remarks>
<docgen category='Queue Options' order='10' />
</member>
<member name="P:NLog.Targets.MessageQueueTarget.CreateQueueIfNotExists">
<summary>
Gets or sets a value indicating whether to create the queue if it doesn't exists.
</summary>
<docgen category='Queue Options' order='10' />
</member>
<member name="P:NLog.Targets.MessageQueueTarget.Recoverable">
<summary>
Gets or sets a value indicating whether to use recoverable messages (with guaranteed delivery).
</summary>
<docgen category='Queue Options' order='10' />
</member>
<member name="P:NLog.Targets.MessageQueueTarget.Encoding">
<summary>
Gets or sets the encoding to be used when writing text to the queue.
</summary>
<docgen category='Layout Options' order='10' />
</member>
<member name="P:NLog.Targets.MessageQueueTarget.UseXmlEncoding">
<summary>
Gets or sets a value indicating whether to use the XML format when serializing message.
</summary>
<docgen category='Layout Options' order='10' />
</member>
<member name="T:NLog.Targets.Wrappers.AspNetBufferingTargetWrapper">
<summary>
Buffers log events for the duration of ASP.NET request and sends them down
to the wrapped target at the end of a request.
</summary>
<seealso href="http://nlog-project.org/wiki/AspNetBufferingWrapper_target">Documentation on NLog Wiki</seealso>
<remarks>
<p>
Typically this target is used in cooperation with PostFilteringTargetWrapper
to provide verbose logging for failing requests and normal or no logging for
successful requests. We need to make the decision of the final filtering rule
to apply after all logs for a page have been generated.
</p>
<p>
To use this target, you need to add an entry in the httpModules section of
web.config:
</p>
<code lang="XML">
<![CDATA[<?xml version="1.0" ?>
<configuration>
<system.web>
<httpModules>
<add name="NLog" type="NLog.Web.NLogHttpModule, NLog"/>
</httpModules>
</system.web>
</configuration>
]]>
</code>
</remarks>
<example>
<p>To set up the ASP.NET Buffering target wrapper <a href="config.html">configuration file</a>, put
the following in <c>web.nlog</c> file in your web application directory (this assumes
that PostFilteringWrapper is used to provide the filtering and actual logs go to
a file).
</p>
<code lang="XML" source="examples/targets/Configuration File/ASPNetBufferingWrapper/web.nlog" />
<p>
This assumes just one target and a single rule. More configuration
options are described <a href="config.html">here</a>.
</p>
<p>
To configure the target programmatically, put the following
piece of code in your <c>Application_OnStart()</c> handler in Global.asax.cs
or some other place that gets executed at the very beginning of your code:
</p>
<code lang="C#" source="examples/targets/Configuration API/ASPNetBufferingWrapper/Global.asax.cs" />
<p>
Fully working C# project can be found in the <c>Examples/Targets/Configuration API/ASPNetBufferingWrapper</c>
directory along with usage instructions.
</p>
</example>
</member>
<member name="M:NLog.Targets.Wrappers.AspNetBufferingTargetWrapper.#ctor">
<summary>
Initializes a new instance of the <see cref="T:NLog.Targets.Wrappers.AspNetBufferingTargetWrapper"/> class.
</summary>
</member>
<member name="M:NLog.Targets.Wrappers.AspNetBufferingTargetWrapper.#ctor(NLog.Targets.Target)">
<summary>
Initializes a new instance of the <see cref="T:NLog.Targets.Wrappers.AspNetBufferingTargetWrapper"/> class.
</summary>
<param name="wrappedTarget">The wrapped target.</param>
</member>
<member name="M:NLog.Targets.Wrappers.AspNetBufferingTargetWrapper.#ctor(NLog.Targets.Target,System.Int32)">
<summary>
Initializes a new instance of the <see cref="T:NLog.Targets.Wrappers.AspNetBufferingTargetWrapper"/> class.
</summary>
<param name="wrappedTarget">The wrapped target.</param>
<param name="bufferSize">Size of the buffer.</param>
</member>
<member name="M:NLog.Targets.Wrappers.AspNetBufferingTargetWrapper.InitializeTarget">
<summary>
Initializes the target by hooking up the NLogHttpModule BeginRequest and EndRequest events.
</summary>
</member>
<member name="M:NLog.Targets.Wrappers.AspNetBufferingTargetWrapper.CloseTarget">
<summary>
Closes the target by flushing pending events in the buffer (if any).
</summary>
</member>
<member name="M:NLog.Targets.Wrappers.AspNetBufferingTargetWrapper.Write(NLog.Common.AsyncLogEventInfo)">
<summary>
Adds the specified log event to the buffer.
</summary>
<param name="logEvent">The log event.</param>
</member>
<member name="P:NLog.Targets.Wrappers.AspNetBufferingTargetWrapper.BufferSize">
<summary>
Gets or sets the number of log events to be buffered.
</summary>
<docgen category='Buffering Options' order='100' />
</member>
<member name="P:NLog.Targets.Wrappers.AspNetBufferingTargetWrapper.GrowBufferAsNeeded">
<summary>
Gets or sets a value indicating whether buffer should grow as needed.
</summary>
<value>A value of <c>true</c> if buffer should grow as needed; otherwise, <c>false</c>.</value>
<remarks>
Value of <c>true</c> causes the buffer to expand until <see cref="P:NLog.Targets.Wrappers.AspNetBufferingTargetWrapper.BufferGrowLimit"/> is hit,
<c>false</c> causes the buffer to never expand and lose the earliest entries in case of overflow.
</remarks>
<docgen category="Buffering Options" order="100"/>
</member>
<member name="P:NLog.Targets.Wrappers.AspNetBufferingTargetWrapper.BufferGrowLimit">
<summary>
Gets or sets the maximum number of log events that the buffer can keep.
</summary>
<docgen category='Buffering Options' order='100' />
</member>
<member name="T:NLog.Web.NLogHttpModule">
<summary>
ASP.NET HttpModule that enables NLog to hook BeginRequest and EndRequest events easily.
</summary>
</member>
<member name="M:NLog.Web.NLogHttpModule.Init(System.Web.HttpApplication)">
<summary>
Initializes the HttpModule.
</summary>
<param name="application">
ASP.NET application.
</param>
</member>
<member name="M:NLog.Web.NLogHttpModule.Dispose">
<summary>
Disposes the module.
</summary>
</member>
<member name="E:NLog.Web.NLogHttpModule.EndRequest">
<summary>
Event to be raised at the end of each HTTP Request.
</summary>
</member>
<member name="E:NLog.Web.NLogHttpModule.BeginRequest">
<summary>
Event to be raised at the beginning of each HTTP Request.
</summary>
</member>
</members>
</doc>

Binary file not shown.

Binary file not shown.

File diff suppressed because it is too large Load Diff

@ -1,11 +1,13 @@
using System;
using System.IO;
using System.Web;
using log4net;
using Ninject;
using NLog.Config;
using NLog.Targets;
using NzbDrone.Core.Providers;
using SubSonic.DataProviders;
using SubSonic.Repository;
using NLog;
namespace NzbDrone.Core
{
@ -20,7 +22,7 @@ namespace NzbDrone.Core
kernel.Bind<IDiskProvider>().To<DiskProvider>();
kernel.Bind<ITvDbProvider>().To<TvDbProvider>();
kernel.Bind<IConfigProvider>().To<ConfigProvider>();
kernel.Bind<ILog>().ToMethod(c => LogManager.GetLogger("logger-name"));
kernel.Bind<log4net.ILog>().ToMethod(c => log4net.LogManager.GetLogger("logger-name"));
kernel.Bind<IRepository>().ToMethod(c => new SimpleRepository(provider, SimpleRepositoryOptions.RunMigrations));
}
@ -28,5 +30,42 @@ namespace NzbDrone.Core
{
get { return new DirectoryInfo(HttpContext.Current.Server.MapPath("\\")).Parent.FullName; }
}
public static void ConfigureNlog()
{
// Step 1. Create configuration object
var config = new LoggingConfiguration();
// Step 2. Create targets and add them to the configuration
var consoleTarget = new DebuggerTarget();
config.AddTarget("console", consoleTarget);
FileTarget fileTarget = new FileTarget();
config.AddTarget("file", fileTarget);
// Step 3. Set target properties
consoleTarget.Layout = "${logger} ${message}";
fileTarget.FileName = "${basedir}/test.log";
fileTarget.Layout = "${message}";
// Step 4. Define rules
LoggingRule rule1 = new LoggingRule("*", LogLevel.Trace, consoleTarget);
config.LoggingRules.Add(rule1);
LoggingRule rule2 = new LoggingRule("*", LogLevel.Trace, fileTarget);
config.LoggingRules.Add(rule2);
// Step 5. Activate the configuration
NLog.LogManager.Configuration = config;
Logger logger = LogManager.GetCurrentClassLogger();
logger.Trace("trace log message");
logger.Debug("debug log message");
logger.Info("info log message");
logger.Warn("warn log message");
logger.Error("error log message");
logger.Fatal("fatal log message");
}
}
}

@ -128,6 +128,10 @@
<Reference Include="log4net, Version=1.2.10.0, Culture=neutral, PublicKeyToken=1b44e1d426115821, processorArchitecture=MSIL" />
<Reference Include="Ninject, Version=2.0.0.0, Culture=neutral, PublicKeyToken=c7192dc5380945e7, processorArchitecture=MSIL" />
<Reference Include="NLog, Version=2.0.0.0, Culture=neutral, PublicKeyToken=5120e14c03d0593c, processorArchitecture=MSIL" />
<Reference Include="NLog.Extended, Version=2.0.0.0, Culture=neutral, PublicKeyToken=5120e14c03d0593c, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>Libraries\NLog.Extended.dll</HintPath>
</Reference>
<Reference Include="SubSonic.Core, Version=3.0.1.3, Culture=neutral, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>Libraries\SubSonic.Core.dll</HintPath>
@ -202,6 +206,9 @@
<Content Include="Libraries\Ninject.Web.Mvc.pdb" />
<Content Include="Libraries\Ninject.xml" />
<Content Include="Libraries\NLog.dll" />
<Content Include="Libraries\NLog.Extended.dll" />
<Content Include="Libraries\NLog.Extended.pdb" />
<Content Include="Libraries\NLog.Extended.xml" />
<Content Include="Libraries\NLog.pdb" />
<Content Include="Libraries\NLog.xml" />
<Content Include="Libraries\SubSonic.Core.dll" />
@ -212,9 +219,6 @@
<Content Include="Libraries\TvdbLib.pdb" />
<Content Include="Libraries\TvdbLib.XML" />
</ItemGroup>
<ItemGroup>
<None Include="nzbdrone.db" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.

@ -1,6 +1,8 @@
using System.Collections.Generic;
using System;
using System.Collections.Generic;
using System.Linq;
using NzbDrone.Core.Repository;
using TvdbLib.Data;
namespace NzbDrone.Core.Providers
{
@ -23,5 +25,9 @@ namespace NzbDrone.Core.Providers
/// <param name="id">The TVDB ID of the series</param>
/// <returns>Whether or not the show is monitored</returns>
bool IsMonitored(long id);
bool RegisterSeries(string path);
void RegisterSeries(string path, TvdbSeries series);
List<String> GetUnmappedFolders();
}
}

@ -4,6 +4,7 @@ using System.IO;
using System.Linq;
using System.Text.RegularExpressions;
using log4net;
using NLog;
using NzbDrone.Core.Repository;
using SubSonic.Repository;
using TvdbLib.Data;
@ -13,6 +14,8 @@ namespace NzbDrone.Core.Providers
public class SeriesProvider : ISeriesProvider
{
//TODO: Remove parsing of rest of tv show info we just need the show name
private static readonly Regex CleanUpRegex = new Regex(@"((\s|^)the(\s|$))|((\s|^)and(\s|$))|[^a-z]", RegexOptions.IgnoreCase | RegexOptions.Compiled);
private static readonly Regex ParseRegex = new Regex(@"(?<showName>.*)
(?:
s(?<seasonNumber>\d+)e(?<episodeNumber>\d+)-?e(?<episodeNumber2>\d+)
@ -28,15 +31,16 @@ namespace NzbDrone.Core.Providers
| (?<episodeName>.*)
)", RegexOptions.IgnoreCase | RegexOptions.Compiled | RegexOptions.IgnorePatternWhitespace);
private readonly IConfigProvider _config;
private readonly IDiskProvider _diskProvider;
private readonly ILog _logger;
private readonly IRepository _sonioRepo;
private readonly ITvDbProvider _tvDb;
private static readonly Logger Logger = NLog.LogManager.GetCurrentClassLogger();
public SeriesProvider(ILog logger, IDiskProvider diskProvider, IConfigProvider configProvider, IRepository dataRepository, ITvDbProvider tvDbProvider)
public SeriesProvider(IDiskProvider diskProvider, IConfigProvider configProvider, IRepository dataRepository, ITvDbProvider tvDbProvider)
{
_logger = logger;
_diskProvider = diskProvider;
_config = configProvider;
_sonioRepo = dataRepository;
@ -55,74 +59,135 @@ namespace NzbDrone.Core.Providers
return _sonioRepo.Single<Series>(s => s.TvdbId == tvdbId);
}
public IList<Season> GetSeasons(long tvdbId)
/// <summary>
/// Determines if a series is being actively watched.
/// </summary>
/// <param name="id">The TVDB ID of the series</param>
/// <returns>Whether or not the show is monitored</returns>
public bool IsMonitored(long id)
{
return _sonioRepo.Find<Season>(c => c.SeriesId == tvdbId);
return _sonioRepo.Exists<Series>(c => c.TvdbId == id && c.Monitored);
}
/// <summary>
/// Parses a post title
/// </summary>
/// <param name="postTitle">Title of the report</param>
/// <returns>TVDB id of the series this report belongs to</returns>
public long Parse(string postTitle)
{
var match = ParseRegex.Match(postTitle);
if (!match.Success)
throw new ArgumentException(String.Format("Title doesn't match any know patterns. [{0}]", postTitle));
//TODO: title should be mapped to a proper Series object. with tvdbId and everything even if it is not in the db or being tracked.
throw new NotImplementedException();
}
public void SyncSeriesWithDisk()
{
if (String.IsNullOrEmpty(_config.SeriesRoot))
throw new InvalidOperationException("TV Series folder is not configured yet.");
foreach (string seriesFolder in GetUnmappedFolders())
{
Logger.Info("Folder '{0}' isn't mapped to a series in the database. Trying to map it.'", seriesFolder);
RegisterSeries(seriesFolder);
}
}
public List<String> GetUnmappedFolders()
{
var results = new List<String>();
foreach (string seriesFolder in _diskProvider.GetDirectories(_config.SeriesRoot))
{
var cleanPath = DiskProvider.CleanPath(new DirectoryInfo(seriesFolder).FullName);
if (!_sonioRepo.Exists<Series>(s => s.Path == cleanPath))
{
_logger.InfoFormat("Folder '{0} isn't mapped to a series in the database. Trying to map it.'", cleanPath);
AddShow(cleanPath);
results.Add(cleanPath);
}
}
}
#endregion
return results;
}
private void AddShow(string path)
public bool RegisterSeries(string path)
{
var searchResults = _tvDb.SearchSeries(new DirectoryInfo(path).Name);
if (searchResults.Count != 0 && !_sonioRepo.Exists<Series>(s => s.TvdbId == searchResults[0].Id))
AddShow(path, _tvDb.GetSeries(searchResults[0].Id, searchResults[0].Language));
var seriesPath = new DirectoryInfo(path);
var searchResults = _tvDb.SearchSeries(seriesPath.Name);
Logger.Debug("Search for '{0}' returned {1} results", searchResults.Count);
if (searchResults.Count == 0)
return false;
foreach (var tvdbSearchResult in searchResults)
{
TvdbSearchResult result = tvdbSearchResult;
if (IsTitleMatch(seriesPath.Name, result.SeriesName) && !_sonioRepo.Exists<Series>(c => c.TvdbId == result.Id))
{
RegisterSeries(path, _tvDb.GetSeries(result.Id, result.Language));
return true;
}
}
Logger.Info("Unable to fine a match for {0}", seriesPath.Name);
return false;
}
private void AddShow(string path, TvdbSeries series)
public void RegisterSeries(string path, TvdbSeries series)
{
Logger.Info("registering '{0}' with [{1}]-{2}", path, series.Id, series.SeriesName);
var repoSeries = new Series();
repoSeries.TvdbId = series.Id;
repoSeries.SeriesName = series.SeriesName;
repoSeries.Title = series.SeriesName;
repoSeries.AirTimes = series.AirsTime;
repoSeries.AirsDayOfWeek = series.AirsDayOfWeek;
repoSeries.Overview = series.Overview;
repoSeries.Status = series.Status;
repoSeries.Language = series.Language != null ? series.Language.Abbriviation : string.Empty;
repoSeries.Path = path;
repoSeries.CleanTitle = CleanUpRegex.Replace(series.SeriesName, "").ToLower();
_sonioRepo.Add(repoSeries);
}
#endregion
#region Static Helpers
/// <summary>
/// Parses a post title
/// Determines whether a title in a search result is equal to the title searched for.
/// </summary>
/// <param name="postTitle">Title of the report</param>
/// <returns>TVDB id of the series this report belongs to</returns>
public long Parse(string postTitle)
/// <param name="directoryName">Name of the directory.</param>
/// <param name="tvdbTitle">The TVDB title.</param>
/// <returns>
/// <c>true</c> if the titles are found to be same; otherwise, <c>false</c>.
/// </returns>
public static bool IsTitleMatch(string directoryName, string tvdbTitle)
{
var match = ParseRegex.Match(postTitle);
if (!match.Success)
throw new ArgumentException(String.Format("Title doesn't match any know patterns. [{0}]", postTitle));
//TODO: title should be mapped to a proper Series object. with tvdbId and everything even if it is not in the db or being tracked.
throw new NotImplementedException();
}
var result = false;
/// <summary>
/// Determines if a series is being actively watched.
/// </summary>
/// <param name="id">The TVDB ID of the series</param>
/// <returns>Whether or not the show is monitored</returns>
public bool IsMonitored(long id)
{
//should just check the db for now, if it exists its being monitored.
throw new NotImplementedException();
if (String.IsNullOrEmpty(directoryName))
throw new ArgumentException("directoryName");
if (String.IsNullOrEmpty(tvdbTitle))
throw new ArgumentException("tvdbTitle");
if (String.Equals(directoryName, tvdbTitle, StringComparison.CurrentCultureIgnoreCase))
{
result = true;
}
else if (String.Equals(CleanUpRegex.Replace(directoryName, ""), CleanUpRegex.Replace(tvdbTitle, ""), StringComparison.InvariantCultureIgnoreCase))
result = true;
Logger.Debug("Match between '{0}' and '{1}' was {2}", tvdbTitle, directoryName, result);
return result;
}
#endregion
}
}

@ -9,7 +9,9 @@ namespace NzbDrone.Core.Repository
[SubSonicPrimaryKey(false)]
public int TvdbId { get; set; }
public string SeriesName { get; set; }
public string Title { get; set; }
public string CleanTitle { get; set; }
public string Status { get; set; }
@ -24,10 +26,14 @@ namespace NzbDrone.Core.Repository
public string Path { get; set; }
public bool Monitored { get; set; }
[SubSonicToManyRelation]
public virtual List<Season> Seasons { get; private set; }
[SubSonicToManyRelation]
public virtual List<Episode> Episodes { get; private set; }
}
}

@ -5,78 +5,79 @@ to use a different color start by replacing all instances of
----------------------------------------------------------*/
body
{
background-image: url( 'Background.jpg' );
background-position: left top;
background-repeat: repeat-x;
font-size: .75em;
font-family: Verdana, Helvetica, Sans-Serif;
margin: 0;
padding: 0;
color: #696969;
background-color: #EEF1FA;
background-image: url( 'Background.jpg' );
background-position: left top;
background-repeat: repeat-x;
font-size: .75em;
font-family: Verdana, Helvetica, Sans-Serif;
margin: 0;
padding: 0;
color: #696969;
background-color: #EEF1FA;
}
a:link
{
color: #034af3;
text-decoration: none;
color: #034af3;
text-decoration: none;
}
a:visited
{
color: #505abc;
color: #505abc;
}
a:hover
{
color: #3C80DB;
text-decoration: underline;
color: #3C80DB;
text-decoration: underline;
}
a:active
{
color: #12eb87;
color: #12eb87;
}
p, ul
{
margin-bottom: 20px;
line-height: 1.6em;
margin-bottom: 20px;
line-height: 1.6em;
}
/* HEADINGS
----------------------------------------------------------*/
h1, h2, h3, h4, h5, h6
{
font-size: 1.5em;
color: #3C80DB;
font-size: 1.5em;
color: #3C80DB;
}
h1
{
font-size: 2em;
padding-bottom: 0;
margin-bottom: 0;
font-size: 2em;
padding-bottom: 0;
margin-bottom: 0;
}
h2
{
padding: 0 0 10px 0;
padding: 0 0 10px 0;
}
h3
{
font-size: 1.2em;
font-size: 1.2em;
}
h4
{
font-size: 1.1em;
font-size: 1.1em;
}
h5, h6
{
font-size: 1em;
font-size: 1em;
}
/* this rule styles <h2> tags that are the
first child of the left and right table columns */
.rightColumn > h1, .rightColumn > h2, .leftColumn > h1, .leftColumn > h2
{
margin-top: 0;
margin-top: 0;
}
/* PRIMARY LAYOUT ELEMENTS
@ -86,110 +87,110 @@ first child of the left and right table columns */
page width. Or, you can specify an exact pixel width. */
.page
{
width: 90%;
margin-left: auto;
margin-right: auto;
width: 90%;
margin-left: auto;
margin-right: auto;
}
#header
{
position: relative;
margin-bottom: 0px;
color: #000;
padding: 0 0 20px 0px;
background-image: url( 'BannerTopEdge.gif' );
background-position: bottom;
background-repeat: repeat-x;
position: relative;
margin-bottom: 0px;
color: #000;
padding: 0 0 20px 0px;
background-image: url( 'BannerTopEdge.gif' );
background-position: bottom;
background-repeat: repeat-x;
}
#header h1
{
font-weight: bold;
padding: 5px 0;
margin: 0;
color: #fff;
border: none;
line-height: 2em;
font-size: 32px !important;
font-weight: bold;
padding: 5px 0;
margin: 0;
color: #fff;
border: none;
line-height: 2em;
font-size: 32px !important;
}
#main
{
padding: 30px 30px 15px 30px;
background-color: #fff;
margin-bottom: 30px;
_height: 1px; /* only IE6 applies CSS properties starting with an underscrore */
background-image: url( 'BannerBottomEdge.gif' );
background-position: bottom;
background-repeat: repeat-x;
padding: 30px 30px 15px 30px;
background-color: #fff;
margin-bottom: 30px;
_height: 1px; /* only IE6 applies CSS properties starting with an underscrore */
background-image: url( 'BannerBottomEdge.gif' );
background-position: bottom;
background-repeat: repeat-x;
}
#footer
{
color: #999;
padding: 10px 0;
text-align: center;
line-height: normal;
margin: 0;
font-size: .9em;
color: #999;
padding: 10px 0;
text-align: center;
line-height: normal;
margin: 0;
font-size: .9em;
}
/* TAB MENU
----------------------------------------------------------*/
ul#menu
{
padding: 0;
position: relative;
margin: 0;
text-align: right;
padding: 0;
position: relative;
margin: 0;
text-align: right;
}
ul#menu li
{
display: inline;
list-style: none;
display: inline;
list-style: none;
}
ul#menu li#greeting
{
padding: 10px 20px;
font-weight: bold;
text-decoration: none;
line-height: 2.8em;
color: #fff;
padding: 10px 20px;
font-weight: bold;
text-decoration: none;
line-height: 2.8em;
color: #fff;
}
ul#menu li a
{
padding: 10px 20px;
font-weight: bold;
text-decoration: none;
line-height: 2.8em;
background-color: #fff;
color: #3C80DB;
border: Solid 1px #AECAF0;
padding: 10px 20px;
font-weight: bold;
text-decoration: none;
line-height: 2.8em;
background-color: #fff;
color: #3C80DB;
border: Solid 1px #AECAF0;
}
ul#menu li a:hover
{
background-color: #3C80DB;
text-decoration: none;
color: #fff;
border: Solid 1px #AECAF0;
background-color: #3C80DB;
text-decoration: none;
color: #fff;
border: Solid 1px #AECAF0;
}
ul#menu li a:active
{
background-color: #3C80DB;
text-decoration: none;
color: #fff;
border: Solid 1px #AECAF0;
background-color: #3C80DB;
text-decoration: none;
color: #fff;
border: Solid 1px #AECAF0;
}
ul#menu li.selected a
{
background-color: #fff;
color: #000;
background-color: #fff;
color: #000;
}
/* FORM LAYOUT ELEMENTS
@ -197,56 +198,66 @@ ul#menu li.selected a
fieldset
{
margin: 1em 0;
padding: 1em;
border: 1px solid #CCC;
margin: 1em 0;
padding: 1em;
border: 1px solid #CCC;
}
fieldset p
{
margin: 2px 12px 10px 10px;
margin: 2px 12px 10px 10px;
}
fieldset label
{
display: block;
color: #303030;
display: block;
color: #303030;
}
fieldset label.inline
{
display: inline;
display: inline;
}
legend
{
font-size: 1.1em;
font-weight: 600;
padding: 2px 4px 8px 4px;
color: #3C80DB;
font-size: 1.1em;
font-weight: 600;
padding: 2px 4px 8px 4px;
color: #3C80DB;
}
input[type="text"]
{
width: 200px;
border: 1px solid #3C80DB;
color: #696969;
width: 200px;
border: 1px solid #3C80DB;
color: #696969;
}
input[type="password"]
{
width: 200px;
border: 1px solid #3C80DB;
color: #696969;
width: 200px;
border: 1px solid #3C80DB;
color: #696969;
}
input[type="submit"]
{
background-image: url( 'ButtonBack.gif' );
background-repeat: repeat-x;
border: 1px solid #3C80DB;
color: #283B56;
cursor: pointer;
background-image: url( 'ButtonBack.gif' );
background-repeat: repeat-x;
border: 1px solid #3C80DB;
color: #283B56;
cursor: pointer;
}
.display-label
{
font-weight: bolder;
}
.display-field
{
padding: 7px;
}
/* TABLE
@ -254,105 +265,107 @@ input[type="submit"]
table
{
border: Solid 1px #A3C0E8;
border-collapse: collapse;
color: #283B56;
border: Solid 1px #A3C0E8;
border-collapse: collapse;
color: #283B56;
}
table td
{
padding: 5px;
border: Solid 1px #A3C0E8;
padding: 5px;
border: Solid 1px #A3C0E8;
}
table th
{
padding: 6px 5px;
text-align: left;
border: Solid 1px #A3C0E8;
background-image: url( 'TableHeaderBackground.gif' );
background-position: left;
background-repeat: repeat-x;
color: #283B56;
cursor: pointer;
padding: 6px 5px;
text-align: left;
border: Solid 1px #A3C0E8;
background-image: url( 'TableHeaderBackground.gif' );
background-position: left;
background-repeat: repeat-x;
color: #283B56;
cursor: pointer;
text-align: center;
}
/* MISC
----------------------------------------------------------*/
.clear
{
clear: both;
clear: both;
}
.error
{
color: Red;
color: Red;
}
#menucontainer
{
margin-top: 40px;
margin-top: 40px;
}
div#title
{
display: block;
float: left;
text-align: left;
display: block;
float: left;
text-align: left;
}
#logindisplay
{
font-size: 1.1em;
display: block;
text-align: right;
margin: 10px;
color: White;
font-size: 1.1em;
display: block;
text-align: right;
margin: 10px;
color: White;
}
#logindisplay a:link
{
color: white;
text-decoration: underline;
color: white;
text-decoration: underline;
}
#logindisplay a:visited
{
color: white;
text-decoration: underline;
color: white;
text-decoration: underline;
}
#logindisplay a:hover
{
color: white;
text-decoration: none;
color: white;
text-decoration: none;
}
.field-validation-error
{
color: #ff0000;
background-image: url( 'Error.png' );
background-position: left;
background-repeat: no-repeat;
padding: 0px 0px 0px 20px;
font-size:0.9em;
color: #ff0000;
background-image: url( 'Error.png' );
background-position: left;
background-repeat: no-repeat;
padding: 0px 0px 0px 20px;
font-size: 0.9em;
}
.input-validation-error
{
background-color: #ffeeee;
background-color: #ffeeee;
}
.validation-summary-errors
{
font-size:0.9em;
padding: 4px;
color: #ff0000;
font-size: 0.9em;
padding: 4px;
color: #ff0000;
}
.validation-summary-errors li
{
margin: 0px;
padding: 0px;
list-style: none;
margin: 0px;
padding: 0px;
list-style: none;
}

@ -32,6 +32,13 @@ namespace NzbDrone.Web.Controllers
}
public ActionResult UnMapped()
{
_seriesProvider.SyncSeriesWithDisk();
return View(_seriesProvider.GetUnmappedFolders());
}
//
// GET: /Series/Details/5

@ -27,6 +27,7 @@ namespace NzbDrone.Web
protected override void OnApplicationStarted()
{
Main.ConfigureNlog();
AreaRegistration.RegisterAllAreas();
RegisterRoutes(RouteTable.Routes);
base.OnApplicationStarted();

@ -94,6 +94,7 @@
<Content Include="Global.asax" />
<Content Include="Views\Series\Details.aspx" />
<Content Include="Views\Series\index.aspx" />
<Content Include="Views\Series\Unmapped.aspx" />
<Content Include="Views\Settings\Index.aspx" />
<Content Include="Web.config">
<SubType>Designer</SubType>

@ -6,35 +6,28 @@
<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">
<h2>Details</h2>
<h2><%: Model.Title %></h2>
<fieldset>
<legend>Fields</legend>
<div class="display-label">Id</div>
<div class="display-label">ID</div>
<div class="display-field"><%: Model.TvdbId %></div>
<div class="display-label">SeriesName</div>
<div class="display-field"><%: Model.SeriesName %></div>
<div class="display-label">Status</div>
<div class="display-field"><%: Model.Status %></div>
<div class="display-label">Overview</div>
<div class="display-field"><%: Model.Overview %></div>
<div class="display-label">Status</div>
<div class="display-field"><%: Model.Status %></div>
<div class="display-label">AirTimes</div>
<div class="display-field"><%: Model.AirTimes %></div>
<div class="display-label">Language</div>
<div class="display-field"><%: Model.Language %></div>
<div class="display-field"><%: Model.Language.ToUpper() %></div>
<div class="display-label">Path</div>
<div class="display-label">Location</div>
<div class="display-field"><%: Model.Path %></div>
<div class="display-label">TvdbId</div>
<div class="display-field"><%: Model.TvdbId %></div>
</fieldset>
<p>
<%-- <%: Html.ActionLink("Edit", "Edit", new { /* id=Model.PrimaryKey */ }) %> |--%>

@ -0,0 +1,25 @@
<%@ Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage<List<String>>" %>
<asp:Content ID="Content1" ContentPlaceHolderID="TitleContent" runat="server">
Unmapped
</asp:Content>
<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">
<h2> The following folders aren't currently mapped to any Series.</h2>
<h3> Please Re-sync your folders.
If the problem persists try renaming your folders to something more similar to the name of series they contain.</h3>
<table>
<tr>
<th>
Folder
</th>
</tr>
<% foreach (var item in Model)
{ %>
<tr>
<td>
<%: item %>
</td>
</tr>
<% } %>
</table>
</asp:Content>

@ -4,8 +4,16 @@
SeriesView
</asp:Content>
<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">
<ul id="menu">
<li>
<%: Html.ActionLink("Sync With Disk", "Sync") %></li>
<li>
<%: Html.ActionLink("View Unmapped Folders", "Unmapped") %></li>
</ul>
<h2>
SeriesView</h2>
<p>
</p>
<table>
<tr>
<th>
@ -24,7 +32,7 @@
<% foreach (var item in Model)
{ %>
<tr>
<%-- <td>
<%-- <td>
<%: Html.ActionLink("Details", "Details", new { item.TvdbId })%>
|
<%: Html.ActionLink("Delete", "Delete", new { item.TvdbId })%>
@ -33,7 +41,7 @@
<%: item.TvdbId.ToString()%>
</td>
<td>
<%: Html.ActionLink(item.SeriesName, "Details", new { item.TvdbId })%>
<%: Html.ActionLink(item.Title, "Details", new { item.TvdbId })%>
</td>
<td>
<%: item.Status %>
@ -44,8 +52,4 @@
</tr>
<% } %>
</table>
<p>
<%: Html.ActionLink("Create New", "Create") %>
<%: Html.ActionLink("Sync With Disk", "Sync") %>
</p>
</asp:Content>

@ -22,7 +22,7 @@
<div id="menucontainer">
<ul id="menu">
<li><%: Html.ActionLink("Home", "Index", "Home")%></li>
<li><%: Html.ActionLink("Series", "Index", "Series")%></li>
<li><%: Html.ActionLink("Settings", "Index", "Settings")%></li>
</ul>

Loading…
Cancel
Save