mirror of
https://github.com/Sonarr/Sonarr.git
synced 2026-04-19 21:46:43 -04:00
Compare commits
56 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 143ccb1e2a | |||
| 29480d9544 | |||
| 6de536a7ad | |||
| bce848facf | |||
| ea4fe392a0 | |||
| 45fe585944 | |||
| a0d2933134 | |||
| 4c622fd412 | |||
| fb060730c7 | |||
| 6d5ff9c4d6 | |||
| 63bed3e670 | |||
| e684c10432 | |||
| d2509798e9 | |||
| 6c39855ebe | |||
| a30e9da767 | |||
| f8e81396d4 | |||
| 7fccf590a8 | |||
| e1b937e8d5 | |||
| c331c8bd11 | |||
| 52b72925f9 | |||
| 378fedcd9d | |||
| a90ab1a8fd | |||
| 0edc5ba99a | |||
| ea54ade9bf | |||
| e07eb05e8b | |||
| d9b771ab0b | |||
| 6b08e849b8 | |||
| 9c1f48ebc9 | |||
| fd3dd1ab7d | |||
| 11e5c5a11b | |||
| 48f0291884 | |||
| af0e55aef4 | |||
| 39a439eb4c | |||
| 66940b283b | |||
| 2a662afaef | |||
| 62a9c2519b | |||
| ca372bee25 | |||
| 0904a0737e | |||
| 70bc26dc19 | |||
| a2e0002a08 | |||
| d7ceb11a64 | |||
| cc5b5463f2 | |||
| 9b4ff657af | |||
| aea50fa47e | |||
| 05edd44ed6 | |||
| 4440aa3cac | |||
| 084fcc2295 | |||
| 536ff142c3 | |||
| 627b2a4289 | |||
| 9734c2d144 | |||
| c7c1e3ac9e | |||
| 429444d085 | |||
| 5cb649e9d8 | |||
| cac7d239ea | |||
| 3940059ea3 | |||
| 20d00fe88c |
@@ -22,7 +22,7 @@ env:
|
|||||||
FRAMEWORK: net6.0
|
FRAMEWORK: net6.0
|
||||||
RAW_BRANCH_NAME: ${{ github.head_ref || github.ref_name }}
|
RAW_BRANCH_NAME: ${{ github.head_ref || github.ref_name }}
|
||||||
SONARR_MAJOR_VERSION: 4
|
SONARR_MAJOR_VERSION: 4
|
||||||
VERSION: 4.0.4
|
VERSION: 4.0.5
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
backend:
|
backend:
|
||||||
@@ -48,7 +48,7 @@ jobs:
|
|||||||
|
|
||||||
echo "SDK_PATH=${{ env.DOTNET_ROOT }}/sdk/${DOTNET_VERSION}" >> "$GITHUB_ENV"
|
echo "SDK_PATH=${{ env.DOTNET_ROOT }}/sdk/${DOTNET_VERSION}" >> "$GITHUB_ENV"
|
||||||
echo "SONARR_VERSION=$SONARR_VERSION" >> "$GITHUB_ENV"
|
echo "SONARR_VERSION=$SONARR_VERSION" >> "$GITHUB_ENV"
|
||||||
echo "BRANCH=${RAW_BRANCH_NAME/\//-}" >> "$GITHUB_ENV"
|
echo "BRANCH=${RAW_BRANCH_NAME//\//-}" >> "$GITHUB_ENV"
|
||||||
|
|
||||||
echo "framework=${{ env.FRAMEWORK }}" >> "$GITHUB_OUTPUT"
|
echo "framework=${{ env.FRAMEWORK }}" >> "$GITHUB_OUTPUT"
|
||||||
echo "major_version=${{ env.SONARR_MAJOR_VERSION }}" >> "$GITHUB_OUTPUT"
|
echo "major_version=${{ env.SONARR_MAJOR_VERSION }}" >> "$GITHUB_OUTPUT"
|
||||||
|
|||||||
@@ -8,5 +8,6 @@ jobs:
|
|||||||
contents: read
|
contents: read
|
||||||
pull-requests: write
|
pull-requests: write
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
if: github.repository == 'Sonarr/Sonarr'
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/labeler@v5
|
- uses: actions/labeler@v5
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ on:
|
|||||||
jobs:
|
jobs:
|
||||||
lock:
|
lock:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
if: github.repository == 'Sonarr/Sonarr'
|
||||||
steps:
|
steps:
|
||||||
- uses: dessant/lock-threads@v5
|
- uses: dessant/lock-threads@v5
|
||||||
with:
|
with:
|
||||||
|
|||||||
@@ -25,17 +25,23 @@ slnFile=src/Sonarr.sln
|
|||||||
|
|
||||||
platform=Posix
|
platform=Posix
|
||||||
|
|
||||||
|
if [ "$PLATFORM" = "Windows" ]; then
|
||||||
|
application=Sonarr.Console.dll
|
||||||
|
else
|
||||||
|
application=Sonarr.dll
|
||||||
|
fi
|
||||||
|
|
||||||
dotnet clean $slnFile -c Debug
|
dotnet clean $slnFile -c Debug
|
||||||
dotnet clean $slnFile -c Release
|
dotnet clean $slnFile -c Release
|
||||||
|
|
||||||
dotnet msbuild -restore $slnFile -p:Configuration=Debug -p:Platform=$platform -p:RuntimeIdentifiers=$RUNTIME -t:PublishAllRids
|
dotnet msbuild -restore $slnFile -p:Configuration=Debug -p:Platform=$platform -p:RuntimeIdentifiers=$RUNTIME -t:PublishAllRids
|
||||||
|
|
||||||
dotnet new tool-manifest
|
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
|
kill %1
|
||||||
|
|
||||||
|
|||||||
@@ -217,6 +217,7 @@ class Queue extends Component {
|
|||||||
>
|
>
|
||||||
<TableOptionsModalWrapper
|
<TableOptionsModalWrapper
|
||||||
columns={columns}
|
columns={columns}
|
||||||
|
maxPageSize={200}
|
||||||
{...otherProps}
|
{...otherProps}
|
||||||
optionsComponent={QueueOptionsConnector}
|
optionsComponent={QueueOptionsConnector}
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -70,6 +70,11 @@ function QueueStatus(props) {
|
|||||||
iconName = icons.DOWNLOADED;
|
iconName = icons.DOWNLOADED;
|
||||||
title = translate('Downloaded');
|
title = translate('Downloaded');
|
||||||
|
|
||||||
|
if (trackedDownloadState === 'importBlocked') {
|
||||||
|
title += ` - ${translate('UnableToImportAutomatically')}`;
|
||||||
|
iconKind = kinds.WARNING;
|
||||||
|
}
|
||||||
|
|
||||||
if (trackedDownloadState === 'importPending') {
|
if (trackedDownloadState === 'importPending') {
|
||||||
title += ` - ${translate('WaitingToImport')}`;
|
title += ` - ${translate('WaitingToImport')}`;
|
||||||
iconKind = kinds.PURPLE;
|
iconKind = kinds.PURPLE;
|
||||||
|
|||||||
@@ -24,7 +24,11 @@ function TimeleftCell(props) {
|
|||||||
} = props;
|
} = props;
|
||||||
|
|
||||||
if (status === 'delay') {
|
if (status === 'delay') {
|
||||||
const date = getRelativeDate(estimatedCompletionTime, shortDateFormat, showRelativeDates);
|
const date = getRelativeDate({
|
||||||
|
date: estimatedCompletionTime,
|
||||||
|
shortDateFormat,
|
||||||
|
showRelativeDates
|
||||||
|
});
|
||||||
const time = formatTime(estimatedCompletionTime, timeFormat, { includeMinuteZero: true });
|
const time = formatTime(estimatedCompletionTime, timeFormat, { includeMinuteZero: true });
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -40,7 +44,11 @@ function TimeleftCell(props) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (status === 'downloadClientUnavailable') {
|
if (status === 'downloadClientUnavailable') {
|
||||||
const date = getRelativeDate(estimatedCompletionTime, shortDateFormat, showRelativeDates);
|
const date = getRelativeDate({
|
||||||
|
date: estimatedCompletionTime,
|
||||||
|
shortDateFormat,
|
||||||
|
showRelativeDates
|
||||||
|
});
|
||||||
const time = formatTime(estimatedCompletionTime, timeFormat, { includeMinuteZero: true });
|
const time = formatTime(estimatedCompletionTime, timeFormat, { includeMinuteZero: true });
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ class DayOfWeek extends Component {
|
|||||||
if (view === calendarViews.WEEK) {
|
if (view === calendarViews.WEEK) {
|
||||||
formatedDate = momentDate.format(calendarWeekColumnHeader);
|
formatedDate = momentDate.format(calendarWeekColumnHeader);
|
||||||
} else if (view === calendarViews.FORECAST) {
|
} else if (view === calendarViews.FORECAST) {
|
||||||
formatedDate = getRelativeDate(date, shortDateFormat, showRelativeDates);
|
formatedDate = getRelativeDate({ date, shortDateFormat, showRelativeDates });
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -271,26 +271,32 @@ class EnhancedSelectInput extends Component {
|
|||||||
this.setState({ isOpen: !this.state.isOpen });
|
this.setState({ isOpen: !this.state.isOpen });
|
||||||
};
|
};
|
||||||
|
|
||||||
onSelect = (value) => {
|
onSelect = (newValue) => {
|
||||||
if (Array.isArray(this.props.value)) {
|
const { name, value, values, onChange } = this.props;
|
||||||
let newValue = null;
|
const additionalProperties = values.find((v) => v.key === newValue)?.additionalProperties;
|
||||||
const index = this.props.value.indexOf(value);
|
|
||||||
|
if (Array.isArray(value)) {
|
||||||
|
let arrayValue = null;
|
||||||
|
const index = value.indexOf(newValue);
|
||||||
|
|
||||||
if (index === -1) {
|
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 {
|
} else {
|
||||||
newValue = [...this.props.value];
|
arrayValue = [...value];
|
||||||
newValue.splice(index, 1);
|
arrayValue.splice(index, 1);
|
||||||
}
|
}
|
||||||
this.props.onChange({
|
onChange({
|
||||||
name: this.props.name,
|
name,
|
||||||
value: newValue
|
value: arrayValue,
|
||||||
|
additionalProperties
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
this.setState({ isOpen: false });
|
this.setState({ isOpen: false });
|
||||||
|
|
||||||
this.props.onChange({
|
onChange({
|
||||||
name: this.props.name,
|
name,
|
||||||
value
|
value: newValue,
|
||||||
|
additionalProperties
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -485,7 +491,7 @@ class EnhancedSelectInput extends Component {
|
|||||||
values.map((v, index) => {
|
values.map((v, index) => {
|
||||||
const hasParent = v.parentKey !== undefined;
|
const hasParent = v.parentKey !== undefined;
|
||||||
const depth = hasParent ? 1 : 0;
|
const depth = hasParent ? 1 : 0;
|
||||||
const parentSelected = hasParent && value.includes(v.parentKey);
|
const parentSelected = hasParent && Array.isArray(value) && value.includes(v.parentKey);
|
||||||
return (
|
return (
|
||||||
<OptionComponent
|
<OptionComponent
|
||||||
key={v.key}
|
key={v.key}
|
||||||
|
|||||||
@@ -9,7 +9,8 @@ import EnhancedSelectInput from './EnhancedSelectInput';
|
|||||||
const importantFieldNames = [
|
const importantFieldNames = [
|
||||||
'baseUrl',
|
'baseUrl',
|
||||||
'apiPath',
|
'apiPath',
|
||||||
'apiKey'
|
'apiKey',
|
||||||
|
'authToken'
|
||||||
];
|
];
|
||||||
|
|
||||||
function getProviderDataKey(providerData) {
|
function getProviderDataKey(providerData) {
|
||||||
@@ -34,7 +35,9 @@ function getSelectOptions(items) {
|
|||||||
key: option.value,
|
key: option.value,
|
||||||
value: option.name,
|
value: option.name,
|
||||||
hint: option.hint,
|
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,
|
provider: PropTypes.string.isRequired,
|
||||||
providerData: PropTypes.object.isRequired,
|
providerData: PropTypes.object.isRequired,
|
||||||
name: PropTypes.string.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,
|
values: PropTypes.arrayOf(PropTypes.object).isRequired,
|
||||||
selectOptionsProviderAction: PropTypes.string,
|
selectOptionsProviderAction: PropTypes.string,
|
||||||
onChange: PropTypes.func.isRequired,
|
onChange: PropTypes.func.isRequired,
|
||||||
|
|||||||
@@ -1,5 +0,0 @@
|
|||||||
.input {
|
|
||||||
composes: input from '~Components/Form/TextInput.css';
|
|
||||||
|
|
||||||
font-family: $passwordFamily;
|
|
||||||
}
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
// This file is automatically generated.
|
|
||||||
// Please do not change this file!
|
|
||||||
interface CssExports {
|
|
||||||
'input': string;
|
|
||||||
}
|
|
||||||
export const cssExports: CssExports;
|
|
||||||
export default cssExports;
|
|
||||||
@@ -1,7 +1,5 @@
|
|||||||
import PropTypes from 'prop-types';
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import TextInput from './TextInput';
|
import TextInput from './TextInput';
|
||||||
import styles from './PasswordInput.css';
|
|
||||||
|
|
||||||
// Prevent a user from copying (or cutting) the password from the input
|
// Prevent a user from copying (or cutting) the password from the input
|
||||||
function onCopy(e) {
|
function onCopy(e) {
|
||||||
@@ -13,17 +11,14 @@ function PasswordInput(props) {
|
|||||||
return (
|
return (
|
||||||
<TextInput
|
<TextInput
|
||||||
{...props}
|
{...props}
|
||||||
|
type="password"
|
||||||
onCopy={onCopy}
|
onCopy={onCopy}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
PasswordInput.propTypes = {
|
PasswordInput.propTypes = {
|
||||||
className: PropTypes.string.isRequired
|
...TextInput.props
|
||||||
};
|
|
||||||
|
|
||||||
PasswordInput.defaultProps = {
|
|
||||||
className: styles.input
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export default PasswordInput;
|
export default PasswordInput;
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ function createCleanSeriesSelector() {
|
|||||||
tvdbId,
|
tvdbId,
|
||||||
tvMazeId,
|
tvMazeId,
|
||||||
imdbId,
|
imdbId,
|
||||||
|
tmdbId,
|
||||||
tags = []
|
tags = []
|
||||||
} = series;
|
} = series;
|
||||||
|
|
||||||
@@ -33,6 +34,7 @@ function createCleanSeriesSelector() {
|
|||||||
tvdbId,
|
tvdbId,
|
||||||
tvMazeId,
|
tvMazeId,
|
||||||
imdbId,
|
imdbId,
|
||||||
|
tmdbId,
|
||||||
firstCharacter: title.charAt(0).toLowerCase(),
|
firstCharacter: title.charAt(0).toLowerCase(),
|
||||||
tags: tags.reduce((acc, id) => {
|
tags: tags.reduce((acc, id) => {
|
||||||
const matchingTag = allTags.find((tag) => tag.id === id);
|
const matchingTag = allTags.find((tag) => tag.id === id);
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ function SeriesSearchResult(props) {
|
|||||||
tvdbId,
|
tvdbId,
|
||||||
tvMazeId,
|
tvMazeId,
|
||||||
imdbId,
|
imdbId,
|
||||||
|
tmdbId,
|
||||||
tags
|
tags
|
||||||
} = props;
|
} = props;
|
||||||
|
|
||||||
@@ -73,6 +74,14 @@ function SeriesSearchResult(props) {
|
|||||||
null
|
null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
match.key === 'tmdbId' && tmdbId ?
|
||||||
|
<div className={styles.alternateTitle}>
|
||||||
|
TmdbId: {tmdbId}
|
||||||
|
</div> :
|
||||||
|
null
|
||||||
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
tag ?
|
tag ?
|
||||||
<div className={styles.tagContainer}>
|
<div className={styles.tagContainer}>
|
||||||
@@ -97,6 +106,7 @@ SeriesSearchResult.propTypes = {
|
|||||||
tvdbId: PropTypes.number,
|
tvdbId: PropTypes.number,
|
||||||
tvMazeId: PropTypes.number,
|
tvMazeId: PropTypes.number,
|
||||||
imdbId: PropTypes.string,
|
imdbId: PropTypes.string,
|
||||||
|
tmdbId: PropTypes.number,
|
||||||
tags: PropTypes.arrayOf(PropTypes.object).isRequired,
|
tags: PropTypes.arrayOf(PropTypes.object).isRequired,
|
||||||
match: PropTypes.object.isRequired
|
match: PropTypes.object.isRequired
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ const fuseOptions = {
|
|||||||
'tvdbId',
|
'tvdbId',
|
||||||
'tvMazeId',
|
'tvMazeId',
|
||||||
'imdbId',
|
'imdbId',
|
||||||
|
'tmdbId',
|
||||||
'tags.label'
|
'tags.label'
|
||||||
]
|
]
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -244,7 +244,7 @@ class SignalRConnector extends Component {
|
|||||||
handleWantedCutoff = (body) => {
|
handleWantedCutoff = (body) => {
|
||||||
if (body.action === 'updated') {
|
if (body.action === 'updated') {
|
||||||
this.props.dispatchUpdateItem({
|
this.props.dispatchUpdateItem({
|
||||||
section: 'cutoffUnmet',
|
section: 'wanted.cutoffUnmet',
|
||||||
updateOnly: true,
|
updateOnly: true,
|
||||||
...body.resource
|
...body.resource
|
||||||
});
|
});
|
||||||
@@ -254,7 +254,7 @@ class SignalRConnector extends Component {
|
|||||||
handleWantedMissing = (body) => {
|
handleWantedMissing = (body) => {
|
||||||
if (body.action === 'updated') {
|
if (body.action === 'updated') {
|
||||||
this.props.dispatchUpdateItem({
|
this.props.dispatchUpdateItem({
|
||||||
section: 'missing',
|
section: 'wanted.missing',
|
||||||
updateOnly: true,
|
updateOnly: true,
|
||||||
...body.resource
|
...body.resource
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ class RelativeDateCell extends PureComponent {
|
|||||||
className,
|
className,
|
||||||
date,
|
date,
|
||||||
includeSeconds,
|
includeSeconds,
|
||||||
|
includeTime,
|
||||||
showRelativeDates,
|
showRelativeDates,
|
||||||
shortDateFormat,
|
shortDateFormat,
|
||||||
longDateFormat,
|
longDateFormat,
|
||||||
@@ -39,7 +40,7 @@ class RelativeDateCell extends PureComponent {
|
|||||||
title={formatDateTime(date, longDateFormat, timeFormat, { includeSeconds, includeRelativeDay: !showRelativeDates })}
|
title={formatDateTime(date, longDateFormat, timeFormat, { includeSeconds, includeRelativeDay: !showRelativeDates })}
|
||||||
{...otherProps}
|
{...otherProps}
|
||||||
>
|
>
|
||||||
{getRelativeDate(date, shortDateFormat, showRelativeDates, { timeFormat, includeSeconds, timeForToday: true })}
|
{getRelativeDate({ date, shortDateFormat, showRelativeDates, timeFormat, includeSeconds, includeTime, timeForToday: true })}
|
||||||
</Component>
|
</Component>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -49,6 +50,7 @@ RelativeDateCell.propTypes = {
|
|||||||
className: PropTypes.string.isRequired,
|
className: PropTypes.string.isRequired,
|
||||||
date: PropTypes.string,
|
date: PropTypes.string,
|
||||||
includeSeconds: PropTypes.bool.isRequired,
|
includeSeconds: PropTypes.bool.isRequired,
|
||||||
|
includeTime: PropTypes.bool.isRequired,
|
||||||
showRelativeDates: PropTypes.bool.isRequired,
|
showRelativeDates: PropTypes.bool.isRequired,
|
||||||
shortDateFormat: PropTypes.string.isRequired,
|
shortDateFormat: PropTypes.string.isRequired,
|
||||||
longDateFormat: PropTypes.string.isRequired,
|
longDateFormat: PropTypes.string.isRequired,
|
||||||
@@ -60,6 +62,7 @@ RelativeDateCell.propTypes = {
|
|||||||
RelativeDateCell.defaultProps = {
|
RelativeDateCell.defaultProps = {
|
||||||
className: styles.cell,
|
className: styles.cell,
|
||||||
includeSeconds: false,
|
includeSeconds: false,
|
||||||
|
includeTime: false,
|
||||||
component: TableRowCell
|
component: TableRowCell
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -49,11 +49,12 @@ class TableOptionsModal extends Component {
|
|||||||
|
|
||||||
onPageSizeChange = ({ value }) => {
|
onPageSizeChange = ({ value }) => {
|
||||||
let pageSizeError = null;
|
let pageSizeError = null;
|
||||||
|
const maxPageSize = this.props.maxPageSize ?? 250;
|
||||||
|
|
||||||
if (value < 5) {
|
if (value < 5) {
|
||||||
pageSizeError = translate('TablePageSizeMinimum', { minimumValue: '5' });
|
pageSizeError = translate('TablePageSizeMinimum', { minimumValue: '5' });
|
||||||
} else if (value > 250) {
|
} else if (value > maxPageSize) {
|
||||||
pageSizeError = translate('TablePageSizeMaximum', { maximumValue: '250' });
|
pageSizeError = translate('TablePageSizeMaximum', { maximumValue: `${maxPageSize}` });
|
||||||
} else {
|
} else {
|
||||||
this.props.onTableOptionChange({ pageSize: value });
|
this.props.onTableOptionChange({ pageSize: value });
|
||||||
}
|
}
|
||||||
@@ -248,6 +249,7 @@ TableOptionsModal.propTypes = {
|
|||||||
isOpen: PropTypes.bool.isRequired,
|
isOpen: PropTypes.bool.isRequired,
|
||||||
columns: PropTypes.arrayOf(PropTypes.object).isRequired,
|
columns: PropTypes.arrayOf(PropTypes.object).isRequired,
|
||||||
pageSize: PropTypes.number,
|
pageSize: PropTypes.number,
|
||||||
|
maxPageSize: PropTypes.number,
|
||||||
canModifyColumns: PropTypes.bool.isRequired,
|
canModifyColumns: PropTypes.bool.isRequired,
|
||||||
optionsComponent: PropTypes.elementType,
|
optionsComponent: PropTypes.elementType,
|
||||||
onTableOptionChange: PropTypes.func.isRequired,
|
onTableOptionChange: PropTypes.func.isRequired,
|
||||||
|
|||||||
@@ -25,14 +25,3 @@
|
|||||||
font-family: 'Ubuntu Mono';
|
font-family: 'Ubuntu Mono';
|
||||||
src: url('UbuntuMono-Regular.eot?#iefix&v=1.3.0') format('embedded-opentype'), url('UbuntuMono-Regular.woff?v=1.3.0') format('woff'), url('UbuntuMono-Regular.ttf?v=1.3.0') format('truetype');
|
src: url('UbuntuMono-Regular.eot?#iefix&v=1.3.0') format('embedded-opentype'), url('UbuntuMono-Regular.woff?v=1.3.0') format('woff'), url('UbuntuMono-Regular.ttf?v=1.3.0') format('truetype');
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* text-security-disc
|
|
||||||
*/
|
|
||||||
|
|
||||||
@font-face {
|
|
||||||
font-weight: normal;
|
|
||||||
font-style: normal;
|
|
||||||
font-family: 'text-security-disc';
|
|
||||||
src: url('text-security-disc.woff?v=1.3.0') format('woff'), url('text-security-disc.ttf?v=1.3.0') format('truetype');
|
|
||||||
}
|
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
@@ -111,6 +111,8 @@ class EpisodeHistoryRow extends Component {
|
|||||||
|
|
||||||
<RelativeDateCellConnector
|
<RelativeDateCellConnector
|
||||||
date={date}
|
date={date}
|
||||||
|
includeSeconds={true}
|
||||||
|
includeTime={true}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<TableRowCell className={styles.actions}>
|
<TableRowCell className={styles.actions}>
|
||||||
|
|||||||
@@ -14,4 +14,9 @@
|
|||||||
.deleteFilesMessage {
|
.deleteFilesMessage {
|
||||||
margin-top: 20px;
|
margin-top: 20px;
|
||||||
color: var(--dangerColor);
|
color: var(--dangerColor);
|
||||||
|
|
||||||
|
.deleteCount {
|
||||||
|
margin-top: 20px;
|
||||||
|
color: var(--warningColor);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
// This file is automatically generated.
|
// This file is automatically generated.
|
||||||
// Please do not change this file!
|
// Please do not change this file!
|
||||||
interface CssExports {
|
interface CssExports {
|
||||||
|
'deleteCount': string;
|
||||||
'deleteFilesMessage': string;
|
'deleteFilesMessage': string;
|
||||||
'folderPath': string;
|
'folderPath': string;
|
||||||
'pathContainer': string;
|
'pathContainer': string;
|
||||||
|
|||||||
@@ -50,15 +50,15 @@ class DeleteSeriesModalContent extends Component {
|
|||||||
const {
|
const {
|
||||||
title,
|
title,
|
||||||
path,
|
path,
|
||||||
statistics,
|
statistics = {},
|
||||||
deleteOptions,
|
deleteOptions,
|
||||||
onModalClose,
|
onModalClose,
|
||||||
onDeleteOptionChange
|
onDeleteOptionChange
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
|
||||||
const {
|
const {
|
||||||
episodeFileCount,
|
episodeFileCount = 0,
|
||||||
sizeOnDisk
|
sizeOnDisk = 0
|
||||||
} = statistics;
|
} = statistics;
|
||||||
|
|
||||||
const deleteFiles = this.state.deleteFiles;
|
const deleteFiles = this.state.deleteFiles;
|
||||||
@@ -108,16 +108,20 @@ class DeleteSeriesModalContent extends Component {
|
|||||||
</FormGroup>
|
</FormGroup>
|
||||||
|
|
||||||
{
|
{
|
||||||
deleteFiles &&
|
deleteFiles ?
|
||||||
<div className={styles.deleteFilesMessage}>
|
<div className={styles.deleteFilesMessage}>
|
||||||
<div><InlineMarkdown data={translate('DeleteSeriesFolderConfirmation', { path })} blockClassName={styles.folderPath} /></div>
|
<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>
|
</ModalBody>
|
||||||
|
|
||||||
<ModalFooter>
|
<ModalFooter>
|
||||||
|
|||||||
@@ -175,6 +175,7 @@ class SeriesDetails extends Component {
|
|||||||
tvdbId,
|
tvdbId,
|
||||||
tvMazeId,
|
tvMazeId,
|
||||||
imdbId,
|
imdbId,
|
||||||
|
tmdbId,
|
||||||
title,
|
title,
|
||||||
runtime,
|
runtime,
|
||||||
ratings,
|
ratings,
|
||||||
@@ -566,6 +567,7 @@ class SeriesDetails extends Component {
|
|||||||
tvdbId={tvdbId}
|
tvdbId={tvdbId}
|
||||||
tvMazeId={tvMazeId}
|
tvMazeId={tvMazeId}
|
||||||
imdbId={imdbId}
|
imdbId={imdbId}
|
||||||
|
tmdbId={tmdbId}
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
kind={kinds.INVERSE}
|
kind={kinds.INVERSE}
|
||||||
@@ -719,6 +721,7 @@ SeriesDetails.propTypes = {
|
|||||||
tvdbId: PropTypes.number.isRequired,
|
tvdbId: PropTypes.number.isRequired,
|
||||||
tvMazeId: PropTypes.number,
|
tvMazeId: PropTypes.number,
|
||||||
imdbId: PropTypes.string,
|
imdbId: PropTypes.string,
|
||||||
|
tmdbId: PropTypes.number,
|
||||||
title: PropTypes.string.isRequired,
|
title: PropTypes.string.isRequired,
|
||||||
runtime: PropTypes.number.isRequired,
|
runtime: PropTypes.number.isRequired,
|
||||||
ratings: PropTypes.object.isRequired,
|
ratings: PropTypes.object.isRequired,
|
||||||
|
|||||||
@@ -9,7 +9,8 @@ function SeriesDetailsLinks(props) {
|
|||||||
const {
|
const {
|
||||||
tvdbId,
|
tvdbId,
|
||||||
tvMazeId,
|
tvMazeId,
|
||||||
imdbId
|
imdbId,
|
||||||
|
tmdbId
|
||||||
} = props;
|
} = props;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -71,6 +72,22 @@ function SeriesDetailsLinks(props) {
|
|||||||
</Label>
|
</Label>
|
||||||
</Link>
|
</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>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -78,7 +95,8 @@ function SeriesDetailsLinks(props) {
|
|||||||
SeriesDetailsLinks.propTypes = {
|
SeriesDetailsLinks.propTypes = {
|
||||||
tvdbId: PropTypes.number.isRequired,
|
tvdbId: PropTypes.number.isRequired,
|
||||||
tvMazeId: PropTypes.number,
|
tvMazeId: PropTypes.number,
|
||||||
imdbId: PropTypes.string
|
imdbId: PropTypes.string,
|
||||||
|
tmdbId: PropTypes.number
|
||||||
};
|
};
|
||||||
|
|
||||||
export default SeriesDetailsLinks;
|
export default SeriesDetailsLinks;
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ function SeriesHistoryModal(props) {
|
|||||||
return (
|
return (
|
||||||
<Modal
|
<Modal
|
||||||
isOpen={isOpen}
|
isOpen={isOpen}
|
||||||
size={sizes.EXTRA_LARGE}
|
size={sizes.EXTRA_EXTRA_LARGE}
|
||||||
onModalClose={onModalClose}
|
onModalClose={onModalClose}
|
||||||
>
|
>
|
||||||
<SeriesHistoryModalContentConnector
|
<SeriesHistoryModalContentConnector
|
||||||
|
|||||||
@@ -135,6 +135,8 @@ class SeriesHistoryRow extends Component {
|
|||||||
|
|
||||||
<RelativeDateCellConnector
|
<RelativeDateCellConnector
|
||||||
date={date}
|
date={date}
|
||||||
|
includeSeconds={true}
|
||||||
|
includeTime={true}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<TableRowCell className={styles.actions}>
|
<TableRowCell className={styles.actions}>
|
||||||
|
|||||||
@@ -138,7 +138,10 @@ function getInfoRowProps(
|
|||||||
}),
|
}),
|
||||||
iconName: icons.CALENDAR,
|
iconName: icons.CALENDAR,
|
||||||
label:
|
label:
|
||||||
getRelativeDate(previousAiring, shortDateFormat, showRelativeDates, {
|
getRelativeDate({
|
||||||
|
date: previousAiring,
|
||||||
|
shortDateFormat,
|
||||||
|
showRelativeDates,
|
||||||
timeFormat,
|
timeFormat,
|
||||||
timeForToday: true,
|
timeForToday: true,
|
||||||
}) ?? '',
|
}) ?? '',
|
||||||
@@ -156,7 +159,10 @@ function getInfoRowProps(
|
|||||||
}),
|
}),
|
||||||
iconName: icons.ADD,
|
iconName: icons.ADD,
|
||||||
label:
|
label:
|
||||||
getRelativeDate(added, shortDateFormat, showRelativeDates, {
|
getRelativeDate({
|
||||||
|
date: added,
|
||||||
|
shortDateFormat,
|
||||||
|
showRelativeDates,
|
||||||
timeFormat,
|
timeFormat,
|
||||||
timeForToday: true,
|
timeForToday: true,
|
||||||
}) ?? '',
|
}) ?? '',
|
||||||
@@ -232,15 +238,13 @@ function SeriesIndexOverviewInfo(props: SeriesIndexOverviewInfoProps) {
|
|||||||
<SeriesIndexOverviewInfoRow
|
<SeriesIndexOverviewInfoRow
|
||||||
title={formatDateTime(nextAiring, longDateFormat, timeFormat)}
|
title={formatDateTime(nextAiring, longDateFormat, timeFormat)}
|
||||||
iconName={icons.SCHEDULED}
|
iconName={icons.SCHEDULED}
|
||||||
label={getRelativeDate(
|
label={getRelativeDate({
|
||||||
nextAiring,
|
date: nextAiring,
|
||||||
shortDateFormat,
|
shortDateFormat,
|
||||||
showRelativeDates,
|
showRelativeDates,
|
||||||
{
|
timeFormat,
|
||||||
timeFormat,
|
timeForToday: true,
|
||||||
timeForToday: true,
|
})}
|
||||||
}
|
|
||||||
)}
|
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
|||||||
@@ -217,7 +217,10 @@ function SeriesIndexPoster(props: SeriesIndexPosterProps) {
|
|||||||
timeFormat
|
timeFormat
|
||||||
)}`}
|
)}`}
|
||||||
>
|
>
|
||||||
{getRelativeDate(nextAiring, shortDateFormat, showRelativeDates, {
|
{getRelativeDate({
|
||||||
|
date: nextAiring,
|
||||||
|
shortDateFormat,
|
||||||
|
showRelativeDates,
|
||||||
timeFormat,
|
timeFormat,
|
||||||
timeForToday: true,
|
timeForToday: true,
|
||||||
})}
|
})}
|
||||||
|
|||||||
@@ -80,7 +80,10 @@ function SeriesIndexPosterInfo(props: SeriesIndexPosterInfoProps) {
|
|||||||
timeFormat
|
timeFormat
|
||||||
)}`}
|
)}`}
|
||||||
>
|
>
|
||||||
{getRelativeDate(previousAiring, shortDateFormat, showRelativeDates, {
|
{getRelativeDate({
|
||||||
|
date: previousAiring,
|
||||||
|
shortDateFormat,
|
||||||
|
showRelativeDates,
|
||||||
timeFormat,
|
timeFormat,
|
||||||
timeForToday: true,
|
timeForToday: true,
|
||||||
})}
|
})}
|
||||||
@@ -89,15 +92,13 @@ function SeriesIndexPosterInfo(props: SeriesIndexPosterInfoProps) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (sortKey === 'added' && added) {
|
if (sortKey === 'added' && added) {
|
||||||
const addedDate = getRelativeDate(
|
const addedDate = getRelativeDate({
|
||||||
added,
|
date: added,
|
||||||
shortDateFormat,
|
shortDateFormat,
|
||||||
showRelativeDates,
|
showRelativeDates,
|
||||||
{
|
timeFormat,
|
||||||
timeFormat,
|
timeForToday: false,
|
||||||
timeForToday: false,
|
});
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
|
|||||||
@@ -10,4 +10,15 @@
|
|||||||
.path {
|
.path {
|
||||||
margin-left: 5px;
|
margin-left: 5px;
|
||||||
color: var(--dangerColor);
|
color: var(--dangerColor);
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.statistics {
|
||||||
|
margin-left: 5px;
|
||||||
|
color: var(--warningColor);
|
||||||
|
}
|
||||||
|
|
||||||
|
.deleteFilesMessage {
|
||||||
|
margin-top: 20px;
|
||||||
|
color: var(--warningColor);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +1,11 @@
|
|||||||
// This file is automatically generated.
|
// This file is automatically generated.
|
||||||
// Please do not change this file!
|
// Please do not change this file!
|
||||||
interface CssExports {
|
interface CssExports {
|
||||||
|
'deleteFilesMessage': string;
|
||||||
'message': string;
|
'message': string;
|
||||||
'path': string;
|
'path': string;
|
||||||
'pathContainer': string;
|
'pathContainer': string;
|
||||||
|
'statistics': string;
|
||||||
}
|
}
|
||||||
export const cssExports: CssExports;
|
export const cssExports: CssExports;
|
||||||
export default cssExports;
|
export default cssExports;
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ import Series from 'Series/Series';
|
|||||||
import { bulkDeleteSeries, setDeleteOption } from 'Store/Actions/seriesActions';
|
import { bulkDeleteSeries, setDeleteOption } from 'Store/Actions/seriesActions';
|
||||||
import createAllSeriesSelector from 'Store/Selectors/createAllSeriesSelector';
|
import createAllSeriesSelector from 'Store/Selectors/createAllSeriesSelector';
|
||||||
import { CheckInputChanged } from 'typings/inputs';
|
import { CheckInputChanged } from 'typings/inputs';
|
||||||
|
import formatBytes from 'Utilities/Number/formatBytes';
|
||||||
import translate from 'Utilities/String/translate';
|
import translate from 'Utilities/String/translate';
|
||||||
import styles from './DeleteSeriesModalContent.css';
|
import styles from './DeleteSeriesModalContent.css';
|
||||||
|
|
||||||
@@ -85,6 +86,24 @@ function DeleteSeriesModalContent(props: DeleteSeriesModalContentProps) {
|
|||||||
onModalClose,
|
onModalClose,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
const { totalEpisodeFileCount, totalSizeOnDisk } = useMemo(() => {
|
||||||
|
return series.reduce(
|
||||||
|
(acc, s) => {
|
||||||
|
const { statistics = { episodeFileCount: 0, sizeOnDisk: 0 } } = s;
|
||||||
|
const { episodeFileCount = 0, sizeOnDisk = 0 } = statistics;
|
||||||
|
|
||||||
|
acc.totalEpisodeFileCount += episodeFileCount;
|
||||||
|
acc.totalSizeOnDisk += sizeOnDisk;
|
||||||
|
|
||||||
|
return acc;
|
||||||
|
},
|
||||||
|
{
|
||||||
|
totalEpisodeFileCount: 0,
|
||||||
|
totalSizeOnDisk: 0,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}, [series]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ModalContent onModalClose={onModalClose}>
|
<ModalContent onModalClose={onModalClose}>
|
||||||
<ModalHeader>{translate('DeleteSelectedSeries')}</ModalHeader>
|
<ModalHeader>{translate('DeleteSelectedSeries')}</ModalHeader>
|
||||||
@@ -137,19 +156,43 @@ function DeleteSeriesModalContent(props: DeleteSeriesModalContentProps) {
|
|||||||
|
|
||||||
<ul>
|
<ul>
|
||||||
{series.map((s) => {
|
{series.map((s) => {
|
||||||
|
const { episodeFileCount = 0, sizeOnDisk = 0 } = s.statistics;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<li key={s.title}>
|
<li key={s.title}>
|
||||||
<span>{s.title}</span>
|
<span>{s.title}</span>
|
||||||
|
|
||||||
{deleteFiles && (
|
{deleteFiles && (
|
||||||
<span className={styles.pathContainer}>
|
<span>
|
||||||
-<span className={styles.path}>{s.path}</span>
|
<span className={styles.pathContainer}>
|
||||||
|
-<span className={styles.path}>{s.path}</span>
|
||||||
|
</span>
|
||||||
|
|
||||||
|
{!!episodeFileCount && (
|
||||||
|
<span className={styles.statistics}>
|
||||||
|
(
|
||||||
|
{translate('DeleteSeriesFolderEpisodeCount', {
|
||||||
|
episodeFileCount,
|
||||||
|
size: formatBytes(sizeOnDisk),
|
||||||
|
})}
|
||||||
|
)
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
</span>
|
</span>
|
||||||
)}
|
)}
|
||||||
</li>
|
</li>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
|
{deleteFiles && !!totalEpisodeFileCount ? (
|
||||||
|
<div className={styles.deleteFilesMessage}>
|
||||||
|
{translate('DeleteSeriesFolderEpisodeCount', {
|
||||||
|
episodeFileCount: totalEpisodeFileCount,
|
||||||
|
size: formatBytes(totalSizeOnDisk),
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
) : null}
|
||||||
</ModalBody>
|
</ModalBody>
|
||||||
|
|
||||||
<ModalFooter>
|
<ModalFooter>
|
||||||
|
|||||||
@@ -70,6 +70,7 @@ interface Series extends ModelBase {
|
|||||||
tvdbId: number;
|
tvdbId: number;
|
||||||
tvMazeId: number;
|
tvMazeId: number;
|
||||||
tvRageId: number;
|
tvRageId: number;
|
||||||
|
tmdbId: number;
|
||||||
useSceneNumbering: boolean;
|
useSceneNumbering: boolean;
|
||||||
year: number;
|
year: number;
|
||||||
isSaving?: boolean;
|
isSaving?: boolean;
|
||||||
|
|||||||
@@ -99,6 +99,7 @@ const seriesTokens = [
|
|||||||
const seriesIdTokens = [
|
const seriesIdTokens = [
|
||||||
{ token: '{ImdbId}', example: 'tt12345' },
|
{ token: '{ImdbId}', example: 'tt12345' },
|
||||||
{ token: '{TvdbId}', example: '12345' },
|
{ token: '{TvdbId}', example: '12345' },
|
||||||
|
{ token: '{TmdbId}', example: '11223' },
|
||||||
{ token: '{TvMazeId}', example: '54321' }
|
{ token: '{TvMazeId}', example: '54321' }
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|||||||
+5
-5
@@ -4,7 +4,7 @@ import { connect } from 'react-redux';
|
|||||||
import { createSelector } from 'reselect';
|
import { createSelector } from 'reselect';
|
||||||
import {
|
import {
|
||||||
saveNotification,
|
saveNotification,
|
||||||
setNotificationFieldValue,
|
setNotificationFieldValues,
|
||||||
setNotificationValue,
|
setNotificationValue,
|
||||||
testNotification,
|
testNotification,
|
||||||
toggleAdvancedSettings
|
toggleAdvancedSettings
|
||||||
@@ -27,7 +27,7 @@ function createMapStateToProps() {
|
|||||||
|
|
||||||
const mapDispatchToProps = {
|
const mapDispatchToProps = {
|
||||||
setNotificationValue,
|
setNotificationValue,
|
||||||
setNotificationFieldValue,
|
setNotificationFieldValues,
|
||||||
saveNotification,
|
saveNotification,
|
||||||
testNotification,
|
testNotification,
|
||||||
toggleAdvancedSettings
|
toggleAdvancedSettings
|
||||||
@@ -51,8 +51,8 @@ class EditNotificationModalContentConnector extends Component {
|
|||||||
this.props.setNotificationValue({ name, value });
|
this.props.setNotificationValue({ name, value });
|
||||||
};
|
};
|
||||||
|
|
||||||
onFieldChange = ({ name, value }) => {
|
onFieldChange = ({ name, value, additionalProperties = {} }) => {
|
||||||
this.props.setNotificationFieldValue({ name, value });
|
this.props.setNotificationFieldValues({ properties: { ...additionalProperties, [name]: value } });
|
||||||
};
|
};
|
||||||
|
|
||||||
onSavePress = () => {
|
onSavePress = () => {
|
||||||
@@ -91,7 +91,7 @@ EditNotificationModalContentConnector.propTypes = {
|
|||||||
saveError: PropTypes.object,
|
saveError: PropTypes.object,
|
||||||
item: PropTypes.object.isRequired,
|
item: PropTypes.object.isRequired,
|
||||||
setNotificationValue: PropTypes.func.isRequired,
|
setNotificationValue: PropTypes.func.isRequired,
|
||||||
setNotificationFieldValue: PropTypes.func.isRequired,
|
setNotificationFieldValues: PropTypes.func.isRequired,
|
||||||
saveNotification: PropTypes.func.isRequired,
|
saveNotification: PropTypes.func.isRequired,
|
||||||
testNotification: PropTypes.func.isRequired,
|
testNotification: PropTypes.func.isRequired,
|
||||||
toggleAdvancedSettings: PropTypes.func.isRequired,
|
toggleAdvancedSettings: PropTypes.func.isRequired,
|
||||||
|
|||||||
@@ -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;
|
||||||
@@ -5,6 +5,7 @@ import createRemoveItemHandler from 'Store/Actions/Creators/createRemoveItemHand
|
|||||||
import createSaveProviderHandler, { createCancelSaveProviderHandler } from 'Store/Actions/Creators/createSaveProviderHandler';
|
import createSaveProviderHandler, { createCancelSaveProviderHandler } from 'Store/Actions/Creators/createSaveProviderHandler';
|
||||||
import createTestProviderHandler, { createCancelTestProviderHandler } from 'Store/Actions/Creators/createTestProviderHandler';
|
import createTestProviderHandler, { createCancelTestProviderHandler } from 'Store/Actions/Creators/createTestProviderHandler';
|
||||||
import createSetProviderFieldValueReducer from 'Store/Actions/Creators/Reducers/createSetProviderFieldValueReducer';
|
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 createSetSettingValueReducer from 'Store/Actions/Creators/Reducers/createSetSettingValueReducer';
|
||||||
import { createThunk } from 'Store/thunks';
|
import { createThunk } from 'Store/thunks';
|
||||||
import selectProviderSchema from 'Utilities/State/selectProviderSchema';
|
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 SELECT_NOTIFICATION_SCHEMA = 'settings/notifications/selectNotificationSchema';
|
||||||
export const SET_NOTIFICATION_VALUE = 'settings/notifications/setNotificationValue';
|
export const SET_NOTIFICATION_VALUE = 'settings/notifications/setNotificationValue';
|
||||||
export const SET_NOTIFICATION_FIELD_VALUE = 'settings/notifications/setNotificationFieldValue';
|
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 SAVE_NOTIFICATION = 'settings/notifications/saveNotification';
|
||||||
export const CANCEL_SAVE_NOTIFICATION = 'settings/notifications/cancelSaveNotification';
|
export const CANCEL_SAVE_NOTIFICATION = 'settings/notifications/cancelSaveNotification';
|
||||||
export const DELETE_NOTIFICATION = 'settings/notifications/deleteNotification';
|
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
|
// Details
|
||||||
|
|
||||||
@@ -99,6 +108,7 @@ export default {
|
|||||||
reducers: {
|
reducers: {
|
||||||
[SET_NOTIFICATION_VALUE]: createSetSettingValueReducer(section),
|
[SET_NOTIFICATION_VALUE]: createSetSettingValueReducer(section),
|
||||||
[SET_NOTIFICATION_FIELD_VALUE]: createSetProviderFieldValueReducer(section),
|
[SET_NOTIFICATION_FIELD_VALUE]: createSetProviderFieldValueReducer(section),
|
||||||
|
[SET_NOTIFICATION_FIELD_VALUES]: createSetProviderFieldValuesReducer(section),
|
||||||
|
|
||||||
[SELECT_NOTIFICATION_SCHEMA]: (state, { payload }) => {
|
[SELECT_NOTIFICATION_SCHEMA]: (state, { payload }) => {
|
||||||
return selectProviderSchema(state, section, payload, (selectedSchema) => {
|
return selectProviderSchema(state, section, payload, (selectedSchema) => {
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ module.exports = {
|
|||||||
// Families
|
// Families
|
||||||
defaultFontFamily: 'Roboto, "open sans", "Helvetica Neue", Helvetica, Arial, sans-serif',
|
defaultFontFamily: 'Roboto, "open sans", "Helvetica Neue", Helvetica, Arial, sans-serif',
|
||||||
monoSpaceFontFamily: '"Ubuntu Mono", Menlo, Monaco, Consolas, "Courier New", monospace;',
|
monoSpaceFontFamily: '"Ubuntu Mono", Menlo, Monaco, Consolas, "Courier New", monospace;',
|
||||||
passwordFamily: 'text-security-disc',
|
|
||||||
|
|
||||||
// Sizes
|
// Sizes
|
||||||
extraSmallFontSize: '11px',
|
extraSmallFontSize: '11px',
|
||||||
|
|||||||
@@ -1,43 +0,0 @@
|
|||||||
import moment from 'moment';
|
|
||||||
import formatTime from 'Utilities/Date/formatTime';
|
|
||||||
import isInNextWeek from 'Utilities/Date/isInNextWeek';
|
|
||||||
import isToday from 'Utilities/Date/isToday';
|
|
||||||
import isTomorrow from 'Utilities/Date/isTomorrow';
|
|
||||||
import isYesterday from 'Utilities/Date/isYesterday';
|
|
||||||
import translate from 'Utilities/String/translate';
|
|
||||||
|
|
||||||
function getRelativeDate(date, shortDateFormat, showRelativeDates, { timeFormat, includeSeconds = false, timeForToday = false } = {}) {
|
|
||||||
if (!date) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
const isTodayDate = isToday(date);
|
|
||||||
|
|
||||||
if (isTodayDate && timeForToday && timeFormat) {
|
|
||||||
return formatTime(date, timeFormat, { includeMinuteZero: true, includeSeconds });
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!showRelativeDates) {
|
|
||||||
return moment(date).format(shortDateFormat);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isYesterday(date)) {
|
|
||||||
return translate('Yesterday');
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isTodayDate) {
|
|
||||||
return translate('Today');
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isTomorrow(date)) {
|
|
||||||
return translate('Tomorrow');
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isInNextWeek(date)) {
|
|
||||||
return moment(date).format('dddd');
|
|
||||||
}
|
|
||||||
|
|
||||||
return moment(date).format(shortDateFormat);
|
|
||||||
}
|
|
||||||
|
|
||||||
export default getRelativeDate;
|
|
||||||
@@ -0,0 +1,82 @@
|
|||||||
|
import moment from 'moment';
|
||||||
|
import formatTime from 'Utilities/Date/formatTime';
|
||||||
|
import isInNextWeek from 'Utilities/Date/isInNextWeek';
|
||||||
|
import isToday from 'Utilities/Date/isToday';
|
||||||
|
import isTomorrow from 'Utilities/Date/isTomorrow';
|
||||||
|
import isYesterday from 'Utilities/Date/isYesterday';
|
||||||
|
import translate from 'Utilities/String/translate';
|
||||||
|
import formatDateTime from './formatDateTime';
|
||||||
|
|
||||||
|
interface GetRelativeDateOptions {
|
||||||
|
date?: string;
|
||||||
|
shortDateFormat: string;
|
||||||
|
showRelativeDates: boolean;
|
||||||
|
timeFormat?: string;
|
||||||
|
includeSeconds?: boolean;
|
||||||
|
timeForToday?: boolean;
|
||||||
|
includeTime?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getRelativeDate({
|
||||||
|
date,
|
||||||
|
shortDateFormat,
|
||||||
|
showRelativeDates,
|
||||||
|
timeFormat,
|
||||||
|
includeSeconds = false,
|
||||||
|
timeForToday = false,
|
||||||
|
includeTime = false,
|
||||||
|
}: GetRelativeDateOptions) {
|
||||||
|
if (!date) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((includeTime || timeForToday) && !timeFormat) {
|
||||||
|
throw new Error(
|
||||||
|
"getRelativeDate: 'timeFormat' is required when 'includeTime' or 'timeForToday' is true"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const isTodayDate = isToday(date);
|
||||||
|
const time = timeFormat
|
||||||
|
? formatTime(date, timeFormat, {
|
||||||
|
includeMinuteZero: true,
|
||||||
|
includeSeconds,
|
||||||
|
})
|
||||||
|
: '';
|
||||||
|
|
||||||
|
if (isTodayDate && timeForToday) {
|
||||||
|
return time;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!showRelativeDates) {
|
||||||
|
return moment(date).format(shortDateFormat);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isYesterday(date)) {
|
||||||
|
return includeTime
|
||||||
|
? translate('YesterdayAt', { time })
|
||||||
|
: translate('Yesterday');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isTodayDate) {
|
||||||
|
return includeTime ? translate('TodayAt', { time }) : translate('Today');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isTomorrow(date)) {
|
||||||
|
return includeTime
|
||||||
|
? translate('TomorrowAt', { time })
|
||||||
|
: translate('Tomorrow');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isInNextWeek(date)) {
|
||||||
|
const day = moment(date).format('dddd');
|
||||||
|
|
||||||
|
return includeTime ? translate('DayOfWeekAt', { day, time }) : day;
|
||||||
|
}
|
||||||
|
|
||||||
|
return includeTime
|
||||||
|
? formatDateTime(date, shortDateFormat, timeFormat, { includeSeconds })
|
||||||
|
: moment(date).format(shortDateFormat);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default getRelativeDate;
|
||||||
@@ -116,6 +116,7 @@
|
|||||||
border: 1px solid var(--inputBorderColor);
|
border: 1px solid var(--inputBorderColor);
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
box-shadow: inset 0 1px 1px var(--inputBoxShadowColor);
|
box-shadow: inset 0 1px 1px var(--inputBoxShadowColor);
|
||||||
|
color: var(--textColor);
|
||||||
}
|
}
|
||||||
|
|
||||||
.form-input:focus {
|
.form-input:focus {
|
||||||
@@ -296,7 +297,7 @@
|
|||||||
var light = {
|
var light = {
|
||||||
white: '#fff',
|
white: '#fff',
|
||||||
pageBackground: '#f5f7fa',
|
pageBackground: '#f5f7fa',
|
||||||
textColor: '#656565',
|
textColor: '#515253',
|
||||||
themeDarkColor: '#3a3f51',
|
themeDarkColor: '#3a3f51',
|
||||||
panelBackground: '#fff',
|
panelBackground: '#fff',
|
||||||
inputBackgroundColor: '#fff',
|
inputBackgroundColor: '#fff',
|
||||||
@@ -316,7 +317,7 @@
|
|||||||
var dark = {
|
var dark = {
|
||||||
white: '#fff',
|
white: '#fff',
|
||||||
pageBackground: '#202020',
|
pageBackground: '#202020',
|
||||||
textColor: '#656565',
|
textColor: '#ccc',
|
||||||
themeDarkColor: '#494949',
|
themeDarkColor: '#494949',
|
||||||
panelBackground: '#111',
|
panelBackground: '#111',
|
||||||
inputBackgroundColor: '#333',
|
inputBackgroundColor: '#333',
|
||||||
|
|||||||
@@ -153,7 +153,11 @@ namespace NzbDrone.Common.Disk
|
|||||||
{
|
{
|
||||||
Ensure.That(path, () => path).IsValidPath(PathValidationType.CurrentOs);
|
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)
|
public IEnumerable<string> GetFiles(string path, bool recursive)
|
||||||
|
|||||||
@@ -366,7 +366,7 @@ namespace NzbDrone.Core.Test.Download.CompletedDownloadServiceTests
|
|||||||
Mocker.GetMock<IEventAggregator>()
|
Mocker.GetMock<IEventAggregator>()
|
||||||
.Verify(v => v.PublishEvent(It.IsAny<DownloadCompletedEvent>()), Times.Never());
|
.Verify(v => v.PublishEvent(It.IsAny<DownloadCompletedEvent>()), Times.Never());
|
||||||
|
|
||||||
_trackedDownload.State.Should().Be(TrackedDownloadState.ImportPending);
|
_trackedDownload.State.Should().Be(TrackedDownloadState.ImportBlocked);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void AssertImported()
|
private void AssertImported()
|
||||||
|
|||||||
@@ -608,5 +608,47 @@ namespace NzbDrone.Core.Test.IndexerSearchTests
|
|||||||
allCriteria.Last().As<SingleEpisodeSearchCriteria>().SeasonNumber.Should().Be(2);
|
allCriteria.Last().As<SingleEpisodeSearchCriteria>().SeasonNumber.Should().Be(2);
|
||||||
allCriteria.Last().As<SingleEpisodeSearchCriteria>().EpisodeNumber.Should().Be(3);
|
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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ namespace NzbDrone.Core.Test.IndexerTests.NewznabTests
|
|||||||
|
|
||||||
_singleEpisodeSearchCriteria = new SingleEpisodeSearchCriteria
|
_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" },
|
SceneTitles = new List<string> { "Monkey Island" },
|
||||||
SeasonNumber = 1,
|
SeasonNumber = 1,
|
||||||
EpisodeNumber = 2
|
EpisodeNumber = 2
|
||||||
@@ -44,14 +44,14 @@ namespace NzbDrone.Core.Test.IndexerTests.NewznabTests
|
|||||||
|
|
||||||
_seasonSearchCriteria = new SeasonSearchCriteria
|
_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" },
|
SceneTitles = new List<string> { "Monkey Island" },
|
||||||
SeasonNumber = 1,
|
SeasonNumber = 1,
|
||||||
};
|
};
|
||||||
|
|
||||||
_animeSearchCriteria = new AnimeEpisodeSearchCriteria()
|
_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" },
|
SceneTitles = new List<string>() { "Monkey+Island" },
|
||||||
AbsoluteEpisodeNumber = 100,
|
AbsoluteEpisodeNumber = 100,
|
||||||
SeasonNumber = 5,
|
SeasonNumber = 5,
|
||||||
@@ -60,7 +60,7 @@ namespace NzbDrone.Core.Test.IndexerTests.NewznabTests
|
|||||||
|
|
||||||
_animeSeasonSearchCriteria = new AnimeSeasonSearchCriteria()
|
_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" },
|
SceneTitles = new List<string> { "Monkey Island" },
|
||||||
SeasonNumber = 3,
|
SeasonNumber = 3,
|
||||||
};
|
};
|
||||||
@@ -268,6 +268,19 @@ namespace NzbDrone.Core.Test.IndexerTests.NewznabTests
|
|||||||
page.Url.Query.Should().Contain("imdbid=t40");
|
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]
|
[Test]
|
||||||
public void should_prefer_search_by_tvdbid_if_rid_supported()
|
public void should_prefer_search_by_tvdbid_if_rid_supported()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -107,7 +107,7 @@ namespace NzbDrone.Core.Test.MediaFiles.MediaInfo
|
|||||||
[TestCase(10, "", "", "", null, HdrFormat.None)]
|
[TestCase(10, "", "", "", null, HdrFormat.None)]
|
||||||
[TestCase(10, "bt709", "bt709", "", null, HdrFormat.None)]
|
[TestCase(10, "bt709", "bt709", "", null, HdrFormat.None)]
|
||||||
[TestCase(8, "bt2020", "smpte2084", "", 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", "arib-std-b67", "", null, HdrFormat.Hlg10)]
|
||||||
[TestCase(10, "bt2020", "smpte2084", "", null, HdrFormat.Pq10)]
|
[TestCase(10, "bt2020", "smpte2084", "", null, HdrFormat.Pq10)]
|
||||||
[TestCase(10, "bt2020", "smpte2084", "FFMpegCore.SideData", null, HdrFormat.Pq10)]
|
[TestCase(10, "bt2020", "smpte2084", "FFMpegCore.SideData", null, HdrFormat.Pq10)]
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ using FluentValidation.Results;
|
|||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
using NzbDrone.Core.MediaFiles;
|
using NzbDrone.Core.MediaFiles;
|
||||||
using NzbDrone.Core.Notifications;
|
using NzbDrone.Core.Notifications;
|
||||||
using NzbDrone.Core.ThingiProvider;
|
|
||||||
using NzbDrone.Core.Tv;
|
using NzbDrone.Core.Tv;
|
||||||
using NzbDrone.Core.Validation;
|
using NzbDrone.Core.Validation;
|
||||||
using NzbDrone.Test.Common;
|
using NzbDrone.Test.Common;
|
||||||
@@ -15,9 +14,9 @@ namespace NzbDrone.Core.Test.NotificationTests
|
|||||||
[TestFixture]
|
[TestFixture]
|
||||||
public class NotificationBaseFixture : TestBase
|
public class NotificationBaseFixture : TestBase
|
||||||
{
|
{
|
||||||
private class TestSetting : IProviderConfig
|
private class TestSetting : NotificationSettingsBase<TestSetting>
|
||||||
{
|
{
|
||||||
public NzbDroneValidationResult Validate()
|
public override NzbDroneValidationResult Validate()
|
||||||
{
|
{
|
||||||
return new NzbDroneValidationResult();
|
return new NzbDroneValidationResult();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -56,5 +56,14 @@ namespace NzbDrone.Core.Test.OrganizerTests.FileNameBuilderTests
|
|||||||
Subject.GetSeriesFolder(_series)
|
Subject.GetSeriesFolder(_series)
|
||||||
.Should().Be($"Series Title ({_series.TvMazeId})");
|
.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})");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -39,6 +39,13 @@ namespace NzbDrone.Core.Test.ParserTests
|
|||||||
ExceptionVerification.IgnoreWarns();
|
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]
|
[Test]
|
||||||
public void should_not_parse_md5()
|
public void should_not_parse_md5()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ namespace NzbDrone.Core.Test.ParserTests
|
|||||||
[TestCase("2019_08_20_1080_all.mp4", "", 2019, 8, 20)]
|
[TestCase("2019_08_20_1080_all.mp4", "", 2019, 8, 20)]
|
||||||
[TestCase("Series and Title 20201013 Ep7432 [720p WebRip (x264)] [SUBS]", "Series and Title", 2020, 10, 13)]
|
[TestCase("Series and Title 20201013 Ep7432 [720p WebRip (x264)] [SUBS]", "Series and Title", 2020, 10, 13)]
|
||||||
[TestCase("Series Title (1955) - 1954-01-23 05 00 00 - Cottage for Sale.ts", "Series Title (1955)", 1954, 1, 23)]
|
[TestCase("Series Title (1955) - 1954-01-23 05 00 00 - Cottage for Sale.ts", "Series Title (1955)", 1954, 1, 23)]
|
||||||
|
[TestCase("Series Title - 30-04-2024 HDTV 1080p H264 AAC", "Series Title", 2024, 4, 30)]
|
||||||
|
|
||||||
// [TestCase("", "", 0, 0, 0)]
|
// [TestCase("", "", 0, 0, 0)]
|
||||||
public void should_parse_daily_episode(string postTitle, string title, int year, int month, int day)
|
public void should_parse_daily_episode(string postTitle, string title, int year, int month, int day)
|
||||||
@@ -100,5 +101,13 @@ namespace NzbDrone.Core.Test.ParserTests
|
|||||||
|
|
||||||
Parser.Parser.ParseTitle(title).Should().BeNull();
|
Parser.Parser.ParseTitle(title).Should().BeNull();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[TestCase("Tmc - Quotidien - 05-06-2024 HDTV 1080p H264 AAC")]
|
||||||
|
|
||||||
|
// [TestCase("", "", 0, 0, 0)]
|
||||||
|
public void should_not_parse_ambiguous_daily_episode(string postTitle)
|
||||||
|
{
|
||||||
|
Parser.Parser.ParseTitle(postTitle).Should().BeNull();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -109,6 +109,8 @@ namespace NzbDrone.Core.Test.ParserTests
|
|||||||
[TestCase("The.Series.S01E05.480p.BluRay.DD5.1.x264-HiSD", false)]
|
[TestCase("The.Series.S01E05.480p.BluRay.DD5.1.x264-HiSD", false)]
|
||||||
[TestCase("The Series (BD)(640x480(RAW) (BATCH 1) (1-13)", false)]
|
[TestCase("The Series (BD)(640x480(RAW) (BATCH 1) (1-13)", false)]
|
||||||
[TestCase("[Doki] Series - 02 (848x480 XviD BD MP3) [95360783]", false)]
|
[TestCase("[Doki] Series - 02 (848x480 XviD BD MP3) [95360783]", false)]
|
||||||
|
[TestCase("Adventures.of.Sonic.the.Hedgehog.S01.BluRay.480i.DD.2.0.AVC.REMUX-FraMeSToR", false)]
|
||||||
|
[TestCase("Adventures.of.Sonic.the.Hedgehog.S01E01.Best.Hedgehog.480i.DD.2.0.AVC.REMUX-FraMeSToR", false)]
|
||||||
public void should_parse_bluray480p_quality(string title, bool proper)
|
public void should_parse_bluray480p_quality(string title, bool proper)
|
||||||
{
|
{
|
||||||
ParseAndVerifyQuality(title, Quality.Bluray480p, proper);
|
ParseAndVerifyQuality(title, Quality.Bluray480p, proper);
|
||||||
@@ -309,6 +311,7 @@ namespace NzbDrone.Core.Test.ParserTests
|
|||||||
[TestCase("Sans.Series.De.Traces.FRENCH.720p.BluRay.x264-FHD", false)]
|
[TestCase("Sans.Series.De.Traces.FRENCH.720p.BluRay.x264-FHD", false)]
|
||||||
[TestCase("Series.Black.1x01.Selezione.Naturale.ITA.720p.BDMux.x264-NovaRip", false)]
|
[TestCase("Series.Black.1x01.Selezione.Naturale.ITA.720p.BDMux.x264-NovaRip", false)]
|
||||||
[TestCase("Series.Hunter.S02.720p.Blu-ray.Remux.AVC.FLAC.2.0-SiCFoI", false)]
|
[TestCase("Series.Hunter.S02.720p.Blu-ray.Remux.AVC.FLAC.2.0-SiCFoI", false)]
|
||||||
|
[TestCase("Adventures.of.Sonic.the.Hedgehog.S01E01.Best.Hedgehog.720p.DD.2.0.AVC.REMUX-FraMeSToR", false)]
|
||||||
public void should_parse_bluray720p_quality(string title, bool proper)
|
public void should_parse_bluray720p_quality(string title, bool proper)
|
||||||
{
|
{
|
||||||
ParseAndVerifyQuality(title, Quality.Bluray720p, proper);
|
ParseAndVerifyQuality(title, Quality.Bluray720p, proper);
|
||||||
@@ -340,6 +343,7 @@ namespace NzbDrone.Core.Test.ParserTests
|
|||||||
[TestCase("Series.Title.S03E01.The.Calm.1080p.DTS-HD.MA.5.1.AVC.REMUX-FraMeSToR", false)]
|
[TestCase("Series.Title.S03E01.The.Calm.1080p.DTS-HD.MA.5.1.AVC.REMUX-FraMeSToR", false)]
|
||||||
[TestCase("Series Title Season 2 (BDRemux 1080p HEVC FLAC) [Netaro]", false)]
|
[TestCase("Series Title Season 2 (BDRemux 1080p HEVC FLAC) [Netaro]", false)]
|
||||||
[TestCase("[Vodes] Series Title - Other Title (2020) [BDRemux 1080p HEVC Dual-Audio]", false)]
|
[TestCase("[Vodes] Series Title - Other Title (2020) [BDRemux 1080p HEVC Dual-Audio]", false)]
|
||||||
|
[TestCase("Adventures.of.Sonic.the.Hedgehog.S01E01.Best.Hedgehog.1080p.DD.2.0.AVC.REMUX-FraMeSToR", false)]
|
||||||
public void should_parse_bluray1080p_remux_quality(string title, bool proper)
|
public void should_parse_bluray1080p_remux_quality(string title, bool proper)
|
||||||
{
|
{
|
||||||
ParseAndVerifyQuality(title, Quality.Bluray1080pRemux, proper);
|
ParseAndVerifyQuality(title, Quality.Bluray1080pRemux, proper);
|
||||||
@@ -360,6 +364,7 @@ namespace NzbDrone.Core.Test.ParserTests
|
|||||||
[TestCase("Series.Title.S01E08.The.Sonarr.BluRay.2160p.AVC.DTS-HD.MA.5.1.REMUX-FraMeSToR", false)]
|
[TestCase("Series.Title.S01E08.The.Sonarr.BluRay.2160p.AVC.DTS-HD.MA.5.1.REMUX-FraMeSToR", false)]
|
||||||
[TestCase("Series.Title.2x11.Nato.Per.The.Sonarr.Bluray.Remux.AVC.2160p.AC3.ITA", false)]
|
[TestCase("Series.Title.2x11.Nato.Per.The.Sonarr.Bluray.Remux.AVC.2160p.AC3.ITA", false)]
|
||||||
[TestCase("[Dolby Vision] Sonarr.of.Series.S07.MULTi.UHD.BLURAY.REMUX.DV-NoTag", false)]
|
[TestCase("[Dolby Vision] Sonarr.of.Series.S07.MULTi.UHD.BLURAY.REMUX.DV-NoTag", false)]
|
||||||
|
[TestCase("Adventures.of.Sonic.the.Hedgehog.S01E01.Best.Hedgehog.2160p.DD.2.0.AVC.REMUX-FraMeSToR", false)]
|
||||||
public void should_parse_bluray2160p_remux_quality(string title, bool proper)
|
public void should_parse_bluray2160p_remux_quality(string title, bool proper)
|
||||||
{
|
{
|
||||||
ParseAndVerifyQuality(title, Quality.Bluray2160pRemux, proper);
|
ParseAndVerifyQuality(title, Quality.Bluray2160pRemux, proper);
|
||||||
|
|||||||
@@ -33,6 +33,8 @@ 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 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 / 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("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)]
|
||||||
public void should_parse_full_season_release(string postTitle, string title, int season)
|
public void should_parse_full_season_release(string postTitle, string title, int season)
|
||||||
{
|
{
|
||||||
var result = Parser.Parser.ParseTitle(postTitle);
|
var result = Parser.Parser.ParseTitle(postTitle);
|
||||||
@@ -75,6 +77,7 @@ namespace NzbDrone.Core.Test.ParserTests
|
|||||||
|
|
||||||
[TestCase("The.Series.2016.S02.Part.1.1080p.NF.WEBRip.DD5.1.x264-NTb", "The Series 2016", 2, 1)]
|
[TestCase("The.Series.2016.S02.Part.1.1080p.NF.WEBRip.DD5.1.x264-NTb", "The Series 2016", 2, 1)]
|
||||||
[TestCase("The.Series.S07.Vol.1.1080p.NF.WEBRip.DD5.1.x264-NTb", "The Series", 7, 1)]
|
[TestCase("The.Series.S07.Vol.1.1080p.NF.WEBRip.DD5.1.x264-NTb", "The Series", 7, 1)]
|
||||||
|
[TestCase("The.Series.S06.P1.1080p.Blu-Ray.10-Bit.Dual-Audio.TrueHD.x265-iAHD", "The Series", 6, 1)]
|
||||||
public void should_parse_partial_season_release(string postTitle, string title, int season, int seasonPart)
|
public void should_parse_partial_season_release(string postTitle, string title, int season, int seasonPart)
|
||||||
{
|
{
|
||||||
var result = Parser.Parser.ParseTitle(postTitle);
|
var result = Parser.Parser.ParseTitle(postTitle);
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ namespace NzbDrone.Core.Test.ParserTests
|
|||||||
[TestCase("[www.test-hyphen.ca] - Series (2011) S01", "Series (2011)")]
|
[TestCase("[www.test-hyphen.ca] - Series (2011) S01", "Series (2011)")]
|
||||||
[TestCase("test123.ca - Series Time S02 720p HDTV x264 CRON", "Series Time")]
|
[TestCase("test123.ca - Series Time S02 720p HDTV x264 CRON", "Series Time")]
|
||||||
[TestCase("[www.test-hyphen123.co.za] - Series Title S01E01", "Series Title")]
|
[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)
|
public void should_not_parse_url_in_name(string postTitle, string title)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -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>()));
|
.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]
|
[Test]
|
||||||
public void should_log_error_if_tvdb_id_not_found()
|
public void should_log_error_if_tvdb_id_not_found()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -48,11 +48,11 @@ namespace NzbDrone.Core.Test.UpdateTests
|
|||||||
{
|
{
|
||||||
const string branch = "main";
|
const string branch = "main";
|
||||||
UseRealHttp();
|
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().NotBeEmpty();
|
||||||
recent.Should().OnlyContain(c => c.Hash.IsNotNullOrWhiteSpace());
|
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.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.New != null);
|
||||||
recent.Where(c => c.Changes != null).Should().OnlyContain(c => c.Changes.Fixed != null);
|
recent.Where(c => c.Changes != null).Should().OnlyContain(c => c.Changes.Fixed != null);
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
|
|
||||||
namespace NzbDrone.Core.Annotations
|
namespace NzbDrone.Core.Annotations
|
||||||
@@ -59,13 +60,27 @@ namespace NzbDrone.Core.Annotations
|
|||||||
public string Value { get; set; }
|
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 string Name { get; set; }
|
||||||
public int Order { get; set; }
|
public int Order { get; set; }
|
||||||
public string Hint { 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
|
public enum FieldType
|
||||||
|
|||||||
@@ -242,7 +242,7 @@ namespace NzbDrone.Core.Configuration
|
|||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
var urlBase = _serverOptions.UrlBase ?? GetValue("UrlBase", "").Trim('/');
|
var urlBase = (_serverOptions.UrlBase ?? GetValue("UrlBase", "")).Trim('/');
|
||||||
|
|
||||||
if (urlBase.IsNullOrWhiteSpace())
|
if (urlBase.IsNullOrWhiteSpace())
|
||||||
{
|
{
|
||||||
@@ -419,13 +419,21 @@ namespace NzbDrone.Core.Configuration
|
|||||||
throw new InvalidConfigFileException($"{_configFile} is corrupt. Please delete the config file and Sonarr will recreate it.");
|
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"));
|
var newXDoc = new XDocument(new XDeclaration("1.0", "utf-8", "yes"));
|
||||||
xDoc.Add(new XElement(CONFIG_ELEMENT_NAME));
|
newXDoc.Add(new XElement(CONFIG_ELEMENT_NAME));
|
||||||
|
|
||||||
return xDoc;
|
return newXDoc;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (XmlException ex)
|
catch (XmlException ex)
|
||||||
|
|||||||
@@ -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");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -25,6 +25,11 @@ namespace NzbDrone.Core.Download.Aggregation
|
|||||||
|
|
||||||
public RemoteEpisode Augment(RemoteEpisode remoteEpisode)
|
public RemoteEpisode Augment(RemoteEpisode remoteEpisode)
|
||||||
{
|
{
|
||||||
|
if (remoteEpisode == null)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
foreach (var augmenter in _augmenters)
|
foreach (var augmenter in _augmenters)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
using FluentValidation;
|
using FluentValidation;
|
||||||
using NzbDrone.Core.Annotations;
|
using NzbDrone.Core.Annotations;
|
||||||
using NzbDrone.Core.ThingiProvider;
|
|
||||||
using NzbDrone.Core.Validation;
|
using NzbDrone.Core.Validation;
|
||||||
|
|
||||||
namespace NzbDrone.Core.Download.Clients.Aria2
|
namespace NzbDrone.Core.Download.Clients.Aria2
|
||||||
@@ -13,9 +12,9 @@ namespace NzbDrone.Core.Download.Clients.Aria2
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public class Aria2Settings : IProviderConfig
|
public class Aria2Settings : DownloadClientSettingsBase<Aria2Settings>
|
||||||
{
|
{
|
||||||
private static readonly Aria2SettingsValidator Validator = new Aria2SettingsValidator();
|
private static readonly Aria2SettingsValidator Validator = new ();
|
||||||
|
|
||||||
public Aria2Settings()
|
public Aria2Settings()
|
||||||
{
|
{
|
||||||
@@ -44,7 +43,7 @@ namespace NzbDrone.Core.Download.Clients.Aria2
|
|||||||
[FieldDefinition(5, Label = "Directory", Type = FieldType.Textbox, HelpText = "DownloadClientAriaSettingsDirectoryHelpText")]
|
[FieldDefinition(5, Label = "Directory", Type = FieldType.Textbox, HelpText = "DownloadClientAriaSettingsDirectoryHelpText")]
|
||||||
public string Directory { get; set; }
|
public string Directory { get; set; }
|
||||||
|
|
||||||
public NzbDroneValidationResult Validate()
|
public override NzbDroneValidationResult Validate()
|
||||||
{
|
{
|
||||||
return new NzbDroneValidationResult(Validator.Validate(this));
|
return new NzbDroneValidationResult(Validator.Validate(this));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ using System.ComponentModel;
|
|||||||
using FluentValidation;
|
using FluentValidation;
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
using NzbDrone.Core.Annotations;
|
using NzbDrone.Core.Annotations;
|
||||||
using NzbDrone.Core.ThingiProvider;
|
|
||||||
using NzbDrone.Core.Validation;
|
using NzbDrone.Core.Validation;
|
||||||
using NzbDrone.Core.Validation.Paths;
|
using NzbDrone.Core.Validation.Paths;
|
||||||
|
|
||||||
@@ -18,7 +17,7 @@ namespace NzbDrone.Core.Download.Clients.Blackhole
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public class TorrentBlackholeSettings : IProviderConfig
|
public class TorrentBlackholeSettings : DownloadClientSettingsBase<TorrentBlackholeSettings>
|
||||||
{
|
{
|
||||||
public TorrentBlackholeSettings()
|
public TorrentBlackholeSettings()
|
||||||
{
|
{
|
||||||
@@ -26,7 +25,7 @@ namespace NzbDrone.Core.Download.Clients.Blackhole
|
|||||||
ReadOnly = true;
|
ReadOnly = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static readonly TorrentBlackholeSettingsValidator Validator = new TorrentBlackholeSettingsValidator();
|
private static readonly TorrentBlackholeSettingsValidator Validator = new ();
|
||||||
|
|
||||||
[FieldDefinition(0, Label = "TorrentBlackholeTorrentFolder", Type = FieldType.Path, HelpText = "BlackholeFolderHelpText")]
|
[FieldDefinition(0, Label = "TorrentBlackholeTorrentFolder", Type = FieldType.Path, HelpText = "BlackholeFolderHelpText")]
|
||||||
[FieldToken(TokenField.HelpText, "TorrentBlackholeTorrentFolder", "extension", ".torrent")]
|
[FieldToken(TokenField.HelpText, "TorrentBlackholeTorrentFolder", "extension", ".torrent")]
|
||||||
@@ -48,7 +47,7 @@ namespace NzbDrone.Core.Download.Clients.Blackhole
|
|||||||
[FieldDefinition(4, Label = "TorrentBlackholeSaveMagnetFilesReadOnly", Type = FieldType.Checkbox, HelpText = "TorrentBlackholeSaveMagnetFilesReadOnlyHelpText")]
|
[FieldDefinition(4, Label = "TorrentBlackholeSaveMagnetFilesReadOnly", Type = FieldType.Checkbox, HelpText = "TorrentBlackholeSaveMagnetFilesReadOnlyHelpText")]
|
||||||
public bool ReadOnly { get; set; }
|
public bool ReadOnly { get; set; }
|
||||||
|
|
||||||
public NzbDroneValidationResult Validate()
|
public override NzbDroneValidationResult Validate()
|
||||||
{
|
{
|
||||||
return new NzbDroneValidationResult(Validator.Validate(this));
|
return new NzbDroneValidationResult(Validator.Validate(this));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
using FluentValidation;
|
using FluentValidation;
|
||||||
using NzbDrone.Core.Annotations;
|
using NzbDrone.Core.Annotations;
|
||||||
using NzbDrone.Core.ThingiProvider;
|
|
||||||
using NzbDrone.Core.Validation;
|
using NzbDrone.Core.Validation;
|
||||||
using NzbDrone.Core.Validation.Paths;
|
using NzbDrone.Core.Validation.Paths;
|
||||||
|
|
||||||
@@ -15,9 +14,9 @@ namespace NzbDrone.Core.Download.Clients.Blackhole
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public class UsenetBlackholeSettings : IProviderConfig
|
public class UsenetBlackholeSettings : DownloadClientSettingsBase<UsenetBlackholeSettings>
|
||||||
{
|
{
|
||||||
private static readonly UsenetBlackholeSettingsValidator Validator = new UsenetBlackholeSettingsValidator();
|
private static readonly UsenetBlackholeSettingsValidator Validator = new ();
|
||||||
|
|
||||||
[FieldDefinition(0, Label = "UsenetBlackholeNzbFolder", Type = FieldType.Path, HelpText = "BlackholeFolderHelpText")]
|
[FieldDefinition(0, Label = "UsenetBlackholeNzbFolder", Type = FieldType.Path, HelpText = "BlackholeFolderHelpText")]
|
||||||
[FieldToken(TokenField.HelpText, "UsenetBlackholeNzbFolder", "extension", ".nzb")]
|
[FieldToken(TokenField.HelpText, "UsenetBlackholeNzbFolder", "extension", ".nzb")]
|
||||||
@@ -26,7 +25,7 @@ namespace NzbDrone.Core.Download.Clients.Blackhole
|
|||||||
[FieldDefinition(1, Label = "BlackholeWatchFolder", Type = FieldType.Path, HelpText = "BlackholeWatchFolderHelpText")]
|
[FieldDefinition(1, Label = "BlackholeWatchFolder", Type = FieldType.Path, HelpText = "BlackholeWatchFolderHelpText")]
|
||||||
public string WatchFolder { get; set; }
|
public string WatchFolder { get; set; }
|
||||||
|
|
||||||
public NzbDroneValidationResult Validate()
|
public override NzbDroneValidationResult Validate()
|
||||||
{
|
{
|
||||||
return new NzbDroneValidationResult(Validator.Validate(this));
|
return new NzbDroneValidationResult(Validator.Validate(this));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -124,14 +124,23 @@ namespace NzbDrone.Core.Download.Clients.Deluge
|
|||||||
}
|
}
|
||||||
|
|
||||||
var items = new List<DownloadClientItem>();
|
var items = new List<DownloadClientItem>();
|
||||||
|
var ignoredCount = 0;
|
||||||
|
|
||||||
foreach (var torrent in torrents)
|
foreach (var torrent in torrents)
|
||||||
{
|
{
|
||||||
if (torrent.Hash == null)
|
// Silently ignore torrents with no hash
|
||||||
|
if (torrent.Hash.IsNullOrWhiteSpace())
|
||||||
{
|
{
|
||||||
continue;
|
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();
|
var item = new DownloadClientItem();
|
||||||
item.DownloadId = torrent.Hash.ToUpper();
|
item.DownloadId = torrent.Hash.ToUpper();
|
||||||
item.Title = torrent.Name;
|
item.Title = torrent.Name;
|
||||||
@@ -189,6 +198,11 @@ namespace NzbDrone.Core.Download.Clients.Deluge
|
|||||||
items.Add(item);
|
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;
|
return items;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
using FluentValidation;
|
using FluentValidation;
|
||||||
using NzbDrone.Core.Annotations;
|
using NzbDrone.Core.Annotations;
|
||||||
using NzbDrone.Core.ThingiProvider;
|
|
||||||
using NzbDrone.Core.Validation;
|
using NzbDrone.Core.Validation;
|
||||||
|
|
||||||
namespace NzbDrone.Core.Download.Clients.Deluge
|
namespace NzbDrone.Core.Download.Clients.Deluge
|
||||||
@@ -17,9 +16,9 @@ namespace NzbDrone.Core.Download.Clients.Deluge
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public class DelugeSettings : IProviderConfig
|
public class DelugeSettings : DownloadClientSettingsBase<DelugeSettings>
|
||||||
{
|
{
|
||||||
private static readonly DelugeSettingsValidator Validator = new DelugeSettingsValidator();
|
private static readonly DelugeSettingsValidator Validator = new ();
|
||||||
|
|
||||||
public DelugeSettings()
|
public DelugeSettings()
|
||||||
{
|
{
|
||||||
@@ -67,7 +66,7 @@ namespace NzbDrone.Core.Download.Clients.Deluge
|
|||||||
[FieldDefinition(11, Label = "DownloadClientDelugeSettingsDirectoryCompleted", Type = FieldType.Textbox, Advanced = true, HelpText = "DownloadClientDelugeSettingsDirectoryCompletedHelpText")]
|
[FieldDefinition(11, Label = "DownloadClientDelugeSettingsDirectoryCompleted", Type = FieldType.Textbox, Advanced = true, HelpText = "DownloadClientDelugeSettingsDirectoryCompletedHelpText")]
|
||||||
public string CompletedDirectory { get; set; }
|
public string CompletedDirectory { get; set; }
|
||||||
|
|
||||||
public NzbDroneValidationResult Validate()
|
public override NzbDroneValidationResult Validate()
|
||||||
{
|
{
|
||||||
return new NzbDroneValidationResult(Validator.Validate(this));
|
return new NzbDroneValidationResult(Validator.Validate(this));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,30 @@
|
|||||||
|
using System;
|
||||||
|
using Equ;
|
||||||
|
using NzbDrone.Core.ThingiProvider;
|
||||||
|
using NzbDrone.Core.Validation;
|
||||||
|
|
||||||
|
namespace NzbDrone.Core.Download.Clients
|
||||||
|
{
|
||||||
|
public abstract class DownloadClientSettingsBase<TSettings> : IProviderConfig, IEquatable<TSettings>
|
||||||
|
where TSettings : DownloadClientSettingsBase<TSettings>
|
||||||
|
{
|
||||||
|
private static readonly MemberwiseEqualityComparer<TSettings> Comparer = MemberwiseEqualityComparer<TSettings>.ByProperties;
|
||||||
|
|
||||||
|
public abstract NzbDroneValidationResult Validate();
|
||||||
|
|
||||||
|
public bool Equals(TSettings other)
|
||||||
|
{
|
||||||
|
return Comparer.Equals(this as TSettings, other);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override bool Equals(object obj)
|
||||||
|
{
|
||||||
|
return Equals(obj as TSettings);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override int GetHashCode()
|
||||||
|
{
|
||||||
|
return Comparer.GetHashCode(this as TSettings);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,8 +1,7 @@
|
|||||||
using System.Text.RegularExpressions;
|
using System.Text.RegularExpressions;
|
||||||
using FluentValidation;
|
using FluentValidation;
|
||||||
using NzbDrone.Common.Extensions;
|
using NzbDrone.Common.Extensions;
|
||||||
using NzbDrone.Core.Annotations;
|
using NzbDrone.Core.Annotations;
|
||||||
using NzbDrone.Core.ThingiProvider;
|
|
||||||
using NzbDrone.Core.Validation;
|
using NzbDrone.Core.Validation;
|
||||||
|
|
||||||
namespace NzbDrone.Core.Download.Clients.DownloadStation
|
namespace NzbDrone.Core.Download.Clients.DownloadStation
|
||||||
@@ -26,9 +25,9 @@ namespace NzbDrone.Core.Download.Clients.DownloadStation
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public class DownloadStationSettings : IProviderConfig
|
public class DownloadStationSettings : DownloadClientSettingsBase<DownloadStationSettings>
|
||||||
{
|
{
|
||||||
private static readonly DownloadStationSettingsValidator Validator = new DownloadStationSettingsValidator();
|
private static readonly DownloadStationSettingsValidator Validator = new ();
|
||||||
|
|
||||||
[FieldDefinition(0, Label = "Host", Type = FieldType.Textbox)]
|
[FieldDefinition(0, Label = "Host", Type = FieldType.Textbox)]
|
||||||
public string Host { get; set; }
|
public string Host { get; set; }
|
||||||
@@ -58,7 +57,7 @@ namespace NzbDrone.Core.Download.Clients.DownloadStation
|
|||||||
this.Port = 5000;
|
this.Port = 5000;
|
||||||
}
|
}
|
||||||
|
|
||||||
public NzbDroneValidationResult Validate()
|
public override NzbDroneValidationResult Validate()
|
||||||
{
|
{
|
||||||
return new NzbDroneValidationResult(Validator.Validate(this));
|
return new NzbDroneValidationResult(Validator.Validate(this));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ using System.Linq;
|
|||||||
using FluentValidation;
|
using FluentValidation;
|
||||||
using NzbDrone.Core.Annotations;
|
using NzbDrone.Core.Annotations;
|
||||||
using NzbDrone.Core.Download.Clients.Flood.Models;
|
using NzbDrone.Core.Download.Clients.Flood.Models;
|
||||||
using NzbDrone.Core.ThingiProvider;
|
|
||||||
using NzbDrone.Core.Validation;
|
using NzbDrone.Core.Validation;
|
||||||
|
|
||||||
namespace NzbDrone.Core.Download.Clients.Flood
|
namespace NzbDrone.Core.Download.Clients.Flood
|
||||||
@@ -17,9 +16,9 @@ namespace NzbDrone.Core.Download.Clients.Flood
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public class FloodSettings : IProviderConfig
|
public class FloodSettings : DownloadClientSettingsBase<FloodSettings>
|
||||||
{
|
{
|
||||||
private static readonly FloodSettingsValidator Validator = new FloodSettingsValidator();
|
private static readonly FloodSettingsValidator Validator = new ();
|
||||||
|
|
||||||
public FloodSettings()
|
public FloodSettings()
|
||||||
{
|
{
|
||||||
@@ -69,7 +68,7 @@ namespace NzbDrone.Core.Download.Clients.Flood
|
|||||||
[FieldDefinition(10, Label = "DownloadClientFloodSettingsStartOnAdd", Type = FieldType.Checkbox)]
|
[FieldDefinition(10, Label = "DownloadClientFloodSettingsStartOnAdd", Type = FieldType.Checkbox)]
|
||||||
public bool StartOnAdd { get; set; }
|
public bool StartOnAdd { get; set; }
|
||||||
|
|
||||||
public NzbDroneValidationResult Validate()
|
public override NzbDroneValidationResult Validate()
|
||||||
{
|
{
|
||||||
return new NzbDroneValidationResult(Validator.Validate(this));
|
return new NzbDroneValidationResult(Validator.Validate(this));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ using System.Text.RegularExpressions;
|
|||||||
using FluentValidation;
|
using FluentValidation;
|
||||||
using NzbDrone.Common.Extensions;
|
using NzbDrone.Common.Extensions;
|
||||||
using NzbDrone.Core.Annotations;
|
using NzbDrone.Core.Annotations;
|
||||||
using NzbDrone.Core.ThingiProvider;
|
|
||||||
using NzbDrone.Core.Validation;
|
using NzbDrone.Core.Validation;
|
||||||
using NzbDrone.Core.Validation.Paths;
|
using NzbDrone.Core.Validation.Paths;
|
||||||
|
|
||||||
@@ -34,9 +33,9 @@ namespace NzbDrone.Core.Download.Clients.FreeboxDownload
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public class FreeboxDownloadSettings : IProviderConfig
|
public class FreeboxDownloadSettings : DownloadClientSettingsBase<FreeboxDownloadSettings>
|
||||||
{
|
{
|
||||||
private static readonly FreeboxDownloadSettingsValidator Validator = new FreeboxDownloadSettingsValidator();
|
private static readonly FreeboxDownloadSettingsValidator Validator = new ();
|
||||||
|
|
||||||
public FreeboxDownloadSettings()
|
public FreeboxDownloadSettings()
|
||||||
{
|
{
|
||||||
@@ -84,7 +83,7 @@ namespace NzbDrone.Core.Download.Clients.FreeboxDownload
|
|||||||
[FieldDefinition(10, Label = "DownloadClientSettingsAddPaused", Type = FieldType.Checkbox)]
|
[FieldDefinition(10, Label = "DownloadClientSettingsAddPaused", Type = FieldType.Checkbox)]
|
||||||
public bool AddPaused { get; set; }
|
public bool AddPaused { get; set; }
|
||||||
|
|
||||||
public NzbDroneValidationResult Validate()
|
public override NzbDroneValidationResult Validate()
|
||||||
{
|
{
|
||||||
return new NzbDroneValidationResult(Validator.Validate(this));
|
return new NzbDroneValidationResult(Validator.Validate(this));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
using FluentValidation;
|
using FluentValidation;
|
||||||
using NzbDrone.Common.Extensions;
|
using NzbDrone.Common.Extensions;
|
||||||
using NzbDrone.Core.Annotations;
|
using NzbDrone.Core.Annotations;
|
||||||
using NzbDrone.Core.ThingiProvider;
|
|
||||||
using NzbDrone.Core.Validation;
|
using NzbDrone.Core.Validation;
|
||||||
|
|
||||||
namespace NzbDrone.Core.Download.Clients.Hadouken
|
namespace NzbDrone.Core.Download.Clients.Hadouken
|
||||||
@@ -22,9 +21,9 @@ namespace NzbDrone.Core.Download.Clients.Hadouken
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public class HadoukenSettings : IProviderConfig
|
public class HadoukenSettings : DownloadClientSettingsBase<HadoukenSettings>
|
||||||
{
|
{
|
||||||
private static readonly HadoukenSettingsValidator Validator = new HadoukenSettingsValidator();
|
private static readonly HadoukenSettingsValidator Validator = new ();
|
||||||
|
|
||||||
public HadoukenSettings()
|
public HadoukenSettings()
|
||||||
{
|
{
|
||||||
@@ -57,7 +56,7 @@ namespace NzbDrone.Core.Download.Clients.Hadouken
|
|||||||
[FieldDefinition(6, Label = "Category", Type = FieldType.Textbox, HelpText = "DownloadClientSettingsCategoryHelpText")]
|
[FieldDefinition(6, Label = "Category", Type = FieldType.Textbox, HelpText = "DownloadClientSettingsCategoryHelpText")]
|
||||||
public string Category { get; set; }
|
public string Category { get; set; }
|
||||||
|
|
||||||
public NzbDroneValidationResult Validate()
|
public override NzbDroneValidationResult Validate()
|
||||||
{
|
{
|
||||||
return new NzbDroneValidationResult(Validator.Validate(this));
|
return new NzbDroneValidationResult(Validator.Validate(this));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
using FluentValidation;
|
using FluentValidation;
|
||||||
using NzbDrone.Common.Extensions;
|
using NzbDrone.Common.Extensions;
|
||||||
using NzbDrone.Core.Annotations;
|
using NzbDrone.Core.Annotations;
|
||||||
using NzbDrone.Core.ThingiProvider;
|
|
||||||
using NzbDrone.Core.Validation;
|
using NzbDrone.Core.Validation;
|
||||||
|
|
||||||
namespace NzbDrone.Core.Download.Clients.NzbVortex
|
namespace NzbDrone.Core.Download.Clients.NzbVortex
|
||||||
@@ -23,9 +22,9 @@ namespace NzbDrone.Core.Download.Clients.NzbVortex
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public class NzbVortexSettings : IProviderConfig
|
public class NzbVortexSettings : DownloadClientSettingsBase<NzbVortexSettings>
|
||||||
{
|
{
|
||||||
private static readonly NzbVortexSettingsValidator Validator = new NzbVortexSettingsValidator();
|
private static readonly NzbVortexSettingsValidator Validator = new ();
|
||||||
|
|
||||||
public NzbVortexSettings()
|
public NzbVortexSettings()
|
||||||
{
|
{
|
||||||
@@ -59,7 +58,7 @@ namespace NzbDrone.Core.Download.Clients.NzbVortex
|
|||||||
[FieldDefinition(6, Label = "DownloadClientSettingsOlderPriority", Type = FieldType.Select, SelectOptions = typeof(NzbVortexPriority), HelpText = "DownloadClientSettingsOlderPriorityEpisodeHelpText")]
|
[FieldDefinition(6, Label = "DownloadClientSettingsOlderPriority", Type = FieldType.Select, SelectOptions = typeof(NzbVortexPriority), HelpText = "DownloadClientSettingsOlderPriorityEpisodeHelpText")]
|
||||||
public int OlderTvPriority { get; set; }
|
public int OlderTvPriority { get; set; }
|
||||||
|
|
||||||
public NzbDroneValidationResult Validate()
|
public override NzbDroneValidationResult Validate()
|
||||||
{
|
{
|
||||||
return new NzbDroneValidationResult(Validator.Validate(this));
|
return new NzbDroneValidationResult(Validator.Validate(this));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
using FluentValidation;
|
using FluentValidation;
|
||||||
using NzbDrone.Common.Extensions;
|
using NzbDrone.Common.Extensions;
|
||||||
using NzbDrone.Core.Annotations;
|
using NzbDrone.Core.Annotations;
|
||||||
using NzbDrone.Core.ThingiProvider;
|
|
||||||
using NzbDrone.Core.Validation;
|
using NzbDrone.Core.Validation;
|
||||||
|
|
||||||
namespace NzbDrone.Core.Download.Clients.Nzbget
|
namespace NzbDrone.Core.Download.Clients.Nzbget
|
||||||
@@ -21,9 +20,9 @@ namespace NzbDrone.Core.Download.Clients.Nzbget
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public class NzbgetSettings : IProviderConfig
|
public class NzbgetSettings : DownloadClientSettingsBase<NzbgetSettings>
|
||||||
{
|
{
|
||||||
private static readonly NzbgetSettingsValidator Validator = new NzbgetSettingsValidator();
|
private static readonly NzbgetSettingsValidator Validator = new ();
|
||||||
|
|
||||||
public NzbgetSettings()
|
public NzbgetSettings()
|
||||||
{
|
{
|
||||||
@@ -67,7 +66,7 @@ namespace NzbDrone.Core.Download.Clients.Nzbget
|
|||||||
[FieldDefinition(9, Label = "DownloadClientSettingsAddPaused", Type = FieldType.Checkbox, HelpText = "DownloadClientNzbgetSettingsAddPausedHelpText")]
|
[FieldDefinition(9, Label = "DownloadClientSettingsAddPaused", Type = FieldType.Checkbox, HelpText = "DownloadClientNzbgetSettingsAddPausedHelpText")]
|
||||||
public bool AddPaused { get; set; }
|
public bool AddPaused { get; set; }
|
||||||
|
|
||||||
public NzbDroneValidationResult Validate()
|
public override NzbDroneValidationResult Validate()
|
||||||
{
|
{
|
||||||
return new NzbDroneValidationResult(Validator.Validate(this));
|
return new NzbDroneValidationResult(Validator.Validate(this));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
using FluentValidation;
|
using FluentValidation;
|
||||||
using NzbDrone.Core.Annotations;
|
using NzbDrone.Core.Annotations;
|
||||||
using NzbDrone.Core.ThingiProvider;
|
|
||||||
using NzbDrone.Core.Validation;
|
using NzbDrone.Core.Validation;
|
||||||
using NzbDrone.Core.Validation.Paths;
|
using NzbDrone.Core.Validation.Paths;
|
||||||
|
|
||||||
@@ -15,9 +14,9 @@ namespace NzbDrone.Core.Download.Clients.Pneumatic
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public class PneumaticSettings : IProviderConfig
|
public class PneumaticSettings : DownloadClientSettingsBase<PneumaticSettings>
|
||||||
{
|
{
|
||||||
private static readonly PneumaticSettingsValidator Validator = new PneumaticSettingsValidator();
|
private static readonly PneumaticSettingsValidator Validator = new ();
|
||||||
|
|
||||||
[FieldDefinition(0, Label = "DownloadClientPneumaticSettingsNzbFolder", Type = FieldType.Path, HelpText = "DownloadClientPneumaticSettingsNzbFolderHelpText")]
|
[FieldDefinition(0, Label = "DownloadClientPneumaticSettingsNzbFolder", Type = FieldType.Path, HelpText = "DownloadClientPneumaticSettingsNzbFolderHelpText")]
|
||||||
public string NzbFolder { get; set; }
|
public string NzbFolder { get; set; }
|
||||||
@@ -25,7 +24,7 @@ namespace NzbDrone.Core.Download.Clients.Pneumatic
|
|||||||
[FieldDefinition(1, Label = "DownloadClientPneumaticSettingsStrmFolder", Type = FieldType.Path, HelpText = "DownloadClientPneumaticSettingsStrmFolderHelpText")]
|
[FieldDefinition(1, Label = "DownloadClientPneumaticSettingsStrmFolder", Type = FieldType.Path, HelpText = "DownloadClientPneumaticSettingsStrmFolderHelpText")]
|
||||||
public string StrmFolder { get; set; }
|
public string StrmFolder { get; set; }
|
||||||
|
|
||||||
public NzbDroneValidationResult Validate()
|
public override NzbDroneValidationResult Validate()
|
||||||
{
|
{
|
||||||
return new NzbDroneValidationResult(Validator.Validate(this));
|
return new NzbDroneValidationResult(Validator.Validate(this));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
using FluentValidation;
|
using FluentValidation;
|
||||||
using NzbDrone.Common.Extensions;
|
using NzbDrone.Common.Extensions;
|
||||||
using NzbDrone.Core.Annotations;
|
using NzbDrone.Core.Annotations;
|
||||||
using NzbDrone.Core.ThingiProvider;
|
|
||||||
using NzbDrone.Core.Validation;
|
using NzbDrone.Core.Validation;
|
||||||
|
|
||||||
namespace NzbDrone.Core.Download.Clients.QBittorrent
|
namespace NzbDrone.Core.Download.Clients.QBittorrent
|
||||||
@@ -19,9 +18,9 @@ namespace NzbDrone.Core.Download.Clients.QBittorrent
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public class QBittorrentSettings : IProviderConfig
|
public class QBittorrentSettings : DownloadClientSettingsBase<QBittorrentSettings>
|
||||||
{
|
{
|
||||||
private static readonly QBittorrentSettingsValidator Validator = new QBittorrentSettingsValidator();
|
private static readonly QBittorrentSettingsValidator Validator = new ();
|
||||||
|
|
||||||
public QBittorrentSettings()
|
public QBittorrentSettings()
|
||||||
{
|
{
|
||||||
@@ -74,7 +73,7 @@ namespace NzbDrone.Core.Download.Clients.QBittorrent
|
|||||||
[FieldDefinition(13, Label = "DownloadClientQbittorrentSettingsContentLayout", Type = FieldType.Select, SelectOptions = typeof(QBittorrentContentLayout), HelpText = "DownloadClientQbittorrentSettingsContentLayoutHelpText")]
|
[FieldDefinition(13, Label = "DownloadClientQbittorrentSettingsContentLayout", Type = FieldType.Select, SelectOptions = typeof(QBittorrentContentLayout), HelpText = "DownloadClientQbittorrentSettingsContentLayoutHelpText")]
|
||||||
public int ContentLayout { get; set; }
|
public int ContentLayout { get; set; }
|
||||||
|
|
||||||
public NzbDroneValidationResult Validate()
|
public override NzbDroneValidationResult Validate()
|
||||||
{
|
{
|
||||||
return new NzbDroneValidationResult(Validator.Validate(this));
|
return new NzbDroneValidationResult(Validator.Validate(this));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
using FluentValidation;
|
using FluentValidation;
|
||||||
using NzbDrone.Common.Extensions;
|
using NzbDrone.Common.Extensions;
|
||||||
using NzbDrone.Core.Annotations;
|
using NzbDrone.Core.Annotations;
|
||||||
using NzbDrone.Core.ThingiProvider;
|
|
||||||
using NzbDrone.Core.Validation;
|
using NzbDrone.Core.Validation;
|
||||||
|
|
||||||
namespace NzbDrone.Core.Download.Clients.Sabnzbd
|
namespace NzbDrone.Core.Download.Clients.Sabnzbd
|
||||||
@@ -32,9 +31,9 @@ namespace NzbDrone.Core.Download.Clients.Sabnzbd
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public class SabnzbdSettings : IProviderConfig
|
public class SabnzbdSettings : DownloadClientSettingsBase<SabnzbdSettings>
|
||||||
{
|
{
|
||||||
private static readonly SabnzbdSettingsValidator Validator = new SabnzbdSettingsValidator();
|
private static readonly SabnzbdSettingsValidator Validator = new ();
|
||||||
|
|
||||||
public SabnzbdSettings()
|
public SabnzbdSettings()
|
||||||
{
|
{
|
||||||
@@ -78,7 +77,7 @@ namespace NzbDrone.Core.Download.Clients.Sabnzbd
|
|||||||
[FieldDefinition(9, Label = "DownloadClientSettingsOlderPriority", Type = FieldType.Select, SelectOptions = typeof(SabnzbdPriority), HelpText = "DownloadClientSettingsOlderPriorityEpisodeHelpText")]
|
[FieldDefinition(9, Label = "DownloadClientSettingsOlderPriority", Type = FieldType.Select, SelectOptions = typeof(SabnzbdPriority), HelpText = "DownloadClientSettingsOlderPriorityEpisodeHelpText")]
|
||||||
public int OlderTvPriority { get; set; }
|
public int OlderTvPriority { get; set; }
|
||||||
|
|
||||||
public NzbDroneValidationResult Validate()
|
public override NzbDroneValidationResult Validate()
|
||||||
{
|
{
|
||||||
return new NzbDroneValidationResult(Validator.Validate(this));
|
return new NzbDroneValidationResult(Validator.Validate(this));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
using System;
|
using System;
|
||||||
|
|
||||||
namespace NzbDrone.Core.Download.Clients
|
namespace NzbDrone.Core.Download.Clients
|
||||||
{
|
{
|
||||||
public class TorrentSeedConfiguration
|
public class TorrentSeedConfiguration
|
||||||
{
|
{
|
||||||
public static TorrentSeedConfiguration DefaultConfiguration = new TorrentSeedConfiguration();
|
public static TorrentSeedConfiguration DefaultConfiguration = new ();
|
||||||
|
|
||||||
public double? Ratio { get; set; }
|
public double? Ratio { get; set; }
|
||||||
public TimeSpan? SeedTime { get; set; }
|
public TimeSpan? SeedTime { get; set; }
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ using System.Text.RegularExpressions;
|
|||||||
using FluentValidation;
|
using FluentValidation;
|
||||||
using NzbDrone.Common.Extensions;
|
using NzbDrone.Common.Extensions;
|
||||||
using NzbDrone.Core.Annotations;
|
using NzbDrone.Core.Annotations;
|
||||||
using NzbDrone.Core.ThingiProvider;
|
|
||||||
using NzbDrone.Core.Validation;
|
using NzbDrone.Core.Validation;
|
||||||
|
|
||||||
namespace NzbDrone.Core.Download.Clients.Transmission
|
namespace NzbDrone.Core.Download.Clients.Transmission
|
||||||
@@ -24,9 +23,9 @@ namespace NzbDrone.Core.Download.Clients.Transmission
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public class TransmissionSettings : IProviderConfig
|
public class TransmissionSettings : DownloadClientSettingsBase<TransmissionSettings>
|
||||||
{
|
{
|
||||||
private static readonly TransmissionSettingsValidator Validator = new TransmissionSettingsValidator();
|
private static readonly TransmissionSettingsValidator Validator = new ();
|
||||||
|
|
||||||
public TransmissionSettings()
|
public TransmissionSettings()
|
||||||
{
|
{
|
||||||
@@ -72,7 +71,7 @@ namespace NzbDrone.Core.Download.Clients.Transmission
|
|||||||
[FieldDefinition(10, Label = "DownloadClientSettingsAddPaused", Type = FieldType.Checkbox)]
|
[FieldDefinition(10, Label = "DownloadClientSettingsAddPaused", Type = FieldType.Checkbox)]
|
||||||
public bool AddPaused { get; set; }
|
public bool AddPaused { get; set; }
|
||||||
|
|
||||||
public NzbDroneValidationResult Validate()
|
public override NzbDroneValidationResult Validate()
|
||||||
{
|
{
|
||||||
return new NzbDroneValidationResult(Validator.Validate(this));
|
return new NzbDroneValidationResult(Validator.Validate(this));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
using FluentValidation;
|
using FluentValidation;
|
||||||
using NzbDrone.Core.Annotations;
|
using NzbDrone.Core.Annotations;
|
||||||
using NzbDrone.Core.ThingiProvider;
|
|
||||||
using NzbDrone.Core.Validation;
|
using NzbDrone.Core.Validation;
|
||||||
|
|
||||||
namespace NzbDrone.Core.Download.Clients.RTorrent
|
namespace NzbDrone.Core.Download.Clients.RTorrent
|
||||||
@@ -17,9 +16,9 @@ namespace NzbDrone.Core.Download.Clients.RTorrent
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public class RTorrentSettings : IProviderConfig
|
public class RTorrentSettings : DownloadClientSettingsBase<RTorrentSettings>
|
||||||
{
|
{
|
||||||
private static readonly RTorrentSettingsValidator Validator = new RTorrentSettingsValidator();
|
private static readonly RTorrentSettingsValidator Validator = new ();
|
||||||
|
|
||||||
public RTorrentSettings()
|
public RTorrentSettings()
|
||||||
{
|
{
|
||||||
@@ -70,7 +69,7 @@ namespace NzbDrone.Core.Download.Clients.RTorrent
|
|||||||
[FieldDefinition(11, Label = "DownloadClientRTorrentSettingsAddStopped", Type = FieldType.Checkbox, HelpText = "DownloadClientRTorrentSettingsAddStoppedHelpText")]
|
[FieldDefinition(11, Label = "DownloadClientRTorrentSettingsAddStopped", Type = FieldType.Checkbox, HelpText = "DownloadClientRTorrentSettingsAddStoppedHelpText")]
|
||||||
public bool AddStopped { get; set; }
|
public bool AddStopped { get; set; }
|
||||||
|
|
||||||
public NzbDroneValidationResult Validate()
|
public override NzbDroneValidationResult Validate()
|
||||||
{
|
{
|
||||||
return new NzbDroneValidationResult(Validator.Validate(this));
|
return new NzbDroneValidationResult(Validator.Validate(this));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
using FluentValidation;
|
using FluentValidation;
|
||||||
using NzbDrone.Common.Extensions;
|
using NzbDrone.Common.Extensions;
|
||||||
using NzbDrone.Core.Annotations;
|
using NzbDrone.Core.Annotations;
|
||||||
using NzbDrone.Core.ThingiProvider;
|
|
||||||
using NzbDrone.Core.Validation;
|
using NzbDrone.Core.Validation;
|
||||||
|
|
||||||
namespace NzbDrone.Core.Download.Clients.UTorrent
|
namespace NzbDrone.Core.Download.Clients.UTorrent
|
||||||
@@ -17,9 +16,9 @@ namespace NzbDrone.Core.Download.Clients.UTorrent
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public class UTorrentSettings : IProviderConfig
|
public class UTorrentSettings : DownloadClientSettingsBase<UTorrentSettings>
|
||||||
{
|
{
|
||||||
private static readonly UTorrentSettingsValidator Validator = new UTorrentSettingsValidator();
|
private static readonly UTorrentSettingsValidator Validator = new ();
|
||||||
|
|
||||||
public UTorrentSettings()
|
public UTorrentSettings()
|
||||||
{
|
{
|
||||||
@@ -65,7 +64,7 @@ namespace NzbDrone.Core.Download.Clients.UTorrent
|
|||||||
[FieldToken(TokenField.HelpText, "DownloadClientSettingsInitialState", "clientName", "uTorrent")]
|
[FieldToken(TokenField.HelpText, "DownloadClientSettingsInitialState", "clientName", "uTorrent")]
|
||||||
public int IntialState { get; set; }
|
public int IntialState { get; set; }
|
||||||
|
|
||||||
public NzbDroneValidationResult Validate()
|
public override NzbDroneValidationResult Validate()
|
||||||
{
|
{
|
||||||
return new NzbDroneValidationResult(Validator.Validate(this));
|
return new NzbDroneValidationResult(Validator.Validate(this));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -64,8 +64,8 @@ namespace NzbDrone.Core.Download
|
|||||||
|
|
||||||
SetImportItem(trackedDownload);
|
SetImportItem(trackedDownload);
|
||||||
|
|
||||||
// Only process tracked downloads that are still 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)
|
if (trackedDownload.State != TrackedDownloadState.Downloading && trackedDownload.State != TrackedDownloadState.ImportBlocked)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -96,7 +96,7 @@ namespace NzbDrone.Core.Download
|
|||||||
if (series == null)
|
if (series == null)
|
||||||
{
|
{
|
||||||
trackedDownload.Warn("Series title mismatch; automatic import is not possible. Check the download troubleshooting entry on the wiki for common causes.");
|
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;
|
return;
|
||||||
}
|
}
|
||||||
@@ -108,7 +108,7 @@ namespace NzbDrone.Core.Download
|
|||||||
if (seriesMatchType == SeriesMatchType.Id && releaseSource != ReleaseSourceType.InteractiveSearch)
|
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.");
|
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;
|
return;
|
||||||
}
|
}
|
||||||
@@ -129,7 +129,7 @@ namespace NzbDrone.Core.Download
|
|||||||
if (trackedDownload.RemoteEpisode == null)
|
if (trackedDownload.RemoteEpisode == null)
|
||||||
{
|
{
|
||||||
trackedDownload.Warn("Unable to parse download, automatic import is not possible.");
|
trackedDownload.Warn("Unable to parse download, automatic import is not possible.");
|
||||||
SendManualInteractionRequiredNotification(trackedDownload);
|
SetStateToImportBlocked(trackedDownload);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -187,7 +187,7 @@ namespace NzbDrone.Core.Download
|
|||||||
if (statusMessages.Any())
|
if (statusMessages.Any())
|
||||||
{
|
{
|
||||||
trackedDownload.Warn(statusMessages.ToArray());
|
trackedDownload.Warn(statusMessages.ToArray());
|
||||||
SendManualInteractionRequiredNotification(trackedDownload);
|
SetStateToImportBlocked(trackedDownload);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -254,8 +254,10 @@ namespace NzbDrone.Core.Download
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void SendManualInteractionRequiredNotification(TrackedDownload trackedDownload)
|
private void SetStateToImportBlocked(TrackedDownload trackedDownload)
|
||||||
{
|
{
|
||||||
|
trackedDownload.State = TrackedDownloadState.ImportBlocked;
|
||||||
|
|
||||||
if (!trackedDownload.HasNotifiedManualInteractionRequired)
|
if (!trackedDownload.HasNotifiedManualInteractionRequired)
|
||||||
{
|
{
|
||||||
var grabbedHistories = _historyService.FindByDownloadId(trackedDownload.DownloadItem.DownloadId).Where(h => h.EventType == EpisodeHistoryEventType.Grabbed).ToList();
|
var grabbedHistories = _historyService.FindByDownloadId(trackedDownload.DownloadItem.DownloadId).Where(h => h.EventType == EpisodeHistoryEventType.Grabbed).ToList();
|
||||||
|
|||||||
@@ -1,14 +1,35 @@
|
|||||||
using NzbDrone.Core.Indexers;
|
using System;
|
||||||
|
using Equ;
|
||||||
|
using NzbDrone.Core.Indexers;
|
||||||
using NzbDrone.Core.ThingiProvider;
|
using NzbDrone.Core.ThingiProvider;
|
||||||
|
|
||||||
namespace NzbDrone.Core.Download
|
namespace NzbDrone.Core.Download
|
||||||
{
|
{
|
||||||
public class DownloadClientDefinition : ProviderDefinition
|
public class DownloadClientDefinition : ProviderDefinition, IEquatable<DownloadClientDefinition>
|
||||||
{
|
{
|
||||||
|
private static readonly MemberwiseEqualityComparer<DownloadClientDefinition> Comparer = MemberwiseEqualityComparer<DownloadClientDefinition>.ByProperties;
|
||||||
|
|
||||||
|
[MemberwiseEqualityIgnore]
|
||||||
public DownloadProtocol Protocol { get; set; }
|
public DownloadProtocol Protocol { get; set; }
|
||||||
|
|
||||||
public int Priority { get; set; } = 1;
|
public int Priority { get; set; } = 1;
|
||||||
|
|
||||||
public bool RemoveCompletedDownloads { get; set; } = true;
|
public bool RemoveCompletedDownloads { get; set; } = true;
|
||||||
public bool RemoveFailedDownloads { get; set; } = true;
|
public bool RemoveFailedDownloads { get; set; } = true;
|
||||||
|
|
||||||
|
public bool Equals(DownloadClientDefinition other)
|
||||||
|
{
|
||||||
|
return Comparer.Equals(this, other);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override bool Equals(object obj)
|
||||||
|
{
|
||||||
|
return Equals(obj as DownloadClientDefinition);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override int GetHashCode()
|
||||||
|
{
|
||||||
|
return Comparer.GetHashCode(this);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -73,8 +73,8 @@ namespace NzbDrone.Core.Download
|
|||||||
|
|
||||||
public void Check(TrackedDownload trackedDownload)
|
public void Check(TrackedDownload trackedDownload)
|
||||||
{
|
{
|
||||||
// Only process tracked downloads that are still 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)
|
if (trackedDownload.State != TrackedDownloadState.Downloading && trackedDownload.State != TrackedDownloadState.ImportBlocked)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -122,7 +122,7 @@ namespace NzbDrone.Core.Download.TrackedDownloads
|
|||||||
_trackedDownloadService.TrackDownload((DownloadClientDefinition)downloadClient.Definition,
|
_trackedDownloadService.TrackDownload((DownloadClientDefinition)downloadClient.Definition,
|
||||||
downloadItem);
|
downloadItem);
|
||||||
|
|
||||||
if (trackedDownload != null && trackedDownload.State == TrackedDownloadState.Downloading)
|
if (trackedDownload is { State: TrackedDownloadState.Downloading or TrackedDownloadState.ImportBlocked })
|
||||||
{
|
{
|
||||||
_failedDownloadService.Check(trackedDownload);
|
_failedDownloadService.Check(trackedDownload);
|
||||||
_completedDownloadService.Check(trackedDownload);
|
_completedDownloadService.Check(trackedDownload);
|
||||||
|
|||||||
@@ -40,6 +40,7 @@ namespace NzbDrone.Core.Download.TrackedDownloads
|
|||||||
public enum TrackedDownloadState
|
public enum TrackedDownloadState
|
||||||
{
|
{
|
||||||
Downloading,
|
Downloading,
|
||||||
|
ImportBlocked,
|
||||||
ImportPending,
|
ImportPending,
|
||||||
Importing,
|
Importing,
|
||||||
Imported,
|
Imported,
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ namespace NzbDrone.Core.Download.TrackedDownloads
|
|||||||
|
|
||||||
public class TrackedDownloadService : ITrackedDownloadService,
|
public class TrackedDownloadService : ITrackedDownloadService,
|
||||||
IHandle<EpisodeInfoRefreshedEvent>,
|
IHandle<EpisodeInfoRefreshedEvent>,
|
||||||
|
IHandle<SeriesAddedEvent>,
|
||||||
IHandle<SeriesDeletedEvent>
|
IHandle<SeriesDeletedEvent>
|
||||||
{
|
{
|
||||||
private readonly IParsingService _parsingService;
|
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)
|
public void Handle(SeriesDeletedEvent message)
|
||||||
{
|
{
|
||||||
var cachedItems = _cache.Values.Where(t =>
|
var cachedItems = _cache.Values
|
||||||
t.RemoteEpisode?.Series != null &&
|
.Where(t =>
|
||||||
message.Series.Any(s => s.Id == t.RemoteEpisode.Series.Id))
|
t.RemoteEpisode?.Series != null &&
|
||||||
.ToList();
|
message.Series.Any(s => s.Id == t.RemoteEpisode.Series.Id || s.TvdbId == t.RemoteEpisode.Series.TvdbId))
|
||||||
|
.ToList();
|
||||||
|
|
||||||
if (cachedItems.Any())
|
if (cachedItems.Any())
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ namespace NzbDrone.Core.Extras.Metadata.Consumers.Xbmc
|
|||||||
Tvdb = series.TvdbId.ToString();
|
Tvdb = series.TvdbId.ToString();
|
||||||
TvMaze = series.TvMazeId > 0 ? series.TvMazeId.ToString() : null;
|
TvMaze = series.TvMazeId > 0 ? series.TvMazeId.ToString() : null;
|
||||||
TvRage = series.TvRageId > 0 ? series.TvMazeId.ToString() : null;
|
TvRage = series.TvRageId > 0 ? series.TvMazeId.ToString() : null;
|
||||||
|
Tmdb = series.TmdbId > 0 ? series.TmdbId.ToString() : null;
|
||||||
Imdb = series.ImdbId;
|
Imdb = series.ImdbId;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,18 +29,17 @@ namespace NzbDrone.Core.ImportLists.AniList
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public class AniListSettingsBase<TSettings> : IImportListSettings
|
public class AniListSettingsBase<TSettings> : ImportListSettingsBase<TSettings>
|
||||||
where TSettings : AniListSettingsBase<TSettings>
|
where TSettings : AniListSettingsBase<TSettings>
|
||||||
{
|
{
|
||||||
protected virtual AbstractValidator<TSettings> Validator => new AniListSettingsBaseValidator<TSettings>();
|
private static readonly AniListSettingsBaseValidator<TSettings> Validator = new ();
|
||||||
|
|
||||||
public AniListSettingsBase()
|
public AniListSettingsBase()
|
||||||
{
|
{
|
||||||
BaseUrl = "https://graphql.anilist.co";
|
|
||||||
SignIn = "startOAuth";
|
SignIn = "startOAuth";
|
||||||
}
|
}
|
||||||
|
|
||||||
public string BaseUrl { get; set; }
|
public override string BaseUrl { get; set; } = "https://graphql.anilist.co";
|
||||||
|
|
||||||
[FieldDefinition(0, Label = "ImportListsSettingsAccessToken", Type = FieldType.Textbox, Hidden = HiddenType.Hidden)]
|
[FieldDefinition(0, Label = "ImportListsSettingsAccessToken", Type = FieldType.Textbox, Hidden = HiddenType.Hidden)]
|
||||||
public string AccessToken { get; set; }
|
public string AccessToken { get; set; }
|
||||||
@@ -54,7 +53,7 @@ namespace NzbDrone.Core.ImportLists.AniList
|
|||||||
[FieldDefinition(99, Label = "ImportListsAniListSettingsAuthenticateWithAniList", Type = FieldType.OAuth)]
|
[FieldDefinition(99, Label = "ImportListsAniListSettingsAuthenticateWithAniList", Type = FieldType.OAuth)]
|
||||||
public string SignIn { get; set; }
|
public string SignIn { get; set; }
|
||||||
|
|
||||||
public NzbDroneValidationResult Validate()
|
public override NzbDroneValidationResult Validate()
|
||||||
{
|
{
|
||||||
return new NzbDroneValidationResult(Validator.Validate((TSettings)this));
|
return new NzbDroneValidationResult(Validator.Validate((TSettings)this));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
using FluentValidation;
|
using FluentValidation;
|
||||||
using NzbDrone.Core.Annotations;
|
using NzbDrone.Core.Annotations;
|
||||||
|
using NzbDrone.Core.Validation;
|
||||||
|
|
||||||
namespace NzbDrone.Core.ImportLists.AniList.List
|
namespace NzbDrone.Core.ImportLists.AniList.List
|
||||||
{
|
{
|
||||||
public class AniListSettingsValidator : AniListSettingsBaseValidator<AniListSettings>
|
public class AniListSettingsValidator : AniListSettingsBaseValidator<AniListSettings>
|
||||||
{
|
{
|
||||||
public AniListSettingsValidator()
|
public AniListSettingsValidator()
|
||||||
: base()
|
|
||||||
{
|
{
|
||||||
RuleFor(c => c.Username).NotEmpty();
|
RuleFor(c => c.Username).NotEmpty();
|
||||||
|
|
||||||
@@ -18,10 +18,11 @@ namespace NzbDrone.Core.ImportLists.AniList.List
|
|||||||
|
|
||||||
public class AniListSettings : AniListSettingsBase<AniListSettings>
|
public class AniListSettings : AniListSettingsBase<AniListSettings>
|
||||||
{
|
{
|
||||||
public const string sectionImport = "Import List Status";
|
public const string SectionImport = "Import List Status";
|
||||||
|
|
||||||
|
private static readonly AniListSettingsValidator Validator = new ();
|
||||||
|
|
||||||
public AniListSettings()
|
public AniListSettings()
|
||||||
: base()
|
|
||||||
{
|
{
|
||||||
ImportCurrent = true;
|
ImportCurrent = true;
|
||||||
ImportPlanning = true;
|
ImportPlanning = true;
|
||||||
@@ -29,42 +30,45 @@ namespace NzbDrone.Core.ImportLists.AniList.List
|
|||||||
ImportFinished = true;
|
ImportFinished = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override AbstractValidator<AniListSettings> Validator => new AniListSettingsValidator();
|
|
||||||
|
|
||||||
[FieldDefinition(1, Label = "Username", HelpText = "ImportListsAniListSettingsUsernameHelpText")]
|
[FieldDefinition(1, Label = "Username", HelpText = "ImportListsAniListSettingsUsernameHelpText")]
|
||||||
public string Username { get; set; }
|
public string Username { get; set; }
|
||||||
|
|
||||||
[FieldDefinition(2, Label = "ImportListsAniListSettingsImportWatching", Type = FieldType.Checkbox, Section = sectionImport, HelpText = "ImportListsAniListSettingsImportWatchingHelpText")]
|
[FieldDefinition(2, Label = "ImportListsAniListSettingsImportWatching", Type = FieldType.Checkbox, Section = SectionImport, HelpText = "ImportListsAniListSettingsImportWatchingHelpText")]
|
||||||
public bool ImportCurrent { get; set; }
|
public bool ImportCurrent { get; set; }
|
||||||
|
|
||||||
[FieldDefinition(3, Label = "ImportListsAniListSettingsImportPlanning", Type = FieldType.Checkbox, Section = sectionImport, HelpText = "ImportListsAniListSettingsImportPlanningHelpText")]
|
[FieldDefinition(3, Label = "ImportListsAniListSettingsImportPlanning", Type = FieldType.Checkbox, Section = SectionImport, HelpText = "ImportListsAniListSettingsImportPlanningHelpText")]
|
||||||
public bool ImportPlanning { get; set; }
|
public bool ImportPlanning { get; set; }
|
||||||
|
|
||||||
[FieldDefinition(4, Label = "ImportListsAniListSettingsImportCompleted", Type = FieldType.Checkbox, Section = sectionImport, HelpText = "ImportListsAniListSettingsImportCompletedHelpText")]
|
[FieldDefinition(4, Label = "ImportListsAniListSettingsImportCompleted", Type = FieldType.Checkbox, Section = SectionImport, HelpText = "ImportListsAniListSettingsImportCompletedHelpText")]
|
||||||
public bool ImportCompleted { get; set; }
|
public bool ImportCompleted { get; set; }
|
||||||
|
|
||||||
[FieldDefinition(5, Label = "ImportListsAniListSettingsImportDropped", Type = FieldType.Checkbox, Section = sectionImport, HelpText = "ImportListsAniListSettingsImportDroppedHelpText")]
|
[FieldDefinition(5, Label = "ImportListsAniListSettingsImportDropped", Type = FieldType.Checkbox, Section = SectionImport, HelpText = "ImportListsAniListSettingsImportDroppedHelpText")]
|
||||||
public bool ImportDropped { get; set; }
|
public bool ImportDropped { get; set; }
|
||||||
|
|
||||||
[FieldDefinition(6, Label = "ImportListsAniListSettingsImportPaused", Type = FieldType.Checkbox, Section = sectionImport, HelpText = "ImportListsAniListSettingsImportPausedHelpText")]
|
[FieldDefinition(6, Label = "ImportListsAniListSettingsImportPaused", Type = FieldType.Checkbox, Section = SectionImport, HelpText = "ImportListsAniListSettingsImportPausedHelpText")]
|
||||||
public bool ImportPaused { get; set; }
|
public bool ImportPaused { get; set; }
|
||||||
|
|
||||||
[FieldDefinition(7, Label = "ImportListsAniListSettingsImportRepeating", Type = FieldType.Checkbox, Section = sectionImport, HelpText = "ImportListsAniListSettingsImportRepeatingHelpText")]
|
[FieldDefinition(7, Label = "ImportListsAniListSettingsImportRepeating", Type = FieldType.Checkbox, Section = SectionImport, HelpText = "ImportListsAniListSettingsImportRepeatingHelpText")]
|
||||||
public bool ImportRepeating { get; set; }
|
public bool ImportRepeating { get; set; }
|
||||||
|
|
||||||
[FieldDefinition(8, Label = "ImportListsAniListSettingsImportFinished", Type = FieldType.Checkbox, Section = sectionImport, HelpText = "ImportListsAniListSettingsImportFinishedHelpText")]
|
[FieldDefinition(8, Label = "ImportListsAniListSettingsImportFinished", Type = FieldType.Checkbox, Section = SectionImport, HelpText = "ImportListsAniListSettingsImportFinishedHelpText")]
|
||||||
public bool ImportFinished { get; set; }
|
public bool ImportFinished { get; set; }
|
||||||
|
|
||||||
[FieldDefinition(9, Label = "ImportListsAniListSettingsImportReleasing", Type = FieldType.Checkbox, Section = sectionImport, HelpText = "ImportListsAniListSettingsImportReleasingHelpText")]
|
[FieldDefinition(9, Label = "ImportListsAniListSettingsImportReleasing", Type = FieldType.Checkbox, Section = SectionImport, HelpText = "ImportListsAniListSettingsImportReleasingHelpText")]
|
||||||
public bool ImportReleasing { get; set; }
|
public bool ImportReleasing { get; set; }
|
||||||
|
|
||||||
[FieldDefinition(10, Label = "ImportListsAniListSettingsImportNotYetReleased", Type = FieldType.Checkbox, Section = sectionImport, HelpText = "ImportListsAniListSettingsImportNotYetReleasedHelpText")]
|
[FieldDefinition(10, Label = "ImportListsAniListSettingsImportNotYetReleased", Type = FieldType.Checkbox, Section = SectionImport, HelpText = "ImportListsAniListSettingsImportNotYetReleasedHelpText")]
|
||||||
public bool ImportUnreleased { get; set; }
|
public bool ImportUnreleased { get; set; }
|
||||||
|
|
||||||
[FieldDefinition(11, Label = "ImportListsAniListSettingsImportCancelled", Type = FieldType.Checkbox, Section = sectionImport, HelpText = "ImportListsAniListSettingsImportCancelledHelpText")]
|
[FieldDefinition(11, Label = "ImportListsAniListSettingsImportCancelled", Type = FieldType.Checkbox, Section = SectionImport, HelpText = "ImportListsAniListSettingsImportCancelledHelpText")]
|
||||||
public bool ImportCancelled { get; set; }
|
public bool ImportCancelled { get; set; }
|
||||||
|
|
||||||
[FieldDefinition(12, Label = "ImportListsAniListSettingsImportHiatus", Type = FieldType.Checkbox, Section = sectionImport, HelpText = "ImportListsAniListSettingsImportHiatusHelpText")]
|
[FieldDefinition(12, Label = "ImportListsAniListSettingsImportHiatus", Type = FieldType.Checkbox, Section = SectionImport, HelpText = "ImportListsAniListSettingsImportHiatusHelpText")]
|
||||||
public bool ImportHiatus { get; set; }
|
public bool ImportHiatus { get; set; }
|
||||||
|
|
||||||
|
public override NzbDroneValidationResult Validate()
|
||||||
|
{
|
||||||
|
return new NzbDroneValidationResult(Validator.Validate(this));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,19 +13,14 @@ namespace NzbDrone.Core.ImportLists.Custom
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public class CustomSettings : IImportListSettings
|
public class CustomSettings : ImportListSettingsBase<CustomSettings>
|
||||||
{
|
{
|
||||||
private static readonly CustomSettingsValidator Validator = new CustomSettingsValidator();
|
private static readonly CustomSettingsValidator Validator = new ();
|
||||||
|
|
||||||
public CustomSettings()
|
|
||||||
{
|
|
||||||
BaseUrl = "";
|
|
||||||
}
|
|
||||||
|
|
||||||
[FieldDefinition(0, Label = "ImportListsCustomListSettingsUrl", HelpText = "ImportListsCustomListSettingsUrlHelpText")]
|
[FieldDefinition(0, Label = "ImportListsCustomListSettingsUrl", HelpText = "ImportListsCustomListSettingsUrlHelpText")]
|
||||||
public string BaseUrl { get; set; }
|
public override string BaseUrl { get; set; } = string.Empty;
|
||||||
|
|
||||||
public NzbDroneValidationResult Validate()
|
public override NzbDroneValidationResult Validate()
|
||||||
{
|
{
|
||||||
return new NzbDroneValidationResult(Validator.Validate(this));
|
return new NzbDroneValidationResult(Validator.Validate(this));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ namespace NzbDrone.Core.ImportLists.Imdb
|
|||||||
// Parse TSV response from IMDB export
|
// Parse TSV response from IMDB export
|
||||||
var rows = importResponse.Content.Split(new char[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries);
|
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;
|
return series;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,16 +14,16 @@ namespace NzbDrone.Core.ImportLists.Imdb
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public class ImdbListSettings : IImportListSettings
|
public class ImdbListSettings : ImportListSettingsBase<ImdbListSettings>
|
||||||
{
|
{
|
||||||
private static readonly ImdbSettingsValidator Validator = new ImdbSettingsValidator();
|
private static readonly ImdbSettingsValidator Validator = new ();
|
||||||
|
|
||||||
public string BaseUrl { get; set; }
|
public override string BaseUrl { get; set; }
|
||||||
|
|
||||||
[FieldDefinition(1, Label = "ImportListsImdbSettingsListId", HelpText = "ImportListsImdbSettingsListIdHelpText")]
|
[FieldDefinition(1, Label = "ImportListsImdbSettingsListId", HelpText = "ImportListsImdbSettingsListIdHelpText")]
|
||||||
public string ListId { get; set; }
|
public string ListId { get; set; }
|
||||||
|
|
||||||
public NzbDroneValidationResult Validate()
|
public override NzbDroneValidationResult Validate()
|
||||||
{
|
{
|
||||||
return new NzbDroneValidationResult(Validator.Validate(this));
|
return new NzbDroneValidationResult(Validator.Validate(this));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,11 +1,14 @@
|
|||||||
using System;
|
using System;
|
||||||
|
using Equ;
|
||||||
using NzbDrone.Core.ThingiProvider;
|
using NzbDrone.Core.ThingiProvider;
|
||||||
using NzbDrone.Core.Tv;
|
using NzbDrone.Core.Tv;
|
||||||
|
|
||||||
namespace NzbDrone.Core.ImportLists
|
namespace NzbDrone.Core.ImportLists
|
||||||
{
|
{
|
||||||
public class ImportListDefinition : ProviderDefinition
|
public class ImportListDefinition : ProviderDefinition, IEquatable<ImportListDefinition>
|
||||||
{
|
{
|
||||||
|
private static readonly MemberwiseEqualityComparer<ImportListDefinition> Comparer = MemberwiseEqualityComparer<ImportListDefinition>.ByProperties;
|
||||||
|
|
||||||
public bool EnableAutomaticAdd { get; set; }
|
public bool EnableAutomaticAdd { get; set; }
|
||||||
public bool SearchForMissingEpisodes { get; set; }
|
public bool SearchForMissingEpisodes { get; set; }
|
||||||
public MonitorTypes ShouldMonitor { get; set; }
|
public MonitorTypes ShouldMonitor { get; set; }
|
||||||
@@ -15,10 +18,31 @@ namespace NzbDrone.Core.ImportLists
|
|||||||
public bool SeasonFolder { get; set; }
|
public bool SeasonFolder { get; set; }
|
||||||
public string RootFolderPath { get; set; }
|
public string RootFolderPath { get; set; }
|
||||||
|
|
||||||
|
[MemberwiseEqualityIgnore]
|
||||||
public override bool Enable => EnableAutomaticAdd;
|
public override bool Enable => EnableAutomaticAdd;
|
||||||
|
|
||||||
|
[MemberwiseEqualityIgnore]
|
||||||
public ImportListStatus Status { get; set; }
|
public ImportListStatus Status { get; set; }
|
||||||
|
|
||||||
|
[MemberwiseEqualityIgnore]
|
||||||
public ImportListType ListType { get; set; }
|
public ImportListType ListType { get; set; }
|
||||||
|
|
||||||
|
[MemberwiseEqualityIgnore]
|
||||||
public TimeSpan MinRefreshInterval { get; set; }
|
public TimeSpan MinRefreshInterval { get; set; }
|
||||||
|
|
||||||
|
public bool Equals(ImportListDefinition other)
|
||||||
|
{
|
||||||
|
return Comparer.Equals(this, other);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override bool Equals(object obj)
|
||||||
|
{
|
||||||
|
return Equals(obj as ImportListDefinition);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override int GetHashCode()
|
||||||
|
{
|
||||||
|
return Comparer.GetHashCode(this);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,31 @@
|
|||||||
|
using System;
|
||||||
|
using Equ;
|
||||||
|
using NzbDrone.Core.Validation;
|
||||||
|
|
||||||
|
namespace NzbDrone.Core.ImportLists
|
||||||
|
{
|
||||||
|
public abstract class ImportListSettingsBase<TSettings> : IImportListSettings, IEquatable<TSettings>
|
||||||
|
where TSettings : ImportListSettingsBase<TSettings>
|
||||||
|
{
|
||||||
|
private static readonly MemberwiseEqualityComparer<TSettings> Comparer = MemberwiseEqualityComparer<TSettings>.ByProperties;
|
||||||
|
|
||||||
|
public abstract string BaseUrl { get; set; }
|
||||||
|
|
||||||
|
public abstract NzbDroneValidationResult Validate();
|
||||||
|
|
||||||
|
public bool Equals(TSettings other)
|
||||||
|
{
|
||||||
|
return Comparer.Equals(this as TSettings, other);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override bool Equals(object obj)
|
||||||
|
{
|
||||||
|
return Equals(obj as TSettings);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override int GetHashCode()
|
||||||
|
{
|
||||||
|
return Comparer.GetHashCode(this as TSettings);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -24,16 +24,11 @@ namespace NzbDrone.Core.ImportLists.MyAnimeList
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public class MyAnimeListSettings : IImportListSettings
|
public class MyAnimeListSettings : ImportListSettingsBase<MyAnimeListSettings>
|
||||||
{
|
{
|
||||||
public string BaseUrl { get; set; }
|
private static readonly MalSettingsValidator Validator = new ();
|
||||||
|
|
||||||
protected AbstractValidator<MyAnimeListSettings> Validator => new MalSettingsValidator();
|
public override string BaseUrl { get; set; } = "https://api.myanimelist.net/v2";
|
||||||
|
|
||||||
public MyAnimeListSettings()
|
|
||||||
{
|
|
||||||
BaseUrl = "https://api.myanimelist.net/v2";
|
|
||||||
}
|
|
||||||
|
|
||||||
[FieldDefinition(0, Label = "ImportListsMyAnimeListSettingsListStatus", Type = FieldType.Select, SelectOptions = typeof(MyAnimeListStatus), HelpText = "ImportListsMyAnimeListSettingsListStatusHelpText")]
|
[FieldDefinition(0, Label = "ImportListsMyAnimeListSettingsListStatus", Type = FieldType.Select, SelectOptions = typeof(MyAnimeListStatus), HelpText = "ImportListsMyAnimeListSettingsListStatusHelpText")]
|
||||||
public int ListStatus { get; set; }
|
public int ListStatus { get; set; }
|
||||||
@@ -50,7 +45,7 @@ namespace NzbDrone.Core.ImportLists.MyAnimeList
|
|||||||
[FieldDefinition(99, Label = "ImportListsMyAnimeListSettingsAuthenticateWithMyAnimeList", Type = FieldType.OAuth)]
|
[FieldDefinition(99, Label = "ImportListsMyAnimeListSettingsAuthenticateWithMyAnimeList", Type = FieldType.OAuth)]
|
||||||
public string SignIn { get; set; }
|
public string SignIn { get; set; }
|
||||||
|
|
||||||
public NzbDroneValidationResult Validate()
|
public override NzbDroneValidationResult Validate()
|
||||||
{
|
{
|
||||||
return new NzbDroneValidationResult(Validator.Validate(this));
|
return new NzbDroneValidationResult(Validator.Validate(this));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,9 +14,9 @@ namespace NzbDrone.Core.ImportLists.Plex
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public class PlexListSettings : IImportListSettings
|
public class PlexListSettings : ImportListSettingsBase<PlexListSettings>
|
||||||
{
|
{
|
||||||
protected virtual PlexListSettingsValidator Validator => new PlexListSettingsValidator();
|
private static readonly PlexListSettingsValidator Validator = new ();
|
||||||
|
|
||||||
public PlexListSettings()
|
public PlexListSettings()
|
||||||
{
|
{
|
||||||
@@ -25,7 +25,7 @@ namespace NzbDrone.Core.ImportLists.Plex
|
|||||||
|
|
||||||
public virtual string Scope => "";
|
public virtual string Scope => "";
|
||||||
|
|
||||||
public string BaseUrl { get; set; }
|
public override string BaseUrl { get; set; }
|
||||||
|
|
||||||
[FieldDefinition(0, Label = "ImportListsSettingsAccessToken", Type = FieldType.Textbox, Hidden = HiddenType.Hidden)]
|
[FieldDefinition(0, Label = "ImportListsSettingsAccessToken", Type = FieldType.Textbox, Hidden = HiddenType.Hidden)]
|
||||||
public string AccessToken { get; set; }
|
public string AccessToken { get; set; }
|
||||||
@@ -33,7 +33,7 @@ namespace NzbDrone.Core.ImportLists.Plex
|
|||||||
[FieldDefinition(99, Label = "ImportListsPlexSettingsAuthenticateWithPlex", Type = FieldType.OAuth)]
|
[FieldDefinition(99, Label = "ImportListsPlexSettingsAuthenticateWithPlex", Type = FieldType.OAuth)]
|
||||||
public string SignIn { get; set; }
|
public string SignIn { get; set; }
|
||||||
|
|
||||||
public NzbDroneValidationResult Validate()
|
public override NzbDroneValidationResult Validate()
|
||||||
{
|
{
|
||||||
return new NzbDroneValidationResult(Validator.Validate(this));
|
return new NzbDroneValidationResult(Validator.Validate(this));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,9 +12,9 @@ namespace NzbDrone.Core.ImportLists.Rss.Plex
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public class PlexRssImportSettings : RssImportBaseSettings
|
public class PlexRssImportSettings : RssImportBaseSettings<PlexRssImportSettings>
|
||||||
{
|
{
|
||||||
private PlexRssImportSettingsValidator Validator => new ();
|
private static readonly PlexRssImportSettingsValidator Validator = new ();
|
||||||
|
|
||||||
[FieldDefinition(0, Label = "ImportListsSettingsRssUrl", Type = FieldType.Textbox, HelpLink = "https://app.plex.tv/desktop/#!/settings/watchlist")]
|
[FieldDefinition(0, Label = "ImportListsSettingsRssUrl", Type = FieldType.Textbox, HelpLink = "https://app.plex.tv/desktop/#!/settings/watchlist")]
|
||||||
public override string Url { get; set; }
|
public override string Url { get; set; }
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ using NzbDrone.Core.Parser;
|
|||||||
namespace NzbDrone.Core.ImportLists.Rss
|
namespace NzbDrone.Core.ImportLists.Rss
|
||||||
{
|
{
|
||||||
public class RssImportBase<TSettings> : HttpImportListBase<TSettings>
|
public class RssImportBase<TSettings> : HttpImportListBase<TSettings>
|
||||||
where TSettings : RssImportBaseSettings, new()
|
where TSettings : RssImportBaseSettings<TSettings>, new()
|
||||||
{
|
{
|
||||||
public override string Name => "RSS List Base";
|
public override string Name => "RSS List Base";
|
||||||
public override ImportListType ListType => ImportListType.Advanced;
|
public override ImportListType ListType => ImportListType.Advanced;
|
||||||
@@ -36,7 +36,7 @@ namespace NzbDrone.Core.ImportLists.Rss
|
|||||||
|
|
||||||
public override IImportListRequestGenerator GetRequestGenerator()
|
public override IImportListRequestGenerator GetRequestGenerator()
|
||||||
{
|
{
|
||||||
return new RssImportRequestGenerator
|
return new RssImportRequestGenerator<TSettings>
|
||||||
{
|
{
|
||||||
Settings = Settings
|
Settings = Settings
|
||||||
};
|
};
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user