fixed a bunch of things. added basic support for file scanning. logs are now avilable in the UI
This commit is contained in:
parent
c8a8fb4d62
commit
fa0af257ff
|
@ -30,4 +30,4 @@ _ReSharper*/
|
||||||
[Ll]ogs/
|
[Ll]ogs/
|
||||||
/[Pp]ackage/
|
/[Pp]ackage/
|
||||||
#NZBDrone specific
|
#NZBDrone specific
|
||||||
nzbdrone.db
|
*.db
|
|
@ -0,0 +1,15 @@
|
||||||
|
<configuration>
|
||||||
|
|
||||||
|
<configSections>
|
||||||
|
<section name="configurationRedirection" />
|
||||||
|
</configSections>
|
||||||
|
|
||||||
|
<configProtectedData>
|
||||||
|
<providers>
|
||||||
|
<add name="IISRsaProvider" type="" description="Uses RsaCryptoServiceProvider to encrypt and decrypt" keyContainerName="iisConfigurationKey" cspProviderName="" useMachineContainer="true" useOAEP="false" />
|
||||||
|
</providers>
|
||||||
|
</configProtectedData>
|
||||||
|
|
||||||
|
<configurationRedirection />
|
||||||
|
|
||||||
|
</configuration>
|
|
@ -1,6 +1,9 @@
|
||||||
using System;
|
using System;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using MbUnit.Framework;
|
using MbUnit.Framework;
|
||||||
|
using NLog;
|
||||||
|
using NLog.Config;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
namespace NzbDrone.Core.Test
|
namespace NzbDrone.Core.Test
|
||||||
{
|
{
|
||||||
|
@ -10,24 +13,43 @@ namespace NzbDrone.Core.Test
|
||||||
[TearDown]
|
[TearDown]
|
||||||
public void TearDown()
|
public void TearDown()
|
||||||
{
|
{
|
||||||
var dbFiles = Directory.GetFiles(Directory.GetCurrentDirectory(), "*.testdb");
|
foreach (var file in Directory.GetFiles(Directory.GetCurrentDirectory(), "*.db", SearchOption.AllDirectories))
|
||||||
|
|
||||||
foreach (var dbFile in dbFiles)
|
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
File.Delete(dbFile);
|
File.Delete(file);
|
||||||
}
|
}
|
||||||
catch
|
catch
|
||||||
{ }
|
{ }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[FixtureTearDown]
|
||||||
|
public void FixtureTearDown()
|
||||||
|
{
|
||||||
|
foreach (var file in Directory.GetFiles(Directory.GetCurrentDirectory(), "*.*", SearchOption.AllDirectories))
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
File.Delete(file);
|
||||||
|
}
|
||||||
|
catch { }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[SetUp]
|
[SetUp]
|
||||||
public void Setup()
|
public void SetUp()
|
||||||
{
|
{
|
||||||
Instrumentation.Setup();
|
try
|
||||||
|
{
|
||||||
|
LogManager.Configuration = new XmlLoggingConfiguration(Path.Combine(CentralDispatch.AppPath, "log.config"), false);
|
||||||
}
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
Console.WriteLine("Unable to configure logging. " + e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,6 +1,8 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Linq.Expressions;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
using FizzWare.NBuilder;
|
||||||
using Gallio.Framework;
|
using Gallio.Framework;
|
||||||
using MbUnit.Framework;
|
using MbUnit.Framework;
|
||||||
using MbUnit.Framework.ContractVerifiers;
|
using MbUnit.Framework.ContractVerifiers;
|
||||||
|
@ -18,68 +20,144 @@ namespace NzbDrone.Core.Test
|
||||||
// ReSharper disable InconsistentNaming
|
// ReSharper disable InconsistentNaming
|
||||||
public class MediaFileProviderTests
|
public class MediaFileProviderTests
|
||||||
{
|
{
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void scan_test()
|
[Description("Verifies that a new file imported properly")]
|
||||||
|
public void import_new_file()
|
||||||
{
|
{
|
||||||
//Arrange
|
//Arrange
|
||||||
var repository = new Mock<IRepository>();
|
/////////////////////////////////////////
|
||||||
repository.Setup(c => c.Update(It.IsAny<Episode>())).Verifiable();
|
|
||||||
|
|
||||||
var diskProvider = MockLib.GetStandardDisk(1, 2);
|
//Constants
|
||||||
|
const string fileName = "WEEDS.S03E01-06.DUAL.BDRip.XviD.AC3.-HELLYWOOD.avi";
|
||||||
|
const int seasonNumber = 3;
|
||||||
|
const int episodeNumner = 01;
|
||||||
|
const int size = 12345;
|
||||||
|
|
||||||
|
//Fakes
|
||||||
|
var fakeSeries = Builder<Series>.CreateNew().Build();
|
||||||
|
var fakeEpisode = Builder<Episode>.CreateNew().With(c => c.SeriesId = fakeSeries.SeriesId).Build();
|
||||||
|
|
||||||
|
//Mocks
|
||||||
|
var repository = new Mock<IRepository>();
|
||||||
|
repository.Setup(r => r.Exists<EpisodeFile>(It.IsAny<Expression<Func<EpisodeFile, Boolean>>>())).Returns(false).Verifiable();
|
||||||
|
|
||||||
|
var episodeProvider = new Mock<IEpisodeProvider>();
|
||||||
|
episodeProvider.Setup(e => e.GetEpisode(fakeSeries.SeriesId, seasonNumber, episodeNumner)).Returns(fakeEpisode).Verifiable();
|
||||||
|
|
||||||
|
var diskProvider = new Mock<IDiskProvider>();
|
||||||
|
diskProvider.Setup(e => e.GetSize(fileName)).Returns(12345).Verifiable();
|
||||||
|
|
||||||
var kernel = new MockingKernel();
|
var kernel = new MockingKernel();
|
||||||
kernel.Bind<IDiskProvider>().ToConstant(diskProvider);
|
|
||||||
kernel.Bind<IRepository>().ToConstant(repository.Object);
|
kernel.Bind<IRepository>().ToConstant(repository.Object);
|
||||||
|
|
||||||
|
kernel.Bind<IEpisodeProvider>().ToConstant(episodeProvider.Object);
|
||||||
|
kernel.Bind<IDiskProvider>().ToConstant(diskProvider.Object);
|
||||||
kernel.Bind<IMediaFileProvider>().To<MediaFileProvider>();
|
kernel.Bind<IMediaFileProvider>().To<MediaFileProvider>();
|
||||||
|
|
||||||
var fakeSeries = new Series()
|
|
||||||
{
|
|
||||||
Path = MockLib.StandardSeries[0]
|
|
||||||
};
|
|
||||||
|
|
||||||
//Act
|
//Act
|
||||||
kernel.Get<IMediaFileProvider>().Scan(fakeSeries);
|
var result = kernel.Get<IMediaFileProvider>().ImportFile(fakeSeries, fileName);
|
||||||
|
|
||||||
//Assert
|
//Assert
|
||||||
repository.Verify(c => c.Update(It.IsAny<Episode>()), Times.Exactly(1 * 2));
|
repository.VerifyAll();
|
||||||
|
episodeProvider.VerifyAll();
|
||||||
|
diskProvider.VerifyAll();
|
||||||
|
Assert.IsNotNull(result);
|
||||||
|
repository.Verify(r => r.Add<EpisodeFile>(result), Times.Once());
|
||||||
|
|
||||||
|
Assert.AreEqual(fakeEpisode.EpisodeId, result.EpisodeId);
|
||||||
|
Assert.AreEqual(fakeEpisode.SeriesId, result.SeriesId);
|
||||||
|
Assert.AreEqual(QualityTypes.DVD, result.Quality);
|
||||||
|
Assert.AreEqual(Parser.NormalizePath(fileName), result.Path);
|
||||||
|
Assert.AreEqual(size, result.Size);
|
||||||
|
Assert.AreEqual(false, result.Proper);
|
||||||
|
Assert.AreNotEqual(new DateTime(), result.DateAdded);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
[Description("Verifies that an existing file will skip import")]
|
||||||
|
public void import_existing_file()
|
||||||
|
{
|
||||||
|
//Arrange
|
||||||
|
/////////////////////////////////////////
|
||||||
|
|
||||||
|
//Constants
|
||||||
|
const string fileName = "WEEDS.S03E01-06.DUAL.BDRip.XviD.AC3.-HELLYWOOD.avi";
|
||||||
|
|
||||||
|
//Fakes
|
||||||
|
var fakeSeries = Builder<Series>.CreateNew().Build();
|
||||||
|
var fakeEpisode = Builder<Episode>.CreateNew().With(c => c.SeriesId = fakeSeries.SeriesId).Build();
|
||||||
|
|
||||||
|
//Mocks
|
||||||
|
var repository = new Mock<IRepository>(MockBehavior.Strict);
|
||||||
|
repository.Setup(r => r.Exists<EpisodeFile>(It.IsAny<Expression<Func<EpisodeFile, Boolean>>>())).Returns(true).Verifiable();
|
||||||
|
|
||||||
|
var episodeProvider = new Mock<IEpisodeProvider>(MockBehavior.Strict);
|
||||||
|
var diskProvider = new Mock<IDiskProvider>(MockBehavior.Strict);
|
||||||
|
|
||||||
|
var kernel = new MockingKernel();
|
||||||
|
kernel.Bind<IRepository>().ToConstant(repository.Object);
|
||||||
|
|
||||||
|
kernel.Bind<IEpisodeProvider>().ToConstant(episodeProvider.Object);
|
||||||
|
kernel.Bind<IDiskProvider>().ToConstant(diskProvider.Object);
|
||||||
|
kernel.Bind<IMediaFileProvider>().To<MediaFileProvider>();
|
||||||
|
|
||||||
|
//Act
|
||||||
|
var result = kernel.Get<IMediaFileProvider>().ImportFile(fakeSeries, fileName);
|
||||||
|
|
||||||
|
//Assert
|
||||||
|
repository.VerifyAll();
|
||||||
|
episodeProvider.VerifyAll();
|
||||||
|
diskProvider.VerifyAll();
|
||||||
|
Assert.IsNull(result);
|
||||||
|
repository.Verify(r => r.Add<EpisodeFile>(result), Times.Never());
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
[Row("WEEDS.S03E01-06.DUAL.BDRip.XviD.AC3.-HELLYWOOD", 3, 1)]
|
[Description("Verifies that a file with no episode is skipped")]
|
||||||
[Row("Two.and.a.Half.Me.103.720p.HDTV.X264-DIMENSION", 1, 3)]
|
public void import_file_with_no_episode()
|
||||||
[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);
|
//Arrange
|
||||||
Assert.Count(1, result);
|
/////////////////////////////////////////
|
||||||
Assert.AreEqual(season, result[0].SeasonNumber);
|
|
||||||
Assert.AreEqual(episode, result[0].EpisodeNumber);
|
//Constants
|
||||||
|
const string fileName = "WEEDS.S03E01-06.DUAL.BDRip.XviD.AC3.-HELLYWOOD.avi";
|
||||||
|
const int seasonNumber = 3;
|
||||||
|
const int episodeNumner = 01;
|
||||||
|
|
||||||
|
//Fakes
|
||||||
|
var fakeSeries = Builder<Series>.CreateNew().Build();
|
||||||
|
|
||||||
|
//Mocks
|
||||||
|
var repository = new Mock<IRepository>(MockBehavior.Strict);
|
||||||
|
repository.Setup(r => r.Exists<EpisodeFile>(It.IsAny<Expression<Func<EpisodeFile, Boolean>>>())).Returns(false).Verifiable();
|
||||||
|
|
||||||
|
var episodeProvider = new Mock<IEpisodeProvider>(MockBehavior.Strict);
|
||||||
|
episodeProvider.Setup(e => e.GetEpisode(fakeSeries.SeriesId, seasonNumber, episodeNumner)).Returns<Episode>(null).Verifiable();
|
||||||
|
|
||||||
|
var diskProvider = new Mock<IDiskProvider>(MockBehavior.Strict);
|
||||||
|
|
||||||
|
|
||||||
|
var kernel = new MockingKernel();
|
||||||
|
kernel.Bind<IRepository>().ToConstant(repository.Object);
|
||||||
|
kernel.Bind<IEpisodeProvider>().ToConstant(episodeProvider.Object);
|
||||||
|
kernel.Bind<IDiskProvider>().ToConstant(diskProvider.Object);
|
||||||
|
kernel.Bind<IMediaFileProvider>().To<MediaFileProvider>();
|
||||||
|
|
||||||
|
//Act
|
||||||
|
var result = kernel.Get<IMediaFileProvider>().ImportFile(fakeSeries, fileName);
|
||||||
|
|
||||||
|
//Assert
|
||||||
|
repository.VerifyAll();
|
||||||
|
episodeProvider.VerifyAll();
|
||||||
|
diskProvider.VerifyAll();
|
||||||
|
Assert.IsNull(result);
|
||||||
|
repository.Verify(r => r.Add<EpisodeFile>(result), Times.Never());
|
||||||
}
|
}
|
||||||
|
|
||||||
[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);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -7,6 +7,7 @@ using System.Text;
|
||||||
using FizzWare.NBuilder;
|
using FizzWare.NBuilder;
|
||||||
using Moq;
|
using Moq;
|
||||||
using NLog;
|
using NLog;
|
||||||
|
using NzbDrone.Core.Instrumentation;
|
||||||
using NzbDrone.Core.Providers;
|
using NzbDrone.Core.Providers;
|
||||||
using SubSonic.DataProviders;
|
using SubSonic.DataProviders;
|
||||||
using SubSonic.Repository;
|
using SubSonic.Repository;
|
||||||
|
@ -32,10 +33,10 @@ namespace NzbDrone.Core.Test
|
||||||
public static IRepository GetEmptyRepository(bool enableLogging)
|
public static IRepository GetEmptyRepository(bool enableLogging)
|
||||||
{
|
{
|
||||||
Console.WriteLine("Creating an empty SQLite database");
|
Console.WriteLine("Creating an empty SQLite database");
|
||||||
var provider = ProviderFactory.GetProvider("Data Source=" + Guid.NewGuid() + ".testdb;Version=3;New=True", "System.Data.SQLite");
|
var provider = ProviderFactory.GetProvider("Data Source=" + Guid.NewGuid() + ".db;Version=3;New=True", "System.Data.SQLite");
|
||||||
if (enableLogging)
|
if (enableLogging)
|
||||||
{
|
{
|
||||||
provider.Log = new Instrumentation.NlogWriter();
|
provider.Log = new NlogWriter();
|
||||||
provider.LogParams = true;
|
provider.LogParams = true;
|
||||||
}
|
}
|
||||||
return new SimpleRepository(provider, SimpleRepositoryOptions.RunMigrations);
|
return new SimpleRepository(provider, SimpleRepositoryOptions.RunMigrations);
|
||||||
|
@ -55,7 +56,7 @@ namespace NzbDrone.Core.Test
|
||||||
{
|
{
|
||||||
var mock = new Mock<IDiskProvider>();
|
var mock = new Mock<IDiskProvider>();
|
||||||
mock.Setup(c => c.GetDirectories(It.IsAny<String>())).Returns(StandardSeries);
|
mock.Setup(c => c.GetDirectories(It.IsAny<String>())).Returns(StandardSeries);
|
||||||
mock.Setup(c => c.Exists(It.Is<String>(d => StandardSeries.Contains(d)))).Returns(true);
|
mock.Setup(c => c.FolderExists(It.Is<String>(d => StandardSeries.Contains(d)))).Returns(true);
|
||||||
|
|
||||||
|
|
||||||
foreach (var series in StandardSeries)
|
foreach (var series in StandardSeries)
|
||||||
|
|
|
@ -74,6 +74,7 @@
|
||||||
<Compile Include="Ninject.Moq\ExtensionsForBindingSyntax.cs" />
|
<Compile Include="Ninject.Moq\ExtensionsForBindingSyntax.cs" />
|
||||||
<Compile Include="Ninject.Moq\MockingKernel.cs" />
|
<Compile Include="Ninject.Moq\MockingKernel.cs" />
|
||||||
<Compile Include="Ninject.Moq\MockProvider.cs" />
|
<Compile Include="Ninject.Moq\MockProvider.cs" />
|
||||||
|
<Compile Include="ParserTest.cs" />
|
||||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||||
<Compile Include="QualityProfileTest.cs" />
|
<Compile Include="QualityProfileTest.cs" />
|
||||||
<Compile Include="RepoTest.cs" />
|
<Compile Include="RepoTest.cs" />
|
||||||
|
|
|
@ -0,0 +1,60 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text;
|
||||||
|
using Gallio.Framework;
|
||||||
|
using MbUnit.Framework;
|
||||||
|
using MbUnit.Framework.ContractVerifiers;
|
||||||
|
using NzbDrone.Core.Repository.Quality;
|
||||||
|
|
||||||
|
namespace NzbDrone.Core.Test
|
||||||
|
{
|
||||||
|
[TestFixture]
|
||||||
|
// ReSharper disable InconsistentNaming
|
||||||
|
public class ParserTest
|
||||||
|
{
|
||||||
|
[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);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
[Row(@"c:\test\", @"c:\test")]
|
||||||
|
[Row(@"c:\\test\\", @"c:\test")]
|
||||||
|
[Row(@"C:\\Test\\", @"c:\test")]
|
||||||
|
[Row(@"C:\\Test\\Test\", @"c:\test\test")]
|
||||||
|
public void Normalize_Path(string dirty, string clean)
|
||||||
|
{
|
||||||
|
var result = Parser.NormalizePath(dirty);
|
||||||
|
Assert.AreEqual(clean, result);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -6,8 +6,13 @@ using FizzWare.NBuilder;
|
||||||
using Gallio.Framework;
|
using Gallio.Framework;
|
||||||
using MbUnit.Framework;
|
using MbUnit.Framework;
|
||||||
using MbUnit.Framework.ContractVerifiers;
|
using MbUnit.Framework.ContractVerifiers;
|
||||||
|
using Ninject;
|
||||||
|
using NLog;
|
||||||
|
using NzbDrone.Core.Instrumentation;
|
||||||
using NzbDrone.Core.Model;
|
using NzbDrone.Core.Model;
|
||||||
using NzbDrone.Core.Repository;
|
using NzbDrone.Core.Repository;
|
||||||
|
using LogLevel = NzbDrone.Core.Instrumentation.LogLevel;
|
||||||
|
using NLog.Config;
|
||||||
|
|
||||||
namespace NzbDrone.Core.Test
|
namespace NzbDrone.Core.Test
|
||||||
{
|
{
|
||||||
|
@ -62,5 +67,69 @@ namespace NzbDrone.Core.Test
|
||||||
Console.WriteLine(new Episode().ToString());
|
Console.WriteLine(new Episode().ToString());
|
||||||
Console.WriteLine(new EpisodeModel().ToString());
|
Console.WriteLine(new EpisodeModel().ToString());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void write_log()
|
||||||
|
{
|
||||||
|
//setup
|
||||||
|
var message = Guid.NewGuid().ToString();
|
||||||
|
|
||||||
|
var sonicRepo = MockLib.GetEmptyRepository();
|
||||||
|
|
||||||
|
var sonicTarget = new SubsonicTarget(sonicRepo);
|
||||||
|
LogManager.Configuration.AddTarget("DbLogger", sonicTarget);
|
||||||
|
LogManager.Configuration.LoggingRules.Add(new LoggingRule("*", NLog.LogLevel.Info, sonicTarget));
|
||||||
|
LogManager.Configuration.Reload();
|
||||||
|
|
||||||
|
Logger Logger = LogManager.GetCurrentClassLogger();
|
||||||
|
//Act
|
||||||
|
|
||||||
|
Logger.Info(message);
|
||||||
|
|
||||||
|
//Assert
|
||||||
|
Assert.IsNotEmpty(sonicRepo.All<Log>());
|
||||||
|
Assert.Count(1, sonicRepo.All<Log>());
|
||||||
|
|
||||||
|
var logItem = sonicRepo.All<Log>().First();
|
||||||
|
Assert.AreNotEqual(new DateTime(), logItem.Time);
|
||||||
|
Assert.AreEqual(message, logItem.Message);
|
||||||
|
Assert.AreEqual(Logger.Name, logItem.Logger);
|
||||||
|
Assert.AreEqual(Logger.Name, logItem.Logger);
|
||||||
|
Assert.AreEqual(LogLevel.Info, logItem.Level);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void write_log_exception()
|
||||||
|
{
|
||||||
|
//setup
|
||||||
|
var message = Guid.NewGuid().ToString();
|
||||||
|
|
||||||
|
var sonicRepo = MockLib.GetEmptyRepository();
|
||||||
|
|
||||||
|
var sonicTarget = new SubsonicTarget(sonicRepo);
|
||||||
|
LogManager.Configuration.AddTarget("DbLogger", sonicTarget);
|
||||||
|
LogManager.Configuration.LoggingRules.Add(new LoggingRule("*", NLog.LogLevel.Info, sonicTarget));
|
||||||
|
LogManager.Configuration.Reload();
|
||||||
|
|
||||||
|
Logger Logger = LogManager.GetCurrentClassLogger();
|
||||||
|
|
||||||
|
var ex = new InvalidOperationException("Fake Exception");
|
||||||
|
//Act
|
||||||
|
|
||||||
|
Logger.ErrorException(message, ex);
|
||||||
|
|
||||||
|
//Assert
|
||||||
|
Assert.IsNotEmpty(sonicRepo.All<Log>());
|
||||||
|
Assert.Count(1, sonicRepo.All<Log>());
|
||||||
|
|
||||||
|
var logItem = sonicRepo.All<Log>().First();
|
||||||
|
Assert.AreNotEqual(new DateTime(), logItem.Time);
|
||||||
|
Assert.AreEqual(message, logItem.Message);
|
||||||
|
Assert.AreEqual(Logger.Name, logItem.Logger);
|
||||||
|
Assert.AreEqual(LogLevel.Error, logItem.Level);
|
||||||
|
Assert.AreEqual(ex.GetType().ToString(), logItem.ExceptionType);
|
||||||
|
Assert.AreEqual(ex.ToString(), logItem.ExceptionString);
|
||||||
|
Assert.AreEqual(ex.Message, logItem.ExceptionMessage);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,6 +5,7 @@ using System.Web;
|
||||||
using Ninject;
|
using Ninject;
|
||||||
using NLog.Config;
|
using NLog.Config;
|
||||||
using NLog.Targets;
|
using NLog.Targets;
|
||||||
|
using NzbDrone.Core.Instrumentation;
|
||||||
using NzbDrone.Core.Providers;
|
using NzbDrone.Core.Providers;
|
||||||
using NzbDrone.Core.Providers.Fakes;
|
using NzbDrone.Core.Providers.Fakes;
|
||||||
using NzbDrone.Core.Repository;
|
using NzbDrone.Core.Repository;
|
||||||
|
@ -29,9 +30,14 @@ namespace NzbDrone.Core
|
||||||
_kernel = new StandardKernel();
|
_kernel = new StandardKernel();
|
||||||
|
|
||||||
string connectionString = String.Format("Data Source={0};Version=3;", Path.Combine(AppPath, "nzbdrone.db"));
|
string connectionString = String.Format("Data Source={0};Version=3;", Path.Combine(AppPath, "nzbdrone.db"));
|
||||||
var provider = ProviderFactory.GetProvider(connectionString, "System.Data.SQLite");
|
var dbProvider = ProviderFactory.GetProvider(connectionString, "System.Data.SQLite");
|
||||||
provider.Log = new Instrumentation.NlogWriter();
|
|
||||||
provider.LogParams = true;
|
string logConnectionString = String.Format("Data Source={0};Version=3;", Path.Combine(AppPath, "log.db"));
|
||||||
|
var logDbProvider = ProviderFactory.GetProvider(logConnectionString, "System.Data.SQLite");
|
||||||
|
var logRepository = new SimpleRepository(logDbProvider, SimpleRepositoryOptions.RunMigrations);
|
||||||
|
|
||||||
|
dbProvider.Log = new NlogWriter();
|
||||||
|
dbProvider.LogParams = true;
|
||||||
|
|
||||||
_kernel.Bind<ISeriesProvider>().To<SeriesProvider>().InSingletonScope();
|
_kernel.Bind<ISeriesProvider>().To<SeriesProvider>().InSingletonScope();
|
||||||
_kernel.Bind<ISeasonProvider>().To<SeasonProvider>();
|
_kernel.Bind<ISeasonProvider>().To<SeasonProvider>();
|
||||||
|
@ -41,7 +47,12 @@ namespace NzbDrone.Core
|
||||||
_kernel.Bind<IConfigProvider>().To<ConfigProvider>().InSingletonScope();
|
_kernel.Bind<IConfigProvider>().To<ConfigProvider>().InSingletonScope();
|
||||||
_kernel.Bind<ISyncProvider>().To<SyncProvider>().InSingletonScope();
|
_kernel.Bind<ISyncProvider>().To<SyncProvider>().InSingletonScope();
|
||||||
_kernel.Bind<INotificationProvider>().To<NotificationProvider>().InSingletonScope();
|
_kernel.Bind<INotificationProvider>().To<NotificationProvider>().InSingletonScope();
|
||||||
_kernel.Bind<IRepository>().ToMethod(c => new SimpleRepository(provider, SimpleRepositoryOptions.RunMigrations)).InSingletonScope();
|
_kernel.Bind<ILogProvider>().To<LogProvider>().InSingletonScope();
|
||||||
|
_kernel.Bind<IRepository>().ToMethod(c => new SimpleRepository(dbProvider, SimpleRepositoryOptions.RunMigrations)).InSingletonScope();
|
||||||
|
|
||||||
|
_kernel.Bind<IRepository>().ToConstant(logRepository).WhenInjectedInto<SubsonicTarget>().InSingletonScope();
|
||||||
|
_kernel.Bind<IRepository>().ToConstant(logRepository).WhenInjectedInto<LogProvider>().InSingletonScope();
|
||||||
|
|
||||||
|
|
||||||
ForceMigration(_kernel.Get<IRepository>());
|
ForceMigration(_kernel.Get<IRepository>());
|
||||||
}
|
}
|
||||||
|
@ -57,7 +68,6 @@ namespace NzbDrone.Core
|
||||||
}
|
}
|
||||||
return Directory.GetCurrentDirectory();
|
return Directory.GetCurrentDirectory();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static IKernel NinjectKernel
|
public static IKernel NinjectKernel
|
||||||
|
@ -75,6 +85,7 @@ namespace NzbDrone.Core
|
||||||
private static void ForceMigration(IRepository repository)
|
private static void ForceMigration(IRepository repository)
|
||||||
{
|
{
|
||||||
repository.GetPaged<Series>(0, 1);
|
repository.GetPaged<Series>(0, 1);
|
||||||
|
repository.GetPaged<EpisodeFile>(0, 1);
|
||||||
repository.GetPaged<Episode>(0, 1);
|
repository.GetPaged<Episode>(0, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,86 +0,0 @@
|
||||||
using System;
|
|
||||||
using System.Diagnostics;
|
|
||||||
using System.IO;
|
|
||||||
using System.Text;
|
|
||||||
using Exceptioneer.WindowsFormsClient;
|
|
||||||
using NLog;
|
|
||||||
using NLog.Config;
|
|
||||||
using NLog.Targets;
|
|
||||||
|
|
||||||
namespace NzbDrone.Core
|
|
||||||
{
|
|
||||||
public static class Instrumentation
|
|
||||||
{
|
|
||||||
public static void Setup()
|
|
||||||
{
|
|
||||||
if (Debugger.IsAttached)
|
|
||||||
{
|
|
||||||
LogManager.ThrowExceptions = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
LogManager.Configuration = new XmlLoggingConfiguration(Path.Combine(CentralDispatch.AppPath, "log.config"), false);
|
|
||||||
LogManager.ConfigurationReloaded += ((s, e) => BindExceptioneer());
|
|
||||||
BindExceptioneer();
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void BindExceptioneer()
|
|
||||||
{
|
|
||||||
var exTarget = new ExceptioneerTarget();
|
|
||||||
LogManager.Configuration.AddTarget("Exceptioneer", exTarget);
|
|
||||||
LogManager.Configuration.LoggingRules.Add(new LoggingRule("*", LogLevel.Error, exTarget));
|
|
||||||
LogManager.Configuration.Reload();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
public class NlogWriter : TextWriter
|
|
||||||
{
|
|
||||||
private static readonly Logger Logger = LogManager.GetLogger("DB");
|
|
||||||
|
|
||||||
|
|
||||||
public override void Write(char[] buffer, int index, int count)
|
|
||||||
{
|
|
||||||
Write(new string(buffer, index, count));
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void Write(string value)
|
|
||||||
{
|
|
||||||
DbAction(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void DbAction(string value)
|
|
||||||
{
|
|
||||||
Logger.Trace(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
public override Encoding Encoding
|
|
||||||
{
|
|
||||||
get { return Encoding.Default; }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public class ExceptioneerTarget : Target
|
|
||||||
{
|
|
||||||
protected override void Write(LogEventInfo logEvent)
|
|
||||||
{
|
|
||||||
if (logEvent.Exception == null)
|
|
||||||
throw new InvalidOperationException(@"Missing Exception Object.. Please Use Logger.FatalException() or Logger.ErrorException() rather
|
|
||||||
than Logger.Fatal() and Logger.Error()");
|
|
||||||
|
|
||||||
if (!Debugger.IsAttached)
|
|
||||||
{
|
|
||||||
new Client
|
|
||||||
{
|
|
||||||
ApiKey = "43BBF60A-EB2A-4C1C-B09E-422ADF637265",
|
|
||||||
ApplicationName = "NZBDrone",
|
|
||||||
CurrentException = logEvent.Exception
|
|
||||||
}.Submit();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,28 @@
|
||||||
|
using System;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using Exceptioneer.WindowsFormsClient;
|
||||||
|
using NLog;
|
||||||
|
using NLog.Targets;
|
||||||
|
|
||||||
|
namespace NzbDrone.Core.Instrumentation
|
||||||
|
{
|
||||||
|
public class ExceptioneerTarget : Target
|
||||||
|
{
|
||||||
|
protected override void Write(LogEventInfo logEvent)
|
||||||
|
{
|
||||||
|
if (logEvent.Exception == null)
|
||||||
|
throw new InvalidOperationException(@"Missing Exception Object.. Please Use Logger.FatalException() or Logger.ErrorException() rather
|
||||||
|
than Logger.Fatal() and Logger.Error()");
|
||||||
|
|
||||||
|
if (!Debugger.IsAttached)
|
||||||
|
{
|
||||||
|
new Client
|
||||||
|
{
|
||||||
|
ApiKey = "43BBF60A-EB2A-4C1C-B09E-422ADF637265",
|
||||||
|
ApplicationName = "NZBDrone",
|
||||||
|
CurrentException = logEvent.Exception
|
||||||
|
}.Submit();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,11 @@
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
|
namespace NzbDrone.Core.Instrumentation
|
||||||
|
{
|
||||||
|
public interface ILogProvider
|
||||||
|
{
|
||||||
|
IQueryable<Log> GetAllLogs();
|
||||||
|
void DeleteAll();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,30 @@
|
||||||
|
using System;
|
||||||
|
using SubSonic.SqlGeneration.Schema;
|
||||||
|
|
||||||
|
namespace NzbDrone.Core.Instrumentation
|
||||||
|
{
|
||||||
|
public class Log
|
||||||
|
{
|
||||||
|
public int LogId { get; set; }
|
||||||
|
public string Message { get; set; }
|
||||||
|
public DateTime Time { get; set; }
|
||||||
|
public string Logger { get; set; }
|
||||||
|
|
||||||
|
[SubSonicNullString]
|
||||||
|
public string Stack { get; set; }
|
||||||
|
[SubSonicNullString]
|
||||||
|
public string ExceptionMessage { get; set; }
|
||||||
|
[SubSonicNullString]
|
||||||
|
public string ExceptionString { get; set; }
|
||||||
|
[SubSonicNullString]
|
||||||
|
public string ExceptionType { get; set; }
|
||||||
|
|
||||||
|
public LogLevel Level { get; set; }
|
||||||
|
|
||||||
|
//This is needed for telerik grid binding
|
||||||
|
[SubSonicIgnore]
|
||||||
|
public string DisplayLevel{
|
||||||
|
get { return Level.ToString(); }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,40 @@
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.IO;
|
||||||
|
using NLog;
|
||||||
|
using NLog.Config;
|
||||||
|
using Ninject;
|
||||||
|
|
||||||
|
namespace NzbDrone.Core.Instrumentation
|
||||||
|
{
|
||||||
|
public static class LogConfiguration
|
||||||
|
{
|
||||||
|
public static void Setup()
|
||||||
|
{
|
||||||
|
if (Debugger.IsAttached)
|
||||||
|
{
|
||||||
|
LogManager.ThrowExceptions = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
LogManager.Configuration = new XmlLoggingConfiguration(Path.Combine(CentralDispatch.AppPath, "log.config"), false);
|
||||||
|
LogManager.ConfigurationReloaded += ((s, e) => BindCustomLoggers());
|
||||||
|
BindCustomLoggers();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void BindCustomLoggers()
|
||||||
|
{
|
||||||
|
var exTarget = new ExceptioneerTarget();
|
||||||
|
LogManager.Configuration.AddTarget("Exceptioneer", exTarget);
|
||||||
|
LogManager.Configuration.LoggingRules.Add(new LoggingRule("*", NLog.LogLevel.Error, exTarget));
|
||||||
|
|
||||||
|
var sonicTarget = CentralDispatch.NinjectKernel.Get<SubsonicTarget>();
|
||||||
|
LogManager.Configuration.AddTarget("DbLogger", sonicTarget);
|
||||||
|
LogManager.Configuration.LoggingRules.Add(new LoggingRule("*", NLog.LogLevel.Info, sonicTarget));
|
||||||
|
|
||||||
|
LogManager.Configuration.Reload();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,12 @@
|
||||||
|
namespace NzbDrone.Core.Instrumentation
|
||||||
|
{
|
||||||
|
public enum LogLevel
|
||||||
|
{
|
||||||
|
Trace,
|
||||||
|
Debug,
|
||||||
|
Info,
|
||||||
|
Warn,
|
||||||
|
Error,
|
||||||
|
Fatal
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,32 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using NLog;
|
||||||
|
using SubSonic.Repository;
|
||||||
|
|
||||||
|
namespace NzbDrone.Core.Instrumentation
|
||||||
|
{
|
||||||
|
|
||||||
|
public class LogProvider : ILogProvider
|
||||||
|
{
|
||||||
|
private static readonly Logger Logger = LogManager.GetCurrentClassLogger();
|
||||||
|
private readonly IRepository _repository;
|
||||||
|
|
||||||
|
public LogProvider(IRepository repository)
|
||||||
|
{
|
||||||
|
_repository = repository;
|
||||||
|
}
|
||||||
|
|
||||||
|
public IQueryable<Log> GetAllLogs()
|
||||||
|
{
|
||||||
|
return _repository.All<Log>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void DeleteAll()
|
||||||
|
{
|
||||||
|
_repository.DeleteMany(GetAllLogs());
|
||||||
|
Logger.Info("Cleared Log History");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,32 @@
|
||||||
|
using System.IO;
|
||||||
|
using System.Text;
|
||||||
|
using NLog;
|
||||||
|
|
||||||
|
namespace NzbDrone.Core.Instrumentation
|
||||||
|
{
|
||||||
|
public class NlogWriter : TextWriter
|
||||||
|
{
|
||||||
|
private static readonly Logger Logger = LogManager.GetLogger("DB");
|
||||||
|
|
||||||
|
|
||||||
|
public override void Write(char[] buffer, int index, int count)
|
||||||
|
{
|
||||||
|
Write(new string(buffer, index, count));
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Write(string value)
|
||||||
|
{
|
||||||
|
DbAction(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void DbAction(string value)
|
||||||
|
{
|
||||||
|
Logger.Trace(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override Encoding Encoding
|
||||||
|
{
|
||||||
|
get { return Encoding.Default; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,78 @@
|
||||||
|
using System;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using Exceptioneer.WindowsFormsClient;
|
||||||
|
using NLog;
|
||||||
|
using NLog.Targets;
|
||||||
|
using SubSonic.Repository;
|
||||||
|
using Ninject;
|
||||||
|
using NzbDrone.Core.Repository;
|
||||||
|
|
||||||
|
namespace NzbDrone.Core.Instrumentation
|
||||||
|
{
|
||||||
|
public class SubsonicTarget : Target
|
||||||
|
{
|
||||||
|
private readonly IRepository _repo;
|
||||||
|
|
||||||
|
public SubsonicTarget(IRepository repo)
|
||||||
|
{
|
||||||
|
_repo = repo;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void Write(LogEventInfo logEvent)
|
||||||
|
{
|
||||||
|
var log = new Log();
|
||||||
|
log.Time = logEvent.TimeStamp;
|
||||||
|
log.Message = logEvent.FormattedMessage;
|
||||||
|
|
||||||
|
if (log.Stack != null)
|
||||||
|
{
|
||||||
|
log.Stack = logEvent.StackTrace.ToString();
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Logger = logEvent.LoggerName;
|
||||||
|
|
||||||
|
if (logEvent.Exception != null)
|
||||||
|
{
|
||||||
|
log.ExceptionMessage = logEvent.Exception.Message;
|
||||||
|
log.ExceptionString = logEvent.Exception.ToString();
|
||||||
|
log.ExceptionType = logEvent.Exception.GetType().ToString();
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (logEvent.Level.Name.ToLower())
|
||||||
|
{
|
||||||
|
case "trace":
|
||||||
|
{
|
||||||
|
log.Level = LogLevel.Trace;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case "debug":
|
||||||
|
{
|
||||||
|
log.Level = LogLevel.Debug;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case "info":
|
||||||
|
{
|
||||||
|
log.Level = LogLevel.Info;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case "warn":
|
||||||
|
{
|
||||||
|
log.Level = LogLevel.Warn;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case "error":
|
||||||
|
{
|
||||||
|
log.Level = LogLevel.Error;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case "fatal":
|
||||||
|
{
|
||||||
|
log.Level = LogLevel.Fatal;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_repo.Add(log);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -150,13 +150,19 @@
|
||||||
<Reference Include="UPnP, Version=1.0.3932.37442, Culture=neutral, processorArchitecture=MSIL" />
|
<Reference Include="UPnP, Version=1.0.3932.37442, Culture=neutral, processorArchitecture=MSIL" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
<Compile Include="Instrumentation\ILogProvider.cs" />
|
||||||
|
<Compile Include="Instrumentation\LogLevel.cs" />
|
||||||
|
<Compile Include="Instrumentation\LogProvider.cs" />
|
||||||
|
<Compile Include="Instrumentation\SubsonicTarget.cs" />
|
||||||
|
<Compile Include="Instrumentation\ExceptioneerTarget.cs" />
|
||||||
|
<Compile Include="Instrumentation\NlogWriter.cs" />
|
||||||
<Compile Include="Model\EpisodeParseResult.cs" />
|
<Compile Include="Model\EpisodeParseResult.cs" />
|
||||||
<Compile Include="Model\EpisodeModel.cs" />
|
<Compile Include="Model\EpisodeModel.cs" />
|
||||||
<Compile Include="Repository\EpisodeFile.cs" />
|
<Compile Include="Repository\EpisodeFile.cs" />
|
||||||
<Compile Include="Model\Notification\BasicNotification.cs" />
|
<Compile Include="Model\Notification\BasicNotification.cs" />
|
||||||
<Compile Include="Model\Notification\ProgressNotificationStatus.cs" />
|
<Compile Include="Model\Notification\ProgressNotificationStatus.cs" />
|
||||||
<Compile Include="Model\Notification\BasicNotificationType.cs" />
|
<Compile Include="Model\Notification\BasicNotificationType.cs" />
|
||||||
<Compile Include="Instrumentation.cs" />
|
<Compile Include="Instrumentation\LogConfiguration.cs" />
|
||||||
<Compile Include="Parser.cs" />
|
<Compile Include="Parser.cs" />
|
||||||
<Compile Include="Providers\Fakes\FakeNotificationProvider.cs" />
|
<Compile Include="Providers\Fakes\FakeNotificationProvider.cs" />
|
||||||
<Compile Include="Providers\IMediaFileProvider.cs" />
|
<Compile Include="Providers\IMediaFileProvider.cs" />
|
||||||
|
@ -182,6 +188,7 @@
|
||||||
<Compile Include="Providers\SabProvider.cs" />
|
<Compile Include="Providers\SabProvider.cs" />
|
||||||
<Compile Include="Providers\SeasonProvider.cs" />
|
<Compile Include="Providers\SeasonProvider.cs" />
|
||||||
<Compile Include="Repository\Episode.cs" />
|
<Compile Include="Repository\Episode.cs" />
|
||||||
|
<Compile Include="Instrumentation\Log.cs" />
|
||||||
<Compile Include="Repository\Quality\AllowedQuality.cs" />
|
<Compile Include="Repository\Quality\AllowedQuality.cs" />
|
||||||
<Compile Include="Repository\Config.cs" />
|
<Compile Include="Repository\Config.cs" />
|
||||||
<Compile Include="Repository\Quality\QualityProfile.cs" />
|
<Compile Include="Repository\Quality\QualityProfile.cs" />
|
||||||
|
|
|
@ -131,5 +131,12 @@ namespace NzbDrone.Core
|
||||||
{
|
{
|
||||||
return NormalizeRegex.Replace(title, String.Empty).ToLower();
|
return NormalizeRegex.Replace(title, String.Empty).ToLower();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static string NormalizePath(string path)
|
||||||
|
{
|
||||||
|
if (String.IsNullOrEmpty(path))
|
||||||
|
throw new ArgumentException("Path can not be null or empty");
|
||||||
|
return new FileInfo(path).FullName.ToLower().Trim('/', '\\', ' ');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,11 +7,16 @@ namespace NzbDrone.Core.Providers
|
||||||
{
|
{
|
||||||
#region IDiskProvider Members
|
#region IDiskProvider Members
|
||||||
|
|
||||||
public bool Exists(string path)
|
public bool FolderExists(string path)
|
||||||
{
|
{
|
||||||
return Directory.Exists(path);
|
return Directory.Exists(path);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public bool FileExists(string path)
|
||||||
|
{
|
||||||
|
return File.Exists(path);
|
||||||
|
}
|
||||||
|
|
||||||
public string[] GetDirectories(string path)
|
public string[] GetDirectories(string path)
|
||||||
{
|
{
|
||||||
return Directory.GetDirectories(path);
|
return Directory.GetDirectories(path);
|
||||||
|
@ -22,18 +27,16 @@ namespace NzbDrone.Core.Providers
|
||||||
return Directory.GetFiles(path, pattern, searchOption);
|
return Directory.GetFiles(path, pattern, searchOption);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public long GetSize(string path)
|
||||||
|
{
|
||||||
|
return new FileInfo(path).Length;
|
||||||
|
}
|
||||||
|
|
||||||
public String CreateDirectory(string path)
|
public String CreateDirectory(string path)
|
||||||
{
|
{
|
||||||
return Directory.CreateDirectory(path).FullName;
|
return Directory.CreateDirectory(path).FullName;
|
||||||
}
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
public static string CleanPath(string path)
|
|
||||||
{
|
|
||||||
if (string.IsNullOrEmpty(path))
|
|
||||||
throw new ArgumentException("Path can not be null or empty");
|
|
||||||
return path.ToLower().Trim('/', '\\', ' ');
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -30,7 +30,12 @@ namespace NzbDrone.Core.Providers
|
||||||
|
|
||||||
public Episode GetEpisode(long id)
|
public Episode GetEpisode(long id)
|
||||||
{
|
{
|
||||||
return _sonicRepo.Single<Episode>(e => e.EpisodeId == id);
|
return _sonicRepo.Single<Episode>(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Episode GetEpisode(int seriesId, int seasonNumber, int episodeNumber)
|
||||||
|
{
|
||||||
|
return _sonicRepo.Single<Episode>(c => c.SeriesId == seriesId && c.SeasonNumber == seasonNumber && c.EpisodeNumber == episodeNumber);
|
||||||
}
|
}
|
||||||
|
|
||||||
public IList<Episode> GetEpisodeBySeries(long seriesId)
|
public IList<Episode> GetEpisodeBySeries(long seriesId)
|
||||||
|
|
|
@ -5,9 +5,11 @@ namespace NzbDrone.Core.Providers
|
||||||
{
|
{
|
||||||
public interface IDiskProvider
|
public interface IDiskProvider
|
||||||
{
|
{
|
||||||
bool Exists(string path);
|
bool FolderExists(string path);
|
||||||
string[] GetDirectories(string path);
|
string[] GetDirectories(string path);
|
||||||
String CreateDirectory(string path);
|
String CreateDirectory(string path);
|
||||||
string[] GetFiles(string path, string pattern, SearchOption searchOption);
|
string[] GetFiles(string path, string pattern, SearchOption searchOption);
|
||||||
|
bool FileExists(string path);
|
||||||
|
long GetSize(string path);
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -8,6 +8,7 @@ namespace NzbDrone.Core.Providers
|
||||||
public interface IEpisodeProvider
|
public interface IEpisodeProvider
|
||||||
{
|
{
|
||||||
Episode GetEpisode(long id);
|
Episode GetEpisode(long id);
|
||||||
|
Episode GetEpisode(int seriesId, int seasonNumber, int episodeNumber);
|
||||||
IList<Episode> GetEpisodeBySeries(long seriesId);
|
IList<Episode> GetEpisodeBySeries(long seriesId);
|
||||||
String GetSabTitle(Episode episode);
|
String GetSabTitle(Episode episode);
|
||||||
|
|
||||||
|
|
|
@ -9,5 +9,7 @@ namespace NzbDrone.Core.Providers
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="series">The series to be scanned</param>
|
/// <param name="series">The series to be scanned</param>
|
||||||
void Scan(Series series);
|
void Scan(Series series);
|
||||||
|
|
||||||
|
EpisodeFile ImportFile(Series series, string filePath);
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -5,22 +5,24 @@ using System.Linq;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Text.RegularExpressions;
|
using System.Text.RegularExpressions;
|
||||||
using NLog;
|
using NLog;
|
||||||
|
using NzbDrone.Core.Model;
|
||||||
using NzbDrone.Core.Repository;
|
using NzbDrone.Core.Repository;
|
||||||
|
using SubSonic.Repository;
|
||||||
|
|
||||||
namespace NzbDrone.Core.Providers
|
namespace NzbDrone.Core.Providers
|
||||||
{
|
{
|
||||||
public class MediaFileProvider : IMediaFileProvider
|
public class MediaFileProvider : IMediaFileProvider
|
||||||
{
|
{
|
||||||
|
private readonly IRepository _repository;
|
||||||
|
|
||||||
private readonly IDiskProvider _diskProvider;
|
private readonly IDiskProvider _diskProvider;
|
||||||
private readonly IEpisodeProvider _episodeProvider;
|
private readonly IEpisodeProvider _episodeProvider;
|
||||||
|
|
||||||
private static readonly Logger Logger = LogManager.GetCurrentClassLogger();
|
private static readonly Logger Logger = LogManager.GetCurrentClassLogger();
|
||||||
private static readonly string[] MediaExtentions = new[] { "*.mkv", "*.avi", "*.wmv" };
|
private static readonly string[] MediaExtentions = new[] { "*.mkv", "*.avi", "*.wmv" };
|
||||||
|
|
||||||
public MediaFileProvider(IDiskProvider diskProvider, IEpisodeProvider episodeProvider)
|
public MediaFileProvider(IRepository repository, IDiskProvider diskProvider, IEpisodeProvider episodeProvider)
|
||||||
{
|
{
|
||||||
|
_repository = repository;
|
||||||
_diskProvider = diskProvider;
|
_diskProvider = diskProvider;
|
||||||
_episodeProvider = episodeProvider;
|
_episodeProvider = episodeProvider;
|
||||||
}
|
}
|
||||||
|
@ -31,19 +33,82 @@ namespace NzbDrone.Core.Providers
|
||||||
/// <param name="series">The series to be scanned</param>
|
/// <param name="series">The series to be scanned</param>
|
||||||
public void Scan(Series series)
|
public void Scan(Series series)
|
||||||
{
|
{
|
||||||
|
var mediaFileList = GetMediaFileList(series.Path);
|
||||||
|
|
||||||
|
foreach (var filePath in mediaFileList)
|
||||||
|
{
|
||||||
|
ImportFile(series, filePath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public EpisodeFile ImportFile(Series series, string filePath)
|
||||||
|
{
|
||||||
|
Logger.Trace("Importing file to database [{0}]", filePath);
|
||||||
|
|
||||||
|
if (!_repository.Exists<EpisodeFile>(e => e.Path == Parser.NormalizePath(filePath)))
|
||||||
|
{
|
||||||
|
var episodesInFile = Parser.ParseEpisodeInfo(filePath);
|
||||||
|
|
||||||
|
foreach (var parsedEpisode in episodesInFile)
|
||||||
|
{
|
||||||
|
EpisodeParseResult closureEpisode = parsedEpisode;
|
||||||
|
var episode = _episodeProvider.GetEpisode(series.SeriesId, closureEpisode.SeasonNumber, closureEpisode.EpisodeNumber);
|
||||||
|
if (episode != null)
|
||||||
|
{
|
||||||
|
var epFile = new EpisodeFile();
|
||||||
|
epFile.DateAdded = DateTime.Now;
|
||||||
|
epFile.SeriesId = series.SeriesId;
|
||||||
|
epFile.EpisodeId = episode.EpisodeId;
|
||||||
|
epFile.Path = Parser.NormalizePath(filePath);
|
||||||
|
epFile.Size = _diskProvider.GetSize(filePath);
|
||||||
|
epFile.Quality = Parser.ParseQuality(filePath);
|
||||||
|
epFile.Proper = Parser.ParseProper(filePath);
|
||||||
|
_repository.Add(epFile);
|
||||||
|
Logger.Info("File '{0}' successfully attached to {1}", episode.EpisodeId);
|
||||||
|
|
||||||
|
return epFile;
|
||||||
|
}
|
||||||
|
|
||||||
|
Logger.Warn("Unable to find Series:{0} Season:{1} Episode:{2} in the database.", series.Title, closureEpisode.SeasonNumber, closureEpisode.EpisodeNumber);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Logger.Trace("[{0}] already exists in the database. skipping.", filePath);
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Removes files that no longer exist from the database
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="files">list of files to verify</param>
|
||||||
|
public void CleanUp(List<EpisodeFile> files)
|
||||||
|
{
|
||||||
|
foreach (var episodeFile in files)
|
||||||
|
{
|
||||||
|
if (!_diskProvider.FileExists(episodeFile.Path))
|
||||||
|
{
|
||||||
|
Logger.Trace("File {0} no longer exists on disk. removing from database.", episodeFile.Path);
|
||||||
|
_repository.Delete<EpisodeFile>(episodeFile);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<string> GetMediaFileList(string path)
|
||||||
|
{
|
||||||
|
Logger.Info("Scanning '{0}' for episodes", path);
|
||||||
|
|
||||||
var mediaFileList = new List<string>();
|
var mediaFileList = new List<string>();
|
||||||
Logger.Info("Scanning '{0}'", series.Path);
|
|
||||||
foreach (var ext in MediaExtentions)
|
foreach (var ext in MediaExtentions)
|
||||||
{
|
{
|
||||||
mediaFileList.AddRange(_diskProvider.GetFiles(series.Path, ext, SearchOption.AllDirectories));
|
mediaFileList.AddRange(_diskProvider.GetFiles(path, ext, SearchOption.AllDirectories));
|
||||||
}
|
}
|
||||||
|
|
||||||
Logger.Info("{0} media files were found", mediaFileList.Count);
|
Logger.Info("{0} media files were found in {1}", mediaFileList.Count, path);
|
||||||
|
return mediaFileList;
|
||||||
foreach (var file in mediaFileList)
|
|
||||||
{
|
|
||||||
var episode = Parser.ParseEpisodeInfo(file);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -61,7 +61,7 @@ namespace NzbDrone.Core.Providers
|
||||||
var results = new List<String>();
|
var results = new List<String>();
|
||||||
foreach (string seriesFolder in _diskProvider.GetDirectories(_config.SeriesRoot))
|
foreach (string seriesFolder in _diskProvider.GetDirectories(_config.SeriesRoot))
|
||||||
{
|
{
|
||||||
var cleanPath = DiskProvider.CleanPath(new DirectoryInfo(seriesFolder).FullName);
|
var cleanPath = Parser.NormalizePath(new DirectoryInfo(seriesFolder).FullName);
|
||||||
if (!_sonioRepo.Exists<Series>(s => s.Path == cleanPath))
|
if (!_sonioRepo.Exists<Series>(s => s.Path == cleanPath))
|
||||||
{
|
{
|
||||||
results.Add(cleanPath);
|
results.Add(cleanPath);
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
using SubSonic.SqlGeneration.Schema;
|
using SubSonic.SqlGeneration.Schema;
|
||||||
|
|
||||||
namespace NzbDrone.Core.Repository
|
namespace NzbDrone.Core.Repository
|
||||||
|
@ -16,14 +17,13 @@ namespace NzbDrone.Core.Repository
|
||||||
public string Overview { get; set; }
|
public string Overview { get; set; }
|
||||||
public string Language { get; set; }
|
public string Language { get; set; }
|
||||||
|
|
||||||
[SubSonicNullString]
|
|
||||||
public string Path { get; set; }
|
|
||||||
public long? Size { get; set; }
|
|
||||||
|
|
||||||
[SubSonicToOneRelation(ThisClassContainsJoinKey = true)]
|
[SubSonicToOneRelation(ThisClassContainsJoinKey = true)]
|
||||||
public virtual Season Season { get; set; }
|
public virtual Season Season { get; set; }
|
||||||
|
|
||||||
[SubSonicToOneRelation(ThisClassContainsJoinKey = true)]
|
[SubSonicToOneRelation(ThisClassContainsJoinKey = true)]
|
||||||
public virtual Series Series { get; private set; }
|
public virtual Series Series { get; private set; }
|
||||||
|
|
||||||
|
[SubSonicToManyRelation]
|
||||||
|
public virtual List<EpisodeFile> Files { get; private set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,18 +4,19 @@ using SubSonic.SqlGeneration.Schema;
|
||||||
|
|
||||||
namespace NzbDrone.Core.Repository
|
namespace NzbDrone.Core.Repository
|
||||||
{
|
{
|
||||||
class EpisodeFile
|
public class EpisodeFile
|
||||||
{
|
{
|
||||||
[SubSonicPrimaryKey]
|
[SubSonicPrimaryKey]
|
||||||
public virtual int FileId { get; set; }
|
public virtual int FileId { get; set; }
|
||||||
public int EpisodeId { get; set; }
|
public int EpisodeId { get; set; }
|
||||||
|
public int SeriesId { get; set; }
|
||||||
public string Path { get; set; }
|
public string Path { get; set; }
|
||||||
public QualityTypes Quality { get; set; }
|
public QualityTypes Quality { get; set; }
|
||||||
public bool Proper { get; set; }
|
public bool Proper { get; set; }
|
||||||
public long Size { get; set; }
|
public long Size { get; set; }
|
||||||
public DateTime DateAdded { get; set; }
|
public DateTime DateAdded { get; set; }
|
||||||
|
|
||||||
[SubSonicToOneRelation]
|
[SubSonicToOneRelation(ThisClassContainsJoinKey = true)]
|
||||||
public virtual Episode Episode { get; set; }
|
public virtual Episode Episode { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -35,6 +35,7 @@ namespace NzbDrone.Core.Repository
|
||||||
[SubSonicToManyRelation]
|
[SubSonicToManyRelation]
|
||||||
public virtual List<Episode> Episodes { get; private set; }
|
public virtual List<Episode> Episodes { get; private set; }
|
||||||
|
|
||||||
|
[SubSonicToManyRelation]
|
||||||
|
public virtual List<EpisodeFile> Files { get; private set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -0,0 +1,43 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Web;
|
||||||
|
using System.Web.Mvc;
|
||||||
|
using NzbDrone.Core.Instrumentation;
|
||||||
|
using SubSonic.Repository;
|
||||||
|
using Telerik.Web.Mvc;
|
||||||
|
|
||||||
|
namespace NzbDrone.Web.Controllers
|
||||||
|
{
|
||||||
|
public class LogController : Controller
|
||||||
|
{
|
||||||
|
private readonly ILogProvider _logProvider;
|
||||||
|
|
||||||
|
public LogController(ILogProvider logProvider)
|
||||||
|
{
|
||||||
|
_logProvider = logProvider;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ActionResult Index()
|
||||||
|
{
|
||||||
|
return View();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public ActionResult Clear()
|
||||||
|
{
|
||||||
|
_logProvider.DeleteAll();
|
||||||
|
return RedirectToAction("Index");
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
[GridAction]
|
||||||
|
public ActionResult _AjaxBinding()
|
||||||
|
{
|
||||||
|
return View(new GridModel(_logProvider.GetAllLogs()));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -34,7 +34,7 @@ namespace NzbDrone.Web.Controllers
|
||||||
_configProvider.SeriesRoot = model.TvFolder;
|
_configProvider.SeriesRoot = model.TvFolder;
|
||||||
//return RedirectToAction("index");
|
//return RedirectToAction("index");
|
||||||
}
|
}
|
||||||
return View(model);
|
return RedirectToAction("index", "series");
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,6 +8,8 @@ using Ninject;
|
||||||
using Ninject.Web.Mvc;
|
using Ninject.Web.Mvc;
|
||||||
using NLog;
|
using NLog;
|
||||||
using NzbDrone.Core;
|
using NzbDrone.Core;
|
||||||
|
using NzbDrone.Core.Instrumentation;
|
||||||
|
using SubSonic.Repository;
|
||||||
|
|
||||||
namespace NzbDrone.Web
|
namespace NzbDrone.Web
|
||||||
{
|
{
|
||||||
|
@ -30,7 +32,8 @@ namespace NzbDrone.Web
|
||||||
|
|
||||||
protected override void OnApplicationStarted()
|
protected override void OnApplicationStarted()
|
||||||
{
|
{
|
||||||
Instrumentation.Setup();
|
LogConfiguration.Setup();
|
||||||
|
Logger.Info("NZBDrone Starting up.");
|
||||||
CentralDispatch.DedicateToHost();
|
CentralDispatch.DedicateToHost();
|
||||||
AreaRegistration.RegisterAllAreas();
|
AreaRegistration.RegisterAllAreas();
|
||||||
RegisterRoutes(RouteTable.Routes);
|
RegisterRoutes(RouteTable.Routes);
|
||||||
|
@ -39,7 +42,10 @@ namespace NzbDrone.Web
|
||||||
|
|
||||||
protected override IKernel CreateKernel()
|
protected override IKernel CreateKernel()
|
||||||
{
|
{
|
||||||
return CentralDispatch.NinjectKernel;
|
var kernel = CentralDispatch.NinjectKernel;
|
||||||
|
// kernel.Bind<IRepository>().ToConstant(kernel.Get<IRepository>("LogDb"));
|
||||||
|
|
||||||
|
return kernel;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ReSharper disable InconsistentNaming
|
// ReSharper disable InconsistentNaming
|
||||||
|
|
|
@ -38,6 +38,9 @@
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Reference Include="Ninject, Version=2.1.0.76, Culture=neutral, PublicKeyToken=c7192dc5380945e7, processorArchitecture=MSIL" />
|
<Reference Include="Ninject, Version=2.1.0.76, Culture=neutral, PublicKeyToken=c7192dc5380945e7, processorArchitecture=MSIL" />
|
||||||
<Reference Include="NLog, Version=2.0.0.0, Culture=neutral, PublicKeyToken=5120e14c03d0593c, processorArchitecture=MSIL" />
|
<Reference Include="NLog, Version=2.0.0.0, Culture=neutral, PublicKeyToken=5120e14c03d0593c, processorArchitecture=MSIL" />
|
||||||
|
<Reference Include="SubSonic.Core, Version=3.0.0.3, Culture=neutral, processorArchitecture=MSIL">
|
||||||
|
<HintPath>D:\My Dropbox\Git\NzbDrone\NzbDrone.Core\Libraries\SubSonic.Core.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
<Reference Include="System" />
|
<Reference Include="System" />
|
||||||
<Reference Include="System.Data" />
|
<Reference Include="System.Data" />
|
||||||
<Reference Include="System.Data.DataSetExtensions" />
|
<Reference Include="System.Data.DataSetExtensions" />
|
||||||
|
@ -74,6 +77,7 @@
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Compile Include="Controllers\AccountController.cs" />
|
<Compile Include="Controllers\AccountController.cs" />
|
||||||
<Compile Include="Controllers\HomeController.cs" />
|
<Compile Include="Controllers\HomeController.cs" />
|
||||||
|
<Compile Include="Controllers\LogController.cs" />
|
||||||
<Compile Include="Controllers\NotificationController.cs" />
|
<Compile Include="Controllers\NotificationController.cs" />
|
||||||
<Compile Include="Controllers\SeriesController.cs" />
|
<Compile Include="Controllers\SeriesController.cs" />
|
||||||
<Compile Include="Controllers\SettingsController.cs" />
|
<Compile Include="Controllers\SettingsController.cs" />
|
||||||
|
@ -209,7 +213,10 @@
|
||||||
<Content Include="Scripts\jquery-ui-1.8.5.custom.min.js" />
|
<Content Include="Scripts\jquery-ui-1.8.5.custom.min.js" />
|
||||||
<Content Include="Scripts\jquery.jgrowl.js" />
|
<Content Include="Scripts\jquery.jgrowl.js" />
|
||||||
<Content Include="Scripts\Notification.js" />
|
<Content Include="Scripts\Notification.js" />
|
||||||
|
<Content Include="Views\Log\LogDetail.ascx" />
|
||||||
|
<Content Include="Views\Log\Index.aspx" />
|
||||||
<Content Include="Views\Series\Details.aspx" />
|
<Content Include="Views\Series\Details.aspx" />
|
||||||
|
<Content Include="Views\Series\EpisodeDetail.ascx" />
|
||||||
<Content Include="Views\Series\index.aspx" />
|
<Content Include="Views\Series\index.aspx" />
|
||||||
<Content Include="Views\Series\Unmapped.aspx" />
|
<Content Include="Views\Series\Unmapped.aspx" />
|
||||||
<Content Include="Views\Settings\Index.aspx" />
|
<Content Include="Views\Settings\Index.aspx" />
|
||||||
|
@ -244,6 +251,7 @@
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Folder Include="App_Data\" />
|
<Folder Include="App_Data\" />
|
||||||
|
<Folder Include="Views\Series\DisplayTemplates\" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\NzbDrone.Core\NzbDrone.Core.csproj">
|
<ProjectReference Include="..\NzbDrone.Core\NzbDrone.Core.csproj">
|
||||||
|
@ -269,7 +277,15 @@
|
||||||
<VisualStudio>
|
<VisualStudio>
|
||||||
<FlavorProperties GUID="{349c5851-65df-11da-9384-00065b846f21}">
|
<FlavorProperties GUID="{349c5851-65df-11da-9384-00065b846f21}">
|
||||||
<WebProjectProperties>
|
<WebProjectProperties>
|
||||||
<SaveServerSettingsInUserFile>True</SaveServerSettingsInUserFile>
|
<UseIIS>False</UseIIS>
|
||||||
|
<AutoAssignPort>True</AutoAssignPort>
|
||||||
|
<DevelopmentServerPort>21704</DevelopmentServerPort>
|
||||||
|
<DevelopmentServerVPath>/</DevelopmentServerVPath>
|
||||||
|
<IISUrl>http://localhost/NzbDrone</IISUrl>
|
||||||
|
<NTLMAuthentication>False</NTLMAuthentication>
|
||||||
|
<UseCustomServer>True</UseCustomServer>
|
||||||
|
<CustomServerUrl>http://localhost:8989</CustomServerUrl>
|
||||||
|
<SaveServerSettingsInUserFile>False</SaveServerSettingsInUserFile>
|
||||||
</WebProjectProperties>
|
</WebProjectProperties>
|
||||||
</FlavorProperties>
|
</FlavorProperties>
|
||||||
</VisualStudio>
|
</VisualStudio>
|
||||||
|
|
|
@ -5,7 +5,7 @@ $(function () {
|
||||||
refreshNotifications();
|
refreshNotifications();
|
||||||
|
|
||||||
var timer = window.setInterval(function () {
|
var timer = window.setInterval(function () {
|
||||||
speed = 1800;
|
speed = 1000;
|
||||||
refreshNotifications();
|
refreshNotifications();
|
||||||
}, 2000);
|
}, 2000);
|
||||||
|
|
||||||
|
|
|
@ -1,12 +1,19 @@
|
||||||
<%@ Page Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage" %>
|
<%@ Page Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage" %>
|
||||||
|
|
||||||
|
<%@ Import Namespace="Telerik.Web.Mvc.UI" %>
|
||||||
<asp:Content ID="Content1" ContentPlaceHolderID="TitleContent" runat="server">
|
<asp:Content ID="Content1" ContentPlaceHolderID="TitleContent" runat="server">
|
||||||
Home Page
|
Logs
|
||||||
|
</asp:Content>
|
||||||
|
<asp:Content ID="Menu" ContentPlaceHolderID="ActionMenu" runat="server">
|
||||||
|
<%
|
||||||
|
Html.Telerik().Menu().Name("logMenu").Items(items => items.Add().Text("Clear Logs").Action("Clear", "Log")).Render();
|
||||||
|
%>
|
||||||
</asp:Content>
|
</asp:Content>
|
||||||
|
|
||||||
<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">
|
<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">
|
||||||
<h2><%: ViewData["Message"] %></h2>
|
<h2>
|
||||||
|
<%: ViewData["Message"] %></h2>
|
||||||
<p>
|
<p>
|
||||||
To learn more about ASP.NET MVC visit <a href="http://asp.net/mvc" title="ASP.NET MVC Website">http://asp.net/mvc</a>.
|
To learn more about ASP.NET MVC visit <a href="http://asp.net/mvc" title="ASP.NET MVC Website">
|
||||||
|
http://asp.net/mvc</a>.
|
||||||
</p>
|
</p>
|
||||||
</asp:Content>
|
</asp:Content>
|
||||||
|
|
|
@ -0,0 +1,31 @@
|
||||||
|
<%@ Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage<IEnumerable<NzbDrone.Core.Instrumentation.Log>>" %>
|
||||||
|
|
||||||
|
<%@ Import Namespace="Telerik.Web.Mvc.UI" %>
|
||||||
|
<asp:Content ID="Content1" ContentPlaceHolderID="TitleContent" runat="server">
|
||||||
|
Logs
|
||||||
|
</asp:Content>
|
||||||
|
<asp:Content ID="Menu" ContentPlaceHolderID="ActionMenu" runat="server">
|
||||||
|
<%
|
||||||
|
Html.Telerik().Menu().Name("logMenu").Items(items => items.Add().Text("Clear Logs").Action("Clear", "Log")).Render();
|
||||||
|
%>
|
||||||
|
</asp:Content>
|
||||||
|
<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">
|
||||||
|
<%Html.Telerik().Grid(Model).Name("logs")
|
||||||
|
.Columns(columns =>
|
||||||
|
{
|
||||||
|
columns.Bound(c => c.Time).Title("Time").Width(190);
|
||||||
|
//columns.Bound(c => c.Time).Title("Time").Template(c => c.Time.ToShortTimeString()).Groupable(false);
|
||||||
|
columns.Bound(c => c.DisplayLevel).Title("Level").Width(0);
|
||||||
|
columns.Bound(c => c.Message);
|
||||||
|
})
|
||||||
|
.DataBinding(dataBinding => dataBinding.Ajax().Select("_AjaxBinding", "Log"))
|
||||||
|
//.DetailView(detailView => detailView.Template(e => Html.RenderPartial("LogDetail", e)))
|
||||||
|
.Sortable(rows => rows.OrderBy(epSort => epSort.Add(c => c.Time).Descending()).Enabled(true))
|
||||||
|
.Pageable(c => c.PageSize(50).Position(GridPagerPosition.Both).Style(GridPagerStyles.PageInput | GridPagerStyles.NextPreviousAndNumeric))
|
||||||
|
//.Groupable()
|
||||||
|
.Filterable()
|
||||||
|
//.Groupable(grouping => grouping.Groups(groups => groups.Add(c => c.Time.Date)).Enabled(true))
|
||||||
|
.Render();
|
||||||
|
%>
|
||||||
|
</asp:Content>
|
||||||
|
|
|
@ -0,0 +1,15 @@
|
||||||
|
<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<NzbDrone.Core.Instrumentation.Log>" %>
|
||||||
|
<ul>
|
||||||
|
<li>
|
||||||
|
<%: Model.Logger %>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<%: Model.ExceptionType%>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<%: Model.ExceptionMessage%>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<%: Model.ExceptionString%>
|
||||||
|
</li>
|
||||||
|
</ul>
|
|
@ -1,6 +1,7 @@
|
||||||
<%@ Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage<NzbDrone.Core.Repository.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" %>
|
<%@ Import Namespace="Telerik.Web.Mvc.UI" %>
|
||||||
|
<%@ Import Namespace="NzbDrone.Core.Repository" %>
|
||||||
<asp:Content ID="Content1" ContentPlaceHolderID="TitleContent" runat="server">
|
<asp:Content ID="Content1" ContentPlaceHolderID="TitleContent" runat="server">
|
||||||
<%: Model.Title %>
|
<%: Model.Title %>
|
||||||
</asp:Content>
|
</asp:Content>
|
||||||
|
@ -31,22 +32,56 @@
|
||||||
<div class="display-field">
|
<div class="display-field">
|
||||||
<%: Model.Path %></div>
|
<%: Model.Path %></div>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
<%= Html.Telerik().Grid(Model.Episodes)
|
<%
|
||||||
.Name("Episodes")
|
//Normal Seasons
|
||||||
|
foreach (var season in Model.Seasons.Where(s => s.SeasonNumber > 0))
|
||||||
|
{
|
||||||
|
%>
|
||||||
|
<br />
|
||||||
|
<h3>
|
||||||
|
Season
|
||||||
|
<%: season.SeasonNumber %></h3>
|
||||||
|
<%
|
||||||
|
Html.Telerik().Grid(season.Episodes).Name("seasons_" + season.SeasonNumber)
|
||||||
.Columns(columns =>
|
.Columns(columns =>
|
||||||
{
|
{
|
||||||
columns.Bound(c => c.EpisodeNumber).Width(10);
|
columns.Bound(c => c.SeasonNumber).Width(0).Title("Seasons");
|
||||||
|
columns.Bound(c => c.EpisodeNumber).Width(0).Title("Episode");
|
||||||
columns.Bound(c => c.Title);
|
columns.Bound(c => c.Title);
|
||||||
columns.Bound(c => c.AirDate).Format("{0:d}").Width(150);
|
columns.Bound(c => c.AirDate).Format("{0:d}").Width(0);
|
||||||
})
|
})
|
||||||
.Groupable(grouping => grouping.Groups(groups => groups.Add(c => c.SeasonNumber)))
|
.DetailView(detailView => detailView.Template(e => Html.RenderPartial("EpisodeDetail", e)))
|
||||||
.Sortable(rows=>rows
|
.Sortable(rows => rows.OrderBy(epSort => epSort.Add(c => c.EpisodeNumber)).Enabled(false))
|
||||||
.OrderBy(epSort => epSort.Add(c => c.EpisodeNumber)))
|
.Footer(false)
|
||||||
|
.Render();
|
||||||
|
}
|
||||||
|
|
||||||
|
//Specials
|
||||||
|
var specialSeasons = Model.Seasons.Where(s => s.SeasonNumber == 0).FirstOrDefault();
|
||||||
|
|
||||||
|
if (specialSeasons != null)
|
||||||
|
{
|
||||||
|
%>
|
||||||
|
<br />
|
||||||
|
<h3>
|
||||||
|
Specials</h3>
|
||||||
|
<%
|
||||||
|
|
||||||
|
Html.Telerik().Grid(specialSeasons.Episodes).Name("seasons_specials")
|
||||||
|
.Columns(columns =>
|
||||||
|
{
|
||||||
|
columns.Bound(c => c.EpisodeNumber).Width(0).Title("Episode");
|
||||||
|
columns.Bound(c => c.Title);
|
||||||
|
columns.Bound(c => c.AirDate).Format("{0:d}").Width(0);
|
||||||
|
})
|
||||||
|
.DetailView(detailView => detailView.Template(e => Html.RenderPartial("EpisodeDetail", e)))
|
||||||
|
.Sortable(rows => rows.OrderBy(epSort => epSort.Add(c => c.EpisodeNumber)).Enabled(false))
|
||||||
|
.Footer(false)
|
||||||
|
.Render();
|
||||||
|
}
|
||||||
%>
|
%>
|
||||||
<p>
|
<p>
|
||||||
<%-- <%: Html.ActionLink("Edit", "Edit", new { /* id=Model.PrimaryKey */ }) %> |--%>
|
<%-- <%: Html.ActionLink("Edit", "Edit", new { /* id=Model.PrimaryKey */ }) %> |--%>
|
||||||
<%: Html.ActionLink("Back to List", "Index") %>
|
<%: Html.ActionLink("Back to Series", "Index") %>
|
||||||
<%: Html.ActionLink("Load Episodes", "LoadEpisodes", new{seriesId= Model.SeriesId}) %>
|
|
||||||
</p>
|
</p>
|
||||||
</asp:Content>
|
</asp:Content>
|
||||||
|
|
|
@ -0,0 +1,14 @@
|
||||||
|
<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<NzbDrone.Core.Repository.Episode>" %>
|
||||||
|
<%@ Import Namespace="Telerik.Web.Mvc.UI" %>
|
||||||
|
<%: Model.Overview %>
|
||||||
|
<%:
|
||||||
|
Html.Telerik().Grid(Model.Files)
|
||||||
|
.Name("files_" + Model.EpisodeId)
|
||||||
|
.Columns(columns =>
|
||||||
|
{
|
||||||
|
columns.Bound(c => c.Path);
|
||||||
|
columns.Bound(c => c.Quality);
|
||||||
|
columns.Bound(c => c.DateAdded);
|
||||||
|
})
|
||||||
|
.Footer(false)
|
||||||
|
%>
|
|
@ -5,9 +5,7 @@
|
||||||
<asp:Content ID="Content1" ContentPlaceHolderID="TitleContent" runat="server">
|
<asp:Content ID="Content1" ContentPlaceHolderID="TitleContent" runat="server">
|
||||||
Series
|
Series
|
||||||
</asp:Content>
|
</asp:Content>
|
||||||
<asp:Content ID="Menue" ContentPlaceHolderID="ActionMenue" runat="server">
|
<asp:Content ID="Menu" ContentPlaceHolderID="ActionMenu" runat="server">
|
||||||
<div id="Mediabox">
|
|
||||||
</div>
|
|
||||||
<%
|
<%
|
||||||
Html.Telerik().Menu().Name("telerikGrid").Items(items => { items.Add().Text("View Unmapped Folders").Action("Unmapped", "Series"); })
|
Html.Telerik().Menu().Name("telerikGrid").Items(items => { items.Add().Text("View Unmapped Folders").Action("Unmapped", "Series"); })
|
||||||
.Items(items => items.Add().Text("Sync With Disk").Action("Sync", "Series"))
|
.Items(items => items.Add().Text("Sync With Disk").Action("Sync", "Series"))
|
||||||
|
|
|
@ -32,6 +32,10 @@ Released : 20100727
|
||||||
%>
|
%>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
<a href="http://github.com/kayone/NzbDrone">
|
||||||
|
<img style="position: absolute; top: 0; left: 0; border: 0;" src="http://s3.amazonaws.com/github/ribbons/forkme_left_darkblue_121621.png"
|
||||||
|
alt="Fork me on GitHub" />
|
||||||
|
</a>
|
||||||
<div id="header">
|
<div id="header">
|
||||||
<div id="msgBox">
|
<div id="msgBox">
|
||||||
<span id="msgText">Scanning Series Folder...</span>
|
<span id="msgText">Scanning Series Folder...</span>
|
||||||
|
@ -40,6 +44,7 @@ Released : 20100727
|
||||||
<ul>
|
<ul>
|
||||||
<%=Html.CurrentActionLink("Series", "Index", "Series") %>
|
<%=Html.CurrentActionLink("Series", "Index", "Series") %>
|
||||||
<%=Html.CurrentActionLink("Settings", "Index", "Settings") %>
|
<%=Html.CurrentActionLink("Settings", "Index", "Settings") %>
|
||||||
|
<%=Html.CurrentActionLink("Logs", "Index", "Log") %>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
<!-- end #menu -->
|
<!-- end #menu -->
|
||||||
|
@ -53,7 +58,7 @@ Released : 20100727
|
||||||
<hr />
|
<hr />
|
||||||
<!-- end #logo -->
|
<!-- end #logo -->
|
||||||
<div id="page">
|
<div id="page">
|
||||||
<asp:ContentPlaceHolder ID="ActionMenue" runat="server" />
|
<asp:ContentPlaceHolder ID="ActionMenu" runat="server" />
|
||||||
<div id="content">
|
<div id="content">
|
||||||
<div class="post">
|
<div class="post">
|
||||||
<div class="entry">
|
<div class="entry">
|
||||||
|
|
|
@ -1,16 +1,17 @@
|
||||||
|
|
||||||
Microsoft Visual Studio Solution File, Format Version 11.00
|
Microsoft Visual Studio Solution File, Format Version 11.00
|
||||||
# Visual Studio 2010
|
# Visual Studio 2010
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NzbDrone.Web.Tests", "NzbDrone.Web.Test\NzbDrone.Web.Tests.csproj", "{99CDD5DC-698F-4624-B431-2D6381CE3A15}"
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NzbDrone", "NzbDrone\NzbDrone.csproj", "{D12F7F2F-8A3C-415F-88FA-6DD061A84869}"
|
||||||
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NzbDrone.Core", "NzbDrone.Core\NzbDrone.Core.csproj", "{FF5EE3B6-913B-47CE-9CEB-11C51B4E1205}"
|
||||||
EndProject
|
EndProject
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NzbDrone.Web", "NzbDrone.Web\NzbDrone.Web.csproj", "{43BD3BBD-1531-4D8F-9C08-E1CD544AB2CD}"
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NzbDrone.Web", "NzbDrone.Web\NzbDrone.Web.csproj", "{43BD3BBD-1531-4D8F-9C08-E1CD544AB2CD}"
|
||||||
EndProject
|
EndProject
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NzbDrone.Core.Test", "NzbDrone.Core.Test\NzbDrone.Core.Test.csproj", "{193ADD3B-792B-4173-8E4C-5A3F8F0237F0}"
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NzbDrone.Core.Test", "NzbDrone.Core.Test\NzbDrone.Core.Test.csproj", "{193ADD3B-792B-4173-8E4C-5A3F8F0237F0}"
|
||||||
EndProject
|
EndProject
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NzbDrone.Core", "NzbDrone.Core\NzbDrone.Core.csproj", "{FF5EE3B6-913B-47CE-9CEB-11C51B4E1205}"
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NzbDrone.Web.Tests", "NzbDrone.Web.Test\NzbDrone.Web.Tests.csproj", "{99CDD5DC-698F-4624-B431-2D6381CE3A15}"
|
||||||
EndProject
|
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NzbDrone", "NzbDrone\NzbDrone.csproj", "{D12F7F2F-8A3C-415F-88FA-6DD061A84869}"
|
|
||||||
EndProject
|
EndProject
|
||||||
|
|
||||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tests", "Tests", "{57A04B72-8088-4F75-A582-1158CF8291F7}"
|
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tests", "Tests", "{57A04B72-8088-4F75-A582-1158CF8291F7}"
|
||||||
EndProject
|
EndProject
|
||||||
Global
|
Global
|
||||||
|
|
|
@ -35,8 +35,8 @@ namespace NzbDrone
|
||||||
dte2 = (DTE2)System.Runtime.InteropServices.Marshal.
|
dte2 = (DTE2)System.Runtime.InteropServices.Marshal.
|
||||||
GetActiveObject("VisualStudio.DTE.10.0");
|
GetActiveObject("VisualStudio.DTE.10.0");
|
||||||
|
|
||||||
var pa = new ProcessAttacher(dte2, "iisexpress", 20);
|
var pa = new ProcessAttacher(dte2, "iisexpress", 10);
|
||||||
pa.OptimisticAttachManaged();
|
pa.PessimisticAttachManaged();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -87,10 +87,9 @@ namespace NzbDrone
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
if (ex.Message.Contains("Invalid index."))
|
|
||||||
{
|
|
||||||
return AttachResult.NotRunning;
|
return AttachResult.NotRunning;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
proc.Attach2(eng);
|
proc.Attach2(eng);
|
||||||
|
|
|
@ -40,9 +40,17 @@ namespace NzbDrone
|
||||||
#if DEBUG
|
#if DEBUG
|
||||||
//Manually Attach debugger to IISExpress
|
//Manually Attach debugger to IISExpress
|
||||||
if (Debugger.IsAttached)
|
if (Debugger.IsAttached)
|
||||||
|
{
|
||||||
|
try
|
||||||
{
|
{
|
||||||
ProcessAttacher.Attach();
|
ProcessAttacher.Attach();
|
||||||
}
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
Logger.Warn("Unable to attach to debugger", e);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue