Example #1
0
        public NamingOptions()
        {
            VideoFileExtensions = new[]
            {
                ".m4v",
                ".3gp",
                ".nsv",
                ".ts",
                ".ty",
                ".strm",
                ".rm",
                ".rmvb",
                ".ifo",
                ".mov",
                ".qt",
                ".divx",
                ".xvid",
                ".bivx",
                ".vob",
                ".nrg",
                ".img",
                ".iso",
                ".pva",
                ".wmv",
                ".asf",
                ".asx",
                ".ogm",
                ".m2v",
                ".avi",
                ".bin",
                ".dvr-ms",
                ".mpg",
                ".mpeg",
                ".mp4",
                ".mkv",
                ".avc",
                ".vp3",
                ".svq3",
                ".nuv",
                ".viv",
                ".dv",
                ".fli",
                ".flv",
                ".001",
                ".tp"
            };

            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"
                }
            };

            VideoFileStackingExpressions = new[]
            {
                "(.*?)([ _.-]*(?:cd|dvd|p(?:ar)?t|dis[ck])[ _.-]*[0-9]+)(.*?)(\\.[^.]+)$",
                "(.*?)([ _.-]*(?:cd|dvd|p(?:ar)?t|dis[ck])[ _.-]*[a-d])(.*?)(\\.[^.]+)$",
                "(.*?)([ ._-]*[a-d])(.*?)(\\.[^.]+)$"
            };

            CleanDateTimes = new[]
            {
                @"(.+[^_\,\.\(\)\[\]\-])[_\.\(\)\[\]\-](19[0-9]{2}|20[0-9]{2})([ _\,\.\(\)\[\]\-][^0-9]|).*(19[0-9]{2}|20[0-9]{2})*",
                @"(.+[^_\,\.\(\)\[\]\-])[ _\.\(\)\[\]\-]+(19[0-9]{2}|20[0-9]{2})([ _\,\.\(\)\[\]\-][^0-9]|).*(19[0-9]{2}|20[0-9]{2})*"
            };

            CleanStrings = new[]
            {
                @"[ _\,\.\(\)\[\]\-](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|x264|h264|xvid|xvidvd|xxx|www.www|\[.*\])([ _\,\.\(\)\[\]\-]|$)",
                @"(\[.*\])"
            };

            SubtitleFileExtensions = new[]
            {
                ".srt",
                ".ssa",
                ".ass",
                ".sub"
            };

            SubtitleFlagDelimiters = new[]
            {
                '.'
            };

            SubtitleForcedFlags = new[]
            {
                "foreign",
                "forced"
            };

            SubtitleDefaultFlags = new[]
            {
                "default"
            };

            AlbumStackingPrefixes = new[]
            {
                "disc",
                "cd",
                "disk",
                "vol",
                "volume"
            };

            AudioFileExtensions = new[]
            {
                ".nsv",
                ".m4a",
                ".flac",
                ".aac",
                ".strm",
                ".pls",
                ".rm",
                ".mpa",
                ".wav",
                ".wma",
                ".ogg",
                ".opus",
                ".mp3",
                ".mp2",
                ".mod",
                ".amf",
                ".669",
                ".dmf",
                ".dsm",
                ".far",
                ".gdm",
                ".imf",
                ".it",
                ".m15",
                ".med",
                ".okt",
                ".s3m",
                ".stm",
                ".sfx",
                ".ult",
                ".uni",
                ".xm",
                ".sid",
                ".ac3",
                ".dts",
                ".cue",
                ".aif",
                ".aiff",
                ".ape",
                ".mac",
                ".mpc",
                ".mp+",
                ".mpp",
                ".shn",
                ".wv",
                ".nsf",
                ".spc",
                ".gym",
                ".adplug",
                ".adx",
                ".dsp",
                ".adp",
                ".ymf",
                ".ast",
                ".afc",
                ".hps",
                ".xsp",
                ".acc",
                ".m4b",
                ".oga",
                ".dsf",
                ".mka"
            };

            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]+)([^\\/]*)$"),
                new EpisodeExpression("([0-9]{4})[\\.-]([0-9]{2})[\\.-]([0-9]{2})", true)
                {
                    DateTimeFormats = new[]
                    {
                        "yyyy.MM.dd",
                        "yyyy-MM-dd",
                        "yyyy_MM_dd"
                    }
                },
                new EpisodeExpression("([0-9]{2})[\\.-]([0-9]{2})[\\.-]([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
                },
                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

                // [bar] Foo - 1 [baz]
                new EpisodeExpression(@".*?(\[.*?\])+.*?(?<seriesname>[\w\s]+?)[-\s_]+(?<epnumber>[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
                },
                // "Episode 16", "Episode 16 - Title"
                new EpisodeExpression(@".*[\\\/][^\\\/]* (?<epnumber>[0-9]{1,3})(-(?<endingepnumber>[0-9]{2,3}))*[^\\\/]*$")
                {
                    IsOptimistic = true,
                    IsNamed      = true
                }
            };

            EpisodeWithoutSeasonExpressions = new[]
            {
                @"[/\._ \-]()([0-9]+)(-[0-9]+)?"
            };

            EpisodeMultiPartExpressions = new[]
            {
                @"^[-_ex]+([0-9]+(?:(?:[a-i]|\\.[1-9])(?![0-9]))?)"
            };

            VideoExtraRules = new[]
            {
                new ExtraRule
                {
                    ExtraType = ExtraType.Trailer,
                    RuleType  = ExtraRuleType.Filename,
                    Token     = "trailer",
                    MediaType = MediaType.Video
                },
                new ExtraRule
                {
                    ExtraType = ExtraType.Trailer,
                    RuleType  = ExtraRuleType.Suffix,
                    Token     = "-trailer",
                    MediaType = MediaType.Video
                },
                new ExtraRule
                {
                    ExtraType = ExtraType.Trailer,
                    RuleType  = ExtraRuleType.Suffix,
                    Token     = ".trailer",
                    MediaType = MediaType.Video
                },
                new ExtraRule
                {
                    ExtraType = ExtraType.Trailer,
                    RuleType  = ExtraRuleType.Suffix,
                    Token     = "_trailer",
                    MediaType = MediaType.Video
                },
                new ExtraRule
                {
                    ExtraType = ExtraType.Trailer,
                    RuleType  = ExtraRuleType.Suffix,
                    Token     = " trailer",
                    MediaType = MediaType.Video
                },
                new ExtraRule
                {
                    ExtraType = ExtraType.Sample,
                    RuleType  = ExtraRuleType.Filename,
                    Token     = "sample",
                    MediaType = MediaType.Video
                },
                new ExtraRule
                {
                    ExtraType = ExtraType.Sample,
                    RuleType  = ExtraRuleType.Suffix,
                    Token     = "-sample",
                    MediaType = MediaType.Video
                },
                new ExtraRule
                {
                    ExtraType = ExtraType.Sample,
                    RuleType  = ExtraRuleType.Suffix,
                    Token     = ".sample",
                    MediaType = MediaType.Video
                },
                new ExtraRule
                {
                    ExtraType = ExtraType.Sample,
                    RuleType  = ExtraRuleType.Suffix,
                    Token     = "_sample",
                    MediaType = MediaType.Video
                },
                new ExtraRule
                {
                    ExtraType = ExtraType.Sample,
                    RuleType  = ExtraRuleType.Suffix,
                    Token     = " sample",
                    MediaType = MediaType.Video
                },
                new ExtraRule
                {
                    ExtraType = ExtraType.ThemeSong,
                    RuleType  = ExtraRuleType.Filename,
                    Token     = "theme",
                    MediaType = MediaType.Audio
                },
                new ExtraRule
                {
                    ExtraType = ExtraType.Scene,
                    RuleType  = ExtraRuleType.Suffix,
                    Token     = "-scene",
                    MediaType = MediaType.Video
                },
                new ExtraRule
                {
                    ExtraType = ExtraType.Clip,
                    RuleType  = ExtraRuleType.Suffix,
                    Token     = "-clip",
                    MediaType = MediaType.Video
                },
                new ExtraRule
                {
                    ExtraType = ExtraType.Interview,
                    RuleType  = ExtraRuleType.Suffix,
                    Token     = "-interview",
                    MediaType = MediaType.Video
                },
                new ExtraRule
                {
                    ExtraType = ExtraType.BehindTheScenes,
                    RuleType  = ExtraRuleType.Suffix,
                    Token     = "-behindthescenes",
                    MediaType = MediaType.Video
                },
                new ExtraRule
                {
                    ExtraType = ExtraType.DeletedScene,
                    RuleType  = ExtraRuleType.Suffix,
                    Token     = "-deleted",
                    MediaType = MediaType.Video
                },
                new ExtraRule
                {
                    ExtraType = ExtraType.Clip,
                    RuleType  = ExtraRuleType.Suffix,
                    Token     = "-featurette",
                    MediaType = MediaType.Video
                },
                new ExtraRule
                {
                    ExtraType = ExtraType.Clip,
                    RuleType  = ExtraRuleType.Suffix,
                    Token     = "-short",
                    MediaType = MediaType.Video
                },
                new ExtraRule
                {
                    ExtraType = ExtraType.BehindTheScenes,
                    RuleType  = ExtraRuleType.DirectoryName,
                    Token     = "behind the scenes",
                    MediaType = MediaType.Video,
                },
                new ExtraRule
                {
                    ExtraType = ExtraType.DeletedScene,
                    RuleType  = ExtraRuleType.DirectoryName,
                    Token     = "deleted scenes",
                    MediaType = MediaType.Video,
                },
                new ExtraRule
                {
                    ExtraType = ExtraType.Interview,
                    RuleType  = ExtraRuleType.DirectoryName,
                    Token     = "interviews",
                    MediaType = MediaType.Video,
                },
                new ExtraRule
                {
                    ExtraType = ExtraType.Scene,
                    RuleType  = ExtraRuleType.DirectoryName,
                    Token     = "scenes",
                    MediaType = MediaType.Video,
                },
                new ExtraRule
                {
                    ExtraType = ExtraType.Sample,
                    RuleType  = ExtraRuleType.DirectoryName,
                    Token     = "samples",
                    MediaType = MediaType.Video,
                },
                new ExtraRule
                {
                    ExtraType = ExtraType.Clip,
                    RuleType  = ExtraRuleType.DirectoryName,
                    Token     = "shorts",
                    MediaType = MediaType.Video,
                },
                new ExtraRule
                {
                    ExtraType = ExtraType.Clip,
                    RuleType  = ExtraRuleType.DirectoryName,
                    Token     = "featurettes",
                    MediaType = MediaType.Video,
                },
                new ExtraRule
                {
                    ExtraType = ExtraType.Unknown,
                    RuleType  = ExtraRuleType.DirectoryName,
                    Token     = "extras",
                    MediaType = MediaType.Video,
                },
            };

            Format3DRules = new[]
            {
                // Kodi rules:
                new Format3DRule
                {
                    PreceedingToken = "3d",
                    Token           = "hsbs"
                },
                new Format3DRule
                {
                    PreceedingToken = "3d",
                    Token           = "sbs"
                },
                new Format3DRule
                {
                    PreceedingToken = "3d",
                    Token           = "htab"
                },
                new Format3DRule
                {
                    PreceedingToken = "3d",
                    Token           = "tab"
                },
                // 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"
                }
            };
            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
                "(?<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]+)"
            };

            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",
                ".mxf"
            });

            MultipleEpisodeExpressions = new string[]
            {
                @".*(\\|\/)[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();

            VideoFileExtensions = extensions
                                  .Distinct(StringComparer.OrdinalIgnoreCase)
                                  .ToArray();

            Compile();
        }
Example #2
0
        /// <summary>
        /// Initializes a new instance of the <see cref="NamingOptions"/> class.
        /// </summary>
        public NamingOptions()
        {
            VideoFileExtensions = new[]
            {
                ".m4v",
                ".3gp",
                ".nsv",
                ".ts",
                ".ty",
                ".strm",
                ".rm",
                ".rmvb",
                ".ifo",
                ".mov",
                ".qt",
                ".divx",
                ".xvid",
                ".bivx",
                ".vob",
                ".nrg",
                ".img",
                ".iso",
                ".pva",
                ".wmv",
                ".asf",
                ".asx",
                ".ogm",
                ".m2v",
                ".avi",
                ".bin",
                ".dvr-ms",
                ".mpg",
                ".mpeg",
                ".mp4",
                ".mkv",
                ".avc",
                ".vp3",
                ".svq3",
                ".nuv",
                ".viv",
                ".dv",
                ".fli",
                ".flv",
                ".001",
                ".tp"
            };

            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[]
            {
                ".srt",
                ".ssa",
                ".ass",
                ".sub"
            };

            SubtitleFlagDelimiters = new[]
            {
                '.'
            };

            SubtitleForcedFlags = new[]
            {
                "foreign",
                "forced"
            };

            SubtitleDefaultFlags = new[]
            {
                "default"
            };

            AlbumStackingPrefixes = new[]
            {
                "disc",
                "cd",
                "disk",
                "vol",
                "volume"
            };

            AudioFileExtensions = new[]
            {
                ".nsv",
                ".m4a",
                ".flac",
                ".aac",
                ".strm",
                ".pls",
                ".rm",
                ".mpa",
                ".wav",
                ".wma",
                ".ogg",
                ".opus",
                ".mp3",
                ".mp2",
                ".mod",
                ".amf",
                ".669",
                ".dmf",
                ".dsm",
                ".far",
                ".gdm",
                ".imf",
                ".it",
                ".m15",
                ".med",
                ".okt",
                ".s3m",
                ".stm",
                ".sfx",
                ".ult",
                ".uni",
                ".xm",
                ".sid",
                ".ac3",
                ".dts",
                ".cue",
                ".aif",
                ".aiff",
                ".ape",
                ".mac",
                ".mpc",
                ".mp+",
                ".mpp",
                ".shn",
                ".wv",
                ".nsf",
                ".spc",
                ".gym",
                ".adplug",
                ".adx",
                ".dsp",
                ".adp",
                ".ymf",
                ".ast",
                ".afc",
                ".hps",
                ".xsp",
                ".acc",
                ".m4b",
                ".oga",
                ".dsf",
                ".mka"
            };

            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.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.ThemeVideo,
                    ExtraRuleType.DirectoryName,
                    "backdrops",
                    MediaType.Video),

                new ExtraRule(
                    ExtraType.ThemeSong,
                    ExtraRuleType.Filename,
                    "theme",
                    MediaType.Audio),

                new ExtraRule(
                    ExtraType.ThemeSong,
                    ExtraRuleType.DirectoryName,
                    "theme-music",
                    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.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)
            };

            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*$"
            };

            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",
                ".mxf"
            });

            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();

            VideoFileExtensions = extensions
                                  .Distinct(StringComparer.OrdinalIgnoreCase)
                                  .ToArray();

            AllExtrasTypesFolderNames = new Dictionary <string, ExtraType>(StringComparer.OrdinalIgnoreCase)
            {
                ["trailers"]          = ExtraType.Trailer,
                ["theme-music"]       = ExtraType.ThemeSong,
                ["backdrops"]         = ExtraType.ThemeVideo,
                ["extras"]            = ExtraType.Unknown,
                ["behind the scenes"] = ExtraType.BehindTheScenes,
                ["deleted scenes"]    = ExtraType.DeletedScene,
                ["interviews"]        = ExtraType.Interview,
                ["scenes"]            = ExtraType.Scene,
                ["samples"]           = ExtraType.Sample,
                ["shorts"]            = ExtraType.Clip,
                ["featurettes"]       = ExtraType.Clip
            };

            Compile();
        }
