Compare commits

..

1 Commits

Author SHA1 Message Date
Mark McDowall
bd8f1823bb Sliding expiration for auth cookie and a little clean up
(cherry picked from commit 05ee4e644907b7f1e84589465ac9ab1848f5a766)
2022-08-19 06:29:50 +00:00
163 changed files with 722 additions and 2490 deletions

View File

@@ -5,9 +5,9 @@ body:
- type: checkboxes
attributes:
label: Is there an existing issue for this?
description: Please search to see if an open or closed issue already exists for the bug you encountered. If a bug exists and is closed note that it may only be fixed in an unstable branch.
description: Please search to see if an issue already exists for the bug you encountered.
options:
- label: I have searched the existing open and closed issues
- label: I have searched the existing issues
required: true
- type: textarea
attributes:
@@ -42,14 +42,12 @@ body:
- **Docker Install**: Yes
- **Using Reverse Proxy**: No
- **Browser**: Firefox 90 (If UI related)
- **Database**: Sqlite 3.36.0
value: |
- OS:
- Readarr:
- Docker Install:
- Using Reverse Proxy:
- Browser:
- Database:
render: markdown
validations:
required: true

View File

@@ -5,9 +5,9 @@ body:
- type: checkboxes
attributes:
label: Is there an existing issue for this?
description: Please search to see if an open or closed issue already exists for the feature you are requesting. If a request exists and is closed note that it may only be fixed in an unstable branch.
description: Please search to see if an issue already exists for the feature you are requesting.
options:
- label: I have searched the existing open and closed issues
- label: I have searched the existing issues
required: true
- type: textarea
attributes:

View File

