mirror of https://github.com/lidarr/Lidarr
New: Seperate Naming for Multi-Disc Albums
This commit is contained in:
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 = () => {
|
||||
this.setState({
|
||||
isNamingModalOpen: true,
|
||||
|
@ -87,6 +99,8 @@ class Naming extends Component {
|
|||
|
||||
const standardTrackFormatHelpTexts = [];
|
||||
const standardTrackFormatErrors = [];
|
||||
const multiDiscTrackFormatHelpTexts = [];
|
||||
const multiDiscTrackFormatErrors = [];
|
||||
const artistFolderFormatHelpTexts = [];
|
||||
const artistFolderFormatErrors = [];
|
||||
const albumFolderFormatHelpTexts = [];
|
||||
|
@ -99,6 +113,12 @@ class Naming extends Component {
|
|||
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) {
|
||||
artistFolderFormatHelpTexts.push(`Example: ${examples.artistFolderExample}`);
|
||||
} else {
|
||||
|
@ -169,6 +189,21 @@ class Naming extends Component {
|
|||
/>
|
||||
</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>
|
||||
}
|
||||
|
||||
|
|
|
@ -37,6 +37,7 @@ 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();
|
||||
}
|
||||
|
@ -60,6 +61,12 @@ namespace Lidarr.Api.V1.Config
|
|||
basicConfig.AddToResource(resource);
|
||||
}
|
||||
|
||||
if (resource.MultiDiscTrackFormat.IsNotNullOrWhiteSpace())
|
||||
{
|
||||
var basicConfig = _filenameBuilder.GetBasicNamingConfig(nameSpec);
|
||||
basicConfig.AddToResource(resource);
|
||||
}
|
||||
|
||||
return resource;
|
||||
}
|
||||
|
||||
|
@ -79,11 +86,16 @@ namespace Lidarr.Api.V1.Config
|
|||
var sampleResource = new NamingExampleResource();
|
||||
|
||||
var singleTrackSampleResult = _filenameSampleService.GetStandardTrackSample(nameSpec);
|
||||
var multiDiscTrackSampleResult = _filenameSampleService.GetMultiDiscTrackSample(nameSpec);
|
||||
|
||||
sampleResource.SingleTrackExample = _filenameValidationService.ValidateTrackFilename(singleTrackSampleResult) != null
|
||||
? null
|
||||
: singleTrackSampleResult.FileName;
|
||||
|
||||
sampleResource.MultiDiscTrackExample = _filenameValidationService.ValidateTrackFilename(multiDiscTrackSampleResult) != null
|
||||
? null
|
||||
: multiDiscTrackSampleResult.FileName;
|
||||
|
||||
sampleResource.ArtistFolderExample = nameSpec.ArtistFolderFormat.IsNullOrWhiteSpace()
|
||||
? null
|
||||
: _filenameSampleService.GetArtistFolderSample(nameSpec);
|
||||
|
|
|
@ -7,6 +7,7 @@ namespace Lidarr.Api.V1.Config
|
|||
public bool RenameTracks { get; set; }
|
||||
public bool ReplaceIllegalCharacters { get; set; }
|
||||
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; }
|
||||
|
|
|
@ -5,6 +5,7 @@ namespace Lidarr.Api.V1.Config
|
|||
public class NamingExampleResource
|
||||
{
|
||||
public string SingleTrackExample { get; set; }
|
||||
public string MultiDiscTrackExample { get; set; }
|
||||
public string ArtistFolderExample { get; set; }
|
||||
public string AlbumFolderExample { get; set; }
|
||||
}
|
||||
|
@ -20,6 +21,7 @@ namespace Lidarr.Api.V1.Config
|
|||
RenameTracks = model.RenameTracks,
|
||||
ReplaceIllegalCharacters = model.ReplaceIllegalCharacters,
|
||||
StandardTrackFormat = model.StandardTrackFormat,
|
||||
MultiDiscTrackFormat = model.MultiDiscTrackFormat,
|
||||
ArtistFolderFormat = model.ArtistFolderFormat,
|
||||
AlbumFolderFormat = model.AlbumFolderFormat
|
||||
};
|
||||
|
@ -44,6 +46,7 @@ namespace Lidarr.Api.V1.Config
|
|||
RenameTracks = resource.RenameTracks,
|
||||
ReplaceIllegalCharacters = resource.ReplaceIllegalCharacters,
|
||||
StandardTrackFormat = resource.StandardTrackFormat,
|
||||
MultiDiscTrackFormat = resource.MultiDiscTrackFormat,
|
||||
|
||||
ArtistFolderFormat = resource.ArtistFolderFormat,
|
||||
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)
|
||||
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);
|
||||
|
||||
|
@ -96,18 +96,27 @@ namespace NzbDrone.Core.Organizer
|
|||
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;
|
||||
|
||||
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);
|
||||
|
||||
tracks = tracks.OrderBy(e => e.AlbumReleaseId).ThenBy(e => e.TrackNumber).ToList();
|
||||
|
||||
pattern = FormatTrackNumberTokens(pattern, "", tracks);
|
||||
pattern = FormatMediumNumberTokens(pattern, "", tracks);
|
||||
safePattern = FormatTrackNumberTokens(safePattern, "", tracks);
|
||||
safePattern = FormatMediumNumberTokens(safePattern, "", tracks);
|
||||
|
||||
AddArtistTokens(tokenHandlers, artist);
|
||||
AddAlbumTokens(tokenHandlers, album);
|
||||
|
@ -118,7 +127,7 @@ namespace NzbDrone.Core.Organizer
|
|||
AddMediaInfoTokens(tokenHandlers, trackFile);
|
||||
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 = 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)
|
||||
{
|
||||
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);
|
||||
|
||||
tokenHandlers["{Quality Full}"] = m => String.Format("{0}", qualityTitle);
|
||||
tokenHandlers["{Quality Title}"] = m => qualityTitle;
|
||||
//tokenHandlers["{Quality Proper}"] = m => qualityProper;
|
||||
tokenHandlers["{Quality Proper}"] = m => qualityProper;
|
||||
//tokenHandlers["{Quality Real}"] = m => qualityReal;
|
||||
}
|
||||
|
||||
|
@ -459,17 +468,17 @@ namespace NzbDrone.Core.Organizer
|
|||
|
||||
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)
|
||||
.Distinct()
|
||||
.ToList();
|
||||
|
||||
if (titles.All(t => t.IsNullOrWhiteSpace()))
|
||||
{
|
||||
titles = tracks.Select(c => c.Title.TrimEnd(EpisodeTitleTrimCharacters))
|
||||
titles = tracks.Select(c => c.Title.TrimEnd(TrackTitleTrimCharacters))
|
||||
.Distinct()
|
||||
.ToList();
|
||||
}
|
||||
|
@ -483,21 +492,20 @@ namespace NzbDrone.Core.Organizer
|
|||
return MultiPartCleanupRegex.Replace(title, string.Empty).Trim();
|
||||
}
|
||||
|
||||
// TODO: DO WE NEED FOR MUSIC?
|
||||
//private string GetQualityProper(Series series, QualityModel quality)
|
||||
//{
|
||||
// if (quality.Revision.Version > 1)
|
||||
// {
|
||||
// if (series.SeriesType == SeriesTypes.Anime)
|
||||
// {
|
||||
// return "v" + quality.Revision.Version;
|
||||
// }
|
||||
private string GetQualityProper(QualityModel quality)
|
||||
{
|
||||
if (quality.Revision.Version > 1)
|
||||
{
|
||||
if (quality.Revision.IsRepack)
|
||||
{
|
||||
return "Repack";
|
||||
}
|
||||
|
||||
// return "Proper";
|
||||
// }
|
||||
return "Proper";
|
||||
}
|
||||
|
||||
// return String.Empty;
|
||||
//}
|
||||
return String.Empty;
|
||||
}
|
||||
|
||||
//private string GetQualityReal(Series series, QualityModel quality)
|
||||
//{
|
||||
|
|
|
@ -9,7 +9,7 @@ namespace NzbDrone.Core.Organizer
|
|||
public interface IFilenameSampleService
|
||||
{
|
||||
SampleResult GetStandardTrackSample(NamingConfig nameSpec);
|
||||
|
||||
SampleResult GetMultiDiscTrackSample(NamingConfig nameSpec);
|
||||
string GetArtistFolderSample(NamingConfig nameSpec);
|
||||
string GetAlbumFolderSample(NamingConfig nameSpec);
|
||||
}
|
||||
|
@ -20,6 +20,8 @@ namespace NzbDrone.Core.Organizer
|
|||
|
||||
private static Artist _standardArtist;
|
||||
private static Album _standardAlbum;
|
||||
private static AlbumRelease _singleRelease;
|
||||
private static AlbumRelease _multiRelease;
|
||||
private static Track _track1;
|
||||
private static List<Track> _singleTrack;
|
||||
private static TrackFile _singleTrackFile;
|
||||
|
@ -47,7 +49,7 @@ namespace NzbDrone.Core.Organizer
|
|||
Disambiguation = "The Best Album",
|
||||
};
|
||||
|
||||
var _release = new AlbumRelease
|
||||
_singleRelease = new AlbumRelease
|
||||
{
|
||||
Album = _standardAlbum,
|
||||
Media = new List<Medium>
|
||||
|
@ -62,9 +64,30 @@ namespace NzbDrone.Core.Organizer
|
|||
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
|
||||
{
|
||||
AlbumRelease = _release,
|
||||
AlbumRelease = _singleRelease,
|
||||
AbsoluteTrackNumber = 3,
|
||||
MediumNumber = 1,
|
||||
|
||||
|
@ -102,6 +125,24 @@ namespace NzbDrone.Core.Organizer
|
|||
|
||||
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
|
||||
{
|
||||
FileName = BuildTrackSample(_singleTrack, _standardArtist, _standardAlbum, _singleTrackFile, nameSpec),
|
||||
|
|
|
@ -9,6 +9,7 @@ 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}",
|
||||
ArtistFolderFormat = "{Artist Name}",
|
||||
AlbumFolderFormat = "{Album Title} ({Release Year})"
|
||||
};
|
||||
|
@ -16,6 +17,7 @@ namespace NzbDrone.Core.Organizer
|
|||
public bool RenameTracks { get; set; }
|
||||
public bool ReplaceIllegalCharacters { get; set; }
|
||||
public string StandardTrackFormat { get; set; }
|
||||
public string MultiDiscTrackFormat { get; set; }
|
||||
public string ArtistFolderFormat { get; set; }
|
||||
public string AlbumFolderFormat { get; set; }
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue