[videodb] remove unused seasons table from episode_view
[xbmc.git] / xbmc / video / VideoInfoTag.cpp
blob22cc5b923c1e90a438ef5ea1c8fd72bb50cab1ac
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 "guilib/LocalizeStrings.h"
13 #include "imagefiles/ImageFileURL.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"
21 #include "video/VideoManagerTypes.h"
23 #include <algorithm>
24 #include <sstream>
25 #include <string>
26 #include <vector>
28 void CVideoInfoTag::Reset()
30 m_director.clear();
31 m_writingCredits.clear();
32 m_genre.clear();
33 m_country.clear();
34 m_strTagLine.clear();
35 m_strPlotOutline.clear();
36 m_strPlot.clear();
37 m_strPictureURL.Clear();
38 m_strTitle.clear();
39 m_strShowTitle.clear();
40 m_strOriginalTitle.clear();
41 m_strSortTitle.clear();
42 m_cast.clear();
43 m_set.title.clear();
44 m_set.id = -1;
45 m_set.overview.clear();
46 m_tags.clear();
47 m_assetInfo.Clear();
48 m_hasVideoVersions = false;
49 m_hasVideoExtras = false;
50 m_isDefaultVideoVersion = false;
51 m_strFile.clear();
52 m_strPath.clear();
53 m_strMPAARating.clear();
54 m_strFileNameAndPath.clear();
55 m_premiered.Reset();
56 m_bHasPremiered = false;
57 m_strStatus.clear();
58 m_strProductionCode.clear();
59 m_firstAired.Reset();
60 m_studio.clear();
61 m_strAlbum.clear();
62 m_artist.clear();
63 m_strTrailer.clear();
64 m_iTop250 = 0;
65 m_year = -1;
66 m_iSeason = -1;
67 m_iEpisode = -1;
68 m_iIdUniqueID = -1;
69 m_uniqueIDs.clear();
70 m_strDefaultUniqueID = "unknown";
71 m_iSpecialSortSeason = -1;
72 m_iSpecialSortEpisode = -1;
73 m_strDefaultRating = "default";
74 m_iIdRating = -1;
75 m_ratings.clear();
76 m_iUserRating = 0;
77 m_iDbId = -1;
78 m_iFileId = -1;
79 m_iBookmarkId = -1;
80 m_iTrack = -1;
81 m_fanart.m_xml.clear();
82 m_duration = 0;
83 m_lastPlayed.Reset();
84 m_showLink.clear();
85 m_namedSeasons.clear();
86 m_streamDetails.Reset();
87 m_playCount = PLAYCOUNT_NOT_SET;
88 m_EpBookmark.Reset();
89 m_EpBookmark.type = CBookmark::EPISODE;
90 m_basePath.clear();
91 m_parentPathID = -1;
92 m_resumePoint.Reset();
93 m_resumePoint.type = CBookmark::RESUME;
94 m_iIdShow = -1;
95 m_iIdSeason = -1;
96 m_dateAdded.Reset();
97 m_type.clear();
98 m_relevance = -1;
99 m_parsedDetails = 0;
100 m_coverArt.clear();
101 m_updateSetOverview = true;
104 bool CVideoInfoTag::Save(TiXmlNode *node, const std::string &tag, bool savePathInfo, const TiXmlElement *additionalNode)
106 if (!node) return false;
108 // we start with a <tag> tag
109 TiXmlElement movieElement(tag.c_str());
110 TiXmlNode *movie = node->InsertEndChild(movieElement);
112 if (!movie) return false;
114 XMLUtils::SetString(movie, "title", m_strTitle);
115 if (!m_strOriginalTitle.empty())
116 XMLUtils::SetString(movie, "originaltitle", m_strOriginalTitle);
117 if (!m_strShowTitle.empty())
118 XMLUtils::SetString(movie, "showtitle", m_strShowTitle);
119 if (!m_strSortTitle.empty())
120 XMLUtils::SetString(movie, "sorttitle", m_strSortTitle);
121 if (!m_ratings.empty())
123 TiXmlElement ratings("ratings");
124 for (const auto& it : m_ratings)
126 TiXmlElement rating("rating");
127 rating.SetAttribute("name", it.first.c_str());
128 XMLUtils::SetFloat(&rating, "value", it.second.rating);
129 XMLUtils::SetInt(&rating, "votes", it.second.votes);
130 rating.SetAttribute("max", 10);
131 if (it.first == m_strDefaultRating)
132 rating.SetAttribute("default", "true");
133 ratings.InsertEndChild(rating);
135 movie->InsertEndChild(ratings);
137 XMLUtils::SetInt(movie, "userrating", m_iUserRating);
139 if (m_EpBookmark.timeInSeconds > 0)
141 TiXmlElement epbookmark("episodebookmark");
142 XMLUtils::SetDouble(&epbookmark, "position", m_EpBookmark.timeInSeconds);
143 if (!m_EpBookmark.playerState.empty())
145 TiXmlElement playerstate("playerstate");
146 CXBMCTinyXML doc;
147 doc.Parse(m_EpBookmark.playerState);
148 playerstate.InsertEndChild(*doc.RootElement());
149 epbookmark.InsertEndChild(playerstate);
151 movie->InsertEndChild(epbookmark);
154 XMLUtils::SetInt(movie, "top250", m_iTop250);
155 if (tag == "episodedetails" || tag == "tvshow")
157 XMLUtils::SetInt(movie, "season", m_iSeason);
158 XMLUtils::SetInt(movie, "episode", m_iEpisode);
159 XMLUtils::SetInt(movie, "displayseason",m_iSpecialSortSeason);
160 XMLUtils::SetInt(movie, "displayepisode",m_iSpecialSortEpisode);
162 if (tag == "musicvideo")
164 XMLUtils::SetInt(movie, "track", m_iTrack);
165 XMLUtils::SetString(movie, "album", m_strAlbum);
167 XMLUtils::SetString(movie, "outline", m_strPlotOutline);
168 XMLUtils::SetString(movie, "plot", m_strPlot);
169 XMLUtils::SetString(movie, "tagline", m_strTagLine);
170 XMLUtils::SetInt(movie, "runtime", GetDuration() / 60);
171 if (m_strPictureURL.HasData())
173 CXBMCTinyXML doc;
174 doc.Parse(m_strPictureURL.GetData());
175 const TiXmlNode* thumb = doc.FirstChild("thumb");
176 while (thumb)
178 movie->InsertEndChild(*thumb);
179 thumb = thumb->NextSibling("thumb");
182 if (m_fanart.m_xml.size())
184 CXBMCTinyXML doc;
185 doc.Parse(m_fanart.m_xml);
186 movie->InsertEndChild(*doc.RootElement());
188 XMLUtils::SetString(movie, "mpaa", m_strMPAARating);
189 XMLUtils::SetInt(movie, "playcount", GetPlayCount());
190 XMLUtils::SetDate(movie, "lastplayed", m_lastPlayed);
191 if (savePathInfo)
193 XMLUtils::SetString(movie, "file", m_strFile);
194 XMLUtils::SetString(movie, "path", m_strPath);
195 XMLUtils::SetString(movie, "filenameandpath", m_strFileNameAndPath);
196 XMLUtils::SetString(movie, "basepath", m_basePath);
198 if (!m_strEpisodeGuide.empty())
200 CXBMCTinyXML doc;
201 doc.Parse(m_strEpisodeGuide);
202 if (doc.RootElement())
203 movie->InsertEndChild(*doc.RootElement());
204 else
205 XMLUtils::SetString(movie, "episodeguide", m_strEpisodeGuide);
208 XMLUtils::SetString(movie, "id", GetUniqueID());
209 for (const auto& uniqueid : m_uniqueIDs)
211 TiXmlElement uniqueID("uniqueid");
212 uniqueID.SetAttribute("type", uniqueid.first);
213 if (uniqueid.first == m_strDefaultUniqueID)
214 uniqueID.SetAttribute("default", "true");
215 TiXmlText value(uniqueid.second);
216 uniqueID.InsertEndChild(value);
218 movie->InsertEndChild(uniqueID);
220 XMLUtils::SetStringArray(movie, "genre", m_genre);
221 XMLUtils::SetStringArray(movie, "country", m_country);
222 if (!m_set.title.empty())
224 TiXmlElement set("set");
225 XMLUtils::SetString(&set, "name", m_set.title);
226 if (!m_set.overview.empty())
227 XMLUtils::SetString(&set, "overview", m_set.overview);
228 movie->InsertEndChild(set);
230 XMLUtils::SetStringArray(movie, "tag", m_tags);
231 m_assetInfo.Save(movie);
232 XMLUtils::SetBoolean(movie, "hasvideoversions", m_hasVideoVersions);
233 XMLUtils::SetBoolean(movie, "hasvideoextras", m_hasVideoExtras);
234 XMLUtils::SetBoolean(movie, "isdefaultvideoversion", m_isDefaultVideoVersion);
235 XMLUtils::SetStringArray(movie, "credits", m_writingCredits);
236 XMLUtils::SetStringArray(movie, "director", m_director);
237 if (HasPremiered())
238 XMLUtils::SetDate(movie, "premiered", m_premiered);
239 if (HasYear())
240 XMLUtils::SetInt(movie, "year", GetYear());
241 XMLUtils::SetString(movie, "status", m_strStatus);
242 XMLUtils::SetString(movie, "code", m_strProductionCode);
243 XMLUtils::SetDate(movie, "aired", m_firstAired);
244 XMLUtils::SetStringArray(movie, "studio", m_studio);
245 XMLUtils::SetString(movie, "trailer", m_strTrailer);
247 if (m_streamDetails.HasItems())
249 // it goes fileinfo/streamdetails/[video|audio|subtitle]
250 TiXmlElement fileinfo("fileinfo");
251 TiXmlElement streamdetails("streamdetails");
252 for (int iStream=1; iStream<=m_streamDetails.GetVideoStreamCount(); iStream++)
254 TiXmlElement stream("video");
255 XMLUtils::SetString(&stream, "codec", m_streamDetails.GetVideoCodec(iStream));
256 XMLUtils::SetFloat(&stream, "aspect", m_streamDetails.GetVideoAspect(iStream));
257 XMLUtils::SetInt(&stream, "width", m_streamDetails.GetVideoWidth(iStream));
258 XMLUtils::SetInt(&stream, "height", m_streamDetails.GetVideoHeight(iStream));
259 XMLUtils::SetInt(&stream, "durationinseconds", m_streamDetails.GetVideoDuration(iStream));
260 XMLUtils::SetString(&stream, "stereomode", m_streamDetails.GetStereoMode(iStream));
261 XMLUtils::SetString(&stream, "hdrtype", m_streamDetails.GetVideoHdrType(iStream));
262 streamdetails.InsertEndChild(stream);
264 for (int iStream=1; iStream<=m_streamDetails.GetAudioStreamCount(); iStream++)
266 TiXmlElement stream("audio");
267 XMLUtils::SetString(&stream, "codec", m_streamDetails.GetAudioCodec(iStream));
268 XMLUtils::SetString(&stream, "language", m_streamDetails.GetAudioLanguage(iStream));
269 XMLUtils::SetInt(&stream, "channels", m_streamDetails.GetAudioChannels(iStream));
270 streamdetails.InsertEndChild(stream);
272 for (int iStream=1; iStream<=m_streamDetails.GetSubtitleStreamCount(); iStream++)
274 TiXmlElement stream("subtitle");
275 XMLUtils::SetString(&stream, "language", m_streamDetails.GetSubtitleLanguage(iStream));
276 streamdetails.InsertEndChild(stream);
278 fileinfo.InsertEndChild(streamdetails);
279 movie->InsertEndChild(fileinfo);
280 } /* if has stream details */
282 // cast
283 for (iCast it = m_cast.begin(); it != m_cast.end(); ++it)
285 // add a <actor> tag
286 TiXmlElement cast("actor");
287 TiXmlNode *node = movie->InsertEndChild(cast);
288 XMLUtils::SetString(node, "name", it->strName);
289 XMLUtils::SetString(node, "role", it->strRole);
290 XMLUtils::SetInt(node, "order", it->order);
291 XMLUtils::SetString(node, "thumb", it->thumbUrl.GetFirstUrlByType().m_url);
293 XMLUtils::SetStringArray(movie, "artist", m_artist);
294 XMLUtils::SetStringArray(movie, "showlink", m_showLink);
296 for (const auto& namedSeason : m_namedSeasons)
298 TiXmlElement season("namedseason");
299 season.SetAttribute("number", namedSeason.first);
300 TiXmlText value(namedSeason.second);
301 season.InsertEndChild(value);
302 movie->InsertEndChild(season);
305 TiXmlElement resume("resume");
306 XMLUtils::SetDouble(&resume, "position", m_resumePoint.timeInSeconds);
307 XMLUtils::SetDouble(&resume, "total", m_resumePoint.totalTimeInSeconds);
308 if (!m_resumePoint.playerState.empty())
310 TiXmlElement playerstate("playerstate");
311 CXBMCTinyXML doc;
312 doc.Parse(m_resumePoint.playerState);
313 playerstate.InsertEndChild(*doc.RootElement());
314 resume.InsertEndChild(playerstate);
316 movie->InsertEndChild(resume);
318 XMLUtils::SetDateTime(movie, "dateadded", m_dateAdded);
320 if (additionalNode)
321 movie->InsertEndChild(*additionalNode);
323 return true;
326 bool CVideoInfoTag::Load(const TiXmlElement *element, bool append, bool prioritise)
328 if (!element)
329 return false;
330 if (!append)
331 Reset();
332 ParseNative(element, prioritise);
333 return true;
336 void CVideoInfoTag::Merge(CVideoInfoTag& other)
338 if (!other.m_director.empty())
339 m_director = other.m_director;
340 if (!other.m_writingCredits.empty())
341 m_writingCredits = other.m_writingCredits;
342 if (!other.m_genre.empty())
343 m_genre = other.m_genre;
344 if (!other.m_country.empty())
345 m_country = other.m_country;
346 if (!other.m_strTagLine.empty())
347 m_strTagLine = other.m_strTagLine;
348 if (!other.m_strPlotOutline.empty())
349 m_strPlotOutline = other.m_strPlotOutline;
350 if (!other.m_strPlot.empty())
351 m_strPlot = other.m_strPlot;
352 if (other.m_strPictureURL.HasData())
353 m_strPictureURL = other.m_strPictureURL;
354 if (!other.m_strTitle.empty())
355 m_strTitle = other.m_strTitle;
356 if (!other.m_strShowTitle.empty())
357 m_strShowTitle = other.m_strShowTitle;
358 if (!other.m_strOriginalTitle.empty())
359 m_strOriginalTitle = other.m_strOriginalTitle;
360 if (!other.m_strSortTitle.empty())
361 m_strSortTitle = other.m_strSortTitle;
362 if (other.m_cast.size())
363 m_cast = other.m_cast;
365 if (!other.m_set.title.empty())
366 m_set.title = other.m_set.title;
367 if (other.m_set.id)
368 m_set.id = other.m_set.id;
369 if (!other.m_set.overview.empty())
370 m_set.overview = other.m_set.overview;
371 if (!other.m_tags.empty())
372 m_tags = other.m_tags;
374 if (!other.m_strFile.empty())
375 m_strFile = other.m_strFile;
376 if (!other.m_strPath.empty())
377 m_strPath = other.m_strPath;
379 if (!other.m_strMPAARating.empty())
380 m_strMPAARating = other.m_strMPAARating;
381 if (!other.m_strFileNameAndPath.empty())
382 m_strFileNameAndPath = other.m_strFileNameAndPath;
384 if (other.m_premiered.IsValid())
385 SetPremiered(other.GetPremiered());
387 if (!other.m_strStatus.empty())
388 m_strStatus = other.m_strStatus;
389 if (!other.m_strProductionCode.empty())
390 m_strProductionCode = other.m_strProductionCode;
392 if (other.m_firstAired.IsValid())
393 m_firstAired = other.m_firstAired;
394 if (!other.m_studio.empty())
395 m_studio = other.m_studio;
396 if (!other.m_strAlbum.empty())
397 m_strAlbum = other.m_strAlbum;
398 if (!other.m_artist.empty())
399 m_artist = other.m_artist;
400 if (!other.m_strTrailer.empty())
401 m_strTrailer = other.m_strTrailer;
402 if (other.m_iTop250)
403 m_iTop250 = other.m_iTop250;
404 if (other.m_iSeason != -1)
405 m_iSeason = other.m_iSeason;
406 if (other.m_iEpisode != -1)
407 m_iEpisode = other.m_iEpisode;
409 if (other.m_iIdUniqueID != -1)
410 m_iIdUniqueID = other.m_iIdUniqueID;
411 if (other.m_uniqueIDs.size())
413 m_uniqueIDs = other.m_uniqueIDs;
414 m_strDefaultUniqueID = other.m_strDefaultUniqueID;
416 if (other.m_iSpecialSortSeason != -1)
417 m_iSpecialSortSeason = other.m_iSpecialSortSeason;
418 if (other.m_iSpecialSortEpisode != -1)
419 m_iSpecialSortEpisode = other.m_iSpecialSortEpisode;
421 if (!other.m_ratings.empty())
423 m_ratings = other.m_ratings;
424 m_strDefaultRating = other.m_strDefaultRating;
426 if (other.m_iIdRating != -1)
427 m_iIdRating = other.m_iIdRating;
428 if (other.m_iUserRating)
429 m_iUserRating = other.m_iUserRating;
431 if (other.m_iDbId != -1)
432 m_iDbId = other.m_iDbId;
433 if (other.m_iFileId != -1)
434 m_iFileId = other.m_iFileId;
435 if (other.m_iBookmarkId != -1)
436 m_iBookmarkId = other.m_iBookmarkId;
437 if (other.m_iTrack != -1)
438 m_iTrack = other.m_iTrack;
440 if (other.m_fanart.GetNumFanarts())
441 m_fanart = other.m_fanart;
443 if (other.m_duration)
444 m_duration = other.m_duration;
445 if (other.m_lastPlayed.IsValid())
446 m_lastPlayed = other.m_lastPlayed;
448 if (!other.m_showLink.empty())
449 m_showLink = other.m_showLink;
450 if (other.m_namedSeasons.size())
451 m_namedSeasons = other.m_namedSeasons;
452 if (other.m_streamDetails.HasItems())
453 m_streamDetails = other.m_streamDetails;
454 if (other.IsPlayCountSet())
455 SetPlayCount(other.GetPlayCount());
457 if (other.m_EpBookmark.IsSet())
458 m_EpBookmark = other.m_EpBookmark;
460 if (!other.m_basePath.empty())
461 m_basePath = other.m_basePath;
462 if (other.m_parentPathID != -1)
463 m_parentPathID = other.m_parentPathID;
464 if (other.GetResumePoint().IsSet())
465 SetResumePoint(other.GetResumePoint());
466 if (other.m_iIdShow != -1)
467 m_iIdShow = other.m_iIdShow;
468 if (other.m_iIdSeason != -1)
469 m_iIdSeason = other.m_iIdSeason;
471 if (other.m_dateAdded.IsValid())
472 m_dateAdded = other.m_dateAdded;
474 if (!other.m_type.empty())
475 m_type = other.m_type;
477 if (other.m_relevance != -1)
478 m_relevance = other.m_relevance;
479 if (other.m_parsedDetails)
480 m_parsedDetails = other.m_parsedDetails;
481 if (other.m_coverArt.size())
482 m_coverArt = other.m_coverArt;
483 if (other.m_year != -1)
484 m_year = other.m_year;
486 m_assetInfo.Merge(other.GetAssetInfo());
487 m_hasVideoVersions = other.m_hasVideoVersions;
488 m_hasVideoExtras = other.m_hasVideoExtras;
489 m_isDefaultVideoVersion = other.m_isDefaultVideoVersion;
492 void CVideoInfoTag::Archive(CArchive& ar)
494 if (ar.IsStoring())
496 ar << m_director;
497 ar << m_writingCredits;
498 ar << m_genre;
499 ar << m_country;
500 ar << m_strTagLine;
501 ar << m_strPlotOutline;
502 ar << m_strPlot;
503 ar << m_strPictureURL.GetData();
504 ar << m_fanart.m_xml;
505 ar << m_strTitle;
506 ar << m_strSortTitle;
507 ar << m_studio;
508 ar << m_strTrailer;
509 ar << (int)m_cast.size();
510 for (unsigned int i=0;i<m_cast.size();++i)
512 ar << m_cast[i].strName;
513 ar << m_cast[i].strRole;
514 ar << m_cast[i].order;
515 ar << m_cast[i].thumb;
516 ar << m_cast[i].thumbUrl.GetData();
519 ar << m_set.title;
520 ar << m_set.id;
521 ar << m_set.overview;
522 ar << m_tags;
523 m_assetInfo.Archive(ar);
524 ar << m_hasVideoVersions;
525 ar << m_hasVideoExtras;
526 ar << m_isDefaultVideoVersion;
527 ar << m_duration;
528 ar << m_strFile;
529 ar << m_strPath;
530 ar << m_strMPAARating;
531 ar << m_strFileNameAndPath;
532 ar << m_strOriginalTitle;
533 ar << m_strEpisodeGuide;
534 ar << m_premiered;
535 ar << m_bHasPremiered;
536 ar << m_strStatus;
537 ar << m_strProductionCode;
538 ar << m_firstAired;
539 ar << m_strShowTitle;
540 ar << m_strAlbum;
541 ar << m_artist;
542 ar << GetPlayCount();
543 ar << m_lastPlayed;
544 ar << m_iTop250;
545 ar << m_iSeason;
546 ar << m_iEpisode;
547 ar << (int)m_uniqueIDs.size();
548 for (const auto& i : m_uniqueIDs)
550 ar << i.first;
551 ar << (i.first == m_strDefaultUniqueID);
552 ar << i.second;
554 ar << (int)m_ratings.size();
555 for (const auto& i : m_ratings)
557 ar << i.first;
558 ar << (i.first == m_strDefaultRating);
559 ar << i.second.rating;
560 ar << i.second.votes;
562 ar << m_iUserRating;
563 ar << m_iDbId;
564 ar << m_iFileId;
565 ar << m_iSpecialSortSeason;
566 ar << m_iSpecialSortEpisode;
567 ar << m_iBookmarkId;
568 ar << m_iTrack;
569 ar << dynamic_cast<IArchivable&>(m_streamDetails);
570 ar << m_showLink;
571 ar << static_cast<int>(m_namedSeasons.size());
572 for (const auto& namedSeason : m_namedSeasons)
574 ar << namedSeason.first;
575 ar << namedSeason.second;
577 ar << m_EpBookmark.playerState;
578 ar << m_EpBookmark.timeInSeconds;
579 ar << m_basePath;
580 ar << m_parentPathID;
581 ar << m_resumePoint.timeInSeconds;
582 ar << m_resumePoint.totalTimeInSeconds;
583 ar << m_resumePoint.playerState;
584 ar << m_iIdShow;
585 ar << m_dateAdded.GetAsDBDateTime();
586 ar << m_type;
587 ar << m_iIdSeason;
588 ar << m_coverArt.size();
589 for (auto& it : m_coverArt)
590 ar << it;
592 else
594 ar >> m_director;
595 ar >> m_writingCredits;
596 ar >> m_genre;
597 ar >> m_country;
598 ar >> m_strTagLine;
599 ar >> m_strPlotOutline;
600 ar >> m_strPlot;
601 std::string data;
602 ar >> data;
603 m_strPictureURL.SetData(data);
604 ar >> m_fanart.m_xml;
605 ar >> m_strTitle;
606 ar >> m_strSortTitle;
607 ar >> m_studio;
608 ar >> m_strTrailer;
609 int iCastSize;
610 ar >> iCastSize;
611 m_cast.reserve(iCastSize);
612 for (int i=0;i<iCastSize;++i)
614 SActorInfo info;
615 ar >> info.strName;
616 ar >> info.strRole;
617 ar >> info.order;
618 ar >> info.thumb;
619 std::string strXml;
620 ar >> strXml;
621 info.thumbUrl.ParseFromData(strXml);
622 m_cast.push_back(info);
625 ar >> m_set.title;
626 ar >> m_set.id;
627 ar >> m_set.overview;
628 ar >> m_tags;
629 m_assetInfo.Archive(ar);
630 ar >> m_hasVideoVersions;
631 ar >> m_hasVideoExtras;
632 ar >> m_isDefaultVideoVersion;
633 ar >> m_duration;
634 ar >> m_strFile;
635 ar >> m_strPath;
636 ar >> m_strMPAARating;
637 ar >> m_strFileNameAndPath;
638 ar >> m_strOriginalTitle;
639 ar >> m_strEpisodeGuide;
640 ar >> m_premiered;
641 ar >> m_bHasPremiered;
642 ar >> m_strStatus;
643 ar >> m_strProductionCode;
644 ar >> m_firstAired;
645 ar >> m_strShowTitle;
646 ar >> m_strAlbum;
647 ar >> m_artist;
648 ar >> m_playCount;
649 ar >> m_lastPlayed;
650 ar >> m_iTop250;
651 ar >> m_iSeason;
652 ar >> m_iEpisode;
653 int iUniqueIDSize;
654 ar >> iUniqueIDSize;
655 for (int i = 0; i < iUniqueIDSize; ++i)
657 std::string value;
658 std::string name;
659 bool defaultUniqueID;
660 ar >> name;
661 ar >> defaultUniqueID;
662 ar >> value;
663 SetUniqueID(value, name);
664 if (defaultUniqueID)
665 m_strDefaultUniqueID = name;
667 int iRatingSize;
668 ar >> iRatingSize;
669 for (int i = 0; i < iRatingSize; ++i)
671 CRating rating;
672 std::string name;
673 bool defaultRating;
674 ar >> name;
675 ar >> defaultRating;
676 ar >> rating.rating;
677 ar >> rating.votes;
678 SetRating(rating, name);
679 if (defaultRating)
680 m_strDefaultRating = name;
682 ar >> m_iUserRating;
683 ar >> m_iDbId;
684 ar >> m_iFileId;
685 ar >> m_iSpecialSortSeason;
686 ar >> m_iSpecialSortEpisode;
687 ar >> m_iBookmarkId;
688 ar >> m_iTrack;
689 ar >> dynamic_cast<IArchivable&>(m_streamDetails);
690 ar >> m_showLink;
692 int namedSeasonSize;
693 ar >> namedSeasonSize;
694 for (int i = 0; i < namedSeasonSize; ++i)
696 int seasonNumber;
697 ar >> seasonNumber;
698 std::string seasonName;
699 ar >> seasonName;
700 m_namedSeasons.insert(std::make_pair(seasonNumber, seasonName));
702 ar >> m_EpBookmark.playerState;
703 ar >> m_EpBookmark.timeInSeconds;
704 ar >> m_basePath;
705 ar >> m_parentPathID;
706 ar >> m_resumePoint.timeInSeconds;
707 ar >> m_resumePoint.totalTimeInSeconds;
708 ar >> m_resumePoint.playerState;
709 ar >> m_iIdShow;
711 std::string dateAdded;
712 ar >> dateAdded;
713 m_dateAdded.SetFromDBDateTime(dateAdded);
714 ar >> m_type;
715 ar >> m_iIdSeason;
716 size_t size;
717 ar >> size;
718 m_coverArt.resize(size);
719 for (size_t i = 0; i < size; ++i)
720 ar >> m_coverArt[i];
724 void CVideoInfoTag::Serialize(CVariant& value) const
726 value["director"] = m_director;
727 value["writer"] = m_writingCredits;
728 value["genre"] = m_genre;
729 value["country"] = m_country;
730 value["tagline"] = m_strTagLine;
731 value["plotoutline"] = m_strPlotOutline;
732 value["plot"] = m_strPlot;
733 value["title"] = m_strTitle;
734 value["votes"] = std::to_string(GetRating().votes);
735 value["studio"] = m_studio;
736 value["trailer"] = m_strTrailer;
737 value["cast"] = CVariant(CVariant::VariantTypeArray);
738 for (unsigned int i = 0; i < m_cast.size(); ++i)
740 CVariant actor;
741 actor["name"] = m_cast[i].strName;
742 actor["role"] = m_cast[i].strRole;
743 actor["order"] = m_cast[i].order;
744 if (!m_cast[i].thumb.empty())
745 actor["thumbnail"] = IMAGE_FILES::URLFromFile(m_cast[i].thumb);
746 value["cast"].push_back(actor);
748 value["set"] = m_set.title;
749 value["setid"] = m_set.id;
750 value["setoverview"] = m_set.overview;
751 value["tag"] = m_tags;
752 m_assetInfo.Serialize(value);
753 value["hasvideoversions"] = m_hasVideoVersions;
754 value["hasvideoextras"] = m_hasVideoExtras;
755 value["isdefaultvideoversion"] = m_isDefaultVideoVersion;
756 value["runtime"] = GetDuration();
757 value["file"] = m_strFile;
758 value["path"] = m_strPath;
759 value["imdbnumber"] = GetUniqueID();
760 value["mpaa"] = m_strMPAARating;
761 value["filenameandpath"] = m_strFileNameAndPath;
762 value["originaltitle"] = m_strOriginalTitle;
763 value["sorttitle"] = m_strSortTitle;
764 value["episodeguide"] = m_strEpisodeGuide;
765 value["premiered"] = m_premiered.IsValid() ? m_premiered.GetAsDBDate() : StringUtils::Empty;
766 value["status"] = m_strStatus;
767 value["productioncode"] = m_strProductionCode;
768 value["firstaired"] = m_firstAired.IsValid() ? m_firstAired.GetAsDBDate() : StringUtils::Empty;
769 value["showtitle"] = m_strShowTitle;
770 value["album"] = m_strAlbum;
771 value["artist"] = m_artist;
772 value["playcount"] = GetPlayCount();
773 value["lastplayed"] = m_lastPlayed.IsValid() ? m_lastPlayed.GetAsDBDateTime() : StringUtils::Empty;
774 value["top250"] = m_iTop250;
775 value["year"] = GetYear();
776 value["season"] = m_iSeason;
777 value["episode"] = m_iEpisode;
778 for (const auto& i : m_uniqueIDs)
779 value["uniqueid"][i.first] = i.second;
781 value["rating"] = GetRating().rating;
782 CVariant ratings = CVariant(CVariant::VariantTypeObject);
783 for (const auto& i : m_ratings)
785 CVariant rating;
786 rating["rating"] = i.second.rating;
787 rating["votes"] = i.second.votes;
788 rating["default"] = i.first == m_strDefaultRating;
790 ratings[i.first] = rating;
792 value["ratings"] = ratings;
793 value["userrating"] = m_iUserRating;
794 value["dbid"] = m_iDbId;
795 value["fileid"] = m_iFileId;
796 value["track"] = m_iTrack;
797 value["showlink"] = m_showLink;
798 m_streamDetails.Serialize(value["streamdetails"]);
799 CVariant resume = CVariant(CVariant::VariantTypeObject);
800 resume["position"] = m_resumePoint.timeInSeconds;
801 resume["total"] = m_resumePoint.totalTimeInSeconds;
802 value["resume"] = resume;
803 value["tvshowid"] = m_iIdShow;
804 value["dateadded"] = m_dateAdded.IsValid() ? m_dateAdded.GetAsDBDateTime() : StringUtils::Empty;
805 value["type"] = m_type;
806 value["seasonid"] = m_iIdSeason;
807 value["specialsortseason"] = m_iSpecialSortSeason;
808 value["specialsortepisode"] = m_iSpecialSortEpisode;
811 void CVideoInfoTag::ToSortable(SortItem& sortable, Field field) const
813 switch (field)
815 case FieldDirector: sortable[FieldDirector] = m_director; break;
816 case FieldWriter: sortable[FieldWriter] = m_writingCredits; break;
817 case FieldGenre: sortable[FieldGenre] = m_genre; break;
818 case FieldCountry: sortable[FieldCountry] = m_country; break;
819 case FieldTagline: sortable[FieldTagline] = m_strTagLine; break;
820 case FieldPlotOutline: sortable[FieldPlotOutline] = m_strPlotOutline; break;
821 case FieldPlot: sortable[FieldPlot] = m_strPlot; break;
822 case FieldTitle:
824 // make sure not to overwrite an existing title with an empty one
825 std::string title = m_strTitle;
826 if (!title.empty() || sortable.find(FieldTitle) == sortable.end())
827 sortable[FieldTitle] = title;
828 break;
830 case FieldVotes: sortable[FieldVotes] = GetRating().votes; break;
831 case FieldStudio: sortable[FieldStudio] = m_studio; break;
832 case FieldTrailer: sortable[FieldTrailer] = m_strTrailer; break;
833 case FieldSet: sortable[FieldSet] = m_set.title; break;
834 case FieldTime: sortable[FieldTime] = GetDuration(); break;
835 case FieldFilename: sortable[FieldFilename] = m_strFile; break;
836 case FieldMPAA: sortable[FieldMPAA] = m_strMPAARating; break;
837 case FieldPath:
839 // make sure not to overwrite an existing path with an empty one
840 std::string path = GetPath();
841 if (!path.empty() || sortable.find(FieldPath) == sortable.end())
842 sortable[FieldPath] = path;
843 break;
845 case FieldSortTitle:
847 // seasons with a custom name/title need special handling as they should be sorted by season number
848 if (m_type == MediaTypeSeason && !m_strSortTitle.empty())
849 sortable[FieldSortTitle] = StringUtils::Format(g_localizeStrings.Get(20358), m_iSeason);
850 else
851 sortable[FieldSortTitle] = m_strSortTitle;
852 break;
854 case FieldOriginalTitle:
856 // seasons with a custom name/title need special handling as they should be sorted by season number
857 if (m_type == MediaTypeSeason && !m_strOriginalTitle.empty())
858 sortable[FieldOriginalTitle] =
859 StringUtils::Format(g_localizeStrings.Get(20358).c_str(), m_iSeason);
860 else
861 sortable[FieldOriginalTitle] = m_strOriginalTitle;
862 break;
864 case FieldTvShowStatus: sortable[FieldTvShowStatus] = m_strStatus; break;
865 case FieldProductionCode: sortable[FieldProductionCode] = m_strProductionCode; break;
866 case FieldAirDate: sortable[FieldAirDate] = m_firstAired.IsValid() ? m_firstAired.GetAsDBDate() : (m_premiered.IsValid() ? m_premiered.GetAsDBDate() : StringUtils::Empty); break;
867 case FieldTvShowTitle: sortable[FieldTvShowTitle] = m_strShowTitle; break;
868 case FieldAlbum: sortable[FieldAlbum] = m_strAlbum; break;
869 case FieldArtist: sortable[FieldArtist] = m_artist; break;
870 case FieldPlaycount: sortable[FieldPlaycount] = GetPlayCount(); break;
871 case FieldLastPlayed: sortable[FieldLastPlayed] = m_lastPlayed.IsValid() ? m_lastPlayed.GetAsDBDateTime() : StringUtils::Empty; break;
872 case FieldTop250: sortable[FieldTop250] = m_iTop250; break;
873 case FieldYear: sortable[FieldYear] = GetYear(); break;
874 case FieldSeason: sortable[FieldSeason] = m_iSeason; break;
875 case FieldEpisodeNumber: sortable[FieldEpisodeNumber] = m_iEpisode; break;
876 case FieldNumberOfEpisodes: sortable[FieldNumberOfEpisodes] = m_iEpisode; break;
877 case FieldNumberOfWatchedEpisodes: sortable[FieldNumberOfWatchedEpisodes] = m_iEpisode; break;
878 case FieldEpisodeNumberSpecialSort: sortable[FieldEpisodeNumberSpecialSort] = m_iSpecialSortEpisode; break;
879 case FieldSeasonSpecialSort: sortable[FieldSeasonSpecialSort] = m_iSpecialSortSeason; break;
880 case FieldRating: sortable[FieldRating] = GetRating().rating; break;
881 case FieldUserRating: sortable[FieldUserRating] = m_iUserRating; break;
882 case FieldId: sortable[FieldId] = m_iDbId; break;
883 case FieldTrackNumber: sortable[FieldTrackNumber] = m_iTrack; break;
884 case FieldTag: sortable[FieldTag] = m_tags; break;
885 case FieldVideoAssetTitle:
886 sortable[FieldVideoAssetTitle] = m_assetInfo.GetTitle();
887 break;
889 case FieldVideoResolution: sortable[FieldVideoResolution] = m_streamDetails.GetVideoHeight(); break;
890 case FieldVideoAspectRatio: sortable[FieldVideoAspectRatio] = m_streamDetails.GetVideoAspect(); break;
891 case FieldVideoCodec: sortable[FieldVideoCodec] = m_streamDetails.GetVideoCodec(); break;
892 case FieldStereoMode: sortable[FieldStereoMode] = m_streamDetails.GetStereoMode(); break;
894 case FieldAudioChannels: sortable[FieldAudioChannels] = m_streamDetails.GetAudioChannels(); break;
895 case FieldAudioCodec: sortable[FieldAudioCodec] = m_streamDetails.GetAudioCodec(); break;
896 case FieldAudioLanguage: sortable[FieldAudioLanguage] = m_streamDetails.GetAudioLanguage(); break;
898 case FieldSubtitleLanguage: sortable[FieldSubtitleLanguage] = m_streamDetails.GetSubtitleLanguage(); break;
900 case FieldInProgress: sortable[FieldInProgress] = m_resumePoint.IsPartWay(); break;
901 case FieldDateAdded: sortable[FieldDateAdded] = m_dateAdded.IsValid() ? m_dateAdded.GetAsDBDateTime() : StringUtils::Empty; break;
902 case FieldMediaType: sortable[FieldMediaType] = m_type; break;
903 case FieldRelevance: sortable[FieldRelevance] = m_relevance; break;
904 default: break;
908 const CRating CVideoInfoTag::GetRating(std::string type) const
910 if (type.empty())
911 type = m_strDefaultRating;
913 const auto& rating = m_ratings.find(type);
914 if (rating == m_ratings.end())
915 return CRating();
917 return rating->second;
920 const std::string& CVideoInfoTag::GetDefaultRating() const
922 return m_strDefaultRating;
925 bool CVideoInfoTag::HasYear() const
927 return m_year > 0 || m_firstAired.IsValid() || m_premiered.IsValid();
930 int CVideoInfoTag::GetYear() const
932 if (m_year > 0)
933 return m_year;
934 if (m_firstAired.IsValid())
935 return GetFirstAired().GetYear();
936 if (m_premiered.IsValid())
937 return GetPremiered().GetYear();
938 return 0;
941 bool CVideoInfoTag::HasPremiered() const
943 return m_bHasPremiered;
946 const CDateTime& CVideoInfoTag::GetPremiered() const
948 return m_premiered;
951 const CDateTime& CVideoInfoTag::GetFirstAired() const
953 return m_firstAired;
956 const std::string CVideoInfoTag::GetUniqueID(std::string type) const
958 if (type.empty())
959 type = m_strDefaultUniqueID;
961 const auto& uniqueid = m_uniqueIDs.find(type);
962 if (uniqueid == m_uniqueIDs.end())
963 return "";
965 return uniqueid->second;
968 const std::map<std::string, std::string>& CVideoInfoTag::GetUniqueIDs() const
970 return m_uniqueIDs;
973 const std::string& CVideoInfoTag::GetDefaultUniqueID() const
975 return m_strDefaultUniqueID;
978 bool CVideoInfoTag::HasUniqueID() const
980 return !m_uniqueIDs.empty();
983 const std::string CVideoInfoTag::GetCast(bool bIncludeRole /*= false*/) const
985 std::string strLabel;
986 for (iCast it = m_cast.begin(); it != m_cast.end(); ++it)
988 std::string character;
989 if (it->strRole.empty() || !bIncludeRole)
990 character = StringUtils::Format("{}\n", it->strName);
991 else
992 character =
993 StringUtils::Format("{} {} {}\n", it->strName, g_localizeStrings.Get(20347), it->strRole);
994 strLabel += character;
996 return StringUtils::TrimRight(strLabel, "\n");
999 void CVideoInfoTag::ParseNative(const TiXmlElement* movie, bool prioritise)
1001 std::string value;
1002 float fValue;
1004 if (XMLUtils::GetString(movie, "title", value))
1005 SetTitle(value);
1007 if (XMLUtils::GetString(movie, "originaltitle", value))
1008 SetOriginalTitle(value);
1010 if (XMLUtils::GetString(movie, "showtitle", value))
1011 SetShowTitle(value);
1013 if (XMLUtils::GetString(movie, "sorttitle", value))
1014 SetSortTitle(value);
1016 const TiXmlElement* node = movie->FirstChildElement("ratings");
1017 if (node)
1019 for (const TiXmlElement* child = node->FirstChildElement("rating"); child != nullptr; child = child->NextSiblingElement("rating"))
1021 CRating r;
1022 std::string name;
1023 if (child->QueryStringAttribute("name", &name) != TIXML_SUCCESS)
1024 name = "default";
1025 XMLUtils::GetFloat(child, "value", r.rating);
1026 if (XMLUtils::GetString(child, "votes", value))
1027 r.votes = StringUtils::ReturnDigits(value);
1028 int max_value = 10;
1029 if ((child->QueryIntAttribute("max", &max_value) == TIXML_SUCCESS) && max_value >= 1)
1030 r.rating = r.rating / max_value * 10; // Normalise the Movie Rating to between 1 and 10
1031 SetRating(r, name);
1032 bool isDefault = false;
1033 // guard against assert in tinyxml
1034 const char* rAtt = child->Attribute("default", static_cast<int*>(nullptr));
1035 if (rAtt && strlen(rAtt) != 0 &&
1036 (child->QueryBoolAttribute("default", &isDefault) == TIXML_SUCCESS) && isDefault)
1037 m_strDefaultRating = name;
1040 else if (XMLUtils::GetFloat(movie, "rating", fValue))
1042 CRating r(fValue, 0);
1043 if (XMLUtils::GetString(movie, "votes", value))
1044 r.votes = StringUtils::ReturnDigits(value);
1045 int max_value = 10;
1046 const TiXmlElement* rElement = movie->FirstChildElement("rating");
1047 if (rElement && (rElement->QueryIntAttribute("max", &max_value) == TIXML_SUCCESS) && max_value >= 1)
1048 r.rating = r.rating / max_value * 10; // Normalise the Movie Rating to between 1 and 10
1049 SetRating(r, "default");
1050 m_strDefaultRating = "default";
1052 XMLUtils::GetInt(movie, "userrating", m_iUserRating);
1054 const TiXmlElement *epbookmark = movie->FirstChildElement("episodebookmark");
1055 if (epbookmark)
1057 XMLUtils::GetDouble(epbookmark, "position", m_EpBookmark.timeInSeconds);
1058 const TiXmlElement *playerstate = epbookmark->FirstChildElement("playerstate");
1059 if (playerstate)
1061 const TiXmlElement *value = playerstate->FirstChildElement();
1062 if (value)
1063 m_EpBookmark.playerState << *value;
1066 else
1067 XMLUtils::GetDouble(movie, "epbookmark", m_EpBookmark.timeInSeconds);
1069 int max_value = 10;
1070 const TiXmlElement* urElement = movie->FirstChildElement("userrating");
1071 if (urElement && (urElement->QueryIntAttribute("max", &max_value) == TIXML_SUCCESS) && max_value >= 1)
1072 m_iUserRating = m_iUserRating / max_value * 10; // Normalise the user Movie Rating to between 1 and 10
1073 XMLUtils::GetInt(movie, "top250", m_iTop250);
1074 XMLUtils::GetInt(movie, "season", m_iSeason);
1075 XMLUtils::GetInt(movie, "episode", m_iEpisode);
1076 XMLUtils::GetInt(movie, "track", m_iTrack);
1078 XMLUtils::GetInt(movie, "displayseason", m_iSpecialSortSeason);
1079 XMLUtils::GetInt(movie, "displayepisode", m_iSpecialSortEpisode);
1080 int after=0;
1081 XMLUtils::GetInt(movie, "displayafterseason",after);
1082 if (after > 0)
1084 m_iSpecialSortSeason = after;
1085 m_iSpecialSortEpisode = 0x1000; // should be more than any realistic episode number
1088 if (XMLUtils::GetString(movie, "outline", value))
1089 SetPlotOutline(value);
1091 if (XMLUtils::GetString(movie, "plot", value))
1092 SetPlot(value);
1094 if (XMLUtils::GetString(movie, "tagline", value))
1095 SetTagLine(value);
1098 if (XMLUtils::GetString(movie, "runtime", value) && !value.empty())
1099 m_duration = GetDurationFromMinuteString(StringUtils::Trim(value));
1101 if (XMLUtils::GetString(movie, "mpaa", value))
1102 SetMPAARating(value);
1104 XMLUtils::GetInt(movie, "playcount", m_playCount);
1105 XMLUtils::GetDate(movie, "lastplayed", m_lastPlayed);
1107 if (XMLUtils::GetString(movie, "file", value))
1108 SetFile(value);
1110 if (XMLUtils::GetString(movie, "path", value))
1111 SetPath(value);
1113 const TiXmlElement* uniqueid = movie->FirstChildElement("uniqueid");
1114 if (uniqueid == nullptr)
1116 if (XMLUtils::GetString(movie, "id", value))
1117 SetUniqueID(value);
1119 else
1121 for (; uniqueid != nullptr; uniqueid = uniqueid->NextSiblingElement("uniqueid"))
1123 if (uniqueid->FirstChild())
1125 if (uniqueid->QueryStringAttribute("type", &value) == TIXML_SUCCESS)
1126 SetUniqueID(uniqueid->FirstChild()->ValueStr(), value);
1127 else
1128 SetUniqueID(uniqueid->FirstChild()->ValueStr());
1129 bool isDefault;
1130 if (m_strDefaultUniqueID == "unknown" &&
1131 (uniqueid->QueryBoolAttribute("default", &isDefault) == TIXML_SUCCESS) && isDefault)
1133 m_strDefaultUniqueID = value;
1139 if (XMLUtils::GetString(movie, "filenameandpath", value))
1140 SetFileNameAndPath(value);
1142 if (XMLUtils::GetDate(movie, "premiered", m_premiered))
1144 m_bHasPremiered = true;
1146 else
1148 int year;
1149 if (XMLUtils::GetInt(movie, "year", year))
1150 SetYear(year);
1153 if (XMLUtils::GetString(movie, "status", value))
1154 SetStatus(value);
1156 if (XMLUtils::GetString(movie, "code", value))
1157 SetProductionCode(value);
1159 XMLUtils::GetDate(movie, "aired", m_firstAired);
1161 if (XMLUtils::GetString(movie, "album", value))
1162 SetAlbum(value);
1164 if (XMLUtils::GetString(movie, "trailer", value))
1165 SetTrailer(value);
1167 if (XMLUtils::GetString(movie, "basepath", value))
1168 SetBasePath(value);
1170 // make sure the picture URLs have been parsed
1171 m_strPictureURL.Parse();
1172 size_t iThumbCount = m_strPictureURL.GetUrls().size();
1173 std::string xmlAdd = m_strPictureURL.GetData();
1175 const TiXmlElement* thumb = movie->FirstChildElement("thumb");
1176 while (thumb)
1178 m_strPictureURL.ParseAndAppendUrl(thumb);
1179 if (prioritise)
1181 std::string temp;
1182 temp << *thumb;
1183 xmlAdd = temp+xmlAdd;
1185 thumb = thumb->NextSiblingElement("thumb");
1188 // prioritise thumbs from nfos
1189 if (prioritise && iThumbCount && iThumbCount != m_strPictureURL.GetUrls().size())
1191 auto thumbUrls = m_strPictureURL.GetUrls();
1192 rotate(thumbUrls.begin(), thumbUrls.begin() + iThumbCount, thumbUrls.end());
1193 m_strPictureURL.SetUrls(thumbUrls);
1194 m_strPictureURL.SetData(xmlAdd);
1197 const std::string itemSeparator = CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->m_videoItemSeparator;
1199 std::vector<std::string> genres(m_genre);
1200 if (XMLUtils::GetStringArray(movie, "genre", genres, prioritise, itemSeparator))
1201 SetGenre(genres);
1203 std::vector<std::string> country(m_country);
1204 if (XMLUtils::GetStringArray(movie, "country", country, prioritise, itemSeparator))
1205 SetCountry(country);
1207 std::vector<std::string> credits(m_writingCredits);
1208 if (XMLUtils::GetStringArray(movie, "credits", credits, prioritise, itemSeparator))
1209 SetWritingCredits(credits);
1211 std::vector<std::string> director(m_director);
1212 if (XMLUtils::GetStringArray(movie, "director", director, prioritise, itemSeparator))
1213 SetDirector(director);
1215 std::vector<std::string> showLink(m_showLink);
1216 if (XMLUtils::GetStringArray(movie, "showlink", showLink, prioritise, itemSeparator))
1217 SetShowLink(showLink);
1219 const TiXmlElement* namedSeason = movie->FirstChildElement("namedseason");
1220 while (namedSeason != nullptr)
1222 if (namedSeason->FirstChild() != nullptr)
1224 int seasonNumber;
1225 std::string seasonName = namedSeason->FirstChild()->ValueStr();
1226 if (!seasonName.empty() &&
1227 namedSeason->Attribute("number", &seasonNumber) != nullptr)
1228 m_namedSeasons.insert(std::make_pair(seasonNumber, seasonName));
1231 namedSeason = namedSeason->NextSiblingElement("namedseason");
1234 // cast
1235 node = movie->FirstChildElement("actor");
1236 if (node && node->FirstChild() && prioritise)
1237 m_cast.clear();
1238 while (node)
1240 const TiXmlNode *actor = node->FirstChild("name");
1241 if (actor && actor->FirstChild())
1243 SActorInfo info;
1244 info.strName = actor->FirstChild()->Value();
1246 if (XMLUtils::GetString(node, "role", value))
1247 info.strRole = StringUtils::Trim(value);
1249 XMLUtils::GetInt(node, "order", info.order);
1250 const TiXmlElement* thumb = node->FirstChildElement("thumb");
1251 while (thumb)
1253 info.thumbUrl.ParseAndAppendUrl(thumb);
1254 thumb = thumb->NextSiblingElement("thumb");
1256 const char* clear=node->Attribute("clear");
1257 if (clear && StringUtils::CompareNoCase(clear, "true"))
1258 m_cast.clear();
1259 m_cast.push_back(info);
1261 node = node->NextSiblingElement("actor");
1264 // Pre-Jarvis NFO file:
1265 // <set>A set</set>
1266 m_updateSetOverview = false;
1267 if (XMLUtils::GetString(movie, "set", value))
1268 SetSet(value);
1269 // Jarvis+:
1270 // <set><name>A set</name><overview>A set with a number of movies...</overview></set>
1271 node = movie->FirstChildElement("set");
1272 if (node)
1274 // No name, no set
1275 if (XMLUtils::GetString(node, "name", value))
1277 SetSet(value);
1278 if (XMLUtils::GetString(node, "overview", value))
1280 SetSetOverview(value);
1281 m_updateSetOverview = true;
1286 std::vector<std::string> tags(m_tags);
1287 if (XMLUtils::GetStringArray(movie, "tag", tags, prioritise, itemSeparator))
1288 SetTags(tags);
1290 m_assetInfo.ParseNative(movie);
1292 std::vector<std::string> studio(m_studio);
1293 if (XMLUtils::GetStringArray(movie, "studio", studio, prioritise, itemSeparator))
1294 SetStudio(studio);
1296 // artists
1297 std::vector<std::string> artist(m_artist);
1298 node = movie->FirstChildElement("artist");
1299 if (node && node->FirstChild() && prioritise)
1300 artist.clear();
1301 while (node)
1303 const TiXmlNode* pNode = node->FirstChild("name");
1304 const char* pValue=NULL;
1305 if (pNode && pNode->FirstChild())
1306 pValue = pNode->FirstChild()->Value();
1307 else if (node->FirstChild())
1308 pValue = node->FirstChild()->Value();
1309 if (pValue)
1311 const char* clear=node->Attribute("clear");
1312 if (clear && StringUtils::CompareNoCase(clear, "true") == 0)
1313 artist.clear();
1314 std::vector<std::string> newArtists = StringUtils::Split(pValue, itemSeparator);
1315 artist.insert(artist.end(), newArtists.begin(), newArtists.end());
1317 node = node->NextSiblingElement("artist");
1319 SetArtist(artist);
1321 node = movie->FirstChildElement("fileinfo");
1322 if (node)
1324 // Try to pull from fileinfo/streamdetails/[video|audio|subtitle]
1325 const TiXmlNode *nodeStreamDetails = node->FirstChild("streamdetails");
1326 if (nodeStreamDetails)
1328 const TiXmlNode *nodeDetail = NULL;
1329 while ((nodeDetail = nodeStreamDetails->IterateChildren("audio", nodeDetail)))
1331 CStreamDetailAudio *p = new CStreamDetailAudio();
1332 if (XMLUtils::GetString(nodeDetail, "codec", value))
1333 p->m_strCodec = StringUtils::Trim(value);
1335 if (XMLUtils::GetString(nodeDetail, "language", value))
1336 p->m_strLanguage = StringUtils::Trim(value);
1338 XMLUtils::GetInt(nodeDetail, "channels", p->m_iChannels);
1339 StringUtils::ToLower(p->m_strCodec);
1340 StringUtils::ToLower(p->m_strLanguage);
1341 m_streamDetails.AddStream(p);
1343 nodeDetail = NULL;
1344 while ((nodeDetail = nodeStreamDetails->IterateChildren("video", nodeDetail)))
1346 CStreamDetailVideo *p = new CStreamDetailVideo();
1347 if (XMLUtils::GetString(nodeDetail, "codec", value))
1348 p->m_strCodec = StringUtils::Trim(value);
1350 XMLUtils::GetFloat(nodeDetail, "aspect", p->m_fAspect);
1351 XMLUtils::GetInt(nodeDetail, "width", p->m_iWidth);
1352 XMLUtils::GetInt(nodeDetail, "height", p->m_iHeight);
1353 XMLUtils::GetInt(nodeDetail, "durationinseconds", p->m_iDuration);
1354 if (XMLUtils::GetString(nodeDetail, "stereomode", value))
1355 p->m_strStereoMode = StringUtils::Trim(value);
1356 if (XMLUtils::GetString(nodeDetail, "language", value))
1357 p->m_strLanguage = StringUtils::Trim(value);
1358 if (XMLUtils::GetString(nodeDetail, "hdrtype", value))
1359 p->m_strHdrType = StringUtils::Trim(value);
1361 StringUtils::ToLower(p->m_strCodec);
1362 StringUtils::ToLower(p->m_strStereoMode);
1363 StringUtils::ToLower(p->m_strLanguage);
1364 StringUtils::ToLower(p->m_strHdrType);
1365 m_streamDetails.AddStream(p);
1367 nodeDetail = NULL;
1368 while ((nodeDetail = nodeStreamDetails->IterateChildren("subtitle", nodeDetail)))
1370 CStreamDetailSubtitle *p = new CStreamDetailSubtitle();
1371 if (XMLUtils::GetString(nodeDetail, "language", value))
1372 p->m_strLanguage = StringUtils::Trim(value);
1373 StringUtils::ToLower(p->m_strLanguage);
1374 m_streamDetails.AddStream(p);
1377 m_streamDetails.DetermineBestStreams();
1378 } /* if fileinfo */
1380 if (m_strEpisodeGuide.empty())
1382 const TiXmlElement* epguide = movie->FirstChildElement("episodeguide");
1383 if (epguide)
1385 // DEPRECIATE ME - support for old XML-encoded <episodeguide> blocks.
1386 if (epguide->FirstChild() &&
1387 StringUtils::CompareNoCase("<episodeguide", epguide->FirstChild()->Value(), 13) == 0)
1389 m_strEpisodeGuide = epguide->FirstChild()->Value();
1391 else
1393 std::stringstream stream;
1394 stream << *epguide;
1395 m_strEpisodeGuide = stream.str();
1400 // fanart
1401 const TiXmlElement *fanart = movie->FirstChildElement("fanart");
1402 if (fanart)
1404 // we prioritise mixed-mode nfo's with fanart set
1405 if (prioritise)
1407 std::string temp;
1408 temp << *fanart;
1409 m_fanart.m_xml = temp+m_fanart.m_xml;
1411 else
1412 m_fanart.m_xml << *fanart;
1413 m_fanart.Unpack();
1416 // resumePoint
1417 const TiXmlNode *resume = movie->FirstChild("resume");
1418 if (resume)
1420 XMLUtils::GetDouble(resume, "position", m_resumePoint.timeInSeconds);
1421 XMLUtils::GetDouble(resume, "total", m_resumePoint.totalTimeInSeconds);
1422 const TiXmlElement *playerstate = resume->FirstChildElement("playerstate");
1423 if (playerstate)
1425 const TiXmlElement *value = playerstate->FirstChildElement();
1426 if (value)
1427 m_resumePoint.playerState << *value;
1431 XMLUtils::GetDateTime(movie, "dateadded", m_dateAdded);
1434 bool CVideoInfoTag::HasStreamDetails() const
1436 return m_streamDetails.HasItems();
1439 bool CVideoInfoTag::IsEmpty() const
1441 return (m_strTitle.empty() &&
1442 m_strFile.empty() &&
1443 m_strPath.empty());
1446 void CVideoInfoTag::SetDuration(int duration)
1448 m_duration = duration;
1451 unsigned int CVideoInfoTag::GetDuration() const
1454 Prefer the duration from the stream if it isn't too
1455 small (60%) compared to the duration from the tag.
1457 unsigned int duration = m_streamDetails.GetVideoDuration();
1458 if (duration > m_duration * 0.6)
1459 return duration;
1461 return m_duration;
1464 unsigned int CVideoInfoTag::GetStaticDuration() const
1466 return m_duration;
1469 unsigned int CVideoInfoTag::GetDurationFromMinuteString(const std::string &runtime)
1471 unsigned int duration = (unsigned int)str2uint64(runtime);
1472 if (!duration)
1473 { // failed for some reason, or zero
1474 duration = strtoul(runtime.c_str(), NULL, 10);
1475 CLog::Log(LOGWARNING, "{} <runtime> should be in minutes. Interpreting '{}' as {} minutes",
1476 __FUNCTION__, runtime, duration);
1478 return duration*60;
1481 void CVideoInfoTag::SetBasePath(std::string basePath)
1483 m_basePath = Trim(std::move(basePath));
1486 void CVideoInfoTag::SetDirector(std::vector<std::string> director)
1488 m_director = Trim(std::move(director));
1491 void CVideoInfoTag::SetWritingCredits(std::vector<std::string> writingCredits)
1493 m_writingCredits = Trim(std::move(writingCredits));
1496 void CVideoInfoTag::SetGenre(std::vector<std::string> genre)
1498 m_genre = Trim(std::move(genre));
1501 void CVideoInfoTag::SetCountry(std::vector<std::string> country)
1503 m_country = Trim(std::move(country));
1506 void CVideoInfoTag::SetTagLine(std::string tagLine)
1508 m_strTagLine = Trim(std::move(tagLine));
1511 void CVideoInfoTag::SetPlotOutline(std::string plotOutline)
1513 m_strPlotOutline = Trim(std::move(plotOutline));
1516 void CVideoInfoTag::SetTrailer(std::string trailer)
1518 m_strTrailer = Trim(std::move(trailer));
1521 void CVideoInfoTag::SetPlot(std::string plot)
1523 m_strPlot = Trim(std::move(plot));
1526 void CVideoInfoTag::SetTitle(std::string title)
1528 m_strTitle = Trim(std::move(title));
1531 std::string const& CVideoInfoTag::GetTitle() const
1533 return m_strTitle;
1536 void CVideoInfoTag::SetSortTitle(std::string sortTitle)
1538 m_strSortTitle = Trim(std::move(sortTitle));
1541 void CVideoInfoTag::SetPictureURL(CScraperUrl &pictureURL)
1543 m_strPictureURL = pictureURL;
1546 void CVideoInfoTag::SetRating(float rating, int votes, const std::string& type /* = "" */, bool def /* = false */)
1548 SetRating(CRating(rating, votes), type, def);
1551 void CVideoInfoTag::SetRating(CRating rating, const std::string& type /* = "" */, bool def /* = false */)
1553 if (rating.rating <= 0 || rating.rating > 10)
1554 return;
1556 if (type.empty())
1557 m_ratings[m_strDefaultRating] = rating;
1558 else
1560 if (def || m_ratings.empty())
1561 m_strDefaultRating = type;
1562 m_ratings[type] = rating;
1566 void CVideoInfoTag::SetRating(float rating, const std::string& type /* = "" */, bool def /* = false */)
1568 if (rating <= 0 || rating > 10)
1569 return;
1571 if (type.empty())
1572 m_ratings[m_strDefaultRating].rating = rating;
1573 else
1575 if (def || m_ratings.empty())
1576 m_strDefaultRating = type;
1577 m_ratings[type].rating = rating;
1581 void CVideoInfoTag::RemoveRating(const std::string& type)
1583 if (m_ratings.find(type) != m_ratings.end())
1585 m_ratings.erase(type);
1586 if (m_strDefaultRating == type && !m_ratings.empty())
1587 m_strDefaultRating = m_ratings.begin()->first;
1591 void CVideoInfoTag::SetRatings(RatingMap ratings, const std::string& defaultRating /* = "" */)
1593 m_ratings = std::move(ratings);
1595 if (!defaultRating.empty() && m_ratings.find(defaultRating) != m_ratings.end())
1596 m_strDefaultRating = defaultRating;
1599 void CVideoInfoTag::SetVotes(int votes, const std::string& type /* = "" */)
1601 if (type.empty())
1602 m_ratings[m_strDefaultRating].votes = votes;
1603 else
1604 m_ratings[type].votes = votes;
1607 void CVideoInfoTag::SetPremiered(const CDateTime& premiered)
1609 m_premiered = premiered;
1610 m_bHasPremiered = premiered.IsValid();
1613 void CVideoInfoTag::SetPremieredFromDBDate(const std::string& premieredString)
1615 CDateTime premiered;
1616 premiered.SetFromDBDate(premieredString);
1617 SetPremiered(premiered);
1620 void CVideoInfoTag::SetYear(int year)
1622 if (year <= 0)
1623 return;
1625 m_year = year;
1628 void CVideoInfoTag::SetArtist(std::vector<std::string> artist)
1630 m_artist = Trim(std::move(artist));
1633 void CVideoInfoTag::SetUniqueIDs(std::map<std::string, std::string> uniqueIDs)
1635 for (const auto& uniqueid : uniqueIDs)
1637 if (uniqueid.first.empty())
1638 uniqueIDs.erase(uniqueid.first);
1640 if (uniqueIDs.find(m_strDefaultUniqueID) == uniqueIDs.end())
1642 const auto defaultUniqueId = GetUniqueID();
1643 if (!defaultUniqueId.empty())
1644 uniqueIDs[m_strDefaultUniqueID] = defaultUniqueId;
1646 m_uniqueIDs = std::move(uniqueIDs);
1649 void CVideoInfoTag::SetSet(std::string set)
1651 m_set.title = Trim(std::move(set));
1654 void CVideoInfoTag::SetSetOverview(std::string setOverview)
1656 m_set.overview = Trim(std::move(setOverview));
1659 void CVideoInfoTag::SetTags(std::vector<std::string> tags)
1661 m_tags = Trim(std::move(tags));
1664 void CVideoInfoTag::SetFile(std::string file)
1666 m_strFile = Trim(std::move(file));
1669 void CVideoInfoTag::SetPath(std::string path)
1671 m_strPath = Trim(std::move(path));
1674 void CVideoInfoTag::SetMPAARating(std::string mpaaRating)
1676 m_strMPAARating = Trim(std::move(mpaaRating));
1679 void CVideoInfoTag::SetFileNameAndPath(std::string fileNameAndPath)
1681 m_strFileNameAndPath = Trim(std::move(fileNameAndPath));
1684 void CVideoInfoTag::SetOriginalTitle(std::string originalTitle)
1686 m_strOriginalTitle = Trim(std::move(originalTitle));
1689 void CVideoInfoTag::SetEpisodeGuide(std::string episodeGuide)
1691 if (StringUtils::StartsWith(episodeGuide, "<episodeguide"))
1692 m_strEpisodeGuide = Trim(std::move(episodeGuide));
1693 else
1694 m_strEpisodeGuide =
1695 StringUtils::Format("<episodeguide>{}</episodeguide>", Trim(std::move(episodeGuide)));
1698 void CVideoInfoTag::SetStatus(std::string status)
1700 m_strStatus = Trim(std::move(status));
1703 void CVideoInfoTag::SetProductionCode(std::string productionCode)
1705 m_strProductionCode = Trim(std::move(productionCode));
1708 void CVideoInfoTag::SetShowTitle(std::string showTitle)
1710 m_strShowTitle = Trim(std::move(showTitle));
1713 void CVideoInfoTag::SetStudio(std::vector<std::string> studio)
1715 m_studio = Trim(std::move(studio));
1718 void CVideoInfoTag::SetAlbum(std::string album)
1720 m_strAlbum = Trim(std::move(album));
1723 void CVideoInfoTag::SetShowLink(std::vector<std::string> showLink)
1725 m_showLink = Trim(std::move(showLink));
1728 void CVideoInfoTag::SetUniqueID(const std::string& uniqueid, const std::string& type /* = "" */, bool isDefaultID /* = false */)
1730 if (uniqueid.empty())
1731 return;
1733 if (type.empty())
1734 m_uniqueIDs[m_strDefaultUniqueID] = uniqueid;
1735 else
1737 m_uniqueIDs[type] = uniqueid;
1738 if (isDefaultID)
1739 m_strDefaultUniqueID = type;
1743 void CVideoInfoTag::RemoveUniqueID(const std::string& type)
1745 if (m_uniqueIDs.find(type) != m_uniqueIDs.end())
1746 m_uniqueIDs.erase(type);
1749 void CVideoInfoTag::SetNamedSeasons(std::map<int, std::string> namedSeasons)
1751 m_namedSeasons = std::move(namedSeasons);
1754 void CVideoInfoTag::SetUserrating(int userrating)
1756 //This value needs to be between 0-10 - 0 will unset the userrating
1757 userrating = std::max(userrating, 0);
1758 userrating = std::min(userrating, 10);
1760 m_iUserRating = userrating;
1763 std::string CVideoInfoTag::Trim(std::string &&value)
1765 return StringUtils::Trim(value);
1768 std::vector<std::string> CVideoInfoTag::Trim(std::vector<std::string>&& items)
1770 std::for_each(items.begin(), items.end(), [](std::string &str){
1771 str = StringUtils::Trim(str);
1773 return std::move(items);
1776 int CVideoInfoTag::GetPlayCount() const
1778 return IsPlayCountSet() ? m_playCount : 0;
1781 bool CVideoInfoTag::SetPlayCount(int count)
1783 m_playCount = count;
1784 return true;
1787 bool CVideoInfoTag::IncrementPlayCount()
1789 if (!IsPlayCountSet())
1790 m_playCount = 0;
1792 m_playCount++;
1793 return true;
1796 void CVideoInfoTag::ResetPlayCount()
1798 m_playCount = PLAYCOUNT_NOT_SET;
1801 bool CVideoInfoTag::IsPlayCountSet() const
1803 return m_playCount != PLAYCOUNT_NOT_SET;
1806 CBookmark CVideoInfoTag::GetResumePoint() const
1808 return m_resumePoint;
1811 bool CVideoInfoTag::SetResumePoint(const CBookmark &resumePoint)
1813 m_resumePoint = resumePoint;
1814 return true;
1817 bool CVideoInfoTag::SetResumePoint(double timeInSeconds, double totalTimeInSeconds, const std::string &playerState)
1819 CBookmark resumePoint;
1820 resumePoint.timeInSeconds = timeInSeconds;
1821 resumePoint.totalTimeInSeconds = totalTimeInSeconds;
1822 resumePoint.playerState = playerState;
1823 resumePoint.type = CBookmark::RESUME;
1825 m_resumePoint = resumePoint;
1826 return true;
1829 void CVideoInfoTag::CAssetInfo::Clear()
1831 m_id = -1;
1832 m_title.clear();
1833 m_type = VideoAssetType::UNKNOWN;
1836 void CVideoInfoTag::CAssetInfo::Archive(CArchive& ar)
1838 if (ar.IsStoring())
1840 ar << m_title;
1841 ar << m_id;
1842 ar << static_cast<int>(m_type);
1844 else
1846 ar >> m_title;
1847 ar >> m_id;
1848 int assetType{0};
1849 ar >> assetType;
1850 m_type = static_cast<VideoAssetType>(assetType);
1854 void CVideoInfoTag::CAssetInfo::Save(TiXmlNode* movie)
1856 XMLUtils::SetString(movie, "videoassettitle", m_title);
1857 XMLUtils::SetInt(movie, "videoassetid", m_id);
1858 XMLUtils::SetInt(movie, "videoassettype", static_cast<int>(m_type));
1861 void CVideoInfoTag::CAssetInfo::ParseNative(const TiXmlElement* movie)
1863 std::string value;
1864 if (XMLUtils::GetString(movie, "videoassettitle", value))
1865 m_title = value;
1867 XMLUtils::GetInt(movie, "videoassetid", m_id);
1869 int assetType{-1};
1870 XMLUtils::GetInt(movie, "videoassettype", assetType);
1871 m_type = static_cast<VideoAssetType>(assetType);
1874 void CVideoInfoTag::CAssetInfo::Merge(CAssetInfo& other)
1876 if (!other.m_title.empty())
1877 m_title = other.m_title;
1878 if (other.m_id >= 0)
1879 m_id = other.m_id;
1880 if (other.m_type != VideoAssetType::UNKNOWN)
1881 m_type = other.m_type;
1884 void CVideoInfoTag::CAssetInfo::Serialize(CVariant& value) const
1886 value["videoassettitle"] = m_title;
1887 value["videoassetid"] = m_id;
1888 value["videoassettype"] = static_cast<int>(m_type);
1891 void CVideoInfoTag::CAssetInfo::SetTitle(const std::string& assetTitle)
1893 std::string title{assetTitle};
1894 m_title = StringUtils::Trim(title);
1897 void CVideoInfoTag::CAssetInfo::SetId(int assetId)
1899 m_id = assetId;
1902 void CVideoInfoTag::CAssetInfo::SetType(VideoAssetType assetType)
1904 m_type = assetType;
1907 void CVideoInfoTag::SetHasVideoVersions(bool hasVersions)
1909 m_hasVideoVersions = hasVersions;
1912 void CVideoInfoTag::SetHasVideoExtras(bool hasExtras)
1914 m_hasVideoExtras = hasExtras;
1917 void CVideoInfoTag::SetIsDefaultVideoVersion(bool isDefaultVideoVersion)
1919 m_isDefaultVideoVersion = isDefaultVideoVersion;