mirror of
https://github.com/lidarr/lidarr.git
synced 2025-07-16 10:03:51 -07:00
[UI Work] Interactive Import, More Artist Detail
This commit is contained in:
parent
0054226307
commit
f05332cf6e
42 changed files with 486 additions and 549 deletions
|
@ -71,7 +71,7 @@ class ImportArtistRowConnector extends Component {
|
||||||
<ImportArtistRow
|
<ImportArtistRow
|
||||||
{...this.props}
|
{...this.props}
|
||||||
onInputChange={this.onInputChange}
|
onInputChange={this.onInputChange}
|
||||||
onSeriesSelect={this.onSeriesSelect}
|
onArtistSelect={this.onArtistSelect}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -99,10 +99,10 @@ class ImportArtistSelectArtist extends Component {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
onSeriesSelect = (foreignArtistId) => {
|
onArtistSelect = (foreignArtistId) => {
|
||||||
this.setState({ isOpen: false });
|
this.setState({ isOpen: false });
|
||||||
|
|
||||||
this.props.onSeriesSelect(foreignArtistId);
|
this.props.onArtistSelect(foreignArtistId);
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
|
@ -117,7 +117,7 @@ class ImportArtistSelectArtist extends Component {
|
||||||
error,
|
error,
|
||||||
items,
|
items,
|
||||||
queued,
|
queued,
|
||||||
onSeriesSelect
|
onArtistSelect
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
|
||||||
const errorMessage = error &&
|
const errorMessage = error &&
|
||||||
|
@ -233,7 +233,7 @@ class ImportArtistSelectArtist extends Component {
|
||||||
overview={item.overview}
|
overview={item.overview}
|
||||||
// year={item.year}
|
// year={item.year}
|
||||||
// network={item.network}
|
// network={item.network}
|
||||||
onPress={this.onSeriesSelect}
|
onPress={this.onArtistSelect}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
})
|
})
|
||||||
|
@ -257,7 +257,7 @@ ImportArtistSelectArtist.propTypes = {
|
||||||
items: PropTypes.arrayOf(PropTypes.object).isRequired,
|
items: PropTypes.arrayOf(PropTypes.object).isRequired,
|
||||||
queued: PropTypes.bool.isRequired,
|
queued: PropTypes.bool.isRequired,
|
||||||
onSearchInputChange: PropTypes.func.isRequired,
|
onSearchInputChange: PropTypes.func.isRequired,
|
||||||
onSeriesSelect: PropTypes.func.isRequired
|
onArtistSelect: PropTypes.func.isRequired
|
||||||
};
|
};
|
||||||
|
|
||||||
ImportArtistSelectArtist.defaultProps = {
|
ImportArtistSelectArtist.defaultProps = {
|
||||||
|
|
|
@ -33,7 +33,7 @@ class ImportArtistSelectArtistConnector extends Component {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
onSeriesSelect = (foreignArtistId) => {
|
onArtistSelect = (foreignArtistId) => {
|
||||||
const {
|
const {
|
||||||
id,
|
id,
|
||||||
items
|
items
|
||||||
|
@ -53,7 +53,7 @@ class ImportArtistSelectArtistConnector extends Component {
|
||||||
<ImportArtistSelectArtist
|
<ImportArtistSelectArtist
|
||||||
{...this.props}
|
{...this.props}
|
||||||
onSearchInputChange={this.onSearchInputChange}
|
onSearchInputChange={this.onSearchInputChange}
|
||||||
onSeriesSelect={this.onSeriesSelect}
|
onArtistSelect={this.onArtistSelect}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -45,7 +45,7 @@ function App({ store, history }) {
|
||||||
<PageConnector>
|
<PageConnector>
|
||||||
<Switch>
|
<Switch>
|
||||||
{/*
|
{/*
|
||||||
Series
|
Artist
|
||||||
*/}
|
*/}
|
||||||
|
|
||||||
<Route
|
<Route
|
||||||
|
|
|
@ -112,7 +112,7 @@ class SeriesDetails extends Component {
|
||||||
this.setState(getExpandedState(selectAll(expandedState, !allExpanded)));
|
this.setState(getExpandedState(selectAll(expandedState, !allExpanded)));
|
||||||
}
|
}
|
||||||
|
|
||||||
onExpandPress = (seasonNumber, isExpanded) => {
|
onExpandPress = (albumId, isExpanded) => {
|
||||||
this.setState((state) => {
|
this.setState((state) => {
|
||||||
const convertedState = {
|
const convertedState = {
|
||||||
allSelected: state.allExpanded,
|
allSelected: state.allExpanded,
|
||||||
|
@ -120,7 +120,7 @@ class SeriesDetails extends Component {
|
||||||
selectedState: state.expandedState
|
selectedState: state.expandedState
|
||||||
};
|
};
|
||||||
|
|
||||||
const newState = toggleSelected(convertedState, [], seasonNumber, isExpanded, false);
|
const newState = toggleSelected(convertedState, [], albumId, isExpanded, false);
|
||||||
|
|
||||||
return getExpandedState(newState);
|
return getExpandedState(newState);
|
||||||
});
|
});
|
||||||
|
@ -136,10 +136,9 @@ class SeriesDetails extends Component {
|
||||||
tvMazeId,
|
tvMazeId,
|
||||||
imdbId,
|
imdbId,
|
||||||
artistName,
|
artistName,
|
||||||
runtime,
|
|
||||||
ratings,
|
ratings,
|
||||||
sizeOnDisk,
|
sizeOnDisk,
|
||||||
episodeFileCount,
|
trackFileCount,
|
||||||
qualityProfileId,
|
qualityProfileId,
|
||||||
monitored,
|
monitored,
|
||||||
status,
|
status,
|
||||||
|
@ -175,10 +174,10 @@ class SeriesDetails extends Component {
|
||||||
|
|
||||||
let episodeFilesCountMessage = 'No episode files';
|
let episodeFilesCountMessage = 'No episode files';
|
||||||
|
|
||||||
if (episodeFileCount === 1) {
|
if (trackFileCount === 1) {
|
||||||
episodeFilesCountMessage = '1 episode file';
|
episodeFilesCountMessage = '1 episode file';
|
||||||
} else if (episodeFileCount > 1) {
|
} else if (trackFileCount > 1) {
|
||||||
episodeFilesCountMessage = `${episodeFileCount} episode files`;
|
episodeFilesCountMessage = `${trackFileCount} episode files`;
|
||||||
}
|
}
|
||||||
|
|
||||||
let expandIcon = icons.EXPAND_INDETERMINATE;
|
let expandIcon = icons.EXPAND_INDETERMINATE;
|
||||||
|
@ -310,13 +309,6 @@ class SeriesDetails extends Component {
|
||||||
|
|
||||||
<div className={styles.details}>
|
<div className={styles.details}>
|
||||||
<div>
|
<div>
|
||||||
{
|
|
||||||
!!runtime &&
|
|
||||||
<span className={styles.runtime}>
|
|
||||||
{runtime} Minutes
|
|
||||||
</span>
|
|
||||||
}
|
|
||||||
|
|
||||||
<HeartRating
|
<HeartRating
|
||||||
rating={ratings.value}
|
rating={ratings.value}
|
||||||
iconSize={20}
|
iconSize={20}
|
||||||
|
@ -491,10 +483,12 @@ class SeriesDetails extends Component {
|
||||||
albums.slice(0).reverse().map((season) => {
|
albums.slice(0).reverse().map((season) => {
|
||||||
return (
|
return (
|
||||||
<SeriesDetailsSeasonConnector
|
<SeriesDetailsSeasonConnector
|
||||||
key={season.seasonNumber}
|
key={season.id}
|
||||||
artistId={id}
|
artistId={id}
|
||||||
|
albumId={season.id}
|
||||||
|
statistics={season.statistics}
|
||||||
{...season}
|
{...season}
|
||||||
isExpanded={expandedState[season.seasonNumber]}
|
isExpanded={expandedState[season.id]}
|
||||||
onExpandPress={this.onExpandPress}
|
onExpandPress={this.onExpandPress}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
@ -548,10 +542,9 @@ SeriesDetails.propTypes = {
|
||||||
tvMazeId: PropTypes.number,
|
tvMazeId: PropTypes.number,
|
||||||
imdbId: PropTypes.string,
|
imdbId: PropTypes.string,
|
||||||
artistName: PropTypes.string.isRequired,
|
artistName: PropTypes.string.isRequired,
|
||||||
runtime: PropTypes.number.isRequired,
|
|
||||||
ratings: PropTypes.object.isRequired,
|
ratings: PropTypes.object.isRequired,
|
||||||
sizeOnDisk: PropTypes.number.isRequired,
|
sizeOnDisk: PropTypes.number.isRequired,
|
||||||
episodeFileCount: PropTypes.number,
|
trackFileCount: PropTypes.number,
|
||||||
qualityProfileId: PropTypes.number.isRequired,
|
qualityProfileId: PropTypes.number.isRequired,
|
||||||
monitored: PropTypes.bool.isRequired,
|
monitored: PropTypes.bool.isRequired,
|
||||||
status: PropTypes.string.isRequired,
|
status: PropTypes.string.isRequired,
|
||||||
|
|
|
@ -16,30 +16,16 @@ 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=${foreignArtistId}`}
|
to={`https://musicbrainz.org/artist/${foreignArtistId}`}
|
||||||
>
|
>
|
||||||
<Label
|
<Label
|
||||||
className={styles.linkLabel}
|
className={styles.linkLabel}
|
||||||
kind={kinds.INFO}
|
kind={kinds.INFO}
|
||||||
size={sizes.LARGE}
|
size={sizes.LARGE}
|
||||||
>
|
>
|
||||||
The TVDB
|
Musicbrainz
|
||||||
</Label>
|
</Label>
|
||||||
</Link>
|
</Link>
|
||||||
|
|
||||||
<Link
|
|
||||||
className={styles.link}
|
|
||||||
to={`http://trakt.tv/search/tvdb/${foreignArtistId}?id_type=show`}
|
|
||||||
>
|
|
||||||
<Label
|
|
||||||
className={styles.linkLabel}
|
|
||||||
kind={kinds.INFO}
|
|
||||||
size={sizes.LARGE}
|
|
||||||
>
|
|
||||||
Trakt
|
|
||||||
</Label>
|
|
||||||
</Link>
|
|
||||||
|
|
||||||
{
|
{
|
||||||
!!tvMazeId &&
|
!!tvMazeId &&
|
||||||
<Link
|
<Link
|
||||||
|
|
|
@ -38,7 +38,7 @@
|
||||||
.left {
|
.left {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
flex: 0 1 300px;
|
flex: 0 1 600px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.left,
|
.left,
|
||||||
|
|
|
@ -23,30 +23,6 @@ import OrganizePreviewModalConnector from 'Organize/OrganizePreviewModalConnecto
|
||||||
import EpisodeRowConnector from './EpisodeRowConnector';
|
import EpisodeRowConnector from './EpisodeRowConnector';
|
||||||
import styles from './SeriesDetailsSeason.css';
|
import styles from './SeriesDetailsSeason.css';
|
||||||
|
|
||||||
function getSeasonStatistics(episodes) {
|
|
||||||
let episodeCount = 0;
|
|
||||||
let episodeFileCount = 0;
|
|
||||||
let totalEpisodeCount = 0;
|
|
||||||
|
|
||||||
episodes.forEach((episode) => {
|
|
||||||
if (episode.episodeFileId || (episode.monitored && isBefore(episode.airDateUtc))) {
|
|
||||||
episodeCount++;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (episode.episodeFileId) {
|
|
||||||
episodeFileCount++;
|
|
||||||
}
|
|
||||||
|
|
||||||
totalEpisodeCount++;
|
|
||||||
});
|
|
||||||
|
|
||||||
return {
|
|
||||||
episodeCount,
|
|
||||||
episodeFileCount,
|
|
||||||
totalEpisodeCount
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function getEpisodeCountKind(monitored, episodeFileCount, episodeCount) {
|
function getEpisodeCountKind(monitored, episodeFileCount, episodeCount) {
|
||||||
if (episodeFileCount === episodeCount && episodeCount > 0) {
|
if (episodeFileCount === episodeCount && episodeCount > 0) {
|
||||||
return kinds.SUCCESS;
|
return kinds.SUCCESS;
|
||||||
|
@ -89,7 +65,7 @@ class SeriesDetailsSeason extends Component {
|
||||||
|
|
||||||
_expandByDefault() {
|
_expandByDefault() {
|
||||||
const {
|
const {
|
||||||
seasonNumber,
|
albumId,
|
||||||
onExpandPress,
|
onExpandPress,
|
||||||
items
|
items
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
@ -99,7 +75,7 @@ class SeriesDetailsSeason extends Component {
|
||||||
isAfter(item.airDateUtc, { days: -30 });
|
isAfter(item.airDateUtc, { days: -30 });
|
||||||
});
|
});
|
||||||
|
|
||||||
onExpandPress(seasonNumber, expand && seasonNumber > 0);
|
onExpandPress(albumId, expand && albumId > 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
|
@ -123,11 +99,11 @@ class SeriesDetailsSeason extends Component {
|
||||||
|
|
||||||
onExpandPress = () => {
|
onExpandPress = () => {
|
||||||
const {
|
const {
|
||||||
seasonNumber,
|
albumId,
|
||||||
isExpanded
|
isExpanded
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
|
||||||
this.props.onExpandPress(seasonNumber, !isExpanded);
|
this.props.onExpandPress(albumId, !isExpanded);
|
||||||
}
|
}
|
||||||
|
|
||||||
onMonitorEpisodePress = (episodeId, monitored, { shiftKey }) => {
|
onMonitorEpisodePress = (episodeId, monitored, { shiftKey }) => {
|
||||||
|
@ -155,7 +131,10 @@ class SeriesDetailsSeason extends Component {
|
||||||
const {
|
const {
|
||||||
artistId,
|
artistId,
|
||||||
monitored,
|
monitored,
|
||||||
seasonNumber,
|
title,
|
||||||
|
releaseDate,
|
||||||
|
albumId,
|
||||||
|
statistics,
|
||||||
items,
|
items,
|
||||||
columns,
|
columns,
|
||||||
isSaving,
|
isSaving,
|
||||||
|
@ -169,10 +148,10 @@ class SeriesDetailsSeason extends Component {
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
|
||||||
const {
|
const {
|
||||||
episodeCount,
|
trackCount,
|
||||||
episodeFileCount,
|
trackFileCount,
|
||||||
totalEpisodeCount
|
totalTrackCount
|
||||||
} = getSeasonStatistics(items);
|
} = statistics;
|
||||||
|
|
||||||
const {
|
const {
|
||||||
isOrganizeModalOpen,
|
isOrganizeModalOpen,
|
||||||
|
@ -194,22 +173,22 @@ class SeriesDetailsSeason extends Component {
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{
|
{
|
||||||
seasonNumber === 0 ?
|
albumId === 0 ?
|
||||||
<span className={styles.seasonNumber}>
|
<span className={styles.seasonNumber}>
|
||||||
Specials
|
Specials
|
||||||
</span> :
|
</span> :
|
||||||
<span className={styles.seasonNumber}>
|
<span className={styles.seasonNumber}>
|
||||||
Season {seasonNumber}
|
{title}
|
||||||
</span>
|
</span>
|
||||||
}
|
}
|
||||||
|
|
||||||
<Label
|
<Label
|
||||||
title={`${totalEpisodeCount} episodes total. ${episodeFileCount} episodes with files.`}
|
title={`${totalTrackCount} tracks total. ${trackFileCount} tracks with files.`}
|
||||||
kind={getEpisodeCountKind(monitored, episodeFileCount, episodeCount)}
|
kind={getEpisodeCountKind(monitored, trackFileCount, trackCount)}
|
||||||
size={sizes.LARGE}
|
size={sizes.LARGE}
|
||||||
>
|
>
|
||||||
{
|
{
|
||||||
<span>{episodeFileCount} / {episodeCount}</span>
|
<span>{trackFileCount} / {trackCount}</span>
|
||||||
}
|
}
|
||||||
</Label>
|
</Label>
|
||||||
</div>
|
</div>
|
||||||
|
@ -218,12 +197,7 @@ class SeriesDetailsSeason extends Component {
|
||||||
className={styles.expandButton}
|
className={styles.expandButton}
|
||||||
onPress={this.onExpandPress}
|
onPress={this.onExpandPress}
|
||||||
>
|
>
|
||||||
<Icon
|
|
||||||
className={styles.expandButtonIcon}
|
|
||||||
name={isExpanded ? icons.COLLAPSE : icons.EXPAND}
|
|
||||||
title={isExpanded ? 'Hide episodes' : 'Show episodes'}
|
|
||||||
size={24}
|
|
||||||
/>
|
|
||||||
{
|
{
|
||||||
!isSmallScreen &&
|
!isSmallScreen &&
|
||||||
<span> </span>
|
<span> </span>
|
||||||
|
@ -277,7 +251,7 @@ class SeriesDetailsSeason extends Component {
|
||||||
name={icons.EPISODE_FILE}
|
name={icons.EPISODE_FILE}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
Manage Episodes
|
Manage Tracks
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
</MenuContent>
|
</MenuContent>
|
||||||
</Menu> :
|
</Menu> :
|
||||||
|
@ -286,7 +260,7 @@ class SeriesDetailsSeason extends Component {
|
||||||
<SpinnerIconButton
|
<SpinnerIconButton
|
||||||
className={styles.actionButton}
|
className={styles.actionButton}
|
||||||
name={icons.SEARCH}
|
name={icons.SEARCH}
|
||||||
title="Search for monitored episodes in this seasons"
|
title="Search for album"
|
||||||
size={24}
|
size={24}
|
||||||
isSpinning={isSearching}
|
isSpinning={isSearching}
|
||||||
onPress={onSearchPress}
|
onPress={onSearchPress}
|
||||||
|
@ -295,7 +269,7 @@ class SeriesDetailsSeason extends Component {
|
||||||
<IconButton
|
<IconButton
|
||||||
className={styles.actionButton}
|
className={styles.actionButton}
|
||||||
name={icons.ORGANIZE}
|
name={icons.ORGANIZE}
|
||||||
title="Preview rename for this season"
|
title="Preview rename for this album"
|
||||||
size={24}
|
size={24}
|
||||||
onPress={this.onOrganizePress}
|
onPress={this.onOrganizePress}
|
||||||
/>
|
/>
|
||||||
|
@ -303,7 +277,7 @@ class SeriesDetailsSeason extends Component {
|
||||||
<IconButton
|
<IconButton
|
||||||
className={styles.actionButton}
|
className={styles.actionButton}
|
||||||
name={icons.EPISODE_FILE}
|
name={icons.EPISODE_FILE}
|
||||||
title="Manage episode files in this series"
|
title="Manage track files in this artist"
|
||||||
size={24}
|
size={24}
|
||||||
onPress={this.onManageEpisodesPress}
|
onPress={this.onManageEpisodesPress}
|
||||||
/>
|
/>
|
||||||
|
@ -312,59 +286,17 @@ class SeriesDetailsSeason extends Component {
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
|
||||||
{
|
|
||||||
isExpanded &&
|
|
||||||
<div className={styles.episodes}>
|
|
||||||
{
|
|
||||||
items.length ?
|
|
||||||
<Table
|
|
||||||
columns={columns}
|
|
||||||
onTableOptionChange={onTableOptionChange}
|
|
||||||
>
|
|
||||||
<TableBody>
|
|
||||||
{
|
|
||||||
items.map((item) => {
|
|
||||||
return (
|
|
||||||
<EpisodeRowConnector
|
|
||||||
key={item.id}
|
|
||||||
columns={columns}
|
|
||||||
{...item}
|
|
||||||
onMonitorEpisodePress={this.onMonitorEpisodePress}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
})
|
|
||||||
}
|
|
||||||
</TableBody>
|
|
||||||
</Table> :
|
|
||||||
|
|
||||||
<div className={styles.noEpisodes}>
|
|
||||||
No episodes in this season
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
<div className={styles.collapseButtonContainer}>
|
|
||||||
<IconButton
|
|
||||||
name={icons.COLLAPSE}
|
|
||||||
size={20}
|
|
||||||
title="Hide episodes"
|
|
||||||
onPress={this.onExpandPress}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<OrganizePreviewModalConnector
|
<OrganizePreviewModalConnector
|
||||||
isOpen={isOrganizeModalOpen}
|
isOpen={isOrganizeModalOpen}
|
||||||
artistId={artistId}
|
artistId={artistId}
|
||||||
seasonNumber={seasonNumber}
|
albumId={albumId}
|
||||||
onModalClose={this.onOrganizeModalClose}
|
onModalClose={this.onOrganizeModalClose}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<EpisodeFileEditorModal
|
<EpisodeFileEditorModal
|
||||||
isOpen={isManageEpisodesOpen}
|
isOpen={isManageEpisodesOpen}
|
||||||
artistId={artistId}
|
artistId={artistId}
|
||||||
seasonNumber={seasonNumber}
|
albumId={albumId}
|
||||||
onModalClose={this.onManageEpisodesModalClose}
|
onModalClose={this.onManageEpisodesModalClose}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
@ -375,7 +307,10 @@ class SeriesDetailsSeason extends Component {
|
||||||
SeriesDetailsSeason.propTypes = {
|
SeriesDetailsSeason.propTypes = {
|
||||||
artistId: PropTypes.number.isRequired,
|
artistId: PropTypes.number.isRequired,
|
||||||
monitored: PropTypes.bool.isRequired,
|
monitored: PropTypes.bool.isRequired,
|
||||||
seasonNumber: PropTypes.number.isRequired,
|
title: PropTypes.string.isRequired,
|
||||||
|
releaseDate: PropTypes.string.isRequired,
|
||||||
|
albumId: PropTypes.number.isRequired,
|
||||||
|
statistics: PropTypes.object.isRequired,
|
||||||
items: PropTypes.arrayOf(PropTypes.object).isRequired,
|
items: PropTypes.arrayOf(PropTypes.object).isRequired,
|
||||||
columns: PropTypes.arrayOf(PropTypes.object).isRequired,
|
columns: PropTypes.arrayOf(PropTypes.object).isRequired,
|
||||||
isSaving: PropTypes.bool,
|
isSaving: PropTypes.bool,
|
||||||
|
@ -390,4 +325,12 @@ SeriesDetailsSeason.propTypes = {
|
||||||
onSearchPress: PropTypes.func.isRequired
|
onSearchPress: PropTypes.func.isRequired
|
||||||
};
|
};
|
||||||
|
|
||||||
|
SeriesDetailsSeason.defaultProps = {
|
||||||
|
statistics: {
|
||||||
|
trackFileCount: 0,
|
||||||
|
totalTrackCount: 0,
|
||||||
|
percentOfTracks: 0
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
export default SeriesDetailsSeason;
|
export default SeriesDetailsSeason;
|
||||||
|
|
|
@ -60,12 +60,12 @@ class SeriesDetailsSeasonConnector extends Component {
|
||||||
onMonitorSeasonPress = (monitored) => {
|
onMonitorSeasonPress = (monitored) => {
|
||||||
const {
|
const {
|
||||||
artistId,
|
artistId,
|
||||||
seasonNumber
|
albumId
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
|
||||||
this.props.toggleSeasonMonitored({
|
this.props.toggleSeasonMonitored({
|
||||||
artistId,
|
artistId,
|
||||||
seasonNumber,
|
albumId,
|
||||||
monitored
|
monitored
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -73,13 +73,13 @@ class SeriesDetailsSeasonConnector extends Component {
|
||||||
onSearchPress = () => {
|
onSearchPress = () => {
|
||||||
const {
|
const {
|
||||||
artistId,
|
artistId,
|
||||||
seasonNumber
|
albumId
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
|
||||||
this.props.executeCommand({
|
this.props.executeCommand({
|
||||||
name: commandNames.SEASON_SEARCH,
|
name: commandNames.SEASON_SEARCH,
|
||||||
artistId,
|
artistId,
|
||||||
seasonNumber
|
albumIds: [albumId]
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -108,7 +108,7 @@ class SeriesDetailsSeasonConnector extends Component {
|
||||||
|
|
||||||
SeriesDetailsSeasonConnector.propTypes = {
|
SeriesDetailsSeasonConnector.propTypes = {
|
||||||
artistId: PropTypes.number.isRequired,
|
artistId: PropTypes.number.isRequired,
|
||||||
seasonNumber: PropTypes.number.isRequired,
|
albumId: PropTypes.number.isRequired,
|
||||||
toggleSeasonMonitored: PropTypes.func.isRequired,
|
toggleSeasonMonitored: PropTypes.func.isRequired,
|
||||||
toggleEpisodesMonitored: PropTypes.func.isRequired,
|
toggleEpisodesMonitored: PropTypes.func.isRequired,
|
||||||
setEpisodesTableOption: PropTypes.func.isRequired,
|
setEpisodesTableOption: PropTypes.func.isRequired,
|
||||||
|
|
|
@ -15,5 +15,5 @@ export const RENAME_FILES = 'RenameFiles';
|
||||||
export const RENAME_ARTIST = 'RenameArtist';
|
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 = 'AlbumSearch';
|
||||||
export const ARTIST_SEARCH = 'ArtistSearch';
|
export const ARTIST_SEARCH = 'ArtistSearch';
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import React, { Component } from 'react';
|
import React, { Component } from 'react';
|
||||||
import Modal from 'Components/Modal/Modal';
|
import Modal from 'Components/Modal/Modal';
|
||||||
import SelectSeasonModalContentConnector from './SelectSeasonModalContentConnector';
|
import SelectAlbumModalContentConnector from './SelectAlbumModalContentConnector';
|
||||||
|
|
||||||
class SelectSeasonModal extends Component {
|
class SelectAlbumModal extends Component {
|
||||||
|
|
||||||
//
|
//
|
||||||
// Render
|
// Render
|
||||||
|
@ -20,7 +20,7 @@ class SelectSeasonModal extends Component {
|
||||||
isOpen={isOpen}
|
isOpen={isOpen}
|
||||||
onModalClose={onModalClose}
|
onModalClose={onModalClose}
|
||||||
>
|
>
|
||||||
<SelectSeasonModalContentConnector
|
<SelectAlbumModalContentConnector
|
||||||
{...otherProps}
|
{...otherProps}
|
||||||
onModalClose={onModalClose}
|
onModalClose={onModalClose}
|
||||||
/>
|
/>
|
||||||
|
@ -29,9 +29,9 @@ class SelectSeasonModal extends Component {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
SelectSeasonModal.propTypes = {
|
SelectAlbumModal.propTypes = {
|
||||||
isOpen: PropTypes.bool.isRequired,
|
isOpen: PropTypes.bool.isRequired,
|
||||||
onModalClose: PropTypes.func.isRequired
|
onModalClose: PropTypes.func.isRequired
|
||||||
};
|
};
|
||||||
|
|
||||||
export default SelectSeasonModal;
|
export default SelectAlbumModal;
|
|
@ -5,9 +5,9 @@ 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 SelectSeasonRow from './SelectSeasonRow';
|
import SelectAlbumRow from './SelectAlbumRow';
|
||||||
|
|
||||||
class SelectSeasonModalContent extends Component {
|
class SelectAlbumModalContent extends Component {
|
||||||
|
|
||||||
//
|
//
|
||||||
// Render
|
// Render
|
||||||
|
@ -15,24 +15,25 @@ class SelectSeasonModalContent extends Component {
|
||||||
render() {
|
render() {
|
||||||
const {
|
const {
|
||||||
items,
|
items,
|
||||||
onSeasonSelect,
|
onAlbumSelect,
|
||||||
onModalClose
|
onModalClose
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ModalContent onModalClose={onModalClose}>
|
<ModalContent onModalClose={onModalClose}>
|
||||||
<ModalHeader>
|
<ModalHeader>
|
||||||
Manual Import - Select Season
|
Manual Import - Select Album
|
||||||
</ModalHeader>
|
</ModalHeader>
|
||||||
|
|
||||||
<ModalBody>
|
<ModalBody>
|
||||||
{
|
{
|
||||||
items.map((item) => {
|
items.map((item) => {
|
||||||
return (
|
return (
|
||||||
<SelectSeasonRow
|
<SelectAlbumRow
|
||||||
key={item.seasonNumber}
|
key={item.id}
|
||||||
seasonNumber={item.seasonNumber}
|
id={item.id}
|
||||||
onSeasonSelect={onSeasonSelect}
|
title={item.title}
|
||||||
|
onAlbumSelect={onAlbumSelect}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
})
|
})
|
||||||
|
@ -49,10 +50,10 @@ class SelectSeasonModalContent extends Component {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
SelectSeasonModalContent.propTypes = {
|
SelectAlbumModalContent.propTypes = {
|
||||||
items: PropTypes.arrayOf(PropTypes.object).isRequired,
|
items: PropTypes.arrayOf(PropTypes.object).isRequired,
|
||||||
onSeasonSelect: PropTypes.func.isRequired,
|
onAlbumSelect: PropTypes.func.isRequired,
|
||||||
onModalClose: PropTypes.func.isRequired
|
onModalClose: PropTypes.func.isRequired
|
||||||
};
|
};
|
||||||
|
|
||||||
export default SelectSeasonModalContent;
|
export default SelectAlbumModalContent;
|
|
@ -1,17 +1,18 @@
|
||||||
|
import _ from 'lodash';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import React, { Component } from 'react';
|
import React, { Component } from 'react';
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import { createSelector } from 'reselect';
|
import { createSelector } from 'reselect';
|
||||||
import { updateInteractiveImportItem } from 'Store/Actions/interactiveImportActions';
|
import { updateInteractiveImportItem } from 'Store/Actions/interactiveImportActions';
|
||||||
import createArtistSelector from 'Store/Selectors/createArtistSelector';
|
import createArtistSelector from 'Store/Selectors/createArtistSelector';
|
||||||
import SelectSeasonModalContent from './SelectSeasonModalContent';
|
import SelectAlbumModalContent from './SelectAlbumModalContent';
|
||||||
|
|
||||||
function createMapStateToProps() {
|
function createMapStateToProps() {
|
||||||
return createSelector(
|
return createSelector(
|
||||||
createArtistSelector(),
|
createArtistSelector(),
|
||||||
(series) => {
|
(series) => {
|
||||||
return {
|
return {
|
||||||
items: series.seasons
|
items: series.albums
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
@ -21,16 +22,18 @@ const mapDispatchToProps = {
|
||||||
updateInteractiveImportItem
|
updateInteractiveImportItem
|
||||||
};
|
};
|
||||||
|
|
||||||
class SelectSeasonModalContentConnector extends Component {
|
class SelectAlbumModalContentConnector extends Component {
|
||||||
|
|
||||||
//
|
//
|
||||||
// Listeners
|
// Listeners
|
||||||
|
|
||||||
onSeasonSelect = (seasonNumber) => {
|
onAlbumSelect = (albumId) => {
|
||||||
|
const album = _.find(this.props.items, { id: albumId });
|
||||||
|
|
||||||
this.props.ids.forEach((id) => {
|
this.props.ids.forEach((id) => {
|
||||||
this.props.updateInteractiveImportItem({
|
this.props.updateInteractiveImportItem({
|
||||||
id,
|
id,
|
||||||
seasonNumber,
|
album,
|
||||||
episodes: []
|
episodes: []
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -43,15 +46,15 @@ class SelectSeasonModalContentConnector extends Component {
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
return (
|
return (
|
||||||
<SelectSeasonModalContent
|
<SelectAlbumModalContent
|
||||||
{...this.props}
|
{...this.props}
|
||||||
onSeasonSelect={this.onSeasonSelect}
|
onAlbumSelect={this.onAlbumSelect}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
SelectSeasonModalContentConnector.propTypes = {
|
SelectAlbumModalContentConnector.propTypes = {
|
||||||
ids: PropTypes.arrayOf(PropTypes.number).isRequired,
|
ids: PropTypes.arrayOf(PropTypes.number).isRequired,
|
||||||
artistId: PropTypes.number.isRequired,
|
artistId: PropTypes.number.isRequired,
|
||||||
items: PropTypes.arrayOf(PropTypes.object).isRequired,
|
items: PropTypes.arrayOf(PropTypes.object).isRequired,
|
||||||
|
@ -59,4 +62,4 @@ SelectSeasonModalContentConnector.propTypes = {
|
||||||
onModalClose: PropTypes.func.isRequired
|
onModalClose: PropTypes.func.isRequired
|
||||||
};
|
};
|
||||||
|
|
||||||
export default connect(createMapStateToProps, mapDispatchToProps)(SelectSeasonModalContentConnector);
|
export default connect(createMapStateToProps, mapDispatchToProps)(SelectAlbumModalContentConnector);
|
37
frontend/src/InteractiveImport/Album/SelectAlbumRow.js
Normal file
37
frontend/src/InteractiveImport/Album/SelectAlbumRow.js
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import React, { Component } from 'react';
|
||||||
|
import Link from 'Components/Link/Link';
|
||||||
|
import styles from './SelectAlbumRow.css';
|
||||||
|
|
||||||
|
class SelectAlbumRow extends Component {
|
||||||
|
|
||||||
|
//
|
||||||
|
// Listeners
|
||||||
|
|
||||||
|
onPress = () => {
|
||||||
|
this.props.onAlbumSelect(this.props.id);
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Render
|
||||||
|
|
||||||
|
render() {
|
||||||
|
return (
|
||||||
|
<Link
|
||||||
|
className={styles.season}
|
||||||
|
component="div"
|
||||||
|
onPress={this.onPress}
|
||||||
|
>
|
||||||
|
{this.props.title}
|
||||||
|
</Link>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SelectAlbumRow.propTypes = {
|
||||||
|
id: PropTypes.number.isRequired,
|
||||||
|
title: PropTypes.string.isRequired,
|
||||||
|
onAlbumSelect: PropTypes.func.isRequired
|
||||||
|
};
|
||||||
|
|
||||||
|
export default SelectAlbumRow;
|
|
@ -37,7 +37,7 @@ class SelectArtistModalContent extends Component {
|
||||||
render() {
|
render() {
|
||||||
const {
|
const {
|
||||||
items,
|
items,
|
||||||
onSeriesSelect,
|
onArtistSelect,
|
||||||
onModalClose
|
onModalClose
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
|
||||||
|
@ -46,7 +46,7 @@ class SelectArtistModalContent extends Component {
|
||||||
return (
|
return (
|
||||||
<ModalContent onModalClose={onModalClose}>
|
<ModalContent onModalClose={onModalClose}>
|
||||||
<ModalHeader>
|
<ModalHeader>
|
||||||
Manual Import - Select Series
|
Manual Import - Select Artist
|
||||||
</ModalHeader>
|
</ModalHeader>
|
||||||
|
|
||||||
<ModalBody
|
<ModalBody
|
||||||
|
@ -55,7 +55,7 @@ class SelectArtistModalContent extends Component {
|
||||||
>
|
>
|
||||||
<TextInput
|
<TextInput
|
||||||
className={styles.filterInput}
|
className={styles.filterInput}
|
||||||
placeholder="Filter series"
|
placeholder="Filter artist"
|
||||||
name="filter"
|
name="filter"
|
||||||
value={filter}
|
value={filter}
|
||||||
autoFocus={true}
|
autoFocus={true}
|
||||||
|
@ -65,13 +65,13 @@ class SelectArtistModalContent extends Component {
|
||||||
<Scroller className={styles.scroller}>
|
<Scroller className={styles.scroller}>
|
||||||
{
|
{
|
||||||
items.map((item) => {
|
items.map((item) => {
|
||||||
return item.title.toLowerCase().includes(filter) ?
|
return item.artistName.toLowerCase().includes(filter) ?
|
||||||
(
|
(
|
||||||
<SelectArtistRow
|
<SelectArtistRow
|
||||||
key={item.id}
|
key={item.id}
|
||||||
id={item.id}
|
id={item.id}
|
||||||
title={item.title}
|
artistName={item.artistName}
|
||||||
onSeriesSelect={onSeriesSelect}
|
onArtistSelect={onArtistSelect}
|
||||||
/>
|
/>
|
||||||
) :
|
) :
|
||||||
null;
|
null;
|
||||||
|
@ -92,7 +92,7 @@ class SelectArtistModalContent extends Component {
|
||||||
|
|
||||||
SelectArtistModalContent.propTypes = {
|
SelectArtistModalContent.propTypes = {
|
||||||
items: PropTypes.arrayOf(PropTypes.object).isRequired,
|
items: PropTypes.arrayOf(PropTypes.object).isRequired,
|
||||||
onSeriesSelect: PropTypes.func.isRequired,
|
onArtistSelect: PropTypes.func.isRequired,
|
||||||
onModalClose: PropTypes.func.isRequired
|
onModalClose: PropTypes.func.isRequired
|
||||||
};
|
};
|
||||||
|
|
|
@ -27,15 +27,15 @@ class SelectArtistModalContentConnector extends Component {
|
||||||
//
|
//
|
||||||
// Listeners
|
// Listeners
|
||||||
|
|
||||||
onSeriesSelect = (artistId) => {
|
onArtistSelect = (artistId) => {
|
||||||
const series = _.find(this.props.items, { id: artistId });
|
const artist = _.find(this.props.items, { id: artistId });
|
||||||
|
|
||||||
this.props.ids.forEach((id) => {
|
this.props.ids.forEach((id) => {
|
||||||
this.props.updateInteractiveImportItem({
|
this.props.updateInteractiveImportItem({
|
||||||
id,
|
id,
|
||||||
series,
|
artist,
|
||||||
seasonNumber: undefined,
|
album: undefined,
|
||||||
episodes: []
|
tracks: []
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -49,7 +49,7 @@ class SelectArtistModalContentConnector extends Component {
|
||||||
return (
|
return (
|
||||||
<SelectArtistModalContent
|
<SelectArtistModalContent
|
||||||
{...this.props}
|
{...this.props}
|
||||||
onSeriesSelect={this.onSeriesSelect}
|
onArtistSelect={this.onArtistSelect}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
.series {
|
.artist {
|
||||||
padding: 8px;
|
padding: 8px;
|
||||||
border-bottom: 1px solid $borderColor;
|
border-bottom: 1px solid $borderColor;
|
||||||
}
|
}
|
|
@ -9,7 +9,7 @@ class SelectArtistRow extends Component {
|
||||||
// Listeners
|
// Listeners
|
||||||
|
|
||||||
onPress = () => {
|
onPress = () => {
|
||||||
this.props.onSeriesSelect(this.props.id);
|
this.props.onArtistSelect(this.props.id);
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
|
@ -18,11 +18,11 @@ class SelectArtistRow extends Component {
|
||||||
render() {
|
render() {
|
||||||
return (
|
return (
|
||||||
<Link
|
<Link
|
||||||
className={styles.series}
|
className={styles.artist}
|
||||||
component="div"
|
component="div"
|
||||||
onPress={this.onPress}
|
onPress={this.onPress}
|
||||||
>
|
>
|
||||||
{this.props.title}
|
{this.props.artistName}
|
||||||
</Link>
|
</Link>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -30,8 +30,8 @@ class SelectArtistRow extends Component {
|
||||||
|
|
||||||
SelectArtistRow.propTypes = {
|
SelectArtistRow.propTypes = {
|
||||||
id: PropTypes.number.isRequired,
|
id: PropTypes.number.isRequired,
|
||||||
title: PropTypes.string.isRequired,
|
artistName: PropTypes.string.isRequired,
|
||||||
onSeriesSelect: PropTypes.func.isRequired
|
onArtistSelect: PropTypes.func.isRequired
|
||||||
};
|
};
|
||||||
|
|
||||||
export default SelectArtistRow;
|
export default SelectArtistRow;
|
|
@ -15,8 +15,8 @@ import ModalBody from 'Components/Modal/ModalBody';
|
||||||
import ModalFooter from 'Components/Modal/ModalFooter';
|
import ModalFooter from 'Components/Modal/ModalFooter';
|
||||||
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 SelectArtistModal from 'InteractiveImport/Series/SelectArtistModal';
|
import SelectArtistModal from 'InteractiveImport/Artist/SelectArtistModal';
|
||||||
import SelectSeasonModal from 'InteractiveImport/Season/SelectSeasonModal';
|
import SelectAlbumModal from 'InteractiveImport/Album/SelectAlbumModal';
|
||||||
import InteractiveImportRow from './InteractiveImportRow';
|
import InteractiveImportRow from './InteractiveImportRow';
|
||||||
import styles from './InteractiveImportModalContent.css';
|
import styles from './InteractiveImportModalContent.css';
|
||||||
|
|
||||||
|
@ -28,19 +28,19 @@ const columns = [
|
||||||
isVisible: true
|
isVisible: true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'series',
|
name: 'artist',
|
||||||
label: 'Series',
|
label: 'Artist',
|
||||||
isSortable: true,
|
isSortable: true,
|
||||||
isVisible: true
|
isVisible: true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'season',
|
name: 'album',
|
||||||
label: 'Season',
|
label: 'Album',
|
||||||
isVisible: true
|
isVisible: true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'episodes',
|
name: 'tracks',
|
||||||
label: 'Episode(s)',
|
label: 'Track(s)',
|
||||||
isVisible: true
|
isVisible: true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -79,7 +79,7 @@ class InteractiveImportModalContent extends Component {
|
||||||
selectedState: {},
|
selectedState: {},
|
||||||
invalidRowsSelected: [],
|
invalidRowsSelected: [],
|
||||||
isSelectArtistModalOpen: false,
|
isSelectArtistModalOpen: false,
|
||||||
isSelectSeasonModalOpen: false
|
isSelectAlbumModalOpen: false
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -131,16 +131,16 @@ class InteractiveImportModalContent extends Component {
|
||||||
this.setState({ isSelectArtistModalOpen: true });
|
this.setState({ isSelectArtistModalOpen: true });
|
||||||
}
|
}
|
||||||
|
|
||||||
onSelectSeasonPress = () => {
|
onSelectAlbumPress = () => {
|
||||||
this.setState({ isSelectSeasonModalOpen: true });
|
this.setState({ isSelectAlbumModalOpen: true });
|
||||||
}
|
}
|
||||||
|
|
||||||
onSelectArtistModalClose = () => {
|
onSelectArtistModalClose = () => {
|
||||||
this.setState({ isSelectArtistModalOpen: false });
|
this.setState({ isSelectArtistModalOpen: false });
|
||||||
}
|
}
|
||||||
|
|
||||||
onSelectSeasonModalClose = () => {
|
onSelectAlbumModalClose = () => {
|
||||||
this.setState({ isSelectSeasonModalOpen: false });
|
this.setState({ isSelectAlbumModalOpen: false });
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
|
@ -169,7 +169,7 @@ class InteractiveImportModalContent extends Component {
|
||||||
selectedState,
|
selectedState,
|
||||||
invalidRowsSelected,
|
invalidRowsSelected,
|
||||||
isSelectArtistModalOpen,
|
isSelectArtistModalOpen,
|
||||||
isSelectSeasonModalOpen
|
isSelectAlbumModalOpen
|
||||||
} = this.state;
|
} = this.state;
|
||||||
|
|
||||||
const selectedIds = this.getSelectedIds();
|
const selectedIds = this.getSelectedIds();
|
||||||
|
@ -250,11 +250,11 @@ class InteractiveImportModalContent extends Component {
|
||||||
|
|
||||||
<div className={downloadId ? styles.leftButtons : styles.centerButtons}>
|
<div className={downloadId ? styles.leftButtons : styles.centerButtons}>
|
||||||
<Button onPress={this.onSelectArtistPress}>
|
<Button onPress={this.onSelectArtistPress}>
|
||||||
Select Series
|
Select Artist
|
||||||
</Button>
|
</Button>
|
||||||
|
|
||||||
<Button onPress={this.onSelectSeasonPress}>
|
<Button onPress={this.onSelectAlbumPress}>
|
||||||
Select Season
|
Select Album
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -284,11 +284,11 @@ class InteractiveImportModalContent extends Component {
|
||||||
onModalClose={this.onSelectArtistModalClose}
|
onModalClose={this.onSelectArtistModalClose}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<SelectSeasonModal
|
<SelectAlbumModal
|
||||||
isOpen={isSelectSeasonModalOpen}
|
isOpen={isSelectAlbumModalOpen}
|
||||||
ids={selectedIds}
|
ids={selectedIds}
|
||||||
artistId={selectedItem && selectedItem.series && selectedItem.series.id}
|
artistId={selectedItem && selectedItem.series && selectedItem.series.id}
|
||||||
onModalClose={this.onSelectSeasonModalClose}
|
onModalClose={this.onSelectAlbumModalClose}
|
||||||
/>
|
/>
|
||||||
</ModalContent>
|
</ModalContent>
|
||||||
);
|
);
|
||||||
|
|
|
@ -71,31 +71,32 @@ class InteractiveImportModalContentConnector extends Component {
|
||||||
|
|
||||||
if (isSelected) {
|
if (isSelected) {
|
||||||
const {
|
const {
|
||||||
series,
|
artist,
|
||||||
seasonNumber,
|
album,
|
||||||
episodes,
|
tracks,
|
||||||
quality
|
quality
|
||||||
} = item;
|
} = item;
|
||||||
|
|
||||||
if (!series) {
|
if (!artist) {
|
||||||
this.setState({ interactiveImportErrorMessage: 'Series must be chosen for each selected file' });
|
this.setState({ interactiveImportErrorMessage: 'Artist must be chosen for each selected file' });
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isNaN(seasonNumber)) {
|
if (!album) {
|
||||||
this.setState({ interactiveImportErrorMessage: 'Season must be chosen for each selected file' });
|
this.setState({ interactiveImportErrorMessage: 'Album must be chosen for each selected file' });
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!episodes || !episodes.length) {
|
if (!tracks || !tracks.length) {
|
||||||
this.setState({ interactiveImportErrorMessage: 'One or more episodes must be chosen for each selected file' });
|
this.setState({ interactiveImportErrorMessage: 'One or more tracks must be chosen for each selected file' });
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
files.push({
|
files.push({
|
||||||
path: item.path,
|
path: item.path,
|
||||||
artistId: series.id,
|
artistId: artist.id,
|
||||||
episodeIds: _.map(episodes, 'id'),
|
albumId: album.id,
|
||||||
|
trackIds: _.map(tracks, 'id'),
|
||||||
quality,
|
quality,
|
||||||
downloadId: this.props.downloadId
|
downloadId: this.props.downloadId
|
||||||
});
|
});
|
||||||
|
|
|
@ -9,9 +9,9 @@ import TableRowCellButton from 'Components/Table/Cells/TableRowCellButton';
|
||||||
import TableSelectCell from 'Components/Table/Cells/TableSelectCell';
|
import TableSelectCell from 'Components/Table/Cells/TableSelectCell';
|
||||||
import Popover from 'Components/Tooltip/Popover';
|
import Popover from 'Components/Tooltip/Popover';
|
||||||
import EpisodeQuality from 'Episode/EpisodeQuality';
|
import EpisodeQuality from 'Episode/EpisodeQuality';
|
||||||
import SelectArtistModal from 'InteractiveImport/Series/SelectArtistModal';
|
import SelectArtistModal from 'InteractiveImport/Artist/SelectArtistModal';
|
||||||
import SelectSeasonModal from 'InteractiveImport/Season/SelectSeasonModal';
|
import SelectAlbumModal from 'InteractiveImport/Album/SelectAlbumModal';
|
||||||
import SelectEpisodeModal from 'InteractiveImport/Episode/SelectEpisodeModal';
|
import SelectTrackModal from 'InteractiveImport/Track/SelectTrackModal';
|
||||||
import SelectQualityModal from 'InteractiveImport/Quality/SelectQualityModal';
|
import SelectQualityModal from 'InteractiveImport/Quality/SelectQualityModal';
|
||||||
import InteractiveImportRowCellPlaceholder from './InteractiveImportRowCellPlaceholder';
|
import InteractiveImportRowCellPlaceholder from './InteractiveImportRowCellPlaceholder';
|
||||||
import styles from './InteractiveImportRow.css';
|
import styles from './InteractiveImportRow.css';
|
||||||
|
@ -26,8 +26,8 @@ class InteractiveImportRow extends Component {
|
||||||
|
|
||||||
this.state = {
|
this.state = {
|
||||||
isSelectArtistModalOpen: false,
|
isSelectArtistModalOpen: false,
|
||||||
isSelectSeasonModalOpen: false,
|
isSelectAlbumModalOpen: false,
|
||||||
isSelectEpisodeModalOpen: false,
|
isSelectTrackModalOpen: false,
|
||||||
isSelectQualityModalOpen: false
|
isSelectQualityModalOpen: false
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -35,13 +35,13 @@ class InteractiveImportRow extends Component {
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
const {
|
const {
|
||||||
id,
|
id,
|
||||||
series,
|
artist,
|
||||||
seasonNumber,
|
album,
|
||||||
episodes,
|
tracks,
|
||||||
quality
|
quality
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
|
||||||
if (series && seasonNumber !== undefined && episodes.length && quality) {
|
if (artist && album !== undefined && tracks.length && quality) {
|
||||||
this.props.onSelectedChange({ id, value: true });
|
this.props.onSelectedChange({ id, value: true });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -49,9 +49,9 @@ class InteractiveImportRow extends Component {
|
||||||
componentDidUpdate(prevProps) {
|
componentDidUpdate(prevProps) {
|
||||||
const {
|
const {
|
||||||
id,
|
id,
|
||||||
series,
|
artist,
|
||||||
seasonNumber,
|
album,
|
||||||
episodes,
|
tracks,
|
||||||
quality,
|
quality,
|
||||||
isSelected,
|
isSelected,
|
||||||
onValidRowChange
|
onValidRowChange
|
||||||
|
@ -61,7 +61,7 @@ class InteractiveImportRow extends Component {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const isValid = !!(series && seasonNumber != null && episodes.length && quality);
|
const isValid = !!(artist && album != null && tracks.length && quality);
|
||||||
|
|
||||||
if (isSelected && !isValid) {
|
if (isSelected && !isValid) {
|
||||||
onValidRowChange(id, false);
|
onValidRowChange(id, false);
|
||||||
|
@ -91,12 +91,12 @@ class InteractiveImportRow extends Component {
|
||||||
this.setState({ isSelectArtistModalOpen: true });
|
this.setState({ isSelectArtistModalOpen: true });
|
||||||
}
|
}
|
||||||
|
|
||||||
onSelectSeasonPress = () => {
|
onSelectAlbumPress = () => {
|
||||||
this.setState({ isSelectSeasonModalOpen: true });
|
this.setState({ isSelectAlbumModalOpen: true });
|
||||||
}
|
}
|
||||||
|
|
||||||
onSelectEpisodePress = () => {
|
onSelectTrackPress = () => {
|
||||||
this.setState({ isSelectEpisodeModalOpen: true });
|
this.setState({ isSelectTrackModalOpen: true });
|
||||||
}
|
}
|
||||||
|
|
||||||
onSelectQualityPress = () => {
|
onSelectQualityPress = () => {
|
||||||
|
@ -108,13 +108,13 @@ class InteractiveImportRow extends Component {
|
||||||
this.selectRowAfterChange(changed);
|
this.selectRowAfterChange(changed);
|
||||||
}
|
}
|
||||||
|
|
||||||
onSelectSeasonModalClose = (changed) => {
|
onSelectAlbumModalClose = (changed) => {
|
||||||
this.setState({ isSelectSeasonModalOpen: false });
|
this.setState({ isSelectAlbumModalOpen: false });
|
||||||
this.selectRowAfterChange(changed);
|
this.selectRowAfterChange(changed);
|
||||||
}
|
}
|
||||||
|
|
||||||
onSelectEpisodeModalClose = (changed) => {
|
onSelectTrackModalClose = (changed) => {
|
||||||
this.setState({ isSelectEpisodeModalOpen: false });
|
this.setState({ isSelectTrackModalOpen: false });
|
||||||
this.selectRowAfterChange(changed);
|
this.selectRowAfterChange(changed);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -130,9 +130,9 @@ class InteractiveImportRow extends Component {
|
||||||
const {
|
const {
|
||||||
id,
|
id,
|
||||||
relativePath,
|
relativePath,
|
||||||
series,
|
artist,
|
||||||
seasonNumber,
|
album,
|
||||||
episodes,
|
tracks,
|
||||||
quality,
|
quality,
|
||||||
size,
|
size,
|
||||||
rejections,
|
rejections,
|
||||||
|
@ -142,18 +142,19 @@ class InteractiveImportRow extends Component {
|
||||||
|
|
||||||
const {
|
const {
|
||||||
isSelectArtistModalOpen,
|
isSelectArtistModalOpen,
|
||||||
isSelectSeasonModalOpen,
|
isSelectAlbumModalOpen,
|
||||||
isSelectEpisodeModalOpen,
|
isSelectTrackModalOpen,
|
||||||
isSelectQualityModalOpen
|
isSelectQualityModalOpen
|
||||||
} = this.state;
|
} = this.state;
|
||||||
|
|
||||||
const seriesTitle = series ? series.title : '';
|
const seriesTitle = artist ? artist.artistName : '';
|
||||||
const episodeNumbers = episodes.map((episode) => episode.episodeNumber)
|
const albumTitle = album ? album.title : '';
|
||||||
|
const trackNumbers = tracks.map((episode) => episode.trackNumber)
|
||||||
.join(', ');
|
.join(', ');
|
||||||
|
|
||||||
const showSeriesPlaceholder = isSelected && !series;
|
const showSeriesPlaceholder = isSelected && !artist;
|
||||||
const showSeasonNumberPlaceholder = isSelected && !!series && isNaN(seasonNumber);
|
const showSeasonNumberPlaceholder = isSelected && !!artist && !album;
|
||||||
const showEpisodeNumbersPlaceholder = isSelected && Number.isInteger(seasonNumber) && !episodes.length;
|
const showEpisodeNumbersPlaceholder = isSelected && !!album && !tracks.length;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<TableRow>
|
<TableRow>
|
||||||
|
@ -179,20 +180,20 @@ class InteractiveImportRow extends Component {
|
||||||
</TableRowCellButton>
|
</TableRowCellButton>
|
||||||
|
|
||||||
<TableRowCellButton
|
<TableRowCellButton
|
||||||
isDisabled={!series}
|
isDisabled={!artist}
|
||||||
onPress={this.onSelectSeasonPress}
|
onPress={this.onSelectAlbumPress}
|
||||||
>
|
>
|
||||||
{
|
{
|
||||||
showSeasonNumberPlaceholder ? <InteractiveImportRowCellPlaceholder /> : seasonNumber
|
showSeasonNumberPlaceholder ? <InteractiveImportRowCellPlaceholder /> : albumTitle
|
||||||
}
|
}
|
||||||
</TableRowCellButton>
|
</TableRowCellButton>
|
||||||
|
|
||||||
<TableRowCellButton
|
<TableRowCellButton
|
||||||
isDisabled={!series || isNaN(seasonNumber)}
|
isDisabled={!artist || !album}
|
||||||
onPress={this.onSelectEpisodePress}
|
onPress={this.onSelectTrackPress}
|
||||||
>
|
>
|
||||||
{
|
{
|
||||||
showEpisodeNumbersPlaceholder ? <InteractiveImportRowCellPlaceholder /> : episodeNumbers
|
showEpisodeNumbersPlaceholder ? <InteractiveImportRowCellPlaceholder /> : trackNumbers
|
||||||
}
|
}
|
||||||
</TableRowCellButton>
|
</TableRowCellButton>
|
||||||
|
|
||||||
|
@ -244,19 +245,19 @@ class InteractiveImportRow extends Component {
|
||||||
onModalClose={this.onSelectArtistModalClose}
|
onModalClose={this.onSelectArtistModalClose}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<SelectSeasonModal
|
<SelectAlbumModal
|
||||||
isOpen={isSelectSeasonModalOpen}
|
isOpen={isSelectAlbumModalOpen}
|
||||||
ids={[id]}
|
ids={[id]}
|
||||||
artistId={series && series.id}
|
artistId={artist && artist.id}
|
||||||
onModalClose={this.onSelectSeasonModalClose}
|
onModalClose={this.onSelectAlbumModalClose}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<SelectEpisodeModal
|
<SelectTrackModal
|
||||||
isOpen={isSelectEpisodeModalOpen}
|
isOpen={isSelectTrackModalOpen}
|
||||||
id={id}
|
id={id}
|
||||||
artistId={series && series.id}
|
artistId={artist && artist.id}
|
||||||
seasonNumber={seasonNumber}
|
albumId={album && album.id}
|
||||||
onModalClose={this.onSelectEpisodeModalClose}
|
onModalClose={this.onSelectTrackModalClose}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<SelectQualityModal
|
<SelectQualityModal
|
||||||
|
@ -276,9 +277,9 @@ class InteractiveImportRow extends Component {
|
||||||
InteractiveImportRow.propTypes = {
|
InteractiveImportRow.propTypes = {
|
||||||
id: PropTypes.number.isRequired,
|
id: PropTypes.number.isRequired,
|
||||||
relativePath: PropTypes.string.isRequired,
|
relativePath: PropTypes.string.isRequired,
|
||||||
series: PropTypes.object,
|
artist: PropTypes.object,
|
||||||
seasonNumber: PropTypes.number,
|
album: PropTypes.object,
|
||||||
episodes: PropTypes.arrayOf(PropTypes.object).isRequired,
|
tracks: PropTypes.arrayOf(PropTypes.object).isRequired,
|
||||||
quality: PropTypes.object,
|
quality: PropTypes.object,
|
||||||
size: PropTypes.number.isRequired,
|
size: PropTypes.number.isRequired,
|
||||||
rejections: PropTypes.arrayOf(PropTypes.object).isRequired,
|
rejections: PropTypes.arrayOf(PropTypes.object).isRequired,
|
||||||
|
@ -288,7 +289,7 @@ InteractiveImportRow.propTypes = {
|
||||||
};
|
};
|
||||||
|
|
||||||
InteractiveImportRow.defaultProps = {
|
InteractiveImportRow.defaultProps = {
|
||||||
episodes: []
|
tracks: []
|
||||||
};
|
};
|
||||||
|
|
||||||
export default InteractiveImportRow;
|
export default InteractiveImportRow;
|
||||||
|
|
|
@ -1,40 +0,0 @@
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
import React, { Component } from 'react';
|
|
||||||
import Link from 'Components/Link/Link';
|
|
||||||
import styles from './SelectSeasonRow.css';
|
|
||||||
|
|
||||||
class SelectSeasonRow extends Component {
|
|
||||||
|
|
||||||
//
|
|
||||||
// Listeners
|
|
||||||
|
|
||||||
onPress = () => {
|
|
||||||
this.props.onSeasonSelect(this.props.seasonNumber);
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
// Render
|
|
||||||
|
|
||||||
render() {
|
|
||||||
const seasonNumber = this.props.seasonNumber;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Link
|
|
||||||
className={styles.season}
|
|
||||||
component="div"
|
|
||||||
onPress={this.onPress}
|
|
||||||
>
|
|
||||||
{
|
|
||||||
seasonNumber === 0 ? 'Specials' : `Season ${seasonNumber}`
|
|
||||||
}
|
|
||||||
</Link>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
SelectSeasonRow.propTypes = {
|
|
||||||
seasonNumber: PropTypes.number.isRequired,
|
|
||||||
onSeasonSelect: PropTypes.func.isRequired
|
|
||||||
};
|
|
||||||
|
|
||||||
export default SelectSeasonRow;
|
|
|
@ -1,9 +1,9 @@
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import React, { Component } from 'react';
|
import React, { Component } from 'react';
|
||||||
import Modal from 'Components/Modal/Modal';
|
import Modal from 'Components/Modal/Modal';
|
||||||
import SelectEpisodeModalContentConnector from './SelectEpisodeModalContentConnector';
|
import SelectTrackModalContentConnector from './SelectTrackModalContentConnector';
|
||||||
|
|
||||||
class SelectEpisodeModal extends Component {
|
class SelectTrackModal extends Component {
|
||||||
|
|
||||||
//
|
//
|
||||||
// Render
|
// Render
|
||||||
|
@ -20,7 +20,7 @@ class SelectEpisodeModal extends Component {
|
||||||
isOpen={isOpen}
|
isOpen={isOpen}
|
||||||
onModalClose={onModalClose}
|
onModalClose={onModalClose}
|
||||||
>
|
>
|
||||||
<SelectEpisodeModalContentConnector
|
<SelectTrackModalContentConnector
|
||||||
{...otherProps}
|
{...otherProps}
|
||||||
onModalClose={onModalClose}
|
onModalClose={onModalClose}
|
||||||
/>
|
/>
|
||||||
|
@ -29,9 +29,9 @@ class SelectEpisodeModal extends Component {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
SelectEpisodeModal.propTypes = {
|
SelectTrackModal.propTypes = {
|
||||||
isOpen: PropTypes.bool.isRequired,
|
isOpen: PropTypes.bool.isRequired,
|
||||||
onModalClose: PropTypes.func.isRequired
|
onModalClose: PropTypes.func.isRequired
|
||||||
};
|
};
|
||||||
|
|
||||||
export default SelectEpisodeModal;
|
export default SelectTrackModal;
|
|
@ -12,11 +12,11 @@ import ModalBody from 'Components/Modal/ModalBody';
|
||||||
import ModalFooter from 'Components/Modal/ModalFooter';
|
import ModalFooter from 'Components/Modal/ModalFooter';
|
||||||
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 SelectEpisodeRow from './SelectEpisodeRow';
|
import SelectTrackRow from './SelectTrackRow';
|
||||||
|
|
||||||
const columns = [
|
const columns = [
|
||||||
{
|
{
|
||||||
name: 'episodeNumber',
|
name: 'trackNumber',
|
||||||
label: '#',
|
label: '#',
|
||||||
isSortable: true,
|
isSortable: true,
|
||||||
isVisible: true
|
isVisible: true
|
||||||
|
@ -25,15 +25,10 @@ const columns = [
|
||||||
name: 'title',
|
name: 'title',
|
||||||
label: 'Title',
|
label: 'Title',
|
||||||
isVisible: true
|
isVisible: true
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'airDate',
|
|
||||||
label: 'Air Date',
|
|
||||||
isVisible: true
|
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
class SelectEpisodeModalContent extends Component {
|
class SelectTrackModalContent extends Component {
|
||||||
|
|
||||||
//
|
//
|
||||||
// Lifecycle
|
// Lifecycle
|
||||||
|
@ -69,8 +64,8 @@ class SelectEpisodeModalContent extends Component {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
onEpisodesSelect = () => {
|
onTracksSelect = () => {
|
||||||
this.props.onEpisodesSelect(this.getSelectedIds());
|
this.props.onTracksSelect(this.getSelectedIds());
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
|
@ -94,12 +89,12 @@ class SelectEpisodeModalContent extends Component {
|
||||||
selectedState
|
selectedState
|
||||||
} = this.state;
|
} = this.state;
|
||||||
|
|
||||||
const errorMessage = error && error.message || 'Unable to load episodes';
|
const errorMessage = error && error.message || 'Unable to load tracks';
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ModalContent onModalClose={onModalClose}>
|
<ModalContent onModalClose={onModalClose}>
|
||||||
<ModalHeader>
|
<ModalHeader>
|
||||||
Manual Import - Select Episode(s)
|
Manual Import - Select Track(s)
|
||||||
</ModalHeader>
|
</ModalHeader>
|
||||||
|
|
||||||
<ModalBody>
|
<ModalBody>
|
||||||
|
@ -129,12 +124,11 @@ class SelectEpisodeModalContent extends Component {
|
||||||
{
|
{
|
||||||
items.map((item) => {
|
items.map((item) => {
|
||||||
return (
|
return (
|
||||||
<SelectEpisodeRow
|
<SelectTrackRow
|
||||||
key={item.id}
|
key={item.id}
|
||||||
id={item.id}
|
id={item.id}
|
||||||
episodeNumber={item.episodeNumber}
|
trackNumber={item.trackNumber}
|
||||||
title={item.title}
|
title={item.title}
|
||||||
airDate={item.airDate}
|
|
||||||
isSelected={selectedState[item.id]}
|
isSelected={selectedState[item.id]}
|
||||||
onSelectedChange={this.onSelectedChange}
|
onSelectedChange={this.onSelectedChange}
|
||||||
/>
|
/>
|
||||||
|
@ -147,7 +141,7 @@ class SelectEpisodeModalContent extends Component {
|
||||||
|
|
||||||
{
|
{
|
||||||
isPopulated && !items.length &&
|
isPopulated && !items.length &&
|
||||||
'No episodes were found for the selected season'
|
'No tracks were found for the selected album'
|
||||||
}
|
}
|
||||||
</ModalBody>
|
</ModalBody>
|
||||||
|
|
||||||
|
@ -158,9 +152,9 @@ class SelectEpisodeModalContent extends Component {
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
kind={kinds.SUCCESS}
|
kind={kinds.SUCCESS}
|
||||||
onPress={this.onEpisodesSelect}
|
onPress={this.onTracksSelect}
|
||||||
>
|
>
|
||||||
Select Episodes
|
Select Tracks
|
||||||
</Button>
|
</Button>
|
||||||
</ModalFooter>
|
</ModalFooter>
|
||||||
</ModalContent>
|
</ModalContent>
|
||||||
|
@ -168,7 +162,7 @@ class SelectEpisodeModalContent extends Component {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
SelectEpisodeModalContent.propTypes = {
|
SelectTrackModalContent.propTypes = {
|
||||||
isFetching: PropTypes.bool.isRequired,
|
isFetching: PropTypes.bool.isRequired,
|
||||||
isPopulated: PropTypes.bool.isRequired,
|
isPopulated: PropTypes.bool.isRequired,
|
||||||
error: PropTypes.object,
|
error: PropTypes.object,
|
||||||
|
@ -176,8 +170,8 @@ SelectEpisodeModalContent.propTypes = {
|
||||||
sortKey: PropTypes.string,
|
sortKey: PropTypes.string,
|
||||||
sortDirection: PropTypes.string,
|
sortDirection: PropTypes.string,
|
||||||
onSortPress: PropTypes.func.isRequired,
|
onSortPress: PropTypes.func.isRequired,
|
||||||
onEpisodesSelect: PropTypes.func.isRequired,
|
onTracksSelect: PropTypes.func.isRequired,
|
||||||
onModalClose: PropTypes.func.isRequired
|
onModalClose: PropTypes.func.isRequired
|
||||||
};
|
};
|
||||||
|
|
||||||
export default SelectEpisodeModalContent;
|
export default SelectTrackModalContent;
|
|
@ -6,7 +6,7 @@ import connectSection from 'Store/connectSection';
|
||||||
import { fetchEpisodes, setEpisodesSort, clearEpisodes } from 'Store/Actions/episodeActions';
|
import { fetchEpisodes, setEpisodesSort, clearEpisodes } from 'Store/Actions/episodeActions';
|
||||||
import { updateInteractiveImportItem } from 'Store/Actions/interactiveImportActions';
|
import { updateInteractiveImportItem } from 'Store/Actions/interactiveImportActions';
|
||||||
import createClientSideCollectionSelector from 'Store/Selectors/createClientSideCollectionSelector';
|
import createClientSideCollectionSelector from 'Store/Selectors/createClientSideCollectionSelector';
|
||||||
import SelectEpisodeModalContent from './SelectEpisodeModalContent';
|
import SelectTrackModalContent from './SelectTrackModalContent';
|
||||||
|
|
||||||
function createMapStateToProps() {
|
function createMapStateToProps() {
|
||||||
return createSelector(
|
return createSelector(
|
||||||
|
@ -24,7 +24,7 @@ const mapDispatchToProps = {
|
||||||
updateInteractiveImportItem
|
updateInteractiveImportItem
|
||||||
};
|
};
|
||||||
|
|
||||||
class SelectEpisodeModalContentConnector extends Component {
|
class SelectTrackModalContentConnector extends Component {
|
||||||
|
|
||||||
//
|
//
|
||||||
// Lifecycle
|
// Lifecycle
|
||||||
|
@ -32,10 +32,10 @@ class SelectEpisodeModalContentConnector extends Component {
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
const {
|
const {
|
||||||
artistId,
|
artistId,
|
||||||
seasonNumber
|
albumId
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
|
||||||
this.props.fetchEpisodes({ artistId, seasonNumber });
|
this.props.fetchEpisodes({ artistId, albumId });
|
||||||
}
|
}
|
||||||
|
|
||||||
componentWillUnmount() {
|
componentWillUnmount() {
|
||||||
|
@ -51,8 +51,8 @@ class SelectEpisodeModalContentConnector extends Component {
|
||||||
this.props.setEpisodesSort({ sortKey, sortDirection });
|
this.props.setEpisodesSort({ sortKey, sortDirection });
|
||||||
}
|
}
|
||||||
|
|
||||||
onEpisodesSelect = (episodeIds) => {
|
onTracksSelect = (episodeIds) => {
|
||||||
const episodes = _.reduce(this.props.items, (acc, item) => {
|
const tracks = _.reduce(this.props.items, (acc, item) => {
|
||||||
if (episodeIds.indexOf(item.id) > -1) {
|
if (episodeIds.indexOf(item.id) > -1) {
|
||||||
acc.push(item);
|
acc.push(item);
|
||||||
}
|
}
|
||||||
|
@ -62,7 +62,7 @@ class SelectEpisodeModalContentConnector extends Component {
|
||||||
|
|
||||||
this.props.updateInteractiveImportItem({
|
this.props.updateInteractiveImportItem({
|
||||||
id: this.props.id,
|
id: this.props.id,
|
||||||
episodes: _.sortBy(episodes, 'episodeNumber')
|
tracks: _.sortBy(tracks, 'trackNumber')
|
||||||
});
|
});
|
||||||
|
|
||||||
this.props.onModalClose(true);
|
this.props.onModalClose(true);
|
||||||
|
@ -73,19 +73,19 @@ class SelectEpisodeModalContentConnector extends Component {
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
return (
|
return (
|
||||||
<SelectEpisodeModalContent
|
<SelectTrackModalContent
|
||||||
{...this.props}
|
{...this.props}
|
||||||
onSortPress={this.onSortPress}
|
onSortPress={this.onSortPress}
|
||||||
onEpisodesSelect={this.onEpisodesSelect}
|
onTracksSelect={this.onTracksSelect}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
SelectEpisodeModalContentConnector.propTypes = {
|
SelectTrackModalContentConnector.propTypes = {
|
||||||
id: PropTypes.number.isRequired,
|
id: PropTypes.number.isRequired,
|
||||||
artistId: PropTypes.number.isRequired,
|
artistId: PropTypes.number.isRequired,
|
||||||
seasonNumber: PropTypes.number.isRequired,
|
albumId: PropTypes.number.isRequired,
|
||||||
items: PropTypes.arrayOf(PropTypes.object).isRequired,
|
items: PropTypes.arrayOf(PropTypes.object).isRequired,
|
||||||
fetchEpisodes: PropTypes.func.isRequired,
|
fetchEpisodes: PropTypes.func.isRequired,
|
||||||
setEpisodesSort: PropTypes.func.isRequired,
|
setEpisodesSort: PropTypes.func.isRequired,
|
||||||
|
@ -100,4 +100,4 @@ export default connectSection(
|
||||||
undefined,
|
undefined,
|
||||||
undefined,
|
undefined,
|
||||||
{ section: 'episodes' }
|
{ section: 'episodes' }
|
||||||
)(SelectEpisodeModalContentConnector);
|
)(SelectTrackModalContentConnector);
|
|
@ -4,7 +4,7 @@ import TableRowButton from 'Components/Table/TableRowButton';
|
||||||
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';
|
||||||
|
|
||||||
class SelectEpisodeRow extends Component {
|
class SelectTrackRow extends Component {
|
||||||
|
|
||||||
//
|
//
|
||||||
// Listeners
|
// Listeners
|
||||||
|
@ -24,9 +24,8 @@ class SelectEpisodeRow extends Component {
|
||||||
render() {
|
render() {
|
||||||
const {
|
const {
|
||||||
id,
|
id,
|
||||||
episodeNumber,
|
trackNumber,
|
||||||
title,
|
title,
|
||||||
airDate,
|
|
||||||
isSelected,
|
isSelected,
|
||||||
onSelectedChange
|
onSelectedChange
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
@ -40,28 +39,24 @@ class SelectEpisodeRow extends Component {
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<TableRowCell>
|
<TableRowCell>
|
||||||
{episodeNumber}
|
{trackNumber}
|
||||||
</TableRowCell>
|
</TableRowCell>
|
||||||
|
|
||||||
<TableRowCell>
|
<TableRowCell>
|
||||||
{title}
|
{title}
|
||||||
</TableRowCell>
|
</TableRowCell>
|
||||||
|
|
||||||
<TableRowCell>
|
|
||||||
{airDate}
|
|
||||||
</TableRowCell>
|
|
||||||
</TableRowButton>
|
</TableRowButton>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
SelectEpisodeRow.propTypes = {
|
SelectTrackRow.propTypes = {
|
||||||
id: PropTypes.number.isRequired,
|
id: PropTypes.number.isRequired,
|
||||||
episodeNumber: PropTypes.number.isRequired,
|
trackNumber: PropTypes.number.isRequired,
|
||||||
title: PropTypes.string.isRequired,
|
title: PropTypes.string.isRequired,
|
||||||
airDate: PropTypes.string.isRequired,
|
|
||||||
isSelected: PropTypes.bool,
|
isSelected: PropTypes.bool,
|
||||||
onSelectedChange: PropTypes.func.isRequired
|
onSelectedChange: PropTypes.func.isRequired
|
||||||
};
|
};
|
||||||
|
|
||||||
export default SelectEpisodeRow;
|
export default SelectTrackRow;
|
|
@ -9,7 +9,7 @@ import { updateItem } from './baseActions';
|
||||||
const section = 'episodes';
|
const section = 'episodes';
|
||||||
|
|
||||||
const episodeActionHandlers = {
|
const episodeActionHandlers = {
|
||||||
[types.FETCH_EPISODES]: createFetchHandler(section, '/episode'),
|
[types.FETCH_EPISODES]: createFetchHandler(section, '/track'),
|
||||||
|
|
||||||
[types.TOGGLE_EPISODE_MONITORED]: function(payload) {
|
[types.TOGGLE_EPISODE_MONITORED]: function(payload) {
|
||||||
return function(dispatch, getState) {
|
return function(dispatch, getState) {
|
||||||
|
|
|
@ -8,10 +8,10 @@ import * as types from './actionTypes';
|
||||||
import { set, removeItem, updateItem } from './baseActions';
|
import { set, removeItem, updateItem } from './baseActions';
|
||||||
|
|
||||||
const section = 'episodeFiles';
|
const section = 'episodeFiles';
|
||||||
const deleteEpisodeFile = createRemoveItemHandler(section, '/episodeFile');
|
const deleteEpisodeFile = createRemoveItemHandler(section, '/trackFile');
|
||||||
|
|
||||||
const episodeFileActionHandlers = {
|
const episodeFileActionHandlers = {
|
||||||
[types.FETCH_EPISODE_FILES]: createFetchHandler(section, '/episodeFile'),
|
[types.FETCH_EPISODE_FILES]: createFetchHandler(section, '/trackFile'),
|
||||||
|
|
||||||
[types.DELETE_EPISODE_FILE]: function(payload) {
|
[types.DELETE_EPISODE_FILE]: function(payload) {
|
||||||
return function(dispatch, getState) {
|
return function(dispatch, getState) {
|
||||||
|
|
|
@ -13,8 +13,8 @@ export default function(history) {
|
||||||
isProduction
|
isProduction
|
||||||
} = window.Sonarr;
|
} = window.Sonarr;
|
||||||
|
|
||||||
const dsn = isProduction ? 'https://b80ca60625b443c38b242e0d21681eb7@sentry.sonarr.tv/13' :
|
const dsn = isProduction ? 'https://c3a5b33e08de4e18b7d0505e942dbc95:e35e6d535b034995a6896022c6bfed04@sentry.io/216290' :
|
||||||
'https://8dbaacdfe2ff4caf97dc7945aecf9ace@sentry.sonarr.tv/12';
|
'https://c3a5b33e08de4e18b7d0505e942dbc95:e35e6d535b034995a6896022c6bfed04@sentry.io/216290';
|
||||||
|
|
||||||
const middlewares = [];
|
const middlewares = [];
|
||||||
|
|
||||||
|
|
|
@ -60,11 +60,6 @@ namespace Lidarr.Api.V3.Artist
|
||||||
//public bool SeasonFolder { get; set; }
|
//public bool SeasonFolder { get; set; }
|
||||||
//public bool Monitored { get; set; }
|
//public bool Monitored { get; set; }
|
||||||
|
|
||||||
//public bool UseSceneNumbering { get; set; }
|
|
||||||
//public int Runtime { get; set; }
|
|
||||||
//public int TvdbId { get; set; }
|
|
||||||
//public int TvRageId { get; set; }
|
|
||||||
//public int TvMazeId { get; set; }
|
|
||||||
//public DateTime? FirstAired { get; set; }
|
//public DateTime? FirstAired { get; set; }
|
||||||
public DateTime? LastInfoSync { get; set; }
|
public DateTime? LastInfoSync { get; set; }
|
||||||
////public SeriesTypes SeriesType { get; set; }
|
////public SeriesTypes SeriesType { get; set; }
|
||||||
|
@ -138,10 +133,6 @@ namespace Lidarr.Api.V3.Artist
|
||||||
//AlternateTitles
|
//AlternateTitles
|
||||||
SortName = model.SortName,
|
SortName = model.SortName,
|
||||||
|
|
||||||
//TotalEpisodeCount
|
|
||||||
//EpisodeCount
|
|
||||||
//EpisodeFileCount
|
|
||||||
//SizeOnDisk
|
|
||||||
Status = model.Status,
|
Status = model.Status,
|
||||||
Overview = model.Overview,
|
Overview = model.Overview,
|
||||||
//NextAiring
|
//NextAiring
|
||||||
|
@ -160,12 +151,6 @@ namespace Lidarr.Api.V3.Artist
|
||||||
AlbumFolder = model.AlbumFolder,
|
AlbumFolder = model.AlbumFolder,
|
||||||
Monitored = model.Monitored,
|
Monitored = model.Monitored,
|
||||||
|
|
||||||
//UseSceneNumbering = model.UseSceneNumbering,
|
|
||||||
//Runtime = model.Runtime,
|
|
||||||
//TvdbId = model.TvdbId,
|
|
||||||
//TvRageId = model.TvRageId,
|
|
||||||
//TvMazeId = model.TvMazeId,
|
|
||||||
//FirstAired = model.FirstAired,
|
|
||||||
LastInfoSync = model.LastInfoSync,
|
LastInfoSync = model.LastInfoSync,
|
||||||
//SeriesType = model.SeriesType,
|
//SeriesType = model.SeriesType,
|
||||||
CleanName = model.CleanName,
|
CleanName = model.CleanName,
|
||||||
|
@ -193,10 +178,6 @@ namespace Lidarr.Api.V3.Artist
|
||||||
//AlternateTitles
|
//AlternateTitles
|
||||||
SortName = resource.SortName,
|
SortName = resource.SortName,
|
||||||
|
|
||||||
//TotalEpisodeCount
|
|
||||||
//EpisodeCount
|
|
||||||
//EpisodeFileCount
|
|
||||||
//SizeOnDisk
|
|
||||||
Status = resource.Status,
|
Status = resource.Status,
|
||||||
Overview = resource.Overview,
|
Overview = resource.Overview,
|
||||||
//NextAiring
|
//NextAiring
|
||||||
|
@ -215,12 +196,6 @@ namespace Lidarr.Api.V3.Artist
|
||||||
AlbumFolder = resource.AlbumFolder,
|
AlbumFolder = resource.AlbumFolder,
|
||||||
Monitored = resource.Monitored,
|
Monitored = resource.Monitored,
|
||||||
|
|
||||||
//UseSceneNumbering = resource.UseSceneNumbering,
|
|
||||||
//Runtime = resource.Runtime,
|
|
||||||
//TvdbId = resource.TvdbId,
|
|
||||||
//TvRageId = resource.TvRageId,
|
|
||||||
//TvMazeId = resource.TvMazeId,
|
|
||||||
//FirstAired = resource.FirstAired,
|
|
||||||
LastInfoSync = resource.LastInfoSync,
|
LastInfoSync = resource.LastInfoSync,
|
||||||
//SeriesType = resource.SeriesType,
|
//SeriesType = resource.SeriesType,
|
||||||
CleanName = resource.CleanName,
|
CleanName = resource.CleanName,
|
||||||
|
|
|
@ -3,6 +3,7 @@ using NzbDrone.Core.DecisionEngine;
|
||||||
using NzbDrone.Core.MediaFiles.TrackImport.Manual;
|
using NzbDrone.Core.MediaFiles.TrackImport.Manual;
|
||||||
using NzbDrone.Core.Qualities;
|
using NzbDrone.Core.Qualities;
|
||||||
using Lidarr.Api.V3.Artist;
|
using Lidarr.Api.V3.Artist;
|
||||||
|
using Lidarr.Api.V3.Albums;
|
||||||
using Lidarr.Api.V3.Tracks;
|
using Lidarr.Api.V3.Tracks;
|
||||||
using Lidarr.Http.REST;
|
using Lidarr.Http.REST;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
@ -16,9 +17,9 @@ namespace Lidarr.Api.V3.ManualImport
|
||||||
public string RelativePath { get; set; }
|
public string RelativePath { get; set; }
|
||||||
public string Name { get; set; }
|
public string Name { get; set; }
|
||||||
public long Size { get; set; }
|
public long Size { get; set; }
|
||||||
//public ArtistResource Artist { get; set; }
|
public ArtistResource Artist { get; set; }
|
||||||
public int? SeasonNumber { get; set; }
|
public AlbumResource Album { get; set; }
|
||||||
public List<TrackResource> Episodes { get; set; }
|
public List<TrackResource> Tracks { get; set; }
|
||||||
public QualityModel Quality { get; set; }
|
public QualityModel Quality { get; set; }
|
||||||
public int QualityWeight { get; set; }
|
public int QualityWeight { get; set; }
|
||||||
public string DownloadId { get; set; }
|
public string DownloadId { get; set; }
|
||||||
|
@ -38,9 +39,9 @@ namespace Lidarr.Api.V3.ManualImport
|
||||||
RelativePath = model.RelativePath,
|
RelativePath = model.RelativePath,
|
||||||
Name = model.Name,
|
Name = model.Name,
|
||||||
Size = model.Size,
|
Size = model.Size,
|
||||||
//Artist = model.,
|
Artist = model.Artist.ToResource(),
|
||||||
SeasonNumber = model.SeasonNumber,
|
Album = model.Album.ToResource(),
|
||||||
//Episodes = model.Episodes.ToResource(),
|
Tracks = model.Tracks.ToResource(),
|
||||||
Quality = model.Quality,
|
Quality = model.Quality,
|
||||||
//QualityWeight
|
//QualityWeight
|
||||||
DownloadId = model.DownloadId,
|
DownloadId = model.DownloadId,
|
||||||
|
|
|
@ -15,9 +15,6 @@ namespace NzbDrone.Api.ManualImport
|
||||||
public string RelativePath { get; set; }
|
public string RelativePath { get; set; }
|
||||||
public string Name { get; set; }
|
public string Name { get; set; }
|
||||||
public long Size { get; set; }
|
public long Size { get; set; }
|
||||||
public SeriesResource Series { get; set; }
|
|
||||||
public int? SeasonNumber { get; set; }
|
|
||||||
public List<EpisodeResource> Episodes { get; set; }
|
|
||||||
public QualityModel Quality { get; set; }
|
public QualityModel Quality { get; set; }
|
||||||
public int QualityWeight { get; set; }
|
public int QualityWeight { get; set; }
|
||||||
public string DownloadId { get; set; }
|
public string DownloadId { get; set; }
|
||||||
|
@ -38,9 +35,6 @@ namespace NzbDrone.Api.ManualImport
|
||||||
RelativePath = model.RelativePath,
|
RelativePath = model.RelativePath,
|
||||||
Name = model.Name,
|
Name = model.Name,
|
||||||
Size = model.Size,
|
Size = model.Size,
|
||||||
Series = model.Series.ToResource(),
|
|
||||||
SeasonNumber = model.SeasonNumber,
|
|
||||||
Episodes = model.Episodes.ToResource(),
|
|
||||||
Quality = model.Quality,
|
Quality = model.Quality,
|
||||||
//QualityWeight
|
//QualityWeight
|
||||||
DownloadId = model.DownloadId,
|
DownloadId = model.DownloadId,
|
||||||
|
|
|
@ -84,7 +84,7 @@ namespace NzbDrone.Core.MediaFiles.TrackImport
|
||||||
trackFile.Size = _diskProvider.GetFileSize(localTrack.Path);
|
trackFile.Size = _diskProvider.GetFileSize(localTrack.Path);
|
||||||
trackFile.Quality = localTrack.Quality;
|
trackFile.Quality = localTrack.Quality;
|
||||||
trackFile.MediaInfo = localTrack.MediaInfo;
|
trackFile.MediaInfo = localTrack.MediaInfo;
|
||||||
trackFile.AlbumId = _albumRepository.FindByArtistAndName(localTrack.Artist.Name, Parser.Parser.CleanArtistTitle(localTrack.ParsedTrackInfo.AlbumTitle)).Id;
|
trackFile.AlbumId = localTrack.Album.Id;
|
||||||
trackFile.ReleaseGroup = localTrack.ParsedTrackInfo.ReleaseGroup;
|
trackFile.ReleaseGroup = localTrack.ParsedTrackInfo.ReleaseGroup;
|
||||||
trackFile.Tracks = localTrack.Tracks;
|
trackFile.Tracks = localTrack.Tracks;
|
||||||
trackFile.Language = localTrack.Language;
|
trackFile.Language = localTrack.Language;
|
||||||
|
|
|
@ -6,8 +6,9 @@ namespace NzbDrone.Core.MediaFiles.TrackImport.Manual
|
||||||
public class ManualImportFile
|
public class ManualImportFile
|
||||||
{
|
{
|
||||||
public string Path { get; set; }
|
public string Path { get; set; }
|
||||||
public int SeriesId { get; set; }
|
public int ArtistId { get; set; }
|
||||||
public List<int> EpisodeIds { get; set; }
|
public int AlbumId { get; set; }
|
||||||
|
public List<int> TrackIds { get; set; }
|
||||||
public QualityModel Quality { get; set; }
|
public QualityModel Quality { get; set; }
|
||||||
public string DownloadId { get; set; }
|
public string DownloadId { get; set; }
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using NzbDrone.Core.DecisionEngine;
|
using NzbDrone.Core.DecisionEngine;
|
||||||
using NzbDrone.Core.Qualities;
|
using NzbDrone.Core.Qualities;
|
||||||
using NzbDrone.Core.Tv;
|
using NzbDrone.Core.Music;
|
||||||
|
|
||||||
namespace NzbDrone.Core.MediaFiles.TrackImport.Manual
|
namespace NzbDrone.Core.MediaFiles.TrackImport.Manual
|
||||||
{
|
{
|
||||||
|
@ -11,9 +11,9 @@ namespace NzbDrone.Core.MediaFiles.TrackImport.Manual
|
||||||
public string RelativePath { get; set; }
|
public string RelativePath { get; set; }
|
||||||
public string Name { get; set; }
|
public string Name { get; set; }
|
||||||
public long Size { get; set; }
|
public long Size { get; set; }
|
||||||
public Series Series { get; set; }
|
public Artist Artist { get; set; }
|
||||||
public int? SeasonNumber { get; set; }
|
public Album Album { get; set; }
|
||||||
public List<Episode> Episodes { get; set; }
|
public List<Track> Tracks { get; set; }
|
||||||
public QualityModel Quality { get; set; }
|
public QualityModel Quality { get; set; }
|
||||||
public string DownloadId { get; set; }
|
public string DownloadId { get; set; }
|
||||||
public IEnumerable<Rejection> Rejections { get; set; }
|
public IEnumerable<Rejection> Rejections { get; set; }
|
||||||
|
|
|
@ -14,7 +14,7 @@ using NzbDrone.Core.Messaging.Commands;
|
||||||
using NzbDrone.Core.Messaging.Events;
|
using NzbDrone.Core.Messaging.Events;
|
||||||
using NzbDrone.Core.Parser;
|
using NzbDrone.Core.Parser;
|
||||||
using NzbDrone.Core.Parser.Model;
|
using NzbDrone.Core.Parser.Model;
|
||||||
using NzbDrone.Core.Tv;
|
using NzbDrone.Core.Music;
|
||||||
|
|
||||||
namespace NzbDrone.Core.MediaFiles.TrackImport.Manual
|
namespace NzbDrone.Core.MediaFiles.TrackImport.Manual
|
||||||
{
|
{
|
||||||
|
@ -29,10 +29,11 @@ namespace NzbDrone.Core.MediaFiles.TrackImport.Manual
|
||||||
private readonly IParsingService _parsingService;
|
private readonly IParsingService _parsingService;
|
||||||
private readonly IDiskScanService _diskScanService;
|
private readonly IDiskScanService _diskScanService;
|
||||||
private readonly IMakeImportDecision _importDecisionMaker;
|
private readonly IMakeImportDecision _importDecisionMaker;
|
||||||
private readonly ISeriesService _seriesService;
|
private readonly IArtistService _artistService;
|
||||||
private readonly IEpisodeService _episodeService;
|
private readonly IAlbumService _albumService;
|
||||||
|
private readonly ITrackService _trackService;
|
||||||
private readonly IVideoFileInfoReader _videoFileInfoReader;
|
private readonly IVideoFileInfoReader _videoFileInfoReader;
|
||||||
private readonly IImportApprovedEpisodes _importApprovedEpisodes;
|
private readonly IImportApprovedTracks _importApprovedTracks;
|
||||||
private readonly ITrackedDownloadService _trackedDownloadService;
|
private readonly ITrackedDownloadService _trackedDownloadService;
|
||||||
private readonly IDownloadedEpisodesImportService _downloadedEpisodesImportService;
|
private readonly IDownloadedEpisodesImportService _downloadedEpisodesImportService;
|
||||||
private readonly IEventAggregator _eventAggregator;
|
private readonly IEventAggregator _eventAggregator;
|
||||||
|
@ -42,10 +43,11 @@ namespace NzbDrone.Core.MediaFiles.TrackImport.Manual
|
||||||
IParsingService parsingService,
|
IParsingService parsingService,
|
||||||
IDiskScanService diskScanService,
|
IDiskScanService diskScanService,
|
||||||
IMakeImportDecision importDecisionMaker,
|
IMakeImportDecision importDecisionMaker,
|
||||||
ISeriesService seriesService,
|
IArtistService artistService,
|
||||||
IEpisodeService episodeService,
|
IAlbumService albumService,
|
||||||
|
ITrackService trackService,
|
||||||
IVideoFileInfoReader videoFileInfoReader,
|
IVideoFileInfoReader videoFileInfoReader,
|
||||||
IImportApprovedEpisodes importApprovedEpisodes,
|
IImportApprovedTracks importApprovedTracks,
|
||||||
ITrackedDownloadService trackedDownloadService,
|
ITrackedDownloadService trackedDownloadService,
|
||||||
IDownloadedEpisodesImportService downloadedEpisodesImportService,
|
IDownloadedEpisodesImportService downloadedEpisodesImportService,
|
||||||
IEventAggregator eventAggregator,
|
IEventAggregator eventAggregator,
|
||||||
|
@ -55,10 +57,11 @@ namespace NzbDrone.Core.MediaFiles.TrackImport.Manual
|
||||||
_parsingService = parsingService;
|
_parsingService = parsingService;
|
||||||
_diskScanService = diskScanService;
|
_diskScanService = diskScanService;
|
||||||
_importDecisionMaker = importDecisionMaker;
|
_importDecisionMaker = importDecisionMaker;
|
||||||
_seriesService = seriesService;
|
_artistService = artistService;
|
||||||
_episodeService = episodeService;
|
_albumService = albumService;
|
||||||
|
_trackService = trackService;
|
||||||
_videoFileInfoReader = videoFileInfoReader;
|
_videoFileInfoReader = videoFileInfoReader;
|
||||||
_importApprovedEpisodes = importApprovedEpisodes;
|
_importApprovedTracks = importApprovedTracks;
|
||||||
_trackedDownloadService = trackedDownloadService;
|
_trackedDownloadService = trackedDownloadService;
|
||||||
_downloadedEpisodesImportService = downloadedEpisodesImportService;
|
_downloadedEpisodesImportService = downloadedEpisodesImportService;
|
||||||
_eventAggregator = eventAggregator;
|
_eventAggregator = eventAggregator;
|
||||||
|
@ -94,179 +97,176 @@ namespace NzbDrone.Core.MediaFiles.TrackImport.Manual
|
||||||
|
|
||||||
private List<ManualImportItem> ProcessFolder(string folder, string downloadId)
|
private List<ManualImportItem> ProcessFolder(string folder, string downloadId)
|
||||||
{
|
{
|
||||||
throw new System.NotImplementedException("TODO: This will be rewritten for Music");
|
var directoryInfo = new DirectoryInfo(folder);
|
||||||
//var directoryInfo = new DirectoryInfo(folder);
|
var artist = _parsingService.GetArtist(directoryInfo.Name);
|
||||||
//var series = _parsingService.GetSeries(directoryInfo.Name);
|
|
||||||
|
|
||||||
//if (series == null && downloadId.IsNotNullOrWhiteSpace())
|
if (artist == null && downloadId.IsNotNullOrWhiteSpace())
|
||||||
//{
|
{
|
||||||
// var trackedDownload = _trackedDownloadService.Find(downloadId);
|
var trackedDownload = _trackedDownloadService.Find(downloadId);
|
||||||
// series = trackedDownload.RemoteEpisode.Series;
|
artist = trackedDownload.RemoteAlbum.Artist;
|
||||||
//}
|
}
|
||||||
|
|
||||||
//if (series == null)
|
if (artist == null)
|
||||||
//{
|
{
|
||||||
// var files = _diskScanService.GetVideoFiles(folder);
|
var files = _diskScanService.GetAudioFiles(folder);
|
||||||
|
|
||||||
// return files.Select(file => ProcessFile(file, downloadId, folder)).Where(i => i != null).ToList();
|
return files.Select(file => ProcessFile(file, downloadId, folder)).Where(i => i != null).ToList();
|
||||||
//}
|
}
|
||||||
|
|
||||||
//var folderInfo = Parser.Parser.ParseTitle(directoryInfo.Name);
|
var folderInfo = Parser.Parser.ParseMusicTitle(directoryInfo.Name);
|
||||||
//var seriesFiles = _diskScanService.GetVideoFiles(folder).ToList();
|
var artistFiles = _diskScanService.GetAudioFiles(folder).ToList();
|
||||||
//var decisions = _importDecisionMaker.GetImportDecisions(seriesFiles, series, folderInfo, SceneSource(series, folder));
|
var decisions = _importDecisionMaker.GetImportDecisions(artistFiles, artist, folderInfo);
|
||||||
|
|
||||||
//return decisions.Select(decision => MapItem(decision, folder, downloadId)).ToList();
|
return decisions.Select(decision => MapItem(decision, folder, downloadId)).ToList();
|
||||||
}
|
}
|
||||||
|
|
||||||
private ManualImportItem ProcessFile(string file, string downloadId, string folder = null)
|
private ManualImportItem ProcessFile(string file, string downloadId, string folder = null)
|
||||||
{
|
{
|
||||||
throw new System.NotImplementedException("TODO: This will be rewritten for Music");
|
if (folder.IsNullOrWhiteSpace())
|
||||||
//if (folder.IsNullOrWhiteSpace())
|
{
|
||||||
//{
|
folder = new FileInfo(file).Directory.FullName;
|
||||||
// folder = new FileInfo(file).Directory.FullName;
|
}
|
||||||
//}
|
|
||||||
|
|
||||||
//var relativeFile = folder.GetRelativePath(file);
|
var relativeFile = folder.GetRelativePath(file);
|
||||||
|
|
||||||
//var series = _parsingService.GetSeries(relativeFile.Split('\\', '/')[0]);
|
var artist = _parsingService.GetArtist(relativeFile.Split('\\', '/')[0]);
|
||||||
|
|
||||||
//if (series == null)
|
if (artist == null)
|
||||||
//{
|
{
|
||||||
// series = _parsingService.GetSeries(relativeFile);
|
artist = _parsingService.GetArtistFromTag(file);
|
||||||
//}
|
}
|
||||||
|
|
||||||
//if (series == null && downloadId.IsNotNullOrWhiteSpace())
|
if (artist == null && downloadId.IsNotNullOrWhiteSpace())
|
||||||
//{
|
{
|
||||||
// var trackedDownload = _trackedDownloadService.Find(downloadId);
|
var trackedDownload = _trackedDownloadService.Find(downloadId);
|
||||||
// series = trackedDownload.RemoteEpisode.Series;
|
artist = trackedDownload.RemoteAlbum.Artist;
|
||||||
//}
|
}
|
||||||
|
|
||||||
//if (series == null)
|
if (artist == null)
|
||||||
//{
|
{
|
||||||
// var localEpisode = new LocalEpisode();
|
var localTrack = new LocalTrack();
|
||||||
// localEpisode.Path = file;
|
localTrack.Path = file;
|
||||||
// localEpisode.Quality = QualityParser.ParseQuality(file);
|
localTrack.Quality = QualityParser.ParseQuality(file);
|
||||||
// localEpisode.Size = _diskProvider.GetFileSize(file);
|
localTrack.Size = _diskProvider.GetFileSize(file);
|
||||||
|
|
||||||
// return MapItem(new ImportDecision(localEpisode, new Rejection("Unknown Series")), folder, downloadId);
|
return MapItem(new ImportDecision(localTrack, new Rejection("Unknown Artist")), folder, downloadId);
|
||||||
//}
|
}
|
||||||
|
|
||||||
//var importDecisions = _importDecisionMaker.GetImportDecisions(new List<string> {file},
|
var importDecisions = _importDecisionMaker.GetImportDecisions(new List<string> { file },
|
||||||
// series, null, SceneSource(series, folder));
|
artist, null);
|
||||||
|
|
||||||
//return importDecisions.Any() ? MapItem(importDecisions.First(), folder, downloadId) : null;
|
return importDecisions.Any() ? MapItem(importDecisions.First(), folder, downloadId) : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool SceneSource(Series series, string folder)
|
private bool SceneSource(Artist artist, string folder)
|
||||||
{
|
{
|
||||||
return !(series.Path.PathEquals(folder) || series.Path.IsParentPath(folder));
|
return !(artist.Path.PathEquals(folder) || artist.Path.IsParentPath(folder));
|
||||||
}
|
}
|
||||||
|
|
||||||
private ManualImportItem MapItem(ImportDecision decision, string folder, string downloadId)
|
private ManualImportItem MapItem(ImportDecision decision, string folder, string downloadId)
|
||||||
{
|
{
|
||||||
throw new System.NotImplementedException("TODO: This will be rewritten for Music");
|
var item = new ManualImportItem();
|
||||||
//var item = new ManualImportItem();
|
|
||||||
|
|
||||||
//item.Path = decision.LocalEpisode.Path;
|
item.Path = decision.LocalTrack.Path;
|
||||||
//item.RelativePath = folder.GetRelativePath(decision.LocalEpisode.Path);
|
item.RelativePath = folder.GetRelativePath(decision.LocalTrack.Path);
|
||||||
//item.Name = Path.GetFileNameWithoutExtension(decision.LocalEpisode.Path);
|
item.Name = Path.GetFileNameWithoutExtension(decision.LocalTrack.Path);
|
||||||
//item.DownloadId = downloadId;
|
item.DownloadId = downloadId;
|
||||||
|
|
||||||
//if (decision.LocalEpisode.Series != null)
|
if (decision.LocalTrack.Artist != null)
|
||||||
//{
|
{
|
||||||
// item.Series = decision.LocalEpisode.Series;
|
item.Artist = decision.LocalTrack.Artist;
|
||||||
//}
|
}
|
||||||
|
|
||||||
//if (decision.LocalEpisode.Episodes.Any())
|
if (decision.LocalTrack.Tracks.Any())
|
||||||
//{
|
{
|
||||||
// item.SeasonNumber = decision.LocalEpisode.SeasonNumber;
|
item.Tracks = decision.LocalTrack.Tracks;
|
||||||
// item.Episodes = decision.LocalEpisode.Episodes;
|
}
|
||||||
//}
|
|
||||||
|
|
||||||
//item.Quality = decision.LocalEpisode.Quality;
|
item.Quality = decision.LocalTrack.Quality;
|
||||||
//item.Size = _diskProvider.GetFileSize(decision.LocalEpisode.Path);
|
item.Size = _diskProvider.GetFileSize(decision.LocalTrack.Path);
|
||||||
//item.Rejections = decision.Rejections;
|
item.Rejections = decision.Rejections;
|
||||||
|
|
||||||
//return item;
|
return item;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Execute(ManualImportCommand message)
|
public void Execute(ManualImportCommand message)
|
||||||
{
|
{
|
||||||
_logger.ProgressTrace("Manually importing {0} files using mode {1}", message.Files.Count, message.ImportMode);
|
_logger.ProgressTrace("Manually importing {0} files using mode {1}", message.Files.Count, message.ImportMode);
|
||||||
throw new System.NotImplementedException("TODO: This will be rewritten for Music");
|
|
||||||
|
|
||||||
//var imported = new List<ImportResult>();
|
var imported = new List<ImportResult>();
|
||||||
//var importedTrackedDownload = new List<ManuallyImportedFile>();
|
var importedTrackedDownload = new List<ManuallyImportedFile>();
|
||||||
|
|
||||||
//for (int i = 0; i < message.Files.Count; i++)
|
for (int i = 0; i < message.Files.Count; i++)
|
||||||
//{
|
{
|
||||||
// _logger.ProgressTrace("Processing file {0} of {1}", i + 1, message.Files.Count);
|
_logger.ProgressTrace("Processing file {0} of {1}", i + 1, message.Files.Count);
|
||||||
|
|
||||||
// var file = message.Files[i];
|
var file = message.Files[i];
|
||||||
// var series = _seriesService.GetSeries(file.SeriesId);
|
var artist = _artistService.GetArtist(file.ArtistId);
|
||||||
// var episodes = _episodeService.GetEpisodes(file.EpisodeIds);
|
var album = _albumService.GetAlbum(file.AlbumId);
|
||||||
// var parsedEpisodeInfo = Parser.Parser.ParsePath(file.Path) ?? new ParsedEpisodeInfo();
|
var tracks = _trackService.GetTracks(file.TrackIds);
|
||||||
// var mediaInfo = _videoFileInfoReader.GetMediaInfo(file.Path);
|
var parsedTrackInfo = Parser.Parser.ParseMusicPath(file.Path) ?? new ParsedTrackInfo();
|
||||||
// var existingFile = series.Path.IsParentPath(file.Path);
|
var mediaInfo = _videoFileInfoReader.GetMediaInfo(file.Path);
|
||||||
|
var existingFile = artist.Path.IsParentPath(file.Path);
|
||||||
|
|
||||||
// var localEpisode = new LocalEpisode
|
var localTrack = new LocalTrack
|
||||||
// {
|
{
|
||||||
// ExistingFile = false,
|
ExistingFile = false,
|
||||||
// Episodes = episodes,
|
Tracks = tracks,
|
||||||
// MediaInfo = mediaInfo,
|
MediaInfo = mediaInfo,
|
||||||
// ParsedEpisodeInfo = parsedEpisodeInfo,
|
ParsedTrackInfo = parsedTrackInfo,
|
||||||
// Path = file.Path,
|
Path = file.Path,
|
||||||
// Quality = file.Quality,
|
Quality = file.Quality,
|
||||||
// Series = series,
|
Artist = artist,
|
||||||
// Size = 0
|
Album = album,
|
||||||
// };
|
Size = 0
|
||||||
|
};
|
||||||
|
|
||||||
// //TODO: Cleanup non-tracked downloads
|
//TODO: Cleanup non-tracked downloads
|
||||||
|
|
||||||
// var importDecision = new ImportDecision(localEpisode);
|
var importDecision = new ImportDecision(localTrack);
|
||||||
|
|
||||||
// if (file.DownloadId.IsNullOrWhiteSpace())
|
if (file.DownloadId.IsNullOrWhiteSpace())
|
||||||
// {
|
{
|
||||||
// imported.AddRange(_importApprovedEpisodes.Import(new List<ImportDecision> { importDecision }, !existingFile, null, message.ImportMode));
|
imported.AddRange(_importApprovedTracks.Import(new List<ImportDecision> { importDecision }, !existingFile, null, message.ImportMode));
|
||||||
// }
|
}
|
||||||
|
|
||||||
// else
|
else
|
||||||
// {
|
{
|
||||||
// var trackedDownload = _trackedDownloadService.Find(file.DownloadId);
|
var trackedDownload = _trackedDownloadService.Find(file.DownloadId);
|
||||||
// var importResult = _importApprovedEpisodes.Import(new List<ImportDecision> { importDecision }, true, trackedDownload.DownloadItem, message.ImportMode).First();
|
var importResult = _importApprovedTracks.Import(new List<ImportDecision> { importDecision }, true, trackedDownload.DownloadItem, message.ImportMode).First();
|
||||||
|
|
||||||
// imported.Add(importResult);
|
imported.Add(importResult);
|
||||||
|
|
||||||
// importedTrackedDownload.Add(new ManuallyImportedFile
|
importedTrackedDownload.Add(new ManuallyImportedFile
|
||||||
// {
|
{
|
||||||
// TrackedDownload = trackedDownload,
|
TrackedDownload = trackedDownload,
|
||||||
// ImportResult = importResult
|
ImportResult = importResult
|
||||||
// });
|
});
|
||||||
// }
|
}
|
||||||
//}
|
}
|
||||||
|
|
||||||
//_logger.ProgressTrace("Manually imported {0} files", imported.Count);
|
_logger.ProgressTrace("Manually imported {0} files", imported.Count);
|
||||||
|
|
||||||
//foreach (var groupedTrackedDownload in importedTrackedDownload.GroupBy(i => i.TrackedDownload.DownloadItem.DownloadId).ToList())
|
foreach (var groupedTrackedDownload in importedTrackedDownload.GroupBy(i => i.TrackedDownload.DownloadItem.DownloadId).ToList())
|
||||||
//{
|
{
|
||||||
// var trackedDownload = groupedTrackedDownload.First().TrackedDownload;
|
var trackedDownload = groupedTrackedDownload.First().TrackedDownload;
|
||||||
|
|
||||||
// if (_diskProvider.FolderExists(trackedDownload.DownloadItem.OutputPath.FullPath))
|
if (_diskProvider.FolderExists(trackedDownload.DownloadItem.OutputPath.FullPath))
|
||||||
// {
|
{
|
||||||
// if (_downloadedEpisodesImportService.ShouldDeleteFolder(
|
if (_downloadedEpisodesImportService.ShouldDeleteFolder(
|
||||||
// new DirectoryInfo(trackedDownload.DownloadItem.OutputPath.FullPath),
|
new DirectoryInfo(trackedDownload.DownloadItem.OutputPath.FullPath),
|
||||||
// trackedDownload.RemoteEpisode.Series) && !trackedDownload.DownloadItem.IsReadOnly)
|
trackedDownload.RemoteEpisode.Series) && !trackedDownload.DownloadItem.IsReadOnly)
|
||||||
// {
|
{
|
||||||
// _diskProvider.DeleteFolder(trackedDownload.DownloadItem.OutputPath.FullPath, true);
|
_diskProvider.DeleteFolder(trackedDownload.DownloadItem.OutputPath.FullPath, true);
|
||||||
// }
|
}
|
||||||
// }
|
}
|
||||||
|
|
||||||
// if (groupedTrackedDownload.Select(c => c.ImportResult).Count(c => c.Result == ImportResultType.Imported) >= Math.Max(1, trackedDownload.RemoteEpisode.Episodes.Count))
|
if (groupedTrackedDownload.Select(c => c.ImportResult).Count(c => c.Result == ImportResultType.Imported) >= Math.Max(1, trackedDownload.RemoteAlbum.Albums.Count))
|
||||||
// {
|
{
|
||||||
// trackedDownload.State = TrackedDownloadStage.Imported;
|
trackedDownload.State = TrackedDownloadStage.Imported;
|
||||||
// _eventAggregator.PublishEvent(new DownloadCompletedEvent(trackedDownload));
|
_eventAggregator.PublishEvent(new DownloadCompletedEvent(trackedDownload));
|
||||||
// }
|
}
|
||||||
//}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,6 +15,9 @@ namespace NzbDrone.Core.Parser.Model
|
||||||
public string ArtistTitle { get; set; }
|
public string ArtistTitle { get; set; }
|
||||||
public string AlbumTitle { get; set; }
|
public string AlbumTitle { get; set; }
|
||||||
public ArtistTitleInfo ArtistTitleInfo { get; set; }
|
public ArtistTitleInfo ArtistTitleInfo { get; set; }
|
||||||
|
public string ArtistMBId { get; set; }
|
||||||
|
public string AlbumMBId { get; set; }
|
||||||
|
public string TrackMBId { get; set; }
|
||||||
public QualityModel Quality { get; set; }
|
public QualityModel Quality { get; set; }
|
||||||
public int[] TrackNumbers { get; set; }
|
public int[] TrackNumbers { get; set; }
|
||||||
public Language Language { get; set; }
|
public Language Language { get; set; }
|
||||||
|
@ -26,21 +29,18 @@ namespace NzbDrone.Core.Parser.Model
|
||||||
TrackNumbers = new int[0];
|
TrackNumbers = new int[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
public override string ToString()
|
public override string ToString()
|
||||||
{
|
{
|
||||||
string episodeString = "[Unknown Track]";
|
string trackString = "[Unknown Track]";
|
||||||
|
|
||||||
|
|
||||||
if (TrackNumbers != null && TrackNumbers.Any())
|
if (TrackNumbers != null && TrackNumbers.Any())
|
||||||
{
|
{
|
||||||
episodeString = string.Format("T{0}", string.Join("-", TrackNumbers.Select(c => c.ToString("00"))));
|
trackString = string.Format("T{0}", string.Join("-", TrackNumbers.Select(c => c.ToString("00"))));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
return string.Format("{0} - {1} {2}", ArtistTitle, episodeString, Quality);
|
return string.Format("{0} - {1} {2}", ArtistTitle, trackString, Quality);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -336,9 +336,16 @@ namespace NzbDrone.Core.Parser
|
||||||
var trackName = file.Tag.Title;
|
var trackName = file.Tag.Title;
|
||||||
var trackNumber = file.Tag.Track;
|
var trackNumber = file.Tag.Track;
|
||||||
|
|
||||||
|
var artist = file.Tag.FirstAlbumArtist;
|
||||||
|
|
||||||
|
if (artist.IsNotNullOrWhiteSpace())
|
||||||
|
{
|
||||||
|
artist = file.Tag.FirstPerformer;
|
||||||
|
}
|
||||||
|
|
||||||
var artistTitleInfo = new ArtistTitleInfo
|
var artistTitleInfo = new ArtistTitleInfo
|
||||||
{
|
{
|
||||||
Title = file.Tag.Title,
|
Title = artist,
|
||||||
Year = (int)file.Tag.Year
|
Year = (int)file.Tag.Year
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -348,7 +355,10 @@ namespace NzbDrone.Core.Parser
|
||||||
{
|
{
|
||||||
Language = Language.English, //TODO Parse from Tag/Mediainfo
|
Language = Language.English, //TODO Parse from Tag/Mediainfo
|
||||||
AlbumTitle = file.Tag.Album,
|
AlbumTitle = file.Tag.Album,
|
||||||
ArtistTitle = file.Tag.FirstAlbumArtist,
|
ArtistTitle = artist,
|
||||||
|
ArtistMBId = file.Tag.MusicBrainzArtistId,
|
||||||
|
AlbumMBId = file.Tag.MusicBrainzReleaseId,
|
||||||
|
TrackMBId = file.Tag.MusicBrainzReleaseType,
|
||||||
Quality = QualityParser.ParseQuality(trackName),
|
Quality = QualityParser.ParseQuality(trackName),
|
||||||
TrackNumbers = temp,
|
TrackNumbers = temp,
|
||||||
ArtistTitleInfo = artistTitleInfo,
|
ArtistTitleInfo = artistTitleInfo,
|
||||||
|
|
|
@ -18,6 +18,8 @@ namespace NzbDrone.Core.Parser
|
||||||
LocalEpisode GetLocalEpisode(string filename, Series series);
|
LocalEpisode GetLocalEpisode(string filename, Series series);
|
||||||
LocalEpisode GetLocalEpisode(string filename, Series series, ParsedEpisodeInfo folderInfo, bool sceneSource);
|
LocalEpisode GetLocalEpisode(string filename, Series series, ParsedEpisodeInfo folderInfo, bool sceneSource);
|
||||||
Series GetSeries(string title);
|
Series GetSeries(string title);
|
||||||
|
Artist GetArtist(string title);
|
||||||
|
Artist GetArtistFromTag(string file);
|
||||||
RemoteEpisode Map(ParsedEpisodeInfo parsedEpisodeInfo, int tvdbId, int tvRageId, SearchCriteriaBase searchCriteria = null);
|
RemoteEpisode Map(ParsedEpisodeInfo parsedEpisodeInfo, int tvdbId, int tvRageId, SearchCriteriaBase searchCriteria = null);
|
||||||
RemoteEpisode Map(ParsedEpisodeInfo parsedEpisodeInfo, int seriesId, IEnumerable<int> episodeIds);
|
RemoteEpisode Map(ParsedEpisodeInfo parsedEpisodeInfo, int seriesId, IEnumerable<int> episodeIds);
|
||||||
RemoteAlbum Map(ParsedAlbumInfo parsedAlbumInfo, SearchCriteriaBase searchCriteria = null);
|
RemoteAlbum Map(ParsedAlbumInfo parsedAlbumInfo, SearchCriteriaBase searchCriteria = null);
|
||||||
|
@ -137,6 +139,44 @@ namespace NzbDrone.Core.Parser
|
||||||
return series;
|
return series;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Artist GetArtist(string title)
|
||||||
|
{
|
||||||
|
var parsedAlbumInfo = Parser.ParseAlbumTitle(title);
|
||||||
|
|
||||||
|
if (parsedAlbumInfo == null || parsedAlbumInfo.ArtistName.IsNullOrWhiteSpace())
|
||||||
|
{
|
||||||
|
return _artistService.FindByName(title);
|
||||||
|
}
|
||||||
|
|
||||||
|
return _artistService.FindByName(parsedAlbumInfo.ArtistName);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public Artist GetArtistFromTag(string file)
|
||||||
|
{
|
||||||
|
var parsedTrackInfo = Parser.ParseMusicPath(file);
|
||||||
|
|
||||||
|
var artist = new Artist();
|
||||||
|
|
||||||
|
if (parsedTrackInfo.ArtistMBId.IsNotNullOrWhiteSpace())
|
||||||
|
{
|
||||||
|
artist = _artistService.FindById(parsedTrackInfo.ArtistMBId);
|
||||||
|
|
||||||
|
if (artist != null)
|
||||||
|
{
|
||||||
|
return artist;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (parsedTrackInfo == null || parsedTrackInfo.ArtistTitle.IsNullOrWhiteSpace())
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return _artistService.FindByName(parsedTrackInfo.ArtistTitle);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
[System.Obsolete("Used for sonarr, not lidarr")]
|
[System.Obsolete("Used for sonarr, not lidarr")]
|
||||||
public RemoteEpisode Map(ParsedEpisodeInfo parsedEpisodeInfo, int tvdbId, int tvRageId, SearchCriteriaBase searchCriteria = null)
|
public RemoteEpisode Map(ParsedEpisodeInfo parsedEpisodeInfo, int tvdbId, int tvRageId, SearchCriteriaBase searchCriteria = null)
|
||||||
{
|
{
|
||||||
|
@ -670,10 +710,12 @@ namespace NzbDrone.Core.Parser
|
||||||
}
|
}
|
||||||
|
|
||||||
var tracks = GetTracks(parsedTrackInfo, artist);
|
var tracks = GetTracks(parsedTrackInfo, artist);
|
||||||
|
var album = _albumService.FindByTitle(artist.Id, parsedTrackInfo.AlbumTitle);
|
||||||
|
|
||||||
return new LocalTrack
|
return new LocalTrack
|
||||||
{
|
{
|
||||||
Artist = artist,
|
Artist = artist,
|
||||||
|
Album = album,
|
||||||
Quality = parsedTrackInfo.Quality,
|
Quality = parsedTrackInfo.Quality,
|
||||||
Language = parsedTrackInfo.Language,
|
Language = parsedTrackInfo.Language,
|
||||||
Tracks = tracks,
|
Tracks = tracks,
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue