1
0
mirror of https://github.com/Sonarr/Sonarr.git synced 2026-03-24 17:24:38 -04:00

Compare commits

..

2 Commits

Author SHA1 Message Date
Taloth Saldono
97c3863efb Temporary mock data while services isn't updated with new endpoint. 2017-05-13 00:19:34 +02:00
Taloth Saldono
7b4cb4145d Ability to blacklist and rename indexer urls via services. 2017-05-13 00:19:34 +02:00
223 changed files with 1911 additions and 3955 deletions

View File

@@ -25,7 +25,7 @@ gulp.task('copyHtml', function () {
});
gulp.task('copyContent', function () {
return gulp.src([paths.src.content + '**/*.*', '!**/*.less', '!**/*.css'])
return gulp.src([paths.src.content + '**/*.*', '!**/*.less'])
.pipe(gulp.dest(paths.dest.content))
.pipe(livereload());
});

View File

@@ -16,10 +16,6 @@ gulp.task('less', function() {
paths.src.content + 'bootstrap.less',
paths.src.content + 'theme.less',
paths.src.content + 'overrides.less',
paths.src.content + 'bootstrap.toggle-switch.css',
paths.src.content + 'fullcalendar.css',
paths.src.content + 'Messenger/messenger.css',
paths.src.content + 'Messenger/messenger.flat.css',
paths.src.root + 'Series/series.less',
paths.src.root + 'Activity/activity.less',
paths.src.root + 'AddSeries/addSeries.less',

View File

@@ -64,8 +64,6 @@ namespace NzbDrone.Api.Authentication
new DefaultHmacProvider(new PassphraseKeyGenerator(_configService.HmacPassphrase, Encoding.ASCII.GetBytes(_configService.HmacSalt)))
);
FormsAuthentication.FormsAuthenticationCookieName = "_ncfa_sonarr";
FormsAuthentication.Enable(pipelines, new FormsAuthenticationConfiguration
{
RedirectUrl = _configFileProvider.UrlBase + "/login",

View File

@@ -1,13 +1,16 @@
using System.Collections.Generic;
using System.IO;
using NLog;
using NzbDrone.Api.REST;
using NzbDrone.Common.Disk;
using NzbDrone.Common.Extensions;
using NzbDrone.Core.Datastore.Events;
using NzbDrone.Core.MediaFiles;
using NzbDrone.Core.MediaFiles.Events;
using NzbDrone.Core.Messaging.Events;
using NzbDrone.Core.Tv;
using NzbDrone.Core.DecisionEngine;
using NzbDrone.Core.Exceptions;
using NzbDrone.SignalR;
using HttpStatusCode = System.Net.HttpStatusCode;
namespace NzbDrone.Api.EpisodeFiles
{
@@ -15,21 +18,27 @@ namespace NzbDrone.Api.EpisodeFiles
IHandle<EpisodeFileAddedEvent>
{
private readonly IMediaFileService _mediaFileService;
private readonly IDeleteMediaFiles _mediaFileDeletionService;
private readonly IDiskProvider _diskProvider;
private readonly IRecycleBinProvider _recycleBinProvider;
private readonly ISeriesService _seriesService;
private readonly IQualityUpgradableSpecification _qualityUpgradableSpecification;
private readonly Logger _logger;
public EpisodeFileModule(IBroadcastSignalRMessage signalRBroadcaster,
IMediaFileService mediaFileService,
IDeleteMediaFiles mediaFileDeletionService,
IDiskProvider diskProvider,
IRecycleBinProvider recycleBinProvider,
ISeriesService seriesService,
IQualityUpgradableSpecification qualityUpgradableSpecification)
IQualityUpgradableSpecification qualityUpgradableSpecification,
Logger logger)
: base(signalRBroadcaster)
{
_mediaFileService = mediaFileService;
_mediaFileDeletionService = mediaFileDeletionService;
_diskProvider = diskProvider;
_recycleBinProvider = recycleBinProvider;
_seriesService = seriesService;
_qualityUpgradableSpecification = qualityUpgradableSpecification;
_logger = logger;
GetResourceById = GetEpisodeFile;
GetResourceAll = GetEpisodeFiles;
UpdateResource = SetQuality;
@@ -68,15 +77,13 @@ namespace NzbDrone.Api.EpisodeFiles
private void DeleteEpisodeFile(int id)
{
var episodeFile = _mediaFileService.Get(id);
if (episodeFile == null)
{
throw new NzbDroneClientException(HttpStatusCode.NotFound, "Episode file not found");
}
var series = _seriesService.GetSeries(episodeFile.SeriesId);
var fullPath = Path.Combine(series.Path, episodeFile.RelativePath);
var subfolder = _diskProvider.GetParentFolder(series.Path).GetRelativePath(_diskProvider.GetParentFolder(fullPath));
_mediaFileDeletionService.DeleteEpisodeFile(series, episodeFile);
_logger.Info("Deleting episode file: {0}", fullPath);
_recycleBinProvider.DeleteFile(fullPath, subfolder);
_mediaFileService.Delete(episodeFile, DeleteMediaFileReason.Manual);
}
public void Handle(EpisodeFileAddedEvent message)

View File

@@ -14,7 +14,7 @@ namespace NzbDrone.Api.Episodes
{
public abstract class EpisodeModuleWithSignalR : NzbDroneRestModuleWithSignalR<EpisodeResource, Episode>,
IHandle<EpisodeGrabbedEvent>,
IHandle<EpisodeImportedEvent>
IHandle<EpisodeDownloadedEvent>
{
protected readonly IEpisodeService _episodeService;
protected readonly ISeriesService _seriesService;
@@ -115,14 +115,9 @@ namespace NzbDrone.Api.Episodes
}
}
public void Handle(EpisodeImportedEvent message)
public void Handle(EpisodeDownloadedEvent message)
{
if (!message.NewDownload)
{
return;
}
foreach (var episode in message.EpisodeInfo.Episodes)
foreach (var episode in message.Episode.Episodes)
{
BroadcastResourceChange(ModelAction.Updated, episode.Id);
}

View File

@@ -1,46 +0,0 @@
using System;
using Nancy;
using Nancy.Bootstrapper;
using Nancy.Responses;
using NzbDrone.Common.Extensions;
using NzbDrone.Core.Configuration;
namespace NzbDrone.Api.Extensions.Pipelines
{
public class UrlBasePipeline : IRegisterNancyPipeline
{
private readonly string _urlBase;
public UrlBasePipeline(IConfigFileProvider configFileProvider)
{
_urlBase = configFileProvider.UrlBase;
}
public int Order => 99;
public void Register(IPipelines pipelines)
{
if (_urlBase.IsNotNullOrWhiteSpace())
{
pipelines.BeforeRequest.AddItemToStartOfPipeline((Func<NancyContext, Response>) Handle);
}
}
private Response Handle(NancyContext context)
{
var basePath = context.Request.Url.BasePath;
if (basePath.IsNullOrWhiteSpace())
{
return new RedirectResponse($"{_urlBase}{context.Request.Path}{context.Request.Url.Query}");
}
if (_urlBase != basePath)
{
return new NotFoundResponse();
}
return null;
}
}
}

View File

@@ -1,4 +1,4 @@
using System;
using System;
using System.IO;
using System.Text.RegularExpressions;
using Nancy;
@@ -17,7 +17,7 @@ namespace NzbDrone.Api.Frontend.Mappers
private readonly IAnalyticsService _analyticsService;
private readonly Func<ICacheBreakerProvider> _cacheBreakProviderFactory;
private readonly string _indexPath;
private static readonly Regex ReplaceRegex = new Regex(@"(?:(?<attribute>href|src)=\"")(?<path>.*?(?<extension>css|js|png|ico|ics|svg))(?:\"")(?:\s(?<nohash>data-no-hash))?", RegexOptions.Compiled | RegexOptions.IgnoreCase);
private static readonly Regex ReplaceRegex = new Regex(@"(?:(?<attribute>href|src)=\"")(?<path>.*?(?<extension>css|js|png|ico|ics))(?:\"")(?:\s(?<nohash>data-no-hash))?", RegexOptions.Compiled | RegexOptions.IgnoreCase);
private static string API_KEY;
private static string URL_BASE;

View File

@@ -1,4 +1,4 @@
using System;
using System;
using System.Collections.Generic;
using System.Linq;
using Nancy.Responses;
@@ -38,6 +38,20 @@ namespace NzbDrone.Api.Frontend
return new NotFoundResponse();
}
//Redirect to the subfolder if the request went to the base URL
if (path.Equals("/"))
{
var urlBase = _configFileProvider.UrlBase;
if (!string.IsNullOrEmpty(urlBase))
{
if (Request.Url.BasePath != urlBase)
{
return new RedirectResponse(urlBase + "/");
}
}
}
var mapper = _requestMappers.SingleOrDefault(m => m.CanHandle(path));
if (mapper != null)

View File

@@ -53,7 +53,7 @@ namespace NzbDrone.Api.Indexers
private Response DownloadRelease(ReleaseResource release)
{
var remoteEpisode = _remoteEpisodeCache.Find(GetCacheKey(release));
var remoteEpisode = _remoteEpisodeCache.Find(release.Guid);
if (remoteEpisode == null)
{
@@ -68,7 +68,7 @@ namespace NzbDrone.Api.Indexers
}
catch (ReleaseDownloadException ex)
{
_logger.Error(ex, ex.Message);
_logger.Error(ex);
throw new NzbDroneClientException(HttpStatusCode.Conflict, "Getting release from indexer failed");
}
@@ -113,14 +113,8 @@ namespace NzbDrone.Api.Indexers
protected override ReleaseResource MapDecision(DownloadDecision decision, int initialWeight)
{
var resource = base.MapDecision(decision, initialWeight);
_remoteEpisodeCache.Set(GetCacheKey(resource), decision.RemoteEpisode, TimeSpan.FromMinutes(30));
return resource;
}
private string GetCacheKey(ReleaseResource resource)
{
return string.Concat(resource.IndexerId, "_", resource.Guid);
_remoteEpisodeCache.Set(decision.RemoteEpisode.Release.Guid, decision.RemoteEpisode, TimeSpan.FromMinutes(30));
return base.MapDecision(decision, initialWeight);
}
}
}

View File

@@ -106,7 +106,6 @@
<Compile Include="Commands\CommandResource.cs" />
<Compile Include="Extensions\AccessControlHeaders.cs" />
<Compile Include="Extensions\Pipelines\CorsPipeline.cs" />
<Compile Include="Extensions\Pipelines\UrlBasePipeline.cs" />
<Compile Include="Extensions\Pipelines\RequestLoggingPipeline.cs" />
<Compile Include="Frontend\Mappers\LoginHtmlMapper.cs" />
<Compile Include="Frontend\Mappers\RobotsTxtMapper.cs" />

View File

@@ -1,14 +1,13 @@
namespace NzbDrone.Common.Http
namespace NzbDrone.Common.Http
{
public enum HttpMethod
{
GET,
POST,
PUT,
DELETE,
POST,
HEAD,
OPTIONS,
DELETE,
PATCH,
MERGE
OPTIONS
}
}

View File

@@ -60,11 +60,6 @@ namespace NzbDrone.Common.Reflection
return (T)attribute;
}
public static T[] GetAttributes<T>(this MemberInfo member) where T : Attribute
{
return member.GetCustomAttributes(typeof(T), false).OfType<T>().ToArray();
}
public static Type FindTypeByName(this Assembly assembly, string name)
{
return assembly.GetTypes().SingleOrDefault(c => c.Name.Equals(name, StringComparison.InvariantCultureIgnoreCase));
@@ -75,4 +70,4 @@ namespace NzbDrone.Common.Reflection
return type.GetCustomAttributes(typeof(TAttribute), true).Any();
}
}
}
}

View File

@@ -1,7 +1,6 @@
using System;
using System.Net;
using NLog;
using NzbDrone.Common.EnvironmentInfo;
using NzbDrone.Common.Instrumentation;
namespace NzbDrone.Common.Security
@@ -15,12 +14,6 @@ namespace NzbDrone.Common.Security
public static void Register()
{
if (OsInfo.IsNotWindows)
{
// This was never meant to be used on mono, and will cause issues with mono 5 and higher if btls is enabled.
return;
}
try
{
// TODO: In v3 we should drop support for SSL3 because its very insecure. Only leaving it enabled because some people might rely on it.

View File

@@ -5,7 +5,6 @@ using FizzWare.NBuilder;
using FluentAssertions;
using Moq;
using NUnit.Framework;
using NzbDrone.Common.Extensions;
using NzbDrone.Core.DataAugmentation.Xem;
using NzbDrone.Core.DataAugmentation.Xem.Model;
using NzbDrone.Core.Test.Framework;
@@ -99,6 +98,7 @@ namespace NzbDrone.Core.Test.DataAugmentation.SceneNumbering
});
}
[Test]
public void should_not_fetch_scenenumbering_if_not_listed()
{
@@ -308,19 +308,5 @@ namespace NzbDrone.Core.Test.DataAugmentation.SceneNumbering
episode.SceneSeasonNumber.Should().NotHaveValue();
episode.SceneEpisodeNumber.Should().NotHaveValue();
}
[Test]
public void should_skip_mapping_when_scene_information_is_all_zero()
{
GivenTvdbMappings();
AddTvdbMapping(0, 0, 0, 8, 3, 1); // 3x01 -> 3x01
AddTvdbMapping(0, 0, 0, 9, 3, 2); // 3x02 -> 3x02
Subject.Handle(new SeriesUpdatedEvent(_series));
Mocker.GetMock<IEpisodeService>()
.Verify(v => v.UpdateEpisodes(It.Is<List<Episode>>(e => e.Any(c => c.SceneAbsoluteEpisodeNumber == 0 && c.SceneSeasonNumber == 0 && c.SceneEpisodeNumber == 0))), Times.Never());
}
}
}

View File

@@ -1,54 +0,0 @@
using System;
using System.Collections.Generic;
using FluentAssertions;
using NUnit.Framework;
using NzbDrone.Core.DecisionEngine;
using NzbDrone.Core.DecisionEngine.Specifications;
using NzbDrone.Core.Indexers;
using NzbDrone.Core.Parser.Model;
using NzbDrone.Core.Test.Framework;
namespace NzbDrone.Core.Test.DecisionEngineTests
{
[TestFixture]
public class BlockedIndexerSpecificationFixture : CoreTest<BlockedIndexerSpecification>
{
private RemoteEpisode _remoteEpisode;
[SetUp]
public void Setup()
{
_remoteEpisode = new RemoteEpisode
{
Release = new ReleaseInfo { IndexerId = 1 }
};
Mocker.GetMock<IIndexerStatusService>()
.Setup(v => v.GetBlockedProviders())
.Returns(new List<IndexerStatus>());
}
private void WithBlockedIndexer()
{
Mocker.GetMock<IIndexerStatusService>()
.Setup(v => v.GetBlockedProviders())
.Returns(new List<IndexerStatus> { new IndexerStatus { ProviderId = 1, DisabledTill = DateTime.UtcNow } });
}
[Test]
public void should_return_true_if_no_blocked_indexer()
{
Subject.IsSatisfiedBy(_remoteEpisode, null).Accepted.Should().BeTrue();
}
[Test]
public void should_return_false_if_blocked_indexer()
{
WithBlockedIndexer();
Subject.IsSatisfiedBy(_remoteEpisode, null).Accepted.Should().BeFalse();
Subject.Type.Should().Be(RejectionType.Temporary);
}
}
}

View File

@@ -1,111 +0,0 @@
using FizzWare.NBuilder;
using FluentAssertions;
using Moq;
using NUnit.Framework;
using NzbDrone.Core.Datastore;
using NzbDrone.Core.DecisionEngine.Specifications.Search;
using NzbDrone.Core.Indexers;
using NzbDrone.Core.Indexers.TorrentRss;
using NzbDrone.Core.IndexerSearch.Definitions;
using NzbDrone.Core.Parser.Model;
using NzbDrone.Core.Tv;
using NzbDrone.Test.Common;
namespace NzbDrone.Core.Test.DecisionEngineTests.Search
{
[TestFixture]
public class TorrentSeedingSpecificationFixture : TestBase<TorrentSeedingSpecification>
{
private Series _series;
private RemoteEpisode _remoteEpisode;
private IndexerDefinition _indexerDefinition;
[SetUp]
public void Setup()
{
_series = Builder<Series>.CreateNew().With(s => s.Id = 1).Build();
_remoteEpisode = new RemoteEpisode
{
Series = _series,
Release = new TorrentInfo
{
IndexerId = 1,
Title = "Series.Title.S01.720p.BluRay.X264-RlsGrp",
Seeders = 0
}
};
_indexerDefinition = new IndexerDefinition
{
Settings = new TorrentRssIndexerSettings { MinimumSeeders = 5 }
};
Mocker.GetMock<IIndexerFactory>()
.Setup(v => v.Get(1))
.Returns(_indexerDefinition);
}
private void GivenReleaseSeeders(int? seeders)
{
(_remoteEpisode.Release as TorrentInfo).Seeders = seeders;
}
[Test]
public void should_return_true_if_not_torrent()
{
_remoteEpisode.Release = new ReleaseInfo
{
IndexerId = 1,
Title = "Series.Title.S01.720p.BluRay.X264-RlsGrp"
};
Subject.IsSatisfiedBy(_remoteEpisode, null).Accepted.Should().BeTrue();
}
[Test]
public void should_return_true_if_indexer_not_specified()
{
_remoteEpisode.Release.IndexerId = 0;
Subject.IsSatisfiedBy(_remoteEpisode, null).Accepted.Should().BeTrue();
}
[Test]
public void should_return_true_if_indexer_no_longer_exists()
{
Mocker.GetMock<IIndexerFactory>()
.Setup(v => v.Get(It.IsAny<int>()))
.Callback<int>(i => { throw new ModelNotFoundException(typeof(IndexerDefinition), i); });
Subject.IsSatisfiedBy(_remoteEpisode, null).Accepted.Should().BeTrue();
}
[Test]
public void should_return_true_if_seeds_unknown()
{
GivenReleaseSeeders(null);
Subject.IsSatisfiedBy(_remoteEpisode, null).Accepted.Should().BeTrue();
}
[TestCase(5)]
[TestCase(6)]
public void should_return_true_if_seeds_above_or_equal_to_limit(int seeders)
{
GivenReleaseSeeders(seeders);
Subject.IsSatisfiedBy(_remoteEpisode, null).Accepted.Should().BeTrue();
}
[TestCase(0)]
[TestCase(4)]
public void should_return_false_if_seeds_belove_limit(int seeders)
{
GivenReleaseSeeders(seeders);
Subject.IsSatisfiedBy(_remoteEpisode, null).Accepted.Should().BeFalse();
}
}
}

View File

@@ -6,9 +6,7 @@ using Moq;
using NUnit.Framework;
using NzbDrone.Core.DecisionEngine;
using NzbDrone.Core.Download;
using NzbDrone.Core.Download.Clients;
using NzbDrone.Core.Download.Pending;
using NzbDrone.Core.Indexers;
using NzbDrone.Core.Parser.Model;
using NzbDrone.Core.Profiles;
using NzbDrone.Core.Qualities;
@@ -37,7 +35,7 @@ namespace NzbDrone.Core.Test.Download.DownloadApprovedReportsTests
.Build();
}
private RemoteEpisode GetRemoteEpisode(List<Episode> episodes, QualityModel quality, DownloadProtocol downloadProtocol = DownloadProtocol.Usenet)
private RemoteEpisode GetRemoteEpisode(List<Episode> episodes, QualityModel quality)
{
var remoteEpisode = new RemoteEpisode();
remoteEpisode.ParsedEpisodeInfo = new ParsedEpisodeInfo();
@@ -47,7 +45,6 @@ namespace NzbDrone.Core.Test.Download.DownloadApprovedReportsTests
remoteEpisode.Episodes.AddRange(episodes);
remoteEpisode.Release = new ReleaseInfo();
remoteEpisode.Release.DownloadProtocol = downloadProtocol;
remoteEpisode.Release.PublishDate = DateTime.UtcNow;
remoteEpisode.Series = Builder<Series>.CreateNew()
@@ -195,6 +192,7 @@ namespace NzbDrone.Core.Test.Download.DownloadApprovedReportsTests
var decisions = new List<DownloadDecision>();
decisions.Add(new DownloadDecision(remoteEpisode, new Rejection("Failure!", RejectionType.Temporary)));
decisions.Add(new DownloadDecision(remoteEpisode));
Subject.ProcessDecisions(decisions);
Mocker.GetMock<IDownloadService>().Verify(v => v.DownloadReport(It.IsAny<RemoteEpisode>()), Times.Never());
@@ -211,7 +209,7 @@ namespace NzbDrone.Core.Test.Download.DownloadApprovedReportsTests
decisions.Add(new DownloadDecision(remoteEpisode, new Rejection("Failure!", RejectionType.Temporary)));
Subject.ProcessDecisions(decisions);
Mocker.GetMock<IPendingReleaseService>().Verify(v => v.Add(It.IsAny<DownloadDecision>(), It.IsAny<PendingReleaseReason>()), Times.Never());
Mocker.GetMock<IPendingReleaseService>().Verify(v => v.Add(It.IsAny<DownloadDecision>()), Times.Never());
}
[Test]
@@ -225,43 +223,7 @@ namespace NzbDrone.Core.Test.Download.DownloadApprovedReportsTests
decisions.Add(new DownloadDecision(remoteEpisode, new Rejection("Failure!", RejectionType.Temporary)));
Subject.ProcessDecisions(decisions);
Mocker.GetMock<IPendingReleaseService>().Verify(v => v.Add(It.IsAny<DownloadDecision>(), It.IsAny<PendingReleaseReason>()), Times.Exactly(2));
}
[Test]
public void should_add_to_failed_if_already_failed_for_that_protocol()
{
var episodes = new List<Episode> { GetEpisode(1) };
var remoteEpisode = GetRemoteEpisode(episodes, new QualityModel(Quality.HDTV720p));
var decisions = new List<DownloadDecision>();
decisions.Add(new DownloadDecision(remoteEpisode));
decisions.Add(new DownloadDecision(remoteEpisode));
Mocker.GetMock<IDownloadService>().Setup(s => s.DownloadReport(It.IsAny<RemoteEpisode>()))
.Throws(new DownloadClientUnavailableException("Download client failed"));
Subject.ProcessDecisions(decisions);
Mocker.GetMock<IDownloadService>().Verify(v => v.DownloadReport(It.IsAny<RemoteEpisode>()), Times.Once());
}
[Test]
public void should_not_add_to_failed_if_failed_for_a_different_protocol()
{
var episodes = new List<Episode> { GetEpisode(1) };
var remoteEpisode = GetRemoteEpisode(episodes, new QualityModel(Quality.HDTV720p), DownloadProtocol.Usenet);
var remoteEpisode2 = GetRemoteEpisode(episodes, new QualityModel(Quality.HDTV720p), DownloadProtocol.Torrent);
var decisions = new List<DownloadDecision>();
decisions.Add(new DownloadDecision(remoteEpisode));
decisions.Add(new DownloadDecision(remoteEpisode2));
Mocker.GetMock<IDownloadService>().Setup(s => s.DownloadReport(It.Is<RemoteEpisode>(r => r.Release.DownloadProtocol == DownloadProtocol.Usenet)))
.Throws(new DownloadClientUnavailableException("Download client failed"));
Subject.ProcessDecisions(decisions);
Mocker.GetMock<IDownloadService>().Verify(v => v.DownloadReport(It.Is<RemoteEpisode>(r => r.Release.DownloadProtocol == DownloadProtocol.Usenet)), Times.Once());
Mocker.GetMock<IDownloadService>().Verify(v => v.DownloadReport(It.Is<RemoteEpisode>(r => r.Release.DownloadProtocol == DownloadProtocol.Torrent)), Times.Once());
Mocker.GetMock<IPendingReleaseService>().Verify(v => v.Add(It.IsAny<DownloadDecision>()), Times.Exactly(2));
}
}
}

View File

@@ -1,156 +0,0 @@
using System;
using System.Linq;
using FluentAssertions;
using Moq;
using NUnit.Framework;
using NzbDrone.Core.Download;
using NzbDrone.Core.Test.Framework;
namespace NzbDrone.Core.Test.Download
{
public class DownloadClientStatusServiceFixture : CoreTest<DownloadClientStatusService>
{
private DateTime _epoch;
[SetUp]
public void SetUp()
{
_epoch = DateTime.UtcNow;
}
private DownloadClientStatus WithStatus(DownloadClientStatus status)
{
Mocker.GetMock<IDownloadClientStatusRepository>()
.Setup(v => v.FindByProviderId(1))
.Returns(status);
Mocker.GetMock<IDownloadClientStatusRepository>()
.Setup(v => v.All())
.Returns(new[] { status });
return status;
}
private void VerifyUpdate()
{
Mocker.GetMock<IDownloadClientStatusRepository>()
.Verify(v => v.Upsert(It.IsAny<DownloadClientStatus>()), Times.Once());
}
private void VerifyNoUpdate()
{
Mocker.GetMock<IDownloadClientStatusRepository>()
.Verify(v => v.Upsert(It.IsAny<DownloadClientStatus>()), Times.Never());
}
[Test]
public void should_not_consider_blocked_within_5_minutes_since_initial_failure()
{
WithStatus(new DownloadClientStatus
{
InitialFailure = _epoch - TimeSpan.FromMinutes(4),
MostRecentFailure = _epoch - TimeSpan.FromSeconds(4),
EscalationLevel = 3
});
Subject.RecordFailure(1);
VerifyUpdate();
var status = Subject.GetBlockedProviders().FirstOrDefault();
status.Should().BeNull();
}
[Test]
public void should_consider_blocked_after_5_minutes_since_initial_failure()
{
WithStatus(new DownloadClientStatus
{
InitialFailure = _epoch - TimeSpan.FromMinutes(6),
MostRecentFailure = _epoch - TimeSpan.FromSeconds(120),
EscalationLevel = 3
});
Subject.RecordFailure(1);
VerifyUpdate();
var status = Subject.GetBlockedProviders().FirstOrDefault();
status.Should().NotBeNull();
}
[Test]
public void should_not_escalate_further_till_after_5_minutes_since_initial_failure()
{
var origStatus = WithStatus(new DownloadClientStatus
{
InitialFailure = _epoch - TimeSpan.FromMinutes(4),
MostRecentFailure = _epoch - TimeSpan.FromSeconds(4),
EscalationLevel = 3
});
Subject.RecordFailure(1);
Subject.RecordFailure(1);
Subject.RecordFailure(1);
Subject.RecordFailure(1);
Subject.RecordFailure(1);
Subject.RecordFailure(1);
Subject.RecordFailure(1);
var status = Subject.GetBlockedProviders().FirstOrDefault();
status.Should().BeNull();
origStatus.EscalationLevel.Should().Be(3);
}
[Test]
public void should_escalate_further_after_5_minutes_since_initial_failure()
{
WithStatus(new DownloadClientStatus
{
InitialFailure = _epoch - TimeSpan.FromMinutes(6),
MostRecentFailure = _epoch - TimeSpan.FromSeconds(120),
EscalationLevel = 3
});
Subject.RecordFailure(1);
Subject.RecordFailure(1);
Subject.RecordFailure(1);
Subject.RecordFailure(1);
Subject.RecordFailure(1);
Subject.RecordFailure(1);
Subject.RecordFailure(1);
var status = Subject.GetBlockedProviders().FirstOrDefault();
status.Should().NotBeNull();
status.EscalationLevel.Should().BeGreaterThan(3);
}
[Test]
public void should_not_escalate_beyond_3_hours()
{
WithStatus(new DownloadClientStatus
{
InitialFailure = _epoch - TimeSpan.FromMinutes(6),
MostRecentFailure = _epoch - TimeSpan.FromSeconds(120),
EscalationLevel = 3
});
Subject.RecordFailure(1);
Subject.RecordFailure(1);
Subject.RecordFailure(1);
Subject.RecordFailure(1);
Subject.RecordFailure(1);
Subject.RecordFailure(1);
Subject.RecordFailure(1);
Subject.RecordFailure(1);
Subject.RecordFailure(1);
var status = Subject.GetBlockedProviders().FirstOrDefault();
status.Should().NotBeNull();
status.DisabledTill.Should().HaveValue();
status.DisabledTill.Should().NotBeAfter(_epoch + TimeSpan.FromHours(3.1));
}
}
}

View File

@@ -19,7 +19,6 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.NzbgetTests
private NzbgetQueueItem _queued;
private NzbgetHistoryItem _failed;
private NzbgetHistoryItem _completed;
private Dictionary<string, string> _configItems;
[SetUp]
public void Setup()
@@ -81,18 +80,13 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.NzbgetTests
DownloadRate = 7000000
});
Mocker.GetMock<INzbgetProxy>()
.Setup(v => v.GetVersion(It.IsAny<NzbgetSettings>()))
.Returns("14.0");
_configItems = new Dictionary<string, string>();
_configItems.Add("Category1.Name", "tv");
_configItems.Add("Category1.DestDir", @"/remote/mount/tv");
var configItems = new Dictionary<string, string>();
configItems.Add("Category1.Name", "tv");
configItems.Add("Category1.DestDir", @"/remote/mount/tv");
Mocker.GetMock<INzbgetProxy>()
.Setup(v => v.GetConfig(It.IsAny<NzbgetSettings>()))
.Returns(_configItems);
.Returns(configItems);
}
protected void GivenFailedDownload()
@@ -420,18 +414,5 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.NzbgetTests
error.IsValid.Should().Be(expected);
}
[TestCase("0", false)]
[TestCase("1", true)]
[TestCase(" 7", false)]
[TestCase("5000000", false)]
public void should_test_keephistory(string keephistory, bool expected)
{
_configItems["KeepHistory"] = keephistory;
var error = Subject.Test();
error.IsValid.Should().Be(expected);
}
}
}

View File

@@ -102,7 +102,7 @@ namespace NzbDrone.Core.Test.Download.Pending.PendingReleaseServiceTests
[Test]
public void should_add()
{
Subject.Add(_temporarilyRejected, PendingReleaseReason.Delay);
Subject.Add(_temporarilyRejected);
VerifyInsert();
}
@@ -112,7 +112,7 @@ namespace NzbDrone.Core.Test.Download.Pending.PendingReleaseServiceTests
{
GivenHeldRelease(_release.Title, _release.Indexer, _release.PublishDate);
Subject.Add(_temporarilyRejected, PendingReleaseReason.Delay);
Subject.Add(_temporarilyRejected);
VerifyNoInsert();
}
@@ -122,7 +122,7 @@ namespace NzbDrone.Core.Test.Download.Pending.PendingReleaseServiceTests
{
GivenHeldRelease(_release.Title + "-RP", _release.Indexer, _release.PublishDate);
Subject.Add(_temporarilyRejected, PendingReleaseReason.Delay);
Subject.Add(_temporarilyRejected);
VerifyInsert();
}
@@ -132,7 +132,7 @@ namespace NzbDrone.Core.Test.Download.Pending.PendingReleaseServiceTests
{
GivenHeldRelease(_release.Title, "AnotherIndexer", _release.PublishDate);
Subject.Add(_temporarilyRejected, PendingReleaseReason.Delay);
Subject.Add(_temporarilyRejected);
VerifyInsert();
}
@@ -142,7 +142,7 @@ namespace NzbDrone.Core.Test.Download.Pending.PendingReleaseServiceTests
{
GivenHeldRelease(_release.Title, _release.Indexer, _release.PublishDate.AddHours(1));
Subject.Add(_temporarilyRejected, PendingReleaseReason.Delay);
Subject.Add(_temporarilyRejected);
VerifyInsert();
}

View File

@@ -27,7 +27,7 @@ namespace NzbDrone.Core.Test.Download.Pending.PendingReleaseServiceTests
public void should_not_ignore_pending_items_from_available_indexer()
{
Mocker.GetMock<IIndexerStatusService>()
.Setup(v => v.GetBlockedProviders())
.Setup(v => v.GetBlockedIndexers())
.Returns(new List<IndexerStatus>());
GivenPendingRelease();
@@ -43,7 +43,7 @@ namespace NzbDrone.Core.Test.Download.Pending.PendingReleaseServiceTests
public void should_ignore_pending_items_from_unavailable_indexer()
{
Mocker.GetMock<IIndexerStatusService>()
.Setup(v => v.GetBlockedProviders())
.Setup(v => v.GetBlockedIndexers())
.Returns(new List<IndexerStatus> { new IndexerStatus { ProviderId = 1, DisabledTill = DateTime.UtcNow.AddHours(2) } });
GivenPendingRelease();

View File

@@ -1,60 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:newznab="http://www.newznab.com/DTD/2010/feeds/attributes/" xmlns:torznab="http://torznab.com/schemas/2015/feed">
<channel>
<atom:link href="https://localhost/feed/rssx" rel="self" type="application/rss+xml" />
<title>Anime Tosho</title>
<link>https://localhost/</link>
<description>Latest releases feed</description>
<language>en-gb</language>
<ttl>30</ttl>
<lastBuildDate>Wed, 17 May 2017 20:36:06 +0000</lastBuildDate>
<newznab:response offset="0"/>
<item>
<title>[finFAGs]_Frame_Arms_Girl_07_(1280x720_TV_AAC)_[1262B6F7].mkv</title>
<pubDate>Wed, 17 May 2017 20:36:06 +0000</pubDate>
<guid isPermaLink="true">https://localhost/view/123451</guid>
<category>Anime</category>
<description><![CDATA[<strong>Total Size</strong>: 301.8 MB<br />]]></description>
<link>https://localhost/view/finfags-_frame_arms_girl_07_-1280x720_tv_aac-_-1262b6f7-mkv.123451</link>
<comments>https://localhost/view/finfags-_frame_arms_girl_07_-1280x720_tv_aac-_-1262b6f7-mkv.123451</comments>
<enclosure url="http://storage.localhost/torrents/123451.torrent" type="application/x-bittorrent" length="0" />
<source url="http://www.tokyotosho.info/details.php?id=123451">TokyoTosho</source>
<newznab:attr name="category" value="5070" />
<newznab:attr name="category" value="100001" />
<newznab:attr name="files" value="1" />
<newznab:attr name="size" value="316477946" />
<torznab:attr name="files" value="1" />
<torznab:attr name="size" value="316477946" />
<torznab:attr name="category" value="5070" />
<torznab:attr name="category" value="100001" />
<torznab:attr name="infohash" value="2d69a861bef5a9f2cdf791b7328e37b7953205e1" />
<torznab:attr name="magneturl" value="magnet:?xt=urn:btih:VU2QYN66WU7FTPXSG3TFDRXW6KTEBPBF" />
</item>
<item>
<title>[HorribleSubs] Frame Arms Girl - 07 [720p].mkv</title>
<pubDate>Mon, 15 May 2017 19:15:56 +0000</pubDate>
<guid isPermaLink="true">https://localhost/view/123452</guid>
<category>Anime</category>
<description><![CDATA[<strong>Total Size</strong>: 452.0 MB<br />]]></description>
<link>https://localhost/view/horriblesubs-frame-arms-girl-07-720p-mkv.123452</link>
<comments>https://localhost/view/horriblesubs-frame-arms-girl-07-720p-mkv.123452</comments>
<enclosure url="http://storage.localhost/torrents/123452.torrent" type="application/x-bittorrent" length="0" />
<enclosure url="http://storage.localhost/nzb/123452.nzb" type="application/x-nzb" length="0" />
<source url="http://www.tokyotosho.info/details.php?id=123452">TokyoTosho</source>
<newznab:attr name="category" value="5070" />
<newznab:attr name="category" value="100001" />
<newznab:attr name="files" value="1" />
<newznab:attr name="size" value="473987489" />
<torznab:attr name="files" value="1" />
<torznab:attr name="size" value="473987489" />
<torznab:attr name="category" value="5070" />
<torznab:attr name="category" value="100001" />
<torznab:attr name="infohash" value="bff4afebcd50c21949ed6a06323d2120c649bd82" />
<torznab:attr name="magneturl" value="magnet:?xt=urn:btih:5QK77JL7LZVIMEGKJ5VVAMMR5EEQMMSN" />
</item>
</channel>
</rss>

View File

@@ -3,6 +3,7 @@ using System.Collections.Generic;
using NUnit.Framework;
using NzbDrone.Core.Download;
using NzbDrone.Core.HealthCheck.Checks;
using NzbDrone.Core.Indexers;
using NzbDrone.Core.Test.Framework;
using NzbDrone.Test.Common;
@@ -25,7 +26,7 @@ namespace NzbDrone.Core.Test.HealthCheck.Checks
public void should_return_error_when_download_client_throws()
{
var downloadClient = Mocker.GetMock<IDownloadClient>();
downloadClient.Setup(s => s.Definition).Returns(new DownloadClientDefinition{Name = "Test"});
downloadClient.Setup(s => s.Definition).Returns(new IndexerDefinition{Name = "Test"});
downloadClient.Setup(s => s.GetItems())
.Throws<Exception>();
@@ -35,6 +36,8 @@ namespace NzbDrone.Core.Test.HealthCheck.Checks
.Returns(new IDownloadClient[] { downloadClient.Object });
Subject.Check().ShouldBeError();
ExceptionVerification.ExpectedErrors(1);
}
[Test]

View File

@@ -22,7 +22,7 @@ namespace NzbDrone.Core.Test.HealthCheck.Checks
.Returns(_indexers);
Mocker.GetMock<IIndexerStatusService>()
.Setup(v => v.GetBlockedProviders())
.Setup(v => v.GetBlockedIndexers())
.Returns(_blockedIndexers);
}
@@ -57,6 +57,13 @@ namespace NzbDrone.Core.Test.HealthCheck.Checks
{
Subject.Check().ShouldBeOk();
}
[Test]
public void should_not_return_error_when_indexer_failed_less_than_an_hour()
{
GivenIndexer(1, 0.1, 0.5);
Subject.Check().ShouldBeOk();
}
[Test]
public void should_return_warning_if_indexer_unavailable()

View File

@@ -13,7 +13,6 @@ using NzbDrone.Core.Qualities;
using System.Collections.Generic;
using NzbDrone.Core.Test.Qualities;
using FluentAssertions;
using NzbDrone.Core.Download;
using NzbDrone.Core.Tv;
namespace NzbDrone.Core.Test.HistoryTests
@@ -82,13 +81,7 @@ namespace NzbDrone.Core.Test.HistoryTests
Path = @"C:\Test\Unsorted\Series.s01e01.mkv"
};
var downloadClientItem = new DownloadClientItem
{
DownloadClient = "sab",
DownloadId = "abcd"
};
Subject.Handle(new EpisodeImportedEvent(localEpisode, episodeFile, new List<EpisodeFile>(), true, downloadClientItem));
Subject.Handle(new EpisodeImportedEvent(localEpisode, episodeFile, true, "sab", "abcd"));
Mocker.GetMock<IHistoryRepository>()
.Verify(v => v.Insert(It.Is<History.History>(h => h.SourceTitle == Path.GetFileNameWithoutExtension(localEpisode.Path))));

View File

@@ -1,60 +0,0 @@
using System;
using FizzWare.NBuilder;
using FluentAssertions;
using NUnit.Framework;
using NzbDrone.Core.Download.Pending;
using NzbDrone.Core.Housekeeping.Housekeepers;
using NzbDrone.Core.Parser.Model;
using NzbDrone.Core.Test.Framework;
namespace NzbDrone.Core.Test.Housekeeping.Housekeepers
{
[TestFixture]
public class CleanupDownloadClientUnavailablePendingReleasesFixture : DbTest<CleanupDownloadClientUnavailablePendingReleases, PendingRelease>
{
[Test]
public void should_delete_old_DownloadClientUnavailable_pending_items()
{
var pendingRelease = Builder<PendingRelease>.CreateNew()
.With(h => h.Reason = PendingReleaseReason.DownloadClientUnavailable)
.With(h => h.Added = DateTime.UtcNow.AddDays(-21))
.With(h => h.ParsedEpisodeInfo = new ParsedEpisodeInfo())
.With(h => h.Release = new ReleaseInfo())
.BuildNew();
Db.Insert(pendingRelease);
Subject.Clean();
AllStoredModels.Should().BeEmpty();
}
[Test]
public void should_delete_old_Fallback_pending_items()
{
var pendingRelease = Builder<PendingRelease>.CreateNew()
.With(h => h.Reason = PendingReleaseReason.Fallback)
.With(h => h.Added = DateTime.UtcNow.AddDays(-21))
.With(h => h.ParsedEpisodeInfo = new ParsedEpisodeInfo())
.With(h => h.Release = new ReleaseInfo())
.BuildNew();
Db.Insert(pendingRelease);
Subject.Clean();
AllStoredModels.Should().BeEmpty();
}
[Test]
public void should_not_delete_old_Delay_pending_items()
{
var pendingRelease = Builder<PendingRelease>.CreateNew()
.With(h => h.Reason = PendingReleaseReason.Delay)
.With(h => h.Added = DateTime.UtcNow.AddDays(-21))
.With(h => h.ParsedEpisodeInfo = new ParsedEpisodeInfo())
.With(h => h.Release = new ReleaseInfo())
.BuildNew();
Db.Insert(pendingRelease);
Subject.Clean();
AllStoredModels.Should().HaveCount(1);
}
}
}

View File

