mirror of
https://github.com/Radarr/Radarr.git
synced 2026-04-17 21:26:22 -04:00
Fixed: Backend/Frontend Cleanup
This commit is contained in:
Binary file not shown.
Binary file not shown.
@@ -2,6 +2,7 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Threading;
|
||||
using FluentAssertions;
|
||||
@@ -24,18 +25,64 @@ namespace NzbDrone.Common.Test.Http
|
||||
[TestFixture(typeof(CurlHttpDispatcher))]
|
||||
public class HttpClientFixture<TDispatcher> : TestBase<HttpClient> where TDispatcher : IHttpDispatcher
|
||||
{
|
||||
private static string[] _httpBinHosts = new[] { "eu.httpbin.org", "httpbin.org" };
|
||||
private static int _httpBinRandom;
|
||||
private string[] _httpBinHosts;
|
||||
private int _httpBinSleep;
|
||||
private int _httpBinRandom;
|
||||
private string _httpBinHost;
|
||||
private string _httpBinHost2;
|
||||
|
||||
[OneTimeSetUp]
|
||||
public void FixtureSetUp()
|
||||
{
|
||||
var candidates = new[] { "eu.httpbin.org", /*"httpbin.org",*/ "www.httpbin.org" };
|
||||
// httpbin.org is broken right now, occassionally redirecting to https if it's unavailable.
|
||||
_httpBinHosts = candidates.Where(IsTestSiteAvailable).ToArray();
|
||||
|
||||
TestLogger.Info($"{candidates.Length} TestSites available.");
|
||||
|
||||
_httpBinSleep = _httpBinHosts.Count() < 2 ? 100 : 10;
|
||||
}
|
||||
|
||||
private bool IsTestSiteAvailable(string site)
|
||||
{
|
||||
try
|
||||
{
|
||||
var req = WebRequest.Create($"http://{site}/get") as HttpWebRequest;
|
||||
var res = req.GetResponse() as HttpWebResponse;
|
||||
if (res.StatusCode != HttpStatusCode.OK) return false;
|
||||
|
||||
try
|
||||
{
|
||||
req = WebRequest.Create($"http://{site}/status/429") as HttpWebRequest;
|
||||
res = req.GetResponse() as HttpWebResponse;
|
||||
}
|
||||
catch (WebException ex)
|
||||
{
|
||||
res = ex.Response as HttpWebResponse;
|
||||
}
|
||||
|
||||
if (res == null || res.StatusCode != (HttpStatusCode)429) return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
[SetUp]
|
||||
public void SetUp()
|
||||
{
|
||||
if (!_httpBinHosts.Any())
|
||||
{
|
||||
Assert.Inconclusive("No TestSites available");
|
||||
}
|
||||
|
||||
Mocker.GetMock<IPlatformInfo>().Setup(c => c.Version).Returns(new Version("1.0.0"));
|
||||
Mocker.GetMock<IOsInfo>().Setup(c => c.Name).Returns("TestOS");
|
||||
Mocker.GetMock<IOsInfo>().Setup(c => c.Version).Returns("9.0.0");
|
||||
|
||||
|
||||
Mocker.SetConstant<IUserAgentBuilder>(Mocker.Resolve<UserAgentBuilder>());
|
||||
|
||||
Mocker.SetConstant<ICacheManager>(Mocker.Resolve<CacheManager>());
|
||||
@@ -51,6 +98,13 @@ namespace NzbDrone.Common.Test.Http
|
||||
|
||||
// Roundrobin over the two servers, to reduce the chance of hitting the ratelimiter.
|
||||
_httpBinHost = _httpBinHosts[_httpBinRandom++ % _httpBinHosts.Length];
|
||||
_httpBinHost2 = _httpBinHosts[_httpBinRandom % _httpBinHosts.Length];
|
||||
}
|
||||
|
||||
[TearDown]
|
||||
public void TearDown()
|
||||
{
|
||||
Thread.Sleep(_httpBinSleep);
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -76,11 +130,12 @@ namespace NzbDrone.Common.Test.Http
|
||||
[Test]
|
||||
public void should_execute_typed_get()
|
||||
{
|
||||
var request = new HttpRequest($"http://{_httpBinHost}/get");
|
||||
var request = new HttpRequest($"http://{_httpBinHost}/get?test=1");
|
||||
|
||||
var response = Subject.Get<HttpBinResource>(request);
|
||||
|
||||
response.Resource.Url.Should().Be(request.Url.FullUri);
|
||||
response.Resource.Url.EndsWith("/get?test=1");
|
||||
response.Resource.Args.Should().Contain("test", "1");
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -163,6 +218,11 @@ namespace NzbDrone.Common.Test.Http
|
||||
[Test]
|
||||
public void should_follow_redirects_to_https()
|
||||
{
|
||||
if (typeof(TDispatcher) == typeof(ManagedHttpDispatcher) && PlatformInfo.IsMono)
|
||||
{
|
||||
Assert.Ignore("Will fail on tls1.2 via managed dispatcher, ignore.");
|
||||
}
|
||||
|
||||
var request = new HttpRequestBuilder($"http://{_httpBinHost}/redirect-to")
|
||||
.AddQueryParam("url", $"https://sonarr.tv/")
|
||||
.Build();
|
||||
@@ -241,7 +301,12 @@ namespace NzbDrone.Common.Test.Http
|
||||
|
||||
public void GivenOldCookie()
|
||||
{
|
||||
var oldRequest = new HttpRequest("http://eu.httpbin.org/get");
|
||||
if (_httpBinHost == _httpBinHost2)
|
||||
{
|
||||
Assert.Inconclusive("Need both httpbin.org and eu.httpbin.org to run this test.");
|
||||
}
|
||||
|
||||
var oldRequest = new HttpRequest($"http://{_httpBinHost2}/get");
|
||||
oldRequest.Cookies["my"] = "cookie";
|
||||
|
||||
var oldClient = new HttpClient(new IHttpRequestInterceptor[0], Mocker.Resolve<ICacheManager>(), Mocker.Resolve<IRateLimitService>(), Mocker.Resolve<IHttpDispatcher>(), Mocker.GetMock<IUserAgentBuilder>().Object, Mocker.Resolve<Logger>());
|
||||
@@ -258,7 +323,7 @@ namespace NzbDrone.Common.Test.Http
|
||||
{
|
||||
GivenOldCookie();
|
||||
|
||||
var request = new HttpRequest("http://eu.httpbin.org/get");
|
||||
var request = new HttpRequest($"http://{_httpBinHost2}/get");
|
||||
|
||||
var response = Subject.Get<HttpBinResource>(request);
|
||||
|
||||
@@ -274,7 +339,7 @@ namespace NzbDrone.Common.Test.Http
|
||||
{
|
||||
GivenOldCookie();
|
||||
|
||||
var request = new HttpRequest("http://httpbin.org/get");
|
||||
var request = new HttpRequest($"http://{_httpBinHost}/get");
|
||||
|
||||
var response = Subject.Get<HttpBinResource>(request);
|
||||
|
||||
@@ -334,6 +399,28 @@ namespace NzbDrone.Common.Test.Http
|
||||
responseCookies.Resource.Cookies.Should().BeEmpty();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_clear_request_cookie()
|
||||
{
|
||||
var requestSet = new HttpRequest($"http://{_httpBinHost}/cookies");
|
||||
requestSet.Cookies.Add("my", "cookie");
|
||||
requestSet.AllowAutoRedirect = false;
|
||||
requestSet.StoreRequestCookie = true;
|
||||
requestSet.StoreResponseCookie = false;
|
||||
|
||||
var responseSet = Subject.Get<HttpCookieResource>(requestSet);
|
||||
|
||||
var requestClear = new HttpRequest($"http://{_httpBinHost}/cookies");
|
||||
requestClear.Cookies.Add("my", null);
|
||||
requestClear.AllowAutoRedirect = false;
|
||||
requestClear.StoreRequestCookie = true;
|
||||
requestClear.StoreResponseCookie = false;
|
||||
|
||||
var responseClear = Subject.Get<HttpCookieResource>(requestClear);
|
||||
|
||||
responseClear.Resource.Cookies.Should().BeEmpty();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_not_store_response_cookie()
|
||||
{
|
||||
@@ -518,20 +605,6 @@ namespace NzbDrone.Common.Test.Http
|
||||
ExceptionVerification.IgnoreErrors();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_not_send_old_cookie()
|
||||
{
|
||||
GivenOldCookie();
|
||||
|
||||
var requestCookies = new HttpRequest($"http://{_httpBinHost}/cookies");
|
||||
requestCookies.IgnorePersistentCookies = true;
|
||||
requestCookies.StoreRequestCookie = false;
|
||||
requestCookies.StoreResponseCookie = false;
|
||||
var responseCookies = Subject.Get<HttpCookieResource>(requestCookies);
|
||||
|
||||
responseCookies.Resource.Cookies.Should().BeEmpty();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_throw_on_http429_too_many_requests()
|
||||
{
|
||||
@@ -610,8 +683,7 @@ namespace NzbDrone.Common.Test.Http
|
||||
{
|
||||
try
|
||||
{
|
||||
string url =
|
||||
$"http://{_httpBinHost}/response-headers?Set-Cookie={Uri.EscapeUriString(malformedCookie)}";
|
||||
string url = $"http://{_httpBinHost}/response-headers?Set-Cookie={Uri.EscapeUriString(malformedCookie)}";
|
||||
|
||||
var requestSet = new HttpRequest(url);
|
||||
requestSet.AllowAutoRedirect = false;
|
||||
@@ -635,6 +707,7 @@ namespace NzbDrone.Common.Test.Http
|
||||
|
||||
public class HttpBinResource
|
||||
{
|
||||
public Dictionary<string, object> Args { get; set; }
|
||||
public Dictionary<string, object> Headers { get; set; }
|
||||
public string Origin { get; set; }
|
||||
public string Url { get; set; }
|
||||
|
||||
@@ -21,13 +21,13 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.QBittorrentTests
|
||||
{
|
||||
Subject.Definition = new DownloadClientDefinition();
|
||||
Subject.Definition.Settings = new QBittorrentSettings
|
||||
{
|
||||
Host = "127.0.0.1",
|
||||
Port = 2222,
|
||||
Username = "admin",
|
||||
Password = "pass",
|
||||
MovieCategory = "movies-radarr"
|
||||
};
|
||||
{
|
||||
Host = "127.0.0.1",
|
||||
Port = 2222,
|
||||
Username = "admin",
|
||||
Password = "pass",
|
||||
MovieCategory = "movies-radarr"
|
||||
};
|
||||
|
||||
Mocker.GetMock<ITorrentFileInfoReader>()
|
||||
.Setup(s => s.GetHashFromTorrentFile(It.IsAny<Byte[]>()))
|
||||
|
||||
@@ -4,11 +4,9 @@ using FluentAssertions;
|
||||
using NUnit.Framework;
|
||||
using NzbDrone.Common.Extensions;
|
||||
using NzbDrone.Core.Indexers;
|
||||
using NzbDrone.Core.Indexers.Nyaa;
|
||||
using NzbDrone.Core.IndexerSearch.Definitions;
|
||||
using NzbDrone.Core.Parser.Model;
|
||||
using NzbDrone.Core.Test.Framework;
|
||||
using NzbDrone.Core.ThingiProvider;
|
||||
using NzbDrone.Test.Common.Categories;
|
||||
|
||||
namespace NzbDrone.Core.Test.IndexerTests.IntegrationTests
|
||||
@@ -30,40 +28,6 @@ namespace NzbDrone.Core.Test.IndexerTests.IntegrationTests
|
||||
};
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void nyaa_fetch_recent()
|
||||
{
|
||||
var indexer = Mocker.Resolve<Nyaa>();
|
||||
|
||||
indexer.Definition = new IndexerDefinition
|
||||
{
|
||||
Name = "MyIndexer",
|
||||
Settings = new NyaaSettings()
|
||||
};
|
||||
|
||||
var result = indexer.FetchRecent();
|
||||
|
||||
ValidateTorrentResult(result, hasSize: true);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void nyaa_search_single()
|
||||
{
|
||||
var indexer = Mocker.Resolve<Nyaa>();
|
||||
|
||||
indexer.Definition = new IndexerDefinition
|
||||
{
|
||||
Name = "MyIndexer",
|
||||
Settings = new NyaaSettings()
|
||||
};
|
||||
|
||||
var result = indexer.Fetch(_singleSearchCriteria);
|
||||
|
||||
ValidateTorrentResult(result, hasSize: true);
|
||||
}
|
||||
|
||||
|
||||
|
||||
private void ValidateTorrentResult(IList<ReleaseInfo> reports, bool hasSize = false, bool hasInfoUrl = false, bool hasMagnet = false)
|
||||
{
|
||||
reports.Should().OnlyContain(c => c.GetType() == typeof(TorrentInfo));
|
||||
|
||||
@@ -1,145 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using FizzWare.NBuilder;
|
||||
using Moq;
|
||||
using NUnit.Framework;
|
||||
using NzbDrone.Common.Disk;
|
||||
using NzbDrone.Core.Download;
|
||||
using NzbDrone.Core.Download.TrackedDownloads;
|
||||
using NzbDrone.Core.MediaFiles;
|
||||
using NzbDrone.Core.MediaFiles.Commands;
|
||||
using NzbDrone.Core.MediaFiles.EpisodeImport;
|
||||
using NzbDrone.Core.Parser.Model;
|
||||
using NzbDrone.Core.Test.Framework;
|
||||
using NzbDrone.Core.Tv;
|
||||
using NzbDrone.Test.Common;
|
||||
|
||||
namespace NzbDrone.Core.Test.MediaFiles
|
||||
{
|
||||
[TestFixture]
|
||||
public class DownloadedEpisodesCommandServiceFixture : CoreTest<DownloadedEpisodesCommandService>
|
||||
{
|
||||
private string _downloadFolder = "c:\\drop_other\\Show.S01E01\\".AsOsAgnostic();
|
||||
private string _downloadFile = "c:\\drop_other\\Show.S01E01.mkv".AsOsAgnostic();
|
||||
|
||||
private TrackedDownload _trackedDownload;
|
||||
|
||||
[SetUp]
|
||||
public void Setup()
|
||||
{
|
||||
Mocker.GetMock<IDownloadedEpisodesImportService>()
|
||||
.Setup(v => v.ProcessRootFolder(It.IsAny<DirectoryInfo>()))
|
||||
.Returns(new List<ImportResult>());
|
||||
|
||||
Mocker.GetMock<IDownloadedEpisodesImportService>()
|
||||
.Setup(v => v.ProcessPath(It.IsAny<string>(), It.IsAny<ImportMode>(), It.IsAny<Series>(), It.IsAny<DownloadClientItem>()))
|
||||
.Returns(new List<ImportResult>());
|
||||
|
||||
var downloadItem = Builder<DownloadClientItem>.CreateNew()
|
||||
.With(v => v.DownloadId = "sab1")
|
||||
.With(v => v.Status = DownloadItemStatus.Downloading)
|
||||
.Build();
|
||||
|
||||
var remoteEpisode = Builder<RemoteEpisode>.CreateNew()
|
||||
.With(v => v.Series = new Series())
|
||||
.Build();
|
||||
|
||||
_trackedDownload = new TrackedDownload
|
||||
{
|
||||
DownloadItem = downloadItem,
|
||||
RemoteEpisode = remoteEpisode,
|
||||
State = TrackedDownloadStage.Downloading
|
||||
};
|
||||
}
|
||||
|
||||
private void GivenExistingFolder(string path)
|
||||
{
|
||||
Mocker.GetMock<IDiskProvider>().Setup(c => c.FolderExists(It.IsAny<string>()))
|
||||
.Returns(true);
|
||||
}
|
||||
|
||||
private void GivenExistingFile(string path)
|
||||
{
|
||||
Mocker.GetMock<IDiskProvider>().Setup(c => c.FileExists(It.IsAny<string>()))
|
||||
.Returns(true);
|
||||
}
|
||||
|
||||
private void GivenValidQueueItem()
|
||||
{
|
||||
Mocker.GetMock<ITrackedDownloadService>()
|
||||
.Setup(s => s.Find("sab1"))
|
||||
.Returns(_trackedDownload);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_skip_import_if_dronefactory_doesnt_exist()
|
||||
{
|
||||
Assert.Throws<ArgumentException>(() => Subject.Execute(new DownloadedEpisodesScanCommand()));
|
||||
|
||||
Mocker.GetMock<IDownloadedEpisodesImportService>().Verify(c => c.ProcessRootFolder(It.IsAny<DirectoryInfo>()), Times.Never());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_process_folder_if_downloadclientid_is_not_specified()
|
||||
{
|
||||
GivenExistingFolder(_downloadFolder);
|
||||
|
||||
Subject.Execute(new DownloadedEpisodesScanCommand() { Path = _downloadFolder });
|
||||
|
||||
Mocker.GetMock<IDownloadedEpisodesImportService>().Verify(c => c.ProcessPath(It.IsAny<string>(), ImportMode.Auto, null, null), Times.Once());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_process_file_if_downloadclientid_is_not_specified()
|
||||
{
|
||||
GivenExistingFile(_downloadFile);
|
||||
|
||||
Subject.Execute(new DownloadedEpisodesScanCommand() { Path = _downloadFile });
|
||||
|
||||
Mocker.GetMock<IDownloadedEpisodesImportService>().Verify(c => c.ProcessPath(It.IsAny<string>(), ImportMode.Auto, null, null), Times.Once());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_process_folder_with_downloadclientitem_if_available()
|
||||
{
|
||||
GivenExistingFolder(_downloadFolder);
|
||||
GivenValidQueueItem();
|
||||
|
||||
Subject.Execute(new DownloadedEpisodesScanCommand() { Path = _downloadFolder, DownloadClientId = "sab1" });
|
||||
|
||||
Mocker.GetMock<IDownloadedEpisodesImportService>().Verify(c => c.ProcessPath(_downloadFolder, ImportMode.Auto, _trackedDownload.RemoteEpisode.Series, _trackedDownload.DownloadItem), Times.Once());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_process_folder_without_downloadclientitem_if_not_available()
|
||||
{
|
||||
GivenExistingFolder(_downloadFolder);
|
||||
|
||||
Subject.Execute(new DownloadedEpisodesScanCommand() { Path = _downloadFolder, DownloadClientId = "sab1" });
|
||||
|
||||
Mocker.GetMock<IDownloadedEpisodesImportService>().Verify(c => c.ProcessPath(_downloadFolder, ImportMode.Auto, null, null), Times.Once());
|
||||
|
||||
ExceptionVerification.ExpectedWarns(1);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_warn_if_neither_folder_or_file_exists()
|
||||
{
|
||||
Subject.Execute(new DownloadedEpisodesScanCommand() { Path = _downloadFolder });
|
||||
|
||||
Mocker.GetMock<IDownloadedEpisodesImportService>().Verify(c => c.ProcessPath(It.IsAny<string>(), ImportMode.Auto, null, null), Times.Never());
|
||||
|
||||
ExceptionVerification.ExpectedWarns(1);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_override_import_mode()
|
||||
{
|
||||
GivenExistingFile(_downloadFile);
|
||||
|
||||
Subject.Execute(new DownloadedEpisodesScanCommand() { Path = _downloadFile, ImportMode = ImportMode.Copy });
|
||||
|
||||
Mocker.GetMock<IDownloadedEpisodesImportService>().Verify(c => c.ProcessPath(It.IsAny<string>(), ImportMode.Copy, null, null), Times.Once());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,377 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using FizzWare.NBuilder;
|
||||
using Moq;
|
||||
using NUnit.Framework;
|
||||
using NzbDrone.Common.Disk;
|
||||
using NzbDrone.Core.MediaFiles;
|
||||
using NzbDrone.Core.MediaFiles.EpisodeImport;
|
||||
using NzbDrone.Core.Parser;
|
||||
using NzbDrone.Core.Parser.Model;
|
||||
using NzbDrone.Core.Qualities;
|
||||
using NzbDrone.Core.Test.Framework;
|
||||
using NzbDrone.Core.Tv;
|
||||
using NzbDrone.Test.Common;
|
||||
using FluentAssertions;
|
||||
|
||||
namespace NzbDrone.Core.Test.MediaFiles
|
||||
{
|
||||
[TestFixture]
|
||||
public class DownloadedEpisodesImportServiceFixture : CoreTest<DownloadedEpisodesImportService>
|
||||
{
|
||||
private string _droneFactory = "c:\\drop\\".AsOsAgnostic();
|
||||
private string[] _subFolders = new[] { "c:\\root\\foldername".AsOsAgnostic() };
|
||||
private string[] _videoFiles = new[] { "c:\\root\\foldername\\30.rock.s01e01.ext".AsOsAgnostic() };
|
||||
|
||||
[SetUp]
|
||||
public void Setup()
|
||||
{
|
||||
Mocker.GetMock<IDiskScanService>().Setup(c => c.GetVideoFiles(It.IsAny<string>(), It.IsAny<bool>()))
|
||||
.Returns(_videoFiles);
|
||||
|
||||
Mocker.GetMock<IDiskProvider>().Setup(c => c.GetDirectories(It.IsAny<string>()))
|
||||
.Returns(_subFolders);
|
||||
|
||||
Mocker.GetMock<IDiskProvider>().Setup(c => c.FolderExists(It.IsAny<string>()))
|
||||
.Returns(true);
|
||||
|
||||
Mocker.GetMock<IImportApprovedEpisodes>()
|
||||
.Setup(s => s.Import(It.IsAny<List<ImportDecision>>(), true, null, ImportMode.Auto))
|
||||
.Returns(new List<ImportResult>());
|
||||
}
|
||||
|
||||
private void GivenValidSeries()
|
||||
{
|
||||
Mocker.GetMock<IParsingService>()
|
||||
.Setup(s => s.GetSeries(It.IsAny<string>()))
|
||||
.Returns(Builder<Series>.CreateNew().Build());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_search_for_series_using_folder_name()
|
||||
{
|
||||
Subject.ProcessRootFolder(new DirectoryInfo(_droneFactory));
|
||||
|
||||
Mocker.GetMock<IParsingService>().Verify(c => c.GetSeries("foldername"), Times.Once());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_skip_if_file_is_in_use_by_another_process()
|
||||
{
|
||||
GivenValidSeries();
|
||||
|
||||
Mocker.GetMock<IDiskProvider>().Setup(c => c.IsFileLocked(It.IsAny<string>()))
|
||||
.Returns(true);
|
||||
|
||||
Subject.ProcessRootFolder(new DirectoryInfo(_droneFactory));
|
||||
|
||||
VerifyNoImport();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_skip_if_no_series_found()
|
||||
{
|
||||
Mocker.GetMock<IParsingService>().Setup(c => c.GetSeries("foldername")).Returns((Series)null);
|
||||
|
||||
Subject.ProcessRootFolder(new DirectoryInfo(_droneFactory));
|
||||
|
||||
Mocker.GetMock<IMakeImportDecision>()
|
||||
.Verify(c => c.GetImportDecisions(It.IsAny<List<string>>(), It.IsAny<Series>(), It.IsAny<ParsedEpisodeInfo>(), It.IsAny<bool>()),
|
||||
Times.Never());
|
||||
|
||||
VerifyNoImport();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_not_import_if_folder_is_a_series_path()
|
||||
{
|
||||
GivenValidSeries();
|
||||
|
||||
Mocker.GetMock<ISeriesService>()
|
||||
.Setup(s => s.SeriesPathExists(It.IsAny<string>()))
|
||||
.Returns(true);
|
||||
|
||||
Mocker.GetMock<IDiskScanService>()
|
||||
.Setup(c => c.GetVideoFiles(It.IsAny<string>(), It.IsAny<bool>()))
|
||||
.Returns(new string[0]);
|
||||
|
||||
Subject.ProcessRootFolder(new DirectoryInfo(_droneFactory));
|
||||
|
||||
Mocker.GetMock<IDiskScanService>()
|
||||
.Verify(v => v.GetVideoFiles(It.IsAny<string>(), true), Times.Never());
|
||||
|
||||
ExceptionVerification.ExpectedWarns(1);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_not_delete_folder_if_no_files_were_imported()
|
||||
{
|
||||
Mocker.GetMock<IImportApprovedEpisodes>()
|
||||
.Setup(s => s.Import(It.IsAny<List<ImportDecision>>(), false, null, ImportMode.Auto))
|
||||
.Returns(new List<ImportResult>());
|
||||
|
||||
Subject.ProcessRootFolder(new DirectoryInfo(_droneFactory));
|
||||
|
||||
Mocker.GetMock<IDiskProvider>()
|
||||
.Verify(v => v.GetFolderSize(It.IsAny<string>()), Times.Never());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_not_delete_folder_if_files_were_imported_and_video_files_remain()
|
||||
{
|
||||
GivenValidSeries();
|
||||
|
||||
var localEpisode = new LocalEpisode();
|
||||
|
||||
var imported = new List<ImportDecision>();
|
||||
imported.Add(new ImportDecision(localEpisode));
|
||||
|
||||
Mocker.GetMock<IMakeImportDecision>()
|
||||
.Setup(s => s.GetImportDecisions(It.IsAny<List<string>>(), It.IsAny<Series>(), null, true))
|
||||
.Returns(imported);
|
||||
|
||||
Mocker.GetMock<IImportApprovedEpisodes>()
|
||||
.Setup(s => s.Import(It.IsAny<List<ImportDecision>>(), true, null, ImportMode.Auto))
|
||||
.Returns(imported.Select(i => new ImportResult(i)).ToList());
|
||||
|
||||
Subject.ProcessRootFolder(new DirectoryInfo(_droneFactory));
|
||||
|
||||
Mocker.GetMock<IDiskProvider>()
|
||||
.Verify(v => v.DeleteFolder(It.IsAny<string>(), true), Times.Never());
|
||||
|
||||
ExceptionVerification.ExpectedWarns(1);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_delete_folder_if_files_were_imported_and_only_sample_files_remain()
|
||||
{
|
||||
GivenValidSeries();
|
||||
|
||||
var localEpisode = new LocalEpisode();
|
||||
|
||||
var imported = new List<ImportDecision>();
|
||||
imported.Add(new ImportDecision(localEpisode));
|
||||
|
||||
Mocker.GetMock<IMakeImportDecision>()
|
||||
.Setup(s => s.GetImportDecisions(It.IsAny<List<string>>(), It.IsAny<Series>(), null, true))
|
||||
.Returns(imported);
|
||||
|
||||
Mocker.GetMock<IImportApprovedEpisodes>()
|
||||
.Setup(s => s.Import(It.IsAny<List<ImportDecision>>(), true, null, ImportMode.Auto))
|
||||
.Returns(imported.Select(i => new ImportResult(i)).ToList());
|
||||
|
||||
Mocker.GetMock<IDetectSample>()
|
||||
.Setup(s => s.IsSample(It.IsAny<Series>(),
|
||||
It.IsAny<QualityModel>(),
|
||||
It.IsAny<string>(),
|
||||
It.IsAny<long>(),
|
||||
It.IsAny<bool>()))
|
||||
.Returns(true);
|
||||
|
||||
Subject.ProcessRootFolder(new DirectoryInfo(_droneFactory));
|
||||
|
||||
Mocker.GetMock<IDiskProvider>()
|
||||
.Verify(v => v.DeleteFolder(It.IsAny<string>(), true), Times.Once());
|
||||
}
|
||||
|
||||
[TestCase("_UNPACK_")]
|
||||
[TestCase("_FAILED_")]
|
||||
public void should_remove_unpack_from_folder_name(string prefix)
|
||||
{
|
||||
var folderName = "30.rock.s01e01.pilot.hdtv-lol";
|
||||
var folders = new[] { string.Format(@"C:\Test\Unsorted\{0}{1}", prefix, folderName).AsOsAgnostic() };
|
||||
|
||||
Mocker.GetMock<IDiskProvider>()
|
||||
.Setup(c => c.GetDirectories(It.IsAny<string>()))
|
||||
.Returns(folders);
|
||||
|
||||
Subject.ProcessRootFolder(new DirectoryInfo(_droneFactory));
|
||||
|
||||
Mocker.GetMock<IParsingService>()
|
||||
.Verify(v => v.GetSeries(folderName), Times.Once());
|
||||
|
||||
Mocker.GetMock<IParsingService>()
|
||||
.Verify(v => v.GetSeries(It.Is<string>(s => s.StartsWith(prefix))), Times.Never());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_return_importresult_on_unknown_series()
|
||||
{
|
||||
Mocker.GetMock<IDiskProvider>().Setup(c => c.FolderExists(It.IsAny<string>()))
|
||||
.Returns(false);
|
||||
|
||||
Mocker.GetMock<IDiskProvider>().Setup(c => c.FileExists(It.IsAny<string>()))
|
||||
.Returns(true);
|
||||
|
||||
var fileName = @"C:\folder\file.mkv".AsOsAgnostic();
|
||||
|
||||
var result = Subject.ProcessPath(fileName);
|
||||
|
||||
result.Should().HaveCount(1);
|
||||
result.First().ImportDecision.Should().NotBeNull();
|
||||
result.First().ImportDecision.LocalEpisode.Should().NotBeNull();
|
||||
result.First().ImportDecision.LocalEpisode.Path.Should().Be(fileName);
|
||||
result.First().Result.Should().Be(ImportResultType.Rejected);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_not_delete_if_there_is_large_rar_file()
|
||||
{
|
||||
GivenValidSeries();
|
||||
|
||||
var localEpisode = new LocalEpisode();
|
||||
|
||||
var imported = new List<ImportDecision>();
|
||||
imported.Add(new ImportDecision(localEpisode));
|
||||
|
||||
Mocker.GetMock<IMakeImportDecision>()
|
||||
.Setup(s => s.GetImportDecisions(It.IsAny<List<string>>(), It.IsAny<Series>(), null, true))
|
||||
.Returns(imported);
|
||||
|
||||
Mocker.GetMock<IImportApprovedEpisodes>()
|
||||
.Setup(s => s.Import(It.IsAny<List<ImportDecision>>(), true, null, ImportMode.Auto))
|
||||
.Returns(imported.Select(i => new ImportResult(i)).ToList());
|
||||
|
||||
Mocker.GetMock<IDetectSample>()
|
||||
.Setup(s => s.IsSample(It.IsAny<Series>(),
|
||||
It.IsAny<QualityModel>(),
|
||||
It.IsAny<string>(),
|
||||
It.IsAny<long>(),
|
||||
It.IsAny<bool>()))
|
||||
.Returns(true);
|
||||
|
||||
Mocker.GetMock<IDiskProvider>()
|
||||
.Setup(s => s.GetFiles(It.IsAny<string>(), SearchOption.AllDirectories))
|
||||
.Returns(new []{ _videoFiles.First().Replace(".ext", ".rar") });
|
||||
|
||||
Mocker.GetMock<IDiskProvider>()
|
||||
.Setup(s => s.GetFileSize(It.IsAny<string>()))
|
||||
.Returns(15.Megabytes());
|
||||
|
||||
Subject.ProcessRootFolder(new DirectoryInfo(_droneFactory));
|
||||
|
||||
Mocker.GetMock<IDiskProvider>()
|
||||
.Verify(v => v.DeleteFolder(It.IsAny<string>(), true), Times.Never());
|
||||
|
||||
ExceptionVerification.ExpectedWarns(1);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_use_folder_if_folder_import()
|
||||
{
|
||||
GivenValidSeries();
|
||||
|
||||
var folderName = @"C:\media\ba09030e-1234-1234-1234-123456789abc\[HorribleSubs] Maria the Virgin Witch - 09 [720p]".AsOsAgnostic();
|
||||
var fileName = @"C:\media\ba09030e-1234-1234-1234-123456789abc\[HorribleSubs] Maria the Virgin Witch - 09 [720p]\[HorribleSubs] Maria the Virgin Witch - 09 [720p].mkv".AsOsAgnostic();
|
||||
|
||||
Mocker.GetMock<IDiskProvider>().Setup(c => c.FolderExists(folderName))
|
||||
.Returns(true);
|
||||
|
||||
Mocker.GetMock<IDiskProvider>().Setup(c => c.GetFiles(folderName, SearchOption.TopDirectoryOnly))
|
||||
.Returns(new[] { fileName });
|
||||
|
||||
var localEpisode = new LocalEpisode();
|
||||
|
||||
var imported = new List<ImportDecision>();
|
||||
imported.Add(new ImportDecision(localEpisode));
|
||||
|
||||
|
||||
Subject.ProcessPath(fileName);
|
||||
|
||||
Mocker.GetMock<IMakeImportDecision>()
|
||||
.Verify(s => s.GetImportDecisions(It.IsAny<List<string>>(), It.IsAny<Series>(), It.Is<ParsedEpisodeInfo>(v => v.AbsoluteEpisodeNumbers.First() == 9), true), Times.Once());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_not_use_folder_if_file_import()
|
||||
{
|
||||
GivenValidSeries();
|
||||
|
||||
var fileName = @"C:\media\ba09030e-1234-1234-1234-123456789abc\Torrents\[HorribleSubs] Maria the Virgin Witch - 09 [720p].mkv".AsOsAgnostic();
|
||||
|
||||
Mocker.GetMock<IDiskProvider>().Setup(c => c.FolderExists(fileName))
|
||||
.Returns(false);
|
||||
|
||||
Mocker.GetMock<IDiskProvider>().Setup(c => c.FileExists(fileName))
|
||||
.Returns(true);
|
||||
|
||||
var localEpisode = new LocalEpisode();
|
||||
|
||||
var imported = new List<ImportDecision>();
|
||||
imported.Add(new ImportDecision(localEpisode));
|
||||
|
||||
var result = Subject.ProcessPath(fileName);
|
||||
|
||||
Mocker.GetMock<IMakeImportDecision>()
|
||||
.Verify(s => s.GetImportDecisions(It.IsAny<List<string>>(), It.IsAny<Series>(), null, true), Times.Once());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_not_process_if_file_and_folder_do_not_exist()
|
||||
{
|
||||
var folderName = @"C:\media\ba09030e-1234-1234-1234-123456789abc\[HorribleSubs] Maria the Virgin Witch - 09 [720p]".AsOsAgnostic();
|
||||
|
||||
Mocker.GetMock<IDiskProvider>().Setup(c => c.FolderExists(folderName))
|
||||
.Returns(false);
|
||||
|
||||
Mocker.GetMock<IDiskProvider>().Setup(c => c.FileExists(folderName))
|
||||
.Returns(false);
|
||||
|
||||
Subject.ProcessPath(folderName).Should().BeEmpty();
|
||||
|
||||
Mocker.GetMock<IParsingService>()
|
||||
.Verify(v => v.GetSeries(It.IsAny<string>()), Times.Never());
|
||||
|
||||
ExceptionVerification.ExpectedErrors(1);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_not_delete_if_no_files_were_imported()
|
||||
{
|
||||
GivenValidSeries();
|
||||
|
||||
var localEpisode = new LocalEpisode();
|
||||
|
||||
var imported = new List<ImportDecision>();
|
||||
imported.Add(new ImportDecision(localEpisode));
|
||||
|
||||
Mocker.GetMock<IMakeImportDecision>()
|
||||
.Setup(s => s.GetImportDecisions(It.IsAny<List<string>>(), It.IsAny<Series>(), null, true))
|
||||
.Returns(imported);
|
||||
|
||||
Mocker.GetMock<IImportApprovedEpisodes>()
|
||||
.Setup(s => s.Import(It.IsAny<List<ImportDecision>>(), true, null, ImportMode.Auto))
|
||||
.Returns(new List<ImportResult>());
|
||||
|
||||
Mocker.GetMock<IDetectSample>()
|
||||
.Setup(s => s.IsSample(It.IsAny<Series>(),
|
||||
It.IsAny<QualityModel>(),
|
||||
It.IsAny<string>(),
|
||||
It.IsAny<long>(),
|
||||
It.IsAny<bool>()))
|
||||
.Returns(true);
|
||||
|
||||
Mocker.GetMock<IDiskProvider>()
|
||||
.Setup(s => s.GetFileSize(It.IsAny<string>()))
|
||||
.Returns(15.Megabytes());
|
||||
|
||||
Subject.ProcessRootFolder(new DirectoryInfo(_droneFactory));
|
||||
|
||||
Mocker.GetMock<IDiskProvider>()
|
||||
.Verify(v => v.DeleteFolder(It.IsAny<string>(), true), Times.Never());
|
||||
}
|
||||
|
||||
private void VerifyNoImport()
|
||||
{
|
||||
Mocker.GetMock<IImportApprovedEpisodes>().Verify(c => c.Import(It.IsAny<List<ImportDecision>>(), true, null, ImportMode.Auto),
|
||||
Times.Never());
|
||||
}
|
||||
|
||||
private void VerifyImport()
|
||||
{
|
||||
Mocker.GetMock<IImportApprovedEpisodes>().Verify(c => c.Import(It.IsAny<List<ImportDecision>>(), true, null, ImportMode.Auto),
|
||||
Times.Once());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -53,7 +53,7 @@ namespace NzbDrone.Core.Test.MediaFiles.MediaInfo
|
||||
info.AudioBitrate.Should().Be(128000);
|
||||
info.AudioChannels.Should().Be(2);
|
||||
info.AudioLanguages.Should().Be("English");
|
||||
info.AudioAdditionalFeatures.Should().Be("LC");
|
||||
info.AudioAdditionalFeatures.Should().BeOneOf("", "LC");
|
||||
info.Height.Should().Be(320);
|
||||
info.RunTime.Seconds.Should().Be(10);
|
||||
info.ScanType.Should().Be("Progressive");
|
||||
@@ -90,7 +90,7 @@ namespace NzbDrone.Core.Test.MediaFiles.MediaInfo
|
||||
info.AudioBitrate.Should().Be(128000);
|
||||
info.AudioChannels.Should().Be(2);
|
||||
info.AudioLanguages.Should().Be("English");
|
||||
info.AudioAdditionalFeatures.Should().Be("LC");
|
||||
info.AudioAdditionalFeatures.Should().BeOneOf("", "LC");
|
||||
info.Height.Should().Be(320);
|
||||
info.RunTime.Seconds.Should().Be(10);
|
||||
info.ScanType.Should().Be("Progressive");
|
||||
|
||||
@@ -27,6 +27,9 @@ namespace NzbDrone.Core.Test.ParserTests
|
||||
[TestCase("Seed S02E09 HDTV x264-2HD [eztv]-[rarbg.com]", "2HD")]
|
||||
[TestCase("7s-atlantis-s02e01-720p.mkv", null)]
|
||||
[TestCase("The.Middle.720p.HEVC.x265-MeGusta-Pre", "MeGusta")]
|
||||
[TestCase("Blue.Bloods.S08E05.The.Forgotten.1080p.AMZN.WEB-DL.DDP5.1.H.264-NTb-Rakuv", "NTb")]
|
||||
[TestCase("Lie.To.Me.S01E13.720p.BluRay.x264-SiNNERS-Rakuvfinhel", "SiNNERS")]
|
||||
[TestCase("Who.is.America.S01E01.INTERNAL.720p.HDTV.x264-aAF-RakuvUS-Obfuscated", "aAF")]
|
||||
[TestCase("Haunted.Hayride.2018.720p.WEBRip.DDP5.1.x264-NTb-postbot", "NTb")]
|
||||
[TestCase("Haunted.Hayride.2018.720p.WEBRip.DDP5.1.x264-NTb-xpost", "NTb")]
|
||||
//[TestCase("", "")]
|
||||
|
||||
@@ -142,7 +142,7 @@ namespace NzbDrone.Core.Extras.Metadata.Consumers.Roksbox
|
||||
if (image == null)
|
||||
{
|
||||
_logger.Trace("Failed to find suitable Movie image for movie {0}.", movie.Title);
|
||||
return null;
|
||||
return new List<ImageFileResult>();
|
||||
}
|
||||
|
||||
var source = _mediaCoverService.GetCoverPath(movie.Id, image.CoverType);
|
||||
|
||||
@@ -110,7 +110,7 @@ namespace NzbDrone.Core.MediaFiles.MovieImport.Manual
|
||||
|
||||
if (movie == null)
|
||||
{
|
||||
movie = trackedDownload.RemoteMovie.Movie;
|
||||
movie = trackedDownload.RemoteMovie?.Movie;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
107
src/NzbDrone.Core/Notifications/Discord/Discord.cs
Normal file
107
src/NzbDrone.Core/Notifications/Discord/Discord.cs
Normal file
@@ -0,0 +1,107 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using FluentValidation.Results;
|
||||
using NzbDrone.Common.Extensions;
|
||||
using NzbDrone.Core.Notifications.Discord.Payloads;
|
||||
using NzbDrone.Core.Validation;
|
||||
|
||||
namespace NzbDrone.Core.Notifications.Discord
|
||||
{
|
||||
public class Discord : NotificationBase<DiscordSettings>
|
||||
{
|
||||
private readonly IDiscordProxy _proxy;
|
||||
|
||||
public Discord(IDiscordProxy proxy)
|
||||
{
|
||||
_proxy = proxy;
|
||||
}
|
||||
|
||||
public override string Name => "Discord";
|
||||
public override string Link => "https://support.discordapp.com/hc/en-us/articles/228383668-Intro-to-Webhooks";
|
||||
|
||||
public override void OnGrab(GrabMessage message)
|
||||
{
|
||||
var embeds = new List<Embed>
|
||||
{
|
||||
new Embed
|
||||
{
|
||||
Description = message.Message,
|
||||
Title = message.Movie.Title,
|
||||
Text = message.Message,
|
||||
Color = (int)DiscordColors.Warning
|
||||
}
|
||||
};
|
||||
var payload = CreatePayload($"Grabbed: {message.Message}", embeds);
|
||||
|
||||
_proxy.SendPayload(payload, Settings);
|
||||
}
|
||||
|
||||
public override void OnDownload(DownloadMessage message)
|
||||
{
|
||||
var embeds = new List<Embed>
|
||||
{
|
||||
new Embed
|
||||
{
|
||||
Description = message.Message,
|
||||
Title = message.Movie.Title,
|
||||
Text = message.Message,
|
||||
Color = (int)DiscordColors.Success
|
||||
}
|
||||
};
|
||||
var payload = CreatePayload($"Imported: {message.Message}", embeds);
|
||||
|
||||
_proxy.SendPayload(payload, Settings);
|
||||
}
|
||||
|
||||
public override ValidationResult Test()
|
||||
{
|
||||
var failures = new List<ValidationFailure>();
|
||||
|
||||
failures.AddIfNotNull(TestMessage());
|
||||
|
||||
return new ValidationResult(failures);
|
||||
}
|
||||
|
||||
public ValidationFailure TestMessage()
|
||||
{
|
||||
try
|
||||
{
|
||||
var message = $"Test message from Radarr posted at {DateTime.Now}";
|
||||
var payload = CreatePayload(message);
|
||||
|
||||
_proxy.SendPayload(payload, Settings);
|
||||
|
||||
}
|
||||
catch (DiscordException ex)
|
||||
{
|
||||
return new NzbDroneValidationFailure("Unable to post", ex.Message);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private DiscordPayload CreatePayload(string message, List<Embed> embeds = null)
|
||||
{
|
||||
var avatar = Settings.Avatar;
|
||||
|
||||
var payload = new DiscordPayload
|
||||
{
|
||||
Username = Settings.Username,
|
||||
Content = message,
|
||||
Embeds = embeds
|
||||
};
|
||||
|
||||
if (avatar.IsNotNullOrWhiteSpace())
|
||||
{
|
||||
payload.AvatarUrl = avatar;
|
||||
}
|
||||
|
||||
if (Settings.Username.IsNotNullOrWhiteSpace())
|
||||
{
|
||||
payload.Username = Settings.Username;
|
||||
}
|
||||
|
||||
return payload;
|
||||
}
|
||||
}
|
||||
}
|
||||
9
src/NzbDrone.Core/Notifications/Discord/DiscordColors.cs
Normal file
9
src/NzbDrone.Core/Notifications/Discord/DiscordColors.cs
Normal file
@@ -0,0 +1,9 @@
|
||||
namespace NzbDrone.Core.Notifications.Discord
|
||||
{
|
||||
public enum DiscordColors
|
||||
{
|
||||
Danger = 15749200,
|
||||
Success = 2605644,
|
||||
Warning = 16753920
|
||||
}
|
||||
}
|
||||
16
src/NzbDrone.Core/Notifications/Discord/DiscordException.cs
Normal file
16
src/NzbDrone.Core/Notifications/Discord/DiscordException.cs
Normal file
@@ -0,0 +1,16 @@
|
||||
using System;
|
||||
using NzbDrone.Common.Exceptions;
|
||||
|
||||
namespace NzbDrone.Core.Notifications.Discord
|
||||
{
|
||||
class DiscordException : NzbDroneException
|
||||
{
|
||||
public DiscordException(string message) : base(message)
|
||||
{
|
||||
}
|
||||
|
||||
public DiscordException(string message, Exception innerException, params object[] args) : base(message, innerException, args)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
46
src/NzbDrone.Core/Notifications/Discord/DiscordProxy.cs
Normal file
46
src/NzbDrone.Core/Notifications/Discord/DiscordProxy.cs
Normal file
@@ -0,0 +1,46 @@
|
||||
using NLog;
|
||||
using NzbDrone.Common.Http;
|
||||
using NzbDrone.Common.Serializer;
|
||||
using NzbDrone.Core.Notifications.Discord.Payloads;
|
||||
using NzbDrone.Core.Rest;
|
||||
|
||||
namespace NzbDrone.Core.Notifications.Discord
|
||||
{
|
||||
public interface IDiscordProxy
|
||||
{
|
||||
void SendPayload(DiscordPayload payload, DiscordSettings settings);
|
||||
}
|
||||
|
||||
public class DiscordProxy : IDiscordProxy
|
||||
{
|
||||
private readonly IHttpClient _httpClient;
|
||||
private readonly Logger _logger;
|
||||
|
||||
public DiscordProxy(IHttpClient httpClient, Logger logger)
|
||||
{
|
||||
_httpClient = httpClient;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public void SendPayload(DiscordPayload payload, DiscordSettings settings)
|
||||
{
|
||||
try
|
||||
{
|
||||
var request = new HttpRequestBuilder(settings.WebHookUrl)
|
||||
.Accept(HttpAccept.Json)
|
||||
.Build();
|
||||
|
||||
request.Method = HttpMethod.POST;
|
||||
request.Headers.ContentType = "application/json";
|
||||
request.SetContent(payload.ToJson());
|
||||
|
||||
_httpClient.Execute(request);
|
||||
}
|
||||
catch (RestException ex)
|
||||
{
|
||||
_logger.Error(ex, "Unable to post payload {0}", payload);
|
||||
throw new DiscordException("Unable to post payload", ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
35
src/NzbDrone.Core/Notifications/Discord/DiscordSettings.cs
Normal file
35
src/NzbDrone.Core/Notifications/Discord/DiscordSettings.cs
Normal file
@@ -0,0 +1,35 @@
|
||||
using FluentValidation;
|
||||
using NzbDrone.Core.Annotations;
|
||||
using NzbDrone.Core.ThingiProvider;
|
||||
using NzbDrone.Core.Validation;
|
||||
|
||||
namespace NzbDrone.Core.Notifications.Discord
|
||||
{
|
||||
public class DiscordSettingsValidator : AbstractValidator<DiscordSettings>
|
||||
{
|
||||
public DiscordSettingsValidator()
|
||||
{
|
||||
RuleFor(c => c.WebHookUrl).IsValidUrl();
|
||||
}
|
||||
}
|
||||
|
||||
public class DiscordSettings : IProviderConfig
|
||||
{
|
||||
private static readonly DiscordSettingsValidator Validator = new DiscordSettingsValidator();
|
||||
|
||||
[FieldDefinition(0, Label = "Webhook URL", HelpText = "Discord channel webhook url")]
|
||||
public string WebHookUrl { get; set; }
|
||||
|
||||
[FieldDefinition(1, Label = "Username", HelpText = "The username to post as, defaults to Discord webhook default")]
|
||||
public string Username { get; set; }
|
||||
|
||||
[FieldDefinition(2, Label = "Avatar", HelpText = "Change the avatar that is used for messages from this integration", Type = FieldType.Textbox)]
|
||||
public string Avatar { get; set; }
|
||||
|
||||
|
||||
public NzbDroneValidationResult Validate()
|
||||
{
|
||||
return new NzbDroneValidationResult(Validator.Validate(this));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
using System.Collections.Generic;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace NzbDrone.Core.Notifications.Discord.Payloads
|
||||
{
|
||||
public class DiscordPayload
|
||||
{
|
||||
public string Content { get; set; }
|
||||
|
||||
public string Username { get; set; }
|
||||
|
||||
[JsonProperty("avatar_url")]
|
||||
public string AvatarUrl { get; set; }
|
||||
|
||||
public List<Embed> Embeds { get; set; }
|
||||
}
|
||||
}
|
||||
10
src/NzbDrone.Core/Notifications/Discord/Payloads/Embed.cs
Normal file
10
src/NzbDrone.Core/Notifications/Discord/Payloads/Embed.cs
Normal file
@@ -0,0 +1,10 @@
|
||||
namespace NzbDrone.Core.Notifications.Discord.Payloads
|
||||
{
|
||||
public class Embed
|
||||
{
|
||||
public string Description { get; set; }
|
||||
public string Title { get; set; }
|
||||
public string Text { get; set; }
|
||||
public int Color { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
namespace NzbDrone.Core.Notifications.Gotify
|
||||
|
||||
{
|
||||
public enum GotifyPriority
|
||||
{
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using FluentValidation.Results;
|
||||
using NLog;
|
||||
using NzbDrone.Common.Extensions;
|
||||
using NzbDrone.Core.Notifications.Slack.Payloads;
|
||||
using NzbDrone.Core.Movies;
|
||||
@@ -13,12 +12,10 @@ namespace NzbDrone.Core.Notifications.Slack
|
||||
public class Slack : NotificationBase<SlackSettings>
|
||||
{
|
||||
private readonly ISlackProxy _proxy;
|
||||
private readonly Logger _logger;
|
||||
|
||||
public Slack(ISlackProxy proxy, Logger logger)
|
||||
public Slack(ISlackProxy proxy)
|
||||
{
|
||||
_proxy = proxy;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public override string Name => "Slack";
|
||||
|
||||
@@ -964,10 +964,17 @@
|
||||
<Compile Include="Extras\Metadata\MetadataType.cs" />
|
||||
<Compile Include="MetadataSource\TmdbConfigurationService.cs" />
|
||||
<Compile Include="NetImport\NetImportSyncCommand.cs" />
|
||||
<Compile Include="Notifications\Discord\Discord.cs" />
|
||||
<Compile Include="Notifications\Discord\DiscordColors.cs" />
|
||||
<Compile Include="Notifications\Discord\DiscordException.cs" />
|
||||
<Compile Include="Notifications\Discord\DiscordProxy.cs" />
|
||||
<Compile Include="Notifications\Discord\DiscordSettings.cs" />
|
||||
<Compile Include="Notifications\Discord\Payloads\DiscordPayload.cs" />
|
||||
<Compile Include="Notifications\Discord\Payloads\Embed.cs" />
|
||||
<Compile Include="Notifications\Gotify\GotifyProxy.cs" />
|
||||
<Compile Include="Notifications\Gotify\InvalidResponseException.cs" />
|
||||
<Compile Include="Notifications\Gotify\Gotify.cs" />
|
||||
<Compile Include="Notifications\Gotify\GotifyPriority.cs" />
|
||||
<Compile Include="Notifications\Gotify\GotifyService.cs" />
|
||||
<Compile Include="Notifications\Gotify\GotifySettings.cs" />
|
||||
<Compile Include="Notifications\Join\JoinAuthException.cs" />
|
||||
<Compile Include="Notifications\Join\JoinInvalidDeviceException.cs" />
|
||||
|
||||
@@ -35,18 +35,9 @@ namespace NzbDrone.Core.Organizer
|
||||
private static readonly Regex TitleRegex = new Regex(@"\{(?<prefix>[- ._\[(]*)(?<token>(?:[a-z0-9]+)(?:(?<separator>[- ._]+)(?:[a-z0-9]+))?)(?::(?<customFormat>[a-z0-9]+))?(?<suffix>[- ._)\]]*)\}",
|
||||
RegexOptions.Compiled | RegexOptions.IgnoreCase);
|
||||
|
||||
private static readonly Regex EpisodeRegex = new Regex(@"(?<episode>\{episode(?:\:0+)?})",
|
||||
RegexOptions.Compiled | RegexOptions.IgnoreCase);
|
||||
|
||||
private static readonly Regex TagsRegex = new Regex(@"(?<tags>\{tags(?:\:0+)?})",
|
||||
RegexOptions.Compiled | RegexOptions.IgnoreCase);
|
||||
|
||||
private static readonly Regex SeasonRegex = new Regex(@"(?<season>\{season(?:\:0+)?})",
|
||||
RegexOptions.Compiled | RegexOptions.IgnoreCase);
|
||||
|
||||
private static readonly Regex AbsoluteEpisodeRegex = new Regex(@"(?<absolute>\{absolute(?:\:0+)?})",
|
||||
RegexOptions.Compiled | RegexOptions.IgnoreCase);
|
||||
|
||||
public static readonly Regex SeasonEpisodePatternRegex = new Regex(@"(?<separator>(?<=})[- ._]+?)?(?<seasonEpisode>s?{season(?:\:0+)?}(?<episodeSeparator>[- ._]?[ex])(?<episode>{episode(?:\:0+)?}))(?<separator>[- ._]+?(?={))?",
|
||||
RegexOptions.Compiled | RegexOptions.IgnoreCase);
|
||||
|
||||
@@ -178,48 +169,6 @@ namespace NzbDrone.Core.Organizer
|
||||
{
|
||||
return new BasicNamingConfig(); //For now let's be lazy
|
||||
|
||||
//var episodeFormat = GetEpisodeFormat(nameSpec.StandardMovieFormat).LastOrDefault();
|
||||
|
||||
//if (episodeFormat == null)
|
||||
//{
|
||||
// return new BasicNamingConfig();
|
||||
//}
|
||||
|
||||
//var basicNamingConfig = new BasicNamingConfig
|
||||
//{
|
||||
// Separator = episodeFormat.Separator,
|
||||
// NumberStyle = episodeFormat.SeasonEpisodePattern
|
||||
//};
|
||||
|
||||
//var titleTokens = TitleRegex.Matches(nameSpec.StandardMovieFormat);
|
||||
|
||||
//foreach (Match match in titleTokens)
|
||||
//{
|
||||
// var separator = match.Groups["separator"].Value;
|
||||
// var token = match.Groups["token"].Value;
|
||||
|
||||
// if (!separator.Equals(" "))
|
||||
// {
|
||||
// basicNamingConfig.ReplaceSpaces = true;
|
||||
// }
|
||||
|
||||
// if (token.StartsWith("{Series", StringComparison.InvariantCultureIgnoreCase))
|
||||
// {
|
||||
// basicNamingConfig.IncludeSeriesTitle = true;
|
||||
// }
|
||||
|
||||
// if (token.StartsWith("{Episode", StringComparison.InvariantCultureIgnoreCase))
|
||||
// {
|
||||
// basicNamingConfig.IncludeEpisodeTitle = true;
|
||||
// }
|
||||
|
||||
// if (token.StartsWith("{Quality", StringComparison.InvariantCultureIgnoreCase))
|
||||
// {
|
||||
// basicNamingConfig.IncludeQuality = true;
|
||||
// }
|
||||
//}
|
||||
|
||||
//return basicNamingConfig;
|
||||
}
|
||||
|
||||
public string GetMovieFolder(Movie movie, NamingConfig namingConfig = null)
|
||||
@@ -521,18 +470,6 @@ namespace NzbDrone.Core.Organizer
|
||||
return value.ToString(split[1]);
|
||||
}
|
||||
|
||||
private EpisodeFormat[] GetEpisodeFormat(string pattern)
|
||||
{
|
||||
return _episodeFormatCache.Get(pattern, () => SeasonEpisodePatternRegex.Matches(pattern).OfType<Match>()
|
||||
.Select(match => new EpisodeFormat
|
||||
{
|
||||
EpisodeSeparator = match.Groups["episodeSeparator"].Value,
|
||||
Separator = match.Groups["separator"].Value,
|
||||
EpisodePattern = match.Groups["episode"].Value,
|
||||
SeasonEpisodePattern = match.Groups["seasonEpisode"].Value,
|
||||
}).ToArray());
|
||||
}
|
||||
|
||||
private string GetQualityProper(Movie movie, QualityModel quality)
|
||||
{
|
||||
if (quality.Revision.Version > 1)
|
||||
|
||||
@@ -113,7 +113,7 @@ namespace NzbDrone.Core.Parser
|
||||
private static readonly Regex SixDigitAirDateRegex = new Regex(@"(?<=[_.-])(?<airdate>(?<!\d)(?<airyear>[1-9]\d{1})(?<airmonth>[0-1][0-9])(?<airday>[0-3][0-9]))(?=[_.-])",
|
||||
RegexOptions.IgnoreCase | RegexOptions.Compiled);
|
||||
|
||||
private static readonly Regex CleanReleaseGroupRegex = new Regex(@"^(.*?[-._ ](S\d+E\d+)[-._ ])|(-(RP|1|NZBGeek|Obfuscated|sample|Pre|postbot|xpost))+$",
|
||||
private static readonly Regex CleanReleaseGroupRegex = new Regex(@"^(.*?[-._ ](S\d+E\d+)[-._ ])|(-(RP|1|NZBGeek|Obfuscated|sample|Pre|postbot|xpost|Rakuv[a-z]*|WhiteRev|BUYMORE))+$",
|
||||
RegexOptions.IgnoreCase | RegexOptions.Compiled);
|
||||
|
||||
private static readonly Regex CleanTorrentSuffixRegex = new Regex(@"\[(?:ettv|rartv|rarbg|cttv)\]$",
|
||||
|
||||
@@ -16,10 +16,12 @@ namespace NzbDrone.Integration.Test
|
||||
config.LogLevel = "Trace";
|
||||
HostConfig.Put(config);
|
||||
|
||||
var resultGet = Movies.All();
|
||||
|
||||
var logFile = Path.Combine(_runner.AppData, "logs", "radarr.trace.txt");
|
||||
var logLines = File.ReadAllLines(logFile);
|
||||
|
||||
var result = Movies.InvalidPost(new MovieResource());
|
||||
var resultPost = Movies.InvalidPost(new MovieResource());
|
||||
|
||||
logLines = File.ReadAllLines(logFile).Skip(logLines.Length).ToArray();
|
||||
|
||||
|
||||
@@ -125,6 +125,9 @@ namespace NzbDrone.Integration.Test
|
||||
public void IntegrationSetUp()
|
||||
{
|
||||
TempDirectory = Path.Combine(TestContext.CurrentContext.TestDirectory, "_test_" + DateTime.UtcNow.Ticks);
|
||||
|
||||
// Wait for things to get quiet, otherwise the previous test might influence the current one.
|
||||
Commands.WaitAll();
|
||||
}
|
||||
|
||||
[TearDown]
|
||||
|
||||
@@ -26,4 +26,4 @@ namespace NzbDrone.Mono.Test.EnvironmentInfo
|
||||
info.Version.Should().NotBeNullOrWhiteSpace();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -74,4 +74,4 @@ namespace NzbDrone.Mono.Test.EnvironmentInfo.VersionAdapters
|
||||
.Verify(c => c.GetFiles(It.IsAny<string>(), SearchOption.TopDirectoryOnly), Times.Never());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -79,4 +79,4 @@ namespace NzbDrone.Mono.Test.EnvironmentInfo.VersionAdapters
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -10,6 +10,7 @@ namespace NzbDrone.Mono.Disk
|
||||
{
|
||||
{ "afpfs", DriveType.Network },
|
||||
{ "apfs", DriveType.Fixed },
|
||||
{ "fuse.mergerfs", DriveType.Fixed },
|
||||
{ "zfs", DriveType.Fixed }
|
||||
};
|
||||
|
||||
|
||||
@@ -88,7 +88,7 @@
|
||||
<Compile Include="SignalRDependencyResolver.cs" />
|
||||
<Compile Include="SignalRJsonSerializer.cs" />
|
||||
<Compile Include="SignalRMessage.cs" />
|
||||
<Compile Include="SonarrPerformanceCounterManager.cs" />
|
||||
<Compile Include="RadarrPerformanceCounterManager.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\NzbDrone.Common\NzbDrone.Common.csproj">
|
||||
|
||||
@@ -3,7 +3,7 @@ using Microsoft.AspNet.SignalR.Infrastructure;
|
||||
|
||||
namespace NzbDrone.SignalR
|
||||
{
|
||||
public class SonarrPerformanceCounterManager : IPerformanceCounterManager
|
||||
public class RadarrPerformanceCounterManager : IPerformanceCounterManager
|
||||
{
|
||||
private readonly IPerformanceCounter _counter = new NoOpPerformanceCounter();
|
||||
|
||||
@@ -17,7 +17,7 @@ namespace NzbDrone.SignalR
|
||||
private SignalRDependencyResolver(IContainer container)
|
||||
{
|
||||
_container = container;
|
||||
var performanceCounterManager = new SonarrPerformanceCounterManager();
|
||||
var performanceCounterManager = new RadarrPerformanceCounterManager();
|
||||
Register(typeof(IPerformanceCounterManager), () => performanceCounterManager);
|
||||
}
|
||||
|
||||
|
||||
@@ -36,7 +36,6 @@ namespace Radarr.Api.V2.Calendar
|
||||
var start = DateTime.Today.AddDays(-pastDays);
|
||||
var end = DateTime.Today.AddDays(futureDays);
|
||||
var unmonitored = false;
|
||||
//var premiersOnly = false;
|
||||
var tags = new List<int>();
|
||||
|
||||
// TODO: Remove start/end parameters in v3, they don't work well for iCal
|
||||
@@ -45,7 +44,6 @@ namespace Radarr.Api.V2.Calendar
|
||||
var queryPastDays = Request.Query.PastDays;
|
||||
var queryFutureDays = Request.Query.FutureDays;
|
||||
var queryUnmonitored = Request.Query.Unmonitored;
|
||||
// var queryPremiersOnly = Request.Query.PremiersOnly;
|
||||
var queryTags = Request.Query.Tags;
|
||||
|
||||
if (queryStart.HasValue) start = DateTime.Parse(queryStart.Value);
|
||||
@@ -68,11 +66,6 @@ namespace Radarr.Api.V2.Calendar
|
||||
unmonitored = bool.Parse(queryUnmonitored.Value);
|
||||
}
|
||||
|
||||
//if (queryPremiersOnly.HasValue)
|
||||
//{
|
||||
// premiersOnly = bool.Parse(queryPremiersOnly.Value);
|
||||
//}
|
||||
|
||||
if (queryTags.HasValue)
|
||||
{
|
||||
var tagInput = (string)queryTags.Value.ToString();
|
||||
@@ -116,7 +109,7 @@ namespace Radarr.Api.V2.Calendar
|
||||
}
|
||||
|
||||
var occurrence = calendar.Create<Event>();
|
||||
occurrence.Uid = "NzbDrone_movie_" + movie.Id + (cinemasRelease ? "_cinemas" : "_physical");
|
||||
occurrence.Uid = "Radarr_movie_" + movie.Id + (cinemasRelease ? "_cinemas" : "_physical");
|
||||
occurrence.Status = movie.Status == MovieStatusType.Announced ? EventStatus.Tentative : EventStatus.Confirmed;
|
||||
|
||||
occurrence.Start = new CalDateTime(date.Value);
|
||||
|
||||
@@ -2,9 +2,9 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using FluentValidation;
|
||||
using Nancy;
|
||||
using Nancy.ModelBinding;
|
||||
using NLog;
|
||||
using NzbDrone.Common.Cache;
|
||||
using NzbDrone.Common.Extensions;
|
||||
using NzbDrone.Core.DecisionEngine;
|
||||
using NzbDrone.Core.Download;
|
||||
using NzbDrone.Core.Exceptions;
|
||||
@@ -43,12 +43,12 @@ namespace Radarr.Api.V2.Indexers
|
||||
_downloadService = downloadService;
|
||||
_logger = logger;
|
||||
|
||||
GetResourceAll = GetReleases;
|
||||
Post["/"] = x => DownloadRelease(ReadResourceFromRequest());
|
||||
|
||||
PostValidator.RuleFor(s => s.IndexerId).ValidId();
|
||||
PostValidator.RuleFor(s => s.Guid).NotEmpty();
|
||||
|
||||
GetResourceAll = GetReleases;
|
||||
Post["/"] = x => DownloadRelease(ReadResourceFromRequest());
|
||||
|
||||
_remoteMovieCache = cacheManager.GetCache<RemoteMovie>(GetType(), "remoteMovies");
|
||||
}
|
||||
|
||||
@@ -69,7 +69,7 @@ namespace Radarr.Api.V2.Indexers
|
||||
}
|
||||
catch (ReleaseDownloadException ex)
|
||||
{
|
||||
_logger.ErrorException(ex.Message, ex);
|
||||
_logger.Error(ex, ex.Message);
|
||||
throw new NzbDroneClientException(HttpStatusCode.Conflict, "Getting release from indexer failed");
|
||||
}
|
||||
|
||||
|
||||
@@ -56,10 +56,10 @@ namespace Radarr.Api.V2.Indexers
|
||||
|
||||
if (firstDecision?.RemoteMovie.ParsedMovieInfo == null)
|
||||
{
|
||||
throw new ValidationException(new List<ValidationFailure> { new ValidationFailure("Title", "Unable to parse", release.Title) });
|
||||
throw new ValidationException(new List<ValidationFailure>{ new ValidationFailure("Title", "Unable to parse", release.Title) });
|
||||
}
|
||||
|
||||
return MapDecisions(new[] { firstDecision }).AsResponse();
|
||||
return MapDecisions(new [] { firstDecision }).AsResponse();
|
||||
}
|
||||
|
||||
private void ResolveIndexer(ReleaseInfo release)
|
||||
@@ -74,7 +74,7 @@ namespace Radarr.Api.V2.Indexers
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.Debug("Push Release {0} not associated with unknown indexer {1}.", release.Title, release.Indexer);
|
||||
_logger.Debug("Push Release {0} not associated with known indexer {1}.", release.Title, release.Indexer);
|
||||
}
|
||||
}
|
||||
else if (release.IndexerId != 0 && release.Indexer.IsNullOrWhiteSpace())
|
||||
@@ -87,7 +87,7 @@ namespace Radarr.Api.V2.Indexers
|
||||
}
|
||||
catch (ModelNotFoundException)
|
||||
{
|
||||
_logger.Debug("Push Release {0} not associated with unknown indexer {0}.", release.Title, release.IndexerId);
|
||||
_logger.Debug("Push Release {0} not associated with known indexer {0}.", release.Title, release.IndexerId);
|
||||
release.IndexerId = 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Newtonsoft.Json;
|
||||
using NzbDrone.Core.DecisionEngine;
|
||||
using NzbDrone.Core.Indexers;
|
||||
using NzbDrone.Core.Languages;
|
||||
@@ -46,11 +47,16 @@ namespace Radarr.Api.V2.Indexers
|
||||
public int? Leechers { get; set; }
|
||||
public DownloadProtocol Protocol { get; set; }
|
||||
|
||||
|
||||
public bool IsDaily { get; set; }
|
||||
public bool IsAbsoluteNumbering { get; set; }
|
||||
public bool IsPossibleSpecialEpisode { get; set; }
|
||||
public bool Special { get; set; }
|
||||
|
||||
// Sent when queuing an unknown release
|
||||
|
||||
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)]
|
||||
public int? MovieId { get; set; }
|
||||
|
||||
}
|
||||
|
||||
public static class ReleaseResourceMapper
|
||||
@@ -88,7 +94,7 @@ namespace Radarr.Api.V2.Indexers
|
||||
CommentUrl = releaseInfo.CommentUrl,
|
||||
DownloadUrl = releaseInfo.DownloadUrl,
|
||||
InfoUrl = releaseInfo.InfoUrl,
|
||||
// DownloadAllowed = remoteMovie.DownloadAllowed,
|
||||
DownloadAllowed = remoteMovie.DownloadAllowed,
|
||||
//ReleaseWeight
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user