Compare commits

..

1 Commits

Author SHA1 Message Date
Bakerboy448 09d423848d New: Use Notifiarr API
New: Notifiarr Add Instance Name Support

Fixed: Notifiarr - Better HTTP Error Handling

also quiet sentry

(cherry picked from commit 1db690ad39ec103c0f4dc89ac4545801ef95bec7)

Fixed: Improve Notifiarr Exception Handling and Validation Errors

(cherry picked from commit 6aaa024d71b939030950460ae986ada5bbae5ad7)

also move notifiarr to header auth from url auth
2022-11-25 20:34:22 -06:00
66 changed files with 258 additions and 1039 deletions
-9
View File
@@ -62,15 +62,6 @@ Support this project by becoming a sponsor. Your logo will show up here with a l
[![Mega Sponsors List](https://opencollective.com/Readarr/tiers/mega-sponsor.svg?width=890)](https://opencollective.com/readarr#mega-sponsor) [![Mega Sponsors List](https://opencollective.com/Readarr/tiers/mega-sponsor.svg?width=890)](https://opencollective.com/readarr#mega-sponsor)
## DigitalOcean
This project is also supported by DigitalOcean
<p>
<a href="https://www.digitalocean.com/">
<img src="https://opensource.nyc3.cdn.digitaloceanspaces.com/attribution/assets/SVG/DO_Logo_horizontal_blue.svg" width="201px">
</a>
</p>
### License ### License
* [GNU GPL v3](http://www.gnu.org/licenses/gpl.html) * [GNU GPL v3](http://www.gnu.org/licenses/gpl.html)
+1 -1
View File
@@ -9,7 +9,7 @@ variables:
testsFolder: './_tests' testsFolder: './_tests'
yarnCacheFolder: $(Pipeline.Workspace)/.yarn yarnCacheFolder: $(Pipeline.Workspace)/.yarn
nugetCacheFolder: $(Pipeline.Workspace)/.nuget/packages nugetCacheFolder: $(Pipeline.Workspace)/.nuget/packages
majorVersion: '0.1.2' majorVersion: '0.1.1'
minorVersion: $[counter('minorVersion', 1)] minorVersion: $[counter('minorVersion', 1)]
readarrVersion: '$(majorVersion).$(minorVersion)' readarrVersion: '$(majorVersion).$(minorVersion)'
buildName: '$(Build.SourceBranchName).$(readarrVersion)' buildName: '$(Build.SourceBranchName).$(readarrVersion)'
@@ -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',
+7 -3
View File
@@ -1,18 +1,22 @@
import createAjaxRequest from 'Utilities/createAjaxRequest'; import $ from 'jquery';
function getTranslations() { function getTranslations() {
let localization = null; let localization = null;
const ajaxOptions = { const ajaxOptions = {
async: false, async: false,
type: 'GET',
global: false,
dataType: 'json', dataType: 'json',
url: '/localization', url: `${window.Readarr.apiRoot}/localization`,
success: function(data) { success: function(data) {
localization = data.Strings; localization = data.Strings;
} }
}; };
createAjaxRequest(ajaxOptions); ajaxOptions.headers = ajaxOptions.headers || {};
ajaxOptions.headers['X-Api-Key'] = window.Readarr.apiKey;
$.ajax(ajaxOptions);
return localization; return localization;
} }
+4 -4
View File
@@ -26,12 +26,12 @@
<PackageVersion Include="Microsoft.Win32.Registry" Version="5.0.0" /> <PackageVersion Include="Microsoft.Win32.Registry" Version="5.0.0" />
<PackageVersion Include="Mono.Posix.NETStandard" Version="5.20.1.34-servarr22" /> <PackageVersion Include="Mono.Posix.NETStandard" Version="5.20.1.34-servarr22" />
<PackageVersion Include="Moq" Version="4.17.2" /> <PackageVersion Include="Moq" Version="4.17.2" />
<PackageVersion Include="MonoTorrent" Version="2.0.7" /> <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" />
+2 -3
View File
@@ -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();
}
} }
} }
@@ -27,7 +27,7 @@ namespace NzbDrone.Core.Test.Download
_downloadClients = new List<IDownloadClient>(); _downloadClients = new List<IDownloadClient>();
Mocker.GetMock<IProvideDownloadClient>() Mocker.GetMock<IProvideDownloadClient>()
.Setup(v => v.GetDownloadClients(It.IsAny<bool>())) .Setup(v => v.GetDownloadClients())
.Returns(_downloadClients); .Returns(_downloadClients);
Mocker.GetMock<IProvideDownloadClient>() Mocker.GetMock<IProvideDownloadClient>()
@@ -24,7 +24,7 @@ namespace NzbDrone.Core.Test.HealthCheck.Checks
public void should_return_warning_when_download_client_has_not_been_configured() public void should_return_warning_when_download_client_has_not_been_configured()
{ {
Mocker.GetMock<IProvideDownloadClient>() Mocker.GetMock<IProvideDownloadClient>()
.Setup(s => s.GetDownloadClients(It.IsAny<bool>())) .Setup(s => s.GetDownloadClients())
.Returns(Array.Empty<IDownloadClient>()); .Returns(Array.Empty<IDownloadClient>());
Subject.Check().ShouldBeWarning(); Subject.Check().ShouldBeWarning();
@@ -40,7 +40,7 @@ namespace NzbDrone.Core.Test.HealthCheck.Checks
.Throws<Exception>(); .Throws<Exception>();
Mocker.GetMock<IProvideDownloadClient>() Mocker.GetMock<IProvideDownloadClient>()
.Setup(s => s.GetDownloadClients(It.IsAny<bool>())) .Setup(s => s.GetDownloadClients())
.Returns(new IDownloadClient[] { downloadClient.Object }); .Returns(new IDownloadClient[] { downloadClient.Object });
Subject.Check().ShouldBeError(); Subject.Check().ShouldBeError();
@@ -55,7 +55,7 @@ namespace NzbDrone.Core.Test.HealthCheck.Checks
.Returns(new List<DownloadClientItem>()); .Returns(new List<DownloadClientItem>());
Mocker.GetMock<IProvideDownloadClient>() Mocker.GetMock<IProvideDownloadClient>()
.Setup(s => s.GetDownloadClients(It.IsAny<bool>())) .Setup(s => s.GetDownloadClients())
.Returns(new IDownloadClient[] { downloadClient.Object }); .Returns(new IDownloadClient[] { downloadClient.Object });
Subject.Check().ShouldBeOk(); Subject.Check().ShouldBeOk();
@@ -49,7 +49,7 @@ namespace NzbDrone.Core.Test.HealthCheck.Checks
.Returns(_clientStatus); .Returns(_clientStatus);
Mocker.GetMock<IProvideDownloadClient>() Mocker.GetMock<IProvideDownloadClient>()
.Setup(s => s.GetDownloadClients(It.IsAny<bool>())) .Setup(s => s.GetDownloadClients())
.Returns(new IDownloadClient[] { _downloadClient.Object }); .Returns(new IDownloadClient[] { _downloadClient.Object });
Mocker.GetMock<IDiskProvider>() Mocker.GetMock<IDiskProvider>()
@@ -63,7 +63,7 @@ namespace NzbDrone.Core.Test.HealthCheck.Checks
.Returns(_clientStatus); .Returns(_clientStatus);
Mocker.GetMock<IProvideDownloadClient>() Mocker.GetMock<IProvideDownloadClient>()
.Setup(s => s.GetDownloadClients(It.IsAny<bool>())) .Setup(s => s.GetDownloadClients())
.Returns(new IDownloadClient[] { _downloadClient.Object }); .Returns(new IDownloadClient[] { _downloadClient.Object });
Mocker.GetMock<IProvideDownloadClient>() Mocker.GetMock<IProvideDownloadClient>()
@@ -75,7 +75,7 @@ namespace NzbDrone.Core.Test.MetadataSource.Goodreads
[Test] [Test]
public void getting_details_of_invalid_book() public void getting_details_of_invalid_book()
{ {
Assert.Throws<BookNotFoundException>(() => Subject.GetBookInfo("1")); Assert.Throws<BookNotFoundException>(() => Subject.GetBookInfo("99999999"));
} }
private void ValidateAuthor(Author author) private void ValidateAuthor(Author author)
@@ -45,17 +45,13 @@ namespace NzbDrone.Core.AuthorStats
} }
} }
private SqlBuilder Builder() private SqlBuilder Builder() => new SqlBuilder(_database.DatabaseType)
{ .Select(@"""Authors"".""Id"" AS ""AuthorId"",
var trueIndicator = _database.DatabaseType == DatabaseType.PostgreSQL ? "true" : "1";
return new SqlBuilder(_database.DatabaseType)
.Select($@"""Authors"".""Id"" AS ""AuthorId"",
""Books"".""Id"" AS ""BookId"", ""Books"".""Id"" AS ""BookId"",
SUM(COALESCE(""BookFiles"".""Size"", 0)) AS ""SizeOnDisk"", SUM(COALESCE(""BookFiles"".""Size"", 0)) AS ""SizeOnDisk"",
1 AS ""TotalBookCount"", 1 AS ""TotalBookCount"",
CASE WHEN MIN(""BookFiles"".""Id"") IS NULL THEN 0 ELSE 1 END AS ""AvailableBookCount"", CASE WHEN MIN(""BookFiles"".""Id"") IS NULL THEN 0 ELSE 1 END AS ""AvailableBookCount"",
CASE WHEN (""Books"".""Monitored"" = {trueIndicator} AND (""Books"".""ReleaseDate"" < @currentDate) OR ""Books"".""ReleaseDate"" IS NULL) OR MIN(""BookFiles"".""Id"") IS NOT NULL THEN 1 ELSE 0 END AS ""BookCount"", CASE WHEN (""Books"".""Monitored"" = true AND (""Books"".""ReleaseDate"" < @currentDate) OR ""Books"".""ReleaseDate"" IS NULL) OR MIN(""BookFiles"".""Id"") IS NOT NULL THEN 1 ELSE 0 END AS ""BookCount"",
CASE WHEN MIN(""BookFiles"".""Id"") IS NULL THEN 0 ELSE COUNT(""BookFiles"".""Id"") END AS ""BookFileCount""") CASE WHEN MIN(""BookFiles"".""Id"") IS NULL THEN 0 ELSE COUNT(""BookFiles"".""Id"") END AS ""BookFileCount""")
.Join<Edition, Book>((e, b) => e.BookId == b.Id) .Join<Edition, Book>((e, b) => e.BookId == b.Id)
.Join<Book, Author>((book, author) => book.AuthorMetadataId == author.AuthorMetadataId) .Join<Book, Author>((book, author) => book.AuthorMetadataId == author.AuthorMetadataId)
@@ -64,6 +60,5 @@ namespace NzbDrone.Core.AuthorStats
.GroupBy<Author>(x => x.Id) .GroupBy<Author>(x => x.Id)
.GroupBy<Book>(x => x.Id) .GroupBy<Book>(x => x.Id)
.AddParameters(new Dictionary<string, object> { { "currentDate", DateTime.UtcNow } }); .AddParameters(new Dictionary<string, object> { { "currentDate", DateTime.UtcNow } });
}
} }
} }
@@ -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;
@@ -9,7 +9,7 @@ namespace NzbDrone.Core.Download
public interface IProvideDownloadClient public interface IProvideDownloadClient
{ {
IDownloadClient GetDownloadClient(DownloadProtocol downloadProtocol); IDownloadClient GetDownloadClient(DownloadProtocol downloadProtocol);
IEnumerable<IDownloadClient> GetDownloadClients(bool filterBlockedClients = false); IEnumerable<IDownloadClient> GetDownloadClients();
IDownloadClient Get(int id); IDownloadClient Get(int id);
} }
@@ -67,39 +67,14 @@ namespace NzbDrone.Core.Download
return provider; return provider;
} }
public IEnumerable<IDownloadClient> GetDownloadClients(bool filterBlockedClients = false) public IEnumerable<IDownloadClient> GetDownloadClients()
{ {
var enabledClients = _downloadClientFactory.GetAvailableProviders(); return _downloadClientFactory.GetAvailableProviders();
if (filterBlockedClients)
{
return FilterBlockedIndexers(enabledClients).ToList();
}
return enabledClients;
} }
public IDownloadClient Get(int id) public IDownloadClient Get(int id)
{ {
return _downloadClientFactory.GetAvailableProviders().Single(d => d.Definition.Id == id); return _downloadClientFactory.GetAvailableProviders().Single(d => d.Definition.Id == id);
} }
private IEnumerable<IDownloadClient> FilterBlockedIndexers(IEnumerable<IDownloadClient> clients)
{
var blockedClients = _downloadClientStatusService.GetBlockedProviders().ToDictionary(v => v.ProviderId, v => v);
foreach (var client in clients)
{
DownloadClientStatus blockedClientStatus;
if (blockedClients.TryGetValue(client.Definition.Id, out blockedClientStatus))
{
_logger.Debug("Temporarily ignoring client {0} till {1} due to recent failures.", client.Definition.Name, blockedClientStatus.DisabledTill.Value.ToLocalTime());
continue;
}
yield return client;
}
}
} }
} }
@@ -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"
}; };
@@ -1,6 +1,5 @@
using System; using System;
using System.Linq; using System.Linq;
using System.Net.Http;
using NLog; using NLog;
using NzbDrone.Common.Extensions; using NzbDrone.Common.Extensions;
using NzbDrone.Core.Datastore.Events; using NzbDrone.Core.Datastore.Events;
@@ -38,9 +37,7 @@ namespace NzbDrone.Core.HealthCheck.Checks
public override HealthCheck Check() public override HealthCheck Check()
{ {
// Only check clients not in failure status, those get another message var clients = _downloadClientProvider.GetDownloadClients();
var clients = _downloadClientProvider.GetDownloadClients(true);
var rootFolders = _rootFolderService.All(); var rootFolders = _rootFolderService.All();
foreach (var client in clients) foreach (var client in clients)
@@ -61,10 +58,6 @@ namespace NzbDrone.Core.HealthCheck.Checks
{ {
_logger.Debug(ex, "Unable to communicate with {0}", client.Definition.Name); _logger.Debug(ex, "Unable to communicate with {0}", client.Definition.Name);
} }
catch (HttpRequestException ex)
{
_logger.Debug(ex, "Unable to communicate with {0}", client.Definition.Name);
}
catch (Exception ex) catch (Exception ex)
{ {
_logger.Error(ex, "Unknown error occured in DownloadClientRootFolderCheck HealthCheck"); _logger.Error(ex, "Unknown error occured in DownloadClientRootFolderCheck HealthCheck");
@@ -53,8 +53,7 @@ namespace NzbDrone.Core.HealthCheck.Checks
return new HealthCheck(GetType()); return new HealthCheck(GetType());
} }
// Only check clients not in failure status, those get another message var clients = _downloadClientProvider.GetDownloadClients();
var clients = _downloadClientProvider.GetDownloadClients(true);
foreach (var client in clients) foreach (var client in clients)
{ {
@@ -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;
+1 -4
View File
@@ -1,4 +1 @@
{ {}
"About": "সম্পর্কিত",
"Actions": "ক্রিয়াকাণ্ড"
}
+5 -7
View File
@@ -349,7 +349,7 @@
"ShownAboveEachColumnWhenWeekIsTheActiveView": "Wird in der Wochenansicht über jeder Spalte angezeigt", "ShownAboveEachColumnWhenWeekIsTheActiveView": "Wird in der Wochenansicht über jeder Spalte angezeigt",
"Size": " Größe", "Size": " Größe",
"SkipFreeSpaceCheck": "Pürfung des freien Speichers überspringen", "SkipFreeSpaceCheck": "Pürfung des freien Speichers überspringen",
"SkipFreeSpaceCheckWhenImportingHelpText": "Aktiviere diese Option, wenn es Readarr nicht möglich ist, den freien Speicherplatz des Stammverzeichnisses für Autoren zu erkennen", "SkipFreeSpaceCheckWhenImportingHelpText": "Aktiviere diese Option, wenn es Readarr nicht möglich ist, den freien Speicherplatz des Stammverzeichnises für Autoren zu erkennen",
"SorryThatAuthorCannotBeFound": "Schade, dieser Film kann nicht gefunden werden.", "SorryThatAuthorCannotBeFound": "Schade, dieser Film kann nicht gefunden werden.",
"SorryThatBookCannotBeFound": "Schade, dieser Film kann nicht gefunden werden.", "SorryThatBookCannotBeFound": "Schade, dieser Film kann nicht gefunden werden.",
"Source": "Quelle", "Source": "Quelle",
@@ -426,7 +426,7 @@
"UnmonitoredHelpText": "Nicht beobachtete Filme im iCal-Feed einschließen", "UnmonitoredHelpText": "Nicht beobachtete Filme im iCal-Feed einschließen",
"UpdateAll": "Alle aktualisieren", "UpdateAll": "Alle aktualisieren",
"UpdateAutomaticallyHelpText": "Updates automatisch herunteraden und installieren. Es kann weiterhin unter \"System -> Updates\" ein manuelles Update angestoßen werden", "UpdateAutomaticallyHelpText": "Updates automatisch herunteraden und installieren. Es kann weiterhin unter \"System -> Updates\" ein manuelles Update angestoßen werden",
"UpdateMechanismHelpText": "Readarr's Built-In Updater oder ein Script verwenden", "UpdateMechanismHelpText": "Benutze Readarr's Built-In Updater oder ein Script",
"UpdateScriptPathHelpText": "Pfad zu einem benutzerdefinierten Skript, das ein extrahiertes Update-Paket übernimmt und den Rest des Update-Prozesses abwickelt", "UpdateScriptPathHelpText": "Pfad zu einem benutzerdefinierten Skript, das ein extrahiertes Update-Paket übernimmt und den Rest des Update-Prozesses abwickelt",
"Updates": "Updates", "Updates": "Updates",
"UpgradeAllowedHelpText": "Wenn deaktiviert wird die Qualität nicht verbessert", "UpgradeAllowedHelpText": "Wenn deaktiviert wird die Qualität nicht verbessert",
@@ -543,7 +543,7 @@
"DeleteMetadataProfile": "Metadaten Profil löschen", "DeleteMetadataProfile": "Metadaten Profil löschen",
"ImportListExclusions": "Ausschlüsse der Importliste", "ImportListExclusions": "Ausschlüsse der Importliste",
"ImportLists": "Importlisten", "ImportLists": "Importlisten",
"ImportListSettings": "Allgemeine Importlisten-Einstellungen", "ImportListSettings": "Allgemeine Einstellungen der Importliste",
"ImportListSpecificSettings": "Listenspezifische Einstellungen importieren", "ImportListSpecificSettings": "Listenspezifische Einstellungen importieren",
"IndexerLongTermStatusCheckSingleClientMessage": "Indexer wegen über 6 Stunden langen bestehenden Fehlern nicht verfügbar: {0}", "IndexerLongTermStatusCheckSingleClientMessage": "Indexer wegen über 6 Stunden langen bestehenden Fehlern nicht verfügbar: {0}",
"IndexerPriorityHelpText": "Indexer Priorität von 1 (höchste) bis 50 (niedrigste). Standard: 25. Wird beim Holen von Veröffentlichungen als Tiebreaker für ansonsten gleiche Veröffentlichungen verwendet, Readarr benützt weiterhin alle aktivierten Indexer für RSS-Synchronisierung und -Suche.", "IndexerPriorityHelpText": "Indexer Priorität von 1 (höchste) bis 50 (niedrigste). Standard: 25. Wird beim Holen von Veröffentlichungen als Tiebreaker für ansonsten gleiche Veröffentlichungen verwendet, Readarr benützt weiterhin alle aktivierten Indexer für RSS-Synchronisierung und -Suche.",
@@ -702,7 +702,7 @@
"ImportListStatusCheckSingleClientMessage": "Listen aufgrund von Fehlern nicht verfügbar: {0}", "ImportListStatusCheckSingleClientMessage": "Listen aufgrund von Fehlern nicht verfügbar: {0}",
"ImportMechanismHealthCheckMessage": "Aktiviere die Verarbeitung der abgeschlossenen Downloads", "ImportMechanismHealthCheckMessage": "Aktiviere die Verarbeitung der abgeschlossenen Downloads",
"IndexerRssHealthCheckNoIndexers": "Da keine Indexer mit aktivierter RSS-Synchronisierung aktiviert sind, erfasst Readarr neue Erscheinungen nicht automatisch", "IndexerRssHealthCheckNoIndexers": "Da keine Indexer mit aktivierter RSS-Synchronisierung aktiviert sind, erfasst Readarr neue Erscheinungen nicht automatisch",
"IndexerSearchCheckNoInteractiveMessage": "Keine Indexer mit aktivierter interaktiver Suche verfügbar, Readarr liefert keine interaktiven Suchergebnisse", "IndexerSearchCheckNoInteractiveMessage": "Keine Indexer mit interaktiver Suche verfügbar, Readarr liefert keine interaktiven Suchergebnisse",
"IndexerStatusCheckSingleClientMessage": "Indexer aufgrund von Fehlern nicht verfügbar: {0}", "IndexerStatusCheckSingleClientMessage": "Indexer aufgrund von Fehlern nicht verfügbar: {0}",
"ChownGroup": "chown Gruppe", "ChownGroup": "chown Gruppe",
"AllowFingerprintingHelpText": "Benutze Fingerabdrücke um die Genauigkeit der Buch Übereinstimmungen zu verbessern", "AllowFingerprintingHelpText": "Benutze Fingerabdrücke um die Genauigkeit der Buch Übereinstimmungen zu verbessern",
@@ -877,7 +877,5 @@
"ShowBookCount": "Zeige Anzahl an Büchern", "ShowBookCount": "Zeige Anzahl an Büchern",
"SkipPartBooksAndSets": "Überspringe Teilbücher und Sets", "SkipPartBooksAndSets": "Überspringe Teilbücher und Sets",
"TagsHelpText": "Gilt für Autoren mit mindestens einem passenden Tag. Leer lassen, um auf alle Autoren anzuwenden", "TagsHelpText": "Gilt für Autoren mit mindestens einem passenden Tag. Leer lassen, um auf alle Autoren anzuwenden",
"TagsSettingsSummary": "Verwalten von Autoren-, Profil-, Beschränkungs- und Benachrichtigungs-Tags", "TagsSettingsSummary": "Verwalten von Autoren-, Profil-, Beschränkungs- und Benachrichtigungs-Tags"
"ApplicationURL": "Anwendungs-URL",
"ApplicationUrlHelpText": "Die externe URL der Anwendung inklusive http(s)://, Port und URL-Basis"
} }
+3 -7
View File
@@ -6,8 +6,8 @@
"Size": " Μέγεθος", "Size": " Μέγεθος",
"Source": "Πηγή", "Source": "Πηγή",
"Uptime": "Ώρα", "Uptime": "Ώρα",
"20MinutesTwenty": "20 λεπτά: {0}", "20MinutesTwenty": "60 λεπτά: {0}",
"45MinutesFourtyFive": "45 λεπτά: {0}", "45MinutesFourtyFive": "60 λεπτά: {0}",
"60MinutesSixty": "60 λεπτά: {0}", "60MinutesSixty": "60 λεπτά: {0}",
"APIKey": "Κλειδί API", "APIKey": "Κλειδί API",
"About": "Σχετικά", "About": "Σχετικά",
@@ -557,9 +557,5 @@
"Test": "Δοκιμή", "Test": "Δοκιμή",
"AddList": "Προσθήκη Λίστας", "AddList": "Προσθήκη Λίστας",
"RenameFiles": "Μετονομασία αρχείων", "RenameFiles": "Μετονομασία αρχείων",
"ManualImportSelectEdition": "Μη αυτόματη εισαγωγή - Επιλέξτε ταινία", "ManualImportSelectEdition": "Μη αυτόματη εισαγωγή - Επιλέξτε ταινία"
"AllExpandedCollapseAll": "Σύμπτυξη Όλων",
"AllExpandedExpandAll": "Ανάπτυξη Όλων",
"AddNewItem": "Προσθήκη Νέου",
"AddMissing": "Προσθήκη Στοιχείου Που Λείπει"
} }
@@ -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",
+8 -61
View File
@@ -448,7 +448,7 @@
"LoadingBooksFailed": "Elokuvatiedostojen lataaminen epäonnistui", "LoadingBooksFailed": "Elokuvatiedostojen lataaminen epäonnistui",
"ProxyPasswordHelpText": "Käyttäjätunnus ja salasana tulee syöttää vain tarvittaessa. Muussa tapauksessa jätä kentät tyhjiksi.", "ProxyPasswordHelpText": "Käyttäjätunnus ja salasana tulee syöttää vain tarvittaessa. Muussa tapauksessa jätä kentät tyhjiksi.",
"SslCertPathHelpTextWarning": "Käyttöönotto vaatii uudelleenkäynnistyksen.", "SslCertPathHelpTextWarning": "Käyttöönotto vaatii uudelleenkäynnistyksen.",
"UnableToLoadMetadataProfiles": "Metatietoprofiileja ei voida ladata.", "UnableToLoadMetadataProfiles": "Metatietoprofiilien lataus epäonnistui.",
"DownloadClientCheckDownloadingToRoot": "Lataustyökalu '{0}' sijoittaa lataukset juurikansioon '{1}' ja näin ei pitäisi tehdä, vaan lataukset tulee tallentaa erilliseen sijaintiin.", "DownloadClientCheckDownloadingToRoot": "Lataustyökalu '{0}' sijoittaa lataukset juurikansioon '{1}' ja näin ei pitäisi tehdä, vaan lataukset tulee tallentaa erilliseen sijaintiin.",
"ReplaceIllegalCharactersHelpText": "Korvaa laittomat merkit. Jos ei käytössä, laittomat merkit poistetaan.", "ReplaceIllegalCharactersHelpText": "Korvaa laittomat merkit. Jos ei käytössä, laittomat merkit poistetaan.",
"OutputPath": "Tallennussijainti", "OutputPath": "Tallennussijainti",
@@ -624,7 +624,7 @@
"UpdateAvailable": "Uusi päivitys on saatavilla", "UpdateAvailable": "Uusi päivitys on saatavilla",
"Filters": "Suodattimet", "Filters": "Suodattimet",
"General": "Yleiset", "General": "Yleiset",
"ImportMechanismHealthCheckMessage": "Käytä valmiiden latausten käsittelyä", "ImportMechanismHealthCheckMessage": "Ota valmiiden latausten käsittely käyttöön",
"IndexerJackettAll": "Jackettin ei-tuettua 'all'-päätettä käyttävät tietolähteet: {0}", "IndexerJackettAll": "Jackettin ei-tuettua 'all'-päätettä käyttävät tietolähteet: {0}",
"IndexersSettingsSummary": "Sisältölähteet ja julkaisurajoitukset.", "IndexersSettingsSummary": "Sisältölähteet ja julkaisurajoitukset.",
"IndexerStatusCheckAllClientMessage": "Tietolähteet eivät ole käytettävissä virheiden vuoksi", "IndexerStatusCheckAllClientMessage": "Tietolähteet eivät ole käytettävissä virheiden vuoksi",
@@ -637,7 +637,7 @@
"RemotePathMappingCheckFilesGenericPermissions": "Lataustyökalu '{0}' ilmoitti tiedostosijainniksi '{1}', mutta Radarr ei näe sitä. Saata joutua muokkaamaan kansion käyttöoikeuksia.", "RemotePathMappingCheckFilesGenericPermissions": "Lataustyökalu '{0}' ilmoitti tiedostosijainniksi '{1}', mutta Radarr ei näe sitä. Saata joutua muokkaamaan kansion käyttöoikeuksia.",
"RemotePathMappingCheckImportFailed": "Radarr ei voinut tuoda elokuvaa. Tarkista loki saadaksesi lisätietoja.", "RemotePathMappingCheckImportFailed": "Radarr ei voinut tuoda elokuvaa. Tarkista loki saadaksesi lisätietoja.",
"CouldntFindAnyResultsForTerm": "Haku '{0}' ei tuottanut tuloksia.", "CouldntFindAnyResultsForTerm": "Haku '{0}' ei tuottanut tuloksia.",
"IndexerSearchCheckNoAutomaticMessage": "Automaattihaussa käytettäviä tietolähteitä ei ole käytettävissä, eikä automaattisia hakutuloksia ole tämän vuoksi saatavilla.", "IndexerSearchCheckNoAutomaticMessage": "Ei hakemistoja, joissa automaattinen haku on käytössä, Radarr ei tarjoa automaattisia hakutuloksia",
"IndexerSearchCheckNoAvailableIndexersMessage": "Kaikki hakukelpoiset tietolähteet ovat tilapaisesti tavoittamattomissa viimeaikaisten tietolähdevirheiden vuoksi.", "IndexerSearchCheckNoAvailableIndexersMessage": "Kaikki hakukelpoiset tietolähteet ovat tilapaisesti tavoittamattomissa viimeaikaisten tietolähdevirheiden vuoksi.",
"RemotePathMappingCheckDockerFolderMissing": "Käytät Dockeria ja lataustyökalu '{0}' sijoittaa lataukset kohteeseen '{1}', mutta kansiota ei näytä olevan olemassa containerissa. Tarkista etäsijaintiesi kartoitukset ja containerin tallennusmedia-asetukset.", "RemotePathMappingCheckDockerFolderMissing": "Käytät Dockeria ja lataustyökalu '{0}' sijoittaa lataukset kohteeseen '{1}', mutta kansiota ei näytä olevan olemassa containerissa. Tarkista etäsijaintiesi kartoitukset ja containerin tallennusmedia-asetukset.",
"RemotePathMappingCheckDownloadPermissions": "Radarr näkee, muttei voi käyttää ladattua elokuvaa '{0}'. Todennäköinen syy on sijainnin käyttöoikeusvirhe.", "RemotePathMappingCheckDownloadPermissions": "Radarr näkee, muttei voi käyttää ladattua elokuvaa '{0}'. Todennäköinen syy on sijainnin käyttöoikeusvirhe.",
@@ -663,7 +663,7 @@
"FileWasDeletedByUpgrade": "Tiedosto poistettiin päivityksen tuontia varten.", "FileWasDeletedByUpgrade": "Tiedosto poistettiin päivityksen tuontia varten.",
"HealthNoIssues": "Kokoonpanossasi ei ole ongelmia.", "HealthNoIssues": "Kokoonpanossasi ei ole ongelmia.",
"IndexerRssHealthCheckNoAvailableIndexers": "Kaikki RSS-tietolähteet ovat tilapaisesti tavoittamattomissa viimeaikaisten tietolähdevirheiden vuoksi.", "IndexerRssHealthCheckNoAvailableIndexers": "Kaikki RSS-tietolähteet ovat tilapaisesti tavoittamattomissa viimeaikaisten tietolähdevirheiden vuoksi.",
"IndexerRssHealthCheckNoIndexers": "Yhtään RSS-synkronointia käyttävää tietolähdettä ei ole käytettävissä, eikä uusia julkaisuja sen vuoksi siepata automaattisesti.", "IndexerRssHealthCheckNoIndexers": "Yhtään RSS-synkronointia käyttävää tietolähdettä ei ole käytettävissä, eikä Radarr tämän vuoksi sieppaa uusia julkaisuja automaattisesti.",
"IndexerStatusCheckSingleClientMessage": "Tietolähteet eivät ole käytettävissä virheiden vuoksi: {0}", "IndexerStatusCheckSingleClientMessage": "Tietolähteet eivät ole käytettävissä virheiden vuoksi: {0}",
"MissingFromDisk": "Radarr ei löytänyt tiedostoa levyltä, joten sen kytkös kirjaston elokuvaan poistettiin", "MissingFromDisk": "Radarr ei löytänyt tiedostoa levyltä, joten sen kytkös kirjaston elokuvaan poistettiin",
"Monitor": "Valvo", "Monitor": "Valvo",
@@ -676,7 +676,7 @@
"OnRename": "Kun elokuva nimetään uudelleen", "OnRename": "Kun elokuva nimetään uudelleen",
"OnUpgrade": "Kun elokuva päivitetään", "OnUpgrade": "Kun elokuva päivitetään",
"ProxyCheckResolveIpMessage": "Määritetyn välityspalvelimen '{0}' IP-osoitteen selvitys epäonnistui.", "ProxyCheckResolveIpMessage": "Määritetyn välityspalvelimen '{0}' IP-osoitteen selvitys epäonnistui.",
"QualitySettingsSummary": "Laatumääritykset erilaisia sisältömuotoja ja tiedostokokoja varten.", "QualitySettingsSummary": "Laatumääritykset erilaisia sisältömuotoja ja teidostokokoja varten.",
"QueueIsEmpty": "Jono on tyhjä", "QueueIsEmpty": "Jono on tyhjä",
"ReadarrSupportsAnyDownloadClient": "Radarr tukee kaikkien Newznab-yhteensopivien lataustyökalujen ohella myös monia muita alla listattuja torrent- ja Usenet-lataustyökaluja.", "ReadarrSupportsAnyDownloadClient": "Radarr tukee kaikkien Newznab-yhteensopivien lataustyökalujen ohella myös monia muita alla listattuja torrent- ja Usenet-lataustyökaluja.",
"RefreshAndScan": "Päivitä ja tarkista", "RefreshAndScan": "Päivitä ja tarkista",
@@ -687,7 +687,7 @@
"SettingsRemotePathMappingRemotePath": "Etäsijainti", "SettingsRemotePathMappingRemotePath": "Etäsijainti",
"SettingsRemotePathMappingRemotePathHelpText": "Lataustyökalun käyttämän hakemiston juurisijainti", "SettingsRemotePathMappingRemotePathHelpText": "Lataustyökalun käyttämän hakemiston juurisijainti",
"SizeLimit": "Kokorajoitus", "SizeLimit": "Kokorajoitus",
"SystemTimeCheckMessage": "Järjestelmän aika on pielessä yli vuorokauden. Ajoitetut tehtävät eivät luultavasti toimi oikein ennen sen korjausta.", "SystemTimeCheckMessage": "Järjestelmän aika on heittä yli vuorokauden verran. Ajoitetut tehtävät eivät luultavasti toimi oikein ennen ajan korjausta.",
"UISettingsSummary": "Kalenterin, päiväyksen ja kellonajan sekä kielen ja heikentyneelle värinäölle sopivan tilan asetukset.", "UISettingsSummary": "Kalenterin, päiväyksen ja kellonajan sekä kielen ja heikentyneelle värinäölle sopivan tilan asetukset.",
"UserAgentProvidedByTheAppThatCalledTheAPI": "User-Agent-tiedon ilmoitti sovellus, joka kommunikoi API:n kanssa", "UserAgentProvidedByTheAppThatCalledTheAPI": "User-Agent-tiedon ilmoitti sovellus, joka kommunikoi API:n kanssa",
"WatchLibraryForChangesHelpText": "Suorita automaattinen uudelleentutkinta, kun juurikansiossa havaitaan tiedostomuutoksia.", "WatchLibraryForChangesHelpText": "Suorita automaattinen uudelleentutkinta, kun juurikansiossa havaitaan tiedostomuutoksia.",
@@ -699,61 +699,8 @@
"MonitorNewBooks": "Valvo uusia kirjoja", "MonitorNewBooks": "Valvo uusia kirjoja",
"RenameFiles": "Uudelleennimeä tiedostot", "RenameFiles": "Uudelleennimeä tiedostot",
"Test": "Kokeile", "Test": "Kokeile",
"AllowFingerprintingHelpText": "Tarkenna kirjojen täsmäystarkkuutta piiloleimauksen avulla.", "AllowFingerprintingHelpText": "Käytä piiloleimausta kirjojen täsmäyksen tarkennukseen",
"AllowFingerprinting": "Salli piiloleimaus", "AllowFingerprinting": "Salli piiloleimaus",
"Database": "Tietokanta", "Database": "Tietokanta",
"ManualImportSelectEdition": "Manuaalinen tuonti - Valitse versio", "ManualImportSelectEdition": "Manuaalinen tuonti - Valitse versio"
"ApplicationURL": "Sovelluksen URL-osoite",
"MusicBrainzReleaseID": "MusicBrainz-julkaisutunniste",
"MusicBrainzTrackID": "MusicBrainz-kappaletunniste",
"ShowBanners": "Näytä bannerit",
"CatalogNumber": "Luettelonumero",
"Continuing": "Jatkuva",
"DeleteRootFolder": "Poista juurikansio",
"DiscCount": "Levyjen määrä",
"EditList": "Muokkaa listaa",
"EnableProfile": "Käytä profiilia",
"FutureDays": "Tulevat päivät",
"ImportFailures": "Tuontivirheet",
"ImportLists": "Tuotilistat",
"ImportListSettings": "Tuontilistojen yleiset asetukset",
"ImportListSpecificSettings": "Tuotilistakohtaiset asetukset",
"IsShowingMonitoredMonitorSelected": "Valvo valittuja",
"IsShowingMonitoredUnmonitorSelected": "Lopeta valittujen valvonta",
"PreferredHelpTexts2": "Positiivisia tuloksia suositaan enemmän",
"MusicbrainzId": "MusicBrainz-tunniste",
"MusicBrainzRecordingID": "MusicBrainz-tallennetunniste",
"ShouldSearchHelpText": "Etsi tietolähteistä hiljattain lisättyjä kohteita. Käytä suurien listojen kanssa varoen.",
"OnDownloadFailure": "Latauksen epäonnistuessa",
"OnDownloadFailureHelpText": "Latauksen epäonnistuessa",
"OnImportFailure": "Tuonnin epäonistuessa",
"OnImportFailureHelpText": "Tuonnin epäonistuessa",
"OnReleaseImport": "Tuotaessa julkaisu",
"OnReleaseImportHelpText": "Tuotaessa julkaisu",
"PreferredHelpTexts3": "Negatiivisia tuloksia suositaan vähemmän",
"ReleaseProfiles": "Julkaisuprofiilit",
"ShowBannersHelpText": "Näytä nimien sijaan bannerit.",
"StatusEndedContinuing": "Jatkuu",
"UnableToLoadMetadataProviderSettings": "Metatietolähteen asetuksia ei voitu ladata",
"UnmappedFiles": "Kartoittamattomat tiedostot",
"UpdatingIsDisabledInsideADockerContainerUpdateTheContainerImageInstead": "Päivittäminen ei ole Docker-säiliössä käytettävissä. Päivitä sen sijaan säiliön kuva.",
"WatchRootFoldersForFileChanges": "Seuraa juurikansioiden tiedostomuutoksia",
"WriteMetadataTags": "Tallenna metatietotagit",
"Country": "Maa",
"MonitoringOptions": "Valvonta-asetukset",
"PastDays": "Menneet päivät",
"SearchMonitored": "Etsi valvottuja",
"SkipRedownload": "Ohita uudelleenlataus",
"TrackNumber": "Kappaleiden numero",
"TrackTitle": "Kappaleiden nimi",
"IsExpandedHideFileInfo": "Piilota tiedostotiedot",
"MetadataConsumers": "Metatietojen kuluttajat",
"MetadataProviderSource": "Metatietotoimittajan lähde",
"MetadataSource": "Metatietojen lähde",
"FutureDaysHelpText": "Päivien määrä, jonka verran tulevaisuuteen iCal-syötettä seurataan.",
"ManualDownload": "Manuaalinen lataus",
"PastDaysHelpText": "Päivien määrä, jonka verran menneisyyteen iCal-syötettä seurataan.",
"DiscNumber": "Levyn numero",
"ForeignIdHelpText": "Ohitettavan kirjailijan/kirjan MusicBrainz-tunniste.",
"IsExpandedShowFileInfo": "Näytä tiedostotiedot"
} }
+1 -3
View File
@@ -877,7 +877,5 @@
"InstanceNameHelpText": "Példánynév a böngésző lapon és a syslog alkalmazás neve", "InstanceNameHelpText": "Példánynév a böngésző lapon és a syslog alkalmazás neve",
"LoadingEditionsFailed": "A kiadások betöltése nem sikerült", "LoadingEditionsFailed": "A kiadások betöltése nem sikerült",
"ManualImportSelectEdition": "Kézi importálás Válaszd ki a Kiadást", "ManualImportSelectEdition": "Kézi importálás Válaszd ki a Kiadást",
"Database": "Adatbázis", "Database": "Adatbázis"
"ApplicationURL": "Alkalmazás URL-je",
"ApplicationUrlHelpText": "Az alkalmazás külső URL-címe, beleértve a http(s)://-t, a portot és az URL-alapot"
} }
+10 -35
View File
@@ -4,7 +4,7 @@
"45MinutesFourtyFive": "45 Minuti: {0}", "45MinutesFourtyFive": "45 Minuti: {0}",
"60MinutesSixty": "60 Minuti: {0}", "60MinutesSixty": "60 Minuti: {0}",
"APIKey": "Chiave API", "APIKey": "Chiave API",
"About": "Info", "About": "Informazioni",
"AddListExclusion": "Aggiungi Lista Esclusioni", "AddListExclusion": "Aggiungi Lista Esclusioni",
"AddingTag": "Aggiungi etichetta", "AddingTag": "Aggiungi etichetta",
"AdvancedSettingsHiddenClickToShow": "Nascosto, clicca per mostrare", "AdvancedSettingsHiddenClickToShow": "Nascosto, clicca per mostrare",
@@ -44,11 +44,11 @@
"AutoRedownloadFailedHelpText": "Cerca e prova a scaricare automaticamente un'altra versione", "AutoRedownloadFailedHelpText": "Cerca e prova a scaricare automaticamente un'altra versione",
"Automatic": "Automatico", "Automatic": "Automatico",
"BackupFolderHelpText": "I percorsi relativi saranno nella cartella AppData di Readarr", "BackupFolderHelpText": "I percorsi relativi saranno nella cartella AppData di Readarr",
"BackupNow": "Esegui backup ora", "BackupNow": "Effettua il Backup adesso",
"BackupRetentionHelpText": "I backup automatici più vecchi del periodo di conservazione verranno eliminati automaticamente", "BackupRetentionHelpText": "I backup automatici più vecchi del periodo di conservazione verranno eliminati automaticamente",
"Backups": "I Backup", "Backups": "I Backup",
"BindAddress": "Indirizzo di Bind", "BindAddress": "Indirizzo di Bind",
"BindAddressHelpText": "Indirizzo IPV4 valido o '*' per tutte le interfacce di rete", "BindAddressHelpText": "Indirizzo IPv4 valido o '*' per tutte le interfacce",
"BindAddressHelpTextWarning": "Richiede il riavvio per avere effetto", "BindAddressHelpTextWarning": "Richiede il riavvio per avere effetto",
"BookIsDownloading": "Libro in download", "BookIsDownloading": "Libro in download",
"BookIsDownloadingInterp": "Libro in download - {0}% {1}", "BookIsDownloadingInterp": "Libro in download - {0}% {1}",
@@ -59,7 +59,7 @@
"Cancel": "Annulla", "Cancel": "Annulla",
"CancelMessageText": "Sei sicuro di voler cancellare questa operazione in sospeso?", "CancelMessageText": "Sei sicuro di voler cancellare questa operazione in sospeso?",
"CertificateValidation": "Convalida del Certificato", "CertificateValidation": "Convalida del Certificato",
"CertificateValidationHelpText": "Cambia quanto rigorosamente vengono validati i certificati HTTPS. Non cambiare senza conoscerne i rischi.", "CertificateValidationHelpText": "Cambia quanto è rigorosa la convalida del certificato HTTPS. Non cambiare a meno che tu non comprenda i rischi.",
"ChangeFileDate": "Cambiare la Data del File", "ChangeFileDate": "Cambiare la Data del File",
"ChangeHasNotBeenSavedYet": "Il cambio non è stato ancora salvato", "ChangeHasNotBeenSavedYet": "Il cambio non è stato ancora salvato",
"ChmodFolder": "Permessi Cartella", "ChmodFolder": "Permessi Cartella",
@@ -381,9 +381,9 @@
"SupportsSearchvalueWillBeUsedWhenInteractiveSearchIsUsed": "Verrà usato durante la ricerca interattiva", "SupportsSearchvalueWillBeUsedWhenInteractiveSearchIsUsed": "Verrà usato durante la ricerca interattiva",
"TagIsNotUsedAndCanBeDeleted": "L'etichetta non è in uso e può essere eliminata", "TagIsNotUsedAndCanBeDeleted": "L'etichetta non è in uso e può essere eliminata",
"Tasks": "Attività", "Tasks": "Attività",
"TestAll": "Prova Tutti", "TestAll": "Testa Tutti",
"TestAllClients": "Testa tutti i client", "TestAllClients": "Testa Tutti i Client",
"TestAllIndexers": "Prova tutti gli indicizzatori", "TestAllIndexers": "Testa tutti gli Indicizzatori",
"TestAllLists": "Testa tutte le liste", "TestAllLists": "Testa tutte le liste",
"ThisWillApplyToAllIndexersPleaseFollowTheRulesSetForthByThem": "Questo verrà applicato a tutti gli indexer, segui le regole impostate da loro", "ThisWillApplyToAllIndexersPleaseFollowTheRulesSetForthByThem": "Questo verrà applicato a tutti gli indexer, segui le regole impostate da loro",
"TimeFormat": "Formato orario", "TimeFormat": "Formato orario",
@@ -544,7 +544,7 @@
"ProxyCheckBadRequestMessage": "Il test del proxy è fallito. Codice Stato: {0}", "ProxyCheckBadRequestMessage": "Il test del proxy è fallito. Codice Stato: {0}",
"ProxyCheckFailedToTestMessage": "Test del proxy fallito: {0}", "ProxyCheckFailedToTestMessage": "Test del proxy fallito: {0}",
"QualitySettingsSummary": "Dimensioni delle qualità e denominazione", "QualitySettingsSummary": "Dimensioni delle qualità e denominazione",
"Queued": "In coda", "Queued": "Messo in coda",
"RefreshAndScan": "Aggiorna e Scansiona", "RefreshAndScan": "Aggiorna e Scansiona",
"RemotePathMappingCheckBadDockerPath": "Stai utilizzando docker; Il client di download {0} mette i download in {1} ma questo non è un percorso valido {2}. Controlla la mappa dei percorsi remoti e le impostazioni del client di download.", "RemotePathMappingCheckBadDockerPath": "Stai utilizzando docker; Il client di download {0} mette i download in {1} ma questo non è un percorso valido {2}. Controlla la mappa dei percorsi remoti e le impostazioni del client di download.",
"RemotePathMappingCheckDockerFolderMissing": "Stai utilizzando docker; il download client {0} riporta files in {1} ma questa directory non sembra esistere nel contenitore. Controlla la mappa dei percorsi remoti e le impostazioni dei volumi del container.", "RemotePathMappingCheckDockerFolderMissing": "Stai utilizzando docker; il download client {0} riporta files in {1} ma questa directory non sembra esistere nel contenitore. Controlla la mappa dei percorsi remoti e le impostazioni dei volumi del container.",
@@ -617,34 +617,9 @@
"Series": "Serie", "Series": "Serie",
"Test": "Test", "Test": "Test",
"InstanceName": "Nome Istanza", "InstanceName": "Nome Istanza",
"InstanceNameHelpText": "Nome istanza nella scheda e per il nome dell'app nel Syslog", "InstanceNameHelpText": "Nome dell'istanza nella scheda e per il nome dell'applicazione Syslog",
"LogRotateHelpText": "Numero massimo di file di log da tenere salvati nella cartella log", "LogRotateHelpText": "Numero massimo di file di log da tenere salvati nella cartella log",
"LogRotation": "Rotazione Log", "LogRotation": "Rotazione Log",
"ManualImportSelectEdition": "Importazione manuale: seleziona Film", "ManualImportSelectEdition": "Importazione manuale: seleziona Film",
"Database": "Database", "Database": "Database"
"ApplicationURL": "URL Applicazione",
"ApplicationUrlHelpText": "L'URL esterno di questa applicazione, incluso http(s)://, porta e URL base",
"CatalogNumber": "Numero Catalogo",
"Country": "Nazione",
"Continuing": "Continuando",
"EditList": "Modifica Lista",
"EnableProfile": "Abilita Profilo",
"ChownGroup": "chown Group",
"DiscNumber": "Numero Disco",
"DeleteImportList": "Cancella la lista di importazione",
"DeleteRootFolder": "Cancella la cartella principale",
"CalibreSettings": "Impostazioni di Calibre",
"Started": "Iniziato",
"CalibreContentServer": "Server di Contenuto Calibre",
"BooksTotal": "Libri ({0})",
"BookTitle": "Titolo del Libro",
"CalibrePassword": "Password di Calibre",
"CalibrePort": "Porta di Calibre",
"CalibreUrlBase": "URL di Base di Calibre",
"CalibreUsername": "Nome Utente di Calibre",
"CalibreHost": "Host di Calibre",
"CalibreLibrary": "Libreria di Calibre",
"CalibreMetadata": "Metadati di Calibre",
"CalibreOutputFormat": "Formato di output di Calibre",
"CalibreOutputProfile": "Profilo di output di Calibre"
} }
+3 -10
View File
@@ -42,7 +42,7 @@
"BackupRetentionHelpText": "Automatische veiligheidskopieën ouder dan de retentie periode zullen worden opgeruimd", "BackupRetentionHelpText": "Automatische veiligheidskopieën ouder dan de retentie periode zullen worden opgeruimd",
"Backups": "Veiligheidskopieën", "Backups": "Veiligheidskopieën",
"BindAddress": "Gebonden Adres", "BindAddress": "Gebonden Adres",
"BindAddressHelpText": "Geldig IPv4-adres of '*' voor alle interfaces", "BindAddressHelpText": "Geldig IPv4 adres of '*' voor alle interfaces",
"BindAddressHelpTextWarning": "Herstarten vereist om in werking te treden", "BindAddressHelpTextWarning": "Herstarten vereist om in werking te treden",
"BookIsDownloading": "Film is aan het downloaden", "BookIsDownloading": "Film is aan het downloaden",
"BookIsDownloadingInterp": "Film is aan het downloaden - {0}% {1}", "BookIsDownloadingInterp": "Film is aan het downloaden - {0}% {1}",
@@ -53,7 +53,7 @@
"Cancel": "Annuleer", "Cancel": "Annuleer",
"CancelMessageText": "Bent u zeker dat u deze taak in afwachting wilt annuleren?", "CancelMessageText": "Bent u zeker dat u deze taak in afwachting wilt annuleren?",
"CertificateValidation": "Certificaat Validatie", "CertificateValidation": "Certificaat Validatie",
"CertificateValidationHelpText": "Wijzig hoe strict HTTPS certificaat validatie is. Wijzig dit niet behalve als je de risico's begrijpt.", "CertificateValidationHelpText": "Wijzig hoe strikt HTTPS certificaat validatie is",
"ChangeFileDate": "Wijzig Bestandsdatum", "ChangeFileDate": "Wijzig Bestandsdatum",
"ChangeHasNotBeenSavedYet": "Wijziging is nog niet opgeslagen", "ChangeHasNotBeenSavedYet": "Wijziging is nog niet opgeslagen",
"ChmodFolder": "chmod Map", "ChmodFolder": "chmod Map",
@@ -591,12 +591,5 @@
"RenameFiles": "Hernoem Bestanden", "RenameFiles": "Hernoem Bestanden",
"Test": "Test", "Test": "Test",
"ManualImportSelectEdition": "Manuele import - Selecteer Film", "ManualImportSelectEdition": "Manuele import - Selecteer Film",
"Database": "Databasis", "Database": "Databasis"
"AddNewItem": "Voeg nieuwe toe",
"AddImportListExclusionHelpText": "Voorkom dat Readarr het boek toevoegt met Import Lijsten of Auteur Verversing",
"AllAuthorBooks": "Alle boeken van deze auteur",
"AllExpandedCollapseAll": "Klap alles in",
"AllExpandedExpandAll": "Klap alles uit",
"AllowAuthorChangeClickToChangeAuthor": "Klik om auteur aan te passen",
"AllowedLanguages": "Toegestane talen"
} }
@@ -224,7 +224,7 @@
"MinimumFreeSpace": "Mínimo de espaço livre", "MinimumFreeSpace": "Mínimo de espaço livre",
"MinimumFreeSpaceWhenImportingHelpText": "Impedir a importação se deixar menos do que esta quantidade de espaço em disco disponível", "MinimumFreeSpaceWhenImportingHelpText": "Impedir a importação se deixar menos do que esta quantidade de espaço em disco disponível",
"MinimumLimits": "Limites mínimos", "MinimumLimits": "Limites mínimos",
"MonoVersion": "Versão Mono", "MonoVersion": "Versão do Mono",
"MoreInfo": "Mais informações", "MoreInfo": "Mais informações",
"MustNotContain": "Não deve conter", "MustNotContain": "Não deve conter",
"Name": "Nome", "Name": "Nome",
@@ -260,7 +260,7 @@
"Preferred": "Preferido", "Preferred": "Preferido",
"PreviewRename": "Visualizar renomeação", "PreviewRename": "Visualizar renomeação",
"Profiles": "Perfis", "Profiles": "Perfis",
"Proper": "Apropriado", "Proper": "Proper",
"PropersAndRepacks": "Propers e repacks", "PropersAndRepacks": "Propers e repacks",
"Protocol": "Protocolo", "Protocol": "Protocolo",
"ProtocolHelpText": "Escolha que protocolo(s) utilizar e qual o preferido ao escolher entre versões iguais", "ProtocolHelpText": "Escolha que protocolo(s) utilizar e qual o preferido ao escolher entre versões iguais",
@@ -761,7 +761,7 @@
"OnRename": "Ao Renomear", "OnRename": "Ao Renomear",
"OnUpgrade": "Ao Atualizar", "OnUpgrade": "Ao Atualizar",
"AppDataLocationHealthCheckMessage": "A atualização não será possível para evitar a exclusão de AppData na atualização", "AppDataLocationHealthCheckMessage": "A atualização não será possível para evitar a exclusão de AppData na atualização",
"IndexerSearchCheckNoInteractiveMessage": "Nenhum indexador disponível com a Pesquisa interativa habilitada, o Readarr não fornecerá nenhum resultado de pesquisa interativo", "IndexerSearchCheckNoInteractiveMessage": "Nenhum indexador disponível com a Pesquisa Interativa habilitada, Readarr não irá prover quaisquer resultados para pesquisa interativa",
"ConnectSettingsSummary": "Notificações, conexões com servidores/tocadores de mídia e scripts personalizados", "ConnectSettingsSummary": "Notificações, conexões com servidores/tocadores de mídia e scripts personalizados",
"DownloadClientStatusCheckAllClientMessage": "Todos os clientes download não estão disponíveis devido a falhas", "DownloadClientStatusCheckAllClientMessage": "Todos os clientes download não estão disponíveis devido a falhas",
"DownloadClientsSettingsSummary": "Clientes de download, gerenciamento do download e mapeamento remoto de caminhos", "DownloadClientsSettingsSummary": "Clientes de download, gerenciamento do download e mapeamento remoto de caminhos",
@@ -821,7 +821,7 @@
"ReadarrSupportsAnyDownloadClient": "Readarr suporta muitos clientes populares de download de torrent e usenet.", "ReadarrSupportsAnyDownloadClient": "Readarr suporta muitos clientes populares de download de torrent e usenet.",
"RemotePathMappingCheckDockerFolderMissing": "Você está usando o docker; cliente de download {0} coloca downloads em {1}, mas esse diretório parece não existir dentro do contêiner. Revise seus mapeamentos de caminho remoto e configurações de volume de contêiner.", "RemotePathMappingCheckDockerFolderMissing": "Você está usando o docker; cliente de download {0} coloca downloads em {1}, mas esse diretório parece não existir dentro do contêiner. Revise seus mapeamentos de caminho remoto e configurações de volume de contêiner.",
"RemotePathMappingCheckFilesLocalWrongOSPath": "O cliente de download local {0} relatou arquivos em {1}, mas este não é um caminho {2} válido. Revise as configurações do seu cliente de download.", "RemotePathMappingCheckFilesLocalWrongOSPath": "O cliente de download local {0} relatou arquivos em {1}, mas este não é um caminho {2} válido. Revise as configurações do seu cliente de download.",
"RemotePathMappingCheckGenericPermissions": "O cliente de download {0} coloca os downloads em {1}, mas Readarr não pode ver este diretório. Pode ser necessário ajustar as permissões da pasta.", "RemotePathMappingCheckGenericPermissions": "O cliente de download {0} coloca os downloads em {1}, mas o Readarr não pode ver este diretório. Pode ser necessário ajustar as permissões da pasta.",
"RemotePathMappingCheckRemoteDownloadClient": "O cliente de download remoto {0} relatou arquivos em {1}, mas este diretório parece não existir. Provavelmente faltando mapeamento de caminho remoto.", "RemotePathMappingCheckRemoteDownloadClient": "O cliente de download remoto {0} relatou arquivos em {1}, mas este diretório parece não existir. Provavelmente faltando mapeamento de caminho remoto.",
"SettingsRemotePathMappingLocalPathHelpText": "Caminho que Readarr deve usar para acessar o caminho remoto localmente", "SettingsRemotePathMappingLocalPathHelpText": "Caminho que Readarr deve usar para acessar o caminho remoto localmente",
"RootFolderCheckSingleMessage": "Pasta raiz ausente: {0}", "RootFolderCheckSingleMessage": "Pasta raiz ausente: {0}",
@@ -851,7 +851,7 @@
"UpdateCheckStartupTranslocationMessage": "Não é possível instalar a atualização porque a pasta de inicialização '{0}' está em uma pasta App Translocation.", "UpdateCheckStartupTranslocationMessage": "Não é possível instalar a atualização porque a pasta de inicialização '{0}' está em uma pasta App Translocation.",
"TagsSettingsSummary": "Gerenciar etiquetas de autor, perfil, restrição e notificação", "TagsSettingsSummary": "Gerenciar etiquetas de autor, perfil, restrição e notificação",
"InstanceNameHelpText": "Nome da instância na guia e para o nome do aplicativo Syslog", "InstanceNameHelpText": "Nome da instância na guia e para o nome do aplicativo Syslog",
"AddList": "Adicionar Lista", "AddList": "Adicionar à Lista",
"DataExistingBooks": "Monitorar livros que você tem arquivos ou que não foram lançados ainda", "DataExistingBooks": "Monitorar livros que você tem arquivos ou que não foram lançados ainda",
"DataListMonitorAll": "Monitorar autores e todos os livros para cada autor incluído na lista de importação", "DataListMonitorAll": "Monitorar autores e todos os livros para cada autor incluído na lista de importação",
"DataListMonitorNone": "Não monitorar autores ou livros", "DataListMonitorNone": "Não monitorar autores ou livros",
@@ -861,7 +861,7 @@
"RenameFiles": "Renomear Arquivos", "RenameFiles": "Renomear Arquivos",
"Test": "Testar", "Test": "Testar",
"WriteMetadataTags": "Salvar Etiquetas de Metadados", "WriteMetadataTags": "Salvar Etiquetas de Metadados",
"RestartRequiredHelpTextWarning": "Requer reinicialização para ter efeito", "RestartRequiredHelpTextWarning": "Requer reinicio para fazer efeito",
"InstanceName": "Nome da instância", "InstanceName": "Nome da instância",
"ConvertToFormat": "Converter para o Formato", "ConvertToFormat": "Converter para o Formato",
"DataAllBooks": "Monitorar todos os livros", "DataAllBooks": "Monitorar todos os livros",
@@ -877,7 +877,5 @@
"SetReadarrTags": "Configurar Etiquetas do Readarr", "SetReadarrTags": "Configurar Etiquetas do Readarr",
"Database": "Banco de dados", "Database": "Banco de dados",
"LoadingEditionsFailed": "Falha ao carregar edições", "LoadingEditionsFailed": "Falha ao carregar edições",
"ManualImportSelectEdition": "Importação Manual - Selecionar Edição", "ManualImportSelectEdition": "Importação Manual - Selecionar Edição"
"ApplicationURL": "URL do Aplicativo",
"ApplicationUrlHelpText": "A URL externa deste aplicativo, incluindo http(s)://, porta e base da URL"
} }
+1 -4
View File
@@ -592,8 +592,5 @@
"Started": "Запущено", "Started": "Запущено",
"Database": "База данных", "Database": "База данных",
"InstanceName": "Имя экземпляра", "InstanceName": "Имя экземпляра",
"InstanceNameHelpText": "Имя экземпляра на вкладке и для имени приложения системного журнала", "InstanceNameHelpText": "Имя экземпляра на вкладке и для имени приложения системного журнала"
"AllowedLanguages": "Разрешенные языки",
"ApplicationURL": "URL-адрес приложения",
"ApplicationUrlHelpText": "Внешний URL-адрес этого приложения, включая http(s)://, порт и базовый URL-адрес"
} }
+1 -3
View File
@@ -803,7 +803,5 @@
"DataExistingBooks": "Bevaka album som har filer eller inte har släppts än", "DataExistingBooks": "Bevaka album som har filer eller inte har släppts än",
"DataFirstBook": "Bevaka första album. Alla andra album kommer bli ignorerade", "DataFirstBook": "Bevaka första album. Alla andra album kommer bli ignorerade",
"DataFuturebooks": "Bevaka albums som inte har släppts än", "DataFuturebooks": "Bevaka albums som inte har släppts än",
"DataMissingBooks": "Bevaka album som har filer eller inte har släppts än", "DataMissingBooks": "Bevaka album som har filer eller inte har släppts än"
"ApplicationURL": "Applikations-URL",
"ApplicationUrlHelpText": "Denna applikations externa URL inklusive http(s)://, port och URL-bas"
} }
+1 -371
View File
@@ -95,375 +95,5 @@
"BackupFolderHelpText": "Відносні шляхи будуть у каталозі AppData Radarr", "BackupFolderHelpText": "Відносні шляхи будуть у каталозі AppData Radarr",
"BlocklistHelpText": "Забороняє Radarr знову автоматично захопити цей випуск", "BlocklistHelpText": "Забороняє Radarr знову автоматично захопити цей випуск",
"UsingExternalUpdateMechanismBranchUsedByExternalUpdateMechanism": "Гілка, що використовується зовнішнім механізмом оновлення", "UsingExternalUpdateMechanismBranchUsedByExternalUpdateMechanism": "Гілка, що використовується зовнішнім механізмом оновлення",
"AddList": "Додати список", "AddList": "Додати список"
"ShowDateAdded": "Показати дату додавання",
"UnableToLoadBlocklist": "Не вдалося завантажити список блокувань",
"UnableToLoadQualityProfiles": "Не вдалося завантажити профілі якості",
"YesCancel": "Так, скасувати",
"Yesterday": "Вчора",
"ImportFailedInterp": "Помилка імпорту: {0}",
"NoLogFiles": "Немає файлів журналу",
"RSSSync": "Синхронізація RSS",
"RSSSyncInterval": "Інтервал синхронізації RSS",
"DeleteTag": "Видалити тег",
"DownloadClientCheckDownloadingToRoot": "Клієнт завантаження {0} розміщує завантаження в кореневій папці {1}. Ви не повинні завантажувати в кореневу папку.",
"DownloadClientCheckNoneAvailableMessage": "Немає доступного клієнта для завантаження",
"DownloadClients": "Клієнти завантажувачів",
"Enable": "Увімкнути",
"EnableAutomaticSearch": "Увімкнути автоматичний пошук",
"EnableColorImpairedMode": "Увімкнути режим із порушенням кольору",
"EnableColorImpairedModeHelpText": "Змінений стиль, щоб користувачі з вадами кольору могли краще розрізняти кольорову кодовану інформацію",
"Interval": "Інтервал",
"ApplicationURL": "URL програми",
"ApplicationUrlHelpText": "Зовнішня URL-адреса цієї програми, включаючи http(s)://, порт і базу URL-адрес",
"DownloadClientsSettingsSummary": "Клієнти завантаження, обробка завантажень і віддалені відображення шляхів",
"DownloadClientStatusCheckAllClientMessage": "Усі клієнти завантаження недоступні через збої",
"DownloadFailedCheckDownloadClientForMoreDetails": "Помилка завантаження: перевірте клієнт завантаження, щоб дізнатися більше",
"DownloadFailedInterp": "Помилка завантаження: {0}",
"DownloadWarningCheckDownloadClientForMoreDetails": "Попередження про завантаження: перевірте клієнт завантаження, щоб дізнатися більше",
"Edition": "Видання",
"EnableCompletedDownloadHandlingHelpText": "Автоматично імпортувати завершені завантаження з клієнта завантажень",
"EnableHelpText": "Увімкнути створення файлу метаданих для цього типу метаданих",
"EnableSslHelpText": " Щоб набуло чинності, потрібно перезапустити роботу від імені адміністратора",
"ImportListStatusCheckAllClientMessage": "Усі списки недоступні через помилки",
"ImportMechanismHealthCheckMessage": "Увімкнути обробку завершених завантажень",
"IncludeHealthWarningsHelpText": "Включайте попередження про здоров’я",
"Path": "Шлях",
"RetryingDownloadInterp": "Повторна спроба завантажити {0} о {1}",
"UnableToLoadBackups": "Не вдалося завантажити резервні копії",
"UpdateScriptPathHelpText": "Шлях до спеціального сценарію, який приймає витягнутий пакет оновлення та обробляє решту процесу оновлення",
"Message": "Повідомлення",
"DownloadClientSettings": "Налаштування клієнта завантажувача",
"MIA": "MIA",
"MinimumAgeHelpText": "Тільки Usenet: мінімальний вік NZB у хвилинах до їх захоплення. Використовуйте це, щоб дати новим випускам час для поширення до вашого провайдера usenet.",
"MinimumFreeSpace": "Мінімальний вільний простір",
"MinimumFreeSpaceWhenImportingHelpText": "Заборонити імпорт, якщо він залишить менше, ніж цей обсяг доступного дискового простору",
"EnableInteractiveSearch": "Увімкнути інтерактивний пошук",
"ErrorLoadingContents": "Помилка завантаження вмісту",
"ExtraFileExtensionsHelpTexts1": "Розділений комами список додаткових файлів для імпорту (.nfo буде імпортовано як .nfo-orig)",
"FileWasDeletedByUpgrade": "Файл видалено, щоб імпортувати оновлення",
"FirstDayOfWeek": "Перший день тижня",
"IndexerSearchCheckNoAvailableIndexersMessage": "Усі індексатори з можливістю пошуку тимчасово недоступні через нещодавні помилки індексатора",
"None": "Жодного",
"GeneralSettings": "Загальні налаштування",
"NotificationTriggers": "Тригери сповіщень",
"GeneralSettingsSummary": "Порт, SSL, ім’я користувача/пароль, проксі, аналітика та оновлення",
"NotAvailable": "Недоступний",
"GoToInterp": "Перейти до {0}",
"NotMonitored": "Не контролюється",
"NoUpdatesAreAvailable": "Немає оновлень",
"HealthNoIssues": "Немає проблем із вашою конфігурацією",
"ICalHttpUrlHelpText": "Скопіюйте цю URL-адресу до своїх клієнтів або натисніть, щоб підписатися, якщо ваш браузер підтримує веб-канал",
"OnHealthIssue": "Про питання здоров'я",
"OnHealthIssueHelpText": "Про питання здоров'я",
"OnRename": "При перейменуванні",
"OnRenameHelpText": "При перейменуванні",
"OnUpgrade": "При оновленні",
"OpenBrowserOnStart": "Відкрийте браузер при запуску",
"OutputPath": "Вихідний шлях",
"Overview": "Огляд",
"PackageVersion": "Версія пакета",
"PageSize": "Розмір сторінки",
"PageSizeHelpText": "Кількість елементів для показу на кожній сторінці",
"Password": "Пароль",
"IndexerPriority": "Пріоритет індексатора",
"IndexerRssHealthCheckNoAvailableIndexers": "Усі індексатори з підтримкою rss тимчасово недоступні через нещодавні помилки індексатора",
"DiskSpace": "Дисковий простір",
"Permissions": "Дозволи",
"Port": "Порт",
"PortNumber": "Номер порту",
"IndexerStatusCheckSingleClientMessage": "Індексатори недоступні через помилки: {0}",
"PosterSize": "Розмір плаката",
"PreviewRename": "Попередній перегляд Перейменування",
"Profiles": "Профілі",
"Proxy": "Проксі",
"ProtocolHelpText": "Виберіть протокол(и) для використання та який із них є кращим під час вибору між однаковими випусками",
"ProxyPasswordHelpText": "Вам потрібно лише ввести ім’я користувача та пароль, якщо вони потрібні. В іншому випадку залиште їх порожніми.",
"ProxyType": "Тип проксі",
"ProxyUsernameHelpText": "Вам потрібно лише ввести ім’я користувача та пароль, якщо вони потрібні. В іншому випадку залиште їх порожніми.",
"PublishedDate": "Дата публікації",
"Quality": "Якість",
"QualityDefinitions": "Визначення якості",
"QualityProfile": "Профіль якості",
"RemotePathMappingCheckLocalFolderMissing": "Клієнт віддаленого завантаження {0} розміщує завантаження в {1}, але цей каталог не існує. Ймовірно, віддалений шлях відсутній або неправильний.",
"RemotePathMappingCheckLocalWrongOSPath": "Локальний клієнт завантаження {0} розміщує завантаження в {1}, але це недійсний шлях {2}. Перегляньте налаштування клієнта завантаження.",
"RemoveSelected": "Видалити вибране",
"RemotePathMappingCheckWrongOSPath": "Клієнт віддаленого завантаження {0} розміщує завантаження в {1}, але це недійсний шлях {2}. Перегляньте свої віддалені відображення шляхів і завантажте налаштування клієнта.",
"MaximumLimits": "Максимальні обмеження",
"Reset": "Скинути",
"Restart": "Перезавантажити",
"MinimumLimits": "Мінімальні обмеження",
"MoreInfo": "Більше інформації",
"RootFolders": "Кореневі папки",
"SearchFiltered": "Пошук відфільтровано",
"NoBackupsAreAvailable": "Немає резервних копій",
"SourcePath": "Вихідний шлях",
"StartupDirectory": "Каталог запуску",
"Status": "Статус",
"SSLPort": "Порт SSL",
"Started": "Розпочато",
"StartTypingOrSelectAPathBelow": "Почніть вводити текст або виберіть шлях нижче",
"TagIsNotUsedAndCanBeDeleted": "Тег не використовується і може бути видалений",
"Tags": "Теги",
"SystemTimeCheckMessage": "Системний час вимкнено більш ніж на 1 день. Заплановані завдання можуть не працювати належним чином, доки час не буде виправлено",
"TestAll": "Перевірити все",
"Test": "Тест",
"TestAllIndexers": "Перевірити всі індексатори",
"ProxyBypassFilterHelpText": "Використовуйте «,» як роздільник і «*». як символ підстановки для субдоменів",
"ThisCannotBeCancelled": "Це не можна скасувати після запуску без вимкнення всіх ваших індексаторів.",
"Time": "Час",
"UnableToAddANewListPleaseTryAgain": "Не вдалося додати новий список, спробуйте ще раз.",
"UnableToAddANewQualityProfilePleaseTryAgain": "Не вдалося додати новий профіль якості, спробуйте ще раз.",
"UnableToAddANewRemotePathMappingPleaseTryAgain": "Не вдалося додати нове зіставлення віддаленого шляху, спробуйте ще раз.",
"UnableToAddANewNotificationPleaseTryAgain": "Не вдалося додати нове сповіщення, спробуйте ще раз.",
"UnableToLoadDownloadClients": "Не вдалося завантажити клієнти для завантаження",
"UnableToLoadGeneralSettings": "Не вдалося завантажити загальні налаштування",
"UnableToLoadIndexers": "Не вдалося завантажити індексатори",
"UnableToLoadMediaManagementSettings": "Не вдалося завантажити налаштування керування медіафайлами",
"UnableToLoadMetadata": "Не вдалося завантажити метадані",
"UnableToLoadNotifications": "Не вдалося завантажити сповіщення",
"UnableToLoadQualities": "Неможливо завантажити якості",
"UnableToLoadNamingSettings": "Не вдалося завантажити налаштування імен",
"UnableToLoadUISettings": "Не вдалося завантажити налаштування інтерфейсу користувача",
"Ungroup": "Розгрупувати",
"UnableToLoadRootFolders": "Не вдалося завантажити кореневі папки",
"UnableToLoadTags": "Не вдалося завантажити теги",
"UnableToLoadTheCalendar": "Неможливо завантажити календар",
"RemotePathMappingCheckFilesLocalWrongOSPath": "Локальний клієнт завантаження {0} повідомив про файли в {1}, але це недійсний шлях {2}. Перегляньте налаштування клієнта завантаження.",
"RemotePathMappingCheckFilesWrongOSPath": "Клієнт віддаленого завантаження {0} повідомив про файли в {1}, але це недійсний шлях {2}. Перегляньте свої віддалені відображення шляхів і завантажте налаштування клієнта.",
"Unmonitored": "Неконтрольований",
"Year": "Рік",
"RemoveHelpTextWarning": "Видалення видалить завантаження та файл(и) із клієнта завантаження.",
"Search": "Пошук",
"SendAnonymousUsageData": "Надсилати анонімні дані про використання",
"SetPermissionsLinuxHelpText": "Чи слід запускати chmod, коли файли імпортуються/перейменовуються?",
"ShowMonitoredHelpText": "Показати відстежуваний статус під плакатом",
"ShowReleaseDate": "Показати дату випуску",
"SkipFreeSpaceCheck": "Пропустити перевірку вільного місця",
"Tomorrow": "Завтра",
"UILanguageHelpTextWarning": "Потрібно перезавантажити браузер",
"UISettings": "Налаштування інтерфейсу користувача",
"UISettingsSummary": "Параметри календаря, дати та кольору",
"UnableToAddANewIndexerPleaseTryAgain": "Не вдалося додати новий індексатор, спробуйте ще раз.",
"UnableToLoadIndexerOptions": "Не вдалося завантажити параметри індексатора",
"UnableToLoadRemotePathMappings": "Неможливо завантажити віддалені відображення шляхів",
"UpdateAutomaticallyHelpText": "Автоматичне завантаження та встановлення оновлень. Ви все ще зможете встановити з System: Updates",
"UpdateCheckStartupTranslocationMessage": "Неможливо встановити оновлення, оскільки папка запуску \"{0}\" знаходиться в папці переміщення програми.",
"Version": "Версія",
"Original": "Оригінал",
"Options": "Опції",
"RefreshInformationAndScanDisk": "Оновити інформацію та сканувати диск",
"ManualImport": "Імпорт вручну",
"ProxyCheckBadRequestMessage": "Не вдалося перевірити проксі. Код стану: {0}",
"Reason": "Причина",
"RemoveFromQueue": "Видалити з черги",
"Restore": "Відновлення",
"SelectAll": "Вибрати все",
"TorrentDelayHelpText": "Затримка в хвилинах, щоб зачекати, перш ніж захопити торрент",
"Fixed": "Виправлено",
"DownloadClient": "Клієнт завантажувача",
"DownloadClientCheckUnableToCommunicateMessage": "Неможливо зв'язатися з {0}.",
"DownloadClientStatusCheckSingleClientMessage": "Завантаження клієнтів недоступне через помилки: {0}",
"Edit": "Редагувати",
"EnableAutomaticAdd": "Увімкнути автоматичне додавання",
"DestinationPath": "Шлях призначення",
"DetailedProgressBar": "Детальний індикатор прогресу",
"DetailedProgressBarHelpText": "Показати текст на панелі виконання",
"Disabled": "Вимкнено",
"Duration": "Тривалість",
"EnableRSS": "Увімкнути RSS",
"EnableSSL": "Увімкнути SSL",
"Ended": "Завершено",
"ErrorLoadingPreviews": "Помилка завантаження попереднього перегляду",
"Exception": "Виняток",
"FailedDownloadHandling": "Помилка обробки завантаження",
"FailedToLoadQueue": "Не вдалося завантажити чергу",
"FileDateHelpText": "Змінити дату файлу під час імпорту/повторного сканування",
"FileManagement": "Керування файлами",
"Filename": "Ім'я файлу",
"FileNames": "Імена файлів",
"Files": "Файли",
"FileWasDeletedByViaUI": "Файл видалено через інтерфейс користувача",
"Folder": "Папка",
"Folders": "Папки",
"General": "Загальний",
"Global": "Глобальний",
"IllRestartLater": "Я перезапущу пізніше",
"ImportedTo": "Імпортовано в",
"ImportExtraFiles": "Імпорт додаткових файлів",
"Filters": "Фільтри",
"Importing": "Імпорт",
"Indexers": "Індексатори",
"ImportListStatusCheckSingleClientMessage": "Списки недоступні через помилки: {0}",
"Indexer": "Індексатор",
"IndexerLongTermStatusCheckAllClientMessage": "Усі індексатори недоступні через збої більше 6 годин",
"IndexerLongTermStatusCheckSingleClientMessage": "Індексатори недоступні через збої більше 6 годин: {0}",
"IndexerJackettAll": "Індексатори, які використовують непідтримувану кінцеву точку Jackett 'all': {0}",
"IndexerSettings": "Налаштування індексатора",
"IndexersSettingsSummary": "Індексатори та обмеження випуску",
"IndexerStatusCheckAllClientMessage": "Усі індексатори недоступні через збої",
"InstanceName": "Ім'я екземпляра",
"InstanceNameHelpText": "Ім’я екземпляра на вкладці та ім’я програми Syslog",
"Language": "Мова",
"Level": "Рівень",
"Lists": "Списки",
"MaximumSize": "Максимальний розмір",
"Mechanism": "Механізм",
"MediaInfo": "Медіа інформація",
"MinimumAge": "Мінімальний вік",
"Mode": "Режим",
"MustContain": "Має містити",
"MustNotContain": "Не повинен містити",
"Name": "Ім'я",
"NamingSettings": "Налаштування імен",
"New": "Новий",
"NoLeaveIt": "Ні, залиште це",
"NoLimitForAnyRuntime": "Немає обмежень для будь-якого часу виконання",
"NoMinimumForAnyRuntime": "Немає мінімуму для будь-якого часу виконання",
"OnUpgradeHelpText": "При оновленні",
"Progress": "Прогрес",
"Proper": "Належний",
"Protocol": "Протокол",
"ProxyCheckFailedToTestMessage": "Не вдалося перевірити проксі: {0}",
"ProxyCheckResolveIpMessage": "Не вдалося визначити IP-адресу для налаштованого проксі-сервера {0}",
"RemotePathMappingCheckRemoteDownloadClient": "Клієнт віддаленого завантаження {0} повідомив про файли в {1}, але цей каталог, здається, не існує. Ймовірно, відсутнє відображення віддаленого шляху.",
"RemoveFromDownloadClient": "Видалити з клієнта завантаження",
"Reorder": "Змінити порядок",
"ReplaceIllegalCharacters": "Замінити неприпустимі символи",
"ResetAPIKey": "Скинути ключ API",
"RestartNow": "Перезавантажити зараз",
"RestartRequiredHelpTextWarning": "Щоб набуло чинності, потрібно перезапустити",
"Save": "Зберегти",
"Scheduled": "За розкладом",
"Score": "Оцінка",
"ScriptPath": "Шлях сценарію",
"SearchAll": "Пошук у всіх",
"SearchForMissing": "Розшук зниклих",
"SearchSelected": "Пошук вибрано",
"Security": "Безпека",
"SetPermissions": "Встановити дозволи",
"SetPermissionsLinuxHelpTextWarning": "Якщо ви не впевнені, що ці налаштування роблять, не змінюйте їх.",
"Settings": "Налаштування",
"SettingsRemotePathMappingLocalPath": "Місцевий шлях",
"SettingsRemotePathMappingRemotePath": "Віддалений шлях",
"SettingsRemotePathMappingRemotePathHelpText": "Кореневий шлях до каталогу, до якого має доступ клієнт завантаження",
"ShowCutoffUnmetIconHelpText": "Показувати піктограму для файлів, коли обмеження не досягнуто",
"ShowMonitored": "Показати Моніторинг",
"ShowPath": "Показати шлях",
"ShowQualityProfileHelpText": "Покажіть якісний профіль під плакатом",
"ShowSearch": "Показати пошук",
"ShowSizeOnDisk": "Показати розмір на диску",
"ShowTitle": "Показати назву",
"SizeLimit": "Обмеження розміру",
"Source": "Джерело",
"Style": "Стиль",
"Tasks": "Задачі",
"TestAllClients": "Перевірте всіх клієнтів",
"TestAllLists": "Перевірити всі списки",
"TimeFormat": "Формат часу",
"Title": "Назва",
"Today": "Сьогодні",
"TotalFileSize": "Загальний розмір файлу",
"UILanguage": "Мова інтерфейсу користувача",
"UnableToAddANewDownloadClientPleaseTryAgain": "Не вдається додати новий клієнт для завантаження, повторіть спробу.",
"UnableToLoadDelayProfiles": "Неможливо завантажити профілі затримки",
"UnableToLoadDownloadClientOptions": "Не вдалося завантажити параметри клієнта для завантаження",
"UnableToLoadLists": "Не вдалося завантажити списки",
"UnableToLoadQualityDefinitions": "Не вдалося завантажити визначення якості",
"UnselectAll": "Скасувати вибір усіх",
"UpdateAvailable": "Доступне нове оновлення",
"UpdateCheckStartupNotWritableMessage": "Неможливо встановити оновлення, оскільки папка запуску \"{0}\" не може бути записана користувачем \"{1}\".",
"UpdateCheckUINotWritableMessage": "Неможливо встановити оновлення, оскільки папка інтерфейсу користувача \"{0}\" не може бути записана користувачем \"{1}\".",
"Updates": "Оновлення",
"Uptime": "Час роботи",
"URLBase": "URL-адреса",
"UseHardlinksInsteadOfCopy": "Використовуйте жорсткі посилання замість копіювати",
"UsenetDelay": "Затримка Usenet",
"UseProxy": "Використовуйте проксі",
"Username": "Ім'я користувача",
"Wanted": "Розшукується",
"WeekColumnHeader": "Заголовок стовпця тижня",
"Local": "Місцевий",
"LogFiles": "Файли журналів",
"LogLevel": "Рівень журналу",
"Logs": "Журнали",
"MarkAsFailed": "Позначити як помилку",
"MarkAsFailedMessageText": "Ви впевнені, що бажаєте позначити \"{0}\" як невдале?",
"QualityProfiles": "Профілі якості",
"QualitySettings": "Налаштування якості",
"QualitySettingsSummary": "Якісні розміри та найменування",
"Queue": "Черга",
"Queued": "У черзі",
"QueueIsEmpty": "Черга порожня",
"ReadTheWikiForMoreInformation": "Читайте Wiki для отримання додаткової інформації",
"Real": "Справжня",
"RecycleBinCleanupDaysHelpText": "Встановіть значення 0, щоб вимкнути автоматичне очищення",
"RecycleBinCleanupDaysHelpTextWarning": "Файли в кошику, старші за вибрану кількість днів, будуть очищені автоматично",
"RecyclingBin": "Сміттєвий кошик",
"RecyclingBinCleanup": "Очищення сміттєвого кошика",
"Redownload": "Повторне завантаження",
"Refresh": "Оновити",
"RefreshAndScan": "Оновити та сканувати",
"UsenetDelayHelpText": "Затримка в хвилинах, щоб зачекати, перш ніж отримати випуск від Usenet",
"DeleteImportListExclusion": "Видалити виключення зі списку імпорту",
"DeleteIndexer": "Видалити індексатор",
"DeleteNotification": "Видалити сповіщення",
"DeleteQualityProfile": "Видалити профіль якості",
"Docker": "Docker",
"Group": "Група",
"History": "Історія",
"Host": "Хост",
"Hostname": "Ім'я хоста",
"ICalFeed": "Канал iCal",
"IgnoredAddresses": "Ігноровані адреси",
"IgnoredPlaceHolder": "Додайте нове обмеження",
"Missing": "Відсутня",
"Preferred": "Бажано",
"RemotePathMappingCheckDockerFolderMissing": "Ви використовуєте docker; клієнт завантаження {0} розміщує завантаження в {1}, але цей каталог не існує всередині контейнера. Перегляньте свої віддалені відображення шляхів і налаштування обсягу контейнера.",
"RemotePathMappingCheckFileRemoved": "Файл {0} видалено під час обробки.",
"RemotePathMappingCheckFilesBadDockerPath": "Ви використовуєте docker; завантажити клієнтські {0} звітні файли в {1}, але це недійсний {2} шлях. Перегляньте свої віддалені відображення шляхів і завантажте налаштування клієнта.",
"ReleaseGroup": "Група випуску",
"ReleaseRejected": "Реліз відхилено",
"ReleaseTitle": "Назва випуску",
"ReleaseWillBeProcessedInterp": "Випуск буде оброблено {0}",
"Reload": "Перезавантажити",
"RemotePathMappingCheckBadDockerPath": "Ви використовуєте docker; клієнт завантаження {0} розміщує завантаження в {1}, але це недійсний шлях {2}. Перегляньте свої віддалені відображення шляхів і завантажте налаштування клієнта.",
"RenameFiles": "Перейменування файлів",
"RestoreBackup": "Відновлення резервної копії",
"Result": "Результат",
"Retention": "Утримання",
"RetentionHelpText": "Лише Usenet: встановіть нуль, щоб налаштувати необмежену утримку",
"RootFolder": "Коренева папка",
"RemotePathMappings": "Віддалені відображення шляхів",
"Remove": "Видалити",
"RemoveCompletedDownloadsHelpText": "Видалити імпортовані завантаження з історії клієнта завантажень",
"RemovedFromTaskQueue": "Видалено з черги завдань",
"RemoveFailedDownloadsHelpText": "Видаліть невдалі завантаження з історії завантажень клієнта",
"RemoveFilter": "Видалити фільтр",
"RemoveFromBlocklist": "Видалити зі списку блокувань",
"RootFolderCheckMultipleMessage": "Відсутні кілька кореневих папок: {0}",
"RootFolderCheckSingleMessage": "Відсутня коренева папка: {0}",
"TorrentDelay": "Затримка торрента",
"Torrents": "Торренти",
"UpgradeAllowedHelpText": "Якщо відключені якості не будуть покращені",
"MediaManagementSettings": "Налаштування Управління медіа",
"OnGrab": "При захопленні",
"OnGrabHelpText": "При захопленні",
"SSLCertPassword": "Пароль SSL сертифіката",
"SSLCertPath": "Шлях сертифіката SSL",
"IncludeUnmonitored": "Включити неконтрольований",
"MediaManagement": "Управління медіа",
"MetadataSettings": "Налаштування метаданих",
"Monitor": "Контрольований",
"Monitored": "Відстежується",
"UI": "Інтерфейс користувача",
"Usenet": "Usenet",
"Logging": "Журналування",
"MaintenanceRelease": "Випуск для обслуговування: виправлення помилок та інші покращення. Щоб отримати докладнішу інформацію, перегляньте історію фіксації Github",
"Metadata": "Метадані",
"GrabRelease": "Захопити реліз",
"GrabSelected": "Захопити вибране",
"IconForCutoffUnmet": "Значок \"Не виконано відсікання\"",
"Grab": "Захопити",
"GrabID": "Захопити ID"
} }
@@ -83,7 +83,7 @@
"DeleteImportListMessageText": "您确定要删除列表 '{0}'", "DeleteImportListMessageText": "您确定要删除列表 '{0}'",
"DeleteIndexer": "删除索引", "DeleteIndexer": "删除索引",
"DeleteIndexerMessageText": "您确定要删除索引 '{0}'吗?", "DeleteIndexerMessageText": "您确定要删除索引 '{0}'吗?",
"DeleteMetadataProfileMessageText": "确定要删除元数据配置吗‘{0}’?", "DeleteMetadataProfileMessageText": "确定要删除影片质量配置“{0}",
"DeleteNotification": "删除消息推送", "DeleteNotification": "删除消息推送",
"DeleteNotificationMessageText": "您确定要删除推送 '{0}' 吗?", "DeleteNotificationMessageText": "您确定要删除推送 '{0}' 吗?",
"DeleteQualityProfile": "删除电影质量配置", "DeleteQualityProfile": "删除电影质量配置",
@@ -433,7 +433,7 @@
"WeekColumnHeader": "日期格式", "WeekColumnHeader": "日期格式",
"Year": "年", "Year": "年",
"YesCancel": "是,取消", "YesCancel": "是,取消",
"20MinutesTwenty": "20 分钟{0}", "20MinutesTwenty": "20分钟: {0}",
"45MinutesFourtyFive": "45分钟: {0}", "45MinutesFourtyFive": "45分钟: {0}",
"60MinutesSixty": "60分钟: {0}", "60MinutesSixty": "60分钟: {0}",
"APIKey": "API Key", "APIKey": "API Key",
@@ -877,7 +877,5 @@
"CollapseMultipleBooksHelpText": "折叠在同日发行的多本书籍", "CollapseMultipleBooksHelpText": "折叠在同日发行的多本书籍",
"ASIN": "亚马逊标准识别码", "ASIN": "亚马逊标准识别码",
"NoTagsHaveBeenAddedYet": "尚未添加标签,添加标签以链接具有延迟配置文件、限制或通知的作者。单击{0}以了解有关Readarr中标签的更多信息。", "NoTagsHaveBeenAddedYet": "尚未添加标签,添加标签以链接具有延迟配置文件、限制或通知的作者。单击{0}以了解有关Readarr中标签的更多信息。",
"BindAddressHelpText": "有效的 IPv4 地址或以'*'代表所有接口", "BindAddressHelpText": "有效的 IPv4 地址或以'*'代表所有接口"
"ApplicationURL": "程序URL",
"ApplicationUrlHelpText": "此应用的外部URL,包含 http(s)://、端口和基本URL"
} }
+2 -2
View File
@@ -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";
} }
@@ -125,7 +125,7 @@ namespace NzbDrone.Core.MetadataSource.BookInfo
catch (BookInfoException e) catch (BookInfoException e)
{ {
_logger.Warn(e, "Unexpected error getting book info"); _logger.Warn(e, "Unexpected error getting book info");
throw new BookNotFoundException(foreignBookId); throw new AuthorNotFoundException(foreignBookId);
} }
} }
@@ -378,29 +378,14 @@ namespace NzbDrone.Core.MetadataSource.BookInfo
private Book GetEditionInfo(int id, bool getAllEditions) private Book GetEditionInfo(int id, bool getAllEditions)
{ {
HttpRequest httpRequest; var httpRequest = _requestBuilder.GetRequestBuilder().Create()
HttpResponse httpResponse; .SetSegment("route", $"book/{id}")
.Build();
while (true) httpRequest.SuppressHttpError = true;
{
httpRequest = _requestBuilder.GetRequestBuilder().Create()
.SetSegment("route", $"book/{id}")
.Build();
httpRequest.SuppressHttpError = true; // we expect a redirect
var httpResponse = _httpClient.Get(httpRequest);
// we expect a redirect
httpResponse = _httpClient.Get(httpRequest);
if (httpResponse.StatusCode == HttpStatusCode.TooManyRequests)
{
WaitUntilRetry(httpResponse);
}
else
{
break;
}
}
if (httpResponse.StatusCode == HttpStatusCode.NotFound) if (httpResponse.StatusCode == HttpStatusCode.NotFound)
{ {
@@ -413,9 +398,9 @@ namespace NzbDrone.Core.MetadataSource.BookInfo
} }
var location = httpResponse.Headers.GetSingleValue("Location"); var location = httpResponse.Headers.GetSingleValue("Location");
var split = location.Split('/').Reverse().ToList(); var split = location.Split('/');
var newId = split[0]; var type = split[0];
var type = split[1]; var newId = split[1];
Book book; Book book;
List<AuthorMetadata> authors; List<AuthorMetadata> authors;
@@ -452,10 +437,6 @@ namespace NzbDrone.Core.MetadataSource.BookInfo
trimmed.AuthorMetadata = book.AuthorMetadata.Value; trimmed.AuthorMetadata = book.AuthorMetadata.Value;
trimmed.SeriesLinks = book.SeriesLinks; trimmed.SeriesLinks = book.SeriesLinks;
var edition = book.Editions.Value.SingleOrDefault(e => e.ForeignEditionId == id.ToString()); var edition = book.Editions.Value.SingleOrDefault(e => e.ForeignEditionId == id.ToString());
if (edition != null)
{
edition.Monitored = true;
}
trimmed.Editions = new List<Edition> { edition }; trimmed.Editions = new List<Edition> { edition };
book = trimmed; book = trimmed;
@@ -469,32 +450,16 @@ namespace NzbDrone.Core.MetadataSource.BookInfo
private List<Book> MapSearchResult(List<int> ids) private List<Book> MapSearchResult(List<int> ids)
{ {
HttpRequest httpRequest; var httpRequest = _requestBuilder.GetRequestBuilder().Create()
HttpResponse<BulkBookResource> httpResponse; .SetSegment("route", $"book/bulk")
.SetHeader("Content-Type", "application/json")
.Build();
while (true) httpRequest.SetContent(ids.ToJson());
{
httpRequest = _requestBuilder.GetRequestBuilder().Create()
.SetSegment("route", $"book/bulk")
.SetHeader("Content-Type", "application/json")
.Build();
httpRequest.SetContent(ids.ToJson()); httpRequest.AllowAutoRedirect = true;
httpRequest.AllowAutoRedirect = true; var httpResponse = _httpClient.Post<BulkBookResource>(httpRequest);
httpRequest.SuppressHttpError = true;
httpResponse = _httpClient.Post<BulkBookResource>(httpRequest);
if (httpResponse.StatusCode == HttpStatusCode.TooManyRequests)
{
WaitUntilRetry(httpResponse);
}
else
{
break;
}
}
var mapped = MapBulkBook(httpResponse.Resource); var mapped = MapBulkBook(httpResponse.Resource);
@@ -609,12 +574,7 @@ namespace NzbDrone.Core.MetadataSource.BookInfo
if (httpResponse.HasHttpError) if (httpResponse.HasHttpError)
{ {
if (httpResponse.StatusCode == HttpStatusCode.TooManyRequests) if (httpResponse.StatusCode == HttpStatusCode.NotFound)
{
WaitUntilRetry(httpResponse);
continue;
}
else if (httpResponse.StatusCode == HttpStatusCode.NotFound)
{ {
throw new AuthorNotFoundException(foreignAuthorId); throw new AuthorNotFoundException(foreignAuthorId);
} }
@@ -664,12 +624,6 @@ namespace NzbDrone.Core.MetadataSource.BookInfo
// this may redirect to an author // this may redirect to an author
var httpResponse = _httpClient.Get(httpRequest); var httpResponse = _httpClient.Get(httpRequest);
if (httpResponse.StatusCode == HttpStatusCode.TooManyRequests)
{
WaitUntilRetry(httpResponse);
continue;
}
if (httpResponse.StatusCode == HttpStatusCode.NotFound) if (httpResponse.StatusCode == HttpStatusCode.NotFound)
{ {
throw new BookNotFoundException(foreignBookId); throw new BookNotFoundException(foreignBookId);
@@ -678,9 +632,9 @@ namespace NzbDrone.Core.MetadataSource.BookInfo
if (httpResponse.HasHttpRedirect) if (httpResponse.HasHttpRedirect)
{ {
var location = httpResponse.Headers.GetSingleValue("Location"); var location = httpResponse.Headers.GetSingleValue("Location");
var split = location.Split('/').Reverse().ToList(); var split = location.Split('/');
var newId = split[0]; var type = split[0];
var type = split[1]; var newId = split[1];
if (type == "author") if (type == "author")
{ {
@@ -739,25 +693,6 @@ namespace NzbDrone.Core.MetadataSource.BookInfo
return Tuple.Create(authorId, book, metadata); return Tuple.Create(authorId, book, metadata);
} }
private void WaitUntilRetry(HttpResponse response)
{
var seconds = 5;
if (response.Headers.ContainsKey("Retry-After"))
{
var retryAfter = response.Headers["Retry-After"];
if (!int.TryParse(retryAfter, out seconds))
{
seconds = 5;
}
}
_logger.Info("BookInfo returned 429, backing off for {0}s", seconds);
Thread.Sleep(TimeSpan.FromSeconds(seconds));
}
private static AuthorMetadata MapAuthorMetadata(AuthorResource resource) private static AuthorMetadata MapAuthorMetadata(AuthorResource resource)
{ {
var metadata = new AuthorMetadata var metadata = new AuthorMetadata
@@ -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;
} }
} }
} }
+5 -14
View File
@@ -215,20 +215,11 @@ namespace NzbDrone.Host
private static IConfiguration GetConfiguration(StartupContext context) private static IConfiguration GetConfiguration(StartupContext context)
{ {
var appFolder = new AppFolderInfo(context); var appFolder = new AppFolderInfo(context);
var configPath = appFolder.GetConfigPath(); return new ConfigurationBuilder()
.AddXmlFile(appFolder.GetConfigPath(), optional: true, reloadOnChange: false)
try .AddInMemoryCollection(new List<KeyValuePair<string, string>> { new ("dataProtectionFolder", appFolder.GetDataProtectionPath()) })
{ .AddEnvironmentVariables()
return new ConfigurationBuilder() .Build();
.AddXmlFile(configPath, optional: true, reloadOnChange: false)
.AddInMemoryCollection(new List<KeyValuePair<string, string>> { new ("dataProtectionFolder", appFolder.GetDataProtectionPath()) })
.AddEnvironmentVariables()
.Build();
}
catch (InvalidDataException ex)
{
throw new InvalidConfigFileException($"{configPath} is corrupt or invalid. Please delete the config file and Readarr will recreate it.", ex);
}
} }
private static string BuildUrl(string scheme, string bindAddress, int port) private static string BuildUrl(string scheme, string bindAddress, int port)
@@ -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);
}
} }
} }
@@ -1,6 +1,5 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Threading;
using FluentValidation; using FluentValidation;
using FluentValidation.Results; using FluentValidation.Results;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
@@ -23,8 +22,6 @@ namespace Readarr.Api.V1.Indexers
private readonly IIndexerFactory _indexerFactory; private readonly IIndexerFactory _indexerFactory;
private readonly Logger _logger; private readonly Logger _logger;
private static readonly object PushLock = new object();
public ReleasePushController(IMakeDownloadDecision downloadDecisionMaker, public ReleasePushController(IMakeDownloadDecision downloadDecisionMaker,
IProcessDownloadDecisions downloadDecisionProcessor, IProcessDownloadDecisions downloadDecisionProcessor,
IIndexerFactory indexerFactory, IIndexerFactory indexerFactory,
@@ -54,13 +51,8 @@ namespace Readarr.Api.V1.Indexers
ResolveIndexer(info); ResolveIndexer(info);
List<DownloadDecision> decisions; var decisions = _downloadDecisionMaker.GetRssDecision(new List<ReleaseInfo> { info });
_downloadDecisionProcessor.ProcessDecisions(decisions);
lock (PushLock)
{
decisions = _downloadDecisionMaker.GetRssDecision(new List<ReleaseInfo> { info });
_downloadDecisionProcessor.ProcessDecisions(decisions);
}
var firstDecision = decisions.FirstOrDefault(); var firstDecision = decisions.FirstOrDefault();
@@ -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);
}
} }
} }
+11 -12
View File
@@ -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();
+1 -1
View File
@@ -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":
+1 -1
View File
@@ -74,7 +74,7 @@ namespace Readarr.Http.REST
} }
var attributes = descriptor.MethodInfo.CustomAttributes; var attributes = descriptor.MethodInfo.CustomAttributes;
if (attributes.Any(x => VALIDATE_ID_ATTRIBUTES.Contains(x.AttributeType)) && !skipValidate) if (attributes.Any(x => VALIDATE_ID_ATTRIBUTES.Contains(x.GetType())) && !skipValidate)
{ {
if (context.ActionArguments.TryGetValue("id", out var idObj)) if (context.ActionArguments.TryGetValue("id", out var idObj))
{ {