@@ -11,7 +11,7 @@ namespace NzbDrone.Core.Test.IndexerTests
public class IndexerStatusServiceFixture : CoreTest<IndexerStatusService>
{
private DateTime _epoch;
[SetUp]
public void SetUp()
{
@@ -21,7 +21,7 @@ namespace NzbDrone.Core.Test.IndexerTests
private void WithStatus(IndexerStatus status)
{
Mocker.GetMock<IIndexerStatusRepository>()
.Setup(v => v.FindByProviderId(1))
.Setup(v => v.FindByIndexerId(1))
.Returns(status);
Mocker.GetMock<IIndexerStatusRepository>()
@@ -29,16 +29,25 @@ namespace NzbDrone.Core.Test.IndexerTests
.Returns(new[] { status });
}
private void VerifyUpdate()
private void VerifyUpdate(bool updated = true)
{
Mocker.GetMock<IIndexerStatusRepository>()
.Verify(v => v.Upsert(It.IsAny<IndexerStatus>()), Times.Once());
.Verify(v => v.Upsert(It.IsAny<IndexerStatus>()), Times.Exactly(updated ? 1 : 0));
}
private void VerifyNoUpdate()
[Test]
public void should_start_backoff_on_first_failure()
{
Mocker.GetMock<IIndexerStatusRepository>()
.Verify(v => v.Upsert(It.IsAny<IndexerStatus>()), Times.Never());
WithStatus(new IndexerStatus());
Subject.RecordFailure(1);
VerifyUpdate();
var status = Subject.GetBlockedIndexers().FirstOrDefault();
status.Should().NotBeNull();
status.DisabledTill.Should().HaveValue();
status.DisabledTill.Value.Should().BeCloseTo(_epoch + TimeSpan.FromMinutes(5), 500);
}
[Test]
@@ -50,7 +59,7 @@ namespace NzbDrone.Core.Test.IndexerTests
VerifyUpdate();
var status = Subject.GetBlockedProviders().FirstOrDefault();
var status = Subject.GetBlockedIndexers().FirstOrDefault();
status.Should().BeNull();
}
@@ -61,7 +70,22 @@ namespace NzbDrone.Core.Test.IndexerTests
Subject.RecordSuccess(1);
VerifyNoUpdate();
VerifyUpdate(false);
}
[Test]
public void should_preserve_escalation_on_intermittent_success()
{
WithStatus(new IndexerStatus { MostRecentFailure = _epoch - TimeSpan.FromSeconds(4), EscalationLevel = 3 });
Subject.RecordSuccess(1);
Subject.RecordSuccess(1);
Subject.RecordFailure(1);
var status = Subject.GetBlockedIndexers().FirstOrDefault();
status.Should().NotBeNull();
status.DisabledTill.Should().HaveValue();
status.DisabledTill.Value.Should().BeCloseTo(_epoch + TimeSpan.FromMinutes(15), 500);
}
}
}

View File

@@ -7,7 +7,6 @@ using NUnit.Framework;
using NzbDrone.Common.Http;
using NzbDrone.Core.Indexers;
using NzbDrone.Core.Indexers.Newznab;
using NzbDrone.Core.Parser.Model;
using NzbDrone.Core.Test.Framework;
using NzbDrone.Test.Common;
@@ -64,35 +63,6 @@ namespace NzbDrone.Core.Test.IndexerTests.NewznabTests
releaseInfo.Size.Should().Be(1183105773);
}
[Test]
public void should_parse_recent_feed_from_newznab_animetosho()
{
var recentFeed = ReadAllText(@"Files/Indexers/Torznab/torznab_animetosho.xml");
Mocker.GetMock<IHttpClient>()
.Setup(o => o.Execute(It.Is<HttpRequest>(v => v.Method == HttpMethod.GET)))
.Returns<HttpRequest>(r => new HttpResponse(r, new HttpHeader(), recentFeed));
var releases = Subject.FetchRecent();
releases.Should().HaveCount(1);
releases.First().Should().BeOfType<ReleaseInfo>();
var releaseInfo = releases.First() as ReleaseInfo;
releaseInfo.Title.Should().Be("[HorribleSubs] Frame Arms Girl - 07 [720p].mkv");
releaseInfo.DownloadProtocol.Should().Be(DownloadProtocol.Usenet);
releaseInfo.DownloadUrl.Should().Be("http://storage.localhost/nzb/123452.nzb");
releaseInfo.InfoUrl.Should().Be("https://localhost/view/horriblesubs-frame-arms-girl-07-720p-mkv.123452");
releaseInfo.CommentUrl.Should().Be("https://localhost/view/horriblesubs-frame-arms-girl-07-720p-mkv.123452");
releaseInfo.Indexer.Should().Be(Subject.Definition.Name);
releaseInfo.PublishDate.Should().Be(DateTime.Parse("Mon, 15 May 2017 19:15:56 +0000").ToUniversalTime());
releaseInfo.Size.Should().Be(473987489);
releaseInfo.TvdbId.Should().Be(0);
releaseInfo.TvRageId.Should().Be(0);
}
[Test]
public void should_use_pagesize_reported_by_caps()
{

View File

@@ -254,11 +254,13 @@ namespace NzbDrone.Core.Test.IndexerTests.TorrentRssIndexerTests
}
[TestCase("BitMeTv/BitMeTv.xml")]
[TestCase("Fanzub/fanzub.xml")]
[TestCase("IPTorrents/IPTorrents.xml")]
[TestCase("Newznab/newznab_nzb_su.xml")]
[TestCase("Nyaa/Nyaa.xml")]
[TestCase("Omgwtfnzbs/Omgwtfnzbs.xml")]
[TestCase("Torznab/torznab_hdaccess_net.xml")]
[TestCase("Torznab/torznab_tpb.xml")]
[TestCase("Torznab/torznab_animetosho.xml")]
public void should_detect_recent_feed(string rssXmlFile)
{
GivenRecentFeedResponse(rssXmlFile);

View File

@@ -97,37 +97,6 @@ namespace NzbDrone.Core.Test.IndexerTests.TorznabTests
releaseInfo.Peers.Should().Be(36724);
}
[Test]
public void should_parse_recent_feed_from_torznab_animetosho()
{
var recentFeed = ReadAllText(@"Files/Indexers/Torznab/torznab_animetosho.xml");
Mocker.GetMock<IHttpClient>()
.Setup(o => o.Execute(It.Is<HttpRequest>(v => v.Method == HttpMethod.GET)))
.Returns<HttpRequest>(r => new HttpResponse(r, new HttpHeader(), recentFeed));
var releases = Subject.FetchRecent();
releases.Should().HaveCount(2);
releases.First().Should().BeOfType<TorrentInfo>();
var releaseInfo = releases.First() as TorrentInfo;
releaseInfo.Title.Should().Be("[finFAGs]_Frame_Arms_Girl_07_(1280x720_TV_AAC)_[1262B6F7].mkv");
releaseInfo.DownloadProtocol.Should().Be(DownloadProtocol.Torrent);
releaseInfo.DownloadUrl.Should().Be("http://storage.localhost/torrents/123451.torrent");
releaseInfo.InfoUrl.Should().Be("https://localhost/view/finfags-_frame_arms_girl_07_-1280x720_tv_aac-_-1262b6f7-mkv.123451");
releaseInfo.CommentUrl.Should().Be("https://localhost/view/finfags-_frame_arms_girl_07_-1280x720_tv_aac-_-1262b6f7-mkv.123451");
releaseInfo.Indexer.Should().Be(Subject.Definition.Name);
releaseInfo.PublishDate.Should().Be(DateTime.Parse("Wed, 17 May 2017 20:36:06 +0000").ToUniversalTime());
releaseInfo.Size.Should().Be(316477946);
releaseInfo.TvdbId.Should().Be(0);
releaseInfo.TvRageId.Should().Be(0);
releaseInfo.InfoHash.Should().Be("2d69a861bef5a9f2cdf791b7328e37b7953205e1");
releaseInfo.Seeders.Should().BeNull();
releaseInfo.Peers.Should().BeNull();
}
[Test]
public void should_use_pagesize_reported_by_caps()
{

View File

@@ -164,9 +164,11 @@ namespace NzbDrone.Core.Test.MediaFiles
Mocker.GetMock<IDetectSample>()
.Setup(s => s.IsSample(It.IsAny<Series>(),
It.IsAny<QualityModel>(),
It.IsAny<string>(),
It.IsAny<long>(),
It.IsAny<bool>()))
.Returns(DetectSampleResult.Sample);
.Returns(true);
Subject.ProcessRootFolder(new DirectoryInfo(_droneFactory));
@@ -234,9 +236,11 @@ namespace NzbDrone.Core.Test.MediaFiles
Mocker.GetMock<IDetectSample>()
.Setup(s => s.IsSample(It.IsAny<Series>(),
It.IsAny<QualityModel>(),
It.IsAny<string>(),
It.IsAny<long>(),
It.IsAny<bool>()))
.Returns(DetectSampleResult.Sample);
.Returns(true);
Mocker.GetMock<IDiskProvider>()
.Setup(s => s.GetFiles(It.IsAny<string>(), SearchOption.AllDirectories))
@@ -343,9 +347,11 @@ namespace NzbDrone.Core.Test.MediaFiles
Mocker.GetMock<IDetectSample>()
.Setup(s => s.IsSample(It.IsAny<Series>(),
It.IsAny<QualityModel>(),
It.IsAny<string>(),
It.IsAny<long>(),
It.IsAny<bool>()))
.Returns(DetectSampleResult.Sample);
.Returns(true);
Mocker.GetMock<IDiskProvider>()
.Setup(s => s.GetFileSize(It.IsAny<string>()))

View File

@@ -333,16 +333,8 @@ namespace NzbDrone.Core.Test.MediaFiles.EpisodeImport
GivenVideoFiles(videoFiles.ToList());
Mocker.GetMock<IDetectSample>()
.Setup(s => s.IsSample(_series, It.IsAny<string>(), It.IsAny<bool>()))
.Returns((Series s, string path, bool special) =>
{
if (path.Contains("sample"))
{
return DetectSampleResult.Sample;
}
return DetectSampleResult.NotSample;
});
.Setup(s => s.IsSample(_series, It.IsAny<QualityModel>(), It.Is<string>(c => c.Contains("sample")), It.IsAny<long>(), It.IsAny<bool>()))
.Returns(true);
var folderInfo = Parser.Parser.ParseTitle("Series.Title.S01E01");

View File

@@ -10,12 +10,11 @@ using NzbDrone.Core.Parser.Model;
using NzbDrone.Core.Qualities;
using NzbDrone.Core.Test.Framework;
using NzbDrone.Core.Tv;
using NzbDrone.Test.Common;
namespace NzbDrone.Core.Test.MediaFiles.EpisodeImport
{
[TestFixture]
public class DetectSampleFixture : CoreTest<DetectSample>
public class SampleServiceFixture : CoreTest<DetectSample>
{
private Series _series;
private LocalEpisode _localEpisode;
@@ -43,6 +42,11 @@ namespace NzbDrone.Core.Test.MediaFiles.EpisodeImport
};
}
private void GivenFileSize(long size)
{
_localEpisode.Size = size;
}
private void GivenRuntime(int seconds)
{
Mocker.GetMock<IVideoFileInfoReader>()
@@ -54,7 +58,7 @@ namespace NzbDrone.Core.Test.MediaFiles.EpisodeImport
public void should_return_false_if_season_zero()
{
_localEpisode.Episodes[0].SeasonNumber = 0;
ShouldBeNotSample();
ShouldBeFalse();
}
[Test]
@@ -62,7 +66,7 @@ namespace NzbDrone.Core.Test.MediaFiles.EpisodeImport
{
_localEpisode.Path = @"C:\Test\some.show.s01e01.flv";
ShouldBeNotSample();
ShouldBeFalse();
Mocker.GetMock<IVideoFileInfoReader>().Verify(c => c.GetRunTime(It.IsAny<string>()), Times.Never());
}
@@ -72,7 +76,7 @@ namespace NzbDrone.Core.Test.MediaFiles.EpisodeImport
{
_localEpisode.Path = @"C:\Test\some.show.s01e01.strm";
ShouldBeNotSample();
ShouldBeFalse();
Mocker.GetMock<IVideoFileInfoReader>().Verify(c => c.GetRunTime(It.IsAny<string>()), Times.Never());
}
@@ -81,9 +85,12 @@ namespace NzbDrone.Core.Test.MediaFiles.EpisodeImport
public void should_use_runtime()
{
GivenRuntime(120);
GivenFileSize(1000.Megabytes());
Subject.IsSample(_localEpisode.Series,
_localEpisode.Quality,
_localEpisode.Path,
_localEpisode.Size,
_localEpisode.IsSpecial);
Mocker.GetMock<IVideoFileInfoReader>().Verify(v => v.GetRunTime(It.IsAny<string>()), Times.Once());
@@ -94,7 +101,7 @@ namespace NzbDrone.Core.Test.MediaFiles.EpisodeImport
{
GivenRuntime(60);
ShouldBeSample();
ShouldBeTrue();
}
[Test]
@@ -102,7 +109,7 @@ namespace NzbDrone.Core.Test.MediaFiles.EpisodeImport
{
GivenRuntime(600);
ShouldBeNotSample();
ShouldBeFalse();
}
[Test]
@@ -111,7 +118,7 @@ namespace NzbDrone.Core.Test.MediaFiles.EpisodeImport
_series.Runtime = 6;
GivenRuntime(299);
ShouldBeNotSample();
ShouldBeFalse();
}
[Test]
@@ -120,7 +127,7 @@ namespace NzbDrone.Core.Test.MediaFiles.EpisodeImport
_series.Runtime = 2;
GivenRuntime(60);
ShouldBeNotSample();
ShouldBeFalse();
}
[Test]
@@ -129,21 +136,29 @@ namespace NzbDrone.Core.Test.MediaFiles.EpisodeImport
_series.Runtime = 2;
GivenRuntime(10);
ShouldBeSample();
ShouldBeTrue();
}
[Test]
public void should_return_indeterminate_if_mediainfo_result_is_null()
public void should_fall_back_to_file_size_if_mediainfo_dll_not_found_acceptable_size()
{
Mocker.GetMock<IVideoFileInfoReader>()
.Setup(s => s.GetRunTime(It.IsAny<string>()))
.Returns((TimeSpan?)null);
.Throws<DllNotFoundException>();
Subject.IsSample(_localEpisode.Series,
_localEpisode.Path,
_localEpisode.IsSpecial).Should().Be(DetectSampleResult.Indeterminate);
GivenFileSize(1000.Megabytes());
ShouldBeFalse();
}
ExceptionVerification.ExpectedErrors(1);
[Test]
public void should_fall_back_to_file_size_if_mediainfo_dll_not_found_undersize()
{
Mocker.GetMock<IVideoFileInfoReader>()
.Setup(s => s.GetRunTime(It.IsAny<string>()))
.Throws<DllNotFoundException>();
GivenFileSize(1.Megabytes());
ShouldBeTrue();
}
[Test]
@@ -152,7 +167,7 @@ namespace NzbDrone.Core.Test.MediaFiles.EpisodeImport
GivenRuntime(600);
_series.SeriesType = SeriesTypes.Daily;
_localEpisode.Episodes[0].SeasonNumber = 0;
ShouldBeNotSample();
ShouldBeFalse();
}
[Test]
@@ -161,21 +176,25 @@ namespace NzbDrone.Core.Test.MediaFiles.EpisodeImport
_series.SeriesType = SeriesTypes.Anime;
_localEpisode.Episodes[0].SeasonNumber = 0;
ShouldBeNotSample();
ShouldBeFalse();
}
private void ShouldBeSample()
private void ShouldBeTrue()
{
Subject.IsSample(_localEpisode.Series,
_localEpisode.Path,
_localEpisode.IsSpecial).Should().Be(DetectSampleResult.Sample);
_localEpisode.Quality,
_localEpisode.Path,
_localEpisode.Size,
_localEpisode.IsSpecial).Should().BeTrue();
}
private void ShouldBeNotSample()
private void ShouldBeFalse()
{
Subject.IsSample(_localEpisode.Series,
_localEpisode.Quality,
_localEpisode.Path,
_localEpisode.IsSpecial).Should().Be(DetectSampleResult.NotSample);
_localEpisode.Size,
_localEpisode.IsSpecial).Should().BeFalse();
}
}
}

View File

@@ -63,35 +63,6 @@ namespace NzbDrone.Core.Test.MediaFiles.EpisodeImport.Specifications
Subject.IsSatisfiedBy(_localEpisode, _downloadClientItem).Accepted.Should().BeTrue();
}
[Test]
public void should_be_accepted_if_grabbed_history_is_for_a_season_pack()
{
var history = Builder<History.History>.CreateListOfSize(1)
.All()
.With(h => h.EventType = HistoryEventType.Grabbed)
.With(h => h.Quality = _localEpisode.Quality)
.With(h => h.SourceTitle = "Series.Title.S01.720p.HDTV.x264-RlsGroup")
.BuildList();
GivenHistory(history);
Subject.IsSatisfiedBy(_localEpisode, _downloadClientItem).Accepted.Should().BeTrue();
}
[Test]
public void should_be_accepted_if_grabbed_history_quality_is_unknown()
{
var history = Builder<History.History>.CreateListOfSize(1)
.All()
.With(h => h.EventType = HistoryEventType.Grabbed)
.With(h => h.Quality = new QualityModel(Quality.Unknown))
.BuildList();
GivenHistory(history);
Subject.IsSatisfiedBy(_localEpisode, _downloadClientItem).Accepted.Should().BeTrue();
}
[Test]
public void should_be_accepted_if_grabbed_history_quality_matches()
{

View File

@@ -1,140 +0,0 @@
using System.IO;
using FizzWare.NBuilder;
using Moq;
using NUnit.Framework;
using NzbDrone.Common.Disk;
using NzbDrone.Core.Exceptions;
using NzbDrone.Core.MediaFiles;
using NzbDrone.Core.Test.Framework;
using NzbDrone.Core.Tv;
using NzbDrone.Test.Common;
namespace NzbDrone.Core.Test.MediaFiles.MediaFileDeletionService
{
[TestFixture]
public class DeleteEpisodeFileFixture : CoreTest<Core.MediaFiles.MediaFileDeletionService>
{
private static readonly string RootFolder = @"C:\Test\TV";
private Series _series;
private EpisodeFile _episodeFile;
[SetUp]
public void Setup()
{
_series = Builder<Series>.CreateNew()
.With(s => s.Path = Path.Combine(RootFolder, "Series Title"))
.Build();
_episodeFile = Builder<EpisodeFile>.CreateNew()
.With(f => f.RelativePath = "Series Title - S01E01")
.With(f => f.Path = Path.Combine(_series.Path, "Series Title - S01E01"))
.Build();
Mocker.GetMock<IDiskProvider>()
.Setup(s => s.GetParentFolder(_series.Path))
.Returns(RootFolder);
Mocker.GetMock<IDiskProvider>()
.Setup(s => s.GetParentFolder(_episodeFile.Path))
.Returns(_series.Path);
}
private void GivenRootFolderExists()
{
Mocker.GetMock<IDiskProvider>()
.Setup(s => s.FolderExists(RootFolder))
.Returns(true);
}
private void GivenRootFolderHasFolders()
{
Mocker.GetMock<IDiskProvider>()
.Setup(s => s.GetDirectories(RootFolder))
.Returns(new[] { _series.Path });
}
private void GivenSeriesFolderExists()
{
Mocker.GetMock<IDiskProvider>()
.Setup(s => s.FolderExists(_series.Path))
.Returns(true);
}
[Test]
public void should_throw_if_root_folder_does_not_exist()
{
Assert.Throws<NzbDroneClientException>(() => Subject.DeleteEpisodeFile(_series, _episodeFile));
}
[Test]
public void should_should_throw_if_root_folder_is_empty()
{
GivenRootFolderExists();
Assert.Throws<NzbDroneClientException>(() => Subject.DeleteEpisodeFile(_series, _episodeFile));
}
[Test]
public void should_delete_from_db_if_series_folder_does_not_exist()
{
GivenRootFolderExists();
GivenRootFolderHasFolders();
Subject.DeleteEpisodeFile(_series, _episodeFile);
Mocker.GetMock<IMediaFileService>().Verify(v => v.Delete(_episodeFile, DeleteMediaFileReason.Manual), Times.Once());
Mocker.GetMock<IRecycleBinProvider>().Verify(v => v.DeleteFile(_episodeFile.Path, It.IsAny<string>()), Times.Never());
}
[Test]
public void should_delete_from_db_if_episode_file_does_not_exist()
{
GivenRootFolderExists();
GivenRootFolderHasFolders();
GivenSeriesFolderExists();
Subject.DeleteEpisodeFile(_series, _episodeFile);
Mocker.GetMock<IMediaFileService>().Verify(v => v.Delete(_episodeFile, DeleteMediaFileReason.Manual), Times.Once());
Mocker.GetMock<IRecycleBinProvider>().Verify(v => v.DeleteFile(_episodeFile.Path, It.IsAny<string>()), Times.Never());
}
[Test]
public void should_delete_from_disk_and_db_if_episode_file_exists()
{
GivenRootFolderExists();
GivenRootFolderHasFolders();
GivenSeriesFolderExists();
Mocker.GetMock<IDiskProvider>()
.Setup(s => s.FileExists(_episodeFile.Path))
.Returns(true);
Subject.DeleteEpisodeFile(_series, _episodeFile);
Mocker.GetMock<IRecycleBinProvider>().Verify(v => v.DeleteFile(_episodeFile.Path, "Series Title"), Times.Once());
Mocker.GetMock<IMediaFileService>().Verify(v => v.Delete(_episodeFile, DeleteMediaFileReason.Manual), Times.Once());
}
[Test]
public void should_handle_error_deleting_episode_file()
{
GivenRootFolderExists();
GivenRootFolderHasFolders();
GivenSeriesFolderExists();
Mocker.GetMock<IDiskProvider>()
.Setup(s => s.FileExists(_episodeFile.Path))
.Returns(true);
Mocker.GetMock<IRecycleBinProvider>()
.Setup(s => s.DeleteFile(_episodeFile.Path, "Series Title"))
.Throws(new IOException());
Assert.Throws<NzbDroneClientException>(() => Subject.DeleteEpisodeFile(_series, _episodeFile));
ExceptionVerification.ExpectedErrors(1);
Mocker.GetMock<IRecycleBinProvider>().Verify(v => v.DeleteFile(_episodeFile.Path, "Series Title"), Times.Once());
Mocker.GetMock<IMediaFileService>().Verify(v => v.Delete(_episodeFile, DeleteMediaFileReason.Manual), Times.Never());
}
}
}

View File

@@ -1,12 +1,11 @@
using FluentAssertions;
using NUnit.Framework;
using NzbDrone.Core.MediaFiles.MediaInfo;
using NzbDrone.Test.Common;
namespace NzbDrone.Core.Test.MediaFiles.MediaInfo.MediaInfoFormatterTests
namespace NzbDrone.Core.Test.MediaFiles.MediaInfo
{
[TestFixture]
public class FormatAudioChannelsFixture : TestBase
public class FormattedAudioChannelsFixture
{
[Test]
public void should_subtract_one_from_AudioChannels_as_total_channels_if_LFE_in_AudioChannelPositionsText()
@@ -18,7 +17,7 @@ namespace NzbDrone.Core.Test.MediaFiles.MediaInfo.MediaInfoFormatterTests
AudioChannelPositionsText = "Front: L C R, Side: L R, LFE"
};
MediaInfoFormatter.FormatAudioChannels(mediaInfoModel).Should().Be(5.1m);
mediaInfoModel.FormattedAudioChannels.Should().Be(5.1m);
}
[Test]
@@ -31,7 +30,7 @@ namespace NzbDrone.Core.Test.MediaFiles.MediaInfo.MediaInfoFormatterTests
AudioChannelPositionsText = "Front: L R"
};
MediaInfoFormatter.FormatAudioChannels(mediaInfoModel).Should().Be(2);
mediaInfoModel.FormattedAudioChannels.Should().Be(2);
}
[Test]
@@ -45,7 +44,7 @@ namespace NzbDrone.Core.Test.MediaFiles.MediaInfo.MediaInfoFormatterTests
SchemaRevision = 2
};
MediaInfoFormatter.FormatAudioChannels(mediaInfoModel).Should().Be(0);
mediaInfoModel.FormattedAudioChannels.Should().Be(0);
}
[Test]
@@ -59,7 +58,7 @@ namespace NzbDrone.Core.Test.MediaFiles.MediaInfo.MediaInfoFormatterTests
SchemaRevision = 3
};
MediaInfoFormatter.FormatAudioChannels(mediaInfoModel).Should().Be(2);
mediaInfoModel.FormattedAudioChannels.Should().Be(2);
}
[Test]
@@ -73,7 +72,7 @@ namespace NzbDrone.Core.Test.MediaFiles.MediaInfo.MediaInfoFormatterTests
SchemaRevision = 3
};
MediaInfoFormatter.FormatAudioChannels(mediaInfoModel).Should().Be(2);
mediaInfoModel.FormattedAudioChannels.Should().Be(2);
}
[Test]
@@ -87,7 +86,7 @@ namespace NzbDrone.Core.Test.MediaFiles.MediaInfo.MediaInfoFormatterTests
SchemaRevision = 3
};
MediaInfoFormatter.FormatAudioChannels(mediaInfoModel).Should().Be(5.1m);
mediaInfoModel.FormattedAudioChannels.Should().Be(5.1m);
}
[Test]
@@ -101,7 +100,7 @@ namespace NzbDrone.Core.Test.MediaFiles.MediaInfo.MediaInfoFormatterTests
SchemaRevision = 3
};
MediaInfoFormatter.FormatAudioChannels(mediaInfoModel).Should().Be(7.1m);
mediaInfoModel.FormattedAudioChannels.Should().Be(7.1m);
}
[Test]
@@ -115,7 +114,7 @@ namespace NzbDrone.Core.Test.MediaFiles.MediaInfo.MediaInfoFormatterTests
SchemaRevision = 3
};
MediaInfoFormatter.FormatAudioChannels(mediaInfoModel).Should().Be(7.1m);
mediaInfoModel.FormattedAudioChannels.Should().Be(7.1m);
}
}
}

