mirror of https://github.com/lidarr/Lidarr
New: Remove AlbumFolder, Support Nested Track Format
This commit is contained in:
parent
8063a32acd
commit
774a3597de
|
@ -72,7 +72,6 @@ class EditArtistModalContent extends Component {
|
|||
|
||||
const {
|
||||
monitored,
|
||||
albumFolder,
|
||||
qualityProfileId,
|
||||
metadataProfileId,
|
||||
path,
|
||||
|
@ -99,18 +98,6 @@ class EditArtistModalContent extends Component {
|
|||
/>
|
||||
</FormGroup>
|
||||
|
||||
<FormGroup>
|
||||
<FormLabel>Use Album Folder</FormLabel>
|
||||
|
||||
<FormInputGroup
|
||||
type={inputTypes.CHECK}
|
||||
name="albumFolder"
|
||||
helpText="Sort tracks into album folders"
|
||||
{...albumFolder}
|
||||
onChange={onInputChange}
|
||||
/>
|
||||
</FormGroup>
|
||||
|
||||
<FormGroup>
|
||||
<FormLabel>Quality Profile</FormLabel>
|
||||
|
||||
|
|
|
@ -39,7 +39,6 @@ function createMapStateToProps() {
|
|||
|
||||
const artistSettings = _.pick(artist, [
|
||||
'monitored',
|
||||
'albumFolder',
|
||||
'qualityProfileId',
|
||||
'metadataProfileId',
|
||||
'path',
|
||||
|
|
|
@ -27,7 +27,6 @@ class ArtistEditorFooter extends Component {
|
|||
monitored: NO_CHANGE,
|
||||
qualityProfileId: NO_CHANGE,
|
||||
metadataProfileId: NO_CHANGE,
|
||||
albumFolder: NO_CHANGE,
|
||||
rootFolderPath: NO_CHANGE,
|
||||
savingTags: false,
|
||||
isDeleteArtistModalOpen: false,
|
||||
|
@ -48,7 +47,6 @@ class ArtistEditorFooter extends Component {
|
|||
monitored: NO_CHANGE,
|
||||
qualityProfileId: NO_CHANGE,
|
||||
metadataProfileId: NO_CHANGE,
|
||||
albumFolder: NO_CHANGE,
|
||||
rootFolderPath: NO_CHANGE,
|
||||
savingTags: false
|
||||
});
|
||||
|
@ -75,9 +73,6 @@ class ArtistEditorFooter extends Component {
|
|||
case 'monitored':
|
||||
this.props.onSaveSelected({ [name]: value === 'monitored' });
|
||||
break;
|
||||
case 'albumFolder':
|
||||
this.props.onSaveSelected({ [name]: value === 'yes' });
|
||||
break;
|
||||
default:
|
||||
this.props.onSaveSelected({ [name]: value });
|
||||
}
|
||||
|
@ -152,7 +147,6 @@ class ArtistEditorFooter extends Component {
|
|||
monitored,
|
||||
qualityProfileId,
|
||||
metadataProfileId,
|
||||
albumFolder,
|
||||
rootFolderPath,
|
||||
savingTags,
|
||||
isTagsModalOpen,
|
||||
|
@ -167,12 +161,6 @@ class ArtistEditorFooter extends Component {
|
|||
{ key: 'unmonitored', value: 'Unmonitored' }
|
||||
];
|
||||
|
||||
const albumFolderOptions = [
|
||||
{ key: NO_CHANGE, value: 'No Change', disabled: true },
|
||||
{ key: 'yes', value: 'Yes' },
|
||||
{ key: 'no', value: 'No' }
|
||||
];
|
||||
|
||||
return (
|
||||
<PageContentFooter>
|
||||
<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') {
|
||||
return (
|
||||
<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 ArtistNameLink from 'Artist/ArtistNameLink';
|
||||
import ArtistStatusCell from 'Artist/Index/Table/ArtistStatusCell';
|
||||
import CheckInput from 'Components/Form/CheckInput';
|
||||
import TableRowCell from 'Components/Table/Cells/TableRowCell';
|
||||
import TableSelectCell from 'Components/Table/Cells/TableSelectCell';
|
||||
import TableRow from 'Components/Table/TableRow';
|
||||
import TagListConnector from 'Components/TagListConnector';
|
||||
import formatBytes from 'Utilities/Number/formatBytes';
|
||||
import styles from './ArtistEditorRow.css';
|
||||
|
||||
class ArtistEditorRow extends Component {
|
||||
|
||||
//
|
||||
// Listeners
|
||||
|
||||
onAlbumFolderChange = () => {
|
||||
// Mock handler to satisfy `onChange` being required for `CheckInput`.
|
||||
//
|
||||
}
|
||||
|
||||
//
|
||||
// Render
|
||||
|
||||
|
@ -33,7 +23,6 @@ class ArtistEditorRow extends Component {
|
|||
monitored,
|
||||
metadataProfile,
|
||||
qualityProfile,
|
||||
albumFolder,
|
||||
path,
|
||||
statistics,
|
||||
tags,
|
||||
|
@ -80,7 +69,6 @@ class ArtistEditorRow extends Component {
|
|||
return (
|
||||
<TableRowCell
|
||||
key={name}
|
||||
className={styles.title}
|
||||
>
|
||||
<ArtistNameLink
|
||||
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') {
|
||||
return (
|
||||
<TableRowCell key={name}>
|
||||
|
@ -165,7 +137,6 @@ ArtistEditorRow.propTypes = {
|
|||
monitored: PropTypes.bool.isRequired,
|
||||
metadataProfile: PropTypes.object.isRequired,
|
||||
qualityProfile: PropTypes.object.isRequired,
|
||||
albumFolder: PropTypes.bool.isRequired,
|
||||
path: PropTypes.string.isRequired,
|
||||
statistics: PropTypes.object.isRequired,
|
||||
tags: PropTypes.arrayOf(PropTypes.number).isRequired,
|
||||
|
|
|
@ -87,7 +87,6 @@ class AddNewAlbumModalContentConnector extends Component {
|
|||
monitor,
|
||||
qualityProfileId,
|
||||
metadataProfileId,
|
||||
albumFolder,
|
||||
tags
|
||||
} = this.props;
|
||||
|
||||
|
@ -97,7 +96,6 @@ class AddNewAlbumModalContentConnector extends Component {
|
|||
monitor: monitor.value,
|
||||
qualityProfileId: qualityProfileId.value,
|
||||
metadataProfileId: metadataProfileId.value,
|
||||
albumFolder: albumFolder.value,
|
||||
tags: tags.value,
|
||||
searchForNewAlbum
|
||||
});
|
||||
|
@ -125,7 +123,6 @@ AddNewAlbumModalContentConnector.propTypes = {
|
|||
qualityProfileId: PropTypes.object,
|
||||
metadataProfileId: PropTypes.object,
|
||||
noneMetadataProfileId: PropTypes.number.isRequired,
|
||||
albumFolder: PropTypes.object.isRequired,
|
||||
tags: PropTypes.object.isRequired,
|
||||
onModalClose: PropTypes.func.isRequired,
|
||||
setAddDefault: PropTypes.func.isRequired,
|
||||
|
|
|
@ -59,7 +59,6 @@ class AddNewArtistModalContentConnector extends Component {
|
|||
monitor,
|
||||
qualityProfileId,
|
||||
metadataProfileId,
|
||||
albumFolder,
|
||||
tags
|
||||
} = this.props;
|
||||
|
||||
|
@ -69,7 +68,6 @@ class AddNewArtistModalContentConnector extends Component {
|
|||
monitor: monitor.value,
|
||||
qualityProfileId: qualityProfileId.value,
|
||||
metadataProfileId: metadataProfileId.value,
|
||||
albumFolder: albumFolder.value,
|
||||
tags: tags.value,
|
||||
searchForMissingAlbums
|
||||
});
|
||||
|
@ -95,7 +93,6 @@ AddNewArtistModalContentConnector.propTypes = {
|
|||
monitor: PropTypes.object.isRequired,
|
||||
qualityProfileId: PropTypes.object,
|
||||
metadataProfileId: PropTypes.object,
|
||||
albumFolder: PropTypes.object.isRequired,
|
||||
tags: PropTypes.object.isRequired,
|
||||
onModalClose: PropTypes.func.isRequired,
|
||||
setAddDefault: PropTypes.func.isRequired,
|
||||
|
|
|
@ -35,7 +35,6 @@ class AddArtistOptionsForm extends Component {
|
|||
metadataProfileId,
|
||||
includeNoneMetadataProfile,
|
||||
showMetadataProfile,
|
||||
albumFolder,
|
||||
tags,
|
||||
onInputChange,
|
||||
...otherProps
|
||||
|
@ -119,17 +118,6 @@ class AddArtistOptionsForm extends Component {
|
|||
/>
|
||||
</FormGroup>
|
||||
|
||||
<FormGroup>
|
||||
<FormLabel>Album Folder</FormLabel>
|
||||
|
||||
<FormInputGroup
|
||||
type={inputTypes.CHECK}
|
||||
name="albumFolder"
|
||||
onChange={onInputChange}
|
||||
{...albumFolder}
|
||||
/>
|
||||
</FormGroup>
|
||||
|
||||
<FormGroup>
|
||||
<FormLabel>Tags</FormLabel>
|
||||
|
||||
|
@ -152,7 +140,6 @@ AddArtistOptionsForm.propTypes = {
|
|||
metadataProfileId: PropTypes.object,
|
||||
showMetadataProfile: PropTypes.bool.isRequired,
|
||||
includeNoneMetadataProfile: PropTypes.bool.isRequired,
|
||||
albumFolder: PropTypes.object.isRequired,
|
||||
tags: PropTypes.object.isRequired,
|
||||
onInputChange: PropTypes.func.isRequired
|
||||
};
|
||||
|
|
|
@ -61,16 +61,6 @@ class Naming extends Component {
|
|||
});
|
||||
}
|
||||
|
||||
onAlbumFolderNamingModalOpenClick = () => {
|
||||
this.setState({
|
||||
isNamingModalOpen: true,
|
||||
namingModalOptions: {
|
||||
name: 'albumFolderFormat',
|
||||
album: true
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
onNamingModalClose = () => {
|
||||
this.setState({ isNamingModalOpen: false });
|
||||
}
|
||||
|
@ -103,8 +93,6 @@ class Naming extends Component {
|
|||
const multiDiscTrackFormatErrors = [];
|
||||
const artistFolderFormatHelpTexts = [];
|
||||
const artistFolderFormatErrors = [];
|
||||
const albumFolderFormatHelpTexts = [];
|
||||
const albumFolderFormatErrors = [];
|
||||
|
||||
if (examplesPopulated) {
|
||||
if (examples.singleTrackExample) {
|
||||
|
@ -124,12 +112,6 @@ class Naming extends Component {
|
|||
} else {
|
||||
artistFolderFormatErrors.push({ message: 'Invalid Format' });
|
||||
}
|
||||
|
||||
if (examples.albumFolderExample) {
|
||||
albumFolderFormatHelpTexts.push(`Example: ${examples.albumFolderExample}`);
|
||||
} else {
|
||||
albumFolderFormatErrors.push({ message: 'Invalid Format' });
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
|
@ -225,21 +207,6 @@ class Naming extends Component {
|
|||
/>
|
||||
</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 &&
|
||||
<NamingModal
|
||||
|
|
|
@ -34,7 +34,6 @@ export const defaultState = {
|
|||
monitor: monitorOptions[0].key,
|
||||
qualityProfileId: 0,
|
||||
metadataProfileId: 0,
|
||||
albumFolder: true,
|
||||
tags: []
|
||||
}
|
||||
};
|
||||
|
|
|
@ -16,7 +16,6 @@ function createImportArtistItemSelector() {
|
|||
return {
|
||||
defaultMonitor: addArtist.defaults.monitor,
|
||||
defaultQualityProfileId: addArtist.defaults.qualityProfileId,
|
||||
defaultAlbumFolder: addArtist.defaults.albumFolder,
|
||||
...item,
|
||||
isExistingArtist
|
||||
};
|
||||
|
|
|
@ -6,7 +6,6 @@ function getNewArtist(artist, payload) {
|
|||
qualityProfileId,
|
||||
metadataProfileId,
|
||||
artistType,
|
||||
albumFolder,
|
||||
tags,
|
||||
searchForMissingAlbums = false
|
||||
} = payload;
|
||||
|
@ -22,7 +21,6 @@ function getNewArtist(artist, payload) {
|
|||
artist.metadataProfileId = metadataProfileId;
|
||||
artist.rootFolderPath = rootFolderPath;
|
||||
artist.artistType = artistType;
|
||||
artist.albumFolder = albumFolder;
|
||||
artist.tags = tags;
|
||||
|
||||
return artist;
|
||||
|
|
|
@ -46,11 +46,6 @@ namespace Lidarr.Api.V1.Artist
|
|||
artist.MetadataProfileId = resource.MetadataProfileId.Value;
|
||||
}
|
||||
|
||||
if (resource.AlbumFolder.HasValue)
|
||||
{
|
||||
artist.AlbumFolder = resource.AlbumFolder.Value;
|
||||
}
|
||||
|
||||
if (resource.RootFolderPath.IsNotNullOrWhiteSpace())
|
||||
{
|
||||
artist.RootFolderPath = resource.RootFolderPath;
|
||||
|
|
|
@ -45,7 +45,6 @@ namespace Lidarr.Api.V1.Artist
|
|||
public int MetadataProfileId { get; set; }
|
||||
|
||||
//Editing Only
|
||||
public bool AlbumFolder { get; set; }
|
||||
public bool Monitored { get; set; }
|
||||
|
||||
public string RootFolderPath { get; set; }
|
||||
|
@ -91,7 +90,6 @@ namespace Lidarr.Api.V1.Artist
|
|||
MetadataProfileId = model.MetadataProfileId,
|
||||
Links = model.Metadata.Value.Links,
|
||||
|
||||
AlbumFolder = model.AlbumFolder,
|
||||
Monitored = model.Monitored,
|
||||
|
||||
CleanName = model.CleanName,
|
||||
|
@ -139,7 +137,6 @@ namespace Lidarr.Api.V1.Artist
|
|||
QualityProfileId = resource.QualityProfileId,
|
||||
MetadataProfileId = resource.MetadataProfileId,
|
||||
|
||||
AlbumFolder = resource.AlbumFolder,
|
||||
Monitored = resource.Monitored,
|
||||
|
||||
CleanName = resource.CleanName,
|
||||
|
|
|
@ -35,7 +35,6 @@ namespace Lidarr.Api.V1.Config
|
|||
SharedValidator.RuleFor(c => c.StandardTrackFormat).ValidTrackFormat();
|
||||
SharedValidator.RuleFor(c => c.MultiDiscTrackFormat).ValidTrackFormat();
|
||||
SharedValidator.RuleFor(c => c.ArtistFolderFormat).ValidArtistFolderFormat();
|
||||
SharedValidator.RuleFor(c => c.AlbumFolderFormat).ValidAlbumFolderFormat();
|
||||
}
|
||||
|
||||
private void UpdateNamingConfig(NamingConfigResource resource)
|
||||
|
@ -96,10 +95,6 @@ namespace Lidarr.Api.V1.Config
|
|||
? null
|
||||
: _filenameSampleService.GetArtistFolderSample(nameSpec);
|
||||
|
||||
sampleResource.AlbumFolderExample = nameSpec.AlbumFolderFormat.IsNullOrWhiteSpace()
|
||||
? null
|
||||
: _filenameSampleService.GetAlbumFolderSample(nameSpec);
|
||||
|
||||
return sampleResource;
|
||||
}
|
||||
|
||||
|
|
|
@ -9,7 +9,6 @@ namespace Lidarr.Api.V1.Config
|
|||
public string StandardTrackFormat { get; set; }
|
||||
public string MultiDiscTrackFormat { get; set; }
|
||||
public string ArtistFolderFormat { get; set; }
|
||||
public string AlbumFolderFormat { get; set; }
|
||||
public bool IncludeArtistName { get; set; }
|
||||
public bool IncludeAlbumTitle { get; set; }
|
||||
public bool IncludeQuality { get; set; }
|
||||
|
|
|
@ -22,8 +22,7 @@ namespace Lidarr.Api.V1.Config
|
|||
ReplaceIllegalCharacters = model.ReplaceIllegalCharacters,
|
||||
StandardTrackFormat = model.StandardTrackFormat,
|
||||
MultiDiscTrackFormat = model.MultiDiscTrackFormat,
|
||||
ArtistFolderFormat = model.ArtistFolderFormat,
|
||||
AlbumFolderFormat = model.AlbumFolderFormat
|
||||
ArtistFolderFormat = model.ArtistFolderFormat
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -48,8 +47,7 @@ namespace Lidarr.Api.V1.Config
|
|||
StandardTrackFormat = resource.StandardTrackFormat,
|
||||
MultiDiscTrackFormat = resource.MultiDiscTrackFormat,
|
||||
|
||||
ArtistFolderFormat = resource.ArtistFolderFormat,
|
||||
AlbumFolderFormat = resource.AlbumFolderFormat
|
||||
ArtistFolderFormat = resource.ArtistFolderFormat
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
@ -44,16 +44,12 @@ namespace NzbDrone.Core.Test.MediaFiles.TrackFileMovingServiceTests
|
|||
|
||||
Mocker.GetMock<IBuildFileNames>()
|
||||
.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>()
|
||||
.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());
|
||||
|
||||
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();
|
||||
Mocker.GetMock<IDiskProvider>()
|
||||
.Setup(s => s.FolderExists(rootFolder))
|
||||
|
|
|
@ -24,24 +24,17 @@ namespace NzbDrone.Core.Test.OrganizerTests
|
|||
}
|
||||
|
||||
[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 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()
|
||||
.With(s => s.Name = "Fake: The Artist")
|
||||
.With(s => s.Path = @"C:\Test\Fake- The Artist".AsOsAgnostic())
|
||||
.With(s => s.AlbumFolder = true)
|
||||
.Build();
|
||||
|
||||
var fakeAlbum = Builder<Album>.CreateNew()
|
||||
.With(s => s.Title = "Fake: Album")
|
||||
.Build();
|
||||
|
||||
_namingConfig.AlbumFolderFormat = "{Artist Name} {Album Title}";
|
||||
|
||||
Subject.BuildTrackFilePath(fakeArtist, fakeAlbum, filename, ".flac").Should().Be(expectedPath.AsOsAgnostic());
|
||||
Subject.BuildTrackFilePath(fakeArtist, 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,
|
||||
MetadataProfileId = importList.MetadataProfileId,
|
||||
Tags = importList.Tags,
|
||||
AlbumFolder = true,
|
||||
AddOptions = new AddArtistOptions
|
||||
{
|
||||
SearchForMissingAlbums = monitored,
|
||||
|
@ -275,7 +274,6 @@ namespace NzbDrone.Core.ImportLists
|
|||
QualityProfileId = importList.ProfileId,
|
||||
MetadataProfileId = importList.MetadataProfileId,
|
||||
Tags = importList.Tags,
|
||||
AlbumFolder = true,
|
||||
AddOptions = new AddArtistOptions
|
||||
{
|
||||
SearchForMissingAlbums = monitored,
|
||||
|
|
|
@ -93,7 +93,7 @@ namespace NzbDrone.Core.MediaFiles
|
|||
var album = _albumService.GetAlbum(tracksInFile.First().AlbumId);
|
||||
|
||||
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))
|
||||
{
|
||||
|
|
|
@ -66,7 +66,7 @@ namespace NzbDrone.Core.MediaFiles
|
|||
var tracks = _trackService.GetTracksByFileId(trackFile.Id);
|
||||
var album = _albumService.GetAlbum(trackFile.AlbumId);
|
||||
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);
|
||||
|
||||
|
@ -78,7 +78,7 @@ namespace NzbDrone.Core.MediaFiles
|
|||
public TrackFile MoveTrackFile(TrackFile trackFile, LocalTrack localTrack)
|
||||
{
|
||||
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);
|
||||
|
||||
|
@ -90,7 +90,7 @@ namespace NzbDrone.Core.MediaFiles
|
|||
public TrackFile CopyTrackFile(TrackFile trackFile, LocalTrack localTrack)
|
||||
{
|
||||
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);
|
||||
|
||||
|
@ -132,13 +132,6 @@ namespace NzbDrone.Core.MediaFiles
|
|||
try
|
||||
{
|
||||
_mediaFileAttributeService.SetFolderLastWriteTime(artist.Path, trackFile.DateAdded);
|
||||
|
||||
if (artist.AlbumFolder)
|
||||
{
|
||||
var albumFolder = Path.GetDirectoryName(destinationFilePath);
|
||||
|
||||
_mediaFileAttributeService.SetFolderLastWriteTime(albumFolder, trackFile.DateAdded);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
@ -158,7 +151,6 @@ namespace NzbDrone.Core.MediaFiles
|
|||
private void EnsureTrackFolder(TrackFile trackFile, Artist artist, Album album, string filePath)
|
||||
{
|
||||
var trackFolder = Path.GetDirectoryName(filePath);
|
||||
var albumFolder = _buildFileNames.BuildAlbumPath(artist, album);
|
||||
var artistFolder = artist.Path;
|
||||
var rootFolder = new OsPath(artistFolder).Directory.FullPath;
|
||||
|
||||
|
@ -170,7 +162,7 @@ namespace NzbDrone.Core.MediaFiles
|
|||
var changed = false;
|
||||
var newEvent = new TrackFolderCreatedEvent(artist, trackFile);
|
||||
|
||||
_rootFolderWatchingService.ReportFileSystemChangeBeginning(artistFolder, albumFolder, trackFolder);
|
||||
_rootFolderWatchingService.ReportFileSystemChangeBeginning(artistFolder, trackFolder);
|
||||
|
||||
if (!_diskProvider.FolderExists(artistFolder))
|
||||
{
|
||||
|
@ -179,16 +171,10 @@ namespace NzbDrone.Core.MediaFiles
|
|||
changed = true;
|
||||
}
|
||||
|
||||
if (artistFolder != albumFolder && !_diskProvider.FolderExists(albumFolder))
|
||||
{
|
||||
CreateFolder(albumFolder);
|
||||
newEvent.AlbumFolder = albumFolder;
|
||||
changed = true;
|
||||
}
|
||||
|
||||
if (albumFolder != trackFolder && !_diskProvider.FolderExists(trackFolder))
|
||||
if (artistFolder != trackFolder && !_diskProvider.FolderExists(trackFolder))
|
||||
{
|
||||
CreateFolder(trackFolder);
|
||||
newEvent.AlbumFolder = trackFolder;
|
||||
newEvent.TrackFolder = trackFolder;
|
||||
changed = true;
|
||||
}
|
||||
|
|
|
@ -336,7 +336,6 @@ namespace NzbDrone.Core.MediaFiles.TrackImport
|
|||
artist.RootFolderPath = rootFolder.Path;
|
||||
artist.MetadataProfileId = rootFolder.DefaultMetadataProfileId;
|
||||
artist.QualityProfileId = rootFolder.DefaultQualityProfileId;
|
||||
artist.AlbumFolder = true;
|
||||
artist.Monitored = rootFolder.DefaultMonitorOption != MonitorTypes.None;
|
||||
artist.Tags = rootFolder.DefaultTags;
|
||||
artist.AddOptions = new AddArtistOptions
|
||||
|
|
|
@ -21,7 +21,6 @@ namespace NzbDrone.Core.Music
|
|||
public string CleanName { get; set; }
|
||||
public string SortName { get; set; }
|
||||
public bool Monitored { get; set; }
|
||||
public bool AlbumFolder { get; set; }
|
||||
public DateTime? LastInfoSync { get; set; }
|
||||
public string Path { get; set; }
|
||||
public string RootFolderPath { get; set; }
|
||||
|
@ -71,7 +70,6 @@ namespace NzbDrone.Core.Music
|
|||
Id = other.Id;
|
||||
ArtistMetadataId = other.ArtistMetadataId;
|
||||
Monitored = other.Monitored;
|
||||
AlbumFolder = other.AlbumFolder;
|
||||
LastInfoSync = other.LastInfoSync;
|
||||
Path = other.Path;
|
||||
RootFolderPath = other.RootFolderPath;
|
||||
|
@ -95,7 +93,6 @@ namespace NzbDrone.Core.Music
|
|||
AddOptions = other.AddOptions;
|
||||
RootFolderPath = other.RootFolderPath;
|
||||
Monitored = other.Monitored;
|
||||
AlbumFolder = other.AlbumFolder;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -125,7 +125,6 @@ namespace NzbDrone.Core.Music
|
|||
QualityProfileId = oldArtist.QualityProfileId,
|
||||
RootFolderPath = oldArtist.RootFolderPath,
|
||||
Monitored = oldArtist.Monitored,
|
||||
AlbumFolder = oldArtist.AlbumFolder,
|
||||
Tags = oldArtist.Tags
|
||||
};
|
||||
_logger.Debug($"Adding missing parent artist {addArtist}");
|
||||
|
|
|
@ -18,11 +18,9 @@ namespace NzbDrone.Core.Organizer
|
|||
public interface IBuildFileNames
|
||||
{
|
||||
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 BuildAlbumPath(Artist artist, Album album);
|
||||
string BuildTrackFilePath(Artist artist, string fileName, string extension);
|
||||
BasicNamingConfig GetBasicNamingConfig(NamingConfig nameSpec);
|
||||
string GetArtistFolder(Artist artist, NamingConfig namingConfig = null);
|
||||
string GetAlbumFolder(Artist artist, Album album, NamingConfig namingConfig = null);
|
||||
}
|
||||
|
||||
public class FileNameBuilder : IBuildFileNames
|
||||
|
@ -108,16 +106,10 @@ namespace NzbDrone.Core.Organizer
|
|||
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);
|
||||
|
||||
tracks = tracks.OrderBy(e => e.AlbumReleaseId).ThenBy(e => e.TrackNumber).ToList();
|
||||
|
||||
safePattern = FormatTrackNumberTokens(safePattern, "", tracks);
|
||||
safePattern = FormatMediumNumberTokens(safePattern, "", tracks);
|
||||
|
||||
AddArtistTokens(tokenHandlers, artist);
|
||||
AddAlbumTokens(tokenHandlers, album);
|
||||
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);
|
||||
AddPreferredWords(tokenHandlers, artist, trackFile, preferredWords);
|
||||
|
||||
var fileName = ReplaceTokens(safePattern, tokenHandlers, namingConfig).Trim();
|
||||
fileName = FileNameCleanupRegex.Replace(fileName, match => match.Captures[0].Value[0].ToString());
|
||||
fileName = TrimSeparatorsRegex.Replace(fileName, string.Empty);
|
||||
var splitPatterns = pattern.Split(new char[] { '\\', '/' }, StringSplitOptions.RemoveEmptyEntries);
|
||||
var components = new List<string>();
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
return Path.Combine(components.ToArray());
|
||||
}
|
||||
|
||||
public string BuildTrackFilePath(Artist artist, Album album, string fileName, string extension)
|
||||
public string BuildTrackFilePath(Artist artist, string fileName, string extension)
|
||||
{
|
||||
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;
|
||||
|
||||
if (artist.AlbumFolder)
|
||||
{
|
||||
var albumFolder = GetAlbumFolder(artist, album);
|
||||
|
||||
albumFolder = CleanFileName(albumFolder);
|
||||
|
||||
path = Path.Combine(path, albumFolder);
|
||||
}
|
||||
|
||||
return path;
|
||||
return Path.Combine(path, fileName + extension);
|
||||
}
|
||||
|
||||
public BasicNamingConfig GetBasicNamingConfig(NamingConfig nameSpec)
|
||||
|
@ -211,26 +204,28 @@ namespace NzbDrone.Core.Organizer
|
|||
namingConfig = _namingConfigService.GetConfig();
|
||||
}
|
||||
|
||||
var pattern = namingConfig.ArtistFolderFormat;
|
||||
var tokenHandlers = new Dictionary<string, Func<TokenMatch, string>>(FileNameBuilderTokenEqualityComparer.Instance);
|
||||
|
||||
AddArtistTokens(tokenHandlers, artist);
|
||||
|
||||
return CleanFolderName(ReplaceTokens(namingConfig.ArtistFolderFormat, tokenHandlers, namingConfig));
|
||||
}
|
||||
var splitPatterns = pattern.Split(new char[] { '\\', '/' }, StringSplitOptions.RemoveEmptyEntries);
|
||||
var components = new List<string>();
|
||||
|
||||
public string GetAlbumFolder(Artist artist, Album album, NamingConfig namingConfig = null)
|
||||
{
|
||||
if (namingConfig == null)
|
||||
foreach (var s in splitPatterns)
|
||||
{
|
||||
namingConfig = _namingConfigService.GetConfig();
|
||||
var splitPattern = s;
|
||||
|
||||
var component = ReplaceTokens(splitPattern, tokenHandlers, namingConfig);
|
||||
component = CleanFolderName(component);
|
||||
|
||||
if (component.IsNotNullOrWhiteSpace())
|
||||
{
|
||||
components.Add(component);
|
||||
}
|
||||
}
|
||||
|
||||
var tokenHandlers = new Dictionary<string, Func<TokenMatch, string>>(FileNameBuilderTokenEqualityComparer.Instance);
|
||||
|
||||
AddAlbumTokens(tokenHandlers, album);
|
||||
AddArtistTokens(tokenHandlers, artist);
|
||||
|
||||
return CleanFolderName(ReplaceTokens(namingConfig.AlbumFolderFormat, tokenHandlers, namingConfig));
|
||||
return Path.Combine(components.ToArray());
|
||||
}
|
||||
|
||||
public static string CleanTitle(string title)
|
||||
|
|
|
@ -11,7 +11,6 @@ namespace NzbDrone.Core.Organizer
|
|||
SampleResult GetStandardTrackSample(NamingConfig nameSpec);
|
||||
SampleResult GetMultiDiscTrackSample(NamingConfig nameSpec);
|
||||
string GetArtistFolderSample(NamingConfig nameSpec);
|
||||
string GetAlbumFolderSample(NamingConfig nameSpec);
|
||||
}
|
||||
|
||||
public class FileNameSampleService : IFilenameSampleService
|
||||
|
@ -156,11 +155,6 @@ namespace NzbDrone.Core.Organizer
|
|||
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)
|
||||
{
|
||||
try
|
||||
|
|
|
@ -8,10 +8,9 @@ namespace NzbDrone.Core.Organizer
|
|||
{
|
||||
RenameTracks = false,
|
||||
ReplaceIllegalCharacters = true,
|
||||
StandardTrackFormat = "{Artist Name} - {Album Title} - {track:00} - {Track Title}",
|
||||
MultiDiscTrackFormat = "{Medium Format} {medium:00}/{Artist Name} - {Album Title} - {track:00} - {Track Title}",
|
||||
StandardTrackFormat = "{Album Title} ({Release Year})/{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}",
|
||||
AlbumFolderFormat = "{Album Title} ({Release Year})"
|
||||
};
|
||||
|
||||
public bool RenameTracks { get; set; }
|
||||
|
@ -19,6 +18,5 @@ namespace NzbDrone.Core.Organizer
|
|||
public string StandardTrackFormat { get; set; }
|
||||
public string MultiDiscTrackFormat { get; set; }
|
||||
public string ArtistFolderFormat { get; set; }
|
||||
public string AlbumFolderFormat { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue