mirror of
https://github.com/lidarr/lidarr.git
synced 2025-07-14 00:53:57 -07:00
New: Remove AlbumFolder, Support Nested Track Format
This commit is contained in:
parent
8063a32acd
commit
774a3597de
31 changed files with 207 additions and 288 deletions
|
@ -72,7 +72,6 @@ class EditArtistModalContent extends Component {
|
||||||
|
|
||||||
const {
|
const {
|
||||||
monitored,
|
monitored,
|
||||||
albumFolder,
|
|
||||||
qualityProfileId,
|
qualityProfileId,
|
||||||
metadataProfileId,
|
metadataProfileId,
|
||||||
path,
|
path,
|
||||||
|
@ -99,18 +98,6 @@ class EditArtistModalContent extends Component {
|
||||||
/>
|
/>
|
||||||
</FormGroup>
|
</FormGroup>
|
||||||
|
|
||||||
<FormGroup>
|
|
||||||
<FormLabel>Use Album Folder</FormLabel>
|
|
||||||
|
|
||||||
<FormInputGroup
|
|
||||||
type={inputTypes.CHECK}
|
|
||||||
name="albumFolder"
|
|
||||||
helpText="Sort tracks into album folders"
|
|
||||||
{...albumFolder}
|
|
||||||
onChange={onInputChange}
|
|
||||||
/>
|
|
||||||
</FormGroup>
|
|
||||||
|
|
||||||
<FormGroup>
|
<FormGroup>
|
||||||
<FormLabel>Quality Profile</FormLabel>
|
<FormLabel>Quality Profile</FormLabel>
|
||||||
|
|
||||||
|
|
|
@ -39,7 +39,6 @@ function createMapStateToProps() {
|
||||||
|
|
||||||
const artistSettings = _.pick(artist, [
|
const artistSettings = _.pick(artist, [
|
||||||
'monitored',
|
'monitored',
|
||||||
'albumFolder',
|
|
||||||
'qualityProfileId',
|
'qualityProfileId',
|
||||||
'metadataProfileId',
|
'metadataProfileId',
|
||||||
'path',
|
'path',
|
||||||
|
|
|
@ -27,7 +27,6 @@ class ArtistEditorFooter extends Component {
|
||||||
monitored: NO_CHANGE,
|
monitored: NO_CHANGE,
|
||||||
qualityProfileId: NO_CHANGE,
|
qualityProfileId: NO_CHANGE,
|
||||||
metadataProfileId: NO_CHANGE,
|
metadataProfileId: NO_CHANGE,
|
||||||
albumFolder: NO_CHANGE,
|
|
||||||
rootFolderPath: NO_CHANGE,
|
rootFolderPath: NO_CHANGE,
|
||||||
savingTags: false,
|
savingTags: false,
|
||||||
isDeleteArtistModalOpen: false,
|
isDeleteArtistModalOpen: false,
|
||||||
|
@ -48,7 +47,6 @@ class ArtistEditorFooter extends Component {
|
||||||
monitored: NO_CHANGE,
|
monitored: NO_CHANGE,
|
||||||
qualityProfileId: NO_CHANGE,
|
qualityProfileId: NO_CHANGE,
|
||||||
metadataProfileId: NO_CHANGE,
|
metadataProfileId: NO_CHANGE,
|
||||||
albumFolder: NO_CHANGE,
|
|
||||||
rootFolderPath: NO_CHANGE,
|
rootFolderPath: NO_CHANGE,
|
||||||
savingTags: false
|
savingTags: false
|
||||||
});
|
});
|
||||||
|
@ -75,9 +73,6 @@ class ArtistEditorFooter extends Component {
|
||||||
case 'monitored':
|
case 'monitored':
|
||||||
this.props.onSaveSelected({ [name]: value === 'monitored' });
|
this.props.onSaveSelected({ [name]: value === 'monitored' });
|
||||||
break;
|
break;
|
||||||
case 'albumFolder':
|
|
||||||
this.props.onSaveSelected({ [name]: value === 'yes' });
|
|
||||||
break;
|
|
||||||
default:
|
default:
|
||||||
this.props.onSaveSelected({ [name]: value });
|
this.props.onSaveSelected({ [name]: value });
|
||||||
}
|
}
|
||||||
|
@ -152,7 +147,6 @@ class ArtistEditorFooter extends Component {
|
||||||
monitored,
|
monitored,
|
||||||
qualityProfileId,
|
qualityProfileId,
|
||||||
metadataProfileId,
|
metadataProfileId,
|
||||||
albumFolder,
|
|
||||||
rootFolderPath,
|
rootFolderPath,
|
||||||
savingTags,
|
savingTags,
|
||||||
isTagsModalOpen,
|
isTagsModalOpen,
|
||||||
|
@ -167,12 +161,6 @@ class ArtistEditorFooter extends Component {
|
||||||
{ key: 'unmonitored', value: 'Unmonitored' }
|
{ key: 'unmonitored', value: 'Unmonitored' }
|
||||||
];
|
];
|
||||||
|
|
||||||
const albumFolderOptions = [
|
|
||||||
{ key: NO_CHANGE, value: 'No Change', disabled: true },
|
|
||||||
{ key: 'yes', value: 'Yes' },
|
|
||||||
{ key: 'no', value: 'No' }
|
|
||||||
];
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<PageContentFooter>
|
<PageContentFooter>
|
||||||
<div className={styles.inputContainer}>
|
<div className={styles.inputContainer}>
|
||||||
|
@ -245,28 +233,6 @@ class ArtistEditorFooter extends Component {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (name === 'albumFolder') {
|
|
||||||
return (
|
|
||||||
<div
|
|
||||||
key={name}
|
|
||||||
className={styles.inputContainer}
|
|
||||||
>
|
|
||||||
<ArtistEditorFooterLabel
|
|
||||||
label="Album Folder"
|
|
||||||
isSaving={isSaving && albumFolder !== NO_CHANGE}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<SelectInput
|
|
||||||
name="albumFolder"
|
|
||||||
value={albumFolder}
|
|
||||||
values={albumFolderOptions}
|
|
||||||
isDisabled={!selectedCount}
|
|
||||||
onChange={this.onInputChange}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (name === 'path') {
|
if (name === 'path') {
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
|
|
|
@ -1,5 +0,0 @@
|
||||||
.albumFolder {
|
|
||||||
composes: cell from '~Components/Table/Cells/TableRowCell.css';
|
|
||||||
|
|
||||||
width: 150px;
|
|
||||||
}
|
|
|
@ -2,24 +2,14 @@ import PropTypes from 'prop-types';
|
||||||
import React, { Component } from 'react';
|
import React, { Component } from 'react';
|
||||||
import ArtistNameLink from 'Artist/ArtistNameLink';
|
import ArtistNameLink from 'Artist/ArtistNameLink';
|
||||||
import ArtistStatusCell from 'Artist/Index/Table/ArtistStatusCell';
|
import ArtistStatusCell from 'Artist/Index/Table/ArtistStatusCell';
|
||||||
import CheckInput from 'Components/Form/CheckInput';
|
|
||||||
import TableRowCell from 'Components/Table/Cells/TableRowCell';
|
import TableRowCell from 'Components/Table/Cells/TableRowCell';
|
||||||
import TableSelectCell from 'Components/Table/Cells/TableSelectCell';
|
import TableSelectCell from 'Components/Table/Cells/TableSelectCell';
|
||||||
import TableRow from 'Components/Table/TableRow';
|
import TableRow from 'Components/Table/TableRow';
|
||||||
import TagListConnector from 'Components/TagListConnector';
|
import TagListConnector from 'Components/TagListConnector';
|
||||||
import formatBytes from 'Utilities/Number/formatBytes';
|
import formatBytes from 'Utilities/Number/formatBytes';
|
||||||
import styles from './ArtistEditorRow.css';
|
|
||||||
|
|
||||||
class ArtistEditorRow extends Component {
|
class ArtistEditorRow extends Component {
|
||||||
|
|
||||||
//
|
|
||||||
// Listeners
|
|
||||||
|
|
||||||
onAlbumFolderChange = () => {
|
|
||||||
// Mock handler to satisfy `onChange` being required for `CheckInput`.
|
|
||||||
//
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
//
|
||||||
// Render
|
// Render
|
||||||
|
|
||||||
|
@ -33,7 +23,6 @@ class ArtistEditorRow extends Component {
|
||||||
monitored,
|
monitored,
|
||||||
metadataProfile,
|
metadataProfile,
|
||||||
qualityProfile,
|
qualityProfile,
|
||||||
albumFolder,
|
|
||||||
path,
|
path,
|
||||||
statistics,
|
statistics,
|
||||||
tags,
|
tags,
|
||||||
|
@ -80,7 +69,6 @@ class ArtistEditorRow extends Component {
|
||||||
return (
|
return (
|
||||||
<TableRowCell
|
<TableRowCell
|
||||||
key={name}
|
key={name}
|
||||||
className={styles.title}
|
|
||||||
>
|
>
|
||||||
<ArtistNameLink
|
<ArtistNameLink
|
||||||
foreignArtistId={foreignArtistId}
|
foreignArtistId={foreignArtistId}
|
||||||
|
@ -106,22 +94,6 @@ class ArtistEditorRow extends Component {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (name === 'albumFolder') {
|
|
||||||
return (
|
|
||||||
<TableRowCell
|
|
||||||
key={name}
|
|
||||||
className={styles.albumFolder}
|
|
||||||
>
|
|
||||||
<CheckInput
|
|
||||||
name="albumFolder"
|
|
||||||
value={albumFolder}
|
|
||||||
isDisabled={true}
|
|
||||||
onChange={this.onAlbumFolderChange}
|
|
||||||
/>
|
|
||||||
</TableRowCell>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (name === 'path') {
|
if (name === 'path') {
|
||||||
return (
|
return (
|
||||||
<TableRowCell key={name}>
|
<TableRowCell key={name}>
|
||||||
|
@ -165,7 +137,6 @@ ArtistEditorRow.propTypes = {
|
||||||
monitored: PropTypes.bool.isRequired,
|
monitored: PropTypes.bool.isRequired,
|
||||||
metadataProfile: PropTypes.object.isRequired,
|
metadataProfile: PropTypes.object.isRequired,
|
||||||
qualityProfile: PropTypes.object.isRequired,
|
qualityProfile: PropTypes.object.isRequired,
|
||||||
albumFolder: PropTypes.bool.isRequired,
|
|
||||||
path: PropTypes.string.isRequired,
|
path: PropTypes.string.isRequired,
|
||||||
statistics: PropTypes.object.isRequired,
|
statistics: PropTypes.object.isRequired,
|
||||||
tags: PropTypes.arrayOf(PropTypes.number).isRequired,
|
tags: PropTypes.arrayOf(PropTypes.number).isRequired,
|
||||||
|
|
|
@ -87,7 +87,6 @@ class AddNewAlbumModalContentConnector extends Component {
|
||||||
monitor,
|
monitor,
|
||||||
qualityProfileId,
|
qualityProfileId,
|
||||||
metadataProfileId,
|
metadataProfileId,
|
||||||
albumFolder,
|
|
||||||
tags
|
tags
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
|
||||||
|
@ -97,7 +96,6 @@ class AddNewAlbumModalContentConnector extends Component {
|
||||||
monitor: monitor.value,
|
monitor: monitor.value,
|
||||||
qualityProfileId: qualityProfileId.value,
|
qualityProfileId: qualityProfileId.value,
|
||||||
metadataProfileId: metadataProfileId.value,
|
metadataProfileId: metadataProfileId.value,
|
||||||
albumFolder: albumFolder.value,
|
|
||||||
tags: tags.value,
|
tags: tags.value,
|
||||||
searchForNewAlbum
|
searchForNewAlbum
|
||||||
});
|
});
|
||||||
|
@ -125,7 +123,6 @@ AddNewAlbumModalContentConnector.propTypes = {
|
||||||
qualityProfileId: PropTypes.object,
|
qualityProfileId: PropTypes.object,
|
||||||
metadataProfileId: PropTypes.object,
|
metadataProfileId: PropTypes.object,
|
||||||
noneMetadataProfileId: PropTypes.number.isRequired,
|
noneMetadataProfileId: PropTypes.number.isRequired,
|
||||||
albumFolder: PropTypes.object.isRequired,
|
|
||||||
tags: PropTypes.object.isRequired,
|
tags: PropTypes.object.isRequired,
|
||||||
onModalClose: PropTypes.func.isRequired,
|
onModalClose: PropTypes.func.isRequired,
|
||||||
setAddDefault: PropTypes.func.isRequired,
|
setAddDefault: PropTypes.func.isRequired,
|
||||||
|
|
|
@ -59,7 +59,6 @@ class AddNewArtistModalContentConnector extends Component {
|
||||||
monitor,
|
monitor,
|
||||||
qualityProfileId,
|
qualityProfileId,
|
||||||
metadataProfileId,
|
metadataProfileId,
|
||||||
albumFolder,
|
|
||||||
tags
|
tags
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
|
||||||
|
@ -69,7 +68,6 @@ class AddNewArtistModalContentConnector extends Component {
|
||||||
monitor: monitor.value,
|
monitor: monitor.value,
|
||||||
qualityProfileId: qualityProfileId.value,
|
qualityProfileId: qualityProfileId.value,
|
||||||
metadataProfileId: metadataProfileId.value,
|
metadataProfileId: metadataProfileId.value,
|
||||||
albumFolder: albumFolder.value,
|
|
||||||
tags: tags.value,
|
tags: tags.value,
|
||||||
searchForMissingAlbums
|
searchForMissingAlbums
|
||||||
});
|
});
|
||||||
|
@ -95,7 +93,6 @@ AddNewArtistModalContentConnector.propTypes = {
|
||||||
monitor: PropTypes.object.isRequired,
|
monitor: PropTypes.object.isRequired,
|
||||||
qualityProfileId: PropTypes.object,
|
qualityProfileId: PropTypes.object,
|
||||||
metadataProfileId: PropTypes.object,
|
metadataProfileId: PropTypes.object,
|
||||||
albumFolder: PropTypes.object.isRequired,
|
|
||||||
tags: PropTypes.object.isRequired,
|
tags: PropTypes.object.isRequired,
|
||||||
onModalClose: PropTypes.func.isRequired,
|
onModalClose: PropTypes.func.isRequired,
|
||||||
setAddDefault: PropTypes.func.isRequired,
|
setAddDefault: PropTypes.func.isRequired,
|
||||||
|
|
|
@ -35,7 +35,6 @@ class AddArtistOptionsForm extends Component {
|
||||||
metadataProfileId,
|
metadataProfileId,
|
||||||
includeNoneMetadataProfile,
|
includeNoneMetadataProfile,
|
||||||
showMetadataProfile,
|
showMetadataProfile,
|
||||||
albumFolder,
|
|
||||||
tags,
|
tags,
|
||||||
onInputChange,
|
onInputChange,
|
||||||
...otherProps
|
...otherProps
|
||||||
|
@ -119,17 +118,6 @@ class AddArtistOptionsForm extends Component {
|
||||||
/>
|
/>
|
||||||
</FormGroup>
|
</FormGroup>
|
||||||
|
|
||||||
<FormGroup>
|
|
||||||
<FormLabel>Album Folder</FormLabel>
|
|
||||||
|
|
||||||
<FormInputGroup
|
|
||||||
type={inputTypes.CHECK}
|
|
||||||
name="albumFolder"
|
|
||||||
onChange={onInputChange}
|
|
||||||
{...albumFolder}
|
|
||||||
/>
|
|
||||||
</FormGroup>
|
|
||||||
|
|
||||||
<FormGroup>
|
<FormGroup>
|
||||||
<FormLabel>Tags</FormLabel>
|
<FormLabel>Tags</FormLabel>
|
||||||
|
|
||||||
|
@ -152,7 +140,6 @@ AddArtistOptionsForm.propTypes = {
|
||||||
metadataProfileId: PropTypes.object,
|
metadataProfileId: PropTypes.object,
|
||||||
showMetadataProfile: PropTypes.bool.isRequired,
|
showMetadataProfile: PropTypes.bool.isRequired,
|
||||||
includeNoneMetadataProfile: PropTypes.bool.isRequired,
|
includeNoneMetadataProfile: PropTypes.bool.isRequired,
|
||||||
albumFolder: PropTypes.object.isRequired,
|
|
||||||
tags: PropTypes.object.isRequired,
|
tags: PropTypes.object.isRequired,
|
||||||
onInputChange: PropTypes.func.isRequired
|
onInputChange: PropTypes.func.isRequired
|
||||||
};
|
};
|
||||||
|
|
|
@ -61,16 +61,6 @@ class Naming extends Component {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
onAlbumFolderNamingModalOpenClick = () => {
|
|
||||||
this.setState({
|
|
||||||
isNamingModalOpen: true,
|
|
||||||
namingModalOptions: {
|
|
||||||
name: 'albumFolderFormat',
|
|
||||||
album: true
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
onNamingModalClose = () => {
|
onNamingModalClose = () => {
|
||||||
this.setState({ isNamingModalOpen: false });
|
this.setState({ isNamingModalOpen: false });
|
||||||
}
|
}
|
||||||
|
@ -103,8 +93,6 @@ class Naming extends Component {
|
||||||
const multiDiscTrackFormatErrors = [];
|
const multiDiscTrackFormatErrors = [];
|
||||||
const artistFolderFormatHelpTexts = [];
|
const artistFolderFormatHelpTexts = [];
|
||||||
const artistFolderFormatErrors = [];
|
const artistFolderFormatErrors = [];
|
||||||
const albumFolderFormatHelpTexts = [];
|
|
||||||
const albumFolderFormatErrors = [];
|
|
||||||
|
|
||||||
if (examplesPopulated) {
|
if (examplesPopulated) {
|
||||||
if (examples.singleTrackExample) {
|
if (examples.singleTrackExample) {
|
||||||
|
@ -124,12 +112,6 @@ class Naming extends Component {
|
||||||
} else {
|
} else {
|
||||||
artistFolderFormatErrors.push({ message: 'Invalid Format' });
|
artistFolderFormatErrors.push({ message: 'Invalid Format' });
|
||||||
}
|
}
|
||||||
|
|
||||||
if (examples.albumFolderExample) {
|
|
||||||
albumFolderFormatHelpTexts.push(`Example: ${examples.albumFolderExample}`);
|
|
||||||
} else {
|
|
||||||
albumFolderFormatErrors.push({ message: 'Invalid Format' });
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -225,21 +207,6 @@ class Naming extends Component {
|
||||||
/>
|
/>
|
||||||
</FormGroup>
|
</FormGroup>
|
||||||
|
|
||||||
<FormGroup>
|
|
||||||
<FormLabel>Album Folder Format</FormLabel>
|
|
||||||
|
|
||||||
<FormInputGroup
|
|
||||||
inputClassName={styles.namingInput}
|
|
||||||
type={inputTypes.TEXT}
|
|
||||||
name="albumFolderFormat"
|
|
||||||
buttons={<FormInputButton onPress={this.onAlbumFolderNamingModalOpenClick}>?</FormInputButton>}
|
|
||||||
onChange={onInputChange}
|
|
||||||
{...settings.albumFolderFormat}
|
|
||||||
helpTexts={albumFolderFormatHelpTexts}
|
|
||||||
errors={[...albumFolderFormatErrors, ...settings.albumFolderFormat.errors]}
|
|
||||||
/>
|
|
||||||
</FormGroup>
|
|
||||||
|
|
||||||
{
|
{
|
||||||
namingModalOptions &&
|
namingModalOptions &&
|
||||||
<NamingModal
|
<NamingModal
|
||||||
|
|
|
@ -34,7 +34,6 @@ export const defaultState = {
|
||||||
monitor: monitorOptions[0].key,
|
monitor: monitorOptions[0].key,
|
||||||
qualityProfileId: 0,
|
qualityProfileId: 0,
|
||||||
metadataProfileId: 0,
|
metadataProfileId: 0,
|
||||||
albumFolder: true,
|
|
||||||
tags: []
|
tags: []
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -16,7 +16,6 @@ function createImportArtistItemSelector() {
|
||||||
return {
|
return {
|
||||||
defaultMonitor: addArtist.defaults.monitor,
|
defaultMonitor: addArtist.defaults.monitor,
|
||||||
defaultQualityProfileId: addArtist.defaults.qualityProfileId,
|
defaultQualityProfileId: addArtist.defaults.qualityProfileId,
|
||||||
defaultAlbumFolder: addArtist.defaults.albumFolder,
|
|
||||||
...item,
|
...item,
|
||||||
isExistingArtist
|
isExistingArtist
|
||||||
};
|
};
|
||||||
|
|
|
@ -6,7 +6,6 @@ function getNewArtist(artist, payload) {
|
||||||
qualityProfileId,
|
qualityProfileId,
|
||||||
metadataProfileId,
|
metadataProfileId,
|
||||||
artistType,
|
artistType,
|
||||||
albumFolder,
|
|
||||||
tags,
|
tags,
|
||||||
searchForMissingAlbums = false
|
searchForMissingAlbums = false
|
||||||
} = payload;
|
} = payload;
|
||||||
|
@ -22,7 +21,6 @@ function getNewArtist(artist, payload) {
|
||||||
artist.metadataProfileId = metadataProfileId;
|
artist.metadataProfileId = metadataProfileId;
|
||||||
artist.rootFolderPath = rootFolderPath;
|
artist.rootFolderPath = rootFolderPath;
|
||||||
artist.artistType = artistType;
|
artist.artistType = artistType;
|
||||||
artist.albumFolder = albumFolder;
|
|
||||||
artist.tags = tags;
|
artist.tags = tags;
|
||||||
|
|
||||||
return artist;
|
return artist;
|
||||||
|
|
|
@ -46,11 +46,6 @@ namespace Lidarr.Api.V1.Artist
|
||||||
artist.MetadataProfileId = resource.MetadataProfileId.Value;
|
artist.MetadataProfileId = resource.MetadataProfileId.Value;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (resource.AlbumFolder.HasValue)
|
|
||||||
{
|
|
||||||
artist.AlbumFolder = resource.AlbumFolder.Value;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (resource.RootFolderPath.IsNotNullOrWhiteSpace())
|
if (resource.RootFolderPath.IsNotNullOrWhiteSpace())
|
||||||
{
|
{
|
||||||
artist.RootFolderPath = resource.RootFolderPath;
|
artist.RootFolderPath = resource.RootFolderPath;
|
||||||
|
|
|
@ -45,7 +45,6 @@ namespace Lidarr.Api.V1.Artist
|
||||||
public int MetadataProfileId { get; set; }
|
public int MetadataProfileId { get; set; }
|
||||||
|
|
||||||
//Editing Only
|
//Editing Only
|
||||||
public bool AlbumFolder { get; set; }
|
|
||||||
public bool Monitored { get; set; }
|
public bool Monitored { get; set; }
|
||||||
|
|
||||||
public string RootFolderPath { get; set; }
|
public string RootFolderPath { get; set; }
|
||||||
|
@ -91,7 +90,6 @@ namespace Lidarr.Api.V1.Artist
|
||||||
MetadataProfileId = model.MetadataProfileId,
|
MetadataProfileId = model.MetadataProfileId,
|
||||||
Links = model.Metadata.Value.Links,
|
Links = model.Metadata.Value.Links,
|
||||||
|
|
||||||
AlbumFolder = model.AlbumFolder,
|
|
||||||
Monitored = model.Monitored,
|
Monitored = model.Monitored,
|
||||||
|
|
||||||
CleanName = model.CleanName,
|
CleanName = model.CleanName,
|
||||||
|
@ -139,7 +137,6 @@ namespace Lidarr.Api.V1.Artist
|
||||||
QualityProfileId = resource.QualityProfileId,
|
QualityProfileId = resource.QualityProfileId,
|
||||||
MetadataProfileId = resource.MetadataProfileId,
|
MetadataProfileId = resource.MetadataProfileId,
|
||||||
|
|
||||||
AlbumFolder = resource.AlbumFolder,
|
|
||||||
Monitored = resource.Monitored,
|
Monitored = resource.Monitored,
|
||||||
|
|
||||||
CleanName = resource.CleanName,
|
CleanName = resource.CleanName,
|
||||||
|
|
|
@ -35,7 +35,6 @@ namespace Lidarr.Api.V1.Config
|
||||||
SharedValidator.RuleFor(c => c.StandardTrackFormat).ValidTrackFormat();
|
SharedValidator.RuleFor(c => c.StandardTrackFormat).ValidTrackFormat();
|
||||||
SharedValidator.RuleFor(c => c.MultiDiscTrackFormat).ValidTrackFormat();
|
SharedValidator.RuleFor(c => c.MultiDiscTrackFormat).ValidTrackFormat();
|
||||||
SharedValidator.RuleFor(c => c.ArtistFolderFormat).ValidArtistFolderFormat();
|
SharedValidator.RuleFor(c => c.ArtistFolderFormat).ValidArtistFolderFormat();
|
||||||
SharedValidator.RuleFor(c => c.AlbumFolderFormat).ValidAlbumFolderFormat();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void UpdateNamingConfig(NamingConfigResource resource)
|
private void UpdateNamingConfig(NamingConfigResource resource)
|
||||||
|
@ -96,10 +95,6 @@ namespace Lidarr.Api.V1.Config
|
||||||
? null
|
? null
|
||||||
: _filenameSampleService.GetArtistFolderSample(nameSpec);
|
: _filenameSampleService.GetArtistFolderSample(nameSpec);
|
||||||
|
|
||||||
sampleResource.AlbumFolderExample = nameSpec.AlbumFolderFormat.IsNullOrWhiteSpace()
|
|
||||||
? null
|
|
||||||
: _filenameSampleService.GetAlbumFolderSample(nameSpec);
|
|
||||||
|
|
||||||
return sampleResource;
|
return sampleResource;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -9,7 +9,6 @@ namespace Lidarr.Api.V1.Config
|
||||||
public string StandardTrackFormat { get; set; }
|
public string StandardTrackFormat { get; set; }
|
||||||
public string MultiDiscTrackFormat { get; set; }
|
public string MultiDiscTrackFormat { get; set; }
|
||||||
public string ArtistFolderFormat { get; set; }
|
public string ArtistFolderFormat { get; set; }
|
||||||
public string AlbumFolderFormat { get; set; }
|
|
||||||
public bool IncludeArtistName { get; set; }
|
public bool IncludeArtistName { get; set; }
|
||||||
public bool IncludeAlbumTitle { get; set; }
|
public bool IncludeAlbumTitle { get; set; }
|
||||||
public bool IncludeQuality { get; set; }
|
public bool IncludeQuality { get; set; }
|
||||||
|
|
|
@ -22,8 +22,7 @@ namespace Lidarr.Api.V1.Config
|
||||||
ReplaceIllegalCharacters = model.ReplaceIllegalCharacters,
|
ReplaceIllegalCharacters = model.ReplaceIllegalCharacters,
|
||||||
StandardTrackFormat = model.StandardTrackFormat,
|
StandardTrackFormat = model.StandardTrackFormat,
|
||||||
MultiDiscTrackFormat = model.MultiDiscTrackFormat,
|
MultiDiscTrackFormat = model.MultiDiscTrackFormat,
|
||||||
ArtistFolderFormat = model.ArtistFolderFormat,
|
ArtistFolderFormat = model.ArtistFolderFormat
|
||||||
AlbumFolderFormat = model.AlbumFolderFormat
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -48,8 +47,7 @@ namespace Lidarr.Api.V1.Config
|
||||||
StandardTrackFormat = resource.StandardTrackFormat,
|
StandardTrackFormat = resource.StandardTrackFormat,
|
||||||
MultiDiscTrackFormat = resource.MultiDiscTrackFormat,
|
MultiDiscTrackFormat = resource.MultiDiscTrackFormat,
|
||||||
|
|
||||||
ArtistFolderFormat = resource.ArtistFolderFormat,
|
ArtistFolderFormat = resource.ArtistFolderFormat
|
||||||
AlbumFolderFormat = resource.AlbumFolderFormat
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -44,16 +44,12 @@ namespace NzbDrone.Core.Test.MediaFiles.TrackFileMovingServiceTests
|
||||||
|
|
||||||
Mocker.GetMock<IBuildFileNames>()
|
Mocker.GetMock<IBuildFileNames>()
|
||||||
.Setup(s => s.BuildTrackFileName(It.IsAny<List<Track>>(), It.IsAny<Artist>(), It.IsAny<Album>(), It.IsAny<TrackFile>(), null, null))
|
.Setup(s => s.BuildTrackFileName(It.IsAny<List<Track>>(), It.IsAny<Artist>(), It.IsAny<Album>(), It.IsAny<TrackFile>(), null, null))
|
||||||
.Returns("File Name");
|
.Returns("Album\\File Name");
|
||||||
|
|
||||||
Mocker.GetMock<IBuildFileNames>()
|
Mocker.GetMock<IBuildFileNames>()
|
||||||
.Setup(s => s.BuildTrackFilePath(It.IsAny<Artist>(), It.IsAny<Album>(), It.IsAny<string>(), It.IsAny<string>()))
|
.Setup(s => s.BuildTrackFilePath(It.IsAny<Artist>(), It.IsAny<string>(), It.IsAny<string>()))
|
||||||
.Returns(@"C:\Test\Music\Artist\Album\File Name.mp3".AsOsAgnostic());
|
.Returns(@"C:\Test\Music\Artist\Album\File Name.mp3".AsOsAgnostic());
|
||||||
|
|
||||||
Mocker.GetMock<IBuildFileNames>()
|
|
||||||
.Setup(s => s.BuildAlbumPath(It.IsAny<Artist>(), It.IsAny<Album>()))
|
|
||||||
.Returns(@"C:\Test\Music\Artist\Album".AsOsAgnostic());
|
|
||||||
|
|
||||||
var rootFolder = @"C:\Test\Music\".AsOsAgnostic();
|
var rootFolder = @"C:\Test\Music\".AsOsAgnostic();
|
||||||
Mocker.GetMock<IDiskProvider>()
|
Mocker.GetMock<IDiskProvider>()
|
||||||
.Setup(s => s.FolderExists(rootFolder))
|
.Setup(s => s.FolderExists(rootFolder))
|
||||||
|
|
|
@ -24,24 +24,17 @@ namespace NzbDrone.Core.Test.OrganizerTests
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void should_clean_album_folder_when_it_contains_illegal_characters_in_album_or_artist_title()
|
public void should_clean_artist_folder_when_it_contains_illegal_characters_in_album_or_artist_title()
|
||||||
{
|
{
|
||||||
var filename = @"02 - Track Title";
|
var filename = @"02 - Track Title";
|
||||||
var expectedPath = @"C:\Test\Fake- The Artist\Fake- The Artist Fake- Album\02 - Track Title.flac";
|
var expectedPath = @"C:\Test\Fake- The Artist\02 - Track Title.flac";
|
||||||
|
|
||||||
var fakeArtist = Builder<Artist>.CreateNew()
|
var fakeArtist = Builder<Artist>.CreateNew()
|
||||||
.With(s => s.Name = "Fake: The Artist")
|
.With(s => s.Name = "Fake: The Artist")
|
||||||
.With(s => s.Path = @"C:\Test\Fake- The Artist".AsOsAgnostic())
|
.With(s => s.Path = @"C:\Test\Fake- The Artist".AsOsAgnostic())
|
||||||
.With(s => s.AlbumFolder = true)
|
|
||||||
.Build();
|
.Build();
|
||||||
|
|
||||||
var fakeAlbum = Builder<Album>.CreateNew()
|
Subject.BuildTrackFilePath(fakeArtist, filename, ".flac").Should().Be(expectedPath.AsOsAgnostic());
|
||||||
.With(s => s.Title = "Fake: Album")
|
|
||||||
.Build();
|
|
||||||
|
|
||||||
_namingConfig.AlbumFolderFormat = "{Artist Name} {Album Title}";
|
|
||||||
|
|
||||||
Subject.BuildTrackFilePath(fakeArtist, fakeAlbum, filename, ".flac").Should().Be(expectedPath.AsOsAgnostic());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,134 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using FizzWare.NBuilder;
|
||||||
|
using FluentAssertions;
|
||||||
|
using NUnit.Framework;
|
||||||
|
using NzbDrone.Core.MediaFiles;
|
||||||
|
using NzbDrone.Core.Music;
|
||||||
|
using NzbDrone.Core.Organizer;
|
||||||
|
using NzbDrone.Core.Qualities;
|
||||||
|
using NzbDrone.Core.Test.Framework;
|
||||||
|
using NzbDrone.Test.Common;
|
||||||
|
|
||||||
|
namespace NzbDrone.Core.Test.OrganizerTests.FileNameBuilderTests
|
||||||
|
{
|
||||||
|
public class NestedFileNameBuilderFixture : CoreTest<FileNameBuilder>
|
||||||
|
{
|
||||||
|
private Artist _artist;
|
||||||
|
private Album _album;
|
||||||
|
private Medium _medium;
|
||||||
|
private Medium _medium2;
|
||||||
|
private AlbumRelease _release;
|
||||||
|
private Track _track1;
|
||||||
|
private TrackFile _trackFile;
|
||||||
|
private NamingConfig _namingConfig;
|
||||||
|
|
||||||
|
[SetUp]
|
||||||
|
public void Setup()
|
||||||
|
{
|
||||||
|
_artist = Builder<Artist>
|
||||||
|
.CreateNew()
|
||||||
|
.With(s => s.Name = "Linkin Park")
|
||||||
|
.With(s => s.Metadata = new ArtistMetadata
|
||||||
|
{
|
||||||
|
Disambiguation = "US Rock Band",
|
||||||
|
Name = "Linkin Park"
|
||||||
|
})
|
||||||
|
.Build();
|
||||||
|
|
||||||
|
_medium = Builder<Medium>
|
||||||
|
.CreateNew()
|
||||||
|
.With(m => m.Number = 3)
|
||||||
|
.Build();
|
||||||
|
|
||||||
|
_medium2 = Builder<Medium>
|
||||||
|
.CreateNew()
|
||||||
|
.With(m => m.Number = 4)
|
||||||
|
.Build();
|
||||||
|
|
||||||
|
_release = Builder<AlbumRelease>
|
||||||
|
.CreateNew()
|
||||||
|
.With(s => s.Media = new List<Medium> { _medium })
|
||||||
|
.With(s => s.Monitored = true)
|
||||||
|
.Build();
|
||||||
|
|
||||||
|
_album = Builder<Album>
|
||||||
|
.CreateNew()
|
||||||
|
.With(s => s.Title = "Hybrid Theory")
|
||||||
|
.With(s => s.ReleaseDate = new DateTime(2020, 1, 15))
|
||||||
|
.With(s => s.AlbumType = "Album")
|
||||||
|
.With(s => s.Disambiguation = "The Best Album")
|
||||||
|
.Build();
|
||||||
|
|
||||||
|
_namingConfig = NamingConfig.Default;
|
||||||
|
_namingConfig.RenameTracks = true;
|
||||||
|
|
||||||
|
Mocker.GetMock<INamingConfigService>()
|
||||||
|
.Setup(c => c.GetConfig()).Returns(_namingConfig);
|
||||||
|
|
||||||
|
_track1 = Builder<Track>.CreateNew()
|
||||||
|
.With(e => e.Title = "City Sushi")
|
||||||
|
.With(e => e.AbsoluteTrackNumber = 6)
|
||||||
|
.With(e => e.AlbumRelease = _release)
|
||||||
|
.With(e => e.MediumNumber = _medium.Number)
|
||||||
|
.Build();
|
||||||
|
|
||||||
|
_trackFile = Builder<TrackFile>.CreateNew()
|
||||||
|
.With(e => e.Quality = new QualityModel(Quality.MP3_256))
|
||||||
|
.With(e => e.ReleaseGroup = "LidarrTest")
|
||||||
|
.With(e => e.MediaInfo = new Parser.Model.MediaInfoModel
|
||||||
|
{
|
||||||
|
AudioBitrate = 320,
|
||||||
|
AudioBits = 16,
|
||||||
|
AudioChannels = 2,
|
||||||
|
AudioFormat = "Flac Audio",
|
||||||
|
AudioSampleRate = 44100
|
||||||
|
}).Build();
|
||||||
|
|
||||||
|
Mocker.GetMock<IQualityDefinitionService>()
|
||||||
|
.Setup(v => v.Get(Moq.It.IsAny<Quality>()))
|
||||||
|
.Returns<Quality>(v => Quality.DefaultQualityDefinitions.First(c => c.Quality == v));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void should_build_nested_standard_track_filename_with_forward_slash()
|
||||||
|
{
|
||||||
|
_namingConfig.StandardTrackFormat = "{Album Title} {(Release Year)}/{Artist Name} - {track:00} [{Quality Title}] {[Quality Proper]}";
|
||||||
|
|
||||||
|
Subject.BuildTrackFileName(new List<Track> { _track1 }, _artist, _album, _trackFile)
|
||||||
|
.Should().Be("Hybrid Theory (2020)\\Linkin Park - 06 [MP3-256]".AsOsAgnostic());
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void should_build_nested_standard_track_filename_with_back_slash()
|
||||||
|
{
|
||||||
|
_namingConfig.StandardTrackFormat = "{Album Title} {(Release Year)}\\{Artist Name} - {track:00} [{Quality Title}] {[Quality Proper]}";
|
||||||
|
|
||||||
|
Subject.BuildTrackFileName(new List<Track> { _track1 }, _artist, _album, _trackFile)
|
||||||
|
.Should().Be("Hybrid Theory (2020)\\Linkin Park - 06 [MP3-256]".AsOsAgnostic());
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void should_build_nested_multi_track_filename_with_forward_slash()
|
||||||
|
{
|
||||||
|
_namingConfig.MultiDiscTrackFormat = "{Album Title} {(Release Year)}/CD {medium:00}/{Artist Name} - {track:00} [{Quality Title}] {[Quality Proper]}";
|
||||||
|
|
||||||
|
_release.Media.Add(_medium2);
|
||||||
|
|
||||||
|
Subject.BuildTrackFileName(new List<Track> { _track1 }, _artist, _album, _trackFile)
|
||||||
|
.Should().Be("Hybrid Theory (2020)\\CD 03\\Linkin Park - 06 [MP3-256]".AsOsAgnostic());
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void should_build_nested_multi_track_filename_with_back_slash()
|
||||||
|
{
|
||||||
|
_namingConfig.MultiDiscTrackFormat = "{Album Title} {(Release Year)}\\CD {medium:00}\\{Artist Name} - {track:00} [{Quality Title}] {[Quality Proper]}";
|
||||||
|
|
||||||
|
_release.Media.Add(_medium2);
|
||||||
|
|
||||||
|
Subject.BuildTrackFileName(new List<Track> { _track1 }, _artist, _album, _trackFile)
|
||||||
|
.Should().Be("Hybrid Theory (2020)\\CD 03\\Linkin Park - 06 [MP3-256]".AsOsAgnostic());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,35 +0,0 @@
|
||||||
using FluentAssertions;
|
|
||||||
using NUnit.Framework;
|
|
||||||
using NzbDrone.Core.Music;
|
|
||||||
using NzbDrone.Core.Organizer;
|
|
||||||
using NzbDrone.Core.Test.Framework;
|
|
||||||
|
|
||||||
namespace NzbDrone.Core.Test.OrganizerTests
|
|
||||||
{
|
|
||||||
[TestFixture]
|
|
||||||
public class GetAlbumFolderFixture : CoreTest<FileNameBuilder>
|
|
||||||
{
|
|
||||||
private NamingConfig _namingConfig;
|
|
||||||
|
|
||||||
[SetUp]
|
|
||||||
public void Setup()
|
|
||||||
{
|
|
||||||
_namingConfig = NamingConfig.Default;
|
|
||||||
|
|
||||||
Mocker.GetMock<INamingConfigService>()
|
|
||||||
.Setup(c => c.GetConfig()).Returns(_namingConfig);
|
|
||||||
}
|
|
||||||
|
|
||||||
[TestCase("Venture Bros.", "Today", "{Artist.Name}.{Album.Title}", "Venture.Bros.Today")]
|
|
||||||
[TestCase("Venture Bros.", "Today", "{Artist Name} {Album Title}", "Venture Bros. Today")]
|
|
||||||
public void should_use_albumFolderFormat_to_build_folder_name(string artistName, string albumTitle, string format, string expected)
|
|
||||||
{
|
|
||||||
_namingConfig.AlbumFolderFormat = format;
|
|
||||||
|
|
||||||
var artist = new Artist { Name = artistName };
|
|
||||||
var album = new Album { Title = albumTitle };
|
|
||||||
|
|
||||||
Subject.GetAlbumFolder(artist, album, _namingConfig).Should().Be(expected);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,19 @@
|
||||||
|
using FluentMigrator;
|
||||||
|
using NzbDrone.Core.Datastore.Migration.Framework;
|
||||||
|
|
||||||
|
namespace NzbDrone.Core.Datastore.Migration
|
||||||
|
{
|
||||||
|
[Migration(42)]
|
||||||
|
public class remove_album_folders : NzbDroneMigrationBase
|
||||||
|
{
|
||||||
|
protected override void MainDbUpgrade()
|
||||||
|
{
|
||||||
|
Delete.Column("AlbumFolder").FromTable("Artists");
|
||||||
|
|
||||||
|
Execute.Sql("UPDATE NamingConfig SET StandardTrackFormat = AlbumFolderFormat || '/' || StandardTrackFormat");
|
||||||
|
Execute.Sql("UPDATE NamingConfig SET MultiDiscTrackFormat = AlbumFolderFormat || '/' || MultiDiscTrackFormat");
|
||||||
|
|
||||||
|
Delete.Column("AlbumFolderFormat").FromTable("NamingConfig");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -202,7 +202,6 @@ namespace NzbDrone.Core.ImportLists
|
||||||
QualityProfileId = importList.ProfileId,
|
QualityProfileId = importList.ProfileId,
|
||||||
MetadataProfileId = importList.MetadataProfileId,
|
MetadataProfileId = importList.MetadataProfileId,
|
||||||
Tags = importList.Tags,
|
Tags = importList.Tags,
|
||||||
AlbumFolder = true,
|
|
||||||
AddOptions = new AddArtistOptions
|
AddOptions = new AddArtistOptions
|
||||||
{
|
{
|
||||||
SearchForMissingAlbums = monitored,
|
SearchForMissingAlbums = monitored,
|
||||||
|
@ -275,7 +274,6 @@ namespace NzbDrone.Core.ImportLists
|
||||||
QualityProfileId = importList.ProfileId,
|
QualityProfileId = importList.ProfileId,
|
||||||
MetadataProfileId = importList.MetadataProfileId,
|
MetadataProfileId = importList.MetadataProfileId,
|
||||||
Tags = importList.Tags,
|
Tags = importList.Tags,
|
||||||
AlbumFolder = true,
|
|
||||||
AddOptions = new AddArtistOptions
|
AddOptions = new AddArtistOptions
|
||||||
{
|
{
|
||||||
SearchForMissingAlbums = monitored,
|
SearchForMissingAlbums = monitored,
|
||||||
|
|
|
@ -93,7 +93,7 @@ namespace NzbDrone.Core.MediaFiles
|
||||||
var album = _albumService.GetAlbum(tracksInFile.First().AlbumId);
|
var album = _albumService.GetAlbum(tracksInFile.First().AlbumId);
|
||||||
|
|
||||||
var newName = _filenameBuilder.BuildTrackFileName(tracksInFile, artist, album, file);
|
var newName = _filenameBuilder.BuildTrackFileName(tracksInFile, artist, album, file);
|
||||||
var newPath = _filenameBuilder.BuildTrackFilePath(artist, album, newName, Path.GetExtension(trackFilePath));
|
var newPath = _filenameBuilder.BuildTrackFilePath(artist, newName, Path.GetExtension(trackFilePath));
|
||||||
|
|
||||||
if (!trackFilePath.PathEquals(newPath, StringComparison.Ordinal))
|
if (!trackFilePath.PathEquals(newPath, StringComparison.Ordinal))
|
||||||
{
|
{
|
||||||
|
|
|
@ -66,7 +66,7 @@ namespace NzbDrone.Core.MediaFiles
|
||||||
var tracks = _trackService.GetTracksByFileId(trackFile.Id);
|
var tracks = _trackService.GetTracksByFileId(trackFile.Id);
|
||||||
var album = _albumService.GetAlbum(trackFile.AlbumId);
|
var album = _albumService.GetAlbum(trackFile.AlbumId);
|
||||||
var newFileName = _buildFileNames.BuildTrackFileName(tracks, artist, album, trackFile);
|
var newFileName = _buildFileNames.BuildTrackFileName(tracks, artist, album, trackFile);
|
||||||
var filePath = _buildFileNames.BuildTrackFilePath(artist, album, newFileName, Path.GetExtension(trackFile.Path));
|
var filePath = _buildFileNames.BuildTrackFilePath(artist, newFileName, Path.GetExtension(trackFile.Path));
|
||||||
|
|
||||||
EnsureTrackFolder(trackFile, artist, album, filePath);
|
EnsureTrackFolder(trackFile, artist, album, filePath);
|
||||||
|
|
||||||
|
@ -78,7 +78,7 @@ namespace NzbDrone.Core.MediaFiles
|
||||||
public TrackFile MoveTrackFile(TrackFile trackFile, LocalTrack localTrack)
|
public TrackFile MoveTrackFile(TrackFile trackFile, LocalTrack localTrack)
|
||||||
{
|
{
|
||||||
var newFileName = _buildFileNames.BuildTrackFileName(localTrack.Tracks, localTrack.Artist, localTrack.Album, trackFile);
|
var newFileName = _buildFileNames.BuildTrackFileName(localTrack.Tracks, localTrack.Artist, localTrack.Album, trackFile);
|
||||||
var filePath = _buildFileNames.BuildTrackFilePath(localTrack.Artist, localTrack.Album, newFileName, Path.GetExtension(localTrack.Path));
|
var filePath = _buildFileNames.BuildTrackFilePath(localTrack.Artist, newFileName, Path.GetExtension(localTrack.Path));
|
||||||
|
|
||||||
EnsureTrackFolder(trackFile, localTrack, filePath);
|
EnsureTrackFolder(trackFile, localTrack, filePath);
|
||||||
|
|
||||||
|
@ -90,7 +90,7 @@ namespace NzbDrone.Core.MediaFiles
|
||||||
public TrackFile CopyTrackFile(TrackFile trackFile, LocalTrack localTrack)
|
public TrackFile CopyTrackFile(TrackFile trackFile, LocalTrack localTrack)
|
||||||
{
|
{
|
||||||
var newFileName = _buildFileNames.BuildTrackFileName(localTrack.Tracks, localTrack.Artist, localTrack.Album, trackFile);
|
var newFileName = _buildFileNames.BuildTrackFileName(localTrack.Tracks, localTrack.Artist, localTrack.Album, trackFile);
|
||||||
var filePath = _buildFileNames.BuildTrackFilePath(localTrack.Artist, localTrack.Album, newFileName, Path.GetExtension(localTrack.Path));
|
var filePath = _buildFileNames.BuildTrackFilePath(localTrack.Artist, newFileName, Path.GetExtension(localTrack.Path));
|
||||||
|
|
||||||
EnsureTrackFolder(trackFile, localTrack, filePath);
|
EnsureTrackFolder(trackFile, localTrack, filePath);
|
||||||
|
|
||||||
|
@ -132,13 +132,6 @@ namespace NzbDrone.Core.MediaFiles
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
_mediaFileAttributeService.SetFolderLastWriteTime(artist.Path, trackFile.DateAdded);
|
_mediaFileAttributeService.SetFolderLastWriteTime(artist.Path, trackFile.DateAdded);
|
||||||
|
|
||||||
if (artist.AlbumFolder)
|
|
||||||
{
|
|
||||||
var albumFolder = Path.GetDirectoryName(destinationFilePath);
|
|
||||||
|
|
||||||
_mediaFileAttributeService.SetFolderLastWriteTime(albumFolder, trackFile.DateAdded);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
|
@ -158,7 +151,6 @@ namespace NzbDrone.Core.MediaFiles
|
||||||
private void EnsureTrackFolder(TrackFile trackFile, Artist artist, Album album, string filePath)
|
private void EnsureTrackFolder(TrackFile trackFile, Artist artist, Album album, string filePath)
|
||||||
{
|
{
|
||||||
var trackFolder = Path.GetDirectoryName(filePath);
|
var trackFolder = Path.GetDirectoryName(filePath);
|
||||||
var albumFolder = _buildFileNames.BuildAlbumPath(artist, album);
|
|
||||||
var artistFolder = artist.Path;
|
var artistFolder = artist.Path;
|
||||||
var rootFolder = new OsPath(artistFolder).Directory.FullPath;
|
var rootFolder = new OsPath(artistFolder).Directory.FullPath;
|
||||||
|
|
||||||
|
@ -170,7 +162,7 @@ namespace NzbDrone.Core.MediaFiles
|
||||||
var changed = false;
|
var changed = false;
|
||||||
var newEvent = new TrackFolderCreatedEvent(artist, trackFile);
|
var newEvent = new TrackFolderCreatedEvent(artist, trackFile);
|
||||||
|
|
||||||
_rootFolderWatchingService.ReportFileSystemChangeBeginning(artistFolder, albumFolder, trackFolder);
|
_rootFolderWatchingService.ReportFileSystemChangeBeginning(artistFolder, trackFolder);
|
||||||
|
|
||||||
if (!_diskProvider.FolderExists(artistFolder))
|
if (!_diskProvider.FolderExists(artistFolder))
|
||||||
{
|
{
|
||||||
|
@ -179,16 +171,10 @@ namespace NzbDrone.Core.MediaFiles
|
||||||
changed = true;
|
changed = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (artistFolder != albumFolder && !_diskProvider.FolderExists(albumFolder))
|
if (artistFolder != trackFolder && !_diskProvider.FolderExists(trackFolder))
|
||||||
{
|
|
||||||
CreateFolder(albumFolder);
|
|
||||||
newEvent.AlbumFolder = albumFolder;
|
|
||||||
changed = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (albumFolder != trackFolder && !_diskProvider.FolderExists(trackFolder))
|
|
||||||
{
|
{
|
||||||
CreateFolder(trackFolder);
|
CreateFolder(trackFolder);
|
||||||
|
newEvent.AlbumFolder = trackFolder;
|
||||||
newEvent.TrackFolder = trackFolder;
|
newEvent.TrackFolder = trackFolder;
|
||||||
changed = true;
|
changed = true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -336,7 +336,6 @@ namespace NzbDrone.Core.MediaFiles.TrackImport
|
||||||
artist.RootFolderPath = rootFolder.Path;
|
artist.RootFolderPath = rootFolder.Path;
|
||||||
artist.MetadataProfileId = rootFolder.DefaultMetadataProfileId;
|
artist.MetadataProfileId = rootFolder.DefaultMetadataProfileId;
|
||||||
artist.QualityProfileId = rootFolder.DefaultQualityProfileId;
|
artist.QualityProfileId = rootFolder.DefaultQualityProfileId;
|
||||||
artist.AlbumFolder = true;
|
|
||||||
artist.Monitored = rootFolder.DefaultMonitorOption != MonitorTypes.None;
|
artist.Monitored = rootFolder.DefaultMonitorOption != MonitorTypes.None;
|
||||||
artist.Tags = rootFolder.DefaultTags;
|
artist.Tags = rootFolder.DefaultTags;
|
||||||
artist.AddOptions = new AddArtistOptions
|
artist.AddOptions = new AddArtistOptions
|
||||||
|
|
|
@ -21,7 +21,6 @@ namespace NzbDrone.Core.Music
|
||||||
public string CleanName { get; set; }
|
public string CleanName { get; set; }
|
||||||
public string SortName { get; set; }
|
public string SortName { get; set; }
|
||||||
public bool Monitored { get; set; }
|
public bool Monitored { get; set; }
|
||||||
public bool AlbumFolder { get; set; }
|
|
||||||
public DateTime? LastInfoSync { get; set; }
|
public DateTime? LastInfoSync { get; set; }
|
||||||
public string Path { get; set; }
|
public string Path { get; set; }
|
||||||
public string RootFolderPath { get; set; }
|
public string RootFolderPath { get; set; }
|
||||||
|
@ -71,7 +70,6 @@ namespace NzbDrone.Core.Music
|
||||||
Id = other.Id;
|
Id = other.Id;
|
||||||
ArtistMetadataId = other.ArtistMetadataId;
|
ArtistMetadataId = other.ArtistMetadataId;
|
||||||
Monitored = other.Monitored;
|
Monitored = other.Monitored;
|
||||||
AlbumFolder = other.AlbumFolder;
|
|
||||||
LastInfoSync = other.LastInfoSync;
|
LastInfoSync = other.LastInfoSync;
|
||||||
Path = other.Path;
|
Path = other.Path;
|
||||||
RootFolderPath = other.RootFolderPath;
|
RootFolderPath = other.RootFolderPath;
|
||||||
|
@ -95,7 +93,6 @@ namespace NzbDrone.Core.Music
|
||||||
AddOptions = other.AddOptions;
|
AddOptions = other.AddOptions;
|
||||||
RootFolderPath = other.RootFolderPath;
|
RootFolderPath = other.RootFolderPath;
|
||||||
Monitored = other.Monitored;
|
Monitored = other.Monitored;
|
||||||
AlbumFolder = other.AlbumFolder;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -125,7 +125,6 @@ namespace NzbDrone.Core.Music
|
||||||
QualityProfileId = oldArtist.QualityProfileId,
|
QualityProfileId = oldArtist.QualityProfileId,
|
||||||
RootFolderPath = oldArtist.RootFolderPath,
|
RootFolderPath = oldArtist.RootFolderPath,
|
||||||
Monitored = oldArtist.Monitored,
|
Monitored = oldArtist.Monitored,
|
||||||
AlbumFolder = oldArtist.AlbumFolder,
|
|
||||||
Tags = oldArtist.Tags
|
Tags = oldArtist.Tags
|
||||||
};
|
};
|
||||||
_logger.Debug($"Adding missing parent artist {addArtist}");
|
_logger.Debug($"Adding missing parent artist {addArtist}");
|
||||||
|
|
|
@ -18,11 +18,9 @@ namespace NzbDrone.Core.Organizer
|
||||||
public interface IBuildFileNames
|
public interface IBuildFileNames
|
||||||
{
|
{
|
||||||
string BuildTrackFileName(List<Track> tracks, Artist artist, Album album, TrackFile trackFile, NamingConfig namingConfig = null, List<string> preferredWords = null);
|
string BuildTrackFileName(List<Track> tracks, Artist artist, Album album, TrackFile trackFile, NamingConfig namingConfig = null, List<string> preferredWords = null);
|
||||||
string BuildTrackFilePath(Artist artist, Album album, string fileName, string extension);
|
string BuildTrackFilePath(Artist artist, string fileName, string extension);
|
||||||
string BuildAlbumPath(Artist artist, Album album);
|
|
||||||
BasicNamingConfig GetBasicNamingConfig(NamingConfig nameSpec);
|
BasicNamingConfig GetBasicNamingConfig(NamingConfig nameSpec);
|
||||||
string GetArtistFolder(Artist artist, NamingConfig namingConfig = null);
|
string GetArtistFolder(Artist artist, NamingConfig namingConfig = null);
|
||||||
string GetAlbumFolder(Artist artist, Album album, NamingConfig namingConfig = null);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public class FileNameBuilder : IBuildFileNames
|
public class FileNameBuilder : IBuildFileNames
|
||||||
|
@ -108,16 +106,10 @@ namespace NzbDrone.Core.Organizer
|
||||||
pattern = namingConfig.MultiDiscTrackFormat;
|
pattern = namingConfig.MultiDiscTrackFormat;
|
||||||
}
|
}
|
||||||
|
|
||||||
var subFolders = pattern.Split(new[] { '/', '\\' }, StringSplitOptions.RemoveEmptyEntries);
|
|
||||||
var safePattern = subFolders.Aggregate("", (current, folderLevel) => Path.Combine(current, folderLevel));
|
|
||||||
|
|
||||||
var tokenHandlers = new Dictionary<string, Func<TokenMatch, string>>(FileNameBuilderTokenEqualityComparer.Instance);
|
var tokenHandlers = new Dictionary<string, Func<TokenMatch, string>>(FileNameBuilderTokenEqualityComparer.Instance);
|
||||||
|
|
||||||
tracks = tracks.OrderBy(e => e.AlbumReleaseId).ThenBy(e => e.TrackNumber).ToList();
|
tracks = tracks.OrderBy(e => e.AlbumReleaseId).ThenBy(e => e.TrackNumber).ToList();
|
||||||
|
|
||||||
safePattern = FormatTrackNumberTokens(safePattern, "", tracks);
|
|
||||||
safePattern = FormatMediumNumberTokens(safePattern, "", tracks);
|
|
||||||
|
|
||||||
AddArtistTokens(tokenHandlers, artist);
|
AddArtistTokens(tokenHandlers, artist);
|
||||||
AddAlbumTokens(tokenHandlers, album);
|
AddAlbumTokens(tokenHandlers, album);
|
||||||
AddMediumTokens(tokenHandlers, tracks.First().AlbumRelease.Value.Media.SingleOrDefault(m => m.Number == tracks.First().MediumNumber));
|
AddMediumTokens(tokenHandlers, tracks.First().AlbumRelease.Value.Media.SingleOrDefault(m => m.Number == tracks.First().MediumNumber));
|
||||||
|
@ -127,36 +119,37 @@ namespace NzbDrone.Core.Organizer
|
||||||
AddMediaInfoTokens(tokenHandlers, trackFile);
|
AddMediaInfoTokens(tokenHandlers, trackFile);
|
||||||
AddPreferredWords(tokenHandlers, artist, trackFile, preferredWords);
|
AddPreferredWords(tokenHandlers, artist, trackFile, preferredWords);
|
||||||
|
|
||||||
var fileName = ReplaceTokens(safePattern, tokenHandlers, namingConfig).Trim();
|
var splitPatterns = pattern.Split(new char[] { '\\', '/' }, StringSplitOptions.RemoveEmptyEntries);
|
||||||
fileName = FileNameCleanupRegex.Replace(fileName, match => match.Captures[0].Value[0].ToString());
|
var components = new List<string>();
|
||||||
fileName = TrimSeparatorsRegex.Replace(fileName, string.Empty);
|
|
||||||
|
|
||||||
return fileName;
|
foreach (var s in splitPatterns)
|
||||||
|
{
|
||||||
|
var splitPattern = s;
|
||||||
|
|
||||||
|
splitPattern = FormatTrackNumberTokens(splitPattern, "", tracks);
|
||||||
|
splitPattern = FormatMediumNumberTokens(splitPattern, "", tracks);
|
||||||
|
|
||||||
|
var component = ReplaceTokens(splitPattern, tokenHandlers, namingConfig).Trim();
|
||||||
|
|
||||||
|
component = FileNameCleanupRegex.Replace(component, match => match.Captures[0].Value[0].ToString());
|
||||||
|
component = TrimSeparatorsRegex.Replace(component, string.Empty);
|
||||||
|
|
||||||
|
if (component.IsNotNullOrWhiteSpace())
|
||||||
|
{
|
||||||
|
components.Add(component);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public string BuildTrackFilePath(Artist artist, Album album, string fileName, string extension)
|
return Path.Combine(components.ToArray());
|
||||||
|
}
|
||||||
|
|
||||||
|
public string BuildTrackFilePath(Artist artist, string fileName, string extension)
|
||||||
{
|
{
|
||||||
Ensure.That(extension, () => extension).IsNotNullOrWhiteSpace();
|
Ensure.That(extension, () => extension).IsNotNullOrWhiteSpace();
|
||||||
|
|
||||||
var path = BuildAlbumPath(artist, album);
|
|
||||||
|
|
||||||
return Path.Combine(path, fileName + extension);
|
|
||||||
}
|
|
||||||
|
|
||||||
public string BuildAlbumPath(Artist artist, Album album)
|
|
||||||
{
|
|
||||||
var path = artist.Path;
|
var path = artist.Path;
|
||||||
|
|
||||||
if (artist.AlbumFolder)
|
return Path.Combine(path, fileName + extension);
|
||||||
{
|
|
||||||
var albumFolder = GetAlbumFolder(artist, album);
|
|
||||||
|
|
||||||
albumFolder = CleanFileName(albumFolder);
|
|
||||||
|
|
||||||
path = Path.Combine(path, albumFolder);
|
|
||||||
}
|
|
||||||
|
|
||||||
return path;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public BasicNamingConfig GetBasicNamingConfig(NamingConfig nameSpec)
|
public BasicNamingConfig GetBasicNamingConfig(NamingConfig nameSpec)
|
||||||
|
@ -211,26 +204,28 @@ namespace NzbDrone.Core.Organizer
|
||||||
namingConfig = _namingConfigService.GetConfig();
|
namingConfig = _namingConfigService.GetConfig();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var pattern = namingConfig.ArtistFolderFormat;
|
||||||
var tokenHandlers = new Dictionary<string, Func<TokenMatch, string>>(FileNameBuilderTokenEqualityComparer.Instance);
|
var tokenHandlers = new Dictionary<string, Func<TokenMatch, string>>(FileNameBuilderTokenEqualityComparer.Instance);
|
||||||
|
|
||||||
AddArtistTokens(tokenHandlers, artist);
|
AddArtistTokens(tokenHandlers, artist);
|
||||||
|
|
||||||
return CleanFolderName(ReplaceTokens(namingConfig.ArtistFolderFormat, tokenHandlers, namingConfig));
|
var splitPatterns = pattern.Split(new char[] { '\\', '/' }, StringSplitOptions.RemoveEmptyEntries);
|
||||||
|
var components = new List<string>();
|
||||||
|
|
||||||
|
foreach (var s in splitPatterns)
|
||||||
|
{
|
||||||
|
var splitPattern = s;
|
||||||
|
|
||||||
|
var component = ReplaceTokens(splitPattern, tokenHandlers, namingConfig);
|
||||||
|
component = CleanFolderName(component);
|
||||||
|
|
||||||
|
if (component.IsNotNullOrWhiteSpace())
|
||||||
|
{
|
||||||
|
components.Add(component);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public string GetAlbumFolder(Artist artist, Album album, NamingConfig namingConfig = null)
|
return Path.Combine(components.ToArray());
|
||||||
{
|
|
||||||
if (namingConfig == null)
|
|
||||||
{
|
|
||||||
namingConfig = _namingConfigService.GetConfig();
|
|
||||||
}
|
|
||||||
|
|
||||||
var tokenHandlers = new Dictionary<string, Func<TokenMatch, string>>(FileNameBuilderTokenEqualityComparer.Instance);
|
|
||||||
|
|
||||||
AddAlbumTokens(tokenHandlers, album);
|
|
||||||
AddArtistTokens(tokenHandlers, artist);
|
|
||||||
|
|
||||||
return CleanFolderName(ReplaceTokens(namingConfig.AlbumFolderFormat, tokenHandlers, namingConfig));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static string CleanTitle(string title)
|
public static string CleanTitle(string title)
|
||||||
|
|
|
@ -11,7 +11,6 @@ namespace NzbDrone.Core.Organizer
|
||||||
SampleResult GetStandardTrackSample(NamingConfig nameSpec);
|
SampleResult GetStandardTrackSample(NamingConfig nameSpec);
|
||||||
SampleResult GetMultiDiscTrackSample(NamingConfig nameSpec);
|
SampleResult GetMultiDiscTrackSample(NamingConfig nameSpec);
|
||||||
string GetArtistFolderSample(NamingConfig nameSpec);
|
string GetArtistFolderSample(NamingConfig nameSpec);
|
||||||
string GetAlbumFolderSample(NamingConfig nameSpec);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public class FileNameSampleService : IFilenameSampleService
|
public class FileNameSampleService : IFilenameSampleService
|
||||||
|
@ -156,11 +155,6 @@ namespace NzbDrone.Core.Organizer
|
||||||
return _buildFileNames.GetArtistFolder(_standardArtist, nameSpec);
|
return _buildFileNames.GetArtistFolder(_standardArtist, nameSpec);
|
||||||
}
|
}
|
||||||
|
|
||||||
public string GetAlbumFolderSample(NamingConfig nameSpec)
|
|
||||||
{
|
|
||||||
return _buildFileNames.GetAlbumFolder(_standardArtist, _standardAlbum, nameSpec);
|
|
||||||
}
|
|
||||||
|
|
||||||
private string BuildTrackSample(List<Track> tracks, Artist artist, Album album, TrackFile trackFile, NamingConfig nameSpec)
|
private string BuildTrackSample(List<Track> tracks, Artist artist, Album album, TrackFile trackFile, NamingConfig nameSpec)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
|
|
|
@ -8,10 +8,9 @@ namespace NzbDrone.Core.Organizer
|
||||||
{
|
{
|
||||||
RenameTracks = false,
|
RenameTracks = false,
|
||||||
ReplaceIllegalCharacters = true,
|
ReplaceIllegalCharacters = true,
|
||||||
StandardTrackFormat = "{Artist Name} - {Album Title} - {track:00} - {Track Title}",
|
StandardTrackFormat = "{Album Title} ({Release Year})/{Artist Name} - {Album Title} - {track:00} - {Track Title}",
|
||||||
MultiDiscTrackFormat = "{Medium Format} {medium:00}/{Artist Name} - {Album Title} - {track:00} - {Track Title}",
|
MultiDiscTrackFormat = "{Album Title} ({Release Year})/{Medium Format} {medium:00}/{Artist Name} - {Album Title} - {track:00} - {Track Title}",
|
||||||
ArtistFolderFormat = "{Artist Name}",
|
ArtistFolderFormat = "{Artist Name}",
|
||||||
AlbumFolderFormat = "{Album Title} ({Release Year})"
|
|
||||||
};
|
};
|
||||||
|
|
||||||
public bool RenameTracks { get; set; }
|
public bool RenameTracks { get; set; }
|
||||||
|
@ -19,6 +18,5 @@ namespace NzbDrone.Core.Organizer
|
||||||
public string StandardTrackFormat { get; set; }
|
public string StandardTrackFormat { get; set; }
|
||||||
public string MultiDiscTrackFormat { get; set; }
|
public string MultiDiscTrackFormat { get; set; }
|
||||||
public string ArtistFolderFormat { get; set; }
|
public string ArtistFolderFormat { get; set; }
|
||||||
public string AlbumFolderFormat { get; set; }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue