Fixed notification issues

Added basic support for file scan
Major redactor of ReportTitle/File parsing
Updated Ninject/Ninject.MVC
Removed dependency from Microsoft.Web.Administration
reactored Episode repository structure
This commit is contained in:
Keivan 2010-10-20 18:49:23 -07:00
parent 41d9b0364f
commit c8a8fb4d62
57 changed files with 5569 additions and 320 deletions

View File

@ -1,4 +1,4 @@
<?xml version="1.0" encoding="UTF-8"?>
<?xml version="1.0" encoding="utf-8"?>
<!--
IIS configuration sections.
@ -17,9 +17,7 @@
%SYSTEMDRIVE% - The drive letter of %IIS_BIN%
-->
<configuration>
<!--
The <configSections> section controls the registration of sections.
@ -58,7 +56,6 @@
<section name="sites" allowDefinition="AppHostOnly" overrideModeDefault="Deny" />
<section name="webLimits" allowDefinition="AppHostOnly" overrideModeDefault="Deny" />
</sectionGroup>
<sectionGroup name="system.webServer">
<section name="asp" overrideModeDefault="Deny" />
<section name="caching" overrideModeDefault="Allow" />
@ -117,7 +114,6 @@
</sectionGroup>
</sectionGroup>
</configSections>
<configProtectedData>
<providers>
<add name="IISWASOnlyRsaProvider" type="" description="Uses RsaCryptoServiceProvider to encrypt and decrypt" keyContainerName="iisWasKey" cspProviderName="" useMachineContainer="true" useOAEP="false" />
@ -125,9 +121,7 @@
<add name="IISWASOnlyAesProvider" type="Microsoft.ApplicationHost.AesProtectedConfigurationProvider" description="Uses an AES session key to encrypt and decrypt" keyContainerName="iisWasKey" cspProviderName="" useOAEP="false" useMachineContainer="true" sessionKey="AQIAAA5mAAAApAAA4WoiRJ8KHwzAG8AgejPxEOO4/2Vhkolbwo/8gZeNdUDSD36m55hWv4uC9tr/MlKdnwRLL0NhT50Gccyftqz5xTZ0dg5FtvQhTw/he1NwexTKbV+I4Zrd+sZUqHZTsr7JiEr6OHGXL70qoISW5G2m9U8wKT3caPiDPNj2aAaYPLo=" />
</providers>
</configProtectedData>
<system.applicationHost>
<applicationPools>
<!-- <add name="Clr4IntegratedAppPool" managedRuntimeVersion="v4.0" managedPipelineMode="Integrated" CLRConfigFile="%IIS_USER_HOME%\config\aspnet.config" autoStart="true" />
<add name="Clr4ClassicAppPool" managedRuntimeVersion="v4.0" managedPipelineMode="Classic" CLRConfigFile="%IIS_USER_HOME%\config\aspnet.config" autoStart="true" />
@ -139,7 +133,6 @@
<processModel />
</applicationPoolDefaults>
</applicationPools>
<!--
The <listenerAdapters> section defines the protocols with which the
@ -149,14 +142,13 @@
<listenerAdapters>
<add name="http" />
</listenerAdapters>
<sites>
<site name="NZBDrone" id="1" serverAutoStart="true">
<application path="/">
<virtualDirectory path="/" physicalPath="%NZBDRONE_PATH%\NZBDrone.Web" />
</application>
<bindings>
<binding protocol="http" bindingInformation="*:8981:" />
<binding protocol="http" bindingInformation="*:8989:" />
</bindings>
</site>
<siteDefaults>
@ -166,25 +158,16 @@
<applicationDefaults applicationPool="IISExpressAppPool" />
<virtualDirectoryDefaults allowSubDirConfig="true" />
</sites>
<webLimits />
</system.applicationHost>
<system.webServer>
<serverRuntime />
<asp scriptErrorSentToBrowser="true">
<cache diskTemplateCacheDirectory="%TEMP%\iisexpress\ASP Compiled Templates" />
<limits />
</asp>
<caching enabled="true" enableKernelCache="true">
</caching>
<caching enabled="true" enableKernelCache="true"></caching>
<cgi />
<defaultDocument enabled="true">
<files>
<add value="Default.htm" />
@ -194,11 +177,8 @@
<add value="default.aspx" />
</files>
</defaultDocument>
<directoryBrowse enabled="false" />
<fastCgi />
<!--
The <globalModules> section defines all native-code modules.
@ -244,7 +224,6 @@
<add name="ManagedEngine" image="%windir%\Microsoft.NET\Framework\v2.0.50727\webengine.dll" preCondition="integratedMode,runtimeVersionv2.0,bitness32" />
<add name="ManagedEngineV4.0_32bit" image="%windir%\Microsoft.NET\Framework\v4.0.30319\webengine4.dll" preCondition="integratedMode,runtimeVersionv4.0,bitness32" />
</globalModules>
<httpCompression directory="%TEMP%\iisexpress\IIS Temporary Compressed Files">
<scheme name="gzip" dll="%IIS_BIN%\gzip.dll" />
<dynamicTypes>
@ -262,7 +241,6 @@
<add mimeType="*/*" enabled="false" />
</staticTypes>
</httpCompression>
<httpErrors lockAttributes="allowAbsolutePathsWhenDelegated,defaultPath">
<error statusCode="401" prefixLanguageFilePath="%IIS_BIN%\custerr" path="401.htm" />
<error statusCode="403" prefixLanguageFilePath="%IIS_BIN%\custerr" path="403.htm" />
@ -274,9 +252,7 @@
<error statusCode="501" prefixLanguageFilePath="%IIS_BIN%\custerr" path="501.htm" />
<error statusCode="502" prefixLanguageFilePath="%IIS_BIN%\custerr" path="502.htm" />
</httpErrors>
<httpLogging dontLog="false" />
<httpProtocol>
<customHeaders>
<clear />
@ -286,60 +262,39 @@
<clear />
</redirectHeaders>
</httpProtocol>
<httpRedirect enabled="false" />
<httpTracing>
</httpTracing>
<httpTracing></httpTracing>
<isapiFilters>
<filter name="ASP.Net_2.0.50727.0" path="%windir%\Microsoft.NET\Framework\v2.0.50727\aspnet_filter.dll" enableCache="true" preCondition="bitness32,runtimeVersionv2.0" />
<filter name="ASP.Net_2.0_for_v1.1" path="%windir%\Microsoft.NET\Framework\v2.0.50727\aspnet_filter.dll" enableCache="true" preCondition="runtimeVersionv1.1" />
<filter name="ASP.Net_4.0_32bit" path="%windir%\Microsoft.NET\Framework\v4.0.30319\aspnet_filter.dll" enableCache="true" preCondition="bitness32,runtimeVersionv4.0" />
</isapiFilters>
<odbcLogging />
<security>
<access sslFlags="None" />
<applicationDependencies>
<application name="Active Server Pages" groupId="ASP" />
</applicationDependencies>
<authentication>
<anonymousAuthentication enabled="true" userName="" />
<basicAuthentication enabled="false" />
<clientCertificateMappingAuthentication enabled="false" />
<digestAuthentication enabled="false" />
<iisClientCertificateMappingAuthentication enabled="false">
</iisClientCertificateMappingAuthentication>
<iisClientCertificateMappingAuthentication enabled="false"></iisClientCertificateMappingAuthentication>
<windowsAuthentication enabled="false">
<providers>
<add value="Negotiate" />
<add value="NTLM" />
</providers>
</windowsAuthentication>
</authentication>
<authorization>
<add accessType="Allow" users="*" />
</authorization>
<ipSecurity allowUnlisted="true" />
<isapiCgiRestriction notListedIsapisAllowed="true" notListedCgisAllowed="true">
<add path="%windir%\Microsoft.NET\Framework\v4.0.30319\webengine4.dll" allowed="true" groupId="ASP.NET_v4.0" description="ASP.NET_v4.0" />
</isapiCgiRestriction>
<requestFiltering>
<fileExtensions allowUnlisted="true" applyToWebDAV="true">
<add fileExtension=".asa" allowed="false" />
@ -399,11 +354,8 @@
<add segment="App_Browsers" />
</hiddenSegments>
</requestFiltering>
</security>
<serverSideInclude ssiExecDisable="false" />
<staticContent lockAttributes="isDocFooterFileName">
<mimeMap fileExtension=".323" mimeType="text/h323" />
<mimeMap fileExtension=".aaf" mimeType="application/octet-stream" />
@ -752,9 +704,7 @@
<mimeMap fileExtension=".z" mimeType="application/x-compress" />
<mimeMap fileExtension=".zip" mimeType="application/x-zip-compressed" />
</staticContent>
<tracing>
<traceProviderDefinitions>
<add name="WWW Server" guid="{3a2a4e84-4c21-4981-ae10-3fda0d9b0f83}">
<areas>
@ -791,7 +741,6 @@
</areas>
</add>
</traceProviderDefinitions>
<traceFailedRequests>
<add path="*">
<traceAreas>
@ -803,11 +752,8 @@
<failureDefinitions statusCodes="200-999" />
</add>
</traceFailedRequests>
</tracing>
<urlCompression />
<validation />
<webdav>
<globalSettings>
@ -817,14 +763,12 @@
<lockStores>
<add name="webdav_simple_lock" image="%IIS_BIN%\webdav_simple_lock.dll" image32="%windir%\syswow64\inetsrv\webdav_simple_lock.dll" />
</lockStores>
</globalSettings>
<authoring>
<locks enabled="true" lockStore="webdav_simple_lock" />
</authoring>
<authoringRules />
</webdav>
</system.webServer>
<location path="" overrideMode="Allow">
<system.webServer>
@ -950,4 +894,4 @@
</handlers>
</system.webServer>
</location>
</configuration>
</configuration>

View File

@ -5,8 +5,8 @@ using Gallio.Framework;
using MbUnit.Framework;
using MbUnit.Framework.ContractVerifiers;
using Moq;
using NzbDrone.Core.Entities;
using NzbDrone.Core.Providers;
using NzbDrone.Core.Repository;
using SubSonic.Repository;
namespace NzbDrone.Core.Test

View File

@ -21,11 +21,11 @@ namespace NzbDrone.Core.Test
public class EpisodeProviderTest
{
[Test]
public void BulkAddSpeedTest()
public void RefreshEpisodeInfo()
{
//Arrange
int seriesId = 71663;
int episodeCount = 500;
int episodeCount = 10;
var fakeEpisodes = Builder<TvdbSeries>.CreateNew().With(
c => c.Episodes =
new List<TvdbEpisode>(Builder<TvdbEpisode>.CreateListOfSize(episodeCount).

Binary file not shown.

View File

@ -0,0 +1,105 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Text;
using Gallio.Framework;
using MbUnit.Framework;
using MbUnit.Framework.ContractVerifiers;
using Moq;
using Ninject;
using Ninject.Moq;
using NzbDrone.Core.Entities;
using NzbDrone.Core.Entities.Episode;
using NzbDrone.Core.Entities.Quality;
using NzbDrone.Core.Providers;
using SubSonic.Repository;
namespace NzbDrone.Core.Test
{
[TestFixture]
// ReSharper disable InconsistentNaming
public class MediaFileProviderTests
{
[Test]
public void scan_test()
{
//Arrange
var repository = new Mock<IRepository>();
repository.Setup(c => c.Update(It.IsAny<EpisodeInfo>())).Verifiable();
var diskProvider = MockLib.GetStandardDisk(1, 2);
var kernel = new MockingKernel();
kernel.Bind<IDiskProvider>().ToConstant(diskProvider);
kernel.Bind<IRepository>().ToConstant(repository.Object);
kernel.Bind<IMediaFileProvider>().To<MediaFileProvider>();
var fakeSeries = new Series()
{
Path = MockLib.StandardSeries[0]
};
//Act
kernel.Get<IMediaFileProvider>().Scan(fakeSeries);
//Assert
repository.Verify(c => c.Update(It.IsAny<EpisodeInfo>()), Times.Exactly(1 * 2));
}
[Test]
[Row("WEEDS.S03E01-06.DUAL.BDRip.XviD.AC3.-HELLYWOOD", 3, 1)]
[Row("Two.and.a.Half.Me.103.720p.HDTV.X264-DIMENSION", 1, 3)]
[Row("Chuck.4x05.HDTV.XviD-LOL", 4, 5)]
[Row("The.Girls.Next.Door.S03E06.DVDRip.XviD-WiDE", 3, 6)]
[Row("Degrassi.S10E27.WS.DSR.XviD-2HD", 10, 27)]
public void episode_parse(string path, int season, int episode)
{
var result = Parser.ParseBasicEpisode(path);
Assert.AreEqual(season, result.SeasonNumber);
Assert.AreEqual(episode, result.EpisodeNumber);
}
[Test]
[Row("WEEDS.S03E01-06.DUAL.BDRip.XviD.AC3.-HELLYWOOD", QualityTypes.DVD)]
[Row("WEEDS.S03E01-06.DUAL.BDRip.AC3.-HELLYWOOD", QualityTypes.Bluray)]
[Row("Two.and.a.Half.Men.S08E05.720p.HDTV.X264-DIMENSION", QualityTypes.HDTV)]
[Row("Chuck.S04E05.HDTV.XviD-LOL", QualityTypes.TV)]
[Row("The.Girls.Next.Door.S03E06.DVDRip.XviD-WiDE", QualityTypes.DVD)]
[Row("Degrassi.S10E27.WS.DSR.XviD-2HD", QualityTypes.TV)]
[Row("Sonny.With.a.Chance.S02E15.720p.WEB-DL.DD5.1.H.264-SURFER", QualityTypes.WEBDL)]
[Row("Sonny.With.a.Chance.S02E15.720p", QualityTypes.HDTV)]
[Row("Sonny.With.a.Chance.S02E15.mkv", QualityTypes.HDTV)]
[Row("Sonny.With.a.Chance.S02E15.avi", QualityTypes.TV)]
[Row("Sonny.With.a.Chance.S02E15.xvid", QualityTypes.TV)]
[Row("Sonny.With.a.Chance.S02E15.divx", QualityTypes.TV)]
[Row("Sonny.With.a.Chance.S02E15", QualityTypes.Unknown)]
public void quality_parse(string path, object quality)
{
var result = Parser.ParseQuality(path);
Assert.AreEqual(quality, result);
}
[Test]
[Timeout(2)]
public void quality_parse()
{
var sw = Stopwatch.StartNew();
var name = "WEEDSawdawdadawdawd\\awdawdawdadadad.mkv";
var quality = QualityTypes.HDTV;
for (int i = 0; i < 100000; i++)
{
Assert.AreEqual(quality, Parser.ParseQuality(name));
}
Console.WriteLine(sw.Elapsed.ToString());
}
}
}

View File

@ -0,0 +1,87 @@
using System;
using System.Collections.Generic;
using System.Text;
using Gallio.Framework;
using MbUnit.Framework;
using MbUnit.Framework.ContractVerifiers;
using Moq;
using Ninject;
using Ninject.Moq;
using NzbDrone.Core.Providers;
using NzbDrone.Core.Repository;
using NzbDrone.Core.Repository.Quality;
using SubSonic.Repository;
namespace NzbDrone.Core.Test
{
[TestFixture]
// ReSharper disable InconsistentNaming
public class MediaFileProviderTests
{
[Test]
public void scan_test()
{
//Arrange
var repository = new Mock<IRepository>();
repository.Setup(c => c.Update(It.IsAny<Episode>())).Verifiable();
var diskProvider = MockLib.GetStandardDisk(1, 2);
var kernel = new MockingKernel();
kernel.Bind<IDiskProvider>().ToConstant(diskProvider);
kernel.Bind<IRepository>().ToConstant(repository.Object);
kernel.Bind<IMediaFileProvider>().To<MediaFileProvider>();
var fakeSeries = new Series()
{
Path = MockLib.StandardSeries[0]
};
//Act
kernel.Get<IMediaFileProvider>().Scan(fakeSeries);
//Assert
repository.Verify(c => c.Update(It.IsAny<Episode>()), Times.Exactly(1 * 2));
}
[Test]
[Row("WEEDS.S03E01-06.DUAL.BDRip.XviD.AC3.-HELLYWOOD", 3, 1)]
[Row("Two.and.a.Half.Me.103.720p.HDTV.X264-DIMENSION", 1, 3)]
[Row("Chuck.4x05.HDTV.XviD-LOL", 4, 5)]
[Row("The.Girls.Next.Door.S03E06.DVDRip.XviD-WiDE", 3, 6)]
[Row("Degrassi.S10E27.WS.DSR.XviD-2HD", 10, 27)]
public void episode_parse(string path, int season, int episode)
{
var result = Parser.ParseEpisodeInfo(path);
Assert.Count(1, result);
Assert.AreEqual(season, result[0].SeasonNumber);
Assert.AreEqual(episode, result[0].EpisodeNumber);
}
[Test]
[Row("WEEDS.S03E01-06.DUAL.BDRip.XviD.AC3.-HELLYWOOD", QualityTypes.DVD)]
[Row("WEEDS.S03E01-06.DUAL.BDRip.AC3.-HELLYWOOD", QualityTypes.Bluray)]
[Row("Two.and.a.Half.Men.S08E05.720p.HDTV.X264-DIMENSION", QualityTypes.HDTV)]
[Row("Chuck.S04E05.HDTV.XviD-LOL", QualityTypes.TV)]
[Row("The.Girls.Next.Door.S03E06.DVDRip.XviD-WiDE", QualityTypes.DVD)]
[Row("Degrassi.S10E27.WS.DSR.XviD-2HD", QualityTypes.TV)]
[Row("Sonny.With.a.Chance.S02E15.720p.WEB-DL.DD5.1.H.264-SURFER", QualityTypes.WEBDL)]
[Row("Sonny.With.a.Chance.S02E15.720p", QualityTypes.HDTV)]
[Row("Sonny.With.a.Chance.S02E15.mkv", QualityTypes.HDTV)]
[Row("Sonny.With.a.Chance.S02E15.avi", QualityTypes.TV)]
[Row("Sonny.With.a.Chance.S02E15.xvid", QualityTypes.TV)]
[Row("Sonny.With.a.Chance.S02E15.divx", QualityTypes.TV)]
[Row("Sonny.With.a.Chance.S02E15", QualityTypes.Unknown)]
public void quality_parse(string path, object quality)
{
var result = Parser.ParseQuality(path);
Assert.AreEqual(quality, result);
}
}
}

View File

@ -1,9 +1,10 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Text;
using FizzWare.NBuilder;
using Moq;
using NLog;
using NzbDrone.Core.Providers;
@ -50,11 +51,28 @@ namespace NzbDrone.Core.Test
}
}
public static IDiskProvider GetStandardDisk()
public static IDiskProvider GetStandardDisk(int seasons, int episodes)
{
var mock = new Mock<IDiskProvider>();
mock.Setup(c => c.GetDirectories(It.IsAny<String>())).Returns(StandardSeries);
mock.Setup(c => c.Exists(It.Is<String>(d => StandardSeries.Contains(d)))).Returns(true);
foreach (var series in StandardSeries)
{
var file = new List<String>();
for (int s = 0; s < seasons; s++)
{
for (int e = 0; e < episodes; e++)
{
file.Add(String.Format("{0}\\Seasons {1}\\myepname.S{1:00}E{2:00}.avi", series, s, e));
}
}
string series1 = series;
mock.Setup(c => c.GetFiles(series1, "*.avi", SearchOption.AllDirectories)).Returns(file.ToArray());
}
return mock.Object;
}
}

View File

@ -31,16 +31,20 @@
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
<Reference Include="Accessibility">
<EmbedInteropTypes>True</EmbedInteropTypes>
</Reference>
<Reference Include="Castle.Core, Version=2.5.0.0, Culture=neutral, PublicKeyToken=407dd0808d44fbdc, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\NzbDrone.Core\Libraries\Castle.Core.dll</HintPath>
</Reference>
<Reference Include="FizzWare.NBuilder, Version=2.1.9.0, Culture=neutral, PublicKeyToken=5651b03e12e42c12, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>Libs\FizzWare.NBuilder.dll</HintPath>
</Reference>
<Reference Include="Gallio, Version=3.2.0.0, Culture=neutral, PublicKeyToken=eb9cfa67ee6ab36e, processorArchitecture=MSIL" />
<Reference Include="MbUnit, Version=3.2.0.0, Culture=neutral, PublicKeyToken=eb9cfa67ee6ab36e, processorArchitecture=MSIL" />
<Reference Include="Moq, Version=4.0.10827.0, Culture=neutral, PublicKeyToken=69f491c39445e920, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>Moq\Moq.dll</HintPath>
</Reference>
<Reference Include="Moq, Version=4.0.10827.0, Culture=neutral, PublicKeyToken=69f491c39445e920, processorArchitecture=MSIL" />
<Reference Include="Ninject, Version=2.0.0.0, Culture=neutral, PublicKeyToken=c7192dc5380945e7, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\NzbDrone.Core\Libraries\Ninject.dll</HintPath>
@ -62,6 +66,7 @@
</Reference>
</ItemGroup>
<ItemGroup>
<Compile Include="MediaFileProviderTests.cs" />
<Compile Include="DbConfigControllerTest.cs" />
<Compile Include="EpisodeProviderTest.cs" />
<Compile Include="Fixtures.cs" />
@ -84,8 +89,6 @@
</ItemGroup>
<ItemGroup>
<Content Include="Libs\FizzWare.NBuilder.dll" />
<Content Include="Libs\Moq.dll" />
<Content Include="Libs\Moq.xml" />
<Content Include="Files\Queue.xml">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
@ -95,6 +98,9 @@
<Content Include="Files\QueueError.xml">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="Libs\Moq.dll" />
<Content Include="Libs\Moq.pdb" />
<Content Include="Libs\Moq.xml" />
<Content Include="Libs\System.Data.SQLite.DLL">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>

View File

@ -1,7 +1,7 @@
using System.Collections.Generic;
using System.IO;
using MbUnit.Framework;
using NzbDrone.Core.Entities.Quality;
using NzbDrone.Core.Repository.Quality;
namespace NzbDrone.Core.Test
{
@ -23,7 +23,7 @@ namespace NzbDrone.Core.Test
var repo = MockLib.GetEmptyRepository();
var testProfile = new QualityProfile
{
Cutoff = QualityTypes.SDTV,
Cutoff = QualityTypes.TV,
Allowed = new List<QualityTypes>() { QualityTypes.HDTV, QualityTypes.DVD },
};

View File

@ -6,8 +6,8 @@ using FizzWare.NBuilder;
using Gallio.Framework;
using MbUnit.Framework;
using MbUnit.Framework.ContractVerifiers;
using NzbDrone.Core.Entities;
using NzbDrone.Core.Entities.Episode;
using NzbDrone.Core.Model;
using NzbDrone.Core.Repository;
namespace NzbDrone.Core.Test
{
@ -20,7 +20,7 @@ namespace NzbDrone.Core.Test
{
//Arrange
var fakeSeries = Builder<Series>.CreateNew().With(s => s.SeriesId = 69).Build();
var fakeEpisode = Builder<EpisodeInfo>.CreateNew().With(c => c.SeriesId = 69).Build();
var fakeEpisode = Builder<Episode>.CreateNew().With(c => c.SeriesId = 69).Build();
//Act
var repo = MockLib.GetEmptyRepository();
@ -55,5 +55,12 @@ namespace NzbDrone.Core.Test
Assert.IsNotEmpty(allSeries);
Assert.AreEqual(tvdbId, allSeries.First().SeriesId);
}
[Test]
public void enteties_toString()
{
Console.WriteLine(new Episode().ToString());
Console.WriteLine(new EpisodeModel().ToString());
}
}
}

View File

@ -10,8 +10,8 @@ using MbUnit.Framework.ContractVerifiers;
using Moq;
using Ninject;
using Ninject.Moq;
using NzbDrone.Core.Entities;
using NzbDrone.Core.Providers;
using NzbDrone.Core.Repository;
using SubSonic.Repository;
using TvdbLib.Data;
using System.Linq;
@ -59,10 +59,11 @@ namespace NzbDrone.Core.Test
[Row(new object[] { "Van.Duin.Op.Zn.Best.S02E05.DUTCH.WS.PDTV.XViD-DiFFERENT", "Van Duin Op Zn Best" })]
[Row(new object[] { "Dollhouse.S02E06.The.Left.Hand.720p.BluRay.x264-SiNNERS", "Dollhouse" })]
[Row(new object[] { "Heroes.S02.COMPLETE.German.PROPER.DVDRip.XviD-Prim3time", "Heroes" })]
[Ignore("should be updated to validate agains a remote episode instance rather than just the title string")]
public void Test_Parse_Success(string postTitle, string title)
{
var result = SeriesProvider.ParseTitle(postTitle);
Assert.AreEqual(title, result, postTitle);
var result = Parser.ParseEpisodeInfo(postTitle);
//Assert.AreEqual(title, result, postTitle);
}
[Test]
@ -73,7 +74,8 @@ namespace NzbDrone.Core.Test
kernel.Bind<ISeriesProvider>().To<SeriesProvider>();
kernel.Bind<IDiskProvider>().ToConstant(MockLib.GetStandardDisk());
kernel.Bind<IDiskProvider>().ToConstant(MockLib.GetStandardDisk(0, 0));
kernel.Bind<IConfigProvider>().ToConstant(MockLib.StandardConfig);
var seriesController = kernel.Get<ISeriesProvider>();
@ -84,6 +86,6 @@ namespace NzbDrone.Core.Test
Assert.AreElementsEqualIgnoringOrder(MockLib.StandardSeries, unmappedFolder);
}
}
}

