{translate('MassSearchCancelWarning')}
diff --git a/frontend/src/Wanted/Missing/Missing.js b/frontend/src/Wanted/Missing/Missing.js
index 4185e54e3..70054a73b 100644
--- a/frontend/src/Wanted/Missing/Missing.js
+++ b/frontend/src/Wanted/Missing/Missing.js
@@ -260,11 +260,11 @@ class Missing extends Component {
- {translate('SearchForAllMissingConfirmationCount', { totalRecords })}
+ {translate('SearchForAllMissingEpisodesConfirmationCount', { totalRecords })}
{translate('MassSearchCancelWarning')}
diff --git a/frontend/src/typings/History.ts b/frontend/src/typings/History.ts
new file mode 100644
index 000000000..99fabe275
--- /dev/null
+++ b/frontend/src/typings/History.ts
@@ -0,0 +1,28 @@
+import Language from 'Language/Language';
+import { QualityModel } from 'Quality/Quality';
+import CustomFormat from './CustomFormat';
+
+export type HistoryEventType =
+ | 'grabbed'
+ | 'seriesFolderImported'
+ | 'downloadFolderImported'
+ | 'downloadFailed'
+ | 'episodeFileDeleted'
+ | 'episodeFileRenamed'
+ | 'downloadIgnored';
+
+export default interface History {
+ episodeId: number;
+ seriesId: number;
+ sourceTitle: string;
+ languages: Language[];
+ quality: QualityModel;
+ customFormats: CustomFormat[];
+ customFormatScore: number;
+ qualityCutoffNotMet: boolean;
+ date: string;
+ downloadId: string;
+ eventType: HistoryEventType;
+ data: unknown;
+ id: number;
+}
diff --git a/frontend/src/typings/Queue.ts b/frontend/src/typings/Queue.ts
new file mode 100644
index 000000000..855173306
--- /dev/null
+++ b/frontend/src/typings/Queue.ts
@@ -0,0 +1,46 @@
+import ModelBase from 'App/ModelBase';
+import Language from 'Language/Language';
+import { QualityModel } from 'Quality/Quality';
+import CustomFormat from 'typings/CustomFormat';
+
+export type QueueTrackedDownloadStatus = 'ok' | 'warning' | 'error';
+
+export type QueueTrackedDownloadState =
+ | 'downloading'
+ | 'importPending'
+ | 'importing'
+ | 'imported'
+ | 'failedPending'
+ | 'failed'
+ | 'ignored';
+
+export interface StatusMessage {
+ title: string;
+ messages: string[];
+}
+
+interface Queue extends ModelBase {
+ languages: Language[];
+ quality: QualityModel;
+ customFormats: CustomFormat[];
+ size: number;
+ title: string;
+ sizeleft: number;
+ timeleft: string;
+ estimatedCompletionTime: string;
+ status: string;
+ trackedDownloadStatus: QueueTrackedDownloadStatus;
+ trackedDownloadState: QueueTrackedDownloadState;
+ statusMessages: StatusMessage[];
+ errorMessage: string;
+ downloadId: string;
+ protocol: string;
+ downloadClient: string;
+ outputPath: string;
+ episodeHasFile: boolean;
+ seriesId?: number;
+ episodeId?: number;
+ seasonNumber?: number;
+}
+
+export default Queue;
diff --git a/package.json b/package.json
index c09c4e1b7..a3ad4b517 100644
--- a/package.json
+++ b/package.json
@@ -94,6 +94,7 @@
"@babel/preset-typescript": "7.22.11",
"@types/classnames": "2.3.1",
"@types/lodash": "4.14.194",
+ "@types/react-lazyload": "3.2.0",
"@types/react-router-dom": "5.3.3",
"@types/react-text-truncate": "0.14.1",
"@types/react-window": "1.8.5",
diff --git a/src/NzbDrone.Api.Test/ClientSchemaTests/SchemaBuilderFixture.cs b/src/NzbDrone.Api.Test/ClientSchemaTests/SchemaBuilderFixture.cs
index d3c036327..6af8abb7b 100644
--- a/src/NzbDrone.Api.Test/ClientSchemaTests/SchemaBuilderFixture.cs
+++ b/src/NzbDrone.Api.Test/ClientSchemaTests/SchemaBuilderFixture.cs
@@ -1,6 +1,9 @@
+using System.Collections.Generic;
using FluentAssertions;
+using Moq;
using NUnit.Framework;
using NzbDrone.Core.Annotations;
+using NzbDrone.Core.Localization;
using NzbDrone.Test.Common;
using Sonarr.Http.ClientSchema;
@@ -9,6 +12,16 @@ namespace NzbDrone.Api.Test.ClientSchemaTests
[TestFixture]
public class SchemaBuilderFixture : TestBase
{
+ [SetUp]
+ public void Setup()
+ {
+ Mocker.GetMock()
+ .Setup(s => s.GetLocalizedString(It.IsAny(), It.IsAny>()))
+ .Returns>((s, d) => s);
+
+ SchemaBuilder.Initialize(Mocker.Container);
+ }
+
[Test]
public void should_return_field_for_every_property()
{
diff --git a/src/NzbDrone.Common.Test/InstrumentationTests/CleanseLogMessageFixture.cs b/src/NzbDrone.Common.Test/InstrumentationTests/CleanseLogMessageFixture.cs
index 388a96635..6b1ea4171 100644
--- a/src/NzbDrone.Common.Test/InstrumentationTests/CleanseLogMessageFixture.cs
+++ b/src/NzbDrone.Common.Test/InstrumentationTests/CleanseLogMessageFixture.cs
@@ -71,15 +71,15 @@ namespace NzbDrone.Common.Test.InstrumentationTests
[TestCase(@"[Info] MigrationController: *** Migrating Database=sonarr-main;Host=postgres14;Username=mySecret;Password=mySecret;Port=5432;token=mySecret;Enlist=False&username=mySecret;mypassword=mySecret;mypass=shouldkeep1;test_token=mySecret;password=123%@%_@!#^#@;use_password=mySecret;get_token=shouldkeep2;usetoken=shouldkeep3;passwrd=mySecret;")]
// Announce URLs (passkeys) Magnet & Tracker
- [TestCase(@"magnet_uri"":""magnet:?xt=urn:btih:9pr04sgkillroyimaveql2tyu8xyui&dn=&tr=https%3a%2f%2fxxx.yyy%2f9pr04sg601233210imaveql2tyu8xyui%2fannounce""}")]
- [TestCase(@"magnet_uri"":""magnet:?xt=urn:btih:9pr04sgkillroyimaveql2tyu8xyui&dn=&tr=https%3a%2f%2fxxx.yyy%2ftracker.php%2f9pr04sg601233210imaveql2tyu8xyui%2fannounce""}")]
- [TestCase(@"magnet_uri"":""magnet:?xt=urn:btih:9pr04sgkillroyimaveql2tyu8xyui&dn=&tr=https%3a%2f%2fxxx.yyy%2fannounce%2f9pr04sg601233210imaveql2tyu8xyui""}")]
- [TestCase(@"magnet_uri"":""magnet:?xt=urn:btih:9pr04sgkillroyimaveql2tyu8xyui&dn=&tr=https%3a%2f%2fxxx.yyy%2fannounce.php%3fpasskey%3d9pr04sg601233210imaveql2tyu8xyui""}")]
- [TestCase(@"tracker"":""https://xxx.yyy/9pr04sg601233210imaveql2tyu8xyui/announce""}")]
- [TestCase(@"tracker"":""https://xxx.yyy/tracker.php/9pr04sg601233210imaveql2tyu8xyui/announce""}")]
- [TestCase(@"tracker"":""https://xxx.yyy/announce/9pr04sg601233210imaveql2tyu8xyui""}")]
- [TestCase(@"tracker"":""https://xxx.yyy/announce.php?passkey=9pr04sg601233210imaveql2tyu8xyui""}")]
- [TestCase(@"tracker"":""http://xxx.yyy/announce.php?passkey=9pr04sg601233210imaveql2tyu8xyui"",""info"":""http://xxx.yyy/info?a=b""")]
+ [TestCase(@"magnet_uri"":""magnet:?xt=urn:btih:9pr04sgkillroyimaveql2tyu8xyui&dn=&tr=https%3a%2f%2fxxx.yyy%2f9pr04sg601233210IMAveQL2tyu8xyui%2fannounce""}")]
+ [TestCase(@"magnet_uri"":""magnet:?xt=urn:btih:9pr04sgkillroyimaveql2tyu8xyui&dn=&tr=https%3a%2f%2fxxx.yyy%2ftracker.php%2f9pr04sg601233210IMAveQL2tyu8xyui%2fannounce""}")]
+ [TestCase(@"magnet_uri"":""magnet:?xt=urn:btih:9pr04sgkillroyimaveql2tyu8xyui&dn=&tr=https%3a%2f%2fxxx.yyy%2fannounce%2f9pr04sg601233210IMAveQL2tyu8xyui""}")]
+ [TestCase(@"magnet_uri"":""magnet:?xt=urn:btih:9pr04sgkillroyimaveql2tyu8xyui&dn=&tr=https%3a%2f%2fxxx.yyy%2fannounce.php%3fpasskey%3d9pr04sg601233210IMAveQL2tyu8xyui""}")]
+ [TestCase(@"tracker"":""https://xxx.yyy/9pr04sg601233210IMAveQL2tyu8xyui/announce""}")]
+ [TestCase(@"tracker"":""https://xxx.yyy/tracker.php/9pr04sg601233210IMAveQL2tyu8xyui/announce""}")]
+ [TestCase(@"tracker"":""https://xxx.yyy/announce/9pr04sg601233210IMAveQL2tyu8xyui""}")]
+ [TestCase(@"tracker"":""https://xxx.yyy/announce.php?passkey=9pr04sg601233210IMAveQL2tyu8xyui""}")]
+ [TestCase(@"tracker"":""http://xxx.yyy/announce.php?passkey=9pr04sg601233210IMAveQL2tyu8xyui"",""info"":""http://xxx.yyy/info?a=b""")]
// Webhooks - Notifiarr
[TestCase(@"https://xxx.yyy/api/v1/notification/sonarr/9pr04sg6-0123-3210-imav-eql2tyu8xyui")]
diff --git a/src/NzbDrone.Common/Extensions/PathExtensions.cs b/src/NzbDrone.Common/Extensions/PathExtensions.cs
index 896eb3d91..280083e4c 100644
--- a/src/NzbDrone.Common/Extensions/PathExtensions.cs
+++ b/src/NzbDrone.Common/Extensions/PathExtensions.cs
@@ -171,7 +171,7 @@ namespace NzbDrone.Common.Extensions
{
if (text.IsNullOrWhiteSpace())
{
- throw new ArgumentNullException("text");
+ throw new ArgumentNullException(nameof(text));
}
return text.IndexOfAny(Path.GetInvalidPathChars()) >= 0;
diff --git a/src/NzbDrone.Common/Http/Dispatchers/ManagedHttpDispatcher.cs b/src/NzbDrone.Common/Http/Dispatchers/ManagedHttpDispatcher.cs
index a9449efe7..5281d74fe 100644
--- a/src/NzbDrone.Common/Http/Dispatchers/ManagedHttpDispatcher.cs
+++ b/src/NzbDrone.Common/Http/Dispatchers/ManagedHttpDispatcher.cs
@@ -114,7 +114,7 @@ namespace NzbDrone.Common.Http.Dispatchers
}
else
{
- data = responseMessage.Content.ReadAsByteArrayAsync(cts.Token).GetAwaiter().GetResult();
+ data = await responseMessage.Content.ReadAsByteArrayAsync(cts.Token);
}
}
catch (Exception ex)
diff --git a/src/NzbDrone.Common/Instrumentation/CleanseLogMessage.cs b/src/NzbDrone.Common/Instrumentation/CleanseLogMessage.cs
index d0150a7bc..12d027afa 100644
--- a/src/NzbDrone.Common/Instrumentation/CleanseLogMessage.cs
+++ b/src/NzbDrone.Common/Instrumentation/CleanseLogMessage.cs
@@ -20,7 +20,7 @@ namespace NzbDrone.Common.Instrumentation
new (@"\b(\w*)?(_?(?[^&=]+?)(?= |&|$|;)", RegexOptions.Compiled | RegexOptions.IgnoreCase),
// Trackers Announce Keys; Designed for Qbit Json; should work for all in theory
- new (@"announce(\.php)?(/|%2f|%3fpasskey%3d)(?[a-z0-9]{16,})|(?[a-z0-9]{16,})(/|%2f)announce"),
+ new (@"announce(\.php)?(/|%2f|%3fpasskey%3d)(?[a-z0-9]{16,})|(?[a-z0-9]{16,})(/|%2f)announce", RegexOptions.Compiled | RegexOptions.IgnoreCase),
// Path
new (@"C:\\Users\\(?[^\""]+?)(\\|$)", RegexOptions.Compiled | RegexOptions.IgnoreCase),
diff --git a/src/NzbDrone.Core.Test/DecisionEngineTests/RawDiskSpecificationFixture.cs b/src/NzbDrone.Core.Test/DecisionEngineTests/RawDiskSpecificationFixture.cs
index a07f6dca9..9831d1fea 100644
--- a/src/NzbDrone.Core.Test/DecisionEngineTests/RawDiskSpecificationFixture.cs
+++ b/src/NzbDrone.Core.Test/DecisionEngineTests/RawDiskSpecificationFixture.cs
@@ -1,4 +1,4 @@
-using FluentAssertions;
+using FluentAssertions;
using NUnit.Framework;
using NzbDrone.Core.DecisionEngine.Specifications;
using NzbDrone.Core.Indexers;
@@ -72,15 +72,27 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
Subject.IsSatisfiedBy(_remoteEpisode, null).Accepted.Should().BeFalse();
}
- [TestCase("How the Earth Was Made S02 Disc 1 1080i Blu-ray DTS-HD MA 2.0 AVC-TrollHD")]
- [TestCase("The Universe S03 Disc 1 1080p Blu-ray LPCM 2.0 AVC-TrollHD")]
- [TestCase("HELL ON WHEELS S02 1080P FULL BLURAY AVC DTS-HD MA 5 1")]
- [TestCase("Game.of.Thrones.S06.2016.DISC.3.BluRay.1080p.AVC.Atmos.TrueHD7.1-MTeam")]
- [TestCase("Game of Thrones S05 Disc 1 BluRay 1080p AVC Atmos TrueHD 7 1-MTeam")]
+ [TestCase("Series Title S02 Disc 1 1080i Blu-ray DTS-HD MA 2.0 AVC-TrollHD")]
+ [TestCase("Series Title S03 Disc 1 1080p Blu-ray LPCM 2.0 AVC-TrollHD")]
+ [TestCase("SERIES TITLE S02 1080P FULL BLURAY AVC DTS-HD MA 5 1")]
+ [TestCase("Series.Title.S06.2016.DISC.3.BluRay.1080p.AVC.Atmos.TrueHD7.1-MTeam")]
+ [TestCase("Series Title S05 Disc 1 BluRay 1080p AVC Atmos TrueHD 7 1-MTeam")]
+ [TestCase("Series Title S05 Disc 1 BluRay 1080p AVC Atmos TrueHD 7 1-MTeam")]
+ [TestCase("Someone.the.Entertainer.Presents.S01.NTSC.3xDVD9.MPEG-2.DD2.0")]
+ [TestCase("Series.Title.S00.The.Christmas.Special.2011.PAL.DVD5.DD2.0")]
+ [TestCase("Series.of.Desire.2000.S1_D01.NTSC.DVD5")]
public void should_return_false_if_matches_disc_format(string title)
{
_remoteEpisode.Release.Title = title;
Subject.IsSatisfiedBy(_remoteEpisode, null).Accepted.Should().BeFalse();
}
+
+ [TestCase("Series Title EP50 USLT NTSC DVDRemux DD2.0")]
+ [TestCase("Series.Title.S01.NTSC.DVDRip.DD2.0.x264-PLAiD")]
+ public void should_return_true_if_dvdrip(string title)
+ {
+ _remoteEpisode.Release.Title = title;
+ Subject.IsSatisfiedBy(_remoteEpisode, null).Accepted.Should().BeTrue();
+ }
}
}
diff --git a/src/NzbDrone.Core.Test/Download/DownloadClientTests/DownloadClientFixtureBase.cs b/src/NzbDrone.Core.Test/Download/DownloadClientTests/DownloadClientFixtureBase.cs
index 32e4a6335..804099e4a 100644
--- a/src/NzbDrone.Core.Test/Download/DownloadClientTests/DownloadClientFixtureBase.cs
+++ b/src/NzbDrone.Core.Test/Download/DownloadClientTests/DownloadClientFixtureBase.cs
@@ -11,6 +11,7 @@ using NzbDrone.Core.Configuration;
using NzbDrone.Core.Download;
using NzbDrone.Core.Indexers;
using NzbDrone.Core.IndexerSearch.Definitions;
+using NzbDrone.Core.Localization;
using NzbDrone.Core.Parser;
using NzbDrone.Core.Parser.Model;
using NzbDrone.Core.RemotePathMappings;
@@ -70,7 +71,8 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests
Mocker.Resolve(),
Mocker.Resolve(),
Mocker.Resolve(),
- Mocker.Resolve());
+ Mocker.Resolve(),
+ Mocker.Resolve());
}
protected void VerifyIdentifiable(DownloadClientItem downloadClientItem)
diff --git a/src/NzbDrone.Core.Test/Download/DownloadClientTests/HadoukenTests/HadoukenFixture.cs b/src/NzbDrone.Core.Test/Download/DownloadClientTests/HadoukenTests/HadoukenFixture.cs
index 9cc35560f..bf609e48c 100644
--- a/src/NzbDrone.Core.Test/Download/DownloadClientTests/HadoukenTests/HadoukenFixture.cs
+++ b/src/NzbDrone.Core.Test/Download/DownloadClientTests/HadoukenTests/HadoukenFixture.cs
@@ -320,7 +320,7 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.HadoukenTests
var result = Subject.Test();
- result.Errors.First().ErrorMessage.Should().Be("Old Hadouken client with unsupported API, need 5.1 or higher");
+ result.Errors.Count.Should().Be(1);
}
}
}
diff --git a/src/NzbDrone.Core.Test/Download/DownloadClientTests/PneumaticProviderFixture.cs b/src/NzbDrone.Core.Test/Download/DownloadClientTests/PneumaticProviderFixture.cs
index d2d206bd6..c11b1317a 100644
--- a/src/NzbDrone.Core.Test/Download/DownloadClientTests/PneumaticProviderFixture.cs
+++ b/src/NzbDrone.Core.Test/Download/DownloadClientTests/PneumaticProviderFixture.cs
@@ -11,6 +11,7 @@ using NzbDrone.Core.Configuration;
using NzbDrone.Core.Download;
using NzbDrone.Core.Download.Clients.Pneumatic;
using NzbDrone.Core.Indexers;
+using NzbDrone.Core.Localization;
using NzbDrone.Core.Parser;
using NzbDrone.Core.Parser.Model;
using NzbDrone.Core.Test.Framework;
@@ -51,7 +52,8 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests
Mocker.Resolve(),
Mocker.Resolve(),
Mocker.Resolve(),
- Mocker.Resolve());
+ Mocker.Resolve(),
+ Mocker.Resolve());
_downloadClientItem = Builder
.CreateNew().With(d => d.DownloadId = "_Droned.S01E01.Pilot.1080p.WEB-DL-DRONE_0")
diff --git a/src/NzbDrone.Core.Test/Download/DownloadClientTests/QBittorrentTests/QBittorrentFixture.cs b/src/NzbDrone.Core.Test/Download/DownloadClientTests/QBittorrentTests/QBittorrentFixture.cs
index 559423f2f..3ae0edd84 100644
--- a/src/NzbDrone.Core.Test/Download/DownloadClientTests/QBittorrentTests/QBittorrentFixture.cs
+++ b/src/NzbDrone.Core.Test/Download/DownloadClientTests/QBittorrentTests/QBittorrentFixture.cs
@@ -426,7 +426,7 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.QBittorrentTests
Size = 1000,
Progress = 0.7,
Eta = 8640000,
- State = "stalledDL",
+ State = "pausedUP",
Label = "",
SavePath = @"C:\Torrents".AsOsAgnostic(),
ContentPath = @"C:\Torrents\Droned.S01.12".AsOsAgnostic()
diff --git a/src/NzbDrone.Core.Test/Download/DownloadClientTests/SabnzbdTests/SabnzbdFixture.cs b/src/NzbDrone.Core.Test/Download/DownloadClientTests/SabnzbdTests/SabnzbdFixture.cs
index 2c955bd39..b81633b51 100644
--- a/src/NzbDrone.Core.Test/Download/DownloadClientTests/SabnzbdTests/SabnzbdFixture.cs
+++ b/src/NzbDrone.Core.Test/Download/DownloadClientTests/SabnzbdTests/SabnzbdFixture.cs
@@ -452,6 +452,30 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.SabnzbdTests
result.OutputRootFolders.First().Should().Be(fullCategoryDir);
}
+ [TestCase("0")]
+ [TestCase("15d")]
+ public void should_set_history_removes_completed_downloads_false(string historyRetention)
+ {
+ _config.Misc.history_retention = historyRetention;
+
+ var downloadClientInfo = Subject.GetStatus();
+
+ downloadClientInfo.RemovesCompletedDownloads.Should().BeFalse();
+ }
+
+ [TestCase("-1")]
+ [TestCase("15")]
+ [TestCase("3")]
+ [TestCase("3d")]
+ public void should_set_history_removes_completed_downloads_true(string historyRetention)
+ {
+ _config.Misc.history_retention = historyRetention;
+
+ var downloadClientInfo = Subject.GetStatus();
+
+ downloadClientInfo.RemovesCompletedDownloads.Should().BeTrue();
+ }
+
[TestCase(@"Y:\nzbget\root", @"completed\downloads", @"vv", @"Y:\nzbget\root\completed\downloads", @"Y:\nzbget\root\completed\downloads\vv")]
[TestCase(@"Y:\nzbget\root", @"completed", @"vv", @"Y:\nzbget\root\completed", @"Y:\nzbget\root\completed\vv")]
[TestCase(@"/nzbget/root", @"completed/downloads", @"vv", @"/nzbget/root/completed/downloads", @"/nzbget/root/completed/downloads/vv")]
@@ -519,6 +543,52 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.SabnzbdTests
result.HasWarnings.Should().BeTrue();
}
+ [Test]
+ public void should_test_success_if_sorters_are_empty()
+ {
+ _config.Misc.enable_tv_sorting = false;
+ _config.Misc.tv_categories = null;
+ _config.Sorters = new List();
+
+ var result = new NzbDroneValidationResult(Subject.Test());
+
+ result.IsValid.Should().BeTrue();
+ }
+
+ [Test]
+ public void should_test_failed_if_sorter_is_enabled_for_non_tv_category()
+ {
+ _config.Misc.enable_tv_sorting = false;
+ _config.Misc.tv_categories = null;
+ _config.Sorters = Builder.CreateListOfSize(1)
+ .All()
+ .With(s => s.is_active = true)
+ .With(s => s.sort_cats = new List { "tv-custom" })
+ .Build()
+ .ToList();
+
+ var result = new NzbDroneValidationResult(Subject.Test());
+
+ result.IsValid.Should().BeTrue();
+ }
+
+ [Test]
+ public void should_test_failed_if_sorter_is_enabled_for_tv_category()
+ {
+ _config.Misc.enable_tv_sorting = false;
+ _config.Misc.tv_categories = null;
+ _config.Sorters = Builder.CreateListOfSize(1)
+ .All()
+ .With(s => s.is_active = true)
+ .With(s => s.sort_cats = new List { "tv" })
+ .Build()
+ .ToList();
+
+ var result = new NzbDroneValidationResult(Subject.Test());
+
+ result.IsValid.Should().BeFalse();
+ }
+
[Test]
public void should_test_success_if_tv_sorting_disabled()
{
diff --git a/src/NzbDrone.Core.Test/HealthCheck/Checks/DownloadClientRemovesCompletedDownloadsCheckFixture.cs b/src/NzbDrone.Core.Test/HealthCheck/Checks/DownloadClientRemovesCompletedDownloadsCheckFixture.cs
new file mode 100644
index 000000000..abaad836f
--- /dev/null
+++ b/src/NzbDrone.Core.Test/HealthCheck/Checks/DownloadClientRemovesCompletedDownloadsCheckFixture.cs
@@ -0,0 +1,77 @@
+using System;
+using Moq;
+using NUnit.Framework;
+using NzbDrone.Core.Download;
+using NzbDrone.Core.Download.Clients;
+using NzbDrone.Core.HealthCheck.Checks;
+using NzbDrone.Core.Localization;
+using NzbDrone.Core.Test.Framework;
+using NzbDrone.Test.Common;
+
+namespace NzbDrone.Core.Test.HealthCheck.Checks
+{
+ [TestFixture]
+ public class DownloadClientRemovesCompletedDownloadsCheckFixture : CoreTest
+ {
+ private DownloadClientInfo _clientStatus;
+ private Mock _downloadClient;
+
+ private static Exception[] DownloadClientExceptions =
+ {
+ new DownloadClientUnavailableException("error"),
+ new DownloadClientAuthenticationException("error"),
+ new DownloadClientException("error")
+ };
+
+ [SetUp]
+ public void Setup()
+ {
+ _clientStatus = new DownloadClientInfo
+ {
+ IsLocalhost = true,
+ SortingMode = null,
+ RemovesCompletedDownloads = true
+ };
+
+ _downloadClient = Mocker.GetMock();
+ _downloadClient.Setup(s => s.Definition)
+ .Returns(new DownloadClientDefinition { Name = "Test" });
+
+ _downloadClient.Setup(s => s.GetStatus())
+ .Returns(_clientStatus);
+
+ Mocker.GetMock()
+ .Setup(s => s.GetDownloadClients(It.IsAny()))
+ .Returns(new IDownloadClient[] { _downloadClient.Object });
+
+ Mocker.GetMock()
+ .Setup(s => s.GetLocalizedString(It.IsAny()))
+ .Returns("Some Warning Message");
+ }
+
+ [Test]
+ public void should_return_warning_if_removing_completed_downloads_is_enabled()
+ {
+ Subject.Check().ShouldBeWarning();
+ }
+
+ [Test]
+ public void should_return_ok_if_remove_completed_downloads_is_not_enabled()
+ {
+ _clientStatus.RemovesCompletedDownloads = false;
+ Subject.Check().ShouldBeOk();
+ }
+
+ [Test]
+ [TestCaseSource("DownloadClientExceptions")]
+ public void should_return_ok_if_client_throws_downloadclientexception(Exception ex)
+ {
+ _downloadClient.Setup(s => s.GetStatus())
+ .Throws(ex);
+
+ Subject.Check().ShouldBeOk();
+
+ ExceptionVerification.ExpectedErrors(0);
+ }
+ }
+}
diff --git a/src/NzbDrone.Core.Test/IndexerSearchTests/ReleaseSearchServiceFixture.cs b/src/NzbDrone.Core.Test/IndexerSearchTests/ReleaseSearchServiceFixture.cs
index a2b77ab7d..522d92f9d 100644
--- a/src/NzbDrone.Core.Test/IndexerSearchTests/ReleaseSearchServiceFixture.cs
+++ b/src/NzbDrone.Core.Test/IndexerSearchTests/ReleaseSearchServiceFixture.cs
@@ -568,5 +568,45 @@ namespace NzbDrone.Core.Test.IndexerSearchTests
allCriteria.First().Should().BeOfType();
allCriteria.First().As().SeasonNumber.Should().Be(7);
}
+
+ [Test]
+ public async Task episode_search_should_use_all_available_numbering_from_services_and_xem()
+ {
+ WithEpisode(1, 12, 2, 3);
+
+ Mocker.GetMock()
+ .Setup(s => s.FindByTvdbId(It.IsAny()))
+ .Returns(new List
+ {
+ new SceneMapping
+ {
+ TvdbId = _xemSeries.TvdbId,
+ SearchTerm = _xemSeries.Title,
+ ParseTerm = _xemSeries.Title,
+ FilterRegex = "(?i)-(BTN)$",
+ SeasonNumber = 1,
+ SceneSeasonNumber = 1,
+ SceneOrigin = "tvdb",
+ Type = "ServicesProvider"
+ }
+ });
+
+ var allCriteria = WatchForSearchCriteria();
+
+ await Subject.EpisodeSearch(_xemEpisodes.First(), false, false);
+
+ Mocker.GetMock()
+ .Verify(v => v.FindByTvdbId(_xemSeries.Id), Times.Once());
+
+ allCriteria.Should().HaveCount(2);
+
+ allCriteria.First().Should().BeOfType();
+ allCriteria.First().As().SeasonNumber.Should().Be(1);
+ allCriteria.First().As().EpisodeNumber.Should().Be(12);
+
+ allCriteria.Last().Should().BeOfType();
+ allCriteria.Last().As().SeasonNumber.Should().Be(2);
+ allCriteria.Last().As().EpisodeNumber.Should().Be(3);
+ }
}
}
diff --git a/src/NzbDrone.Core.Test/IndexerTests/IndexerStatusServiceFixture.cs b/src/NzbDrone.Core.Test/IndexerTests/IndexerStatusServiceFixture.cs
index 55ed8abfb..f836852e1 100644
--- a/src/NzbDrone.Core.Test/IndexerTests/IndexerStatusServiceFixture.cs
+++ b/src/NzbDrone.Core.Test/IndexerTests/IndexerStatusServiceFixture.cs
@@ -68,5 +68,16 @@ namespace NzbDrone.Core.Test.IndexerTests
VerifyNoUpdate();
}
+
+ [Test]
+ public void should_not_record_failure_for_unknown_provider()
+ {
+ Subject.RecordFailure(0);
+
+ Mocker.GetMock()
+ .Verify(v => v.FindByProviderId(1), Times.Never);
+
+ VerifyNoUpdate();
+ }
}
}
diff --git a/src/NzbDrone.Core.Test/IndexerTests/TestIndexer.cs b/src/NzbDrone.Core.Test/IndexerTests/TestIndexer.cs
index 2b73b3b73..24fe564dd 100644
--- a/src/NzbDrone.Core.Test/IndexerTests/TestIndexer.cs
+++ b/src/NzbDrone.Core.Test/IndexerTests/TestIndexer.cs
@@ -2,6 +2,7 @@
using NzbDrone.Common.Http;
using NzbDrone.Core.Configuration;
using NzbDrone.Core.Indexers;
+using NzbDrone.Core.Localization;
using NzbDrone.Core.Parser;
namespace NzbDrone.Core.Test.IndexerTests
@@ -15,8 +16,8 @@ namespace NzbDrone.Core.Test.IndexerTests
public int _supportedPageSize;
public override int PageSize => _supportedPageSize;
- public TestIndexer(IHttpClient httpClient, IIndexerStatusService indexerStatusService, IConfigService configService, IParsingService parsingService, Logger logger)
- : base(httpClient, indexerStatusService, configService, parsingService, logger)
+ public TestIndexer(IHttpClient httpClient, IIndexerStatusService indexerStatusService, IConfigService configService, IParsingService parsingService, Logger logger, ILocalizationService localizationService)
+ : base(httpClient, indexerStatusService, configService, parsingService, logger, localizationService)
{
}
diff --git a/src/NzbDrone.Core.Test/IndexerTests/TorrentRssIndexerTests/TestTorrentRssIndexer.cs b/src/NzbDrone.Core.Test/IndexerTests/TorrentRssIndexerTests/TestTorrentRssIndexer.cs
index d969c5653..301da2da6 100644
--- a/src/NzbDrone.Core.Test/IndexerTests/TorrentRssIndexerTests/TestTorrentRssIndexer.cs
+++ b/src/NzbDrone.Core.Test/IndexerTests/TorrentRssIndexerTests/TestTorrentRssIndexer.cs
@@ -7,14 +7,15 @@ using NzbDrone.Common.Http;
using NzbDrone.Core.Configuration;
using NzbDrone.Core.Indexers;
using NzbDrone.Core.Indexers.TorrentRss;
+using NzbDrone.Core.Localization;
using NzbDrone.Core.Parser;
namespace NzbDrone.Core.Test.IndexerTests.TorrentRssIndexerTests
{
public class TestTorrentRssIndexer : TorrentRssIndexer
{
- public TestTorrentRssIndexer(ITorrentRssParserFactory torrentRssParserFactory, IHttpClient httpClient, IIndexerStatusService indexerStatusService, IConfigService configService, IParsingService parsingService, Logger logger)
- : base(torrentRssParserFactory, httpClient, indexerStatusService, configService, parsingService, logger)
+ public TestTorrentRssIndexer(ITorrentRssParserFactory torrentRssParserFactory, IHttpClient httpClient, IIndexerStatusService indexerStatusService, IConfigService configService, IParsingService parsingService, Logger logger, ILocalizationService localizationService)
+ : base(torrentRssParserFactory, httpClient, indexerStatusService, configService, parsingService, logger, localizationService)
{
}
diff --git a/src/NzbDrone.Core.Test/Localization/LocalizationServiceFixture.cs b/src/NzbDrone.Core.Test/Localization/LocalizationServiceFixture.cs
index 180292622..d43657c7f 100644
--- a/src/NzbDrone.Core.Test/Localization/LocalizationServiceFixture.cs
+++ b/src/NzbDrone.Core.Test/Localization/LocalizationServiceFixture.cs
@@ -30,29 +30,22 @@ namespace NzbDrone.Core.Test.Localization
}
[Test]
- public void should_get_string_in_default_language_dictionary_if_no_lang_country_code_exists_and_string_exists()
+ public void should_get_string_in_french()
{
- var localizedString = Subject.GetLocalizedString("UiLanguage", "fr_fr");
+ Mocker.GetMock().Setup(m => m.UILanguage).Returns((int)Language.French);
- localizedString.Should().Be("UI Langue");
+ var localizedString = Subject.GetLocalizedString("UiLanguage");
+
+ localizedString.Should().Be("Langue de l'interface utilisateur");
ExceptionVerification.ExpectedErrors(1);
}
[Test]
- public void should_get_string_in_default_dictionary_if_no_lang_exists_and_string_exists()
+ public void should_get_string_in_default_dictionary_if_unknown_language_and_string_exists()
{
- var localizedString = Subject.GetLocalizedString("UiLanguage", "an");
-
- localizedString.Should().Be("UI Language");
-
- ExceptionVerification.ExpectedErrors(1);
- }
-
- [Test]
- public void should_get_string_in_default_dictionary_if_lang_empty_and_string_exists()
- {
- var localizedString = Subject.GetLocalizedString("UiLanguage", "");
+ Mocker.GetMock().Setup(m => m.UILanguage).Returns(0);
+ var localizedString = Subject.GetLocalizedString("UiLanguage");
localizedString.Should().Be("UI Language");
}
@@ -60,7 +53,7 @@ namespace NzbDrone.Core.Test.Localization
[Test]
public void should_return_argument_if_string_doesnt_exists()
{
- var localizedString = Subject.GetLocalizedString("badString", "en");
+ var localizedString = Subject.GetLocalizedString("badString");
localizedString.Should().Be("badString");
}
diff --git a/src/NzbDrone.Core.Test/MediaFiles/DiskScanServiceTests/ScanFixture.cs b/src/NzbDrone.Core.Test/MediaFiles/DiskScanServiceTests/ScanFixture.cs
index cd9da2e05..d3b8c000f 100644
--- a/src/NzbDrone.Core.Test/MediaFiles/DiskScanServiceTests/ScanFixture.cs
+++ b/src/NzbDrone.Core.Test/MediaFiles/DiskScanServiceTests/ScanFixture.cs
@@ -267,7 +267,7 @@ namespace NzbDrone.Core.Test.MediaFiles.DiskScanServiceTests
Subject.Scan(_series);
Mocker.GetMock()
- .Verify(v => v.GetFiles(It.IsAny(), It.IsAny()), Times.Once());
+ .Verify(v => v.GetFiles(It.IsAny(), It.IsAny()), Times.Exactly(2));
Mocker.GetMock()
.Verify(v => v.GetImportDecisions(It.Is>(l => l.Count == 1), _series, false), Times.Once());
diff --git a/src/NzbDrone.Core.Test/MediaFiles/EpisodeImport/Specifications/AlreadyImportedSpecificationFixture.cs b/src/NzbDrone.Core.Test/MediaFiles/EpisodeImport/Specifications/AlreadyImportedSpecificationFixture.cs
index 9e276d909..3898118ef 100644
--- a/src/NzbDrone.Core.Test/MediaFiles/EpisodeImport/Specifications/AlreadyImportedSpecificationFixture.cs
+++ b/src/NzbDrone.Core.Test/MediaFiles/EpisodeImport/Specifications/AlreadyImportedSpecificationFixture.cs
@@ -123,7 +123,7 @@ namespace NzbDrone.Core.Test.MediaFiles.EpisodeImport.Specifications
GivenHistory(history);
- Subject.IsSatisfiedBy(_localEpisode, _downloadClientItem).Accepted.Should().BeTrue();
+ Subject.IsSatisfiedBy(_localEpisode, _downloadClientItem).Accepted.Should().BeFalse();
}
}
}
diff --git a/src/NzbDrone.Core.Test/MediaFiles/MediaInfo/UpdateMediaInfoServiceFixture.cs b/src/NzbDrone.Core.Test/MediaFiles/MediaInfo/UpdateMediaInfoServiceFixture.cs
index de3621fcd..72af3a7ca 100644
--- a/src/NzbDrone.Core.Test/MediaFiles/MediaInfo/UpdateMediaInfoServiceFixture.cs
+++ b/src/NzbDrone.Core.Test/MediaFiles/MediaInfo/UpdateMediaInfoServiceFixture.cs
@@ -1,3 +1,4 @@
+using System.Collections.Generic;
using System.IO;
using FizzWare.NBuilder;
using FluentAssertions;
@@ -71,7 +72,7 @@ namespace NzbDrone.Core.Test.MediaFiles.MediaInfo
GivenFileExists();
GivenSuccessfulScan();
- Subject.Handle(new SeriesScannedEvent(_series));
+ Subject.Handle(new SeriesScannedEvent(_series, new List()));
Mocker.GetMock()
.Verify(v => v.GetMediaInfo(Path.Combine(_series.Path, "media.mkv")), Times.Exactly(2));
@@ -97,7 +98,7 @@ namespace NzbDrone.Core.Test.MediaFiles.MediaInfo
GivenFileExists();
GivenSuccessfulScan();
- Subject.Handle(new SeriesScannedEvent(_series));
+ Subject.Handle(new SeriesScannedEvent(_series, new List()));
Mocker.GetMock()
.Verify(v => v.GetMediaInfo(Path.Combine(_series.Path, "media.mkv")), Times.Exactly(2));
@@ -123,7 +124,7 @@ namespace NzbDrone.Core.Test.MediaFiles.MediaInfo
GivenFileExists();
GivenSuccessfulScan();
- Subject.Handle(new SeriesScannedEvent(_series));
+ Subject.Handle(new SeriesScannedEvent(_series, new List()));
Mocker.GetMock()
.Verify(v => v.GetMediaInfo(Path.Combine(_series.Path, "media.mkv")), Times.Exactly(3));
@@ -146,7 +147,7 @@ namespace NzbDrone.Core.Test.MediaFiles.MediaInfo
GivenSuccessfulScan();
- Subject.Handle(new SeriesScannedEvent(_series));
+ Subject.Handle(new SeriesScannedEvent(_series, new List()));
Mocker.GetMock()
.Verify(v => v.GetMediaInfo("media.mkv"), Times.Never());
@@ -173,7 +174,7 @@ namespace NzbDrone.Core.Test.MediaFiles.MediaInfo
GivenSuccessfulScan();
GivenFailedScan(Path.Combine(_series.Path, "media2.mkv"));
- Subject.Handle(new SeriesScannedEvent(_series));
+ Subject.Handle(new SeriesScannedEvent(_series, new List()));
Mocker.GetMock()
.Verify(v => v.GetMediaInfo(Path.Combine(_series.Path, "media.mkv")), Times.Exactly(1));
@@ -203,7 +204,7 @@ namespace NzbDrone.Core.Test.MediaFiles.MediaInfo
GivenFileExists();
GivenSuccessfulScan();
- Subject.Handle(new SeriesScannedEvent(_series));
+ Subject.Handle(new SeriesScannedEvent(_series, new List()));
Mocker.GetMock()
.Verify(v => v.GetMediaInfo(It.IsAny()), Times.Never());
diff --git a/src/NzbDrone.Core.Test/OrganizerTests/FileNameBuilderTests/CleanTitleTheFixture.cs b/src/NzbDrone.Core.Test/OrganizerTests/FileNameBuilderTests/CleanTitleTheFixture.cs
new file mode 100644
index 000000000..6b738f250
--- /dev/null
+++ b/src/NzbDrone.Core.Test/OrganizerTests/FileNameBuilderTests/CleanTitleTheFixture.cs
@@ -0,0 +1,86 @@
+using System.Collections.Generic;
+using System.Linq;
+using FizzWare.NBuilder;
+using FluentAssertions;
+using NUnit.Framework;
+using NzbDrone.Core.CustomFormats;
+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 CleanTitleTheFixture : CoreTest
+ {
+ private Series _series;
+ private Episode _episode;
+ private EpisodeFile _episodeFile;
+ private NamingConfig _namingConfig;
+
+ [SetUp]
+ public void Setup()
+ {
+ _series = Builder
+ .CreateNew()
+ .Build();
+
+ _episode = Builder.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()
+ .Setup(c => c.GetConfig()).Returns(_namingConfig);
+
+ Mocker.GetMock()
+ .Setup(v => v.Get(Moq.It.IsAny()))
+ .Returns(v => Quality.DefaultQualityDefinitions.First(c => c.Quality == v));
+
+ Mocker.GetMock()
+ .Setup(v => v.All())
+ .Returns(new List());
+ }
+
+ [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 AandE")]
+ [TestCase("The Climax: I (Almost) Got Away With It (2016)", "Climax I Almost Got Away With It, The 2016")]
+ public void should_get_expected_title_back(string title, string expected)
+ {
+ _series.Title = title;
+ _namingConfig.StandardEpisodeFormat = "{Series CleanTitleThe}";
+
+ Subject.BuildFileName(new List { _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 CleanTitleThe}";
+
+ Subject.BuildFileName(new List { _episode }, _series, _episodeFile)
+ .Should().Be(title);
+ }
+ }
+}
diff --git a/src/NzbDrone.Core.Test/OrganizerTests/FileNameBuilderTests/CleanTitleTheWithoutYearFixture.cs b/src/NzbDrone.Core.Test/OrganizerTests/FileNameBuilderTests/CleanTitleTheWithoutYearFixture.cs
new file mode 100644
index 000000000..104f51867
--- /dev/null
+++ b/src/NzbDrone.Core.Test/OrganizerTests/FileNameBuilderTests/CleanTitleTheWithoutYearFixture.cs
@@ -0,0 +1,79 @@
+using System.Collections.Generic;
+using System.Linq;
+using FizzWare.NBuilder;
+using FluentAssertions;
+using NUnit.Framework;
+using NzbDrone.Core.CustomFormats;
+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 CleanTitleTheWithoutYearFixture : CoreTest
+ {
+ private Series _series;
+ private Episode _episode;
+ private EpisodeFile _episodeFile;
+ private NamingConfig _namingConfig;
+
+ [SetUp]
+ public void Setup()
+ {
+ _series = Builder
+ .CreateNew()
+ .Build();
+
+ _episode = Builder.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()
+ .Setup(c => c.GetConfig()).Returns(_namingConfig);
+
+ Mocker.GetMock()
+ .Setup(v => v.Get(Moq.It.IsAny()))
+ .Returns(v => Quality.DefaultQualityDefinitions.First(c => c.Quality == v));
+
+ Mocker.GetMock()
+ .Setup(v => v.All())
+ .Returns(new List());
+ }
+
+ [TestCase("The Mist", 2018, "Mist, The")]
+ [TestCase("The Rat Pack (A&E)", 1999, "Rat Pack, The AandE")]
+ [TestCase("The Climax: I (Almost) Got Away With It (2016)", 2016, "Climax I Almost Got Away With It, The")]
+ [TestCase("A", 2017, "A")]
+ public void should_get_expected_title_back(string title, int year, string expected)
+ {
+ _series.Title = title;
+ _series.Year = year;
+ _namingConfig.StandardEpisodeFormat = "{Series CleanTitleTheWithoutYear}";
+
+ Subject.BuildFileName(new List { _episode }, _series, _episodeFile)
+ .Should().Be(expected);
+ }
+
+ [Test]
+ public void should_not_include_0_for_year()
+ {
+ _series.Title = "The Alienist";
+ _series.Year = 0;
+ _namingConfig.StandardEpisodeFormat = "{Series CleanTitleTheWithoutYear}";
+
+ Subject.BuildFileName(new List { _episode }, _series, _episodeFile)
+ .Should().Be("Alienist, The");
+ }
+ }
+}
diff --git a/src/NzbDrone.Core.Test/OrganizerTests/FileNameBuilderTests/CleanTitleTheYearFixture.cs b/src/NzbDrone.Core.Test/OrganizerTests/FileNameBuilderTests/CleanTitleTheYearFixture.cs
new file mode 100644
index 000000000..bc06d4698
--- /dev/null
+++ b/src/NzbDrone.Core.Test/OrganizerTests/FileNameBuilderTests/CleanTitleTheYearFixture.cs
@@ -0,0 +1,81 @@
+using System.Collections.Generic;
+using System.Linq;
+using FizzWare.NBuilder;
+using FluentAssertions;
+using NUnit.Framework;
+using NzbDrone.Core.CustomFormats;
+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 CleanTitleTheYearFixture : CoreTest
+ {
+ private Series _series;
+ private Episode _episode;
+ private EpisodeFile _episodeFile;
+ private NamingConfig _namingConfig;
+
+ [SetUp]
+ public void Setup()
+ {
+ _series = Builder
+ .CreateNew()
+ .Build();
+
+ _episode = Builder.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()
+ .Setup(c => c.GetConfig()).Returns(_namingConfig);
+
+ Mocker.GetMock()
+ .Setup(v => v.Get(Moq.It.IsAny()))
+ .Returns(v => Quality.DefaultQualityDefinitions.First(c => c.Quality == v));
+
+ Mocker.GetMock()
+ .Setup(v => v.All())
+ .Returns(new List());
+ }
+
+ [TestCase("The Mist", 2018, "Mist, The 2018")]
+ [TestCase("The Rat Pack (A&E)", 1999, "Rat Pack, The AandE 1999")]
+ [TestCase("The Climax: I (Almost) Got Away With It (2016)", 2016, "Climax I Almost Got Away With It, The 2016")]
+ [TestCase("The Climax: I (Almost) Got Away With It (2016)", 0, "Climax I Almost Got Away With It, The 2016")]
+ [TestCase("The Climax: I (Almost) Got Away With It", 0, "Climax I Almost Got Away With It, The")]
+ [TestCase("A", 2017, "A 2017")]
+ public void should_get_expected_title_back(string title, int year, string expected)
+ {
+ _series.Title = title;
+ _series.Year = year;
+ _namingConfig.StandardEpisodeFormat = "{Series CleanTitleTheYear}";
+
+ Subject.BuildFileName(new List { _episode }, _series, _episodeFile)
+ .Should().Be(expected);
+ }
+
+ [Test]
+ public void should_not_include_0_for_year()
+ {
+ _series.Title = "The Alienist";
+ _series.Year = 0;
+ _namingConfig.StandardEpisodeFormat = "{Series TitleTheYear}";
+
+ Subject.BuildFileName(new List { _episode }, _series, _episodeFile)
+ .Should().Be("Alienist, The");
+ }
+ }
+}
diff --git a/src/NzbDrone.Core.Test/OrganizerTests/FileNameBuilderTests/SeriesTitleFirstCharacterFixture.cs b/src/NzbDrone.Core.Test/OrganizerTests/FileNameBuilderTests/SeriesTitleFirstCharacterFixture.cs
index 763b948a6..5b3d09d6a 100644
--- a/src/NzbDrone.Core.Test/OrganizerTests/FileNameBuilderTests/SeriesTitleFirstCharacterFixture.cs
+++ b/src/NzbDrone.Core.Test/OrganizerTests/FileNameBuilderTests/SeriesTitleFirstCharacterFixture.cs
@@ -36,6 +36,13 @@ namespace NzbDrone.Core.Test.OrganizerTests.FileNameBuilderTests
[TestCase("The Mist", "M\\The Mist")]
[TestCase("A", "A\\A")]
[TestCase("30 Rock", "3\\30 Rock")]
+ [TestCase("The '80s Greatest", "8\\The '80s Greatest")]
+ [TestCase("좀비버스", "좀\\좀비버스")]
+ [TestCase("¡Mucha Lucha!", "M\\¡Mucha Lucha!")]
+ [TestCase(".hack", "H\\hack")]
+ [TestCase("Ütopya", "U\\Ütopya")]
+ [TestCase("Æon Flux", "A\\Æon Flux")]
+
public void should_get_expected_folder_name_back(string title, string expected)
{
_series.Title = title;
diff --git a/src/NzbDrone.Core.Test/ParserTests/AbsoluteEpisodeNumberParserFixture.cs b/src/NzbDrone.Core.Test/ParserTests/AbsoluteEpisodeNumberParserFixture.cs
index e0cae8bf2..5dccae231 100644
--- a/src/NzbDrone.Core.Test/ParserTests/AbsoluteEpisodeNumberParserFixture.cs
+++ b/src/NzbDrone.Core.Test/ParserTests/AbsoluteEpisodeNumberParserFixture.cs
@@ -122,6 +122,12 @@ namespace NzbDrone.Core.Test.ParserTests
[TestCase("[SubsPlease] Anime Title 100 S3 - 01 (1080p) [5A493522]", "Anime Title 100 S3", 1, 0, 0)]
[TestCase("[CameEsp] Another Anime 100 - Another 100 Anime - 01 [720p][ESP-ENG][mkv]", "Another Anime 100 - Another 100 Anime", 1, 0, 0)]
[TestCase("[SubsPlease] Another Anime 100 - Another 100 Anime - 01 (1080p) [4E6B4518].mkv", "Another Anime 100 - Another 100 Anime", 1, 0, 0)]
+ [TestCase("Some show 69. Blm (29.10.2023) 1080p WebDL #turkseed", "Some show", 69, 0, 0)]
+ [TestCase("Soap opera 01 BLM(01.11.2023) 1080p HDTV AC3 x264 TURG", "Soap opera", 1, 0, 0)]
+ [TestCase("Turkish show 60.Bolum (31.01.2023) 720p WebDL AAC H.264 - TURG", "Turkish show", 60, 0, 0)]
+ [TestCase("Different show 1. Bölüm (23.10.2023) 720p WebDL AAC H.264 - TURG", "Different show", 1, 0, 0)]
+ [TestCase("Dubbed show 79.BLM Sezon Finali(25.06.2023) 720p WEB-DL AAC2.0 H.264-TURG", "Dubbed show", 79, 0, 0)]
+ [TestCase("Exclusive BLM Documentary with no false positives EP03.1080p.AAC.x264", "Exclusive BLM Documentary with no false positives", 3, 0, 0)]
// [TestCase("", "", 0, 0, 0)]
public void should_parse_absolute_numbers(string postTitle, string title, int absoluteEpisodeNumber, int seasonNumber, int episodeNumber)
diff --git a/src/NzbDrone.Core.Test/ParserTests/MultiEpisodeParserFixture.cs b/src/NzbDrone.Core.Test/ParserTests/MultiEpisodeParserFixture.cs
index 3fbd06f34..6af9a1733 100644
--- a/src/NzbDrone.Core.Test/ParserTests/MultiEpisodeParserFixture.cs
+++ b/src/NzbDrone.Core.Test/ParserTests/MultiEpisodeParserFixture.cs
@@ -75,6 +75,7 @@ namespace NzbDrone.Core.Test.ParserTests
[TestCase("13 Series Se.1 afl.2-3-4 [VTM]", "13 Series", 1, new[] { 2, 3, 4 })]
[TestCase("Series T Se.3 afl.3 en 4", "Series T", 3, new[] { 3, 4 })]
[TestCase("Series Title (S15E06-08) City Sushi", "Series Title", 15, new[] { 6, 7, 8 })]
+ [TestCase("Босх: Спадок (S2E1-4) / Series: Legacy (S2E1-4) (2023) WEB-DL 1080p Ukr/Eng | sub Eng", "Series: Legacy", 2, new[] { 1, 2, 3, 4 })]
// [TestCase("", "", , new [] { })]
public void should_parse_multiple_episodes(string postTitle, string title, int season, int[] episodes)
diff --git a/src/NzbDrone.Core.Test/ParserTests/ParserFixture.cs b/src/NzbDrone.Core.Test/ParserTests/ParserFixture.cs
index 91cf0e477..db1a61d3f 100644
--- a/src/NzbDrone.Core.Test/ParserTests/ParserFixture.cs
+++ b/src/NzbDrone.Core.Test/ParserTests/ParserFixture.cs
@@ -92,5 +92,12 @@ namespace NzbDrone.Core.Test.ParserTests
var result = Parser.Parser.ParseTitle(path);
result.ReleaseTitle.Should().Be(releaseTitle);
}
+
+ [TestCase("Босх: Спадок (S2E1) / Series: Legacy (S2E1) (2023) WEB-DL 1080p Ukr/Eng | sub Eng", "Босх: Спадок", "Series: Legacy")]
+ public void should_parse_multiple_series_titles(string postTitle, params string[] titles)
+ {
+ var seriesTitleInfo = Parser.Parser.ParseTitle(postTitle).SeriesTitleInfo;
+ seriesTitleInfo.AllTitles.Should().BeEquivalentTo(titles);
+ }
}
}
diff --git a/src/NzbDrone.Core.Test/ParserTests/ReleaseGroupParserFixture.cs b/src/NzbDrone.Core.Test/ParserTests/ReleaseGroupParserFixture.cs
index 003fb50b5..dc7c38ab5 100644
--- a/src/NzbDrone.Core.Test/ParserTests/ReleaseGroupParserFixture.cs
+++ b/src/NzbDrone.Core.Test/ParserTests/ReleaseGroupParserFixture.cs
@@ -70,6 +70,18 @@ namespace NzbDrone.Core.Test.ParserTests
[TestCase("Show Title (2021) S01 (2160p ATVP WEB-DL Hybrid H265 DV HDR10+ DDP Atmos 5.1 English - HONE)", "HONE")]
[TestCase("Series.Title.S01E09.1080p.DSNP.WEB-DL.DDP2.0.H.264-VARYG (Blue Lock, Multi-Subs)", "VARYG")]
[TestCase("Series.Title (2014) S09E10 (1080p AMZN WEB-DL x265 HEVC 10bit DDP 5.1 Vyndros)", "Vyndros")]
+ [TestCase("Series Title S02E03 Title 4k to 1080p DSNP WEBrip x265 DDP 5 1 Releaser[SEV]", "SEV")]
+ [TestCase("Series Title Season 01 S01 1080p AMZN UHD WebRip x265 DDP 5.1 Atmos Releaser-SEV", "SEV")]
+ [TestCase("Series Title - S01.E06 - Title 1080p AMZN WebRip x265 DDP 5.1 Atmos Releaser [SEV]", "SEV")]
+ [TestCase("Grey's Anatomy (2005) - S01E01 - A Hard Day's Night (1080p DSNP WEB-DL x265 Garshasp).mkv", "Garshasp")]
+ [TestCase("Marvel's Agent Carter (2015) - S02E04 - Smoke & Mirrors (1080p BluRay x265 Kappa).mkv", "Kappa")]
+ [TestCase("Snowpiercer (2020) - S02E03 - A Great Odyssey (1080p BluRay x265 Kappa).mkv", "Kappa")]
+ [TestCase("Enaaya (2019) - S01E01 - Episode 1 (1080p WEB-DL x265 Natty).mkv", "Natty")]
+ [TestCase("SpongeBob SquarePants (1999) - S03E01-E02 - Mermaid Man and Barnacle Boy IV & Doing Time (1080p AMZN WEB-DL x265 RCVR).mkv", "RCVR")]
+ [TestCase("Invincible (2021) - S01E02 - Here Goes Nothing (1080p WEB-DL x265 SAMPA).mkv", "SAMPA")]
+ [TestCase("The Bad Batch (2021) - S01E01 - Aftermath (1080p DSNP WEB-DL x265 YOGI).mkv", "YOGI")]
+ [TestCase("Line of Duty (2012) - S01E01 - Episode 1 (1080p BluRay x265 r00t).mkv", "r00t")]
+ [TestCase("Rich & Shameless - S01E01 - Girls Gone Wild Exposed (720p x265 EDGE2020).mkv", "EDGE2020")]
public void should_parse_exception_release_group(string title, string expected)
{
Parser.Parser.ParseReleaseGroup(title).Should().Be(expected);
diff --git a/src/NzbDrone.Core.Test/ParserTests/SeasonParserFixture.cs b/src/NzbDrone.Core.Test/ParserTests/SeasonParserFixture.cs
index 6f31129cf..9910d612f 100644
--- a/src/NzbDrone.Core.Test/ParserTests/SeasonParserFixture.cs
+++ b/src/NzbDrone.Core.Test/ParserTests/SeasonParserFixture.cs
@@ -72,6 +72,7 @@ namespace NzbDrone.Core.Test.ParserTests
}
[TestCase("The.Series.2016.S02.Part.1.1080p.NF.WEBRip.DD5.1.x264-NTb", "The Series 2016", 2, 1)]
+ [TestCase("The.Series.S07.Vol.1.1080p.NF.WEBRip.DD5.1.x264-NTb", "The Series", 7, 1)]
public void should_parse_partial_season_release(string postTitle, string title, int season, int seasonPart)
{
var result = Parser.Parser.ParseTitle(postTitle);
diff --git a/src/NzbDrone.Core.Test/ParserTests/SingleEpisodeParserFixture.cs b/src/NzbDrone.Core.Test/ParserTests/SingleEpisodeParserFixture.cs
index 4ebd0f1e1..245fee1a6 100644
--- a/src/NzbDrone.Core.Test/ParserTests/SingleEpisodeParserFixture.cs
+++ b/src/NzbDrone.Core.Test/ParserTests/SingleEpisodeParserFixture.cs
@@ -161,6 +161,10 @@ namespace NzbDrone.Core.Test.ParserTests
[TestCase("Series-S07E12-31st_Century_Fox-[Bluray-1080p].mkv", "Series", 7, 12)]
[TestCase("TheTitle-S12E13-3_Acts_of_God.mkv", "TheTitle", 12, 13)]
[TestCase("Series Title - Temporada 2 [HDTV 720p][Cap.408]", "Series Title", 4, 8)]
+ [TestCase("Series Title [HDTV][Cap.104](website.com).avi", "Series Title", 1, 4)]
+ [TestCase("Series Title [HDTV][Cap.402](website.com).avi", "Series Title", 4, 2)]
+ [TestCase("Series Title [HDTV 720p][Cap.101](website.com).mkv", "Series Title", 1, 1)]
+ [TestCase("Босх: Спадок (S2E1) / Series: Legacy (S2E1) (2023) WEB-DL 1080p Ukr/Eng | sub Eng", "Series: Legacy", 2, 1)]
// [TestCase("", "", 0, 0)]
public void should_parse_single_episode(string postTitle, string title, int seasonNumber, int episodeNumber)
diff --git a/src/NzbDrone.Core.Test/Profiles/QualityProfileServiceFixture.cs b/src/NzbDrone.Core.Test/Profiles/QualityProfileServiceFixture.cs
index 4c776af57..fae7da081 100644
--- a/src/NzbDrone.Core.Test/Profiles/QualityProfileServiceFixture.cs
+++ b/src/NzbDrone.Core.Test/Profiles/QualityProfileServiceFixture.cs
@@ -32,7 +32,7 @@ namespace NzbDrone.Core.Test.Profiles
Subject.Handle(new ApplicationStartedEvent());
- Mocker.GetMock()
+ Mocker.GetMock()
.Verify(v => v.Insert(It.IsAny()), Times.Exactly(6));
}
@@ -42,13 +42,13 @@ namespace NzbDrone.Core.Test.Profiles
// We don't want to keep adding them back if a user deleted them on purpose.
public void Init_should_skip_if_any_profiles_already_exist()
{
- Mocker.GetMock()
+ Mocker.GetMock()
.Setup(s => s.All())
.Returns(Builder.CreateListOfSize(2).Build().ToList());
Subject.Handle(new ApplicationStartedEvent());
- Mocker.GetMock()
+ Mocker.GetMock()
.Verify(v => v.Insert(It.IsAny()), Times.Never());
}
@@ -65,11 +65,11 @@ namespace NzbDrone.Core.Test.Profiles
.Build().ToList();
Mocker.GetMock().Setup(c => c.GetAllSeries()).Returns(seriesList);
- Mocker.GetMock().Setup(c => c.Get(profile.Id)).Returns(profile);
+ Mocker.GetMock().Setup(c => c.Get(profile.Id)).Returns(profile);
Assert.Throws(() => Subject.Delete(profile.Id));
- Mocker.GetMock().Verify(c => c.Delete(It.IsAny()), Times.Never());
+ Mocker.GetMock().Verify(c => c.Delete(It.IsAny()), Times.Never());
}
[Test]
@@ -84,7 +84,7 @@ namespace NzbDrone.Core.Test.Profiles
Subject.Delete(1);
- Mocker.GetMock().Verify(c => c.Delete(1), Times.Once());
+ Mocker.GetMock().Verify(c => c.Delete(1), Times.Once());
}
[Test]
@@ -103,7 +103,7 @@ namespace NzbDrone.Core.Test.Profiles
.Random(1)
.Build().ToList();
- Mocker.GetMock().Setup(c => c.Get(profile.Id)).Returns(profile);
+ Mocker.GetMock().Setup(c => c.Get(profile.Id)).Returns(profile);
Mocker.GetMock().Setup(c => c.GetAllSeries()).Returns(seriesList);
Mocker.GetMock()
@@ -112,7 +112,7 @@ namespace NzbDrone.Core.Test.Profiles
Assert.Throws(() => Subject.Delete(1));
- Mocker.GetMock().Verify(c => c.Delete(It.IsAny()), Times.Never());
+ Mocker.GetMock().Verify(c => c.Delete(It.IsAny()), Times.Never());
}
}
}
diff --git a/src/NzbDrone.Core.Test/SeriesStatsTests/SeriesStatisticsFixture.cs b/src/NzbDrone.Core.Test/SeriesStatsTests/SeriesStatisticsFixture.cs
index 772b084ae..f6d386364 100644
--- a/src/NzbDrone.Core.Test/SeriesStatsTests/SeriesStatisticsFixture.cs
+++ b/src/NzbDrone.Core.Test/SeriesStatsTests/SeriesStatisticsFixture.cs
@@ -94,20 +94,6 @@ namespace NzbDrone.Core.Test.SeriesStatsTests
stats.First().NextAiring.Should().NotHaveValue();
}
- [Test]
- public void should_have_previous_airing_for_old_episode_with_file()
- {
- GivenEpisodeWithFile();
- GivenOldEpisode();
- GivenEpisode();
-
- var stats = Subject.SeriesStatistics();
-
- stats.Should().HaveCount(1);
- stats.First().NextAiring.Should().NotHaveValue();
- stats.First().PreviousAiring.Should().BeCloseTo(_episode.AirDateUtc.Value, TimeSpan.FromMilliseconds(1000));
- }
-
[Test]
public void should_have_previous_airing_for_old_episode_without_file_monitored()
{
diff --git a/src/NzbDrone.Core.Test/TvTests/EpisodeMonitoredServiceTests/SetEpisodeMontitoredFixture.cs b/src/NzbDrone.Core.Test/TvTests/EpisodeMonitoredServiceTests/SetEpisodeMontitoredFixture.cs
index 0ad93ce7e..23d43095a 100644
--- a/src/NzbDrone.Core.Test/TvTests/EpisodeMonitoredServiceTests/SetEpisodeMontitoredFixture.cs
+++ b/src/NzbDrone.Core.Test/TvTests/EpisodeMonitoredServiceTests/SetEpisodeMontitoredFixture.cs
@@ -202,7 +202,7 @@ namespace NzbDrone.Core.Test.TvTests.EpisodeMonitoredServiceTests
}
[Test]
- public void should_not_monitor_season_when_all_episodes_are_monitored_except_latest_season()
+ public void should_not_monitor_season_when_all_episodes_are_monitored_except_last_season()
{
_series.Seasons = Builder.CreateListOfSize(2)
.All()
@@ -226,7 +226,7 @@ namespace NzbDrone.Core.Test.TvTests.EpisodeMonitoredServiceTests
var monitoringOptions = new MonitoringOptions
{
- Monitor = MonitorTypes.LatestSeason
+ Monitor = MonitorTypes.LastSeason
};
Subject.SetEpisodeMonitoredStatus(_series, monitoringOptions);
@@ -264,13 +264,47 @@ namespace NzbDrone.Core.Test.TvTests.EpisodeMonitoredServiceTests
}
[Test]
- public void should_not_monitor_latest_season_if_all_episodes_aired_more_than_90_days_ago()
+ public void should_monitor_last_season_if_all_episodes_aired_more_than_90_days_ago()
+ {
+ _series.Seasons = Builder.CreateListOfSize(2)
+ .All()
+ .With(n => n.Monitored = true)
+ .Build()
+ .ToList();
+
+ _episodes = Builder.CreateListOfSize(5)
+ .All()
+ .With(e => e.SeasonNumber = 1)
+ .With(e => e.EpisodeFileId = 0)
+ .With(e => e.AirDateUtc = DateTime.UtcNow.AddDays(-200))
+ .TheLast(2)
+ .With(e => e.SeasonNumber = 2)
+ .With(e => e.AirDateUtc = DateTime.UtcNow.AddDays(-100))
+ .Build()
+ .ToList();
+
+ var monitoringOptions = new MonitoringOptions
+ {
+ Monitor = MonitorTypes.LastSeason
+ };
+
+ Subject.SetEpisodeMonitoredStatus(_series, monitoringOptions);
+
+ VerifySeasonMonitored(n => n.SeasonNumber == 2);
+ VerifyMonitored(n => n.SeasonNumber == 2);
+
+ VerifySeasonNotMonitored(n => n.SeasonNumber == 1);
+ VerifyNotMonitored(n => n.SeasonNumber == 1);
+ }
+
+ [Test]
+ public void should_not_monitor_any_recent_episodes_if_all_episodes_aired_more_than_90_days_ago()
{
_episodes.ForEach(e => e.AirDateUtc = DateTime.UtcNow.AddDays(-100));
var monitoringOptions = new MonitoringOptions
{
- Monitor = MonitorTypes.LatestSeason
+ Monitor = MonitorTypes.Recent
};
Subject.SetEpisodeMonitoredStatus(_series, monitoringOptions);
@@ -279,6 +313,43 @@ namespace NzbDrone.Core.Test.TvTests.EpisodeMonitoredServiceTests
.Verify(v => v.UpdateEpisodes(It.Is>(l => l.All(e => !e.Monitored))));
}
+ [Test]
+ public void should_monitor_any_recent_and_future_episodes_if_all_episodes_aired_within_90_days()
+ {
+ _series.Seasons = Builder.CreateListOfSize(1)
+ .All()
+ .With(n => n.Monitored = true)
+ .Build()
+ .ToList();
+
+ _episodes = Builder.CreateListOfSize(5)
+ .All()
+ .With(e => e.SeasonNumber = 1)
+ .With(e => e.EpisodeFileId = 0)
+ .With(e => e.AirDateUtc = DateTime.UtcNow.AddDays(-200))
+ .TheLast(3)
+ .With(e => e.AirDateUtc = DateTime.UtcNow.AddDays(-5))
+ .TheLast(1)
+ .With(e => e.AirDateUtc = DateTime.UtcNow.AddDays(30))
+ .Build()
+ .ToList();
+
+ Mocker.GetMock()
+ .Setup(s => s.GetEpisodeBySeries(It.IsAny()))
+ .Returns(_episodes);
+
+ var monitoringOptions = new MonitoringOptions
+ {
+ Monitor = MonitorTypes.Recent
+ };
+
+ Subject.SetEpisodeMonitoredStatus(_series, monitoringOptions);
+
+ VerifySeasonMonitored(n => n.SeasonNumber == 1);
+ VerifyNotMonitored(n => n.AirDateUtc.HasValue && n.AirDateUtc.Value.Before(DateTime.UtcNow.AddDays(-90)));
+ VerifyMonitored(n => n.AirDateUtc.HasValue && n.AirDateUtc.Value.After(DateTime.UtcNow.AddDays(-90)));
+ }
+
[Test]
public void should_monitor_latest_season_if_some_episodes_have_aired()
{
@@ -302,7 +373,7 @@ namespace NzbDrone.Core.Test.TvTests.EpisodeMonitoredServiceTests
var monitoringOptions = new MonitoringOptions
{
- Monitor = MonitorTypes.LatestSeason
+ Monitor = MonitorTypes.LastSeason
};
Subject.SetEpisodeMonitoredStatus(_series, monitoringOptions);
diff --git a/src/NzbDrone.Core.Test/TvTests/RefreshSeriesServiceFixture.cs b/src/NzbDrone.Core.Test/TvTests/RefreshSeriesServiceFixture.cs
index 7a154d5fa..429eef2d2 100644
--- a/src/NzbDrone.Core.Test/TvTests/RefreshSeriesServiceFixture.cs
+++ b/src/NzbDrone.Core.Test/TvTests/RefreshSeriesServiceFixture.cs
@@ -58,9 +58,10 @@ namespace NzbDrone.Core.Test.TvTests
}
[Test]
- public void should_monitor_new_seasons_automatically_if_series_is_monitored()
+ public void should_monitor_new_seasons_automatically_if_monitor_new_items_is_all()
{
- _series.Monitored = true;
+ _series.MonitorNewItems = NewItemMonitorTypes.All;
+
var newSeriesInfo = _series.JsonClone();
newSeriesInfo.Seasons.Add(Builder.CreateNew()
.With(s => s.SeasonNumber = 2)
@@ -75,9 +76,10 @@ namespace NzbDrone.Core.Test.TvTests
}
[Test]
- public void should_not_monitor_new_seasons_automatically_if_series_is_not_monitored()
+ public void should_not_monitor_new_seasons_automatically_if_monitor_new_items_is_none()
{
- _series.Monitored = false;
+ _series.MonitorNewItems = NewItemMonitorTypes.None;
+
var newSeriesInfo = _series.JsonClone();
newSeriesInfo.Seasons.Add(Builder.CreateNew()
.With(s => s.SeasonNumber = 2)
diff --git a/src/NzbDrone.Core/Analytics/AnalyticsService.cs b/src/NzbDrone.Core/Analytics/AnalyticsService.cs
index 6c71576a7..a4a043e01 100644
--- a/src/NzbDrone.Core/Analytics/AnalyticsService.cs
+++ b/src/NzbDrone.Core/Analytics/AnalyticsService.cs
@@ -1,4 +1,4 @@
-using System;
+using System;
using System.Linq;
using NzbDrone.Common.EnvironmentInfo;
using NzbDrone.Core.Configuration;
@@ -30,7 +30,7 @@ namespace NzbDrone.Core.Analytics
{
get
{
- var lastRecord = _historyService.Paged(new PagingSpec() { Page = 0, PageSize = 1, SortKey = "date", SortDirection = SortDirection.Descending });
+ var lastRecord = _historyService.Paged(new PagingSpec() { Page = 0, PageSize = 1, SortKey = "date", SortDirection = SortDirection.Descending }, null, null);
var monthAgo = DateTime.UtcNow.AddMonths(-1);
return lastRecord.Records.Any(v => v.Date > monthAgo);
diff --git a/src/NzbDrone.Core/Annotations/FieldDefinitionAttribute.cs b/src/NzbDrone.Core/Annotations/FieldDefinitionAttribute.cs
index 0a30a9f3f..a0a1896e0 100644
--- a/src/NzbDrone.Core/Annotations/FieldDefinitionAttribute.cs
+++ b/src/NzbDrone.Core/Annotations/FieldDefinitionAttribute.cs
@@ -42,6 +42,23 @@ namespace NzbDrone.Core.Annotations
public string RequestAction { get; set; }
}
+ [AttributeUsage(AttributeTargets.Property, AllowMultiple = true)]
+ public class FieldTokenAttribute : Attribute
+ {
+ public FieldTokenAttribute(TokenField field, string label = "", string token = "", object value = null)
+ {
+ Label = label;
+ Field = field;
+ Token = token;
+ Value = value?.ToString();
+ }
+
+ public string Label { get; set; }
+ public TokenField Field { get; set; }
+ public string Token { get; set; }
+ public string Value { get; set; }
+ }
+
public class FieldSelectOption
{
public int Value { get; set; }
@@ -67,7 +84,8 @@ namespace NzbDrone.Core.Annotations
OAuth,
Device,
TagSelect,
- RootFolder
+ RootFolder,
+ QualityProfile
}
public enum HiddenType
@@ -84,4 +102,11 @@ namespace NzbDrone.Core.Annotations
ApiKey,
UserName
}
+
+ public enum TokenField
+ {
+ Label,
+ HelpText,
+ HelpTextWarning
+ }
}
diff --git a/src/NzbDrone.Core/AutoTagging/Specifications/GenreSpecification.cs b/src/NzbDrone.Core/AutoTagging/Specifications/GenreSpecification.cs
index 59173f312..032813f20 100644
--- a/src/NzbDrone.Core/AutoTagging/Specifications/GenreSpecification.cs
+++ b/src/NzbDrone.Core/AutoTagging/Specifications/GenreSpecification.cs
@@ -1,6 +1,7 @@
using System.Collections.Generic;
using System.Linq;
using FluentValidation;
+using NzbDrone.Common.Extensions;
using NzbDrone.Core.Annotations;
using NzbDrone.Core.Tv;
using NzbDrone.Core.Validation;
@@ -17,7 +18,7 @@ namespace NzbDrone.Core.AutoTagging.Specifications
public class GenreSpecification : AutoTaggingSpecificationBase
{
- private static readonly GenreSpecificationValidator Validator = new GenreSpecificationValidator();
+ private static readonly GenreSpecificationValidator Validator = new ();
public override int Order => 1;
public override string ImplementationName => "Genre";
@@ -27,7 +28,7 @@ namespace NzbDrone.Core.AutoTagging.Specifications
protected override bool IsSatisfiedByWithoutNegate(Series series)
{
- return series.Genres.Any(genre => Value.Contains(genre));
+ return series.Genres.Any(genre => Value.ContainsIgnoreCase(genre));
}
public override NzbDroneValidationResult Validate()
diff --git a/src/NzbDrone.Core/AutoTagging/Specifications/QualityProfileSpecification.cs b/src/NzbDrone.Core/AutoTagging/Specifications/QualityProfileSpecification.cs
new file mode 100644
index 000000000..4ac2ef14b
--- /dev/null
+++ b/src/NzbDrone.Core/AutoTagging/Specifications/QualityProfileSpecification.cs
@@ -0,0 +1,36 @@
+using FluentValidation;
+using NzbDrone.Core.Annotations;
+using NzbDrone.Core.Tv;
+using NzbDrone.Core.Validation;
+
+namespace NzbDrone.Core.AutoTagging.Specifications
+{
+ public class QualityProfileSpecificationValidator : AbstractValidator
+ {
+ public QualityProfileSpecificationValidator()
+ {
+ RuleFor(c => c.Value).GreaterThan(0);
+ }
+ }
+
+ public class QualityProfileSpecification : AutoTaggingSpecificationBase
+ {
+ private static readonly QualityProfileSpecificationValidator Validator = new ();
+
+ public override int Order => 1;
+ public override string ImplementationName => "Quality Profile";
+
+ [FieldDefinition(1, Label = "Quality Profile", Type = FieldType.QualityProfile)]
+ public int Value { get; set; }
+
+ protected override bool IsSatisfiedByWithoutNegate(Series series)
+ {
+ return Value == series.QualityProfileId;
+ }
+
+ public override NzbDroneValidationResult Validate()
+ {
+ return new NzbDroneValidationResult(Validator.Validate(this));
+ }
+ }
+}
diff --git a/src/NzbDrone.Core/Blocklisting/BlocklistService.cs b/src/NzbDrone.Core/Blocklisting/BlocklistService.cs
index 93203cc81..808e3dff5 100644
--- a/src/NzbDrone.Core/Blocklisting/BlocklistService.cs
+++ b/src/NzbDrone.Core/Blocklisting/BlocklistService.cs
@@ -15,6 +15,7 @@ namespace NzbDrone.Core.Blocklisting
public interface IBlocklistService
{
bool Blocklisted(int seriesId, ReleaseInfo release);
+ bool BlocklistedTorrentHash(int seriesId, string hash);
PagingSpec Paged(PagingSpec pagingSpec);
void Block(RemoteEpisode remoteEpisode, string message);
void Delete(int id);
@@ -61,6 +62,12 @@ namespace NzbDrone.Core.Blocklisting
.Any(b => SameNzb(b, release));
}
+ public bool BlocklistedTorrentHash(int seriesId, string hash)
+ {
+ return _blocklistRepository.BlocklistedByTorrentInfoHash(seriesId, hash).Any(b =>
+ b.TorrentInfoHash.Equals(hash, StringComparison.InvariantCultureIgnoreCase));
+ }
+
public PagingSpec Paged(PagingSpec pagingSpec)
{
return _blocklistRepository.GetPaged(pagingSpec);
diff --git a/src/NzbDrone.Core/Configuration/ConfigFileProvider.cs b/src/NzbDrone.Core/Configuration/ConfigFileProvider.cs
index e48f9c245..d2cdd6fed 100644
--- a/src/NzbDrone.Core/Configuration/ConfigFileProvider.cs
+++ b/src/NzbDrone.Core/Configuration/ConfigFileProvider.cs
@@ -339,6 +339,20 @@ namespace NzbDrone.Core.Configuration
}
}
+ public void MigrateConfigFile()
+ {
+ if (!File.Exists(_configFile))
+ {
+ return;
+ }
+
+ // If SSL is enabled and a cert hash is still in the config file disable SSL
+ if (EnableSsl && GetValue("SslCertHash", null).IsNotNullOrWhiteSpace())
+ {
+ SetValue("EnableSsl", false);
+ }
+ }
+
private void DeleteOldValues()
{
var xDoc = LoadConfigFile();
@@ -410,6 +424,7 @@ namespace NzbDrone.Core.Configuration
public void HandleAsync(ApplicationStartedEvent message)
{
+ MigrateConfigFile();
EnsureDefaultConfigFile();
DeleteOldValues();
}
diff --git a/src/NzbDrone.Core/Configuration/ConfigService.cs b/src/NzbDrone.Core/Configuration/ConfigService.cs
index 85186e91b..2193b182b 100644
--- a/src/NzbDrone.Core/Configuration/ConfigService.cs
+++ b/src/NzbDrone.Core/Configuration/ConfigService.cs
@@ -144,6 +144,13 @@ namespace NzbDrone.Core.Configuration
set { SetValue("AutoRedownloadFailed", value); }
}
+ public bool AutoRedownloadFailedFromInteractiveSearch
+ {
+ get { return GetValueBoolean("AutoRedownloadFailedFromInteractiveSearch", true); }
+
+ set { SetValue("AutoRedownloadFailedFromInteractiveSearch", value); }
+ }
+
public bool CreateEmptySeriesFolders
{
get { return GetValueBoolean("CreateEmptySeriesFolders", false); }
diff --git a/src/NzbDrone.Core/Configuration/IConfigService.cs b/src/NzbDrone.Core/Configuration/IConfigService.cs
index 2ebc262ac..2bcd7b923 100644
--- a/src/NzbDrone.Core/Configuration/IConfigService.cs
+++ b/src/NzbDrone.Core/Configuration/IConfigService.cs
@@ -20,6 +20,7 @@ namespace NzbDrone.Core.Configuration
// Completed/Failed Download Handling (Download client)
bool EnableCompletedDownloadHandling { get; set; }
bool AutoRedownloadFailed { get; set; }
+ bool AutoRedownloadFailedFromInteractiveSearch { get; set; }
// Media Management
bool AutoUnmonitorPreviouslyDownloadedEpisodes { get; set; }
diff --git a/src/NzbDrone.Core/CustomFormats/CustomFormatCalculationService.cs b/src/NzbDrone.Core/CustomFormats/CustomFormatCalculationService.cs
index 18adc70cd..e344c355f 100644
--- a/src/NzbDrone.Core/CustomFormats/CustomFormatCalculationService.cs
+++ b/src/NzbDrone.Core/CustomFormats/CustomFormatCalculationService.cs
@@ -150,7 +150,7 @@ namespace NzbDrone.Core.CustomFormats
}
}
- return matches;
+ return matches.OrderBy(x => x.Name).ToList();
}
private static List ParseCustomFormat(EpisodeFile episodeFile, Series series, List allCustomFormats)
diff --git a/src/NzbDrone.Core/Datastore/BasicRepository.cs b/src/NzbDrone.Core/Datastore/BasicRepository.cs
index b5e57be87..a7da9eb38 100644
--- a/src/NzbDrone.Core/Datastore/BasicRepository.cs
+++ b/src/NzbDrone.Core/Datastore/BasicRepository.cs
@@ -407,7 +407,7 @@ namespace NzbDrone.Core.Datastore
return pagingSpec;
}
- private void AddFilters(SqlBuilder builder, PagingSpec pagingSpec)
+ protected void AddFilters(SqlBuilder builder, PagingSpec pagingSpec)
{
var filters = pagingSpec.FilterExpressions;
diff --git a/src/NzbDrone.Core/Datastore/ConnectionStringFactory.cs b/src/NzbDrone.Core/Datastore/ConnectionStringFactory.cs
index 069b944fb..19c938737 100644
--- a/src/NzbDrone.Core/Datastore/ConnectionStringFactory.cs
+++ b/src/NzbDrone.Core/Datastore/ConnectionStringFactory.cs
@@ -44,11 +44,12 @@ namespace NzbDrone.Core.Datastore
var connectionBuilder = new SQLiteConnectionStringBuilder
{
DataSource = dbPath,
- CacheSize = (int)-10000,
+ CacheSize = (int)-20000,
DateTimeKind = DateTimeKind.Utc,
JournalMode = OsInfo.IsOsx ? SQLiteJournalModeEnum.Truncate : SQLiteJournalModeEnum.Wal,
Pooling = true,
- Version = 3
+ Version = 3,
+ BusyTimeout = 100
};
if (OsInfo.IsOsx)
diff --git a/src/NzbDrone.Core/Datastore/Migration/195_parse_language_tags_from_existing_subtitle_files.cs b/src/NzbDrone.Core/Datastore/Migration/195_parse_language_tags_from_existing_subtitle_files.cs
index b49bfaae5..934b2b6da 100644
--- a/src/NzbDrone.Core/Datastore/Migration/195_parse_language_tags_from_existing_subtitle_files.cs
+++ b/src/NzbDrone.Core/Datastore/Migration/195_parse_language_tags_from_existing_subtitle_files.cs
@@ -1,8 +1,5 @@
-using System;
using System.Collections.Generic;
using System.Data;
-using System.Text.Json;
-using System.Text.Json.Serialization;
using Dapper;
using FluentMigrator;
using NzbDrone.Core.Datastore.Migration.Framework;
@@ -21,7 +18,6 @@ namespace NzbDrone.Core.Datastore.Migration
private void UpdateLanguageTags(IDbConnection conn, IDbTransaction tran)
{
var updatedLanguageTags = new List