Example #3
0
        /// <summary>
        /// Initializes a new instance of the <see cref="NamingOptions"/> class.
        /// </summary>
        public NamingOptions()
        {
            VideoFileExtensions = new[]
            {
                ".m4v",
                ".3gp",
                ".nsv",
                ".ts",
                ".ty",
                ".strm",
                ".rm",
                ".rmvb",
                ".ifo",
                ".mov",
                ".qt",
                ".divx",
                ".xvid",
                ".bivx",
                ".vob",
                ".nrg",
                ".img",
                ".iso",
                ".pva",
                ".wmv",
                ".asf",
                ".asx",
                ".ogm",
                ".m2v",
                ".avi",
                ".bin",
                ".dvr-ms",
                ".mpg",
                ".mpeg",
                ".mp4",
                ".mkv",
                ".avc",
                ".vp3",
                ".svq3",
                ".nuv",
                ".viv",
                ".dv",
                ".fli",
                ".flv",
                ".001",
                ".tp"
            };

            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")
            };

            VideoFileStackingExpressions = new[]
            {
                "(?<title>.*?)(?<volume>[ _.-]*(?:cd|dvd|p(?:ar)?t|dis[ck])[ _.-]*[0-9]+)(?<ignore>.*?)(?<extension>\\.[^.]+)$",
                "(?<title>.*?)(?<volume>[ _.-]*(?:cd|dvd|p(?:ar)?t|dis[ck])[ _.-]*[a-d])(?<ignore>.*?)(?<extension>\\.[^.]+)$",
                "(?<title>.*?)(?<volume>[ ._-]*[a-d])(?<ignore>.*?)(?<extension>\\.[^.]+)$"
            };

            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[]
            {
                @"^(?:(?<header>\[雪飘工作室]\[|\[FLsnow]\[|\[Nekomoe\ kissaten]\[|\[LKSUB]\[|\[UHA-WINGS]\[|\[BDRIP]\[|\[YG&Neo\.sub]\[|\[KTXP]\[)(?<seriesname>[^[\]【]+)|(?:(?<seriesname>\d+[ .][^[\]【]+)\.bhd)|(?:【[^】]*】|\[[^\]]*])*(?<seriesname>[^[\]【]+?)(?:第[一二三四五六七八九1-9]季[ ]?)?(?: - \d+)?(?:\(480P.*)?(?:【[^】]*】|\[[^\]]*])*$|^\[(?<seriesname>[^\]]*)])"
            };

            SubtitleFileExtensions = new[]
            {
                ".srt",
                ".ssa",
                ".ass",
                ".sub"
            };

            SubtitleFlagDelimiters = new[]
            {
                '.'
            };

            SubtitleForcedFlags = new[]
            {
                "foreign",
                "forced"
            };

            SubtitleDefaultFlags = new[]
            {
                "default"
            };

            AlbumStackingPrefixes = new[]
            {
                "disc",
                "cd",
                "disk",
                "vol",
                "volume"
            };

            AudioFileExtensions = new[]
            {
                ".nsv",
                ".m4a",
                ".flac",
                ".aac",
                ".strm",
                ".pls",
                ".rm",
                ".mpa",
                ".wav",
                ".wma",
                ".ogg",
                ".opus",
                ".mp3",
                ".mp2",
                ".mod",
                ".amf",
                ".669",
                ".dmf",
                ".dsm",
                ".far",
                ".gdm",
                ".imf",
                ".it",
                ".m15",
                ".med",
                ".okt",
                ".s3m",
                ".stm",
                ".sfx",
                ".ult",
                ".uni",
                ".xm",
                ".sid",
                ".ac3",
                ".dts",
                ".cue",
                ".aif",
                ".aiff",
                ".ape",
                ".mac",
                ".mpc",
                ".mp+",
                ".mpp",
                ".shn",
                ".wv",
                ".nsf",
                ".spc",
                ".gym",
                ".adplug",
                ".adx",
                ".dsp",
                ".adp",
                ".ymf",
                ".ast",
                ".afc",
                ".hps",
                ".xsp",
                ".acc",
                ".m4b",
                ".oga",
                ".dsf",
                ".mka"
            };

            EpisodeExpressions = new[]
            {
                // 用.fsx拼接编写的针对TV目录,适配各种中文格式的超长正则表达式
                new EpisodeExpression(@"^(?:.*[\\/][[【](?<header>Nekomoe kissaten|LKSUB|UHA-WINGS|YG&Neo.sub|KTXP|動畫瘋|HYSUB|UHA-WINGS|Nekomoe kissaten&VCB-Studio|BDRIP)[\]】]\[?\ ?(?<seriesname>.+?)\ ?(?:第(?<seasonnumber>[一二三四五六七八九十1-9])季[ ]?|[Ss](?<seasonnumber>[1-9]))?(?<specialSeason>\[特別篇])?(?:\[年齡限制版])?(?:\ (?<epnumber>[0-9.]+)\ |]?\[(?<epnumber>[0-9.]+)])?(?:\[[^\]]*])*\.[A-z1-9]+|(?<seriesname>.+?)[\\/]\k<seriesname>\ (?<epnumber>[0-9.]+)\.[A-z1-9]+|(?<specialSeason>OVA|OAD|SP|特别篇|特別篇|【?剧场版】?)?(?<seriesname>.*?)\ ?(?:TV|MV|1080P|720P)?(?:第(?<seasonnumber>[一二三四五六七八九十1-9])季[ ]?|(?<seasonnumber>[1-9])|(?<seasonnumber>[Ⅰ-Ⅹ]))?(?<specialSeason>OVA|OAD|SP|特别篇|特別篇|【?剧场版】?)?(?:\[[^\]]*])*(?:\d{4})?[\\/](?:【漫锋网】)?(?:TV|MV|1080P|720P)?[\ _]?(?:第(?<seasonnumber>[一二三四五六七八九十1-9])季[ ]?)?(?:(?:(?:\[.*?])?\[)?(?:(?<specialSeason>OVA|OAD|SP|特别篇|特別篇|【?剧场版】?)|OP-ED|PV-CF|MV|AD|\ The\ Movie\ 简体).*?)?(?:(?<=[\\/ ])Menu)?(?:(?<=[\\/ ]|EP|OVA|OAD|SP|特别篇|特別篇|【?剧场版】?|[-_\u4e00-\u9fa5])(?<epnumber>[0-9.]+))?(?:\[[^\]]*])*(?:.*?\.bhd)?(?:\..*?)?(?:\ \(.*?)?\.[A-z1-9]+|(?<specialSeason>OVA|OAD|SP|特别篇|特別篇|【?剧场版】?)?.*?\ ?(?:TV|MV|1080P|720P)?(?:第(?<seasonnumber>[一二三四五六七八九十1-9])季[ ]?|(?<seasonnumber>[1-9])|(?<seasonnumber>[Ⅰ-Ⅹ]))?(?<specialSeason>OVA|OAD|SP|特别篇|特別篇|【?剧场版】?)?(?:\[[^\]]*])*(?:\d{4})?[\\/](?:【漫锋网】)?(?<seriesname>.*?)(?:TV|MV|1080P|720P)?[\ _]?(?:第(?<seasonnumber>[一二三四五六七八九十1-9])季[ ]?)?(?:(?:(?:\[.*?])?\[)?(?:(?<specialSeason>OVA|OAD|SP|特别篇|特別篇|【?剧场版】?)|OP-ED|PV-CF|MV|AD|\ The\ Movie\ 简体).*?)?(?:(?<=[\\/ ])Menu)?(?:(?<=[\\/ ]|EP|OVA|OAD|SP|特别篇|特別篇|【?剧场版】?|[-_\u4e00-\u9fa5])(?<epnumber>[0-9.]+))?(?:\[[^\]]*])*(?:.*?\.bhd)?(?:\..*?)?(?:\ \(.*?)?\.[A-z1-9]+)$")
                {
                    IsNamed = true
                },
                // *** 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]+)([^\\/]*)$"),
                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

                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
                },
                // "Episode 16", "Episode 16 - Title"
                new EpisodeExpression(@".*[\\\/][^\\\/]* (?<epnumber>[0-9]{1,3})(-(?<endingepnumber>[0-9]{2,3}))*[^\\\/]*$")
                {
                    IsOptimistic = true,
                    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.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.Clip,
                    ExtraRuleType.Suffix,
                    "-featurette",
                    MediaType.Video),

                new ExtraRule(
                    ExtraType.Clip,
                    ExtraRuleType.Suffix,
                    "-short",
                    MediaType.Video),

                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),
            };

            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*$"
            };

            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",
                ".mxf"
            });

            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();

            VideoFileExtensions = extensions
                                  .Distinct(StringComparer.OrdinalIgnoreCase)
                                  .ToArray();

            Compile();
        }
