NamingOptions.cs 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715
  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. "(?<title>.*?)(?<volume>[ _.-]*(?:cd|dvd|p(?:ar)?t|dis[ck])[ _.-]*[0-9]+)(?<ignore>.*?)(?<extension>\\.[^.]+)$",
  108. "(?<title>.*?)(?<volume>[ _.-]*(?:cd|dvd|p(?:ar)?t|dis[ck])[ _.-]*[a-d])(?<ignore>.*?)(?<extension>\\.[^.]+)$",
  109. "(?<title>.*?)(?<volume>[ ._-]*[a-d])(?<ignore>.*?)(?<extension>\\.[^.]+)$"
  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|blu-ray|x264|x265|h264|xvid|xvidvd|xxx|www.www|AAC|DTS|\[.*\])([ _\,\.\(\)\[\]\-]|$)",
  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("(?<year>[0-9]{4})[\\.-](?<month>[0-9]{2})[\\.-](?<day>[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(@"(?<day>[0-9]{2})[.-](?<month>[0-9]{2})[.-](?<year>[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. // Case Closed (1996-2007)/Case Closed - 317.mkv
  254. // /server/anything_102.mp4
  255. // /server/james.corden.2017.04.20.anne.hathaway.720p.hdtv.x264-crooks.mkv
  256. // /server/anything_1996.11.14.mp4
  257. new EpisodeExpression(@"[\\\\/\\._ -](?<seriesname>(?![0-9]+[0-9][0-9])([^\\\/])*)[\\\\/\\._ -](?<seasonnumber>[0-9]+)(?<epnumber>[0-9][0-9](?:(?:[a-i]|\\.[1-9])(?![0-9]))?)([\\._ -][^\\\\/]*)$")
  258. {
  259. IsOptimistic = true,
  260. IsNamed = true,
  261. SupportsAbsoluteEpisodeNumbers = false
  262. },
  263. new EpisodeExpression("[\\/._ -]p(?:ar)?t[_. -]()([ivx]+|[0-9]+)([._ -][^\\/]*)$")
  264. {
  265. SupportsAbsoluteEpisodeNumbers = true
  266. },
  267. // *** End Kodi Standard Naming
  268. // [bar] Foo - 1 [baz]
  269. new EpisodeExpression(@".*?(\[.*?\])+.*?(?<seriesname>[\w\s]+?)[-\s_]+(?<epnumber>[0-9]+).*$")
  270. {
  271. IsNamed = true
  272. },
  273. new EpisodeExpression(@".*(\\|\/)[sS]?(?<seasonnumber>[0-9]+)[xX](?<epnumber>[0-9]+)[^\\\/]*$")
  274. {
  275. IsNamed = true
  276. },
  277. new EpisodeExpression(@".*(\\|\/)[sS](?<seasonnumber>[0-9]+)[x,X]?[eE](?<epnumber>[0-9]+)[^\\\/]*$")
  278. {
  279. IsNamed = true
  280. },
  281. new EpisodeExpression(@".*(\\|\/)(?<seriesname>((?![sS]?[0-9]{1,4}[xX][0-9]{1,3})[^\\\/])*)?([sS]?(?<seasonnumber>[0-9]{1,4})[xX](?<epnumber>[0-9]+))[^\\\/]*$")
  282. {
  283. IsNamed = true
  284. },
  285. new EpisodeExpression(@".*(\\|\/)(?<seriesname>[^\\\/]*)[sS](?<seasonnumber>[0-9]{1,4})[xX\.]?[eE](?<epnumber>[0-9]+)[^\\\/]*$")
  286. {
  287. IsNamed = true
  288. },
  289. // "01.avi"
  290. new EpisodeExpression(@".*[\\\/](?<epnumber>[0-9]+)(-(?<endingepnumber>[0-9]+))*\.\w+$")
  291. {
  292. IsOptimistic = true,
  293. IsNamed = true
  294. },
  295. // "1-12 episode title"
  296. new EpisodeExpression(@"([0-9]+)-([0-9]+)"),
  297. // "01 - blah.avi", "01-blah.avi"
  298. new EpisodeExpression(@".*(\\|\/)(?<epnumber>[0-9]{1,3})(-(?<endingepnumber>[0-9]{2,3}))*\s?-\s?[^\\\/]*$")
  299. {
  300. IsOptimistic = true,
  301. IsNamed = true
  302. },
  303. // "01.blah.avi"
  304. new EpisodeExpression(@".*(\\|\/)(?<epnumber>[0-9]{1,3})(-(?<endingepnumber>[0-9]{2,3}))*\.[^\\\/]+$")
  305. {
  306. IsOptimistic = true,
  307. IsNamed = true
  308. },
  309. // "blah - 01.avi", "blah 2 - 01.avi", "blah - 01 blah.avi", "blah 2 - 01 blah", "blah - 01 - blah.avi", "blah 2 - 01 - blah"
  310. new EpisodeExpression(@".*[\\\/][^\\\/]* - (?<epnumber>[0-9]{1,3})(-(?<endingepnumber>[0-9]{2,3}))*[^\\\/]*$")
  311. {
  312. IsOptimistic = true,
  313. IsNamed = true
  314. },
  315. // "01 episode title.avi"
  316. new EpisodeExpression(@"[Ss]eason[\._ ](?<seasonnumber>[0-9]+)[\\\/](?<epnumber>[0-9]{1,3})([^\\\/]*)$")
  317. {
  318. IsOptimistic = true,
  319. IsNamed = true
  320. },
  321. // "Episode 16", "Episode 16 - Title"
  322. new EpisodeExpression(@".*[\\\/][^\\\/]* (?<epnumber>[0-9]{1,3})(-(?<endingepnumber>[0-9]{2,3}))*[^\\\/]*$")
  323. {
  324. IsOptimistic = true,
  325. IsNamed = true
  326. }
  327. };
  328. EpisodeWithoutSeasonExpressions = new[]
  329. {
  330. @"[/\._ \-]()([0-9]+)(-[0-9]+)?"
  331. };
  332. EpisodeMultiPartExpressions = new[]
  333. {
  334. @"^[-_ex]+([0-9]+(?:(?:[a-i]|\\.[1-9])(?![0-9]))?)"
  335. };
  336. VideoExtraRules = new[]
  337. {
  338. new ExtraRule(
  339. ExtraType.Trailer,
  340. ExtraRuleType.Filename,
  341. "trailer",
  342. MediaType.Video),
  343. new ExtraRule(
  344. ExtraType.Trailer,
  345. ExtraRuleType.Suffix,
  346. "-trailer",
  347. MediaType.Video),
  348. new ExtraRule(
  349. ExtraType.Trailer,
  350. ExtraRuleType.Suffix,
  351. ".trailer",
  352. MediaType.Video),
  353. new ExtraRule(
  354. ExtraType.Trailer,
  355. ExtraRuleType.Suffix,
  356. "_trailer",
  357. MediaType.Video),
  358. new ExtraRule(
  359. ExtraType.Trailer,
  360. ExtraRuleType.Suffix,
  361. " trailer",
  362. MediaType.Video),
  363. new ExtraRule(
  364. ExtraType.Sample,
  365. ExtraRuleType.Filename,
  366. "sample",
  367. MediaType.Video),
  368. new ExtraRule(
  369. ExtraType.Sample,
  370. ExtraRuleType.Suffix,
  371. "-sample",
  372. MediaType.Video),
  373. new ExtraRule(
  374. ExtraType.Sample,
  375. ExtraRuleType.Suffix,
  376. ".sample",
  377. MediaType.Video),
  378. new ExtraRule(
  379. ExtraType.Sample,
  380. ExtraRuleType.Suffix,
  381. "_sample",
  382. MediaType.Video),
  383. new ExtraRule(
  384. ExtraType.Sample,
  385. ExtraRuleType.Suffix,
  386. " sample",
  387. MediaType.Video),
  388. new ExtraRule(
  389. ExtraType.ThemeSong,
  390. ExtraRuleType.Filename,
  391. "theme",
  392. MediaType.Audio),
  393. new ExtraRule(
  394. ExtraType.Scene,
  395. ExtraRuleType.Suffix,
  396. "-scene",
  397. MediaType.Video),
  398. new ExtraRule(
  399. ExtraType.Clip,
  400. ExtraRuleType.Suffix,
  401. "-clip",
  402. MediaType.Video),
  403. new ExtraRule(
  404. ExtraType.Interview,
  405. ExtraRuleType.Suffix,
  406. "-interview",
  407. MediaType.Video),
  408. new ExtraRule(
  409. ExtraType.BehindTheScenes,
  410. ExtraRuleType.Suffix,
  411. "-behindthescenes",
  412. MediaType.Video),
  413. new ExtraRule(
  414. ExtraType.DeletedScene,
  415. ExtraRuleType.Suffix,
  416. "-deleted",
  417. MediaType.Video),
  418. new ExtraRule(
  419. ExtraType.Clip,
  420. ExtraRuleType.Suffix,
  421. "-featurette",
  422. MediaType.Video),
  423. new ExtraRule(
  424. ExtraType.Clip,
  425. ExtraRuleType.Suffix,
  426. "-short",
  427. MediaType.Video),
  428. new ExtraRule(
  429. ExtraType.BehindTheScenes,
  430. ExtraRuleType.DirectoryName,
  431. "behind the scenes",
  432. MediaType.Video),
  433. new ExtraRule(
  434. ExtraType.DeletedScene,
  435. ExtraRuleType.DirectoryName,
  436. "deleted scenes",
  437. MediaType.Video),
  438. new ExtraRule(
  439. ExtraType.Interview,
  440. ExtraRuleType.DirectoryName,
  441. "interviews",
  442. MediaType.Video),
  443. new ExtraRule(
  444. ExtraType.Scene,
  445. ExtraRuleType.DirectoryName,
  446. "scenes",
  447. MediaType.Video),
  448. new ExtraRule(
  449. ExtraType.Sample,
  450. ExtraRuleType.DirectoryName,
  451. "samples",
  452. MediaType.Video),
  453. new ExtraRule(
  454. ExtraType.Clip,
  455. ExtraRuleType.DirectoryName,
  456. "shorts",
  457. MediaType.Video),
  458. new ExtraRule(
  459. ExtraType.Clip,
  460. ExtraRuleType.DirectoryName,
  461. "featurettes",
  462. MediaType.Video),
  463. new ExtraRule(
  464. ExtraType.Unknown,
  465. ExtraRuleType.DirectoryName,
  466. "extras",
  467. MediaType.Video),
  468. };
  469. Format3DRules = new[]
  470. {
  471. // Kodi rules:
  472. new Format3DRule(
  473. precedingToken: "3d",
  474. token: "hsbs"),
  475. new Format3DRule(
  476. precedingToken: "3d",
  477. token: "sbs"),
  478. new Format3DRule(
  479. precedingToken: "3d",
  480. token: "htab"),
  481. new Format3DRule(
  482. precedingToken: "3d",
  483. token: "tab"),
  484. // Media Browser rules:
  485. new Format3DRule("fsbs"),
  486. new Format3DRule("hsbs"),
  487. new Format3DRule("sbs"),
  488. new Format3DRule("ftab"),
  489. new Format3DRule("htab"),
  490. new Format3DRule("tab"),
  491. new Format3DRule("sbs3d"),
  492. new Format3DRule("mvc")
  493. };
  494. AudioBookPartsExpressions = new[]
  495. {
  496. // Detect specified chapters, like CH 01
  497. @"ch(?:apter)?[\s_-]?(?<chapter>[0-9]+)",
  498. // Detect specified parts, like Part 02
  499. @"p(?:ar)?t[\s_-]?(?<part>[0-9]+)",
  500. // Chapter is often beginning of filename
  501. "^(?<chapter>[0-9]+)",
  502. // Part if often ending of filename
  503. @"(?<!ch(?:apter) )(?<part>[0-9]+)$",
  504. // Sometimes named as 0001_005 (chapter_part)
  505. "(?<chapter>[0-9]+)_(?<part>[0-9]+)",
  506. // Some audiobooks are ripped from cd's, and will be named by disk number.
  507. @"dis(?:c|k)[\s_-]?(?<chapter>[0-9]+)"
  508. };
  509. AudioBookNamesExpressions = new[]
  510. {
  511. // Detect year usually in brackets after name Batman (2020)
  512. @"^(?<name>.+?)\s*\(\s*(?<year>\d{4})\s*\)\s*$",
  513. @"^\s*(?<name>[^ ].*?)\s*$"
  514. };
  515. var extensions = VideoFileExtensions.ToList();
  516. extensions.AddRange(new[]
  517. {
  518. ".mkv",
  519. ".m2t",
  520. ".m2ts",
  521. ".img",
  522. ".iso",
  523. ".mk3d",
  524. ".ts",
  525. ".rmvb",
  526. ".mov",
  527. ".avi",
  528. ".mpg",
  529. ".mpeg",
  530. ".wmv",
  531. ".mp4",
  532. ".divx",
  533. ".dvr-ms",
  534. ".wtv",
  535. ".ogm",
  536. ".ogv",
  537. ".asf",
  538. ".m4v",
  539. ".flv",
  540. ".f4v",
  541. ".3gp",
  542. ".webm",
  543. ".mts",
  544. ".m2v",
  545. ".rec",
  546. ".mxf"
  547. });
  548. MultipleEpisodeExpressions = new[]
  549. {
  550. @".*(\\|\/)[sS]?(?<seasonnumber>[0-9]{1,4})[xX](?<epnumber>[0-9]{1,3})((-| - )[0-9]{1,4}[eExX](?<endingepnumber>[0-9]{1,3}))+[^\\\/]*$",
  551. @".*(\\|\/)[sS]?(?<seasonnumber>[0-9]{1,4})[xX](?<epnumber>[0-9]{1,3})((-| - )[0-9]{1,4}[xX][eE](?<endingepnumber>[0-9]{1,3}))+[^\\\/]*$",
  552. @".*(\\|\/)[sS]?(?<seasonnumber>[0-9]{1,4})[xX](?<epnumber>[0-9]{1,3})((-| - )?[xXeE](?<endingepnumber>[0-9]{1,3}))+[^\\\/]*$",
  553. @".*(\\|\/)[sS]?(?<seasonnumber>[0-9]{1,4})[xX](?<epnumber>[0-9]{1,3})(-[xE]?[eE]?(?<endingepnumber>[0-9]{1,3}))+[^\\\/]*$",
  554. @".*(\\|\/)(?<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}))+[^\\\/]*$",
  555. @".*(\\|\/)(?<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}))+[^\\\/]*$",
  556. @".*(\\|\/)(?<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}))+[^\\\/]*$",
  557. @".*(\\|\/)(?<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}))+[^\\\/]*$",
  558. @".*(\\|\/)(?<seriesname>[^\\\/]*)[sS](?<seasonnumber>[0-9]{1,4})[xX\.]?[eE](?<epnumber>[0-9]{1,3})((-| - )?[xXeE](?<endingepnumber>[0-9]{1,3}))+[^\\\/]*$",
  559. @".*(\\|\/)(?<seriesname>[^\\\/]*)[sS](?<seasonnumber>[0-9]{1,4})[xX\.]?[eE](?<epnumber>[0-9]{1,3})(-[xX]?[eE]?(?<endingepnumber>[0-9]{1,3}))+[^\\\/]*$"
  560. }.Select(i => new EpisodeExpression(i)
  561. {
  562. IsNamed = true
  563. }).ToArray();
  564. VideoFileExtensions = extensions
  565. .Distinct(StringComparer.OrdinalIgnoreCase)
  566. .ToArray();
  567. Compile();
  568. }
  569. public string[] AudioFileExtensions { get; set; }
  570. public string[] AlbumStackingPrefixes { get; set; }
  571. public string[] SubtitleFileExtensions { get; set; }
  572. public char[] SubtitleFlagDelimiters { get; set; }
  573. public string[] SubtitleForcedFlags { get; set; }
  574. public string[] SubtitleDefaultFlags { get; set; }
  575. public EpisodeExpression[] EpisodeExpressions { get; set; }
  576. public string[] EpisodeWithoutSeasonExpressions { get; set; }
  577. public string[] EpisodeMultiPartExpressions { get; set; }
  578. public string[] VideoFileExtensions { get; set; }
  579. public string[] StubFileExtensions { get; set; }
  580. public string[] AudioBookPartsExpressions { get; set; }
  581. public string[] AudioBookNamesExpressions { get; set; }
  582. public StubTypeRule[] StubTypes { get; set; }
  583. public char[] VideoFlagDelimiters { get; set; }
  584. public Format3DRule[] Format3DRules { get; set; }
  585. public string[] VideoFileStackingExpressions { get; set; }
  586. public string[] CleanDateTimes { get; set; }
  587. public string[] CleanStrings { get; set; }
  588. public EpisodeExpression[] MultipleEpisodeExpressions { get; set; }
  589. public ExtraRule[] VideoExtraRules { get; set; }
  590. public Regex[] VideoFileStackingRegexes { get; private set; } = Array.Empty<Regex>();
  591. public Regex[] CleanDateTimeRegexes { get; private set; } = Array.Empty<Regex>();
  592. public Regex[] CleanStringRegexes { get; private set; } = Array.Empty<Regex>();
  593. public Regex[] EpisodeWithoutSeasonRegexes { get; private set; } = Array.Empty<Regex>();
  594. public Regex[] EpisodeMultiPartRegexes { get; private set; } = Array.Empty<Regex>();
  595. public void Compile()
  596. {
  597. VideoFileStackingRegexes = VideoFileStackingExpressions.Select(Compile).ToArray();
  598. CleanDateTimeRegexes = CleanDateTimes.Select(Compile).ToArray();
  599. CleanStringRegexes = CleanStrings.Select(Compile).ToArray();
  600. EpisodeWithoutSeasonRegexes = EpisodeWithoutSeasonExpressions.Select(Compile).ToArray();
  601. EpisodeMultiPartRegexes = EpisodeMultiPartExpressions.Select(Compile).ToArray();
  602. }
  603. private Regex Compile(string exp)
  604. {
  605. return new Regex(exp, RegexOptions.IgnoreCase | RegexOptions.Compiled);
  606. }
  607. }
  608. }