mirror of
https://github.com/Readarr/Readarr.git
synced 2026-04-17 21:25:39 -04:00
Compare commits
1 Commits
sonarr-pul
...
notifiarr-
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
09d423848d |
@@ -169,16 +169,6 @@ class HistoryRow extends Component {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (name === 'sourceTitle') {
|
|
||||||
return (
|
|
||||||
<TableRowCell
|
|
||||||
key={name}
|
|
||||||
>
|
|
||||||
{sourceTitle}
|
|
||||||
</TableRowCell>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (name === 'details') {
|
if (name === 'details') {
|
||||||
return (
|
return (
|
||||||
<TableRowCell
|
<TableRowCell
|
||||||
|
|||||||
@@ -21,7 +21,6 @@ function HostSettings(props) {
|
|||||||
port,
|
port,
|
||||||
urlBase,
|
urlBase,
|
||||||
instanceName,
|
instanceName,
|
||||||
applicationUrl,
|
|
||||||
enableSsl,
|
enableSsl,
|
||||||
sslPort,
|
sslPort,
|
||||||
sslCertPath,
|
sslCertPath,
|
||||||
@@ -97,21 +96,6 @@ function HostSettings(props) {
|
|||||||
/>
|
/>
|
||||||
</FormGroup>
|
</FormGroup>
|
||||||
|
|
||||||
<FormGroup
|
|
||||||
advancedSettings={advancedSettings}
|
|
||||||
isAdvanced={true}
|
|
||||||
>
|
|
||||||
<FormLabel>{translate('ApplicationURL')}</FormLabel>
|
|
||||||
|
|
||||||
<FormInputGroup
|
|
||||||
type={inputTypes.TEXT}
|
|
||||||
name="applicationUrl"
|
|
||||||
helpText={translate('ApplicationUrlHelpText')}
|
|
||||||
onChange={onInputChange}
|
|
||||||
{...applicationUrl}
|
|
||||||
/>
|
|
||||||
</FormGroup>
|
|
||||||
|
|
||||||
<FormGroup
|
<FormGroup
|
||||||
advancedSettings={advancedSettings}
|
advancedSettings={advancedSettings}
|
||||||
isAdvanced={true}
|
isAdvanced={true}
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ import getProviderState from 'Utilities/State/getProviderState';
|
|||||||
import { removeItem, set, updateItem } from '../baseActions';
|
import { removeItem, set, updateItem } from '../baseActions';
|
||||||
|
|
||||||
const abortCurrentRequests = {};
|
const abortCurrentRequests = {};
|
||||||
let lastSaveData = null;
|
|
||||||
|
|
||||||
export function createCancelSaveProviderHandler(section) {
|
export function createCancelSaveProviderHandler(section) {
|
||||||
return function(getState, payload, dispatch) {
|
return function(getState, payload, dispatch) {
|
||||||
@@ -28,33 +27,27 @@ function createSaveProviderHandler(section, url, options = {}, removeStale = fal
|
|||||||
} = payload;
|
} = payload;
|
||||||
|
|
||||||
const saveData = Array.isArray(id) ? id.map((x) => getProviderState({ id: x, ...otherPayload }, getState, section)) : getProviderState({ id, ...otherPayload }, getState, section);
|
const saveData = Array.isArray(id) ? id.map((x) => getProviderState({ id: x, ...otherPayload }, getState, section)) : getProviderState({ id, ...otherPayload }, getState, section);
|
||||||
const requestUrl = id ? `${url}/${id}` : url;
|
|
||||||
const params = { ...queryParams };
|
|
||||||
|
|
||||||
// If the user is re-saving the same provider without changes
|
|
||||||
// force it to be saved. Only applies to editing existing providers.
|
|
||||||
|
|
||||||
if (id && _.isEqual(saveData, lastSaveData)) {
|
|
||||||
params.forceSave = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
lastSaveData = saveData;
|
|
||||||
|
|
||||||
const ajaxOptions = {
|
const ajaxOptions = {
|
||||||
url: `${requestUrl}?${$.param(params, true)}`,
|
url: `${url}?${$.param(queryParams, true)}`,
|
||||||
method: id ? 'PUT' : 'POST',
|
method: 'POST',
|
||||||
contentType: 'application/json',
|
contentType: 'application/json',
|
||||||
dataType: 'json',
|
dataType: 'json',
|
||||||
data: JSON.stringify(saveData)
|
data: JSON.stringify(saveData)
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if (id) {
|
||||||
|
ajaxOptions.method = 'PUT';
|
||||||
|
if (!Array.isArray(id)) {
|
||||||
|
ajaxOptions.url = `${url}/${id}?${$.param(queryParams, true)}`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const { request, abortRequest } = createAjaxRequest(ajaxOptions);
|
const { request, abortRequest } = createAjaxRequest(ajaxOptions);
|
||||||
|
|
||||||
abortCurrentRequests[section] = abortRequest;
|
abortCurrentRequests[section] = abortRequest;
|
||||||
|
|
||||||
request.done((data) => {
|
request.done((data) => {
|
||||||
lastSaveData = null;
|
|
||||||
|
|
||||||
if (!Array.isArray(data)) {
|
if (!Array.isArray(data)) {
|
||||||
data = [data];
|
data = [data];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -71,11 +71,6 @@ export const defaultState = {
|
|||||||
label: 'Release Group',
|
label: 'Release Group',
|
||||||
isVisible: false
|
isVisible: false
|
||||||
},
|
},
|
||||||
{
|
|
||||||
name: 'sourceTitle',
|
|
||||||
label: 'Source Title',
|
|
||||||
isVisible: false
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
name: 'details',
|
name: 'details',
|
||||||
columnLabel: 'Details',
|
columnLabel: 'Details',
|
||||||
|
|||||||
@@ -29,9 +29,9 @@
|
|||||||
<PackageVersion Include="MonoTorrent" Version="2.0.6" />
|
<PackageVersion Include="MonoTorrent" Version="2.0.6" />
|
||||||
<PackageVersion Include="NBuilder" Version="6.1.0" />
|
<PackageVersion Include="NBuilder" Version="6.1.0" />
|
||||||
<PackageVersion Include="Newtonsoft.Json" Version="13.0.1" />
|
<PackageVersion Include="Newtonsoft.Json" Version="13.0.1" />
|
||||||
<PackageVersion Include="NLog.Extensions.Logging" Version="5.1.0" />
|
<PackageVersion Include="NLog.Extensions.Logging" Version="1.7.4" />
|
||||||
<PackageVersion Include="NLog" Version="5.0.5" />
|
<PackageVersion Include="NLog" Version="4.7.14" />
|
||||||
<PackageVersion Include="NLog.Targets.Syslog" Version="7.0.0" />
|
<PackageVersion Include="NLog.Targets.Syslog" Version="6.0.2" />
|
||||||
<PackageVersion Include="Npgsql" Version="6.0.3" />
|
<PackageVersion Include="Npgsql" Version="6.0.3" />
|
||||||
<PackageVersion Include="NUnit3TestAdapter" Version="4.2.1" />
|
<PackageVersion Include="NUnit3TestAdapter" Version="4.2.1" />
|
||||||
<PackageVersion Include="NUnit" Version="3.13.3" />
|
<PackageVersion Include="NUnit" Version="3.13.3" />
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using NzbDrone.Common.EnvironmentInfo;
|
using NzbDrone.Common.EnvironmentInfo;
|
||||||
|
|
||||||
@@ -24,8 +24,7 @@ namespace NzbDrone.Common.Disk
|
|||||||
"/boot",
|
"/boot",
|
||||||
"/lib",
|
"/lib",
|
||||||
"/sbin",
|
"/sbin",
|
||||||
"/proc",
|
"/proc"
|
||||||
"/usr/bin"
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
using System.Collections.Generic;
|
using System.Linq;
|
||||||
using System.Linq;
|
|
||||||
using NLog;
|
using NLog;
|
||||||
|
using NLog.Fluent;
|
||||||
|
|
||||||
namespace NzbDrone.Common.Instrumentation.Extensions
|
namespace NzbDrone.Common.Instrumentation.Extensions
|
||||||
{
|
{
|
||||||
@@ -8,46 +8,47 @@ namespace NzbDrone.Common.Instrumentation.Extensions
|
|||||||
{
|
{
|
||||||
public static readonly Logger SentryLogger = LogManager.GetLogger("Sentry");
|
public static readonly Logger SentryLogger = LogManager.GetLogger("Sentry");
|
||||||
|
|
||||||
public static LogEventBuilder SentryFingerprint(this LogEventBuilder logBuilder, params string[] fingerprint)
|
public static LogBuilder SentryFingerprint(this LogBuilder logBuilder, params string[] fingerprint)
|
||||||
{
|
{
|
||||||
return logBuilder.Property("Sentry", fingerprint);
|
return logBuilder.Property("Sentry", fingerprint);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static LogEventBuilder WriteSentryDebug(this LogEventBuilder logBuilder, params string[] fingerprint)
|
public static LogBuilder WriteSentryDebug(this LogBuilder logBuilder, params string[] fingerprint)
|
||||||
{
|
{
|
||||||
return LogSentryMessage(logBuilder, LogLevel.Debug, fingerprint);
|
return LogSentryMessage(logBuilder, LogLevel.Debug, fingerprint);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static LogEventBuilder WriteSentryInfo(this LogEventBuilder logBuilder, params string[] fingerprint)
|
public static LogBuilder WriteSentryInfo(this LogBuilder logBuilder, params string[] fingerprint)
|
||||||
{
|
{
|
||||||
return LogSentryMessage(logBuilder, LogLevel.Info, fingerprint);
|
return LogSentryMessage(logBuilder, LogLevel.Info, fingerprint);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static LogEventBuilder WriteSentryWarn(this LogEventBuilder logBuilder, params string[] fingerprint)
|
public static LogBuilder WriteSentryWarn(this LogBuilder logBuilder, params string[] fingerprint)
|
||||||
{
|
{
|
||||||
return LogSentryMessage(logBuilder, LogLevel.Warn, fingerprint);
|
return LogSentryMessage(logBuilder, LogLevel.Warn, fingerprint);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static LogEventBuilder WriteSentryError(this LogEventBuilder logBuilder, params string[] fingerprint)
|
public static LogBuilder WriteSentryError(this LogBuilder logBuilder, params string[] fingerprint)
|
||||||
{
|
{
|
||||||
return LogSentryMessage(logBuilder, LogLevel.Error, fingerprint);
|
return LogSentryMessage(logBuilder, LogLevel.Error, fingerprint);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static LogEventBuilder LogSentryMessage(LogEventBuilder logBuilder, LogLevel level, string[] fingerprint)
|
private static LogBuilder LogSentryMessage(LogBuilder logBuilder, LogLevel level, string[] fingerprint)
|
||||||
{
|
{
|
||||||
SentryLogger.ForLogEvent(level)
|
SentryLogger.Log(level)
|
||||||
.CopyLogEvent(logBuilder.LogEvent)
|
.CopyLogEvent(logBuilder.LogEventInfo)
|
||||||
.SentryFingerprint(fingerprint)
|
.SentryFingerprint(fingerprint)
|
||||||
.Log();
|
.Write();
|
||||||
|
|
||||||
return logBuilder.Property<string>("Sentry", null);
|
return logBuilder.Property("Sentry", null);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static LogEventBuilder CopyLogEvent(this LogEventBuilder logBuilder, LogEventInfo logEvent)
|
private static LogBuilder CopyLogEvent(this LogBuilder logBuilder, LogEventInfo logEvent)
|
||||||
{
|
{
|
||||||
return logBuilder.TimeStamp(logEvent.TimeStamp)
|
return logBuilder.LoggerName(logEvent.LoggerName)
|
||||||
|
.TimeStamp(logEvent.TimeStamp)
|
||||||
.Message(logEvent.Message, logEvent.Parameters)
|
.Message(logEvent.Message, logEvent.Parameters)
|
||||||
.Properties(logEvent.Properties.Select(p => new KeyValuePair<string, object>(p.Key.ToString(), p.Value)))
|
.Properties(logEvent.Properties.ToDictionary(v => v.Key, v => v.Value))
|
||||||
.Exception(logEvent.Exception);
|
.Exception(logEvent.Exception);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,15 +1,13 @@
|
|||||||
using System.Text;
|
using NLog;
|
||||||
using NLog;
|
|
||||||
using NLog.Targets;
|
using NLog.Targets;
|
||||||
|
|
||||||
namespace NzbDrone.Common.Instrumentation
|
namespace NzbDrone.Common.Instrumentation
|
||||||
{
|
{
|
||||||
public class NzbDroneFileTarget : FileTarget
|
public class NzbDroneFileTarget : FileTarget
|
||||||
{
|
{
|
||||||
protected override void RenderFormattedMessage(LogEventInfo logEvent, StringBuilder target)
|
protected override string GetFormattedMessage(LogEventInfo logEvent)
|
||||||
{
|
{
|
||||||
var result = CleanseLogMessage.Cleanse(Layout.Render(logEvent));
|
return CleanseLogMessage.Cleanse(Layout.Render(logEvent));
|
||||||
target.Append(result);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -34,8 +34,6 @@ namespace NzbDrone.Common.Instrumentation
|
|||||||
|
|
||||||
var appFolderInfo = new AppFolderInfo(startupContext);
|
var appFolderInfo = new AppFolderInfo(startupContext);
|
||||||
|
|
||||||
RegisterGlobalFilters();
|
|
||||||
|
|
||||||
if (Debugger.IsAttached)
|
if (Debugger.IsAttached)
|
||||||
{
|
{
|
||||||
RegisterDebugger();
|
RegisterDebugger();
|
||||||
@@ -103,16 +101,6 @@ namespace NzbDrone.Common.Instrumentation
|
|||||||
LogManager.Configuration.LoggingRules.Add(loggingRule);
|
LogManager.Configuration.LoggingRules.Add(loggingRule);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void RegisterGlobalFilters()
|
|
||||||
{
|
|
||||||
LogManager.Setup().LoadConfiguration(c =>
|
|
||||||
{
|
|
||||||
c.ForLogger("Microsoft.Hosting.Lifetime*").WriteToNil(LogLevel.Info);
|
|
||||||
c.ForLogger("System*").WriteToNil(LogLevel.Warn);
|
|
||||||
c.ForLogger("Microsoft*").WriteToNil(LogLevel.Warn);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void RegisterConsole()
|
private static void RegisterConsole()
|
||||||
{
|
{
|
||||||
var level = LogLevel.Trace;
|
var level = LogLevel.Trace;
|
||||||
|
|||||||
@@ -206,11 +206,7 @@ namespace NzbDrone.Common.Instrumentation.Sentry
|
|||||||
if (ex != null)
|
if (ex != null)
|
||||||
{
|
{
|
||||||
fingerPrint.Add(ex.GetType().FullName);
|
fingerPrint.Add(ex.GetType().FullName);
|
||||||
if (ex.TargetSite != null)
|
fingerPrint.Add(ex.TargetSite.ToString());
|
||||||
{
|
|
||||||
fingerPrint.Add(ex.TargetSite.ToString());
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ex.InnerException != null)
|
if (ex.InnerException != null)
|
||||||
{
|
{
|
||||||
fingerPrint.Add(ex.InnerException.GetType().FullName);
|
fingerPrint.Add(ex.InnerException.GetType().FullName);
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using FizzWare.NBuilder;
|
using FizzWare.NBuilder;
|
||||||
@@ -6,7 +5,6 @@ using FluentAssertions;
|
|||||||
using Moq;
|
using Moq;
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
using NzbDrone.Core.Books;
|
using NzbDrone.Core.Books;
|
||||||
using NzbDrone.Core.Configuration;
|
|
||||||
using NzbDrone.Core.DecisionEngine.Specifications;
|
using NzbDrone.Core.DecisionEngine.Specifications;
|
||||||
using NzbDrone.Core.MediaFiles;
|
using NzbDrone.Core.MediaFiles;
|
||||||
using NzbDrone.Core.Parser.Model;
|
using NzbDrone.Core.Parser.Model;
|
||||||
@@ -246,89 +244,5 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
|
|||||||
.Should()
|
.Should()
|
||||||
.BeFalse();
|
.BeFalse();
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
|
||||||
public void should_return_true_when_repacks_are_not_preferred()
|
|
||||||
{
|
|
||||||
Mocker.GetMock<IConfigService>()
|
|
||||||
.Setup(s => s.DownloadPropersAndRepacks)
|
|
||||||
.Returns(ProperDownloadTypes.DoNotPrefer);
|
|
||||||
|
|
||||||
_trackFiles.Select(c =>
|
|
||||||
{
|
|
||||||
c.ReleaseGroup = "";
|
|
||||||
return c;
|
|
||||||
}).ToList();
|
|
||||||
|
|
||||||
_trackFiles.Select(c =>
|
|
||||||
{
|
|
||||||
c.Quality = new QualityModel(Quality.FLAC);
|
|
||||||
return c;
|
|
||||||
}).ToList();
|
|
||||||
|
|
||||||
var remoteAlbum = Builder<RemoteBook>.CreateNew()
|
|
||||||
.With(e => e.ParsedBookInfo = _parsedBookInfo)
|
|
||||||
.With(e => e.Books = _books)
|
|
||||||
.Build();
|
|
||||||
|
|
||||||
Subject.IsSatisfiedBy(remoteAlbum, null).Accepted.Should().BeTrue();
|
|
||||||
}
|
|
||||||
|
|
||||||
[Test]
|
|
||||||
public void should_return_true_when_repack_but_auto_download_repacks_is_true()
|
|
||||||
{
|
|
||||||
Mocker.GetMock<IConfigService>()
|
|
||||||
.Setup(s => s.DownloadPropersAndRepacks)
|
|
||||||
.Returns(ProperDownloadTypes.PreferAndUpgrade);
|
|
||||||
|
|
||||||
_parsedBookInfo.Quality.Revision.IsRepack = true;
|
|
||||||
|
|
||||||
_trackFiles.Select(c =>
|
|
||||||
{
|
|
||||||
c.ReleaseGroup = "Readarr";
|
|
||||||
return c;
|
|
||||||
}).ToList();
|
|
||||||
|
|
||||||
_trackFiles.Select(c =>
|
|
||||||
{
|
|
||||||
c.Quality = new QualityModel(Quality.FLAC);
|
|
||||||
return c;
|
|
||||||
}).ToList();
|
|
||||||
|
|
||||||
var remoteAlbum = Builder<RemoteBook>.CreateNew()
|
|
||||||
.With(e => e.ParsedBookInfo = _parsedBookInfo)
|
|
||||||
.With(e => e.Books = _books)
|
|
||||||
.Build();
|
|
||||||
|
|
||||||
Subject.IsSatisfiedBy(remoteAlbum, null).Accepted.Should().BeTrue();
|
|
||||||
}
|
|
||||||
|
|
||||||
[Test]
|
|
||||||
public void should_return_false_when_repack_but_auto_download_repacks_is_false()
|
|
||||||
{
|
|
||||||
Mocker.GetMock<IConfigService>()
|
|
||||||
.Setup(s => s.DownloadPropersAndRepacks)
|
|
||||||
.Returns(ProperDownloadTypes.DoNotUpgrade);
|
|
||||||
|
|
||||||
_parsedBookInfo.Quality.Revision.IsRepack = true;
|
|
||||||
|
|
||||||
_trackFiles.Select(c =>
|
|
||||||
{
|
|
||||||
c.ReleaseGroup = "Readarr";
|
|
||||||
return c;
|
|
||||||
}).ToList();
|
|
||||||
_trackFiles.Select(c =>
|
|
||||||
{
|
|
||||||
c.Quality = new QualityModel(Quality.FLAC);
|
|
||||||
return c;
|
|
||||||
}).ToList();
|
|
||||||
|
|
||||||
var remoteAlbum = Builder<RemoteBook>.CreateNew()
|
|
||||||
.With(e => e.ParsedBookInfo = _parsedBookInfo)
|
|
||||||
.With(e => e.Books = _books)
|
|
||||||
.Build();
|
|
||||||
|
|
||||||
Subject.IsSatisfiedBy(remoteAlbum, null).Accepted.Should().BeFalse();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -410,8 +410,6 @@ namespace NzbDrone.Core.Configuration
|
|||||||
public CertificateValidationType CertificateValidation =>
|
public CertificateValidationType CertificateValidation =>
|
||||||
GetValueEnum("CertificateValidation", CertificateValidationType.Enabled);
|
GetValueEnum("CertificateValidation", CertificateValidationType.Enabled);
|
||||||
|
|
||||||
public string ApplicationUrl => GetValue("ApplicationUrl", string.Empty);
|
|
||||||
|
|
||||||
private string GetValue(string key)
|
private string GetValue(string key)
|
||||||
{
|
{
|
||||||
return GetValue(key, string.Empty);
|
return GetValue(key, string.Empty);
|
||||||
|
|||||||
@@ -97,6 +97,5 @@ namespace NzbDrone.Core.Configuration
|
|||||||
int BackupRetention { get; }
|
int BackupRetention { get; }
|
||||||
|
|
||||||
CertificateValidationType CertificateValidation { get; }
|
CertificateValidationType CertificateValidation { get; }
|
||||||
string ApplicationUrl { get; }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,11 +1,9 @@
|
|||||||
using System;
|
using System;
|
||||||
using NLog;
|
using NLog;
|
||||||
using NzbDrone.Common.Extensions;
|
using NzbDrone.Common.Extensions;
|
||||||
using NzbDrone.Core.Configuration;
|
|
||||||
using NzbDrone.Core.IndexerSearch.Definitions;
|
using NzbDrone.Core.IndexerSearch.Definitions;
|
||||||
using NzbDrone.Core.MediaFiles;
|
using NzbDrone.Core.MediaFiles;
|
||||||
using NzbDrone.Core.Parser.Model;
|
using NzbDrone.Core.Parser.Model;
|
||||||
using NzbDrone.Core.Qualities;
|
|
||||||
|
|
||||||
namespace NzbDrone.Core.DecisionEngine.Specifications
|
namespace NzbDrone.Core.DecisionEngine.Specifications
|
||||||
{
|
{
|
||||||
@@ -13,14 +11,12 @@ namespace NzbDrone.Core.DecisionEngine.Specifications
|
|||||||
{
|
{
|
||||||
private readonly IMediaFileService _mediaFileService;
|
private readonly IMediaFileService _mediaFileService;
|
||||||
private readonly UpgradableSpecification _upgradableSpecification;
|
private readonly UpgradableSpecification _upgradableSpecification;
|
||||||
private readonly IConfigService _configService;
|
|
||||||
private readonly Logger _logger;
|
private readonly Logger _logger;
|
||||||
|
|
||||||
public RepackSpecification(IMediaFileService mediaFileService, UpgradableSpecification upgradableSpecification, IConfigService configService, Logger logger)
|
public RepackSpecification(IMediaFileService mediaFileService, UpgradableSpecification upgradableSpecification, Logger logger)
|
||||||
{
|
{
|
||||||
_mediaFileService = mediaFileService;
|
_mediaFileService = mediaFileService;
|
||||||
_upgradableSpecification = upgradableSpecification;
|
_upgradableSpecification = upgradableSpecification;
|
||||||
_configService = configService;
|
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -34,14 +30,6 @@ namespace NzbDrone.Core.DecisionEngine.Specifications
|
|||||||
return Decision.Accept();
|
return Decision.Accept();
|
||||||
}
|
}
|
||||||
|
|
||||||
var downloadPropersAndRepacks = _configService.DownloadPropersAndRepacks;
|
|
||||||
|
|
||||||
if (downloadPropersAndRepacks == ProperDownloadTypes.DoNotPrefer)
|
|
||||||
{
|
|
||||||
_logger.Debug("Repacks are not preferred, skipping check");
|
|
||||||
return Decision.Accept();
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (var book in subject.Books)
|
foreach (var book in subject.Books)
|
||||||
{
|
{
|
||||||
var releaseGroup = subject.ParsedBookInfo.ReleaseGroup;
|
var releaseGroup = subject.ParsedBookInfo.ReleaseGroup;
|
||||||
@@ -51,12 +39,6 @@ namespace NzbDrone.Core.DecisionEngine.Specifications
|
|||||||
{
|
{
|
||||||
if (_upgradableSpecification.IsRevisionUpgrade(file.Quality, subject.ParsedBookInfo.Quality))
|
if (_upgradableSpecification.IsRevisionUpgrade(file.Quality, subject.ParsedBookInfo.Quality))
|
||||||
{
|
{
|
||||||
if (downloadPropersAndRepacks == ProperDownloadTypes.DoNotUpgrade)
|
|
||||||
{
|
|
||||||
_logger.Debug("Auto downloading of repacks is disabled");
|
|
||||||
return Decision.Reject("Repack downloading is disabled");
|
|
||||||
}
|
|
||||||
|
|
||||||
var fileReleaseGroup = file.ReleaseGroup;
|
var fileReleaseGroup = file.ReleaseGroup;
|
||||||
|
|
||||||
if (fileReleaseGroup.IsNullOrWhiteSpace())
|
if (fileReleaseGroup.IsNullOrWhiteSpace())
|
||||||
|
|||||||
@@ -125,22 +125,22 @@ namespace NzbDrone.Core.Download.Clients.Flood
|
|||||||
item.RemainingTime = TimeSpan.FromSeconds(properties.Eta);
|
item.RemainingTime = TimeSpan.FromSeconds(properties.Eta);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (properties.Status.Contains("seeding") || properties.Status.Contains("complete"))
|
if (properties.Status.Contains("error"))
|
||||||
{
|
|
||||||
item.Status = DownloadItemStatus.Completed;
|
|
||||||
}
|
|
||||||
else if (properties.Status.Contains("stopped"))
|
|
||||||
{
|
|
||||||
item.Status = DownloadItemStatus.Paused;
|
|
||||||
}
|
|
||||||
else if (properties.Status.Contains("error"))
|
|
||||||
{
|
{
|
||||||
item.Status = DownloadItemStatus.Warning;
|
item.Status = DownloadItemStatus.Warning;
|
||||||
}
|
}
|
||||||
|
else if (properties.Status.Contains("seeding") || properties.Status.Contains("complete"))
|
||||||
|
{
|
||||||
|
item.Status = DownloadItemStatus.Completed;
|
||||||
|
}
|
||||||
else if (properties.Status.Contains("downloading"))
|
else if (properties.Status.Contains("downloading"))
|
||||||
{
|
{
|
||||||
item.Status = DownloadItemStatus.Downloading;
|
item.Status = DownloadItemStatus.Downloading;
|
||||||
}
|
}
|
||||||
|
else if (properties.Status.Contains("stopped"))
|
||||||
|
{
|
||||||
|
item.Status = DownloadItemStatus.Paused;
|
||||||
|
}
|
||||||
|
|
||||||
if (item.Status == DownloadItemStatus.Completed)
|
if (item.Status == DownloadItemStatus.Completed)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -115,7 +115,7 @@ namespace NzbDrone.Core.Download.Clients.RTorrent
|
|||||||
|
|
||||||
public override string Name => "rTorrent";
|
public override string Name => "rTorrent";
|
||||||
|
|
||||||
public override ProviderMessage Message => new ProviderMessage($"rTorrent will not pause torrents when they meet the seed criteria. Readarr will handle automatic removal of torrents based on the current seed criteria in Settings->Indexers only when Remove Completed is enabled. After importing it will also set \"{_imported_view}\" as an rTorrent view, which can be used in rTorrent scripts to customize behavior.", ProviderMessageType.Info);
|
public override ProviderMessage Message => new ProviderMessage($"Readarr will handle automatic removal of torrents based on the current seed criteria in Settings->Indexers. After importing it will also set \"{_imported_view}\" as an rTorrent view, which can be used in rTorrent scripts to customize behavior.", ProviderMessageType.Info);
|
||||||
|
|
||||||
public override IEnumerable<DownloadClientItem> GetItems()
|
public override IEnumerable<DownloadClientItem> GetItems()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -37,10 +37,10 @@ namespace NzbDrone.Core.Download.Clients.RTorrent
|
|||||||
[FieldDefinition(1, Label = "Port", Type = FieldType.Textbox)]
|
[FieldDefinition(1, Label = "Port", Type = FieldType.Textbox)]
|
||||||
public int Port { get; set; }
|
public int Port { get; set; }
|
||||||
|
|
||||||
[FieldDefinition(2, Label = "Use SSL", Type = FieldType.Checkbox, HelpText = "Use secure connection when connecting to rTorrent")]
|
[FieldDefinition(2, Label = "Use SSL", Type = FieldType.Checkbox, HelpText = "Use secure connection when connecting to ruTorrent")]
|
||||||
public bool UseSsl { get; set; }
|
public bool UseSsl { get; set; }
|
||||||
|
|
||||||
[FieldDefinition(3, Label = "Url Path", Type = FieldType.Textbox, HelpText = "Path to the XMLRPC endpoint, see http(s)://[host]:[port]/[urlPath]. This is usually RPC2 or [path to ruTorrent]/plugins/rpc/rpc.php when using ruTorrent.")]
|
[FieldDefinition(3, Label = "Url Path", Type = FieldType.Textbox, HelpText = "Path to the XMLRPC endpoint, see http(s)://[host]:[port]/[urlPath]. When using ruTorrent this usually is RPC2 or (path to ruTorrent)/plugins/rpc/rpc.php")]
|
||||||
public string UrlBase { get; set; }
|
public string UrlBase { get; set; }
|
||||||
|
|
||||||
[FieldDefinition(4, Label = "Username", Type = FieldType.Textbox, Privacy = PrivacyLevel.UserName)]
|
[FieldDefinition(4, Label = "Username", Type = FieldType.Textbox, Privacy = PrivacyLevel.UserName)]
|
||||||
@@ -64,7 +64,7 @@ namespace NzbDrone.Core.Download.Clients.RTorrent
|
|||||||
[FieldDefinition(10, Label = "Older Priority", Type = FieldType.Select, SelectOptions = typeof(RTorrentPriority), HelpText = "Priority to use when grabbing books released over 14 days ago")]
|
[FieldDefinition(10, Label = "Older Priority", Type = FieldType.Select, SelectOptions = typeof(RTorrentPriority), HelpText = "Priority to use when grabbing books released over 14 days ago")]
|
||||||
public int OlderTvPriority { get; set; }
|
public int OlderTvPriority { get; set; }
|
||||||
|
|
||||||
[FieldDefinition(11, Label = "Add Stopped", Type = FieldType.Checkbox, HelpText = "Enabling will add torrents and magnets to rTorrent in a stopped state. This may break magnet files.")]
|
[FieldDefinition(11, Label = "Add Stopped", Type = FieldType.Checkbox, HelpText = "Enabling will add torrents and magnets to ruTorrent in a stopped state")]
|
||||||
public bool AddStopped { get; set; }
|
public bool AddStopped { get; set; }
|
||||||
|
|
||||||
public NzbDroneValidationResult Validate()
|
public NzbDroneValidationResult Validate()
|
||||||
|
|||||||
@@ -164,14 +164,14 @@ namespace NzbDrone.Core.Download
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
_logger.ForDebugEvent()
|
_logger.Debug()
|
||||||
.Message("No books were just imported, but all books were previously imported, possible issue with download history.")
|
.Message("No books were just imported, but all books were previously imported, possible issue with download history.")
|
||||||
.Property("AuthorId", trackedDownload.RemoteBook.Author.Id)
|
.Property("AuthorId", trackedDownload.RemoteBook.Author.Id)
|
||||||
.Property("DownloadId", trackedDownload.DownloadItem.DownloadId)
|
.Property("DownloadId", trackedDownload.DownloadItem.DownloadId)
|
||||||
.Property("Title", trackedDownload.DownloadItem.Title)
|
.Property("Title", trackedDownload.DownloadItem.Title)
|
||||||
.Property("Path", trackedDownload.DownloadItem.OutputPath.ToString())
|
.Property("Path", trackedDownload.DownloadItem.OutputPath.ToString())
|
||||||
.WriteSentryWarn("DownloadHistoryIncomplete")
|
.WriteSentryWarn("DownloadHistoryIncomplete")
|
||||||
.Log();
|
.Write();
|
||||||
}
|
}
|
||||||
|
|
||||||
trackedDownload.State = TrackedDownloadState.Imported;
|
trackedDownload.State = TrackedDownloadState.Imported;
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using NzbDrone.Common.Messaging;
|
using NzbDrone.Common.Messaging;
|
||||||
using NzbDrone.Core.Download.TrackedDownloads;
|
|
||||||
using NzbDrone.Core.Qualities;
|
using NzbDrone.Core.Qualities;
|
||||||
|
|
||||||
namespace NzbDrone.Core.Download
|
namespace NzbDrone.Core.Download
|
||||||
@@ -14,6 +13,5 @@ namespace NzbDrone.Core.Download
|
|||||||
public DownloadClientItemClientInfo DownloadClientInfo { get; set; }
|
public DownloadClientItemClientInfo DownloadClientInfo { get; set; }
|
||||||
public string DownloadId { get; set; }
|
public string DownloadId { get; set; }
|
||||||
public string Message { get; set; }
|
public string Message { get; set; }
|
||||||
public TrackedDownload TrackedDownload { get; set; }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -110,7 +110,7 @@ namespace NzbDrone.Core.Download
|
|||||||
bookGrabbedEvent.DownloadId = downloadClientId;
|
bookGrabbedEvent.DownloadId = downloadClientId;
|
||||||
}
|
}
|
||||||
|
|
||||||
_logger.ProgressInfo("Report sent to {0} from indexer {1}. {2}", downloadClient.Definition.Name, remoteBook.Release.Indexer, downloadTitle);
|
_logger.ProgressInfo("Report sent to {0}. {1}", downloadClient.Definition.Name, downloadTitle);
|
||||||
_eventAggregator.PublishEvent(bookGrabbedEvent);
|
_eventAggregator.PublishEvent(bookGrabbedEvent);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -42,7 +42,6 @@ namespace NzbDrone.Core.Download
|
|||||||
SourceTitle = trackedDownload.DownloadItem.Title,
|
SourceTitle = trackedDownload.DownloadItem.Title,
|
||||||
DownloadClientInfo = trackedDownload.DownloadItem.DownloadClientInfo,
|
DownloadClientInfo = trackedDownload.DownloadItem.DownloadClientInfo,
|
||||||
DownloadId = trackedDownload.DownloadItem.DownloadId,
|
DownloadId = trackedDownload.DownloadItem.DownloadId,
|
||||||
TrackedDownload = trackedDownload,
|
|
||||||
Message = "Manually ignored"
|
Message = "Manually ignored"
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -200,7 +200,6 @@ namespace NzbDrone.Core.History
|
|||||||
};
|
};
|
||||||
|
|
||||||
history.Data.Add("StatusMessages", message.TrackedDownload.StatusMessages.ToJson());
|
history.Data.Add("StatusMessages", message.TrackedDownload.StatusMessages.ToJson());
|
||||||
history.Data.Add("ReleaseGroup", message.TrackedDownload?.RemoteBook?.ParsedBookInfo?.ReleaseGroup);
|
|
||||||
_historyRepository.Insert(history);
|
_historyRepository.Insert(history);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -236,7 +235,6 @@ namespace NzbDrone.Core.History
|
|||||||
history.Data.Add("ImportedPath", message.ImportedBook.Path);
|
history.Data.Add("ImportedPath", message.ImportedBook.Path);
|
||||||
history.Data.Add("DownloadClient", message.DownloadClientInfo?.Type);
|
history.Data.Add("DownloadClient", message.DownloadClientInfo?.Type);
|
||||||
history.Data.Add("DownloadClientName", message.DownloadClientInfo?.Name);
|
history.Data.Add("DownloadClientName", message.DownloadClientInfo?.Name);
|
||||||
history.Data.Add("ReleaseGroup", message.BookInfo.ReleaseGroup);
|
|
||||||
|
|
||||||
_historyRepository.Insert(history);
|
_historyRepository.Insert(history);
|
||||||
}
|
}
|
||||||
@@ -288,7 +286,6 @@ namespace NzbDrone.Core.History
|
|||||||
};
|
};
|
||||||
|
|
||||||
history.Data.Add("Reason", message.Reason.ToString());
|
history.Data.Add("Reason", message.Reason.ToString());
|
||||||
history.Data.Add("ReleaseGroup", message.BookFile.ReleaseGroup);
|
|
||||||
|
|
||||||
_historyRepository.Insert(history);
|
_historyRepository.Insert(history);
|
||||||
}
|
}
|
||||||
@@ -310,7 +307,6 @@ namespace NzbDrone.Core.History
|
|||||||
|
|
||||||
history.Data.Add("SourcePath", sourcePath);
|
history.Data.Add("SourcePath", sourcePath);
|
||||||
history.Data.Add("Path", path);
|
history.Data.Add("Path", path);
|
||||||
history.Data.Add("ReleaseGroup", message.BookFile.ReleaseGroup);
|
|
||||||
|
|
||||||
_historyRepository.Insert(history);
|
_historyRepository.Insert(history);
|
||||||
}
|
}
|
||||||
@@ -362,7 +358,6 @@ namespace NzbDrone.Core.History
|
|||||||
};
|
};
|
||||||
|
|
||||||
history.Data.Add("DownloadClient", message.DownloadClientInfo.Name);
|
history.Data.Add("DownloadClient", message.DownloadClientInfo.Name);
|
||||||
history.Data.Add("ReleaseGroup", message.TrackedDownload?.RemoteBook?.ParsedBookInfo?.ReleaseGroup);
|
|
||||||
history.Data.Add("Message", message.Message);
|
history.Data.Add("Message", message.Message);
|
||||||
|
|
||||||
historyToAdd.Add(history);
|
historyToAdd.Add(history);
|
||||||
|
|||||||
@@ -117,6 +117,7 @@ namespace NzbDrone.Core.Instrumentation
|
|||||||
syslogTarget.MessageSend.Protocol = ProtocolType.Udp;
|
syslogTarget.MessageSend.Protocol = ProtocolType.Udp;
|
||||||
syslogTarget.MessageSend.Udp.Port = syslogPort;
|
syslogTarget.MessageSend.Udp.Port = syslogPort;
|
||||||
syslogTarget.MessageSend.Udp.Server = syslogServer;
|
syslogTarget.MessageSend.Udp.Server = syslogServer;
|
||||||
|
syslogTarget.MessageSend.Udp.ReconnectInterval = 500;
|
||||||
syslogTarget.MessageCreation.Rfc = RfcNumber.Rfc5424;
|
syslogTarget.MessageCreation.Rfc = RfcNumber.Rfc5424;
|
||||||
syslogTarget.MessageCreation.Rfc5424.AppName = _configFileProvider.InstanceName;
|
syslogTarget.MessageCreation.Rfc5424.AppName = _configFileProvider.InstanceName;
|
||||||
|
|
||||||
|
|||||||
@@ -34,8 +34,6 @@
|
|||||||
"ApiKeyHelpTextWarning": "Requires restart to take effect",
|
"ApiKeyHelpTextWarning": "Requires restart to take effect",
|
||||||
"AppDataDirectory": "AppData directory",
|
"AppDataDirectory": "AppData directory",
|
||||||
"AppDataLocationHealthCheckMessage": "Updating will not be possible to prevent deleting AppData on Update",
|
"AppDataLocationHealthCheckMessage": "Updating will not be possible to prevent deleting AppData on Update",
|
||||||
"ApplicationURL": "Application URL",
|
|
||||||
"ApplicationUrlHelpText": "This application's external URL including http(s)://, port and URL base",
|
|
||||||
"ApplyTags": "Apply Tags",
|
"ApplyTags": "Apply Tags",
|
||||||
"ApplyTagsHelpTexts1": "How to apply tags to the selected author",
|
"ApplyTagsHelpTexts1": "How to apply tags to the selected author",
|
||||||
"ApplyTagsHelpTexts2": "Add: Add the tags the existing list of tags",
|
"ApplyTagsHelpTexts2": "Add: Add the tags the existing list of tags",
|
||||||
|
|||||||
@@ -400,11 +400,11 @@ namespace NzbDrone.Core.MediaFiles
|
|||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
Logger.ForWarnEvent()
|
Logger.Warn()
|
||||||
.Exception(ex)
|
.Exception(ex)
|
||||||
.Message($"Tag writing failed for {path}")
|
.Message($"Tag writing failed for {path}")
|
||||||
.WriteSentryWarn("Tag writing failed")
|
.WriteSentryWarn("Tag writing failed")
|
||||||
.Log();
|
.Write();
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -147,11 +147,11 @@ namespace NzbDrone.Core.MediaFiles
|
|||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
_logger.ForWarnEvent()
|
_logger.Warn()
|
||||||
.Exception(ex)
|
.Exception(ex)
|
||||||
.Message($"Tag removal failed for {path}")
|
.Message($"Tag removal failed for {path}")
|
||||||
.WriteSentryWarn("Tag removal failed")
|
.WriteSentryWarn("Tag removal failed")
|
||||||
.Log();
|
.Write();
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -65,10 +65,10 @@ namespace NzbDrone.Core.MediaFiles
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Logger.ForDebugEvent()
|
Logger.Debug()
|
||||||
.Message("Unknown audio format: '{0}'.", string.Join(", ", mediaInfo.AudioFormat))
|
.Message("Unknown audio format: '{0}'.", string.Join(", ", mediaInfo.AudioFormat))
|
||||||
.WriteSentryWarn("UnknownAudioFormat", mediaInfo.AudioFormat)
|
.WriteSentryWarn("UnknownAudioFormat", mediaInfo.AudioFormat)
|
||||||
.Log();
|
.Write();
|
||||||
|
|
||||||
return "Unknown";
|
return "Unknown";
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,11 +1,10 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Collections.Specialized;
|
using System.Collections.Specialized;
|
||||||
using System.Net;
|
|
||||||
using FluentValidation.Results;
|
using FluentValidation.Results;
|
||||||
using NLog;
|
using NLog;
|
||||||
using NzbDrone.Common.EnvironmentInfo;
|
using NzbDrone.Common.Extensions;
|
||||||
using NzbDrone.Common.Http;
|
using NzbDrone.Common.Http;
|
||||||
|
using NzbDrone.Core.Configuration;
|
||||||
|
|
||||||
namespace NzbDrone.Core.Notifications.Notifiarr
|
namespace NzbDrone.Core.Notifications.Notifiarr
|
||||||
{
|
{
|
||||||
@@ -17,27 +16,21 @@ namespace NzbDrone.Core.Notifications.Notifiarr
|
|||||||
|
|
||||||
public class NotifiarrProxy : INotifiarrProxy
|
public class NotifiarrProxy : INotifiarrProxy
|
||||||
{
|
{
|
||||||
private const string URL = "https://notifiarr.com/notifier.php";
|
private const string URL = "https://notifiarr.com";
|
||||||
private readonly IHttpClient _httpClient;
|
private readonly IHttpClient _httpClient;
|
||||||
|
private readonly IConfigFileProvider _configFileProvider;
|
||||||
private readonly Logger _logger;
|
private readonly Logger _logger;
|
||||||
|
|
||||||
public NotifiarrProxy(IHttpClient httpClient, Logger logger)
|
public NotifiarrProxy(IHttpClient httpClient, IConfigFileProvider configFileProvider, Logger logger)
|
||||||
{
|
{
|
||||||
_httpClient = httpClient;
|
_httpClient = httpClient;
|
||||||
|
_configFileProvider = configFileProvider;
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SendNotification(StringDictionary message, NotifiarrSettings settings)
|
public void SendNotification(StringDictionary message, NotifiarrSettings settings)
|
||||||
{
|
{
|
||||||
try
|
|
||||||
{
|
|
||||||
ProcessNotification(message, settings);
|
ProcessNotification(message, settings);
|
||||||
}
|
|
||||||
catch (NotifiarrException ex)
|
|
||||||
{
|
|
||||||
_logger.Error(ex, "Unable to send notification");
|
|
||||||
throw new NotifiarrException("Unable to send notification");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public ValidationFailure Test(NotifiarrSettings settings)
|
public ValidationFailure Test(NotifiarrSettings settings)
|
||||||
@@ -50,21 +43,14 @@ namespace NzbDrone.Core.Notifications.Notifiarr
|
|||||||
SendNotification(variables, settings);
|
SendNotification(variables, settings);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
catch (HttpException ex)
|
catch (NotifiarrException ex)
|
||||||
{
|
{
|
||||||
if (ex.Response.StatusCode == HttpStatusCode.Unauthorized)
|
return new ValidationFailure("APIKey", ex.Message);
|
||||||
{
|
|
||||||
_logger.Error(ex, "API key is invalid: " + ex.Message);
|
|
||||||
return new ValidationFailure("APIKey", "API key is invalid");
|
|
||||||
}
|
|
||||||
|
|
||||||
_logger.Error(ex, "Unable to send test message: " + ex.Message);
|
|
||||||
return new ValidationFailure("APIKey", "Unable to send test notification");
|
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
_logger.Error(ex, "Unable to send test notification: " + ex.Message);
|
_logger.Error(ex, ex.Message);
|
||||||
return new ValidationFailure("", "Unable to send test notification");
|
return new ValidationFailure("", "Unable to send test notification. Check the log for more details.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -72,8 +58,10 @@ namespace NzbDrone.Core.Notifications.Notifiarr
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var requestBuilder = new HttpRequestBuilder(URL).Post();
|
var instanceName = _configFileProvider.InstanceName;
|
||||||
requestBuilder.AddFormParameter("api", settings.APIKey).Build();
|
var requestBuilder = new HttpRequestBuilder(URL + "/api/v1/notification/readarr").Post();
|
||||||
|
requestBuilder.AddFormParameter("instanceName", instanceName).Build();
|
||||||
|
requestBuilder.SetHeader("X-API-Key", settings.APIKey);
|
||||||
|
|
||||||
foreach (string key in message.Keys)
|
foreach (string key in message.Keys)
|
||||||
{
|
{
|
||||||
@@ -86,13 +74,31 @@ namespace NzbDrone.Core.Notifications.Notifiarr
|
|||||||
}
|
}
|
||||||
catch (HttpException ex)
|
catch (HttpException ex)
|
||||||
{
|
{
|
||||||
if (ex.Response.StatusCode == HttpStatusCode.BadRequest)
|
var responseCode = ex.Response.StatusCode;
|
||||||
|
switch ((int)responseCode)
|
||||||
{
|
{
|
||||||
_logger.Error(ex, "API key is invalid");
|
case 401:
|
||||||
throw;
|
_logger.Error("Unauthorized", "HTTP 401 - API key is invalid");
|
||||||
|
throw new NotifiarrException("API key is invalid");
|
||||||
|
case 400:
|
||||||
|
_logger.Error("Invalid Request", "HTTP 400 - Unable to send notification. Ensure Readarr Integration is enabled & assigned a channel on Notifiarr");
|
||||||
|
throw new NotifiarrException("Unable to send notification. Ensure Readarr Integration is enabled & assigned a channel on Notifiarr");
|
||||||
|
case 502:
|
||||||
|
case 503:
|
||||||
|
case 504:
|
||||||
|
_logger.Error("Service Unavailable", "Unable to send notification. Service Unavailable");
|
||||||
|
throw new NotifiarrException("Unable to send notification. Service Unavailable", ex);
|
||||||
|
case 520:
|
||||||
|
case 521:
|
||||||
|
case 522:
|
||||||
|
case 523:
|
||||||
|
case 524:
|
||||||
|
_logger.Error(ex, "Cloudflare Related HTTP Error - Unable to send notification");
|
||||||
|
throw new NotifiarrException("Cloudflare Related HTTP Error - Unable to send notification", ex);
|
||||||
|
default:
|
||||||
|
_logger.Error(ex, "Unknown HTTP Error - Unable to send notification");
|
||||||
|
throw new NotifiarrException("Unknown HTTP Error - Unable to send notification", ex);
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new NotifiarrException("Unable to send notification", ex);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,6 @@
|
|||||||
using System.Collections.Generic;
|
|
||||||
using System.IO;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Text.RegularExpressions;
|
using System.Text.RegularExpressions;
|
||||||
using FluentValidation;
|
using FluentValidation;
|
||||||
using FluentValidation.Validators;
|
using FluentValidation.Validators;
|
||||||
using NzbDrone.Common.Extensions;
|
|
||||||
|
|
||||||
namespace NzbDrone.Core.Organizer
|
namespace NzbDrone.Core.Organizer
|
||||||
{
|
{
|
||||||
@@ -16,16 +12,12 @@ namespace NzbDrone.Core.Organizer
|
|||||||
public static IRuleBuilderOptions<T, string> ValidBookFormat<T>(this IRuleBuilder<T, string> ruleBuilder)
|
public static IRuleBuilderOptions<T, string> ValidBookFormat<T>(this IRuleBuilder<T, string> ruleBuilder)
|
||||||
{
|
{
|
||||||
ruleBuilder.SetValidator(new NotEmptyValidator(null));
|
ruleBuilder.SetValidator(new NotEmptyValidator(null));
|
||||||
ruleBuilder.SetValidator(new IllegalCharactersValidator());
|
|
||||||
|
|
||||||
return ruleBuilder.SetValidator(new ValidStandardTrackFormatValidator());
|
return ruleBuilder.SetValidator(new ValidStandardTrackFormatValidator());
|
||||||
}
|
}
|
||||||
|
|
||||||
public static IRuleBuilderOptions<T, string> ValidAuthorFolderFormat<T>(this IRuleBuilder<T, string> ruleBuilder)
|
public static IRuleBuilderOptions<T, string> ValidAuthorFolderFormat<T>(this IRuleBuilder<T, string> ruleBuilder)
|
||||||
{
|
{
|
||||||
ruleBuilder.SetValidator(new NotEmptyValidator(null));
|
ruleBuilder.SetValidator(new NotEmptyValidator(null));
|
||||||
ruleBuilder.SetValidator(new IllegalCharactersValidator());
|
|
||||||
|
|
||||||
return ruleBuilder.SetValidator(new RegularExpressionValidator(FileNameBuilder.AuthorNameRegex)).WithMessage("Must contain Author name");
|
return ruleBuilder.SetValidator(new RegularExpressionValidator(FileNameBuilder.AuthorNameRegex)).WithMessage("Must contain Author name");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -50,41 +42,4 @@ namespace NzbDrone.Core.Organizer
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public class IllegalCharactersValidator : PropertyValidator
|
|
||||||
{
|
|
||||||
private readonly char[] _invalidPathChars = Path.GetInvalidPathChars();
|
|
||||||
|
|
||||||
public IllegalCharactersValidator()
|
|
||||||
: base("Contains illegal characters: {InvalidCharacters}")
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override bool IsValid(PropertyValidatorContext context)
|
|
||||||
{
|
|
||||||
var value = context.PropertyValue as string;
|
|
||||||
var invalidCharacters = new List<char>();
|
|
||||||
|
|
||||||
if (value.IsNullOrWhiteSpace())
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (var i in _invalidPathChars)
|
|
||||||
{
|
|
||||||
if (value.IndexOf(i) >= 0)
|
|
||||||
{
|
|
||||||
invalidCharacters.Add(i);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (invalidCharacters.Any())
|
|
||||||
{
|
|
||||||
context.MessageFormatter.AppendArgument("InvalidCharacters", string.Join("", invalidCharacters));
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ namespace NzbDrone.Core.Validation.Paths
|
|||||||
private readonly IAppFolderInfo _appFolderInfo;
|
private readonly IAppFolderInfo _appFolderInfo;
|
||||||
|
|
||||||
public StartupFolderValidator(IAppFolderInfo appFolderInfo)
|
public StartupFolderValidator(IAppFolderInfo appFolderInfo)
|
||||||
: base("Path cannot be {relationship} the start up folder")
|
: base("Path cannot be an ancestor of the start up folder")
|
||||||
{
|
{
|
||||||
_appFolderInfo = appFolderInfo;
|
_appFolderInfo = appFolderInfo;
|
||||||
}
|
}
|
||||||
@@ -21,24 +21,7 @@ namespace NzbDrone.Core.Validation.Paths
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
var startupFolder = _appFolderInfo.StartUpFolder;
|
return !_appFolderInfo.StartUpFolder.IsParentPath(context.PropertyValue.ToString());
|
||||||
var folder = context.PropertyValue.ToString();
|
|
||||||
|
|
||||||
if (startupFolder.PathEquals(folder))
|
|
||||||
{
|
|
||||||
context.MessageFormatter.AppendArgument("relationship", "set to");
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (startupFolder.IsParentPath(folder))
|
|
||||||
{
|
|
||||||
context.MessageFormatter.AppendArgument("relationship", "child of");
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,7 +26,6 @@ namespace Readarr.Api.V1.Config
|
|||||||
public string SslCertPassword { get; set; }
|
public string SslCertPassword { get; set; }
|
||||||
public string UrlBase { get; set; }
|
public string UrlBase { get; set; }
|
||||||
public string InstanceName { get; set; }
|
public string InstanceName { get; set; }
|
||||||
public string ApplicationUrl { get; set; }
|
|
||||||
public bool UpdateAutomatically { get; set; }
|
public bool UpdateAutomatically { get; set; }
|
||||||
public UpdateMechanism UpdateMechanism { get; set; }
|
public UpdateMechanism UpdateMechanism { get; set; }
|
||||||
public string UpdateScriptPath { get; set; }
|
public string UpdateScriptPath { get; set; }
|
||||||
@@ -83,8 +82,7 @@ namespace Readarr.Api.V1.Config
|
|||||||
CertificateValidation = configService.CertificateValidation,
|
CertificateValidation = configService.CertificateValidation,
|
||||||
BackupFolder = configService.BackupFolder,
|
BackupFolder = configService.BackupFolder,
|
||||||
BackupInterval = configService.BackupInterval,
|
BackupInterval = configService.BackupInterval,
|
||||||
BackupRetention = configService.BackupRetention,
|
BackupRetention = configService.BackupRetention
|
||||||
ApplicationUrl = configService.ApplicationUrl
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,5 +12,15 @@ namespace Readarr.Api.V1.DownloadClient
|
|||||||
: base(downloadClientFactory, "downloadclient", ResourceMapper)
|
: base(downloadClientFactory, "downloadclient", ResourceMapper)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected override void Validate(DownloadClientDefinition definition, bool includeWarnings)
|
||||||
|
{
|
||||||
|
if (!definition.Enable)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
base.Validate(definition, includeWarnings);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,5 +22,15 @@ namespace Readarr.Api.V1.ImportLists
|
|||||||
SharedValidator.RuleFor(c => c.QualityProfileId).SetValidator(qualityProfileExistsValidator);
|
SharedValidator.RuleFor(c => c.QualityProfileId).SetValidator(qualityProfileExistsValidator);
|
||||||
SharedValidator.RuleFor(c => c.MetadataProfileId).SetValidator(metadataProfileExistsValidator);
|
SharedValidator.RuleFor(c => c.MetadataProfileId).SetValidator(metadataProfileExistsValidator);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected override void Validate(ImportListDefinition definition, bool includeWarnings)
|
||||||
|
{
|
||||||
|
if (!definition.Enable)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
base.Validate(definition, includeWarnings);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,5 +12,15 @@ namespace Readarr.Api.V1.Indexers
|
|||||||
: base(indexerFactory, "indexer", ResourceMapper)
|
: base(indexerFactory, "indexer", ResourceMapper)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected override void Validate(IndexerDefinition definition, bool includeWarnings)
|
||||||
|
{
|
||||||
|
if (!definition.Enable)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
base.Validate(definition, includeWarnings);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,5 +12,15 @@ namespace Readarr.Api.V1.Metadata
|
|||||||
: base(metadataFactory, "metadata", ResourceMapper)
|
: base(metadataFactory, "metadata", ResourceMapper)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected override void Validate(MetadataDefinition definition, bool includeWarnings)
|
||||||
|
{
|
||||||
|
if (!definition.Enable)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
base.Validate(definition, includeWarnings);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,5 +12,15 @@ namespace Readarr.Api.V1.Notifications
|
|||||||
: base(notificationFactory, "notification", ResourceMapper)
|
: base(notificationFactory, "notification", ResourceMapper)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected override void Validate(NotificationDefinition definition, bool includeWarnings)
|
||||||
|
{
|
||||||
|
if (!definition.Enable)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
base.Validate(definition, includeWarnings);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,7 +7,6 @@ using NzbDrone.Common.Serializer;
|
|||||||
using NzbDrone.Core.ThingiProvider;
|
using NzbDrone.Core.ThingiProvider;
|
||||||
using NzbDrone.Core.Validation;
|
using NzbDrone.Core.Validation;
|
||||||
using NzbDrone.Http.REST.Attributes;
|
using NzbDrone.Http.REST.Attributes;
|
||||||
using Readarr.Http.Extensions;
|
|
||||||
using Readarr.Http.REST;
|
using Readarr.Http.REST;
|
||||||
|
|
||||||
namespace Readarr.Api.V1
|
namespace Readarr.Api.V1
|
||||||
@@ -61,7 +60,7 @@ namespace Readarr.Api.V1
|
|||||||
[RestPostById]
|
[RestPostById]
|
||||||
public ActionResult<TProviderResource> CreateProvider(TProviderResource providerResource)
|
public ActionResult<TProviderResource> CreateProvider(TProviderResource providerResource)
|
||||||
{
|
{
|
||||||
var providerDefinition = GetDefinition(providerResource, true, false, false);
|
var providerDefinition = GetDefinition(providerResource, false);
|
||||||
|
|
||||||
if (providerDefinition.Enable)
|
if (providerDefinition.Enable)
|
||||||
{
|
{
|
||||||
@@ -76,11 +75,11 @@ namespace Readarr.Api.V1
|
|||||||
[RestPutById]
|
[RestPutById]
|
||||||
public ActionResult<TProviderResource> UpdateProvider(TProviderResource providerResource)
|
public ActionResult<TProviderResource> UpdateProvider(TProviderResource providerResource)
|
||||||
{
|
{
|
||||||
var providerDefinition = GetDefinition(providerResource, true, false, false);
|
var providerDefinition = GetDefinition(providerResource, false);
|
||||||
var forceSave = Request.GetBooleanQueryParameter("forceSave");
|
var existingDefinition = _providerFactory.Get(providerDefinition.Id);
|
||||||
|
|
||||||
// Only test existing definitions if it is enabled and forceSave isn't set.
|
// Only test existing definitions if it was previously disabled
|
||||||
if (providerDefinition.Enable && !forceSave)
|
if (providerDefinition.Enable && !existingDefinition.Enable)
|
||||||
{
|
{
|
||||||
Test(providerDefinition, false);
|
Test(providerDefinition, false);
|
||||||
}
|
}
|
||||||
@@ -90,11 +89,11 @@ namespace Readarr.Api.V1
|
|||||||
return Accepted(providerResource.Id);
|
return Accepted(providerResource.Id);
|
||||||
}
|
}
|
||||||
|
|
||||||
private TProviderDefinition GetDefinition(TProviderResource providerResource, bool validate, bool includeWarnings, bool forceValidate)
|
private TProviderDefinition GetDefinition(TProviderResource providerResource, bool includeWarnings = false, bool validate = true)
|
||||||
{
|
{
|
||||||
var definition = _resourceMapper.ToModel(providerResource);
|
var definition = _resourceMapper.ToModel(providerResource);
|
||||||
|
|
||||||
if (validate && (definition.Enable || forceValidate))
|
if (validate)
|
||||||
{
|
{
|
||||||
Validate(definition, includeWarnings);
|
Validate(definition, includeWarnings);
|
||||||
}
|
}
|
||||||
@@ -114,7 +113,7 @@ namespace Readarr.Api.V1
|
|||||||
{
|
{
|
||||||
var defaultDefinitions = _providerFactory.GetDefaultDefinitions().OrderBy(p => p.ImplementationName).ToList();
|
var defaultDefinitions = _providerFactory.GetDefaultDefinitions().OrderBy(p => p.ImplementationName).ToList();
|
||||||
|
|
||||||
var result = new List<TProviderResource>(defaultDefinitions.Count);
|
var result = new List<TProviderResource>(defaultDefinitions.Count());
|
||||||
|
|
||||||
foreach (var providerDefinition in defaultDefinitions)
|
foreach (var providerDefinition in defaultDefinitions)
|
||||||
{
|
{
|
||||||
@@ -135,7 +134,7 @@ namespace Readarr.Api.V1
|
|||||||
[HttpPost("test")]
|
[HttpPost("test")]
|
||||||
public object Test([FromBody] TProviderResource providerResource)
|
public object Test([FromBody] TProviderResource providerResource)
|
||||||
{
|
{
|
||||||
var providerDefinition = GetDefinition(providerResource, true, true, true);
|
var providerDefinition = GetDefinition(providerResource, true);
|
||||||
|
|
||||||
Test(providerDefinition, true);
|
Test(providerDefinition, true);
|
||||||
|
|
||||||
@@ -168,7 +167,7 @@ namespace Readarr.Api.V1
|
|||||||
[HttpPost("action/{name}")]
|
[HttpPost("action/{name}")]
|
||||||
public IActionResult RequestAction(string name, [FromBody] TProviderResource resource)
|
public IActionResult RequestAction(string name, [FromBody] TProviderResource resource)
|
||||||
{
|
{
|
||||||
var providerDefinition = GetDefinition(resource, false, false, false);
|
var providerDefinition = GetDefinition(resource, true, false);
|
||||||
|
|
||||||
var query = Request.Query.ToDictionary(x => x.Key, x => x.Value.ToString());
|
var query = Request.Query.ToDictionary(x => x.Key, x => x.Value.ToString());
|
||||||
|
|
||||||
@@ -177,7 +176,7 @@ namespace Readarr.Api.V1
|
|||||||
return Content(data.ToJson(), "application/json");
|
return Content(data.ToJson(), "application/json");
|
||||||
}
|
}
|
||||||
|
|
||||||
private void Validate(TProviderDefinition definition, bool includeWarnings)
|
protected virtual void Validate(TProviderDefinition definition, bool includeWarnings)
|
||||||
{
|
{
|
||||||
var validationResult = definition.Settings.Validate();
|
var validationResult = definition.Settings.Validate();
|
||||||
|
|
||||||
|
|||||||
@@ -176,7 +176,7 @@ namespace Readarr.Api.V1.Queue
|
|||||||
case "status":
|
case "status":
|
||||||
return q => q.Status;
|
return q => q.Status;
|
||||||
case "authors.sortName":
|
case "authors.sortName":
|
||||||
return q => q.Author?.Metadata.Value.SortName ?? q.Title;
|
return q => q.Author?.Metadata.Value.SortName ?? string.Empty;
|
||||||
case "authors.sortNameLastFirst":
|
case "authors.sortNameLastFirst":
|
||||||
return q => q.Author?.Metadata.Value.SortNameLastFirst ?? string.Empty;
|
return q => q.Author?.Metadata.Value.SortNameLastFirst ?? string.Empty;
|
||||||
case "title":
|
case "title":
|
||||||
|
|||||||
@@ -162,7 +162,39 @@ namespace Readarr.Http.Extensions
|
|||||||
remoteIP = remoteIP.MapToIPv4();
|
remoteIP = remoteIP.MapToIPv4();
|
||||||
}
|
}
|
||||||
|
|
||||||
return remoteIP.ToString();
|
var remoteAddress = remoteIP.ToString();
|
||||||
|
|
||||||
|
// Only check if forwarded by a local network reverse proxy
|
||||||
|
if (remoteIP.IsLocalAddress())
|
||||||
|
{
|
||||||
|
var realIPHeader = request.Headers["X-Real-IP"];
|
||||||
|
if (realIPHeader.Any())
|
||||||
|
{
|
||||||
|
return realIPHeader.First().ToString();
|
||||||
|
}
|
||||||
|
|
||||||
|
var forwardedForHeader = request.Headers["X-Forwarded-For"];
|
||||||
|
if (forwardedForHeader.Any())
|
||||||
|
{
|
||||||
|
// Get the first address that was forwarded by a local IP to prevent remote clients faking another proxy
|
||||||
|
foreach (var forwardedForAddress in forwardedForHeader.SelectMany(v => v.Split(',')).Select(v => v.Trim()).Reverse())
|
||||||
|
{
|
||||||
|
if (!IPAddress.TryParse(forwardedForAddress, out remoteIP))
|
||||||
|
{
|
||||||
|
return remoteAddress;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!remoteIP.IsLocalAddress())
|
||||||
|
{
|
||||||
|
return forwardedForAddress;
|
||||||
|
}
|
||||||
|
|
||||||
|
remoteAddress = forwardedForAddress;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return remoteAddress;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void DisableCache(this IHeaderDictionary headers)
|
public static void DisableCache(this IHeaderDictionary headers)
|
||||||
|
|||||||
Reference in New Issue
Block a user