Merge remote-tracking branch 'origin/pneumatic'

pull/4/head
Mark McDowall 12 years ago
commit 0b8ca7c3b0

@ -0,0 +1 @@
plugin://plugin.program.pneumatic/?mode=strm&type=add_file&nzb=C:\Test\Pneumatic\30 Rock - 6x18 - Murphy Brown Lied to Us [SDTV].nzb&nzbname=30 Rock - 6x18 - Murphy Brown Lied to Us [SDTV]

@ -0,0 +1 @@
plugin://plugin.program.pneumatic/?mode=strm&type=add_file&nzb=C:\Test\Pneumatic\30 Rock - 6x19 - Live from Studio 6H (East Coast) [SDTV] [Proper].nzb&nzbname=30 Rock - 6x19 - Live from Studio 6H (East Coast) [SDTV] [Proper]

@ -0,0 +1 @@
plugin://plugin.program.pneumatic/?mode=strm&type=add_file&nzb=C:\Test\Pneumatic\30 Rock - 6x21 - The Return of Avery Jessup [SDTV].nzb&nzbname=30 Rock - 6x21 - The Return of Avery Jessup [SDTV]

@ -42,6 +42,14 @@ namespace NzbDrone.Common
.Max(c => c.LastWriteTimeUtc); .Max(c => c.LastWriteTimeUtc);
} }
public virtual DateTime GetLastFileWrite(string path)
{
if (!FileExists(path))
throw new FileNotFoundException("File doesn't exist: " + path);
return new FileInfo(path).LastWriteTimeUtc;
}
public virtual bool FolderExists(string path) public virtual bool FolderExists(string path)
{ {
return Directory.Exists(path); return Directory.Exists(path);
@ -209,5 +217,13 @@ namespace NzbDrone.Common
{ {
return String.Equals(firstPath.NormalizePath(), secondPath.NormalizePath(), StringComparison.InvariantCultureIgnoreCase); return String.Equals(firstPath.NormalizePath(), secondPath.NormalizePath(), StringComparison.InvariantCultureIgnoreCase);
} }
public virtual long GetFileSize(string path)
{
if (!FileExists(path))
throw new FileNotFoundException("File doesn't exist: " + path);
return new FileInfo(path).Length;
}
} }
} }

@ -114,8 +114,15 @@
<Compile Include="ProviderTests\ConfigProviderTests\ConfigCachingFixture.cs" /> <Compile Include="ProviderTests\ConfigProviderTests\ConfigCachingFixture.cs" />
<Compile Include="ProviderTests\BannerProviderTest.cs" /> <Compile Include="ProviderTests\BannerProviderTest.cs" />
<Compile Include="ProviderTests\DecisionEngineTests\AllowedReleaseGroupSpecificationFixture.cs" /> <Compile Include="ProviderTests\DecisionEngineTests\AllowedReleaseGroupSpecificationFixture.cs" />
<Compile Include="ProviderTests\DiskScanProviderTests\CleanUpFixture.cs" />
<Compile Include="ProviderTests\DiskScanProviderTests\CleanUpDropFolderFixture.cs" />
<Compile Include="ProviderTests\DiskScanProviderTests\GetVideoFilesFixture.cs" />
<Compile Include="ProviderTests\DiskScanProviderTests\ScanFixture.cs" />
<Compile Include="ProviderTests\DownloadClientTests\PneumaticProviderFixture.cs" />
<Compile Include="ProviderTests\Metadata\Xbmc_ForEpisodeFile_Fixture.cs" /> <Compile Include="ProviderTests\Metadata\Xbmc_ForEpisodeFile_Fixture.cs" />
<Compile Include="ProviderTests\Metadata\Xbmc_ForSeries_Fixture.cs" /> <Compile Include="ProviderTests\Metadata\Xbmc_ForSeries_Fixture.cs" />
<Compile Include="ProviderTests\PostDownloadProviderTests\ProcessDropDirectoryFixture.cs" />
<Compile Include="ProviderTests\PostDownloadProviderTests\ProcessVideoFileFixture.cs" />
<Compile Include="ProviderTests\SearchHistoryProviderTest.cs" /> <Compile Include="ProviderTests\SearchHistoryProviderTest.cs" />
<Compile Include="ProviderTests\PlexProviderTest.cs" /> <Compile Include="ProviderTests\PlexProviderTest.cs" />
<Compile Include="ProviderTests\SeasonProviderTest.cs" /> <Compile Include="ProviderTests\SeasonProviderTest.cs" />
@ -139,9 +146,9 @@
<Compile Include="ProviderTests\ProwlProviderTest.cs" /> <Compile Include="ProviderTests\ProwlProviderTest.cs" />
<Compile Include="ProviderTests\GrowlProviderTest.cs" /> <Compile Include="ProviderTests\GrowlProviderTest.cs" />
<Compile Include="ProviderTests\DiskProviderTests\ExtractArchiveFixture.cs" /> <Compile Include="ProviderTests\DiskProviderTests\ExtractArchiveFixture.cs" />
<Compile Include="ProviderTests\PostDownloadProviderTests\PostDownloadProviderFixture.cs" /> <Compile Include="ProviderTests\PostDownloadProviderTests\GetFolderNameWithStatusFixture.cs" />
<Compile Include="JobTests\SearchJobTest.cs" /> <Compile Include="JobTests\SearchJobTest.cs" />
<Compile Include="ProviderTests\PostDownloadProviderTests\ProcessDownloadProviderFixture.cs" /> <Compile Include="ProviderTests\PostDownloadProviderTests\ProcessDownloadFixture.cs" />
<Compile Include="ProviderTests\JobProviderTests\TestJobs.cs" /> <Compile Include="ProviderTests\JobProviderTests\TestJobs.cs" />
<Compile Include="JobTests\AppUpdateJobFixture.cs" /> <Compile Include="JobTests\AppUpdateJobFixture.cs" />
<Compile Include="ProviderTests\UpdateProviderTests\GetUpdateLogFixture.cs" /> <Compile Include="ProviderTests\UpdateProviderTests\GetUpdateLogFixture.cs" />
@ -157,9 +164,9 @@
<Compile Include="ProviderTests\EventClientProviderTest.cs" /> <Compile Include="ProviderTests\EventClientProviderTest.cs" />
<Compile Include="CentralDispatchFixture.cs" /> <Compile Include="CentralDispatchFixture.cs" />
<Compile Include="ProviderTests\XbmcProviderTest.cs" /> <Compile Include="ProviderTests\XbmcProviderTest.cs" />
<Compile Include="ProviderTests\DiskScanProviderTest.cs" /> <Compile Include="ProviderTests\DiskScanProviderTests\MoveEpisodeFileFixture.cs" />
<Compile Include="ProviderTests\EpisodeProviderTest_GetEpisodesByParseResult.cs" /> <Compile Include="ProviderTests\EpisodeProviderTest_GetEpisodesByParseResult.cs" />
<Compile Include="ProviderTests\DiskScanProviderTest_ImportFile.cs" /> <Compile Include="ProviderTests\DiskScanProviderTests\ImportFileFixture.cs" />
<Compile Include="FluentTest.cs" /> <Compile Include="FluentTest.cs" />
<Compile Include="ProviderTests\LogProviderTests\LogProviderFixture.cs" /> <Compile Include="ProviderTests\LogProviderTests\LogProviderFixture.cs" />
<Compile Include="ProviderTests\UpcomingEpisodesProviderTest.cs" /> <Compile Include="ProviderTests\UpcomingEpisodesProviderTest.cs" />

