From 774a3597deb6a41c64d41afda82760dfd3fd4cf4 Mon Sep 17 00:00:00 2001 From: Qstick Date: Wed, 6 Jan 2021 21:36:28 +0000 Subject: [PATCH] New: Remove AlbumFolder, Support Nested Track Format --- .../src/Artist/Edit/EditArtistModalContent.js | 13 -- .../Edit/EditArtistModalContentConnector.js | 1 - .../src/Artist/Editor/ArtistEditorFooter.js | 34 ----- .../src/Artist/Editor/ArtistEditorRow.css | 5 - frontend/src/Artist/Editor/ArtistEditorRow.js | 29 ---- .../Album/AddNewAlbumModalContentConnector.js | 3 - .../AddNewArtistModalContentConnector.js | 3 - .../src/Search/Common/AddArtistOptionsForm.js | 13 -- .../Settings/MediaManagement/Naming/Naming.js | 33 ----- frontend/src/Store/Actions/searchActions.js | 1 - .../createImportArtistItemSelector.js | 1 - frontend/src/Utilities/Artist/getNewArtist.js | 2 - .../Artist/ArtistEditorModule.cs | 5 - src/Lidarr.Api.V1/Artist/ArtistResource.cs | 3 - .../Config/NamingConfigModule.cs | 5 - .../Config/NamingConfigResource.cs | 1 - .../Config/NamingExampleResource.cs | 6 +- .../MoveTrackFileFixture.cs | 8 +- .../OrganizerTests/BuildFilePathFixture.cs | 13 +- .../NestedFileNameBuilderFixture.cs | 134 ++++++++++++++++++ .../OrganizerTests/GetAlbumFolderFixture.cs | 35 ----- .../Migration/042_remove_album_folders.cs | 19 +++ .../ImportLists/ImportListSyncService.cs | 2 - .../MediaFiles/RenameTrackFileService.cs | 2 +- .../MediaFiles/TrackFileMovingService.cs | 26 +--- .../TrackImport/ImportApprovedTracks.cs | 1 - src/NzbDrone.Core/Music/Model/Artist.cs | 3 - .../Music/Services/RefreshAlbumService.cs | 1 - .../Organizer/FileNameBuilder.cs | 81 +++++------ .../Organizer/FileNameSampleService.cs | 6 - src/NzbDrone.Core/Organizer/NamingConfig.cs | 6 +- 31 files changed, 207 insertions(+), 288 deletions(-) delete mode 100644 frontend/src/Artist/Editor/ArtistEditorRow.css create mode 100644 src/NzbDrone.Core.Test/OrganizerTests/FileNameBuilderTests/NestedFileNameBuilderFixture.cs delete mode 100644 src/NzbDrone.Core.Test/OrganizerTests/GetAlbumFolderFixture.cs create mode 100644 src/NzbDrone.Core/Datastore/Migration/042_remove_album_folders.cs diff --git a/frontend/src/Artist/Edit/EditArtistModalContent.js b/frontend/src/Artist/Edit/EditArtistModalContent.js index 797edd3f9..41a8f3222 100644 --- a/frontend/src/Artist/Edit/EditArtistModalContent.js +++ b/frontend/src/Artist/Edit/EditArtistModalContent.js @@ -72,7 +72,6 @@ class EditArtistModalContent extends Component { const { monitored, - albumFolder, qualityProfileId, metadataProfileId, path, @@ -99,18 +98,6 @@ class EditArtistModalContent extends Component { /> - - Use Album Folder - - - - Quality Profile diff --git a/frontend/src/Artist/Edit/EditArtistModalContentConnector.js b/frontend/src/Artist/Edit/EditArtistModalContentConnector.js index 18762bcee..b0efeb63f 100644 --- a/frontend/src/Artist/Edit/EditArtistModalContentConnector.js +++ b/frontend/src/Artist/Edit/EditArtistModalContentConnector.js @@ -39,7 +39,6 @@ function createMapStateToProps() { const artistSettings = _.pick(artist, [ 'monitored', - 'albumFolder', 'qualityProfileId', 'metadataProfileId', 'path', diff --git a/frontend/src/Artist/Editor/ArtistEditorFooter.js b/frontend/src/Artist/Editor/ArtistEditorFooter.js index b7ee90170..1cfed4345 100644 --- a/frontend/src/Artist/Editor/ArtistEditorFooter.js +++ b/frontend/src/Artist/Editor/ArtistEditorFooter.js @@ -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 (
@@ -245,28 +233,6 @@ class ArtistEditorFooter extends Component { ); } - if (name === 'albumFolder') { - return ( -
- - - -
- ); - } - if (name === 'path') { return (
{ - // 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 ( - - - ); - } - if (name === 'path') { return ( @@ -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, diff --git a/frontend/src/Search/Album/AddNewAlbumModalContentConnector.js b/frontend/src/Search/Album/AddNewAlbumModalContentConnector.js index dde5cfd25..699a47244 100644 --- a/frontend/src/Search/Album/AddNewAlbumModalContentConnector.js +++ b/frontend/src/Search/Album/AddNewAlbumModalContentConnector.js @@ -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, diff --git a/frontend/src/Search/Artist/AddNewArtistModalContentConnector.js b/frontend/src/Search/Artist/AddNewArtistModalContentConnector.js index 7b6083bcb..bd188f8e9 100644 --- a/frontend/src/Search/Artist/AddNewArtistModalContentConnector.js +++ b/frontend/src/Search/Artist/AddNewArtistModalContentConnector.js @@ -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, diff --git a/frontend/src/Search/Common/AddArtistOptionsForm.js b/frontend/src/Search/Common/AddArtistOptionsForm.js index 82bcdce84..304e35fca 100644 --- a/frontend/src/Search/Common/AddArtistOptionsForm.js +++ b/frontend/src/Search/Common/AddArtistOptionsForm.js @@ -35,7 +35,6 @@ class AddArtistOptionsForm extends Component { metadataProfileId, includeNoneMetadataProfile, showMetadataProfile, - albumFolder, tags, onInputChange, ...otherProps @@ -119,17 +118,6 @@ class AddArtistOptionsForm extends Component { /> - - Album Folder - - - - Tags @@ -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 }; diff --git a/frontend/src/Settings/MediaManagement/Naming/Naming.js b/frontend/src/Settings/MediaManagement/Naming/Naming.js index 71850aa2d..b66451e7d 100644 --- a/frontend/src/Settings/MediaManagement/Naming/Naming.js +++ b/frontend/src/Settings/MediaManagement/Naming/Naming.js @@ -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 { /> - - Album Folder Format - - ?} - onChange={onInputChange} - {...settings.albumFolderFormat} - helpTexts={albumFolderFormatHelpTexts} - errors={[...albumFolderFormatErrors, ...settings.albumFolderFormat.errors]} - /> - - { namingModalOptions && 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; } diff --git a/src/Lidarr.Api.V1/Config/NamingConfigResource.cs b/src/Lidarr.Api.V1/Config/NamingConfigResource.cs index ee39b9040..ed00d6b78 100644 --- a/src/Lidarr.Api.V1/Config/NamingConfigResource.cs +++ b/src/Lidarr.Api.V1/Config/NamingConfigResource.cs @@ -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; } diff --git a/src/Lidarr.Api.V1/Config/NamingExampleResource.cs b/src/Lidarr.Api.V1/Config/NamingExampleResource.cs index 7a12db6ea..ad587b431 100644 --- a/src/Lidarr.Api.V1/Config/NamingExampleResource.cs +++ b/src/Lidarr.Api.V1/Config/NamingExampleResource.cs @@ -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 }; } } diff --git a/src/NzbDrone.Core.Test/MediaFiles/TrackFileMovingServiceTests/MoveTrackFileFixture.cs b/src/NzbDrone.Core.Test/MediaFiles/TrackFileMovingServiceTests/MoveTrackFileFixture.cs index 4136c498f..e2b18e5cc 100644 --- a/src/NzbDrone.Core.Test/MediaFiles/TrackFileMovingServiceTests/MoveTrackFileFixture.cs +++ b/src/NzbDrone.Core.Test/MediaFiles/TrackFileMovingServiceTests/MoveTrackFileFixture.cs @@ -44,16 +44,12 @@ namespace NzbDrone.Core.Test.MediaFiles.TrackFileMovingServiceTests Mocker.GetMock() .Setup(s => s.BuildTrackFileName(It.IsAny>(), It.IsAny(), It.IsAny(), It.IsAny(), null, null)) - .Returns("File Name"); + .Returns("Album\\File Name"); Mocker.GetMock() - .Setup(s => s.BuildTrackFilePath(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) + .Setup(s => s.BuildTrackFilePath(It.IsAny(), It.IsAny(), It.IsAny())) .Returns(@"C:\Test\Music\Artist\Album\File Name.mp3".AsOsAgnostic()); - Mocker.GetMock() - .Setup(s => s.BuildAlbumPath(It.IsAny(), It.IsAny())) - .Returns(@"C:\Test\Music\Artist\Album".AsOsAgnostic()); - var rootFolder = @"C:\Test\Music\".AsOsAgnostic(); Mocker.GetMock() .Setup(s => s.FolderExists(rootFolder)) diff --git a/src/NzbDrone.Core.Test/OrganizerTests/BuildFilePathFixture.cs b/src/NzbDrone.Core.Test/OrganizerTests/BuildFilePathFixture.cs index f76c1c262..2d3abed95 100644 --- a/src/NzbDrone.Core.Test/OrganizerTests/BuildFilePathFixture.cs +++ b/src/NzbDrone.Core.Test/OrganizerTests/BuildFilePathFixture.cs @@ -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.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.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()); } } } diff --git a/src/NzbDrone.Core.Test/OrganizerTests/FileNameBuilderTests/NestedFileNameBuilderFixture.cs b/src/NzbDrone.Core.Test/OrganizerTests/FileNameBuilderTests/NestedFileNameBuilderFixture.cs new file mode 100644 index 000000000..da077e41f --- /dev/null +++ b/src/NzbDrone.Core.Test/OrganizerTests/FileNameBuilderTests/NestedFileNameBuilderFixture.cs @@ -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 + { + 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 + .CreateNew() + .With(s => s.Name = "Linkin Park") + .With(s => s.Metadata = new ArtistMetadata + { + Disambiguation = "US Rock Band", + Name = "Linkin Park" + }) + .Build(); + + _medium = Builder + .CreateNew() + .With(m => m.Number = 3) + .Build(); + + _medium2 = Builder + .CreateNew() + .With(m => m.Number = 4) + .Build(); + + _release = Builder + .CreateNew() + .With(s => s.Media = new List { _medium }) + .With(s => s.Monitored = true) + .Build(); + + _album = Builder + .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() + .Setup(c => c.GetConfig()).Returns(_namingConfig); + + _track1 = Builder.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.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() + .Setup(v => v.Get(Moq.It.IsAny())) + .Returns(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 { _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 { _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 { _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 { _track1 }, _artist, _album, _trackFile) + .Should().Be("Hybrid Theory (2020)\\CD 03\\Linkin Park - 06 [MP3-256]".AsOsAgnostic()); + } + } +} diff --git a/src/NzbDrone.Core.Test/OrganizerTests/GetAlbumFolderFixture.cs b/src/NzbDrone.Core.Test/OrganizerTests/GetAlbumFolderFixture.cs deleted file mode 100644 index 359b016b5..000000000 --- a/src/NzbDrone.Core.Test/OrganizerTests/GetAlbumFolderFixture.cs +++ /dev/null @@ -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 - { - private NamingConfig _namingConfig; - - [SetUp] - public void Setup() - { - _namingConfig = NamingConfig.Default; - - Mocker.GetMock() - .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); - } - } -} diff --git a/src/NzbDrone.Core/Datastore/Migration/042_remove_album_folders.cs b/src/NzbDrone.Core/Datastore/Migration/042_remove_album_folders.cs new file mode 100644 index 000000000..a0a54f90a --- /dev/null +++ b/src/NzbDrone.Core/Datastore/Migration/042_remove_album_folders.cs @@ -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"); + } + } +} diff --git a/src/NzbDrone.Core/ImportLists/ImportListSyncService.cs b/src/NzbDrone.Core/ImportLists/ImportListSyncService.cs index 87e9cc020..f115e74af 100644 --- a/src/NzbDrone.Core/ImportLists/ImportListSyncService.cs +++ b/src/NzbDrone.Core/ImportLists/ImportListSyncService.cs @@ -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, diff --git a/src/NzbDrone.Core/MediaFiles/RenameTrackFileService.cs b/src/NzbDrone.Core/MediaFiles/RenameTrackFileService.cs index 6df288c6d..2e0d0d623 100644 --- a/src/NzbDrone.Core/MediaFiles/RenameTrackFileService.cs +++ b/src/NzbDrone.Core/MediaFiles/RenameTrackFileService.cs @@ -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)) { diff --git a/src/NzbDrone.Core/MediaFiles/TrackFileMovingService.cs b/src/NzbDrone.Core/MediaFiles/TrackFileMovingService.cs index 12947516f..9892fbec4 100644 --- a/src/NzbDrone.Core/MediaFiles/TrackFileMovingService.cs +++ b/src/NzbDrone.Core/MediaFiles/TrackFileMovingService.cs @@ -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; } diff --git a/src/NzbDrone.Core/MediaFiles/TrackImport/ImportApprovedTracks.cs b/src/NzbDrone.Core/MediaFiles/TrackImport/ImportApprovedTracks.cs index 781f40a89..b3fac6534 100644 --- a/src/NzbDrone.Core/MediaFiles/TrackImport/ImportApprovedTracks.cs +++ b/src/NzbDrone.Core/MediaFiles/TrackImport/ImportApprovedTracks.cs @@ -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 diff --git a/src/NzbDrone.Core/Music/Model/Artist.cs b/src/NzbDrone.Core/Music/Model/Artist.cs index 8689d9476..cd9d5cbf8 100644 --- a/src/NzbDrone.Core/Music/Model/Artist.cs +++ b/src/NzbDrone.Core/Music/Model/Artist.cs @@ -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; } } } diff --git a/src/NzbDrone.Core/Music/Services/RefreshAlbumService.cs b/src/NzbDrone.Core/Music/Services/RefreshAlbumService.cs index 542d28559..7bcb3857f 100644 --- a/src/NzbDrone.Core/Music/Services/RefreshAlbumService.cs +++ b/src/NzbDrone.Core/Music/Services/RefreshAlbumService.cs @@ -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}"); diff --git a/src/NzbDrone.Core/Organizer/FileNameBuilder.cs b/src/NzbDrone.Core/Organizer/FileNameBuilder.cs index 38aa8a2e9..3737eae61 100644 --- a/src/NzbDrone.Core/Organizer/FileNameBuilder.cs +++ b/src/NzbDrone.Core/Organizer/FileNameBuilder.cs @@ -18,11 +18,9 @@ namespace NzbDrone.Core.Organizer public interface IBuildFileNames { string BuildTrackFileName(List tracks, Artist artist, Album album, TrackFile trackFile, NamingConfig namingConfig = null, List 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>(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(); - 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>(FileNameBuilderTokenEqualityComparer.Instance); AddArtistTokens(tokenHandlers, artist); - return CleanFolderName(ReplaceTokens(namingConfig.ArtistFolderFormat, tokenHandlers, namingConfig)); - } + var splitPatterns = pattern.Split(new char[] { '\\', '/' }, StringSplitOptions.RemoveEmptyEntries); + var components = new List(); - 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>(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) diff --git a/src/NzbDrone.Core/Organizer/FileNameSampleService.cs b/src/NzbDrone.Core/Organizer/FileNameSampleService.cs index d2a1b8ce0..23b902e80 100644 --- a/src/NzbDrone.Core/Organizer/FileNameSampleService.cs +++ b/src/NzbDrone.Core/Organizer/FileNameSampleService.cs @@ -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 tracks, Artist artist, Album album, TrackFile trackFile, NamingConfig nameSpec) { try diff --git a/src/NzbDrone.Core/Organizer/NamingConfig.cs b/src/NzbDrone.Core/Organizer/NamingConfig.cs index 4226200d5..02760ad37 100644 --- a/src/NzbDrone.Core/Organizer/NamingConfig.cs +++ b/src/NzbDrone.Core/Organizer/NamingConfig.cs @@ -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; } } }