mirror of
https://github.com/lidarr/lidarr.git
synced 2025-07-31 04:00:18 -07:00
New: Unmapped files view (#888)
* New: Unmapped files view Displays all trackfiles that haven't been matched to a track. Generalised the file details component and adds it to the album details screen. * Add sorting by quality * New: MediaServiceTests & MediaRepoTests
This commit is contained in:
parent
74cb2a6f52
commit
4413c7e46c
36 changed files with 1507 additions and 404 deletions
|
@ -1,65 +0,0 @@
|
|||
.fileDetails {
|
||||
margin-bottom: 20px;
|
||||
border: 1px solid $borderColor;
|
||||
border-radius: 4px;
|
||||
background-color: $white;
|
||||
|
||||
&:last-of-type {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.header {
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
.filename {
|
||||
flex-grow: 1;
|
||||
margin-right: 10px;
|
||||
margin-left: 10px;
|
||||
}
|
||||
|
||||
.expandButton {
|
||||
position: relative;
|
||||
width: 60px;
|
||||
height: 60px;
|
||||
}
|
||||
|
||||
.actionButton {
|
||||
composes: button from '~Components/Link/IconButton.css';
|
||||
|
||||
width: 30px;
|
||||
}
|
||||
|
||||
.audioTags {
|
||||
padding-top: 15px;
|
||||
padding-bottom: 15px;
|
||||
border-top: 1px solid $borderColor;
|
||||
}
|
||||
|
||||
.expandButtonIcon {
|
||||
composes: actionButton;
|
||||
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
margin-top: -12px;
|
||||
margin-left: -15px;
|
||||
}
|
||||
|
||||
@media only screen and (max-width: $breakpointSmall) {
|
||||
.medium {
|
||||
border-right: 0;
|
||||
border-left: 0;
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
.expandButtonIcon {
|
||||
position: static;
|
||||
margin: 0;
|
||||
}
|
||||
}
|
|
@ -1,258 +0,0 @@
|
|||
import _ from 'lodash';
|
||||
import PropTypes from 'prop-types';
|
||||
import React, { Component } from 'react';
|
||||
import { icons } from 'Helpers/Props';
|
||||
import formatTimeSpan from 'Utilities/Date/formatTimeSpan';
|
||||
import Icon from 'Components/Icon';
|
||||
import Link from 'Components/Link/Link';
|
||||
import DescriptionList from 'Components/DescriptionList/DescriptionList';
|
||||
import DescriptionListItem from 'Components/DescriptionList/DescriptionListItem';
|
||||
import DescriptionListItemTitle from 'Components/DescriptionList/DescriptionListItemTitle';
|
||||
import DescriptionListItemDescription from 'Components/DescriptionList/DescriptionListItemDescription';
|
||||
import styles from './FileDetails.css';
|
||||
|
||||
class FileDetails extends Component {
|
||||
|
||||
//
|
||||
// Lifecycle
|
||||
|
||||
constructor(props, context) {
|
||||
super(props, context);
|
||||
|
||||
this.state = {
|
||||
isExpanded: props.isExpanded
|
||||
};
|
||||
}
|
||||
|
||||
//
|
||||
// Listeners
|
||||
|
||||
onExpandPress = () => {
|
||||
const {
|
||||
isExpanded
|
||||
} = this.state;
|
||||
this.setState({ isExpanded: !isExpanded });
|
||||
}
|
||||
|
||||
//
|
||||
// Render
|
||||
|
||||
renderRejections() {
|
||||
const {
|
||||
rejections
|
||||
} = this.props;
|
||||
|
||||
return (
|
||||
<span>
|
||||
<DescriptionListItemTitle>
|
||||
Rejections
|
||||
</DescriptionListItemTitle>
|
||||
{
|
||||
_.map(rejections, (item, key) => {
|
||||
return (
|
||||
<DescriptionListItemDescription key={key}>
|
||||
{item.reason}
|
||||
</DescriptionListItemDescription>
|
||||
);
|
||||
})
|
||||
}
|
||||
</span>
|
||||
);
|
||||
}
|
||||
|
||||
render() {
|
||||
const {
|
||||
filename,
|
||||
audioTags,
|
||||
rejections
|
||||
} = this.props;
|
||||
|
||||
const {
|
||||
isExpanded
|
||||
} = this.state;
|
||||
|
||||
return (
|
||||
<div
|
||||
className={styles.fileDetails}
|
||||
>
|
||||
<div className={styles.header} onClick={this.onExpandPress}>
|
||||
<div className={styles.filename}>
|
||||
{filename}
|
||||
</div>
|
||||
|
||||
<div className={styles.expandButton}>
|
||||
<Icon
|
||||
className={styles.expandButtonIcon}
|
||||
name={isExpanded ? icons.COLLAPSE : icons.EXPAND}
|
||||
title={isExpanded ? 'Hide file info' : 'Show file info'}
|
||||
size={24}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
{
|
||||
isExpanded &&
|
||||
<div className={styles.audioTags}>
|
||||
|
||||
<DescriptionList>
|
||||
{
|
||||
audioTags.title !== undefined &&
|
||||
<DescriptionListItem
|
||||
title="Track Title"
|
||||
data={audioTags.title}
|
||||
/>
|
||||
}
|
||||
{
|
||||
audioTags.trackNumbers[0] > 0 &&
|
||||
<DescriptionListItem
|
||||
title="Track Number"
|
||||
data={audioTags.trackNumbers[0]}
|
||||
/>
|
||||
}
|
||||
{
|
||||
audioTags.discNumber > 0 &&
|
||||
<DescriptionListItem
|
||||
title="Disc Number"
|
||||
data={audioTags.discNumber}
|
||||
/>
|
||||
}
|
||||
{
|
||||
audioTags.discCount > 0 &&
|
||||
<DescriptionListItem
|
||||
title="Disc Count"
|
||||
data={audioTags.discCount}
|
||||
/>
|
||||
}
|
||||
{
|
||||
audioTags.albumTitle !== undefined &&
|
||||
<DescriptionListItem
|
||||
title="Album"
|
||||
data={audioTags.albumTitle}
|
||||
/>
|
||||
}
|
||||
{
|
||||
audioTags.artistTitle !== undefined &&
|
||||
<DescriptionListItem
|
||||
title="Artist"
|
||||
data={audioTags.artistTitle}
|
||||
/>
|
||||
}
|
||||
{
|
||||
audioTags.country !== undefined &&
|
||||
<DescriptionListItem
|
||||
title="Country"
|
||||
data={audioTags.country.name}
|
||||
/>
|
||||
}
|
||||
{
|
||||
audioTags.year > 0 &&
|
||||
<DescriptionListItem
|
||||
title="Year"
|
||||
data={audioTags.year}
|
||||
/>
|
||||
}
|
||||
{
|
||||
audioTags.label !== undefined &&
|
||||
<DescriptionListItem
|
||||
title="Label"
|
||||
data={audioTags.label}
|
||||
/>
|
||||
}
|
||||
{
|
||||
audioTags.catalogNumber !== undefined &&
|
||||
<DescriptionListItem
|
||||
title="Catalog Number"
|
||||
data={audioTags.catalogNumber}
|
||||
/>
|
||||
}
|
||||
{
|
||||
audioTags.disambiguation !== undefined &&
|
||||
<DescriptionListItem
|
||||
title="Disambiguation"
|
||||
data={audioTags.disambiguation}
|
||||
/>
|
||||
}
|
||||
{
|
||||
audioTags.duration !== undefined &&
|
||||
<DescriptionListItem
|
||||
title="Duration"
|
||||
data={formatTimeSpan(audioTags.duration)}
|
||||
/>
|
||||
}
|
||||
{
|
||||
audioTags.artistMBId !== undefined &&
|
||||
<Link
|
||||
to={`https://musicbrainz.org/artist/${audioTags.artistMBId}`}
|
||||
>
|
||||
<DescriptionListItem
|
||||
title="MusicBrainz Artist ID"
|
||||
data={audioTags.artistMBId}
|
||||
/>
|
||||
</Link>
|
||||
}
|
||||
{
|
||||
audioTags.albumMBId !== undefined &&
|
||||
<Link
|
||||
to={`https://musicbrainz.org/release-group/${audioTags.albumMBId}`}
|
||||
>
|
||||
<DescriptionListItem
|
||||
title="MusicBrainz Album ID"
|
||||
data={audioTags.albumMBId}
|
||||
/>
|
||||
</Link>
|
||||
}
|
||||
{
|
||||
audioTags.releaseMBId !== undefined &&
|
||||
<Link
|
||||
to={`https://musicbrainz.org/release/${audioTags.releaseMBId}`}
|
||||
>
|
||||
<DescriptionListItem
|
||||
title="MusicBrainz Release ID"
|
||||
data={audioTags.releaseMBId}
|
||||
/>
|
||||
</Link>
|
||||
}
|
||||
{
|
||||
audioTags.recordingMBId !== undefined &&
|
||||
<Link
|
||||
to={`https://musicbrainz.org/recording/${audioTags.recordingMBId}`}
|
||||
>
|
||||
<DescriptionListItem
|
||||
title="MusicBrainz Recording ID"
|
||||
data={audioTags.recordingMBId}
|
||||
/>
|
||||
</Link>
|
||||
}
|
||||
{
|
||||
audioTags.trackMBId !== undefined &&
|
||||
<Link
|
||||
to={`https://musicbrainz.org/track/${audioTags.trackMBId}`}
|
||||
>
|
||||
<DescriptionListItem
|
||||
title="MusicBrainz Track ID"
|
||||
data={audioTags.trackMBId}
|
||||
/>
|
||||
</Link>
|
||||
}
|
||||
{
|
||||
rejections.length > 0 &&
|
||||
this.renderRejections()
|
||||
}
|
||||
</DescriptionList>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
FileDetails.propTypes = {
|
||||
audioTags: PropTypes.object.isRequired,
|
||||
filename: PropTypes.string.isRequired,
|
||||
rejections: PropTypes.arrayOf(PropTypes.object).isRequired,
|
||||
isExpanded: PropTypes.bool
|
||||
};
|
||||
|
||||
export default FileDetails;
|
|
@ -15,7 +15,7 @@ import ModalFooter from 'Components/Modal/ModalFooter';
|
|||
import Table from 'Components/Table/Table';
|
||||
import TableBody from 'Components/Table/TableBody';
|
||||
import SelectTrackRow from './SelectTrackRow';
|
||||
import FileDetails from '../FileDetails';
|
||||
import ExpandingFileDetails from 'TrackFile/ExpandingFileDetails';
|
||||
|
||||
const columns = [
|
||||
{
|
||||
|
@ -151,7 +151,7 @@ class SelectTrackModalContent extends Component {
|
|||
<div>{errorMessage}</div>
|
||||
}
|
||||
|
||||
<FileDetails
|
||||
<ExpandingFileDetails
|
||||
audioTags={audioTags}
|
||||
filename={filename}
|
||||
rejections={rejections}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue