mirror of
https://github.com/Radarr/Radarr
synced 2026-05-09 11:10:23 +02:00
Fixed: Manual Import language handling
This commit is contained in:
parent
974e44ce48
commit
d1fa92bc6c
15 changed files with 328 additions and 50 deletions
|
|
@ -19,6 +19,7 @@ import { align, icons, kinds, scrollDirections } from 'Helpers/Props';
|
||||||
import SelectLanguageModal from 'InteractiveImport/Language/SelectLanguageModal';
|
import SelectLanguageModal from 'InteractiveImport/Language/SelectLanguageModal';
|
||||||
import SelectMovieModal from 'InteractiveImport/Movie/SelectMovieModal';
|
import SelectMovieModal from 'InteractiveImport/Movie/SelectMovieModal';
|
||||||
import SelectQualityModal from 'InteractiveImport/Quality/SelectQualityModal';
|
import SelectQualityModal from 'InteractiveImport/Quality/SelectQualityModal';
|
||||||
|
import SelectReleaseGroupModal from 'InteractiveImport/ReleaseGroup/SelectReleaseGroupModal';
|
||||||
import getErrorMessage from 'Utilities/Object/getErrorMessage';
|
import getErrorMessage from 'Utilities/Object/getErrorMessage';
|
||||||
import translate from 'Utilities/String/translate';
|
import translate from 'Utilities/String/translate';
|
||||||
import getSelectedIds from 'Utilities/Table/getSelectedIds';
|
import getSelectedIds from 'Utilities/Table/getSelectedIds';
|
||||||
|
|
@ -40,6 +41,11 @@ const columns = [
|
||||||
isSortable: true,
|
isSortable: true,
|
||||||
isVisible: true
|
isVisible: true
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: 'releaseGroup',
|
||||||
|
label: translate('ReleaseGroup'),
|
||||||
|
isVisible: true
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: 'quality',
|
name: 'quality',
|
||||||
label: translate('Quality'),
|
label: translate('Quality'),
|
||||||
|
|
@ -83,6 +89,7 @@ const SELECT = 'select';
|
||||||
const MOVIE = 'movie';
|
const MOVIE = 'movie';
|
||||||
const LANGUAGE = 'language';
|
const LANGUAGE = 'language';
|
||||||
const QUALITY = 'quality';
|
const QUALITY = 'quality';
|
||||||
|
const RELEASE_GROUP = 'releaseGroup';
|
||||||
|
|
||||||
class InteractiveImportModalContent extends Component {
|
class InteractiveImportModalContent extends Component {
|
||||||
|
|
||||||
|
|
@ -202,10 +209,11 @@ class InteractiveImportModalContent extends Component {
|
||||||
const errorMessage = getErrorMessage(error, translate('UnableToLoadManualImportItems'));
|
const errorMessage = getErrorMessage(error, translate('UnableToLoadManualImportItems'));
|
||||||
|
|
||||||
const bulkSelectOptions = [
|
const bulkSelectOptions = [
|
||||||
{
|
{ key: SELECT, value: translate('SelectDotDot'), disabled: true },
|
||||||
key: SELECT, value: translate('SelectDotDot'), disabled: true },
|
|
||||||
{ key: LANGUAGE, value: translate('SelectLanguage') },
|
{ key: LANGUAGE, value: translate('SelectLanguage') },
|
||||||
{ key: QUALITY, value: translate('SelectQuality') }];
|
{ key: QUALITY, value: translate('SelectQuality') },
|
||||||
|
{ key: RELEASE_GROUP, value: translate('SelectReleaseGroup') }
|
||||||
|
];
|
||||||
|
|
||||||
if (allowMovieChange) {
|
if (allowMovieChange) {
|
||||||
bulkSelectOptions.splice(1, 0, {
|
bulkSelectOptions.splice(1, 0, {
|
||||||
|
|
@ -372,6 +380,13 @@ class InteractiveImportModalContent extends Component {
|
||||||
real={false}
|
real={false}
|
||||||
onModalClose={this.onSelectModalClose}
|
onModalClose={this.onSelectModalClose}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
<SelectReleaseGroupModal
|
||||||
|
isOpen={selectModalOpen === RELEASE_GROUP}
|
||||||
|
ids={selectedIds}
|
||||||
|
releaseGroup=""
|
||||||
|
onModalClose={this.onSelectModalClose}
|
||||||
|
/>
|
||||||
</ModalContent>
|
</ModalContent>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -110,7 +110,8 @@ class InteractiveImportModalContentConnector extends Component {
|
||||||
const {
|
const {
|
||||||
movie,
|
movie,
|
||||||
quality,
|
quality,
|
||||||
languages
|
languages,
|
||||||
|
releaseGroup
|
||||||
} = item;
|
} = item;
|
||||||
|
|
||||||
if (!movie) {
|
if (!movie) {
|
||||||
|
|
@ -132,6 +133,7 @@ class InteractiveImportModalContentConnector extends Component {
|
||||||
path: item.path,
|
path: item.path,
|
||||||
folderName: item.folderName,
|
folderName: item.folderName,
|
||||||
movieId: movie.id,
|
movieId: movie.id,
|
||||||
|
releaseGroup,
|
||||||
quality,
|
quality,
|
||||||
languages,
|
languages,
|
||||||
downloadId: this.props.downloadId
|
downloadId: this.props.downloadId
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,7 @@ import { icons, kinds, tooltipPositions } from 'Helpers/Props';
|
||||||
import SelectLanguageModal from 'InteractiveImport/Language/SelectLanguageModal';
|
import SelectLanguageModal from 'InteractiveImport/Language/SelectLanguageModal';
|
||||||
import SelectMovieModal from 'InteractiveImport/Movie/SelectMovieModal';
|
import SelectMovieModal from 'InteractiveImport/Movie/SelectMovieModal';
|
||||||
import SelectQualityModal from 'InteractiveImport/Quality/SelectQualityModal';
|
import SelectQualityModal from 'InteractiveImport/Quality/SelectQualityModal';
|
||||||
|
import SelectReleaseGroupModal from 'InteractiveImport/ReleaseGroup/SelectReleaseGroupModal';
|
||||||
import MovieLanguage from 'Movie/MovieLanguage';
|
import MovieLanguage from 'Movie/MovieLanguage';
|
||||||
import MovieQuality from 'Movie/MovieQuality';
|
import MovieQuality from 'Movie/MovieQuality';
|
||||||
import formatBytes from 'Utilities/Number/formatBytes';
|
import formatBytes from 'Utilities/Number/formatBytes';
|
||||||
|
|
@ -28,6 +29,7 @@ class InteractiveImportRow extends Component {
|
||||||
|
|
||||||
this.state = {
|
this.state = {
|
||||||
isSelectMovieModalOpen: false,
|
isSelectMovieModalOpen: false,
|
||||||
|
isSelectReleaseGroupModalOpen: false,
|
||||||
isSelectQualityModalOpen: false,
|
isSelectQualityModalOpen: false,
|
||||||
isSelectLanguageModalOpen: false
|
isSelectLanguageModalOpen: false
|
||||||
};
|
};
|
||||||
|
|
@ -103,6 +105,10 @@ class InteractiveImportRow extends Component {
|
||||||
this.setState({ isSelectMovieModalOpen: true });
|
this.setState({ isSelectMovieModalOpen: true });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onSelectReleaseGroupPress = () => {
|
||||||
|
this.setState({ isSelectReleaseGroupModalOpen: true });
|
||||||
|
}
|
||||||
|
|
||||||
onSelectQualityPress = () => {
|
onSelectQualityPress = () => {
|
||||||
this.setState({ isSelectQualityModalOpen: true });
|
this.setState({ isSelectQualityModalOpen: true });
|
||||||
}
|
}
|
||||||
|
|
@ -116,6 +122,11 @@ class InteractiveImportRow extends Component {
|
||||||
this.selectRowAfterChange(changed);
|
this.selectRowAfterChange(changed);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onSelectReleaseGroupModalClose = (changed) => {
|
||||||
|
this.setState({ isSelectReleaseGroupModalOpen: false });
|
||||||
|
this.selectRowAfterChange(changed);
|
||||||
|
}
|
||||||
|
|
||||||
onSelectQualityModalClose = (changed) => {
|
onSelectQualityModalClose = (changed) => {
|
||||||
this.setState({ isSelectQualityModalOpen: false });
|
this.setState({ isSelectQualityModalOpen: false });
|
||||||
this.selectRowAfterChange(changed);
|
this.selectRowAfterChange(changed);
|
||||||
|
|
@ -137,6 +148,7 @@ class InteractiveImportRow extends Component {
|
||||||
movie,
|
movie,
|
||||||
quality,
|
quality,
|
||||||
languages,
|
languages,
|
||||||
|
releaseGroup,
|
||||||
size,
|
size,
|
||||||
rejections,
|
rejections,
|
||||||
isReprocessing,
|
isReprocessing,
|
||||||
|
|
@ -147,7 +159,8 @@ class InteractiveImportRow extends Component {
|
||||||
const {
|
const {
|
||||||
isSelectMovieModalOpen,
|
isSelectMovieModalOpen,
|
||||||
isSelectQualityModalOpen,
|
isSelectQualityModalOpen,
|
||||||
isSelectLanguageModalOpen
|
isSelectLanguageModalOpen,
|
||||||
|
isSelectReleaseGroupModalOpen
|
||||||
} = this.state;
|
} = this.state;
|
||||||
|
|
||||||
const movieTitle = movie ? movie.title + ( movie.year > 0 ? ` (${movie.year})` : '') : '';
|
const movieTitle = movie ? movie.title + ( movie.year > 0 ? ` (${movie.year})` : '') : '';
|
||||||
|
|
@ -155,6 +168,7 @@ class InteractiveImportRow extends Component {
|
||||||
const showMoviePlaceholder = isSelected && !movie;
|
const showMoviePlaceholder = isSelected && !movie;
|
||||||
const showQualityPlaceholder = isSelected && !quality;
|
const showQualityPlaceholder = isSelected && !quality;
|
||||||
const showLanguagePlaceholder = isSelected && !languages && !isReprocessing;
|
const showLanguagePlaceholder = isSelected && !languages && !isReprocessing;
|
||||||
|
const showReleaseGroupPlaceholder = isSelected && !releaseGroup;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<TableRow>
|
<TableRow>
|
||||||
|
|
@ -181,6 +195,17 @@ class InteractiveImportRow extends Component {
|
||||||
}
|
}
|
||||||
</TableRowCellButton>
|
</TableRowCellButton>
|
||||||
|
|
||||||
|
<TableRowCellButton
|
||||||
|
title={translate('ClickToChangeReleaseGroup')}
|
||||||
|
onPress={this.onSelectReleaseGroupPress}
|
||||||
|
>
|
||||||
|
{
|
||||||
|
showReleaseGroupPlaceholder ?
|
||||||
|
<InteractiveImportRowCellPlaceholder /> :
|
||||||
|
releaseGroup
|
||||||
|
}
|
||||||
|
</TableRowCellButton>
|
||||||
|
|
||||||
<TableRowCellButton
|
<TableRowCellButton
|
||||||
className={styles.quality}
|
className={styles.quality}
|
||||||
title={translate('ClickToChangeQuality')}
|
title={translate('ClickToChangeQuality')}
|
||||||
|
|
@ -268,6 +293,13 @@ class InteractiveImportRow extends Component {
|
||||||
onModalClose={this.onSelectMovieModalClose}
|
onModalClose={this.onSelectMovieModalClose}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
<SelectReleaseGroupModal
|
||||||
|
isOpen={isSelectReleaseGroupModalOpen}
|
||||||
|
ids={[id]}
|
||||||
|
releaseGroup={releaseGroup ?? ''}
|
||||||
|
onModalClose={this.onSelectReleaseGroupModalClose}
|
||||||
|
/>
|
||||||
|
|
||||||
<SelectQualityModal
|
<SelectQualityModal
|
||||||
isOpen={isSelectQualityModalOpen}
|
isOpen={isSelectQualityModalOpen}
|
||||||
ids={[id]}
|
ids={[id]}
|
||||||
|
|
@ -296,6 +328,7 @@ InteractiveImportRow.propTypes = {
|
||||||
movie: PropTypes.object,
|
movie: PropTypes.object,
|
||||||
quality: PropTypes.object,
|
quality: PropTypes.object,
|
||||||
languages: PropTypes.arrayOf(PropTypes.object),
|
languages: PropTypes.arrayOf(PropTypes.object),
|
||||||
|
releaseGroup: PropTypes.string,
|
||||||
size: PropTypes.number.isRequired,
|
size: PropTypes.number.isRequired,
|
||||||
rejections: PropTypes.arrayOf(PropTypes.object).isRequired,
|
rejections: PropTypes.arrayOf(PropTypes.object).isRequired,
|
||||||
isReprocessing: PropTypes.bool,
|
isReprocessing: PropTypes.bool,
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,37 @@
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import React, { Component } from 'react';
|
||||||
|
import Modal from 'Components/Modal/Modal';
|
||||||
|
import SelectReleaseGroupModalContentConnector from './SelectReleaseGroupModalContentConnector';
|
||||||
|
|
||||||
|
class SelectReleaseGroupModal extends Component {
|
||||||
|
|
||||||
|
//
|
||||||
|
// Render
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const {
|
||||||
|
isOpen,
|
||||||
|
onModalClose,
|
||||||
|
...otherProps
|
||||||
|
} = this.props;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Modal
|
||||||
|
isOpen={isOpen}
|
||||||
|
onModalClose={onModalClose}
|
||||||
|
>
|
||||||
|
<SelectReleaseGroupModalContentConnector
|
||||||
|
{...otherProps}
|
||||||
|
onModalClose={onModalClose}
|
||||||
|
/>
|
||||||
|
</Modal>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SelectReleaseGroupModal.propTypes = {
|
||||||
|
isOpen: PropTypes.bool.isRequired,
|
||||||
|
onModalClose: PropTypes.func.isRequired
|
||||||
|
};
|
||||||
|
|
||||||
|
export default SelectReleaseGroupModal;
|
||||||
|
|
@ -0,0 +1,99 @@
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import React, { Component } 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 translate from 'Utilities/String/translate';
|
||||||
|
|
||||||
|
class SelectReleaseGroupModalContent extends Component {
|
||||||
|
|
||||||
|
//
|
||||||
|
// Lifecycle
|
||||||
|
|
||||||
|
constructor(props, context) {
|
||||||
|
super(props, context);
|
||||||
|
|
||||||
|
const {
|
||||||
|
releaseGroup
|
||||||
|
} = props;
|
||||||
|
|
||||||
|
this.state = {
|
||||||
|
releaseGroup
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Listeners
|
||||||
|
|
||||||
|
onReleaseGroupChange = ({ value }) => {
|
||||||
|
this.setState({ releaseGroup: value });
|
||||||
|
}
|
||||||
|
|
||||||
|
onReleaseGroupSelect = () => {
|
||||||
|
this.props.onReleaseGroupSelect(this.state);
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Render
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const {
|
||||||
|
onModalClose
|
||||||
|
} = this.props;
|
||||||
|
|
||||||
|
const {
|
||||||
|
releaseGroup
|
||||||
|
} = this.state;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ModalContent onModalClose={onModalClose}>
|
||||||
|
<ModalHeader>
|
||||||
|
{translate('ManualImportSetReleaseGroup')}
|
||||||
|
</ModalHeader>
|
||||||
|
|
||||||
|
<ModalBody>
|
||||||
|
<Form>
|
||||||
|
<FormGroup>
|
||||||
|
<FormLabel>{translate('ReleaseGroup')}</FormLabel>
|
||||||
|
|
||||||
|
<FormInputGroup
|
||||||
|
type={inputTypes.TEXT}
|
||||||
|
name="releaseGroup"
|
||||||
|
value={releaseGroup}
|
||||||
|
onChange={this.onReleaseGroupChange}
|
||||||
|
/>
|
||||||
|
</FormGroup>
|
||||||
|
</Form>
|
||||||
|
</ModalBody>
|
||||||
|
|
||||||
|
<ModalFooter>
|
||||||
|
<Button onPress={onModalClose}>
|
||||||
|
Cancel
|
||||||
|
</Button>
|
||||||
|
|
||||||
|
<Button
|
||||||
|
kind={kinds.SUCCESS}
|
||||||
|
onPress={this.onReleaseGroupSelect}
|
||||||
|
>
|
||||||
|
Set Release Group
|
||||||
|
</Button>
|
||||||
|
</ModalFooter>
|
||||||
|
</ModalContent>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SelectReleaseGroupModalContent.propTypes = {
|
||||||
|
releaseGroup: PropTypes.string.isRequired,
|
||||||
|
onReleaseGroupSelect: PropTypes.func.isRequired,
|
||||||
|
onModalClose: PropTypes.func.isRequired
|
||||||
|
};
|
||||||
|
|
||||||
|
export default SelectReleaseGroupModalContent;
|
||||||
|
|
@ -0,0 +1,54 @@
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import React, { Component } from 'react';
|
||||||
|
import { connect } from 'react-redux';
|
||||||
|
import { reprocessInteractiveImportItems, updateInteractiveImportItems } from 'Store/Actions/interactiveImportActions';
|
||||||
|
import SelectReleaseGroupModalContent from './SelectReleaseGroupModalContent';
|
||||||
|
|
||||||
|
const mapDispatchToProps = {
|
||||||
|
dispatchUpdateInteractiveImportItems: updateInteractiveImportItems,
|
||||||
|
dispatchReprocessInteractiveImportItems: reprocessInteractiveImportItems
|
||||||
|
};
|
||||||
|
|
||||||
|
class SelectReleaseGroupModalContentConnector extends Component {
|
||||||
|
|
||||||
|
//
|
||||||
|
// Listeners
|
||||||
|
|
||||||
|
onReleaseGroupSelect = ({ releaseGroup }) => {
|
||||||
|
const {
|
||||||
|
ids,
|
||||||
|
dispatchUpdateInteractiveImportItems,
|
||||||
|
dispatchReprocessInteractiveImportItems
|
||||||
|
} = this.props;
|
||||||
|
|
||||||
|
dispatchUpdateInteractiveImportItems({
|
||||||
|
ids,
|
||||||
|
releaseGroup
|
||||||
|
});
|
||||||
|
|
||||||
|
dispatchReprocessInteractiveImportItems({ ids });
|
||||||
|
|
||||||
|
this.props.onModalClose(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Render
|
||||||
|
|
||||||
|
render() {
|
||||||
|
return (
|
||||||
|
<SelectReleaseGroupModalContent
|
||||||
|
{...this.props}
|
||||||
|
onReleaseGroupSelect={this.onReleaseGroupSelect}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SelectReleaseGroupModalContentConnector.propTypes = {
|
||||||
|
ids: PropTypes.arrayOf(PropTypes.number).isRequired,
|
||||||
|
dispatchUpdateInteractiveImportItems: PropTypes.func.isRequired,
|
||||||
|
dispatchReprocessInteractiveImportItems: PropTypes.func.isRequired,
|
||||||
|
onModalClose: PropTypes.func.isRequired
|
||||||
|
};
|
||||||
|
|
||||||
|
export default connect(null, mapDispatchToProps)(SelectReleaseGroupModalContentConnector);
|
||||||
|
|
@ -148,9 +148,10 @@ export const actionHandlers = handleThunks({
|
||||||
return {
|
return {
|
||||||
id,
|
id,
|
||||||
path: item.path,
|
path: item.path,
|
||||||
movieId: item.movie.id,
|
movieId: item.movie ? item.movie.id : undefined,
|
||||||
quality: item.quality,
|
quality: item.quality,
|
||||||
languages: item.languages,
|
languages: item.languages,
|
||||||
|
releaseGroup: item.releaseGroup,
|
||||||
downloadId: item.downloadId
|
downloadId: item.downloadId
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -128,6 +128,7 @@
|
||||||
"ClickToChangeLanguage": "Click to change language",
|
"ClickToChangeLanguage": "Click to change language",
|
||||||
"ClickToChangeMovie": "Click to change movie",
|
"ClickToChangeMovie": "Click to change movie",
|
||||||
"ClickToChangeQuality": "Click to change quality",
|
"ClickToChangeQuality": "Click to change quality",
|
||||||
|
"ClickToChangeReleaseGroup": "Click to change release group",
|
||||||
"ClientPriority": "Client Priority",
|
"ClientPriority": "Client Priority",
|
||||||
"CloneCustomFormat": "Clone Custom Format",
|
"CloneCustomFormat": "Clone Custom Format",
|
||||||
"CloneFormatTag": "Clone Format Tag",
|
"CloneFormatTag": "Clone Format Tag",
|
||||||
|
|
@ -491,6 +492,7 @@
|
||||||
"ManualImportSelectLanguage": "Manual Import - Select Language",
|
"ManualImportSelectLanguage": "Manual Import - Select Language",
|
||||||
"ManualImportSelectMovie": "Manual Import - Select Movie",
|
"ManualImportSelectMovie": "Manual Import - Select Movie",
|
||||||
"ManualImportSelectQuality": " Manual Import - Select Quality",
|
"ManualImportSelectQuality": " Manual Import - Select Quality",
|
||||||
|
"ManualImportSetReleaseGroup": "Manual Import - Set Release Group",
|
||||||
"MappedDrivesRunningAsService": "Mapped network drives are not available when running as a Windows Service. Please see the FAQ for more information",
|
"MappedDrivesRunningAsService": "Mapped network drives are not available when running as a Windows Service. Please see the FAQ for more information",
|
||||||
"MarkAsFailed": "Mark as Failed",
|
"MarkAsFailed": "Mark as Failed",
|
||||||
"MarkAsFailedMessageText": "Are you sure you want to mark '{0}' as failed?",
|
"MarkAsFailedMessageText": "Are you sure you want to mark '{0}' as failed?",
|
||||||
|
|
@ -881,6 +883,7 @@
|
||||||
"SelectLanguges": "Select Languages",
|
"SelectLanguges": "Select Languages",
|
||||||
"SelectMovie": "Select Movie",
|
"SelectMovie": "Select Movie",
|
||||||
"SelectQuality": "Select Quality",
|
"SelectQuality": "Select Quality",
|
||||||
|
"SelectReleaseGroup": "Select Release Group",
|
||||||
"SendAnonymousUsageData": "Send Anonymous Usage Data",
|
"SendAnonymousUsageData": "Send Anonymous Usage Data",
|
||||||
"SetPermissions": "Set Permissions",
|
"SetPermissions": "Set Permissions",
|
||||||
"SetPermissionsLinuxHelpText": "Should chmod be run when files are imported/renamed?",
|
"SetPermissionsLinuxHelpText": "Should chmod be run when files are imported/renamed?",
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,7 @@ public AggregateLanguage(IEnumerable<IAugmentLanguage> augmentLanguages,
|
||||||
|
|
||||||
public LocalMovie Aggregate(LocalMovie localMovie, DownloadClientItem downloadClientItem, bool otherFiles)
|
public LocalMovie Aggregate(LocalMovie localMovie, DownloadClientItem downloadClientItem, bool otherFiles)
|
||||||
{
|
{
|
||||||
var languages = new List<Language> { localMovie.Movie.OriginalLanguage ?? Language.Unknown };
|
var languages = new List<Language> { localMovie.Movie?.OriginalLanguage ?? Language.Unknown };
|
||||||
var languagesConfidence = Confidence.Default;
|
var languagesConfidence = Confidence.Default;
|
||||||
|
|
||||||
foreach (var augmentLanguage in _augmentLanguages)
|
foreach (var augmentLanguage in _augmentLanguages)
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,7 @@ public class ManualImportFile : IEquatable<ManualImportFile>
|
||||||
public string FolderName { get; set; }
|
public string FolderName { get; set; }
|
||||||
public QualityModel Quality { get; set; }
|
public QualityModel Quality { get; set; }
|
||||||
public List<Language> Languages { get; set; }
|
public List<Language> Languages { get; set; }
|
||||||
|
public string ReleaseGroup { get; set; }
|
||||||
public string DownloadId { get; set; }
|
public string DownloadId { get; set; }
|
||||||
public int MovieId { get; set; }
|
public int MovieId { get; set; }
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,7 @@ public class ManualImportItem
|
||||||
public long Size { get; set; }
|
public long Size { get; set; }
|
||||||
public QualityModel Quality { get; set; }
|
public QualityModel Quality { get; set; }
|
||||||
public List<Language> Languages { get; set; }
|
public List<Language> Languages { get; set; }
|
||||||
|
public string ReleaseGroup { get; set; }
|
||||||
public string DownloadId { get; set; }
|
public string DownloadId { get; set; }
|
||||||
public IEnumerable<Rejection> Rejections { get; set; }
|
public IEnumerable<Rejection> Rejections { get; set; }
|
||||||
public Movie Movie { get; set; }
|
public Movie Movie { get; set; }
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
|
@ -22,7 +23,7 @@ namespace NzbDrone.Core.MediaFiles.MovieImport.Manual
|
||||||
public interface IManualImportService
|
public interface IManualImportService
|
||||||
{
|
{
|
||||||
List<ManualImportItem> GetMediaFiles(string path, string downloadId, int? movieId, bool filterExistingFiles);
|
List<ManualImportItem> GetMediaFiles(string path, string downloadId, int? movieId, bool filterExistingFiles);
|
||||||
ManualImportItem ReprocessItem(string path, string downloadId, int movieId, QualityModel quality, List<Language> languages);
|
ManualImportItem ReprocessItem(string path, string downloadId, int movieId, string releaseGroup, QualityModel quality, List<Language> languages);
|
||||||
}
|
}
|
||||||
|
|
||||||
public class ManualImportService : IExecute<ManualImportCommand>, IManualImportService
|
public class ManualImportService : IExecute<ManualImportCommand>, IManualImportService
|
||||||
|
|
@ -92,13 +93,21 @@ public List<ManualImportItem> GetMediaFiles(string path, string downloadId, int?
|
||||||
return ProcessFolder(path, path, downloadId, movieId, filterExistingFiles);
|
return ProcessFolder(path, path, downloadId, movieId, filterExistingFiles);
|
||||||
}
|
}
|
||||||
|
|
||||||
public ManualImportItem ReprocessItem(string path, string downloadId, int movieId, QualityModel quality, List<Language> languages)
|
public ManualImportItem ReprocessItem(string path, string downloadId, int movieId, string releaseGroup, QualityModel quality, List<Language> languages)
|
||||||
{
|
{
|
||||||
var rootFolder = Path.GetDirectoryName(path);
|
var rootFolder = Path.GetDirectoryName(path);
|
||||||
var movie = _movieService.GetMovie(movieId);
|
var movie = _movieService.GetMovie(movieId);
|
||||||
|
|
||||||
var downloadClientItem = GetTrackedDownload(downloadId)?.DownloadItem;
|
var downloadClientItem = GetTrackedDownload(downloadId)?.DownloadItem;
|
||||||
|
|
||||||
|
var languageParse = LanguageParser.ParseLanguages(path);
|
||||||
|
|
||||||
|
if (languageParse.Count <= 1 && languageParse.First() == Language.Unknown && movie != null)
|
||||||
|
{
|
||||||
|
languageParse = new List<Language> { movie.OriginalLanguage };
|
||||||
|
_logger.Debug("Language couldn't be parsed from release, fallback to movie original language: {0}", movie.OriginalLanguage.Name);
|
||||||
|
}
|
||||||
|
|
||||||
var localEpisode = new LocalMovie
|
var localEpisode = new LocalMovie
|
||||||
{
|
{
|
||||||
Movie = movie,
|
Movie = movie,
|
||||||
|
|
@ -108,8 +117,9 @@ public ManualImportItem ReprocessItem(string path, string downloadId, int movieI
|
||||||
SceneSource = SceneSource(movie, rootFolder),
|
SceneSource = SceneSource(movie, rootFolder),
|
||||||
ExistingFile = movie.Path.IsParentPath(path),
|
ExistingFile = movie.Path.IsParentPath(path),
|
||||||
Size = _diskProvider.GetFileSize(path),
|
Size = _diskProvider.GetFileSize(path),
|
||||||
Languages = (languages?.SingleOrDefault() ?? Language.Unknown) == Language.Unknown ? LanguageParser.ParseLanguages(path) : languages,
|
Languages = (languages?.SingleOrDefault() ?? Language.Unknown) == Language.Unknown ? languageParse : languages,
|
||||||
Quality = quality.Quality == Quality.Unknown ? QualityParser.ParseQuality(path) : quality
|
Quality = quality.Quality == Quality.Unknown ? QualityParser.ParseQuality(path) : quality,
|
||||||
|
ReleaseGroup = releaseGroup.IsNullOrWhiteSpace() ? Parser.Parser.ParseReleaseGroup(path) : releaseGroup,
|
||||||
};
|
};
|
||||||
|
|
||||||
return MapItem(_importDecisionMaker.GetDecision(localEpisode, downloadClientItem), rootFolder, downloadId, null);
|
return MapItem(_importDecisionMaker.GetDecision(localEpisode, downloadClientItem), rootFolder, downloadId, null);
|
||||||
|
|
@ -118,6 +128,7 @@ public ManualImportItem ReprocessItem(string path, string downloadId, int movieI
|
||||||
private List<ManualImportItem> ProcessFolder(string rootFolder, string baseFolder, string downloadId, int? movieId, bool filterExistingFiles)
|
private List<ManualImportItem> ProcessFolder(string rootFolder, string baseFolder, string downloadId, int? movieId, bool filterExistingFiles)
|
||||||
{
|
{
|
||||||
DownloadClientItem downloadClientItem = null;
|
DownloadClientItem downloadClientItem = null;
|
||||||
|
|
||||||
var directoryInfo = new DirectoryInfo(baseFolder);
|
var directoryInfo = new DirectoryInfo(baseFolder);
|
||||||
|
|
||||||
var movie = movieId.HasValue ?
|
var movie = movieId.HasValue ?
|
||||||
|
|
@ -165,50 +176,58 @@ private List<ManualImportItem> ProcessFolder(string rootFolder, string baseFolde
|
||||||
|
|
||||||
private ManualImportItem ProcessFile(string rootFolder, string baseFolder, string file, string downloadId, Movie movie = null)
|
private ManualImportItem ProcessFile(string rootFolder, string baseFolder, string file, string downloadId, Movie movie = null)
|
||||||
{
|
{
|
||||||
var trackedDownload = GetTrackedDownload(downloadId);
|
try
|
||||||
var relativeFile = baseFolder.GetRelativePath(file);
|
|
||||||
|
|
||||||
if (movie == null)
|
|
||||||
{
|
{
|
||||||
movie = _parsingService.GetMovie(relativeFile.Split('\\', '/')[0]);
|
var trackedDownload = GetTrackedDownload(downloadId);
|
||||||
}
|
var relativeFile = baseFolder.GetRelativePath(file);
|
||||||
|
|
||||||
if (movie == null)
|
if (movie == null)
|
||||||
{
|
|
||||||
movie = _parsingService.GetMovie(relativeFile);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (trackedDownload != null && movie == null)
|
|
||||||
{
|
|
||||||
movie = trackedDownload?.RemoteMovie?.Movie;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (movie == null)
|
|
||||||
{
|
|
||||||
var relativeParseInfo = Parser.Parser.ParseMoviePath(relativeFile);
|
|
||||||
|
|
||||||
if (relativeParseInfo != null)
|
|
||||||
{
|
{
|
||||||
movie = _movieService.FindByTitle(relativeParseInfo.PrimaryMovieTitle, relativeParseInfo.Year);
|
movie = _parsingService.GetMovie(relativeFile.Split('\\', '/')[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (movie == null)
|
||||||
|
{
|
||||||
|
movie = _parsingService.GetMovie(relativeFile);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (trackedDownload != null && movie == null)
|
||||||
|
{
|
||||||
|
movie = trackedDownload?.RemoteMovie?.Movie;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (movie == null)
|
||||||
|
{
|
||||||
|
var relativeParseInfo = Parser.Parser.ParseMoviePath(relativeFile);
|
||||||
|
|
||||||
|
if (relativeParseInfo != null)
|
||||||
|
{
|
||||||
|
movie = _movieService.FindByTitle(relativeParseInfo.PrimaryMovieTitle, relativeParseInfo.Year);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (movie == null)
|
||||||
|
{
|
||||||
|
var localMovie = new LocalMovie();
|
||||||
|
localMovie.Path = file;
|
||||||
|
localMovie.FileMovieInfo = Parser.Parser.ParseMoviePath(file);
|
||||||
|
localMovie.DownloadClientMovieInfo = trackedDownload?.RemoteMovie?.ParsedMovieInfo;
|
||||||
|
|
||||||
|
localMovie = _aggregationService.Augment(localMovie, null, false);
|
||||||
|
|
||||||
|
return MapItem(new ImportDecision(localMovie, new Rejection("Unknown Movie")), rootFolder, downloadId, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
var importDecisions = _importDecisionMaker.GetImportDecisions(new List<string> { file }, movie, trackedDownload?.DownloadItem, null, SceneSource(movie, baseFolder));
|
||||||
|
|
||||||
|
if (importDecisions.Any())
|
||||||
|
{
|
||||||
|
return MapItem(importDecisions.First(), rootFolder, downloadId, null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
catch (Exception ex)
|
||||||
if (movie == null)
|
|
||||||
{
|
{
|
||||||
var localMovie = new LocalMovie();
|
_logger.Warn(ex, "Failed to process file: {0}", file);
|
||||||
localMovie.Path = file;
|
|
||||||
localMovie.Quality = QualityParser.ParseQuality(file);
|
|
||||||
localMovie.Languages = LanguageParser.ParseLanguages(file);
|
|
||||||
localMovie.Size = _diskProvider.GetFileSize(file);
|
|
||||||
|
|
||||||
return MapItem(new ImportDecision(localMovie, new Rejection("Unknown Movie")), rootFolder, downloadId, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
var importDecisions = _importDecisionMaker.GetImportDecisions(new List<string> { file }, movie, trackedDownload?.DownloadItem, null, SceneSource(movie, baseFolder));
|
|
||||||
|
|
||||||
if (importDecisions.Any())
|
|
||||||
{
|
|
||||||
return MapItem(importDecisions.First(), rootFolder, downloadId, null);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return new ManualImportItem
|
return new ManualImportItem
|
||||||
|
|
@ -231,6 +250,7 @@ private List<ManualImportItem> ProcessDownloadDirectory(string rootFolder, List<
|
||||||
localEpisode.Path = file;
|
localEpisode.Path = file;
|
||||||
localEpisode.Quality = new QualityModel(Quality.Unknown);
|
localEpisode.Quality = new QualityModel(Quality.Unknown);
|
||||||
localEpisode.Languages = new List<Language> { Language.Unknown };
|
localEpisode.Languages = new List<Language> { Language.Unknown };
|
||||||
|
localEpisode.ReleaseGroup = Parser.Parser.ParseReleaseGroup(file);
|
||||||
localEpisode.Size = _diskProvider.GetFileSize(file);
|
localEpisode.Size = _diskProvider.GetFileSize(file);
|
||||||
|
|
||||||
items.Add(MapItem(new ImportDecision(localEpisode), rootFolder, null, null));
|
items.Add(MapItem(new ImportDecision(localEpisode), rootFolder, null, null));
|
||||||
|
|
@ -274,6 +294,7 @@ private ManualImportItem MapItem(ImportDecision decision, string rootFolder, str
|
||||||
item.Quality = decision.LocalMovie.Quality;
|
item.Quality = decision.LocalMovie.Quality;
|
||||||
item.Size = _diskProvider.GetFileSize(decision.LocalMovie.Path);
|
item.Size = _diskProvider.GetFileSize(decision.LocalMovie.Path);
|
||||||
item.Languages = decision.LocalMovie.Languages;
|
item.Languages = decision.LocalMovie.Languages;
|
||||||
|
item.ReleaseGroup = decision.LocalMovie.ReleaseGroup;
|
||||||
item.Rejections = decision.Rejections;
|
item.Rejections = decision.Rejections;
|
||||||
|
|
||||||
return item;
|
return item;
|
||||||
|
|
@ -303,6 +324,7 @@ public void Execute(ManualImportCommand message)
|
||||||
Path = file.Path,
|
Path = file.Path,
|
||||||
Quality = file.Quality,
|
Quality = file.Quality,
|
||||||
Languages = file.Languages,
|
Languages = file.Languages,
|
||||||
|
ReleaseGroup = file.ReleaseGroup,
|
||||||
Movie = movie,
|
Movie = movie,
|
||||||
Size = 0
|
Size = 0
|
||||||
};
|
};
|
||||||
|
|
@ -325,6 +347,7 @@ public void Execute(ManualImportCommand message)
|
||||||
localMovie.Movie = movie;
|
localMovie.Movie = movie;
|
||||||
localMovie.Quality = file.Quality;
|
localMovie.Quality = file.Quality;
|
||||||
localMovie.Languages = file.Languages;
|
localMovie.Languages = file.Languages;
|
||||||
|
localMovie.ReleaseGroup = file.ReleaseGroup;
|
||||||
|
|
||||||
//TODO: Cleanup non-tracked downloads
|
//TODO: Cleanup non-tracked downloads
|
||||||
var importDecision = new ImportDecision(localMovie);
|
var importDecision = new ImportDecision(localMovie);
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using NzbDrone.Common.Extensions;
|
||||||
using NzbDrone.Core.Languages;
|
using NzbDrone.Core.Languages;
|
||||||
using NzbDrone.Core.MediaFiles.MovieImport.Manual;
|
using NzbDrone.Core.MediaFiles.MovieImport.Manual;
|
||||||
using NzbDrone.Core.Qualities;
|
using NzbDrone.Core.Qualities;
|
||||||
|
|
@ -30,7 +31,7 @@ public object ReprocessItems([FromBody] List<ManualImportReprocessResource> item
|
||||||
{
|
{
|
||||||
foreach (var item in items)
|
foreach (var item in items)
|
||||||
{
|
{
|
||||||
var processedItem = _manualImportService.ReprocessItem(item.Path, item.DownloadId, item.MovieId, item.Quality, item.Languages);
|
var processedItem = _manualImportService.ReprocessItem(item.Path, item.DownloadId, item.MovieId, item.ReleaseGroup, item.Quality, item.Languages);
|
||||||
|
|
||||||
item.Movie = processedItem.Movie.ToResource(0);
|
item.Movie = processedItem.Movie.ToResource(0);
|
||||||
item.Rejections = processedItem.Rejections;
|
item.Rejections = processedItem.Rejections;
|
||||||
|
|
@ -43,6 +44,11 @@ public object ReprocessItems([FromBody] List<ManualImportReprocessResource> item
|
||||||
{
|
{
|
||||||
item.Quality = processedItem.Quality;
|
item.Quality = processedItem.Quality;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (item.ReleaseGroup.IsNotNullOrWhiteSpace())
|
||||||
|
{
|
||||||
|
item.ReleaseGroup = processedItem.ReleaseGroup;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return items;
|
return items;
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,7 @@ public class ManualImportReprocessResource : RestResource
|
||||||
public MovieResource Movie { get; set; }
|
public MovieResource Movie { get; set; }
|
||||||
public QualityModel Quality { get; set; }
|
public QualityModel Quality { get; set; }
|
||||||
public List<Language> Languages { get; set; }
|
public List<Language> Languages { get; set; }
|
||||||
|
public string ReleaseGroup { get; set; }
|
||||||
public string DownloadId { get; set; }
|
public string DownloadId { get; set; }
|
||||||
|
|
||||||
public IEnumerable<Rejection> Rejections { get; set; }
|
public IEnumerable<Rejection> Rejections { get; set; }
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,7 @@ public class ManualImportResource : RestResource
|
||||||
public MovieResource Movie { get; set; }
|
public MovieResource Movie { get; set; }
|
||||||
public QualityModel Quality { get; set; }
|
public QualityModel Quality { get; set; }
|
||||||
public List<Language> Languages { get; set; }
|
public List<Language> Languages { get; set; }
|
||||||
|
public string ReleaseGroup { get; set; }
|
||||||
public int QualityWeight { get; set; }
|
public int QualityWeight { get; set; }
|
||||||
public string DownloadId { get; set; }
|
public string DownloadId { get; set; }
|
||||||
public IEnumerable<Rejection> Rejections { get; set; }
|
public IEnumerable<Rejection> Rejections { get; set; }
|
||||||
|
|
@ -45,6 +46,7 @@ public static ManualImportResource ToResource(this ManualImportItem model)
|
||||||
Movie = model.Movie.ToResource(0),
|
Movie = model.Movie.ToResource(0),
|
||||||
Quality = model.Quality,
|
Quality = model.Quality,
|
||||||
Languages = model.Languages,
|
Languages = model.Languages,
|
||||||
|
ReleaseGroup = model.ReleaseGroup,
|
||||||
|
|
||||||
//QualityWeight
|
//QualityWeight
|
||||||
DownloadId = model.DownloadId,
|
DownloadId = model.DownloadId,
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue