mirror of
https://github.com/Sonarr/Sonarr.git
synced 2026-04-17 21:26:13 -04:00
Compare commits
44 Commits
v4.0.0.825
...
v4.0.1.933
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d336aaf3f0 | ||
|
|
ec40bc6eea | ||
|
|
75bb34afaa | ||
|
|
de1cc25c90 | ||
|
|
9884f6f282 | ||
|
|
e792db4d33 | ||
|
|
2dbf5b5a71 | ||
|
|
c6bb6ad878 | ||
|
|
bfd24da2d9 | ||
|
|
8dd3b45c90 | ||
|
|
0b0f21d0ac | ||
|
|
738f5c58ad | ||
|
|
9a7b5bf14e | ||
|
|
e1260d504e | ||
|
|
736651324f | ||
|
|
489f03441b | ||
|
|
e4b5d559df | ||
|
|
07fbb0d1f4 | ||
|
|
666455f9b1 | ||
|
|
091449d9bf | ||
|
|
f87a66fcba | ||
|
|
1bba7e177b | ||
|
|
57445bbe57 | ||
|
|
ec91142c85 | ||
|
|
0685896ed8 | ||
|
|
ee0048c768 | ||
|
|
16e5ffa467 | ||
|
|
431c66c7c1 | ||
|
|
57bd6539c8 | ||
|
|
637cb1711d | ||
|
|
7e011df2b2 | ||
|
|
79907c881c | ||
|
|
db6a627983 | ||
|
|
d76a489be6 | ||
|
|
fd17df0dd0 | ||
|
|
53cf530893 | ||
|
|
91f33c670e | ||
|
|
d322619733 | ||
|
|
1182798929 | ||
|
|
ad0249c7db | ||
|
|
3259e6dc10 | ||
|
|
541d3307e1 | ||
|
|
ad60352bae | ||
|
|
02e051580c |
@@ -203,6 +203,7 @@ dotnet_diagnostic.CA1819.severity = suggestion
|
||||
dotnet_diagnostic.CA1822.severity = suggestion
|
||||
dotnet_diagnostic.CA1823.severity = suggestion
|
||||
dotnet_diagnostic.CA1824.severity = suggestion
|
||||
dotnet_diagnostic.CA1825.severity = suggestion
|
||||
dotnet_diagnostic.CA2000.severity = suggestion
|
||||
dotnet_diagnostic.CA2002.severity = suggestion
|
||||
dotnet_diagnostic.CA2007.severity = suggestion
|
||||
|
||||
2
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
2
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
@@ -1,5 +1,5 @@
|
||||
name: Bug Report
|
||||
description: 'Support Requests will be closed immediately, if you are not 100% certain this is a bug please go to our Reddit, Discord, Forums, or IRC first. Only bug reports for v4 will be accepted, older versions are EOL & unsupported.'
|
||||
description: 'Only bug reports for v4 will be accepted, older versions are no longer receiving bug fixes and support issues will be closed immediately.'
|
||||
labels: ['needs-triage']
|
||||
body:
|
||||
- type: checkboxes
|
||||
|
||||
15
.github/PULL_REQUEST_TEMPLATE.md
vendored
15
.github/PULL_REQUEST_TEMPLATE.md
vendored
@@ -1,14 +1,15 @@
|
||||
#### Database Migration
|
||||
YES | NO
|
||||
|
||||
#### Description
|
||||
A few sentences describing the overall goals of the pull request's commits.
|
||||
|
||||
#### Todos
|
||||
- [ ] Tests
|
||||
- [ ] Wiki Updates
|
||||
<!-- Remove any of the following sections if they are not used -->
|
||||
|
||||
#### Screenshots for UI Changes
|
||||
|
||||
|
||||
#### Database Migration
|
||||
YES - ###
|
||||
|
||||
|
||||
#### Issues Fixed or Closed by this PR
|
||||
* Closes #
|
||||
|
||||
*
|
||||
|
||||
16
.github/actions/test/action.yml
vendored
16
.github/actions/test/action.yml
vendored
@@ -77,12 +77,20 @@ runs:
|
||||
|
||||
- name: Run tests
|
||||
shell: bash
|
||||
run: dotnet test ./_tests/Sonarr.*.Test.dll --filter "${{ inputs.filter }}" --logger trx --results-directory "${{ env.RESULTS_NAME }}"
|
||||
run: dotnet test ./_tests/Sonarr.*.Test.dll --filter "${{ inputs.filter }}" --logger "trx;LogFileName=${{ env.RESULTS_NAME }}.trx"
|
||||
|
||||
- name: Upload Test Results
|
||||
if: ${{ !cancelled() }}
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: results-${{ env.RESULTS_NAME }}
|
||||
path: TestResults/*.trx
|
||||
|
||||
- name: Publish Test Results
|
||||
if: ${{ !cancelled() }}
|
||||
uses: phoenix-actions/test-reporting@v12
|
||||
with:
|
||||
name: ${{ env.RESULTS_NAME }}
|
||||
path: ${{ env.RESULTS_NAME }}/*.trx
|
||||
name: Test Results
|
||||
output-to: step-summary
|
||||
path: '*.trx'
|
||||
reporter: dotnet-trx
|
||||
working-directory: TestResults
|
||||
|
||||
6
.github/workflows/api_docs.yml
vendored
6
.github/workflows/api_docs.yml
vendored
@@ -31,12 +31,6 @@ jobs:
|
||||
- name: Setup dotnet
|
||||
uses: actions/setup-dotnet@v3
|
||||
id: setup-dotnet
|
||||
with:
|
||||
dotnet-version: '6.0.x'
|
||||
|
||||
- name: Create temporary global.json
|
||||
run: |
|
||||
echo '{"sdk":{"version": "${{ steps.setup-dotnet.outputs.dotnet-version }}" } }' > ./global.json
|
||||
|
||||
- name: Create openapi.json
|
||||
run: ./docs.sh Linux
|
||||
|
||||
4
.github/workflows/build.yml
vendored
4
.github/workflows/build.yml
vendored
@@ -17,7 +17,7 @@ env:
|
||||
FRAMEWORK: net6.0
|
||||
BRANCH: ${{ github.head_ref || github.ref_name }}
|
||||
SONARR_MAJOR_VERSION: 4
|
||||
VERSION: 4.0.0
|
||||
VERSION: 4.0.1
|
||||
|
||||
jobs:
|
||||
backend:
|
||||
@@ -188,7 +188,7 @@ jobs:
|
||||
binary_path: osx-x64/${{ needs.backend.outputs.framework }}/Sonarr
|
||||
- os: windows-latest
|
||||
artifact: tests-win-x64
|
||||
filter: TestCategory!=ManualTest&TestCategory=WINDOWS&TestCategory=IntegrationTest
|
||||
filter: TestCategory!=ManualTest&TestCategory!=LINUX&TestCategory=IntegrationTest
|
||||
binary_artifact: build_windows
|
||||
binary_path: win-x64/${{ needs.backend.outputs.framework }}/Sonarr
|
||||
runs-on: ${{ matrix.os }}
|
||||
|
||||
41
.github/workflows/publish-test-results.yml
vendored
Normal file
41
.github/workflows/publish-test-results.yml
vendored
Normal file
@@ -0,0 +1,41 @@
|
||||
name: Publish Test Results
|
||||
|
||||
on:
|
||||
workflow_run:
|
||||
workflows: ['Build']
|
||||
types:
|
||||
- completed
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
actions: read
|
||||
checks: write
|
||||
|
||||
jobs:
|
||||
report:
|
||||
if: ${{ github.event.workflow_run.conclusion != 'skipped' && github.event.workflow_run.conclusion != 'cancelled' }}
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Check out
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Download Test Reports
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
path: test-results
|
||||
pattern: results-*
|
||||
merge-multiple: true
|
||||
repository: ${{ github.event.repository.owner.login }}/${{ github.event.repository.name }}
|
||||
run-id: ${{ github.event.workflow_run.id }}
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Publish Test Results
|
||||
uses: phoenix-actions/test-reporting@v12
|
||||
with:
|
||||
list-suites: failed
|
||||
list-tests: failed
|
||||
name: Test Results
|
||||
only-summary: true
|
||||
path: '*.trx'
|
||||
reporter: dotnet-trx
|
||||
working-directory: test-results
|
||||
@@ -98,7 +98,7 @@ echo "Directories created"
|
||||
echo ""
|
||||
echo "Installing pre-requisite Packages"
|
||||
# shellcheck disable=SC2086
|
||||
apt update && apt install $app_prereq
|
||||
apt update && apt install -y $app_prereq
|
||||
echo ""
|
||||
ARCH=$(dpkg --print-architecture)
|
||||
# get arch
|
||||
|
||||
@@ -6,7 +6,7 @@ import { icons, kinds } from 'Helpers/Props';
|
||||
import translate from 'Utilities/String/translate';
|
||||
import styles from './HistoryEventTypeCell.css';
|
||||
|
||||
function getIconName(eventType) {
|
||||
function getIconName(eventType, data) {
|
||||
switch (eventType) {
|
||||
case 'grabbed':
|
||||
return icons.DOWNLOADING;
|
||||
@@ -17,7 +17,7 @@ function getIconName(eventType) {
|
||||
case 'downloadFailed':
|
||||
return icons.DOWNLOADING;
|
||||
case 'episodeFileDeleted':
|
||||
return icons.DELETE;
|
||||
return data.reason === 'MissingFromDisk' ? icons.FILE_MISSING : icons.DELETE;
|
||||
case 'episodeFileRenamed':
|
||||
return icons.ORGANIZE;
|
||||
case 'downloadIgnored':
|
||||
@@ -47,7 +47,7 @@ function getTooltip(eventType, data) {
|
||||
case 'downloadFailed':
|
||||
return translate('DownloadFailedEpisodeTooltip');
|
||||
case 'episodeFileDeleted':
|
||||
return translate('EpisodeFileDeletedTooltip');
|
||||
return data.reason === 'MissingFromDisk' ? translate('EpisodeFileMissingTooltip') : translate('EpisodeFileDeletedTooltip');
|
||||
case 'episodeFileRenamed':
|
||||
return translate('EpisodeFileRenamedTooltip');
|
||||
case 'downloadIgnored':
|
||||
@@ -58,7 +58,7 @@ function getTooltip(eventType, data) {
|
||||
}
|
||||
|
||||
function HistoryEventTypeCell({ eventType, data }) {
|
||||
const iconName = getIconName(eventType);
|
||||
const iconName = getIconName(eventType, data);
|
||||
const iconKind = getIconKind(eventType);
|
||||
const tooltip = getTooltip(eventType, data);
|
||||
|
||||
|
||||
@@ -100,6 +100,7 @@ class QueueRow extends Component {
|
||||
outputPath,
|
||||
downloadClient,
|
||||
estimatedCompletionTime,
|
||||
added,
|
||||
timeleft,
|
||||
size,
|
||||
sizeleft,
|
||||
@@ -362,6 +363,15 @@ class QueueRow extends Component {
|
||||
);
|
||||
}
|
||||
|
||||
if (name === 'added') {
|
||||
return (
|
||||
<RelativeDateCellConnector
|
||||
key={name}
|
||||
date={added}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
if (name === 'actions') {
|
||||
return (
|
||||
<TableRowCell
|
||||
@@ -441,6 +451,7 @@ QueueRow.propTypes = {
|
||||
outputPath: PropTypes.string,
|
||||
downloadClient: PropTypes.string,
|
||||
estimatedCompletionTime: PropTypes.string,
|
||||
added: PropTypes.string,
|
||||
timeleft: PropTypes.string,
|
||||
size: PropTypes.number,
|
||||
sizeleft: PropTypes.number,
|
||||
|
||||
@@ -44,7 +44,16 @@ export interface CustomFilter {
|
||||
filers: PropertyFilter[];
|
||||
}
|
||||
|
||||
export interface AppSectionState {
|
||||
dimensions: {
|
||||
isSmallScreen: boolean;
|
||||
width: number;
|
||||
height: number;
|
||||
};
|
||||
}
|
||||
|
||||
interface AppState {
|
||||
app: AppSectionState;
|
||||
calendar: CalendarAppState;
|
||||
commands: CommandAppState;
|
||||
episodeFiles: EpisodeFilesAppState;
|
||||
|
||||
@@ -52,6 +52,10 @@ $fullColorGradient: rgba(244, 245, 246, 0.2);
|
||||
.statusContainer {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
&:global(.fullColor) {
|
||||
filter: var(--calendarFullColorFilter)
|
||||
}
|
||||
}
|
||||
|
||||
.statusIcon {
|
||||
|
||||
@@ -102,7 +102,12 @@ class CalendarEvent extends Component {
|
||||
{series.title}
|
||||
</div>
|
||||
|
||||
<div className={styles.statusContainer}>
|
||||
<div
|
||||
className={classNames(
|
||||
styles.statusContainer,
|
||||
fullColorEvents && 'fullColor'
|
||||
)}
|
||||
>
|
||||
{
|
||||
missingAbsoluteNumber ?
|
||||
<Icon
|
||||
@@ -128,6 +133,7 @@ class CalendarEvent extends Component {
|
||||
<span className={styles.statusIcon}>
|
||||
<CalendarEventQueueDetails
|
||||
{...queueItem}
|
||||
fullColorEvents={fullColorEvents}
|
||||
/>
|
||||
</span> :
|
||||
null
|
||||
@@ -150,7 +156,7 @@ class CalendarEvent extends Component {
|
||||
<Icon
|
||||
className={styles.statusIcon}
|
||||
name={icons.EPISODE_FILE}
|
||||
kind={fullColorEvents ? kinds.DEFAULT : kinds.WARNING}
|
||||
kind={kinds.WARNING}
|
||||
title={translate('QualityCutoffNotMet')}
|
||||
/> :
|
||||
null
|
||||
@@ -160,9 +166,8 @@ class CalendarEvent extends Component {
|
||||
episodeNumber === 1 && seasonNumber > 0 ?
|
||||
<Icon
|
||||
className={styles.statusIcon}
|
||||
name={icons.INFO}
|
||||
name={icons.PREMIERE}
|
||||
kind={kinds.INFO}
|
||||
darken={fullColorEvents}
|
||||
title={seasonNumber === 1 ? translate('SeriesPremiere') : translate('SeasonPremiere')}
|
||||
/> :
|
||||
null
|
||||
@@ -173,8 +178,8 @@ class CalendarEvent extends Component {
|
||||
finaleType ?
|
||||
<Icon
|
||||
className={styles.statusIcon}
|
||||
name={icons.INFO}
|
||||
kind={fullColorEvents ? kinds.DEFAULT : kinds.WARNING}
|
||||
name={finaleType === 'series' ? icons.FINALE_SERIES : icons.FINALE_SEASON}
|
||||
kind={finaleType === 'series' ? kinds.DANGER : kinds.WARNING}
|
||||
title={getFinaleTypeName(finaleType)}
|
||||
/> :
|
||||
null
|
||||
@@ -187,7 +192,6 @@ class CalendarEvent extends Component {
|
||||
className={styles.statusIcon}
|
||||
name={icons.INFO}
|
||||
kind={kinds.PINK}
|
||||
darken={fullColorEvents}
|
||||
title={translate('Special')}
|
||||
/> :
|
||||
null
|
||||
|
||||
@@ -50,6 +50,15 @@
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
.statusContainer {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
&:global(.fullColor) {
|
||||
filter: var(--calendarFullColorFilter)
|
||||
}
|
||||
}
|
||||
|
||||
.statusIcon {
|
||||
margin-left: 3px;
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@ interface CssExports {
|
||||
'onAir': string;
|
||||
'premiere': string;
|
||||
'seriesTitle': string;
|
||||
'statusContainer': string;
|
||||
'statusIcon': string;
|
||||
'unaired': string;
|
||||
'unmonitored': string;
|
||||
|
||||
@@ -145,45 +145,51 @@ class CalendarEventGroup extends Component {
|
||||
{series.title}
|
||||
</div>
|
||||
|
||||
{
|
||||
isMissingAbsoluteNumber &&
|
||||
<Icon
|
||||
containerClassName={styles.statusIcon}
|
||||
name={icons.WARNING}
|
||||
title={translate('EpisodeMissingAbsoluteNumber')}
|
||||
/>
|
||||
}
|
||||
<div
|
||||
className={classNames(
|
||||
styles.statusContainer,
|
||||
fullColorEvents && 'fullColor'
|
||||
)}
|
||||
>
|
||||
{
|
||||
isMissingAbsoluteNumber &&
|
||||
<Icon
|
||||
containerClassName={styles.statusIcon}
|
||||
name={icons.WARNING}
|
||||
title={translate('EpisodeMissingAbsoluteNumber')}
|
||||
/>
|
||||
}
|
||||
|
||||
{
|
||||
anyDownloading &&
|
||||
<Icon
|
||||
containerClassName={styles.statusIcon}
|
||||
name={icons.DOWNLOADING}
|
||||
title={translate('AnEpisodeIsDownloading')}
|
||||
/>
|
||||
}
|
||||
{
|
||||
anyDownloading &&
|
||||
<Icon
|
||||
containerClassName={styles.statusIcon}
|
||||
name={icons.DOWNLOADING}
|
||||
title={translate('AnEpisodeIsDownloading')}
|
||||
/>
|
||||
}
|
||||
|
||||
{
|
||||
firstEpisode.episodeNumber === 1 && seasonNumber > 0 &&
|
||||
<Icon
|
||||
containerClassName={styles.statusIcon}
|
||||
name={icons.INFO}
|
||||
kind={kinds.INFO}
|
||||
darken={fullColorEvents}
|
||||
title={seasonNumber === 1 ? translate('SeriesPremiere') : translate('SeasonPremiere')}
|
||||
/>
|
||||
}
|
||||
{
|
||||
firstEpisode.episodeNumber === 1 && seasonNumber > 0 &&
|
||||
<Icon
|
||||
containerClassName={styles.statusIcon}
|
||||
name={icons.PREMIERE}
|
||||
kind={kinds.INFO}
|
||||
title={seasonNumber === 1 ? translate('SeriesPremiere') : translate('SeasonPremiere')}
|
||||
/>
|
||||
}
|
||||
|
||||
{
|
||||
showFinaleIcon &&
|
||||
lastEpisode.finaleType ?
|
||||
<Icon
|
||||
containerClassName={styles.statusIcon}
|
||||
name={icons.INFO}
|
||||
kind={fullColorEvents ? kinds.DEFAULT : kinds.WARNING}
|
||||
title={getFinaleTypeName(lastEpisode.finaleType)}
|
||||
/> : null
|
||||
}
|
||||
{
|
||||
showFinaleIcon &&
|
||||
lastEpisode.finaleType ?
|
||||
<Icon
|
||||
containerClassName={styles.statusIcon}
|
||||
name={lastEpisode.finaleType === 'series' ? icons.FINALE_SERIES : icons.FINALE_SEASON}
|
||||
kind={lastEpisode.finaleType === 'series' ? kinds.DANGER : kinds.WARNING}
|
||||
title={getFinaleTypeName(lastEpisode.finaleType)}
|
||||
/> : null
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className={styles.airingInfo}>
|
||||
|
||||
@@ -22,9 +22,20 @@ function Legend(props) {
|
||||
if (showFinaleIcon) {
|
||||
iconsToShow.push(
|
||||
<LegendIconItem
|
||||
name="Finale"
|
||||
icon={icons.INFO}
|
||||
kind={fullColorEvents ? kinds.DEFAULT : kinds.WARNING}
|
||||
name={translate('SeasonFinale')}
|
||||
icon={icons.FINALE_SEASON}
|
||||
kind={kinds.WARNING}
|
||||
fullColorEvents={fullColorEvents}
|
||||
tooltip={translate('CalendarLegendSeriesFinaleTooltip')}
|
||||
/>
|
||||
);
|
||||
|
||||
iconsToShow.push(
|
||||
<LegendIconItem
|
||||
name={translate('SeriesFinale')}
|
||||
icon={icons.FINALE_SERIES}
|
||||
kind={kinds.DANGER}
|
||||
fullColorEvents={fullColorEvents}
|
||||
tooltip={translate('CalendarLegendSeriesFinaleTooltip')}
|
||||
/>
|
||||
);
|
||||
@@ -33,10 +44,10 @@ function Legend(props) {
|
||||
if (showSpecialIcon) {
|
||||
iconsToShow.push(
|
||||
<LegendIconItem
|
||||
name="Special"
|
||||
name={translate('Special')}
|
||||
icon={icons.INFO}
|
||||
kind={kinds.PINK}
|
||||
darken={fullColorEvents}
|
||||
fullColorEvents={fullColorEvents}
|
||||
tooltip={translate('SpecialEpisode')}
|
||||
/>
|
||||
);
|
||||
@@ -45,9 +56,10 @@ function Legend(props) {
|
||||
if (showCutoffUnmetIcon) {
|
||||
iconsToShow.push(
|
||||
<LegendIconItem
|
||||
name="Cutoff Not Met"
|
||||
name={translate('Cutoff Not Met')}
|
||||
icon={icons.EPISODE_FILE}
|
||||
kind={fullColorEvents ? kinds.DEFAULT : kinds.WARNING}
|
||||
kind={kinds.WARNING}
|
||||
fullColorEvents={fullColorEvents}
|
||||
tooltip={translate('QualityCutoffNotMet')}
|
||||
/>
|
||||
);
|
||||
@@ -112,10 +124,10 @@ function Legend(props) {
|
||||
|
||||
<div>
|
||||
<LegendIconItem
|
||||
name="Premiere"
|
||||
icon={icons.INFO}
|
||||
name={translate('Premiere')}
|
||||
icon={icons.PREMIERE}
|
||||
kind={kinds.INFO}
|
||||
darken={true}
|
||||
fullColorEvents={fullColorEvents}
|
||||
tooltip={translate('CalendarLegendSeriesPremiereTooltip')}
|
||||
/>
|
||||
|
||||
@@ -129,6 +141,12 @@ function Legend(props) {
|
||||
{iconsToShow[2]}
|
||||
</div>
|
||||
}
|
||||
{
|
||||
iconsToShow.length > 3 &&
|
||||
<div>
|
||||
{iconsToShow[3]}
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -7,4 +7,8 @@
|
||||
|
||||
.icon {
|
||||
margin-right: 5px;
|
||||
|
||||
&:global(.fullColorEvents) {
|
||||
filter: var(--calendarFullColorFilter)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import classNames from 'classnames';
|
||||
import PropTypes from 'prop-types';
|
||||
import React from 'react';
|
||||
import Icon from 'Components/Icon';
|
||||
@@ -6,9 +7,9 @@ import styles from './LegendIconItem.css';
|
||||
function LegendIconItem(props) {
|
||||
const {
|
||||
name,
|
||||
fullColorEvents,
|
||||
icon,
|
||||
kind,
|
||||
darken,
|
||||
tooltip
|
||||
} = props;
|
||||
|
||||
@@ -18,9 +19,11 @@ function LegendIconItem(props) {
|
||||
title={tooltip}
|
||||
>
|
||||
<Icon
|
||||
className={styles.icon}
|
||||
className={classNames(
|
||||
styles.icon,
|
||||
fullColorEvents && 'fullColorEvents'
|
||||
)}
|
||||
name={icon}
|
||||
darken={darken}
|
||||
kind={kind}
|
||||
/>
|
||||
|
||||
@@ -31,14 +34,10 @@ function LegendIconItem(props) {
|
||||
|
||||
LegendIconItem.propTypes = {
|
||||
name: PropTypes.string.isRequired,
|
||||
fullColorEvents: PropTypes.bool.isRequired,
|
||||
icon: PropTypes.object.isRequired,
|
||||
kind: PropTypes.string.isRequired,
|
||||
darken: PropTypes.bool.isRequired,
|
||||
tooltip: PropTypes.string.isRequired
|
||||
};
|
||||
|
||||
LegendIconItem.defaultProps = {
|
||||
darken: false
|
||||
};
|
||||
|
||||
export default LegendIconItem;
|
||||
|
||||
@@ -30,22 +30,24 @@ function CustomFiltersModalContent(props) {
|
||||
|
||||
<ModalBody>
|
||||
{
|
||||
customFilters.map((customFilter) => {
|
||||
return (
|
||||
<CustomFilter
|
||||
key={customFilter.id}
|
||||
id={customFilter.id}
|
||||
label={customFilter.label}
|
||||
filters={customFilter.filters}
|
||||
selectedFilterKey={selectedFilterKey}
|
||||
isDeleting={isDeleting}
|
||||
deleteError={deleteError}
|
||||
dispatchSetFilter={dispatchSetFilter}
|
||||
dispatchDeleteCustomFilter={dispatchDeleteCustomFilter}
|
||||
onEditPress={onEditCustomFilter}
|
||||
/>
|
||||
);
|
||||
})
|
||||
customFilters
|
||||
.sort((a, b) => a.label.localeCompare(b.label))
|
||||
.map((customFilter) => {
|
||||
return (
|
||||
<CustomFilter
|
||||
key={customFilter.id}
|
||||
id={customFilter.id}
|
||||
label={customFilter.label}
|
||||
filters={customFilter.filters}
|
||||
selectedFilterKey={selectedFilterKey}
|
||||
isDeleting={isDeleting}
|
||||
deleteError={deleteError}
|
||||
dispatchSetFilter={dispatchSetFilter}
|
||||
dispatchDeleteCustomFilter={dispatchDeleteCustomFilter}
|
||||
onEditPress={onEditCustomFilter}
|
||||
/>
|
||||
);
|
||||
})
|
||||
}
|
||||
|
||||
<div className={styles.addButtonContainer}>
|
||||
|
||||
@@ -12,18 +12,10 @@
|
||||
|
||||
.info {
|
||||
color: var(--infoColor);
|
||||
|
||||
&:global(.darken) {
|
||||
color: color(var(--infoColor) shade(30%));
|
||||
}
|
||||
}
|
||||
|
||||
.pink {
|
||||
color: var(--pink);
|
||||
|
||||
&:global(.darken) {
|
||||
color: color(var(--pink) shade(30%));
|
||||
}
|
||||
}
|
||||
|
||||
.success {
|
||||
|
||||
@@ -18,7 +18,6 @@ class Icon extends PureComponent {
|
||||
kind,
|
||||
size,
|
||||
title,
|
||||
darken,
|
||||
isSpinning,
|
||||
...otherProps
|
||||
} = this.props;
|
||||
@@ -27,8 +26,7 @@ class Icon extends PureComponent {
|
||||
<FontAwesomeIcon
|
||||
className={classNames(
|
||||
className,
|
||||
styles[kind],
|
||||
darken && 'darken'
|
||||
styles[kind]
|
||||
)}
|
||||
icon={name}
|
||||
spin={isSpinning}
|
||||
@@ -61,7 +59,6 @@ Icon.propTypes = {
|
||||
kind: PropTypes.string.isRequired,
|
||||
size: PropTypes.number.isRequired,
|
||||
title: PropTypes.oneOfType([PropTypes.string, PropTypes.func]),
|
||||
darken: PropTypes.bool.isRequired,
|
||||
isSpinning: PropTypes.bool.isRequired,
|
||||
fixedWidth: PropTypes.bool.isRequired
|
||||
};
|
||||
@@ -69,7 +66,6 @@ Icon.propTypes = {
|
||||
Icon.defaultProps = {
|
||||
kind: kinds.DEFAULT,
|
||||
size: 14,
|
||||
darken: false,
|
||||
isSpinning: false,
|
||||
fixedWidth: false
|
||||
};
|
||||
|
||||
@@ -40,18 +40,26 @@ class FilterMenuContent extends Component {
|
||||
}
|
||||
|
||||
{
|
||||
customFilters.map((filter) => {
|
||||
return (
|
||||
<FilterMenuItem
|
||||
key={filter.id}
|
||||
filterKey={filter.id}
|
||||
selectedFilterKey={selectedFilterKey}
|
||||
onPress={onFilterSelect}
|
||||
>
|
||||
{filter.label}
|
||||
</FilterMenuItem>
|
||||
);
|
||||
})
|
||||
customFilters.length > 0 ?
|
||||
<MenuItemSeparator /> :
|
||||
null
|
||||
}
|
||||
|
||||
{
|
||||
customFilters
|
||||
.sort((a, b) => a.label.localeCompare(b.label))
|
||||
.map((filter) => {
|
||||
return (
|
||||
<FilterMenuItem
|
||||
key={filter.id}
|
||||
filterKey={filter.id}
|
||||
selectedFilterKey={selectedFilterKey}
|
||||
onPress={onFilterSelect}
|
||||
>
|
||||
{filter.label}
|
||||
</FilterMenuItem>
|
||||
);
|
||||
})
|
||||
}
|
||||
|
||||
{
|
||||
|
||||
@@ -41,6 +41,9 @@ import {
|
||||
faChevronCircleUp as fasChevronCircleUp,
|
||||
faCircle as fasCircle,
|
||||
faCircleDown as fasCircleDown,
|
||||
faCirclePause as fasCirclePause,
|
||||
faCirclePlay as fasCirclePlay,
|
||||
faCircleStop as fasCircleStop,
|
||||
faCloud as fasCloud,
|
||||
faCloudDownloadAlt as fasCloudDownloadAlt,
|
||||
faCog as fasCog,
|
||||
@@ -55,6 +58,7 @@ import {
|
||||
faEye as fasEye,
|
||||
faFastBackward as fasFastBackward,
|
||||
faFastForward as fasFastForward,
|
||||
faFileCircleQuestion as fasFileCircleQuestion,
|
||||
faFileExport as fasFileExport,
|
||||
faFileInvoice as farFileInvoice,
|
||||
faFilter as fasFilter,
|
||||
@@ -146,7 +150,10 @@ export const EXPORT = fasFileExport;
|
||||
export const EXTERNAL_LINK = fasExternalLinkAlt;
|
||||
export const FATAL = fasTimesCircle;
|
||||
export const FILE = farFile;
|
||||
export const FILE_MISSING = fasFileCircleQuestion;
|
||||
export const FILTER = fasFilter;
|
||||
export const FINALE_SEASON = fasCirclePause;
|
||||
export const FINALE_SERIES = fasCircleStop;
|
||||
export const FOOTNOTE = fasAsterisk;
|
||||
export const FOLDER = farFolder;
|
||||
export const FOLDER_OPEN = fasFolderOpen;
|
||||
@@ -178,6 +185,7 @@ export const PARENT = fasLevelUpAlt;
|
||||
export const PARSE = fasCalculator;
|
||||
export const PAUSED = fasPause;
|
||||
export const PENDING = farClock;
|
||||
export const PREMIERE = fasCirclePlay;
|
||||
export const PROFILE = fasUser;
|
||||
export const POSTER = fasTh;
|
||||
export const QUEUED = fasCloud;
|
||||
|
||||
@@ -269,33 +269,6 @@ function InteractiveImportModalContent(
|
||||
const [interactiveImportErrorMessage, setInteractiveImportErrorMessage] =
|
||||
useState<string | null>(null);
|
||||
const [selectState, setSelectState] = useSelectState();
|
||||
const [bulkSelectOptions, setBulkSelectOptions] = useState([
|
||||
{
|
||||
key: 'select',
|
||||
value: translate('SelectDropdown'),
|
||||
disabled: true,
|
||||
},
|
||||
{
|
||||
key: 'season',
|
||||
value: translate('SelectSeason'),
|
||||
},
|
||||
{
|
||||
key: 'episode',
|
||||
value: translate('SelectEpisodes'),
|
||||
},
|
||||
{
|
||||
key: 'quality',
|
||||
value: translate('SelectQuality'),
|
||||
},
|
||||
{
|
||||
key: 'releaseGroup',
|
||||
value: translate('SelectReleaseGroup'),
|
||||
},
|
||||
{
|
||||
key: 'language',
|
||||
value: translate('SelectLanguage'),
|
||||
},
|
||||
]);
|
||||
const { allSelected, allUnselected, selectedState } = selectState;
|
||||
const previousIsDeleting = usePrevious(isDeleting);
|
||||
const dispatch = useDispatch();
|
||||
@@ -318,19 +291,66 @@ function InteractiveImportModalContent(
|
||||
return getSelectedIds(selectedState);
|
||||
}, [selectedState]);
|
||||
|
||||
const bulkSelectOptions = useMemo(() => {
|
||||
const { seasonSelectDisabled, episodeSelectDisabled } = items.reduce(
|
||||
(acc, item) => {
|
||||
if (!selectedIds.includes(item.id)) {
|
||||
return acc;
|
||||
}
|
||||
|
||||
acc.seasonSelectDisabled ||= !item.series;
|
||||
acc.episodeSelectDisabled ||= !item.seasonNumber;
|
||||
|
||||
return acc;
|
||||
},
|
||||
{
|
||||
seasonSelectDisabled: false,
|
||||
episodeSelectDisabled: false,
|
||||
}
|
||||
);
|
||||
|
||||
const options = [
|
||||
{
|
||||
key: 'select',
|
||||
value: translate('SelectDropdown'),
|
||||
disabled: true,
|
||||
},
|
||||
{
|
||||
key: 'season',
|
||||
value: translate('SelectSeason'),
|
||||
disabled: seasonSelectDisabled,
|
||||
},
|
||||
{
|
||||
key: 'episode',
|
||||
value: translate('SelectEpisodes'),
|
||||
disabled: episodeSelectDisabled,
|
||||
},
|
||||
{
|
||||
key: 'quality',
|
||||
value: translate('SelectQuality'),
|
||||
},
|
||||
{
|
||||
key: 'releaseGroup',
|
||||
value: translate('SelectReleaseGroup'),
|
||||
},
|
||||
{
|
||||
key: 'language',
|
||||
value: translate('SelectLanguage'),
|
||||
},
|
||||
];
|
||||
|
||||
if (allowSeriesChange) {
|
||||
options.splice(1, 0, {
|
||||
key: 'series',
|
||||
value: translate('SelectSeries'),
|
||||
});
|
||||
}
|
||||
|
||||
return options;
|
||||
}, [allowSeriesChange, items, selectedIds]);
|
||||
|
||||
useEffect(
|
||||
() => {
|
||||
if (allowSeriesChange) {
|
||||
const newBulkSelectOptions = [...bulkSelectOptions];
|
||||
|
||||
newBulkSelectOptions.splice(1, 0, {
|
||||
key: 'series',
|
||||
value: translate('SelectSeries'),
|
||||
});
|
||||
|
||||
setBulkSelectOptions(newBulkSelectOptions);
|
||||
}
|
||||
|
||||
if (initialSortKey) {
|
||||
const sortProps: { sortKey: string; sortDirection?: string } = {
|
||||
sortKey: initialSortKey,
|
||||
|
||||
@@ -202,13 +202,18 @@ export default function SeriesIndexPosters(props: SeriesIndexPostersProps) {
|
||||
if (current) {
|
||||
const width = current.clientWidth;
|
||||
const padding = bodyPadding - 5;
|
||||
const finalWidth = width - padding * 2;
|
||||
|
||||
if (Math.abs(size.width - finalWidth) < 20 || size.width === finalWidth) {
|
||||
return;
|
||||
}
|
||||
|
||||
setSize({
|
||||
width: width - padding * 2,
|
||||
width: finalWidth,
|
||||
height: window.innerHeight,
|
||||
});
|
||||
}
|
||||
}, [isSmallScreen, scrollerRef, bounds]);
|
||||
}, [isSmallScreen, size, scrollerRef, bounds]);
|
||||
|
||||
useEffect(() => {
|
||||
const currentScrollerRef = scrollerRef.current as HTMLElement;
|
||||
|
||||
@@ -133,7 +133,7 @@ class EditDownloadClientModalContent extends Component {
|
||||
<FormInputGroup
|
||||
type={inputTypes.NUMBER}
|
||||
name="priority"
|
||||
helpText={translate('PriorityHelpText')}
|
||||
helpText={translate('DownloadClientPriorityHelpText')}
|
||||
min={1}
|
||||
max={50}
|
||||
{...priority}
|
||||
|
||||
@@ -149,7 +149,13 @@ export default {
|
||||
delete selectedSchema.name;
|
||||
|
||||
selectedSchema.fields = selectedSchema.fields.map((field) => {
|
||||
return { ...field };
|
||||
const newField = { ...field };
|
||||
|
||||
if (newField.privacy === 'apiKey' || newField.privacy === 'password') {
|
||||
newField.value = '';
|
||||
}
|
||||
|
||||
return newField;
|
||||
});
|
||||
|
||||
newState.selectedSchema = selectedSchema;
|
||||
|
||||
@@ -158,6 +158,12 @@ export const defaultState = {
|
||||
isSortable: true,
|
||||
isVisible: true
|
||||
},
|
||||
{
|
||||
name: 'added',
|
||||
label: () => translate('Added'),
|
||||
isSortable: true,
|
||||
isVisible: false
|
||||
},
|
||||
{
|
||||
name: 'progress',
|
||||
label: () => translate('Progress'),
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
import { createSelector } from 'reselect';
|
||||
import AppState from 'App/State/AppState';
|
||||
|
||||
function createDimensionsSelector() {
|
||||
return createSelector(
|
||||
(state) => state.app.dimensions,
|
||||
(state: AppState) => state.app.dimensions,
|
||||
(dimensions) => {
|
||||
return dimensions;
|
||||
}
|
||||
@@ -213,6 +213,8 @@ module.exports = {
|
||||
calendarTextDim: '#eee',
|
||||
calendarTextDimAlternate: '#fff',
|
||||
|
||||
calendarFullColorFilter: 'grayscale(90%) contrast(200%) saturate(50%)',
|
||||
|
||||
//
|
||||
// Table
|
||||
|
||||
|
||||
@@ -215,6 +215,8 @@ module.exports = {
|
||||
calendarTextDim: '#666',
|
||||
calendarTextDimAlternate: '#242424',
|
||||
|
||||
calendarFullColorFilter: 'brightness(30%)',
|
||||
|
||||
//
|
||||
// Table
|
||||
|
||||
|
||||
@@ -28,6 +28,7 @@ interface Queue extends ModelBase {
|
||||
sizeleft: number;
|
||||
timeleft: string;
|
||||
estimatedCompletionTime: string;
|
||||
added?: string;
|
||||
status: string;
|
||||
trackedDownloadStatus: QueueTrackedDownloadStatus;
|
||||
trackedDownloadState: QueueTrackedDownloadState;
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
export interface UiSettings {
|
||||
theme: string;
|
||||
showRelativeDates: boolean;
|
||||
shortDateFormat: string;
|
||||
longDateFormat: string;
|
||||
|
||||
@@ -131,6 +131,16 @@ namespace NzbDrone.Common.Test.Http
|
||||
response.Content.Should().NotBeNullOrWhiteSpace();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_throw_timeout_request()
|
||||
{
|
||||
var request = new HttpRequest($"https://{_httpBinHost}/delay/10");
|
||||
|
||||
request.RequestTimeout = new TimeSpan(0, 0, 5);
|
||||
|
||||
Assert.ThrowsAsync<WebException>(async () => await Subject.ExecuteAsync(request));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task should_execute_https_get()
|
||||
{
|
||||
|
||||
@@ -102,31 +102,38 @@ namespace NzbDrone.Common.Http.Dispatchers
|
||||
|
||||
var httpClient = GetClient(request.Url);
|
||||
|
||||
using var responseMessage = await httpClient.SendAsync(requestMessage, HttpCompletionOption.ResponseHeadersRead, cts.Token);
|
||||
try
|
||||
{
|
||||
byte[] data = null;
|
||||
|
||||
try
|
||||
using var responseMessage = await httpClient.SendAsync(requestMessage, HttpCompletionOption.ResponseHeadersRead, cts.Token);
|
||||
{
|
||||
if (request.ResponseStream != null && responseMessage.StatusCode == HttpStatusCode.OK)
|
||||
byte[] data = null;
|
||||
|
||||
try
|
||||
{
|
||||
await responseMessage.Content.CopyToAsync(request.ResponseStream, null, cts.Token);
|
||||
if (request.ResponseStream != null && responseMessage.StatusCode == HttpStatusCode.OK)
|
||||
{
|
||||
await responseMessage.Content.CopyToAsync(request.ResponseStream, null, cts.Token);
|
||||
}
|
||||
else
|
||||
{
|
||||
data = await responseMessage.Content.ReadAsByteArrayAsync(cts.Token);
|
||||
}
|
||||
}
|
||||
else
|
||||
catch (Exception ex)
|
||||
{
|
||||
data = await responseMessage.Content.ReadAsByteArrayAsync(cts.Token);
|
||||
throw new WebException("Failed to read complete http response", ex, WebExceptionStatus.ReceiveFailure, null);
|
||||
}
|
||||
|
||||
var headers = responseMessage.Headers.ToNameValueCollection();
|
||||
|
||||
headers.Add(responseMessage.Content.Headers.ToNameValueCollection());
|
||||
|
||||
return new HttpResponse(request, new HttpHeader(headers), data, responseMessage.StatusCode, responseMessage.Version);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
throw new WebException("Failed to read complete http response", ex, WebExceptionStatus.ReceiveFailure, null);
|
||||
}
|
||||
|
||||
var headers = responseMessage.Headers.ToNameValueCollection();
|
||||
|
||||
headers.Add(responseMessage.Content.Headers.ToNameValueCollection());
|
||||
|
||||
return new HttpResponse(request, new HttpHeader(headers), data, responseMessage.StatusCode, responseMessage.Version);
|
||||
}
|
||||
catch (OperationCanceledException ex) when (cts.IsCancellationRequested)
|
||||
{
|
||||
throw new WebException("Http request timed out", ex.InnerException, WebExceptionStatus.Timeout, null);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -92,6 +92,10 @@ namespace NzbDrone.Common.Http
|
||||
{
|
||||
data = new XElement("base64", Convert.ToBase64String(bytes));
|
||||
}
|
||||
else if (value is Dictionary<string, string> d)
|
||||
{
|
||||
data = new XElement("struct", d.Select(p => new XElement("member", new XElement("name", p.Key), new XElement("value", p.Value))));
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new InvalidOperationException($"Unhandled argument type {value.GetType().Name}");
|
||||
|
||||
@@ -33,7 +33,7 @@ namespace NzbDrone.Core.Test.IndexerTests.FileListTests
|
||||
|
||||
Mocker.GetMock<IHttpClient>()
|
||||
.Setup(o => o.ExecuteAsync(It.Is<HttpRequest>(v => v.Method == HttpMethod.Get)))
|
||||
.Returns<HttpRequest>(r => Task.FromResult(new HttpResponse(r, new HttpHeader(), recentFeed)));
|
||||
.Returns<HttpRequest>(r => Task.FromResult(new HttpResponse(r, new HttpHeader { { "Content-Type", "application/json" } }, recentFeed)));
|
||||
|
||||
var releases = await Subject.FetchRecent();
|
||||
|
||||
|
||||
@@ -23,7 +23,7 @@ namespace NzbDrone.Core.AutoTagging.Specifications
|
||||
public override int Order => 1;
|
||||
public override string ImplementationName => "Genre";
|
||||
|
||||
[FieldDefinition(1, Label = "Genre(s)", Type = FieldType.Tag)]
|
||||
[FieldDefinition(1, Label = "AutoTaggingSpecificationGenre", Type = FieldType.Tag)]
|
||||
public IEnumerable<string> Value { get; set; }
|
||||
|
||||
protected override bool IsSatisfiedByWithoutNegate(Series series)
|
||||
|
||||
@@ -21,7 +21,7 @@ namespace NzbDrone.Core.AutoTagging.Specifications
|
||||
public override int Order => 1;
|
||||
public override string ImplementationName => "Original Language";
|
||||
|
||||
[FieldDefinition(1, Label = "Language", Type = FieldType.Select, SelectOptions = typeof(OriginalLanguageFieldConverter))]
|
||||
[FieldDefinition(1, Label = "AutoTaggingSpecificationOriginalLanguage", Type = FieldType.Select, SelectOptions = typeof(OriginalLanguageFieldConverter))]
|
||||
public int Value { get; set; }
|
||||
|
||||
protected override bool IsSatisfiedByWithoutNegate(Series series)
|
||||
|
||||
@@ -20,7 +20,7 @@ namespace NzbDrone.Core.AutoTagging.Specifications
|
||||
public override int Order => 1;
|
||||
public override string ImplementationName => "Quality Profile";
|
||||
|
||||
[FieldDefinition(1, Label = "Quality Profile", Type = FieldType.QualityProfile)]
|
||||
[FieldDefinition(1, Label = "AutoTaggingSpecificationQualityProfile", Type = FieldType.QualityProfile)]
|
||||
public int Value { get; set; }
|
||||
|
||||
protected override bool IsSatisfiedByWithoutNegate(Series series)
|
||||
|
||||
@@ -22,7 +22,7 @@ namespace NzbDrone.Core.AutoTagging.Specifications
|
||||
public override int Order => 1;
|
||||
public override string ImplementationName => "Root Folder";
|
||||
|
||||
[FieldDefinition(1, Label = "Root Folder", Type = FieldType.RootFolder)]
|
||||
[FieldDefinition(1, Label = "AutoTaggingSpecificationRootFolder", Type = FieldType.RootFolder)]
|
||||
public string Value { get; set; }
|
||||
|
||||
protected override bool IsSatisfiedByWithoutNegate(Series series)
|
||||
|
||||
@@ -20,7 +20,7 @@ namespace NzbDrone.Core.AutoTagging.Specifications
|
||||
public override int Order => 2;
|
||||
public override string ImplementationName => "Series Type";
|
||||
|
||||
[FieldDefinition(1, Label = "Series Type", Type = FieldType.Select, SelectOptions = typeof(SeriesTypes))]
|
||||
[FieldDefinition(1, Label = "AutoTaggingSpecificationSeriesType", Type = FieldType.Select, SelectOptions = typeof(SeriesTypes))]
|
||||
public int Value { get; set; }
|
||||
|
||||
protected override bool IsSatisfiedByWithoutNegate(Series series)
|
||||
|
||||
@@ -16,7 +16,7 @@ namespace NzbDrone.Core.AutoTagging.Specifications
|
||||
public override int Order => 1;
|
||||
public override string ImplementationName => "Status";
|
||||
|
||||
[FieldDefinition(1, Label = "Status", Type = FieldType.Select, SelectOptions = typeof(SeriesStatusType))]
|
||||
[FieldDefinition(1, Label = "AutoTaggingSpecificationStatus", Type = FieldType.Select, SelectOptions = typeof(SeriesStatusType))]
|
||||
public int Status { get; set; }
|
||||
|
||||
protected override bool IsSatisfiedByWithoutNegate(Series series)
|
||||
|
||||
@@ -23,10 +23,10 @@ namespace NzbDrone.Core.AutoTagging.Specifications
|
||||
public override int Order => 1;
|
||||
public override string ImplementationName => "Year";
|
||||
|
||||
[FieldDefinition(1, Label = "Minimum Year", Type = FieldType.Number)]
|
||||
[FieldDefinition(1, Label = "AutoTaggingSpecificationMinimumYear", Type = FieldType.Number)]
|
||||
public int Min { get; set; }
|
||||
|
||||
[FieldDefinition(2, Label = "Maximum Year", Type = FieldType.Number)]
|
||||
[FieldDefinition(2, Label = "AutoTaggingSpecificationMaximumYear", Type = FieldType.Number)]
|
||||
public int Max { get; set; }
|
||||
|
||||
protected override bool IsSatisfiedByWithoutNegate(Series series)
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using NLog;
|
||||
using NzbDrone.Common.Extensions;
|
||||
using NzbDrone.Core.Blocklisting;
|
||||
using NzbDrone.Core.History;
|
||||
@@ -23,10 +24,12 @@ namespace NzbDrone.Core.CustomFormats
|
||||
public class CustomFormatCalculationService : ICustomFormatCalculationService
|
||||
{
|
||||
private readonly ICustomFormatService _formatService;
|
||||
private readonly Logger _logger;
|
||||
|
||||
public CustomFormatCalculationService(ICustomFormatService formatService)
|
||||
public CustomFormatCalculationService(ICustomFormatService formatService, Logger logger)
|
||||
{
|
||||
_formatService = formatService;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public List<CustomFormat> ParseCustomFormat(RemoteEpisode remoteEpisode, long size)
|
||||
@@ -153,20 +156,23 @@ namespace NzbDrone.Core.CustomFormats
|
||||
return matches.OrderBy(x => x.Name).ToList();
|
||||
}
|
||||
|
||||
private static List<CustomFormat> ParseCustomFormat(EpisodeFile episodeFile, Series series, List<CustomFormat> allCustomFormats)
|
||||
private List<CustomFormat> ParseCustomFormat(EpisodeFile episodeFile, Series series, List<CustomFormat> allCustomFormats)
|
||||
{
|
||||
var releaseTitle = string.Empty;
|
||||
|
||||
if (episodeFile.SceneName.IsNotNullOrWhiteSpace())
|
||||
{
|
||||
_logger.Trace("Using scene name for release title: {0}", episodeFile.SceneName);
|
||||
releaseTitle = episodeFile.SceneName;
|
||||
}
|
||||
else if (episodeFile.OriginalFilePath.IsNotNullOrWhiteSpace())
|
||||
{
|
||||
_logger.Trace("Using original file path for release title: {0}", Path.GetFileName(episodeFile.OriginalFilePath));
|
||||
releaseTitle = Path.GetFileName(episodeFile.OriginalFilePath);
|
||||
}
|
||||
else if (episodeFile.RelativePath.IsNotNullOrWhiteSpace())
|
||||
{
|
||||
_logger.Trace("Using relative path for release title: {0}", Path.GetFileName(episodeFile.RelativePath));
|
||||
releaseTitle = Path.GetFileName(episodeFile.RelativePath);
|
||||
}
|
||||
|
||||
|
||||
@@ -27,7 +27,7 @@ namespace NzbDrone.Core.CustomFormats
|
||||
public override int Order => 3;
|
||||
public override string ImplementationName => "Language";
|
||||
|
||||
[FieldDefinition(1, Label = "Language", Type = FieldType.Select, SelectOptions = typeof(LanguageFieldConverter))]
|
||||
[FieldDefinition(1, Label = "CustomFormatsSpecificationLanguage", Type = FieldType.Select, SelectOptions = typeof(LanguageFieldConverter))]
|
||||
public int Value { get; set; }
|
||||
|
||||
protected override bool IsSatisfiedByWithoutNegate(CustomFormatInput input)
|
||||
|
||||
@@ -21,7 +21,7 @@ namespace NzbDrone.Core.CustomFormats
|
||||
protected Regex _regex;
|
||||
protected string _raw;
|
||||
|
||||
[FieldDefinition(1, Label = "Regular Expression", HelpText = "Custom Format RegEx is Case Insensitive")]
|
||||
[FieldDefinition(1, Label = "CustomFormatsSpecificationRegularExpression", HelpText = "CustomFormatsSpecificationRegularExpressionHelpText")]
|
||||
public string Value
|
||||
{
|
||||
get => _raw;
|
||||
|
||||
@@ -20,7 +20,7 @@ namespace NzbDrone.Core.CustomFormats
|
||||
public override int Order => 6;
|
||||
public override string ImplementationName => "Resolution";
|
||||
|
||||
[FieldDefinition(1, Label = "Resolution", Type = FieldType.Select, SelectOptions = typeof(Resolution))]
|
||||
[FieldDefinition(1, Label = "CustomFormatsSpecificationResolution", Type = FieldType.Select, SelectOptions = typeof(Resolution))]
|
||||
public int Value { get; set; }
|
||||
|
||||
protected override bool IsSatisfiedByWithoutNegate(CustomFormatInput input)
|
||||
|
||||
@@ -20,10 +20,10 @@ namespace NzbDrone.Core.CustomFormats
|
||||
public override int Order => 8;
|
||||
public override string ImplementationName => "Size";
|
||||
|
||||
[FieldDefinition(1, Label = "Minimum Size", HelpText = "Release must be greater than this size", Unit = "GB", Type = FieldType.Number)]
|
||||
[FieldDefinition(1, Label = "CustomFormatsSpecificationMinimumSize", HelpText = "CustomFormatsSpecificationMinimumSizeHelpText", Unit = "GB", Type = FieldType.Number)]
|
||||
public double Min { get; set; }
|
||||
|
||||
[FieldDefinition(1, Label = "Maximum Size", HelpText = "Release must be less than or equal to this size", Unit = "GB", Type = FieldType.Number)]
|
||||
[FieldDefinition(1, Label = "CustomFormatsSpecificationMaximumSize", HelpText = "CustomFormatsSpecificationMaximumSizeHelpText", Unit = "GB", Type = FieldType.Number)]
|
||||
public double Max { get; set; }
|
||||
|
||||
protected override bool IsSatisfiedByWithoutNegate(CustomFormatInput input)
|
||||
|
||||
@@ -20,7 +20,7 @@ namespace NzbDrone.Core.CustomFormats
|
||||
public override int Order => 5;
|
||||
public override string ImplementationName => "Source";
|
||||
|
||||
[FieldDefinition(1, Label = "Source", Type = FieldType.Select, SelectOptions = typeof(QualitySource))]
|
||||
[FieldDefinition(1, Label = "CustomFormatsSpecificationSource", Type = FieldType.Select, SelectOptions = typeof(QualitySource))]
|
||||
public int Value { get; set; }
|
||||
|
||||
protected override bool IsSatisfiedByWithoutNegate(CustomFormatInput input)
|
||||
|
||||
@@ -8,6 +8,8 @@ namespace NzbDrone.Core.Datastore.Migration
|
||||
{
|
||||
protected override void MainDbUpgrade()
|
||||
{
|
||||
Delete.FromTable("Commands").AllRows();
|
||||
|
||||
Alter.Table("Blocklist").AlterColumn("Date").AsDateTimeOffset().NotNullable();
|
||||
Alter.Table("Blocklist").AlterColumn("PublishedDate").AsDateTimeOffset().Nullable();
|
||||
Alter.Table("Commands").AlterColumn("QueuedAt").AsDateTimeOffset().NotNullable();
|
||||
|
||||
@@ -2,6 +2,7 @@ using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Xml.Linq;
|
||||
using System.Xml.XPath;
|
||||
using NzbDrone.Common.Extensions;
|
||||
using NzbDrone.Common.Http;
|
||||
using NzbDrone.Core.Download.Extensions;
|
||||
|
||||
@@ -97,8 +98,14 @@ namespace NzbDrone.Core.Download.Clients.Aria2
|
||||
|
||||
public string AddMagnet(Aria2Settings settings, string magnet)
|
||||
{
|
||||
var response = ExecuteRequest(settings, "aria2.addUri", GetToken(settings), new List<string> { magnet });
|
||||
var options = new Dictionary<string, string>();
|
||||
|
||||
if (settings.Directory.IsNotNullOrWhiteSpace())
|
||||
{
|
||||
options.Add("dir", settings.Directory);
|
||||
}
|
||||
|
||||
var response = ExecuteRequest(settings, "aria2.addUri", GetToken(settings), new List<string> { magnet }, options);
|
||||
var gid = response.GetStringResponse();
|
||||
|
||||
return gid;
|
||||
@@ -106,8 +113,16 @@ namespace NzbDrone.Core.Download.Clients.Aria2
|
||||
|
||||
public string AddTorrent(Aria2Settings settings, byte[] torrent)
|
||||
{
|
||||
var response = ExecuteRequest(settings, "aria2.addTorrent", GetToken(settings), torrent);
|
||||
// Aria2's second parameter is an array of URIs and needs to be sent if options are provided, this satisfies that requirement.
|
||||
var emptyListOfUris = new List<string>();
|
||||
var options = new Dictionary<string, string>();
|
||||
|
||||
if (settings.Directory.IsNotNullOrWhiteSpace())
|
||||
{
|
||||
options.Add("dir", settings.Directory);
|
||||
}
|
||||
|
||||
var response = ExecuteRequest(settings, "aria2.addTorrent", GetToken(settings), torrent, emptyListOfUris, options);
|
||||
var gid = response.GetStringResponse();
|
||||
|
||||
return gid;
|
||||
|
||||
@@ -41,6 +41,9 @@ namespace NzbDrone.Core.Download.Clients.Aria2
|
||||
[FieldDefinition(4, Label = "SecretToken", Type = FieldType.Password, Privacy = PrivacyLevel.Password)]
|
||||
public string SecretToken { get; set; }
|
||||
|
||||
[FieldDefinition(5, Label = "Directory", Type = FieldType.Textbox, HelpText = "DownloadClientAriaSettingsDirectoryHelpText")]
|
||||
public string Directory { get; set; }
|
||||
|
||||
public NzbDroneValidationResult Validate()
|
||||
{
|
||||
return new NzbDroneValidationResult(Validator.Validate(this));
|
||||
|
||||
@@ -620,7 +620,7 @@ namespace NzbDrone.Core.Download.Clients.QBittorrent
|
||||
}
|
||||
else if (torrent.RatioLimit == -2 && config.MaxRatioEnabled)
|
||||
{
|
||||
if (torrent.Ratio >= config.MaxRatio)
|
||||
if (Math.Round(torrent.Ratio, 2) >= config.MaxRatio)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -368,6 +368,7 @@ namespace NzbDrone.Core.Download.Pending
|
||||
RemoteEpisode = pendingRelease.RemoteEpisode,
|
||||
Timeleft = timeleft,
|
||||
EstimatedCompletionTime = ect,
|
||||
Added = pendingRelease.Added,
|
||||
Status = pendingRelease.Reason.ToString(),
|
||||
Protocol = pendingRelease.RemoteEpisode.Release.DownloadProtocol,
|
||||
Indexer = pendingRelease.RemoteEpisode.Release.Indexer
|
||||
|
||||
@@ -225,9 +225,7 @@ namespace NzbDrone.Core.Download
|
||||
}
|
||||
catch (FormatException ex)
|
||||
{
|
||||
_logger.Error(ex, "Failed to parse magnetlink for episode '{0}': '{1}'", remoteEpisode.Release.Title, magnetUrl);
|
||||
|
||||
return null;
|
||||
throw new ReleaseDownloadException(remoteEpisode.Release, "Failed to parse magnetlink for episode '{0}': '{1}'", ex, remoteEpisode.Release.Title, magnetUrl);
|
||||
}
|
||||
|
||||
if (hash != null)
|
||||
|
||||
@@ -15,6 +15,7 @@ namespace NzbDrone.Core.Download.TrackedDownloads
|
||||
public TrackedDownloadStatusMessage[] StatusMessages { get; private set; }
|
||||
public DownloadProtocol Protocol { get; set; }
|
||||
public string Indexer { get; set; }
|
||||
public DateTime? Added { get; set; }
|
||||
public bool IsTrackable { get; set; }
|
||||
public bool HasNotifiedManualInteractionRequired { get; set; }
|
||||
|
||||
|
||||
@@ -135,6 +135,7 @@ namespace NzbDrone.Core.Download.TrackedDownloads
|
||||
var grabbedEvent = historyItems.FirstOrDefault(v => v.EventType == EpisodeHistoryEventType.Grabbed);
|
||||
|
||||
trackedDownload.Indexer = grabbedEvent?.Data["indexer"];
|
||||
trackedDownload.Added = grabbedEvent?.Date;
|
||||
|
||||
if (parsedEpisodeInfo == null ||
|
||||
trackedDownload.RemoteEpisode == null ||
|
||||
|
||||
@@ -18,7 +18,7 @@ namespace NzbDrone.Core.Extras.Metadata.Consumers.Plex
|
||||
SeriesPlexMatchFile = true;
|
||||
}
|
||||
|
||||
[FieldDefinition(0, Label = "Series Plex Match File", Type = FieldType.Checkbox, Section = MetadataSectionType.Metadata, HelpText = "Creates a .plexmatch file in the series folder")]
|
||||
[FieldDefinition(0, Label = "MetadataPlexSettingsSeriesPlexMatchFile", Type = FieldType.Checkbox, Section = MetadataSectionType.Metadata, HelpText = "MetadataPlexSettingsSeriesPlexMatchFileHelpText")]
|
||||
public bool SeriesPlexMatchFile { get; set; }
|
||||
|
||||
public NzbDroneValidationResult Validate()
|
||||
|
||||
@@ -21,16 +21,16 @@ namespace NzbDrone.Core.Extras.Metadata.Consumers.Roksbox
|
||||
EpisodeImages = true;
|
||||
}
|
||||
|
||||
[FieldDefinition(0, Label = "Episode Metadata", Type = FieldType.Checkbox, Section = MetadataSectionType.Metadata, HelpText = "Season##\\filename.xml")]
|
||||
[FieldDefinition(0, Label = "MetadataSettingsEpisodeMetadata", Type = FieldType.Checkbox, Section = MetadataSectionType.Metadata, HelpText = "Season##\\filename.xml")]
|
||||
public bool EpisodeMetadata { get; set; }
|
||||
|
||||
[FieldDefinition(1, Label = "Series Images", Type = FieldType.Checkbox, Section = MetadataSectionType.Image, HelpText = "Series Title.jpg")]
|
||||
[FieldDefinition(1, Label = "MetadataSettingsSeriesImages", Type = FieldType.Checkbox, Section = MetadataSectionType.Image, HelpText = "Series Title.jpg")]
|
||||
public bool SeriesImages { get; set; }
|
||||
|
||||
[FieldDefinition(2, Label = "Season Images", Type = FieldType.Checkbox, Section = MetadataSectionType.Image, HelpText = "Season ##.jpg")]
|
||||
[FieldDefinition(2, Label = "MetadataSettingsSeasonImages", Type = FieldType.Checkbox, Section = MetadataSectionType.Image, HelpText = "Season ##.jpg")]
|
||||
public bool SeasonImages { get; set; }
|
||||
|
||||
[FieldDefinition(3, Label = "Episode Images", Type = FieldType.Checkbox, Section = MetadataSectionType.Image, HelpText = "Season##\\filename.jpg")]
|
||||
[FieldDefinition(3, Label = "MetadataSettingsEpisodeImages", Type = FieldType.Checkbox, Section = MetadataSectionType.Image, HelpText = "Season##\\filename.jpg")]
|
||||
public bool EpisodeImages { get; set; }
|
||||
|
||||
public bool IsValid => true;
|
||||
|
||||
@@ -21,16 +21,16 @@ namespace NzbDrone.Core.Extras.Metadata.Consumers.Wdtv
|
||||
EpisodeImages = true;
|
||||
}
|
||||
|
||||
[FieldDefinition(0, Label = "Episode Metadata", Type = FieldType.Checkbox, Section = MetadataSectionType.Metadata, HelpText = "Season##\\filename.xml")]
|
||||
[FieldDefinition(0, Label = "MetadataSettingsEpisodeMetadata", Type = FieldType.Checkbox, Section = MetadataSectionType.Metadata, HelpText = "Season##\\filename.xml")]
|
||||
public bool EpisodeMetadata { get; set; }
|
||||
|
||||
[FieldDefinition(1, Label = "Series Images", Type = FieldType.Checkbox, Section = MetadataSectionType.Image, HelpText = "folder.jpg")]
|
||||
[FieldDefinition(1, Label = "MetadataSettingsSeriesImages", Type = FieldType.Checkbox, Section = MetadataSectionType.Image, HelpText = "folder.jpg")]
|
||||
public bool SeriesImages { get; set; }
|
||||
|
||||
[FieldDefinition(2, Label = "Season Images", Type = FieldType.Checkbox, Section = MetadataSectionType.Image, HelpText = "Season##\\folder.jpg")]
|
||||
[FieldDefinition(2, Label = "MetadataSettingsSeasonImages", Type = FieldType.Checkbox, Section = MetadataSectionType.Image, HelpText = "Season##\\folder.jpg")]
|
||||
public bool SeasonImages { get; set; }
|
||||
|
||||
[FieldDefinition(3, Label = "Episode Images", Type = FieldType.Checkbox, Section = MetadataSectionType.Image, HelpText = "Season##\\filename.metathumb")]
|
||||
[FieldDefinition(3, Label = "MetadataSettingsEpisodeImages", Type = FieldType.Checkbox, Section = MetadataSectionType.Image, HelpText = "Season##\\filename.metathumb")]
|
||||
public bool EpisodeImages { get; set; }
|
||||
|
||||
public bool IsValid => true;
|
||||
|
||||
@@ -24,28 +24,28 @@ namespace NzbDrone.Core.Extras.Metadata.Consumers.Xbmc
|
||||
EpisodeImages = true;
|
||||
}
|
||||
|
||||
[FieldDefinition(0, Label = "Series Metadata", Type = FieldType.Checkbox, Section = MetadataSectionType.Metadata, HelpText = "tvshow.nfo with full series metadata")]
|
||||
[FieldDefinition(0, Label = "MetadataSettingsSeriesMetadata", Type = FieldType.Checkbox, Section = MetadataSectionType.Metadata, HelpText = "MetadataXmbcSettingsSeriesMetadataHelpText")]
|
||||
public bool SeriesMetadata { get; set; }
|
||||
|
||||
[FieldDefinition(1, Label = "Series Metadata Episode Guide", Type = FieldType.Checkbox, Section = MetadataSectionType.Metadata, HelpText = "Include JSON formatted episode guide element in tvshow.nfo (Requires 'Series Metadata')", Advanced = true)]
|
||||
[FieldDefinition(1, Label = "MetadataSettingsSeriesMetadataEpisodeGuide", Type = FieldType.Checkbox, Section = MetadataSectionType.Metadata, HelpText = "MetadataXmbcSettingsSeriesMetadataEpisodeGuideHelpText", Advanced = true)]
|
||||
public bool SeriesMetadataEpisodeGuide { get; set; }
|
||||
|
||||
[FieldDefinition(2, Label = "Series Metadata URL", Type = FieldType.Checkbox, Section = MetadataSectionType.Metadata, HelpText = "Include TheTVDB show URL in tvshow.nfo (can be combined with 'Series Metadata')", Advanced = true)]
|
||||
[FieldDefinition(2, Label = "MetadataSettingsSeriesMetadataUrl", Type = FieldType.Checkbox, Section = MetadataSectionType.Metadata, HelpText = "MetadataXmbcSettingsSeriesMetadataUrlHelpText", Advanced = true)]
|
||||
public bool SeriesMetadataUrl { get; set; }
|
||||
|
||||
[FieldDefinition(3, Label = "Episode Metadata", Type = FieldType.Checkbox, Section = MetadataSectionType.Metadata, HelpText = "<filename>.nfo")]
|
||||
[FieldDefinition(3, Label = "MetadataSettingsEpisodeMetadata", Type = FieldType.Checkbox, Section = MetadataSectionType.Metadata, HelpText = "<filename>.nfo")]
|
||||
public bool EpisodeMetadata { get; set; }
|
||||
|
||||
[FieldDefinition(4, Label = "Episode Metadata Image Thumbs", Type = FieldType.Checkbox, Section = MetadataSectionType.Image, HelpText = "Include image thumb tags in <filename>.nfo (Requires 'Episode Metadata')", Advanced = true)]
|
||||
[FieldDefinition(4, Label = "MetadataSettingsEpisodeMetadataImageThumbs", Type = FieldType.Checkbox, Section = MetadataSectionType.Image, HelpText = "MetadataXmbcSettingsEpisodeMetadataImageThumbsHelpText", Advanced = true)]
|
||||
public bool EpisodeImageThumb { get; set; }
|
||||
|
||||
[FieldDefinition(5, Label = "Series Images", Type = FieldType.Checkbox, Section = MetadataSectionType.Image, HelpText = "fanart.jpg, poster.jpg, banner.jpg")]
|
||||
[FieldDefinition(5, Label = "MetadataSettingsSeriesImages", Type = FieldType.Checkbox, Section = MetadataSectionType.Image, HelpText = "fanart.jpg, poster.jpg, banner.jpg")]
|
||||
public bool SeriesImages { get; set; }
|
||||
|
||||
[FieldDefinition(6, Label = "Season Images", Type = FieldType.Checkbox, Section = MetadataSectionType.Image, HelpText = "season##-poster.jpg, season##-banner.jpg, season-specials-poster.jpg, season-specials-banner.jpg")]
|
||||
[FieldDefinition(6, Label = "MetadataSettingsSeasonImages", Type = FieldType.Checkbox, Section = MetadataSectionType.Image, HelpText = "season##-poster.jpg, season##-banner.jpg, season-specials-poster.jpg, season-specials-banner.jpg")]
|
||||
public bool SeasonImages { get; set; }
|
||||
|
||||
[FieldDefinition(7, Label = "Episode Images", Type = FieldType.Checkbox, Section = MetadataSectionType.Image, HelpText = "<filename>-thumb.jpg")]
|
||||
[FieldDefinition(7, Label = "MetadataSettingsEpisodeImages", Type = FieldType.Checkbox, Section = MetadataSectionType.Image, HelpText = "<filename>-thumb.jpg")]
|
||||
public bool EpisodeImages { get; set; }
|
||||
|
||||
public bool IsValid => true;
|
||||
|
||||
@@ -124,22 +124,20 @@ namespace NzbDrone.Core.History
|
||||
|
||||
public PagingSpec<EpisodeHistory> GetPaged(PagingSpec<EpisodeHistory> pagingSpec, int[] languages, int[] qualities)
|
||||
{
|
||||
pagingSpec.Records = GetPagedRecords(PagedBuilder(pagingSpec, languages, qualities), pagingSpec, PagedQuery);
|
||||
pagingSpec.Records = GetPagedRecords(PagedBuilder(languages, qualities), pagingSpec, PagedQuery);
|
||||
|
||||
var countTemplate = $"SELECT COUNT(*) FROM (SELECT /**select**/ FROM \"{TableMapping.Mapper.TableNameMapping(typeof(EpisodeHistory))}\" /**join**/ /**innerjoin**/ /**leftjoin**/ /**where**/ /**groupby**/ /**having**/) AS \"Inner\"";
|
||||
pagingSpec.TotalRecords = GetPagedRecordCount(PagedBuilder(pagingSpec, languages, qualities).Select(typeof(EpisodeHistory)), pagingSpec, countTemplate);
|
||||
pagingSpec.TotalRecords = GetPagedRecordCount(PagedBuilder(languages, qualities).Select(typeof(EpisodeHistory)), pagingSpec, countTemplate);
|
||||
|
||||
return pagingSpec;
|
||||
}
|
||||
|
||||
private SqlBuilder PagedBuilder(PagingSpec<EpisodeHistory> pagingSpec, int[] languages, int[] qualities)
|
||||
private SqlBuilder PagedBuilder(int[] languages, int[] qualities)
|
||||
{
|
||||
var builder = Builder()
|
||||
.Join<EpisodeHistory, Series>((h, a) => h.SeriesId == a.Id)
|
||||
.Join<EpisodeHistory, Episode>((h, a) => h.EpisodeId == a.Id);
|
||||
|
||||
AddFilters(builder, pagingSpec);
|
||||
|
||||
if (languages is { Length: > 0 })
|
||||
{
|
||||
builder.Where($"({BuildLanguageWhereClause(languages)})");
|
||||
|
||||
@@ -3,6 +3,7 @@ using System.Collections.Generic;
|
||||
using NLog;
|
||||
using NzbDrone.Common.Http;
|
||||
using NzbDrone.Core.Configuration;
|
||||
using NzbDrone.Core.Localization;
|
||||
using NzbDrone.Core.Parser;
|
||||
using NzbDrone.Core.Parser.Model;
|
||||
using NzbDrone.Core.Validation;
|
||||
@@ -28,8 +29,9 @@ namespace NzbDrone.Core.ImportLists.AniList
|
||||
IImportListStatusService importListStatusService,
|
||||
IConfigService configService,
|
||||
IParsingService parsingService,
|
||||
ILocalizationService localizationService,
|
||||
Logger logger)
|
||||
: base(httpClient, importListStatusService, configService, parsingService, logger)
|
||||
: base(httpClient, importListStatusService, configService, parsingService, localizationService, logger)
|
||||
{
|
||||
_importListRepository = netImportRepository;
|
||||
}
|
||||
|
||||
@@ -42,16 +42,16 @@ namespace NzbDrone.Core.ImportLists.AniList
|
||||
|
||||
public string BaseUrl { get; set; }
|
||||
|
||||
[FieldDefinition(0, Label = "Access Token", Type = FieldType.Textbox, Hidden = HiddenType.Hidden)]
|
||||
[FieldDefinition(0, Label = "ImportListsSettingsAccessToken", Type = FieldType.Textbox, Hidden = HiddenType.Hidden)]
|
||||
public string AccessToken { get; set; }
|
||||
|
||||
[FieldDefinition(0, Label = "Refresh Token", Type = FieldType.Textbox, Hidden = HiddenType.Hidden)]
|
||||
[FieldDefinition(0, Label = "ImportListsSettingsRefreshToken", Type = FieldType.Textbox, Hidden = HiddenType.Hidden)]
|
||||
public string RefreshToken { get; set; }
|
||||
|
||||
[FieldDefinition(0, Label = "Expires", Type = FieldType.Textbox, Hidden = HiddenType.Hidden)]
|
||||
[FieldDefinition(0, Label = "ImportListsSettingsExpires", Type = FieldType.Textbox, Hidden = HiddenType.Hidden)]
|
||||
public DateTime Expires { get; set; }
|
||||
|
||||
[FieldDefinition(99, Label = "Authenticate with AniList", Type = FieldType.OAuth)]
|
||||
[FieldDefinition(99, Label = "ImportListsAniListSettingsAuthenticateWithAniList", Type = FieldType.OAuth)]
|
||||
public string SignIn { get; set; }
|
||||
|
||||
public NzbDroneValidationResult Validate()
|
||||
|
||||
@@ -9,6 +9,7 @@ using NzbDrone.Core.Configuration;
|
||||
using NzbDrone.Core.Http.CloudFlare;
|
||||
using NzbDrone.Core.ImportLists.Exceptions;
|
||||
using NzbDrone.Core.Indexers.Exceptions;
|
||||
using NzbDrone.Core.Localization;
|
||||
using NzbDrone.Core.Parser;
|
||||
using NzbDrone.Core.Parser.Model;
|
||||
|
||||
@@ -21,12 +22,13 @@ namespace NzbDrone.Core.ImportLists.AniList.List
|
||||
IImportListStatusService importListStatusService,
|
||||
IConfigService configService,
|
||||
IParsingService parsingService,
|
||||
ILocalizationService localizationService,
|
||||
Logger logger)
|
||||
: base(netImportRepository, httpClient, importListStatusService, configService, parsingService, logger)
|
||||
: base(netImportRepository, httpClient, importListStatusService, configService, parsingService, localizationService, logger)
|
||||
{
|
||||
}
|
||||
|
||||
public override string Name => "AniList List";
|
||||
public override string Name => _localizationService.GetLocalizedString("TypeOfList", new Dictionary<string, object> { { "typeOfList", "AniList" } });
|
||||
|
||||
public override AniListRequestGenerator GetRequestGenerator()
|
||||
{
|
||||
|
||||
@@ -31,40 +31,40 @@ namespace NzbDrone.Core.ImportLists.AniList.List
|
||||
|
||||
protected override AbstractValidator<AniListSettings> Validator => new AniListSettingsValidator();
|
||||
|
||||
[FieldDefinition(1, Label = "Username", HelpText = "Username for the List to import from")]
|
||||
[FieldDefinition(1, Label = "Username", HelpText = "ImportListsAniListSettingsUsernameHelpText")]
|
||||
public string Username { get; set; }
|
||||
|
||||
[FieldDefinition(2, Label = "Import Watching", Type = FieldType.Checkbox, Section = sectionImport, HelpText = "List: Currently Watching")]
|
||||
[FieldDefinition(2, Label = "ImportListsAniListSettingsImportWatching", Type = FieldType.Checkbox, Section = sectionImport, HelpText = "ImportListsAniListSettingsImportWatchingHelpText")]
|
||||
public bool ImportCurrent { get; set; }
|
||||
|
||||
[FieldDefinition(3, Label = "Import Planning", Type = FieldType.Checkbox, Section = sectionImport, HelpText = "List: Planning to Watch")]
|
||||
[FieldDefinition(3, Label = "ImportListsAniListSettingsImportPlanning", Type = FieldType.Checkbox, Section = sectionImport, HelpText = "ImportListsAniListSettingsImportPlanningHelpText")]
|
||||
public bool ImportPlanning { get; set; }
|
||||
|
||||
[FieldDefinition(4, Label = "Import Completed", Type = FieldType.Checkbox, Section = sectionImport, HelpText = "List: Completed Watching")]
|
||||
[FieldDefinition(4, Label = "ImportListsAniListSettingsImportCompleted", Type = FieldType.Checkbox, Section = sectionImport, HelpText = "ImportListsAniListSettingsImportCompletedHelpText")]
|
||||
public bool ImportCompleted { get; set; }
|
||||
|
||||
[FieldDefinition(5, Label = "Import Dropped", Type = FieldType.Checkbox, Section = sectionImport, HelpText = "List: Dropped")]
|
||||
[FieldDefinition(5, Label = "ImportListsAniListSettingsImportDropped", Type = FieldType.Checkbox, Section = sectionImport, HelpText = "ImportListsAniListSettingsImportDroppedHelpText")]
|
||||
public bool ImportDropped { get; set; }
|
||||
|
||||
[FieldDefinition(6, Label = "Import Paused", Type = FieldType.Checkbox, Section = sectionImport, HelpText = "List: On Hold")]
|
||||
[FieldDefinition(6, Label = "ImportListsAniListSettingsImportPaused", Type = FieldType.Checkbox, Section = sectionImport, HelpText = "ImportListsAniListSettingsImportPausedHelpText")]
|
||||
public bool ImportPaused { get; set; }
|
||||
|
||||
[FieldDefinition(7, Label = "Import Repeating", Type = FieldType.Checkbox, Section = sectionImport, HelpText = "List: Currently Rewatching")]
|
||||
[FieldDefinition(7, Label = "ImportListsAniListSettingsImportRepeating", Type = FieldType.Checkbox, Section = sectionImport, HelpText = "ImportListsAniListSettingsImportRepeatingHelpText")]
|
||||
public bool ImportRepeating { get; set; }
|
||||
|
||||
[FieldDefinition(8, Label = "Import Finished", Type = FieldType.Checkbox, Section = sectionImport, HelpText = "Media: All episodes have aired")]
|
||||
[FieldDefinition(8, Label = "ImportListsAniListSettingsImportFinished", Type = FieldType.Checkbox, Section = sectionImport, HelpText = "ImportListsAniListSettingsImportFinishedHelpText")]
|
||||
public bool ImportFinished { get; set; }
|
||||
|
||||
[FieldDefinition(9, Label = "Import Releasing", Type = FieldType.Checkbox, Section = sectionImport, HelpText = "Media: Currently airing new episodes")]
|
||||
[FieldDefinition(9, Label = "ImportListsAniListSettingsImportReleasing", Type = FieldType.Checkbox, Section = sectionImport, HelpText = "ImportListsAniListSettingsImportReleasingHelpText")]
|
||||
public bool ImportReleasing { get; set; }
|
||||
|
||||
[FieldDefinition(10, Label = "Import Not Yet Released", Type = FieldType.Checkbox, Section = sectionImport, HelpText = "Media: Airing has not yet started")]
|
||||
[FieldDefinition(10, Label = "ImportListsAniListSettingsImportNotYetReleased", Type = FieldType.Checkbox, Section = sectionImport, HelpText = "ImportListsAniListSettingsImportNotYetReleasedHelpText")]
|
||||
public bool ImportUnreleased { get; set; }
|
||||
|
||||
[FieldDefinition(11, Label = "Import Cancelled", Type = FieldType.Checkbox, Section = sectionImport, HelpText = "Media: Series is cancelled")]
|
||||
[FieldDefinition(11, Label = "ImportListsAniListSettingsImportCancelled", Type = FieldType.Checkbox, Section = sectionImport, HelpText = "ImportListsAniListSettingsImportCancelledHelpText")]
|
||||
public bool ImportCancelled { get; set; }
|
||||
|
||||
[FieldDefinition(12, Label = "Import Hiatus", Type = FieldType.Checkbox, Section = sectionImport, HelpText = "Media: Series on Hiatus")]
|
||||
[FieldDefinition(12, Label = "ImportListsAniListSettingsImportHiatus", Type = FieldType.Checkbox, Section = sectionImport, HelpText = "ImportListsAniListSettingsImportHiatusHelpText")]
|
||||
public bool ImportHiatus { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ using FluentValidation.Results;
|
||||
using NLog;
|
||||
using NzbDrone.Common.Extensions;
|
||||
using NzbDrone.Core.Configuration;
|
||||
using NzbDrone.Core.Localization;
|
||||
using NzbDrone.Core.Parser;
|
||||
using NzbDrone.Core.Parser.Model;
|
||||
|
||||
@@ -12,7 +13,7 @@ namespace NzbDrone.Core.ImportLists.Custom
|
||||
public class CustomImport : ImportListBase<CustomSettings>
|
||||
{
|
||||
private readonly ICustomImportProxy _customProxy;
|
||||
public override string Name => "Custom List";
|
||||
public override string Name => _localizationService.GetLocalizedString("ImportListsCustomListSettingsName");
|
||||
|
||||
public override TimeSpan MinRefreshInterval => TimeSpan.FromHours(6);
|
||||
|
||||
@@ -22,8 +23,9 @@ namespace NzbDrone.Core.ImportLists.Custom
|
||||
IImportListStatusService importListStatusService,
|
||||
IConfigService configService,
|
||||
IParsingService parsingService,
|
||||
ILocalizationService localizationService,
|
||||
Logger logger)
|
||||
: base(importListStatusService, configService, parsingService, logger)
|
||||
: base(importListStatusService, configService, parsingService, localizationService, logger)
|
||||
{
|
||||
_customProxy = customProxy;
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ using NLog;
|
||||
|
||||
using NzbDrone.Common.Extensions;
|
||||
using NzbDrone.Common.Http;
|
||||
using NzbDrone.Core.Localization;
|
||||
|
||||
namespace NzbDrone.Core.ImportLists.Custom
|
||||
{
|
||||
@@ -22,11 +23,13 @@ namespace NzbDrone.Core.ImportLists.Custom
|
||||
public class CustomImportProxy : ICustomImportProxy
|
||||
{
|
||||
private readonly IHttpClient _httpClient;
|
||||
private readonly ILocalizationService _localizationService;
|
||||
private readonly Logger _logger;
|
||||
|
||||
public CustomImportProxy(IHttpClient httpClient, Logger logger)
|
||||
public CustomImportProxy(IHttpClient httpClient, ILocalizationService localizationService, Logger logger)
|
||||
{
|
||||
_httpClient = httpClient;
|
||||
_localizationService = localizationService;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
@@ -46,16 +49,16 @@ namespace NzbDrone.Core.ImportLists.Custom
|
||||
if (ex.Response.StatusCode == HttpStatusCode.Unauthorized)
|
||||
{
|
||||
_logger.Error(ex, "There was an authorization issue. We cannot get the list from the provider.");
|
||||
return new ValidationFailure("BaseUrl", "It seems we are unauthorized to make this request.");
|
||||
return new ValidationFailure("BaseUrl", _localizationService.GetLocalizedString("ImportListsCustomListValidationAuthenticationFailure"));
|
||||
}
|
||||
|
||||
_logger.Error(ex, "Unable to connect to import list.");
|
||||
return new ValidationFailure("BaseUrl", $"We are unable to make the request to that URL. StatusCode: {ex.Response.StatusCode}");
|
||||
return new ValidationFailure("BaseUrl", _localizationService.GetLocalizedString("ImportListsCustomListValidationConnectionError", new Dictionary<string, object> { { "exceptionStatusCode", ex.Response.StatusCode } }));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.Error(ex, "Unable to connect to import list.");
|
||||
return new ValidationFailure(string.Empty, $"Unable to connect to import list: {ex.Message}. Check the log surrounding this error for details.");
|
||||
return new ValidationFailure(string.Empty, _localizationService.GetLocalizedString("ImportListsValidationUnableToConnectException", new Dictionary<string, object> { { "exceptionMessage", ex.Message } }));
|
||||
}
|
||||
|
||||
return null;
|
||||
|
||||
@@ -22,7 +22,7 @@ namespace NzbDrone.Core.ImportLists.Custom
|
||||
BaseUrl = "";
|
||||
}
|
||||
|
||||
[FieldDefinition(0, Label = "List URL", HelpText = "The URL for the series list")]
|
||||
[FieldDefinition(0, Label = "ImportListsCustomListSettingsUrl", HelpText = "ImportListsCustomListSettingsUrlHelpText")]
|
||||
public string BaseUrl { get; set; }
|
||||
|
||||
public NzbDroneValidationResult Validate()
|
||||
|
||||
@@ -10,6 +10,7 @@ using NzbDrone.Core.Configuration;
|
||||
using NzbDrone.Core.Http.CloudFlare;
|
||||
using NzbDrone.Core.ImportLists.Exceptions;
|
||||
using NzbDrone.Core.Indexers.Exceptions;
|
||||
using NzbDrone.Core.Localization;
|
||||
using NzbDrone.Core.Parser;
|
||||
using NzbDrone.Core.Parser.Model;
|
||||
using NzbDrone.Core.Validation;
|
||||
@@ -31,8 +32,8 @@ namespace NzbDrone.Core.ImportLists
|
||||
public abstract IImportListRequestGenerator GetRequestGenerator();
|
||||
public abstract IParseImportListResponse GetParser();
|
||||
|
||||
public HttpImportListBase(IHttpClient httpClient, IImportListStatusService importListStatusService, IConfigService configService, IParsingService parsingService, Logger logger)
|
||||
: base(importListStatusService, configService, parsingService, logger)
|
||||
public HttpImportListBase(IHttpClient httpClient, IImportListStatusService importListStatusService, IConfigService configService, IParsingService parsingService, ILocalizationService localizationService, Logger logger)
|
||||
: base(importListStatusService, configService, parsingService, localizationService, logger)
|
||||
{
|
||||
_httpClient = httpClient;
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ using System.Collections.Generic;
|
||||
using NLog;
|
||||
using NzbDrone.Common.Http;
|
||||
using NzbDrone.Core.Configuration;
|
||||
using NzbDrone.Core.Localization;
|
||||
using NzbDrone.Core.Parser;
|
||||
using NzbDrone.Core.ThingiProvider;
|
||||
|
||||
@@ -10,7 +11,7 @@ namespace NzbDrone.Core.ImportLists.Imdb
|
||||
{
|
||||
public class ImdbListImport : HttpImportListBase<ImdbListSettings>
|
||||
{
|
||||
public override string Name => "IMDb Lists";
|
||||
public override string Name => _localizationService.GetLocalizedString("TypeOfList", new Dictionary<string, object> { { "typeOfList", "IMDb" } });
|
||||
|
||||
public override ImportListType ListType => ImportListType.Other;
|
||||
public override TimeSpan MinRefreshInterval => TimeSpan.FromHours(12);
|
||||
@@ -19,8 +20,9 @@ namespace NzbDrone.Core.ImportLists.Imdb
|
||||
IImportListStatusService importListStatusService,
|
||||
IConfigService configService,
|
||||
IParsingService parsingService,
|
||||
ILocalizationService localizationService,
|
||||
Logger logger)
|
||||
: base(httpClient, importListStatusService, configService, parsingService, logger)
|
||||
: base(httpClient, importListStatusService, configService, parsingService, localizationService, logger)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@ namespace NzbDrone.Core.ImportLists.Imdb
|
||||
|
||||
public string BaseUrl { get; set; }
|
||||
|
||||
[FieldDefinition(1, Label = "List ID", HelpText = "IMDb list ID (e.g ls12345678)")]
|
||||
[FieldDefinition(1, Label = "ImportListsImdbSettingsListId", HelpText = "ImportListsImdbSettingsListIdHelpText")]
|
||||
public string ListId { get; set; }
|
||||
|
||||
public NzbDroneValidationResult Validate()
|
||||
|
||||
@@ -4,6 +4,7 @@ using System.Linq;
|
||||
using FluentValidation.Results;
|
||||
using NLog;
|
||||
using NzbDrone.Core.Configuration;
|
||||
using NzbDrone.Core.Localization;
|
||||
using NzbDrone.Core.Parser;
|
||||
using NzbDrone.Core.Parser.Model;
|
||||
using NzbDrone.Core.ThingiProvider;
|
||||
@@ -16,6 +17,7 @@ namespace NzbDrone.Core.ImportLists
|
||||
protected readonly IImportListStatusService _importListStatusService;
|
||||
protected readonly IConfigService _configService;
|
||||
protected readonly IParsingService _parsingService;
|
||||
protected readonly ILocalizationService _localizationService;
|
||||
protected readonly Logger _logger;
|
||||
|
||||
public abstract string Name { get; }
|
||||
@@ -24,11 +26,12 @@ namespace NzbDrone.Core.ImportLists
|
||||
|
||||
public abstract TimeSpan MinRefreshInterval { get; }
|
||||
|
||||
public ImportListBase(IImportListStatusService importListStatusService, IConfigService configService, IParsingService parsingService, Logger logger)
|
||||
public ImportListBase(IImportListStatusService importListStatusService, IConfigService configService, IParsingService parsingService, ILocalizationService localizationService, Logger logger)
|
||||
{
|
||||
_importListStatusService = importListStatusService;
|
||||
_configService = configService;
|
||||
_parsingService = parsingService;
|
||||
_localizationService = localizationService;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
@@ -86,7 +89,7 @@ namespace NzbDrone.Core.ImportLists
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.Error(ex, "Test aborted due to exception");
|
||||
failures.Add(new ValidationFailure(string.Empty, "Test was aborted due to an error: " + ex.Message));
|
||||
failures.Add(new ValidationFailure(string.Empty, _localizationService.GetLocalizedString("ImportListsValidationTestFailed", new Dictionary<string, object> { { "exceptionMessage", ex.Message } })));
|
||||
}
|
||||
|
||||
return new ValidationResult(failures);
|
||||
|
||||
@@ -5,6 +5,7 @@ using NzbDrone.Common.Extensions;
|
||||
using NzbDrone.Common.Http;
|
||||
using NzbDrone.Core.Configuration;
|
||||
using NzbDrone.Core.Exceptions;
|
||||
using NzbDrone.Core.Localization;
|
||||
using NzbDrone.Core.Notifications.Plex.PlexTv;
|
||||
using NzbDrone.Core.Parser;
|
||||
using NzbDrone.Core.Parser.Model;
|
||||
@@ -24,13 +25,14 @@ namespace NzbDrone.Core.ImportLists.Plex
|
||||
IImportListStatusService importListStatusService,
|
||||
IConfigService configService,
|
||||
IParsingService parsingService,
|
||||
ILocalizationService localizationService,
|
||||
Logger logger)
|
||||
: base(httpClient, importListStatusService, configService, parsingService, logger)
|
||||
: base(httpClient, importListStatusService, configService, parsingService, localizationService, logger)
|
||||
{
|
||||
_plexTvService = plexTvService;
|
||||
}
|
||||
|
||||
public override string Name => "Plex Watchlist";
|
||||
public override string Name => _localizationService.GetLocalizedString("ImportListsPlexSettingsWatchlistName");
|
||||
public override int PageSize => 50;
|
||||
|
||||
public override IList<ImportListItemInfo> Fetch()
|
||||
|
||||
@@ -27,10 +27,10 @@ namespace NzbDrone.Core.ImportLists.Plex
|
||||
|
||||
public string BaseUrl { get; set; }
|
||||
|
||||
[FieldDefinition(0, Label = "Access Token", Type = FieldType.Textbox, Hidden = HiddenType.Hidden)]
|
||||
[FieldDefinition(0, Label = "ImportListsSettingsAccessToken", Type = FieldType.Textbox, Hidden = HiddenType.Hidden)]
|
||||
public string AccessToken { get; set; }
|
||||
|
||||
[FieldDefinition(99, Label = "Authenticate with Plex.tv", Type = FieldType.OAuth)]
|
||||
[FieldDefinition(99, Label = "ImportListsPlexSettingsAuthenticateWithPlex", Type = FieldType.OAuth)]
|
||||
public string SignIn { get; set; }
|
||||
|
||||
public NzbDroneValidationResult Validate()
|
||||
|
||||
@@ -2,13 +2,14 @@ using System;
|
||||
using NLog;
|
||||
using NzbDrone.Common.Http;
|
||||
using NzbDrone.Core.Configuration;
|
||||
using NzbDrone.Core.Localization;
|
||||
using NzbDrone.Core.Parser;
|
||||
|
||||
namespace NzbDrone.Core.ImportLists.Rss.Plex
|
||||
{
|
||||
public class PlexRssImport : RssImportBase<PlexRssImportSettings>
|
||||
{
|
||||
public override string Name => "Plex Watchlist RSS";
|
||||
public override string Name => _localizationService.GetLocalizedString("ImportListsPlexSettingsWatchlistRSSName");
|
||||
public override ImportListType ListType => ImportListType.Plex;
|
||||
public override TimeSpan MinRefreshInterval => TimeSpan.FromHours(6);
|
||||
|
||||
@@ -16,8 +17,9 @@ namespace NzbDrone.Core.ImportLists.Rss.Plex
|
||||
IImportListStatusService importListStatusService,
|
||||
IConfigService configService,
|
||||
IParsingService parsingService,
|
||||
ILocalizationService localizationService,
|
||||
Logger logger)
|
||||
: base(httpClient, importListStatusService, configService, parsingService, logger)
|
||||
: base(httpClient, importListStatusService, configService, parsingService, localizationService, logger)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
@@ -3,18 +3,19 @@ using System.Xml.Linq;
|
||||
using NLog;
|
||||
using NzbDrone.Common.Extensions;
|
||||
using NzbDrone.Core.Indexers;
|
||||
using NzbDrone.Core.Indexers.Exceptions;
|
||||
using NzbDrone.Core.Parser.Model;
|
||||
|
||||
namespace NzbDrone.Core.ImportLists.Rss.Plex
|
||||
{
|
||||
public class PlexRssImportParser : RssImportBaseParser
|
||||
{
|
||||
private readonly Logger _logger;
|
||||
private static readonly Regex ImdbIdRegex = new (@"(tt\d{7,8})", RegexOptions.IgnoreCase | RegexOptions.Compiled);
|
||||
|
||||
public PlexRssImportParser(Logger logger)
|
||||
: base(logger)
|
||||
{
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
protected override ImportListItemInfo ProcessItem(XElement item)
|
||||
@@ -53,7 +54,9 @@ namespace NzbDrone.Core.ImportLists.Rss.Plex
|
||||
|
||||
if (info.ImdbId.IsNullOrWhiteSpace() && info.TvdbId == 0 && info.TmdbId == 0)
|
||||
{
|
||||
throw new UnsupportedFeedException("Each item in the RSS feed must have a guid element with a IMDB ID, TVDB ID or TMDB ID");
|
||||
_logger.Warn("Each item in the RSS feed must have a guid element with a IMDB ID, TVDB ID or TMDB ID: '{0}'", info.Title);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
return info;
|
||||
|
||||
@@ -16,7 +16,7 @@ namespace NzbDrone.Core.ImportLists.Rss.Plex
|
||||
{
|
||||
private PlexRssImportSettingsValidator Validator => new ();
|
||||
|
||||
[FieldDefinition(0, Label = "Url", 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 NzbDroneValidationResult Validate()
|
||||
|
||||
@@ -3,6 +3,7 @@ using System.Collections.Generic;
|
||||
using NLog;
|
||||
using NzbDrone.Common.Http;
|
||||
using NzbDrone.Core.Configuration;
|
||||
using NzbDrone.Core.Localization;
|
||||
using NzbDrone.Core.Parser;
|
||||
using NzbDrone.Core.Parser.Model;
|
||||
|
||||
@@ -19,8 +20,9 @@ namespace NzbDrone.Core.ImportLists.Rss
|
||||
IImportListStatusService importListStatusService,
|
||||
IConfigService configService,
|
||||
IParsingService parsingService,
|
||||
ILocalizationService localizationService,
|
||||
Logger logger)
|
||||
: base(httpClient, importListStatusService, configService, parsingService, logger)
|
||||
: base(httpClient, importListStatusService, configService, parsingService, localizationService, logger)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@ namespace NzbDrone.Core.ImportLists.Rss
|
||||
|
||||
public string BaseUrl { get; set; }
|
||||
|
||||
[FieldDefinition(0, Label = "Url", Type = FieldType.Textbox)]
|
||||
[FieldDefinition(0, Label = "ImportListsSettingsRssUrl", Type = FieldType.Textbox)]
|
||||
public virtual string Url { get; set; }
|
||||
|
||||
public virtual NzbDroneValidationResult Validate()
|
||||
|
||||
@@ -4,6 +4,7 @@ using NLog;
|
||||
using NzbDrone.Common.Extensions;
|
||||
using NzbDrone.Common.Http;
|
||||
using NzbDrone.Core.Configuration;
|
||||
using NzbDrone.Core.Localization;
|
||||
using NzbDrone.Core.Parser;
|
||||
using NzbDrone.Core.Parser.Model;
|
||||
using NzbDrone.Core.Validation;
|
||||
@@ -28,8 +29,9 @@ namespace NzbDrone.Core.ImportLists.Simkl
|
||||
IImportListStatusService importListStatusService,
|
||||
IConfigService configService,
|
||||
IParsingService parsingService,
|
||||
ILocalizationService localizationService,
|
||||
Logger logger)
|
||||
: base(httpClient, importListStatusService, configService, parsingService, logger)
|
||||
: base(httpClient, importListStatusService, configService, parsingService, localizationService, logger)
|
||||
{
|
||||
_importListRepository = netImportRepository;
|
||||
}
|
||||
|
||||
@@ -37,19 +37,19 @@ namespace NzbDrone.Core.ImportLists.Simkl
|
||||
|
||||
public string BaseUrl { get; set; }
|
||||
|
||||
[FieldDefinition(0, Label = "Access Token", Type = FieldType.Textbox, Hidden = HiddenType.Hidden)]
|
||||
[FieldDefinition(0, Label = "ImportListsSettingsAccessToken", Type = FieldType.Textbox, Hidden = HiddenType.Hidden)]
|
||||
public string AccessToken { get; set; }
|
||||
|
||||
[FieldDefinition(0, Label = "Refresh Token", Type = FieldType.Textbox, Hidden = HiddenType.Hidden)]
|
||||
[FieldDefinition(0, Label = "ImportListsSettingsRefreshToken", Type = FieldType.Textbox, Hidden = HiddenType.Hidden)]
|
||||
public string RefreshToken { get; set; }
|
||||
|
||||
[FieldDefinition(0, Label = "Expires", Type = FieldType.Textbox, Hidden = HiddenType.Hidden)]
|
||||
[FieldDefinition(0, Label = "ImportListsSettingsExpires", Type = FieldType.Textbox, Hidden = HiddenType.Hidden)]
|
||||
public DateTime Expires { get; set; }
|
||||
|
||||
[FieldDefinition(0, Label = "Auth User", Type = FieldType.Textbox, Hidden = HiddenType.Hidden)]
|
||||
[FieldDefinition(0, Label = "ImportListsSettingsAuthUser", Type = FieldType.Textbox, Hidden = HiddenType.Hidden)]
|
||||
public string AuthUser { get; set; }
|
||||
|
||||
[FieldDefinition(99, Label = "Authenticate with Simkl", Type = FieldType.OAuth)]
|
||||
[FieldDefinition(99, Label = "ImportListsSimklSettingsAuthenticatewithSimkl", Type = FieldType.OAuth)]
|
||||
public string SignIn { get; set; }
|
||||
|
||||
public NzbDroneValidationResult Validate()
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using NLog;
|
||||
using NzbDrone.Common.Http;
|
||||
using NzbDrone.Core.Configuration;
|
||||
using NzbDrone.Core.Localization;
|
||||
using NzbDrone.Core.Parser;
|
||||
|
||||
namespace NzbDrone.Core.ImportLists.Simkl.User
|
||||
@@ -12,12 +13,13 @@ namespace NzbDrone.Core.ImportLists.Simkl.User
|
||||
IImportListStatusService netImportStatusService,
|
||||
IConfigService configService,
|
||||
IParsingService parsingService,
|
||||
ILocalizationService localizationService,
|
||||
Logger logger)
|
||||
: base(netImportRepository, httpClient, netImportStatusService, configService, parsingService, logger)
|
||||
: base(netImportRepository, httpClient, netImportStatusService, configService, parsingService, localizationService, logger)
|
||||
{
|
||||
}
|
||||
|
||||
public override string Name => "Simkl User Watchlist";
|
||||
public override string Name => _localizationService.GetLocalizedString("ImportListsSimklSettingsName");
|
||||
|
||||
public override IImportListRequestGenerator GetRequestGenerator()
|
||||
{
|
||||
|
||||
@@ -1,18 +1,18 @@
|
||||
using System.Runtime.Serialization;
|
||||
using NzbDrone.Core.Annotations;
|
||||
|
||||
namespace NzbDrone.Core.ImportLists.Simkl.User
|
||||
{
|
||||
public enum SimklUserListType
|
||||
{
|
||||
[EnumMember(Value = "Watching")]
|
||||
[FieldOption(Label = "ImportListsSimklSettingsUserListTypeWatching")]
|
||||
Watching = 0,
|
||||
[EnumMember(Value = "Plan To Watch")]
|
||||
[FieldOption(Label = "ImportListsSimklSettingsUserListTypePlanToWatch")]
|
||||
PlanToWatch = 1,
|
||||
[EnumMember(Value = "Hold")]
|
||||
[FieldOption(Label = "ImportListsSimklSettingsUserListTypeHold")]
|
||||
Hold = 2,
|
||||
[EnumMember(Value = "Completed")]
|
||||
[FieldOption(Label = "ImportListsSimklSettingsUserListTypeCompleted")]
|
||||
Completed = 3,
|
||||
[EnumMember(Value = "Dropped")]
|
||||
[FieldOption(Label = "ImportListsSimklSettingsUserListTypeDropped")]
|
||||
Dropped = 4
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,10 +22,10 @@ namespace NzbDrone.Core.ImportLists.Simkl.User
|
||||
ShowType = (int)SimklUserShowType.Shows;
|
||||
}
|
||||
|
||||
[FieldDefinition(1, Label = "List Type", Type = FieldType.Select, SelectOptions = typeof(SimklUserListType), HelpText = "Type of list you're seeking to import from")]
|
||||
[FieldDefinition(1, Label = "ImportListsSimklSettingsListType", Type = FieldType.Select, SelectOptions = typeof(SimklUserListType), HelpText = "ImportListsSimklSettingsListTypeHelpText")]
|
||||
public int ListType { get; set; }
|
||||
|
||||
[FieldDefinition(1, Label = "Show Type", Type = FieldType.Select, SelectOptions = typeof(SimklUserShowType), HelpText = "Type of show you're seeking to import from")]
|
||||
[FieldDefinition(1, Label = "ImportListsSimklSettingsShowType", Type = FieldType.Select, SelectOptions = typeof(SimklUserShowType), HelpText = "ImportListsSimklSettingsShowTypeHelpText")]
|
||||
public int ShowType { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ using FluentValidation.Results;
|
||||
using NLog;
|
||||
using NzbDrone.Common.Extensions;
|
||||
using NzbDrone.Core.Configuration;
|
||||
using NzbDrone.Core.Localization;
|
||||
using NzbDrone.Core.Parser;
|
||||
using NzbDrone.Core.Parser.Model;
|
||||
using NzbDrone.Core.Validation;
|
||||
@@ -23,8 +24,9 @@ namespace NzbDrone.Core.ImportLists.Sonarr
|
||||
IImportListStatusService importListStatusService,
|
||||
IConfigService configService,
|
||||
IParsingService parsingService,
|
||||
ILocalizationService localizationService,
|
||||
Logger logger)
|
||||
: base(importListStatusService, configService, parsingService, logger)
|
||||
: base(importListStatusService, configService, parsingService, localizationService, logger)
|
||||
{
|
||||
_sonarrV3Proxy = sonarrV3Proxy;
|
||||
}
|
||||
|
||||
@@ -29,22 +29,23 @@ namespace NzbDrone.Core.ImportLists.Sonarr
|
||||
RootFolderPaths = Array.Empty<string>();
|
||||
}
|
||||
|
||||
[FieldDefinition(0, Label = "Full URL", HelpText = "URL, including port, of the Sonarr instance to import from")]
|
||||
[FieldDefinition(0, Label = "ImportListsSonarrSettingsFullUrl", HelpText = "ImportListsSonarrSettingsFullUrlHelpText")]
|
||||
public string BaseUrl { get; set; }
|
||||
|
||||
[FieldDefinition(1, Label = "API Key", HelpText = "Apikey of the Sonarr instance to import from")]
|
||||
[FieldDefinition(1, Label = "ApiKey", HelpText = "ImportListsSonarrSettingsApiKeyHelpText")]
|
||||
public string ApiKey { get; set; }
|
||||
|
||||
[FieldDefinition(2, Type = FieldType.Select, SelectOptionsProviderAction = "getProfiles", Label = "Quality Profiles", HelpText = "Quality Profiles from the source instance to import from")]
|
||||
[FieldDefinition(2, Type = FieldType.Select, SelectOptionsProviderAction = "getProfiles", Label = "QualityProfiles", HelpText = "ImportListsSonarrSettingsQualityProfilesHelpText")]
|
||||
public IEnumerable<int> ProfileIds { get; set; }
|
||||
|
||||
// TODO: Remove this eventually, no translation added as deprecated
|
||||
[FieldDefinition(3, Type = FieldType.Select, SelectOptionsProviderAction = "getLanguageProfiles", Label = "Language Profiles", HelpText = "Language Profiles from the source instance to import from")]
|
||||
public IEnumerable<int> LanguageProfileIds { get; set; }
|
||||
|
||||
[FieldDefinition(4, Type = FieldType.Select, SelectOptionsProviderAction = "getTags", Label = "Tags", HelpText = "Tags from the source instance to import from")]
|
||||
[FieldDefinition(4, Type = FieldType.Select, SelectOptionsProviderAction = "getTags", Label = "Tags", HelpText = "ImportListsSonarrSettingsTagsHelpText")]
|
||||
public IEnumerable<int> TagIds { get; set; }
|
||||
|
||||
[FieldDefinition(5, Type = FieldType.Select, SelectOptionsProviderAction = "getRootFolders", Label = "Root Folders", HelpText = "Root Folders from the source instance to import from")]
|
||||
[FieldDefinition(5, Type = FieldType.Select, SelectOptionsProviderAction = "getRootFolders", Label = "RootFolders", HelpText = "ImportListsSonarrSettingsRootFoldersHelpText")]
|
||||
public IEnumerable<string> RootFolderPaths { get; set; }
|
||||
|
||||
public NzbDroneValidationResult Validate()
|
||||
|
||||
@@ -6,6 +6,7 @@ using Newtonsoft.Json;
|
||||
using NLog;
|
||||
using NzbDrone.Common.Extensions;
|
||||
using NzbDrone.Common.Http;
|
||||
using NzbDrone.Core.Localization;
|
||||
|
||||
namespace NzbDrone.Core.ImportLists.Sonarr
|
||||
{
|
||||
@@ -23,10 +24,12 @@ namespace NzbDrone.Core.ImportLists.Sonarr
|
||||
{
|
||||
private readonly IHttpClient _httpClient;
|
||||
private readonly Logger _logger;
|
||||
private readonly ILocalizationService _localizationService;
|
||||
|
||||
public SonarrV3Proxy(IHttpClient httpClient, Logger logger)
|
||||
public SonarrV3Proxy(IHttpClient httpClient, ILocalizationService localizationService, Logger logger)
|
||||
{
|
||||
_httpClient = httpClient;
|
||||
_localizationService = localizationService;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
@@ -66,22 +69,22 @@ namespace NzbDrone.Core.ImportLists.Sonarr
|
||||
if (ex.Response.StatusCode == HttpStatusCode.Unauthorized)
|
||||
{
|
||||
_logger.Error(ex, "API Key is invalid");
|
||||
return new ValidationFailure("ApiKey", "API Key is invalid");
|
||||
return new ValidationFailure("ApiKey", _localizationService.GetLocalizedString("ImportListsValidationInvalidApiKey"));
|
||||
}
|
||||
|
||||
if (ex.Response.HasHttpRedirect)
|
||||
{
|
||||
_logger.Error(ex, "Sonarr returned redirect and is invalid");
|
||||
return new ValidationFailure("BaseUrl", "Sonarr URL is invalid, are you missing a URL base?");
|
||||
return new ValidationFailure("BaseUrl", _localizationService.GetLocalizedString("ImportListsSonarrValidationInvalidUrl"));
|
||||
}
|
||||
|
||||
_logger.Error(ex, "Unable to connect to import list.");
|
||||
return new ValidationFailure(string.Empty, $"Unable to connect to import list: {ex.Message}. Check the log surrounding this error for details.");
|
||||
return new ValidationFailure(string.Empty, _localizationService.GetLocalizedString("ImportListsValidationUnableToConnectException", new Dictionary<string, object> { { "exceptionMessage", ex.Message } }));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.Error(ex, "Unable to connect to import list.");
|
||||
return new ValidationFailure(string.Empty, $"Unable to connect to import list: {ex.Message}. Check the log surrounding this error for details.");
|
||||
return new ValidationFailure(string.Empty, _localizationService.GetLocalizedString("ImportListsValidationUnableToConnectException", new Dictionary<string, object> { { "exceptionMessage", ex.Message } }));
|
||||
}
|
||||
|
||||
return null;
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
using System.Collections.Generic;
|
||||
using NLog;
|
||||
using NzbDrone.Common.Http;
|
||||
using NzbDrone.Core.Configuration;
|
||||
using NzbDrone.Core.Localization;
|
||||
using NzbDrone.Core.Parser;
|
||||
|
||||
namespace NzbDrone.Core.ImportLists.Trakt.List
|
||||
@@ -12,12 +14,13 @@ namespace NzbDrone.Core.ImportLists.Trakt.List
|
||||
IImportListStatusService netImportStatusService,
|
||||
IConfigService configService,
|
||||
IParsingService parsingService,
|
||||
ILocalizationService localizationService,
|
||||
Logger logger)
|
||||
: base(netImportRepository, httpClient, netImportStatusService, configService, parsingService, logger)
|
||||
: base(netImportRepository, httpClient, netImportStatusService, configService, parsingService, localizationService, logger)
|
||||
{
|
||||
}
|
||||
|
||||
public override string Name => "Trakt List";
|
||||
public override string Name => _localizationService.GetLocalizedString("TypeOfList", new Dictionary<string, object> { { "typeOfList", "Trakt" } });
|
||||
|
||||
public override IImportListRequestGenerator GetRequestGenerator()
|
||||
{
|
||||
|
||||
@@ -17,10 +17,10 @@ namespace NzbDrone.Core.ImportLists.Trakt.List
|
||||
{
|
||||
protected override AbstractValidator<TraktListSettings> Validator => new TraktListSettingsValidator();
|
||||
|
||||
[FieldDefinition(1, Label = "Username", HelpText = "Username for the List to import from")]
|
||||
[FieldDefinition(1, Label = "Username", HelpText = "ImportListsTraktSettingsUsernameHelpText")]
|
||||
public string Username { get; set; }
|
||||
|
||||
[FieldDefinition(2, Label = "List Name", HelpText = "List name for import, list must be public or you must have access to the list")]
|
||||
[FieldDefinition(2, Label = "ImportListsTraktSettingsListName", HelpText = "ImportListsTraktSettingsListNameHelpText")]
|
||||
public string Listname { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using NLog;
|
||||
using NzbDrone.Common.Http;
|
||||
using NzbDrone.Core.Configuration;
|
||||
using NzbDrone.Core.Localization;
|
||||
using NzbDrone.Core.Parser;
|
||||
|
||||
namespace NzbDrone.Core.ImportLists.Trakt.Popular
|
||||
@@ -12,12 +13,13 @@ namespace NzbDrone.Core.ImportLists.Trakt.Popular
|
||||
IImportListStatusService netImportStatusService,
|
||||
IConfigService configService,
|
||||
IParsingService parsingService,
|
||||
ILocalizationService localizationService,
|
||||
Logger logger)
|
||||
: base(netImportRepository, httpClient, netImportStatusService, configService, parsingService, logger)
|
||||
: base(netImportRepository, httpClient, netImportStatusService, configService, parsingService, localizationService, logger)
|
||||
{
|
||||
}
|
||||
|
||||
public override string Name => "Trakt Popular List";
|
||||
public override string Name => _localizationService.GetLocalizedString("ImportListsTraktSettingsPopularName");
|
||||
|
||||
public override IParseImportListResponse GetParser()
|
||||
{
|
||||
|
||||
@@ -1,31 +1,30 @@
|
||||
using System.Runtime.Serialization;
|
||||
using NzbDrone.Core.Annotations;
|
||||
|
||||
namespace NzbDrone.Core.ImportLists.Trakt.Popular
|
||||
{
|
||||
public enum TraktPopularListType
|
||||
{
|
||||
[EnumMember(Value = "Trending Shows")]
|
||||
[FieldOption(Label = "ImportListsTraktSettingsPopularListTypeTrendingShows")]
|
||||
Trending = 0,
|
||||
[EnumMember(Value = "Popular Shows")]
|
||||
[FieldOption(Label = "ImportListsTraktSettingsPopularListTypePopularShows")]
|
||||
Popular = 1,
|
||||
[EnumMember(Value = "Anticipated Shows")]
|
||||
[FieldOption(Label = "ImportListsTraktSettingsPopularListTypeAnticipatedShows")]
|
||||
Anticipated = 2,
|
||||
|
||||
[EnumMember(Value = "Top Watched Shows By Week")]
|
||||
[FieldOption(Label = "ImportListsTraktSettingsPopularListTypeTopWeekShows")]
|
||||
TopWatchedByWeek = 3,
|
||||
[EnumMember(Value = "Top Watched Shows By Month")]
|
||||
[FieldOption(Label = "ImportListsTraktSettingsPopularListTypeTopMonthShows")]
|
||||
TopWatchedByMonth = 4,
|
||||
[EnumMember(Value = "Top Watched Shows By Year")]
|
||||
[FieldOption(Label = "ImportListsTraktSettingsPopularListTypeTopYearShows")]
|
||||
TopWatchedByYear = 5,
|
||||
[EnumMember(Value = "Top Watched Shows Of All Time")]
|
||||
[FieldOption(Label = "ImportListsTraktSettingsPopularListTypeTopAllTimeShows")]
|
||||
TopWatchedByAllTime = 6,
|
||||
[EnumMember(Value = "Recommended Shows By Week")]
|
||||
[FieldOption(Label = "ImportListsTraktSettingsPopularListTypeRecommendedWeekShows")]
|
||||
RecommendedByWeek = 7,
|
||||
[EnumMember(Value = "Recommended Shows By Month")]
|
||||
[FieldOption(Label = "ImportListsTraktSettingsPopularListTypeRecommendedMonthShows")]
|
||||
RecommendedByMonth = 8,
|
||||
[EnumMember(Value = "Recommended Shows By Year")]
|
||||
[FieldOption(Label = "ImportListsTraktSettingsPopularListTypeRecommendedYearShows")]
|
||||
RecommendedByYear = 9,
|
||||
[EnumMember(Value = "Recommended Shows Of All Time")]
|
||||
[FieldOption(Label = "ImportListsTraktSettingsPopularListTypeRecommendedAllTimeShows")]
|
||||
RecommendedByAllTime = 10
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,16 +35,16 @@ namespace NzbDrone.Core.ImportLists.Trakt.Popular
|
||||
TraktListType = (int)TraktPopularListType.Popular;
|
||||
}
|
||||
|
||||
[FieldDefinition(1, Label = "List Type", Type = FieldType.Select, SelectOptions = typeof(TraktPopularListType), HelpText = "Type of list you're seeking to import from")]
|
||||
[FieldDefinition(1, Label = "ImportListsTraktSettingsListType", Type = FieldType.Select, SelectOptions = typeof(TraktPopularListType), HelpText = "ImportListsTraktSettingsListTypeHelpText")]
|
||||
public int TraktListType { get; set; }
|
||||
|
||||
[FieldDefinition(2, Label = "Rating", HelpText = "Filter series by rating range (0-100)")]
|
||||
[FieldDefinition(2, Label = "ImportListsTraktSettingsRating", HelpText = "ImportListsTraktSettingsRatingHelpText")]
|
||||
public string Rating { get; set; }
|
||||
|
||||
[FieldDefinition(4, Label = "Genres", HelpText = "Filter series by Trakt Genre Slug (Comma Separated) Only for Popular Lists")]
|
||||
[FieldDefinition(4, Label = "ImportListsTraktSettingsGenres", HelpText = "ImportListsTraktSettingsGenresHelpText")]
|
||||
public string Genres { get; set; }
|
||||
|
||||
[FieldDefinition(5, Label = "Years", HelpText = "Filter series by year or year range")]
|
||||
[FieldDefinition(5, Label = "ImportListsTraktSettingsYears", HelpText = "ImportListsTraktSettingsYearsHelpText")]
|
||||
public string Years { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ using NLog;
|
||||
using NzbDrone.Common.Extensions;
|
||||
using NzbDrone.Common.Http;
|
||||
using NzbDrone.Core.Configuration;
|
||||
using NzbDrone.Core.Localization;
|
||||
using NzbDrone.Core.Parser;
|
||||
using NzbDrone.Core.Parser.Model;
|
||||
using NzbDrone.Core.Validation;
|
||||
@@ -28,8 +29,9 @@ namespace NzbDrone.Core.ImportLists.Trakt
|
||||
IImportListStatusService importListStatusService,
|
||||
IConfigService configService,
|
||||
IParsingService parsingService,
|
||||
ILocalizationService localizationService,
|
||||
Logger logger)
|
||||
: base(httpClient, importListStatusService, configService, parsingService, logger)
|
||||
: base(httpClient, importListStatusService, configService, parsingService, localizationService, logger)
|
||||
{
|
||||
_importListRepository = netImportRepository;
|
||||
}
|
||||
|
||||
@@ -48,25 +48,25 @@ namespace NzbDrone.Core.ImportLists.Trakt
|
||||
|
||||
public string BaseUrl { get; set; }
|
||||
|
||||
[FieldDefinition(0, Label = "Access Token", Type = FieldType.Textbox, Hidden = HiddenType.Hidden)]
|
||||
[FieldDefinition(0, Label = "ImportListsSettingsAccessToken", Type = FieldType.Textbox, Hidden = HiddenType.Hidden)]
|
||||
public string AccessToken { get; set; }
|
||||
|
||||
[FieldDefinition(0, Label = "Refresh Token", Type = FieldType.Textbox, Hidden = HiddenType.Hidden)]
|
||||
[FieldDefinition(0, Label = "ImportListsSettingsRefreshToken", Type = FieldType.Textbox, Hidden = HiddenType.Hidden)]
|
||||
public string RefreshToken { get; set; }
|
||||
|
||||
[FieldDefinition(0, Label = "Expires", Type = FieldType.Textbox, Hidden = HiddenType.Hidden)]
|
||||
[FieldDefinition(0, Label = "ImportListsSettingsExpires", Type = FieldType.Textbox, Hidden = HiddenType.Hidden)]
|
||||
public DateTime Expires { get; set; }
|
||||
|
||||
[FieldDefinition(0, Label = "Auth User", Type = FieldType.Textbox, Hidden = HiddenType.Hidden)]
|
||||
[FieldDefinition(0, Label = "ImportListsSettingsAuthUser", Type = FieldType.Textbox, Hidden = HiddenType.Hidden)]
|
||||
public string AuthUser { get; set; }
|
||||
|
||||
[FieldDefinition(5, Label = "Limit", HelpText = "Limit the number of series to get")]
|
||||
[FieldDefinition(5, Label = "ImportListsTraktSettingsLimit", HelpText = "ImportListsTraktSettingsLimitHelpText")]
|
||||
public int Limit { get; set; }
|
||||
|
||||
[FieldDefinition(6, Label = "Additional Parameters", HelpText = "Additional Trakt API parameters", Advanced = true)]
|
||||
[FieldDefinition(6, Label = "ImportListsTraktSettingsAdditionalParameters", HelpText = "ImportListsTraktSettingsAdditionalParametersHelpText", Advanced = true)]
|
||||
public string TraktAdditionalParameters { get; set; }
|
||||
|
||||
[FieldDefinition(99, Label = "Authenticate with Trakt", Type = FieldType.OAuth)]
|
||||
[FieldDefinition(99, Label = "ImportListsTraktSettingsAuthenticateWithTrakt", Type = FieldType.OAuth)]
|
||||
public string SignIn { get; set; }
|
||||
|
||||
public NzbDroneValidationResult Validate()
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using NLog;
|
||||
using NzbDrone.Common.Http;
|
||||
using NzbDrone.Core.Configuration;
|
||||
using NzbDrone.Core.Localization;
|
||||
using NzbDrone.Core.Parser;
|
||||
|
||||
namespace NzbDrone.Core.ImportLists.Trakt.User
|
||||
@@ -12,12 +13,13 @@ namespace NzbDrone.Core.ImportLists.Trakt.User
|
||||
IImportListStatusService netImportStatusService,
|
||||
IConfigService configService,
|
||||
IParsingService parsingService,
|
||||
ILocalizationService localizationService,
|
||||
Logger logger)
|
||||
: base(netImportRepository, httpClient, netImportStatusService, configService, parsingService, logger)
|
||||
: base(netImportRepository, httpClient, netImportStatusService, configService, parsingService, localizationService, logger)
|
||||
{
|
||||
}
|
||||
|
||||
public override string Name => "Trakt User";
|
||||
public override string Name => _localizationService.GetLocalizedString("ImportListsTraktSettingsUserListName");
|
||||
|
||||
public override IParseImportListResponse GetParser()
|
||||
{
|
||||
|
||||
@@ -4,11 +4,11 @@ namespace NzbDrone.Core.ImportLists.Trakt.User
|
||||
{
|
||||
public enum TraktUserListType
|
||||
{
|
||||
[FieldOption(Label = "User Watch List")]
|
||||
[FieldOption(Label = "ImportListsTraktSettingsUserListTypeWatch")]
|
||||
UserWatchList = 0,
|
||||
[FieldOption(Label = "User Watched List")]
|
||||
[FieldOption(Label = "ImportListsTraktSettingsUserListTypeWatched")]
|
||||
UserWatchedList = 1,
|
||||
[FieldOption(Label = "User Collection List")]
|
||||
[FieldOption(Label = "ImportListsTraktSettingsUserListTypeCollection")]
|
||||
UserCollectionList = 2
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,16 +25,16 @@ namespace NzbDrone.Core.ImportLists.Trakt.User
|
||||
TraktWatchSorting = (int)TraktUserWatchSorting.Rank;
|
||||
}
|
||||
|
||||
[FieldDefinition(1, Label = "List Type", Type = FieldType.Select, SelectOptions = typeof(TraktUserListType), HelpText = "Type of list you're seeking to import from")]
|
||||
[FieldDefinition(1, Label = "ImportListsTraktSettingsListType", Type = FieldType.Select, SelectOptions = typeof(TraktUserListType), HelpText = "ImportListsTraktSettingsListTypeHelpText")]
|
||||
public int TraktListType { get; set; }
|
||||
|
||||
[FieldDefinition(2, Label = "Watched List Filter", Type = FieldType.Select, SelectOptions = typeof(TraktUserWatchedListType), HelpText = "If List Type is Watched. Series do you want to import from")]
|
||||
[FieldDefinition(2, Label = "ImportListsTraktSettingsWatchedListFilter", Type = FieldType.Select, SelectOptions = typeof(TraktUserWatchedListType), HelpText = "ImportListsTraktSettingsWatchedListFilterHelpText")]
|
||||
public int TraktWatchedListType { get; set; }
|
||||
|
||||
[FieldDefinition(3, Label = "Watch List Sorting", Type = FieldType.Select, SelectOptions = typeof(TraktUserWatchSorting), HelpText = "If List Type is Watch")]
|
||||
[FieldDefinition(3, Label = "ImportListsTraktSettingsWatchedListSorting", Type = FieldType.Select, SelectOptions = typeof(TraktUserWatchSorting), HelpText = "ImportListsTraktSettingsWatchedListSortingHelpText")]
|
||||
public int TraktWatchSorting { get; set; }
|
||||
|
||||
[FieldDefinition(4, Label = "Username", HelpText = "Username for the List to import from (empty to use Auth User)")]
|
||||
[FieldDefinition(4, Label = "Username", HelpText = "ImportListsTraktSettingsUserListUsernameHelpText")]
|
||||
public string Username { get; set; }
|
||||
}
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user