mirror of
https://github.com/Sonarr/Sonarr.git
synced 2025-01-10 23:29:53 +02:00
parent
a169ebff2a
commit
74cdf01e49
17
frontend/src/Episode/getReleaseTypeName.ts
Normal file
17
frontend/src/Episode/getReleaseTypeName.ts
Normal file
@ -0,0 +1,17 @@
|
||||
import ReleaseType from 'InteractiveImport/ReleaseType';
|
||||
import translate from 'Utilities/String/translate';
|
||||
|
||||
export default function getReleaseTypeName(
|
||||
releaseType?: ReleaseType
|
||||
): string | null {
|
||||
switch (releaseType) {
|
||||
case 'singleEpisode':
|
||||
return translate('SingleEpisode');
|
||||
case 'multiEpisode':
|
||||
return translate('MultiEpisode');
|
||||
case 'seasonPack':
|
||||
return translate('SeasonPack');
|
||||
default:
|
||||
return translate('Unknown');
|
||||
}
|
||||
}
|
@ -1,4 +1,5 @@
|
||||
import ModelBase from 'App/ModelBase';
|
||||
import ReleaseType from 'InteractiveImport/ReleaseType';
|
||||
import Language from 'Language/Language';
|
||||
import { QualityModel } from 'Quality/Quality';
|
||||
import CustomFormat from 'typings/CustomFormat';
|
||||
@ -17,6 +18,7 @@ export interface EpisodeFile extends ModelBase {
|
||||
quality: QualityModel;
|
||||
customFormats: CustomFormat[];
|
||||
indexerFlags: number;
|
||||
releaseType: ReleaseType;
|
||||
mediaInfo: MediaInfo;
|
||||
qualityCutoffNotMet: boolean;
|
||||
}
|
||||
|
@ -36,6 +36,7 @@ import InteractiveImport, {
|
||||
import SelectLanguageModal from 'InteractiveImport/Language/SelectLanguageModal';
|
||||
import SelectQualityModal from 'InteractiveImport/Quality/SelectQualityModal';
|
||||
import SelectReleaseGroupModal from 'InteractiveImport/ReleaseGroup/SelectReleaseGroupModal';
|
||||
import SelectReleaseTypeModal from 'InteractiveImport/ReleaseType/SelectReleaseTypeModal';
|
||||
import SelectSeasonModal from 'InteractiveImport/Season/SelectSeasonModal';
|
||||
import SelectSeriesModal from 'InteractiveImport/Series/SelectSeriesModal';
|
||||
import Language from 'Language/Language';
|
||||
@ -73,7 +74,8 @@ type SelectType =
|
||||
| 'releaseGroup'
|
||||
| 'quality'
|
||||
| 'language'
|
||||
| 'indexerFlags';
|
||||
| 'indexerFlags'
|
||||
| 'releaseType';
|
||||
|
||||
type FilterExistingFiles = 'all' | 'new';
|
||||
|
||||
@ -128,6 +130,12 @@ const COLUMNS = [
|
||||
isSortable: true,
|
||||
isVisible: true,
|
||||
},
|
||||
{
|
||||
name: 'releaseType',
|
||||
label: () => translate('ReleaseType'),
|
||||
isSortable: true,
|
||||
isVisible: true,
|
||||
},
|
||||
{
|
||||
name: 'customFormats',
|
||||
label: React.createElement(Icon, {
|
||||
@ -369,6 +377,10 @@ function InteractiveImportModalContent(
|
||||
key: 'indexerFlags',
|
||||
value: translate('SelectIndexerFlags'),
|
||||
},
|
||||
{
|
||||
key: 'releaseType',
|
||||
value: translate('SelectReleaseType'),
|
||||
},
|
||||
];
|
||||
|
||||
if (allowSeriesChange) {
|
||||
@ -511,6 +523,7 @@ function InteractiveImportModalContent(
|
||||
languages,
|
||||
indexerFlags,
|
||||
episodeFileId,
|
||||
releaseType,
|
||||
} = item;
|
||||
|
||||
if (!series) {
|
||||
@ -560,6 +573,7 @@ function InteractiveImportModalContent(
|
||||
quality,
|
||||
languages,
|
||||
indexerFlags,
|
||||
releaseType,
|
||||
});
|
||||
|
||||
return;
|
||||
@ -575,6 +589,7 @@ function InteractiveImportModalContent(
|
||||
quality,
|
||||
languages,
|
||||
indexerFlags,
|
||||
releaseType,
|
||||
downloadId,
|
||||
episodeFileId,
|
||||
});
|
||||
@ -787,6 +802,22 @@ function InteractiveImportModalContent(
|
||||
[selectedIds, dispatch]
|
||||
);
|
||||
|
||||
const onReleaseTypeSelect = useCallback(
|
||||
(releaseType: string) => {
|
||||
dispatch(
|
||||
updateInteractiveImportItems({
|
||||
ids: selectedIds,
|
||||
releaseType,
|
||||
})
|
||||
);
|
||||
|
||||
dispatch(reprocessInteractiveImportItems({ ids: selectedIds }));
|
||||
|
||||
setSelectModalOpen(null);
|
||||
},
|
||||
[selectedIds, dispatch]
|
||||
);
|
||||
|
||||
const orderedSelectedIds = items.reduce((acc: number[], file) => {
|
||||
if (selectedIds.includes(file.id)) {
|
||||
acc.push(file.id);
|
||||
@ -1000,6 +1031,14 @@ function InteractiveImportModalContent(
|
||||
onModalClose={onSelectModalClose}
|
||||
/>
|
||||
|
||||
<SelectReleaseTypeModal
|
||||
isOpen={selectModalOpen === 'releaseType'}
|
||||
releaseType="unknown"
|
||||
modalTitle={modalTitle}
|
||||
onReleaseTypeSelect={onReleaseTypeSelect}
|
||||
onModalClose={onSelectModalClose}
|
||||
/>
|
||||
|
||||
<ConfirmModal
|
||||
isOpen={isConfirmDeleteModalOpen}
|
||||
kind={kinds.DANGER}
|
||||
|
@ -12,6 +12,7 @@ import Episode from 'Episode/Episode';
|
||||
import EpisodeFormats from 'Episode/EpisodeFormats';
|
||||
import EpisodeLanguages from 'Episode/EpisodeLanguages';
|
||||
import EpisodeQuality from 'Episode/EpisodeQuality';
|
||||
import getReleaseTypeName from 'Episode/getReleaseTypeName';
|
||||
import IndexerFlags from 'Episode/IndexerFlags';
|
||||
import { icons, kinds, tooltipPositions } from 'Helpers/Props';
|
||||
import SelectEpisodeModal from 'InteractiveImport/Episode/SelectEpisodeModal';
|
||||
@ -20,6 +21,8 @@ import SelectIndexerFlagsModal from 'InteractiveImport/IndexerFlags/SelectIndexe
|
||||
import SelectLanguageModal from 'InteractiveImport/Language/SelectLanguageModal';
|
||||
import SelectQualityModal from 'InteractiveImport/Quality/SelectQualityModal';
|
||||
import SelectReleaseGroupModal from 'InteractiveImport/ReleaseGroup/SelectReleaseGroupModal';
|
||||
import ReleaseType from 'InteractiveImport/ReleaseType';
|
||||
import SelectReleaseTypeModal from 'InteractiveImport/ReleaseType/SelectReleaseTypeModal';
|
||||
import SelectSeasonModal from 'InteractiveImport/Season/SelectSeasonModal';
|
||||
import SelectSeriesModal from 'InteractiveImport/Series/SelectSeriesModal';
|
||||
import Language from 'Language/Language';
|
||||
@ -44,7 +47,8 @@ type SelectType =
|
||||
| 'releaseGroup'
|
||||
| 'quality'
|
||||
| 'language'
|
||||
| 'indexerFlags';
|
||||
| 'indexerFlags'
|
||||
| 'releaseType';
|
||||
|
||||
type SelectedChangeProps = SelectStateInputProps & {
|
||||
hasEpisodeFileId: boolean;
|
||||
@ -61,6 +65,7 @@ interface InteractiveImportRowProps {
|
||||
quality?: QualityModel;
|
||||
languages?: Language[];
|
||||
size: number;
|
||||
releaseType: ReleaseType;
|
||||
customFormats?: object[];
|
||||
customFormatScore?: number;
|
||||
indexerFlags: number;
|
||||
@ -86,6 +91,7 @@ function InteractiveImportRow(props: InteractiveImportRowProps) {
|
||||
languages,
|
||||
releaseGroup,
|
||||
size,
|
||||
releaseType,
|
||||
customFormats,
|
||||
customFormatScore,
|
||||
indexerFlags,
|
||||
@ -315,6 +321,27 @@ function InteractiveImportRow(props: InteractiveImportRowProps) {
|
||||
[id, dispatch, setSelectModalOpen, selectRowAfterChange]
|
||||
);
|
||||
|
||||
const onSelectReleaseTypePress = useCallback(() => {
|
||||
setSelectModalOpen('releaseType');
|
||||
}, [setSelectModalOpen]);
|
||||
|
||||
const onReleaseTypeSelect = useCallback(
|
||||
(releaseType: ReleaseType) => {
|
||||
dispatch(
|
||||
updateInteractiveImportItem({
|
||||
id,
|
||||
releaseType,
|
||||
})
|
||||
);
|
||||
|
||||
dispatch(reprocessInteractiveImportItems({ ids: [id] }));
|
||||
|
||||
setSelectModalOpen(null);
|
||||
selectRowAfterChange();
|
||||
},
|
||||
[id, dispatch, setSelectModalOpen, selectRowAfterChange]
|
||||
);
|
||||
|
||||
const onSelectIndexerFlagsPress = useCallback(() => {
|
||||
setSelectModalOpen('indexerFlags');
|
||||
}, [setSelectModalOpen]);
|
||||
@ -461,6 +488,13 @@ function InteractiveImportRow(props: InteractiveImportRowProps) {
|
||||
|
||||
<TableRowCell>{formatBytes(size)}</TableRowCell>
|
||||
|
||||
<TableRowCellButton
|
||||
title={translate('ClickToChangeReleaseType')}
|
||||
onPress={onSelectReleaseTypePress}
|
||||
>
|
||||
{getReleaseTypeName(releaseType)}
|
||||
</TableRowCellButton>
|
||||
|
||||
<TableRowCell>
|
||||
{customFormats?.length ? (
|
||||
<Popover
|
||||
@ -572,6 +606,14 @@ function InteractiveImportRow(props: InteractiveImportRowProps) {
|
||||
onModalClose={onSelectModalClose}
|
||||
/>
|
||||
|
||||
<SelectReleaseTypeModal
|
||||
isOpen={selectModalOpen === 'releaseType'}
|
||||
releaseType={releaseType ?? 'unknown'}
|
||||
modalTitle={modalTitle}
|
||||
onReleaseTypeSelect={onReleaseTypeSelect}
|
||||
onModalClose={onSelectModalClose}
|
||||
/>
|
||||
|
||||
<SelectIndexerFlagsModal
|
||||
isOpen={selectModalOpen === 'indexerFlags'}
|
||||
indexerFlags={indexerFlags ?? 0}
|
||||
|
@ -15,6 +15,7 @@ export interface InteractiveImportCommandOptions {
|
||||
quality: QualityModel;
|
||||
languages: Language[];
|
||||
indexerFlags: number;
|
||||
releaseType: ReleaseType;
|
||||
downloadId?: string;
|
||||
episodeFileId?: number;
|
||||
}
|
||||
|
@ -0,0 +1,30 @@
|
||||
import React from 'react';
|
||||
import Modal from 'Components/Modal/Modal';
|
||||
import ReleaseType from 'InteractiveImport/ReleaseType';
|
||||
import SelectReleaseTypeModalContent from './SelectReleaseTypeModalContent';
|
||||
|
||||
interface SelectQualityModalProps {
|
||||
isOpen: boolean;
|
||||
releaseType: ReleaseType;
|
||||
modalTitle: string;
|
||||
onReleaseTypeSelect(releaseType: ReleaseType): void;
|
||||
onModalClose(): void;
|
||||
}
|
||||
|
||||
function SelectReleaseTypeModal(props: SelectQualityModalProps) {
|
||||
const { isOpen, releaseType, modalTitle, onReleaseTypeSelect, onModalClose } =
|
||||
props;
|
||||
|
||||
return (
|
||||
<Modal isOpen={isOpen} onModalClose={onModalClose}>
|
||||
<SelectReleaseTypeModalContent
|
||||
releaseType={releaseType}
|
||||
modalTitle={modalTitle}
|
||||
onReleaseTypeSelect={onReleaseTypeSelect}
|
||||
onModalClose={onModalClose}
|
||||
/>
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
|
||||
export default SelectReleaseTypeModal;
|
@ -0,0 +1,99 @@
|
||||
import React, { useCallback, useState } from 'react';
|
||||
import Form from 'Components/Form/Form';
|
||||
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 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 } from 'Helpers/Props';
|
||||
import ReleaseType from 'InteractiveImport/ReleaseType';
|
||||
import translate from 'Utilities/String/translate';
|
||||
|
||||
const options = [
|
||||
{
|
||||
key: 'unknown',
|
||||
get value() {
|
||||
return translate('Unknown');
|
||||
},
|
||||
},
|
||||
{
|
||||
key: 'singleEpisode',
|
||||
get value() {
|
||||
return translate('SingleEpisode');
|
||||
},
|
||||
},
|
||||
{
|
||||
key: 'multiEpisode',
|
||||
get value() {
|
||||
return translate('MultiEpisode');
|
||||
},
|
||||
},
|
||||
{
|
||||
key: 'seasonPack',
|
||||
get value() {
|
||||
return translate('SeasonPack');
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
interface SelectReleaseTypeModalContentProps {
|
||||
releaseType: ReleaseType;
|
||||
modalTitle: string;
|
||||
onReleaseTypeSelect(releaseType: ReleaseType): void;
|
||||
onModalClose(): void;
|
||||
}
|
||||
|
||||
function SelectReleaseTypeModalContent(
|
||||
props: SelectReleaseTypeModalContentProps
|
||||
) {
|
||||
const { modalTitle, onReleaseTypeSelect, onModalClose } = props;
|
||||
const [releaseType, setReleaseType] = useState(props.releaseType);
|
||||
|
||||
const handleReleaseTypeChange = useCallback(
|
||||
({ value }: { value: string }) => {
|
||||
setReleaseType(value as ReleaseType);
|
||||
},
|
||||
[setReleaseType]
|
||||
);
|
||||
|
||||
const handleReleaseTypeSelect = useCallback(() => {
|
||||
onReleaseTypeSelect(releaseType);
|
||||
}, [releaseType, onReleaseTypeSelect]);
|
||||
|
||||
return (
|
||||
<ModalContent onModalClose={onModalClose}>
|
||||
<ModalHeader>
|
||||
{modalTitle} - {translate('SelectReleaseType')}
|
||||
</ModalHeader>
|
||||
|
||||
<ModalBody>
|
||||
<Form>
|
||||
<FormGroup>
|
||||
<FormLabel>{translate('ReleaseType')}</FormLabel>
|
||||
|
||||
<FormInputGroup
|
||||
type={inputTypes.SELECT}
|
||||
name="releaseType"
|
||||
value={releaseType}
|
||||
values={options}
|
||||
onChange={handleReleaseTypeChange}
|
||||
/>
|
||||
</FormGroup>
|
||||
</Form>
|
||||
</ModalBody>
|
||||
|
||||
<ModalFooter>
|
||||
<Button onPress={onModalClose}>{translate('Cancel')}</Button>
|
||||
|
||||
<Button kind={kinds.SUCCESS} onPress={handleReleaseTypeSelect}>
|
||||
{translate('SelectReleaseType')}
|
||||
</Button>
|
||||
</ModalFooter>
|
||||
</ModalContent>
|
||||
);
|
||||
}
|
||||
|
||||
export default SelectReleaseTypeModalContent;
|
@ -218,6 +218,7 @@
|
||||
"ClickToChangeLanguage": "Click to change language",
|
||||
"ClickToChangeQuality": "Click to change quality",
|
||||
"ClickToChangeReleaseGroup": "Click to change release group",
|
||||
"ClickToChangeReleaseType": "Click to change release type",
|
||||
"ClickToChangeSeason": "Click to change season",
|
||||
"ClickToChangeSeries": "Click to change series",
|
||||
"ClientPriority": "Client Priority",
|
||||
@ -1777,6 +1778,7 @@
|
||||
"SelectLanguages": "Select Languages",
|
||||
"SelectQuality": "Select Quality",
|
||||
"SelectReleaseGroup": "Select Release Group",
|
||||
"SelectReleaseType": "Select Release Type",
|
||||
"SelectSeason": "Select Season",
|
||||
"SelectSeasonModalTitle": "{modalTitle} - Select Season",
|
||||
"SelectSeries": "Select Series",
|
||||
|
@ -123,6 +123,7 @@ public List<ImportResult> Import(List<ImportDecision> decisions, bool newDownloa
|
||||
else
|
||||
{
|
||||
episodeFile.IndexerFlags = localEpisode.IndexerFlags;
|
||||
episodeFile.ReleaseType = localEpisode.ReleaseType;
|
||||
}
|
||||
|
||||
// Fall back to parsed information if history is unavailable or missing
|
||||
|
@ -43,6 +43,7 @@ public object ReprocessItems([FromBody] List<ManualImportReprocessResource> item
|
||||
|
||||
item.SeasonNumber = processedItem.SeasonNumber;
|
||||
item.Episodes = processedItem.Episodes.ToResource();
|
||||
item.ReleaseType = processedItem.ReleaseType;
|
||||
item.IndexerFlags = processedItem.IndexerFlags;
|
||||
item.Rejections = processedItem.Rejections;
|
||||
item.CustomFormats = processedItem.CustomFormats.ToResource(false);
|
||||
|
Loading…
Reference in New Issue
Block a user