Compare commits

...

36 Commits

Author SHA1 Message Date
Bogdan
7cc02f95af Fix filename for class 2025-06-10 13:21:32 +03:00
Bogdan
1f92bf6679 Fix fullscreen automation screenshots 2025-06-10 13:16:08 +03:00
Taloth Saldono
a8d4aa6770 Used ReflectionOnly and/or public types where possible to avoid loading related assemblies unnecessarily. 2025-06-08 10:38:03 +03:00
Bogdan
7661b5bc87 Bump version to 0.4.18 2025-06-08 10:31:09 +03:00
Weblate
200ef600cd Multiple Translations updated by Weblate
ignore-downstream

Co-authored-by: Fixer <ygj59783@zslsz.com>
Co-authored-by: Ilbebino <tommasobellandi08@gmail.com>
Co-authored-by: NanderTGA <nander.roobaert@gmail.com>
Co-authored-by: Weblate <noreply-mt-weblate@weblate.org>
Co-authored-by: Weblate <noreply@weblate.org>
Translate-URL: https://translate.servarr.com/projects/servarr/readarr/it/
Translate-URL: https://translate.servarr.com/projects/servarr/readarr/nl/
Translate-URL: https://translate.servarr.com/projects/servarr/readarr/sk/
Translate-URL: https://translate.servarr.com/projects/servarr/readarr/uk/
Translate-URL: https://translate.servarr.com/projects/servarr/readarr/zh_Hans/
Translation: Servarr/Readarr
2025-06-07 18:16:49 +03:00
Bogdan
ad6228983b Skip failing tests 2025-06-03 11:18:20 +03:00
Servarr
582ec9f7ce Automated API Docs update 2025-06-02 00:04:28 +03:00
Taloth Saldono
525e855038 Fixed: Return remote image links for RemotePoster and RemoteCover
(cherry picked from commit 4219cdb3644f96e1e8f3178fe0a50430c1004506)

Fixes #4101
Closes #212
2025-06-01 23:55:06 +03:00
Weblate
7a629ed044 Multiple Translations updated by Weblate
ignore-downstream

Co-authored-by: Hugoren Martinako <aumpfbahn@gmail.com>
Co-authored-by: Oskari Lavinto <olavinto@protonmail.com>
Co-authored-by: Weblate <noreply-mt-weblate@weblate.org>
Co-authored-by: Weblate <noreply@weblate.org>
Co-authored-by: Youngzheimer <me@youngzheimer.com>
Translate-URL: https://translate.servarr.com/projects/servarr/readarr/es/
Translate-URL: https://translate.servarr.com/projects/servarr/readarr/fi/
Translate-URL: https://translate.servarr.com/projects/servarr/readarr/ko/
Translate-URL: https://translate.servarr.com/projects/servarr/readarr/nb_NO/
Translate-URL: https://translate.servarr.com/projects/servarr/readarr/pt/
Translation: Servarr/Readarr
2025-05-04 21:07:53 +03:00
Bogdan
7f501322dd Bump version to 0.4.17 2025-05-04 21:07:14 +03:00
Samuel Mercer
18bca0b228 Fixed: Displayed root folder path getting truncated when adding an author with a long name 2025-05-01 13:53:47 +03:00
Bogdan
9ddac60b47 Bump version to 0.4.16 2025-04-20 09:01:31 +03:00
Bogdan
bd8bc0b35b Pass messages with arguments to NLog in LoggerExtensions 2025-04-13 13:17:00 +03:00
Bogdan
ae623f4481 Fixed: Use template for log messages in Import Books 2025-04-13 12:55:42 +03:00
Bogdan
e67d133bb6 Mark as template for log progress messages 2025-04-13 12:55:42 +03:00
Weblate
772ea95ce4 Multiple Translations updated by Weblate
ignore-downstream

Co-authored-by: Havok Dan <havokdan@yahoo.com.br>
Co-authored-by: Hugoren Martinako <aumpfbahn@gmail.com>
Co-authored-by: Weblate <noreply-mt-weblate@weblate.org>
Co-authored-by: Weblate <noreply@weblate.org>
Co-authored-by: fordas <fordas15@gmail.com>
Translate-URL: https://translate.servarr.com/projects/servarr/readarr/ar/
Translate-URL: https://translate.servarr.com/projects/servarr/readarr/bg/
Translate-URL: https://translate.servarr.com/projects/servarr/readarr/ca/
Translate-URL: https://translate.servarr.com/projects/servarr/readarr/cs/
Translate-URL: https://translate.servarr.com/projects/servarr/readarr/da/
Translate-URL: https://translate.servarr.com/projects/servarr/readarr/de/
Translate-URL: https://translate.servarr.com/projects/servarr/readarr/el/
Translate-URL: https://translate.servarr.com/projects/servarr/readarr/es/
Translate-URL: https://translate.servarr.com/projects/servarr/readarr/fi/
Translate-URL: https://translate.servarr.com/projects/servarr/readarr/fr/
Translate-URL: https://translate.servarr.com/projects/servarr/readarr/he/
Translate-URL: https://translate.servarr.com/projects/servarr/readarr/hi/
Translate-URL: https://translate.servarr.com/projects/servarr/readarr/hr/
Translate-URL: https://translate.servarr.com/projects/servarr/readarr/hu/
Translate-URL: https://translate.servarr.com/projects/servarr/readarr/is/
Translate-URL: https://translate.servarr.com/projects/servarr/readarr/it/
Translate-URL: https://translate.servarr.com/projects/servarr/readarr/ja/
Translate-URL: https://translate.servarr.com/projects/servarr/readarr/ko/
Translate-URL: https://translate.servarr.com/projects/servarr/readarr/nb_NO/
Translate-URL: https://translate.servarr.com/projects/servarr/readarr/nl/
Translate-URL: https://translate.servarr.com/projects/servarr/readarr/pl/
Translate-URL: https://translate.servarr.com/projects/servarr/readarr/pt/
Translate-URL: https://translate.servarr.com/projects/servarr/readarr/pt_BR/
Translate-URL: https://translate.servarr.com/projects/servarr/readarr/ro/
Translate-URL: https://translate.servarr.com/projects/servarr/readarr/ru/
Translate-URL: https://translate.servarr.com/projects/servarr/readarr/sk/
Translate-URL: https://translate.servarr.com/projects/servarr/readarr/sv/
Translate-URL: https://translate.servarr.com/projects/servarr/readarr/th/
Translate-URL: https://translate.servarr.com/projects/servarr/readarr/tr/
Translate-URL: https://translate.servarr.com/projects/servarr/readarr/uk/
Translate-URL: https://translate.servarr.com/projects/servarr/readarr/vi/
Translate-URL: https://translate.servarr.com/projects/servarr/readarr/zh_CN/
Translate-URL: https://translate.servarr.com/projects/servarr/readarr/zh_TW/
Translation: Servarr/Readarr
2025-04-13 10:09:39 +03:00
Bogdan
5459a7bb7e Bump version to 0.4.15 2025-04-13 09:48:49 +03:00
Servarr
614f98f9b4 Automated API Docs update 2025-04-08 22:09:31 +03:00
Bogdan
55763dae43 Fixed disabled options for SelectInput 2025-04-08 21:53:35 +03:00
Bogdan
a362dab503 Console warnings for missing translations on development builds
(cherry picked from commit 67a1ecb0fea4e6c7dfdb68fbe3ef30d4c22398d8)

Closes #3863
2025-04-08 21:53:35 +03:00
Mark McDowall
dba9fbf254 Fixed: Trying to add an author when root folders hadn't populated
(cherry picked from commit a6d0dddaf7fa51f334e32d4fd49486d06fb6ba65)
2025-04-08 21:41:48 +03:00
Bogdan
59a7605385 Improve validation message for AuthorFolderAsRootFolderValidator
(cherry picked from commit a117001de673e80abd90d54a34a7c86292b3a649)
2025-04-08 21:41:48 +03:00
Mark McDowall
f819e582cf New: Author folder hint when selecting a root folder while adding a new author
(cherry picked from commit dd09f31abb4dd3f699bcff0a47577075300c70ee)

Fix AuthorFolderAsRootFolderValidator

(cherry picked from commit 0ce81e1ab69d43fde382cc4ae22cd46fe626dea7)
2025-04-08 21:41:48 +03:00
Weblate
0972d41bf8 Multiple Translations updated by Weblate
ignore-downstream

Co-authored-by: Hugoren Martinako <aumpfbahn@gmail.com>
Translate-URL: https://translate.servarr.com/projects/servarr/readarr/ca/
Translation: Servarr/Readarr
2025-04-08 17:28:32 +03:00
Servarr
05d2335bfe Automated API Docs update 2025-04-08 17:27:23 +03:00
Bogdan
5b4f54a959 Prevent NullRef for cases when media covers have nullable urls
(cherry picked from commit a26df9e9afa8d925c2ad62c126d4edebec7e4e54)

Closes #2981
2025-04-08 16:30:39 +03:00
Taloth Saldono
bb5ad605fd Fixed: Posters not always showing when searching for new authors
(cherry picked from commit 10dc884fa87a8337e9f0622c269adede0b262029)

Co-authored-by: optimous012

Closes #145
2025-04-08 16:30:33 +03:00
Bogdan
52c3a95e63 Bump browserslist-db 2025-04-08 15:52:06 +03:00
Mark McDowall
52c5460537 New: Prevent Remote Path Mapping local folder being set to System folder or '/'
(cherry picked from commit 0f904e091702a2ac53771ee3aeb5aafe62688035)
2025-04-08 15:48:56 +03:00
Mark McDowall
280cec3d0e Fixed: Set output encoding to UTF-8 when running external processes
(cherry picked from commit f8e57b09856278a6d0c65f18704e96a33459687d)
2025-04-08 15:46:48 +03:00
Mark McDowall
f10c2c01d8 Update WikiUrl type in API docs
(cherry picked from commit 9bd619ccfe074abe396bbf043a36a5be18a7ba4b)
2025-04-08 15:46:26 +03:00
Bogdan
2b6a328dac Bump Selenium.WebDriver.ChromeDriver 2025-04-08 15:46:14 +03:00
Bogdan
4078525f67 Log delete statements only once 2025-04-08 15:45:20 +03:00
Bogdan
214e4270ac Fixed: Disallow tags creation with empty label 2025-04-08 15:45:10 +03:00
Bogdan
5396dd3e8e Bump linux agent to ubuntu-22.04 2025-04-02 00:11:18 +03:00
Bogdan
d5d4996c40 Bump version to 0.4.14 2025-03-30 10:29:09 +03:00
98 changed files with 1030 additions and 327 deletions

View File

@@ -9,7 +9,7 @@ variables:
testsFolder: './_tests'
yarnCacheFolder: $(Pipeline.Workspace)/.yarn
nugetCacheFolder: $(Pipeline.Workspace)/.nuget/packages
majorVersion: '0.4.13'
majorVersion: '0.4.18'
minorVersion: $[counter('minorVersion', 1)]
readarrVersion: '$(majorVersion).$(minorVersion)'
buildName: '$(Build.SourceBranchName).$(readarrVersion)'
@@ -19,7 +19,7 @@ variables:
nodeVersion: '20.X'
innoVersion: '6.2.0'
windowsImage: 'windows-2022'
linuxImage: 'ubuntu-20.04'
linuxImage: 'ubuntu-22.04'
macImage: 'macOS-13'
trigger:

View File

