[UI] Refactor TrackFile Modal

pull/6/head
Qstick 7 years ago
parent e3c6bc3263
commit 9be4ec4c15

@ -156,7 +156,7 @@ function HistoryDetails(props) {
); );
} }
if (eventType === 'episodeFileDeleted') { if (eventType === 'trackFileDeleted') {
const { const {
reason reason
} = data; } = data;
@ -192,7 +192,7 @@ function HistoryDetails(props) {
); );
} }
if (eventType === 'episodeFileRenamed') { if (eventType === 'trackFileRenamed') {
const { const {
sourcePath, sourcePath,
sourceRelativePath, sourceRelativePath,

@ -19,9 +19,9 @@ function getHeaderTitle(eventType) {
return 'Download Failed'; return 'Download Failed';
case 'downloadFolderImported': case 'downloadFolderImported':
return 'Episode Imported'; return 'Episode Imported';
case 'episodeFileDeleted': case 'trackFileDeleted':
return 'Episode File Deleted'; return 'Episode File Deleted';
case 'episodeFileRenamed': case 'trackFileRenamed':
return 'Episode File Renamed'; return 'Episode File Renamed';
default: default:
return 'Unknown'; return 'Unknown';

@ -15,9 +15,9 @@ function getIconName(eventType) {
return icons.DOWNLOADED; return icons.DOWNLOADED;
case 'downloadFailed': case 'downloadFailed':
return icons.DOWNLOADING; return icons.DOWNLOADING;
case 'episodeFileDeleted': case 'trackFileDeleted':
return icons.DELETE; return icons.DELETE;
case 'episodeFileRenamed': case 'trackFileRenamed':
return icons.ORGANIZE; return icons.ORGANIZE;
default: default:
return icons.UNKNOWN; return icons.UNKNOWN;
@ -43,9 +43,9 @@ function getTooltip(eventType, data) {
return 'Album downloaded successfully and picked up from download client'; return 'Album downloaded successfully and picked up from download client';
case 'downloadFailed': case 'downloadFailed':
return 'Album download failed'; return 'Album download failed';
case 'episodeFileDeleted': case 'trackFileDeleted':
return 'Track file deleted'; return 'Track file deleted';
case 'episodeFileRenamed': case 'trackFileRenamed':
return 'Track file renamed'; return 'Track file renamed';
default: default:
return 'Unknown event'; return 'Unknown event';

@ -160,7 +160,7 @@ class QueueRow extends Component {
<EpisodeTitleLink <EpisodeTitleLink
episodeId={episode.id} episodeId={episode.id}
artistId={series.id} artistId={series.id}
episodeFileId={episode.episodeFileId} trackFileId={episode.trackFileId}
episodeEntity={episodeEntity} episodeEntity={episodeEntity}
episodeTitle={episode.title} episodeTitle={episode.title}
showOpenArtistButton={true} showOpenArtistButton={true}

@ -12,8 +12,8 @@ import EpisodeTitleLink from 'Episode/EpisodeTitleLink';
import styles from './AlbumRow.css'; import styles from './AlbumRow.css';
function getEpisodeCountKind(monitored, episodeFileCount, episodeCount) { function getEpisodeCountKind(monitored, trackFileCount, episodeCount) {
if (episodeFileCount === episodeCount && episodeCount > 0) { if (trackFileCount === episodeCount && episodeCount > 0) {
return kinds.SUCCESS; return kinds.SUCCESS;
} }

@ -2,7 +2,7 @@ import _ from 'lodash';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { createSelector } from 'reselect'; import { createSelector } from 'reselect';
import createArtistSelector from 'Store/Selectors/createArtistSelector'; import createArtistSelector from 'Store/Selectors/createArtistSelector';
import createEpisodeFileSelector from 'Store/Selectors/createEpisodeFileSelector'; import createTrackFileSelector from 'Store/Selectors/createTrackFileSelector';
import createCommandsSelector from 'Store/Selectors/createCommandsSelector'; import createCommandsSelector from 'Store/Selectors/createCommandsSelector';
import AlbumRow from './AlbumRow'; import AlbumRow from './AlbumRow';
@ -11,16 +11,16 @@ function createMapStateToProps() {
(state, { id }) => id, (state, { id }) => id,
(state, { sceneSeasonNumber }) => sceneSeasonNumber, (state, { sceneSeasonNumber }) => sceneSeasonNumber,
createArtistSelector(), createArtistSelector(),
createEpisodeFileSelector(), createTrackFileSelector(),
createCommandsSelector(), createCommandsSelector(),
(id, sceneSeasonNumber, series, episodeFile, commands) => { (id, sceneSeasonNumber, series, trackFile, commands) => {
const alternateTitles = sceneSeasonNumber ? _.filter(series.alternateTitles, { sceneSeasonNumber }) : []; const alternateTitles = sceneSeasonNumber ? _.filter(series.alternateTitles, { sceneSeasonNumber }) : [];
return { return {
artistMonitored: series.monitored, artistMonitored: series.monitored,
seriesType: series.seriesType, seriesType: series.seriesType,
episodeFilePath: episodeFile ? episodeFile.path : null, trackFilePath: trackFile ? trackFile.path : null,
episodeFileRelativePath: episodeFile ? episodeFile.relativePath : null, trackFileRelativePath: trackFile ? trackFile.relativePath : null,
alternateTitles alternateTitles
}; };
} }

@ -18,7 +18,7 @@ import PageToolbarSeparator from 'Components/Page/Toolbar/PageToolbarSeparator';
import PageToolbarButton from 'Components/Page/Toolbar/PageToolbarButton'; import PageToolbarButton from 'Components/Page/Toolbar/PageToolbarButton';
import Popover from 'Components/Tooltip/Popover'; import Popover from 'Components/Tooltip/Popover';
import Tooltip from 'Components/Tooltip/Tooltip'; import Tooltip from 'Components/Tooltip/Tooltip';
import EpisodeFileEditorModal from 'EpisodeFile/Editor/EpisodeFileEditorModal'; import TrackFileEditorModal from 'TrackFile/Editor/TrackFileEditorModal';
import OrganizePreviewModalConnector from 'Organize/OrganizePreviewModalConnector'; import OrganizePreviewModalConnector from 'Organize/OrganizePreviewModalConnector';
import QualityProfileNameConnector from 'Settings/Profiles/Quality/QualityProfileNameConnector'; import QualityProfileNameConnector from 'Settings/Profiles/Quality/QualityProfileNameConnector';
import ArtistPoster from 'Artist/ArtistPoster'; import ArtistPoster from 'Artist/ArtistPoster';
@ -179,7 +179,7 @@ class ArtistDetails extends Component {
isFetching, isFetching,
isPopulated, isPopulated,
episodesError, episodesError,
episodeFilesError, trackFilesError,
previousArtist, previousArtist,
nextArtist, nextArtist,
onRefreshPress, onRefreshPress,
@ -198,12 +198,12 @@ class ArtistDetails extends Component {
const continuing = status === 'continuing'; const continuing = status === 'continuing';
let episodeFilesCountMessage = 'No track files'; let trackFilesCountMessage = 'No track files';
if (trackFileCount === 1) { if (trackFileCount === 1) {
episodeFilesCountMessage = '1 track file'; trackFilesCountMessage = '1 track file';
} else if (trackFileCount > 1) { } else if (trackFileCount > 1) {
episodeFilesCountMessage = `${trackFileCount} track files`; trackFilesCountMessage = `${trackFileCount} track files`;
} }
let expandIcon = icons.EXPAND_INDETERMINATE; let expandIcon = icons.EXPAND_INDETERMINATE;
@ -345,7 +345,7 @@ class ArtistDetails extends Component {
<div className={styles.detailsLabels}> <div className={styles.detailsLabels}>
<Label <Label
className={styles.detailsLabel} className={styles.detailsLabel}
title={episodeFilesCountMessage} title={trackFilesCountMessage}
size={sizes.LARGE} size={sizes.LARGE}
> >
<Icon <Icon
@ -469,7 +469,7 @@ class ArtistDetails extends Component {
<div className={styles.contentContainer}> <div className={styles.contentContainer}>
{ {
!isPopulated && !episodesError && !episodeFilesError && !isPopulated && !episodesError && !trackFilesError &&
<LoadingIndicator /> <LoadingIndicator />
} }
@ -479,7 +479,7 @@ class ArtistDetails extends Component {
} }
{ {
!isFetching && episodeFilesError && !isFetching && trackFilesError &&
<div>Loading episode files failed</div> <div>Loading episode files failed</div>
} }
@ -518,7 +518,7 @@ class ArtistDetails extends Component {
onModalClose={this.onOrganizeModalClose} onModalClose={this.onOrganizeModalClose}
/> />
<EpisodeFileEditorModal <TrackFileEditorModal
isOpen={isManageEpisodesOpen} isOpen={isManageEpisodesOpen}
artistId={id} artistId={id}
onModalClose={this.onManageEpisodesModalClose} onModalClose={this.onManageEpisodesModalClose}
@ -563,7 +563,7 @@ ArtistDetails.propTypes = {
isFetching: PropTypes.bool.isRequired, isFetching: PropTypes.bool.isRequired,
isPopulated: PropTypes.bool.isRequired, isPopulated: PropTypes.bool.isRequired,
episodesError: PropTypes.object, episodesError: PropTypes.object,
episodeFilesError: PropTypes.object, trackFilesError: PropTypes.object,
previousArtist: PropTypes.object.isRequired, previousArtist: PropTypes.object.isRequired,
nextArtist: PropTypes.object.isRequired, nextArtist: PropTypes.object.isRequired,
onRefreshPress: PropTypes.func.isRequired, onRefreshPress: PropTypes.func.isRequired,

@ -7,7 +7,7 @@ import { findCommand } from 'Utilities/Command';
import createAllArtistSelector from 'Store/Selectors/createAllArtistSelector'; import createAllArtistSelector from 'Store/Selectors/createAllArtistSelector';
import createCommandsSelector from 'Store/Selectors/createCommandsSelector'; import createCommandsSelector from 'Store/Selectors/createCommandsSelector';
import { fetchEpisodes, clearEpisodes } from 'Store/Actions/episodeActions'; import { fetchEpisodes, clearEpisodes } from 'Store/Actions/episodeActions';
import { fetchEpisodeFiles, clearEpisodeFiles } from 'Store/Actions/episodeFileActions'; import { fetchTrackFiles, clearTrackFiles } from 'Store/Actions/trackFileActions';
import { fetchQueueDetails, clearQueueDetails } from 'Store/Actions/queueActions'; import { fetchQueueDetails, clearQueueDetails } from 'Store/Actions/queueActions';
import { executeCommand } from 'Store/Actions/commandActions'; import { executeCommand } from 'Store/Actions/commandActions';
import * as commandNames from 'Commands/commandNames'; import * as commandNames from 'Commands/commandNames';
@ -17,10 +17,10 @@ function createMapStateToProps() {
return createSelector( return createSelector(
(state, { nameSlug }) => nameSlug, (state, { nameSlug }) => nameSlug,
(state) => state.episodes, (state) => state.episodes,
(state) => state.episodeFiles, (state) => state.trackFiles,
createAllArtistSelector(), createAllArtistSelector(),
createCommandsSelector(), createCommandsSelector(),
(nameSlug, episodes, episodeFiles, allArtists, commands) => { (nameSlug, episodes, trackFiles, allArtists, commands) => {
const sortedArtist = _.orderBy(allArtists, 'sortName'); const sortedArtist = _.orderBy(allArtists, 'sortName');
const artistIndex = _.findIndex(sortedArtist, { nameSlug }); const artistIndex = _.findIndex(sortedArtist, { nameSlug });
const series = sortedArtist[artistIndex]; const series = sortedArtist[artistIndex];
@ -39,10 +39,10 @@ function createMapStateToProps() {
const isRenamingArtistCommand = findCommand(commands, { name: commandNames.RENAME_ARTIST }); const isRenamingArtistCommand = findCommand(commands, { name: commandNames.RENAME_ARTIST });
const isRenamingArtist = !!(isRenamingArtistCommand && isRenamingArtistCommand.body.artistId.indexOf(series.id) > -1); const isRenamingArtist = !!(isRenamingArtistCommand && isRenamingArtistCommand.body.artistId.indexOf(series.id) > -1);
const isFetching = episodes.isFetching || episodeFiles.isFetching; const isFetching = episodes.isFetching || trackFiles.isFetching;
const isPopulated = episodes.isPopulated && episodeFiles.isPopulated; const isPopulated = episodes.isPopulated && trackFiles.isPopulated;
const episodesError = episodes.error; const episodesError = episodes.error;
const episodeFilesError = episodeFiles.error; const trackFilesError = trackFiles.error;
const alternateTitles = _.reduce(series.alternateTitles, (acc, alternateTitle) => { const alternateTitles = _.reduce(series.alternateTitles, (acc, alternateTitle) => {
if ((alternateTitle.seasonNumber === -1 || alternateTitle.seasonNumber === undefined) && if ((alternateTitle.seasonNumber === -1 || alternateTitle.seasonNumber === undefined) &&
(alternateTitle.sceneSeasonNumber === -1 || alternateTitle.sceneSeasonNumber === undefined)) { (alternateTitle.sceneSeasonNumber === -1 || alternateTitle.sceneSeasonNumber === undefined)) {
@ -62,7 +62,7 @@ function createMapStateToProps() {
isFetching, isFetching,
isPopulated, isPopulated,
episodesError, episodesError,
episodeFilesError, trackFilesError,
previousArtist, previousArtist,
nextArtist nextArtist
}; };
@ -73,8 +73,8 @@ function createMapStateToProps() {
const mapDispatchToProps = { const mapDispatchToProps = {
fetchEpisodes, fetchEpisodes,
clearEpisodes, clearEpisodes,
fetchEpisodeFiles, fetchTrackFiles,
clearEpisodeFiles, clearTrackFiles,
fetchQueueDetails, fetchQueueDetails,
clearQueueDetails, clearQueueDetails,
executeCommand executeCommand
@ -125,13 +125,13 @@ class ArtistDetailsConnector extends Component {
const artistId = this.props.id; const artistId = this.props.id;
this.props.fetchEpisodes({ artistId }); this.props.fetchEpisodes({ artistId });
this.props.fetchEpisodeFiles({ artistId }); this.props.fetchTrackFiles({ artistId });
this.props.fetchQueueDetails({ artistId }); this.props.fetchQueueDetails({ artistId });
} }
_unpopulate() { _unpopulate() {
this.props.clearEpisodes(); this.props.clearEpisodes();
this.props.clearEpisodeFiles(); this.props.clearTrackFiles();
this.props.clearQueueDetails(); this.props.clearQueueDetails();
} }
@ -174,8 +174,8 @@ ArtistDetailsConnector.propTypes = {
isRenamingArtist: PropTypes.bool.isRequired, isRenamingArtist: PropTypes.bool.isRequired,
fetchEpisodes: PropTypes.func.isRequired, fetchEpisodes: PropTypes.func.isRequired,
clearEpisodes: PropTypes.func.isRequired, clearEpisodes: PropTypes.func.isRequired,
fetchEpisodeFiles: PropTypes.func.isRequired, fetchTrackFiles: PropTypes.func.isRequired,
clearEpisodeFiles: PropTypes.func.isRequired, clearTrackFiles: PropTypes.func.isRequired,
fetchQueueDetails: PropTypes.func.isRequired, fetchQueueDetails: PropTypes.func.isRequired,
clearQueueDetails: PropTypes.func.isRequired, clearQueueDetails: PropTypes.func.isRequired,
executeCommand: PropTypes.func.isRequired executeCommand: PropTypes.func.isRequired

@ -16,7 +16,7 @@ import MenuContent from 'Components/Menu/MenuContent';
import MenuItem from 'Components/Menu/MenuItem'; import MenuItem from 'Components/Menu/MenuItem';
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 EpisodeFileEditorModal from 'EpisodeFile/Editor/EpisodeFileEditorModal'; import TrackFileEditorModal from 'TrackFile/Editor/TrackFileEditorModal';
import OrganizePreviewModalConnector from 'Organize/OrganizePreviewModalConnector'; import OrganizePreviewModalConnector from 'Organize/OrganizePreviewModalConnector';
import AlbumRowConnector from './AlbumRowConnector'; import AlbumRowConnector from './AlbumRowConnector';
import styles from './ArtistDetailsSeason.css'; import styles from './ArtistDetailsSeason.css';
@ -287,7 +287,7 @@ class ArtistDetailsSeason extends Component {
onModalClose={this.onOrganizeModalClose} onModalClose={this.onOrganizeModalClose}
/> />
<EpisodeFileEditorModal <TrackFileEditorModal
isOpen={isManageEpisodesOpen} isOpen={isManageEpisodesOpen}
artistId={artistId} artistId={artistId}
onModalClose={this.onManageEpisodesModalClose} onModalClose={this.onManageEpisodesModalClose}

@ -5,7 +5,7 @@ import { createSelector } from 'reselect';
import hasDifferentItems from 'Utilities/Object/hasDifferentItems'; import hasDifferentItems from 'Utilities/Object/hasDifferentItems';
import selectUniqueIds from 'Utilities/Object/selectUniqueIds'; import selectUniqueIds from 'Utilities/Object/selectUniqueIds';
import * as calendarActions from 'Store/Actions/calendarActions'; import * as calendarActions from 'Store/Actions/calendarActions';
import { fetchEpisodeFiles, clearEpisodeFiles } from 'Store/Actions/episodeFileActions'; import { fetchTrackFiles, clearTrackFiles } from 'Store/Actions/trackFileActions';
import { fetchQueueDetails, clearQueueDetails } from 'Store/Actions/queueActions'; import { fetchQueueDetails, clearQueueDetails } from 'Store/Actions/queueActions';
import Calendar from './Calendar'; import Calendar from './Calendar';
@ -22,8 +22,8 @@ function createMapStateToProps() {
const mapDispatchToProps = { const mapDispatchToProps = {
...calendarActions, ...calendarActions,
fetchEpisodeFiles, fetchTrackFiles,
clearEpisodeFiles, clearTrackFiles,
fetchQueueDetails, fetchQueueDetails,
clearQueueDetails clearQueueDetails
}; };
@ -52,12 +52,12 @@ class CalendarConnector extends Component {
if (hasDifferentItems(prevProps.items, items)) { if (hasDifferentItems(prevProps.items, items)) {
const albumIds = selectUniqueIds(items, 'id'); const albumIds = selectUniqueIds(items, 'id');
// const episodeFileIds = selectUniqueIds(items, 'episodeFileId'); // const trackFileIds = selectUniqueIds(items, 'trackFileId');
this.props.fetchQueueDetails({ albumIds }); this.props.fetchQueueDetails({ albumIds });
// if (episodeFileIds.length) { // if (trackFileIds.length) {
// this.props.fetchEpisodeFiles({ episodeFileIds }); // this.props.fetchTrackFiles({ trackFileIds });
// } // }
} }
@ -69,7 +69,7 @@ class CalendarConnector extends Component {
componentWillUnmount() { componentWillUnmount() {
this.props.clearCalendar(); this.props.clearCalendar();
this.props.clearQueueDetails(); this.props.clearQueueDetails();
this.props.clearEpisodeFiles(); this.props.clearTrackFiles();
this.clearUpdateTimeout(); this.clearUpdateTimeout();
} }
@ -136,8 +136,8 @@ CalendarConnector.propTypes = {
gotoCalendarPreviousRange: PropTypes.func.isRequired, gotoCalendarPreviousRange: PropTypes.func.isRequired,
gotoCalendarNextRange: PropTypes.func.isRequired, gotoCalendarNextRange: PropTypes.func.isRequired,
clearCalendar: PropTypes.func.isRequired, clearCalendar: PropTypes.func.isRequired,
fetchEpisodeFiles: PropTypes.func.isRequired, fetchTrackFiles: PropTypes.func.isRequired,
clearEpisodeFiles: PropTypes.func.isRequired, clearTrackFiles: PropTypes.func.isRequired,
fetchQueueDetails: PropTypes.func.isRequired, fetchQueueDetails: PropTypes.func.isRequired,
clearQueueDetails: PropTypes.func.isRequired clearQueueDetails: PropTypes.func.isRequired
}; };

@ -6,7 +6,7 @@ export const CLEAR_LOGS = 'ClearLog';
export const CUTOFF_UNMET_EPISODE_SEARCH = 'CutoffUnmetEpisodeSearch'; export const CUTOFF_UNMET_EPISODE_SEARCH = 'CutoffUnmetEpisodeSearch';
export const DELETE_LOG_FILES = 'DeleteLogFiles'; export const DELETE_LOG_FILES = 'DeleteLogFiles';
export const DELETE_UPDATE_LOG_FILES = 'DeleteUpdateLogFiles'; export const DELETE_UPDATE_LOG_FILES = 'DeleteUpdateLogFiles';
export const DOWNLOADED_EPSIODES_SCAN = 'DownloadedEpisodesScan'; export const DOWNLOADED_ALBUMS_SCAN = 'DownloadedAlbumsScan';
export const EPISODE_SEARCH = 'AlbumSearch'; export const EPISODE_SEARCH = 'AlbumSearch';
export const INTERACTIVE_IMPORT = 'ManualImport'; export const INTERACTIVE_IMPORT = 'ManualImport';
export const MISSING_ALBUM_SEARCH = 'MissingAlbumSearch'; export const MISSING_ALBUM_SEARCH = 'MissingAlbumSearch';

@ -120,7 +120,7 @@ class SignalRConnector extends Component {
} }
if (name === 'episodefile') { if (name === 'episodefile') {
this.handleEpisodeFile(body); this.handleTrackFile(body);
return; return;
} }
@ -206,10 +206,10 @@ class SignalRConnector extends Component {
} }
} }
handleEpisodeFile = (body) => { handleTrackFile = (body) => {
if (body.action === 'updated') { if (body.action === 'updated') {
this.props.updateItem({ this.props.updateItem({
section: 'episodeFiles', section: 'trackFiles',
...body.resource }); ...body.resource });
} }
} }

@ -8,7 +8,7 @@ import createEpisodeSelector from 'Store/Selectors/createEpisodeSelector';
import createArtistSelector from 'Store/Selectors/createArtistSelector'; import createArtistSelector from 'Store/Selectors/createArtistSelector';
import episodeEntities from 'Episode/episodeEntities'; import episodeEntities from 'Episode/episodeEntities';
import { fetchTracks, clearTracks } from 'Store/Actions/trackActions'; import { fetchTracks, clearTracks } from 'Store/Actions/trackActions';
import { fetchEpisodeFiles, clearEpisodeFiles } from 'Store/Actions/episodeFileActions'; import { fetchTrackFiles, clearTrackFiles } from 'Store/Actions/trackFileActions';
import EpisodeDetailsModalContent from './EpisodeDetailsModalContent'; import EpisodeDetailsModalContent from './EpisodeDetailsModalContent';
function createMapStateToProps() { function createMapStateToProps() {
@ -38,8 +38,8 @@ const mapDispatchToProps = {
clearReleases, clearReleases,
fetchTracks, fetchTracks,
clearTracks, clearTracks,
fetchEpisodeFiles, fetchTrackFiles,
clearEpisodeFiles, clearTrackFiles,
toggleEpisodeMonitored toggleEpisodeMonitored
}; };
@ -65,15 +65,14 @@ class EpisodeDetailsModalContentConnector extends Component {
const artistId = this.props.artistId; const artistId = this.props.artistId;
const albumId = this.props.episodeId; const albumId = this.props.episodeId;
this.props.fetchTracks({ artistId, albumId }); this.props.fetchTracks({ artistId, albumId });
// this.props.fetchEpisodeFiles({ artistId, albumId }); // this.props.fetchTrackFiles({ artistId, albumId });
} }
_unpopulate() { _unpopulate() {
this.props.clearTracks(); this.props.clearTracks();
// this.props.clearEpisodeFiles(); // this.props.clearTrackFiles();
} }
// //
// Listeners // Listeners
@ -109,8 +108,8 @@ EpisodeDetailsModalContentConnector.propTypes = {
artistId: PropTypes.number.isRequired, artistId: PropTypes.number.isRequired,
fetchTracks: PropTypes.func.isRequired, fetchTracks: PropTypes.func.isRequired,
clearTracks: PropTypes.func.isRequired, clearTracks: PropTypes.func.isRequired,
fetchEpisodeFiles: PropTypes.func.isRequired, fetchTrackFiles: PropTypes.func.isRequired,
clearEpisodeFiles: PropTypes.func.isRequired, clearTrackFiles: PropTypes.func.isRequired,
clearReleases: PropTypes.func.isRequired, clearReleases: PropTypes.func.isRequired,
toggleEpisodeMonitored: PropTypes.func.isRequired toggleEpisodeMonitored: PropTypes.func.isRequired
}; };

@ -14,10 +14,10 @@ function EpisodeStatus(props) {
monitored, monitored,
grabbed, grabbed,
queueItem, queueItem,
episodeFile trackFile
} = props; } = props;
const hasEpisodeFile = !!episodeFile; const hasTrackFile = !!trackFile;
const isQueued = !!queueItem; const isQueued = !!queueItem;
const hasAired = isBefore(airDateUtc); const hasAired = isBefore(airDateUtc);
@ -57,15 +57,15 @@ function EpisodeStatus(props) {
); );
} }
if (hasEpisodeFile) { if (hasTrackFile) {
const quality = episodeFile.quality; const quality = trackFile.quality;
const isCutoffNotMet = episodeFile.qualityCutoffNotMet; const isCutoffNotMet = trackFile.qualityCutoffNotMet;
return ( return (
<div className={styles.center}> <div className={styles.center}>
<EpisodeQuality <EpisodeQuality
quality={quality} quality={quality}
size={episodeFile.size} size={trackFile.size}
isCutoffNotMet={isCutoffNotMet} isCutoffNotMet={isCutoffNotMet}
title="Episode Downloaded" title="Episode Downloaded"
/> />
@ -121,7 +121,7 @@ EpisodeStatus.propTypes = {
monitored: PropTypes.bool, monitored: PropTypes.bool,
grabbed: PropTypes.bool, grabbed: PropTypes.bool,
queueItem: PropTypes.object, queueItem: PropTypes.object,
episodeFile: PropTypes.object trackFile: PropTypes.object
}; };
export default EpisodeStatus; export default EpisodeStatus;

@ -5,15 +5,15 @@ import { connect } from 'react-redux';
import { createSelector } from 'reselect'; import { createSelector } from 'reselect';
import createEpisodeSelector from 'Store/Selectors/createEpisodeSelector'; import createEpisodeSelector from 'Store/Selectors/createEpisodeSelector';
import createQueueItemSelector from 'Store/Selectors/createQueueItemSelector'; import createQueueItemSelector from 'Store/Selectors/createQueueItemSelector';
import createEpisodeFileSelector from 'Store/Selectors/createEpisodeFileSelector'; import createTrackFileSelector from 'Store/Selectors/createTrackFileSelector';
import EpisodeStatus from './EpisodeStatus'; import EpisodeStatus from './EpisodeStatus';
function createMapStateToProps() { function createMapStateToProps() {
return createSelector( return createSelector(
createEpisodeSelector(), createEpisodeSelector(),
createQueueItemSelector(), createQueueItemSelector(),
createEpisodeFileSelector(), createTrackFileSelector(),
(episode, queueItem, episodeFile) => { (episode, queueItem, trackFile) => {
const result = _.pick(episode, [ const result = _.pick(episode, [
'airDateUtc', 'airDateUtc',
'monitored', 'monitored',
@ -21,7 +21,7 @@ function createMapStateToProps() {
]); ]);
result.queueItem = queueItem; result.queueItem = queueItem;
result.episodeFile = episodeFile; result.trackFile = trackFile;
return result; return result;
} }
@ -47,7 +47,7 @@ class EpisodeStatusConnector extends Component {
EpisodeStatusConnector.propTypes = { EpisodeStatusConnector.propTypes = {
episodeId: PropTypes.number.isRequired, episodeId: PropTypes.number.isRequired,
episodeFileId: PropTypes.number.isRequired trackFileId: PropTypes.number.isRequired
}; };
export default connect(createMapStateToProps, mapDispatchToProps)(EpisodeStatusConnector); export default connect(createMapStateToProps, mapDispatchToProps)(EpisodeStatusConnector);

@ -22,24 +22,24 @@ class EpisodeSummary extends Component {
super(props, context); super(props, context);
this.state = { this.state = {
isRemoveEpisodeFileModalOpen: false isRemoveTrackFileModalOpen: false
}; };
} }
// //
// Listeners // Listeners
onRemoveEpisodeFilePress = () => { onRemoveTrackFilePress = () => {
this.setState({ isRemoveEpisodeFileModalOpen: true }); this.setState({ isRemoveTrackFileModalOpen: true });
} }
onConfirmRemoveEpisodeFile = () => { onConfirmRemoveTrackFile = () => {
this.props.onDeleteEpisodeFile(); this.props.onDeleteTrackFile();
this.setState({ isRemoveEpisodeFileModalOpen: false }); this.setState({ isRemoveTrackFileModalOpen: false });
} }
onRemoveEpisodeFileModalClose = () => { onRemoveTrackFileModalClose = () => {
this.setState({ isRemoveEpisodeFileModalOpen: false }); this.setState({ isRemoveTrackFileModalOpen: false });
} }
// //
@ -125,13 +125,13 @@ class EpisodeSummary extends Component {
</div> </div>
<ConfirmModal <ConfirmModal
isOpen={this.state.isRemoveEpisodeFileModalOpen} isOpen={this.state.isRemoveTrackFileModalOpen}
kind={kinds.DANGER} kind={kinds.DANGER}
title="Delete Episode File" title="Delete Episode File"
message={`Are you sure you want to delete '${path}'?`} message={`Are you sure you want to delete '${path}'?`}
confirmLabel="Delete" confirmLabel="Delete"
onConfirm={this.onConfirmRemoveEpisodeFile} onConfirm={this.onConfirmRemoveTrackFile}
onCancel={this.onRemoveEpisodeFileModalClose} onCancel={this.onRemoveTrackFileModalClose}
/> />
</div> </div>
); );
@ -149,7 +149,7 @@ EpisodeSummary.propTypes = {
size: PropTypes.number, size: PropTypes.number,
quality: PropTypes.object, quality: PropTypes.object,
qualityCutoffNotMet: PropTypes.bool, qualityCutoffNotMet: PropTypes.bool,
onDeleteEpisodeFile: PropTypes.func.isRequired onDeleteTrackFile: PropTypes.func.isRequired
}; };
export default EpisodeSummary; export default EpisodeSummary;

@ -1,9 +1,8 @@
import _ from 'lodash';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { createSelector } from 'reselect'; import { createSelector } from 'reselect';
import { deleteEpisodeFile } from 'Store/Actions/episodeFileActions'; import { deleteTrackFile } from 'Store/Actions/trackFileActions';
import createEpisodeSelector from 'Store/Selectors/createEpisodeSelector'; import createEpisodeSelector from 'Store/Selectors/createEpisodeSelector';
import createTrackSelector from 'Store/Selectors/createTrackSelector';
import createEpisodeFileSelector from 'Store/Selectors/createEpisodeFileSelector';
import createDimensionsSelector from 'Store/Selectors/createDimensionsSelector'; import createDimensionsSelector from 'Store/Selectors/createDimensionsSelector';
import createArtistSelector from 'Store/Selectors/createArtistSelector'; import createArtistSelector from 'Store/Selectors/createArtistSelector';
import createCommandsSelector from 'Store/Selectors/createCommandsSelector'; import createCommandsSelector from 'Store/Selectors/createCommandsSelector';
@ -11,18 +10,19 @@ import EpisodeSummary from './EpisodeSummary';
function createMapStateToProps() { function createMapStateToProps() {
return createSelector( return createSelector(
(state, { episode }) => episode,
(state) => state.tracks, (state) => state.tracks,
createEpisodeSelector(), createEpisodeSelector(),
createCommandsSelector(), createCommandsSelector(),
createDimensionsSelector(), createDimensionsSelector(),
(albumId, tracks, episode, commands, dimensions) => { (tracks, episode, commands, dimensions) => {
const items = _.filter(tracks.items, { albumId: episode.id });
return { return {
network: episode.label, network: episode.label,
qualityProfileId: episode.profileId, qualityProfileId: episode.profileId,
airDateUtc: episode.releaseDate, airDateUtc: episode.releaseDate,
overview: episode.overview, overview: episode.overview,
items: tracks.items, items,
columns: tracks.columns columns: tracks.columns
}; };
} }
@ -31,9 +31,9 @@ function createMapStateToProps() {
function createMapDispatchToProps(dispatch, props) { function createMapDispatchToProps(dispatch, props) {
return { return {
onDeleteEpisodeFile() { onDeleteTrackFile() {
dispatch(deleteEpisodeFile({ dispatch(deleteTrackFile({
id: props.episodeFileId, id: props.trackFileId,
episodeEntity: props.episodeEntity episodeEntity: props.episodeEntity
})); }));
} }

@ -3,8 +3,8 @@ import React, { Component } from 'react';
import TableRow from 'Components/Table/TableRow'; import TableRow from 'Components/Table/TableRow';
import TableRowCell from 'Components/Table/Cells/TableRowCell'; import TableRowCell from 'Components/Table/Cells/TableRowCell';
import formatTimeSpan from 'Utilities/Date/formatTimeSpan'; import formatTimeSpan from 'Utilities/Date/formatTimeSpan';
import MediaInfoConnector from 'EpisodeFile/MediaInfoConnector'; import MediaInfoConnector from 'TrackFile/MediaInfoConnector';
import * as mediaInfoTypes from 'EpisodeFile/mediaInfoTypes'; import * as mediaInfoTypes from 'TrackFile/mediaInfoTypes';
import EpisodeStatusConnector from 'Episode/EpisodeStatusConnector'; import EpisodeStatusConnector from 'Episode/EpisodeStatusConnector';
import styles from './TrackDetailRow.css'; import styles from './TrackDetailRow.css';
@ -83,7 +83,7 @@ class TrackDetailRow extends Component {
> >
<MediaInfoConnector <MediaInfoConnector
type={mediaInfoTypes.AUDIO} type={mediaInfoTypes.AUDIO}
episodeFileId={trackFileId} trackFileId={trackFileId}
/> />
</TableRowCell> </TableRowCell>
); );
@ -97,7 +97,7 @@ class TrackDetailRow extends Component {
> >
<EpisodeStatusConnector <EpisodeStatusConnector
episodeId={id} episodeId={id}
episodeFileId={trackFileId} trackFileId={trackFileId}
/> />
</TableRowCell> </TableRowCell>
); );

@ -32,7 +32,7 @@ class InteractiveImportSelectFolderModalContentConnector extends Component {
this.props.addRecentFolder({ folder }); this.props.addRecentFolder({ folder });
this.props.executeCommand({ this.props.executeCommand({
name: commandNames.DOWNLOADED_EPSIODES_SCAN, name: commandNames.DOWNLOADED_ALBUMS_SCAN,
path: folder path: folder
}); });

@ -236,7 +236,7 @@ class InteractiveImportModalContent extends Component {
{ {
isPopulated && !items.length && isPopulated && !items.length &&
'No video files were found in the selected folder' 'No audio files were found in the selected folder'
} }
</ModalBody> </ModalBody>

@ -95,11 +95,11 @@ export const CLEAR_TRACKS = 'CLEAR_TRACKS';
// //
// Episode Files // Episode Files
export const FETCH_EPISODE_FILES = 'FETCH_EPISODE_FILES'; export const FETCH_TRACK_FILES = 'FETCH_TRACK_FILES';
export const CLEAR_EPISODE_FILES = 'CLEAR_EPISODE_FILES'; export const CLEAR_TRACK_FILES = 'CLEAR_TRACK_FILES';
export const DELETE_EPISODE_FILE = 'DELETE_EPISODE_FILE'; export const DELETE_TRACK_FILE = 'DELETE_TRACK_FILE';
export const DELETE_EPISODE_FILES = 'DELETE_EPISODE_FILES'; export const DELETE_TRACK_FILES = 'DELETE_TRACK_FILES';
export const UPDATE_EPISODE_FILES = 'UPDATE_EPISODE_FILES'; export const UPDATE_TRACK_FILES = 'UPDATE_TRACK_FILES';
// //
// Episode History // Episode History

@ -1,9 +0,0 @@
import { createAction } from 'redux-actions';
import * as types from './actionTypes';
import episodeFileActionHandlers from './episodeFileActionHandlers';
export const fetchEpisodeFiles = episodeFileActionHandlers[types.FETCH_EPISODE_FILES];
export const deleteEpisodeFile = episodeFileActionHandlers[types.DELETE_EPISODE_FILE];
export const deleteEpisodeFiles = episodeFileActionHandlers[types.DELETE_EPISODE_FILES];
export const updateEpisodeFiles = episodeFileActionHandlers[types.UPDATE_EPISODE_FILES];
export const clearEpisodeFiles = createAction(types.CLEAR_EPISODE_FILES);

@ -7,33 +7,33 @@ import createRemoveItemHandler from './Creators/createRemoveItemHandler';
import * as types from './actionTypes'; import * as types from './actionTypes';
import { set, removeItem, updateItem } from './baseActions'; import { set, removeItem, updateItem } from './baseActions';
const section = 'episodeFiles'; const section = 'trackFiles';
const deleteEpisodeFile = createRemoveItemHandler(section, '/trackFile'); const deleteTrackFile = createRemoveItemHandler(section, '/trackFile');
const episodeFileActionHandlers = { const trackFileActionHandlers = {
[types.FETCH_EPISODE_FILES]: createFetchHandler(section, '/trackFile'), [types.FETCH_TRACK_FILES]: createFetchHandler(section, '/trackFile'),
[types.DELETE_EPISODE_FILE]: function(payload) { [types.DELETE_TRACK_FILE]: function(payload) {
return function(dispatch, getState) { return function(dispatch, getState) {
const { const {
id: episodeFileId, id: trackFileId,
episodeEntity = episodeEntities.EPISODES episodeEntity = episodeEntities.EPISODES
} = payload; } = payload;
const episodeSection = _.last(episodeEntity.split('.')); const episodeSection = _.last(episodeEntity.split('.'));
const deletePromise = deleteEpisodeFile(payload)(dispatch, getState); const deletePromise = deleteTrackFile(payload)(dispatch, getState);
deletePromise.done(() => { deletePromise.done(() => {
const episodes = getState().episodes.items; const episodes = getState().episodes.items;
const episodesWithRemovedFiles = _.filter(episodes, { episodeFileId }); const episodesWithRemovedFiles = _.filter(episodes, { trackFileId });
dispatch(batchActions([ dispatch(batchActions([
...episodesWithRemovedFiles.map((episode) => { ...episodesWithRemovedFiles.map((episode) => {
return updateItem({ return updateItem({
section: episodeSection, section: episodeSection,
...episode, ...episode,
episodeFileId: 0, trackFileId: 0,
hasFile: false hasFile: false
}); });
}) })
@ -42,31 +42,31 @@ const episodeFileActionHandlers = {
}; };
}, },
[types.DELETE_EPISODE_FILES]: function(payload) { [types.DELETE_TRACK_FILES]: function(payload) {
return function(dispatch, getState) { return function(dispatch, getState) {
const { const {
episodeFileIds trackFileIds
} = payload; } = payload;
dispatch(set({ section, isDeleting: true })); dispatch(set({ section, isDeleting: true }));
const promise = $.ajax({ const promise = $.ajax({
url: '/episodeFile/bulk', url: '/trackFile/bulk',
method: 'DELETE', method: 'DELETE',
dataType: 'json', dataType: 'json',
data: JSON.stringify({ episodeFileIds }) data: JSON.stringify({ trackFileIds })
}); });
promise.done(() => { promise.done(() => {
const episodes = getState().episodes.items; const episodes = getState().episodes.items;
const episodesWithRemovedFiles = episodeFileIds.reduce((acc, episodeFileId) => { const episodesWithRemovedFiles = trackFileIds.reduce((acc, trackFileId) => {
acc.push(..._.filter(episodes, { episodeFileId })); acc.push(..._.filter(episodes, { trackFileId }));
return acc; return acc;
}, []); }, []);
dispatch(batchActions([ dispatch(batchActions([
...episodeFileIds.map((id) => { ...trackFileIds.map((id) => {
return removeItem({ section, id }); return removeItem({ section, id });
}), }),
@ -74,7 +74,7 @@ const episodeFileActionHandlers = {
return updateItem({ return updateItem({
section: 'episodes', section: 'episodes',
...episode, ...episode,
episodeFileId: 0, trackFileId: 0,
hasFile: false hasFile: false
}); });
}), }),
@ -97,10 +97,10 @@ const episodeFileActionHandlers = {
}; };
}, },
[types.UPDATE_EPISODE_FILES]: function(payload) { [types.UPDATE_TRACK_FILES]: function(payload) {
return function(dispatch, getState) { return function(dispatch, getState) {
const { const {
episodeFileIds, trackFileIds,
language, language,
quality quality
} = payload; } = payload;
@ -108,7 +108,7 @@ const episodeFileActionHandlers = {
dispatch(set({ section, isSaving: true })); dispatch(set({ section, isSaving: true }));
const data = { const data = {
episodeFileIds trackFileIds
}; };
if (language) { if (language) {
@ -120,7 +120,7 @@ const episodeFileActionHandlers = {
} }
const promise = $.ajax({ const promise = $.ajax({
url: '/episodeFile/editor', url: '/trackFile/editor',
method: 'PUT', method: 'PUT',
dataType: 'json', dataType: 'json',
data: JSON.stringify(data) data: JSON.stringify(data)
@ -128,7 +128,7 @@ const episodeFileActionHandlers = {
promise.done(() => { promise.done(() => {
dispatch(batchActions([ dispatch(batchActions([
...episodeFileIds.map((id) => { ...trackFileIds.map((id) => {
const props = {}; const props = {};
if (language) { if (language) {
@ -161,4 +161,4 @@ const episodeFileActionHandlers = {
} }
}; };
export default episodeFileActionHandlers; export default trackFileActionHandlers;

@ -0,0 +1,9 @@
import { createAction } from 'redux-actions';
import * as types from './actionTypes';
import trackFileActionHandlers from './trackFileActionHandlers';
export const fetchTrackFiles = trackFileActionHandlers[types.FETCH_TRACK_FILES];
export const deleteTrackFile = trackFileActionHandlers[types.DELETE_TRACK_FILE];
export const deleteTrackFiles = trackFileActionHandlers[types.DELETE_TRACK_FILES];
export const updateTrackFiles = trackFileActionHandlers[types.UPDATE_TRACK_FILES];
export const clearTrackFiles = createAction(types.CLEAR_TRACK_FILES);

@ -14,7 +14,7 @@ import queue, { defaultState as defaultQueueState } from './queueReducers';
import blacklist, { defaultState as defaultBlacklistState } from './blacklistReducers'; import blacklist, { defaultState as defaultBlacklistState } from './blacklistReducers';
import episodes, { defaultState as defaultEpisodesState } from './episodeReducers'; import episodes, { defaultState as defaultEpisodesState } from './episodeReducers';
import tracks, { defaultState as defaultTracksState } from './trackReducers'; import tracks, { defaultState as defaultTracksState } from './trackReducers';
import episodeFiles, { defaultState as defaultEpisodeFilesState } from './episodeFileReducers'; import trackFiles, { defaultState as defaultTrackFilesState } from './trackFileReducers';
import albumHistory, { defaultState as defaultAlbumHistoryState } from './albumHistoryReducers'; import albumHistory, { defaultState as defaultAlbumHistoryState } from './albumHistoryReducers';
import releases, { defaultState as defaultReleasesState } from './releaseReducers'; import releases, { defaultState as defaultReleasesState } from './releaseReducers';
import wanted, { defaultState as defaultWantedState } from './wantedReducers'; import wanted, { defaultState as defaultWantedState } from './wantedReducers';
@ -43,7 +43,7 @@ export const defaultState = {
blacklist: defaultBlacklistState, blacklist: defaultBlacklistState,
episodes: defaultEpisodesState, episodes: defaultEpisodesState,
tracks: defaultTracksState, tracks: defaultTracksState,
episodeFiles: defaultEpisodeFilesState, trackFiles: defaultTrackFilesState,
albumHistory: defaultAlbumHistoryState, albumHistory: defaultAlbumHistoryState,
releases: defaultReleasesState, releases: defaultReleasesState,
wanted: defaultWantedState, wanted: defaultWantedState,
@ -73,7 +73,7 @@ export default enableBatching(combineReducers({
blacklist, blacklist,
episodes, episodes,
tracks, tracks,
episodeFiles, trackFiles,
albumHistory, albumHistory,
releases, releases,
wanted, wanted,

@ -16,19 +16,19 @@ export const defaultState = {
items: [] items: []
}; };
const reducerSection = 'episodeFiles'; const reducerSection = 'trackFiles';
const episodeFileReducers = handleActions({ const trackFileReducers = handleActions({
[types.SET]: createSetReducer(reducerSection), [types.SET]: createSetReducer(reducerSection),
[types.UPDATE]: createUpdateReducer(reducerSection), [types.UPDATE]: createUpdateReducer(reducerSection),
[types.UPDATE_ITEM]: createUpdateItemReducer(reducerSection), [types.UPDATE_ITEM]: createUpdateItemReducer(reducerSection),
[types.REMOVE_ITEM]: createRemoveItemReducer(reducerSection), [types.REMOVE_ITEM]: createRemoveItemReducer(reducerSection),
[types.CLEAR_EPISODE_FILES]: (state) => { [types.CLEAR_TRACK_FILES]: (state) => {
return Object.assign({}, state, defaultState); return Object.assign({}, state, defaultState);
} }
}, defaultState); }, defaultState);
export default episodeFileReducers; export default trackFileReducers;

@ -1,18 +0,0 @@
import _ from 'lodash';
import { createSelector } from 'reselect';
function createEpisodeFileSelector() {
return createSelector(
(state, { episodeFileId }) => episodeFileId,
(state) => state.episodeFiles,
(episodeFileId, episodeFiles) => {
if (!episodeFileId) {
return null;
}
return _.find(episodeFiles.items, { id: episodeFileId });
}
);
}
export default createEpisodeFileSelector;

@ -0,0 +1,18 @@
import _ from 'lodash';
import { createSelector } from 'reselect';
function createTrackFileSelector() {
return createSelector(
(state, { trackFileId }) => trackFileId,
(state) => state.trackFiles,
(trackFileId, trackFiles) => {
if (!trackFileId) {
return null;
}
return _.find(trackFiles.items, { id: trackFileId });
}
);
}
export default createTrackFileSelector;

@ -1,9 +1,9 @@
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import React from 'react'; import React from 'react';
import Modal from 'Components/Modal/Modal'; import Modal from 'Components/Modal/Modal';
import EpisodeFileEditorModalContentConnector from './EpisodeFileEditorModalContentConnector'; import TrackFileEditorModalContentConnector from './TrackFileEditorModalContentConnector';
function EpisodeFileEditorModal(props) { function TrackFileEditorModal(props) {
const { const {
isOpen, isOpen,
onModalClose, onModalClose,
@ -17,7 +17,7 @@ function EpisodeFileEditorModal(props) {
> >
{ {
isOpen && isOpen &&
<EpisodeFileEditorModalContentConnector <TrackFileEditorModalContentConnector
{...otherProps} {...otherProps}
onModalClose={onModalClose} onModalClose={onModalClose}
/> />
@ -26,9 +26,9 @@ function EpisodeFileEditorModal(props) {
); );
} }
EpisodeFileEditorModal.propTypes = { TrackFileEditorModal.propTypes = {
isOpen: PropTypes.bool.isRequired, isOpen: PropTypes.bool.isRequired,
onModalClose: PropTypes.func.isRequired onModalClose: PropTypes.func.isRequired
}; };
export default EpisodeFileEditorModal; export default TrackFileEditorModal;

@ -15,13 +15,13 @@ 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 EpisodeFileEditorRow from './EpisodeFileEditorRow'; import TrackFileEditorRow from './TrackFileEditorRow';
import styles from './EpisodeFileEditorModalContent.css'; import styles from './TrackFileEditorModalContent.css';
const columns = [ const columns = [
{ {
name: 'episodeNumber', name: 'trackNumber',
label: 'Episode', label: 'Track',
isVisible: true isVisible: true
}, },
{ {
@ -29,11 +29,6 @@ const columns = [
label: 'Relative Path', label: 'Relative Path',
isVisible: true isVisible: true
}, },
{
name: 'airDateUtc',
label: 'Air Date',
isVisible: true
},
{ {
name: 'language', name: 'language',
label: 'Language', label: 'Language',
@ -46,7 +41,7 @@ const columns = [
} }
]; ];
class EpisodeFileEditorModalContent extends Component { class TrackFileEditorModalContent extends Component {
// //
// Lifecycle // Lifecycle
@ -76,7 +71,7 @@ class EpisodeFileEditorModalContent extends Component {
const selectedIds = getSelectedIds(this.state.selectedState); const selectedIds = getSelectedIds(this.state.selectedState);
return _.uniq(_.map(selectedIds, (id) => { return _.uniq(_.map(selectedIds, (id) => {
return _.find(this.props.items, { id }).episodeFileId; return _.find(this.props.items, { id }).trackFileId;
})); }));
} }
@ -135,7 +130,6 @@ class EpisodeFileEditorModalContent extends Component {
items, items,
languages, languages,
qualities, qualities,
seriesType,
onModalClose onModalClose
} = this.props; } = this.props;
@ -169,14 +163,14 @@ class EpisodeFileEditorModalContent extends Component {
return ( return (
<ModalContent onModalClose={onModalClose}> <ModalContent onModalClose={onModalClose}>
<ModalHeader> <ModalHeader>
Manage Episodes Manage Tracks
</ModalHeader> </ModalHeader>
<ModalBody> <ModalBody>
{ {
!items.length && !items.length &&
<div> <div>
No episode files to manage. No track files to manage.
</div> </div>
} }
@ -193,9 +187,8 @@ class EpisodeFileEditorModalContent extends Component {
{ {
items.map((item) => { items.map((item) => {
return ( return (
<EpisodeFileEditorRow <TrackFileEditorRow
key={item.id} key={item.id}
seriesType={seriesType}
isSelected={selectedState[item.id]} isSelected={selectedState[item.id]}
{...item} {...item}
onSelectedChange={this.onSelectedChange} onSelectedChange={this.onSelectedChange}
@ -250,8 +243,8 @@ class EpisodeFileEditorModalContent extends Component {
<ConfirmModal <ConfirmModal
isOpen={isConfirmDeleteModalOpen} isOpen={isConfirmDeleteModalOpen}
kind={kinds.DANGER} kind={kinds.DANGER}
title="Delete Selected Episode Files" title="Delete Selected Track Files"
message={'Are you sure you want to delete the selected episode files?'} message={'Are you sure you want to delete the selected track files?'}
confirmLabel="Delete" confirmLabel="Delete"
onConfirm={this.onConfirmDelete} onConfirm={this.onConfirmDelete}
onCancel={this.onConfirmDeleteModalClose} onCancel={this.onConfirmDeleteModalClose}
@ -261,16 +254,15 @@ class EpisodeFileEditorModalContent extends Component {
} }
} }
EpisodeFileEditorModalContent.propTypes = { TrackFileEditorModalContent.propTypes = {
isDeleting: PropTypes.bool.isRequired, isDeleting: PropTypes.bool.isRequired,
items: PropTypes.arrayOf(PropTypes.object).isRequired, items: PropTypes.arrayOf(PropTypes.object).isRequired,
languages: PropTypes.arrayOf(PropTypes.object).isRequired, languages: PropTypes.arrayOf(PropTypes.object).isRequired,
qualities: PropTypes.arrayOf(PropTypes.object).isRequired, qualities: PropTypes.arrayOf(PropTypes.object).isRequired,
seriesType: PropTypes.string.isRequired,
onDeletePress: PropTypes.func.isRequired, onDeletePress: PropTypes.func.isRequired,
onLanguageChange: PropTypes.func.isRequired, onLanguageChange: PropTypes.func.isRequired,
onQualityChange: PropTypes.func.isRequired, onQualityChange: PropTypes.func.isRequired,
onModalClose: PropTypes.func.isRequired onModalClose: PropTypes.func.isRequired
}; };
export default EpisodeFileEditorModalContent; export default TrackFileEditorModalContent;

@ -4,48 +4,49 @@ import React, { Component } from 'react';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { createSelector } from 'reselect'; import { createSelector } from 'reselect';
import createArtistSelector from 'Store/Selectors/createArtistSelector'; import createArtistSelector from 'Store/Selectors/createArtistSelector';
import { deleteEpisodeFiles, updateEpisodeFiles } from 'Store/Actions/episodeFileActions'; import { deleteTrackFiles, updateTrackFiles } from 'Store/Actions/trackFileActions';
import { fetchTracks, clearTracks } from 'Store/Actions/trackActions';
import { fetchLanguageProfileSchema, fetchQualityProfileSchema } from 'Store/Actions/settingsActions'; import { fetchLanguageProfileSchema, fetchQualityProfileSchema } from 'Store/Actions/settingsActions';
import EpisodeFileEditorModalContent from './EpisodeFileEditorModalContent'; import TrackFileEditorModalContent from './TrackFileEditorModalContent';
function createMapStateToProps() { function createMapStateToProps() {
return createSelector( return createSelector(
(state, { seasonNumber }) => seasonNumber, (state, { albumId }) => albumId,
(state) => state.episodes, (state) => state.tracks,
(state) => state.episodeFiles, (state) => state.trackFiles,
(state) => state.settings.languageProfiles.schema, (state) => state.settings.languageProfiles.schema,
(state) => state.settings.qualityProfiles.schema, (state) => state.settings.qualityProfiles.schema,
createArtistSelector(), createArtistSelector(),
( (
seasonNumber, albumId,
episodes, tracks,
episodeFiles, trackFiles,
languageProfilesSchema, languageProfilesSchema,
qualityProfileSchema, qualityProfileSchema,
series series
) => { ) => {
const filtered = _.filter(episodes.items, (episode) => { const filtered = _.filter(tracks.items, (track) => {
if (seasonNumber >= 0 && episode.seasonNumber !== seasonNumber) { if (albumId >= 0 && track.albumId !== albumId) {
return false; return false;
} }
if (!episode.episodeFileId) { if (!track.trackFileId) {
return false; return false;
} }
return _.some(episodeFiles.items, { id: episode.episodeFileId }); return _.some(trackFiles.items, { id: track.trackFileId });
}); });
const sorted = _.orderBy(filtered, ['seasonNumber', 'episodeNumber'], ['desc', 'desc']); const sorted = _.orderBy(filtered, ['albumId', 'trackNumber'], ['desc', 'asc']);
const items = _.map(sorted, (episode) => { const items = _.map(sorted, (track) => {
const episodeFile = _.find(episodeFiles.items, { id: episode.episodeFileId }); const trackFile = _.find(trackFiles.items, { id: track.trackFileId });
return { return {
relativePath: episodeFile.relativePath, relativePath: trackFile.relativePath,
language: episodeFile.language, language: trackFile.language,
quality: episodeFile.quality, quality: trackFile.quality,
...episode ...track
}; };
}); });
@ -55,8 +56,8 @@ function createMapStateToProps() {
return { return {
items, items,
seriesType: series.seriesType, seriesType: series.seriesType,
isDeleting: episodeFiles.isDeleting, isDeleting: trackFiles.isDeleting,
isSaving: episodeFiles.isSaving, isSaving: trackFiles.isSaving,
languages, languages,
qualities qualities
}; };
@ -66,6 +67,14 @@ function createMapStateToProps() {
function createMapDispatchToProps(dispatch, props) { function createMapDispatchToProps(dispatch, props) {
return { return {
dispatchClearTracks() {
dispatch(clearTracks());
},
dispatchFetchTracks(updateProps) {
dispatch(fetchTracks(updateProps));
},
dispatchFetchLanguageProfileSchema(name, path) { dispatchFetchLanguageProfileSchema(name, path) {
dispatch(fetchLanguageProfileSchema()); dispatch(fetchLanguageProfileSchema());
}, },
@ -74,16 +83,15 @@ function createMapDispatchToProps(dispatch, props) {
dispatch(fetchQualityProfileSchema()); dispatch(fetchQualityProfileSchema());
}, },
dispatchUpdateEpisodeFiles(updateProps) { dispatchUpdateTrackFiles(updateProps) {
dispatch(updateEpisodeFiles(updateProps)); dispatch(updateTrackFiles(updateProps));
}, },
onDeletePress(episodeFileIds) { onDeletePress(trackFileIds) {
dispatch(deleteEpisodeFiles({ episodeFileIds })); dispatch(deleteTrackFiles({ trackFileIds }));
}, },
onQualityChange(trackFileIds, qualityId) {
onQualityChange(episodeFileIds, qualityId) {
const quality = { const quality = {
quality: _.find(this.props.qualities, { id: qualityId }), quality: _.find(this.props.qualities, { id: qualityId }),
revision: { revision: {
@ -92,34 +100,42 @@ function createMapDispatchToProps(dispatch, props) {
} }
}; };
dispatch(updateEpisodeFiles({ episodeFileIds, quality })); dispatch(updateTrackFiles({ trackFileIds, quality }));
} }
}; };
} }
class EpisodeFileEditorModalContentConnector extends Component { class TrackFileEditorModalContentConnector extends Component {
// //
// Lifecycle // Lifecycle
componentDidMount() { componentDidMount() {
const artistId = this.props.artistId;
this.props.dispatchFetchTracks({ artistId });
this.props.dispatchFetchLanguageProfileSchema(); this.props.dispatchFetchLanguageProfileSchema();
this.props.dispatchFetchQualityProfileSchema(); this.props.dispatchFetchQualityProfileSchema();
} }
componentWillUnmount() {
this.props.dispatchClearTracks();
}
// //
// Render // Render
// //
// Listeners // Listeners
onLanguageChange = (episodeFileIds, languageId) => { onLanguageChange = (trackFileIds, languageId) => {
const language = _.find(this.props.languages, { id: languageId }); const language = _.find(this.props.languages, { id: languageId });
this.props.dispatchUpdateEpisodeFiles({ episodeFileIds, language }); this.props.dispatchUpdateTrackFiles({ trackFileIds, language });
} }
onQualityChange = (episodeFileIds, qualityId) => { onQualityChange = (trackFileIds, qualityId) => {
const quality = { const quality = {
quality: _.find(this.props.qualities, { id: qualityId }), quality: _.find(this.props.qualities, { id: qualityId }),
revision: { revision: {
@ -128,19 +144,21 @@ class EpisodeFileEditorModalContentConnector extends Component {
} }
}; };
this.props.dispatchUpdateEpisodeFiles({ episodeFileIds, quality }); this.props.dispatchUpdateTrackFiles({ trackFileIds, quality });
} }
render() { render() {
const { const {
dispatchFetchLanguageProfileSchema, dispatchFetchLanguageProfileSchema,
dispatchFetchQualityProfileSchema, dispatchFetchQualityProfileSchema,
dispatchUpdateEpisodeFiles, dispatchUpdateTrackFiles,
dispatchFetchTracks,
dispatchClearTracks,
...otherProps ...otherProps
} = this.props; } = this.props;
return ( return (
<EpisodeFileEditorModalContent <TrackFileEditorModalContent
{...otherProps} {...otherProps}
onLanguageChange={this.onLanguageChange} onLanguageChange={this.onLanguageChange}
onQualityChange={this.onQualityChange} onQualityChange={this.onQualityChange}
@ -149,14 +167,16 @@ class EpisodeFileEditorModalContentConnector extends Component {
} }
} }
EpisodeFileEditorModalContentConnector.propTypes = { TrackFileEditorModalContentConnector.propTypes = {
artistId: PropTypes.number.isRequired, artistId: PropTypes.number.isRequired,
seasonNumber: PropTypes.number, albumId: PropTypes.number,
languages: PropTypes.arrayOf(PropTypes.object).isRequired, languages: PropTypes.arrayOf(PropTypes.object).isRequired,
qualities: PropTypes.arrayOf(PropTypes.object).isRequired, qualities: PropTypes.arrayOf(PropTypes.object).isRequired,
dispatchFetchTracks: PropTypes.func.isRequired,
dispatchClearTracks: PropTypes.func.isRequired,
dispatchFetchLanguageProfileSchema: PropTypes.func.isRequired, dispatchFetchLanguageProfileSchema: PropTypes.func.isRequired,
dispatchFetchQualityProfileSchema: PropTypes.func.isRequired, dispatchFetchQualityProfileSchema: PropTypes.func.isRequired,
dispatchUpdateEpisodeFiles: PropTypes.func.isRequired dispatchUpdateTrackFiles: PropTypes.func.isRequired
}; };
export default connect(createMapStateToProps, createMapDispatchToProps)(EpisodeFileEditorModalContentConnector); export default connect(createMapStateToProps, createMapDispatchToProps)(TrackFileEditorModalContentConnector);

@ -7,17 +7,13 @@ import TableRow from 'Components/Table/TableRow';
import TableRowCell from 'Components/Table/Cells/TableRowCell'; import TableRowCell from 'Components/Table/Cells/TableRowCell';
import TableSelectCell from 'Components/Table/Cells/TableSelectCell'; import TableSelectCell from 'Components/Table/Cells/TableSelectCell';
import EpisodeQuality from 'Episode/EpisodeQuality'; import EpisodeQuality from 'Episode/EpisodeQuality';
import styles from './EpisodeFileEditorRow'; import styles from './TrackFileEditorRow';
function EpisodeFileEditorRow(props) { function TrackFileEditorRow(props) {
const { const {
id, id,
seriesType, trackNumber,
seasonNumber,
episodeNumber,
absoluteEpisodeNumber,
relativePath, relativePath,
airDateUtc,
language, language,
quality, quality,
isSelected, isSelected,
@ -33,24 +29,13 @@ function EpisodeFileEditorRow(props) {
/> />
<TableRowCell> <TableRowCell>
{seasonNumber}x{padNumber(episodeNumber, 2)} {padNumber(trackNumber, 2)}
{
seriesType === 'anime' && !!absoluteEpisodeNumber &&
<span className={styles.absoluteEpisodeNumber}>
({absoluteEpisodeNumber})
</span>
}
</TableRowCell> </TableRowCell>
<TableRowCell> <TableRowCell>
{relativePath} {relativePath}
</TableRowCell> </TableRowCell>
<RelativeDateCellConnector
date={airDateUtc}
/>
<TableRowCell> <TableRowCell>
<Label> <Label>
{language.name} {language.name}
@ -66,18 +51,14 @@ function EpisodeFileEditorRow(props) {
); );
} }
EpisodeFileEditorRow.propTypes = { TrackFileEditorRow.propTypes = {
id: PropTypes.number.isRequired, id: PropTypes.number.isRequired,
seriesType: PropTypes.string.isRequired, trackNumber: PropTypes.number.isRequired,
seasonNumber: PropTypes.number.isRequired,
episodeNumber: PropTypes.number.isRequired,
absoluteEpisodeNumber: PropTypes.number,
relativePath: PropTypes.string.isRequired, relativePath: PropTypes.string.isRequired,
airDateUtc: PropTypes.string.isRequired,
language: PropTypes.object.isRequired, language: PropTypes.object.isRequired,
quality: PropTypes.object.isRequired, quality: PropTypes.object.isRequired,
isSelected: PropTypes.bool, isSelected: PropTypes.bool,
onSelectedChange: PropTypes.func.isRequired onSelectedChange: PropTypes.func.isRequired
}; };
export default EpisodeFileEditorRow; export default TrackFileEditorRow;

@ -39,7 +39,6 @@ function MediaInfo(props) {
); );
} }
return null; return null;
} }

@ -1,15 +1,15 @@
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { createSelector } from 'reselect'; import { createSelector } from 'reselect';
import createEpisodeFileSelector from 'Store/Selectors/createEpisodeFileSelector'; import createTrackFileSelector from 'Store/Selectors/createTrackFileSelector';
import MediaInfo from './MediaInfo'; import MediaInfo from './MediaInfo';
function createMapStateToProps() { function createMapStateToProps() {
return createSelector( return createSelector(
createEpisodeFileSelector(), createTrackFileSelector(),
(episodeFile) => { (trackFile) => {
if (episodeFile) { if (trackFile) {
return { return {
...episodeFile.mediaInfo ...trackFile.mediaInfo
}; };
} }

@ -1,14 +1,14 @@
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { createSelector } from 'reselect'; import { createSelector } from 'reselect';
import createEpisodeFileSelector from 'Store/Selectors/createEpisodeFileSelector'; import createTrackFileSelector from 'Store/Selectors/createTrackFileSelector';
import EpisodeLanguage from 'Episode/EpisodeLanguage'; import EpisodeLanguage from 'Episode/EpisodeLanguage';
function createMapStateToProps() { function createMapStateToProps() {
return createSelector( return createSelector(
createEpisodeFileSelector(), createTrackFileSelector(),
(episodeFile) => { (trackFile) => {
return { return {
language: episodeFile ? episodeFile.language : undefined language: trackFile ? trackFile.language : undefined
}; };
} }
); );

@ -9,7 +9,7 @@ import createCommandsSelector from 'Store/Selectors/createCommandsSelector';
import * as wantedActions from 'Store/Actions/wantedActions'; import * as wantedActions from 'Store/Actions/wantedActions';
import { executeCommand } from 'Store/Actions/commandActions'; import { executeCommand } from 'Store/Actions/commandActions';
import { fetchQueueDetails, clearQueueDetails } from 'Store/Actions/queueActions'; import { fetchQueueDetails, clearQueueDetails } from 'Store/Actions/queueActions';
import { fetchEpisodeFiles, clearEpisodeFiles } from 'Store/Actions/episodeFileActions'; import { fetchTrackFiles, clearTrackFiles } from 'Store/Actions/trackFileActions';
import * as commandNames from 'Commands/commandNames'; import * as commandNames from 'Commands/commandNames';
import CutoffUnmet from './CutoffUnmet'; import CutoffUnmet from './CutoffUnmet';
@ -36,8 +36,8 @@ const mapDispatchToProps = {
executeCommand, executeCommand,
fetchQueueDetails, fetchQueueDetails,
clearQueueDetails, clearQueueDetails,
fetchEpisodeFiles, fetchTrackFiles,
clearEpisodeFiles clearTrackFiles
}; };
class CutoffUnmetConnector extends Component { class CutoffUnmetConnector extends Component {
@ -52,12 +52,12 @@ class CutoffUnmetConnector extends Component {
componentDidUpdate(prevProps) { componentDidUpdate(prevProps) {
if (hasDifferentItems(prevProps.items, this.props.items)) { if (hasDifferentItems(prevProps.items, this.props.items)) {
const albumIds = selectUniqueIds(this.props.items, 'id'); const albumIds = selectUniqueIds(this.props.items, 'id');
const episodeFileIds = selectUniqueIds(this.props.items, 'episodeFileId'); const trackFileIds = selectUniqueIds(this.props.items, 'trackFileId');
this.props.fetchQueueDetails({ albumIds }); this.props.fetchQueueDetails({ albumIds });
if (episodeFileIds.length) { if (trackFileIds.length) {
this.props.fetchEpisodeFiles({ episodeFileIds }); this.props.fetchTrackFiles({ trackFileIds });
} }
} }
} }
@ -65,7 +65,7 @@ class CutoffUnmetConnector extends Component {
componentWillUnmount() { componentWillUnmount() {
this.props.clearCutoffUnmet(); this.props.clearCutoffUnmet();
this.props.clearQueueDetails(); this.props.clearQueueDetails();
this.props.clearEpisodeFiles(); this.props.clearTrackFiles();
} }
// //
@ -173,8 +173,8 @@ CutoffUnmetConnector.propTypes = {
executeCommand: PropTypes.func.isRequired, executeCommand: PropTypes.func.isRequired,
fetchQueueDetails: PropTypes.func.isRequired, fetchQueueDetails: PropTypes.func.isRequired,
clearQueueDetails: PropTypes.func.isRequired, clearQueueDetails: PropTypes.func.isRequired,
fetchEpisodeFiles: PropTypes.func.isRequired, fetchTrackFiles: PropTypes.func.isRequired,
clearEpisodeFiles: PropTypes.func.isRequired clearTrackFiles: PropTypes.func.isRequired
}; };
export default connect(createMapStateToProps, mapDispatchToProps)(CutoffUnmetConnector); export default connect(createMapStateToProps, mapDispatchToProps)(CutoffUnmetConnector);

@ -5,7 +5,7 @@ import EpisodeTitleLink from 'Episode/EpisodeTitleLink';
import EpisodeStatusConnector from 'Episode/EpisodeStatusConnector'; import EpisodeStatusConnector from 'Episode/EpisodeStatusConnector';
import SeasonEpisodeNumber from 'Episode/SeasonEpisodeNumber'; import SeasonEpisodeNumber from 'Episode/SeasonEpisodeNumber';
import EpisodeSearchCellConnector from 'Episode/EpisodeSearchCellConnector'; import EpisodeSearchCellConnector from 'Episode/EpisodeSearchCellConnector';
import EpisodeFileLanguageConnector from 'EpisodeFile/EpisodeFileLanguageConnector'; import TrackFileLanguageConnector from 'TrackFile/TrackFileLanguageConnector';
import ArtistNameLink from 'Artist/ArtistNameLink'; import ArtistNameLink from 'Artist/ArtistNameLink';
import RelativeDateCellConnector from 'Components/Table/Cells/RelativeDateCellConnector'; import RelativeDateCellConnector from 'Components/Table/Cells/RelativeDateCellConnector';
import TableRow from 'Components/Table/TableRow'; import TableRow from 'Components/Table/TableRow';
@ -16,7 +16,7 @@ import styles from './CutoffUnmetRow.css';
function CutoffUnmetRow(props) { function CutoffUnmetRow(props) {
const { const {
id, id,
episodeFileId, trackFileId,
series, series,
seasonNumber, seasonNumber,
episodeNumber, episodeNumber,
@ -109,8 +109,8 @@ function CutoffUnmetRow(props) {
key={name} key={name}
className={styles.language} className={styles.language}
> >
<EpisodeFileLanguageConnector <TrackFileLanguageConnector
episodeFileId={episodeFileId} trackFileId={trackFileId}
/> />
</TableRowCell> </TableRowCell>
); );
@ -124,7 +124,7 @@ function CutoffUnmetRow(props) {
> >
<EpisodeStatusConnector <EpisodeStatusConnector
episodeId={id} episodeId={id}
episodeFileId={episodeFileId} trackFileId={trackFileId}
episodeEntity={episodeEntities.WANTED_CUTOFF_UNMET} episodeEntity={episodeEntities.WANTED_CUTOFF_UNMET}
/> />
</TableRowCell> </TableRowCell>
@ -151,7 +151,7 @@ function CutoffUnmetRow(props) {
CutoffUnmetRow.propTypes = { CutoffUnmetRow.propTypes = {
id: PropTypes.number.isRequired, id: PropTypes.number.isRequired,
episodeFileId: PropTypes.number, trackFileId: PropTypes.number,
series: PropTypes.object.isRequired, series: PropTypes.object.isRequired,
seasonNumber: PropTypes.number.isRequired, seasonNumber: PropTypes.number.isRequired,
episodeNumber: PropTypes.number.isRequired, episodeNumber: PropTypes.number.isRequired,

@ -15,7 +15,7 @@ import styles from './MissingRow.css';
function MissingRow(props) { function MissingRow(props) {
const { const {
id, id,
// episodeFileId, // trackFileId,
artist, artist,
// seasonNumber, // seasonNumber,
// episodeNumber, // episodeNumber,
@ -110,7 +110,7 @@ function MissingRow(props) {
// > // >
// <EpisodeStatusConnector // <EpisodeStatusConnector
// episodeId={id} // episodeId={id}
// episodeFileId={episodeFileId} // trackFileId={trackFileId}
// episodeEntity={episodeEntities.WANTED_MISSING} // episodeEntity={episodeEntities.WANTED_MISSING}
// /> // />
// </TableRowCell> // </TableRowCell>
@ -137,7 +137,7 @@ function MissingRow(props) {
MissingRow.propTypes = { MissingRow.propTypes = {
id: PropTypes.number.isRequired, id: PropTypes.number.isRequired,
// episodeFileId: PropTypes.number, // trackFileId: PropTypes.number,
artist: PropTypes.object.isRequired, artist: PropTypes.object.isRequired,
// seasonNumber: PropTypes.number.isRequired, // seasonNumber: PropTypes.number.isRequired,
// episodeNumber: PropTypes.number.isRequired, // episodeNumber: PropTypes.number.isRequired,

@ -11,7 +11,6 @@ using NzbDrone.Core.MediaFiles.Events;
using NzbDrone.Core.Messaging.Events; using NzbDrone.Core.Messaging.Events;
using NzbDrone.Core.Music; using NzbDrone.Core.Music;
using NzbDrone.SignalR; using NzbDrone.SignalR;
using Lidarr.Api.V3.Series;
using Lidarr.Http; using Lidarr.Http;
using Lidarr.Http.Extensions; using Lidarr.Http.Extensions;
using Lidarr.Http.REST; using Lidarr.Http.REST;
@ -49,16 +48,16 @@ namespace Lidarr.Api.V3.TrackFiles
UpdateResource = SetQuality; UpdateResource = SetQuality;
DeleteResource = DeleteTrackFile; DeleteResource = DeleteTrackFile;
Put["/editor"] = episodeFiles => SetQuality(); Put["/editor"] = trackFiles => SetQuality();
Delete["/bulk"] = episodeFiles => DeleteTrackFiles(); Delete["/bulk"] = trackFiles => DeleteTrackFiles();
} }
private TrackFileResource GetTrackFile(int id) private TrackFileResource GetTrackFile(int id)
{ {
var trackFile = _mediaFileService.Get(id); var trackFile = _mediaFileService.Get(id);
var series = _artistService.GetArtist(trackFile.ArtistId); var artist = _artistService.GetArtist(trackFile.ArtistId);
return trackFile.ToResource(series, _upgradableSpecification); return trackFile.ToResource(artist, _upgradableSpecification);
} }
private List<TrackFileResource> GetTrackFiles() private List<TrackFileResource> GetTrackFiles()
@ -72,7 +71,7 @@ namespace Lidarr.Api.V3.TrackFiles
throw new BadRequestException("artistId, albumId, or trackFileIds must be provided"); throw new BadRequestException("artistId, albumId, or trackFileIds must be provided");
} }
if (artistIdQuery.HasValue) if (artistIdQuery.HasValue && !albumIdQuery.HasValue)
{ {
int artistId = Convert.ToInt32(artistIdQuery.Value); int artistId = Convert.ToInt32(artistIdQuery.Value);
var artist = _artistService.GetArtist(artistId); var artist = _artistService.GetArtist(artistId);

@ -18,10 +18,10 @@ namespace Lidarr.Api.V3.Tracks
IBroadcastSignalRMessage signalRBroadcaster) IBroadcastSignalRMessage signalRBroadcaster)
: base(trackService, artistService, upgradableSpecification, signalRBroadcaster) : base(trackService, artistService, upgradableSpecification, signalRBroadcaster)
{ {
GetResourceAll = GetEpisodes; GetResourceAll = GetTracks;
} }
private List<TrackResource> GetEpisodes() private List<TrackResource> GetTracks()
{ {
var artistIdQuery = Request.Query.ArtistId; var artistIdQuery = Request.Query.ArtistId;
var albumIdQuery = Request.Query.AlbumId; var albumIdQuery = Request.Query.AlbumId;

Loading…
Cancel
Save