/// <summary> /// Initializes a new instance of the <see cref="NamingOptions"/> class. /// </summary> public NamingOptions() { VideoFileExtensions = new[] { ".001", ".3g2", ".3gp", ".amv", ".asf", ".asx", ".avi", ".bin", ".bivx", ".divx", ".dv", ".dvr-ms", ".f4v", ".fli", ".flv", ".ifo", ".img", ".iso", ".m2t", ".m2ts", ".m2v", ".m4v", ".mkv", ".mk3d", ".mov", ".mp2", ".mp4", ".mpe", ".mpeg", ".mpg", ".mts", ".mxf", ".nrg", ".nsv", ".nuv", ".ogg", ".ogm", ".ogv", ".pva", ".qt", ".rec", ".rm", ".rmvb", ".svq3", ".tp", ".ts", ".ty", ".viv", ".vob", ".vp3", ".webm", ".wmv", ".wtv", ".xvid" }; VideoFlagDelimiters = new[] { '(', ')', '-', '.', '_', '[', ']' }; StubFileExtensions = new[] { ".disc" }; StubTypes = new[] { new StubTypeRule( stubType: "dvd", token: "dvd"), new StubTypeRule( stubType: "hddvd", token: "hddvd"), new StubTypeRule( stubType: "bluray", token: "bluray"), new StubTypeRule( stubType: "bluray", token: "brrip"), new StubTypeRule( stubType: "bluray", token: "bd25"), new StubTypeRule( stubType: "bluray", token: "bd50"), new StubTypeRule( stubType: "vhs", token: "vhs"), new StubTypeRule( stubType: "tv", token: "HDTV"), new StubTypeRule( stubType: "tv", token: "PDTV"), new StubTypeRule( stubType: "tv", token: "DSR") }; VideoFileStackingRules = new[] { new FileStackRule(@"^(?<filename>.*?)(?:(?<=[\]\)\}])|[ _.-]+)[\(\[]?(?<parttype>cd|dvd|part|pt|dis[ck])[ _.-]*(?<number>[0-9]+)[\)\]]?(?:\.[^.]+)?$", true), new FileStackRule(@"^(?<filename>.*?)(?:(?<=[\]\)\}])|[ _.-]+)[\(\[]?(?<parttype>cd|dvd|part|pt|dis[ck])[ _.-]*(?<number>[a-d])[\)\]]?(?:\.[^.]+)?$", false), new FileStackRule(@"^(?<filename>.*?)(?:(?<=[\]\)\}])|[ _.-]?)(?<number>[a-d])(?:\.[^.]+)?$", false) }; CleanDateTimes = new[] { @"(.+[^_\,\.\(\)\[\]\-])[_\.\(\)\[\]\-](19[0-9]{2}|20[0-9]{2})(?![0-9]+|\W[0-9]{2}\W[0-9]{2})([ _\,\.\(\)\[\]\-][^0-9]|).*(19[0-9]{2}|20[0-9]{2})*", @"(.+[^_\,\.\(\)\[\]\-])[ _\.\(\)\[\]\-]+(19[0-9]{2}|20[0-9]{2})(?![0-9]+|\W[0-9]{2}\W[0-9]{2})([ _\,\.\(\)\[\]\-][^0-9]|).*(19[0-9]{2}|20[0-9]{2})*" }; CleanStrings = new[] { @"^\s*(?<cleaned>.+?)[ _\,\.\(\)\[\]\-](3d|sbs|tab|hsbs|htab|mvc|HDR|HDC|UHD|UltraHD|4k|ac3|dts|custom|dc|divx|divx5|dsr|dsrip|dutch|dvd|dvdrip|dvdscr|dvdscreener|screener|dvdivx|cam|fragment|fs|hdtv|hdrip|hdtvrip|internal|limited|multisubs|ntsc|ogg|ogm|pal|pdtv|proper|repack|rerip|retail|cd[1-9]|r3|r5|bd5|bd|se|svcd|swedish|german|read.nfo|nfofix|unrated|ws|telesync|ts|telecine|tc|brrip|bdrip|480p|480i|576p|576i|720p|720i|1080p|1080i|2160p|hrhd|hrhdtv|hddvd|bluray|blu-ray|x264|x265|h264|h265|xvid|xvidvd|xxx|www.www|AAC|DTS|\[.*\])([ _\,\.\(\)\[\]\-]|$)", @"^(?<cleaned>.+?)(\[.*\])", @"^\s*(?<cleaned>.+?)\WE[0-9]+(-|~)E?[0-9]+(\W|$)", @"^\s*\[[^\]]+\](?!\.\w+$)\s*(?<cleaned>.+)", @"^\s*(?<cleaned>.+?)\s+-\s+[0-9]+\s*$" }; SubtitleFileExtensions = new[] { ".ass", ".mks", ".sami", ".smi", ".srt", ".ssa", ".sub", ".vtt", }; AlbumStackingPrefixes = new[] { "cd", "disc", "disk", "vol", "volume" }; AudioFileExtensions = new[] { ".669", ".3gp", ".aa", ".aac", ".aax", ".ac3", ".act", ".adp", ".adplug", ".adx", ".afc", ".amf", ".aif", ".aiff", ".alac", ".amr", ".ape", ".ast", ".au", ".awb", ".cda", ".cue", ".dmf", ".dsf", ".dsm", ".dsp", ".dts", ".dvf", ".far", ".flac", ".gdm", ".gsm", ".gym", ".hps", ".imf", ".it", ".m15", ".m4a", ".m4b", ".mac", ".med", ".mka", ".mmf", ".mod", ".mogg", ".mp2", ".mp3", ".mpa", ".mpc", ".mpp", ".mp+", ".msv", ".nmf", ".nsf", ".nsv", ".oga", ".ogg", ".okt", ".opus", ".pls", ".ra", ".rf64", ".rm", ".s3m", ".sfx", ".shn", ".sid", ".spc", ".stm", ".strm", ".ult", ".uni", ".vox", ".wav", ".wma", ".wv", ".xm", ".xsp", ".ymf" }; MediaFlagDelimiters = new[] { "." }; MediaForcedFlags = new[] { "foreign", "forced" }; MediaDefaultFlags = new[] { "default" }; EpisodeExpressions = new[] { // *** Begin Kodi Standard Naming // <!-- foo.s01.e01, foo.s01_e01, S01E02 foo, S01 - E02 --> new EpisodeExpression(@".*(\\|\/)(?<seriesname>((?![Ss]([0-9]+)[][ ._-]*[Ee]([0-9]+))[^\\\/])*)?[Ss](?<seasonnumber>[0-9]+)[][ ._-]*[Ee](?<epnumber>[0-9]+)([^\\/]*)$") { IsNamed = true }, // <!-- foo.ep01, foo.EP_01 --> new EpisodeExpression(@"[\._ -]()[Ee][Pp]_?([0-9]+)([^\\/]*)$"), // <!-- foo.E01., foo.e01. --> new EpisodeExpression(@"[^\\/]*?()\.?[Ee]([0-9]+)\.([^\\/]*)$"), new EpisodeExpression("(?<year>[0-9]{4})[\\.-](?<month>[0-9]{2})[\\.-](?<day>[0-9]{2})", true) { DateTimeFormats = new[] { "yyyy.MM.dd", "yyyy-MM-dd", "yyyy_MM_dd" } }, new EpisodeExpression(@"(?<day>[0-9]{2})[.-](?<month>[0-9]{2})[.-](?<year>[0-9]{4})", true) { DateTimeFormats = new[] { "dd.MM.yyyy", "dd-MM-yyyy", "dd_MM_yyyy" } }, // This isn't a Kodi naming rule, but the expression below causes false positives, // so we make sure this one gets tested first. // "Foo Bar 889" new EpisodeExpression(@".*[\\\/](?![Ee]pisode)(?<seriesname>[\w\s]+?)\s(?<epnumber>[0-9]{1,3})(-(?<endingepnumber>[0-9]{2,3}))*[^\\\/x]*$") { IsNamed = true }, new EpisodeExpression(@"[\\\/\._ \[\(-]([0-9]+)x([0-9]+(?:(?:[a-i]|\.[1-9])(?![0-9]))?)([^\\\/]*)$") { SupportsAbsoluteEpisodeNumbers = true }, // Not a Kodi rule as well, but below rule also causes false positives for triple-digit episode names // [bar] Foo - 1 [baz] special case of below expression to prevent false positives with digits in the series name new EpisodeExpression(@".*[\\\/]?.*?(\[.*?\])+.*?(?<seriesname>[-\w\s]+?)[\s_]*-[\s_]*(?<epnumber>[0-9]+).*$") { IsNamed = true }, // /server/anything_102.mp4 // /server/james.corden.2017.04.20.anne.hathaway.720p.hdtv.x264-crooks.mkv // /server/anything_1996.11.14.mp4 new EpisodeExpression(@"[\\/._ -](?<seriesname>(?![0-9]+[0-9][0-9])([^\\\/_])*)[\\\/._ -](?<seasonnumber>[0-9]+)(?<epnumber>[0-9][0-9](?:(?:[a-i]|\.[1-9])(?![0-9]))?)([._ -][^\\\/]*)$") { IsOptimistic = true, IsNamed = true, SupportsAbsoluteEpisodeNumbers = false }, new EpisodeExpression("[\\/._ -]p(?:ar)?t[_. -]()([ivx]+|[0-9]+)([._ -][^\\/]*)$") { SupportsAbsoluteEpisodeNumbers = true }, // *** End Kodi Standard Naming // "Episode 16", "Episode 16 - Title" new EpisodeExpression(@"[Ee]pisode (?<epnumber>[0-9]+)(-(?<endingepnumber>[0-9]+))?[^\\\/]*$") { IsNamed = true }, new EpisodeExpression(@".*(\\|\/)[sS]?(?<seasonnumber>[0-9]+)[xX](?<epnumber>[0-9]+)[^\\\/]*$") { IsNamed = true }, new EpisodeExpression(@".*(\\|\/)[sS](?<seasonnumber>[0-9]+)[x,X]?[eE](?<epnumber>[0-9]+)[^\\\/]*$") { IsNamed = true }, new EpisodeExpression(@".*(\\|\/)(?<seriesname>((?![sS]?[0-9]{1,4}[xX][0-9]{1,3})[^\\\/])*)?([sS]?(?<seasonnumber>[0-9]{1,4})[xX](?<epnumber>[0-9]+))[^\\\/]*$") { IsNamed = true }, new EpisodeExpression(@".*(\\|\/)(?<seriesname>[^\\\/]*)[sS](?<seasonnumber>[0-9]{1,4})[xX\.]?[eE](?<epnumber>[0-9]+)[^\\\/]*$") { IsNamed = true }, // "01.avi" new EpisodeExpression(@".*[\\\/](?<epnumber>[0-9]+)(-(?<endingepnumber>[0-9]+))*\.\w+$") { IsOptimistic = true, IsNamed = true }, // "1-12 episode title" new EpisodeExpression(@"([0-9]+)-([0-9]+)"), // "01 - blah.avi", "01-blah.avi" new EpisodeExpression(@".*(\\|\/)(?<epnumber>[0-9]{1,3})(-(?<endingepnumber>[0-9]{2,3}))*\s?-\s?[^\\\/]*$") { IsOptimistic = true, IsNamed = true }, // "01.blah.avi" new EpisodeExpression(@".*(\\|\/)(?<epnumber>[0-9]{1,3})(-(?<endingepnumber>[0-9]{2,3}))*\.[^\\\/]+$") { IsOptimistic = true, IsNamed = true }, // "blah - 01.avi", "blah 2 - 01.avi", "blah - 01 blah.avi", "blah 2 - 01 blah", "blah - 01 - blah.avi", "blah 2 - 01 - blah" new EpisodeExpression(@".*[\\\/][^\\\/]* - (?<epnumber>[0-9]{1,3})(-(?<endingepnumber>[0-9]{2,3}))*[^\\\/]*$") { IsOptimistic = true, IsNamed = true }, // "01 episode title.avi" new EpisodeExpression(@"[Ss]eason[\._ ](?<seasonnumber>[0-9]+)[\\\/](?<epnumber>[0-9]{1,3})([^\\\/]*)$") { IsOptimistic = true, IsNamed = true }, // Series and season only expression // "the show/season 1", "the show/s01" new EpisodeExpression(@"(.*(\\|\/))*(?<seriesname>.+)\/[Ss](eason)?[\. _\-]*(?<seasonnumber>[0-9]+)") { IsNamed = true }, // Series and season only expression // "the show S01", "the show season 1" new EpisodeExpression(@"(.*(\\|\/))*(?<seriesname>.+)[\. _\-]+[sS](eason)?[\. _\-]*(?<seasonnumber>[0-9]+)") { IsNamed = true }, }; EpisodeWithoutSeasonExpressions = new[] { @"[/\._ \-]()([0-9]+)(-[0-9]+)?" }; EpisodeMultiPartExpressions = new[] { @"^[-_ex]+([0-9]+(?:(?:[a-i]|\\.[1-9])(?![0-9]))?)" }; VideoExtraRules = new[] { new ExtraRule( ExtraType.Trailer, ExtraRuleType.DirectoryName, "trailers", MediaType.Video), new ExtraRule( ExtraType.ThemeVideo, ExtraRuleType.DirectoryName, "backdrops", MediaType.Video), new ExtraRule( ExtraType.ThemeSong, ExtraRuleType.DirectoryName, "theme-music", MediaType.Audio), new ExtraRule( ExtraType.BehindTheScenes, ExtraRuleType.DirectoryName, "behind the scenes", MediaType.Video), new ExtraRule( ExtraType.DeletedScene, ExtraRuleType.DirectoryName, "deleted scenes", MediaType.Video), new ExtraRule( ExtraType.Interview, ExtraRuleType.DirectoryName, "interviews", MediaType.Video), new ExtraRule( ExtraType.Scene, ExtraRuleType.DirectoryName, "scenes", MediaType.Video), new ExtraRule( ExtraType.Sample, ExtraRuleType.DirectoryName, "samples", MediaType.Video), new ExtraRule( ExtraType.Clip, ExtraRuleType.DirectoryName, "shorts", MediaType.Video), new ExtraRule( ExtraType.Clip, ExtraRuleType.DirectoryName, "featurettes", MediaType.Video), new ExtraRule( ExtraType.Unknown, ExtraRuleType.DirectoryName, "extras", MediaType.Video), new ExtraRule( ExtraType.Trailer, ExtraRuleType.Filename, "trailer", MediaType.Video), new ExtraRule( ExtraType.Trailer, ExtraRuleType.Suffix, "-trailer", MediaType.Video), new ExtraRule( ExtraType.Trailer, ExtraRuleType.Suffix, ".trailer", MediaType.Video), new ExtraRule( ExtraType.Trailer, ExtraRuleType.Suffix, "_trailer", MediaType.Video), new ExtraRule( ExtraType.Trailer, ExtraRuleType.Suffix, " trailer", MediaType.Video), new ExtraRule( ExtraType.Sample, ExtraRuleType.Filename, "sample", MediaType.Video), new ExtraRule( ExtraType.Sample, ExtraRuleType.Suffix, "-sample", MediaType.Video), new ExtraRule( ExtraType.Sample, ExtraRuleType.Suffix, ".sample", MediaType.Video), new ExtraRule( ExtraType.Sample, ExtraRuleType.Suffix, "_sample", MediaType.Video), new ExtraRule( ExtraType.Sample, ExtraRuleType.Suffix, " sample", MediaType.Video), new ExtraRule( ExtraType.ThemeSong, ExtraRuleType.Filename, "theme", MediaType.Audio), new ExtraRule( ExtraType.Scene, ExtraRuleType.Suffix, "-scene", MediaType.Video), new ExtraRule( ExtraType.Clip, ExtraRuleType.Suffix, "-clip", MediaType.Video), new ExtraRule( ExtraType.Interview, ExtraRuleType.Suffix, "-interview", MediaType.Video), new ExtraRule( ExtraType.BehindTheScenes, ExtraRuleType.Suffix, "-behindthescenes", MediaType.Video), new ExtraRule( ExtraType.DeletedScene, ExtraRuleType.Suffix, "-deleted", MediaType.Video), new ExtraRule( ExtraType.DeletedScene, ExtraRuleType.Suffix, "-deletedscene", MediaType.Video), new ExtraRule( ExtraType.Clip, ExtraRuleType.Suffix, "-featurette", MediaType.Video), new ExtraRule( ExtraType.Clip, ExtraRuleType.Suffix, "-short", MediaType.Video), new ExtraRule( ExtraType.Unknown, ExtraRuleType.Suffix, "-extra", MediaType.Video) }; AllExtrasTypesFolderNames = VideoExtraRules .Where(i => i.RuleType == ExtraRuleType.DirectoryName) .ToDictionary(i => i.Token, i => i.ExtraType, StringComparer.OrdinalIgnoreCase); Format3DRules = new[] { // Kodi rules: new Format3DRule( precedingToken: "3d", token: "hsbs"), new Format3DRule( precedingToken: "3d", token: "sbs"), new Format3DRule( precedingToken: "3d", token: "htab"), new Format3DRule( precedingToken: "3d", token: "tab"), // Media Browser rules: new Format3DRule("fsbs"), new Format3DRule("hsbs"), new Format3DRule("sbs"), new Format3DRule("ftab"), new Format3DRule("htab"), new Format3DRule("tab"), new Format3DRule("sbs3d"), new Format3DRule("mvc") }; AudioBookPartsExpressions = new[] { // Detect specified chapters, like CH 01 @"ch(?:apter)?[\s_-]?(?<chapter>[0-9]+)", // Detect specified parts, like Part 02 @"p(?:ar)?t[\s_-]?(?<part>[0-9]+)", // Chapter is often beginning of filename "^(?<chapter>[0-9]+)", // Part if often ending of filename @"(?<!ch(?:apter) )(?<part>[0-9]+)$", // Sometimes named as 0001_005 (chapter_part) "(?<chapter>[0-9]+)_(?<part>[0-9]+)", // Some audiobooks are ripped from cd's, and will be named by disk number. @"dis(?:c|k)[\s_-]?(?<chapter>[0-9]+)" }; AudioBookNamesExpressions = new[] { // Detect year usually in brackets after name Batman (2020) @"^(?<name>.+?)\s*\(\s*(?<year>[0-9]{4})\s*\)\s*$", @"^\s*(?<name>[^ ].*?)\s*$" }; MultipleEpisodeExpressions = new[] { @".*(\\|\/)[sS]?(?<seasonnumber>[0-9]{1,4})[xX](?<epnumber>[0-9]{1,3})((-| - )[0-9]{1,4}[eExX](?<endingepnumber>[0-9]{1,3}))+[^\\\/]*$", @".*(\\|\/)[sS]?(?<seasonnumber>[0-9]{1,4})[xX](?<epnumber>[0-9]{1,3})((-| - )[0-9]{1,4}[xX][eE](?<endingepnumber>[0-9]{1,3}))+[^\\\/]*$", @".*(\\|\/)[sS]?(?<seasonnumber>[0-9]{1,4})[xX](?<epnumber>[0-9]{1,3})((-| - )?[xXeE](?<endingepnumber>[0-9]{1,3}))+[^\\\/]*$", @".*(\\|\/)[sS]?(?<seasonnumber>[0-9]{1,4})[xX](?<epnumber>[0-9]{1,3})(-[xE]?[eE]?(?<endingepnumber>[0-9]{1,3}))+[^\\\/]*$", @".*(\\|\/)(?<seriesname>((?![sS]?[0-9]{1,4}[xX][0-9]{1,3})[^\\\/])*)?([sS]?(?<seasonnumber>[0-9]{1,4})[xX](?<epnumber>[0-9]{1,3}))((-| - )[0-9]{1,4}[xXeE](?<endingepnumber>[0-9]{1,3}))+[^\\\/]*$", @".*(\\|\/)(?<seriesname>((?![sS]?[0-9]{1,4}[xX][0-9]{1,3})[^\\\/])*)?([sS]?(?<seasonnumber>[0-9]{1,4})[xX](?<epnumber>[0-9]{1,3}))((-| - )[0-9]{1,4}[xX][eE](?<endingepnumber>[0-9]{1,3}))+[^\\\/]*$", @".*(\\|\/)(?<seriesname>((?![sS]?[0-9]{1,4}[xX][0-9]{1,3})[^\\\/])*)?([sS]?(?<seasonnumber>[0-9]{1,4})[xX](?<epnumber>[0-9]{1,3}))((-| - )?[xXeE](?<endingepnumber>[0-9]{1,3}))+[^\\\/]*$", @".*(\\|\/)(?<seriesname>((?![sS]?[0-9]{1,4}[xX][0-9]{1,3})[^\\\/])*)?([sS]?(?<seasonnumber>[0-9]{1,4})[xX](?<epnumber>[0-9]{1,3}))(-[xX]?[eE]?(?<endingepnumber>[0-9]{1,3}))+[^\\\/]*$", @".*(\\|\/)(?<seriesname>[^\\\/]*)[sS](?<seasonnumber>[0-9]{1,4})[xX\.]?[eE](?<epnumber>[0-9]{1,3})((-| - )?[xXeE](?<endingepnumber>[0-9]{1,3}))+[^\\\/]*$", @".*(\\|\/)(?<seriesname>[^\\\/]*)[sS](?<seasonnumber>[0-9]{1,4})[xX\.]?[eE](?<epnumber>[0-9]{1,3})(-[xX]?[eE]?(?<endingepnumber>[0-9]{1,3}))+[^\\\/]*$" }.Select(i => new EpisodeExpression(i) { IsNamed = true }).ToArray(); Compile(); }
public ExtendedNamingOptions() { var extensions = VideoFileExtensions.ToList(); extensions.AddRange(new[] { ".mkv", ".m2t", ".m2ts", ".img", ".iso", ".mk3d", ".ts", ".rmvb", ".mov", ".avi", ".mpg", ".mpeg", ".wmv", ".mp4", ".divx", ".dvr-ms", ".wtv", ".ogm", ".ogv", ".asf", ".m4v", ".flv", ".f4v", ".3gp", ".webm", ".mts", ".m2v", ".rec" }); // Problematic. Can always become configurable if needed. extensions.Remove(".dat"); extensions.Remove(".wpl"); extensions.Remove(".m3u"); VideoFileExtensions = extensions .Distinct(StringComparer.OrdinalIgnoreCase) .ToList(); VideoExtraRules.AddRange(new List <ExtraRule> { new ExtraRule { ExtraType = "scene", RuleType = ExtraRuleType.Suffix, Token = "-scene", MediaType = MediaType.Video }, new ExtraRule { ExtraType = "clip", RuleType = ExtraRuleType.Suffix, Token = "-clip", MediaType = MediaType.Video }, new ExtraRule { ExtraType = "interview", RuleType = ExtraRuleType.Suffix, Token = "-interview", MediaType = MediaType.Video }, new ExtraRule { ExtraType = "behindthescenes", RuleType = ExtraRuleType.Suffix, Token = "-behindthescenes", MediaType = MediaType.Video }, new ExtraRule { ExtraType = "deletedscene", RuleType = ExtraRuleType.Suffix, Token = "-deleted", MediaType = MediaType.Video } }); Format3DRules.AddRange(new List <Format3DRule> { // Media Browser rules: new Format3DRule { Token = "fsbs" }, new Format3DRule { Token = "hsbs" }, new Format3DRule { Token = "sbs" }, new Format3DRule { Token = "ftab" }, new Format3DRule { Token = "htab" }, new Format3DRule { Token = "tab" }, new Format3DRule { Token = "sbs3d" }, new Format3DRule { Token = "mvc" } }); }