@@ -2,6 +2,7 @@ import PropTypes from 'prop-types';
import React from 'react';
import Button from 'Components/Link/Button';
import { kinds } from 'Helpers/Props';
import translate from 'Utilities/String/translate';
import styles from './NoAuthor.css';
function NoAuthor(props) {
@@ -31,7 +32,7 @@ function NoAuthor(props) {
to="/settings/mediamanagement"
kind={kinds.PRIMARY}
>
Add Root Folder
{translate('AddRootFolder')}
</Button>
</div>
@@ -40,7 +41,7 @@ function NoAuthor(props) {
to="/add/search"
kind={kinds.PRIMARY}
>
Add New Author
{translate('AddNewAuthor')}
</Button>
</div>
</div>

View File

@@ -28,8 +28,7 @@ function createMapStateToProps() {
if (includeNoChange) {
values.unshift({
key: 'noChange',
value: '',
name: translate('NoChange'),
value: translate('NoChange'),
isDisabled: includeNoChangeDisabled,
isMissing: false
});
@@ -39,7 +38,6 @@ function createMapStateToProps() {
values.push({
key: '',
value: '',
name: '',
isDisabled: true,
isHidden: true
});
@@ -56,8 +54,7 @@ function createMapStateToProps() {
values.push({
key: ADD_NEW_KEY,
value: '',
name: 'Add a new path'
value: 'Add a new path'
});
return {
@@ -105,6 +102,27 @@ class RootFolderSelectInputConnector extends Component {
}
}
componentDidUpdate(prevProps) {
const {
name,
value,
values,
onChange
} = this.props;
if (prevProps.values === values) {
return;
}
if (!value && values.length && values.some((v) => !!v.key && v.key !== ADD_NEW_KEY)) {
const defaultValue = values[0];
if (defaultValue.key !== ADD_NEW_KEY) {
onChange({ name, value: defaultValue.key });
}
}
}
//
// Render

View File

@@ -13,6 +13,15 @@
}
}
.value {
display: flex;
}
.authorFolder {
flex: 0 0 auto;
color: var(--disabledColor);
}
.freeSpace {
margin-left: 15px;
color: var(--darkGray);

View File

@@ -1,10 +1,12 @@
// This file is automatically generated.
// Please do not change this file!
interface CssExports {
'authorFolder': string;
'freeSpace': string;
'isMissing': string;
'isMobile': string;
'optionText': string;
'value': string;
}
export const cssExports: CssExports;
export default cssExports;

View File

@@ -7,18 +7,24 @@ import styles from './RootFolderSelectInputOption.css';
function RootFolderSelectInputOption(props) {
const {
id,
value,
name,
freeSpace,
authorFolder,
isMissing,
isMobile,
isWindows,
...otherProps
} = props;
const text = value === '' ? name : `${name} [${value}]`;
const slashCharacter = isWindows ? '\\' : '/';
const text = name === '' ? value : `[${name}] ${value}`;
return (
<EnhancedSelectInputOption
id={id}
isMobile={isMobile}
{...otherProps}
>
@@ -27,7 +33,18 @@ function RootFolderSelectInputOption(props) {
isMobile && styles.isMobile
)}
>
<div>{text}</div>
<div className={styles.value}>
{text}
{
authorFolder && id !== 'addNew' ?
<div className={styles.authorFolder}>
{slashCharacter}
{authorFolder}
</div> :
null
}
</div>
{
freeSpace == null ?
@@ -50,11 +67,18 @@ function RootFolderSelectInputOption(props) {
}
RootFolderSelectInputOption.propTypes = {
id: PropTypes.string.isRequired,
name: PropTypes.string.isRequired,
value: PropTypes.string.isRequired,
freeSpace: PropTypes.number,
authorFolder: PropTypes.string,
isMissing: PropTypes.bool,
isMobile: PropTypes.bool.isRequired
isMobile: PropTypes.bool.isRequired,
isWindows: PropTypes.bool
};
RootFolderSelectInputOption.defaultProps = {
name: ''
};
export default RootFolderSelectInputOption;

View File

@@ -7,12 +7,22 @@
overflow: hidden;
}
.path {
.pathContainer {
@add-mixin truncate;
display: flex;
flex: 1 0 0;
}
.path {
flex: 0 1 auto;
}
.authorFolder {
@add-mixin truncate;
flex: 0 1 auto;
color: var(--disabledColor);
}
.freeSpace {
flex: 0 0 auto;
margin-left: 15px;

View File

@@ -1,8 +1,10 @@
// This file is automatically generated.
// Please do not change this file!
interface CssExports {
'authorFolder': string;
'freeSpace': string;
'path': string;
'pathContainer': string;
'selectedValue': string;
}
export const cssExports: CssExports;

View File

@@ -9,19 +9,34 @@ function RootFolderSelectInputSelectedValue(props) {
name,
value,
freeSpace,
authorFolder,
includeFreeSpace,
isWindows,
...otherProps
} = props;
const text = value === '' ? name : `${name} [${value}]`;
const slashCharacter = isWindows ? '\\' : '/';
const text = name === '' ? value : `[${name}] ${value}`;
return (
<EnhancedSelectInputSelectedValue
className={styles.selectedValue}
{...otherProps}
>
<div className={styles.path}>
{text}
<div className={styles.pathContainer}>
<div className={styles.path}>
{text}
</div>
{
authorFolder ?
<div className={styles.authorFolder}>
{slashCharacter}
{authorFolder}
</div> :
null
}
</div>
{
@@ -38,10 +53,13 @@ RootFolderSelectInputSelectedValue.propTypes = {
name: PropTypes.string,
value: PropTypes.string,
freeSpace: PropTypes.number,
authorFolder: PropTypes.string,
isWindows: PropTypes.bool,
includeFreeSpace: PropTypes.bool.isRequired
};
RootFolderSelectInputSelectedValue.defaultProps = {
name: '',
includeFreeSpace: true
};

View File

@@ -52,6 +52,7 @@ class SelectInput extends Component {
const {
key,
value: optionValue,
isDisabled: optionIsDisabled = false,
...otherOptionProps
} = option;
@@ -59,6 +60,7 @@ class SelectInput extends Component {
<option
key={key}
value={key}
disabled={optionIsDisabled}
{...otherOptionProps}
>
{typeof optionValue === 'function' ? optionValue() : optionValue}

View File

@@ -35,11 +35,12 @@
.message {
margin-top: 30px;
text-align: center;
font-weight: 300;
font-size: $largeFontSize;
}
.helpText {
margin-bottom: 10px;
font-weight: 300;
font-size: 24px;
}

View File

@@ -82,7 +82,8 @@ class AddNewItem extends Component {
render() {
const {
error,
items
items,
hasExistingAuthors
} = this.props;
const term = this.state.term;
@@ -186,7 +187,8 @@ class AddNewItem extends Component {
}
{
!term &&
term ?
null :
<div className={styles.message}>
<div className={styles.helpText}>
{translate('ItsEasyToAddANewAuthorOrBookJustStartTypingTheNameOfTheItemYouWantToAdd')}
@@ -199,6 +201,24 @@ class AddNewItem extends Component {
</div>
}
{
!term && !hasExistingAuthors ?
<div className={styles.message}>
<div className={styles.noAuthorsText}>
You haven't added any authors yet, do you want to add an existing library location (Root Folder) and update?
</div>
<div>
<Button
to="/settings/mediamanagement"
kind={kinds.PRIMARY}
>
{translate('AddRootFolder')}
</Button>
</div>
</div> :
null
}
<div />
</PageContentBody>
</PageContent>
@@ -213,6 +233,7 @@ AddNewItem.propTypes = {
isAdding: PropTypes.bool.isRequired,
addError: PropTypes.object,
items: PropTypes.arrayOf(PropTypes.object).isRequired,
hasExistingAuthors: PropTypes.bool.isRequired,
onSearchChange: PropTypes.func.isRequired,
onClearSearch: PropTypes.func.isRequired
};

View File

@@ -10,13 +10,15 @@ import AddNewItem from './AddNewItem';
function createMapStateToProps() {
return createSelector(
(state) => state.search,
(state) => state.authors.items.length,
(state) => state.router.location,
(search, location) => {
(search, existingAuthorsCount, location) => {
const { params } = parseUrl(location.search);
return {
...search,
term: params.term,
...search
hasExistingAuthors: existingAuthorsCount > 0
};
}
);

View File

@@ -9,6 +9,7 @@ import ModalContent from 'Components/Modal/ModalContent';
import ModalFooter from 'Components/Modal/ModalFooter';
import ModalHeader from 'Components/Modal/ModalHeader';
import { kinds } from 'Helpers/Props';
import translate from 'Utilities/String/translate';
import AddAuthorOptionsForm from '../Common/AddAuthorOptionsForm.js';
import styles from './AddNewAuthorModalContent.css';
@@ -54,7 +55,7 @@ class AddNewAuthorModalContent extends Component {
return (
<ModalContent onModalClose={onModalClose}>
<ModalHeader>
Add new Author
{translate('AddNewAuthor')}
</ModalHeader>
<ModalBody>
@@ -133,7 +134,7 @@ class AddNewAuthorModalContent extends Component {
AddNewAuthorModalContent.propTypes = {
authorName: PropTypes.string.isRequired,
disambiguation: PropTypes.string.isRequired,
disambiguation: PropTypes.string,
overview: PropTypes.string,
images: PropTypes.arrayOf(PropTypes.object).isRequired,
isAdding: PropTypes.bool.isRequired,

View File

@@ -4,6 +4,7 @@ import { connect } from 'react-redux';
import { createSelector } from 'reselect';
import { addAuthor, setAuthorAddDefault } from 'Store/Actions/searchActions';
import createDimensionsSelector from 'Store/Selectors/createDimensionsSelector';
import createSystemStatusSelector from 'Store/Selectors/createSystemStatusSelector';
import selectSettings from 'Store/Selectors/selectSettings';
import AddNewAuthorModalContent from './AddNewAuthorModalContent';
@@ -12,7 +13,8 @@ function createMapStateToProps() {
(state) => state.search,
(state) => state.settings.metadataProfiles,
createDimensionsSelector(),
(searchState, metadataProfiles, dimensions) => {
createSystemStatusSelector(),
(searchState, metadataProfiles, dimensions, systemStatus) => {
const {
isAdding,
addError,
@@ -32,6 +34,7 @@ function createMapStateToProps() {
isSmallScreen: dimensions.isSmallScreen,
validationErrors,
validationWarnings,
isWindows: systemStatus.isWindows,
...settings
};
}

View File

@@ -78,6 +78,7 @@ class AddNewAuthorSearchResult extends Component {
status,
overview,
ratings,
folder,
images,
isExistingAuthor,
isSmallScreen
@@ -205,6 +206,7 @@ class AddNewAuthorSearchResult extends Component {
disambiguation={disambiguation}
year={year}
overview={overview}
folder={folder}
images={images}
onModalClose={this.onAddAuthorModalClose}
/>
@@ -222,6 +224,7 @@ AddNewAuthorSearchResult.propTypes = {
status: PropTypes.string.isRequired,
overview: PropTypes.string,
ratings: PropTypes.object.isRequired,
folder: PropTypes.string.isRequired,
images: PropTypes.arrayOf(PropTypes.object).isRequired,
isExistingAuthor: PropTypes.bool.isRequired,
isSmallScreen: PropTypes.bool.isRequired

View File

@@ -10,6 +10,7 @@ import ModalFooter from 'Components/Modal/ModalFooter';
import ModalHeader from 'Components/Modal/ModalHeader';
import { kinds } from 'Helpers/Props';
import stripHtml from 'Utilities/String/stripHtml';
import translate from 'Utilities/String/translate';
import AddAuthorOptionsForm from '../Common/AddAuthorOptionsForm.js';
import styles from './AddNewBookModalContent.css';
@@ -58,7 +59,7 @@ class AddNewBookModalContent extends Component {
return (
<ModalContent onModalClose={onModalClose}>
<ModalHeader>
Add new Book
{translate('AddNewBook')}
</ModalHeader>
<ModalBody>

View File

@@ -4,6 +4,7 @@ import { connect } from 'react-redux';
import { createSelector } from 'reselect';
import { addBook, setBookAddDefault } from 'Store/Actions/searchActions';
import createDimensionsSelector from 'Store/Selectors/createDimensionsSelector';
import createSystemStatusSelector from 'Store/Selectors/createSystemStatusSelector';
import selectSettings from 'Store/Selectors/selectSettings';
import AddNewBookModalContent from './AddNewBookModalContent';
@@ -13,7 +14,8 @@ function createMapStateToProps() {
(state) => state.search,
(state) => state.settings.metadataProfiles,
createDimensionsSelector(),
(isExistingAuthor, searchState, metadataProfiles, dimensions) => {
createSystemStatusSelector(),
(isExistingAuthor, searchState, metadataProfiles, dimensions, systemStatus) => {
const {
isAdding,
addError,
@@ -33,6 +35,7 @@ function createMapStateToProps() {
isSmallScreen: dimensions.isSmallScreen,
validationErrors,
validationWarnings,
isWindows: systemStatus.isWindows,
...settings
};
}

View File

@@ -203,6 +203,7 @@ class AddNewBookSearchResult extends Component {
disambiguation={disambiguation}
authorName={author.authorName}
overview={overview}
folder={author.folder}
images={images}
onModalClose={this.onAddBookModalClose}
/>

View File

@@ -39,7 +39,9 @@ class AddAuthorOptionsForm extends Component {
includeNoneMetadataProfile,
includeSpecificBookMonitor,
showMetadataProfile,
folder,
tags,
isWindows,
onInputChange,
...otherProps
} = this.props;
@@ -54,6 +56,15 @@ class AddAuthorOptionsForm extends Component {
<FormInputGroup
type={inputTypes.ROOT_FOLDER_SELECT}
name="rootFolderPath"
valueOptions={{
authorFolder: folder,
isWindows
}}
selectedValueOptions={{
authorFolder: folder,
isWindows
}}
helpText={translate('AddNewAuthorRootFolderHelpText', { folder })}
onChange={onInputChange}
{...rootFolderPath}
/>
@@ -179,8 +190,14 @@ AddAuthorOptionsForm.propTypes = {
showMetadataProfile: PropTypes.bool.isRequired,
includeNoneMetadataProfile: PropTypes.bool.isRequired,
includeSpecificBookMonitor: PropTypes.bool.isRequired,
folder: PropTypes.string.isRequired,
tags: PropTypes.object.isRequired,
isWindows: PropTypes.bool.isRequired,
onInputChange: PropTypes.func.isRequired
};
AddAuthorOptionsForm.defaultProps = {
includeSpecificBookMonitor: false
};
export default AddAuthorOptionsForm;

View File

@@ -27,6 +27,12 @@ export default function translate(
key: string,
tokens: Record<string, string | number | boolean> = {}
) {
const { isProduction = true } = window.Readarr;
if (!isProduction && !(key in translations)) {
console.warn(`Missing translation for key: ${key}`);
}
const translation = translations[key] || key;
tokens.appName = 'Readarr';

View File

@@ -7,5 +7,6 @@ interface Window {
theme: string;
urlBase: string;
version: string;
isProduction: boolean;
};
}

View File

@@ -45,7 +45,7 @@
<PackageVersion Include="RestSharp.Serializers.SystemTextJson" Version="106.15.0" />
<PackageVersion Include="RestSharp" Version="106.15.0" />
<PackageVersion Include="Selenium.Support" Version="3.141.0" />
<PackageVersion Include="Selenium.WebDriver.ChromeDriver" Version="91.0.4472.10100" />
<PackageVersion Include="Selenium.WebDriver.ChromeDriver" Version="134.0.6998.16500" />
<PackageVersion Include="Sentry" Version="4.0.2" />
<PackageVersion Include="SharpZipLib" Version="1.4.2" />
<PackageVersion Include="SixLabors.ImageSharp" Version="3.1.7" />

View File

@@ -40,15 +40,16 @@ namespace NzbDrone.Automation.Test
var service = ChromeDriverService.CreateDefaultService();
// Timeout as windows automation tests seem to take alot longer to get going
driver = new ChromeDriver(service, options, new TimeSpan(0, 3, 0));
driver = new ChromeDriver(service, options, TimeSpan.FromMinutes(3));
driver.Manage().Window.Size = new System.Drawing.Size(1920, 1080);
driver.Manage().Window.FullScreen();
_runner = new NzbDroneRunner(LogManager.GetCurrentClassLogger(), null);
_runner.KillAll();
_runner.Start(true);
driver.Url = "http://localhost:8787";
driver.Navigate().GoToUrl("http://localhost:8787");
var page = new PageBase(driver);
page.WaitForNoSpinner();
@@ -68,7 +69,7 @@ namespace NzbDrone.Automation.Test
{
try
{
var image = ((ITakesScreenshot)driver).GetScreenshot();
var image = (driver as ITakesScreenshot).GetScreenshot();
image.SaveAsFile($"./{name}_test_screenshot.png", ScreenshotImageFormat.Png);
}
catch (Exception ex)

View File

@@ -1,19 +1,17 @@
using System;
using System.Threading;
using OpenQA.Selenium;
using OpenQA.Selenium.Remote;
using OpenQA.Selenium.Support.UI;
namespace NzbDrone.Automation.Test.PageModel
{
public class PageBase
{
private readonly RemoteWebDriver _driver;
private readonly IWebDriver _driver;
public PageBase(RemoteWebDriver driver)
public PageBase(IWebDriver driver)
{
_driver = driver;
driver.Manage().Window.Maximize();
}
public IWebElement FindByClass(string className, int timeout = 5)

View File

@@ -108,6 +108,15 @@ namespace NzbDrone.Common.Extensions
return Directory.GetParent(cleanPath)?.FullName;
}
public static string GetCleanPath(this string path)
{
var cleanPath = OsInfo.IsWindows
? PARENT_PATH_END_SLASH_REGEX.Replace(path, "")
: path.TrimEnd(Path.DirectorySeparatorChar);
return cleanPath;
}
public static bool IsParentPath(this string parentPath, string childPath)
{
if (parentPath != "/" && !parentPath.EndsWith(":\\"))

View File

@@ -4,27 +4,27 @@ namespace NzbDrone.Common.Instrumentation.Extensions
{
public static class LoggerExtensions
{
[MessageTemplateFormatMethod("message")]
public static void ProgressInfo(this Logger logger, string message, params object[] args)
{
var formattedMessage = string.Format(message, args);
LogProgressMessage(logger, LogLevel.Info, formattedMessage);
LogProgressMessage(logger, LogLevel.Info, message, args);
}
[MessageTemplateFormatMethod("message")]
public static void ProgressDebug(this Logger logger, string message, params object[] args)
{
var formattedMessage = string.Format(message, args);
LogProgressMessage(logger, LogLevel.Debug, formattedMessage);
LogProgressMessage(logger, LogLevel.Debug, message, args);
}
[MessageTemplateFormatMethod("message")]
public static void ProgressTrace(this Logger logger, string message, params object[] args)
{
var formattedMessage = string.Format(message, args);
LogProgressMessage(logger, LogLevel.Trace, formattedMessage);
LogProgressMessage(logger, LogLevel.Trace, message, args);
}
private static void LogProgressMessage(Logger logger, LogLevel level, string message)
private static void LogProgressMessage(Logger logger, LogLevel level, string message, object[] parameters)
{
var logEvent = new LogEventInfo(level, logger.Name, message);
var logEvent = new LogEventInfo(level, logger.Name, null, message, parameters);
logEvent.Properties.Add("Status", "");
logger.Log(logEvent);

View File

@@ -6,6 +6,7 @@ using System.ComponentModel;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Text;
using NLog;
using NzbDrone.Common.EnvironmentInfo;
using NzbDrone.Common.Model;
@@ -117,7 +118,9 @@ namespace NzbDrone.Common.Processes
UseShellExecute = false,
RedirectStandardError = true,
RedirectStandardOutput = true,
RedirectStandardInput = true
RedirectStandardInput = true,
StandardOutputEncoding = Encoding.UTF8,
StandardErrorEncoding = Encoding.UTF8
};
if (environmentVariables != null)

View File

@@ -17,7 +17,7 @@ namespace NzbDrone.Common.Reflection
public static List<Type> ImplementationsOf<T>(this Assembly assembly)
{
return assembly.GetTypes().Where(c => typeof(T).IsAssignableFrom(c)).ToList();
return assembly.GetExportedTypes().Where(c => typeof(T).IsAssignableFrom(c)).ToList();
}
public static bool IsSimpleType(this Type type)
@@ -68,7 +68,7 @@ namespace NzbDrone.Common.Reflection
public static Type FindTypeByName(this Assembly assembly, string name)
{
return assembly.GetTypes().SingleOrDefault(c => c.Name.Equals(name, StringComparison.InvariantCultureIgnoreCase));
return assembly.GetExportedTypes().SingleOrDefault(c => c.Name.Equals(name, StringComparison.InvariantCultureIgnoreCase));
}
public static bool HasAttribute<TAttribute>(this Type type)

View File

@@ -13,7 +13,7 @@ using NzbDrone.Core.Test.Framework;
namespace NzbDrone.Core.Test.MetadataSource.Goodreads
{
[TestFixture]
[Ignore("Waiting for metadata to be back again", Until = "2025-05-15 00:00:00Z")]
[Ignore("Waiting for metadata to be back again", Until = "2026-01-15 00:00:00Z")]
public class BookInfoProxyFixture : CoreTest<BookInfoProxy>
{
private MetadataProfile _metadataProfile;

View File

@@ -15,7 +15,7 @@ using NzbDrone.Test.Common;
namespace NzbDrone.Core.Test.MetadataSource.Goodreads
{
[TestFixture]
[Ignore("Waiting for metadata to be back again", Until = "2025-05-15 00:00:00Z")]
[Ignore("Waiting for metadata to be back again", Until = "2026-01-15 00:00:00Z")]
public class BookInfoProxySearchFixture : CoreTest<BookInfoProxy>
{
[SetUp]

View File

@@ -264,7 +264,7 @@ namespace NzbDrone.Core.Datastore
protected void Delete(SqlBuilder builder)
{
var sql = builder.AddDeleteTemplate(typeof(TModel)).LogQuery();
var sql = builder.AddDeleteTemplate(typeof(TModel));
using (var conn = _database.OpenConnection())
{

View File

@@ -40,6 +40,7 @@ namespace NzbDrone.Core.Datastore
Environment.SetEnvironmentVariable("No_Expand", "true");
Environment.SetEnvironmentVariable("No_SQLiteXmlConfigFile", "true");
Environment.SetEnvironmentVariable("No_PreLoadSQLite", "true");
Environment.SetEnvironmentVariable("No_SQLiteFunctions", "true");
}
public DbFactory(IMigrationController migrationController,

View File

@@ -653,5 +653,7 @@
"UnmappedFiles": "المجلدات غير المعينة",
"UpdateAppDirectlyLoadError": "تعذر تحديث {appName} مباشرة ،",
"Clone": "قريب",
"BuiltIn": "مدمج"
"BuiltIn": "مدمج",
"AddNewAuthorRootFolderHelpText": "سيتم إنشاء المجلد الفرعي \"{folder}\" تلقائيًا",
"AddRootFolder": "إضافة مجلد جذر"
}

View File

@@ -695,5 +695,7 @@
"AuthenticationRequired": "Изисква се удостоверяване",
"AuthenticationRequiredPasswordHelpTextWarning": "Въведете нова парола",
"ApplyChanges": "Прилагане на промените",
"AuthenticationRequiredPasswordConfirmationHelpTextWarning": "Потвърдете новата парола"
"AuthenticationRequiredPasswordConfirmationHelpTextWarning": "Потвърдете новата парола",
"AddNewAuthorRootFolderHelpText": "Подпапката „{0}“ ще бъде създадена автоматично",
"AddRootFolder": "Добавяне на коренна папка"
}

View File

@@ -242,7 +242,7 @@
"PackageVersion": "Versió del paquet",
"PageSize": "Mida de la pàgina",
"PageSizeHelpText": "Nombre d'elements per mostrar a cada pàgina",
"Proper": "Proper",
"Proper": "Correcte",
"ProxyBypassFilterHelpText": "Utilitzeu ',' com a separador i '*.' com a comodí per als subdominis",
"ProxyCheckBadRequestMessage": "No s'ha pogut provar el servidor intermediari. Codi d'estat: {0}",
"ProxyCheckFailedToTestMessage": "No s'ha pogut provar el servidor intermediari: {0}",
@@ -316,7 +316,7 @@
"URLBase": "Base URL",
"Backups": "Còpies de seguretat",
"Connections": "Connexions",
"CopyUsingHardlinksHelpText": "Utilitzeu els enllaços durs quan intenteu copiar fitxers de torrents que encara s'estan sembrant",
"CopyUsingHardlinksHelpText": "Els enllaços durs permeten a Readarr importar torrents de sembra a la carpeta de la sèrie sense prendre espai de disc extra o copiar tot el contingut del fitxer. Els enllaços durs només funcionaran si l'origen i la destinació estan en el mateix volum",
"DeleteBackup": "Suprimeix la còpia de seguretat",
"DeleteBackupMessageText": "Esteu segur que voleu suprimir la còpia de seguretat '{name}'?",
"DeleteDownloadClient": "Suprimeix el client de descàrrega",
@@ -336,7 +336,7 @@
"IgnoredAddresses": "Adreces ignorades",
"IgnoredPlaceHolder": "Afegeix una nova restricció",
"ImportExtraFiles": "Importa fitxers addicionals",
"ImportFailedInterp": "ImportFailedInterp",
"ImportFailedInterp": "Importació fallida: {0}",
"IncludeHealthWarningsHelpText": "Inclou advertències de salut",
"NotificationTriggers": "Activadors de notificacions",
"NoUpdatesAreAvailable": "No hi ha actualitzacions disponibles",
@@ -428,52 +428,52 @@
"BlocklistRelease": "Publicació de la llista de bloqueig",
"HasPendingChangesNoChanges": "Sense Canvis",
"ManualImportSelectEdition": "Importació manual - Seleccioneu la pel·lícula",
"MissingFromDisk": "{appName} no ha pogut trobar el fitxer al disc, de manera que el fitxer es desenllaçarà de la pel·lícula a la base de dades",
"MissingFromDisk": "Readarr no ha pogut trobar el fitxer al disc, de manera que el fitxer es desenllaçarà de la pel·lícula a la base de dades",
"SupportsRssvalueRSSIsNotSupportedWithThisIndexer": "RSS no és compatible amb aquest indexador",
"SupportsSearchvalueWillBeUsedWhenAutomaticSearchesArePerformedViaTheUIOrByReadarr": "S'utilitzarà quan es realitzin cerques automàtiques mitjançant la interfície d'usuari o per {appName}",
"CutoffHelpText": "Un cop s'assoleixi aquesta qualitat, {appName} ja no baixarà pel·lícules",
"SupportsSearchvalueWillBeUsedWhenAutomaticSearchesArePerformedViaTheUIOrByReadarr": "S'utilitzarà quan es realitzin cerques automàtiques mitjançant la interfície d'usuari o per Readarr",
"CutoffHelpText": "Un cop s'assoleixi aquesta qualitat, Readarr ja no baixarà pel·lícules",
"ResetAPIKeyMessageText": "Esteu segur que voleu restablir la clau API?",
"PropersAndRepacks": "Propietats i Repacks",
"RemotePathMappingCheckFolderPermissions": "{appName} pot veure però no accedir al directori de descàrregues {0}. Error de permisos probable.",
"RemotePathMappingCheckFolderPermissions": "Readarr pot veure però no accedir al directori de descàrregues {1}. Error de permisos probable.",
"RescanAuthorFolderAfterRefresh": "Torna a escanejar la carpeta de pel·lícules després de l'actualització",
"RescanAfterRefreshHelpText": "Torneu a escanejar la carpeta de la pel·lícula després d'actualitzar la pel·lícula",
"RestartReadarr": "Reinicia {appName}",
"RestartReadarr": "Reinicia Readarr",
"ShowRelativeDatesHelpText": "Mostra dates relatives (avui/ahir/etc) o absolutes",
"ShowSearchActionHelpText": "Mostra el botó de cerca al passar el cursor",
"TheAuthorFolderAndAllOfItsContentWillBeDeleted": "La carpeta de pel·lícules '{0}' i tot el seu contingut es suprimiran.",
"UrlBaseHelpTextWarning": "Cal reiniciar perquè tingui efecte",
"ApplicationURL": "URL de l'aplicació",
"ApplicationUrlHelpText": "URL extern de l'aplicació, inclòs http(s)://, port i URL base",
"BackupFolderHelpText": "Els camins relatius estaran sota el directori AppData del {appName}",
"BackupFolderHelpText": "Els camins relatius estaran sota el directori AppData de Readarr",
"CancelPendingTask": "Esteu segur que voleu cancel·lar aquesta tasca pendent?",
"ChownGroupHelpTextWarning": "Això només funciona si l'usuari que executa {appName} és el propietari del fitxer. És millor assegurar-se que el client de descàrrega utilitza el mateix grup que {appName}.",
"ChownGroupHelpTextWarning": "Això només funciona si l'usuari que executa Readarr és el propietari del fitxer. És millor assegurar-se que el client de descàrrega utilitza el mateix grup que Readarr.",
"ConnectSettingsSummary": "Notificacions, connexions a servidors/reproductors multimèdia i scripts personalitzats",
"DeleteEmptyFoldersHelpText": "Suprimeix les carpetes de pel·lícules buides durant l'exploració del disc i quan s'esborren els fitxers de pel·lícules",
"DeleteImportListMessageText": "Esteu segur que voleu suprimir la llista '{name}'?",
"DeleteMetadataProfileMessageText": "Esteu segur que voleu suprimir el perfil de qualitat {0}",
"DeleteMetadataProfileMessageText": "Esteu segur que voleu suprimir el perfil de metadades {name}?",
"ForMoreInformationOnTheIndividualDownloadClientsClickOnTheInfoButtons": "Per obtenir més informació sobre els clients de baixada individuals, feu clic als botons de més informació.",
"ForMoreInformationOnTheIndividualIndexersClickOnTheInfoButtons": "Per obtenir més informació sobre els indexadors individuals, feu clic als botons d'informació.",
"ForMoreInformationOnTheIndividualListsClickOnTheInfoButtons": "Per obtenir més informació sobre les llistes d'importació individuals, feu clic als botons d'informació.",
"IndexerPriorityHelpText": "Prioritat de l'indexador d'1 (la més alta) a 50 (la més baixa). Per defecte: 25. S'utilitza quan s'agafa llançaments com a desempat per a versions iguals, {appName} encara utilitzarà tots els indexadors habilitats per a la sincronització i la cerca RSS",
"IndexerRssHealthCheckNoIndexers": "No hi ha indexadors disponibles amb la sincronització RSS activada, {appName} no capturarà les noves versions automàticament",
"IndexerSearchCheckNoAutomaticMessage": "No hi ha indexadors disponibles amb la cerca automàtica activada, {appName} no proporcionarà cap resultat de cerca automàtica",
"IndexerSearchCheckNoInteractiveMessage": "No hi ha indexadors amb la cerca interactiva activada, {appName} no obtindrà cap resultat de cerca",
"IndexerPriorityHelpText": "Prioritat de l'indexador d'1 (la més alta) a 50 (la més baixa). Per defecte: 25. Usada per a desempatar llançaments iguals quan es capturen, Readarr continuarà usant tots els indexadores habilitats per a Sincronització d'RSS i Cerca.",
"IndexerRssHealthCheckNoIndexers": "No hi ha indexadors disponibles amb la sincronització RSS activada, Readarr no capturarà les noves versions automàticament",
"IndexerSearchCheckNoAutomaticMessage": "No hi ha indexadors disponibles amb la cerca automàtica activada, Readarr no proporcionarà cap resultat de cerca automàtica",
"IndexerSearchCheckNoInteractiveMessage": "No hi ha indexadors amb la cerca interactiva activada, Readarr no obtindrà cap resultat de cerca",
"IsCutoffUpgradeUntilThisQualityIsMetOrExceeded": "Actualitzeu fins que s'assoleixi o superi aquesta qualitat",
"IsTagUsedCannotBeDeletedWhileInUse": "No es pot suprimir mentre està en ús",
"LaunchBrowserHelpText": " Obriu un navegador web i navegueu a la pàgina d'inici de {appName} a l'inici de l'aplicació.",
"LaunchBrowserHelpText": " Obriu un navegador web i navegueu a la pàgina d'inici de Readarr a l'inici de l'aplicació.",
"LoadingBookFilesFailed": "No s'han pogut carregar els fitxers de pel·lícules",
"NoHistory": "Sense història",
"NoHistory": "Sense història.",
"OnBookFileDeleteForUpgradeHelpText": "Al suprimir el fitxer de pel·lícula per a l'actualització",
"OnBookFileDeleteHelpText": "Al suprimir fitxer de pel·lícula",
"ReleaseBranchCheckOfficialBranchMessage": "La branca {0} no és una branca de llançament de {appName} vàlida, no rebreu actualitzacions",
"ReleaseBranchCheckOfficialBranchMessage": "La branca {0} no és una branca de llançament de Readarr vàlida, no rebreu actualitzacions",
"ReleaseDate": "Dates de llançament",
"RemotePathMappingCheckDownloadPermissions": "{appName} pot veure però no accedir a la pel·lícula baixada {0}. Error de permisos probable.",
"RemotePathMappingCheckFilesGenericPermissions": "El client de baixada {0} ha informat de fitxers a {1} però {appName} no pot veure aquest directori. És possible que hàgiu d'ajustar els permisos de la carpeta.",
"RemotePathMappingCheckGenericPermissions": "El client de baixada {0} col·loca les baixades a {1} però {appName} no pot veure aquest directori. És possible que hàgiu d'ajustar els permisos de la carpeta.",
"ReplaceIllegalCharactersHelpText": "Substitueix caràcters il·legals. Si no es marca, {appName} els eliminarà",
"RemotePathMappingCheckDownloadPermissions": "Readarr pot veure però no accedir a la pel·lícula baixada {0}. Error de permisos probable.",
"RemotePathMappingCheckFilesGenericPermissions": "El client de baixada {0} ha informat de fitxers a {1} però Readarr no pot veure aquest directori. És possible que hàgiu d'ajustar els permisos de la carpeta.",
"RemotePathMappingCheckGenericPermissions": "El client de baixada {0} col·loca les baixades a {1} però Readarr no pot veure aquest directori. És possible que hàgiu d'ajustar els permisos de la carpeta.",
"ReplaceIllegalCharactersHelpText": "Substitueix caràcters il·legals. Si no es marca, Readarr els eliminarà",
"RssSyncIntervalHelpText": "Interval en minuts. Establiu a zero per desactivar (això aturarà tota captura automàtica de llançaments)",
"SelectedCountBooksSelectedInterp": "S'han seleccionat {0} pel·lícules",
"SettingsRemotePathMappingLocalPathHelpText": "Camí que {appName} hauria d'utilitzar per accedir al camí remot localment",
"SettingsRemotePathMappingLocalPathHelpText": "Camí que Readarr hauria d'utilitzar per accedir al camí remot localment",
"ShortDateFormat": "Format de data curta",
"ShowBookTitleHelpText": "Mostra el títol de la pel·lícula sota el cartell",
"ShowRelativeDates": "Mostra les dates relatives",
@@ -491,34 +491,34 @@
"UserAgentProvidedByTheAppThatCalledTheAPI": "Agent d'usuari proporcionat per l'aplicació per fer peticions a l'API",
"BranchUpdateMechanism": "Branca utilitzada pel mecanisme d'actualització extern",
"WriteTagsNo": "Mai",
"RestartReloadNote": "Nota: {appName} es reiniciarà i tornarà a carregar automàticament la interfície d'usuari durant el procés de restauració.",
"RestartReloadNote": "Nota: Readarr es reiniciarà i tornarà a carregar automàticament la interfície d'usuari durant el procés de restauració.",
"Series": "Sèries",
"ShownAboveEachColumnWhenWeekIsTheActiveView": "Es mostra a sobre de cada columna quan la setmana és la visualització activa",
"SorryThatAuthorCannotBeFound": "Ho sentim, aquesta pel·lícula no s'ha trobat.",
"SorryThatBookCannotBeFound": "Ho sentim, aquesta pel·lícula no s'ha trobat.",
"SupportsSearchvalueWillBeUsedWhenInteractiveSearchIsUsed": "S'utilitzarà quan s'utilitzi la cerca interactiva",
"ThisWillApplyToAllIndexersPleaseFollowTheRulesSetForthByThem": "Això s'aplicarà a tots els indexadors, si us plau, seguiu les regles establertes per ells",
"UnableToLoadHistory": "No es pot carregar l'historial",
"UnableToLoadHistory": "No es pot carregar l'historial.",
"IconTooltip": "Programat",
"ReadarrTags": "Etiquetes de {appName}",
"ReadarrTags": "Etiquetes de Readarr",
"DownloadPropersAndRepacksHelpTexts1": "Si s'ha d'actualitzar automàticament o no a Propers/Repacks",
"GrabReleaseMessageText": "{appName} no ha pogut determinar per a quina pel·lícula era aquest llançament. És possible que {appName} no pugui importar automàticament aquesta versió. Voleu capturar \"{0}\"?",
"GrabReleaseMessageText": "Readarr no ha pogut determinar per a quina pel·lícula era aquest llançament. És possible que Readarr no pugui importar automàticament aquesta versió. Voleu capturar '{0}'?",
"IsCutoffCutoff": "Requisit",
"MountCheckMessage": "El muntatge que conté una ruta de pel·lícula es munta com a només de lectura: ",
"RescanAfterRefreshHelpTextWarning": "{appName} no detectarà automàticament els canvis als fitxers quan no estigui configurat com a \"Sempre\"",
"RescanAfterRefreshHelpTextWarning": "Readarr no detectarà automàticament els canvis als fitxers quan no estigui configurat com a \"Sempre\"",
"ShowUnknownAuthorItems": "Mostra elements de pel·lícula desconeguda",
"Size": " Mida",
"SkipFreeSpaceCheckWhenImportingHelpText": "Utilitzeu-lo quan {appName} no pugui detectar espai lliure a la carpeta arrel de la pel·lícula",
"SkipFreeSpaceCheckWhenImportingHelpText": "Utilitzeu-lo quan Readarr no pugui detectar espai lliure a la carpeta arrel de la pel·lícula",
"StandardBookFormat": "Format de pel·lícula estàndard",
"UnableToAddANewImportListExclusionPleaseTryAgain": "No es pot afegir una nova llista d'exclusió, torneu-ho a provar.",
"UnableToLoadReleaseProfiles": "No es poden carregar els perfils de retard",
"UnmonitoredHelpText": "Inclou pel·lícules no monitorades al canal iCal",
"UpdateAll": "Actualitzar-ho tot",
"AutoUnmonitorPreviouslyDownloadedBooksHelpText": "Les pel·lícules suprimides del disc no es descarten automàticament al {appName}",
"AutoUnmonitorPreviouslyDownloadedBooksHelpText": "Les pel·lícules suprimides del disc no es descarten automàticament al Readarr",
"ChownGroupHelpText": "Nom del grup o gid. Utilitzeu gid per a sistemes de fitxers remots.",
"AuthorClickToChangeBook": "Feu clic per canviar la pel·lícula",
"ChmodFolderHelpTextWarning": "Això només funciona si l'usuari que executa {appName} és el propietari del fitxer. És millor assegurar-se que el client de descàrrega estableixi correctament els permisos.",
"CopyUsingHardlinksHelpTextWarning": "De tant en tant, els bloquejos de fitxers poden impedir reanomenar els fitxers que s'estan sembrant. Podeu desactivar temporalment la compartició i utilitzar la funció de reanomenar de {appName} com a solució.",
"ChmodFolderHelpTextWarning": "Això només funciona si l'usuari que executa Readarr és el propietari del fitxer. És millor assegurar-se que el client de descàrrega estableixi correctament els permisos.",
"CopyUsingHardlinksHelpTextWarning": "De tant en tant, els bloquejos de fitxers poden impedir reanomenar els fitxers que s'estan sembrant. Podeu desactivar temporalment la compartició i utilitzar la funció de reanomenar de Readarr com a solució.",
"CouldntFindAnyResultsForTerm": "No s'ha pogut trobar cap resultat per a '{0}'",
"CreateEmptyAuthorFolders": "Creeu carpetes buides per a les pel·lícules",
"CreateEmptyAuthorFoldersHelpText": "Creeu carpetes de pel·lícules que falten durant l'exploració del disc",
@@ -531,32 +531,32 @@
"ImportExtraFilesHelpText": "Importeu fitxers addicionals coincidents (subtítols, nfo, etc.) després d'importar un fitxer de pel·lícula",
"ImportListExclusions": "Suprimeix l'exclusió de la llista d'importació",
"LongDateFormat": "Format de data llarga",
"MaximumSizeHelpText": "Mida màxima per a una versió que es pot capturar en MB. Establiu a zero per establir-lo en il·limitat",
"MaximumSizeHelpText": "Mida màxima per a una versió que es pot capturar en MB. Establiu a zero per establir-lo en il·limitat,",
"MetadataProfile": "perfil de metadades",
"MetadataProfiles": "perfil de metadades",
"OnBookFileDelete": "Al suprimir fitxer de pel·lícula",
"OnBookFileDeleteForUpgrade": "Al suprimir el fitxer de pel·lícula per a l'actualització",
"ReadarrSupportsAnyDownloadClient": "{appName} admet molts clients de baixada de torrent i usenet populars.",
"ReadarrSupportsAnyIndexerThatUsesTheNewznabStandardAsWellAsOtherIndexersListedBelow": "{appName} admet qualsevol indexador que utilitzi l'estàndard Newznab, així com altres indexadors que s'enumeren a continuació.",
"ReadarrSupportsAnyDownloadClient": "Readarr admet molts clients de baixada de torrent i usenet populars.",
"ReadarrSupportsAnyIndexerThatUsesTheNewznabStandardAsWellAsOtherIndexersListedBelow": "Readarr admet qualsevol indexador que utilitzi l'estàndard Newznab, així com altres indexadors que s'enumeren a continuació.",
"RecycleBinHelpText": "Els fitxers de pel·lícula aniran aquí quan se suprimeixin en lloc de suprimir-se permanentment",
"RenameBooksHelpText": "{appName} utilitzarà el nom del fitxer existent si el reanomenat està desactivat",
"RequiredHelpText": "El llançament ha de contenir almenys un d'aquests termes (no distingeix entre majúscules i minúscules)",
"UILanguageHelpText": "Idioma que utilitzarà {appName} per a la interfície d'usuari",
"RenameBooksHelpText": "Readarr utilitzarà el nom del fitxer existent si el reanomenat està desactivat",
"RequiredHelpText": "Aquesta condició {0} ha de coincidir amb el format personalitzat a aplicar. En cas contrari, una única coincidència {0} és suficient.",
"UILanguageHelpText": "Idioma que utilitzarà Readarr per a la interfície d'usuari",
"UnableToAddANewRootFolderPleaseTryAgain": "No es pot afegir un indexador nou, torneu-ho a provar.",
"UnableToLoadMetadataProfiles": "No es poden carregar els perfils de qualitat",
"UpdateMechanismHelpText": "Utilitzeu l'actualitzador integrat de {appName} o un script",
"UpdateMechanismHelpText": "Utilitzeu l'actualitzador integrat de Readarr o un script",
"UpdateSelected": "Actualització seleccionada",
"Database": "Base de dades",
"DeleteQualityProfileMessageText": "Esteu segur que voleu suprimir el perfil de qualitat '{name}'?",
"DeleteReleaseProfile": "Suprimeix el perfil de llançament",
"DeleteReleaseProfileMessageText": "Esteu segur que voleu suprimir aquest perfil de retard?",
"DeleteRootFolderMessageText": "Esteu segur que voleu suprimir l'indexador '{0}'?",
"DeleteRootFolderMessageText": "Esteu segur que voleu suprimir la carpeta arrel '{name}'?",
"DeleteSelectedBookFiles": "Suprimeix els fitxers de pel·lícules seleccionats",
"DeleteSelectedBookFilesMessageText": "Esteu segur que voleu suprimir els fitxers de pel·lícules seleccionats?",
"IncludeUnknownAuthorItemsHelpText": "Mostra elements sense pel·lícula a la cua. Això podria incloure pel·lícules eliminades o qualsevol altra cosa de la categoria de {appName}",
"IncludeUnknownAuthorItemsHelpText": "Mostra elements sense pel·lícula a la cua. Això podria incloure pel·lícules eliminades o qualsevol altra cosa de la categoria de Readarr",
"LogLevelvalueTraceTraceLoggingShouldOnlyBeEnabledTemporarily": "El registre de traça només s'hauria d'habilitar temporalment",
"PortHelpTextWarning": "Cal reiniciar perquè tingui efecte",
"RemotePathMappingCheckImportFailed": "{appName} no ha pogut importar una pel·lícula. Comproveu els vostres registres per obtenir més informació.",
"RemotePathMappingCheckImportFailed": "Readarr no ha pogut importar una pel·lícula. Comproveu els vostres registres per obtenir més informació.",
"RemoveTagExistingTag": "Etiqueta existent",
"RemoveTagRemovingTag": "S'està eliminant l'etiqueta",
"SupportsSearchvalueSearchIsNotSupportedWithThisIndexer": "La cerca no és compatible amb aquest indexador",
@@ -564,9 +564,9 @@
"RequiredPlaceHolder": "Afegeix una nova restricció",
"20MinutesTwenty": "20 minuts: {0}",
"AlternateTitles": "Títols alternatius",
"AnalyticsEnabledHelpText": "Envieu informació anònima d'ús i errors als servidors de {appName}. Això inclou informació sobre el vostre navegador, quines pàgines {appName} WebUI feu servir, informes d'errors, així com el sistema operatiu i la versió del temps d'execució. Utilitzarem aquesta informació per prioritzar les funcions i les correccions d'errors.",
"AnalyticsEnabledHelpText": "Envieu informació anònima d'ús i errors als servidors de Readarr. Això inclou informació sobre el vostre navegador, quines pàgines de l'interfície de Readarr feu servir, informes d'errors, així com el sistema operatiu i la versió del temps d'execució. Utilitzarem aquesta informació per prioritzar les funcions i les correccions d'errors.",
"AnalyticsEnabledHelpTextWarning": "Cal reiniciar perquè tingui efecte",
"AuthenticationMethodHelpText": "Requereix nom d'usuari i contrasenya per accedir al radar",
"AuthenticationMethodHelpText": "Requereix el nom d'usuari i la contrasenya per accedir a {appName}",
"CalendarWeekColumnHeaderHelpText": "Es mostra a sobre de cada columna quan la setmana és la visualització activa",
"45MinutesFourtyFive": "45 minuts: {0}",
"60MinutesSixty": "60 minuts: {0}",
@@ -584,13 +584,13 @@
"BypassIfHighestQuality": "Bypass si és de màxima qualitat",
"MinimumCustomFormatScore": "Puntuació mínima de format personalitzat",
"CustomFormatScore": "Puntuació de format personalitzat",
"EnableRssHelpText": "S'utilitzarà quan {appName} cerqui publicacions periòdicament mitjançant RSS Sync",
"EnableRssHelpText": "S'utilitzarà quan Readarr cerqui publicacions periòdicament mitjançant RSS Sync",
"ImportListMultipleMissingRoots": "Falten diverses carpetes arrel per a les llistes d'importació: {0}",
"IndexerDownloadClientHelpText": "Especifiqueu quin client de baixada s'utilitza per a capturar des d'aquest indexador",
"ThemeHelpText": "Canvieu el tema de la interfície d'usuari de l'aplicació, el tema \"Automàtic\" utilitzarà el tema del vostre sistema operatiu per configurar el mode clar o fosc. Inspirat en Theme.Park",
"UnableToLoadCustomFormats": "No es poden carregar formats personalitzats",
"DeleteCustomFormat": "Suprimeix el format personalitzat",
"DeleteFormatMessageText": "Esteu segur que voleu suprimir l'etiqueta de format {0} ?",
"DeleteFormatMessageText": "Esteu segur que voleu suprimir l'etiqueta de format '{0}'?",
"ExportCustomFormat": "Exporta el format personalitzat",
"Formats": "Formats",
"IncludeCustomFormatWhenRenamingHelpText": "Inclou en {Custom Formats} el format de canvi de nom",
@@ -607,8 +607,8 @@
"CustomFormat": "Format personalitzat",
"CustomFormatSettings": "Configuració de formats personalitzats",
"CustomFormats": "Formats personalitzats",
"CutoffFormatScoreHelpText": "Un cop s'arribi a aquesta puntuació de format personalitzat, {appName} ja no baixarà pel·lícules",
"DeleteCustomFormatMessageText": "Esteu segur que voleu suprimir l'indexador '{0}'?",
"CutoffFormatScoreHelpText": "Un cop s'arribi a aquesta puntuació de format personalitzat, Readarr ja no baixarà pel·lícules",
"DeleteCustomFormatMessageText": "Esteu segur que voleu suprimir el format personalitzat {name}?",
"ImportListMissingRoot": "Falta la carpeta arrel per a les llistes d'importació: {0}",
"IndexerTagsHelpText": "Utilitzeu aquest indexador només per a pel·lícules amb almenys una etiqueta coincident. Deixeu-ho en blanc per utilitzar-ho amb totes les pel·lícules.",
"ColonReplacement": "Substitució de dos punts",
@@ -711,7 +711,7 @@
"DownloadClientDelugeSettingsDirectoryCompleted": "Directori al qual es mou quan s'hagi completat",
"DownloadClientDelugeSettingsDirectoryCompletedHelpText": "Ubicació opcional de les baixades completades, deixeu-lo en blanc per utilitzar la ubicació predeterminada de Deluge",
"DownloadClientDelugeSettingsDirectoryHelpText": "Ubicació opcional de les baixades completades, deixeu-lo en blanc per utilitzar la ubicació predeterminada de Deluge",
"WhatsNew": "Novetats",
"WhatsNew": "Què hi ha de nou?",
"SelectDropdown": "Seleccioneu...",
"NoCutoffUnmetItems": "No hi ha elements de tall no assolits",
"ApplyTagsHelpTextHowToApplyAuthors": "Com aplicar etiquetes a les pel·lícules seleccionades",
@@ -723,13 +723,13 @@
"ResetQualityDefinitions": "Restableix les definicions de qualitat",
"Small": "Petita",
"TotalSpace": "Espai total",
"BlocklistReleaseHelpText": "Impedeix que {appName} torni a capturar aquesta versió automàticament",
"BlocklistReleaseHelpText": "Impedeix que Readarr torni a capturar aquesta versió automàticament",
"CatalogNumber": "número de catàleg",
"LastWriteTime": "La darrera hora d'escriptura",
"NextExecution": "Propera execució",
"RemoveCompleted": "S'ha eliminat",
"SelectReleaseGroup": "Seleccioneu grup de llançament",
"CountDownloadClientsSelected": "{count} client(s) de baixada seleccionat(s)",
"CountDownloadClientsSelected": "{selectedCount} client(s) de baixada seleccionat(s)",
"Authors": "Autor",
"FreeSpace": "Espai lliure",
"ExtraFileExtensionsHelpText": "Llista separada per comes de fitxers addicionals per importar (.nfo s'importarà com a .nfo-orig)",
@@ -740,7 +740,7 @@
"RemoveFailed": "Ha fallat l'eliminació",
"ImportLists": "llista d'importació",
"RemovingTag": "S'està eliminant l'etiqueta",
"ApiKeyValidationHealthCheckMessage": "Actualitzeu la vostra clau de l'API perquè tingui almenys {length} caràcters. Podeu fer-ho mitjançant la configuració o el fitxer de configuració",
"ApiKeyValidationHealthCheckMessage": "Actualitzeu la vostra clau de l'API perquè tingui almenys {0} caràcters. Podeu fer-ho mitjançant la configuració o el fitxer de configuració",
"ExtraFileExtensionsHelpTextsExamples": "Exemples: '.sub, .nfo' o 'sub,nfo'",
"SourceTitle": "Títol de la font",
"NoEventsFound": "No s'han trobat esdeveniments",
@@ -750,9 +750,9 @@
"RecentChanges": "Canvis recents",
"Rejections": "Rebutjats",
"StatusEndedContinuing": "Continua",
"DeleteBookFileMessageText": "Esteu segur que voleu suprimir '{path}'?",
"DeleteBookFileMessageText": "Esteu segur que voleu suprimir '{0}'?",
"DownloadClientTagHelpText": "Utilitzeu aquest indexador només per a pel·lícules amb almenys una etiqueta coincident. Deixeu-ho en blanc per utilitzar-ho amb totes les pel·lícules.",
"DownloadClientRemovesCompletedDownloadsHealthCheckMessage": "El client de baixada {downloadClientName} està configurat per eliminar les baixades completades. Això pot provocar que les baixades s'eliminin del vostre client abans que {1} pugui importar-les.",
"DownloadClientRemovesCompletedDownloadsHealthCheckMessage": "El client de baixada {0} està configurat per eliminar les baixades completades. Això pot provocar que les baixades s'eliminin del vostre client abans que {1} pugui importar-les.",
"AutoRedownloadFailedFromInteractiveSearchHelpText": "Cerqueu i intenteu baixar automàticament una versió diferent quan es trobi una versió fallida a la cerca interactiva",
"FailedLoadingSearchResults": "No s'han pogut carregar els resultats de la cerca, torneu-ho a provar.",
"IndexerFlags": "Indicadors de l'indexador",
@@ -858,5 +858,269 @@
"FailedToFetchSettings": "No s'ha pogut recuperar la configuració",
"MonitoringOptions": "Opcions de monitoratge",
"RootFolderPathHelpText": "Els elements de la llista del directori arrel s'afegiran a",
"ThereWasAnErrorLoadingThisPage": "S'ha produït un error en carregar aquesta pàgina"
"ThereWasAnErrorLoadingThisPage": "S'ha produït un error en carregar aquesta pàgina",
"BookEditor": "Editor de llibres",
"BookProgressBarText": "{bookCount} / {totalBookCount} (Files: {bookFileCount})",
"ConsoleLogLevel": "Nivell de registre de la consola",
"Country": "País",
"DataFutureBooks": "Controla els llibres que encara no s'han publicat",
"EmbedMetadataInBookFiles": "Incrusta les metadades als fitxers de llibre",
"ForeignId": "ID estranger",
"GoToAuthorListing": "Ves a la llista d'autors",
"HasMonitoredBooksNoMonitoredBooksForThisAuthor": "No hi ha llibres supervisats per a aquest autor",
"HideBooks": "Oculta els llibres",
"ISBN": "ISBN",
"IgnoreDeletedBooks": "Ignora els llibres eliminats",
"IfYouDontAddAnImportListExclusionAndTheAuthorHasAMetadataProfileOtherThanNoneThenThisBookMayBeReaddedDuringTheNextAuthorRefresh": "Si no afegeixes una exclusió de llista d'importació i l'autor té un perfil de metadades distint de 'Cap', llavors aquest llibre pot ser de nou afegit durant el següent refresc d'autor.",
"IgnoredMetaHelpText": "Els llibres s'ignoraran si contenen un o més termes (insensible a majúscules i minúscules)",
"MassBookSearchWarning": "Esteu segur que voleu realitzar una cerca massiva de llibres per {0} llibres?",
"InteractiveSearchModalHeaderBookAuthor": "Cerca interactiva - {bookTitle} per {authorName}",
"IsCalibreLibraryHelpText": "Usa el servidor de contingut del Calibre per manipular la biblioteca",
"IsExpandedHideBooks": "Oculta els llibres",
"IsExpandedShowBooks": "Mostra els llibres",
"IsInUseCantDeleteAQualityProfileThatIsAttachedToAnAuthorOrImportList": "No es pot suprimir un perfil de qualitat que està adjuntat a un autor o llista d'importació",
"Iso639-3": "Codis de llengua ISO 639-3, o 'null', separats per comes",
"LibraryHelpText": "Nom de la biblioteca del servidor de contingut del Calibre. Deixeu-ho en blanc per defecte.",
"LogRotation": "Rotació del registre",
"LogSqlHelpText": "Registra totes les consultes SQL des de Readarr",
"MassBookSearch": "Cerca massiva de llibres",
"MetadataConsumers": "Consumidors de metadades",
"MetadataSourceHelpText": "Font alternativa de metadades (Deixa en blanc per defecte)",
"BookNaming": "Nom del llibre",
"CalibreLibrary": "Biblioteca del Calibre",
"OnImportFailure": "En importar fallada",
"OnAuthorAdded": "En afegir l'autor",
"SendMetadataToCalibre": "Envia les metadades al Calibre",
"SeriesNumber": "Número de sèrie",
"SeriesTotal": "Sèries ({0})",
"AllowFingerprintingHelpTextWarning": "Això requereix que el Readarr llegeixi parts del fitxer que alentiran els escanejos i poden causar una alta activitat de disc o de xarxa.",
"AuthorProgressBarText": "{availableBookCount} / {bookCount} (Total: {totalBookCount}, Fitxers: {bookFileCount})",
"AutomaticallySwitchEdition": "Canvia l'edició automàticament",
"BackupIntervalHelpText": "Interval per a fer una còpia de seguretat de la base de dades de Readarr i de la configuració",
"CalibreContentServer": "Servidor de contingut del Calibre",
"CalibreContentServerText": "L'ús d'un Servidor de Contingut Calibre (no Calibre Web) permet a Readarr afegir llibres a la vostra biblioteca Calibre i activar les conversions entre formats",
"CalibrePassword": "Contrasenya del Calibre",
"CalibreUsername": "Nom d'usuari del Calibre",
"CountIndexersSelected": "{selectedCount} indexador(s) seleccionat",
"DataListMonitorAll": "Controla els autors i tots els llibres de cada autor inclosos a la llista d'importació",
"DataMissingBooks": "Monitoritza els llibres que encara no tenen fitxers o que encara no s'han publicat",
"DefaultMonitorOptionHelpText": "Quins llibres s'han de controlar en afegir-los inicialment per als autors detectats en aquesta carpeta",
"DefaultQualityProfileIdHelpText": "Perfil de qualitat per defecte per als autors detectats en aquesta carpeta",
"EditList": "Edita la llista",
"EmbedMetadataHelpText": "Digues al Calibre que escrigui les metadades al fitxer de llibre real",
"ExistingBooks": "Llibres existents",
"ForeignIdHelpText": "L'identificador estranger de l'autor/llibre a excloure",
"FutureDaysHelpText": "Dies per a l'alimentació iCal per mirar al futur",
"ImportFailures": "Importa fallades",
"ImportListSpecificSettings": "Importa la configuració específica de la llista",
"ItsEasyToAddANewAuthorOrBookJustStartTypingTheNameOfTheItemYouWantToAdd": "És fàcil afegir un Autor nou o un Llibre simplement començar a escriure el nom de l'element que voleu afegir",
"LatestBook": "Últim llibre",
"MinPagesHelpText": "Ignora els llibres amb menys pàgines que això",
"MinPopularityHelpText": "La popularitat és la qualificació mitjana * nombre de vots",
"MinimumPopularity": "Popularitat mínima",
"MissingBooks": "Llibres que falten",
"MonitorAuthor": "Autor del monitor",
"MonitorBook": "Llibre del monitor",
"MonitorBookExistingOnlyWarning": "Aquest és un ajust ajustat de la configuració supervisada per a cada llibre. Utilitzeu l'opció d'Autor/Edita per controlar què passa amb els llibres acabats d'afegir",
"MonitorNewBooks": "Monitora els llibres nous",
"MonitorNewItemsHelpText": "Quins llibres nous s'han de controlar",
"MonitoredAuthorIsMonitored": "L'autor està monitoritzat",
"MonitoredAuthorIsUnmonitored": "L'autor no està monitoritzat",
"MonitoringOptionsHelpText": "Quins llibres s'han de controlar després d'afegir l'autor (ajust d'un sol cop)",
"MusicBrainzBookID": "ID del llibre MusicBrainz",
"MusicBrainzReleaseID": "ID de llançament del MusicBrainz",
"MusicBrainzTrackID": "ID de la pista MusicBrainz",
"NameLastFirst": "Cognom Nom",
"NewBooks": "Llibres nous",
"NoName": "No mostris el nom",
"OnAuthorAddedHelpText": "En afegir l'autor",
"OnAuthorDelete": "En suprimir l'autor",
"OnAuthorDeleteHelpText": "En suprimir l'autor",
"OnBookDeleteHelpText": "En suprimir el llibre",
"OnBookRetagHelpText": "En reetiquetar el llibre",
"OnDownloadFailure": "A la fallada de baixada",
"OnDownloadFailureHelpText": "A la fallada de baixada",
"OnImportFailureHelpText": "En importar fallada",
"OnReleaseImport": "En publicar la importació",
"OnReleaseImportHelpText": "En publicar la importació",
"OutputFormatHelpText": "Opcionalment, demaneu al Calibre que es converteixi a altres formats en importar. Llista separada per comes.",
"PathHelpText": "Carpeta arrel que conté la biblioteca de llibres",
"PathHelpTextWarning": "Això ha de ser diferent del directori on el vostre client de baixada posa fitxers",
"PortHelpText": "Port del servidor de contingut del Calibre",
"PreviewRetag": "Reetiqueta de la vista prèvia",
"QualityProfileIdHelpText": "Els elements de la llista de perfils de qualitat s'han d'afegir amb",
"ReadarrSupportsMultipleListsForImportingBooksAndAuthorsIntoTheDatabase": "Readarr admet múltiples llistes per importar llibres i autors a la base de dades.",
"RecycleBinUnableToWriteHealthCheck": "No s'ha pogut escriure a la carpeta de contenidors de reciclatge configurada: {0}. Assegureu-vos que aquest camí existeix i que l'usuari que executa el Readarr pot escriure",
"RefreshAuthor": "Actualitza l'autor",
"RefreshBook": "Actualitza el llibre",
"RefreshInformation": "Actualitza la informació",
"RemotePathMappingsInfo": "Remote Path Mappings són molt rarament necessaris, si {app} i el vostre client de descàrrega estan en el mateix sistema, és millor que coincideixi amb els vostres camins. Per a més informació, vegeu el [wiki]({wikiLink}).",
"SearchForMonitoredBooks": "Cerca llibres monitoritzats",
"SearchForNewItems": "Cerca elements nous",
"SelectBook": "Selecciona el llibre",
"SelectEdition": "Selecciona l'edició",
"ShouldMonitorExisting": "Controla els llibres existents",
"ShouldSearchHelpText": "Cerca indexadors per als elements nous afegits. Utilitza amb precaució per a llistes grans.",
"ShowBookCount": "Mostra el recompte de llibres",
"ShowTitleHelpText": "Mostra el nom de l'autor sota el cartell",
"StatusEndedDeceased": "Defunció",
"TooManyBooks": "Falten o hi ha massa llibres? Modifica o crea un nou",
"UnableToLoadMetadataProviderSettings": "No s'ha pogut carregar la configuració del proveïdor de metadades",
"UrlBaseHelpText": "Afegeix un prefix a l'URL del Calibre, p. ex. http://[host]:[port]/[urlBase]",
"ShouldMonitorExistingHelpText": "Controla automàticament els llibres d'aquesta llista que ja són a Readarr",
"ShowBannersHelpText": "Mostra els bàners en lloc dels noms",
"ShowLastBook": "Mostra l'últim llibre",
"ShowName": "Mostra el nom",
"SkipBooksWithNoISBNOrASIN": "Omet els llibres sense ISBN ni ASIN",
"SkipPartBooksAndSets": "Omet els llibres de parts i els conjunts",
"SkipSecondarySeriesBooks": "Omet els llibres de les sèries secundàries",
"SpecificBook": "Llibre específic",
"FileDetails": "Detalls del fitxer",
"FilesTotal": "Fitxers ({0})",
"FilterAnalyticsEvents": "Filtra els esdeveniments d'anàlisi",
"FilterAuthor": "Filtra l'autor",
"FilterSentryEventsHelpText": "Filtra els esdeveniments d'error d'usuari coneguts perquè s'enviïn com a Analytics",
"IsExpandedShowFileInfo": "Mostra la informació del fitxer",
"IsInUseCantDeleteAMetadataProfileThatIsAttachedToAnAuthorOrImportList": "No es pot suprimir un perfil de metadades que està adjuntat a un autor o a una llista d'importació",
"PasswordHelpText": "Contrasenya del servidor de contingut del Calibre",
"PastDays": "Dies passats",
"WriteTagsAll": "Tots els fitxers; només importació inicial",
"TagsHelpText": "Aplica als autors amb almenys una etiqueta coincident. Deixa en blanc per aplicar a tots els autors",
"TotalBookCountBooksTotalBookFileCountBooksWithFilesInterp": "{0} llibres en total. {1} llibres amb fitxers.",
"TrackNumber": "Número de pista",
"TrackTitle": "Títol de la pista",
"WatchLibraryForChangesHelpText": "Torna a explorar automàticament quan els fitxers canviïn en una carpeta arrel",
"WatchRootFoldersForFileChanges": "Vigila les carpetes arrel per als canvis de fitxer",
"WriteAudioTagsScrub": "Neteja les etiquetes existents",
"WriteAudioTagsScrubHelp": "Elimina les etiquetes existents dels fitxers, deixant només les afegides pel Readarr.",
"LogSQL": "Log SQL",
"AnyEditionOkHelpText": "Readarr canviarà automàticament a l'edició que coincideixi millor amb els fitxers baixats",
"AudioFileMetadata": "Escriu les metadades als fitxers d'àudio",
"AuthorEditor": "Editor de l'autor",
"AuthorFolderFormat": "Format de carpeta d'autor",
"EntityName": "Nom de l'entitat",
"FilterPlaceHolder": "Filtra el llibre",
"UsernameHelpText": "Nom d'usuari del servidor de contingut del Calibre",
"OnBookDelete": "En suprimir el llibre",
"BypassIfHighestQualityHelpText": "Evita el retard quan el llançament tingui la qualitat més alta habilitada en el perfil de qualitat",
"CountImportListsSelected": "{selectedCount} llista(es) d'importació seleccionada",
"DataNone": "No es controlarà cap llibre",
"IsExpandedHideFileInfo": "Amaga la informació del fitxer",
"NoTagsHaveBeenAddedYet": "No s'han afegit etiquetes encara. Afegeix etiquetes per enllaçar autors amb perfils de retard, restriccions o notificacions. Feu clic a {0} per obtenir més informació sobre les etiquetes a Readarr.",
"DeleteMetadataProfile": "Suprimeix el perfil de metadades",
"FutureBooks": "Llibres futurs",
"FutureDays": "Dies de futur",
"MetadataProfileIdHelpText": "Els elements de la llista de perfils de metadades s'han d'afegir amb",
"NameFirstLast": "Nom Cognom",
"DownloadPropersAndRepacksHelpTexts2": "Usa 'No prefereixis' per ordenar per puntuació de paraules preferida sobre propers/repacks",
"IndexerIdHelpTextWarning": "L'ús d'un indexador específic amb paraules preferides pot conduir a versions duplicades",
"LogRotateHelpText": "Nombre màxim de fitxers de registre a mantenir desats a la carpeta de registres",
"MetadataProviderSource": "Font del proveïdor de metadades",
"MissingBooksAuthorMonitored": "Llibres que falten (controlat per l'autor)",
"MissingBooksAuthorNotMonitored": "Llibres que falten (Autor no supervisat)",
"MonitoredHelpText": "Readarr cercarà i descarregarà un llibre",
"UseCalibreContentServer": "Usa el servidor de contingut del Calibre",
"MonitorNewItems": "Monitora els llibres nous",
"SearchForAllCutoffUnmetBooks": "Cerca tots els llibres retallats i no satisfets",
"SearchForAllMissingBooks": "Cerca tots els llibres que falten",
"TheFollowingFilesWillBeDeleted": "S'eliminaran els següents fitxers:",
"TagsSettingsSummary": "Gestiona les etiquetes d'autor, perfil, restricció i notificació",
"WriteAudioTags": "Etiqueta els fitxers d'àudio amb metadades",
"AddImportListExclusionHelpText": "Evita que el llibre s'afegeixi al Readarr mitjançant la importació de llistes o l'actualització de l'autor",
"BookStudio": "Book Studio",
"BookTitle": "Títol del llibre",
"Books": "Llibres",
"DataListMonitorSpecificBook": "Autors de monitors, però només monitoritza els llibres inclosos explícitament a la llista",
"OnBookTagUpdate": "En actualitzar l'etiqueta del llibre",
"CalibreNotCalibreWeb": "Readarr pot interactuar amb el servidor de continguts de Calibre. No pot utilitzar Calibre-Web, que és programari no relacionat.",
"ShouldMonitorHelpText": "Monitora els autors i llibres nous afegits d'aquesta llista",
"MusicBrainzRecordingID": "ID d'enregistrament del MusicBrainz",
"AllAuthorBooks": "Tots els autors",
"EditionsHelpText": "Canvia l'edició d'aquest llibre",
"BooksTotal": "Llibres ({0})",
"CountAuthorsSelected": "{selectedCount} autors seleccionats",
"DataNewAllBooks": "Monitora tots els llibres nous",
"LoadingEditionsFailed": "Ha fallat la càrrega d'edicions",
"MetadataSettingsSummary": "Crea fitxers de metadades quan s'importin llibres o s'actualitzi l'autor",
"SearchBoxPlaceHolder": "P. ex. Guerra i pau, goodreads:656, isbn:067003469X, asin:B00JCDK5ME",
"SkipRedownloadHelpText": "Evita que el Readarr intenti baixar versions alternatives per als elements eliminats",
"CalibreMetadata": "Metadades del Calibre",
"Bookshelf": "Prestatge",
"DataListMonitorNone": "No vigilar autors ni llibres",
"ImportListSettings": "Configuració general de la llista d'importació",
"AuthorIndex": "Índex de l'autor",
"AuthorNameHelpText": "El nom de l'autor/llibre a excloure (pot ser qualsevol cosa significativa)",
"BookIndex": "Índex del llibre",
"BookList": "Llista de llibres",
"CalibreHost": "Amfitrió del Calibre",
"CalibrePort": "Port del Calibre",
"CalibreSettings": "Paràmetres del Calibre",
"CalibreUrlBase": "Base d'url del Calibre",
"UpdateCovers": "Actualitza les converses",
"UpdateCoversHelpText": "Estableix les portades del llibre al Calibre perquè coincideixin amb les del Readarr",
"UseSslHelpText": "Usa SSL per a connectar al servidor de contingut Calibre",
"WriteBookTagsHelpTextWarning": "En seleccionar Tots els fitxers s'alteraran els fitxers existents quan s'importin.",
"WriteTagsNew": "Només per a baixades noves",
"AddedAuthorSettings": "Configuració de l'autor afegit",
"DefaultMetadataProfileIdHelpText": "Perfil predeterminat de metadades per als autors detectats en aquesta carpeta",
"ContinuingMoreBooksAreExpected": "S'esperen més llibres",
"DataNewBooks": "Supervisa els llibres nous publicats després del llibre més nou existent",
"AllowAuthorChangeClickToChangeAuthor": "Feu clic per canviar l'autor",
"AllowedLanguages": "Llengües permeses",
"ContinuingNoAdditionalBooksAreExpected": "No s'espera cap llibre addicional",
"DeleteBookFile": "Suprimeix el fitxer de llibre",
"ConvertToFormat": "Converteix a format",
"DataAllBooks": "Controla tots els llibres",
"DataExistingBooks": "Controla els llibres que tenen fitxers o que encara no s'han publicat",
"DeleteFormat": "Suprimeix el format",
"DeleteFilesHelpText": "Suprimeix els fitxers del llibre i la carpeta de l'autor",
"DiscNumber": "Número de disc",
"EndedAllBooksDownloaded": "Ended (Tots els llibres baixats)",
"FirstBook": "Primer llibre",
"HostHelpText": "Servidor de contingut del Calibre",
"MonitorExistingBooks": "Monitora els llibres existents",
"MusicBrainzAuthorID": "ID de l'autor del MusicBrainz",
"NameStyle": "Estil del nom de l'autor",
"PastDaysHelpText": "Dies per a l'alimentació iCal per a mirar el passat",
"DataFirstBook": "Fes el seguiment del primer llibre. Tots els altres llibres seran ignorats",
"DataLatestBook": "Controla els últims llibres i futurs",
"DataNewNone": "No vigila cap llibre nou",
"DefaultReadarrTags": "Etiquetes del lector per defecte",
"DefaultTagsHelpText": "Etiquetes de Readarr per defecte per als autors detectats en aquesta carpeta",
"DiscCount": "Comptador de discs",
"EditAuthor": "Edita l'autor",
"EditBook": "Edita el llibre",
"ExistingItems": "Elements existents",
"ManualDownload": "Baixada manual",
"MinimumPages": "Pàgines mínimes",
"CollapseMultipleBooks": "Redueix diversos llibres",
"CollapseMultipleBooksHelpText": "Redueix diversos llibres que es publiquen el mateix dia",
"ContinuingAllBooksDownloaded": "Continuant (tots els llibres baixats)",
"Development": "Desenvolupament",
"EnableAutomaticAddHelpText": "Afegeix autor/llibres al Readarr quan es realitzin sincronitzacions a través de la IU o per Readarr",
"RenameBooks": "Canvia el nom dels llibres",
"SearchBook": "Cerca al llibre",
"SelectedCountAuthorsSelectedInterp": "{0} Autor/s seleccionat/s",
"SetReadarrTags": "Estableix les etiquetes del Readarr",
"SkipBooksWithMissingReleaseDate": "Omet els llibres amb la data de publicació que manca",
"TheBooksFilesWillBeDeleted": "S'eliminaran els fitxers del llibre.",
"UpdatingIsDisabledInsideADockerContainerUpdateTheContainerImageInstead": "L'actualització està desactivada dins d'un contenidor d'acobladors. Actualitza la imatge del contenidor.",
"WriteTagsSync": "Tots els fitxers; mantén la sincronització amb Goodreads",
"ASIN": "ASIN",
"BookAvailableButMissing": "Llibre disponible, però desaparegut",
"CalibreOutputFormat": "Format de sortida del Calibre",
"CalibreOutputProfile": "Perfil de sortida del Calibre",
"BookFilesCountMessage": "No hi ha fitxers de llibre",
"BookMonitoring": "Seguiment del llibre",
"AllBooks": "Tots els llibres",
"AllowFingerprinting": "Permet la impressió digital",
"AllowFingerprintingHelpText": "Utilitza l'empremta digital per millorar la precisió de la coincidència de llibres",
"ExistingTagsScrubbed": "Etiquetes existents rastrejades",
"NETCore": ".NET Core",
"WriteMetadataTags": "Escriu les etiquetes de les metadades",
"AddNewAuthorRootFolderHelpText": "La subcarpeta '{folder}' es crearà automàticament",
"AddRootFolder": "Afegeix una carpeta arrel",
"Book": "Llibre",
"AddNewBook": "Afegeix nou llibre",
"AddNewAuthor": "Afegeix nou autor"
}

View File

@@ -758,5 +758,7 @@
"NoCutoffUnmetItems": "Žádné neodpovídající nesplněné položky",
"DownloadClientDelugeSettingsDirectoryCompleted": "Adresář kam přesunout po dokončení",
"DownloadClientDelugeSettingsDirectoryCompletedHelpText": "Nepovinné - umístění kam přesunout dokončená stahování, pokud ponecháte prázné, použije se výchozí umístění Deluge",
"DownloadClientDelugeSettingsDirectoryHelpText": "Nepovinné - umístění stahovaných souborů, pokud ponecháte prázné, použije se výchozí umístění Deluge"
"DownloadClientDelugeSettingsDirectoryHelpText": "Nepovinné - umístění stahovaných souborů, pokud ponecháte prázné, použije se výchozí umístění Deluge",
"AddNewAuthorRootFolderHelpText": "Podsložka '{folder}' bude vytvořena automaticky",
"AddRootFolder": "Přidat kořenový adresář"
}

View File

@@ -671,5 +671,7 @@
"OnLatestVersion": "Den seneste version af {appName} er allerede installeret",
"WouldYouLikeToRestoreBackup": "Vil du gendanne sikkerhedskopien »{name}«?",
"MetadataProfile": "metadataprofil",
"Unknown": "Ukendt"
"Unknown": "Ukendt",
"AddRootFolder": "Tilføj rodmappe",
"AddNewAuthorRootFolderHelpText": "Undermappen '{0}' oprettes automatisk"
}

View File

@@ -1118,5 +1118,7 @@
"SelectBook": "Buch auswählen",
"SelectEdition": "Wähle Edition",
"LastSearched": "Letzte Suche",
"Unknown": "Unbekannt"
"Unknown": "Unbekannt",
"AddRootFolder": "Stammverzeichnis hinzufügen",
"AddNewAuthorRootFolderHelpText": "'{folder}' Unterordner wird automatisch erstellt werden"
}

View File

@@ -1020,5 +1020,7 @@
"Script": "Γραφή",
"UpdateAppDirectlyLoadError": "Δεν είναι δυνατή η απευθείας ενημέρωση του {appName},",
"ExternalUpdater": "Το {appName} έχει ρυθμιστεί να χρησιμοποιεί έναν εξωτερικό μηχανισμό ενημέρωσης",
"InstallLatest": "Εγκατάσταση πιο πρόσφατου"
"InstallLatest": "Εγκατάσταση πιο πρόσφατου",
"AddNewAuthorRootFolderHelpText": "Ο υποφάκελος \"{0}\" θα δημιουργηθεί αυτόματα",
"AddRootFolder": "Προσθήκη φακέλου ρίζας"
}

View File

@@ -11,7 +11,11 @@
"AddListExclusion": "Add List Exclusion",
"AddMissing": "Add missing",
"AddNew": "Add New",
"AddNewBook": "Add New Book",
"AddNewAuthor": "Add New Author",
"AddNewAuthorRootFolderHelpText": "'{folder}' subfolder will be created automatically",
"AddNewItem": "Add New Item",
"AddRootFolder": "Add Root Folder",
"AddedAuthorSettings": "Added Author Settings",
"AddingTag": "Adding tag",
"AgeWhenGrabbed": "Age (when grabbed)",

View File

@@ -237,7 +237,7 @@
"PosterSize": "Tamaño de póster",
"PreviewRename": "Previsualizar renombrado",
"Profiles": "Perfiles",
"Proper": "Proper",
"Proper": "Correcto",
"PropersAndRepacks": "Propers y Repacks",
"Protocol": "Protocolo",
"ProtocolHelpText": "Elige qué protocolo(s) usar y cuál se prefiere cuando se elige entre lanzamientos equivalentes",
@@ -564,7 +564,7 @@
"Label": "Etiqueta",
"MissingFromDisk": "Readarr no pudo encontrar el archivo en el disco, por lo que el archivo fue desvinculado del libro en la base de datos",
"RemotePathMappingCheckDownloadPermissions": "Readarr puede ver pero no acceder al libro descargado {0}. Probablemente sea un error de permisos.",
"RemotePathMappingCheckFolderPermissions": "Readarr puede ver pero no acceder al directorio de descarga {0}. Probablemente sea un error de permisos.",
"RemotePathMappingCheckFolderPermissions": "Readarr puede ver pero no acceder al directorio de descarga {1}. Probablemente sea un error de permisos.",
"RemotePathMappingCheckFilesGenericPermissions": "El cliente de descarga {0} informó de la existencia de archivos en {1} pero Readarr no puede ver este directorio. Puede que tengas que ajustar los permisos de la carpeta.",
"RemotePathMappingCheckGenericPermissions": "El cliente de descarga {0} coloca las descargas en {1} pero Readarr no puede ver este directorio. Puede que tengas que ajustar los permisos de la carpeta.",
"RemotePathMappingCheckImportFailed": "Readarr falló al importar un libro. Comprueba tus registros para más detalles.",
@@ -855,13 +855,13 @@
"NotificationsSettingsUseSslHelpText": "Conectar a {serviceName} sobre HTTPS en vez de HTTP",
"Rejections": "Rechazados",
"SelectIndexerFlags": "Seleccionar indicadores del indexador",
"RecycleBinUnableToWriteHealthCheck": "No se pudo escribir en la carpeta configurada de la papelera de reciclaje: {path}. Asegúrate de que esta ruta existe y es modificable por el usuario que ejecuta {appName}",
"RecycleBinUnableToWriteHealthCheck": "No se pudo escribir en la carpeta configurada de la papelera de reciclaje: {0}. Asegúrate de que esta ruta existe y es modificable por el usuario que ejecuta Readarr",
"SearchForAllMissingBooks": "Buscar todos los episodios perdidos",
"IndexerIdHelpText": "Especifica a qué indexador se aplica el perfil",
"ProfilesSettingsSummary": "Perfiles de calidad, de retraso de idioma y de lanzamiento",
"DataExistingBooks": "Monitoriza libros que no tienen archivos o que no se han lanzado aún",
"MonitoredAuthorIsMonitored": "El artista no está vigilado",
"RemotePathMappingsInfo": "Los mapeos de ruta remota son muy raramente solicitados, si {appName} y tu cliente de descarga están en el mismo sistema es mejor coincidir sus rutas. Para más información consulta la [wiki]({wikiLink}).",
"RemotePathMappingsInfo": "Los mapeos de ruta remota son muy raramente requeridos, si {app} y tu cliente de descarga están en el mismo sistema es mejor coincidir sus rutas. Para más información consulta la [wiki]({wikiLink}).",
"ShowBannersHelpText": "Muestra carteles en lugar de nombres",
"DefaultTagsHelpText": "Etiquetas predeterminadas de Readarr para los autores detectados en esta carpeta",
"UseSSL": "Usar SSL",
@@ -1118,5 +1118,9 @@
"InstallMajorVersionUpdateMessage": "Esta actualización instalará una nueva versión principal y podría no ser compatible con tu sistema. ¿Estás seguro que quieres instalar esta actualización?",
"InstallMajorVersionUpdateMessageLink": "Por favor revisa [{domain}]({url}) para más información.",
"LastSearched": "Último buscado",
"Unknown": "Desconocido"
"Unknown": "Desconocido",
"AddNewBook": "Añadir Nuevo Libro",
"AddNewAuthor": "Añadir Nuevo Autor",
"AddNewAuthorRootFolderHelpText": "La subcarpeta '{folder}' será creada automáticamente",
"AddRootFolder": "Añadir Carpeta Raíz"
}

View File

@@ -1118,5 +1118,9 @@
"DownloadPropersAndRepacksHelpTexts2": "\"Älä suosi\" käyttää Proper-/Repack-julkaisujen sijaan haluttua sanapisteytystä.",
"IgnoreDeletedBooks": "Ohita poistetut kirjat",
"LastSearched": "Edellinen haku",
"Unknown": "Tuntematon"
"Unknown": "Tuntematon",
"AddNewAuthorRootFolderHelpText": "Alikansio \"{folder}\" luodaan automaattisesti.",
"AddRootFolder": "Lisää juurikansio",
"AddNewBook": "Lisää uusi kirja",
"AddNewAuthor": "Lisää uusi kirjailija"
}

View File

@@ -1118,5 +1118,7 @@
"UpdateAppDirectlyLoadError": "Impossible de mettre à jour directement {appName},",
"InstallMajorVersionUpdateMessage": "Cette mise à jour installera une nouvelle version majeure et pourrait ne pas être compatible avec votre système. Êtes-vous sûr de vouloir installer cette mise à jour ?",
"LastSearched": "Dernière recherche",
"Unknown": "Inconnu"
"Unknown": "Inconnu",
"AddNewAuthorRootFolderHelpText": "Le sous-dossier « {folder} » sera créé automatiquement",
"AddRootFolder": "Ajouter un dossier racine"
}

View File

@@ -687,5 +687,7 @@
"Script": "תַסרִיט",
"UnmappedFiles": "תיקיות לא ממופות",
"UpdateAppDirectlyLoadError": "לא ניתן לעדכן את {appName} ישירות,",
"Clone": "סגור"
"Clone": "סגור",
"AddNewAuthorRootFolderHelpText": "תיקיית המשנה '{0}' תיווצר באופן אוטומטי",
"AddRootFolder": "הוסף תיקיית שורש"
}

View File

@@ -650,5 +650,7 @@
"OnLatestVersion": "रेडर का नवीनतम संस्करण पहले से ही स्थापित है",
"Script": "लिपि",
"UpdateAppDirectlyLoadError": "सीधे {appName} अद्यतन करने में असमर्थ,",
"Clone": "बंद करे"
"Clone": "बंद करे",
"AddNewAuthorRootFolderHelpText": "'{0}' सबफ़ोल्डर स्वचालित रूप से बनाया जाएगा",
"AddRootFolder": "रूट फ़ोल्डर जोड़ें"
}

View File

@@ -229,5 +229,6 @@
"DeleteSelectedBookFilesMessageText": "Jeste li sigurni da želite obrisati ovaj profil odgode?",
"Clone": "Zatvori",
"BuiltIn": "Ugrađeno",
"AptUpdater": "Koristi apt kako bi instalirao ažuriranje"
"AptUpdater": "Koristi apt kako bi instalirao ažuriranje",
"AddRootFolder": "Dodaj Korijensku Mapu"
}

View File

@@ -1085,5 +1085,7 @@
"PreviouslyInstalled": "Korábban telepítve",
"Script": "Szkript",
"UpdateAppDirectlyLoadError": "Nem lehetséges közvetlenül frissíteni a {appName}-t",
"Unknown": "Ismeretlen"
"Unknown": "Ismeretlen",
"AddNewAuthorRootFolderHelpText": "A „{folder}” almappa automatikusan létrejön",
"AddRootFolder": "Gyökérmappa hozzáadása"
}

View File

@@ -652,5 +652,7 @@
"OnLatestVersion": "Nýjasta útgáfan af {appName} er þegar uppsett",
"Script": "Handrit",
"UnmappedFiles": "Ókortlagðar möppur",
"UpdateAppDirectlyLoadError": "Ekki er hægt að uppfæra {appName} beint,"
"UpdateAppDirectlyLoadError": "Ekki er hægt að uppfæra {appName} beint,",
"AddNewAuthorRootFolderHelpText": "„{0}“ undirmöppan verður búin til sjálfkrafa",
"AddRootFolder": "Bæta við rótarmöppu"
}

View File

@@ -725,7 +725,7 @@
"AppUpdated": "{appName} Aggiornato",
"AllResultsAreHiddenByTheAppliedFilter": "Tutti i risultati sono nascosti dal filtro applicato",
"AutoRedownloadFailed": "Download fallito",
"AddListExclusion": "Aggiungi elenco esclusioni",
"AddListExclusion": "Aggiungi lista esclusioni",
"Location": "Posizione",
"ListsSettingsSummary": "Liste",
"RecentChanges": "Cambiamenti Recenti",
@@ -913,5 +913,9 @@
"UpdateAppDirectlyLoadError": "Impossibile aggiornare {appName} direttamente,",
"AutoRedownloadFailedFromInteractiveSearch": "Riesecuzione del download non riuscita dalla ricerca interattiva",
"AutoRedownloadFailedFromInteractiveSearchHelpText": "Cerca automaticamente e tenta di scaricare una versione diversa quando il rilascio non riuscito è stato acquisito dalla ricerca interattiva",
"Unknown": "Sconosciuto"
"Unknown": "Sconosciuto",
"AddNewAuthorRootFolderHelpText": "La sottocartella '{0}' verrà creata automaticamente",
"AddRootFolder": "Aggiungi Cartella Radice",
"AddNewBook": "Aggiungi nuovo libro",
"AddNewAuthor": "Aggiungi nuovo autore"
}

View File

@@ -651,5 +651,7 @@
"CurrentlyInstalled": "現在インストール中",
"DockerUpdater": "Dockerコンテナを更新して、更新を受信します",
"ExternalUpdater": "{appName}は、外部更新メカニズムを使用するように構成されています",
"OnLatestVersion": "{appName}の最新バージョンはすでにインストールされています"
"OnLatestVersion": "{appName}の最新バージョンはすでにインストールされています",
"AddRootFolder": "ルートフォルダを追加する",
"AddNewAuthorRootFolderHelpText": "'{0}'サブフォルダは自動的に作成されます"
}

View File

@@ -4,8 +4,8 @@
"AlternateTitles": "대체 제목",
"Analytics": "해석학",
"DownloadPropersAndRepacksHelpTexts1": "Propers / Repacks로 자동 업그레이드할지 여부",
"20MinutesTwenty": "120 분 : {0}",
"45MinutesFourtyFive": "60 분 : {0}",
"20MinutesTwenty": "20분 : {0}",
"45MinutesFourtyFive": "45분 : {0}",
"ChownGroupHelpText": "그룹 이름 또는 gid. 원격 파일 시스템에 gid를 사용하십시오.",
"DeleteReleaseProfile": "지연 프로필 삭제",
"DeleteIndexer": "인덱서 삭제",
@@ -780,5 +780,7 @@
"NotificationsSettingsUpdateMapPathsTo": "지도 경로",
"Series": "시리즈",
"IgnoreDownloadsHint": "{appName}가 이러한 다운로드를 더 이상 처리하지 않도록 중지합니다",
"InstallMajorVersionUpdateMessageLink": "상세 내용은 [{domain}]({url})을 확인하세요."
"InstallMajorVersionUpdateMessageLink": "상세 내용은 [{domain}]({url})을 확인하세요.",
"AddNewAuthorRootFolderHelpText": "'{folder}' 하위 폴더가 자동으로 생성됩니다",
"AddRootFolder": "루트 폴더 추가"
}

View File

@@ -206,5 +206,8 @@
"FailedLoadingSearchResults": "Kunne ikke laste søkeresultat, vennligst prøv igjen.",
"IgnoredPlaceHolder": "Legg til ny begrensning",
"RequiredPlaceHolder": "Legg til ny begrensning",
"UnableToAddANewRemotePathMappingPleaseTryAgain": "Kunne ikke legge til ny ekstern stimapping, vennligst prøv igjen."
"UnableToAddANewRemotePathMappingPleaseTryAgain": "Kunne ikke legge til ny ekstern stimapping, vennligst prøv igjen.",
"AddNewAuthorRootFolderHelpText": "Undermappa \"{folder}\" vil bli automatisk laget",
"AddRootFolder": "Legg til rotmappe",
"History": "Historikk"
}

View File

@@ -50,22 +50,22 @@
"ChangeHasNotBeenSavedYet": "Wijziging is nog niet opgeslagen",
"ChmodFolder": "chmod Map",
"ChmodFolderHelpText": "Octaal, toegepast tijdens importeren/hernoemen op media mappen en bestanden (zonder uitvoeringsbits)",
"ChmodFolderHelpTextWarning": "Dit werkt alleen als de gebruiker die {appName} draait de eigenaar is van het bestand. Het is beter om zeker te zijn dat de downloader de juiste rechten zet.",
"ChownGroupHelpText": "Groep naam of gid. Gebruik gid voor externe bestandssystemen.",
"ChownGroupHelpTextWarning": "Dit werkt alleen als de gebruiker die {appName} draait de eigenaar is van het bestand. Het is beter om zeker te zijn dat de downloader dezelfde groep gebruikt als {appName}.",
"ChmodFolderHelpTextWarning": "Dit werkt alleen als de gebruiker die {appName} uitvoert de eigenaar is van het bestand. Het is beter om ervoor te zorgen dat de downloadclient de juiste rechten instelt.",
"ChownGroupHelpText": "Groepsnaam of gid. Gebruik gid voor externe bestandssystemen.",
"ChownGroupHelpTextWarning": "Dit werkt alleen als de gebruiker die {appName} uitvoert de eigenaar is van het bestand. Het is beter om ervoor te zorgen dat de downloadclient dezelfde groep gebruikt als {appName}.",
"Clear": "Wis",
"ClickToChangeQuality": "Klik om kwaliteit te wijzigen",
"ClientPriority": "Client Prioriteit",
"CloneIndexer": "Dupliceer Indexeerder",
"CloneProfile": "Dupliceer Profiel",
"Close": "Sluit",
"ClientPriority": "Prioriteit Client",
"CloneIndexer": "Indexeerder Dupliceren",
"CloneProfile": "Profiel Dupliceren",
"Close": "Sluiten",
"Columns": "Kolommen",
"CompletedDownloadHandling": "Voltooide Download Afhandeling",
"ConnectSettings": "Connecties Instellingen",
"Connections": "Connecties",
"CopyUsingHardlinksHelpText": "Gebruik hardlinks bij het kopiëren van torrent bestanden die nog actief zijn",
"CopyUsingHardlinksHelpTextWarning": "Sporadisch kan bestandsvergrendeling het hernoemen van in gebruik zijnde bestanden blokkeren. Als noodoplossing kunt u de hernoem functie van {appName} gebruiken na het opheffen van de bestandsvergrendeling.",
"CreateEmptyAuthorFoldersHelpText": "Film mappen aanmaken voor ontbrekende films tijdens schijfscan",
"CompletedDownloadHandling": "Afhandeling Voltooide Downloads",
"ConnectSettings": "Instellingen Verbindingen",
"Connections": "Verbindingen",
"CopyUsingHardlinksHelpText": "Hardlinks staan {appName} toe om torrents te importeren die nog actief zijn zonder extra opslag in beslag te nemen of het volledige bestand te kopiëren. Hardlinks werken alleen maar als de bron en de bestemming zich op hetzelfde opslagvolume bevinden",
"CopyUsingHardlinksHelpTextWarning": "Sporadisch kan bestandsvergrendeling het hernoemen van bestanden blokkeren die geseed worden. Als tijdelijke oplossing kan u seeden uitzetten en de hernoemfunctie van {appName} gebruiken.",
"CreateEmptyAuthorFoldersHelpText": "Ontbrekende auteursmappen aanmaken tijdens schijfscan",
"CreateGroup": "Groep aanmaken",
"CutoffHelpText": "Wanneer deze kwaliteit is behaald, zal {appName} niet langer films downloaden",
"CutoffUnmet": "Onbereikte Drempel",
@@ -104,7 +104,7 @@
"DetailedProgressBarHelpText": "Toon tekst op voortgangsbalk",
"DiskSpace": "Schijfruimte",
"Docker": "Docker",
"DownloadClient": "Downloader",
"DownloadClient": "Downloadclient",
"DownloadClientSettings": "Downloader Instellingen",
"DownloadClients": "Downloaders",
"DownloadFailedCheckDownloadClientForMoreDetails": "Download mislukt: controleer de downloader voor meer details",
@@ -435,7 +435,7 @@
"ProxyPasswordHelpText": "Je moet alleen een gebruikersnaam en wachtwoord ingeven als dit vereist is, laat ze anders leeg.",
"SslCertPathHelpTextWarning": "Herstarten vereist om in werking te treden",
"UnableToLoadMetadataProfiles": "Vertragingsprofielen kunnen niet worden geladen",
"DownloadClientCheckDownloadingToRoot": "Downloadclient {0} plaatst downloads in de hoofdmap {1}. U mag niet naar een hoofdmap downloaden.",
"DownloadClientCheckDownloadingToRoot": "Downloadclient {0} plaatst downloads in de hoofdmap {1}. Het wordt afgeraden om naar een hoofdmap downloaden.",
"MaintenanceRelease": "Onderhoudsuitgave",
"Actions": "Acties",
"Tomorrow": "Morgen",
@@ -493,7 +493,7 @@
"MissingFromDisk": "{appName} kon het bestand niet vinden op de schijf dus werd het verwijderd",
"Disabled": "Uitgeschakeld",
"IndexerPriorityHelpText": "Indexeerder Prioriteit van 1 (Hoogste) tot 50 (Laagste). Standaard: 25.",
"ConnectSettingsSummary": "Notificaties, connecties met media servers/spelers en scripts",
"ConnectSettingsSummary": "Meldingen, verbindingen met mediaservers/spelers en scripts",
"DownloadClientCheckNoneAvailableMessage": "Er is geen downloader beschikbaar",
"DownloadClientCheckUnableToCommunicateMessage": "Niet in staat om te communiceren met {0}.",
"IndexersSettingsSummary": "Indexeerders en uitgave restricties",
@@ -559,7 +559,7 @@
"OnBookFileDeleteHelpText": "Op filmbestand verwijderen",
"UpdateCheckStartupTranslocationMessage": "Kan de update niet installeren omdat de map '{0}' zich in een 'App Translocation' map bevindt.",
"WriteTagsNo": "Nooit",
"Connect": "Connecties",
"Connect": "Verbindingen",
"General": "Algemeen",
"Lists": "Lijsten",
"ProxyCheckBadRequestMessage": "Testen van proxy is mislukt. Statuscode: {0}",
@@ -568,7 +568,7 @@
"TimeLeft": "Resterende Tijd",
"UpdateCheckStartupNotWritableMessage": "Kan de update niet installeren omdat de map '{0}' niet schrijfbaar is voor de gebruiker '{1}'.",
"CouldntFindAnyResultsForTerm": "Kon geen resultaten vinden voor '{0}'",
"CreateEmptyAuthorFolders": "Lege film mappen aanmaken",
"CreateEmptyAuthorFolders": "Lege auteursmappen aanmaken",
"FileWasDeletedByViaUI": "File werd verwijderd via de UI",
"RestartRequiredHelpTextWarning": "Herstarten vereist om in werking te treden",
"AddList": "Lijst Toevoegen",
@@ -588,16 +588,16 @@
"ApplicationUrlHelpText": "De externe URL van deze applicatie inclusief http(s)://,Port en URL base",
"ImportListExclusions": "Verwijder van Uitzonderingenlijst",
"MetadataProfile": "Metadata profiel toevoegen",
"ChooseImportMethod": "Kies Importmodus",
"ClickToChangeReleaseGroup": "Klik om de releasegroep te wijzigen",
"ChooseImportMethod": "Kies Importmethode",
"ClickToChangeReleaseGroup": "Klik om de uitgavegroep te wijzigen",
"HardlinkCopyFiles": "Hardlink/Kopieer Bestanden",
"OnApplicationUpdate": "Bij applicatie update",
"MoveFiles": "Verplaats Bestanden",
"OnApplicationUpdateHelpText": "Bij applicatie update",
"BypassIfHighestQuality": "Omzeilen indien de hoogste kwaliteit",
"CustomFormatScore": "Eigen Formaat Score",
"CustomFormatScore": "Aangepaste Formaatscore",
"MinimumCustomFormatScore": "Minimum Eigen Formaat Score",
"Conditions": "Condities",
"Conditions": "Voorwaarden",
"DeleteCustomFormat": "Verwijder Eigen Formaat",
"DeleteCustomFormatMessageText": "Bent u zeker dat u de indexeerder '{0}' wilt verwijderen?",
"DeleteFormatMessageText": "Weet je zeker dat je formaat tag {0} wilt verwijderen?",
@@ -607,12 +607,12 @@
"NegateHelpText": "Indien aangevinkt, zal het eigen formaat niet worden toegepast indien deze {0} conditie overeenstemt.",
"UnableToLoadCustomFormats": "Eigen formaten kunnen niet worden geladen",
"UpgradesAllowed": "Upgrades toegestaan",
"CloneCustomFormat": "Dupliceer Eigen Formaat",
"CopyToClipboard": "Kopieer naar Klembord",
"CustomFormat": "Eigen Formaat",
"CustomFormatSettings": "Eigen Formaten Instellingen",
"CustomFormats": "Eigen Formaten",
"CutoffFormatScoreHelpText": "Wanneer deze eigen formaat score is behaald, zal {appName} niet langer films downloaden",
"CloneCustomFormat": "Aangepast Formaat Dupliceren",
"CopyToClipboard": "Kopiëren naar klembord",
"CustomFormat": "Aangepast Formaat",
"CustomFormatSettings": "Aangepaste Formaatinstellingen",
"CustomFormats": "Aangepaste Formaten",
"CutoffFormatScoreHelpText": "Wanneer deze aangepaste formaatscore is behaald, zal {appName} niet langer boeken downloaden",
"IncludeCustomFormatWhenRenamingHelpText": "Voeg toe aan het {Eigen Formaten} hernoemingsformaat",
"ImportListMissingRoot": "Ontbrekende hoofdmap voor importlijst(en): {0}",
"ImportListMultipleMissingRoots": "Er ontbreken meerdere hoofdmappen voor importlijsten: {0}",
@@ -635,7 +635,7 @@
"ResetTitles": "Reset titels",
"ApplyTagsHelpTextHowToApplyIndexers": "Hoe tags toepassen op de geselecteerde indexeerders",
"AutomaticAdd": "Automatisch Toevoegen",
"CloneCondition": "Kloon Conditie",
"CloneCondition": "Voorwaarde dupliceren",
"ApplyTagsHelpTextHowToApplyImportLists": "Hoe tags toepassen op de geselecteerde import lijsten",
"ApplyChanges": "Pas Wijzigingen Toe",
"ApplyTagsHelpTextAdd": "Toevoegen: Voeg de tags toe aan de bestaande tag lijst",
@@ -662,7 +662,7 @@
"No": "Nee",
"NoChange": "Geen Wijziging",
"ApplyTagsHelpTextHowToApplyAuthors": "Hoe tags toe te passen op de geselecteerd films",
"CountDownloadClientsSelected": "{count} download client(s) geselecteerd",
"CountDownloadClientsSelected": "{selectedCount} downloadclient(s) geselecteerd",
"DeleteConditionMessageText": "Bent u zeker dat u de lijst '{name}' wilt verwijderen?",
"FreeSpace": "Vrije Ruimte",
"ImportLists": "importlijst",
@@ -685,7 +685,7 @@
"RemoveSelectedItem": "Verwijder geselecteerde item",
"DeleteSelectedIndexers": "Verwijder Indexeerder",
"ExistingTag": "Bestaande tag",
"ConnectionLost": "Verbinding Onderbroken",
"ConnectionLost": "Verbinding Verbroken",
"LastDuration": "Laatste Looptijd",
"Medium": "Gemiddeld",
"NoEventsFound": "Geen gebeurtenissen gevonden",
@@ -694,7 +694,7 @@
"EnableRssHelpText": "Wordt gebruikt wanneer {appName} periodiek zoekt naar uitgaven via RSS synchronisatie",
"Location": "Locatie",
"RecentChanges": "Recente wijzigingen",
"CustomFilter": "Aangepaste Filters",
"CustomFilter": "Aangepaste Filter",
"ErrorLoadingContent": "Er ging iets fout bij het laden van dit item",
"SourceTitle": "Bron Titel",
"FailedLoadingSearchResults": "Fout bij laden van zoek resultaten, probeer het opnieuw.",
@@ -740,20 +740,20 @@
"DisabledForLocalAddresses": "Uitgeschakeld voor lokale adressen",
"Enabled": "Ingeschakeld",
"ApiKey": "API-sleutel",
"ClickToChangeIndexerFlags": "Klik om indexeringsvlaggen te wijzigen",
"ClickToChangeIndexerFlags": "Klik om indexeerdersvlaggen te wijzigen",
"CustomFormatsSpecificationFlag": "Vlag",
"CustomFormatsSpecificationRegularExpression": "Reguliere expressie",
"BlocklistOnlyHint": "Blokkeer lijst zonder te zoeken naar een vervanger",
"BlocklistAndSearch": "Blokkeerlijst en zoeken",
"BlocklistAndSearchHint": "Een vervanger zoeken na het blokkeren",
"BlocklistAndSearchMultipleHint": "Zoekopdrachten voor vervangers starten na het blokkeren van de lijst",
"CustomFormatsSettingsTriggerInfo": "Een Aangepast Formaat wordt toegepast op een uitgave of bestand als het overeenkomt met ten minste één van de verschillende condities die zijn gekozen.",
"ConnectionSettingsUrlBaseHelpText": "Voegt een voorvoegsel toe aan de {connectionName} url, zoals {url}",
"CustomFormatsSettingsTriggerInfo": "Een aangepast formaat wordt toegepast op een uitgave of bestand wanneer het overeenkomt met ten minste één van elk van de verschillende voorwaarden die zijn gekozen.",
"ConnectionSettingsUrlBaseHelpText": "Voegt een voorvoegsel toe aan de {connectionName} URL, zoals {url}",
"BlocklistMultipleOnlyHint": "Blocklist zonder te zoeken naar vervangers",
"BlocklistOnly": "Alleen bloklijst",
"ChangeCategoryHint": "Verandert download naar de 'Post-Import Categorie' van Downloadclient",
"Clone": "Kloon",
"CustomFormatsSpecificationRegularExpressionHelpText": "Aangepaste opmaak RegEx is hoofdletterongevoelig",
"Clone": "Dupliceren",
"CustomFormatsSpecificationRegularExpressionHelpText": "RegEx van aangepaste formaten is hoofdletterongevoelig",
"ChangeCategoryMultipleHint": "Wijzigt downloads naar de 'Post-Import Categorie' van Downloadclient",
"BypassIfAboveCustomFormatScore": "Omzeilen indien boven aangepaste opmaak score",
"BypassIfAboveCustomFormatScoreHelpText": "Schakel omleiding in als de release een score heeft die hoger is dan de geconfigureerde minimale aangepaste formaatscore",
@@ -767,5 +767,19 @@
"Script": "Script",
"UpdateAppDirectlyLoadError": "Kan {appName} niet rechtstreeks updaten,",
"CurrentlyInstalled": "Momenteel Geïnstalleerd",
"ExternalUpdater": "{appName} is geconfigureerd om een extern update mechanisme te gebruiken"
"ExternalUpdater": "{appName} is geconfigureerd om een extern update mechanisme te gebruiken",
"AddNewAuthorRootFolderHelpText": "'{0}' submap zal automatisch worden aangemaakt",
"AddRootFolder": "Voeg hoofdmap toe",
"CollapseMultipleBooks": "Meerdere Boeken Samenvouwen",
"Country": "Land",
"CollapseMultipleBooksHelpText": "Meerdere boeken die op dezelfde dag worden uitgegeven samenvouwen",
"ConvertToFormat": "Converteren Naar Formaat",
"ContinuingMoreBooksAreExpected": "Er worden meer boeken verwacht",
"CountAuthorsSelected": "{selectedCount} auteur(s) geselecteerd",
"ContinuingNoAdditionalBooksAreExpected": "Er worden geen nieuwe boeken verwacht",
"CountImportListsSelected": "{selectedCount} importeerlijst(en) geselecteerd",
"CountIndexersSelected": "{selectedCount} indexeerder(s) geselecteerd",
"Continuing": "Doorgaan",
"ContinuingAllBooksDownloaded": "Doorgaan (Alle boeken gedownload)",
"StatusEndedContinuing": "Doorgaan"
}

View File

@@ -718,5 +718,7 @@
"Script": "Scenariusz",
"UnmappedFiles": "Niezmapowane foldery",
"UpdateAppDirectlyLoadError": "Nie można bezpośrednio zaktualizować {appName},",
"DockerUpdater": "zaktualizuj kontener Dockera, aby otrzymać aktualizację"
"DockerUpdater": "zaktualizuj kontener Dockera, aby otrzymać aktualizację",
"AddNewAuthorRootFolderHelpText": "Podfolder „{0}” zostanie utworzony automatycznie",
"AddRootFolder": "Dodaj folder główny"
}

View File

@@ -963,5 +963,9 @@
"DockerUpdater": "atualize o contentor do Docker para receber a atualização",
"ExternalUpdater": "O {appName} está definido para usar um mecanismo de atualização externo",
"Script": "Script",
"UpdateAppDirectlyLoadError": "Não foi possível atualizar o {appName} diretamente,"
"UpdateAppDirectlyLoadError": "Não foi possível atualizar o {appName} diretamente,",
"AddNewAuthorRootFolderHelpText": "A subpasta \"{0}\" será criada automaticamente",
"AddRootFolder": "Adicionar pasta raiz",
"ThereWasAnErrorLoadingThisItem": "Houve um erro ao carregar este item",
"ThereWasAnErrorLoadingThisPage": "Houve um erro ao carregar esta página"
}

View File

@@ -1118,5 +1118,9 @@
"InstallMajorVersionUpdateMessage": "Esta atualização instalará uma nova versão principal e pode não ser compatível com o seu sistema. Tem certeza de que deseja instalar esta atualização?",
"InstallMajorVersionUpdateMessageLink": "Verifique [{domain}]({url}) para obter mais informações.",
"LastSearched": "Última Pesquisa",
"Unknown": "Desconhecido"
"Unknown": "Desconhecido",
"AddRootFolder": "Adicionar pasta raiz",
"AddNewAuthorRootFolderHelpText": "A subpasta \"{folder}\" será criada automaticamente",
"AddNewBook": "Adicionar Novo Livro",
"AddNewAuthor": "Adicionar Novo Autor"
}

View File

@@ -672,5 +672,7 @@
"OnLatestVersion": "Cea mai recentă versiune a {appName} este deja instalată",
"Script": "Script",
"UpdateAppDirectlyLoadError": "Imposibil de actualizat direct {appName},",
"Unknown": "Necunoscut"
"Unknown": "Necunoscut",
"AddRootFolder": "Adăugați folderul rădăcină",
"AddNewAuthorRootFolderHelpText": "Subfolderul „{0}” va fi creat automat"
}

View File

@@ -882,5 +882,7 @@
"DeleteSelected": "Удалить выбранные",
"Unknown": "Неизвестный",
"WhySearchesCouldBeFailing": "Нажмите здесь, чтобы найти причину ошибок писка",
"LastSearched": "Искали недавно"
"LastSearched": "Искали недавно",
"AddRootFolder": "Добавить корневой каталог",
"AddNewAuthorRootFolderHelpText": "Подпапка \"{folder}\" будет создана автоматически"
}

View File

@@ -200,5 +200,9 @@
"Reason": "Séria",
"Clone": "Zatvoriť",
"AptUpdater": "Použiť apt pre inštaláciu aktualizácie",
"BuiltIn": "Vstavaný"
"BuiltIn": "Vstavaný",
"AddRootFolder": "Pridať koreňový priečinok",
"AddNewAuthorRootFolderHelpText": "'{folder}' podpriečinok sa vytvorí automaticky",
"IgnoredPlaceHolder": "Pridať nové obmedzenie",
"RequiredPlaceHolder": "Pridať nové obmedzenie"
}

View File

@@ -882,5 +882,7 @@
"InstallLatest": "Installera senaste",
"OnLatestVersion": "Den senaste versionen av {appName} är redan installerad",
"Script": "Skript",
"UpdateAppDirectlyLoadError": "Det går inte att uppdatera {appName} direkt,"
"UpdateAppDirectlyLoadError": "Det går inte att uppdatera {appName} direkt,",
"AddNewAuthorRootFolderHelpText": "Mappen \"{folder}\" kommer skapas automatiskt",
"AddRootFolder": "Lägg till rotmapp"
}

View File

@@ -653,5 +653,7 @@
"UpdateAppDirectlyLoadError": "ไม่สามารถอัปเดต {appName} ได้โดยตรง",
"UnmappedFiles": "โฟลเดอร์ที่ไม่ได้แมป",
"AptUpdater": "ใช้ apt เพื่อติดตั้งการอัปเดต",
"OnLatestVersion": "มีการติดตั้ง {appName} เวอร์ชันล่าสุดแล้ว"
"OnLatestVersion": "มีการติดตั้ง {appName} เวอร์ชันล่าสุดแล้ว",
"AddRootFolder": "เพิ่มโฟลเดอร์รูท",
"AddNewAuthorRootFolderHelpText": "โฟลเดอร์ย่อย \"{0}\" จะถูกสร้างขึ้นโดยอัตโนมัติ"
}

View File

@@ -865,5 +865,7 @@
"FilterAnalyticsEvents": "Analitik Olayları Filtrele",
"ConsoleLogLevel": "Konsol Günlük Düzeyi",
"LastSearched": "Son Aranan",
"Unknown": "Bilinmeyen"
"Unknown": "Bilinmeyen",
"AddNewAuthorRootFolderHelpText": "'{0}' alt klasörü otomatik olarak oluşturulacak",
"AddRootFolder": "Kök Klasör Ekle"
}

View File

@@ -851,5 +851,14 @@
"NotificationsSettingsUpdateMapPathsTo": "Карта шляхів до",
"PasswordConfirmation": "Підтвердження пароля",
"PreviouslyInstalled": "Раніше встановлений",
"SetIndexerFlags": "Встановити прапорці індексатора"
"SetIndexerFlags": "Встановити прапорці індексатора",
"AddRootFolder": "Додати корневий каталог",
"AddNewAuthorRootFolderHelpText": "Вкладена папка \"{0}\" буде створена автоматично",
"WriteBookTagsHelpTextWarning": "Вибір \"Усі файли\" змінить існуючі файли під час їх імпорту.",
"ForeignId": "Зовнішній ідентифікатор",
"AudioFileMetadata": "Записувати метадані до аудіофайлів",
"OnDownloadFailureHelpText": "При помилці завантаження",
"WriteAudioTags": "Тегувати аудіофайли метаданими",
"SearchForNewItems": "Пошук нових елементів",
"WriteAudioTagsScrub": "Очистити існуючі теги"
}

View File

@@ -658,5 +658,7 @@
"AptUpdater": "Sử dụng apt để cài đặt bản cập nhật",
"ExternalUpdater": "{appName} được định cấu hình để sử dụng cơ chế cập nhật bên ngoài",
"WhySearchesCouldBeFailing": "Nhấp vào đây để tìm hiểu lý do tại sao tìm kiếm thất bại",
"Unknown": "Không rõ"
"Unknown": "Không rõ",
"AddNewAuthorRootFolderHelpText": "Thư mục con '{0}' sẽ được tạo tự động",
"AddRootFolder": "Thêm thư mục gốc"
}

View File

@@ -1118,5 +1118,7 @@
"UpdateAppDirectlyLoadError": "无法直接更新{appName}",
"FailedToFetchSettings": "设置同步失败",
"LastSearched": "最近搜索",
"Unknown": "未知"
"Unknown": "未知",
"AddNewAuthorRootFolderHelpText": "将自动创建 '{folder}' 子文件夹",
"AddRootFolder": "添加根目录"
}

View File

@@ -2,5 +2,6 @@
"Analytics": "分析",
"About": "关于",
"Username": "用户名",
"Activity": "111"
"Activity": "111",
"Actions": "Actions"
}

View File

@@ -171,5 +171,6 @@
"IgnoredPlaceHolder": "加入新的限制",
"RequiredPlaceHolder": "加入新的限制",
"RedownloadFailed": "失敗時重新下載",
"UnableToAddANewRemotePathMappingPleaseTryAgain": "無法加入新的遠程路徑對應,請重試。"
"UnableToAddANewRemotePathMappingPleaseTryAgain": "無法加入新的遠程路徑對應,請重試。",
"AddRootFolder": "加入根目錄資料夾"
}

View File

@@ -30,13 +30,11 @@ namespace NzbDrone.Core.MediaCover
private string _url;
public string Url
{
get
{
return _url;
}
get => _url;
set
{
_url = value;
if (Extension.IsNullOrWhiteSpace())
{
Extension = Path.GetExtension(value);
@@ -46,6 +44,7 @@ namespace NzbDrone.Core.MediaCover
public MediaCoverTypes CoverType { get; set; }
public string Extension { get; private set; }
public string RemoteUrl { get; set; }
public MediaCover()
{

View File

@@ -0,0 +1,70 @@
using System;
using System.Collections.Generic;
using System.IO;
using NzbDrone.Common.Cache;
using NzbDrone.Common.Extensions;
using NzbDrone.Common.Http;
using NzbDrone.Core.Configuration;
namespace NzbDrone.Core.MediaCover
{
public interface IMediaCoverProxy
{
string RegisterUrl(string url);
string GetUrl(string hash);
byte[] GetImage(string hash);
}
public class MediaCoverProxy : IMediaCoverProxy
{
private readonly IHttpClient _httpClient;
private readonly IConfigFileProvider _configFileProvider;
private readonly ICached<string> _cache;
public MediaCoverProxy(IHttpClient httpClient, IConfigFileProvider configFileProvider, ICacheManager cacheManager)
{
_httpClient = httpClient;
_configFileProvider = configFileProvider;
_cache = cacheManager.GetCache<string>(GetType());
}
public string RegisterUrl(string url)
{
if (url.IsNullOrWhiteSpace())
{
return null;
}
var hash = url.SHA256Hash();
_cache.Set(hash, url, TimeSpan.FromHours(24));
_cache.ClearExpired();
var fileName = Path.GetFileName(url);
return _configFileProvider.UrlBase + @"/MediaCoverProxy/" + hash + "/" + fileName;
}
public string GetUrl(string hash)
{
var result = _cache.Find(hash);
if (result == null)
{
throw new KeyNotFoundException("Url no longer in cache");
}
return result;
}
public byte[] GetImage(string hash)
{
var url = GetUrl(hash);
var request = new HttpRequest(url);
return _httpClient.Get(request).ResponseData;
}
}
}

View File

@@ -31,6 +31,7 @@ namespace NzbDrone.Core.MediaCover
{
private const string USER_AGENT = "Dalvik/2.1.0 (Linux; U; Android 10; SM-G975U Build/QP1A.190711.020)";
private readonly IMediaCoverProxy _mediaCoverProxy;
private readonly IImageResizer _resizer;
private readonly IBookService _bookService;
private readonly IHttpClient _httpClient;
@@ -46,7 +47,8 @@ namespace NzbDrone.Core.MediaCover
// So limit the number of concurrent resizing tasks
private static SemaphoreSlim _semaphore = new SemaphoreSlim((int)Math.Ceiling(Environment.ProcessorCount / 2.0));
public MediaCoverService(IImageResizer resizer,
public MediaCoverService(IMediaCoverProxy mediaCoverProxy,
IImageResizer resizer,
IBookService bookService,
IHttpClient httpClient,
IDiskProvider diskProvider,
@@ -56,6 +58,7 @@ namespace NzbDrone.Core.MediaCover
IEventAggregator eventAggregator,
Logger logger)
{
_mediaCoverProxy = mediaCoverProxy;
_resizer = resizer;
_bookService = bookService;
_httpClient = httpClient;
@@ -82,28 +85,42 @@ namespace NzbDrone.Core.MediaCover
public void ConvertToLocalUrls(int entityId, MediaCoverEntity coverEntity, IEnumerable<MediaCover> covers)
{
foreach (var mediaCover in covers)
if (entityId == 0)
{
if (mediaCover.CoverType == MediaCoverTypes.Unknown)
// Author isn't in Readarr yet, map via a proxy to circument referrer issues
foreach (var mediaCover in covers)
{
continue;
mediaCover.RemoteUrl = mediaCover.Url;
mediaCover.Url = _mediaCoverProxy.RegisterUrl(mediaCover.RemoteUrl);
}
var filePath = GetCoverPath(entityId, coverEntity, mediaCover.CoverType, mediaCover.Extension, null);
if (coverEntity == MediaCoverEntity.Book)
}
else
{
foreach (var mediaCover in covers)
{
mediaCover.Url = _configFileProvider.UrlBase + @"/MediaCover/Books/" + entityId + "/" + mediaCover.CoverType.ToString().ToLower() + GetExtension(mediaCover.CoverType, mediaCover.Extension);
}
else
{
mediaCover.Url = _configFileProvider.UrlBase + @"/MediaCover/" + entityId + "/" + mediaCover.CoverType.ToString().ToLower() + GetExtension(mediaCover.CoverType, mediaCover.Extension);
}
if (mediaCover.CoverType == MediaCoverTypes.Unknown)
{
continue;
}
if (_diskProvider.FileExists(filePath))
{
var lastWrite = _diskProvider.FileGetLastWrite(filePath);
mediaCover.Url += "?lastWrite=" + lastWrite.Ticks;
var filePath = GetCoverPath(entityId, coverEntity, mediaCover.CoverType, mediaCover.Extension, null);
mediaCover.RemoteUrl = mediaCover.Url;
if (coverEntity == MediaCoverEntity.Book)
{
mediaCover.Url = _configFileProvider.UrlBase + @"/MediaCover/Books/" + entityId + "/" + mediaCover.CoverType.ToString().ToLower() + GetExtension(mediaCover.CoverType, mediaCover.Extension);
}
else
{
mediaCover.Url = _configFileProvider.UrlBase + @"/MediaCover/" + entityId + "/" + mediaCover.CoverType.ToString().ToLower() + GetExtension(mediaCover.CoverType, mediaCover.Extension);
}
if (_diskProvider.FileExists(filePath))
{
var lastWrite = _diskProvider.FileGetLastWrite(filePath);
mediaCover.Url += "?lastWrite=" + lastWrite.Ticks;
}
}
}
}

View File

@@ -97,7 +97,7 @@ namespace NzbDrone.Core.MediaFiles.BookImport
var iDecision = 1;
foreach (var bookDecision in bookDecisions)
{
_logger.ProgressInfo($"Importing book {iDecision++}/{bookDecisions.Count} {bookDecision.First().Item.Book}");
_logger.ProgressInfo("Importing book {0}/{1} {2}", iDecision++, bookDecisions.Count, bookDecision.First().Item.Book);
var decisionList = bookDecision.ToList();
@@ -130,7 +130,7 @@ namespace NzbDrone.Core.MediaFiles.BookImport
// RemoveExistingTrackFiles(author, book);
// }
// make sure part numbers are populated for audio books
// Make sure part numbers are populated for audiobooks
// If all audio files and all part numbers are zero, set them by filename order
if (decisionList.All(b => MediaFileExtensions.AudioExtensions.Contains(Path.GetExtension(b.Item.Path)) && b.Item.Part == 0))
{
@@ -147,7 +147,7 @@ namespace NzbDrone.Core.MediaFiles.BookImport
book.Editions = _editionService.SetMonitored(newRelease);
// Publish book edited event.
// Deliberatly don't put in the old book since we don't want to trigger an AuthorScan.
// Deliberately don't put in the old book since we don't want to trigger an AuthorScan.
_eventAggregator.PublishEvent(new BookEditedEvent(book, book));
}
@@ -158,8 +158,8 @@ namespace NzbDrone.Core.MediaFiles.BookImport
.SelectMany(c => c)
.ToList();
_logger.ProgressInfo($"Importing {qualifiedImports.Count} files");
_logger.Debug($"Importing {qualifiedImports.Count} files. replaceExisting: {replaceExisting}");
_logger.ProgressInfo("Importing {0} files", qualifiedImports.Count);
_logger.Debug("Importing {0} files. Replace existing: {1}", qualifiedImports.Count, replaceExisting);
var filesToAdd = new List<BookFile>(qualifiedImports.Count);
var trackImportedEvents = new List<TrackImportedEvent>(qualifiedImports.Count);
@@ -310,7 +310,7 @@ namespace NzbDrone.Core.MediaFiles.BookImport
var watch = new System.Diagnostics.Stopwatch();
watch.Start();
_mediaFileService.AddMany(filesToAdd);
_logger.Debug($"Inserted new trackfiles in {watch.ElapsedMilliseconds}ms");
_logger.Debug("Inserted new trackfiles in {0}ms", watch.ElapsedMilliseconds);
// now that trackfiles have been inserted and ids generated, publish the import events
foreach (var trackImportedEvent in trackImportedEvents)
@@ -354,7 +354,7 @@ namespace NzbDrone.Core.MediaFiles.BookImport
if (booksToRefresh.Any())
{
_logger.Debug($"Refreshing info for {booksToRefresh.Count} new books");
_logger.Debug("Refreshing info for {0} new books", booksToRefresh.Count);
_commandQueueManager.Push(new BulkRefreshBookCommand(booksToRefresh.Select(x => x.Id).ToList()));
}
@@ -371,7 +371,8 @@ namespace NzbDrone.Core.MediaFiles.BookImport
if (dbAuthor == null)
{
_logger.Debug($"Adding remote author {author}");
_logger.Debug("Adding remote author {0}", author);
var path = decisions.First().Item.Path;
var rootFolder = _rootFolderService.GetBestRootFolder(path);
@@ -438,7 +439,7 @@ namespace NzbDrone.Core.MediaFiles.BookImport
if (dbBook == null)
{
_logger.Debug($"Adding remote book {book}");
_logger.Debug("Adding remote book {0}", book);
if (book.AuthorMetadataId == 0)
{
@@ -497,7 +498,8 @@ namespace NzbDrone.Core.MediaFiles.BookImport
if (dbEdition == null)
{
_logger.Debug($"Adding remote edition {edition}");
_logger.Debug("Adding remote edition {0}", edition);
try
{
edition.BookId = book.Id;
@@ -540,7 +542,7 @@ namespace NzbDrone.Core.MediaFiles.BookImport
var rootFolder = _diskProvider.GetParentFolder(author.Path);
var previousFiles = _mediaFileService.GetFilesByBook(book.Id);
_logger.Debug($"Deleting {previousFiles.Count} existing files for {book}");
_logger.Debug("Deleting {0} existing files for {1}", previousFiles.Count, book);
foreach (var previousFile in previousFiles)
{

View File

@@ -7,7 +7,7 @@ using Readarr.Api.V1.Author;
namespace NzbDrone.Integration.Test.ApiTests
{
[TestFixture]
[Ignore("Waiting for metadata to be back again", Until = "2025-05-15 00:00:00Z")]
[Ignore("Waiting for metadata to be back again", Until = "2026-01-15 00:00:00Z")]
public class AuthorEditorFixture : IntegrationTest
{
private void GivenExistingAuthor()

View File

@@ -7,7 +7,7 @@ using NUnit.Framework;
namespace NzbDrone.Integration.Test.ApiTests
{
[TestFixture]
[Ignore("Waiting for metadata to be back again", Until = "2025-05-15 00:00:00Z")]
[Ignore("Waiting for metadata to be back again", Until = "2026-01-15 00:00:00Z")]
public class AuthorFixture : IntegrationTest
{
[Test]

View File

@@ -4,7 +4,7 @@ using NUnit.Framework;
namespace NzbDrone.Integration.Test.ApiTests
{
[TestFixture]
[Ignore("Waiting for metadata to be back again", Until = "2025-05-15 00:00:00Z")]
[Ignore("Waiting for metadata to be back again", Until = "2026-01-15 00:00:00Z")]
public class AuthorLookupFixture : IntegrationTest
{
[TestCase("Robert Harris", "Robert Harris")]

View File

@@ -6,7 +6,7 @@ using Readarr.Api.V1.Blocklist;
namespace NzbDrone.Integration.Test.ApiTests
{
[TestFixture]
[Ignore("Waiting for metadata to be back again", Until = "2025-05-15 00:00:00Z")]
[Ignore("Waiting for metadata to be back again", Until = "2026-01-15 00:00:00Z")]
public class BlocklistFixture : IntegrationTest
{
private AuthorResource _author;

View File

@@ -9,7 +9,7 @@ using Readarr.Api.V1.Books;
namespace NzbDrone.Integration.Test.ApiTests
{
[TestFixture]
[Ignore("Waiting for metadata to be back again", Until = "2025-05-15 00:00:00Z")]
[Ignore("Waiting for metadata to be back again", Until = "2026-01-15 00:00:00Z")]
public class CalendarFixture : IntegrationTest
{
public ClientBase<BookResource> Calendar;

View File

@@ -8,7 +8,7 @@ using Readarr.Api.V1.RootFolders;
namespace NzbDrone.Integration.Test.ApiTests.WantedTests
{
[TestFixture]
[Ignore("Waiting for metadata to be back again", Until = "2025-05-15 00:00:00Z")]
[Ignore("Waiting for metadata to be back again", Until = "2026-01-15 00:00:00Z")]
public class CutoffUnmetFixture : IntegrationTest
{
[SetUp]

View File

@@ -7,7 +7,7 @@ using Readarr.Api.V1.RootFolders;
namespace NzbDrone.Integration.Test.ApiTests.WantedTests
{
[TestFixture]
[Ignore("Waiting for metadata to be back again", Until = "2025-05-15 00:00:00Z")]
[Ignore("Waiting for metadata to be back again", Until = "2026-01-15 00:00:00Z")]
public class MissingFixture : IntegrationTest
{
[SetUp]

View File

@@ -59,7 +59,8 @@ namespace Readarr.Api.V1.Author
AuthorAncestorValidator authorAncestorValidator,
SystemFolderValidator systemFolderValidator,
QualityProfileExistsValidator qualityProfileExistsValidator,
MetadataProfileExistsValidator metadataProfileExistsValidator)
MetadataProfileExistsValidator metadataProfileExistsValidator,
AuthorFolderAsRootFolderValidator authorFolderAsRootFolderValidator)
: base(signalRBroadcaster)
{
_authorService = authorService;
@@ -89,7 +90,10 @@ namespace Readarr.Api.V1.Author
SharedValidator.RuleFor(s => s.MetadataProfileId).SetValidator(metadataProfileExistsValidator);
PostValidator.RuleFor(s => s.Path).IsValidPath().When(s => s.RootFolderPath.IsNullOrWhiteSpace());
PostValidator.RuleFor(s => s.RootFolderPath).IsValidPath().When(s => s.Path.IsNullOrWhiteSpace());
PostValidator.RuleFor(s => s.RootFolderPath)
.IsValidPath()
.SetValidator(authorFolderAsRootFolderValidator)
.When(s => s.Path.IsNullOrWhiteSpace());
PostValidator.RuleFor(s => s.AuthorName).NotEmpty();
PostValidator.RuleFor(s => s.ForeignAuthorId).NotEmpty().SetValidator(authorExistsValidator);

View File

@@ -0,0 +1,56 @@
using System;
using System.IO;
using FluentValidation.Validators;
using NzbDrone.Common.Extensions;
using NzbDrone.Core.Organizer;
namespace Readarr.Api.V1.Author
{
public class AuthorFolderAsRootFolderValidator : PropertyValidator
{
private readonly IBuildFileNames _fileNameBuilder;
public AuthorFolderAsRootFolderValidator(IBuildFileNames fileNameBuilder)
{
_fileNameBuilder = fileNameBuilder;
}
protected override string GetDefaultMessageTemplate() => "Root folder path '{rootFolderPath}' contains author folder '{authorFolder}'";
protected override bool IsValid(PropertyValidatorContext context)
{
if (context.PropertyValue == null)
{
return true;
}
if (context.ParentContext.InstanceToValidate is not AuthorResource authorResource)
{
return true;
}
var rootFolderPath = context.PropertyValue.ToString();
if (rootFolderPath.IsNullOrWhiteSpace())
{
return true;
}
var rootFolder = new DirectoryInfo(rootFolderPath!).Name;
var author = authorResource.ToModel();
var authorFolder = _fileNameBuilder.GetAuthorFolder(author);
context.MessageFormatter.AppendArgument("rootFolderPath", rootFolderPath);
context.MessageFormatter.AppendArgument("authorFolder", authorFolder);
if (authorFolder == rootFolder)
{
return false;
}
var distance = authorFolder.LevenshteinDistance(rootFolder);
return distance >= Math.Max(1, authorFolder.Length * 0.2);
}
}
}

View File

@@ -3,6 +3,7 @@ using System.Linq;
using Microsoft.AspNetCore.Mvc;
using NzbDrone.Core.MediaCover;
using NzbDrone.Core.MetadataSource;
using NzbDrone.Core.Organizer;
using Readarr.Http;
namespace Readarr.Api.V1.Author
@@ -11,10 +12,14 @@ namespace Readarr.Api.V1.Author
public class AuthorLookupController : Controller
{
private readonly ISearchForNewAuthor _searchProxy;
private readonly IBuildFileNames _fileNameBuilder;
private readonly IMapCoversToLocal _coverMapper;
public AuthorLookupController(ISearchForNewAuthor searchProxy)
public AuthorLookupController(ISearchForNewAuthor searchProxy, IBuildFileNames fileNameBuilder, IMapCoversToLocal coverMapper)
{
_searchProxy = searchProxy;
_fileNameBuilder = fileNameBuilder;
_coverMapper = coverMapper;
}
[HttpGet]
@@ -24,17 +29,23 @@ namespace Readarr.Api.V1.Author
return MapToResource(searchResults).ToList();
}
private static IEnumerable<AuthorResource> MapToResource(IEnumerable<NzbDrone.Core.Books.Author> author)
private IEnumerable<AuthorResource> MapToResource(IEnumerable<NzbDrone.Core.Books.Author> author)
{
foreach (var currentAuthor in author)
{
var resource = currentAuthor.ToResource();
var poster = currentAuthor.Metadata.Value.Images.FirstOrDefault(c => c.CoverType == MediaCoverTypes.Poster);
_coverMapper.ConvertToLocalUrls(resource.Id, MediaCoverEntity.Author, resource.Images);
var poster = resource.Images.FirstOrDefault(c => c.CoverType == MediaCoverTypes.Poster);
if (poster != null)
{
resource.RemotePoster = poster.Url;
resource.RemotePoster = poster.RemoteUrl;
}
resource.Folder = _fileNameBuilder.GetAuthorFolder(currentAuthor);
yield return resource;
}
}

View File

@@ -45,6 +45,7 @@ namespace Readarr.Api.V1.Author
public NewItemMonitorTypes MonitorNewItems { get; set; }
public string RootFolderPath { get; set; }
public string Folder { get; set; }
public List<string> Genres { get; set; }
public string CleanName { get; set; }
public string SortName { get; set; }

View File

@@ -11,10 +11,12 @@ namespace Readarr.Api.V1.Books
public class BookLookupController : Controller
{
private readonly ISearchForNewBook _searchProxy;
private readonly IMapCoversToLocal _coverMapper;
public BookLookupController(ISearchForNewBook searchProxy)
public BookLookupController(ISearchForNewBook searchProxy, IMapCoversToLocal coverMapper)
{
_searchProxy = searchProxy;
_coverMapper = coverMapper;
}
[HttpGet]
@@ -24,15 +26,19 @@ namespace Readarr.Api.V1.Books
return MapToResource(searchResults).ToList();
}
private static IEnumerable<BookResource> MapToResource(IEnumerable<NzbDrone.Core.Books.Book> books)
private IEnumerable<BookResource> MapToResource(IEnumerable<NzbDrone.Core.Books.Book> books)
{
foreach (var currentBook in books)
{
var resource = currentBook.ToResource();
var cover = currentBook.Editions.Value.Single(x => x.Monitored).Images.FirstOrDefault(c => c.CoverType == MediaCoverTypes.Cover);
_coverMapper.ConvertToLocalUrls(resource.Id, MediaCoverEntity.Book, resource.Images);
var cover = resource.Images.FirstOrDefault(c => c.CoverType == MediaCoverTypes.Cover);
if (cover != null)
{
resource.RemoteCover = cover.Url;
resource.RemoteCover = cover.RemoteUrl;
}
yield return resource;

View File

@@ -1,6 +1,5 @@
using System.Collections.Generic;
using System.Linq;
using NzbDrone.Common.Http;
using NzbDrone.Core.HealthCheck;
using Readarr.Http.REST;
@@ -11,7 +10,7 @@ namespace Readarr.Api.V1.Health
public string Source { get; set; }
public HealthCheckResult Type { get; set; }
public string Message { get; set; }
public HttpUri WikiUrl { get; set; }
public string WikiUrl { get; set; }
}
public static class HealthResourceMapper
@@ -29,7 +28,7 @@ namespace Readarr.Api.V1.Health
Source = model.Source.Name,
Type = model.Type,
Message = model.Message,
WikiUrl = model.WikiUrl
WikiUrl = model.WikiUrl.FullUri
};
}

View File

@@ -30,8 +30,11 @@ namespace Readarr.Api.V1.RemotePathMappings
SharedValidator.RuleFor(c => c.LocalPath)
.Cascade(CascadeMode.Stop)
.IsValidPath()
.SetValidator(mappedNetworkDriveValidator)
.SetValidator(pathExistsValidator);
.SetValidator(mappedNetworkDriveValidator)
.SetValidator(pathExistsValidator)
.SetValidator(new SystemFolderValidator())
.NotEqual("/")
.WithMessage("Cannot be set to '/'");
}
protected override RemotePathMappingResource GetResourceById(int id)
@@ -40,7 +43,8 @@ namespace Readarr.Api.V1.RemotePathMappings
}
[RestPostById]
public ActionResult<RemotePathMappingResource> CreateMapping(RemotePathMappingResource resource)
[Consumes("application/json")]
public ActionResult<RemotePathMappingResource> CreateMapping([FromBody] RemotePathMappingResource resource)
{
var model = resource.ToModel();
@@ -48,6 +52,7 @@ namespace Readarr.Api.V1.RemotePathMappings
}
[HttpGet]
[Produces("application/json")]
public List<RemotePathMappingResource> GetMappings()
{
return _remotePathMappingService.All().ToResource();
@@ -60,7 +65,7 @@ namespace Readarr.Api.V1.RemotePathMappings
}
[RestPutById]
public ActionResult<RemotePathMappingResource> UpdateMapping(RemotePathMappingResource resource)
public ActionResult<RemotePathMappingResource> UpdateMapping([FromBody] RemotePathMappingResource resource)
{
var mapping = resource.ToModel();

View File

@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
using NzbDrone.Common.Extensions;
using NzbDrone.Core.Books;
using NzbDrone.Core.Books.Calibre;
using NzbDrone.Core.RootFolders;
@@ -47,7 +48,7 @@ namespace Readarr.Api.V1.RootFolders
Id = model.Id,
Name = model.Name,
Path = model.Path,
Path = model.Path.GetCleanPath(),
DefaultMetadataProfileId = model.DefaultMetadataProfileId,
DefaultQualityProfileId = model.DefaultQualityProfileId,
DefaultMonitorOption = model.DefaultMonitorOption,

View File

@@ -4,6 +4,7 @@ using System.Linq;
using Microsoft.AspNetCore.Mvc;
using NzbDrone.Core.MediaCover;
using NzbDrone.Core.MetadataSource;
using NzbDrone.Core.Organizer;
using Readarr.Api.V1.Author;
using Readarr.Api.V1.Books;
using Readarr.Http;
@@ -14,10 +15,14 @@ namespace Readarr.Api.V1.Search
public class SearchController : Controller
{
private readonly ISearchForNewEntity _searchProxy;
private readonly IBuildFileNames _fileNameBuilder;
private readonly IMapCoversToLocal _coverMapper;
public SearchController(ISearchForNewEntity searchProxy)
public SearchController(ISearchForNewEntity searchProxy, IBuildFileNames fileNameBuilder, IMapCoversToLocal coverMapper)
{
_searchProxy = searchProxy;
_fileNameBuilder = fileNameBuilder;
_coverMapper = coverMapper;
}
[HttpGet]
@@ -27,7 +32,7 @@ namespace Readarr.Api.V1.Search
return MapToResource(searchResults).ToList();
}
private static IEnumerable<SearchResource> MapToResource(IEnumerable<object> results)
private IEnumerable<SearchResource> MapToResource(IEnumerable<object> results)
{
var id = 1;
foreach (var result in results)
@@ -35,32 +40,40 @@ namespace Readarr.Api.V1.Search
var resource = new SearchResource();
resource.Id = id++;
if (result is NzbDrone.Core.Books.Author)
if (result is NzbDrone.Core.Books.Author author)
{
var author = (NzbDrone.Core.Books.Author)result;
resource.Author = author.ToResource();
resource.ForeignId = author.ForeignAuthorId;
var poster = author.Metadata.Value.Images.FirstOrDefault(c => c.CoverType == MediaCoverTypes.Poster);
_coverMapper.ConvertToLocalUrls(resource.Author.Id, MediaCoverEntity.Author, resource.Author.Images);
var poster = resource.Author.Images.FirstOrDefault(c => c.CoverType == MediaCoverTypes.Poster);
if (poster != null)
{
resource.Author.RemotePoster = poster.Url;
resource.Author.RemotePoster = poster.RemoteUrl;
}
resource.Author.Folder = _fileNameBuilder.GetAuthorFolder(author);
}
else if (result is NzbDrone.Core.Books.Book)
else if (result is NzbDrone.Core.Books.Book book)
{
var book = (NzbDrone.Core.Books.Book)result;
resource.Book = book.ToResource();
resource.Book.Overview = book.Editions.Value.Single(x => x.Monitored).Overview;
resource.Book.Author = book.Author.Value.ToResource();
resource.Book.Editions = book.Editions.Value.ToResource();
resource.ForeignId = book.ForeignBookId;
var cover = book.Editions.Value.Single(x => x.Monitored).Images.FirstOrDefault(c => c.CoverType == MediaCoverTypes.Cover);
_coverMapper.ConvertToLocalUrls(resource.Book.Id, MediaCoverEntity.Book, resource.Book.Images);
var cover = resource.Book.Images.FirstOrDefault(c => c.CoverType == MediaCoverTypes.Cover);
if (cover != null)
{
resource.Book.RemoteCover = cover.Url;
resource.Book.RemoteCover = cover.RemoteUrl;
}
resource.Book.Author.Folder = _fileNameBuilder.GetAuthorFolder(book.Author);
}
else
{

View File

@@ -4,8 +4,7 @@ using Readarr.Http.REST;
namespace Readarr.Api.V1.Search
{
public class
SearchResource : RestResource
public class SearchResource : RestResource
{
public string ForeignId { get; set; }
public AuthorResource Author { get; set; }

View File

@@ -1,4 +1,5 @@
using System.Collections.Generic;
using FluentValidation;
using Microsoft.AspNetCore.Mvc;
using NzbDrone.Core.Datastore.Events;
using NzbDrone.Core.Messaging.Events;
@@ -20,6 +21,8 @@ namespace Readarr.Api.V1.Tags
: base(signalRBroadcaster)
{
_tagService = tagService;
SharedValidator.RuleFor(c => c.Label).NotEmpty();
}
protected override TagResource GetResourceById(int id)

View File

@@ -7119,16 +7119,6 @@
"schema": {
"$ref": "#/components/schemas/RemotePathMappingResource"
}
},
"text/json": {
"schema": {
"$ref": "#/components/schemas/RemotePathMappingResource"
}
},
"application/*+json": {
"schema": {
"$ref": "#/components/schemas/RemotePathMappingResource"
}
}
}
},
@@ -7163,14 +7153,6 @@
"200": {
"description": "OK",
"content": {
"text/plain": {
"schema": {
"type": "array",
"items": {
"$ref": "#/components/schemas/RemotePathMappingResource"
}
}
},
"application/json": {
"schema": {
"type": "array",
@@ -7178,14 +7160,6 @@
"$ref": "#/components/schemas/RemotePathMappingResource"
}
}
},
"text/json": {
"schema": {
"type": "array",
"items": {
"$ref": "#/components/schemas/RemotePathMappingResource"
}
}
}
}
}
@@ -8787,6 +8761,10 @@
"type": "string",
"nullable": true
},
"folder": {
"type": "string",
"nullable": true
},
"genres": {
"type": "array",
"items": {
@@ -10475,7 +10453,8 @@
"nullable": true
},
"wikiUrl": {
"$ref": "#/components/schemas/HttpUri"
"type": "string",
"nullable": true
}
},
"additionalProperties": false
@@ -10718,48 +10697,6 @@
},
"additionalProperties": false
},
"HttpUri": {
"type": "object",
"properties": {
"fullUri": {
"type": "string",
"nullable": true,
"readOnly": true
},
"scheme": {
"type": "string",
"nullable": true,
"readOnly": true
},
"host": {
"type": "string",
"nullable": true,
"readOnly": true
},
"port": {
"type": "integer",
"format": "int32",
"nullable": true,
"readOnly": true
},
"path": {
"type": "string",
"nullable": true,
"readOnly": true
},
"query": {
"type": "string",
"nullable": true,
"readOnly": true
},
"fragment": {
"type": "string",
"nullable": true,
"readOnly": true
}
},
"additionalProperties": false
},
"ICustomFormatSpecification": {
"type": "object",
"properties": {
@@ -11433,6 +11370,10 @@
"type": "string",
"nullable": true,
"readOnly": true
},
"remoteUrl": {
"type": "string",
"nullable": true
}
},
"additionalProperties": false

View File

@@ -6,6 +6,6 @@ namespace Readarr.Http.Frontend.Mappers
{
string Map(string resourceUrl);
bool CanHandle(string resourceUrl);
FileStreamResult GetResponse(string resourceUrl);
IActionResult GetResponse(string resourceUrl);
}
}

View File

@@ -43,7 +43,7 @@ namespace Readarr.Http.Frontend.Mappers
public override bool CanHandle(string resourceUrl)
{
return resourceUrl.StartsWith("/MediaCover", StringComparison.InvariantCultureIgnoreCase);
return resourceUrl.StartsWith("/MediaCover/", StringComparison.InvariantCultureIgnoreCase);
}
}
}

View File

@@ -0,0 +1,55 @@
using System;
using System.Net;
using System.Text.RegularExpressions;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.StaticFiles;
using NzbDrone.Core.MediaCover;
namespace Readarr.Http.Frontend.Mappers
{
public class MediaCoverProxyMapper : IMapHttpRequestsToDisk
{
private readonly Regex _regex = new Regex(@"/MediaCoverProxy/(?<hash>\w+)/(?<filename>(.+)\.(jpg|png|gif))");
private readonly IMediaCoverProxy _mediaCoverProxy;
private readonly IContentTypeProvider _mimeTypeProvider;
public MediaCoverProxyMapper(IMediaCoverProxy mediaCoverProxy)
{
_mediaCoverProxy = mediaCoverProxy;
_mimeTypeProvider = new FileExtensionContentTypeProvider();
}
public string Map(string resourceUrl)
{
return null;
}
public bool CanHandle(string resourceUrl)
{
return resourceUrl.StartsWith("/MediaCoverProxy/", StringComparison.InvariantCultureIgnoreCase);
}
public IActionResult GetResponse(string resourceUrl)
{
var match = _regex.Match(resourceUrl);
if (!match.Success)
{
return new StatusCodeResult((int)HttpStatusCode.NotFound);
}
var hash = match.Groups["hash"].Value;
var filename = match.Groups["filename"].Value;
var imageData = _mediaCoverProxy.GetImage(hash);
if (!_mimeTypeProvider.TryGetContentType(filename, out var contentType))
{
contentType = "application/octet-stream";
}
return new FileContentResult(imageData, contentType);
}
}
}

View File

@@ -30,7 +30,7 @@ namespace Readarr.Http.Frontend.Mappers
public abstract bool CanHandle(string resourceUrl);
public FileStreamResult GetResponse(string resourceUrl)
public IActionResult GetResponse(string resourceUrl)
{
var filePath = Map(resourceUrl);

View File

@@ -57,7 +57,7 @@ namespace Readarr.Http.Frontend
if (result != null)
{
if (result.ContentType == "text/html")
if ((result as FileResult)?.ContentType == "text/html")
{
Response.Headers.DisableCache();
}

View File

@@ -2243,9 +2243,9 @@ camelcase@^6.3.0:
integrity sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==
caniuse-lite@^1.0.30001646, caniuse-lite@^1.0.30001663:
version "1.0.30001707"
resolved "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001707.tgz"
integrity sha512-3qtRjw/HQSMlDWf+X79N206fepf4SOOU6SQLMaq/0KkZLmSjPxAkBOQQ+FxbHKfHmYLZFfdWsO3KA90ceHPSnw==
version "1.0.30001712"
resolved "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001712.tgz"
integrity sha512-MBqPpGYYdQ7/hfKiet9SCI+nmN5/hp4ZzveOJubl5DTAMa5oggjAuoi0Z4onBpKPFI2ePGnQuQIzF3VxDjDJig==
chalk@^2.4.1, chalk@^2.4.2:
version "2.4.2"