New: Seperate Naming for Multi-Disc Albums

pull/954/head
Qstick 6 years ago
parent 4edad5f563
commit 1425bc8bd9

@ -40,6 +40,18 @@ class Naming extends Component {
}); });
} }
onMultiDiscNamingModalOpenClick = () => {
this.setState({
isNamingModalOpen: true,
namingModalOptions: {
name: 'multiDiscTrackFormat',
album: true,
track: true,
additional: true
}
});
}
onArtistFolderNamingModalOpenClick = () => { onArtistFolderNamingModalOpenClick = () => {
this.setState({ this.setState({
isNamingModalOpen: true, isNamingModalOpen: true,
@ -87,6 +99,8 @@ class Naming extends Component {
const standardTrackFormatHelpTexts = []; const standardTrackFormatHelpTexts = [];
const standardTrackFormatErrors = []; const standardTrackFormatErrors = [];
const multiDiscTrackFormatHelpTexts = [];
const multiDiscTrackFormatErrors = [];
const artistFolderFormatHelpTexts = []; const artistFolderFormatHelpTexts = [];
const artistFolderFormatErrors = []; const artistFolderFormatErrors = [];
const albumFolderFormatHelpTexts = []; const albumFolderFormatHelpTexts = [];
@ -99,6 +113,12 @@ class Naming extends Component {
standardTrackFormatErrors.push({ message: 'Single Track: Invalid Format' }); standardTrackFormatErrors.push({ message: 'Single Track: Invalid Format' });
} }
if (examples.multiDiscTrackExample) {
multiDiscTrackFormatHelpTexts.push(`Multi Disc Track: ${examples.multiDiscTrackExample}`);
} else {
multiDiscTrackFormatErrors.push({ message: 'Single Track: Invalid Format' });
}
if (examples.artistFolderExample) { if (examples.artistFolderExample) {
artistFolderFormatHelpTexts.push(`Example: ${examples.artistFolderExample}`); artistFolderFormatHelpTexts.push(`Example: ${examples.artistFolderExample}`);
} else { } else {
@ -169,6 +189,21 @@ class Naming extends Component {
/> />
</FormGroup> </FormGroup>
<FormGroup size={sizes.LARGE}>
<FormLabel>Multi Disc Track Format</FormLabel>
<FormInputGroup
inputClassName={styles.namingInput}
type={inputTypes.TEXT}
name="multiDiscTrackFormat"
buttons={<FormInputButton onPress={this.onMultiDiscNamingModalOpenClick}>?</FormInputButton>}
onChange={onInputChange}
{...settings.multiDiscTrackFormat}
helpTexts={multiDiscTrackFormatHelpTexts}
errors={[...multiDiscTrackFormatErrors, ...settings.multiDiscTrackFormat.errors]}
/>
</FormGroup>
</div> </div>
} }

@ -37,6 +37,7 @@ 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.ArtistFolderFormat).ValidArtistFolderFormat(); SharedValidator.RuleFor(c => c.ArtistFolderFormat).ValidArtistFolderFormat();
SharedValidator.RuleFor(c => c.AlbumFolderFormat).ValidAlbumFolderFormat(); SharedValidator.RuleFor(c => c.AlbumFolderFormat).ValidAlbumFolderFormat();
} }
@ -60,6 +61,12 @@ namespace Lidarr.Api.V1.Config
basicConfig.AddToResource(resource); basicConfig.AddToResource(resource);
} }
if (resource.MultiDiscTrackFormat.IsNotNullOrWhiteSpace())
{
var basicConfig = _filenameBuilder.GetBasicNamingConfig(nameSpec);
basicConfig.AddToResource(resource);
}
return resource; return resource;
} }
@ -79,11 +86,16 @@ namespace Lidarr.Api.V1.Config
var sampleResource = new NamingExampleResource(); var sampleResource = new NamingExampleResource();
var singleTrackSampleResult = _filenameSampleService.GetStandardTrackSample(nameSpec); var singleTrackSampleResult = _filenameSampleService.GetStandardTrackSample(nameSpec);
var multiDiscTrackSampleResult = _filenameSampleService.GetMultiDiscTrackSample(nameSpec);
sampleResource.SingleTrackExample = _filenameValidationService.ValidateTrackFilename(singleTrackSampleResult) != null sampleResource.SingleTrackExample = _filenameValidationService.ValidateTrackFilename(singleTrackSampleResult) != null
? null ? null
: singleTrackSampleResult.FileName; : singleTrackSampleResult.FileName;
sampleResource.MultiDiscTrackExample = _filenameValidationService.ValidateTrackFilename(multiDiscTrackSampleResult) != null
? null
: multiDiscTrackSampleResult.FileName;
sampleResource.ArtistFolderExample = nameSpec.ArtistFolderFormat.IsNullOrWhiteSpace() sampleResource.ArtistFolderExample = nameSpec.ArtistFolderFormat.IsNullOrWhiteSpace()
? null ? null
: _filenameSampleService.GetArtistFolderSample(nameSpec); : _filenameSampleService.GetArtistFolderSample(nameSpec);

@ -7,6 +7,7 @@ namespace Lidarr.Api.V1.Config
public bool RenameTracks { get; set; } public bool RenameTracks { get; set; }
public bool ReplaceIllegalCharacters { get; set; } public bool ReplaceIllegalCharacters { get; set; }
public string StandardTrackFormat { get; set; } public string StandardTrackFormat { get; set; }
public string MultiDiscTrackFormat { get; set; }
public string ArtistFolderFormat { get; set; } public string ArtistFolderFormat { get; set; }
public string AlbumFolderFormat { get; set; } public string AlbumFolderFormat { get; set; }
public bool IncludeArtistName { get; set; } public bool IncludeArtistName { get; set; }

@ -5,6 +5,7 @@ namespace Lidarr.Api.V1.Config
public class NamingExampleResource public class NamingExampleResource
{ {
public string SingleTrackExample { get; set; } public string SingleTrackExample { get; set; }
public string MultiDiscTrackExample { get; set; }
public string ArtistFolderExample { get; set; } public string ArtistFolderExample { get; set; }
public string AlbumFolderExample { get; set; } public string AlbumFolderExample { get; set; }
} }
@ -20,6 +21,7 @@ namespace Lidarr.Api.V1.Config
RenameTracks = model.RenameTracks, RenameTracks = model.RenameTracks,
ReplaceIllegalCharacters = model.ReplaceIllegalCharacters, ReplaceIllegalCharacters = model.ReplaceIllegalCharacters,
StandardTrackFormat = model.StandardTrackFormat, StandardTrackFormat = model.StandardTrackFormat,
MultiDiscTrackFormat = model.MultiDiscTrackFormat,
ArtistFolderFormat = model.ArtistFolderFormat, ArtistFolderFormat = model.ArtistFolderFormat,
AlbumFolderFormat = model.AlbumFolderFormat AlbumFolderFormat = model.AlbumFolderFormat
}; };
@ -44,6 +46,7 @@ namespace Lidarr.Api.V1.Config
RenameTracks = resource.RenameTracks, RenameTracks = resource.RenameTracks,
ReplaceIllegalCharacters = resource.ReplaceIllegalCharacters, ReplaceIllegalCharacters = resource.ReplaceIllegalCharacters,
StandardTrackFormat = resource.StandardTrackFormat, StandardTrackFormat = resource.StandardTrackFormat,
MultiDiscTrackFormat = resource.MultiDiscTrackFormat,
ArtistFolderFormat = resource.ArtistFolderFormat, ArtistFolderFormat = resource.ArtistFolderFormat,
AlbumFolderFormat = resource.AlbumFolderFormat AlbumFolderFormat = resource.AlbumFolderFormat

@ -0,0 +1,17 @@
using FluentMigrator;
using NzbDrone.Core.Datastore.Migration.Framework;
using System.Data;
using System.IO;
namespace NzbDrone.Core.Datastore.Migration
{
[Migration(35)]
public class multi_disc_naming_format : NzbDroneMigrationBase
{
protected override void MainDbUpgrade()
{
Alter.Table("NamingConfig").AddColumn("MultiDiscTrackFormat").AsString().Nullable();
Execute.Sql("UPDATE NamingConfig SET MultiDiscTrackFormat = '{Medium Format} {medium:00}/{Artist Name} - {Album Title} - {track:00} - {Track Title}'");
}
}
}

@ -1,10 +0,0 @@
namespace NzbDrone.Core.Organizer
{
public class EpisodeSortingType
{
public int Id { get; set; }
public string Name { get; set; }
public string Pattern { get; set; }
public string EpisodeSeparator { get; set; }
}
}

@ -66,7 +66,7 @@ namespace NzbDrone.Core.Organizer
//TODO: Support Written numbers (One, Two, etc) and Roman Numerals (I, II, III etc) //TODO: Support Written numbers (One, Two, etc) and Roman Numerals (I, II, III etc)
private static readonly Regex MultiPartCleanupRegex = new Regex(@"(?:\(\d+\)|(Part|Pt\.?)\s?\d+)$", RegexOptions.Compiled | RegexOptions.IgnoreCase); private static readonly Regex MultiPartCleanupRegex = new Regex(@"(?:\(\d+\)|(Part|Pt\.?)\s?\d+)$", RegexOptions.Compiled | RegexOptions.IgnoreCase);
private static readonly char[] EpisodeTitleTrimCharacters = new[] { ' ', '.', '?' }; private static readonly char[] TrackTitleTrimCharacters = new[] { ' ', '.', '?' };
private static readonly Regex TitlePrefixRegex = new Regex(@"^(The|An|A) (.*?)((?: *\([^)]+\))*)$", RegexOptions.Compiled | RegexOptions.IgnoreCase); private static readonly Regex TitlePrefixRegex = new Regex(@"^(The|An|A) (.*?)((?: *\([^)]+\))*)$", RegexOptions.Compiled | RegexOptions.IgnoreCase);
@ -96,18 +96,27 @@ namespace NzbDrone.Core.Organizer
return GetOriginalFileName(trackFile); return GetOriginalFileName(trackFile);
} }
if (namingConfig.StandardTrackFormat.IsNullOrWhiteSpace()) if (namingConfig.StandardTrackFormat.IsNullOrWhiteSpace() || namingConfig.MultiDiscTrackFormat.IsNullOrWhiteSpace())
{ {
throw new NamingFormatException("Standard track format cannot be empty"); throw new NamingFormatException("Standard and Multi track formats cannot be empty");
} }
var pattern = namingConfig.StandardTrackFormat; var pattern = namingConfig.StandardTrackFormat;
if (tracks.First().AlbumRelease.Value.Media.Count() > 1)
{
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();
pattern = FormatTrackNumberTokens(pattern, "", tracks); safePattern = FormatTrackNumberTokens(safePattern, "", tracks);
pattern = FormatMediumNumberTokens(pattern, "", tracks); safePattern = FormatMediumNumberTokens(safePattern, "", tracks);
AddArtistTokens(tokenHandlers, artist); AddArtistTokens(tokenHandlers, artist);
AddAlbumTokens(tokenHandlers, album); AddAlbumTokens(tokenHandlers, album);
@ -118,7 +127,7 @@ namespace NzbDrone.Core.Organizer
AddMediaInfoTokens(tokenHandlers, trackFile); AddMediaInfoTokens(tokenHandlers, trackFile);
AddPreferredWords(tokenHandlers, artist, trackFile, preferredWords); AddPreferredWords(tokenHandlers, artist, trackFile, preferredWords);
var fileName = ReplaceTokens(pattern, tokenHandlers, namingConfig).Trim(); var fileName = ReplaceTokens(safePattern, tokenHandlers, namingConfig).Trim();
fileName = FileNameCleanupRegex.Replace(fileName, match => match.Captures[0].Value[0].ToString()); fileName = FileNameCleanupRegex.Replace(fileName, match => match.Captures[0].Value[0].ToString());
fileName = TrimSeparatorsRegex.Replace(fileName, string.Empty); fileName = TrimSeparatorsRegex.Replace(fileName, string.Empty);
@ -315,12 +324,12 @@ namespace NzbDrone.Core.Organizer
private void AddQualityTokens(Dictionary<string, Func<TokenMatch, string>> tokenHandlers, Artist artist, TrackFile trackFile) private void AddQualityTokens(Dictionary<string, Func<TokenMatch, string>> tokenHandlers, Artist artist, TrackFile trackFile)
{ {
var qualityTitle = _qualityDefinitionService.Get(trackFile.Quality.Quality).Title; var qualityTitle = _qualityDefinitionService.Get(trackFile.Quality.Quality).Title;
//var qualityProper = GetQualityProper(artist, trackFile.Quality); var qualityProper = GetQualityProper(trackFile.Quality);
//var qualityReal = GetQualityReal(artist, trackFile.Quality); //var qualityReal = GetQualityReal(artist, trackFile.Quality);
tokenHandlers["{Quality Full}"] = m => String.Format("{0}", qualityTitle); tokenHandlers["{Quality Full}"] = m => String.Format("{0}", qualityTitle);
tokenHandlers["{Quality Title}"] = m => qualityTitle; tokenHandlers["{Quality Title}"] = m => qualityTitle;
//tokenHandlers["{Quality Proper}"] = m => qualityProper; tokenHandlers["{Quality Proper}"] = m => qualityProper;
//tokenHandlers["{Quality Real}"] = m => qualityReal; //tokenHandlers["{Quality Real}"] = m => qualityReal;
} }
@ -459,17 +468,17 @@ namespace NzbDrone.Core.Organizer
if (tracks.Count == 1) if (tracks.Count == 1)
{ {
return tracks.First().Title.TrimEnd(EpisodeTitleTrimCharacters); return tracks.First().Title.TrimEnd(TrackTitleTrimCharacters);
} }
var titles = tracks.Select(c => c.Title.TrimEnd(EpisodeTitleTrimCharacters)) var titles = tracks.Select(c => c.Title.TrimEnd(TrackTitleTrimCharacters))
.Select(CleanupTrackTitle) .Select(CleanupTrackTitle)
.Distinct() .Distinct()
.ToList(); .ToList();
if (titles.All(t => t.IsNullOrWhiteSpace())) if (titles.All(t => t.IsNullOrWhiteSpace()))
{ {
titles = tracks.Select(c => c.Title.TrimEnd(EpisodeTitleTrimCharacters)) titles = tracks.Select(c => c.Title.TrimEnd(TrackTitleTrimCharacters))
.Distinct() .Distinct()
.ToList(); .ToList();
} }
@ -483,21 +492,20 @@ namespace NzbDrone.Core.Organizer
return MultiPartCleanupRegex.Replace(title, string.Empty).Trim(); return MultiPartCleanupRegex.Replace(title, string.Empty).Trim();
} }
// TODO: DO WE NEED FOR MUSIC? private string GetQualityProper(QualityModel quality)
//private string GetQualityProper(Series series, QualityModel quality) {
//{ if (quality.Revision.Version > 1)
// if (quality.Revision.Version > 1) {
// { if (quality.Revision.IsRepack)
// if (series.SeriesType == SeriesTypes.Anime) {
// { return "Repack";
// return "v" + quality.Revision.Version; }
// }
// return "Proper"; return "Proper";
// } }
// return String.Empty; return String.Empty;
//} }
//private string GetQualityReal(Series series, QualityModel quality) //private string GetQualityReal(Series series, QualityModel quality)
//{ //{