View File

@@ -1,49 +0,0 @@
using FluentAssertions;
using NUnit.Framework;
using NzbDrone.Core.MediaFiles.MediaInfo;
using NzbDrone.Test.Common;
namespace NzbDrone.Core.Test.MediaFiles.MediaInfo.MediaInfoFormatterTests
{
[TestFixture]
public class FormatAudioCodecFixture : TestBase
{
[TestCase("AC-3", "AC3")]
[TestCase("E-AC-3", "EAC3")]
[TestCase("MPEG Audio", "MPEG Audio")]
[TestCase("DTS", "DTS")]
public void should_format_audio_format(string audioFormat, string expectedFormat)
{
var mediaInfoModel = new MediaInfoModel
{
AudioFormat = audioFormat
};
MediaInfoFormatter.FormatAudioCodec(mediaInfoModel).Should().Be(expectedFormat);
}
[Test]
public void should_return_MP3_for_MPEG_Audio_with_Layer_3_for_the_profile()
{
var mediaInfoModel = new MediaInfoModel
{
AudioFormat = "MPEG Audio",
AudioProfile = "Layer 3"
};
MediaInfoFormatter.FormatAudioCodec(mediaInfoModel).Should().Be("MP3");
}
[Test]
public void should_return_AudioFormat_by_default()
{
var mediaInfoModel = new MediaInfoModel
{
AudioFormat = "Other Audio Format"
};
MediaInfoFormatter.FormatAudioCodec(mediaInfoModel).Should().Be(mediaInfoModel.AudioFormat);
ExceptionVerification.ExpectedErrors(1);
}
}
}

View File

@@ -1,40 +0,0 @@
using FluentAssertions;
using NUnit.Framework;
using NzbDrone.Core.MediaFiles.MediaInfo;
using NzbDrone.Test.Common;
namespace NzbDrone.Core.Test.MediaFiles.MediaInfo.MediaInfoFormatterTests
{
[TestFixture]
public class FormatVideoCodecFixture : TestBase
{
[TestCase("AVC", null, "x264")]
[TestCase("AVC", "source.title.x264.720p-Sonarr", "x264")]
[TestCase("AVC", "source.title.h264.720p-Sonarr", "h264")]
[TestCase("V_MPEGH/ISO/HEVC", null, "x265")]
[TestCase("V_MPEGH/ISO/HEVC", "source.title.x265.720p-Sonarr", "x265")]
[TestCase("V_MPEGH/ISO/HEVC", "source.title.h265.720p-Sonarr", "h265")]
[TestCase("MPEG-2 Video", null, "MPEG2")]
public void should_format_video_codec_with_source_title(string videoCodec, string sceneName, string expectedFormat)
{
var mediaInfoModel = new MediaInfoModel
{
VideoCodec = videoCodec
};
MediaInfoFormatter.FormatVideoCodec(mediaInfoModel, sceneName).Should().Be(expectedFormat);
}
[Test]
public void should_return_VideoCodec_by_default()
{
var mediaInfoModel = new MediaInfoModel
{
VideoCodec = "VideoCodec"
};
MediaInfoFormatter.FormatVideoCodec(mediaInfoModel, null).Should().Be(mediaInfoModel.VideoCodec);
ExceptionVerification.ExpectedErrors(1);
}
}
}

View File

@@ -1,4 +1,4 @@
using System.IO;
using System.IO;
using FluentAssertions;
using Moq;
using NUnit.Framework;
@@ -30,9 +30,11 @@ namespace NzbDrone.Core.Test.MediaFiles.MediaInfo
{
var path = Path.Combine(TestContext.CurrentContext.TestDirectory, "Files", "Media", "H264_sample.mp4");
Subject.GetRunTime(path).Value.Seconds.Should().Be(10);
Subject.GetRunTime(path).Seconds.Should().Be(10);
}
[Test]
public void get_info()
{
@@ -84,6 +86,7 @@ namespace NzbDrone.Core.Test.MediaFiles.MediaInfo
info.VideoCodec.Should().Be("AVC");
info.VideoFps.Should().Be(24);
info.Width.Should().Be(480);
}
[Test]

View File

@@ -53,7 +53,7 @@ namespace NzbDrone.Core.Test.MetadataSource.SkyHook
{
series.Should().NotBeNull();
series.Title.Should().NotBeNullOrWhiteSpace();
series.CleanTitle.Should().Be(Parser.NormalizeParsedTitle.CleanSeriesTitle(series.Title));
series.CleanTitle.Should().Be(Parser.Parser.CleanSeriesTitle(series.Title));
series.SortTitle.Should().Be(SeriesTitleNormalizer.Normalize(series.Title, series.TvdbId));
series.Overview.Should().NotBeNullOrWhiteSpace();
series.AirTime.Should().NotBeNullOrWhiteSpace();

View File

@@ -161,11 +161,9 @@
<Compile Include="DecisionEngineTests\RssSync\DelaySpecificationFixture.cs" />
<Compile Include="DecisionEngineTests\RssSync\DeletedEpisodeFileSpecificationFixture.cs" />
<Compile Include="DecisionEngineTests\RssSync\ProperSpecificationFixture.cs" />
<Compile Include="DecisionEngineTests\Search\TorrentSeedingSpecificationFixture.cs" />
<Compile Include="DecisionEngineTests\Search\SeriesSpecificationFixture.cs" />
<Compile Include="DecisionEngineTests\SameEpisodesSpecificationFixture.cs" />
<Compile Include="DecisionEngineTests\RawDiskSpecificationFixture.cs" />
<Compile Include="DecisionEngineTests\BlockedIndexerSpecificationFixture.cs" />
<Compile Include="DecisionEngineTests\UpgradeDiskSpecificationFixture.cs" />
<Compile Include="DiskSpace\DiskSpaceServiceFixture.cs" />
<Compile Include="Download\CompletedDownloadServiceFixture.cs" />
@@ -180,7 +178,6 @@
<Compile Include="Download\DownloadClientTests\DownloadStationTests\SharedFolderResolverFixture.cs" />
<Compile Include="Download\DownloadClientTests\DownloadStationTests\UsenetDownloadStationFixture.cs" />
<Compile Include="Download\DownloadClientTests\HadoukenTests\HadoukenFixture.cs" />
<Compile Include="Download\DownloadClientStatusServiceFixture.cs" />
<Compile Include="Download\DownloadClientTests\NzbgetTests\NzbgetFixture.cs" />
<Compile Include="Download\DownloadClientTests\NzbVortexTests\NzbVortexFixture.cs" />
<Compile Include="Download\DownloadClientTests\PneumaticProviderFixture.cs" />
@@ -238,7 +235,6 @@
<Compile Include="Housekeeping\Housekeepers\CleanupOrphanedIndexerStatusFixture.cs" />
<Compile Include="Housekeeping\Housekeepers\CleanupOrphanedHistoryItemsFixture.cs" />
<Compile Include="Housekeeping\Housekeepers\CleanupOrphanedMetadataFilesFixture.cs" />
<Compile Include="Housekeeping\Housekeepers\CleanupDownloadClientUnavailablePendingReleasesFixture.cs" />
<Compile Include="Housekeeping\Housekeepers\CleanupUnusedTagsFixture.cs" />
<Compile Include="Housekeeping\Housekeepers\CleanupOrphanedPendingReleasesFixture.cs" />
<Compile Include="Housekeeping\Housekeepers\FixFutureRunScheduledTasksFixture.cs" />
@@ -284,7 +280,7 @@
<Compile Include="MediaFiles\DownloadedEpisodesImportServiceFixture.cs" />
<Compile Include="MediaFiles\EpisodeFileMovingServiceTests\MoveEpisodeFileFixture.cs" />
<Compile Include="MediaFiles\EpisodeImport\ImportDecisionMakerFixture.cs" />
<Compile Include="MediaFiles\EpisodeImport\DetectSampleFixture.cs" />
<Compile Include="MediaFiles\EpisodeImport\SampleServiceFixture.cs" />
<Compile Include="MediaFiles\EpisodeImport\Specifications\FreeSpaceSpecificationFixture.cs" />
<Compile Include="MediaFiles\EpisodeImport\Specifications\FullSeasonSpecificationFixture.cs" />
<Compile Include="MediaFiles\EpisodeImport\Specifications\SameFileSpecificationFixture.cs" />
@@ -294,11 +290,8 @@
<Compile Include="MediaFiles\EpisodeImport\Specifications\NotUnpackingSpecificationFixture.cs" />
<Compile Include="MediaFiles\EpisodeImport\Specifications\UpgradeSpecificationFixture.cs" />
<Compile Include="MediaFiles\ImportApprovedEpisodesFixture.cs" />
<Compile Include="MediaFiles\MediaFileDeletionService\DeleteEpisodeFileFixture.cs" />
<Compile Include="MediaFiles\MediaFileRepositoryFixture.cs" />
<Compile Include="MediaFiles\MediaInfo\MediaInfoFormatterTests\FormatAudioCodecFixture.cs" />
<Compile Include="MediaFiles\MediaInfo\MediaInfoFormatterTests\FormatVideoCodecFixture.cs" />
<Compile Include="MediaFiles\MediaInfo\MediaInfoFormatterTests\FormatAudioChannelsFixture.cs" />
<Compile Include="MediaFiles\MediaInfo\FormattedAudioChannelsFixture.cs" />
<Compile Include="Messaging\Commands\CommandQueueManagerFixture.cs" />
<Compile Include="MetadataSource\SkyHook\SkyHookProxySearchFixture.cs" />
<Compile Include="MetadataSource\SearchSeriesComparerFixture.cs" />
@@ -308,8 +301,6 @@
<Compile Include="OrganizerTests\FileNameBuilderTests\CleanTitleFixture.cs" />
<Compile Include="OrganizerTests\FileNameBuilderTests\EpisodeTitleCollapseFixture.cs" />
<Compile Include="OrganizerTests\FileNameBuilderTests\MultiEpisodeFixture.cs" />
<Compile Include="OrganizerTests\FileNameBuilderTests\TitleTheFixture.cs" />
<Compile Include="OrganizerTests\NormalizeOfficialTitleFixture.cs" />
<Compile Include="ParserTests\MiniSeriesEpisodeParserFixture.cs" />
<Compile Include="Qualities\RevisionComparableFixture.cs" />
<Compile Include="QueueTests\QueueServiceFixture.cs" />
@@ -375,8 +366,8 @@
<Compile Include="Qualities\QualityModelComparerFixture.cs" />
<Compile Include="RootFolderTests\RootFolderServiceFixture.cs" />
<Compile Include="SeriesStatsTests\SeriesStatisticsFixture.cs" />
<Compile Include="ThingiProviderTests\ProviderStatusServiceFixture.cs" />
<Compile Include="ThingiProviderTests\ProviderBaseFixture.cs" />
<Compile Include="SkyhookNotifications\SkyhookNotificationServiceFixture.cs" />
<Compile Include="ThingiProvider\ProviderBaseFixture.cs" />
<Compile Include="ThingiProviderTests\NullConfigFixture.cs" />
<Compile Include="TvTests\EpisodeServiceTests\FindEpisodeByTitleFixture.cs" />
<Compile Include="TvTests\EpisodeServiceTests\HandleEpisodeFileDeletedFixture.cs" />
@@ -440,9 +431,6 @@
<Content Include="Files\Indexers\TorrentRss\LimeTorrents.xml">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
<Content Include="Files\Indexers\Torznab\torznab_animetosho.xml">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
<Content Include="License.txt" />
<None Include="Files\Indexers\BroadcastheNet\RecentFeed.json">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>

View File

@@ -16,4 +16,4 @@ namespace NzbDrone.Core.Test.OrganizerTests
}
}
}
}

View File

@@ -1,83 +0,0 @@
using System.Collections.Generic;
using System.Linq;
using FizzWare.NBuilder;
using FluentAssertions;
using NUnit.Framework;
using NzbDrone.Core.MediaFiles;
using NzbDrone.Core.Organizer;
using NzbDrone.Core.Qualities;
using NzbDrone.Core.Test.Framework;
using NzbDrone.Core.Tv;
namespace NzbDrone.Core.Test.OrganizerTests.FileNameBuilderTests
{
[TestFixture]
public class TitleTheFixture : CoreTest<FileNameBuilder>
{
private Series _series;
private Episode _episode;
private EpisodeFile _episodeFile;
private NamingConfig _namingConfig;
[SetUp]
public void Setup()
{
_series = Builder<Series>
.CreateNew()
.With(s => s.Title = "South Park")
.Build();
_episode = Builder<Episode>.CreateNew()
.With(e => e.Title = "City Sushi")
.With(e => e.SeasonNumber = 15)
.With(e => e.EpisodeNumber = 6)
.With(e => e.AbsoluteEpisodeNumber = 100)
.Build();
_episodeFile = new EpisodeFile { Quality = new QualityModel(Quality.HDTV720p), ReleaseGroup = "SonarrTest" };
_namingConfig = NamingConfig.Default;
_namingConfig.RenameEpisodes = true;
Mocker.GetMock<INamingConfigService>()
.Setup(c => c.GetConfig()).Returns(_namingConfig);
Mocker.GetMock<IQualityDefinitionService>()
.Setup(v => v.Get(Moq.It.IsAny<Quality>()))
.Returns<Quality>(v => Quality.DefaultQualityDefinitions.First(c => c.Quality == v));
}
[TestCase("The Mist", "Mist, The")]
[TestCase("A Place to Call Home", "Place to Call Home, A")]
[TestCase("An Adventure in Space and Time", "Adventure in Space and Time, An")]
[TestCase("The Flash (2010)", "Flash, The (2010)")]
[TestCase("A League Of Their Own (AU)", "League Of Their Own, A (AU)")]
[TestCase("The Fixer (ZH) (2015)", "Fixer, The (ZH) (2015)")]
[TestCase("The Sixth Sense 2 (Thai)", "Sixth Sense 2, The (Thai)")]
[TestCase("The Amazing Race (Latin America)", "Amazing Race, The (Latin America)")]
[TestCase("The Rat Pack (A&E)", "Rat Pack, The (A&E)")]
[TestCase("The Climax: I (Almost) Got Away With It (2016)", "Climax- I (Almost) Got Away With It, The (2016)")]
//[TestCase("", "")]
public void should_get_expected_title_back(string title, string expected)
{
_series.Title = title;
_namingConfig.StandardEpisodeFormat = "{Series TitleThe}";
Subject.BuildFileName(new List<Episode> { _episode }, _series, _episodeFile)
.Should().Be(expected);
}
[TestCase("A")]
[TestCase("Anne")]
[TestCase("Theodore")]
[TestCase("3%")]
public void should_not_change_title(string title)
{
_series.Title = title;
_namingConfig.StandardEpisodeFormat = "{Series TitleThe}";
Subject.BuildFileName(new List<Episode> { _episode }, _series, _episodeFile)
.Should().Be(title);
}
}
}

View File

@@ -1,126 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using FluentAssertions;
using NUnit.Framework;
using NzbDrone.Core.Organizer;
using NzbDrone.Core.Test.Framework;
namespace NzbDrone.Core.Test.OrganizerTests
{
[TestFixture]
public class NormalizeOfficialTitleFixture : CoreTest
{
[TestCase("$#*! My Dad Says", "S#* My Dad Says")]
//[TestCase("", "")]
public void should_scenify_special_cases(string title, string expected)
{
// These need special handling on a case by case basis.
NormalizeOfficialTitle.ScenifyTitle(title).Should().Be(expected);
}
[TestCase("@midnight", "At midnight")]
[TestCase("Murder @ 9", "Murder at 9")]
[TestCase("T@gged", "Tagged")]
[TestCase("PUCHIM@S", "PUCHIMAS")]
[TestCase("extr@", "extra")]
[TestCase("Live@Much", "Live at Much")]
//[TestCase("", "")]
public void should_scenify_at_char(string title, string expected)
{
NormalizeOfficialTitle.ScenifyTitle(title).Should().Be(expected);
}
[TestCase("3%", "3 Percent")]
//[TestCase("", "")]
public void should_scenify_percent_char(string title, string expected)
{
NormalizeOfficialTitle.ScenifyTitle(title).Should().Be(expected);
}
[TestCase("Law & Order (UK)", "Law and Order UK")]
[TestCase("Sun, Sea and A&E", "Sun Sea and A and E")]
//[TestCase("", "")]
public void should_scenify_and_char(string title, string expected)
{
NormalizeOfficialTitle.ScenifyTitle(title).Should().Be(expected);
}
[TestCase("Code:Breaker", "Code Breaker")]
[TestCase("Transformers: Prime", "Transformers Prime")]
[TestCase("Mobile Suit Gundam UC RE:0096", "Mobile Suit Gundam UC RE 0096")]
[TestCase("What the Bleep!?: Down the Rabbit Hole", "What the Bleep Down the Rabbit Hole")]
//[TestCase("", "")]
public void should_scenify_colon_char(string title, string expected)
{
NormalizeOfficialTitle.ScenifyTitle(title).Should().Be(expected);
}
[TestCase("Sun, Sea and A&E", "Sun Sea and A and E")]
[TestCase("The $25,000 Pyramid", "The 25000 Pyramid")]
//[TestCase("", "")]
public void should_scenify_comma_char(string title, string expected)
{
NormalizeOfficialTitle.ScenifyTitle(title).Should().Be(expected);
}
//[TestCase("The $100,000 Pyramid", "The 100000 Dollar Pyramid")]
[TestCase("$25 Million Dollar Hoax", "25 Million Dollar Hoax")]
[TestCase("Arli$$", "Arliss")]
[TestCase("Country Buck$", "Country Bucks")]
[TestCase("Tamara Ecclestone: Billion $$ Girl", "Tamara Ecclestone Billion Dollar Girl")]
[TestCase("$#*! My Dad Says", "S#* My Dad Says")]
//[TestCase("", "")]
public void should_scenify_dollar_char(string title, string expected)
{
NormalizeOfficialTitle.ScenifyTitle(title).Should().Be(expected);
}
[TestCase("Separation?!", "Separation")]
[TestCase("Snog Marry Avoid?", "Snog Marry Avoid")]
[TestCase("What the Bleep!?: Down the Rabbit Hole", "What the Bleep Down the Rabbit Hole")]
//[TestCase("", "")]
public void should_scenify_question_char(string title, string expected)
{
NormalizeOfficialTitle.ScenifyTitle(title).Should().Be(expected);
}
[TestCase("Separation?!", "Separation")]
[TestCase("What the Bleep!?: Down the Rabbit Hole", "What the Bleep Down the Rabbit Hole")]
[TestCase("What's Happening!!", "Whats Happening")]
//[TestCase("", "")]
public void should_scenify_exclamation_char(string title, string expected)
{
NormalizeOfficialTitle.ScenifyTitle(title).Should().Be(expected);
}
[TestCase("Bro'Town", "Bro Town")]
[TestCase("'Til Death", "Til Death")]
[TestCase("Those Who Can't", "Those Who Cant")]
[TestCase("Paul O'Grady: For the Love of Dogs", "Paul O Grady For the Love of Dogs")]
[TestCase("Bitchin' Rides", "Bitchin Rides")]
[TestCase("Trust Me, I'm a Vet", "Trust Me Im a Vet")]
[TestCase("You're the Worst", "Youre the Worst")]
//[TestCase("", "")]
public void should_scenify_quote_char(string title, string expected)
{
NormalizeOfficialTitle.ScenifyTitle(title).Should().Be(expected);
}
[TestCase("Robotics;Notes", "Robotics Notes")]
[TestCase("Myself; Yourself", "Myself Yourself")]
//[TestCase("", "")]
public void should_scenify_semicolon_char(string title, string expected)
{
NormalizeOfficialTitle.ScenifyTitle(title).Should().Be(expected);
}
[TestCase("Acquisitions Incorporated: The \"C\" Team", "Acquisitions Incorporated The C Team")]
//[TestCase("", "")]
public void should_scenify_doublequote_char(string title, string expected)
{
NormalizeOfficialTitle.ScenifyTitle(title).Should().Be(expected);
}
}
}

View File

@@ -134,23 +134,5 @@ namespace NzbDrone.Core.Test.ParserTests
{
"Tokyo Ghoul A".CleanSeriesTitle().Should().Be("tokyoghoula");
}
[TestCase("A 120% deal", "a120percentdeal")]
[TestCase("The z0%e", "thez0e")]
[TestCase("That f$%king mess", "thatfkingmess")]
public void should_replace_percentage_character(string title, string normalizedTitle)
{
title.CleanSeriesTitle().Should().Be(normalizedTitle);
}
[TestCase("@midnight", "atmidnight")]
[TestCase("Murder @ 9", "murderat9")]
[TestCase("T@gged", "tagged")]
[TestCase("PUCHIM@S", "puchimas")]
[TestCase("Live@Much", "liveamuch")] // liveatmuch
public void should_replace_at_character(string title, string normalizedTitle)
{
title.CleanSeriesTitle().Should().Be(normalizedTitle);
}
}
}

View File

@@ -0,0 +1,172 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using FluentAssertions;
using Moq;
using NUnit.Framework;
using NzbDrone.Common.EnvironmentInfo;
using NzbDrone.Core.SkyhookNotifications;
using NzbDrone.Core.Test.Framework;
namespace NzbDrone.Core.Test.SkyhookNotifications
{
public class SkyhookNotificationServiceFixture : CoreTest<SkyhookNotificationService>
{
private static readonly Version _previousVersion = new Version(BuildInfo.Version.Major, BuildInfo.Version.Minor, BuildInfo.Version.Build, BuildInfo.Version.Revision - 1);
private static readonly Version _currentVersion = BuildInfo.Version;
private static readonly Version _nextVersion = new Version(BuildInfo.Version.Major, BuildInfo.Version.Minor, BuildInfo.Version.Build, BuildInfo.Version.Revision + 1);
private List<SkyhookNotification> _expiredNotifications;
private List<SkyhookNotification> _currentNotifications;
private List<SkyhookNotification> _futureNotifications;
private List<SkyhookNotification> _urlNotifications;
[SetUp]
public void SetUp()
{
_expiredNotifications = new List<SkyhookNotification>
{
new SkyhookNotification
{
Id = 1,
Type = SkyhookNotificationType.Notification,
Title = "Expired Notification",
MaximumVersion = _previousVersion.ToString()
}
};
_currentNotifications = new List<SkyhookNotification>
{
new SkyhookNotification
{
Id = 2,
Type = SkyhookNotificationType.Notification,
Title = "Timeless current Notification"
},
new SkyhookNotification
{
Id = 2,
Type = SkyhookNotificationType.Notification,
Title = "Ending current Notification",
MaximumVersion = _currentVersion.ToString()
},
new SkyhookNotification
{
Id = 2,
Type = SkyhookNotificationType.Notification,
Title = "Ending future Notification",
MaximumVersion = _nextVersion.ToString()
},
new SkyhookNotification
{
Id = 2,
Type = SkyhookNotificationType.Notification,
Title = "Starting previous Notification",
MinimumVersion = _previousVersion.ToString()
},
new SkyhookNotification
{
Id = 2,
Type = SkyhookNotificationType.Notification,
Title = "Starting current Notification",
MinimumVersion = _currentVersion.ToString()
}
};
_futureNotifications = new List<SkyhookNotification>
{
new SkyhookNotification
{
Id = 3,
Type = SkyhookNotificationType.Notification,
Title = "Future Notification",
MinimumVersion = _nextVersion.ToString()
}
};
_urlNotifications = new List<SkyhookNotification>
{
new SkyhookNotification
{
Id = 3,
Type = SkyhookNotificationType.UrlBlacklist,
Title = "Future Notification"
},
new SkyhookNotification
{
Id = 3,
Type = SkyhookNotificationType.UrlReplace,
Title = "Future Notification"
}
};
}
private void GivenNotifications(List<SkyhookNotification> notifications)
{
Mocker.GetMock<ISkyhookNotificationProxy>()
.Setup(v => v.GetNotifications())
.Returns(notifications);
}
[Test]
public void should_not_return_expired_notifications()
{
GivenNotifications(_expiredNotifications);
Subject.GetUserNotifications().Should().BeEmpty();
}
[Test]
public void should_not_return_future_notifications()
{
GivenNotifications(_futureNotifications);
Subject.GetUserNotifications().Should().BeEmpty();
}
[Test]
public void should_return_current_notifications()
{
GivenNotifications(_currentNotifications);
Subject.GetUserNotifications().Should().HaveCount(_currentNotifications.Count);
}
[Test]
public void should_not_return_user_notifications()
{
GivenNotifications(_currentNotifications);
Subject.GetUrlNotifications().Should().BeEmpty();
}
[Test]
public void should_not_return_url_notifications()
{
GivenNotifications(_urlNotifications);
Subject.GetUserNotifications().Should().BeEmpty();
}
[Test]
public void should_return_url_notifications()
{
GivenNotifications(_urlNotifications);
Subject.GetUrlNotifications().Should().HaveCount(_urlNotifications.Count);
}
[Test]
public void should_cache_api_result()
{
GivenNotifications(_urlNotifications);
Subject.GetUrlNotifications();
Subject.GetUrlNotifications();
Mocker.GetMock<ISkyhookNotificationProxy>()
.Verify(v => v.GetNotifications(), Times.Once());
}
}
}

View File

@@ -5,8 +5,9 @@ using NzbDrone.Core.Indexers;
using NzbDrone.Core.Indexers.Newznab;
using NzbDrone.Core.Test.Framework;
namespace NzbDrone.Core.Test.ThingiProviderTests
namespace NzbDrone.Core.Test.ThingiProvider
{
public class ProviderRepositoryFixture : DbTest<IndexerRepository, IndexerDefinition>
{
[Test]
@@ -26,4 +27,4 @@ namespace NzbDrone.Core.Test.ThingiProviderTests
storedSetting.ShouldBeEquivalentTo(newznabSettings, o=>o.IncludingAllRuntimeProperties());
}
}
}
}

View File

@@ -14,4 +14,4 @@ namespace NzbDrone.Core.Test.ThingiProviderTests
Subject.Validate().IsValid.Should().BeTrue();
}
}
}
}

View File

@@ -1,126 +0,0 @@
using System;
using System.Linq;
using FluentAssertions;
using Moq;
using NLog;
using NUnit.Framework;
using NzbDrone.Core.Messaging.Events;
using NzbDrone.Core.Test.Framework;
using NzbDrone.Core.ThingiProvider;
using NzbDrone.Core.ThingiProvider.Status;
namespace NzbDrone.Core.Test.ThingiProviderTests
{
public class MockProviderStatus : ProviderStatusBase
{
}
public interface IMockProvider : IProvider
{
}
public interface IMockProviderStatusRepository : IProviderStatusRepository<MockProviderStatus>
{
}
public class MockProviderStatusService : ProviderStatusServiceBase<IMockProvider, MockProviderStatus>
{
public MockProviderStatusService(IMockProviderStatusRepository providerStatusRepository, IEventAggregator eventAggregator, Logger logger)
: base(providerStatusRepository, eventAggregator, logger)
{
}
}
public class ProviderStatusServiceFixture : CoreTest<MockProviderStatusService>
{
private DateTime _epoch;
[SetUp]
public void SetUp()
{
_epoch = DateTime.UtcNow;
}
private void WithStatus(MockProviderStatus status)
{
Mocker.GetMock<IMockProviderStatusRepository>()
.Setup(v => v.FindByProviderId(1))
.Returns(status);
Mocker.GetMock<IMockProviderStatusRepository>()
.Setup(v => v.All())
.Returns(new[] { status });
}
private void VerifyUpdate()
{
Mocker.GetMock<IMockProviderStatusRepository>()
.Verify(v => v.Upsert(It.IsAny<MockProviderStatus>()), Times.Once());
}
private void VerifyNoUpdate()
{
Mocker.GetMock<IMockProviderStatusRepository>()
.Verify(v => v.Upsert(It.IsAny<MockProviderStatus>()), Times.Never());
}
[Test]
public void should_start_backoff_on_first_failure()
{
WithStatus(new MockProviderStatus());
Subject.RecordFailure(1);
VerifyUpdate();
var status = Subject.GetBlockedProviders().FirstOrDefault();
status.Should().NotBeNull();
status.DisabledTill.Should().HaveValue();
status.DisabledTill.Value.Should().BeCloseTo(_epoch + TimeSpan.FromMinutes(5), 500);
}
[Test]
public void should_cancel_backoff_on_success()
{
WithStatus(new MockProviderStatus { EscalationLevel = 2 });
Subject.RecordSuccess(1);
VerifyUpdate();
var status = Subject.GetBlockedProviders().FirstOrDefault();
status.Should().BeNull();
}
[Test]
public void should_not_store_update_if_already_okay()
{
WithStatus(new MockProviderStatus { EscalationLevel = 0 });
Subject.RecordSuccess(1);
VerifyNoUpdate();
}
[Test]
public void should_preserve_escalation_on_intermittent_success()
{
WithStatus(new MockProviderStatus
{
InitialFailure = _epoch - TimeSpan.FromSeconds(20),
MostRecentFailure = _epoch - TimeSpan.FromSeconds(4),
EscalationLevel = 3
});
Subject.RecordSuccess(1);
Subject.RecordSuccess(1);
Subject.RecordFailure(1);
var status = Subject.GetBlockedProviders().FirstOrDefault();
status.Should().NotBeNull();
status.DisabledTill.Should().HaveValue();
status.DisabledTill.Value.Should().BeCloseTo(_epoch + TimeSpan.FromMinutes(15), 500);
}
}
}

View File

@@ -194,46 +194,6 @@ namespace NzbDrone.Core.Test.TvTests.EpisodeMonitoredServiceTests
.Verify(v => v.UpdateEpisodes(It.Is<List<Episode>>(l => l.All(e => !e.Monitored))));
}
[Test]
public void should_should_not_monitor_episodes_if_season_is_not_monitored()
{
_series = Builder<Series>.CreateNew()
.With(s => s.Seasons = Builder<Season>.CreateListOfSize(2)
.TheFirst(1)
.With(n => n.Monitored = true)
.TheLast(1)
.With(n => n.Monitored = false)
.Build()
.ToList())
.Build();
var episodes = Builder<Episode>.CreateListOfSize(10)
.All()
.With(e => e.Monitored = true)
.With(e => e.EpisodeFileId = 0)
.With(e => e.AirDateUtc = DateTime.UtcNow.AddDays(-7))
.TheFirst(5)
.With(e => e.SeasonNumber = 1)
.TheLast(5)
.With(e => e.SeasonNumber = 2)
.BuildList();
Mocker.GetMock<IEpisodeService>()
.Setup(s => s.GetEpisodeBySeries(It.IsAny<int>()))
.Returns(episodes);
Subject.SetEpisodeMonitoredStatus(_series, new MonitoringOptions
{
IgnoreEpisodesWithFiles = true,
IgnoreEpisodesWithoutFiles = false
});
VerifyMonitored(e => e.SeasonNumber == 1);
VerifyNotMonitored(e => e.SeasonNumber == 2);
VerifySeasonMonitored(s => s.SeasonNumber == 1);
VerifySeasonNotMonitored(s => s.SeasonNumber == 2);
}
private void VerifyMonitored(Func<Episode, bool> predicate)
{
Mocker.GetMock<IEpisodeService>()

View File

@@ -1,4 +1,4 @@
using System;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
@@ -142,21 +142,7 @@ namespace NzbDrone.Core.Configuration
public bool LaunchBrowser => GetValueBoolean("LaunchBrowser", true);
public string ApiKey
{
get
{
var apiKey = GetValue("ApiKey", GenerateApiKey());
if (apiKey.IsNullOrWhiteSpace())
{
apiKey = GenerateApiKey();
SetValue("ApiKey", apiKey);
}
return apiKey;
}
}
public string ApiKey => GetValue("ApiKey", GenerateApiKey());
public AuthenticationType AuthenticationMethod
{

View File

@@ -61,15 +61,7 @@ namespace NzbDrone.Core.DataAugmentation.Xem
if (episode == null)
{
_logger.Debug("Information hasn't been added to TheTVDB yet, skipping");
continue;
}
if (mapping.Scene.Absolute == 0 &&
mapping.Scene.Season == 0 &&
mapping.Scene.Episode == 0)
{
_logger.Debug("Mapping for {0} S{1:00}E{2:00} is invalid, skipping", series, mapping.Tvdb.Season, mapping.Tvdb.Episode);
_logger.Debug("Information hasn't been added to TheTVDB yet, skipping.");
continue;
}

View File

@@ -27,7 +27,7 @@ namespace NzbDrone.Core.Datastore.Migration
var id = seriesReader.GetInt32(0);
var title = seriesReader.GetString(1);
var sortTitle = Parser.NormalizeParsedTitle.NormalizeTitle(title).ToLower();
var sortTitle = Parser.Parser.NormalizeTitle(title).ToLower();
using (IDbCommand updateCmd = conn.CreateCommand())
{

View File

@@ -1,14 +0,0 @@
using FluentMigrator;
using NzbDrone.Core.Datastore.Migration.Framework;
namespace NzbDrone.Core.Datastore.Migration
{
[Migration(97)]
public class add_reason_to_pending_releases : NzbDroneMigrationBase
{
protected override void MainDbUpgrade()
{
Alter.Table("PendingReleases").AddColumn("Reason").AsInt32().WithDefaultValue(0);
}
}
}

View File

@@ -1,19 +0,0 @@
using FluentMigrator;
using NzbDrone.Core.Datastore.Migration.Framework;
namespace NzbDrone.Core.Datastore.Migration
{
[Migration(115)]
public class add_downloadclient_status : NzbDroneMigrationBase
{
protected override void MainDbUpgrade()
{
Create.TableForModel("DownloadClientStatus")
.WithColumn("ProviderId").AsInt32().NotNullable().Unique()
.WithColumn("InitialFailure").AsDateTime().Nullable()
.WithColumn("MostRecentFailure").AsDateTime().Nullable()
.WithColumn("EscalationLevel").AsInt32().NotNullable()
.WithColumn("DisabledTill").AsDateTime().Nullable();
}
}
}

View File

@@ -1,14 +0,0 @@
using FluentMigrator;
using NzbDrone.Core.Datastore.Migration.Framework;
namespace NzbDrone.Core.Datastore.Migration
{
[Migration(116)]
public class disable_nyaa : NzbDroneMigrationBase
{
protected override void MainDbUpgrade()
{
Execute.Sql("UPDATE Indexers SET EnableRss = 0, EnableSearch = 0, Settings = Replace(Settings, 'https://nyaa.se', '') WHERE Implementation = 'Nyaa' AND Settings LIKE '%nyaa.se%';");
}
}
}

View File

@@ -60,7 +60,7 @@ namespace NzbDrone.Core.Datastore
.Ignore(i => i.SupportsOnDownload)
.Ignore(i => i.SupportsOnUpgrade)
.Ignore(i => i.SupportsOnRename);
Mapper.Entity<MetadataDefinition>().RegisterDefinition("Metadata");
Mapper.Entity<DownloadClientDefinition>().RegisterDefinition("DownloadClients")
@@ -80,7 +80,7 @@ namespace NzbDrone.Core.Datastore
.Ignore(f => f.Path)
.Relationships.AutoMapICollectionOrComplexProperties()
.For("Episodes")
.LazyLoad(condition: parent => parent.Id > 0,
.LazyLoad(condition: parent => parent.Id > 0,
query: (db, parent) => db.Query<Episode>().Where(c => c.EpisodeFileId == parent.Id).ToList())
.HasOne(file => file.Series, file => file.SeriesId);
@@ -116,7 +116,6 @@ namespace NzbDrone.Core.Datastore
.Ignore(c => c.Message);
Mapper.Entity<IndexerStatus>().RegisterModel("IndexerStatus");
Mapper.Entity<DownloadClientStatus>().RegisterModel("DownloadClientStatus");
}
private static void RegisterMappers()
@@ -172,4 +171,4 @@ namespace NzbDrone.Core.Datastore
}
}
}
}
}

View File

@@ -1,46 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using NLog;
using NzbDrone.Common.Cache;
using NzbDrone.Core.Indexers;
using NzbDrone.Core.IndexerSearch.Definitions;
using NzbDrone.Core.Parser.Model;
namespace NzbDrone.Core.DecisionEngine.Specifications
{
public class BlockedIndexerSpecification : IDecisionEngineSpecification
{
private readonly IIndexerStatusService _indexerStatusService;
private readonly Logger _logger;
private readonly ICachedDictionary<IndexerStatus> _blockedIndexerCache;
public BlockedIndexerSpecification(IIndexerStatusService indexerStatusService, ICacheManager cacheManager, Logger logger)
{
_indexerStatusService = indexerStatusService;
_logger = logger;
_blockedIndexerCache = cacheManager.GetCacheDictionary(GetType(), "blocked", FetchBlockedIndexer, TimeSpan.FromSeconds(15));
}
public SpecificationPriority Priority => SpecificationPriority.Database;
public RejectionType Type => RejectionType.Temporary;
public virtual Decision IsSatisfiedBy(RemoteEpisode subject, SearchCriteriaBase searchCriteria)
{
var status = _blockedIndexerCache.Find(subject.Release.IndexerId.ToString());
if (status != null)
{
return Decision.Reject($"Indexer {subject.Release.Indexer} is blocked till {status.DisabledTill} due to failures, cannot grab release.");
}
return Decision.Accept();
}
private IDictionary<string, IndexerStatus> FetchBlockedIndexer()
{
return _indexerStatusService.GetBlockedProviders().ToDictionary(v => v.ProviderId.ToString());
}
}
}

View File

@@ -1,5 +1,4 @@
using System;
using NLog;
using NLog;
using NzbDrone.Core.Configuration;
using NzbDrone.Core.IndexerSearch.Definitions;
using NzbDrone.Core.Parser.Model;
@@ -30,7 +29,6 @@ namespace NzbDrone.Core.DecisionEngine.Specifications
var age = subject.Release.AgeMinutes;
var minimumAge = _configService.MinimumAge;
var ageRounded = Math.Round(age, 1);
if (minimumAge == 0)
{
@@ -39,15 +37,15 @@ namespace NzbDrone.Core.DecisionEngine.Specifications
}
_logger.Debug("Checking if report meets minimum age requirements. {0}", ageRounded);
_logger.Debug("Checking if report meets minimum age requirements. {0}", age);
if (age < minimumAge)
{
_logger.Debug("Only {0} minutes old, minimum age is {1} minutes", ageRounded, minimumAge);
return Decision.Reject("Only {0} minutes old, minimum age is {1} minutes", ageRounded, minimumAge);
_logger.Debug("Only {0} minutes old, minimum age is {1} minutes", age, minimumAge);
return Decision.Reject("Only {0} minutes old, minimum age is {1} minutes", age, minimumAge);
}
_logger.Debug("Release is {0} minutes old, greater than minimum age of {1} minutes", ageRounded, minimumAge);
_logger.Debug("Release is {0} minutes old, greater than minimum age of {1} minutes", age, minimumAge);
return Decision.Accept();
}

View File

@@ -0,0 +1,38 @@
using NLog;
using NzbDrone.Core.IndexerSearch.Definitions;
using NzbDrone.Core.Parser.Model;
namespace NzbDrone.Core.DecisionEngine.Specifications.Search
{
public class TorrentSeedingSpecification : IDecisionEngineSpecification
{
private readonly Logger _logger;
public TorrentSeedingSpecification(Logger logger)
{
_logger = logger;
}
public SpecificationPriority Priority => SpecificationPriority.Default;
public RejectionType Type => RejectionType.Permanent;
public Decision IsSatisfiedBy(RemoteEpisode remoteEpisode, SearchCriteriaBase searchCriteria)
{
var torrentInfo = remoteEpisode.Release as TorrentInfo;
if (torrentInfo == null)
{
return Decision.Accept();
}
if (torrentInfo.Seeders != null && torrentInfo.Seeders < 1)
{
_logger.Debug("Not enough seeders. ({0})", torrentInfo.Seeders);
return Decision.Reject("Not enough seeders. ({0})", torrentInfo.Seeders);
}
return Decision.Accept();
}
}
}

View File

@@ -1,60 +0,0 @@
using NLog;
using NzbDrone.Core.Datastore;
using NzbDrone.Core.Indexers;
using NzbDrone.Core.IndexerSearch.Definitions;
using NzbDrone.Core.Parser.Model;
namespace NzbDrone.Core.DecisionEngine.Specifications.Search
{
public class TorrentSeedingSpecification : IDecisionEngineSpecification
{
private readonly IIndexerFactory _indexerFactory;
private readonly Logger _logger;
public TorrentSeedingSpecification(IIndexerFactory indexerFactory, Logger logger)
{
_indexerFactory = indexerFactory;
_logger = logger;
}
public SpecificationPriority Priority => SpecificationPriority.Default;
public RejectionType Type => RejectionType.Permanent;
public Decision IsSatisfiedBy(RemoteEpisode remoteEpisode, SearchCriteriaBase searchCriteria)
{
var torrentInfo = remoteEpisode.Release as TorrentInfo;
if (torrentInfo == null || torrentInfo.IndexerId == 0)
{
return Decision.Accept();
}
IndexerDefinition indexer;
try
{
indexer = _indexerFactory.Get(torrentInfo.IndexerId);
}
catch (ModelNotFoundException)
{
_logger.Debug("Indexer with id {0} does not exist, skipping seeders check", torrentInfo.IndexerId);
return Decision.Accept();
}
var torrentIndexerSettings = indexer.Settings as ITorrentIndexerSettings;
if (torrentIndexerSettings != null)
{
var minimumSeeders = torrentIndexerSettings.MinimumSeeders;
if (torrentInfo.Seeders.HasValue && torrentInfo.Seeders.Value < minimumSeeders)
{
_logger.Debug("Not enough seeders: {0}. Minimum seeders: {1}", torrentInfo.Seeders, minimumSeeders);
return Decision.Reject("Not enough seeders: {0}. Minimum seeders: {1}", torrentInfo.Seeders, minimumSeeders);
}
}
return Decision.Accept();
}
}
}

View File

@@ -81,13 +81,21 @@ namespace NzbDrone.Core.Download.Clients.Deluge
{
IEnumerable<DelugeTorrent> torrents;
if (!Settings.TvCategory.IsNullOrWhiteSpace())
try
{
torrents = _proxy.GetTorrentsByLabel(Settings.TvCategory, Settings);
if (!Settings.TvCategory.IsNullOrWhiteSpace())
{
torrents = _proxy.GetTorrentsByLabel(Settings.TvCategory, Settings);
}
else
{
torrents = _proxy.GetTorrents(Settings);
}
}
else
catch (DownloadClientException ex)
{
torrents = _proxy.GetTorrents(Settings);
_logger.Error(ex, "Couldn't get list of torrents");
return Enumerable.Empty<DownloadClientItem>();
}
var items = new List<DownloadClientItem>();
@@ -183,13 +191,12 @@ namespace NzbDrone.Core.Download.Clients.Deluge
}
catch (DownloadClientAuthenticationException ex)
{
_logger.Error(ex, ex.Message);
_logger.Error(ex);
return new NzbDroneValidationFailure("Password", "Authentication failed");
}
catch (WebException ex)
{
_logger.Error(ex, "Unble to test connection");
_logger.Error(ex);
switch (ex.Status)
{
case WebExceptionStatus.ConnectFailure:
@@ -213,7 +220,7 @@ namespace NzbDrone.Core.Download.Clients.Deluge
}
catch (Exception ex)
{
_logger.Error(ex, "Failed to test connection");
_logger.Error(ex);
return new NzbDroneValidationFailure(string.Empty, "Unknown exception: " + ex.Message);
}
@@ -264,7 +271,7 @@ namespace NzbDrone.Core.Download.Clients.Deluge
}
catch (Exception ex)
{
_logger.Error(ex, "Unable to get torrents");
_logger.Error(ex);
return new NzbDroneValidationFailure(string.Empty, "Failed to get the list of torrents: " + ex.Message);
}

View File

@@ -231,7 +231,7 @@ namespace NzbDrone.Core.Download.Clients.Deluge
}
catch (WebException ex)
{
throw new DownloadClientUnavailableException("Unable to connect to Deluge, please check your settings", ex);
throw new DownloadClientException("Unable to connect to Deluge, please check your settings", ex);
}
}

View File

@@ -8,16 +8,19 @@ namespace NzbDrone.Core.Download.Clients
public DownloadClientException(string message, params object[] args)
: base(string.Format(message, args))
{
}
public DownloadClientException(string message)
: base(message)
{
}
public DownloadClientException(string message, Exception innerException, params object[] args)
: base(string.Format(message, args), innerException)
{
}
public DownloadClientException(string message, Exception innerException)

View File

@@ -1,27 +0,0 @@
using System;
namespace NzbDrone.Core.Download.Clients
{
public class DownloadClientUnavailableException : DownloadClientException
{
public DownloadClientUnavailableException(string message, params object[] args)
: base(string.Format(message, args))
{
}
public DownloadClientUnavailableException(string message)
: base(message)
{
}
public DownloadClientUnavailableException(string message, Exception innerException, params object[] args)
: base(string.Format(message, args), innerException)
{
}
public DownloadClientUnavailableException(string message, Exception innerException)
: base(message, innerException)
{
}
}
}

View File

@@ -72,20 +72,7 @@ namespace NzbDrone.Core.Download.Clients.DownloadStation.Proxies
DownloadStationSettings settings) where T : new()
{
var request = requestBuilder.Build();
HttpResponse response;
try
{
response = _httpClient.Execute(request);
}
catch (HttpException ex)
{
throw new DownloadClientException("Unable to connect to Diskstation, please check your settings", ex);
}
catch (WebException ex)
{
throw new DownloadClientUnavailableException("Unable to connect to Diskstation, please check your settings", ex);
}
var response = _httpClient.Execute(request);
_logger.Debug("Trying to {0}", operation);

View File

@@ -320,12 +320,12 @@ namespace NzbDrone.Core.Download.Clients.DownloadStation
}
catch (DownloadClientAuthenticationException ex) // User could not have permission to access to downloadstation
{
_logger.Error(ex, ex.Message);
_logger.Error(ex);
return new NzbDroneValidationFailure(string.Empty, ex.Message);
}
catch (Exception ex)
{
_logger.Error(ex, "Error testing Torrent Download Station");
_logger.Error(ex);
return new NzbDroneValidationFailure(string.Empty, $"Unknown exception: {ex.Message}");
}
}
@@ -346,7 +346,7 @@ namespace NzbDrone.Core.Download.Clients.DownloadStation
}
catch (WebException ex)
{
_logger.Error(ex, "Unable to connect to Torrent Download Station");
_logger.Error(ex);
if (ex.Status == WebExceptionStatus.ConnectFailure)
{
@@ -359,7 +359,7 @@ namespace NzbDrone.Core.Download.Clients.DownloadStation
}
catch (Exception ex)
{
_logger.Error(ex, "Error testing Torrent Download Station");
_logger.Error(ex);
return new NzbDroneValidationFailure(string.Empty, $"Unknown exception: {ex.Message}");
}
}

View File

@@ -234,12 +234,12 @@ namespace NzbDrone.Core.Download.Clients.DownloadStation
}
catch (DownloadClientAuthenticationException ex) // User could not have permission to access to downloadstation
{
_logger.Error(ex, ex.Message);
_logger.Error(ex);
return new NzbDroneValidationFailure(string.Empty, ex.Message);
}
catch (Exception ex)
{
_logger.Error(ex, "Error testing Usenet Download Station");
_logger.Error(ex);
return new NzbDroneValidationFailure(string.Empty, $"Unknown exception: {ex.Message}");
}
}
@@ -260,7 +260,7 @@ namespace NzbDrone.Core.Download.Clients.DownloadStation
}
catch (WebException ex)
{
_logger.Error(ex, "Unable to connect to Usenet Download Station");
_logger.Error(ex);
if (ex.Status == WebExceptionStatus.ConnectFailure)
{
@@ -273,7 +273,7 @@ namespace NzbDrone.Core.Download.Clients.DownloadStation
}
catch (Exception ex)
{
_logger.Error(ex, "Error testing Torrent Download Station");
_logger.Error(ex);
return new NzbDroneValidationFailure(string.Empty, "Unknown exception: " + ex.Message);
}
}

View File

@@ -35,7 +35,17 @@ namespace NzbDrone.Core.Download.Clients.Hadouken
public override IEnumerable<DownloadClientItem> GetItems()
{
var torrents = _proxy.GetTorrents(Settings);
HadoukenTorrent[] torrents;
try
{
torrents = _proxy.GetTorrents(Settings);
}
catch (DownloadClientException ex)
{
_logger.ErrorException(ex.Message, ex);
return Enumerable.Empty<DownloadClientItem>();
}
var items = new List<DownloadClientItem>();

View File

@@ -77,21 +77,7 @@ namespace NzbDrone.Core.Download.Clients.Hadouken
requestBuilder.Headers.Add("Accept-Encoding", "gzip,deflate");
var httpRequest = requestBuilder.Build();
HttpResponse response;
try
{
response = _httpClient.Execute(httpRequest);
}
catch (HttpException ex)
{
throw new DownloadClientException("Unable to connect to Hadouken, please check your settings", ex);
}
catch (WebException ex)
{
throw new DownloadClientUnavailableException("Unable to connect to Hadouken, please check your settings", ex);
}
var response = _httpClient.Execute(httpRequest);
var result = Json.Deserialize<JsonRpcResponse<T>>(response.Content);
if (result.Error != null)

View File

@@ -47,7 +47,17 @@ namespace NzbDrone.Core.Download.Clients.NzbVortex
public override IEnumerable<DownloadClientItem> GetItems()
{
var vortexQueue = _proxy.GetQueue(30, Settings);
List<NzbVortexQueueItem> vortexQueue;
try
{
vortexQueue = _proxy.GetQueue(30, Settings);
}
catch (DownloadClientException ex)
{
_logger.Warn("Couldn't get download queue. {0}", ex.Message);
return Enumerable.Empty<DownloadClientItem>();
}
var queueItems = new List<DownloadClientItem>();
@@ -158,7 +168,7 @@ namespace NzbDrone.Core.Download.Clients.NzbVortex
}
catch (Exception ex)
{
_logger.Error(ex, "Unable to connect to NZBVortex");
_logger.Error(ex);
return new ValidationFailure("Host", "Unable to connect to NZBVortex");
}
@@ -179,7 +189,7 @@ namespace NzbDrone.Core.Download.Clients.NzbVortex
}
catch (Exception ex)
{
_logger.Error(ex, "Unable to connect to NZBVortex");
_logger.Error(ex);
return new ValidationFailure("Host", "Unable to connect to NZBVortex");
}

View File

@@ -164,7 +164,7 @@ namespace NzbDrone.Core.Download.Clients.NzbVortex
}
catch (WebException ex)
{
throw new DownloadClientUnavailableException("Unable to connect to NZBVortex, please check your settings", ex);
throw new DownloadClientException("Unable to connect to NZBVortex, please check your settings", ex);
}
}

View File

@@ -1,8 +1,7 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Collections.Generic;
using FluentValidation.Results;
using NLog;
using NzbDrone.Common.Disk;
@@ -10,8 +9,8 @@ using NzbDrone.Common.Extensions;
using NzbDrone.Common.Http;
using NzbDrone.Core.Configuration;
using NzbDrone.Core.Parser.Model;
using NzbDrone.Core.RemotePathMappings;
using NzbDrone.Core.Validation;
using NzbDrone.Core.RemotePathMappings;
namespace NzbDrone.Core.Download.Clients.Nzbget
{
@@ -52,8 +51,19 @@ namespace NzbDrone.Core.Download.Clients.Nzbget
private IEnumerable<DownloadClientItem> GetQueue()
{
var globalStatus = _proxy.GetGlobalStatus(Settings);
var queue = _proxy.GetQueue(Settings);
NzbgetGlobalStatus globalStatus;
List<NzbgetQueueItem> queue;
try
{
globalStatus = _proxy.GetGlobalStatus(Settings);
queue = _proxy.GetQueue(Settings);
}
catch (DownloadClientException ex)
{
_logger.Error(ex);
return Enumerable.Empty<DownloadClientItem>();
}
var queueItems = new List<DownloadClientItem>();
@@ -109,7 +119,17 @@ namespace NzbDrone.Core.Download.Clients.Nzbget
private IEnumerable<DownloadClientItem> GetHistory()
{
var history = _proxy.GetHistory(Settings).Take(_configService.DownloadClientHistoryLimit).ToList();
List<NzbgetHistoryItem> history;
try
{
history = _proxy.GetHistory(Settings).Take(_configService.DownloadClientHistoryLimit).ToList();
}
catch (DownloadClientException ex)
{
_logger.Error(ex);
return Enumerable.Empty<DownloadClientItem>();
}
var historyItems = new List<DownloadClientItem>();
@@ -269,7 +289,7 @@ namespace NzbDrone.Core.Download.Clients.Nzbget
{
return new ValidationFailure("Username", "Authentication failed");
}
_logger.Error(ex, "Unable to connect to NZBGet");
_logger.Error(ex);
return new ValidationFailure("Host", "Unable to connect to NZBGet");
}
@@ -297,9 +317,8 @@ namespace NzbDrone.Core.Download.Clients.Nzbget
{
var config = _proxy.GetConfig(Settings);
var keepHistory = config.GetValueOrDefault("KeepHistory", "7");
int value;
if (!int.TryParse(keepHistory, NumberStyles.None, CultureInfo.InvariantCulture, out value) || value == 0)
var keepHistory = config.GetValueOrDefault("KeepHistory");
if (keepHistory == "0")
{
return new NzbDroneValidationFailure(string.Empty, "NzbGet setting KeepHistory should be greater than 0")
{
@@ -307,14 +326,6 @@ namespace NzbDrone.Core.Download.Clients.Nzbget
DetailedDescription = "NzbGet setting KeepHistory is set to 0. Which prevents Sonarr from seeing completed downloads."
};
}
else if (value > 25000)
{
return new NzbDroneValidationFailure(string.Empty, "NzbGet setting KeepHistory should be less than 25000")
{
InfoLink = string.Format("http://{0}:{1}/", Settings.Host, Settings.Port),
DetailedDescription = "NzbGet setting KeepHistory is set too high."
};
}
return null;
}

View File

@@ -235,14 +235,14 @@ namespace NzbDrone.Core.Download.Clients.Nzbget
{
if (ex.Response.StatusCode == HttpStatusCode.Unauthorized)
{
throw new DownloadClientAuthenticationException("Authentication failed for NzbGet, please check your settings", ex);
throw new DownloadClientException("Authentication failed for NzbGet, please check your settings", ex);
}
throw new DownloadClientException("Unable to connect to NzbGet. " + ex.Message, ex);
}
catch (WebException ex)
{
throw new DownloadClientUnavailableException("Unable to connect to NzbGet. " + ex.Message, ex);
throw new DownloadClientException("Unable to connect to NzbGet. " + ex.Message, ex);
}
var result = Json.Deserialize<JsonRpcResponse<T>>(response.Content);

View File

@@ -89,8 +89,19 @@ namespace NzbDrone.Core.Download.Clients.QBittorrent
public override IEnumerable<DownloadClientItem> GetItems()
{
var config = _proxy.GetConfig(Settings);
var torrents = _proxy.GetTorrents(Settings);
QBittorrentPreferences config;
List<QBittorrentTorrent> torrents;
try
{
config = _proxy.GetConfig(Settings);
torrents = _proxy.GetTorrents(Settings);
}
catch (DownloadClientException ex)
{
_logger.Error(ex);
return Enumerable.Empty<DownloadClientItem>();
}
var queueItems = new List<DownloadClientItem>();
@@ -230,7 +241,7 @@ namespace NzbDrone.Core.Download.Clients.QBittorrent
}
catch (DownloadClientAuthenticationException ex)
{
_logger.Error(ex, ex.Message);
_logger.Error(ex);
return new NzbDroneValidationFailure("Username", "Authentication failure")
{
DetailedDescription = "Please verify your username and password."
@@ -238,7 +249,7 @@ namespace NzbDrone.Core.Download.Clients.QBittorrent
}
catch (WebException ex)
{
_logger.Error(ex, "Unable to connect to qBittorrent");
_logger.Error(ex);
if (ex.Status == WebExceptionStatus.ConnectFailure)
{
return new NzbDroneValidationFailure("Host", "Unable to connect")
@@ -250,7 +261,7 @@ namespace NzbDrone.Core.Download.Clients.QBittorrent
}
catch (Exception ex)
{
_logger.Error(ex, "Unable to test qBittorrent");
_logger.Error(ex);
return new NzbDroneValidationFailure(String.Empty, "Unknown exception: " + ex.Message);
}
@@ -285,7 +296,7 @@ namespace NzbDrone.Core.Download.Clients.QBittorrent
}
catch (Exception ex)
{
_logger.Error(ex, "Failed to test qBittorrent");
_logger.Error(ex);
return new NzbDroneValidationFailure(String.Empty, "Unknown exception: " + ex.Message);
}
@@ -300,7 +311,7 @@ namespace NzbDrone.Core.Download.Clients.QBittorrent
}
catch (Exception ex)
{
_logger.Error(ex, "Failed to get torrents");
_logger.Error(ex);
return new NzbDroneValidationFailure(String.Empty, "Failed to get the list of torrents: " + ex.Message);
}

View File

@@ -156,7 +156,6 @@ namespace NzbDrone.Core.Download.Clients.QBittorrent
AuthenticateClient(requestBuilder, settings);
var request = requestBuilder.Build();
request.LogResponseContent = true;
HttpResponse response;
try
@@ -226,7 +225,7 @@ namespace NzbDrone.Core.Download.Clients.QBittorrent
}
catch (WebException ex)
{
throw new DownloadClientUnavailableException("Failed to connect to qBitTorrent, please check your settings.", ex);
throw new DownloadClientException("Failed to connect to qBitTorrent, please check your settings.", ex);
}
if (response.Content != "Ok.") // returns "Fails." on bad login

View File

@@ -57,7 +57,7 @@ namespace NzbDrone.Core.Download.Clients.Sabnzbd
}
catch (DownloadClientException ex)
{
_logger.Warn(ex, "Couldn't get download queue. {0}", ex.Message);
_logger.Warn("Couldn't get download queue. {0}", ex.Message);
return Enumerable.Empty<DownloadClientItem>();
}
@@ -112,7 +112,17 @@ namespace NzbDrone.Core.Download.Clients.Sabnzbd
private IEnumerable<DownloadClientItem> GetHistory()
{
var sabHistory = _proxy.GetHistory(0, _configService.DownloadClientHistoryLimit, Settings.TvCategory, Settings);
SabnzbdHistory sabHistory;
try
{
sabHistory = _proxy.GetHistory(0, _configService.DownloadClientHistoryLimit, Settings.TvCategory, Settings);
}
catch (DownloadClientException ex)
{
_logger.Error(ex);
return Enumerable.Empty<DownloadClientItem>();
}
var historyItems = new List<DownloadClientItem>();
@@ -178,7 +188,7 @@ namespace NzbDrone.Core.Download.Clients.Sabnzbd
}
}
historyItems.Add(historyItem);
}
@@ -378,7 +388,7 @@ namespace NzbDrone.Core.Download.Clients.Sabnzbd
}
catch (Exception ex)
{
_logger.Error(ex, ex.Message);
_logger.Error(ex);
return new ValidationFailure("Host", "Unable to connect to SABnzbd");
}
}