@@ -1,132 +0,0 @@
# Contributor Covenant Code of Conduct
## Our Pledge
We as members, contributors, and leaders pledge to make participation in our
community a harassment-free experience for everyone, regardless of age, body
size, visible or invisible disability, ethnicity, sex characteristics, gender
identity and expression, level of experience, education, socio-economic status,
nationality, personal appearance, race, caste, color, religion, or sexual
identity and orientation.
We pledge to act and interact in ways that contribute to an open, welcoming,
diverse, inclusive, and healthy community.
## Our Standards
Examples of behavior that contributes to a positive environment for our
community include:
* Demonstrating empathy and kindness toward other people
* Being respectful of differing opinions, viewpoints, and experiences
* Giving and gracefully accepting constructive feedback
* Accepting responsibility and apologizing to those affected by our mistakes,
and learning from the experience
* Focusing on what is best not just for us as individuals, but for the overall
community
Examples of unacceptable behavior include:
* The use of sexualized language or imagery, and sexual attention or advances of
any kind
* Trolling, insulting or derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or email address,
without their explicit permission
* Other conduct which could reasonably be considered inappropriate in a
professional setting
## Enforcement Responsibilities
Community leaders are responsible for clarifying and enforcing our standards of
acceptable behavior and will take appropriate and fair corrective action in
response to any behavior that they deem inappropriate, threatening, offensive,
or harmful.
Community leaders have the right and responsibility to remove, edit, or reject
comments, commits, code, wiki edits, issues, and other contributions that are
not aligned to this Code of Conduct, and will communicate reasons for moderation
decisions when appropriate.
## Scope
This Code of Conduct applies within all community spaces, and also applies when
an individual is officially representing the community in public spaces.
Examples of representing our community include using an official e-mail address,
posting via an official social media account, or acting as an appointed
representative at an online or offline event.
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported to the community leaders responsible for enforcement at
<development@readarr.com>.
All complaints will be reviewed and investigated promptly and fairly.
All community leaders are obligated to respect the privacy and security of the
reporter of any incident.
## Enforcement Guidelines
Community leaders will follow these Community Impact Guidelines in determining
the consequences for any action they deem in violation of this Code of Conduct:
### 1. Correction
**Community Impact**: Use of inappropriate language or other behavior deemed
unprofessional or unwelcome in the community.
**Consequence**: A private, written warning from community leaders, providing
clarity around the nature of the violation and an explanation of why the
behavior was inappropriate. A public apology may be requested.
### 2. Warning
**Community Impact**: A violation through a single incident or series of
actions.
**Consequence**: A warning with consequences for continued behavior. No
interaction with the people involved, including unsolicited interaction with
those enforcing the Code of Conduct, for a specified period of time. This
includes avoiding interactions in community spaces as well as external channels
like social media. Violating these terms may lead to a temporary or permanent
ban.
### 3. Temporary Ban
**Community Impact**: A serious violation of community standards, including
sustained inappropriate behavior.
**Consequence**: A temporary ban from any sort of interaction or public
communication with the community for a specified period of time. No public or
private interaction with the people involved, including unsolicited interaction
with those enforcing the Code of Conduct, is allowed during this period.
Violating these terms may lead to a permanent ban.
### 4. Permanent Ban
**Community Impact**: Demonstrating a pattern of violation of community
standards, including sustained inappropriate behavior, harassment of an
individual, or aggression toward or disparagement of classes of individuals.
**Consequence**: A permanent ban from any sort of public interaction within the
community.
## Attribution
This Code of Conduct is adapted from the [Contributor Covenant][homepage],
version 2.1, available at
[https://www.contributor-covenant.org/version/2/1/code_of_conduct.html][v2.1].
Community Impact Guidelines were inspired by
[Mozilla's code of conduct enforcement ladder][Mozilla CoC].
For answers to common questions about this code of conduct, see the FAQ at
[https://www.contributor-covenant.org/faq][FAQ]. Translations are available at
[https://www.contributor-covenant.org/translations][translations].
[homepage]: https://www.contributor-covenant.org
[v2.1]: https://www.contributor-covenant.org/version/2/1/code_of_conduct.html
[Mozilla CoC]: https://github.com/mozilla/diversity
[FAQ]: https://www.contributor-covenant.org/faq
[translations]: https://www.contributor-covenant.org/translations

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

@@ -1,8 +0,0 @@
# Security Policy
## 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
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)'
@@ -730,7 +730,7 @@ stages:
inputs:
buildType: 'current'
artifactName: Packages
itemPattern: '**/$(pattern)'
itemPattern: '/$(pattern)'
targetPath: $(Build.ArtifactStagingDirectory)
- bash: |
mkdir -p ${BUILD_ARTIFACTSTAGINGDIRECTORY}/bin
@@ -1050,4 +1050,3 @@ stages:
SYSTEM_ACCESSTOKEN: $(System.AccessToken)
DISCORDCHANNELID: $(discordChannelId)
DISCORDWEBHOOKKEY: $(discordWebhookKey)
DISCORDTHREADID: $(discordThreadId)

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,5 +1,4 @@
.jumpBar {
z-index: $pageJumpBarZIndex;
display: flex;
align-content: stretch;
align-items: stretch;

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

@@ -148,10 +148,11 @@ class AddNewItem extends Component {
);
} else if (item.book) {
const book = item.book;
const edition = book.editions.find((x) => x.monitored);
return (
<AddNewBookSearchResultConnector
key={item.id}
isExistingBook={'id' in book && book.id !== 0}
isExistingBook={'id' in edition && edition.id !== 0}
isExistingAuthor={'id' in book.author && book.author.id !== 0}
{...book}
/>

View File

@@ -138,20 +138,17 @@ class AddNewBookSearchResult extends Component {
null
}
{
editions && editions.length > 1 ?
<Link
className={styles.mbLink}
to={`https://goodreads.com/book/show/${editions[0].foreignEditionId}`}
onPress={this.onTVDBLinkPress}
>
<Icon
className={styles.mbLinkIcon}
name={icons.EXTERNAL_LINK}
size={28}
/>
</Link> : null
}
<Link
className={styles.mbLink}
to={`https://goodreads.com/book/show/${editions[0].foreignEditionId}`}
onPress={this.onTVDBLinkPress}
>
<Icon
className={styles.mbLinkIcon}
name={icons.EXTERNAL_LINK}
size={28}
/>
</Link>
</div>
</div>
@@ -221,7 +218,7 @@ AddNewBookSearchResult.propTypes = {
overview: PropTypes.string,
ratings: PropTypes.object.isRequired,
author: PropTypes.object,
editions: PropTypes.arrayOf(PropTypes.object),
editions: PropTypes.arrayOf(PropTypes.object).isRequired,
images: PropTypes.arrayOf(PropTypes.object).isRequired,
isExistingBook: PropTypes.bool.isRequired,
isExistingAuthor: PropTypes.bool.isRequired,

View File

@@ -173,7 +173,7 @@ class AddAuthorOptionsForm extends Component {
AddAuthorOptionsForm.propTypes = {
rootFolderPath: PropTypes.object,
monitor: PropTypes.object.isRequired,
monitorNewItems: PropTypes.object.isRequired,
monitorNewItems: PropTypes.string.isRequired,
qualityProfileId: PropTypes.object,
metadataProfileId: PropTypes.object,
showMetadataProfile: PropTypes.bool.isRequired,

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,5 +1,4 @@
module.exports = {
pageJumpBarZIndex: 10,
modalZIndex: 1000,
popperZIndex: 2000
};

View File

@@ -20,6 +20,7 @@ class About extends Component {
packageVersion,
packageAuthor,
isNetCore,
isMono,
isDocker,
runtimeVersion,
migrationVersion,
@@ -49,6 +50,14 @@ class About extends Component {
/>
}
{
isMono &&
<DescriptionListItem
title={translate('MonoVersion')}
data={runtimeVersion}
/>
}
{
isNetCore &&
<DescriptionListItem
@@ -112,6 +121,7 @@ About.propTypes = {
packageVersion: PropTypes.string,
packageAuthor: PropTypes.string,
isNetCore: PropTypes.bool.isRequired,
isMono: PropTypes.bool.isRequired,
runtimeVersion: PropTypes.string.isRequired,
isDocker: PropTypes.bool.isRequired,
migrationVersion: PropTypes.number.isRequired,

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

@@ -437,6 +437,24 @@ namespace NzbDrone.Common.Test.DiskTests
Assert.Throws<IOException>(() => Subject.TransferFolder(source.FullName, destination.FullName, TransferMode.Copy));
}
[Test]
public void CopyFolder_should_not_copy_casesensitive_folder()
{
MonoOnly();
WithRealDiskProvider();
var original = GetFilledTempFolder();
var root = new DirectoryInfo(GetTempFilePath());
var source = new DirectoryInfo(root.FullName + "A/series");
var destination = new DirectoryInfo(root.FullName + "A/Series");
Subject.TransferFolder(original.FullName, source.FullName, TransferMode.Copy);
// Note: Although technically possible top copy to different case, we're not allowing it
Assert.Throws<IOException>(() => Subject.TransferFolder(source.FullName, destination.FullName, TransferMode.Copy));
}
[Test]
public void CopyFolder_should_ignore_nfs_temp_file()
{
@@ -522,6 +540,26 @@ namespace NzbDrone.Common.Test.DiskTests
source.FullName.GetActualCasing().Should().Be(destination.FullName);
}
[Test]
public void MoveFolder_should_rename_casesensitive_folder()
{
MonoOnly();
WithRealDiskProvider();
var original = GetFilledTempFolder();
var root = new DirectoryInfo(GetTempFilePath());
var source = new DirectoryInfo(root.FullName + "A/series");
var destination = new DirectoryInfo(root.FullName + "A/Series");
Subject.TransferFolder(original.FullName, source.FullName, TransferMode.Copy);
Subject.TransferFolder(source.FullName, destination.FullName, TransferMode.Move);
Directory.Exists(source.FullName).Should().Be(false);
Directory.Exists(destination.FullName).Should().Be(true);
}
[Test]
public void should_throw_if_destination_is_readonly()
{

View File

@@ -207,7 +207,6 @@ namespace NzbDrone.Common.Test.Http
}
[Test]
[Platform(Exclude = "MacOsX", Reason = "Azure agent update prevents brotli on OSX")]
public void should_execute_get_using_brotli()
{
var request = new HttpRequest($"https://{_httpBinHost}/brotli");
@@ -308,6 +307,11 @@ namespace NzbDrone.Common.Test.Http
[Test]
public void should_follow_redirects_to_https()
{
if (typeof(TDispatcher) == typeof(ManagedHttpDispatcher) && PlatformInfo.IsMono)
{
Assert.Ignore("Will fail on tls1.2 via managed dispatcher, ignore.");
}
var request = new HttpRequestBuilder($"https://{_httpBinHost}/redirect-to")
.AddQueryParam("url", $"https://readarr.com/")
.Build();

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

@@ -279,7 +279,7 @@ namespace NzbDrone.Common.Test
[Test]
public void GetUpdateClientExePath()
{
GetIAppDirectoryInfo().GetUpdateClientExePath().Should().BeEquivalentTo(@"C:\Temp\readarr_update\Readarr.Update".AsOsAgnostic().ProcessNameToExe());
GetIAppDirectoryInfo().GetUpdateClientExePath(PlatformType.DotNet).Should().BeEquivalentTo(@"C:\Temp\readarr_update\Readarr.Update.exe".AsOsAgnostic());
}
[Test]

View File

@@ -1,4 +1,4 @@
using System;
using System;
using System.ComponentModel;
using System.Diagnostics;
using System.IO;
@@ -171,7 +171,7 @@ namespace NzbDrone.Common.Test
var processStarted = new ManualResetEventSlim();
string suffix;
if (OsInfo.IsWindows)
if (OsInfo.IsWindows || PlatformInfo.IsMono)
{
suffix = ".exe";
}

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

@@ -1,7 +1,17 @@
using System;
using System;
using System.Reflection;
using System.Text.RegularExpressions;
using Microsoft.Win32;
namespace NzbDrone.Common.EnvironmentInfo
{
public enum PlatformType
{
DotNet = 0,
Mono = 1,
NetCore = 2
}
public interface IPlatformInfo
{
Version Version { get; }
@@ -9,18 +19,38 @@ namespace NzbDrone.Common.EnvironmentInfo
public class PlatformInfo : IPlatformInfo
{
private static readonly Regex MonoVersionRegex = new Regex(@"(?<=\W|^)(?<version>\d+\.\d+(\.\d+)?(\.\d+)?)(?=\W)", RegexOptions.Compiled | RegexOptions.IgnoreCase);
private static PlatformType _platform;
private static Version _version;
static PlatformInfo()
{
_platform = PlatformType.NetCore;
_version = Environment.Version;
}
public static PlatformType Platform => _platform;
public static bool IsMono => Platform == PlatformType.Mono;
public static bool IsDotNet => Platform == PlatformType.DotNet;
public static bool IsNetCore => Platform == PlatformType.NetCore;
public static string PlatformName
{
get
{
return ".NET";
if (IsDotNet)
{
return ".NET";
}
else if (IsMono)
{
return "Mono";
}
else
{
return ".NET Core";
}
}
}
@@ -30,5 +60,107 @@ namespace NzbDrone.Common.EnvironmentInfo
{
return _version;
}
private static Version GetMonoVersion()
{
try
{
var type = Type.GetType("Mono.Runtime");
if (type != null)
{
var displayNameMethod = type.GetMethod("GetDisplayName", BindingFlags.NonPublic | BindingFlags.Static);
if (displayNameMethod != null)
{
var displayName = displayNameMethod.Invoke(null, null).ToString();
var versionMatch = MonoVersionRegex.Match(displayName);
if (versionMatch.Success)
{
return new Version(versionMatch.Groups["version"].Value);
}
}
}
}
catch (Exception ex)
{
Console.WriteLine("Couldnt get Mono version: " + ex.ToString());
}
return new Version();
}
private static Version GetDotNetVersion()
{
try
{
const string subkey = @"SOFTWARE\Microsoft\NET Framework Setup\NDP\v4\Full\";
using (var ndpKey = RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, RegistryView.Registry32).OpenSubKey(subkey))
{
if (ndpKey == null)
{
return new Version(4, 0);
}
var releaseKey = (int)ndpKey.GetValue("Release");
if (releaseKey >= 528040)
{
return new Version(4, 8, 0);
}
if (releaseKey >= 461808)
{
return new Version(4, 7, 2);
}
if (releaseKey >= 461308)
{
return new Version(4, 7, 1);
}
if (releaseKey >= 460798)
{
return new Version(4, 7);
}
if (releaseKey >= 394802)
{
return new Version(4, 6, 2);
}
if (releaseKey >= 394254)
{
return new Version(4, 6, 1);
}
if (releaseKey >= 393295)
{
return new Version(4, 6);
}
if (releaseKey >= 379893)
{
return new Version(4, 5, 2);
}
if (releaseKey >= 378675)
{
return new Version(4, 5, 1);
}
if (releaseKey >= 378389)
{
return new Version(4, 5);
}
}
}
catch (Exception ex)
{
Console.WriteLine("Couldnt get .NET framework version: " + ex.ToString());
}
return new Version(4, 0);
}
}
}

View File

@@ -240,9 +240,9 @@ namespace NzbDrone.Common.Extensions
return null;
}
public static string ProcessNameToExe(this string processName)
public static string ProcessNameToExe(this string processName, PlatformType runtime)
{
if (OsInfo.IsWindows)
if (OsInfo.IsWindows || runtime != PlatformType.NetCore)
{
processName += ".exe";
}
@@ -250,6 +250,11 @@ namespace NzbDrone.Common.Extensions
return processName;
}
public static string ProcessNameToExe(this string processName)
{
return processName.ProcessNameToExe(PlatformInfo.Platform);
}
public static string GetLongestCommonPath(this List<string> paths)
{
var firstPath = paths.First();
@@ -342,9 +347,9 @@ namespace NzbDrone.Common.Extensions
return Path.Combine(GetUpdatePackageFolder(appFolderInfo), UPDATE_CLIENT_FOLDER_NAME);
}
public static string GetUpdateClientExePath(this IAppFolderInfo appFolderInfo)
public static string GetUpdateClientExePath(this IAppFolderInfo appFolderInfo, PlatformType runtime)
{
return Path.Combine(GetUpdateSandboxFolder(appFolderInfo), UPDATE_CLIENT_EXE_NAME).ProcessNameToExe();
return Path.Combine(GetUpdateSandboxFolder(appFolderInfo), UPDATE_CLIENT_EXE_NAME).ProcessNameToExe(runtime);
}
public static string GetDatabase(this IAppFolderInfo appFolderInfo)

View File

@@ -55,8 +55,7 @@ namespace NzbDrone.Common.Http
StatusCode == HttpStatusCode.Found ||
StatusCode == HttpStatusCode.TemporaryRedirect ||
StatusCode == HttpStatusCode.RedirectMethod ||
StatusCode == HttpStatusCode.SeeOther ||
StatusCode == HttpStatusCode.PermanentRedirect;
StatusCode == HttpStatusCode.SeeOther;
public string[] GetCookieHeaders()
{

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

@@ -38,6 +38,16 @@ namespace NzbDrone.Common.Instrumentation
return;
}
if (PlatformInfo.IsMono)
{
if ((exception is TypeInitializationException && exception.InnerException is DllNotFoundException) ||
exception is DllNotFoundException)
{
Logger.Debug(exception, "Minor Fail: " + exception.Message);
return;
}
}
Console.WriteLine("EPIC FAIL: {0}", exception);
Logger.Fatal(exception, "EPIC FAIL.");
}

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

@@ -109,6 +109,13 @@ namespace NzbDrone.Common.Instrumentation.Sentry
o.Debug = false;
o.DiagnosticLevel = SentryLevel.Debug;
o.Release = BuildInfo.Release;
if (PlatformInfo.IsMono)
{
// Mono 6.0 broke GzipStream.WriteAsync
// TODO: Check specific version
o.RequestBodyCompressionLevel = System.IO.Compression.CompressionLevel.NoCompression;
}
o.BeforeSend = x => SentryCleanser.CleanseEvent(x);
o.BeforeBreadcrumb = x => SentryCleanser.CleanseBreadcrumb(x);
o.Environment = BuildInfo.Branch;
@@ -151,6 +158,14 @@ namespace NzbDrone.Common.Instrumentation.Sentry
SentrySdk.ConfigureScope(scope =>
{
scope.SetTag("is_docker", $"{osInfo.IsDocker}");
if (osInfo.Name != null && PlatformInfo.IsMono)
{
// Sentry auto-detection of non-Windows platforms isn't that accurate on certain devices.
scope.Contexts.OperatingSystem.Name = osInfo.Name.FirstCharToUpper();
scope.Contexts.OperatingSystem.RawDescription = osInfo.FullName;
scope.Contexts.OperatingSystem.Version = osInfo.Version.ToString();
}
});
}
@@ -206,11 +221,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

@@ -377,6 +377,11 @@ namespace NzbDrone.Common.Processes
private (string Path, string Args) GetPathAndArgs(string path, string args)
{
if (PlatformInfo.IsMono && path.EndsWith(".exe", StringComparison.InvariantCultureIgnoreCase))
{
return ("mono", $"--debug {path} {args}");
}
if (OsInfo.IsWindows && path.EndsWith(".bat", StringComparison.InvariantCultureIgnoreCase))
{
return ("cmd.exe", $"/c {path} {args}");

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

@@ -16,6 +16,11 @@ namespace NzbDrone.Core.Test.MediaCoverTests
[SetUp]
public void SetUp()
{
if (PlatformInfo.IsMono && PlatformInfo.GetVersion() < new Version(5, 8))
{
Assert.Inconclusive("Not supported on Mono < 5.8");
}
Mocker.GetMock<IDiskProvider>()
.Setup(v => v.FileExists(It.IsAny<string>()))
.Returns<string>(s => File.Exists(s));

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

@@ -7,6 +7,7 @@ using NzbDrone.Core.Test.Framework;
namespace NzbDrone.Core.Test.ParserTests
{
[TestFixture]
public class QualityParserFixture : CoreTest
{
public static object[] SelfQualityParserCases =

View File

@@ -69,7 +69,7 @@ namespace NzbDrone.Core.Test.UpdateTests
.Returns(true);
Mocker.GetMock<IDiskProvider>()
.Setup(v => v.FileExists(It.Is<string>(s => s.EndsWith("Readarr.Update".ProcessNameToExe()))))
.Setup(v => v.FileExists(It.Is<string>(s => s.EndsWith("Readarr.Update.exe"))))
.Returns(true);
_sandboxFolder = Mocker.GetMock<IAppFolderInfo>().Object.GetUpdateSandboxFolder();
@@ -165,7 +165,7 @@ namespace NzbDrone.Core.Test.UpdateTests
public void should_return_with_warning_if_updater_doesnt_exists()
{
Mocker.GetMock<IDiskProvider>()
.Setup(v => v.FileExists(It.Is<string>(s => s.EndsWith("Readarr.Update".ProcessNameToExe()))))
.Setup(v => v.FileExists(It.Is<string>(s => s.EndsWith("Readarr.Update.exe"))))
.Returns(false);
Subject.Execute(new ApplicationUpdateCommand());

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,7 +1,6 @@
using System;
using System.Data.Common;
using System.Data.SQLite;
using System.Net.Sockets;
using NLog;
using Npgsql;
using NzbDrone.Common.Disk;
@@ -132,37 +131,6 @@ namespace NzbDrone.Core.Datastore
throw new CorruptDatabaseException("Database file: {0} is corrupt, restore from backup if available. See: https://wiki.servarr.com/readarr/faq#i-am-getting-an-error-database-disk-image-is-malformed", e, fileName);
}
catch (NpgsqlException e)
{
if (e.InnerException is SocketException)
{
var retryCount = 3;
while (true)
{
Logger.Error(e, "Failure to connect to Postgres DB, {0} retries remaining", retryCount);
try
{
_migrationController.Migrate(connectionString, migrationContext);
}
catch (Exception ex)
{
if (--retryCount > 0)
{
System.Threading.Thread.Sleep(5000);
continue;
}
throw new ReadarrStartupException(ex, "Error creating main database");
}
}
}
else
{
throw new ReadarrStartupException(e, "Error creating main database");
}
}
catch (Exception e)
{
throw new ReadarrStartupException(e, "Error creating main database");

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

@@ -88,8 +88,6 @@ namespace NzbDrone.Core.Download.Clients.QBittorrent
if (!addHasSetShareLimits && setShareLimits)
{
Proxy.SetTorrentSeedingConfiguration(hash.ToLower(), remoteBook.SeedConfiguration, Settings);
try
{
Proxy.SetTorrentSeedingConfiguration(hash.ToLower(), remoteBook.SeedConfiguration, Settings);
@@ -288,8 +286,7 @@ namespace NzbDrone.Core.Download.Clients.QBittorrent
break;
case "forcedDL": // torrent is being downloaded, and was forced started
case "forcedMetaDL": // torrent metadata is being forcibly downloaded
case "forcedDL": //torrent is being downloaded, and was forced started
case "moving": // torrent is being moved from a folder
case "downloading": // torrent is being downloaded and data is being transferred
item.Status = DownloadItemStatus.Downloading;

View File

@@ -142,8 +142,20 @@ namespace NzbDrone.Core.Download.Clients.QBittorrent
var request = BuildRequest(settings).Resource("/api/v2/torrents/add")
.Post()
.AddFormParameter("urls", torrentUrl);
if (settings.MusicCategory.IsNotNullOrWhiteSpace())
{
request.AddFormParameter("category", settings.MusicCategory);
}
AddTorrentDownloadFormParameters(request, settings);
// Note: ForceStart is handled by separate api call
if ((QBittorrentState)settings.InitialState == QBittorrentState.Start)
{
request.AddFormParameter("paused", false);
}
else if ((QBittorrentState)settings.InitialState == QBittorrentState.Pause)
{
request.AddFormParameter("paused", true);
}
if (seedConfiguration != null)
{
@@ -165,7 +177,20 @@ namespace NzbDrone.Core.Download.Clients.QBittorrent
.Post()
.AddFormUpload("torrents", fileName, fileContent);
AddTorrentDownloadFormParameters(request, settings);
if (settings.MusicCategory.IsNotNullOrWhiteSpace())
{
request.AddFormParameter("category", settings.MusicCategory);
}
// Note: ForceStart is handled by separate api call
if ((QBittorrentState)settings.InitialState == QBittorrentState.Start)
{
request.AddFormParameter("paused", false);
}
else if ((QBittorrentState)settings.InitialState == QBittorrentState.Pause)
{
request.AddFormParameter("paused", true);
}
if (seedConfiguration != null)
{
@@ -234,34 +259,6 @@ namespace NzbDrone.Core.Download.Clients.QBittorrent
}
}
private void AddTorrentDownloadFormParameters(HttpRequestBuilder request, QBittorrentSettings settings)
{
if (settings.MusicCategory.IsNotNullOrWhiteSpace())
{
request.AddFormParameter("category", settings.MusicCategory);
}
// Note: ForceStart is handled by separate api call
if ((QBittorrentState)settings.InitialState == QBittorrentState.Start)
{
request.AddFormParameter("paused", false);
}
else if ((QBittorrentState)settings.InitialState == QBittorrentState.Pause)
{
request.AddFormParameter("paused", true);
}
if (settings.SequentialOrder)
{
request.AddFormParameter("sequentialDownload", true);
}
if (settings.FirstAndLast)
{
request.AddFormParameter("firstLastPiecePrio", true);
}
}
public void SetTorrentSeedingConfiguration(string hash, TorrentSeedConfiguration seedConfiguration, QBittorrentSettings settings)
{
var request = BuildRequest(settings).Resource("/api/v2/torrents/setShareLimits")

View File

@@ -63,12 +63,6 @@ namespace NzbDrone.Core.Download.Clients.QBittorrent
[FieldDefinition(10, Label = "Initial State", Type = FieldType.Select, SelectOptions = typeof(QBittorrentState), HelpText = "Initial state for torrents added to qBittorrent. Note that Forced Torrents do not abide by seed restrictions")]
public int InitialState { get; set; }
[FieldDefinition(11, Label = "Sequential Order", Type = FieldType.Checkbox, HelpText = "Download in sequential order (qBittorrent 4.1.0+)")]
public bool SequentialOrder { get; set; }
[FieldDefinition(12, Label = "First and Last First", Type = FieldType.Checkbox, HelpText = "Download first and last pieces first (qBittorrent 4.1.0+)")]
public bool FirstAndLast { get; set; }
public NzbDroneValidationResult Validate()
{
return new NzbDroneValidationResult(Validator.Validate(this));

View File

@@ -14,7 +14,7 @@ namespace NzbDrone.Core.Download.Clients.QBittorrent
public double Progress { get; set; } // Torrent progress (%/100)
public BigInteger Eta { get; set; } // Torrent ETA (seconds) (QBit contains a bug exceeding ulong limits)
public BigInteger Eta { get; set; } // Torrent ETA (seconds)
public string State { get; set; } // Torrent state. See possible values here below

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

@@ -52,7 +52,7 @@ namespace NzbDrone.Core.HealthCheck
.AddQueryParam("version", BuildInfo.Version)
.AddQueryParam("os", OsInfo.Os.ToString().ToLowerInvariant())
.AddQueryParam("arch", RuntimeInformation.OSArchitecture)
.AddQueryParam("runtime", "netcore")
.AddQueryParam("runtime", PlatformInfo.Platform.ToString().ToLowerInvariant())
.AddQueryParam("branch", _configFileProvider.Branch)
.Build();
try

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

@@ -50,7 +50,7 @@ namespace NzbDrone.Core.Indexers
{
private static readonly SeedCriteriaSettingsValidator Validator = new SeedCriteriaSettingsValidator();
[FieldDefinition(0, Type = FieldType.Textbox, Label = "Seed Ratio", HelpText = "The ratio a torrent should reach before stopping, empty is download client's default. Ratio should be at least 1.0 and follow the indexers rules")]
[FieldDefinition(0, Type = FieldType.Textbox, Label = "Seed Ratio", HelpText = "The ratio a torrent should reach before stopping, empty is download client's default", Advanced = true)]
public double? SeedRatio { get; set; }
[FieldDefinition(1, Type = FieldType.Textbox, Label = "Seed Time", Unit = "minutes", HelpText = "The time a torrent should be seeded before stopping, empty is download client's default", Advanced = true)]

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

@@ -557,6 +557,5 @@
"RestartRequiredHelpTextWarning": "يتطلب إعادة التشغيل ليصبح ساري المفعول",
"AddList": "اضف قائمة",
"RenameFiles": "إعادة تسمية الملفات",
"Test": "اختبار",
"ManualImportSelectEdition": "استيراد يدوي - حدد فيلم"
"Test": "اختبار"
}

View File

@@ -556,6 +556,5 @@
"RestartRequiredHelpTextWarning": "Изисква рестартиране, за да влезе в сила",
"Test": "Тест",
"AddList": "Добавяне на списък",
"RenameFiles": "Преименувайте файловете",
"ManualImportSelectEdition": "Ръчно импортиране - Изберете филм"
"RenameFiles": "Преименувайте файловете"
}

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",
@@ -556,6 +556,5 @@
"RestartRequiredHelpTextWarning": "Vyžaduje restart, aby se projevilo",
"RenameFiles": "Přejmenovat soubory",
"Test": "Test",
"AddList": "Přidat seznam",
"ManualImportSelectEdition": "Ruční import - vyberte Film"
"AddList": "Přidat seznam"
}

View File

@@ -556,6 +556,5 @@
"RestartRequiredHelpTextWarning": "Kræver genstart for at træde i kraft",
"AddList": "Tilføj Liste",
"Test": "Prøve",
"RenameFiles": "Omdøb filer",
"ManualImportSelectEdition": "Manuel import - Vælg film"
"RenameFiles": "Omdøb filer"
}

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",
@@ -92,8 +92,8 @@
"DeleteNotificationMessageText": "Benachrichtigung '{0}' wirklich löschen?",
"DeleteQualityProfile": "Qualitätsdefinition löschen",
"DeleteQualityProfileMessageText": "Qualitätsprofil '{0}' wirklich löschen?",
"DeleteReleaseProfile": "Release-Profil löschen",
"DeleteReleaseProfileMessageText": "Bist du sicher, dass du dieses Release-Profil löschen willst?",
"DeleteReleaseProfile": "Verzögerungsprofil löschen",
"DeleteReleaseProfileMessageText": "Bist du sicher, dass du dieses Verzögerungs-Profil löschen willst?",
"DeleteRootFolderMessageText": "Indexer '{0}' wirklich löschen?",
"DeleteSelectedBookFiles": "Ausgewählte Filmdateien löschen",
"DeleteSelectedBookFilesMessageText": "Ausgewählte Filme wirklich löschen?",
@@ -172,7 +172,7 @@
"ImportedTo": "Importiert nach",
"Importing": "Importiere",
"IncludeHealthWarningsHelpText": "Zustandswarnung",
"IncludeUnknownAuthorItemsHelpText": "Einträge ohne eine Zuordnung in der Warteschlange anzeigen. Dies könnten gelöschte Autoren, Bücher oder alles andere mit Readarrs Downloadkategorie sein",
"IncludeUnknownAuthorItemsHelpText": "Einträge ohne eine Zuordnung in der Warteschlange anzeigen. Dies könnten gelöschte Filme oder alles andere mit Radarrs Downloadkategorie sein",
"IncludeUnmonitored": "Nicht beobachtete einbeziehen",
"Indexer": "Indexer",
"IndexerPriority": "Priorität",
@@ -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,10 +543,10 @@
"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.",
"IndexerPriorityHelpText": "Indexer Priorität von 1 (höchste) bis 50 (niedrigste). Standard: 25.",
"IndexerRssHealthCheckNoAvailableIndexers": "Alle RSS-fähigen Indexer sind aufgrund der kürzlichen Indexerfehler vorübergehend nicht verfügbar",
"DiscNumber": "Plattennummer",
"Lists": "Listen",
@@ -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",
@@ -713,7 +713,7 @@
"PathHelpText": "Stammordner für die Musikbibliothek",
"ProxyCheckBadRequestMessage": "Proxy konnte nicht getestet werden. StatusCode: {0}",
"ProxyCheckFailedToTestMessage": "Proxy konnte nicht getestet werden: {0}",
"Queued": "In der Warteschlange",
"Queued": "Eingereiht",
"RefreshAndScan": "Aktualisieren",
"SettingsRemotePathMappingRemotePathHelpText": "Root-Pfad zum Verzeichnis, auf das der Download-Client zugreift",
"Yesterday": "Gestern",
@@ -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öchlicherweise 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,104 +780,11 @@
"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",
"RenameFiles": "Dateien umbenennen",
"LoadingEditionsFailed": "Das Laden der Ausgaben ist fehlgeschlagen",
"MissingBooksAuthorMonitored": "Fehlende Bücher (Autoren überwacht)",
"MonitorBook": "Buch überwachen",
"MinimumPages": "Mindestseiten",
"MassBookSearch": "Massensuche nach Büchern",
"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",
"MissingBooks": "Fehlende Bücher",
"MonitorAuthor": "Autor überwachen",
"MissingBooksAuthorNotMonitored": "Fehlende Bücher (Autor nicht überwacht)",
"ConvertToFormat": "In Format umwandeln",
"EditList": "Liste bearbeiten",
"ManualImportSelectEdition": "Manueller Import - Ausgabe auswählen",
"MetadataSettingsSummary": "Metadaten-Dateien erstellen, wenn Bücher importiert oder Autoren aktualisiert werden",
"DataAllBooks": "Alle Bücher überwachen",
"DataLatestBook": "Die neusten und zukünftigen Bücher überwachen",
"Database": "Datenbank",
"DataListMonitorAll": "Autoren und alle Bücher für jeden Autor werden auf der Import-Liste miteinbezogen",
"DataListMonitorNone": "Autoren oder Bücher nicht überwachen",
"DataListMonitorSpecificBook": "Autoren überwachen aber nur Bücher überwachen, welche explizit in der Liste miteinbezogen wurden",
"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"
"DataNone": "Es werden keine Alben beobachtet",
"RenameFiles": "Dateien umbenennen"
}

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": "Σχετικά",
@@ -556,10 +556,5 @@
"RestartRequiredHelpTextWarning": "Απαιτείται επανεκκίνηση για να τεθεί σε ισχύ",
"Test": "Δοκιμή",
"AddList": "Προσθήκη Λίστας",
"RenameFiles": "Μετονομασία αρχείων",
"ManualImportSelectEdition": "Μη αυτόματη εισαγωγή - Επιλέξτε ταινία",
"AllExpandedCollapseAll": "Σύμπτυξη Όλων",
"AllExpandedExpandAll": "Ανάπτυξη Όλων",
"AddNewItem": "Προσθήκη Νέου",
"AddMissing": "Προσθήκη Στοιχείου Που Λείπει"
"RenameFiles": "Μετονομασία αρχείων"
}

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

@@ -216,7 +216,7 @@
"MinimumLimits": "Límites Mínimos",
"Missing": "Falta",
"Mode": "Modo",
"Monitored": "Monitoreada",
"Monitored": "Monitoreadas",
"MonoVersion": "Version de Mono",
"MoreInfo": "Más Información",
"MustContain": "Debe Contener",
@@ -235,8 +235,8 @@
"NotificationTriggers": "Desencadenantes de Notificaciones",
"OnGrabHelpText": "Al Capturar",
"OnHealthIssueHelpText": "En Problema de Salud",
"OnRenameHelpText": "Al Renombrar",
"OnUpgradeHelpText": "Al Mejorar La Calidad",
"OnRenameHelpText": "En Renombrado",
"OnUpgradeHelpText": "Al Actualizar",
"OpenBrowserOnStart": "Abrir navegador al arrancar",
"Options": "Opciones",
"Original": "Original",
@@ -269,7 +269,7 @@
"QualityProfiles": "Perfiles de Calidad",
"QualitySettings": "Ajustes de Calidad",
"Queue": "Cola",
"RSSSync": "Sincronizar RSS",
"RSSSync": "Sincronización RSS",
"RSSSyncInterval": "Intervalo de Sincronización de RSS",
"ReadTheWikiForMoreInformation": "Lee la Wiki para más información",
"ReadarrSupportsAnyIndexerThatUsesTheNewznabStandardAsWellAsOtherIndexersListedBelow": "Radarr soporta cualquier indexer que utilice el estandar Newznab, como también cualquiera de los indexers listados debajo.",
@@ -557,8 +557,8 @@
"OnBookFileDeleteForUpgradeHelpText": "En archivo de película Eliminar para actualizar",
"OnBookFileDeleteHelpText": "Al eliminar archivo de película",
"OnGrab": "Al Capturar",
"OnRename": "Al Renombrar",
"OnUpgrade": "Al Mejorar La Calidad",
"OnRename": "En Renombrado",
"OnUpgrade": "Al Actualizar",
"ProxyCheckBadRequestMessage": "Fallo al comprobar el proxy. StatusCode: {0}",
"ProxyCheckFailedToTestMessage": "Fallo al comprobar el proxy: {0}",
"QualitySettingsSummary": "Tamaños de calidad y nombrado",
@@ -597,9 +597,5 @@
"RestartRequiredHelpTextWarning": "Requiere reiniciar para que surta efecto",
"AddList": "Añadir lista",
"RenameFiles": "Renombrar Archivos",
"Test": "Test",
"InstanceName": "Nombre de Instancia",
"InstanceNameHelpText": "Nombre de instancia en pestaña y para nombre de aplicación en Syslog",
"Database": "Base de Datos",
"ManualImportSelectEdition": "Importar Manualmente - Seleccionar Película"
"Test": "Test"
}

View File

@@ -20,11 +20,11 @@
"Analytics": "Analytiikka",
"AnalyticsEnabledHelpText": "Lähetä nimettömiä käyttö- ja virhetietoja sovelluksen palvelimille. Tämä sisältää tietoja selaimestasi, verkkokäyttöliittymän sivujen käytöstä, virheraportoinnista sekä käyttöjärjestelmästäsi ja versiosta. Käytämme näitä tietoja ominaisuuksien ja virhekorjauksien painotukseen.",
"AppDataDirectory": "AppData-kansio",
"ApplyTags": "Tunnistetoimenpide",
"ApplyTags": "Toimenpide tunnisteille",
"ApplyTagsHelpTexts1": "Miten tunnisteita sovelletaan valittuun kirjailijaan",
"ApplyTagsHelpTexts2": " 'Lisää' syötetyt tunnisteet aiempiin tunnisteisiin",
"ApplyTagsHelpTexts3": "- \"Poista\" tyhjentää syötetyt tunnisteet.",
"ApplyTagsHelpTexts4": "- \"Korvaa\" nykyiset tunnisteet syötetyillä tai tyhjennä kaikki tunnisteet jättämällä tyhjäksi.",
"ApplyTagsHelpTexts3": " 'Poista' ainoastaan syötetyt tunnisteet",
"ApplyTagsHelpTexts4": " 'Korvaa' kaikki aiemmat tunnisteet tai poista kaikki tunnisteet jättämällä tyhjäksi",
"Authentication": "Todennus",
"AuthenticationMethodHelpText": "Vaadi käyttäjätunnus ja salasana.",
"AuthorClickToChangeBook": "Vaihda kirjaa painamalla",
@@ -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,6 @@
"MonitorNewBooks": "Valvo uusia kirjoja",
"RenameFiles": "Uudelleennimeä tiedostot",
"Test": "Kokeile",
"AllowFingerprintingHelpText": "Tarkenna kirjojen täsmäystarkkuutta piiloleimauksen avulla.",
"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"
"AllowFingerprintingHelpText": "Käytä piiloleimausta kirjojen täsmäyksen tarkennukseen",
"AllowFingerprinting": "Salli piiloleimaus"
}

View File

@@ -666,15 +666,5 @@
"DefaultMonitorOptionHelpText": "Quels livres doivent être surveillés lors de l'ajout initial pour les auteurs détectés dans ce dossier",
"DefaultQualityProfileIdHelpText": "Profil de qualité par défaut pour les auteurs détectés dans ce dossier",
"AddMissing": "Ajouter les manquants",
"BookFileCountBookCountTotalTotalBookCountInterp": "{0} / {1} (Total : {2})",
"InstanceName": "Nom de l'instance",
"InstanceNameHelpText": "Nom de l'instance dans l'onglet du navigateur et pour le nom d'application dans Syslog",
"UseCalibreContentServer": "Serveur de contenu Calibre",
"DefaultTagsHelpText": "Profil de métadonnées par défaut pour les auteurs détectés dans ce dossier",
"MetadataProfiles": "profil de métadonnées",
"ManualImportSelectEdition": "Importation manuelle - Sélectionnez un film",
"Term": "Terme",
"MetadataProfile": "profil de métadonnées",
"StatusEndedContinuing": "Continuant",
"Database": "Base de données"
"BookFileCountBookCountTotalTotalBookCountInterp": "{0} / {1} (Total : {2})"
}

View File

@@ -568,7 +568,5 @@
"RenameFiles": "שנה את שם הקבצים",
"Test": "מִבְחָן",
"InstanceName": "שם מופע",
"RemotePathMappingCheckWrongOSPath": "אתה משתמש בדוקר; קליינט ההורדות {0} שם הורדות ב-{1} אבל הנתיב לא תקין {2}. בחן מחדש את ניתוב התיקיות והגדרות קליינט ההורדות.",
"ManualImportSelectEdition": "ייבוא ידני - בחר סרט",
"Database": "מסד נתונים"
"RemotePathMappingCheckWrongOSPath": "אתה משתמש בדוקר; קליינט ההורדות {0} שם הורדות ב-{1} אבל הנתיב לא תקין {2}. בחן מחדש את ניתוב התיקיות והגדרות קליינט ההורדות."
}

View File

@@ -556,6 +556,5 @@
"RenameFiles": "फ़ाइलों का नाम बदलें",
"RestartRequiredHelpTextWarning": "प्रभावी करने के लिए पुनरारंभ की आवश्यकता है",
"AddList": "सूची में जोड़ने",
"Test": "परीक्षा",
"ManualImportSelectEdition": "मैनुअल आयात - मूवी का चयन करें"
"Test": "परीक्षा"
}

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

@@ -556,6 +556,5 @@
"RestartRequiredHelpTextWarning": "Krefst endurræsingar til að taka gildi",
"AddList": "Bæta við lista",
"RenameFiles": "Endurnefna skrár",
"Test": "Próf",
"ManualImportSelectEdition": "Handvirkur innflutningur - Veldu kvikmynd"
"Test": "Próf"
}

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