mirror of
https://github.com/lidarr/lidarr.git
synced 2025-07-12 08:07:10 -07:00
Add Tracklist to Album Modal
This commit is contained in:
parent
90d9741056
commit
d243a8c8c4
16 changed files with 283 additions and 104 deletions
|
@ -188,7 +188,7 @@ EpisodeDetailsModalContent.propTypes = {
|
||||||
|
|
||||||
EpisodeDetailsModalContent.defaultProps = {
|
EpisodeDetailsModalContent.defaultProps = {
|
||||||
selectedTab: 'details',
|
selectedTab: 'details',
|
||||||
albumLabel: 'Unknown',
|
albumLabel: ['Unknown'],
|
||||||
episodeEntity: episodeEntities.EPISODES,
|
episodeEntity: episodeEntities.EPISODES,
|
||||||
startInteractiveSearch: false
|
startInteractiveSearch: false
|
||||||
};
|
};
|
||||||
|
|
|
@ -7,6 +7,8 @@ import { toggleEpisodeMonitored } from 'Store/Actions/episodeActions';
|
||||||
import createEpisodeSelector from 'Store/Selectors/createEpisodeSelector';
|
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 { fetchEpisodeFiles, clearEpisodeFiles } from 'Store/Actions/episodeFileActions';
|
||||||
import EpisodeDetailsModalContent from './EpisodeDetailsModalContent';
|
import EpisodeDetailsModalContent from './EpisodeDetailsModalContent';
|
||||||
|
|
||||||
function createMapStateToProps() {
|
function createMapStateToProps() {
|
||||||
|
@ -34,6 +36,10 @@ function createMapStateToProps() {
|
||||||
|
|
||||||
const mapDispatchToProps = {
|
const mapDispatchToProps = {
|
||||||
clearReleases,
|
clearReleases,
|
||||||
|
fetchTracks,
|
||||||
|
clearTracks,
|
||||||
|
fetchEpisodeFiles,
|
||||||
|
clearEpisodeFiles,
|
||||||
toggleEpisodeMonitored
|
toggleEpisodeMonitored
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -41,14 +47,33 @@ class EpisodeDetailsModalContentConnector extends Component {
|
||||||
|
|
||||||
//
|
//
|
||||||
// Lifecycle
|
// Lifecycle
|
||||||
|
componentDidMount() {
|
||||||
|
this._populate();
|
||||||
|
}
|
||||||
|
|
||||||
componentWillUnmount() {
|
componentWillUnmount() {
|
||||||
// Clear pending releases here so we can reshow the search
|
// Clear pending releases here so we can reshow the search
|
||||||
// results even after switching tabs.
|
// results even after switching tabs.
|
||||||
|
this._unpopulate();
|
||||||
this.props.clearReleases();
|
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
|
// Listeners
|
||||||
|
|
||||||
|
@ -82,6 +107,10 @@ EpisodeDetailsModalContentConnector.propTypes = {
|
||||||
episodeId: PropTypes.number.isRequired,
|
episodeId: PropTypes.number.isRequired,
|
||||||
episodeEntity: PropTypes.string.isRequired,
|
episodeEntity: PropTypes.string.isRequired,
|
||||||
artistId: PropTypes.number.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,
|
clearReleases: PropTypes.func.isRequired,
|
||||||
toggleEpisodeMonitored: PropTypes.func.isRequired
|
toggleEpisodeMonitored: PropTypes.func.isRequired
|
||||||
};
|
};
|
||||||
|
|
|
@ -118,7 +118,7 @@ function EpisodeStatus(props) {
|
||||||
|
|
||||||
EpisodeStatus.propTypes = {
|
EpisodeStatus.propTypes = {
|
||||||
airDateUtc: PropTypes.string,
|
airDateUtc: PropTypes.string,
|
||||||
monitored: PropTypes.bool.isRequired,
|
monitored: PropTypes.bool,
|
||||||
grabbed: PropTypes.bool,
|
grabbed: PropTypes.bool,
|
||||||
queueItem: PropTypes.object,
|
queueItem: PropTypes.object,
|
||||||
episodeFile: PropTypes.object
|
episodeFile: PropTypes.object
|
||||||
|
|
|
@ -7,7 +7,10 @@ import ConfirmModal from 'Components/Modal/ConfirmModal';
|
||||||
import Label from 'Components/Label';
|
import Label from 'Components/Label';
|
||||||
import QualityProfileNameConnector from 'Settings/Profiles/Quality/QualityProfileNameConnector';
|
import QualityProfileNameConnector from 'Settings/Profiles/Quality/QualityProfileNameConnector';
|
||||||
import EpisodeQuality from 'Episode/EpisodeQuality';
|
import EpisodeQuality from 'Episode/EpisodeQuality';
|
||||||
|
import Table from 'Components/Table/Table';
|
||||||
|
import TableBody from 'Components/Table/TableBody';
|
||||||
import EpisodeAiringConnector from './EpisodeAiringConnector';
|
import EpisodeAiringConnector from './EpisodeAiringConnector';
|
||||||
|
import TrackDetailRow from './TrackDetailRow';
|
||||||
import styles from './EpisodeSummary.css';
|
import styles from './EpisodeSummary.css';
|
||||||
|
|
||||||
class EpisodeSummary extends Component {
|
class EpisodeSummary extends Component {
|
||||||
|
@ -49,9 +52,11 @@ class EpisodeSummary extends Component {
|
||||||
releaseDate,
|
releaseDate,
|
||||||
albumLabel,
|
albumLabel,
|
||||||
path,
|
path,
|
||||||
|
items,
|
||||||
size,
|
size,
|
||||||
quality,
|
quality,
|
||||||
qualityCutoffNotMet
|
qualityCutoffNotMet,
|
||||||
|
columns
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
|
||||||
const hasOverview = !!overview;
|
const hasOverview = !!overview;
|
||||||
|
@ -88,53 +93,36 @@ class EpisodeSummary extends Component {
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{
|
<div>
|
||||||
path &&
|
{
|
||||||
<div className={styles.files}>
|
<div className={styles.episodes}>
|
||||||
<div className={styles.filesHeader}>
|
{
|
||||||
<div className={styles.path}>
|
items.length ?
|
||||||
Path
|
<Table
|
||||||
</div>
|
columns={columns}
|
||||||
|
>
|
||||||
|
<TableBody>
|
||||||
|
{
|
||||||
|
items.map((item) => {
|
||||||
|
return (
|
||||||
|
<TrackDetailRow
|
||||||
|
key={item.id}
|
||||||
|
columns={columns}
|
||||||
|
{...item}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
})
|
||||||
|
}
|
||||||
|
</TableBody>
|
||||||
|
</Table> :
|
||||||
|
|
||||||
<div className={styles.size}>
|
<div className={styles.noEpisodes}>
|
||||||
Size
|
No tracks in this group
|
||||||
</div>
|
</div>
|
||||||
|
}
|
||||||
<div className={styles.quality}>
|
|
||||||
Quality
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className={styles.actions}></div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className={styles.fileRow}>
|
|
||||||
<div
|
|
||||||
className={styles.path}
|
|
||||||
title={path}
|
|
||||||
>
|
|
||||||
{path}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className={styles.size}>
|
|
||||||
{formatBytes(size)}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className={styles.quality}>
|
|
||||||
<EpisodeQuality
|
|
||||||
quality={quality}
|
|
||||||
isCutoffNotMet={qualityCutoffNotMet}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className={styles.actions}>
|
|
||||||
<IconButton
|
|
||||||
name={icons.REMOVE}
|
|
||||||
onPress={this.onRemoveEpisodeFilePress}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
</div>
|
||||||
|
|
||||||
<ConfirmModal
|
<ConfirmModal
|
||||||
isOpen={this.state.isRemoveEpisodeFileModalOpen}
|
isOpen={this.state.isRemoveEpisodeFileModalOpen}
|
||||||
|
@ -155,6 +143,8 @@ EpisodeSummary.propTypes = {
|
||||||
overview: PropTypes.string,
|
overview: PropTypes.string,
|
||||||
albumLabel: PropTypes.arrayOf(PropTypes.string),
|
albumLabel: PropTypes.arrayOf(PropTypes.string),
|
||||||
releaseDate: PropTypes.string.isRequired,
|
releaseDate: PropTypes.string.isRequired,
|
||||||
|
items: PropTypes.arrayOf(PropTypes.object).isRequired,
|
||||||
|
columns: PropTypes.arrayOf(PropTypes.object).isRequired,
|
||||||
path: PropTypes.string,
|
path: PropTypes.string,
|
||||||
size: PropTypes.number,
|
size: PropTypes.number,
|
||||||
quality: PropTypes.object,
|
quality: PropTypes.object,
|
||||||
|
|
|
@ -2,42 +2,28 @@ import { connect } from 'react-redux';
|
||||||
import { createSelector } from 'reselect';
|
import { createSelector } from 'reselect';
|
||||||
import { deleteEpisodeFile } from 'Store/Actions/episodeFileActions';
|
import { deleteEpisodeFile } from 'Store/Actions/episodeFileActions';
|
||||||
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 createEpisodeFileSelector from 'Store/Selectors/createEpisodeFileSelector';
|
||||||
|
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 EpisodeSummary from './EpisodeSummary';
|
import EpisodeSummary from './EpisodeSummary';
|
||||||
|
|
||||||
function createMapStateToProps() {
|
function createMapStateToProps() {
|
||||||
return createSelector(
|
return createSelector(
|
||||||
createArtistSelector(),
|
(state, { episode }) => episode,
|
||||||
|
(state) => state.tracks,
|
||||||
createEpisodeSelector(),
|
createEpisodeSelector(),
|
||||||
createEpisodeFileSelector(),
|
createCommandsSelector(),
|
||||||
(series, episode, episodeFile) => {
|
createDimensionsSelector(),
|
||||||
const {
|
(albumId, tracks, episode, commands, dimensions) => {
|
||||||
qualityProfileId,
|
|
||||||
network
|
|
||||||
} = series;
|
|
||||||
|
|
||||||
const {
|
|
||||||
airDateUtc,
|
|
||||||
overview
|
|
||||||
} = episode;
|
|
||||||
|
|
||||||
const {
|
|
||||||
path,
|
|
||||||
size,
|
|
||||||
quality,
|
|
||||||
qualityCutoffNotMet
|
|
||||||
} = episodeFile || {};
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
network,
|
network: episode.label,
|
||||||
qualityProfileId,
|
qualityProfileId: episode.profileId,
|
||||||
airDateUtc,
|
airDateUtc: episode.releaseDate,
|
||||||
overview,
|
overview: episode.overview,
|
||||||
path,
|
items: tracks.items,
|
||||||
size,
|
columns: tracks.columns
|
||||||
quality,
|
|
||||||
qualityCutoffNotMet
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
26
frontend/src/Episode/Summary/TrackDetailRow.css
Normal file
26
frontend/src/Episode/Summary/TrackDetailRow.css
Normal file
|
@ -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;
|
||||||
|
}
|
123
frontend/src/Episode/Summary/TrackDetailRow.js
Normal file
123
frontend/src/Episode/Summary/TrackDetailRow.js
Normal file
|
@ -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 (
|
||||||
|
<TableRow>
|
||||||
|
{
|
||||||
|
columns.map((column) => {
|
||||||
|
const {
|
||||||
|
name,
|
||||||
|
isVisible
|
||||||
|
} = column;
|
||||||
|
|
||||||
|
if (!isVisible) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (name === 'trackNumber') {
|
||||||
|
return (
|
||||||
|
<TableRowCell
|
||||||
|
key={name}
|
||||||
|
className={styles.trackNumber}
|
||||||
|
>
|
||||||
|
{trackNumber}
|
||||||
|
</TableRowCell>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (name === 'title') {
|
||||||
|
return (
|
||||||
|
<TableRowCell
|
||||||
|
key={name}
|
||||||
|
className={styles.title}
|
||||||
|
>
|
||||||
|
{title}
|
||||||
|
</TableRowCell>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (name === 'duration') {
|
||||||
|
return (
|
||||||
|
<TableRowCell key={name}>
|
||||||
|
{
|
||||||
|
formatTimeSpan(duration)
|
||||||
|
}
|
||||||
|
</TableRowCell>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (name === 'audioInfo') {
|
||||||
|
return (
|
||||||
|
<TableRowCell
|
||||||
|
key={name}
|
||||||
|
className={styles.audio}
|
||||||
|
>
|
||||||
|
<MediaInfoConnector
|
||||||
|
type={mediaInfoTypes.AUDIO}
|
||||||
|
episodeFileId={trackFileId}
|
||||||
|
/>
|
||||||
|
</TableRowCell>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (name === 'status') {
|
||||||
|
return (
|
||||||
|
<TableRowCell
|
||||||
|
key={name}
|
||||||
|
className={styles.status}
|
||||||
|
>
|
||||||
|
<EpisodeStatusConnector
|
||||||
|
episodeId={id}
|
||||||
|
episodeFileId={trackFileId}
|
||||||
|
/>
|
||||||
|
</TableRowCell>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
})
|
||||||
|
}
|
||||||
|
</TableRow>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
|
@ -18,7 +18,7 @@ export const defaultState = {
|
||||||
columns: [
|
columns: [
|
||||||
{
|
{
|
||||||
name: 'trackNumber',
|
name: 'trackNumber',
|
||||||
label: 'Track Number',
|
label: '#',
|
||||||
isVisible: true
|
isVisible: true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -31,6 +31,16 @@ export const defaultState = {
|
||||||
label: 'Duration',
|
label: 'Duration',
|
||||||
isVisible: true
|
isVisible: true
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: 'audioInfo',
|
||||||
|
label: 'Audio Info',
|
||||||
|
isVisible: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'status',
|
||||||
|
label: 'Status',
|
||||||
|
isVisible: true
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: 'actions',
|
name: 'actions',
|
||||||
columnLabel: 'Actions',
|
columnLabel: 'Actions',
|
||||||
|
|
|
@ -14,6 +14,7 @@ namespace Lidarr.Api.V3.Albums
|
||||||
public string Title { get; set; }
|
public string Title { get; set; }
|
||||||
public int ArtistId { get; set; }
|
public int ArtistId { get; set; }
|
||||||
public List<string> AlbumLabel { get; set; }
|
public List<string> AlbumLabel { get; set; }
|
||||||
|
public string ForeignAlbumId { get; set; }
|
||||||
public bool Monitored { get; set; }
|
public bool Monitored { get; set; }
|
||||||
public string Path { get; set; }
|
public string Path { get; set; }
|
||||||
public int ProfileId { get; set; }
|
public int ProfileId { get; set; }
|
||||||
|
@ -42,6 +43,7 @@ namespace Lidarr.Api.V3.Albums
|
||||||
Id = model.Id,
|
Id = model.Id,
|
||||||
ArtistId = model.ArtistId,
|
ArtistId = model.ArtistId,
|
||||||
AlbumLabel = model.Label,
|
AlbumLabel = model.Label,
|
||||||
|
ForeignAlbumId = model.ForeignAlbumId,
|
||||||
Path = model.Path,
|
Path = model.Path,
|
||||||
ProfileId = model.ProfileId,
|
ProfileId = model.ProfileId,
|
||||||
Monitored = model.Monitored,
|
Monitored = model.Monitored,
|
||||||
|
|
|
@ -24,6 +24,7 @@ namespace Lidarr.Api.V3.TrackFiles
|
||||||
private readonly IMediaFileService _mediaFileService;
|
private readonly IMediaFileService _mediaFileService;
|
||||||
private readonly IRecycleBinProvider _recycleBinProvider;
|
private readonly IRecycleBinProvider _recycleBinProvider;
|
||||||
private readonly IArtistService _artistService;
|
private readonly IArtistService _artistService;
|
||||||
|
private readonly IAlbumService _albumService;
|
||||||
private readonly IUpgradableSpecification _upgradableSpecification;
|
private readonly IUpgradableSpecification _upgradableSpecification;
|
||||||
private readonly Logger _logger;
|
private readonly Logger _logger;
|
||||||
|
|
||||||
|
@ -31,6 +32,7 @@ namespace Lidarr.Api.V3.TrackFiles
|
||||||
IMediaFileService mediaFileService,
|
IMediaFileService mediaFileService,
|
||||||
IRecycleBinProvider recycleBinProvider,
|
IRecycleBinProvider recycleBinProvider,
|
||||||
IArtistService artistService,
|
IArtistService artistService,
|
||||||
|
IAlbumService albumService,
|
||||||
IUpgradableSpecification upgradableSpecification,
|
IUpgradableSpecification upgradableSpecification,
|
||||||
Logger logger)
|
Logger logger)
|
||||||
: base(signalRBroadcaster)
|
: base(signalRBroadcaster)
|
||||||
|
@ -38,6 +40,7 @@ namespace Lidarr.Api.V3.TrackFiles
|
||||||
_mediaFileService = mediaFileService;
|
_mediaFileService = mediaFileService;
|
||||||
_recycleBinProvider = recycleBinProvider;
|
_recycleBinProvider = recycleBinProvider;
|
||||||
_artistService = artistService;
|
_artistService = artistService;
|
||||||
|
_albumService = albumService;
|
||||||
_upgradableSpecification = upgradableSpecification;
|
_upgradableSpecification = upgradableSpecification;
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
|
|
||||||
|
@ -62,10 +65,11 @@ namespace Lidarr.Api.V3.TrackFiles
|
||||||
{
|
{
|
||||||
var artistIdQuery = Request.Query.ArtistId;
|
var artistIdQuery = Request.Query.ArtistId;
|
||||||
var trackFileIdsQuery = Request.Query.TrackFileIds;
|
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)
|
if (artistIdQuery.HasValue)
|
||||||
|
@ -76,6 +80,14 @@ namespace Lidarr.Api.V3.TrackFiles
|
||||||
return _mediaFileService.GetFilesByArtist(artistId).ConvertAll(f => f.ToResource(artist, _upgradableSpecification));
|
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
|
else
|
||||||
{
|
{
|
||||||
string episodeFileIdsValue = trackFileIdsQuery.Value.ToString();
|
string episodeFileIdsValue = trackFileIdsQuery.Value.ToString();
|
||||||
|
|
|
@ -24,26 +24,28 @@ namespace Lidarr.Api.V3.Tracks
|
||||||
private List<TrackResource> GetEpisodes()
|
private List<TrackResource> GetEpisodes()
|
||||||
{
|
{
|
||||||
var artistIdQuery = Request.Query.ArtistId;
|
var artistIdQuery = Request.Query.ArtistId;
|
||||||
|
var albumIdQuery = Request.Query.AlbumId;
|
||||||
var trackIdsQuery = Request.Query.TrackIds;
|
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");
|
throw new BadRequestException("artistId or trackIds must be provided");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (artistIdQuery.HasValue)
|
if (artistIdQuery.HasValue && !albumIdQuery.HasValue)
|
||||||
{
|
{
|
||||||
int artistId = Convert.ToInt32(artistIdQuery.Value);
|
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);
|
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();
|
string trackIdsValue = trackIdsQuery.Value.ToString();
|
||||||
|
|
||||||
var trackIds = trackIdsValue.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries)
|
var trackIds = trackIdsValue.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries)
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
@ -71,7 +71,7 @@ namespace NzbDrone.Core.MediaFiles
|
||||||
{
|
{
|
||||||
|
|
||||||
var artist = _artistService.GetArtist(artistId);
|
var artist = _artistService.GetArtist(artistId);
|
||||||
var tracks = _trackService.GetTracksByAlbum(artistId, albumId);
|
var tracks = _trackService.GetTracksByAlbum(albumId);
|
||||||
var files = _mediaFileService.GetFilesByAlbum(artistId, albumId);
|
var files = _mediaFileService.GetFilesByAlbum(artistId, albumId);
|
||||||
|
|
||||||
return GetPreviews(artist, tracks, files)
|
return GetPreviews(artist, tracks, files)
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using NLog;
|
using NLog;
|
||||||
|
@ -57,7 +57,7 @@ namespace NzbDrone.Core.Music
|
||||||
foreach (var album in albums)
|
foreach (var album in albums)
|
||||||
{
|
{
|
||||||
album.Monitored = monitored;
|
album.Monitored = monitored;
|
||||||
var tracks = _trackService.GetTracksByAlbum(album.ArtistId, album.Id);
|
var tracks = _trackService.GetTracksByAlbum(album.Id);
|
||||||
foreach (var track in tracks)
|
foreach (var track in tracks)
|
||||||
{
|
{
|
||||||
track.Monitored = monitored;
|
track.Monitored = monitored;
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
using NLog;
|
using NLog;
|
||||||
using NzbDrone.Common.Extensions;
|
using NzbDrone.Common.Extensions;
|
||||||
using NzbDrone.Core.Messaging.Events;
|
using NzbDrone.Core.Messaging.Events;
|
||||||
using NzbDrone.Core.Music.Events;
|
using NzbDrone.Core.Music.Events;
|
||||||
|
@ -37,7 +37,7 @@ namespace NzbDrone.Core.Music
|
||||||
|
|
||||||
album = _albumService.FindById(album.ForeignAlbumId);
|
album = _albumService.FindById(album.ForeignAlbumId);
|
||||||
|
|
||||||
var existingTracks = _trackService.GetTracksByAlbum(album.ArtistId, album.Id);
|
var existingTracks = _trackService.GetTracksByAlbum(album.Id);
|
||||||
|
|
||||||
var updateList = new List<Track>();
|
var updateList = new List<Track>();
|
||||||
var newList = new List<Track>();
|
var newList = new List<Track>();
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
using NzbDrone.Core.Datastore;
|
using NzbDrone.Core.Datastore;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using NLog;
|
using NLog;
|
||||||
|
@ -15,7 +15,7 @@ namespace NzbDrone.Core.Music
|
||||||
{
|
{
|
||||||
Track Find(int artistId, int albumId, int trackNumber);
|
Track Find(int artistId, int albumId, int trackNumber);
|
||||||
List<Track> GetTracks(int artistId);
|
List<Track> GetTracks(int artistId);
|
||||||
List<Track> GetTracks(int artistId, int albumId);
|
List<Track> GetTracksByAlbum(int albumId);
|
||||||
List<Track> GetTracksByFileId(int fileId);
|
List<Track> GetTracksByFileId(int fileId);
|
||||||
List<Track> TracksWithFiles(int artistId);
|
List<Track> TracksWithFiles(int artistId);
|
||||||
PagingSpec<Track> TracksWithoutFiles(PagingSpec<Track> pagingSpec);
|
PagingSpec<Track> TracksWithoutFiles(PagingSpec<Track> pagingSpec);
|
||||||
|
@ -51,10 +51,9 @@ namespace NzbDrone.Core.Music
|
||||||
return Query.Where(s => s.ArtistId == artistId).ToList();
|
return Query.Where(s => s.ArtistId == artistId).ToList();
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<Track> GetTracks(int artistId, int albumId)
|
public List<Track> GetTracksByAlbum(int albumId)
|
||||||
{
|
{
|
||||||
return Query.Where(s => s.ArtistId == artistId)
|
return Query.Where(s => s.AlbumId == albumId)
|
||||||
.AndWhere(s => s.AlbumId == albumId)
|
|
||||||
.ToList();
|
.ToList();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
using NLog;
|
using NLog;
|
||||||
using NzbDrone.Core.Configuration;
|
using NzbDrone.Core.Configuration;
|
||||||
using NzbDrone.Core.Datastore;
|
using NzbDrone.Core.Datastore;
|
||||||
using NzbDrone.Core.MediaFiles;
|
using NzbDrone.Core.MediaFiles;
|
||||||
|
@ -19,7 +19,7 @@ namespace NzbDrone.Core.Music
|
||||||
Track FindTrack(int artistId, int albumId, int trackNumber);
|
Track FindTrack(int artistId, int albumId, int trackNumber);
|
||||||
Track FindTrackByTitle(int artistId, int albumId, string releaseTitle);
|
Track FindTrackByTitle(int artistId, int albumId, string releaseTitle);
|
||||||
List<Track> GetTracksByArtist(int artistId);
|
List<Track> GetTracksByArtist(int artistId);
|
||||||
List<Track> GetTracksByAlbum(int artistId, int albumId);
|
List<Track> GetTracksByAlbum(int albumId);
|
||||||
//List<Track> GetTracksByAlbumTitle(string artistId, string albumTitle);
|
//List<Track> GetTracksByAlbumTitle(string artistId, string albumTitle);
|
||||||
List<Track> TracksWithFiles(int artistId);
|
List<Track> TracksWithFiles(int artistId);
|
||||||
//PagingSpec<Track> TracksWithoutFiles(PagingSpec<Track> pagingSpec);
|
//PagingSpec<Track> TracksWithoutFiles(PagingSpec<Track> pagingSpec);
|
||||||
|
@ -70,16 +70,16 @@ namespace NzbDrone.Core.Music
|
||||||
return _trackRepository.GetTracks(artistId).ToList();
|
return _trackRepository.GetTracks(artistId).ToList();
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<Track> GetTracksByAlbum(int artistId, int albumId)
|
public List<Track> GetTracksByAlbum(int albumId)
|
||||||
{
|
{
|
||||||
return _trackRepository.GetTracks(artistId, albumId);
|
return _trackRepository.GetTracksByAlbum(albumId);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Track FindTrackByTitle(int artistId, int albumId, string releaseTitle)
|
public Track FindTrackByTitle(int artistId, int albumId, string releaseTitle)
|
||||||
{
|
{
|
||||||
// TODO: can replace this search mechanism with something smarter/faster/better
|
// TODO: can replace this search mechanism with something smarter/faster/better
|
||||||
var normalizedReleaseTitle = Parser.Parser.NormalizeEpisodeTitle(releaseTitle).Replace(".", " ");
|
var normalizedReleaseTitle = Parser.Parser.NormalizeEpisodeTitle(releaseTitle).Replace(".", " ");
|
||||||
var tracks = _trackRepository.GetTracks(artistId, albumId);
|
var tracks = _trackRepository.GetTracksByAlbum(albumId);
|
||||||
|
|
||||||
var matches = tracks.Select(
|
var matches = tracks.Select(
|
||||||
track => new
|
track => new
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue