@ -78,197 +78,6 @@ namespace NzbDrone.Core.Parser
RegexOptions . IgnoreCase | RegexOptions . Compiled ) ,
} ;
private static readonly Regex [ ] ReportTitleRegex = new [ ]
{
//Anime - Absolute Episode Number + Title + Season+Episode
//Todo: This currently breaks series that start with numbers
// new Regex(@"^(?:(?<absoluteepisode>\d{2,3})(?:_|-|\s|\.)+)+(?<title>.+?)(?:\W|_)+(?:S?(?<season>(?<!\d+)\d{1,2}(?!\d+))(?:(?:\-|[ex]|\W[ex]){1,2}(?<episode>\d{2}(?!\d+)))+)",
// RegexOptions.IgnoreCase | RegexOptions.Compiled),
//Multi-Part episodes without a title (S01E05.S01E06)
new Regex ( @"^(?:\W*S?(?<season>(?<!\d+)(?:\d{1,2}|\d{4})(?!\d+))(?:(?:[ex]){1,2}(?<episode>\d{1,3}(?!\d+)))+){2,}" ,
RegexOptions . IgnoreCase | RegexOptions . Compiled ) ,
//Episodes without a title, Single (S01E05, 1x05) AND Multi (S01E04E05, 1x04x05, etc)
new Regex ( @"^(?:S?(?<season>(?<!\d+)(?:\d{1,2}|\d{4})(?!\d+))(?:(?:\-|[ex]|\W[ex]|_){1,2}(?<episode>\d{2,3}(?!\d+)))+)" ,
RegexOptions . IgnoreCase | RegexOptions . Compiled ) ,
//Anime - [SubGroup] Title Episode Absolute Episode Number ([SubGroup] Series Title Episode 01)
new Regex ( @"^(?:\[(?<subgroup>.+?)\][-_. ]?)(?<title>.+?)[-_. ](?:Episode)(?:[-_. ]+(?<absoluteepisode>(?<!\d+)\d{2,3}(?!\d+)))+(?:_|-|\s|\.)*?(?<hash>\[.{8}\])?(?:$|\.)?" ,
RegexOptions . IgnoreCase | RegexOptions . Compiled ) ,
//Anime - [SubGroup] Title Absolute Episode Number + Season+Episode
new Regex ( @"^(?:\[(?<subgroup>.+?)\](?:_|-|\s|\.)?)(?<title>.+?)(?:(?:[-_\W](?<![()\[!]))+(?<absoluteepisode>\d{2,3}))+(?:_|-|\s|\.)+(?:S?(?<season>(?<!\d+)\d{1,2}(?!\d+))(?:(?:\-|[ex]|\W[ex]){1,2}(?<episode>\d{2}(?!\d+)))+).*?(?<hash>[(\[]\w{8}[)\]])?(?:$|\.)" ,
RegexOptions . IgnoreCase | RegexOptions . Compiled ) ,
//Anime - [SubGroup] Title Season+Episode + Absolute Episode Number
new Regex ( @"^(?:\[(?<subgroup>.+?)\](?:_|-|\s|\.)?)(?<title>.+?)(?:[-_\W](?<![()\[!]))+(?:S?(?<season>(?<!\d+)\d{1,2}(?!\d+))(?:(?:\-|[ex]|\W[ex]){1,2}(?<episode>\d{2}(?!\d+)))+)(?:(?:_|-|\s|\.)+(?<absoluteepisode>(?<!\d+)\d{2,3}(?!\d+)))+.*?(?<hash>\[\w{8}\])?(?:$|\.)" ,
RegexOptions . IgnoreCase | RegexOptions . Compiled ) ,
//Anime - [SubGroup] Title Season+Episode
new Regex ( @"^(?:\[(?<subgroup>.+?)\](?:_|-|\s|\.)?)(?<title>.+?)(?:[-_\W](?<![()\[!]))+(?:S?(?<season>(?<!\d+)\d{1,2}(?!\d+))(?:(?:[ex]|\W[ex]){1,2}(?<episode>\d{2}(?!\d+)))+)(?:\s|\.).*?(?<hash>\[\w{8}\])?(?:$|\.)" ,
RegexOptions . IgnoreCase | RegexOptions . Compiled ) ,
//Anime - [SubGroup] Title with trailing number Absolute Episode Number
new Regex ( @"^\[(?<subgroup>.+?)\][-_. ]?(?<title>[^-]+?\d+?)[-_. ]+(?:[-_. ]?(?<absoluteepisode>\d{3}(?!\d+)))+(?:[-_. ]+(?<special>special|ova|ovd))?.*?(?<hash>\[\w{8}\])?(?:$|\.mkv)" ,
RegexOptions . IgnoreCase | RegexOptions . Compiled ) ,
//Anime - [SubGroup] Title - Absolute Episode Number
new Regex ( @"^\[(?<subgroup>.+?)\][-_. ]?(?<title>.+?)(?:[. ]-[. ](?<absoluteepisode>\d{2,3}(?!\d+|[-])))+(?:[-_. ]+(?<special>special|ova|ovd))?.*?(?<hash>\[\w{8}\])?(?:$|\.mkv)" ,
RegexOptions . IgnoreCase | RegexOptions . Compiled ) ,
//Anime - [SubGroup] Title Absolute Episode Number
new Regex ( @"^\[(?<subgroup>.+?)\][-_. ]?(?<title>.+?)[-_. ]+\(?(?:[-_. ]?(?<absoluteepisode>\d{2,3}(?!\d+)))+\)?(?:[-_. ]+(?<special>special|ova|ovd))?.*?(?<hash>\[\w{8}\])?(?:$|\.mkv)" ,
RegexOptions . IgnoreCase | RegexOptions . Compiled ) ,
//Anime - Title Season EpisodeNumber + Absolute Episode Number [SubGroup]
new Regex ( @"^(?<title>.+?)(?:[-_\W](?<![()\[!]))+(?:S?(?<season>(?<!\d+)\d{1,2}(?!\d+))(?:(?:[ex]|\W[ex]){1,2}(?<episode>(?<!\d+)\d{2}(?!\d+)))+).+?(?:[-_. ]?(?<absoluteepisode>(?<!\d+)\d{3}(?!\d+)))+.+?\[(?<subgroup>.+?)\](?:$|\.mkv)" ,
RegexOptions . IgnoreCase | RegexOptions . Compiled ) ,
//Anime - Title Absolute Episode Number [SubGroup]
new Regex ( @"^(?<title>.+?)(?:(?:_|-|\s|\.)+(?<absoluteepisode>\d{3}(?!\d+)))+(?:.+?)\[(?<subgroup>.+?)\].*?(?<hash>\[\w{8}\])?(?:$|\.)" ,
RegexOptions . IgnoreCase | RegexOptions . Compiled ) ,
//Anime - Title Absolute Episode Number [Hash]
new Regex ( @"^(?<title>.+?)(?:(?:_|-|\s|\.)+(?<absoluteepisode>\d{2,3}(?!\d+)))+(?:[-_. ]+(?<special>special|ova|ovd))?[-_. ]+.*?(?<hash>\[\w{8}\])(?:$|\.)" ,
RegexOptions . IgnoreCase | RegexOptions . Compiled ) ,
//Episodes with airdate AND season/episode number, capture season/epsiode only
new Regex ( @"^(?<title>.+?)?\W*(?<airdate>\d{4}\W+[0-1][0-9]\W+[0-3][0-9])(?!\W+[0-3][0-9])[-_. ](?:s?(?<season>(?<!\d+)(?:\d{1,2})(?!\d+)))(?:[ex](?<episode>(?<!\d+)(?:\d{1,3})(?!\d+)))" ,
RegexOptions . IgnoreCase | RegexOptions . Compiled ) ,
//Episodes with airdate AND season/episode number
new Regex ( @"^(?<title>.+?)?\W*(?<airyear>\d{4})\W+(?<airmonth>[0-1][0-9])\W+(?<airday>[0-3][0-9])(?!\W+[0-3][0-9]).+?(?:s?(?<season>(?<!\d+)(?:\d{1,2})(?!\d+)))(?:[ex](?<episode>(?<!\d+)(?:\d{1,3})(?!\d+)))" ,
RegexOptions . IgnoreCase | RegexOptions . Compiled ) ,
//Multi-episode Repeated (S01E05 - S01E06, 1x05 - 1x06, etc)
new Regex ( @"^(?<title>.+?)(?:(?:[-_\W](?<![()\[!]))+S?(?<season>(?<!\d+)(?:\d{1,2}|\d{4})(?!\d+))(?:(?:[ex]|[-_. ]e){1,2}(?<episode>\d{1,3}(?!\d+)))+){2,}" ,
RegexOptions . IgnoreCase | RegexOptions . Compiled ) ,
//Episodes with a title, Single episodes (S01E05, 1x05, etc) & Multi-episode (S01E05E06, S01E05-06, S01E05 E06, etc)
new Regex ( @"^(?<title>.+?)(?:(?:[-_\W](?<![()\[!]))+S?(?<season>(?<!\d+)(?:\d{1,2})(?!\d+))(?:[ex]|\W[ex]|_){1,2}(?<episode>\d{2,3}(?!\d+))(?:(?:\-|[ex]|\W[ex]|_){1,2}(?<episode>\d{2,3}(?!\d+)))*)\W?(?!\\)" ,
RegexOptions . IgnoreCase | RegexOptions . Compiled ) ,
//Episodes with a title, 4 digit season number, Single episodes (S2016E05, etc) & Multi-episode (S2016E05E06, S2016E05-06, S2016E05 E06, etc)
new Regex ( @"^(?<title>.+?)(?:(?:[-_\W](?<![()\[!]))+S(?<season>(?<!\d+)(?:\d{4})(?!\d+))(?:e|\We|_){1,2}(?<episode>\d{2,3}(?!\d+))(?:(?:\-|e|\We|_){1,2}(?<episode>\d{2,3}(?!\d+)))*)\W?(?!\\)" ,
RegexOptions . IgnoreCase | RegexOptions . Compiled ) ,
//Episodes with a title, 4 digit season number, Single episodes (2016x05, etc) & Multi-episode (2016x05x06, 2016x05-06, 2016x05 x06, etc)
new Regex ( @"^(?<title>.+?)(?:(?:[-_\W](?<![()\[!]))+(?<season>(?<!\d+)(?:\d{4})(?!\d+))(?:x|\Wx|_){1,2}(?<episode>\d{2,3}(?!\d+))(?:(?:\-|x|\Wx|_){1,2}(?<episode>\d{2,3}(?!\d+)))*)\W?(?!\\)" ,
RegexOptions . IgnoreCase | RegexOptions . Compiled ) ,
//Mini-Series with year in title, treated as season 1, episodes are labelled as Part01, Part 01, Part.1
new Regex ( @"^(?<title>.+?\d{4})(?:\W+(?:(?:Part\W?|e)(?<episode>\d{1,2}(?!\d+)))+)" ,
RegexOptions . IgnoreCase | RegexOptions . Compiled ) ,
//Mini-Series, treated as season 1, episodes are labelled as Part01, Part 01, Part.1
new Regex ( @"^(?<title>.+?)(?:\W+(?:(?:Part\W?|(?<!\d+\W+)e)(?<episode>\d{1,2}(?!\d+)))+)" ,
RegexOptions . IgnoreCase | RegexOptions . Compiled ) ,
//Mini-Series, treated as season 1, episodes are labelled as Part One/Two/Three/...Nine, Part.One, Part_One
new Regex ( @"^(?<title>.+?)(?:\W+(?:Part[-._ ](?<episode>One|Two|Three|Four|Five|Six|Seven|Eight|Nine)(?>[-._ ])))" ,
RegexOptions . IgnoreCase | RegexOptions . Compiled ) ,
//Mini-Series, treated as season 1, episodes are labelled as XofY
new Regex ( @"^(?<title>.+?)(?:\W+(?:(?<episode>(?<!\d+)\d{1,2}(?!\d+))of\d+)+)" ,
RegexOptions . IgnoreCase | RegexOptions . Compiled ) ,
//Supports Season 01 Episode 03
new Regex ( @"(?:.*(?:\""|^))(?<title>.*?)(?:[-_\W](?<![()\[]))+(?:\W?Season\W?)(?<season>(?<!\d+)\d{1,2}(?!\d+))(?:\W|_)+(?:Episode\W)(?:[-_. ]?(?<episode>(?<!\d+)\d{1,2}(?!\d+)))+" ,
RegexOptions . IgnoreCase | RegexOptions . Compiled ) ,
//Multi-episode release with no space between series title and season (S01E11E12)
new Regex ( @"(?:.*(?:^))(?<title>.*?)(?:\W?|_)S(?<season>(?<!\d+)\d{2}(?!\d+))(?:E(?<episode>(?<!\d+)\d{2}(?!\d+)))+" ,
RegexOptions . IgnoreCase | RegexOptions . Compiled ) ,
//Multi-episode with single episode numbers (S6.E1-E2, S6.E1E2, S6E1E2, etc)
new Regex ( @"^(?<title>.+?)[-_. ]S(?<season>(?<!\d+)(?:\d{1,2}|\d{4})(?!\d+))(?:[-_. ]?[ex]?(?<episode>(?<!\d+)\d{1,2}(?!\d+)))+" ,
RegexOptions . IgnoreCase | RegexOptions . Compiled ) ,
//Single episode season or episode S1E1 or S1-E1
new Regex ( @"(?:.*(?:\""|^))(?<title>.*?)(?:\W?|_)S(?<season>(?<!\d+)\d{1,2}(?!\d+))(?:\W|_)?E(?<episode>(?<!\d+)\d{1,2}(?!\d+))" ,
RegexOptions . IgnoreCase | RegexOptions . Compiled ) ,
//3 digit season S010E05
new Regex ( @"(?:.*(?:\""|^))(?<title>.*?)(?:\W?|_)S(?<season>(?<!\d+)\d{3}(?!\d+))(?:\W|_)?E(?<episode>(?<!\d+)\d{1,2}(?!\d+))" ,
RegexOptions . IgnoreCase | RegexOptions . Compiled ) ,
//5 digit episode number with a title
new Regex ( @"^(?:(?<title>.+?)(?:_|-|\s|\.)+)(?:S?(?<season>(?<!\d+)\d{1,2}(?!\d+)))(?:(?:\-|[ex]|\W[ex]|_){1,2}(?<episode>(?<!\d+)\d{5}(?!\d+)))" ,
RegexOptions . IgnoreCase | RegexOptions . Compiled ) ,
//5 digit multi-episode with a title
new Regex ( @"^(?:(?<title>.+?)(?:_|-|\s|\.)+)(?:S?(?<season>(?<!\d+)\d{1,2}(?!\d+)))(?:(?:[-_. ]{1,3}ep){1,2}(?<episode>(?<!\d+)\d{5}(?!\d+)))+" ,
RegexOptions . IgnoreCase | RegexOptions . Compiled ) ,
// Separated season and episode numbers S01 - E01
new Regex ( @"^(?<title>.+?)(?:_|-|\s|\.)+S(?<season>\d{2}(?!\d+))(\W-\W)E(?<episode>(?<!\d+)\d{2}(?!\d+))(?!\\)" ,
RegexOptions . IgnoreCase | RegexOptions . Compiled ) ,
//Season only releases
new Regex ( @"^(?<title>.+?)\W(?:S|Season)\W?(?<season>\d{1,2}(?!\d+))(\W+|_|$)(?<extras>EXTRAS|SUBPACK)?(?!\\)" ,
RegexOptions . IgnoreCase | RegexOptions . Compiled ) ,
//4 digit season only releases
new Regex ( @"^(?<title>.+?)\W(?:S|Season)\W?(?<season>\d{4}(?!\d+))(\W+|_|$)(?<extras>EXTRAS|SUBPACK)?(?!\\)" ,
RegexOptions . IgnoreCase | RegexOptions . Compiled ) ,
//Episodes with a title and season/episode in square brackets
new Regex ( @"^(?<title>.+?)(?:(?:[-_\W](?<![()\[!]))+\[S?(?<season>(?<!\d+)\d{1,2}(?!\d+))(?:(?:\-|[ex]|\W[ex]|_){1,2}(?<episode>(?<!\d+)\d{2}(?!\d+|i|p)))+\])\W?(?!\\)" ,
RegexOptions . IgnoreCase | RegexOptions . Compiled ) ,
//Supports 103/113 naming
new Regex ( @"^(?<title>.+?)?(?:(?:[-_\W](?<![()\[!]))+(?<season>(?<!\d+)[1-9])(?<episode>[1-9][0-9]|[0][1-9])(?![a-z]|\d+))+" ,
RegexOptions . IgnoreCase | RegexOptions . Compiled ) ,
//Episodes with airdate
new Regex ( @"^(?<title>.+?)?\W*(?<airyear>\d{4})\W+(?<airmonth>[0-1][0-9])\W+(?<airday>[0-3][0-9])(?!\W+[0-3][0-9])" ,
RegexOptions . IgnoreCase | RegexOptions . Compiled ) ,
//Supports 1103/1113 naming
new Regex ( @"^(?<title>.+?)?(?:(?:[-_\W](?<![()\[!]))*(?<season>(?<!\d+|\(|\[|e|x)\d{2})(?<episode>(?<!e|x)\d{2}(?!p|i|\d+|\)|\]|\W\d+)))+(\W+|_|$)(?!\\)" ,
RegexOptions . IgnoreCase | RegexOptions . Compiled ) ,
//4 digit episode number
//Episodes without a title, Single (S01E05, 1x05) AND Multi (S01E04E05, 1x04x05, etc)
new Regex ( @"^(?:S?(?<season>(?<!\d+)\d{1,2}(?!\d+))(?:(?:\-|[ex]|\W[ex]|_){1,2}(?<episode>\d{4}(?!\d+|i|p)))+)(\W+|_|$)(?!\\)" ,
RegexOptions . IgnoreCase | RegexOptions . Compiled ) ,
//4 digit episode number
//Episodes with a title, Single episodes (S01E05, 1x05, etc) & Multi-episode (S01E05E06, S01E05-06, S01E05 E06, etc)
new Regex ( @"^(?<title>.+?)(?:(?:[-_\W](?<![()\[!]))+S?(?<season>(?<!\d+)\d{1,2}(?!\d+))(?:(?:\-|[ex]|\W[ex]|_){1,2}(?<episode>\d{4}(?!\d+|i|p)))+)\W?(?!\\)" ,
RegexOptions . IgnoreCase | RegexOptions . Compiled ) ,
//Episodes with single digit episode number (S01E1, S01E5E6, etc)
new Regex ( @"^(?<title>.*?)(?:(?:[-_\W](?<![()\[!]))+S?(?<season>(?<!\d+)\d{1,2}(?!\d+))(?:(?:\-|[ex]){1,2}(?<episode>\d{1}))+)+(\W+|_|$)(?!\\)" ,
RegexOptions . IgnoreCase | RegexOptions . Compiled ) ,
//iTunes Season 1\05 Title (Quality).ext
new Regex ( @"^(?:Season(?:_|-|\s|\.)(?<season>(?<!\d+)\d{1,2}(?!\d+)))(?:_|-|\s|\.)(?<episode>(?<!\d+)\d{1,2}(?!\d+))" ,
RegexOptions . IgnoreCase | RegexOptions . Compiled ) ,
//Anime - Title Absolute Episode Number (e66)
new Regex ( @"^(?:\[(?<subgroup>.+?)\][-_. ]?)?(?<title>.+?)(?:(?:_|-|\s|\.)+(?:e|ep)(?<absoluteepisode>\d{2,3}))+.*?(?<hash>\[\w{8}\])?(?:$|\.)" ,
RegexOptions . IgnoreCase | RegexOptions . Compiled ) ,
//Anime - Title Episode Absolute Episode Number (Series Title Episode 01)
new Regex ( @"^(?<title>.+?)[-_. ](?:Episode)(?:[-_. ]+(?<absoluteepisode>(?<!\d+)\d{2,3}(?!\d+)))+(?:_|-|\s|\.)*?(?<hash>\[.{8}\])?(?:$|\.)?" ,
RegexOptions . IgnoreCase | RegexOptions . Compiled ) ,
//Anime - Title Absolute Episode Number
new Regex ( @"^(?:\[(?<subgroup>.+?)\][-_. ]?)?(?<title>.+?)(?:[-_. ]+(?<absoluteepisode>(?<!\d+)\d{2,3}(?!\d+)))+(?:_|-|\s|\.)*?(?<hash>\[.{8}\])?(?:$|\.)?" ,
RegexOptions . IgnoreCase | RegexOptions . Compiled ) ,
//Anime - Title {Absolute Episode Number}
new Regex ( @"^(?:\[(?<subgroup>.+?)\][-_. ]?)?(?<title>.+?)(?:(?:[-_\W](?<![()\[!]))+(?<absoluteepisode>(?<!\d+)\d{2,3}(?!\d+)))+(?:_|-|\s|\.)*?(?<hash>\[.{8}\])?(?:$|\.)?" ,
RegexOptions . IgnoreCase | RegexOptions . Compiled ) ,
//Extant, terrible multi-episode naming (extant.10708.hdtv-lol.mp4)
new Regex ( @"^(?<title>.+?)[-_. ](?<season>[0]?\d?)(?:(?<episode>\d{2}){2}(?!\d+))[-_. ]" ,
RegexOptions . IgnoreCase | RegexOptions . Compiled )
} ;
private static readonly Regex [ ] RejectHashedReleasesRegex = new Regex [ ]
{
// Generic match for md5 and mixed-case hashes.
@ -328,9 +137,6 @@ namespace NzbDrone.Core.Parser
private static readonly Regex AnimeReleaseGroupRegex = new Regex ( @"^(?:\[(?<subgroup>(?!\s).+?(?<!\s))\](?:_|-|\s|\.)?)" ,
RegexOptions . IgnoreCase | RegexOptions . Compiled ) ;
private static readonly Regex LanguageRegex = new Regex ( @"(?:\W|_)(?<italian>\b(?:ita|italian)\b)|(?<german>german\b|videomann)|(?<flemish>flemish)|(?<greek>greek)|(?<french>(?:\W|_)(?:FR|VOSTFR)(?:\W|_))|(?<russian>\brus\b)|(?<dutch>nl\W?subs?)|(?<hungarian>\b(?:HUNDUB|HUN)\b)|(?<spanish>\b(?:español|castellano)\b)" ,
RegexOptions . IgnoreCase | RegexOptions . Compiled ) ;
private static readonly Regex YearInTitleRegex = new Regex ( @"^(?<title>.+?)(?:\W|_)?(?<year>\d{4})" ,
RegexOptions . IgnoreCase | RegexOptions . Compiled ) ;
@ -346,59 +152,20 @@ namespace NzbDrone.Core.Parser
public static ParsedTrackInfo ParseMusicPath ( string path )
{
var fileInfo = new FileInfo ( path ) ;
var file = TagLib . File . Create ( path ) ;
var trackNumber = file . Tag . Track ;
var trackTitle = file . Tag . Title ;
var artist = file . Tag . FirstAlbumArtist ;
var result = new ParsedTrackInfo { } ;
if ( artist. IsNullOrWhiteSpace ( ) )
if ( MediaFiles. MediaFileExtensions . Extensions . Contains ( fileInfo . Extension ) )
{
artist = file . Tag . FirstPerformer ;
result = ParseAudioTags ( path ) ;
}
var artistTitleInfo = new ArtistTitleInfo
else
{
Title = artist ,
Year = ( int ) file . Tag . Year
} ;
var temp = new int [ 1 ] ;
temp [ 0 ] = ( int ) trackNumber ;
var result = new ParsedTrackInfo
{
Language = Language . English , //TODO Parse from Tag/Mediainfo
AlbumTitle = file . Tag . Album ,
ArtistTitle = artist ,
ArtistMBId = file . Tag . MusicBrainzArtistId ,
AlbumMBId = file . Tag . MusicBrainzReleaseId ,
TrackMBId = file . Tag . MusicBrainzReleaseType ,
TrackNumbers = temp ,
ArtistTitleInfo = artistTitleInfo ,
Title = trackTitle
} ;
foreach ( TagLib . ICodec codec in file . Properties . Codecs )
{
TagLib . IAudioCodec acodec = codec as TagLib . IAudioCodec ;
TagLib . IVideoCodec vcodec = codec as TagLib . IVideoCodec ;
if ( acodec ! = null & & ( acodec . MediaTypes & TagLib . MediaTypes . Audio ) ! = TagLib . MediaTypes . None )
{
Logger . Debug ( "Audio Properties : " + acodec . Description ) ;
Logger . Debug ( "Bitrate: " + acodec . AudioBitrate ) ;
Logger . Debug ( "SampleRate: " + acodec . AudioSampleRate ) ;
Logger . Debug ( "Channels: " + acodec . AudioChannels + "\n" ) ;
result . Quality = QualityParser . ParseQuality ( acodec . Description , acodec . AudioBitrate , acodec . AudioSampleRate ) ;
Logger . Debug ( "Quality parsed: {0}" , result . Quality ) ;
}
result = null ;
}
// TODO: Check if it is common that we might need to fallback to parser to gather details
//var result = ParseMusicTitle(fileInfo.Name);
@ -632,17 +399,6 @@ namespace NzbDrone.Core.Parser
return null ;
}
public static string CleanSeriesTitle ( this string title )
{
long number = 0 ;
//If Title only contains numbers return it as is.
if ( long . TryParse ( title , out number ) )
return title ;
return NormalizeRegex . Replace ( title , string . Empty ) . ToLower ( ) . RemoveAccent ( ) ;
}
public static string CleanArtistName ( this string name )
{
long number = 0 ;
@ -722,97 +478,59 @@ namespace NzbDrone.Core.Parser
return title ;
}
p ublic static Language ParseLanguage ( string title )
p rivate static ParsedTrackInfo ParseAudioTags ( string path )
{
var lowerTitle = title . ToLower ( ) ;
if ( lowerTitle . Contains ( "english" ) )
return Language . English ;
if ( lowerTitle . Contains ( "french" ) )
return Language . French ;
if ( lowerTitle . Contains ( "spanish" ) )
return Language . Spanish ;
if ( lowerTitle . Contains ( "danish" ) )
return Language . Danish ;
if ( lowerTitle . Contains ( "dutch" ) )
return Language . Dutch ;
if ( lowerTitle . Contains ( "japanese" ) )
return Language . Japanese ;
if ( lowerTitle . Contains ( "cantonese" ) )
return Language . Cantonese ;
if ( lowerTitle . Contains ( "mandarin" ) )
return Language . Mandarin ;
if ( lowerTitle . Contains ( "korean" ) )
return Language . Korean ;
if ( lowerTitle . Contains ( "russian" ) )
return Language . Russian ;
if ( lowerTitle . Contains ( "polish" ) )
return Language . Polish ;
if ( lowerTitle . Contains ( "vietnamese" ) )
return Language . Vietnamese ;
if ( lowerTitle . Contains ( "swedish" ) )
return Language . Swedish ;
if ( lowerTitle . Contains ( "norwegian" ) )
return Language . Norwegian ;
if ( lowerTitle . Contains ( "nordic" ) )
return Language . Norwegian ;
if ( lowerTitle . Contains ( "finnish" ) )
return Language . Finnish ;
if ( lowerTitle . Contains ( "turkish" ) )
return Language . Turkish ;
if ( lowerTitle . Contains ( "portuguese" ) )
return Language . Portuguese ;
if ( lowerTitle . Contains ( "hungarian" ) )
return Language . Hungarian ;
var match = LanguageRegex . Match ( title ) ;
if ( match . Groups [ "italian" ] . Captures . Cast < Capture > ( ) . Any ( ) )
return Language . Italian ;
if ( match . Groups [ "german" ] . Captures . Cast < Capture > ( ) . Any ( ) )
return Language . German ;
var fileInfo = new FileInfo ( path ) ;
var file = TagLib . File . Create ( path ) ;
var trackNumber = file . Tag . Track ;
var trackTitle = file . Tag . Title ;
if ( match . Groups [ "flemish" ] . Captures . Cast < Capture > ( ) . Any ( ) )
return Language . Flemish ;
var artist = file . Tag . FirstAlbumArtist ;
if ( match . Groups [ "greek" ] . Captures . Cast < Capture > ( ) . Any ( ) )
return Language . Greek ;
if ( artist . IsNullOrWhiteSpace ( ) )
{
artist = file . Tag . FirstPerformer ;
}
if ( match . Groups [ "spanish" ] . Captures . Cast < Capture > ( ) . Any ( ) )
return Language . Spanish ;
var artistTitleInfo = new ArtistTitleInfo
{
Title = artist ,
Year = ( int ) file . Tag . Year
} ;
if ( match . Groups [ "french" ] . Success )
return Language . French ;
var temp = new int [ 1 ] ;
temp [ 0 ] = ( int ) trackNumber ;
var result = new ParsedTrackInfo
{
Language = Language . English , //TODO Parse from Tag/Mediainfo
AlbumTitle = file . Tag . Album ,
ArtistTitle = artist ,
ArtistMBId = file . Tag . MusicBrainzArtistId ,
AlbumMBId = file . Tag . MusicBrainzReleaseId ,
TrackMBId = file . Tag . MusicBrainzReleaseType ,
TrackNumbers = temp ,
ArtistTitleInfo = artistTitleInfo ,
Title = trackTitle
} ;
if ( match . Groups [ "russian" ] . Success )
return Language . Russian ;
foreach ( TagLib . ICodec codec in file . Properties . Codecs )
{
TagLib . IAudioCodec acodec = codec as TagLib . IAudioCodec ;
TagLib . IVideoCodec vcodec = codec as TagLib . IVideoCodec ;
if ( match . Groups [ "dutch" ] . Success )
return Language . Dutch ;
if ( acodec ! = null & & ( acodec . MediaTypes & TagLib . MediaTypes . Audio ) ! = TagLib . MediaTypes . None )
{
Logger . Debug ( "Audio Properties : " + acodec . Description ) ;
Logger . Debug ( "Bitrate: " + acodec . AudioBitrate ) ;
Logger . Debug ( "SampleRate: " + acodec . AudioSampleRate ) ;
Logger . Debug ( "Channels: " + acodec . AudioChannels + "\n" ) ;
if ( match . Groups [ "hungarian" ] . Success )
return Language . Hungarian ;
result . Quality = QualityParser . ParseQuality ( acodec . Description , acodec . AudioBitrate , acodec . AudioSampleRate ) ;
Logger . Debug ( "Quality parsed: {0}" , result . Quality ) ;
}
}
return Language . English ;
return result ;
}
private static ParsedTrackInfo ParseMatchMusicCollection ( MatchCollection matchCollection )
@ -898,7 +616,7 @@ namespace NzbDrone.Core.Parser
if ( parseResult = = null )
{
return Clean SeriesTitl e( title ) ;
return Clean ArtistNam e( title ) ;
}
return parseResult . ArtistName ;