1
0
mirror of https://github.com/Sonarr/Sonarr.git synced 2026-03-05 13:20:20 -05:00

Compare commits

...

43 Commits

Author SHA1 Message Date
Mark McDowall
04f8595498 Custom Import List improvements
Fixed: Add placeholder title for Custom Import List title
New: Support 'title' property for Custom Import List
2024-07-05 16:35:08 -07:00
Bogdan
81ac73299a Fixed: Bulk series deletion for unmonitored series
Closes #6933
2024-07-05 19:34:56 -04:00
Mark McDowall
a779a5fad2 Fixed: Parsing of anime season releases with 3-digit number in title 2024-07-05 16:34:06 -07:00
Mark McDowall
bfe6a740fa Fixed: Parsing of anime releases using standard numbering
Closes #6925
2024-07-05 16:34:06 -07:00
Mark McDowall
c9ea40b874 New: Parse VFI as French
Closes #6927
2024-07-05 16:34:00 -07:00
Bogdan
4ee0ae1418 Fixed: History with unknown series 2024-07-05 16:33:50 -07:00
Bogdan
ac1da45ecd Fixed: Calculate Custom Formats after user specified options in Manual Import 2024-07-05 19:33:33 -04:00
Weblate
5c327d5be3 Multiple Translations updated by Weblate
ignore-downstream

Co-authored-by: Havok Dan <havokdan@yahoo.com.br>
Co-authored-by: Lizandra Candido da Silva <lizandra.c.s@gmail.com>
Co-authored-by: MattiaPell <mattiapellegrini16@gmail.com>
Co-authored-by: Weblate <noreply@weblate.org>
Co-authored-by: fordas <fordas15@gmail.com>
Co-authored-by: quek76 <quek@libertysurf.fr>
Translate-URL: https://translate.servarr.com/projects/servarr/sonarr/
Translate-URL: https://translate.servarr.com/projects/servarr/sonarr/es/
Translate-URL: https://translate.servarr.com/projects/servarr/sonarr/fr/
Translate-URL: https://translate.servarr.com/projects/servarr/sonarr/it/
Translate-URL: https://translate.servarr.com/projects/servarr/sonarr/pt_BR/
Translation: Servarr/Sonarr
2024-07-05 16:31:56 -07:00
Mark McDowall
55c1ce2e3d Bump version to 4.0.6 2024-06-30 15:42:47 -07:00
Weblate
fd7f0ea973 Multiple Translations updated by Weblate
ignore-downstream

Co-authored-by: GkhnGRBZ <gkhn.gurbuz@hotmail.com>
Co-authored-by: Havok Dan <havokdan@yahoo.com.br>
Co-authored-by: Kshitij Burman <kburman6@gmail.com>
Co-authored-by: MattiaPell <mattiapellegrini16@gmail.com>
Co-authored-by: Weblate <noreply@weblate.org>
Co-authored-by: damienmillet <contact@damien-millet.dev>
Co-authored-by: fordas <fordas15@gmail.com>
Translate-URL: https://translate.servarr.com/projects/servarr/sonarr/
Translate-URL: https://translate.servarr.com/projects/servarr/sonarr/es/
Translate-URL: https://translate.servarr.com/projects/servarr/sonarr/fr/
Translate-URL: https://translate.servarr.com/projects/servarr/sonarr/hi/
Translate-URL: https://translate.servarr.com/projects/servarr/sonarr/it/
Translate-URL: https://translate.servarr.com/projects/servarr/sonarr/pt_BR/
Translate-URL: https://translate.servarr.com/projects/servarr/sonarr/tr/
Translation: Servarr/Sonarr
2024-06-30 15:42:41 -07:00
Bogdan
d5dff8e8d6 Fixed: Trimming disabled logs database
Closes #6918
2024-06-30 13:49:41 -04:00
Bogdan
8099ba10af Fixed: Already imported downloads appearing in Queue briefly 2024-06-30 13:47:00 -04:00
Mark McDowall
143ccb1e2a Remove seriesTitle from EpisodeResource
Closes #6841
2024-06-28 06:22:10 -07:00
Mark McDowall
29480d9544 Fixed: Don't use cleaned up release title for release title 2024-06-28 06:22:04 -07:00
Mark McDowall
6de536a7ad Fixed: Limit Queue maximum page size to 200
Closes #6899
2024-06-26 09:45:43 -07:00
Mark McDowall
bce848facf Fixed: Reprocessing items that were previously blocked during importing 2024-06-26 09:45:28 -07:00
Mark McDowall
ea4fe392a0 New: Remove websites in parentheses before parsing 2024-06-25 15:52:24 -07:00
Mark McDowall
45fe585944 Fixed: Prevent errors parsing releases in unexpected formats 2024-06-25 15:52:24 -07:00
Mark McDowall
a0d2933134 New: Ignore Deluge torrents without a title
Closes #6885
2024-06-25 18:52:12 -04:00
Mark McDowall
4c622fd412 New: Ability to select Plex Media Server from plex.tv
Closes #6887
2024-06-25 15:51:57 -07:00
Bogdan
fb060730c7 Fixed: Exclude invalid releases from Newznab and Torznab parsers 2024-06-25 15:51:41 -07:00
Mark McDowall
6d5ff9c4d6 New: Improve UI status when downloads cannot be imported automatically
Closes #6873
2024-06-25 18:51:20 -04:00
Mark McDowall
63bed3e670 New: Parse anime seasons with trailing number in title
Closes #6883
2024-06-25 15:51:03 -07:00
Weblate
e684c10432 Multiple Translations updated by Weblate
ignore-downstream

Co-authored-by: Havok Dan <havokdan@yahoo.com.br>
Co-authored-by: MattiaPell <mattiapellegrini16@gmail.com>
Co-authored-by: Taylan Tatlı <taylantatli90@gmail.com>
Co-authored-by: Weblate <noreply@weblate.org>
Translate-URL: https://translate.servarr.com/projects/servarr/sonarr/it/
Translate-URL: https://translate.servarr.com/projects/servarr/sonarr/pt_BR/
Translate-URL: https://translate.servarr.com/projects/servarr/sonarr/tr/
Translation: Servarr/Sonarr
2024-06-25 15:50:56 -07:00
Bogdan
d2509798e9 New: Display stats for delete multiple series modal 2024-06-17 20:40:04 -07:00
Bogdan
6c39855ebe Fix UpdatePackageProviderFixture for v4
ignore-downstream
2024-06-17 20:39:33 -07:00
Bogdan
a30e9da767 New: Ignore inaccessible folders when getting folders 2024-06-17 20:39:33 -07:00
Mark McDowall
f8e81396d4 Fixed: Importing from IMDb list 2024-06-17 20:39:22 -07:00
Mark McDowall
7fccf590a8 Fixed: Adding series with unknown items in queue 2024-06-17 23:39:13 -04:00
Stephan Sundermann
e1b937e8d5 New: Add TMDB ID support
Closes #6866
2024-06-17 23:38:41 -04:00
Bogdan
c331c8bd11 Ignore Grabbed from API docs
Run application in docs.sh specific to platform
2024-06-10 20:30:26 -07:00
Mark McDowall
52b72925f9 Fixed: Improve error messaging if config file isn't formatted correctly
Closes #6860
2024-06-10 20:30:13 -07:00
Mark McDowall
378fedcd9d Fixed: Skip invalid series paths during validation 2024-06-10 23:30:03 -04:00
Bogdan
a90ab1a8fd Fixed: Ignore case when resolving indexer by name in release push 2024-06-10 20:29:46 -07:00
Bogdan
0edc5ba99a Fixed: Ignore case for name validation in providers 2024-06-10 20:29:46 -07:00
Bogdan
ea54ade9bf New: Refresh cache for tracked queue on series add 2024-06-10 20:29:41 -07:00
Weblate
e07eb05e8b Multiple Translations updated by Weblate
ignore-downstream

Co-authored-by: AlbertCoolGuy <Albert.rosenstand@gmail.com>
Co-authored-by: Havok Dan <havokdan@yahoo.com.br>
Co-authored-by: Weblate <noreply@weblate.org>
Co-authored-by: fordas <fordas15@gmail.com>
Co-authored-by: xuzhihui <5894940@qq.com>
Translate-URL: https://translate.servarr.com/projects/servarr/sonarr/da/
Translate-URL: https://translate.servarr.com/projects/servarr/sonarr/es/
Translate-URL: https://translate.servarr.com/projects/servarr/sonarr/pt_BR/
Translate-URL: https://translate.servarr.com/projects/servarr/sonarr/zh_CN/
Translation: Servarr/Sonarr
2024-06-10 20:29:28 -07:00
Bogdan
d9b771ab0b Fixed: Error sending Manual Interaction Required when series is unknown 2024-05-31 20:11:31 -04:00
Mark McDowall
6b08e849b8 Search for raw and clean titles for Newznab/Torznab indexers that support raw title searching 2024-05-31 17:10:32 -07:00
Mark McDowall
9c1f48ebc9 Fixed: Include full series title in episode search 2024-05-31 17:10:32 -07:00
Mark McDowall
fd3dd1ab7d New: Genres and Images for Webhooks and Notifiarr
Closes #6822
2024-05-31 17:10:13 -07:00
yammes08
11e5c5a11b Fixed: SDR Files Being Parsed As HLG 2024-05-31 20:09:53 -04:00
Weblate
48f0291884 Multiple Translations updated by Weblate
ignore-downstream

Co-authored-by: Ano10 <arnaudthommeray+github@ik.me>
Co-authored-by: Weblate <noreply@weblate.org>
Co-authored-by: r0bertreh <Robert.reh@live.de>
Translate-URL: https://translate.servarr.com/projects/servarr/sonarr/de/
Translate-URL: https://translate.servarr.com/projects/servarr/sonarr/fr/
Translation: Servarr/Sonarr
2024-05-31 17:09:11 -07:00
107 changed files with 1862 additions and 590 deletions

View File

@@ -22,7 +22,7 @@ env:
FRAMEWORK: net6.0
RAW_BRANCH_NAME: ${{ github.head_ref || github.ref_name }}
SONARR_MAJOR_VERSION: 4
VERSION: 4.0.5
VERSION: 4.0.6
jobs:
backend:

12
docs.sh
View File

@@ -25,17 +25,23 @@ slnFile=src/Sonarr.sln
platform=Posix
if [ "$PLATFORM" = "Windows" ]; then
application=Sonarr.Console.dll
else
application=Sonarr.dll
fi
dotnet clean $slnFile -c Debug
dotnet clean $slnFile -c Release
dotnet msbuild -restore $slnFile -p:Configuration=Debug -p:Platform=$platform -p:RuntimeIdentifiers=$RUNTIME -t:PublishAllRids
dotnet new tool-manifest
dotnet tool install --version 6.5.0 Swashbuckle.AspNetCore.Cli
dotnet tool install --version 6.6.2 Swashbuckle.AspNetCore.Cli
dotnet tool run swagger tofile --output ./src/Sonarr.Api.V3/openapi.json "$outputFolder/$FRAMEWORK/$RUNTIME/Sonarr.dll" v3 &
dotnet tool run swagger tofile --output ./src/Sonarr.Api.V3/openapi.json "$outputFolder/$FRAMEWORK/$RUNTIME/$application" v3 &
sleep 30
sleep 45
kill %1

View File

@@ -77,7 +77,7 @@ class HistoryRow extends Component {
onMarkAsFailedPress
} = this.props;
if (!episode) {
if (!series || !episode) {
return null;
}

View File

@@ -217,6 +217,7 @@ class Queue extends Component {
>
<TableOptionsModalWrapper
columns={columns}
maxPageSize={200}
{...otherProps}
optionsComponent={QueueOptionsConnector}
>

View File

@@ -70,6 +70,11 @@ function QueueStatus(props) {
iconName = icons.DOWNLOADED;
title = translate('Downloaded');
if (trackedDownloadState === 'importBlocked') {
title += ` - ${translate('UnableToImportAutomatically')}`;
iconKind = kinds.WARNING;
}
if (trackedDownloadState === 'importPending') {
title += ` - ${translate('WaitingToImport')}`;
iconKind = kinds.PURPLE;

View File

@@ -271,26 +271,32 @@ class EnhancedSelectInput extends Component {
this.setState({ isOpen: !this.state.isOpen });
};
onSelect = (value) => {
if (Array.isArray(this.props.value)) {
let newValue = null;
const index = this.props.value.indexOf(value);
onSelect = (newValue) => {
const { name, value, values, onChange } = this.props;
const additionalProperties = values.find((v) => v.key === newValue)?.additionalProperties;
if (Array.isArray(value)) {
let arrayValue = null;
const index = value.indexOf(newValue);
if (index === -1) {
newValue = this.props.values.map((v) => v.key).filter((v) => (v === value) || this.props.value.includes(v));
arrayValue = values.map((v) => v.key).filter((v) => (v === newValue) || value.includes(v));
} else {
newValue = [...this.props.value];
newValue.splice(index, 1);
arrayValue = [...value];
arrayValue.splice(index, 1);
}
this.props.onChange({
name: this.props.name,
value: newValue
onChange({
name,
value: arrayValue,
additionalProperties
});
} else {
this.setState({ isOpen: false });
this.props.onChange({
name: this.props.name,
value
onChange({
name,
value: newValue,
additionalProperties
});
}
};
@@ -485,7 +491,7 @@ class EnhancedSelectInput extends Component {
values.map((v, index) => {
const hasParent = v.parentKey !== undefined;
const depth = hasParent ? 1 : 0;
const parentSelected = hasParent && value.includes(v.parentKey);
const parentSelected = hasParent && Array.isArray(value) && value.includes(v.parentKey);
return (
<OptionComponent
key={v.key}

View File

@@ -9,7 +9,8 @@ import EnhancedSelectInput from './EnhancedSelectInput';
const importantFieldNames = [
'baseUrl',
'apiPath',
'apiKey'
'apiKey',
'authToken'
];
function getProviderDataKey(providerData) {
@@ -34,7 +35,9 @@ function getSelectOptions(items) {
key: option.value,
value: option.name,
hint: option.hint,
parentKey: option.parentValue
parentKey: option.parentValue,
isDisabled: option.isDisabled,
additionalProperties: option.additionalProperties
};
});
}
@@ -147,7 +150,7 @@ EnhancedSelectInputConnector.propTypes = {
provider: PropTypes.string.isRequired,
providerData: PropTypes.object.isRequired,
name: PropTypes.string.isRequired,
value: PropTypes.arrayOf(PropTypes.oneOfType([PropTypes.number, PropTypes.string])).isRequired,
value: PropTypes.oneOfType([PropTypes.string, PropTypes.number, PropTypes.arrayOf(PropTypes.string), PropTypes.arrayOf(PropTypes.number)]).isRequired,
values: PropTypes.arrayOf(PropTypes.object).isRequired,
selectOptionsProviderAction: PropTypes.string,
onChange: PropTypes.func.isRequired,

View File

@@ -21,6 +21,7 @@ function createCleanSeriesSelector() {
tvdbId,
tvMazeId,
imdbId,
tmdbId,
tags = []
} = series;
@@ -33,6 +34,7 @@ function createCleanSeriesSelector() {
tvdbId,
tvMazeId,
imdbId,
tmdbId,
firstCharacter: title.charAt(0).toLowerCase(),
tags: tags.reduce((acc, id) => {
const matchingTag = allTags.find((tag) => tag.id === id);

View File

@@ -14,6 +14,7 @@ function SeriesSearchResult(props) {
tvdbId,
tvMazeId,
imdbId,
tmdbId,
tags
} = props;
@@ -73,6 +74,14 @@ function SeriesSearchResult(props) {
null
}
{
match.key === 'tmdbId' && tmdbId ?
<div className={styles.alternateTitle}>
TmdbId: {tmdbId}
</div> :
null
}
{
tag ?
<div className={styles.tagContainer}>
@@ -97,6 +106,7 @@ SeriesSearchResult.propTypes = {
tvdbId: PropTypes.number,
tvMazeId: PropTypes.number,
imdbId: PropTypes.string,
tmdbId: PropTypes.number,
tags: PropTypes.arrayOf(PropTypes.object).isRequired,
match: PropTypes.object.isRequired
};

View File

@@ -13,6 +13,7 @@ const fuseOptions = {
'tvdbId',
'tvMazeId',
'imdbId',
'tmdbId',
'tags.label'
]
};

View File

@@ -49,11 +49,12 @@ class TableOptionsModal extends Component {
onPageSizeChange = ({ value }) => {
let pageSizeError = null;
const maxPageSize = this.props.maxPageSize ?? 250;
if (value < 5) {
pageSizeError = translate('TablePageSizeMinimum', { minimumValue: '5' });
} else if (value > 250) {
pageSizeError = translate('TablePageSizeMaximum', { maximumValue: '250' });
} else if (value > maxPageSize) {
pageSizeError = translate('TablePageSizeMaximum', { maximumValue: `${maxPageSize}` });
} else {
this.props.onTableOptionChange({ pageSize: value });
}
@@ -248,6 +249,7 @@ TableOptionsModal.propTypes = {
isOpen: PropTypes.bool.isRequired,
columns: PropTypes.arrayOf(PropTypes.object).isRequired,
pageSize: PropTypes.number,
maxPageSize: PropTypes.number,
canModifyColumns: PropTypes.bool.isRequired,
optionsComponent: PropTypes.elementType,
onTableOptionChange: PropTypes.func.isRequired,

View File

@@ -14,4 +14,9 @@
.deleteFilesMessage {
margin-top: 20px;
color: var(--dangerColor);
.deleteCount {
margin-top: 20px;
color: var(--warningColor);
}
}

View File

@@ -1,6 +1,7 @@
// This file is automatically generated.
// Please do not change this file!
interface CssExports {
'deleteCount': string;
'deleteFilesMessage': string;
'folderPath': string;
'pathContainer': string;

View File

@@ -50,15 +50,15 @@ class DeleteSeriesModalContent extends Component {
const {
title,
path,
statistics,
statistics = {},
deleteOptions,
onModalClose,
onDeleteOptionChange
} = this.props;
const {
episodeFileCount,
sizeOnDisk
episodeFileCount = 0,
sizeOnDisk = 0
} = statistics;
const deleteFiles = this.state.deleteFiles;
@@ -108,16 +108,20 @@ class DeleteSeriesModalContent extends Component {
</FormGroup>
{
deleteFiles &&
deleteFiles ?
<div className={styles.deleteFilesMessage}>
<div><InlineMarkdown data={translate('DeleteSeriesFolderConfirmation', { path })} blockClassName={styles.folderPath} /></div>
{
!!episodeFileCount &&
<div>{translate('DeleteSeriesFolderEpisodeCount', { episodeFileCount, size: formatBytes(sizeOnDisk) })}</div>
}
</div>
}
{
episodeFileCount ?
<div className={styles.deleteCount}>
{translate('DeleteSeriesFolderEpisodeCount', { episodeFileCount, size: formatBytes(sizeOnDisk) })}
</div> :
null
}
</div> :
null
}
</ModalBody>
<ModalFooter>

View File

@@ -175,6 +175,7 @@ class SeriesDetails extends Component {
tvdbId,
tvMazeId,
imdbId,
tmdbId,
title,
runtime,
ratings,
@@ -566,6 +567,7 @@ class SeriesDetails extends Component {
tvdbId={tvdbId}
tvMazeId={tvMazeId}
imdbId={imdbId}
tmdbId={tmdbId}
/>
}
kind={kinds.INVERSE}
@@ -719,6 +721,7 @@ SeriesDetails.propTypes = {
tvdbId: PropTypes.number.isRequired,
tvMazeId: PropTypes.number,
imdbId: PropTypes.string,
tmdbId: PropTypes.number,
title: PropTypes.string.isRequired,
runtime: PropTypes.number.isRequired,
ratings: PropTypes.object.isRequired,

View File

@@ -9,7 +9,8 @@ function SeriesDetailsLinks(props) {
const {
tvdbId,
tvMazeId,
imdbId
imdbId,
tmdbId
} = props;
return (
@@ -71,6 +72,22 @@ function SeriesDetailsLinks(props) {
</Label>
</Link>
}
{
!!tmdbId &&
<Link
className={styles.link}
to={`https://www.themoviedb.org/tv/${tmdbId}`}
>
<Label
className={styles.linkLabel}
kind={kinds.INFO}
size={sizes.LARGE}
>
TMDB
</Label>
</Link>
}
</div>
);
}
@@ -78,7 +95,8 @@ function SeriesDetailsLinks(props) {
SeriesDetailsLinks.propTypes = {
tvdbId: PropTypes.number.isRequired,
tvMazeId: PropTypes.number,
imdbId: PropTypes.string
imdbId: PropTypes.string,
tmdbId: PropTypes.number
};
export default SeriesDetailsLinks;

View File

@@ -10,4 +10,15 @@
.path {
margin-left: 5px;
color: var(--dangerColor);
font-weight: bold;
}
.statistics {
margin-left: 5px;
color: var(--warningColor);
}
.deleteFilesMessage {
margin-top: 20px;
color: var(--warningColor);
}

View File

@@ -1,9 +1,11 @@
// This file is automatically generated.
// Please do not change this file!
interface CssExports {
'deleteFilesMessage': string;
'message': string;
'path': string;
'pathContainer': string;
'statistics': string;
}
export const cssExports: CssExports;
export default cssExports;

View File

@@ -16,6 +16,7 @@ import Series from 'Series/Series';
import { bulkDeleteSeries, setDeleteOption } from 'Store/Actions/seriesActions';
import createAllSeriesSelector from 'Store/Selectors/createAllSeriesSelector';
import { CheckInputChanged } from 'typings/inputs';
import formatBytes from 'Utilities/Number/formatBytes';
import translate from 'Utilities/String/translate';
import styles from './DeleteSeriesModalContent.css';
@@ -85,6 +86,23 @@ function DeleteSeriesModalContent(props: DeleteSeriesModalContentProps) {
onModalClose,
]);
const { totalEpisodeFileCount, totalSizeOnDisk } = useMemo(() => {
return series.reduce(
(acc, { statistics = {} }) => {
const { episodeFileCount = 0, sizeOnDisk = 0 } = statistics;
acc.totalEpisodeFileCount += episodeFileCount;
acc.totalSizeOnDisk += sizeOnDisk;
return acc;
},
{
totalEpisodeFileCount: 0,
totalSizeOnDisk: 0,
}
);
}, [series]);
return (
<ModalContent onModalClose={onModalClose}>
<ModalHeader>{translate('DeleteSelectedSeries')}</ModalHeader>
@@ -136,20 +154,44 @@ function DeleteSeriesModalContent(props: DeleteSeriesModalContentProps) {
</div>
<ul>
{series.map((s) => {
{series.map(({ title, path, statistics = {} }) => {
const { episodeFileCount = 0, sizeOnDisk = 0 } = statistics;
return (
<li key={s.title}>
<span>{s.title}</span>
<li key={title}>
<span>{title}</span>
{deleteFiles && (
<span className={styles.pathContainer}>
-<span className={styles.path}>{s.path}</span>
<span>
<span className={styles.pathContainer}>
-<span className={styles.path}>{path}</span>
</span>
{!!episodeFileCount && (
<span className={styles.statistics}>
(
{translate('DeleteSeriesFolderEpisodeCount', {
episodeFileCount,
size: formatBytes(sizeOnDisk),
})}
)
</span>
)}
</span>
)}
</li>
);
})}
</ul>
{deleteFiles && !!totalEpisodeFileCount ? (
<div className={styles.deleteFilesMessage}>
{translate('DeleteSeriesFolderEpisodeCount', {
episodeFileCount: totalEpisodeFileCount,
size: formatBytes(totalSizeOnDisk),
})}
</div>
) : null}
</ModalBody>
<ModalFooter>

View File

@@ -70,6 +70,7 @@ interface Series extends ModelBase {
tvdbId: number;
tvMazeId: number;
tvRageId: number;
tmdbId: number;
useSceneNumbering: boolean;
year: number;
isSaving?: boolean;

View File

@@ -99,6 +99,7 @@ const seriesTokens = [
const seriesIdTokens = [
{ token: '{ImdbId}', example: 'tt12345' },
{ token: '{TvdbId}', example: '12345' },
{ token: '{TmdbId}', example: '11223' },
{ token: '{TvMazeId}', example: '54321' }
];

View File

@@ -4,7 +4,7 @@ import { connect } from 'react-redux';
import { createSelector } from 'reselect';
import {
saveNotification,
setNotificationFieldValue,
setNotificationFieldValues,
setNotificationValue,
testNotification,
toggleAdvancedSettings
@@ -27,7 +27,7 @@ function createMapStateToProps() {
const mapDispatchToProps = {
setNotificationValue,
setNotificationFieldValue,
setNotificationFieldValues,
saveNotification,
testNotification,
toggleAdvancedSettings
@@ -51,8 +51,8 @@ class EditNotificationModalContentConnector extends Component {
this.props.setNotificationValue({ name, value });
};
onFieldChange = ({ name, value }) => {
this.props.setNotificationFieldValue({ name, value });
onFieldChange = ({ name, value, additionalProperties = {} }) => {
this.props.setNotificationFieldValues({ properties: { ...additionalProperties, [name]: value } });
};
onSavePress = () => {
@@ -91,7 +91,7 @@ EditNotificationModalContentConnector.propTypes = {
saveError: PropTypes.object,
item: PropTypes.object.isRequired,
setNotificationValue: PropTypes.func.isRequired,
setNotificationFieldValue: PropTypes.func.isRequired,
setNotificationFieldValues: PropTypes.func.isRequired,
saveNotification: PropTypes.func.isRequired,
testNotification: PropTypes.func.isRequired,
toggleAdvancedSettings: PropTypes.func.isRequired,

View File

@@ -0,0 +1,25 @@
import getSectionState from 'Utilities/State/getSectionState';
import updateSectionState from 'Utilities/State/updateSectionState';
function createSetProviderFieldValuesReducer(section) {
return (state, { payload }) => {
if (section === payload.section) {
const { properties } = payload;
const newState = getSectionState(state, section);
newState.pendingChanges = Object.assign({}, newState.pendingChanges);
const fields = Object.assign({}, newState.pendingChanges.fields || {});
Object.keys(properties).forEach((name) => {
fields[name] = properties[name];
});
newState.pendingChanges.fields = fields;
return updateSectionState(state, section, newState);
}
return state;
};
}
export default createSetProviderFieldValuesReducer;

View File

@@ -5,6 +5,7 @@ import createRemoveItemHandler from 'Store/Actions/Creators/createRemoveItemHand
import createSaveProviderHandler, { createCancelSaveProviderHandler } from 'Store/Actions/Creators/createSaveProviderHandler';
import createTestProviderHandler, { createCancelTestProviderHandler } from 'Store/Actions/Creators/createTestProviderHandler';
import createSetProviderFieldValueReducer from 'Store/Actions/Creators/Reducers/createSetProviderFieldValueReducer';
import createSetProviderFieldValuesReducer from 'Store/Actions/Creators/Reducers/createSetProviderFieldValuesReducer';
import createSetSettingValueReducer from 'Store/Actions/Creators/Reducers/createSetSettingValueReducer';
import { createThunk } from 'Store/thunks';
import selectProviderSchema from 'Utilities/State/selectProviderSchema';
@@ -22,6 +23,7 @@ export const FETCH_NOTIFICATION_SCHEMA = 'settings/notifications/fetchNotificati
export const SELECT_NOTIFICATION_SCHEMA = 'settings/notifications/selectNotificationSchema';
export const SET_NOTIFICATION_VALUE = 'settings/notifications/setNotificationValue';
export const SET_NOTIFICATION_FIELD_VALUE = 'settings/notifications/setNotificationFieldValue';
export const SET_NOTIFICATION_FIELD_VALUES = 'settings/notifications/setNotificationFieldValues';
export const SAVE_NOTIFICATION = 'settings/notifications/saveNotification';
export const CANCEL_SAVE_NOTIFICATION = 'settings/notifications/cancelSaveNotification';
export const DELETE_NOTIFICATION = 'settings/notifications/deleteNotification';
@@ -55,6 +57,13 @@ export const setNotificationFieldValue = createAction(SET_NOTIFICATION_FIELD_VAL
};
});
export const setNotificationFieldValues = createAction(SET_NOTIFICATION_FIELD_VALUES, (payload) => {
return {
section,
...payload
};
});
//
// Details
@@ -99,6 +108,7 @@ export default {
reducers: {
[SET_NOTIFICATION_VALUE]: createSetSettingValueReducer(section),
[SET_NOTIFICATION_FIELD_VALUE]: createSetProviderFieldValueReducer(section),
[SET_NOTIFICATION_FIELD_VALUES]: createSetProviderFieldValuesReducer(section),
[SELECT_NOTIFICATION_SCHEMA]: (state, { payload }) => {
return selectProviderSchema(state, section, payload, (selectedSchema) => {

View File

@@ -153,7 +153,11 @@ namespace NzbDrone.Common.Disk
{
Ensure.That(path, () => path).IsValidPath(PathValidationType.CurrentOs);
return Directory.EnumerateDirectories(path);
return Directory.EnumerateDirectories(path, "*", new EnumerationOptions
{
AttributesToSkip = FileAttributes.System,
IgnoreInaccessible = true
});
}
public IEnumerable<string> GetFiles(string path, bool recursive)

View File

@@ -366,7 +366,7 @@ namespace NzbDrone.Core.Test.Download.CompletedDownloadServiceTests
Mocker.GetMock<IEventAggregator>()
.Verify(v => v.PublishEvent(It.IsAny<DownloadCompletedEvent>()), Times.Never());
_trackedDownload.State.Should().Be(TrackedDownloadState.ImportPending);
_trackedDownload.State.Should().Be(TrackedDownloadState.ImportBlocked);
}
private void AssertImported()

View File

@@ -608,5 +608,47 @@ namespace NzbDrone.Core.Test.IndexerSearchTests
allCriteria.Last().As<SingleEpisodeSearchCriteria>().SeasonNumber.Should().Be(2);
allCriteria.Last().As<SingleEpisodeSearchCriteria>().EpisodeNumber.Should().Be(3);
}
[Test]
public async Task episode_search_should_include_series_title_when_not_a_direct_title_match()
{
_xemSeries.Title = "Sonarr's Title";
_xemSeries.CleanTitle = "sonarrstitle";
WithEpisode(1, 12, 2, 3);
Mocker.GetMock<ISceneMappingService>()
.Setup(s => s.FindByTvdbId(It.IsAny<int>()))
.Returns(new List<SceneMapping>
{
new SceneMapping
{
TvdbId = _xemSeries.TvdbId,
SearchTerm = "Sonarrs Title",
ParseTerm = _xemSeries.CleanTitle,
SeasonNumber = 1,
SceneSeasonNumber = 1,
SceneOrigin = "tvdb",
Type = "ServicesProvider"
}
});
var allCriteria = WatchForSearchCriteria();
await Subject.EpisodeSearch(_xemEpisodes.First(), false, false);
Mocker.GetMock<ISceneMappingService>()
.Verify(v => v.FindByTvdbId(_xemSeries.Id), Times.Once());
allCriteria.Should().HaveCount(2);
allCriteria.First().Should().BeOfType<SingleEpisodeSearchCriteria>();
allCriteria.First().As<SingleEpisodeSearchCriteria>().SeasonNumber.Should().Be(1);
allCriteria.First().As<SingleEpisodeSearchCriteria>().EpisodeNumber.Should().Be(12);
allCriteria.Last().Should().BeOfType<SingleEpisodeSearchCriteria>();
allCriteria.Last().As<SingleEpisodeSearchCriteria>().SeasonNumber.Should().Be(2);
allCriteria.Last().As<SingleEpisodeSearchCriteria>().EpisodeNumber.Should().Be(3);
}
}
}

View File

@@ -36,7 +36,7 @@ namespace NzbDrone.Core.Test.IndexerTests.NewznabTests
_singleEpisodeSearchCriteria = new SingleEpisodeSearchCriteria
{
Series = new Tv.Series { TvRageId = 10, TvdbId = 20, TvMazeId = 30, ImdbId = "t40" },
Series = new Tv.Series { TvRageId = 10, TvdbId = 20, TvMazeId = 30, ImdbId = "t40", TmdbId = 50 },
SceneTitles = new List<string> { "Monkey Island" },
SeasonNumber = 1,
EpisodeNumber = 2
@@ -44,14 +44,14 @@ namespace NzbDrone.Core.Test.IndexerTests.NewznabTests
_seasonSearchCriteria = new SeasonSearchCriteria
{
Series = new Tv.Series { TvRageId = 10, TvdbId = 20, TvMazeId = 30, ImdbId = "t40" },
Series = new Tv.Series { TvRageId = 10, TvdbId = 20, TvMazeId = 30, ImdbId = "t40", TmdbId = 50 },
SceneTitles = new List<string> { "Monkey Island" },
SeasonNumber = 1,
};
_animeSearchCriteria = new AnimeEpisodeSearchCriteria()
{
Series = new Tv.Series { TvRageId = 10, TvdbId = 20, TvMazeId = 30, ImdbId = "t40" },
Series = new Tv.Series { TvRageId = 10, TvdbId = 20, TvMazeId = 30, ImdbId = "t40", TmdbId = 50 },
SceneTitles = new List<string>() { "Monkey+Island" },
AbsoluteEpisodeNumber = 100,
SeasonNumber = 5,
@@ -60,7 +60,7 @@ namespace NzbDrone.Core.Test.IndexerTests.NewznabTests
_animeSeasonSearchCriteria = new AnimeSeasonSearchCriteria()
{
Series = new Tv.Series { TvRageId = 10, TvdbId = 20, TvMazeId = 30, ImdbId = "t40" },
Series = new Tv.Series { TvRageId = 10, TvdbId = 20, TvMazeId = 30, ImdbId = "t40", TmdbId = 50 },
SceneTitles = new List<string> { "Monkey Island" },
SeasonNumber = 3,
};
@@ -268,6 +268,19 @@ namespace NzbDrone.Core.Test.IndexerTests.NewznabTests
page.Url.Query.Should().Contain("imdbid=t40");
}
[Test]
public void should_search_by_tmdb_if_supported()
{
_capabilities.SupportedTvSearchParameters = new[] { "q", "tmdbid", "season", "ep" };
var results = Subject.GetSearchRequests(_singleEpisodeSearchCriteria);
results.GetTier(0).Should().HaveCount(1);
var page = results.GetAllTiers().First().First();
page.Url.Query.Should().Contain("tmdbid=50");
}
[Test]
public void should_prefer_search_by_tvdbid_if_rid_supported()
{

View File

@@ -107,7 +107,7 @@ namespace NzbDrone.Core.Test.MediaFiles.MediaInfo
[TestCase(10, "", "", "", null, HdrFormat.None)]
[TestCase(10, "bt709", "bt709", "", null, HdrFormat.None)]
[TestCase(8, "bt2020", "smpte2084", "", null, HdrFormat.None)]
[TestCase(10, "bt2020", "bt2020-10", "", null, HdrFormat.Hlg10)]
[TestCase(10, "bt2020", "bt2020-10", "", null, HdrFormat.None)]
[TestCase(10, "bt2020", "arib-std-b67", "", null, HdrFormat.Hlg10)]
[TestCase(10, "bt2020", "smpte2084", "", null, HdrFormat.Pq10)]
[TestCase(10, "bt2020", "smpte2084", "FFMpegCore.SideData", null, HdrFormat.Pq10)]

View File

@@ -56,5 +56,14 @@ namespace NzbDrone.Core.Test.OrganizerTests.FileNameBuilderTests
Subject.GetSeriesFolder(_series)
.Should().Be($"Series Title ({_series.TvMazeId})");
}
[Test]
public void should_add_tmdb_id()
{
_namingConfig.SeriesFolderFormat = "{Series Title} ({TmdbId})";
Subject.GetSeriesFolder(_series)
.Should().Be($"Series Title ({_series.TmdbId})");
}
}
}

View File

@@ -39,6 +39,13 @@ namespace NzbDrone.Core.Test.ParserTests
ExceptionVerification.IgnoreWarns();
}
[TestCase("علم نف) أ.دعادل الأبيض ٢٠٢٤ ٣ ٣")]
[TestCase("ror-240618_1007-1022-")]
public void should_parse_unknown_formats_without_error(string title)
{
Parser.Parser.ParseTitle(title).Should().NotBeNull();
}
[Test]
public void should_not_parse_md5()
{

View File

@@ -64,6 +64,7 @@ namespace NzbDrone.Core.Test.ParserTests
[TestCase("Title.S01.720p.VFF.WEB-DL.AAC2.0.H.264-BTN")]
[TestCase("Title.S01.720p.VFQ.WEB-DL.AAC2.0.H.264-BTN")]
[TestCase("Title.S01.720p.TRUEFRENCH.WEB-DL.AAC2.0.H.264-BTN")]
[TestCase("Series In The Middle S01 Multi VFI VO 1080p WEB x265 HEVC AAC 5.1-Papaya")]
public void should_parse_language_french(string postTitle)
{
var result = LanguageParser.ParseLanguages(postTitle);

View File

@@ -33,6 +33,9 @@ namespace NzbDrone.Core.Test.ParserTests
[TestCase("Series No More S01 2023 1080p WEB-DL AVC AC3 2.0 Dual Audio -ZR-", "Series No More", 1)]
[TestCase("Series Title / S1E1-8 of 8 [2024, WEB-DL 1080p] + Original + RUS", "Series Title", 1)]
[TestCase("Series Title / S2E1-16 of 16 [2022, WEB-DL] RUS", "Series Title", 2)]
[TestCase("[hchcsen] Mobile Series 00 S01 [BD Remux Dual Audio 1080p AVC 2xFLAC] (Kidou Senshi Gundam 00 Season 1)", "Mobile Series 00", 1)]
[TestCase("[HorribleRips] Mobile Series 00 S1 [1080p]", "Mobile Series 00", 1)]
[TestCase("[Zoombie] Zom 100: Bucket List of the Dead S01 [Web][MKV][h265 10-bit][1080p][AC3 2.0][Softsubs (Zoombie)]", "Zom 100: Bucket List of the Dead", 1)]
public void should_parse_full_season_release(string postTitle, string title, int season)
{
var result = Parser.Parser.ParseTitle(postTitle);

View File

@@ -168,6 +168,10 @@ namespace NzbDrone.Core.Test.ParserTests
[TestCase("Босх: Спадок / Series: Legacy / S2E1 of 10 (2023) WEB-DL 1080p Ukr/Eng | sub Eng", "Series: Legacy", 2, 1)]
[TestCase("Titles.s06e01.1999.BDRip.1080p.Ukr.Eng.AC3.Hurtom.TNU.Tenax555", "Titles", 6, 1)]
[TestCase("Titles.s06.01.1999.BDRip.1080p.Ukr.Eng.AC3.Hurtom.TNU.Tenax555", "Titles", 6, 1)]
[TestCase("[Judas] Series Title (2024) - S01E14", "Series Title (2024)", 1, 14)]
[TestCase("[ReleaseGroup] SeriesTitle S01E1 Webdl 1080p", "SeriesTitle", 1, 1)]
[TestCase("[SubsPlus+] Series no Chill - S02E01 (NF WEB 1080p AVC AAC)", "Series no Chill", 2, 1)]
[TestCase("[SubsPlus+] Series no Chill - S02E01v2 (NF WEB 1080p AVC AAC)", "Series no Chill", 2, 1)]
// [TestCase("", "", 0, 0)]
public void should_parse_single_episode(string postTitle, string title, int seasonNumber, int episodeNumber)

View File

@@ -23,6 +23,7 @@ namespace NzbDrone.Core.Test.ParserTests
[TestCase("[www.test-hyphen.ca] - Series (2011) S01", "Series (2011)")]
[TestCase("test123.ca - Series Time S02 720p HDTV x264 CRON", "Series Time")]
[TestCase("[www.test-hyphen123.co.za] - Series Title S01E01", "Series Title")]
[TestCase("(seriesawake.com) Series Super - 57 [720p] [English Subbed]", "Series Super")]
public void should_not_parse_url_in_name(string postTitle, string title)
{

View File

@@ -44,6 +44,7 @@ namespace NzbDrone.Core.Test.QueueTests
_trackedDownloads = Builder<TrackedDownload>.CreateListOfSize(1)
.All()
.With(v => v.IsTrackable = true)
.With(v => v.DownloadItem = downloadItem)
.With(v => v.RemoteEpisode = remoteEpisode)
.Build()

View File

@@ -137,6 +137,20 @@ namespace NzbDrone.Core.Test.TvTests
.Verify(v => v.UpdateSeries(It.Is<Series>(s => s.TvMazeId == newSeriesInfo.TvMazeId), It.IsAny<bool>(), It.IsAny<bool>()));
}
[Test]
public void should_update_tmdb_id_if_changed()
{
var newSeriesInfo = _series.JsonClone();
newSeriesInfo.TmdbId = _series.TmdbId + 1;
GivenNewSeriesInfo(newSeriesInfo);
Subject.Execute(new RefreshSeriesCommand(new List<int> { _series.Id }));
Mocker.GetMock<ISeriesService>()
.Verify(v => v.UpdateSeries(It.Is<Series>(s => s.TmdbId == newSeriesInfo.TmdbId), It.IsAny<bool>(), It.IsAny<bool>()));
}
[Test]
public void should_log_error_if_tvdb_id_not_found()
{

View File

@@ -48,11 +48,11 @@ namespace NzbDrone.Core.Test.UpdateTests
{
const string branch = "main";
UseRealHttp();
var recent = Subject.GetRecentUpdates(branch, new Version(3, 0), null);
var recent = Subject.GetRecentUpdates(branch, new Version(4, 0), null);
recent.Should().NotBeEmpty();
recent.Should().OnlyContain(c => c.Hash.IsNotNullOrWhiteSpace());
recent.Should().OnlyContain(c => c.FileName.Contains($"Sonarr.{c.Branch}.3."));
recent.Should().OnlyContain(c => c.FileName.Contains($"Sonarr.{c.Branch}.4."));
recent.Should().OnlyContain(c => c.ReleaseDate.Year >= 2014);
recent.Where(c => c.Changes != null).Should().OnlyContain(c => c.Changes.New != null);
recent.Where(c => c.Changes != null).Should().OnlyContain(c => c.Changes.Fixed != null);

View File

@@ -1,4 +1,5 @@
using System;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
namespace NzbDrone.Core.Annotations
@@ -59,13 +60,27 @@ namespace NzbDrone.Core.Annotations
public string Value { get; set; }
}
public class FieldSelectOption
public class FieldSelectOption<T>
where T : struct
{
public int Value { get; set; }
public T Value { get; set; }
public string Name { get; set; }
public int Order { get; set; }
public string Hint { get; set; }
public int? ParentValue { get; set; }
public T? ParentValue { get; set; }
public bool? IsDisabled { get; set; }
public Dictionary<string, object> AdditionalProperties { get; set; }
}
public class FieldSelectStringOption
{
public string Value { get; set; }
public string Name { get; set; }
public int Order { get; set; }
public string Hint { get; set; }
public string ParentValue { get; set; }
public bool? IsDisabled { get; set; }
public Dictionary<string, object> AdditionalProperties { get; set; }
}
public enum FieldType

View File

@@ -419,13 +419,21 @@ namespace NzbDrone.Core.Configuration
throw new InvalidConfigFileException($"{_configFile} is corrupt. Please delete the config file and Sonarr will recreate it.");
}
return XDocument.Parse(_diskProvider.ReadAllText(_configFile));
var xDoc = XDocument.Parse(_diskProvider.ReadAllText(_configFile));
var config = xDoc.Descendants(CONFIG_ELEMENT_NAME).ToList();
if (config.Count != 1)
{
throw new InvalidConfigFileException($"{_configFile} is invalid. Please delete the config file and Sonarr will recreate it.");
}
return xDoc;
}
var xDoc = new XDocument(new XDeclaration("1.0", "utf-8", "yes"));
xDoc.Add(new XElement(CONFIG_ELEMENT_NAME));
var newXDoc = new XDocument(new XDeclaration("1.0", "utf-8", "yes"));
newXDoc.Add(new XElement(CONFIG_ELEMENT_NAME));
return xDoc;
return newXDoc;
}
}
catch (XmlException ex)

View File

@@ -0,0 +1,15 @@
using FluentMigrator;
using NzbDrone.Core.Datastore.Migration.Framework;
namespace NzbDrone.Core.Datastore.Migration
{
[Migration(206)]
public class add_tmdbid : NzbDroneMigrationBase
{
protected override void MainDbUpgrade()
{
Alter.Table("Series").AddColumn("TmdbId").AsInt32().WithDefaultValue(0);
Create.Index().OnTable("Series").OnColumn("TmdbId");
}
}
}

View File

@@ -25,6 +25,11 @@ namespace NzbDrone.Core.Download.Aggregation
public RemoteEpisode Augment(RemoteEpisode remoteEpisode)
{
if (remoteEpisode == null)
{
return null;
}
foreach (var augmenter in _augmenters)
{
try

View File

@@ -124,14 +124,23 @@ namespace NzbDrone.Core.Download.Clients.Deluge
}
var items = new List<DownloadClientItem>();
var ignoredCount = 0;
foreach (var torrent in torrents)
{
if (torrent.Hash == null)
// Silently ignore torrents with no hash
if (torrent.Hash.IsNullOrWhiteSpace())
{
continue;
}
// Ignore torrents without a name, but track to log a single warning for all invalid torrents.
if (torrent.Name.IsNullOrWhiteSpace())
{
ignoredCount++;
continue;
}
var item = new DownloadClientItem();
item.DownloadId = torrent.Hash.ToUpper();
item.Title = torrent.Name;
@@ -189,6 +198,11 @@ namespace NzbDrone.Core.Download.Clients.Deluge
items.Add(item);
}
if (ignoredCount > 0)
{
_logger.Warn("{0} torrent(s) were ignored becuase they did not have a title, check Deluge and remove any invalid torrents");
}
return items;
}

View File

@@ -64,8 +64,8 @@ namespace NzbDrone.Core.Download
SetImportItem(trackedDownload);
// Only process tracked downloads that are still downloading
if (trackedDownload.State != TrackedDownloadState.Downloading)
// Only process tracked downloads that are still downloading or have been blocked for importing due to an issue with matching
if (trackedDownload.State != TrackedDownloadState.Downloading && trackedDownload.State != TrackedDownloadState.ImportBlocked)
{
return;
}
@@ -96,7 +96,7 @@ namespace NzbDrone.Core.Download
if (series == null)
{
trackedDownload.Warn("Series title mismatch; automatic import is not possible. Check the download troubleshooting entry on the wiki for common causes.");
SendManualInteractionRequiredNotification(trackedDownload);
SetStateToImportBlocked(trackedDownload);
return;
}
@@ -108,7 +108,7 @@ namespace NzbDrone.Core.Download
if (seriesMatchType == SeriesMatchType.Id && releaseSource != ReleaseSourceType.InteractiveSearch)
{
trackedDownload.Warn("Found matching series via grab history, but release was matched to series by ID. Automatic import is not possible. See the FAQ for details.");
SendManualInteractionRequiredNotification(trackedDownload);
SetStateToImportBlocked(trackedDownload);
return;
}
@@ -129,7 +129,7 @@ namespace NzbDrone.Core.Download
if (trackedDownload.RemoteEpisode == null)
{
trackedDownload.Warn("Unable to parse download, automatic import is not possible.");
SendManualInteractionRequiredNotification(trackedDownload);
SetStateToImportBlocked(trackedDownload);
return;
}
@@ -187,7 +187,7 @@ namespace NzbDrone.Core.Download
if (statusMessages.Any())
{
trackedDownload.Warn(statusMessages.ToArray());
SendManualInteractionRequiredNotification(trackedDownload);
SetStateToImportBlocked(trackedDownload);
}
}
@@ -254,8 +254,10 @@ namespace NzbDrone.Core.Download
return false;
}
private void SendManualInteractionRequiredNotification(TrackedDownload trackedDownload)
private void SetStateToImportBlocked(TrackedDownload trackedDownload)
{
trackedDownload.State = TrackedDownloadState.ImportBlocked;
if (!trackedDownload.HasNotifiedManualInteractionRequired)
{
var grabbedHistories = _historyService.FindByDownloadId(trackedDownload.DownloadItem.DownloadId).Where(h => h.EventType == EpisodeHistoryEventType.Grabbed).ToList();

View File

@@ -73,8 +73,8 @@ namespace NzbDrone.Core.Download
public void Check(TrackedDownload trackedDownload)
{
// Only process tracked downloads that are still downloading
if (trackedDownload.State != TrackedDownloadState.Downloading)
// Only process tracked downloads that are still downloading or import is blocked (if they fail after attempting to be processed)
if (trackedDownload.State != TrackedDownloadState.Downloading && trackedDownload.State != TrackedDownloadState.ImportBlocked)
{
return;
}

View File

@@ -122,7 +122,7 @@ namespace NzbDrone.Core.Download.TrackedDownloads
_trackedDownloadService.TrackDownload((DownloadClientDefinition)downloadClient.Definition,
downloadItem);
if (trackedDownload != null && trackedDownload.State == TrackedDownloadState.Downloading)
if (trackedDownload is { State: TrackedDownloadState.Downloading or TrackedDownloadState.ImportBlocked })
{
_failedDownloadService.Check(trackedDownload);
_completedDownloadService.Check(trackedDownload);

View File

@@ -40,6 +40,7 @@ namespace NzbDrone.Core.Download.TrackedDownloads
public enum TrackedDownloadState
{
Downloading,
ImportBlocked,
ImportPending,
Importing,
Imported,

View File

@@ -28,6 +28,7 @@ namespace NzbDrone.Core.Download.TrackedDownloads
public class TrackedDownloadService : ITrackedDownloadService,
IHandle<EpisodeInfoRefreshedEvent>,
IHandle<SeriesAddedEvent>,
IHandle<SeriesDeletedEvent>
{
private readonly IParsingService _parsingService;
@@ -278,12 +279,29 @@ namespace NzbDrone.Core.Download.TrackedDownloads
}
}
public void Handle(SeriesAddedEvent message)
{
var cachedItems = _cache.Values
.Where(t =>
t.RemoteEpisode?.Series == null ||
message.Series?.TvdbId == t.RemoteEpisode.Series.TvdbId)
.ToList();
if (cachedItems.Any())
{
cachedItems.ForEach(UpdateCachedItem);
_eventAggregator.PublishEvent(new TrackedDownloadRefreshedEvent(GetTrackedDownloads()));
}
}
public void Handle(SeriesDeletedEvent message)
{
var cachedItems = _cache.Values.Where(t =>
t.RemoteEpisode?.Series != null &&
message.Series.Any(s => s.Id == t.RemoteEpisode.Series.Id))
.ToList();
var cachedItems = _cache.Values
.Where(t =>
t.RemoteEpisode?.Series != null &&
message.Series.Any(s => s.Id == t.RemoteEpisode.Series.Id || s.TvdbId == t.RemoteEpisode.Series.TvdbId))
.ToList();
if (cachedItems.Any())
{

View File

@@ -29,6 +29,7 @@ namespace NzbDrone.Core.Extras.Metadata.Consumers.Xbmc
Tvdb = series.TvdbId.ToString();
TvMaze = series.TvMazeId > 0 ? series.TvMazeId.ToString() : null;
TvRage = series.TvRageId > 0 ? series.TvMazeId.ToString() : null;
Tmdb = series.TmdbId > 0 ? series.TmdbId.ToString() : null;
Imdb = series.ImdbId;
}
}

View File

@@ -1,18 +1,26 @@
using NzbDrone.Core.Instrumentation;
using NzbDrone.Core.Configuration;
using NzbDrone.Core.Instrumentation;
namespace NzbDrone.Core.Housekeeping.Housekeepers
{
public class TrimLogDatabase : IHousekeepingTask
{
private readonly ILogRepository _logRepo;
private readonly IConfigFileProvider _configFileProvider;
public TrimLogDatabase(ILogRepository logRepo)
public TrimLogDatabase(ILogRepository logRepo, IConfigFileProvider configFileProvider)
{
_logRepo = logRepo;
_configFileProvider = configFileProvider;
}
public void Clean()
{
if (!_configFileProvider.LogDbEnabled)
{
return;
}
_logRepo.Trim();
}
}

View File

@@ -2,6 +2,7 @@ namespace NzbDrone.Core.ImportLists.Custom
{
public class CustomSeries
{
public string Title { get; set; }
public int TvdbId { get; set; }
}
}

View File

@@ -43,6 +43,7 @@ namespace NzbDrone.Core.ImportLists.Custom
{
series.Add(new ImportListItemInfo
{
Title = item.Title.IsNullOrWhiteSpace() ? $"TvdbId: {item.TvdbId}" : item.Title,
TvdbId = item.TvdbId
});
}

View File

@@ -24,7 +24,7 @@ namespace NzbDrone.Core.ImportLists.Imdb
// Parse TSV response from IMDB export
var rows = importResponse.Content.Split(new char[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries);
series = rows.Skip(1).SelectList(m => m.Split(',')).Where(m => m.Length > 1).SelectList(i => new ImportListItemInfo { ImdbId = i[1] });
series = rows.Skip(1).SelectList(m => m.Split(',')).Where(m => m.Length > 5).SelectList(i => new ImportListItemInfo { ImdbId = i[1], Title = i[5] });
return series;
}

View File

@@ -22,6 +22,7 @@ namespace NzbDrone.Core.IndexerSearch.Definitions
public virtual bool UserInvokedSearch { get; set; }
public virtual bool InteractiveSearch { get; set; }
public List<string> AllSceneTitles => SceneTitles.Concat(CleanSceneTitles).Distinct().ToList();
public List<string> CleanSceneTitles => SceneTitles.Select(GetCleanSceneTitle).Distinct().ToList();
public static string GetCleanSceneTitle(string title)

View File

@@ -265,7 +265,7 @@ namespace NzbDrone.Core.IndexerSearch
}
}
if (sceneMapping.ParseTerm == series.CleanTitle && sceneMapping.FilterRegex.IsNullOrWhiteSpace())
if (sceneMapping.SearchTerm == series.Title && sceneMapping.FilterRegex.IsNullOrWhiteSpace())
{
// Disable the implied mapping if we have an explicit mapping by the same name
includeGlobal = false;

View File

@@ -6,7 +6,7 @@ namespace NzbDrone.Core.Indexers.Newznab
{
public static class NewznabCategoryFieldOptionsConverter
{
public static List<FieldSelectOption> GetFieldSelectOptions(List<NewznabCategory> categories)
public static List<FieldSelectOption<int>> GetFieldSelectOptions(List<NewznabCategory> categories)
{
// Categories not relevant for Sonarr
var ignoreCategories = new[] { 1000, 3000, 4000, 6000, 7000 };
@@ -14,7 +14,7 @@ namespace NzbDrone.Core.Indexers.Newznab
// And maybe relevant for specific users
var unimportantCategories = new[] { 0, 2000 };
var result = new List<FieldSelectOption>();
var result = new List<FieldSelectOption<int>>();
if (categories == null)
{
@@ -41,7 +41,7 @@ namespace NzbDrone.Core.Indexers.Newznab
foreach (var category in categories.Where(cat => !ignoreCategories.Contains(cat.Id)).OrderBy(cat => unimportantCategories.Contains(cat.Id)).ThenBy(cat => cat.Id))
{
result.Add(new FieldSelectOption
result.Add(new FieldSelectOption<int>
{
Value = category.Id,
Name = category.Name,
@@ -52,7 +52,7 @@ namespace NzbDrone.Core.Indexers.Newznab
{
foreach (var subcat in category.Subcategories.OrderBy(cat => cat.Id))
{
result.Add(new FieldSelectOption
result.Add(new FieldSelectOption<int>
{
Value = subcat.Id,
Name = subcat.Name,

View File

@@ -119,12 +119,23 @@ namespace NzbDrone.Core.Indexers.Newznab
}
}
private bool SupportsTmdbSearch
{
get
{
var capabilities = _capabilitiesProvider.GetCapabilities(Settings);
return capabilities.SupportedTvSearchParameters != null &&
capabilities.SupportedTvSearchParameters.Contains("tmdbid");
}
}
// Combines all ID based searches
private bool SupportsTvIdSearches
{
get
{
return SupportsTvdbSearch || SupportsImdbSearch || SupportsTvRageSearch || SupportsTvMazeSearch;
return SupportsTvdbSearch || SupportsImdbSearch || SupportsTvRageSearch || SupportsTvMazeSearch || SupportsTmdbSearch;
}
}
@@ -410,7 +421,7 @@ namespace NzbDrone.Core.Indexers.Newznab
$"&season={NewznabifySeasonNumber(searchCriteria.SeasonNumber)}&ep={searchCriteria.EpisodeNumber}");
}
var queryTitles = TextSearchEngine == "raw" ? searchCriteria.SceneTitles : searchCriteria.CleanSceneTitles;
var queryTitles = TextSearchEngine == "raw" ? searchCriteria.AllSceneTitles : searchCriteria.CleanSceneTitles;
foreach (var queryTitle in queryTitles)
{
@@ -484,8 +495,9 @@ namespace NzbDrone.Core.Indexers.Newznab
var includeImdbSearch = SupportsImdbSearch && searchCriteria.Series.ImdbId.IsNotNullOrWhiteSpace();
var includeTvRageSearch = SupportsTvRageSearch && searchCriteria.Series.TvRageId > 0;
var includeTvMazeSearch = SupportsTvMazeSearch && searchCriteria.Series.TvMazeId > 0;
var includeTmdbSearch = SupportsTmdbSearch && searchCriteria.Series.TmdbId > 0;
if (SupportsAggregatedIdSearch && (includeTvdbSearch || includeTvRageSearch || includeTvMazeSearch))
if (SupportsAggregatedIdSearch && (includeTvdbSearch || includeTvRageSearch || includeTvMazeSearch || includeTmdbSearch))
{
var ids = "";
@@ -509,6 +521,11 @@ namespace NzbDrone.Core.Indexers.Newznab
ids += "&tvmazeid=" + searchCriteria.Series.TvMazeId;
}
if (includeTmdbSearch)
{
ids += "&tmdbid=" + searchCriteria.Series.TmdbId;
}
chain.Add(GetPagedRequests(MaxPages, categories, "tvsearch", ids + parameters));
}
else
@@ -541,6 +558,13 @@ namespace NzbDrone.Core.Indexers.Newznab
"tvsearch",
$"&tvmazeid={searchCriteria.Series.TvMazeId}{parameters}"));
}
else if (includeTmdbSearch)
{
chain.Add(GetPagedRequests(MaxPages,
categories,
"tvsearch",
$"&tmdbid={searchCriteria.Series.TmdbId}{parameters}"));
}
}
}

View File

@@ -68,16 +68,17 @@ namespace NzbDrone.Core.Indexers.Newznab
protected override bool PostProcess(IndexerResponse indexerResponse, List<XElement> items, List<ReleaseInfo> releases)
{
var enclosureTypes = items.SelectMany(GetEnclosures).Select(v => v.Type).Distinct().ToArray();
if (enclosureTypes.Any() && enclosureTypes.Intersect(PreferredEnclosureMimeTypes).Empty())
{
if (enclosureTypes.Intersect(TorrentEnclosureMimeTypes).Any())
{
_logger.Warn("{0} does not contain {1}, found {2}, did you intend to add a Torznab indexer?", indexerResponse.Request.Url, NzbEnclosureMimeType, enclosureTypes[0]);
return false;
}
else
{
_logger.Warn("{1} does not contain {1}, found {2}.", indexerResponse.Request.Url, NzbEnclosureMimeType, enclosureTypes[0]);
}
_logger.Warn("{0} does not contain {1}, found {2}.", indexerResponse.Request.Url, NzbEnclosureMimeType, enclosureTypes[0]);
}
return true;

View File

@@ -262,26 +262,26 @@ namespace NzbDrone.Core.Indexers
protected virtual RssEnclosure[] GetEnclosures(XElement item)
{
var enclosures = item.Elements("enclosure")
.Select(v =>
{
try
{
return new RssEnclosure
{
Url = v.Attribute("url")?.Value,
Type = v.Attribute("type")?.Value,
Length = v.Attribute("length")?.Value?.ParseInt64() ?? 0
};
}
catch (Exception e)
{
_logger.Warn(e, "Failed to get enclosure for: {0}", item.Title());
}
.Select(v =>
{
try
{
return new RssEnclosure
{
Url = v.Attribute("url")?.Value,
Type = v.Attribute("type")?.Value,
Length = v.Attribute("length")?.Value?.ParseInt64() ?? 0
};
}
catch (Exception ex)
{
_logger.Warn(ex, "Failed to get enclosure for: {0}", item.Title());
}
return null;
})
.Where(v => v != null)
.ToArray();
return null;
})
.Where(v => v != null)
.ToArray();
return enclosures;
}

View File

@@ -59,16 +59,17 @@ namespace NzbDrone.Core.Indexers.Torznab
protected override bool PostProcess(IndexerResponse indexerResponse, List<XElement> items, List<ReleaseInfo> releases)
{
var enclosureTypes = items.SelectMany(GetEnclosures).Select(v => v.Type).Distinct().ToArray();
if (enclosureTypes.Any() && enclosureTypes.Intersect(PreferredEnclosureMimeTypes).Empty())
{
if (enclosureTypes.Intersect(UsenetEnclosureMimeTypes).Any())
{
_logger.Warn("{0} does not contain {1}, found {2}, did you intend to add a Newznab indexer?", indexerResponse.Request.Url, TorrentEnclosureMimeType, enclosureTypes[0]);
return false;
}
else
{
_logger.Warn("{1} does not contain {1}, found {2}.", indexerResponse.Request.Url, TorrentEnclosureMimeType, enclosureTypes[0]);
}
_logger.Warn("{0} does not contain {1}, found {2}.", indexerResponse.Request.Url, TorrentEnclosureMimeType, enclosureTypes[0]);
}
return true;

View File

@@ -34,5 +34,9 @@
"AddImportListImplementation": "Tilføj importliste - {implementationName}",
"AddRootFolderError": "Kunne ikke tilføje rodmappe",
"Table": "Tabel",
"AddIndexer": "Tilføj indekser"
"AddIndexer": "Tilføj indekser",
"AddDownloadClient": "Tilføj downloadklient",
"AddImportListExclusion": "Tilføj ekslusion til importeringslisten",
"AddDelayProfileError": "Kan ikke tilføje en ny forsinkelsesprofil. Prøv venligst igen.",
"AddDownloadClientError": "Ikke muligt at tilføje en ny downloadklient. Prøv venligst igen."
}

View File

@@ -128,7 +128,7 @@
"AddConnectionImplementation": "Verbindung hinzufügen - {implementationName}",
"AddDownloadClientImplementation": "Download-Client hinzufügen - {implementationName}",
"AddIndexerImplementation": "Indexer hinzufügen - {implementationName}",
"AddNotificationError": "Neue Benachrichtigung konnte nicht hinzugefügt werden, bitte versuchen Sie es erneut.",
"AddNotificationError": "Die neue Benachrichtigung konnte nicht hinzugefügt werden, bitte erneut probieren.",
"AddQualityProfileError": "Qualitätsprofil konnte nicht hinzugefügt werden. Bitte versuchen Sie es erneut.",
"AddNewSeriesRootFolderHelpText": "Unterordner '{folder}' wird automatisch erstellt",
"AddNewSeriesSearchForMissingEpisodes": "Suche für fehlende Episoden starten",
@@ -144,7 +144,7 @@
"AuthenticationRequiredPasswordHelpTextWarning": "Gib ein neues Passwort ein",
"AuthenticationRequiredUsernameHelpTextWarning": "Gib einen neuen Benutzernamen ein",
"AuthenticationRequiredHelpText": "Ändern, welche anfragen Authentifizierung benötigen. Ändere nichts wenn du dir nicht des Risikos bewusst bist.",
"AnalyseVideoFilesHelpText": "Videoinformationen wie Auflösung, Laufzeit und Codec-Informationen aus Dateien extrahieren. Dies erfordert, dass {appName} Teile der Datei liest, was bei Scans zu hoher Festplatten- oder Netzwerkaktivität führen kann.",
"AnalyseVideoFilesHelpText": "Videoinformationen wie Auflösung, Laufzeit und Codec aus Datien erkennen. Dazu ist es erforderlich, dass {appName} Teile der Datei liest, was zu hoher Festplatten- oder Netzwerkaktivität während der Scans führen kann.",
"AnalyticsEnabledHelpText": "Senden Sie anonyme Nutzungs- und Fehlerinformationen an die Server von {appName}. Dazu gehören Informationen zu Ihrem Browser, welche {appName}-WebUI-Seiten Sie verwenden, Fehlerberichte sowie Betriebssystem- und Laufzeitversion. Wir werden diese Informationen verwenden, um Funktionen und Fehlerbehebungen zu priorisieren.",
"AutoTaggingNegateHelpText": "Falls aktiviert wird die Auto Tagging Regel nicht angewendet, solange diese Bedingung {implementationName} zutrifft.",
"CopyUsingHardlinksSeriesHelpText": "Mithilfe von Hardlinks kann {appName} Seeding-Torrents in den Serienordner importieren, ohne zusätzlichen Speicherplatz zu beanspruchen oder den gesamten Inhalt der Datei zu kopieren. Hardlinks funktionieren nur, wenn sich Quelle und Ziel auf demselben Volume befinden",
@@ -164,7 +164,7 @@
"RestartReloadNote": "Hinweis: {appName} startet während des Wiederherstellungsvorgangs automatisch neu und lädt die Benutzeroberfläche neu.",
"AutoRedownloadFailedHelpText": "Suchen Sie automatisch nach einer anderen Version und versuchen Sie, sie herunterzuladen",
"AirDate": "Ausstrahlungsdatum",
"AgeWhenGrabbed": "Alter (zum Zeitpunkt der Entführung)",
"AgeWhenGrabbed": "Alter (bei Erfassung)",
"ApplyTagsHelpTextHowToApplySeries": "So wenden Sie Tags auf die ausgewählte Serie an",
"ApiKey": "API-Schlüssel",
"AutoTaggingLoadError": "Automatisches Tagging konnte nicht geladen werden",
@@ -272,7 +272,7 @@
"ContinuingOnly": "Nur fortlaufend",
"ContinuingSeriesDescription": "Weitere Episoden/eine weitere Staffel werden erwartet",
"CopyToClipboard": "In die Zwischenablage kopieren",
"CouldNotFindResults": "Es konnten keine Ergebnisse für „{term}“ gefunden werden.",
"CouldNotFindResults": "Es konnten keine Ergebnisse für „{term}“ gefunden werden",
"CountSeriesSelected": "{count} Serie ausgewählt",
"CreateEmptySeriesFoldersHelpText": "Erstellen Sie beim Festplatten-Scan Ordner für fehlende Serien",
"CreateGroup": "Gruppe erstellen",
@@ -416,7 +416,7 @@
"DownloadClientSabnzbdValidationEnableDisableDateSorting": "Deaktivieren Sie die Datumssortierung",
"DownloadClientSabnzbdValidationEnableDisableDateSortingDetail": "Sie müssen die Datumssortierung für die von {appName} verwendete Kategorie deaktivieren, um Importprobleme zu vermeiden. Gehen Sie zu Sabnzbd, um das Problem zu beheben.",
"DownloadClientSabnzbdValidationEnableDisableMovieSorting": "Deaktivieren Sie die Filmsortierung",
"AllResultsAreHiddenByTheAppliedFilter": "Alle Resultate werden wegen des angewandten Filters nicht angezeigt",
"AllResultsAreHiddenByTheAppliedFilter": "Alle Ergebnisse werden durch den angewendeten Filter ausgeblendet",
"RegularExpressionsCanBeTested": "Reguläre Ausdrücke können [hier] getestet werden ({url}).",
"ReleaseSceneIndicatorUnknownSeries": "Unbekannte Folge oder Serie.",
"RemoveFilter": "Filter entfernen",
@@ -549,7 +549,7 @@
"CountImportListsSelected": "{count} Importliste(n) ausgewählt",
"CountIndexersSelected": "{count} Indexer ausgewählt",
"CountSelectedFiles": "{selectedCount} ausgewählte Dateien",
"CustomFormatUnknownConditionOption": "Unbekannte Option {key} für Bedingung {implementation}",
"CustomFormatUnknownConditionOption": "Unbekannte Option '{key}' für die Bedingung '{implementation}'",
"CustomFormatsSettings": "Einstellungen für eigene Formate",
"Daily": "Täglich",
"Dash": "Bindestrich",
@@ -580,7 +580,7 @@
"SslCertPassword": "SSL-Zertifikatskennwort",
"SpecialsFolderFormat": "Specials-Ordnerformat",
"SourceTitle": "Quellentitel",
"Agenda": "Tagesordnung",
"Agenda": "Agenda",
"AnEpisodeIsDownloading": "Eine Episode wird heruntergeladen",
"CollapseMultipleEpisodesHelpText": "Reduzieren Sie mehrere Episoden, die am selben Tag ausgestrahlt werden",
"Connect": "Verbinden",
@@ -792,5 +792,86 @@
"MediaManagement": "Medienverwaltung",
"StartupDirectory": "Start-Verzeichnis",
"OnRename": "Bei Umbenennung",
"MaintenanceRelease": "Maintenance Release: Fehlerbehebungen und andere Verbesserungen. Siehe Github Commit Verlauf für weitere Details"
"MaintenanceRelease": "Maintenance Release: Fehlerbehebungen und andere Verbesserungen. Siehe Github Commit Verlauf für weitere Details",
"BlocklistRelease": "Release sperren",
"BranchUpdateMechanism": "Git-Branch für den externen Updateablauf",
"AutoTaggingSpecificationGenre": "Genre(s)",
"AutoTaggingSpecificationOriginalLanguage": "Sprache",
"AutoTaggingSpecificationQualityProfile": "Qualitätsprofil",
"AutoTaggingSpecificationRootFolder": "Stammverzeichnis",
"AutoTaggingSpecificationStatus": "Status",
"ConnectionSettingsUrlBaseHelpText": "Fügt ein Präfix zur {connectionName} URL hinzu, z. B. {url}",
"DeleteImportListExclusion": "Importlisten Ausschluss löschen",
"DeleteTag": "Tag löschen",
"DoNotBlocklistHint": "Entfernen ohne Sperren",
"DownloadClientPriorityHelpText": "Download-Client-Priorität von 1 (Höchste) bis 50 (Niedrigste). Standard: 1. Round-Robin wird für Clients mit der gleichen Priorität verwendet.",
"DownloadClientSettingsRecentPriority": "Neueste Priorität",
"DownloadClientValidationApiKeyIncorrect": "API-Key fehlerhaft",
"ClientPriority": "Priorität",
"Cutoff": "Schwelle",
"DownloadClient": "Downloader",
"DownloadClientSabnzbdValidationUnknownVersion": "Unbekannte Version: {rawVersion}",
"CutoffUnmet": "Schwelle nicht erreicht",
"DownloadClientSettingsInitialState": "Ausgangszustand",
"DownloadClientValidationApiKeyRequired": "API-Key benötigt",
"CustomFormatsSpecificationRegularExpressionHelpText": "Benutzerdefiniertes Format RegEx ist nicht groß-/kleinschreibungssensitiv",
"CustomFormatsSpecificationMinimumSize": "Mindestgröße",
"CustomFormatsSpecificationRegularExpression": "Regulären Ausdruck",
"CustomFormatsSpecificationReleaseGroup": "Release-Gruppe",
"CustomFormatsSpecificationResolution": "Auflösung",
"DeleteImportListExclusionMessageText": "Bist du sicher, dass du diesen Importlisten Ausschluss löschen willst?",
"DownloadClientSabnzbdValidationEnableDisableTvSorting": "TV-Sortierung deaktivieren",
"CustomFormatUnknownCondition": "Unbekannte Eigene Formatbedingung '{implementation}'",
"ReleaseGroups": "Release Gruppen",
"DownloadClientSettingsUseSslHelpText": "Sichere Verbindung verwenden, wenn Verbindung zu {clientName} hergestellt wird",
"ReleaseRejected": "Release abgelehnt",
"Clear": "Leeren",
"DownloadClientValidationCategoryMissing": "Kategorie existiert nicht",
"DownloadClientValidationAuthenticationFailure": "Authentifizierung fehlgeschlagen",
"DownloadClientValidationErrorVersion": "{clientName} Version sollte mindestens {requiredVersion} sein. Die gemeldete Version ist {reportedVersion}",
"DownloadClientValidationGroupMissing": "Gruppe existiert nicht",
"DownloadClientValidationSslConnectFailure": "Verbindung über SSL nicht möglich",
"ReleaseProfilesLoadError": "Release-Profile können nicht geladen werden",
"DownloadClientDelugeSettingsDirectory": "Download Verzeichnis",
"DownloadClientDelugeSettingsDirectoryCompleted": "Verschieben, wenn Verzeichnis abgeschlossen",
"DownloadClientSettings": "Downloader Einstellungen",
"IgnoreDownloadHint": "Hält {appName} von der weiteren Verarbeitung dieses Downloads ab",
"ClearBlocklist": "Sperrliste leeren",
"CleanLibraryLevel": "Mediathek aufräumen",
"CloneAutoTag": "Automatische Tags kopieren",
"DownloadClientSettingsOlderPriorityEpisodeHelpText": "Priorität beim Abrufen von Episoden, die vor mehr als 14 Tagen ausgestrahlt wurden",
"DownloadClientSettingsInitialStateHelpText": "Anfangszustand für zu {clientName} hinzugefügte Torrents",
"DownloadClientSettingsOlderPriority": "Ältere Priorität",
"IgnoreDownload": "Download ignorieren",
"CustomFormatsSettingsTriggerInfo": "Ein Eigenes Format wird auf eine Veröffentlichung oder Datei angewandt, wenn sie mindestens einer der verschiedenen ausgewählten Bedingungen entspricht.",
"DatabaseMigration": "DB Migration",
"DownloadClientSettingsDestinationHelpText": "Legt das Ziel für den Download manuell fest; lassen Sie es leer, um die Standardeinstellung zu verwenden",
"DownloadClientSettingsCategorySubFolderHelpText": "Das Hinzufügen einer spezifischen Kategorie für {appName} vermeidet Konflikte mit nicht verwandten Downloads, die nicht {appName} sind. Die Verwendung einer Kategorie ist optional, wird aber dringend empfohlen. Erzeugt ein Unterverzeichnis [category] im Ausgabeverzeichnis.",
"BlocklistReleases": "Release sperren",
"DownloadClientQbittorrentTorrentStateMissingFiles": "qBittorrent meldet fehlende Dateien",
"ChangeCategoryHint": "Änderung des Downloads in die 'Post-Import-Kategorie' vom Download-Client",
"ChangeCategoryMultipleHint": "Änderung der Downloads in die 'Post-Import-Kategorie' vom Download-Client",
"CustomFormatsSpecificationSource": "Quelle",
"BlocklistFilterHasNoItems": "Ausgewählter Blocklistenfilter enthält keine Elemente",
"CustomFilter": "Benutzerdefinierter Filter",
"CustomFormatsSpecificationFlag": "Markierung",
"CustomFormatScore": "Eigenes Format Bewertungspunkte",
"CustomFormatsSpecificationLanguage": "Sprache",
"CustomFormatsSpecificationMaximumSize": "Maximale Größe",
"AutoTaggingSpecificationTag": "Tag",
"BindAddress": "Adresse binden",
"DownloadClientSettingsRecentPriorityEpisodeHelpText": "Priorität beim Abrufen von Episoden, die innerhalb der letzten 14 Tage ausgestrahlt wurden",
"DownloadClientSettingsUrlBaseHelpText": "Fügt ein Präfix zur {clientName} Url hinzu, z.B. {url}",
"DownloadClientTransmissionSettingsDirectoryHelpText": "Optionaler Speicherort für Downloads; leer lassen, um den Standardspeicherort für Übertragungen zu verwenden",
"DownloadClientUTorrentTorrentStateError": "uTorrent meldet einen Fehler",
"DoNotBlocklist": "Nicht Sperren",
"ReleaseHash": "Release Hash",
"DownloadClientAriaSettingsDirectoryHelpText": "Optionaler Speicherort für Downloads. Lassen Sie das Feld leer, um den standardmäßigen rTorrent-Speicherort zu verwenden",
"DownloadClientSeriesTagHelpText": "Verwenden Sie diesen Downloader nur für Serien mit mindestens einem passenden Tag. Lassen Sie ihn leer, um ihn für alle Serien zu verwenden.",
"DownloadClientSettingsCategoryHelpText": "Das Hinzufügen einer spezifischen Kategorie für {appName} vermeidet Konflikte mit nicht verwandten Downloads, die nicht {appName} sind. Die Verwendung einer Kategorie ist optional, wird aber dringend empfohlen.",
"DownloadClientSettingsPostImportCategoryHelpText": "Kategorie für {appName}, die nach dem Importieren des Downloads festgelegt wird. {appName} wird Torrents in dieser Kategorie nicht entfernen, auch wenn das Seeding beendet ist. Leer lassen, um dieselbe Kategorie beizubehalten.",
"DownloadClientValidationCategoryMissingDetail": "Die von Ihnen eingegebene Kategorie existiert nicht in {clientName}. Erstellen Sie sie zuerst in {clientName}.",
"DownloadClientValidationGroupMissingDetail": "Die von Ihnen eingegebene Gruppe existiert nicht in {clientName}. Erstellen Sie sie zuerst in {clientName}.",
"IgnoreDownloads": "Downloads ignorieren",
"IgnoreDownloadsHint": "Hindert {appName}, diese Downloads weiter zu verarbeiten"
}

View File

@@ -1361,6 +1361,8 @@
"NotificationsNtfyValidationAuthorizationRequired": "Authorization is required",
"NotificationsPlexSettingsAuthToken": "Auth Token",
"NotificationsPlexSettingsAuthenticateWithPlexTv": "Authenticate with Plex.tv",
"NotificationsPlexSettingsServer": "Server",
"NotificationsPlexSettingsServerHelpText": "Select server from plex.tv account after authenticating",
"NotificationsPlexValidationNoTvLibraryFound": "At least one TV library is required",
"NotificationsPushBulletSettingSenderId": "Sender ID",
"NotificationsPushBulletSettingSenderIdHelpText": "The device ID to send notifications from, use device_iden in the device's URL on pushbullet.com (leave blank to send from yourself)",
@@ -1992,6 +1994,7 @@
"Umask770Description": "{octal} - Owner & Group write",
"Umask775Description": "{octal} - Owner & Group write, Other read",
"Umask777Description": "{octal} - Everyone write",
"UnableToImportAutomatically": "Unable to Import Automatically",
"UnableToLoadAutoTagging": "Unable to load auto tagging",
"UnableToLoadBackups": "Unable to load backups",
"UnableToUpdateSonarrDirectly": "Unable to update {appName} directly,",

View File

@@ -1,7 +1,7 @@
{
"Added": "Añadido",
"ApplyChanges": "Aplicar Cambios",
"AuthBasic": "Básico (ventana emergente del navegador)",
"AuthBasic": "Básica (Ventana emergente del navegador)",
"BackupFolderHelpText": "Las rutas relativas estarán en el directorio AppData de {appName}",
"BackupsLoadError": "No se pudieron cargar las copias de seguridad",
"Enable": "Habilitar",
@@ -36,22 +36,22 @@
"AddNewRestriction": "Añadir nueva restricción",
"AddRemotePathMapping": "Añadir Asignación de Ruta Remota",
"Analytics": "Analíticas",
"ApiKey": "Clave de API",
"ApiKey": "Clave API",
"AnimeEpisodeFormat": "Formato de Episodio de Anime",
"ApplicationUrlHelpText": "La URL externa de la aplicación incluyendo http(s)://, puerto y URL base",
"ApplyTagsHelpTextReplace": "Reemplazar: Sustituye las etiquetas por las introducidas (introduce \"no tags\" para borrar todas las etiquetas)",
"ApplicationURL": "URL de la aplicación",
"Authentication": "Autenticación",
"AuthForm": "Formularios (Página de inicio de sesión)",
"AuthenticationMethodHelpText": "Requerir nombre de usuario y contraseña para acceder {appName}",
"AuthenticationMethodHelpText": "Requiere usuario y contraseña para acceder a {appName}",
"AuthenticationRequired": "Autenticación requerida",
"AutoTaggingLoadError": "No se pudo cargar el etiquetado automático",
"AutoRedownloadFailedHelpText": "Buscar e intentar descargar automáticamente una versión diferente",
"AutoRedownloadFailedHelpText": "Busca e intenta descargar automáticamente una versión diferente",
"Backup": "Copia de seguridad",
"AutomaticSearch": "Búsqueda Automática",
"Automatic": "Automático",
"BindAddressHelpText": "Dirección IP4 válida, localhost o '*' para todas las interfaces",
"BindAddress": "Dirección de Ligado",
"BindAddress": "Dirección de enlace",
"Branch": "Rama",
"BuiltIn": "Integrado",
"Condition": "Condición",
@@ -65,7 +65,7 @@
"Duplicate": "Duplicar",
"Error": "Error",
"Episodes": "Episodios",
"External": "Externo",
"External": "Externa",
"Extend": "Extender",
"Restore": "Restaurar",
"Security": "Seguridad",
@@ -78,10 +78,10 @@
"Torrents": "Torrents",
"Ui": "Interfaz",
"Underscore": "Guion bajo",
"UpdateMechanismHelpText": "Usar el actualizador integrado de {appName} o un script",
"UpdateMechanismHelpText": "Usa el actualizador integrado de {appName} o un script",
"Warn": "Advertencia",
"AutoTagging": "Etiquetado Automático",
"AddAutoTag": "Añadir Etiqueta Automática",
"AddAutoTag": "Añadir etiqueta automática",
"AddCondition": "Añadir Condición",
"AbsoluteEpisodeNumbers": "Número(s) de Episodio Absoluto(s)",
"AirDate": "Fecha de Emisión",
@@ -128,7 +128,7 @@
"AudioLanguages": "Idiomas de Audio",
"Episode": "Episodio",
"Activity": "Actividad",
"AddNew": "Añadir Nuevo",
"AddNew": "Añadir nuevo",
"ApplyTagsHelpTextAdd": "Añadir: Añade las etiquetas a la lista de etiquetas existente",
"ApplyTagsHelpTextRemove": "Eliminar: Elimina las etiquetas introducidas",
"Blocklist": "Lista de bloqueos",
@@ -165,7 +165,7 @@
"AddRemotePathMappingError": "No se pudo añadir una nueva asignación de ruta remota, inténtelo de nuevo.",
"AgeWhenGrabbed": "Antigüedad (cuando se añadió)",
"AllResultsAreHiddenByTheAppliedFilter": "Todos los resultados están ocultos por el filtro aplicado",
"AnalyseVideoFilesHelpText": "Extraer información de video como la resolución, el tiempo de ejecución y la información del códec de los archivos. Esto requiere que {appName} lea partes del archivo lo cual puede causar una alta actividad en el disco o en la red durante los escaneos.",
"AnalyseVideoFilesHelpText": "Extrae información de video como la resolución, el tiempo de ejecución y la información del códec de los archivos. Esto requiere que {appName} lea partes del archivo lo cual puede causar una alta actividad en el disco o en la red durante los escaneos.",
"AnimeEpisodeTypeDescription": "Episodios lanzados usando un número de episodio absoluto",
"ApiKeyValidationHealthCheckMessage": "Por favor actualiza tu clave API para que tenga de longitud al menos {length} caracteres. Puedes hacerlo en los ajustes o en el archivo de configuración",
"AppDataLocationHealthCheckMessage": "No será posible actualizar para evitar la eliminación de AppData al actualizar",
@@ -174,7 +174,7 @@
"Clone": "Clonar",
"Connections": "Conexiones",
"Dash": "Guion",
"AnalyticsEnabledHelpText": "Envíe información anónima de uso y error a los servidores de {appName}. Esto incluye información sobre su navegador, qué páginas de {appName} WebUI utiliza, informes de errores, así como el sistema operativo y la versión en tiempo de ejecución. Usaremos esta información para priorizar funciones y correcciones de errores.",
"AnalyticsEnabledHelpText": "Envía información anónima de uso y error a los servidores de {appName}. Esto incluye información sobre tu navegador, qué páginas de interfaz web de {appName} utilizas, informes de errores, así como el sistema operativo y la versión en tiempo de ejecución. Usaremos esta información para priorizar funciones y correcciones de errores.",
"BackupIntervalHelpText": "Intervalo entre copias de seguridad automáticas",
"BackupRetentionHelpText": "Las copias de seguridad automáticas anteriores al período de retención serán borradas automáticamente",
"AddNewSeries": "Añadir Nueva Serie",
@@ -182,7 +182,7 @@
"AddNewSeriesHelpText": "Es fácil añadir una nueva serie, empiece escribiendo el nombre de la serie que desea añadir.",
"AddNewSeriesRootFolderHelpText": "La subcarpeta '{folder}' será creada automáticamente",
"AddNewSeriesSearchForCutoffUnmetEpisodes": "Empezar la búsqueda de episodios con límites no alcanzados",
"AddNewSeriesSearchForMissingEpisodes": "Empezar búsqueda de episodios faltantes",
"AddNewSeriesSearchForMissingEpisodes": "Empezar la búsqueda de episodios faltantes",
"AddQualityProfile": "Añadir Perfil de Calidad",
"AddQualityProfileError": "No se pudo añadir un nuevo perfil de calidad, inténtelo de nuevo.",
"AddReleaseProfile": "Añadir perfil de lanzamiento",
@@ -199,7 +199,7 @@
"EditSelectedDownloadClients": "Editar Clientes de Descarga Seleccionados",
"DeleteRemotePathMappingMessageText": "¿Está seguro de querer eliminar esta asignación de ruta remota?",
"Implementation": "Implementación",
"ImportUsingScript": "Importar Script de Uso",
"ImportUsingScript": "Importar usando un script",
"CloneAutoTag": "Clonar Etiquetado Automático",
"ManageIndexers": "Gestionar Indexadores",
"DeleteAutoTag": "Eliminar Etiquetado Automático",
@@ -219,15 +219,15 @@
"AddConditionImplementation": "Añadir condición - {implementationName}",
"AppUpdated": "{appName} Actualizado",
"AutomaticUpdatesDisabledDocker": "Las actualizaciones automáticas no están soportadas directamente cuando se utiliza el mecanismo de actualización de Docker. Tendrá que actualizar la imagen del contenedor fuera de {appName} o utilizar un script",
"AuthenticationRequiredHelpText": "Cambiar para que las solicitudes requieran autenticación. No lo cambie a menos que entienda los riesgos.",
"AuthenticationRequiredWarning": "Para evitar el acceso remoto sin autenticación, {appName} ahora requiere que la autenticación esté habilitada. Opcionalmente puede desactivar la autenticación desde una dirección local.",
"AuthenticationRequiredPasswordHelpTextWarning": "Introduzca una nueva contraseña",
"AuthenticationRequiredUsernameHelpTextWarning": "Introduzca un nuevo nombre de usuario",
"AuthenticationRequiredHelpText": "Cambia para qué solicitudes se requiere autenticación. No cambiar a menos que entiendas los riesgos.",
"AuthenticationRequiredWarning": "Para evitar el acceso remoto sin autenticación, {appName} ahora requiere que la autenticación sea habilitada. Opcionalmente puedes deshabilitar la autenticación desde direcciones locales.",
"AuthenticationRequiredPasswordHelpTextWarning": "Introduce una nueva contraseña",
"AuthenticationRequiredUsernameHelpTextWarning": "Introduce un nuevo usuario",
"AuthenticationMethod": "Método de autenticación",
"AddConnectionImplementation": "Añadir Conexión - {implementationName}",
"AddDownloadClientImplementation": "Añadir Cliente de Descarga - {implementationName}",
"VideoDynamicRange": "Video de Rango Dinámico",
"AuthenticationMethodHelpTextWarning": "Por favor selecciona un método válido de autenticación",
"AuthenticationMethodHelpTextWarning": "Por favor selecciona un método de autenticación válido",
"AddCustomFilter": "Añadir Filtro Personalizado",
"BypassDelayIfAboveCustomFormatScoreMinimumScore": "Puntuación mínima de formato personalizado",
"CountIndexersSelected": "{count} indexador(es) seleccionado(s)",
@@ -266,12 +266,12 @@
"DeleteSelectedImportListsMessageText": "¿Estás seguro que quieres eliminar {count} lista(s) de importación seleccionada(s)?",
"DeletedReasonUpgrade": "Se ha borrado el archivo para importar una versión mejorada",
"DeleteTagMessageText": "¿Está seguro de querer eliminar la etiqueta '{label}'?",
"DisabledForLocalAddresses": "Deshabilitado para Direcciones Locales",
"DisabledForLocalAddresses": "Deshabilitada para direcciones locales",
"DeletedReasonManual": "El archivo fue eliminado usando {appName}, o bien manualmente o por otra herramienta a través de la API",
"ClearBlocklist": "Limpiar lista de bloqueos",
"AuthenticationRequiredPasswordConfirmationHelpTextWarning": "Confirma la nueva contraseña",
"MonitorPilotEpisode": "Episodio Piloto",
"MonitorRecentEpisodesDescription": "Monitorizar episodios emitidos en los últimos 90 días y los episodios futuros",
"MonitorRecentEpisodesDescription": "Monitoriza episodios emitidos en los últimos 90 días y los episodios futuros",
"MonitorSelected": "Monitorizar seleccionados",
"MonitorSeries": "Monitorizar Series",
"NoHistory": "Sin historial",
@@ -287,8 +287,8 @@
"History": "Historial",
"MonitorNoNewSeasonsDescription": "No monitorizar automáticamente ninguna temporada nueva",
"HistoryLoadError": "No se pudo cargar el historial",
"LibraryImport": "Importar Librería",
"RescanSeriesFolderAfterRefresh": "Re-escanear la Carpeta de Series tras Actualizar",
"LibraryImport": "Importar biblioteca",
"RescanSeriesFolderAfterRefresh": "Volver a escanear la carpeta de series tras actualizar",
"Wanted": "Buscado",
"MonitorPilotEpisodeDescription": "Sólo monitorizar el primer episodio de la primera temporada",
"MonitorRecentEpisodes": "Episodios Recientes",
@@ -300,14 +300,14 @@
"MonitorSpecialEpisodes": "Monitorizar Especiales",
"Queue": "Cola",
"RescanAfterRefreshHelpTextWarning": "{appName} no detectará automáticamente cambios en los archivos si no se elige 'Siempre'",
"RescanAfterRefreshSeriesHelpText": "Re-escanear la carpeta de series tras actualizar las series",
"RescanAfterRefreshSeriesHelpText": "Vuelve a escanear la carpeta de series tras actualizar las series",
"MonitorNoNewSeasons": "Sin Nuevas Temporadas",
"MonitorSpecialEpisodesDescription": "Monitorizar todos los episodios especiales sin cambiar el estado de monitorizado de otros episodios",
"MonitorSpecialEpisodesDescription": "Monitoriza todos los episodios especiales sin cambiar el estado de monitorizado de otros episodios",
"Calendar": "Calendario",
"BlocklistRelease": "Lista de bloqueos de lanzamiento",
"CountSeasons": "{count} Temporadas",
"BranchUpdate": "Rama a usar para actualizar {appName}",
"ChmodFolder": "Carpeta chmod",
"ChmodFolder": "chmod de la carpeta",
"CheckDownloadClientForDetails": "Revisar el cliente de descarga para más detalles",
"ChooseAnotherFolder": "Elige otra Carpeta",
"ClientPriority": "Prioridad del Cliente",
@@ -317,7 +317,7 @@
"CalendarLoadError": "Incapaz de cargar el calendario",
"CertificateValidation": "Validacion de certificado",
"BypassProxyForLocalAddresses": "Omitir Proxy para Direcciones Locales",
"ChangeFileDateHelpText": "Cambiar la fecha del archivo al importar/rescan",
"ChangeFileDateHelpText": "Cambia la fecha del archivo al importar/volver a escanear",
"ChownGroupHelpText": "Nombre del grupo o gid. Utilice gid para sistemas de archivos remotos.",
"CloneProfile": "Clonar Perfil",
"CollectionsLoadError": "No se han podido cargar las colecciones",
@@ -355,7 +355,7 @@
"Agenda": "Agenda",
"Cancel": "Cancelar",
"ChangeFileDate": "Cambiar fecha de archivo",
"CertificateValidationHelpText": "Cambiar como es la validacion de la certificacion estricta de HTTPS. No cambiar a menos que entiendas las consecuencias.",
"CertificateValidationHelpText": "Cambia cómo de estricta es la validación de certificación de HTTPS. No cambiar a menos que entiendas los riesgos.",
"AddListExclusion": "Añadir lista de exclusión",
"AddedDate": "Agregado: {date}",
"AllSeriesAreHiddenByTheAppliedFilter": "Todos los resultados estan ocultos por el filtro aplicado",
@@ -422,7 +422,7 @@
"FailedToFetchUpdates": "Fallo al buscar las actualizaciones",
"FailedToUpdateSettings": "Fallo al actualizar los ajustes",
"MaintenanceRelease": "Lanzamiento de mantenimiento: Corrección de errores y otras mejoras. Ver historial de commits de Github para mas detalle",
"CreateEmptySeriesFoldersHelpText": "Cree carpetas de series faltantes durante el análisis del disco",
"CreateEmptySeriesFoldersHelpText": "Crea carpetas de series faltantes durante el análisis del disco",
"DefaultCase": "Caso predeterminado",
"Daily": "Diario",
"CollapseMultipleEpisodesHelpText": "Contraer varios episodios que se emiten el mismo día",
@@ -465,7 +465,7 @@
"InteractiveImportNoQuality": "La calidad debe elegirse para cada archivo seleccionado",
"InteractiveSearchModalHeader": "Búsqueda interactiva",
"InvalidUILanguage": "Su interfaz de usuario está configurada en un idioma no válido, corríjalo y guarde la configuración",
"ChownGroup": "chown grupo",
"ChownGroup": "chown del grupo",
"DelayProfileProtocol": "Protocolo: {preferredProtocol}",
"DelayProfilesLoadError": "Incapaz de cargar Perfiles de Retardo",
"ContinuingSeriesDescription": "Se esperan más episodios u otra temporada",
@@ -598,7 +598,7 @@
"DownloadClientNzbgetValidationKeepHistoryZero": "La configuración KeepHistory de NzbGet debería ser mayor de 0",
"DownloadClientNzbgetValidationKeepHistoryZeroDetail": "La configuración KeepHistory de NzbGet está establecida a 0. Esto evita que {appName} vea las descargas completadas.",
"DownloadClientDownloadStationValidationSharedFolderMissing": "La carpeta compartida no existe",
"DownloadPropersAndRepacksHelpText": "Decidir si automáticamente actualizar a Propers/Repacks",
"DownloadPropersAndRepacksHelpText": "Actualiza automáticamente o no a Propers/Repacks",
"EditListExclusion": "Editar exclusión de lista",
"EnableAutomaticAdd": "Habilitar añadido automático",
"EditQualityProfile": "Editar perfil de calidad",
@@ -737,7 +737,7 @@
"DownloadClientQbittorrentSettingsInitialStateHelpText": "Estado inicial para los torrents añadidos a qBittorrent. Ten en cuenta que Forzar torrents no cumple las restricciones de semilla",
"DownloadClientQbittorrentSettingsSequentialOrderHelpText": "Descarga en orden secuencial (qBittorrent 4.1.0+)",
"DownloadClientDownloadStationValidationSharedFolderMissingDetail": "El Diskstation no tiene una carpeta compartida con el nombre '{sharedFolder}'. ¿Estás seguro que lo has especificado correctamente?",
"EnableCompletedDownloadHandlingHelpText": "Importar automáticamente las descargas completas del gestor de descargas",
"EnableCompletedDownloadHandlingHelpText": "Importa automáticamente las descargas completas del gestor de descargas",
"EnableInteractiveSearchHelpTextWarning": "Buscar no está soportado por este indexador",
"EnableRss": "Habilitar RSS",
"Ended": "Terminado",
@@ -783,7 +783,7 @@
"FilterDoesNotEndWith": "no termina en",
"Fixed": "Arreglado",
"Global": "Global",
"Enabled": "Habilitado",
"Enabled": "Habilitada",
"EpisodeHistoryLoadError": "No se puede cargar el historial del episodio",
"EpisodeIsDownloading": "El episodio se está descargando",
"EpisodeHasNotAired": "El episodio no está en emisión",
@@ -801,7 +801,7 @@
"GeneralSettings": "Opciones generales",
"GeneralSettingsLoadError": "No se pueden cargar las opciones generales",
"Grab": "Capturar",
"GrabReleaseUnknownSeriesOrEpisodeMessageText": "{appName} no pudo determinar para qué serie y episodio era este lanzamiento. {appName} no pudo automáticamente importar este lanzamiento. ¿Te gustaría capturar '{title}'?",
"GrabReleaseUnknownSeriesOrEpisodeMessageText": "{appName} no pudo determinar para qué serie y episodio era este lanzamiento. {appName} no pudo importar automáticamente este lanzamiento. ¿Te gustaría capturar '{title}'?",
"HasMissingSeason": "Tiene temporadas faltantes",
"ImportListSearchForMissingEpisodesHelpText": "Una vez se añada la serie a {appName}, buscar automáticamente episodios faltantes",
"ImportListsSonarrValidationInvalidUrl": "La URL de {appName} es inválida. ¿Te falta la URL base?",
@@ -989,7 +989,7 @@
"ImportListsValidationInvalidApiKey": "La clave API es inválida",
"ImportListsValidationTestFailed": "El test fue abortado debido a un error: {exceptionMessage}",
"ImportScriptPathHelpText": "La ruta al script a usar para importar",
"ImportUsingScriptHelpText": "Copiar archivos para importar usando un script (p. ej. para transcodificación)",
"ImportUsingScriptHelpText": "Copia archivos para importar usando un script (p. ej. para transcodificación)",
"Importing": "Importando",
"IncludeUnmonitored": "Incluir no monitorizadas",
"IndexerLongTermStatusAllUnavailableHealthCheckMessage": "Ningún indexador está disponible debido a errores durante más de 6 horas",
@@ -1040,7 +1040,7 @@
"ImportMechanismHandlingDisabledHealthCheckMessage": "Habilitar Gestión de descargas completadas",
"ImportedTo": "Importar a",
"IncludeCustomFormatWhenRenaming": "Incluir formato personalizado cuando se renombra",
"CleanLibraryLevel": "Limpiar el nivel de la librería",
"CleanLibraryLevel": "Limpiar nivel de biblioteca",
"SearchForCutoffUnmetEpisodes": "Buscar todos los episodios con límites no alcanzados",
"IconForSpecials": "Icono para Especiales",
"ImportListExclusions": "Importar lista de exclusiones",
@@ -1067,7 +1067,7 @@
"IndexerSearchNoAutomaticHealthCheckMessage": "No hay indexadores disponibles con Búsqueda Automática activada, {appName} no proporcionará ningún resultado de búsquedas automáticas",
"IndexerSearchNoAvailableIndexersHealthCheckMessage": "Todos los indexadores con capacidad de búsqueda no están disponibles temporalmente debido a errores recientes del indexadores",
"IndexerSearchNoInteractiveHealthCheckMessage": "No hay indexadores disponibles con Búsqueda Interactiva activada, {appName} no proporcionará ningún resultado de búsquedas interactivas",
"PasswordConfirmation": "Confirmación de Contraseña",
"PasswordConfirmation": "Confirmación de contraseña",
"IndexerSettingsAdditionalParameters": "Parámetros adicionales",
"IndexerSettingsAllowZeroSizeHelpText": "Activar esta opción le permitirá utilizar fuentes que no especifiquen el tamaño del lanzamiento, pero tenga cuidado, no se realizarán comprobaciones relacionadas con el tamaño.",
"IndexerSettingsAllowZeroSize": "Permitir Tamaño Cero",
@@ -1189,7 +1189,7 @@
"ListSyncTag": "Etiqueta de Sincronización de Lista",
"ListSyncTagHelpText": "Esta etiqueta se añadirá cuando una serie desaparezca o ya no esté en su(s) lista(s)",
"MetadataLoadError": "No se puede cargar Metadatos",
"MetadataSourceSettingsSeriesSummary": "Información de dónde {appName} obtiene información de series y episodio",
"MetadataSourceSettingsSeriesSummary": "Fuente de información de donde {appName} obtiene información de series y episodios",
"Max": "Máximo",
"MaximumSizeHelpText": "Tamaño máximo en MB para que un lanzamiento sea capturado. Establece a cero para establecer a ilimitado",
"MatchedToEpisodes": "Ajustado a Episodios",
@@ -1486,7 +1486,7 @@
"RemoveQueueItemConfirmation": "¿Estás seguro que quieres eliminar '{sourceTitle}' de la cola?",
"RemoveRootFolder": "Eliminar la carpeta raíz",
"RemoveSelectedItem": "Eliminar elemento seleccionado",
"RemoveTagsAutomaticallyHelpText": "Eliminar etiquetas automáticamente si las condiciones no se cumplen",
"RemoveTagsAutomaticallyHelpText": "Elimina etiquetas automáticamente si las condiciones no se cumplen",
"RemovedFromTaskQueue": "Eliminar de la cola de tareas",
"RemovedSeriesMultipleRemovedHealthCheckMessage": "Las series {series} fueron eliminadas de TheTVDB",
"RenameFiles": "Renombrar archivos",
@@ -1595,7 +1595,7 @@
"TestAllIndexers": "Probar todos los indexadores",
"TestAllLists": "Probar todas las listas",
"TestParsing": "Probar análisis",
"ThemeHelpText": "Cambiar el tema de la interfaz de la aplicación, el tema 'Auto' usará el tema de tu sistema para establecer el modo luminoso u oscuro. Inspirado por Theme.Park",
"ThemeHelpText": "Cambia el tema de la interfaz de la aplicación, el tema 'Auto' usará el tema de tu sistema para establecer el modo luminoso u oscuro. Inspirado por Theme.Park",
"TimeLeft": "Tiempo restante",
"ToggleMonitoredSeriesUnmonitored ": "No se puede conmutar el estado monitorizado cuando la serie no está monitorizada",
"Tomorrow": "Mañana",
@@ -1621,7 +1621,7 @@
"Upcoming": "Próximamente",
"UpcomingSeriesDescription": "Series que han sido anunciadas pero aún no hay fecha de emisión exacta",
"ReleaseSceneIndicatorUnknownSeries": "Episodio o serie desconocido.",
"RemoveDownloadsAlert": "Las opciones de Eliminar fueron movidas a las opciones del cliente de descarga individual en la table anterior.",
"RemoveDownloadsAlert": "Las opciones de eliminación fueron trasladadas a las opciones del cliente de descarga individual en la tabla anterior.",
"RestartRequiredHelpTextWarning": "Requiere reiniciar para que tenga efecto",
"SelectFolder": "Seleccionar carpeta",
"TestAllClients": "Probar todos los clientes",
@@ -1894,7 +1894,7 @@
"UiLanguage": "Idioma de interfaz",
"UiLanguageHelpText": "Idioma que {appName} usará en la interfaz",
"UiSettingsSummary": "Opciones de calendario, fecha y color alterado",
"UpdateAutomaticallyHelpText": "Descargar e instalar actualizaciones automáticamente. Todavía puedes instalar desde Sistema: Actualizaciones",
"UpdateAutomaticallyHelpText": "Descarga e instala actualizaciones automáticamente. Podrás seguir instalándolas desde Sistema: Actualizaciones",
"TotalRecords": "Total de registros: {totalRecords}",
"WantMoreControlAddACustomFormat": "¿Quieres más control sobre qué descargas son preferidas? Añade un [formato personalizado](/opciones/formatospersonalizados)",
"OrganizeModalHeader": "Organizar y renombrar",
@@ -1916,7 +1916,7 @@
"SelectEpisodesModalTitle": "{modalTitle} - Seleccionar episodio(s)",
"DownloadClientDelugeSettingsDirectory": "Directorio de descarga",
"DownloadClientDelugeSettingsDirectoryHelpText": "Ubicación opcional en la que poner las descargas, dejar en blanco para usar la ubicación predeterminada de Deluge",
"UnmonitorSpecialsEpisodesDescription": "Dejar de monitorizar todos los episodios especiales sin cambiar el estado monitorizado de otros episodios",
"UnmonitorSpecialsEpisodesDescription": "Deja de monitorizar todos los episodios especiales sin cambiar el estado monitorizado de otros episodios",
"DownloadClientDelugeSettingsDirectoryCompletedHelpText": "Ubicación opcional a la que mover las descargas completadas, dejar en blanco para usar la ubicación predeterminada de Deluge",
"DownloadClientDelugeSettingsDirectoryCompleted": "Directorio al que mover cuando se complete",
"NotificationsDiscordSettingsWebhookUrlHelpText": "URL de canal webhook de Discord",
@@ -2078,5 +2078,8 @@
"TomorrowAt": "Mañana a las {time}",
"YesterdayAt": "Ayer a las {time}",
"TodayAt": "Hoy a las {time}",
"DayOfWeekAt": "{day} a las {time}"
"DayOfWeekAt": "{day} a las {time}",
"UnableToImportAutomatically": "No se pudo importar automáticamente",
"NotificationsPlexSettingsServer": "Servidor",
"NotificationsPlexSettingsServerHelpText": "Selecciona el servidor desde una cuenta de plex.tv después de autenticarse"
}

View File

@@ -299,7 +299,7 @@
"SkipFreeSpaceCheck": "Ignorer la vérification de l'espace libre",
"Sunday": "Dimanche",
"TorrentDelay": "Retard du torrent",
"DownloadClients": "Clients de téléchargement",
"DownloadClients": "Clients de télécharg.",
"CustomFormats": "Formats perso.",
"NoIndexersFound": "Aucun indexeur n'a été trouvé",
"Profiles": "Profils",
@@ -550,7 +550,7 @@
"LastWriteTime": "Heure de la dernière écriture",
"LatestSeason": "Dernière saison",
"LibraryImportTipsDontUseDownloadsFolder": "Ne l'utilisez pas pour importer des téléchargements à partir de votre client de téléchargement, cela concerne uniquement les bibliothèques organisées existantes, pas les fichiers non triés.",
"ListWillRefreshEveryInterval": "La liste sera actualisée tous les {refreshInterval}",
"ListWillRefreshEveryInterval": "La liste se rafraîchira toutes les {refreshInterval}",
"ListsLoadError": "Impossible de charger les listes",
"Local": "Locale",
"LocalPath": "Chemin local",
@@ -616,7 +616,7 @@
"OnSeriesDelete": "Lors de la suppression de la série",
"OnlyTorrent": "Uniquement Torrent",
"OpenBrowserOnStart": "Ouvrir le navigateur au démarrage",
"OpenSeries": "Série ouverte",
"OpenSeries": "Ouvrir la série",
"Options": "Options",
"Organize": "Organiser",
"OrganizeLoadError": "Erreur lors du chargement des aperçus",
@@ -849,7 +849,7 @@
"MinimumFreeSpaceHelpText": "Empêcher l'importation si elle laisse moins d'espace disque disponible",
"MinimumLimits": "Limites minimales",
"MinutesFortyFive": "45 Minutes: {fortyFive}",
"Monitor": "Surveillé",
"Monitor": "Surveiller",
"MonitorAllEpisodesDescription": "Surveillez tous les épisodes sauf les spéciaux",
"MonitorExistingEpisodes": "Épisodes existants",
"MonitorExistingEpisodesDescription": "Surveiller les épisodes contenant des fichiers ou qui n'ont pas encore été diffusés",
@@ -1131,7 +1131,7 @@
"NotSeasonPack": "Pas de pack saisonnier",
"NotificationTriggersHelpText": "Sélectionnez les événements qui doivent déclencher cette notification",
"NotificationsTagsSeriesHelpText": "N'envoyer des notifications que pour les séries avec au moins une balise correspondante",
"OnApplicationUpdate": "Sur la mise à jour de l'application",
"OnApplicationUpdate": "Lors de la mise à jour de l'application",
"OnEpisodeFileDelete": "Lors de la suppression du fichier de l'épisode",
"OnHealthIssue": "Sur la question de la santé",
"OnManualInteractionRequired": "Sur l'interaction manuelle requise",
@@ -1344,7 +1344,7 @@
"DeleteDelayProfile": "Supprimer le profil de retard",
"DeleteDelayProfileMessageText": "Êtes-vous sûr de vouloir supprimer ce profil de retard ?",
"DeleteEpisodeFile": "Supprimer le fichier de l'épisode",
"DeleteEpisodeFileMessage": "Supprimer le fichier de l'épisode '{path}'?",
"DeleteEpisodeFileMessage": "Êtes-vous sûr de vouloir supprimer « {path} » ?",
"DeleteEpisodeFromDisk": "Supprimer l'épisode du disque",
"DeleteImportListMessageText": "Êtes-vous sûr de vouloir supprimer la liste « {name} » ?",
"DeleteSelectedEpisodeFiles": "Supprimer les fichiers d'épisode sélectionnés",
@@ -1779,7 +1779,7 @@
"NotificationsPushoverSettingsDevicesHelpText": "Liste des noms des appareils (laisser vide pour envoyer à tous les appareils)",
"NotificationsPushoverSettingsDevices": "Appareils",
"NotificationsPushcutSettingsTimeSensitiveHelpText": "Activer pour marquer la notification comme « Time Sensitive »",
"NotificationsPushcutSettingsTimeSensitive": "Time Sensitive",
"NotificationsPushcutSettingsTimeSensitive": "Sensible au temps",
"NotificationsPushcutSettingsNotificationNameHelpText": "Nom de la notification de l'onglet Notifications de l'app Pushcut",
"NotificationsPushcutSettingsNotificationName": "Nom de la notification",
"NotificationsPushcutSettingsApiKeyHelpText": "Les clés API peuvent être gérées dans la vue Compte de l'app Pushcut",
@@ -2071,5 +2071,13 @@
"NotificationsTelegramSettingsIncludeAppName": "Inclure {appName} dans le Titre",
"NotificationsTelegramSettingsIncludeAppNameHelpText": "Préfixer éventuellement le titre du message par {appName} pour différencier les notifications des différentes applications",
"IndexerSettingsMultiLanguageRelease": "Multilingue",
"IndexerSettingsMultiLanguageReleaseHelpText": "Quelles langues sont normalement présentes dans une version multiple de l'indexeur ?"
"IndexerSettingsMultiLanguageReleaseHelpText": "Quelles langues sont normalement présentes dans une version multiple de l'indexeur ?",
"DownloadClientQbittorrentTorrentStateMissingFiles": "qBittorrent signale des fichiers manquants",
"BlocklistFilterHasNoItems": "La liste de blocage sélectionnée ne contient aucun élément",
"HasUnmonitoredSeason": "A une saison non surveillée",
"YesterdayAt": "Hier à {time}",
"UnableToImportAutomatically": "Impossible d'importer automatiquement",
"DayOfWeekAt": "{day} à {time}",
"TomorrowAt": "Demain à {time}",
"TodayAt": "Aujourd'hui à {time}"
}

View File

@@ -1 +1,4 @@
{}
{
"About": "के बारे में",
"Absolute": "पूर्ण"
}

View File

@@ -20,7 +20,7 @@
"ApplyChanges": "Applica Cambiamenti",
"ApplyTags": "Applica Etichette",
"BackupNow": "Esegui backup ora",
"Backups": "Backups",
"Backups": "Backup",
"Blocklist": "Lista dei Blocchi",
"Activity": "Attività",
"About": "Info",
@@ -59,12 +59,12 @@
"AppUpdated": "{appName} Aggiornato",
"AppUpdatedVersion": "{appName} è stato aggiornato alla versione `{version}`, per vedere le modifiche devi ricaricare {appName} ",
"ApplicationURL": "URL Applicazione",
"AuthenticationMethodHelpText": "Inserisci Username e Password per accedere a {appName}",
"AuthenticationMethodHelpText": "Utilizza nome utente e password per accedere a {appName}",
"BindAddressHelpText": "Indirizzi IP validi, localhost o '*' per tutte le interfacce",
"BeforeUpdate": "Prima dell'aggiornamento",
"CalendarFeed": "Feed calendario {appName}",
"CalendarOptions": "Opzioni del Calendario",
"ChooseImportMode": "Selezionare Metodo di Importazione",
"ChooseImportMode": "Seleziona Metodo di Importazione",
"CollapseMultipleEpisodes": "Collassa Episodi Multipli",
"Conditions": "Condizioni",
"Continuing": "In Corso",
@@ -147,7 +147,7 @@
"CreateGroup": "Crea gruppo",
"DeleteEmptyFolders": "Cancella le cartelle vuote",
"Enabled": "Abilitato",
"UpdateMechanismHelpText": "Usa il sistema di aggiornamento interno di {appName} o uno script",
"UpdateMechanismHelpText": "Usa il sistema di aggiornamento incorporato di {appName} o uno script",
"AllResultsAreHiddenByTheAppliedFilter": "Tutti i risultati sono nascosti dal filtro applicato",
"EditSelectedDownloadClients": "Modifica i Client di Download Selezionati",
"EditSelectedImportLists": "Modifica le Liste di Importazione Selezionate",
@@ -250,5 +250,618 @@
"AutoRedownloadFailed": "Download fallito",
"AddDelayProfileError": "Impossibile aggiungere un nuovo profilo di ritardo, riprova.",
"Cutoff": "Taglio",
"AddListExclusion": "Aggiungi elenco esclusioni"
"AddListExclusion": "Aggiungi Lista esclusioni",
"DownloadClientValidationApiKeyRequired": "API Key Richiesta",
"Donate": "Dona",
"DownloadClientDownloadStationValidationNoDefaultDestination": "Nessuna destinazione predefinita",
"ImportListSettings": "Impostazioni delle Liste",
"DownloadClientFreeboxSettingsAppId": "ID App",
"DownloadClientFreeboxSettingsAppToken": "Token App",
"DownloadClientPneumaticSettingsNzbFolderHelpText": "Questa cartella dovrà essere raggiungibile da XBMC",
"DownloadClientPneumaticSettingsNzbFolder": "Cartella Nzb",
"DownloadClientSabnzbdValidationEnableDisableDateSorting": "Disattiva ordinamento per data",
"DownloadClientQbittorrentValidationCategoryUnsupported": "Categoria non è supportata",
"DownloadClientValidationCategoryMissing": "Categoria non esiste",
"DownloadClientRTorrentSettingsUrlPath": "Percorso Url",
"Default": "Predefinito",
"DownloadClientFreeboxSettingsApiUrl": "API URL",
"DownloadClientQbittorrentValidationCategoryRecommended": "Categoria è raccomandata",
"Discord": "Discord",
"DownloadClientDownloadStationValidationFolderMissing": "Cartella non esiste",
"DownloadClientValidationAuthenticationFailure": "Autenticazione Fallita",
"DownloadClientDownloadStationValidationFolderMissingDetail": "La cartella '{downloadDir}' non esiste, deve essere creata manualmente all'interno della Cartella Condivisa '{sharedFolder}'.",
"DownloadClientSabnzbdValidationUnknownVersion": "Versione sconosciuta: {rawVersion}",
"DownloadClientValidationVerifySsl": "Verifica impostazioni SSL",
"ChangeCategory": "Cambia Categoria",
"DownloadClientPneumaticSettingsStrmFolder": "Cartella Strm",
"Destination": "Destinazione",
"DownloadClientDownloadStationValidationSharedFolderMissing": "Cartella condivisa non esiste",
"DownloadClientQbittorrentValidationCategoryRecommendedDetail": "{appName} non tenterà di importare i download completati senza una categoria.",
"DownloadClientValidationGroupMissing": "Gruppo non esistente",
"DownloadWarning": "Avviso di download: {warningMessage}",
"IndexerSettingsAdditionalParameters": "Parametri Addizionali",
"IndexerSettingsCookie": "Cookie",
"BlackholeWatchFolderHelpText": "Cartella da cui {appName} dovrebbe importare i download completati",
"NotificationsEmailSettingsServer": "Server",
"NotificationsNtfySettingsPasswordHelpText": "Password opzionale",
"NotificationsPlexSettingsAuthenticateWithPlexTv": "Autentica con Plex.tv",
"NotificationsPushcutSettingsNotificationName": "Nome Notifica",
"NotificationsTraktSettingsExpires": "Scadenze",
"NotificationsTraktSettingsAuthenticateWithTrakt": "Autentica con Trakt",
"NotificationsValidationUnableToConnectToApi": "Impossibile connettersi alle API di {service}. Connessione al server fallita: ({responseCode}) {exceptionMessage}",
"InteractiveImportNoFilesFound": "Nessun video trovato nella castella selezionata",
"Or": "o",
"ManageLists": "Gestisci Liste",
"OriginalLanguage": "Lingua Originale",
"OverrideGrabNoQuality": "Qualità deve essere selezionata",
"XmlRpcPath": "Percorso XML RPC",
"WouldYouLikeToRestoreBackup": "Vuoi ripristinare il backup '{name}'?",
"PendingDownloadClientUnavailable": "In Attesa - Client di Download in attesa",
"PreviouslyInstalled": "Precedentemente Installato",
"MissingLoadError": "Errore caricando elementi mancanti",
"MonitorSelected": "Monitora Selezionati",
"Period": "Periodo",
"RemoveFailedDownloads": "Rimuovi Download Falliti",
"RemoveMultipleFromDownloadClientHint": "Rimuovi i download e i file dal client di download",
"RemoveSelectedItemQueueMessageText": "Sei sicuro di voler rimuovere 1 elemento dalla coda?",
"TagDetails": "Dettagli Etichetta - {label}",
"BranchUpdate": "Branca da usare per aggiornare {appName}",
"DefaultNotFoundMessage": "Ti devi essere perso, non c'è nulla da vedere qui.",
"DeleteIndexerMessageText": "Sicuro di voler eliminare l'indicizzatore '{name}'?",
"Socks5": "Socks5 (Supporto TOR)",
"DeleteEpisodeFileMessage": "Sei sicuro di volere eliminare '{path}'?",
"NotificationsKodiSettingsCleanLibraryHelpText": "Pulisci libreria dopo l'aggiornamento",
"PreferProtocol": "Preferisci {preferredProtocol}",
"RetryingDownloadOn": "Riprovando il download il {date} alle {time}",
"DeleteNotificationMessageText": "Sei sicuro di voler eliminare la notifica '{name}'?",
"RemoveCompletedDownloads": "Rimuovi Download Completati",
"DownloadClientFloodSettingsAdditionalTags": "Tag addizionali",
"DelayingDownloadUntil": "Ritardare il download fino al {date} alle {time}",
"DeleteDownloadClientMessageText": "Sei sicuro di voler eliminare il client di download '{name}'?",
"NoHistoryFound": "Nessun storico trovato",
"OneMinute": "1 Minuto",
"OptionalName": "Nome opzionale",
"DeleteSelectedIndexers": "Elimina Indicizzatore/i",
"Branch": "Branca",
"Debug": "Debug",
"Never": "Mai",
"UsenetDelayTime": "Ritardo Usenet: {usenetDelay}",
"OrganizeModalHeader": "Organizza & Rinomina",
"Parse": "Analizza",
"RemoveFromDownloadClient": "Rimuovi dal client di download",
"RemoveQueueItemConfirmation": "Sei sicuro di voler rimuovere '{sourceTitle}' dalla coda?",
"NoIndexersFound": "Nessun indicizzatore trovato",
"DeleteImportListMessageText": "Sei sicuro di volere eliminare la lista '{name}'?",
"DeleteDelayProfile": "Elimina Profilo di Ritardo",
"DeleteTagMessageText": "Sei sicuro di voler eliminare l'etichetta '{label}'?",
"MinutesSixty": "60 Minuti: {sixty}",
"NotificationsCustomScriptSettingsName": "Script personalizzato",
"NotificationsCustomScriptValidationFileDoesNotExist": "File non esiste",
"Database": "Database",
"NotificationsPushoverSettingsExpire": "Scadenza",
"NotificationsSettingsWebhookMethod": "Metodo",
"NotificationsSynologyValidationInvalidOs": "Deve essere un Synology",
"NotificationsTraktSettingsRefreshToken": "Refresh Token",
"CouldNotFindResults": "Nessun risultato trovato per '{term}'",
"IndexerSettingsApiPath": "Percorso API",
"AutoTaggingSpecificationMaximumYear": "Anno Massimo",
"AutoTaggingSpecificationGenre": "Genere/i",
"AutoTaggingSpecificationMinimumYear": "Anno Minimo",
"AutoTaggingSpecificationOriginalLanguage": "Lingua",
"AutoTaggingSpecificationQualityProfile": "Profilo Qualità",
"AutoTaggingSpecificationRootFolder": "Cartella Radice",
"AutoTaggingSpecificationStatus": "Stato",
"CustomFormatsSpecificationLanguage": "Linguaggio",
"CustomFormatsSpecificationMaximumSize": "Dimensione Massima",
"CustomFormatsSpecificationMinimumSize": "Dimensione Minima",
"DelayProfile": "Profilo di Ritardo",
"DeleteBackupMessageText": "Sei sicuro di voler cancellare il backup '{name}'?",
"DeleteDelayProfileMessageText": "Sei sicuro di volere eliminare questo profilo di ritardo?",
"NotificationsTelegramSettingsSendSilentlyHelpText": "Invia il messaggio silenziosamente. L'utente riceverà una notifica senza suono",
"NotificationsPushoverSettingsRetry": "Riprova",
"NotificationsSettingsWebhookUrl": "URL Webhook",
"PackageVersionInfo": "{packageVersion} di {packageAuthor}",
"RemoveQueueItem": "Rimuovi - {sourceTitle}",
"ParseModalErrorParsing": "Errore durante l'analisi, per favore prova di nuovo.",
"OverrideGrabNoLanguage": "Almeno una lingua deve essere selezionata",
"PasswordConfirmation": "Conferma Password",
"RemoveSelectedItemsQueueMessageText": "Sei sicuro di voler rimuovere {selectedCount} elementi dalla coda?",
"ConnectionLostToBackend": "{appName} ha perso la connessione al backend e dovrà essere ricaricato per ripristinare la funzionalità.",
"CountIndexersSelected": "{count} indicizzatore(i) selezionato(i)",
"CountDownloadClientsSelected": "{count} client di download selezionato/i",
"PrioritySettings": "Priorità: {priority}",
"OverrideAndAddToDownloadQueue": "Sovrascrivi e aggiungi alla coda di download",
"NotificationsSettingsUseSslHelpText": "Connetti a {serviceName} tramite HTTPS indece di HTTP",
"OrganizeRelativePaths": "Tutti i percorsi sono relativi a: `{path}`",
"CurrentlyInstalled": "Attualmente Installato",
"NotificationsEmailSettingsName": "Email",
"NotificationsNtfySettingsServerUrl": "URL Server",
"NotificationsPushoverSettingsSound": "Suono",
"NotificationsSignalValidationSslRequired": "SSL sembra essere richiesto",
"TorrentDelayTime": "Ritardo torrent: {torrentDelay}",
"NotificationsTwitterSettingsMention": "Menziona",
"NotificationsPushoverSettingsDevices": "Dispositivi",
"NotificationsTelegramSettingsSendSilently": "Invia Silenziosamente",
"DatabaseMigration": "Migrazione Database",
"AutoTaggingSpecificationTag": "Etichetta",
"CustomFormatUnknownConditionOption": "Opzione sconosciuta '{key}' per la condizione '{implementation}'",
"CustomFormatsSpecificationResolution": "Risoluzione",
"CustomFormatsSpecificationSource": "Fonte",
"BlocklistAndSearch": "Lista dei Blocchi e Ricerca",
"NotificationsEmbySettingsSendNotifications": "Invia Notifiche",
"IndexerHDBitsSettingsMediumsHelpText": "Se non specificato, saranno utilizzate tutte le opzioni.",
"DeleteQualityProfile": "Elimina Profilo Qualità",
"DeleteSelectedEpisodeFiles": "Elimina i File degli Episodi Selezionati",
"DeleteEpisodesFiles": "Elimina i File di {episodeFileCount} Episodi",
"CustomFilter": "Filtro Personalizzato",
"NotificationsTelegramSettingsIncludeAppName": "Includi {appName} nel Titolo",
"IndexerHDBitsSettingsCodecsHelpText": "Se non specificato, saranno utilizzate tutte le opzioni.",
"NotificationsGotifySettingsAppToken": "App Token",
"InfoUrl": "URL Info",
"ConnectionLostReconnect": "{appName} cercherà di connettersi automaticamente, oppure clicca su ricarica qui sotto.",
"ListWillRefreshEveryInterval": "Le liste verranno aggiornate ogni {refreshInterval}",
"NotificationsNtfySettingsServerUrlHelpText": "Lascia vuoto per usare il server pubblico {url}",
"NotificationsTwitterSettingsMentionHelpText": "Menziona questo utente nei tweet inviati",
"NotificationsValidationUnableToSendTestMessageApiResponse": "Impossibile inviare messaggio di prova. Risposta dalle API: {error}",
"RemoveFromDownloadClientHint": "Rimuovi il download e i file dal client di download",
"DayOfWeekAt": "{day} alle {time}",
"DeleteRootFolder": "Elimina Cartella Radice",
"DeleteRootFolderMessageText": "Sei sicuro di volere eliminare la cartella radice '{path}'?",
"ManageIndexers": "Gestisci Indicizzatori",
"MissingNoItems": "Nessun elemento mancante",
"NotificationsKodiSettingsCleanLibrary": "Pulisci Libreria",
"NotificationsNtfySettingsUsernameHelpText": "Nome utente opzionale",
"NotificationsSettingsUpdateLibrary": "Aggiorna Libreria",
"NotificationsSlackSettingsChannel": "Canale",
"NotificationsSlackSettingsIcon": "Icona",
"NotificationsTelegramSettingsBotToken": "Token Bot",
"NotificationsTwitterSettingsAccessToken": "Access Token",
"NotificationsTwitterSettingsConnectToTwitter": "Connetti a Twitter / X",
"NotificationsTwitterSettingsDirectMessage": "Messaggio Diretto",
"NotificationsValidationInvalidUsernamePassword": "Nome Utente o password non validi",
"NotificationsValidationUnableToConnect": "Impossibile connettersi: {exceptionMessage}",
"NotificationsValidationUnableToConnectToService": "Impossibile connettersi a {serviceName}",
"NotificationsValidationUnableToSendTestMessage": "Impossibile inviare messaggio di prova: {exceptionMessage}",
"ThemeHelpText": "Cambia il Tema dell'interfaccia dellapplicazione, il Tema 'Auto' userà il suo Tema di Sistema per impostare la modalità Chiara o Scura. Ispirato da Theme.Park",
"Torrents": "Torrents",
"Upcoming": "In arrivo",
"DownloadClientUTorrentTorrentStateError": "uTorrent sta segnalando un errore",
"DownloadClientValidationSslConnectFailure": "Impossibile connettersi tramite SSL",
"ErrorLoadingContent": "Si è verificato un errore caricando questo contenuto",
"FilterDoesNotStartWith": "non inizia con",
"Filters": "Filtri",
"IgnoreDownloads": "Ignora Download",
"IndexerSettingsApiPathHelpText": "Percorso API, solitamente {url}",
"BlackholeFolderHelpText": "Cartella nella quale {appName} salverà i file di tipo {extension}",
"UseSeasonFolder": "Usa Cartella Stagione",
"Monday": "Lunedì",
"DetailedProgressBarHelpText": "Mostra testo sulla barra di avanzamento",
"DownloadClientValidationUnknownException": "Eccezione sconosciuta: {exception}",
"OnlyTorrent": "Solo Torrent",
"OpenBrowserOnStart": "Apri browser all'avvio",
"OnlyUsenet": "Solo Usenet",
"OpenSeries": "Apri Serie",
"Organize": "Organizza",
"Other": "Altri",
"Yesterday": "Ieri",
"Paused": "In Pausa",
"Priority": "Priorità",
"Metadata": "Metadati",
"Quality": "Qualità",
"MidseasonFinale": "Finale di Metà Stagione",
"QualityProfile": "Profilo Qualità",
"MinimumAge": "Età Minima",
"ShowAdvanced": "Mostra Avanzate",
"MonitorFirstSeason": "Prima Stagione",
"MonitoredOnly": "Solo Monitorati",
"MoreInfo": "Ulteriori Informazioni",
"FormatRuntimeMinutes": "{minutes}m",
"Options": "Opzioni",
"PartialSeason": "Stagione Parziale",
"Port": "Porta",
"PreferTorrent": "Preferisci Torrent",
"Reset": "Reimposta",
"RssIsNotSupportedWithThisIndexer": "RSS non è supportato con questo indicizzatore",
"PublishedDate": "Data Pubblicazione",
"SeriesDetailsGoTo": "Vai a {title}",
"Security": "Sicurezza",
"Settings": "Impostazioni",
"ShowDateAdded": "Mostra Data Aggiunta",
"Reason": "Ragione",
"RecyclingBin": "Cestino",
"SpecialEpisode": "Episodio Speciale",
"Space": "Spazio",
"SslPort": "Porta SSL",
"StartImport": "Inizia Importazione",
"Test": "Prova",
"Titles": "Titoli",
"Result": "Risultato",
"Unavailable": "Non disponibile",
"SearchSelected": "Ricerca Selezionate",
"SeasonCount": "Conteggio Stagioni",
"Season": "Stagione",
"SeriesType": "Tipo Serie",
"ShowNetwork": "Mostra Rete",
"MoveSeriesFoldersMoveFiles": "Sì, Sposta i File",
"Negated": "Negato",
"SizeOnDisk": "Dimensione sul disco",
"Sort": "Ordina",
"FilterSeriesPlaceholder": "Filtra serie",
"FormatShortTimeSpanHours": "{hours} ora/e",
"Monitoring": "Monitorando",
"Month": "Mese",
"MonitoredEpisodesHelpText": "Scarica gli episodi monitorati in questa serie",
"NoChanges": "Nessun Cambiamento",
"NotificationsNtfyValidationAuthorizationRequired": "Autorizzazione richiesta",
"PreferredProtocol": "Protocollo Preferito",
"QualitiesHelpText": "Qualità più alte nella lista sono quelle preferite. Qualità all'interno dello stesso gruppo sono equivalenti. Solo le qualità selezionate saranno ricercate",
"Real": "Reale",
"RefreshAndScanTooltip": "Aggiorna informazioni e scansiona disco",
"SeriesIndexFooterMissingMonitored": "Episodi Mancanti (Serie monitorate)",
"SingleEpisode": "Episodio Singolo",
"Shutdown": "Spegnimento",
"Wiki": "Wiki",
"True": "Vero",
"WhatsNew": "Cosa c'è di nuovo?",
"SelectQuality": "Seleziona Qualità",
"SelectLanguages": "Seleziona Lingue",
"TableOptions": "Opzioni Tabella",
"Tba": "TBA",
"TablePageSizeMinimum": "La dimensione della pagina deve essere almeno {minimumValue}",
"Theme": "Tema",
"Updates": "Aggiornamenti",
"VersionNumber": "Versione {version}",
"EditSelectedIndexers": "Modifica Indicizzatori Selezionati",
"Example": "Esempio",
"FilterContains": "contiene",
"FilterDoesNotContain": "non contiene",
"FilterIsAfter": "è dopo",
"FilterIsBefore": "è prima",
"FilterIs": "è",
"FilterLessThanOrEqual": "meno o uguale di",
"FilterNotEqual": "non uguale",
"DownloadClientFreeboxApiError": "Le API di Freebox hanno segnalato un errore: {errorDescription}",
"DailyEpisodeFormat": "Formato Episodi Giornalieri",
"FileManagement": "Gestione File",
"MissingEpisodes": "Episodi Mancanti",
"Mode": "Modalità",
"MonitorAllEpisodesDescription": "Monitora tutti gli episodi esclusi gli speciali",
"MonitorAllEpisodes": "Tutti gli Episodi",
"MonitorExistingEpisodes": "Episodi Esistenti",
"Preferred": "Preferito",
"Scene": "Scene",
"SizeLimit": "Limite Dimensione",
"Twitter": "Twitter",
"Type": "Tipo",
"DownloadClientNzbgetSettingsAddPausedHelpText": "Questa opzione richiede almeno la versione 16.0 di NzbGet",
"System": "Sistema",
"CollectionsLoadError": "Impossibile caricare le collezioni",
"ConnectSettingsSummary": "Notifiche, connessioni a media servers/players, e script personalizzati",
"Folders": "Cartelle",
"PreferredSize": "Dimensione Preferita",
"Proxy": "Proxy",
"DownloadClientQbittorrentSettingsUseSslHelpText": "Usa una connessione sicura. Vedi Opzioni -> Web UI -> 'Usa HTTPS invece di HTTP' in qBittorrent.",
"DownloadClientQbittorrentTorrentStateError": "qBittorrent sta segnalando un errore",
"TimeLeft": "Tempo Rimasto",
"Unlimited": "Illimitato",
"Title": "Titolo",
"Original": "Originale",
"Path": "Percorso",
"MaximumSize": "Dimensione Massima",
"ProgressBarProgress": "Barra Progressi al {progress}%",
"RootFolderSelectFreeSpace": "{freeSpace} Libero",
"RssSync": "Sincronizza RSS",
"SaveSettings": "Salva Impostazioni",
"Search": "Ricerca",
"SearchForMissing": "Ricerca dei Mancanti",
"SearchForMonitoredEpisodes": "Ricerca degli episodi monitorati",
"SeasonFolder": "Cartella della Stagione",
"SeasonNumber": "Numero Stagione",
"SelectLanguageModalTitle": "{modalTitle} - Seleziona Lingua",
"SeriesDetailsRuntime": "{runtime} Minuti",
"SeriesIsUnmonitored": "Serie non monitorata",
"SeriesProgressBarText": "{episodeFileCount} / {episodeCount} (Totale: {totalEpisodeCount}, Scaricando: {downloadingCount})",
"ShowEpisodes": "Mostra Episodi",
"ShowEpisodeInformationHelpText": "Mostra titolo e numero dell'episodio",
"Source": "Fonte",
"Unknown": "Sconosciuto",
"UseSsl": "Usa SSL",
"DownloadClientSettingsDestinationHelpText": "Specifica manualmente la destinazione dei download, lascia vuoti per usare la predefinita",
"DownloadClientSettingsInitialState": "Stato Iniziale",
"Folder": "Cartella",
"TorrentBlackholeSaveMagnetFilesReadOnly": "Solo Lettura",
"Ui": "Interfaccia",
"UiLanguage": "Lingua Interfaccia",
"UiSettingsLoadError": "Impossibile caricare le impostazioni interfaccia",
"MustNotContain": "Non Deve Contenere",
"Network": "Rete",
"ReadTheWikiForMoreInformation": "Leggi la Wiki per più informazioni",
"RecentChanges": "Cambiamenti Recenti",
"Refresh": "Aggiorna",
"DownloadClientSettingsUseSslHelpText": "Usa connessione sicura quando connetti a {clientName}",
"SelectEpisodes": "Seleziona Episodio/i",
"TestAll": "Prova Tutto",
"SelectFolder": "Seleziona Cartella",
"DownloadStationStatusExtracting": "Estrazione: {progress}&",
"DownloadClientQbittorrentSettingsSequentialOrder": "Ordine Sequenziale",
"DownloadClientQbittorrentValidationCategoryAddFailureDetail": "{appName} non è stato in grado di aggiungere l'etichetta a qBittorrent.",
"DownloadClientQbittorrentSettingsSequentialOrderHelpText": "Scarica in ordine sequenziale (qBittorrent 4.1.0+)",
"MegabytesPerMinute": "Megabyte Per Minuto",
"MonitorFirstSeasonDescription": "Monitora tutti gli episodi delle prima stagione. Tutte le altre stagioni saranno ignorate",
"MustContain": "Deve Contenere",
"PreferUsenet": "Preferisci Usenet",
"NoChange": "Nessun Cambio",
"RestoreBackup": "Ripristina Backup",
"SelectAll": "Seleziona Tutto",
"SelectSeries": "Seleziona Serie",
"SeriesTitle": "Titolo Serie",
"ShowPath": "Mostra Percorso",
"Table": "Tabella",
"TheTvdb": "TheTVDB",
"Total": "Totale",
"TotalFileSize": "Totale Dimensione File",
"DownloadClientQbittorrentTorrentStateMetadata": "qBittorrent sta scaricando i metadati",
"DownloadClientQbittorrentTorrentStateUnknown": "Stato di download sconosciuto: {state}",
"External": "Esterno",
"Failed": "Fallito",
"FilterLessThan": "meno di",
"FilterDoesNotEndWith": "non termina con",
"FilterEqual": "uguale",
"FormatDateTimeRelative": "{relativeDay}, {formattedDate} {formattedTime}",
"FormatShortTimeSpanMinutes": "{minutes} minuto/i",
"InvalidUILanguage": "L'interfaccia è impostata in una lingua non valida, correggi e salva le tue impostazioni",
"Max": "Massimo",
"MinutesFortyFive": "45 Minuti: {fortyFive}",
"NoDownloadClientsFound": "Nessun client di download trovato",
"MassSearchCancelWarning": "Questo non può essere cancellato una volta avviato senza riavviare {appName} o disattivando tutti i tuoi indicizzatori.",
"Profiles": "Profili",
"Qualities": "Qualità",
"QualityProfilesLoadError": "Impossibile caricare Profili Qualità",
"QueueLoadError": "Impossibile caricare la Coda",
"Queued": "In Coda",
"SaveChanges": "Salva Cambiamenti",
"Seasons": "Stagioni",
"Series": "Serie",
"MonitorRecentEpisodes": "Episodi Recenti",
"MoreDetails": "Ulteriore dettagli",
"MonitorSpecialEpisodes": "Monitora Speciali",
"TimeFormat": "Formato Orario",
"UsenetDisabled": "Usenet Disabilitato",
"Version": "Versione",
"WithFiles": "Con i File",
"Username": "Nome Utente",
"YesCancel": "Sì, Cancella",
"Yes": "Sì",
"DownloadClientRTorrentSettingsAddStopped": "Aggiungi Fermato",
"Restart": "Riavvia",
"Rss": "RSS",
"ProxyBadRequestHealthCheckMessage": "Test del proxy fallito: Status Code: {statusCode}",
"ProxyBypassFilterHelpText": "Usa ',' come separatore, e '*.' come wildcard per i sottodomini",
"RestartRequiredToApplyChanges": "{appName} richiede un riavvio per applicare i cambiamenti, vuoi riavviare ora?",
"SearchMonitored": "Ricerca Monitorate",
"SeasonDetails": "Dettagli Stagione",
"SelectDownloadClientModalTitle": "{modalTitle} - Seleziona Client di Download",
"SelectDropdown": "Seleziona...",
"SeriesIsMonitored": "Serie monitorata",
"Special": "Speciale",
"TablePageSizeMaximum": "La dimensione della pagina non deve superare {maximumValue}",
"TablePageSize": "Dimensione Pagina",
"UnknownDownloadState": "Stato download sconosciuto: {state}",
"UsenetBlackholeNzbFolder": "Cartella Nzb",
"From": "Da",
"QualityProfiles": "Profili Qualità",
"RecyclingBinCleanupHelpText": "Imposta a 0 per disattivare la pulizia automatica",
"RefreshAndScan": "Aggiorna & Scansiona",
"HourShorthand": "h",
"InteractiveImportNoImportMode": "Una modalità di importazione deve essere selezionata",
"MyComputer": "Mio Computer",
"Posters": "Locandine",
"SeasonNumberToken": "Stagione {seasonNumber}",
"ShowTitle": "Mostra Titolo",
"TorrentBlackholeTorrentFolder": "Cartella Torrent",
"UiLanguageHelpText": "Lingua che {appName} userà per l'interfaccia",
"DownloadIgnored": "Download Ignorato",
"CustomFormatsSpecificationMaximumSizeHelpText": "La release deve essere minore o uguale a questa dimensione",
"CustomFormatsSpecificationMinimumSizeHelpText": "La release deve essere maggiore di questa dimensione",
"CustomFormatsSpecificationRegularExpression": "Espressione Regolare",
"DefaultDelayProfileSeries": "Questo è il profilo di default. Viene Applicato a tutte le serie che non hanno in profilo esplicito.",
"DownloadClientDelugeTorrentStateError": "Deluge sta segnalando un errore",
"DownloadClientDelugeSettingsUrlBaseHelpText": "Aggiungi un prefisso all'url del json di deluge, vedi {url}",
"FilterEpisodesPlaceholder": "Filtra episodi per titolo o numero",
"FilterGreaterThanOrEqual": "più grande o uguale di",
"FilterIsNot": "non è",
"FilterGreaterThan": "più grande di",
"Formats": "Formati",
"FormatRuntimeHours": "{hours}h",
"FirstDayOfWeek": "Primo Giorno della Settimana",
"FreeSpace": "Spazio Libero",
"FormatDateTime": "{formattedDate} {formattedTime}",
"FormatTimeSpanDays": "{days}d {time}",
"MetadataSettingsSeriesImages": "Immagini della Serie",
"MetadataSettingsSeriesMetadata": "Metadati della Serie",
"MetadataSettingsSeasonImages": "Immagini della Stagione",
"NotificationsEmailSettingsBccAddress": "Indirizzo/i BCC",
"OrganizeLoadError": "Errore caricando le anteprime",
"OrganizeSelectedSeriesModalHeader": "Organizza Serie Selezionate",
"Today": "Oggi",
"Specials": "Speciali",
"NotificationsLoadError": "Impossibile caricare Notifiche",
"SeasonPassTruncated": "Solo le ultime 25 stagione sono mostrate, vai ai dettagli per vedere tutte le stagioni",
"SeriesCannotBeFound": "Scusa, quella serie non può essere trovata.",
"Style": "Stile",
"NotificationsEmailSettingsCcAddress": "Indirizzo/i CC",
"NotificationsMailgunSettingsUseEuEndpoint": "Usa Endpoint EU",
"Pending": "In Attesa",
"PendingChangesStayReview": "Rimani e rivedi i cambiamenti",
"FormatAgeDays": "giorni",
"FormatAgeHour": "ora",
"FormatAgeHours": "ore",
"FormatAgeMinute": "minuto",
"FormatAgeMinutes": "minuti",
"FormatAgeDay": "giorno",
"MetadataPlexSettingsSeriesPlexMatchFileHelpText": "Crea un file .plexmatch nella cartella della serie",
"MonitorMissingEpisodes": "Episodi Mancanti",
"New": "Nuovo",
"NoBackupsAreAvailable": "Nessun backup disponibile",
"MetadataSettingsEpisodeImages": "Immagini dell'Episodio",
"PosterSize": "Dimensioni Locandina",
"Restore": "Ripristina",
"RestartReloadNote": "Nota: {appName} si riavvierà automaticamente e ricaricherà l'interfaccia durante il processo di ripristino.",
"SeasonPassEpisodesDownloaded": "{episodeFileCount}/{totalEpisodeCount} episodi scaricati",
"SelectLanguage": "Seleziona Lingua",
"SeriesIndexFooterDownloading": "Scaricando (Uno o più episodi)",
"SeriesTypes": "Tipi Serie",
"SetPermissions": "Imposta Permessi",
"StartupDirectory": "Cartella di Avvio",
"Tomorrow": "Domani",
"UnknownEventTooltip": "Evento sconosciuto",
"UseProxy": "Usa Proxy",
"InteractiveImportNoQuality": "Una qualità deve essere scelta per ogni file selezionato",
"PortNumber": "Numero Porta",
"MonitorAllSeasons": "Tutte le Stagioni",
"DownloadClientValidationUnableToConnect": "Impossibile connettersi a {clientName}",
"DownloadClientValidationVerifySslDetail": "Per favore verifica la tua configurazione SSL su entrambi {clientName} e {appName}",
"IndexerDownloadClientHealthCheckMessage": "Indicizzatori con client di download non validi: {indexerNames}.",
"MonitorAllSeasonsDescription": "Monitora tutte le nuove stagioni automaticamente",
"MonitorFutureEpisodes": "Episodi Futuri",
"No": "No",
"MonitorLastSeason": "Ultima Stagione",
"NoEpisodesInThisSeason": "Nessun episodio in questa stagione",
"OrganizeModalHeaderSeason": "Organizza & Rinomina - {season}",
"Save": "Salva",
"More": "Altro",
"MonitorLastSeasonDescription": "Monitora tutti gli episodi della ultima stagione",
"RestartSonarr": "Riavvia {appName}",
"SeasonFinale": "Finale di Stagione",
"RestartNow": "Riavvia ora",
"TablePageSizeHelpText": "Numero di elementi da mostrare in ogni pagina",
"UiSettings": "Impostazioni Interfaccia",
"TvdbId": "ID TVDB",
"Wanted": "Ricercato",
"CustomFormatsLoadError": "Impossibile a caricare Formati Personalizzati",
"FilterEndsWith": "termina con",
"MetadataSource": "Fonte Metadati",
"Monitored": "Monitorato",
"MoveSeriesFoldersDontMoveFiles": "No, Sposterò i File da Solo",
"RemoveQueueItemRemovalMethod": "Metodo di Rimozione",
"SecretToken": "Secret Token",
"UrlBase": "Base Url",
"NotificationsEmailSettingsFromAddress": "Dall'Indirizzo",
"NotificationsGotifySettingsServer": "Server Gotify",
"NotificationsGotifySettingsPriorityHelpText": "Priorità della notifica",
"Score": "Punteggio",
"SelectSeasonModalTitle": "{modalTitle} - Seleziona Stagione",
"UpdateAvailableHealthCheckMessage": "Nuovo aggiornamento disponibile",
"Permissions": "Permessi",
"Scheduled": "Pianificato",
"SearchAll": "Ricerca tutto",
"VisitTheWikiForMoreDetails": "Visita la wiki per ulteriori dettagli: ",
"Week": "Settimana",
"DownloadClientValidationSslConnectFailureDetail": "{appName} non è in grado di connettersi a {clientName} usando SSL. Questo problema potrebbe essere legato al computer. Prova a configurare entrambi {appName} e {clientName} senza usare SSL.",
"LogFilesLocation": "File di Log localizzati in: {location}",
"PendingChangesMessage": "Hai dei cambiamenti non salvati, sei sicuro di volere lasciare questa pagina?",
"NotificationsEmailSettingsUseEncryption": "Usa Crittografia",
"DailyEpisodeTypeDescription": "Episodi rilasciati giornalmente o meno frequentemente che usano anno-mese-giorno (2023-08-04)",
"DownloadClientQbittorrentTorrentStateMissingFiles": "qBittorrent sta segnalando dei file mancanti",
"NoEventsFound": "Nessun evento trovato",
"SearchIsNotSupportedWithThisIndexer": "Ricerca non supportata con questo indicizzatore",
"SelectReleaseType": "Seleziona Tipo Release",
"Uppercase": "Maiuscolo",
"False": "Falso",
"Files": "File",
"Filter": "Filtro",
"OverrideGrabNoEpisode": "Almeno un episodio deve essere selezionato",
"FilterStartsWith": "inizia con",
"FinaleTooltip": "Serie o finale di stagione",
"QualitySettings": "Impostazioni Qualità",
"Queue": "Coda",
"QueueIsEmpty": "La coda è vuota",
"QuickSearch": "Ricerca Veloce",
"RefreshSeries": "Aggiorna Serie",
"Year": "Anno",
"LanguagesLoadError": "Impossibile caricare le lingue",
"MetadataLoadError": "Impossibile caricare i Metadati",
"MetadataSettings": "Impostazioni Metadati",
"MetadataSettingsEpisodeMetadata": "Metadati dell'Episodio",
"Status": "Stato",
"DeleteQualityProfileMessageText": "Sicuro di voler cancellare il profilo di qualità '{name}'?",
"NotificationsGotifySettingsServerHelpText": "URL server Gotify, includendo http(s):// e porta se necessario",
"Time": "Orario",
"Password": "Password",
"OrganizeNothingToRename": "Successo! Il mio lavoro è finito, nessun file da rinominare.",
"PosterOptions": "Opzioni Locandina",
"Progress": "Progressi",
"Protocol": "Protocollo",
"ProxyFailedToTestHealthCheckMessage": "Test del proxy fallito: {url}",
"ProxyType": "Tipo Proxy",
"QualitiesLoadError": "Impossibile caricare qualità",
"RecyclingBinCleanup": "Pulizia Cestino",
"SelectFolderModalTitle": "{modalTitle} - Seleziona Cartella",
"ResetTitles": "Reimposta Titoli",
"RestartLater": "Lo riavvierò dopo",
"RssSyncInterval": "Intervallo Sincronizzazione RSS",
"Runtime": "Tempo di esecuzione",
"SearchByTvdbId": "Puoi anche ricercare usando l'ID TVDB di uno show. Es. tvdb:71663",
"SearchForMonitoredEpisodesSeason": "Ricerca degli episodi monitorati in questa stagione",
"SelectEpisodesModalTitle": "{modalTitle} - Seleziona Episodio/i",
"SelectSeason": "Seleziona Stagione",
"SeriesIndexFooterMissingUnmonitored": "Episodi Mancanti (Serie non monitorate)",
"ShowEpisodeInformation": "Mostra Informazioni Episodio",
"Size": "Dimensione",
"YesterdayAt": "Ieri alle {time}",
"UnableToImportAutomatically": "Impossibile Importare Automaticamente",
"Small": "Piccolo",
"Socks4": "Socks4",
"Standard": "Standard",
"Started": "Iniziato",
"TorrentsDisabled": "Torrent Disattivati",
"UnsavedChanges": "Cambiamenti Non Salvati",
"UnselectAll": "Deseleziona Tutto",
"UpdateAll": "Aggiorna Tutto",
"UpdateSonarrDirectlyLoadError": "Impossibile aggiornare {appName} direttamente,",
"UseSeasonFolderHelpText": "Ordina episodi dentro all cartella della stagione",
"VideoCodec": "Codec Video",
"DownloadClientValidationCategoryMissingDetail": "La categoria che ha inserito non esiste in {clientName}. Crealo prima su {clientName}.",
"RestrictionsLoadError": "Impossibile caricare le Restrizioni",
"SearchFailedError": "Ricerca fallita, per favore riprova nuovamente dopo.",
"DownloadClientDelugeValidationLabelPluginFailureDetail": "{appName} non è stato in grado di aggiungere l'etichetta a {clientName}.",
"DownloadClientFreeboxAuthenticationError": "Autenticazione alle API di Freebox fallita. Ragione: {errorDescription}",
"DownloadClientQbittorrentValidationCategoryUnsupportedDetail": "Categorie non supportate fino alla versione 3.3.0 di qBittorrent. Per favore aggiorna o prova con una Categoria vuota.",
"DownloadClientSettingsInitialStateHelpText": "Stato iniziale per i torrent aggiunti a {clientName}",
"DeleteSelectedDownloadClientsMessageText": "Sei sicuro di voler eliminare i '{count}' client di download selezionato/i?",
"DownloadClientSettingsAddPaused": "Aggiungi In Pausa",
"DownloadClientValidationUnableToConnectDetail": "Per favore verifica nome host e porta.",
"FormatShortTimeSpanSeconds": "{seconds} secondo/i",
"IgnoreDownload": "Ignora Download",
"Implementation": "Implementazione",
"File": "File",
"LabelIsRequired": "Etichetta richiesta",
"IndexerSettingsSeedRatio": "Rapporto Seed",
"ManageClients": "Gestisci Clients",
"ManageDownloadClients": "Gestisci Clients di Download",
"Message": "Messaggio",
"Min": "Min",
"MinutesThirty": "30 Minuti: {thirty}",
"Missing": "Mancante",
"MonitorNewItems": "Monitora Nuovi Elementi",
"MonitorNewSeasons": "Monitora Nuove Stagioni",
"MonitorNewSeasonsHelpText": "Quali nuove stagioni devono essere monitorati automaticamente",
"MonitorNoEpisodes": "Nessuno",
"MonitorNoEpisodesDescription": "Nessun episodio sarà monitorato",
"NotificationsDiscordSettingsAvatar": "Avatar",
"NotificationsMailgunSettingsUseEuEndpointHelpText": "Abilita per usare l'endpoint EU di MailGun",
"MonitorNoNewSeasonsDescription": "Non monitorare nessuna nuova stagione automaticamente",
"MonitorPilotEpisode": "Episodio Pilota",
"MonitorPilotEpisodeDescription": "Monitora solo il primo episodio della prima stagione",
"MonitorSeries": "Monitora Serie",
"MonitoredStatus": "Monitorato/Stato",
"MoveFiles": "Sposta File",
"Name": "Nome",
"Negate": "Nega",
"SubtitleLanguages": "Lingua Sottotitoli",
"Sunday": "Domenica",
"TableColumns": "Colonne",
"TodayAt": "Oggi alle {time}",
"TomorrowAt": "Domani alle {time}",
"TotalSpace": "Totale Spazio"
}

View File

@@ -169,7 +169,7 @@
"Calendar": "Calendário",
"Connect": "Conectar",
"CustomFormats": "Formatos personalizados",
"CutoffUnmet": "Limite não alcançado",
"CutoffUnmet": "Corte Não Alcançado",
"DownloadClients": "Clientes de download",
"Events": "Eventos",
"General": "Geral",
@@ -205,7 +205,7 @@
"SeasonNumber": "Número da Temporada",
"SeriesTitle": "Título da Série",
"Special": "Especial",
"TestParsing": "Testar Análise",
"TestParsing": "Análise de teste",
"About": "Sobre",
"Actions": "Ações",
"AppDataDirectory": "Diretório AppData",
@@ -432,7 +432,7 @@
"AuthBasic": "Básico (pop-up do navegador)",
"AuthForm": "Formulário (página de login)",
"Authentication": "Autenticação",
"AuthenticationMethodHelpText": "Exigir nome de usuário e senha para acessar o {appName}",
"AuthenticationMethodHelpText": "Exigir Nome de Usuário e Senha para acessar {appName}",
"AuthenticationRequired": "Autenticação exigida",
"AutoRedownloadFailedHelpText": "Procurar automaticamente e tente baixar uma versão diferente",
"AutoTaggingLoadError": "Não foi possível carregar tagging automática",
@@ -494,7 +494,7 @@
"CustomFormatsSettings": "Configurações de Formatos Personalizados",
"CustomFormatsSettingsSummary": "Configurações e Formatos Personalizados",
"DailyEpisodeFormat": "Formato do episódio diário",
"Cutoff": "Limite",
"Cutoff": "Corte",
"Dash": "Traço",
"Dates": "Datas",
"Debug": "Depuração",
@@ -891,7 +891,7 @@
"UnmonitorDeletedEpisodes": "Cancelar Monitoramento de Episódios Excluídos",
"UnsavedChanges": "Alterações Não Salvas",
"UpdateAutomaticallyHelpText": "Baixe e instale atualizações automaticamente. Você ainda poderá instalar a partir do Sistema: Atualizações",
"UpdateMechanismHelpText": "Use o atualizador integrado do {appName} ou um script",
"UpdateMechanismHelpText": "Usar o atualizador integrado do {appName} ou um script",
"UpdateSonarrDirectlyLoadError": "Incapaz de atualizar o {appName} diretamente,",
"UpdateUiNotWritableHealthCheckMessage": "Não é possível instalar a atualização porque a pasta de IU '{uiFolder}' não pode ser salva pelo usuário '{userName}'.",
"UpgradeUntil": "Atualizar Até",
@@ -2078,5 +2078,6 @@
"TodayAt": "Hoje às {time}",
"TomorrowAt": "Amanhã às {time}",
"HasUnmonitoredSeason": "Tem Temporada Não Monitorada",
"YesterdayAt": "Ontem às {time}"
"YesterdayAt": "Ontem às {time}",
"UnableToImportAutomatically": "Não foi possível importar automaticamente"
}

View File

@@ -73,7 +73,7 @@
"AppUpdated": "{appName} Güncellendi",
"ApplicationURL": "Uygulama URL'si",
"ApplyTagsHelpTextAdd": "Ekle: Etiketleri mevcut etiket listesine ekleyin",
"ApplyTagsHelpTextHowToApplyIndexers": "Seçilen indeksleyicilere etiketler nasıl uygulanır?",
"ApplyTagsHelpTextHowToApplyIndexers": "Seçilen indeksleyicilere etiketler nasıl uygulanır",
"ApplyTagsHelpTextRemove": "Kaldır: Girilen etiketleri kaldırın",
"AuthenticationRequiredPasswordHelpTextWarning": "Yeni şifre girin",
"AuthenticationRequiredUsernameHelpTextWarning": "Yeni kullanıcı adınızı girin",
@@ -88,8 +88,8 @@
"CustomFormatUnknownConditionOption": "'{implementation}' koşulu için bilinmeyen seçenek '{key}'",
"AutoTagging": "Otomatik Etiketleme",
"AutoTaggingNegateHelpText": "İşaretlenirse, {implementationName} koşulu eşleştiğinde otomatik etiketleme kuralı uygulanmayacaktır.",
"ApplyTagsHelpTextHowToApplyDownloadClients": "Seçilen indirme istemcilerine etiketler nasıl uygulanır?",
"ApplyTagsHelpTextHowToApplyImportLists": "Seçilen içe aktarma listelerine etiketler nasıl uygulanır?",
"ApplyTagsHelpTextHowToApplyDownloadClients": "Seçilen indirme istemcilerine etiketler nasıl uygulanır",
"ApplyTagsHelpTextHowToApplyImportLists": "Seçilen içe aktarma listelerine etiketler nasıl uygulanır",
"AuthenticationRequiredHelpText": "İstekler için Kimlik doğrulamanın gereklilik ayarını değiştirin. Riskleri anlamadığınız sürece değiştirmeyin.",
"AutoTaggingLoadError": "Otomatik etiketleme yüklenemiyor",
"BypassDelayIfAboveCustomFormatScore": "Özel Format Koşullarının Üstündeyse Baypas Et",
@@ -837,5 +837,15 @@
"UpdateAutomaticallyHelpText": "Güncelleştirmeleri otomatik olarak indirip yükleyin. Sistem: Güncellemeler'den yükleme yapmaya devam edebileceksiniz",
"Wanted": "Arananlar",
"Cutoff": "Kesinti",
"Required": "Gerekli"
"Required": "Gerekli",
"AirsTbaOn": "Daha sonra duyurulacak {networkLabel}'de",
"AllFiles": "Tüm dosyalar",
"AllSeriesAreHiddenByTheAppliedFilter": "Tüm sonuçlar, uygulanan filtre tarafından gizlenir",
"Always": "Her zaman",
"AirsDateAtTimeOn": "{date} saat {time} {networkLabel}'de",
"AllResultsAreHiddenByTheAppliedFilter": "Tüm sonuçlar, uygulanan filtre tarafından gizlenir",
"AllSeriesInRootFolderHaveBeenImported": "{path} içerisindeki tüm diziler içeri aktarıldı",
"AlternateTitles": "Alternatif Başlıklar",
"AnEpisodeIsDownloading": "Bir bölüm indiriliyor",
"UnableToImportAutomatically": "Otomatikman İçe Aktarılamıyor"
}

View File

@@ -550,7 +550,7 @@
"AutoRedownloadFailedHelpText": "自动搜索并尝试下载不同的版本",
"AutoTaggingLoadError": "无法加载自动标记",
"Automatic": "自动化",
"AutoTaggingRequiredHelpText": "这个{implementationName}条件必须匹配自动标记规则才能应用。否则,一个{implementationName}匹配就足够了。",
"AutoTaggingRequiredHelpText": "这个{0}条件必须匹配自动标记规则才能应用。否则,一个{0}匹配就足够了。",
"AutoTaggingNegateHelpText": "如果选中,当 {implementationName} 条件匹配时,自动标记不会应用。",
"BackupRetentionHelpText": "超过保留期限的自动备份将被自动清理",
"BackupsLoadError": "无法加载备份",

View File

@@ -395,14 +395,6 @@ namespace NzbDrone.Core.MediaFiles.EpisodeImport.Manual
item.Name = Path.GetFileNameWithoutExtension(decision.LocalEpisode.Path);
item.DownloadId = downloadId;
if (decision.LocalEpisode.Series != null)
{
item.Series = decision.LocalEpisode.Series;
item.CustomFormats = _formatCalculator.ParseCustomFormat(decision.LocalEpisode);
item.CustomFormatScore = item.Series.QualityProfile?.Value.CalculateCustomFormatScore(item.CustomFormats) ?? 0;
}
if (decision.LocalEpisode.Episodes.Any() && decision.LocalEpisode.Episodes.Select(c => c.SeasonNumber).Distinct().Count() == 1)
{
var seasons = decision.LocalEpisode.Episodes.Select(c => c.SeasonNumber).Distinct().ToList();
@@ -430,6 +422,14 @@ namespace NzbDrone.Core.MediaFiles.EpisodeImport.Manual
item.IndexerFlags = (int)decision.LocalEpisode.IndexerFlags;
item.ReleaseType = decision.LocalEpisode.ReleaseType;
if (decision.LocalEpisode.Series != null)
{
item.Series = decision.LocalEpisode.Series;
item.CustomFormats = _formatCalculator.ParseCustomFormat(decision.LocalEpisode);
item.CustomFormatScore = item.Series.QualityProfile?.Value.CalculateCustomFormatScore(item.CustomFormats) ?? 0;
}
return item;
}
@@ -506,8 +506,6 @@ namespace NzbDrone.Core.MediaFiles.EpisodeImport.Manual
// Augment episode file so imported files have all additional information an automatic import would
localEpisode = _aggregationService.Augment(localEpisode, trackedDownload?.DownloadItem);
localEpisode.CustomFormats = _formatCalculator.ParseCustomFormat(localEpisode);
localEpisode.CustomFormatScore = localEpisode.Series.QualityProfile?.Value.CalculateCustomFormatScore(localEpisode.CustomFormats) ?? 0;
// Apply the user-chosen values.
localEpisode.Series = series;
@@ -518,6 +516,9 @@ namespace NzbDrone.Core.MediaFiles.EpisodeImport.Manual
localEpisode.IndexerFlags = (IndexerFlags)file.IndexerFlags;
localEpisode.ReleaseType = file.ReleaseType;
localEpisode.CustomFormats = _formatCalculator.ParseCustomFormat(localEpisode);
localEpisode.CustomFormatScore = localEpisode.Series.QualityProfile?.Value.CalculateCustomFormatScore(localEpisode.CustomFormats) ?? 0;
// TODO: Cleanup non-tracked downloads
var importDecision = new ImportDecision(localEpisode);

View File

@@ -22,10 +22,10 @@ namespace NzbDrone.Core.MediaFiles.MediaInfo
private readonly List<FFProbePixelFormat> _pixelFormats;
public const int MINIMUM_MEDIA_INFO_SCHEMA_REVISION = 8;
public const int CURRENT_MEDIA_INFO_SCHEMA_REVISION = 10;
public const int CURRENT_MEDIA_INFO_SCHEMA_REVISION = 11;
private static readonly string[] ValidHdrColourPrimaries = { "bt2020" };
private static readonly string[] HlgTransferFunctions = { "bt2020-10", "arib-std-b67" };
private static readonly string[] HlgTransferFunctions = { "arib-std-b67" };
private static readonly string[] PqTransferFunctions = { "smpte2084" };
private static readonly string[] ValidHdrTransferFunctions = HlgTransferFunctions.Concat(PqTransferFunctions).ToArray();

View File

@@ -137,6 +137,7 @@ namespace NzbDrone.Core.MediaFiles
environmentVariables.Add("Sonarr_Series_Path", series.Path);
environmentVariables.Add("Sonarr_Series_TvdbId", series.TvdbId.ToString());
environmentVariables.Add("Sonarr_Series_TvMazeId", series.TvMazeId.ToString());
environmentVariables.Add("Sonarr_Series_TmdbId", series.TmdbId.ToString());
environmentVariables.Add("Sonarr_Series_ImdbId", series.ImdbId ?? string.Empty);
environmentVariables.Add("Sonarr_Series_Type", series.SeriesType.ToString());
environmentVariables.Add("Sonarr_Series_OriginalLanguage", IsoLanguages.Get(series.OriginalLanguage).ThreeLetterCode);

View File

@@ -23,6 +23,7 @@ namespace NzbDrone.Core.MetadataSource.SkyHook.Resource
public string LastAired { get; set; }
public int? TvRageId { get; set; }
public int? TvMazeId { get; set; }
public int? TmdbId { get; set; }
public string Status { get; set; }
public int? Runtime { get; set; }

View File

@@ -188,6 +188,11 @@ namespace NzbDrone.Core.MetadataSource.SkyHook
series.TvMazeId = show.TvMazeId.Value;
}
if (show.TmdbId.HasValue)
{
series.TmdbId = show.TmdbId.Value;
}
series.ImdbId = show.ImdbId;
series.Title = show.Title;
series.CleanTitle = Parser.Parser.CleanSeriesTitle(show.Title);

View File

@@ -69,6 +69,7 @@ namespace NzbDrone.Core.Notifications.CustomScript
environmentVariables.Add("Sonarr_Series_TitleSlug", series.TitleSlug);
environmentVariables.Add("Sonarr_Series_TvdbId", series.TvdbId.ToString());
environmentVariables.Add("Sonarr_Series_TvMazeId", series.TvMazeId.ToString());
environmentVariables.Add("Sonarr_Series_TmdbId", series.TmdbId.ToString());
environmentVariables.Add("Sonarr_Series_ImdbId", series.ImdbId ?? string.Empty);
environmentVariables.Add("Sonarr_Series_Type", series.SeriesType.ToString());
environmentVariables.Add("Sonarr_Series_Year", series.Year.ToString());
@@ -116,6 +117,7 @@ namespace NzbDrone.Core.Notifications.CustomScript
environmentVariables.Add("Sonarr_Series_Path", series.Path);
environmentVariables.Add("Sonarr_Series_TvdbId", series.TvdbId.ToString());
environmentVariables.Add("Sonarr_Series_TvMazeId", series.TvMazeId.ToString());
environmentVariables.Add("Sonarr_Series_TmdbId", series.TmdbId.ToString());
environmentVariables.Add("Sonarr_Series_ImdbId", series.ImdbId ?? string.Empty);
environmentVariables.Add("Sonarr_Series_Type", series.SeriesType.ToString());
environmentVariables.Add("Sonarr_Series_Year", series.Year.ToString());
@@ -181,6 +183,7 @@ namespace NzbDrone.Core.Notifications.CustomScript
environmentVariables.Add("Sonarr_Series_Path", series.Path);
environmentVariables.Add("Sonarr_Series_TvdbId", series.TvdbId.ToString());
environmentVariables.Add("Sonarr_Series_TvMazeId", series.TvMazeId.ToString());
environmentVariables.Add("Sonarr_Series_TmdbId", series.TmdbId.ToString());
environmentVariables.Add("Sonarr_Series_ImdbId", series.ImdbId ?? string.Empty);
environmentVariables.Add("Sonarr_Series_Type", series.SeriesType.ToString());
environmentVariables.Add("Sonarr_Series_Year", series.Year.ToString());
@@ -213,6 +216,7 @@ namespace NzbDrone.Core.Notifications.CustomScript
environmentVariables.Add("Sonarr_Series_Path", series.Path);
environmentVariables.Add("Sonarr_Series_TvdbId", series.TvdbId.ToString());
environmentVariables.Add("Sonarr_Series_TvMazeId", series.TvMazeId.ToString());
environmentVariables.Add("Sonarr_Series_TmdbId", series.TmdbId.ToString());
environmentVariables.Add("Sonarr_Series_ImdbId", series.ImdbId ?? string.Empty);
environmentVariables.Add("Sonarr_Series_Type", series.SeriesType.ToString());
environmentVariables.Add("Sonarr_Series_Year", series.Year.ToString());
@@ -252,6 +256,7 @@ namespace NzbDrone.Core.Notifications.CustomScript
environmentVariables.Add("Sonarr_Series_Path", series.Path);
environmentVariables.Add("Sonarr_Series_TvdbId", series.TvdbId.ToString());
environmentVariables.Add("Sonarr_Series_TvMazeId", series.TvMazeId.ToString());
environmentVariables.Add("Sonarr_Series_TmdbId", series.TmdbId.ToString());
environmentVariables.Add("Sonarr_Series_ImdbId", series.ImdbId ?? string.Empty);
environmentVariables.Add("Sonarr_Series_Type", series.SeriesType.ToString());
environmentVariables.Add("Sonarr_Series_Year", series.Year.ToString());
@@ -276,6 +281,7 @@ namespace NzbDrone.Core.Notifications.CustomScript
environmentVariables.Add("Sonarr_Series_Path", series.Path);
environmentVariables.Add("Sonarr_Series_TvdbId", series.TvdbId.ToString());
environmentVariables.Add("Sonarr_Series_TvMazeId", series.TvMazeId.ToString());
environmentVariables.Add("Sonarr_Series_TmdbId", series.TmdbId.ToString());
environmentVariables.Add("Sonarr_Series_ImdbId", series.ImdbId ?? string.Empty);
environmentVariables.Add("Sonarr_Series_Type", series.SeriesType.ToString());
environmentVariables.Add("Sonarr_Series_Year", series.Year.ToString());
@@ -345,6 +351,7 @@ namespace NzbDrone.Core.Notifications.CustomScript
environmentVariables.Add("Sonarr_Series_Path", series.Path);
environmentVariables.Add("Sonarr_Series_TvdbId", series.TvdbId.ToString());
environmentVariables.Add("Sonarr_Series_TvMazeId", series.TvMazeId.ToString());
environmentVariables.Add("Sonarr_Series_TmdbId", series.TmdbId.ToString());
environmentVariables.Add("Sonarr_Series_ImdbId", series.ImdbId ?? string.Empty);
environmentVariables.Add("Sonarr_Series_Type", series.SeriesType.ToString());
environmentVariables.Add("Sonarr_Series_Year", series.Year.ToString());

View File

@@ -20,6 +20,7 @@ namespace NzbDrone.Core.Notifications.Emby
public string Imdb { get; set; }
public int Tvdb { get; set; }
public int TvMaze { get; set; }
public int Tmdb { get; set; }
public int TvRage { get; set; }
}

View File

@@ -68,6 +68,11 @@ namespace NzbDrone.Core.Notifications.Emby
return MediaBrowserMatchQuality.Id;
}
if (item is { ProviderIds.Tmdb: int tmdbid } && tmdbid != 0 && tmdbid == series.TmdbId)
{
return MediaBrowserMatchQuality.Id;
}
if (item is { ProviderIds.TvRage: int tvrageid } && tvrageid != 0 && tvrageid == series.TvRageId)
{
return MediaBrowserMatchQuality.Id;

View File

@@ -3,6 +3,7 @@ using FluentValidation.Results;
using NzbDrone.Common.Extensions;
using NzbDrone.Core.Configuration;
using NzbDrone.Core.Localization;
using NzbDrone.Core.MediaCover;
using NzbDrone.Core.MediaFiles;
using NzbDrone.Core.Notifications.Webhook;
using NzbDrone.Core.Tags;
@@ -15,8 +16,8 @@ namespace NzbDrone.Core.Notifications.Notifiarr
{
private readonly INotifiarrProxy _proxy;
public Notifiarr(INotifiarrProxy proxy, IConfigFileProvider configFileProvider, IConfigService configService, ILocalizationService localizationService, ITagRepository tagRepository)
: base(configFileProvider, configService, localizationService, tagRepository)
public Notifiarr(INotifiarrProxy proxy, IConfigFileProvider configFileProvider, IConfigService configService, ILocalizationService localizationService, ITagRepository tagRepository, IMapCoversToLocal mediaCoverService)
: base(configFileProvider, configService, localizationService, tagRepository, mediaCoverService)
{
_proxy = proxy;
}

View File

@@ -233,7 +233,7 @@ namespace NzbDrone.Core.Notifications
public void Handle(ManualInteractionRequiredEvent message)
{
var series = message.Episode.Series;
var series = message.Episode?.Series;
var mess = "";
if (series != null)
@@ -255,7 +255,7 @@ namespace NzbDrone.Core.Notifications
{
Message = mess,
Series = series,
Quality = message.Episode.ParsedEpisodeInfo.Quality,
Quality = message.Episode?.ParsedEpisodeInfo.Quality,
Episode = message.Episode,
TrackedDownload = message.TrackedDownload,
DownloadClientInfo = message.TrackedDownload.DownloadItem?.DownloadClientInfo,

View File

@@ -1,4 +1,5 @@
using System;
using System.Collections.Generic;
using System.Net;
using NLog;
using NzbDrone.Common.EnvironmentInfo;
@@ -12,6 +13,7 @@ namespace NzbDrone.Core.Notifications.Plex.PlexTv
{
string GetAuthToken(string clientIdentifier, int pinId);
bool Ping(string clientIdentifier, string authToken);
List<PlexTvResource> GetResources(string clientIdentifier, string authToken);
}
public class PlexTvProxy : IPlexTvProxy
@@ -62,6 +64,33 @@ namespace NzbDrone.Core.Notifications.Plex.PlexTv
return false;
}
public List<PlexTvResource> GetResources(string clientIdentifier, string authToken)
{
try
{
// Allows us to tell plex.tv that we're still active and tokens should not be expired.
var request = BuildRequest(clientIdentifier);
request.ResourceUrl = "/api/v2/resources";
request.AddQueryParam("includeHttps", 1);
request.AddQueryParam("clientID", clientIdentifier);
request.AddQueryParam("X-Plex-Token", authToken);
if (Json.TryDeserialize<List<PlexTvResource>>(ProcessRequest(request), out var response))
{
return response;
}
}
catch (Exception e)
{
// Catch all exceptions and log at trace, this information could be interesting in debugging, but expired tokens will be handled elsewhere.
_logger.Trace(e, "Unable to ping plex.tv");
}
return new List<PlexTvResource>();
}
private HttpRequestBuilder BuildRequest(string clientIdentifier)
{
var requestBuilder = new HttpRequestBuilder("https://plex.tv")

View File

@@ -0,0 +1,32 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Newtonsoft.Json;
using NzbDrone.Common.Extensions;
namespace NzbDrone.Core.Notifications.Plex.PlexTv
{
public class PlexTvResource
{
public string Name { get; set; }
public bool Owned { get; set; }
public List<PlexTvResourceConnection> Connections { get; set; }
[JsonProperty("provides")]
public string ProvidesRaw { get; set; }
[JsonIgnore]
public List<string> Provides => ProvidesRaw.Split(",").ToList();
}
public class PlexTvResourceConnection
{
public string Uri { get; set; }
public string Protocol { get; set; }
public string Address { get; set; }
public int Port { get; set; }
public bool Local { get; set; }
public string Host => Uri.IsNullOrWhiteSpace() ? Address : new Uri(Uri).Host;
}
}

View File

@@ -1,4 +1,5 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using NzbDrone.Common.Cache;
@@ -14,6 +15,7 @@ namespace NzbDrone.Core.Notifications.Plex.PlexTv
PlexTvSignInUrlResponse GetSignInUrl(string callbackUrl, int pinId, string pinCode);
string GetAuthToken(int pinId);
void Ping(string authToken);
List<PlexTvResource> GetServers(string authToken);
HttpRequest GetWatchlist(string authToken, int pageSize, int pageOffset);
}
@@ -93,6 +95,16 @@ namespace NzbDrone.Core.Notifications.Plex.PlexTv
_cache.Get(authToken, () => _proxy.Ping(_configService.PlexClientIdentifier, authToken), TimeSpan.FromHours(24));
}
public List<PlexTvResource> GetServers(string authToken)
{
Ping(authToken);
var clientIdentifier = _configService.PlexClientIdentifier;
var resources = _proxy.GetResources(clientIdentifier, authToken);
return resources.Where(r => r.Owned && r.Provides.Contains("server")).ToList();
}
public HttpRequest GetWatchlist(string authToken, int pageSize, int pageOffset)
{
Ping(authToken);

View File

@@ -5,6 +5,7 @@ using FluentValidation.Results;
using NLog;
using NzbDrone.Common.Cache;
using NzbDrone.Common.Extensions;
using NzbDrone.Core.Annotations;
using NzbDrone.Core.Exceptions;
using NzbDrone.Core.MediaFiles;
using NzbDrone.Core.Notifications.Plex.PlexTv;
@@ -193,6 +194,79 @@ namespace NzbDrone.Core.Notifications.Plex.Server
};
}
if (action == "servers")
{
Settings.Validate().Filter("AuthToken").ThrowOnError();
if (Settings.AuthToken.IsNullOrWhiteSpace())
{
return new { };
}
var servers = _plexTvService.GetServers(Settings.AuthToken);
var options = servers.SelectMany(s =>
{
var result = new List<FieldSelectStringOption>();
// result.Add(new FieldSelectStringOption
// {
// Value = s.Name,
// Name = s.Name,
// IsDisabled = true
// });
s.Connections.ForEach(c =>
{
var isSecure = c.Protocol == "https";
var additionalProperties = new Dictionary<string, object>();
var hints = new List<string>();
additionalProperties.Add("host", c.Host);
additionalProperties.Add("port", c.Port);
additionalProperties.Add("useSsl", isSecure);
hints.Add(c.Local ? "Local" : "Remote");
if (isSecure)
{
hints.Add("Secure");
}
result.Add(new FieldSelectStringOption
{
Value = c.Uri,
Name = $"{s.Name} ({c.Host})",
Hint = string.Join(", ", hints),
AdditionalProperties = additionalProperties
});
if (isSecure)
{
var uri = $"http://{c.Address}:{c.Port}";
var insecureAdditionalProperties = new Dictionary<string, object>();
insecureAdditionalProperties.Add("host", c.Address);
insecureAdditionalProperties.Add("port", c.Port);
insecureAdditionalProperties.Add("useSsl", false);
result.Add(new FieldSelectStringOption
{
Value = uri,
Name = $"{s.Name} ({c.Address})",
Hint = c.Local ? "Local" : "Remote",
AdditionalProperties = insecureAdditionalProperties
});
}
});
return result;
});
return new
{
options
};
}
return new { };
}
}

View File

@@ -1,4 +1,5 @@
using FluentValidation;
using Newtonsoft.Json;
using NzbDrone.Common.Extensions;
using NzbDrone.Core.Annotations;
using NzbDrone.Core.Validation;
@@ -22,40 +23,45 @@ namespace NzbDrone.Core.Notifications.Plex.Server
public PlexServerSettings()
{
Host = "";
Port = 32400;
UpdateLibrary = true;
SignIn = "startOAuth";
}
[FieldDefinition(0, Label = "Host")]
[JsonIgnore]
[FieldDefinition(0, Label = "NotificationsPlexSettingsServer", Type = FieldType.Select, SelectOptionsProviderAction = "servers", HelpText = "NotificationsPlexSettingsServerHelpText")]
public string Server { get; set; }
[FieldDefinition(1, Label = "Host")]
public string Host { get; set; }
[FieldDefinition(1, Label = "Port")]
[FieldDefinition(2, Label = "Port")]
public int Port { get; set; }
[FieldDefinition(2, Label = "UseSsl", Type = FieldType.Checkbox, HelpText = "NotificationsSettingsUseSslHelpText")]
[FieldDefinition(3, Label = "UseSsl", Type = FieldType.Checkbox, HelpText = "NotificationsSettingsUseSslHelpText")]
[FieldToken(TokenField.HelpText, "UseSsl", "serviceName", "Plex")]
public bool UseSsl { get; set; }
[FieldDefinition(3, Label = "UrlBase", Type = FieldType.Textbox, Advanced = true, HelpText = "ConnectionSettingsUrlBaseHelpText")]
[FieldDefinition(4, Label = "UrlBase", Type = FieldType.Textbox, Advanced = true, HelpText = "ConnectionSettingsUrlBaseHelpText")]
[FieldToken(TokenField.HelpText, "UrlBase", "connectionName", "Plex")]
[FieldToken(TokenField.HelpText, "UrlBase", "url", "http://[host]:[port]/[urlBase]/plex")]
public string UrlBase { get; set; }
[FieldDefinition(4, Label = "NotificationsPlexSettingsAuthToken", Type = FieldType.Textbox, Privacy = PrivacyLevel.ApiKey, Advanced = true)]
[FieldDefinition(5, Label = "NotificationsPlexSettingsAuthToken", Type = FieldType.Textbox, Privacy = PrivacyLevel.ApiKey, Advanced = true)]
public string AuthToken { get; set; }
[FieldDefinition(5, Label = "NotificationsPlexSettingsAuthenticateWithPlexTv", Type = FieldType.OAuth)]
[FieldDefinition(6, Label = "NotificationsPlexSettingsAuthenticateWithPlexTv", Type = FieldType.OAuth)]
public string SignIn { get; set; }
[FieldDefinition(6, Label = "NotificationsSettingsUpdateLibrary", Type = FieldType.Checkbox)]
[FieldDefinition(7, Label = "NotificationsSettingsUpdateLibrary", Type = FieldType.Checkbox)]
public bool UpdateLibrary { get; set; }
[FieldDefinition(7, Label = "NotificationsSettingsUpdateMapPathsFrom", Type = FieldType.Textbox, Advanced = true, HelpText = "NotificationsSettingsUpdateMapPathsFromHelpText")]
[FieldDefinition(8, Label = "NotificationsSettingsUpdateMapPathsFrom", Type = FieldType.Textbox, Advanced = true, HelpText = "NotificationsSettingsUpdateMapPathsFromHelpText")]
[FieldToken(TokenField.HelpText, "NotificationsSettingsUpdateMapPathsFrom", "serviceName", "Plex")]
public string MapFrom { get; set; }
[FieldDefinition(8, Label = "NotificationsSettingsUpdateMapPathsTo", Type = FieldType.Textbox, Advanced = true, HelpText = "NotificationsSettingsUpdateMapPathsToHelpText")]
[FieldDefinition(9, Label = "NotificationsSettingsUpdateMapPathsTo", Type = FieldType.Textbox, Advanced = true, HelpText = "NotificationsSettingsUpdateMapPathsToHelpText")]
[FieldToken(TokenField.HelpText, "NotificationsSettingsUpdateMapPathsTo", "serviceName", "Plex")]
public string MapTo { get; set; }

View File

@@ -3,6 +3,7 @@ using FluentValidation.Results;
using NzbDrone.Common.Extensions;
using NzbDrone.Core.Configuration;
using NzbDrone.Core.Localization;
using NzbDrone.Core.MediaCover;
using NzbDrone.Core.MediaFiles;
using NzbDrone.Core.Tags;
using NzbDrone.Core.Tv;
@@ -14,8 +15,8 @@ namespace NzbDrone.Core.Notifications.Webhook
{
private readonly IWebhookProxy _proxy;
public Webhook(IWebhookProxy proxy, IConfigFileProvider configFileProvider, IConfigService configService, ILocalizationService localizationService, ITagRepository tagRepository)
: base(configFileProvider, configService, localizationService, tagRepository)
public Webhook(IWebhookProxy proxy, IConfigFileProvider configFileProvider, IConfigService configService, ILocalizationService localizationService, ITagRepository tagRepository, IMapCoversToLocal mediaCoverService)
: base(configFileProvider, configService, localizationService, tagRepository, mediaCoverService)
{
_proxy = proxy;
}

View File

@@ -4,6 +4,7 @@ using System.Linq;
using NzbDrone.Common.Extensions;
using NzbDrone.Core.Configuration;
using NzbDrone.Core.Localization;
using NzbDrone.Core.MediaCover;
using NzbDrone.Core.MediaFiles;
using NzbDrone.Core.Tags;
using NzbDrone.Core.Tv;
@@ -17,13 +18,15 @@ namespace NzbDrone.Core.Notifications.Webhook
private readonly IConfigService _configService;
protected readonly ILocalizationService _localizationService;
private readonly ITagRepository _tagRepository;
private readonly IMapCoversToLocal _mediaCoverService;
protected WebhookBase(IConfigFileProvider configFileProvider, IConfigService configService, ILocalizationService localizationService, ITagRepository tagRepository)
protected WebhookBase(IConfigFileProvider configFileProvider, IConfigService configService, ILocalizationService localizationService, ITagRepository tagRepository, IMapCoversToLocal mediaCoverService)
{
_configFileProvider = configFileProvider;
_configService = configService;
_localizationService = localizationService;
_tagRepository = tagRepository;
_mediaCoverService = mediaCoverService;
}
protected WebhookGrabPayload BuildOnGrabPayload(GrabMessage message)
@@ -36,7 +39,7 @@ namespace NzbDrone.Core.Notifications.Webhook
EventType = WebhookEventType.Grab,
InstanceName = _configFileProvider.InstanceName,
ApplicationUrl = _configService.ApplicationUrl,
Series = new WebhookSeries(message.Series, GetTagLabels(message.Series)),
Series = GetSeries(message.Series),
Episodes = remoteEpisode.Episodes.ConvertAll(x => new WebhookEpisode(x)),
Release = new WebhookRelease(quality, remoteEpisode),
DownloadClient = message.DownloadClientName,
@@ -55,7 +58,7 @@ namespace NzbDrone.Core.Notifications.Webhook
EventType = WebhookEventType.Download,
InstanceName = _configFileProvider.InstanceName,
ApplicationUrl = _configService.ApplicationUrl,
Series = new WebhookSeries(message.Series, GetTagLabels(message.Series)),
Series = GetSeries(message.Series),
Episodes = episodeFile.Episodes.Value.ConvertAll(x => new WebhookEpisode(x)),
EpisodeFile = new WebhookEpisodeFile(episodeFile),
Release = new WebhookGrabbedRelease(message.Release),
@@ -85,7 +88,7 @@ namespace NzbDrone.Core.Notifications.Webhook
EventType = WebhookEventType.EpisodeFileDelete,
InstanceName = _configFileProvider.InstanceName,
ApplicationUrl = _configService.ApplicationUrl,
Series = new WebhookSeries(deleteMessage.Series, GetTagLabels(deleteMessage.Series)),
Series = GetSeries(deleteMessage.Series),
Episodes = deleteMessage.EpisodeFile.Episodes.Value.ConvertAll(x => new WebhookEpisode(x)),
EpisodeFile = new WebhookEpisodeFile(deleteMessage.EpisodeFile),
DeleteReason = deleteMessage.Reason
@@ -99,7 +102,7 @@ namespace NzbDrone.Core.Notifications.Webhook
EventType = WebhookEventType.SeriesAdd,
InstanceName = _configFileProvider.InstanceName,
ApplicationUrl = _configService.ApplicationUrl,
Series = new WebhookSeries(addMessage.Series, GetTagLabels(addMessage.Series)),
Series = GetSeries(addMessage.Series),
};
}
@@ -110,7 +113,7 @@ namespace NzbDrone.Core.Notifications.Webhook
EventType = WebhookEventType.SeriesDelete,
InstanceName = _configFileProvider.InstanceName,
ApplicationUrl = _configService.ApplicationUrl,
Series = new WebhookSeries(deleteMessage.Series, GetTagLabels(deleteMessage.Series)),
Series = GetSeries(deleteMessage.Series),
DeletedFiles = deleteMessage.DeletedFiles
};
}
@@ -122,7 +125,7 @@ namespace NzbDrone.Core.Notifications.Webhook
EventType = WebhookEventType.Rename,
InstanceName = _configFileProvider.InstanceName,
ApplicationUrl = _configService.ApplicationUrl,
Series = new WebhookSeries(series, GetTagLabels(series)),
Series = GetSeries(series),
RenamedEpisodeFiles = renamedFiles.ConvertAll(x => new WebhookRenamedEpisodeFile(x))
};
}
@@ -175,7 +178,7 @@ namespace NzbDrone.Core.Notifications.Webhook
EventType = WebhookEventType.ManualInteractionRequired,
InstanceName = _configFileProvider.InstanceName,
ApplicationUrl = _configService.ApplicationUrl,
Series = new WebhookSeries(message.Series, GetTagLabels(message.Series)),
Series = GetSeries(message.Series),
Episodes = remoteEpisode.Episodes.ConvertAll(x => new WebhookEpisode(x)),
DownloadInfo = new WebhookDownloadClientItem(quality, message.TrackedDownload.DownloadItem),
DownloadClient = message.DownloadClientInfo?.Name,
@@ -216,6 +219,13 @@ namespace NzbDrone.Core.Notifications.Webhook
};
}
private WebhookSeries GetSeries(Series series)
{
_mediaCoverService.ConvertToLocalUrls(series.Id, series.Images);
return new WebhookSeries(series, GetTagLabels(series));
}
private List<string> GetTagLabels(Series series)
{
return _tagRepository.GetTags(series.Tags)

View File

@@ -0,0 +1,18 @@
using NzbDrone.Core.MediaCover;
namespace NzbDrone.Core.Notifications.Webhook
{
public class WebhookImage
{
public MediaCoverTypes CoverType { get; set; }
public string Url { get; set; }
public string RemoteUrl { get; set; }
public WebhookImage(MediaCover.MediaCover image)
{
CoverType = image.CoverType;
RemoteUrl = image.RemoteUrl;
Url = image.Url;
}
}
}

View File

@@ -1,4 +1,5 @@
using System.Collections.Generic;
using System.Linq;
using NzbDrone.Core.Tv;
namespace NzbDrone.Core.Notifications.Webhook
@@ -11,9 +12,12 @@ namespace NzbDrone.Core.Notifications.Webhook
public string Path { get; set; }
public int TvdbId { get; set; }
public int TvMazeId { get; set; }
public int TmdbId { get; set; }
public string ImdbId { get; set; }
public SeriesTypes Type { get; set; }
public int Year { get; set; }
public List<string> Genres { get; set; }
public List<WebhookImage> Images { get; set; }
public List<string> Tags { get; set; }
public WebhookSeries()
@@ -28,9 +32,12 @@ namespace NzbDrone.Core.Notifications.Webhook
Path = series.Path;
TvdbId = series.TvdbId;
TvMazeId = series.TvMazeId;
TmdbId = series.TmdbId;
ImdbId = series.ImdbId;
Type = series.SeriesType;
Year = series.Year;
Genres = series.Genres;
Images = series.Images.Select(i => new WebhookImage(i)).ToList();
Tags = tags;
}
}

View File

@@ -642,7 +642,7 @@ namespace NzbDrone.Core.Organizer
new Dictionary<string, int>(FileNameBuilderTokenEqualityComparer.Instance)
{
{ MediaInfoVideoDynamicRangeToken, 5 },
{ MediaInfoVideoDynamicRangeTypeToken, 10 }
{ MediaInfoVideoDynamicRangeTypeToken, 11 }
};
private void AddMediaInfoTokens(Dictionary<string, Func<TokenMatch, string>> tokenHandlers, EpisodeFile episodeFile)
@@ -715,6 +715,7 @@ namespace NzbDrone.Core.Organizer
tokenHandlers["{ImdbId}"] = m => series.ImdbId ?? string.Empty;
tokenHandlers["{TvdbId}"] = m => series.TvdbId.ToString();
tokenHandlers["{TvMazeId}"] = m => series.TvMazeId > 0 ? series.TvMazeId.ToString() : string.Empty;
tokenHandlers["{TmdbId}"] = m => series.TmdbId > 0 ? series.TmdbId.ToString() : string.Empty;
}
private string GetCustomFormatsToken(List<CustomFormat> customFormats, string filter)

View File

@@ -48,7 +48,8 @@ namespace NzbDrone.Core.Organizer
Year = 2010,
ImdbId = "tt12345",
TvdbId = 12345,
TvMazeId = 54321
TvMazeId = 54321,
TmdbId = 11223
};
_dailySeries = new Series
@@ -58,7 +59,8 @@ namespace NzbDrone.Core.Organizer
Year = 2010,
ImdbId = "tt12345",
TvdbId = 12345,
TvMazeId = 54321
TvMazeId = 54321,
TmdbId = 11223
};
_animeSeries = new Series
@@ -68,7 +70,8 @@ namespace NzbDrone.Core.Organizer
Year = 2010,
ImdbId = "tt12345",
TvdbId = 12345,
TvMazeId = 54321
TvMazeId = 54321,
TmdbId = 11223
};
_episode1 = new Episode

View File

@@ -20,7 +20,7 @@ namespace NzbDrone.Core.Parser
new RegexReplace(@".*?[_. ](S\d{2}(?:E\d{2,4})*[_. ].*)", "$1", RegexOptions.Compiled | RegexOptions.IgnoreCase)
};
private static readonly Regex LanguageRegex = new Regex(@"(?:\W|_)(?<english>\b(?:ing|eng)\b)|(?<italian>\b(?:ita|italian)\b)|(?<german>german\b|videomann|ger[. ]dub)|(?<flemish>flemish)|(?<greek>greek)|(?<french>(?:\W|_)(?:FR|VF|VF2|VFF|VFQ|TRUEFRENCH)(?:\W|_))|(?<russian>\b(?:rus|ru)\b)|(?<hungarian>\b(?:HUNDUB|HUN)\b)|(?<hebrew>\bHebDub\b)|(?<polish>\b(?:PL\W?DUB|DUB\W?PL|LEK\W?PL|PL\W?LEK)\b)|(?<chinese>\[(?:CH[ST]|BIG5|GB)\]|简|繁|字幕)|(?<bulgarian>\bbgaudio\b)|(?<spanish>\b(?:español|castellano|esp|spa(?!\(Latino\)))\b)|(?<ukrainian>\b(?:\dx?)?(?:ukr))|(?<thai>\b(?:THAI)\b)|(?<romanian>\b(?:RoDubbed|ROMANIAN)\b)|(?<catalan>[-,. ]cat[. ](?:DD|subs)|\b(?:catalan|catalán)\b)|(?<latvian>\b(?:lat|lav|lv)\b)|(?<turkish>\b(?:tur)\b)",
private static readonly Regex LanguageRegex = new Regex(@"(?:\W|_)(?<english>\b(?:ing|eng)\b)|(?<italian>\b(?:ita|italian)\b)|(?<german>german\b|videomann|ger[. ]dub)|(?<flemish>flemish)|(?<greek>greek)|(?<french>(?:\W|_)(?:FR|VF|VF2|VFF|VFI|VFQ|TRUEFRENCH)(?:\W|_))|(?<russian>\b(?:rus|ru)\b)|(?<hungarian>\b(?:HUNDUB|HUN)\b)|(?<hebrew>\bHebDub\b)|(?<polish>\b(?:PL\W?DUB|DUB\W?PL|LEK\W?PL|PL\W?LEK)\b)|(?<chinese>\[(?:CH[ST]|BIG5|GB)\]|简|繁|字幕)|(?<bulgarian>\bbgaudio\b)|(?<spanish>\b(?:español|castellano|esp|spa(?!\(Latino\)))\b)|(?<ukrainian>\b(?:\dx?)?(?:ukr))|(?<thai>\b(?:THAI)\b)|(?<romanian>\b(?:RoDubbed|ROMANIAN)\b)|(?<catalan>[-,. ]cat[. ](?:DD|subs)|\b(?:catalan|catalán)\b)|(?<latvian>\b(?:lat|lav|lv)\b)|(?<turkish>\b(?:tur)\b)",
RegexOptions.IgnoreCase | RegexOptions.Compiled);
private static readonly Regex CaseSensitiveLanguageRegex = new Regex(@"(?:(?i)(?<!SUB[\W|_|^]))(?:(?<lithuanian>\bLT\b)|(?<czech>\bCZ\b)|(?<polish>\bPL\b)|(?<bulgarian>\bBG\b)|(?<slovak>\bSK\b))(?:(?i)(?![\W|_|^]SUB))",

View File

@@ -83,7 +83,7 @@ namespace NzbDrone.Core.Parser
RegexOptions.IgnoreCase | RegexOptions.Compiled),
// Anime - [SubGroup] Title Season+Episode
new Regex(@"^(?:\[(?<subgroup>.+?)\](?:_|-|\s|\.)?)(?<title>.+?)(?:[-_\W](?<![()\[!]))+(?:S?(?<season>(?<!\d+)\d{1,2}(?!\d+))(?:(?:[ex]|\W[ex]){1,2}(?<episode>\d{2}(?!\d+)))+)(?:[_. ](?!\d+)).*?(?<hash>[(\[]\w{8}[)\]])?$",
new Regex(@"^(?:\[(?<subgroup>.+?)\](?:_|-|\s|\.)?)(?<title>.+?)(?:[-_\W](?<![()\[!]))+(?:S?(?<season>(?<!\d+)\d{1,2}(?!\d+))(?:(?:[ex]|\W[ex]){1,2}(?<episode>\d{2}(?!\d+)))+)(?:v\d+)?(?:[_. ](?!\d+)).*?(?<hash>[(\[]\w{8}[)\]])?$",
RegexOptions.IgnoreCase | RegexOptions.Compiled),
// Anime - [SubGroup] Title Episode Absolute Episode Number ([SubGroup] Series Title Episode 01)
@@ -114,6 +114,10 @@ namespace NzbDrone.Core.Parser
new Regex(@"^\[(?<subgroup>.+?)\][-_. ]?(?<title>[^]]+?)(?:[-_. ]{3}?(?<absoluteepisode>\d{2}(\.\d{1,2})?(?!-?\d+|-[a-z]+)))+(?:[-_. ]+(?<special>special|ova|ovd))?.*?(?<hash>[(\[]\w{8}[)\]])?(?:$|\.mkv)",
RegexOptions.IgnoreCase | RegexOptions.Compiled),
// Anime - [SubGroup] Title with trailing number S## (Full season)
new Regex(@"^\[(?<subgroup>.+?)\][-_. ]?(?<title>.+?)[-_. ]+(?:S(?<season>(?<!\d+)(?:\d{1,2}|\d{4})(?![ex]?\d+))).+?(?:$|\.mkv)",
RegexOptions.IgnoreCase | RegexOptions.Compiled),
// Anime - [SubGroup] Title with trailing number Absolute Episode Number
new Regex(@"^\[(?<subgroup>.+?)\][-_. ]?(?<title>[^-]+?)(?:(?<![-_. ]|\b[0]\d+)[_ ]+)(?:[-_. ]?(?<absoluteepisode>\d{3}(\.\d{1,2})?(?!\d+|-[a-z]+)))+(?:[-_. ]+(?<special>special|ova|ovd))?.*?(?<hash>[(\[]\w{8}[)\]])?(?:$|\.mkv)",
RegexOptions.IgnoreCase | RegexOptions.Compiled),
@@ -510,7 +514,7 @@ namespace NzbDrone.Core.Parser
// Valid TLDs http://data.iana.org/TLD/tlds-alpha-by-domain.txt
private static readonly RegexReplace WebsitePrefixRegex = new RegexReplace(@"^(?:\[\s*)?(?:www\.)?[-a-z0-9-]{1,256}\.(?<!Naruto-Kun\.)(?:[a-z]{2,6}\.[a-z]{2,6}|xn--[a-z0-9-]{4,}|[a-z]{2,})\b(?:\s*\]|[ -]{2,})[ -]*",
private static readonly RegexReplace WebsitePrefixRegex = new RegexReplace(@"^(?:(?:\[|\()\s*)?(?:www\.)?[-a-z0-9-]{1,256}\.(?<!Naruto-Kun\.)(?:[a-z]{2,6}\.[a-z]{2,6}|xn--[a-z0-9-]{4,}|[a-z]{2,})\b(?:\s*(?:\]|\))|[ -]{2,})[ -]*",
string.Empty,
RegexOptions.IgnoreCase | RegexOptions.Compiled);
@@ -1205,7 +1209,7 @@ namespace NzbDrone.Core.Parser
}
}
if (lastSeasonEpisodeStringIndex != releaseTitle.Length)
if (lastSeasonEpisodeStringIndex < releaseTitle.Length)
{
result.ReleaseTokens = releaseTitle.Substring(lastSeasonEpisodeStringIndex);
}
@@ -1285,7 +1289,7 @@ namespace NzbDrone.Core.Parser
private static int ParseNumber(string value)
{
var normalized = value.Normalize(NormalizationForm.FormKC);
var normalized = ConvertToNumerals(value.Normalize(NormalizationForm.FormKC));
if (int.TryParse(normalized, out var number))
{
@@ -1304,7 +1308,7 @@ namespace NzbDrone.Core.Parser
private static decimal ParseDecimal(string value)
{
var normalized = value.Normalize(NormalizationForm.FormKC);
var normalized = ConvertToNumerals(value.Normalize(NormalizationForm.FormKC));
if (decimal.TryParse(normalized, NumberStyles.Float, CultureInfo.InvariantCulture, out var number))
{
@@ -1313,5 +1317,24 @@ namespace NzbDrone.Core.Parser
throw new FormatException(string.Format("{0} isn't a number", value));
}
private static string ConvertToNumerals(string input)
{
var result = new StringBuilder(input.Length);
foreach (var c in input.ToCharArray())
{
if (char.IsNumber(c))
{
result.Append(char.GetNumericValue(c));
}
else
{
result.Append(c);
}
}
return result.ToString();
}
}
}

View File

@@ -20,7 +20,7 @@ namespace NzbDrone.Core.Queue
public class QueueService : IQueueService, IHandle<TrackedDownloadRefreshedEvent>
{
private readonly IEventAggregator _eventAggregator;
private static List<Queue> _queue = new List<Queue>();
private static List<Queue> _queue = new ();
public QueueService(IEventAggregator eventAggregator)
{
@@ -96,8 +96,11 @@ namespace NzbDrone.Core.Queue
public void Handle(TrackedDownloadRefreshedEvent message)
{
_queue = message.TrackedDownloads.OrderBy(c => c.DownloadItem.RemainingTime).SelectMany(MapQueue)
.ToList();
_queue = message.TrackedDownloads
.Where(t => t.IsTrackable)
.OrderBy(c => c.DownloadItem.RemainingTime)
.SelectMany(MapQueue)
.ToList();
_eventAggregator.PublishEvent(new QueueUpdatedEvent());
}

View File

@@ -37,6 +37,12 @@ namespace NzbDrone.Core.Tv
private void MoveSingleSeries(Series series, string sourcePath, string destinationPath, int? index = null, int? total = null)
{
if (!sourcePath.IsPathValid(PathValidationType.CurrentOs))
{
_logger.Warn("Folder '{0}' for '{1}' is invalid, unable to move series. Try moving files manually", sourcePath, series.Title);
return;
}
if (!_diskProvider.FolderExists(sourcePath))
{
_logger.Debug("Folder '{0}' for '{1}' does not exist, not moving.", sourcePath, series.Title);

View File

@@ -92,6 +92,7 @@ namespace NzbDrone.Core.Tv
series.TitleSlug = seriesInfo.TitleSlug;
series.TvRageId = seriesInfo.TvRageId;
series.TvMazeId = seriesInfo.TvMazeId;
series.TmdbId = seriesInfo.TmdbId;
series.ImdbId = seriesInfo.ImdbId;
series.AirTime = seriesInfo.AirTime;
series.Overview = seriesInfo.Overview;

View File

@@ -23,6 +23,7 @@ namespace NzbDrone.Core.Tv
public int TvRageId { get; set; }
public int TvMazeId { get; set; }
public string ImdbId { get; set; }
public int TmdbId { get; set; }
public string Title { get; set; }
public string CleanTitle { get; set; }
public string SortTitle { get; set; }

View File

@@ -58,12 +58,12 @@ namespace NzbDrone.Core.Tv
}
else
{
if (series.AddOptions.SearchForMissingEpisodes)
if (addOptions.SearchForMissingEpisodes)
{
_commandQueueManager.Push(new MissingEpisodeSearchCommand(series.Id));
}
if (series.AddOptions.SearchForCutoffUnmetEpisodes)
if (addOptions.SearchForCutoffUnmetEpisodes)
{
_commandQueueManager.Push(new CutoffUnmetEpisodeSearchCommand(series.Id));
}

View File

@@ -1,4 +1,5 @@
using FluentValidation.Validators;
using FluentValidation.Validators;
using NzbDrone.Common.Disk;
using NzbDrone.Common.Extensions;
using NzbDrone.Core.RootFolders;
@@ -24,7 +25,7 @@ namespace NzbDrone.Core.Validation.Paths
context.MessageFormatter.AppendArgument("path", context.PropertyValue.ToString());
return !_rootFolderService.All().Exists(r => r.Path.PathEquals(context.PropertyValue.ToString()));
return !_rootFolderService.All().Exists(r => r.Path.IsPathValid(PathValidationType.CurrentOs) && r.Path.PathEquals(context.PropertyValue.ToString()));
}
}
}

View File

@@ -1,5 +1,6 @@
using System.Linq;
using FluentValidation.Validators;
using NzbDrone.Common.Disk;
using NzbDrone.Common.Extensions;
using NzbDrone.Core.Tv;
@@ -28,7 +29,10 @@ namespace NzbDrone.Core.Validation.Paths
dynamic instance = context.ParentContext.InstanceToValidate;
var instanceId = (int)instance.Id;
return !_seriesService.GetAllSeriesPaths().Any(s => s.Value.PathEquals(context.PropertyValue.ToString()) && s.Key != instanceId);
// Skip the path for this series and any invalid paths
return !_seriesService.GetAllSeriesPaths().Any(s => s.Key != instanceId &&
s.Value.IsPathValid(PathValidationType.CurrentOs) &&
s.Value.PathEquals(context.PropertyValue.ToString()));
}
}
}

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