View File

@@ -183,7 +183,7 @@ namespace NzbDrone.Core.Download.Clients.Sabnzbd
}
catch (WebException ex)
{
throw new DownloadClientUnavailableException("Unable to connect to SABnzbd, please check your settings", ex);
throw new DownloadClientException("Unable to connect to SABnzbd, please check your settings", ex);
}
CheckForError(response);

View File

@@ -1,4 +1,4 @@
using System;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
@@ -33,7 +33,17 @@ namespace NzbDrone.Core.Download.Clients.Transmission
public override IEnumerable<DownloadClientItem> GetItems()
{
var torrents = _proxy.GetTorrents(Settings);
List<TransmissionTorrent> torrents;
try
{
torrents = _proxy.GetTorrents(Settings);
}
catch (DownloadClientException ex)
{
_logger.Error(ex);
return Enumerable.Empty<DownloadClientItem>();
}
var items = new List<DownloadClientItem>();
@@ -195,24 +205,27 @@ namespace NzbDrone.Core.Download.Clients.Transmission
}
catch (DownloadClientAuthenticationException ex)
{
_logger.Error(ex, ex.Message);
_logger.Error(ex);
return new NzbDroneValidationFailure("Username", "Authentication failure")
{
DetailedDescription = string.Format("Please verify your username and password. Also verify if the host running Sonarr isn't blocked from accessing {0} by WhiteList limitations in the {0} configuration.", Name)
};
}
catch (DownloadClientUnavailableException ex)
catch (WebException ex)
{
_logger.Error(ex, ex.Message);
return new NzbDroneValidationFailure("Host", "Unable to connect")
_logger.Error(ex);
if (ex.Status == WebExceptionStatus.ConnectFailure)
{
DetailedDescription = "Please verify the hostname and port."
};
return new NzbDroneValidationFailure("Host", "Unable to connect")
{
DetailedDescription = "Please verify the hostname and port."
};
}
return new NzbDroneValidationFailure(string.Empty, "Unknown exception: " + ex.Message);
}
catch (Exception ex)
{
_logger.Error(ex, "Failed to test");
_logger.Error(ex);
return new NzbDroneValidationFailure(string.Empty, "Unknown exception: " + ex.Message);
}
}
@@ -227,7 +240,7 @@ namespace NzbDrone.Core.Download.Clients.Transmission
}
catch (Exception ex)
{
_logger.Error(ex, "Failed to get torrents");
_logger.Error(ex);
return new NzbDroneValidationFailure(string.Empty, "Failed to get the list of torrents: " + ex.Message);
}

View File

@@ -238,66 +238,54 @@ namespace NzbDrone.Core.Download.Clients.Transmission
public TransmissionResponse ProcessRequest(string action, object arguments, TransmissionSettings settings)
{
try
var requestBuilder = BuildRequest(settings);
requestBuilder.Headers.ContentType = "application/json";
requestBuilder.SuppressHttpError = true;
AuthenticateClient(requestBuilder, settings);
var request = requestBuilder.Post().Build();
var data = new Dictionary<string, object>();
data.Add("method", action);
if (arguments != null)
{
var requestBuilder = BuildRequest(settings);
requestBuilder.Headers.ContentType = "application/json";
requestBuilder.SuppressHttpError = true;
data.Add("arguments", arguments);
}
AuthenticateClient(requestBuilder, settings);
request.SetContent(data.ToJson());
request.ContentSummary = string.Format("{0}(...)", action);
var request = requestBuilder.Post().Build();
var response = _httpClient.Execute(request);
if (response.StatusCode == HttpStatusCode.Conflict)
{
AuthenticateClient(requestBuilder, settings, true);
var data = new Dictionary<string, object>();
data.Add("method", action);
if (arguments != null)
{
data.Add("arguments", arguments);
}
request = requestBuilder.Post().Build();
request.SetContent(data.ToJson());
request.ContentSummary = string.Format("{0}(...)", action);
var response = _httpClient.Execute(request);
if (response.StatusCode == HttpStatusCode.Conflict)
{
AuthenticateClient(requestBuilder, settings, true);
request = requestBuilder.Post().Build();
request.SetContent(data.ToJson());
request.ContentSummary = string.Format("{0}(...)", action);
response = _httpClient.Execute(request);
}
else if (response.StatusCode == HttpStatusCode.Unauthorized)
{
throw new DownloadClientAuthenticationException("User authentication failed.");
}
var transmissionResponse = Json.Deserialize<TransmissionResponse>(response.Content);
if (transmissionResponse == null)
{
throw new TransmissionException("Unexpected response");
}
else if (transmissionResponse.Result != "success")
{
throw new TransmissionException(transmissionResponse.Result);
}
return transmissionResponse;
response = _httpClient.Execute(request);
}
catch (HttpException ex)
else if (response.StatusCode == HttpStatusCode.Unauthorized)
{
throw new DownloadClientException("Unable to connect to Transmission, please check your settings", ex);
throw new DownloadClientAuthenticationException("User authentication failed.");
}
catch (WebException ex)
var transmissionResponse = Json.Deserialize<TransmissionResponse>(response.Content);
if (transmissionResponse == null)
{
throw new DownloadClientUnavailableException("Unable to connect to Transmission, please check your settings", ex);
throw new TransmissionException("Unexpected response");
}
else if (transmissionResponse.Result != "success")
{
throw new TransmissionException(transmissionResponse.Result);
}
return transmissionResponse;
}
}
}

View File

@@ -81,60 +81,69 @@ namespace NzbDrone.Core.Download.Clients.RTorrent
public override IEnumerable<DownloadClientItem> GetItems()
{
var torrents = _proxy.GetTorrents(Settings);
_logger.Debug("Retrieved metadata of {0} torrents in client", torrents.Count);
var items = new List<DownloadClientItem>();
foreach (RTorrentTorrent torrent in torrents)
try
{
// Don't concern ourselves with categories other than specified
if (torrent.Category != Settings.TvCategory) continue;
var torrents = _proxy.GetTorrents(Settings);
if (torrent.Path.StartsWith("."))
_logger.Debug("Retrieved metadata of {0} torrents in client", torrents.Count);
var items = new List<DownloadClientItem>();
foreach (RTorrentTorrent torrent in torrents)
{
throw new DownloadClientException("Download paths paths must be absolute. Please specify variable \"directory\" in rTorrent.");
// Don't concern ourselves with categories other than specified
if (torrent.Category != Settings.TvCategory) continue;
if (torrent.Path.StartsWith("."))
{
throw new DownloadClientException("Download paths paths must be absolute. Please specify variable \"directory\" in rTorrent.");
}
var item = new DownloadClientItem();
item.DownloadClient = Definition.Name;
item.Title = torrent.Name;
item.DownloadId = torrent.Hash;
item.OutputPath = _remotePathMappingService.RemapRemoteToLocal(Settings.Host, new OsPath(torrent.Path));
item.TotalSize = torrent.TotalSize;
item.RemainingSize = torrent.RemainingSize;
item.Category = torrent.Category;
if (torrent.DownRate > 0)
{
var secondsLeft = torrent.RemainingSize / torrent.DownRate;
item.RemainingTime = TimeSpan.FromSeconds(secondsLeft);
}
else
{
item.RemainingTime = TimeSpan.Zero;
}
if (torrent.IsFinished)
{
item.Status = DownloadItemStatus.Completed;
}
else if (torrent.IsActive)
{
item.Status = DownloadItemStatus.Downloading;
}
else if (!torrent.IsActive)
{
item.Status = DownloadItemStatus.Paused;
}
// No stop ratio data is present, so do not delete
item.CanMoveFiles = item.CanBeRemoved = false;
items.Add(item);
}
var item = new DownloadClientItem();
item.DownloadClient = Definition.Name;
item.Title = torrent.Name;
item.DownloadId = torrent.Hash;
item.OutputPath = _remotePathMappingService.RemapRemoteToLocal(Settings.Host, new OsPath(torrent.Path));
item.TotalSize = torrent.TotalSize;
item.RemainingSize = torrent.RemainingSize;
item.Category = torrent.Category;
if (torrent.DownRate > 0)
{
var secondsLeft = torrent.RemainingSize / torrent.DownRate;
item.RemainingTime = TimeSpan.FromSeconds(secondsLeft);
}
else
{
item.RemainingTime = TimeSpan.Zero;
}
if (torrent.IsFinished)
{
item.Status = DownloadItemStatus.Completed;
}
else if (torrent.IsActive)
{
item.Status = DownloadItemStatus.Downloading;
}
else if (!torrent.IsActive)
{
item.Status = DownloadItemStatus.Paused;
}
// No stop ratio data is present, so do not delete
item.CanMoveFiles = item.CanBeRemoved = false;
items.Add(item);
return items;
}
catch (DownloadClientException ex)
{
_logger.Error(ex);
return Enumerable.Empty<DownloadClientItem>();
}
return items;
}
public override void RemoveItem(string downloadId, bool deleteData)
@@ -180,7 +189,7 @@ namespace NzbDrone.Core.Download.Clients.RTorrent
}
catch (Exception ex)
{
_logger.Error(ex, "Failed to test rTorrent");
_logger.Error(ex);
return new NzbDroneValidationFailure(string.Empty, "Unknown exception: " + ex.Message);
}
@@ -195,7 +204,7 @@ namespace NzbDrone.Core.Download.Clients.RTorrent
}
catch (Exception ex)
{
_logger.Error(ex, "Failed to get torrents");
_logger.Error(ex);
return new NzbDroneValidationFailure(string.Empty, "Failed to get the list of torrents: " + ex.Message);
}

View File

@@ -2,8 +2,6 @@
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Runtime.InteropServices.ComTypes;
using NLog;
using NzbDrone.Common.Extensions;
using CookComputing.XmlRpc;
@@ -56,7 +54,8 @@ namespace NzbDrone.Core.Download.Clients.RTorrent
_logger.Debug("Executing remote method: system.client_version");
var client = BuildClient(settings);
var version = ExecuteRequest(() => client.GetVersion());
var version = client.GetVersion();
return version;
}
@@ -66,22 +65,20 @@ namespace NzbDrone.Core.Download.Clients.RTorrent
_logger.Debug("Executing remote method: d.multicall2");
var client = BuildClient(settings);
var ret = ExecuteRequest(() => client.TorrentMulticall("", "",
"d.name=", // string
"d.hash=", // string
"d.base_path=", // string
"d.custom1=", // string (label)
"d.size_bytes=", // long
"d.left_bytes=", // long
"d.down.rate=", // long (in bytes / s)
"d.ratio=", // long
"d.is_open=", // long
"d.is_active=", // long
"d.complete=") //long
);
var ret = client.TorrentMulticall("", "",
"d.name=", // string
"d.hash=", // string
"d.base_path=", // string
"d.custom1=", // string (label)
"d.size_bytes=", // long
"d.left_bytes=", // long
"d.down.rate=", // long (in bytes / s)
"d.ratio=", // long
"d.is_open=", // long
"d.is_active=", // long
"d.complete="); //long
var items = new List<RTorrentTorrent>();
foreach (object[] torrent in ret)
{
var labelDecoded = System.Web.HttpUtility.UrlDecode((string) torrent[3]);
@@ -110,8 +107,8 @@ namespace NzbDrone.Core.Download.Clients.RTorrent
_logger.Debug("Executing remote method: load.normal");
var client = BuildClient(settings);
var response = ExecuteRequest(() => client.LoadStart("", torrentUrl, GetCommands(label, priority, directory)));
var response = client.LoadStart("", torrentUrl, GetCommands(label, priority, directory));
if (response != 0)
{
throw new DownloadClientException("Could not add torrent: {0}.", torrentUrl);
@@ -123,8 +120,8 @@ namespace NzbDrone.Core.Download.Clients.RTorrent
_logger.Debug("Executing remote method: load.raw");
var client = BuildClient(settings);
var response = ExecuteRequest(() => client.LoadRawStart("", fileContent, GetCommands(label, priority, directory)));
var response = client.LoadRawStart("", fileContent, GetCommands(label, priority, directory));
if (response != 0)
{
throw new DownloadClientException("Could not add torrent: {0}.", fileName);
@@ -136,39 +133,14 @@ namespace NzbDrone.Core.Download.Clients.RTorrent
_logger.Debug("Executing remote method: d.erase");
var client = BuildClient(settings);
var response = ExecuteRequest(() => client.Remove(hash));
var response = client.Remove(hash);
if (response != 0)
{
throw new DownloadClientException("Could not remove torrent: {0}.", hash);
}
}
public bool HasHashTorrent(string hash, RTorrentSettings settings)
{
_logger.Debug("Executing remote method: d.name");
var client = BuildClient(settings);
try
{
var name = ExecuteRequest(() => client.GetName(hash));
if (name.IsNullOrWhiteSpace())
{
return false;
}
var metaTorrent = name == (hash + ".meta");
return !metaTorrent;
}
catch (Exception)
{
return false;
}
}
private string[] GetCommands(string label, RTorrentPriority priority, string directory)
{
var result = new List<string>();
@@ -191,6 +163,25 @@ namespace NzbDrone.Core.Download.Clients.RTorrent
return result.ToArray();
}
public bool HasHashTorrent(string hash, RTorrentSettings settings)
{
_logger.Debug("Executing remote method: d.name");
var client = BuildClient(settings);
try
{
var name = client.GetName(hash);
if (name.IsNullOrWhiteSpace()) return false;
bool metaTorrent = name == (hash + ".meta");
return !metaTorrent;
}
catch (Exception)
{
return false;
}
}
private IRTorrent BuildClient(RTorrentSettings settings)
{
var client = XmlRpcProxyGen.Create<IRTorrent>();
@@ -210,21 +201,5 @@ namespace NzbDrone.Core.Download.Clients.RTorrent
return client;
}
private T ExecuteRequest<T>(Func<T> task)
{
try
{
return task();
}
catch (XmlRpcServerException ex)
{
throw new DownloadClientException("Unable to connect to rTorrent, please check your settings", ex);
}
catch (WebException ex)
{
throw new DownloadClientUnavailableException("Unable to connect to rTorrent, please check your settings", ex);
}
}
}
}

View File

@@ -72,7 +72,42 @@ namespace NzbDrone.Core.Download.Clients.UTorrent
public override IEnumerable<DownloadClientItem> GetItems()
{
var torrents = GetTorrents();
List<UTorrentTorrent> torrents;
try
{
var cacheKey = string.Format("{0}:{1}:{2}", Settings.Host, Settings.Port, Settings.TvCategory);
var cache = _torrentCache.Find(cacheKey);
var response = _proxy.GetTorrents(cache == null ? null : cache.CacheID, Settings);
if (cache != null && response.Torrents == null)
{
var removedAndUpdated = new HashSet<string>(response.TorrentsChanged.Select(v => v.Hash).Concat(response.TorrentsRemoved));
torrents = cache.Torrents
.Where(v => !removedAndUpdated.Contains(v.Hash))
.Concat(response.TorrentsChanged)
.ToList();
}
else
{
torrents = response.Torrents;
}
cache = new UTorrentTorrentCache
{
CacheID = response.CacheNumber,
Torrents = torrents
};
_torrentCache.Set(cacheKey, cache, TimeSpan.FromMinutes(15));
}
catch (DownloadClientException ex)
{
_logger.Error(ex);
return Enumerable.Empty<DownloadClientItem>();
}
var queueItems = new List<DownloadClientItem>();
@@ -138,40 +173,6 @@ namespace NzbDrone.Core.Download.Clients.UTorrent
return queueItems;
}
private List<UTorrentTorrent> GetTorrents()
{
List<UTorrentTorrent> torrents;
var cacheKey = string.Format("{0}:{1}:{2}", Settings.Host, Settings.Port, Settings.TvCategory);
var cache = _torrentCache.Find(cacheKey);
var response = _proxy.GetTorrents(cache == null ? null : cache.CacheID, Settings);
if (cache != null && response.Torrents == null)
{
var removedAndUpdated = new HashSet<string>(response.TorrentsChanged.Select(v => v.Hash).Concat(response.TorrentsRemoved));
torrents = cache.Torrents
.Where(v => !removedAndUpdated.Contains(v.Hash))
.Concat(response.TorrentsChanged)
.ToList();
}
else
{
torrents = response.Torrents;
}
cache = new UTorrentTorrentCache
{
CacheID = response.CacheNumber,
Torrents = torrents
};
_torrentCache.Set(cacheKey, cache, TimeSpan.FromMinutes(15));
return torrents;
}
public override void RemoveItem(string downloadId, bool deleteData)
{
_proxy.RemoveTorrent(downloadId, deleteData, Settings);
@@ -231,7 +232,7 @@ namespace NzbDrone.Core.Download.Clients.UTorrent
}
catch (DownloadClientAuthenticationException ex)
{
_logger.Error(ex, ex.Message);
_logger.Error(ex);
return new NzbDroneValidationFailure("Username", "Authentication failure")
{
DetailedDescription = "Please verify your username and password."
@@ -239,7 +240,7 @@ namespace NzbDrone.Core.Download.Clients.UTorrent
}
catch (WebException ex)
{
_logger.Error(ex, "Unable to connect to uTorrent");
_logger.Error(ex);
if (ex.Status == WebExceptionStatus.ConnectFailure)
{
return new NzbDroneValidationFailure("Host", "Unable to connect")
@@ -251,7 +252,7 @@ namespace NzbDrone.Core.Download.Clients.UTorrent
}
catch (Exception ex)
{
_logger.Error(ex, "Failed to test uTorrent");
_logger.Error(ex);
return new NzbDroneValidationFailure(string.Empty, "Unknown exception: " + ex.Message);
}
@@ -266,7 +267,7 @@ namespace NzbDrone.Core.Download.Clients.UTorrent
}
catch (Exception ex)
{
_logger.Error(ex, "Failed to get torrents");
_logger.Error(ex);
return new NzbDroneValidationFailure(string.Empty, "Failed to get the list of torrents: " + ex.Message);
}

View File

@@ -244,7 +244,7 @@ namespace NzbDrone.Core.Download.Clients.UTorrent
}
catch (WebException ex)
{
throw new DownloadClientUnavailableException("Unable to connect to uTorrent, please check your settings", ex);
throw new DownloadClientException("Unable to connect to uTorrent, please check your settings", ex);
}
cookies = response.GetCookies();

View File

@@ -1,7 +1,5 @@
using System;
using System.Collections.Generic;
using System.Collections.Generic;
using System.Linq;
using FluentValidation.Results;
using NLog;
using NzbDrone.Common.Composition;
using NzbDrone.Core.Messaging.Events;
@@ -11,24 +9,17 @@ namespace NzbDrone.Core.Download
{
public interface IDownloadClientFactory : IProviderFactory<IDownloadClient, DownloadClientDefinition>
{
List<IDownloadClient> DownloadHandlingEnabled(bool filterBlockedClients = true);
}
public class DownloadClientFactory : ProviderFactory<IDownloadClient, DownloadClientDefinition>, IDownloadClientFactory
{
private readonly IDownloadClientStatusService _downloadClientStatusService;
private readonly Logger _logger;
private readonly IDownloadClientRepository _providerRepository;
public DownloadClientFactory(IDownloadClientStatusService downloadClientStatusService,
IDownloadClientRepository providerRepository,
IEnumerable<IDownloadClient> providers,
IContainer container,
IEventAggregator eventAggregator,
Logger logger)
public DownloadClientFactory(IDownloadClientRepository providerRepository, IEnumerable<IDownloadClient> providers, IContainer container, IEventAggregator eventAggregator, Logger logger)
: base(providerRepository, providers, container, eventAggregator, logger)
{
_downloadClientStatusService = downloadClientStatusService;
_logger = logger;
_providerRepository = providerRepository;
}
protected override List<DownloadClientDefinition> Active()
@@ -42,46 +33,5 @@ namespace NzbDrone.Core.Download
definition.Protocol = provider.Protocol;
}
public List<IDownloadClient> DownloadHandlingEnabled(bool filterBlockedClients = true)
{
var enabledClients = GetAvailableProviders();
if (filterBlockedClients)
{
return FilterBlockedClients(enabledClients).ToList();
}
return enabledClients.ToList();
}
private IEnumerable<IDownloadClient> FilterBlockedClients(IEnumerable<IDownloadClient> clients)
{
var blockedIndexers = _downloadClientStatusService.GetBlockedProviders().ToDictionary(v => v.ProviderId, v => v);
foreach (var client in clients)
{
DownloadClientStatus downloadClientStatus;
if (blockedIndexers.TryGetValue(client.Definition.Id, out downloadClientStatus))
{
_logger.Debug("Temporarily ignoring download client {0} till {1} due to recent failures.", client.Definition.Name, downloadClientStatus.DisabledTill.Value.ToLocalTime());
continue;
}
yield return client;
}
}
public override ValidationResult Test(DownloadClientDefinition definition)
{
var result = base.Test(definition);
if ((result == null || result.IsValid) && definition.Id != 0)
{
_downloadClientStatusService.RecordSuccess(definition.Id);
}
return result;
}
}
}
}

View File

