New: Optionally remove from queue by changing category to 'Post-Import Category' when configured
Closes #6023
This commit is contained in:
parent
31baed4b2c
commit
345854d0fe
|
@ -25,7 +25,7 @@ import toggleSelected from 'Utilities/Table/toggleSelected';
|
||||||
import QueueFilterModal from './QueueFilterModal';
|
import QueueFilterModal from './QueueFilterModal';
|
||||||
import QueueOptionsConnector from './QueueOptionsConnector';
|
import QueueOptionsConnector from './QueueOptionsConnector';
|
||||||
import QueueRowConnector from './QueueRowConnector';
|
import QueueRowConnector from './QueueRowConnector';
|
||||||
import RemoveQueueItemsModal from './RemoveQueueItemsModal';
|
import RemoveQueueItemModal from './RemoveQueueItemModal';
|
||||||
|
|
||||||
class Queue extends Component {
|
class Queue extends Component {
|
||||||
|
|
||||||
|
@ -305,9 +305,16 @@ class Queue extends Component {
|
||||||
}
|
}
|
||||||
</PageContentBody>
|
</PageContentBody>
|
||||||
|
|
||||||
<RemoveQueueItemsModal
|
<RemoveQueueItemModal
|
||||||
isOpen={isConfirmRemoveModalOpen}
|
isOpen={isConfirmRemoveModalOpen}
|
||||||
selectedCount={selectedCount}
|
selectedCount={selectedCount}
|
||||||
|
canChangeCategory={isConfirmRemoveModalOpen && (
|
||||||
|
selectedIds.every((id) => {
|
||||||
|
const item = items.find((i) => i.id === id);
|
||||||
|
|
||||||
|
return !!(item && item.downloadClientHasPostImportCategory);
|
||||||
|
})
|
||||||
|
)}
|
||||||
canIgnore={isConfirmRemoveModalOpen && (
|
canIgnore={isConfirmRemoveModalOpen && (
|
||||||
selectedIds.every((id) => {
|
selectedIds.every((id) => {
|
||||||
const item = items.find((i) => i.id === id);
|
const item = items.find((i) => i.id === id);
|
||||||
|
@ -315,7 +322,7 @@ class Queue extends Component {
|
||||||
return !!(item && item.seriesId && item.episodeId);
|
return !!(item && item.seriesId && item.episodeId);
|
||||||
})
|
})
|
||||||
)}
|
)}
|
||||||
allPending={isConfirmRemoveModalOpen && (
|
pending={isConfirmRemoveModalOpen && (
|
||||||
selectedIds.every((id) => {
|
selectedIds.every((id) => {
|
||||||
const item = items.find((i) => i.id === id);
|
const item = items.find((i) => i.id === id);
|
||||||
|
|
||||||
|
|
|
@ -99,6 +99,7 @@ class QueueRow extends Component {
|
||||||
indexer,
|
indexer,
|
||||||
outputPath,
|
outputPath,
|
||||||
downloadClient,
|
downloadClient,
|
||||||
|
downloadClientHasPostImportCategory,
|
||||||
estimatedCompletionTime,
|
estimatedCompletionTime,
|
||||||
added,
|
added,
|
||||||
timeleft,
|
timeleft,
|
||||||
|
@ -420,6 +421,7 @@ class QueueRow extends Component {
|
||||||
<RemoveQueueItemModal
|
<RemoveQueueItemModal
|
||||||
isOpen={isRemoveQueueItemModalOpen}
|
isOpen={isRemoveQueueItemModalOpen}
|
||||||
sourceTitle={title}
|
sourceTitle={title}
|
||||||
|
canChangeCategory={!!downloadClientHasPostImportCategory}
|
||||||
canIgnore={!!series}
|
canIgnore={!!series}
|
||||||
isPending={isPending}
|
isPending={isPending}
|
||||||
onRemovePress={this.onRemoveQueueItemModalConfirmed}
|
onRemovePress={this.onRemoveQueueItemModalConfirmed}
|
||||||
|
@ -450,6 +452,7 @@ QueueRow.propTypes = {
|
||||||
indexer: PropTypes.string,
|
indexer: PropTypes.string,
|
||||||
outputPath: PropTypes.string,
|
outputPath: PropTypes.string,
|
||||||
downloadClient: PropTypes.string,
|
downloadClient: PropTypes.string,
|
||||||
|
downloadClientHasPostImportCategory: PropTypes.bool,
|
||||||
estimatedCompletionTime: PropTypes.string,
|
estimatedCompletionTime: PropTypes.string,
|
||||||
added: PropTypes.string,
|
added: PropTypes.string,
|
||||||
timeleft: PropTypes.string,
|
timeleft: PropTypes.string,
|
||||||
|
|
|
@ -1,171 +0,0 @@
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
import React, { Component } from 'react';
|
|
||||||
import FormGroup from 'Components/Form/FormGroup';
|
|
||||||
import FormInputGroup from 'Components/Form/FormInputGroup';
|
|
||||||
import FormLabel from 'Components/Form/FormLabel';
|
|
||||||
import Button from 'Components/Link/Button';
|
|
||||||
import Modal from 'Components/Modal/Modal';
|
|
||||||
import ModalBody from 'Components/Modal/ModalBody';
|
|
||||||
import ModalContent from 'Components/Modal/ModalContent';
|
|
||||||
import ModalFooter from 'Components/Modal/ModalFooter';
|
|
||||||
import ModalHeader from 'Components/Modal/ModalHeader';
|
|
||||||
import { inputTypes, kinds, sizes } from 'Helpers/Props';
|
|
||||||
import translate from 'Utilities/String/translate';
|
|
||||||
|
|
||||||
class RemoveQueueItemModal extends Component {
|
|
||||||
|
|
||||||
//
|
|
||||||
// Lifecycle
|
|
||||||
|
|
||||||
constructor(props, context) {
|
|
||||||
super(props, context);
|
|
||||||
|
|
||||||
this.state = {
|
|
||||||
remove: true,
|
|
||||||
blocklist: false,
|
|
||||||
skipRedownload: false
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
// Control
|
|
||||||
|
|
||||||
resetState = function() {
|
|
||||||
this.setState({
|
|
||||||
remove: true,
|
|
||||||
blocklist: false,
|
|
||||||
skipRedownload: false
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
//
|
|
||||||
// Listeners
|
|
||||||
|
|
||||||
onRemoveChange = ({ value }) => {
|
|
||||||
this.setState({ remove: value });
|
|
||||||
};
|
|
||||||
|
|
||||||
onBlocklistChange = ({ value }) => {
|
|
||||||
this.setState({ blocklist: value });
|
|
||||||
};
|
|
||||||
|
|
||||||
onSkipRedownloadChange = ({ value }) => {
|
|
||||||
this.setState({ skipRedownload: value });
|
|
||||||
};
|
|
||||||
|
|
||||||
onRemoveConfirmed = () => {
|
|
||||||
const state = this.state;
|
|
||||||
|
|
||||||
this.resetState();
|
|
||||||
this.props.onRemovePress(state);
|
|
||||||
};
|
|
||||||
|
|
||||||
onModalClose = () => {
|
|
||||||
this.resetState();
|
|
||||||
this.props.onModalClose();
|
|
||||||
};
|
|
||||||
|
|
||||||
//
|
|
||||||
// Render
|
|
||||||
|
|
||||||
render() {
|
|
||||||
const {
|
|
||||||
isOpen,
|
|
||||||
sourceTitle,
|
|
||||||
canIgnore,
|
|
||||||
isPending
|
|
||||||
} = this.props;
|
|
||||||
|
|
||||||
const { remove, blocklist, skipRedownload } = this.state;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Modal
|
|
||||||
isOpen={isOpen}
|
|
||||||
size={sizes.MEDIUM}
|
|
||||||
onModalClose={this.onModalClose}
|
|
||||||
>
|
|
||||||
<ModalContent
|
|
||||||
onModalClose={this.onModalClose}
|
|
||||||
>
|
|
||||||
<ModalHeader>
|
|
||||||
{translate('RemoveQueueItem', { sourceTitle })}
|
|
||||||
</ModalHeader>
|
|
||||||
|
|
||||||
<ModalBody>
|
|
||||||
<div>
|
|
||||||
{translate('RemoveQueueItemConfirmation', { sourceTitle })}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{
|
|
||||||
isPending ?
|
|
||||||
null :
|
|
||||||
<FormGroup>
|
|
||||||
<FormLabel>{translate('RemoveFromDownloadClient')}</FormLabel>
|
|
||||||
|
|
||||||
<FormInputGroup
|
|
||||||
type={inputTypes.CHECK}
|
|
||||||
name="remove"
|
|
||||||
value={remove}
|
|
||||||
helpTextWarning={translate('RemoveFromDownloadClientHelpTextWarning')}
|
|
||||||
isDisabled={!canIgnore}
|
|
||||||
onChange={this.onRemoveChange}
|
|
||||||
/>
|
|
||||||
</FormGroup>
|
|
||||||
}
|
|
||||||
|
|
||||||
<FormGroup>
|
|
||||||
<FormLabel>{translate('BlocklistRelease')}</FormLabel>
|
|
||||||
|
|
||||||
<FormInputGroup
|
|
||||||
type={inputTypes.CHECK}
|
|
||||||
name="blocklist"
|
|
||||||
value={blocklist}
|
|
||||||
helpText={translate('BlocklistReleaseSearchEpisodeAgainHelpText')}
|
|
||||||
onChange={this.onBlocklistChange}
|
|
||||||
/>
|
|
||||||
</FormGroup>
|
|
||||||
|
|
||||||
{
|
|
||||||
blocklist ?
|
|
||||||
<FormGroup>
|
|
||||||
<FormLabel>{translate('SkipRedownload')}</FormLabel>
|
|
||||||
<FormInputGroup
|
|
||||||
type={inputTypes.CHECK}
|
|
||||||
name="skipRedownload"
|
|
||||||
value={skipRedownload}
|
|
||||||
helpText={translate('SkipRedownloadHelpText')}
|
|
||||||
onChange={this.onSkipRedownloadChange}
|
|
||||||
/>
|
|
||||||
</FormGroup> :
|
|
||||||
null
|
|
||||||
}
|
|
||||||
</ModalBody>
|
|
||||||
|
|
||||||
<ModalFooter>
|
|
||||||
<Button onPress={this.onModalClose}>
|
|
||||||
{translate('Close')}
|
|
||||||
</Button>
|
|
||||||
|
|
||||||
<Button
|
|
||||||
kind={kinds.DANGER}
|
|
||||||
onPress={this.onRemoveConfirmed}
|
|
||||||
>
|
|
||||||
{translate('Remove')}
|
|
||||||
</Button>
|
|
||||||
</ModalFooter>
|
|
||||||
</ModalContent>
|
|
||||||
</Modal>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
RemoveQueueItemModal.propTypes = {
|
|
||||||
isOpen: PropTypes.bool.isRequired,
|
|
||||||
sourceTitle: PropTypes.string.isRequired,
|
|
||||||
canIgnore: PropTypes.bool.isRequired,
|
|
||||||
isPending: PropTypes.bool.isRequired,
|
|
||||||
onRemovePress: PropTypes.func.isRequired,
|
|
||||||
onModalClose: PropTypes.func.isRequired
|
|
||||||
};
|
|
||||||
|
|
||||||
export default RemoveQueueItemModal;
|
|
|
@ -0,0 +1,230 @@
|
||||||
|
import React, { useCallback, useMemo, useState } from 'react';
|
||||||
|
import FormGroup from 'Components/Form/FormGroup';
|
||||||
|
import FormInputGroup from 'Components/Form/FormInputGroup';
|
||||||
|
import FormLabel from 'Components/Form/FormLabel';
|
||||||
|
import Button from 'Components/Link/Button';
|
||||||
|
import Modal from 'Components/Modal/Modal';
|
||||||
|
import ModalBody from 'Components/Modal/ModalBody';
|
||||||
|
import ModalContent from 'Components/Modal/ModalContent';
|
||||||
|
import ModalFooter from 'Components/Modal/ModalFooter';
|
||||||
|
import ModalHeader from 'Components/Modal/ModalHeader';
|
||||||
|
import { inputTypes, kinds, sizes } from 'Helpers/Props';
|
||||||
|
import translate from 'Utilities/String/translate';
|
||||||
|
import styles from './RemoveQueueItemModal.css';
|
||||||
|
|
||||||
|
interface RemovePressProps {
|
||||||
|
remove: boolean;
|
||||||
|
changeCategory: boolean;
|
||||||
|
blocklist: boolean;
|
||||||
|
skipRedownload: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface RemoveQueueItemModalProps {
|
||||||
|
isOpen: boolean;
|
||||||
|
sourceTitle: string;
|
||||||
|
canChangeCategory: boolean;
|
||||||
|
canIgnore: boolean;
|
||||||
|
isPending: boolean;
|
||||||
|
selectedCount?: number;
|
||||||
|
onRemovePress(props: RemovePressProps): void;
|
||||||
|
onModalClose: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
type RemovalMethod = 'removeFromClient' | 'changeCategory' | 'ignore';
|
||||||
|
type BlocklistMethod =
|
||||||
|
| 'doNotBlocklist'
|
||||||
|
| 'blocklistAndSearch'
|
||||||
|
| 'blocklistOnly';
|
||||||
|
|
||||||
|
function RemoveQueueItemModal(props: RemoveQueueItemModalProps) {
|
||||||
|
const {
|
||||||
|
isOpen,
|
||||||
|
sourceTitle,
|
||||||
|
canIgnore,
|
||||||
|
canChangeCategory,
|
||||||
|
isPending,
|
||||||
|
selectedCount,
|
||||||
|
onRemovePress,
|
||||||
|
onModalClose,
|
||||||
|
} = props;
|
||||||
|
|
||||||
|
const multipleSelected = selectedCount && selectedCount > 1;
|
||||||
|
|
||||||
|
const [removalMethod, setRemovalMethod] =
|
||||||
|
useState<RemovalMethod>('removeFromClient');
|
||||||
|
const [blocklistMethod, setBlocklistMethod] =
|
||||||
|
useState<BlocklistMethod>('doNotBlocklist');
|
||||||
|
|
||||||
|
const { title, message } = useMemo(() => {
|
||||||
|
if (!selectedCount) {
|
||||||
|
return {
|
||||||
|
title: translate('RemoveQueueItem', { sourceTitle }),
|
||||||
|
message: translate('RemoveQueueItemConfirmation', { sourceTitle }),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (selectedCount === 1) {
|
||||||
|
return {
|
||||||
|
title: translate('RemoveSelectedItem'),
|
||||||
|
message: translate('RemoveSelectedItemQueueMessageText'),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
title: translate('RemoveSelectedItems'),
|
||||||
|
message: translate('RemoveSelectedItemsQueueMessageText', {
|
||||||
|
selectedCount,
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
}, [sourceTitle, selectedCount]);
|
||||||
|
|
||||||
|
const removalMethodOptions = useMemo(() => {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
key: 'removeFromClient',
|
||||||
|
value: translate('RemoveFromDownloadClient'),
|
||||||
|
hint: multipleSelected
|
||||||
|
? translate('RemoveMultipleFromDownloadClientHint')
|
||||||
|
: translate('RemoveFromDownloadClientHint'),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'changeCategory',
|
||||||
|
value: translate('ChangeCategory'),
|
||||||
|
isDisabled: !canChangeCategory,
|
||||||
|
hint: multipleSelected
|
||||||
|
? translate('ChangeCategoryMultipleHint')
|
||||||
|
: translate('ChangeCategoryHint'),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'ignore',
|
||||||
|
value: multipleSelected
|
||||||
|
? translate('IgnoreDownloads')
|
||||||
|
: translate('IgnoreDownload'),
|
||||||
|
isDisabled: !canIgnore,
|
||||||
|
hint: multipleSelected
|
||||||
|
? translate('IgnoreDownloadsHint')
|
||||||
|
: translate('IgnoreDownloadHint'),
|
||||||
|
},
|
||||||
|
];
|
||||||
|
}, [canChangeCategory, canIgnore, multipleSelected]);
|
||||||
|
|
||||||
|
const blocklistMethodOptions = useMemo(() => {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
key: 'doNotBlocklist',
|
||||||
|
value: translate('DoNotBlocklist'),
|
||||||
|
hint: translate('DoNotBlocklistHint'),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'blocklistAndSearch',
|
||||||
|
value: translate('BlocklistAndSearch'),
|
||||||
|
hint: multipleSelected
|
||||||
|
? translate('BlocklistAndSearchMultipleHint')
|
||||||
|
: translate('BlocklistAndSearchHint'),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'blocklistOnly',
|
||||||
|
value: translate('BlocklistOnly'),
|
||||||
|
hint: multipleSelected
|
||||||
|
? translate('BlocklistMultipleOnlyHint')
|
||||||
|
: translate('BlocklistOnlyHint'),
|
||||||
|
},
|
||||||
|
];
|
||||||
|
}, [multipleSelected]);
|
||||||
|
|
||||||
|
const handleRemovalMethodChange = useCallback(
|
||||||
|
({ value }: { value: RemovalMethod }) => {
|
||||||
|
setRemovalMethod(value);
|
||||||
|
},
|
||||||
|
[setRemovalMethod]
|
||||||
|
);
|
||||||
|
|
||||||
|
const handleBlocklistMethodChange = useCallback(
|
||||||
|
({ value }: { value: BlocklistMethod }) => {
|
||||||
|
setBlocklistMethod(value);
|
||||||
|
},
|
||||||
|
[setBlocklistMethod]
|
||||||
|
);
|
||||||
|
|
||||||
|
const handleConfirmRemove = useCallback(() => {
|
||||||
|
onRemovePress({
|
||||||
|
remove: removalMethod === 'removeFromClient',
|
||||||
|
changeCategory: removalMethod === 'changeCategory',
|
||||||
|
blocklist: blocklistMethod !== 'doNotBlocklist',
|
||||||
|
skipRedownload: blocklistMethod === 'blocklistOnly',
|
||||||
|
});
|
||||||
|
|
||||||
|
setRemovalMethod('removeFromClient');
|
||||||
|
setBlocklistMethod('doNotBlocklist');
|
||||||
|
}, [
|
||||||
|
removalMethod,
|
||||||
|
blocklistMethod,
|
||||||
|
setRemovalMethod,
|
||||||
|
setBlocklistMethod,
|
||||||
|
onRemovePress,
|
||||||
|
]);
|
||||||
|
|
||||||
|
const handleModalClose = useCallback(() => {
|
||||||
|
setRemovalMethod('removeFromClient');
|
||||||
|
setBlocklistMethod('doNotBlocklist');
|
||||||
|
|
||||||
|
onModalClose();
|
||||||
|
}, [setRemovalMethod, setBlocklistMethod, onModalClose]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Modal isOpen={isOpen} size={sizes.MEDIUM} onModalClose={handleModalClose}>
|
||||||
|
<ModalContent onModalClose={handleModalClose}>
|
||||||
|
<ModalHeader>{title}</ModalHeader>
|
||||||
|
|
||||||
|
<ModalBody>
|
||||||
|
<div className={styles.message}>{message}</div>
|
||||||
|
|
||||||
|
{isPending ? null : (
|
||||||
|
<FormGroup>
|
||||||
|
<FormLabel>{translate('RemoveQueueItemRemovalMethod')}</FormLabel>
|
||||||
|
|
||||||
|
<FormInputGroup
|
||||||
|
type={inputTypes.SELECT}
|
||||||
|
name="removalMethod"
|
||||||
|
value={removalMethod}
|
||||||
|
values={removalMethodOptions}
|
||||||
|
isDisabled={!canChangeCategory && !canIgnore}
|
||||||
|
helpTextWarning={translate(
|
||||||
|
'RemoveQueueItemRemovalMethodHelpTextWarning'
|
||||||
|
)}
|
||||||
|
onChange={handleRemovalMethodChange}
|
||||||
|
/>
|
||||||
|
</FormGroup>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<FormGroup>
|
||||||
|
<FormLabel>
|
||||||
|
{multipleSelected
|
||||||
|
? translate('BlocklistReleases')
|
||||||
|
: translate('BlocklistRelease')}
|
||||||
|
</FormLabel>
|
||||||
|
|
||||||
|
<FormInputGroup
|
||||||
|
type={inputTypes.SELECT}
|
||||||
|
name="blocklistMethod"
|
||||||
|
value={blocklistMethod}
|
||||||
|
values={blocklistMethodOptions}
|
||||||
|
helpText={translate('BlocklistReleaseHelpText')}
|
||||||
|
onChange={handleBlocklistMethodChange}
|
||||||
|
/>
|
||||||
|
</FormGroup>
|
||||||
|
</ModalBody>
|
||||||
|
|
||||||
|
<ModalFooter>
|
||||||
|
<Button onPress={handleModalClose}>{translate('Close')}</Button>
|
||||||
|
|
||||||
|
<Button kind={kinds.DANGER} onPress={handleConfirmRemove}>
|
||||||
|
{translate('Remove')}
|
||||||
|
</Button>
|
||||||
|
</ModalFooter>
|
||||||
|
</ModalContent>
|
||||||
|
</Modal>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default RemoveQueueItemModal;
|
|
@ -1,174 +0,0 @@
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
import React, { Component } from 'react';
|
|
||||||
import FormGroup from 'Components/Form/FormGroup';
|
|
||||||
import FormInputGroup from 'Components/Form/FormInputGroup';
|
|
||||||
import FormLabel from 'Components/Form/FormLabel';
|
|
||||||
import Button from 'Components/Link/Button';
|
|
||||||
import Modal from 'Components/Modal/Modal';
|
|
||||||
import ModalBody from 'Components/Modal/ModalBody';
|
|
||||||
import ModalContent from 'Components/Modal/ModalContent';
|
|
||||||
import ModalFooter from 'Components/Modal/ModalFooter';
|
|
||||||
import ModalHeader from 'Components/Modal/ModalHeader';
|
|
||||||
import { inputTypes, kinds, sizes } from 'Helpers/Props';
|
|
||||||
import translate from 'Utilities/String/translate';
|
|
||||||
import styles from './RemoveQueueItemsModal.css';
|
|
||||||
|
|
||||||
class RemoveQueueItemsModal extends Component {
|
|
||||||
|
|
||||||
//
|
|
||||||
// Lifecycle
|
|
||||||
|
|
||||||
constructor(props, context) {
|
|
||||||
super(props, context);
|
|
||||||
|
|
||||||
this.state = {
|
|
||||||
remove: true,
|
|
||||||
blocklist: false,
|
|
||||||
skipRedownload: false
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
// Control
|
|
||||||
|
|
||||||
resetState = function() {
|
|
||||||
this.setState({
|
|
||||||
remove: true,
|
|
||||||
blocklist: false,
|
|
||||||
skipRedownload: false
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
//
|
|
||||||
// Listeners
|
|
||||||
|
|
||||||
onRemoveChange = ({ value }) => {
|
|
||||||
this.setState({ remove: value });
|
|
||||||
};
|
|
||||||
|
|
||||||
onBlocklistChange = ({ value }) => {
|
|
||||||
this.setState({ blocklist: value });
|
|
||||||
};
|
|
||||||
|
|
||||||
onSkipRedownloadChange = ({ value }) => {
|
|
||||||
this.setState({ skipRedownload: value });
|
|
||||||
};
|
|
||||||
|
|
||||||
onRemoveConfirmed = () => {
|
|
||||||
const state = this.state;
|
|
||||||
|
|
||||||
this.resetState();
|
|
||||||
this.props.onRemovePress(state);
|
|
||||||
};
|
|
||||||
|
|
||||||
onModalClose = () => {
|
|
||||||
this.resetState();
|
|
||||||
this.props.onModalClose();
|
|
||||||
};
|
|
||||||
|
|
||||||
//
|
|
||||||
// Render
|
|
||||||
|
|
||||||
render() {
|
|
||||||
const {
|
|
||||||
isOpen,
|
|
||||||
selectedCount,
|
|
||||||
canIgnore,
|
|
||||||
allPending
|
|
||||||
} = this.props;
|
|
||||||
|
|
||||||
const { remove, blocklist, skipRedownload } = this.state;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Modal
|
|
||||||
isOpen={isOpen}
|
|
||||||
size={sizes.MEDIUM}
|
|
||||||
onModalClose={this.onModalClose}
|
|
||||||
>
|
|
||||||
<ModalContent
|
|
||||||
onModalClose={this.onModalClose}
|
|
||||||
>
|
|
||||||
<ModalHeader>
|
|
||||||
{selectedCount > 1 ? translate('RemoveSelectedItems') : translate('RemoveSelectedItem')}
|
|
||||||
</ModalHeader>
|
|
||||||
|
|
||||||
<ModalBody>
|
|
||||||
<div className={styles.message}>
|
|
||||||
{selectedCount > 1 ? translate('RemoveSelectedItemsQueueMessageText', { selectedCount }) : translate('RemoveSelectedItemQueueMessageText')}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{
|
|
||||||
allPending ?
|
|
||||||
null :
|
|
||||||
<FormGroup>
|
|
||||||
<FormLabel>{translate('RemoveFromDownloadClient')}</FormLabel>
|
|
||||||
|
|
||||||
<FormInputGroup
|
|
||||||
type={inputTypes.CHECK}
|
|
||||||
name="remove"
|
|
||||||
value={remove}
|
|
||||||
helpTextWarning={translate('RemoveFromDownloadClientHelpTextWarning')}
|
|
||||||
isDisabled={!canIgnore}
|
|
||||||
onChange={this.onRemoveChange}
|
|
||||||
/>
|
|
||||||
</FormGroup>
|
|
||||||
}
|
|
||||||
|
|
||||||
<FormGroup>
|
|
||||||
<FormLabel>
|
|
||||||
{selectedCount > 1 ? translate('BlocklistReleases') : translate('BlocklistRelease')}
|
|
||||||
</FormLabel>
|
|
||||||
|
|
||||||
<FormInputGroup
|
|
||||||
type={inputTypes.CHECK}
|
|
||||||
name="blocklist"
|
|
||||||
value={blocklist}
|
|
||||||
helpText={translate('BlocklistReleaseSearchEpisodeAgainHelpText')}
|
|
||||||
onChange={this.onBlocklistChange}
|
|
||||||
/>
|
|
||||||
</FormGroup>
|
|
||||||
|
|
||||||
{
|
|
||||||
blocklist ?
|
|
||||||
<FormGroup>
|
|
||||||
<FormLabel>{translate('SkipRedownload')}</FormLabel>
|
|
||||||
<FormInputGroup
|
|
||||||
type={inputTypes.CHECK}
|
|
||||||
name="skipRedownload"
|
|
||||||
value={skipRedownload}
|
|
||||||
helpText={translate('SkipRedownloadHelpText')}
|
|
||||||
onChange={this.onSkipRedownloadChange}
|
|
||||||
/>
|
|
||||||
</FormGroup> :
|
|
||||||
null
|
|
||||||
}
|
|
||||||
</ModalBody>
|
|
||||||
|
|
||||||
<ModalFooter>
|
|
||||||
<Button onPress={this.onModalClose}>
|
|
||||||
{translate('Close')}
|
|
||||||
</Button>
|
|
||||||
|
|
||||||
<Button
|
|
||||||
kind={kinds.DANGER}
|
|
||||||
onPress={this.onRemoveConfirmed}
|
|
||||||
>
|
|
||||||
{translate('Remove')}
|
|
||||||
</Button>
|
|
||||||
</ModalFooter>
|
|
||||||
</ModalContent>
|
|
||||||
</Modal>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
RemoveQueueItemsModal.propTypes = {
|
|
||||||
isOpen: PropTypes.bool.isRequired,
|
|
||||||
selectedCount: PropTypes.number.isRequired,
|
|
||||||
canIgnore: PropTypes.bool.isRequired,
|
|
||||||
allPending: PropTypes.bool.isRequired,
|
|
||||||
onRemovePress: PropTypes.func.isRequired,
|
|
||||||
onModalClose: PropTypes.func.isRequired
|
|
||||||
};
|
|
||||||
|
|
||||||
export default RemoveQueueItemsModal;
|
|
|
@ -264,6 +264,7 @@ FormInputGroup.propTypes = {
|
||||||
name: PropTypes.string.isRequired,
|
name: PropTypes.string.isRequired,
|
||||||
value: PropTypes.any,
|
value: PropTypes.any,
|
||||||
values: PropTypes.arrayOf(PropTypes.any),
|
values: PropTypes.arrayOf(PropTypes.any),
|
||||||
|
isDisabled: PropTypes.bool,
|
||||||
type: PropTypes.string.isRequired,
|
type: PropTypes.string.isRequired,
|
||||||
kind: PropTypes.oneOf(kinds.all),
|
kind: PropTypes.oneOf(kinds.all),
|
||||||
min: PropTypes.number,
|
min: PropTypes.number,
|
||||||
|
|
|
@ -430,13 +430,14 @@ export const actionHandlers = handleThunks({
|
||||||
id,
|
id,
|
||||||
remove,
|
remove,
|
||||||
blocklist,
|
blocklist,
|
||||||
skipRedownload
|
skipRedownload,
|
||||||
|
changeCategory
|
||||||
} = payload;
|
} = payload;
|
||||||
|
|
||||||
dispatch(updateItem({ section: paged, id, isRemoving: true }));
|
dispatch(updateItem({ section: paged, id, isRemoving: true }));
|
||||||
|
|
||||||
const promise = createAjaxRequest({
|
const promise = createAjaxRequest({
|
||||||
url: `/queue/${id}?removeFromClient=${remove}&blocklist=${blocklist}&skipRedownload=${skipRedownload}`,
|
url: `/queue/${id}?removeFromClient=${remove}&blocklist=${blocklist}&skipRedownload=${skipRedownload}&changeCategory=${changeCategory}`,
|
||||||
method: 'DELETE'
|
method: 'DELETE'
|
||||||
}).request;
|
}).request;
|
||||||
|
|
||||||
|
@ -454,7 +455,8 @@ export const actionHandlers = handleThunks({
|
||||||
ids,
|
ids,
|
||||||
remove,
|
remove,
|
||||||
blocklist,
|
blocklist,
|
||||||
skipRedownload
|
skipRedownload,
|
||||||
|
changeCategory
|
||||||
} = payload;
|
} = payload;
|
||||||
|
|
||||||
dispatch(batchActions([
|
dispatch(batchActions([
|
||||||
|
@ -470,7 +472,7 @@ export const actionHandlers = handleThunks({
|
||||||
]));
|
]));
|
||||||
|
|
||||||
const promise = createAjaxRequest({
|
const promise = createAjaxRequest({
|
||||||
url: `/queue/bulk?removeFromClient=${remove}&blocklist=${blocklist}&skipRedownload=${skipRedownload}`,
|
url: `/queue/bulk?removeFromClient=${remove}&blocklist=${blocklist}&skipRedownload=${skipRedownload}&changeCategory=${changeCategory}`,
|
||||||
method: 'DELETE',
|
method: 'DELETE',
|
||||||
dataType: 'json',
|
dataType: 'json',
|
||||||
contentType: 'application/json',
|
contentType: 'application/json',
|
||||||
|
|
|
@ -134,7 +134,7 @@ namespace NzbDrone.Core.Download.Clients.Aria2
|
||||||
CanMoveFiles = false,
|
CanMoveFiles = false,
|
||||||
CanBeRemoved = torrent.Status == "complete",
|
CanBeRemoved = torrent.Status == "complete",
|
||||||
Category = null,
|
Category = null,
|
||||||
DownloadClientInfo = DownloadClientItemClientInfo.FromDownloadClient(this),
|
DownloadClientInfo = DownloadClientItemClientInfo.FromDownloadClient(this, false),
|
||||||
DownloadId = torrent.InfoHash?.ToUpper(),
|
DownloadId = torrent.InfoHash?.ToUpper(),
|
||||||
IsEncrypted = false,
|
IsEncrypted = false,
|
||||||
Message = torrent.ErrorMessage,
|
Message = torrent.ErrorMessage,
|
||||||
|
|
|
@ -91,7 +91,7 @@ namespace NzbDrone.Core.Download.Clients.Blackhole
|
||||||
{
|
{
|
||||||
yield return new DownloadClientItem
|
yield return new DownloadClientItem
|
||||||
{
|
{
|
||||||
DownloadClientInfo = DownloadClientItemClientInfo.FromDownloadClient(this),
|
DownloadClientInfo = DownloadClientItemClientInfo.FromDownloadClient(this, false),
|
||||||
DownloadId = Definition.Name + "_" + item.DownloadId,
|
DownloadId = Definition.Name + "_" + item.DownloadId,
|
||||||
Category = "sonarr",
|
Category = "sonarr",
|
||||||
Title = item.Title,
|
Title = item.Title,
|
||||||
|
|
|
@ -61,7 +61,7 @@ namespace NzbDrone.Core.Download.Clients.Blackhole
|
||||||
{
|
{
|
||||||
yield return new DownloadClientItem
|
yield return new DownloadClientItem
|
||||||
{
|
{
|
||||||
DownloadClientInfo = DownloadClientItemClientInfo.FromDownloadClient(this),
|
DownloadClientInfo = DownloadClientItemClientInfo.FromDownloadClient(this, false),
|
||||||
DownloadId = Definition.Name + "_" + item.DownloadId,
|
DownloadId = Definition.Name + "_" + item.DownloadId,
|
||||||
Category = "sonarr",
|
Category = "sonarr",
|
||||||
Title = item.Title,
|
Title = item.Title,
|
||||||
|
|
|
@ -137,7 +137,7 @@ namespace NzbDrone.Core.Download.Clients.Deluge
|
||||||
item.Title = torrent.Name;
|
item.Title = torrent.Name;
|
||||||
item.Category = Settings.TvCategory;
|
item.Category = Settings.TvCategory;
|
||||||
|
|
||||||
item.DownloadClientInfo = DownloadClientItemClientInfo.FromDownloadClient(this);
|
item.DownloadClientInfo = DownloadClientItemClientInfo.FromDownloadClient(this, Settings.TvImportedCategory.IsNotNullOrWhiteSpace());
|
||||||
|
|
||||||
var outputPath = _remotePathMappingService.RemapRemoteToLocal(Settings.Host, new OsPath(torrent.DownloadPath));
|
var outputPath = _remotePathMappingService.RemapRemoteToLocal(Settings.Host, new OsPath(torrent.DownloadPath));
|
||||||
item.OutputPath = outputPath + torrent.Name;
|
item.OutputPath = outputPath + torrent.Name;
|
||||||
|
|
|
@ -91,7 +91,7 @@ namespace NzbDrone.Core.Download.Clients.DownloadStation
|
||||||
var item = new DownloadClientItem()
|
var item = new DownloadClientItem()
|
||||||
{
|
{
|
||||||
Category = Settings.TvCategory,
|
Category = Settings.TvCategory,
|
||||||
DownloadClientInfo = DownloadClientItemClientInfo.FromDownloadClient(this),
|
DownloadClientInfo = DownloadClientItemClientInfo.FromDownloadClient(this, false),
|
||||||
DownloadId = CreateDownloadId(torrent.Id, serialNumber),
|
DownloadId = CreateDownloadId(torrent.Id, serialNumber),
|
||||||
Title = torrent.Title,
|
Title = torrent.Title,
|
||||||
TotalSize = torrent.Size,
|
TotalSize = torrent.Size,
|
||||||
|
|
|
@ -99,7 +99,7 @@ namespace NzbDrone.Core.Download.Clients.DownloadStation
|
||||||
var item = new DownloadClientItem()
|
var item = new DownloadClientItem()
|
||||||
{
|
{
|
||||||
Category = Settings.TvCategory,
|
Category = Settings.TvCategory,
|
||||||
DownloadClientInfo = DownloadClientItemClientInfo.FromDownloadClient(this),
|
DownloadClientInfo = DownloadClientItemClientInfo.FromDownloadClient(this, false),
|
||||||
DownloadId = CreateDownloadId(nzb.Id, serialNumber),
|
DownloadId = CreateDownloadId(nzb.Id, serialNumber),
|
||||||
Title = nzb.Title,
|
Title = nzb.Title,
|
||||||
TotalSize = nzb.Size,
|
TotalSize = nzb.Size,
|
||||||
|
|
|
@ -118,7 +118,7 @@ namespace NzbDrone.Core.Download.Clients.Flood
|
||||||
|
|
||||||
var item = new DownloadClientItem
|
var item = new DownloadClientItem
|
||||||
{
|
{
|
||||||
DownloadClientInfo = DownloadClientItemClientInfo.FromDownloadClient(this),
|
DownloadClientInfo = DownloadClientItemClientInfo.FromDownloadClient(this, false),
|
||||||
DownloadId = torrent.Key,
|
DownloadId = torrent.Key,
|
||||||
Title = properties.Name,
|
Title = properties.Name,
|
||||||
OutputPath = _remotePathMappingService.RemapRemoteToLocal(Settings.Host, new OsPath(properties.Directory)),
|
OutputPath = _remotePathMappingService.RemapRemoteToLocal(Settings.Host, new OsPath(properties.Directory)),
|
||||||
|
|
|
@ -75,7 +75,7 @@ namespace NzbDrone.Core.Download.Clients.FreeboxDownload
|
||||||
Category = Settings.Category,
|
Category = Settings.Category,
|
||||||
Title = torrent.Name,
|
Title = torrent.Name,
|
||||||
TotalSize = torrent.Size,
|
TotalSize = torrent.Size,
|
||||||
DownloadClientInfo = DownloadClientItemClientInfo.FromDownloadClient(this),
|
DownloadClientInfo = DownloadClientItemClientInfo.FromDownloadClient(this, false),
|
||||||
RemainingSize = (long)(torrent.Size * (double)(1 - ((double)torrent.ReceivedPrct / 10000))),
|
RemainingSize = (long)(torrent.Size * (double)(1 - ((double)torrent.ReceivedPrct / 10000))),
|
||||||
RemainingTime = torrent.Eta <= 0 ? null : TimeSpan.FromSeconds(torrent.Eta),
|
RemainingTime = torrent.Eta <= 0 ? null : TimeSpan.FromSeconds(torrent.Eta),
|
||||||
SeedRatio = torrent.StopRatio <= 0 ? 0 : torrent.StopRatio / 100,
|
SeedRatio = torrent.StopRatio <= 0 ? 0 : torrent.StopRatio / 100,
|
||||||
|
|
|
@ -59,7 +59,7 @@ namespace NzbDrone.Core.Download.Clients.Hadouken
|
||||||
|
|
||||||
var item = new DownloadClientItem
|
var item = new DownloadClientItem
|
||||||
{
|
{
|
||||||
DownloadClientInfo = DownloadClientItemClientInfo.FromDownloadClient(this),
|
DownloadClientInfo = DownloadClientItemClientInfo.FromDownloadClient(this, false),
|
||||||
DownloadId = torrent.InfoHash.ToUpper(),
|
DownloadId = torrent.InfoHash.ToUpper(),
|
||||||
OutputPath = outputPath + torrent.Name,
|
OutputPath = outputPath + torrent.Name,
|
||||||
RemainingSize = torrent.TotalSize - torrent.DownloadedBytes,
|
RemainingSize = torrent.TotalSize - torrent.DownloadedBytes,
|
||||||
|
|
|
@ -58,7 +58,7 @@ namespace NzbDrone.Core.Download.Clients.NzbVortex
|
||||||
{
|
{
|
||||||
var queueItem = new DownloadClientItem();
|
var queueItem = new DownloadClientItem();
|
||||||
|
|
||||||
queueItem.DownloadClientInfo = DownloadClientItemClientInfo.FromDownloadClient(this);
|
queueItem.DownloadClientInfo = DownloadClientItemClientInfo.FromDownloadClient(this, false);
|
||||||
queueItem.DownloadId = vortexQueueItem.AddUUID ?? vortexQueueItem.Id.ToString();
|
queueItem.DownloadId = vortexQueueItem.AddUUID ?? vortexQueueItem.Id.ToString();
|
||||||
queueItem.Category = vortexQueueItem.GroupName;
|
queueItem.Category = vortexQueueItem.GroupName;
|
||||||
queueItem.Title = vortexQueueItem.UiTitle;
|
queueItem.Title = vortexQueueItem.UiTitle;
|
||||||
|
|
|
@ -73,7 +73,7 @@ namespace NzbDrone.Core.Download.Clients.Nzbget
|
||||||
queueItem.Title = item.NzbName;
|
queueItem.Title = item.NzbName;
|
||||||
queueItem.TotalSize = totalSize;
|
queueItem.TotalSize = totalSize;
|
||||||
queueItem.Category = item.Category;
|
queueItem.Category = item.Category;
|
||||||
queueItem.DownloadClientInfo = DownloadClientItemClientInfo.FromDownloadClient(this);
|
queueItem.DownloadClientInfo = DownloadClientItemClientInfo.FromDownloadClient(this, false);
|
||||||
queueItem.CanMoveFiles = true;
|
queueItem.CanMoveFiles = true;
|
||||||
queueItem.CanBeRemoved = true;
|
queueItem.CanBeRemoved = true;
|
||||||
|
|
||||||
|
@ -120,7 +120,7 @@ namespace NzbDrone.Core.Download.Clients.Nzbget
|
||||||
var historyItem = new DownloadClientItem();
|
var historyItem = new DownloadClientItem();
|
||||||
var itemDir = item.FinalDir.IsNullOrWhiteSpace() ? item.DestDir : item.FinalDir;
|
var itemDir = item.FinalDir.IsNullOrWhiteSpace() ? item.DestDir : item.FinalDir;
|
||||||
|
|
||||||
historyItem.DownloadClientInfo = DownloadClientItemClientInfo.FromDownloadClient(this);
|
historyItem.DownloadClientInfo = DownloadClientItemClientInfo.FromDownloadClient(this, false);
|
||||||
historyItem.DownloadId = droneParameter == null ? item.Id.ToString() : droneParameter.Value.ToString();
|
historyItem.DownloadId = droneParameter == null ? item.Id.ToString() : droneParameter.Value.ToString();
|
||||||
historyItem.Title = item.Name;
|
historyItem.Title = item.Name;
|
||||||
historyItem.TotalSize = MakeInt64(item.FileSizeHi, item.FileSizeLo);
|
historyItem.TotalSize = MakeInt64(item.FileSizeHi, item.FileSizeLo);
|
||||||
|
|
|
@ -75,7 +75,7 @@ namespace NzbDrone.Core.Download.Clients.Pneumatic
|
||||||
|
|
||||||
var historyItem = new DownloadClientItem
|
var historyItem = new DownloadClientItem
|
||||||
{
|
{
|
||||||
DownloadClientInfo = DownloadClientItemClientInfo.FromDownloadClient(this),
|
DownloadClientInfo = DownloadClientItemClientInfo.FromDownloadClient(this, false),
|
||||||
DownloadId = GetDownloadClientId(file),
|
DownloadId = GetDownloadClientId(file),
|
||||||
Title = title,
|
Title = title,
|
||||||
|
|
||||||
|
|
|
@ -231,7 +231,7 @@ namespace NzbDrone.Core.Download.Clients.QBittorrent
|
||||||
Category = torrent.Category.IsNotNullOrWhiteSpace() ? torrent.Category : torrent.Label,
|
Category = torrent.Category.IsNotNullOrWhiteSpace() ? torrent.Category : torrent.Label,
|
||||||
Title = torrent.Name,
|
Title = torrent.Name,
|
||||||
TotalSize = torrent.Size,
|
TotalSize = torrent.Size,
|
||||||
DownloadClientInfo = DownloadClientItemClientInfo.FromDownloadClient(this),
|
DownloadClientInfo = DownloadClientItemClientInfo.FromDownloadClient(this, Settings.TvImportedCategory.IsNotNullOrWhiteSpace()),
|
||||||
RemainingSize = (long)(torrent.Size * (1.0 - torrent.Progress)),
|
RemainingSize = (long)(torrent.Size * (1.0 - torrent.Progress)),
|
||||||
RemainingTime = GetRemainingTime(torrent),
|
RemainingTime = GetRemainingTime(torrent),
|
||||||
SeedRatio = torrent.Ratio
|
SeedRatio = torrent.Ratio
|
||||||
|
|
|
@ -65,7 +65,7 @@ namespace NzbDrone.Core.Download.Clients.Sabnzbd
|
||||||
}
|
}
|
||||||
|
|
||||||
var queueItem = new DownloadClientItem();
|
var queueItem = new DownloadClientItem();
|
||||||
queueItem.DownloadClientInfo = DownloadClientItemClientInfo.FromDownloadClient(this);
|
queueItem.DownloadClientInfo = DownloadClientItemClientInfo.FromDownloadClient(this, false);
|
||||||
queueItem.DownloadId = sabQueueItem.Id;
|
queueItem.DownloadId = sabQueueItem.Id;
|
||||||
queueItem.Category = sabQueueItem.Category;
|
queueItem.Category = sabQueueItem.Category;
|
||||||
queueItem.Title = sabQueueItem.Title;
|
queueItem.Title = sabQueueItem.Title;
|
||||||
|
@ -120,7 +120,7 @@ namespace NzbDrone.Core.Download.Clients.Sabnzbd
|
||||||
|
|
||||||
var historyItem = new DownloadClientItem
|
var historyItem = new DownloadClientItem
|
||||||
{
|
{
|
||||||
DownloadClientInfo = DownloadClientItemClientInfo.FromDownloadClient(this),
|
DownloadClientInfo = DownloadClientItemClientInfo.FromDownloadClient(this, false),
|
||||||
DownloadId = sabHistoryItem.Id,
|
DownloadId = sabHistoryItem.Id,
|
||||||
Category = sabHistoryItem.Category,
|
Category = sabHistoryItem.Category,
|
||||||
Title = sabHistoryItem.Title,
|
Title = sabHistoryItem.Title,
|
||||||
|
|
|
@ -74,7 +74,7 @@ namespace NzbDrone.Core.Download.Clients.Transmission
|
||||||
item.Category = Settings.TvCategory;
|
item.Category = Settings.TvCategory;
|
||||||
item.Title = torrent.Name;
|
item.Title = torrent.Name;
|
||||||
|
|
||||||
item.DownloadClientInfo = DownloadClientItemClientInfo.FromDownloadClient(this);
|
item.DownloadClientInfo = DownloadClientItemClientInfo.FromDownloadClient(this, false);
|
||||||
|
|
||||||
item.OutputPath = GetOutputPath(outputPath, torrent);
|
item.OutputPath = GetOutputPath(outputPath, torrent);
|
||||||
item.TotalSize = torrent.TotalSize;
|
item.TotalSize = torrent.TotalSize;
|
||||||
|
|
|
@ -148,7 +148,7 @@ namespace NzbDrone.Core.Download.Clients.RTorrent
|
||||||
}
|
}
|
||||||
|
|
||||||
var item = new DownloadClientItem();
|
var item = new DownloadClientItem();
|
||||||
item.DownloadClientInfo = DownloadClientItemClientInfo.FromDownloadClient(this);
|
item.DownloadClientInfo = DownloadClientItemClientInfo.FromDownloadClient(this, Settings.TvImportedCategory.IsNotNullOrWhiteSpace());
|
||||||
item.Title = torrent.Name;
|
item.Title = torrent.Name;
|
||||||
item.DownloadId = torrent.Hash;
|
item.DownloadId = torrent.Hash;
|
||||||
item.OutputPath = _remotePathMappingService.RemapRemoteToLocal(Settings.Host, new OsPath(torrent.Path));
|
item.OutputPath = _remotePathMappingService.RemapRemoteToLocal(Settings.Host, new OsPath(torrent.Path));
|
||||||
|
|
|
@ -122,7 +122,7 @@ namespace NzbDrone.Core.Download.Clients.UTorrent
|
||||||
item.Title = torrent.Name;
|
item.Title = torrent.Name;
|
||||||
item.TotalSize = torrent.Size;
|
item.TotalSize = torrent.Size;
|
||||||
item.Category = torrent.Label;
|
item.Category = torrent.Label;
|
||||||
item.DownloadClientInfo = DownloadClientItemClientInfo.FromDownloadClient(this);
|
item.DownloadClientInfo = DownloadClientItemClientInfo.FromDownloadClient(this, Settings.TvImportedCategory.IsNotNullOrWhiteSpace());
|
||||||
item.RemainingSize = torrent.Remaining;
|
item.RemainingSize = torrent.Remaining;
|
||||||
item.SeedRatio = torrent.Ratio;
|
item.SeedRatio = torrent.Ratio;
|
||||||
|
|
||||||
|
|
|
@ -37,9 +37,10 @@ namespace NzbDrone.Core.Download
|
||||||
public string Type { get; set; }
|
public string Type { get; set; }
|
||||||
public int Id { get; set; }
|
public int Id { get; set; }
|
||||||
public string Name { get; set; }
|
public string Name { get; set; }
|
||||||
|
public bool HasPostImportCategory { get; set; }
|
||||||
|
|
||||||
public static DownloadClientItemClientInfo FromDownloadClient<TSettings>(
|
public static DownloadClientItemClientInfo FromDownloadClient<TSettings>(
|
||||||
DownloadClientBase<TSettings> downloadClient)
|
DownloadClientBase<TSettings> downloadClient, bool hasPostImportCategory)
|
||||||
where TSettings : IProviderConfig, new()
|
where TSettings : IProviderConfig, new()
|
||||||
{
|
{
|
||||||
return new DownloadClientItemClientInfo
|
return new DownloadClientItemClientInfo
|
||||||
|
@ -47,7 +48,8 @@ namespace NzbDrone.Core.Download
|
||||||
Protocol = downloadClient.Protocol,
|
Protocol = downloadClient.Protocol,
|
||||||
Type = downloadClient.Name,
|
Type = downloadClient.Name,
|
||||||
Id = downloadClient.Definition.Id,
|
Id = downloadClient.Definition.Id,
|
||||||
Name = downloadClient.Definition.Name
|
Name = downloadClient.Definition.Name,
|
||||||
|
HasPostImportCategory = hasPostImportCategory
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -153,9 +153,15 @@
|
||||||
"BlackholeWatchFolder": "Watch Folder",
|
"BlackholeWatchFolder": "Watch Folder",
|
||||||
"BlackholeWatchFolderHelpText": "Folder from which {appName} should import completed downloads",
|
"BlackholeWatchFolderHelpText": "Folder from which {appName} should import completed downloads",
|
||||||
"Blocklist": "Blocklist",
|
"Blocklist": "Blocklist",
|
||||||
|
"BlocklistAndSearch": "Blocklist and Search",
|
||||||
|
"BlocklistAndSearchHint": "Start a search for a replacement after blocklisting",
|
||||||
|
"BlocklistAndSearchMultipleHint": "Start searches for replacements after blocklisting",
|
||||||
"BlocklistLoadError": "Unable to load blocklist",
|
"BlocklistLoadError": "Unable to load blocklist",
|
||||||
|
"BlocklistMultipleOnlyHint": "Blocklist without searching for replacements",
|
||||||
|
"BlocklistOnly": "Blocklist Only",
|
||||||
|
"BlocklistOnlyHint": "Blocklist without searching for a replacement",
|
||||||
"BlocklistRelease": "Blocklist Release",
|
"BlocklistRelease": "Blocklist Release",
|
||||||
"BlocklistReleaseSearchEpisodeAgainHelpText": "Starts a search for this episode again and prevents this release from being grabbed again",
|
"BlocklistReleaseHelpText": "Blocks this release from being redownloaded by {appName} via RSS or Automatic Search",
|
||||||
"BlocklistReleases": "Blocklist Releases",
|
"BlocklistReleases": "Blocklist Releases",
|
||||||
"Branch": "Branch",
|
"Branch": "Branch",
|
||||||
"BranchUpdate": "Branch to use to update {appName}",
|
"BranchUpdate": "Branch to use to update {appName}",
|
||||||
|
@ -188,6 +194,9 @@
|
||||||
"CertificateValidation": "Certificate Validation",
|
"CertificateValidation": "Certificate Validation",
|
||||||
"CertificateValidationHelpText": "Change how strict HTTPS certification validation is. Do not change unless you understand the risks.",
|
"CertificateValidationHelpText": "Change how strict HTTPS certification validation is. Do not change unless you understand the risks.",
|
||||||
"Certification": "Certification",
|
"Certification": "Certification",
|
||||||
|
"ChangeCategory": "Change Category",
|
||||||
|
"ChangeCategoryHint": "Changes download to the 'Post-Import Category' from Download Client",
|
||||||
|
"ChangeCategoryMultipleHint": "Changes downloads to the 'Post-Import Category' from Download Client",
|
||||||
"ChangeFileDate": "Change File Date",
|
"ChangeFileDate": "Change File Date",
|
||||||
"ChangeFileDateHelpText": "Change file date on import/rescan",
|
"ChangeFileDateHelpText": "Change file date on import/rescan",
|
||||||
"CheckDownloadClientForDetails": "check download client for more details",
|
"CheckDownloadClientForDetails": "check download client for more details",
|
||||||
|
@ -377,6 +386,8 @@
|
||||||
"DisabledForLocalAddresses": "Disabled for Local Addresses",
|
"DisabledForLocalAddresses": "Disabled for Local Addresses",
|
||||||
"Discord": "Discord",
|
"Discord": "Discord",
|
||||||
"DiskSpace": "Disk Space",
|
"DiskSpace": "Disk Space",
|
||||||
|
"DoNotBlocklist": "Do not Blocklist",
|
||||||
|
"DoNotBlocklistHint": "Remove without blocklisting",
|
||||||
"DoNotPrefer": "Do not Prefer",
|
"DoNotPrefer": "Do not Prefer",
|
||||||
"DoNotUpgradeAutomatically": "Do not Upgrade Automatically",
|
"DoNotUpgradeAutomatically": "Do not Upgrade Automatically",
|
||||||
"Docker": "Docker",
|
"Docker": "Docker",
|
||||||
|
@ -754,6 +765,10 @@
|
||||||
"IconForFinalesHelpText": "Show icon for series/season finales based on available episode information",
|
"IconForFinalesHelpText": "Show icon for series/season finales based on available episode information",
|
||||||
"IconForSpecials": "Icon for Specials",
|
"IconForSpecials": "Icon for Specials",
|
||||||
"IconForSpecialsHelpText": "Show icon for special episodes (season 0)",
|
"IconForSpecialsHelpText": "Show icon for special episodes (season 0)",
|
||||||
|
"IgnoreDownload": "Ignore Download",
|
||||||
|
"IgnoreDownloads": "Ignore Downloads",
|
||||||
|
"IgnoreDownloadHint": "Stops {appName} from processing this download further",
|
||||||
|
"IgnoreDownloadsHint": "Stops {appName} from processing these downloads further",
|
||||||
"Ignored": "Ignored",
|
"Ignored": "Ignored",
|
||||||
"IgnoredAddresses": "Ignored Addresses",
|
"IgnoredAddresses": "Ignored Addresses",
|
||||||
"Images": "Images",
|
"Images": "Images",
|
||||||
|
@ -1596,11 +1611,15 @@
|
||||||
"RemoveFailedDownloadsHelpText": "Remove failed downloads from download client history",
|
"RemoveFailedDownloadsHelpText": "Remove failed downloads from download client history",
|
||||||
"RemoveFilter": "Remove filter",
|
"RemoveFilter": "Remove filter",
|
||||||
"RemoveFromBlocklist": "Remove from Blocklist",
|
"RemoveFromBlocklist": "Remove from Blocklist",
|
||||||
"RemoveFromDownloadClient": "Remove From Download Client",
|
"RemoveFromDownloadClient": "Remove from Download Client",
|
||||||
"RemoveFromDownloadClientHelpTextWarning": "Removing will remove the download and the file(s) from the download client.",
|
"RemoveFromDownloadClientHint": "Removes download and file(s) from download client",
|
||||||
"RemoveFromQueue": "Remove from queue",
|
"RemoveFromQueue": "Remove from queue",
|
||||||
|
"RemoveMultipleFromDownloadClientHint": "Removes downloads and files from download client",
|
||||||
"RemoveQueueItem": "Remove - {sourceTitle}",
|
"RemoveQueueItem": "Remove - {sourceTitle}",
|
||||||
|
"RemoveQueueItemRemovalMethodHelpTextWarning": "'Remove from Download Client' will remove the download and the file(s) from the download client.",
|
||||||
"RemoveQueueItemConfirmation": "Are you sure you want to remove '{sourceTitle}' from the queue?",
|
"RemoveQueueItemConfirmation": "Are you sure you want to remove '{sourceTitle}' from the queue?",
|
||||||
|
"RemoveQueueItemRemovalMethod": "Removal Method",
|
||||||
|
"RemoveQueueItemsRemovalMethodHelpTextWarning": "'Remove from Download Client' will remove the downloads and the files from the download client.",
|
||||||
"RemoveRootFolder": "Remove root folder",
|
"RemoveRootFolder": "Remove root folder",
|
||||||
"RemoveSelected": "Remove Selected",
|
"RemoveSelected": "Remove Selected",
|
||||||
"RemoveSelectedBlocklistMessageText": "Are you sure you want to remove the selected items from the blocklist?",
|
"RemoveSelectedBlocklistMessageText": "Are you sure you want to remove the selected items from the blocklist?",
|
||||||
|
|
|
@ -30,6 +30,7 @@ namespace NzbDrone.Core.Queue
|
||||||
public RemoteEpisode RemoteEpisode { get; set; }
|
public RemoteEpisode RemoteEpisode { get; set; }
|
||||||
public DownloadProtocol Protocol { get; set; }
|
public DownloadProtocol Protocol { get; set; }
|
||||||
public string DownloadClient { get; set; }
|
public string DownloadClient { get; set; }
|
||||||
|
public bool DownloadClientHasPostImportCategory { get; set; }
|
||||||
public string Indexer { get; set; }
|
public string Indexer { get; set; }
|
||||||
public string OutputPath { get; set; }
|
public string OutputPath { get; set; }
|
||||||
public string ErrorMessage { get; set; }
|
public string ErrorMessage { get; set; }
|
||||||
|
|
|
@ -80,7 +80,8 @@ namespace NzbDrone.Core.Queue
|
||||||
DownloadClient = trackedDownload.DownloadItem.DownloadClientInfo.Name,
|
DownloadClient = trackedDownload.DownloadItem.DownloadClientInfo.Name,
|
||||||
Indexer = trackedDownload.Indexer,
|
Indexer = trackedDownload.Indexer,
|
||||||
OutputPath = trackedDownload.DownloadItem.OutputPath.ToString(),
|
OutputPath = trackedDownload.DownloadItem.OutputPath.ToString(),
|
||||||
Added = trackedDownload.Added
|
Added = trackedDownload.Added,
|
||||||
|
DownloadClientHasPostImportCategory = trackedDownload.DownloadItem.DownloadClientInfo.HasPostImportCategory
|
||||||
};
|
};
|
||||||
|
|
||||||
queue.Id = HashConverter.GetHashInt31($"trackedDownload-{trackedDownload.DownloadClient}-{trackedDownload.DownloadItem.DownloadId}-ep{episode?.Id ?? 0}");
|
queue.Id = HashConverter.GetHashInt31($"trackedDownload-{trackedDownload.DownloadClient}-{trackedDownload.DownloadItem.DownloadId}-ep{episode?.Id ?? 0}");
|
||||||
|
|
|
@ -71,7 +71,7 @@ namespace Sonarr.Api.V3.Queue
|
||||||
}
|
}
|
||||||
|
|
||||||
[RestDeleteById]
|
[RestDeleteById]
|
||||||
public void RemoveAction(int id, bool removeFromClient = true, bool blocklist = false, bool skipRedownload = false)
|
public void RemoveAction(int id, bool removeFromClient = true, bool blocklist = false, bool skipRedownload = false, bool changeCategory = false)
|
||||||
{
|
{
|
||||||
var pendingRelease = _pendingReleaseService.FindPendingQueueItem(id);
|
var pendingRelease = _pendingReleaseService.FindPendingQueueItem(id);
|
||||||
|
|
||||||
|
@ -89,12 +89,12 @@ namespace Sonarr.Api.V3.Queue
|
||||||
throw new NotFoundException();
|
throw new NotFoundException();
|
||||||
}
|
}
|
||||||
|
|
||||||
Remove(trackedDownload, removeFromClient, blocklist, skipRedownload);
|
Remove(trackedDownload, removeFromClient, blocklist, skipRedownload, changeCategory);
|
||||||
_trackedDownloadService.StopTracking(trackedDownload.DownloadItem.DownloadId);
|
_trackedDownloadService.StopTracking(trackedDownload.DownloadItem.DownloadId);
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpDelete("bulk")]
|
[HttpDelete("bulk")]
|
||||||
public object RemoveMany([FromBody] QueueBulkResource resource, [FromQuery] bool removeFromClient = true, [FromQuery] bool blocklist = false, [FromQuery] bool skipRedownload = false)
|
public object RemoveMany([FromBody] QueueBulkResource resource, [FromQuery] bool removeFromClient = true, [FromQuery] bool blocklist = false, [FromQuery] bool skipRedownload = false, [FromQuery] bool changeCategory = false)
|
||||||
{
|
{
|
||||||
var trackedDownloadIds = new List<string>();
|
var trackedDownloadIds = new List<string>();
|
||||||
var pendingToRemove = new List<NzbDrone.Core.Queue.Queue>();
|
var pendingToRemove = new List<NzbDrone.Core.Queue.Queue>();
|
||||||
|
@ -125,7 +125,7 @@ namespace Sonarr.Api.V3.Queue
|
||||||
|
|
||||||
foreach (var trackedDownload in trackedToRemove.DistinctBy(t => t.DownloadItem.DownloadId))
|
foreach (var trackedDownload in trackedToRemove.DistinctBy(t => t.DownloadItem.DownloadId))
|
||||||
{
|
{
|
||||||
Remove(trackedDownload, removeFromClient, blocklist, skipRedownload);
|
Remove(trackedDownload, removeFromClient, blocklist, skipRedownload, changeCategory);
|
||||||
trackedDownloadIds.Add(trackedDownload.DownloadItem.DownloadId);
|
trackedDownloadIds.Add(trackedDownload.DownloadItem.DownloadId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -292,7 +292,7 @@ namespace Sonarr.Api.V3.Queue
|
||||||
_pendingReleaseService.RemovePendingQueueItems(pendingRelease.Id);
|
_pendingReleaseService.RemovePendingQueueItems(pendingRelease.Id);
|
||||||
}
|
}
|
||||||
|
|
||||||
private TrackedDownload Remove(TrackedDownload trackedDownload, bool removeFromClient, bool blocklist, bool skipRedownload)
|
private TrackedDownload Remove(TrackedDownload trackedDownload, bool removeFromClient, bool blocklist, bool skipRedownload, bool changeCategory)
|
||||||
{
|
{
|
||||||
if (removeFromClient)
|
if (removeFromClient)
|
||||||
{
|
{
|
||||||
|
@ -305,13 +305,24 @@ namespace Sonarr.Api.V3.Queue
|
||||||
|
|
||||||
downloadClient.RemoveItem(trackedDownload.DownloadItem, true);
|
downloadClient.RemoveItem(trackedDownload.DownloadItem, true);
|
||||||
}
|
}
|
||||||
|
else if (changeCategory)
|
||||||
|
{
|
||||||
|
var downloadClient = _downloadClientProvider.Get(trackedDownload.DownloadClient);
|
||||||
|
|
||||||
|
if (downloadClient == null)
|
||||||
|
{
|
||||||
|
throw new BadRequestException();
|
||||||
|
}
|
||||||
|
|
||||||
|
downloadClient.MarkItemAsImported(trackedDownload.DownloadItem);
|
||||||
|
}
|
||||||
|
|
||||||
if (blocklist)
|
if (blocklist)
|
||||||
{
|
{
|
||||||
_failedDownloadService.MarkAsFailed(trackedDownload.DownloadItem.DownloadId, skipRedownload);
|
_failedDownloadService.MarkAsFailed(trackedDownload.DownloadItem.DownloadId, skipRedownload);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!removeFromClient && !blocklist)
|
if (!removeFromClient && !blocklist && !changeCategory)
|
||||||
{
|
{
|
||||||
if (!_ignoredDownloadService.IgnoreDownload(trackedDownload))
|
if (!_ignoredDownloadService.IgnoreDownload(trackedDownload))
|
||||||
{
|
{
|
||||||
|
|
|
@ -38,6 +38,7 @@ namespace Sonarr.Api.V3.Queue
|
||||||
public string DownloadId { get; set; }
|
public string DownloadId { get; set; }
|
||||||
public DownloadProtocol Protocol { get; set; }
|
public DownloadProtocol Protocol { get; set; }
|
||||||
public string DownloadClient { get; set; }
|
public string DownloadClient { get; set; }
|
||||||
|
public bool DownloadClientHasPostImportCategory { get; set; }
|
||||||
public string Indexer { get; set; }
|
public string Indexer { get; set; }
|
||||||
public string OutputPath { get; set; }
|
public string OutputPath { get; set; }
|
||||||
public bool EpisodeHasFile { get; set; }
|
public bool EpisodeHasFile { get; set; }
|
||||||
|
@ -81,6 +82,7 @@ namespace Sonarr.Api.V3.Queue
|
||||||
DownloadId = model.DownloadId,
|
DownloadId = model.DownloadId,
|
||||||
Protocol = model.Protocol,
|
Protocol = model.Protocol,
|
||||||
DownloadClient = model.DownloadClient,
|
DownloadClient = model.DownloadClient,
|
||||||
|
DownloadClientHasPostImportCategory = model.DownloadClientHasPostImportCategory,
|
||||||
Indexer = model.Indexer,
|
Indexer = model.Indexer,
|
||||||
OutputPath = model.OutputPath,
|
OutputPath = model.OutputPath,
|
||||||
EpisodeHasFile = model.Episode?.HasFile ?? false
|
EpisodeHasFile = model.Episode?.HasFile ?? false
|
||||||
|
|
Loading…
Reference in New Issue