NamingOptions.cs 31 KB

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