@ -1,376 +0,0 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Linq.Expressions;
using FizzWare.NBuilder;
using FluentAssertions;
using Moq;
using NUnit.Framework;
using NzbDrone.Common;
using NzbDrone.Core.Model;
using NzbDrone.Core.Providers;
using NzbDrone.Core.Providers.Core;
using NzbDrone.Core.Repository;
using NzbDrone.Core.Repository.Quality;
using NzbDrone.Core.Test.Framework;
using NzbDrone.Test.Common;
using NzbDrone.Test.Common.AutoMoq;
namespace NzbDrone.Core.Test.ProviderTests
{
// ReSharper disable InconsistentNaming
public class DiskScanProviderTest : CoreTest
{
[Test]
public void scan_series_should_update_the_last_scan_date()
{
Mocker.GetMock<SeriesProvider>()
.Setup(c => c.UpdateSeries(It.Is<Series>(s => s.LastDiskSync != null))).Verifiable();
Mocker.GetMock<EpisodeProvider>()
.Setup(c => c.GetEpisodeBySeries(It.IsAny<long>()))
.Returns(new List<Episode> { new Episode() });
Mocker.GetMock<DiskProvider>()
.Setup(c => c.FolderExists(It.IsAny<string>()))
.Returns(true);
Mocker.GetMock<MediaFileProvider>()
.Setup(c => c.GetSeriesFiles(It.IsAny<int>()))
.Returns(new List<EpisodeFile>());
Mocker.Resolve<DiskScanProvider>().Scan(new Series());
Mocker.VerifyAllMocks();
}
[Test]
public void cleanup_should_skip_existing_files()
{
WithStrictMocker();
var episodes = Builder<EpisodeFile>.CreateListOfSize(10).Build();
Mocker.GetMock<DiskProvider>()
.Setup(e => e.FileExists(It.IsAny<String>()))
.Returns(true);
//Act
Mocker.Resolve<DiskScanProvider>().CleanUp(episodes);
//Assert
Mocker.VerifyAllMocks();
}
[Test]
public void cleanup_should_delete_none_existing_files()
{
WithStrictMocker();
var episodes = Builder<EpisodeFile>.CreateListOfSize(10).Build();
Mocker.GetMock<DiskProvider>()
.Setup(e => e.FileExists(It.IsAny<String>()))
.Returns(false);
Mocker.GetMock<EpisodeProvider>()
.Setup(e => e.GetEpisodesByFileId(It.IsAny<int>()))
.Returns(new List<Episode>());
Mocker.GetMock<MediaFileProvider>()
.Setup(e => e.Delete(It.IsAny<int>()));
//Act
Mocker.Resolve<DiskScanProvider>().CleanUp(episodes);
//Assert
Mocker.VerifyAllMocks();
Mocker.GetMock<EpisodeProvider>()
.Verify(e => e.GetEpisodesByFileId(It.IsAny<int>()), Times.Exactly(10));
Mocker.GetMock<MediaFileProvider>()
.Verify(e => e.Delete(It.IsAny<int>()), Times.Exactly(10));
}
[Test]
public void cleanup_should_delete_none_existing_files_remove_links_to_episodes()
{
WithStrictMocker();
var episodes = Builder<EpisodeFile>.CreateListOfSize(10).Build();
Mocker.GetMock<DiskProvider>()
.Setup(e => e.FileExists(It.IsAny<String>()))
.Returns(false);
Mocker.GetMock<EpisodeProvider>()
.Setup(e => e.GetEpisodesByFileId(It.IsAny<int>()))
.Returns(new List<Episode> { new Episode { EpisodeFileId = 10 }, new Episode { EpisodeFileId = 10 } });
Mocker.GetMock<EpisodeProvider>()
.Setup(e => e.UpdateEpisode(It.IsAny<Episode>()));
Mocker.GetMock<MediaFileProvider>()
.Setup(e => e.Delete(It.IsAny<int>()));
Mocker.GetMock<ConfigProvider>()
.SetupGet(s => s.AutoIgnorePreviouslyDownloadedEpisodes)
.Returns(true);
//Act
Mocker.Resolve<DiskScanProvider>().CleanUp(episodes);
//Assert
Mocker.VerifyAllMocks();
Mocker.GetMock<EpisodeProvider>()
.Verify(e => e.GetEpisodesByFileId(It.IsAny<int>()), Times.Exactly(10));
Mocker.GetMock<EpisodeProvider>()
.Verify(e => e.UpdateEpisode(It.Is<Episode>(g=>g.EpisodeFileId == 0)), Times.Exactly(20));
Mocker.GetMock<MediaFileProvider>()
.Verify(e => e.Delete(It.IsAny<int>()), Times.Exactly(10));
Mocker.GetMock<MediaFileProvider>()
.Verify(e => e.Delete(It.IsAny<int>()), Times.Exactly(10));
}
[Test]
public void scan_series_should_log_warning_if_path_doesnt_exist_on_disk()
{
//Setup
WithStrictMocker();
var series = Builder<Series>.CreateNew()
.With(s => s.Path = @"C:\Test\TV\SeriesName\")
.Build();
Mocker.GetMock<MediaFileProvider>()
.Setup(c => c.CleanUpDatabase());
Mocker.GetMock<DiskProvider>()
.Setup(c => c.FolderExists(series.Path))
.Returns(false);
//Act
Mocker.Resolve<DiskScanProvider>().Scan(series, series.Path);
//Assert
Mocker.VerifyAllMocks();
ExceptionVerification.ExpectedWarns(1);
}
[Test]
public void move_should_not_move_file_if_source_and_destination_are_the_same_path()
{
var fakeSeries = Builder<Series>.CreateNew()
.With(s => s.SeriesId = 5)
.With(s => s.Title = "30 Rock")
.Build();
var fakeEpisode = Builder<Episode>.CreateListOfSize(1)
.All()
.With(e => e.SeriesId = fakeSeries.SeriesId)
.With(e => e.SeasonNumber = 1)
.With(e => e.EpisodeNumber = 1)
.Build();
const string filename = @"30 Rock - S01E01 - TBD";
var fi = new FileInfo(Path.Combine(@"C:\Test\TV\30 Rock\Season 01\", filename + ".avi"));
var file = Builder<EpisodeFile>.CreateNew()
.With(f => f.SeriesId = fakeSeries.SeriesId)
.With(f => f.Path = fi.FullName)
.Build();
Mocker.GetMock<SeriesProvider>()
.Setup(e => e.GetSeries(fakeSeries.SeriesId))
.Returns(fakeSeries);
Mocker.GetMock<EpisodeProvider>()
.Setup(e => e.GetEpisodesByFileId(file.EpisodeFileId))
.Returns(fakeEpisode);
Mocker.GetMock<MediaFileProvider>()
.Setup(e => e.GetNewFilename(fakeEpisode, fakeSeries.Title, It.IsAny<QualityTypes>(), It.IsAny<bool>(), It.IsAny<EpisodeFile>()))
.Returns(filename);
Mocker.GetMock<MediaFileProvider>()
.Setup(e => e.CalculateFilePath(It.IsAny<Series>(), fakeEpisode.First().SeasonNumber, filename, ".avi"))
.Returns(fi);
//Act
var result = Mocker.Resolve<DiskScanProvider>().MoveEpisodeFile(file, false);
//Assert
result.Should().BeNull();
}
[Test]
public void CleanUpDropFolder_should_do_nothing_if_no_files_are_found()
{
//Setup
var folder = @"C:\Test\DropDir\The Office";
Mocker.GetMock<DiskProvider>().Setup(s => s.GetFiles(folder, SearchOption.AllDirectories))
.Returns(new string[0]);
//Act
Mocker.Resolve<DiskScanProvider>().CleanUpDropFolder(folder);
//Assert
Mocker.GetMock<MediaFileProvider>().Verify(v => v.GetFileByPath(It.IsAny<string>()), Times.Never());
}
[Test]
public void CleanUpDropFolder_should_do_nothing_if_no_conflicting_files_are_found()
{
//Setup
var folder = @"C:\Test\DropDir\The Office";
var filename = Path.Combine(folder, "NotAProblem.avi");
var episodeFile = Builder<EpisodeFile>.CreateNew()
.With(f => f.Path = filename.NormalizePath())
.With(f => f.SeriesId = 12345)
.Build();
Mocker.GetMock<DiskProvider>().Setup(s => s.GetFiles(folder, SearchOption.AllDirectories))
.Returns(new string[] { filename });
Mocker.GetMock<MediaFileProvider>().Setup(s => s.GetFileByPath(filename))
.Returns(() => null);
//Act
Mocker.Resolve<DiskScanProvider>().CleanUpDropFolder(folder);
//Assert
Mocker.GetMock<MediaFileProvider>().Verify(v => v.GetFileByPath(filename), Times.Once());
Mocker.GetMock<SeriesProvider>().Verify(v => v.GetSeries(It.IsAny<int>()), Times.Never());
}
[Test]
public void CleanUpDropFolder_should_move_file_if_a_conflict_is_found()
{
//Setup
var folder = @"C:\Test\DropDir\The Office";
var filename = Path.Combine(folder, "Problem.avi");
var seriesId = 12345;
var newFilename = "S01E01 - Title";
var newFilePath = @"C:\Test\TV\The Office\Season 01\S01E01 - Title.avi";
var episodeFile = Builder<EpisodeFile>.CreateNew()
.With(f => f.Path = filename.NormalizePath())
.With(f => f.SeriesId = seriesId)
.Build();
var series = Builder<Series>.CreateNew()
.With(s => s.SeriesId = seriesId)
.With(s => s.Title = "The Office")
.Build();
var episode = Builder<Episode>.CreateListOfSize(1)
.All()
.With(e => e.SeriesId = seriesId)
.With(e => e.EpisodeFileId = episodeFile.EpisodeFileId)
.Build();
Mocker.GetMock<MediaFileProvider>().Setup(v => v.GetFileByPath(filename))
.Returns(() => null);
Mocker.GetMock<DiskProvider>().Setup(s => s.GetFiles(folder, SearchOption.AllDirectories))
.Returns(new string[] { filename });
Mocker.GetMock<MediaFileProvider>().Setup(s => s.GetFileByPath(filename))
.Returns(episodeFile);
Mocker.GetMock<SeriesProvider>().Setup(s => s.GetSeries(It.IsAny<int>()))
.Returns(series);
Mocker.GetMock<EpisodeProvider>().Setup(s => s.GetEpisodesByFileId(episodeFile.EpisodeFileId))
.Returns(episode);
Mocker.GetMock<MediaFileProvider>().Setup(s => s.GetNewFilename(It.IsAny<IList<Episode>>(), series.Title, QualityTypes.Unknown, false, It.IsAny<EpisodeFile>()))
.Returns(newFilename);
Mocker.GetMock<MediaFileProvider>().Setup(s => s.CalculateFilePath(It.IsAny<Series>(), It.IsAny<int>(), It.IsAny<string>(), It.IsAny<string>()))
.Returns(new FileInfo(newFilePath));
Mocker.GetMock<DiskProvider>().Setup(s => s.MoveFile(episodeFile.Path, newFilePath));
//Act
Mocker.Resolve<DiskScanProvider>().CleanUpDropFolder(folder);
//Assert
Mocker.GetMock<MediaFileProvider>().Verify(v => v.GetFileByPath(filename), Times.Once());
Mocker.GetMock<DiskProvider>().Verify(v => v.MoveFile(filename.NormalizePath(), newFilePath), Times.Once());
}
[Test]
public void MoveEpisodeFile_should_use_EpisodeFiles_quality()
{
var fakeSeries = Builder<Series>.CreateNew()
.With(s => s.SeriesId = 5)
.With(s => s.Title = "30 Rock")
.Build();
var fakeEpisode = Builder<Episode>.CreateListOfSize(1)
.All()
.With(e => e.SeriesId = fakeSeries.SeriesId)
.With(e => e.SeasonNumber = 1)
.With(e => e.EpisodeNumber = 1)
.Build();
const string filename = @"30 Rock - S01E01 - TBD";
var fi = new FileInfo(Path.Combine(@"C:\Test\TV\30 Rock\Season 01\", filename + ".mkv"));
var currentFilename = Path.Combine(@"C:\Test\TV\30 Rock\Season 01\", "30.Rock.S01E01.Test.WED-DL.mkv");
const string message = "30 Rock - 1x01 - [WEBDL]";
var file = Builder<EpisodeFile>.CreateNew()
.With(f => f.SeriesId = fakeSeries.SeriesId)
.With(f => f.Path = currentFilename)
.With(f => f.Quality = QualityTypes.WEBDL)
.With(f => f.Proper = false)
.Build();
Mocker.GetMock<SeriesProvider>()
.Setup(e => e.GetSeries(fakeSeries.SeriesId))
.Returns(fakeSeries);
Mocker.GetMock<EpisodeProvider>()
.Setup(e => e.GetEpisodesByFileId(file.EpisodeFileId))
.Returns(fakeEpisode);
Mocker.GetMock<MediaFileProvider>()
.Setup(e => e.GetNewFilename(fakeEpisode, fakeSeries.Title, It.IsAny<QualityTypes>(), It.IsAny<bool>(), It.IsAny<EpisodeFile>()))
.Returns(filename);
Mocker.GetMock<MediaFileProvider>()
.Setup(e => e.CalculateFilePath(It.IsAny<Series>(), fakeEpisode.First().SeasonNumber, filename, ".mkv"))
.Returns(fi);
Mocker.GetMock<DownloadProvider>()
.Setup(s => s.GetDownloadTitle(It.Is<EpisodeParseResult>(e => e.Quality == new Quality{ QualityType = QualityTypes.WEBDL, Proper = false })))
.Returns(message);
Mocker.GetMock<ExternalNotificationProvider>()
.Setup(e => e.OnDownload("30 Rock - 1x01 - [WEBDL]", It.IsAny<Series>()));
//Act
var result = Mocker.Resolve<DiskScanProvider>().MoveEpisodeFile(file, true);
//Assert
result.Should().NotBeNull();
Mocker.GetMock<ExternalNotificationProvider>()
.Verify(e => e.OnDownload("30 Rock - 1x01 - [WEBDL]", It.IsAny<Series>()), Times.Once());
}
}
}

@ -0,0 +1,124 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Linq.Expressions;
using FizzWare.NBuilder;
using FluentAssertions;
using Moq;
using NUnit.Framework;
using NzbDrone.Common;
using NzbDrone.Core.Model;
using NzbDrone.Core.Providers;
using NzbDrone.Core.Providers.Core;
using NzbDrone.Core.Repository;
using NzbDrone.Core.Repository.Quality;
using NzbDrone.Core.Test.Framework;
using NzbDrone.Test.Common;
using NzbDrone.Test.Common.AutoMoq;
namespace NzbDrone.Core.Test.ProviderTests.DiskScanProviderTests
{
// ReSharper disable InconsistentNaming
public class CleanUpDropFolderFixture : CoreTest
{
[Test]
public void should_do_nothing_if_no_files_are_found()
{
//Setup
var folder = @"C:\Test\DropDir\The Office";
Mocker.GetMock<DiskProvider>().Setup(s => s.GetFiles(folder, SearchOption.AllDirectories))
.Returns(new string[0]);
//Act
Mocker.Resolve<DiskScanProvider>().CleanUpDropFolder(folder);
//Assert
Mocker.GetMock<MediaFileProvider>().Verify(v => v.GetFileByPath(It.IsAny<string>()), Times.Never());
}
[Test]
public void should_do_nothing_if_no_conflicting_files_are_found()
{
//Setup
var folder = @"C:\Test\DropDir\The Office";
var filename = Path.Combine(folder, "NotAProblem.avi");
var episodeFile = Builder<EpisodeFile>.CreateNew()
.With(f => f.Path = filename.NormalizePath())
.With(f => f.SeriesId = 12345)
.Build();
Mocker.GetMock<DiskProvider>().Setup(s => s.GetFiles(folder, SearchOption.AllDirectories))
.Returns(new string[] { filename });
Mocker.GetMock<MediaFileProvider>().Setup(s => s.GetFileByPath(filename))
.Returns(() => null);
//Act
Mocker.Resolve<DiskScanProvider>().CleanUpDropFolder(folder);
//Assert
Mocker.GetMock<MediaFileProvider>().Verify(v => v.GetFileByPath(filename), Times.Once());
Mocker.GetMock<SeriesProvider>().Verify(v => v.GetSeries(It.IsAny<int>()), Times.Never());
}
[Test]
public void should_move_file_if_a_conflict_is_found()
{
//Setup
var folder = @"C:\Test\DropDir\The Office";
var filename = Path.Combine(folder, "Problem.avi");
var seriesId = 12345;
var newFilename = "S01E01 - Title";
var newFilePath = @"C:\Test\TV\The Office\Season 01\S01E01 - Title.avi";
var episodeFile = Builder<EpisodeFile>.CreateNew()
.With(f => f.Path = filename.NormalizePath())
.With(f => f.SeriesId = seriesId)
.Build();
var series = Builder<Series>.CreateNew()
.With(s => s.SeriesId = seriesId)
.With(s => s.Title = "The Office")
.Build();
var episode = Builder<Episode>.CreateListOfSize(1)
.All()
.With(e => e.SeriesId = seriesId)
.With(e => e.EpisodeFileId = episodeFile.EpisodeFileId)
.Build();
Mocker.GetMock<MediaFileProvider>().Setup(v => v.GetFileByPath(filename))
.Returns(() => null);
Mocker.GetMock<DiskProvider>().Setup(s => s.GetFiles(folder, SearchOption.AllDirectories))
.Returns(new string[] { filename });
Mocker.GetMock<MediaFileProvider>().Setup(s => s.GetFileByPath(filename))
.Returns(episodeFile);
Mocker.GetMock<SeriesProvider>().Setup(s => s.GetSeries(It.IsAny<int>()))
.Returns(series);
Mocker.GetMock<EpisodeProvider>().Setup(s => s.GetEpisodesByFileId(episodeFile.EpisodeFileId))
.Returns(episode);
Mocker.GetMock<MediaFileProvider>().Setup(s => s.GetNewFilename(It.IsAny<IList<Episode>>(), series.Title, QualityTypes.Unknown, false, It.IsAny<EpisodeFile>()))
.Returns(newFilename);
Mocker.GetMock<MediaFileProvider>().Setup(s => s.CalculateFilePath(It.IsAny<Series>(), It.IsAny<int>(), It.IsAny<string>(), It.IsAny<string>()))
.Returns(new FileInfo(newFilePath));
Mocker.GetMock<DiskProvider>().Setup(s => s.MoveFile(episodeFile.Path, newFilePath));
//Act
Mocker.Resolve<DiskScanProvider>().CleanUpDropFolder(folder);
//Assert
Mocker.GetMock<MediaFileProvider>().Verify(v => v.GetFileByPath(filename), Times.Once());
Mocker.GetMock<DiskProvider>().Verify(v => v.MoveFile(filename.NormalizePath(), newFilePath), Times.Once());
}
}
}

@ -0,0 +1,116 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Linq.Expressions;
using FizzWare.NBuilder;
using FluentAssertions;
using Moq;
using NUnit.Framework;
using NzbDrone.Common;
using NzbDrone.Core.Model;
using NzbDrone.Core.Providers;
using NzbDrone.Core.Providers.Core;
using NzbDrone.Core.Repository;
using NzbDrone.Core.Repository.Quality;
using NzbDrone.Core.Test.Framework;
using NzbDrone.Test.Common;
using NzbDrone.Test.Common.AutoMoq;
namespace NzbDrone.Core.Test.ProviderTests.DiskScanProviderTests
{
// ReSharper disable InconsistentNaming
public class CleanUpFixture : CoreTest
{
[Test]
public void should_skip_existing_files()
{
var episodes = Builder<EpisodeFile>.CreateListOfSize(10).Build();
Mocker.GetMock<DiskProvider>()
.Setup(e => e.FileExists(It.IsAny<String>()))
.Returns(true);
//Act
Mocker.Resolve<DiskScanProvider>().CleanUp(episodes);
//Assert
Mocker.VerifyAllMocks();
}
[Test]
public void should_delete_none_existing_files()
{
var episodes = Builder<EpisodeFile>.CreateListOfSize(10).Build();
Mocker.GetMock<DiskProvider>()
.Setup(e => e.FileExists(It.IsAny<String>()))
.Returns(false);
Mocker.GetMock<EpisodeProvider>()
.Setup(e => e.GetEpisodesByFileId(It.IsAny<int>()))
.Returns(new List<Episode>());
Mocker.GetMock<MediaFileProvider>()
.Setup(e => e.Delete(It.IsAny<int>()));
//Act
Mocker.Resolve<DiskScanProvider>().CleanUp(episodes);
//Assert
Mocker.VerifyAllMocks();
Mocker.GetMock<EpisodeProvider>()
.Verify(e => e.GetEpisodesByFileId(It.IsAny<int>()), Times.Exactly(10));
Mocker.GetMock<MediaFileProvider>()
.Verify(e => e.Delete(It.IsAny<int>()), Times.Exactly(10));
}
[Test]
public void should_delete_none_existing_files_remove_links_to_episodes()
{
var episodes = Builder<EpisodeFile>.CreateListOfSize(10).Build();
Mocker.GetMock<DiskProvider>()
.Setup(e => e.FileExists(It.IsAny<String>()))
.Returns(false);
Mocker.GetMock<EpisodeProvider>()
.Setup(e => e.GetEpisodesByFileId(It.IsAny<int>()))
.Returns(new List<Episode> { new Episode { EpisodeFileId = 10 }, new Episode { EpisodeFileId = 10 } });
Mocker.GetMock<EpisodeProvider>()
.Setup(e => e.UpdateEpisode(It.IsAny<Episode>()));
Mocker.GetMock<MediaFileProvider>()
.Setup(e => e.Delete(It.IsAny<int>()));
Mocker.GetMock<ConfigProvider>()
.SetupGet(s => s.AutoIgnorePreviouslyDownloadedEpisodes)
.Returns(true);
//Act
Mocker.Resolve<DiskScanProvider>().CleanUp(episodes);
//Assert
Mocker.VerifyAllMocks();
Mocker.GetMock<EpisodeProvider>()
.Verify(e => e.GetEpisodesByFileId(It.IsAny<int>()), Times.Exactly(10));
Mocker.GetMock<EpisodeProvider>()
.Verify(e => e.UpdateEpisode(It.Is<Episode>(g=>g.EpisodeFileId == 0)), Times.Exactly(20));
Mocker.GetMock<MediaFileProvider>()
.Verify(e => e.Delete(It.IsAny<int>()), Times.Exactly(10));
Mocker.GetMock<MediaFileProvider>()
.Verify(e => e.Delete(It.IsAny<int>()), Times.Exactly(10));
}
}
}

@ -0,0 +1,85 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Linq.Expressions;
using FizzWare.NBuilder;
using FluentAssertions;
using Moq;
using NUnit.Framework;
using NzbDrone.Common;
using NzbDrone.Core.Model;
using NzbDrone.Core.Providers;
using NzbDrone.Core.Providers.Core;
using NzbDrone.Core.Repository;
using NzbDrone.Core.Repository.Quality;
using NzbDrone.Core.Test.Framework;
using NzbDrone.Test.Common;
using NzbDrone.Test.Common.AutoMoq;
namespace NzbDrone.Core.Test.ProviderTests.DiskScanProviderTests
{
// ReSharper disable InconsistentNaming
public class GetVideoFilesFixture : CoreTest
{
private string[] _files;
[SetUp]
public void Setup()
{
_files = new string[]
{
@"C:\Test\30 Rock1.mkv",
@"C:\Test\30 Rock2.avi",
@"C:\Test\30 Rock3.mp4",
@"C:\Test\30 Rock4.wmv",
@"C:\Test\movie.exe"
};
Mocker.GetMock<DiskProvider>()
.Setup(s => s.GetFiles(It.IsAny<String>(), SearchOption.AllDirectories))
.Returns(_files);
}
[Test]
public void should_check_all_directories()
{
var path = @"C:\Test\";
Mocker.Resolve<DiskScanProvider>().GetVideoFiles(path);
Mocker.GetMock<DiskProvider>().Verify(s => s.GetFiles(path, SearchOption.AllDirectories), Times.Once());
Mocker.GetMock<DiskProvider>().Verify(s => s.GetFiles(path, SearchOption.TopDirectoryOnly), Times.Never());
}
[Test]
public void should_check_all_directories_when_allDirectories_is_true()
{
var path = @"C:\Test\";
Mocker.Resolve<DiskScanProvider>().GetVideoFiles(path, true);
Mocker.GetMock<DiskProvider>().Verify(s => s.GetFiles(path, SearchOption.AllDirectories), Times.Once());
Mocker.GetMock<DiskProvider>().Verify(s => s.GetFiles(path, SearchOption.TopDirectoryOnly), Times.Never());
}
[Test]
public void should_check_top_level_directory_only_when_allDirectories_is_false()
{
var path = @"C:\Test\";
Mocker.Resolve<DiskScanProvider>().GetVideoFiles(path, false);
Mocker.GetMock<DiskProvider>().Verify(s => s.GetFiles(path, SearchOption.AllDirectories), Times.Never());
Mocker.GetMock<DiskProvider>().Verify(s => s.GetFiles(path, SearchOption.TopDirectoryOnly), Times.Once());
}
[Test]
public void should_return_video_files_only()
{
var path = @"C:\Test\";
Mocker.Resolve<DiskScanProvider>().GetVideoFiles(path).Should().HaveCount(4);
}
}
}

@ -15,10 +15,10 @@ using NzbDrone.Core.Test.Framework;
using NzbDrone.Test.Common; using NzbDrone.Test.Common;
using NzbDrone.Test.Common.AutoMoq; using NzbDrone.Test.Common.AutoMoq;
namespace NzbDrone.Core.Test.ProviderTests namespace NzbDrone.Core.Test.ProviderTests.DiskScanProviderTests
{ {
// ReSharper disable InconsistentNaming // ReSharper disable InconsistentNaming
public class DiskScanProviderTest_ImportFile : CoreTest public class ImportFileFixture : CoreTest
{ {
[Test] [Test]
public void import_new_file_should_succeed() public void import_new_file_should_succeed()

@ -0,0 +1,130 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Linq.Expressions;
using FizzWare.NBuilder;
using FluentAssertions;
using Moq;
using NUnit.Framework;
using NzbDrone.Common;
using NzbDrone.Core.Model;
using NzbDrone.Core.Providers;
using NzbDrone.Core.Providers.Core;
using NzbDrone.Core.Repository;
using NzbDrone.Core.Repository.Quality;
using NzbDrone.Core.Test.Framework;
using NzbDrone.Test.Common;
using NzbDrone.Test.Common.AutoMoq;
namespace NzbDrone.Core.Test.ProviderTests.DiskScanProviderTests
{
// ReSharper disable InconsistentNaming
public class MoveEpisodeFileFixture : CoreTest
{
[Test]
public void should_not_move_file_if_source_and_destination_are_the_same_path()
{
var fakeSeries = Builder<Series>.CreateNew()
.With(s => s.SeriesId = 5)
.With(s => s.Title = "30 Rock")
.Build();
var fakeEpisode = Builder<Episode>.CreateListOfSize(1)
.All()
.With(e => e.SeriesId = fakeSeries.SeriesId)
.With(e => e.SeasonNumber = 1)
.With(e => e.EpisodeNumber = 1)
.Build();
const string filename = @"30 Rock - S01E01 - TBD";
var fi = new FileInfo(Path.Combine(@"C:\Test\TV\30 Rock\Season 01\", filename + ".avi"));
var file = Builder<EpisodeFile>.CreateNew()
.With(f => f.SeriesId = fakeSeries.SeriesId)
.With(f => f.Path = fi.FullName)
.Build();
Mocker.GetMock<SeriesProvider>()
.Setup(e => e.GetSeries(fakeSeries.SeriesId))
.Returns(fakeSeries);
Mocker.GetMock<EpisodeProvider>()
.Setup(e => e.GetEpisodesByFileId(file.EpisodeFileId))
.Returns(fakeEpisode);
Mocker.GetMock<MediaFileProvider>()
.Setup(e => e.GetNewFilename(fakeEpisode, fakeSeries.Title, It.IsAny<QualityTypes>(), It.IsAny<bool>(), It.IsAny<EpisodeFile>()))
.Returns(filename);
Mocker.GetMock<MediaFileProvider>()
.Setup(e => e.CalculateFilePath(It.IsAny<Series>(), fakeEpisode.First().SeasonNumber, filename, ".avi"))
.Returns(fi);
//Act
var result = Mocker.Resolve<DiskScanProvider>().MoveEpisodeFile(file, false);
//Assert
result.Should().BeNull();
}
[Test]
public void should_use_EpisodeFiles_quality()
{
var fakeSeries = Builder<Series>.CreateNew()
.With(s => s.SeriesId = 5)
.With(s => s.Title = "30 Rock")
.Build();
var fakeEpisode = Builder<Episode>.CreateListOfSize(1)
.All()
.With(e => e.SeriesId = fakeSeries.SeriesId)
.With(e => e.SeasonNumber = 1)
.With(e => e.EpisodeNumber = 1)
.Build();
const string filename = @"30 Rock - S01E01 - TBD";
var fi = new FileInfo(Path.Combine(@"C:\Test\TV\30 Rock\Season 01\", filename + ".mkv"));
var currentFilename = Path.Combine(@"C:\Test\TV\30 Rock\Season 01\", "30.Rock.S01E01.Test.WED-DL.mkv");
const string message = "30 Rock - 1x01 - [WEBDL]";
var file = Builder<EpisodeFile>.CreateNew()
.With(f => f.SeriesId = fakeSeries.SeriesId)
.With(f => f.Path = currentFilename)
.With(f => f.Quality = QualityTypes.WEBDL)
.With(f => f.Proper = false)
.Build();
Mocker.GetMock<SeriesProvider>()
.Setup(e => e.GetSeries(fakeSeries.SeriesId))
.Returns(fakeSeries);
Mocker.GetMock<EpisodeProvider>()
.Setup(e => e.GetEpisodesByFileId(file.EpisodeFileId))
.Returns(fakeEpisode);
Mocker.GetMock<MediaFileProvider>()
.Setup(e => e.GetNewFilename(fakeEpisode, fakeSeries.Title, It.IsAny<QualityTypes>(), It.IsAny<bool>(), It.IsAny<EpisodeFile>()))
.Returns(filename);
Mocker.GetMock<MediaFileProvider>()
.Setup(e => e.CalculateFilePath(It.IsAny<Series>(), fakeEpisode.First().SeasonNumber, filename, ".mkv"))
.Returns(fi);
Mocker.GetMock<DownloadProvider>()
.Setup(s => s.GetDownloadTitle(It.Is<EpisodeParseResult>(e => e.Quality == new Quality{ QualityType = QualityTypes.WEBDL, Proper = false })))
.Returns(message);
Mocker.GetMock<ExternalNotificationProvider>()
.Setup(e => e.OnDownload("30 Rock - 1x01 - [WEBDL]", It.IsAny<Series>()));
//Act
var result = Mocker.Resolve<DiskScanProvider>().MoveEpisodeFile(file, true);
//Assert
result.Should().NotBeNull();
Mocker.GetMock<ExternalNotificationProvider>()
.Verify(e => e.OnDownload("30 Rock - 1x01 - [WEBDL]", It.IsAny<Series>()), Times.Once());
}
}
}

@ -0,0 +1,78 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Linq.Expressions;
using FizzWare.NBuilder;
using FluentAssertions;
using Moq;
using NUnit.Framework;
using NzbDrone.Common;
using NzbDrone.Core.Model;
using NzbDrone.Core.Providers;
using NzbDrone.Core.Providers.Core;
using NzbDrone.Core.Repository;
using NzbDrone.Core.Repository.Quality;
using NzbDrone.Core.Test.Framework;
using NzbDrone.Test.Common;
using NzbDrone.Test.Common.AutoMoq;
namespace NzbDrone.Core.Test.ProviderTests.DiskScanProviderTests
{
// ReSharper disable InconsistentNaming
public class ScanFixture : CoreTest
{
[Test]
public void series_should_update_the_last_scan_date()
{
Mocker.GetMock<SeriesProvider>()
.Setup(c => c.UpdateSeries(It.Is<Series>(s => s.LastDiskSync != null))).Verifiable();
Mocker.GetMock<EpisodeProvider>()
.Setup(c => c.GetEpisodeBySeries(It.IsAny<long>()))
.Returns(new List<Episode> { new Episode() });
Mocker.GetMock<DiskProvider>()
.Setup(c => c.FolderExists(It.IsAny<string>()))
.Returns(true);
Mocker.GetMock<MediaFileProvider>()
.Setup(c => c.GetSeriesFiles(It.IsAny<int>()))
.Returns(new List<EpisodeFile>());
Mocker.Resolve<DiskScanProvider>().Scan(new Series());
Mocker.VerifyAllMocks();
}
[Test]
public void series_should_log_warning_if_path_doesnt_exist_on_disk()
{
//Setup
WithStrictMocker();
var series = Builder<Series>.CreateNew()
.With(s => s.Path = @"C:\Test\TV\SeriesName\")
.Build();
Mocker.GetMock<MediaFileProvider>()
.Setup(c => c.CleanUpDatabase());
Mocker.GetMock<DiskProvider>()
.Setup(c => c.FolderExists(series.Path))
.Returns(false);
//Act
Mocker.Resolve<DiskScanProvider>().Scan(series, series.Path);
//Assert
Mocker.VerifyAllMocks();
ExceptionVerification.ExpectedWarns(1);
}
}
}

@ -0,0 +1,78 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Text;
using FluentAssertions;
using Moq;
using NUnit.Framework;
using NzbDrone.Common;
using NzbDrone.Core.Providers.Core;
using NzbDrone.Core.Providers.DownloadClients;
using NzbDrone.Core.Test.Framework;
using NzbDrone.Test.Common;
namespace NzbDrone.Core.Test.ProviderTests.DownloadClientTests
{
[TestFixture]
public class PneumaticProviderFixture : CoreTest
{
private const string nzbUrl = "http://www.nzbs.com/url";
private const string title = "some_nzb_title";
private const string pneumaticFolder = @"d:\nzb\pneumatic\";
private const string nzbPath = @"d:\nzb\blackhole\some_nzb_title.nzb";
[SetUp]
public void Setup()
{
Mocker.GetMock<ConfigProvider>().SetupGet(c => c.BlackholeDirectory).Returns(pneumaticFolder);
}
private void WithExistingFile()
{
Mocker.GetMock<DiskProvider>().Setup(c => c.FileExists(nzbPath)).Returns(true);
}
private void WithFailedDownload()
{
Mocker.GetMock<HttpProvider>().Setup(c => c.DownloadFile(It.IsAny<string>(), It.IsAny<string>())).Throws(new WebException());
}
[Test]
public void should_download_file_if_it_doesnt_exist()
{
Mocker.Resolve<PneumaticProvider>().DownloadNzb(nzbUrl, title).Should().BeTrue();
Mocker.GetMock<HttpProvider>().Verify(c => c.DownloadFile(nzbUrl, nzbPath),Times.Once());
}
[Test]
public void should_not_download_file_if_it_doesn_exist()
{
WithExistingFile();
Mocker.Resolve<PneumaticProvider>().DownloadNzb(nzbUrl, title).Should().BeTrue();
Mocker.GetMock<HttpProvider>().Verify(c => c.DownloadFile(It.IsAny<string>(), It.IsAny<string>()), Times.Never());
}
[Test]
public void should_return_false_on_failed_download()
{
WithFailedDownload();
Mocker.Resolve<PneumaticProvider>().DownloadNzb(nzbUrl, title).Should().BeFalse();
ExceptionVerification.ExpectedWarns(1);
}
[Test]
public void should_skip_if_full_season_download()
{
Mocker.Resolve<PneumaticProvider>().DownloadNzb(nzbUrl, "30 Rock - Season 1").Should().BeFalse();
ExceptionVerification.ExpectedErrors(1);
}
}
}

@ -13,7 +13,7 @@ namespace NzbDrone.Core.Test.ProviderTests.PostDownloadProviderTests
{ {
[TestFixture] [TestFixture]
// ReSharper disable InconsistentNaming // ReSharper disable InconsistentNaming
public class PostDownloadProviderFixture : CoreTest public class GetFolderNameWithStatusFixture : CoreTest
{ {
[TestCase(@"c:\_NzbDrone_InvalidEpisode_Title", @"c:\_UnknownSeries_Title", PostDownloadStatusType.UnknownSeries)] [TestCase(@"c:\_NzbDrone_InvalidEpisode_Title", @"c:\_UnknownSeries_Title", PostDownloadStatusType.UnknownSeries)]
[TestCase(@"c:\Title", @"c:\_Failed_Title", PostDownloadStatusType.Failed)] [TestCase(@"c:\Title", @"c:\_Failed_Title", PostDownloadStatusType.Failed)]

@ -18,7 +18,7 @@ using NzbDrone.Test.Common.AutoMoq;
namespace NzbDrone.Core.Test.ProviderTests.PostDownloadProviderTests namespace NzbDrone.Core.Test.ProviderTests.PostDownloadProviderTests
{ {
[TestFixture] [TestFixture]
public class ProcessDownloadProviderFixture : CoreTest public class ProcessDownloadFixture : CoreTest
{ {
Series fakeSeries; Series fakeSeries;
@ -328,51 +328,7 @@ namespace NzbDrone.Core.Test.ProviderTests.PostDownloadProviderTests
} }
[Test] [Test]
public void ProcessDropFolder_should_only_process_folders_that_arent_known_series_folders() public void should_logError_and_return_if_size_exceeds_free_space()
{
WithLotsOfFreeDiskSpace();
var subFolders = new[]
{
@"c:\drop\episode1",
@"c:\drop\episode2",
@"c:\drop\episode3",
@"c:\drop\episode4"
};
Mocker.GetMock<DiskProvider>()
.Setup(c => c.GetDirectories(It.IsAny<String>()))
.Returns(subFolders);
Mocker.GetMock<SeriesProvider>()
.Setup(c => c.SeriesPathExists(subFolders[1]))
.Returns(true);
Mocker.GetMock<SeriesProvider>()
.Setup(c => c.FindSeries(It.IsAny<String>()))
.Returns(fakeSeries);
Mocker.GetMock<DiskScanProvider>()
.Setup(c => c.Scan(It.IsAny<Series>(), It.IsAny<String>()))
.Returns(new List<EpisodeFile>());
Mocker.GetMock<DiskProvider>()
.Setup(c => c.GetDirectorySize(It.IsAny<String>()))
.Returns(10);
//Act
Mocker.Resolve<PostDownloadProvider>().ProcessDropFolder(@"C:\drop\");
//Assert
Mocker.GetMock<DiskScanProvider>().Verify(c => c.Scan(It.IsAny<Series>(), subFolders[0]), Times.Once());
Mocker.GetMock<DiskScanProvider>().Verify(c => c.Scan(It.IsAny<Series>(), subFolders[1]), Times.Never());
Mocker.GetMock<DiskScanProvider>().Verify(c => c.Scan(It.IsAny<Series>(), subFolders[2]), Times.Once());
Mocker.GetMock<DiskScanProvider>().Verify(c => c.Scan(It.IsAny<Series>(), subFolders[3]), Times.Once());
}
[Test]
public void ProcessDownload_should_logError_and_return_if_size_exceeds_free_space()
{ {
var downloadName = new DirectoryInfo(@"C:\Test\Drop\30.Rock.S01E01.Pilot"); var downloadName = new DirectoryInfo(@"C:\Test\Drop\30.Rock.S01E01.Pilot");
@ -403,7 +359,7 @@ namespace NzbDrone.Core.Test.ProviderTests.PostDownloadProviderTests
} }
[Test] [Test]
public void ProcessDownload_should_process_if_free_disk_space_exceeds_size() public void should_process_if_free_disk_space_exceeds_size()
{ {
WithLotsOfFreeDiskSpace(); WithLotsOfFreeDiskSpace();
WithValidSeries(); WithValidSeries();
@ -429,7 +385,7 @@ namespace NzbDrone.Core.Test.ProviderTests.PostDownloadProviderTests
} }
[Test] [Test]
public void ProcessDownload_should_process_if_free_disk_space_equals_size() public void should_process_if_free_disk_space_equals_size()
{ {
var downloadName = new DirectoryInfo(@"C:\Test\Drop\30.Rock.S01E01.Pilot"); var downloadName = new DirectoryInfo(@"C:\Test\Drop\30.Rock.S01E01.Pilot");

@ -0,0 +1,123 @@
// ReSharper disable InconsistentNaming
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using FizzWare.NBuilder;
using Moq;
using NUnit.Framework;
using NzbDrone.Common;
using NzbDrone.Core.Model;
using NzbDrone.Core.Providers;
using NzbDrone.Core.Repository;
using NzbDrone.Core.Test.Framework;
using NzbDrone.Test.Common;
using NzbDrone.Test.Common.AutoMoq;
namespace NzbDrone.Core.Test.ProviderTests.PostDownloadProviderTests
{
[TestFixture]
public class ProcessDropDirectoryFixture : CoreTest
{
Series fakeSeries;
[SetUp]
public void Setup()
{
fakeSeries = Builder<Series>.CreateNew()
.With(s => s.Path = @"C:\Test\TV\30 Rock")
.Build();
}
private void WithLotsOfFreeDiskSpace()
{
Mocker.GetMock<DiskProvider>().Setup(s => s.FreeDiskSpace(It.IsAny<DirectoryInfo>())).Returns(1000000000);
}
[Test]
public void ProcessDropFolder_should_only_process_folders_that_arent_known_series_folders()
{
WithLotsOfFreeDiskSpace();
var subFolders = new[]
{
@"c:\drop\episode1",
@"c:\drop\episode2",
@"c:\drop\episode3",
@"c:\drop\episode4"
};
Mocker.GetMock<DiskScanProvider>()
.Setup(c => c.GetVideoFiles(It.IsAny<String>(), false))
.Returns(new List<String>());
Mocker.GetMock<DiskProvider>()
.Setup(c => c.GetDirectories(It.IsAny<String>()))
.Returns(subFolders);
Mocker.GetMock<SeriesProvider>()
.Setup(c => c.SeriesPathExists(subFolders[1]))
.Returns(true);
Mocker.GetMock<SeriesProvider>()
.Setup(c => c.FindSeries(It.IsAny<String>()))
.Returns(fakeSeries);
Mocker.GetMock<DiskScanProvider>()
.Setup(c => c.Scan(It.IsAny<Series>(), It.IsAny<String>()))
.Returns(new List<EpisodeFile>());
Mocker.GetMock<DiskProvider>()
.Setup(c => c.GetDirectorySize(It.IsAny<String>()))
.Returns(10);
//Act
Mocker.Resolve<PostDownloadProvider>().ProcessDropFolder(@"C:\drop\");
//Assert
Mocker.GetMock<DiskScanProvider>().Verify(c => c.Scan(It.IsAny<Series>(), subFolders[0]), Times.Once());
Mocker.GetMock<DiskScanProvider>().Verify(c => c.Scan(It.IsAny<Series>(), subFolders[1]), Times.Never());
Mocker.GetMock<DiskScanProvider>().Verify(c => c.Scan(It.IsAny<Series>(), subFolders[2]), Times.Once());
Mocker.GetMock<DiskScanProvider>().Verify(c => c.Scan(It.IsAny<Series>(), subFolders[3]), Times.Once());
}
[Test]
public void ProcessDropFolder_should_process_individual_video_files_in_drop_folder()
{
WithLotsOfFreeDiskSpace();
var files = new List<String>
{
@"c:\drop\30 Rock - episode1.avi",
@"c:\drop\30 Rock - episode2.mkv",
@"c:\drop\30 Rock - episode3.mp4",
@"c:\drop\30 Rock - episode4.wmv"
};
Mocker.GetMock<DiskScanProvider>()
.Setup(c => c.GetVideoFiles(It.IsAny<String>(), false))
.Returns(files);
Mocker.GetMock<SeriesProvider>()
.Setup(c => c.FindSeries(It.IsAny<String>()))
.Returns(fakeSeries);
Mocker.GetMock<DiskScanProvider>()
.Setup(c => c.Scan(It.IsAny<Series>(), It.IsAny<String>()))
.Returns(new List<EpisodeFile>());
Mocker.GetMock<DiskProvider>()
.Setup(c => c.GetDirectorySize(It.IsAny<String>()))
.Returns(10);
//Act
Mocker.Resolve<PostDownloadProvider>().ProcessDropFolder(@"C:\drop\");
//Assert
Mocker.GetMock<DiskScanProvider>().Verify(c => c.ImportFile(It.IsAny<Series>(), It.IsAny<String>()), Times.Exactly(4));
}
}
}

@ -0,0 +1,215 @@
// ReSharper disable InconsistentNaming
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using FizzWare.NBuilder;
using Moq;
using NUnit.Framework;
using NzbDrone.Common;
using NzbDrone.Core.Model;
using NzbDrone.Core.Providers;
using NzbDrone.Core.Repository;
using NzbDrone.Core.Test.Framework;
using NzbDrone.Test.Common;
using NzbDrone.Test.Common.AutoMoq;
namespace NzbDrone.Core.Test.ProviderTests.PostDownloadProviderTests
{
[TestFixture]
public class ProcessVideoFileFixture : CoreTest
{
Series fakeSeries;
[SetUp]
public void Setup()
{
fakeSeries = Builder<Series>.CreateNew()
.With(s => s.Path = @"C:\Test\TV\30 Rock")
.Build();
}
private void WithOldWrite()
{
Mocker.GetMock<DiskProvider>()
.Setup(c => c.GetLastFileWrite(It.IsAny<String>()))
.Returns(DateTime.Now.AddDays(-5));
}
private void WithRecentWrite()
{
Mocker.GetMock<DiskProvider>()
.Setup(c => c.GetLastFileWrite(It.IsAny<String>()))
.Returns(DateTime.UtcNow);
}
private void WithValidSeries()
{
Mocker.GetMock<SeriesProvider>()
.Setup(c => c.FindSeries(It.IsAny<string>()))
.Returns(fakeSeries);
}
private void WithImportableFiles()
{
Mocker.GetMock<DiskScanProvider>()
.Setup(c => c.Scan(It.IsAny<Series>(), It.IsAny<string>()))
.Returns(Builder<EpisodeFile>.CreateListOfSize(1).Build().ToList());
}
private void WithLotsOfFreeDiskSpace()
{
Mocker.GetMock<DiskProvider>().Setup(s => s.FreeDiskSpace(It.IsAny<DirectoryInfo>())).Returns(1000000000);
}
private void WithImportedFile(string file)
{
var fakeEpisodeFile = Builder<EpisodeFile>.CreateNew()
.With(f => f.SeriesId = fakeSeries.SeriesId)
.Build();
Mocker.GetMock<DiskScanProvider>().Setup(s => s.ImportFile(fakeSeries, file)).Returns(fakeEpisodeFile);
}
[Test]
public void should_skip_if_and_too_fresh()
{
WithStrictMocker();
WithRecentWrite();
var file = Path.Combine(TempFolder, "test.avi");
Mocker.Resolve<PostDownloadProvider>().ProcessVideoFile(file);
}
[Test]
public void should_continue_processing_if_not_fresh()
{
WithOldWrite();
var file = Path.Combine(TempFolder, "test.avi");
//Act
Mocker.GetMock<SeriesProvider>().Setup(s => s.FindSeries(It.IsAny<String>())).Returns<Series>(null).Verifiable();
Mocker.Resolve<PostDownloadProvider>().ProcessVideoFile(file);
//Assert
Mocker.GetMock<SeriesProvider>().Verify(s => s.FindSeries(It.IsAny<String>()), Times.Once());
ExceptionVerification.IgnoreWarns();
}
[Test]
public void should_return_if_series_is_not_found()
{
WithOldWrite();
var file = Path.Combine(TempFolder, "test.avi");
//Act
Mocker.GetMock<SeriesProvider>().Setup(s => s.FindSeries(It.IsAny<String>())).Returns<Series>(null);
Mocker.Resolve<PostDownloadProvider>().ProcessVideoFile(file);
//Assert
Mocker.GetMock<DiskProvider>().Verify(s => s.GetFileSize(It.IsAny<String>()), Times.Never());
ExceptionVerification.IgnoreWarns();
}
[Test]
public void should_move_file_if_imported()
{
WithLotsOfFreeDiskSpace();
WithOldWrite();
var file = Path.Combine(TempFolder, "test.avi");
WithValidSeries();
WithImportedFile(file);
//Act
Mocker.Resolve<PostDownloadProvider>().ProcessVideoFile(file);
//Assert
Mocker.GetMock<DiskScanProvider>().Verify(s => s.MoveEpisodeFile(It.IsAny<EpisodeFile>(), true), Times.Once());
ExceptionVerification.IgnoreWarns();
}
[Test]
public void should_logError_and_return_if_size_exceeds_free_space()
{
var downloadName = @"C:\Test\Drop\30.Rock.S01E01.Pilot.mkv";
var series = Builder<Series>.CreateNew()
.With(s => s.Title = "30 Rock")
.With(s => s.Path = @"C:\Test\TV\30 Rock")
.Build();
Mocker.GetMock<SeriesProvider>()
.Setup(c => c.FindSeries("rock"))
.Returns(series);
Mocker.GetMock<DiskProvider>()
.Setup(s => s.GetFileSize(downloadName))
.Returns(10);
Mocker.GetMock<DiskProvider>()
.Setup(s => s.FreeDiskSpace(new DirectoryInfo(series.Path)))
.Returns(9);
//Act
Mocker.Resolve<PostDownloadProvider>().ProcessVideoFile(downloadName);
//Assert
Mocker.GetMock<DiskScanProvider>().Verify(c => c.ImportFile(series, downloadName), Times.Never());
ExceptionVerification.ExpectedErrors(1);
}
[Test]
public void should_process_if_free_disk_space_exceeds_size()
{
WithLotsOfFreeDiskSpace();
WithValidSeries();
var downloadName = @"C:\Test\Drop\30.Rock.S01E01.Pilot.mkv";
Mocker.GetMock<SeriesProvider>()
.Setup(c => c.FindSeries("rock"))
.Returns(fakeSeries);
Mocker.GetMock<DiskProvider>()
.Setup(s => s.GetFileSize(downloadName))
.Returns(8);
//Act
Mocker.Resolve<PostDownloadProvider>().ProcessVideoFile(downloadName);
//Assert
Mocker.GetMock<DiskScanProvider>().Verify(c => c.ImportFile(fakeSeries, downloadName), Times.Once());
}
[Test]
public void should_process_if_free_disk_space_equals_size()
{
var downloadName = @"C:\Test\Drop\30.Rock.S01E01.Pilot.mkv";
WithValidSeries();
Mocker.GetMock<DiskProvider>()
.Setup(s => s.GetDirectorySize(downloadName))
.Returns(10);
Mocker.GetMock<DiskProvider>()
.Setup(s => s.FreeDiskSpace(It.IsAny<DirectoryInfo>()))
.Returns(10);
//Act
Mocker.Resolve<PostDownloadProvider>().ProcessVideoFile(downloadName);
//Assert
Mocker.GetMock<DiskScanProvider>().Verify(c => c.ImportFile(fakeSeries, downloadName), Times.Once());
}
}
}

@ -3,6 +3,7 @@
public enum DownloadClientType public enum DownloadClientType
{ {
Sabnzbd = 0, Sabnzbd = 0,
Blackhole = 1 Blackhole = 1,
Pneumatic = 2
} }
} }

@ -289,6 +289,7 @@
<Compile Include="Model\Xbmc\IconType.cs" /> <Compile Include="Model\Xbmc\IconType.cs" />
<Compile Include="Providers\BannerProvider.cs" /> <Compile Include="Providers\BannerProvider.cs" />
<Compile Include="Providers\DecisionEngine\AllowedReleaseGroupSpecification.cs" /> <Compile Include="Providers\DecisionEngine\AllowedReleaseGroupSpecification.cs" />
<Compile Include="Providers\DownloadClients\PneumaticProvider.cs" />
<Compile Include="Providers\Indexer\NzbClub.cs" /> <Compile Include="Providers\Indexer\NzbClub.cs" />
<Compile Include="Providers\Indexer\NzbIndex.cs" /> <Compile Include="Providers\Indexer\NzbIndex.cs" />
<Compile Include="Providers\Indexer\FileSharingTalk.cs" /> <Compile Include="Providers\Indexer\FileSharingTalk.cs" />

@ -520,6 +520,12 @@ namespace NzbDrone.Core.Providers.Core
set { SetValue("AllowedReleaseGroups", value); } set { SetValue("AllowedReleaseGroups", value); }
} }
public virtual string PneumaticDirectory
{
get { return GetValue("PneumaticDirectory", String.Empty); }
set { SetValue("PneumaticDirectory", value); }
}
private string GetValue(string key) private string GetValue(string key)
{ {
return GetValue(key, String.Empty); return GetValue(key, String.Empty);

@ -284,11 +284,12 @@ namespace NzbDrone.Core.Providers
} }
} }
private List<string> GetVideoFiles(string path) public virtual List<string> GetVideoFiles(string path, bool allDirectories = true)
{ {
Logger.Debug("Scanning '{0}' for video files", path); Logger.Debug("Scanning '{0}' for video files", path);
var filesOnDisk = _diskProvider.GetFiles(path, SearchOption.AllDirectories); var searchOption = allDirectories ? SearchOption.AllDirectories : SearchOption.TopDirectoryOnly;
var filesOnDisk = _diskProvider.GetFiles(path, searchOption);
var mediaFileList = filesOnDisk.Where(c => mediaExtentions.Contains(Path.GetExtension(c).ToLower())).ToList(); var mediaFileList = filesOnDisk.Where(c => mediaExtentions.Contains(Path.GetExtension(c).ToLower())).ToList();

@ -0,0 +1,79 @@
using System;
using System.IO;
using System.Linq;
using NLog;
using Ninject;
using NzbDrone.Common;
using NzbDrone.Core.Model;
using NzbDrone.Core.Providers.Core;
using NzbDrone.Core.Providers.DecisionEngine;
namespace NzbDrone.Core.Providers.DownloadClients
{
public class PneumaticProvider : IDownloadClient
{
private readonly ConfigProvider _configProvider;
private readonly HttpProvider _httpProvider;
private readonly DiskProvider _diskProvider;
private readonly UpgradeHistorySpecification _upgradeHistorySpecification;
private static readonly Logger logger = LogManager.GetCurrentClassLogger();
[Inject]
public PneumaticProvider(ConfigProvider configProvider, HttpProvider httpProvider,
DiskProvider diskProvider, UpgradeHistorySpecification upgradeHistorySpecification)
{
_configProvider = configProvider;
_httpProvider = httpProvider;
_diskProvider = diskProvider;
_upgradeHistorySpecification = upgradeHistorySpecification;
}
public PneumaticProvider()
{
}
public virtual bool DownloadNzb(string url, string title)
{
try
{
//Todo: Allow full season releases
if (Parser.ParseTitle(title).FullSeason)
{
logger.Warn("Skipping Full Season Release: {0}", title);
return false;
}
//Save to the Pneumatic directory (The user will need to ensure its accessible by XBMC)
var filename = Path.Combine(_configProvider.PneumaticDirectory, title + ".nzb");
if (_diskProvider.FileExists(filename))
{
//Return true so a lesser quality is not returned.
logger.Info("NZB already exists on disk: {0}", filename);
return true;
}
logger.Trace("Downloading NZB from: {0} to: {1}", url, filename);
_httpProvider.DownloadFile(url, filename);
logger.Trace("NZB Download succeeded, saved to: {0}", filename);
var contents = String.Format("plugin://plugin.program.pneumatic/?mode=strm&type=add_file&nzb={0}&nzbname={1}", filename, title);
_diskProvider.WriteAllText(Path.Combine(_configProvider.SabDropDirectory, title + ".strm"), contents);
return true;
}
catch (Exception ex)
{
logger.WarnException("Failed to download NZB: " + url, ex);
return false;
}
}
public virtual bool IsInQueue(EpisodeParseResult newParseResult)
{
return !_upgradeHistorySpecification.IsSatisfiedBy(newParseResult);
}
}
}

@ -18,6 +18,7 @@ namespace NzbDrone.Core.Providers
private readonly ConfigProvider _configProvider; private readonly ConfigProvider _configProvider;
private readonly BlackholeProvider _blackholeProvider; private readonly BlackholeProvider _blackholeProvider;
private readonly SignalRProvider _signalRProvider; private readonly SignalRProvider _signalRProvider;
private readonly PneumaticProvider _pneumaticProvider;
private static readonly Logger logger = LogManager.GetCurrentClassLogger(); private static readonly Logger logger = LogManager.GetCurrentClassLogger();
@ -25,7 +26,7 @@ namespace NzbDrone.Core.Providers
public DownloadProvider(SabProvider sabProvider, HistoryProvider historyProvider, public DownloadProvider(SabProvider sabProvider, HistoryProvider historyProvider,
EpisodeProvider episodeProvider, ExternalNotificationProvider externalNotificationProvider, EpisodeProvider episodeProvider, ExternalNotificationProvider externalNotificationProvider,
ConfigProvider configProvider, BlackholeProvider blackholeProvider, ConfigProvider configProvider, BlackholeProvider blackholeProvider,
SignalRProvider signalRProvider) SignalRProvider signalRProvider, PneumaticProvider pneumaticProvider)
{ {
_sabProvider = sabProvider; _sabProvider = sabProvider;
_historyProvider = historyProvider; _historyProvider = historyProvider;
@ -34,6 +35,7 @@ namespace NzbDrone.Core.Providers
_configProvider = configProvider; _configProvider = configProvider;
_blackholeProvider = blackholeProvider; _blackholeProvider = blackholeProvider;
_signalRProvider = signalRProvider; _signalRProvider = signalRProvider;
_pneumaticProvider = pneumaticProvider;
} }
public DownloadProvider() public DownloadProvider()
@ -54,16 +56,18 @@ namespace NzbDrone.Core.Providers
foreach (var episode in _episodeProvider.GetEpisodesByParseResult(parseResult)) foreach (var episode in _episodeProvider.GetEpisodesByParseResult(parseResult))
{ {
var history = new History(); var history = new History
history.Date = DateTime.Now; {
history.Indexer = parseResult.Indexer; Date = DateTime.Now,
history.IsProper = parseResult.Quality.Proper; Indexer = parseResult.Indexer,
history.Quality = parseResult.Quality.QualityType; IsProper = parseResult.Quality.Proper,
history.NzbTitle = parseResult.OriginalString; Quality = parseResult.Quality.QualityType,
history.EpisodeId = episode.EpisodeId; NzbTitle = parseResult.OriginalString,
history.SeriesId = episode.SeriesId; EpisodeId = episode.EpisodeId,
history.NzbInfoUrl = parseResult.NzbInfoUrl; SeriesId = episode.SeriesId,
history.ReleaseGroup = parseResult.ReleaseGroup; NzbInfoUrl = parseResult.NzbInfoUrl,
ReleaseGroup = parseResult.ReleaseGroup,
};
_historyProvider.Add(history); _historyProvider.Add(history);
_episodeProvider.MarkEpisodeAsFetched(episode.EpisodeId); _episodeProvider.MarkEpisodeAsFetched(episode.EpisodeId);
@ -77,13 +81,16 @@ namespace NzbDrone.Core.Providers
return success; return success;
} }
public virtual IDownloadClient GetActiveDownloadClient() public virtual IDownloadClient GetActiveDownloadClient()
{ {
switch (_configProvider.DownloadClient) switch (_configProvider.DownloadClient)
{ {
case DownloadClientType.Blackhole: case DownloadClientType.Blackhole:
return _blackholeProvider; return _blackholeProvider;
case DownloadClientType.Pneumatic:
return _pneumaticProvider;
default: default:
return _sabProvider; return _sabProvider;
} }

@ -49,6 +49,18 @@ namespace NzbDrone.Core.Providers
Logger.ErrorException("An error has occurred while importing folder" + subfolder, e); Logger.ErrorException("An error has occurred while importing folder" + subfolder, e);
} }
} }
foreach(var videoFile in _diskScanProvider.GetVideoFiles(dropFolder, false))
{
try
{
ProcessVideoFile(videoFile);
}
catch(Exception ex)
{
Logger.ErrorException("An error has occurred while importing video file" + videoFile, ex);
}
}
} }
public virtual void ProcessDownload(DirectoryInfo subfolderInfo) public virtual void ProcessDownload(DirectoryInfo subfolderInfo)
@ -109,6 +121,40 @@ namespace NzbDrone.Core.Providers
} }
} }
public virtual void ProcessVideoFile(string videoFile)
{
if (_diskProvider.GetLastFileWrite(videoFile).AddMinutes(2) > DateTime.UtcNow)
{
Logger.Trace("[{0}] is too fresh. skipping", videoFile);
return;
}
var seriesName = Parser.ParseSeriesName(Path.GetFileNameWithoutExtension(videoFile));
var series = _seriesProvider.FindSeries(seriesName);
if (series == null)
{
Logger.Trace("Unknown Series on Import: {0}", videoFile);
return;
}
var size = _diskProvider.GetFileSize(videoFile);
var freeSpace = _diskProvider.FreeDiskSpace(new DirectoryInfo(series.Path));
if (Convert.ToUInt64(size) > freeSpace)
{
Logger.Error("Not enough free disk space for series: {0}, {1}", series.Title, series.Path);
return;
}
var episodeFile = _diskScanProvider.ImportFile(series, videoFile);
if (episodeFile != null)
{
_diskScanProvider.MoveEpisodeFile(episodeFile, true);
_metadataProvider.CreateForEpisodeFile(episodeFile);
}
}
private void TagFolder(DirectoryInfo directory, PostDownloadStatusType status) private void TagFolder(DirectoryInfo directory, PostDownloadStatusType status)
{ {
//Turning off tagging folder for now, to stop messing people's series folders. //Turning off tagging folder for now, to stop messing people's series folders.

@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Web.Mvc; using System.Web.Mvc;
using NLog;
using NzbDrone.Common; using NzbDrone.Common;
using NzbDrone.Core.Jobs; using NzbDrone.Core.Jobs;
using NzbDrone.Core.Providers; using NzbDrone.Core.Providers;
@ -23,6 +24,8 @@ namespace NzbDrone.Web.Controllers
private readonly TvDbProvider _tvDbProvider; private readonly TvDbProvider _tvDbProvider;
private readonly DiskProvider _diskProvider; private readonly DiskProvider _diskProvider;
private static readonly Logger logger = LogManager.GetCurrentClassLogger();
public AddSeriesController(RootDirProvider rootFolderProvider, public AddSeriesController(RootDirProvider rootFolderProvider,
ConfigProvider configProvider, ConfigProvider configProvider,
QualityProvider qualityProvider, TvDbProvider tvDbProvider, QualityProvider qualityProvider, TvDbProvider tvDbProvider,
@ -83,17 +86,26 @@ namespace NzbDrone.Web.Controllers
foreach (var folder in unmappedList) foreach (var folder in unmappedList)
{ {
var foldername = new DirectoryInfo(folder).Name; var foldername = new DirectoryInfo(folder).Name;
var tvdbResult = _tvDbProvider.SearchSeries(foldername).FirstOrDefault();
var title = String.Empty; try
var seriesId = 0;
if (tvdbResult != null)
{ {
title = tvdbResult.SeriesName; var tvdbResult = _tvDbProvider.SearchSeries(foldername).FirstOrDefault();
seriesId = tvdbResult.Id;
} var title = String.Empty;
var seriesId = 0;
if (tvdbResult != null)
{
title = tvdbResult.SeriesName;
seriesId = tvdbResult.Id;
}
result.ExistingSeries.Add(new Tuple<string, string, int>(folder, title, seriesId)); result.ExistingSeries.Add(new Tuple<string, string, int>(folder, title, seriesId));
}
catch(Exception ex)
{
logger.WarnException("Failed to connect to TheTVDB to search for: " + foldername, ex);
return View();
}
} }
var defaultQuality = Convert.ToInt32(_configProvider.DefaultQualityProfile); var defaultQuality = Convert.ToInt32(_configProvider.DefaultQualityProfile);

@ -111,7 +111,8 @@ namespace NzbDrone.Web.Controllers
SabTvCategorySelectList = tvCategorySelectList, SabTvCategorySelectList = tvCategorySelectList,
DownloadClient = (int)_configProvider.DownloadClient, DownloadClient = (int)_configProvider.DownloadClient,
BlackholeDirectory = _configProvider.BlackholeDirectory, BlackholeDirectory = _configProvider.BlackholeDirectory,
DownloadClientSelectList = new SelectList(downloadClientTypes, "Key", "Value") DownloadClientSelectList = new SelectList(downloadClientTypes, "Key", "Value"),
PneumaticDirectory = _configProvider.PneumaticDirectory
}; };
return View(model); return View(model);
@ -430,6 +431,7 @@ namespace NzbDrone.Web.Controllers
_configProvider.SabDropDirectory = data.DownloadClientDropDirectory; _configProvider.SabDropDirectory = data.DownloadClientDropDirectory;
_configProvider.BlackholeDirectory = data.BlackholeDirectory; _configProvider.BlackholeDirectory = data.BlackholeDirectory;
_configProvider.DownloadClient = (DownloadClientType)data.DownloadClient; _configProvider.DownloadClient = (DownloadClientType)data.DownloadClient;
_configProvider.PneumaticDirectory = data.PneumaticDirectory;
return GetSuccessResult(); return GetSuccessResult();
} }

@ -70,6 +70,12 @@ namespace NzbDrone.Web.Models
[Description("What method do you download NZBs with?")] [Description("What method do you download NZBs with?")]
public int DownloadClient { get; set; } public int DownloadClient { get; set; }
[DisplayName("Pneumatic Nzb Directory")]
[Description("Directory to save NZBs for Pneumatic, must be able from XBMC")]
[DisplayFormat(ConvertEmptyStringToNull = false)]
[RequiredIf("DownloadClient", (int)DownloadClientType.Pneumatic, ErrorMessage = "Required when Download Client is Blackhole")]
public string PneumaticDirectory { get; set; }
public SelectList SabTvCategorySelectList { get; set; } public SelectList SabTvCategorySelectList { get; set; }
public SelectList DownloadClientSelectList { get; set; } public SelectList DownloadClientSelectList { get; set; }
} }

@ -554,6 +554,9 @@
<ItemGroup> <ItemGroup>
<Content Include="Views\Shared\_MINIPROFILER UPDATED Layout.cshtml" /> <Content Include="Views\Shared\_MINIPROFILER UPDATED Layout.cshtml" />
</ItemGroup> </ItemGroup>
<ItemGroup>
<Content Include="Views\Settings\Pneumatic.cshtml" />
</ItemGroup>
<PropertyGroup> <PropertyGroup>
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">10.0</VisualStudioVersion> <VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">10.0</VisualStudioVersion>
<VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath> <VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>

@ -6,7 +6,14 @@
Layout = null; Layout = null;
} }
@if (Model.ExistingSeries.Count == 0) @if (Model == null)
{
<h2 style="color: tomato">
Error searching TheTVDB, please try again later.
</h2>
}
else if (!Model.ExistingSeries.Any())
{ {
<h2 style="color: tomato"> <h2 style="color: tomato">
No series available. Try adding a new Root Folder. No series available. Try adding a new Root Folder.

@ -41,12 +41,14 @@
</div> </div>
<div class="jquery-accordion" id="downloadClientAccordion"> <div class="jquery-accordion" id="downloadClientAccordion">
<h3> <h3><a href="#">Sabnzbd</a></h3>
<a href="#">Sabnzbd</a></h3>
@{Html.RenderPartial("Sabnzbd", Model);} @{Html.RenderPartial("Sabnzbd", Model);}
<h3>
<a href="#">Blackhole</a></h3> <h3><a href="#">Blackhole</a></h3>
@{Html.RenderPartial("Blackhole", Model);} @{Html.RenderPartial("Blackhole", Model);}
<h3><a href="#">Pneumatic</a></h3>
@{Html.RenderPartial("Pneumatic", Model);}
</div> </div>
<button type="submit" class="save_button" disabled="disabled"> <button type="submit" class="save_button" disabled="disabled">

@ -0,0 +1,14 @@
@using NzbDrone.Web.Helpers;
@model NzbDrone.Web.Models.DownloadClientSettingsModel
@{
Layout = null;
}
<div class="downloadClient">
<label class="labelClass">@Html.LabelFor(m => m.PneumaticDirectory)
<span class="small">@Html.DescriptionFor(m => m.PneumaticDirectory)</span>
<span class="small">@Html.ValidationMessageFor(m => m.PneumaticDirectory)</span>
</label>
@Html.TextBoxFor(m => m.PneumaticDirectory, new { @class = "inputClass folderLookup" })
</div>
Loading…
Cancel
Save