New: Remove AlbumFolder, Support Nested Track Format

pull/1918/head
Qstick 4 years ago
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;
public string BuildTrackFilePath(Artist artist, Album album, string fileName, string extension)
{
Ensure.That(extension, () => extension).IsNotNullOrWhiteSpace();
splitPattern = FormatTrackNumberTokens(splitPattern, "", tracks);
splitPattern = FormatMediumNumberTokens(splitPattern, "", tracks);
var path = BuildAlbumPath(artist, album);
var component = ReplaceTokens(splitPattern, tokenHandlers, namingConfig).Trim();
return Path.Combine(path, fileName + extension);
}
component = FileNameCleanupRegex.Replace(component, match => match.Captures[0].Value[0].ToString());
component = TrimSeparatorsRegex.Replace(component, string.Empty);
public string BuildAlbumPath(Artist artist, Album album)
{
var path = artist.Path;
if (component.IsNotNullOrWhiteSpace())
{
components.Add(component);
}
}
if (artist.AlbumFolder)
{
var albumFolder = GetAlbumFolder(artist, album);
return Path.Combine(components.ToArray());
}
albumFolder = CleanFileName(albumFolder);
public string BuildTrackFilePath(Artist artist, string fileName, string extension)
{
Ensure.That(extension, () => extension).IsNotNullOrWhiteSpace();
path = Path.Combine(path, albumFolder);
}
var path = artist.Path;
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 tokenHandlers = new Dictionary<string, Func<TokenMatch, string>>(FileNameBuilderTokenEqualityComparer.Instance);
var component = ReplaceTokens(splitPattern, tokenHandlers, namingConfig);
component = CleanFolderName(component);
AddAlbumTokens(tokenHandlers, album);
AddArtistTokens(tokenHandlers, artist);
if (component.IsNotNullOrWhiteSpace())
{
components.Add(component);
}
}
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…
Cancel
Save