< Summary - Jellyfin

Information
Class: Emby.Naming.Common.NamingOptions
Assembly: Emby.Naming
File(s): /srv/git/jellyfin/Emby.Naming/Common/NamingOptions.cs
Line coverage
100%
Covered lines: 739
Uncovered lines: 0
Coverable lines: 739
Total lines: 922
Line coverage: 100%
Branch coverage
N/A
Covered branches: 0
Total branches: 0
Branch coverage: N/A
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Coverage history

Coverage history 0 25 50 75 100 3/26/2026 - 12:14:14 AM Line coverage: 100% (731/731) Total lines: 9145/5/2026 - 12:15:44 AM Line coverage: 100% (739/739) Total lines: 922

Coverage delta

Coverage delta 1 -1

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.ctor()100%11100%
Compile()100%11100%
Compile(...)100%11100%

File(s)

/srv/git/jellyfin/Emby.Naming/Common/NamingOptions.cs

#LineLine coverage
 1#pragma warning disable CA1819
 2
 3using System;
 4using System.Collections.Generic;
 5using System.Linq;
 6using System.Text.RegularExpressions;
 7using Emby.Naming.Video;
 8using MediaBrowser.Model.Entities;
 9
 10// ReSharper disable StringLiteralTypo
 11
 12namespace Emby.Naming.Common
 13{
 14    /// <summary>
 15    /// Big ugly class containing lot of different naming options that should be split and injected instead of passes ev
 16    /// </summary>
 17    public class NamingOptions
 18    {
 19        /// <summary>
 20        /// Initializes a new instance of the <see cref="NamingOptions"/> class.
 21        /// </summary>
 22        public NamingOptions()
 23        {
 72324            VideoFileExtensions =
 72325            [
 72326                ".001",
 72327                ".3g2",
 72328                ".3gp",
 72329                ".amv",
 72330                ".asf",
 72331                ".asx",
 72332                ".avi",
 72333                ".bin",
 72334                ".bivx",
 72335                ".divx",
 72336                ".dv",
 72337                ".dvr-ms",
 72338                ".f4v",
 72339                ".fli",
 72340                ".flv",
 72341                ".ifo",
 72342                ".img",
 72343                ".iso",
 72344                ".m2t",
 72345                ".m2ts",
 72346                ".m2v",
 72347                ".m4v",
 72348                ".mkv",
 72349                ".mk3d",
 72350                ".mov",
 72351                ".mp4",
 72352                ".mpe",
 72353                ".mpeg",
 72354                ".mpg",
 72355                ".mts",
 72356                ".mxf",
 72357                ".nrg",
 72358                ".nsv",
 72359                ".nuv",
 72360                ".ogg",
 72361                ".ogm",
 72362                ".ogv",
 72363                ".pva",
 72364                ".qt",
 72365                ".rec",
 72366                ".rm",
 72367                ".rmvb",
 72368                ".strm",
 72369                ".svq3",
 72370                ".tp",
 72371                ".ts",
 72372                ".ty",
 72373                ".viv",
 72374                ".vob",
 72375                ".vp3",
 72376                ".webm",
 72377                ".wmv",
 72378                ".wtv",
 72379                ".xvid"
 72380            ];
 81
 72382            VideoFlagDelimiters =
 72383            [
 72384                '(',
 72385                ')',
 72386                '-',
 72387                '.',
 72388                '_',
 72389                '[',
 72390                ']'
 72391            ];
 92
 72393            StubFileExtensions =
 72394            [
 72395                ".disc"
 72396            ];
 97
 72398            StubTypes =
 72399            [
 723100                new StubTypeRule(
 723101                    stubType: "dvd",
 723102                    token: "dvd"),
 723103
 723104                new StubTypeRule(
 723105                    stubType: "hddvd",
 723106                    token: "hddvd"),
 723107
 723108                new StubTypeRule(
 723109                    stubType: "bluray",
 723110                    token: "bluray"),
 723111
 723112                new StubTypeRule(
 723113                    stubType: "bluray",
 723114                    token: "brrip"),
 723115
 723116                new StubTypeRule(
 723117                    stubType: "bluray",
 723118                    token: "bd25"),
 723119
 723120                new StubTypeRule(
 723121                    stubType: "bluray",
 723122                    token: "bd50"),
 723123
 723124                new StubTypeRule(
 723125                    stubType: "vhs",
 723126                    token: "vhs"),
 723127
 723128                new StubTypeRule(
 723129                    stubType: "tv",
 723130                    token: "HDTV"),
 723131
 723132                new StubTypeRule(
 723133                    stubType: "tv",
 723134                    token: "PDTV"),
 723135
 723136                new StubTypeRule(
 723137                    stubType: "tv",
 723138                    token: "DSR")
 723139            ];
 140
 723141            VideoFileStackingRules =
 723142            [
 723143                new FileStackRule(@"^(?<filename>.*?)(?:(?<=[\]\)\}])|[ _.-]+)[\(\[]?(?<parttype>cd|dvd|part|pt|dis[ck])
 723144                new FileStackRule(@"^(?<filename>.*?)(?:(?<=[\]\)\}])|[ _.-]+)[\(\[]?(?<parttype>cd|dvd|part|pt|dis[ck])
 723145            ];
 146
 723147            CleanDateTimes =
 723148            [
 723149                @"(.+[^_\,\.\(\)\[\]\-])[_\.\(\)\[\]\-](19[0-9]{2}|20[0-9]{2})(?![0-9]+|\W[0-9]{2}\W[0-9]{2})([ _\,\.\(\
 723150                @"(.+[^_\,\.\(\)\[\]\-])[ _\.\(\)\[\]\-]+(19[0-9]{2}|20[0-9]{2})(?![0-9]+|\W[0-9]{2}\W[0-9]{2})([ _\,\.\
 723151            ];
 152
 723153            CleanStrings =
 723154            [
 723155                @"^\s*(?<cleaned>.+?)[ _\,\.\(\)\[\]\-](3d|sbs|tab|hsbs|htab|mvc|HDR|HDC|UHD|UltraHD|4k|ac3|dts|custom|d
 723156                @"^\s*(?<cleaned>.+?)((\s*\[[^\]]+\]\s*)+)(\.[^\s]+)?$",
 723157                @"^\s*(?<cleaned>.+?)\WE[0-9]+(-|~)E?[0-9]+(\W|$)",
 723158                @"^\s*\[[^\]]+\](?!\.\w+$)\s*(?<cleaned>.+)",
 723159                @"^\s*(?<cleaned>.+?)\s+-\s+[0-9]+\s*$",
 723160                @"^\s*(?<cleaned>.+?)(([-._ ](trailer|sample))|-(scene|clip|behindthescenes|deleted|deletedscene|feature
 723161            ];
 162
 723163            SubtitleFileExtensions =
 723164            [
 723165                ".ass",
 723166                ".mks",
 723167                ".sami",
 723168                ".smi",
 723169                ".srt",
 723170                ".ssa",
 723171                ".sub",
 723172                ".sup",
 723173                ".vtt",
 723174            ];
 175
 723176            LyricFileExtensions =
 723177            [
 723178                ".lrc",
 723179                ".elrc",
 723180                ".txt"
 723181            ];
 182
 723183            AlbumStackingPrefixes =
 723184            [
 723185                "cd",
 723186                "digital media",
 723187                "disc",
 723188                "disk",
 723189                "vol",
 723190                "volume",
 723191                "part",
 723192                "act"
 723193            ];
 194
 723195            ArtistSubfolders =
 723196            [
 723197                "albums",
 723198                "broadcasts",
 723199                "bootlegs",
 723200                "compilations",
 723201                "dj-mixes",
 723202                "eps",
 723203                "live",
 723204                "mixtapes",
 723205                "others",
 723206                "remixes",
 723207                "singles",
 723208                "soundtracks",
 723209                "spokenwords",
 723210                "streets"
 723211            ];
 212
 723213            AudioFileExtensions =
 723214            [
 723215                ".669",
 723216                ".3gp",
 723217                ".aa",
 723218                ".aac",
 723219                ".aax",
 723220                ".ac3",
 723221                ".act",
 723222                ".adp",
 723223                ".adplug",
 723224                ".adx",
 723225                ".afc",
 723226                ".amf",
 723227                ".aif",
 723228                ".aifc",
 723229                ".aiff",
 723230                ".alac",
 723231                ".amr",
 723232                ".ape",
 723233                ".ast",
 723234                ".au",
 723235                ".awb",
 723236                ".cda",
 723237                ".cue",
 723238                ".dmf",
 723239                ".dsf",
 723240                ".dsm",
 723241                ".dsp",
 723242                ".dts",
 723243                ".dvf",
 723244                ".eac3",
 723245                ".ec3",
 723246                ".far",
 723247                ".flac",
 723248                ".gdm",
 723249                ".gsm",
 723250                ".gym",
 723251                ".hps",
 723252                ".imf",
 723253                ".it",
 723254                ".m15",
 723255                ".m4a",
 723256                ".m4b",
 723257                ".mac",
 723258                ".med",
 723259                ".mka",
 723260                ".mmf",
 723261                ".mod",
 723262                ".mogg",
 723263                ".mp2",
 723264                ".mp3",
 723265                ".mpa",
 723266                ".mpc",
 723267                ".mpp",
 723268                ".mp+",
 723269                ".msv",
 723270                ".nmf",
 723271                ".nsf",
 723272                ".nsv",
 723273                ".oga",
 723274                ".ogg",
 723275                ".okt",
 723276                ".opus",
 723277                ".pls",
 723278                ".ra",
 723279                ".rf64",
 723280                ".rm",
 723281                ".s3m",
 723282                ".sfx",
 723283                ".shn",
 723284                ".sid",
 723285                ".stm",
 723286                ".strm",
 723287                ".ult",
 723288                ".uni",
 723289                ".vox",
 723290                ".wav",
 723291                ".wma",
 723292                ".wv",
 723293                ".xm",
 723294                ".xsp",
 723295                ".ymf"
 723296            ];
 297
 723298            MediaFlagDelimiters =
 723299            [
 723300                '.'
 723301            ];
 302
 723303            MediaForcedFlags =
 723304            [
 723305                "foreign",
 723306                "forced"
 723307            ];
 308
 723309            MediaDefaultFlags =
 723310            [
 723311                "default"
 723312            ];
 313
 723314            MediaHearingImpairedFlags =
 723315            [
 723316                "cc",
 723317                "hi",
 723318                "sdh"
 723319            ];
 320
 723321            EpisodeExpressions =
 723322            [
 723323                // *** Begin Kodi Standard Naming
 723324                // <!-- foo.s01.e01, foo.s01_e01, S01E02 foo, S01 - E02 -->
 723325                new EpisodeExpression(@".*(\\|\/)(?<seriesname>((?![Ss]([0-9]+)[][ ._-]*[Ee]([0-9]+))[^\\\/])*)?[Ss](?<s
 723326                {
 723327                    IsNamed = true
 723328                },
 723329                // <!-- foo.ep01, foo.EP_01 -->
 723330                new EpisodeExpression(@"[\._ -]()[Ee][Pp]_?([0-9]+)([^\\/]*)$"),
 723331                // <!-- foo.E01., foo.e01. -->
 723332                new EpisodeExpression(@"[^\\/]*?()\.?[Ee]([0-9]+)\.([^\\/]*)$"),
 723333                new EpisodeExpression("(?<year>[0-9]{4})[._ -](?<month>[0-9]{2})[._ -](?<day>[0-9]{2})", true)
 723334                {
 723335                    DateTimeFormats =
 723336                    [
 723337                        "yyyy.MM.dd",
 723338                        "yyyy-MM-dd",
 723339                        "yyyy_MM_dd",
 723340                        "yyyy MM dd"
 723341                    ]
 723342                },
 723343                new EpisodeExpression("(?<day>[0-9]{2})[._ -](?<month>[0-9]{2})[._ -](?<year>[0-9]{4})", true)
 723344                {
 723345                    DateTimeFormats =
 723346                    [
 723347                        "dd.MM.yyyy",
 723348                        "dd-MM-yyyy",
 723349                        "dd_MM_yyyy",
 723350                        "dd MM yyyy"
 723351                    ]
 723352                },
 723353
 723354                // This isn't a Kodi naming rule, but the expression below causes false episode numbers for
 723355                // Title Season X Episode X naming schemes.
 723356                // "Series Season X Episode X - Title.avi", "Series S03 E09.avi", "s3 e9 - Title.avi"
 723357                new EpisodeExpression(@".*[\\\/]((?<seriesname>[^\\/]+?)\s)?[Ss](?:eason)?\s*(?<seasonnumber>[0-9]+)\s+[
 723358                {
 723359                    IsNamed = true
 723360                },
 723361
 723362                // Not a Kodi rule as well, but the expression below also causes false positives,
 723363                // so we make sure this one gets tested first.
 723364                // "Foo Bar 889"
 723365                new EpisodeExpression(@".*[\\\/](?![Ee]pisode)(?<seriesname>[\w\s]+?)\s(?<epnumber>[0-9]{1,4})(-(?<endin
 723366                {
 723367                    IsNamed = true
 723368                },
 723369
 723370                new EpisodeExpression(@"[\\\/\._ \[\(-]([0-9]+)x([0-9]+(?:(?:[a-i]|\.[1-9])(?![0-9]))?)([^\\\/]*)$")
 723371                {
 723372                    SupportsAbsoluteEpisodeNumbers = true
 723373                },
 723374
 723375                // Not a Kodi rule as well, but below rule also causes false positives for triple-digit episode names
 723376                // [bar] Foo - 1 [baz] special case of below expression to prevent false positives with digits in the se
 723377                new EpisodeExpression(@".*[\\\/]?.*?(\[.*?\])+.*?(?<seriesname>[-\w\s]+?)[\s_]*-[\s_]*(?<epnumber>[0-9]+
 723378                {
 723379                    IsNamed = true
 723380                },
 723381
 723382                // "Name - 101.mkv", "Name - 101 [720p].mkv", "Name - 101 (2020).mkv"
 723383                // Handles absolute episode numbers with hyphen delimiter (common in anime)
 723384                // Without brackets (bracketed version handled above)
 723385                new EpisodeExpression(@".*[\\\/](?<seriesname>[^\\\/]+?)[\s_]+-[\s_]+(?<epnumber>[0-9]+)[\s_]*(?:\[.*?\]
 723386                {
 723387                    IsNamed = true
 723388                },
 723389
 723390                // /server/anything_102.mp4
 723391                // /server/james.corden.2017.04.20.anne.hathaway.720p.hdtv.x264-crooks.mkv
 723392                // /server/anything_1996.11.14.mp4
 723393                new EpisodeExpression(@"[\\/._ -](?<seriesname>(?![0-9]+[0-9][0-9])([^\\\/_])*)[\\\/._ -](?<seasonnumber
 723394                {
 723395                    IsOptimistic = true,
 723396                    IsNamed = true,
 723397                    SupportsAbsoluteEpisodeNumbers = false
 723398                },
 723399                new EpisodeExpression(@"[\/._ -]p(?:ar)?t[_. -]()([ivx]+|[0-9]+)([._ -][^\/]*)$")
 723400                {
 723401                    SupportsAbsoluteEpisodeNumbers = true
 723402                },
 723403
 723404                // *** End Kodi Standard Naming
 723405
 723406                // "Episode 16", "Episode 16 - Title"
 723407                new EpisodeExpression(@"[Ee]pisode (?<epnumber>[0-9]+)(-(?<endingepnumber>[0-9]+))?[^\\\/]*$")
 723408                {
 723409                    IsNamed = true
 723410                },
 723411
 723412                new EpisodeExpression(@".*(\\|\/)[sS]?(?<seasonnumber>[0-9]+)[xX](?<epnumber>[0-9]+)[^\\\/]*$")
 723413                {
 723414                    IsNamed = true
 723415                },
 723416
 723417                new EpisodeExpression(@".*(\\|\/)[sS](?<seasonnumber>[0-9]+)[x,X]?[eE](?<epnumber>[0-9]+)[^\\\/]*$")
 723418                {
 723419                    IsNamed = true
 723420                },
 723421
 723422                new EpisodeExpression(@".*(\\|\/)(?<seriesname>((?![sS]?[0-9]{1,4}[xX][0-9]{1,3})[^\\\/])*)?([sS]?(?<sea
 723423                {
 723424                    IsNamed = true
 723425                },
 723426
 723427                new EpisodeExpression(@".*(\\|\/)(?<seriesname>[^\\\/]*)[sS](?<seasonnumber>[0-9]{1,4})[xX\.]?[eE](?<epn
 723428                {
 723429                    IsNamed = true
 723430                },
 723431
 723432                // "01.avi"
 723433                new EpisodeExpression(@".*[\\\/](?<epnumber>[0-9]+)(-(?<endingepnumber>[0-9]+))*\.\w+$")
 723434                {
 723435                    IsOptimistic = true,
 723436                    IsNamed = true
 723437                },
 723438
 723439                // "1-12 episode title"
 723440                new EpisodeExpression("([0-9]+)-([0-9]+)"),
 723441
 723442                // "01 - blah.avi", "01-blah.avi"
 723443                new EpisodeExpression(@".*(\\|\/)(?<epnumber>[0-9]{1,3})(-(?<endingepnumber>[0-9]{2,3}))*\s?-\s?[^\\\/]*
 723444                {
 723445                    IsOptimistic = true,
 723446                    IsNamed = true
 723447                },
 723448
 723449                // "01.blah.avi"
 723450                new EpisodeExpression(@".*(\\|\/)(?<epnumber>[0-9]{1,3})(-(?<endingepnumber>[0-9]{2,3}))*\.[^\\\/]+$")
 723451                {
 723452                    IsOptimistic = true,
 723453                    IsNamed = true
 723454                },
 723455
 723456                // "blah - 01.avi", "blah 2 - 01.avi", "blah - 01 blah.avi", "blah 2 - 01 blah", "blah - 01 - blah.avi",
 723457                new EpisodeExpression(@".*[\\\/][^\\\/]* - (?<epnumber>[0-9]{1,3})(-(?<endingepnumber>[0-9]{2,3}))*[^\\\
 723458                {
 723459                    IsOptimistic = true,
 723460                    IsNamed = true
 723461                },
 723462
 723463                // "01 episode title.avi"
 723464                new EpisodeExpression(@"[Ss]eason[\._ ](?<seasonnumber>[0-9]+)[\\\/](?<epnumber>[0-9]{1,3})([^\\\/]*)$")
 723465                {
 723466                    IsOptimistic = true,
 723467                    IsNamed = true
 723468                },
 723469
 723470                // Series and season only expression
 723471                // "the show/season 1", "the show/s01"
 723472                new EpisodeExpression(@"(.*(\\|\/))*(?<seriesname>.+)\/[Ss](eason)?[\. _\-]*(?<seasonnumber>[0-9]+)")
 723473                {
 723474                    IsNamed = true
 723475                },
 723476
 723477                // Series and season only expression
 723478                // "the show S01", "the show season 1"
 723479                new EpisodeExpression(@"(.*(\\|\/))*(?<seriesname>.+)[\. _\-]+[sS](eason)?[\. _\-]*(?<seasonnumber>[0-9]
 723480                {
 723481                    IsNamed = true
 723482                },
 723483
 723484                // Anime style expression
 723485                // "[Group][Series Name][21][1080p][FLAC][HASH]"
 723486                // "[Group] Series Name [04][BDRIP]"
 723487                new EpisodeExpression(@"(?:\[(?:[^\]]+)\]\s*)?(?<seriesname>\[[^\]]+\]|[^[\]]+)\s*\[(?<epnumber>[0-9]+)\
 723488                {
 723489                    IsNamed = true
 723490                },
 723491            ];
 492
 723493            VideoExtraRules =
 723494            [
 723495                new ExtraRule(
 723496                    ExtraType.Trailer,
 723497                    ExtraRuleType.DirectoryName,
 723498                    "trailers",
 723499                    MediaType.Video),
 723500
 723501                new ExtraRule(
 723502                    ExtraType.ThemeVideo,
 723503                    ExtraRuleType.DirectoryName,
 723504                    "backdrops",
 723505                    MediaType.Video),
 723506
 723507                new ExtraRule(
 723508                    ExtraType.ThemeSong,
 723509                    ExtraRuleType.DirectoryName,
 723510                    "theme-music",
 723511                    MediaType.Audio),
 723512
 723513                new ExtraRule(
 723514                    ExtraType.BehindTheScenes,
 723515                    ExtraRuleType.DirectoryName,
 723516                    "behind the scenes",
 723517                    MediaType.Video),
 723518
 723519                new ExtraRule(
 723520                    ExtraType.DeletedScene,
 723521                    ExtraRuleType.DirectoryName,
 723522                    "deleted scenes",
 723523                    MediaType.Video),
 723524
 723525                new ExtraRule(
 723526                    ExtraType.Interview,
 723527                    ExtraRuleType.DirectoryName,
 723528                    "interviews",
 723529                    MediaType.Video),
 723530
 723531                new ExtraRule(
 723532                    ExtraType.Scene,
 723533                    ExtraRuleType.DirectoryName,
 723534                    "scenes",
 723535                    MediaType.Video),
 723536
 723537                new ExtraRule(
 723538                    ExtraType.Sample,
 723539                    ExtraRuleType.DirectoryName,
 723540                    "samples",
 723541                    MediaType.Video),
 723542
 723543                new ExtraRule(
 723544                    ExtraType.Short,
 723545                    ExtraRuleType.DirectoryName,
 723546                    "shorts",
 723547                    MediaType.Video),
 723548
 723549                new ExtraRule(
 723550                    ExtraType.Featurette,
 723551                    ExtraRuleType.DirectoryName,
 723552                    "featurettes",
 723553                    MediaType.Video),
 723554
 723555                new ExtraRule(
 723556                    ExtraType.Unknown,
 723557                    ExtraRuleType.DirectoryName,
 723558                    "extras",
 723559                    MediaType.Video),
 723560
 723561                new ExtraRule(
 723562                    ExtraType.Unknown,
 723563                    ExtraRuleType.DirectoryName,
 723564                    "extra",
 723565                    MediaType.Video),
 723566
 723567                new ExtraRule(
 723568                    ExtraType.Unknown,
 723569                    ExtraRuleType.DirectoryName,
 723570                    "other",
 723571                    MediaType.Video),
 723572
 723573                new ExtraRule(
 723574                    ExtraType.Clip,
 723575                    ExtraRuleType.DirectoryName,
 723576                    "clips",
 723577                    MediaType.Video),
 723578
 723579                new ExtraRule(
 723580                    ExtraType.Trailer,
 723581                    ExtraRuleType.Filename,
 723582                    "trailer",
 723583                    MediaType.Video),
 723584
 723585                new ExtraRule(
 723586                    ExtraType.Sample,
 723587                    ExtraRuleType.Filename,
 723588                    "sample",
 723589                    MediaType.Video),
 723590
 723591                new ExtraRule(
 723592                    ExtraType.ThemeSong,
 723593                    ExtraRuleType.Filename,
 723594                    "theme",
 723595                    MediaType.Audio),
 723596
 723597                new ExtraRule(
 723598                    ExtraType.Trailer,
 723599                    ExtraRuleType.Suffix,
 723600                    "-trailer",
 723601                    MediaType.Video),
 723602
 723603                new ExtraRule(
 723604                    ExtraType.Trailer,
 723605                    ExtraRuleType.Suffix,
 723606                    ".trailer",
 723607                    MediaType.Video),
 723608
 723609                new ExtraRule(
 723610                    ExtraType.Trailer,
 723611                    ExtraRuleType.Suffix,
 723612                    "_trailer",
 723613                    MediaType.Video),
 723614
 723615                new ExtraRule(
 723616                    ExtraType.Trailer,
 723617                    ExtraRuleType.Suffix,
 723618                    "- trailer",
 723619                    MediaType.Video),
 723620
 723621                new ExtraRule(
 723622                    ExtraType.Sample,
 723623                    ExtraRuleType.Suffix,
 723624                    "-sample",
 723625                    MediaType.Video),
 723626
 723627                new ExtraRule(
 723628                    ExtraType.Sample,
 723629                    ExtraRuleType.Suffix,
 723630                    ".sample",
 723631                    MediaType.Video),
 723632
 723633                new ExtraRule(
 723634                    ExtraType.Sample,
 723635                    ExtraRuleType.Suffix,
 723636                    "_sample",
 723637                    MediaType.Video),
 723638
 723639                new ExtraRule(
 723640                    ExtraType.Sample,
 723641                    ExtraRuleType.Suffix,
 723642                    "- sample",
 723643                    MediaType.Video),
 723644
 723645                new ExtraRule(
 723646                    ExtraType.Scene,
 723647                    ExtraRuleType.Suffix,
 723648                    "-scene",
 723649                    MediaType.Video),
 723650
 723651                new ExtraRule(
 723652                    ExtraType.Clip,
 723653                    ExtraRuleType.Suffix,
 723654                    "-clip",
 723655                    MediaType.Video),
 723656
 723657                new ExtraRule(
 723658                    ExtraType.Interview,
 723659                    ExtraRuleType.Suffix,
 723660                    "-interview",
 723661                    MediaType.Video),
 723662
 723663                new ExtraRule(
 723664                    ExtraType.BehindTheScenes,
 723665                    ExtraRuleType.Suffix,
 723666                    "-behindthescenes",
 723667                    MediaType.Video),
 723668
 723669                new ExtraRule(
 723670                    ExtraType.DeletedScene,
 723671                    ExtraRuleType.Suffix,
 723672                    "-deleted",
 723673                    MediaType.Video),
 723674
 723675                new ExtraRule(
 723676                    ExtraType.DeletedScene,
 723677                    ExtraRuleType.Suffix,
 723678                    "-deletedscene",
 723679                    MediaType.Video),
 723680
 723681                new ExtraRule(
 723682                    ExtraType.Featurette,
 723683                    ExtraRuleType.Suffix,
 723684                    "-featurette",
 723685                    MediaType.Video),
 723686
 723687                new ExtraRule(
 723688                    ExtraType.Short,
 723689                    ExtraRuleType.Suffix,
 723690                    "-short",
 723691                    MediaType.Video),
 723692
 723693                new ExtraRule(
 723694                    ExtraType.Unknown,
 723695                    ExtraRuleType.Suffix,
 723696                    "-extra",
 723697                    MediaType.Video),
 723698
 723699                new ExtraRule(
 723700                    ExtraType.Unknown,
 723701                    ExtraRuleType.Suffix,
 723702                    "-other",
 723703                    MediaType.Video)
 723704            ];
 705
 723706            AllExtrasTypesFolderNames = VideoExtraRules
 723707                .Where(i => i.RuleType == ExtraRuleType.DirectoryName)
 723708                .ToDictionary(i => i.Token, i => i.ExtraType, StringComparer.OrdinalIgnoreCase);
 709
 723710            Format3DRules =
 723711            [
 723712                // Kodi rules:
 723713                new Format3DRule(
 723714                    precedingToken: "3d",
 723715                    token: "hsbs"),
 723716
 723717                new Format3DRule(
 723718                    precedingToken: "3d",
 723719                    token: "sbs"),
 723720
 723721                new Format3DRule(
 723722                    precedingToken: "3d",
 723723                    token: "htab"),
 723724
 723725                new Format3DRule(
 723726                    precedingToken: "3d",
 723727                    token: "tab"),
 723728
 723729                 // Media Browser rules:
 723730                new Format3DRule("fsbs"),
 723731                new Format3DRule("hsbs"),
 723732                new Format3DRule("sbs"),
 723733                new Format3DRule("ftab"),
 723734                new Format3DRule("htab"),
 723735                new Format3DRule("tab"),
 723736                new Format3DRule("sbs3d"),
 723737                new Format3DRule("mvc")
 723738            ];
 739
 723740            AudioBookPartsExpressions =
 723741            [
 723742                // Detect specified chapters, like CH 01
 723743                @"ch(?:apter)?[\s_-]?(?<chapter>[0-9]+)",
 723744                // Detect specified parts, like Part 02
 723745                @"p(?:ar)?t[\s_-]?(?<part>[0-9]+)",
 723746                // Chapter is often beginning of filename
 723747                "^(?<chapter>[0-9]+)",
 723748                // Part if often ending of filename
 723749                "(?<!ch(?:apter) )(?<part>[0-9]+)$",
 723750                // Sometimes named as 0001_005 (chapter_part)
 723751                "(?<chapter>[0-9]+)_(?<part>[0-9]+)",
 723752                // Some audiobooks are ripped from cd's, and will be named by disk number.
 723753                @"dis(?:c|k)[\s_-]?(?<chapter>[0-9]+)"
 723754            ];
 755
 723756            AudioBookNamesExpressions =
 723757            [
 723758                // Detect year usually in brackets after name Batman (2020)
 723759                @"^(?<name>.+?)\s*\(\s*(?<year>[0-9]{4})\s*\)\s*$",
 723760                @"^\s*(?<name>[^ ].*?)\s*$"
 723761            ];
 762
 723763            MultipleEpisodeExpressions = new[]
 723764            {
 723765                @".*(\\|\/)[sS]?(?<seasonnumber>[0-9]{1,4})[xX](?<epnumber>[0-9]{1,3})((-| - )[0-9]{1,4}[eExX](?<endinge
 723766                @".*(\\|\/)[sS]?(?<seasonnumber>[0-9]{1,4})[xX](?<epnumber>[0-9]{1,3})((-| - )[0-9]{1,4}[xX][eE](?<endin
 723767                @".*(\\|\/)[sS]?(?<seasonnumber>[0-9]{1,4})[xX](?<epnumber>[0-9]{1,3})((-| - )?[xXeE](?<endingepnumber>[
 723768                @".*(\\|\/)[sS]?(?<seasonnumber>[0-9]{1,4})[xX](?<epnumber>[0-9]{1,3})(-[xE]?[eE]?(?<endingepnumber>[0-9
 723769                @".*(\\|\/)(?<seriesname>((?![sS]?[0-9]{1,4}[xX][0-9]{1,3})[^\\\/])*)?([sS]?(?<seasonnumber>[0-9]{1,4})[
 723770                @".*(\\|\/)(?<seriesname>((?![sS]?[0-9]{1,4}[xX][0-9]{1,3})[^\\\/])*)?([sS]?(?<seasonnumber>[0-9]{1,4})[
 723771                @".*(\\|\/)(?<seriesname>((?![sS]?[0-9]{1,4}[xX][0-9]{1,3})[^\\\/])*)?([sS]?(?<seasonnumber>[0-9]{1,4})[
 723772                @".*(\\|\/)(?<seriesname>((?![sS]?[0-9]{1,4}[xX][0-9]{1,3})[^\\\/])*)?([sS]?(?<seasonnumber>[0-9]{1,4})[
 723773                @".*(\\|\/)(?<seriesname>[^\\\/]*)[sS](?<seasonnumber>[0-9]{1,4})[xX\.]?[eE](?<epnumber>[0-9]{1,3})((-| 
 723774                @".*(\\|\/)(?<seriesname>[^\\\/]*)[sS](?<seasonnumber>[0-9]{1,4})[xX\.]?[eE](?<epnumber>[0-9]{1,3})(-[xX
 723775            }.Select(i => new EpisodeExpression(i)
 723776            {
 723777                IsNamed = true
 723778            }).ToArray();
 779
 723780            Compile();
 723781        }
 782
 783        /// <summary>
 784        /// Gets or sets the folder name to extra types mapping.
 785        /// </summary>
 786        public Dictionary<string, ExtraType> AllExtrasTypesFolderNames { get; set; }
 787
 788        /// <summary>
 789        /// Gets or sets list of audio file extensions.
 790        /// </summary>
 791        public string[] AudioFileExtensions { get; set; }
 792
 793        /// <summary>
 794        /// Gets or sets list of external media flag delimiters.
 795        /// </summary>
 796        public char[] MediaFlagDelimiters { get; set; }
 797
 798        /// <summary>
 799        /// Gets or sets list of external media forced flags.
 800        /// </summary>
 801        public string[] MediaForcedFlags { get; set; }
 802
 803        /// <summary>
 804        /// Gets or sets list of external media default flags.
 805        /// </summary>
 806        public string[] MediaDefaultFlags { get; set; }
 807
 808        /// <summary>
 809        /// Gets or sets list of external media hearing impaired flags.
 810        /// </summary>
 811        public string[] MediaHearingImpairedFlags { get; set; }
 812
 813        /// <summary>
 814        /// Gets or sets list of album stacking prefixes.
 815        /// </summary>
 816        public string[] AlbumStackingPrefixes { get; set; }
 817
 818        /// <summary>
 819        /// Gets or sets list of artist subfolders.
 820        /// </summary>
 821        public string[] ArtistSubfolders { get; set; }
 822
 823        /// <summary>
 824        /// Gets or sets list of subtitle file extensions.
 825        /// </summary>
 826        public string[] SubtitleFileExtensions { get; set; }
 827
 828        /// <summary>
 829        /// Gets the list of lyric file extensions.
 830        /// </summary>
 831        public string[] LyricFileExtensions { get; }
 832
 833        /// <summary>
 834        /// Gets or sets list of episode regular expressions.
 835        /// </summary>
 836        public EpisodeExpression[] EpisodeExpressions { get; set; }
 837
 838        /// <summary>
 839        /// Gets or sets list of video file extensions.
 840        /// </summary>
 841        public string[] VideoFileExtensions { get; set; }
 842
 843        /// <summary>
 844        /// Gets or sets list of video stub file extensions.
 845        /// </summary>
 846        public string[] StubFileExtensions { get; set; }
 847
 848        /// <summary>
 849        /// Gets or sets list of raw audiobook parts regular expressions strings.
 850        /// </summary>
 851        public string[] AudioBookPartsExpressions { get; set; }
 852
 853        /// <summary>
 854        /// Gets or sets list of raw audiobook names regular expressions strings.
 855        /// </summary>
 856        public string[] AudioBookNamesExpressions { get; set; }
 857
 858        /// <summary>
 859        /// Gets or sets list of stub type rules.
 860        /// </summary>
 861        public StubTypeRule[] StubTypes { get; set; }
 862
 863        /// <summary>
 864        /// Gets or sets list of video flag delimiters.
 865        /// </summary>
 866        public char[] VideoFlagDelimiters { get; set; }
 867
 868        /// <summary>
 869        /// Gets or sets list of 3D Format rules.
 870        /// </summary>
 871        public Format3DRule[] Format3DRules { get; set; }
 872
 873        /// <summary>
 874        /// Gets the file stacking rules.
 875        /// </summary>
 876        public FileStackRule[] VideoFileStackingRules { get; }
 877
 878        /// <summary>
 879        /// Gets or sets list of raw clean DateTimes regular expressions strings.
 880        /// </summary>
 881        public string[] CleanDateTimes { get; set; }
 882
 883        /// <summary>
 884        /// Gets or sets list of raw clean strings regular expressions strings.
 885        /// </summary>
 886        public string[] CleanStrings { get; set; }
 887
 888        /// <summary>
 889        /// Gets or sets list of multi-episode regular expressions.
 890        /// </summary>
 891        public EpisodeExpression[] MultipleEpisodeExpressions { get; set; }
 892
 893        /// <summary>
 894        /// Gets or sets list of extra rules for videos.
 895        /// </summary>
 896        public ExtraRule[] VideoExtraRules { get; set; }
 897
 898        /// <summary>
 899        /// Gets list of clean datetime regular expressions.
 900        /// </summary>
 901        public Regex[] CleanDateTimeRegexes { get; private set; } = [];
 902
 903        /// <summary>
 904        /// Gets list of clean string regular expressions.
 905        /// </summary>
 906        public Regex[] CleanStringRegexes { get; private set; } = [];
 907
 908        /// <summary>
 909        /// Compiles raw regex strings into regexes.
 910        /// </summary>
 911        public void Compile()
 912        {
 724913            CleanDateTimeRegexes = CleanDateTimes.Select(Compile).ToArray();
 724914            CleanStringRegexes = CleanStrings.Select(Compile).ToArray();
 724915        }
 916
 917        private Regex Compile(string exp)
 918        {
 5792919            return new Regex(exp, RegexOptions.IgnoreCase | RegexOptions.Compiled);
 920        }
 921    }
 922}