@ -9,7 +9,7 @@ namespace NzbDrone.Core.Organizer
public interface IFilenameSampleService public interface IFilenameSampleService
{ {
SampleResult GetStandardTrackSample(NamingConfig nameSpec); SampleResult GetStandardTrackSample(NamingConfig nameSpec);
SampleResult GetMultiDiscTrackSample(NamingConfig nameSpec);
string GetArtistFolderSample(NamingConfig nameSpec); string GetArtistFolderSample(NamingConfig nameSpec);
string GetAlbumFolderSample(NamingConfig nameSpec); string GetAlbumFolderSample(NamingConfig nameSpec);
} }
@ -20,6 +20,8 @@ namespace NzbDrone.Core.Organizer
private static Artist _standardArtist; private static Artist _standardArtist;
private static Album _standardAlbum; private static Album _standardAlbum;
private static AlbumRelease _singleRelease;
private static AlbumRelease _multiRelease;
private static Track _track1; private static Track _track1;
private static List<Track> _singleTrack; private static List<Track> _singleTrack;
private static TrackFile _singleTrackFile; private static TrackFile _singleTrackFile;
@ -47,7 +49,7 @@ namespace NzbDrone.Core.Organizer
Disambiguation = "The Best Album", Disambiguation = "The Best Album",
}; };
var _release = new AlbumRelease _singleRelease = new AlbumRelease
{ {
Album = _standardAlbum, Album = _standardAlbum,
Media = new List<Medium> Media = new List<Medium>
@ -62,9 +64,30 @@ namespace NzbDrone.Core.Organizer
Monitored = true Monitored = true
}; };
_multiRelease = new AlbumRelease
{
Album = _standardAlbum,
Media = new List<Medium>
{
new Medium
{
Name = "CD 1: First Years",
Format = "CD",
Number = 1
},
new Medium
{
Name = "CD 2: Second Best",
Format = "CD",
Number = 2
}
},
Monitored = true
};
_track1 = new Track _track1 = new Track
{ {
AlbumRelease = _release, AlbumRelease = _singleRelease,
AbsoluteTrackNumber = 3, AbsoluteTrackNumber = 3,
MediumNumber = 1, MediumNumber = 1,
@ -102,6 +125,24 @@ namespace NzbDrone.Core.Organizer
public SampleResult GetStandardTrackSample(NamingConfig nameSpec) public SampleResult GetStandardTrackSample(NamingConfig nameSpec)
{ {
_track1.AlbumRelease = _singleRelease;
var result = new SampleResult
{
FileName = BuildTrackSample(_singleTrack, _standardArtist, _standardAlbum, _singleTrackFile, nameSpec),
Artist = _standardArtist,
Album = _standardAlbum,
Tracks = _singleTrack,
TrackFile = _singleTrackFile
};
return result;
}
public SampleResult GetMultiDiscTrackSample(NamingConfig nameSpec)
{
_track1.AlbumRelease = _multiRelease;
var result = new SampleResult var result = new SampleResult
{ {
FileName = BuildTrackSample(_singleTrack, _standardArtist, _standardAlbum, _singleTrackFile, nameSpec), FileName = BuildTrackSample(_singleTrack, _standardArtist, _standardAlbum, _singleTrackFile, nameSpec),

@ -9,6 +9,7 @@ namespace NzbDrone.Core.Organizer
RenameTracks = false, RenameTracks = false,
ReplaceIllegalCharacters = true, ReplaceIllegalCharacters = true,
StandardTrackFormat = "{Artist Name} - {Album Title} - {track:00} - {Track Title}", StandardTrackFormat = "{Artist Name} - {Album Title} - {track:00} - {Track Title}",
MultiDiscTrackFormat = "{Medium Format} {medium:00}/{Artist Name} - {Album Title} - {track:00} - {Track Title}",
ArtistFolderFormat = "{Artist Name}", ArtistFolderFormat = "{Artist Name}",
AlbumFolderFormat = "{Album Title} ({Release Year})" AlbumFolderFormat = "{Album Title} ({Release Year})"
}; };
@ -16,6 +17,7 @@ namespace NzbDrone.Core.Organizer
public bool RenameTracks { get; set; } public bool RenameTracks { get; set; }
public bool ReplaceIllegalCharacters { get; set; } public bool ReplaceIllegalCharacters { get; set; }
public string StandardTrackFormat { get; set; } public string StandardTrackFormat { get; set; }
public string MultiDiscTrackFormat { get; set; }
public string ArtistFolderFormat { get; set; } public string ArtistFolderFormat { get; set; }
public string AlbumFolderFormat { get; set; } public string AlbumFolderFormat { get; set; }
} }

Loading…
Cancel
Save