mirror of
https://github.com/Readarr/Readarr.git
synced 2026-04-18 21:34:28 -04:00
Compare commits
1 Commits
notifiarr-
...
sonarr-pul
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
91049f874b |
6
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
6
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
@@ -5,9 +5,9 @@ body:
|
|||||||
- type: checkboxes
|
- type: checkboxes
|
||||||
attributes:
|
attributes:
|
||||||
label: Is there an existing issue for this?
|
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:
|
options:
|
||||||
- label: I have searched the existing open and closed issues
|
- label: I have searched the existing issues
|
||||||
required: true
|
required: true
|
||||||
- type: textarea
|
- type: textarea
|
||||||
attributes:
|
attributes:
|
||||||
@@ -42,14 +42,12 @@ body:
|
|||||||
- **Docker Install**: Yes
|
- **Docker Install**: Yes
|
||||||
- **Using Reverse Proxy**: No
|
- **Using Reverse Proxy**: No
|
||||||
- **Browser**: Firefox 90 (If UI related)
|
- **Browser**: Firefox 90 (If UI related)
|
||||||
- **Database**: Sqlite 3.36.0
|
|
||||||
value: |
|
value: |
|
||||||
- OS:
|
- OS:
|
||||||
- Readarr:
|
- Readarr:
|
||||||
- Docker Install:
|
- Docker Install:
|
||||||
- Using Reverse Proxy:
|
- Using Reverse Proxy:
|
||||||
- Browser:
|
- Browser:
|
||||||
- Database:
|
|
||||||
render: markdown
|
render: markdown
|
||||||
validations:
|
validations:
|
||||||
required: true
|
required: true
|
||||||
|
|||||||
4
.github/ISSUE_TEMPLATE/feature_request.yml
vendored
4
.github/ISSUE_TEMPLATE/feature_request.yml
vendored
@@ -5,9 +5,9 @@ body:
|
|||||||
- type: checkboxes
|
- type: checkboxes
|
||||||
attributes:
|
attributes:
|
||||||
label: Is there an existing issue for this?
|
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:
|
options:
|
||||||
- label: I have searched the existing open and closed issues
|
- label: I have searched the existing issues
|
||||||
required: true
|
required: true
|
||||||
- type: textarea
|
- type: textarea
|
||||||
attributes:
|
attributes:
|
||||||
|
|||||||
@@ -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
|
|
||||||
@@ -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.
|
|
||||||
@@ -730,7 +730,7 @@ stages:
|
|||||||
inputs:
|
inputs:
|
||||||
buildType: 'current'
|
buildType: 'current'
|
||||||
artifactName: Packages
|
artifactName: Packages
|
||||||
itemPattern: '**/$(pattern)'
|
itemPattern: '/$(pattern)'
|
||||||
targetPath: $(Build.ArtifactStagingDirectory)
|
targetPath: $(Build.ArtifactStagingDirectory)
|
||||||
- bash: |
|
- bash: |
|
||||||
mkdir -p ${BUILD_ARTIFACTSTAGINGDIRECTORY}/bin
|
mkdir -p ${BUILD_ARTIFACTSTAGINGDIRECTORY}/bin
|
||||||
@@ -1050,4 +1050,3 @@ stages:
|
|||||||
SYSTEM_ACCESSTOKEN: $(System.AccessToken)
|
SYSTEM_ACCESSTOKEN: $(System.AccessToken)
|
||||||
DISCORDCHANNELID: $(discordChannelId)
|
DISCORDCHANNELID: $(discordChannelId)
|
||||||
DISCORDWEBHOOKKEY: $(discordWebhookKey)
|
DISCORDWEBHOOKKEY: $(discordWebhookKey)
|
||||||
DISCORDTHREADID: $(discordThreadId)
|
|
||||||
|
|||||||
@@ -39,7 +39,6 @@ module.exports = {
|
|||||||
plugins: [
|
plugins: [
|
||||||
'filenames',
|
'filenames',
|
||||||
'react',
|
'react',
|
||||||
'react-hooks',
|
|
||||||
'simple-import-sort',
|
'simple-import-sort',
|
||||||
'import'
|
'import'
|
||||||
],
|
],
|
||||||
@@ -309,9 +308,7 @@ module.exports = {
|
|||||||
'react/react-in-jsx-scope': 2,
|
'react/react-in-jsx-scope': 2,
|
||||||
'react/self-closing-comp': 2,
|
'react/self-closing-comp': 2,
|
||||||
'react/sort-comp': 2,
|
'react/sort-comp': 2,
|
||||||
'react/jsx-wrap-multilines': 2,
|
'react/jsx-wrap-multilines': 2
|
||||||
'react-hooks/rules-of-hooks': 'error',
|
|
||||||
'react-hooks/exhaustive-deps': 'error'
|
|
||||||
},
|
},
|
||||||
overrides: [
|
overrides: [
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -12,9 +12,9 @@ import ModalBody from 'Components/Modal/ModalBody';
|
|||||||
import Portal from 'Components/Portal';
|
import Portal from 'Components/Portal';
|
||||||
import Scroller from 'Components/Scroller/Scroller';
|
import Scroller from 'Components/Scroller/Scroller';
|
||||||
import { icons, scrollDirections, sizes } from 'Helpers/Props';
|
import { icons, scrollDirections, sizes } from 'Helpers/Props';
|
||||||
import { isMobile as isMobileUtil } from 'Utilities/browser';
|
|
||||||
import * as keyCodes from 'Utilities/Constants/keyCodes';
|
import * as keyCodes from 'Utilities/Constants/keyCodes';
|
||||||
import getUniqueElememtId from 'Utilities/getUniqueElementId';
|
import getUniqueElememtId from 'Utilities/getUniqueElementId';
|
||||||
|
import { isMobile as isMobileUtil } from 'Utilities/mobile';
|
||||||
import HintedSelectInputOption from './HintedSelectInputOption';
|
import HintedSelectInputOption from './HintedSelectInputOption';
|
||||||
import HintedSelectInputSelectedValue from './HintedSelectInputSelectedValue';
|
import HintedSelectInputSelectedValue from './HintedSelectInputSelectedValue';
|
||||||
import TextInput from './TextInput';
|
import TextInput from './TextInput';
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ function ConfirmModal(props) {
|
|||||||
|
|
||||||
return () => unbindShortcut('enter', onConfirm);
|
return () => unbindShortcut('enter', onConfirm);
|
||||||
}
|
}
|
||||||
}, [bindShortcut, unbindShortcut, isOpen, onConfirm]);
|
}, [isOpen, onConfirm]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Modal
|
<Modal
|
||||||
|
|||||||
@@ -6,9 +6,9 @@ import ReactDOM from 'react-dom';
|
|||||||
import FocusLock from 'react-focus-lock';
|
import FocusLock from 'react-focus-lock';
|
||||||
import ErrorBoundary from 'Components/Error/ErrorBoundary';
|
import ErrorBoundary from 'Components/Error/ErrorBoundary';
|
||||||
import { sizes } from 'Helpers/Props';
|
import { sizes } from 'Helpers/Props';
|
||||||
import { isIOS } from 'Utilities/browser';
|
|
||||||
import * as keyCodes from 'Utilities/Constants/keyCodes';
|
import * as keyCodes from 'Utilities/Constants/keyCodes';
|
||||||
import getUniqueElememtId from 'Utilities/getUniqueElementId';
|
import getUniqueElememtId from 'Utilities/getUniqueElementId';
|
||||||
|
import { isIOS } from 'Utilities/mobile';
|
||||||
import { setScrollLock } from 'Utilities/scrollLock';
|
import { setScrollLock } from 'Utilities/scrollLock';
|
||||||
import ModalError from './ModalError';
|
import ModalError from './ModalError';
|
||||||
import styles from './Modal.css';
|
import styles from './Modal.css';
|
||||||
|
|||||||
@@ -1,12 +1,23 @@
|
|||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import React, { Component } from 'react';
|
import React, { Component } from 'react';
|
||||||
|
import OverlayScroller from 'Components/Scroller/OverlayScroller';
|
||||||
import Scroller from 'Components/Scroller/Scroller';
|
import Scroller from 'Components/Scroller/Scroller';
|
||||||
import { scrollDirections } from 'Helpers/Props';
|
import { scrollDirections } from 'Helpers/Props';
|
||||||
|
import { isMobile as isMobileUtil } from 'Utilities/mobile';
|
||||||
import { isLocked } from 'Utilities/scrollLock';
|
import { isLocked } from 'Utilities/scrollLock';
|
||||||
import styles from './PageContentBody.css';
|
import styles from './PageContentBody.css';
|
||||||
|
|
||||||
class PageContentBody extends Component {
|
class PageContentBody extends Component {
|
||||||
|
|
||||||
|
//
|
||||||
|
// Lifecycle
|
||||||
|
|
||||||
|
constructor(props, context) {
|
||||||
|
super(props, context);
|
||||||
|
|
||||||
|
this._isMobile = isMobileUtil();
|
||||||
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
// Listeners
|
// Listeners
|
||||||
|
|
||||||
@@ -30,8 +41,10 @@ class PageContentBody extends Component {
|
|||||||
...otherProps
|
...otherProps
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
|
||||||
|
const ScrollerComponent = this._isMobile ? Scroller : OverlayScroller;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Scroller
|
<ScrollerComponent
|
||||||
className={className}
|
className={className}
|
||||||
scrollDirection={scrollDirections.VERTICAL}
|
scrollDirection={scrollDirections.VERTICAL}
|
||||||
{...otherProps}
|
{...otherProps}
|
||||||
@@ -40,7 +53,7 @@ class PageContentBody extends Component {
|
|||||||
<div className={innerClassName}>
|
<div className={innerClassName}>
|
||||||
{children}
|
{children}
|
||||||
</div>
|
</div>
|
||||||
</Scroller>
|
</ScrollerComponent>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
.jumpBar {
|
.jumpBar {
|
||||||
z-index: $pageJumpBarZIndex;
|
|
||||||
display: flex;
|
display: flex;
|
||||||
align-content: stretch;
|
align-content: stretch;
|
||||||
align-items: stretch;
|
align-items: stretch;
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import React, { Component } from 'react';
|
import React, { Component } from 'react';
|
||||||
import { Scrollbars } from 'react-custom-scrollbars-2';
|
import { Scrollbars } from 'react-custom-scrollbars';
|
||||||
import { scrollDirections } from 'Helpers/Props';
|
import { scrollDirections } from 'Helpers/Props';
|
||||||
import styles from './OverlayScroller.css';
|
import styles from './OverlayScroller.css';
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import React, { Component } from 'react';
|
import React, { Component } from 'react';
|
||||||
import Measure from 'Components/Measure';
|
import Measure from 'Components/Measure';
|
||||||
import { isMobile as isMobileUtil } from 'Utilities/browser';
|
import { isMobile as isMobileUtil } from 'Utilities/mobile';
|
||||||
import styles from './SwipeHeader.css';
|
import styles from './SwipeHeader.css';
|
||||||
|
|
||||||
function cursorPosition(event) {
|
function cursorPosition(event) {
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import { Manager, Popper, Reference } from 'react-popper';
|
|||||||
import Portal from 'Components/Portal';
|
import Portal from 'Components/Portal';
|
||||||
import { kinds, tooltipPositions } from 'Helpers/Props';
|
import { kinds, tooltipPositions } from 'Helpers/Props';
|
||||||
import dimensions from 'Styles/Variables/dimensions';
|
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';
|
import styles from './Tooltip.css';
|
||||||
|
|
||||||
let maxWidth = null;
|
let maxWidth = null;
|
||||||
|
|||||||
@@ -148,10 +148,11 @@ class AddNewItem extends Component {
|
|||||||
);
|
);
|
||||||
} else if (item.book) {
|
} else if (item.book) {
|
||||||
const book = item.book;
|
const book = item.book;
|
||||||
|
const edition = book.editions.find((x) => x.monitored);
|
||||||
return (
|
return (
|
||||||
<AddNewBookSearchResultConnector
|
<AddNewBookSearchResultConnector
|
||||||
key={item.id}
|
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}
|
isExistingAuthor={'id' in book.author && book.author.id !== 0}
|
||||||
{...book}
|
{...book}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -138,20 +138,17 @@ class AddNewBookSearchResult extends Component {
|
|||||||
null
|
null
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
<Link
|
||||||
editions && editions.length > 1 ?
|
className={styles.mbLink}
|
||||||
<Link
|
to={`https://goodreads.com/book/show/${editions[0].foreignEditionId}`}
|
||||||
className={styles.mbLink}
|
onPress={this.onTVDBLinkPress}
|
||||||
to={`https://goodreads.com/book/show/${editions[0].foreignEditionId}`}
|
>
|
||||||
onPress={this.onTVDBLinkPress}
|
<Icon
|
||||||
>
|
className={styles.mbLinkIcon}
|
||||||
<Icon
|
name={icons.EXTERNAL_LINK}
|
||||||
className={styles.mbLinkIcon}
|
size={28}
|
||||||
name={icons.EXTERNAL_LINK}
|
/>
|
||||||
size={28}
|
</Link>
|
||||||
/>
|
|
||||||
</Link> : null
|
|
||||||
}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -221,7 +218,7 @@ AddNewBookSearchResult.propTypes = {
|
|||||||
overview: PropTypes.string,
|
overview: PropTypes.string,
|
||||||
ratings: PropTypes.object.isRequired,
|
ratings: PropTypes.object.isRequired,
|
||||||
author: PropTypes.object,
|
author: PropTypes.object,
|
||||||
editions: PropTypes.arrayOf(PropTypes.object),
|
editions: PropTypes.arrayOf(PropTypes.object).isRequired,
|
||||||
images: PropTypes.arrayOf(PropTypes.object).isRequired,
|
images: PropTypes.arrayOf(PropTypes.object).isRequired,
|
||||||
isExistingBook: PropTypes.bool.isRequired,
|
isExistingBook: PropTypes.bool.isRequired,
|
||||||
isExistingAuthor: PropTypes.bool.isRequired,
|
isExistingAuthor: PropTypes.bool.isRequired,
|
||||||
|
|||||||
@@ -173,7 +173,7 @@ class AddAuthorOptionsForm extends Component {
|
|||||||
AddAuthorOptionsForm.propTypes = {
|
AddAuthorOptionsForm.propTypes = {
|
||||||
rootFolderPath: PropTypes.object,
|
rootFolderPath: PropTypes.object,
|
||||||
monitor: PropTypes.object.isRequired,
|
monitor: PropTypes.object.isRequired,
|
||||||
monitorNewItems: PropTypes.object.isRequired,
|
monitorNewItems: PropTypes.string.isRequired,
|
||||||
qualityProfileId: PropTypes.object,
|
qualityProfileId: PropTypes.object,
|
||||||
metadataProfileId: PropTypes.object,
|
metadataProfileId: PropTypes.object,
|
||||||
showMetadataProfile: PropTypes.bool.isRequired,
|
showMetadataProfile: PropTypes.bool.isRequired,
|
||||||
|
|||||||
@@ -58,7 +58,6 @@ function HostSettings(props) {
|
|||||||
name="port"
|
name="port"
|
||||||
min={1}
|
min={1}
|
||||||
max={65535}
|
max={65535}
|
||||||
autocomplete="off"
|
|
||||||
helpTextWarning={translate('PortHelpTextWarning')}
|
helpTextWarning={translate('PortHelpTextWarning')}
|
||||||
onChange={onInputChange}
|
onChange={onInputChange}
|
||||||
{...port}
|
{...port}
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ function PendingChangesModal(props) {
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
bindShortcut('enter', onConfirm);
|
bindShortcut('enter', onConfirm);
|
||||||
}, [bindShortcut, onConfirm]);
|
}, [onConfirm]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Modal
|
<Modal
|
||||||
|
|||||||
@@ -1,7 +1,4 @@
|
|||||||
@define-mixin scrollbar {
|
@define-mixin scrollbar {
|
||||||
scrollbar-color: var(--scrollbarBackgroundColor) transparent;
|
|
||||||
scrollbar-width: thin;
|
|
||||||
|
|
||||||
&::-webkit-scrollbar {
|
&::-webkit-scrollbar {
|
||||||
width: 10px;
|
width: 10px;
|
||||||
height: 10px;
|
height: 10px;
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
module.exports = {
|
module.exports = {
|
||||||
pageJumpBarZIndex: 10,
|
|
||||||
modalZIndex: 1000,
|
modalZIndex: 1000,
|
||||||
popperZIndex: 2000
|
popperZIndex: 2000
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ class About extends Component {
|
|||||||
packageVersion,
|
packageVersion,
|
||||||
packageAuthor,
|
packageAuthor,
|
||||||
isNetCore,
|
isNetCore,
|
||||||
|
isMono,
|
||||||
isDocker,
|
isDocker,
|
||||||
runtimeVersion,
|
runtimeVersion,
|
||||||
migrationVersion,
|
migrationVersion,
|
||||||
@@ -49,6 +50,14 @@ class About extends Component {
|
|||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
isMono &&
|
||||||
|
<DescriptionListItem
|
||||||
|
title={translate('MonoVersion')}
|
||||||
|
data={runtimeVersion}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
isNetCore &&
|
isNetCore &&
|
||||||
<DescriptionListItem
|
<DescriptionListItem
|
||||||
@@ -112,6 +121,7 @@ About.propTypes = {
|
|||||||
packageVersion: PropTypes.string,
|
packageVersion: PropTypes.string,
|
||||||
packageAuthor: PropTypes.string,
|
packageAuthor: PropTypes.string,
|
||||||
isNetCore: PropTypes.bool.isRequired,
|
isNetCore: PropTypes.bool.isRequired,
|
||||||
|
isMono: PropTypes.bool.isRequired,
|
||||||
runtimeVersion: PropTypes.string.isRequired,
|
runtimeVersion: PropTypes.string.isRequired,
|
||||||
isDocker: PropTypes.bool.isRequired,
|
isDocker: PropTypes.bool.isRequired,
|
||||||
migrationVersion: PropTypes.number.isRequired,
|
migrationVersion: PropTypes.number.isRequired,
|
||||||
|
|||||||
@@ -10,7 +10,3 @@ export function isMobile() {
|
|||||||
export function isIOS() {
|
export function isIOS() {
|
||||||
return mobileDetect.is('iOS');
|
return mobileDetect.is('iOS');
|
||||||
}
|
}
|
||||||
|
|
||||||
export function isFirefox() {
|
|
||||||
return window.navigator.userAgent.toLowerCase().indexOf('firefox/') >= 0;
|
|
||||||
}
|
|
||||||
@@ -58,7 +58,7 @@
|
|||||||
"react-addons-shallow-compare": "15.6.3",
|
"react-addons-shallow-compare": "15.6.3",
|
||||||
"react-async-script": "1.2.0",
|
"react-async-script": "1.2.0",
|
||||||
"react-autosuggest": "10.1.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": "14.0.2",
|
||||||
"react-dnd-html5-backend": "14.0.0",
|
"react-dnd-html5-backend": "14.0.0",
|
||||||
"react-dnd-multi-backend": "6.0.2",
|
"react-dnd-multi-backend": "6.0.2",
|
||||||
@@ -109,7 +109,6 @@
|
|||||||
"eslint-plugin-filenames": "1.3.2",
|
"eslint-plugin-filenames": "1.3.2",
|
||||||
"eslint-plugin-import": "2.23.4",
|
"eslint-plugin-import": "2.23.4",
|
||||||
"eslint-plugin-react": "7.24.0",
|
"eslint-plugin-react": "7.24.0",
|
||||||
"eslint-plugin-react-hooks": "4.6.0",
|
|
||||||
"eslint-plugin-simple-import-sort": "7.0.0",
|
"eslint-plugin-simple-import-sort": "7.0.0",
|
||||||
"esprint": "3.1.0",
|
"esprint": "3.1.0",
|
||||||
"file-loader": "6.2.0",
|
"file-loader": "6.2.0",
|
||||||
@@ -135,5 +134,8 @@
|
|||||||
"webpack-cli": "4.7.2",
|
"webpack-cli": "4.7.2",
|
||||||
"webpack-livereload-plugin": "3.0.1",
|
"webpack-livereload-plugin": "3.0.1",
|
||||||
"worker-loader": "3.0.8"
|
"worker-loader": "3.0.8"
|
||||||
|
},
|
||||||
|
"volta": {
|
||||||
|
"node": "16.17.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -437,6 +437,24 @@ namespace NzbDrone.Common.Test.DiskTests
|
|||||||
Assert.Throws<IOException>(() => Subject.TransferFolder(source.FullName, destination.FullName, TransferMode.Copy));
|
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]
|
[Test]
|
||||||
public void CopyFolder_should_ignore_nfs_temp_file()
|
public void CopyFolder_should_ignore_nfs_temp_file()
|
||||||
{
|
{
|
||||||
@@ -522,6 +540,26 @@ namespace NzbDrone.Common.Test.DiskTests
|
|||||||
source.FullName.GetActualCasing().Should().Be(destination.FullName);
|
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]
|
[Test]
|
||||||
public void should_throw_if_destination_is_readonly()
|
public void should_throw_if_destination_is_readonly()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -207,7 +207,6 @@ namespace NzbDrone.Common.Test.Http
|
|||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
[Platform(Exclude = "MacOsX", Reason = "Azure agent update prevents brotli on OSX")]
|
|
||||||
public void should_execute_get_using_brotli()
|
public void should_execute_get_using_brotli()
|
||||||
{
|
{
|
||||||
var request = new HttpRequest($"https://{_httpBinHost}/brotli");
|
var request = new HttpRequest($"https://{_httpBinHost}/brotli");
|
||||||
@@ -308,6 +307,11 @@ namespace NzbDrone.Common.Test.Http
|
|||||||
[Test]
|
[Test]
|
||||||
public void should_follow_redirects_to_https()
|
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")
|
var request = new HttpRequestBuilder($"https://{_httpBinHost}/redirect-to")
|
||||||
.AddQueryParam("url", $"https://readarr.com/")
|
.AddQueryParam("url", $"https://readarr.com/")
|
||||||
.Build();
|
.Build();
|
||||||
|
|||||||
@@ -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("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("/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;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
|
// Announce URLs (passkeys) Magnet & Tracker
|
||||||
[TestCase(@"magnet_uri"":""magnet:?xt=urn:btih:9pr04sgkillroyimaveql2tyu8xyui&dn=&tr=https%3a%2f%2fxxx.yyy%2f9pr04sg601233210imaveql2tyu8xyui%2fannounce""}")]
|
[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"":""https://xxx.yyy/announce.php?passkey=9pr04sg601233210imaveql2tyu8xyui""}")]
|
||||||
[TestCase(@"tracker"":""http://xxx.yyy/announce.php?passkey=9pr04sg601233210imaveql2tyu8xyui"",""info"":""http://xxx.yyy/info?a=b""")]
|
[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")]
|
[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)
|
public void should_clean_message(string message)
|
||||||
{
|
{
|
||||||
var cleansedMessage = CleanseLogMessage.Cleanse(message);
|
var cleansedMessage = CleanseLogMessage.Cleanse(message);
|
||||||
|
|
||||||
cleansedMessage.Should().NotContain("mySecret");
|
cleansedMessage.Should().NotContain("mySecret");
|
||||||
cleansedMessage.Should().NotContain("123%@%_@!#^#@");
|
|
||||||
cleansedMessage.Should().NotContain("01233210");
|
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
|
//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(@"{""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")]
|
[TestCase(@"https://www.goodreads.com/series/311911?key=1234530f422f4aacb6b301233210aaaa&_nc=1&format=xml")]
|
||||||
|
|||||||
@@ -279,7 +279,7 @@ namespace NzbDrone.Common.Test
|
|||||||
[Test]
|
[Test]
|
||||||
public void GetUpdateClientExePath()
|
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]
|
[Test]
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.ComponentModel;
|
using System.ComponentModel;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
@@ -171,7 +171,7 @@ namespace NzbDrone.Common.Test
|
|||||||
var processStarted = new ManualResetEventSlim();
|
var processStarted = new ManualResetEventSlim();
|
||||||
|
|
||||||
string suffix;
|
string suffix;
|
||||||
if (OsInfo.IsWindows)
|
if (OsInfo.IsWindows || PlatformInfo.IsMono)
|
||||||
{
|
{
|
||||||
suffix = ".exe";
|
suffix = ".exe";
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,17 @@
|
|||||||
using System;
|
using System;
|
||||||
|
using System.Reflection;
|
||||||
|
using System.Text.RegularExpressions;
|
||||||
|
using Microsoft.Win32;
|
||||||
|
|
||||||
namespace NzbDrone.Common.EnvironmentInfo
|
namespace NzbDrone.Common.EnvironmentInfo
|
||||||
{
|
{
|
||||||
|
public enum PlatformType
|
||||||
|
{
|
||||||
|
DotNet = 0,
|
||||||
|
Mono = 1,
|
||||||
|
NetCore = 2
|
||||||
|
}
|
||||||
|
|
||||||
public interface IPlatformInfo
|
public interface IPlatformInfo
|
||||||
{
|
{
|
||||||
Version Version { get; }
|
Version Version { get; }
|
||||||
@@ -9,18 +19,38 @@ namespace NzbDrone.Common.EnvironmentInfo
|
|||||||
|
|
||||||
public class PlatformInfo : IPlatformInfo
|
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;
|
private static Version _version;
|
||||||
|
|
||||||
static PlatformInfo()
|
static PlatformInfo()
|
||||||
{
|
{
|
||||||
|
_platform = PlatformType.NetCore;
|
||||||
_version = Environment.Version;
|
_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
|
public static string PlatformName
|
||||||
{
|
{
|
||||||
get
|
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;
|
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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -240,9 +240,9 @@ namespace NzbDrone.Common.Extensions
|
|||||||
return null;
|
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";
|
processName += ".exe";
|
||||||
}
|
}
|
||||||
@@ -250,6 +250,11 @@ namespace NzbDrone.Common.Extensions
|
|||||||
return processName;
|
return processName;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static string ProcessNameToExe(this string processName)
|
||||||
|
{
|
||||||
|
return processName.ProcessNameToExe(PlatformInfo.Platform);
|
||||||
|
}
|
||||||
|
|
||||||
public static string GetLongestCommonPath(this List<string> paths)
|
public static string GetLongestCommonPath(this List<string> paths)
|
||||||
{
|
{
|
||||||
var firstPath = paths.First();
|
var firstPath = paths.First();
|
||||||
@@ -342,9 +347,9 @@ namespace NzbDrone.Common.Extensions
|
|||||||
return Path.Combine(GetUpdatePackageFolder(appFolderInfo), UPDATE_CLIENT_FOLDER_NAME);
|
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)
|
public static string GetDatabase(this IAppFolderInfo appFolderInfo)
|
||||||
|
|||||||
@@ -55,8 +55,7 @@ namespace NzbDrone.Common.Http
|
|||||||
StatusCode == HttpStatusCode.Found ||
|
StatusCode == HttpStatusCode.Found ||
|
||||||
StatusCode == HttpStatusCode.TemporaryRedirect ||
|
StatusCode == HttpStatusCode.TemporaryRedirect ||
|
||||||
StatusCode == HttpStatusCode.RedirectMethod ||
|
StatusCode == HttpStatusCode.RedirectMethod ||
|
||||||
StatusCode == HttpStatusCode.SeeOther ||
|
StatusCode == HttpStatusCode.SeeOther;
|
||||||
StatusCode == HttpStatusCode.PermanentRedirect;
|
|
||||||
|
|
||||||
public string[] GetCookieHeaders()
|
public string[] GetCookieHeaders()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ namespace NzbDrone.Common.Instrumentation
|
|||||||
new Regex(@"iptorrents\.com/[/a-z0-9?&;]*?(?:[?&;](u|tp)=(?<secret>[^&=;]+?))+(?= |;|&|$)", RegexOptions.Compiled | RegexOptions.IgnoreCase),
|
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(@"/fetch/[a-z0-9]{32}/(?<secret>[a-z0-9]{32})", RegexOptions.Compiled),
|
||||||
new Regex(@"getnzb.*?(?<=\?|&)(r)=(?<secret>[^&=]+?)(?= |&|$)", RegexOptions.Compiled | RegexOptions.IgnoreCase),
|
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
|
// 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"),
|
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
|
// Webhooks
|
||||||
// Notifiarr
|
// Notifiarr
|
||||||
new Regex(@"api/v[0-9]/notification/readarr/(?<secret>[\w-]+)", RegexOptions.Compiled | RegexOptions.IgnoreCase),
|
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)
|
|
||||||
};
|
};
|
||||||
|
|
||||||
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);
|
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);
|
||||||
|
|||||||
@@ -38,6 +38,16 @@ namespace NzbDrone.Common.Instrumentation
|
|||||||
return;
|
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);
|
Console.WriteLine("EPIC FAIL: {0}", exception);
|
||||||
Logger.Fatal(exception, "EPIC FAIL.");
|
Logger.Fatal(exception, "EPIC FAIL.");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -109,6 +109,13 @@ namespace NzbDrone.Common.Instrumentation.Sentry
|
|||||||
o.Debug = false;
|
o.Debug = false;
|
||||||
o.DiagnosticLevel = SentryLevel.Debug;
|
o.DiagnosticLevel = SentryLevel.Debug;
|
||||||
o.Release = BuildInfo.Release;
|
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.BeforeSend = x => SentryCleanser.CleanseEvent(x);
|
||||||
o.BeforeBreadcrumb = x => SentryCleanser.CleanseBreadcrumb(x);
|
o.BeforeBreadcrumb = x => SentryCleanser.CleanseBreadcrumb(x);
|
||||||
o.Environment = BuildInfo.Branch;
|
o.Environment = BuildInfo.Branch;
|
||||||
@@ -151,6 +158,14 @@ namespace NzbDrone.Common.Instrumentation.Sentry
|
|||||||
SentrySdk.ConfigureScope(scope =>
|
SentrySdk.ConfigureScope(scope =>
|
||||||
{
|
{
|
||||||
scope.SetTag("is_docker", $"{osInfo.IsDocker}");
|
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();
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -377,6 +377,11 @@ namespace NzbDrone.Common.Processes
|
|||||||
|
|
||||||
private (string Path, string Args) GetPathAndArgs(string path, string args)
|
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))
|
if (OsInfo.IsWindows && path.EndsWith(".bat", StringComparison.InvariantCultureIgnoreCase))
|
||||||
{
|
{
|
||||||
return ("cmd.exe", $"/c {path} {args}");
|
return ("cmd.exe", $"/c {path} {args}");
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ namespace NzbDrone.Core.Test.IndexerSearchTests
|
|||||||
.Setup(s => s.GetAuthor(It.IsAny<int>()))
|
.Setup(s => s.GetAuthor(It.IsAny<int>()))
|
||||||
.Returns(_author);
|
.Returns(_author);
|
||||||
|
|
||||||
Mocker.GetMock<ISearchForReleases>()
|
Mocker.GetMock<ISearchForNzb>()
|
||||||
.Setup(s => s.AuthorSearch(_author.Id, false, true, false))
|
.Setup(s => s.AuthorSearch(_author.Id, false, true, false))
|
||||||
.Returns(new List<DownloadDecision>());
|
.Returns(new List<DownloadDecision>());
|
||||||
|
|
||||||
@@ -45,7 +45,7 @@ namespace NzbDrone.Core.Test.IndexerSearchTests
|
|||||||
|
|
||||||
Subject.Execute(new AuthorSearchCommand { AuthorId = _author.Id, Trigger = CommandTrigger.Manual });
|
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),
|
.Verify(v => v.AuthorSearch(_author.Id, false, true, false),
|
||||||
Times.Exactly(_author.Books.Value.Count(s => s.Monitored)));
|
Times.Exactly(_author.Books.Value.Count(s => s.Monitored)));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,6 +16,11 @@ namespace NzbDrone.Core.Test.MediaCoverTests
|
|||||||
[SetUp]
|
[SetUp]
|
||||||
public void SetUp()
|
public void SetUp()
|
||||||
{
|
{
|
||||||
|
if (PlatformInfo.IsMono && PlatformInfo.GetVersion() < new Version(5, 8))
|
||||||
|
{
|
||||||
|
Assert.Inconclusive("Not supported on Mono < 5.8");
|
||||||
|
}
|
||||||
|
|
||||||
Mocker.GetMock<IDiskProvider>()
|
Mocker.GetMock<IDiskProvider>()
|
||||||
.Setup(v => v.FileExists(It.IsAny<string>()))
|
.Setup(v => v.FileExists(It.IsAny<string>()))
|
||||||
.Returns<string>(s => File.Exists(s));
|
.Returns<string>(s => File.Exists(s));
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ using NzbDrone.Core.Test.Framework;
|
|||||||
namespace NzbDrone.Core.Test.ParserTests
|
namespace NzbDrone.Core.Test.ParserTests
|
||||||
{
|
{
|
||||||
[TestFixture]
|
[TestFixture]
|
||||||
|
|
||||||
public class QualityParserFixture : CoreTest
|
public class QualityParserFixture : CoreTest
|
||||||
{
|
{
|
||||||
public static object[] SelfQualityParserCases =
|
public static object[] SelfQualityParserCases =
|
||||||
|
|||||||
@@ -69,7 +69,7 @@ namespace NzbDrone.Core.Test.UpdateTests
|
|||||||
.Returns(true);
|
.Returns(true);
|
||||||
|
|
||||||
Mocker.GetMock<IDiskProvider>()
|
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);
|
.Returns(true);
|
||||||
|
|
||||||
_sandboxFolder = Mocker.GetMock<IAppFolderInfo>().Object.GetUpdateSandboxFolder();
|
_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()
|
public void should_return_with_warning_if_updater_doesnt_exists()
|
||||||
{
|
{
|
||||||
Mocker.GetMock<IDiskProvider>()
|
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);
|
.Returns(false);
|
||||||
|
|
||||||
Subject.Execute(new ApplicationUpdateCommand());
|
Subject.Execute(new ApplicationUpdateCommand());
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Data.Common;
|
using System.Data.Common;
|
||||||
using System.Data.SQLite;
|
using System.Data.SQLite;
|
||||||
using System.Net.Sockets;
|
|
||||||
using NLog;
|
using NLog;
|
||||||
using Npgsql;
|
using Npgsql;
|
||||||
using NzbDrone.Common.Disk;
|
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);
|
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)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
throw new ReadarrStartupException(e, "Error creating main database");
|
throw new ReadarrStartupException(e, "Error creating main database");
|
||||||
|
|||||||
@@ -88,8 +88,6 @@ namespace NzbDrone.Core.Download.Clients.QBittorrent
|
|||||||
|
|
||||||
if (!addHasSetShareLimits && setShareLimits)
|
if (!addHasSetShareLimits && setShareLimits)
|
||||||
{
|
{
|
||||||
Proxy.SetTorrentSeedingConfiguration(hash.ToLower(), remoteBook.SeedConfiguration, Settings);
|
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
Proxy.SetTorrentSeedingConfiguration(hash.ToLower(), remoteBook.SeedConfiguration, Settings);
|
Proxy.SetTorrentSeedingConfiguration(hash.ToLower(), remoteBook.SeedConfiguration, Settings);
|
||||||
@@ -288,8 +286,7 @@ namespace NzbDrone.Core.Download.Clients.QBittorrent
|
|||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case "forcedDL": // torrent is being downloaded, and was forced started
|
case "forcedDL": //torrent is being downloaded, and was forced started
|
||||||
case "forcedMetaDL": // torrent metadata is being forcibly downloaded
|
|
||||||
case "moving": // torrent is being moved from a folder
|
case "moving": // torrent is being moved from a folder
|
||||||
case "downloading": // torrent is being downloaded and data is being transferred
|
case "downloading": // torrent is being downloaded and data is being transferred
|
||||||
item.Status = DownloadItemStatus.Downloading;
|
item.Status = DownloadItemStatus.Downloading;
|
||||||
|
|||||||
@@ -142,8 +142,20 @@ namespace NzbDrone.Core.Download.Clients.QBittorrent
|
|||||||
var request = BuildRequest(settings).Resource("/api/v2/torrents/add")
|
var request = BuildRequest(settings).Resource("/api/v2/torrents/add")
|
||||||
.Post()
|
.Post()
|
||||||
.AddFormParameter("urls", torrentUrl);
|
.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)
|
if (seedConfiguration != null)
|
||||||
{
|
{
|
||||||
@@ -165,7 +177,20 @@ namespace NzbDrone.Core.Download.Clients.QBittorrent
|
|||||||
.Post()
|
.Post()
|
||||||
.AddFormUpload("torrents", fileName, fileContent);
|
.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)
|
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)
|
public void SetTorrentSeedingConfiguration(string hash, TorrentSeedConfiguration seedConfiguration, QBittorrentSettings settings)
|
||||||
{
|
{
|
||||||
var request = BuildRequest(settings).Resource("/api/v2/torrents/setShareLimits")
|
var request = BuildRequest(settings).Resource("/api/v2/torrents/setShareLimits")
|
||||||
|
|||||||
@@ -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")]
|
[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; }
|
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()
|
public NzbDroneValidationResult Validate()
|
||||||
{
|
{
|
||||||
return new NzbDroneValidationResult(Validator.Validate(this));
|
return new NzbDroneValidationResult(Validator.Validate(this));
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ namespace NzbDrone.Core.Download.Clients.QBittorrent
|
|||||||
|
|
||||||
public double Progress { get; set; } // Torrent progress (%/100)
|
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
|
public string State { get; set; } // Torrent state. See possible values here below
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
using NzbDrone.Common.Disk;
|
using NzbDrone.Common.Disk;
|
||||||
using NzbDrone.Core.Download.Clients.Sabnzbd.JsonConverters;
|
using NzbDrone.Core.Download.Clients.Sabnzbd.JsonConverters;
|
||||||
@@ -7,14 +7,10 @@ namespace NzbDrone.Core.Download.Clients.Sabnzbd
|
|||||||
{
|
{
|
||||||
public class SabnzbdConfig
|
public class SabnzbdConfig
|
||||||
{
|
{
|
||||||
public SabnzbdConfig()
|
|
||||||
{
|
|
||||||
Categories = new List<SabnzbdCategory>();
|
|
||||||
Servers = new List<object>();
|
|
||||||
}
|
|
||||||
|
|
||||||
public SabnzbdConfigMisc Misc { get; set; }
|
public SabnzbdConfigMisc Misc { get; set; }
|
||||||
|
|
||||||
public List<SabnzbdCategory> Categories { get; set; }
|
public List<SabnzbdCategory> Categories { get; set; }
|
||||||
|
|
||||||
public List<object> Servers { get; set; }
|
public List<object> Servers { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -52,7 +52,7 @@ namespace NzbDrone.Core.HealthCheck
|
|||||||
.AddQueryParam("version", BuildInfo.Version)
|
.AddQueryParam("version", BuildInfo.Version)
|
||||||
.AddQueryParam("os", OsInfo.Os.ToString().ToLowerInvariant())
|
.AddQueryParam("os", OsInfo.Os.ToString().ToLowerInvariant())
|
||||||
.AddQueryParam("arch", RuntimeInformation.OSArchitecture)
|
.AddQueryParam("arch", RuntimeInformation.OSArchitecture)
|
||||||
.AddQueryParam("runtime", "netcore")
|
.AddQueryParam("runtime", PlatformInfo.Platform.ToString().ToLowerInvariant())
|
||||||
.AddQueryParam("branch", _configFileProvider.Branch)
|
.AddQueryParam("branch", _configFileProvider.Branch)
|
||||||
.Build();
|
.Build();
|
||||||
try
|
try
|
||||||
|
|||||||
@@ -7,22 +7,22 @@ namespace NzbDrone.Core.IndexerSearch
|
|||||||
{
|
{
|
||||||
public class AuthorSearchService : IExecute<AuthorSearchCommand>
|
public class AuthorSearchService : IExecute<AuthorSearchCommand>
|
||||||
{
|
{
|
||||||
private readonly ISearchForReleases _releaseSearchService;
|
private readonly ISearchForNzb _nzbSearchService;
|
||||||
private readonly IProcessDownloadDecisions _processDownloadDecisions;
|
private readonly IProcessDownloadDecisions _processDownloadDecisions;
|
||||||
private readonly Logger _logger;
|
private readonly Logger _logger;
|
||||||
|
|
||||||
public AuthorSearchService(ISearchForReleases releaseSearchService,
|
public AuthorSearchService(ISearchForNzb nzbSearchService,
|
||||||
IProcessDownloadDecisions processDownloadDecisions,
|
IProcessDownloadDecisions processDownloadDecisions,
|
||||||
Logger logger)
|
Logger logger)
|
||||||
{
|
{
|
||||||
_releaseSearchService = releaseSearchService;
|
_nzbSearchService = nzbSearchService;
|
||||||
_processDownloadDecisions = processDownloadDecisions;
|
_processDownloadDecisions = processDownloadDecisions;
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Execute(AuthorSearchCommand message)
|
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);
|
var processed = _processDownloadDecisions.ProcessDecisions(decisions);
|
||||||
|
|
||||||
_logger.ProgressInfo("Author search completed. {0} reports downloaded.", processed.Grabbed.Count);
|
_logger.ProgressInfo("Author search completed. {0} reports downloaded.", processed.Grabbed.Count);
|
||||||
|
|||||||
@@ -17,21 +17,21 @@ namespace NzbDrone.Core.IndexerSearch
|
|||||||
IExecute<MissingBookSearchCommand>,
|
IExecute<MissingBookSearchCommand>,
|
||||||
IExecute<CutoffUnmetBookSearchCommand>
|
IExecute<CutoffUnmetBookSearchCommand>
|
||||||
{
|
{
|
||||||
private readonly ISearchForReleases _releaseSearchService;
|
private readonly ISearchForNzb _nzbSearchService;
|
||||||
private readonly IBookService _bookService;
|
private readonly IBookService _bookService;
|
||||||
private readonly IBookCutoffService _bookCutoffService;
|
private readonly IBookCutoffService _bookCutoffService;
|
||||||
private readonly IQueueService _queueService;
|
private readonly IQueueService _queueService;
|
||||||
private readonly IProcessDownloadDecisions _processDownloadDecisions;
|
private readonly IProcessDownloadDecisions _processDownloadDecisions;
|
||||||
private readonly Logger _logger;
|
private readonly Logger _logger;
|
||||||
|
|
||||||
public BookSearchService(ISearchForReleases releaseSearchService,
|
public BookSearchService(ISearchForNzb nzbSearchService,
|
||||||
IBookService bookService,
|
IBookService bookService,
|
||||||
IBookCutoffService bookCutoffService,
|
IBookCutoffService bookCutoffService,
|
||||||
IQueueService queueService,
|
IQueueService queueService,
|
||||||
IProcessDownloadDecisions processDownloadDecisions,
|
IProcessDownloadDecisions processDownloadDecisions,
|
||||||
Logger logger)
|
Logger logger)
|
||||||
{
|
{
|
||||||
_releaseSearchService = releaseSearchService;
|
_nzbSearchService = nzbSearchService;
|
||||||
_bookService = bookService;
|
_bookService = bookService;
|
||||||
_bookCutoffService = bookCutoffService;
|
_bookCutoffService = bookCutoffService;
|
||||||
_queueService = queueService;
|
_queueService = queueService;
|
||||||
@@ -47,7 +47,7 @@ namespace NzbDrone.Core.IndexerSearch
|
|||||||
foreach (var book in books)
|
foreach (var book in books)
|
||||||
{
|
{
|
||||||
List<DownloadDecision> decisions;
|
List<DownloadDecision> decisions;
|
||||||
decisions = _releaseSearchService.BookSearch(book.Id, false, userInvokedSearch, false);
|
decisions = _nzbSearchService.BookSearch(book.Id, false, userInvokedSearch, false);
|
||||||
var processed = _processDownloadDecisions.ProcessDecisions(decisions);
|
var processed = _processDownloadDecisions.ProcessDecisions(decisions);
|
||||||
|
|
||||||
downloadedCount += processed.Grabbed.Count;
|
downloadedCount += processed.Grabbed.Count;
|
||||||
@@ -61,7 +61,7 @@ namespace NzbDrone.Core.IndexerSearch
|
|||||||
foreach (var bookId in message.BookIds)
|
foreach (var bookId in message.BookIds)
|
||||||
{
|
{
|
||||||
var decisions =
|
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);
|
var processed = _processDownloadDecisions.ProcessDecisions(decisions);
|
||||||
|
|
||||||
_logger.ProgressInfo("Book search completed. {0} reports downloaded.", processed.Grabbed.Count);
|
_logger.ProgressInfo("Book search completed. {0} reports downloaded.", processed.Grabbed.Count);
|
||||||
|
|||||||
@@ -14,13 +14,13 @@ using NzbDrone.Core.Parser.Model;
|
|||||||
|
|
||||||
namespace NzbDrone.Core.IndexerSearch
|
namespace NzbDrone.Core.IndexerSearch
|
||||||
{
|
{
|
||||||
public interface ISearchForReleases
|
public interface ISearchForNzb
|
||||||
{
|
{
|
||||||
List<DownloadDecision> BookSearch(int bookId, bool missingOnly, bool userInvokedSearch, bool interactiveSearch);
|
List<DownloadDecision> BookSearch(int bookId, bool missingOnly, bool userInvokedSearch, bool interactiveSearch);
|
||||||
List<DownloadDecision> AuthorSearch(int authorId, 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 IIndexerFactory _indexerFactory;
|
||||||
private readonly IBookService _bookService;
|
private readonly IBookService _bookService;
|
||||||
@@ -28,7 +28,7 @@ namespace NzbDrone.Core.IndexerSearch
|
|||||||
private readonly IMakeDownloadDecision _makeDownloadDecision;
|
private readonly IMakeDownloadDecision _makeDownloadDecision;
|
||||||
private readonly Logger _logger;
|
private readonly Logger _logger;
|
||||||
|
|
||||||
public ReleaseSearchService(IIndexerFactory indexerFactory,
|
public NzbSearchService(IIndexerFactory indexerFactory,
|
||||||
IBookService bookService,
|
IBookService bookService,
|
||||||
IAuthorService authorService,
|
IAuthorService authorService,
|
||||||
IMakeDownloadDecision makeDownloadDecision,
|
IMakeDownloadDecision makeDownloadDecision,
|
||||||
17
src/NzbDrone.Core/Indexers/IndexerSettingUpdatedEvent.cs
Normal file
17
src/NzbDrone.Core/Indexers/IndexerSettingUpdatedEvent.cs
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -4,7 +4,6 @@ using NzbDrone.Core.Datastore;
|
|||||||
using NzbDrone.Core.Download.Clients;
|
using NzbDrone.Core.Download.Clients;
|
||||||
using NzbDrone.Core.Messaging.Events;
|
using NzbDrone.Core.Messaging.Events;
|
||||||
using NzbDrone.Core.Parser.Model;
|
using NzbDrone.Core.Parser.Model;
|
||||||
using NzbDrone.Core.ThingiProvider.Events;
|
|
||||||
|
|
||||||
namespace NzbDrone.Core.Indexers
|
namespace NzbDrone.Core.Indexers
|
||||||
{
|
{
|
||||||
@@ -14,7 +13,7 @@ namespace NzbDrone.Core.Indexers
|
|||||||
TorrentSeedConfiguration GetSeedConfiguration(int indexerId, bool fullSeason);
|
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 IIndexerFactory _indexerFactory;
|
||||||
private readonly ICached<SeedCriteriaSettings> _cache;
|
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();
|
_cache.Clear();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -50,7 +50,7 @@ namespace NzbDrone.Core.Indexers
|
|||||||
{
|
{
|
||||||
private static readonly SeedCriteriaSettingsValidator Validator = new SeedCriteriaSettingsValidator();
|
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; }
|
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)]
|
[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)]
|
||||||
|
|||||||
@@ -557,6 +557,5 @@
|
|||||||
"RestartRequiredHelpTextWarning": "يتطلب إعادة التشغيل ليصبح ساري المفعول",
|
"RestartRequiredHelpTextWarning": "يتطلب إعادة التشغيل ليصبح ساري المفعول",
|
||||||
"AddList": "اضف قائمة",
|
"AddList": "اضف قائمة",
|
||||||
"RenameFiles": "إعادة تسمية الملفات",
|
"RenameFiles": "إعادة تسمية الملفات",
|
||||||
"Test": "اختبار",
|
"Test": "اختبار"
|
||||||
"ManualImportSelectEdition": "استيراد يدوي - حدد فيلم"
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -556,6 +556,5 @@
|
|||||||
"RestartRequiredHelpTextWarning": "Изисква рестартиране, за да влезе в сила",
|
"RestartRequiredHelpTextWarning": "Изисква рестартиране, за да влезе в сила",
|
||||||
"Test": "Тест",
|
"Test": "Тест",
|
||||||
"AddList": "Добавяне на списък",
|
"AddList": "Добавяне на списък",
|
||||||
"RenameFiles": "Преименувайте файловете",
|
"RenameFiles": "Преименувайте файловете"
|
||||||
"ManualImportSelectEdition": "Ръчно импортиране - Изберете филм"
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -51,7 +51,7 @@
|
|||||||
"Cancel": "zrušení",
|
"Cancel": "zrušení",
|
||||||
"CancelMessageText": "Opravdu chcete zrušit tento nevyřízený úkol?",
|
"CancelMessageText": "Opravdu chcete zrušit tento nevyřízený úkol?",
|
||||||
"CertificateValidation": "Ověření certifikátu",
|
"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",
|
"ChangeFileDate": "Změnit datum souboru",
|
||||||
"ChangeHasNotBeenSavedYet": "Změna ještě nebyla uložena",
|
"ChangeHasNotBeenSavedYet": "Změna ještě nebyla uložena",
|
||||||
"ChmodFolder": "Složka chmod",
|
"ChmodFolder": "Složka chmod",
|
||||||
@@ -556,6 +556,5 @@
|
|||||||
"RestartRequiredHelpTextWarning": "Vyžaduje restart, aby se projevilo",
|
"RestartRequiredHelpTextWarning": "Vyžaduje restart, aby se projevilo",
|
||||||
"RenameFiles": "Přejmenovat soubory",
|
"RenameFiles": "Přejmenovat soubory",
|
||||||
"Test": "Test",
|
"Test": "Test",
|
||||||
"AddList": "Přidat seznam",
|
"AddList": "Přidat seznam"
|
||||||
"ManualImportSelectEdition": "Ruční import - vyberte Film"
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -556,6 +556,5 @@
|
|||||||
"RestartRequiredHelpTextWarning": "Kræver genstart for at træde i kraft",
|
"RestartRequiredHelpTextWarning": "Kræver genstart for at træde i kraft",
|
||||||
"AddList": "Tilføj Liste",
|
"AddList": "Tilføj Liste",
|
||||||
"Test": "Prøve",
|
"Test": "Prøve",
|
||||||
"RenameFiles": "Omdøb filer",
|
"RenameFiles": "Omdøb filer"
|
||||||
"ManualImportSelectEdition": "Manuel import - Vælg film"
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -44,7 +44,7 @@
|
|||||||
"Cancel": "Abbrechen",
|
"Cancel": "Abbrechen",
|
||||||
"CancelMessageText": "Diese laufende Aufgabe wirklich abbrechen?",
|
"CancelMessageText": "Diese laufende Aufgabe wirklich abbrechen?",
|
||||||
"CertificateValidation": "Zertifikat Validierung",
|
"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",
|
"ChangeFileDate": "Erstelldatum der Datei anpassen",
|
||||||
"ChangeHasNotBeenSavedYet": "Änderung wurde noch nicht gespeichert",
|
"ChangeHasNotBeenSavedYet": "Änderung wurde noch nicht gespeichert",
|
||||||
"ChmodFolder": "chmod Ordner",
|
"ChmodFolder": "chmod Ordner",
|
||||||
@@ -92,8 +92,8 @@
|
|||||||
"DeleteNotificationMessageText": "Benachrichtigung '{0}' wirklich löschen?",
|
"DeleteNotificationMessageText": "Benachrichtigung '{0}' wirklich löschen?",
|
||||||
"DeleteQualityProfile": "Qualitätsdefinition löschen",
|
"DeleteQualityProfile": "Qualitätsdefinition löschen",
|
||||||
"DeleteQualityProfileMessageText": "Qualitätsprofil '{0}' wirklich löschen?",
|
"DeleteQualityProfileMessageText": "Qualitätsprofil '{0}' wirklich löschen?",
|
||||||
"DeleteReleaseProfile": "Release-Profil löschen",
|
"DeleteReleaseProfile": "Verzögerungsprofil löschen",
|
||||||
"DeleteReleaseProfileMessageText": "Bist du sicher, dass du dieses Release-Profil löschen willst?",
|
"DeleteReleaseProfileMessageText": "Bist du sicher, dass du dieses Verzögerungs-Profil löschen willst?",
|
||||||
"DeleteRootFolderMessageText": "Indexer '{0}' wirklich löschen?",
|
"DeleteRootFolderMessageText": "Indexer '{0}' wirklich löschen?",
|
||||||
"DeleteSelectedBookFiles": "Ausgewählte Filmdateien löschen",
|
"DeleteSelectedBookFiles": "Ausgewählte Filmdateien löschen",
|
||||||
"DeleteSelectedBookFilesMessageText": "Ausgewählte Filme wirklich löschen?",
|
"DeleteSelectedBookFilesMessageText": "Ausgewählte Filme wirklich löschen?",
|
||||||
@@ -172,7 +172,7 @@
|
|||||||
"ImportedTo": "Importiert nach",
|
"ImportedTo": "Importiert nach",
|
||||||
"Importing": "Importiere",
|
"Importing": "Importiere",
|
||||||
"IncludeHealthWarningsHelpText": "Zustandswarnung",
|
"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",
|
"IncludeUnmonitored": "Nicht beobachtete einbeziehen",
|
||||||
"Indexer": "Indexer",
|
"Indexer": "Indexer",
|
||||||
"IndexerPriority": "Priorität",
|
"IndexerPriority": "Priorität",
|
||||||
@@ -291,7 +291,7 @@
|
|||||||
"RemoveFromQueue": "Aus der Warteschlage entfernen",
|
"RemoveFromQueue": "Aus der Warteschlage entfernen",
|
||||||
"RemoveHelpTextWarning": "Dies wird den Download und alle bereits heruntergeladenen Dateien aus dem Downloader entfernen.",
|
"RemoveHelpTextWarning": "Dies wird den Download und alle bereits heruntergeladenen Dateien aus dem Downloader entfernen.",
|
||||||
"RemoveSelected": "Auswahl 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",
|
"RemoveTagExistingTag": "Vorhandener Tag",
|
||||||
"RemoveTagRemovingTag": "Tag entfernen",
|
"RemoveTagRemovingTag": "Tag entfernen",
|
||||||
"RemovedFromTaskQueue": "Aus der Aufgabenwarteschlage entfernt",
|
"RemovedFromTaskQueue": "Aus der Aufgabenwarteschlage entfernt",
|
||||||
@@ -301,7 +301,7 @@
|
|||||||
"RequiredHelpText": "Das Release mus mindesten eines der Begriffe beinhalten ( Groß-/Kleinschreibung wird nicht beachtet )",
|
"RequiredHelpText": "Das Release mus mindesten eines der Begriffe beinhalten ( Groß-/Kleinschreibung wird nicht beachtet )",
|
||||||
"RequiredPlaceHolder": "Neue Beschränkung hinzufügen",
|
"RequiredPlaceHolder": "Neue Beschränkung hinzufügen",
|
||||||
"RescanAfterRefreshHelpTextWarning": "Wenn nicht \"Immer (Always)\" ausgewählt wird, werden Dateiänderungen nicht automatisch erkannt",
|
"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",
|
"Reset": "Zurücksetzen",
|
||||||
"ResetAPIKey": "API-Schlüssel zurücksetzen",
|
"ResetAPIKey": "API-Schlüssel zurücksetzen",
|
||||||
"ResetAPIKeyMessageText": "Bist du sicher, dass du den API-Schlüssel zurücksetzen willst?",
|
"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",
|
"ShownAboveEachColumnWhenWeekIsTheActiveView": "Wird in der Wochenansicht über jeder Spalte angezeigt",
|
||||||
"Size": " Größe",
|
"Size": " Größe",
|
||||||
"SkipFreeSpaceCheck": "Pürfung des freien Speichers überspringen",
|
"SkipFreeSpaceCheck": "Pürfung des freien Speichers überspringen",
|
||||||
"SkipFreeSpaceCheckWhenImportingHelpText": "Aktiviere diese Option, wenn es Readarr nicht möglich ist, den freien Speicherplatz des Stammverzeichnises 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.",
|
"SorryThatAuthorCannotBeFound": "Schade, dieser Film kann nicht gefunden werden.",
|
||||||
"SorryThatBookCannotBeFound": "Schade, dieser Film kann nicht gefunden werden.",
|
"SorryThatBookCannotBeFound": "Schade, dieser Film kann nicht gefunden werden.",
|
||||||
"Source": "Quelle",
|
"Source": "Quelle",
|
||||||
@@ -426,7 +426,7 @@
|
|||||||
"UnmonitoredHelpText": "Nicht beobachtete Filme im iCal-Feed einschließen",
|
"UnmonitoredHelpText": "Nicht beobachtete Filme im iCal-Feed einschließen",
|
||||||
"UpdateAll": "Alle aktualisieren",
|
"UpdateAll": "Alle aktualisieren",
|
||||||
"UpdateAutomaticallyHelpText": "Updates automatisch herunteraden und installieren. Es kann weiterhin unter \"System -> Updates\" ein manuelles Update angestoßen werden",
|
"UpdateAutomaticallyHelpText": "Updates automatisch herunteraden und installieren. Es kann weiterhin unter \"System -> Updates\" ein manuelles Update angestoßen werden",
|
||||||
"UpdateMechanismHelpText": "Benutze Readarr's Built-In Updater oder ein Script",
|
"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",
|
"UpdateScriptPathHelpText": "Pfad zu einem benutzerdefinierten Skript, das ein extrahiertes Update-Paket übernimmt und den Rest des Update-Prozesses abwickelt",
|
||||||
"Updates": "Updates",
|
"Updates": "Updates",
|
||||||
"UpgradeAllowedHelpText": "Wenn deaktiviert wird die Qualität nicht verbessert",
|
"UpgradeAllowedHelpText": "Wenn deaktiviert wird die Qualität nicht verbessert",
|
||||||
@@ -546,7 +546,7 @@
|
|||||||
"ImportListSettings": "Allgemeine Einstellungen der Importliste",
|
"ImportListSettings": "Allgemeine Einstellungen der Importliste",
|
||||||
"ImportListSpecificSettings": "Listenspezifische Einstellungen importieren",
|
"ImportListSpecificSettings": "Listenspezifische Einstellungen importieren",
|
||||||
"IndexerLongTermStatusCheckSingleClientMessage": "Indexer wegen über 6 Stunden langen bestehenden Fehlern nicht verfügbar: {0}",
|
"IndexerLongTermStatusCheckSingleClientMessage": "Indexer wegen über 6 Stunden langen bestehenden Fehlern nicht verfügbar: {0}",
|
||||||
"IndexerPriorityHelpText": "Indexer Priorität von 1 (höchste) bis 50 (niedrigste). Standard: 25. Wird beim Holen von Veröffentlichungen als Tiebreaker für ansonsten gleiche Veröffentlichungen verwendet, Readarr benützt weiterhin alle aktivierten Indexer für RSS-Synchronisierung und -Suche.",
|
"IndexerPriorityHelpText": "Indexer Priorität von 1 (höchste) bis 50 (niedrigste). Standard: 25.",
|
||||||
"IndexerRssHealthCheckNoAvailableIndexers": "Alle RSS-fähigen Indexer sind aufgrund der kürzlichen Indexerfehler vorübergehend nicht verfügbar",
|
"IndexerRssHealthCheckNoAvailableIndexers": "Alle RSS-fähigen Indexer sind aufgrund der kürzlichen Indexerfehler vorübergehend nicht verfügbar",
|
||||||
"DiscNumber": "Plattennummer",
|
"DiscNumber": "Plattennummer",
|
||||||
"Lists": "Listen",
|
"Lists": "Listen",
|
||||||
@@ -702,7 +702,7 @@
|
|||||||
"ImportListStatusCheckSingleClientMessage": "Listen aufgrund von Fehlern nicht verfügbar: {0}",
|
"ImportListStatusCheckSingleClientMessage": "Listen aufgrund von Fehlern nicht verfügbar: {0}",
|
||||||
"ImportMechanismHealthCheckMessage": "Aktiviere die Verarbeitung der abgeschlossenen Downloads",
|
"ImportMechanismHealthCheckMessage": "Aktiviere die Verarbeitung der abgeschlossenen Downloads",
|
||||||
"IndexerRssHealthCheckNoIndexers": "Da keine Indexer mit aktivierter RSS-Synchronisierung aktiviert sind, erfasst Readarr neue Erscheinungen nicht automatisch",
|
"IndexerRssHealthCheckNoIndexers": "Da keine Indexer mit aktivierter RSS-Synchronisierung aktiviert sind, erfasst Readarr neue Erscheinungen nicht automatisch",
|
||||||
"IndexerSearchCheckNoInteractiveMessage": "Keine Indexer mit 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}",
|
"IndexerStatusCheckSingleClientMessage": "Indexer aufgrund von Fehlern nicht verfügbar: {0}",
|
||||||
"ChownGroup": "chown Gruppe",
|
"ChownGroup": "chown Gruppe",
|
||||||
"AllowFingerprintingHelpText": "Benutze Fingerabdrücke um die Genauigkeit der Buch Übereinstimmungen zu verbessern",
|
"AllowFingerprintingHelpText": "Benutze Fingerabdrücke um die Genauigkeit der Buch Übereinstimmungen zu verbessern",
|
||||||
@@ -713,7 +713,7 @@
|
|||||||
"PathHelpText": "Stammordner für die Musikbibliothek",
|
"PathHelpText": "Stammordner für die Musikbibliothek",
|
||||||
"ProxyCheckBadRequestMessage": "Proxy konnte nicht getestet werden. StatusCode: {0}",
|
"ProxyCheckBadRequestMessage": "Proxy konnte nicht getestet werden. StatusCode: {0}",
|
||||||
"ProxyCheckFailedToTestMessage": "Proxy konnte nicht getestet werden: {0}",
|
"ProxyCheckFailedToTestMessage": "Proxy konnte nicht getestet werden: {0}",
|
||||||
"Queued": "In der Warteschlange",
|
"Queued": "Eingereiht",
|
||||||
"RefreshAndScan": "Aktualisieren",
|
"RefreshAndScan": "Aktualisieren",
|
||||||
"SettingsRemotePathMappingRemotePathHelpText": "Root-Pfad zum Verzeichnis, auf das der Download-Client zugreift",
|
"SettingsRemotePathMappingRemotePathHelpText": "Root-Pfad zum Verzeichnis, auf das der Download-Client zugreift",
|
||||||
"Yesterday": "Gestern",
|
"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.",
|
"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.",
|
"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.",
|
"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.",
|
"RemotePathMappingCheckLocalWrongOSPath": "Downloader {0} speichert Downloads in {1}, aber dies ist kein valider {2} Pfad. Überprüfe die Downloader Einstellungen.",
|
||||||
"Monitor": "Beobachten",
|
"Monitor": "Beobachten",
|
||||||
"MusicBrainzAuthorID": "MusicBranz Künstler Id",
|
"MusicBrainzAuthorID": "MusicBranz Künstler Id",
|
||||||
@@ -780,102 +780,11 @@
|
|||||||
"InstanceNameHelpText": "Instanzname im Browser-Tab und für Syslog-Anwendungsname",
|
"InstanceNameHelpText": "Instanzname im Browser-Tab und für Syslog-Anwendungsname",
|
||||||
"RestartRequiredHelpTextWarning": "Erfordert einen Neustart",
|
"RestartRequiredHelpTextWarning": "Erfordert einen Neustart",
|
||||||
"UseCalibreContentServer": "Calibre-Content-Server",
|
"UseCalibreContentServer": "Calibre-Content-Server",
|
||||||
"DataExistingBooks": "Beobachte Bücher die Dateien haben oder noch nicht veröffentlicht wurden",
|
"DataExistingBooks": "Beobachte Alben die Dateien haben oder noch nicht veröffentlicht wurden",
|
||||||
"DataFirstBook": "Beobachte das erste Buch. Alle anderen Bücher werden ignoriert",
|
"DataFirstBook": "Beobachte die ersten Alben. Alle anderen Alben werden ignoriert",
|
||||||
"DataFuturebooks": "Überwachung von Büchern die noch nicht veröffentlicht wurden",
|
"DataFuturebooks": "Überwachung von Alben die noch nicht veröffentlicht wurden",
|
||||||
"DataMissingBooks": "Beobachte Bücher, die noch keine Dateien haben oder noch nicht veröffentlicht wurden",
|
"DataMissingBooks": "Beobachte Alben, die noch keine Dateien haben oder noch nicht veröffentlicht wurden",
|
||||||
"Test": "Testen",
|
"Test": "Testen",
|
||||||
"DataNone": "Es werden keine Bücher beobachtet",
|
"DataNone": "Es werden keine Alben beobachtet",
|
||||||
"RenameFiles": "Dateien umbenennen",
|
"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"
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -556,6 +556,5 @@
|
|||||||
"RestartRequiredHelpTextWarning": "Απαιτείται επανεκκίνηση για να τεθεί σε ισχύ",
|
"RestartRequiredHelpTextWarning": "Απαιτείται επανεκκίνηση για να τεθεί σε ισχύ",
|
||||||
"Test": "Δοκιμή",
|
"Test": "Δοκιμή",
|
||||||
"AddList": "Προσθήκη Λίστας",
|
"AddList": "Προσθήκη Λίστας",
|
||||||
"RenameFiles": "Μετονομασία αρχείων",
|
"RenameFiles": "Μετονομασία αρχείων"
|
||||||
"ManualImportSelectEdition": "Μη αυτόματη εισαγωγή - Επιλέξτε ταινία"
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -216,7 +216,7 @@
|
|||||||
"MinimumLimits": "Límites Mínimos",
|
"MinimumLimits": "Límites Mínimos",
|
||||||
"Missing": "Falta",
|
"Missing": "Falta",
|
||||||
"Mode": "Modo",
|
"Mode": "Modo",
|
||||||
"Monitored": "Monitoreada",
|
"Monitored": "Monitoreadas",
|
||||||
"MonoVersion": "Version de Mono",
|
"MonoVersion": "Version de Mono",
|
||||||
"MoreInfo": "Más Información",
|
"MoreInfo": "Más Información",
|
||||||
"MustContain": "Debe Contener",
|
"MustContain": "Debe Contener",
|
||||||
@@ -235,8 +235,8 @@
|
|||||||
"NotificationTriggers": "Desencadenantes de Notificaciones",
|
"NotificationTriggers": "Desencadenantes de Notificaciones",
|
||||||
"OnGrabHelpText": "Al Capturar",
|
"OnGrabHelpText": "Al Capturar",
|
||||||
"OnHealthIssueHelpText": "En Problema de Salud",
|
"OnHealthIssueHelpText": "En Problema de Salud",
|
||||||
"OnRenameHelpText": "Al Renombrar",
|
"OnRenameHelpText": "En Renombrado",
|
||||||
"OnUpgradeHelpText": "Al Mejorar La Calidad",
|
"OnUpgradeHelpText": "Al Actualizar",
|
||||||
"OpenBrowserOnStart": "Abrir navegador al arrancar",
|
"OpenBrowserOnStart": "Abrir navegador al arrancar",
|
||||||
"Options": "Opciones",
|
"Options": "Opciones",
|
||||||
"Original": "Original",
|
"Original": "Original",
|
||||||
@@ -269,7 +269,7 @@
|
|||||||
"QualityProfiles": "Perfiles de Calidad",
|
"QualityProfiles": "Perfiles de Calidad",
|
||||||
"QualitySettings": "Ajustes de Calidad",
|
"QualitySettings": "Ajustes de Calidad",
|
||||||
"Queue": "Cola",
|
"Queue": "Cola",
|
||||||
"RSSSync": "Sincronizar RSS",
|
"RSSSync": "Sincronización RSS",
|
||||||
"RSSSyncInterval": "Intervalo de Sincronización de RSS",
|
"RSSSyncInterval": "Intervalo de Sincronización de RSS",
|
||||||
"ReadTheWikiForMoreInformation": "Lee la Wiki para más información",
|
"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.",
|
"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",
|
"OnBookFileDeleteForUpgradeHelpText": "En archivo de película Eliminar para actualizar",
|
||||||
"OnBookFileDeleteHelpText": "Al eliminar archivo de película",
|
"OnBookFileDeleteHelpText": "Al eliminar archivo de película",
|
||||||
"OnGrab": "Al Capturar",
|
"OnGrab": "Al Capturar",
|
||||||
"OnRename": "Al Renombrar",
|
"OnRename": "En Renombrado",
|
||||||
"OnUpgrade": "Al Mejorar La Calidad",
|
"OnUpgrade": "Al Actualizar",
|
||||||
"ProxyCheckBadRequestMessage": "Fallo al comprobar el proxy. StatusCode: {0}",
|
"ProxyCheckBadRequestMessage": "Fallo al comprobar el proxy. StatusCode: {0}",
|
||||||
"ProxyCheckFailedToTestMessage": "Fallo al comprobar el proxy: {0}",
|
"ProxyCheckFailedToTestMessage": "Fallo al comprobar el proxy: {0}",
|
||||||
"QualitySettingsSummary": "Tamaños de calidad y nombrado",
|
"QualitySettingsSummary": "Tamaños de calidad y nombrado",
|
||||||
@@ -597,9 +597,5 @@
|
|||||||
"RestartRequiredHelpTextWarning": "Requiere reiniciar para que surta efecto",
|
"RestartRequiredHelpTextWarning": "Requiere reiniciar para que surta efecto",
|
||||||
"AddList": "Añadir lista",
|
"AddList": "Añadir lista",
|
||||||
"RenameFiles": "Renombrar Archivos",
|
"RenameFiles": "Renombrar Archivos",
|
||||||
"Test": "Test",
|
"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"
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,11 +20,11 @@
|
|||||||
"Analytics": "Analytiikka",
|
"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.",
|
"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",
|
"AppDataDirectory": "AppData-kansio",
|
||||||
"ApplyTags": "Tunnistetoimenpide",
|
"ApplyTags": "Toimenpide tunnisteille",
|
||||||
"ApplyTagsHelpTexts1": "Miten tunnisteita sovelletaan valittuun kirjailijaan",
|
"ApplyTagsHelpTexts1": "Miten tunnisteita sovelletaan valittuun kirjailijaan",
|
||||||
"ApplyTagsHelpTexts2": "– 'Lisää' syötetyt tunnisteet aiempiin tunnisteisiin",
|
"ApplyTagsHelpTexts2": "– 'Lisää' syötetyt tunnisteet aiempiin tunnisteisiin",
|
||||||
"ApplyTagsHelpTexts3": "- \"Poista\" tyhjentää syötetyt tunnisteet.",
|
"ApplyTagsHelpTexts3": "– 'Poista' ainoastaan syötetyt tunnisteet",
|
||||||
"ApplyTagsHelpTexts4": "- \"Korvaa\" nykyiset tunnisteet syötetyillä tai tyhjennä kaikki tunnisteet jättämällä tyhjäksi.",
|
"ApplyTagsHelpTexts4": "– 'Korvaa' kaikki aiemmat tunnisteet tai poista kaikki tunnisteet jättämällä tyhjäksi",
|
||||||
"Authentication": "Todennus",
|
"Authentication": "Todennus",
|
||||||
"AuthenticationMethodHelpText": "Vaadi käyttäjätunnus ja salasana.",
|
"AuthenticationMethodHelpText": "Vaadi käyttäjätunnus ja salasana.",
|
||||||
"AuthorClickToChangeBook": "Vaihda kirjaa painamalla",
|
"AuthorClickToChangeBook": "Vaihda kirjaa painamalla",
|
||||||
@@ -36,7 +36,7 @@
|
|||||||
"BackupRetentionHelpText": "Säilytysjaksoa vanhemmat, automaattiset varmuuskopiot poistetaan automaattisesti.",
|
"BackupRetentionHelpText": "Säilytysjaksoa vanhemmat, automaattiset varmuuskopiot poistetaan automaattisesti.",
|
||||||
"Backups": "Varmuuskopiointi",
|
"Backups": "Varmuuskopiointi",
|
||||||
"BindAddress": "Sidososoite",
|
"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.",
|
"BindAddressHelpTextWarning": "Käyttöönotto vaatii uudelleenkäynnistyksen.",
|
||||||
"BookIsDownloading": "Kirjaa ladataan",
|
"BookIsDownloading": "Kirjaa ladataan",
|
||||||
"BookIsDownloadingInterp": "Kirjaa ladataan - {0} % {1}",
|
"BookIsDownloadingInterp": "Kirjaa ladataan - {0} % {1}",
|
||||||
@@ -47,7 +47,7 @@
|
|||||||
"Cancel": "Peruuta",
|
"Cancel": "Peruuta",
|
||||||
"CancelMessageText": "Haluatko varmasti perua tämän odottavan tehtävän?",
|
"CancelMessageText": "Haluatko varmasti perua tämän odottavan tehtävän?",
|
||||||
"CertificateValidation": "Varmenteen vahvistus",
|
"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",
|
"ChangeFileDate": "Muuta tiedoston päiväys",
|
||||||
"ChangeHasNotBeenSavedYet": "Muutosta ei ole vielä tallennettu",
|
"ChangeHasNotBeenSavedYet": "Muutosta ei ole vielä tallennettu",
|
||||||
"ChmodFolder": "chmod-kansio",
|
"ChmodFolder": "chmod-kansio",
|
||||||
@@ -141,7 +141,7 @@
|
|||||||
"Fixed": "Korjattu",
|
"Fixed": "Korjattu",
|
||||||
"Folder": "Kansio",
|
"Folder": "Kansio",
|
||||||
"Folders": "Kansioiden käsittely",
|
"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'.",
|
"ForMoreInformationOnTheIndividualIndexersClickOnTheInfoButtons": "Lue lisää tietolähteestä painamalla 'Lisätietoja'.",
|
||||||
"ForMoreInformationOnTheIndividualListsClickOnTheInfoButtons": "Lue lisää tuontilistoista painamalla 'Lisätietoja'.",
|
"ForMoreInformationOnTheIndividualListsClickOnTheInfoButtons": "Lue lisää tuontilistoista painamalla 'Lisätietoja'.",
|
||||||
"GeneralSettings": "Yleiset asetukset",
|
"GeneralSettings": "Yleiset asetukset",
|
||||||
@@ -271,7 +271,7 @@
|
|||||||
"ReadarrTags": "Radarr-tunnisteet",
|
"ReadarrTags": "Radarr-tunnisteet",
|
||||||
"Real": "Todellinen",
|
"Real": "Todellinen",
|
||||||
"Reason": "Syy",
|
"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.",
|
"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.",
|
"RecycleBinHelpText": "Pysyvän poiston sijaan kirjatiedostot siirretään tähän kansioon.",
|
||||||
"RecyclingBin": "Roskakori",
|
"RecyclingBin": "Roskakori",
|
||||||
@@ -297,7 +297,7 @@
|
|||||||
"RemoveTagExistingTag": "Olemassa oleva tunniste",
|
"RemoveTagExistingTag": "Olemassa oleva tunniste",
|
||||||
"RemoveTagRemovingTag": "Tunniste poistetaan",
|
"RemoveTagRemovingTag": "Tunniste poistetaan",
|
||||||
"RemovedFromTaskQueue": "Poistettu tehtäväjonosta",
|
"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",
|
"Reorder": "Järjestä uudelleen",
|
||||||
"ReplaceIllegalCharacters": "Korvaa kielletyt merkit",
|
"ReplaceIllegalCharacters": "Korvaa kielletyt merkit",
|
||||||
"RequiredHelpText": "Julkaisun tulee sisältää ainakin yksi näistä termeistä (kirjainkokoa ei huomioida).",
|
"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ää.",
|
"ShownAboveEachColumnWhenWeekIsTheActiveView": "Näkyy jokaisen sarakkeen yläpuolella käytettäessä viikkonäkymää.",
|
||||||
"Size": " Koko",
|
"Size": " Koko",
|
||||||
"SkipFreeSpaceCheck": "Ohita vapaan levytilan tarkistus",
|
"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.",
|
"SorryThatAuthorCannotBeFound": "Valitettavasti kirjailijaa ei löydy.",
|
||||||
"SorryThatBookCannotBeFound": "Valitettavasti elokuvaa ei löydy.",
|
"SorryThatBookCannotBeFound": "Valitettavasti elokuvaa ei löydy.",
|
||||||
"Source": "Lähdekoodi",
|
"Source": "Lähdekoodi",
|
||||||
@@ -367,7 +367,7 @@
|
|||||||
"SuccessMyWorkIsDoneNoFilesToRetag": "Menestys! Työni on valmis, ei nimettäviä tiedostoja.",
|
"SuccessMyWorkIsDoneNoFilesToRetag": "Menestys! Työni on valmis, ei nimettäviä tiedostoja.",
|
||||||
"SupportsRssvalueRSSIsNotSupportedWithThisIndexer": "RSS-syötettä ei ole käytettävissä tälle tietolähteelle",
|
"SupportsRssvalueRSSIsNotSupportedWithThisIndexer": "RSS-syötettä ei ole käytettävissä tälle tietolähteelle",
|
||||||
"SupportsSearchvalueSearchIsNotSupportedWithThisIndexer": "Hakemistoa ei tueta tällä hakemistolla",
|
"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ä.",
|
"SupportsSearchvalueWillBeUsedWhenInteractiveSearchIsUsed": "Profiilia käytetään vuorovaikutteisen haun yhteydessä.",
|
||||||
"TagIsNotUsedAndCanBeDeleted": "Tunnistetta ei ole määritetty millekään kohteelle, joten sen voi poistaa.",
|
"TagIsNotUsedAndCanBeDeleted": "Tunnistetta ei ole määritetty millekään kohteelle, joten sen voi poistaa.",
|
||||||
"Tags": "Tunnisteet",
|
"Tags": "Tunnisteet",
|
||||||
@@ -588,10 +588,10 @@
|
|||||||
"MissingBooks": "Puuttuvat kirjat",
|
"MissingBooks": "Puuttuvat kirjat",
|
||||||
"MissingBooksAuthorMonitored": "Puuttuvat kirjat (kirjailijaa valvotaan)",
|
"MissingBooksAuthorMonitored": "Puuttuvat kirjat (kirjailijaa valvotaan)",
|
||||||
"MissingBooksAuthorNotMonitored": "Puuttuvat kirjat (kirjailijaa ei valvota)",
|
"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",
|
"MonitorNewItems": "Valvo uusia kirjoja",
|
||||||
"MonitorNewItemsHelpText": "Uusien kirjojen valvontatapa.",
|
"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.",
|
"OutputFormatHelpText": "Voit halutessasi pyytää Calibrea muuntamaan kirjat eri muotoihin tuonnin yhteydessä. Pilkulla eroteltu lista.",
|
||||||
"PasswordHelpText": "Calibre-sisältöpalvelimen salasana.",
|
"PasswordHelpText": "Calibre-sisältöpalvelimen salasana.",
|
||||||
"PortHelpText": "Calibre-sisältöpalvelimen portti.",
|
"PortHelpText": "Calibre-sisältöpalvelimen portti.",
|
||||||
@@ -700,7 +700,5 @@
|
|||||||
"RenameFiles": "Uudelleennimeä tiedostot",
|
"RenameFiles": "Uudelleennimeä tiedostot",
|
||||||
"Test": "Kokeile",
|
"Test": "Kokeile",
|
||||||
"AllowFingerprintingHelpText": "Käytä piiloleimausta kirjojen täsmäyksen tarkennukseen",
|
"AllowFingerprintingHelpText": "Käytä piiloleimausta kirjojen täsmäyksen tarkennukseen",
|
||||||
"AllowFingerprinting": "Salli piiloleimaus",
|
"AllowFingerprinting": "Salli piiloleimaus"
|
||||||
"Database": "Tietokanta",
|
|
||||||
"ManualImportSelectEdition": "Manuaalinen tuonti - Valitse versio"
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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",
|
"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",
|
"DefaultQualityProfileIdHelpText": "Profil de qualité par défaut pour les auteurs détectés dans ce dossier",
|
||||||
"AddMissing": "Ajouter les manquants",
|
"AddMissing": "Ajouter les manquants",
|
||||||
"BookFileCountBookCountTotalTotalBookCountInterp": "{0} / {1} (Total : {2})",
|
"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"
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -568,7 +568,5 @@
|
|||||||
"RenameFiles": "שנה את שם הקבצים",
|
"RenameFiles": "שנה את שם הקבצים",
|
||||||
"Test": "מִבְחָן",
|
"Test": "מִבְחָן",
|
||||||
"InstanceName": "שם מופע",
|
"InstanceName": "שם מופע",
|
||||||
"RemotePathMappingCheckWrongOSPath": "אתה משתמש בדוקר; קליינט ההורדות {0} שם הורדות ב-{1} אבל הנתיב לא תקין {2}. בחן מחדש את ניתוב התיקיות והגדרות קליינט ההורדות.",
|
"RemotePathMappingCheckWrongOSPath": "אתה משתמש בדוקר; קליינט ההורדות {0} שם הורדות ב-{1} אבל הנתיב לא תקין {2}. בחן מחדש את ניתוב התיקיות והגדרות קליינט ההורדות."
|
||||||
"ManualImportSelectEdition": "ייבוא ידני - בחר סרט",
|
|
||||||
"Database": "מסד נתונים"
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -556,6 +556,5 @@
|
|||||||
"RenameFiles": "फ़ाइलों का नाम बदलें",
|
"RenameFiles": "फ़ाइलों का नाम बदलें",
|
||||||
"RestartRequiredHelpTextWarning": "प्रभावी करने के लिए पुनरारंभ की आवश्यकता है",
|
"RestartRequiredHelpTextWarning": "प्रभावी करने के लिए पुनरारंभ की आवश्यकता है",
|
||||||
"AddList": "सूची में जोड़ने",
|
"AddList": "सूची में जोड़ने",
|
||||||
"Test": "परीक्षा",
|
"Test": "परीक्षा"
|
||||||
"ManualImportSelectEdition": "मैनुअल आयात - मूवी का चयन करें"
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -556,6 +556,5 @@
|
|||||||
"RestartRequiredHelpTextWarning": "Krefst endurræsingar til að taka gildi",
|
"RestartRequiredHelpTextWarning": "Krefst endurræsingar til að taka gildi",
|
||||||
"AddList": "Bæta við lista",
|
"AddList": "Bæta við lista",
|
||||||
"RenameFiles": "Endurnefna skrár",
|
"RenameFiles": "Endurnefna skrár",
|
||||||
"Test": "Próf",
|
"Test": "Próf"
|
||||||
"ManualImportSelectEdition": "Handvirkur innflutningur - Veldu kvikmynd"
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -619,7 +619,5 @@
|
|||||||
"InstanceName": "Nome Istanza",
|
"InstanceName": "Nome Istanza",
|
||||||
"InstanceNameHelpText": "Nome dell'istanza nella scheda e per il nome dell'applicazione Syslog",
|
"InstanceNameHelpText": "Nome dell'istanza nella scheda e per il nome dell'applicazione Syslog",
|
||||||
"LogRotateHelpText": "Numero massimo di file di log da tenere salvati nella cartella log",
|
"LogRotateHelpText": "Numero massimo di file di log da tenere salvati nella cartella log",
|
||||||
"LogRotation": "Rotazione Log",
|
"LogRotation": "Rotazione Log"
|
||||||
"ManualImportSelectEdition": "Importazione manuale: seleziona Film",
|
|
||||||
"Database": "Database"
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -556,6 +556,5 @@
|
|||||||
"RestartRequiredHelpTextWarning": "有効にするには再起動が必要です",
|
"RestartRequiredHelpTextWarning": "有効にするには再起動が必要です",
|
||||||
"RenameFiles": "ファイルの名前を変更する",
|
"RenameFiles": "ファイルの名前を変更する",
|
||||||
"Test": "テスト",
|
"Test": "テスト",
|
||||||
"AddList": "リストを追加",
|
"AddList": "リストを追加"
|
||||||
"ManualImportSelectEdition": "手動インポート-ムービーを選択"
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1 +0,0 @@
|
|||||||
{}
|
|
||||||
@@ -589,7 +589,5 @@
|
|||||||
"RestartRequiredHelpTextWarning": "Herstarten vereist om in werking te treden",
|
"RestartRequiredHelpTextWarning": "Herstarten vereist om in werking te treden",
|
||||||
"AddList": "Lijst Toevoegen",
|
"AddList": "Lijst Toevoegen",
|
||||||
"RenameFiles": "Hernoem Bestanden",
|
"RenameFiles": "Hernoem Bestanden",
|
||||||
"Test": "Test",
|
"Test": "Test"
|
||||||
"ManualImportSelectEdition": "Manuele import - Selecteer Film",
|
|
||||||
"Database": "Databasis"
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -131,11 +131,11 @@
|
|||||||
"DeleteImportListMessageText": "Czy na pewno chcesz usunąć listę „{0}”?",
|
"DeleteImportListMessageText": "Czy na pewno chcesz usunąć listę „{0}”?",
|
||||||
"DeleteIndexer": "Usuń indeksator",
|
"DeleteIndexer": "Usuń indeksator",
|
||||||
"DeleteIndexerMessageText": "Czy na pewno chcesz usunąć indeksator „{0}”?",
|
"DeleteIndexerMessageText": "Czy na pewno chcesz usunąć indeksator „{0}”?",
|
||||||
"DeleteMetadataProfileMessageText": "Czy na pewno chcesz usunąć profil metadanych '{0}'?",
|
"DeleteMetadataProfileMessageText": "Czy na pewno chcesz usunąć profil jakości {0}",
|
||||||
"DeleteNotification": "Usuń powiadomienie",
|
"DeleteNotification": "Usuń powiadomienie",
|
||||||
"DeleteNotificationMessageText": "Czy na pewno chcesz usunąć powiadomienie „{0}”?",
|
"DeleteNotificationMessageText": "Czy na pewno chcesz usunąć powiadomienie „{0}”?",
|
||||||
"DeleteQualityProfile": "Usuń profil jakości",
|
"DeleteQualityProfile": "Usuń profil jakości",
|
||||||
"DeleteQualityProfileMessageText": "Czy na pewno chcesz usunąć profil jakości '{0}'?",
|
"DeleteQualityProfileMessageText": "Czy na pewno chcesz usunąć profil jakości {0}",
|
||||||
"DownloadPropersAndRepacksHelpTexts1": "Czy automatycznie uaktualnić do Propers / Repacks",
|
"DownloadPropersAndRepacksHelpTexts1": "Czy automatycznie uaktualnić do Propers / Repacks",
|
||||||
"DownloadWarningCheckDownloadClientForMoreDetails": "Ostrzeżenie dotyczące pobierania: sprawdź klienta pobierania, aby uzyskać więcej informacji",
|
"DownloadWarningCheckDownloadClientForMoreDetails": "Ostrzeżenie dotyczące pobierania: sprawdź klienta pobierania, aby uzyskać więcej informacji",
|
||||||
"Edit": "Edytować",
|
"Edit": "Edytować",
|
||||||
@@ -217,7 +217,7 @@
|
|||||||
"MarkAsFailedMessageText": "Czy na pewno chcesz oznaczyć „{0}” jako nieudany?",
|
"MarkAsFailedMessageText": "Czy na pewno chcesz oznaczyć „{0}” jako nieudany?",
|
||||||
"MaximumLimits": "Maksymalne limity",
|
"MaximumLimits": "Maksymalne limity",
|
||||||
"MaximumSize": "Największy rozmiar",
|
"MaximumSize": "Największy rozmiar",
|
||||||
"MaximumSizeHelpText": "Maksymalny rozmiar pobrania wydania w MB. Ustaw na zero, aby ustawić na nieograniczony.",
|
"MaximumSizeHelpText": "Maksymalny rozmiar pobrania wydania w MB. Ustaw na zero, aby ustawić na nieograniczony",
|
||||||
"Mechanism": "Mechanizm",
|
"Mechanism": "Mechanizm",
|
||||||
"MinimumAgeHelpText": "Tylko Usenet: minimalny wiek NZB w minutach przed ich przechwyceniem. Wykorzystaj to, aby dać czas na rozpowszechnienie nowych wersji do Twojego dostawcy usenetu.",
|
"MinimumAgeHelpText": "Tylko Usenet: minimalny wiek NZB w minutach przed ich przechwyceniem. Wykorzystaj to, aby dać czas na rozpowszechnienie nowych wersji do Twojego dostawcy usenetu.",
|
||||||
"MinimumFreeSpace": "Minimalna wolna przestrzeń",
|
"MinimumFreeSpace": "Minimalna wolna przestrzeń",
|
||||||
@@ -232,7 +232,7 @@
|
|||||||
"NamingSettings": "Ustawienia nazewnictwa",
|
"NamingSettings": "Ustawienia nazewnictwa",
|
||||||
"New": "Nowy",
|
"New": "Nowy",
|
||||||
"NoBackupsAreAvailable": "Brak dostępnych kopii zapasowych",
|
"NoBackupsAreAvailable": "Brak dostępnych kopii zapasowych",
|
||||||
"NoHistory": "Brak historii.",
|
"NoHistory": "Żadnej historii",
|
||||||
"NoLeaveIt": "Nie, zostaw to",
|
"NoLeaveIt": "Nie, zostaw to",
|
||||||
"NoLimitForAnyRuntime": "Brak ograniczeń dla dowolnego czasu wykonywania",
|
"NoLimitForAnyRuntime": "Brak ograniczeń dla dowolnego czasu wykonywania",
|
||||||
"NoLogFiles": "Brak plików dziennika",
|
"NoLogFiles": "Brak plików dziennika",
|
||||||
@@ -409,7 +409,7 @@
|
|||||||
"UnableToLoadDownloadClientOptions": "Nie można załadować opcji klienta pobierania",
|
"UnableToLoadDownloadClientOptions": "Nie można załadować opcji klienta pobierania",
|
||||||
"UnableToLoadDownloadClients": "Nie można załadować klientów pobierania",
|
"UnableToLoadDownloadClients": "Nie można załadować klientów pobierania",
|
||||||
"UnableToLoadGeneralSettings": "Nie można załadować ustawień ogólnych",
|
"UnableToLoadGeneralSettings": "Nie można załadować ustawień ogólnych",
|
||||||
"UnableToLoadHistory": "Nie można załadować historii.",
|
"UnableToLoadHistory": "Nie można załadować historii",
|
||||||
"UnableToLoadImportListExclusions": "Nie można załadować wykluczeń list",
|
"UnableToLoadImportListExclusions": "Nie można załadować wykluczeń list",
|
||||||
"UnableToLoadIndexerOptions": "Nie można załadować opcji indeksatora",
|
"UnableToLoadIndexerOptions": "Nie można załadować opcji indeksatora",
|
||||||
"UnableToLoadIndexers": "Nie można załadować indeksatorów",
|
"UnableToLoadIndexers": "Nie można załadować indeksatorów",
|
||||||
@@ -577,7 +577,7 @@
|
|||||||
"RemotePathMappingCheckDownloadPermissions": "Radarr widzi film {0}, lecz nie ma do niego dostępu. Najprawdopodobniej to wynik błędu w uprawnieniach dostępu.",
|
"RemotePathMappingCheckDownloadPermissions": "Radarr widzi film {0}, lecz nie ma do niego dostępu. Najprawdopodobniej to wynik błędu w uprawnieniach dostępu.",
|
||||||
"RemotePathMappingCheckFilesGenericPermissions": "Klient pobierania {0} zgłosił pliki w {1}, lecz Radarr nie widzi tego folderu. Być może musisz zmienić uprawnienia dostępu do tego folderu.",
|
"RemotePathMappingCheckFilesGenericPermissions": "Klient pobierania {0} zgłosił pliki w {1}, lecz Radarr nie widzi tego folderu. Być może musisz zmienić uprawnienia dostępu do tego folderu.",
|
||||||
"RemotePathMappingCheckFolderPermissions": "Radarr widzi folder {0}, lecz nie ma do niego dostępu. Prawdopodobnie to wynik błędu uprawnień dostępu.",
|
"RemotePathMappingCheckFolderPermissions": "Radarr widzi folder {0}, lecz nie ma do niego dostępu. Prawdopodobnie to wynik błędu uprawnień dostępu.",
|
||||||
"RemotePathMappingCheckGenericPermissions": "Klient pobierania {0} umieszcza pobrane pliki w {1}, lecz Radarr nie widzi tego folderu. Prawdopodobnie należy zmienić uprawnienia dostępu do folderu.",
|
"RemotePathMappingCheckGenericPermissions": "Klient pobierania {0} umieszcza pobrane pliki w {1}, lecz Radarr nie widzi tego folderu. Być może należy zmienić uprawnienia dostępu do folderu.",
|
||||||
"RemotePathMappingCheckImportFailed": "Radarr nie mógł zaimportować filmu. Szczegóły znajdziesz w dzienniku.",
|
"RemotePathMappingCheckImportFailed": "Radarr nie mógł zaimportować filmu. Szczegóły znajdziesz w dzienniku.",
|
||||||
"RenameFiles": "Zmień nazwy plików",
|
"RenameFiles": "Zmień nazwy plików",
|
||||||
"Test": "Test",
|
"Test": "Test",
|
||||||
|
|||||||
@@ -306,7 +306,7 @@
|
|||||||
"LogLevel": "Nível de log",
|
"LogLevel": "Nível de log",
|
||||||
"LogLevelvalueTraceTraceLoggingShouldOnlyBeEnabledTemporarily": "O registo de rastreio somente deve ser ativado temporariamente",
|
"LogLevelvalueTraceTraceLoggingShouldOnlyBeEnabledTemporarily": "O registo de rastreio somente deve ser ativado temporariamente",
|
||||||
"Logging": "guardar nos registos",
|
"Logging": "guardar nos registos",
|
||||||
"Logs": "Registos",
|
"Logs": "Logs",
|
||||||
"LongDateFormat": "Formato longo de data",
|
"LongDateFormat": "Formato longo de data",
|
||||||
"MIA": "Desaparecidos",
|
"MIA": "Desaparecidos",
|
||||||
"ManualImport": "Importação manual",
|
"ManualImport": "Importação manual",
|
||||||
@@ -355,7 +355,7 @@
|
|||||||
"Preferred": "Preferido",
|
"Preferred": "Preferido",
|
||||||
"PreviewRename": "Pré-visualizar renomeação",
|
"PreviewRename": "Pré-visualizar renomeação",
|
||||||
"Profiles": "Perfis",
|
"Profiles": "Perfis",
|
||||||
"Proper": "Correta",
|
"Proper": "Proper",
|
||||||
"PropersAndRepacks": "Propers e Repacks",
|
"PropersAndRepacks": "Propers e Repacks",
|
||||||
"Protocol": "Protocolo",
|
"Protocol": "Protocolo",
|
||||||
"ProtocolHelpText": "Escolha que protocolo(s) utilizar e qual o preferido ao escolher entre versões iguais",
|
"ProtocolHelpText": "Escolha que protocolo(s) utilizar e qual o preferido ao escolher entre versões iguais",
|
||||||
@@ -494,7 +494,7 @@
|
|||||||
"MetadataConsumers": "Consumidores de metadados",
|
"MetadataConsumers": "Consumidores de metadados",
|
||||||
"MetadataProfile": "Perfil de metadados",
|
"MetadataProfile": "Perfil de metadados",
|
||||||
"ReleaseProfiles": "Perfis de versão",
|
"ReleaseProfiles": "Perfis de versão",
|
||||||
"AddImportListExclusionHelpText": "Impedir que o livro seja adicionado ao Readarr por Listas de Importação ou Atualização do Autor",
|
"AddImportListExclusionHelpText": "Impedir que o livro seja adicionado ao Readarr por listas de importação ou atualização do autor",
|
||||||
"AddMissing": "Adicionar ausentes",
|
"AddMissing": "Adicionar ausentes",
|
||||||
"AddNewItem": "Adicionar novo item",
|
"AddNewItem": "Adicionar novo item",
|
||||||
"AllAuthorBooks": "Todos os livros do autor",
|
"AllAuthorBooks": "Todos os livros do autor",
|
||||||
@@ -803,8 +803,5 @@
|
|||||||
"RestartRequiredHelpTextWarning": "Requer reinício para aplicar alterações",
|
"RestartRequiredHelpTextWarning": "Requer reinício para aplicar alterações",
|
||||||
"AddList": "Adicionar lista",
|
"AddList": "Adicionar lista",
|
||||||
"RenameFiles": "Renomear ficheiros",
|
"RenameFiles": "Renomear ficheiros",
|
||||||
"Test": "Testar",
|
"Test": "Testar"
|
||||||
"InstanceNameHelpText": "Nome da instância na aba e nome da aplicação para Syslog",
|
|
||||||
"Database": "Base de dados",
|
|
||||||
"AddedAuthorSettings": "Definições de Autor adicionadas"
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -239,7 +239,7 @@
|
|||||||
"NoUpdatesAreAvailable": "Não há atualizações disponíveis",
|
"NoUpdatesAreAvailable": "Não há atualizações disponíveis",
|
||||||
"None": "Nenhum",
|
"None": "Nenhum",
|
||||||
"NotificationTriggers": "Acionadores da notificação",
|
"NotificationTriggers": "Acionadores da notificação",
|
||||||
"OnGrabHelpText": "Em Espera",
|
"OnGrabHelpText": "Ao obter",
|
||||||
"OnHealthIssueHelpText": "Ao ter problema de integridade",
|
"OnHealthIssueHelpText": "Ao ter problema de integridade",
|
||||||
"OnRenameHelpText": "Ao renomear",
|
"OnRenameHelpText": "Ao renomear",
|
||||||
"OnUpgradeHelpText": "Ao atualizar",
|
"OnUpgradeHelpText": "Ao atualizar",
|
||||||
@@ -754,14 +754,14 @@
|
|||||||
"OnBookFileDeleteHelpText": "Ao Excluir um Arquivo de Livro",
|
"OnBookFileDeleteHelpText": "Ao Excluir um Arquivo de Livro",
|
||||||
"OnBookTagUpdate": "Ao Atualizar Etiqueta de um Livro",
|
"OnBookTagUpdate": "Ao Atualizar Etiqueta de um Livro",
|
||||||
"OnDownloadFailure": "Na Falha do Download",
|
"OnDownloadFailure": "Na Falha do Download",
|
||||||
"OnGrab": "Em Espera",
|
"OnGrab": "Ao Baixar",
|
||||||
"OnHealthIssue": "Em Problema de Saúde",
|
"OnHealthIssue": "Em Problema de Saúde",
|
||||||
"OnImportFailure": "Em uma Falha de Importação",
|
"OnImportFailure": "Em uma Falha de Importação",
|
||||||
"OnReleaseImport": "Ao Importar Lançamento",
|
"OnReleaseImport": "Ao Importar Lançamento",
|
||||||
"OnRename": "Ao Renomear",
|
"OnRename": "Ao Renomear",
|
||||||
"OnUpgrade": "Ao Atualizar",
|
"OnUpgrade": "Ao Atualizar",
|
||||||
"AppDataLocationHealthCheckMessage": "A atualização não será possível para evitar a exclusão de AppData na atualização",
|
"AppDataLocationHealthCheckMessage": "A atualização não será possível para evitar a exclusão de AppData na atualização",
|
||||||
"IndexerSearchCheckNoInteractiveMessage": "Nenhum indexador disponível com a Pesquisa Interativa habilitada, Readarr não irá prover quaisquer resultados para pesquisa interativa",
|
"IndexerSearchCheckNoInteractiveMessage": "Nenhum indexador disponível com a Pesquisa interativa ativada, o Readarr não fornecerá nenhum resultado de pesquisa interativo",
|
||||||
"ConnectSettingsSummary": "Notificações, conexões com servidores/tocadores de mídia e scripts personalizados",
|
"ConnectSettingsSummary": "Notificações, conexões com servidores/tocadores de mídia e scripts personalizados",
|
||||||
"DownloadClientStatusCheckAllClientMessage": "Todos os clientes download não estão disponíveis devido a falhas",
|
"DownloadClientStatusCheckAllClientMessage": "Todos os clientes download não estão disponíveis devido a falhas",
|
||||||
"DownloadClientsSettingsSummary": "Clientes de download, gerenciamento do download e mapeamento remoto de caminhos",
|
"DownloadClientsSettingsSummary": "Clientes de download, gerenciamento do download e mapeamento remoto de caminhos",
|
||||||
@@ -874,8 +874,5 @@
|
|||||||
"DataNewNone": "Não monitorar nenhum novo livro",
|
"DataNewNone": "Não monitorar nenhum novo livro",
|
||||||
"DataNone": "Nenhum livro irá ser monitorado",
|
"DataNone": "Nenhum livro irá ser monitorado",
|
||||||
"MonitorNewBooks": "Monitorar Novos Livros",
|
"MonitorNewBooks": "Monitorar Novos Livros",
|
||||||
"SetReadarrTags": "Configurar Etiquetas do Readarr",
|
"SetReadarrTags": "Configurar Etiquetas do Readarr"
|
||||||
"Database": "Banco de dados",
|
|
||||||
"LoadingEditionsFailed": "Falha ao carregar edições",
|
|
||||||
"ManualImportSelectEdition": "Importação Manual - Selecionar Edição"
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -47,23 +47,5 @@
|
|||||||
"AgeWhenGrabbed": "Vek (po uchopení)",
|
"AgeWhenGrabbed": "Vek (po uchopení)",
|
||||||
"Analytics": "Analytika",
|
"Analytics": "Analytika",
|
||||||
"BindAddressHelpText": "Platná adresa IP4 alebo '*' pre všetky rozhrania",
|
"BindAddressHelpText": "Platná adresa IP4 alebo '*' pre všetky rozhrania",
|
||||||
"AppDataLocationHealthCheckMessage": "Aktualizácia nebude možná, aby sa predišlo zmazaniu AppData v priebehu aktualizácie",
|
"AppDataLocationHealthCheckMessage": "Aktualizácia nebude možná, aby sa predišlo zmazaniu AppData v priebehu aktualizácie"
|
||||||
"ChmodFolder": "chmod Priečinok",
|
|
||||||
"ClickToChangeQuality": "Kliknutím zmeníte kvalitu",
|
|
||||||
"ChangeFileDate": "Zmeniť dátum súboru",
|
|
||||||
"ChangeHasNotBeenSavedYet": "Zmena ešte nebola uložená",
|
|
||||||
"ClientPriority": "Priorita klienta",
|
|
||||||
"Component": "Komponent",
|
|
||||||
"CertificateValidation": "Overenie certifikátu",
|
|
||||||
"CertificateValidationHelpText": "Zmeňte, aké prísne je overenie certifikácie HTTPS. Nemeňte, pokiaľ nerozumiete rizikám.",
|
|
||||||
"Close": "Zatvoriť",
|
|
||||||
"CloneIndexer": "Klonovať indexer",
|
|
||||||
"CloneProfile": "Klonovať profil",
|
|
||||||
"Connect": "Pripojiť",
|
|
||||||
"BindAddress": "Viazať adresu",
|
|
||||||
"Clear": "Vymazať",
|
|
||||||
"Blocklist": "Blocklist",
|
|
||||||
"BlocklistRelease": "Blocklistnúť vydanie",
|
|
||||||
"Columns": "Stĺpce",
|
|
||||||
"CompletedDownloadHandling": "Zaobchádzanie s dokončenými sťahovaniami"
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -32,6 +32,7 @@
|
|||||||
"BackupRetentionHelpText": "早于保留周期的自动备份将被自动清除",
|
"BackupRetentionHelpText": "早于保留周期的自动备份将被自动清除",
|
||||||
"Backups": "备份",
|
"Backups": "备份",
|
||||||
"BindAddress": "绑定地址",
|
"BindAddress": "绑定地址",
|
||||||
|
"BindAddressHelpText": "有效的 IP4 地址或以'*'代表所有地址",
|
||||||
"BindAddressHelpTextWarning": "需重启以生效",
|
"BindAddressHelpTextWarning": "需重启以生效",
|
||||||
"BookIsDownloading": "影片正在下载中",
|
"BookIsDownloading": "影片正在下载中",
|
||||||
"BookIsDownloadingInterp": "影片正在下载中 - {0}% {1}",
|
"BookIsDownloadingInterp": "影片正在下载中 - {0}% {1}",
|
||||||
@@ -89,7 +90,7 @@
|
|||||||
"DeleteQualityProfile": "删除电影质量配置",
|
"DeleteQualityProfile": "删除电影质量配置",
|
||||||
"DeleteQualityProfileMessageText": "确定要删除影片质量配置“{0}”",
|
"DeleteQualityProfileMessageText": "确定要删除影片质量配置“{0}”",
|
||||||
"DeleteReleaseProfile": "删除延迟配置",
|
"DeleteReleaseProfile": "删除延迟配置",
|
||||||
"DeleteReleaseProfileMessageText": "您确定要删除这个发行配置?",
|
"DeleteReleaseProfileMessageText": "您确定要删除这个延迟配置?",
|
||||||
"DeleteSelectedBookFiles": "删除选择的电影文件",
|
"DeleteSelectedBookFiles": "删除选择的电影文件",
|
||||||
"DeleteSelectedBookFilesMessageText": "您确定要删除选择的电影文件吗?",
|
"DeleteSelectedBookFilesMessageText": "您确定要删除选择的电影文件吗?",
|
||||||
"DeleteTagMessageText": "您确定要删除标签 '{0}' 吗?",
|
"DeleteTagMessageText": "您确定要删除标签 '{0}' 吗?",
|
||||||
@@ -134,7 +135,7 @@
|
|||||||
"Fixed": "已修复",
|
"Fixed": "已修复",
|
||||||
"Folder": "文件夹",
|
"Folder": "文件夹",
|
||||||
"Folders": "文件夹",
|
"Folders": "文件夹",
|
||||||
"ForMoreInformationOnTheIndividualDownloadClientsClickOnTheInfoButtons": "有关单个下载客户端的详细信息,请单击info按钮。",
|
"ForMoreInformationOnTheIndividualDownloadClientsClickOnTheInfoButtons": "有关个别下载客户端的详细信息,请单击info按钮。",
|
||||||
"ForMoreInformationOnTheIndividualIndexersClickOnTheInfoButtons": "有关更多搜刮器的详细信息,请单击信息按钮。",
|
"ForMoreInformationOnTheIndividualIndexersClickOnTheInfoButtons": "有关更多搜刮器的详细信息,请单击信息按钮。",
|
||||||
"ForMoreInformationOnTheIndividualListsClickOnTheInfoButtons": "有关更多导入列表的详细信息,请单击info按钮。",
|
"ForMoreInformationOnTheIndividualListsClickOnTheInfoButtons": "有关更多导入列表的详细信息,请单击info按钮。",
|
||||||
"GeneralSettings": "通用设置",
|
"GeneralSettings": "通用设置",
|
||||||
@@ -164,7 +165,7 @@
|
|||||||
"ImportedTo": "导入到",
|
"ImportedTo": "导入到",
|
||||||
"Importing": "导入中",
|
"Importing": "导入中",
|
||||||
"IncludeHealthWarningsHelpText": "包含健康度警告",
|
"IncludeHealthWarningsHelpText": "包含健康度警告",
|
||||||
"IncludeUnknownAuthorItemsHelpText": "显示队列中没有作者的项,这可能包括已移除的作者、书籍或Readarr类别中的任何其他内容",
|
"IncludeUnknownAuthorItemsHelpText": "显示队列中没有电影的项目,这可能包括被删除的电影或Radarr类别中的任何其他内容",
|
||||||
"IncludeUnmonitored": "包含未监控的",
|
"IncludeUnmonitored": "包含未监控的",
|
||||||
"Indexer": "索引器",
|
"Indexer": "索引器",
|
||||||
"IndexerPriority": "索引器优先级",
|
"IndexerPriority": "索引器优先级",
|
||||||
@@ -292,7 +293,7 @@
|
|||||||
"RequiredHelpText": "发布的影片必须至少包含一个这些项目(不区分大小写)",
|
"RequiredHelpText": "发布的影片必须至少包含一个这些项目(不区分大小写)",
|
||||||
"RequiredPlaceHolder": "添加新限制",
|
"RequiredPlaceHolder": "添加新限制",
|
||||||
"RescanAfterRefreshHelpTextWarning": "当没有设置为“总是”时,Radarr将不会自动检测文件的更改",
|
"RescanAfterRefreshHelpTextWarning": "当没有设置为“总是”时,Radarr将不会自动检测文件的更改",
|
||||||
"RescanAuthorFolderAfterRefresh": "刷新后重新扫描作者文件夹",
|
"RescanAuthorFolderAfterRefresh": "刷新后重新扫描影片文件夹",
|
||||||
"Reset": "重置",
|
"Reset": "重置",
|
||||||
"ResetAPIKey": "重置API Key",
|
"ResetAPIKey": "重置API Key",
|
||||||
"ResetAPIKeyMessageText": "您确定要重置您的API密钥吗?",
|
"ResetAPIKeyMessageText": "您确定要重置您的API密钥吗?",
|
||||||
@@ -433,8 +434,8 @@
|
|||||||
"WeekColumnHeader": "日期格式",
|
"WeekColumnHeader": "日期格式",
|
||||||
"Year": "年",
|
"Year": "年",
|
||||||
"YesCancel": "是,取消",
|
"YesCancel": "是,取消",
|
||||||
"20MinutesTwenty": "20分钟: {0}",
|
"20MinutesTwenty": "60分钟: {0}",
|
||||||
"45MinutesFourtyFive": "45分钟: {0}",
|
"45MinutesFourtyFive": "60分钟: {0}",
|
||||||
"60MinutesSixty": "60分钟: {0}",
|
"60MinutesSixty": "60分钟: {0}",
|
||||||
"APIKey": "API Key",
|
"APIKey": "API Key",
|
||||||
"About": "关于",
|
"About": "关于",
|
||||||
@@ -595,7 +596,7 @@
|
|||||||
"ShowUnknownAuthorItems": "显示未知影片条目",
|
"ShowUnknownAuthorItems": "显示未知影片条目",
|
||||||
"AllowFingerprintingHelpText": "利用指纹技术提高航迹匹配的准确性",
|
"AllowFingerprintingHelpText": "利用指纹技术提高航迹匹配的准确性",
|
||||||
"AllowFingerprintingHelpTextWarning": "这需要读取部分文件,这将减慢扫描速度,并可能导致磁盘或网络活动频繁。",
|
"AllowFingerprintingHelpTextWarning": "这需要读取部分文件,这将减慢扫描速度,并可能导致磁盘或网络活动频繁。",
|
||||||
"AuthorFolderFormat": "作者文件夹格式",
|
"AuthorFolderFormat": "艺术家文件夹格式",
|
||||||
"Authors": "作者",
|
"Authors": "作者",
|
||||||
"BackupIntervalHelpText": "备份 Lidarr 数据库和设置的时间间隔",
|
"BackupIntervalHelpText": "备份 Lidarr 数据库和设置的时间间隔",
|
||||||
"Term": "项",
|
"Term": "项",
|
||||||
@@ -603,279 +604,5 @@
|
|||||||
"MetadataProfiles": "元数据配置",
|
"MetadataProfiles": "元数据配置",
|
||||||
"InstanceName": "中文",
|
"InstanceName": "中文",
|
||||||
"InstanceNameHelpText": "选项卡及日志应用名称",
|
"InstanceNameHelpText": "选项卡及日志应用名称",
|
||||||
"AddList": "添加列表",
|
"AddList": "添加列表"
|
||||||
"BookList": "书籍清单",
|
|
||||||
"Books": "书籍",
|
|
||||||
"BookFileCountBookCountTotalTotalBookCountInterp": "{0} / {1} (全部: {2})",
|
|
||||||
"BookFileCounttotalBookCountBooksDownloadedInterp": "{0}/{1} 书籍已下载",
|
|
||||||
"BookFilesCountMessage": "无书籍文件",
|
|
||||||
"AutomaticallySwitchEdition": "自动切换版本",
|
|
||||||
"AllAuthorBooks": "该作者所有书籍",
|
|
||||||
"AllBooks": "所有书籍",
|
|
||||||
"AllowedLanguages": "允许的语言",
|
|
||||||
"Book": "书籍",
|
|
||||||
"BookIndex": "书籍索引",
|
|
||||||
"CalibreContentServerText": "使用Calibre内容服务器(非 Calibre Web)允许Readarr将书籍添加到您的Calibre库,并触发格式之间的转换",
|
|
||||||
"CalibreNotCalibreWeb": "Readarr可以与Calibre内容服务器相接。无法使用Calibre-Web,两者不同。",
|
|
||||||
"CatalogNumber": "目录编号",
|
|
||||||
"ConvertToFormat": "转换为格式",
|
|
||||||
"DataAllBooks": "监控所有书籍",
|
|
||||||
"DataExistingBooks": "监控书籍有文件或尚未发布",
|
|
||||||
"DataFirstBook": "监控第一本书。其他所有书籍都将被忽略",
|
|
||||||
"DataFuturebooks": "监控尚未发布的书籍",
|
|
||||||
"DataLatestBook": "监控最新书籍及未来的书籍",
|
|
||||||
"DataListMonitorAll": "监控作者们及导入列表中每个作者的所有书籍",
|
|
||||||
"DataListMonitorNone": "不要监控作者或书籍",
|
|
||||||
"DataListMonitorSpecificBook": "监控作者,但只监控明确包含在列表中的书籍",
|
|
||||||
"DataMissingBooks": "监控没有文件或尚未发布的书籍",
|
|
||||||
"DataNewAllBooks": "监控所有新书籍",
|
|
||||||
"DataNewNone": "不要监控任何新书籍",
|
|
||||||
"DataNone": "不会监控任何书籍",
|
|
||||||
"DataNewBooks": "监控现有最新书籍之后发布的新书",
|
|
||||||
"DefaultQualityProfileIdHelpText": "此文件夹中检测到的作者的默认质量配置",
|
|
||||||
"DefaultMetadataProfileIdHelpText": "此文件夹中检测到的作者的默认元数据配置文件",
|
|
||||||
"DefaultMonitorOptionHelpText": "对于在此文件夹中检测到的作者,应在初始添加时监控哪些书籍",
|
|
||||||
"DefaultReadarrTags": "默认Readarr标签",
|
|
||||||
"DefaultTagsHelpText": "对于在此文件夹中检测到的作者使用默认Readarr标签",
|
|
||||||
"DeleteBookFile": "删除书籍文件",
|
|
||||||
"DeleteFilesHelpText": "删除书籍文件及作者文件夹",
|
|
||||||
"DeleteImportList": "删除导入的列表",
|
|
||||||
"DeleteMetadataProfile": "删除元数据配置",
|
|
||||||
"DeleteRootFolder": "删除根目录",
|
|
||||||
"DiscNumber": "磁盘编号",
|
|
||||||
"Development": "开发",
|
|
||||||
"DiscCount": "磁盘数量",
|
|
||||||
"DownloadPropersAndRepacksHelpTexts2": "使用“不要偏好”按首字母而不是专有/重新打包进行排序",
|
|
||||||
"EditionsHelpText": "修改这本书的版本",
|
|
||||||
"EmbedMetadataHelpText": "告知Calibre将元数据写入实际的图书文件中",
|
|
||||||
"EmbedMetadataInBookFiles": "将元数据写入书籍文件",
|
|
||||||
"EditBook": "编辑书籍",
|
|
||||||
"EditList": "编辑列表",
|
|
||||||
"EnableAutomaticAddHelpText": "当通过UI或Readarr执行同步时,将作者/书籍添加到Readarr",
|
|
||||||
"EnabledHelpText": "检查以启用发布配置文件",
|
|
||||||
"FileDetails": "文件详情",
|
|
||||||
"FilterPlaceHolder": "过滤书籍",
|
|
||||||
"FilterAuthor": "过滤作者",
|
|
||||||
"FirstBook": "第一本书籍",
|
|
||||||
"ForeignId": "国外 ID",
|
|
||||||
"ForeignIdHelpText": "作者/书籍的Musicbrainz ID排除",
|
|
||||||
"FutureBooks": "未来书籍",
|
|
||||||
"FutureDays": "未来天数",
|
|
||||||
"FutureDaysHelpText": "iCal订阅地址等待天数",
|
|
||||||
"GoToAuthorListing": "转到作者列表",
|
|
||||||
"HasMonitoredBooksNoMonitoredBooksForThisAuthor": "此作者没有受监控的书籍",
|
|
||||||
"ImportListSettings": "常规导入列表设置",
|
|
||||||
"ImportListSpecificSettings": "导入列表特定设置",
|
|
||||||
"IndexerIdHelpText": "指定配置文件应用于哪个索引器",
|
|
||||||
"MusicbrainzId": "Musicbrainz ID",
|
|
||||||
"MusicBrainzRecordingID": "MusicBrainz 唱片ID",
|
|
||||||
"MusicBrainzReleaseID": "MusicBrainz发行ID",
|
|
||||||
"MusicBrainzAuthorID": "MusicBrainz作者ID",
|
|
||||||
"OnBookDelete": "在书籍删除时",
|
|
||||||
"OnBookTagUpdate": "在书籍标签更新时",
|
|
||||||
"OnDownloadFailure": "在下载失败时",
|
|
||||||
"PastDays": "过去天数",
|
|
||||||
"SearchBook": "搜索书籍",
|
|
||||||
"SearchBoxPlaceHolder": "例: War and Peace, goodreads:656, isbn:067003469X, asin:B00JCDK5ME",
|
|
||||||
"TrackNumber": "跟踪编号",
|
|
||||||
"RootFolderPathHelpText": "根目录文件夹列表项需添加",
|
|
||||||
"SelectedCountAuthorsSelectedInterp": "{0} 作者选中",
|
|
||||||
"SearchForNewItems": "搜索新项目",
|
|
||||||
"SearchMonitored": "搜索已监控",
|
|
||||||
"ShowBookCount": "显示书籍序号",
|
|
||||||
"ShowBannersHelpText": "显示横幅而不是名称",
|
|
||||||
"ShowLastBook": "显示最后书籍",
|
|
||||||
"SkipPartBooksAndSets": "跳过节选和系列",
|
|
||||||
"StatusEndedContinuing": "仍在继续",
|
|
||||||
"UpdatingIsDisabledInsideADockerContainerUpdateTheContainerImageInstead": "更新在docker容器内被禁用. 改为更新容器映像。",
|
|
||||||
"WatchLibraryForChangesHelpText": "当根目录文件夹中的文件发生变化时,会自动重新扫描",
|
|
||||||
"WatchRootFoldersForFileChanges": "监测根目录文件夹的文件更改",
|
|
||||||
"EditAuthor": "编辑作者",
|
|
||||||
"EndedAllBooksDownloaded": "完成(所以书籍已下载)",
|
|
||||||
"EntityName": "实体名称",
|
|
||||||
"ShowBanners": "显示横幅",
|
|
||||||
"LogSQL": "Log SQL",
|
|
||||||
"CalibreSettings": "Calibre设置",
|
|
||||||
"Country": "国家",
|
|
||||||
"Iso639-3": "ISO 639-3语言代码,或“null”,逗点分离",
|
|
||||||
"ExistingItems": "现有项目",
|
|
||||||
"ExistingBooks": "现有书籍",
|
|
||||||
"ExistingTagsScrubbed": "已删除现有标签",
|
|
||||||
"ImportFailures": "导入失败",
|
|
||||||
"IgnoreDeletedBooks": "忽略已删除的书籍",
|
|
||||||
"WriteTagsAll": "所有文件;仅限初始导入",
|
|
||||||
"WriteTagsNew": "仅适用于新下载",
|
|
||||||
"LoadingEditionsFailed": "加载版本失败",
|
|
||||||
"ManualImportSelectEdition": "手动导入 - 选择版本",
|
|
||||||
"MassBookSearch": "批量书籍搜索",
|
|
||||||
"MetadataProviderSource": "元数据提供源",
|
|
||||||
"MetadataSource": "元数据源",
|
|
||||||
"MetadataSourceHelpText": "备选元数据源(默认为空)",
|
|
||||||
"MinimumPopularity": "最低人气",
|
|
||||||
"MinPagesHelpText": "忽略页数比这少的书",
|
|
||||||
"MissingBooksAuthorMonitored": "书籍丢失(已监控作者)",
|
|
||||||
"MissingBooks": "书籍丢失",
|
|
||||||
"MissingBooksAuthorNotMonitored": "丢失书籍(未监控作者)",
|
|
||||||
"MonitorAuthor": "监控作者",
|
|
||||||
"MonitorBook": "监控书籍",
|
|
||||||
"MonitoredAuthorIsMonitored": "作者已监控",
|
|
||||||
"MonitoredAuthorIsUnmonitored": "作者未监控",
|
|
||||||
"MonitoredHelpText": "Readarr将搜索及下载书籍",
|
|
||||||
"MonitorExistingBooks": "监控现有书籍",
|
|
||||||
"Monitoring": "监控中",
|
|
||||||
"MonitoringOptions": "监控选项",
|
|
||||||
"MonitoringOptionsHelpText": "添加作者后应该监控哪些书籍(一次性调整)",
|
|
||||||
"MonitorNewBooks": "监控新书",
|
|
||||||
"MonitorNewItems": "监控新书",
|
|
||||||
"MonitorNewItemsHelpText": "哪些新书应被监控",
|
|
||||||
"MusicBrainzBookID": "MusicBrainz书籍ID",
|
|
||||||
"MusicBrainzTrackID": "MusicBrainz曲目ID",
|
|
||||||
"NameLastFirst": "名姓",
|
|
||||||
"NameStyle": "作者姓名样式",
|
|
||||||
"NETCore": ".NET Core",
|
|
||||||
"NewBooks": "新书",
|
|
||||||
"NoName": "不显示名称",
|
|
||||||
"OnAuthorDeleteHelpText": "在作者删除时",
|
|
||||||
"OnBookDeleteHelpText": "在书籍删除时",
|
|
||||||
"OnDownloadFailureHelpText": "在下载失败时",
|
|
||||||
"OnImportFailureHelpText": "在导入失败时",
|
|
||||||
"OnReleaseImportHelpText": "在发行导入时",
|
|
||||||
"Other": "其他",
|
|
||||||
"PastDaysHelpText": "iCal订阅地址 过去天数",
|
|
||||||
"PathHelpText": "根目录文件夹包含您的书籍库",
|
|
||||||
"PortHelpText": "Calibre内容服务器端口",
|
|
||||||
"PreferredHelpTexts1": "发行将优先根据每个期间评分(不区分大小写)",
|
|
||||||
"PreferredHelpTexts2": "高得分将更优先",
|
|
||||||
"PreferredHelpTexts3": "低分将不优先",
|
|
||||||
"PreviewRetag": "预览重新标记",
|
|
||||||
"QualityProfileIdHelpText": "质量配置列表项应该被添加",
|
|
||||||
"ReadarrSupportsMultipleListsForImportingBooksAndAuthorsIntoTheDatabase": "Readarr支持将书籍和作者导入数据库的多个列表。",
|
|
||||||
"RefreshAuthor": "刷新作者",
|
|
||||||
"RefreshBook": "刷新书籍",
|
|
||||||
"RefreshInformation": "刷新信息",
|
|
||||||
"ReleaseProfiles": "刷新配置文件",
|
|
||||||
"RenameBooks": "重命名书籍",
|
|
||||||
"RenameFiles": "重命名文件",
|
|
||||||
"RestartRequiredHelpTextWarning": "需要重新启动才能生效",
|
|
||||||
"SearchForAllMissingBooks": "搜索所有丢失的书籍",
|
|
||||||
"SendMetadataToCalibre": "发送元数据至Calibre",
|
|
||||||
"SeriesNumber": "系列编号",
|
|
||||||
"SeriesTotal": "系列 ({0})",
|
|
||||||
"SetReadarrTags": "设置Readarr标签",
|
|
||||||
"ShouldMonitorExistingHelpText": "自动监控此列表中已经在Readarr中的书籍",
|
|
||||||
"ShouldMonitorHelpText": "监控从此列表中添加的新作者和书籍",
|
|
||||||
"SkipBooksWithMissingReleaseDate": "跳过缺少发行日期的书籍",
|
|
||||||
"SkipBooksWithNoISBNOrASIN": "跳过没有ISBN或ASIN的书籍",
|
|
||||||
"SkipRedownload": "跳过重新下载",
|
|
||||||
"SkipSecondarySeriesBooks": "跳过重版系列书籍",
|
|
||||||
"StatusEndedDeceased": "已故",
|
|
||||||
"TagsHelpText": "适用于至少一个匹配标签的作者,留空以适用于所有作者",
|
|
||||||
"TagsSettingsSummary": "管理作者、配置文件、限制和通知标签",
|
|
||||||
"Test": "测试",
|
|
||||||
"TheFollowingFilesWillBeDeleted": "以下文件将被删除:",
|
|
||||||
"TrackTitle": "跟踪标题",
|
|
||||||
"UnableToLoadMetadataProviderSettings": "无法加载元数据源设置",
|
|
||||||
"UnmappedFiles": "未映射文件",
|
|
||||||
"UpdateCovers": "更新封面",
|
|
||||||
"UpdateCoversHelpText": "在Calibre里设置书籍封面,以匹配Readarr中的封面",
|
|
||||||
"UrlBaseHelpText": "添加Calibre url,例 http://[主机/ip]:[端口]/[urlBase]",
|
|
||||||
"UseCalibreContentServer": "使用Calibre内容服务器",
|
|
||||||
"UsernameHelpText": "Calibre内容服务器用户名",
|
|
||||||
"WriteAudioTagsScrub": "覆盖现有标签",
|
|
||||||
"WriteBookTagsHelpTextWarning": "选择“所有文件”将在导入时更改现有文件。",
|
|
||||||
"WriteMetadataTags": "编写元数据标签",
|
|
||||||
"WriteTagsSync": "所有文件;与Goodreads保持同步",
|
|
||||||
"ShowName": "显示名称",
|
|
||||||
"AddedAuthorSettings": "已添加作者设置",
|
|
||||||
"Continuing": "仍在继续",
|
|
||||||
"ContinuingMoreBooksAreExpected": "预计会有更多书籍",
|
|
||||||
"ContinuingNoAdditionalBooksAreExpected": "预计不会有其他书籍",
|
|
||||||
"ProfilesSettingsSummary": "质量、元数据、延迟、发行配置",
|
|
||||||
"AuthorIndex": "作者索引",
|
|
||||||
"BookEditor": "书籍编辑",
|
|
||||||
"CalibreUsername": "Calibre用户名",
|
|
||||||
"ChownGroup": "修改组权限",
|
|
||||||
"ContinuingAllBooksDownloaded": "仍在继续(所有书籍已下载)",
|
|
||||||
"EnableProfile": "启用配置文件",
|
|
||||||
"IfYouDontAddAnImportListExclusionAndTheAuthorHasAMetadataProfileOtherThanNoneThenThisBookMayBeReaddedDuringTheNextAuthorRefresh": "如果您不添加导入列表例外,并且作者元数据配置不是“无”,那这本书可能会在下次作者刷新是重新添加",
|
|
||||||
"IncludePreferredWhenRenaming": "重命名时包括首选",
|
|
||||||
"IndexerIdHelpTextWarning": "使用带有首字母的特定索引器可能会导致复制版本被抓取",
|
|
||||||
"MassBookSearchWarning": "您确定要对{0}本书进行批量书籍搜索吗?",
|
|
||||||
"MediaManagementSettingsSummary": "命名、文件管理设置、根目录文件夹",
|
|
||||||
"MetadataConsumers": "用户元数据",
|
|
||||||
"MetadataProfileIdHelpText": "元数据配置文件列表项应添加",
|
|
||||||
"MetadataSettingsSummary": "在导入书籍或刷新作者时创建元数据文件",
|
|
||||||
"MinimumPages": "最小页面",
|
|
||||||
"MinPopularityHelpText": "人气是平均评分*票",
|
|
||||||
"MonitorBookExistingOnlyWarning": "这是对每本书籍的监控设置的一次性调整 使用作者/编辑下的选项来控制新添加的书籍将如何",
|
|
||||||
"NameFirstLast": "姓名",
|
|
||||||
"NoHistoryBlocklist": "没有历史黑名单列表",
|
|
||||||
"OnAuthorDelete": "在作者删除时",
|
|
||||||
"OnBookRetagHelpText": "在书籍重加标签时",
|
|
||||||
"OnImportFailure": "在导入失败时",
|
|
||||||
"OnReleaseImport": "在发行导入时",
|
|
||||||
"OutputFormatHelpText": "可选择让Calibre在导入时转换为其他格式, 逗点分隔列表。",
|
|
||||||
"PasswordHelpText": "Calibre内容服务器密码",
|
|
||||||
"PathHelpTextWarning": "这必须与下载客户端放置文件的目录不同",
|
|
||||||
"SearchForAllCutoffUnmetBooks": "搜索所有Cutoff Unmet书籍",
|
|
||||||
"SearchForMonitoredBooks": "搜索监控中的书籍",
|
|
||||||
"ShouldMonitorExisting": "监控现有书籍",
|
|
||||||
"ShouldSearchHelpText": "在索引器中搜索新添加的项目, 小心使用长列表。",
|
|
||||||
"ShowTitleHelpText": "在海报下显示作者姓名",
|
|
||||||
"SkipredownloadHelpText": "阻止Readarr尝试为删除的项目下载替代版本",
|
|
||||||
"SpecificBook": "特定书籍",
|
|
||||||
"TheBooksFilesWillBeDeleted": "此书籍文件将删除。",
|
|
||||||
"TooManyBooks": "丢失还是过多书籍?修改或创建新的",
|
|
||||||
"TotalBookCountBooksTotalBookFileCountBooksWithFilesInterp": "共{0}本书籍 . {1} 有文件.",
|
|
||||||
"UseSSL": "使用SSL",
|
|
||||||
"UseSslHelpText": "使用SSL连接到Calibre内容服务器",
|
|
||||||
"WriteAudioTags": "使用元数据标记音频文件",
|
|
||||||
"WriteAudioTagsScrubHelp": "从文件中删除现有标签,只留下Readarr添加的标签。",
|
|
||||||
"Database": "数据库",
|
|
||||||
"FilesTotal": "文件 ({0})",
|
|
||||||
"HideBooks": "隐藏书籍",
|
|
||||||
"HostHelpText": "Calibre内容服务器主机",
|
|
||||||
"IgnoredMetaHelpText": "如果书籍包含一个/多个术语(不区分大小写)将被忽略",
|
|
||||||
"ImportListExclusions": "导入列表例外",
|
|
||||||
"ImportLists": "导入列表",
|
|
||||||
"IndexerIdvalue0IncludeInPreferredWordsRenamingFormat": "以 {Preferred Words} 重命名格式包含",
|
|
||||||
"IndexerIdvalue0OnlySupportedWhenIndexerIsSetToAll": "仅当索引器设置为 (全部)时才支持",
|
|
||||||
"ISBN": "ISBN",
|
|
||||||
"IsCalibreLibraryHelpText": "使用Calibre内容服务器操作库",
|
|
||||||
"IsExpandedHideBooks": "隐藏书籍",
|
|
||||||
"IsExpandedHideFileInfo": "隐藏文件信息",
|
|
||||||
"IsExpandedShowBooks": "显示书籍",
|
|
||||||
"IsExpandedShowFileInfo": "显示文件信息",
|
|
||||||
"IsInUseCantDeleteAMetadataProfileThatIsAttachedToAnAuthorOrImportList": "无法删除元数据配置文件,其已经附加到作者或为导入列表",
|
|
||||||
"IsInUseCantDeleteAQualityProfileThatIsAttachedToAnAuthorOrImportList": "无法删除质量配置文件,其已附加到作者或为导入列表",
|
|
||||||
"IsShowingMonitoredMonitorSelected": "监控选中的",
|
|
||||||
"IsShowingMonitoredUnmonitorSelected": "未监控选中的",
|
|
||||||
"ItsEasyToAddANewAuthorOrBookJustStartTypingTheNameOfTheItemYouWantToAdd": "添加新作者或书籍很容易,只需开始输入要添加的条目的名称",
|
|
||||||
"LatestBook": "最新书籍",
|
|
||||||
"LibraryHelpText": "Calibre内容服务器库名 默认留空",
|
|
||||||
"ListsSettingsSummary": "导入列表",
|
|
||||||
"AddImportListExclusionHelpText": "通过导入列表或作者刷新 防止书籍添加到Readarr",
|
|
||||||
"ManualDownload": "手动下载",
|
|
||||||
"BookMonitoring": "书籍监控",
|
|
||||||
"BookNaming": "书籍命名",
|
|
||||||
"BooksTotal": "书籍 ({0})",
|
|
||||||
"BookTitle": "书籍名称",
|
|
||||||
"CalibreContentServer": "Calibre内容服务器",
|
|
||||||
"CalibreHost": "Calibre主机",
|
|
||||||
"CalibreLibrary": "Calibre库",
|
|
||||||
"CalibreMetadata": "Calibre元数据",
|
|
||||||
"CalibreOutputFormat": "Calibre输出格式",
|
|
||||||
"CalibreOutputProfile": "Calibre输出配置文件",
|
|
||||||
"CalibrePassword": "Calibre密码",
|
|
||||||
"CalibrePort": "Calibre端口",
|
|
||||||
"CalibreUrlBase": "Calibre基本URL",
|
|
||||||
"BookStudio": "书籍出版商",
|
|
||||||
"AudioFileMetadata": "将元数据写入音频文件",
|
|
||||||
"AuthorEditor": "作者编辑",
|
|
||||||
"AuthorNameHelpText": "要排除的作者/书籍的名称(任何有涵义的均可)",
|
|
||||||
"CollapseMultipleBooks": "折叠多本书籍",
|
|
||||||
"CollapseMultipleBooksHelpText": "折叠在同日发行的多本书籍",
|
|
||||||
"ASIN": "亚马逊标准识别码",
|
|
||||||
"NoTagsHaveBeenAddedYet": "尚未添加标签,添加标签以链接具有延迟配置文件、限制或通知的作者。单击{0}以了解有关Readarr中标签的更多信息。",
|
|
||||||
"BindAddressHelpText": "有效的 IPv4 地址或以'*'代表所有接口"
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,5 @@
|
|||||||
"Actions": "執行",
|
"Actions": "執行",
|
||||||
"All": "全部",
|
"All": "全部",
|
||||||
"AddingTag": "新增標籤",
|
"AddingTag": "新增標籤",
|
||||||
"Analytics": "分析",
|
"Analytics": "分析"
|
||||||
"AddList": "新增列表"
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
using NzbDrone.Common.Disk;
|
using NzbDrone.Common.Disk;
|
||||||
using NzbDrone.Common.EnvironmentInfo;
|
using NzbDrone.Common.EnvironmentInfo;
|
||||||
using SixLabors.ImageSharp;
|
using SixLabors.ImageSharp;
|
||||||
using SixLabors.ImageSharp.Formats.Jpeg;
|
using SixLabors.ImageSharp.Formats.Jpeg;
|
||||||
@@ -21,6 +21,12 @@ namespace NzbDrone.Core.MediaCover
|
|||||||
{
|
{
|
||||||
_diskProvider = diskProvider;
|
_diskProvider = diskProvider;
|
||||||
|
|
||||||
|
// Random segfaults on mono 5.0 and 5.4
|
||||||
|
if (PlatformInfo.IsMono && platformInfo.Version < new System.Version(5, 8))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
_enabled = true;
|
_enabled = true;
|
||||||
|
|
||||||
// More conservative memory allocation
|
// More conservative memory allocation
|
||||||
|
|||||||
@@ -9,7 +9,6 @@ using NzbDrone.Common.Extensions;
|
|||||||
using NzbDrone.Common.Processes;
|
using NzbDrone.Common.Processes;
|
||||||
using NzbDrone.Common.Serializer;
|
using NzbDrone.Common.Serializer;
|
||||||
using NzbDrone.Core.Books;
|
using NzbDrone.Core.Books;
|
||||||
using NzbDrone.Core.HealthCheck;
|
|
||||||
using NzbDrone.Core.ThingiProvider;
|
using NzbDrone.Core.ThingiProvider;
|
||||||
using NzbDrone.Core.Validation;
|
using NzbDrone.Core.Validation;
|
||||||
|
|
||||||
@@ -206,7 +205,7 @@ namespace NzbDrone.Core.Notifications.CustomScript
|
|||||||
var environmentVariables = new StringDictionary();
|
var environmentVariables = new StringDictionary();
|
||||||
|
|
||||||
environmentVariables.Add("Readarr_EventType", "HealthIssue");
|
environmentVariables.Add("Readarr_EventType", "HealthIssue");
|
||||||
environmentVariables.Add("Readarr_Health_Issue_Level", Enum.GetName(typeof(HealthCheckResult), healthCheck.Type));
|
environmentVariables.Add("Readarr_Health_Issue_Level", nameof(healthCheck.Type));
|
||||||
environmentVariables.Add("Readarr_Health_Issue_Message", healthCheck.Message);
|
environmentVariables.Add("Readarr_Health_Issue_Message", healthCheck.Message);
|
||||||
environmentVariables.Add("Readarr_Health_Issue_Type", healthCheck.Source.Name);
|
environmentVariables.Add("Readarr_Health_Issue_Type", healthCheck.Source.Name);
|
||||||
environmentVariables.Add("Readarr_Health_Issue_Wiki", healthCheck.WikiUrl.ToString() ?? string.Empty);
|
environmentVariables.Add("Readarr_Health_Issue_Wiki", healthCheck.WikiUrl.ToString() ?? string.Empty);
|
||||||
|
|||||||
@@ -1,74 +0,0 @@
|
|||||||
using System.Collections.Generic;
|
|
||||||
using System.IO;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Net.Sockets;
|
|
||||||
using FluentValidation.Results;
|
|
||||||
using NLog;
|
|
||||||
using NzbDrone.Common.Extensions;
|
|
||||||
|
|
||||||
namespace NzbDrone.Core.Notifications.Kavita;
|
|
||||||
|
|
||||||
public class Kavita : NotificationBase<KavitaSettings>
|
|
||||||
{
|
|
||||||
private readonly IKavitaService _kavitaService;
|
|
||||||
private readonly Logger _logger;
|
|
||||||
|
|
||||||
public Kavita(IKavitaService kavitaService, Logger logger)
|
|
||||||
{
|
|
||||||
_kavitaService = kavitaService;
|
|
||||||
_logger = logger;
|
|
||||||
}
|
|
||||||
|
|
||||||
public override string Link => "https://www.kavitareader.com/";
|
|
||||||
|
|
||||||
public override void OnReleaseImport(BookDownloadMessage message)
|
|
||||||
{
|
|
||||||
var allPaths = message.BookFiles.Select(v => v.Path).Distinct();
|
|
||||||
var path = Directory.GetParent(allPaths.First())?.FullName;
|
|
||||||
Notify(Settings, BOOK_DOWNLOADED_TITLE_BRANDED, path);
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void OnBookDelete(BookDeleteMessage deleteMessage)
|
|
||||||
{
|
|
||||||
var allPaths = deleteMessage.Book.BookFiles.Value.Select(v => v.Path).Distinct();
|
|
||||||
var path = Directory.GetParent(allPaths.First())?.FullName;
|
|
||||||
Notify(Settings, BOOK_FILE_DELETED_TITLE_BRANDED, path);
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void OnBookFileDelete(BookFileDeleteMessage message)
|
|
||||||
{
|
|
||||||
Notify(Settings, BOOK_FILE_DELETED_TITLE_BRANDED, Directory.GetParent(message.BookFile.Path)?.FullName);
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void OnBookRetag(BookRetagMessage message)
|
|
||||||
{
|
|
||||||
Notify(Settings, BOOK_RETAGGED_TITLE_BRANDED, Directory.GetParent(message.BookFile.Path)?.FullName);
|
|
||||||
}
|
|
||||||
|
|
||||||
public override string Name => "Kavita";
|
|
||||||
|
|
||||||
public override ValidationResult Test()
|
|
||||||
{
|
|
||||||
var failures = new List<ValidationFailure>();
|
|
||||||
|
|
||||||
failures.AddIfNotNull(_kavitaService.Test(Settings, "Success! Kavita has been successfully configured!"));
|
|
||||||
|
|
||||||
return new ValidationResult(failures);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void Notify(KavitaSettings settings, string header, string message)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
if (Settings.Notify)
|
|
||||||
{
|
|
||||||
_kavitaService.Notify(Settings, $"{header} - {message}");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (SocketException ex)
|
|
||||||
{
|
|
||||||
var logMessage = $"Unable to connect to Subsonic Host: {Settings.Host}:{Settings.Port}";
|
|
||||||
_logger.Debug(ex, logMessage);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
namespace NzbDrone.Core.Notifications.Kavita;
|
|
||||||
|
|
||||||
public class KavitaAuthenticationException : KavitaException
|
|
||||||
{
|
|
||||||
public KavitaAuthenticationException(string message)
|
|
||||||
: base(message)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public KavitaAuthenticationException(string message, params object[] args)
|
|
||||||
: base(message, args)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
using System.Text.Json.Serialization;
|
|
||||||
|
|
||||||
namespace NzbDrone.Core.Notifications.Kavita;
|
|
||||||
|
|
||||||
public class KavitaAuthenticationResult
|
|
||||||
{
|
|
||||||
[JsonPropertyName("token")]
|
|
||||||
public string Token { get; set; }
|
|
||||||
[JsonPropertyName("apiKey")]
|
|
||||||
public string ApiKey { get; init; }
|
|
||||||
}
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
using NzbDrone.Common.Exceptions;
|
|
||||||
|
|
||||||
namespace NzbDrone.Core.Notifications.Kavita;
|
|
||||||
|
|
||||||
public class KavitaException : NzbDroneException
|
|
||||||
{
|
|
||||||
public KavitaException(string message)
|
|
||||||
: base(message)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public KavitaException(string message, params object[] args)
|
|
||||||
: base(message, args)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,56 +0,0 @@
|
|||||||
using System;
|
|
||||||
using FluentValidation.Results;
|
|
||||||
using NLog;
|
|
||||||
|
|
||||||
namespace NzbDrone.Core.Notifications.Kavita;
|
|
||||||
|
|
||||||
public interface IKavitaService
|
|
||||||
{
|
|
||||||
void Notify(KavitaSettings settings, string message);
|
|
||||||
ValidationFailure Test(KavitaSettings settings, string message);
|
|
||||||
}
|
|
||||||
|
|
||||||
public class KavitaService : IKavitaService
|
|
||||||
{
|
|
||||||
private readonly IKavitaServiceProxy _proxy;
|
|
||||||
private readonly Logger _logger;
|
|
||||||
|
|
||||||
public KavitaService(IKavitaServiceProxy proxy,
|
|
||||||
Logger logger)
|
|
||||||
{
|
|
||||||
_proxy = proxy;
|
|
||||||
_logger = logger;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Notify(KavitaSettings settings, string folderPath)
|
|
||||||
{
|
|
||||||
_proxy.Notify(settings, folderPath);
|
|
||||||
}
|
|
||||||
|
|
||||||
private string GetToken(KavitaSettings settings)
|
|
||||||
{
|
|
||||||
return _proxy.GetToken(settings);
|
|
||||||
}
|
|
||||||
|
|
||||||
public ValidationFailure Test(KavitaSettings settings, string message)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
_logger.Debug("Determining Authentication of Host: {0}", _proxy.GetBaseUrl(settings));
|
|
||||||
var token = GetToken(settings);
|
|
||||||
_logger.Debug("Token is: {0}", token);
|
|
||||||
}
|
|
||||||
catch (KavitaAuthenticationException ex)
|
|
||||||
{
|
|
||||||
_logger.Error(ex, "Unable to connect to Kavita Server");
|
|
||||||
return new ValidationFailure("ApiKey", "Incorrect ApiKey");
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
_logger.Error(ex, "Unable to connect to Kavita Server");
|
|
||||||
return new ValidationFailure("Host", "Unable to connect to Kavita Server");
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,87 +0,0 @@
|
|||||||
using System.Net.Http;
|
|
||||||
using System.Text.Json;
|
|
||||||
using NLog;
|
|
||||||
using NzbDrone.Common.EnvironmentInfo;
|
|
||||||
using NzbDrone.Common.Extensions;
|
|
||||||
using NzbDrone.Common.Http;
|
|
||||||
using NzbDrone.Common.Serializer;
|
|
||||||
|
|
||||||
namespace NzbDrone.Core.Notifications.Kavita;
|
|
||||||
|
|
||||||
public interface IKavitaServiceProxy
|
|
||||||
{
|
|
||||||
string GetBaseUrl(KavitaSettings settings, string relativePath = null);
|
|
||||||
void Notify(KavitaSettings settings, string message);
|
|
||||||
string GetToken(KavitaSettings settings);
|
|
||||||
}
|
|
||||||
|
|
||||||
public class KavitaServiceProxy : IKavitaServiceProxy
|
|
||||||
{
|
|
||||||
private readonly IHttpClient _httpClient;
|
|
||||||
private readonly Logger _logger;
|
|
||||||
|
|
||||||
public KavitaServiceProxy(IHttpClient httpClient, Logger logger)
|
|
||||||
{
|
|
||||||
_httpClient = httpClient;
|
|
||||||
_logger = logger;
|
|
||||||
}
|
|
||||||
|
|
||||||
public string GetBaseUrl(KavitaSettings settings, string relativePath = null)
|
|
||||||
{
|
|
||||||
var baseUrl = HttpRequestBuilder.BuildBaseUrl(settings.UseSsl, settings.Host, settings.Port, string.Empty);
|
|
||||||
baseUrl = HttpUri.CombinePath(baseUrl, relativePath);
|
|
||||||
|
|
||||||
return baseUrl;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Notify(KavitaSettings settings, string folderPath)
|
|
||||||
{
|
|
||||||
var request = GetKavitaServerRequest("library/scan-folder", HttpMethod.Post, settings);
|
|
||||||
request.Headers.ContentType = "application/json";
|
|
||||||
var postRequest = request.Build();
|
|
||||||
postRequest.SetContent(new
|
|
||||||
{
|
|
||||||
ApiKey = settings.ApiKey,
|
|
||||||
FolderPath = folderPath.Replace("/", "//")
|
|
||||||
}.ToJson());
|
|
||||||
|
|
||||||
var response = _httpClient.Post(postRequest);
|
|
||||||
_logger.Trace("Update response: {0}", string.IsNullOrEmpty(response.Content) ? "Success" : response.Content);
|
|
||||||
}
|
|
||||||
|
|
||||||
public string GetToken(KavitaSettings settings)
|
|
||||||
{
|
|
||||||
var request = GetKavitaServerRequest("plugin/authenticate", HttpMethod.Post, settings);
|
|
||||||
request.AddQueryParam("apiKey", settings.ApiKey)
|
|
||||||
.AddQueryParam("pluginName", BuildInfo.AppName);
|
|
||||||
var response = _httpClient.Execute(request.Build());
|
|
||||||
|
|
||||||
_logger.Trace("Authenticate response: {0}", response.Content);
|
|
||||||
|
|
||||||
var authResult = JsonSerializer.Deserialize<KavitaAuthenticationResult>(response.Content);
|
|
||||||
|
|
||||||
if (authResult == null)
|
|
||||||
{
|
|
||||||
throw new KavitaException("Could not authenticate with Kavita");
|
|
||||||
}
|
|
||||||
|
|
||||||
return authResult.Token;
|
|
||||||
}
|
|
||||||
|
|
||||||
private HttpRequestBuilder GetKavitaServerRequest(string resource, HttpMethod method, KavitaSettings settings)
|
|
||||||
{
|
|
||||||
var client = new HttpRequestBuilder(GetBaseUrl(settings, "api"));
|
|
||||||
|
|
||||||
client.Resource(resource);
|
|
||||||
|
|
||||||
if (settings.ApiKey.IsNotNullOrWhiteSpace())
|
|
||||||
{
|
|
||||||
client.Headers["x-kavita-apikey"] = settings.ApiKey;
|
|
||||||
client.Headers["x-kavita-plugin"] = BuildInfo.AppName;
|
|
||||||
}
|
|
||||||
|
|
||||||
client.Method = method;
|
|
||||||
|
|
||||||
return client;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,46 +0,0 @@
|
|||||||
using FluentValidation;
|
|
||||||
using NzbDrone.Core.Annotations;
|
|
||||||
using NzbDrone.Core.ThingiProvider;
|
|
||||||
using NzbDrone.Core.Validation;
|
|
||||||
|
|
||||||
namespace NzbDrone.Core.Notifications.Kavita;
|
|
||||||
|
|
||||||
public class KavitaSettingsValidator : AbstractValidator<KavitaSettings>
|
|
||||||
{
|
|
||||||
public KavitaSettingsValidator()
|
|
||||||
{
|
|
||||||
RuleFor(c => c.Host).ValidHost();
|
|
||||||
RuleFor(c => c.Port).InclusiveBetween(1, 65535);
|
|
||||||
RuleFor(c => c.ApiKey).NotEmpty();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class KavitaSettings : IProviderConfig
|
|
||||||
{
|
|
||||||
private static readonly KavitaSettingsValidator Validator = new KavitaSettingsValidator();
|
|
||||||
|
|
||||||
public KavitaSettings()
|
|
||||||
{
|
|
||||||
Port = 4040;
|
|
||||||
}
|
|
||||||
|
|
||||||
[FieldDefinition(0, Label = "Host")]
|
|
||||||
public string Host { get; set; }
|
|
||||||
|
|
||||||
[FieldDefinition(1, Label = "Port")]
|
|
||||||
public int Port { get; set; }
|
|
||||||
|
|
||||||
[FieldDefinition(2, Label = "API Key", Privacy = PrivacyLevel.ApiKey, HelpLink = "https://wiki.kavitareader.com/en/guides/settings/opds")]
|
|
||||||
public string ApiKey { get; set; }
|
|
||||||
|
|
||||||
[FieldDefinition(3, Label = "Use SSL", Type = FieldType.Checkbox, HelpText = "Connect to Kavita over HTTPS instead of HTTP")]
|
|
||||||
public bool UseSsl { get; set; }
|
|
||||||
|
|
||||||
[FieldDefinition(4, Label = "Update Library", Type = FieldType.Checkbox)]
|
|
||||||
public bool Notify { get; set; }
|
|
||||||
|
|
||||||
public NzbDroneValidationResult Validate()
|
|
||||||
{
|
|
||||||
return new NzbDroneValidationResult(Validator.Validate(this));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,10 +1,11 @@
|
|||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
using System.Collections.Specialized;
|
using System.Collections.Specialized;
|
||||||
|
using System.Net;
|
||||||
using FluentValidation.Results;
|
using FluentValidation.Results;
|
||||||
using NLog;
|
using NLog;
|
||||||
using NzbDrone.Common.Extensions;
|
using NzbDrone.Common.EnvironmentInfo;
|
||||||
using NzbDrone.Common.Http;
|
using NzbDrone.Common.Http;
|
||||||
using NzbDrone.Core.Configuration;
|
|
||||||
|
|
||||||
namespace NzbDrone.Core.Notifications.Notifiarr
|
namespace NzbDrone.Core.Notifications.Notifiarr
|
||||||
{
|
{
|
||||||
@@ -16,21 +17,27 @@ namespace NzbDrone.Core.Notifications.Notifiarr
|
|||||||
|
|
||||||
public class NotifiarrProxy : INotifiarrProxy
|
public class NotifiarrProxy : INotifiarrProxy
|
||||||
{
|
{
|
||||||
private const string URL = "https://notifiarr.com";
|
private const string URL = "https://notifiarr.com/notifier.php";
|
||||||
private readonly IHttpClient _httpClient;
|
private readonly IHttpClient _httpClient;
|
||||||
private readonly IConfigFileProvider _configFileProvider;
|
|
||||||
private readonly Logger _logger;
|
private readonly Logger _logger;
|
||||||
|
|
||||||
public NotifiarrProxy(IHttpClient httpClient, IConfigFileProvider configFileProvider, Logger logger)
|
public NotifiarrProxy(IHttpClient httpClient, Logger logger)
|
||||||
{
|
{
|
||||||
_httpClient = httpClient;
|
_httpClient = httpClient;
|
||||||
_configFileProvider = configFileProvider;
|
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SendNotification(StringDictionary message, NotifiarrSettings settings)
|
public void SendNotification(StringDictionary message, NotifiarrSettings settings)
|
||||||
{
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
ProcessNotification(message, settings);
|
ProcessNotification(message, settings);
|
||||||
|
}
|
||||||
|
catch (NotifiarrException ex)
|
||||||
|
{
|
||||||
|
_logger.Error(ex, "Unable to send notification");
|
||||||
|
throw new NotifiarrException("Unable to send notification");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public ValidationFailure Test(NotifiarrSettings settings)
|
public ValidationFailure Test(NotifiarrSettings settings)
|
||||||
@@ -43,14 +50,21 @@ namespace NzbDrone.Core.Notifications.Notifiarr
|
|||||||
SendNotification(variables, settings);
|
SendNotification(variables, settings);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
catch (NotifiarrException ex)
|
catch (HttpException ex)
|
||||||
{
|
{
|
||||||
return new ValidationFailure("APIKey", ex.Message);
|
if (ex.Response.StatusCode == HttpStatusCode.Unauthorized)
|
||||||
|
{
|
||||||
|
_logger.Error(ex, "API key is invalid: " + ex.Message);
|
||||||
|
return new ValidationFailure("APIKey", "API key is invalid");
|
||||||
|
}
|
||||||
|
|
||||||
|
_logger.Error(ex, "Unable to send test message: " + ex.Message);
|
||||||
|
return new ValidationFailure("APIKey", "Unable to send test notification");
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
_logger.Error(ex, ex.Message);
|
_logger.Error(ex, "Unable to send test notification: " + ex.Message);
|
||||||
return new ValidationFailure("", "Unable to send test notification. Check the log for more details.");
|
return new ValidationFailure("", "Unable to send test notification");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -58,10 +72,8 @@ namespace NzbDrone.Core.Notifications.Notifiarr
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var instanceName = _configFileProvider.InstanceName;
|
var requestBuilder = new HttpRequestBuilder(URL).Post();
|
||||||
var requestBuilder = new HttpRequestBuilder(URL + "/api/v1/notification/readarr").Post();
|
requestBuilder.AddFormParameter("api", settings.APIKey).Build();
|
||||||
requestBuilder.AddFormParameter("instanceName", instanceName).Build();
|
|
||||||
requestBuilder.SetHeader("X-API-Key", settings.APIKey);
|
|
||||||
|
|
||||||
foreach (string key in message.Keys)
|
foreach (string key in message.Keys)
|
||||||
{
|
{
|
||||||
@@ -74,31 +86,13 @@ namespace NzbDrone.Core.Notifications.Notifiarr
|
|||||||
}
|
}
|
||||||
catch (HttpException ex)
|
catch (HttpException ex)
|
||||||
{
|
{
|
||||||
var responseCode = ex.Response.StatusCode;
|
if (ex.Response.StatusCode == HttpStatusCode.BadRequest)
|
||||||
switch ((int)responseCode)
|
|
||||||
{
|
{
|
||||||
case 401:
|
_logger.Error(ex, "API key is invalid");
|
||||||
_logger.Error("Unauthorized", "HTTP 401 - API key is invalid");
|
throw;
|
||||||
throw new NotifiarrException("API key is invalid");
|
|
||||||
case 400:
|
|
||||||
_logger.Error("Invalid Request", "HTTP 400 - Unable to send notification. Ensure Readarr Integration is enabled & assigned a channel on Notifiarr");
|
|
||||||
throw new NotifiarrException("Unable to send notification. Ensure Readarr Integration is enabled & assigned a channel on Notifiarr");
|
|
||||||
case 502:
|
|
||||||
case 503:
|
|
||||||
case 504:
|
|
||||||
_logger.Error("Service Unavailable", "Unable to send notification. Service Unavailable");
|
|
||||||
throw new NotifiarrException("Unable to send notification. Service Unavailable", ex);
|
|
||||||
case 520:
|
|
||||||
case 521:
|
|
||||||
case 522:
|
|
||||||
case 523:
|
|
||||||
case 524:
|
|
||||||
_logger.Error(ex, "Cloudflare Related HTTP Error - Unable to send notification");
|
|
||||||
throw new NotifiarrException("Cloudflare Related HTTP Error - Unable to send notification", ex);
|
|
||||||
default:
|
|
||||||
_logger.Error(ex, "Unknown HTTP Error - Unable to send notification");
|
|
||||||
throw new NotifiarrException("Unknown HTTP Error - Unable to send notification", ex);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
throw new NotifiarrException("Unable to send notification", ex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,59 +0,0 @@
|
|||||||
using System.Collections.Generic;
|
|
||||||
using FluentValidation.Results;
|
|
||||||
using NzbDrone.Common.Extensions;
|
|
||||||
|
|
||||||
namespace NzbDrone.Core.Notifications.Ntfy
|
|
||||||
{
|
|
||||||
public class Ntfy : NotificationBase<NtfySettings>
|
|
||||||
{
|
|
||||||
private readonly INtfyProxy _proxy;
|
|
||||||
|
|
||||||
public Ntfy(INtfyProxy proxy)
|
|
||||||
{
|
|
||||||
_proxy = proxy;
|
|
||||||
}
|
|
||||||
|
|
||||||
public override string Name => "ntfy.sh";
|
|
||||||
|
|
||||||
public override string Link => "https://ntfy.sh/";
|
|
||||||
|
|
||||||
public override void OnGrab(GrabMessage grabMessage)
|
|
||||||
{
|
|
||||||
_proxy.SendNotification(BOOK_GRABBED_TITLE_BRANDED, grabMessage.Message, Settings);
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void OnReleaseImport(BookDownloadMessage message)
|
|
||||||
{
|
|
||||||
_proxy.SendNotification(BOOK_DOWNLOADED_TITLE_BRANDED, message.Message, Settings);
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void OnAuthorDelete(AuthorDeleteMessage deleteMessage)
|
|
||||||
{
|
|
||||||
_proxy.SendNotification(AUTHOR_DELETED_TITLE, deleteMessage.Message, Settings);
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void OnBookDelete(BookDeleteMessage deleteMessage)
|
|
||||||
{
|
|
||||||
_proxy.SendNotification(BOOK_DELETED_TITLE, deleteMessage.Message, Settings);
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void OnBookFileDelete(BookFileDeleteMessage deleteMessage)
|
|
||||||
{
|
|
||||||
_proxy.SendNotification(BOOK_FILE_DELETED_TITLE, deleteMessage.Message, Settings);
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void OnHealthIssue(HealthCheck.HealthCheck healthCheck)
|
|
||||||
{
|
|
||||||
_proxy.SendNotification(HEALTH_ISSUE_TITLE_BRANDED, healthCheck.Message, Settings);
|
|
||||||
}
|
|
||||||
|
|
||||||
public override ValidationResult Test()
|
|
||||||
{
|
|
||||||
var failures = new List<ValidationFailure>();
|
|
||||||
|
|
||||||
failures.AddIfNotNull(_proxy.Test(Settings));
|
|
||||||
|
|
||||||
return new ValidationResult(failures);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,18 +0,0 @@
|
|||||||
using System;
|
|
||||||
using NzbDrone.Common.Exceptions;
|
|
||||||
|
|
||||||
namespace NzbDrone.Core.Notifications.Ntfy
|
|
||||||
{
|
|
||||||
public class NtfyException : NzbDroneException
|
|
||||||
{
|
|
||||||
public NtfyException(string message)
|
|
||||||
: base(message)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public NtfyException(string message, Exception innerException, params object[] args)
|
|
||||||
: base(message, innerException, args)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
namespace NzbDrone.Core.Notifications.Ntfy
|
|
||||||
{
|
|
||||||
public enum NtfyPriority
|
|
||||||
{
|
|
||||||
Min = 1,
|
|
||||||
Low = 2,
|
|
||||||
Default = 3,
|
|
||||||
High = 4,
|
|
||||||
Max = 5
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,137 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Net;
|
|
||||||
using FluentValidation.Results;
|
|
||||||
using NLog;
|
|
||||||
using NzbDrone.Common.Extensions;
|
|
||||||
using NzbDrone.Common.Http;
|
|
||||||
|
|
||||||
namespace NzbDrone.Core.Notifications.Ntfy
|
|
||||||
{
|
|
||||||
public interface INtfyProxy
|
|
||||||
{
|
|
||||||
void SendNotification(string title, string message, NtfySettings settings);
|
|
||||||
|
|
||||||
ValidationFailure Test(NtfySettings settings);
|
|
||||||
}
|
|
||||||
|
|
||||||
public class NtfyProxy : INtfyProxy
|
|
||||||
{
|
|
||||||
private const string DEFAULT_PUSH_URL = "https://ntfy.sh";
|
|
||||||
|
|
||||||
private readonly IHttpClient _httpClient;
|
|
||||||
|
|
||||||
private readonly Logger _logger;
|
|
||||||
|
|
||||||
public NtfyProxy(IHttpClient httpClient, Logger logger)
|
|
||||||
{
|
|
||||||
_httpClient = httpClient;
|
|
||||||
_logger = logger;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void SendNotification(string title, string message, NtfySettings settings)
|
|
||||||
{
|
|
||||||
var error = false;
|
|
||||||
|
|
||||||
var serverUrl = settings.ServerUrl.IsNullOrWhiteSpace() ? NtfyProxy.DEFAULT_PUSH_URL : settings.ServerUrl;
|
|
||||||
|
|
||||||
foreach (var topic in settings.Topics)
|
|
||||||
{
|
|
||||||
var request = BuildTopicRequest(serverUrl, topic);
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
SendNotification(title, message, request, settings);
|
|
||||||
}
|
|
||||||
catch (NtfyException ex)
|
|
||||||
{
|
|
||||||
_logger.Error(ex, "Unable to send test message to {0}", topic);
|
|
||||||
error = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (error)
|
|
||||||
{
|
|
||||||
throw new NtfyException("Unable to send Ntfy notifications to all topics");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private HttpRequestBuilder BuildTopicRequest(string serverUrl, string topic)
|
|
||||||
{
|
|
||||||
var trimServerUrl = serverUrl.TrimEnd('/');
|
|
||||||
|
|
||||||
var requestBuilder = new HttpRequestBuilder($"{trimServerUrl}/{topic}").Post();
|
|
||||||
|
|
||||||
return requestBuilder;
|
|
||||||
}
|
|
||||||
|
|
||||||
public ValidationFailure Test(NtfySettings settings)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
const string title = "Radarr - Test Notification";
|
|
||||||
|
|
||||||
const string body = "This is a test message from Readarr";
|
|
||||||
|
|
||||||
SendNotification(title, body, settings);
|
|
||||||
}
|
|
||||||
catch (HttpException ex)
|
|
||||||
{
|
|
||||||
if (ex.Response.StatusCode == HttpStatusCode.Unauthorized || ex.Response.StatusCode == HttpStatusCode.Forbidden)
|
|
||||||
{
|
|
||||||
_logger.Error(ex, "Authorization is required");
|
|
||||||
return new ValidationFailure("UserName", "Authorization is required");
|
|
||||||
}
|
|
||||||
|
|
||||||
_logger.Error(ex, "Unable to send test message");
|
|
||||||
return new ValidationFailure("ServerUrl", "Unable to send test message");
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
_logger.Error(ex, "Unable to send test message");
|
|
||||||
return new ValidationFailure("", "Unable to send test message");
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void SendNotification(string title, string message, HttpRequestBuilder requestBuilder, NtfySettings settings)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
requestBuilder.Headers.Add("X-Title", title);
|
|
||||||
requestBuilder.Headers.Add("X-Message", message);
|
|
||||||
requestBuilder.Headers.Add("X-Priority", settings.Priority.ToString());
|
|
||||||
|
|
||||||
if (settings.Tags.Any())
|
|
||||||
{
|
|
||||||
requestBuilder.Headers.Add("X-Tags", settings.Tags.Join(","));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!settings.ClickUrl.IsNullOrWhiteSpace())
|
|
||||||
{
|
|
||||||
requestBuilder.Headers.Add("X-Click", settings.ClickUrl);
|
|
||||||
}
|
|
||||||
|
|
||||||
var request = requestBuilder.Build();
|
|
||||||
|
|
||||||
if (!settings.UserName.IsNullOrWhiteSpace() && !settings.Password.IsNullOrWhiteSpace())
|
|
||||||
{
|
|
||||||
request.Credentials = new BasicNetworkCredential(settings.UserName, settings.Password);
|
|
||||||
}
|
|
||||||
|
|
||||||
_httpClient.Execute(request);
|
|
||||||
}
|
|
||||||
catch (HttpException ex)
|
|
||||||
{
|
|
||||||
if (ex.Response.StatusCode == HttpStatusCode.Unauthorized || ex.Response.StatusCode == HttpStatusCode.Forbidden)
|
|
||||||
{
|
|
||||||
_logger.Error(ex, "Authorization is required");
|
|
||||||
throw;
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new NtfyException("Unable to send text message: {0}", ex, ex.Message);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,63 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using FluentValidation;
|
|
||||||
using NzbDrone.Common.Extensions;
|
|
||||||
using NzbDrone.Core.Annotations;
|
|
||||||
using NzbDrone.Core.ThingiProvider;
|
|
||||||
using NzbDrone.Core.Validation;
|
|
||||||
|
|
||||||
namespace NzbDrone.Core.Notifications.Ntfy
|
|
||||||
{
|
|
||||||
public class NtfySettingsValidator : AbstractValidator<NtfySettings>
|
|
||||||
{
|
|
||||||
public NtfySettingsValidator()
|
|
||||||
{
|
|
||||||
RuleFor(c => c.Topics).NotEmpty();
|
|
||||||
RuleFor(c => c.Priority).InclusiveBetween(1, 5);
|
|
||||||
RuleFor(c => c.ServerUrl).IsValidUrl().When(c => !c.ServerUrl.IsNullOrWhiteSpace());
|
|
||||||
RuleFor(c => c.ClickUrl).IsValidUrl().When(c => !c.ClickUrl.IsNullOrWhiteSpace());
|
|
||||||
RuleFor(c => c.UserName).NotEmpty().When(c => !c.Password.IsNullOrWhiteSpace());
|
|
||||||
RuleFor(c => c.Password).NotEmpty().When(c => !c.UserName.IsNullOrWhiteSpace());
|
|
||||||
RuleForEach(c => c.Topics).NotEmpty().Matches("[a-zA-Z0-9_-]+").Must(c => !InvalidTopics.Contains(c)).WithMessage("Invalid topic");
|
|
||||||
}
|
|
||||||
|
|
||||||
private static List<string> InvalidTopics => new List<string> { "announcements", "app", "docs", "settings", "stats", "mytopic-rw", "mytopic-ro", "mytopic-wo" };
|
|
||||||
}
|
|
||||||
|
|
||||||
public class NtfySettings : IProviderConfig
|
|
||||||
{
|
|
||||||
private static readonly NtfySettingsValidator Validator = new NtfySettingsValidator();
|
|
||||||
|
|
||||||
public NtfySettings()
|
|
||||||
{
|
|
||||||
Topics = Array.Empty<string>();
|
|
||||||
Priority = 3;
|
|
||||||
}
|
|
||||||
|
|
||||||
[FieldDefinition(0, Label = "Server Url", Type = FieldType.Url, HelpLink = "https://ntfy.sh/docs/install/", HelpText = "Leave blank to use public server (https://ntfy.sh)")]
|
|
||||||
public string ServerUrl { get; set; }
|
|
||||||
|
|
||||||
[FieldDefinition(1, Label = "User Name", HelpText = "Optional Authorization", Privacy = PrivacyLevel.UserName)]
|
|
||||||
public string UserName { get; set; }
|
|
||||||
|
|
||||||
[FieldDefinition(2, Label = "Password", Type = FieldType.Password, HelpText = "Optional Password", Privacy = PrivacyLevel.Password)]
|
|
||||||
public string Password { get; set; }
|
|
||||||
|
|
||||||
[FieldDefinition(3, Label = "Priority", Type = FieldType.Select, SelectOptions = typeof(NtfyPriority))]
|
|
||||||
public int Priority { get; set; }
|
|
||||||
|
|
||||||
[FieldDefinition(4, Label = "Topics", HelpText = "List of Topics to send notifications to", Type = FieldType.Tag)]
|
|
||||||
public IEnumerable<string> Topics { get; set; }
|
|
||||||
|
|
||||||
[FieldDefinition(5, Label = "Ntfy Tags and Emojis", Type = FieldType.Tag, HelpText = "Optional list of tags or emojis to use", HelpLink = "https://ntfy.sh/docs/emojis/")]
|
|
||||||
public IEnumerable<string> Tags { get; set; }
|
|
||||||
|
|
||||||
[FieldDefinition(6, Label = "Click Url", Type = FieldType.Url, HelpText = "Optional link when user clicks notification")]
|
|
||||||
public string ClickUrl { get; set; }
|
|
||||||
|
|
||||||
public NzbDroneValidationResult Validate()
|
|
||||||
{
|
|
||||||
return new NzbDroneValidationResult(Validator.Validate(this));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -22,7 +22,7 @@ namespace NzbDrone.Core.Parser
|
|||||||
private static readonly Regex RepackRegex = new Regex(@"\b(?<repack>repack|rerip)\b",
|
private static readonly Regex RepackRegex = new Regex(@"\b(?<repack>repack|rerip)\b",
|
||||||
RegexOptions.Compiled | RegexOptions.IgnoreCase);
|
RegexOptions.Compiled | RegexOptions.IgnoreCase);
|
||||||
|
|
||||||
private static readonly Regex VersionRegex = new Regex(@"\d[-._ ]?v(?<version>\d)[-._ ]|\[v(?<version>\d)\]",
|
private static readonly Regex VersionRegex = new Regex(@"\dv(?<version>\d)\b|\[v(?<version>\d)\]",
|
||||||
RegexOptions.Compiled | RegexOptions.IgnoreCase);
|
RegexOptions.Compiled | RegexOptions.IgnoreCase);
|
||||||
|
|
||||||
private static readonly Regex RealRegex = new Regex(@"\b(?<real>REAL)\b",
|
private static readonly Regex RealRegex = new Regex(@"\b(?<real>REAL)\b",
|
||||||
|
|||||||
@@ -146,7 +146,7 @@ namespace NzbDrone.Core.Update
|
|||||||
_logger.Info("Preparing client");
|
_logger.Info("Preparing client");
|
||||||
_diskTransferService.TransferFolder(_appFolderInfo.GetUpdateClientFolder(), updateSandboxFolder, TransferMode.Move);
|
_diskTransferService.TransferFolder(_appFolderInfo.GetUpdateClientFolder(), updateSandboxFolder, TransferMode.Move);
|
||||||
|
|
||||||
var updateClientExePath = _appFolderInfo.GetUpdateClientExePath();
|
var updateClientExePath = _appFolderInfo.GetUpdateClientExePath(updatePackage.Runtime);
|
||||||
|
|
||||||
if (!_diskProvider.FileExists(updateClientExePath))
|
if (!_diskProvider.FileExists(updateClientExePath))
|
||||||
{
|
{
|
||||||
@@ -155,7 +155,7 @@ namespace NzbDrone.Core.Update
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Set executable flag on update app
|
// Set executable flag on update app
|
||||||
if (OsInfo.IsOsx || OsInfo.IsLinux)
|
if (OsInfo.IsOsx || (OsInfo.IsLinux && PlatformInfo.IsNetCore))
|
||||||
{
|
{
|
||||||
_diskProvider.SetFilePermissions(updateClientExePath, "755", null);
|
_diskProvider.SetFilePermissions(updateClientExePath, "755", null);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
using System;
|
using System;
|
||||||
using NzbDrone.Common.EnvironmentInfo;
|
using NzbDrone.Common.EnvironmentInfo;
|
||||||
|
|
||||||
namespace NzbDrone.Core.Update
|
namespace NzbDrone.Core.Update
|
||||||
@@ -12,5 +12,6 @@ namespace NzbDrone.Core.Update
|
|||||||
public UpdateChanges Changes { get; set; }
|
public UpdateChanges Changes { get; set; }
|
||||||
public string Hash { get; set; }
|
public string Hash { get; set; }
|
||||||
public string Branch { get; set; }
|
public string Branch { get; set; }
|
||||||
|
public PlatformType Runtime { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
using FluentValidation.Validators;
|
|
||||||
using NzbDrone.Common.Cloud;
|
using NzbDrone.Common.Cloud;
|
||||||
using NzbDrone.Common.EnvironmentInfo;
|
using NzbDrone.Common.EnvironmentInfo;
|
||||||
using NzbDrone.Common.Http;
|
using NzbDrone.Common.Http;
|
||||||
@@ -40,7 +39,7 @@ namespace NzbDrone.Core.Update
|
|||||||
.AddQueryParam("version", currentVersion)
|
.AddQueryParam("version", currentVersion)
|
||||||
.AddQueryParam("os", OsInfo.Os.ToString().ToLowerInvariant())
|
.AddQueryParam("os", OsInfo.Os.ToString().ToLowerInvariant())
|
||||||
.AddQueryParam("arch", RuntimeInformation.OSArchitecture)
|
.AddQueryParam("arch", RuntimeInformation.OSArchitecture)
|
||||||
.AddQueryParam("runtime", "netcore")
|
.AddQueryParam("runtime", PlatformInfo.Platform.ToString().ToLowerInvariant())
|
||||||
.AddQueryParam("runtimeVer", _platformInfo.Version)
|
.AddQueryParam("runtimeVer", _platformInfo.Version)
|
||||||
.AddQueryParam("dbType", _mainDatabase.DatabaseType)
|
.AddQueryParam("dbType", _mainDatabase.DatabaseType)
|
||||||
.SetSegment("branch", branch);
|
.SetSegment("branch", branch);
|
||||||
@@ -68,7 +67,7 @@ namespace NzbDrone.Core.Update
|
|||||||
.AddQueryParam("version", currentVersion)
|
.AddQueryParam("version", currentVersion)
|
||||||
.AddQueryParam("os", OsInfo.Os.ToString().ToLowerInvariant())
|
.AddQueryParam("os", OsInfo.Os.ToString().ToLowerInvariant())
|
||||||
.AddQueryParam("arch", RuntimeInformation.OSArchitecture)
|
.AddQueryParam("arch", RuntimeInformation.OSArchitecture)
|
||||||
.AddQueryParam("runtime", "netcore")
|
.AddQueryParam("runtime", PlatformInfo.Platform.ToString().ToLowerInvariant())
|
||||||
.AddQueryParam("runtimeVer", _platformInfo.Version)
|
.AddQueryParam("runtimeVer", _platformInfo.Version)
|
||||||
.SetSegment("branch", branch);
|
.SetSegment("branch", branch);
|
||||||
|
|
||||||
|
|||||||
@@ -17,8 +17,6 @@ using NzbDrone.Common.Serializer;
|
|||||||
using NzbDrone.Core.Configuration;
|
using NzbDrone.Core.Configuration;
|
||||||
using NzbDrone.Core.Datastore;
|
using NzbDrone.Core.Datastore;
|
||||||
using NzbDrone.Core.Instrumentation;
|
using NzbDrone.Core.Instrumentation;
|
||||||
using NzbDrone.Core.Lifecycle;
|
|
||||||
using NzbDrone.Core.Messaging.Events;
|
|
||||||
using NzbDrone.Host.AccessControl;
|
using NzbDrone.Host.AccessControl;
|
||||||
using NzbDrone.Http.Authentication;
|
using NzbDrone.Http.Authentication;
|
||||||
using NzbDrone.SignalR;
|
using NzbDrone.SignalR;
|
||||||
@@ -134,7 +132,6 @@ namespace NzbDrone.Host
|
|||||||
IConfigFileProvider configFileProvider,
|
IConfigFileProvider configFileProvider,
|
||||||
IRuntimeInfo runtimeInfo,
|
IRuntimeInfo runtimeInfo,
|
||||||
IFirewallAdapter firewallAdapter,
|
IFirewallAdapter firewallAdapter,
|
||||||
IEventAggregator eventAggregator,
|
|
||||||
ReadarrErrorPipeline errorHandler)
|
ReadarrErrorPipeline errorHandler)
|
||||||
{
|
{
|
||||||
initializeLogger.Initialize();
|
initializeLogger.Initialize();
|
||||||
@@ -157,8 +154,6 @@ namespace NzbDrone.Host
|
|||||||
Console.CancelKeyPress += (sender, eventArgs) => NLog.LogManager.Configuration = null;
|
Console.CancelKeyPress += (sender, eventArgs) => NLog.LogManager.Configuration = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
eventAggregator.PublishEvent(new ApplicationStartingEvent());
|
|
||||||
|
|
||||||
if (OsInfo.IsWindows && runtimeInfo.IsAdmin)
|
if (OsInfo.IsWindows && runtimeInfo.IsAdmin)
|
||||||
{
|
{
|
||||||
firewallAdapter.MakeAccessible();
|
firewallAdapter.MakeAccessible();
|
||||||
|
|||||||
@@ -10,6 +10,11 @@ namespace NzbDrone.Mono.Test.DiskProviderTests
|
|||||||
[Platform(Exclude = "Win")]
|
[Platform(Exclude = "Win")]
|
||||||
public class FreeSpaceFixture : FreeSpaceFixtureBase<DiskProvider>
|
public class FreeSpaceFixture : FreeSpaceFixtureBase<DiskProvider>
|
||||||
{
|
{
|
||||||
|
public FreeSpaceFixture()
|
||||||
|
{
|
||||||
|
MonoOnly();
|
||||||
|
}
|
||||||
|
|
||||||
[SetUp]
|
[SetUp]
|
||||||
public void Setup()
|
public void Setup()
|
||||||
{
|
{
|
||||||
@@ -19,7 +24,6 @@ namespace NzbDrone.Mono.Test.DiskProviderTests
|
|||||||
.Returns<string>(s => s);
|
.Returns<string>(s => s);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Ignore("Docker")]
|
|
||||||
[Test]
|
[Test]
|
||||||
public void should_be_able_to_check_space_on_ramdrive()
|
public void should_be_able_to_check_space_on_ramdrive()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -8,9 +8,14 @@ using NzbDrone.Test.Common;
|
|||||||
namespace NzbDrone.Mono.Test.DiskProviderTests
|
namespace NzbDrone.Mono.Test.DiskProviderTests
|
||||||
{
|
{
|
||||||
[TestFixture]
|
[TestFixture]
|
||||||
[Platform(Exclude = "Win")]
|
[Platform("Mono")]
|
||||||
public class SymbolicLinkResolverFixture : TestBase<SymbolicLinkResolver>
|
public class SymbolicLinkResolverFixture : TestBase<SymbolicLinkResolver>
|
||||||
{
|
{
|
||||||
|
public SymbolicLinkResolverFixture()
|
||||||
|
{
|
||||||
|
MonoOnly();
|
||||||
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void should_follow_nested_symlinks()
|
public void should_follow_nested_symlinks()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -309,7 +309,7 @@ namespace NzbDrone.Mono.Disk
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
TransferFilePatched(source, destination, false, true);
|
base.MoveFileInternal(source, destination);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -321,7 +321,9 @@ namespace NzbDrone.Mono.Disk
|
|||||||
// Catch the exception and attempt to handle these edgecases
|
// Catch the exception and attempt to handle these edgecases
|
||||||
|
|
||||||
// Mono 6.x till 6.10 doesn't properly try use rename first.
|
// Mono 6.x till 6.10 doesn't properly try use rename first.
|
||||||
if (move)
|
if (move &&
|
||||||
|
((PlatformInfo.Platform == PlatformType.Mono && PlatformInfo.GetVersion() < new Version(6, 10)) ||
|
||||||
|
(PlatformInfo.Platform == PlatformType.NetCore)))
|
||||||
{
|
{
|
||||||
if (Syscall.lstat(source, out var sourcestat) == 0 &&
|
if (Syscall.lstat(source, out var sourcestat) == 0 &&
|
||||||
Syscall.lstat(destination, out var deststat) != 0 &&
|
Syscall.lstat(destination, out var deststat) != 0 &&
|
||||||
@@ -349,7 +351,32 @@ namespace NzbDrone.Mono.Disk
|
|||||||
var dstInfo = new FileInfo(destination);
|
var dstInfo = new FileInfo(destination);
|
||||||
var exists = dstInfo.Exists && srcInfo.Exists;
|
var exists = dstInfo.Exists && srcInfo.Exists;
|
||||||
|
|
||||||
if (exists && dstInfo.Length == srcInfo.Length)
|
if (PlatformInfo.Platform == PlatformType.Mono && PlatformInfo.GetVersion() >= new Version(6, 6) &&
|
||||||
|
exists && dstInfo.Length == 0 && srcInfo.Length != 0)
|
||||||
|
{
|
||||||
|
// mono >=6.6 bug: zero length file since chmod happens at the start
|
||||||
|
_logger.Debug("{3} failed to {2} file likely due to known {3} bug, attempting to {2} directly. '{0}' -> '{1}'", source, destination, move ? "move" : "copy", PlatformInfo.PlatformName);
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
_logger.Trace("Copying content from {0} to {1} ({2} bytes)", source, destination, srcInfo.Length);
|
||||||
|
using (var srcStream = new FileStream(source, FileMode.Open, FileAccess.Read))
|
||||||
|
using (var dstStream = new FileStream(destination, FileMode.Create, FileAccess.Write))
|
||||||
|
{
|
||||||
|
srcStream.CopyTo(dstStream);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
// If it fails again then bail
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (((PlatformInfo.Platform == PlatformType.Mono &&
|
||||||
|
PlatformInfo.GetVersion() >= new Version(6, 0) &&
|
||||||
|
PlatformInfo.GetVersion() < new Version(6, 6)) ||
|
||||||
|
PlatformInfo.Platform == PlatformType.NetCore) &&
|
||||||
|
exists && dstInfo.Length == srcInfo.Length)
|
||||||
{
|
{
|
||||||
// mono 6.0, mono 6.4 and netcore 3.1 bug: full length file since utime and chmod happens at the end
|
// mono 6.0, mono 6.4 and netcore 3.1 bug: full length file since utime and chmod happens at the end
|
||||||
_logger.Debug("{3} failed to {2} file likely due to known {3} bug, attempting to {2} directly. '{0}' -> '{1}'", source, destination, move ? "move" : "copy", PlatformInfo.PlatformName);
|
_logger.Debug("{3} failed to {2} file likely due to known {3} bug, attempting to {2} directly. '{0}' -> '{1}'", source, destination, move ? "move" : "copy", PlatformInfo.PlatformName);
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFrameworks>net6.0</TargetFrameworks>
|
<TargetFrameworks>net6.0</TargetFrameworks>
|
||||||
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
|
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="NLog" />
|
<PackageReference Include="NLog" />
|
||||||
|
|||||||
@@ -50,6 +50,10 @@ namespace NzbDrone.Test.Common
|
|||||||
{
|
{
|
||||||
readarrConsoleExe = "Readarr.Console.exe";
|
readarrConsoleExe = "Readarr.Console.exe";
|
||||||
}
|
}
|
||||||
|
else if (PlatformInfo.IsMono)
|
||||||
|
{
|
||||||
|
readarrConsoleExe = "Readarr.exe";
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
readarrConsoleExe = "Readarr";
|
readarrConsoleExe = "Readarr";
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user