Example #4
0
        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"
                }
            });
        }
Example #5
0
        public NamingOptions()
        {
            VideoFileExtensions = new[]
            {
                ".m4v",
                ".3gp",
                ".nsv",
                ".ts",
                ".ty",
                ".strm",
                ".rm",
                ".rmvb",
                ".ifo",
                ".mov",
                ".qt",
                ".divx",
                ".xvid",
                ".bivx",
                ".vob",
                ".nrg",
                ".img",
                ".iso",
                ".pva",
                ".wmv",
                ".asf",
                ".asx",
                ".ogm",
                ".m2v",
                ".avi",
                ".bin",
                ".dvr-ms",
                ".mpg",
                ".mpeg",
                ".mp4",
                ".mkv",
                ".avc",
                ".vp3",
                ".svq3",
                ".nuv",
                ".viv",
                ".dv",
                ".fli",
                ".flv",
                ".001",
                ".tp"
            };

            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"
                }
            };

            VideoFileStackingExpressions = new[]
            {
                "(.*?)([ _.-]*(?:cd|dvd|p(?:ar)?t|dis[ck])[ _.-]*[0-9]+)(.*?)(\\.[^.]+)$",
                "(.*?)([ _.-]*(?:cd|dvd|p(?:ar)?t|dis[ck])[ _.-]*[a-d])(.*?)(\\.[^.]+)$",
                "(.*?)([ ._-]*[a-d])(.*?)(\\.[^.]+)$"
            };

            CleanDateTimes = new[]
            {
                @"(.+[^ _\,\.\(\)\[\]\-])[ _\.\(\)\[\]\-]+(19[0-9][0-9]|20[0-1][0-9])([ _\,\.\(\)\[\]\-][^0-9]|$)"
            };

            CleanStrings = new[]
            {
                @"[ _\,\.\(\)\[\]\-](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|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|x264|h264|xvid|xvidvd|xxx|www.www|\[.*\])([ _\,\.\(\)\[\]\-]|$)",
                @"[ _\,\.\(\)\[\]\-](3d|sbs|tab|hsbs|htab|mvc|\[.*\])([ _\,\.\(\)\[\]\-]|$)",
                @"(\[.*\])"
            };

            SubtitleFileExtensions = new[]
            {
                ".srt",
                ".ssa",
                ".ass",
                ".sub"
            };

            SubtitleFlagDelimiters = new[]
            {
                '.'
            };

            SubtitleForcedFlags = new[]
            {
                "foreign",
                "forced"
            };

            SubtitleDefaultFlags = new[]
            {
                "default"
            };

            AlbumStackingPrefixes = new[]
            {
                "disc",
                "cd",
                "disk",
                "vol",
                "volume"
            };

            AudioFileExtensions = new[]
            {
                ".nsv",
                ".m4a",
                ".flac",
                ".aac",
                ".strm",
                ".pls",
                ".rm",
                ".mpa",
                ".wav",
                ".wma",
                ".ogg",
                ".opus",
                ".mp3",
                ".mp2",
                ".mod",
                ".amf",
                ".669",
                ".dmf",
                ".dsm",
                ".far",
                ".gdm",
                ".imf",
                ".it",
                ".m15",
                ".med",
                ".okt",
                ".s3m",
                ".stm",
                ".sfx",
                ".ult",
                ".uni",
                ".xm",
                ".sid",
                ".ac3",
                ".dts",
                ".cue",
                ".aif",
                ".aiff",
                ".ape",
                ".mac",
                ".mpc",
                ".mp+",
                ".mpp",
                ".shn",
                ".wv",
                ".nsf",
                ".spc",
                ".gym",
                ".adplug",
                ".adx",
                ".dsp",
                ".adp",
                ".ymf",
                ".ast",
                ".afc",
                ".hps",
                ".xsp",
                ".acc",
                ".m4b",
                ".oga",
                ".dsf",
                ".mka"
            };

            // Notice that these expressions need to match the whole path without the extension
            EpisodeExpressions = new[]
            {
                // *** Start Kodi Standard Naming
                // sname.s01.e01, sname.s01_e01, S01E02 sname, 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]+)([^\\/]*)$"),

                // 0001-01-01, 0001 01 01   [is this useless because they do not match the paths?]
                new EpisodeExpression("([0-9]{4})[\\.-]([0-9]{2})[\\.-]([0-9]{2})", true)
                {
                    DateTimeFormats = new[]
                    {
                        "yyyy.MM.dd",
                        "yyyy-MM-dd",
                        "yyyy_MM_dd"
                    }
                },
                // 01-01-0001 [is this useless because they do not match the paths?]
                new EpisodeExpression("([0-9]{2})[\\.-]([0-9]{2})[\\.-]([0-9]{4})", true)
                {
                    DateTimeFormats = new[]
                    {
                        "dd.MM.yyyy",
                        "dd-MM-yyyy",
                        "dd_MM_yyyy"
                    }
                },
                // 01x01 foo
                new EpisodeExpression("[\\\\/\\._ \\[\\(-]([0-9]+)x([0-9]+(?:(?:[a-i]|\\.[1-9])(?![0-9]))?)([^\\\\/]*)$")
                {
                    SupportsAbsoluteEpisodeNumbers = true
                },
                // sname0001/0000101 [last two numbers are episodes, the rest is season] {also why is it matched weirdly after the series name?}
                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
                },
                // what is this? are we matching the letter a and r? and afterwards some ivx?
                new EpisodeExpression("[\\/._ -]p(?:ar)?t[_. -]()([ivx]+|[0-9]+)([._ -][^\\/]*)$")
                {
                    SupportsAbsoluteEpisodeNumbers = true
                },
                // *** End Kodi Standard Naming

                // S0001E001 (or less leading zeroes and lower case)
                new EpisodeExpression(@".*(\\|\/)[sS]?(?<seasonnumber>\d{1,4})[xX](?<epnumber>\d{1,3})[^\\\/]*$")
                {
                    IsNamed = true
                },
                // S0001xE001 (or less leading zeroes and lower case)
                new EpisodeExpression(@".*(\\|\/)[sS](?<seasonnumber>\d{1,4})[x,X]?[eE](?<epnumber>\d{1,3})[^\\\/]*$")
                {
                    IsNamed = true
                },
                // sname S0001X001/S0001X001
                new EpisodeExpression(@".*(\\|\/)(?<seriesname>((?![sS]?\d{1,4}[xX]\d{1,3})[^\\\/])*)?([sS]?(?<seasonnumber>\d{1,4})[xX](?<epnumber>\d{1,3}))[^\\\/]*$")
                {
                    IsNamed = true
                },
                // foo/S0001.E001 (or x instead of .)
                new EpisodeExpression(@".*(\\|\/)(?<seriesname>[^\\\/]*)[sS](?<seasonnumber>\d{1,4})[xX\.]?[eE](?<epnumber>\d{1,3})[^\\\/]*$")
                {
                    IsNamed = true
                },
                // "01"
                new EpisodeExpression(@".*[\\\/](?<epnumber>\d{1,3})(-(?<endingepnumber>\d{2,3}))*\.\w+$")
                {
                    IsOptimistic = true,
                    IsNamed      = true
                },

                // "1-12 episode title"
                new EpisodeExpression(@"([0-9]+)-([0-9]+)"),

                // "01 - foo", "01-foo.avi"
                new EpisodeExpression(@".*(\\|\/)(?<epnumber>\d{1,3})(-(?<endingepnumber>\d{2,3}))*\s?-\s?[^\\\/]*$")
                {
                    IsOptimistic = true,
                    IsNamed      = true
                },

                // "01.foo.avi"
                new EpisodeExpression(@".*(\\|\/)(?<epnumber>\d{1,3})(-(?<endingepnumber>\d{2,3}))*\.[^\\\/]+$")
                {
                    IsOptimistic = true,
                    IsNamed      = true
                },

                // "foo - 01", "foo 2 - 01", "foo - 01 foo", "foo 2 - 01 foo", "foo - 01 - foo", "foo 2 - 01 - bar"
                new EpisodeExpression(@".*[\\\/][^\\\/]* - (?<epnumber>\d{1,3})(-(?<endingepnumber>\d{2,3}))*[^\\\/]*$")
                {
                    IsOptimistic = true,
                    IsNamed      = true
                },

                // "01 episode title.avi"
                new EpisodeExpression(@"[Ss]eason[\._ ](?<seasonnumber>[0-9]+)[\\\/](?<epnumber>\d{1,3})([^\\\/]*)$")
                {
                    IsOptimistic = true,
                    IsNamed      = true
                },
                // "Episode 16", "Episode 16 - Title"
                new EpisodeExpression(@".*[\\\/][^\\\/]* (?<epnumber>\d{1,3})(-(?<endingepnumber>\d{2,3}))*[^\\\/]*$")
                {
                    IsOptimistic = true,
                    IsNamed      = true
                },
                // "[*] Name 01 [*]
                new EpisodeExpression(@".*?\[.*?\].*?(?<seriesname>\S+?(\s.+?)*?)[-\s]+(?<epnumber>[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",
                    RuleType  = ExtraRuleType.Filename,
                    Token     = "trailer",
                    MediaType = MediaType.Video
                },
                new ExtraRule
                {
                    ExtraType = "trailer",
                    RuleType  = ExtraRuleType.Suffix,
                    Token     = "-trailer",
                    MediaType = MediaType.Video
                },
                new ExtraRule
                {
                    ExtraType = "trailer",
                    RuleType  = ExtraRuleType.Suffix,
                    Token     = ".trailer",
                    MediaType = MediaType.Video
                },
                new ExtraRule
                {
                    ExtraType = "trailer",
                    RuleType  = ExtraRuleType.Suffix,
                    Token     = "_trailer",
                    MediaType = MediaType.Video
                },
                new ExtraRule
                {
                    ExtraType = "trailer",
                    RuleType  = ExtraRuleType.Suffix,
                    Token     = " trailer",
                    MediaType = MediaType.Video
                },
                new ExtraRule
                {
                    ExtraType = "sample",
                    RuleType  = ExtraRuleType.Filename,
                    Token     = "sample",
                    MediaType = MediaType.Video
                },
                new ExtraRule
                {
                    ExtraType = "sample",
                    RuleType  = ExtraRuleType.Suffix,
                    Token     = "-sample",
                    MediaType = MediaType.Video
                },
                new ExtraRule
                {
                    ExtraType = "sample",
                    RuleType  = ExtraRuleType.Suffix,
                    Token     = ".sample",
                    MediaType = MediaType.Video
                },
                new ExtraRule
                {
                    ExtraType = "sample",
                    RuleType  = ExtraRuleType.Suffix,
                    Token     = "_sample",
                    MediaType = MediaType.Video
                },
                new ExtraRule
                {
                    ExtraType = "sample",
                    RuleType  = ExtraRuleType.Suffix,
                    Token     = " sample",
                    MediaType = MediaType.Video
                },
                new ExtraRule
                {
                    ExtraType = "themesong",
                    RuleType  = ExtraRuleType.Filename,
                    Token     = "theme",
                    MediaType = MediaType.Audio
                },
                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
                },
                new ExtraRule
                {
                    ExtraType = "featurette",
                    RuleType  = ExtraRuleType.Suffix,
                    Token     = "-featurette",
                    MediaType = MediaType.Video
                },
                new ExtraRule
                {
                    ExtraType = "short",
                    RuleType  = ExtraRuleType.Suffix,
                    Token     = "-short",
                    MediaType = MediaType.Video
                }
            };

            Format3DRules = new[]
            {
                // Kodi rules:
                new Format3DRule
                {
                    PreceedingToken = "3d",
                    Token           = "hsbs"
                },
                new Format3DRule
                {
                    PreceedingToken = "3d",
                    Token           = "sbs"
                },
                new Format3DRule
                {
                    PreceedingToken = "3d",
                    Token           = "htab"
                },
                new Format3DRule
                {
                    PreceedingToken = "3d",
                    Token           = "tab"
                },
                // 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"
                }
            };
            AudioBookPartsExpressions = new[]
            {
                // Detect specified chapters, like CH 01
                @"ch(?:apter)?[\s_-]?(?<chapter>\d+)",
                // Detect specified parts, like Part 02
                @"p(?:ar)?t[\s_-]?(?<part>\d+)",
                // Chapter is often beginning of filename
                @"^(?<chapter>\d+)",
                // Part if often ending of filename
                @"(?<part>\d+)$",
                // Sometimes named as 0001_005 (chapter_part)
                @"(?<chapter>\d+)_(?<part>\d+)",
                // Some audiobooks are ripped from cd's, and will be named by disk number.
                @"dis(?:c|k)[\s_-]?(?<chapter>\d+)"
            };

            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",
                ".mxf"
            });

            MultipleEpisodeExpressions = new string[]
            {
                @".*(\\|\/)[sS]?(?<seasonnumber>\d{1,4})[xX](?<epnumber>\d{1,3})((-| - )\d{1,4}[eExX](?<endingepnumber>\d{1,3}))+[^\\\/]*$",
                @".*(\\|\/)[sS]?(?<seasonnumber>\d{1,4})[xX](?<epnumber>\d{1,3})((-| - )\d{1,4}[xX][eE](?<endingepnumber>\d{1,3}))+[^\\\/]*$",
                @".*(\\|\/)[sS]?(?<seasonnumber>\d{1,4})[xX](?<epnumber>\d{1,3})((-| - )?[xXeE](?<endingepnumber>\d{1,3}))+[^\\\/]*$",
                @".*(\\|\/)[sS]?(?<seasonnumber>\d{1,4})[xX](?<epnumber>\d{1,3})(-[xE]?[eE]?(?<endingepnumber>\d{1,3}))+[^\\\/]*$",
                @".*(\\|\/)(?<seriesname>((?![sS]?\d{1,4}[xX]\d{1,3})[^\\\/])*)?([sS]?(?<seasonnumber>\d{1,4})[xX](?<epnumber>\d{1,3}))((-| - )\d{1,4}[xXeE](?<endingepnumber>\d{1,3}))+[^\\\/]*$",
                @".*(\\|\/)(?<seriesname>((?![sS]?\d{1,4}[xX]\d{1,3})[^\\\/])*)?([sS]?(?<seasonnumber>\d{1,4})[xX](?<epnumber>\d{1,3}))((-| - )\d{1,4}[xX][eE](?<endingepnumber>\d{1,3}))+[^\\\/]*$",
                @".*(\\|\/)(?<seriesname>((?![sS]?\d{1,4}[xX]\d{1,3})[^\\\/])*)?([sS]?(?<seasonnumber>\d{1,4})[xX](?<epnumber>\d{1,3}))((-| - )?[xXeE](?<endingepnumber>\d{1,3}))+[^\\\/]*$",
                @".*(\\|\/)(?<seriesname>((?![sS]?\d{1,4}[xX]\d{1,3})[^\\\/])*)?([sS]?(?<seasonnumber>\d{1,4})[xX](?<epnumber>\d{1,3}))(-[xX]?[eE]?(?<endingepnumber>\d{1,3}))+[^\\\/]*$",
                @".*(\\|\/)(?<seriesname>[^\\\/]*)[sS](?<seasonnumber>\d{1,4})[xX\.]?[eE](?<epnumber>\d{1,3})((-| - )?[xXeE](?<endingepnumber>\d{1,3}))+[^\\\/]*$",
                @".*(\\|\/)(?<seriesname>[^\\\/]*)[sS](?<seasonnumber>\d{1,4})[xX\.]?[eE](?<epnumber>\d{1,3})(-[xX]?[eE]?(?<endingepnumber>\d{1,3}))+[^\\\/]*$"
            }.Select(i => new EpisodeExpression(i)
            {
                IsNamed = true
            }).ToArray();

            VideoFileExtensions = extensions
                                  .Distinct(StringComparer.OrdinalIgnoreCase)
                                  .ToArray();

            Compile();
        }