diff --git a/frontend/src/Episode/EpisodeDetailsModalContent.js b/frontend/src/Episode/EpisodeDetailsModalContent.js
index 15381fa20..45215cac0 100644
--- a/frontend/src/Episode/EpisodeDetailsModalContent.js
+++ b/frontend/src/Episode/EpisodeDetailsModalContent.js
@@ -188,7 +188,7 @@ EpisodeDetailsModalContent.propTypes = {
EpisodeDetailsModalContent.defaultProps = {
selectedTab: 'details',
- albumLabel: 'Unknown',
+ albumLabel: ['Unknown'],
episodeEntity: episodeEntities.EPISODES,
startInteractiveSearch: false
};
diff --git a/frontend/src/Episode/EpisodeDetailsModalContentConnector.js b/frontend/src/Episode/EpisodeDetailsModalContentConnector.js
index fb41d023b..86479b0b4 100644
--- a/frontend/src/Episode/EpisodeDetailsModalContentConnector.js
+++ b/frontend/src/Episode/EpisodeDetailsModalContentConnector.js
@@ -7,6 +7,8 @@ import { toggleEpisodeMonitored } from 'Store/Actions/episodeActions';
import createEpisodeSelector from 'Store/Selectors/createEpisodeSelector';
import createArtistSelector from 'Store/Selectors/createArtistSelector';
import episodeEntities from 'Episode/episodeEntities';
+import { fetchTracks, clearTracks } from 'Store/Actions/trackActions';
+import { fetchEpisodeFiles, clearEpisodeFiles } from 'Store/Actions/episodeFileActions';
import EpisodeDetailsModalContent from './EpisodeDetailsModalContent';
function createMapStateToProps() {
@@ -34,6 +36,10 @@ function createMapStateToProps() {
const mapDispatchToProps = {
clearReleases,
+ fetchTracks,
+ clearTracks,
+ fetchEpisodeFiles,
+ clearEpisodeFiles,
toggleEpisodeMonitored
};
@@ -41,14 +47,33 @@ class EpisodeDetailsModalContentConnector extends Component {
//
// Lifecycle
+ componentDidMount() {
+ this._populate();
+ }
componentWillUnmount() {
// Clear pending releases here so we can reshow the search
// results even after switching tabs.
-
+ this._unpopulate();
this.props.clearReleases();
}
+ //
+ // Control
+
+ _populate() {
+ const artistId = this.props.artistId;
+ const albumId = this.props.episodeId;
+ this.props.fetchTracks({ artistId, albumId });
+ // this.props.fetchEpisodeFiles({ artistId, albumId });
+ }
+
+ _unpopulate() {
+ this.props.clearTracks();
+ // this.props.clearEpisodeFiles();
+ }
+
+
//
// Listeners
@@ -82,6 +107,10 @@ EpisodeDetailsModalContentConnector.propTypes = {
episodeId: PropTypes.number.isRequired,
episodeEntity: PropTypes.string.isRequired,
artistId: PropTypes.number.isRequired,
+ fetchTracks: PropTypes.func.isRequired,
+ clearTracks: PropTypes.func.isRequired,
+ fetchEpisodeFiles: PropTypes.func.isRequired,
+ clearEpisodeFiles: PropTypes.func.isRequired,
clearReleases: PropTypes.func.isRequired,
toggleEpisodeMonitored: PropTypes.func.isRequired
};
diff --git a/frontend/src/Episode/EpisodeStatus.js b/frontend/src/Episode/EpisodeStatus.js
index aa0d923bd..7bf28a1f5 100644
--- a/frontend/src/Episode/EpisodeStatus.js
+++ b/frontend/src/Episode/EpisodeStatus.js
@@ -118,7 +118,7 @@ function EpisodeStatus(props) {
EpisodeStatus.propTypes = {
airDateUtc: PropTypes.string,
- monitored: PropTypes.bool.isRequired,
+ monitored: PropTypes.bool,
grabbed: PropTypes.bool,
queueItem: PropTypes.object,
episodeFile: PropTypes.object
diff --git a/frontend/src/Episode/Summary/EpisodeSummary.js b/frontend/src/Episode/Summary/EpisodeSummary.js
index 2b51ba7cf..695f08462 100644
--- a/frontend/src/Episode/Summary/EpisodeSummary.js
+++ b/frontend/src/Episode/Summary/EpisodeSummary.js
@@ -7,7 +7,10 @@ import ConfirmModal from 'Components/Modal/ConfirmModal';
import Label from 'Components/Label';
import QualityProfileNameConnector from 'Settings/Profiles/Quality/QualityProfileNameConnector';
import EpisodeQuality from 'Episode/EpisodeQuality';
+import Table from 'Components/Table/Table';
+import TableBody from 'Components/Table/TableBody';
import EpisodeAiringConnector from './EpisodeAiringConnector';
+import TrackDetailRow from './TrackDetailRow';
import styles from './EpisodeSummary.css';
class EpisodeSummary extends Component {
@@ -49,9 +52,11 @@ class EpisodeSummary extends Component {
releaseDate,
albumLabel,
path,
+ items,
size,
quality,
- qualityCutoffNotMet
+ qualityCutoffNotMet,
+ columns
} = this.props;
const hasOverview = !!overview;
@@ -88,53 +93,36 @@ class EpisodeSummary extends Component {
}
- {
- path &&
-
-
-
- Path
-
-
-
- Size
-
-
-
- Quality
-
-
-
-
-
-
-
- {path}
-
-
-
- {formatBytes(size)}
-
-
-
-
-
-
-
-
-
-
+
+ {
+
+ {
+ items.length ?
+
+
+ {
+ items.map((item) => {
+ return (
+
+ );
+ })
+ }
+
+
:
+
+
+ No tracks in this group
+
+ }
- }
+ }
+
episode,
+ (state) => state.tracks,
createEpisodeSelector(),
- createEpisodeFileSelector(),
- (series, episode, episodeFile) => {
- const {
- qualityProfileId,
- network
- } = series;
-
- const {
- airDateUtc,
- overview
- } = episode;
-
- const {
- path,
- size,
- quality,
- qualityCutoffNotMet
- } = episodeFile || {};
-
+ createCommandsSelector(),
+ createDimensionsSelector(),
+ (albumId, tracks, episode, commands, dimensions) => {
return {
- network,
- qualityProfileId,
- airDateUtc,
- overview,
- path,
- size,
- quality,
- qualityCutoffNotMet
+ network: episode.label,
+ qualityProfileId: episode.profileId,
+ airDateUtc: episode.releaseDate,
+ overview: episode.overview,
+ items: tracks.items,
+ columns: tracks.columns
};
}
);
diff --git a/frontend/src/Episode/Summary/TrackDetailRow.css b/frontend/src/Episode/Summary/TrackDetailRow.css
new file mode 100644
index 000000000..7dacb6a1e
--- /dev/null
+++ b/frontend/src/Episode/Summary/TrackDetailRow.css
@@ -0,0 +1,26 @@
+.title {
+ composes: cell from 'Components/Table/Cells/TableRowCell.css';
+
+ white-space: nowrap;
+}
+
+.monitored {
+ composes: cell from 'Components/Table/Cells/TableRowCell.css';
+
+ width: 42px;
+}
+
+.trackNumber {
+ composes: cell from 'Components/Table/Cells/TableRowCell.css';
+
+ width: 50px;
+}
+
+.language,
+.audio,
+.video,
+.status {
+ composes: cell from 'Components/Table/Cells/TableRowCell.css';
+
+ width: 100px;
+}
\ No newline at end of file
diff --git a/frontend/src/Episode/Summary/TrackDetailRow.js b/frontend/src/Episode/Summary/TrackDetailRow.js
new file mode 100644
index 000000000..3db53e693
--- /dev/null
+++ b/frontend/src/Episode/Summary/TrackDetailRow.js
@@ -0,0 +1,123 @@
+import PropTypes from 'prop-types';
+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 EpisodeStatusConnector from 'Episode/EpisodeStatusConnector';
+
+import styles from './TrackDetailRow.css';
+
+class TrackDetailRow extends Component {
+
+ //
+ // Lifecycle
+
+ //
+ // Listeners
+
+ //
+ // Render
+
+ render() {
+ const {
+ id,
+ title,
+ trackNumber,
+ duration,
+ columns,
+ trackFileId
+ } = this.props;
+
+ return (
+
+ {
+ columns.map((column) => {
+ const {
+ name,
+ isVisible
+ } = column;
+
+ if (!isVisible) {
+ return null;
+ }
+
+ if (name === 'trackNumber') {
+ return (
+
+ {trackNumber}
+
+ );
+ }
+
+ if (name === 'title') {
+ return (
+
+ {title}
+
+ );
+ }
+
+ if (name === 'duration') {
+ return (
+
+ {
+ formatTimeSpan(duration)
+ }
+
+ );
+ }
+
+ if (name === 'audioInfo') {
+ return (
+
+
+
+ );
+ }
+
+ if (name === 'status') {
+ return (
+
+
+
+ );
+ }
+
+ return null;
+ })
+ }
+
+ );
+ }
+}
+
+TrackDetailRow.propTypes = {
+ id: PropTypes.number.isRequired,
+ title: PropTypes.string.isRequired,
+ duration: PropTypes.number.isRequired,
+ trackFileId: PropTypes.number.isRequired,
+ columns: PropTypes.arrayOf(PropTypes.object).isRequired,
+ trackNumber: PropTypes.number.isRequired
+};
+
+export default TrackDetailRow;
diff --git a/frontend/src/Store/Reducers/trackReducers.js b/frontend/src/Store/Reducers/trackReducers.js
index bf17b4d5d..3f684fd9d 100644
--- a/frontend/src/Store/Reducers/trackReducers.js
+++ b/frontend/src/Store/Reducers/trackReducers.js
@@ -18,7 +18,7 @@ export const defaultState = {
columns: [
{
name: 'trackNumber',
- label: 'Track Number',
+ label: '#',
isVisible: true
},
{
@@ -31,6 +31,16 @@ export const defaultState = {
label: 'Duration',
isVisible: true
},
+ {
+ name: 'audioInfo',
+ label: 'Audio Info',
+ isVisible: true
+ },
+ {
+ name: 'status',
+ label: 'Status',
+ isVisible: true
+ },
{
name: 'actions',
columnLabel: 'Actions',
diff --git a/src/Lidarr.Api.V3/Albums/AlbumResource.cs b/src/Lidarr.Api.V3/Albums/AlbumResource.cs
index 3d13d276a..ddfc8c5b1 100644
--- a/src/Lidarr.Api.V3/Albums/AlbumResource.cs
+++ b/src/Lidarr.Api.V3/Albums/AlbumResource.cs
@@ -14,6 +14,7 @@ namespace Lidarr.Api.V3.Albums
public string Title { get; set; }
public int ArtistId { get; set; }
public List AlbumLabel { get; set; }
+ public string ForeignAlbumId { get; set; }
public bool Monitored { get; set; }
public string Path { get; set; }
public int ProfileId { get; set; }
@@ -42,6 +43,7 @@ namespace Lidarr.Api.V3.Albums
Id = model.Id,
ArtistId = model.ArtistId,
AlbumLabel = model.Label,
+ ForeignAlbumId = model.ForeignAlbumId,
Path = model.Path,
ProfileId = model.ProfileId,
Monitored = model.Monitored,
diff --git a/src/Lidarr.Api.V3/TrackFiles/TrackFileModule.cs b/src/Lidarr.Api.V3/TrackFiles/TrackFileModule.cs
index ab0ea125f..eab63474c 100644
--- a/src/Lidarr.Api.V3/TrackFiles/TrackFileModule.cs
+++ b/src/Lidarr.Api.V3/TrackFiles/TrackFileModule.cs
@@ -24,6 +24,7 @@ namespace Lidarr.Api.V3.TrackFiles
private readonly IMediaFileService _mediaFileService;
private readonly IRecycleBinProvider _recycleBinProvider;
private readonly IArtistService _artistService;
+ private readonly IAlbumService _albumService;
private readonly IUpgradableSpecification _upgradableSpecification;
private readonly Logger _logger;
@@ -31,6 +32,7 @@ namespace Lidarr.Api.V3.TrackFiles
IMediaFileService mediaFileService,
IRecycleBinProvider recycleBinProvider,
IArtistService artistService,
+ IAlbumService albumService,
IUpgradableSpecification upgradableSpecification,
Logger logger)
: base(signalRBroadcaster)
@@ -38,6 +40,7 @@ namespace Lidarr.Api.V3.TrackFiles
_mediaFileService = mediaFileService;
_recycleBinProvider = recycleBinProvider;
_artistService = artistService;
+ _albumService = albumService;
_upgradableSpecification = upgradableSpecification;
_logger = logger;
@@ -62,10 +65,11 @@ namespace Lidarr.Api.V3.TrackFiles
{
var artistIdQuery = Request.Query.ArtistId;
var trackFileIdsQuery = Request.Query.TrackFileIds;
+ var albumIdQuery = Request.Query.AlbumId;
- if (!artistIdQuery.HasValue && !trackFileIdsQuery.HasValue)
+ if (!artistIdQuery.HasValue && !trackFileIdsQuery.HasValue && !albumIdQuery.HasValue)
{
- throw new BadRequestException("artistId or trackFileIds must be provided");
+ throw new BadRequestException("artistId, albumId, or trackFileIds must be provided");
}
if (artistIdQuery.HasValue)
@@ -76,6 +80,14 @@ namespace Lidarr.Api.V3.TrackFiles
return _mediaFileService.GetFilesByArtist(artistId).ConvertAll(f => f.ToResource(artist, _upgradableSpecification));
}
+ if (albumIdQuery.HasValue)
+ {
+ int albumId = Convert.ToInt32(albumIdQuery.Value);
+ var album = _albumService.GetAlbum(albumId);
+
+ return _mediaFileService.GetFilesByAlbum(album.ArtistId, album.Id).ConvertAll(f => f.ToResource(album.Artist, _upgradableSpecification));
+ }
+
else
{
string episodeFileIdsValue = trackFileIdsQuery.Value.ToString();
diff --git a/src/Lidarr.Api.V3/Tracks/TrackModule.cs b/src/Lidarr.Api.V3/Tracks/TrackModule.cs
index ad3120515..2e67a0921 100644
--- a/src/Lidarr.Api.V3/Tracks/TrackModule.cs
+++ b/src/Lidarr.Api.V3/Tracks/TrackModule.cs
@@ -24,26 +24,28 @@ namespace Lidarr.Api.V3.Tracks
private List GetEpisodes()
{
var artistIdQuery = Request.Query.ArtistId;
+ var albumIdQuery = Request.Query.AlbumId;
var trackIdsQuery = Request.Query.TrackIds;
- if (!artistIdQuery.HasValue && !trackIdsQuery.HasValue)
+ if (!artistIdQuery.HasValue && !trackIdsQuery.HasValue && !albumIdQuery.HasValue)
{
throw new BadRequestException("artistId or trackIds must be provided");
}
- if (artistIdQuery.HasValue)
+ if (artistIdQuery.HasValue && !albumIdQuery.HasValue)
{
int artistId = Convert.ToInt32(artistIdQuery.Value);
- var albumId = Request.Query.AlbumId.HasValue ? (int)Request.Query.AlbumId : (int?)null;
-
- if (albumId.HasValue)
- {
- return MapToResource(_trackService.GetTracksByAlbum(artistId, albumId.Value), false, false);
- }
return MapToResource(_trackService.GetTracksByArtist(artistId), false, false);
}
+ if (albumIdQuery.HasValue)
+ {
+ int albumId = Convert.ToInt32(albumIdQuery.Value);
+
+ return MapToResource(_trackService.GetTracksByAlbum(albumId), false, false);
+ }
+
string trackIdsValue = trackIdsQuery.Value.ToString();
var trackIds = trackIdsValue.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries)
diff --git a/src/NzbDrone.Core/MediaFiles/RenameTrackFileService.cs b/src/NzbDrone.Core/MediaFiles/RenameTrackFileService.cs
index bea565262..8b1352d0d 100644
--- a/src/NzbDrone.Core/MediaFiles/RenameTrackFileService.cs
+++ b/src/NzbDrone.Core/MediaFiles/RenameTrackFileService.cs
@@ -1,4 +1,4 @@
-using System;
+using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
@@ -71,7 +71,7 @@ namespace NzbDrone.Core.MediaFiles
{
var artist = _artistService.GetArtist(artistId);
- var tracks = _trackService.GetTracksByAlbum(artistId, albumId);
+ var tracks = _trackService.GetTracksByAlbum(albumId);
var files = _mediaFileService.GetFilesByAlbum(artistId, albumId);
return GetPreviews(artist, tracks, files)
diff --git a/src/NzbDrone.Core/Music/AlbumMonitoredService.cs b/src/NzbDrone.Core/Music/AlbumMonitoredService.cs
index c19965f41..6ffb30971 100644
--- a/src/NzbDrone.Core/Music/AlbumMonitoredService.cs
+++ b/src/NzbDrone.Core/Music/AlbumMonitoredService.cs
@@ -1,4 +1,4 @@
-using System;
+using System;
using System.Collections.Generic;
using System.Linq;
using NLog;
@@ -57,7 +57,7 @@ namespace NzbDrone.Core.Music
foreach (var album in albums)
{
album.Monitored = monitored;
- var tracks = _trackService.GetTracksByAlbum(album.ArtistId, album.Id);
+ var tracks = _trackService.GetTracksByAlbum(album.Id);
foreach (var track in tracks)
{
track.Monitored = monitored;
diff --git a/src/NzbDrone.Core/Music/RefreshTrackService.cs b/src/NzbDrone.Core/Music/RefreshTrackService.cs
index e84ba318a..6e024c880 100644
--- a/src/NzbDrone.Core/Music/RefreshTrackService.cs
+++ b/src/NzbDrone.Core/Music/RefreshTrackService.cs
@@ -1,4 +1,4 @@
-using NLog;
+using NLog;
using NzbDrone.Common.Extensions;
using NzbDrone.Core.Messaging.Events;
using NzbDrone.Core.Music.Events;
@@ -37,7 +37,7 @@ namespace NzbDrone.Core.Music
album = _albumService.FindById(album.ForeignAlbumId);
- var existingTracks = _trackService.GetTracksByAlbum(album.ArtistId, album.Id);
+ var existingTracks = _trackService.GetTracksByAlbum(album.Id);
var updateList = new List