[PVR][Estuary] Timer settings dialog: Show client name in timer type selection dialog...
[xbmc.git] / xbmc / video / VideoInfoTag.cpp
blob811dd061279270c0a755dfb5dbdd53aef58104a6
1 /*
2 * Copyright (C) 2005-2018 Team Kodi
3 * This file is part of Kodi - https://kodi.tv
5 * SPDX-License-Identifier: GPL-2.0-or-later
6 * See LICENSES/README.md for more information.
7 */
9 #include "VideoInfoTag.h"
11 #include "ServiceBroker.h"
12 #include "TextureDatabase.h"
13 #include "guilib/LocalizeStrings.h"
14 #include "settings/AdvancedSettings.h"
15 #include "settings/SettingsComponent.h"
16 #include "utils/Archive.h"
17 #include "utils/StringUtils.h"
18 #include "utils/Variant.h"
19 #include "utils/XMLUtils.h"
20 #include "utils/log.h"
22 #include <algorithm>
23 #include <sstream>
24 #include <string>
25 #include <vector>
27 void CVideoInfoTag::Reset()
29 m_director.clear();
30 m_writingCredits.clear();
31 m_genre.clear();
32 m_country.clear();
33 m_strTagLine.clear();
34 m_strPlotOutline.clear();
35 m_strPlot.clear();
36 m_strPictureURL.Clear();
37 m_strTitle.clear();
38 m_strShowTitle.clear();
39 m_strOriginalTitle.clear();
40 m_strSortTitle.clear();
41 m_cast.clear();
42 m_set.title.clear();
43 m_set.id = -1;
44 m_set.overview.clear();
45 m_tags.clear();
46 m_strFile.clear();
47 m_strPath.clear();
48 m_strMPAARating.clear();
49 m_strFileNameAndPath.clear();
50 m_premiered.Reset();
51 m_bHasPremiered = false;
52 m_strStatus.clear();
53 m_strProductionCode.clear();
54 m_firstAired.Reset();
55 m_studio.clear();
56 m_strAlbum.clear();
57 m_artist.clear();
58 m_strTrailer.clear();
59 m_iTop250 = 0;
60 m_year = -1;
61 m_iSeason = -1;
62 m_iEpisode = -1;
63 m_iIdUniqueID = -1;
64 m_uniqueIDs.clear();
65 m_strDefaultUniqueID = "unknown";
66 m_iSpecialSortSeason = -1;
67 m_iSpecialSortEpisode = -1;
68 m_strDefaultRating = "default";
69 m_iIdRating = -1;
70 m_ratings.clear();
71 m_iUserRating = 0;
72 m_iDbId = -1;
73 m_iFileId = -1;
74 m_iBookmarkId = -1;
75 m_iTrack = -1;
76 m_fanart.m_xml.clear();
77 m_duration = 0;
78 m_lastPlayed.Reset();
79 m_showLink.clear();
80 m_namedSeasons.clear();
81 m_streamDetails.Reset();
82 m_playCount = PLAYCOUNT_NOT_SET;
83 m_EpBookmark.Reset();
84 m_EpBookmark.type = CBookmark::EPISODE;
85 m_basePath.clear();
86 m_parentPathID = -1;
87 m_resumePoint.Reset();
88 m_resumePoint.type = CBookmark::RESUME;
89 m_iIdShow = -1;
90 m_iIdSeason = -1;
91 m_dateAdded.Reset();
92 m_type.clear();
93 m_relevance = -1;
94 m_parsedDetails = 0;
95 m_coverArt.clear();
98 bool CVideoInfoTag::Save(TiXmlNode *node, const std::string &tag, bool savePathInfo, const TiXmlElement *additionalNode)
100 if (!node) return false;
102 // we start with a <tag> tag
103 TiXmlElement movieElement(tag.c_str());
104 TiXmlNode *movie = node->InsertEndChild(movieElement);
106 if (!movie) return false;
108 XMLUtils::SetString(movie, "title", m_strTitle);
109 if (!m_strOriginalTitle.empty())
110 XMLUtils::SetString(movie, "originaltitle", m_strOriginalTitle);
111 if (!m_strShowTitle.empty())
112 XMLUtils::SetString(movie, "showtitle", m_strShowTitle);
113 if (!m_strSortTitle.empty())
114 XMLUtils::SetString(movie, "sorttitle", m_strSortTitle);
115 if (!m_ratings.empty())
117 TiXmlElement ratings("ratings");
118 for (const auto& it : m_ratings)
120 TiXmlElement rating("rating");
121 rating.SetAttribute("name", it.first.c_str());
122 XMLUtils::SetFloat(&rating, "value", it.second.rating);
123 XMLUtils::SetInt(&rating, "votes", it.second.votes);
124 rating.SetAttribute("max", 10);
125 if (it.first == m_strDefaultRating)
126 rating.SetAttribute("default", "true");
127 ratings.InsertEndChild(rating);
129 movie->InsertEndChild(ratings);
131 XMLUtils::SetInt(movie, "userrating", m_iUserRating);
133 if (m_EpBookmark.timeInSeconds > 0)
135 TiXmlElement epbookmark("episodebookmark");
136 XMLUtils::SetDouble(&epbookmark, "position", m_EpBookmark.timeInSeconds);
137 if (!m_EpBookmark.playerState.empty())
139 TiXmlElement playerstate("playerstate");
140 CXBMCTinyXML doc;
141 doc.Parse(m_EpBookmark.playerState);
142 playerstate.InsertEndChild(*doc.RootElement());
143 epbookmark.InsertEndChild(playerstate);
145 movie->InsertEndChild(epbookmark);
148 XMLUtils::SetInt(movie, "top250", m_iTop250);
149 if (tag == "episodedetails" || tag == "tvshow")
151 XMLUtils::SetInt(movie, "season", m_iSeason);
152 XMLUtils::SetInt(movie, "episode", m_iEpisode);
153 XMLUtils::SetInt(movie, "displayseason",m_iSpecialSortSeason);
154 XMLUtils::SetInt(movie, "displayepisode",m_iSpecialSortEpisode);
156 if (tag == "musicvideo")
158 XMLUtils::SetInt(movie, "track", m_iTrack);
159 XMLUtils::SetString(movie, "album", m_strAlbum);
161 XMLUtils::SetString(movie, "outline", m_strPlotOutline);
162 XMLUtils::SetString(movie, "plot", m_strPlot);
163 XMLUtils::SetString(movie, "tagline", m_strTagLine);
164 XMLUtils::SetInt(movie, "runtime", GetDuration() / 60);
165 if (m_strPictureURL.HasData())
167 CXBMCTinyXML doc;
168 doc.Parse(m_strPictureURL.GetData());
169 const TiXmlNode* thumb = doc.FirstChild("thumb");
170 while (thumb)
172 movie->InsertEndChild(*thumb);
173 thumb = thumb->NextSibling("thumb");
176 if (m_fanart.m_xml.size())
178 CXBMCTinyXML doc;
179 doc.Parse(m_fanart.m_xml);
180 movie->InsertEndChild(*doc.RootElement());
182 XMLUtils::SetString(movie, "mpaa", m_strMPAARating);
183 XMLUtils::SetInt(movie, "playcount", GetPlayCount());
184 XMLUtils::SetDate(movie, "lastplayed", m_lastPlayed);
185 if (savePathInfo)
187 XMLUtils::SetString(movie, "file", m_strFile);
188 XMLUtils::SetString(movie, "path", m_strPath);
189 XMLUtils::SetString(movie, "filenameandpath", m_strFileNameAndPath);
190 XMLUtils::SetString(movie, "basepath", m_basePath);
192 if (!m_strEpisodeGuide.empty())
194 CXBMCTinyXML doc;
195 doc.Parse(m_strEpisodeGuide);
196 if (doc.RootElement())
197 movie->InsertEndChild(*doc.RootElement());
198 else
199 XMLUtils::SetString(movie, "episodeguide", m_strEpisodeGuide);
202 XMLUtils::SetString(movie, "id", GetUniqueID());
203 for (const auto& uniqueid : m_uniqueIDs)
205 TiXmlElement uniqueID("uniqueid");
206 uniqueID.SetAttribute("type", uniqueid.first);
207 if (uniqueid.first == m_strDefaultUniqueID)
208 uniqueID.SetAttribute("default", "true");
209 TiXmlText value(uniqueid.second);
210 uniqueID.InsertEndChild(value);
212 movie->InsertEndChild(uniqueID);
214 XMLUtils::SetStringArray(movie, "genre", m_genre);
215 XMLUtils::SetStringArray(movie, "country", m_country);
216 if (!m_set.title.empty())
218 TiXmlElement set("set");
219 XMLUtils::SetString(&set, "name", m_set.title);
220 if (!m_set.overview.empty())
221 XMLUtils::SetString(&set, "overview", m_set.overview);
222 movie->InsertEndChild(set);
224 XMLUtils::SetStringArray(movie, "tag", m_tags);
225 XMLUtils::SetStringArray(movie, "credits", m_writingCredits);
226 XMLUtils::SetStringArray(movie, "director", m_director);
227 if (HasPremiered())
228 XMLUtils::SetDate(movie, "premiered", m_premiered);
229 if (HasYear())
230 XMLUtils::SetInt(movie, "year", GetYear());
231 XMLUtils::SetString(movie, "status", m_strStatus);
232 XMLUtils::SetString(movie, "code", m_strProductionCode);
233 XMLUtils::SetDate(movie, "aired", m_firstAired);
234 XMLUtils::SetStringArray(movie, "studio", m_studio);
235 XMLUtils::SetString(movie, "trailer", m_strTrailer);
237 if (m_streamDetails.HasItems())
239 // it goes fileinfo/streamdetails/[video|audio|subtitle]
240 TiXmlElement fileinfo("fileinfo");
241 TiXmlElement streamdetails("streamdetails");
242 for (int iStream=1; iStream<=m_streamDetails.GetVideoStreamCount(); iStream++)
244 TiXmlElement stream("video");
245 XMLUtils::SetString(&stream, "codec", m_streamDetails.GetVideoCodec(iStream));
246 XMLUtils::SetFloat(&stream, "aspect", m_streamDetails.GetVideoAspect(iStream));
247 XMLUtils::SetInt(&stream, "width", m_streamDetails.GetVideoWidth(iStream));
248 XMLUtils::SetInt(&stream, "height", m_streamDetails.GetVideoHeight(iStream));
249 XMLUtils::SetInt(&stream, "durationinseconds", m_streamDetails.GetVideoDuration(iStream));
250 XMLUtils::SetString(&stream, "stereomode", m_streamDetails.GetStereoMode(iStream));
251 XMLUtils::SetString(&stream, "hdrtype", m_streamDetails.GetVideoHdrType(iStream));
252 streamdetails.InsertEndChild(stream);
254 for (int iStream=1; iStream<=m_streamDetails.GetAudioStreamCount(); iStream++)
256 TiXmlElement stream("audio");
257 XMLUtils::SetString(&stream, "codec", m_streamDetails.GetAudioCodec(iStream));
258 XMLUtils::SetString(&stream, "language", m_streamDetails.GetAudioLanguage(iStream));
259 XMLUtils::SetInt(&stream, "channels", m_streamDetails.GetAudioChannels(iStream));
260 streamdetails.InsertEndChild(stream);
262 for (int iStream=1; iStream<=m_streamDetails.GetSubtitleStreamCount(); iStream++)
264 TiXmlElement stream("subtitle");
265 XMLUtils::SetString(&stream, "language", m_streamDetails.GetSubtitleLanguage(iStream));
266 streamdetails.InsertEndChild(stream);
268 fileinfo.InsertEndChild(streamdetails);
269 movie->InsertEndChild(fileinfo);
270 } /* if has stream details */
272 // cast
273 for (iCast it = m_cast.begin(); it != m_cast.end(); ++it)
275 // add a <actor> tag
276 TiXmlElement cast("actor");
277 TiXmlNode *node = movie->InsertEndChild(cast);
278 XMLUtils::SetString(node, "name", it->strName);
279 XMLUtils::SetString(node, "role", it->strRole);
280 XMLUtils::SetInt(node, "order", it->order);
281 XMLUtils::SetString(node, "thumb", it->thumbUrl.GetFirstUrlByType().m_url);
283 XMLUtils::SetStringArray(movie, "artist", m_artist);
284 XMLUtils::SetStringArray(movie, "showlink", m_showLink);
286 for (const auto& namedSeason : m_namedSeasons)
288 TiXmlElement season("namedseason");
289 season.SetAttribute("number", namedSeason.first);
290 TiXmlText value(namedSeason.second);
291 season.InsertEndChild(value);
292 movie->InsertEndChild(season);
295 TiXmlElement resume("resume");
296 XMLUtils::SetDouble(&resume, "position", m_resumePoint.timeInSeconds);
297 XMLUtils::SetDouble(&resume, "total", m_resumePoint.totalTimeInSeconds);
298 if (!m_resumePoint.playerState.empty())
300 TiXmlElement playerstate("playerstate");
301 CXBMCTinyXML doc;
302 doc.Parse(m_resumePoint.playerState);
303 playerstate.InsertEndChild(*doc.RootElement());
304 resume.InsertEndChild(playerstate);
306 movie->InsertEndChild(resume);
308 XMLUtils::SetDateTime(movie, "dateadded", m_dateAdded);
310 if (additionalNode)
311 movie->InsertEndChild(*additionalNode);
313 return true;
316 bool CVideoInfoTag::Load(const TiXmlElement *element, bool append, bool prioritise)
318 if (!element)
319 return false;
320 if (!append)
321 Reset();
322 ParseNative(element, prioritise);
323 return true;
326 void CVideoInfoTag::Merge(CVideoInfoTag& other)
328 if (!other.m_director.empty())
329 m_director = other.m_director;
330 if (!other.m_writingCredits.empty())
331 m_writingCredits = other.m_writingCredits;
332 if (!other.m_genre.empty())
333 m_genre = other.m_genre;
334 if (!other.m_country.empty())
335 m_country = other.m_country;
336 if (!other.m_strTagLine.empty())
337 m_strTagLine = other.m_strTagLine;
338 if (!other.m_strPlotOutline.empty())
339 m_strPlotOutline = other.m_strPlotOutline;
340 if (!other.m_strPlot.empty())
341 m_strPlot = other.m_strPlot;
342 if (other.m_strPictureURL.HasData())
343 m_strPictureURL = other.m_strPictureURL;
344 if (!other.m_strTitle.empty())
345 m_strTitle = other.m_strTitle;
346 if (!other.m_strShowTitle.empty())
347 m_strShowTitle = other.m_strShowTitle;
348 if (!other.m_strOriginalTitle.empty())
349 m_strOriginalTitle = other.m_strOriginalTitle;
350 if (!other.m_strSortTitle.empty())
351 m_strSortTitle = other.m_strSortTitle;
352 if (other.m_cast.size())
353 m_cast = other.m_cast;
355 if (!other.m_set.title.empty())
356 m_set.title = other.m_set.title;
357 if (other.m_set.id)
358 m_set.id = other.m_set.id;
359 if (!other.m_set.overview.empty())
360 m_set.overview = other.m_set.overview;
361 if (!other.m_tags.empty())
362 m_tags = other.m_tags;
364 if (!other.m_strFile.empty())
365 m_strFile = other.m_strFile;
366 if (!other.m_strPath.empty())
367 m_strPath = other.m_strPath;
369 if (!other.m_strMPAARating.empty())
370 m_strMPAARating = other.m_strMPAARating;
371 if (!other.m_strFileNameAndPath.empty())
372 m_strFileNameAndPath = other.m_strFileNameAndPath;
374 if (other.m_premiered.IsValid())
375 SetPremiered(other.GetPremiered());
377 if (!other.m_strStatus.empty())
378 m_strStatus = other.m_strStatus;
379 if (!other.m_strProductionCode.empty())
380 m_strProductionCode = other.m_strProductionCode;
382 if (other.m_firstAired.IsValid())
383 m_firstAired = other.m_firstAired;
384 if (!other.m_studio.empty())
385 m_studio = other.m_studio;
386 if (!other.m_strAlbum.empty())
387 m_strAlbum = other.m_strAlbum;
388 if (!other.m_artist.empty())
389 m_artist = other.m_artist;
390 if (!other.m_strTrailer.empty())
391 m_strTrailer = other.m_strTrailer;
392 if (other.m_iTop250)
393 m_iTop250 = other.m_iTop250;
394 if (other.m_iSeason != -1)
395 m_iSeason = other.m_iSeason;
396 if (other.m_iEpisode != -1)
397 m_iEpisode = other.m_iEpisode;
399 if (other.m_iIdUniqueID != -1)
400 m_iIdUniqueID = other.m_iIdUniqueID;
401 if (other.m_uniqueIDs.size())
403 m_uniqueIDs = other.m_uniqueIDs;
404 m_strDefaultUniqueID = other.m_strDefaultUniqueID;
406 if (other.m_iSpecialSortSeason != -1)
407 m_iSpecialSortSeason = other.m_iSpecialSortSeason;
408 if (other.m_iSpecialSortEpisode != -1)
409 m_iSpecialSortEpisode = other.m_iSpecialSortEpisode;
411 if (!other.m_ratings.empty())
413 m_ratings = other.m_ratings;
414 m_strDefaultRating = other.m_strDefaultRating;
416 if (other.m_iIdRating != -1)
417 m_iIdRating = other.m_iIdRating;
418 if (other.m_iUserRating)
419 m_iUserRating = other.m_iUserRating;
421 if (other.m_iDbId != -1)
422 m_iDbId = other.m_iDbId;
423 if (other.m_iFileId != -1)
424 m_iFileId = other.m_iFileId;
425 if (other.m_iBookmarkId != -1)
426 m_iBookmarkId = other.m_iBookmarkId;
427 if (other.m_iTrack != -1)
428 m_iTrack = other.m_iTrack;
430 if (other.m_fanart.GetNumFanarts())
431 m_fanart = other.m_fanart;
433 if (other.m_duration)
434 m_duration = other.m_duration;
435 if (other.m_lastPlayed.IsValid())
436 m_lastPlayed = other.m_lastPlayed;
438 if (!other.m_showLink.empty())
439 m_showLink = other.m_showLink;
440 if (other.m_namedSeasons.size())
441 m_namedSeasons = other.m_namedSeasons;
442 if (other.m_streamDetails.HasItems())
443 m_streamDetails = other.m_streamDetails;
444 if (other.IsPlayCountSet())
445 SetPlayCount(other.GetPlayCount());
447 if (other.m_EpBookmark.IsSet())
448 m_EpBookmark = other.m_EpBookmark;
450 if (!other.m_basePath.empty())
451 m_basePath = other.m_basePath;
452 if (other.m_parentPathID != -1)
453 m_parentPathID = other.m_parentPathID;
454 if (other.GetResumePoint().IsSet())
455 SetResumePoint(other.GetResumePoint());
456 if (other.m_iIdShow != -1)
457 m_iIdShow = other.m_iIdShow;
458 if (other.m_iIdSeason != -1)
459 m_iIdSeason = other.m_iIdSeason;
461 if (other.m_dateAdded.IsValid())
462 m_dateAdded = other.m_dateAdded;
464 if (!other.m_type.empty())
465 m_type = other.m_type;
467 if (other.m_relevance != -1)
468 m_relevance = other.m_relevance;
469 if (other.m_parsedDetails)
470 m_parsedDetails = other.m_parsedDetails;
471 if (other.m_coverArt.size())
472 m_coverArt = other.m_coverArt;
473 if (other.m_year != -1)
474 m_year = other.m_year;
477 void CVideoInfoTag::Archive(CArchive& ar)
479 if (ar.IsStoring())
481 ar << m_director;
482 ar << m_writingCredits;
483 ar << m_genre;
484 ar << m_country;
485 ar << m_strTagLine;
486 ar << m_strPlotOutline;
487 ar << m_strPlot;
488 ar << m_strPictureURL.GetData();
489 ar << m_fanart.m_xml;
490 ar << m_strTitle;
491 ar << m_strSortTitle;
492 ar << m_studio;
493 ar << m_strTrailer;
494 ar << (int)m_cast.size();
495 for (unsigned int i=0;i<m_cast.size();++i)
497 ar << m_cast[i].strName;
498 ar << m_cast[i].strRole;
499 ar << m_cast[i].order;
500 ar << m_cast[i].thumb;
501 ar << m_cast[i].thumbUrl.GetData();
504 ar << m_set.title;
505 ar << m_set.id;
506 ar << m_set.overview;
507 ar << m_tags;
508 ar << m_duration;
509 ar << m_strFile;
510 ar << m_strPath;
511 ar << m_strMPAARating;
512 ar << m_strFileNameAndPath;
513 ar << m_strOriginalTitle;
514 ar << m_strEpisodeGuide;
515 ar << m_premiered;
516 ar << m_bHasPremiered;
517 ar << m_strStatus;
518 ar << m_strProductionCode;
519 ar << m_firstAired;
520 ar << m_strShowTitle;
521 ar << m_strAlbum;
522 ar << m_artist;
523 ar << GetPlayCount();
524 ar << m_lastPlayed;
525 ar << m_iTop250;
526 ar << m_iSeason;
527 ar << m_iEpisode;
528 ar << (int)m_uniqueIDs.size();
529 for (const auto& i : m_uniqueIDs)
531 ar << i.first;
532 ar << (i.first == m_strDefaultUniqueID);
533 ar << i.second;
535 ar << (int)m_ratings.size();
536 for (const auto& i : m_ratings)
538 ar << i.first;
539 ar << (i.first == m_strDefaultRating);
540 ar << i.second.rating;
541 ar << i.second.votes;
543 ar << m_iUserRating;
544 ar << m_iDbId;
545 ar << m_iFileId;
546 ar << m_iSpecialSortSeason;
547 ar << m_iSpecialSortEpisode;
548 ar << m_iBookmarkId;
549 ar << m_iTrack;
550 ar << dynamic_cast<IArchivable&>(m_streamDetails);
551 ar << m_showLink;
552 ar << static_cast<int>(m_namedSeasons.size());
553 for (const auto& namedSeason : m_namedSeasons)
555 ar << namedSeason.first;
556 ar << namedSeason.second;
558 ar << m_EpBookmark.playerState;
559 ar << m_EpBookmark.timeInSeconds;
560 ar << m_basePath;
561 ar << m_parentPathID;
562 ar << m_resumePoint.timeInSeconds;
563 ar << m_resumePoint.totalTimeInSeconds;
564 ar << m_resumePoint.playerState;
565 ar << m_iIdShow;
566 ar << m_dateAdded.GetAsDBDateTime();
567 ar << m_type;
568 ar << m_iIdSeason;
569 ar << m_coverArt.size();
570 for (auto& it : m_coverArt)
571 ar << it;
573 else
575 ar >> m_director;
576 ar >> m_writingCredits;
577 ar >> m_genre;
578 ar >> m_country;
579 ar >> m_strTagLine;
580 ar >> m_strPlotOutline;
581 ar >> m_strPlot;
582 std::string data;
583 ar >> data;
584 m_strPictureURL.SetData(data);
585 ar >> m_fanart.m_xml;
586 ar >> m_strTitle;
587 ar >> m_strSortTitle;
588 ar >> m_studio;
589 ar >> m_strTrailer;
590 int iCastSize;
591 ar >> iCastSize;
592 m_cast.reserve(iCastSize);
593 for (int i=0;i<iCastSize;++i)
595 SActorInfo info;
596 ar >> info.strName;
597 ar >> info.strRole;
598 ar >> info.order;
599 ar >> info.thumb;
600 std::string strXml;
601 ar >> strXml;
602 info.thumbUrl.ParseFromData(strXml);
603 m_cast.push_back(info);
606 ar >> m_set.title;
607 ar >> m_set.id;
608 ar >> m_set.overview;
609 ar >> m_tags;
610 ar >> m_duration;
611 ar >> m_strFile;
612 ar >> m_strPath;
613 ar >> m_strMPAARating;
614 ar >> m_strFileNameAndPath;
615 ar >> m_strOriginalTitle;
616 ar >> m_strEpisodeGuide;
617 ar >> m_premiered;
618 ar >> m_bHasPremiered;
619 ar >> m_strStatus;
620 ar >> m_strProductionCode;
621 ar >> m_firstAired;
622 ar >> m_strShowTitle;
623 ar >> m_strAlbum;
624 ar >> m_artist;
625 ar >> m_playCount;
626 ar >> m_lastPlayed;
627 ar >> m_iTop250;
628 ar >> m_iSeason;
629 ar >> m_iEpisode;
630 int iUniqueIDSize;
631 ar >> iUniqueIDSize;
632 for (int i = 0; i < iUniqueIDSize; ++i)
634 std::string value;
635 std::string name;
636 bool defaultUniqueID;
637 ar >> name;
638 ar >> defaultUniqueID;
639 ar >> value;
640 SetUniqueID(value, name);
641 if (defaultUniqueID)
642 m_strDefaultUniqueID = name;
644 int iRatingSize;
645 ar >> iRatingSize;
646 for (int i = 0; i < iRatingSize; ++i)
648 CRating rating;
649 std::string name;
650 bool defaultRating;
651 ar >> name;
652 ar >> defaultRating;
653 ar >> rating.rating;
654 ar >> rating.votes;
655 SetRating(rating, name);
656 if (defaultRating)
657 m_strDefaultRating = name;
659 ar >> m_iUserRating;
660 ar >> m_iDbId;
661 ar >> m_iFileId;
662 ar >> m_iSpecialSortSeason;
663 ar >> m_iSpecialSortEpisode;
664 ar >> m_iBookmarkId;
665 ar >> m_iTrack;
666 ar >> dynamic_cast<IArchivable&>(m_streamDetails);
667 ar >> m_showLink;
669 int namedSeasonSize;
670 ar >> namedSeasonSize;
671 for (int i = 0; i < namedSeasonSize; ++i)
673 int seasonNumber;
674 ar >> seasonNumber;
675 std::string seasonName;
676 ar >> seasonName;
677 m_namedSeasons.insert(std::make_pair(seasonNumber, seasonName));
679 ar >> m_EpBookmark.playerState;
680 ar >> m_EpBookmark.timeInSeconds;
681 ar >> m_basePath;
682 ar >> m_parentPathID;
683 ar >> m_resumePoint.timeInSeconds;
684 ar >> m_resumePoint.totalTimeInSeconds;
685 ar >> m_resumePoint.playerState;
686 ar >> m_iIdShow;
688 std::string dateAdded;
689 ar >> dateAdded;
690 m_dateAdded.SetFromDBDateTime(dateAdded);
691 ar >> m_type;
692 ar >> m_iIdSeason;
693 size_t size;
694 ar >> size;
695 m_coverArt.resize(size);
696 for (size_t i = 0; i < size; ++i)
697 ar >> m_coverArt[i];
701 void CVideoInfoTag::Serialize(CVariant& value) const
703 value["director"] = m_director;
704 value["writer"] = m_writingCredits;
705 value["genre"] = m_genre;
706 value["country"] = m_country;
707 value["tagline"] = m_strTagLine;
708 value["plotoutline"] = m_strPlotOutline;
709 value["plot"] = m_strPlot;
710 value["title"] = m_strTitle;
711 value["votes"] = std::to_string(GetRating().votes);
712 value["studio"] = m_studio;
713 value["trailer"] = m_strTrailer;
714 value["cast"] = CVariant(CVariant::VariantTypeArray);
715 for (unsigned int i = 0; i < m_cast.size(); ++i)
717 CVariant actor;
718 actor["name"] = m_cast[i].strName;
719 actor["role"] = m_cast[i].strRole;
720 actor["order"] = m_cast[i].order;
721 if (!m_cast[i].thumb.empty())
722 actor["thumbnail"] = CTextureUtils::GetWrappedImageURL(m_cast[i].thumb);
723 value["cast"].push_back(actor);
725 value["set"] = m_set.title;
726 value["setid"] = m_set.id;
727 value["setoverview"] = m_set.overview;
728 value["tag"] = m_tags;
729 value["runtime"] = GetDuration();
730 value["file"] = m_strFile;
731 value["path"] = m_strPath;
732 value["imdbnumber"] = GetUniqueID();
733 value["mpaa"] = m_strMPAARating;
734 value["filenameandpath"] = m_strFileNameAndPath;
735 value["originaltitle"] = m_strOriginalTitle;
736 value["sorttitle"] = m_strSortTitle;
737 value["episodeguide"] = m_strEpisodeGuide;
738 value["premiered"] = m_premiered.IsValid() ? m_premiered.GetAsDBDate() : StringUtils::Empty;
739 value["status"] = m_strStatus;
740 value["productioncode"] = m_strProductionCode;
741 value["firstaired"] = m_firstAired.IsValid() ? m_firstAired.GetAsDBDate() : StringUtils::Empty;
742 value["showtitle"] = m_strShowTitle;
743 value["album"] = m_strAlbum;
744 value["artist"] = m_artist;
745 value["playcount"] = GetPlayCount();
746 value["lastplayed"] = m_lastPlayed.IsValid() ? m_lastPlayed.GetAsDBDateTime() : StringUtils::Empty;
747 value["top250"] = m_iTop250;
748 value["year"] = GetYear();
749 value["season"] = m_iSeason;
750 value["episode"] = m_iEpisode;
751 for (const auto& i : m_uniqueIDs)
752 value["uniqueid"][i.first] = i.second;
754 value["rating"] = GetRating().rating;
755 CVariant ratings = CVariant(CVariant::VariantTypeObject);
756 for (const auto& i : m_ratings)
758 CVariant rating;
759 rating["rating"] = i.second.rating;
760 rating["votes"] = i.second.votes;
761 rating["default"] = i.first == m_strDefaultRating;
763 ratings[i.first] = rating;
765 value["ratings"] = ratings;
766 value["userrating"] = m_iUserRating;
767 value["dbid"] = m_iDbId;
768 value["fileid"] = m_iFileId;
769 value["track"] = m_iTrack;
770 value["showlink"] = m_showLink;
771 m_streamDetails.Serialize(value["streamdetails"]);
772 CVariant resume = CVariant(CVariant::VariantTypeObject);
773 resume["position"] = m_resumePoint.timeInSeconds;
774 resume["total"] = m_resumePoint.totalTimeInSeconds;
775 value["resume"] = resume;
776 value["tvshowid"] = m_iIdShow;
777 value["dateadded"] = m_dateAdded.IsValid() ? m_dateAdded.GetAsDBDateTime() : StringUtils::Empty;
778 value["type"] = m_type;
779 value["seasonid"] = m_iIdSeason;
780 value["specialsortseason"] = m_iSpecialSortSeason;
781 value["specialsortepisode"] = m_iSpecialSortEpisode;
784 void CVideoInfoTag::ToSortable(SortItem& sortable, Field field) const
786 switch (field)
788 case FieldDirector: sortable[FieldDirector] = m_director; break;
789 case FieldWriter: sortable[FieldWriter] = m_writingCredits; break;
790 case FieldGenre: sortable[FieldGenre] = m_genre; break;
791 case FieldCountry: sortable[FieldCountry] = m_country; break;
792 case FieldTagline: sortable[FieldTagline] = m_strTagLine; break;
793 case FieldPlotOutline: sortable[FieldPlotOutline] = m_strPlotOutline; break;
794 case FieldPlot: sortable[FieldPlot] = m_strPlot; break;
795 case FieldTitle:
797 // make sure not to overwrite an existing title with an empty one
798 std::string title = m_strTitle;
799 if (!title.empty() || sortable.find(FieldTitle) == sortable.end())
800 sortable[FieldTitle] = title;
801 break;
803 case FieldVotes: sortable[FieldVotes] = GetRating().votes; break;
804 case FieldStudio: sortable[FieldStudio] = m_studio; break;
805 case FieldTrailer: sortable[FieldTrailer] = m_strTrailer; break;
806 case FieldSet: sortable[FieldSet] = m_set.title; break;
807 case FieldTime: sortable[FieldTime] = GetDuration(); break;
808 case FieldFilename: sortable[FieldFilename] = m_strFile; break;
809 case FieldMPAA: sortable[FieldMPAA] = m_strMPAARating; break;
810 case FieldPath:
812 // make sure not to overwrite an existing path with an empty one
813 std::string path = GetPath();
814 if (!path.empty() || sortable.find(FieldPath) == sortable.end())
815 sortable[FieldPath] = path;
816 break;
818 case FieldSortTitle:
820 // seasons with a custom name/title need special handling as they should be sorted by season number
821 if (m_type == MediaTypeSeason && !m_strSortTitle.empty())
822 sortable[FieldSortTitle] = StringUtils::Format(g_localizeStrings.Get(20358), m_iSeason);
823 else
824 sortable[FieldSortTitle] = m_strSortTitle;
825 break;
827 case FieldOriginalTitle:
829 // seasons with a custom name/title need special handling as they should be sorted by season number
830 if (m_type == MediaTypeSeason && !m_strOriginalTitle.empty())
831 sortable[FieldOriginalTitle] =
832 StringUtils::Format(g_localizeStrings.Get(20358).c_str(), m_iSeason);
833 else
834 sortable[FieldOriginalTitle] = m_strOriginalTitle;
835 break;
837 case FieldTvShowStatus: sortable[FieldTvShowStatus] = m_strStatus; break;
838 case FieldProductionCode: sortable[FieldProductionCode] = m_strProductionCode; break;
839 case FieldAirDate: sortable[FieldAirDate] = m_firstAired.IsValid() ? m_firstAired.GetAsDBDate() : (m_premiered.IsValid() ? m_premiered.GetAsDBDate() : StringUtils::Empty); break;
840 case FieldTvShowTitle: sortable[FieldTvShowTitle] = m_strShowTitle; break;
841 case FieldAlbum: sortable[FieldAlbum] = m_strAlbum; break;
842 case FieldArtist: sortable[FieldArtist] = m_artist; break;
843 case FieldPlaycount: sortable[FieldPlaycount] = GetPlayCount(); break;
844 case FieldLastPlayed: sortable[FieldLastPlayed] = m_lastPlayed.IsValid() ? m_lastPlayed.GetAsDBDateTime() : StringUtils::Empty; break;
845 case FieldTop250: sortable[FieldTop250] = m_iTop250; break;
846 case FieldYear: sortable[FieldYear] = GetYear(); break;
847 case FieldSeason: sortable[FieldSeason] = m_iSeason; break;
848 case FieldEpisodeNumber: sortable[FieldEpisodeNumber] = m_iEpisode; break;
849 case FieldNumberOfEpisodes: sortable[FieldNumberOfEpisodes] = m_iEpisode; break;
850 case FieldNumberOfWatchedEpisodes: sortable[FieldNumberOfWatchedEpisodes] = m_iEpisode; break;
851 case FieldEpisodeNumberSpecialSort: sortable[FieldEpisodeNumberSpecialSort] = m_iSpecialSortEpisode; break;
852 case FieldSeasonSpecialSort: sortable[FieldSeasonSpecialSort] = m_iSpecialSortSeason; break;
853 case FieldRating: sortable[FieldRating] = GetRating().rating; break;
854 case FieldUserRating: sortable[FieldUserRating] = m_iUserRating; break;
855 case FieldId: sortable[FieldId] = m_iDbId; break;
856 case FieldTrackNumber: sortable[FieldTrackNumber] = m_iTrack; break;
857 case FieldTag: sortable[FieldTag] = m_tags; break;
859 case FieldVideoResolution: sortable[FieldVideoResolution] = m_streamDetails.GetVideoHeight(); break;
860 case FieldVideoAspectRatio: sortable[FieldVideoAspectRatio] = m_streamDetails.GetVideoAspect(); break;
861 case FieldVideoCodec: sortable[FieldVideoCodec] = m_streamDetails.GetVideoCodec(); break;
862 case FieldStereoMode: sortable[FieldStereoMode] = m_streamDetails.GetStereoMode(); break;
864 case FieldAudioChannels: sortable[FieldAudioChannels] = m_streamDetails.GetAudioChannels(); break;
865 case FieldAudioCodec: sortable[FieldAudioCodec] = m_streamDetails.GetAudioCodec(); break;
866 case FieldAudioLanguage: sortable[FieldAudioLanguage] = m_streamDetails.GetAudioLanguage(); break;
868 case FieldSubtitleLanguage: sortable[FieldSubtitleLanguage] = m_streamDetails.GetSubtitleLanguage(); break;
870 case FieldInProgress: sortable[FieldInProgress] = m_resumePoint.IsPartWay(); break;
871 case FieldDateAdded: sortable[FieldDateAdded] = m_dateAdded.IsValid() ? m_dateAdded.GetAsDBDateTime() : StringUtils::Empty; break;
872 case FieldMediaType: sortable[FieldMediaType] = m_type; break;
873 case FieldRelevance: sortable[FieldRelevance] = m_relevance; break;
874 default: break;
878 const CRating CVideoInfoTag::GetRating(std::string type) const
880 if (type.empty())
881 type = m_strDefaultRating;
883 const auto& rating = m_ratings.find(type);
884 if (rating == m_ratings.end())
885 return CRating();
887 return rating->second;
890 const std::string& CVideoInfoTag::GetDefaultRating() const
892 return m_strDefaultRating;
895 bool CVideoInfoTag::HasYear() const
897 return m_year > 0 || m_firstAired.IsValid() || m_premiered.IsValid();
900 int CVideoInfoTag::GetYear() const
902 if (m_year > 0)
903 return m_year;
904 if (m_firstAired.IsValid())
905 return GetFirstAired().GetYear();
906 if (m_premiered.IsValid())
907 return GetPremiered().GetYear();
908 return 0;
911 bool CVideoInfoTag::HasPremiered() const
913 return m_bHasPremiered;
916 const CDateTime& CVideoInfoTag::GetPremiered() const
918 return m_premiered;
921 const CDateTime& CVideoInfoTag::GetFirstAired() const
923 return m_firstAired;
926 const std::string CVideoInfoTag::GetUniqueID(std::string type) const
928 if (type.empty())
929 type = m_strDefaultUniqueID;
931 const auto& uniqueid = m_uniqueIDs.find(type);
932 if (uniqueid == m_uniqueIDs.end())
933 return "";
935 return uniqueid->second;
938 const std::map<std::string, std::string>& CVideoInfoTag::GetUniqueIDs() const
940 return m_uniqueIDs;
943 const std::string& CVideoInfoTag::GetDefaultUniqueID() const
945 return m_strDefaultUniqueID;
948 bool CVideoInfoTag::HasUniqueID() const
950 return !m_uniqueIDs.empty();
953 const std::string CVideoInfoTag::GetCast(bool bIncludeRole /*= false*/) const
955 std::string strLabel;
956 for (iCast it = m_cast.begin(); it != m_cast.end(); ++it)
958 std::string character;
959 if (it->strRole.empty() || !bIncludeRole)
960 character = StringUtils::Format("{}\n", it->strName);
961 else
962 character =
963 StringUtils::Format("{} {} {}\n", it->strName, g_localizeStrings.Get(20347), it->strRole);
964 strLabel += character;
966 return StringUtils::TrimRight(strLabel, "\n");
969 void CVideoInfoTag::ParseNative(const TiXmlElement* movie, bool prioritise)
971 std::string value;
972 float fValue;
974 if (XMLUtils::GetString(movie, "title", value))
975 SetTitle(value);
977 if (XMLUtils::GetString(movie, "originaltitle", value))
978 SetOriginalTitle(value);
980 if (XMLUtils::GetString(movie, "showtitle", value))
981 SetShowTitle(value);
983 if (XMLUtils::GetString(movie, "sorttitle", value))
984 SetSortTitle(value);
986 const TiXmlElement* node = movie->FirstChildElement("ratings");
987 if (node)
989 for (const TiXmlElement* child = node->FirstChildElement("rating"); child != nullptr; child = child->NextSiblingElement("rating"))
991 CRating r;
992 std::string name;
993 if (child->QueryStringAttribute("name", &name) != TIXML_SUCCESS)
994 name = "default";
995 XMLUtils::GetFloat(child, "value", r.rating);
996 if (XMLUtils::GetString(child, "votes", value))
997 r.votes = StringUtils::ReturnDigits(value);
998 int max_value = 10;
999 if ((child->QueryIntAttribute("max", &max_value) == TIXML_SUCCESS) && max_value >= 1)
1000 r.rating = r.rating / max_value * 10; // Normalise the Movie Rating to between 1 and 10
1001 SetRating(r, name);
1002 bool isDefault = false;
1003 // guard against assert in tinyxml
1004 const char* rAtt = child->Attribute("default", static_cast<int*>(nullptr));
1005 if (rAtt && strlen(rAtt) != 0 &&
1006 (child->QueryBoolAttribute("default", &isDefault) == TIXML_SUCCESS) && isDefault)
1007 m_strDefaultRating = name;
1010 else if (XMLUtils::GetFloat(movie, "rating", fValue))
1012 CRating r(fValue, 0);
1013 if (XMLUtils::GetString(movie, "votes", value))
1014 r.votes = StringUtils::ReturnDigits(value);
1015 int max_value = 10;
1016 const TiXmlElement* rElement = movie->FirstChildElement("rating");
1017 if (rElement && (rElement->QueryIntAttribute("max", &max_value) == TIXML_SUCCESS) && max_value >= 1)
1018 r.rating = r.rating / max_value * 10; // Normalise the Movie Rating to between 1 and 10
1019 SetRating(r, "default");
1020 m_strDefaultRating = "default";
1022 XMLUtils::GetInt(movie, "userrating", m_iUserRating);
1024 const TiXmlElement *epbookmark = movie->FirstChildElement("episodebookmark");
1025 if (epbookmark)
1027 XMLUtils::GetDouble(epbookmark, "position", m_EpBookmark.timeInSeconds);
1028 const TiXmlElement *playerstate = epbookmark->FirstChildElement("playerstate");
1029 if (playerstate)
1031 const TiXmlElement *value = playerstate->FirstChildElement();
1032 if (value)
1033 m_EpBookmark.playerState << *value;
1036 else
1037 XMLUtils::GetDouble(movie, "epbookmark", m_EpBookmark.timeInSeconds);
1039 int max_value = 10;
1040 const TiXmlElement* urElement = movie->FirstChildElement("userrating");
1041 if (urElement && (urElement->QueryIntAttribute("max", &max_value) == TIXML_SUCCESS) && max_value >= 1)
1042 m_iUserRating = m_iUserRating / max_value * 10; // Normalise the user Movie Rating to between 1 and 10
1043 XMLUtils::GetInt(movie, "top250", m_iTop250);
1044 XMLUtils::GetInt(movie, "season", m_iSeason);
1045 XMLUtils::GetInt(movie, "episode", m_iEpisode);
1046 XMLUtils::GetInt(movie, "track", m_iTrack);
1048 XMLUtils::GetInt(movie, "displayseason", m_iSpecialSortSeason);
1049 XMLUtils::GetInt(movie, "displayepisode", m_iSpecialSortEpisode);
1050 int after=0;
1051 XMLUtils::GetInt(movie, "displayafterseason",after);
1052 if (after > 0)
1054 m_iSpecialSortSeason = after;
1055 m_iSpecialSortEpisode = 0x1000; // should be more than any realistic episode number
1058 if (XMLUtils::GetString(movie, "outline", value))
1059 SetPlotOutline(value);
1061 if (XMLUtils::GetString(movie, "plot", value))
1062 SetPlot(value);
1064 if (XMLUtils::GetString(movie, "tagline", value))
1065 SetTagLine(value);
1068 if (XMLUtils::GetString(movie, "runtime", value) && !value.empty())
1069 m_duration = GetDurationFromMinuteString(StringUtils::Trim(value));
1071 if (XMLUtils::GetString(movie, "mpaa", value))
1072 SetMPAARating(value);
1074 XMLUtils::GetInt(movie, "playcount", m_playCount);
1075 XMLUtils::GetDate(movie, "lastplayed", m_lastPlayed);
1077 if (XMLUtils::GetString(movie, "file", value))
1078 SetFile(value);
1080 if (XMLUtils::GetString(movie, "path", value))
1081 SetPath(value);
1083 const TiXmlElement* uniqueid = movie->FirstChildElement("uniqueid");
1084 if (uniqueid == nullptr)
1086 if (XMLUtils::GetString(movie, "id", value))
1087 SetUniqueID(value);
1089 else
1091 for (; uniqueid != nullptr; uniqueid = uniqueid->NextSiblingElement("uniqueid"))
1093 if (uniqueid->FirstChild())
1095 if (uniqueid->QueryStringAttribute("type", &value) == TIXML_SUCCESS)
1096 SetUniqueID(uniqueid->FirstChild()->ValueStr(), value);
1097 else
1098 SetUniqueID(uniqueid->FirstChild()->ValueStr());
1099 bool isDefault;
1100 if (m_strDefaultUniqueID == "unknown" &&
1101 (uniqueid->QueryBoolAttribute("default", &isDefault) == TIXML_SUCCESS) && isDefault)
1103 m_strDefaultUniqueID = value;
1109 if (XMLUtils::GetString(movie, "filenameandpath", value))
1110 SetFileNameAndPath(value);
1112 if (XMLUtils::GetDate(movie, "premiered", m_premiered))
1114 m_bHasPremiered = true;
1116 else
1118 int year;
1119 if (XMLUtils::GetInt(movie, "year", year))
1120 SetYear(year);
1123 if (XMLUtils::GetString(movie, "status", value))
1124 SetStatus(value);
1126 if (XMLUtils::GetString(movie, "code", value))
1127 SetProductionCode(value);
1129 XMLUtils::GetDate(movie, "aired", m_firstAired);
1131 if (XMLUtils::GetString(movie, "album", value))
1132 SetAlbum(value);
1134 if (XMLUtils::GetString(movie, "trailer", value))
1135 SetTrailer(value);
1137 if (XMLUtils::GetString(movie, "basepath", value))
1138 SetBasePath(value);
1140 // make sure the picture URLs have been parsed
1141 m_strPictureURL.Parse();
1142 size_t iThumbCount = m_strPictureURL.GetUrls().size();
1143 std::string xmlAdd = m_strPictureURL.GetData();
1145 const TiXmlElement* thumb = movie->FirstChildElement("thumb");
1146 while (thumb)
1148 m_strPictureURL.ParseAndAppendUrl(thumb);
1149 if (prioritise)
1151 std::string temp;
1152 temp << *thumb;
1153 xmlAdd = temp+xmlAdd;
1155 thumb = thumb->NextSiblingElement("thumb");
1158 // prioritise thumbs from nfos
1159 if (prioritise && iThumbCount && iThumbCount != m_strPictureURL.GetUrls().size())
1161 auto thumbUrls = m_strPictureURL.GetUrls();
1162 rotate(thumbUrls.begin(), thumbUrls.begin() + iThumbCount, thumbUrls.end());
1163 m_strPictureURL.SetUrls(thumbUrls);
1164 m_strPictureURL.SetData(xmlAdd);
1167 const std::string itemSeparator = CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->m_videoItemSeparator;
1169 std::vector<std::string> genres(m_genre);
1170 if (XMLUtils::GetStringArray(movie, "genre", genres, prioritise, itemSeparator))
1171 SetGenre(genres);
1173 std::vector<std::string> country(m_country);
1174 if (XMLUtils::GetStringArray(movie, "country", country, prioritise, itemSeparator))
1175 SetCountry(country);
1177 std::vector<std::string> credits(m_writingCredits);
1178 if (XMLUtils::GetStringArray(movie, "credits", credits, prioritise, itemSeparator))
1179 SetWritingCredits(credits);
1181 std::vector<std::string> director(m_director);
1182 if (XMLUtils::GetStringArray(movie, "director", director, prioritise, itemSeparator))
1183 SetDirector(director);
1185 std::vector<std::string> showLink(m_showLink);
1186 if (XMLUtils::GetStringArray(movie, "showlink", showLink, prioritise, itemSeparator))
1187 SetShowLink(showLink);
1189 const TiXmlElement* namedSeason = movie->FirstChildElement("namedseason");
1190 while (namedSeason != nullptr)
1192 if (namedSeason->FirstChild() != nullptr)
1194 int seasonNumber;
1195 std::string seasonName = namedSeason->FirstChild()->ValueStr();
1196 if (!seasonName.empty() &&
1197 namedSeason->Attribute("number", &seasonNumber) != nullptr)
1198 m_namedSeasons.insert(std::make_pair(seasonNumber, seasonName));
1201 namedSeason = namedSeason->NextSiblingElement("namedseason");
1204 // cast
1205 node = movie->FirstChildElement("actor");
1206 if (node && node->FirstChild() && prioritise)
1207 m_cast.clear();
1208 while (node)
1210 const TiXmlNode *actor = node->FirstChild("name");
1211 if (actor && actor->FirstChild())
1213 SActorInfo info;
1214 info.strName = actor->FirstChild()->Value();
1216 if (XMLUtils::GetString(node, "role", value))
1217 info.strRole = StringUtils::Trim(value);
1219 XMLUtils::GetInt(node, "order", info.order);
1220 const TiXmlElement* thumb = node->FirstChildElement("thumb");
1221 while (thumb)
1223 info.thumbUrl.ParseAndAppendUrl(thumb);
1224 thumb = thumb->NextSiblingElement("thumb");
1226 const char* clear=node->Attribute("clear");
1227 if (clear && StringUtils::CompareNoCase(clear, "true"))
1228 m_cast.clear();
1229 m_cast.push_back(info);
1231 node = node->NextSiblingElement("actor");
1234 // Pre-Jarvis NFO file:
1235 // <set>A set</set>
1236 if (XMLUtils::GetString(movie, "set", value))
1237 SetSet(value);
1238 // Jarvis+:
1239 // <set><name>A set</name><overview>A set with a number of movies...</overview></set>
1240 node = movie->FirstChildElement("set");
1241 if (node)
1243 // No name, no set
1244 if (XMLUtils::GetString(node, "name", value))
1246 SetSet(value);
1247 if (XMLUtils::GetString(node, "overview", value))
1248 SetSetOverview(value);
1252 std::vector<std::string> tags(m_tags);
1253 if (XMLUtils::GetStringArray(movie, "tag", tags, prioritise, itemSeparator))
1254 SetTags(tags);
1256 std::vector<std::string> studio(m_studio);
1257 if (XMLUtils::GetStringArray(movie, "studio", studio, prioritise, itemSeparator))
1258 SetStudio(studio);
1260 // artists
1261 std::vector<std::string> artist(m_artist);
1262 node = movie->FirstChildElement("artist");
1263 if (node && node->FirstChild() && prioritise)
1264 artist.clear();
1265 while (node)
1267 const TiXmlNode* pNode = node->FirstChild("name");
1268 const char* pValue=NULL;
1269 if (pNode && pNode->FirstChild())
1270 pValue = pNode->FirstChild()->Value();
1271 else if (node->FirstChild())
1272 pValue = node->FirstChild()->Value();
1273 if (pValue)
1275 const char* clear=node->Attribute("clear");
1276 if (clear && StringUtils::CompareNoCase(clear, "true") == 0)
1277 artist.clear();
1278 std::vector<std::string> newArtists = StringUtils::Split(pValue, itemSeparator);
1279 artist.insert(artist.end(), newArtists.begin(), newArtists.end());
1281 node = node->NextSiblingElement("artist");
1283 SetArtist(artist);
1285 node = movie->FirstChildElement("fileinfo");
1286 if (node)
1288 // Try to pull from fileinfo/streamdetails/[video|audio|subtitle]
1289 const TiXmlNode *nodeStreamDetails = node->FirstChild("streamdetails");
1290 if (nodeStreamDetails)
1292 const TiXmlNode *nodeDetail = NULL;
1293 while ((nodeDetail = nodeStreamDetails->IterateChildren("audio", nodeDetail)))
1295 CStreamDetailAudio *p = new CStreamDetailAudio();
1296 if (XMLUtils::GetString(nodeDetail, "codec", value))
1297 p->m_strCodec = StringUtils::Trim(value);
1299 if (XMLUtils::GetString(nodeDetail, "language", value))
1300 p->m_strLanguage = StringUtils::Trim(value);
1302 XMLUtils::GetInt(nodeDetail, "channels", p->m_iChannels);
1303 StringUtils::ToLower(p->m_strCodec);
1304 StringUtils::ToLower(p->m_strLanguage);
1305 m_streamDetails.AddStream(p);
1307 nodeDetail = NULL;
1308 while ((nodeDetail = nodeStreamDetails->IterateChildren("video", nodeDetail)))
1310 CStreamDetailVideo *p = new CStreamDetailVideo();
1311 if (XMLUtils::GetString(nodeDetail, "codec", value))
1312 p->m_strCodec = StringUtils::Trim(value);
1314 XMLUtils::GetFloat(nodeDetail, "aspect", p->m_fAspect);
1315 XMLUtils::GetInt(nodeDetail, "width", p->m_iWidth);
1316 XMLUtils::GetInt(nodeDetail, "height", p->m_iHeight);
1317 XMLUtils::GetInt(nodeDetail, "durationinseconds", p->m_iDuration);
1318 if (XMLUtils::GetString(nodeDetail, "stereomode", value))
1319 p->m_strStereoMode = StringUtils::Trim(value);
1320 if (XMLUtils::GetString(nodeDetail, "language", value))
1321 p->m_strLanguage = StringUtils::Trim(value);
1322 if (XMLUtils::GetString(nodeDetail, "hdrtype", value))
1323 p->m_strHdrType = StringUtils::Trim(value);
1325 StringUtils::ToLower(p->m_strCodec);
1326 StringUtils::ToLower(p->m_strStereoMode);
1327 StringUtils::ToLower(p->m_strLanguage);
1328 StringUtils::ToLower(p->m_strHdrType);
1329 m_streamDetails.AddStream(p);
1331 nodeDetail = NULL;
1332 while ((nodeDetail = nodeStreamDetails->IterateChildren("subtitle", nodeDetail)))
1334 CStreamDetailSubtitle *p = new CStreamDetailSubtitle();
1335 if (XMLUtils::GetString(nodeDetail, "language", value))
1336 p->m_strLanguage = StringUtils::Trim(value);
1337 StringUtils::ToLower(p->m_strLanguage);
1338 m_streamDetails.AddStream(p);
1341 m_streamDetails.DetermineBestStreams();
1342 } /* if fileinfo */
1344 if (m_strEpisodeGuide.empty())
1346 const TiXmlElement* epguide = movie->FirstChildElement("episodeguide");
1347 if (epguide)
1349 // DEPRECIATE ME - support for old XML-encoded <episodeguide> blocks.
1350 if (epguide->FirstChild() &&
1351 StringUtils::CompareNoCase("<episodeguide", epguide->FirstChild()->Value(), 13) == 0)
1353 m_strEpisodeGuide = epguide->FirstChild()->Value();
1355 else
1357 std::stringstream stream;
1358 stream << *epguide;
1359 m_strEpisodeGuide = stream.str();
1364 // fanart
1365 const TiXmlElement *fanart = movie->FirstChildElement("fanart");
1366 if (fanart)
1368 // we prioritise mixed-mode nfo's with fanart set
1369 if (prioritise)
1371 std::string temp;
1372 temp << *fanart;
1373 m_fanart.m_xml = temp+m_fanart.m_xml;
1375 else
1376 m_fanart.m_xml << *fanart;
1377 m_fanart.Unpack();
1380 // resumePoint
1381 const TiXmlNode *resume = movie->FirstChild("resume");
1382 if (resume)
1384 XMLUtils::GetDouble(resume, "position", m_resumePoint.timeInSeconds);
1385 XMLUtils::GetDouble(resume, "total", m_resumePoint.totalTimeInSeconds);
1386 const TiXmlElement *playerstate = resume->FirstChildElement("playerstate");
1387 if (playerstate)
1389 const TiXmlElement *value = playerstate->FirstChildElement();
1390 if (value)
1391 m_resumePoint.playerState << *value;
1395 XMLUtils::GetDateTime(movie, "dateadded", m_dateAdded);
1398 bool CVideoInfoTag::HasStreamDetails() const
1400 return m_streamDetails.HasItems();
1403 bool CVideoInfoTag::IsEmpty() const
1405 return (m_strTitle.empty() &&
1406 m_strFile.empty() &&
1407 m_strPath.empty());
1410 void CVideoInfoTag::SetDuration(int duration)
1412 m_duration = duration;
1415 unsigned int CVideoInfoTag::GetDuration() const
1418 Prefer the duration from the stream if it isn't too
1419 small (60%) compared to the duration from the tag.
1421 unsigned int duration = m_streamDetails.GetVideoDuration();
1422 if (duration > m_duration * 0.6)
1423 return duration;
1425 return m_duration;
1428 unsigned int CVideoInfoTag::GetStaticDuration() const
1430 return m_duration;
1433 unsigned int CVideoInfoTag::GetDurationFromMinuteString(const std::string &runtime)
1435 unsigned int duration = (unsigned int)str2uint64(runtime);
1436 if (!duration)
1437 { // failed for some reason, or zero
1438 duration = strtoul(runtime.c_str(), NULL, 10);
1439 CLog::Log(LOGWARNING, "{} <runtime> should be in minutes. Interpreting '{}' as {} minutes",
1440 __FUNCTION__, runtime, duration);
1442 return duration*60;
1445 void CVideoInfoTag::SetBasePath(std::string basePath)
1447 m_basePath = Trim(std::move(basePath));
1450 void CVideoInfoTag::SetDirector(std::vector<std::string> director)
1452 m_director = Trim(std::move(director));
1455 void CVideoInfoTag::SetWritingCredits(std::vector<std::string> writingCredits)
1457 m_writingCredits = Trim(std::move(writingCredits));
1460 void CVideoInfoTag::SetGenre(std::vector<std::string> genre)
1462 m_genre = Trim(std::move(genre));
1465 void CVideoInfoTag::SetCountry(std::vector<std::string> country)
1467 m_country = Trim(std::move(country));
1470 void CVideoInfoTag::SetTagLine(std::string tagLine)
1472 m_strTagLine = Trim(std::move(tagLine));
1475 void CVideoInfoTag::SetPlotOutline(std::string plotOutline)
1477 m_strPlotOutline = Trim(std::move(plotOutline));
1480 void CVideoInfoTag::SetTrailer(std::string trailer)
1482 m_strTrailer = Trim(std::move(trailer));
1485 void CVideoInfoTag::SetPlot(std::string plot)
1487 m_strPlot = Trim(std::move(plot));
1490 void CVideoInfoTag::SetTitle(std::string title)
1492 m_strTitle = Trim(std::move(title));
1495 std::string const &CVideoInfoTag::GetTitle()
1497 return m_strTitle;
1500 void CVideoInfoTag::SetSortTitle(std::string sortTitle)
1502 m_strSortTitle = Trim(std::move(sortTitle));
1505 void CVideoInfoTag::SetPictureURL(CScraperUrl &pictureURL)
1507 m_strPictureURL = pictureURL;
1510 void CVideoInfoTag::SetRating(float rating, int votes, const std::string& type /* = "" */, bool def /* = false */)
1512 SetRating(CRating(rating, votes), type, def);
1515 void CVideoInfoTag::SetRating(CRating rating, const std::string& type /* = "" */, bool def /* = false */)
1517 if (rating.rating <= 0 || rating.rating > 10)
1518 return;
1520 if (type.empty())
1521 m_ratings[m_strDefaultRating] = rating;
1522 else
1524 if (def || m_ratings.empty())
1525 m_strDefaultRating = type;
1526 m_ratings[type] = rating;
1530 void CVideoInfoTag::SetRating(float rating, const std::string& type /* = "" */, bool def /* = false */)
1532 if (rating <= 0 || rating > 10)
1533 return;
1535 if (type.empty())
1536 m_ratings[m_strDefaultRating].rating = rating;
1537 else
1539 if (def || m_ratings.empty())
1540 m_strDefaultRating = type;
1541 m_ratings[type].rating = rating;
1545 void CVideoInfoTag::RemoveRating(const std::string& type)
1547 if (m_ratings.find(type) != m_ratings.end())
1549 m_ratings.erase(type);
1550 if (m_strDefaultRating == type && !m_ratings.empty())
1551 m_strDefaultRating = m_ratings.begin()->first;
1555 void CVideoInfoTag::SetRatings(RatingMap ratings, const std::string& defaultRating /* = "" */)
1557 m_ratings = std::move(ratings);
1559 if (!defaultRating.empty() && m_ratings.find(defaultRating) != m_ratings.end())
1560 m_strDefaultRating = defaultRating;
1563 void CVideoInfoTag::SetVotes(int votes, const std::string& type /* = "" */)
1565 if (type.empty())
1566 m_ratings[m_strDefaultRating].votes = votes;
1567 else
1568 m_ratings[type].votes = votes;
1571 void CVideoInfoTag::SetPremiered(const CDateTime& premiered)
1573 m_premiered = premiered;
1574 m_bHasPremiered = premiered.IsValid();
1577 void CVideoInfoTag::SetPremieredFromDBDate(const std::string& premieredString)
1579 CDateTime premiered;
1580 premiered.SetFromDBDate(premieredString);
1581 SetPremiered(premiered);
1584 void CVideoInfoTag::SetYear(int year)
1586 if (year <= 0)
1587 return;
1589 m_year = year;
1592 void CVideoInfoTag::SetArtist(std::vector<std::string> artist)
1594 m_artist = Trim(std::move(artist));
1597 void CVideoInfoTag::SetUniqueIDs(std::map<std::string, std::string> uniqueIDs)
1599 for (const auto& uniqueid : uniqueIDs)
1601 if (uniqueid.first.empty())
1602 uniqueIDs.erase(uniqueid.first);
1604 if (uniqueIDs.find(m_strDefaultUniqueID) == uniqueIDs.end())
1606 const auto defaultUniqueId = GetUniqueID();
1607 if (!defaultUniqueId.empty())
1608 uniqueIDs[m_strDefaultUniqueID] = defaultUniqueId;
1610 m_uniqueIDs = std::move(uniqueIDs);
1613 void CVideoInfoTag::SetSet(std::string set)
1615 m_set.title = Trim(std::move(set));
1618 void CVideoInfoTag::SetSetOverview(std::string setOverview)
1620 m_set.overview = Trim(std::move(setOverview));
1623 void CVideoInfoTag::SetTags(std::vector<std::string> tags)
1625 m_tags = Trim(std::move(tags));
1628 void CVideoInfoTag::SetFile(std::string file)
1630 m_strFile = Trim(std::move(file));
1633 void CVideoInfoTag::SetPath(std::string path)
1635 m_strPath = Trim(std::move(path));
1638 void CVideoInfoTag::SetMPAARating(std::string mpaaRating)
1640 m_strMPAARating = Trim(std::move(mpaaRating));
1643 void CVideoInfoTag::SetFileNameAndPath(std::string fileNameAndPath)
1645 m_strFileNameAndPath = Trim(std::move(fileNameAndPath));
1648 void CVideoInfoTag::SetOriginalTitle(std::string originalTitle)
1650 m_strOriginalTitle = Trim(std::move(originalTitle));
1653 void CVideoInfoTag::SetEpisodeGuide(std::string episodeGuide)
1655 if (StringUtils::StartsWith(episodeGuide, "<episodeguide"))
1656 m_strEpisodeGuide = Trim(std::move(episodeGuide));
1657 else
1658 m_strEpisodeGuide =
1659 StringUtils::Format("<episodeguide>{}</episodeguide>", Trim(std::move(episodeGuide)));
1662 void CVideoInfoTag::SetStatus(std::string status)
1664 m_strStatus = Trim(std::move(status));
1667 void CVideoInfoTag::SetProductionCode(std::string productionCode)
1669 m_strProductionCode = Trim(std::move(productionCode));
1672 void CVideoInfoTag::SetShowTitle(std::string showTitle)
1674 m_strShowTitle = Trim(std::move(showTitle));
1677 void CVideoInfoTag::SetStudio(std::vector<std::string> studio)
1679 m_studio = Trim(std::move(studio));
1682 void CVideoInfoTag::SetAlbum(std::string album)
1684 m_strAlbum = Trim(std::move(album));
1687 void CVideoInfoTag::SetShowLink(std::vector<std::string> showLink)
1689 m_showLink = Trim(std::move(showLink));
1692 void CVideoInfoTag::SetUniqueID(const std::string& uniqueid, const std::string& type /* = "" */, bool isDefaultID /* = false */)
1694 if (uniqueid.empty())
1695 return;
1697 if (type.empty())
1698 m_uniqueIDs[m_strDefaultUniqueID] = uniqueid;
1699 else
1701 m_uniqueIDs[type] = uniqueid;
1702 if (isDefaultID)
1703 m_strDefaultUniqueID = type;
1707 void CVideoInfoTag::RemoveUniqueID(const std::string& type)
1709 if (m_uniqueIDs.find(type) != m_uniqueIDs.end())
1710 m_uniqueIDs.erase(type);
1713 void CVideoInfoTag::SetNamedSeasons(std::map<int, std::string> namedSeasons)
1715 m_namedSeasons = std::move(namedSeasons);
1718 void CVideoInfoTag::SetUserrating(int userrating)
1720 //This value needs to be between 0-10 - 0 will unset the userrating
1721 userrating = std::max(userrating, 0);
1722 userrating = std::min(userrating, 10);
1724 m_iUserRating = userrating;
1727 std::string CVideoInfoTag::Trim(std::string &&value)
1729 return StringUtils::Trim(value);
1732 std::vector<std::string> CVideoInfoTag::Trim(std::vector<std::string>&& items)
1734 std::for_each(items.begin(), items.end(), [](std::string &str){
1735 str = StringUtils::Trim(str);
1737 return std::move(items);
1740 int CVideoInfoTag::GetPlayCount() const
1742 return IsPlayCountSet() ? m_playCount : 0;
1745 bool CVideoInfoTag::SetPlayCount(int count)
1747 m_playCount = count;
1748 return true;
1751 bool CVideoInfoTag::IncrementPlayCount()
1753 if (!IsPlayCountSet())
1754 m_playCount = 0;
1756 m_playCount++;
1757 return true;
1760 void CVideoInfoTag::ResetPlayCount()
1762 m_playCount = PLAYCOUNT_NOT_SET;
1765 bool CVideoInfoTag::IsPlayCountSet() const
1767 return m_playCount != PLAYCOUNT_NOT_SET;
1770 CBookmark CVideoInfoTag::GetResumePoint() const
1772 return m_resumePoint;
1775 bool CVideoInfoTag::SetResumePoint(const CBookmark &resumePoint)
1777 m_resumePoint = resumePoint;
1778 return true;
1781 bool CVideoInfoTag::SetResumePoint(double timeInSeconds, double totalTimeInSeconds, const std::string &playerState)
1783 CBookmark resumePoint;
1784 resumePoint.timeInSeconds = timeInSeconds;
1785 resumePoint.totalTimeInSeconds = totalTimeInSeconds;
1786 resumePoint.playerState = playerState;
1787 resumePoint.type = CBookmark::RESUME;
1789 m_resumePoint = resumePoint;
1790 return true;