Compare commits

..

1 Commits

Author SHA1 Message Date
Mark McDowall
edce18e2cf Added SECURITY.md
(cherry picked from commit 80af164385d9087a627142ca2281ae74ac0572af)
2022-11-19 06:10:03 +00:00
101 changed files with 315 additions and 1514 deletions

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)
## 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
* [GNU GPL v3](http://www.gnu.org/licenses/gpl.html)

View File

@@ -3,6 +3,7 @@
## Reporting a Vulnerability
Please report (suspected) security vulnerabilities on Discord (preferred) to
any of the Servarr Dev role holders (red names) or via email: development@servarr.com. You will receive a response from
either markus101
#2148 or Taloth#7357 or via email: security@sonarr.tv. You will receive a response from
us within 72 hours. If the issue is confirmed, we will release a patch as soon
as possible depending on complexity/severity.

View File

@@ -9,7 +9,7 @@ variables:
testsFolder: './_tests'
yarnCacheFolder: $(Pipeline.Workspace)/.yarn
nugetCacheFolder: $(Pipeline.Workspace)/.nuget/packages
majorVersion: '0.1.2'
majorVersion: '0.1.1'
minorVersion: $[counter('minorVersion', 1)]
readarrVersion: '$(majorVersion).$(minorVersion)'
buildName: '$(Build.SourceBranchName).$(readarrVersion)'

View File

@@ -39,7 +39,6 @@ module.exports = {
plugins: [
'filenames',
'react',
'react-hooks',
'simple-import-sort',
'import'
],
@@ -309,9 +308,7 @@ module.exports = {
'react/react-in-jsx-scope': 2,
'react/self-closing-comp': 2,
'react/sort-comp': 2,
'react/jsx-wrap-multilines': 2,
'react-hooks/rules-of-hooks': 'error',
'react-hooks/exhaustive-deps': 'error'
'react/jsx-wrap-multilines': 2
},
overrides: [
{

View File

@@ -169,16 +169,6 @@ class HistoryRow extends Component {
);
}
if (name === 'sourceTitle') {
return (
<TableRowCell
key={name}
>
{sourceTitle}
</TableRowCell>
);
}
if (name === 'details') {
return (
<TableRowCell

View File

@@ -12,9 +12,9 @@ import ModalBody from 'Components/Modal/ModalBody';
import Portal from 'Components/Portal';
import Scroller from 'Components/Scroller/Scroller';
import { icons, scrollDirections, sizes } from 'Helpers/Props';
import { isMobile as isMobileUtil } from 'Utilities/browser';
import * as keyCodes from 'Utilities/Constants/keyCodes';
import getUniqueElememtId from 'Utilities/getUniqueElementId';
import { isMobile as isMobileUtil } from 'Utilities/mobile';
import HintedSelectInputOption from './HintedSelectInputOption';
import HintedSelectInputSelectedValue from './HintedSelectInputSelectedValue';
import TextInput from './TextInput';

View File

@@ -33,7 +33,7 @@ function ConfirmModal(props) {
return () => unbindShortcut('enter', onConfirm);
}
}, [bindShortcut, unbindShortcut, isOpen, onConfirm]);
}, [isOpen, onConfirm]);
return (
<Modal

View File

@@ -6,9 +6,9 @@ import ReactDOM from 'react-dom';
import FocusLock from 'react-focus-lock';
import ErrorBoundary from 'Components/Error/ErrorBoundary';
import { sizes } from 'Helpers/Props';
import { isIOS } from 'Utilities/browser';
import * as keyCodes from 'Utilities/Constants/keyCodes';
import getUniqueElememtId from 'Utilities/getUniqueElementId';
import { isIOS } from 'Utilities/mobile';
import { setScrollLock } from 'Utilities/scrollLock';
import ModalError from './ModalError';
import styles from './Modal.css';

View File

@@ -1,12 +1,23 @@
import PropTypes from 'prop-types';
import React, { Component } from 'react';
import OverlayScroller from 'Components/Scroller/OverlayScroller';
import Scroller from 'Components/Scroller/Scroller';
import { scrollDirections } from 'Helpers/Props';
import { isMobile as isMobileUtil } from 'Utilities/mobile';
import { isLocked } from 'Utilities/scrollLock';
import styles from './PageContentBody.css';
class PageContentBody extends Component {
//
// Lifecycle
constructor(props, context) {
super(props, context);
this._isMobile = isMobileUtil();
}
//
// Listeners
@@ -30,8 +41,10 @@ class PageContentBody extends Component {
...otherProps
} = this.props;
const ScrollerComponent = this._isMobile ? Scroller : OverlayScroller;
return (
<Scroller
<ScrollerComponent
className={className}
scrollDirection={scrollDirections.VERTICAL}
{...otherProps}
@@ -40,7 +53,7 @@ class PageContentBody extends Component {
<div className={innerClassName}>
{children}
</div>
</Scroller>
</ScrollerComponent>
);
}
}

View File

@@ -1,6 +1,6 @@
import PropTypes from 'prop-types';
import React, { Component } from 'react';
import { Scrollbars } from 'react-custom-scrollbars-2';
import { Scrollbars } from 'react-custom-scrollbars';
import { scrollDirections } from 'Helpers/Props';
import styles from './OverlayScroller.css';

View File

@@ -1,7 +1,7 @@
import PropTypes from 'prop-types';
import React, { Component } from 'react';
import Measure from 'Components/Measure';
import { isMobile as isMobileUtil } from 'Utilities/browser';
import { isMobile as isMobileUtil } from 'Utilities/mobile';
import styles from './SwipeHeader.css';
function cursorPosition(event) {

View File

@@ -5,7 +5,7 @@ import { Manager, Popper, Reference } from 'react-popper';
import Portal from 'Components/Portal';
import { kinds, tooltipPositions } from 'Helpers/Props';
import dimensions from 'Styles/Variables/dimensions';
import { isMobile as isMobileUtil } from 'Utilities/browser';
import { isMobile as isMobileUtil } from 'Utilities/mobile';
import styles from './Tooltip.css';
let maxWidth = null;

View File

@@ -21,7 +21,6 @@ function HostSettings(props) {
port,
urlBase,
instanceName,
applicationUrl,
enableSsl,
sslPort,
sslCertPath,
@@ -59,7 +58,6 @@ function HostSettings(props) {
name="port"
min={1}
max={65535}
autocomplete="off"
helpTextWarning={translate('PortHelpTextWarning')}
onChange={onInputChange}
{...port}
@@ -97,21 +95,6 @@ function HostSettings(props) {
/>
</FormGroup>
<FormGroup
advancedSettings={advancedSettings}
isAdvanced={true}
>
<FormLabel>{translate('ApplicationURL')}</FormLabel>
<FormInputGroup
type={inputTypes.TEXT}
name="applicationUrl"
helpText={translate('ApplicationUrlHelpText')}
onChange={onInputChange}
{...applicationUrl}
/>
</FormGroup>
<FormGroup
advancedSettings={advancedSettings}
isAdvanced={true}

View File

@@ -19,7 +19,7 @@ function PendingChangesModal(props) {
useEffect(() => {
bindShortcut('enter', onConfirm);
}, [bindShortcut, onConfirm]);
}, [onConfirm]);
return (
<Modal

View File

@@ -6,7 +6,6 @@ import getProviderState from 'Utilities/State/getProviderState';
import { removeItem, set, updateItem } from '../baseActions';
const abortCurrentRequests = {};
let lastSaveData = null;
export function createCancelSaveProviderHandler(section) {
return function(getState, payload, dispatch) {
@@ -28,33 +27,27 @@ function createSaveProviderHandler(section, url, options = {}, removeStale = fal
} = payload;
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 = {
url: `${requestUrl}?${$.param(params, true)}`,
method: id ? 'PUT' : 'POST',
url: `${url}?${$.param(queryParams, true)}`,
method: 'POST',
contentType: 'application/json',
dataType: 'json',
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);
abortCurrentRequests[section] = abortRequest;
request.done((data) => {
lastSaveData = null;
if (!Array.isArray(data)) {
data = [data];
}

View File

@@ -71,11 +71,6 @@ export const defaultState = {
label: 'Release Group',
isVisible: false
},
{
name: 'sourceTitle',
label: 'Source Title',
isVisible: false
},
{
name: 'details',
columnLabel: 'Details',

View File

@@ -1,7 +1,4 @@
@define-mixin scrollbar {
scrollbar-color: var(--scrollbarBackgroundColor) transparent;
scrollbar-width: thin;
&::-webkit-scrollbar {
width: 10px;
height: 10px;

View File

@@ -1,18 +1,22 @@
import createAjaxRequest from 'Utilities/createAjaxRequest';
import $ from 'jquery';
function getTranslations() {
let localization = null;
const ajaxOptions = {
async: false,
type: 'GET',
global: false,
dataType: 'json',
url: '/localization',
url: `${window.Readarr.apiRoot}/localization`,
success: function(data) {
localization = data.Strings;
}
};
createAjaxRequest(ajaxOptions);
ajaxOptions.headers = ajaxOptions.headers || {};
ajaxOptions.headers['X-Api-Key'] = window.Readarr.apiKey;
$.ajax(ajaxOptions);
return localization;
}

View File

@@ -10,7 +10,3 @@ export function isMobile() {
export function isIOS() {
return mobileDetect.is('iOS');
}
export function isFirefox() {
return window.navigator.userAgent.toLowerCase().indexOf('firefox/') >= 0;
}

View File

@@ -58,7 +58,7 @@
"react-addons-shallow-compare": "15.6.3",
"react-async-script": "1.2.0",
"react-autosuggest": "10.1.0",
"react-custom-scrollbars-2": "4.5.0",
"react-custom-scrollbars": "4.2.1",
"react-dnd": "14.0.2",
"react-dnd-html5-backend": "14.0.0",
"react-dnd-multi-backend": "6.0.2",
@@ -109,7 +109,6 @@
"eslint-plugin-filenames": "1.3.2",
"eslint-plugin-import": "2.23.4",
"eslint-plugin-react": "7.24.0",
"eslint-plugin-react-hooks": "4.6.0",
"eslint-plugin-simple-import-sort": "7.0.0",
"esprint": "3.1.0",
"file-loader": "6.2.0",

View File

@@ -26,12 +26,12 @@
<PackageVersion Include="Microsoft.Win32.Registry" Version="5.0.0" />
<PackageVersion Include="Mono.Posix.NETStandard" Version="5.20.1.34-servarr22" />
<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="Newtonsoft.Json" Version="13.0.1" />
<PackageVersion Include="NLog.Extensions.Logging" Version="5.1.0" />
<PackageVersion Include="NLog" Version="5.0.5" />
<PackageVersion Include="NLog.Targets.Syslog" Version="7.0.0" />
<PackageVersion Include="NLog.Extensions.Logging" Version="1.7.4" />
<PackageVersion Include="NLog" Version="4.7.14" />
<PackageVersion Include="NLog.Targets.Syslog" Version="6.0.2" />
<PackageVersion Include="Npgsql" Version="6.0.3" />
<PackageVersion Include="NUnit3TestAdapter" Version="4.2.1" />
<PackageVersion Include="NUnit" Version="3.13.3" />

View File

@@ -61,7 +61,6 @@ namespace NzbDrone.Common.Test.InstrumentationTests
[TestCase("https://notifiarr.com/notifier.php: api=1234530f-422f-4aac-b6b3-01233210aaaa&radarr_health_issue_message=Download")]
[TestCase("/readarr/signalr/messages/negotiate?access_token=1234530f422f4aacb6b301233210aaaa&negotiateVersion=1")]
[TestCase(@"[Info] MigrationController: *** Migrating Database=readarr-main;Host=postgres14;Username=mySecret;Password=mySecret;Port=5432;Enlist=False ***")]
[TestCase(@"[Info] MigrationController: *** Migrating Database=readarr-main;Host=postgres14;Username=mySecret;Password=mySecret;Port=5432;token=mySecret;Enlist=False&username=mySecret;mypassword=mySecret;mypass=shouldkeep1;test_token=mySecret;password=123%@%_@!#^#@;use_password=mySecret;get_token=shouldkeep2;usetoken=shouldkeep3;passwrd=mySecret;")]
// Announce URLs (passkeys) Magnet & Tracker
[TestCase(@"magnet_uri"":""magnet:?xt=urn:btih:9pr04sgkillroyimaveql2tyu8xyui&dn=&tr=https%3a%2f%2fxxx.yyy%2f9pr04sg601233210imaveql2tyu8xyui%2fannounce""}")]
@@ -74,36 +73,17 @@ namespace NzbDrone.Common.Test.InstrumentationTests
[TestCase(@"tracker"":""https://xxx.yyy/announce.php?passkey=9pr04sg601233210imaveql2tyu8xyui""}")]
[TestCase(@"tracker"":""http://xxx.yyy/announce.php?passkey=9pr04sg601233210imaveql2tyu8xyui"",""info"":""http://xxx.yyy/info?a=b""")]
// Notifiarr
// Webhooks - Notifiarr
[TestCase(@"https://xxx.yyy/api/v1/notification/readarr/9pr04sg6-0123-3210-imav-eql2tyu8xyui")]
// Discord
[TestCase(@"https://discord.com/api/webhooks/mySecret")]
[TestCase(@"https://discord.com/api/webhooks/mySecret/01233210")]
public void should_clean_message(string message)
{
var cleansedMessage = CleanseLogMessage.Cleanse(message);
cleansedMessage.Should().NotContain("mySecret");
cleansedMessage.Should().NotContain("123%@%_@!#^#@");
cleansedMessage.Should().NotContain("01233210");
}
[TestCase(@"[Info] MigrationController: *** Migrating Database=radarr-main;Host=postgres14;Username=mySecret;Password=mySecret;Port=5432;token=mySecret;Enlist=False&username=mySecret;mypassword=mySecret;mypass=shouldkeep1;test_token=mySecret;password=123%@%_@!#^#@;use_password=mySecret;get_token=shouldkeep2;usetoken=shouldkeep3;passwrd=mySecret;")]
public void should_keep_message(string message)
{
var cleansedMessage = CleanseLogMessage.Cleanse(message);
cleansedMessage.Should().NotContain("mySecret");
cleansedMessage.Should().NotContain("123%@%_@!#^#@");
cleansedMessage.Should().NotContain("01233210");
cleansedMessage.Should().Contain("shouldkeep1");
cleansedMessage.Should().Contain("shouldkeep2");
cleansedMessage.Should().Contain("shouldkeep3");
}
//GoodReads
[TestCase(@"{""signatureMethod"": ""hmacSha1"",""signatureTreatment"": ""escaped"",""type"": ""protectedResource"",""method"": ""GET"",""token"": ""mytoken"",""tokenSecret"": ""mytokensecret"",""requestUrl"": ""https://www.goodreads.com/review/list.xml"",""parameters"": { ""_nc"": ""1"", ""v"": ""2"", ""id"": ""999999999"", ""shelf"": ""currently-reading"", ""per_page"": ""200"", ""page"": ""1""}")]
[TestCase(@"https://www.goodreads.com/series/311911?key=1234530f422f4aacb6b301233210aaaa&_nc=1&format=xml")]

View File

@@ -1,4 +1,4 @@
using System;
using System;
using System.Collections.Generic;
using NzbDrone.Common.EnvironmentInfo;
@@ -24,8 +24,7 @@ namespace NzbDrone.Common.Disk
"/boot",
"/lib",
"/sbin",
"/proc",
"/usr/bin"
"/proc"
};
}
}

View File

@@ -18,7 +18,7 @@ namespace NzbDrone.Common.Instrumentation
new Regex(@"iptorrents\.com/[/a-z0-9?&;]*?(?:[?&;](u|tp)=(?<secret>[^&=;]+?))+(?= |;|&|$)", RegexOptions.Compiled | RegexOptions.IgnoreCase),
new Regex(@"/fetch/[a-z0-9]{32}/(?<secret>[a-z0-9]{32})", RegexOptions.Compiled),
new Regex(@"getnzb.*?(?<=\?|&)(r)=(?<secret>[^&=]+?)(?= |&|$)", RegexOptions.Compiled | RegexOptions.IgnoreCase),
new Regex(@"\b(\w*)?(_?(?<!use|get_)token|username|passwo?rd)=(?<secret>[^&=]+?)(?= |&|$|;)", RegexOptions.Compiled | RegexOptions.IgnoreCase),
new Regex(@"(?<=[?& ;])[^=]*?(_?(?<!use|get_)token|username|passwo?rd)=(?<secret>[^&=]+?)(?= |&|$|;)", RegexOptions.Compiled | RegexOptions.IgnoreCase),
// Trackers Announce Keys; Designed for Qbit Json; should work for all in theory
new Regex(@"announce(\.php)?(/|%2f|%3fpasskey%3d)(?<secret>[a-z0-9]{16,})|(?<secret>[a-z0-9]{16,})(/|%2f)announce"),
@@ -51,10 +51,7 @@ namespace NzbDrone.Common.Instrumentation
// Webhooks
// Notifiarr
new Regex(@"api/v[0-9]/notification/readarr/(?<secret>[\w-]+)", RegexOptions.Compiled | RegexOptions.IgnoreCase),
// Discord
new Regex(@"discord.com/api/webhooks/((?<secret>[\w-]+)/)?(?<secret>[\w-]+)", RegexOptions.Compiled | RegexOptions.IgnoreCase)
new Regex(@"api/v[0-9]/notification/readarr/(?<secret>[\w-]+)", RegexOptions.Compiled | RegexOptions.IgnoreCase)
};
private static readonly Regex CleanseRemoteIPRegex = new Regex(@"(?:Auth-\w+(?<!Failure|Unauthorized) ip|from) (\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})", RegexOptions.Compiled);

View File

@@ -1,6 +1,6 @@
using System.Collections.Generic;
using System.Linq;
using System.Linq;
using NLog;
using NLog.Fluent;
namespace NzbDrone.Common.Instrumentation.Extensions
{
@@ -8,46 +8,47 @@ namespace NzbDrone.Common.Instrumentation.Extensions
{
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);
}
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);
}
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);
}
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);
}
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);
}
private static LogEventBuilder LogSentryMessage(LogEventBuilder logBuilder, LogLevel level, string[] fingerprint)
private static LogBuilder LogSentryMessage(LogBuilder logBuilder, LogLevel level, string[] fingerprint)
{
SentryLogger.ForLogEvent(level)
.CopyLogEvent(logBuilder.LogEvent)
SentryLogger.Log(level)
.CopyLogEvent(logBuilder.LogEventInfo)
.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)
.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);
}
}

View File

@@ -1,15 +1,13 @@
using System.Text;
using NLog;
using NLog;
using NLog.Targets;
namespace NzbDrone.Common.Instrumentation
{
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));
target.Append(result);
return CleanseLogMessage.Cleanse(Layout.Render(logEvent));
}
}
}

View File

@@ -34,8 +34,6 @@ namespace NzbDrone.Common.Instrumentation
var appFolderInfo = new AppFolderInfo(startupContext);
RegisterGlobalFilters();
if (Debugger.IsAttached)
{
RegisterDebugger();
@@ -103,16 +101,6 @@ namespace NzbDrone.Common.Instrumentation
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()
{
var level = LogLevel.Trace;

View File

@@ -206,11 +206,7 @@ namespace NzbDrone.Common.Instrumentation.Sentry
if (ex != null)
{
fingerPrint.Add(ex.GetType().FullName);
if (ex.TargetSite != null)
{
fingerPrint.Add(ex.TargetSite.ToString());
}
fingerPrint.Add(ex.TargetSite.ToString());
if (ex.InnerException != null)
{
fingerPrint.Add(ex.InnerException.GetType().FullName);

View File

@@ -1,4 +1,3 @@
using System;
using System.Collections.Generic;
using System.Linq;
using FizzWare.NBuilder;
@@ -6,7 +5,6 @@ using FluentAssertions;
using Moq;
using NUnit.Framework;
using NzbDrone.Core.Books;
using NzbDrone.Core.Configuration;
using NzbDrone.Core.DecisionEngine.Specifications;
using NzbDrone.Core.MediaFiles;
using NzbDrone.Core.Parser.Model;
@@ -246,89 +244,5 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
.Should()
.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();
}
}
}

View File

@@ -27,7 +27,7 @@ namespace NzbDrone.Core.Test.Download
_downloadClients = new List<IDownloadClient>();
Mocker.GetMock<IProvideDownloadClient>()
.Setup(v => v.GetDownloadClients(It.IsAny<bool>()))
.Setup(v => v.GetDownloadClients())
.Returns(_downloadClients);
Mocker.GetMock<IProvideDownloadClient>()

View File

@@ -24,7 +24,7 @@ namespace NzbDrone.Core.Test.HealthCheck.Checks
public void should_return_warning_when_download_client_has_not_been_configured()
{
Mocker.GetMock<IProvideDownloadClient>()
.Setup(s => s.GetDownloadClients(It.IsAny<bool>()))
.Setup(s => s.GetDownloadClients())
.Returns(Array.Empty<IDownloadClient>());
Subject.Check().ShouldBeWarning();
@@ -40,7 +40,7 @@ namespace NzbDrone.Core.Test.HealthCheck.Checks
.Throws<Exception>();
Mocker.GetMock<IProvideDownloadClient>()
.Setup(s => s.GetDownloadClients(It.IsAny<bool>()))
.Setup(s => s.GetDownloadClients())
.Returns(new IDownloadClient[] { downloadClient.Object });
Subject.Check().ShouldBeError();
@@ -55,7 +55,7 @@ namespace NzbDrone.Core.Test.HealthCheck.Checks
.Returns(new List<DownloadClientItem>());
Mocker.GetMock<IProvideDownloadClient>()
.Setup(s => s.GetDownloadClients(It.IsAny<bool>()))
.Setup(s => s.GetDownloadClients())
.Returns(new IDownloadClient[] { downloadClient.Object });
Subject.Check().ShouldBeOk();

View File

@@ -49,7 +49,7 @@ namespace NzbDrone.Core.Test.HealthCheck.Checks
.Returns(_clientStatus);
Mocker.GetMock<IProvideDownloadClient>()
.Setup(s => s.GetDownloadClients(It.IsAny<bool>()))
.Setup(s => s.GetDownloadClients())
.Returns(new IDownloadClient[] { _downloadClient.Object });
Mocker.GetMock<IDiskProvider>()

View File

@@ -63,7 +63,7 @@ namespace NzbDrone.Core.Test.HealthCheck.Checks
.Returns(_clientStatus);
Mocker.GetMock<IProvideDownloadClient>()
.Setup(s => s.GetDownloadClients(It.IsAny<bool>()))
.Setup(s => s.GetDownloadClients())
.Returns(new IDownloadClient[] { _downloadClient.Object });
Mocker.GetMock<IProvideDownloadClient>()

View File

@@ -25,7 +25,7 @@ namespace NzbDrone.Core.Test.IndexerSearchTests
.Setup(s => s.GetAuthor(It.IsAny<int>()))
.Returns(_author);
Mocker.GetMock<ISearchForReleases>()
Mocker.GetMock<ISearchForNzb>()
.Setup(s => s.AuthorSearch(_author.Id, false, true, false))
.Returns(new List<DownloadDecision>());
@@ -45,7 +45,7 @@ namespace NzbDrone.Core.Test.IndexerSearchTests
Subject.Execute(new AuthorSearchCommand { AuthorId = _author.Id, Trigger = CommandTrigger.Manual });
Mocker.GetMock<ISearchForReleases>()
Mocker.GetMock<ISearchForNzb>()
.Verify(v => v.AuthorSearch(_author.Id, false, true, false),
Times.Exactly(_author.Books.Value.Count(s => s.Monitored)));
}

View File

@@ -75,7 +75,7 @@ namespace NzbDrone.Core.Test.MetadataSource.Goodreads
[Test]
public void getting_details_of_invalid_book()
{
Assert.Throws<BookNotFoundException>(() => Subject.GetBookInfo("1"));
Assert.Throws<BookNotFoundException>(() => Subject.GetBookInfo("99999999"));
}
private void ValidateAuthor(Author author)

View File

@@ -45,17 +45,13 @@ namespace NzbDrone.Core.AuthorStats
}
}
private SqlBuilder Builder()
{
var trueIndicator = _database.DatabaseType == DatabaseType.PostgreSQL ? "true" : "1";
return new SqlBuilder(_database.DatabaseType)
.Select($@"""Authors"".""Id"" AS ""AuthorId"",
private SqlBuilder Builder() => new SqlBuilder(_database.DatabaseType)
.Select(@"""Authors"".""Id"" AS ""AuthorId"",
""Books"".""Id"" AS ""BookId"",
SUM(COALESCE(""BookFiles"".""Size"", 0)) AS ""SizeOnDisk"",
1 AS ""TotalBookCount"",
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""")
.Join<Edition, Book>((e, b) => e.BookId == b.Id)
.Join<Book, Author>((book, author) => book.AuthorMetadataId == author.AuthorMetadataId)
@@ -64,6 +60,5 @@ namespace NzbDrone.Core.AuthorStats
.GroupBy<Author>(x => x.Id)
.GroupBy<Book>(x => x.Id)
.AddParameters(new Dictionary<string, object> { { "currentDate", DateTime.UtcNow } });
}
}
}

View File

@@ -410,8 +410,6 @@ namespace NzbDrone.Core.Configuration
public CertificateValidationType CertificateValidation =>
GetValueEnum("CertificateValidation", CertificateValidationType.Enabled);
public string ApplicationUrl => GetValue("ApplicationUrl", string.Empty);
private string GetValue(string key)
{
return GetValue(key, string.Empty);

View File

@@ -97,6 +97,5 @@ namespace NzbDrone.Core.Configuration
int BackupRetention { get; }
CertificateValidationType CertificateValidation { get; }
string ApplicationUrl { get; }
}
}

View File

@@ -1,11 +1,9 @@
using System;
using NLog;
using NzbDrone.Common.Extensions;
using NzbDrone.Core.Configuration;
using NzbDrone.Core.IndexerSearch.Definitions;
using NzbDrone.Core.MediaFiles;
using NzbDrone.Core.Parser.Model;
using NzbDrone.Core.Qualities;
namespace NzbDrone.Core.DecisionEngine.Specifications
{
@@ -13,14 +11,12 @@ namespace NzbDrone.Core.DecisionEngine.Specifications
{
private readonly IMediaFileService _mediaFileService;
private readonly UpgradableSpecification _upgradableSpecification;
private readonly IConfigService _configService;
private readonly Logger _logger;
public RepackSpecification(IMediaFileService mediaFileService, UpgradableSpecification upgradableSpecification, IConfigService configService, Logger logger)
public RepackSpecification(IMediaFileService mediaFileService, UpgradableSpecification upgradableSpecification, Logger logger)
{
_mediaFileService = mediaFileService;
_upgradableSpecification = upgradableSpecification;
_configService = configService;
_logger = logger;
}
@@ -34,14 +30,6 @@ namespace NzbDrone.Core.DecisionEngine.Specifications
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)
{
var releaseGroup = subject.ParsedBookInfo.ReleaseGroup;
@@ -51,12 +39,6 @@ namespace NzbDrone.Core.DecisionEngine.Specifications
{
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;
if (fileReleaseGroup.IsNullOrWhiteSpace())

View File

@@ -125,22 +125,22 @@ namespace NzbDrone.Core.Download.Clients.Flood
item.RemainingTime = TimeSpan.FromSeconds(properties.Eta);
}
if (properties.Status.Contains("seeding") || properties.Status.Contains("complete"))
{
item.Status = DownloadItemStatus.Completed;
}
else if (properties.Status.Contains("stopped"))
{
item.Status = DownloadItemStatus.Paused;
}
else if (properties.Status.Contains("error"))
if (properties.Status.Contains("error"))
{
item.Status = DownloadItemStatus.Warning;
}
else if (properties.Status.Contains("seeding") || properties.Status.Contains("complete"))
{
item.Status = DownloadItemStatus.Completed;
}
else if (properties.Status.Contains("downloading"))
{
item.Status = DownloadItemStatus.Downloading;
}
else if (properties.Status.Contains("stopped"))
{
item.Status = DownloadItemStatus.Paused;
}
if (item.Status == DownloadItemStatus.Completed)
{

View File

@@ -1,4 +1,4 @@
using System.Collections.Generic;
using System.Collections.Generic;
using Newtonsoft.Json;
using NzbDrone.Common.Disk;
using NzbDrone.Core.Download.Clients.Sabnzbd.JsonConverters;
@@ -7,14 +7,10 @@ namespace NzbDrone.Core.Download.Clients.Sabnzbd
{
public class SabnzbdConfig
{
public SabnzbdConfig()
{
Categories = new List<SabnzbdCategory>();
Servers = new List<object>();
}
public SabnzbdConfigMisc Misc { get; set; }
public List<SabnzbdCategory> Categories { get; set; }
public List<object> Servers { get; set; }
}

View File

@@ -115,7 +115,7 @@ namespace NzbDrone.Core.Download.Clients.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()
{

View File

@@ -37,10 +37,10 @@ namespace NzbDrone.Core.Download.Clients.RTorrent
[FieldDefinition(1, Label = "Port", Type = FieldType.Textbox)]
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; }
[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; }
[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")]
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 NzbDroneValidationResult Validate()

View File

@@ -164,14 +164,14 @@ namespace NzbDrone.Core.Download
}
else
{
_logger.ForDebugEvent()
_logger.Debug()
.Message("No books were just imported, but all books were previously imported, possible issue with download history.")
.Property("AuthorId", trackedDownload.RemoteBook.Author.Id)
.Property("DownloadId", trackedDownload.DownloadItem.DownloadId)
.Property("Title", trackedDownload.DownloadItem.Title)
.Property("Path", trackedDownload.DownloadItem.OutputPath.ToString())
.WriteSentryWarn("DownloadHistoryIncomplete")
.Log();
.Write();
}
trackedDownload.State = TrackedDownloadState.Imported;

View File

@@ -9,7 +9,7 @@ namespace NzbDrone.Core.Download
public interface IProvideDownloadClient
{
IDownloadClient GetDownloadClient(DownloadProtocol downloadProtocol);
IEnumerable<IDownloadClient> GetDownloadClients(bool filterBlockedClients = false);
IEnumerable<IDownloadClient> GetDownloadClients();
IDownloadClient Get(int id);
}
@@ -67,39 +67,14 @@ namespace NzbDrone.Core.Download
return provider;
}
public IEnumerable<IDownloadClient> GetDownloadClients(bool filterBlockedClients = false)
public IEnumerable<IDownloadClient> GetDownloadClients()
{
var enabledClients = _downloadClientFactory.GetAvailableProviders();
if (filterBlockedClients)
{
return FilterBlockedIndexers(enabledClients).ToList();
}
return enabledClients;
return _downloadClientFactory.GetAvailableProviders();
}
public IDownloadClient Get(int 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;
}
}
}
}

View File

@@ -1,6 +1,5 @@
using System.Collections.Generic;
using NzbDrone.Common.Messaging;
using NzbDrone.Core.Download.TrackedDownloads;
using NzbDrone.Core.Qualities;
namespace NzbDrone.Core.Download
@@ -14,6 +13,5 @@ namespace NzbDrone.Core.Download
public DownloadClientItemClientInfo DownloadClientInfo { get; set; }
public string DownloadId { get; set; }
public string Message { get; set; }
public TrackedDownload TrackedDownload { get; set; }
}
}

View File

@@ -110,7 +110,7 @@ namespace NzbDrone.Core.Download
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);
}
}

View File

@@ -42,7 +42,6 @@ namespace NzbDrone.Core.Download
SourceTitle = trackedDownload.DownloadItem.Title,
DownloadClientInfo = trackedDownload.DownloadItem.DownloadClientInfo,
DownloadId = trackedDownload.DownloadItem.DownloadId,
TrackedDownload = trackedDownload,
Message = "Manually ignored"
};

View File

@@ -1,6 +1,5 @@
using System;
using System.Linq;
using System.Net.Http;
using NLog;
using NzbDrone.Common.Extensions;
using NzbDrone.Core.Datastore.Events;
@@ -38,9 +37,7 @@ namespace NzbDrone.Core.HealthCheck.Checks
public override HealthCheck Check()
{
// Only check clients not in failure status, those get another message
var clients = _downloadClientProvider.GetDownloadClients(true);
var clients = _downloadClientProvider.GetDownloadClients();
var rootFolders = _rootFolderService.All();
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);
}
catch (HttpRequestException ex)
{
_logger.Debug(ex, "Unable to communicate with {0}", client.Definition.Name);
}
catch (Exception ex)
{
_logger.Error(ex, "Unknown error occured in DownloadClientRootFolderCheck HealthCheck");

View File

@@ -53,8 +53,7 @@ namespace NzbDrone.Core.HealthCheck.Checks
return new HealthCheck(GetType());
}
// Only check clients not in failure status, those get another message
var clients = _downloadClientProvider.GetDownloadClients(true);
var clients = _downloadClientProvider.GetDownloadClients();
foreach (var client in clients)
{

View File

@@ -200,7 +200,6 @@ namespace NzbDrone.Core.History
};
history.Data.Add("StatusMessages", message.TrackedDownload.StatusMessages.ToJson());
history.Data.Add("ReleaseGroup", message.TrackedDownload?.RemoteBook?.ParsedBookInfo?.ReleaseGroup);
_historyRepository.Insert(history);
}
}
@@ -236,7 +235,6 @@ namespace NzbDrone.Core.History
history.Data.Add("ImportedPath", message.ImportedBook.Path);
history.Data.Add("DownloadClient", message.DownloadClientInfo?.Type);
history.Data.Add("DownloadClientName", message.DownloadClientInfo?.Name);
history.Data.Add("ReleaseGroup", message.BookInfo.ReleaseGroup);
_historyRepository.Insert(history);
}
@@ -288,7 +286,6 @@ namespace NzbDrone.Core.History
};
history.Data.Add("Reason", message.Reason.ToString());
history.Data.Add("ReleaseGroup", message.BookFile.ReleaseGroup);
_historyRepository.Insert(history);
}
@@ -310,7 +307,6 @@ namespace NzbDrone.Core.History
history.Data.Add("SourcePath", sourcePath);
history.Data.Add("Path", path);
history.Data.Add("ReleaseGroup", message.BookFile.ReleaseGroup);
_historyRepository.Insert(history);
}
@@ -362,7 +358,6 @@ namespace NzbDrone.Core.History
};
history.Data.Add("DownloadClient", message.DownloadClientInfo.Name);
history.Data.Add("ReleaseGroup", message.TrackedDownload?.RemoteBook?.ParsedBookInfo?.ReleaseGroup);
history.Data.Add("Message", message.Message);
historyToAdd.Add(history);

View File

@@ -7,22 +7,22 @@ namespace NzbDrone.Core.IndexerSearch
{
public class AuthorSearchService : IExecute<AuthorSearchCommand>
{
private readonly ISearchForReleases _releaseSearchService;
private readonly ISearchForNzb _nzbSearchService;
private readonly IProcessDownloadDecisions _processDownloadDecisions;
private readonly Logger _logger;
public AuthorSearchService(ISearchForReleases releaseSearchService,
public AuthorSearchService(ISearchForNzb nzbSearchService,
IProcessDownloadDecisions processDownloadDecisions,
Logger logger)
{
_releaseSearchService = releaseSearchService;
_nzbSearchService = nzbSearchService;
_processDownloadDecisions = processDownloadDecisions;
_logger = logger;
}
public void Execute(AuthorSearchCommand message)
{
var decisions = _releaseSearchService.AuthorSearch(message.AuthorId, false, message.Trigger == CommandTrigger.Manual, false);
var decisions = _nzbSearchService.AuthorSearch(message.AuthorId, false, message.Trigger == CommandTrigger.Manual, false);
var processed = _processDownloadDecisions.ProcessDecisions(decisions);
_logger.ProgressInfo("Author search completed. {0} reports downloaded.", processed.Grabbed.Count);

View File

@@ -17,21 +17,21 @@ namespace NzbDrone.Core.IndexerSearch
IExecute<MissingBookSearchCommand>,
IExecute<CutoffUnmetBookSearchCommand>
{
private readonly ISearchForReleases _releaseSearchService;
private readonly ISearchForNzb _nzbSearchService;
private readonly IBookService _bookService;
private readonly IBookCutoffService _bookCutoffService;
private readonly IQueueService _queueService;
private readonly IProcessDownloadDecisions _processDownloadDecisions;
private readonly Logger _logger;
public BookSearchService(ISearchForReleases releaseSearchService,
public BookSearchService(ISearchForNzb nzbSearchService,
IBookService bookService,
IBookCutoffService bookCutoffService,
IQueueService queueService,
IProcessDownloadDecisions processDownloadDecisions,
Logger logger)
{
_releaseSearchService = releaseSearchService;
_nzbSearchService = nzbSearchService;
_bookService = bookService;
_bookCutoffService = bookCutoffService;
_queueService = queueService;
@@ -47,7 +47,7 @@ namespace NzbDrone.Core.IndexerSearch
foreach (var book in books)
{
List<DownloadDecision> decisions;
decisions = _releaseSearchService.BookSearch(book.Id, false, userInvokedSearch, false);
decisions = _nzbSearchService.BookSearch(book.Id, false, userInvokedSearch, false);
var processed = _processDownloadDecisions.ProcessDecisions(decisions);
downloadedCount += processed.Grabbed.Count;
@@ -61,7 +61,7 @@ namespace NzbDrone.Core.IndexerSearch
foreach (var bookId in message.BookIds)
{
var decisions =
_releaseSearchService.BookSearch(bookId, false, message.Trigger == CommandTrigger.Manual, false);
_nzbSearchService.BookSearch(bookId, false, message.Trigger == CommandTrigger.Manual, false);
var processed = _processDownloadDecisions.ProcessDecisions(decisions);
_logger.ProgressInfo("Book search completed. {0} reports downloaded.", processed.Grabbed.Count);

View File

@@ -14,13 +14,13 @@ using NzbDrone.Core.Parser.Model;
namespace NzbDrone.Core.IndexerSearch
{
public interface ISearchForReleases
public interface ISearchForNzb
{
List<DownloadDecision> BookSearch(int bookId, bool missingOnly, bool userInvokedSearch, bool interactiveSearch);
List<DownloadDecision> AuthorSearch(int authorId, bool missingOnly, bool userInvokedSearch, bool interactiveSearch);
}
public class ReleaseSearchService : ISearchForReleases
public class NzbSearchService : ISearchForNzb
{
private readonly IIndexerFactory _indexerFactory;
private readonly IBookService _bookService;
@@ -28,7 +28,7 @@ namespace NzbDrone.Core.IndexerSearch
private readonly IMakeDownloadDecision _makeDownloadDecision;
private readonly Logger _logger;
public ReleaseSearchService(IIndexerFactory indexerFactory,
public NzbSearchService(IIndexerFactory indexerFactory,
IBookService bookService,
IAuthorService authorService,
IMakeDownloadDecision makeDownloadDecision,

View File

@@ -0,0 +1,17 @@
using NzbDrone.Common.Messaging;
using NzbDrone.Core.ThingiProvider;
namespace NzbDrone.Core.Indexers
{
public class IndexerSettingUpdatedEvent : IEvent
{
public string IndexerName { get; private set; }
public IProviderConfig IndexerSetting { get; private set; }
public IndexerSettingUpdatedEvent(string indexerName, IProviderConfig indexerSetting)
{
IndexerName = indexerName;
IndexerSetting = indexerSetting;
}
}
}

View File

@@ -4,7 +4,6 @@ using NzbDrone.Core.Datastore;
using NzbDrone.Core.Download.Clients;
using NzbDrone.Core.Messaging.Events;
using NzbDrone.Core.Parser.Model;
using NzbDrone.Core.ThingiProvider.Events;
namespace NzbDrone.Core.Indexers
{
@@ -14,7 +13,7 @@ namespace NzbDrone.Core.Indexers
TorrentSeedConfiguration GetSeedConfiguration(int indexerId, bool fullSeason);
}
public class SeedConfigProvider : ISeedConfigProvider, IHandle<ProviderUpdatedEvent<IIndexer>>
public class SeedConfigProvider : ISeedConfigProvider, IHandle<IndexerSettingUpdatedEvent>
{
private readonly IIndexerFactory _indexerFactory;
private readonly ICached<SeedCriteriaSettings> _cache;
@@ -83,7 +82,7 @@ namespace NzbDrone.Core.Indexers
}
}
public void Handle(ProviderUpdatedEvent<IIndexer> message)
public void Handle(IndexerSettingUpdatedEvent message)
{
_cache.Clear();
}

View File

@@ -117,6 +117,7 @@ namespace NzbDrone.Core.Instrumentation
syslogTarget.MessageSend.Protocol = ProtocolType.Udp;
syslogTarget.MessageSend.Udp.Port = syslogPort;
syslogTarget.MessageSend.Udp.Server = syslogServer;
syslogTarget.MessageSend.Udp.ReconnectInterval = 500;
syslogTarget.MessageCreation.Rfc = RfcNumber.Rfc5424;
syslogTarget.MessageCreation.Rfc5424.AppName = _configFileProvider.InstanceName;

View File

@@ -1,4 +1 @@
{
"About": "সম্পর্কিত",
"Actions": "ক্রিয়াকাণ্ড"
}
{}

View File

@@ -51,7 +51,7 @@
"Cancel": "zrušení",
"CancelMessageText": "Opravdu chcete zrušit tento nevyřízený úkol?",
"CertificateValidation": "Ověření certifikátu",
"CertificateValidationHelpText": "Změňte přísnost ověřování certifikátů HTTPS. Neměňte, pokud nerozumíte rizikům.",
"CertificateValidationHelpText": "Změňte, jak přísné je ověření certifikace HTTPS",
"ChangeFileDate": "Změnit datum souboru",
"ChangeHasNotBeenSavedYet": "Změna ještě nebyla uložena",
"ChmodFolder": "Složka chmod",

View File

@@ -44,7 +44,7 @@
"Cancel": "Abbrechen",
"CancelMessageText": "Diese laufende Aufgabe wirklich abbrechen?",
"CertificateValidation": "Zertifikat Validierung",
"CertificateValidationHelpText": "Ändere wie streng die Validierung der HTTPS-Zertifizierung ist. Ändern Sie nicht wenn Ihnen die Risiken nicht bewusst sind.",
"CertificateValidationHelpText": "Ändere wie streng die Validierung der HTTPS-Zertifizierung ist. Nicht anpassen, außer du kennst das Risiko.",
"ChangeFileDate": "Erstelldatum der Datei anpassen",
"ChangeHasNotBeenSavedYet": "Änderung wurde noch nicht gespeichert",
"ChmodFolder": "chmod Ordner",
@@ -291,7 +291,7 @@
"RemoveFromQueue": "Aus der Warteschlage entfernen",
"RemoveHelpTextWarning": "Dies wird den Download und alle bereits heruntergeladenen Dateien aus dem Downloader entfernen.",
"RemoveSelected": "Auswahl entfernen",
"RemoveSelectedMessageText": "Sind sie sicher, dass die ausgewählten Einträge aus der Sperrliste entfernt werden sollen?",
"RemoveSelectedMessageText": "Bist du icher, dass du die ausgewählten Einträge aus der Sperrliste entfernen willst?",
"RemoveTagExistingTag": "Vorhandener Tag",
"RemoveTagRemovingTag": "Tag entfernen",
"RemovedFromTaskQueue": "Aus der Aufgabenwarteschlage entfernt",
@@ -301,7 +301,7 @@
"RequiredHelpText": "Das Release mus mindesten eines der Begriffe beinhalten ( Groß-/Kleinschreibung wird nicht beachtet )",
"RequiredPlaceHolder": "Neue Beschränkung hinzufügen",
"RescanAfterRefreshHelpTextWarning": "Wenn nicht \"Immer (Always)\" ausgewählt wird, werden Dateiänderungen nicht automatisch erkannt",
"RescanAuthorFolderAfterRefresh": "Nach dem Aktualisieren den Autorordner neu scannen",
"RescanAuthorFolderAfterRefresh": "Nach dem aktualisieren den Filmordner neu scannen",
"Reset": "Zurücksetzen",
"ResetAPIKey": "API-Schlüssel zurücksetzen",
"ResetAPIKeyMessageText": "Bist du sicher, dass du den API-Schlüssel zurücksetzen willst?",
@@ -349,7 +349,7 @@
"ShownAboveEachColumnWhenWeekIsTheActiveView": "Wird in der Wochenansicht über jeder Spalte angezeigt",
"Size": " Größe",
"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 dies wenn es nicht möglich ist, den freien Speicherplatz vom Stammverzeichnis zu ermitteln",
"SorryThatAuthorCannotBeFound": "Schade, dieser Film kann nicht gefunden werden.",
"SorryThatBookCannotBeFound": "Schade, dieser Film kann nicht gefunden werden.",
"Source": "Quelle",
@@ -426,7 +426,7 @@
"UnmonitoredHelpText": "Nicht beobachtete Filme im iCal-Feed einschließen",
"UpdateAll": "Alle aktualisieren",
"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 den 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",
"Updates": "Updates",
"UpgradeAllowedHelpText": "Wenn deaktiviert wird die Qualität nicht verbessert",
@@ -543,7 +543,7 @@
"DeleteMetadataProfile": "Metadaten Profil löschen",
"ImportListExclusions": "Ausschlüsse der Importliste",
"ImportLists": "Importlisten",
"ImportListSettings": "Allgemeine Importlisten-Einstellungen",
"ImportListSettings": "Allgemeine Einstellungen der Importliste",
"ImportListSpecificSettings": "Listenspezifische Einstellungen importieren",
"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.",
@@ -702,7 +702,7 @@
"ImportListStatusCheckSingleClientMessage": "Listen aufgrund von Fehlern nicht verfügbar: {0}",
"ImportMechanismHealthCheckMessage": "Aktiviere die Verarbeitung der abgeschlossenen Downloads",
"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 aktiviert, Readarr liefert keine interaktiven Suchergebnisse",
"IndexerStatusCheckSingleClientMessage": "Indexer aufgrund von Fehlern nicht verfügbar: {0}",
"ChownGroup": "chown Gruppe",
"AllowFingerprintingHelpText": "Benutze Fingerabdrücke um die Genauigkeit der Buch Übereinstimmungen zu verbessern",
@@ -725,7 +725,7 @@
"RemotePathMappingCheckDockerFolderMissing": "Docker erkannt; Downloader {0} speichert Downloads in {1}, aber dieser Ordner scheint nicht im Container zu existieren. Überprüfe die Remote-Pfadzuordnungen und die Container Volume Einstellungen.",
"RemotePathMappingCheckFilesGenericPermissions": "Downloader {0} meldet Dateien in {1}, aber Radarr kann dieses Verzeichnis nicht sehen.Möglicherweise müssen die Verzeichnisreche angepasst werden.",
"RemotePathMappingCheckFolderPermissions": "Radarr kann das Downloadverzeichnis sehen, aber nicht verarbeiten {0}. Möglicherwiese ein Rechteproblem.",
"RemotePathMappingCheckGenericPermissions": "Downloader {0} speichert Downloads in {1}, aber Readarr kann dieses Verzeichnis nicht sehen. Möglicherweise müssen die Verzeichnisrechte angepasst werden.",
"RemotePathMappingCheckGenericPermissions": "Downloader {0} speichert Downloads in {1}, aber Radarr kann dieses Verzeichnis nicht sehen. Möglicherweise müssen die Verzeichnisrechte angepasst werden.",
"RemotePathMappingCheckLocalWrongOSPath": "Downloader {0} speichert Downloads in {1}, aber dies ist kein valider {2} Pfad. Überprüfe die Downloader Einstellungen.",
"Monitor": "Beobachten",
"MusicBrainzAuthorID": "MusicBranz Künstler Id",
@@ -780,12 +780,12 @@
"InstanceNameHelpText": "Instanzname im Browser-Tab und für Syslog-Anwendungsname",
"RestartRequiredHelpTextWarning": "Erfordert einen Neustart",
"UseCalibreContentServer": "Calibre-Content-Server",
"DataExistingBooks": "Beobachte Bücher die Dateien haben oder noch nicht veröffentlicht wurden",
"DataFirstBook": "Beobachte das erste Buch. Alle anderen Bücher werden ignoriert",
"DataFuturebooks": "Überwachung von Büchern die noch nicht veröffentlicht wurden",
"DataMissingBooks": "Beobachte Bücher, die noch keine Dateien haben oder noch nicht veröffentlicht wurden",
"DataExistingBooks": "Beobachte Alben die Dateien haben oder noch nicht veröffentlicht wurden",
"DataFirstBook": "Beobachte die ersten Alben. Alle anderen Alben werden ignoriert",
"DataFuturebooks": "Überwachung von Alben die noch nicht veröffentlicht wurden",
"DataMissingBooks": "Beobachte Alben, die noch keine Dateien haben oder noch nicht veröffentlicht wurden",
"Test": "Testen",
"DataNone": "Es werden keine Bücher beobachtet",
"DataNone": "Es werden keine Alben beobachtet",
"RenameFiles": "Dateien umbenennen",
"LoadingEditionsFailed": "Das Laden der Ausgaben ist fehlgeschlagen",
"MissingBooksAuthorMonitored": "Fehlende Bücher (Autoren überwacht)",
@@ -795,7 +795,7 @@
"MediaManagementSettingsSummary": "Namensgebung, Dateimanagement-Einstellungen und Root-Ordner",
"MinimumPopularity": "Mindestpolularität",
"MinPagesHelpText": "Bücher mit weniger Seiten als dieses ignorieren",
"MinPopularityHelpText": "Popularität ist Durchschnittsbewertung * Anzahl der Stimmen",
"MinPopularityHelpText": "Popularität ist Durchschnittsbewertung * Stimmen",
"MissingBooks": "Fehlende Bücher",
"MonitorAuthor": "Autor überwachen",
"MissingBooksAuthorNotMonitored": "Fehlende Bücher (Autor nicht überwacht)",
@@ -812,72 +812,5 @@
"DataNewAllBooks": "Alle neuen Bücher überwachen",
"DataNewBooks": "Alle neuen Bücher, welche nach dem neusten existierenden Buch veröffentlicht werden, überwachen",
"DataNewNone": "Keine neuen Bücher überwachen",
"MassBookSearchWarning": "Sind Sie sicher, dass Sie eine Massensuche für {0} Bücher starten wollen?",
"NoTagsHaveBeenAddedYet": "Es wurden bisher noch keine Tags hinzugefügt. Fügen Sie Tags hinzu um Autoren mit Verzögerunsprofilen, Einschränkungen oder Benachrichtigungen zu verknüpfen. Klicken Sie {0} um mehr über Tags in Readarr herauszufinden.",
"OutputFormatHelpText": "Optional kann Calibre aufgefordert werden, beim Import in andere Formate zu konvertieren. Kommagetrennte Liste.",
"UsernameHelpText": "Calibre Inhaltsserver Benutzername",
"MonitorExistingBooks": "Vorhandene Bücher überwachen",
"MonitoringOptionsHelpText": "Welche Bücher sollen überwacht werden nachdem der Autor hinzugefügt wurde (einmalige Anpassung)",
"MonitorNewBooks": "Neue Bücher überwachen",
"MonitorNewItems": "Neue Bücher überwachen",
"MonitorNewItemsHelpText": "Welche neuen Bücher sollen überwacht werden",
"NewBooks": "Neue Bücher",
"NoHistoryBlocklist": "Keine History Blockliste",
"NoName": "Namen nicht anzeigen",
"OnAuthorDelete": "Beim Löschen eines Autors",
"OnAuthorDeleteHelpText": "Beim Löschen eines Autors",
"OnBookDelete": "Beim Löschen eines Buches",
"OnBookRetagHelpText": "Bei neu markieren eines Buches",
"RefreshBook": "Buch aktualisieren",
"SearchBoxPlaceHolder": "z.B Krieg und Frieden, goodreads:656, isbn:067003469X, asin:B00JCDK5ME",
"SendMetadataToCalibre": "Metadaten an Calibre senden",
"SeriesTotal": "Serie ({0})",
"SetReadarrTags": "Setze Readarr Tags",
"ShouldMonitorExisting": "Existierende Bücher überwachen",
"ShouldMonitorHelpText": "Neue Autoren und Bücher aus dieser Liste überwachen",
"ShouldSearchHelpText": "Dursuche Indexer nach neu hinzugefügten Einträgen. Vorsichtig benutzen bei großen Listen.",
"SkipSecondarySeriesBooks": "Überspringe Bücher von Sekundarserien",
"SpecificBook": "Bestimmtes Buch",
"TheFollowingFilesWillBeDeleted": "Die folgenden Dateien werden gelöscht:",
"TooManyBooks": "Fehlende oder zu viele Bücher? Ändern oder erstellen Sie ein neues",
"TrackTitle": "Track Titel",
"UseSslHelpText": "Benutze SSL um mit dem Calibre Inhaltsserver zu verbinden",
"WriteMetadataTags": "Schreibe Metadaten Tags",
"WriteTagsAll": "Alle Dateien; nur erster Import",
"WriteTagsNew": "Nur für neue Downloads",
"WriteTagsSync": "Alle Dateien; mit Goodreads synchronisiert bleiben",
"SearchForAllCutoffUnmetBooks": "Suche nach allen abgeschnittenen unerfüllten Büchern",
"OnBookTagUpdate": "Bei Update eines Buch Tags",
"ProfilesSettingsSummary": "Qualität, Metadaten, Verzögerung und Release Profile",
"RenameBooks": "Bücher umbenennen",
"SearchForNewItems": "Suche nach neuen Einträgen",
"SeriesNumber": "Seriennummer",
"ShouldMonitorExistingHelpText": "Bücher in dieser Liste welche bereits in Readarr sind überwachen",
"SkipBooksWithNoISBNOrASIN": "Überspringe Bücher ohne ISBN oder ASIN",
"StatusEndedDeceased": "Verstorben",
"UpdateCovers": "Covers aktualisieren",
"UpdateCoversHelpText": "Buchcovers in Calibre so einstellen, dass sie mit denen aus Readarr übereinstimmen",
"UrlBaseHelpText": "Fügt ein Prefix zur Calibre Url hinzu, z.B http://[host]:[port]/[urlBasis]",
"UseSSL": "Benutze SSL",
"MonitorBookExistingOnlyWarning": "Dies ist eine einmalige Anpassung der Überwachungseinstellung für jedes Buch. Verwenden Sie die Option unter Autor/Bearbeiten, um festzulegen, was bei neu hinzugefügten Büchern geschieht",
"MonitoredHelpText": "Readarr wird das Buch suchen und herunterladen",
"MonitoredAuthorIsUnmonitored": "Autor wird nicht überwacht",
"MonitoredAuthorIsMonitored": "Autor wird überwacht",
"NameLastFirst": "Nachname, Vorname",
"NameFirstLast": "Vorname Nachname",
"NameStyle": "Autor Namensstil",
"NETCore": ".NET Core",
"ShowLastBook": "Zeige letztes Buch",
"SkipBooksWithMissingReleaseDate": "Überspringe Bücher ohne Erscheinungsdatum",
"Monitoring": "Überwachung",
"OnBookDeleteHelpText": "Beim Löschen eines Buches",
"RefreshAuthor": "Autor aktualisieren",
"RefreshInformation": "Informationen aktualisieren",
"SearchBook": "Buch suchen",
"ShowBookCount": "Zeige Anzahl an Büchern",
"SkipPartBooksAndSets": "Überspringe Teilbücher und Sets",
"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",
"ApplicationURL": "Anwendungs-URL",
"ApplicationUrlHelpText": "Die externe URL der Anwendung inklusive http(s)://, Port und URL-Basis"
"MassBookSearchWarning": "Sind Sie sicher, dass Sie eine Massensuche für {0} Bücher starten wollen?"
}

View File

@@ -6,8 +6,8 @@
"Size": " Μέγεθος",
"Source": "Πηγή",
"Uptime": "Ώρα",
"20MinutesTwenty": "20 λεπτά: {0}",
"45MinutesFourtyFive": "45 λεπτά: {0}",
"20MinutesTwenty": "60 λεπτά: {0}",
"45MinutesFourtyFive": "60 λεπτά: {0}",
"60MinutesSixty": "60 λεπτά: {0}",
"APIKey": "Κλειδί API",
"About": "Σχετικά",
@@ -557,9 +557,5 @@
"Test": "Δοκιμή",
"AddList": "Προσθήκη Λίστας",
"RenameFiles": "Μετονομασία αρχείων",
"ManualImportSelectEdition": "Μη αυτόματη εισαγωγή - Επιλέξτε ταινία",
"AllExpandedCollapseAll": "Σύμπτυξη Όλων",
"AllExpandedExpandAll": "Ανάπτυξη Όλων",
"AddNewItem": "Προσθήκη Νέου",
"AddMissing": "Προσθήκη Στοιχείου Που Λείπει"
"ManualImportSelectEdition": "Μη αυτόματη εισαγωγή - Επιλέξτε ταινία"
}

View File

@@ -34,8 +34,6 @@
"ApiKeyHelpTextWarning": "Requires restart to take effect",
"AppDataDirectory": "AppData directory",
"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",
"ApplyTagsHelpTexts1": "How to apply tags to the selected author",
"ApplyTagsHelpTexts2": "Add: Add the tags the existing list of tags",

View File

@@ -36,7 +36,7 @@
"BackupRetentionHelpText": "Säilytysjaksoa vanhemmat, automaattiset varmuuskopiot poistetaan automaattisesti.",
"Backups": "Varmuuskopiointi",
"BindAddress": "Sidososoite",
"BindAddressHelpText": "Toimiva IPv4-osoite tai '*' (tähti) kaikille yhteyksille.",
"BindAddressHelpText": "Toimiva IPv4-osoite tai jokerimerkkinä '*' (tähti) kaikille yhteyksille.",
"BindAddressHelpTextWarning": "Käyttöönotto vaatii uudelleenkäynnistyksen.",
"BookIsDownloading": "Kirjaa ladataan",
"BookIsDownloadingInterp": "Kirjaa ladataan - {0} % {1}",
@@ -47,7 +47,7 @@
"Cancel": "Peruuta",
"CancelMessageText": "Haluatko varmasti perua tämän odottavan tehtävän?",
"CertificateValidation": "Varmenteen vahvistus",
"CertificateValidationHelpText": "Muuta HTTPS-varmennevahvistuksen tarkkuutta. Älä muuta, jollet ymmärrä tähän liittyviä riskejä.",
"CertificateValidationHelpText": "Valitse HTTPS-varmenteen vahvistuksen tarkkuus. Älä muuta, jollet ymmärrä tähän liittyviä riskejä.",
"ChangeFileDate": "Muuta tiedoston päiväys",
"ChangeHasNotBeenSavedYet": "Muutosta ei ole vielä tallennettu",
"ChmodFolder": "chmod-kansio",
@@ -141,7 +141,7 @@
"Fixed": "Korjattu",
"Folder": "Kansio",
"Folders": "Kansioiden käsittely",
"ForMoreInformationOnTheIndividualDownloadClientsClickOnTheInfoButtons": "Lataustyökalukohtaisia tietoja saat painamalla lisätietopainikkeita.",
"ForMoreInformationOnTheIndividualDownloadClientsClickOnTheInfoButtons": "Lue lisää yksittäisistä lataustyökaluista painamalla 'Lisätietoja'.",
"ForMoreInformationOnTheIndividualIndexersClickOnTheInfoButtons": "Lue lisää tietolähteestä painamalla 'Lisätietoja'.",
"ForMoreInformationOnTheIndividualListsClickOnTheInfoButtons": "Lue lisää tuontilistoista painamalla 'Lisätietoja'.",
"GeneralSettings": "Yleiset asetukset",
@@ -271,7 +271,7 @@
"ReadarrTags": "Radarr-tunnisteet",
"Real": "Todellinen",
"Reason": "Syy",
"RecycleBinCleanupDaysHelpText": "Poista automaattinen tyhjennys käytöstä asettamalla arvoksi '0'.",
"RecycleBinCleanupDaysHelpText": "Älä tyhjennä automaattisesti asettamalla arvoksi '0'.",
"RecycleBinCleanupDaysHelpTextWarning": "Tässä määritettyä aikaa vanhemmat tiedostot poistetaan roskakorista pysyvästi automaattisesti.",
"RecycleBinHelpText": "Pysyvän poiston sijaan kirjatiedostot siirretään tähän kansioon.",
"RecyclingBin": "Roskakori",
@@ -297,7 +297,7 @@
"RemoveTagExistingTag": "Olemassa oleva tunniste",
"RemoveTagRemovingTag": "Tunniste poistetaan",
"RemovedFromTaskQueue": "Poistettu tehtäväjonosta",
"RenameBooksHelpText": "Jos uudelleennimeäminen ei ole käytössä, käytetään nykyistä tiedostonimeä.",
"RenameBooksHelpText": "Jos uudelleennimeäminen ei ole käytössä, käytetään olemassa olevaa tiedostonimeä.",
"Reorder": "Järjestä uudelleen",
"ReplaceIllegalCharacters": "Korvaa kielletyt merkit",
"RequiredHelpText": "Julkaisun tulee sisältää ainakin yksi näistä termeistä (kirjainkokoa ei huomioida).",
@@ -349,7 +349,7 @@
"ShownAboveEachColumnWhenWeekIsTheActiveView": "Näkyy jokaisen sarakkeen yläpuolella käytettäessä viikkonäkymää.",
"Size": " Koko",
"SkipFreeSpaceCheck": "Ohita vapaan levytilan tarkistus",
"SkipFreeSpaceCheckWhenImportingHelpText": "Käytä, kun vapaata tallennustilaa ei tunnisteta kirjailijoiden juurikansiosta.",
"SkipFreeSpaceCheckWhenImportingHelpText": "Käytä, kun vapaata tilaa ei tunnisteta kirjailijoidesi pääkansiosta.",
"SorryThatAuthorCannotBeFound": "Valitettavasti kirjailijaa ei löydy.",
"SorryThatBookCannotBeFound": "Valitettavasti elokuvaa ei löydy.",
"Source": "Lähdekoodi",
@@ -367,7 +367,7 @@
"SuccessMyWorkIsDoneNoFilesToRetag": "Menestys! Työni on valmis, ei nimettäviä tiedostoja.",
"SupportsRssvalueRSSIsNotSupportedWithThisIndexer": "RSS-syötettä ei ole käytettävissä tälle tietolähteelle",
"SupportsSearchvalueSearchIsNotSupportedWithThisIndexer": "Hakemistoa ei tueta tällä hakemistolla",
"SupportsSearchvalueWillBeUsedWhenAutomaticSearchesArePerformedViaTheUIOrByReadarr": "Profiilia käytetään automaattihaun yhteydessä, kun haku suoritetaan käyttöliittymästä tai Readarrin toimesta.",
"SupportsSearchvalueWillBeUsedWhenAutomaticSearchesArePerformedViaTheUIOrByReadarr": "Käytetään, kun automaattiset haut suoritetaan käyttöliittymän tai Radarrin kautta",
"SupportsSearchvalueWillBeUsedWhenInteractiveSearchIsUsed": "Profiilia käytetään vuorovaikutteisen haun yhteydessä.",
"TagIsNotUsedAndCanBeDeleted": "Tunnistetta ei ole määritetty millekään kohteelle, joten sen voi poistaa.",
"Tags": "Tunnisteet",
@@ -448,7 +448,7 @@
"LoadingBooksFailed": "Elokuvatiedostojen lataaminen epäonnistui",
"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.",
"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.",
"ReplaceIllegalCharactersHelpText": "Korvaa laittomat merkit. Jos ei käytössä, laittomat merkit poistetaan.",
"OutputPath": "Tallennussijainti",
@@ -588,10 +588,10 @@
"MissingBooks": "Puuttuvat kirjat",
"MissingBooksAuthorMonitored": "Puuttuvat kirjat (kirjailijaa valvotaan)",
"MissingBooksAuthorNotMonitored": "Puuttuvat kirjat (kirjailijaa ei valvota)",
"MonitorBookExistingOnlyWarning": "Tämä on kirjakohtaisen valvonnan kertaluontoinen määritys. Määritä Kirjailija/Muokkaa-valinnalla mitä uusille kirjalisäyksille tehdään.",
"MonitorBookExistingOnlyWarning": "Tämä on kirjakohtaisen valvonnan kertaluontoinen määritys. Käytä Kirjailija/Muokkaa-valintaa hallinnoidaksesi mitä uusille kirjalisäyksille tehdään.",
"MonitorNewItems": "Valvo uusia kirjoja",
"MonitorNewItemsHelpText": "Uusien kirjojen valvontatapa.",
"MonitoringOptionsHelpText": "Mitkä kirjat asetetaan valvottaviksi kirjailijan lisäyksen yhteydessä (kertaluontoinen määritys).",
"MonitoringOptionsHelpText": "Kansiosta löydetyille kirjailijoille oletusarvoisesti asetettava kirjojen valvontataso (kertaluontoinen määritys).",
"OutputFormatHelpText": "Voit halutessasi pyytää Calibrea muuntamaan kirjat eri muotoihin tuonnin yhteydessä. Pilkulla eroteltu lista.",
"PasswordHelpText": "Calibre-sisältöpalvelimen salasana.",
"PortHelpText": "Calibre-sisältöpalvelimen portti.",
@@ -624,7 +624,7 @@
"UpdateAvailable": "Uusi päivitys on saatavilla",
"Filters": "Suodattimet",
"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}",
"IndexersSettingsSummary": "Sisältölähteet ja julkaisurajoitukset.",
"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.",
"RemotePathMappingCheckImportFailed": "Radarr ei voinut tuoda elokuvaa. Tarkista loki saadaksesi lisätietoja.",
"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.",
"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.",
@@ -663,7 +663,7 @@
"FileWasDeletedByUpgrade": "Tiedosto poistettiin päivityksen tuontia varten.",
"HealthNoIssues": "Kokoonpanossasi ei ole ongelmia.",
"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}",
"MissingFromDisk": "Radarr ei löytänyt tiedostoa levyltä, joten sen kytkös kirjaston elokuvaan poistettiin",
"Monitor": "Valvo",
@@ -676,7 +676,7 @@
"OnRename": "Kun elokuva nimetään uudelleen",
"OnUpgrade": "Kun elokuva päivitetään",
"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ä",
"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",
@@ -687,7 +687,7 @@
"SettingsRemotePathMappingRemotePath": "Etäsijainti",
"SettingsRemotePathMappingRemotePathHelpText": "Lataustyökalun käyttämän hakemiston juurisijainti",
"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.",
"UserAgentProvidedByTheAppThatCalledTheAPI": "User-Agent-tiedon ilmoitti sovellus, joka kommunikoi API:n kanssa",
"WatchLibraryForChangesHelpText": "Suorita automaattinen uudelleentutkinta, kun juurikansiossa havaitaan tiedostomuutoksia.",
@@ -699,61 +699,8 @@
"MonitorNewBooks": "Valvo uusia kirjoja",
"RenameFiles": "Uudelleennimeä tiedostot",
"Test": "Kokeile",
"AllowFingerprintingHelpText": "Tarkenna kirjojen täsmäystarkkuutta piiloleimauksen avulla.",
"AllowFingerprintingHelpText": "Käytä piiloleimausta kirjojen täsmäyksen tarkennukseen",
"AllowFingerprinting": "Salli piiloleimaus",
"Database": "Tietokanta",
"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"
"ManualImportSelectEdition": "Manuaalinen tuonti - Valitse versio"
}

View File

@@ -877,7 +877,5 @@
"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",
"ManualImportSelectEdition": "Kézi importálás Válaszd ki a Kiadást",
"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"
"Database": "Adatbázis"
}

View File

@@ -4,7 +4,7 @@
"45MinutesFourtyFive": "45 Minuti: {0}",
"60MinutesSixty": "60 Minuti: {0}",
"APIKey": "Chiave API",
"About": "Info",
"About": "Informazioni",
"AddListExclusion": "Aggiungi Lista Esclusioni",
"AddingTag": "Aggiungi etichetta",
"AdvancedSettingsHiddenClickToShow": "Nascosto, clicca per mostrare",
@@ -44,11 +44,11 @@
"AutoRedownloadFailedHelpText": "Cerca e prova a scaricare automaticamente un'altra versione",
"Automatic": "Automatico",
"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",
"Backups": "I Backup",
"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",
"BookIsDownloading": "Libro in download",
"BookIsDownloadingInterp": "Libro in download - {0}% {1}",
@@ -59,7 +59,7 @@
"Cancel": "Annulla",
"CancelMessageText": "Sei sicuro di voler cancellare questa operazione in sospeso?",
"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",
"ChangeHasNotBeenSavedYet": "Il cambio non è stato ancora salvato",
"ChmodFolder": "Permessi Cartella",
@@ -381,9 +381,9 @@
"SupportsSearchvalueWillBeUsedWhenInteractiveSearchIsUsed": "Verrà usato durante la ricerca interattiva",
"TagIsNotUsedAndCanBeDeleted": "L'etichetta non è in uso e può essere eliminata",
"Tasks": "Attività",
"TestAll": "Prova Tutti",
"TestAllClients": "Testa tutti i client",
"TestAllIndexers": "Prova tutti gli indicizzatori",
"TestAll": "Testa Tutti",
"TestAllClients": "Testa Tutti i Client",
"TestAllIndexers": "Testa tutti gli Indicizzatori",
"TestAllLists": "Testa tutte le liste",
"ThisWillApplyToAllIndexersPleaseFollowTheRulesSetForthByThem": "Questo verrà applicato a tutti gli indexer, segui le regole impostate da loro",
"TimeFormat": "Formato orario",
@@ -544,7 +544,7 @@
"ProxyCheckBadRequestMessage": "Il test del proxy è fallito. Codice Stato: {0}",
"ProxyCheckFailedToTestMessage": "Test del proxy fallito: {0}",
"QualitySettingsSummary": "Dimensioni delle qualità e denominazione",
"Queued": "In coda",
"Queued": "Messo in coda",
"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.",
"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",
"Test": "Test",
"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",
"LogRotation": "Rotazione Log",
"ManualImportSelectEdition": "Importazione manuale: seleziona Film",
"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"
"Database": "Database"
}

View File

@@ -42,7 +42,7 @@
"BackupRetentionHelpText": "Automatische veiligheidskopieën ouder dan de retentie periode zullen worden opgeruimd",
"Backups": "Veiligheidskopieën",
"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",
"BookIsDownloading": "Film is aan het downloaden",
"BookIsDownloadingInterp": "Film is aan het downloaden - {0}% {1}",
@@ -53,7 +53,7 @@
"Cancel": "Annuleer",
"CancelMessageText": "Bent u zeker dat u deze taak in afwachting wilt annuleren?",
"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",
"ChangeHasNotBeenSavedYet": "Wijziging is nog niet opgeslagen",
"ChmodFolder": "chmod Map",
@@ -591,12 +591,5 @@
"RenameFiles": "Hernoem Bestanden",
"Test": "Test",
"ManualImportSelectEdition": "Manuele import - Selecteer Film",
"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"
"Database": "Databasis"
}

View File

@@ -224,7 +224,7 @@
"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",
"MinimumLimits": "Limites mínimos",
"MonoVersion": "Versão Mono",
"MonoVersion": "Versão do Mono",
"MoreInfo": "Mais informações",
"MustNotContain": "Não deve conter",
"Name": "Nome",
@@ -260,7 +260,7 @@
"Preferred": "Preferido",
"PreviewRename": "Visualizar renomeação",
"Profiles": "Perfis",
"Proper": "Apropriado",
"Proper": "Proper",
"PropersAndRepacks": "Propers e repacks",
"Protocol": "Protocolo",
"ProtocolHelpText": "Escolha que protocolo(s) utilizar e qual o preferido ao escolher entre versões iguais",
@@ -761,7 +761,7 @@
"OnRename": "Ao Renomear",
"OnUpgrade": "Ao Atualizar",
"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",
"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",
@@ -821,7 +821,7 @@
"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.",
"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.",
"SettingsRemotePathMappingLocalPathHelpText": "Caminho que Readarr deve usar para acessar o caminho remoto localmente",
"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.",
"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",
"AddList": "Adicionar Lista",
"AddList": "Adicionar à Lista",
"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",
"DataListMonitorNone": "Não monitorar autores ou livros",
@@ -861,7 +861,7 @@
"RenameFiles": "Renomear Arquivos",
"Test": "Testar",
"WriteMetadataTags": "Salvar Etiquetas de Metadados",
"RestartRequiredHelpTextWarning": "Requer reinicialização para ter efeito",
"RestartRequiredHelpTextWarning": "Requer reinicio para fazer efeito",
"InstanceName": "Nome da instância",
"ConvertToFormat": "Converter para o Formato",
"DataAllBooks": "Monitorar todos os livros",
@@ -877,7 +877,5 @@
"SetReadarrTags": "Configurar Etiquetas do Readarr",
"Database": "Banco de dados",
"LoadingEditionsFailed": "Falha ao carregar edições",
"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"
"ManualImportSelectEdition": "Importação Manual - Selecionar Edição"
}

View File

@@ -592,8 +592,5 @@
"Started": "Запущено",
"Database": "База данных",
"InstanceName": "Имя экземпляра",
"InstanceNameHelpText": "Имя экземпляра на вкладке и для имени приложения системного журнала",
"AllowedLanguages": "Разрешенные языки",
"ApplicationURL": "URL-адрес приложения",
"ApplicationUrlHelpText": "Внешний URL-адрес этого приложения, включая http(s)://, порт и базовый URL-адрес"
"InstanceNameHelpText": "Имя экземпляра на вкладке и для имени приложения системного журнала"
}

View File

@@ -803,7 +803,5 @@
"DataExistingBooks": "Bevaka album som har filer eller inte har släppts än",
"DataFirstBook": "Bevaka första album. Alla andra album kommer bli ignorerade",
"DataFuturebooks": "Bevaka albums som 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"
"DataMissingBooks": "Bevaka album som har filer eller inte har släppts än"
}

View File

@@ -95,375 +95,5 @@
"BackupFolderHelpText": "Відносні шляхи будуть у каталозі AppData Radarr",
"BlocklistHelpText": "Забороняє Radarr знову автоматично захопити цей випуск",
"UsingExternalUpdateMechanismBranchUsedByExternalUpdateMechanism": "Гілка, що використовується зовнішнім механізмом оновлення",
"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"
"AddList": "Додати список"
}

View File

@@ -83,7 +83,7 @@
"DeleteImportListMessageText": "您确定要删除列表 '{0}'",
"DeleteIndexer": "删除索引",
"DeleteIndexerMessageText": "您确定要删除索引 '{0}'吗?",
"DeleteMetadataProfileMessageText": "确定要删除元数据配置吗‘{0}",
"DeleteMetadataProfileMessageText": "确定要删除影片质量配置“{0}",
"DeleteNotification": "删除消息推送",
"DeleteNotificationMessageText": "您确定要删除推送 '{0}' 吗?",
"DeleteQualityProfile": "删除电影质量配置",
@@ -433,7 +433,7 @@
"WeekColumnHeader": "日期格式",
"Year": "年",
"YesCancel": "是,取消",
"20MinutesTwenty": "20 分钟{0}",
"20MinutesTwenty": "20分钟: {0}",
"45MinutesFourtyFive": "45分钟: {0}",
"60MinutesSixty": "60分钟: {0}",
"APIKey": "API Key",
@@ -877,7 +877,5 @@
"CollapseMultipleBooksHelpText": "折叠在同日发行的多本书籍",
"ASIN": "亚马逊标准识别码",
"NoTagsHaveBeenAddedYet": "尚未添加标签,添加标签以链接具有延迟配置文件、限制或通知的作者。单击{0}以了解有关Readarr中标签的更多信息。",
"BindAddressHelpText": "有效的 IPv4 地址或以'*'代表所有接口",
"ApplicationURL": "程序URL",
"ApplicationUrlHelpText": "此应用的外部URL包含 http(s)://、端口和基本URL"
"BindAddressHelpText": "有效的 IPv4 地址或以'*'代表所有接口"
}

View File

@@ -400,11 +400,11 @@ namespace NzbDrone.Core.MediaFiles
}
catch (Exception ex)
{
Logger.ForWarnEvent()
Logger.Warn()
.Exception(ex)
.Message($"Tag writing failed for {path}")
.WriteSentryWarn("Tag writing failed")
.Log();
.Write();
}
finally
{

View File

@@ -147,11 +147,11 @@ namespace NzbDrone.Core.MediaFiles
}
catch (Exception ex)
{
_logger.ForWarnEvent()
_logger.Warn()
.Exception(ex)
.Message($"Tag removal failed for {path}")
.WriteSentryWarn("Tag removal failed")
.Log();
.Write();
}
finally
{

View File

@@ -65,10 +65,10 @@ namespace NzbDrone.Core.MediaFiles
}
else
{
Logger.ForDebugEvent()
Logger.Debug()
.Message("Unknown audio format: '{0}'.", string.Join(", ", mediaInfo.AudioFormat))
.WriteSentryWarn("UnknownAudioFormat", mediaInfo.AudioFormat)
.Log();
.Write();
return "Unknown";
}

View File

@@ -125,7 +125,7 @@ namespace NzbDrone.Core.MetadataSource.BookInfo
catch (BookInfoException e)
{
_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)
{
HttpRequest httpRequest;
HttpResponse httpResponse;
var httpRequest = _requestBuilder.GetRequestBuilder().Create()
.SetSegment("route", $"book/{id}")
.Build();
while (true)
{
httpRequest = _requestBuilder.GetRequestBuilder().Create()
.SetSegment("route", $"book/{id}")
.Build();
httpRequest.SuppressHttpError = true;
httpRequest.SuppressHttpError = true;
// we expect a redirect
httpResponse = _httpClient.Get(httpRequest);
if (httpResponse.StatusCode == HttpStatusCode.TooManyRequests)
{
WaitUntilRetry(httpResponse);
}
else
{
break;
}
}
// we expect a redirect
var httpResponse = _httpClient.Get(httpRequest);
if (httpResponse.StatusCode == HttpStatusCode.NotFound)
{
@@ -413,9 +398,9 @@ namespace NzbDrone.Core.MetadataSource.BookInfo
}
var location = httpResponse.Headers.GetSingleValue("Location");
var split = location.Split('/').Reverse().ToList();
var newId = split[0];
var type = split[1];
var split = location.Split('/');
var type = split[0];
var newId = split[1];
Book book;
List<AuthorMetadata> authors;
@@ -452,10 +437,6 @@ namespace NzbDrone.Core.MetadataSource.BookInfo
trimmed.AuthorMetadata = book.AuthorMetadata.Value;
trimmed.SeriesLinks = book.SeriesLinks;
var edition = book.Editions.Value.SingleOrDefault(e => e.ForeignEditionId == id.ToString());
if (edition != null)
{
edition.Monitored = true;
}
trimmed.Editions = new List<Edition> { edition };
book = trimmed;
@@ -469,32 +450,16 @@ namespace NzbDrone.Core.MetadataSource.BookInfo
private List<Book> MapSearchResult(List<int> ids)
{
HttpRequest httpRequest;
HttpResponse<BulkBookResource> httpResponse;
var httpRequest = _requestBuilder.GetRequestBuilder().Create()
.SetSegment("route", $"book/bulk")
.SetHeader("Content-Type", "application/json")
.Build();
while (true)
{
httpRequest = _requestBuilder.GetRequestBuilder().Create()
.SetSegment("route", $"book/bulk")
.SetHeader("Content-Type", "application/json")
.Build();
httpRequest.SetContent(ids.ToJson());
httpRequest.SetContent(ids.ToJson());
httpRequest.AllowAutoRedirect = true;
httpRequest.AllowAutoRedirect = true;
httpRequest.SuppressHttpError = true;
httpResponse = _httpClient.Post<BulkBookResource>(httpRequest);
if (httpResponse.StatusCode == HttpStatusCode.TooManyRequests)
{
WaitUntilRetry(httpResponse);
}
else
{
break;
}
}
var httpResponse = _httpClient.Post<BulkBookResource>(httpRequest);
var mapped = MapBulkBook(httpResponse.Resource);
@@ -609,12 +574,7 @@ namespace NzbDrone.Core.MetadataSource.BookInfo
if (httpResponse.HasHttpError)
{
if (httpResponse.StatusCode == HttpStatusCode.TooManyRequests)
{
WaitUntilRetry(httpResponse);
continue;
}
else if (httpResponse.StatusCode == HttpStatusCode.NotFound)
if (httpResponse.StatusCode == HttpStatusCode.NotFound)
{
throw new AuthorNotFoundException(foreignAuthorId);
}
@@ -664,12 +624,6 @@ namespace NzbDrone.Core.MetadataSource.BookInfo
// this may redirect to an author
var httpResponse = _httpClient.Get(httpRequest);
if (httpResponse.StatusCode == HttpStatusCode.TooManyRequests)
{
WaitUntilRetry(httpResponse);
continue;
}
if (httpResponse.StatusCode == HttpStatusCode.NotFound)
{
throw new BookNotFoundException(foreignBookId);
@@ -678,9 +632,9 @@ namespace NzbDrone.Core.MetadataSource.BookInfo
if (httpResponse.HasHttpRedirect)
{
var location = httpResponse.Headers.GetSingleValue("Location");
var split = location.Split('/').Reverse().ToList();
var newId = split[0];
var type = split[1];
var split = location.Split('/');
var type = split[0];
var newId = split[1];
if (type == "author")
{
@@ -739,25 +693,6 @@ namespace NzbDrone.Core.MetadataSource.BookInfo
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)
{
var metadata = new AuthorMetadata

View File

@@ -9,7 +9,6 @@ using NzbDrone.Common.Extensions;
using NzbDrone.Common.Processes;
using NzbDrone.Common.Serializer;
using NzbDrone.Core.Books;
using NzbDrone.Core.HealthCheck;
using NzbDrone.Core.ThingiProvider;
using NzbDrone.Core.Validation;
@@ -206,7 +205,7 @@ namespace NzbDrone.Core.Notifications.CustomScript
var environmentVariables = new StringDictionary();
environmentVariables.Add("Readarr_EventType", "HealthIssue");
environmentVariables.Add("Readarr_Health_Issue_Level", Enum.GetName(typeof(HealthCheckResult), healthCheck.Type));
environmentVariables.Add("Readarr_Health_Issue_Level", nameof(healthCheck.Type));
environmentVariables.Add("Readarr_Health_Issue_Message", healthCheck.Message);
environmentVariables.Add("Readarr_Health_Issue_Type", healthCheck.Source.Name);
environmentVariables.Add("Readarr_Health_Issue_Wiki", healthCheck.WikiUrl.ToString() ?? string.Empty);

View File

@@ -1,59 +0,0 @@
using System.Collections.Generic;
using FluentValidation.Results;
using NzbDrone.Common.Extensions;
namespace NzbDrone.Core.Notifications.Ntfy
{
public class Ntfy : NotificationBase<NtfySettings>
{
private readonly INtfyProxy _proxy;
public Ntfy(INtfyProxy proxy)
{
_proxy = proxy;
}
public override string Name => "ntfy.sh";
public override string Link => "https://ntfy.sh/";
public override void OnGrab(GrabMessage grabMessage)
{
_proxy.SendNotification(BOOK_GRABBED_TITLE_BRANDED, grabMessage.Message, Settings);
}
public override void OnReleaseImport(BookDownloadMessage message)
{
_proxy.SendNotification(BOOK_DOWNLOADED_TITLE_BRANDED, message.Message, Settings);
}
public override void OnAuthorDelete(AuthorDeleteMessage deleteMessage)
{
_proxy.SendNotification(AUTHOR_DELETED_TITLE, deleteMessage.Message, Settings);
}
public override void OnBookDelete(BookDeleteMessage deleteMessage)
{
_proxy.SendNotification(BOOK_DELETED_TITLE, deleteMessage.Message, Settings);
}
public override void OnBookFileDelete(BookFileDeleteMessage deleteMessage)
{
_proxy.SendNotification(BOOK_FILE_DELETED_TITLE, deleteMessage.Message, Settings);
}
public override void OnHealthIssue(HealthCheck.HealthCheck healthCheck)
{
_proxy.SendNotification(HEALTH_ISSUE_TITLE_BRANDED, healthCheck.Message, Settings);
}
public override ValidationResult Test()
{
var failures = new List<ValidationFailure>();
failures.AddIfNotNull(_proxy.Test(Settings));
return new ValidationResult(failures);
}
}
}

View File

@@ -1,18 +0,0 @@
using System;
using NzbDrone.Common.Exceptions;
namespace NzbDrone.Core.Notifications.Ntfy
{
public class NtfyException : NzbDroneException
{
public NtfyException(string message)
: base(message)
{
}
public NtfyException(string message, Exception innerException, params object[] args)
: base(message, innerException, args)
{
}
}
}

View File

@@ -1,11 +0,0 @@
namespace NzbDrone.Core.Notifications.Ntfy
{
public enum NtfyPriority
{
Min = 1,
Low = 2,
Default = 3,
High = 4,
Max = 5
}
}

View File

@@ -1,137 +0,0 @@
using System;
using System.Linq;
using System.Net;
using FluentValidation.Results;
using NLog;
using NzbDrone.Common.Extensions;
using NzbDrone.Common.Http;
namespace NzbDrone.Core.Notifications.Ntfy
{
public interface INtfyProxy
{
void SendNotification(string title, string message, NtfySettings settings);
ValidationFailure Test(NtfySettings settings);
}
public class NtfyProxy : INtfyProxy
{
private const string DEFAULT_PUSH_URL = "https://ntfy.sh";
private readonly IHttpClient _httpClient;
private readonly Logger _logger;
public NtfyProxy(IHttpClient httpClient, Logger logger)
{
_httpClient = httpClient;
_logger = logger;
}
public void SendNotification(string title, string message, NtfySettings settings)
{
var error = false;
var serverUrl = settings.ServerUrl.IsNullOrWhiteSpace() ? NtfyProxy.DEFAULT_PUSH_URL : settings.ServerUrl;
foreach (var topic in settings.Topics)
{
var request = BuildTopicRequest(serverUrl, topic);
try
{
SendNotification(title, message, request, settings);
}
catch (NtfyException ex)
{
_logger.Error(ex, "Unable to send test message to {0}", topic);
error = true;
}
}
if (error)
{
throw new NtfyException("Unable to send Ntfy notifications to all topics");
}
}
private HttpRequestBuilder BuildTopicRequest(string serverUrl, string topic)
{
var trimServerUrl = serverUrl.TrimEnd('/');
var requestBuilder = new HttpRequestBuilder($"{trimServerUrl}/{topic}").Post();
return requestBuilder;
}
public ValidationFailure Test(NtfySettings settings)
{
try
{
const string title = "Radarr - Test Notification";
const string body = "This is a test message from Readarr";
SendNotification(title, body, settings);
}
catch (HttpException ex)
{
if (ex.Response.StatusCode == HttpStatusCode.Unauthorized || ex.Response.StatusCode == HttpStatusCode.Forbidden)
{
_logger.Error(ex, "Authorization is required");
return new ValidationFailure("UserName", "Authorization is required");
}
_logger.Error(ex, "Unable to send test message");
return new ValidationFailure("ServerUrl", "Unable to send test message");
}
catch (Exception ex)
{
_logger.Error(ex, "Unable to send test message");
return new ValidationFailure("", "Unable to send test message");
}
return null;
}
private void SendNotification(string title, string message, HttpRequestBuilder requestBuilder, NtfySettings settings)
{
try
{
requestBuilder.Headers.Add("X-Title", title);
requestBuilder.Headers.Add("X-Message", message);
requestBuilder.Headers.Add("X-Priority", settings.Priority.ToString());
if (settings.Tags.Any())
{
requestBuilder.Headers.Add("X-Tags", settings.Tags.Join(","));
}
if (!settings.ClickUrl.IsNullOrWhiteSpace())
{
requestBuilder.Headers.Add("X-Click", settings.ClickUrl);
}
var request = requestBuilder.Build();
if (!settings.UserName.IsNullOrWhiteSpace() && !settings.Password.IsNullOrWhiteSpace())
{
request.Credentials = new BasicNetworkCredential(settings.UserName, settings.Password);
}
_httpClient.Execute(request);
}
catch (HttpException ex)
{
if (ex.Response.StatusCode == HttpStatusCode.Unauthorized || ex.Response.StatusCode == HttpStatusCode.Forbidden)
{
_logger.Error(ex, "Authorization is required");
throw;
}
throw new NtfyException("Unable to send text message: {0}", ex, ex.Message);
}
}
}
}

View File

@@ -1,63 +0,0 @@
using System;
using System.Collections.Generic;
using FluentValidation;
using NzbDrone.Common.Extensions;
using NzbDrone.Core.Annotations;
using NzbDrone.Core.ThingiProvider;
using NzbDrone.Core.Validation;
namespace NzbDrone.Core.Notifications.Ntfy
{
public class NtfySettingsValidator : AbstractValidator<NtfySettings>
{
public NtfySettingsValidator()
{
RuleFor(c => c.Topics).NotEmpty();
RuleFor(c => c.Priority).InclusiveBetween(1, 5);
RuleFor(c => c.ServerUrl).IsValidUrl().When(c => !c.ServerUrl.IsNullOrWhiteSpace());
RuleFor(c => c.ClickUrl).IsValidUrl().When(c => !c.ClickUrl.IsNullOrWhiteSpace());
RuleFor(c => c.UserName).NotEmpty().When(c => !c.Password.IsNullOrWhiteSpace());
RuleFor(c => c.Password).NotEmpty().When(c => !c.UserName.IsNullOrWhiteSpace());
RuleForEach(c => c.Topics).NotEmpty().Matches("[a-zA-Z0-9_-]+").Must(c => !InvalidTopics.Contains(c)).WithMessage("Invalid topic");
}
private static List<string> InvalidTopics => new List<string> { "announcements", "app", "docs", "settings", "stats", "mytopic-rw", "mytopic-ro", "mytopic-wo" };
}
public class NtfySettings : IProviderConfig
{
private static readonly NtfySettingsValidator Validator = new NtfySettingsValidator();
public NtfySettings()
{
Topics = Array.Empty<string>();
Priority = 3;
}
[FieldDefinition(0, Label = "Server Url", Type = FieldType.Url, HelpLink = "https://ntfy.sh/docs/install/", HelpText = "Leave blank to use public server (https://ntfy.sh)")]
public string ServerUrl { get; set; }
[FieldDefinition(1, Label = "User Name", HelpText = "Optional Authorization", Privacy = PrivacyLevel.UserName)]
public string UserName { get; set; }
[FieldDefinition(2, Label = "Password", Type = FieldType.Password, HelpText = "Optional Password", Privacy = PrivacyLevel.Password)]
public string Password { get; set; }
[FieldDefinition(3, Label = "Priority", Type = FieldType.Select, SelectOptions = typeof(NtfyPriority))]
public int Priority { get; set; }
[FieldDefinition(4, Label = "Topics", HelpText = "List of Topics to send notifications to", Type = FieldType.Tag)]
public IEnumerable<string> Topics { get; set; }
[FieldDefinition(5, Label = "Ntfy Tags and Emojis", Type = FieldType.Tag, HelpText = "Optional list of tags or emojis to use", HelpLink = "https://ntfy.sh/docs/emojis/")]
public IEnumerable<string> Tags { get; set; }
[FieldDefinition(6, Label = "Click Url", Type = FieldType.Url, HelpText = "Optional link when user clicks notification")]
public string ClickUrl { get; set; }
public NzbDroneValidationResult Validate()
{
return new NzbDroneValidationResult(Validator.Validate(this));
}
}
}

View File

@@ -1,10 +1,6 @@
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text.RegularExpressions;
using FluentValidation;
using FluentValidation.Validators;
using NzbDrone.Common.Extensions;
namespace NzbDrone.Core.Organizer
{
@@ -16,16 +12,12 @@ namespace NzbDrone.Core.Organizer
public static IRuleBuilderOptions<T, string> ValidBookFormat<T>(this IRuleBuilder<T, string> ruleBuilder)
{
ruleBuilder.SetValidator(new NotEmptyValidator(null));
ruleBuilder.SetValidator(new IllegalCharactersValidator());
return ruleBuilder.SetValidator(new ValidStandardTrackFormatValidator());
}
public static IRuleBuilderOptions<T, string> ValidAuthorFolderFormat<T>(this IRuleBuilder<T, string> ruleBuilder)
{
ruleBuilder.SetValidator(new NotEmptyValidator(null));
ruleBuilder.SetValidator(new IllegalCharactersValidator());
return ruleBuilder.SetValidator(new RegularExpressionValidator(FileNameBuilder.AuthorNameRegex)).WithMessage("Must contain Author name");
}
}
@@ -50,41 +42,4 @@ namespace NzbDrone.Core.Organizer
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;
}
}
}

View File

@@ -9,7 +9,7 @@ namespace NzbDrone.Core.Validation.Paths
private readonly 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;
}
@@ -21,24 +21,7 @@ namespace NzbDrone.Core.Validation.Paths
return true;
}
var startupFolder = _appFolderInfo.StartUpFolder;
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;
return !_appFolderInfo.StartUpFolder.IsParentPath(context.PropertyValue.ToString());
}
}
}

View File

@@ -215,20 +215,11 @@ namespace NzbDrone.Host
private static IConfiguration GetConfiguration(StartupContext context)
{
var appFolder = new AppFolderInfo(context);
var configPath = appFolder.GetConfigPath();
try
{
return new ConfigurationBuilder()
.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);
}
return new ConfigurationBuilder()
.AddXmlFile(appFolder.GetConfigPath(), optional: true, reloadOnChange: false)
.AddInMemoryCollection(new List<KeyValuePair<string, string>> { new ("dataProtectionFolder", appFolder.GetDataProtectionPath()) })
.AddEnvironmentVariables()
.Build();
}
private static string BuildUrl(string scheme, string bindAddress, int port)

View File

@@ -17,8 +17,6 @@ using NzbDrone.Common.Serializer;
using NzbDrone.Core.Configuration;
using NzbDrone.Core.Datastore;
using NzbDrone.Core.Instrumentation;
using NzbDrone.Core.Lifecycle;
using NzbDrone.Core.Messaging.Events;
using NzbDrone.Host.AccessControl;
using NzbDrone.Http.Authentication;
using NzbDrone.SignalR;
@@ -134,7 +132,6 @@ namespace NzbDrone.Host
IConfigFileProvider configFileProvider,
IRuntimeInfo runtimeInfo,
IFirewallAdapter firewallAdapter,
IEventAggregator eventAggregator,
ReadarrErrorPipeline errorHandler)
{
initializeLogger.Initialize();
@@ -157,8 +154,6 @@ namespace NzbDrone.Host
Console.CancelKeyPress += (sender, eventArgs) => NLog.LogManager.Configuration = null;
}
eventAggregator.PublishEvent(new ApplicationStartingEvent());
if (OsInfo.IsWindows && runtimeInfo.IsAdmin)
{
firewallAdapter.MakeAccessible();

View File

@@ -1,7 +1,6 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>net6.0</TargetFrameworks>
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="NLog" />

View File

@@ -1,7 +1,6 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>net6.0</TargetFrameworks>
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="NLog" />

View File

@@ -26,7 +26,6 @@ namespace Readarr.Api.V1.Config
public string SslCertPassword { get; set; }
public string UrlBase { get; set; }
public string InstanceName { get; set; }
public string ApplicationUrl { get; set; }
public bool UpdateAutomatically { get; set; }
public UpdateMechanism UpdateMechanism { get; set; }
public string UpdateScriptPath { get; set; }
@@ -83,8 +82,7 @@ namespace Readarr.Api.V1.Config
CertificateValidation = configService.CertificateValidation,
BackupFolder = configService.BackupFolder,
BackupInterval = configService.BackupInterval,
BackupRetention = configService.BackupRetention,
ApplicationUrl = configService.ApplicationUrl
BackupRetention = configService.BackupRetention
};
}
}

View File

@@ -12,5 +12,15 @@ namespace Readarr.Api.V1.DownloadClient
: base(downloadClientFactory, "downloadclient", ResourceMapper)
{
}
protected override void Validate(DownloadClientDefinition definition, bool includeWarnings)
{
if (!definition.Enable)
{
return;
}
base.Validate(definition, includeWarnings);
}
}
}

View File

@@ -22,5 +22,15 @@ namespace Readarr.Api.V1.ImportLists
SharedValidator.RuleFor(c => c.QualityProfileId).SetValidator(qualityProfileExistsValidator);
SharedValidator.RuleFor(c => c.MetadataProfileId).SetValidator(metadataProfileExistsValidator);
}
protected override void Validate(ImportListDefinition definition, bool includeWarnings)
{
if (!definition.Enable)
{
return;
}
base.Validate(definition, includeWarnings);
}
}
}

View File

@@ -12,5 +12,15 @@ namespace Readarr.Api.V1.Indexers
: base(indexerFactory, "indexer", ResourceMapper)
{
}
protected override void Validate(IndexerDefinition definition, bool includeWarnings)
{
if (!definition.Enable)
{
return;
}
base.Validate(definition, includeWarnings);
}
}
}

View File

@@ -24,7 +24,7 @@ namespace Readarr.Api.V1.Indexers
public class ReleaseController : ReleaseControllerBase
{
private readonly IFetchAndParseRss _rssFetcherAndParser;
private readonly ISearchForReleases _releaseSearchService;
private readonly ISearchForNzb _nzbSearchService;
private readonly IMakeDownloadDecision _downloadDecisionMaker;
private readonly IPrioritizeDownloadDecision _prioritizeDownloadDecision;
private readonly IDownloadService _downloadService;
@@ -36,7 +36,7 @@ namespace Readarr.Api.V1.Indexers
private readonly ICached<RemoteBook> _remoteBookCache;
public ReleaseController(IFetchAndParseRss rssFetcherAndParser,
ISearchForReleases releaseSearchService,
ISearchForNzb nzbSearchService,
IMakeDownloadDecision downloadDecisionMaker,
IPrioritizeDownloadDecision prioritizeDownloadDecision,
IDownloadService downloadService,
@@ -47,7 +47,7 @@ namespace Readarr.Api.V1.Indexers
Logger logger)
{
_rssFetcherAndParser = rssFetcherAndParser;
_releaseSearchService = releaseSearchService;
_nzbSearchService = nzbSearchService;
_downloadDecisionMaker = downloadDecisionMaker;
_prioritizeDownloadDecision = prioritizeDownloadDecision;
_downloadService = downloadService;
@@ -155,7 +155,7 @@ namespace Readarr.Api.V1.Indexers
{
try
{
var decisions = _releaseSearchService.BookSearch(bookId, true, true, true);
var decisions = _nzbSearchService.BookSearch(bookId, true, true, true);
var prioritizedDecisions = _prioritizeDownloadDecision.PrioritizeDecisions(decisions);
return MapDecisions(prioritizedDecisions);
@@ -171,7 +171,7 @@ namespace Readarr.Api.V1.Indexers
{
try
{
var decisions = _releaseSearchService.AuthorSearch(authorId, false, true, true);
var decisions = _nzbSearchService.AuthorSearch(authorId, false, true, true);
var prioritizedDecisions = _prioritizeDownloadDecision.PrioritizeDecisions(decisions);
return MapDecisions(prioritizedDecisions);

View File

@@ -1,6 +1,5 @@
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using FluentValidation;
using FluentValidation.Results;
using Microsoft.AspNetCore.Mvc;
@@ -23,8 +22,6 @@ namespace Readarr.Api.V1.Indexers
private readonly IIndexerFactory _indexerFactory;
private readonly Logger _logger;
private static readonly object PushLock = new object();
public ReleasePushController(IMakeDownloadDecision downloadDecisionMaker,
IProcessDownloadDecisions downloadDecisionProcessor,
IIndexerFactory indexerFactory,
@@ -54,13 +51,8 @@ namespace Readarr.Api.V1.Indexers
ResolveIndexer(info);
List<DownloadDecision> decisions;
lock (PushLock)
{
decisions = _downloadDecisionMaker.GetRssDecision(new List<ReleaseInfo> { info });
_downloadDecisionProcessor.ProcessDecisions(decisions);
}
var decisions = _downloadDecisionMaker.GetRssDecision(new List<ReleaseInfo> { info });
_downloadDecisionProcessor.ProcessDecisions(decisions);
var firstDecision = decisions.FirstOrDefault();

View File

@@ -12,5 +12,15 @@ namespace Readarr.Api.V1.Metadata
: base(metadataFactory, "metadata", ResourceMapper)
{
}
protected override void Validate(MetadataDefinition definition, bool includeWarnings)
{
if (!definition.Enable)
{
return;
}
base.Validate(definition, includeWarnings);
}
}
}

View File

@@ -12,5 +12,15 @@ namespace Readarr.Api.V1.Notifications
: base(notificationFactory, "notification", ResourceMapper)
{
}
protected override void Validate(NotificationDefinition definition, bool includeWarnings)
{
if (!definition.Enable)
{
return;
}
base.Validate(definition, includeWarnings);
}
}
}

View File

@@ -7,7 +7,6 @@ using NzbDrone.Common.Serializer;
using NzbDrone.Core.ThingiProvider;
using NzbDrone.Core.Validation;
using NzbDrone.Http.REST.Attributes;
using Readarr.Http.Extensions;
using Readarr.Http.REST;
namespace Readarr.Api.V1
@@ -61,7 +60,7 @@ namespace Readarr.Api.V1
[RestPostById]
public ActionResult<TProviderResource> CreateProvider(TProviderResource providerResource)
{
var providerDefinition = GetDefinition(providerResource, true, false, false);
var providerDefinition = GetDefinition(providerResource, false);
if (providerDefinition.Enable)
{
@@ -76,11 +75,11 @@ namespace Readarr.Api.V1
[RestPutById]
public ActionResult<TProviderResource> UpdateProvider(TProviderResource providerResource)
{
var providerDefinition = GetDefinition(providerResource, true, false, false);
var forceSave = Request.GetBooleanQueryParameter("forceSave");
var providerDefinition = GetDefinition(providerResource, false);
var existingDefinition = _providerFactory.Get(providerDefinition.Id);
// Only test existing definitions if it is enabled and forceSave isn't set.
if (providerDefinition.Enable && !forceSave)
// Only test existing definitions if it was previously disabled
if (providerDefinition.Enable && !existingDefinition.Enable)
{
Test(providerDefinition, false);
}
@@ -90,11 +89,11 @@ namespace Readarr.Api.V1
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);
if (validate && (definition.Enable || forceValidate))
if (validate)
{
Validate(definition, includeWarnings);
}
@@ -114,7 +113,7 @@ namespace Readarr.Api.V1
{
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)
{
@@ -135,7 +134,7 @@ namespace Readarr.Api.V1
[HttpPost("test")]
public object Test([FromBody] TProviderResource providerResource)
{
var providerDefinition = GetDefinition(providerResource, true, true, true);
var providerDefinition = GetDefinition(providerResource, true);
Test(providerDefinition, true);
@@ -168,7 +167,7 @@ namespace Readarr.Api.V1
[HttpPost("action/{name}")]
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());
@@ -177,7 +176,7 @@ namespace Readarr.Api.V1
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();

View File

@@ -176,7 +176,7 @@ namespace Readarr.Api.V1.Queue
case "status":
return q => q.Status;
case "authors.sortName":
return q => q.Author?.Metadata.Value.SortName ?? q.Title;
return q => q.Author?.Metadata.Value.SortName ?? string.Empty;
case "authors.sortNameLastFirst":
return q => q.Author?.Metadata.Value.SortNameLastFirst ?? string.Empty;
case "title":

View File

@@ -1,23 +0,0 @@
using System.Collections.Generic;
using Microsoft.AspNetCore.Mvc;
namespace Readarr.Http
{
public class ApiInfoController : Controller
{
public ApiInfoController()
{
}
[HttpGet("/api")]
[Produces("application/json")]
public ApiInfoResource GetApiInfo()
{
return new ApiInfoResource
{
Current = "v1",
Deprecated = new List<string>()
};
}
}
}

View File

@@ -1,14 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Readarr.Http
{
public class ApiInfoResource
{
public string Current { get; set; }
public List<string> Deprecated { get; set; }
}
}

View File

@@ -74,7 +74,7 @@ namespace Readarr.Http.REST
}
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))
{

Some files were not shown because too many files have changed in this diff Show More