|
|
@ -24,7 +24,7 @@ namespace NzbDrone.Core.Organizer
|
|
|
|
BasicNamingConfig GetBasicNamingConfig(NamingConfig nameSpec);
|
|
|
|
BasicNamingConfig GetBasicNamingConfig(NamingConfig nameSpec);
|
|
|
|
string GetSeriesFolder(Series series, NamingConfig namingConfig = null);
|
|
|
|
string GetSeriesFolder(Series series, NamingConfig namingConfig = null);
|
|
|
|
string GetSeasonFolder(Series series, int seasonNumber, NamingConfig namingConfig = null);
|
|
|
|
string GetSeasonFolder(Series series, int seasonNumber, NamingConfig namingConfig = null);
|
|
|
|
string GetMovieFolder(Movie movie);
|
|
|
|
string GetMovieFolder(Movie movie, NamingConfig namingConfig = null);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public class FileNameBuilder : IBuildFileNames
|
|
|
|
public class FileNameBuilder : IBuildFileNames
|
|
|
@ -79,6 +79,7 @@ namespace NzbDrone.Core.Organizer
|
|
|
|
{
|
|
|
|
{
|
|
|
|
_namingConfigService = namingConfigService;
|
|
|
|
_namingConfigService = namingConfigService;
|
|
|
|
_qualityDefinitionService = qualityDefinitionService;
|
|
|
|
_qualityDefinitionService = qualityDefinitionService;
|
|
|
|
|
|
|
|
//_movieFormatCache = cacheManager.GetCache<MovieFormat>(GetType(), "movieFormat");
|
|
|
|
_episodeFormatCache = cacheManager.GetCache<EpisodeFormat[]>(GetType(), "episodeFormat");
|
|
|
|
_episodeFormatCache = cacheManager.GetCache<EpisodeFormat[]>(GetType(), "episodeFormat");
|
|
|
|
_absoluteEpisodeFormatCache = cacheManager.GetCache<AbsoluteEpisodeFormat[]>(GetType(), "absoluteEpisodeFormat");
|
|
|
|
_absoluteEpisodeFormatCache = cacheManager.GetCache<AbsoluteEpisodeFormat[]>(GetType(), "absoluteEpisodeFormat");
|
|
|
|
_logger = logger;
|
|
|
|
_logger = logger;
|
|
|
@ -154,52 +155,20 @@ namespace NzbDrone.Core.Organizer
|
|
|
|
return GetOriginalTitle(movieFile);
|
|
|
|
return GetOriginalTitle(movieFile);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*if (namingConfig.StandardEpisodeFormat.IsNullOrWhiteSpace() && series.SeriesType == SeriesTypes.Standard)
|
|
|
|
//TODO: Update namingConfig for Movies!
|
|
|
|
{
|
|
|
|
var pattern = namingConfig.StandardMovieFormat;
|
|
|
|
throw new NamingFormatException("Standard episode format cannot be empty");
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (namingConfig.DailyEpisodeFormat.IsNullOrWhiteSpace() && series.SeriesType == SeriesTypes.Daily)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
throw new NamingFormatException("Daily episode format cannot be empty");
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (namingConfig.AnimeEpisodeFormat.IsNullOrWhiteSpace() && series.SeriesType == SeriesTypes.Anime)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
throw new NamingFormatException("Anime episode format cannot be empty");
|
|
|
|
|
|
|
|
}*/
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*var pattern = namingConfig.StandardEpisodeFormat;
|
|
|
|
|
|
|
|
var tokenHandlers = new Dictionary<string, Func<TokenMatch, string>>(FileNameBuilderTokenEqualityComparer.Instance);
|
|
|
|
var tokenHandlers = new Dictionary<string, Func<TokenMatch, string>>(FileNameBuilderTokenEqualityComparer.Instance);
|
|
|
|
|
|
|
|
|
|
|
|
episodes = episodes.OrderBy(e => e.SeasonNumber).ThenBy(e => e.EpisodeNumber).ToList();
|
|
|
|
AddMovieTokens(tokenHandlers, movie);
|
|
|
|
|
|
|
|
//AddReleaseDateTokens(tokenHandlers, movie.Year); //In case we want to separate the year
|
|
|
|
if (series.SeriesType == SeriesTypes.Daily)
|
|
|
|
AddQualityTokens(tokenHandlers, movie, movieFile);
|
|
|
|
{
|
|
|
|
AddMediaInfoTokens(tokenHandlers, movieFile);
|
|
|
|
pattern = namingConfig.DailyEpisodeFormat;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (series.SeriesType == SeriesTypes.Anime && episodes.All(e => e.AbsoluteEpisodeNumber.HasValue))
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
pattern = namingConfig.AnimeEpisodeFormat;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
pattern = AddSeasonEpisodeNumberingTokens(pattern, tokenHandlers, episodes, namingConfig);
|
|
|
|
|
|
|
|
pattern = AddAbsoluteNumberingTokens(pattern, tokenHandlers, series, episodes, namingConfig);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
AddSeriesTokens(tokenHandlers, series);
|
|
|
|
|
|
|
|
AddEpisodeTokens(tokenHandlers, episodes);
|
|
|
|
|
|
|
|
AddEpisodeFileTokens(tokenHandlers, episodeFile);
|
|
|
|
|
|
|
|
AddQualityTokens(tokenHandlers, series, episodeFile);
|
|
|
|
|
|
|
|
AddMediaInfoTokens(tokenHandlers, episodeFile);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
var fileName = ReplaceTokens(pattern, tokenHandlers, namingConfig).Trim();
|
|
|
|
var fileName = ReplaceTokens(pattern, 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);
|
|
|
|
|
|
|
|
|
|
|
|
//TODO: Update namingConfig for Movies!
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return GetOriginalTitle(movieFile);
|
|
|
|
return fileName;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public string BuildFilePath(Series series, int seasonNumber, string fileName, string extension)
|
|
|
|
public string BuildFilePath(Series series, int seasonNumber, string fileName, string extension)
|
|
|
@ -318,9 +287,18 @@ namespace NzbDrone.Core.Organizer
|
|
|
|
return CleanFolderName(ReplaceTokens(namingConfig.SeasonFolderFormat, tokenHandlers, namingConfig));
|
|
|
|
return CleanFolderName(ReplaceTokens(namingConfig.SeasonFolderFormat, tokenHandlers, namingConfig));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public string GetMovieFolder(Movie movie)
|
|
|
|
public string GetMovieFolder(Movie movie, NamingConfig namingConfig = null)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
return CleanFolderName(movie.Title);
|
|
|
|
if(namingConfig == null)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
namingConfig = _namingConfigService.GetConfig();
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
var tokenHandlers = new Dictionary<string, Func<TokenMatch, string>>(FileNameBuilderTokenEqualityComparer.Instance);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
AddMovieTokens(tokenHandlers, movie);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return CleanFolderName(ReplaceTokens(namingConfig.MovieFolderFormat, tokenHandlers, namingConfig));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public static string CleanTitle(string title)
|
|
|
|
public static string CleanTitle(string title)
|
|
|
@ -484,6 +462,17 @@ namespace NzbDrone.Core.Organizer
|
|
|
|
return pattern;
|
|
|
|
return pattern;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private void AddMovieTokens(Dictionary<string, Func<TokenMatch, string>> tokenHandlers, Movie movie)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
tokenHandlers["{Movie Title}"] = m => movie.Title;
|
|
|
|
|
|
|
|
tokenHandlers["{Movie CleanTitle}"] = m => CleanTitle(movie.Title);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private void AddReleaseDateTokens(Dictionary<string, Func<TokenMatch, string>> tokenHandlers, int releaseYear)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
tokenHandlers["{Release Year}"] = m => string.Format("({0})", releaseYear.ToString()); //Do I need m.CustomFormat?
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private void AddSeasonTokens(Dictionary<string, Func<TokenMatch, string>> tokenHandlers, int seasonNumber)
|
|
|
|
private void AddSeasonTokens(Dictionary<string, Func<TokenMatch, string>> tokenHandlers, int seasonNumber)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
tokenHandlers["{Season}"] = m => seasonNumber.ToString(m.CustomFormat);
|
|
|
|
tokenHandlers["{Season}"] = m => seasonNumber.ToString(m.CustomFormat);
|
|
|
@ -523,6 +512,18 @@ namespace NzbDrone.Core.Organizer
|
|
|
|
tokenHandlers["{Quality Real}"] = m => qualityReal;
|
|
|
|
tokenHandlers["{Quality Real}"] = m => qualityReal;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private void AddQualityTokens(Dictionary<string, Func<TokenMatch, string>> tokenHandlers, Movie movie, MovieFile movieFile)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
var qualityTitle = _qualityDefinitionService.Get(movieFile.Quality.Quality).Title;
|
|
|
|
|
|
|
|
var qualityProper = GetQualityProper(movie, movieFile.Quality);
|
|
|
|
|
|
|
|
var qualityReal = GetQualityReal(movie, movieFile.Quality);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
tokenHandlers["{Quality Full}"] = m => String.Format("{0} {1} {2}", qualityTitle, qualityProper, qualityReal);
|
|
|
|
|
|
|
|
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)
|
|
|
|
private void AddMediaInfoTokens(Dictionary<string, Func<TokenMatch, string>> tokenHandlers, EpisodeFile episodeFile)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
if (episodeFile.MediaInfo == null) return;
|
|
|
|
if (episodeFile.MediaInfo == null) return;
|
|
|
@ -627,6 +628,110 @@ namespace NzbDrone.Core.Organizer
|
|
|
|
tokenHandlers["{MediaInfo Full}"] = m => string.Format("{0} {1}{2} {3}", videoCodec, audioCodec, mediaInfoAudioLanguages, mediaInfoSubtitleLanguages);
|
|
|
|
tokenHandlers["{MediaInfo Full}"] = m => string.Format("{0} {1}{2} {3}", videoCodec, audioCodec, mediaInfoAudioLanguages, mediaInfoSubtitleLanguages);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private void AddMediaInfoTokens(Dictionary<string, Func<TokenMatch, string>> tokenHandlers, MovieFile movieFile)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
if (movieFile.MediaInfo == null) return;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
string videoCodec;
|
|
|
|
|
|
|
|
switch (movieFile.MediaInfo.VideoCodec)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
case "AVC":
|
|
|
|
|
|
|
|
if (movieFile.SceneName.IsNotNullOrWhiteSpace() && Path.GetFileNameWithoutExtension(movieFile.SceneName).Contains("h264"))
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
videoCodec = "h264";
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
else
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
videoCodec = "x264";
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
case "V_MPEGH/ISO/HEVC":
|
|
|
|
|
|
|
|
if (movieFile.SceneName.IsNotNullOrWhiteSpace() && Path.GetFileNameWithoutExtension(movieFile.SceneName).Contains("h265"))
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
videoCodec = "h265";
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
else
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
videoCodec = "x265";
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
case "MPEG-2 Video":
|
|
|
|
|
|
|
|
videoCodec = "MPEG2";
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
|
|
|
videoCodec = movieFile.MediaInfo.VideoCodec;
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
string audioCodec;
|
|
|
|
|
|
|
|
switch (movieFile.MediaInfo.AudioFormat)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
case "AC-3":
|
|
|
|
|
|
|
|
audioCodec = "AC3";
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
case "E-AC-3":
|
|
|
|
|
|
|
|
audioCodec = "EAC3";
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
case "MPEG Audio":
|
|
|
|
|
|
|
|
if (movieFile.MediaInfo.AudioProfile == "Layer 3")
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
audioCodec = "MP3";
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
else
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
audioCodec = movieFile.MediaInfo.AudioFormat;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
case "DTS":
|
|
|
|
|
|
|
|
audioCodec = movieFile.MediaInfo.AudioFormat;
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
|
|
|
audioCodec = movieFile.MediaInfo.AudioFormat;
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
var mediaInfoAudioLanguages = GetLanguagesToken(movieFile.MediaInfo.AudioLanguages);
|
|
|
|
|
|
|
|
if (!mediaInfoAudioLanguages.IsNullOrWhiteSpace())
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
mediaInfoAudioLanguages = string.Format("[{0}]", mediaInfoAudioLanguages);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (mediaInfoAudioLanguages == "[EN]")
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
mediaInfoAudioLanguages = string.Empty;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
var mediaInfoSubtitleLanguages = GetLanguagesToken(movieFile.MediaInfo.Subtitles);
|
|
|
|
|
|
|
|
if (!mediaInfoSubtitleLanguages.IsNullOrWhiteSpace())
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
mediaInfoSubtitleLanguages = string.Format("[{0}]", mediaInfoSubtitleLanguages);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
var videoBitDepth = movieFile.MediaInfo.VideoBitDepth > 0 ? movieFile.MediaInfo.VideoBitDepth.ToString() : string.Empty;
|
|
|
|
|
|
|
|
var audioChannels = movieFile.MediaInfo.FormattedAudioChannels > 0 ?
|
|
|
|
|
|
|
|
movieFile.MediaInfo.FormattedAudioChannels.ToString("F1", CultureInfo.InvariantCulture) :
|
|
|
|
|
|
|
|
string.Empty;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
tokenHandlers["{MediaInfo Video}"] = m => videoCodec;
|
|
|
|
|
|
|
|
tokenHandlers["{MediaInfo VideoCodec}"] = m => videoCodec;
|
|
|
|
|
|
|
|
tokenHandlers["{MediaInfo VideoBitDepth}"] = m => videoBitDepth;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
tokenHandlers["{MediaInfo Audio}"] = m => audioCodec;
|
|
|
|
|
|
|
|
tokenHandlers["{MediaInfo AudioCodec}"] = m => audioCodec;
|
|
|
|
|
|
|
|
tokenHandlers["{MediaInfo AudioChannels}"] = m => audioChannels;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
tokenHandlers["{MediaInfo Simple}"] = m => string.Format("{0} {1}", videoCodec, audioCodec);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
tokenHandlers["{MediaInfo Full}"] = m => string.Format("{0} {1}{2} {3}", videoCodec, audioCodec, mediaInfoAudioLanguages, mediaInfoSubtitleLanguages);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private string GetLanguagesToken(string mediaInfoLanguages)
|
|
|
|
private string GetLanguagesToken(string mediaInfoLanguages)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
List<string> tokens = new List<string>();
|
|
|
|
List<string> tokens = new List<string>();
|
|
|
@ -806,6 +911,16 @@ namespace NzbDrone.Core.Organizer
|
|
|
|
return MultiPartCleanupRegex.Replace(title, string.Empty).Trim();
|
|
|
|
return MultiPartCleanupRegex.Replace(title, string.Empty).Trim();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private string GetQualityProper(Movie movie, QualityModel quality)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
if (quality.Revision.Version > 1)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
return "Proper";
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return String.Empty;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private string GetQualityProper(Series series, QualityModel quality)
|
|
|
|
private string GetQualityProper(Series series, QualityModel quality)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
if (quality.Revision.Version > 1)
|
|
|
|
if (quality.Revision.Version > 1)
|
|
|
@ -831,6 +946,16 @@ namespace NzbDrone.Core.Organizer
|
|
|
|
return string.Empty;
|
|
|
|
return string.Empty;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private string GetQualityReal(Movie movie, QualityModel quality)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
if (quality.Revision.Real > 0)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
return "REAL";
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return string.Empty;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private string GetOriginalTitle(EpisodeFile episodeFile)
|
|
|
|
private string GetOriginalTitle(EpisodeFile episodeFile)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
if (episodeFile.SceneName.IsNullOrWhiteSpace())
|
|
|
|
if (episodeFile.SceneName.IsNullOrWhiteSpace())
|
|
|
|