View File

@ -5,10 +5,9 @@ using System.Web;
using Ninject;
using NLog.Config;
using NLog.Targets;
using NzbDrone.Core.Entities;
using NzbDrone.Core.Entities.Episode;
using NzbDrone.Core.Providers;
using NzbDrone.Core.Providers.Fakes;
using NzbDrone.Core.Repository;
using SubSonic.DataProviders;
using SubSonic.Repository;
using NLog;
@ -76,7 +75,7 @@ namespace NzbDrone.Core
private static void ForceMigration(IRepository repository)
{
repository.GetPaged<Series>(0, 1);
repository.GetPaged<EpisodeInfo>(0, 1);
repository.GetPaged<Episode>(0, 1);
}

View File

@ -1,14 +0,0 @@
using SubSonic.SqlGeneration.Schema;
namespace NzbDrone.Core.Entities.Episode
{
public class BasicEpisode
{
public virtual int SeriesId { get; set; }
public int SeasonNumber { get; set; }
public int EpisodeNumber { get; set; }
[SubSonicToOneRelation(ThisClassContainsJoinKey = true)]
public virtual Series Series { get; private set; }
}
}

View File

@ -1,12 +0,0 @@
using System.ServiceModel.Syndication;
using NzbDrone.Core.Entities.Quality;
namespace NzbDrone.Core.Entities.Episode
{
public class RemoteEpisode : BasicEpisode
{
public QualityTypes Quality { get; set; }
public SyndicationItem Feed { get; set; }
public bool Proper { get; set; }
}
}

