@ -1,4 +1,4 @@
using System ;
using System ;
using System.Collections.Generic ;
using System.Globalization ;
using System.IO ;
@ -24,7 +24,7 @@ namespace NzbDrone.Core.Organizer
BasicNamingConfig GetBasicNamingConfig ( NamingConfig nameSpec ) ;
string GetSeriesFolder ( Series series , 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
@ -58,6 +58,9 @@ namespace NzbDrone.Core.Organizer
public static readonly Regex SeriesTitleRegex = new Regex ( @"(?<token>\{(?:Series)(?<separator>[- ._])(Clean)?Title\})" ,
RegexOptions . Compiled | RegexOptions . IgnoreCase ) ;
public static readonly Regex MovieTitleRegex = new Regex ( @"(?<token>\{(?:Movie)(?<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 ) ;
@ -76,6 +79,7 @@ namespace NzbDrone.Core.Organizer
{
_namingConfigService = namingConfigService ;
_qualityDefinitionService = qualityDefinitionService ;
//_movieFormatCache = cacheManager.GetCache<MovieFormat>(GetType(), "movieFormat");
_episodeFormatCache = cacheManager . GetCache < EpisodeFormat [ ] > ( GetType ( ) , "episodeFormat" ) ;
_absoluteEpisodeFormatCache = cacheManager . GetCache < AbsoluteEpisodeFormat [ ] > ( GetType ( ) , "absoluteEpisodeFormat" ) ;
_logger = logger ;
@ -151,52 +155,20 @@ namespace NzbDrone.Core.Organizer
return GetOriginalTitle ( movieFile ) ;
}
/ * if ( namingConfig . StandardEpisodeFormat . IsNullOrWhiteSpace ( ) & & series . SeriesType = = SeriesTypes . Standard )
{
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 ;
//TODO: Update namingConfig for Movies!
var pattern = namingConfig . StandardMovieFormat ;
var tokenHandlers = new Dictionary < string , Func < TokenMatch , string > > ( FileNameBuilderTokenEqualityComparer . Instance ) ;
episodes = episodes . OrderBy ( e = > e . SeasonNumber ) . ThenBy ( e = > e . EpisodeNumber ) . ToList ( ) ;
if ( series . SeriesType = = SeriesTypes . Daily )
{
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 ) ;
AddMovieTokens ( tokenHandlers , movie ) ;
AddReleaseDateTokens ( tokenHandlers , movie . Year ) ; //In case we want to separate the year
AddQualityTokens ( tokenHandlers , movie , movieFile ) ;
AddMediaInfoTokens ( tokenHandlers , movieFile ) ;
var fileName = ReplaceTokens ( pattern , tokenHandlers , namingConfig ) . Trim ( ) ;
fileName = FileNameCleanupRegex . Replace ( fileName , match = > match . Captures [ 0 ] . Value [ 0 ] . ToString ( ) ) ;
fileName = TrimSeparatorsRegex . Replace ( fileName , string . Empty ) ; * /
//TODO: Update namingConfig for Movies!
fileName = TrimSeparatorsRegex . Replace ( fileName , string . Empty ) ;
return GetOriginalTitle( movieFile ) ;
return fileName ;
}
public string BuildFilePath ( Series series , int seasonNumber , string fileName , string extension )
@ -242,6 +214,8 @@ namespace NzbDrone.Core.Organizer
public BasicNamingConfig GetBasicNamingConfig ( NamingConfig nameSpec )
{
return new BasicNamingConfig ( ) ; //For now let's be lazy
var episodeFormat = GetEpisodeFormat ( nameSpec . StandardEpisodeFormat ) . LastOrDefault ( ) ;
if ( episodeFormat = = null )
@ -315,9 +289,19 @@ namespace NzbDrone.Core.Organizer
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 ) ;
AddReleaseDateTokens ( tokenHandlers , movie . Year ) ;
return CleanFolderName ( ReplaceTokens ( namingConfig . MovieFolderFormat , tokenHandlers , namingConfig ) ) ;
}
public static string CleanTitle ( string title )
@ -481,6 +465,17 @@ namespace NzbDrone.Core.Organizer
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 )
{
tokenHandlers [ "{Season}" ] = m = > seasonNumber . ToString ( m . CustomFormat ) ;
@ -520,6 +515,18 @@ namespace NzbDrone.Core.Organizer
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 )
{
if ( episodeFile . MediaInfo = = null ) return ;
@ -624,6 +631,110 @@ namespace NzbDrone.Core.Organizer
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 )
{
List < string > tokens = new List < string > ( ) ;
@ -803,6 +914,16 @@ namespace NzbDrone.Core.Organizer
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 )
{
if ( quality . Revision . Version > 1 )
@ -828,6 +949,16 @@ namespace NzbDrone.Core.Organizer
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 )
{
if ( episodeFile . SceneName . IsNullOrWhiteSpace ( ) )