NamingOptions.cs 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828
  1. #pragma warning disable CA1819
  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. /// <summary>
  11. /// Big ugly class containing lot of different naming options that should be split and injected instead of passes everywhere.
  12. /// </summary>
  13. public class NamingOptions
  14. {
  15. /// <summary>
  16. /// Initializes a new instance of the <see cref="NamingOptions"/> class.
  17. /// </summary>
  18. public NamingOptions()
  19. {
  20. VideoFileExtensions = new[]
  21. {
  22. ".m4v",
  23. ".3gp",
  24. ".nsv",
  25. ".ts",
  26. ".ty",
  27. ".strm",
  28. ".rm",
  29. ".rmvb",
  30. ".ifo",
  31. ".mov",
  32. ".qt",
  33. ".divx",
  34. ".xvid",
  35. ".bivx",
  36. ".vob",
  37. ".nrg",
  38. ".img",
  39. ".iso",
  40. ".pva",
  41. ".wmv",
  42. ".asf",
  43. ".asx",
  44. ".ogm",
  45. ".m2v",
  46. ".avi",
  47. ".bin",
  48. ".dvr-ms",
  49. ".mpg",
  50. ".mpeg",
  51. ".mp4",
  52. ".mkv",
  53. ".avc",
  54. ".vp3",
  55. ".svq3",
  56. ".nuv",
  57. ".viv",
  58. ".dv",
  59. ".fli",
  60. ".flv",
  61. ".001",
  62. ".tp"
  63. };
  64. VideoFlagDelimiters = new[]
  65. {
  66. '(',
  67. ')',
  68. '-',
  69. '.',
  70. '_',
  71. '[',
  72. ']'
  73. };
  74. StubFileExtensions = new[]
  75. {
  76. ".disc"
  77. };
  78. StubTypes = new[]
  79. {
  80. new StubTypeRule(
  81. stubType: "dvd",
  82. token: "dvd"),
  83. new StubTypeRule(
  84. stubType: "hddvd",
  85. token: "hddvd"),
  86. new StubTypeRule(
  87. stubType: "bluray",
  88. token: "bluray"),
  89. new StubTypeRule(
  90. stubType: "bluray",
  91. token: "brrip"),
  92. new StubTypeRule(
  93. stubType: "bluray",
  94. token: "bd25"),
  95. new StubTypeRule(
  96. stubType: "bluray",
  97. token: "bd50"),
  98. new StubTypeRule(
  99. stubType: "vhs",
  100. token: "vhs"),
  101. new StubTypeRule(
  102. stubType: "tv",
  103. token: "HDTV"),
  104. new StubTypeRule(
  105. stubType: "tv",
  106. token: "PDTV"),
  107. new StubTypeRule(
  108. stubType: "tv",
  109. token: "DSR")
  110. };
  111. VideoFileStackingExpressions = new[]
  112. {
  113. "(?<title>.*?)(?<volume>[ _.-]*(?:cd|dvd|p(?:ar)?t|dis[ck])[ _.-]*[0-9]+)(?<ignore>.*?)(?<extension>\\.[^.]+)$",
  114. "(?<title>.*?)(?<volume>[ _.-]*(?:cd|dvd|p(?:ar)?t|dis[ck])[ _.-]*[a-d])(?<ignore>.*?)(?<extension>\\.[^.]+)$",
  115. "(?<title>.*?)(?<volume>[ ._-]*[a-d])(?<ignore>.*?)(?<extension>\\.[^.]+)$"
  116. };
  117. CleanDateTimes = new[]
  118. {
  119. @"(.+[^_\,\.\(\)\[\]\-])[_\.\(\)\[\]\-](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})*",
  120. @"(.+[^_\,\.\(\)\[\]\-])[ _\.\(\)\[\]\-]+(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})*"
  121. };
  122. CleanStrings = new[]
  123. {
  124. @"^\s*(?<cleaned>.+?)[ _\,\.\(\)\[\]\-](3d|sbs|tab|hsbs|htab|mvc|HDR|HDC|UHD|UltraHD|4k|ac3|dts|custom|dc|divx|divx5|dsr|dsrip|dutch|dvd|dvdrip|dvdscr|dvdscreener|screener|dvdivx|cam|fragment|fs|hdtv|hdrip|hdtvrip|internal|limited|multisubs|ntsc|ogg|ogm|pal|pdtv|proper|repack|rerip|retail|cd[1-9]|r3|r5|bd5|bd|se|svcd|swedish|german|read.nfo|nfofix|unrated|ws|telesync|ts|telecine|tc|brrip|bdrip|480p|480i|576p|576i|720p|720i|1080p|1080i|2160p|hrhd|hrhdtv|hddvd|bluray|blu-ray|x264|x265|h264|h265|xvid|xvidvd|xxx|www.www|AAC|DTS|\[.*\])([ _\,\.\(\)\[\]\-]|$)",
  125. @"^(?<cleaned>.+?)(\[.*\])",
  126. @"^\s*(?<cleaned>.+?)\WE[0-9]+(-|~)E?[0-9]+(\W|$)",
  127. @"^\s*\[[^\]]+\](?!\.\w+$)\s*(?<cleaned>.+)",
  128. @"^\s*(?<cleaned>.+?)\s+-\s+[0-9]+\s*$"
  129. };
  130. SubtitleFileExtensions = new[]
  131. {
  132. ".srt",
  133. ".ssa",
  134. ".ass",
  135. ".sub"
  136. };
  137. SubtitleFlagDelimiters = new[]
  138. {
  139. '.'
  140. };
  141. SubtitleForcedFlags = new[]
  142. {
  143. "foreign",
  144. "forced"
  145. };
  146. SubtitleDefaultFlags = new[]
  147. {
  148. "default"
  149. };
  150. AlbumStackingPrefixes = new[]
  151. {
  152. "disc",
  153. "cd",
  154. "disk",
  155. "vol",
  156. "volume"
  157. };
  158. AudioFileExtensions = new[]
  159. {
  160. ".nsv",
  161. ".m4a",
  162. ".flac",
  163. ".aac",
  164. ".strm",
  165. ".pls",
  166. ".rm",
  167. ".mpa",
  168. ".wav",
  169. ".wma",
  170. ".ogg",
  171. ".opus",
  172. ".mp3",
  173. ".mp2",
  174. ".mod",
  175. ".amf",
  176. ".669",
  177. ".dmf",
  178. ".dsm",
  179. ".far",
  180. ".gdm",
  181. ".imf",
  182. ".it",
  183. ".m15",
  184. ".med",
  185. ".okt",
  186. ".s3m",
  187. ".stm",
  188. ".sfx",
  189. ".ult",
  190. ".uni",
  191. ".xm",
  192. ".sid",
  193. ".ac3",
  194. ".dts",
  195. ".cue",
  196. ".aif",
  197. ".aiff",
  198. ".ape",
  199. ".mac",
  200. ".mpc",
  201. ".mp+",
  202. ".mpp",
  203. ".shn",
  204. ".wv",
  205. ".nsf",
  206. ".spc",
  207. ".gym",
  208. ".adplug",
  209. ".adx",
  210. ".dsp",
  211. ".adp",
  212. ".ymf",
  213. ".ast",
  214. ".afc",
  215. ".hps",
  216. ".xsp",
  217. ".acc",
  218. ".m4b",
  219. ".oga",
  220. ".dsf",
  221. ".mka"
  222. };
  223. EpisodeExpressions = new[]
  224. {
  225. // *** Begin Kodi Standard Naming
  226. // <!-- foo.s01.e01, foo.s01_e01, S01E02 foo, S01 - E02 -->
  227. new EpisodeExpression(@".*(\\|\/)(?<seriesname>((?![Ss]([0-9]+)[][ ._-]*[Ee]([0-9]+))[^\\\/])*)?[Ss](?<seasonnumber>[0-9]+)[][ ._-]*[Ee](?<epnumber>[0-9]+)([^\\/]*)$")
  228. {
  229. IsNamed = true
  230. },
  231. // <!-- foo.ep01, foo.EP_01 -->
  232. new EpisodeExpression(@"[\._ -]()[Ee][Pp]_?([0-9]+)([^\\/]*)$"),
  233. // <!-- foo.E01., foo.e01. -->
  234. new EpisodeExpression(@"[^\\/]*?()\.?[Ee]([0-9]+)\.([^\\/]*)$"),
  235. new EpisodeExpression("(?<year>[0-9]{4})[\\.-](?<month>[0-9]{2})[\\.-](?<day>[0-9]{2})", true)
  236. {
  237. DateTimeFormats = new[]
  238. {
  239. "yyyy.MM.dd",
  240. "yyyy-MM-dd",
  241. "yyyy_MM_dd"
  242. }
  243. },
  244. new EpisodeExpression(@"(?<day>[0-9]{2})[.-](?<month>[0-9]{2})[.-](?<year>[0-9]{4})", true)
  245. {
  246. DateTimeFormats = new[]
  247. {
  248. "dd.MM.yyyy",
  249. "dd-MM-yyyy",
  250. "dd_MM_yyyy"
  251. }
  252. },
  253. // This isn't a Kodi naming rule, but the expression below causes false positives,
  254. // so we make sure this one gets tested first.
  255. // "Foo Bar 889"
  256. new EpisodeExpression(@".*[\\\/](?![Ee]pisode)(?<seriesname>[\w\s]+?)\s(?<epnumber>[0-9]{1,3})(-(?<endingepnumber>[0-9]{2,3}))*[^\\\/x]*$")
  257. {
  258. IsNamed = true
  259. },
  260. new EpisodeExpression(@"[\\\/\._ \[\(-]([0-9]+)x([0-9]+(?:(?:[a-i]|\.[1-9])(?![0-9]))?)([^\\\/]*)$")
  261. {
  262. SupportsAbsoluteEpisodeNumbers = true
  263. },
  264. // Not a Kodi rule as well, but below rule also causes false positives for triple-digit episode names
  265. // [bar] Foo - 1 [baz] special case of below expression to prevent false positives with digits in the series name
  266. new EpisodeExpression(@".*[\\\/]?.*?(\[.*?\])+.*?(?<seriesname>[-\w\s]+?)[\s_]*-[\s_]*(?<epnumber>[0-9]+).*$")
  267. {
  268. IsNamed = true
  269. },
  270. // /server/anything_102.mp4
  271. // /server/james.corden.2017.04.20.anne.hathaway.720p.hdtv.x264-crooks.mkv
  272. // /server/anything_1996.11.14.mp4
  273. new EpisodeExpression(@"[\\/._ -](?<seriesname>(?![0-9]+[0-9][0-9])([^\\\/_])*)[\\\/._ -](?<seasonnumber>[0-9]+)(?<epnumber>[0-9][0-9](?:(?:[a-i]|\.[1-9])(?![0-9]))?)([._ -][^\\\/]*)$")
  274. {
  275. IsOptimistic = true,
  276. IsNamed = true,
  277. SupportsAbsoluteEpisodeNumbers = false
  278. },
  279. new EpisodeExpression("[\\/._ -]p(?:ar)?t[_. -]()([ivx]+|[0-9]+)([._ -][^\\/]*)$")
  280. {
  281. SupportsAbsoluteEpisodeNumbers = true
  282. },
  283. // *** End Kodi Standard Naming
  284. // "Episode 16", "Episode 16 - Title"
  285. new EpisodeExpression(@"[Ee]pisode (?<epnumber>[0-9]+)(-(?<endingepnumber>[0-9]+))?[^\\\/]*$")
  286. {
  287. IsNamed = true
  288. },
  289. new EpisodeExpression(@".*(\\|\/)[sS]?(?<seasonnumber>[0-9]+)[xX](?<epnumber>[0-9]+)[^\\\/]*$")
  290. {
  291. IsNamed = true
  292. },
  293. new EpisodeExpression(@".*(\\|\/)[sS](?<seasonnumber>[0-9]+)[x,X]?[eE](?<epnumber>[0-9]+)[^\\\/]*$")
  294. {
  295. IsNamed = true
  296. },
  297. new EpisodeExpression(@".*(\\|\/)(?<seriesname>((?![sS]?[0-9]{1,4}[xX][0-9]{1,3})[^\\\/])*)?([sS]?(?<seasonnumber>[0-9]{1,4})[xX](?<epnumber>[0-9]+))[^\\\/]*$")
  298. {
  299. IsNamed = true
  300. },
  301. new EpisodeExpression(@".*(\\|\/)(?<seriesname>[^\\\/]*)[sS](?<seasonnumber>[0-9]{1,4})[xX\.]?[eE](?<epnumber>[0-9]+)[^\\\/]*$")
  302. {
  303. IsNamed = true
  304. },
  305. // "01.avi"
  306. new EpisodeExpression(@".*[\\\/](?<epnumber>[0-9]+)(-(?<endingepnumber>[0-9]+))*\.\w+$")
  307. {
  308. IsOptimistic = true,
  309. IsNamed = true
  310. },
  311. // "1-12 episode title"
  312. new EpisodeExpression(@"([0-9]+)-([0-9]+)"),
  313. // "01 - blah.avi", "01-blah.avi"
  314. new EpisodeExpression(@".*(\\|\/)(?<epnumber>[0-9]{1,3})(-(?<endingepnumber>[0-9]{2,3}))*\s?-\s?[^\\\/]*$")
  315. {
  316. IsOptimistic = true,
  317. IsNamed = true
  318. },
  319. // "01.blah.avi"
  320. new EpisodeExpression(@".*(\\|\/)(?<epnumber>[0-9]{1,3})(-(?<endingepnumber>[0-9]{2,3}))*\.[^\\\/]+$")
  321. {
  322. IsOptimistic = true,
  323. IsNamed = true
  324. },
  325. // "blah - 01.avi", "blah 2 - 01.avi", "blah - 01 blah.avi", "blah 2 - 01 blah", "blah - 01 - blah.avi", "blah 2 - 01 - blah"
  326. new EpisodeExpression(@".*[\\\/][^\\\/]* - (?<epnumber>[0-9]{1,3})(-(?<endingepnumber>[0-9]{2,3}))*[^\\\/]*$")
  327. {
  328. IsOptimistic = true,
  329. IsNamed = true
  330. },
  331. // "01 episode title.avi"
  332. new EpisodeExpression(@"[Ss]eason[\._ ](?<seasonnumber>[0-9]+)[\\\/](?<epnumber>[0-9]{1,3})([^\\\/]*)$")
  333. {
  334. IsOptimistic = true,
  335. IsNamed = true
  336. },
  337. // Series and season only expression
  338. // "the show/season 1", "the show/s01"
  339. new EpisodeExpression(@"(.*(\\|\/))*(?<seriesname>.+)\/[Ss](eason)?[\. _\-]*(?<seasonnumber>[0-9]+)")
  340. {
  341. IsNamed = true
  342. },
  343. // Series and season only expression
  344. // "the show S01", "the show season 1"
  345. new EpisodeExpression(@"(.*(\\|\/))*(?<seriesname>.+)[\. _\-]+[sS](eason)?[\. _\-]*(?<seasonnumber>[0-9]+)")
  346. {
  347. IsNamed = true
  348. },
  349. };
  350. EpisodeWithoutSeasonExpressions = new[]
  351. {
  352. @"[/\._ \-]()([0-9]+)(-[0-9]+)?"
  353. };
  354. EpisodeMultiPartExpressions = new[]
  355. {
  356. @"^[-_ex]+([0-9]+(?:(?:[a-i]|\\.[1-9])(?![0-9]))?)"
  357. };
  358. VideoExtraRules = new[]
  359. {
  360. new ExtraRule(
  361. ExtraType.Trailer,
  362. ExtraRuleType.Filename,
  363. "trailer",
  364. MediaType.Video),
  365. new ExtraRule(
  366. ExtraType.Trailer,
  367. ExtraRuleType.Suffix,
  368. "-trailer",
  369. MediaType.Video),
  370. new ExtraRule(
  371. ExtraType.Trailer,
  372. ExtraRuleType.Suffix,
  373. ".trailer",
  374. MediaType.Video),
  375. new ExtraRule(
  376. ExtraType.Trailer,
  377. ExtraRuleType.Suffix,
  378. "_trailer",
  379. MediaType.Video),
  380. new ExtraRule(
  381. ExtraType.Trailer,
  382. ExtraRuleType.Suffix,
  383. " trailer",
  384. MediaType.Video),
  385. new ExtraRule(
  386. ExtraType.Sample,
  387. ExtraRuleType.Filename,
  388. "sample",
  389. MediaType.Video),
  390. new ExtraRule(
  391. ExtraType.Sample,
  392. ExtraRuleType.Suffix,
  393. "-sample",
  394. MediaType.Video),
  395. new ExtraRule(
  396. ExtraType.Sample,
  397. ExtraRuleType.Suffix,
  398. ".sample",
  399. MediaType.Video),
  400. new ExtraRule(
  401. ExtraType.Sample,
  402. ExtraRuleType.Suffix,
  403. "_sample",
  404. MediaType.Video),
  405. new ExtraRule(
  406. ExtraType.Sample,
  407. ExtraRuleType.Suffix,
  408. " sample",
  409. MediaType.Video),
  410. new ExtraRule(
  411. ExtraType.ThemeSong,
  412. ExtraRuleType.Filename,
  413. "theme",
  414. MediaType.Audio),
  415. new ExtraRule(
  416. ExtraType.Scene,
  417. ExtraRuleType.Suffix,
  418. "-scene",
  419. MediaType.Video),
  420. new ExtraRule(
  421. ExtraType.Clip,
  422. ExtraRuleType.Suffix,
  423. "-clip",
  424. MediaType.Video),
  425. new ExtraRule(
  426. ExtraType.Interview,
  427. ExtraRuleType.Suffix,
  428. "-interview",
  429. MediaType.Video),
  430. new ExtraRule(
  431. ExtraType.BehindTheScenes,
  432. ExtraRuleType.Suffix,
  433. "-behindthescenes",
  434. MediaType.Video),
  435. new ExtraRule(
  436. ExtraType.DeletedScene,
  437. ExtraRuleType.Suffix,
  438. "-deleted",
  439. MediaType.Video),
  440. new ExtraRule(
  441. ExtraType.DeletedScene,
  442. ExtraRuleType.Suffix,
  443. "-deletedscene",
  444. MediaType.Video),
  445. new ExtraRule(
  446. ExtraType.Clip,
  447. ExtraRuleType.Suffix,
  448. "-featurette",
  449. MediaType.Video),
  450. new ExtraRule(
  451. ExtraType.Clip,
  452. ExtraRuleType.Suffix,
  453. "-short",
  454. MediaType.Video),
  455. new ExtraRule(
  456. ExtraType.BehindTheScenes,
  457. ExtraRuleType.DirectoryName,
  458. "behind the scenes",
  459. MediaType.Video),
  460. new ExtraRule(
  461. ExtraType.DeletedScene,
  462. ExtraRuleType.DirectoryName,
  463. "deleted scenes",
  464. MediaType.Video),
  465. new ExtraRule(
  466. ExtraType.Interview,
  467. ExtraRuleType.DirectoryName,
  468. "interviews",
  469. MediaType.Video),
  470. new ExtraRule(
  471. ExtraType.Scene,
  472. ExtraRuleType.DirectoryName,
  473. "scenes",
  474. MediaType.Video),
  475. new ExtraRule(
  476. ExtraType.Sample,
  477. ExtraRuleType.DirectoryName,
  478. "samples",
  479. MediaType.Video),
  480. new ExtraRule(
  481. ExtraType.Clip,
  482. ExtraRuleType.DirectoryName,
  483. "shorts",
  484. MediaType.Video),
  485. new ExtraRule(
  486. ExtraType.Clip,
  487. ExtraRuleType.DirectoryName,
  488. "featurettes",
  489. MediaType.Video),
  490. new ExtraRule(
  491. ExtraType.Unknown,
  492. ExtraRuleType.DirectoryName,
  493. "extras",
  494. MediaType.Video),
  495. };
  496. Format3DRules = new[]
  497. {
  498. // Kodi rules:
  499. new Format3DRule(
  500. precedingToken: "3d",
  501. token: "hsbs"),
  502. new Format3DRule(
  503. precedingToken: "3d",
  504. token: "sbs"),
  505. new Format3DRule(
  506. precedingToken: "3d",
  507. token: "htab"),
  508. new Format3DRule(
  509. precedingToken: "3d",
  510. token: "tab"),
  511. // Media Browser rules:
  512. new Format3DRule("fsbs"),
  513. new Format3DRule("hsbs"),
  514. new Format3DRule("sbs"),
  515. new Format3DRule("ftab"),
  516. new Format3DRule("htab"),
  517. new Format3DRule("tab"),
  518. new Format3DRule("sbs3d"),
  519. new Format3DRule("mvc")
  520. };
  521. AudioBookPartsExpressions = new[]
  522. {
  523. // Detect specified chapters, like CH 01
  524. @"ch(?:apter)?[\s_-]?(?<chapter>[0-9]+)",
  525. // Detect specified parts, like Part 02
  526. @"p(?:ar)?t[\s_-]?(?<part>[0-9]+)",
  527. // Chapter is often beginning of filename
  528. "^(?<chapter>[0-9]+)",
  529. // Part if often ending of filename
  530. @"(?<!ch(?:apter) )(?<part>[0-9]+)$",
  531. // Sometimes named as 0001_005 (chapter_part)
  532. "(?<chapter>[0-9]+)_(?<part>[0-9]+)",
  533. // Some audiobooks are ripped from cd's, and will be named by disk number.
  534. @"dis(?:c|k)[\s_-]?(?<chapter>[0-9]+)"
  535. };
  536. AudioBookNamesExpressions = new[]
  537. {
  538. // Detect year usually in brackets after name Batman (2020)
  539. @"^(?<name>.+?)\s*\(\s*(?<year>[0-9]{4})\s*\)\s*$",
  540. @"^\s*(?<name>[^ ].*?)\s*$"
  541. };
  542. var extensions = VideoFileExtensions.ToList();
  543. extensions.AddRange(new[]
  544. {
  545. ".mkv",
  546. ".m2t",
  547. ".m2ts",
  548. ".img",
  549. ".iso",
  550. ".mk3d",
  551. ".ts",
  552. ".rmvb",
  553. ".mov",
  554. ".avi",
  555. ".mpg",
  556. ".mpeg",
  557. ".wmv",
  558. ".mp4",
  559. ".divx",
  560. ".dvr-ms",
  561. ".wtv",
  562. ".ogm",
  563. ".ogv",
  564. ".asf",
  565. ".m4v",
  566. ".flv",
  567. ".f4v",
  568. ".3gp",
  569. ".webm",
  570. ".mts",
  571. ".m2v",
  572. ".rec",
  573. ".mxf"
  574. });
  575. MultipleEpisodeExpressions = new[]
  576. {
  577. @".*(\\|\/)[sS]?(?<seasonnumber>[0-9]{1,4})[xX](?<epnumber>[0-9]{1,3})((-| - )[0-9]{1,4}[eExX](?<endingepnumber>[0-9]{1,3}))+[^\\\/]*$",
  578. @".*(\\|\/)[sS]?(?<seasonnumber>[0-9]{1,4})[xX](?<epnumber>[0-9]{1,3})((-| - )[0-9]{1,4}[xX][eE](?<endingepnumber>[0-9]{1,3}))+[^\\\/]*$",
  579. @".*(\\|\/)[sS]?(?<seasonnumber>[0-9]{1,4})[xX](?<epnumber>[0-9]{1,3})((-| - )?[xXeE](?<endingepnumber>[0-9]{1,3}))+[^\\\/]*$",
  580. @".*(\\|\/)[sS]?(?<seasonnumber>[0-9]{1,4})[xX](?<epnumber>[0-9]{1,3})(-[xE]?[eE]?(?<endingepnumber>[0-9]{1,3}))+[^\\\/]*$",
  581. @".*(\\|\/)(?<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}))+[^\\\/]*$",
  582. @".*(\\|\/)(?<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}))+[^\\\/]*$",
  583. @".*(\\|\/)(?<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}))+[^\\\/]*$",
  584. @".*(\\|\/)(?<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}))+[^\\\/]*$",
  585. @".*(\\|\/)(?<seriesname>[^\\\/]*)[sS](?<seasonnumber>[0-9]{1,4})[xX\.]?[eE](?<epnumber>[0-9]{1,3})((-| - )?[xXeE](?<endingepnumber>[0-9]{1,3}))+[^\\\/]*$",
  586. @".*(\\|\/)(?<seriesname>[^\\\/]*)[sS](?<seasonnumber>[0-9]{1,4})[xX\.]?[eE](?<epnumber>[0-9]{1,3})(-[xX]?[eE]?(?<endingepnumber>[0-9]{1,3}))+[^\\\/]*$"
  587. }.Select(i => new EpisodeExpression(i)
  588. {
  589. IsNamed = true
  590. }).ToArray();
  591. VideoFileExtensions = extensions
  592. .Distinct(StringComparer.OrdinalIgnoreCase)
  593. .ToArray();
  594. Compile();
  595. }
  596. /// <summary>
  597. /// Gets or sets list of audio file extensions.
  598. /// </summary>
  599. public string[] AudioFileExtensions { get; set; }
  600. /// <summary>
  601. /// Gets or sets list of album stacking prefixes.
  602. /// </summary>
  603. public string[] AlbumStackingPrefixes { get; set; }
  604. /// <summary>
  605. /// Gets or sets list of subtitle file extensions.
  606. /// </summary>
  607. public string[] SubtitleFileExtensions { get; set; }
  608. /// <summary>
  609. /// Gets or sets list of subtitles flag delimiters.
  610. /// </summary>
  611. public char[] SubtitleFlagDelimiters { get; set; }
  612. /// <summary>
  613. /// Gets or sets list of subtitle forced flags.
  614. /// </summary>
  615. public string[] SubtitleForcedFlags { get; set; }
  616. /// <summary>
  617. /// Gets or sets list of subtitle default flags.
  618. /// </summary>
  619. public string[] SubtitleDefaultFlags { get; set; }
  620. /// <summary>
  621. /// Gets or sets list of episode regular expressions.
  622. /// </summary>
  623. public EpisodeExpression[] EpisodeExpressions { get; set; }
  624. /// <summary>
  625. /// Gets or sets list of raw episode without season regular expressions strings.
  626. /// </summary>
  627. public string[] EpisodeWithoutSeasonExpressions { get; set; }
  628. /// <summary>
  629. /// Gets or sets list of raw multi-part episodes regular expressions strings.
  630. /// </summary>
  631. public string[] EpisodeMultiPartExpressions { get; set; }
  632. /// <summary>
  633. /// Gets or sets list of video file extensions.
  634. /// </summary>
  635. public string[] VideoFileExtensions { get; set; }
  636. /// <summary>
  637. /// Gets or sets list of video stub file extensions.
  638. /// </summary>
  639. public string[] StubFileExtensions { get; set; }
  640. /// <summary>
  641. /// Gets or sets list of raw audiobook parts regular expressions strings.
  642. /// </summary>
  643. public string[] AudioBookPartsExpressions { get; set; }
  644. /// <summary>
  645. /// Gets or sets list of raw audiobook names regular expressions strings.
  646. /// </summary>
  647. public string[] AudioBookNamesExpressions { get; set; }
  648. /// <summary>
  649. /// Gets or sets list of stub type rules.
  650. /// </summary>
  651. public StubTypeRule[] StubTypes { get; set; }
  652. /// <summary>
  653. /// Gets or sets list of video flag delimiters.
  654. /// </summary>
  655. public char[] VideoFlagDelimiters { get; set; }
  656. /// <summary>
  657. /// Gets or sets list of 3D Format rules.
  658. /// </summary>
  659. public Format3DRule[] Format3DRules { get; set; }
  660. /// <summary>
  661. /// Gets or sets list of raw video file-stacking expressions strings.
  662. /// </summary>
  663. public string[] VideoFileStackingExpressions { get; set; }
  664. /// <summary>
  665. /// Gets or sets list of raw clean DateTimes regular expressions strings.
  666. /// </summary>
  667. public string[] CleanDateTimes { get; set; }
  668. /// <summary>
  669. /// Gets or sets list of raw clean strings regular expressions strings.
  670. /// </summary>
  671. public string[] CleanStrings { get; set; }
  672. /// <summary>
  673. /// Gets or sets list of multi-episode regular expressions.
  674. /// </summary>
  675. public EpisodeExpression[] MultipleEpisodeExpressions { get; set; }
  676. /// <summary>
  677. /// Gets or sets list of extra rules for videos.
  678. /// </summary>
  679. public ExtraRule[] VideoExtraRules { get; set; }
  680. /// <summary>
  681. /// Gets list of video file-stack regular expressions.
  682. /// </summary>
  683. public Regex[] VideoFileStackingRegexes { get; private set; } = Array.Empty<Regex>();
  684. /// <summary>
  685. /// Gets list of clean datetime regular expressions.
  686. /// </summary>
  687. public Regex[] CleanDateTimeRegexes { get; private set; } = Array.Empty<Regex>();
  688. /// <summary>
  689. /// Gets list of clean string regular expressions.
  690. /// </summary>
  691. public Regex[] CleanStringRegexes { get; private set; } = Array.Empty<Regex>();
  692. /// <summary>
  693. /// Gets list of episode without season regular expressions.
  694. /// </summary>
  695. public Regex[] EpisodeWithoutSeasonRegexes { get; private set; } = Array.Empty<Regex>();
  696. /// <summary>
  697. /// Gets list of multi-part episode regular expressions.
  698. /// </summary>
  699. public Regex[] EpisodeMultiPartRegexes { get; private set; } = Array.Empty<Regex>();
  700. /// <summary>
  701. /// Compiles raw regex strings into regexes.
  702. /// </summary>
  703. public void Compile()
  704. {
  705. VideoFileStackingRegexes = VideoFileStackingExpressions.Select(Compile).ToArray();
  706. CleanDateTimeRegexes = CleanDateTimes.Select(Compile).ToArray();
  707. CleanStringRegexes = CleanStrings.Select(Compile).ToArray();
  708. EpisodeWithoutSeasonRegexes = EpisodeWithoutSeasonExpressions.Select(Compile).ToArray();
  709. EpisodeMultiPartRegexes = EpisodeMultiPartExpressions.Select(Compile).ToArray();
  710. }
  711. private Regex Compile(string exp)
  712. {
  713. return new Regex(exp, RegexOptions.IgnoreCase | RegexOptions.Compiled);
  714. }
  715. }
  716. }