From 9be4ec4c15936db952fe96ff0237a74c5d0e6f43 Mon Sep 17 00:00:00 2001 From: Qstick Date: Sun, 24 Sep 2017 22:58:13 -0400 Subject: [PATCH] [UI] Refactor TrackFile Modal --- .../History/Details/HistoryDetails.js | 4 +- .../History/Details/HistoryDetailsModal.js | 4 +- .../Activity/History/HistoryEventTypeCell.js | 8 +- frontend/src/Activity/Queue/QueueRow.js | 2 +- frontend/src/Artist/Details/AlbumRow.js | 4 +- .../src/Artist/Details/AlbumRowConnector.js | 10 +- frontend/src/Artist/Details/ArtistDetails.js | 20 ++-- .../Artist/Details/ArtistDetailsConnector.js | 26 ++--- .../src/Artist/Details/ArtistDetailsSeason.js | 4 +- frontend/src/Calendar/CalendarConnector.js | 18 ++-- frontend/src/Commands/commandNames.js | 2 +- frontend/src/Components/SignalRConnector.js | 6 +- .../EpisodeDetailsModalContentConnector.js | 15 ++- frontend/src/Episode/EpisodeStatus.js | 14 +-- .../src/Episode/EpisodeStatusConnector.js | 10 +- .../src/Episode/Summary/EpisodeSummary.js | 24 ++--- .../Summary/EpisodeSummaryConnector.js | 18 ++-- .../src/Episode/Summary/TrackDetailRow.js | 8 +- ...ImportSelectFolderModalContentConnector.js | 2 +- .../InteractiveImportModalContent.js | 2 +- frontend/src/Store/Actions/actionTypes.js | 10 +- .../src/Store/Actions/episodeFileActions.js | 9 -- ...Handlers.js => trackFileActionHandlers.js} | 46 ++++----- .../src/Store/Actions/trackFileActions.js | 9 ++ frontend/src/Store/Reducers/index.js | 6 +- ...deFileReducers.js => trackFileReducers.js} | 8 +- .../Selectors/createEpisodeFileSelector.js | 18 ---- .../Selectors/createTrackFileSelector.js | 18 ++++ .../Editor/TrackFileEditorModal.js} | 10 +- .../Editor/TrackFileEditorModalContent.css} | 0 .../Editor/TrackFileEditorModalContent.js} | 34 +++---- .../TrackFileEditorModalContentConnector.js} | 98 +++++++++++-------- .../Editor/TrackFileEditorRow.css} | 0 .../Editor/TrackFileEditorRow.js} | 33 ++----- .../{EpisodeFile => TrackFile}/MediaInfo.js | 1 - .../MediaInfoConnector.js | 10 +- .../TrackFileLanguageConnector.js} | 8 +- .../mediaInfoTypes.js | 0 .../CutoffUnmet/CutoffUnmetConnector.js | 18 ++-- .../src/Wanted/CutoffUnmet/CutoffUnmetRow.js | 12 +-- frontend/src/Wanted/Missing/MissingRow.js | 6 +- .../TrackFiles/TrackFileModule.cs | 11 +-- src/Lidarr.Api.V3/Tracks/TrackModule.cs | 4 +- 43 files changed, 280 insertions(+), 290 deletions(-) delete mode 100644 frontend/src/Store/Actions/episodeFileActions.js rename frontend/src/Store/Actions/{episodeFileActionHandlers.js => trackFileActionHandlers.js} (73%) create mode 100644 frontend/src/Store/Actions/trackFileActions.js rename frontend/src/Store/Reducers/{episodeFileReducers.js => trackFileReducers.js} (84%) delete mode 100644 frontend/src/Store/Selectors/createEpisodeFileSelector.js create mode 100644 frontend/src/Store/Selectors/createTrackFileSelector.js rename frontend/src/{EpisodeFile/Editor/EpisodeFileEditorModal.js => TrackFile/Editor/TrackFileEditorModal.js} (64%) rename frontend/src/{EpisodeFile/Editor/EpisodeFileEditorModalContent.css => TrackFile/Editor/TrackFileEditorModalContent.css} (100%) rename frontend/src/{EpisodeFile/Editor/EpisodeFileEditorModalContent.js => TrackFile/Editor/TrackFileEditorModalContent.js} (89%) rename frontend/src/{EpisodeFile/Editor/EpisodeFileEditorModalContentConnector.js => TrackFile/Editor/TrackFileEditorModalContentConnector.js} (52%) rename frontend/src/{EpisodeFile/Editor/EpisodeFileEditorRow.css => TrackFile/Editor/TrackFileEditorRow.css} (100%) rename frontend/src/{EpisodeFile/Editor/EpisodeFileEditorRow.js => TrackFile/Editor/TrackFileEditorRow.js} (62%) rename frontend/src/{EpisodeFile => TrackFile}/MediaInfo.js (99%) rename frontend/src/{EpisodeFile => TrackFile}/MediaInfoConnector.js (60%) rename frontend/src/{EpisodeFile/EpisodeFileLanguageConnector.js => TrackFile/TrackFileLanguageConnector.js} (59%) rename frontend/src/{EpisodeFile => TrackFile}/mediaInfoTypes.js (100%) diff --git a/frontend/src/Activity/History/Details/HistoryDetails.js b/frontend/src/Activity/History/Details/HistoryDetails.js index 48fe7890c..b2adf1171 100644 --- a/frontend/src/Activity/History/Details/HistoryDetails.js +++ b/frontend/src/Activity/History/Details/HistoryDetails.js @@ -156,7 +156,7 @@ function HistoryDetails(props) { ); } - if (eventType === 'episodeFileDeleted') { + if (eventType === 'trackFileDeleted') { const { reason } = data; @@ -192,7 +192,7 @@ function HistoryDetails(props) { ); } - if (eventType === 'episodeFileRenamed') { + if (eventType === 'trackFileRenamed') { const { sourcePath, sourceRelativePath, diff --git a/frontend/src/Activity/History/Details/HistoryDetailsModal.js b/frontend/src/Activity/History/Details/HistoryDetailsModal.js index 2cf9294f6..0523d4397 100644 --- a/frontend/src/Activity/History/Details/HistoryDetailsModal.js +++ b/frontend/src/Activity/History/Details/HistoryDetailsModal.js @@ -19,9 +19,9 @@ function getHeaderTitle(eventType) { return 'Download Failed'; case 'downloadFolderImported': return 'Episode Imported'; - case 'episodeFileDeleted': + case 'trackFileDeleted': return 'Episode File Deleted'; - case 'episodeFileRenamed': + case 'trackFileRenamed': return 'Episode File Renamed'; default: return 'Unknown'; diff --git a/frontend/src/Activity/History/HistoryEventTypeCell.js b/frontend/src/Activity/History/HistoryEventTypeCell.js index 21a210107..5e02409ea 100644 --- a/frontend/src/Activity/History/HistoryEventTypeCell.js +++ b/frontend/src/Activity/History/HistoryEventTypeCell.js @@ -15,9 +15,9 @@ function getIconName(eventType) { return icons.DOWNLOADED; case 'downloadFailed': return icons.DOWNLOADING; - case 'episodeFileDeleted': + case 'trackFileDeleted': return icons.DELETE; - case 'episodeFileRenamed': + case 'trackFileRenamed': return icons.ORGANIZE; default: return icons.UNKNOWN; @@ -43,9 +43,9 @@ function getTooltip(eventType, data) { return 'Album downloaded successfully and picked up from download client'; case 'downloadFailed': return 'Album download failed'; - case 'episodeFileDeleted': + case 'trackFileDeleted': return 'Track file deleted'; - case 'episodeFileRenamed': + case 'trackFileRenamed': return 'Track file renamed'; default: return 'Unknown event'; diff --git a/frontend/src/Activity/Queue/QueueRow.js b/frontend/src/Activity/Queue/QueueRow.js index 451fd4b99..41840f735 100644 --- a/frontend/src/Activity/Queue/QueueRow.js +++ b/frontend/src/Activity/Queue/QueueRow.js @@ -160,7 +160,7 @@ class QueueRow extends Component { 0) { +function getEpisodeCountKind(monitored, trackFileCount, episodeCount) { + if (trackFileCount === episodeCount && episodeCount > 0) { return kinds.SUCCESS; } diff --git a/frontend/src/Artist/Details/AlbumRowConnector.js b/frontend/src/Artist/Details/AlbumRowConnector.js index eba950ab3..ee61c3dee 100644 --- a/frontend/src/Artist/Details/AlbumRowConnector.js +++ b/frontend/src/Artist/Details/AlbumRowConnector.js @@ -2,7 +2,7 @@ import _ from 'lodash'; import { connect } from 'react-redux'; import { createSelector } from 'reselect'; 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 AlbumRow from './AlbumRow'; @@ -11,16 +11,16 @@ function createMapStateToProps() { (state, { id }) => id, (state, { sceneSeasonNumber }) => sceneSeasonNumber, createArtistSelector(), - createEpisodeFileSelector(), + createTrackFileSelector(), createCommandsSelector(), - (id, sceneSeasonNumber, series, episodeFile, commands) => { + (id, sceneSeasonNumber, series, trackFile, commands) => { const alternateTitles = sceneSeasonNumber ? _.filter(series.alternateTitles, { sceneSeasonNumber }) : []; return { artistMonitored: series.monitored, seriesType: series.seriesType, - episodeFilePath: episodeFile ? episodeFile.path : null, - episodeFileRelativePath: episodeFile ? episodeFile.relativePath : null, + trackFilePath: trackFile ? trackFile.path : null, + trackFileRelativePath: trackFile ? trackFile.relativePath : null, alternateTitles }; } diff --git a/frontend/src/Artist/Details/ArtistDetails.js b/frontend/src/Artist/Details/ArtistDetails.js index 9dd246d51..b1d208d65 100644 --- a/frontend/src/Artist/Details/ArtistDetails.js +++ b/frontend/src/Artist/Details/ArtistDetails.js @@ -18,7 +18,7 @@ import PageToolbarSeparator from 'Components/Page/Toolbar/PageToolbarSeparator'; import PageToolbarButton from 'Components/Page/Toolbar/PageToolbarButton'; import Popover from 'Components/Tooltip/Popover'; import Tooltip from 'Components/Tooltip/Tooltip'; -import EpisodeFileEditorModal from 'EpisodeFile/Editor/EpisodeFileEditorModal'; +import TrackFileEditorModal from 'TrackFile/Editor/TrackFileEditorModal'; import OrganizePreviewModalConnector from 'Organize/OrganizePreviewModalConnector'; import QualityProfileNameConnector from 'Settings/Profiles/Quality/QualityProfileNameConnector'; import ArtistPoster from 'Artist/ArtistPoster'; @@ -179,7 +179,7 @@ class ArtistDetails extends Component { isFetching, isPopulated, episodesError, - episodeFilesError, + trackFilesError, previousArtist, nextArtist, onRefreshPress, @@ -198,12 +198,12 @@ class ArtistDetails extends Component { const continuing = status === 'continuing'; - let episodeFilesCountMessage = 'No track files'; + let trackFilesCountMessage = 'No track files'; if (trackFileCount === 1) { - episodeFilesCountMessage = '1 track file'; + trackFilesCountMessage = '1 track file'; } else if (trackFileCount > 1) { - episodeFilesCountMessage = `${trackFileCount} track files`; + trackFilesCountMessage = `${trackFileCount} track files`; } let expandIcon = icons.EXPAND_INDETERMINATE; @@ -345,7 +345,7 @@ class ArtistDetails extends Component {
); @@ -149,7 +149,7 @@ EpisodeSummary.propTypes = { size: PropTypes.number, quality: PropTypes.object, qualityCutoffNotMet: PropTypes.bool, - onDeleteEpisodeFile: PropTypes.func.isRequired + onDeleteTrackFile: PropTypes.func.isRequired }; export default EpisodeSummary; diff --git a/frontend/src/Episode/Summary/EpisodeSummaryConnector.js b/frontend/src/Episode/Summary/EpisodeSummaryConnector.js index a0f1bf451..8e0976968 100644 --- a/frontend/src/Episode/Summary/EpisodeSummaryConnector.js +++ b/frontend/src/Episode/Summary/EpisodeSummaryConnector.js @@ -1,9 +1,8 @@ +import _ from 'lodash'; import { connect } from 'react-redux'; import { createSelector } from 'reselect'; -import { deleteEpisodeFile } from 'Store/Actions/episodeFileActions'; +import { deleteTrackFile } from 'Store/Actions/trackFileActions'; 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 createArtistSelector from 'Store/Selectors/createArtistSelector'; import createCommandsSelector from 'Store/Selectors/createCommandsSelector'; @@ -11,18 +10,19 @@ import EpisodeSummary from './EpisodeSummary'; function createMapStateToProps() { return createSelector( - (state, { episode }) => episode, (state) => state.tracks, createEpisodeSelector(), createCommandsSelector(), createDimensionsSelector(), - (albumId, tracks, episode, commands, dimensions) => { + (tracks, episode, commands, dimensions) => { + const items = _.filter(tracks.items, { albumId: episode.id }); + return { network: episode.label, qualityProfileId: episode.profileId, airDateUtc: episode.releaseDate, overview: episode.overview, - items: tracks.items, + items, columns: tracks.columns }; } @@ -31,9 +31,9 @@ function createMapStateToProps() { function createMapDispatchToProps(dispatch, props) { return { - onDeleteEpisodeFile() { - dispatch(deleteEpisodeFile({ - id: props.episodeFileId, + onDeleteTrackFile() { + dispatch(deleteTrackFile({ + id: props.trackFileId, episodeEntity: props.episodeEntity })); } diff --git a/frontend/src/Episode/Summary/TrackDetailRow.js b/frontend/src/Episode/Summary/TrackDetailRow.js index 3db53e693..ad5d9a5c4 100644 --- a/frontend/src/Episode/Summary/TrackDetailRow.js +++ b/frontend/src/Episode/Summary/TrackDetailRow.js @@ -3,8 +3,8 @@ import React, { Component } from 'react'; import TableRow from 'Components/Table/TableRow'; import TableRowCell from 'Components/Table/Cells/TableRowCell'; import formatTimeSpan from 'Utilities/Date/formatTimeSpan'; -import MediaInfoConnector from 'EpisodeFile/MediaInfoConnector'; -import * as mediaInfoTypes from 'EpisodeFile/mediaInfoTypes'; +import MediaInfoConnector from 'TrackFile/MediaInfoConnector'; +import * as mediaInfoTypes from 'TrackFile/mediaInfoTypes'; import EpisodeStatusConnector from 'Episode/EpisodeStatusConnector'; import styles from './TrackDetailRow.css'; @@ -83,7 +83,7 @@ class TrackDetailRow extends Component { > ); @@ -97,7 +97,7 @@ class TrackDetailRow extends Component { > ); diff --git a/frontend/src/InteractiveImport/Folder/InteractiveImportSelectFolderModalContentConnector.js b/frontend/src/InteractiveImport/Folder/InteractiveImportSelectFolderModalContentConnector.js index 2df7b1c4c..c838f3bab 100644 --- a/frontend/src/InteractiveImport/Folder/InteractiveImportSelectFolderModalContentConnector.js +++ b/frontend/src/InteractiveImport/Folder/InteractiveImportSelectFolderModalContentConnector.js @@ -32,7 +32,7 @@ class InteractiveImportSelectFolderModalContentConnector extends Component { this.props.addRecentFolder({ folder }); this.props.executeCommand({ - name: commandNames.DOWNLOADED_EPSIODES_SCAN, + name: commandNames.DOWNLOADED_ALBUMS_SCAN, path: folder }); diff --git a/frontend/src/InteractiveImport/Interactive/InteractiveImportModalContent.js b/frontend/src/InteractiveImport/Interactive/InteractiveImportModalContent.js index 5997053f1..7c44f9963 100644 --- a/frontend/src/InteractiveImport/Interactive/InteractiveImportModalContent.js +++ b/frontend/src/InteractiveImport/Interactive/InteractiveImportModalContent.js @@ -236,7 +236,7 @@ class InteractiveImportModalContent extends Component { { isPopulated && !items.length && - 'No video files were found in the selected folder' + 'No audio files were found in the selected folder' } diff --git a/frontend/src/Store/Actions/actionTypes.js b/frontend/src/Store/Actions/actionTypes.js index 39edf059e..78a626b1e 100644 --- a/frontend/src/Store/Actions/actionTypes.js +++ b/frontend/src/Store/Actions/actionTypes.js @@ -95,11 +95,11 @@ export const CLEAR_TRACKS = 'CLEAR_TRACKS'; // // Episode Files -export const FETCH_EPISODE_FILES = 'FETCH_EPISODE_FILES'; -export const CLEAR_EPISODE_FILES = 'CLEAR_EPISODE_FILES'; -export const DELETE_EPISODE_FILE = 'DELETE_EPISODE_FILE'; -export const DELETE_EPISODE_FILES = 'DELETE_EPISODE_FILES'; -export const UPDATE_EPISODE_FILES = 'UPDATE_EPISODE_FILES'; +export const FETCH_TRACK_FILES = 'FETCH_TRACK_FILES'; +export const CLEAR_TRACK_FILES = 'CLEAR_TRACK_FILES'; +export const DELETE_TRACK_FILE = 'DELETE_TRACK_FILE'; +export const DELETE_TRACK_FILES = 'DELETE_TRACK_FILES'; +export const UPDATE_TRACK_FILES = 'UPDATE_TRACK_FILES'; // // Episode History diff --git a/frontend/src/Store/Actions/episodeFileActions.js b/frontend/src/Store/Actions/episodeFileActions.js deleted file mode 100644 index f8087afa8..000000000 --- a/frontend/src/Store/Actions/episodeFileActions.js +++ /dev/null @@ -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); diff --git a/frontend/src/Store/Actions/episodeFileActionHandlers.js b/frontend/src/Store/Actions/trackFileActionHandlers.js similarity index 73% rename from frontend/src/Store/Actions/episodeFileActionHandlers.js rename to frontend/src/Store/Actions/trackFileActionHandlers.js index 086c4c61d..3a7384c25 100644 --- a/frontend/src/Store/Actions/episodeFileActionHandlers.js +++ b/frontend/src/Store/Actions/trackFileActionHandlers.js @@ -7,33 +7,33 @@ import createRemoveItemHandler from './Creators/createRemoveItemHandler'; import * as types from './actionTypes'; import { set, removeItem, updateItem } from './baseActions'; -const section = 'episodeFiles'; -const deleteEpisodeFile = createRemoveItemHandler(section, '/trackFile'); +const section = 'trackFiles'; +const deleteTrackFile = createRemoveItemHandler(section, '/trackFile'); -const episodeFileActionHandlers = { - [types.FETCH_EPISODE_FILES]: createFetchHandler(section, '/trackFile'), +const trackFileActionHandlers = { + [types.FETCH_TRACK_FILES]: createFetchHandler(section, '/trackFile'), - [types.DELETE_EPISODE_FILE]: function(payload) { + [types.DELETE_TRACK_FILE]: function(payload) { return function(dispatch, getState) { const { - id: episodeFileId, + id: trackFileId, episodeEntity = episodeEntities.EPISODES } = payload; const episodeSection = _.last(episodeEntity.split('.')); - const deletePromise = deleteEpisodeFile(payload)(dispatch, getState); + const deletePromise = deleteTrackFile(payload)(dispatch, getState); deletePromise.done(() => { const episodes = getState().episodes.items; - const episodesWithRemovedFiles = _.filter(episodes, { episodeFileId }); + const episodesWithRemovedFiles = _.filter(episodes, { trackFileId }); dispatch(batchActions([ ...episodesWithRemovedFiles.map((episode) => { return updateItem({ section: episodeSection, ...episode, - episodeFileId: 0, + trackFileId: 0, hasFile: false }); }) @@ -42,31 +42,31 @@ const episodeFileActionHandlers = { }; }, - [types.DELETE_EPISODE_FILES]: function(payload) { + [types.DELETE_TRACK_FILES]: function(payload) { return function(dispatch, getState) { const { - episodeFileIds + trackFileIds } = payload; dispatch(set({ section, isDeleting: true })); const promise = $.ajax({ - url: '/episodeFile/bulk', + url: '/trackFile/bulk', method: 'DELETE', dataType: 'json', - data: JSON.stringify({ episodeFileIds }) + data: JSON.stringify({ trackFileIds }) }); promise.done(() => { const episodes = getState().episodes.items; - const episodesWithRemovedFiles = episodeFileIds.reduce((acc, episodeFileId) => { - acc.push(..._.filter(episodes, { episodeFileId })); + const episodesWithRemovedFiles = trackFileIds.reduce((acc, trackFileId) => { + acc.push(..._.filter(episodes, { trackFileId })); return acc; }, []); dispatch(batchActions([ - ...episodeFileIds.map((id) => { + ...trackFileIds.map((id) => { return removeItem({ section, id }); }), @@ -74,7 +74,7 @@ const episodeFileActionHandlers = { return updateItem({ section: 'episodes', ...episode, - episodeFileId: 0, + trackFileId: 0, hasFile: false }); }), @@ -97,10 +97,10 @@ const episodeFileActionHandlers = { }; }, - [types.UPDATE_EPISODE_FILES]: function(payload) { + [types.UPDATE_TRACK_FILES]: function(payload) { return function(dispatch, getState) { const { - episodeFileIds, + trackFileIds, language, quality } = payload; @@ -108,7 +108,7 @@ const episodeFileActionHandlers = { dispatch(set({ section, isSaving: true })); const data = { - episodeFileIds + trackFileIds }; if (language) { @@ -120,7 +120,7 @@ const episodeFileActionHandlers = { } const promise = $.ajax({ - url: '/episodeFile/editor', + url: '/trackFile/editor', method: 'PUT', dataType: 'json', data: JSON.stringify(data) @@ -128,7 +128,7 @@ const episodeFileActionHandlers = { promise.done(() => { dispatch(batchActions([ - ...episodeFileIds.map((id) => { + ...trackFileIds.map((id) => { const props = {}; if (language) { @@ -161,4 +161,4 @@ const episodeFileActionHandlers = { } }; -export default episodeFileActionHandlers; +export default trackFileActionHandlers; diff --git a/frontend/src/Store/Actions/trackFileActions.js b/frontend/src/Store/Actions/trackFileActions.js new file mode 100644 index 000000000..fa4b6544a --- /dev/null +++ b/frontend/src/Store/Actions/trackFileActions.js @@ -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); diff --git a/frontend/src/Store/Reducers/index.js b/frontend/src/Store/Reducers/index.js index 66d5ce808..d4e84b255 100644 --- a/frontend/src/Store/Reducers/index.js +++ b/frontend/src/Store/Reducers/index.js @@ -14,7 +14,7 @@ import queue, { defaultState as defaultQueueState } from './queueReducers'; import blacklist, { defaultState as defaultBlacklistState } from './blacklistReducers'; import episodes, { defaultState as defaultEpisodesState } from './episodeReducers'; 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 releases, { defaultState as defaultReleasesState } from './releaseReducers'; import wanted, { defaultState as defaultWantedState } from './wantedReducers'; @@ -43,7 +43,7 @@ export const defaultState = { blacklist: defaultBlacklistState, episodes: defaultEpisodesState, tracks: defaultTracksState, - episodeFiles: defaultEpisodeFilesState, + trackFiles: defaultTrackFilesState, albumHistory: defaultAlbumHistoryState, releases: defaultReleasesState, wanted: defaultWantedState, @@ -73,7 +73,7 @@ export default enableBatching(combineReducers({ blacklist, episodes, tracks, - episodeFiles, + trackFiles, albumHistory, releases, wanted, diff --git a/frontend/src/Store/Reducers/episodeFileReducers.js b/frontend/src/Store/Reducers/trackFileReducers.js similarity index 84% rename from frontend/src/Store/Reducers/episodeFileReducers.js rename to frontend/src/Store/Reducers/trackFileReducers.js index 904175863..0db17a70f 100644 --- a/frontend/src/Store/Reducers/episodeFileReducers.js +++ b/frontend/src/Store/Reducers/trackFileReducers.js @@ -16,19 +16,19 @@ export const defaultState = { items: [] }; -const reducerSection = 'episodeFiles'; +const reducerSection = 'trackFiles'; -const episodeFileReducers = handleActions({ +const trackFileReducers = handleActions({ [types.SET]: createSetReducer(reducerSection), [types.UPDATE]: createUpdateReducer(reducerSection), [types.UPDATE_ITEM]: createUpdateItemReducer(reducerSection), [types.REMOVE_ITEM]: createRemoveItemReducer(reducerSection), - [types.CLEAR_EPISODE_FILES]: (state) => { + [types.CLEAR_TRACK_FILES]: (state) => { return Object.assign({}, state, defaultState); } }, defaultState); -export default episodeFileReducers; +export default trackFileReducers; diff --git a/frontend/src/Store/Selectors/createEpisodeFileSelector.js b/frontend/src/Store/Selectors/createEpisodeFileSelector.js deleted file mode 100644 index 156c48d95..000000000 --- a/frontend/src/Store/Selectors/createEpisodeFileSelector.js +++ /dev/null @@ -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; diff --git a/frontend/src/Store/Selectors/createTrackFileSelector.js b/frontend/src/Store/Selectors/createTrackFileSelector.js new file mode 100644 index 000000000..fb55fdc94 --- /dev/null +++ b/frontend/src/Store/Selectors/createTrackFileSelector.js @@ -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; diff --git a/frontend/src/EpisodeFile/Editor/EpisodeFileEditorModal.js b/frontend/src/TrackFile/Editor/TrackFileEditorModal.js similarity index 64% rename from frontend/src/EpisodeFile/Editor/EpisodeFileEditorModal.js rename to frontend/src/TrackFile/Editor/TrackFileEditorModal.js index 35d00caf2..7f52aca05 100644 --- a/frontend/src/EpisodeFile/Editor/EpisodeFileEditorModal.js +++ b/frontend/src/TrackFile/Editor/TrackFileEditorModal.js @@ -1,9 +1,9 @@ import PropTypes from 'prop-types'; import React from 'react'; import Modal from 'Components/Modal/Modal'; -import EpisodeFileEditorModalContentConnector from './EpisodeFileEditorModalContentConnector'; +import TrackFileEditorModalContentConnector from './TrackFileEditorModalContentConnector'; -function EpisodeFileEditorModal(props) { +function TrackFileEditorModal(props) { const { isOpen, onModalClose, @@ -17,7 +17,7 @@ function EpisodeFileEditorModal(props) { > { isOpen && - @@ -26,9 +26,9 @@ function EpisodeFileEditorModal(props) { ); } -EpisodeFileEditorModal.propTypes = { +TrackFileEditorModal.propTypes = { isOpen: PropTypes.bool.isRequired, onModalClose: PropTypes.func.isRequired }; -export default EpisodeFileEditorModal; +export default TrackFileEditorModal; diff --git a/frontend/src/EpisodeFile/Editor/EpisodeFileEditorModalContent.css b/frontend/src/TrackFile/Editor/TrackFileEditorModalContent.css similarity index 100% rename from frontend/src/EpisodeFile/Editor/EpisodeFileEditorModalContent.css rename to frontend/src/TrackFile/Editor/TrackFileEditorModalContent.css diff --git a/frontend/src/EpisodeFile/Editor/EpisodeFileEditorModalContent.js b/frontend/src/TrackFile/Editor/TrackFileEditorModalContent.js similarity index 89% rename from frontend/src/EpisodeFile/Editor/EpisodeFileEditorModalContent.js rename to frontend/src/TrackFile/Editor/TrackFileEditorModalContent.js index 8c466798f..a1365dc99 100644 --- a/frontend/src/EpisodeFile/Editor/EpisodeFileEditorModalContent.js +++ b/frontend/src/TrackFile/Editor/TrackFileEditorModalContent.js @@ -15,13 +15,13 @@ import ModalBody from 'Components/Modal/ModalBody'; import ModalFooter from 'Components/Modal/ModalFooter'; import Table from 'Components/Table/Table'; import TableBody from 'Components/Table/TableBody'; -import EpisodeFileEditorRow from './EpisodeFileEditorRow'; -import styles from './EpisodeFileEditorModalContent.css'; +import TrackFileEditorRow from './TrackFileEditorRow'; +import styles from './TrackFileEditorModalContent.css'; const columns = [ { - name: 'episodeNumber', - label: 'Episode', + name: 'trackNumber', + label: 'Track', isVisible: true }, { @@ -29,11 +29,6 @@ const columns = [ label: 'Relative Path', isVisible: true }, - { - name: 'airDateUtc', - label: 'Air Date', - isVisible: true - }, { name: 'language', label: 'Language', @@ -46,7 +41,7 @@ const columns = [ } ]; -class EpisodeFileEditorModalContent extends Component { +class TrackFileEditorModalContent extends Component { // // Lifecycle @@ -76,7 +71,7 @@ class EpisodeFileEditorModalContent extends Component { const selectedIds = getSelectedIds(this.state.selectedState); 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, languages, qualities, - seriesType, onModalClose } = this.props; @@ -169,14 +163,14 @@ class EpisodeFileEditorModalContent extends Component { return ( - Manage Episodes + Manage Tracks { !items.length &&
- No episode files to manage. + No track files to manage.
} @@ -193,9 +187,8 @@ class EpisodeFileEditorModalContent extends Component { { items.map((item) => { return ( - seasonNumber, - (state) => state.episodes, - (state) => state.episodeFiles, + (state, { albumId }) => albumId, + (state) => state.tracks, + (state) => state.trackFiles, (state) => state.settings.languageProfiles.schema, (state) => state.settings.qualityProfiles.schema, createArtistSelector(), ( - seasonNumber, - episodes, - episodeFiles, + albumId, + tracks, + trackFiles, languageProfilesSchema, qualityProfileSchema, series ) => { - const filtered = _.filter(episodes.items, (episode) => { - if (seasonNumber >= 0 && episode.seasonNumber !== seasonNumber) { + const filtered = _.filter(tracks.items, (track) => { + if (albumId >= 0 && track.albumId !== albumId) { return false; } - if (!episode.episodeFileId) { + if (!track.trackFileId) { 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 episodeFile = _.find(episodeFiles.items, { id: episode.episodeFileId }); + const items = _.map(sorted, (track) => { + const trackFile = _.find(trackFiles.items, { id: track.trackFileId }); return { - relativePath: episodeFile.relativePath, - language: episodeFile.language, - quality: episodeFile.quality, - ...episode + relativePath: trackFile.relativePath, + language: trackFile.language, + quality: trackFile.quality, + ...track }; }); @@ -55,8 +56,8 @@ function createMapStateToProps() { return { items, seriesType: series.seriesType, - isDeleting: episodeFiles.isDeleting, - isSaving: episodeFiles.isSaving, + isDeleting: trackFiles.isDeleting, + isSaving: trackFiles.isSaving, languages, qualities }; @@ -66,6 +67,14 @@ function createMapStateToProps() { function createMapDispatchToProps(dispatch, props) { return { + dispatchClearTracks() { + dispatch(clearTracks()); + }, + + dispatchFetchTracks(updateProps) { + dispatch(fetchTracks(updateProps)); + }, + dispatchFetchLanguageProfileSchema(name, path) { dispatch(fetchLanguageProfileSchema()); }, @@ -74,16 +83,15 @@ function createMapDispatchToProps(dispatch, props) { dispatch(fetchQualityProfileSchema()); }, - dispatchUpdateEpisodeFiles(updateProps) { - dispatch(updateEpisodeFiles(updateProps)); + dispatchUpdateTrackFiles(updateProps) { + dispatch(updateTrackFiles(updateProps)); }, - onDeletePress(episodeFileIds) { - dispatch(deleteEpisodeFiles({ episodeFileIds })); + onDeletePress(trackFileIds) { + dispatch(deleteTrackFiles({ trackFileIds })); }, - - onQualityChange(episodeFileIds, qualityId) { + onQualityChange(trackFileIds, qualityId) { const quality = { quality: _.find(this.props.qualities, { id: qualityId }), 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 componentDidMount() { + const artistId = this.props.artistId; + + this.props.dispatchFetchTracks({ artistId }); + this.props.dispatchFetchLanguageProfileSchema(); this.props.dispatchFetchQualityProfileSchema(); } + componentWillUnmount() { + this.props.dispatchClearTracks(); + } + // // Render // // Listeners - onLanguageChange = (episodeFileIds, languageId) => { + onLanguageChange = (trackFileIds, 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 = { quality: _.find(this.props.qualities, { id: qualityId }), revision: { @@ -128,19 +144,21 @@ class EpisodeFileEditorModalContentConnector extends Component { } }; - this.props.dispatchUpdateEpisodeFiles({ episodeFileIds, quality }); + this.props.dispatchUpdateTrackFiles({ trackFileIds, quality }); } render() { const { dispatchFetchLanguageProfileSchema, dispatchFetchQualityProfileSchema, - dispatchUpdateEpisodeFiles, + dispatchUpdateTrackFiles, + dispatchFetchTracks, + dispatchClearTracks, ...otherProps } = this.props; return ( - - {seasonNumber}x{padNumber(episodeNumber, 2)} - - { - seriesType === 'anime' && !!absoluteEpisodeNumber && - - ({absoluteEpisodeNumber}) - - } + {padNumber(trackNumber, 2)} {relativePath} - - ); @@ -124,7 +124,7 @@ function CutoffUnmetRow(props) { > @@ -151,7 +151,7 @@ function CutoffUnmetRow(props) { CutoffUnmetRow.propTypes = { id: PropTypes.number.isRequired, - episodeFileId: PropTypes.number, + trackFileId: PropTypes.number, series: PropTypes.object.isRequired, seasonNumber: PropTypes.number.isRequired, episodeNumber: PropTypes.number.isRequired, diff --git a/frontend/src/Wanted/Missing/MissingRow.js b/frontend/src/Wanted/Missing/MissingRow.js index df3c80db8..e1e41cbbf 100644 --- a/frontend/src/Wanted/Missing/MissingRow.js +++ b/frontend/src/Wanted/Missing/MissingRow.js @@ -15,7 +15,7 @@ import styles from './MissingRow.css'; function MissingRow(props) { const { id, - // episodeFileId, + // trackFileId, artist, // seasonNumber, // episodeNumber, @@ -110,7 +110,7 @@ function MissingRow(props) { // > // // @@ -137,7 +137,7 @@ function MissingRow(props) { MissingRow.propTypes = { id: PropTypes.number.isRequired, - // episodeFileId: PropTypes.number, + // trackFileId: PropTypes.number, artist: PropTypes.object.isRequired, // seasonNumber: PropTypes.number.isRequired, // episodeNumber: PropTypes.number.isRequired, diff --git a/src/Lidarr.Api.V3/TrackFiles/TrackFileModule.cs b/src/Lidarr.Api.V3/TrackFiles/TrackFileModule.cs index eab63474c..2b37a887a 100644 --- a/src/Lidarr.Api.V3/TrackFiles/TrackFileModule.cs +++ b/src/Lidarr.Api.V3/TrackFiles/TrackFileModule.cs @@ -11,7 +11,6 @@ using NzbDrone.Core.MediaFiles.Events; using NzbDrone.Core.Messaging.Events; using NzbDrone.Core.Music; using NzbDrone.SignalR; -using Lidarr.Api.V3.Series; using Lidarr.Http; using Lidarr.Http.Extensions; using Lidarr.Http.REST; @@ -49,16 +48,16 @@ namespace Lidarr.Api.V3.TrackFiles UpdateResource = SetQuality; DeleteResource = DeleteTrackFile; - Put["/editor"] = episodeFiles => SetQuality(); - Delete["/bulk"] = episodeFiles => DeleteTrackFiles(); + Put["/editor"] = trackFiles => SetQuality(); + Delete["/bulk"] = trackFiles => DeleteTrackFiles(); } private TrackFileResource GetTrackFile(int 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 GetTrackFiles() @@ -72,7 +71,7 @@ namespace Lidarr.Api.V3.TrackFiles throw new BadRequestException("artistId, albumId, or trackFileIds must be provided"); } - if (artistIdQuery.HasValue) + if (artistIdQuery.HasValue && !albumIdQuery.HasValue) { int artistId = Convert.ToInt32(artistIdQuery.Value); var artist = _artistService.GetArtist(artistId); diff --git a/src/Lidarr.Api.V3/Tracks/TrackModule.cs b/src/Lidarr.Api.V3/Tracks/TrackModule.cs index 2e67a0921..32d761d9d 100644 --- a/src/Lidarr.Api.V3/Tracks/TrackModule.cs +++ b/src/Lidarr.Api.V3/Tracks/TrackModule.cs @@ -18,10 +18,10 @@ namespace Lidarr.Api.V3.Tracks IBroadcastSignalRMessage signalRBroadcaster) : base(trackService, artistService, upgradableSpecification, signalRBroadcaster) { - GetResourceAll = GetEpisodes; + GetResourceAll = GetTracks; } - private List GetEpisodes() + private List GetTracks() { var artistIdQuery = Request.Query.ArtistId; var albumIdQuery = Request.Query.AlbumId;