diff --git a/frontend/src/AlbumStudio/AlbumStudioConnector.js b/frontend/src/AlbumStudio/AlbumStudioConnector.js index ce82b2e76..aba720cb9 100644 --- a/frontend/src/AlbumStudio/AlbumStudioConnector.js +++ b/frontend/src/AlbumStudio/AlbumStudioConnector.js @@ -4,6 +4,7 @@ import { createSelector } from 'reselect'; import connectSection from 'Store/connectSection'; import createClientSideCollectionSelector from 'Store/Selectors/createClientSideCollectionSelector'; import { setAlbumStudioSort, setAlbumStudioFilter, saveAlbumStudio } from 'Store/Actions/albumStudioActions'; +import { fetchEpisodes, clearEpisodes } from 'Store/Actions/episodeActions'; import AlbumStudio from './AlbumStudio'; function createMapStateToProps() { @@ -18,6 +19,8 @@ function createMapStateToProps() { } const mapDispatchToProps = { + fetchEpisodes, + clearEpisodes, setAlbumStudioSort, setAlbumStudioFilter, saveAlbumStudio @@ -25,6 +28,28 @@ const mapDispatchToProps = { class AlbumStudioConnector extends Component { + // + // Lifecycle + + componentDidMount() { + this.populate(); + } + + componentWillUnmount() { + this.unpopulate(); + } + + // + // Control + + populate = () => { + this.props.fetchEpisodes(); + } + + unpopulate = () => { + this.props.clearEpisodes(); + } + // // Listeners @@ -58,6 +83,8 @@ class AlbumStudioConnector extends Component { AlbumStudioConnector.propTypes = { setAlbumStudioSort: PropTypes.func.isRequired, setAlbumStudioFilter: PropTypes.func.isRequired, + fetchEpisodes: PropTypes.func.isRequired, + clearEpisodes: PropTypes.func.isRequired, saveAlbumStudio: PropTypes.func.isRequired }; diff --git a/frontend/src/AlbumStudio/AlbumStudioRowConnector.js b/frontend/src/AlbumStudio/AlbumStudioRowConnector.js index 825fc6dac..fa54b9064 100644 --- a/frontend/src/AlbumStudio/AlbumStudioRowConnector.js +++ b/frontend/src/AlbumStudio/AlbumStudioRowConnector.js @@ -10,16 +10,22 @@ import AlbumStudioRow from './AlbumStudioRow'; function createMapStateToProps() { return createSelector( + (state) => state.episodes, createArtistSelector(), - (artist) => { - return _.pick(artist, [ - 'status', - 'nameSlug', - 'artistName', - 'monitored', - 'albums', - 'isSaving' - ]); + (episodes, artist) => { + const albumsInArtist = _.filter(episodes.items, { artistId: artist.id }); + const sortedAlbums = _.orderBy(albumsInArtist, 'releaseDate', 'desc'); + + return { + ...artist, + artistId: artist.id, + artistName: artist.artistName, + nameSlug: artist.nameSlug, + monitored: artist.monitored, + status: artist.status, + isSaving: artist.isSaving, + albums: sortedAlbums + }; } ); } @@ -50,7 +56,7 @@ class AlbumStudioRowConnector extends Component { onAlbumMonitoredPress = (albumId, monitored) => { this.props.toggleEpisodeMonitored({ albumId, - monitored: !monitored + monitored }); } diff --git a/frontend/src/Artist/Details/AlbumRow.js b/frontend/src/Artist/Details/AlbumRow.js index 36f0f3c89..0ae589625 100644 --- a/frontend/src/Artist/Details/AlbumRow.js +++ b/frontend/src/Artist/Details/AlbumRow.js @@ -135,7 +135,7 @@ class AlbumRow extends Component { return ( { - statistics.trackCount + statistics.totalTrackCount } ); diff --git a/frontend/src/Artist/Editor/ArtistEditorFooter.js b/frontend/src/Artist/Editor/ArtistEditorFooter.js index 82941a1c5..382c5d092 100644 --- a/frontend/src/Artist/Editor/ArtistEditorFooter.js +++ b/frontend/src/Artist/Editor/ArtistEditorFooter.js @@ -66,7 +66,7 @@ class ArtistEditorFooter extends Component { case 'monitored': this.props.onSaveSelected({ [name]: value === 'monitored' }); break; - case 'seasonFolder': + case 'albumFolder': this.props.onSaveSelected({ [name]: value === 'yes' }); break; default: diff --git a/frontend/src/Artist/Editor/ArtistEditorRow.js b/frontend/src/Artist/Editor/ArtistEditorRow.js index db2cfa281..ddfd8a893 100644 --- a/frontend/src/Artist/Editor/ArtistEditorRow.js +++ b/frontend/src/Artist/Editor/ArtistEditorRow.js @@ -16,7 +16,7 @@ class ArtistEditorRow extends Component { // // Listeners - onSeasonFolderChange = () => { + onAlbumFolderChange = () => { // Mock handler to satisfy `onChange` being required for `CheckInput`. // } @@ -77,7 +77,7 @@ class ArtistEditorRow extends Component { name="albumFolder" value={albumFolder} isDisabled={true} - onChange={this.onSeasonFolderChange} + onChange={this.onAlbumFolderChange} /> diff --git a/frontend/src/Store/Actions/actionTypes.js b/frontend/src/Store/Actions/actionTypes.js index 5ff4302ca..6dfddae70 100644 --- a/frontend/src/Store/Actions/actionTypes.js +++ b/frontend/src/Store/Actions/actionTypes.js @@ -71,9 +71,9 @@ export const BULK_DELETE_ARTIST = 'BULK_DELETE_ARTIST'; // // Season Pass -export const SET_SEASON_PASS_SORT = 'SET_SEASON_PASS_SORT'; -export const SET_SEASON_PASS_FILTER = 'SET_SEASON_PASS_FILTER'; -export const SAVE_SEASON_PASS = 'SAVE_SEASON_PASS'; +export const SET_ALBUM_STUDIO_SORT = 'SET_ALBUM_STUDIO_SORT'; +export const SET_ALBUM_STUDIO_FILTER = 'SET_ALBUM_STUDIO_FILTER'; +export const SAVE_ALBUM_STUDIO = 'SAVE_ALBUM_STUDIO'; // // Episodes diff --git a/frontend/src/Store/Actions/albumStudioActionHandlers.js b/frontend/src/Store/Actions/albumStudioActionHandlers.js index 0aa987dd2..b20b96704 100644 --- a/frontend/src/Store/Actions/albumStudioActionHandlers.js +++ b/frontend/src/Store/Actions/albumStudioActionHandlers.js @@ -8,7 +8,7 @@ import { fetchArtist } from './artistActions'; const section = 'albumStudio'; const albumStudioActionHandlers = { - [types.SAVE_SEASON_PASS]: function(payload) { + [types.SAVE_ALBUM_STUDIO]: function(payload) { return function(dispatch, getState) { const { artistIds, @@ -30,15 +30,15 @@ const albumStudioActionHandlers = { if (monitor) { const { - seasons, + albums, options: artistMonitoringOptions - } = getMonitoringOptions(_.cloneDeep(s.seasons), monitor); + } = getMonitoringOptions(_.cloneDeep(s.albums), monitor); if (!monitoringOptions) { monitoringOptions = artistMonitoringOptions; } - artistToUpdate.seasons = seasons; + artistToUpdate.albums = albums; } artist.push(artistToUpdate); diff --git a/frontend/src/Store/Actions/albumStudioActions.js b/frontend/src/Store/Actions/albumStudioActions.js index 4f447c457..7d90bbcc8 100644 --- a/frontend/src/Store/Actions/albumStudioActions.js +++ b/frontend/src/Store/Actions/albumStudioActions.js @@ -2,6 +2,6 @@ import { createAction } from 'redux-actions'; import * as types from './actionTypes'; import albumStudioActionHandlers from './albumStudioActionHandlers'; -export const setAlbumStudioSort = createAction(types.SET_SEASON_PASS_SORT); -export const setAlbumStudioFilter = createAction(types.SET_SEASON_PASS_FILTER); -export const saveAlbumStudio = albumStudioActionHandlers[types.SAVE_SEASON_PASS]; +export const setAlbumStudioSort = createAction(types.SET_ALBUM_STUDIO_SORT); +export const setAlbumStudioFilter = createAction(types.SET_ALBUM_STUDIO_FILTER); +export const saveAlbumStudio = albumStudioActionHandlers[types.SAVE_ALBUM_STUDIO]; diff --git a/frontend/src/Store/Actions/episodeActionHandlers.js b/frontend/src/Store/Actions/episodeActionHandlers.js index f42034b21..b74f5c3fa 100644 --- a/frontend/src/Store/Actions/episodeActionHandlers.js +++ b/frontend/src/Store/Actions/episodeActionHandlers.js @@ -14,7 +14,7 @@ const episodeActionHandlers = { [types.TOGGLE_EPISODE_MONITORED]: function(payload) { return function(dispatch, getState) { const { - albumId: id, + albumId, episodeEntity = episodeEntities.EPISODES, monitored } = payload; @@ -22,13 +22,13 @@ const episodeActionHandlers = { const episodeSection = _.last(episodeEntity.split('.')); dispatch(updateItem({ - id, + id: albumId, section: episodeSection, isSaving: true })); const promise = $.ajax({ - url: `/album/${id}`, + url: `/album/${albumId}`, method: 'PUT', data: JSON.stringify({ monitored }), dataType: 'json' @@ -36,7 +36,7 @@ const episodeActionHandlers = { promise.done((data) => { dispatch(updateItem({ - id, + id: albumId, section: episodeSection, isSaving: false, monitored @@ -45,7 +45,7 @@ const episodeActionHandlers = { promise.fail((xhr) => { dispatch(updateItem({ - id, + id: albumId, section: episodeSection, isSaving: false })); diff --git a/frontend/src/Store/Reducers/albumStudioReducers.js b/frontend/src/Store/Reducers/albumStudioReducers.js index aa9e523a1..658d02a4e 100644 --- a/frontend/src/Store/Reducers/albumStudioReducers.js +++ b/frontend/src/Store/Reducers/albumStudioReducers.js @@ -31,8 +31,8 @@ const albumStudioReducers = handleActions({ [types.SET]: createSetReducer(reducerSection), - [types.SET_SEASON_PASS_SORT]: createSetClientSideCollectionSortReducer(reducerSection), - [types.SET_SEASON_PASS_FILTER]: createSetClientSideCollectionFilterReducer(reducerSection) + [types.SET_ALBUM_STUDIO_SORT]: createSetClientSideCollectionSortReducer(reducerSection), + [types.SET_ALBUM_STUDIO_FILTER]: createSetClientSideCollectionFilterReducer(reducerSection) }, defaultState); diff --git a/frontend/src/Utilities/Series/getMonitoringOptions.js b/frontend/src/Utilities/Series/getMonitoringOptions.js index d36b7dcfd..741236f8d 100644 --- a/frontend/src/Utilities/Series/getMonitoringOptions.js +++ b/frontend/src/Utilities/Series/getMonitoringOptions.js @@ -10,10 +10,10 @@ function monitorSeasons(seasons, startingSeason) { }); } -function getMonitoringOptions(seasons, monitor) { - if (!seasons.length) { +function getMonitoringOptions(albums, monitor) { + if (!albums.length) { return { - seasons: [], + albums: [], options: { ignoreEpisodesWithFiles: false, ignoreEpisodesWithoutFiles: false @@ -21,10 +21,10 @@ function getMonitoringOptions(seasons, monitor) { }; } - const firstSeason = _.minBy(_.reject(seasons, { seasonNumber: 0 }), 'seasonNumber').seasonNumber; - const lastSeason = _.maxBy(seasons, 'seasonNumber').seasonNumber; + const firstSeason = _.minBy(_.reject(albums, { seasonNumber: 0 }), 'seasonNumber').seasonNumber; + const lastSeason = _.maxBy(albums, 'seasonNumber').seasonNumber; - monitorSeasons(seasons, firstSeason); + monitorSeasons(albums, firstSeason); const monitoringOptions = { ignoreEpisodesWithFiles: false, @@ -37,11 +37,11 @@ function getMonitoringOptions(seasons, monitor) { monitoringOptions.ignoreEpisodesWithoutFiles = true; break; case 'latest': - monitorSeasons(seasons, lastSeason); + monitorSeasons(albums, lastSeason); break; case 'first': - monitorSeasons(seasons, lastSeason + 1); - _.find(seasons, { seasonNumber: firstSeason }).monitored = true; + monitorSeasons(albums, lastSeason + 1); + _.find(albums, { seasonNumber: firstSeason }).monitored = true; break; case 'missing': monitoringOptions.ignoreEpisodesWithFiles = true; @@ -50,14 +50,14 @@ function getMonitoringOptions(seasons, monitor) { monitoringOptions.ignoreEpisodesWithoutFiles = true; break; case 'none': - monitorSeasons(seasons, lastSeason + 1); + monitorSeasons(albums, lastSeason + 1); break; default: break; } return { - seasons: _.map(seasons, (season) => { + seasons: _.map(albums, (season) => { return _.pick(season, [ 'seasonNumber', 'monitored' diff --git a/src/Lidarr.Api.V3/Albums/AlbumModule.cs b/src/Lidarr.Api.V3/Albums/AlbumModule.cs index 975bfab70..6b5e47d31 100644 --- a/src/Lidarr.Api.V3/Albums/AlbumModule.cs +++ b/src/Lidarr.Api.V3/Albums/AlbumModule.cs @@ -32,7 +32,7 @@ namespace Lidarr.Api.V3.Albums if (!Request.Query.ArtistId.HasValue && !albumIdsQuery.HasValue) { - throw new BadRequestException("artistId or albumIds must be provided"); + return MapToResource(_albumService.GetAllAlbums(), false); } if (artistIdQuery.HasValue) diff --git a/src/Lidarr.Api.V3/Artist/ArtistEditorModule.cs b/src/Lidarr.Api.V3/Artist/ArtistEditorModule.cs index 235952e40..2cbdd8046 100644 --- a/src/Lidarr.Api.V3/Artist/ArtistEditorModule.cs +++ b/src/Lidarr.Api.V3/Artist/ArtistEditorModule.cs @@ -15,7 +15,7 @@ namespace Lidarr.Api.V3.Artist { _artistService = artistService; Put["/"] = artist => SaveAll(); - Delete["/"] = artist => DeleteSeries(); + Delete["/"] = artist => DeleteArtist(); } private Response SaveAll() @@ -70,7 +70,7 @@ namespace Lidarr.Api.V3.Artist .AsResponse(HttpStatusCode.Accepted); } - private Response DeleteSeries() + private Response DeleteArtist() { var resource = Request.Body.FromJson(); diff --git a/src/Lidarr.Api.V3/Artist/ArtistModule.cs b/src/Lidarr.Api.V3/Artist/ArtistModule.cs index 0b8707917..175306972 100644 --- a/src/Lidarr.Api.V3/Artist/ArtistModule.cs +++ b/src/Lidarr.Api.V3/Artist/ArtistModule.cs @@ -99,7 +99,7 @@ namespace Lidarr.Api.V3.Artist var resource = artist.ToResource(); MapCoversToLocal(resource); - MapAlbums(resource); + //MapAlbums(resource); FetchAndLinkArtistStatistics(resource); //PopulateAlternateTitles(resource); @@ -112,7 +112,7 @@ namespace Lidarr.Api.V3.Artist var artistsResources = _artistService.GetAllArtists().ToResource(); MapCoversToLocal(artistsResources.ToArray()); - MapAlbums(artistsResources.ToArray()); + //MapAlbums(artistsResources.ToArray()); LinkArtistStatistics(artistsResources, artistStats); //PopulateAlternateTitles(seriesResources); diff --git a/src/NzbDrone.Core/Music/AlbumService.cs b/src/NzbDrone.Core/Music/AlbumService.cs index 209446a58..0e233968d 100644 --- a/src/NzbDrone.Core/Music/AlbumService.cs +++ b/src/NzbDrone.Core/Music/AlbumService.cs @@ -164,12 +164,28 @@ namespace NzbDrone.Core.Music var album = _albumRepository.Get(albumId); _albumRepository.SetMonitoredFlat(album, monitored); + var tracks = _trackService.GetTracksByAlbum(albumId); + foreach (var track in tracks) + { + track.Monitored = monitored; + } + _trackService.UpdateTracks(tracks); + _logger.Debug("Monitored flag for Album:{0} was set to {1}", albumId, monitored); } public void SetMonitored(IEnumerable ids, bool monitored) { _albumRepository.SetMonitored(ids, monitored); + foreach (var id in ids) + { + var tracks = _trackService.GetTracksByAlbum(id); + foreach (var track in tracks) + { + track.Monitored = monitored; + } + _trackService.UpdateTracks(tracks); + } } public List UpdateAlbums(List album)