mirror of
https://github.com/Prowlarr/Prowlarr.git
synced 2026-04-17 21:44:48 -04:00
Compare commits
26 Commits
v1.5.0.339
...
v1.5.1.342
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b3ddf2f9cd | ||
|
|
d9ce9eb0b2 | ||
|
|
29ab1801db | ||
|
|
19ff73dad0 | ||
|
|
c455f1a113 | ||
|
|
b8793d8783 | ||
|
|
ce34940287 | ||
|
|
dcb19a66b0 | ||
|
|
b3bc92e60e | ||
|
|
1b17d38564 | ||
|
|
d8c7361205 | ||
|
|
7a0dd0bc0d | ||
|
|
c02bfb5930 | ||
|
|
d0fbb1f49a | ||
|
|
aafdefe2f0 | ||
|
|
96234c0fe1 | ||
|
|
8b5648d7bd | ||
|
|
1fc79f9e9b | ||
|
|
ec40761757 | ||
|
|
0a8e4eb092 | ||
|
|
ade961fad5 | ||
|
|
81b1c0e445 | ||
|
|
0fe54ed36a | ||
|
|
337828ff9c | ||
|
|
fb34294d2e | ||
|
|
931e3cf42d |
@@ -9,7 +9,7 @@ variables:
|
||||
testsFolder: './_tests'
|
||||
yarnCacheFolder: $(Pipeline.Workspace)/.yarn
|
||||
nugetCacheFolder: $(Pipeline.Workspace)/.nuget/packages
|
||||
majorVersion: '1.5.0'
|
||||
majorVersion: '1.5.1'
|
||||
minorVersion: $[counter('minorVersion', 1)]
|
||||
prowlarrVersion: '$(majorVersion).$(minorVersion)'
|
||||
buildName: '$(Build.SourceBranchName).$(prowlarrVersion)'
|
||||
|
||||
@@ -13,7 +13,7 @@ const messages = [
|
||||
'Loading humorous message... Please Wait',
|
||||
'I could\'ve been faster in Python',
|
||||
'Don\'t forget to rewind your tracks',
|
||||
'Congratulations! you are the 1000th visitor.',
|
||||
'Congratulations! You are the 1000th visitor.',
|
||||
'HELP! I\'m being held hostage and forced to write these stupid lines!',
|
||||
'RE-calibrating the internet...',
|
||||
'I\'ll be here all week',
|
||||
|
||||
@@ -351,6 +351,26 @@ namespace NzbDrone.Common.Test.DiskTests
|
||||
.Verify(v => v.DeleteFile(_targetPath), Times.Once());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_not_rollback_move_on_partial_if_destination_already_exists()
|
||||
{
|
||||
Mocker.GetMock<IDiskProvider>()
|
||||
.Setup(v => v.MoveFile(_sourcePath, _targetPath, false))
|
||||
.Callback(() =>
|
||||
{
|
||||
WithExistingFile(_targetPath, true, 900);
|
||||
});
|
||||
|
||||
Mocker.GetMock<IDiskProvider>()
|
||||
.Setup(v => v.MoveFile(_sourcePath, _targetPath, false))
|
||||
.Throws(new FileAlreadyExistsException("File already exists", _targetPath));
|
||||
|
||||
Assert.Throws<FileAlreadyExistsException>(() => Subject.TransferFile(_sourcePath, _targetPath, TransferMode.Move));
|
||||
|
||||
Mocker.GetMock<IDiskProvider>()
|
||||
.Verify(v => v.DeleteFile(_targetPath), Times.Never());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_log_error_if_rollback_partialmove_fails()
|
||||
{
|
||||
|
||||
@@ -264,6 +264,11 @@ namespace NzbDrone.Common.Disk
|
||||
|
||||
protected virtual void MoveFileInternal(string source, string destination)
|
||||
{
|
||||
if (File.Exists(destination))
|
||||
{
|
||||
throw new FileAlreadyExistsException("File already exists", destination);
|
||||
}
|
||||
|
||||
File.Move(source, destination);
|
||||
}
|
||||
|
||||
|
||||
@@ -500,9 +500,13 @@ namespace NzbDrone.Common.Disk
|
||||
throw new IOException(string.Format("File move incomplete, data loss may have occurred. [{0}] was {1} bytes long instead of the expected {2}.", targetPath, targetSize, originalSize));
|
||||
}
|
||||
}
|
||||
catch
|
||||
catch (Exception ex)
|
||||
{
|
||||
RollbackPartialMove(sourcePath, targetPath);
|
||||
if (ex is not FileAlreadyExistsException)
|
||||
{
|
||||
RollbackPartialMove(sourcePath, targetPath);
|
||||
}
|
||||
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
15
src/NzbDrone.Common/Disk/FileAlreadyExistsException.cs
Normal file
15
src/NzbDrone.Common/Disk/FileAlreadyExistsException.cs
Normal file
@@ -0,0 +1,15 @@
|
||||
using System;
|
||||
|
||||
namespace NzbDrone.Common.Disk
|
||||
{
|
||||
public class FileAlreadyExistsException : Exception
|
||||
{
|
||||
public string Filename { get; set; }
|
||||
|
||||
public FileAlreadyExistsException(string message, string filename)
|
||||
: base(message)
|
||||
{
|
||||
Filename = filename;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -313,7 +313,7 @@ namespace NzbDrone.Common.Processes
|
||||
processInfo = new ProcessInfo();
|
||||
processInfo.Id = process.Id;
|
||||
processInfo.Name = process.ProcessName;
|
||||
processInfo.StartPath = GetExeFileName(process);
|
||||
processInfo.StartPath = process.MainModule.FileName;
|
||||
|
||||
if (process.Id != GetCurrentProcessId() && process.HasExited)
|
||||
{
|
||||
@@ -328,16 +328,6 @@ namespace NzbDrone.Common.Processes
|
||||
return processInfo;
|
||||
}
|
||||
|
||||
private static string GetExeFileName(Process process)
|
||||
{
|
||||
if (process.MainModule.FileName != "mono.exe")
|
||||
{
|
||||
return process.MainModule.FileName;
|
||||
}
|
||||
|
||||
return process.Modules.Cast<ProcessModule>().FirstOrDefault(module => module.ModuleName.ToLower().EndsWith(".exe")).FileName;
|
||||
}
|
||||
|
||||
private List<Process> GetProcessesByName(string name)
|
||||
{
|
||||
//TODO: move this to an OS specific class
|
||||
|
||||
@@ -6,6 +6,7 @@ using NzbDrone.Core.Indexers;
|
||||
using NzbDrone.Core.Indexers.Newznab;
|
||||
using NzbDrone.Core.Lifecycle;
|
||||
using NzbDrone.Core.Test.Framework;
|
||||
using NzbDrone.Test.Common;
|
||||
|
||||
namespace NzbDrone.Core.Test.IndexerTests
|
||||
{
|
||||
@@ -31,13 +32,15 @@ namespace NzbDrone.Core.Test.IndexerTests
|
||||
Mocker.SetConstant<IIndexerRepository>(repo);
|
||||
|
||||
var existingIndexers = Builder<IndexerDefinition>.CreateNew().BuildNew();
|
||||
existingIndexers.ConfigContract = typeof(NewznabSettings).Name;
|
||||
existingIndexers.ConfigContract = nameof(NewznabSettings);
|
||||
|
||||
repo.Insert(existingIndexers);
|
||||
|
||||
Subject.Handle(new ApplicationStartedEvent());
|
||||
|
||||
AllStoredModels.Should().NotContain(c => c.Id == existingIndexers.Id);
|
||||
|
||||
ExceptionVerification.ExpectedWarns(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -63,5 +63,22 @@ namespace NzbDrone.Core.Test.ParserTests
|
||||
{
|
||||
ParseUtil.GetLongFromString(original).Should().Be(parsedInt);
|
||||
}
|
||||
|
||||
[TestCase("tt0183790", "tt0183790")]
|
||||
[TestCase("0183790", "tt0183790")]
|
||||
[TestCase("183790", "tt0183790")]
|
||||
[TestCase("tt10001870", "tt10001870")]
|
||||
[TestCase("10001870", "tt10001870")]
|
||||
[TestCase("tt", null)]
|
||||
[TestCase("tt0", null)]
|
||||
[TestCase("abc", null)]
|
||||
[TestCase("abc0", null)]
|
||||
[TestCase("0", null)]
|
||||
[TestCase("", null)]
|
||||
[TestCase(null, null)]
|
||||
public void should_parse_full_imdb_id_from_string(string input, string expected)
|
||||
{
|
||||
ParseUtil.GetFullImdbId(input).Should().Be(expected);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using FluentValidation.Results;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using NLog;
|
||||
@@ -49,10 +48,10 @@ namespace NzbDrone.Core.Applications.Lidarr
|
||||
{
|
||||
failures.AddIfNotNull(_lidarrV1Proxy.TestConnection(BuildLidarrIndexer(testIndexer, DownloadProtocol.Usenet), Settings));
|
||||
}
|
||||
catch (WebException ex)
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.Error(ex, "Unable to send test message");
|
||||
failures.AddIfNotNull(new ValidationFailure("BaseUrl", "Unable to complete application test, cannot connect to Lidarr"));
|
||||
failures.AddIfNotNull(new ValidationFailure("BaseUrl", $"Unable to complete application test, cannot connect to Lidarr. {ex.Message}"));
|
||||
}
|
||||
|
||||
return new ValidationResult(failures);
|
||||
@@ -61,7 +60,7 @@ namespace NzbDrone.Core.Applications.Lidarr
|
||||
public override List<AppIndexerMap> GetIndexerMappings()
|
||||
{
|
||||
var indexers = _lidarrV1Proxy.GetIndexers(Settings)
|
||||
.Where(i => i.Implementation == "Newznab" || i.Implementation == "Torznab");
|
||||
.Where(i => i.Implementation is "Newznab" or "Torznab");
|
||||
|
||||
var mappings = new List<AppIndexerMap>();
|
||||
|
||||
@@ -174,7 +173,13 @@ namespace NzbDrone.Core.Applications.Lidarr
|
||||
{
|
||||
var cacheKey = $"{Settings.BaseUrl}";
|
||||
var schemas = _schemaCache.Get(cacheKey, () => _lidarrV1Proxy.GetIndexerSchema(Settings), TimeSpan.FromDays(7));
|
||||
var syncFields = new[] { "baseUrl", "apiPath", "apiKey", "categories", "minimumSeeders", "seedCriteria.seedRatio", "seedCriteria.seedTime", "seedCriteria.discographySeedTime" };
|
||||
var syncFields = new List<string> { "baseUrl", "apiPath", "apiKey", "categories", "minimumSeeders", "seedCriteria.seedRatio", "seedCriteria.seedTime", "seedCriteria.discographySeedTime" };
|
||||
|
||||
if (id == 0)
|
||||
{
|
||||
// Ensuring backward compatibility with older versions on first sync
|
||||
syncFields.AddRange(new List<string> { "earlyReleaseLimit", "additionalParameters" });
|
||||
}
|
||||
|
||||
var newznab = schemas.First(i => i.Implementation == "Newznab");
|
||||
var torznab = schemas.First(i => i.Implementation == "Torznab");
|
||||
|
||||
@@ -23,8 +23,11 @@ namespace NzbDrone.Core.Applications.Lidarr
|
||||
|
||||
public class LidarrV1Proxy : ILidarrV1Proxy
|
||||
{
|
||||
private static Version MinimumApplicationVersion => new (1, 0, 2, 0);
|
||||
|
||||
private const string AppApiRoute = "/api/v1";
|
||||
private const string AppIndexerApiRoute = $"{AppApiRoute}/indexer";
|
||||
|
||||
private readonly IHttpClient _httpClient;
|
||||
private readonly Logger _logger;
|
||||
|
||||
@@ -102,7 +105,17 @@ namespace NzbDrone.Core.Applications.Lidarr
|
||||
|
||||
try
|
||||
{
|
||||
Execute<LidarrIndexer>(request);
|
||||
var applicationVersion = _httpClient.Post<LidarrIndexer>(request).Headers.GetSingleValue("X-Application-Version");
|
||||
|
||||
if (applicationVersion == null)
|
||||
{
|
||||
return new ValidationFailure(string.Empty, "Failed to fetch Lidarr version");
|
||||
}
|
||||
|
||||
if (new Version(applicationVersion) < MinimumApplicationVersion)
|
||||
{
|
||||
return new ValidationFailure(string.Empty, $"Lidarr version should be at least {MinimumApplicationVersion.ToString(3)}. Version reported is {applicationVersion}", applicationVersion);
|
||||
}
|
||||
}
|
||||
catch (HttpException ex)
|
||||
{
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using FluentValidation.Results;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using NLog;
|
||||
@@ -49,10 +48,10 @@ namespace NzbDrone.Core.Applications.Radarr
|
||||
{
|
||||
failures.AddIfNotNull(_radarrV3Proxy.TestConnection(BuildRadarrIndexer(testIndexer, DownloadProtocol.Usenet), Settings));
|
||||
}
|
||||
catch (WebException ex)
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.Error(ex, "Unable to send test message");
|
||||
failures.AddIfNotNull(new ValidationFailure("BaseUrl", "Unable to complete application test, cannot connect to Radarr"));
|
||||
failures.AddIfNotNull(new ValidationFailure("BaseUrl", $"Unable to complete application test, cannot connect to Radarr. {ex.Message}"));
|
||||
}
|
||||
|
||||
return new ValidationResult(failures);
|
||||
@@ -61,7 +60,7 @@ namespace NzbDrone.Core.Applications.Radarr
|
||||
public override List<AppIndexerMap> GetIndexerMappings()
|
||||
{
|
||||
var indexers = _radarrV3Proxy.GetIndexers(Settings)
|
||||
.Where(i => i.Implementation == "Newznab" || i.Implementation == "Torznab");
|
||||
.Where(i => i.Implementation is "Newznab" or "Torznab");
|
||||
|
||||
var mappings = new List<AppIndexerMap>();
|
||||
|
||||
@@ -174,7 +173,13 @@ namespace NzbDrone.Core.Applications.Radarr
|
||||
{
|
||||
var cacheKey = $"{Settings.BaseUrl}";
|
||||
var schemas = _schemaCache.Get(cacheKey, () => _radarrV3Proxy.GetIndexerSchema(Settings), TimeSpan.FromDays(7));
|
||||
var syncFields = new[] { "baseUrl", "apiPath", "apiKey", "categories", "minimumSeeders", "seedCriteria.seedRatio", "seedCriteria.seedTime" };
|
||||
var syncFields = new List<string> { "baseUrl", "apiPath", "apiKey", "categories", "minimumSeeders", "seedCriteria.seedRatio", "seedCriteria.seedTime" };
|
||||
|
||||
if (id == 0)
|
||||
{
|
||||
// Ensuring backward compatibility with older versions on first sync
|
||||
syncFields.AddRange(new List<string> { "multiLanguages", "removeYear", "requiredFlags", "additionalParameters" });
|
||||
}
|
||||
|
||||
var newznab = schemas.First(i => i.Implementation == "Newznab");
|
||||
var torznab = schemas.First(i => i.Implementation == "Torznab");
|
||||
|
||||
@@ -23,8 +23,12 @@ namespace NzbDrone.Core.Applications.Radarr
|
||||
|
||||
public class RadarrV3Proxy : IRadarrV3Proxy
|
||||
{
|
||||
private static Version MinimumApplicationV4Version => new (4, 0, 4, 0);
|
||||
private static Version MinimumApplicationV3Version => new (3, 1, 1, 0);
|
||||
|
||||
private const string AppApiRoute = "/api/v3";
|
||||
private const string AppIndexerApiRoute = $"{AppApiRoute}/indexer";
|
||||
|
||||
private readonly IHttpClient _httpClient;
|
||||
private readonly Logger _logger;
|
||||
|
||||
@@ -102,7 +106,29 @@ namespace NzbDrone.Core.Applications.Radarr
|
||||
|
||||
try
|
||||
{
|
||||
Execute<RadarrIndexer>(request);
|
||||
var applicationVersion = _httpClient.Post<RadarrIndexer>(request).Headers.GetSingleValue("X-Application-Version");
|
||||
|
||||
if (applicationVersion == null)
|
||||
{
|
||||
return new ValidationFailure(string.Empty, "Failed to fetch Radarr version");
|
||||
}
|
||||
|
||||
var version = new Version(applicationVersion);
|
||||
|
||||
if (version.Major == 3)
|
||||
{
|
||||
if (version < MinimumApplicationV3Version)
|
||||
{
|
||||
return new ValidationFailure(string.Empty, $"Radarr version should be at least {MinimumApplicationV3Version.ToString(3)}. Version reported is {applicationVersion}", applicationVersion);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (version < MinimumApplicationV4Version)
|
||||
{
|
||||
return new ValidationFailure(string.Empty, $"Radarr version should be at least {MinimumApplicationV4Version.ToString(3)}. Version reported is {applicationVersion}", applicationVersion);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (HttpException ex)
|
||||
{
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using FluentValidation.Results;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using NLog;
|
||||
@@ -49,10 +48,10 @@ namespace NzbDrone.Core.Applications.Sonarr
|
||||
{
|
||||
failures.AddIfNotNull(_sonarrV3Proxy.TestConnection(BuildSonarrIndexer(testIndexer, DownloadProtocol.Usenet), Settings));
|
||||
}
|
||||
catch (WebException ex)
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.Error(ex, "Unable to send test message");
|
||||
failures.AddIfNotNull(new ValidationFailure("BaseUrl", "Unable to complete application test, cannot connect to Sonarr"));
|
||||
failures.AddIfNotNull(new ValidationFailure("BaseUrl", $"Unable to complete application test, cannot connect to Sonarr. {ex.Message}"));
|
||||
}
|
||||
|
||||
return new ValidationResult(failures);
|
||||
@@ -61,7 +60,7 @@ namespace NzbDrone.Core.Applications.Sonarr
|
||||
public override List<AppIndexerMap> GetIndexerMappings()
|
||||
{
|
||||
var indexers = _sonarrV3Proxy.GetIndexers(Settings)
|
||||
.Where(i => i.Implementation == "Newznab" || i.Implementation == "Torznab");
|
||||
.Where(i => i.Implementation is "Newznab" or "Torznab");
|
||||
|
||||
var mappings = new List<AppIndexerMap>();
|
||||
|
||||
@@ -176,7 +175,13 @@ namespace NzbDrone.Core.Applications.Sonarr
|
||||
{
|
||||
var cacheKey = $"{Settings.BaseUrl}";
|
||||
var schemas = _schemaCache.Get(cacheKey, () => _sonarrV3Proxy.GetIndexerSchema(Settings), TimeSpan.FromDays(7));
|
||||
var syncFields = new[] { "baseUrl", "apiPath", "apiKey", "categories", "animeCategories", "minimumSeeders", "seedCriteria.seedRatio", "seedCriteria.seedTime", "seedCriteria.seasonPackSeedTime" };
|
||||
var syncFields = new List<string> { "baseUrl", "apiPath", "apiKey", "categories", "animeCategories", "minimumSeeders", "seedCriteria.seedRatio", "seedCriteria.seedTime", "seedCriteria.seasonPackSeedTime" };
|
||||
|
||||
if (id == 0)
|
||||
{
|
||||
// Ensuring backward compatibility with older versions on first sync
|
||||
syncFields.AddRange(new List<string> { "animeStandardFormatSearch", "additionalParameters" });
|
||||
}
|
||||
|
||||
var newznab = schemas.First(i => i.Implementation == "Newznab");
|
||||
var torznab = schemas.First(i => i.Implementation == "Torznab");
|
||||
|
||||
@@ -23,8 +23,11 @@ namespace NzbDrone.Core.Applications.Sonarr
|
||||
|
||||
public class SonarrV3Proxy : ISonarrV3Proxy
|
||||
{
|
||||
private static Version MinimumApplicationVersion => new (3, 0, 5, 0);
|
||||
|
||||
private const string AppApiRoute = "/api/v3";
|
||||
private const string AppIndexerApiRoute = $"{AppApiRoute}/indexer";
|
||||
|
||||
private readonly IHttpClient _httpClient;
|
||||
private readonly Logger _logger;
|
||||
|
||||
@@ -102,7 +105,17 @@ namespace NzbDrone.Core.Applications.Sonarr
|
||||
|
||||
try
|
||||
{
|
||||
Execute<SonarrIndexer>(request);
|
||||
var applicationVersion = _httpClient.Post<SonarrIndexer>(request).Headers.GetSingleValue("X-Application-Version");
|
||||
|
||||
if (applicationVersion == null)
|
||||
{
|
||||
return new ValidationFailure(string.Empty, "Failed to fetch Sonarr version");
|
||||
}
|
||||
|
||||
if (new Version(applicationVersion) < MinimumApplicationVersion)
|
||||
{
|
||||
return new ValidationFailure(string.Empty, $"Sonarr version should be at least {MinimumApplicationVersion.ToString(3)}. Version reported is {applicationVersion}", applicationVersion);
|
||||
}
|
||||
}
|
||||
catch (HttpException ex)
|
||||
{
|
||||
|
||||
@@ -109,11 +109,6 @@ namespace NzbDrone.Core.Download.Clients.DownloadStation
|
||||
return torrent.Status == DownloadStationTaskStatus.Finished;
|
||||
}
|
||||
|
||||
protected bool IsCompleted(DownloadStationTask torrent)
|
||||
{
|
||||
return torrent.Status == DownloadStationTaskStatus.Seeding || IsFinished(torrent) || (torrent.Status == DownloadStationTaskStatus.Waiting && torrent.Size != 0 && GetRemainingSize(torrent) <= 0);
|
||||
}
|
||||
|
||||
protected string GetMessage(DownloadStationTask torrent)
|
||||
{
|
||||
if (torrent.StatusExtra != null)
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using System.Net;
|
||||
using System.Net.Http;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using NLog;
|
||||
using NzbDrone.Common.Extensions;
|
||||
@@ -208,6 +209,10 @@ namespace NzbDrone.Core.Download.Clients.Sabnzbd
|
||||
{
|
||||
throw new DownloadClientException("Unable to connect to SABnzbd, {0}", ex, ex.Message);
|
||||
}
|
||||
catch (HttpRequestException ex)
|
||||
{
|
||||
throw new DownloadClientUnavailableException("Unable to connect to SABnzbd, {0}", ex, ex.Message);
|
||||
}
|
||||
catch (WebException ex)
|
||||
{
|
||||
if (ex.Status == WebExceptionStatus.TrustFailure)
|
||||
|
||||
@@ -14,7 +14,20 @@ namespace NzbDrone.Core.IndexerProxies
|
||||
|
||||
public Type ConfigContract => typeof(TSettings);
|
||||
|
||||
public IEnumerable<ProviderDefinition> DefaultDefinitions => new List<ProviderDefinition>();
|
||||
public IEnumerable<ProviderDefinition> DefaultDefinitions
|
||||
{
|
||||
get
|
||||
{
|
||||
var config = (IProviderConfig)new TSettings();
|
||||
|
||||
yield return new IndexerProxyDefinition
|
||||
{
|
||||
Name = GetType().Name,
|
||||
Implementation = GetType().Name,
|
||||
Settings = config
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
public ProviderDefinition Definition { get; set; }
|
||||
public abstract ValidationResult Test();
|
||||
|
||||
@@ -57,7 +57,7 @@ namespace NzbDrone.Core.IndexerSearch
|
||||
{
|
||||
var searchSpec = Get<MovieSearchCriteria>(request, indexerIds, interactiveSearch);
|
||||
|
||||
var imdbId = ParseUtil.GetImdbID(request.imdbid);
|
||||
var imdbId = ParseUtil.GetImdbId(request.imdbid);
|
||||
|
||||
searchSpec.ImdbId = imdbId?.ToString("D7");
|
||||
searchSpec.TmdbId = request.tmdbid;
|
||||
@@ -87,7 +87,7 @@ namespace NzbDrone.Core.IndexerSearch
|
||||
{
|
||||
var searchSpec = Get<TvSearchCriteria>(request, indexerIds, interactiveSearch);
|
||||
|
||||
var imdbId = ParseUtil.GetImdbID(request.imdbid);
|
||||
var imdbId = ParseUtil.GetImdbId(request.imdbid);
|
||||
|
||||
searchSpec.ImdbId = imdbId?.ToString("D7");
|
||||
searchSpec.Season = request.season;
|
||||
|
||||
@@ -210,8 +210,8 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
{
|
||||
var searchUrl = _settings.BaseUrl + "ajax/torrents_data.php";
|
||||
|
||||
// replace non-alphanumeric characters with % (wildcard)
|
||||
var searchString = Regex.Replace(term.Trim(), "[^a-zA-Z0-9]+", "%");
|
||||
// replace non-word characters with % (wildcard)
|
||||
var searchString = Regex.Replace(term.Trim(), @"[\W]+", "%");
|
||||
|
||||
var page = searchCriteria.Limit is > 0 && searchCriteria.Offset is > 0 ? (int)(searchCriteria.Offset / searchCriteria.Limit) + 1 : 1;
|
||||
|
||||
@@ -242,7 +242,7 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
yield return new IndexerRequest(requestBuilder.Build());
|
||||
}
|
||||
|
||||
private IEnumerable<string> GetTrackerCategories(string term, SearchCriteriaBase searchCriteria)
|
||||
private List<string> GetTrackerCategories(string term, SearchCriteriaBase searchCriteria)
|
||||
{
|
||||
var searchTerm = term.Trim();
|
||||
|
||||
|
||||
@@ -21,7 +21,7 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
{
|
||||
public override string Name => "Animedia";
|
||||
public override string[] IndexerUrls => new[] { "https://tt.animedia.tv/" };
|
||||
public override string Description => "Animedia is russian anime voiceover group and eponymous anime tracker.";
|
||||
public override string Description => "Animedia is RUSSIAN anime voiceover group and eponymous anime tracker.";
|
||||
public override string Language => "ru-RU";
|
||||
public override Encoding Encoding => Encoding.UTF8;
|
||||
public override DownloadProtocol Protocol => DownloadProtocol.Torrent;
|
||||
|
||||
@@ -246,7 +246,7 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
|
||||
// TODO: TMDb is also available
|
||||
var qImdb = row.QuerySelector("a[href^=\"https://www.imdb.com\"]");
|
||||
var imdb = qImdb != null ? ParseUtil.GetImdbID(qImdb.GetAttribute("href").Split('/').Last()) : null;
|
||||
var imdb = qImdb != null ? ParseUtil.GetImdbId(qImdb.GetAttribute("href").Split('/').Last()) : null;
|
||||
|
||||
var release = new TorrentInfo
|
||||
{
|
||||
|
||||
@@ -75,7 +75,7 @@ namespace NzbDrone.Core.Indexers.Definitions.Avistaz
|
||||
|
||||
if (row.MovieTvinfo != null)
|
||||
{
|
||||
release.ImdbId = ParseUtil.GetImdbID(row.MovieTvinfo.Imdb).GetValueOrDefault();
|
||||
release.ImdbId = ParseUtil.GetImdbId(row.MovieTvinfo.Imdb).GetValueOrDefault();
|
||||
release.TmdbId = row.MovieTvinfo.Tmdb.IsNullOrWhiteSpace() ? 0 : ParseUtil.TryCoerceInt(row.MovieTvinfo.Tmdb, out var tmdbResult) ? tmdbResult : 0;
|
||||
release.TvdbId = row.MovieTvinfo.Tvdb.IsNullOrWhiteSpace() ? 0 : ParseUtil.TryCoerceInt(row.MovieTvinfo.Tvdb, out var tvdbResult) ? tvdbResult : 0;
|
||||
}
|
||||
|
||||
@@ -254,6 +254,14 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
|
||||
foreach (var row in rows)
|
||||
{
|
||||
var downloadVolumeFactor = row.QuerySelector("span.freeleech") != null ? 0 : 1;
|
||||
|
||||
// Skip non-freeleech results when freeleech only is set
|
||||
if (_settings.FreeleechOnly && downloadVolumeFactor != 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var qTitleLink = row.QuerySelector("a.title, a.alt_title");
|
||||
if (qTitleLink == null)
|
||||
{
|
||||
@@ -357,7 +365,7 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
release.PublishDate = DateTime.ParseExact(dateStr, "dd MMM yy", CultureInfo.InvariantCulture);
|
||||
}
|
||||
|
||||
release.DownloadVolumeFactor = row.QuerySelector("span.freeleech") != null ? 0 : 1;
|
||||
release.DownloadVolumeFactor = downloadVolumeFactor;
|
||||
release.UploadVolumeFactor = 1;
|
||||
|
||||
releaseInfos.Add(release);
|
||||
@@ -400,13 +408,21 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
|
||||
public class BakaBTSettings : UserPassTorrentBaseSettings
|
||||
{
|
||||
[FieldDefinition(4, Label = "Add Romaji Title", Type = FieldType.Checkbox, HelpText = "Add releases for Romaji Title")]
|
||||
public BakaBTSettings()
|
||||
{
|
||||
FreeleechOnly = false;
|
||||
}
|
||||
|
||||
[FieldDefinition(4, Label = "Freeleech Only", Type = FieldType.Checkbox, HelpText = "Search freeleech torrents only")]
|
||||
public bool FreeleechOnly { get; set; }
|
||||
|
||||
[FieldDefinition(5, Label = "Add Romaji Title", Type = FieldType.Checkbox, HelpText = "Add releases for Romaji Title")]
|
||||
public bool AddRomajiTitle { get; set; }
|
||||
|
||||
[FieldDefinition(5, Label = "Append Season", Type = FieldType.Checkbox, HelpText = "Append Season for Sonarr Compatibility")]
|
||||
[FieldDefinition(6, Label = "Append Season", Type = FieldType.Checkbox, HelpText = "Append Season for Sonarr Compatibility")]
|
||||
public bool AppendSeason { get; set; }
|
||||
|
||||
[FieldDefinition(6, Label = "Adult Content", Type = FieldType.Checkbox, HelpText = "Allow Adult Content (Must be enabled in BakaBT settings)")]
|
||||
[FieldDefinition(7, Label = "Adult Content", Type = FieldType.Checkbox, HelpText = "Allow Adult Content (Must be enabled in BakaBT settings)")]
|
||||
public bool AdultContent { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -209,7 +209,7 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
|
||||
// BHD can return crazy values for tmdb
|
||||
var tmdbId = row.TmdbId.IsNullOrWhiteSpace() ? 0 : ParseUtil.TryCoerceInt(row.TmdbId.Split("/")[1], out var tmdbResult) ? tmdbResult : 0;
|
||||
var imdbId = ParseUtil.GetImdbID(row.ImdbId).GetValueOrDefault();
|
||||
var imdbId = ParseUtil.GetImdbId(row.ImdbId).GetValueOrDefault();
|
||||
|
||||
var release = new TorrentInfo
|
||||
{
|
||||
|
||||
@@ -192,7 +192,7 @@ public class GreatPosterWallParser : GazelleParser
|
||||
MinimumSeedTime = 172800 // 48 hours
|
||||
};
|
||||
|
||||
var imdbId = ParseUtil.GetImdbID(result.ImdbId);
|
||||
var imdbId = ParseUtil.GetImdbId(result.ImdbId);
|
||||
if (imdbId != null)
|
||||
{
|
||||
release.ImdbId = (int)imdbId;
|
||||
|
||||
@@ -20,7 +20,7 @@ namespace NzbDrone.Core.Indexers.Definitions.HDBits
|
||||
{
|
||||
var pageableRequests = new IndexerPageableRequestChain();
|
||||
var query = new TorrentQuery();
|
||||
var imdbId = ParseUtil.GetImdbID(searchCriteria.ImdbId).GetValueOrDefault(0);
|
||||
var imdbId = ParseUtil.GetImdbId(searchCriteria.ImdbId).GetValueOrDefault(0);
|
||||
|
||||
if (searchCriteria.Categories?.Length > 0)
|
||||
{
|
||||
@@ -78,7 +78,7 @@ namespace NzbDrone.Core.Indexers.Definitions.HDBits
|
||||
var pageableRequests = new IndexerPageableRequestChain();
|
||||
var query = new TorrentQuery();
|
||||
var tvdbId = searchCriteria.TvdbId.GetValueOrDefault(0);
|
||||
var imdbId = ParseUtil.GetImdbID(searchCriteria.ImdbId).GetValueOrDefault(0);
|
||||
var imdbId = ParseUtil.GetImdbId(searchCriteria.ImdbId).GetValueOrDefault(0);
|
||||
|
||||
if (searchCriteria.Categories?.Length > 0)
|
||||
{
|
||||
|
||||
@@ -282,7 +282,7 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
var imdbLink = row.QuerySelector("td:nth-child(2) a[href*=imdb]");
|
||||
if (imdbLink != null)
|
||||
{
|
||||
release.ImdbId = ParseUtil.GetImdbID(imdbLink.GetAttribute("href").Split('/').Last()).GetValueOrDefault();
|
||||
release.ImdbId = ParseUtil.GetImdbId(imdbLink.GetAttribute("href").Split('/').Last()).GetValueOrDefault();
|
||||
}
|
||||
|
||||
//"July 11, 2015, 13:34:09", "Today|Yesterday at 20:04:23"
|
||||
|
||||
@@ -328,7 +328,7 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
}
|
||||
|
||||
var imdbLink = row.QuerySelector("a[href*=\"www.imdb.com/title/\"]")?.GetAttribute("href");
|
||||
var imdb = !string.IsNullOrWhiteSpace(imdbLink) ? ParseUtil.GetImdbID(imdbLink) : null;
|
||||
var imdb = !string.IsNullOrWhiteSpace(imdbLink) ? ParseUtil.GetImdbId(imdbLink) : null;
|
||||
|
||||
var flags = new HashSet<IndexerFlag>();
|
||||
|
||||
|
||||
@@ -126,6 +126,28 @@ namespace NzbDrone.Core.Indexers.Headphones
|
||||
return results;
|
||||
}
|
||||
|
||||
protected override List<string> GetLanguages(XElement item)
|
||||
{
|
||||
var languages = TryGetMultipleNewznabAttributes(item, "language");
|
||||
var results = new List<string>();
|
||||
|
||||
// Try to find <language> elements for some indexers that suck at following the rules.
|
||||
if (languages.Count == 0)
|
||||
{
|
||||
languages = item.Elements("language").Select(e => e.Value).ToList();
|
||||
}
|
||||
|
||||
foreach (var language in languages)
|
||||
{
|
||||
if (language.IsNotNullOrWhiteSpace())
|
||||
{
|
||||
results.Add(language);
|
||||
}
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
protected override long GetSize(XElement item)
|
||||
{
|
||||
long size;
|
||||
|
||||
@@ -154,6 +154,28 @@ namespace NzbDrone.Core.Indexers.Newznab
|
||||
return results;
|
||||
}
|
||||
|
||||
protected override List<string> GetLanguages(XElement item)
|
||||
{
|
||||
var languages = TryGetMultipleNewznabAttributes(item, "language");
|
||||
var results = new List<string>();
|
||||
|
||||
// Try to find <language> elements for some indexers that suck at following the rules.
|
||||
if (languages.Count == 0)
|
||||
{
|
||||
languages = item.Elements("language").Select(e => e.Value).ToList();
|
||||
}
|
||||
|
||||
foreach (var language in languages)
|
||||
{
|
||||
if (language.IsNotNullOrWhiteSpace())
|
||||
{
|
||||
results.Add(language);
|
||||
}
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
protected override long GetSize(XElement item)
|
||||
{
|
||||
long size;
|
||||
|
||||
@@ -313,7 +313,7 @@ public class NorBitsParser : IParseIndexerResponse
|
||||
}
|
||||
|
||||
var imdbLink = row.QuerySelector("a[href*=\"imdb.com/title/tt\"]")?.GetAttribute("href");
|
||||
release.ImdbId = ParseUtil.GetImdbID(imdbLink) ?? 0;
|
||||
release.ImdbId = ParseUtil.GetImdbId(imdbLink) ?? 0;
|
||||
|
||||
if (row.QuerySelector("img[title=\"100% freeleech\"]") != null)
|
||||
{
|
||||
|
||||
@@ -180,7 +180,7 @@ public class PixelHDParser : IParseIndexerResponse
|
||||
var groupName = group.QuerySelector("strong:has(a[title=\"View Torrent\"])")?.TextContent.Replace(" ]", "]");
|
||||
|
||||
var imdbLink = group.QuerySelector("a[href*=\"imdb.com/title/tt\"]")?.GetAttribute("href");
|
||||
var imdbId = ParseUtil.GetImdbID(imdbLink) ?? 0;
|
||||
var imdbId = ParseUtil.GetImdbId(imdbLink) ?? 0;
|
||||
|
||||
var rows = group.QuerySelectorAll("tr.group_torrent:has(a[href^=\"torrents.php?id=\"])");
|
||||
foreach (var row in rows)
|
||||
|
||||
@@ -286,7 +286,7 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
var category = row.QuerySelector(".br_type > a").GetAttribute("href").Replace("browse.php?cat=", string.Empty);
|
||||
|
||||
var qImdb = row.QuerySelector("a[href*=\"www.imdb.com/\"]");
|
||||
var imdb = qImdb != null ? ParseUtil.GetImdbID(qImdb.GetAttribute("href").Split('/').Last()) : null;
|
||||
var imdb = qImdb != null ? ParseUtil.GetImdbId(qImdb.GetAttribute("href").Split('/').Last()) : null;
|
||||
|
||||
var release = new TorrentInfo
|
||||
{
|
||||
|
||||
@@ -104,7 +104,11 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
|
||||
if (!response.Content.Contains("id=\"logged-in-username\""))
|
||||
{
|
||||
throw new IndexerAuthException("RuTracker Auth Failed");
|
||||
var parser = new HtmlParser();
|
||||
var document = await parser.ParseDocumentAsync(response.Content);
|
||||
var errorMessage = document.QuerySelector("h4.warnColor1.tCenter.mrg_16, div.msg-main")?.TextContent.Trim();
|
||||
|
||||
throw new IndexerAuthException(errorMessage ?? "RuTracker Auth Failed");
|
||||
}
|
||||
|
||||
cookies = response.GetCookies();
|
||||
|
||||
@@ -213,7 +213,7 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
Files = item.Value<int>("numfiles"),
|
||||
Seeders = item.Value<int>("seeders"),
|
||||
Peers = item.Value<int>("leechers") + item.Value<int>("seeders"),
|
||||
ImdbId = ParseUtil.GetImdbID(item.Value<string>("imdbid")) ?? 0,
|
||||
ImdbId = ParseUtil.GetImdbId(item.Value<string>("imdbid")) ?? 0,
|
||||
MinimumRatio = 1,
|
||||
MinimumSeedTime = 0,
|
||||
DownloadVolumeFactor = dlVolumeFactor,
|
||||
|
||||
@@ -87,25 +87,26 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
caps.Categories.AddCategoryMapping(57, NewznabStandardCategory.MoviesSD, "Movies SD");
|
||||
caps.Categories.AddCategoryMapping(59, NewznabStandardCategory.MoviesHD, "Movies HD");
|
||||
caps.Categories.AddCategoryMapping(64, NewznabStandardCategory.Movies3D, "Movies 3D");
|
||||
caps.Categories.AddCategoryMapping(82, NewznabStandardCategory.MoviesOther, "Movies CAM-TS");
|
||||
caps.Categories.AddCategoryMapping(82, NewznabStandardCategory.MoviesOther, "Movies CAM/TS");
|
||||
caps.Categories.AddCategoryMapping(16, NewznabStandardCategory.MoviesUHD, "Movies UHD");
|
||||
caps.Categories.AddCategoryMapping(2, NewznabStandardCategory.TVUHD, "TV UHD");
|
||||
caps.Categories.AddCategoryMapping(43, NewznabStandardCategory.TV, "TV Packs");
|
||||
caps.Categories.AddCategoryMapping(9, NewznabStandardCategory.TVHD, "TV HD");
|
||||
caps.Categories.AddCategoryMapping(77, NewznabStandardCategory.TVSD, "TV SD");
|
||||
caps.Categories.AddCategoryMapping(6, NewznabStandardCategory.PCGames, "Games PC ISO");
|
||||
caps.Categories.AddCategoryMapping(1, NewznabStandardCategory.TVAnime, "TV ANIME");
|
||||
caps.Categories.AddCategoryMapping(6, NewznabStandardCategory.PCGames, "Games PC-ISO");
|
||||
caps.Categories.AddCategoryMapping(48, NewznabStandardCategory.ConsoleXBox, "Games XBOX");
|
||||
caps.Categories.AddCategoryMapping(51, NewznabStandardCategory.ConsoleWii, "Games Wii");
|
||||
caps.Categories.AddCategoryMapping(55, NewznabStandardCategory.ConsoleNDS, "Games Nintendo DS");
|
||||
caps.Categories.AddCategoryMapping(12, NewznabStandardCategory.ConsolePS4, "Games/PS");
|
||||
caps.Categories.AddCategoryMapping(55, NewznabStandardCategory.ConsoleNDS, "Games Nintendo");
|
||||
caps.Categories.AddCategoryMapping(12, NewznabStandardCategory.ConsolePS4, "Games PS");
|
||||
caps.Categories.AddCategoryMapping(15, NewznabStandardCategory.ConsoleOther, "Games Dreamcast");
|
||||
caps.Categories.AddCategoryMapping(52, NewznabStandardCategory.PCMac, "Mac/Linux");
|
||||
caps.Categories.AddCategoryMapping(53, NewznabStandardCategory.PC0day, "Apps");
|
||||
caps.Categories.AddCategoryMapping(24, NewznabStandardCategory.PCMobileOther, "Mobile Apps");
|
||||
caps.Categories.AddCategoryMapping(7, NewznabStandardCategory.Books, "Books and Magazines");
|
||||
caps.Categories.AddCategoryMapping(65, NewznabStandardCategory.BooksComics, "Books Comic");
|
||||
caps.Categories.AddCategoryMapping(65, NewznabStandardCategory.BooksComics, "Books Comics");
|
||||
caps.Categories.AddCategoryMapping(4, NewznabStandardCategory.Audio, "Music");
|
||||
caps.Categories.AddCategoryMapping(116, NewznabStandardCategory.Audio, "Music Pack");
|
||||
caps.Categories.AddCategoryMapping(116, NewznabStandardCategory.Audio, "Music Packs");
|
||||
|
||||
caps.Flags = new List<IndexerFlag>
|
||||
{
|
||||
|
||||
@@ -262,7 +262,7 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
Title = CleanTitle(torrent.Name),
|
||||
Description = torrent.ShortDescription,
|
||||
Size = torrent.Size,
|
||||
ImdbId = ParseUtil.GetImdbID(torrent.ImdbId).GetValueOrDefault(),
|
||||
ImdbId = ParseUtil.GetImdbId(torrent.ImdbId).GetValueOrDefault(),
|
||||
DownloadUrl = $"{_settings.BaseUrl}api/torrent/{torrent.Id}/download",
|
||||
PosterUrl = torrent.Poster,
|
||||
InfoUrl = torrent.Url,
|
||||
|
||||
@@ -244,7 +244,7 @@ namespace NzbDrone.Core.Indexers.Definitions
|
||||
var downloadMultiplier = (double?)row["download-multiplier"] ?? 1;
|
||||
var link = new Uri(_settings.BaseUrl + "download.php/" + torrentId + "/" + torrentId + ".torrent");
|
||||
var publishDate = DateTimeUtil.UnixTimestampToDateTime((long)row.ctime).ToLocalTime();
|
||||
var imdb = ParseUtil.GetImdbID(imdbId) ?? 0;
|
||||
var imdb = ParseUtil.GetImdbId(imdbId) ?? 0;
|
||||
|
||||
var release = new TorrentInfo
|
||||
{
|
||||
|
||||
@@ -125,6 +125,28 @@ namespace NzbDrone.Core.Indexers.Torznab
|
||||
return ParseUrl(item.TryGetValue("comments"));
|
||||
}
|
||||
|
||||
protected override List<string> GetLanguages(XElement item)
|
||||
{
|
||||
var languages = TryGetMultipleTorznabAttributes(item, "language");
|
||||
var results = new List<string>();
|
||||
|
||||
// Try to find <language> elements for some indexers that suck at following the rules.
|
||||
if (languages.Count == 0)
|
||||
{
|
||||
languages = item.Elements("language").Select(e => e.Value).ToList();
|
||||
}
|
||||
|
||||
foreach (var language in languages)
|
||||
{
|
||||
if (language.IsNotNullOrWhiteSpace())
|
||||
{
|
||||
results.Add(language);
|
||||
}
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
protected override long GetSize(XElement item)
|
||||
{
|
||||
long size;
|
||||
|
||||
@@ -60,7 +60,7 @@ namespace NzbDrone.Core.Indexers.Definitions.UNIT3D
|
||||
Files = row.Attributes.Files,
|
||||
Grabs = row.Attributes.Grabs,
|
||||
Seeders = row.Attributes.Seeders,
|
||||
ImdbId = ParseUtil.GetImdbID(row.Attributes.ImdbId).GetValueOrDefault(),
|
||||
ImdbId = ParseUtil.GetImdbId(row.Attributes.ImdbId).GetValueOrDefault(),
|
||||
TmdbId = row.Attributes.TmdbId.IsNullOrWhiteSpace() ? 0 : ParseUtil.CoerceInt(row.Attributes.TmdbId),
|
||||
TvdbId = row.Attributes.TvdbId.IsNullOrWhiteSpace() ? 0 : ParseUtil.CoerceInt(row.Attributes.TvdbId),
|
||||
Peers = row.Attributes.Leechers + row.Attributes.Seeders,
|
||||
|
||||
@@ -418,7 +418,7 @@ namespace NzbDrone.Core.Indexers
|
||||
|
||||
if (releases.Count == 0)
|
||||
{
|
||||
_logger.Trace(response.Content);
|
||||
_logger.Trace("No releases found. Response: {0}", response.Content);
|
||||
}
|
||||
|
||||
return new IndexerQueryResult
|
||||
|
||||
@@ -166,6 +166,7 @@ namespace NzbDrone.Core.Indexers
|
||||
releaseInfo.InfoUrl = GetInfoUrl(item);
|
||||
releaseInfo.CommentUrl = GetCommentUrl(item);
|
||||
releaseInfo.Categories = GetCategory(item);
|
||||
releaseInfo.Languages = GetLanguages(item);
|
||||
|
||||
try
|
||||
{
|
||||
@@ -237,6 +238,11 @@ namespace NzbDrone.Core.Indexers
|
||||
return ParseUrl((string)item.Element("comments"));
|
||||
}
|
||||
|
||||
protected virtual List<string> GetLanguages(XElement item)
|
||||
{
|
||||
return new List<string>();
|
||||
}
|
||||
|
||||
protected virtual long GetSize(XElement item)
|
||||
{
|
||||
if (UseEnclosureLength)
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
"Events": "Événements",
|
||||
"Edit": "Éditer",
|
||||
"DownloadClientStatusCheckAllClientMessage": "Aucun client de téléchargement n'est disponible en raison d'échecs",
|
||||
"DownloadClients": "Clients téléchargement",
|
||||
"DownloadClients": "Clients télécharg.",
|
||||
"Dates": "Dates",
|
||||
"Date": "Date",
|
||||
"Delete": "Supprimer",
|
||||
|
||||
@@ -10,5 +10,11 @@
|
||||
"Apply": "Terapkan",
|
||||
"Authentication": "Autentikasi",
|
||||
"About": "Tentang",
|
||||
"Add": "Tambah"
|
||||
"Add": "Tambah",
|
||||
"AppDataDirectory": "Direktori AppData",
|
||||
"Automatic": "Otomatis",
|
||||
"AutomaticSearch": "Penelusuran Otomatis",
|
||||
"BackupNow": "Cadangkan Sekarang",
|
||||
"Cancel": "Batal",
|
||||
"ChangeHasNotBeenSavedYet": "Perubahan belum disimpan"
|
||||
}
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
using System;
|
||||
|
||||
namespace NzbDrone.Core.Notifications.Signal
|
||||
{
|
||||
public class SignalInvalidResponseException : Exception
|
||||
{
|
||||
public SignalInvalidResponseException()
|
||||
{
|
||||
}
|
||||
|
||||
public SignalInvalidResponseException(string message)
|
||||
: base(message)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
48
src/NzbDrone.Core/Notifications/Signal/Signal.cs
Normal file
48
src/NzbDrone.Core/Notifications/Signal/Signal.cs
Normal file
@@ -0,0 +1,48 @@
|
||||
using System.Collections.Generic;
|
||||
using FluentValidation.Results;
|
||||
using NzbDrone.Common.Extensions;
|
||||
|
||||
namespace NzbDrone.Core.Notifications.Signal
|
||||
{
|
||||
public class Signal : NotificationBase<SignalSettings>
|
||||
{
|
||||
private readonly ISignalProxy _proxy;
|
||||
|
||||
public Signal(ISignalProxy proxy)
|
||||
{
|
||||
_proxy = proxy;
|
||||
}
|
||||
|
||||
public override string Name => "Signal";
|
||||
public override string Link => "https://signal.org/";
|
||||
|
||||
public override void OnGrab(GrabMessage grabMessage)
|
||||
{
|
||||
_proxy.SendNotification(RELEASE_GRABBED_TITLE_BRANDED, grabMessage.Message, Settings);
|
||||
}
|
||||
|
||||
public override void OnHealthIssue(HealthCheck.HealthCheck healthCheck)
|
||||
{
|
||||
_proxy.SendNotification(HEALTH_ISSUE_TITLE_BRANDED, healthCheck.Message, Settings);
|
||||
}
|
||||
|
||||
public override void OnHealthRestored(HealthCheck.HealthCheck previousCheck)
|
||||
{
|
||||
_proxy.SendNotification(HEALTH_RESTORED_TITLE, $"The following issue is now resolved: {previousCheck.Message}", Settings);
|
||||
}
|
||||
|
||||
public override void OnApplicationUpdate(ApplicationUpdateMessage updateMessage)
|
||||
{
|
||||
_proxy.SendNotification(APPLICATION_UPDATE_TITLE_BRANDED, updateMessage.Message, Settings);
|
||||
}
|
||||
|
||||
public override ValidationResult Test()
|
||||
{
|
||||
var failures = new List<ValidationFailure>();
|
||||
|
||||
failures.AddIfNotNull(_proxy.Test(Settings));
|
||||
|
||||
return new ValidationResult(failures);
|
||||
}
|
||||
}
|
||||
}
|
||||
7
src/NzbDrone.Core/Notifications/Signal/SignalError.cs
Normal file
7
src/NzbDrone.Core/Notifications/Signal/SignalError.cs
Normal file
@@ -0,0 +1,7 @@
|
||||
namespace NzbDrone.Core.Notifications.Signal
|
||||
{
|
||||
public class SignalError
|
||||
{
|
||||
public string Error { get; set; }
|
||||
}
|
||||
}
|
||||
9
src/NzbDrone.Core/Notifications/Signal/SignalPayload.cs
Normal file
9
src/NzbDrone.Core/Notifications/Signal/SignalPayload.cs
Normal file
@@ -0,0 +1,9 @@
|
||||
namespace NzbDrone.Core.Notifications.Signal
|
||||
{
|
||||
public class SignalPayload
|
||||
{
|
||||
public string Message { get; set; }
|
||||
public string Number { get; set; }
|
||||
public string[] Recipients { get; set; }
|
||||
}
|
||||
}
|
||||
119
src/NzbDrone.Core/Notifications/Signal/SignalProxy.cs
Normal file
119
src/NzbDrone.Core/Notifications/Signal/SignalProxy.cs
Normal file
@@ -0,0 +1,119 @@
|
||||
using System;
|
||||
using System.Net;
|
||||
using System.Text;
|
||||
using FluentValidation.Results;
|
||||
using NLog;
|
||||
using NzbDrone.Common.Extensions;
|
||||
using NzbDrone.Common.Http;
|
||||
using NzbDrone.Common.Serializer;
|
||||
|
||||
namespace NzbDrone.Core.Notifications.Signal
|
||||
{
|
||||
public interface ISignalProxy
|
||||
{
|
||||
void SendNotification(string title, string message, SignalSettings settings);
|
||||
ValidationFailure Test(SignalSettings settings);
|
||||
}
|
||||
|
||||
public class SignalProxy : ISignalProxy
|
||||
{
|
||||
private readonly IHttpClient _httpClient;
|
||||
private readonly Logger _logger;
|
||||
|
||||
public SignalProxy(IHttpClient httpClient, Logger logger)
|
||||
{
|
||||
_httpClient = httpClient;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public void SendNotification(string title, string message, SignalSettings settings)
|
||||
{
|
||||
var text = new StringBuilder();
|
||||
text.AppendLine(title);
|
||||
text.AppendLine(message);
|
||||
|
||||
var urlSignalAPI = HttpRequestBuilder.BuildBaseUrl(
|
||||
settings.UseSsl,
|
||||
settings.Host,
|
||||
settings.Port,
|
||||
"/v2/send");
|
||||
|
||||
var requestBuilder = new HttpRequestBuilder(urlSignalAPI).Post();
|
||||
|
||||
if (settings.AuthUsername.IsNotNullOrWhiteSpace() && settings.AuthPassword.IsNotNullOrWhiteSpace())
|
||||
{
|
||||
requestBuilder.NetworkCredential = new BasicNetworkCredential(settings.AuthUsername, settings.AuthPassword);
|
||||
}
|
||||
|
||||
var request = requestBuilder.Build();
|
||||
|
||||
request.Headers.ContentType = "application/json";
|
||||
|
||||
var payload = new SignalPayload
|
||||
{
|
||||
Message = text.ToString(),
|
||||
Number = settings.SenderNumber,
|
||||
Recipients = new[] { settings.ReceiverId }
|
||||
};
|
||||
request.SetContent(payload.ToJson());
|
||||
_httpClient.Post(request);
|
||||
}
|
||||
|
||||
public ValidationFailure Test(SignalSettings settings)
|
||||
{
|
||||
try
|
||||
{
|
||||
const string title = "Test Notification";
|
||||
const string body = "This is a test message from Sonarr";
|
||||
|
||||
SendNotification(title, body, settings);
|
||||
}
|
||||
catch (WebException ex)
|
||||
{
|
||||
_logger.Error(ex, "Unable to send test message: {0}", ex.Message);
|
||||
return new ValidationFailure("Host", $"Unable to send test message: {ex.Message}");
|
||||
}
|
||||
catch (HttpException ex)
|
||||
{
|
||||
_logger.Error(ex, "Unable to send test message: {0}", ex.Message);
|
||||
|
||||
if (ex.Response.StatusCode == HttpStatusCode.BadRequest)
|
||||
{
|
||||
if (ex.Response.Content.ContainsIgnoreCase("400 The plain HTTP request was sent to HTTPS port"))
|
||||
{
|
||||
return new ValidationFailure("UseSsl", "SSL seems to be required");
|
||||
}
|
||||
|
||||
var error = Json.Deserialize<SignalError>(ex.Response.Content);
|
||||
|
||||
var property = "Host";
|
||||
|
||||
if (error.Error.ContainsIgnoreCase("Invalid group id"))
|
||||
{
|
||||
property = "ReceiverId";
|
||||
}
|
||||
else if (error.Error.ContainsIgnoreCase("Invalid account"))
|
||||
{
|
||||
property = "SenderNumber";
|
||||
}
|
||||
|
||||
return new ValidationFailure(property, $"Unable to send test message: {error.Error}");
|
||||
}
|
||||
|
||||
if (ex.Response.StatusCode == HttpStatusCode.Unauthorized)
|
||||
{
|
||||
return new ValidationFailure("AuthUsername", "Login/Password invalid");
|
||||
}
|
||||
|
||||
return new ValidationFailure("Host", $"Unable to send test message: {ex.Message}");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.Error(ex, "Unable to send test message: {0}", ex.Message);
|
||||
return new ValidationFailure("Host", $"Unable to send test message: {ex.Message}");
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
49
src/NzbDrone.Core/Notifications/Signal/SignalSettings.cs
Normal file
49
src/NzbDrone.Core/Notifications/Signal/SignalSettings.cs
Normal file
@@ -0,0 +1,49 @@
|
||||
using FluentValidation;
|
||||
using NzbDrone.Core.Annotations;
|
||||
using NzbDrone.Core.ThingiProvider;
|
||||
using NzbDrone.Core.Validation;
|
||||
|
||||
namespace NzbDrone.Core.Notifications.Signal
|
||||
{
|
||||
public class SignalSettingsValidator : AbstractValidator<SignalSettings>
|
||||
{
|
||||
public SignalSettingsValidator()
|
||||
{
|
||||
RuleFor(c => c.Host).NotEmpty();
|
||||
RuleFor(c => c.Port).NotEmpty();
|
||||
RuleFor(c => c.SenderNumber).NotEmpty();
|
||||
RuleFor(c => c.ReceiverId).NotEmpty();
|
||||
}
|
||||
}
|
||||
|
||||
public class SignalSettings : IProviderConfig
|
||||
{
|
||||
private static readonly SignalSettingsValidator Validator = new ();
|
||||
|
||||
[FieldDefinition(0, Label = "Host", Type = FieldType.Textbox, HelpText = "localhost")]
|
||||
public string Host { get; set; }
|
||||
|
||||
[FieldDefinition(1, Label = "Port", Type = FieldType.Textbox, HelpText = "8080")]
|
||||
public int Port { get; set; }
|
||||
|
||||
[FieldDefinition(2, Label = "Use SSL", Type = FieldType.Checkbox, HelpText = "Use a secure connection.")]
|
||||
public bool UseSsl { get; set; }
|
||||
|
||||
[FieldDefinition(3, Label = "Sender Number", Privacy = PrivacyLevel.ApiKey, HelpText = "Phone number of the sender register in signal-api")]
|
||||
public string SenderNumber { get; set; }
|
||||
|
||||
[FieldDefinition(4, Label = "Group ID / PhoneNumber", HelpText = "GroupID / PhoneNumber of the receiver")]
|
||||
public string ReceiverId { get; set; }
|
||||
|
||||
[FieldDefinition(5, Label = "Login", Privacy = PrivacyLevel.UserName, HelpText = "Username used to authenticate requests toward signal-api")]
|
||||
public string AuthUsername { get; set; }
|
||||
|
||||
[FieldDefinition(6, Label = "Password", Type = FieldType.Password, Privacy = PrivacyLevel.Password, HelpText = "Password used to authenticate requests toward signal-api")]
|
||||
public string AuthPassword { get; set; }
|
||||
|
||||
public NzbDroneValidationResult Validate()
|
||||
{
|
||||
return new NzbDroneValidationResult(Validator.Validate(this));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -8,7 +8,7 @@ namespace NzbDrone.Core.Parser
|
||||
{
|
||||
public static class ParseUtil
|
||||
{
|
||||
private static readonly Regex ImdbId = new Regex(@"^(?:tt)?(\d{1,8})$", RegexOptions.Compiled);
|
||||
private static readonly Regex ImdbIdRegex = new (@"^(?:tt)?(\d{1,8})$", RegexOptions.Compiled);
|
||||
|
||||
public static string NormalizeMultiSpaces(string s) =>
|
||||
new Regex(@"\s+").Replace(s.Trim(), " ");
|
||||
@@ -85,14 +85,15 @@ namespace NzbDrone.Core.Parser
|
||||
return CoerceLong(extractedLong);
|
||||
}
|
||||
|
||||
public static int? GetImdbID(string imdbstr)
|
||||
public static int? GetImdbId(string value)
|
||||
{
|
||||
if (imdbstr == null)
|
||||
if (value == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var match = ImdbId.Match(imdbstr);
|
||||
var match = ImdbIdRegex.Match(value);
|
||||
|
||||
if (!match.Success)
|
||||
{
|
||||
return null;
|
||||
@@ -101,17 +102,16 @@ namespace NzbDrone.Core.Parser
|
||||
return int.Parse(match.Groups[1].Value, NumberStyles.Any, CultureInfo.InvariantCulture);
|
||||
}
|
||||
|
||||
public static string GetFullImdbId(string imdbstr)
|
||||
public static string GetFullImdbId(string value)
|
||||
{
|
||||
var imdbid = GetImdbID(imdbstr);
|
||||
if (imdbid == null)
|
||||
var imdbId = GetImdbId(value);
|
||||
|
||||
if (imdbId is null or 0)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var imdbLen = ((int)imdbid > 9999999) ? "D8" : "D7";
|
||||
|
||||
return "tt" + ((int)imdbid).ToString(imdbLen);
|
||||
return $"tt{imdbId.GetValueOrDefault():D7}";
|
||||
}
|
||||
|
||||
public static string GetArgumentFromQueryString(string url, string argument)
|
||||
|
||||
@@ -6,7 +6,7 @@ namespace NzbDrone.Core.Validation
|
||||
{
|
||||
public class FolderValidator : PropertyValidator
|
||||
{
|
||||
protected override string GetDefaultMessageTemplate() => "Invalid Path";
|
||||
protected override string GetDefaultMessageTemplate() => "Invalid Path: '{path}'";
|
||||
|
||||
protected override bool IsValid(PropertyValidatorContext context)
|
||||
{
|
||||
@@ -15,6 +15,8 @@ namespace NzbDrone.Core.Validation
|
||||
return false;
|
||||
}
|
||||
|
||||
context.MessageFormatter.AppendArgument("path", context.PropertyValue.ToString());
|
||||
|
||||
return context.PropertyValue.ToString().IsPathValid(PathValidationType.CurrentOs);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,8 +26,7 @@ namespace NzbDrone.Core.Validation
|
||||
{
|
||||
foreach (var item in list)
|
||||
{
|
||||
var extended = item as NzbDroneValidationFailure;
|
||||
if (extended != null && extended.IsWarning)
|
||||
if (item is NzbDroneValidationFailure { IsWarning: true })
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -24,7 +24,7 @@ namespace NzbDrone.Core.Validation
|
||||
CustomState = validationFailure.CustomState;
|
||||
var state = validationFailure.CustomState as NzbDroneValidationState;
|
||||
|
||||
IsWarning = state != null && state.IsWarning;
|
||||
IsWarning = state is { IsWarning: true };
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,8 +26,7 @@ namespace NzbDrone.Core.Validation
|
||||
|
||||
foreach (var failureBase in failures)
|
||||
{
|
||||
var failure = failureBase as NzbDroneValidationFailure;
|
||||
if (failure == null)
|
||||
if (failureBase is not NzbDroneValidationFailure failure)
|
||||
{
|
||||
failure = new NzbDroneValidationFailure(failureBase);
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@ namespace NzbDrone.Core.Validation.Paths
|
||||
_diskProvider = diskProvider;
|
||||
}
|
||||
|
||||
protected override string GetDefaultMessageTemplate() => "File does not exist";
|
||||
protected override string GetDefaultMessageTemplate() => "File '{file}' does not exist";
|
||||
|
||||
protected override bool IsValid(PropertyValidatorContext context)
|
||||
{
|
||||
@@ -21,6 +21,8 @@ namespace NzbDrone.Core.Validation.Paths
|
||||
return false;
|
||||
}
|
||||
|
||||
context.MessageFormatter.AppendArgument("file", context.PropertyValue.ToString());
|
||||
|
||||
return _diskProvider.FileExists(context.PropertyValue.ToString());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@ namespace NzbDrone.Core.Validation.Paths
|
||||
_diskProvider = diskProvider;
|
||||
}
|
||||
|
||||
protected override string GetDefaultMessageTemplate() => $"Folder is not writable by user {Environment.UserName}";
|
||||
protected override string GetDefaultMessageTemplate() => "Folder '{path}' is not writable by user '{user}'";
|
||||
|
||||
protected override bool IsValid(PropertyValidatorContext context)
|
||||
{
|
||||
@@ -22,6 +22,9 @@ namespace NzbDrone.Core.Validation.Paths
|
||||
return false;
|
||||
}
|
||||
|
||||
context.MessageFormatter.AppendArgument("path", context.PropertyValue.ToString());
|
||||
context.MessageFormatter.AppendArgument("user", Environment.UserName);
|
||||
|
||||
return _diskProvider.FolderWritable(context.PropertyValue.ToString());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@ namespace NzbDrone.Core.Validation.Paths
|
||||
_diskProvider = diskProvider;
|
||||
}
|
||||
|
||||
protected override string GetDefaultMessageTemplate() => "Path does not exist";
|
||||
protected override string GetDefaultMessageTemplate() => "Path '{path}' does not exist";
|
||||
|
||||
protected override bool IsValid(PropertyValidatorContext context)
|
||||
{
|
||||
@@ -21,6 +21,8 @@ namespace NzbDrone.Core.Validation.Paths
|
||||
return false;
|
||||
}
|
||||
|
||||
context.MessageFormatter.AppendArgument("path", context.PropertyValue.ToString());
|
||||
|
||||
return _diskProvider.FolderExists(context.PropertyValue.ToString());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@ namespace NzbDrone.Core.Validation.Paths
|
||||
|
||||
public class PathValidator : PropertyValidator
|
||||
{
|
||||
protected override string GetDefaultMessageTemplate() => "Invalid Path";
|
||||
protected override string GetDefaultMessageTemplate() => "Invalid Path: '{path}'";
|
||||
|
||||
protected override bool IsValid(PropertyValidatorContext context)
|
||||
{
|
||||
@@ -24,6 +24,8 @@ namespace NzbDrone.Core.Validation.Paths
|
||||
return false;
|
||||
}
|
||||
|
||||
context.MessageFormatter.AppendArgument("path", context.PropertyValue.ToString());
|
||||
|
||||
return context.PropertyValue.ToString().IsPathValid(PathValidationType.CurrentOs);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@ namespace NzbDrone.Core.Validation.Paths
|
||||
_appFolderInfo = appFolderInfo;
|
||||
}
|
||||
|
||||
protected override string GetDefaultMessageTemplate() => "Path cannot be {relationship} the start up folder";
|
||||
protected override string GetDefaultMessageTemplate() => "Path '{path}' cannot be {relationship} the start up folder";
|
||||
|
||||
protected override bool IsValid(PropertyValidatorContext context)
|
||||
{
|
||||
@@ -24,6 +24,7 @@ namespace NzbDrone.Core.Validation.Paths
|
||||
|
||||
var startupFolder = _appFolderInfo.StartUpFolder;
|
||||
var folder = context.PropertyValue.ToString();
|
||||
context.MessageFormatter.AppendArgument("path", folder);
|
||||
|
||||
if (startupFolder.PathEquals(folder))
|
||||
{
|
||||
|
||||
@@ -6,11 +6,12 @@ namespace NzbDrone.Core.Validation.Paths
|
||||
{
|
||||
public class SystemFolderValidator : PropertyValidator
|
||||
{
|
||||
protected override string GetDefaultMessageTemplate() => "Is {relationship} system folder {systemFolder}";
|
||||
protected override string GetDefaultMessageTemplate() => "Path '{path}' is {relationship} system folder {systemFolder}";
|
||||
|
||||
protected override bool IsValid(PropertyValidatorContext context)
|
||||
{
|
||||
var folder = context.PropertyValue.ToString();
|
||||
context.MessageFormatter.AppendArgument("path", folder);
|
||||
|
||||
foreach (var systemFolder in SystemFolders.GetSystemFolders())
|
||||
{
|
||||
|
||||
@@ -14,7 +14,7 @@ namespace NzbDrone.Core.Validation
|
||||
|
||||
public class UrlValidator : PropertyValidator
|
||||
{
|
||||
protected override string GetDefaultMessageTemplate() => "Invalid Url";
|
||||
protected override string GetDefaultMessageTemplate() => "Invalid Url: '{url}'";
|
||||
|
||||
protected override bool IsValid(PropertyValidatorContext context)
|
||||
{
|
||||
@@ -23,6 +23,8 @@ namespace NzbDrone.Core.Validation
|
||||
return false;
|
||||
}
|
||||
|
||||
context.MessageFormatter.AppendArgument("url", context.PropertyValue.ToString());
|
||||
|
||||
return context.PropertyValue.ToString().IsValidUrl();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@ using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using Microsoft.Extensions.Hosting.WindowsServices;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using NLog;
|
||||
using Npgsql;
|
||||
using NzbDrone.Common.Composition.Extensions;
|
||||
@@ -24,6 +25,7 @@ using NzbDrone.Common.Instrumentation;
|
||||
using NzbDrone.Common.Instrumentation.Extensions;
|
||||
using NzbDrone.Core.Configuration;
|
||||
using NzbDrone.Core.Datastore.Extensions;
|
||||
using LogLevel = Microsoft.Extensions.Logging.LogLevel;
|
||||
using PostgresOptions = NzbDrone.Core.Datastore.PostgresOptions;
|
||||
|
||||
namespace NzbDrone.Host
|
||||
@@ -141,6 +143,10 @@ namespace NzbDrone.Host
|
||||
return new HostBuilder()
|
||||
.UseContentRoot(Directory.GetCurrentDirectory())
|
||||
.UseServiceProviderFactory(new DryIocServiceProviderFactory(new Container(rules => rules.WithNzbDroneRules())))
|
||||
.ConfigureLogging(logging =>
|
||||
{
|
||||
logging.AddFilter("Microsoft.AspNetCore.Diagnostics.ExceptionHandlerMiddleware", LogLevel.None);
|
||||
})
|
||||
.ConfigureContainer<IContainer>(c =>
|
||||
{
|
||||
c.AutoAddServices(Bootstrap.ASSEMBLIES)
|
||||
|
||||
@@ -16,7 +16,7 @@ namespace NzbDrone.Integration.Test.ApiTests
|
||||
|
||||
indexers.Should().NotBeEmpty();
|
||||
indexers.Should().NotContain(c => string.IsNullOrWhiteSpace(c.Name));
|
||||
indexers.Where(c => c.ConfigContract == typeof(NullConfig).Name).Should().OnlyContain(c => c.Enable);
|
||||
indexers.Where(c => c.ConfigContract == nameof(NullConfig)).Should().OnlyContain(c => c.Enable);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -112,7 +112,7 @@ namespace Prowlarr.Api.V1.Commands
|
||||
{
|
||||
BroadcastResourceChange(ModelAction.Updated, pendingUpdate);
|
||||
|
||||
if (pendingUpdate.Name == typeof(MessagingCleanupCommand).Name.Replace("Command", "") &&
|
||||
if (pendingUpdate.Name == nameof(MessagingCleanupCommand).Replace("Command", "") &&
|
||||
pendingUpdate.Status == CommandStatus.Completed)
|
||||
{
|
||||
BroadcastResourceChange(ModelAction.Sync);
|
||||
|
||||
Reference in New Issue
Block a user