View File

@ -1,9 +0,0 @@
namespace NzbDrone.Core.Entities.Notification
{
public enum NotificationStatus
{
InProgress = 0,
Completed = 1,
Failed = 2
}
}

View File

@ -1,9 +0,0 @@
namespace NzbDrone.Core.Entities.Notification
{
public enum NotificationType
{
Info = 0,
Warrning = 1,
Error = 2
}
}

Binary file not shown.

Binary file not shown.

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,16 @@
using NzbDrone.Core.Repository.Quality;
using SubSonic.SqlGeneration.Schema;
namespace NzbDrone.Core.Model
{
public class EpisodeModel
{
public string SeriesTitle { get; set; }
public int SeasonNumber { get; set; }
public int EpisodeNumber { get; set; }
public QualityTypes Quality { get; set; }
public string Path { get; set; }
public long Size { get; set; }
public bool Proper { get; set; }
}
}

View File

@ -0,0 +1,12 @@
using NzbDrone.Core.Repository.Quality;
using SubSonic.SqlGeneration.Schema;
namespace NzbDrone.Core.Model
{
internal struct EpisodeParseResult
{
internal string SeriesTitle { get; set; }
internal int SeasonNumber { get; set; }
internal int EpisodeNumber { get; set; }
}
}

View File

@ -1,6 +1,6 @@
using System;
namespace NzbDrone.Core.Entities.Notification
namespace NzbDrone.Core.Model.Notification
{
public class BasicNotification
{
@ -17,7 +17,7 @@ namespace NzbDrone.Core.Entities.Notification
public String Title { get; set; }
public NotificationType Type { get; set; }
public BasicNotificationType Type { get; set; }
/// <summary>
/// Gets or sets a value indicating whether or not this message should be automatically dismissed after a period of time.

View File

@ -0,0 +1,9 @@
namespace NzbDrone.Core.Model.Notification
{
public enum BasicNotificationType
{
Info = 0,
Warrning = 1,
Error = 2
}
}

View File

@ -1,7 +1,7 @@
using System;
using NLog;
namespace NzbDrone.Core.Entities.Notification
namespace NzbDrone.Core.Model.Notification
{
public class ProgressNotification : IDisposable
{
@ -63,14 +63,14 @@ namespace NzbDrone.Core.Entities.Notification
/// Gets or sets the status.
/// </summary>
/// <value>The status.</value>
public NotificationStatus Status { get; set; }
public ProgressNotificationStatus Status { get; set; }
public void Dispose()
{
if (Status == NotificationStatus.InProgress)
if (Status == ProgressNotificationStatus.InProgress)
{
Logger.Error("Progress notification '{0}' was unexpectedly abandoned. ID:{1} Status:{2} CurrentStatus:{3} PercentComplete:{4}", Title, Id, Status, CurrentStatus, PercentComplete);
Status = NotificationStatus.Failed;
Logger.Warn("Progress notification '{0}' was unexpectedly abandoned. ID:{1} Status:{2} CurrentStatus:{3} PercentComplete:{4}", Title, Id, Status, CurrentStatus, PercentComplete);
Status = ProgressNotificationStatus.Failed;
}
}
}

View File

@ -0,0 +1,9 @@
namespace NzbDrone.Core.Model.Notification
{
public enum ProgressNotificationStatus
{
InProgress = 0,
Completed = 1,
Failed = 2
}
}

View File

@ -121,6 +121,7 @@
<StartupObject />
</PropertyGroup>
<ItemGroup>
<Reference Include="Castle.Core, Version=2.5.0.0, Culture=neutral, PublicKeyToken=407dd0808d44fbdc, processorArchitecture=MSIL" />
<Reference Include="Exceptioneer.WindowsFormsClient, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>Libraries\Exceptioneer.WindowsFormsClient.dll</HintPath>
@ -149,20 +150,25 @@
<Reference Include="UPnP, Version=1.0.3932.37442, Culture=neutral, processorArchitecture=MSIL" />
</ItemGroup>
<ItemGroup>
<Compile Include="Entities\MediaFile.cs" />
<Compile Include="Entities\Notification\BasicNotification.cs" />
<Compile Include="Entities\Notification\NotificationStatus.cs" />
<Compile Include="Entities\Notification\NotificationType.cs" />
<Compile Include="Model\EpisodeParseResult.cs" />
<Compile Include="Model\EpisodeModel.cs" />
<Compile Include="Repository\EpisodeFile.cs" />
<Compile Include="Model\Notification\BasicNotification.cs" />
<Compile Include="Model\Notification\ProgressNotificationStatus.cs" />
<Compile Include="Model\Notification\BasicNotificationType.cs" />
<Compile Include="Instrumentation.cs" />
<Compile Include="Parser.cs" />
<Compile Include="Providers\Fakes\FakeNotificationProvider.cs" />
<Compile Include="Providers\IMediaFileProvider.cs" />
<Compile Include="Providers\INotificationProvider.cs" />
<Compile Include="Providers\IMediaDiscoveryProvider.cs" />
<Compile Include="Providers\IMediaProvider.cs" />
<Compile Include="Providers\ISyncProvider.cs" />
<Compile Include="Providers\MediaDiscoveryProvider.cs" />
<Compile Include="Providers\MediaFileProvider.cs" />
<Compile Include="Providers\SyncProvider.cs" />
<Compile Include="Providers\XBMCMediaProvider.cs" />
<Compile Include="Entities\Notification\ProgressNotification.cs" />
<Compile Include="Model\Notification\ProgressNotification.cs" />
<Compile Include="Providers\NotificationProvider.cs" />
<Compile Include="Providers\ConfigProvider.cs" />
<Compile Include="Providers\EpisodeProvider.cs" />
@ -175,15 +181,13 @@
<Compile Include="Providers\ITvDbProvider.cs" />
<Compile Include="Providers\SabProvider.cs" />
<Compile Include="Providers\SeasonProvider.cs" />
<Compile Include="Entities\Episode\RemoteEpisode.cs" />
<Compile Include="Entities\Episode\EpisodeInfo.cs" />
<Compile Include="Entities\Quality\AllowedQuality.cs" />
<Compile Include="Entities\Config.cs" />
<Compile Include="Entities\Quality\QualityProfile.cs" />
<Compile Include="Entities\Season.cs" />
<Compile Include="Entities\Episode\BasicEpisode.cs" />
<Compile Include="Entities\Quality\QualityTypes.cs" />
<Compile Include="Entities\Series.cs" />
<Compile Include="Repository\Episode.cs" />
<Compile Include="Repository\Quality\AllowedQuality.cs" />
<Compile Include="Repository\Config.cs" />
<Compile Include="Repository\Quality\QualityProfile.cs" />
<Compile Include="Repository\Season.cs" />
<Compile Include="Repository\Quality\QualityTypes.cs" />
<Compile Include="Repository\Series.cs" />
<Compile Include="CentralDispatch.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Providers\DiskProvider.cs" />
@ -215,6 +219,9 @@
</BootstrapperPackage>
</ItemGroup>
<ItemGroup>
<Content Include="Libraries\Castle.Core.dll" />
<Content Include="Libraries\Castle.Core.pdb" />
<Content Include="Libraries\Castle.Core.xml" />
<Content Include="Libraries\Exceptioneer.WindowsFormsClient.dll" />
<Content Include="Libraries\Ninject.dll" />
<Content Include="Libraries\Ninject.xml" />

135
NzbDrone.Core/Parser.cs Normal file
View File

@ -0,0 +1,135 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using NLog;
using NzbDrone.Core.Model;
using NzbDrone.Core.Providers;
using NzbDrone.Core.Repository.Quality;
namespace NzbDrone.Core
{
internal static class Parser
{
private static readonly Logger Logger = LogManager.GetCurrentClassLogger();
private static readonly Regex[] ReportTitleRegex = new[]
{
new Regex(@"(?<title>.+?)?\W(S)?(?<season>\d+)\w(?<episode>\d+)\W", RegexOptions.IgnoreCase | RegexOptions.Compiled)
};
private static readonly Regex NormalizeRegex = new Regex(@"((\s|^)the(\s|$))|((\s|^)and(\s|$))|[^a-z]", RegexOptions.IgnoreCase | RegexOptions.Compiled);
/// <summary>
/// Parses a post title into list of episodes it contains
/// </summary>
/// <param name="title">Title of the report</param>
/// <returns>List of episodes contained to the post</returns>
internal static List<EpisodeParseResult> ParseEpisodeInfo(string title)
{
Logger.Trace("Parsing string '{0}'", title);
var result = new List<EpisodeParseResult>();
foreach (var regex in ReportTitleRegex)
{
var match = regex.Matches(title);
if (match.Count != 0)
{
var seriesName = NormalizeTitle(match[0].Groups["title"].Value);
foreach (Match matchGroup in match)
{
var tuple = new EpisodeParseResult
{
SeriesTitle = seriesName,
SeasonNumber = Convert.ToInt32(matchGroup.Groups["season"].Value),
EpisodeNumber = Convert.ToInt32(matchGroup.Groups["episode"].Value)
};
result.Add(tuple);
Logger.Trace("Episode Parsed. {0}", tuple);
}
}
}
Logger.Trace("{0} episodes parsed from string.", result.Count);
return result;
}
/// <summary>
/// Parses proper status out of a report title
/// </summary>
/// <param name="title">Title of the report</param>
/// <returns></returns>
internal static bool ParseProper(string title)
{
return title.ToLower().Contains("proper");
}
internal static QualityTypes ParseQuality(string name)
{
Logger.Trace("Trying to parse quality for {0}", name);
var result = QualityTypes.Unknown;
name = name.ToLowerInvariant();
if (name.Contains("dvd"))
return QualityTypes.DVD;
if (name.Contains("xvid") || name.Contains("divx"))
{
if (name.Contains("bluray") || name.Contains("bdrip"))
{
return QualityTypes.DVD;
}
return QualityTypes.TV;
}
if (name.Contains("bluray") || name.Contains("bdrip"))
return QualityTypes.Bluray;
if (name.Contains("web-dl"))
return QualityTypes.WEBDL;
if (name.Contains("x264") || name.Contains("h264") || name.Contains("720p"))
return QualityTypes.HDTV;
//Based on extension
if (result == QualityTypes.Unknown)
{
switch (new FileInfo(name).Extension.ToLower())
{
case ".avi":
case ".xvid":
case ".wmv":
{
result = QualityTypes.TV;
break;
}
case ".mkv":
{
result = QualityTypes.HDTV;
break;
}
}
}
Logger.Trace("Quality Parsed:{0} Title:", result, name);
return result;
}
/// <summary>
/// Normalizes the title. removing all non-word characters as well as common tokens
/// such as 'the' and 'and'
/// </summary>
/// <param name="title">title</param>
/// <returns></returns>
internal static string NormalizeTitle(string title)
{
return NormalizeRegex.Replace(title, String.Empty).ToLower();
}
}
}

View File

@ -13,6 +13,7 @@
// *
// */
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following
@ -49,4 +50,5 @@ using System.Runtime.InteropServices;
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("0.2.0.*")]
[assembly: AssemblyVersion("0.2.0.*")]
[assembly: InternalsVisibleTo("NzbDrone.Core.Test")]

View File

@ -1,6 +1,6 @@
using System;
using NLog;
using NzbDrone.Core.Entities;
using NzbDrone.Core.Repository;
using SubSonic.Repository;
namespace NzbDrone.Core.Providers

View File

@ -17,6 +17,11 @@ namespace NzbDrone.Core.Providers
return Directory.GetDirectories(path);
}
public string[] GetFiles(string path, string pattern, SearchOption searchOption)
{
return Directory.GetFiles(path, pattern, searchOption);
}
public String CreateDirectory(string path)
{
return Directory.CreateDirectory(path).FullName;

View File

@ -3,8 +3,8 @@ using System.Linq;
using System.Collections.Generic;
using System.Text.RegularExpressions;
using NLog;
using NzbDrone.Core.Entities.Episode;
using NzbDrone.Core.Entities.Quality;
using NzbDrone.Core.Model;
using NzbDrone.Core.Repository;
using SubSonic.Repository;
namespace NzbDrone.Core.Providers
@ -12,21 +12,6 @@ namespace NzbDrone.Core.Providers
public class EpisodeProvider : IEpisodeProvider
{
//TODO: Remove parsing of the series name, it should be done in series provider
private static readonly Regex ParseRegex = new Regex(@"(?<showName>.*)
(?:
s(?<seasonNumber>\d+)e(?<episodeNumber>\d+)-?e(?<episodeNumber2>\d+)
| s(?<seasonNumber>\d+)e(?<episodeNumber>\d+)
| (?<seasonNumber>\d+)x(?<episodeNumber>\d+)
| (?<airDate>\d{4}.\d{2}.\d{2})
)
(?:
(?<episodeName>.*?)
(?<release>
(?:hdtv|pdtv|xvid|ws|720p|x264|bdrip|dvdrip|dsr|proper)
.*)
| (?<episodeName>.*)
)", RegexOptions.IgnoreCase | RegexOptions.Compiled | RegexOptions.IgnorePatternWhitespace);
private readonly IRepository _sonicRepo;
private readonly ISeriesProvider _series;
@ -43,35 +28,17 @@ namespace NzbDrone.Core.Providers
_seasons = seasonProvider;
}
public EpisodeInfo GetEpisode(long id)
public Episode GetEpisode(long id)
{
return _sonicRepo.Single<EpisodeInfo>(e => e.EpisodeId == id);
return _sonicRepo.Single<Episode>(e => e.EpisodeId == id);
}
public void UpdateEpisode(EpisodeInfo episode)
public IList<Episode> GetEpisodeBySeries(long seriesId)
{
var episodeToUpdate = _sonicRepo.Single<EpisodeInfo>(e => e.EpisodeId == episode.EpisodeId);
episodeToUpdate.AirDate = episode.AirDate;
episodeToUpdate.Overview = episode.Overview;
episodeToUpdate.Title = episode.Title;
episodeToUpdate.EpisodeNumber = episode.EpisodeNumber;
episodeToUpdate.SeasonNumber = episode.SeasonNumber;
_sonicRepo.Update<EpisodeInfo>(episodeToUpdate);
return _sonicRepo.Find<Episode>(e => e.SeriesId == seriesId);
}
public IList<EpisodeInfo> GetEpisodesBySeason(long seasonId)
{
return _sonicRepo.Find<EpisodeInfo>(e => e.SeasonId == seasonId);
}
public IList<EpisodeInfo> GetEpisodeBySeries(long seriesId)
{
return _sonicRepo.Find<EpisodeInfo>(e => e.SeriesId == seriesId);
}
public String GetSabTitle(BasicEpisode episode)
public String GetSabTitle(Episode episode)
{
var series = _series.GetSeries(episode.SeriesId);
if (series == null) throw new ArgumentException("Unknown series. ID: " + episode.SeriesId);
@ -85,7 +52,7 @@ namespace NzbDrone.Core.Providers
/// </summary>
/// <param name="episode">Episode that needs to be checked</param>
/// <returns></returns>
public bool IsNeeded(RemoteEpisode episode)
public bool IsNeeded(EpisodeModel episode)
{
throw new NotImplementedException();
}
@ -97,8 +64,8 @@ namespace NzbDrone.Core.Providers
int failCount = 0;
var targetSeries = _tvDb.GetSeries(seriesId, true);
var updateList = new List<EpisodeInfo>();
var newList = new List<EpisodeInfo>();
var updateList = new List<Episode>();
var newList = new List<Episode>();
Logger.Debug("Updating season info for series:{0}", seriesId);
targetSeries.Episodes.Select(e => new { e.SeasonId, e.SeasonNumber })
@ -110,7 +77,7 @@ namespace NzbDrone.Core.Providers
try
{
Logger.Debug("Updating info for series:{0} - episode:{1}", seriesId, episode.Id);
var newEpisode = new EpisodeInfo()
var newEpisode = new Episode()
{
AirDate = episode.FirstAired,
EpisodeId = episode.Id,
@ -123,7 +90,7 @@ namespace NzbDrone.Core.Providers
Title = episode.EpisodeName
};
if (_sonicRepo.Exists<EpisodeInfo>(e => e.EpisodeId == newEpisode.EpisodeId))
if (_sonicRepo.Exists<Episode>(e => e.EpisodeId == newEpisode.EpisodeId))
{
updateList.Add(newEpisode);
}
@ -146,38 +113,5 @@ namespace NzbDrone.Core.Providers
Logger.Info("Finished episode refresh for series:{0}. Success:{1} - Fail:{2} ", seriesId, successCount, failCount);
}
/// <summary>
/// Parses a post title into list of episode objects
/// </summary>
/// <param name="title">Title of the report</param>
/// <returns>List of episodes relating to the post</returns>
public static List<RemoteEpisode> Parse(string title)
{
var match = ParseRegex.Match(title);
if (!match.Success)
throw new ArgumentException(String.Format("Title doesn't match any know patterns. [{0}]", title));
var result = new List<RemoteEpisode>();
result.Add(new RemoteEpisode { EpisodeNumber = Convert.ToInt32(match.Groups["episodeNumber"].Value) });
if (match.Groups["episodeNumber2"].Success)
{
result.Add(new RemoteEpisode { EpisodeNumber = Convert.ToInt32(match.Groups["episodeNumber2"].Value) });
}
foreach (var ep in result)
{
ep.SeasonNumber = Convert.ToInt32(match.Groups["seasonNumber"].Value);
ep.Proper = title.Contains("PROPER");
ep.Quality = QualityTypes.Unknown;
}
return result;
}
}
}

View File

@ -1,6 +1,6 @@
using System;
using System.Collections.Generic;
using NzbDrone.Core.Entities.Notification;
using NzbDrone.Core.Model.Notification;
namespace NzbDrone.Core.Providers.Fakes
{
@ -33,8 +33,8 @@ namespace NzbDrone.Core.Providers.Fakes
get
{
fakeNotification.Status = NotificationStatus.InProgress;
fakeNotification.Status = NotificationStatus.InProgress;
fakeNotification.Status = ProgressNotificationStatus.InProgress;
fakeNotification.Status = ProgressNotificationStatus.InProgress;
fakeNotification2.CurrentStatus = DateTime.UtcNow.ToString();
fakeNotification.CurrentStatus = DateTime.Now.ToString();
return new List<ProgressNotification> { fakeNotification };

View File

@ -1,4 +1,5 @@
using System;
using System.IO;
namespace NzbDrone.Core.Providers
{
@ -7,5 +8,6 @@ namespace NzbDrone.Core.Providers
bool Exists(string path);
string[] GetDirectories(string path);
String CreateDirectory(string path);
string[] GetFiles(string path, string pattern, SearchOption searchOption);
}
}

View File

@ -1,23 +1,22 @@
using System;
using System.Collections.Generic;
using NzbDrone.Core.Entities.Episode;
using NzbDrone.Core.Model;
using NzbDrone.Core.Repository;
namespace NzbDrone.Core.Providers
{
public interface IEpisodeProvider
{
EpisodeInfo GetEpisode(long id);
void UpdateEpisode(EpisodeInfo episode);
IList<EpisodeInfo> GetEpisodesBySeason(long seasonId);
IList<EpisodeInfo> GetEpisodeBySeries(long seriesId);
String GetSabTitle(BasicEpisode episode);
Episode GetEpisode(long id);
IList<Episode> GetEpisodeBySeries(long seriesId);
String GetSabTitle(Episode episode);
/// <summary>
/// Comprehensive check on whether or not this episode is needed.
/// </summary>
/// <param name="episode">Episode that needs to be checked</param>
/// <returns></returns>
bool IsNeeded(RemoteEpisode episode);
bool IsNeeded(EpisodeModel episode);
void RefreshEpisodeInfo(int seriesId);
}

View File

@ -0,0 +1,13 @@
using NzbDrone.Core.Repository;
namespace NzbDrone.Core.Providers
{
public interface IMediaFileProvider
{
/// <summary>
/// Scans the specified series folder for media files
/// </summary>
/// <param name="series">The series to be scanned</param>
void Scan(Series series);
}
}

View File

@ -1,6 +1,6 @@
using System;
using System.Collections.Generic;
using NzbDrone.Core.Entities.Notification;
using NzbDrone.Core.Model.Notification;
namespace NzbDrone.Core.Providers
{

View File

@ -1,5 +1,5 @@
using System.Collections.Generic;
using NzbDrone.Core.Entities;
using NzbDrone.Core.Repository;
namespace NzbDrone.Core.Providers
{

View File

@ -1,7 +1,8 @@
using System;
using System.Collections.Generic;
using System.Linq;
using NzbDrone.Core.Entities;
using NzbDrone.Core.Repository;
using NzbDrone.Core.Repository;
using TvdbLib.Data;
namespace NzbDrone.Core.Providers

View File

@ -0,0 +1,49 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using NLog;
using NzbDrone.Core.Repository;
namespace NzbDrone.Core.Providers
{
public class MediaFileProvider : IMediaFileProvider
{
private readonly IDiskProvider _diskProvider;
private readonly IEpisodeProvider _episodeProvider;
private static readonly Logger Logger = LogManager.GetCurrentClassLogger();
private static readonly string[] MediaExtentions = new[] { "*.mkv", "*.avi", "*.wmv" };
public MediaFileProvider(IDiskProvider diskProvider, IEpisodeProvider episodeProvider)
{
_diskProvider = diskProvider;
_episodeProvider = episodeProvider;
}
/// <summary>
/// Scans the specified series folder for media files
/// </summary>
/// <param name="series">The series to be scanned</param>
public void Scan(Series series)
{
var mediaFileList = new List<string>();
Logger.Info("Scanning '{0}'", series.Path);
foreach (var ext in MediaExtentions)
{
mediaFileList.AddRange(_diskProvider.GetFiles(series.Path, ext, SearchOption.AllDirectories));
}
Logger.Info("{0} media files were found", mediaFileList.Count);
foreach (var file in mediaFileList)
{
var episode = Parser.ParseEpisodeInfo(file);
}
}
}
}

View File

@ -1,7 +1,7 @@
using System;
using System.Linq;
using System.Collections.Generic;
using NzbDrone.Core.Entities.Notification;
using NzbDrone.Core.Model.Notification;
namespace NzbDrone.Core.Providers
{
@ -28,7 +28,7 @@ namespace NzbDrone.Core.Providers
public List<ProgressNotification> GetProgressNotifications
{
get { return new List<ProgressNotification>(_progressNotification.Values.Where(p => p.Status == NotificationStatus.InProgress)); }
get { return new List<ProgressNotification>(_progressNotification.Values.Where(p => p.Status == ProgressNotificationStatus.InProgress)); }
}
public void Dismiss(Guid notificationId)

View File

@ -1,7 +1,7 @@
using System;
using System.Collections.Generic;
using NLog;
using NzbDrone.Core.Entities;
using NzbDrone.Core.Repository;
using SubSonic.Repository;
namespace NzbDrone.Core.Providers

View File

@ -4,8 +4,7 @@ using System.IO;
using System.Linq;
using System.Text.RegularExpressions;
using NLog;
using NzbDrone.Core.Entities;
using NzbDrone.Core.Entities.Notification;
using NzbDrone.Core.Repository;
using SubSonic.Repository;
using TvdbLib.Data;
@ -16,30 +15,12 @@ namespace NzbDrone.Core.Providers
//TODO: Remove parsing of rest of tv show info we just need the show name
//Trims all white spaces and separators from the end of the title.
private static readonly Regex CleanTitleRegex = new Regex(@"[\s.][^a-z]*$", RegexOptions.IgnoreCase | RegexOptions.Compiled);
private static readonly Regex ParseRegex = new Regex(@"(?<showName>.*)
(?:
s(?<seasonNumber>\d+)e(?<episodeNumber>\d+)-?e(?<episodeNumber2>\d+)
| s(?<seasonNumber>\d+)e(?<episodeNumber>\d+)
| (?<seasonNumber>\d+)x(?<episodeNumber>\d+)
| (?<airDate>\d{4}.\d{2}.\d{2})
)
(?:
(?<episodeName>.*?)
(?<release>
(?:hdtv|pdtv|xvid|ws|720p|x264|bdrip|dvdrip|dsr|proper)
.*)
| (?<episodeName>.*)
)", RegexOptions.IgnoreCase | RegexOptions.Compiled | RegexOptions.IgnorePatternWhitespace);
private readonly IConfigProvider _config;
private readonly IDiskProvider _diskProvider;
private readonly IRepository _sonioRepo;
private readonly ITvDbProvider _tvDb;
private static readonly Logger Logger = LogManager.GetCurrentClassLogger();
private static readonly Regex CleanUpRegex = new Regex(@"((\s|^)the(\s|$))|((\s|^)and(\s|$))|[^a-z]", RegexOptions.IgnoreCase | RegexOptions.Compiled);
public SeriesProvider(IDiskProvider diskProvider, IConfigProvider configProvider, IRepository dataRepository, ITvDbProvider tvDbProvider)
{
@ -71,21 +52,6 @@ namespace NzbDrone.Core.Providers
return _sonioRepo.Exists<Series>(c => c.SeriesId == id && c.Monitored);
}
/// <summary>
/// Parses series name out of a post title
/// </summary>
/// <param name="postTitle">Title of the report</param>
/// <returns>Name series this report belongs to</returns>
public static string ParseTitle(string postTitle)
{
var match = ParseRegex.Match(postTitle);
if (!match.Success)
throw new ArgumentException(String.Format("Title doesn't match any know patterns. [{0}]", postTitle));
return CleanTitleRegex.Replace(match.Groups["showName"].Value, String.Empty).Replace(".", " ");
}
public List<String> GetUnmappedFolders()
{
Logger.Debug("Generating list of unmapped folders");
@ -130,7 +96,7 @@ namespace NzbDrone.Core.Providers
repoSeries.Status = series.Status;
repoSeries.Language = series.Language != null ? series.Language.Abbriviation : string.Empty;
repoSeries.Path = path;
repoSeries.CleanTitle = CleanUpRegex.Replace(series.SeriesName, "").ToLower();
repoSeries.CleanTitle = Parser.NormalizeTitle(series.SeriesName);
_sonioRepo.Add(repoSeries);
}

View File

@ -6,7 +6,7 @@ using System.Linq;
using System.Text;
using System.Threading;
using NLog;
using NzbDrone.Core.Entities.Notification;
using NzbDrone.Core.Model.Notification;
namespace NzbDrone.Core.Providers
{
@ -58,7 +58,6 @@ namespace NzbDrone.Core.Providers
{
_notificationProvider.Register(_seriesSyncNotification);
_seriesSyncNotification.CurrentStatus = "Analysing Folder";
Thread.Sleep(20000);
var unmappedFolders = _seriesProvider.GetUnmappedFolders();
_seriesSyncNotification.ProgressMax = unmappedFolders.Count;
@ -100,7 +99,7 @@ namespace NzbDrone.Core.Providers
_seriesSyncNotification.CurrentStatus = "Series Scan Completed";
Logger.Info("Series folders scan has successfully completed.");
Thread.Sleep(3000);
_seriesSyncNotification.Status = NotificationStatus.Completed;
_seriesSyncNotification.Status = ProgressNotificationStatus.Completed;
}
}
catch (Exception e)

View File

@ -1,6 +1,6 @@
using SubSonic.SqlGeneration.Schema;
namespace NzbDrone.Core.Entities
namespace NzbDrone.Core.Repository
{
[SubSonicTableNameOverride("Config")]
public class Config

View File

@ -1,25 +1,29 @@
using System;
using SubSonic.SqlGeneration.Schema;
namespace NzbDrone.Core.Entities.Episode
namespace NzbDrone.Core.Repository
{
[SubSonicTableNameOverride("EpisodeInfo")]
public class EpisodeInfo : BasicEpisode
public class Episode
{
[SubSonicPrimaryKey(false)]
public virtual int EpisodeId { get; set; }
public virtual int SeriesId { get; set; }
public int SeasonNumber { get; set; }
public int EpisodeNumber { get; set; }
public int SeasonId { get; set; }
public string Title { get; set; }
public DateTime AirDate { get; set; }
[SubSonicLongString]
public string Overview { get; set; }
public string Language { get; set; }
public int MediaFileId { get; set; }
[SubSonicNullString]
public string Path { get; set; }
public long? Size { get; set; }
[SubSonicToOneRelation(ThisClassContainsJoinKey = true)]
public virtual Season Season { get; set; }
[SubSonicToOneRelation(ThisClassContainsJoinKey = true)]
public virtual MediaFile File { get; set; }
public virtual Series Series { get; private set; }
}
}

View File

@ -1,15 +1,21 @@
using NzbDrone.Core.Entities.Quality;
using System;
using NzbDrone.Core.Repository.Quality;
using SubSonic.SqlGeneration.Schema;
namespace NzbDrone.Core.Entities
namespace NzbDrone.Core.Repository
{
public class MediaFile
class EpisodeFile
{
[SubSonicPrimaryKey]
public virtual int FileId { get; set; }
public int EpisodeId { get; set; }
public string Path { get; set; }
public QualityTypes Quality { get; set; }
public long Size { get; set; }
public bool Proper { get; set; }
public long Size { get; set; }
public DateTime DateAdded { get; set; }
[SubSonicToOneRelation]
public virtual Episode Episode { get; set; }
}
}

View File

@ -1,4 +1,4 @@
namespace NzbDrone.Core.Entities.Quality
namespace NzbDrone.Core.Repository.Quality
{
public class AllowedQuality
{

View File

@ -3,7 +3,7 @@ using System.Collections.Generic;
using System.ComponentModel;
using SubSonic.SqlGeneration.Schema;
namespace NzbDrone.Core.Entities.Quality
namespace NzbDrone.Core.Repository.Quality
{
public class QualityProfile
{

View File

@ -1,4 +1,4 @@
namespace NzbDrone.Core.Entities.Quality
namespace NzbDrone.Core.Repository.Quality
{
// ReSharper disable InconsistentNaming
/// <summary>
@ -13,7 +13,7 @@ namespace NzbDrone.Core.Entities.Quality
/// <summary>
/// SD File (Source could be HD)
/// </summary>
SDTV = 1,
TV = 1,
/// <summary>
/// SD File (DVD Source)
/// </summary>

View File

@ -1,7 +1,7 @@
using System.Collections.Generic;
using SubSonic.SqlGeneration.Schema;
namespace NzbDrone.Core.Entities
namespace NzbDrone.Core.Repository
{
public class Season
{
@ -13,7 +13,7 @@ namespace NzbDrone.Core.Entities
public string Folder { get; set; }
[SubSonicToManyRelation]
public virtual List<Episode.BasicEpisode> Episodes { get; private set; }
public virtual List<Episode> Episodes { get; private set; }
[SubSonicToOneRelation(ThisClassContainsJoinKey = true)]
public virtual Series Series { get; private set; }

View File

@ -1,9 +1,8 @@
using System;
using System.Collections.Generic;
using NzbDrone.Core.Entities.Episode;
using SubSonic.SqlGeneration.Schema;
namespace NzbDrone.Core.Entities
namespace NzbDrone.Core.Repository
{
public class Series
{
@ -14,6 +13,7 @@ namespace NzbDrone.Core.Entities
public string CleanTitle { get; set; }
[SubSonicNullString]
public string Status { get; set; }
[SubSonicLongString]
@ -33,7 +33,7 @@ namespace NzbDrone.Core.Entities
public virtual List<Season> Seasons { get; private set; }
[SubSonicToManyRelation]
public virtual List<EpisodeInfo> Episodes { get; private set; }
public virtual List<Episode> Episodes { get; private set; }
}

View File

@ -33,7 +33,7 @@ $(function () {
//set the message text
$("#msgText").text(sMsg);
//show the message
$('#msgBox').slideUp(speed, null);
$('#msgBox').slideDown(speed, null);
}
function CloseMsg() {

View File

@ -1,4 +1,4 @@
<%@ Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage<NzbDrone.Core.Entities.Series>" %>
<%@ Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage<NzbDrone.Core.Repository.Series>" %>
<%@ Import Namespace="Telerik.Web.Mvc.UI" %>
<asp:Content ID="Content1" ContentPlaceHolderID="TitleContent" runat="server">

View File

@ -1,4 +1,4 @@
<%@ Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage<IEnumerable<NzbDrone.Core.Entities.Series>>" %>
<%@ Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage<IEnumerable<NzbDrone.Core.Repository.Series>>" %>
<%@ Import Namespace="Telerik.Web.Mvc.UI" %>
<%@ Import Namespace="System.Globalization" %>

View File

@ -1,10 +1,9 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Text;
using Microsoft.Web.Administration;
using System.Xml.Linq;
using System.Xml.XPath;
using NLog;
namespace NzbDrone
@ -46,7 +45,16 @@ namespace NzbDrone
//Set Variables for the config file.
Environment.SetEnvironmentVariable("NZBDRONE_PATH", Config.ProjectRoot);
UpdateIISConfig();
try
{
UpdateIISConfig();
}
catch (Exception e)
{
Logger.Error("An error has occured while trying to update the config file.", e);
}
Logger.Info("Starting process. [{0}]", IISProcess.StartInfo.FileName);
IISProcess.Start();
@ -101,10 +109,22 @@ namespace NzbDrone
private static void UpdateIISConfig()
{
string configPath = Path.Combine(IISFolder, @"AppServer\applicationhost.config");
Logger.Info(@"Server configuration file: {0}", configPath);
Logger.Info(@"Configuring server to: [http://localhost:{0}]", Config.Port);
var serverManager = new ServerManager(Path.Combine(IISFolder, @"AppServer\applicationhost.config"));
serverManager.Sites["NZBDrone"].Bindings[0].BindingInformation = string.Format("*:{0}:", Config.Port);
serverManager.CommitChanges();
var configXml = XDocument.Load(configPath);
var bindings = configXml.XPathSelectElement("configuration/system.applicationHost/sites").Elements("site").Where(d => d.Attribute("name").Value.ToLowerInvariant() == "nzbdrone").First().Element("bindings");
bindings.Descendants().Remove();
bindings.Add(
new XElement("binding",
new XAttribute("protocol", "http"),
new XAttribute("bindingInformation", String.Format("*:{0}:", Config.Port))
));
configXml.Save(configPath);
}
private static string CleanPath(string path)

View File

@ -50,6 +50,9 @@
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
<Reference Include="Accessibility">
<EmbedInteropTypes>True</EmbedInteropTypes>
</Reference>
<Reference Include="EnvDTE, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<EmbedInteropTypes>True</EmbedInteropTypes>
</Reference>
@ -60,9 +63,6 @@
<SpecificVersion>False</SpecificVersion>
<HintPath>..\NzbDrone.Core\Libraries\Exceptioneer.WindowsFormsClient.dll</HintPath>
</Reference>
<Reference Include="Microsoft.Web.Administration, Version=7.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<Private>True</Private>
</Reference>
<Reference Include="NLog, Version=2.0.0.0, Culture=neutral, PublicKeyToken=5120e14c03d0593c, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\NzbDrone.Core\Libraries\NLog.dll</HintPath>

View File

@ -4,6 +4,6 @@
<supportedRuntime version="v4.0" />
</startup>
<appSettings>
<add key="port" value="8981" />
<add key="port" value="8989" />
</appSettings>
</configuration>