mirror of https://github.com/lidarr/Lidarr
[UI Work] Artist Detail Page, Album Studio, Wanted, NavSearch, Rename
This commit is contained in:
parent
456ead09da
commit
0054226307
|
@ -58,7 +58,7 @@ class AddNewArtistSearchResult extends Component {
|
||||||
isSmallScreen
|
isSmallScreen
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
|
||||||
const linkProps = isExistingArtist ? { to: `/series/${nameSlug}` } : { onPress: this.onPress };
|
const linkProps = isExistingArtist ? { to: `/artist/${nameSlug}` } : { onPress: this.onPress };
|
||||||
let seasons = '1 Season';
|
let seasons = '1 Season';
|
||||||
|
|
||||||
if (seasonCount > 1) {
|
if (seasonCount > 1) {
|
||||||
|
|
|
@ -4,7 +4,7 @@ import React, { Component } from 'react';
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import { createSelector } from 'reselect';
|
import { createSelector } from 'reselect';
|
||||||
import { queueLookupSeries, setImportArtistValue } from 'Store/Actions/importArtistActions';
|
import { queueLookupSeries, setImportArtistValue } from 'Store/Actions/importArtistActions';
|
||||||
import createAllSeriesSelector from 'Store/Selectors/createAllSeriesSelector';
|
import createAllArtistSelector from 'Store/Selectors/createAllArtistSelector';
|
||||||
import ImportArtistRow from './ImportArtistRow';
|
import ImportArtistRow from './ImportArtistRow';
|
||||||
|
|
||||||
function createImportArtistItemSelector() {
|
function createImportArtistItemSelector() {
|
||||||
|
@ -20,7 +20,7 @@ function createImportArtistItemSelector() {
|
||||||
function createMapStateToProps() {
|
function createMapStateToProps() {
|
||||||
return createSelector(
|
return createSelector(
|
||||||
createImportArtistItemSelector(),
|
createImportArtistItemSelector(),
|
||||||
createAllSeriesSelector(),
|
createAllArtistSelector(),
|
||||||
(item, series) => {
|
(item, series) => {
|
||||||
const selectedSeries = item && item.selectedSeries;
|
const selectedSeries = item && item.selectedSeries;
|
||||||
const isExistingArtist = !!selectedSeries && _.some(series, { foreignArtistId: selectedSeries.foreignArtistId });
|
const isExistingArtist = !!selectedSeries && _.some(series, { foreignArtistId: selectedSeries.foreignArtistId });
|
||||||
|
|
|
@ -76,7 +76,7 @@ class ImportArtistTable extends Component {
|
||||||
const isSelected = selectedState[id];
|
const isSelected = selectedState[id];
|
||||||
|
|
||||||
const isExistingArtist = !!selectedSeries &&
|
const isExistingArtist = !!selectedSeries &&
|
||||||
_.some(prevProps.allSeries, { tvdbId: selectedSeries.tvdbId });
|
_.some(prevProps.allSeries, { foreignArtistId: selectedSeries.foreignArtistId });
|
||||||
|
|
||||||
// Props doesn't have a selected series or
|
// Props doesn't have a selected series or
|
||||||
// the selected series is an existing series.
|
// the selected series is an existing series.
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import { createSelector } from 'reselect';
|
import { createSelector } from 'reselect';
|
||||||
import { queueLookupSeries, setImportArtistValue } from 'Store/Actions/importArtistActions';
|
import { queueLookupSeries, setImportArtistValue } from 'Store/Actions/importArtistActions';
|
||||||
import createAllSeriesSelector from 'Store/Selectors/createAllSeriesSelector';
|
import createAllArtistSelector from 'Store/Selectors/createAllArtistSelector';
|
||||||
import ImportArtistTable from './ImportArtistTable';
|
import ImportArtistTable from './ImportArtistTable';
|
||||||
|
|
||||||
function createMapStateToProps() {
|
function createMapStateToProps() {
|
||||||
|
@ -9,7 +9,7 @@ function createMapStateToProps() {
|
||||||
(state) => state.addArtist,
|
(state) => state.addArtist,
|
||||||
(state) => state.importArtist,
|
(state) => state.importArtist,
|
||||||
(state) => state.app.dimensions,
|
(state) => state.app.dimensions,
|
||||||
createAllSeriesSelector(),
|
createAllArtistSelector(),
|
||||||
(addArtist, importArtist, dimensions, allSeries) => {
|
(addArtist, importArtist, dimensions, allSeries) => {
|
||||||
return {
|
return {
|
||||||
defaultMonitor: addArtist.defaults.monitor,
|
defaultMonitor: addArtist.defaults.monitor,
|
||||||
|
|
|
@ -99,10 +99,10 @@ class ImportArtistSelectArtist extends Component {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
onSeriesSelect = (tvdbId) => {
|
onSeriesSelect = (foreignArtistId) => {
|
||||||
this.setState({ isOpen: false });
|
this.setState({ isOpen: false });
|
||||||
|
|
||||||
this.props.onSeriesSelect(tvdbId);
|
this.props.onSeriesSelect(foreignArtistId);
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
|
|
|
@ -15,8 +15,8 @@ import FilterMenuItem from 'Components/Menu/FilterMenuItem';
|
||||||
import Table from 'Components/Table/Table';
|
import Table from 'Components/Table/Table';
|
||||||
import TableBody from 'Components/Table/TableBody';
|
import TableBody from 'Components/Table/TableBody';
|
||||||
import NoArtist from 'Artist/NoArtist';
|
import NoArtist from 'Artist/NoArtist';
|
||||||
import SeasonPassRowConnector from './SeasonPassRowConnector';
|
import AlbumStudioRowConnector from './AlbumStudioRowConnector';
|
||||||
import SeasonPassFooter from './SeasonPassFooter';
|
import AlbumStudioFooter from './AlbumStudioFooter';
|
||||||
|
|
||||||
const columns = [
|
const columns = [
|
||||||
{
|
{
|
||||||
|
@ -24,8 +24,8 @@ const columns = [
|
||||||
isVisible: true
|
isVisible: true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'sortTitle',
|
name: 'sortName',
|
||||||
label: 'Title',
|
label: 'Name',
|
||||||
isSortable: true,
|
isSortable: true,
|
||||||
isVisible: true
|
isVisible: true
|
||||||
},
|
},
|
||||||
|
@ -34,14 +34,14 @@ const columns = [
|
||||||
isVisible: true
|
isVisible: true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'seasonCount',
|
name: 'albumCount',
|
||||||
label: 'Seasons',
|
label: 'Albums',
|
||||||
isSortable: true,
|
isSortable: true,
|
||||||
isVisible: true
|
isVisible: true
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
class SeasonPass extends Component {
|
class AlbumStudio extends Component {
|
||||||
|
|
||||||
//
|
//
|
||||||
// Lifecycle
|
// Lifecycle
|
||||||
|
@ -121,7 +121,7 @@ class SeasonPass extends Component {
|
||||||
} = this.state;
|
} = this.state;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<PageContent title="Season Pass">
|
<PageContent title="Album Studio">
|
||||||
<PageToolbar>
|
<PageToolbar>
|
||||||
<PageToolbarSection />
|
<PageToolbarSection />
|
||||||
<PageToolbarSection alignContent={align.RIGHT}>
|
<PageToolbarSection alignContent={align.RIGHT}>
|
||||||
|
@ -207,7 +207,7 @@ class SeasonPass extends Component {
|
||||||
{
|
{
|
||||||
items.map((item) => {
|
items.map((item) => {
|
||||||
return (
|
return (
|
||||||
<SeasonPassRowConnector
|
<AlbumStudioRowConnector
|
||||||
key={item.id}
|
key={item.id}
|
||||||
artistId={item.id}
|
artistId={item.id}
|
||||||
isSelected={selectedState[item.id]}
|
isSelected={selectedState[item.id]}
|
||||||
|
@ -227,7 +227,7 @@ class SeasonPass extends Component {
|
||||||
}
|
}
|
||||||
</PageContentBodyConnector>
|
</PageContentBodyConnector>
|
||||||
|
|
||||||
<SeasonPassFooter
|
<AlbumStudioFooter
|
||||||
selectedCount={this.getSelectedIds().length}
|
selectedCount={this.getSelectedIds().length}
|
||||||
isSaving={isSaving}
|
isSaving={isSaving}
|
||||||
saveError={saveError}
|
saveError={saveError}
|
||||||
|
@ -238,7 +238,7 @@ class SeasonPass extends Component {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
SeasonPass.propTypes = {
|
AlbumStudio.propTypes = {
|
||||||
isFetching: PropTypes.bool.isRequired,
|
isFetching: PropTypes.bool.isRequired,
|
||||||
isPopulated: PropTypes.bool.isRequired,
|
isPopulated: PropTypes.bool.isRequired,
|
||||||
error: PropTypes.object,
|
error: PropTypes.object,
|
||||||
|
@ -254,4 +254,4 @@ SeasonPass.propTypes = {
|
||||||
onUpdateSelectedPress: PropTypes.func.isRequired
|
onUpdateSelectedPress: PropTypes.func.isRequired
|
||||||
};
|
};
|
||||||
|
|
||||||
export default SeasonPass;
|
export default AlbumStudio;
|
|
@ -3,20 +3,20 @@ import React, { Component } from 'react';
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import padNumber from 'Utilities/Number/padNumber';
|
import padNumber from 'Utilities/Number/padNumber';
|
||||||
import MonitorToggleButton from 'Components/MonitorToggleButton';
|
import MonitorToggleButton from 'Components/MonitorToggleButton';
|
||||||
import styles from './SeasonPassSeason.css';
|
import styles from './AlbumStudioAlbum.css';
|
||||||
|
|
||||||
class SeasonPassSeason extends Component {
|
class AlbumStudioAlbum extends Component {
|
||||||
|
|
||||||
//
|
//
|
||||||
// Listeners
|
// Listeners
|
||||||
|
|
||||||
onSeasonMonitoredPress = () => {
|
onSeasonMonitoredPress = () => {
|
||||||
const {
|
const {
|
||||||
seasonNumber,
|
id,
|
||||||
monitored
|
monitored
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
|
||||||
this.props.onSeasonMonitoredPress(seasonNumber, !monitored);
|
this.props.onSeasonMonitoredPress(id, !monitored);
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
|
@ -24,16 +24,17 @@ class SeasonPassSeason extends Component {
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const {
|
const {
|
||||||
seasonNumber,
|
id,
|
||||||
|
title,
|
||||||
monitored,
|
monitored,
|
||||||
statistics,
|
statistics,
|
||||||
isSaving
|
isSaving
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
|
||||||
const {
|
const {
|
||||||
episodeFileCount,
|
trackFileCount,
|
||||||
totalEpisodeCount,
|
totalTrackCount,
|
||||||
percentOfEpisodes
|
percentOfTracks
|
||||||
} = statistics;
|
} = statistics;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -47,7 +48,7 @@ class SeasonPassSeason extends Component {
|
||||||
|
|
||||||
<span>
|
<span>
|
||||||
{
|
{
|
||||||
seasonNumber === 0 ? 'Specials' : `S${padNumber(seasonNumber, 2)}`
|
`${title}`
|
||||||
}
|
}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
@ -55,12 +56,12 @@ class SeasonPassSeason extends Component {
|
||||||
<div
|
<div
|
||||||
className={classNames(
|
className={classNames(
|
||||||
styles.episodes,
|
styles.episodes,
|
||||||
percentOfEpisodes === 100 && styles.allEpisodes
|
percentOfTracks === 100 && styles.allEpisodes
|
||||||
)}
|
)}
|
||||||
title={`${episodeFileCount}/${totalEpisodeCount} episodes downloaded`}
|
title={`${trackFileCount}/${totalTrackCount} tracks downloaded`}
|
||||||
>
|
>
|
||||||
{
|
{
|
||||||
totalEpisodeCount === 0 ? '0/0' : `${episodeFileCount}/${totalEpisodeCount}`
|
totalTrackCount === 0 ? '0/0' : `${trackFileCount}/${totalTrackCount}`
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -68,21 +69,22 @@ class SeasonPassSeason extends Component {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
SeasonPassSeason.propTypes = {
|
AlbumStudioAlbum.propTypes = {
|
||||||
seasonNumber: PropTypes.number.isRequired,
|
id: PropTypes.number.isRequired,
|
||||||
|
title: PropTypes.string.isRequired,
|
||||||
monitored: PropTypes.bool.isRequired,
|
monitored: PropTypes.bool.isRequired,
|
||||||
statistics: PropTypes.object.isRequired,
|
statistics: PropTypes.object.isRequired,
|
||||||
isSaving: PropTypes.bool.isRequired,
|
isSaving: PropTypes.bool.isRequired,
|
||||||
onSeasonMonitoredPress: PropTypes.func.isRequired
|
onSeasonMonitoredPress: PropTypes.func.isRequired
|
||||||
};
|
};
|
||||||
|
|
||||||
SeasonPassSeason.defaultProps = {
|
AlbumStudioAlbum.defaultProps = {
|
||||||
isSaving: false,
|
isSaving: false,
|
||||||
statistics: {
|
statistics: {
|
||||||
episodeFileCount: 0,
|
trackFileCount: 0,
|
||||||
totalEpisodeCount: 0,
|
totalTrackCount: 0,
|
||||||
percentOfEpisodes: 0
|
percentOfTracks: 0
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export default SeasonPassSeason;
|
export default AlbumStudioAlbum;
|
|
@ -3,8 +3,8 @@ import React, { Component } from 'react';
|
||||||
import { createSelector } from 'reselect';
|
import { createSelector } from 'reselect';
|
||||||
import connectSection from 'Store/connectSection';
|
import connectSection from 'Store/connectSection';
|
||||||
import createClientSideCollectionSelector from 'Store/Selectors/createClientSideCollectionSelector';
|
import createClientSideCollectionSelector from 'Store/Selectors/createClientSideCollectionSelector';
|
||||||
import { setSeasonPassSort, setSeasonPassFilter, saveSeasonPass } from 'Store/Actions/seasonPassActions';
|
import { setAlbumStudioSort, setAlbumStudioFilter, saveAlbumStudio } from 'Store/Actions/albumStudioActions';
|
||||||
import SeasonPass from './SeasonPass';
|
import AlbumStudio from './AlbumStudio';
|
||||||
|
|
||||||
function createMapStateToProps() {
|
function createMapStateToProps() {
|
||||||
return createSelector(
|
return createSelector(
|
||||||
|
@ -18,26 +18,26 @@ function createMapStateToProps() {
|
||||||
}
|
}
|
||||||
|
|
||||||
const mapDispatchToProps = {
|
const mapDispatchToProps = {
|
||||||
setSeasonPassSort,
|
setAlbumStudioSort,
|
||||||
setSeasonPassFilter,
|
setAlbumStudioFilter,
|
||||||
saveSeasonPass
|
saveAlbumStudio
|
||||||
};
|
};
|
||||||
|
|
||||||
class SeasonPassConnector extends Component {
|
class AlbumStudioConnector extends Component {
|
||||||
|
|
||||||
//
|
//
|
||||||
// Listeners
|
// Listeners
|
||||||
|
|
||||||
onSortPress = (sortKey) => {
|
onSortPress = (sortKey) => {
|
||||||
this.props.setSeasonPassSort({ sortKey });
|
this.props.setAlbumStudioSort({ sortKey });
|
||||||
}
|
}
|
||||||
|
|
||||||
onFilterSelect = (filterKey, filterValue, filterType) => {
|
onFilterSelect = (filterKey, filterValue, filterType) => {
|
||||||
this.props.setSeasonPassFilter({ filterKey, filterValue, filterType });
|
this.props.setAlbumStudioFilter({ filterKey, filterValue, filterType });
|
||||||
}
|
}
|
||||||
|
|
||||||
onUpdateSelectedPress = (payload) => {
|
onUpdateSelectedPress = (payload) => {
|
||||||
this.props.saveSeasonPass(payload);
|
this.props.saveAlbumStudio(payload);
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
|
@ -45,7 +45,7 @@ class SeasonPassConnector extends Component {
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
return (
|
return (
|
||||||
<SeasonPass
|
<AlbumStudio
|
||||||
{...this.props}
|
{...this.props}
|
||||||
onSortPress={this.onSortPress}
|
onSortPress={this.onSortPress}
|
||||||
onFilterSelect={this.onFilterSelect}
|
onFilterSelect={this.onFilterSelect}
|
||||||
|
@ -55,10 +55,10 @@ class SeasonPassConnector extends Component {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
SeasonPassConnector.propTypes = {
|
AlbumStudioConnector.propTypes = {
|
||||||
setSeasonPassSort: PropTypes.func.isRequired,
|
setAlbumStudioSort: PropTypes.func.isRequired,
|
||||||
setSeasonPassFilter: PropTypes.func.isRequired,
|
setAlbumStudioFilter: PropTypes.func.isRequired,
|
||||||
saveSeasonPass: PropTypes.func.isRequired
|
saveAlbumStudio: PropTypes.func.isRequired
|
||||||
};
|
};
|
||||||
|
|
||||||
export default connectSection(
|
export default connectSection(
|
||||||
|
@ -66,5 +66,5 @@ export default connectSection(
|
||||||
mapDispatchToProps,
|
mapDispatchToProps,
|
||||||
undefined,
|
undefined,
|
||||||
undefined,
|
undefined,
|
||||||
{ section: 'series', uiSection: 'seasonPass' }
|
{ section: 'series', uiSection: 'albumStudio' }
|
||||||
)(SeasonPassConnector);
|
)(AlbumStudioConnector);
|
|
@ -5,11 +5,11 @@ import SpinnerButton from 'Components/Link/SpinnerButton';
|
||||||
import MonitorAlbumsSelectInput from 'Components/Form/MonitorAlbumsSelectInput';
|
import MonitorAlbumsSelectInput from 'Components/Form/MonitorAlbumsSelectInput';
|
||||||
import SelectInput from 'Components/Form/SelectInput';
|
import SelectInput from 'Components/Form/SelectInput';
|
||||||
import PageContentFooter from 'Components/Page/PageContentFooter';
|
import PageContentFooter from 'Components/Page/PageContentFooter';
|
||||||
import styles from './SeasonPassFooter.css';
|
import styles from './AlbumStudioFooter.css';
|
||||||
|
|
||||||
const NO_CHANGE = 'noChange';
|
const NO_CHANGE = 'noChange';
|
||||||
|
|
||||||
class SeasonPassFooter extends Component {
|
class AlbumStudioFooter extends Component {
|
||||||
|
|
||||||
//
|
//
|
||||||
// Lifecycle
|
// Lifecycle
|
||||||
|
@ -89,7 +89,7 @@ class SeasonPassFooter extends Component {
|
||||||
<PageContentFooter>
|
<PageContentFooter>
|
||||||
<div className={styles.inputContainer}>
|
<div className={styles.inputContainer}>
|
||||||
<div className={styles.label}>
|
<div className={styles.label}>
|
||||||
Monitor Series
|
Monitor Artist
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<SelectInput
|
<SelectInput
|
||||||
|
@ -103,7 +103,7 @@ class SeasonPassFooter extends Component {
|
||||||
|
|
||||||
<div className={styles.inputContainer}>
|
<div className={styles.inputContainer}>
|
||||||
<div className={styles.label}>
|
<div className={styles.label}>
|
||||||
Monitor Episodes
|
Monitor Albums
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<MonitorAlbumsSelectInput
|
<MonitorAlbumsSelectInput
|
||||||
|
@ -117,7 +117,7 @@ class SeasonPassFooter extends Component {
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<div className={styles.label}>
|
<div className={styles.label}>
|
||||||
{selectedCount} Series Selected
|
{selectedCount} Artist(s) Selected
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<SpinnerButton
|
<SpinnerButton
|
||||||
|
@ -135,11 +135,11 @@ class SeasonPassFooter extends Component {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
SeasonPassFooter.propTypes = {
|
AlbumStudioFooter.propTypes = {
|
||||||
selectedCount: PropTypes.number.isRequired,
|
selectedCount: PropTypes.number.isRequired,
|
||||||
isSaving: PropTypes.bool.isRequired,
|
isSaving: PropTypes.bool.isRequired,
|
||||||
saveError: PropTypes.object,
|
saveError: PropTypes.object,
|
||||||
onUpdateSelectedPress: PropTypes.func.isRequired
|
onUpdateSelectedPress: PropTypes.func.isRequired
|
||||||
};
|
};
|
||||||
|
|
||||||
export default SeasonPassFooter;
|
export default AlbumStudioFooter;
|
|
@ -7,10 +7,10 @@ import TableRow from 'Components/Table/TableRow';
|
||||||
import TableRowCell from 'Components/Table/Cells/TableRowCell';
|
import TableRowCell from 'Components/Table/Cells/TableRowCell';
|
||||||
import TableSelectCell from 'Components/Table/Cells/TableSelectCell';
|
import TableSelectCell from 'Components/Table/Cells/TableSelectCell';
|
||||||
import ArtistNameLink from 'Artist/ArtistNameLink';
|
import ArtistNameLink from 'Artist/ArtistNameLink';
|
||||||
import SeasonPassSeason from './SeasonPassSeason';
|
import AlbumStudioAlbum from './AlbumStudioAlbum';
|
||||||
import styles from './SeasonPassRow.css';
|
import styles from './AlbumStudioRow.css';
|
||||||
|
|
||||||
class SeasonPassRow extends Component {
|
class AlbumStudioRow extends Component {
|
||||||
|
|
||||||
//
|
//
|
||||||
// Render
|
// Render
|
||||||
|
@ -19,10 +19,10 @@ class SeasonPassRow extends Component {
|
||||||
const {
|
const {
|
||||||
artistId,
|
artistId,
|
||||||
status,
|
status,
|
||||||
titleSlug,
|
nameSlug,
|
||||||
title,
|
artistName,
|
||||||
monitored,
|
monitored,
|
||||||
seasons,
|
albums,
|
||||||
isSaving,
|
isSaving,
|
||||||
isSelected,
|
isSelected,
|
||||||
onSelectedChange,
|
onSelectedChange,
|
||||||
|
@ -49,8 +49,8 @@ class SeasonPassRow extends Component {
|
||||||
|
|
||||||
<TableRowCell className={styles.title}>
|
<TableRowCell className={styles.title}>
|
||||||
<ArtistNameLink
|
<ArtistNameLink
|
||||||
titleSlug={titleSlug}
|
nameSlug={nameSlug}
|
||||||
title={title}
|
artistName={artistName}
|
||||||
/>
|
/>
|
||||||
</TableRowCell>
|
</TableRowCell>
|
||||||
|
|
||||||
|
@ -64,10 +64,10 @@ class SeasonPassRow extends Component {
|
||||||
|
|
||||||
<TableRowCell className={styles.seasons}>
|
<TableRowCell className={styles.seasons}>
|
||||||
{
|
{
|
||||||
seasons.map((season) => {
|
albums.map((season) => {
|
||||||
return (
|
return (
|
||||||
<SeasonPassSeason
|
<AlbumStudioAlbum
|
||||||
key={season.seasonNumber}
|
key={season.id}
|
||||||
{...season}
|
{...season}
|
||||||
onSeasonMonitoredPress={onSeasonMonitoredPress}
|
onSeasonMonitoredPress={onSeasonMonitoredPress}
|
||||||
/>
|
/>
|
||||||
|
@ -80,13 +80,13 @@ class SeasonPassRow extends Component {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
SeasonPassRow.propTypes = {
|
AlbumStudioRow.propTypes = {
|
||||||
artistId: PropTypes.number.isRequired,
|
artistId: PropTypes.number.isRequired,
|
||||||
status: PropTypes.string.isRequired,
|
status: PropTypes.string.isRequired,
|
||||||
titleSlug: PropTypes.string.isRequired,
|
nameSlug: PropTypes.string.isRequired,
|
||||||
title: PropTypes.string.isRequired,
|
artistName: PropTypes.string.isRequired,
|
||||||
monitored: PropTypes.bool.isRequired,
|
monitored: PropTypes.bool.isRequired,
|
||||||
seasons: PropTypes.arrayOf(PropTypes.object).isRequired,
|
albums: PropTypes.arrayOf(PropTypes.object).isRequired,
|
||||||
isSaving: PropTypes.bool.isRequired,
|
isSaving: PropTypes.bool.isRequired,
|
||||||
isSelected: PropTypes.bool,
|
isSelected: PropTypes.bool,
|
||||||
onSelectedChange: PropTypes.func.isRequired,
|
onSelectedChange: PropTypes.func.isRequired,
|
||||||
|
@ -94,8 +94,8 @@ SeasonPassRow.propTypes = {
|
||||||
onSeasonMonitoredPress: PropTypes.func.isRequired
|
onSeasonMonitoredPress: PropTypes.func.isRequired
|
||||||
};
|
};
|
||||||
|
|
||||||
SeasonPassRow.defaultProps = {
|
AlbumStudioRow.defaultProps = {
|
||||||
isSaving: false
|
isSaving: false
|
||||||
};
|
};
|
||||||
|
|
||||||
export default SeasonPassRow;
|
export default AlbumStudioRow;
|
|
@ -4,8 +4,8 @@ import React, { Component } from 'react';
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import { createSelector } from 'reselect';
|
import { createSelector } from 'reselect';
|
||||||
import createArtistSelector from 'Store/Selectors/createArtistSelector';
|
import createArtistSelector from 'Store/Selectors/createArtistSelector';
|
||||||
import { toggleSeriesMonitored, toggleSeasonMonitored } from 'Store/Actions/seriesActions';
|
import { toggleSeriesMonitored, toggleSeasonMonitored } from 'Store/Actions/artistActions';
|
||||||
import SeasonPassRow from './SeasonPassRow';
|
import AlbumStudioRow from './AlbumStudioRow';
|
||||||
|
|
||||||
function createMapStateToProps() {
|
function createMapStateToProps() {
|
||||||
return createSelector(
|
return createSelector(
|
||||||
|
@ -13,10 +13,10 @@ function createMapStateToProps() {
|
||||||
(series) => {
|
(series) => {
|
||||||
return _.pick(series, [
|
return _.pick(series, [
|
||||||
'status',
|
'status',
|
||||||
'titleSlug',
|
'nameSlug',
|
||||||
'title',
|
'artistName',
|
||||||
'monitored',
|
'monitored',
|
||||||
'seasons',
|
'albums',
|
||||||
'isSaving'
|
'isSaving'
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
@ -28,7 +28,7 @@ const mapDispatchToProps = {
|
||||||
toggleSeasonMonitored
|
toggleSeasonMonitored
|
||||||
};
|
};
|
||||||
|
|
||||||
class SeasonPassRowConnector extends Component {
|
class AlbumStudioRowConnector extends Component {
|
||||||
|
|
||||||
//
|
//
|
||||||
// Listeners
|
// Listeners
|
||||||
|
@ -58,7 +58,7 @@ class SeasonPassRowConnector extends Component {
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
return (
|
return (
|
||||||
<SeasonPassRow
|
<AlbumStudioRow
|
||||||
{...this.props}
|
{...this.props}
|
||||||
onSeriesMonitoredPress={this.onSeriesMonitoredPress}
|
onSeriesMonitoredPress={this.onSeriesMonitoredPress}
|
||||||
onSeasonMonitoredPress={this.onSeasonMonitoredPress}
|
onSeasonMonitoredPress={this.onSeasonMonitoredPress}
|
||||||
|
@ -67,11 +67,11 @@ class SeasonPassRowConnector extends Component {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
SeasonPassRowConnector.propTypes = {
|
AlbumStudioRowConnector.propTypes = {
|
||||||
artistId: PropTypes.number.isRequired,
|
artistId: PropTypes.number.isRequired,
|
||||||
monitored: PropTypes.bool.isRequired,
|
monitored: PropTypes.bool.isRequired,
|
||||||
toggleSeriesMonitored: PropTypes.func.isRequired,
|
toggleSeriesMonitored: PropTypes.func.isRequired,
|
||||||
toggleSeasonMonitored: PropTypes.func.isRequired
|
toggleSeasonMonitored: PropTypes.func.isRequired
|
||||||
};
|
};
|
||||||
|
|
||||||
export default connect(createMapStateToProps, mapDispatchToProps)(SeasonPassRowConnector);
|
export default connect(createMapStateToProps, mapDispatchToProps)(AlbumStudioRowConnector);
|
|
@ -11,8 +11,8 @@ import PageConnector from 'Components/Page/PageConnector';
|
||||||
import ArtistIndexConnector from 'Artist/Index/ArtistIndexConnector';
|
import ArtistIndexConnector from 'Artist/Index/ArtistIndexConnector';
|
||||||
import AddNewArtistConnector from 'AddArtist/AddNewArtist/AddNewArtistConnector';
|
import AddNewArtistConnector from 'AddArtist/AddNewArtist/AddNewArtistConnector';
|
||||||
import ImportArtist from 'AddArtist/ImportArtist/ImportArtist';
|
import ImportArtist from 'AddArtist/ImportArtist/ImportArtist';
|
||||||
import SeriesEditorConnector from 'Artist/Editor/SeriesEditorConnector';
|
import ArtistEditorConnector from 'Artist/Editor/ArtistEditorConnector';
|
||||||
import SeasonPassConnector from 'SeasonPass/SeasonPassConnector';
|
import AlbumStudioConnector from 'AlbumStudio/AlbumStudioConnector';
|
||||||
import SeriesDetailsPageConnector from 'Artist/Details/SeriesDetailsPageConnector';
|
import SeriesDetailsPageConnector from 'Artist/Details/SeriesDetailsPageConnector';
|
||||||
import CalendarPageConnector from 'Calendar/CalendarPageConnector';
|
import CalendarPageConnector from 'Calendar/CalendarPageConnector';
|
||||||
import HistoryConnector from 'Activity/History/HistoryConnector';
|
import HistoryConnector from 'Activity/History/HistoryConnector';
|
||||||
|
@ -82,17 +82,17 @@ function App({ store, history }) {
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<Route
|
<Route
|
||||||
path="/serieseditor"
|
path="/artisteditor"
|
||||||
component={SeriesEditorConnector}
|
component={ArtistEditorConnector}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<Route
|
<Route
|
||||||
path="/seasonpass"
|
path="/albumstudio"
|
||||||
component={SeasonPassConnector}
|
component={AlbumStudioConnector}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<Route
|
<Route
|
||||||
path="/series/:titleSlug"
|
path="/artist/:nameSlug"
|
||||||
component={SeriesDetailsPageConnector}
|
component={SeriesDetailsPageConnector}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
|
|
@ -3,7 +3,7 @@ import React from 'react';
|
||||||
import Link from 'Components/Link/Link';
|
import Link from 'Components/Link/Link';
|
||||||
|
|
||||||
function ArtistNameLink({ nameSlug, artistName }) {
|
function ArtistNameLink({ nameSlug, artistName }) {
|
||||||
const link = `/series/${nameSlug}`;
|
const link = `/artist/${nameSlug}`;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Link to={link}>
|
<Link to={link}>
|
||||||
|
|
|
@ -3,7 +3,7 @@ import React, { Component } from 'react';
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import { createSelector } from 'reselect';
|
import { createSelector } from 'reselect';
|
||||||
import createArtistSelector from 'Store/Selectors/createArtistSelector';
|
import createArtistSelector from 'Store/Selectors/createArtistSelector';
|
||||||
import { deleteArtist } from 'Store/Actions/seriesActions';
|
import { deleteArtist } from 'Store/Actions/artistActions';
|
||||||
import DeleteArtistModalContent from './DeleteArtistModalContent';
|
import DeleteArtistModalContent from './DeleteArtistModalContent';
|
||||||
|
|
||||||
function createMapStateToProps() {
|
function createMapStateToProps() {
|
||||||
|
|
|
@ -99,7 +99,7 @@ class SeriesDetails extends Component {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
onDeleteSeriesModalClose = () => {
|
onDeleteArtistModalClose = () => {
|
||||||
this.setState({ isDeleteArtistModalOpen: false });
|
this.setState({ isDeleteArtistModalOpen: false });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -132,10 +132,10 @@ class SeriesDetails extends Component {
|
||||||
render() {
|
render() {
|
||||||
const {
|
const {
|
||||||
id,
|
id,
|
||||||
tvdbId,
|
foreignArtistId,
|
||||||
tvMazeId,
|
tvMazeId,
|
||||||
imdbId,
|
imdbId,
|
||||||
title,
|
artistName,
|
||||||
runtime,
|
runtime,
|
||||||
ratings,
|
ratings,
|
||||||
sizeOnDisk,
|
sizeOnDisk,
|
||||||
|
@ -146,7 +146,7 @@ class SeriesDetails extends Component {
|
||||||
network,
|
network,
|
||||||
overview,
|
overview,
|
||||||
images,
|
images,
|
||||||
seasons,
|
albums,
|
||||||
alternateTitles,
|
alternateTitles,
|
||||||
tags,
|
tags,
|
||||||
isRefreshing,
|
isRefreshing,
|
||||||
|
@ -190,7 +190,7 @@ class SeriesDetails extends Component {
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<PageContent title={title}>
|
<PageContent title={artistName}>
|
||||||
<PageToolbar>
|
<PageToolbar>
|
||||||
<PageToolbarSection>
|
<PageToolbarSection>
|
||||||
<PageToolbarButton
|
<PageToolbarButton
|
||||||
|
@ -269,7 +269,7 @@ class SeriesDetails extends Component {
|
||||||
<div className={styles.info}>
|
<div className={styles.info}>
|
||||||
<div className={styles.titleContainer}>
|
<div className={styles.titleContainer}>
|
||||||
<div className={styles.title}>
|
<div className={styles.title}>
|
||||||
{title}
|
{artistName}
|
||||||
|
|
||||||
{
|
{
|
||||||
!!alternateTitles.length &&
|
!!alternateTitles.length &&
|
||||||
|
@ -294,16 +294,16 @@ class SeriesDetails extends Component {
|
||||||
className={styles.seriesNavigationButton}
|
className={styles.seriesNavigationButton}
|
||||||
name={icons.ARROW_LEFT}
|
name={icons.ARROW_LEFT}
|
||||||
size={30}
|
size={30}
|
||||||
title={`Go to ${previousSeries.title}`}
|
title={`Go to ${previousSeries.artistName}`}
|
||||||
to={`/series/${previousSeries.titleSlug}`}
|
to={`/artist/${previousSeries.nameSlug}`}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<IconButton
|
<IconButton
|
||||||
className={styles.seriesNavigationButton}
|
className={styles.seriesNavigationButton}
|
||||||
name={icons.ARROW_RIGHT}
|
name={icons.ARROW_RIGHT}
|
||||||
size={30}
|
size={30}
|
||||||
title={`Go to ${nextSeries.title}`}
|
title={`Go to ${nextSeries.artistName}`}
|
||||||
to={`/series/${nextSeries.titleSlug}`}
|
to={`/artist/${nextSeries.nameSlug}`}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -426,7 +426,7 @@ class SeriesDetails extends Component {
|
||||||
}
|
}
|
||||||
tooltip={
|
tooltip={
|
||||||
<SeriesDetailsLinks
|
<SeriesDetailsLinks
|
||||||
tvdbId={tvdbId}
|
foreignArtistId={foreignArtistId}
|
||||||
tvMazeId={tvMazeId}
|
tvMazeId={tvMazeId}
|
||||||
imdbId={imdbId}
|
imdbId={imdbId}
|
||||||
/>
|
/>
|
||||||
|
@ -485,10 +485,10 @@ class SeriesDetails extends Component {
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
isPopulated && !!seasons.length &&
|
isPopulated && !!albums.length &&
|
||||||
<div>
|
<div>
|
||||||
{
|
{
|
||||||
seasons.slice(0).reverse().map((season) => {
|
albums.slice(0).reverse().map((season) => {
|
||||||
return (
|
return (
|
||||||
<SeriesDetailsSeasonConnector
|
<SeriesDetailsSeasonConnector
|
||||||
key={season.seasonNumber}
|
key={season.seasonNumber}
|
||||||
|
@ -504,7 +504,7 @@ class SeriesDetails extends Component {
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
isPopulated && !seasons.length &&
|
isPopulated && !albums.length &&
|
||||||
<div>
|
<div>
|
||||||
No episode information is available.
|
No episode information is available.
|
||||||
</div>
|
</div>
|
||||||
|
@ -534,7 +534,7 @@ class SeriesDetails extends Component {
|
||||||
<DeleteArtistModal
|
<DeleteArtistModal
|
||||||
isOpen={isDeleteArtistModalOpen}
|
isOpen={isDeleteArtistModalOpen}
|
||||||
artistId={id}
|
artistId={id}
|
||||||
onModalClose={this.onDeleteSeriesModalClose}
|
onModalClose={this.onDeleteArtistModalClose}
|
||||||
/>
|
/>
|
||||||
</PageContentBodyConnector>
|
</PageContentBodyConnector>
|
||||||
</PageContent>
|
</PageContent>
|
||||||
|
@ -544,10 +544,10 @@ class SeriesDetails extends Component {
|
||||||
|
|
||||||
SeriesDetails.propTypes = {
|
SeriesDetails.propTypes = {
|
||||||
id: PropTypes.number.isRequired,
|
id: PropTypes.number.isRequired,
|
||||||
tvdbId: PropTypes.number.isRequired,
|
foreignArtistId: PropTypes.string.isRequired,
|
||||||
tvMazeId: PropTypes.number,
|
tvMazeId: PropTypes.number,
|
||||||
imdbId: PropTypes.string,
|
imdbId: PropTypes.string,
|
||||||
title: PropTypes.string.isRequired,
|
artistName: PropTypes.string.isRequired,
|
||||||
runtime: PropTypes.number.isRequired,
|
runtime: PropTypes.number.isRequired,
|
||||||
ratings: PropTypes.object.isRequired,
|
ratings: PropTypes.object.isRequired,
|
||||||
sizeOnDisk: PropTypes.number.isRequired,
|
sizeOnDisk: PropTypes.number.isRequired,
|
||||||
|
@ -558,7 +558,7 @@ SeriesDetails.propTypes = {
|
||||||
network: PropTypes.string,
|
network: PropTypes.string,
|
||||||
overview: PropTypes.string.isRequired,
|
overview: PropTypes.string.isRequired,
|
||||||
images: PropTypes.arrayOf(PropTypes.object).isRequired,
|
images: PropTypes.arrayOf(PropTypes.object).isRequired,
|
||||||
seasons: PropTypes.arrayOf(PropTypes.object).isRequired,
|
albums: PropTypes.arrayOf(PropTypes.object).isRequired,
|
||||||
alternateTitles: PropTypes.arrayOf(PropTypes.string).isRequired,
|
alternateTitles: PropTypes.arrayOf(PropTypes.string).isRequired,
|
||||||
tags: PropTypes.arrayOf(PropTypes.number).isRequired,
|
tags: PropTypes.arrayOf(PropTypes.number).isRequired,
|
||||||
isRefreshing: PropTypes.bool.isRequired,
|
isRefreshing: PropTypes.bool.isRequired,
|
||||||
|
|
|
@ -4,7 +4,7 @@ import React, { Component } from 'react';
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import { createSelector } from 'reselect';
|
import { createSelector } from 'reselect';
|
||||||
import { findCommand } from 'Utilities/Command';
|
import { findCommand } from 'Utilities/Command';
|
||||||
import createAllSeriesSelector from 'Store/Selectors/createAllSeriesSelector';
|
import createAllArtistSelector from 'Store/Selectors/createAllArtistSelector';
|
||||||
import createCommandsSelector from 'Store/Selectors/createCommandsSelector';
|
import createCommandsSelector from 'Store/Selectors/createCommandsSelector';
|
||||||
import { fetchEpisodes, clearEpisodes } from 'Store/Actions/episodeActions';
|
import { fetchEpisodes, clearEpisodes } from 'Store/Actions/episodeActions';
|
||||||
import { fetchEpisodeFiles, clearEpisodeFiles } from 'Store/Actions/episodeFileActions';
|
import { fetchEpisodeFiles, clearEpisodeFiles } from 'Store/Actions/episodeFileActions';
|
||||||
|
@ -15,28 +15,28 @@ import SeriesDetails from './SeriesDetails';
|
||||||
|
|
||||||
function createMapStateToProps() {
|
function createMapStateToProps() {
|
||||||
return createSelector(
|
return createSelector(
|
||||||
(state, { titleSlug }) => titleSlug,
|
(state, { nameSlug }) => nameSlug,
|
||||||
(state) => state.episodes,
|
(state) => state.episodes,
|
||||||
(state) => state.episodeFiles,
|
(state) => state.episodeFiles,
|
||||||
createAllSeriesSelector(),
|
createAllArtistSelector(),
|
||||||
createCommandsSelector(),
|
createCommandsSelector(),
|
||||||
(titleSlug, episodes, episodeFiles, allSeries, commands) => {
|
(nameSlug, episodes, episodeFiles, allSeries, commands) => {
|
||||||
const sortedSeries = _.orderBy(allSeries, 'sortTitle');
|
const sortedArtist = _.orderBy(allSeries, 'sortTitle');
|
||||||
const seriesIndex = _.findIndex(sortedSeries, { titleSlug });
|
const seriesIndex = _.findIndex(sortedArtist, { nameSlug });
|
||||||
const series = sortedSeries[seriesIndex];
|
const series = sortedArtist[seriesIndex];
|
||||||
|
|
||||||
if (!series) {
|
if (!series) {
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
const previousSeries = sortedSeries[seriesIndex - 1] || _.last(sortedSeries);
|
const previousSeries = sortedArtist[seriesIndex - 1] || _.last(sortedArtist);
|
||||||
const nextSeries = sortedSeries[seriesIndex + 1] || _.first(sortedSeries);
|
const nextSeries = sortedArtist[seriesIndex + 1] || _.first(sortedArtist);
|
||||||
const isSeriesRefreshing = !!findCommand(commands, { name: commandNames.REFRESH_SERIES, artistId: series.id });
|
const isSeriesRefreshing = !!findCommand(commands, { name: commandNames.REFRESH_ARTIST, artistId: series.id });
|
||||||
const allSeriesRefreshing = _.some(commands, (command) => command.name === commandNames.REFRESH_SERIES && !command.body.artistId);
|
const allSeriesRefreshing = _.some(commands, (command) => command.name === commandNames.REFRESH_ARTIST && !command.body.artistId);
|
||||||
const isRefreshing = isSeriesRefreshing || allSeriesRefreshing;
|
const isRefreshing = isSeriesRefreshing || allSeriesRefreshing;
|
||||||
const isSearching = !!findCommand(commands, { name: commandNames.SERIES_SEARCH, artistId: series.id });
|
const isSearching = !!findCommand(commands, { name: commandNames.ARTIST_SEARCH, artistId: series.id });
|
||||||
const isRenamingFiles = !!findCommand(commands, { name: commandNames.RENAME_FILES, artistId: series.id });
|
const isRenamingFiles = !!findCommand(commands, { name: commandNames.RENAME_FILES, artistId: series.id });
|
||||||
const isRenamingSeriesCommand = findCommand(commands, { name: commandNames.RENAME_SERIES });
|
const isRenamingSeriesCommand = findCommand(commands, { name: commandNames.RENAME_ARTIST });
|
||||||
const isRenamingSeries = !!(isRenamingSeriesCommand && isRenamingSeriesCommand.body.artistId.indexOf(series.id) > -1);
|
const isRenamingSeries = !!(isRenamingSeriesCommand && isRenamingSeriesCommand.body.artistId.indexOf(series.id) > -1);
|
||||||
|
|
||||||
const isFetching = episodes.isFetching || episodeFiles.isFetching;
|
const isFetching = episodes.isFetching || episodeFiles.isFetching;
|
||||||
|
@ -140,14 +140,14 @@ class SeriesDetailsConnector extends Component {
|
||||||
|
|
||||||
onRefreshPress = () => {
|
onRefreshPress = () => {
|
||||||
this.props.executeCommand({
|
this.props.executeCommand({
|
||||||
name: commandNames.REFRESH_SERIES,
|
name: commandNames.REFRESH_ARTIST,
|
||||||
artistId: this.props.id
|
artistId: this.props.id
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
onSearchPress = () => {
|
onSearchPress = () => {
|
||||||
this.props.executeCommand({
|
this.props.executeCommand({
|
||||||
name: commandNames.SERIES_SEARCH,
|
name: commandNames.ARTIST_SEARCH,
|
||||||
artistId: this.props.id
|
artistId: this.props.id
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -168,7 +168,7 @@ class SeriesDetailsConnector extends Component {
|
||||||
|
|
||||||
SeriesDetailsConnector.propTypes = {
|
SeriesDetailsConnector.propTypes = {
|
||||||
id: PropTypes.number.isRequired,
|
id: PropTypes.number.isRequired,
|
||||||
titleSlug: PropTypes.string.isRequired,
|
nameSlug: PropTypes.string.isRequired,
|
||||||
isRefreshing: PropTypes.bool.isRequired,
|
isRefreshing: PropTypes.bool.isRequired,
|
||||||
isRenamingFiles: PropTypes.bool.isRequired,
|
isRenamingFiles: PropTypes.bool.isRequired,
|
||||||
isRenamingSeries: PropTypes.bool.isRequired,
|
isRenamingSeries: PropTypes.bool.isRequired,
|
||||||
|
|
|
@ -7,7 +7,7 @@ import styles from './SeriesDetailsLinks.css';
|
||||||
|
|
||||||
function SeriesDetailsLinks(props) {
|
function SeriesDetailsLinks(props) {
|
||||||
const {
|
const {
|
||||||
tvdbId,
|
foreignArtistId,
|
||||||
tvMazeId,
|
tvMazeId,
|
||||||
imdbId
|
imdbId
|
||||||
} = props;
|
} = props;
|
||||||
|
@ -16,7 +16,7 @@ function SeriesDetailsLinks(props) {
|
||||||
<div className={styles.links}>
|
<div className={styles.links}>
|
||||||
<Link
|
<Link
|
||||||
className={styles.link}
|
className={styles.link}
|
||||||
to={`http://www.thetvdb.com/?tab=series&id=${tvdbId}`}
|
to={`http://www.thetvdb.com/?tab=series&id=${foreignArtistId}`}
|
||||||
>
|
>
|
||||||
<Label
|
<Label
|
||||||
className={styles.linkLabel}
|
className={styles.linkLabel}
|
||||||
|
@ -29,7 +29,7 @@ function SeriesDetailsLinks(props) {
|
||||||
|
|
||||||
<Link
|
<Link
|
||||||
className={styles.link}
|
className={styles.link}
|
||||||
to={`http://trakt.tv/search/tvdb/${tvdbId}?id_type=show`}
|
to={`http://trakt.tv/search/tvdb/${foreignArtistId}?id_type=show`}
|
||||||
>
|
>
|
||||||
<Label
|
<Label
|
||||||
className={styles.linkLabel}
|
className={styles.linkLabel}
|
||||||
|
@ -76,7 +76,7 @@ function SeriesDetailsLinks(props) {
|
||||||
}
|
}
|
||||||
|
|
||||||
SeriesDetailsLinks.propTypes = {
|
SeriesDetailsLinks.propTypes = {
|
||||||
tvdbId: PropTypes.number.isRequired,
|
foreignArtistId: PropTypes.string.isRequired,
|
||||||
tvMazeId: PropTypes.number,
|
tvMazeId: PropTypes.number,
|
||||||
imdbId: PropTypes.string
|
imdbId: PropTypes.string
|
||||||
};
|
};
|
||||||
|
|
|
@ -4,21 +4,21 @@ import React, { Component } from 'react';
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import { createSelector } from 'reselect';
|
import { createSelector } from 'reselect';
|
||||||
import { push } from 'react-router-redux';
|
import { push } from 'react-router-redux';
|
||||||
import createAllSeriesSelector from 'Store/Selectors/createAllSeriesSelector';
|
import createAllArtistSelector from 'Store/Selectors/createAllArtistSelector';
|
||||||
import NotFound from 'Components/NotFound';
|
import NotFound from 'Components/NotFound';
|
||||||
import SeriesDetailsConnector from './SeriesDetailsConnector';
|
import SeriesDetailsConnector from './SeriesDetailsConnector';
|
||||||
|
|
||||||
function createMapStateToProps() {
|
function createMapStateToProps() {
|
||||||
return createSelector(
|
return createSelector(
|
||||||
(state, { match }) => match,
|
(state, { match }) => match,
|
||||||
createAllSeriesSelector(),
|
createAllArtistSelector(),
|
||||||
(match, allSeries) => {
|
(match, allSeries) => {
|
||||||
const titleSlug = match.params.titleSlug;
|
const nameSlug = match.params.nameSlug;
|
||||||
const seriesIndex = _.findIndex(allSeries, { titleSlug });
|
const seriesIndex = _.findIndex(allSeries, { nameSlug });
|
||||||
|
|
||||||
if (seriesIndex > -1) {
|
if (seriesIndex > -1) {
|
||||||
return {
|
return {
|
||||||
titleSlug
|
nameSlug
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -37,7 +37,7 @@ class SeriesDetailsPageConnector extends Component {
|
||||||
// Lifecycle
|
// Lifecycle
|
||||||
|
|
||||||
componentDidUpdate(prevProps) {
|
componentDidUpdate(prevProps) {
|
||||||
if (!this.props.titleSlug) {
|
if (!this.props.nameSlug) {
|
||||||
this.props.push(`${window.Sonarr.urlBase}/`);
|
this.props.push(`${window.Sonarr.urlBase}/`);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -48,10 +48,10 @@ class SeriesDetailsPageConnector extends Component {
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const {
|
const {
|
||||||
titleSlug
|
nameSlug
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
|
||||||
if (!titleSlug) {
|
if (!nameSlug) {
|
||||||
return (
|
return (
|
||||||
<NotFound
|
<NotFound
|
||||||
message="Sorry, that series cannot be found."
|
message="Sorry, that series cannot be found."
|
||||||
|
@ -61,15 +61,15 @@ class SeriesDetailsPageConnector extends Component {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<SeriesDetailsConnector
|
<SeriesDetailsConnector
|
||||||
titleSlug={titleSlug}
|
nameSlug={nameSlug}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
SeriesDetailsPageConnector.propTypes = {
|
SeriesDetailsPageConnector.propTypes = {
|
||||||
titleSlug: PropTypes.string,
|
nameSlug: PropTypes.string,
|
||||||
match: PropTypes.shape({ params: PropTypes.shape({ titleSlug: PropTypes.string.isRequired }).isRequired }).isRequired,
|
match: PropTypes.shape({ params: PropTypes.shape({ nameSlug: PropTypes.string.isRequired }).isRequired }).isRequired,
|
||||||
push: PropTypes.func.isRequired
|
push: PropTypes.func.isRequired
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -7,7 +7,7 @@ import { findCommand } from 'Utilities/Command';
|
||||||
import createDimensionsSelector from 'Store/Selectors/createDimensionsSelector';
|
import createDimensionsSelector from 'Store/Selectors/createDimensionsSelector';
|
||||||
import createArtistSelector from 'Store/Selectors/createArtistSelector';
|
import createArtistSelector from 'Store/Selectors/createArtistSelector';
|
||||||
import createCommandsSelector from 'Store/Selectors/createCommandsSelector';
|
import createCommandsSelector from 'Store/Selectors/createCommandsSelector';
|
||||||
import { toggleSeasonMonitored } from 'Store/Actions/seriesActions';
|
import { toggleSeasonMonitored } from 'Store/Actions/artistActions';
|
||||||
import { toggleEpisodesMonitored, setEpisodesTableOption } from 'Store/Actions/episodeActions';
|
import { toggleEpisodesMonitored, setEpisodesTableOption } from 'Store/Actions/episodeActions';
|
||||||
import { executeCommand } from 'Store/Actions/commandActions';
|
import { executeCommand } from 'Store/Actions/commandActions';
|
||||||
import * as commandNames from 'Commands/commandNames';
|
import * as commandNames from 'Commands/commandNames';
|
||||||
|
|
|
@ -5,7 +5,7 @@ import { connect } from 'react-redux';
|
||||||
import { createSelector } from 'reselect';
|
import { createSelector } from 'reselect';
|
||||||
import selectSettings from 'Store/Selectors/selectSettings';
|
import selectSettings from 'Store/Selectors/selectSettings';
|
||||||
import createArtistSelector from 'Store/Selectors/createArtistSelector';
|
import createArtistSelector from 'Store/Selectors/createArtistSelector';
|
||||||
import { setSeriesValue, saveArtist } from 'Store/Actions/seriesActions';
|
import { setSeriesValue, saveArtist } from 'Store/Actions/artistActions';
|
||||||
import EditArtistModalContent from './EditArtistModalContent';
|
import EditArtistModalContent from './EditArtistModalContent';
|
||||||
|
|
||||||
function createMapStateToProps() {
|
function createMapStateToProps() {
|
||||||
|
|
|
@ -15,9 +15,9 @@ import FilterMenuItem from 'Components/Menu/FilterMenuItem';
|
||||||
import Table from 'Components/Table/Table';
|
import Table from 'Components/Table/Table';
|
||||||
import TableBody from 'Components/Table/TableBody';
|
import TableBody from 'Components/Table/TableBody';
|
||||||
import NoArtist from 'Artist/NoArtist';
|
import NoArtist from 'Artist/NoArtist';
|
||||||
import SeriesEditorRowConnector from './SeriesEditorRowConnector';
|
import ArtistEditorRowConnector from './ArtistEditorRowConnector';
|
||||||
import SeriesEditorFooter from './SeriesEditorFooter';
|
import ArtistEditorFooter from './ArtistEditorFooter';
|
||||||
import OrganizeSeriesModal from './Organize/OrganizeSeriesModal';
|
import OrganizeArtistModal from './Organize/OrganizeArtistModal';
|
||||||
|
|
||||||
function getColumns(showLanguageProfile) {
|
function getColumns(showLanguageProfile) {
|
||||||
return [
|
return [
|
||||||
|
@ -26,8 +26,8 @@ function getColumns(showLanguageProfile) {
|
||||||
isVisible: true
|
isVisible: true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'sortTitle',
|
name: 'sortName',
|
||||||
label: 'Title',
|
label: 'Name',
|
||||||
isSortable: true,
|
isSortable: true,
|
||||||
isVisible: true
|
isVisible: true
|
||||||
},
|
},
|
||||||
|
@ -44,14 +44,8 @@ function getColumns(showLanguageProfile) {
|
||||||
isVisible: showLanguageProfile
|
isVisible: showLanguageProfile
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'seriesType',
|
name: 'albumFolder',
|
||||||
label: 'Series Type',
|
label: 'Album Folder',
|
||||||
isSortable: false,
|
|
||||||
isVisible: true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'seasonFolder',
|
|
||||||
label: 'Season Folder',
|
|
||||||
isSortable: true,
|
isSortable: true,
|
||||||
isVisible: true
|
isVisible: true
|
||||||
},
|
},
|
||||||
|
@ -70,7 +64,7 @@ function getColumns(showLanguageProfile) {
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
class SeriesEditor extends Component {
|
class ArtistEditor extends Component {
|
||||||
|
|
||||||
//
|
//
|
||||||
// Lifecycle
|
// Lifecycle
|
||||||
|
@ -83,7 +77,7 @@ class SeriesEditor extends Component {
|
||||||
allUnselected: false,
|
allUnselected: false,
|
||||||
lastToggled: null,
|
lastToggled: null,
|
||||||
selectedState: {},
|
selectedState: {},
|
||||||
isOrganizingSeriesModalOpen: false,
|
isOrganizingArtistModalOpen: false,
|
||||||
columns: getColumns(props.showLanguageProfile)
|
columns: getColumns(props.showLanguageProfile)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -130,12 +124,12 @@ class SeriesEditor extends Component {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
onOrganizeSeriesPress = () => {
|
onOrganizeArtistPress = () => {
|
||||||
this.setState({ isOrganizingSeriesModalOpen: true });
|
this.setState({ isOrganizingArtistModalOpen: true });
|
||||||
}
|
}
|
||||||
|
|
||||||
onOrganizeSeriesModalClose = (organized) => {
|
onOrganizeArtistModalClose = (organized) => {
|
||||||
this.setState({ isOrganizingSeriesModalOpen: false });
|
this.setState({ isOrganizingArtistModalOpen: false });
|
||||||
|
|
||||||
if (organized === true) {
|
if (organized === true) {
|
||||||
this.onSelectAllChange({ value: false });
|
this.onSelectAllChange({ value: false });
|
||||||
|
@ -159,7 +153,7 @@ class SeriesEditor extends Component {
|
||||||
saveError,
|
saveError,
|
||||||
isDeleting,
|
isDeleting,
|
||||||
deleteError,
|
deleteError,
|
||||||
isOrganizingSeries,
|
isOrganizingArtist,
|
||||||
showLanguageProfile,
|
showLanguageProfile,
|
||||||
onSortPress,
|
onSortPress,
|
||||||
onFilterSelect
|
onFilterSelect
|
||||||
|
@ -172,10 +166,10 @@ class SeriesEditor extends Component {
|
||||||
columns
|
columns
|
||||||
} = this.state;
|
} = this.state;
|
||||||
|
|
||||||
const selectedSeriesIds = this.getSelectedIds();
|
const selectedArtistIds = this.getSelectedIds();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<PageContent title="Series Editor">
|
<PageContent title="Artist Editor">
|
||||||
<PageToolbar>
|
<PageToolbar>
|
||||||
<PageToolbarSection />
|
<PageToolbarSection />
|
||||||
<PageToolbarSection alignContent={align.RIGHT}>
|
<PageToolbarSection alignContent={align.RIGHT}>
|
||||||
|
@ -261,7 +255,7 @@ class SeriesEditor extends Component {
|
||||||
{
|
{
|
||||||
items.map((item) => {
|
items.map((item) => {
|
||||||
return (
|
return (
|
||||||
<SeriesEditorRowConnector
|
<ArtistEditorRowConnector
|
||||||
key={item.id}
|
key={item.id}
|
||||||
{...item}
|
{...item}
|
||||||
columns={columns}
|
columns={columns}
|
||||||
|
@ -282,30 +276,30 @@ class SeriesEditor extends Component {
|
||||||
}
|
}
|
||||||
</PageContentBodyConnector>
|
</PageContentBodyConnector>
|
||||||
|
|
||||||
<SeriesEditorFooter
|
<ArtistEditorFooter
|
||||||
artistIds={selectedSeriesIds}
|
artistIds={selectedArtistIds}
|
||||||
selectedCount={selectedSeriesIds.length}
|
selectedCount={selectedArtistIds.length}
|
||||||
isSaving={isSaving}
|
isSaving={isSaving}
|
||||||
saveError={saveError}
|
saveError={saveError}
|
||||||
isDeleting={isDeleting}
|
isDeleting={isDeleting}
|
||||||
deleteError={deleteError}
|
deleteError={deleteError}
|
||||||
isOrganizingSeries={isOrganizingSeries}
|
isOrganizingArtist={isOrganizingArtist}
|
||||||
showLanguageProfile={showLanguageProfile}
|
showLanguageProfile={showLanguageProfile}
|
||||||
onSaveSelected={this.onSaveSelected}
|
onSaveSelected={this.onSaveSelected}
|
||||||
onOrganizeSeriesPress={this.onOrganizeSeriesPress}
|
onOrganizeArtistPress={this.onOrganizeArtistPress}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<OrganizeSeriesModal
|
<OrganizeArtistModal
|
||||||
isOpen={this.state.isOrganizingSeriesModalOpen}
|
isOpen={this.state.isOrganizingArtistModalOpen}
|
||||||
artistIds={selectedSeriesIds}
|
artistIds={selectedArtistIds}
|
||||||
onModalClose={this.onOrganizeSeriesModalClose}
|
onModalClose={this.onOrganizeArtistModalClose}
|
||||||
/>
|
/>
|
||||||
</PageContent>
|
</PageContent>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
SeriesEditor.propTypes = {
|
ArtistEditor.propTypes = {
|
||||||
isFetching: PropTypes.bool.isRequired,
|
isFetching: PropTypes.bool.isRequired,
|
||||||
isPopulated: PropTypes.bool.isRequired,
|
isPopulated: PropTypes.bool.isRequired,
|
||||||
error: PropTypes.object,
|
error: PropTypes.object,
|
||||||
|
@ -318,11 +312,11 @@ SeriesEditor.propTypes = {
|
||||||
saveError: PropTypes.object,
|
saveError: PropTypes.object,
|
||||||
isDeleting: PropTypes.bool.isRequired,
|
isDeleting: PropTypes.bool.isRequired,
|
||||||
deleteError: PropTypes.object,
|
deleteError: PropTypes.object,
|
||||||
isOrganizingSeries: PropTypes.bool.isRequired,
|
isOrganizingArtist: PropTypes.bool.isRequired,
|
||||||
showLanguageProfile: PropTypes.bool.isRequired,
|
showLanguageProfile: PropTypes.bool.isRequired,
|
||||||
onSortPress: PropTypes.func.isRequired,
|
onSortPress: PropTypes.func.isRequired,
|
||||||
onFilterSelect: PropTypes.func.isRequired,
|
onFilterSelect: PropTypes.func.isRequired,
|
||||||
onSaveSelected: PropTypes.func.isRequired
|
onSaveSelected: PropTypes.func.isRequired
|
||||||
};
|
};
|
||||||
|
|
||||||
export default SeriesEditor;
|
export default ArtistEditor;
|
|
@ -4,19 +4,19 @@ import { createSelector } from 'reselect';
|
||||||
import connectSection from 'Store/connectSection';
|
import connectSection from 'Store/connectSection';
|
||||||
import createClientSideCollectionSelector from 'Store/Selectors/createClientSideCollectionSelector';
|
import createClientSideCollectionSelector from 'Store/Selectors/createClientSideCollectionSelector';
|
||||||
import createCommandSelector from 'Store/Selectors/createCommandSelector';
|
import createCommandSelector from 'Store/Selectors/createCommandSelector';
|
||||||
import { setSeriesEditorSort, setSeriesEditorFilter, saveArtistEditor } from 'Store/Actions/seriesEditorActions';
|
import { setArtistEditorSort, setArtistEditorFilter, saveArtistEditor } from 'Store/Actions/artistEditorActions';
|
||||||
import { fetchRootFolders } from 'Store/Actions/rootFolderActions';
|
import { fetchRootFolders } from 'Store/Actions/rootFolderActions';
|
||||||
import * as commandNames from 'Commands/commandNames';
|
import * as commandNames from 'Commands/commandNames';
|
||||||
import SeriesEditor from './SeriesEditor';
|
import ArtistEditor from './ArtistEditor';
|
||||||
|
|
||||||
function createMapStateToProps() {
|
function createMapStateToProps() {
|
||||||
return createSelector(
|
return createSelector(
|
||||||
(state) => state.settings.languageProfiles,
|
(state) => state.settings.languageProfiles,
|
||||||
createClientSideCollectionSelector(),
|
createClientSideCollectionSelector(),
|
||||||
createCommandSelector(commandNames.RENAME_SERIES),
|
createCommandSelector(commandNames.RENAME_ARTIST),
|
||||||
(languageProfiles, series, isOrganizingSeries) => {
|
(languageProfiles, series, isOrganizingArtist) => {
|
||||||
return {
|
return {
|
||||||
isOrganizingSeries,
|
isOrganizingArtist,
|
||||||
showLanguageProfile: languageProfiles.items.length > 1,
|
showLanguageProfile: languageProfiles.items.length > 1,
|
||||||
...series
|
...series
|
||||||
};
|
};
|
||||||
|
@ -25,13 +25,13 @@ function createMapStateToProps() {
|
||||||
}
|
}
|
||||||
|
|
||||||
const mapDispatchToProps = {
|
const mapDispatchToProps = {
|
||||||
setSeriesEditorSort,
|
setArtistEditorSort,
|
||||||
setSeriesEditorFilter,
|
setArtistEditorFilter,
|
||||||
saveArtistEditor,
|
saveArtistEditor,
|
||||||
fetchRootFolders
|
fetchRootFolders
|
||||||
};
|
};
|
||||||
|
|
||||||
class SeriesEditorConnector extends Component {
|
class ArtistEditorConnector extends Component {
|
||||||
|
|
||||||
//
|
//
|
||||||
// Lifecycle
|
// Lifecycle
|
||||||
|
@ -44,11 +44,11 @@ class SeriesEditorConnector extends Component {
|
||||||
// Listeners
|
// Listeners
|
||||||
|
|
||||||
onSortPress = (sortKey) => {
|
onSortPress = (sortKey) => {
|
||||||
this.props.setSeriesEditorSort({ sortKey });
|
this.props.setArtistEditorSort({ sortKey });
|
||||||
}
|
}
|
||||||
|
|
||||||
onFilterSelect = (filterKey, filterValue, filterType) => {
|
onFilterSelect = (filterKey, filterValue, filterType) => {
|
||||||
this.props.setSeriesEditorFilter({ filterKey, filterValue, filterType });
|
this.props.setArtistEditorFilter({ filterKey, filterValue, filterType });
|
||||||
}
|
}
|
||||||
|
|
||||||
onSaveSelected = (payload) => {
|
onSaveSelected = (payload) => {
|
||||||
|
@ -60,7 +60,7 @@ class SeriesEditorConnector extends Component {
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
return (
|
return (
|
||||||
<SeriesEditor
|
<ArtistEditor
|
||||||
{...this.props}
|
{...this.props}
|
||||||
onSortPress={this.onSortPress}
|
onSortPress={this.onSortPress}
|
||||||
onFilterSelect={this.onFilterSelect}
|
onFilterSelect={this.onFilterSelect}
|
||||||
|
@ -70,9 +70,9 @@ class SeriesEditorConnector extends Component {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
SeriesEditorConnector.propTypes = {
|
ArtistEditorConnector.propTypes = {
|
||||||
setSeriesEditorSort: PropTypes.func.isRequired,
|
setArtistEditorSort: PropTypes.func.isRequired,
|
||||||
setSeriesEditorFilter: PropTypes.func.isRequired,
|
setArtistEditorFilter: PropTypes.func.isRequired,
|
||||||
saveArtistEditor: PropTypes.func.isRequired,
|
saveArtistEditor: PropTypes.func.isRequired,
|
||||||
fetchRootFolders: PropTypes.func.isRequired
|
fetchRootFolders: PropTypes.func.isRequired
|
||||||
};
|
};
|
||||||
|
@ -82,5 +82,5 @@ export default connectSection(
|
||||||
mapDispatchToProps,
|
mapDispatchToProps,
|
||||||
undefined,
|
undefined,
|
||||||
undefined,
|
undefined,
|
||||||
{ section: 'series', uiSection: 'seriesEditor' }
|
{ section: 'series', uiSection: 'artistEditor' }
|
||||||
)(SeriesEditorConnector);
|
)(ArtistEditorConnector);
|
|
@ -10,12 +10,12 @@ import SpinnerButton from 'Components/Link/SpinnerButton';
|
||||||
import PageContentFooter from 'Components/Page/PageContentFooter';
|
import PageContentFooter from 'Components/Page/PageContentFooter';
|
||||||
import TagsModal from './Tags/TagsModal';
|
import TagsModal from './Tags/TagsModal';
|
||||||
import DeleteArtistModal from './Delete/DeleteArtistModal';
|
import DeleteArtistModal from './Delete/DeleteArtistModal';
|
||||||
import SeriesEditorFooterLabel from './SeriesEditorFooterLabel';
|
import ArtistEditorFooterLabel from './ArtistEditorFooterLabel';
|
||||||
import styles from './SeriesEditorFooter.css';
|
import styles from './ArtistEditorFooter.css';
|
||||||
|
|
||||||
const NO_CHANGE = 'noChange';
|
const NO_CHANGE = 'noChange';
|
||||||
|
|
||||||
class SeriesEditorFooter extends Component {
|
class ArtistEditorFooter extends Component {
|
||||||
|
|
||||||
//
|
//
|
||||||
// Lifecycle
|
// Lifecycle
|
||||||
|
@ -27,8 +27,7 @@ class SeriesEditorFooter extends Component {
|
||||||
monitored: NO_CHANGE,
|
monitored: NO_CHANGE,
|
||||||
qualityProfileId: NO_CHANGE,
|
qualityProfileId: NO_CHANGE,
|
||||||
languageProfileId: NO_CHANGE,
|
languageProfileId: NO_CHANGE,
|
||||||
seriesType: NO_CHANGE,
|
albumFolder: NO_CHANGE,
|
||||||
seasonFolder: NO_CHANGE,
|
|
||||||
rootFolderPath: NO_CHANGE,
|
rootFolderPath: NO_CHANGE,
|
||||||
savingTags: false,
|
savingTags: false,
|
||||||
isDeleteArtistModalOpen: false,
|
isDeleteArtistModalOpen: false,
|
||||||
|
@ -47,8 +46,7 @@ class SeriesEditorFooter extends Component {
|
||||||
monitored: NO_CHANGE,
|
monitored: NO_CHANGE,
|
||||||
qualityProfileId: NO_CHANGE,
|
qualityProfileId: NO_CHANGE,
|
||||||
languageProfileId: NO_CHANGE,
|
languageProfileId: NO_CHANGE,
|
||||||
seriesType: NO_CHANGE,
|
albumFolder: NO_CHANGE,
|
||||||
seasonFolder: NO_CHANGE,
|
|
||||||
rootFolderPath: NO_CHANGE,
|
rootFolderPath: NO_CHANGE,
|
||||||
savingTags: false
|
savingTags: false
|
||||||
});
|
});
|
||||||
|
@ -93,7 +91,7 @@ class SeriesEditorFooter extends Component {
|
||||||
this.setState({ isDeleteArtistModalOpen: true });
|
this.setState({ isDeleteArtistModalOpen: true });
|
||||||
}
|
}
|
||||||
|
|
||||||
onDeleteSeriesModalClose = () => {
|
onDeleteArtistModalClose = () => {
|
||||||
this.setState({ isDeleteArtistModalOpen: false });
|
this.setState({ isDeleteArtistModalOpen: false });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -114,17 +112,16 @@ class SeriesEditorFooter extends Component {
|
||||||
selectedCount,
|
selectedCount,
|
||||||
isSaving,
|
isSaving,
|
||||||
isDeleting,
|
isDeleting,
|
||||||
isOrganizingSeries,
|
isOrganizingArtist,
|
||||||
showLanguageProfile,
|
showLanguageProfile,
|
||||||
onOrganizeSeriesPress
|
onOrganizeArtistPress
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
|
||||||
const {
|
const {
|
||||||
monitored,
|
monitored,
|
||||||
qualityProfileId,
|
qualityProfileId,
|
||||||
languageProfileId,
|
languageProfileId,
|
||||||
seriesType,
|
albumFolder,
|
||||||
seasonFolder,
|
|
||||||
rootFolderPath,
|
rootFolderPath,
|
||||||
savingTags,
|
savingTags,
|
||||||
isTagsModalOpen,
|
isTagsModalOpen,
|
||||||
|
@ -137,7 +134,7 @@ class SeriesEditorFooter extends Component {
|
||||||
{ key: 'unmonitored', value: 'Unmonitored' }
|
{ key: 'unmonitored', value: 'Unmonitored' }
|
||||||
];
|
];
|
||||||
|
|
||||||
const seasonFolderOptions = [
|
const albumFolderOptions = [
|
||||||
{ key: NO_CHANGE, value: 'No Change', disabled: true },
|
{ key: NO_CHANGE, value: 'No Change', disabled: true },
|
||||||
{ key: 'yes', value: 'Yes' },
|
{ key: 'yes', value: 'Yes' },
|
||||||
{ key: 'no', value: 'No' }
|
{ key: 'no', value: 'No' }
|
||||||
|
@ -146,8 +143,8 @@ class SeriesEditorFooter extends Component {
|
||||||
return (
|
return (
|
||||||
<PageContentFooter>
|
<PageContentFooter>
|
||||||
<div className={styles.inputContainer}>
|
<div className={styles.inputContainer}>
|
||||||
<SeriesEditorFooterLabel
|
<ArtistEditorFooterLabel
|
||||||
label="Monitor Series"
|
label="Monitor Artist"
|
||||||
isSaving={isSaving && monitored !== NO_CHANGE}
|
isSaving={isSaving && monitored !== NO_CHANGE}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
@ -161,7 +158,7 @@ class SeriesEditorFooter extends Component {
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className={styles.inputContainer}>
|
<div className={styles.inputContainer}>
|
||||||
<SeriesEditorFooterLabel
|
<ArtistEditorFooterLabel
|
||||||
label="Quality Profile"
|
label="Quality Profile"
|
||||||
isSaving={isSaving && qualityProfileId !== NO_CHANGE}
|
isSaving={isSaving && qualityProfileId !== NO_CHANGE}
|
||||||
/>
|
/>
|
||||||
|
@ -178,7 +175,7 @@ class SeriesEditorFooter extends Component {
|
||||||
{
|
{
|
||||||
showLanguageProfile &&
|
showLanguageProfile &&
|
||||||
<div className={styles.inputContainer}>
|
<div className={styles.inputContainer}>
|
||||||
<SeriesEditorFooterLabel
|
<ArtistEditorFooterLabel
|
||||||
label="Language Profile"
|
label="Language Profile"
|
||||||
isSaving={isSaving && languageProfileId !== NO_CHANGE}
|
isSaving={isSaving && languageProfileId !== NO_CHANGE}
|
||||||
/>
|
/>
|
||||||
|
@ -194,37 +191,22 @@ class SeriesEditorFooter extends Component {
|
||||||
}
|
}
|
||||||
|
|
||||||
<div className={styles.inputContainer}>
|
<div className={styles.inputContainer}>
|
||||||
<SeriesEditorFooterLabel
|
<ArtistEditorFooterLabel
|
||||||
label="Series Type"
|
label="Album Folder"
|
||||||
isSaving={isSaving && seriesType !== NO_CHANGE}
|
isSaving={isSaving && albumFolder !== NO_CHANGE}
|
||||||
/>
|
|
||||||
|
|
||||||
<SeriesTypeSelectInput
|
|
||||||
name="seriesType"
|
|
||||||
value={seriesType}
|
|
||||||
includeNoChange={true}
|
|
||||||
isDisabled={!selectedCount}
|
|
||||||
onChange={this.onInputChange}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className={styles.inputContainer}>
|
|
||||||
<SeriesEditorFooterLabel
|
|
||||||
label="Season Folder"
|
|
||||||
isSaving={isSaving && seasonFolder !== NO_CHANGE}
|
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<SelectInput
|
<SelectInput
|
||||||
name="seasonFolder"
|
name="albumFolder"
|
||||||
value={seasonFolder}
|
value={albumFolder}
|
||||||
values={seasonFolderOptions}
|
values={albumFolderOptions}
|
||||||
isDisabled={!selectedCount}
|
isDisabled={!selectedCount}
|
||||||
onChange={this.onInputChange}
|
onChange={this.onInputChange}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className={styles.inputContainer}>
|
<div className={styles.inputContainer}>
|
||||||
<SeriesEditorFooterLabel
|
<ArtistEditorFooterLabel
|
||||||
label="Root Folder"
|
label="Root Folder"
|
||||||
isSaving={isSaving && rootFolderPath !== NO_CHANGE}
|
isSaving={isSaving && rootFolderPath !== NO_CHANGE}
|
||||||
/>
|
/>
|
||||||
|
@ -241,8 +223,8 @@ class SeriesEditorFooter extends Component {
|
||||||
|
|
||||||
<div className={styles.buttonContainer}>
|
<div className={styles.buttonContainer}>
|
||||||
<div className={styles.buttonContainerContent}>
|
<div className={styles.buttonContainerContent}>
|
||||||
<SeriesEditorFooterLabel
|
<ArtistEditorFooterLabel
|
||||||
label={`${selectedCount} Series Selected`}
|
label={`${selectedCount} Artist(s) Selected`}
|
||||||
isSaving={false}
|
isSaving={false}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
@ -251,9 +233,9 @@ class SeriesEditorFooter extends Component {
|
||||||
<SpinnerButton
|
<SpinnerButton
|
||||||
className={styles.organizeSelectedButton}
|
className={styles.organizeSelectedButton}
|
||||||
kind={kinds.WARNING}
|
kind={kinds.WARNING}
|
||||||
isSpinning={isOrganizingSeries}
|
isSpinning={isOrganizingArtist}
|
||||||
isDisabled={!selectedCount || isOrganizingSeries}
|
isDisabled={!selectedCount || isOrganizingArtist}
|
||||||
onPress={onOrganizeSeriesPress}
|
onPress={onOrganizeArtistPress}
|
||||||
>
|
>
|
||||||
Rename Files
|
Rename Files
|
||||||
</SpinnerButton>
|
</SpinnerButton>
|
||||||
|
@ -261,7 +243,7 @@ class SeriesEditorFooter extends Component {
|
||||||
<SpinnerButton
|
<SpinnerButton
|
||||||
className={styles.tagsButton}
|
className={styles.tagsButton}
|
||||||
isSpinning={isSaving && savingTags}
|
isSpinning={isSaving && savingTags}
|
||||||
isDisabled={!selectedCount || isOrganizingSeries}
|
isDisabled={!selectedCount || isOrganizingArtist}
|
||||||
onPress={this.onTagsPress}
|
onPress={this.onTagsPress}
|
||||||
>
|
>
|
||||||
Set Tags
|
Set Tags
|
||||||
|
@ -291,24 +273,24 @@ class SeriesEditorFooter extends Component {
|
||||||
<DeleteArtistModal
|
<DeleteArtistModal
|
||||||
isOpen={isDeleteArtistModalOpen}
|
isOpen={isDeleteArtistModalOpen}
|
||||||
artistIds={artistIds}
|
artistIds={artistIds}
|
||||||
onModalClose={this.onDeleteSeriesModalClose}
|
onModalClose={this.onDeleteArtistModalClose}
|
||||||
/>
|
/>
|
||||||
</PageContentFooter>
|
</PageContentFooter>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
SeriesEditorFooter.propTypes = {
|
ArtistEditorFooter.propTypes = {
|
||||||
artistIds: PropTypes.arrayOf(PropTypes.number).isRequired,
|
artistIds: PropTypes.arrayOf(PropTypes.number).isRequired,
|
||||||
selectedCount: PropTypes.number.isRequired,
|
selectedCount: PropTypes.number.isRequired,
|
||||||
isSaving: PropTypes.bool.isRequired,
|
isSaving: PropTypes.bool.isRequired,
|
||||||
saveError: PropTypes.object,
|
saveError: PropTypes.object,
|
||||||
isDeleting: PropTypes.bool.isRequired,
|
isDeleting: PropTypes.bool.isRequired,
|
||||||
deleteError: PropTypes.object,
|
deleteError: PropTypes.object,
|
||||||
isOrganizingSeries: PropTypes.bool.isRequired,
|
isOrganizingArtist: PropTypes.bool.isRequired,
|
||||||
showLanguageProfile: PropTypes.bool.isRequired,
|
showLanguageProfile: PropTypes.bool.isRequired,
|
||||||
onSaveSelected: PropTypes.func.isRequired,
|
onSaveSelected: PropTypes.func.isRequired,
|
||||||
onOrganizeSeriesPress: PropTypes.func.isRequired
|
onOrganizeArtistPress: PropTypes.func.isRequired
|
||||||
};
|
};
|
||||||
|
|
||||||
export default SeriesEditorFooter;
|
export default ArtistEditorFooter;
|
|
@ -2,9 +2,9 @@ import PropTypes from 'prop-types';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { icons } from 'Helpers/Props';
|
import { icons } from 'Helpers/Props';
|
||||||
import SpinnerIcon from 'Components/SpinnerIcon';
|
import SpinnerIcon from 'Components/SpinnerIcon';
|
||||||
import styles from './SeriesEditorFooterLabel.css';
|
import styles from './ArtistEditorFooterLabel.css';
|
||||||
|
|
||||||
function SeriesEditorFooterLabel(props) {
|
function ArtistEditorFooterLabel(props) {
|
||||||
const {
|
const {
|
||||||
className,
|
className,
|
||||||
label,
|
label,
|
||||||
|
@ -27,14 +27,14 @@ function SeriesEditorFooterLabel(props) {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
SeriesEditorFooterLabel.propTypes = {
|
ArtistEditorFooterLabel.propTypes = {
|
||||||
className: PropTypes.string.isRequired,
|
className: PropTypes.string.isRequired,
|
||||||
label: PropTypes.string.isRequired,
|
label: PropTypes.string.isRequired,
|
||||||
isSaving: PropTypes.bool.isRequired
|
isSaving: PropTypes.bool.isRequired
|
||||||
};
|
};
|
||||||
|
|
||||||
SeriesEditorFooterLabel.defaultProps = {
|
ArtistEditorFooterLabel.defaultProps = {
|
||||||
className: styles.label
|
className: styles.label
|
||||||
};
|
};
|
||||||
|
|
||||||
export default SeriesEditorFooterLabel;
|
export default ArtistEditorFooterLabel;
|
|
@ -1,4 +1,4 @@
|
||||||
.seasonFolder {
|
.albumFolder {
|
||||||
composes: cell from 'Components/Table/Cells/TableRowCell.css';
|
composes: cell from 'Components/Table/Cells/TableRowCell.css';
|
||||||
|
|
||||||
width: 150px;
|
width: 150px;
|
|
@ -9,9 +9,9 @@ import TableRowCell from 'Components/Table/Cells/TableRowCell';
|
||||||
import TableSelectCell from 'Components/Table/Cells/TableSelectCell';
|
import TableSelectCell from 'Components/Table/Cells/TableSelectCell';
|
||||||
import ArtistNameLink from 'Artist/ArtistNameLink';
|
import ArtistNameLink from 'Artist/ArtistNameLink';
|
||||||
import ArtistStatusCell from 'Artist/Index/Table/ArtistStatusCell';
|
import ArtistStatusCell from 'Artist/Index/Table/ArtistStatusCell';
|
||||||
import styles from './SeriesEditorRow.css';
|
import styles from './ArtistEditorRow.css';
|
||||||
|
|
||||||
class SeriesEditorRow extends Component {
|
class ArtistEditorRow extends Component {
|
||||||
|
|
||||||
//
|
//
|
||||||
// Listeners
|
// Listeners
|
||||||
|
@ -28,13 +28,12 @@ class SeriesEditorRow extends Component {
|
||||||
const {
|
const {
|
||||||
id,
|
id,
|
||||||
status,
|
status,
|
||||||
titleSlug,
|
nameSlug,
|
||||||
title,
|
artistName,
|
||||||
monitored,
|
monitored,
|
||||||
languageProfile,
|
languageProfile,
|
||||||
qualityProfile,
|
qualityProfile,
|
||||||
seriesType,
|
albumFolder,
|
||||||
seasonFolder,
|
|
||||||
path,
|
path,
|
||||||
tags,
|
tags,
|
||||||
columns,
|
columns,
|
||||||
|
@ -57,8 +56,8 @@ class SeriesEditorRow extends Component {
|
||||||
|
|
||||||
<TableRowCell className={styles.title}>
|
<TableRowCell className={styles.title}>
|
||||||
<ArtistNameLink
|
<ArtistNameLink
|
||||||
titleSlug={titleSlug}
|
nameSlug={nameSlug}
|
||||||
title={title}
|
artistName={artistName}
|
||||||
/>
|
/>
|
||||||
</TableRowCell>
|
</TableRowCell>
|
||||||
|
|
||||||
|
@ -73,14 +72,10 @@ class SeriesEditorRow extends Component {
|
||||||
</TableRowCell>
|
</TableRowCell>
|
||||||
}
|
}
|
||||||
|
|
||||||
<TableRowCell>
|
<TableRowCell className={styles.albumFolder}>
|
||||||
{titleCase(seriesType)}
|
|
||||||
</TableRowCell>
|
|
||||||
|
|
||||||
<TableRowCell className={styles.seasonFolder}>
|
|
||||||
<CheckInput
|
<CheckInput
|
||||||
name="seasonFolder"
|
name="albumFolder"
|
||||||
value={seasonFolder}
|
value={albumFolder}
|
||||||
isDisabled={true}
|
isDisabled={true}
|
||||||
onChange={this.onSeasonFolderChange}
|
onChange={this.onSeasonFolderChange}
|
||||||
/>
|
/>
|
||||||
|
@ -100,16 +95,15 @@ class SeriesEditorRow extends Component {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
SeriesEditorRow.propTypes = {
|
ArtistEditorRow.propTypes = {
|
||||||
id: PropTypes.number.isRequired,
|
id: PropTypes.number.isRequired,
|
||||||
status: PropTypes.string.isRequired,
|
status: PropTypes.string.isRequired,
|
||||||
titleSlug: PropTypes.string.isRequired,
|
nameSlug: PropTypes.string.isRequired,
|
||||||
title: PropTypes.string.isRequired,
|
artistName: PropTypes.string.isRequired,
|
||||||
monitored: PropTypes.bool.isRequired,
|
monitored: PropTypes.bool.isRequired,
|
||||||
languageProfile: PropTypes.object.isRequired,
|
languageProfile: PropTypes.object.isRequired,
|
||||||
qualityProfile: PropTypes.object.isRequired,
|
qualityProfile: PropTypes.object.isRequired,
|
||||||
seriesType: PropTypes.string.isRequired,
|
albumFolder: PropTypes.bool.isRequired,
|
||||||
seasonFolder: PropTypes.bool.isRequired,
|
|
||||||
path: PropTypes.string.isRequired,
|
path: PropTypes.string.isRequired,
|
||||||
tags: PropTypes.arrayOf(PropTypes.number).isRequired,
|
tags: PropTypes.arrayOf(PropTypes.number).isRequired,
|
||||||
columns: PropTypes.arrayOf(PropTypes.object).isRequired,
|
columns: PropTypes.arrayOf(PropTypes.object).isRequired,
|
||||||
|
@ -117,4 +111,4 @@ SeriesEditorRow.propTypes = {
|
||||||
onSelectedChange: PropTypes.func.isRequired
|
onSelectedChange: PropTypes.func.isRequired
|
||||||
};
|
};
|
||||||
|
|
||||||
export default SeriesEditorRow;
|
export default ArtistEditorRow;
|
|
@ -4,7 +4,7 @@ import { connect } from 'react-redux';
|
||||||
import { createSelector } from 'reselect';
|
import { createSelector } from 'reselect';
|
||||||
import createLanguageProfileSelector from 'Store/Selectors/createLanguageProfileSelector';
|
import createLanguageProfileSelector from 'Store/Selectors/createLanguageProfileSelector';
|
||||||
import createQualityProfileSelector from 'Store/Selectors/createQualityProfileSelector';
|
import createQualityProfileSelector from 'Store/Selectors/createQualityProfileSelector';
|
||||||
import SeriesEditorRow from './SeriesEditorRow';
|
import ArtistEditorRow from './ArtistEditorRow';
|
||||||
|
|
||||||
function createMapStateToProps() {
|
function createMapStateToProps() {
|
||||||
return createSelector(
|
return createSelector(
|
||||||
|
@ -19,16 +19,16 @@ function createMapStateToProps() {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function SeriesEditorRowConnector(props) {
|
function ArtistEditorRowConnector(props) {
|
||||||
return (
|
return (
|
||||||
<SeriesEditorRow
|
<ArtistEditorRow
|
||||||
{...props}
|
{...props}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
SeriesEditorRowConnector.propTypes = {
|
ArtistEditorRowConnector.propTypes = {
|
||||||
qualityProfileId: PropTypes.number.isRequired
|
qualityProfileId: PropTypes.number.isRequired
|
||||||
};
|
};
|
||||||
|
|
||||||
export default connect(createMapStateToProps)(SeriesEditorRowConnector);
|
export default connect(createMapStateToProps)(ArtistEditorRowConnector);
|
|
@ -51,19 +51,19 @@ class DeleteArtistModalContent extends Component {
|
||||||
return (
|
return (
|
||||||
<ModalContent onModalClose={onModalClose}>
|
<ModalContent onModalClose={onModalClose}>
|
||||||
<ModalHeader>
|
<ModalHeader>
|
||||||
Delete Selected Series
|
Delete Selected Artist
|
||||||
</ModalHeader>
|
</ModalHeader>
|
||||||
|
|
||||||
<ModalBody>
|
<ModalBody>
|
||||||
<div>
|
<div>
|
||||||
<FormGroup>
|
<FormGroup>
|
||||||
<FormLabel>{`Delete Series Folder${series.length > 1 ? 's' : ''}`}</FormLabel>
|
<FormLabel>{`Delete Artist Folder${series.length > 1 ? 's' : ''}`}</FormLabel>
|
||||||
|
|
||||||
<FormInputGroup
|
<FormInputGroup
|
||||||
type={inputTypes.CHECK}
|
type={inputTypes.CHECK}
|
||||||
name="deleteFiles"
|
name="deleteFiles"
|
||||||
value={deleteFiles}
|
value={deleteFiles}
|
||||||
helpText={`Delete Series Folder${series.length > 1 ? 's' : ''} and all contents`}
|
helpText={`Delete Artist Folder${series.length > 1 ? 's' : ''} and all contents`}
|
||||||
kind={kinds.DANGER}
|
kind={kinds.DANGER}
|
||||||
onChange={this.onDeleteFilesChange}
|
onChange={this.onDeleteFilesChange}
|
||||||
/>
|
/>
|
||||||
|
@ -71,15 +71,15 @@ class DeleteArtistModalContent extends Component {
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className={styles.message}>
|
<div className={styles.message}>
|
||||||
{`Are you sure you want to delete ${series.length} selected series${deleteFiles ? ' and all contents' : ''}?`}
|
{`Are you sure you want to delete ${series.length} selected artist${series.length > 1 ? 's' : ''}${deleteFiles ? ' and all contents' : ''}?`}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<ul>
|
<ul>
|
||||||
{
|
{
|
||||||
series.map((s) => {
|
series.map((s) => {
|
||||||
return (
|
return (
|
||||||
<li key={s.title}>
|
<li key={s.artistName}>
|
||||||
<span>{s.title}</span>
|
<span>{s.artistName}</span>
|
||||||
|
|
||||||
{
|
{
|
||||||
deleteFiles &&
|
deleteFiles &&
|
||||||
|
|
|
@ -1,23 +1,23 @@
|
||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import { createSelector } from 'reselect';
|
import { createSelector } from 'reselect';
|
||||||
import createAllSeriesSelector from 'Store/Selectors/createAllSeriesSelector';
|
import createAllArtistSelector from 'Store/Selectors/createAllArtistSelector';
|
||||||
import { bulkDeleteArtist } from 'Store/Actions/seriesEditorActions';
|
import { bulkDeleteArtist } from 'Store/Actions/artistEditorActions';
|
||||||
import DeleteArtistModalContent from './DeleteArtistModalContent';
|
import DeleteArtistModalContent from './DeleteArtistModalContent';
|
||||||
|
|
||||||
function createMapStateToProps() {
|
function createMapStateToProps() {
|
||||||
return createSelector(
|
return createSelector(
|
||||||
(state, { artistIds }) => artistIds,
|
(state, { artistIds }) => artistIds,
|
||||||
createAllSeriesSelector(),
|
createAllArtistSelector(),
|
||||||
(artistIds, allSeries) => {
|
(artistIds, allSeries) => {
|
||||||
const selectedSeries = _.intersectionWith(allSeries, artistIds, (s, id) => {
|
const selectedSeries = _.intersectionWith(allSeries, artistIds, (s, id) => {
|
||||||
return s.id === id;
|
return s.id === id;
|
||||||
});
|
});
|
||||||
|
|
||||||
const sortedSeries = _.orderBy(selectedSeries, 'sortTitle');
|
const sortedArtist = _.orderBy(selectedSeries, 'sortName');
|
||||||
const series = _.map(sortedSeries, (s) => {
|
const series = _.map(sortedArtist, (s) => {
|
||||||
return {
|
return {
|
||||||
title: s.title,
|
artistName: s.artistName,
|
||||||
path: s.path
|
path: s.path
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import Modal from 'Components/Modal/Modal';
|
import Modal from 'Components/Modal/Modal';
|
||||||
import OrganizeSeriesModalContentConnector from './OrganizeSeriesModalContentConnector';
|
import OrganizeArtistModalContentConnector from './OrganizeArtistModalContentConnector';
|
||||||
|
|
||||||
function OrganizeSeriesModal(props) {
|
function OrganizeArtistModal(props) {
|
||||||
const {
|
const {
|
||||||
isOpen,
|
isOpen,
|
||||||
onModalClose,
|
onModalClose,
|
||||||
|
@ -15,7 +15,7 @@ function OrganizeSeriesModal(props) {
|
||||||
isOpen={isOpen}
|
isOpen={isOpen}
|
||||||
onModalClose={onModalClose}
|
onModalClose={onModalClose}
|
||||||
>
|
>
|
||||||
<OrganizeSeriesModalContentConnector
|
<OrganizeArtistModalContentConnector
|
||||||
{...otherProps}
|
{...otherProps}
|
||||||
onModalClose={onModalClose}
|
onModalClose={onModalClose}
|
||||||
/>
|
/>
|
||||||
|
@ -23,9 +23,9 @@ function OrganizeSeriesModal(props) {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
OrganizeSeriesModal.propTypes = {
|
OrganizeArtistModal.propTypes = {
|
||||||
isOpen: PropTypes.bool.isRequired,
|
isOpen: PropTypes.bool.isRequired,
|
||||||
onModalClose: PropTypes.func.isRequired
|
onModalClose: PropTypes.func.isRequired
|
||||||
};
|
};
|
||||||
|
|
||||||
export default OrganizeSeriesModal;
|
export default OrganizeArtistModal;
|
|
@ -8,24 +8,24 @@ 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 './OrganizeSeriesModalContent.css';
|
import styles from './OrganizeArtistModalContent.css';
|
||||||
|
|
||||||
function OrganizeSeriesModalContent(props) {
|
function OrganizeArtistModalContent(props) {
|
||||||
const {
|
const {
|
||||||
seriesTitles,
|
artistNames,
|
||||||
onModalClose,
|
onModalClose,
|
||||||
onOrganizeSeriesPress
|
onOrganizeArtistPress
|
||||||
} = props;
|
} = props;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ModalContent onModalClose={onModalClose}>
|
<ModalContent onModalClose={onModalClose}>
|
||||||
<ModalHeader>
|
<ModalHeader>
|
||||||
Organize Selected Series
|
Organize Selected Artist
|
||||||
</ModalHeader>
|
</ModalHeader>
|
||||||
|
|
||||||
<ModalBody>
|
<ModalBody>
|
||||||
<Alert>
|
<Alert>
|
||||||
Tip: To preview a rename... select "Cancel" then any series title and use the
|
Tip: To preview a rename... select "Cancel" then click any artist name and use the
|
||||||
<Icon
|
<Icon
|
||||||
className={styles.renameIcon}
|
className={styles.renameIcon}
|
||||||
name={icons.ORGANIZE}
|
name={icons.ORGANIZE}
|
||||||
|
@ -33,15 +33,15 @@ function OrganizeSeriesModalContent(props) {
|
||||||
</Alert>
|
</Alert>
|
||||||
|
|
||||||
<div className={styles.message}>
|
<div className={styles.message}>
|
||||||
Are you sure you want to organize all files in the {seriesTitles.length} selected series?
|
Are you sure you want to organize all files in the {artistNames.length} selected artist?
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<ul>
|
<ul>
|
||||||
{
|
{
|
||||||
seriesTitles.map((title) => {
|
artistNames.map((artistName) => {
|
||||||
return (
|
return (
|
||||||
<li key={title}>
|
<li key={artistName}>
|
||||||
{title}
|
{artistName}
|
||||||
</li>
|
</li>
|
||||||
);
|
);
|
||||||
})
|
})
|
||||||
|
@ -56,7 +56,7 @@ function OrganizeSeriesModalContent(props) {
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
kind={kinds.DANGER}
|
kind={kinds.DANGER}
|
||||||
onPress={onOrganizeSeriesPress}
|
onPress={onOrganizeArtistPress}
|
||||||
>
|
>
|
||||||
Organize
|
Organize
|
||||||
</Button>
|
</Button>
|
||||||
|
@ -65,10 +65,10 @@ function OrganizeSeriesModalContent(props) {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
OrganizeSeriesModalContent.propTypes = {
|
OrganizeArtistModalContent.propTypes = {
|
||||||
seriesTitles: PropTypes.arrayOf(PropTypes.string).isRequired,
|
artistNames: PropTypes.arrayOf(PropTypes.string).isRequired,
|
||||||
onModalClose: PropTypes.func.isRequired,
|
onModalClose: PropTypes.func.isRequired,
|
||||||
onOrganizeSeriesPress: PropTypes.func.isRequired
|
onOrganizeArtistPress: PropTypes.func.isRequired
|
||||||
};
|
};
|
||||||
|
|
||||||
export default OrganizeSeriesModalContent;
|
export default OrganizeArtistModalContent;
|
|
@ -3,25 +3,25 @@ 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';
|
||||||
import { createSelector } from 'reselect';
|
import { createSelector } from 'reselect';
|
||||||
import createAllSeriesSelector from 'Store/Selectors/createAllSeriesSelector';
|
import createAllArtistSelector from 'Store/Selectors/createAllArtistSelector';
|
||||||
import { executeCommand } from 'Store/Actions/commandActions';
|
import { executeCommand } from 'Store/Actions/commandActions';
|
||||||
import * as commandNames from 'Commands/commandNames';
|
import * as commandNames from 'Commands/commandNames';
|
||||||
import OrganizeSeriesModalContent from './OrganizeSeriesModalContent';
|
import OrganizeArtistModalContent from './OrganizeArtistModalContent';
|
||||||
|
|
||||||
function createMapStateToProps() {
|
function createMapStateToProps() {
|
||||||
return createSelector(
|
return createSelector(
|
||||||
(state, { artistIds }) => artistIds,
|
(state, { artistIds }) => artistIds,
|
||||||
createAllSeriesSelector(),
|
createAllArtistSelector(),
|
||||||
(artistIds, allSeries) => {
|
(artistIds, allSeries) => {
|
||||||
const series = _.intersectionWith(allSeries, artistIds, (s, id) => {
|
const series = _.intersectionWith(allSeries, artistIds, (s, id) => {
|
||||||
return s.id === id;
|
return s.id === id;
|
||||||
});
|
});
|
||||||
|
|
||||||
const sortedSeries = _.orderBy(series, 'sortTitle');
|
const sortedArtist = _.orderBy(series, 'sortName');
|
||||||
const seriesTitles = _.map(sortedSeries, 'title');
|
const artistNames = _.map(sortedArtist, 'artistName');
|
||||||
|
|
||||||
return {
|
return {
|
||||||
seriesTitles
|
artistNames
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
@ -31,14 +31,14 @@ const mapDispatchToProps = {
|
||||||
executeCommand
|
executeCommand
|
||||||
};
|
};
|
||||||
|
|
||||||
class OrganizeSeriesModalContentConnector extends Component {
|
class OrganizeArtistModalContentConnector extends Component {
|
||||||
|
|
||||||
//
|
//
|
||||||
// Listeners
|
// Listeners
|
||||||
|
|
||||||
onOrganizeSeriesPress = () => {
|
onOrganizeArtistPress = () => {
|
||||||
this.props.executeCommand({
|
this.props.executeCommand({
|
||||||
name: commandNames.RENAME_SERIES,
|
name: commandNames.RENAME_ARTIST,
|
||||||
artistIds: this.props.artistIds
|
artistIds: this.props.artistIds
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -50,18 +50,18 @@ class OrganizeSeriesModalContentConnector extends Component {
|
||||||
|
|
||||||
render(props) {
|
render(props) {
|
||||||
return (
|
return (
|
||||||
<OrganizeSeriesModalContent
|
<OrganizeArtistModalContent
|
||||||
{...this.props}
|
{...this.props}
|
||||||
onOrganizeSeriesPress={this.onOrganizeSeriesPress}
|
onOrganizeArtistPress={this.onOrganizeArtistPress}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
OrganizeSeriesModalContentConnector.propTypes = {
|
OrganizeArtistModalContentConnector.propTypes = {
|
||||||
artistIds: PropTypes.arrayOf(PropTypes.number).isRequired,
|
artistIds: PropTypes.arrayOf(PropTypes.number).isRequired,
|
||||||
onModalClose: PropTypes.func.isRequired,
|
onModalClose: PropTypes.func.isRequired,
|
||||||
executeCommand: PropTypes.func.isRequired
|
executeCommand: PropTypes.func.isRequired
|
||||||
};
|
};
|
||||||
|
|
||||||
export default connect(createMapStateToProps, mapDispatchToProps)(OrganizeSeriesModalContentConnector);
|
export default connect(createMapStateToProps, mapDispatchToProps)(OrganizeArtistModalContentConnector);
|
|
@ -1,14 +1,14 @@
|
||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import { createSelector } from 'reselect';
|
import { createSelector } from 'reselect';
|
||||||
import createAllSeriesSelector from 'Store/Selectors/createAllSeriesSelector';
|
import createAllArtistSelector from 'Store/Selectors/createAllArtistSelector';
|
||||||
import createTagsSelector from 'Store/Selectors/createTagsSelector';
|
import createTagsSelector from 'Store/Selectors/createTagsSelector';
|
||||||
import TagsModalContent from './TagsModalContent';
|
import TagsModalContent from './TagsModalContent';
|
||||||
|
|
||||||
function createMapStateToProps() {
|
function createMapStateToProps() {
|
||||||
return createSelector(
|
return createSelector(
|
||||||
(state, { artistIds }) => artistIds,
|
(state, { artistIds }) => artistIds,
|
||||||
createAllSeriesSelector(),
|
createAllArtistSelector(),
|
||||||
createTagsSelector(),
|
createTagsSelector(),
|
||||||
(artistIds, allSeries, tagList) => {
|
(artistIds, allSeries, tagList) => {
|
||||||
const series = _.intersectionWith(allSeries, artistIds, (s, id) => {
|
const series = _.intersectionWith(allSeries, artistIds, (s, id) => {
|
||||||
|
|
|
@ -168,7 +168,7 @@ class ArtistIndex extends Component {
|
||||||
onSortSelect,
|
onSortSelect,
|
||||||
onFilterSelect,
|
onFilterSelect,
|
||||||
onViewSelect,
|
onViewSelect,
|
||||||
onRefreshSeriesPress,
|
onRefreshArtistPress,
|
||||||
onRssSyncPress,
|
onRssSyncPress,
|
||||||
...otherProps
|
...otherProps
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
@ -192,7 +192,7 @@ class ArtistIndex extends Component {
|
||||||
iconName={icons.REFRESH}
|
iconName={icons.REFRESH}
|
||||||
spinningName={icons.REFRESH}
|
spinningName={icons.REFRESH}
|
||||||
isSpinning={isRefreshingSeries}
|
isSpinning={isRefreshingSeries}
|
||||||
onPress={onRefreshSeriesPress}
|
onPress={onRefreshArtistPress}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<PageToolbarButton
|
<PageToolbarButton
|
||||||
|
@ -257,7 +257,7 @@ class ArtistIndex extends Component {
|
||||||
|
|
||||||
{
|
{
|
||||||
!isFetching && !!error &&
|
!isFetching && !!error &&
|
||||||
<div>Unable to load series</div>
|
<div>Unable to load artist</div>
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
|
@ -318,7 +318,7 @@ ArtistIndex.propTypes = {
|
||||||
onSortSelect: PropTypes.func.isRequired,
|
onSortSelect: PropTypes.func.isRequired,
|
||||||
onFilterSelect: PropTypes.func.isRequired,
|
onFilterSelect: PropTypes.func.isRequired,
|
||||||
onViewSelect: PropTypes.func.isRequired,
|
onViewSelect: PropTypes.func.isRequired,
|
||||||
onRefreshSeriesPress: PropTypes.func.isRequired,
|
onRefreshArtistPress: PropTypes.func.isRequired,
|
||||||
onRssSyncPress: PropTypes.func.isRequired,
|
onRssSyncPress: PropTypes.func.isRequired,
|
||||||
onScroll: PropTypes.func.isRequired
|
onScroll: PropTypes.func.isRequired
|
||||||
};
|
};
|
||||||
|
|
|
@ -6,7 +6,7 @@ import { createSelector } from 'reselect';
|
||||||
import dimensions from 'Styles/Variables/dimensions';
|
import dimensions from 'Styles/Variables/dimensions';
|
||||||
import createCommandSelector from 'Store/Selectors/createCommandSelector';
|
import createCommandSelector from 'Store/Selectors/createCommandSelector';
|
||||||
import createDimensionsSelector from 'Store/Selectors/createDimensionsSelector';
|
import createDimensionsSelector from 'Store/Selectors/createDimensionsSelector';
|
||||||
import { fetchArtist } from 'Store/Actions/seriesActions';
|
import { fetchArtist } from 'Store/Actions/artistActions';
|
||||||
import scrollPositions from 'Store/scrollPositions';
|
import scrollPositions from 'Store/scrollPositions';
|
||||||
import { setArtistSort, setArtistFilter, setArtistView } from 'Store/Actions/artistIndexActions';
|
import { setArtistSort, setArtistFilter, setArtistView } from 'Store/Actions/artistIndexActions';
|
||||||
import { executeCommand } from 'Store/Actions/commandActions';
|
import { executeCommand } from 'Store/Actions/commandActions';
|
||||||
|
@ -41,7 +41,7 @@ function createMapStateToProps() {
|
||||||
return createSelector(
|
return createSelector(
|
||||||
(state) => state.series,
|
(state) => state.series,
|
||||||
(state) => state.seriesIndex,
|
(state) => state.seriesIndex,
|
||||||
createCommandSelector(commandNames.REFRESH_SERIES),
|
createCommandSelector(commandNames.REFRESH_ARTIST),
|
||||||
createCommandSelector(commandNames.RSS_SYNC),
|
createCommandSelector(commandNames.RSS_SYNC),
|
||||||
createDimensionsSelector(),
|
createDimensionsSelector(),
|
||||||
(series, seriesIndex, isRefreshingSeries, isRssSyncExecuting, dimensionsState) => {
|
(series, seriesIndex, isRefreshingSeries, isRssSyncExecuting, dimensionsState) => {
|
||||||
|
@ -113,9 +113,9 @@ class ArtistIndexConnector extends Component {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
onRefreshSeriesPress = () => {
|
onRefreshArtistPress = () => {
|
||||||
this.props.executeCommand({
|
this.props.executeCommand({
|
||||||
name: commandNames.REFRESH_SERIES
|
name: commandNames.REFRESH_ARTIST
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -137,7 +137,7 @@ class ArtistIndexConnector extends Component {
|
||||||
onFilterSelect={this.onFilterSelect}
|
onFilterSelect={this.onFilterSelect}
|
||||||
onViewSelect={this.onViewSelect}
|
onViewSelect={this.onViewSelect}
|
||||||
onScroll={this.onScroll}
|
onScroll={this.onScroll}
|
||||||
onRefreshSeriesPress={this.onRefreshSeriesPress}
|
onRefreshArtistPress={this.onRefreshArtistPress}
|
||||||
onRssSyncPress={this.onRssSyncPress}
|
onRssSyncPress={this.onRssSyncPress}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|
|
@ -18,7 +18,7 @@ function createMapStateToProps() {
|
||||||
createCommandsSelector(),
|
createCommandsSelector(),
|
||||||
(artistId, seasons, qualityProfile, languageProfile, commands) => {
|
(artistId, seasons, qualityProfile, languageProfile, commands) => {
|
||||||
const isRefreshingSeries = _.some(commands, (command) => {
|
const isRefreshingSeries = _.some(commands, (command) => {
|
||||||
return command.name === commandNames.REFRESH_SERIES &&
|
return command.name === commandNames.REFRESH_ARTIST &&
|
||||||
command.body.artistId === artistId;
|
command.body.artistId === artistId;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -43,9 +43,9 @@ class ArtistIndexItemConnector extends Component {
|
||||||
//
|
//
|
||||||
// Listeners
|
// Listeners
|
||||||
|
|
||||||
onRefreshSeriesPress = () => {
|
onRefreshArtistPress = () => {
|
||||||
this.props.executeCommand({
|
this.props.executeCommand({
|
||||||
name: commandNames.REFRESH_SERIES,
|
name: commandNames.REFRESH_ARTIST,
|
||||||
artistId: this.props.id
|
artistId: this.props.id
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -62,7 +62,7 @@ class ArtistIndexItemConnector extends Component {
|
||||||
return (
|
return (
|
||||||
<ItemComponent
|
<ItemComponent
|
||||||
{...otherProps}
|
{...otherProps}
|
||||||
onRefreshSeriesPress={this.onRefreshSeriesPress}
|
onRefreshArtistPress={this.onRefreshArtistPress}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -45,7 +45,7 @@ class ArtistIndexPoster extends Component {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
onDeleteSeriesModalClose = () => {
|
onDeleteArtistModalClose = () => {
|
||||||
this.setState({ isDeleteArtistModalOpen: false });
|
this.setState({ isDeleteArtistModalOpen: false });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -74,7 +74,7 @@ class ArtistIndexPoster extends Component {
|
||||||
shortDateFormat,
|
shortDateFormat,
|
||||||
timeFormat,
|
timeFormat,
|
||||||
isRefreshingSeries,
|
isRefreshingSeries,
|
||||||
onRefreshSeriesPress,
|
onRefreshArtistPress,
|
||||||
...otherProps
|
...otherProps
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
|
||||||
|
@ -83,7 +83,7 @@ class ArtistIndexPoster extends Component {
|
||||||
isDeleteArtistModalOpen
|
isDeleteArtistModalOpen
|
||||||
} = this.state;
|
} = this.state;
|
||||||
|
|
||||||
const link = `/series/${nameSlug}`;
|
const link = `/artist/${nameSlug}`;
|
||||||
|
|
||||||
const elementStyle = {
|
const elementStyle = {
|
||||||
width: `${posterWidth}px`,
|
width: `${posterWidth}px`,
|
||||||
|
@ -100,7 +100,7 @@ class ArtistIndexPoster extends Component {
|
||||||
name={icons.REFRESH}
|
name={icons.REFRESH}
|
||||||
title="Refresh Artist"
|
title="Refresh Artist"
|
||||||
isSpinning={isRefreshingSeries}
|
isSpinning={isRefreshingSeries}
|
||||||
onPress={onRefreshSeriesPress}
|
onPress={onRefreshArtistPress}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<IconButton
|
<IconButton
|
||||||
|
@ -190,7 +190,7 @@ class ArtistIndexPoster extends Component {
|
||||||
<DeleteArtistModal
|
<DeleteArtistModal
|
||||||
isOpen={isDeleteArtistModalOpen}
|
isOpen={isDeleteArtistModalOpen}
|
||||||
artistId={id}
|
artistId={id}
|
||||||
onModalClose={this.onDeleteSeriesModalClose}
|
onModalClose={this.onDeleteArtistModalClose}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -219,7 +219,7 @@ ArtistIndexPoster.propTypes = {
|
||||||
shortDateFormat: PropTypes.string.isRequired,
|
shortDateFormat: PropTypes.string.isRequired,
|
||||||
timeFormat: PropTypes.string.isRequired,
|
timeFormat: PropTypes.string.isRequired,
|
||||||
isRefreshingSeries: PropTypes.bool.isRequired,
|
isRefreshingSeries: PropTypes.bool.isRequired,
|
||||||
onRefreshSeriesPress: PropTypes.func.isRequired
|
onRefreshArtistPress: PropTypes.func.isRequired
|
||||||
};
|
};
|
||||||
|
|
||||||
ArtistIndexPoster.defaultProps = {
|
ArtistIndexPoster.defaultProps = {
|
||||||
|
|
|
@ -39,7 +39,7 @@ class ArtistIndexActionsCell extends Component {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
onDeleteSeriesModalClose = () => {
|
onDeleteArtistModalClose = () => {
|
||||||
this.setState({ isDeleteArtistModalOpen: false });
|
this.setState({ isDeleteArtistModalOpen: false });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -50,7 +50,7 @@ class ArtistIndexActionsCell extends Component {
|
||||||
const {
|
const {
|
||||||
id,
|
id,
|
||||||
isRefreshingSeries,
|
isRefreshingSeries,
|
||||||
onRefreshSeriesPress,
|
onRefreshArtistPress,
|
||||||
...otherProps
|
...otherProps
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
|
||||||
|
@ -67,7 +67,7 @@ class ArtistIndexActionsCell extends Component {
|
||||||
name={icons.REFRESH}
|
name={icons.REFRESH}
|
||||||
title="Refresh Artist"
|
title="Refresh Artist"
|
||||||
isSpinning={isRefreshingSeries}
|
isSpinning={isRefreshingSeries}
|
||||||
onPress={onRefreshSeriesPress}
|
onPress={onRefreshArtistPress}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<IconButton
|
<IconButton
|
||||||
|
@ -86,7 +86,7 @@ class ArtistIndexActionsCell extends Component {
|
||||||
<DeleteArtistModal
|
<DeleteArtistModal
|
||||||
isOpen={isDeleteArtistModalOpen}
|
isOpen={isDeleteArtistModalOpen}
|
||||||
artistId={id}
|
artistId={id}
|
||||||
onModalClose={this.onDeleteSeriesModalClose}
|
onModalClose={this.onDeleteArtistModalClose}
|
||||||
/>
|
/>
|
||||||
</VirtualTableRowCell>
|
</VirtualTableRowCell>
|
||||||
);
|
);
|
||||||
|
@ -96,7 +96,7 @@ class ArtistIndexActionsCell extends Component {
|
||||||
ArtistIndexActionsCell.propTypes = {
|
ArtistIndexActionsCell.propTypes = {
|
||||||
id: PropTypes.number.isRequired,
|
id: PropTypes.number.isRequired,
|
||||||
isRefreshingSeries: PropTypes.bool.isRequired,
|
isRefreshingSeries: PropTypes.bool.isRequired,
|
||||||
onRefreshSeriesPress: PropTypes.func.isRequired
|
onRefreshArtistPress: PropTypes.func.isRequired
|
||||||
};
|
};
|
||||||
|
|
||||||
export default ArtistIndexActionsCell;
|
export default ArtistIndexActionsCell;
|
||||||
|
|
|
@ -46,7 +46,7 @@ class ArtistIndexRow extends Component {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
onDeleteSeriesModalClose = () => {
|
onDeleteArtistModalClose = () => {
|
||||||
this.setState({ isDeleteArtistModalOpen: false });
|
this.setState({ isDeleteArtistModalOpen: false });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -83,7 +83,7 @@ class ArtistIndexRow extends Component {
|
||||||
// useSceneNumbering,
|
// useSceneNumbering,
|
||||||
columns,
|
columns,
|
||||||
isRefreshingSeries,
|
isRefreshingSeries,
|
||||||
onRefreshSeriesPress
|
onRefreshArtistPress
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
|
||||||
const {
|
const {
|
||||||
|
@ -321,7 +321,7 @@ class ArtistIndexRow extends Component {
|
||||||
name={icons.REFRESH}
|
name={icons.REFRESH}
|
||||||
title="Refresh Artist"
|
title="Refresh Artist"
|
||||||
isSpinning={isRefreshingSeries}
|
isSpinning={isRefreshingSeries}
|
||||||
onPress={onRefreshSeriesPress}
|
onPress={onRefreshArtistPress}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<IconButton
|
<IconButton
|
||||||
|
@ -347,7 +347,7 @@ class ArtistIndexRow extends Component {
|
||||||
<DeleteArtistModal
|
<DeleteArtistModal
|
||||||
isOpen={isDeleteArtistModalOpen}
|
isOpen={isDeleteArtistModalOpen}
|
||||||
artistId={id}
|
artistId={id}
|
||||||
onModalClose={this.onDeleteSeriesModalClose}
|
onModalClose={this.onDeleteArtistModalClose}
|
||||||
/>
|
/>
|
||||||
</VirtualTableRow>
|
</VirtualTableRow>
|
||||||
);
|
);
|
||||||
|
@ -378,7 +378,7 @@ ArtistIndexRow.propTypes = {
|
||||||
// useSceneNumbering: PropTypes.bool.isRequired,
|
// useSceneNumbering: PropTypes.bool.isRequired,
|
||||||
columns: PropTypes.arrayOf(PropTypes.object).isRequired,
|
columns: PropTypes.arrayOf(PropTypes.object).isRequired,
|
||||||
isRefreshingSeries: PropTypes.bool.isRequired,
|
isRefreshingSeries: PropTypes.bool.isRequired,
|
||||||
onRefreshSeriesPress: PropTypes.func.isRequired
|
onRefreshArtistPress: PropTypes.func.isRequired
|
||||||
};
|
};
|
||||||
|
|
||||||
ArtistIndexRow.defaultProps = {
|
ArtistIndexRow.defaultProps = {
|
||||||
|
|
|
@ -10,10 +10,10 @@ export const DOWNLOADED_EPSIODES_SCAN = 'DownloadedEpisodesScan';
|
||||||
export const EPISODE_SEARCH = 'EpisodeSearch';
|
export const EPISODE_SEARCH = 'EpisodeSearch';
|
||||||
export const INTERACTIVE_IMPORT = 'ManualImport';
|
export const INTERACTIVE_IMPORT = 'ManualImport';
|
||||||
export const MISSING_EPISODE_SEARCH = 'MissingEpisodeSearch';
|
export const MISSING_EPISODE_SEARCH = 'MissingEpisodeSearch';
|
||||||
export const REFRESH_SERIES = 'RefreshSeries';
|
export const REFRESH_ARTIST = 'RefreshArtist';
|
||||||
export const RENAME_FILES = 'RenameFiles';
|
export const RENAME_FILES = 'RenameFiles';
|
||||||
export const RENAME_SERIES = 'RenameSeries';
|
export const RENAME_ARTIST = 'RenameArtist';
|
||||||
export const RESET_API_KEY = 'ResetApiKey';
|
export const RESET_API_KEY = 'ResetApiKey';
|
||||||
export const RSS_SYNC = 'RssSync';
|
export const RSS_SYNC = 'RssSync';
|
||||||
export const SEASON_SEARCH = 'SeasonSearch';
|
export const SEASON_SEARCH = 'SeasonSearch';
|
||||||
export const SERIES_SEARCH = 'SeriesSearch';
|
export const ARTIST_SEARCH = 'ArtistSearch';
|
||||||
|
|
|
@ -18,9 +18,9 @@ function ErrorPage(props) {
|
||||||
if (!isLocalStorageSupported) {
|
if (!isLocalStorageSupported) {
|
||||||
errorMessage = 'Local Storage is not supported or disabled. A plugin or private browsing may have disabled it.';
|
errorMessage = 'Local Storage is not supported or disabled. A plugin or private browsing may have disabled it.';
|
||||||
} else if (seriesError) {
|
} else if (seriesError) {
|
||||||
errorMessage = getErrorMessage(seriesError, 'Failed to load series from API');
|
errorMessage = getErrorMessage(seriesError, 'Failed to load artist from API');
|
||||||
} else if (tagsError) {
|
} else if (tagsError) {
|
||||||
errorMessage = getErrorMessage(seriesError, 'Failed to load series from API');
|
errorMessage = getErrorMessage(seriesError, 'Failed to load artist from API');
|
||||||
} else if (qualityProfilesError) {
|
} else if (qualityProfilesError) {
|
||||||
errorMessage = getErrorMessage(qualityProfilesError, 'Failed to load quality profiles from API');
|
errorMessage = getErrorMessage(qualityProfilesError, 'Failed to load quality profiles from API');
|
||||||
} else if (uiSettingsError) {
|
} else if (uiSettingsError) {
|
||||||
|
|
|
@ -6,12 +6,12 @@ import jdu from 'jdu';
|
||||||
import { icons } from 'Helpers/Props';
|
import { icons } from 'Helpers/Props';
|
||||||
import Icon from 'Components/Icon';
|
import Icon from 'Components/Icon';
|
||||||
import keyboardShortcuts, { shortcuts } from 'Components/keyboardShortcuts';
|
import keyboardShortcuts, { shortcuts } from 'Components/keyboardShortcuts';
|
||||||
import SeriesSearchResult from './SeriesSearchResult';
|
import ArtistSearchResult from './ArtistSearchResult';
|
||||||
import styles from './SeriesSearchInput.css';
|
import styles from './ArtistSearchInput.css';
|
||||||
|
|
||||||
const ADD_NEW_TYPE = 'addNew';
|
const ADD_NEW_TYPE = 'addNew';
|
||||||
|
|
||||||
class SeriesSearchInput extends Component {
|
class ArtistSearchInput extends Component {
|
||||||
|
|
||||||
//
|
//
|
||||||
// Lifecycle
|
// Lifecycle
|
||||||
|
@ -28,7 +28,7 @@ class SeriesSearchInput extends Component {
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
this.props.bindShortcut(shortcuts.SERIES_SEARCH_INPUT.key, this.focusInput);
|
this.props.bindShortcut(shortcuts.ARTIST_SEARCH_INPUT.key, this.focusInput);
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
|
@ -69,7 +69,7 @@ class SeriesSearchInput extends Component {
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<SeriesSearchResult
|
<ArtistSearchResult
|
||||||
query={query}
|
query={query}
|
||||||
{...item}
|
{...item}
|
||||||
/>
|
/>
|
||||||
|
@ -78,7 +78,7 @@ class SeriesSearchInput extends Component {
|
||||||
|
|
||||||
goToSeries(series) {
|
goToSeries(series) {
|
||||||
this.setState({ value: '' });
|
this.setState({ value: '' });
|
||||||
this.props.onGoToSeries(series.titleSlug);
|
this.props.onGoToSeries(series.nameSlug);
|
||||||
}
|
}
|
||||||
|
|
||||||
reset() {
|
reset() {
|
||||||
|
@ -137,7 +137,7 @@ class SeriesSearchInput extends Component {
|
||||||
const suggestions = _.filter(this.props.series, (series) => {
|
const suggestions = _.filter(this.props.series, (series) => {
|
||||||
// Check the title first and if there isn't a match fallback to the alternate titles
|
// Check the title first and if there isn't a match fallback to the alternate titles
|
||||||
|
|
||||||
const titleMatch = jdu.replace(series.title).toLowerCase().contains(lowerCaseValue);
|
const titleMatch = jdu.replace(series.artistName).toLowerCase().contains(lowerCaseValue);
|
||||||
|
|
||||||
return titleMatch || _.some(series.alternateTitles, (alternateTitle) => {
|
return titleMatch || _.some(series.alternateTitles, (alternateTitle) => {
|
||||||
return jdu.replace(alternateTitle.title).toLowerCase().contains(lowerCaseValue);
|
return jdu.replace(alternateTitle.title).toLowerCase().contains(lowerCaseValue);
|
||||||
|
@ -172,14 +172,14 @@ class SeriesSearchInput extends Component {
|
||||||
|
|
||||||
if (suggestions.length) {
|
if (suggestions.length) {
|
||||||
suggestionGroups.push({
|
suggestionGroups.push({
|
||||||
title: 'Existing Series',
|
title: 'Existing Artist',
|
||||||
suggestions
|
suggestions
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (suggestions.length <= 3) {
|
if (suggestions.length <= 3) {
|
||||||
suggestionGroups.push({
|
suggestionGroups.push({
|
||||||
title: 'Add New Series',
|
title: 'Add New Artist',
|
||||||
suggestions: [
|
suggestions: [
|
||||||
{
|
{
|
||||||
type: ADD_NEW_TYPE,
|
type: ADD_NEW_TYPE,
|
||||||
|
@ -240,11 +240,11 @@ class SeriesSearchInput extends Component {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
SeriesSearchInput.propTypes = {
|
ArtistSearchInput.propTypes = {
|
||||||
series: PropTypes.arrayOf(PropTypes.object).isRequired,
|
series: PropTypes.arrayOf(PropTypes.object).isRequired,
|
||||||
onGoToSeries: PropTypes.func.isRequired,
|
onGoToSeries: PropTypes.func.isRequired,
|
||||||
onGoToAddNewArtist: PropTypes.func.isRequired,
|
onGoToAddNewArtist: PropTypes.func.isRequired,
|
||||||
bindShortcut: PropTypes.func.isRequired
|
bindShortcut: PropTypes.func.isRequired
|
||||||
};
|
};
|
||||||
|
|
||||||
export default keyboardShortcuts(SeriesSearchInput);
|
export default keyboardShortcuts(ArtistSearchInput);
|
|
@ -2,15 +2,15 @@ import _ from 'lodash';
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import { push } from 'react-router-redux';
|
import { push } from 'react-router-redux';
|
||||||
import { createSelector } from 'reselect';
|
import { createSelector } from 'reselect';
|
||||||
import createAllSeriesSelector from 'Store/Selectors/createAllSeriesSelector';
|
import createAllArtistSelector from 'Store/Selectors/createAllArtistSelector';
|
||||||
import SeriesSearchInput from './SeriesSearchInput';
|
import ArtistSearchInput from './ArtistSearchInput';
|
||||||
|
|
||||||
function createMapStateToProps() {
|
function createMapStateToProps() {
|
||||||
return createSelector(
|
return createSelector(
|
||||||
createAllSeriesSelector(),
|
createAllArtistSelector(),
|
||||||
(series) => {
|
(series) => {
|
||||||
return {
|
return {
|
||||||
series: _.sortBy(series, 'sortTitle')
|
series: _.sortBy(series, 'sortName')
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
@ -18,8 +18,8 @@ function createMapStateToProps() {
|
||||||
|
|
||||||
function createMapDispatchToProps(dispatch, props) {
|
function createMapDispatchToProps(dispatch, props) {
|
||||||
return {
|
return {
|
||||||
onGoToSeries(titleSlug) {
|
onGoToSeries(nameSlug) {
|
||||||
dispatch(push(`${window.Sonarr.urlBase}/series/${titleSlug}`));
|
dispatch(push(`${window.Sonarr.urlBase}/artist/${nameSlug}`));
|
||||||
},
|
},
|
||||||
|
|
||||||
onGoToAddNewArtist(query) {
|
onGoToAddNewArtist(query) {
|
||||||
|
@ -28,4 +28,4 @@ function createMapDispatchToProps(dispatch, props) {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export default connect(createMapStateToProps, createMapDispatchToProps)(SeriesSearchInput);
|
export default connect(createMapStateToProps, createMapDispatchToProps)(ArtistSearchInput);
|
|
@ -2,7 +2,7 @@ import _ from 'lodash';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import ArtistPoster from 'Artist/ArtistPoster';
|
import ArtistPoster from 'Artist/ArtistPoster';
|
||||||
import styles from './SeriesSearchResult.css';
|
import styles from './ArtistSearchResult.css';
|
||||||
|
|
||||||
function getMatchingAlternateTile(alternateTitles, query) {
|
function getMatchingAlternateTile(alternateTitles, query) {
|
||||||
return _.first(alternateTitles, (alternateTitle) => {
|
return _.first(alternateTitles, (alternateTitle) => {
|
||||||
|
@ -10,18 +10,18 @@ function getMatchingAlternateTile(alternateTitles, query) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function SeriesSearchResult(props) {
|
function ArtistSearchResult(props) {
|
||||||
const {
|
const {
|
||||||
query,
|
query,
|
||||||
title,
|
artistName,
|
||||||
alternateTitles,
|
// alternateTitles,
|
||||||
images
|
images
|
||||||
} = props;
|
} = props;
|
||||||
|
|
||||||
const index = title.toLowerCase().indexOf(query.toLowerCase());
|
const index = artistName.toLowerCase().indexOf(query.toLowerCase());
|
||||||
const alternateTitle = index === -1 ?
|
// const alternateTitle = index === -1 ?
|
||||||
getMatchingAlternateTile(alternateTitles, query) :
|
// getMatchingAlternateTile(alternateTitles, query) :
|
||||||
null;
|
// null;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={styles.result}>
|
<div className={styles.result}>
|
||||||
|
@ -35,25 +35,25 @@ function SeriesSearchResult(props) {
|
||||||
|
|
||||||
<div className={styles.titles}>
|
<div className={styles.titles}>
|
||||||
<div className={styles.title}>
|
<div className={styles.title}>
|
||||||
{title}
|
{artistName}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{
|
{
|
||||||
!!alternateTitle &&
|
// !!alternateTitle &&
|
||||||
<div className={styles.alternateTitle}>
|
// <div className={styles.alternateTitle}>
|
||||||
{alternateTitle.title}
|
// {alternateTitle.title}
|
||||||
</div>
|
// </div>
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
SeriesSearchResult.propTypes = {
|
ArtistSearchResult.propTypes = {
|
||||||
query: PropTypes.string.isRequired,
|
query: PropTypes.string.isRequired,
|
||||||
title: PropTypes.string.isRequired,
|
artistName: PropTypes.string.isRequired,
|
||||||
alternateTitles: PropTypes.arrayOf(PropTypes.object).isRequired,
|
// alternateTitles: PropTypes.arrayOf(PropTypes.object).isRequired,
|
||||||
images: PropTypes.arrayOf(PropTypes.object).isRequired
|
images: PropTypes.arrayOf(PropTypes.object).isRequired
|
||||||
};
|
};
|
||||||
|
|
||||||
export default SeriesSearchResult;
|
export default ArtistSearchResult;
|
|
@ -4,7 +4,7 @@ import { icons } from 'Helpers/Props';
|
||||||
import keyboardShortcuts, { shortcuts } from 'Components/keyboardShortcuts';
|
import keyboardShortcuts, { shortcuts } from 'Components/keyboardShortcuts';
|
||||||
import IconButton from 'Components/Link/IconButton';
|
import IconButton from 'Components/Link/IconButton';
|
||||||
import Link from 'Components/Link/Link';
|
import Link from 'Components/Link/Link';
|
||||||
import SeriesSearchInputConnector from './SeriesSearchInputConnector';
|
import ArtistSearchInputConnector from './ArtistSearchInputConnector';
|
||||||
import PageHeaderActionsMenuConnector from './PageHeaderActionsMenuConnector';
|
import PageHeaderActionsMenuConnector from './PageHeaderActionsMenuConnector';
|
||||||
import KeyboardShortcutsModal from './KeyboardShortcutsModal';
|
import KeyboardShortcutsModal from './KeyboardShortcutsModal';
|
||||||
import styles from './PageHeader.css';
|
import styles from './PageHeader.css';
|
||||||
|
@ -68,7 +68,7 @@ class PageHeader extends Component {
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<SeriesSearchInputConnector />
|
<ArtistSearchInputConnector />
|
||||||
|
|
||||||
<div className={styles.right}>
|
<div className={styles.right}>
|
||||||
<IconButton
|
<IconButton
|
||||||
|
|
|
@ -5,7 +5,7 @@ import { withRouter } from 'react-router-dom';
|
||||||
import { createSelector } from 'reselect';
|
import { createSelector } from 'reselect';
|
||||||
import createDimensionsSelector from 'Store/Selectors/createDimensionsSelector';
|
import createDimensionsSelector from 'Store/Selectors/createDimensionsSelector';
|
||||||
import { saveDimensions, setIsSidebarVisible } from 'Store/Actions/appActions';
|
import { saveDimensions, setIsSidebarVisible } from 'Store/Actions/appActions';
|
||||||
import { fetchArtist } from 'Store/Actions/seriesActions';
|
import { fetchArtist } from 'Store/Actions/artistActions';
|
||||||
import { fetchTags } from 'Store/Actions/tagActions';
|
import { fetchTags } from 'Store/Actions/tagActions';
|
||||||
import { fetchQualityProfiles, fetchLanguageProfiles, fetchUISettings } from 'Store/Actions/settingsActions';
|
import { fetchQualityProfiles, fetchLanguageProfiles, fetchUISettings } from 'Store/Actions/settingsActions';
|
||||||
import { fetchStatus } from 'Store/Actions/systemActions';
|
import { fetchStatus } from 'Store/Actions/systemActions';
|
||||||
|
|
|
@ -17,7 +17,7 @@ function getIconName(name) {
|
||||||
return icons.SEARCH;
|
return icons.SEARCH;
|
||||||
case 'Housekeeping':
|
case 'Housekeeping':
|
||||||
return icons.HOUSEKEEPING;
|
return icons.HOUSEKEEPING;
|
||||||
case 'RefreshSeries':
|
case 'RefreshArtist':
|
||||||
return icons.REFRESH;
|
return icons.REFRESH;
|
||||||
case 'RssSync':
|
case 'RssSync':
|
||||||
return icons.RSS;
|
return icons.RSS;
|
||||||
|
|
|
@ -34,11 +34,11 @@ const links = [
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: 'Mass Editor',
|
title: 'Mass Editor',
|
||||||
to: '/serieseditor'
|
to: '/artisteditor'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: 'Album Studio',
|
title: 'Album Studio',
|
||||||
to: '/seasonpass'
|
to: '/albumstudio'
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
|
|
@ -8,7 +8,7 @@ export const shortcuts = {
|
||||||
name: 'Open This Modal'
|
name: 'Open This Modal'
|
||||||
},
|
},
|
||||||
|
|
||||||
SERIES_SEARCH_INPUT: {
|
ARTIST_SEARCH_INPUT: {
|
||||||
key: 's',
|
key: 's',
|
||||||
name: 'Focus Search Box'
|
name: 'Focus Search Box'
|
||||||
},
|
},
|
||||||
|
|
|
@ -66,7 +66,7 @@ class EpisodeDetailsModalContent extends Component {
|
||||||
onModalClose
|
onModalClose
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
|
||||||
const seriesLink = `/series/${titleSlug}`;
|
const seriesLink = `/artist/${titleSlug}`;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ModalContent
|
<ModalContent
|
||||||
|
|
|
@ -4,12 +4,12 @@ import React, { Component } from 'react';
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import { createSelector } from 'reselect';
|
import { createSelector } from 'reselect';
|
||||||
import { updateInteractiveImportItem } from 'Store/Actions/interactiveImportActions';
|
import { updateInteractiveImportItem } from 'Store/Actions/interactiveImportActions';
|
||||||
import createAllSeriesSelector from 'Store/Selectors/createAllSeriesSelector';
|
import createAllArtistSelector from 'Store/Selectors/createAllArtistSelector';
|
||||||
import SelectArtistModalContent from './SelectArtistModalContent';
|
import SelectArtistModalContent from './SelectArtistModalContent';
|
||||||
|
|
||||||
function createMapStateToProps() {
|
function createMapStateToProps() {
|
||||||
return createSelector(
|
return createSelector(
|
||||||
createAllSeriesSelector(),
|
createAllArtistSelector(),
|
||||||
(items) => {
|
(items) => {
|
||||||
return {
|
return {
|
||||||
items
|
items
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
}
|
}
|
||||||
|
|
||||||
.episodeFormat {
|
.trackFormat {
|
||||||
margin-left: 5px;
|
margin-left: 5px;
|
||||||
font-family: $monoSpaceFontFamily;
|
font-family: $monoSpaceFontFamily;
|
||||||
}
|
}
|
||||||
|
|
|
@ -75,7 +75,7 @@ class OrganizePreviewModalContent extends Component {
|
||||||
error,
|
error,
|
||||||
items,
|
items,
|
||||||
renameTracks,
|
renameTracks,
|
||||||
episodeFormat,
|
trackFormat,
|
||||||
path,
|
path,
|
||||||
onModalClose
|
onModalClose
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
@ -129,8 +129,8 @@ class OrganizePreviewModalContent extends Component {
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
Naming pattern:
|
Naming pattern:
|
||||||
<span className={styles.episodeFormat}>
|
<span className={styles.trackFormat}>
|
||||||
{episodeFormat}
|
{trackFormat}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</Alert>
|
</Alert>
|
||||||
|
@ -140,11 +140,11 @@ class OrganizePreviewModalContent extends Component {
|
||||||
items.map((item) => {
|
items.map((item) => {
|
||||||
return (
|
return (
|
||||||
<OrganizePreviewRow
|
<OrganizePreviewRow
|
||||||
key={item.episodeFileId}
|
key={item.trackFileId}
|
||||||
id={item.episodeFileId}
|
id={item.trackFileId}
|
||||||
existingPath={item.existingPath}
|
existingPath={item.existingPath}
|
||||||
newPath={item.newPath}
|
newPath={item.newPath}
|
||||||
isSelected={selectedState[item.episodeFileId]}
|
isSelected={selectedState[item.trackFileId]}
|
||||||
onSelectedChange={this.onSelectedChange}
|
onSelectedChange={this.onSelectedChange}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
@ -192,7 +192,7 @@ OrganizePreviewModalContent.propTypes = {
|
||||||
items: PropTypes.arrayOf(PropTypes.object).isRequired,
|
items: PropTypes.arrayOf(PropTypes.object).isRequired,
|
||||||
path: PropTypes.string.isRequired,
|
path: PropTypes.string.isRequired,
|
||||||
renameTracks: PropTypes.bool,
|
renameTracks: PropTypes.bool,
|
||||||
episodeFormat: PropTypes.string,
|
trackFormat: PropTypes.string,
|
||||||
onOrganizePress: PropTypes.func.isRequired,
|
onOrganizePress: PropTypes.func.isRequired,
|
||||||
onModalClose: PropTypes.func.isRequired
|
onModalClose: PropTypes.func.isRequired
|
||||||
};
|
};
|
||||||
|
|
|
@ -20,7 +20,7 @@ function createMapStateToProps() {
|
||||||
props.isPopulated = organizePreview.isPopulated && naming.isPopulated;
|
props.isPopulated = organizePreview.isPopulated && naming.isPopulated;
|
||||||
props.error = organizePreview.error || naming.error;
|
props.error = organizePreview.error || naming.error;
|
||||||
props.renameTracks = naming.item.renameTracks;
|
props.renameTracks = naming.item.renameTracks;
|
||||||
props.episodeFormat = naming.item[`${series.seriesType}EpisodeFormat`];
|
props.trackFormat = naming.item['standardTrackFormat'];
|
||||||
props.path = series.path;
|
props.path = series.path;
|
||||||
|
|
||||||
return props;
|
return props;
|
||||||
|
@ -41,13 +41,13 @@ class OrganizePreviewModalContentConnector extends Component {
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
const {
|
const {
|
||||||
seriesId,
|
artistId,
|
||||||
seasonNumber
|
albumId
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
|
||||||
this.props.fetchOrganizePreview({
|
this.props.fetchOrganizePreview({
|
||||||
seriesId,
|
artistId,
|
||||||
seasonNumber
|
albumId
|
||||||
});
|
});
|
||||||
|
|
||||||
this.props.fetchNamingSettings();
|
this.props.fetchNamingSettings();
|
||||||
|
@ -59,7 +59,7 @@ class OrganizePreviewModalContentConnector extends Component {
|
||||||
onOrganizePress = (files) => {
|
onOrganizePress = (files) => {
|
||||||
this.props.executeCommand({
|
this.props.executeCommand({
|
||||||
name: commandNames.RENAME_FILES,
|
name: commandNames.RENAME_FILES,
|
||||||
seriesId: this.props.seriesId,
|
artistId: this.props.artistId,
|
||||||
files
|
files
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -80,8 +80,8 @@ class OrganizePreviewModalContentConnector extends Component {
|
||||||
}
|
}
|
||||||
|
|
||||||
OrganizePreviewModalContentConnector.propTypes = {
|
OrganizePreviewModalContentConnector.propTypes = {
|
||||||
seriesId: PropTypes.number.isRequired,
|
artistId: PropTypes.number.isRequired,
|
||||||
seasonNumber: PropTypes.number,
|
albumId: PropTypes.number,
|
||||||
fetchOrganizePreview: PropTypes.func.isRequired,
|
fetchOrganizePreview: PropTypes.func.isRequired,
|
||||||
fetchNamingSettings: PropTypes.func.isRequired,
|
fetchNamingSettings: PropTypes.func.isRequired,
|
||||||
executeCommand: PropTypes.func.isRequired,
|
executeCommand: PropTypes.func.isRequired,
|
||||||
|
|
|
@ -59,10 +59,10 @@ export const TOGGLE_ARTIST_MONITORED = 'TOGGLE_ARTIST_MONITORED';
|
||||||
export const TOGGLE_ALBUM_MONITORED = 'TOGGLE_ALBUM_MONITORED';
|
export const TOGGLE_ALBUM_MONITORED = 'TOGGLE_ALBUM_MONITORED';
|
||||||
|
|
||||||
//
|
//
|
||||||
// Series Editor
|
// Artist Editor
|
||||||
|
|
||||||
export const SET_SERIES_EDITOR_SORT = 'SET_SERIES_EDITOR_SORT';
|
export const SET_ARTIST_EDITOR_SORT = 'SET_ARTIST_EDITOR_SORT';
|
||||||
export const SET_SERIES_EDITOR_FILTER = 'SET_SERIES_EDITOR_FILTER';
|
export const SET_ARTIST_EDITOR_FILTER = 'SET_ARTIST_EDITOR_FILTER';
|
||||||
export const SAVE_ARTIST_EDITOR = 'SAVE_ARTIST_EDITOR';
|
export const SAVE_ARTIST_EDITOR = 'SAVE_ARTIST_EDITOR';
|
||||||
export const BULK_DELETE_ARTIST = 'BULK_DELETE_ARTIST';
|
export const BULK_DELETE_ARTIST = 'BULK_DELETE_ARTIST';
|
||||||
|
|
||||||
|
|
|
@ -3,11 +3,11 @@ import $ from 'jquery';
|
||||||
import getMonitoringOptions from 'Utilities/Series/getMonitoringOptions';
|
import getMonitoringOptions from 'Utilities/Series/getMonitoringOptions';
|
||||||
import * as types from './actionTypes';
|
import * as types from './actionTypes';
|
||||||
import { set } from './baseActions';
|
import { set } from './baseActions';
|
||||||
import { fetchArtist } from './seriesActions';
|
import { fetchArtist } from './artistActions';
|
||||||
|
|
||||||
const section = 'seasonPass';
|
const section = 'albumStudio';
|
||||||
|
|
||||||
const seasonPassActionHandlers = {
|
const albumStudioActionHandlers = {
|
||||||
[types.SAVE_SEASON_PASS]: function(payload) {
|
[types.SAVE_SEASON_PASS]: function(payload) {
|
||||||
return function(dispatch, getState) {
|
return function(dispatch, getState) {
|
||||||
const {
|
const {
|
||||||
|
@ -50,7 +50,7 @@ const seasonPassActionHandlers = {
|
||||||
}));
|
}));
|
||||||
|
|
||||||
const promise = $.ajax({
|
const promise = $.ajax({
|
||||||
url: '/seasonPass',
|
url: '/albumStudio',
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
data: JSON.stringify({
|
data: JSON.stringify({
|
||||||
series,
|
series,
|
||||||
|
@ -80,4 +80,4 @@ const seasonPassActionHandlers = {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export default seasonPassActionHandlers;
|
export default albumStudioActionHandlers;
|
|
@ -0,0 +1,7 @@
|
||||||
|
import { createAction } from 'redux-actions';
|
||||||
|
import * as types from './actionTypes';
|
||||||
|
import albumStudioActionHandlers from './albumStudioActionHandlers';
|
||||||
|
|
||||||
|
export const setAlbumStudioSort = createAction(types.SET_SEASON_PASS_SORT);
|
||||||
|
export const setAlbumStudioFilter = createAction(types.SET_SEASON_PASS_FILTER);
|
||||||
|
export const saveAlbumStudio = albumStudioActionHandlers[types.SAVE_SEASON_PASS];
|
|
@ -3,9 +3,9 @@ import { batchActions } from 'redux-batched-actions';
|
||||||
import * as types from './actionTypes';
|
import * as types from './actionTypes';
|
||||||
import { set, updateItem } from './baseActions';
|
import { set, updateItem } from './baseActions';
|
||||||
|
|
||||||
const section = 'seriesEditor';
|
const section = 'artistEditor';
|
||||||
|
|
||||||
const seriesEditorActionHandlers = {
|
const artistEditorActionHandlers = {
|
||||||
[types.SAVE_ARTIST_EDITOR]: function(payload) {
|
[types.SAVE_ARTIST_EDITOR]: function(payload) {
|
||||||
return function(dispatch, getState) {
|
return function(dispatch, getState) {
|
||||||
dispatch(set({
|
dispatch(set({
|
||||||
|
@ -14,7 +14,7 @@ const seriesEditorActionHandlers = {
|
||||||
}));
|
}));
|
||||||
|
|
||||||
const promise = $.ajax({
|
const promise = $.ajax({
|
||||||
url: '/series/editor',
|
url: '/artist/editor',
|
||||||
method: 'PUT',
|
method: 'PUT',
|
||||||
data: JSON.stringify(payload),
|
data: JSON.stringify(payload),
|
||||||
dataType: 'json'
|
dataType: 'json'
|
||||||
|
@ -56,7 +56,7 @@ const seriesEditorActionHandlers = {
|
||||||
}));
|
}));
|
||||||
|
|
||||||
const promise = $.ajax({
|
const promise = $.ajax({
|
||||||
url: '/series/editor',
|
url: '/artist/editor',
|
||||||
method: 'DELETE',
|
method: 'DELETE',
|
||||||
data: JSON.stringify(payload),
|
data: JSON.stringify(payload),
|
||||||
dataType: 'json'
|
dataType: 'json'
|
||||||
|
@ -83,4 +83,4 @@ const seriesEditorActionHandlers = {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export default seriesEditorActionHandlers;
|
export default artistEditorActionHandlers;
|
|
@ -0,0 +1,8 @@
|
||||||
|
import { createAction } from 'redux-actions';
|
||||||
|
import * as types from './actionTypes';
|
||||||
|
import artistEditorActionHandlers from './artistEditorActionHandlers';
|
||||||
|
|
||||||
|
export const setArtistEditorSort = createAction(types.SET_ARTIST_EDITOR_SORT);
|
||||||
|
export const setArtistEditorFilter = createAction(types.SET_ARTIST_EDITOR_FILTER);
|
||||||
|
export const saveArtistEditor = artistEditorActionHandlers[types.SAVE_ARTIST_EDITOR];
|
||||||
|
export const bulkDeleteArtist = artistEditorActionHandlers[types.BULK_DELETE_ARTIST];
|
|
@ -116,7 +116,7 @@ const importArtistActionHandlers = {
|
||||||
|
|
||||||
// Make sure we have a selected series and
|
// Make sure we have a selected series and
|
||||||
// the same series hasn't been added yet.
|
// the same series hasn't been added yet.
|
||||||
if (selectedSeries && !_.some(acc, { tvdbId: selectedSeries.tvdbId })) {
|
if (selectedSeries && !_.some(acc, { foreignArtistId: selectedSeries.foreignArtistId })) {
|
||||||
const newSeries = getNewSeries(_.cloneDeep(selectedSeries), item);
|
const newSeries = getNewSeries(_.cloneDeep(selectedSeries), item);
|
||||||
newSeries.path = item.path;
|
newSeries.path = item.path;
|
||||||
|
|
||||||
|
|
|
@ -1,7 +0,0 @@
|
||||||
import { createAction } from 'redux-actions';
|
|
||||||
import * as types from './actionTypes';
|
|
||||||
import seasonPassActionHandlers from './seasonPassActionHandlers';
|
|
||||||
|
|
||||||
export const setSeasonPassSort = createAction(types.SET_SEASON_PASS_SORT);
|
|
||||||
export const setSeasonPassFilter = createAction(types.SET_SEASON_PASS_FILTER);
|
|
||||||
export const saveSeasonPass = seasonPassActionHandlers[types.SAVE_SEASON_PASS];
|
|
|
@ -1,8 +0,0 @@
|
||||||
import { createAction } from 'redux-actions';
|
|
||||||
import * as types from './actionTypes';
|
|
||||||
import seriesEditorActionHandlers from './seriesEditorActionHandlers';
|
|
||||||
|
|
||||||
export const setSeriesEditorSort = createAction(types.SET_SERIES_EDITOR_SORT);
|
|
||||||
export const setSeriesEditorFilter = createAction(types.SET_SERIES_EDITOR_FILTER);
|
|
||||||
export const saveArtistEditor = seriesEditorActionHandlers[types.SAVE_ARTIST_EDITOR];
|
|
||||||
export const bulkDeleteArtist = seriesEditorActionHandlers[types.BULK_DELETE_ARTIST];
|
|
|
@ -3,8 +3,8 @@ import persistState from 'redux-localstorage';
|
||||||
import * as addArtistReducers from 'Store/Reducers/addArtistReducers';
|
import * as addArtistReducers from 'Store/Reducers/addArtistReducers';
|
||||||
import * as episodeReducers from 'Store/Reducers/episodeReducers';
|
import * as episodeReducers from 'Store/Reducers/episodeReducers';
|
||||||
import * as artistIndexReducers from 'Store/Reducers/artistIndexReducers';
|
import * as artistIndexReducers from 'Store/Reducers/artistIndexReducers';
|
||||||
import * as seriesEditorReducers from 'Store/Reducers/seriesEditorReducers';
|
import * as artistEditorReducers from 'Store/Reducers/artistEditorReducers';
|
||||||
import * as seasonPassReducers from 'Store/Reducers/seasonPassReducers';
|
import * as albumStudioReducers from 'Store/Reducers/albumStudioReducers';
|
||||||
import * as calendarReducers from 'Store/Reducers/calendarReducers';
|
import * as calendarReducers from 'Store/Reducers/calendarReducers';
|
||||||
import * as historyReducers from 'Store/Reducers/historyReducers';
|
import * as historyReducers from 'Store/Reducers/historyReducers';
|
||||||
import * as blacklistReducers from 'Store/Reducers/blacklistReducers';
|
import * as blacklistReducers from 'Store/Reducers/blacklistReducers';
|
||||||
|
@ -18,8 +18,8 @@ const reducers = [
|
||||||
addArtistReducers,
|
addArtistReducers,
|
||||||
episodeReducers,
|
episodeReducers,
|
||||||
artistIndexReducers,
|
artistIndexReducers,
|
||||||
seriesEditorReducers,
|
artistEditorReducers,
|
||||||
seasonPassReducers,
|
albumStudioReducers,
|
||||||
calendarReducers,
|
calendarReducers,
|
||||||
historyReducers,
|
historyReducers,
|
||||||
blacklistReducers,
|
blacklistReducers,
|
||||||
|
|
|
@ -18,16 +18,16 @@ export const defaultState = {
|
||||||
};
|
};
|
||||||
|
|
||||||
export const persistState = [
|
export const persistState = [
|
||||||
'seasonPass.sortKey',
|
'albumStudio.sortKey',
|
||||||
'seasonPass.sortDirection',
|
'albumStudio.sortDirection',
|
||||||
'seasonPass.filterKey',
|
'albumStudio.filterKey',
|
||||||
'seasonPass.filterValue',
|
'albumStudio.filterValue',
|
||||||
'seasonPass.filterType'
|
'albumStudio.filterType'
|
||||||
];
|
];
|
||||||
|
|
||||||
const reducerSection = 'seasonPass';
|
const reducerSection = 'albumStudio';
|
||||||
|
|
||||||
const seasonPassReducers = handleActions({
|
const albumStudioReducers = handleActions({
|
||||||
|
|
||||||
[types.SET]: createSetReducer(reducerSection),
|
[types.SET]: createSetReducer(reducerSection),
|
||||||
|
|
||||||
|
@ -36,4 +36,4 @@ const seasonPassReducers = handleActions({
|
||||||
|
|
||||||
}, defaultState);
|
}, defaultState);
|
||||||
|
|
||||||
export default seasonPassReducers;
|
export default albumStudioReducers;
|
|
@ -20,22 +20,22 @@ export const defaultState = {
|
||||||
};
|
};
|
||||||
|
|
||||||
export const persistState = [
|
export const persistState = [
|
||||||
'seriesEditor.sortKey',
|
'artistEditor.sortKey',
|
||||||
'seriesEditor.sortDirection',
|
'artistEditor.sortDirection',
|
||||||
'seriesEditor.filterKey',
|
'artistEditor.filterKey',
|
||||||
'seriesEditor.filterValue',
|
'artistEditor.filterValue',
|
||||||
'seriesEditor.filterType'
|
'artistEditor.filterType'
|
||||||
];
|
];
|
||||||
|
|
||||||
const reducerSection = 'seriesEditor';
|
const reducerSection = 'artistEditor';
|
||||||
|
|
||||||
const seriesEditorReducers = handleActions({
|
const artistEditorReducers = handleActions({
|
||||||
|
|
||||||
[types.SET]: createSetReducer(reducerSection),
|
[types.SET]: createSetReducer(reducerSection),
|
||||||
|
|
||||||
[types.SET_SERIES_EDITOR_SORT]: createSetClientSideCollectionSortReducer(reducerSection),
|
[types.SET_ARTIST_EDITOR_SORT]: createSetClientSideCollectionSortReducer(reducerSection),
|
||||||
[types.SET_SERIES_EDITOR_FILTER]: createSetClientSideCollectionFilterReducer(reducerSection)
|
[types.SET_ARTIST_EDITOR_FILTER]: createSetClientSideCollectionFilterReducer(reducerSection)
|
||||||
|
|
||||||
}, defaultState);
|
}, defaultState);
|
||||||
|
|
||||||
export default seriesEditorReducers;
|
export default artistEditorReducers;
|
|
@ -22,7 +22,7 @@ export const defaultState = {
|
||||||
|
|
||||||
const reducerSection = 'series';
|
const reducerSection = 'series';
|
||||||
|
|
||||||
const seriesReducers = handleActions({
|
const artistReducers = handleActions({
|
||||||
|
|
||||||
[types.SET]: createSetReducer(reducerSection),
|
[types.SET]: createSetReducer(reducerSection),
|
||||||
[types.UPDATE]: createUpdateReducer(reducerSection),
|
[types.UPDATE]: createUpdateReducer(reducerSection),
|
||||||
|
@ -34,4 +34,4 @@ const seriesReducers = handleActions({
|
||||||
|
|
||||||
}, defaultState);
|
}, defaultState);
|
||||||
|
|
||||||
export default seriesReducers;
|
export default artistReducers;
|
|
@ -4,10 +4,10 @@ import { routerReducer } from 'react-router-redux';
|
||||||
import app, { defaultState as defaultappState } from './appReducers';
|
import app, { defaultState as defaultappState } from './appReducers';
|
||||||
import addArtist, { defaultState as defaultAddSeriesState } from './addArtistReducers';
|
import addArtist, { defaultState as defaultAddSeriesState } from './addArtistReducers';
|
||||||
import importArtist, { defaultState as defaultImportArtistState } from './importArtistReducers';
|
import importArtist, { defaultState as defaultImportArtistState } from './importArtistReducers';
|
||||||
import series, { defaultState as defaultSeriesState } from './seriesReducers';
|
import series, { defaultState as defaultSeriesState } from './artistReducers';
|
||||||
import seriesIndex, { defaultState as defaultSeriesIndexState } from './artistIndexReducers';
|
import seriesIndex, { defaultState as defaultSeriesIndexState } from './artistIndexReducers';
|
||||||
import seriesEditor, { defaultState as defaultSeriesEditorState } from './seriesEditorReducers';
|
import artistEditor, { defaultState as defaultArtistEditorState } from './artistEditorReducers';
|
||||||
import seasonPass, { defaultState as defaultSeasonPassState } from './seasonPassReducers';
|
import albumStudio, { defaultState as defaultAlbumStudioState } from './albumStudioReducers';
|
||||||
import calendar, { defaultState as defaultCalendarState } from './calendarReducers';
|
import calendar, { defaultState as defaultCalendarState } from './calendarReducers';
|
||||||
import history, { defaultState as defaultHistoryState } from './historyReducers';
|
import history, { defaultState as defaultHistoryState } from './historyReducers';
|
||||||
import queue, { defaultState as defaultQueueState } from './queueReducers';
|
import queue, { defaultState as defaultQueueState } from './queueReducers';
|
||||||
|
@ -34,8 +34,8 @@ export const defaultState = {
|
||||||
importArtist: defaultImportArtistState,
|
importArtist: defaultImportArtistState,
|
||||||
series: defaultSeriesState,
|
series: defaultSeriesState,
|
||||||
seriesIndex: defaultSeriesIndexState,
|
seriesIndex: defaultSeriesIndexState,
|
||||||
seriesEditor: defaultSeriesEditorState,
|
artistEditor: defaultArtistEditorState,
|
||||||
seasonPass: defaultSeasonPassState,
|
albumStudio: defaultAlbumStudioState,
|
||||||
calendar: defaultCalendarState,
|
calendar: defaultCalendarState,
|
||||||
history: defaultHistoryState,
|
history: defaultHistoryState,
|
||||||
queue: defaultQueueState,
|
queue: defaultQueueState,
|
||||||
|
@ -63,8 +63,8 @@ export default enableBatching(combineReducers({
|
||||||
importArtist,
|
importArtist,
|
||||||
series,
|
series,
|
||||||
seriesIndex,
|
seriesIndex,
|
||||||
seriesEditor,
|
artistEditor,
|
||||||
seasonPass,
|
albumStudio,
|
||||||
calendar,
|
calendar,
|
||||||
history,
|
history,
|
||||||
queue,
|
queue,
|
||||||
|
|
|
@ -14,7 +14,7 @@ export const defaultState = {
|
||||||
isFetching: false,
|
isFetching: false,
|
||||||
isPopulated: false,
|
isPopulated: false,
|
||||||
pageSize: 20,
|
pageSize: 20,
|
||||||
sortKey: 'airDateUtc',
|
sortKey: 'releaseDate',
|
||||||
sortDirection: sortDirections.DESCENDING,
|
sortDirection: sortDirections.DESCENDING,
|
||||||
filterKey: 'monitored',
|
filterKey: 'monitored',
|
||||||
filterValue: 'true',
|
filterValue: 'true',
|
||||||
|
@ -23,32 +23,32 @@ export const defaultState = {
|
||||||
|
|
||||||
columns: [
|
columns: [
|
||||||
{
|
{
|
||||||
name: 'series.sortTitle',
|
name: 'artist.sortName',
|
||||||
label: 'Series Title',
|
label: 'Artist Name',
|
||||||
isSortable: true,
|
isSortable: true,
|
||||||
isVisible: true
|
isVisible: true
|
||||||
},
|
},
|
||||||
|
// {
|
||||||
|
// name: 'episode',
|
||||||
|
// label: 'Episode',
|
||||||
|
// isVisible: true
|
||||||
|
// },
|
||||||
{
|
{
|
||||||
name: 'episode',
|
name: 'albumTitle',
|
||||||
label: 'Episode',
|
label: 'Album Title',
|
||||||
isVisible: true
|
isVisible: true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'episodeTitle',
|
name: 'releaseDate',
|
||||||
label: 'Episode Title',
|
label: 'Release Date',
|
||||||
isVisible: true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'airDateUtc',
|
|
||||||
label: 'Air Date',
|
|
||||||
isSortable: true,
|
isSortable: true,
|
||||||
isVisible: true
|
isVisible: true
|
||||||
},
|
},
|
||||||
{
|
// {
|
||||||
name: 'status',
|
// name: 'status',
|
||||||
label: 'Status',
|
// label: 'Status',
|
||||||
isVisible: true
|
// isVisible: true
|
||||||
},
|
// },
|
||||||
{
|
{
|
||||||
name: 'actions',
|
name: 'actions',
|
||||||
columnLabel: 'Actions',
|
columnLabel: 'Actions',
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import { createSelector } from 'reselect';
|
import { createSelector } from 'reselect';
|
||||||
|
|
||||||
function createAllSeriesSelector() {
|
function createAllArtistSelector() {
|
||||||
return createSelector(
|
return createSelector(
|
||||||
(state) => state.series,
|
(state) => state.series,
|
||||||
(series) => {
|
(series) => {
|
||||||
|
@ -9,4 +9,4 @@ function createAllSeriesSelector() {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default createAllSeriesSelector;
|
export default createAllArtistSelector;
|
|
@ -1,11 +1,11 @@
|
||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
import { createSelector } from 'reselect';
|
import { createSelector } from 'reselect';
|
||||||
import createAllSeriesSelector from './createAllSeriesSelector';
|
import createAllArtistSelector from './createAllArtistSelector';
|
||||||
|
|
||||||
function createArtistSelector() {
|
function createArtistSelector() {
|
||||||
return createSelector(
|
return createSelector(
|
||||||
(state, { artistId }) => artistId,
|
(state, { artistId }) => artistId,
|
||||||
createAllSeriesSelector(),
|
createAllArtistSelector(),
|
||||||
(artistId, series) => {
|
(artistId, series) => {
|
||||||
return _.find(series, { id: artistId });
|
return _.find(series, { id: artistId });
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
import { createSelector } from 'reselect';
|
import { createSelector } from 'reselect';
|
||||||
import createAllSeriesSelector from './createAllSeriesSelector';
|
import createAllArtistSelector from './createAllArtistSelector';
|
||||||
|
|
||||||
function createExistingArtistSelector() {
|
function createExistingArtistSelector() {
|
||||||
return createSelector(
|
return createSelector(
|
||||||
(state, { foreignArtistId }) => foreignArtistId,
|
(state, { foreignArtistId }) => foreignArtistId,
|
||||||
createAllSeriesSelector(),
|
createAllArtistSelector(),
|
||||||
(foreignArtistId, series) => {
|
(foreignArtistId, series) => {
|
||||||
return _.some(series, { foreignArtistId });
|
return _.some(series, { foreignArtistId });
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,17 +1,17 @@
|
||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
import { createSelector } from 'reselect';
|
import { createSelector } from 'reselect';
|
||||||
import createAllSeriesSelector from './createAllSeriesSelector';
|
import createAllArtistSelector from './createAllArtistSelector';
|
||||||
|
|
||||||
function createImportArtistItemSelector() {
|
function createImportArtistItemSelector() {
|
||||||
return createSelector(
|
return createSelector(
|
||||||
(state, { id }) => id,
|
(state, { id }) => id,
|
||||||
(state) => state.addArtist,
|
(state) => state.addArtist,
|
||||||
(state) => state.importArtist,
|
(state) => state.importArtist,
|
||||||
createAllSeriesSelector(),
|
createAllArtistSelector(),
|
||||||
(id, addArtist, importArtist, series) => {
|
(id, addArtist, importArtist, series) => {
|
||||||
const item = _.find(importArtist.items, { id }) || {};
|
const item = _.find(importArtist.items, { id }) || {};
|
||||||
const selectedSeries = item && item.selectedSeries;
|
const selectedSeries = item && item.selectedSeries;
|
||||||
const isExistingArtist = !!selectedSeries && _.some(series, { tvdbId: selectedSeries.tvdbId });
|
const isExistingArtist = !!selectedSeries && _.some(series, { foreignArtistId: selectedSeries.foreignArtistId });
|
||||||
|
|
||||||
return {
|
return {
|
||||||
defaultMonitor: addArtist.defaults.monitor,
|
defaultMonitor: addArtist.defaults.monitor,
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
import { createSelector } from 'reselect';
|
import { createSelector } from 'reselect';
|
||||||
import createAllSeriesSelector from './createAllSeriesSelector';
|
import createAllArtistSelector from './createAllArtistSelector';
|
||||||
|
|
||||||
function createProfileInUseSelector(profileProp) {
|
function createProfileInUseSelector(profileProp) {
|
||||||
return createSelector(
|
return createSelector(
|
||||||
(state, { id }) => id,
|
(state, { id }) => id,
|
||||||
createAllSeriesSelector(),
|
createAllArtistSelector(),
|
||||||
(id, series) => {
|
(id, series) => {
|
||||||
if (!id) {
|
if (!id) {
|
||||||
return false;
|
return false;
|
||||||
|
|
|
@ -32,8 +32,8 @@ function getInternalLink(source) {
|
||||||
case 'RootFolderCheck':
|
case 'RootFolderCheck':
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<Link to="/serieseditor">
|
<Link to="/artisteditor">
|
||||||
Series Editor
|
Artist Editor
|
||||||
</Link>
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
|
@ -108,7 +108,7 @@ class CutoffUnmet extends Component {
|
||||||
items,
|
items,
|
||||||
columns,
|
columns,
|
||||||
totalRecords,
|
totalRecords,
|
||||||
isSearchingForEpisodes,
|
isSearchingForAlbums,
|
||||||
isSearchingForCutoffUnmetEpisodes,
|
isSearchingForCutoffUnmetEpisodes,
|
||||||
isSaving,
|
isSaving,
|
||||||
filterKey,
|
filterKey,
|
||||||
|
@ -133,7 +133,7 @@ class CutoffUnmet extends Component {
|
||||||
label="Search Selected"
|
label="Search Selected"
|
||||||
iconName={icons.SEARCH}
|
iconName={icons.SEARCH}
|
||||||
isDisabled={!itemsSelected}
|
isDisabled={!itemsSelected}
|
||||||
isSpinning={isSearchingForEpisodes}
|
isSpinning={isSearchingForAlbums}
|
||||||
onPress={this.onSearchSelectedPress}
|
onPress={this.onSearchSelectedPress}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
@ -271,7 +271,7 @@ CutoffUnmet.propTypes = {
|
||||||
items: PropTypes.arrayOf(PropTypes.object).isRequired,
|
items: PropTypes.arrayOf(PropTypes.object).isRequired,
|
||||||
columns: PropTypes.arrayOf(PropTypes.object).isRequired,
|
columns: PropTypes.arrayOf(PropTypes.object).isRequired,
|
||||||
totalRecords: PropTypes.number,
|
totalRecords: PropTypes.number,
|
||||||
isSearchingForEpisodes: PropTypes.bool.isRequired,
|
isSearchingForAlbums: PropTypes.bool.isRequired,
|
||||||
isSearchingForCutoffUnmetEpisodes: PropTypes.bool.isRequired,
|
isSearchingForCutoffUnmetEpisodes: PropTypes.bool.isRequired,
|
||||||
isSaving: PropTypes.bool.isRequired,
|
isSaving: PropTypes.bool.isRequired,
|
||||||
filterKey: PropTypes.string,
|
filterKey: PropTypes.string,
|
||||||
|
|
|
@ -18,11 +18,11 @@ function createMapStateToProps() {
|
||||||
(state) => state.wanted.cutoffUnmet,
|
(state) => state.wanted.cutoffUnmet,
|
||||||
createCommandsSelector(),
|
createCommandsSelector(),
|
||||||
(cutoffUnmet, commands) => {
|
(cutoffUnmet, commands) => {
|
||||||
const isSearchingForEpisodes = _.some(commands, { name: commandNames.EPISODE_SEARCH });
|
const isSearchingForAlbums = _.some(commands, { name: commandNames.EPISODE_SEARCH });
|
||||||
const isSearchingForCutoffUnmetEpisodes = _.some(commands, { name: commandNames.CUTOFF_UNMET_EPISODE_SEARCH });
|
const isSearchingForCutoffUnmetEpisodes = _.some(commands, { name: commandNames.CUTOFF_UNMET_EPISODE_SEARCH });
|
||||||
|
|
||||||
return {
|
return {
|
||||||
isSearchingForEpisodes,
|
isSearchingForAlbums,
|
||||||
isSearchingForCutoffUnmetEpisodes,
|
isSearchingForCutoffUnmetEpisodes,
|
||||||
isSaving: _.some(cutoffUnmet.items, { isSaving: true }),
|
isSaving: _.some(cutoffUnmet.items, { isSaving: true }),
|
||||||
...cutoffUnmet
|
...cutoffUnmet
|
||||||
|
|
|
@ -117,8 +117,8 @@ class Missing extends Component {
|
||||||
items,
|
items,
|
||||||
columns,
|
columns,
|
||||||
totalRecords,
|
totalRecords,
|
||||||
isSearchingForEpisodes,
|
isSearchingForAlbums,
|
||||||
isSearchingForMissingEpisodes,
|
isSearchingForMissingAlbums,
|
||||||
isSaving,
|
isSaving,
|
||||||
filterKey,
|
filterKey,
|
||||||
filterValue,
|
filterValue,
|
||||||
|
@ -143,7 +143,7 @@ class Missing extends Component {
|
||||||
label="Search Selected"
|
label="Search Selected"
|
||||||
iconName={icons.SEARCH}
|
iconName={icons.SEARCH}
|
||||||
isDisabled={!itemsSelected}
|
isDisabled={!itemsSelected}
|
||||||
isSpinning={isSearchingForEpisodes}
|
isSpinning={isSearchingForAlbums}
|
||||||
onPress={this.onSearchSelectedPress}
|
onPress={this.onSearchSelectedPress}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
@ -160,7 +160,7 @@ class Missing extends Component {
|
||||||
<PageToolbarButton
|
<PageToolbarButton
|
||||||
label="Search All"
|
label="Search All"
|
||||||
iconName={icons.SEARCH}
|
iconName={icons.SEARCH}
|
||||||
isSpinning={isSearchingForMissingEpisodes}
|
isSpinning={isSearchingForMissingAlbums}
|
||||||
onPress={this.onSearchAllMissingPress}
|
onPress={this.onSearchAllMissingPress}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
@ -258,11 +258,11 @@ class Missing extends Component {
|
||||||
<ConfirmModal
|
<ConfirmModal
|
||||||
isOpen={isConfirmSearchAllMissingModalOpen}
|
isOpen={isConfirmSearchAllMissingModalOpen}
|
||||||
kind={kinds.DANGER}
|
kind={kinds.DANGER}
|
||||||
title="Search for all missing episodes"
|
title="Search for all missing albums"
|
||||||
message={
|
message={
|
||||||
<div>
|
<div>
|
||||||
<div>
|
<div>
|
||||||
Are you sure you want to search for all {totalRecords} missing episodes?
|
Are you sure you want to search for all {totalRecords} missing albums?
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
This cannot be cancelled once started without restarting Lidarr.
|
This cannot be cancelled once started without restarting Lidarr.
|
||||||
|
@ -293,8 +293,8 @@ Missing.propTypes = {
|
||||||
items: PropTypes.arrayOf(PropTypes.object).isRequired,
|
items: PropTypes.arrayOf(PropTypes.object).isRequired,
|
||||||
columns: PropTypes.arrayOf(PropTypes.object).isRequired,
|
columns: PropTypes.arrayOf(PropTypes.object).isRequired,
|
||||||
totalRecords: PropTypes.number,
|
totalRecords: PropTypes.number,
|
||||||
isSearchingForEpisodes: PropTypes.bool.isRequired,
|
isSearchingForAlbums: PropTypes.bool.isRequired,
|
||||||
isSearchingForMissingEpisodes: PropTypes.bool.isRequired,
|
isSearchingForMissingAlbums: PropTypes.bool.isRequired,
|
||||||
isSaving: PropTypes.bool.isRequired,
|
isSaving: PropTypes.bool.isRequired,
|
||||||
filterKey: PropTypes.string,
|
filterKey: PropTypes.string,
|
||||||
filterValue: PropTypes.oneOfType([PropTypes.bool, PropTypes.number, PropTypes.string]),
|
filterValue: PropTypes.oneOfType([PropTypes.bool, PropTypes.number, PropTypes.string]),
|
||||||
|
|
|
@ -17,12 +17,12 @@ function createMapStateToProps() {
|
||||||
(state) => state.wanted.missing,
|
(state) => state.wanted.missing,
|
||||||
createCommandsSelector(),
|
createCommandsSelector(),
|
||||||
(missing, commands) => {
|
(missing, commands) => {
|
||||||
const isSearchingForEpisodes = _.some(commands, { name: commandNames.EPISODE_SEARCH });
|
const isSearchingForAlbums = _.some(commands, { name: commandNames.EPISODE_SEARCH });
|
||||||
const isSearchingForMissingEpisodes = _.some(commands, { name: commandNames.MISSING_EPISODE_SEARCH });
|
const isSearchingForMissingAlbums = _.some(commands, { name: commandNames.MISSING_EPISODE_SEARCH });
|
||||||
|
|
||||||
return {
|
return {
|
||||||
isSearchingForEpisodes,
|
isSearchingForAlbums,
|
||||||
isSearchingForMissingEpisodes,
|
isSearchingForMissingAlbums,
|
||||||
isSaving: _.some(missing.items, { isSaving: true }),
|
isSaving: _.some(missing.items, { isSaving: true }),
|
||||||
...missing
|
...missing
|
||||||
};
|
};
|
||||||
|
|
|
@ -15,15 +15,15 @@ import styles from './MissingRow.css';
|
||||||
function MissingRow(props) {
|
function MissingRow(props) {
|
||||||
const {
|
const {
|
||||||
id,
|
id,
|
||||||
episodeFileId,
|
// episodeFileId,
|
||||||
series,
|
artist,
|
||||||
seasonNumber,
|
// seasonNumber,
|
||||||
episodeNumber,
|
// episodeNumber,
|
||||||
absoluteEpisodeNumber,
|
// absoluteEpisodeNumber,
|
||||||
sceneSeasonNumber,
|
// sceneSeasonNumber,
|
||||||
sceneEpisodeNumber,
|
// sceneEpisodeNumber,
|
||||||
sceneAbsoluteEpisodeNumber,
|
// sceneAbsoluteEpisodeNumber,
|
||||||
airDateUtc,
|
releaseDate,
|
||||||
title,
|
title,
|
||||||
isSelected,
|
isSelected,
|
||||||
columns,
|
columns,
|
||||||
|
@ -49,42 +49,42 @@ function MissingRow(props) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (name === 'series.sortTitle') {
|
if (name === 'artist.sortName') {
|
||||||
return (
|
return (
|
||||||
<TableRowCell key={name}>
|
<TableRowCell key={name}>
|
||||||
<ArtistNameLink
|
<ArtistNameLink
|
||||||
titleSlug={series.titleSlug}
|
nameSlug={artist.nameSlug}
|
||||||
title={series.title}
|
artistName={artist.artistName}
|
||||||
/>
|
/>
|
||||||
</TableRowCell>
|
</TableRowCell>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (name === 'episode') {
|
// if (name === 'episode') {
|
||||||
return (
|
// return (
|
||||||
<TableRowCell
|
// <TableRowCell
|
||||||
key={name}
|
// key={name}
|
||||||
className={styles.episode}
|
// className={styles.episode}
|
||||||
>
|
// >
|
||||||
<SeasonEpisodeNumber
|
// <SeasonEpisodeNumber
|
||||||
seasonNumber={seasonNumber}
|
// seasonNumber={seasonNumber}
|
||||||
episodeNumber={episodeNumber}
|
// episodeNumber={episodeNumber}
|
||||||
absoluteEpisodeNumber={absoluteEpisodeNumber}
|
// absoluteEpisodeNumber={absoluteEpisodeNumber}
|
||||||
seriesType={series.seriesType}
|
// seriesType={series.seriesType}
|
||||||
sceneSeasonNumber={sceneSeasonNumber}
|
// sceneSeasonNumber={sceneSeasonNumber}
|
||||||
sceneEpisodeNumber={sceneEpisodeNumber}
|
// sceneEpisodeNumber={sceneEpisodeNumber}
|
||||||
sceneAbsoluteEpisodeNumber={sceneAbsoluteEpisodeNumber}
|
// sceneAbsoluteEpisodeNumber={sceneAbsoluteEpisodeNumber}
|
||||||
/>
|
// />
|
||||||
</TableRowCell>
|
// </TableRowCell>
|
||||||
);
|
// );
|
||||||
}
|
// }
|
||||||
|
|
||||||
if (name === 'episodeTitle') {
|
if (name === 'albumTitle') {
|
||||||
return (
|
return (
|
||||||
<TableRowCell key={name}>
|
<TableRowCell key={name}>
|
||||||
<EpisodeTitleLink
|
<EpisodeTitleLink
|
||||||
episodeId={id}
|
episodeId={id}
|
||||||
artistId={series.id}
|
artistId={artist.id}
|
||||||
episodeEntity={episodeEntities.WANTED_MISSING}
|
episodeEntity={episodeEntities.WANTED_MISSING}
|
||||||
episodeTitle={title}
|
episodeTitle={title}
|
||||||
showOpenSeriesButton={true}
|
showOpenSeriesButton={true}
|
||||||
|
@ -93,36 +93,36 @@ function MissingRow(props) {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (name === 'airDateUtc') {
|
if (name === 'releaseDate') {
|
||||||
return (
|
return (
|
||||||
<RelativeDateCellConnector
|
<RelativeDateCellConnector
|
||||||
key={name}
|
key={name}
|
||||||
date={airDateUtc}
|
date={releaseDate}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (name === 'status') {
|
// if (name === 'status') {
|
||||||
return (
|
// return (
|
||||||
<TableRowCell
|
// <TableRowCell
|
||||||
key={name}
|
// key={name}
|
||||||
className={styles.status}
|
// className={styles.status}
|
||||||
>
|
// >
|
||||||
<EpisodeStatusConnector
|
// <EpisodeStatusConnector
|
||||||
episodeId={id}
|
// episodeId={id}
|
||||||
episodeFileId={episodeFileId}
|
// episodeFileId={episodeFileId}
|
||||||
episodeEntity={episodeEntities.WANTED_MISSING}
|
// episodeEntity={episodeEntities.WANTED_MISSING}
|
||||||
/>
|
// />
|
||||||
</TableRowCell>
|
// </TableRowCell>
|
||||||
);
|
// );
|
||||||
}
|
// }
|
||||||
|
|
||||||
if (name === 'actions') {
|
if (name === 'actions') {
|
||||||
return (
|
return (
|
||||||
<EpisodeSearchCellConnector
|
<EpisodeSearchCellConnector
|
||||||
key={name}
|
key={name}
|
||||||
episodeId={id}
|
episodeId={id}
|
||||||
artistId={series.id}
|
artistId={artist.id}
|
||||||
episodeTitle={title}
|
episodeTitle={title}
|
||||||
episodeEntity={episodeEntities.WANTED_MISSING}
|
episodeEntity={episodeEntities.WANTED_MISSING}
|
||||||
showOpenSeriesButton={true}
|
showOpenSeriesButton={true}
|
||||||
|
@ -137,15 +137,15 @@ function MissingRow(props) {
|
||||||
|
|
||||||
MissingRow.propTypes = {
|
MissingRow.propTypes = {
|
||||||
id: PropTypes.number.isRequired,
|
id: PropTypes.number.isRequired,
|
||||||
episodeFileId: PropTypes.number,
|
// episodeFileId: PropTypes.number,
|
||||||
series: PropTypes.object.isRequired,
|
artist: PropTypes.object.isRequired,
|
||||||
seasonNumber: PropTypes.number.isRequired,
|
// seasonNumber: PropTypes.number.isRequired,
|
||||||
episodeNumber: PropTypes.number.isRequired,
|
// episodeNumber: PropTypes.number.isRequired,
|
||||||
absoluteEpisodeNumber: PropTypes.number,
|
// absoluteEpisodeNumber: PropTypes.number,
|
||||||
sceneSeasonNumber: PropTypes.number,
|
// sceneSeasonNumber: PropTypes.number,
|
||||||
sceneEpisodeNumber: PropTypes.number,
|
// sceneEpisodeNumber: PropTypes.number,
|
||||||
sceneAbsoluteEpisodeNumber: PropTypes.number,
|
// sceneAbsoluteEpisodeNumber: PropTypes.number,
|
||||||
airDateUtc: PropTypes.string.isRequired,
|
releaseDate: PropTypes.string.isRequired,
|
||||||
title: PropTypes.string.isRequired,
|
title: PropTypes.string.isRequired,
|
||||||
isSelected: PropTypes.bool,
|
isSelected: PropTypes.bool,
|
||||||
columns: PropTypes.arrayOf(PropTypes.object).isRequired,
|
columns: PropTypes.arrayOf(PropTypes.object).isRequired,
|
||||||
|
|
|
@ -14,6 +14,7 @@ using NzbDrone.Core.Music;
|
||||||
using NzbDrone.Core.Music.Events;
|
using NzbDrone.Core.Music.Events;
|
||||||
using NzbDrone.Core.Validation;
|
using NzbDrone.Core.Validation;
|
||||||
using NzbDrone.Core.Validation.Paths;
|
using NzbDrone.Core.Validation.Paths;
|
||||||
|
using Lidarr.Api.V3.Albums;
|
||||||
using NzbDrone.SignalR;
|
using NzbDrone.SignalR;
|
||||||
using Lidarr.Http;
|
using Lidarr.Http;
|
||||||
using Lidarr.Http.Extensions;
|
using Lidarr.Http.Extensions;
|
||||||
|
@ -35,12 +36,14 @@ namespace Lidarr.Api.V3.Artist
|
||||||
private readonly IAddArtistService _addArtistService;
|
private readonly IAddArtistService _addArtistService;
|
||||||
private readonly IArtistStatisticsService _artistStatisticsService;
|
private readonly IArtistStatisticsService _artistStatisticsService;
|
||||||
private readonly IMapCoversToLocal _coverMapper;
|
private readonly IMapCoversToLocal _coverMapper;
|
||||||
|
private readonly IAlbumService _albumService;
|
||||||
|
|
||||||
public ArtistModule(IBroadcastSignalRMessage signalRBroadcaster,
|
public ArtistModule(IBroadcastSignalRMessage signalRBroadcaster,
|
||||||
IArtistService artistService,
|
IArtistService artistService,
|
||||||
IAddArtistService addArtistService,
|
IAddArtistService addArtistService,
|
||||||
IArtistStatisticsService artistStatisticsService,
|
IArtistStatisticsService artistStatisticsService,
|
||||||
IMapCoversToLocal coverMapper,
|
IMapCoversToLocal coverMapper,
|
||||||
|
IAlbumService albumService,
|
||||||
RootFolderValidator rootFolderValidator,
|
RootFolderValidator rootFolderValidator,
|
||||||
ArtistPathValidator artistPathValidator,
|
ArtistPathValidator artistPathValidator,
|
||||||
ArtistExistsValidator artistExistsValidator,
|
ArtistExistsValidator artistExistsValidator,
|
||||||
|
@ -55,6 +58,7 @@ namespace Lidarr.Api.V3.Artist
|
||||||
_artistStatisticsService = artistStatisticsService;
|
_artistStatisticsService = artistStatisticsService;
|
||||||
|
|
||||||
_coverMapper = coverMapper;
|
_coverMapper = coverMapper;
|
||||||
|
_albumService = albumService;
|
||||||
|
|
||||||
GetResourceAll = AllArtists;
|
GetResourceAll = AllArtists;
|
||||||
GetResourceById = GetArtist;
|
GetResourceById = GetArtist;
|
||||||
|
@ -95,6 +99,7 @@ namespace Lidarr.Api.V3.Artist
|
||||||
|
|
||||||
var resource = artist.ToResource();
|
var resource = artist.ToResource();
|
||||||
MapCoversToLocal(resource);
|
MapCoversToLocal(resource);
|
||||||
|
MapAlbums(resource);
|
||||||
FetchAndLinkArtistStatistics(resource);
|
FetchAndLinkArtistStatistics(resource);
|
||||||
//PopulateAlternateTitles(resource);
|
//PopulateAlternateTitles(resource);
|
||||||
|
|
||||||
|
@ -107,6 +112,7 @@ namespace Lidarr.Api.V3.Artist
|
||||||
var artistsResources = _artistService.GetAllArtists().ToResource();
|
var artistsResources = _artistService.GetAllArtists().ToResource();
|
||||||
|
|
||||||
MapCoversToLocal(artistsResources.ToArray());
|
MapCoversToLocal(artistsResources.ToArray());
|
||||||
|
MapAlbums(artistsResources.ToArray());
|
||||||
LinkArtistStatistics(artistsResources, artistStats);
|
LinkArtistStatistics(artistsResources, artistStats);
|
||||||
//PopulateAlternateTitles(seriesResources);
|
//PopulateAlternateTitles(seriesResources);
|
||||||
|
|
||||||
|
@ -144,6 +150,14 @@ namespace Lidarr.Api.V3.Artist
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void MapAlbums(params ArtistResource[] artists)
|
||||||
|
{
|
||||||
|
foreach (var artistResource in artists)
|
||||||
|
{
|
||||||
|
artistResource.Albums = _albumService.GetAlbumsByArtist(artistResource.Id).ToResource();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void FetchAndLinkArtistStatistics(ArtistResource resource)
|
private void FetchAndLinkArtistStatistics(ArtistResource resource)
|
||||||
{
|
{
|
||||||
LinkArtistStatistics(resource, _artistStatisticsService.ArtistStatistics(resource.Id));
|
LinkArtistStatistics(resource, _artistStatisticsService.ArtistStatistics(resource.Id));
|
||||||
|
@ -168,13 +182,13 @@ namespace Lidarr.Api.V3.Artist
|
||||||
resource.SizeOnDisk = artistStatistics.SizeOnDisk;
|
resource.SizeOnDisk = artistStatistics.SizeOnDisk;
|
||||||
resource.AlbumCount = artistStatistics.AlbumCount;
|
resource.AlbumCount = artistStatistics.AlbumCount;
|
||||||
|
|
||||||
//if (seriesStatistics.AlbumStatistics != null)
|
if (artistStatistics.AlbumStatistics != null)
|
||||||
//{
|
{
|
||||||
// foreach (var album in resource.Albums)
|
foreach (var album in resource.Albums)
|
||||||
// {
|
{
|
||||||
// album.Statistics = seriesStatistics.AlbumStatistics.SingleOrDefault(s => s.AlbumId == album.Id).ToResource();
|
album.Statistics = artistStatistics.AlbumStatistics.SingleOrDefault(s => s.AlbumId == album.Id).ToResource();
|
||||||
// }
|
}
|
||||||
//}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//private void PopulateAlternateTitles(List<ArtistResource> resources)
|
//private void PopulateAlternateTitles(List<ArtistResource> resources)
|
||||||
|
|
|
@ -3,6 +3,7 @@ using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using NzbDrone.Core.MediaCover;
|
using NzbDrone.Core.MediaCover;
|
||||||
using NzbDrone.Core.Music;
|
using NzbDrone.Core.Music;
|
||||||
|
using Lidarr.Api.V3.Albums;
|
||||||
using Lidarr.Http.REST;
|
using Lidarr.Http.REST;
|
||||||
|
|
||||||
namespace Lidarr.Api.V3.Artist
|
namespace Lidarr.Api.V3.Artist
|
||||||
|
@ -97,7 +98,7 @@ namespace Lidarr.Api.V3.Artist
|
||||||
public List<Member> Members { get; set; }
|
public List<Member> Members { get; set; }
|
||||||
|
|
||||||
public string RemotePoster { get; set; }
|
public string RemotePoster { get; set; }
|
||||||
//public List<AlbumResource> Albums { get; set; }
|
public List<AlbumResource> Albums { get; set; }
|
||||||
|
|
||||||
|
|
||||||
//View & Edit
|
//View & Edit
|
||||||
|
@ -149,7 +150,7 @@ namespace Lidarr.Api.V3.Artist
|
||||||
//AirTime = model.AirTime,
|
//AirTime = model.AirTime,
|
||||||
Images = model.Images,
|
Images = model.Images,
|
||||||
|
|
||||||
//Albums = model.Albums.ToResource(),
|
Albums = model.Albums.ToResource(),
|
||||||
//Year = model.Year,
|
//Year = model.Year,
|
||||||
|
|
||||||
Path = model.Path,
|
Path = model.Path,
|
||||||
|
|
|
@ -21,9 +21,9 @@ namespace Lidarr.Api.V3.Tracks
|
||||||
{
|
{
|
||||||
int artistId;
|
int artistId;
|
||||||
|
|
||||||
if (Request.Query.SeriesId.HasValue)
|
if (Request.Query.ArtistId.HasValue)
|
||||||
{
|
{
|
||||||
artistId = (int)Request.Query.SeriesId;
|
artistId = (int)Request.Query.ArtistId;
|
||||||
}
|
}
|
||||||
|
|
||||||
else
|
else
|
||||||
|
|
|
@ -60,10 +60,10 @@ namespace NzbDrone.Core.Organizer
|
||||||
public static readonly Regex SeriesTitleRegex = new Regex(@"(?<token>\{(?:Series)(?<separator>[- ._])(Clean)?Title\})",
|
public static readonly Regex SeriesTitleRegex = new Regex(@"(?<token>\{(?:Series)(?<separator>[- ._])(Clean)?Title\})",
|
||||||
RegexOptions.Compiled | RegexOptions.IgnoreCase);
|
RegexOptions.Compiled | RegexOptions.IgnoreCase);
|
||||||
|
|
||||||
public static readonly Regex ArtistNameRegex = new Regex(@"(?<token>\{(?:Artist)(?<separator>[- ._])(Clean)?Name(The)\})",
|
public static readonly Regex ArtistNameRegex = new Regex(@"(?<token>\{(?:Artist)(?<separator>[- ._])(Clean)?Name(The)?\})",
|
||||||
RegexOptions.Compiled | RegexOptions.IgnoreCase);
|
RegexOptions.Compiled | RegexOptions.IgnoreCase);
|
||||||
|
|
||||||
public static readonly Regex AlbumTitleRegex = new Regex(@"(?<token>\{(?:Album)(?<separator>[- ._])(Clean)?Title(The)\})",
|
public static readonly Regex AlbumTitleRegex = new Regex(@"(?<token>\{(?:Album)(?<separator>[- ._])(Clean)?Title(The)?\})",
|
||||||
RegexOptions.Compiled | RegexOptions.IgnoreCase);
|
RegexOptions.Compiled | RegexOptions.IgnoreCase);
|
||||||
|
|
||||||
private static readonly Regex FileNameCleanupRegex = new Regex(@"([- ._])(\1)+", RegexOptions.Compiled);
|
private static readonly Regex FileNameCleanupRegex = new Regex(@"([- ._])(\1)+", RegexOptions.Compiled);
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
using System.Text.RegularExpressions;
|
using System.Text.RegularExpressions;
|
||||||
using FluentValidation;
|
using FluentValidation;
|
||||||
using FluentValidation.Validators;
|
using FluentValidation.Validators;
|
||||||
|
|
||||||
|
@ -56,7 +56,7 @@ namespace NzbDrone.Core.Organizer
|
||||||
public static IRuleBuilderOptions<T, string> ValidAlbumFolderFormat<T>(this IRuleBuilder<T, string> ruleBuilder)
|
public static IRuleBuilderOptions<T, string> ValidAlbumFolderFormat<T>(this IRuleBuilder<T, string> ruleBuilder)
|
||||||
{
|
{
|
||||||
ruleBuilder.SetValidator(new NotEmptyValidator(null));
|
ruleBuilder.SetValidator(new NotEmptyValidator(null));
|
||||||
return ruleBuilder.SetValidator(new RegularExpressionValidator(FileNameBuilder.AlbumTitleRegex)).WithMessage("Must contain Album name");
|
return ruleBuilder.SetValidator(new RegularExpressionValidator(FileNameBuilder.AlbumTitleRegex)).WithMessage("Must contain Album title");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue