NamingOptions.cs 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710
  1. #pragma warning disable CS1591
  2. using System;
  3. using System.Linq;
  4. using System.Text.RegularExpressions;
  5. using Emby.Naming.Video;
  6. using MediaBrowser.Model.Entities;
  7. // ReSharper disable StringLiteralTypo
  8. namespace Emby.Naming.Common
  9. {
  10. public class NamingOptions
  11. {
  12. public NamingOptions()
  13. {
  14. VideoFileExtensions = new[]
  15. {
  16. ".m4v",
  17. ".3gp",
  18. ".nsv",
  19. ".ts",
  20. ".ty",
  21. ".strm",
  22. ".rm",
  23. ".rmvb",
  24. ".ifo",
  25. ".mov",
  26. ".qt",
  27. ".divx",
  28. ".xvid",
  29. ".bivx",
  30. ".vob",
  31. ".nrg",
  32. ".img",
  33. ".iso",
  34. ".pva",
  35. ".wmv",
  36. ".asf",
  37. ".asx",
  38. ".ogm",
  39. ".m2v",
  40. ".avi",
  41. ".bin",
  42. ".dvr-ms",
  43. ".mpg",
  44. ".mpeg",
  45. ".mp4",
  46. ".mkv",
  47. ".avc",
  48. ".vp3",
  49. ".svq3",
  50. ".nuv",
  51. ".viv",
  52. ".dv",
  53. ".fli",
  54. ".flv",
  55. ".001",
  56. ".tp"
  57. };
  58. VideoFlagDelimiters = new[]
  59. {
  60. '(',
  61. ')',
  62. '-',
  63. '.',
  64. '_',
  65. '[',
  66. ']'
  67. };
  68. StubFileExtensions = new[]
  69. {
  70. ".disc"
  71. };
  72. StubTypes = new[]
  73. {
  74. new StubTypeRule(
  75. stubType: "dvd",
  76. token: "dvd"),
  77. new StubTypeRule(
  78. stubType: "hddvd",
  79. token: "hddvd"),
  80. new StubTypeRule(
  81. stubType: "bluray",
  82. token: "bluray"),
  83. new StubTypeRule(
  84. stubType: "bluray",
  85. token: "brrip"),
  86. new StubTypeRule(
  87. stubType: "bluray",
  88. token: "bd25"),
  89. new StubTypeRule(
  90. stubType: "bluray",
  91. token: "bd50"),
  92. new StubTypeRule(
  93. stubType: "vhs",
  94. token: "vhs"),
  95. new StubTypeRule(
  96. stubType: "tv",
  97. token: "HDTV"),
  98. new StubTypeRule(
  99. stubType: "tv",
  100. token: "PDTV"),
  101. new StubTypeRule(
  102. stubType: "tv",
  103. token: "DSR")
  104. };
  105. VideoFileStackingExpressions = new[]
  106. {
  107. "(.*?)([ _.-]*(?:cd|dvd|p(?:ar)?t|dis[ck])[ _.-]*[0-9]+)(.*?)(\\.[^.]+)$",
  108. "(.*?)([ _.-]*(?:cd|dvd|p(?:ar)?t|dis[ck])[ _.-]*[a-d])(.*?)(\\.[^.]+)$",
  109. "(.*?)([ ._-]*[a-d])(.*?)(\\.[^.]+)$"
  110. };
  111. CleanDateTimes = new[]
  112. {
  113. @"(.+[^_\,\.\(\)\[\]\-])[_\.\(\)\[\]\-](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})*",
  114. @"(.+[^_\,\.\(\)\[\]\-])[ _\.\(\)\[\]\-]+(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})*"
  115. };
  116. CleanStrings = new[]
  117. {
  118. @"[ _\,\.\(\)\[\]\-](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|\[.*\])([ _\,\.\(\)\[\]\-]|$)",
  119. @"(\[.*\])"
  120. };
  121. SubtitleFileExtensions = new[]
  122. {
  123. ".srt",
  124. ".ssa",
  125. ".ass",
  126. ".sub"
  127. };
  128. SubtitleFlagDelimiters = new[]
  129. {
  130. '.'
  131. };
  132. SubtitleForcedFlags = new[]
  133. {
  134. "foreign",
  135. "forced"
  136. };
  137. SubtitleDefaultFlags = new[]
  138. {
  139. "default"
  140. };
  141. AlbumStackingPrefixes = new[]
  142. {
  143. "disc",
  144. "cd",
  145. "disk",
  146. "vol",
  147. "volume"
  148. };
  149. AudioFileExtensions = new[]
  150. {
  151. ".nsv",
  152. ".m4a",
  153. ".flac",
  154. ".aac",
  155. ".strm",
  156. ".pls",
  157. ".rm",
  158. ".mpa",
  159. ".wav",
  160. ".wma",
  161. ".ogg",
  162. ".opus",
  163. ".mp3",
  164. ".mp2",
  165. ".mod",
  166. ".amf",
  167. ".669",
  168. ".dmf",
  169. ".dsm",
  170. ".far",
  171. ".gdm",
  172. ".imf",
  173. ".it",
  174. ".m15",
  175. ".med",
  176. ".okt",
  177. ".s3m",
  178. ".stm",
  179. ".sfx",
  180. ".ult",
  181. ".uni",
  182. ".xm",
  183. ".sid",
  184. ".ac3",
  185. ".dts",
  186. ".cue",
  187. ".aif",
  188. ".aiff",
  189. ".ape",
  190. ".mac",
  191. ".mpc",
  192. ".mp+",
  193. ".mpp",
  194. ".shn",
  195. ".wv",
  196. ".nsf",
  197. ".spc",
  198. ".gym",
  199. ".adplug",
  200. ".adx",
  201. ".dsp",
  202. ".adp",
  203. ".ymf",
  204. ".ast",
  205. ".afc",
  206. ".hps",
  207. ".xsp",
  208. ".acc",
  209. ".m4b",
  210. ".oga",
  211. ".dsf",
  212. ".mka"
  213. };
  214. EpisodeExpressions = new[]
  215. {
  216. // *** Begin Kodi Standard Naming
  217. // <!-- foo.s01.e01, foo.s01_e01, S01E02 foo, S01 - E02 -->
  218. new EpisodeExpression(@".*(\\|\/)(?<seriesname>((?![Ss]([0-9]+)[][ ._-]*[Ee]([0-9]+))[^\\\/])*)?[Ss](?<seasonnumber>[0-9]+)[][ ._-]*[Ee](?<epnumber>[0-9]+)([^\\/]*)$")
  219. {
  220. IsNamed = true
  221. },
  222. // <!-- foo.ep01, foo.EP_01 -->
  223. new EpisodeExpression(@"[\._ -]()[Ee][Pp]_?([0-9]+)([^\\/]*)$"),
  224. new EpisodeExpression("([0-9]{4})[\\.-]([0-9]{2})[\\.-]([0-9]{2})", true)
  225. {
  226. DateTimeFormats = new[]
  227. {
  228. "yyyy.MM.dd",
  229. "yyyy-MM-dd",
  230. "yyyy_MM_dd"
  231. }
  232. },
  233. new EpisodeExpression("([0-9]{2})[\\.-]([0-9]{2})[\\.-]([0-9]{4})", true)
  234. {
  235. DateTimeFormats = new[]
  236. {
  237. "dd.MM.yyyy",
  238. "dd-MM-yyyy",
  239. "dd_MM_yyyy"
  240. }
  241. },
  242. // This isn't a Kodi naming rule, but the expression below causes false positives,
  243. // so we make sure this one gets tested first.
  244. // "Foo Bar 889"
  245. new EpisodeExpression(@".*[\\\/](?![Ee]pisode)(?<seriesname>[\w\s]+?)\s(?<epnumber>[0-9]{1,3})(-(?<endingepnumber>[0-9]{2,3}))*[^\\\/x]*$")
  246. {
  247. IsNamed = true
  248. },
  249. new EpisodeExpression("[\\\\/\\._ \\[\\(-]([0-9]+)x([0-9]+(?:(?:[a-i]|\\.[1-9])(?![0-9]))?)([^\\\\/]*)$")
  250. {
  251. SupportsAbsoluteEpisodeNumbers = true
  252. },
  253. new EpisodeExpression(@"[\\\\/\\._ -](?<seriesname>(?![0-9]+[0-9][0-9])([^\\\/])*)[\\\\/\\._ -](?<seasonnumber>[0-9]+)(?<epnumber>[0-9][0-9](?:(?:[a-i]|\\.[1-9])(?![0-9]))?)([\\._ -][^\\\\/]*)$")
  254. {
  255. IsOptimistic = true,
  256. IsNamed = true,
  257. SupportsAbsoluteEpisodeNumbers = false
  258. },
  259. new EpisodeExpression("[\\/._ -]p(?:ar)?t[_. -]()([ivx]+|[0-9]+)([._ -][^\\/]*)$")
  260. {
  261. SupportsAbsoluteEpisodeNumbers = true
  262. },
  263. // *** End Kodi Standard Naming
  264. // [bar] Foo - 1 [baz]
  265. new EpisodeExpression(@".*?(\[.*?\])+.*?(?<seriesname>[\w\s]+?)[-\s_]+(?<epnumber>[0-9]+).*$")
  266. {
  267. IsNamed = true
  268. },
  269. new EpisodeExpression(@".*(\\|\/)[sS]?(?<seasonnumber>[0-9]+)[xX](?<epnumber>[0-9]+)[^\\\/]*$")
  270. {
  271. IsNamed = true
  272. },
  273. new EpisodeExpression(@".*(\\|\/)[sS](?<seasonnumber>[0-9]+)[x,X]?[eE](?<epnumber>[0-9]+)[^\\\/]*$")
  274. {
  275. IsNamed = true
  276. },
  277. new EpisodeExpression(@".*(\\|\/)(?<seriesname>((?![sS]?[0-9]{1,4}[xX][0-9]{1,3})[^\\\/])*)?([sS]?(?<seasonnumber>[0-9]{1,4})[xX](?<epnumber>[0-9]+))[^\\\/]*$")
  278. {
  279. IsNamed = true
  280. },
  281. new EpisodeExpression(@".*(\\|\/)(?<seriesname>[^\\\/]*)[sS](?<seasonnumber>[0-9]{1,4})[xX\.]?[eE](?<epnumber>[0-9]+)[^\\\/]*$")
  282. {
  283. IsNamed = true
  284. },
  285. // "01.avi"
  286. new EpisodeExpression(@".*[\\\/](?<epnumber>[0-9]+)(-(?<endingepnumber>[0-9]+))*\.\w+$")
  287. {
  288. IsOptimistic = true,
  289. IsNamed = true
  290. },
  291. // "1-12 episode title"
  292. new EpisodeExpression(@"([0-9]+)-([0-9]+)"),
  293. // "01 - blah.avi", "01-blah.avi"
  294. new EpisodeExpression(@".*(\\|\/)(?<epnumber>[0-9]{1,3})(-(?<endingepnumber>[0-9]{2,3}))*\s?-\s?[^\\\/]*$")
  295. {
  296. IsOptimistic = true,
  297. IsNamed = true
  298. },
  299. // "01.blah.avi"
  300. new EpisodeExpression(@".*(\\|\/)(?<epnumber>[0-9]{1,3})(-(?<endingepnumber>[0-9]{2,3}))*\.[^\\\/]+$")
  301. {
  302. IsOptimistic = true,
  303. IsNamed = true
  304. },
  305. // "blah - 01.avi", "blah 2 - 01.avi", "blah - 01 blah.avi", "blah 2 - 01 blah", "blah - 01 - blah.avi", "blah 2 - 01 - blah"
  306. new EpisodeExpression(@".*[\\\/][^\\\/]* - (?<epnumber>[0-9]{1,3})(-(?<endingepnumber>[0-9]{2,3}))*[^\\\/]*$")
  307. {
  308. IsOptimistic = true,
  309. IsNamed = true
  310. },
  311. // "01 episode title.avi"
  312. new EpisodeExpression(@"[Ss]eason[\._ ](?<seasonnumber>[0-9]+)[\\\/](?<epnumber>[0-9]{1,3})([^\\\/]*)$")
  313. {
  314. IsOptimistic = true,
  315. IsNamed = true
  316. },
  317. // "Episode 16", "Episode 16 - Title"
  318. new EpisodeExpression(@".*[\\\/][^\\\/]* (?<epnumber>[0-9]{1,3})(-(?<endingepnumber>[0-9]{2,3}))*[^\\\/]*$")
  319. {
  320. IsOptimistic = true,
  321. IsNamed = true
  322. }
  323. };
  324. EpisodeWithoutSeasonExpressions = new[]
  325. {
  326. @"[/\._ \-]()([0-9]+)(-[0-9]+)?"
  327. };
  328. EpisodeMultiPartExpressions = new[]
  329. {
  330. @"^[-_ex]+([0-9]+(?:(?:[a-i]|\\.[1-9])(?![0-9]))?)"
  331. };
  332. VideoExtraRules = new[]
  333. {
  334. new ExtraRule(
  335. ExtraType.Trailer,
  336. ExtraRuleType.Filename,
  337. "trailer",
  338. MediaType.Video),
  339. new ExtraRule(
  340. ExtraType.Trailer,
  341. ExtraRuleType.Suffix,
  342. "-trailer",
  343. MediaType.Video),
  344. new ExtraRule(
  345. ExtraType.Trailer,
  346. ExtraRuleType.Suffix,
  347. ".trailer",
  348. MediaType.Video),
  349. new ExtraRule(
  350. ExtraType.Trailer,
  351. ExtraRuleType.Suffix,
  352. "_trailer",
  353. MediaType.Video),
  354. new ExtraRule(
  355. ExtraType.Trailer,
  356. ExtraRuleType.Suffix,
  357. " trailer",
  358. MediaType.Video),
  359. new ExtraRule(
  360. ExtraType.Sample,
  361. ExtraRuleType.Filename,
  362. "sample",
  363. MediaType.Video),
  364. new ExtraRule(
  365. ExtraType.Sample,
  366. ExtraRuleType.Suffix,
  367. "-sample",
  368. MediaType.Video),
  369. new ExtraRule(
  370. ExtraType.Sample,
  371. ExtraRuleType.Suffix,
  372. ".sample",
  373. MediaType.Video),
  374. new ExtraRule(
  375. ExtraType.Sample,
  376. ExtraRuleType.Suffix,
  377. "_sample",
  378. MediaType.Video),
  379. new ExtraRule(
  380. ExtraType.Sample,
  381. ExtraRuleType.Suffix,
  382. " sample",
  383. MediaType.Video),
  384. new ExtraRule(
  385. ExtraType.ThemeSong,
  386. ExtraRuleType.Filename,
  387. "theme",
  388. MediaType.Audio),
  389. new ExtraRule(
  390. ExtraType.Scene,
  391. ExtraRuleType.Suffix,
  392. "-scene",
  393. MediaType.Video),
  394. new ExtraRule(
  395. ExtraType.Clip,
  396. ExtraRuleType.Suffix,
  397. "-clip",
  398. MediaType.Video),
  399. new ExtraRule(
  400. ExtraType.Interview,
  401. ExtraRuleType.Suffix,
  402. "-interview",
  403. MediaType.Video),
  404. new ExtraRule(
  405. ExtraType.BehindTheScenes,
  406. ExtraRuleType.Suffix,
  407. "-behindthescenes",
  408. MediaType.Video),
  409. new ExtraRule(
  410. ExtraType.DeletedScene,
  411. ExtraRuleType.Suffix,
  412. "-deleted",
  413. MediaType.Video),
  414. new ExtraRule(
  415. ExtraType.Clip,
  416. ExtraRuleType.Suffix,
  417. "-featurette",
  418. MediaType.Video),
  419. new ExtraRule(
  420. ExtraType.Clip,
  421. ExtraRuleType.Suffix,
  422. "-short",
  423. MediaType.Video),
  424. new ExtraRule(
  425. ExtraType.BehindTheScenes,
  426. ExtraRuleType.DirectoryName,
  427. "behind the scenes",
  428. MediaType.Video),
  429. new ExtraRule(
  430. ExtraType.DeletedScene,
  431. ExtraRuleType.DirectoryName,
  432. "deleted scenes",
  433. MediaType.Video),
  434. new ExtraRule(
  435. ExtraType.Interview,
  436. ExtraRuleType.DirectoryName,
  437. "interviews",
  438. MediaType.Video),
  439. new ExtraRule(
  440. ExtraType.Scene,
  441. ExtraRuleType.DirectoryName,
  442. "scenes",
  443. MediaType.Video),
  444. new ExtraRule(
  445. ExtraType.Sample,
  446. ExtraRuleType.DirectoryName,
  447. "samples",
  448. MediaType.Video),
  449. new ExtraRule(
  450. ExtraType.Clip,
  451. ExtraRuleType.DirectoryName,
  452. "shorts",
  453. MediaType.Video),
  454. new ExtraRule(
  455. ExtraType.Clip,
  456. ExtraRuleType.DirectoryName,
  457. "featurettes",
  458. MediaType.Video),
  459. new ExtraRule(
  460. ExtraType.Unknown,
  461. ExtraRuleType.DirectoryName,
  462. "extras",
  463. MediaType.Video),
  464. };
  465. Format3DRules = new[]
  466. {
  467. // Kodi rules:
  468. new Format3DRule(
  469. precedingToken: "3d",
  470. token: "hsbs"),
  471. new Format3DRule(
  472. precedingToken: "3d",
  473. token: "sbs"),
  474. new Format3DRule(
  475. precedingToken: "3d",
  476. token: "htab"),
  477. new Format3DRule(
  478. precedingToken: "3d",
  479. token: "tab"),
  480. // Media Browser rules:
  481. new Format3DRule("fsbs"),
  482. new Format3DRule("hsbs"),
  483. new Format3DRule("sbs"),
  484. new Format3DRule("ftab"),
  485. new Format3DRule("htab"),
  486. new Format3DRule("tab"),
  487. new Format3DRule("sbs3d"),
  488. new Format3DRule("mvc")
  489. };
  490. AudioBookPartsExpressions = new[]
  491. {
  492. // Detect specified chapters, like CH 01
  493. @"ch(?:apter)?[\s_-]?(?<chapter>[0-9]+)",
  494. // Detect specified parts, like Part 02
  495. @"p(?:ar)?t[\s_-]?(?<part>[0-9]+)",
  496. // Chapter is often beginning of filename
  497. "^(?<chapter>[0-9]+)",
  498. // Part if often ending of filename
  499. @"(?<!ch(?:apter) )(?<part>[0-9]+)$",
  500. // Sometimes named as 0001_005 (chapter_part)
  501. "(?<chapter>[0-9]+)_(?<part>[0-9]+)",
  502. // Some audiobooks are ripped from cd's, and will be named by disk number.
  503. @"dis(?:c|k)[\s_-]?(?<chapter>[0-9]+)"
  504. };
  505. AudioBookNamesExpressions = new[]
  506. {
  507. // Detect year usually in brackets after name Batman (2020)
  508. @"^(?<name>.+?)\s*\(\s*(?<year>\d{4})\s*\)\s*$",
  509. @"^\s*(?<name>[^ ].*?)\s*$"
  510. };
  511. var extensions = VideoFileExtensions.ToList();
  512. extensions.AddRange(new[]
  513. {
  514. ".mkv",
  515. ".m2t",
  516. ".m2ts",
  517. ".img",
  518. ".iso",
  519. ".mk3d",
  520. ".ts",
  521. ".rmvb",
  522. ".mov",
  523. ".avi",
  524. ".mpg",
  525. ".mpeg",
  526. ".wmv",
  527. ".mp4",
  528. ".divx",
  529. ".dvr-ms",
  530. ".wtv",
  531. ".ogm",
  532. ".ogv",
  533. ".asf",
  534. ".m4v",
  535. ".flv",
  536. ".f4v",
  537. ".3gp",
  538. ".webm",
  539. ".mts",
  540. ".m2v",
  541. ".rec",
  542. ".mxf"
  543. });
  544. MultipleEpisodeExpressions = new[]
  545. {
  546. @".*(\\|\/)[sS]?(?<seasonnumber>[0-9]{1,4})[xX](?<epnumber>[0-9]{1,3})((-| - )[0-9]{1,4}[eExX](?<endingepnumber>[0-9]{1,3}))+[^\\\/]*$",
  547. @".*(\\|\/)[sS]?(?<seasonnumber>[0-9]{1,4})[xX](?<epnumber>[0-9]{1,3})((-| - )[0-9]{1,4}[xX][eE](?<endingepnumber>[0-9]{1,3}))+[^\\\/]*$",
  548. @".*(\\|\/)[sS]?(?<seasonnumber>[0-9]{1,4})[xX](?<epnumber>[0-9]{1,3})((-| - )?[xXeE](?<endingepnumber>[0-9]{1,3}))+[^\\\/]*$",
  549. @".*(\\|\/)[sS]?(?<seasonnumber>[0-9]{1,4})[xX](?<epnumber>[0-9]{1,3})(-[xE]?[eE]?(?<endingepnumber>[0-9]{1,3}))+[^\\\/]*$",
  550. @".*(\\|\/)(?<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}))+[^\\\/]*$",
  551. @".*(\\|\/)(?<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}))+[^\\\/]*$",
  552. @".*(\\|\/)(?<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}))+[^\\\/]*$",
  553. @".*(\\|\/)(?<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}))+[^\\\/]*$",
  554. @".*(\\|\/)(?<seriesname>[^\\\/]*)[sS](?<seasonnumber>[0-9]{1,4})[xX\.]?[eE](?<epnumber>[0-9]{1,3})((-| - )?[xXeE](?<endingepnumber>[0-9]{1,3}))+[^\\\/]*$",
  555. @".*(\\|\/)(?<seriesname>[^\\\/]*)[sS](?<seasonnumber>[0-9]{1,4})[xX\.]?[eE](?<epnumber>[0-9]{1,3})(-[xX]?[eE]?(?<endingepnumber>[0-9]{1,3}))+[^\\\/]*$"
  556. }.Select(i => new EpisodeExpression(i)
  557. {
  558. IsNamed = true
  559. }).ToArray();
  560. VideoFileExtensions = extensions
  561. .Distinct(StringComparer.OrdinalIgnoreCase)
  562. .ToArray();
  563. Compile();
  564. }
  565. public string[] AudioFileExtensions { get; set; }
  566. public string[] AlbumStackingPrefixes { get; set; }
  567. public string[] SubtitleFileExtensions { get; set; }
  568. public char[] SubtitleFlagDelimiters { get; set; }
  569. public string[] SubtitleForcedFlags { get; set; }
  570. public string[] SubtitleDefaultFlags { get; set; }
  571. public EpisodeExpression[] EpisodeExpressions { get; set; }
  572. public string[] EpisodeWithoutSeasonExpressions { get; set; }
  573. public string[] EpisodeMultiPartExpressions { get; set; }
  574. public string[] VideoFileExtensions { get; set; }
  575. public string[] StubFileExtensions { get; set; }
  576. public string[] AudioBookPartsExpressions { get; set; }
  577. public string[] AudioBookNamesExpressions { get; set; }
  578. public StubTypeRule[] StubTypes { get; set; }
  579. public char[] VideoFlagDelimiters { get; set; }
  580. public Format3DRule[] Format3DRules { get; set; }
  581. public string[] VideoFileStackingExpressions { get; set; }
  582. public string[] CleanDateTimes { get; set; }
  583. public string[] CleanStrings { get; set; }
  584. public EpisodeExpression[] MultipleEpisodeExpressions { get; set; }
  585. public ExtraRule[] VideoExtraRules { get; set; }
  586. public Regex[] VideoFileStackingRegexes { get; private set; } = Array.Empty<Regex>();
  587. public Regex[] CleanDateTimeRegexes { get; private set; } = Array.Empty<Regex>();
  588. public Regex[] CleanStringRegexes { get; private set; } = Array.Empty<Regex>();
  589. public Regex[] EpisodeWithoutSeasonRegexes { get; private set; } = Array.Empty<Regex>();
  590. public Regex[] EpisodeMultiPartRegexes { get; private set; } = Array.Empty<Regex>();
  591. public void Compile()
  592. {
  593. VideoFileStackingRegexes = VideoFileStackingExpressions.Select(Compile).ToArray();
  594. CleanDateTimeRegexes = CleanDateTimes.Select(Compile).ToArray();
  595. CleanStringRegexes = CleanStrings.Select(Compile).ToArray();
  596. EpisodeWithoutSeasonRegexes = EpisodeWithoutSeasonExpressions.Select(Compile).ToArray();
  597. EpisodeMultiPartRegexes = EpisodeMultiPartExpressions.Select(Compile).ToArray();
  598. }
  599. private Regex Compile(string exp)
  600. {
  601. return new Regex(exp, RegexOptions.IgnoreCase | RegexOptions.Compiled);
  602. }
  603. }
  604. }