aboutsummaryrefslogtreecommitdiff
path: root/Emby.Naming/Common/NamingOptions.cs
diff options
context:
space:
mode:
Diffstat (limited to 'Emby.Naming/Common/NamingOptions.cs')
-rw-r--r--Emby.Naming/Common/NamingOptions.cs687
1 files changed, 687 insertions, 0 deletions
diff --git a/Emby.Naming/Common/NamingOptions.cs b/Emby.Naming/Common/NamingOptions.cs
new file mode 100644
index 000000000..9e65440d0
--- /dev/null
+++ b/Emby.Naming/Common/NamingOptions.cs
@@ -0,0 +1,687 @@
+using Emby.Naming.Video;
+using System.Collections.Generic;
+using System;
+using System.Linq;
+using System.Text.RegularExpressions;
+
+namespace Emby.Naming.Common
+{
+ public class NamingOptions
+ {
+ public string[] AudioFileExtensions { get; set; }
+ public string[] AlbumStackingPrefixes { get; set; }
+
+ public string[] SubtitleFileExtensions { get; set; }
+ public char[] SubtitleFlagDelimiters { get; set; }
+
+ public string[] SubtitleForcedFlags { get; set; }
+ public string[] SubtitleDefaultFlags { get; set; }
+
+ public EpisodeExpression[] EpisodeExpressions { get; set; }
+ public string[] EpisodeWithoutSeasonExpressions { get; set; }
+ public string[] EpisodeMultiPartExpressions { get; set; }
+
+ public string[] VideoFileExtensions { get; set; }
+ public string[] StubFileExtensions { get; set; }
+
+ public string[] AudioBookPartsExpressions { get; set; }
+
+ public StubTypeRule[] StubTypes { get; set; }
+
+ public char[] VideoFlagDelimiters { get; set; }
+ public Format3DRule[] Format3DRules { get; set; }
+
+ public string[] VideoFileStackingExpressions { get; set; }
+ public string[] CleanDateTimes { get; set; }
+ public string[] CleanStrings { get; set; }
+
+
+ public EpisodeExpression[] MultipleEpisodeExpressions { get; set; }
+
+ public ExtraRule[] VideoExtraRules { get; set; }
+
+ public NamingOptions()
+ {
+ VideoFileExtensions = new string[]
+ {
+ ".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"
+ };
+
+ 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"
+ }
+ },
+
+ 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
+
+ new EpisodeExpression(@".*(\\|\/)[sS]?(?<seasonnumber>\d{1,4})[xX](?<epnumber>\d{1,3})[^\\\/]*$")
+ {
+ IsNamed = true
+ },
+
+ new EpisodeExpression(@".*(\\|\/)[sS](?<seasonnumber>\d{1,4})[x,X]?[eE](?<epnumber>\d{1,3})[^\\\/]*$")
+ {
+ IsNamed = true
+ },
+
+ new EpisodeExpression(@".*(\\|\/)(?<seriesname>((?![sS]?\d{1,4}[xX]\d{1,3})[^\\\/])*)?([sS]?(?<seasonnumber>\d{1,4})[xX](?<epnumber>\d{1,3}))[^\\\/]*$")
+ {
+ IsNamed = true
+ },
+
+ new EpisodeExpression(@".*(\\|\/)(?<seriesname>[^\\\/]*)[sS](?<seasonnumber>\d{1,4})[xX\.]?[eE](?<epnumber>\d{1,3})[^\\\/]*$")
+ {
+ IsNamed = true
+ },
+
+ // "01.avi"
+ 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 - blah.avi", "01-blah.avi"
+ new EpisodeExpression(@".*(\\|\/)(?<epnumber>\d{1,3})(-(?<endingepnumber>\d{2,3}))*\s?-\s?[^\\\/]*$")
+ {
+ IsOptimistic = true,
+ IsNamed = true
+ },
+
+ // "01.blah.avi"
+ new EpisodeExpression(@".*(\\|\/)(?<epnumber>\d{1,3})(-(?<endingepnumber>\d{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>\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
+ }
+ };
+
+ 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();
+ }
+
+ public Regex[] VideoFileStackingRegexes { get; private set; }
+ public Regex[] CleanDateTimeRegexes { get; private set; }
+ public Regex[] CleanStringRegexes { get; private set; }
+
+ public Regex[] EpisodeWithoutSeasonRegexes { get; private set; }
+ public Regex[] EpisodeMultiPartRegexes { get; private set; }
+
+ public void Compile()
+ {
+ VideoFileStackingRegexes = VideoFileStackingExpressions.Select(Compile).ToArray();
+ CleanDateTimeRegexes = CleanDateTimes.Select(Compile).ToArray();
+ CleanStringRegexes = CleanStrings.Select(Compile).ToArray();
+ EpisodeWithoutSeasonRegexes = EpisodeWithoutSeasonExpressions.Select(Compile).ToArray();
+ EpisodeMultiPartRegexes = EpisodeMultiPartExpressions.Select(Compile).ToArray();
+ }
+
+ private Regex Compile(string exp)
+ {
+ return new Regex(exp, RegexOptions.IgnoreCase | RegexOptions.Compiled);
+ }
+ }
+}