mirror of
https://github.com/Radarr/Radarr.git
synced 2026-03-24 17:25:22 -04:00
Compare commits
66 Commits
v2.0.0.475
...
v2.0.0.485
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b03f434329 | ||
|
|
68290b0385 | ||
|
|
773274f3cc | ||
|
|
a33d8968ed | ||
|
|
e41baccd02 | ||
|
|
fef3423019 | ||
|
|
11926d8b2d | ||
|
|
4189bc6f76 | ||
|
|
601b54c244 | ||
|
|
905ab18336 | ||
|
|
f46a576df5 | ||
|
|
60231fbcae | ||
|
|
63860e783e | ||
|
|
cf8b9df5ad | ||
|
|
ff6841e410 | ||
|
|
ab49afe116 | ||
|
|
afcf136c4f | ||
|
|
d1b85444d0 | ||
|
|
63e0eba02f | ||
|
|
475c99d492 | ||
|
|
23552c3267 | ||
|
|
a49e37239e | ||
|
|
65b936ed94 | ||
|
|
359ef04861 | ||
|
|
8cf028e071 | ||
|
|
eea3419849 | ||
|
|
62bc63312d | ||
|
|
6a6d415625 | ||
|
|
1fbe82ae47 | ||
|
|
87f3cc9014 | ||
|
|
ab07a40931 | ||
|
|
10f292b225 | ||
|
|
de5ce23989 | ||
|
|
d0e226e269 | ||
|
|
d7cb5090fc | ||
|
|
416e9abca5 | ||
|
|
6dcb7768a9 | ||
|
|
baf83b4c71 | ||
|
|
285288db1a | ||
|
|
ec3dd982f6 | ||
|
|
410aa467b8 | ||
|
|
0c89a4ae8f | ||
|
|
a1edbafa8a | ||
|
|
ef5a400c68 | ||
|
|
8cc02a9d9c | ||
|
|
e83e852e0d | ||
|
|
4e10d30cf6 | ||
|
|
f335cc1af8 | ||
|
|
f4bea5512c | ||
|
|
9f8091e4d7 | ||
|
|
5aa02eb15c | ||
|
|
6bbe4ce066 | ||
|
|
563b5ef017 | ||
|
|
b9df5634bf | ||
|
|
755575d107 | ||
|
|
8eaab46488 | ||
|
|
4fbc481780 | ||
|
|
a41b5723d4 | ||
|
|
c2b66cf524 | ||
|
|
0d782e1cac | ||
|
|
edf549d0fd | ||
|
|
cf7ce9804f | ||
|
|
e8d01daf03 | ||
|
|
766520b851 | ||
|
|
14144bd4d9 | ||
|
|
7b0e40d5d0 |
@@ -25,7 +25,7 @@ gulp.task('copyHtml', function () {
|
||||
});
|
||||
|
||||
gulp.task('copyContent', function () {
|
||||
return gulp.src([paths.src.content + '**/*.*', '!**/*.less'])
|
||||
return gulp.src([paths.src.content + '**/*.*', '!**/*.less', '!**/*.css'])
|
||||
.pipe(gulp.dest(paths.dest.content))
|
||||
.pipe(livereload());
|
||||
});
|
||||
|
||||
@@ -16,6 +16,10 @@ 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',
|
||||
|
||||
@@ -64,6 +64,8 @@ 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",
|
||||
|
||||
@@ -1,16 +1,13 @@
|
||||
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
|
||||
{
|
||||
@@ -18,27 +15,21 @@ namespace NzbDrone.Api.EpisodeFiles
|
||||
IHandle<EpisodeFileAddedEvent>
|
||||
{
|
||||
private readonly IMediaFileService _mediaFileService;
|
||||
private readonly IDiskProvider _diskProvider;
|
||||
private readonly IRecycleBinProvider _recycleBinProvider;
|
||||
private readonly IDeleteMediaFiles _mediaFileDeletionService;
|
||||
private readonly ISeriesService _seriesService;
|
||||
private readonly IQualityUpgradableSpecification _qualityUpgradableSpecification;
|
||||
private readonly Logger _logger;
|
||||
|
||||
public EpisodeFileModule(IBroadcastSignalRMessage signalRBroadcaster,
|
||||
IMediaFileService mediaFileService,
|
||||
IDiskProvider diskProvider,
|
||||
IRecycleBinProvider recycleBinProvider,
|
||||
IDeleteMediaFiles mediaFileDeletionService,
|
||||
ISeriesService seriesService,
|
||||
IQualityUpgradableSpecification qualityUpgradableSpecification,
|
||||
Logger logger)
|
||||
IQualityUpgradableSpecification qualityUpgradableSpecification)
|
||||
: base(signalRBroadcaster)
|
||||
{
|
||||
_mediaFileService = mediaFileService;
|
||||
_diskProvider = diskProvider;
|
||||
_recycleBinProvider = recycleBinProvider;
|
||||
_mediaFileDeletionService = mediaFileDeletionService;
|
||||
_seriesService = seriesService;
|
||||
_qualityUpgradableSpecification = qualityUpgradableSpecification;
|
||||
_logger = logger;
|
||||
GetResourceById = GetEpisodeFile;
|
||||
GetResourceAll = GetEpisodeFiles;
|
||||
UpdateResource = SetQuality;
|
||||
@@ -77,13 +68,15 @@ namespace NzbDrone.Api.EpisodeFiles
|
||||
private void DeleteEpisodeFile(int id)
|
||||
{
|
||||
var episodeFile = _mediaFileService.Get(id);
|
||||
var series = _seriesService.GetSeries(episodeFile.SeriesId);
|
||||
var fullPath = Path.Combine(series.Path, episodeFile.RelativePath);
|
||||
var subfolder = _diskProvider.GetParentFolder(series.Path).GetRelativePath(_diskProvider.GetParentFolder(fullPath));
|
||||
|
||||
_logger.Info("Deleting episode file: {0}", fullPath);
|
||||
_recycleBinProvider.DeleteFile(fullPath, subfolder);
|
||||
_mediaFileService.Delete(episodeFile, DeleteMediaFileReason.Manual);
|
||||
if (episodeFile == null)
|
||||
{
|
||||
throw new NzbDroneClientException(HttpStatusCode.NotFound, "Episode file not found");
|
||||
}
|
||||
|
||||
var series = _seriesService.GetSeries(episodeFile.SeriesId);
|
||||
|
||||
_mediaFileDeletionService.DeleteEpisodeFile(series, episodeFile);
|
||||
}
|
||||
|
||||
public void Handle(EpisodeFileAddedEvent message)
|
||||
|
||||
@@ -14,7 +14,7 @@ namespace NzbDrone.Api.Episodes
|
||||
{
|
||||
public abstract class EpisodeModuleWithSignalR : NzbDroneRestModuleWithSignalR<EpisodeResource, Episode>,
|
||||
IHandle<EpisodeGrabbedEvent>,
|
||||
IHandle<EpisodeDownloadedEvent>
|
||||
IHandle<EpisodeImportedEvent>
|
||||
{
|
||||
protected readonly IEpisodeService _episodeService;
|
||||
protected readonly ISeriesService _seriesService;
|
||||
@@ -115,9 +115,14 @@ namespace NzbDrone.Api.Episodes
|
||||
}
|
||||
}
|
||||
|
||||
public void Handle(EpisodeDownloadedEvent message)
|
||||
public void Handle(EpisodeImportedEvent message)
|
||||
{
|
||||
foreach (var episode in message.Episode.Episodes)
|
||||
if (!message.NewDownload)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (var episode in message.EpisodeInfo.Episodes)
|
||||
{
|
||||
BroadcastResourceChange(ModelAction.Updated, episode.Id);
|
||||
}
|
||||
|
||||
46
src/NzbDrone.Api/Extensions/Pipelines/UrlBasePipeline.cs
Normal file
46
src/NzbDrone.Api/Extensions/Pipelines/UrlBasePipeline.cs
Normal file
@@ -0,0 +1,46 @@
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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))(?:\"")(?:\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|svg))(?:\"")(?:\s(?<nohash>data-no-hash))?", RegexOptions.Compiled | RegexOptions.IgnoreCase);
|
||||
|
||||
private static string API_KEY;
|
||||
private static string URL_BASE;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Nancy.Responses;
|
||||
@@ -38,20 +38,6 @@ 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)
|
||||
|
||||
@@ -53,7 +53,7 @@ namespace NzbDrone.Api.Indexers
|
||||
|
||||
private Response DownloadRelease(ReleaseResource release)
|
||||
{
|
||||
var remoteEpisode = _remoteEpisodeCache.Find(release.Guid);
|
||||
var remoteEpisode = _remoteEpisodeCache.Find(GetCacheKey(release));
|
||||
|
||||
if (remoteEpisode == null)
|
||||
{
|
||||
@@ -68,7 +68,7 @@ namespace NzbDrone.Api.Indexers
|
||||
}
|
||||
catch (ReleaseDownloadException ex)
|
||||
{
|
||||
_logger.Error(ex);
|
||||
_logger.Error(ex, ex.Message);
|
||||
throw new NzbDroneClientException(HttpStatusCode.Conflict, "Getting release from indexer failed");
|
||||
}
|
||||
|
||||
@@ -113,8 +113,14 @@ namespace NzbDrone.Api.Indexers
|
||||
|
||||
protected override ReleaseResource MapDecision(DownloadDecision decision, int initialWeight)
|
||||
{
|
||||
_remoteEpisodeCache.Set(decision.RemoteEpisode.Release.Guid, decision.RemoteEpisode, TimeSpan.FromMinutes(30));
|
||||
return base.MapDecision(decision, 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -106,6 +106,7 @@
|
||||
<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" />
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
namespace NzbDrone.Common.Http
|
||||
namespace NzbDrone.Common.Http
|
||||
{
|
||||
public enum HttpMethod
|
||||
{
|
||||
GET,
|
||||
PUT,
|
||||
POST,
|
||||
HEAD,
|
||||
PUT,
|
||||
DELETE,
|
||||
HEAD,
|
||||
OPTIONS,
|
||||
PATCH,
|
||||
OPTIONS
|
||||
MERGE
|
||||
}
|
||||
}
|
||||
@@ -60,6 +60,11 @@ 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));
|
||||
@@ -70,4 +75,4 @@ namespace NzbDrone.Common.Reflection
|
||||
return type.GetCustomAttributes(typeof(TAttribute), true).Any();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using System;
|
||||
using System.Net;
|
||||
using NLog;
|
||||
using NzbDrone.Common.EnvironmentInfo;
|
||||
using NzbDrone.Common.Instrumentation;
|
||||
|
||||
namespace NzbDrone.Common.Security
|
||||
@@ -14,6 +15,12 @@ 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.
|
||||
|
||||
@@ -5,6 +5,7 @@ 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;
|
||||
@@ -98,7 +99,6 @@ namespace NzbDrone.Core.Test.DataAugmentation.SceneNumbering
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
[Test]
|
||||
public void should_not_fetch_scenenumbering_if_not_listed()
|
||||
{
|
||||
@@ -308,5 +308,19 @@ 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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,111 @@
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -6,7 +6,9 @@ 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;
|
||||
@@ -35,7 +37,7 @@ namespace NzbDrone.Core.Test.Download.DownloadApprovedReportsTests
|
||||
.Build();
|
||||
}
|
||||
|
||||
private RemoteEpisode GetRemoteEpisode(List<Episode> episodes, QualityModel quality)
|
||||
private RemoteEpisode GetRemoteEpisode(List<Episode> episodes, QualityModel quality, DownloadProtocol downloadProtocol = DownloadProtocol.Usenet)
|
||||
{
|
||||
var remoteEpisode = new RemoteEpisode();
|
||||
remoteEpisode.ParsedEpisodeInfo = new ParsedEpisodeInfo();
|
||||
@@ -45,6 +47,7 @@ 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()
|
||||
@@ -192,7 +195,6 @@ 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());
|
||||
@@ -209,7 +211,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>()), Times.Never());
|
||||
Mocker.GetMock<IPendingReleaseService>().Verify(v => v.Add(It.IsAny<DownloadDecision>(), It.IsAny<PendingReleaseReason>()), Times.Never());
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -223,7 +225,43 @@ 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>()), Times.Exactly(2));
|
||||
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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,156 @@
|
||||
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));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -19,6 +19,7 @@ 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()
|
||||
@@ -80,13 +81,18 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.NzbgetTests
|
||||
DownloadRate = 7000000
|
||||
});
|
||||
|
||||
var configItems = new Dictionary<string, string>();
|
||||
configItems.Add("Category1.Name", "tv");
|
||||
configItems.Add("Category1.DestDir", @"/remote/mount/tv");
|
||||
|
||||
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");
|
||||
|
||||
Mocker.GetMock<INzbgetProxy>()
|
||||
.Setup(v => v.GetConfig(It.IsAny<NzbgetSettings>()))
|
||||
.Returns(configItems);
|
||||
.Returns(_configItems);
|
||||
}
|
||||
|
||||
protected void GivenFailedDownload()
|
||||
@@ -414,5 +420,18 @@ 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -102,7 +102,7 @@ namespace NzbDrone.Core.Test.Download.Pending.PendingReleaseServiceTests
|
||||
[Test]
|
||||
public void should_add()
|
||||
{
|
||||
Subject.Add(_temporarilyRejected);
|
||||
Subject.Add(_temporarilyRejected, PendingReleaseReason.Delay);
|
||||
|
||||
VerifyInsert();
|
||||
}
|
||||
@@ -112,7 +112,7 @@ namespace NzbDrone.Core.Test.Download.Pending.PendingReleaseServiceTests
|
||||
{
|
||||
GivenHeldRelease(_release.Title, _release.Indexer, _release.PublishDate);
|
||||
|
||||
Subject.Add(_temporarilyRejected);
|
||||
Subject.Add(_temporarilyRejected, PendingReleaseReason.Delay);
|
||||
|
||||
VerifyNoInsert();
|
||||
}
|
||||
@@ -122,7 +122,7 @@ namespace NzbDrone.Core.Test.Download.Pending.PendingReleaseServiceTests
|
||||
{
|
||||
GivenHeldRelease(_release.Title + "-RP", _release.Indexer, _release.PublishDate);
|
||||
|
||||
Subject.Add(_temporarilyRejected);
|
||||
Subject.Add(_temporarilyRejected, PendingReleaseReason.Delay);
|
||||
|
||||
VerifyInsert();
|
||||
}
|
||||
@@ -132,7 +132,7 @@ namespace NzbDrone.Core.Test.Download.Pending.PendingReleaseServiceTests
|
||||
{
|
||||
GivenHeldRelease(_release.Title, "AnotherIndexer", _release.PublishDate);
|
||||
|
||||
Subject.Add(_temporarilyRejected);
|
||||
Subject.Add(_temporarilyRejected, PendingReleaseReason.Delay);
|
||||
|
||||
VerifyInsert();
|
||||
}
|
||||
@@ -142,7 +142,7 @@ namespace NzbDrone.Core.Test.Download.Pending.PendingReleaseServiceTests
|
||||
{
|
||||
GivenHeldRelease(_release.Title, _release.Indexer, _release.PublishDate.AddHours(1));
|
||||
|
||||
Subject.Add(_temporarilyRejected);
|
||||
Subject.Add(_temporarilyRejected, PendingReleaseReason.Delay);
|
||||
|
||||
VerifyInsert();
|
||||
}
|
||||
|
||||
@@ -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.GetBlockedIndexers())
|
||||
.Setup(v => v.GetBlockedProviders())
|
||||
.Returns(new List<IndexerStatus>());
|
||||
|
||||
GivenPendingRelease();
|
||||
@@ -43,8 +43,8 @@ namespace NzbDrone.Core.Test.Download.Pending.PendingReleaseServiceTests
|
||||
public void should_ignore_pending_items_from_unavailable_indexer()
|
||||
{
|
||||
Mocker.GetMock<IIndexerStatusService>()
|
||||
.Setup(v => v.GetBlockedIndexers())
|
||||
.Returns(new List<IndexerStatus> { new IndexerStatus { IndexerId = 1, DisabledTill = DateTime.UtcNow.AddHours(2) } });
|
||||
.Setup(v => v.GetBlockedProviders())
|
||||
.Returns(new List<IndexerStatus> { new IndexerStatus { ProviderId = 1, DisabledTill = DateTime.UtcNow.AddHours(2) } });
|
||||
|
||||
GivenPendingRelease();
|
||||
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
<?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>
|
||||
@@ -3,7 +3,6 @@ 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;
|
||||
|
||||
@@ -26,7 +25,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 IndexerDefinition{Name = "Test"});
|
||||
downloadClient.Setup(s => s.Definition).Returns(new DownloadClientDefinition{Name = "Test"});
|
||||
|
||||
downloadClient.Setup(s => s.GetItems())
|
||||
.Throws<Exception>();
|
||||
@@ -36,8 +35,6 @@ namespace NzbDrone.Core.Test.HealthCheck.Checks
|
||||
.Returns(new IDownloadClient[] { downloadClient.Object });
|
||||
|
||||
Subject.Check().ShouldBeError();
|
||||
|
||||
ExceptionVerification.ExpectedErrors(1);
|
||||
}
|
||||
|
||||
[Test]
|
||||
|
||||
@@ -22,7 +22,7 @@ namespace NzbDrone.Core.Test.HealthCheck.Checks
|
||||
.Returns(_indexers);
|
||||
|
||||
Mocker.GetMock<IIndexerStatusService>()
|
||||
.Setup(v => v.GetBlockedIndexers())
|
||||
.Setup(v => v.GetBlockedProviders())
|
||||
.Returns(_blockedIndexers);
|
||||
}
|
||||
|
||||
@@ -40,7 +40,7 @@ namespace NzbDrone.Core.Test.HealthCheck.Checks
|
||||
{
|
||||
_blockedIndexers.Add(new IndexerStatus
|
||||
{
|
||||
IndexerId = id,
|
||||
ProviderId = id,
|
||||
InitialFailure = DateTime.UtcNow.AddHours(-failureHours),
|
||||
MostRecentFailure = DateTime.UtcNow.AddHours(-0.1),
|
||||
EscalationLevel = 5,
|
||||
@@ -57,13 +57,6 @@ 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()
|
||||
|
||||
@@ -13,6 +13,7 @@ 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
|
||||
@@ -81,7 +82,13 @@ namespace NzbDrone.Core.Test.HistoryTests
|
||||
Path = @"C:\Test\Unsorted\Series.s01e01.mkv"
|
||||
};
|
||||
|
||||
Subject.Handle(new EpisodeImportedEvent(localEpisode, episodeFile, true, "sab", "abcd"));
|
||||
var downloadClientItem = new DownloadClientItem
|
||||
{
|
||||
DownloadClient = "sab",
|
||||
DownloadId = "abcd"
|
||||
};
|
||||
|
||||
Subject.Handle(new EpisodeImportedEvent(localEpisode, episodeFile, new List<EpisodeFile>(), true, downloadClientItem));
|
||||
|
||||
Mocker.GetMock<IHistoryRepository>()
|
||||
.Verify(v => v.Insert(It.Is<History.History>(h => h.SourceTitle == Path.GetFileNameWithoutExtension(localEpisode.Path))));
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -28,7 +28,7 @@ namespace NzbDrone.Core.Test.Housekeeping.Housekeepers
|
||||
public void should_delete_orphaned_indexerstatus()
|
||||
{
|
||||
var status = Builder<IndexerStatus>.CreateNew()
|
||||
.With(h => h.IndexerId = _indexer.Id)
|
||||
.With(h => h.ProviderId = _indexer.Id)
|
||||
.BuildNew();
|
||||
Db.Insert(status);
|
||||
|
||||
@@ -42,13 +42,13 @@ namespace NzbDrone.Core.Test.Housekeeping.Housekeepers
|
||||
GivenIndexer();
|
||||
|
||||
var status = Builder<IndexerStatus>.CreateNew()
|
||||
.With(h => h.IndexerId = _indexer.Id)
|
||||
.With(h => h.ProviderId = _indexer.Id)
|
||||
.BuildNew();
|
||||
Db.Insert(status);
|
||||
|
||||
Subject.Clean();
|
||||
AllStoredModels.Should().HaveCount(1);
|
||||
AllStoredModels.Should().Contain(h => h.IndexerId == _indexer.Id);
|
||||
AllStoredModels.Should().Contain(h => h.ProviderId == _indexer.Id);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -20,7 +20,7 @@ namespace NzbDrone.Core.Test.IndexerTests.IPTorrentsTests
|
||||
Subject.Definition = new IndexerDefinition()
|
||||
{
|
||||
Name = "IPTorrents",
|
||||
Settings = new IPTorrentsSettings() { Url = "http://fake.com/" }
|
||||
Settings = new IPTorrentsSettings() { BaseUrl = "http://fake.com/" }
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -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.FindByIndexerId(1))
|
||||
.Setup(v => v.FindByProviderId(1))
|
||||
.Returns(status);
|
||||
|
||||
Mocker.GetMock<IIndexerStatusRepository>()
|
||||
@@ -29,25 +29,16 @@ namespace NzbDrone.Core.Test.IndexerTests
|
||||
.Returns(new[] { status });
|
||||
}
|
||||
|
||||
private void VerifyUpdate(bool updated = true)
|
||||
private void VerifyUpdate()
|
||||
{
|
||||
Mocker.GetMock<IIndexerStatusRepository>()
|
||||
.Verify(v => v.Upsert(It.IsAny<IndexerStatus>()), Times.Exactly(updated ? 1 : 0));
|
||||
.Verify(v => v.Upsert(It.IsAny<IndexerStatus>()), Times.Once());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_start_backoff_on_first_failure()
|
||||
private void VerifyNoUpdate()
|
||||
{
|
||||
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);
|
||||
Mocker.GetMock<IIndexerStatusRepository>()
|
||||
.Verify(v => v.Upsert(It.IsAny<IndexerStatus>()), Times.Never());
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -59,7 +50,7 @@ namespace NzbDrone.Core.Test.IndexerTests
|
||||
|
||||
VerifyUpdate();
|
||||
|
||||
var status = Subject.GetBlockedIndexers().FirstOrDefault();
|
||||
var status = Subject.GetBlockedProviders().FirstOrDefault();
|
||||
status.Should().BeNull();
|
||||
}
|
||||
|
||||
@@ -70,22 +61,7 @@ namespace NzbDrone.Core.Test.IndexerTests
|
||||
|
||||
Subject.RecordSuccess(1);
|
||||
|
||||
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);
|
||||
VerifyNoUpdate();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,7 +21,7 @@ namespace NzbDrone.Core.Test.IndexerTests.NewznabTests
|
||||
{
|
||||
_settings = new NewznabSettings()
|
||||
{
|
||||
Url = "http://indxer.local"
|
||||
BaseUrl = "http://indxer.local"
|
||||
};
|
||||
|
||||
_caps = ReadAllText("Files/Indexers/Newznab/newznab_caps.xml");
|
||||
|
||||
@@ -7,6 +7,7 @@ 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;
|
||||
|
||||
@@ -26,7 +27,7 @@ namespace NzbDrone.Core.Test.IndexerTests.NewznabTests
|
||||
Name = "Newznab",
|
||||
Settings = new NewznabSettings()
|
||||
{
|
||||
Url = "http://indexer.local/",
|
||||
BaseUrl = "http://indexer.local/",
|
||||
Categories = new int[] { 1 }
|
||||
}
|
||||
};
|
||||
@@ -63,6 +64,35 @@ 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()
|
||||
{
|
||||
|
||||
@@ -20,10 +20,10 @@ namespace NzbDrone.Core.Test.IndexerTests.NewznabTests
|
||||
{
|
||||
Subject.Settings = new NewznabSettings()
|
||||
{
|
||||
Url = "http://127.0.0.1:1234/",
|
||||
Categories = new [] { 1, 2 },
|
||||
AnimeCategories = new [] { 3, 4 },
|
||||
ApiKey = "abcd",
|
||||
BaseUrl = "http://127.0.0.1:1234/",
|
||||
Categories = new [] { 1, 2 },
|
||||
AnimeCategories = new [] { 3, 4 },
|
||||
ApiKey = "abcd",
|
||||
};
|
||||
|
||||
_singleEpisodeSearchCriteria = new SingleEpisodeSearchCriteria
|
||||
@@ -84,7 +84,7 @@ namespace NzbDrone.Core.Test.IndexerTests.NewznabTests
|
||||
|
||||
page.Url.FullUri.Should().Contain("&cat=3,4&");
|
||||
}
|
||||
|
||||
|
||||
[Test]
|
||||
public void should_use_mode_search_for_anime()
|
||||
{
|
||||
|
||||
@@ -15,12 +15,12 @@ namespace NzbDrone.Core.Test.IndexerTests.NewznabTests
|
||||
var setting = new NewznabSettings()
|
||||
{
|
||||
ApiKey = "",
|
||||
Url = url
|
||||
BaseUrl = url
|
||||
};
|
||||
|
||||
|
||||
setting.Validate().IsValid.Should().BeFalse();
|
||||
setting.Validate().Errors.Should().Contain(c => c.PropertyName == "ApiKey");
|
||||
setting.Validate().Errors.Should().Contain(c => c.PropertyName == nameof(NewznabSettings.ApiKey));
|
||||
|
||||
}
|
||||
|
||||
@@ -32,13 +32,13 @@ namespace NzbDrone.Core.Test.IndexerTests.NewznabTests
|
||||
var setting = new NewznabSettings
|
||||
{
|
||||
ApiKey = "",
|
||||
Url = url
|
||||
BaseUrl = url
|
||||
};
|
||||
|
||||
|
||||
setting.Validate().IsValid.Should().BeFalse();
|
||||
setting.Validate().Errors.Should().NotContain(c => c.PropertyName == "ApiKey");
|
||||
setting.Validate().Errors.Should().Contain(c => c.PropertyName == "Url");
|
||||
setting.Validate().Errors.Should().NotContain(c => c.PropertyName == nameof(NewznabSettings.ApiKey));
|
||||
setting.Validate().Errors.Should().Contain(c => c.PropertyName == nameof(NewznabSettings.BaseUrl));
|
||||
|
||||
}
|
||||
|
||||
@@ -49,11 +49,11 @@ namespace NzbDrone.Core.Test.IndexerTests.NewznabTests
|
||||
var setting = new NewznabSettings()
|
||||
{
|
||||
ApiKey = "",
|
||||
Url = url
|
||||
BaseUrl = url
|
||||
};
|
||||
|
||||
|
||||
setting.Validate().IsValid.Should().BeTrue();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,14 +1,17 @@
|
||||
using System;
|
||||
using NzbDrone.Core.Indexers;
|
||||
using NzbDrone.Core.ThingiProvider;
|
||||
using NzbDrone.Core.Validation;
|
||||
|
||||
namespace NzbDrone.Core.Test.IndexerTests
|
||||
{
|
||||
public class TestIndexerSettings : IProviderConfig
|
||||
public class TestIndexerSettings : IIndexerSettings
|
||||
{
|
||||
public NzbDroneValidationResult Validate()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public string BaseUrl { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -254,13 +254,11 @@ 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);
|
||||
|
||||
@@ -25,7 +25,7 @@ namespace NzbDrone.Core.Test.IndexerTests.TorznabTests
|
||||
Name = "Torznab",
|
||||
Settings = new TorznabSettings()
|
||||
{
|
||||
Url = "http://indexer.local/",
|
||||
BaseUrl = "http://indexer.local/",
|
||||
Categories = new int[] { 1 }
|
||||
}
|
||||
};
|
||||
@@ -44,7 +44,7 @@ namespace NzbDrone.Core.Test.IndexerTests.TorznabTests
|
||||
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(5);
|
||||
@@ -97,6 +97,37 @@ 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()
|
||||
{
|
||||
|
||||
@@ -164,11 +164,9 @@ 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(true);
|
||||
.Returns(DetectSampleResult.Sample);
|
||||
|
||||
Subject.ProcessRootFolder(new DirectoryInfo(_droneFactory));
|
||||
|
||||
@@ -236,11 +234,9 @@ 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(true);
|
||||
.Returns(DetectSampleResult.Sample);
|
||||
|
||||
Mocker.GetMock<IDiskProvider>()
|
||||
.Setup(s => s.GetFiles(It.IsAny<string>(), SearchOption.AllDirectories))
|
||||
@@ -347,11 +343,9 @@ 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(true);
|
||||
.Returns(DetectSampleResult.Sample);
|
||||
|
||||
Mocker.GetMock<IDiskProvider>()
|
||||
.Setup(s => s.GetFileSize(It.IsAny<string>()))
|
||||
|
||||
@@ -10,11 +10,12 @@ 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 SampleServiceFixture : CoreTest<DetectSample>
|
||||
public class DetectSampleFixture : CoreTest<DetectSample>
|
||||
{
|
||||
private Series _series;
|
||||
private LocalEpisode _localEpisode;
|
||||
@@ -42,11 +43,6 @@ namespace NzbDrone.Core.Test.MediaFiles.EpisodeImport
|
||||
};
|
||||
}
|
||||
|
||||
private void GivenFileSize(long size)
|
||||
{
|
||||
_localEpisode.Size = size;
|
||||
}
|
||||
|
||||
private void GivenRuntime(int seconds)
|
||||
{
|
||||
Mocker.GetMock<IVideoFileInfoReader>()
|
||||
@@ -58,7 +54,7 @@ namespace NzbDrone.Core.Test.MediaFiles.EpisodeImport
|
||||
public void should_return_false_if_season_zero()
|
||||
{
|
||||
_localEpisode.Episodes[0].SeasonNumber = 0;
|
||||
ShouldBeFalse();
|
||||
ShouldBeNotSample();
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -66,7 +62,7 @@ namespace NzbDrone.Core.Test.MediaFiles.EpisodeImport
|
||||
{
|
||||
_localEpisode.Path = @"C:\Test\some.show.s01e01.flv";
|
||||
|
||||
ShouldBeFalse();
|
||||
ShouldBeNotSample();
|
||||
|
||||
Mocker.GetMock<IVideoFileInfoReader>().Verify(c => c.GetRunTime(It.IsAny<string>()), Times.Never());
|
||||
}
|
||||
@@ -76,7 +72,7 @@ namespace NzbDrone.Core.Test.MediaFiles.EpisodeImport
|
||||
{
|
||||
_localEpisode.Path = @"C:\Test\some.show.s01e01.strm";
|
||||
|
||||
ShouldBeFalse();
|
||||
ShouldBeNotSample();
|
||||
|
||||
Mocker.GetMock<IVideoFileInfoReader>().Verify(c => c.GetRunTime(It.IsAny<string>()), Times.Never());
|
||||
}
|
||||
@@ -85,12 +81,9 @@ 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());
|
||||
@@ -101,7 +94,7 @@ namespace NzbDrone.Core.Test.MediaFiles.EpisodeImport
|
||||
{
|
||||
GivenRuntime(60);
|
||||
|
||||
ShouldBeTrue();
|
||||
ShouldBeSample();
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -109,7 +102,7 @@ namespace NzbDrone.Core.Test.MediaFiles.EpisodeImport
|
||||
{
|
||||
GivenRuntime(600);
|
||||
|
||||
ShouldBeFalse();
|
||||
ShouldBeNotSample();
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -118,7 +111,7 @@ namespace NzbDrone.Core.Test.MediaFiles.EpisodeImport
|
||||
_series.Runtime = 6;
|
||||
GivenRuntime(299);
|
||||
|
||||
ShouldBeFalse();
|
||||
ShouldBeNotSample();
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -127,7 +120,7 @@ namespace NzbDrone.Core.Test.MediaFiles.EpisodeImport
|
||||
_series.Runtime = 2;
|
||||
GivenRuntime(60);
|
||||
|
||||
ShouldBeFalse();
|
||||
ShouldBeNotSample();
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -136,29 +129,21 @@ namespace NzbDrone.Core.Test.MediaFiles.EpisodeImport
|
||||
_series.Runtime = 2;
|
||||
GivenRuntime(10);
|
||||
|
||||
ShouldBeTrue();
|
||||
ShouldBeSample();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_fall_back_to_file_size_if_mediainfo_dll_not_found_acceptable_size()
|
||||
public void should_return_indeterminate_if_mediainfo_result_is_null()
|
||||
{
|
||||
Mocker.GetMock<IVideoFileInfoReader>()
|
||||
.Setup(s => s.GetRunTime(It.IsAny<string>()))
|
||||
.Throws<DllNotFoundException>();
|
||||
.Returns((TimeSpan?)null);
|
||||
|
||||
GivenFileSize(1000.Megabytes());
|
||||
ShouldBeFalse();
|
||||
}
|
||||
Subject.IsSample(_localEpisode.Series,
|
||||
_localEpisode.Path,
|
||||
_localEpisode.IsSpecial).Should().Be(DetectSampleResult.Indeterminate);
|
||||
|
||||
[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();
|
||||
ExceptionVerification.ExpectedErrors(1);
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -167,7 +152,7 @@ namespace NzbDrone.Core.Test.MediaFiles.EpisodeImport
|
||||
GivenRuntime(600);
|
||||
_series.SeriesType = SeriesTypes.Daily;
|
||||
_localEpisode.Episodes[0].SeasonNumber = 0;
|
||||
ShouldBeFalse();
|
||||
ShouldBeNotSample();
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -176,25 +161,21 @@ namespace NzbDrone.Core.Test.MediaFiles.EpisodeImport
|
||||
_series.SeriesType = SeriesTypes.Anime;
|
||||
_localEpisode.Episodes[0].SeasonNumber = 0;
|
||||
|
||||
ShouldBeFalse();
|
||||
ShouldBeNotSample();
|
||||
}
|
||||
|
||||
private void ShouldBeTrue()
|
||||
private void ShouldBeSample()
|
||||
{
|
||||
Subject.IsSample(_localEpisode.Series,
|
||||
_localEpisode.Quality,
|
||||
_localEpisode.Path,
|
||||
_localEpisode.Size,
|
||||
_localEpisode.IsSpecial).Should().BeTrue();
|
||||
}
|
||||
|
||||
private void ShouldBeFalse()
|
||||
{
|
||||
Subject.IsSample(_localEpisode.Series,
|
||||
_localEpisode.Quality,
|
||||
_localEpisode.Path,
|
||||
_localEpisode.Size,
|
||||
_localEpisode.IsSpecial).Should().BeFalse();
|
||||
_localEpisode.IsSpecial).Should().Be(DetectSampleResult.Sample);
|
||||
}
|
||||
|
||||
private void ShouldBeNotSample()
|
||||
{
|
||||
Subject.IsSample(_localEpisode.Series,
|
||||
_localEpisode.Path,
|
||||
_localEpisode.IsSpecial).Should().Be(DetectSampleResult.NotSample);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -333,8 +333,16 @@ namespace NzbDrone.Core.Test.MediaFiles.EpisodeImport
|
||||
GivenVideoFiles(videoFiles.ToList());
|
||||
|
||||
Mocker.GetMock<IDetectSample>()
|
||||
.Setup(s => s.IsSample(_series, It.IsAny<QualityModel>(), It.Is<string>(c => c.Contains("sample")), It.IsAny<long>(), It.IsAny<bool>()))
|
||||
.Returns(true);
|
||||
.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;
|
||||
});
|
||||
|
||||
var folderInfo = Parser.Parser.ParseTitle("Series.Title.S01E01");
|
||||
|
||||
|
||||
@@ -63,6 +63,35 @@ 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()
|
||||
{
|
||||
|
||||
@@ -0,0 +1,140 @@
|
||||
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());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,11 +1,12 @@
|
||||
using FluentAssertions;
|
||||
using NUnit.Framework;
|
||||
using NzbDrone.Core.MediaFiles.MediaInfo;
|
||||
using NzbDrone.Test.Common;
|
||||
|
||||
namespace NzbDrone.Core.Test.MediaFiles.MediaInfo
|
||||
namespace NzbDrone.Core.Test.MediaFiles.MediaInfo.MediaInfoFormatterTests
|
||||
{
|
||||
[TestFixture]
|
||||
public class FormattedAudioChannelsFixture
|
||||
public class FormatAudioChannelsFixture : TestBase
|
||||
{
|
||||
[Test]
|
||||
public void should_subtract_one_from_AudioChannels_as_total_channels_if_LFE_in_AudioChannelPositionsText()
|
||||
@@ -17,7 +18,7 @@ namespace NzbDrone.Core.Test.MediaFiles.MediaInfo
|
||||
AudioChannelPositionsText = "Front: L C R, Side: L R, LFE"
|
||||
};
|
||||
|
||||
mediaInfoModel.FormattedAudioChannels.Should().Be(5.1m);
|
||||
MediaInfoFormatter.FormatAudioChannels(mediaInfoModel).Should().Be(5.1m);
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -30,7 +31,7 @@ namespace NzbDrone.Core.Test.MediaFiles.MediaInfo
|
||||
AudioChannelPositionsText = "Front: L R"
|
||||
};
|
||||
|
||||
mediaInfoModel.FormattedAudioChannels.Should().Be(2);
|
||||
MediaInfoFormatter.FormatAudioChannels(mediaInfoModel).Should().Be(2);
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -44,7 +45,7 @@ namespace NzbDrone.Core.Test.MediaFiles.MediaInfo
|
||||
SchemaRevision = 2
|
||||
};
|
||||
|
||||
mediaInfoModel.FormattedAudioChannels.Should().Be(0);
|
||||
MediaInfoFormatter.FormatAudioChannels(mediaInfoModel).Should().Be(0);
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -58,7 +59,7 @@ namespace NzbDrone.Core.Test.MediaFiles.MediaInfo
|
||||
SchemaRevision = 3
|
||||
};
|
||||
|
||||
mediaInfoModel.FormattedAudioChannels.Should().Be(2);
|
||||
MediaInfoFormatter.FormatAudioChannels(mediaInfoModel).Should().Be(2);
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -72,7 +73,7 @@ namespace NzbDrone.Core.Test.MediaFiles.MediaInfo
|
||||
SchemaRevision = 3
|
||||
};
|
||||
|
||||
mediaInfoModel.FormattedAudioChannels.Should().Be(2);
|
||||
MediaInfoFormatter.FormatAudioChannels(mediaInfoModel).Should().Be(2);
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -86,7 +87,7 @@ namespace NzbDrone.Core.Test.MediaFiles.MediaInfo
|
||||
SchemaRevision = 3
|
||||
};
|
||||
|
||||
mediaInfoModel.FormattedAudioChannels.Should().Be(5.1m);
|
||||
MediaInfoFormatter.FormatAudioChannels(mediaInfoModel).Should().Be(5.1m);
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -100,7 +101,7 @@ namespace NzbDrone.Core.Test.MediaFiles.MediaInfo
|
||||
SchemaRevision = 3
|
||||
};
|
||||
|
||||
mediaInfoModel.FormattedAudioChannels.Should().Be(7.1m);
|
||||
MediaInfoFormatter.FormatAudioChannels(mediaInfoModel).Should().Be(7.1m);
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -114,7 +115,7 @@ namespace NzbDrone.Core.Test.MediaFiles.MediaInfo
|
||||
SchemaRevision = 3
|
||||
};
|
||||
|
||||
mediaInfoModel.FormattedAudioChannels.Should().Be(7.1m);
|
||||
MediaInfoFormatter.FormatAudioChannels(mediaInfoModel).Should().Be(7.1m);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
using System.IO;
|
||||
using System.IO;
|
||||
using FluentAssertions;
|
||||
using Moq;
|
||||
using NUnit.Framework;
|
||||
@@ -30,11 +30,9 @@ namespace NzbDrone.Core.Test.MediaFiles.MediaInfo
|
||||
{
|
||||
var path = Path.Combine(TestContext.CurrentContext.TestDirectory, "Files", "Media", "H264_sample.mp4");
|
||||
|
||||
Subject.GetRunTime(path).Seconds.Should().Be(10);
|
||||
|
||||
Subject.GetRunTime(path).Value.Seconds.Should().Be(10);
|
||||
}
|
||||
|
||||
|
||||
[Test]
|
||||
public void get_info()
|
||||
{
|
||||
@@ -86,7 +84,6 @@ namespace NzbDrone.Core.Test.MediaFiles.MediaInfo
|
||||
info.VideoCodec.Should().Be("AVC");
|
||||
info.VideoFps.Should().Be(24);
|
||||
info.Width.Should().Be(480);
|
||||
|
||||
}
|
||||
|
||||
[Test]
|
||||
|
||||
@@ -161,9 +161,11 @@
|
||||
<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" />
|
||||
@@ -178,6 +180,7 @@
|
||||
<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" />
|
||||
@@ -235,6 +238,7 @@
|
||||
<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" />
|
||||
@@ -280,7 +284,7 @@
|
||||
<Compile Include="MediaFiles\DownloadedEpisodesImportServiceFixture.cs" />
|
||||
<Compile Include="MediaFiles\EpisodeFileMovingServiceTests\MoveEpisodeFileFixture.cs" />
|
||||
<Compile Include="MediaFiles\EpisodeImport\ImportDecisionMakerFixture.cs" />
|
||||
<Compile Include="MediaFiles\EpisodeImport\SampleServiceFixture.cs" />
|
||||
<Compile Include="MediaFiles\EpisodeImport\DetectSampleFixture.cs" />
|
||||
<Compile Include="MediaFiles\EpisodeImport\Specifications\FreeSpaceSpecificationFixture.cs" />
|
||||
<Compile Include="MediaFiles\EpisodeImport\Specifications\FullSeasonSpecificationFixture.cs" />
|
||||
<Compile Include="MediaFiles\EpisodeImport\Specifications\SameFileSpecificationFixture.cs" />
|
||||
@@ -290,8 +294,11 @@
|
||||
<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\FormattedAudioChannelsFixture.cs" />
|
||||
<Compile Include="MediaFiles\MediaInfo\MediaInfoFormatterTests\FormatAudioCodecFixture.cs" />
|
||||
<Compile Include="MediaFiles\MediaInfo\MediaInfoFormatterTests\FormatVideoCodecFixture.cs" />
|
||||
<Compile Include="MediaFiles\MediaInfo\MediaInfoFormatterTests\FormatAudioChannelsFixture.cs" />
|
||||
<Compile Include="Messaging\Commands\CommandQueueManagerFixture.cs" />
|
||||
<Compile Include="MetadataSource\SkyHook\SkyHookProxySearchFixture.cs" />
|
||||
<Compile Include="MetadataSource\SearchSeriesComparerFixture.cs" />
|
||||
@@ -301,6 +308,7 @@
|
||||
<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="ParserTests\MiniSeriesEpisodeParserFixture.cs" />
|
||||
<Compile Include="Qualities\RevisionComparableFixture.cs" />
|
||||
<Compile Include="QueueTests\QueueServiceFixture.cs" />
|
||||
@@ -366,7 +374,8 @@
|
||||
<Compile Include="Qualities\QualityModelComparerFixture.cs" />
|
||||
<Compile Include="RootFolderTests\RootFolderServiceFixture.cs" />
|
||||
<Compile Include="SeriesStatsTests\SeriesStatisticsFixture.cs" />
|
||||
<Compile Include="ThingiProvider\ProviderBaseFixture.cs" />
|
||||
<Compile Include="ThingiProviderTests\ProviderStatusServiceFixture.cs" />
|
||||
<Compile Include="ThingiProviderTests\ProviderBaseFixture.cs" />
|
||||
<Compile Include="ThingiProviderTests\NullConfigFixture.cs" />
|
||||
<Compile Include="TvTests\EpisodeServiceTests\FindEpisodeByTitleFixture.cs" />
|
||||
<Compile Include="TvTests\EpisodeServiceTests\HandleEpisodeFileDeletedFixture.cs" />
|
||||
@@ -430,6 +439,9 @@
|
||||
<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>
|
||||
|
||||
@@ -0,0 +1,83 @@
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -14,4 +14,4 @@ namespace NzbDrone.Core.Test.ThingiProviderTests
|
||||
Subject.Validate().IsValid.Should().BeTrue();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,9 +5,8 @@ using NzbDrone.Core.Indexers;
|
||||
using NzbDrone.Core.Indexers.Newznab;
|
||||
using NzbDrone.Core.Test.Framework;
|
||||
|
||||
namespace NzbDrone.Core.Test.ThingiProvider
|
||||
namespace NzbDrone.Core.Test.ThingiProviderTests
|
||||
{
|
||||
|
||||
public class ProviderRepositoryFixture : DbTest<IndexerRepository, IndexerDefinition>
|
||||
{
|
||||
[Test]
|
||||
@@ -27,4 +26,4 @@ namespace NzbDrone.Core.Test.ThingiProvider
|
||||
storedSetting.ShouldBeEquivalentTo(newznabSettings, o=>o.IncludingAllRuntimeProperties());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,126 @@
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -194,6 +194,46 @@ 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>()
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
@@ -142,7 +142,21 @@ namespace NzbDrone.Core.Configuration
|
||||
|
||||
public bool LaunchBrowser => GetValueBoolean("LaunchBrowser", true);
|
||||
|
||||
public string ApiKey => GetValue("ApiKey", GenerateApiKey());
|
||||
public string ApiKey
|
||||
{
|
||||
get
|
||||
{
|
||||
var apiKey = GetValue("ApiKey", GenerateApiKey());
|
||||
|
||||
if (apiKey.IsNullOrWhiteSpace())
|
||||
{
|
||||
apiKey = GenerateApiKey();
|
||||
SetValue("ApiKey", apiKey);
|
||||
}
|
||||
|
||||
return apiKey;
|
||||
}
|
||||
}
|
||||
|
||||
public AuthenticationType AuthenticationMethod
|
||||
{
|
||||
|
||||
@@ -61,7 +61,15 @@ namespace NzbDrone.Core.DataAugmentation.Xem
|
||||
|
||||
if (episode == null)
|
||||
{
|
||||
_logger.Debug("Information hasn't been added to TheTVDB yet, skipping.");
|
||||
_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);
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Data;
|
||||
using FluentMigrator;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using NzbDrone.Common.Extensions;
|
||||
using NzbDrone.Common.Serializer;
|
||||
using NzbDrone.Core.Datastore.Migration.Framework;
|
||||
|
||||
namespace NzbDrone.Core.Datastore.Migration
|
||||
{
|
||||
[Migration(113)]
|
||||
public class consolidate_indexer_baseurl : NzbDroneMigrationBase
|
||||
{
|
||||
protected override void MainDbUpgrade()
|
||||
{
|
||||
Execute.WithConnection(RenameUrlToBaseUrl);
|
||||
}
|
||||
|
||||
private void RenameUrlToBaseUrl(IDbConnection conn, IDbTransaction tran)
|
||||
{
|
||||
using (var cmd = conn.CreateCommand())
|
||||
{
|
||||
cmd.Transaction = tran;
|
||||
cmd.CommandText = "SELECT Id, Settings FROM Indexers WHERE ConfigContract IN ('NewznabSettings', 'TorznabSettings', 'IPTorrentsSettings', 'OmgwtfnzbsSettings')";
|
||||
|
||||
using (var reader = cmd.ExecuteReader())
|
||||
{
|
||||
while (reader.Read())
|
||||
{
|
||||
var id = reader.GetInt32(0);
|
||||
var settings = reader.GetString(1);
|
||||
|
||||
if (settings.IsNotNullOrWhiteSpace())
|
||||
{
|
||||
var jsonObject = Json.Deserialize<JObject>(settings);
|
||||
|
||||
if (jsonObject.Property("url") != null)
|
||||
{
|
||||
jsonObject.AddFirst(new JProperty("baseUrl", jsonObject["url"]));
|
||||
jsonObject.Remove("url");
|
||||
settings = jsonObject.ToJson();
|
||||
|
||||
using (var updateCmd = conn.CreateCommand())
|
||||
{
|
||||
updateCmd.Transaction = tran;
|
||||
updateCmd.CommandText = "UPDATE Indexers SET Settings = ? WHERE Id = ?";
|
||||
updateCmd.AddParameter(settings);
|
||||
updateCmd.AddParameter(id);
|
||||
updateCmd.ExecuteNonQuery();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
using FluentMigrator;
|
||||
using NzbDrone.Core.Datastore.Migration.Framework;
|
||||
|
||||
namespace NzbDrone.Core.Datastore.Migration
|
||||
{
|
||||
[Migration(114)]
|
||||
public class rename_indexer_status_id : NzbDroneMigrationBase
|
||||
{
|
||||
protected override void MainDbUpgrade()
|
||||
{
|
||||
Rename.Column("IndexerId").OnTable("IndexerStatus").To("ProviderId");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
14
src/NzbDrone.Core/Datastore/Migration/116_disable_nyaa.cs
Normal file
14
src/NzbDrone.Core/Datastore/Migration/116_disable_nyaa.cs
Normal file
@@ -0,0 +1,14 @@
|
||||
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%';");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Data;
|
||||
using System.Linq;
|
||||
using FluentMigrator;
|
||||
@@ -7,6 +8,7 @@ using FluentMigrator.Model;
|
||||
using FluentMigrator.Runner;
|
||||
using FluentMigrator.Runner.Generators.SQLite;
|
||||
using FluentMigrator.Runner.Processors.SQLite;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace NzbDrone.Core.Datastore.Migration.Framework
|
||||
{
|
||||
@@ -62,6 +64,46 @@ namespace NzbDrone.Core.Datastore.Migration.Framework
|
||||
ProcessAlterTable(tableDefinition);
|
||||
}
|
||||
|
||||
public override void Process(RenameColumnExpression expression)
|
||||
{
|
||||
var tableDefinition = GetTableSchema(expression.TableName);
|
||||
|
||||
var oldColumnDefinitions = tableDefinition.Columns.ToList();
|
||||
var columnDefinitions = tableDefinition.Columns.ToList();
|
||||
var columnIndex = columnDefinitions.FindIndex(c => c.Name == expression.OldName);
|
||||
|
||||
if (columnIndex == -1)
|
||||
{
|
||||
throw new ApplicationException(string.Format("Column {0} does not exist on table {1}.", expression.OldName, expression.TableName));
|
||||
}
|
||||
|
||||
if (columnDefinitions.Any(c => c.Name == expression.NewName))
|
||||
{
|
||||
throw new ApplicationException(string.Format("Column {0} already exists on table {1}.", expression.NewName, expression.TableName));
|
||||
}
|
||||
|
||||
oldColumnDefinitions[columnIndex] = (ColumnDefinition)columnDefinitions[columnIndex].Clone();
|
||||
columnDefinitions[columnIndex].Name = expression.NewName;
|
||||
|
||||
foreach (var index in tableDefinition.Indexes)
|
||||
{
|
||||
if (index.Name.StartsWith("IX_"))
|
||||
{
|
||||
index.Name = Regex.Replace(index.Name, "(?<=_)" + Regex.Escape(expression.OldName) + "(?=_|$)", Regex.Escape(expression.NewName));
|
||||
}
|
||||
|
||||
foreach (var column in index.Columns)
|
||||
{
|
||||
if (column.Name == expression.OldName)
|
||||
{
|
||||
column.Name = expression.NewName;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ProcessAlterTable(tableDefinition, oldColumnDefinitions);
|
||||
}
|
||||
|
||||
protected virtual TableDefinition GetTableSchema(string tableName)
|
||||
{
|
||||
var schemaDumper = new SqliteSchemaDumper(this, Announcer);
|
||||
@@ -70,7 +112,7 @@ namespace NzbDrone.Core.Datastore.Migration.Framework
|
||||
return schema.Single(v => v.Name == tableName);
|
||||
}
|
||||
|
||||
protected virtual void ProcessAlterTable(TableDefinition tableDefinition)
|
||||
protected virtual void ProcessAlterTable(TableDefinition tableDefinition, List<ColumnDefinition> oldColumnDefinitions = null)
|
||||
{
|
||||
var tableName = tableDefinition.Name;
|
||||
var tempTableName = tableName + "_temp";
|
||||
@@ -83,11 +125,12 @@ namespace NzbDrone.Core.Datastore.Migration.Framework
|
||||
|
||||
// What is the cleanest way to do this? Add function to Generator?
|
||||
var quoter = new SQLiteQuoter();
|
||||
var columnsToTransfer = string.Join(", ", tableDefinition.Columns.Select(c => quoter.QuoteColumnName(c.Name)));
|
||||
var columnsToInsert = string.Join(", ", tableDefinition.Columns.Select(c => quoter.QuoteColumnName(c.Name)));
|
||||
var columnsToFetch = string.Join(", ", (oldColumnDefinitions ?? tableDefinition.Columns).Select(c => quoter.QuoteColumnName(c.Name)));
|
||||
|
||||
Process(new CreateTableExpression() { TableName = tempTableName, Columns = tableDefinition.Columns.ToList() });
|
||||
|
||||
Process(string.Format("INSERT INTO {0} SELECT {1} FROM {2}", quoter.QuoteTableName(tempTableName), columnsToTransfer, quoter.QuoteTableName(tableName)));
|
||||
Process(string.Format("INSERT INTO {0} ({1}) SELECT {2} FROM {3}", quoter.QuoteTableName(tempTableName), columnsToInsert, columnsToFetch, quoter.QuoteTableName(tableName)));
|
||||
|
||||
Process(new DeleteTableExpression() { TableName = tableName });
|
||||
|
||||
|
||||
@@ -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,6 +116,7 @@ namespace NzbDrone.Core.Datastore
|
||||
.Ignore(c => c.Message);
|
||||
|
||||
Mapper.Entity<IndexerStatus>().RegisterModel("IndexerStatus");
|
||||
Mapper.Entity<DownloadClientStatus>().RegisterModel("DownloadClientStatus");
|
||||
}
|
||||
|
||||
private static void RegisterMappers()
|
||||
@@ -171,4 +172,4 @@ namespace NzbDrone.Core.Datastore
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
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());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
using NLog;
|
||||
using System;
|
||||
using NLog;
|
||||
using NzbDrone.Core.Configuration;
|
||||
using NzbDrone.Core.IndexerSearch.Definitions;
|
||||
using NzbDrone.Core.Parser.Model;
|
||||
@@ -29,6 +30,7 @@ namespace NzbDrone.Core.DecisionEngine.Specifications
|
||||
|
||||
var age = subject.Release.AgeMinutes;
|
||||
var minimumAge = _configService.MinimumAge;
|
||||
var ageRounded = Math.Round(age, 1);
|
||||
|
||||
if (minimumAge == 0)
|
||||
{
|
||||
@@ -37,15 +39,15 @@ namespace NzbDrone.Core.DecisionEngine.Specifications
|
||||
}
|
||||
|
||||
|
||||
_logger.Debug("Checking if report meets minimum age requirements. {0}", age);
|
||||
_logger.Debug("Checking if report meets minimum age requirements. {0}", ageRounded);
|
||||
|
||||
if (age < 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("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("Release is {0} minutes old, greater than minimum age of {1} minutes", age, minimumAge);
|
||||
_logger.Debug("Release is {0} minutes old, greater than minimum age of {1} minutes", ageRounded, minimumAge);
|
||||
|
||||
return Decision.Accept();
|
||||
}
|
||||
|
||||
@@ -1,38 +0,0 @@
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -119,9 +119,9 @@ namespace NzbDrone.Core.Download.Clients.Blackhole
|
||||
DeleteItemData(downloadId);
|
||||
}
|
||||
|
||||
public override DownloadClientStatus GetStatus()
|
||||
public override DownloadClientInfo GetStatus()
|
||||
{
|
||||
return new DownloadClientStatus
|
||||
return new DownloadClientInfo
|
||||
{
|
||||
IsLocalhost = true,
|
||||
OutputRootFolders = new List<OsPath> { new OsPath(Settings.WatchFolder) }
|
||||
|
||||
@@ -86,9 +86,9 @@ namespace NzbDrone.Core.Download.Clients.Blackhole
|
||||
DeleteItemData(downloadId);
|
||||
}
|
||||
|
||||
public override DownloadClientStatus GetStatus()
|
||||
public override DownloadClientInfo GetStatus()
|
||||
{
|
||||
return new DownloadClientStatus
|
||||
return new DownloadClientInfo
|
||||
{
|
||||
IsLocalhost = true,
|
||||
OutputRootFolders = new List<OsPath> { new OsPath(Settings.WatchFolder) }
|
||||
|
||||
@@ -81,21 +81,13 @@ namespace NzbDrone.Core.Download.Clients.Deluge
|
||||
{
|
||||
IEnumerable<DelugeTorrent> torrents;
|
||||
|
||||
try
|
||||
if (!Settings.TvCategory.IsNullOrWhiteSpace())
|
||||
{
|
||||
if (!Settings.TvCategory.IsNullOrWhiteSpace())
|
||||
{
|
||||
torrents = _proxy.GetTorrentsByLabel(Settings.TvCategory, Settings);
|
||||
}
|
||||
else
|
||||
{
|
||||
torrents = _proxy.GetTorrents(Settings);
|
||||
}
|
||||
torrents = _proxy.GetTorrentsByLabel(Settings.TvCategory, Settings);
|
||||
}
|
||||
catch (DownloadClientException ex)
|
||||
else
|
||||
{
|
||||
_logger.Error(ex, "Couldn't get list of torrents");
|
||||
return Enumerable.Empty<DownloadClientItem>();
|
||||
torrents = _proxy.GetTorrents(Settings);
|
||||
}
|
||||
|
||||
var items = new List<DownloadClientItem>();
|
||||
@@ -151,7 +143,7 @@ namespace NzbDrone.Core.Download.Clients.Deluge
|
||||
_proxy.RemoveTorrent(downloadId.ToLower(), deleteData, Settings);
|
||||
}
|
||||
|
||||
public override DownloadClientStatus GetStatus()
|
||||
public override DownloadClientInfo GetStatus()
|
||||
{
|
||||
var config = _proxy.GetConfig(Settings);
|
||||
|
||||
@@ -162,7 +154,7 @@ namespace NzbDrone.Core.Download.Clients.Deluge
|
||||
destDir = new OsPath(config.GetValueOrDefault("move_completed_path") as string);
|
||||
}
|
||||
|
||||
var status = new DownloadClientStatus
|
||||
var status = new DownloadClientInfo
|
||||
{
|
||||
IsLocalhost = Settings.Host == "127.0.0.1" || Settings.Host == "localhost"
|
||||
};
|
||||
@@ -191,12 +183,13 @@ namespace NzbDrone.Core.Download.Clients.Deluge
|
||||
}
|
||||
catch (DownloadClientAuthenticationException ex)
|
||||
{
|
||||
_logger.Error(ex);
|
||||
_logger.Error(ex, ex.Message);
|
||||
|
||||
return new NzbDroneValidationFailure("Password", "Authentication failed");
|
||||
}
|
||||
catch (WebException ex)
|
||||
{
|
||||
_logger.Error(ex);
|
||||
_logger.Error(ex, "Unble to test connection");
|
||||
switch (ex.Status)
|
||||
{
|
||||
case WebExceptionStatus.ConnectFailure:
|
||||
@@ -220,7 +213,7 @@ namespace NzbDrone.Core.Download.Clients.Deluge
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.Error(ex);
|
||||
_logger.Error(ex, "Failed to test connection");
|
||||
return new NzbDroneValidationFailure(string.Empty, "Unknown exception: " + ex.Message);
|
||||
}
|
||||
|
||||
@@ -271,7 +264,7 @@ namespace NzbDrone.Core.Download.Clients.Deluge
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.Error(ex);
|
||||
_logger.Error(ex, "Unable to get torrents");
|
||||
return new NzbDroneValidationFailure(string.Empty, "Failed to get the list of torrents: " + ex.Message);
|
||||
}
|
||||
|
||||
|
||||
@@ -231,7 +231,7 @@ namespace NzbDrone.Core.Download.Clients.Deluge
|
||||
}
|
||||
catch (WebException ex)
|
||||
{
|
||||
throw new DownloadClientException("Unable to connect to Deluge, please check your settings", ex);
|
||||
throw new DownloadClientUnavailableException("Unable to connect to Deluge, please check your settings", ex);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -8,19 +8,16 @@ 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)
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
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)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -72,7 +72,20 @@ namespace NzbDrone.Core.Download.Clients.DownloadStation.Proxies
|
||||
DownloadStationSettings settings) where T : new()
|
||||
{
|
||||
var request = requestBuilder.Build();
|
||||
var response = _httpClient.Execute(request);
|
||||
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);
|
||||
}
|
||||
|
||||
_logger.Debug("Trying to {0}", operation);
|
||||
|
||||
|
||||
@@ -105,13 +105,13 @@ namespace NzbDrone.Core.Download.Clients.DownloadStation
|
||||
return items;
|
||||
}
|
||||
|
||||
public override DownloadClientStatus GetStatus()
|
||||
public override DownloadClientInfo GetStatus()
|
||||
{
|
||||
try
|
||||
{
|
||||
var path = GetDownloadDirectory();
|
||||
|
||||
return new DownloadClientStatus
|
||||
return new DownloadClientInfo
|
||||
{
|
||||
IsLocalhost = Settings.Host == "127.0.0.1" || Settings.Host == "localhost",
|
||||
OutputRootFolders = new List<OsPath> { _remotePathMappingService.RemapRemoteToLocal(Settings.Host, new OsPath(path)) }
|
||||
@@ -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);
|
||||
_logger.Error(ex, ex.Message);
|
||||
return new NzbDroneValidationFailure(string.Empty, ex.Message);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.Error(ex);
|
||||
_logger.Error(ex, "Error testing Torrent Download Station");
|
||||
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);
|
||||
_logger.Error(ex, "Unable to connect to Torrent Download Station");
|
||||
|
||||
if (ex.Status == WebExceptionStatus.ConnectFailure)
|
||||
{
|
||||
@@ -359,7 +359,7 @@ namespace NzbDrone.Core.Download.Clients.DownloadStation
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.Error(ex);
|
||||
_logger.Error(ex, "Error testing Torrent Download Station");
|
||||
return new NzbDroneValidationFailure(string.Empty, $"Unknown exception: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -130,13 +130,13 @@ namespace NzbDrone.Core.Download.Clients.DownloadStation
|
||||
return finalPath;
|
||||
}
|
||||
|
||||
public override DownloadClientStatus GetStatus()
|
||||
public override DownloadClientInfo GetStatus()
|
||||
{
|
||||
try
|
||||
{
|
||||
var path = GetDownloadDirectory();
|
||||
|
||||
return new DownloadClientStatus
|
||||
return new DownloadClientInfo
|
||||
{
|
||||
IsLocalhost = Settings.Host == "127.0.0.1" || Settings.Host == "localhost",
|
||||
OutputRootFolders = new List<OsPath> { _remotePathMappingService.RemapRemoteToLocal(Settings.Host, new OsPath(path)) }
|
||||
@@ -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);
|
||||
_logger.Error(ex, ex.Message);
|
||||
return new NzbDroneValidationFailure(string.Empty, ex.Message);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.Error(ex);
|
||||
_logger.Error(ex, "Error testing Usenet Download Station");
|
||||
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);
|
||||
_logger.Error(ex, "Unable to connect to Usenet Download Station");
|
||||
|
||||
if (ex.Status == WebExceptionStatus.ConnectFailure)
|
||||
{
|
||||
@@ -273,7 +273,7 @@ namespace NzbDrone.Core.Download.Clients.DownloadStation
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.Error(ex);
|
||||
_logger.Error(ex, "Error testing Torrent Download Station");
|
||||
return new NzbDroneValidationFailure(string.Empty, "Unknown exception: " + ex.Message);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,17 +35,7 @@ namespace NzbDrone.Core.Download.Clients.Hadouken
|
||||
|
||||
public override IEnumerable<DownloadClientItem> GetItems()
|
||||
{
|
||||
HadoukenTorrent[] torrents;
|
||||
|
||||
try
|
||||
{
|
||||
torrents = _proxy.GetTorrents(Settings);
|
||||
}
|
||||
catch (DownloadClientException ex)
|
||||
{
|
||||
_logger.ErrorException(ex.Message, ex);
|
||||
return Enumerable.Empty<DownloadClientItem>();
|
||||
}
|
||||
var torrents = _proxy.GetTorrents(Settings);
|
||||
|
||||
var items = new List<DownloadClientItem>();
|
||||
|
||||
@@ -117,12 +107,12 @@ namespace NzbDrone.Core.Download.Clients.Hadouken
|
||||
}
|
||||
}
|
||||
|
||||
public override DownloadClientStatus GetStatus()
|
||||
public override DownloadClientInfo GetStatus()
|
||||
{
|
||||
var config = _proxy.GetConfig(Settings);
|
||||
var destDir = new OsPath(config.GetValueOrDefault("bittorrent.defaultSavePath") as string);
|
||||
|
||||
var status = new DownloadClientStatus
|
||||
var status = new DownloadClientInfo
|
||||
{
|
||||
IsLocalhost = Settings.Host == "127.0.0.1" || Settings.Host == "localhost"
|
||||
};
|
||||
|
||||
@@ -77,7 +77,21 @@ namespace NzbDrone.Core.Download.Clients.Hadouken
|
||||
requestBuilder.Headers.Add("Accept-Encoding", "gzip,deflate");
|
||||
|
||||
var httpRequest = requestBuilder.Build();
|
||||
var response = _httpClient.Execute(httpRequest);
|
||||
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 result = Json.Deserialize<JsonRpcResponse<T>>(response.Content);
|
||||
|
||||
if (result.Error != null)
|
||||
|
||||
@@ -47,17 +47,7 @@ namespace NzbDrone.Core.Download.Clients.NzbVortex
|
||||
|
||||
public override IEnumerable<DownloadClientItem> GetItems()
|
||||
{
|
||||
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 vortexQueue = _proxy.GetQueue(30, Settings);
|
||||
|
||||
var queueItems = new List<DownloadClientItem>();
|
||||
|
||||
@@ -142,9 +132,9 @@ namespace NzbDrone.Core.Download.Clients.NzbVortex
|
||||
return _proxy.GetGroups(Settings);
|
||||
}
|
||||
|
||||
public override DownloadClientStatus GetStatus()
|
||||
public override DownloadClientInfo GetStatus()
|
||||
{
|
||||
var status = new DownloadClientStatus
|
||||
var status = new DownloadClientInfo
|
||||
{
|
||||
IsLocalhost = Settings.Host == "127.0.0.1" || Settings.Host == "localhost"
|
||||
};
|
||||
@@ -168,7 +158,7 @@ namespace NzbDrone.Core.Download.Clients.NzbVortex
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.Error(ex);
|
||||
_logger.Error(ex, "Unable to connect to NZBVortex");
|
||||
return new ValidationFailure("Host", "Unable to connect to NZBVortex");
|
||||
}
|
||||
|
||||
@@ -189,7 +179,7 @@ namespace NzbDrone.Core.Download.Clients.NzbVortex
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.Error(ex);
|
||||
_logger.Error(ex, "Unable to connect to NZBVortex");
|
||||
return new ValidationFailure("Host", "Unable to connect to NZBVortex");
|
||||
}
|
||||
|
||||
|
||||
@@ -164,7 +164,7 @@ namespace NzbDrone.Core.Download.Clients.NzbVortex
|
||||
}
|
||||
catch (WebException ex)
|
||||
{
|
||||
throw new DownloadClientException("Unable to connect to NZBVortex, please check your settings", ex);
|
||||
throw new DownloadClientUnavailableException("Unable to connect to NZBVortex, please check your settings", ex);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
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;
|
||||
@@ -9,8 +10,8 @@ using NzbDrone.Common.Extensions;
|
||||
using NzbDrone.Common.Http;
|
||||
using NzbDrone.Core.Configuration;
|
||||
using NzbDrone.Core.Parser.Model;
|
||||
using NzbDrone.Core.Validation;
|
||||
using NzbDrone.Core.RemotePathMappings;
|
||||
using NzbDrone.Core.Validation;
|
||||
|
||||
namespace NzbDrone.Core.Download.Clients.Nzbget
|
||||
{
|
||||
@@ -51,19 +52,8 @@ namespace NzbDrone.Core.Download.Clients.Nzbget
|
||||
|
||||
private IEnumerable<DownloadClientItem> GetQueue()
|
||||
{
|
||||
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 globalStatus = _proxy.GetGlobalStatus(Settings);
|
||||
var queue = _proxy.GetQueue(Settings);
|
||||
|
||||
var queueItems = new List<DownloadClientItem>();
|
||||
|
||||
@@ -119,17 +109,7 @@ namespace NzbDrone.Core.Download.Clients.Nzbget
|
||||
|
||||
private IEnumerable<DownloadClientItem> GetHistory()
|
||||
{
|
||||
List<NzbgetHistoryItem> history;
|
||||
|
||||
try
|
||||
{
|
||||
history = _proxy.GetHistory(Settings).Take(_configService.DownloadClientHistoryLimit).ToList();
|
||||
}
|
||||
catch (DownloadClientException ex)
|
||||
{
|
||||
_logger.Error(ex);
|
||||
return Enumerable.Empty<DownloadClientItem>();
|
||||
}
|
||||
var history = _proxy.GetHistory(Settings).Take(_configService.DownloadClientHistoryLimit).ToList();
|
||||
|
||||
var historyItems = new List<DownloadClientItem>();
|
||||
|
||||
@@ -214,13 +194,13 @@ namespace NzbDrone.Core.Download.Clients.Nzbget
|
||||
_proxy.RemoveItem(downloadId, Settings);
|
||||
}
|
||||
|
||||
public override DownloadClientStatus GetStatus()
|
||||
public override DownloadClientInfo GetStatus()
|
||||
{
|
||||
var config = _proxy.GetConfig(Settings);
|
||||
|
||||
var category = GetCategories(config).FirstOrDefault(v => v.Name == Settings.TvCategory);
|
||||
|
||||
var status = new DownloadClientStatus
|
||||
var status = new DownloadClientInfo
|
||||
{
|
||||
IsLocalhost = Settings.Host == "127.0.0.1" || Settings.Host == "localhost"
|
||||
};
|
||||
@@ -289,7 +269,7 @@ namespace NzbDrone.Core.Download.Clients.Nzbget
|
||||
{
|
||||
return new ValidationFailure("Username", "Authentication failed");
|
||||
}
|
||||
_logger.Error(ex);
|
||||
_logger.Error(ex, "Unable to connect to NZBGet");
|
||||
return new ValidationFailure("Host", "Unable to connect to NZBGet");
|
||||
}
|
||||
|
||||
@@ -317,8 +297,9 @@ namespace NzbDrone.Core.Download.Clients.Nzbget
|
||||
{
|
||||
var config = _proxy.GetConfig(Settings);
|
||||
|
||||
var keepHistory = config.GetValueOrDefault("KeepHistory");
|
||||
if (keepHistory == "0")
|
||||
var keepHistory = config.GetValueOrDefault("KeepHistory", "7");
|
||||
int value;
|
||||
if (!int.TryParse(keepHistory, NumberStyles.None, CultureInfo.InvariantCulture, out value) || value == 0)
|
||||
{
|
||||
return new NzbDroneValidationFailure(string.Empty, "NzbGet setting KeepHistory should be greater than 0")
|
||||
{
|
||||
@@ -326,6 +307,14 @@ 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;
|
||||
}
|
||||
|
||||
@@ -235,14 +235,14 @@ namespace NzbDrone.Core.Download.Clients.Nzbget
|
||||
{
|
||||
if (ex.Response.StatusCode == HttpStatusCode.Unauthorized)
|
||||
{
|
||||
throw new DownloadClientException("Authentication failed for NzbGet, please check your settings", ex);
|
||||
throw new DownloadClientAuthenticationException("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 DownloadClientException("Unable to connect to NzbGet. " + ex.Message, ex);
|
||||
throw new DownloadClientUnavailableException("Unable to connect to NzbGet. " + ex.Message, ex);
|
||||
}
|
||||
|
||||
var result = Json.Deserialize<JsonRpcResponse<T>>(response.Content);
|
||||
|
||||
@@ -103,9 +103,9 @@ namespace NzbDrone.Core.Download.Clients.Pneumatic
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
public override DownloadClientStatus GetStatus()
|
||||
public override DownloadClientInfo GetStatus()
|
||||
{
|
||||
var status = new DownloadClientStatus
|
||||
var status = new DownloadClientInfo
|
||||
{
|
||||
IsLocalhost = true
|
||||
};
|
||||
|
||||
@@ -89,19 +89,8 @@ namespace NzbDrone.Core.Download.Clients.QBittorrent
|
||||
|
||||
public override IEnumerable<DownloadClientItem> GetItems()
|
||||
{
|
||||
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 config = _proxy.GetConfig(Settings);
|
||||
var torrents = _proxy.GetTorrents(Settings);
|
||||
|
||||
var queueItems = new List<DownloadClientItem>();
|
||||
|
||||
@@ -174,13 +163,13 @@ namespace NzbDrone.Core.Download.Clients.QBittorrent
|
||||
_proxy.RemoveTorrent(hash.ToLower(), deleteData, Settings);
|
||||
}
|
||||
|
||||
public override DownloadClientStatus GetStatus()
|
||||
public override DownloadClientInfo GetStatus()
|
||||
{
|
||||
var config = _proxy.GetConfig(Settings);
|
||||
|
||||
var destDir = new OsPath(config.SavePath);
|
||||
|
||||
return new DownloadClientStatus
|
||||
return new DownloadClientInfo
|
||||
{
|
||||
IsLocalhost = Settings.Host == "127.0.0.1" || Settings.Host == "localhost",
|
||||
OutputRootFolders = new List<OsPath> { _remotePathMappingService.RemapRemoteToLocal(Settings.Host, destDir) }
|
||||
@@ -241,7 +230,7 @@ namespace NzbDrone.Core.Download.Clients.QBittorrent
|
||||
}
|
||||
catch (DownloadClientAuthenticationException ex)
|
||||
{
|
||||
_logger.Error(ex);
|
||||
_logger.Error(ex, ex.Message);
|
||||
return new NzbDroneValidationFailure("Username", "Authentication failure")
|
||||
{
|
||||
DetailedDescription = "Please verify your username and password."
|
||||
@@ -249,7 +238,7 @@ namespace NzbDrone.Core.Download.Clients.QBittorrent
|
||||
}
|
||||
catch (WebException ex)
|
||||
{
|
||||
_logger.Error(ex);
|
||||
_logger.Error(ex, "Unable to connect to qBittorrent");
|
||||
if (ex.Status == WebExceptionStatus.ConnectFailure)
|
||||
{
|
||||
return new NzbDroneValidationFailure("Host", "Unable to connect")
|
||||
@@ -261,7 +250,7 @@ namespace NzbDrone.Core.Download.Clients.QBittorrent
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.Error(ex);
|
||||
_logger.Error(ex, "Unable to test qBittorrent");
|
||||
return new NzbDroneValidationFailure(String.Empty, "Unknown exception: " + ex.Message);
|
||||
}
|
||||
|
||||
@@ -296,7 +285,7 @@ namespace NzbDrone.Core.Download.Clients.QBittorrent
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.Error(ex);
|
||||
_logger.Error(ex, "Failed to test qBittorrent");
|
||||
return new NzbDroneValidationFailure(String.Empty, "Unknown exception: " + ex.Message);
|
||||
}
|
||||
|
||||
@@ -311,7 +300,7 @@ namespace NzbDrone.Core.Download.Clients.QBittorrent
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.Error(ex);
|
||||
_logger.Error(ex, "Failed to get torrents");
|
||||
return new NzbDroneValidationFailure(String.Empty, "Failed to get the list of torrents: " + ex.Message);
|
||||
}
|
||||
|
||||
|
||||
@@ -156,6 +156,7 @@ namespace NzbDrone.Core.Download.Clients.QBittorrent
|
||||
AuthenticateClient(requestBuilder, settings);
|
||||
|
||||
var request = requestBuilder.Build();
|
||||
request.LogResponseContent = true;
|
||||
|
||||
HttpResponse response;
|
||||
try
|
||||
@@ -225,7 +226,7 @@ namespace NzbDrone.Core.Download.Clients.QBittorrent
|
||||
}
|
||||
catch (WebException ex)
|
||||
{
|
||||
throw new DownloadClientException("Failed to connect to qBitTorrent, please check your settings.", ex);
|
||||
throw new DownloadClientUnavailableException("Failed to connect to qBitTorrent, please check your settings.", ex);
|
||||
}
|
||||
|
||||
if (response.Content != "Ok.") // returns "Fails." on bad login
|
||||
|
||||
@@ -57,7 +57,7 @@ namespace NzbDrone.Core.Download.Clients.Sabnzbd
|
||||
}
|
||||
catch (DownloadClientException ex)
|
||||
{
|
||||
_logger.Warn("Couldn't get download queue. {0}", ex.Message);
|
||||
_logger.Warn(ex, "Couldn't get download queue. {0}", ex.Message);
|
||||
return Enumerable.Empty<DownloadClientItem>();
|
||||
}
|
||||
|
||||
@@ -112,17 +112,7 @@ namespace NzbDrone.Core.Download.Clients.Sabnzbd
|
||||
|
||||
private IEnumerable<DownloadClientItem> GetHistory()
|
||||
{
|
||||
SabnzbdHistory sabHistory;
|
||||
|
||||
try
|
||||
{
|
||||
sabHistory = _proxy.GetHistory(0, _configService.DownloadClientHistoryLimit, Settings.TvCategory, Settings);
|
||||
}
|
||||
catch (DownloadClientException ex)
|
||||
{
|
||||
_logger.Error(ex);
|
||||
return Enumerable.Empty<DownloadClientItem>();
|
||||
}
|
||||
var sabHistory = _proxy.GetHistory(0, _configService.DownloadClientHistoryLimit, Settings.TvCategory, Settings);
|
||||
|
||||
var historyItems = new List<DownloadClientItem>();
|
||||
|
||||
@@ -188,7 +178,7 @@ namespace NzbDrone.Core.Download.Clients.Sabnzbd
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
historyItems.Add(historyItem);
|
||||
}
|
||||
|
||||
@@ -250,7 +240,7 @@ namespace NzbDrone.Core.Download.Clients.Sabnzbd
|
||||
}
|
||||
}
|
||||
|
||||
public override DownloadClientStatus GetStatus()
|
||||
public override DownloadClientInfo GetStatus()
|
||||
{
|
||||
var config = _proxy.GetConfig(Settings);
|
||||
var categories = GetCategories(config).ToArray();
|
||||
@@ -262,7 +252,7 @@ namespace NzbDrone.Core.Download.Clients.Sabnzbd
|
||||
category = categories.FirstOrDefault(v => v.Name == "*");
|
||||
}
|
||||
|
||||
var status = new DownloadClientStatus
|
||||
var status = new DownloadClientInfo
|
||||
{
|
||||
IsLocalhost = Settings.Host == "127.0.0.1" || Settings.Host == "localhost"
|
||||
};
|
||||
@@ -388,7 +378,7 @@ namespace NzbDrone.Core.Download.Clients.Sabnzbd
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.Error(ex);
|
||||
_logger.Error(ex, ex.Message);
|
||||
return new ValidationFailure("Host", "Unable to connect to SABnzbd");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -183,7 +183,7 @@ namespace NzbDrone.Core.Download.Clients.Sabnzbd
|
||||
}
|
||||
catch (WebException ex)
|
||||
{
|
||||
throw new DownloadClientException("Unable to connect to SABnzbd, please check your settings", ex);
|
||||
throw new DownloadClientUnavailableException("Unable to connect to SABnzbd, please check your settings", ex);
|
||||
}
|
||||
|
||||
CheckForError(response);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
@@ -33,17 +33,7 @@ namespace NzbDrone.Core.Download.Clients.Transmission
|
||||
|
||||
public override IEnumerable<DownloadClientItem> GetItems()
|
||||
{
|
||||
List<TransmissionTorrent> torrents;
|
||||
|
||||
try
|
||||
{
|
||||
torrents = _proxy.GetTorrents(Settings);
|
||||
}
|
||||
catch (DownloadClientException ex)
|
||||
{
|
||||
_logger.Error(ex);
|
||||
return Enumerable.Empty<DownloadClientItem>();
|
||||
}
|
||||
var torrents = _proxy.GetTorrents(Settings);
|
||||
|
||||
var items = new List<DownloadClientItem>();
|
||||
|
||||
@@ -119,7 +109,7 @@ namespace NzbDrone.Core.Download.Clients.Transmission
|
||||
_proxy.RemoveTorrent(downloadId.ToLower(), deleteData, Settings);
|
||||
}
|
||||
|
||||
public override DownloadClientStatus GetStatus()
|
||||
public override DownloadClientInfo GetStatus()
|
||||
{
|
||||
var config = _proxy.GetConfig(Settings);
|
||||
var destDir = config.GetValueOrDefault("download-dir") as string;
|
||||
@@ -129,7 +119,7 @@ namespace NzbDrone.Core.Download.Clients.Transmission
|
||||
destDir = string.Format("{0}/.{1}", destDir, Settings.TvCategory);
|
||||
}
|
||||
|
||||
return new DownloadClientStatus
|
||||
return new DownloadClientInfo
|
||||
{
|
||||
IsLocalhost = Settings.Host == "127.0.0.1" || Settings.Host == "localhost",
|
||||
OutputRootFolders = new List<OsPath> { _remotePathMappingService.RemapRemoteToLocal(Settings.Host, new OsPath(destDir)) }
|
||||
@@ -205,27 +195,24 @@ namespace NzbDrone.Core.Download.Clients.Transmission
|
||||
}
|
||||
catch (DownloadClientAuthenticationException ex)
|
||||
{
|
||||
_logger.Error(ex);
|
||||
_logger.Error(ex, ex.Message);
|
||||
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 (WebException ex)
|
||||
catch (DownloadClientUnavailableException ex)
|
||||
{
|
||||
_logger.Error(ex);
|
||||
if (ex.Status == WebExceptionStatus.ConnectFailure)
|
||||
_logger.Error(ex, ex.Message);
|
||||
|
||||
return new NzbDroneValidationFailure("Host", "Unable to connect")
|
||||
{
|
||||
return new NzbDroneValidationFailure("Host", "Unable to connect")
|
||||
{
|
||||
DetailedDescription = "Please verify the hostname and port."
|
||||
};
|
||||
}
|
||||
return new NzbDroneValidationFailure(string.Empty, "Unknown exception: " + ex.Message);
|
||||
DetailedDescription = "Please verify the hostname and port."
|
||||
};
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.Error(ex);
|
||||
_logger.Error(ex, "Failed to test");
|
||||
return new NzbDroneValidationFailure(string.Empty, "Unknown exception: " + ex.Message);
|
||||
}
|
||||
}
|
||||
@@ -240,7 +227,7 @@ namespace NzbDrone.Core.Download.Clients.Transmission
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.Error(ex);
|
||||
_logger.Error(ex, "Failed to get torrents");
|
||||
return new NzbDroneValidationFailure(string.Empty, "Failed to get the list of torrents: " + ex.Message);
|
||||
}
|
||||
|
||||
|
||||
@@ -238,54 +238,66 @@ namespace NzbDrone.Core.Download.Clients.Transmission
|
||||
|
||||
public TransmissionResponse ProcessRequest(string action, object arguments, TransmissionSettings settings)
|
||||
{
|
||||
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)
|
||||
try
|
||||
{
|
||||
data.Add("arguments", arguments);
|
||||
}
|
||||
var requestBuilder = BuildRequest(settings);
|
||||
requestBuilder.Headers.ContentType = "application/json";
|
||||
requestBuilder.SuppressHttpError = true;
|
||||
|
||||
request.SetContent(data.ToJson());
|
||||
request.ContentSummary = string.Format("{0}(...)", action);
|
||||
AuthenticateClient(requestBuilder, settings);
|
||||
|
||||
var response = _httpClient.Execute(request);
|
||||
if (response.StatusCode == HttpStatusCode.Conflict)
|
||||
{
|
||||
AuthenticateClient(requestBuilder, settings, true);
|
||||
var request = requestBuilder.Post().Build();
|
||||
|
||||
request = requestBuilder.Post().Build();
|
||||
var data = new Dictionary<string, object>();
|
||||
data.Add("method", action);
|
||||
|
||||
if (arguments != null)
|
||||
{
|
||||
data.Add("arguments", arguments);
|
||||
}
|
||||
|
||||
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 response = _httpClient.Execute(request);
|
||||
|
||||
var transmissionResponse = Json.Deserialize<TransmissionResponse>(response.Content);
|
||||
if (response.StatusCode == HttpStatusCode.Conflict)
|
||||
{
|
||||
AuthenticateClient(requestBuilder, settings, true);
|
||||
|
||||
if (transmissionResponse == null)
|
||||
{
|
||||
throw new TransmissionException("Unexpected response");
|
||||
}
|
||||
else if (transmissionResponse.Result != "success")
|
||||
{
|
||||
throw new TransmissionException(transmissionResponse.Result);
|
||||
}
|
||||
request = requestBuilder.Post().Build();
|
||||
|
||||
return transmissionResponse;
|
||||
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;
|
||||
}
|
||||
catch (HttpException ex)
|
||||
{
|
||||
throw new DownloadClientException("Unable to connect to Transmission, please check your settings", ex);
|
||||
}
|
||||
catch (WebException ex)
|
||||
{
|
||||
throw new DownloadClientUnavailableException("Unable to connect to Transmission, please check your settings", ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -81,69 +81,60 @@ namespace NzbDrone.Core.Download.Clients.RTorrent
|
||||
|
||||
public override IEnumerable<DownloadClientItem> GetItems()
|
||||
{
|
||||
try
|
||||
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)
|
||||
{
|
||||
var torrents = _proxy.GetTorrents(Settings);
|
||||
// Don't concern ourselves with categories other than specified
|
||||
if (torrent.Category != Settings.TvCategory) continue;
|
||||
|
||||
_logger.Debug("Retrieved metadata of {0} torrents in client", torrents.Count);
|
||||
|
||||
var items = new List<DownloadClientItem>();
|
||||
foreach (RTorrentTorrent torrent in torrents)
|
||||
if (torrent.Path.StartsWith("."))
|
||||
{
|
||||
// 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);
|
||||
throw new DownloadClientException("Download paths paths must be absolute. Please specify variable \"directory\" in rTorrent.");
|
||||
}
|
||||
|
||||
return items;
|
||||
}
|
||||
catch (DownloadClientException ex)
|
||||
{
|
||||
_logger.Error(ex);
|
||||
return Enumerable.Empty<DownloadClientItem>();
|
||||
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;
|
||||
}
|
||||
|
||||
public override void RemoveItem(string downloadId, bool deleteData)
|
||||
@@ -156,11 +147,11 @@ namespace NzbDrone.Core.Download.Clients.RTorrent
|
||||
_proxy.RemoveTorrent(downloadId, Settings);
|
||||
}
|
||||
|
||||
public override DownloadClientStatus GetStatus()
|
||||
public override DownloadClientInfo GetStatus()
|
||||
{
|
||||
// XXX: This function's correctness has not been considered
|
||||
|
||||
var status = new DownloadClientStatus
|
||||
var status = new DownloadClientInfo
|
||||
{
|
||||
IsLocalhost = Settings.Host == "127.0.0.1" || Settings.Host == "localhost"
|
||||
};
|
||||
@@ -189,7 +180,7 @@ namespace NzbDrone.Core.Download.Clients.RTorrent
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.Error(ex);
|
||||
_logger.Error(ex, "Failed to test rTorrent");
|
||||
return new NzbDroneValidationFailure(string.Empty, "Unknown exception: " + ex.Message);
|
||||
}
|
||||
|
||||
@@ -204,7 +195,7 @@ namespace NzbDrone.Core.Download.Clients.RTorrent
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.Error(ex);
|
||||
_logger.Error(ex, "Failed to get torrents");
|
||||
return new NzbDroneValidationFailure(string.Empty, "Failed to get the list of torrents: " + ex.Message);
|
||||
}
|
||||
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
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;
|
||||
@@ -54,8 +56,7 @@ namespace NzbDrone.Core.Download.Clients.RTorrent
|
||||
_logger.Debug("Executing remote method: system.client_version");
|
||||
|
||||
var client = BuildClient(settings);
|
||||
|
||||
var version = client.GetVersion();
|
||||
var version = ExecuteRequest(() => client.GetVersion());
|
||||
|
||||
return version;
|
||||
}
|
||||
@@ -65,20 +66,22 @@ namespace NzbDrone.Core.Download.Clients.RTorrent
|
||||
_logger.Debug("Executing remote method: d.multicall2");
|
||||
|
||||
var client = BuildClient(settings);
|
||||
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 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 items = new List<RTorrentTorrent>();
|
||||
|
||||
foreach (object[] torrent in ret)
|
||||
{
|
||||
var labelDecoded = System.Web.HttpUtility.UrlDecode((string) torrent[3]);
|
||||
@@ -107,8 +110,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);
|
||||
@@ -120,8 +123,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);
|
||||
@@ -133,14 +136,39 @@ 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>();
|
||||
@@ -163,25 +191,6 @@ 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>();
|
||||
@@ -201,5 +210,21 @@ 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -72,42 +72,7 @@ namespace NzbDrone.Core.Download.Clients.UTorrent
|
||||
|
||||
public override IEnumerable<DownloadClientItem> GetItems()
|
||||
{
|
||||
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 torrents = GetTorrents();
|
||||
|
||||
var queueItems = new List<DownloadClientItem>();
|
||||
|
||||
@@ -173,12 +138,46 @@ 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);
|
||||
}
|
||||
|
||||
public override DownloadClientStatus GetStatus()
|
||||
public override DownloadClientInfo GetStatus()
|
||||
{
|
||||
var config = _proxy.GetConfig(Settings);
|
||||
|
||||
@@ -199,7 +198,7 @@ namespace NzbDrone.Core.Download.Clients.UTorrent
|
||||
}
|
||||
}
|
||||
|
||||
var status = new DownloadClientStatus
|
||||
var status = new DownloadClientInfo
|
||||
{
|
||||
IsLocalhost = Settings.Host == "127.0.0.1" || Settings.Host == "localhost"
|
||||
};
|
||||
@@ -232,7 +231,7 @@ namespace NzbDrone.Core.Download.Clients.UTorrent
|
||||
}
|
||||
catch (DownloadClientAuthenticationException ex)
|
||||
{
|
||||
_logger.Error(ex);
|
||||
_logger.Error(ex, ex.Message);
|
||||
return new NzbDroneValidationFailure("Username", "Authentication failure")
|
||||
{
|
||||
DetailedDescription = "Please verify your username and password."
|
||||
@@ -240,7 +239,7 @@ namespace NzbDrone.Core.Download.Clients.UTorrent
|
||||
}
|
||||
catch (WebException ex)
|
||||
{
|
||||
_logger.Error(ex);
|
||||
_logger.Error(ex, "Unable to connect to uTorrent");
|
||||
if (ex.Status == WebExceptionStatus.ConnectFailure)
|
||||
{
|
||||
return new NzbDroneValidationFailure("Host", "Unable to connect")
|
||||
@@ -252,7 +251,7 @@ namespace NzbDrone.Core.Download.Clients.UTorrent
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.Error(ex);
|
||||
_logger.Error(ex, "Failed to test uTorrent");
|
||||
return new NzbDroneValidationFailure(string.Empty, "Unknown exception: " + ex.Message);
|
||||
}
|
||||
|
||||
@@ -267,7 +266,7 @@ namespace NzbDrone.Core.Download.Clients.UTorrent
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.Error(ex);
|
||||
_logger.Error(ex, "Failed to get torrents");
|
||||
return new NzbDroneValidationFailure(string.Empty, "Failed to get the list of torrents: " + ex.Message);
|
||||
}
|
||||
|
||||
|
||||
@@ -244,7 +244,7 @@ namespace NzbDrone.Core.Download.Clients.UTorrent
|
||||
}
|
||||
catch (WebException ex)
|
||||
{
|
||||
throw new DownloadClientException("Unable to connect to uTorrent, please check your settings", ex);
|
||||
throw new DownloadClientUnavailableException("Unable to connect to uTorrent, please check your settings", ex);
|
||||
}
|
||||
|
||||
cookies = response.GetCookies();
|
||||
|
||||
@@ -60,7 +60,7 @@ namespace NzbDrone.Core.Download
|
||||
public abstract string Download(RemoteEpisode remoteEpisode);
|
||||
public abstract IEnumerable<DownloadClientItem> GetItems();
|
||||
public abstract void RemoveItem(string downloadId, bool deleteData);
|
||||
public abstract DownloadClientStatus GetStatus();
|
||||
public abstract DownloadClientInfo GetStatus();
|
||||
|
||||
protected virtual void DeleteItemData(string downloadId)
|
||||
{
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
using System.Collections.Generic;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using FluentValidation.Results;
|
||||
using NLog;
|
||||
using NzbDrone.Common.Composition;
|
||||
using NzbDrone.Core.Messaging.Events;
|
||||
@@ -9,17 +11,24 @@ 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 IDownloadClientRepository _providerRepository;
|
||||
private readonly IDownloadClientStatusService _downloadClientStatusService;
|
||||
private readonly Logger _logger;
|
||||
|
||||
public DownloadClientFactory(IDownloadClientRepository providerRepository, IEnumerable<IDownloadClient> providers, IContainer container, IEventAggregator eventAggregator, Logger logger)
|
||||
public DownloadClientFactory(IDownloadClientStatusService downloadClientStatusService,
|
||||
IDownloadClientRepository providerRepository,
|
||||
IEnumerable<IDownloadClient> providers,
|
||||
IContainer container,
|
||||
IEventAggregator eventAggregator,
|
||||
Logger logger)
|
||||
: base(providerRepository, providers, container, eventAggregator, logger)
|
||||
{
|
||||
_providerRepository = providerRepository;
|
||||
_downloadClientStatusService = downloadClientStatusService;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
protected override List<DownloadClientDefinition> Active()
|
||||
@@ -33,5 +42,46 @@ 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
11
src/NzbDrone.Core/Download/DownloadClientInfo.cs
Normal file
11
src/NzbDrone.Core/Download/DownloadClientInfo.cs
Normal file
@@ -0,0 +1,11 @@
|
||||
using System.Collections.Generic;
|
||||
using NzbDrone.Common.Disk;
|
||||
|
||||
namespace NzbDrone.Core.Download
|
||||
{
|
||||
public class DownloadClientInfo
|
||||
{
|
||||
public bool IsLocalhost { get; set; }
|
||||
public List<OsPath> OutputRootFolders { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -27,19 +27,12 @@ namespace NzbDrone.Core.Download
|
||||
|
||||
public IEnumerable<IDownloadClient> GetDownloadClients()
|
||||
{
|
||||
return _downloadClientFactory.GetAvailableProviders();//.Select(MapDownloadClient);
|
||||
return _downloadClientFactory.GetAvailableProviders();
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +1,9 @@
|
||||
using System.Collections.Generic;
|
||||
using NzbDrone.Common.Disk;
|
||||
using NzbDrone.Core.ThingiProvider.Status;
|
||||
|
||||
namespace NzbDrone.Core.Download
|
||||
{
|
||||
public class DownloadClientStatus
|
||||
public class DownloadClientStatus : ProviderStatusBase
|
||||
{
|
||||
public bool IsLocalhost { get; set; }
|
||||
public List<OsPath> OutputRootFolders { get; set; }
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
19
src/NzbDrone.Core/Download/DownloadClientStatusRepository.cs
Normal file
19
src/NzbDrone.Core/Download/DownloadClientStatusRepository.cs
Normal file
@@ -0,0 +1,19 @@
|
||||
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)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
22
src/NzbDrone.Core/Download/DownloadClientStatusService.cs
Normal file
22
src/NzbDrone.Core/Download/DownloadClientStatusService.cs
Normal file
@@ -0,0 +1,22 @@
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -21,18 +21,21 @@ 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,
|
||||
IIndexerStatusService indexerStatusService,
|
||||
IRateLimitService rateLimitService,
|
||||
IEventAggregator eventAggregator,
|
||||
Logger logger)
|
||||
IDownloadClientStatusService downloadClientStatusService,
|
||||
IIndexerStatusService indexerStatusService,
|
||||
IRateLimitService rateLimitService,
|
||||
IEventAggregator eventAggregator,
|
||||
Logger logger)
|
||||
{
|
||||
_downloadClientProvider = downloadClientProvider;
|
||||
_downloadClientStatusService = downloadClientStatusService;
|
||||
_indexerStatusService = indexerStatusService;
|
||||
_rateLimitService = rateLimitService;
|
||||
_eventAggregator = eventAggregator;
|
||||
@@ -64,6 +67,7 @@ namespace NzbDrone.Core.Download
|
||||
try
|
||||
{
|
||||
downloadClientId = downloadClient.Download(remoteEpisode);
|
||||
_downloadClientStatusService.RecordSuccess(downloadClient.Definition.Id);
|
||||
_indexerStatusService.RecordSuccess(remoteEpisode.Release.IndexerId);
|
||||
}
|
||||
catch (ReleaseDownloadException ex)
|
||||
@@ -92,4 +96,4 @@ namespace NzbDrone.Core.Download
|
||||
_eventAggregator.PublishEvent(episodeGrabbedEvent);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,6 +12,6 @@ namespace NzbDrone.Core.Download
|
||||
string Download(RemoteEpisode remoteEpisode);
|
||||
IEnumerable<DownloadClientItem> GetItems();
|
||||
void RemoveItem(string downloadId, bool deleteData);
|
||||
DownloadClientStatus GetStatus();
|
||||
DownloadClientInfo GetStatus();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ 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; }
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user