mirror of https://github.com/lidarr/Lidarr
NamingConfig Refactor
Adds track NamingConfig, Gets naming section in settings working. Adds Release Year token and track number token
This commit is contained in:
parent
a8ac1f3adc
commit
fe58f54ad4
|
@ -35,10 +35,13 @@ namespace NzbDrone.Api.Config
|
|||
|
||||
SharedValidator.RuleFor(c => c.MultiEpisodeStyle).InclusiveBetween(0, 5);
|
||||
SharedValidator.RuleFor(c => c.StandardEpisodeFormat).ValidEpisodeFormat();
|
||||
SharedValidator.RuleFor(c => c.StandardTrackFormat).ValidTrackFormat();
|
||||
SharedValidator.RuleFor(c => c.DailyEpisodeFormat).ValidDailyEpisodeFormat();
|
||||
SharedValidator.RuleFor(c => c.AnimeEpisodeFormat).ValidAnimeEpisodeFormat();
|
||||
SharedValidator.RuleFor(c => c.SeriesFolderFormat).ValidSeriesFolderFormat();
|
||||
SharedValidator.RuleFor(c => c.SeasonFolderFormat).ValidSeasonFolderFormat();
|
||||
SharedValidator.RuleFor(c => c.ArtistFolderFormat).ValidArtistFolderFormat();
|
||||
SharedValidator.RuleFor(c => c.AlbumFolderFormat).ValidAlbumFolderFormat();
|
||||
}
|
||||
|
||||
private void UpdateNamingConfig(NamingConfigResource resource)
|
||||
|
@ -74,6 +77,7 @@ namespace NzbDrone.Api.Config
|
|||
var sampleResource = new NamingSampleResource();
|
||||
|
||||
var singleEpisodeSampleResult = _filenameSampleService.GetStandardSample(nameSpec);
|
||||
var singleTrackSampleResult = _filenameSampleService.GetStandardTrackSample(nameSpec);
|
||||
var multiEpisodeSampleResult = _filenameSampleService.GetMultiEpisodeSample(nameSpec);
|
||||
var dailyEpisodeSampleResult = _filenameSampleService.GetDailySample(nameSpec);
|
||||
var animeEpisodeSampleResult = _filenameSampleService.GetAnimeSample(nameSpec);
|
||||
|
@ -83,6 +87,10 @@ namespace NzbDrone.Api.Config
|
|||
? "Invalid format"
|
||||
: singleEpisodeSampleResult.FileName;
|
||||
|
||||
sampleResource.SingleTrackExample = _filenameValidationService.ValidateTrackFilename(singleTrackSampleResult) != null
|
||||
? "Invalid format"
|
||||
: singleTrackSampleResult.FileName;
|
||||
|
||||
sampleResource.MultiEpisodeExample = _filenameValidationService.ValidateStandardFilename(multiEpisodeSampleResult) != null
|
||||
? "Invalid format"
|
||||
: multiEpisodeSampleResult.FileName;
|
||||
|
@ -107,18 +115,28 @@ namespace NzbDrone.Api.Config
|
|||
? "Invalid format"
|
||||
: _filenameSampleService.GetSeasonFolderSample(nameSpec);
|
||||
|
||||
sampleResource.ArtistFolderExample = nameSpec.ArtistFolderFormat.IsNullOrWhiteSpace()
|
||||
? "Invalid format"
|
||||
: _filenameSampleService.GetArtistFolderSample(nameSpec);
|
||||
|
||||
sampleResource.AlbumFolderExample = nameSpec.AlbumFolderFormat.IsNullOrWhiteSpace()
|
||||
? "Invalid format"
|
||||
: _filenameSampleService.GetAlbumFolderSample(nameSpec);
|
||||
|
||||
return sampleResource.AsResponse();
|
||||
}
|
||||
|
||||
private void ValidateFormatResult(NamingConfig nameSpec)
|
||||
{
|
||||
var singleEpisodeSampleResult = _filenameSampleService.GetStandardSample(nameSpec);
|
||||
var singleTrackSampleResult = _filenameSampleService.GetStandardTrackSample(nameSpec);
|
||||
var multiEpisodeSampleResult = _filenameSampleService.GetMultiEpisodeSample(nameSpec);
|
||||
var dailyEpisodeSampleResult = _filenameSampleService.GetDailySample(nameSpec);
|
||||
var animeEpisodeSampleResult = _filenameSampleService.GetAnimeSample(nameSpec);
|
||||
var animeMultiEpisodeSampleResult = _filenameSampleService.GetAnimeMultiEpisodeSample(nameSpec);
|
||||
|
||||
var singleEpisodeValidationResult = _filenameValidationService.ValidateStandardFilename(singleEpisodeSampleResult);
|
||||
var singleTrackValidationResult = _filenameValidationService.ValidateTrackFilename(singleTrackSampleResult);
|
||||
var multiEpisodeValidationResult = _filenameValidationService.ValidateStandardFilename(multiEpisodeSampleResult);
|
||||
var dailyEpisodeValidationResult = _filenameValidationService.ValidateDailyFilename(dailyEpisodeSampleResult);
|
||||
var animeEpisodeValidationResult = _filenameValidationService.ValidateAnimeFilename(animeEpisodeSampleResult);
|
||||
|
@ -127,6 +145,7 @@ namespace NzbDrone.Api.Config
|
|||
var validationFailures = new List<ValidationFailure>();
|
||||
|
||||
validationFailures.AddIfNotNull(singleEpisodeValidationResult);
|
||||
validationFailures.AddIfNotNull(singleTrackValidationResult);
|
||||
validationFailures.AddIfNotNull(multiEpisodeValidationResult);
|
||||
validationFailures.AddIfNotNull(dailyEpisodeValidationResult);
|
||||
validationFailures.AddIfNotNull(animeEpisodeValidationResult);
|
||||
|
|
|
@ -6,13 +6,17 @@ namespace NzbDrone.Api.Config
|
|||
public class NamingConfigResource : RestResource
|
||||
{
|
||||
public bool RenameEpisodes { get; set; }
|
||||
public bool RenameTracks { get; set; }
|
||||
public bool ReplaceIllegalCharacters { get; set; }
|
||||
public int MultiEpisodeStyle { get; set; }
|
||||
public string StandardEpisodeFormat { get; set; }
|
||||
public string StandardTrackFormat { get; set; }
|
||||
public string DailyEpisodeFormat { get; set; }
|
||||
public string AnimeEpisodeFormat { get; set; }
|
||||
public string SeriesFolderFormat { get; set; }
|
||||
public string SeasonFolderFormat { get; set; }
|
||||
public string ArtistFolderFormat { get; set; }
|
||||
public string AlbumFolderFormat { get; set; }
|
||||
public bool IncludeSeriesTitle { get; set; }
|
||||
public bool IncludeEpisodeTitle { get; set; }
|
||||
public bool IncludeQuality { get; set; }
|
||||
|
@ -30,13 +34,17 @@ namespace NzbDrone.Api.Config
|
|||
Id = model.Id,
|
||||
|
||||
RenameEpisodes = model.RenameEpisodes,
|
||||
RenameTracks = model.RenameTracks,
|
||||
ReplaceIllegalCharacters = model.ReplaceIllegalCharacters,
|
||||
MultiEpisodeStyle = model.MultiEpisodeStyle,
|
||||
StandardEpisodeFormat = model.StandardEpisodeFormat,
|
||||
StandardTrackFormat = model.StandardTrackFormat,
|
||||
DailyEpisodeFormat = model.DailyEpisodeFormat,
|
||||
AnimeEpisodeFormat = model.AnimeEpisodeFormat,
|
||||
SeriesFolderFormat = model.SeriesFolderFormat,
|
||||
SeasonFolderFormat = model.SeasonFolderFormat
|
||||
SeasonFolderFormat = model.SeasonFolderFormat,
|
||||
ArtistFolderFormat = model.ArtistFolderFormat,
|
||||
AlbumFolderFormat = model.AlbumFolderFormat
|
||||
//IncludeSeriesTitle
|
||||
//IncludeEpisodeTitle
|
||||
//IncludeQuality
|
||||
|
@ -63,13 +71,17 @@ namespace NzbDrone.Api.Config
|
|||
Id = resource.Id,
|
||||
|
||||
RenameEpisodes = resource.RenameEpisodes,
|
||||
RenameTracks = resource.RenameTracks,
|
||||
ReplaceIllegalCharacters = resource.ReplaceIllegalCharacters,
|
||||
MultiEpisodeStyle = resource.MultiEpisodeStyle,
|
||||
StandardEpisodeFormat = resource.StandardEpisodeFormat,
|
||||
StandardTrackFormat = resource.StandardTrackFormat,
|
||||
DailyEpisodeFormat = resource.DailyEpisodeFormat,
|
||||
AnimeEpisodeFormat = resource.AnimeEpisodeFormat,
|
||||
SeriesFolderFormat = resource.SeriesFolderFormat,
|
||||
SeasonFolderFormat = resource.SeasonFolderFormat
|
||||
SeasonFolderFormat = resource.SeasonFolderFormat,
|
||||
ArtistFolderFormat = resource.ArtistFolderFormat,
|
||||
AlbumFolderFormat = resource.AlbumFolderFormat
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,11 +3,14 @@
|
|||
public class NamingSampleResource
|
||||
{
|
||||
public string SingleEpisodeExample { get; set; }
|
||||
public string SingleTrackExample { get; set; }
|
||||
public string MultiEpisodeExample { get; set; }
|
||||
public string DailyEpisodeExample { get; set; }
|
||||
public string AnimeEpisodeExample { get; set; }
|
||||
public string AnimeMultiEpisodeExample { get; set; }
|
||||
public string SeriesFolderExample { get; set; }
|
||||
public string SeasonFolderExample { get; set; }
|
||||
public string ArtistFolderExample { get; set; }
|
||||
public string AlbumFolderExample { get; set; }
|
||||
}
|
||||
}
|
|
@ -97,6 +97,8 @@ namespace NzbDrone.Core.Datastore.Migration
|
|||
|
||||
Alter.Table("NamingConfig")
|
||||
.AddColumn("ArtistFolderFormat").AsString().Nullable()
|
||||
.AddColumn("RenameTracks").AsBoolean().Nullable()
|
||||
.AddColumn("StandardTrackFormat").AsString().Nullable()
|
||||
.AddColumn("AlbumFolderFormat").AsString().Nullable();
|
||||
}
|
||||
|
||||
|
|
|
@ -18,12 +18,13 @@ namespace NzbDrone.Core.Organizer
|
|||
public interface IBuildFileNames
|
||||
{
|
||||
string BuildFileName(List<Episode> episodes, Series series, EpisodeFile episodeFile, NamingConfig namingConfig = null);
|
||||
string BuildTrackFileName(List<Track> tracks, Artist artist, Album album, TrackFile trackFile, NamingConfig namingConfig = null);
|
||||
string BuildFilePath(Series series, int seasonNumber, string fileName, string extension);
|
||||
string BuildSeasonPath(Series series, int seasonNumber);
|
||||
BasicNamingConfig GetBasicNamingConfig(NamingConfig nameSpec);
|
||||
string GetSeriesFolder(Series series, NamingConfig namingConfig = null);
|
||||
string GetArtistFolder(Artist artist, NamingConfig namingConfig = null);
|
||||
string GetAlbumFolder(Album album, NamingConfig namingConfig = null);
|
||||
string GetAlbumFolder(Artist artist, Album album, NamingConfig namingConfig = null);
|
||||
string GetSeasonFolder(Series series, int seasonNumber, NamingConfig namingConfig = null);
|
||||
|
||||
// TODO: Implement Music functions
|
||||
|
@ -44,6 +45,9 @@ namespace NzbDrone.Core.Organizer
|
|||
private static readonly Regex EpisodeRegex = new Regex(@"(?<episode>\{episode(?:\:0+)?})",
|
||||
RegexOptions.Compiled | RegexOptions.IgnoreCase);
|
||||
|
||||
private static readonly Regex TrackRegex = new Regex(@"(?<track>\{track(?:\:0+)?})",
|
||||
RegexOptions.Compiled | RegexOptions.IgnoreCase);
|
||||
|
||||
private static readonly Regex SeasonRegex = new Regex(@"(?<season>\{season(?:\:0+)?})",
|
||||
RegexOptions.Compiled | RegexOptions.IgnoreCase);
|
||||
|
||||
|
@ -61,6 +65,12 @@ namespace NzbDrone.Core.Organizer
|
|||
public static readonly Regex SeriesTitleRegex = new Regex(@"(?<token>\{(?:Series)(?<separator>[- ._])(Clean)?Title\})",
|
||||
RegexOptions.Compiled | RegexOptions.IgnoreCase);
|
||||
|
||||
public static readonly Regex ArtistNameRegex = new Regex(@"(?<token>\{(?:Artist)(?<separator>[- ._])(Clean)?Name\})",
|
||||
RegexOptions.Compiled | RegexOptions.IgnoreCase);
|
||||
|
||||
public static readonly Regex AlbumTitleRegex = new Regex(@"(?<token>\{(?:Album)(?<separator>[- ._])(Clean)?Title\})",
|
||||
RegexOptions.Compiled | RegexOptions.IgnoreCase);
|
||||
|
||||
private static readonly Regex FileNameCleanupRegex = new Regex(@"([- ._])(\1)+", RegexOptions.Compiled);
|
||||
private static readonly Regex TrimSeparatorsRegex = new Regex(@"[- ._]$", RegexOptions.Compiled);
|
||||
|
||||
|
@ -142,6 +152,47 @@ namespace NzbDrone.Core.Organizer
|
|||
return fileName;
|
||||
}
|
||||
|
||||
public string BuildTrackFileName(List<Track> tracks, Artist artist, Album album, TrackFile trackFile, NamingConfig namingConfig = null)
|
||||
{
|
||||
if (namingConfig == null)
|
||||
{
|
||||
namingConfig = _namingConfigService.GetConfig();
|
||||
}
|
||||
|
||||
if (!namingConfig.RenameTracks)
|
||||
{
|
||||
return GetOriginalTitle(trackFile);
|
||||
}
|
||||
|
||||
if (namingConfig.StandardTrackFormat.IsNullOrWhiteSpace())
|
||||
{
|
||||
throw new NamingFormatException("Standard track format cannot be empty");
|
||||
}
|
||||
|
||||
var pattern = namingConfig.StandardTrackFormat;
|
||||
var tokenHandlers = new Dictionary<string, Func<TokenMatch, string>>(FileNameBuilderTokenEqualityComparer.Instance);
|
||||
|
||||
tracks = tracks.OrderBy(e => e.AlbumId).ThenBy(e => e.TrackNumber).ToList();
|
||||
|
||||
//pattern = AddSeasonEpisodeNumberingTokens(pattern, tokenHandlers, episodes, namingConfig);
|
||||
|
||||
pattern = FormatTrackNumberTokens(pattern, "", tracks);
|
||||
//pattern = AddAbsoluteNumberingTokens(pattern, tokenHandlers, series, episodes, namingConfig);
|
||||
|
||||
AddArtistTokens(tokenHandlers, artist);
|
||||
AddAlbumTokens(tokenHandlers, album);
|
||||
AddTrackTokens(tokenHandlers, tracks);
|
||||
AddTrackFileTokens(tokenHandlers, trackFile);
|
||||
AddQualityTokens(tokenHandlers, artist, trackFile);
|
||||
//AddMediaInfoTokens(tokenHandlers, trackFile); TODO ReWork MediaInfo for Tracks
|
||||
|
||||
var fileName = ReplaceTokens(pattern, tokenHandlers, namingConfig).Trim();
|
||||
fileName = FileNameCleanupRegex.Replace(fileName, match => match.Captures[0].Value[0].ToString());
|
||||
fileName = TrimSeparatorsRegex.Replace(fileName, string.Empty);
|
||||
|
||||
return fileName;
|
||||
}
|
||||
|
||||
public string BuildFilePath(Series series, int seasonNumber, string fileName, string extension)
|
||||
{
|
||||
Ensure.That(extension, () => extension).IsNotNullOrWhiteSpace();
|
||||
|
@ -263,7 +314,7 @@ namespace NzbDrone.Core.Organizer
|
|||
return CleanFolderName(ReplaceTokens(namingConfig.SeasonFolderFormat, tokenHandlers, namingConfig));
|
||||
}
|
||||
|
||||
public string GetAlbumFolder(Album album, NamingConfig namingConfig = null)
|
||||
public string GetAlbumFolder(Artist artist, Album album, NamingConfig namingConfig = null)
|
||||
{
|
||||
if (namingConfig == null)
|
||||
{
|
||||
|
@ -273,6 +324,7 @@ namespace NzbDrone.Core.Organizer
|
|||
var tokenHandlers = new Dictionary<string, Func<TokenMatch, string>>(FileNameBuilderTokenEqualityComparer.Instance);
|
||||
|
||||
AddAlbumTokens(tokenHandlers, album);
|
||||
AddArtistTokens(tokenHandlers, artist);
|
||||
|
||||
return CleanFolderName(ReplaceTokens(namingConfig.AlbumFolderFormat, tokenHandlers, namingConfig));
|
||||
}
|
||||
|
@ -322,7 +374,7 @@ namespace NzbDrone.Core.Organizer
|
|||
{
|
||||
tokenHandlers["{Album Title}"] = m => album.Title;
|
||||
tokenHandlers["{Album CleanTitle}"] = m => CleanTitle(album.Title);
|
||||
tokenHandlers["{Album Year}"] = m => album.ReleaseDate.Year.ToString();
|
||||
tokenHandlers["{Release Year}"] = m => album.ReleaseDate.Year.ToString();
|
||||
}
|
||||
|
||||
private string AddSeasonEpisodeNumberingTokens(string pattern, Dictionary<string, Func<TokenMatch, string>> tokenHandlers, List<Episode> episodes, NamingConfig namingConfig)
|
||||
|
@ -469,6 +521,12 @@ namespace NzbDrone.Core.Organizer
|
|||
tokenHandlers["{Episode CleanTitle}"] = m => CleanTitle(GetEpisodeTitle(episodes, "and"));
|
||||
}
|
||||
|
||||
private void AddTrackTokens(Dictionary<string, Func<TokenMatch, string>> tokenHandlers, List<Track> tracks)
|
||||
{
|
||||
tokenHandlers["{Track Title}"] = m => GetTrackTitle(tracks, "+");
|
||||
tokenHandlers["{Track CleanTitle}"] = m => CleanTitle(GetTrackTitle(tracks, "and"));
|
||||
}
|
||||
|
||||
private void AddEpisodeFileTokens(Dictionary<string, Func<TokenMatch, string>> tokenHandlers, EpisodeFile episodeFile)
|
||||
{
|
||||
tokenHandlers["{Original Title}"] = m => GetOriginalTitle(episodeFile);
|
||||
|
@ -476,6 +534,13 @@ namespace NzbDrone.Core.Organizer
|
|||
tokenHandlers["{Release Group}"] = m => episodeFile.ReleaseGroup ?? m.DefaultValue("Lidarr");
|
||||
}
|
||||
|
||||
private void AddTrackFileTokens(Dictionary<string, Func<TokenMatch, string>> tokenHandlers, TrackFile trackFile)
|
||||
{
|
||||
tokenHandlers["{Original Title}"] = m => GetOriginalTitle(trackFile);
|
||||
tokenHandlers["{Original Filename}"] = m => GetOriginalFileName(trackFile);
|
||||
tokenHandlers["{Release Group}"] = m => trackFile.ReleaseGroup ?? m.DefaultValue("Lidarr");
|
||||
}
|
||||
|
||||
private void AddQualityTokens(Dictionary<string, Func<TokenMatch, string>> tokenHandlers, Series series, EpisodeFile episodeFile)
|
||||
{
|
||||
var qualityTitle = _qualityDefinitionService.Get(episodeFile.Quality.Quality).Title;
|
||||
|
@ -488,6 +553,18 @@ namespace NzbDrone.Core.Organizer
|
|||
tokenHandlers["{Quality Real}"] = m => qualityReal;
|
||||
}
|
||||
|
||||
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 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 Real}"] = m => qualityReal;
|
||||
}
|
||||
|
||||
private void AddMediaInfoTokens(Dictionary<string, Func<TokenMatch, string>> tokenHandlers, EpisodeFile episodeFile)
|
||||
{
|
||||
if (episodeFile.MediaInfo == null) return;
|
||||
|
@ -683,6 +760,20 @@ namespace NzbDrone.Core.Organizer
|
|||
return ReplaceSeasonTokens(pattern, episodes.First().SeasonNumber);
|
||||
}
|
||||
|
||||
private string FormatTrackNumberTokens(string basePattern, string formatPattern, List<Track> tracks)
|
||||
{
|
||||
var pattern = string.Empty;
|
||||
|
||||
for (int i = 0; i < tracks.Count; i++)
|
||||
{
|
||||
var patternToReplace = i == 0 ? basePattern : formatPattern;
|
||||
|
||||
pattern += TrackRegex.Replace(patternToReplace, match => ReplaceNumberToken(match.Groups["track"].Value, tracks[i].TrackNumber));
|
||||
}
|
||||
|
||||
return pattern;
|
||||
}
|
||||
|
||||
private string FormatAbsoluteNumberTokens(string basePattern, string formatPattern, List<Episode> episodes)
|
||||
{
|
||||
var pattern = string.Empty;
|
||||
|
@ -765,6 +856,30 @@ namespace NzbDrone.Core.Organizer
|
|||
return string.Join(separator, titles);
|
||||
}
|
||||
|
||||
private string GetTrackTitle(List<Track> tracks, string separator)
|
||||
{
|
||||
separator = string.Format(" {0} ", separator.Trim());
|
||||
|
||||
if (tracks.Count == 1)
|
||||
{
|
||||
return tracks.First().Title.TrimEnd(EpisodeTitleTrimCharacters);
|
||||
}
|
||||
|
||||
var titles = tracks.Select(c => c.Title.TrimEnd(EpisodeTitleTrimCharacters))
|
||||
.Select(CleanupEpisodeTitle)
|
||||
.Distinct()
|
||||
.ToList();
|
||||
|
||||
if (titles.All(t => t.IsNullOrWhiteSpace()))
|
||||
{
|
||||
titles = tracks.Select(c => c.Title.TrimEnd(EpisodeTitleTrimCharacters))
|
||||
.Distinct()
|
||||
.ToList();
|
||||
}
|
||||
|
||||
return string.Join(separator, titles);
|
||||
}
|
||||
|
||||
private string CleanupEpisodeTitle(string title)
|
||||
{
|
||||
//this will remove (1),(2) from the end of multi part episodes.
|
||||
|
@ -806,6 +921,16 @@ namespace NzbDrone.Core.Organizer
|
|||
return episodeFile.SceneName;
|
||||
}
|
||||
|
||||
private string GetOriginalTitle(TrackFile trackFile)
|
||||
{
|
||||
if (trackFile.SceneName.IsNullOrWhiteSpace())
|
||||
{
|
||||
return GetOriginalFileName(trackFile);
|
||||
}
|
||||
|
||||
return trackFile.SceneName;
|
||||
}
|
||||
|
||||
private string GetOriginalFileName(EpisodeFile episodeFile)
|
||||
{
|
||||
if (episodeFile.RelativePath.IsNullOrWhiteSpace())
|
||||
|
@ -816,35 +941,16 @@ namespace NzbDrone.Core.Organizer
|
|||
return Path.GetFileNameWithoutExtension(episodeFile.RelativePath);
|
||||
}
|
||||
|
||||
//public string GetArtistFolder(Artist artist, NamingConfig namingConfig = null)
|
||||
//{
|
||||
// if (namingConfig == null)
|
||||
// {
|
||||
// namingConfig = _namingConfigService.GetConfig();
|
||||
// }
|
||||
private string GetOriginalFileName(TrackFile trackFile)
|
||||
{
|
||||
if (trackFile.RelativePath.IsNullOrWhiteSpace())
|
||||
{
|
||||
return Path.GetFileNameWithoutExtension(trackFile.Path);
|
||||
}
|
||||
|
||||
// var tokenHandlers = new Dictionary<string, Func<TokenMatch, string>>(FileNameBuilderTokenEqualityComparer.Instance);
|
||||
return Path.GetFileNameWithoutExtension(trackFile.RelativePath);
|
||||
}
|
||||
|
||||
// AddArtistTokens(tokenHandlers, artist);
|
||||
|
||||
// return CleanFolderName(ReplaceTokens("{Artist Name}", tokenHandlers, namingConfig)); //namingConfig.ArtistFolderFormat,
|
||||
//}
|
||||
|
||||
//public string GetAlbumFolder(Artist artist, string albumName, NamingConfig namingConfig = null)
|
||||
//{
|
||||
// throw new NotImplementedException();
|
||||
// //if (namingConfig == null)
|
||||
// //{
|
||||
// // namingConfig = _namingConfigService.GetConfig();
|
||||
// //}
|
||||
|
||||
// //var tokenHandlers = new Dictionary<string, Func<TokenMatch, string>>(FileNameBuilderTokenEqualityComparer.Instance);
|
||||
|
||||
// //AddSeriesTokens(tokenHandlers, artist);
|
||||
// //AddSeasonTokens(tokenHandlers, seasonNumber);
|
||||
|
||||
// //return CleanFolderName(ReplaceTokens(namingConfig.SeasonFolderFormat, tokenHandlers, namingConfig));
|
||||
//}
|
||||
}
|
||||
|
||||
internal sealed class TokenMatch
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
using NzbDrone.Core.MediaFiles;
|
||||
using NzbDrone.Core.Qualities;
|
||||
using NzbDrone.Core.Tv;
|
||||
using NzbDrone.Core.Music;
|
||||
using NzbDrone.Core.MediaFiles.MediaInfo;
|
||||
|
||||
namespace NzbDrone.Core.Organizer
|
||||
|
@ -9,26 +10,34 @@ namespace NzbDrone.Core.Organizer
|
|||
public interface IFilenameSampleService
|
||||
{
|
||||
SampleResult GetStandardSample(NamingConfig nameSpec);
|
||||
SampleResult GetStandardTrackSample(NamingConfig nameSpec);
|
||||
SampleResult GetMultiEpisodeSample(NamingConfig nameSpec);
|
||||
SampleResult GetDailySample(NamingConfig nameSpec);
|
||||
SampleResult GetAnimeSample(NamingConfig nameSpec);
|
||||
SampleResult GetAnimeMultiEpisodeSample(NamingConfig nameSpec);
|
||||
string GetSeriesFolderSample(NamingConfig nameSpec);
|
||||
string GetSeasonFolderSample(NamingConfig nameSpec);
|
||||
string GetArtistFolderSample(NamingConfig nameSpec);
|
||||
string GetAlbumFolderSample(NamingConfig nameSpec);
|
||||
}
|
||||
|
||||
public class FileNameSampleService : IFilenameSampleService
|
||||
{
|
||||
private readonly IBuildFileNames _buildFileNames;
|
||||
private static Series _standardSeries;
|
||||
private static Artist _standardArtist;
|
||||
private static Album _standardAlbum;
|
||||
private static Track _track1;
|
||||
private static Series _dailySeries;
|
||||
private static Series _animeSeries;
|
||||
private static Episode _episode1;
|
||||
private static Episode _episode2;
|
||||
private static Episode _episode3;
|
||||
private static List<Episode> _singleEpisode;
|
||||
private static List<Track> _singleTrack;
|
||||
private static List<Episode> _multiEpisodes;
|
||||
private static EpisodeFile _singleEpisodeFile;
|
||||
private static TrackFile _singleTrackFile;
|
||||
private static EpisodeFile _multiEpisodeFile;
|
||||
private static EpisodeFile _dailyEpisodeFile;
|
||||
private static EpisodeFile _animeEpisodeFile;
|
||||
|
@ -44,6 +53,17 @@ namespace NzbDrone.Core.Organizer
|
|||
Title = "Series Title (2010)"
|
||||
};
|
||||
|
||||
_standardArtist = new Artist
|
||||
{
|
||||
Name = "Artist Name"
|
||||
};
|
||||
|
||||
_standardAlbum = new Album
|
||||
{
|
||||
Title = "Album Title",
|
||||
ReleaseDate = System.DateTime.Today
|
||||
};
|
||||
|
||||
_dailySeries = new Series
|
||||
{
|
||||
SeriesType = SeriesTypes.Daily,
|
||||
|
@ -56,6 +76,14 @@ namespace NzbDrone.Core.Organizer
|
|||
Title = "Series Title (2010)"
|
||||
};
|
||||
|
||||
_track1 = new Track
|
||||
{
|
||||
TrackNumber = 3,
|
||||
|
||||
Title = "Track Title (1)",
|
||||
|
||||
};
|
||||
|
||||
_episode1 = new Episode
|
||||
{
|
||||
SeasonNumber = 1,
|
||||
|
@ -82,6 +110,7 @@ namespace NzbDrone.Core.Organizer
|
|||
};
|
||||
|
||||
_singleEpisode = new List<Episode> { _episode1 };
|
||||
_singleTrack = new List<Track> { _track1 };
|
||||
_multiEpisodes = new List<Episode> { _episode1, _episode2, _episode3 };
|
||||
|
||||
var mediaInfo = new MediaInfoModel()
|
||||
|
@ -115,6 +144,15 @@ namespace NzbDrone.Core.Organizer
|
|||
MediaInfo = mediaInfo
|
||||
};
|
||||
|
||||
_singleTrackFile = new TrackFile
|
||||
{
|
||||
Quality = new QualityModel(Quality.MP3256, new Revision(2)),
|
||||
RelativePath = "Artist.Name.Album.Name.TrackNum.Track.Title.MP3256.mp3",
|
||||
SceneName = "Artist.Name.Album.Name.TrackNum.Track.Title.MP3256",
|
||||
ReleaseGroup = "RlsGrp",
|
||||
MediaInfo = mediaInfo
|
||||
};
|
||||
|
||||
_multiEpisodeFile = new EpisodeFile
|
||||
{
|
||||
Quality = new QualityModel(Quality.MP3256, new Revision(2)),
|
||||
|
@ -165,6 +203,20 @@ namespace NzbDrone.Core.Organizer
|
|||
return result;
|
||||
}
|
||||
|
||||
public SampleResult GetStandardTrackSample(NamingConfig nameSpec)
|
||||
{
|
||||
var result = new SampleResult
|
||||
{
|
||||
FileName = BuildTrackSample(_singleTrack, _standardArtist, _standardAlbum, _singleTrackFile, nameSpec),
|
||||
Artist = _standardArtist,
|
||||
Album = _standardAlbum,
|
||||
Tracks = _singleTrack,
|
||||
TrackFile = _singleTrackFile
|
||||
};
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public SampleResult GetMultiEpisodeSample(NamingConfig nameSpec)
|
||||
{
|
||||
var result = new SampleResult
|
||||
|
@ -227,6 +279,16 @@ namespace NzbDrone.Core.Organizer
|
|||
return _buildFileNames.GetSeasonFolder(_standardSeries, _episode1.SeasonNumber, nameSpec);
|
||||
}
|
||||
|
||||
public string GetArtistFolderSample(NamingConfig nameSpec)
|
||||
{
|
||||
return _buildFileNames.GetArtistFolder(_standardArtist, nameSpec);
|
||||
}
|
||||
|
||||
public string GetAlbumFolderSample(NamingConfig nameSpec)
|
||||
{
|
||||
return _buildFileNames.GetAlbumFolder(_standardArtist, _standardAlbum, nameSpec);
|
||||
}
|
||||
|
||||
private string BuildSample(List<Episode> episodes, Series series, EpisodeFile episodeFile, NamingConfig nameSpec)
|
||||
{
|
||||
try
|
||||
|
@ -238,5 +300,17 @@ namespace NzbDrone.Core.Organizer
|
|||
return string.Empty;
|
||||
}
|
||||
}
|
||||
|
||||
private string BuildTrackSample(List<Track> tracks, Artist artist, Album album, TrackFile trackFile, NamingConfig nameSpec)
|
||||
{
|
||||
try
|
||||
{
|
||||
return _buildFileNames.BuildTrackFileName(tracks, artist, album, trackFile, nameSpec);
|
||||
}
|
||||
catch (NamingFormatException)
|
||||
{
|
||||
return string.Empty;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,6 +18,12 @@ namespace NzbDrone.Core.Organizer
|
|||
return ruleBuilder.SetValidator(new ValidStandardEpisodeFormatValidator());
|
||||
}
|
||||
|
||||
public static IRuleBuilderOptions<T, string> ValidTrackFormat<T>(this IRuleBuilder<T, string> ruleBuilder)
|
||||
{
|
||||
ruleBuilder.SetValidator(new NotEmptyValidator(null));
|
||||
return ruleBuilder.SetValidator(new ValidStandardTrackFormatValidator());
|
||||
}
|
||||
|
||||
public static IRuleBuilderOptions<T, string> ValidDailyEpisodeFormat<T>(this IRuleBuilder<T, string> ruleBuilder)
|
||||
{
|
||||
ruleBuilder.SetValidator(new NotEmptyValidator(null));
|
||||
|
@ -41,6 +47,17 @@ namespace NzbDrone.Core.Organizer
|
|||
ruleBuilder.SetValidator(new NotEmptyValidator(null));
|
||||
return ruleBuilder.SetValidator(new RegularExpressionValidator(SeasonFolderRegex)).WithMessage("Must contain season number");
|
||||
}
|
||||
public static IRuleBuilderOptions<T, string> ValidArtistFolderFormat<T>(this IRuleBuilder<T, string> ruleBuilder)
|
||||
{
|
||||
ruleBuilder.SetValidator(new NotEmptyValidator(null));
|
||||
return ruleBuilder.SetValidator(new RegularExpressionValidator(FileNameBuilder.ArtistNameRegex)).WithMessage("Must contain Artist name");
|
||||
}
|
||||
|
||||
public static IRuleBuilderOptions<T, string> ValidAlbumFolderFormat<T>(this IRuleBuilder<T, string> ruleBuilder)
|
||||
{
|
||||
ruleBuilder.SetValidator(new NotEmptyValidator(null));
|
||||
return ruleBuilder.SetValidator(new RegularExpressionValidator(FileNameBuilder.AlbumTitleRegex)).WithMessage("Must contain Album name");
|
||||
}
|
||||
}
|
||||
|
||||
public class ValidStandardEpisodeFormatValidator : PropertyValidator
|
||||
|
@ -65,6 +82,21 @@ namespace NzbDrone.Core.Organizer
|
|||
}
|
||||
}
|
||||
|
||||
public class ValidStandardTrackFormatValidator : PropertyValidator
|
||||
{
|
||||
public ValidStandardTrackFormatValidator()
|
||||
: base("Must contain Album Title and Track numbers OR Original Title")
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
protected override bool IsValid(PropertyValidatorContext context)
|
||||
{
|
||||
|
||||
return true; //TODO Add Logic here
|
||||
}
|
||||
}
|
||||
|
||||
public class ValidDailyEpisodeFormatValidator : PropertyValidator
|
||||
{
|
||||
public ValidDailyEpisodeFormatValidator()
|
||||
|
|
|
@ -9,6 +9,7 @@ namespace NzbDrone.Core.Organizer
|
|||
public interface IFilenameValidationService
|
||||
{
|
||||
ValidationFailure ValidateStandardFilename(SampleResult sampleResult);
|
||||
ValidationFailure ValidateTrackFilename(SampleResult sampleResult);
|
||||
ValidationFailure ValidateDailyFilename(SampleResult sampleResult);
|
||||
ValidationFailure ValidateAnimeFilename(SampleResult sampleResult);
|
||||
}
|
||||
|
@ -35,6 +36,27 @@ namespace NzbDrone.Core.Organizer
|
|||
return null;
|
||||
}
|
||||
|
||||
public ValidationFailure ValidateTrackFilename(SampleResult sampleResult)
|
||||
{
|
||||
var validationFailure = new ValidationFailure("StandardTrackFormat", ERROR_MESSAGE);
|
||||
|
||||
//TODO Add Validation for TrackFilename
|
||||
//var parsedEpisodeInfo = Parser.Parser.ParseTitle(sampleResult.FileName);
|
||||
|
||||
|
||||
//if (parsedEpisodeInfo == null)
|
||||
//{
|
||||
// return validationFailure;
|
||||
//}
|
||||
|
||||
//if (!ValidateSeasonAndEpisodeNumbers(sampleResult.Episodes, parsedEpisodeInfo))
|
||||
//{
|
||||
// return validationFailure;
|
||||
//}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public ValidationFailure ValidateDailyFilename(SampleResult sampleResult)
|
||||
{
|
||||
var validationFailure = new ValidationFailure("DailyEpisodeFormat", ERROR_MESSAGE);
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
using NzbDrone.Core.Datastore;
|
||||
using NzbDrone.Core.Datastore;
|
||||
|
||||
namespace NzbDrone.Core.Organizer
|
||||
{
|
||||
|
@ -7,21 +7,25 @@ namespace NzbDrone.Core.Organizer
|
|||
public static NamingConfig Default => new NamingConfig
|
||||
{
|
||||
RenameEpisodes = false,
|
||||
RenameTracks = false,
|
||||
ReplaceIllegalCharacters = true,
|
||||
MultiEpisodeStyle = 0,
|
||||
StandardEpisodeFormat = "{Series Title} - S{season:00}E{episode:00} - {Episode Title} {Quality Full}",
|
||||
StandardTrackFormat = "{Artist Name} - {track:00} - {Album Title} - {Track Title}",
|
||||
DailyEpisodeFormat = "{Series Title} - {Air-Date} - {Episode Title} {Quality Full}",
|
||||
AnimeEpisodeFormat = "{Series Title} - S{season:00}E{episode:00} - {Episode Title} {Quality Full}",
|
||||
SeriesFolderFormat = "{Series Title}",
|
||||
SeasonFolderFormat = "Season {season}",
|
||||
ArtistFolderFormat = "{Artist Name}",
|
||||
AlbumFolderFormat = "{Album Name} ({Year})"
|
||||
AlbumFolderFormat = "{Album Title} ({Release Year})"
|
||||
};
|
||||
|
||||
public bool RenameEpisodes { get; set; }
|
||||
public bool RenameTracks { get; set; }
|
||||
public bool ReplaceIllegalCharacters { get; set; }
|
||||
public int MultiEpisodeStyle { get; set; }
|
||||
public string StandardEpisodeFormat { get; set; }
|
||||
public string StandardTrackFormat { get; set; }
|
||||
public string DailyEpisodeFormat { get; set; }
|
||||
public string AnimeEpisodeFormat { get; set; }
|
||||
public string SeriesFolderFormat { get; set; }
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
using System.Collections.Generic;
|
||||
using NzbDrone.Core.MediaFiles;
|
||||
using NzbDrone.Core.Tv;
|
||||
using NzbDrone.Core.Music;
|
||||
|
||||
namespace NzbDrone.Core.Organizer
|
||||
{
|
||||
|
@ -8,7 +9,11 @@ namespace NzbDrone.Core.Organizer
|
|||
{
|
||||
public string FileName { get; set; }
|
||||
public Series Series { get; set; }
|
||||
public Artist Artist { get; set; }
|
||||
public Album Album { get; set; }
|
||||
public List<Episode> Episodes { get; set; }
|
||||
public EpisodeFile EpisodeFile { get; set; }
|
||||
public List<Track> Tracks { get; set; }
|
||||
public TrackFile TrackFile { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,26 +10,20 @@ module.exports = (function() {
|
|||
template : 'Settings/MediaManagement/Naming/NamingViewTemplate',
|
||||
ui : {
|
||||
namingOptions : '.x-naming-options',
|
||||
renameEpisodesCheckbox : '.x-rename-episodes',
|
||||
singleEpisodeExample : '.x-single-episode-example',
|
||||
multiEpisodeExample : '.x-multi-episode-example',
|
||||
dailyEpisodeExample : '.x-daily-episode-example',
|
||||
animeEpisodeExample : '.x-anime-episode-example',
|
||||
animeMultiEpisodeExample : '.x-anime-multi-episode-example',
|
||||
renameTracksCheckbox : '.x-rename-tracks',
|
||||
singleTrackExample : '.x-single-track-example',
|
||||
namingTokenHelper : '.x-naming-token-helper',
|
||||
multiEpisodeStyle : '.x-multi-episode-style',
|
||||
seriesFolderExample : '.x-series-folder-example',
|
||||
seasonFolderExample : '.x-season-folder-example'
|
||||
artistFolderExample : '.x-artist-folder-example',
|
||||
albumFolderExample : '.x-album-folder-example'
|
||||
},
|
||||
events : {
|
||||
"change .x-rename-episodes" : '_setFailedDownloadOptionsVisibility',
|
||||
"change .x-rename-tracks" : '_setFailedDownloadOptionsVisibility',
|
||||
"click .x-show-wizard" : '_showWizard',
|
||||
"click .x-naming-token-helper a" : '_addToken',
|
||||
"change .x-multi-episode-style" : '_multiEpisodeFomatChanged'
|
||||
"click .x-naming-token-helper a" : '_addToken'
|
||||
},
|
||||
regions : { basicNamingRegion : '.x-basic-naming' },
|
||||
onRender : function() {
|
||||
if (!this.model.get('renameEpisodes')) {
|
||||
if (!this.model.get('renameTracks')) {
|
||||
this.ui.namingOptions.hide();
|
||||
}
|
||||
var basicNamingView = new BasicNamingView({ model : this.model });
|
||||
|
@ -40,7 +34,7 @@ module.exports = (function() {
|
|||
this._updateSamples();
|
||||
},
|
||||
_setFailedDownloadOptionsVisibility : function() {
|
||||
var checked = this.ui.renameEpisodesCheckbox.prop('checked');
|
||||
var checked = this.ui.renameTracksCheckbox.prop('checked');
|
||||
if (checked) {
|
||||
this.ui.namingOptions.slideDown();
|
||||
} else {
|
||||
|
@ -51,13 +45,9 @@ module.exports = (function() {
|
|||
this.namingSampleModel.fetch({ data : this.model.toJSON() });
|
||||
},
|
||||
_showSamples : function() {
|
||||
this.ui.singleEpisodeExample.html(this.namingSampleModel.get('singleEpisodeExample'));
|
||||
this.ui.multiEpisodeExample.html(this.namingSampleModel.get('multiEpisodeExample'));
|
||||
this.ui.dailyEpisodeExample.html(this.namingSampleModel.get('dailyEpisodeExample'));
|
||||
this.ui.animeEpisodeExample.html(this.namingSampleModel.get('animeEpisodeExample'));
|
||||
this.ui.animeMultiEpisodeExample.html(this.namingSampleModel.get('animeMultiEpisodeExample'));
|
||||
this.ui.seriesFolderExample.html(this.namingSampleModel.get('seriesFolderExample'));
|
||||
this.ui.seasonFolderExample.html(this.namingSampleModel.get('seasonFolderExample'));
|
||||
this.ui.singleTrackExample.html(this.namingSampleModel.get('singleTrackExample'));
|
||||
this.ui.artistFolderExample.html(this.namingSampleModel.get('artistFolderExample'));
|
||||
this.ui.albumFolderExample.html(this.namingSampleModel.get('albumFolderExample'));
|
||||
},
|
||||
_addToken : function(e) {
|
||||
e.preventDefault();
|
||||
|
@ -75,9 +65,6 @@ module.exports = (function() {
|
|||
this.ui.namingTokenHelper.removeClass('open');
|
||||
input.focus();
|
||||
},
|
||||
multiEpisodeFormatChanged : function() {
|
||||
this.model.set('multiEpisodeStyle', this.ui.multiEpisodeStyle.val());
|
||||
}
|
||||
});
|
||||
AsModelBoundView.call(view);
|
||||
AsValidatedView.call(view);
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
<fieldset>
|
||||
<legend>Episode Naming</legend>
|
||||
<legend>Track Naming</legend>
|
||||
|
||||
<div class="form-group">
|
||||
<label class="col-sm-3 control-label">Rename Episodes</label>
|
||||
<label class="col-sm-3 control-label">Rename Tracks</label>
|
||||
|
||||
<div class="col-sm-8">
|
||||
<div class="input-group">
|
||||
<label class="checkbox toggle well">
|
||||
<input type="checkbox" name="renameEpisodes" class="x-rename-episodes"/>
|
||||
<input type="checkbox" name="renameTracks" class="x-rename-tracks"/>
|
||||
|
||||
<p>
|
||||
<span>Yes</span>
|
||||
|
@ -51,7 +51,7 @@
|
|||
<div class="basic-setting x-basic-naming"></div>
|
||||
|
||||
<div class="form-group advanced-setting">
|
||||
<label class="col-sm-3 control-label">Standard Episode Format</label>
|
||||
<label class="col-sm-3 control-label">Standard Track Format</label>
|
||||
|
||||
<div class="col-sm-1 col-sm-push-8 help-inline">
|
||||
<i class="icon-lidarr-form-info" title="" data-original-title="All caps or all lower-case can also be used"></i>
|
||||
|
@ -60,16 +60,17 @@
|
|||
|
||||
<div class="col-sm-8 col-sm-pull-1">
|
||||
<div class="input-group x-helper-input">
|
||||
<input type="text" class="form-control naming-format" name="standardEpisodeFormat" data-onkeyup="true" />
|
||||
<input type="text" class="form-control naming-format" name="standardTrackFormat" data-onkeyup="true" />
|
||||
<div class="input-group-btn btn-group x-naming-token-helper">
|
||||
<button class="btn btn-icon-only dropdown-toggle" data-toggle="dropdown">
|
||||
<i class="icon-lidarr-add"></i>
|
||||
</button>
|
||||
<ul class="dropdown-menu">
|
||||
{{> SeriesTitleNamingPartial}}
|
||||
{{> SeasonNamingPartial}}
|
||||
{{> EpisodeNamingPartial}}
|
||||
{{> EpisodeTitleNamingPartial}}
|
||||
{{> ArtistNameNamingPartial}}
|
||||
{{> AlbumTitleNamingPartial}}
|
||||
{{> ReleaseYearNamingPartial}}
|
||||
{{> TrackNumNamingPartial}}
|
||||
{{> TrackTitleNamingPartial}}
|
||||
{{> QualityNamingPartial}}
|
||||
{{> MediaInfoNamingPartial}}
|
||||
{{> ReleaseGroupNamingPartial}}
|
||||
|
@ -81,87 +82,24 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group advanced-setting">
|
||||
<label class="col-sm-3 control-label">Daily Episode Format</label>
|
||||
|
||||
<div class="col-sm-1 col-sm-push-8 help-inline">
|
||||
<i class="icon-lidarr-form-info" title="" data-original-title="All caps or all lower-case can also be used"></i>
|
||||
<a href="https://github.com/NzbDrone/NzbDrone/wiki/Sorting-and-Renaming" class="help-link" title="More information"><i class="icon-lidarr-form-info-link"/></a>
|
||||
</div>
|
||||
|
||||
<div class="col-sm-8 col-sm-pull-1">
|
||||
<div class="input-group x-helper-input">
|
||||
<input type="text" class="form-control naming-format" name="dailyEpisodeFormat" data-onkeyup="true" />
|
||||
<div class="input-group-btn btn-group x-naming-token-helper">
|
||||
<button class="btn btn-icon-only dropdown-toggle" data-toggle="dropdown">
|
||||
<i class="icon-lidarr-add"></i>
|
||||
</button>
|
||||
<ul class="dropdown-menu">
|
||||
{{> SeriesTitleNamingPartial}}
|
||||
{{> AirDateNamingPartial}}
|
||||
{{> SeasonNamingPartial}}
|
||||
{{> EpisodeNamingPartial}}
|
||||
{{> EpisodeTitleNamingPartial}}
|
||||
{{> QualityNamingPartial}}
|
||||
{{> MediaInfoNamingPartial}}
|
||||
{{> ReleaseGroupNamingPartial}}
|
||||
{{> OriginalTitleNamingPartial}}
|
||||
{{> SeparatorNamingPartial}}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group advanced-setting">
|
||||
<label class="col-sm-3 control-label">Anime Episode Format</label>
|
||||
|
||||
<div class="col-sm-1 col-sm-push-8 help-inline">
|
||||
<i class="icon-lidarr-form-info" title="" data-original-title="All caps or all lower-case can also be used"></i>
|
||||
<a href="https://github.com/NzbDrone/NzbDrone/wiki/Sorting-and-Renaming" class="help-link" title="More information"><i class="icon-lidarr-form-info-link"/></a>
|
||||
</div>
|
||||
|
||||
<div class="col-sm-8 col-sm-pull-1">
|
||||
<div class="input-group x-helper-input">
|
||||
<input type="text" class="form-control naming-format" name="animeEpisodeFormat" data-onkeyup="true" />
|
||||
<div class="input-group-btn btn-group x-naming-token-helper">
|
||||
<button class="btn btn-icon-only dropdown-toggle" data-toggle="dropdown">
|
||||
<i class="icon-lidarr-add"></i>
|
||||
</button>
|
||||
<ul class="dropdown-menu">
|
||||
{{> SeriesTitleNamingPartial}}
|
||||
{{> AbsoluteEpisodeNamingPartial}}
|
||||
{{> SeasonNamingPartial}}
|
||||
{{> EpisodeNamingPartial}}
|
||||
{{> EpisodeTitleNamingPartial}}
|
||||
{{> QualityNamingPartial}}
|
||||
{{> MediaInfoNamingPartial}}
|
||||
{{> ReleaseGroupNamingPartial}}
|
||||
{{> OriginalTitleNamingPartial}}
|
||||
{{> SeparatorNamingPartial}}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group advanced-setting">
|
||||
<label class="col-sm-3 control-label">Series Folder Format</label>
|
||||
<label class="col-sm-3 control-label">Artist Folder Format</label>
|
||||
|
||||
<div class="col-sm-1 col-sm-push-8 help-inline">
|
||||
<i class="icon-lidarr-form-info" title="" data-original-title="All caps or all lower-case can also be used. Only used when adding a new series."></i>
|
||||
<i class="icon-lidarr-form-info" title="" data-original-title="All caps or all lower-case can also be used. Only used when adding a new artist."></i>
|
||||
</div>
|
||||
|
||||
<div class="col-sm-8 col-sm-pull-1">
|
||||
<div class="input-group x-helper-input">
|
||||
<input type="text" class="form-control naming-format" name="seriesFolderFormat" data-onkeyup="true"/>
|
||||
<input type="text" class="form-control naming-format" name="artistFolderFormat" data-onkeyup="true"/>
|
||||
<div class="input-group-btn btn-group x-naming-token-helper">
|
||||
<button class="btn btn-icon-only dropdown-toggle" data-toggle="dropdown">
|
||||
<i class="icon-lidarr-add"></i>
|
||||
</button>
|
||||
<ul class="dropdown-menu">
|
||||
{{> SeriesTitleNamingPartial}}
|
||||
{{> ArtistNameNamingPartial}}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -169,18 +107,19 @@
|
|||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label class="col-sm-3 control-label">Season Folder Format</label>
|
||||
<label class="col-sm-3 control-label">Album Folder Format</label>
|
||||
|
||||
<div class="col-sm-8">
|
||||
<div class="input-group x-helper-input">
|
||||
<input type="text" class="form-control naming-format" name="seasonFolderFormat" data-onkeyup="true"/>
|
||||
<input type="text" class="form-control naming-format" name="albumFolderFormat" data-onkeyup="true"/>
|
||||
<div class="input-group-btn btn-group x-naming-token-helper">
|
||||
<button class="btn btn-icon-only dropdown-toggle" data-toggle="dropdown">
|
||||
<i class="icon-lidarr-add"></i>
|
||||
</button>
|
||||
<ul class="dropdown-menu">
|
||||
{{> SeriesTitleNamingPartial}}
|
||||
{{> SeasonNamingPartial}}
|
||||
{{> ArtistNameNamingPartial}}
|
||||
{{> AlbumTitleNamingPartial}}
|
||||
{{> ReleaseYearNamingPartial}}
|
||||
{{> SeparatorNamingPartial}}
|
||||
</ul>
|
||||
</div>
|
||||
|
@ -188,75 +127,27 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<div class="x-naming-options">
|
||||
<div class="form-group">
|
||||
<label class="col-sm-3 control-label">Multi-Episode Style</label>
|
||||
<div class="form-group">
|
||||
<label class="col-sm-3 control-label">Single Track Example</label>
|
||||
|
||||
<div class="col-sm-2">
|
||||
<select class="form-control x-multi-episode-style" name="multiEpisodeStyle">
|
||||
<option value="0">Extend</option>
|
||||
<option value="1">Duplicate</option>
|
||||
<option value="2">Repeat</option>
|
||||
<option value="3">Scene</option>
|
||||
<option value="4">Range</option>
|
||||
<option value="5">Prefixed Range</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-sm-8">
|
||||
<p class="form-control-static x-single-track-example naming-example"></p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label class="col-sm-3 control-label">Single Episode Example</label>
|
||||
<label class="col-sm-3 control-label">Artist Folder Example</label>
|
||||
|
||||
<div class="col-sm-8">
|
||||
<p class="form-control-static x-single-episode-example naming-example"></p>
|
||||
<p class="form-control-static x-artist-folder-example naming-example"></p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label class="col-sm-3 control-label">Multi-Episode Example</label>
|
||||
<label class="col-sm-3 control-label">Album Folder Example</label>
|
||||
|
||||
<div class="col-sm-8">
|
||||
<p class="form-control-static x-multi-episode-example naming-example"></p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label class="col-sm-3 control-label">Daily-Episode Example</label>
|
||||
|
||||
<div class="col-sm-8">
|
||||
<p class="form-control-static x-daily-episode-example naming-example"></p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label class="col-sm-3 control-label">Anime Episode Example</label>
|
||||
<div class="col-sm-8">
|
||||
<p class="form-control-static x-anime-episode-example naming-example"></p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label class="col-sm-3 control-label">Anime Multi-Episode Example</label>
|
||||
|
||||
<div class="col-sm-8">
|
||||
<p class="form-control-static x-anime-multi-episode-example naming-example"></p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label class="col-sm-3 control-label">Series Folder Example</label>
|
||||
|
||||
<div class="col-sm-8">
|
||||
<p class="form-control-static x-series-folder-example naming-example"></p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label class="col-sm-3 control-label">Season Folder Example</label>
|
||||
|
||||
<div class="col-sm-8">
|
||||
<p class="form-control-static x-season-folder-example naming-example"></p>
|
||||
<p class="form-control-static x-album-folder-example naming-example"></p>
|
||||
</div>
|
||||
</div>
|
||||
</fieldset>
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
<li class="dropdown-submenu">
|
||||
<a href="#" tabindex="-1" data-token="Album Title">Album Title</a>
|
||||
<ul class="dropdown-menu">
|
||||
<li><a href="#" data-token="Album Title">Album Title</a></li>
|
||||
<li><a href="#" data-token="Album.Title">Album.Title</a></li>
|
||||
<li><a href="#" data-token="Album_Title">Album_Title</a></li>
|
||||
<li><a href="#" data-token="Album CleanTitle">Album CleanTitle</a></li>
|
||||
<li><a href="#" data-token="Album.CleanTitle">Album.CleanTitle</a></li>
|
||||
<li><a href="#" data-token="Album_CleanTitle">Album_CleanTitle</a></li>
|
||||
</ul>
|
||||
</li>
|
|
@ -0,0 +1,11 @@
|
|||
<li class="dropdown-submenu">
|
||||
<a href="#" tabindex="-1" data-token="Artist Name">Artist Name</a>
|
||||
<ul class="dropdown-menu">
|
||||
<li><a href="#" data-token="Artist Name">Artist Name</a></li>
|
||||
<li><a href="#" data-token="Artist.Name">Artist.Name</a></li>
|
||||
<li><a href="#" data-token="Artist_Name">Artist_Name</a></li>
|
||||
<li><a href="#" data-token="Artist CleanName">Artist CleanName</a></li>
|
||||
<li><a href="#" data-token="Artist.CleanName">Artist.CleanName</a></li>
|
||||
<li><a href="#" data-token="Artist_CleanName">Artist_CleanName</a></li>
|
||||
</ul>
|
||||
</li>
|
|
@ -0,0 +1 @@
|
|||
<li><a href="#" data-token="Release Year">Release Year</a></li>
|
|
@ -0,0 +1,7 @@
|
|||
<li class="dropdown-submenu">
|
||||
<a href="#" tabindex="-1" data-token="track">Track</a>
|
||||
<ul class="dropdown-menu">
|
||||
<li><a href="#" data-token="track">1</a></li>
|
||||
<li><a href="#" data-token="track:00">01</a></li>
|
||||
</ul>
|
||||
</li>
|
|
@ -0,0 +1,11 @@
|
|||
<li class="dropdown-submenu">
|
||||
<a href="#" tabindex="-1" data-token="Track Title">Track Title</a>
|
||||
<ul class="dropdown-menu">
|
||||
<li><a href="#" data-token="Track Title">Track Title</a></li>
|
||||
<li><a href="#" data-token="Track.Title">Track.Title</a></li>
|
||||
<li><a href="#" data-token="Track_Title">Track_Title</a></li>
|
||||
<li><a href="#" data-token="Track CleanTitle">Track CleanTitle</a></li>
|
||||
<li><a href="#" data-token="Track.CleanTitle">Track.CleanTitle</a></li>
|
||||
<li><a href="#" data-token="Track_CleanTitle">Track_CleanTitle</a></li>
|
||||
</ul>
|
||||
</li>
|
Loading…
Reference in New Issue