BaseItemXmlParser.cs 45 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277
  1. using MediaBrowser.Controller.Entities;
  2. using MediaBrowser.Controller.Persistence;
  3. using MediaBrowser.Model.Entities;
  4. using MediaBrowser.Model.Logging;
  5. using System;
  6. using System.Collections.Generic;
  7. using System.Globalization;
  8. using System.IO;
  9. using System.Linq;
  10. using System.Text;
  11. using System.Threading;
  12. using System.Threading.Tasks;
  13. using System.Xml;
  14. namespace MediaBrowser.Controller.Providers
  15. {
  16. /// <summary>
  17. /// Provides a base class for parsing metadata xml
  18. /// </summary>
  19. /// <typeparam name="T"></typeparam>
  20. public class BaseItemXmlParser<T>
  21. where T : BaseItem
  22. {
  23. /// <summary>
  24. /// The logger
  25. /// </summary>
  26. protected ILogger Logger { get; private set; }
  27. /// <summary>
  28. /// Initializes a new instance of the <see cref="BaseItemXmlParser{T}" /> class.
  29. /// </summary>
  30. /// <param name="logger">The logger.</param>
  31. public BaseItemXmlParser(ILogger logger)
  32. {
  33. Logger = logger;
  34. }
  35. /// <summary>
  36. /// Fetches metadata for an item from one xml file
  37. /// </summary>
  38. /// <param name="item">The item.</param>
  39. /// <param name="metadataFile">The metadata file.</param>
  40. /// <param name="cancellationToken">The cancellation token.</param>
  41. /// <exception cref="System.ArgumentNullException"></exception>
  42. public void Fetch(T item, string metadataFile, CancellationToken cancellationToken)
  43. {
  44. if (item == null)
  45. {
  46. throw new ArgumentNullException();
  47. }
  48. if (string.IsNullOrEmpty(metadataFile))
  49. {
  50. throw new ArgumentNullException();
  51. }
  52. var settings = new XmlReaderSettings
  53. {
  54. CheckCharacters = false,
  55. IgnoreProcessingInstructions = true,
  56. IgnoreComments = true,
  57. ValidationType = ValidationType.None
  58. };
  59. var hasTaglines = item as IHasTaglines;
  60. if (hasTaglines != null)
  61. {
  62. hasTaglines.Taglines.Clear();
  63. }
  64. item.Studios.Clear();
  65. item.Genres.Clear();
  66. item.People.Clear();
  67. var hasTags = item as IHasTags;
  68. if (hasTags != null)
  69. {
  70. hasTags.Tags.Clear();
  71. }
  72. var hasKeywords = item as IHasKeywords;
  73. if (hasKeywords != null)
  74. {
  75. hasKeywords.Keywords.Clear();
  76. }
  77. var hasTrailers = item as IHasTrailers;
  78. if (hasTrailers != null)
  79. {
  80. hasTrailers.RemoteTrailers.Clear();
  81. }
  82. //Fetch(item, metadataFile, settings, Encoding.GetEncoding("ISO-8859-1"), cancellationToken);
  83. Fetch(item, metadataFile, settings, Encoding.UTF8, cancellationToken);
  84. }
  85. /// <summary>
  86. /// Fetches the specified item.
  87. /// </summary>
  88. /// <param name="item">The item.</param>
  89. /// <param name="metadataFile">The metadata file.</param>
  90. /// <param name="settings">The settings.</param>
  91. /// <param name="encoding">The encoding.</param>
  92. /// <param name="cancellationToken">The cancellation token.</param>
  93. private void Fetch(T item, string metadataFile, XmlReaderSettings settings, Encoding encoding, CancellationToken cancellationToken)
  94. {
  95. using (var streamReader = new StreamReader(metadataFile, encoding))
  96. {
  97. // Use XmlReader for best performance
  98. using (var reader = XmlReader.Create(streamReader, settings))
  99. {
  100. reader.MoveToContent();
  101. // Loop through each element
  102. while (reader.Read())
  103. {
  104. cancellationToken.ThrowIfCancellationRequested();
  105. if (reader.NodeType == XmlNodeType.Element)
  106. {
  107. FetchDataFromXmlNode(reader, item);
  108. }
  109. }
  110. }
  111. }
  112. }
  113. private readonly CultureInfo _usCulture = new CultureInfo("en-US");
  114. /// <summary>
  115. /// Fetches metadata from one Xml Element
  116. /// </summary>
  117. /// <param name="reader">The reader.</param>
  118. /// <param name="item">The item.</param>
  119. protected virtual void FetchDataFromXmlNode(XmlReader reader, T item)
  120. {
  121. switch (reader.Name)
  122. {
  123. // DateCreated
  124. case "Added":
  125. DateTime added;
  126. if (DateTime.TryParse(reader.ReadElementContentAsString() ?? string.Empty, out added))
  127. {
  128. item.DateCreated = added.ToUniversalTime();
  129. }
  130. break;
  131. case "LocalTitle":
  132. item.Name = reader.ReadElementContentAsString();
  133. break;
  134. case "Type":
  135. {
  136. var type = reader.ReadElementContentAsString();
  137. if (!string.IsNullOrWhiteSpace(type) && !type.Equals("none", StringComparison.OrdinalIgnoreCase))
  138. {
  139. item.DisplayMediaType = type;
  140. }
  141. break;
  142. }
  143. case "CriticRating":
  144. {
  145. var text = reader.ReadElementContentAsString();
  146. var hasCriticRating = item as IHasCriticRating;
  147. if (hasCriticRating != null && !string.IsNullOrEmpty(text))
  148. {
  149. float value;
  150. if (float.TryParse(text, NumberStyles.Any, _usCulture, out value))
  151. {
  152. hasCriticRating.CriticRating = value;
  153. }
  154. }
  155. break;
  156. }
  157. case "Budget":
  158. {
  159. var text = reader.ReadElementContentAsString();
  160. var hasBudget = item as IHasBudget;
  161. if (hasBudget != null)
  162. {
  163. double value;
  164. if (double.TryParse(text, NumberStyles.Any, _usCulture, out value))
  165. {
  166. hasBudget.Budget = value;
  167. }
  168. }
  169. break;
  170. }
  171. case "Revenue":
  172. {
  173. var text = reader.ReadElementContentAsString();
  174. var hasBudget = item as IHasBudget;
  175. if (hasBudget != null)
  176. {
  177. double value;
  178. if (double.TryParse(text, NumberStyles.Any, _usCulture, out value))
  179. {
  180. hasBudget.Revenue = value;
  181. }
  182. }
  183. break;
  184. }
  185. case "Metascore":
  186. {
  187. var text = reader.ReadElementContentAsString();
  188. var hasMetascore = item as IHasMetascore;
  189. if (hasMetascore != null)
  190. {
  191. float value;
  192. if (float.TryParse(text, NumberStyles.Any, _usCulture, out value))
  193. {
  194. hasMetascore.Metascore = value;
  195. }
  196. }
  197. break;
  198. }
  199. case "AwardSummary":
  200. {
  201. var text = reader.ReadElementContentAsString();
  202. var hasAwards = item as IHasAwards;
  203. if (hasAwards != null)
  204. {
  205. if (!string.IsNullOrWhiteSpace(text))
  206. {
  207. hasAwards.AwardSummary = text;
  208. }
  209. }
  210. break;
  211. }
  212. case "SortTitle":
  213. {
  214. var val = reader.ReadElementContentAsString();
  215. if (!string.IsNullOrWhiteSpace(val))
  216. {
  217. item.ForcedSortName = val;
  218. }
  219. break;
  220. }
  221. case "Overview":
  222. case "Description":
  223. {
  224. var val = reader.ReadElementContentAsString();
  225. if (!string.IsNullOrWhiteSpace(val))
  226. {
  227. item.Overview = val;
  228. }
  229. break;
  230. }
  231. case "CriticRatingSummary":
  232. {
  233. var val = reader.ReadElementContentAsString();
  234. if (!string.IsNullOrWhiteSpace(val))
  235. {
  236. var hasCriticRating = item as IHasCriticRating;
  237. if (hasCriticRating != null)
  238. {
  239. hasCriticRating.CriticRatingSummary = val;
  240. }
  241. }
  242. break;
  243. }
  244. case "Language":
  245. {
  246. var val = reader.ReadElementContentAsString();
  247. var hasLanguage = item as IHasPreferredMetadataLanguage;
  248. if (hasLanguage != null)
  249. {
  250. hasLanguage.PreferredMetadataLanguage = val;
  251. }
  252. break;
  253. }
  254. case "PlaceOfBirth":
  255. {
  256. var val = reader.ReadElementContentAsString();
  257. if (!string.IsNullOrWhiteSpace(val))
  258. {
  259. var person = item as Person;
  260. if (person != null)
  261. {
  262. person.PlaceOfBirth = val;
  263. }
  264. }
  265. break;
  266. }
  267. case "Website":
  268. {
  269. var val = reader.ReadElementContentAsString();
  270. if (!string.IsNullOrWhiteSpace(val))
  271. {
  272. item.HomePageUrl = val;
  273. }
  274. break;
  275. }
  276. case "LockedFields":
  277. {
  278. var fields = new List<MetadataFields>();
  279. var val = reader.ReadElementContentAsString();
  280. if (!string.IsNullOrWhiteSpace(val))
  281. {
  282. var list = val.Split('|').Select(i =>
  283. {
  284. MetadataFields field;
  285. if (Enum.TryParse<MetadataFields>(i, true, out field))
  286. {
  287. return (MetadataFields?)field;
  288. }
  289. return null;
  290. }).Where(i => i.HasValue).Select(i => i.Value);
  291. fields.AddRange(list);
  292. }
  293. item.LockedFields = fields;
  294. break;
  295. }
  296. case "TagLines":
  297. {
  298. using (var subtree = reader.ReadSubtree())
  299. {
  300. FetchFromTaglinesNode(subtree, item);
  301. }
  302. break;
  303. }
  304. case "ContentRating":
  305. case "MPAARating":
  306. {
  307. var rating = reader.ReadElementContentAsString();
  308. if (!string.IsNullOrWhiteSpace(rating))
  309. {
  310. item.OfficialRating = rating;
  311. }
  312. break;
  313. }
  314. case "MPAADescription":
  315. {
  316. var rating = reader.ReadElementContentAsString();
  317. if (!string.IsNullOrWhiteSpace(rating))
  318. {
  319. item.OfficialRatingDescription = rating;
  320. }
  321. break;
  322. }
  323. case "CustomRating":
  324. {
  325. var val = reader.ReadElementContentAsString();
  326. if (!string.IsNullOrWhiteSpace(val))
  327. {
  328. item.CustomRating = val;
  329. }
  330. break;
  331. }
  332. case "RunningTime":
  333. {
  334. var text = reader.ReadElementContentAsString();
  335. if (!string.IsNullOrWhiteSpace(text))
  336. {
  337. int runtime;
  338. if (int.TryParse(text.Split(' ')[0], NumberStyles.Integer, _usCulture, out runtime))
  339. {
  340. item.RunTimeTicks = TimeSpan.FromMinutes(runtime).Ticks;
  341. }
  342. }
  343. break;
  344. }
  345. case "AspectRatio":
  346. {
  347. var val = reader.ReadElementContentAsString();
  348. var hasAspectRatio = item as IHasAspectRatio;
  349. if (!string.IsNullOrWhiteSpace(val) && hasAspectRatio != null)
  350. {
  351. hasAspectRatio.AspectRatio = val;
  352. }
  353. break;
  354. }
  355. case "LockData":
  356. {
  357. var val = reader.ReadElementContentAsString();
  358. if (!string.IsNullOrWhiteSpace(val))
  359. {
  360. item.DontFetchMeta = string.Equals("true", val, StringComparison.OrdinalIgnoreCase);
  361. }
  362. break;
  363. }
  364. case "Network":
  365. {
  366. foreach (var name in SplitNames(reader.ReadElementContentAsString()))
  367. {
  368. if (string.IsNullOrWhiteSpace(name))
  369. {
  370. continue;
  371. }
  372. item.AddStudio(name);
  373. }
  374. break;
  375. }
  376. case "Director":
  377. {
  378. foreach (var p in SplitNames(reader.ReadElementContentAsString()).Select(v => new Entities.PersonInfo { Name = v.Trim(), Type = PersonType.Director }))
  379. {
  380. if (string.IsNullOrWhiteSpace(p.Name))
  381. {
  382. continue;
  383. }
  384. item.AddPerson(p);
  385. }
  386. break;
  387. }
  388. case "Writer":
  389. {
  390. foreach (var p in SplitNames(reader.ReadElementContentAsString()).Select(v => new Entities.PersonInfo { Name = v.Trim(), Type = PersonType.Writer }))
  391. {
  392. if (string.IsNullOrWhiteSpace(p.Name))
  393. {
  394. continue;
  395. }
  396. item.AddPerson(p);
  397. }
  398. break;
  399. }
  400. case "Actors":
  401. {
  402. var actors = reader.ReadInnerXml();
  403. if (actors.Contains("<"))
  404. {
  405. // This is one of the mis-named "Actors" full nodes created by MB2
  406. // Create a reader and pass it to the persons node processor
  407. FetchDataFromPersonsNode(new XmlTextReader(new StringReader("<Persons>" + actors + "</Persons>")), item);
  408. }
  409. else
  410. {
  411. // Old-style piped string
  412. foreach (var p in SplitNames(actors).Select(v => new Entities.PersonInfo { Name = v.Trim(), Type = PersonType.Actor }))
  413. {
  414. if (string.IsNullOrWhiteSpace(p.Name))
  415. {
  416. continue;
  417. }
  418. item.AddPerson(p);
  419. }
  420. }
  421. break;
  422. }
  423. case "GuestStars":
  424. {
  425. foreach (var p in SplitNames(reader.ReadElementContentAsString()).Select(v => new Entities.PersonInfo { Name = v.Trim(), Type = PersonType.GuestStar }))
  426. {
  427. if (string.IsNullOrWhiteSpace(p.Name))
  428. {
  429. continue;
  430. }
  431. item.AddPerson(p);
  432. }
  433. break;
  434. }
  435. case "Trailer":
  436. {
  437. var val = reader.ReadElementContentAsString();
  438. var hasTrailers = item as IHasTrailers;
  439. if (hasTrailers != null)
  440. {
  441. if (!string.IsNullOrWhiteSpace(val))
  442. {
  443. hasTrailers.AddTrailerUrl(val, false);
  444. }
  445. }
  446. break;
  447. }
  448. case "DisplayOrder":
  449. {
  450. var val = reader.ReadElementContentAsString();
  451. var hasDisplayOrder = item as IHasDisplayOrder;
  452. if (hasDisplayOrder != null)
  453. {
  454. if (!string.IsNullOrWhiteSpace(val))
  455. {
  456. hasDisplayOrder.DisplayOrder = val;
  457. }
  458. }
  459. break;
  460. }
  461. case "Trailers":
  462. {
  463. using (var subtree = reader.ReadSubtree())
  464. {
  465. var hasTrailers = item as IHasTrailers;
  466. if (hasTrailers != null)
  467. {
  468. FetchDataFromTrailersNode(subtree, hasTrailers);
  469. }
  470. }
  471. break;
  472. }
  473. case "ProductionYear":
  474. {
  475. var val = reader.ReadElementContentAsString();
  476. if (!string.IsNullOrWhiteSpace(val))
  477. {
  478. int productionYear;
  479. if (int.TryParse(val, out productionYear) && productionYear > 1850)
  480. {
  481. item.ProductionYear = productionYear;
  482. }
  483. }
  484. break;
  485. }
  486. case "Rating":
  487. case "IMDBrating":
  488. {
  489. var rating = reader.ReadElementContentAsString();
  490. if (!string.IsNullOrWhiteSpace(rating))
  491. {
  492. float val;
  493. // All external meta is saving this as '.' for decimal I believe...but just to be sure
  494. if (float.TryParse(rating.Replace(',', '.'), NumberStyles.AllowDecimalPoint, CultureInfo.InvariantCulture, out val))
  495. {
  496. item.CommunityRating = val;
  497. }
  498. }
  499. break;
  500. }
  501. case "BirthDate":
  502. case "PremiereDate":
  503. case "FirstAired":
  504. {
  505. var firstAired = reader.ReadElementContentAsString();
  506. if (!string.IsNullOrWhiteSpace(firstAired))
  507. {
  508. DateTime airDate;
  509. if (DateTime.TryParseExact(firstAired, "yyyy-MM-dd", CultureInfo.InvariantCulture, DateTimeStyles.AssumeLocal, out airDate) && airDate.Year > 1850)
  510. {
  511. item.PremiereDate = airDate.ToUniversalTime();
  512. item.ProductionYear = airDate.Year;
  513. }
  514. }
  515. break;
  516. }
  517. case "DeathDate":
  518. case "EndDate":
  519. {
  520. var firstAired = reader.ReadElementContentAsString();
  521. if (!string.IsNullOrWhiteSpace(firstAired))
  522. {
  523. DateTime airDate;
  524. if (DateTime.TryParseExact(firstAired, "yyyy-MM-dd", CultureInfo.InvariantCulture, DateTimeStyles.AssumeLocal, out airDate) && airDate.Year > 1850)
  525. {
  526. item.EndDate = airDate.ToUniversalTime();
  527. }
  528. }
  529. break;
  530. }
  531. case "TvDbId":
  532. var tvdbId = reader.ReadElementContentAsString();
  533. if (!string.IsNullOrWhiteSpace(tvdbId))
  534. {
  535. item.SetProviderId(MetadataProviders.Tvdb, tvdbId);
  536. }
  537. break;
  538. case "VoteCount":
  539. {
  540. var val = reader.ReadElementContentAsString();
  541. if (!string.IsNullOrWhiteSpace(val))
  542. {
  543. int num;
  544. if (int.TryParse(val, NumberStyles.Integer, _usCulture, out num))
  545. {
  546. item.VoteCount = num;
  547. }
  548. }
  549. break;
  550. }
  551. case "MusicBrainzAlbumId":
  552. {
  553. var mbz = reader.ReadElementContentAsString();
  554. if (!string.IsNullOrWhiteSpace(mbz))
  555. {
  556. item.SetProviderId(MetadataProviders.MusicBrainzAlbum, mbz);
  557. }
  558. break;
  559. }
  560. case "MusicBrainzAlbumArtistId":
  561. {
  562. var mbz = reader.ReadElementContentAsString();
  563. if (!string.IsNullOrWhiteSpace(mbz))
  564. {
  565. item.SetProviderId(MetadataProviders.MusicBrainzAlbumArtist, mbz);
  566. }
  567. break;
  568. }
  569. case "MusicBrainzArtistId":
  570. {
  571. var mbz = reader.ReadElementContentAsString();
  572. if (!string.IsNullOrWhiteSpace(mbz))
  573. {
  574. item.SetProviderId(MetadataProviders.MusicBrainzArtist, mbz);
  575. }
  576. break;
  577. }
  578. case "MusicBrainzReleaseGroupId":
  579. {
  580. var mbz = reader.ReadElementContentAsString();
  581. if (!string.IsNullOrWhiteSpace(mbz))
  582. {
  583. item.SetProviderId(MetadataProviders.MusicBrainzReleaseGroup, mbz);
  584. }
  585. break;
  586. }
  587. case "TVRageId":
  588. {
  589. var id = reader.ReadElementContentAsString();
  590. if (!string.IsNullOrWhiteSpace(id))
  591. {
  592. item.SetProviderId(MetadataProviders.TvRage, id);
  593. }
  594. break;
  595. }
  596. case "AudioDbArtistId":
  597. {
  598. var id = reader.ReadElementContentAsString();
  599. if (!string.IsNullOrWhiteSpace(id))
  600. {
  601. item.SetProviderId(MetadataProviders.AudioDbArtist, id);
  602. }
  603. break;
  604. }
  605. case "AudioDbAlbumId":
  606. {
  607. var id = reader.ReadElementContentAsString();
  608. if (!string.IsNullOrWhiteSpace(id))
  609. {
  610. item.SetProviderId(MetadataProviders.AudioDbAlbum, id);
  611. }
  612. break;
  613. }
  614. case "RottenTomatoesId":
  615. var rtId = reader.ReadElementContentAsString();
  616. if (!string.IsNullOrWhiteSpace(rtId))
  617. {
  618. item.SetProviderId(MetadataProviders.RottenTomatoes, rtId);
  619. }
  620. break;
  621. case "TMDbId":
  622. var tmdb = reader.ReadElementContentAsString();
  623. if (!string.IsNullOrWhiteSpace(tmdb))
  624. {
  625. item.SetProviderId(MetadataProviders.Tmdb, tmdb);
  626. }
  627. break;
  628. case "TMDbCollectionId":
  629. var tmdbCollection = reader.ReadElementContentAsString();
  630. if (!string.IsNullOrWhiteSpace(tmdbCollection))
  631. {
  632. item.SetProviderId(MetadataProviders.TmdbCollection, tmdbCollection);
  633. }
  634. break;
  635. case "TVcomId":
  636. var TVcomId = reader.ReadElementContentAsString();
  637. if (!string.IsNullOrWhiteSpace(TVcomId))
  638. {
  639. item.SetProviderId(MetadataProviders.Tvcom, TVcomId);
  640. }
  641. break;
  642. case "Zap2ItId":
  643. var zap2ItId = reader.ReadElementContentAsString();
  644. if (!string.IsNullOrWhiteSpace(zap2ItId))
  645. {
  646. item.SetProviderId(MetadataProviders.Zap2It, zap2ItId);
  647. }
  648. break;
  649. case "IMDB":
  650. var imDbId = reader.ReadElementContentAsString();
  651. if (!string.IsNullOrWhiteSpace(imDbId))
  652. {
  653. item.SetProviderId(MetadataProviders.Imdb, imDbId);
  654. }
  655. break;
  656. case "Genres":
  657. {
  658. using (var subtree = reader.ReadSubtree())
  659. {
  660. FetchFromGenresNode(subtree, item);
  661. }
  662. break;
  663. }
  664. case "Tags":
  665. {
  666. using (var subtree = reader.ReadSubtree())
  667. {
  668. var hasTags = item as IHasTags;
  669. if (hasTags != null)
  670. {
  671. FetchFromTagsNode(subtree, hasTags);
  672. }
  673. }
  674. break;
  675. }
  676. case "PlotKeywords":
  677. {
  678. using (var subtree = reader.ReadSubtree())
  679. {
  680. var hasTags = item as IHasKeywords;
  681. if (hasTags != null)
  682. {
  683. FetchFromKeywordsNode(subtree, hasTags);
  684. }
  685. }
  686. break;
  687. }
  688. case "Persons":
  689. {
  690. using (var subtree = reader.ReadSubtree())
  691. {
  692. FetchDataFromPersonsNode(subtree, item);
  693. }
  694. break;
  695. }
  696. case "Studios":
  697. {
  698. using (var subtree = reader.ReadSubtree())
  699. {
  700. FetchFromStudiosNode(subtree, item);
  701. }
  702. break;
  703. }
  704. case "Format3D":
  705. {
  706. var video = item as Video;
  707. if (video != null)
  708. {
  709. var val = reader.ReadElementContentAsString();
  710. if (string.Equals("HSBS", val))
  711. {
  712. video.Video3DFormat = Video3DFormat.HalfSideBySide;
  713. }
  714. else if (string.Equals("HTAB", val))
  715. {
  716. video.Video3DFormat = Video3DFormat.HalfTopAndBottom;
  717. }
  718. else if (string.Equals("FTAB", val))
  719. {
  720. video.Video3DFormat = Video3DFormat.FullTopAndBottom;
  721. }
  722. else if (string.Equals("FSBS", val))
  723. {
  724. video.Video3DFormat = Video3DFormat.FullSideBySide;
  725. }
  726. }
  727. break;
  728. }
  729. default:
  730. reader.Skip();
  731. break;
  732. }
  733. }
  734. /// <summary>
  735. /// Fetches from taglines node.
  736. /// </summary>
  737. /// <param name="reader">The reader.</param>
  738. /// <param name="item">The item.</param>
  739. private void FetchFromTaglinesNode(XmlReader reader, T item)
  740. {
  741. reader.MoveToContent();
  742. while (reader.Read())
  743. {
  744. if (reader.NodeType == XmlNodeType.Element)
  745. {
  746. switch (reader.Name)
  747. {
  748. case "Tagline":
  749. {
  750. var val = reader.ReadElementContentAsString();
  751. if (!string.IsNullOrWhiteSpace(val))
  752. {
  753. var hasTaglines = item as IHasTaglines;
  754. if (hasTaglines != null)
  755. {
  756. if (!string.IsNullOrWhiteSpace(val))
  757. {
  758. hasTaglines.AddTagline(val);
  759. }
  760. }
  761. }
  762. break;
  763. }
  764. default:
  765. reader.Skip();
  766. break;
  767. }
  768. }
  769. }
  770. }
  771. /// <summary>
  772. /// Fetches from genres node.
  773. /// </summary>
  774. /// <param name="reader">The reader.</param>
  775. /// <param name="item">The item.</param>
  776. private void FetchFromGenresNode(XmlReader reader, T item)
  777. {
  778. reader.MoveToContent();
  779. while (reader.Read())
  780. {
  781. if (reader.NodeType == XmlNodeType.Element)
  782. {
  783. switch (reader.Name)
  784. {
  785. case "Genre":
  786. {
  787. var genre = reader.ReadElementContentAsString();
  788. if (!string.IsNullOrWhiteSpace(genre))
  789. {
  790. item.AddGenre(genre);
  791. }
  792. break;
  793. }
  794. default:
  795. reader.Skip();
  796. break;
  797. }
  798. }
  799. }
  800. }
  801. private void FetchFromTagsNode(XmlReader reader, IHasTags item)
  802. {
  803. reader.MoveToContent();
  804. while (reader.Read())
  805. {
  806. if (reader.NodeType == XmlNodeType.Element)
  807. {
  808. switch (reader.Name)
  809. {
  810. case "Tag":
  811. {
  812. var tag = reader.ReadElementContentAsString();
  813. if (!string.IsNullOrWhiteSpace(tag))
  814. {
  815. item.AddTag(tag);
  816. }
  817. break;
  818. }
  819. default:
  820. reader.Skip();
  821. break;
  822. }
  823. }
  824. }
  825. }
  826. private void FetchFromKeywordsNode(XmlReader reader, IHasKeywords item)
  827. {
  828. reader.MoveToContent();
  829. while (reader.Read())
  830. {
  831. if (reader.NodeType == XmlNodeType.Element)
  832. {
  833. switch (reader.Name)
  834. {
  835. case "PlotKeyword":
  836. {
  837. var tag = reader.ReadElementContentAsString();
  838. if (!string.IsNullOrWhiteSpace(tag))
  839. {
  840. item.AddKeyword(tag);
  841. }
  842. break;
  843. }
  844. default:
  845. reader.Skip();
  846. break;
  847. }
  848. }
  849. }
  850. }
  851. /// <summary>
  852. /// Fetches the data from persons node.
  853. /// </summary>
  854. /// <param name="reader">The reader.</param>
  855. /// <param name="item">The item.</param>
  856. private void FetchDataFromPersonsNode(XmlReader reader, T item)
  857. {
  858. reader.MoveToContent();
  859. while (reader.Read())
  860. {
  861. if (reader.NodeType == XmlNodeType.Element)
  862. {
  863. switch (reader.Name)
  864. {
  865. case "Person":
  866. case "Actor":
  867. {
  868. using (var subtree = reader.ReadSubtree())
  869. {
  870. foreach (var person in GetPersonsFromXmlNode(subtree))
  871. {
  872. if (string.IsNullOrWhiteSpace(person.Name))
  873. {
  874. continue;
  875. }
  876. item.AddPerson(person);
  877. }
  878. }
  879. break;
  880. }
  881. default:
  882. reader.Skip();
  883. break;
  884. }
  885. }
  886. }
  887. }
  888. private void FetchDataFromTrailersNode(XmlReader reader, IHasTrailers item)
  889. {
  890. reader.MoveToContent();
  891. while (reader.Read())
  892. {
  893. if (reader.NodeType == XmlNodeType.Element)
  894. {
  895. switch (reader.Name)
  896. {
  897. case "Trailer":
  898. {
  899. var val = reader.ReadElementContentAsString();
  900. if (!string.IsNullOrWhiteSpace(val))
  901. {
  902. item.AddTrailerUrl(val, false);
  903. }
  904. break;
  905. }
  906. default:
  907. reader.Skip();
  908. break;
  909. }
  910. }
  911. }
  912. }
  913. protected async Task FetchChaptersFromXmlNode(BaseItem item, XmlReader reader, IItemRepository repository, CancellationToken cancellationToken)
  914. {
  915. var runtime = item.RunTimeTicks ?? 0;
  916. using (reader)
  917. {
  918. var chapters = GetChaptersFromXmlNode(reader)
  919. .Where(i => i.StartPositionTicks >= 0 && i.StartPositionTicks < runtime);
  920. await repository.SaveChapters(item.Id, chapters, cancellationToken).ConfigureAwait(false);
  921. }
  922. }
  923. private IEnumerable<ChapterInfo> GetChaptersFromXmlNode(XmlReader reader)
  924. {
  925. var chapters = new List<ChapterInfo>();
  926. reader.MoveToContent();
  927. while (reader.Read())
  928. {
  929. if (reader.NodeType == XmlNodeType.Element)
  930. {
  931. switch (reader.Name)
  932. {
  933. case "Chapter":
  934. {
  935. using (var subtree = reader.ReadSubtree())
  936. {
  937. chapters.Add(GetChapterInfoFromXmlNode(subtree));
  938. }
  939. break;
  940. }
  941. default:
  942. reader.Skip();
  943. break;
  944. }
  945. }
  946. }
  947. return chapters;
  948. }
  949. private ChapterInfo GetChapterInfoFromXmlNode(XmlReader reader)
  950. {
  951. var chapter = new ChapterInfo();
  952. reader.MoveToContent();
  953. while (reader.Read())
  954. {
  955. if (reader.NodeType == XmlNodeType.Element)
  956. {
  957. switch (reader.Name)
  958. {
  959. case "StartPositionMs":
  960. {
  961. var val = reader.ReadElementContentAsString();
  962. var ms = long.Parse(val, _usCulture);
  963. chapter.StartPositionTicks = TimeSpan.FromMilliseconds(ms).Ticks;
  964. break;
  965. }
  966. case "Name":
  967. {
  968. chapter.Name = reader.ReadElementContentAsString();
  969. break;
  970. }
  971. default:
  972. reader.Skip();
  973. break;
  974. }
  975. }
  976. }
  977. return chapter;
  978. }
  979. /// <summary>
  980. /// Fetches from studios node.
  981. /// </summary>
  982. /// <param name="reader">The reader.</param>
  983. /// <param name="item">The item.</param>
  984. private void FetchFromStudiosNode(XmlReader reader, T item)
  985. {
  986. reader.MoveToContent();
  987. while (reader.Read())
  988. {
  989. if (reader.NodeType == XmlNodeType.Element)
  990. {
  991. switch (reader.Name)
  992. {
  993. case "Studio":
  994. {
  995. var studio = reader.ReadElementContentAsString();
  996. if (!string.IsNullOrWhiteSpace(studio))
  997. {
  998. item.AddStudio(studio);
  999. }
  1000. break;
  1001. }
  1002. default:
  1003. reader.Skip();
  1004. break;
  1005. }
  1006. }
  1007. }
  1008. }
  1009. /// <summary>
  1010. /// Gets the persons from XML node.
  1011. /// </summary>
  1012. /// <param name="reader">The reader.</param>
  1013. /// <returns>IEnumerable{PersonInfo}.</returns>
  1014. private IEnumerable<Entities.PersonInfo> GetPersonsFromXmlNode(XmlReader reader)
  1015. {
  1016. var name = string.Empty;
  1017. var type = "Actor"; // If type is not specified assume actor
  1018. var role = string.Empty;
  1019. int? sortOrder = null;
  1020. reader.MoveToContent();
  1021. while (reader.Read())
  1022. {
  1023. if (reader.NodeType == XmlNodeType.Element)
  1024. {
  1025. switch (reader.Name)
  1026. {
  1027. case "Name":
  1028. name = reader.ReadElementContentAsString() ?? string.Empty;
  1029. break;
  1030. case "Type":
  1031. {
  1032. var val = reader.ReadElementContentAsString();
  1033. if (!string.IsNullOrWhiteSpace(val))
  1034. {
  1035. type = val;
  1036. }
  1037. break;
  1038. }
  1039. case "Role":
  1040. {
  1041. var val = reader.ReadElementContentAsString();
  1042. if (!string.IsNullOrWhiteSpace(val))
  1043. {
  1044. role = val;
  1045. }
  1046. break;
  1047. }
  1048. case "SortOrder":
  1049. {
  1050. var val = reader.ReadElementContentAsString();
  1051. if (!string.IsNullOrWhiteSpace(val))
  1052. {
  1053. int intVal;
  1054. if (int.TryParse(val, NumberStyles.Integer, _usCulture, out intVal))
  1055. {
  1056. sortOrder = intVal;
  1057. }
  1058. }
  1059. break;
  1060. }
  1061. default:
  1062. reader.Skip();
  1063. break;
  1064. }
  1065. }
  1066. }
  1067. var personInfo = new Entities.PersonInfo
  1068. {
  1069. Name = name.Trim(),
  1070. Role = role,
  1071. Type = type,
  1072. SortOrder = sortOrder
  1073. };
  1074. return new[] { personInfo };
  1075. }
  1076. /// <summary>
  1077. /// Used to split names of comma or pipe delimeted genres and people
  1078. /// </summary>
  1079. /// <param name="value">The value.</param>
  1080. /// <returns>IEnumerable{System.String}.</returns>
  1081. private IEnumerable<string> SplitNames(string value)
  1082. {
  1083. value = value ?? string.Empty;
  1084. // Only split by comma if there is no pipe in the string
  1085. // We have to be careful to not split names like Matthew, Jr.
  1086. var separator = value.IndexOf('|') == -1 && value.IndexOf(';') == -1 ? new[] { ',' } : new[] { '|', ';' };
  1087. value = value.Trim().Trim(separator);
  1088. return string.IsNullOrWhiteSpace(value) ? new string[] { } : Split(value, separator, StringSplitOptions.RemoveEmptyEntries);
  1089. }
  1090. /// <summary>
  1091. /// Provides an additional overload for string.split
  1092. /// </summary>
  1093. /// <param name="val">The val.</param>
  1094. /// <param name="separators">The separators.</param>
  1095. /// <param name="options">The options.</param>
  1096. /// <returns>System.String[][].</returns>
  1097. private static string[] Split(string val, char[] separators, StringSplitOptions options)
  1098. {
  1099. return val.Split(separators, options);
  1100. }
  1101. }
  1102. }