@@ -27,12 +27,19 @@ namespace NzbDrone.Core.Download
public IEnumerable<IDownloadClient> GetDownloadClients()
{
return _downloadClientFactory.GetAvailableProviders();
return _downloadClientFactory.GetAvailableProviders();//.Select(MapDownloadClient);
}
public IDownloadClient Get(int id)
{
return _downloadClientFactory.GetAvailableProviders().Single(d => d.Definition.Id == id);
}
public IDownloadClient MapDownloadClient(IDownloadClient downloadClient)
{
_downloadClientFactory.SetProviderCharacteristics(downloadClient, (DownloadClientDefinition)downloadClient.Definition);
return downloadClient;
}
}
}

View File

@@ -1,9 +0,0 @@
using NzbDrone.Core.ThingiProvider.Status;
namespace NzbDrone.Core.Download
{
public class DownloadClientStatus : ProviderStatusBase
{
}
}

View File

@@ -1,19 +0,0 @@
using NzbDrone.Core.Datastore;
using NzbDrone.Core.Messaging.Events;
using NzbDrone.Core.ThingiProvider.Status;
namespace NzbDrone.Core.Download
{
public interface IDownloadClientStatusRepository : IProviderStatusRepository<DownloadClientStatus>
{
}
public class DownloadClientStatusRepository : ProviderStatusRepository<DownloadClientStatus>, IDownloadClientStatusRepository
{
public DownloadClientStatusRepository(IMainDatabase database, IEventAggregator eventAggregator)
: base(database, eventAggregator)
{
}
}
}

View File

@@ -1,22 +0,0 @@
using System;
using NLog;
using NzbDrone.Core.Messaging.Events;
using NzbDrone.Core.ThingiProvider.Status;
namespace NzbDrone.Core.Download
{
public interface IDownloadClientStatusService : IProviderStatusServiceBase<DownloadClientStatus>
{
}
public class DownloadClientStatusService : ProviderStatusServiceBase<IDownloadClient, DownloadClientStatus>, IDownloadClientStatusService
{
public DownloadClientStatusService(IDownloadClientStatusRepository providerStatusRepository, IEventAggregator eventAggregator, Logger logger)
: base(providerStatusRepository, eventAggregator, logger)
{
MinimumTimeSinceInitialFailure = TimeSpan.FromMinutes(5);
MaximumEscalationLevel = 5;
}
}
}

View File

@@ -21,21 +21,18 @@ namespace NzbDrone.Core.Download
public class DownloadService : IDownloadService
{
private readonly IProvideDownloadClient _downloadClientProvider;
private readonly IDownloadClientStatusService _downloadClientStatusService;
private readonly IIndexerStatusService _indexerStatusService;
private readonly IRateLimitService _rateLimitService;
private readonly IEventAggregator _eventAggregator;
private readonly Logger _logger;
public DownloadService(IProvideDownloadClient downloadClientProvider,
IDownloadClientStatusService downloadClientStatusService,
IIndexerStatusService indexerStatusService,
IRateLimitService rateLimitService,
IEventAggregator eventAggregator,
Logger logger)
IIndexerStatusService indexerStatusService,
IRateLimitService rateLimitService,
IEventAggregator eventAggregator,
Logger logger)
{
_downloadClientProvider = downloadClientProvider;
_downloadClientStatusService = downloadClientStatusService;
_indexerStatusService = indexerStatusService;
_rateLimitService = rateLimitService;
_eventAggregator = eventAggregator;
@@ -67,7 +64,6 @@ namespace NzbDrone.Core.Download
try
{
downloadClientId = downloadClient.Download(remoteEpisode);
_downloadClientStatusService.RecordSuccess(downloadClient.Definition.Id);
_indexerStatusService.RecordSuccess(remoteEpisode.Release.IndexerId);
}
catch (ReleaseDownloadException ex)
@@ -96,4 +92,4 @@ namespace NzbDrone.Core.Download
_eventAggregator.PublishEvent(episodeGrabbedEvent);
}
}
}
}

View File

@@ -11,7 +11,6 @@ namespace NzbDrone.Core.Download.Pending
public DateTime Added { get; set; }
public ParsedEpisodeInfo ParsedEpisodeInfo { get; set; }
public ReleaseInfo Release { get; set; }
public PendingReleaseReason Reason { get; set; }
//Not persisted
public RemoteEpisode RemoteEpisode { get; set; }

View File

@@ -1,9 +0,0 @@
namespace NzbDrone.Core.Download.Pending
{
public enum PendingReleaseReason
{
Delay = 0,
DownloadClientUnavailable = 1,
Fallback = 2
}
}

View File

@@ -20,7 +20,8 @@ namespace NzbDrone.Core.Download.Pending
{
public interface IPendingReleaseService
{
void Add(DownloadDecision decision, PendingReleaseReason reason);
void Add(DownloadDecision decision);
List<ReleaseInfo> GetPending();
List<RemoteEpisode> GetPendingRemoteEpisodes(int seriesId);
List<Queue.Queue> GetPendingQueue();
@@ -66,7 +67,7 @@ namespace NzbDrone.Core.Download.Pending
}
public void Add(DownloadDecision decision, PendingReleaseReason reason)
public void Add(DownloadDecision decision)
{
var alreadyPending = GetPendingReleases();
@@ -76,32 +77,14 @@ namespace NzbDrone.Core.Download.Pending
.Intersect(episodeIds)
.Any());
var matchingReports = existingReports.Where(MatchingReleasePredicate(decision.RemoteEpisode.Release)).ToList();
if (matchingReports.Any())
if (existingReports.Any(MatchingReleasePredicate(decision.RemoteEpisode.Release)))
{
var sameReason = true;
foreach (var matchingReport in matchingReports)
{
if (matchingReport.Reason != reason)
{
_logger.Debug("This release is already pending with reason {0}, changing to {1}", matchingReport.Reason, reason);
matchingReport.Reason = reason;
_repository.Update(matchingReport);
sameReason = false;
}
}
if (sameReason)
{
_logger.Debug("This release is already pending with reason {0}, not adding again", reason);
return;
}
_logger.Debug("This release is already pending, not adding again");
return;
}
_logger.Debug("Adding release to pending releases with reason {0}", reason);
Insert(decision, reason);
_logger.Debug("Adding release to pending releases");
Insert(decision);
}
public List<ReleaseInfo> GetPending()
@@ -118,7 +101,7 @@ namespace NzbDrone.Core.Download.Pending
private List<ReleaseInfo> FilterBlockedIndexers(List<ReleaseInfo> releases)
{
var blockedIndexers = new HashSet<int>(_indexerStatusService.GetBlockedProviders().Select(v => v.ProviderId));
var blockedIndexers = new HashSet<int>(_indexerStatusService.GetBlockedIndexers().Select(v => v.ProviderId));
return releases.Where(release => !blockedIndexers.Contains(release.IndexerId)).ToList();
}
@@ -134,7 +117,7 @@ namespace NzbDrone.Core.Download.Pending
var nextRssSync = new Lazy<DateTime>(() => _taskManager.GetNextExecution(typeof(RssSyncCommand)));
foreach (var pendingRelease in GetPendingReleases().Where(p => p.Reason != PendingReleaseReason.Fallback))
foreach (var pendingRelease in GetPendingReleases())
{
foreach (var episode in pendingRelease.RemoteEpisode.Episodes)
{
@@ -149,13 +132,6 @@ namespace NzbDrone.Core.Download.Pending
ect = ect.AddMinutes(_configService.RssSyncInterval);
}
var timeleft = ect.Subtract(DateTime.UtcNow);
if (timeleft.TotalSeconds < 0)
{
timeleft = TimeSpan.Zero;
}
var queue = new Queue.Queue
{
Id = GetQueueId(pendingRelease, episode),
@@ -166,12 +142,11 @@ namespace NzbDrone.Core.Download.Pending
Size = pendingRelease.RemoteEpisode.Release.Size,
Sizeleft = pendingRelease.RemoteEpisode.Release.Size,
RemoteEpisode = pendingRelease.RemoteEpisode,
Timeleft = timeleft,
Timeleft = ect.Subtract(DateTime.UtcNow),
EstimatedCompletionTime = ect,
Status = pendingRelease.Reason.ToString(),
Status = "Pending",
Protocol = pendingRelease.RemoteEpisode.Release.DownloadProtocol
};
queued.Add(queue);
}
}
@@ -249,7 +224,7 @@ namespace NzbDrone.Core.Download.Pending
};
}
private void Insert(DownloadDecision decision, PendingReleaseReason reason)
private void Insert(DownloadDecision decision)
{
_repository.Insert(new PendingRelease
{
@@ -257,8 +232,7 @@ namespace NzbDrone.Core.Download.Pending
ParsedEpisodeInfo = decision.RemoteEpisode.ParsedEpisodeInfo,
Release = decision.RemoteEpisode.Release,
Title = decision.RemoteEpisode.Release.Title,
Added = DateTime.UtcNow,
Reason = reason
Added = DateTime.UtcNow
});
_eventAggregator.PublishEvent(new PendingReleasesUpdatedEvent());

View File

@@ -1,12 +1,9 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using NLog;
using NzbDrone.Core.DecisionEngine;
using NzbDrone.Core.Download.Clients;
using NzbDrone.Core.Download.Pending;
using NzbDrone.Core.Indexers;
namespace NzbDrone.Core.Download
{
@@ -39,33 +36,36 @@ namespace NzbDrone.Core.Download
var prioritizedDecisions = _prioritizeDownloadDecision.PrioritizeDecisions(qualifiedReports);
var grabbed = new List<DownloadDecision>();
var pending = new List<DownloadDecision>();
var failed = new List<DownloadDecision>();
var usenetFailed = false;
var torrentFailed = false;
foreach (var report in prioritizedDecisions)
{
var remoteEpisode = report.RemoteEpisode;
var downloadProtocol = report.RemoteEpisode.Release.DownloadProtocol;
// Skip if already grabbed
if (IsEpisodeProcessed(grabbed, report))
var episodeIds = remoteEpisode.Episodes.Select(e => e.Id).ToList();
//Skip if already grabbed
if (grabbed.SelectMany(r => r.RemoteEpisode.Episodes)
.Select(e => e.Id)
.ToList()
.Intersect(episodeIds)
.Any())
{
continue;
}
if (report.TemporarilyRejected)
{
_pendingReleaseService.Add(report, PendingReleaseReason.Delay);
_pendingReleaseService.Add(report);
pending.Add(report);
continue;
}
if (downloadProtocol == DownloadProtocol.Usenet && usenetFailed ||
downloadProtocol == DownloadProtocol.Torrent && torrentFailed)
if (pending.SelectMany(r => r.RemoteEpisode.Episodes)
.Select(e => e.Id)
.ToList()
.Intersect(episodeIds)
.Any())
{
failed.Add(report);
continue;
}
@@ -74,31 +74,14 @@ namespace NzbDrone.Core.Download
_downloadService.DownloadReport(remoteEpisode);
grabbed.Add(report);
}
catch (Exception ex)
catch (Exception e)
{
if (ex is DownloadClientUnavailableException || ex is DownloadClientAuthenticationException)
{
_logger.Debug("Failed to send release to download client, storing until later");
failed.Add(report);
if (downloadProtocol == DownloadProtocol.Usenet)
{
usenetFailed = true;
}
else if (downloadProtocol == DownloadProtocol.Torrent)
{
torrentFailed = true;
}
}
else
{
_logger.Warn(ex, "Couldn't add report to download queue. " + remoteEpisode);
}
//TODO: support for store & forward
//We'll need to differentiate between a download client error and an indexer error
_logger.Warn(e, "Couldn't add report to download queue. " + remoteEpisode);
}
}
pending.AddRange(ProcessFailedGrabs(grabbed, failed));
return new ProcessedDecisions(grabbed, pending, decisions.Where(d => d.Rejected).ToList());
}
@@ -107,50 +90,5 @@ namespace NzbDrone.Core.Download
//Process both approved and temporarily rejected
return decisions.Where(c => (c.Approved || c.TemporarilyRejected) && c.RemoteEpisode.Episodes.Any()).ToList();
}
private bool IsEpisodeProcessed(List<DownloadDecision> decisions, DownloadDecision report)
{
var episodeIds = report.RemoteEpisode.Episodes.Select(e => e.Id).ToList();
return decisions.SelectMany(r => r.RemoteEpisode.Episodes)
.Select(e => e.Id)
.ToList()
.Intersect(episodeIds)
.Any();
}
private List<DownloadDecision> ProcessFailedGrabs(List<DownloadDecision> grabbed, List<DownloadDecision> failed)
{
var pending = new List<DownloadDecision>();
var stored = new List<DownloadDecision>();
foreach (var report in failed)
{
// If a release was already grabbed with matching episodes we should store it as a fallback
// and filter it out the next time it is processed incase a higher quality release failed to
// add to the download client, but a lower quality release was sent to another client
// If the release wasn't grabbed already, but was already stored, store it as a fallback,
// otherwise store it as DownloadClientUnavailable.
if (IsEpisodeProcessed(grabbed, report))
{
_pendingReleaseService.Add(report, PendingReleaseReason.Fallback);
pending.Add(report);
}
else if (IsEpisodeProcessed(stored, report))
{
_pendingReleaseService.Add(report, PendingReleaseReason.Fallback);
pending.Add(report);
}
else
{
_pendingReleaseService.Add(report, PendingReleaseReason.DownloadClientUnavailable);
pending.Add(report);
stored.Add(report);
}
}
return pending;
}
}
}

View File

@@ -133,9 +133,7 @@ namespace NzbDrone.Core.Download
var response = _httpClient.Get(request);
if (response.StatusCode == HttpStatusCode.MovedPermanently ||
response.StatusCode == HttpStatusCode.Found ||
response.StatusCode == HttpStatusCode.SeeOther)
if (response.StatusCode == HttpStatusCode.SeeOther || response.StatusCode == HttpStatusCode.Found)
{
var locationHeader = response.Headers.GetSingleValue("Location");

View File

@@ -15,8 +15,7 @@ namespace NzbDrone.Core.Download.TrackedDownloads
IHandle<EpisodeGrabbedEvent>,
IHandle<EpisodeImportedEvent>
{
private readonly IDownloadClientStatusService _downloadClientStatusService;
private readonly IDownloadClientFactory _downloadClientFactory;
private readonly IProvideDownloadClient _downloadClientProvider;
private readonly IEventAggregator _eventAggregator;
private readonly IManageCommandQueue _manageCommandQueue;
private readonly IConfigService _configService;
@@ -26,18 +25,16 @@ namespace NzbDrone.Core.Download.TrackedDownloads
private readonly Logger _logger;
private readonly Debouncer _refreshDebounce;
public DownloadMonitoringService(IDownloadClientStatusService downloadClientStatusService,
IDownloadClientFactory downloadClientFactory,
IEventAggregator eventAggregator,
IManageCommandQueue manageCommandQueue,
IConfigService configService,
IFailedDownloadService failedDownloadService,
ICompletedDownloadService completedDownloadService,
ITrackedDownloadService trackedDownloadService,
Logger logger)
public DownloadMonitoringService(IProvideDownloadClient downloadClientProvider,
IEventAggregator eventAggregator,
IManageCommandQueue manageCommandQueue,
IConfigService configService,
IFailedDownloadService failedDownloadService,
ICompletedDownloadService completedDownloadService,
ITrackedDownloadService trackedDownloadService,
Logger logger)
{
_downloadClientStatusService = downloadClientStatusService;
_downloadClientFactory = downloadClientFactory;
_downloadClientProvider = downloadClientProvider;
_eventAggregator = eventAggregator;
_manageCommandQueue = manageCommandQueue;
_configService = configService;
@@ -59,7 +56,7 @@ namespace NzbDrone.Core.Download.TrackedDownloads
_refreshDebounce.Pause();
try
{
var downloadClients = _downloadClientFactory.DownloadHandlingEnabled();
var downloadClients = _downloadClientProvider.GetDownloadClients();
var trackedDownloads = new List<TrackedDownload>();
@@ -87,12 +84,9 @@ namespace NzbDrone.Core.Download.TrackedDownloads
try
{
downloadClientHistory = downloadClient.GetItems().ToList();
_downloadClientStatusService.RecordSuccess(downloadClient.Definition.Id);
}
catch (Exception ex)
{
_downloadClientStatusService.RecordFailure(downloadClient.Definition.Id);
_logger.Warn(ex, "Unable to retrieve queue and history items from " + downloadClient.Definition.Name);
}

View File

@@ -71,7 +71,7 @@ namespace NzbDrone.Core.Extras
.Select(e => e.Trim(' ', '.'))
.ToList();
var matchingFilenames = files.Where(f => Path.GetFileNameWithoutExtension(f).StartsWith(sourceFileName, StringComparison.InvariantCultureIgnoreCase));
var matchingFilenames = files.Where(f => Path.GetFileNameWithoutExtension(f).StartsWith(sourceFileName));
foreach (var matchingFilename in matchingFilenames)
{

View File

@@ -11,7 +11,6 @@ using NzbDrone.Common.Extensions;
using NzbDrone.Core.Extras.Metadata.Files;
using NzbDrone.Core.MediaCover;
using NzbDrone.Core.MediaFiles;
using NzbDrone.Core.MediaFiles.MediaInfo;
using NzbDrone.Core.Tv;
namespace NzbDrone.Core.Extras.Metadata.Consumers.Xbmc
@@ -104,7 +103,7 @@ namespace NzbDrone.Core.Extras.Metadata.Consumers.Xbmc
return metadata;
}
if (filename.Equals("tvshow.nfo", StringComparison.OrdinalIgnoreCase))
if (filename.Equals("tvshow.nfo", StringComparison.InvariantCultureIgnoreCase))
{
metadata.Type = MetadataType.SeriesMetadata;
return metadata;
@@ -114,7 +113,7 @@ namespace NzbDrone.Core.Extras.Metadata.Consumers.Xbmc
if (parseResult != null &&
!parseResult.FullSeason &&
Path.GetExtension(filename).Equals(".nfo", StringComparison.OrdinalIgnoreCase))
Path.GetExtension(filename) == ".nfo")
{
metadata.Type = MetadataType.EpisodeMetadata;
return metadata;
@@ -248,7 +247,7 @@ namespace NzbDrone.Core.Extras.Metadata.Consumers.Xbmc
var video = new XElement("video");
video.Add(new XElement("aspect", (float)episodeFile.MediaInfo.Width / (float)episodeFile.MediaInfo.Height));
video.Add(new XElement("bitrate", episodeFile.MediaInfo.VideoBitrate));
video.Add(new XElement("codec", MediaInfoFormatter.FormatVideoCodec(episodeFile.MediaInfo, episodeFile.SceneName)));
video.Add(new XElement("codec", episodeFile.MediaInfo.VideoCodec));
video.Add(new XElement("framerate", episodeFile.MediaInfo.VideoFps));
video.Add(new XElement("height", episodeFile.MediaInfo.Height));
video.Add(new XElement("scantype", episodeFile.MediaInfo.ScanType));
@@ -265,11 +264,11 @@ namespace NzbDrone.Core.Extras.Metadata.Consumers.Xbmc
var audio = new XElement("audio");
audio.Add(new XElement("bitrate", episodeFile.MediaInfo.AudioBitrate));
audio.Add(new XElement("channels", episodeFile.MediaInfo.AudioChannels));
audio.Add(new XElement("codec", MediaInfoFormatter.FormatAudioCodec(episodeFile.MediaInfo)));
audio.Add(new XElement("codec", GetAudioCodec(episodeFile.MediaInfo.AudioFormat)));
audio.Add(new XElement("language", episodeFile.MediaInfo.AudioLanguages));
streamDetails.Add(audio);
if (episodeFile.MediaInfo.Subtitles.IsNotNullOrWhiteSpace())
if (episodeFile.MediaInfo.Subtitles != null && episodeFile.MediaInfo.Subtitles.Length > 0)
{
var subtitle = new XElement("subtitle");
subtitle.Add(new XElement("language", episodeFile.MediaInfo.Subtitles));
@@ -380,5 +379,15 @@ namespace NzbDrone.Core.Extras.Metadata.Consumers.Xbmc
{
return Path.ChangeExtension(episodeFilePath, "").Trim('.') + "-thumb.jpg";
}
private string GetAudioCodec(string audioCodec)
{
if (audioCodec == "AC-3")
{
return "AC3";
}
return audioCodec;
}
}
}

View File

@@ -1,5 +1,4 @@
using System;
using System.Collections.Generic;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using NLog;
@@ -66,7 +65,7 @@ namespace NzbDrone.Core.Extras.Others
public override ExtraFile Import(Series series, EpisodeFile episodeFile, string path, string extension, bool readOnly)
{
// If the extension is .nfo we need to change it to .nfo-orig
if (Path.GetExtension(path).Equals(".nfo", StringComparison.OrdinalIgnoreCase))
if (Path.GetExtension(path).Equals(".nfo"))
{
extension += "-orig";
}

View File

@@ -1,5 +1,4 @@
using System;
using System.Collections.Generic;
using System.Collections.Generic;
namespace NzbDrone.Core.Extras.Subtitles
{
@@ -9,7 +8,7 @@ namespace NzbDrone.Core.Extras.Subtitles
static SubtitleFileExtensions()
{
_fileExtensions = new HashSet<string>(StringComparer.OrdinalIgnoreCase)
_fileExtensions = new HashSet<string>
{
".aqt",
".ass",

Some files were not shown because too many files have changed in this diff Show More