New: Remove AlbumFolder, Support Nested Track Format

This commit is contained in:
Qstick 2021-01-06 21:36:28 +00:00
parent 8063a32acd
commit 774a3597de
31 changed files with 207 additions and 288 deletions

View file

@ -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>

View file

@ -39,7 +39,6 @@ function createMapStateToProps() {
const artistSettings = _.pick(artist, [ const artistSettings = _.pick(artist, [
'monitored', 'monitored',
'albumFolder',
'qualityProfileId', 'qualityProfileId',
'metadataProfileId', 'metadataProfileId',
'path', 'path',

View file

@ -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

View file

@ -1,5 +0,0 @@
.albumFolder {
composes: cell from '~Components/Table/Cells/TableRowCell.css';
width: 150px;
}

View file

@ -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,

View file

@ -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,

View file

@ -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,

View file

@ -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
}; };

View file

@ -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

View file

@ -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: []
} }
}; };

View file

@ -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
}; };

View file

@ -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;

View file

@ -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;

View file

@ -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,

View file

@ -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;
} }

View file

@ -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; }

View file

@ -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
}; };
} }
} }

View file

@ -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))

View file

@ -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());
} }
} }
} }

View file

@ -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());
}
}
}

View file

@ -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);
}
}
}

View file

@ -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");
}
}
}

View file

@ -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,

View file

@ -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))
{ {

View file

@ -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;
} }

View file

@ -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

View file

@ -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;
} }
} }
} }

View file

@ -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}");

View file

@ -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);
}
}
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(); 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>();
public string GetAlbumFolder(Artist artist, Album album, NamingConfig namingConfig = null) foreach (var s in splitPatterns)
{
if (namingConfig == null)
{ {
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); return Path.Combine(components.ToArray());
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)

View file

@ -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

View file

@ -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; }
} }
} }