1
0
mirror of https://github.com/Radarr/Radarr.git synced 2024-11-04 10:02:40 +01:00

New: Select Multiple Languages on Manual Import

This commit is contained in:
Qstick 2020-01-02 21:11:47 -05:00
parent 4385acef99
commit 02c35f963e
12 changed files with 164 additions and 199 deletions

View File

@ -46,8 +46,8 @@ const columns = [
isVisible: true isVisible: true
}, },
{ {
name: 'language', name: 'languages',
label: 'Language', label: 'Languages',
isSortable: true, isSortable: true,
isVisible: true isVisible: true
}, },

View File

@ -109,7 +109,7 @@ class InteractiveImportModalContentConnector extends Component {
const { const {
movie, movie,
quality, quality,
language languages
} = item; } = item;
if (!movie) { if (!movie) {
@ -122,7 +122,7 @@ class InteractiveImportModalContentConnector extends Component {
return false; return false;
} }
if (!language) { if (!languages) {
this.setState({ interactiveImportErrorMessage: 'Language must be chosen for each selected file' }); this.setState({ interactiveImportErrorMessage: 'Language must be chosen for each selected file' });
return false; return false;
} }
@ -132,7 +132,7 @@ class InteractiveImportModalContentConnector extends Component {
folderName: item.folderName, folderName: item.folderName,
movieId: movie.id, movieId: movie.id,
quality, quality,
languages: [language], languages,
downloadId: this.props.downloadId downloadId: this.props.downloadId
}); });
} }

View File

@ -36,13 +36,13 @@ class InteractiveImportRow extends Component {
id, id,
movie, movie,
quality, quality,
language languages
} = this.props; } = this.props;
if ( if (
movie && movie &&
quality && quality &&
language languages
) { ) {
this.props.onSelectedChange({ id, value: true }); this.props.onSelectedChange({ id, value: true });
} }
@ -53,7 +53,7 @@ class InteractiveImportRow extends Component {
id, id,
movie, movie,
quality, quality,
language, languages,
isSelected, isSelected,
onValidRowChange onValidRowChange
} = this.props; } = this.props;
@ -61,7 +61,7 @@ class InteractiveImportRow extends Component {
if ( if (
prevProps.movie === movie && prevProps.movie === movie &&
prevProps.quality === quality && prevProps.quality === quality &&
prevProps.language === language && prevProps.languages === languages &&
prevProps.isSelected === isSelected prevProps.isSelected === isSelected
) { ) {
return; return;
@ -70,7 +70,7 @@ class InteractiveImportRow extends Component {
const isValid = !!( const isValid = !!(
movie && movie &&
quality && quality &&
language languages
); );
if (isSelected && !isValid) { if (isSelected && !isValid) {
@ -134,7 +134,7 @@ class InteractiveImportRow extends Component {
relativePath, relativePath,
movie, movie,
quality, quality,
language, languages,
size, size,
rejections, rejections,
isSelected, isSelected,
@ -151,9 +151,8 @@ class InteractiveImportRow extends Component {
const showMoviePlaceholder = isSelected && !movie; const showMoviePlaceholder = isSelected && !movie;
const showQualityPlaceholder = isSelected && !quality; const showQualityPlaceholder = isSelected && !quality;
const showLanguagePlaceholder = isSelected && !language; const showLanguagePlaceholder = isSelected && !languages;
// TODO - Placeholder till we implement selection of multiple languages
const languages = [language];
return ( return (
<TableRow> <TableRow>
<TableSelectCell <TableSelectCell
@ -209,7 +208,7 @@ class InteractiveImportRow extends Component {
} }
{ {
!showLanguagePlaceholder && !!language && !showLanguagePlaceholder && !!languages &&
<MovieLanguage <MovieLanguage
className={styles.label} className={styles.label}
languages={languages} languages={languages}
@ -269,7 +268,7 @@ class InteractiveImportRow extends Component {
<SelectLanguageModal <SelectLanguageModal
isOpen={isSelectLanguageModalOpen} isOpen={isSelectLanguageModalOpen}
ids={[id]} ids={[id]}
languageId={language ? language.id : 0} languageIds={languages ? languages.map((l) => l.id) : []}
onModalClose={this.onSelectLanguageModalClose} onModalClose={this.onSelectLanguageModalClose}
/> />
</TableRow> </TableRow>
@ -284,7 +283,7 @@ InteractiveImportRow.propTypes = {
relativePath: PropTypes.string.isRequired, relativePath: PropTypes.string.isRequired,
movie: PropTypes.object, movie: PropTypes.object,
quality: PropTypes.object, quality: PropTypes.object,
language: PropTypes.object, languages: PropTypes.arrayOf(PropTypes.object),
size: PropTypes.number.isRequired, size: PropTypes.number.isRequired,
rejections: PropTypes.arrayOf(PropTypes.object).isRequired, rejections: PropTypes.arrayOf(PropTypes.object).isRequired,
isSelected: PropTypes.bool, isSelected: PropTypes.bool,

View File

@ -1,5 +1,6 @@
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import React, { Component } from 'react'; import React, { Component } from 'react';
import { sizes } from 'Helpers/Props';
import Modal from 'Components/Modal/Modal'; import Modal from 'Components/Modal/Modal';
import SelectLanguageModalContentConnector from './SelectLanguageModalContentConnector'; import SelectLanguageModalContentConnector from './SelectLanguageModalContentConnector';
@ -19,6 +20,7 @@ class SelectLanguageModal extends Component {
<Modal <Modal
isOpen={isOpen} isOpen={isOpen}
onModalClose={onModalClose} onModalClose={onModalClose}
size={sizes.MEDIUM}
> >
<SelectLanguageModalContentConnector <SelectLanguageModalContentConnector
{...otherProps} {...otherProps}

View File

@ -0,0 +1,4 @@
.languageInput {
display: flex;
margin-bottom: 0;
}

View File

@ -1,6 +1,6 @@
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import React from 'react'; import React, { Component } from 'react';
import { inputTypes } from 'Helpers/Props'; import { inputTypes, kinds, sizes } from 'Helpers/Props';
import Button from 'Components/Link/Button'; import Button from 'Components/Link/Button';
import LoadingIndicator from 'Components/Loading/LoadingIndicator'; import LoadingIndicator from 'Components/Loading/LoadingIndicator';
import Form from 'Components/Form/Form'; import Form from 'Components/Form/Form';
@ -11,24 +11,67 @@ import ModalContent from 'Components/Modal/ModalContent';
import ModalHeader from 'Components/Modal/ModalHeader'; import ModalHeader from 'Components/Modal/ModalHeader';
import ModalBody from 'Components/Modal/ModalBody'; import ModalBody from 'Components/Modal/ModalBody';
import ModalFooter from 'Components/Modal/ModalFooter'; import ModalFooter from 'Components/Modal/ModalFooter';
import styles from './SelectLanguageModalContent.css';
class SelectLanguageModalContent extends Component {
//
// Lifecycle
constructor(props, context) {
super(props, context);
function SelectLanguageModalContent(props) {
const { const {
languageId, languageIds
} = props;
this.state = {
languageIds
};
}
//
// Listeners
onLanguageChange = ({ value, name }) => {
const {
languageIds
} = this.state;
const changedId = parseInt(name);
let newLanguages = languageIds;
if (value) {
newLanguages.push(changedId);
}
if (!value) {
newLanguages = languageIds.filter((i) => i !== changedId);
}
this.setState({ languageIds: newLanguages });
}
onLanguageSelect = () => {
this.props.onLanguageSelect(this.state);
}
//
// Render
render() {
const {
isFetching, isFetching,
isPopulated, isPopulated,
error, error,
items, items,
onModalClose, onModalClose
onLanguageSelect } = this.props;
} = props;
const languageOptions = items.map(( language ) => { const {
return { languageIds
key: language.id, } = this.state;
value: language.name
};
});
return ( return (
<ModalContent onModalClose={onModalClose}> <ModalContent onModalClose={onModalClose}>
@ -50,17 +93,25 @@ function SelectLanguageModalContent(props) {
{ {
isPopulated && !error && isPopulated && !error &&
<Form> <Form>
<FormGroup> {
<FormLabel>Language</FormLabel> items.map(( language ) => {
return (
<FormGroup
key={language.id}
size={sizes.EXTRA_SMALL}
className={styles.languageInput}
>
<FormLabel>{language.name}</FormLabel>
<FormInputGroup <FormInputGroup
type={inputTypes.SELECT} type={inputTypes.CHECK}
name="language" name={language.id.toString()}
value={languageId} value={languageIds.includes(language.id)}
values={languageOptions} onChange={this.onLanguageChange}
onChange={onLanguageSelect}
/> />
</FormGroup> </FormGroup>
);
})
}
</Form> </Form>
} }
</ModalBody> </ModalBody>
@ -69,13 +120,21 @@ function SelectLanguageModalContent(props) {
<Button onPress={onModalClose}> <Button onPress={onModalClose}>
Cancel Cancel
</Button> </Button>
<Button
kind={kinds.SUCCESS}
onPress={this.onLanguageSelect}
>
Select Languges
</Button>
</ModalFooter> </ModalFooter>
</ModalContent> </ModalContent>
); );
} }
}
SelectLanguageModalContent.propTypes = { SelectLanguageModalContent.propTypes = {
languageId: PropTypes.number.isRequired, languageIds: PropTypes.arrayOf(PropTypes.number).isRequired,
isFetching: PropTypes.bool.isRequired, isFetching: PropTypes.bool.isRequired,
isPopulated: PropTypes.bool.isRequired, isPopulated: PropTypes.bool.isRequired,
error: PropTypes.object, error: PropTypes.object,
@ -84,4 +143,8 @@ SelectLanguageModalContent.propTypes = {
onModalClose: PropTypes.func.isRequired onModalClose: PropTypes.func.isRequired
}; };
SelectLanguageModalContent.defaultProps = {
languages: []
};
export default SelectLanguageModalContent; export default SelectLanguageModalContent;

View File

@ -47,15 +47,19 @@ class SelectLanguageModalContentConnector extends Component {
// //
// Listeners // Listeners
onLanguageSelect = ({ value }) => { onLanguageSelect = ({ languageIds }) => {
const languageId = parseInt(value); const languages = [];
languageIds.forEach((languageId) => {
const language = _.find(this.props.items, const language = _.find(this.props.items,
(item) => item.id === languageId); (item) => item.id === parseInt(languageId));
languages.push(language);
});
this.props.dispatchUpdateInteractiveImportItems({ this.props.dispatchUpdateInteractiveImportItems({
ids: this.props.ids, ids: this.props.ids,
language languages
}); });
this.props.onModalClose(true); this.props.onModalClose(true);

View File

@ -219,7 +219,7 @@ class MovieFileEditorRow extends Component {
<SelectLanguageModal <SelectLanguageModal
isOpen={isSelectLanguageModalOpen} isOpen={isSelectLanguageModalOpen}
ids={[id]} ids={[id]}
languageId={languages[0] ? languages[0].id : 0} languageIds={languages ? languages.map((l) => l.id) : []}
onModalClose={this.onSelectLanguageModalClose} onModalClose={this.onSelectLanguageModalClose}
/> />
</TableRow> </TableRow>

View File

@ -1,5 +1,4 @@
/* eslint max-params: 0 */ /* eslint max-params: 0 */
import _ from 'lodash';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import React, { Component } from 'react'; import React, { Component } from 'react';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
@ -71,28 +70,6 @@ class MovieFileEditorTableContentConnector extends Component {
// //
// Render // Render
//
// Listeners
onLanguageChange = (movieFileIds, languageId) => {
const language = _.find(this.props.languages, { id: languageId });
// TODO - Placeholder till we implement selection of multiple languages
const languages = [language];
this.props.dispatchUpdateMovieFiles({ movieFileIds, languages });
}
onQualityChange = (movieFileIds, qualityId) => {
const quality = {
quality: _.find(this.props.qualities, { id: qualityId }),
revision: {
version: 1,
real: 0
}
};
this.props.dispatchUpdateMovieFiles({ movieFileIds, quality });
}
render() { render() {
const { const {
dispatchFetchLanguages, dispatchFetchLanguages,
@ -104,8 +81,6 @@ class MovieFileEditorTableContentConnector extends Component {
return ( return (
<MovieFileEditorTableContent <MovieFileEditorTableContent
{...otherProps} {...otherProps}
onLanguageChange={this.onLanguageChange}
onQualityChange={this.onQualityChange}
/> />
); );
} }

View File

@ -1,87 +0,0 @@
import PropTypes from 'prop-types';
import React from 'react';
import { inputTypes } from 'Helpers/Props';
import Button from 'Components/Link/Button';
import LoadingIndicator from 'Components/Loading/LoadingIndicator';
import Form from 'Components/Form/Form';
import FormGroup from 'Components/Form/FormGroup';
import FormLabel from 'Components/Form/FormLabel';
import FormInputGroup from 'Components/Form/FormInputGroup';
import ModalContent from 'Components/Modal/ModalContent';
import ModalHeader from 'Components/Modal/ModalHeader';
import ModalBody from 'Components/Modal/ModalBody';
import ModalFooter from 'Components/Modal/ModalFooter';
function SelectLanguageModalContent(props) {
const {
languageId,
isFetching,
isPopulated,
error,
items,
onModalClose,
onLanguageSelect
} = props;
const languageOptions = items.map(( language ) => {
return {
key: language.id,
value: language.name
};
});
return (
<ModalContent onModalClose={onModalClose}>
<ModalHeader>
Manual Import - Select Language
</ModalHeader>
<ModalBody>
{
isFetching &&
<LoadingIndicator />
}
{
!isFetching && !!error &&
<div>Unable to load languages</div>
}
{
isPopulated && !error &&
<Form>
<FormGroup>
<FormLabel>Language</FormLabel>
<FormInputGroup
type={inputTypes.SELECT}
name="language"
value={languageId}
values={languageOptions}
onChange={onLanguageSelect}
/>
</FormGroup>
</Form>
}
</ModalBody>
<ModalFooter>
<Button onPress={onModalClose}>
Cancel
</Button>
</ModalFooter>
</ModalContent>
);
}
SelectLanguageModalContent.propTypes = {
languageId: PropTypes.number.isRequired,
isFetching: PropTypes.bool.isRequired,
isPopulated: PropTypes.bool.isRequired,
error: PropTypes.object,
items: PropTypes.arrayOf(PropTypes.object).isRequired,
onLanguageSelect: PropTypes.func.isRequired,
onModalClose: PropTypes.func.isRequired
};
export default SelectLanguageModalContent;

View File

@ -5,7 +5,7 @@ import { connect } from 'react-redux';
import { createSelector } from 'reselect'; import { createSelector } from 'reselect';
import { fetchLanguages } from 'Store/Actions/settingsActions'; import { fetchLanguages } from 'Store/Actions/settingsActions';
import { updateMovieFiles } from 'Store/Actions/movieFileActions'; import { updateMovieFiles } from 'Store/Actions/movieFileActions';
import SelectLanguageModalContent from './SelectLanguageModalContent'; import SelectLanguageModalContent from 'InteractiveImport/Language/SelectLanguageModalContent';
function createMapStateToProps() { function createMapStateToProps() {
return createSelector( return createSelector(
@ -47,15 +47,20 @@ class SelectLanguageModalContentConnector extends Component {
// //
// Listeners // Listeners
onLanguageSelect = ({ value }) => { onLanguageSelect = ({ languageIds }) => {
const languageId = parseInt(value); const languages = [];
languageIds.forEach((languageId) => {
const language = _.find(this.props.items, const language = _.find(this.props.items,
(item) => item.id === languageId); (item) => item.id === parseInt(languageId));
const languages = [language];
const movieFileIds = this.props.ids;
this.props.dispatchupdateMovieFiles({ movieFileIds, languages }); languages.push(language);
});
this.props.dispatchupdateMovieFiles({
movieFileIds: this.props.ids,
languages
});
this.props.onModalClose(true); this.props.onModalClose(true);
} }

View File

@ -177,7 +177,7 @@ private ManualImportItem ProcessFile(string rootFolder, string baseFolder, strin
var localMovie = new LocalMovie(); var localMovie = new LocalMovie();
localMovie.Path = file; localMovie.Path = file;
localMovie.Quality = QualityParser.ParseQuality(file); localMovie.Quality = QualityParser.ParseQuality(file);
localMovie.Languages = LanguageParser.ParseLanguages(file); localMovie.Languages = LanguageParser.EnhanceLanguages(file, LanguageParser.ParseLanguages(file));
localMovie.Size = _diskProvider.GetFileSize(file); localMovie.Size = _diskProvider.GetFileSize(file);
return MapItem(new ImportDecision(localMovie, new Rejection("Unknown Movie")), rootFolder, downloadId, null); return MapItem(new ImportDecision(localMovie, new Rejection("Unknown Movie")), rootFolder, downloadId, null);