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.
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"
28 void CVideoInfoTag::Reset()
31 m_writingCredits
.clear();
35 m_strPlotOutline
.clear();
37 m_strPictureURL
.Clear();
39 m_strShowTitle
.clear();
40 m_strOriginalTitle
.clear();
41 m_strSortTitle
.clear();
45 m_set
.overview
.clear();
48 m_hasVideoVersions
= false;
49 m_hasVideoExtras
= false;
50 m_isDefaultVideoVersion
= false;
53 m_strMPAARating
.clear();
54 m_strFileNameAndPath
.clear();
56 m_bHasPremiered
= false;
58 m_strProductionCode
.clear();
70 m_strDefaultUniqueID
= "unknown";
71 m_iSpecialSortSeason
= -1;
72 m_iSpecialSortEpisode
= -1;
73 m_strDefaultRating
= "default";
81 m_fanart
.m_xml
.clear();
85 m_namedSeasons
.clear();
86 m_streamDetails
.Reset();
87 m_playCount
= PLAYCOUNT_NOT_SET
;
89 m_EpBookmark
.type
= CBookmark::EPISODE
;
92 m_resumePoint
.Reset();
93 m_resumePoint
.type
= CBookmark::RESUME
;
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");
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())
174 doc
.Parse(m_strPictureURL
.GetData());
175 const TiXmlNode
* thumb
= doc
.FirstChild("thumb");
178 movie
->InsertEndChild(*thumb
);
179 thumb
= thumb
->NextSibling("thumb");
182 if (m_fanart
.m_xml
.size())
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
);
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())
201 doc
.Parse(m_strEpisodeGuide
);
202 if (doc
.RootElement())
203 movie
->InsertEndChild(*doc
.RootElement());
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
);
238 XMLUtils::SetDate(movie
, "premiered", m_premiered
);
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 */
283 for (iCast it
= m_cast
.begin(); it
!= m_cast
.end(); ++it
)
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");
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
);
321 movie
->InsertEndChild(*additionalNode
);
326 bool CVideoInfoTag::Load(const TiXmlElement
*element
, bool append
, bool prioritise
)
332 ParseNative(element
, prioritise
);
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
;
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
;
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
)
497 ar
<< m_writingCredits
;
501 ar
<< m_strPlotOutline
;
503 ar
<< m_strPictureURL
.GetData();
504 ar
<< m_fanart
.m_xml
;
506 ar
<< m_strSortTitle
;
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();
521 ar
<< m_set
.overview
;
523 m_assetInfo
.Archive(ar
);
524 ar
<< m_hasVideoVersions
;
525 ar
<< m_hasVideoExtras
;
526 ar
<< m_isDefaultVideoVersion
;
530 ar
<< m_strMPAARating
;
531 ar
<< m_strFileNameAndPath
;
532 ar
<< m_strOriginalTitle
;
533 ar
<< m_strEpisodeGuide
;
535 ar
<< m_bHasPremiered
;
537 ar
<< m_strProductionCode
;
539 ar
<< m_strShowTitle
;
542 ar
<< GetPlayCount();
547 ar
<< (int)m_uniqueIDs
.size();
548 for (const auto& i
: m_uniqueIDs
)
551 ar
<< (i
.first
== m_strDefaultUniqueID
);
554 ar
<< (int)m_ratings
.size();
555 for (const auto& i
: m_ratings
)
558 ar
<< (i
.first
== m_strDefaultRating
);
559 ar
<< i
.second
.rating
;
560 ar
<< i
.second
.votes
;
565 ar
<< m_iSpecialSortSeason
;
566 ar
<< m_iSpecialSortEpisode
;
569 ar
<< dynamic_cast<IArchivable
&>(m_streamDetails
);
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
;
580 ar
<< m_parentPathID
;
581 ar
<< m_resumePoint
.timeInSeconds
;
582 ar
<< m_resumePoint
.totalTimeInSeconds
;
583 ar
<< m_resumePoint
.playerState
;
585 ar
<< m_dateAdded
.GetAsDBDateTime();
588 ar
<< m_coverArt
.size();
589 for (auto& it
: m_coverArt
)
595 ar
>> m_writingCredits
;
599 ar
>> m_strPlotOutline
;
603 m_strPictureURL
.SetData(data
);
604 ar
>> m_fanart
.m_xml
;
606 ar
>> m_strSortTitle
;
611 m_cast
.reserve(iCastSize
);
612 for (int i
=0;i
<iCastSize
;++i
)
621 info
.thumbUrl
.ParseFromData(strXml
);
622 m_cast
.push_back(info
);
627 ar
>> m_set
.overview
;
629 m_assetInfo
.Archive(ar
);
630 ar
>> m_hasVideoVersions
;
631 ar
>> m_hasVideoExtras
;
632 ar
>> m_isDefaultVideoVersion
;
636 ar
>> m_strMPAARating
;
637 ar
>> m_strFileNameAndPath
;
638 ar
>> m_strOriginalTitle
;
639 ar
>> m_strEpisodeGuide
;
641 ar
>> m_bHasPremiered
;
643 ar
>> m_strProductionCode
;
645 ar
>> m_strShowTitle
;
655 for (int i
= 0; i
< iUniqueIDSize
; ++i
)
659 bool defaultUniqueID
;
661 ar
>> defaultUniqueID
;
663 SetUniqueID(value
, name
);
665 m_strDefaultUniqueID
= name
;
669 for (int i
= 0; i
< iRatingSize
; ++i
)
678 SetRating(rating
, name
);
680 m_strDefaultRating
= name
;
685 ar
>> m_iSpecialSortSeason
;
686 ar
>> m_iSpecialSortEpisode
;
689 ar
>> dynamic_cast<IArchivable
&>(m_streamDetails
);
693 ar
>> namedSeasonSize
;
694 for (int i
= 0; i
< namedSeasonSize
; ++i
)
698 std::string seasonName
;
700 m_namedSeasons
.insert(std::make_pair(seasonNumber
, seasonName
));
702 ar
>> m_EpBookmark
.playerState
;
703 ar
>> m_EpBookmark
.timeInSeconds
;
705 ar
>> m_parentPathID
;
706 ar
>> m_resumePoint
.timeInSeconds
;
707 ar
>> m_resumePoint
.totalTimeInSeconds
;
708 ar
>> m_resumePoint
.playerState
;
711 std::string dateAdded
;
713 m_dateAdded
.SetFromDBDateTime(dateAdded
);
718 m_coverArt
.resize(size
);
719 for (size_t i
= 0; i
< size
; ++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
)
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
)
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
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;
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
;
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;
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
;
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
);
851 sortable
[FieldSortTitle
] = m_strSortTitle
;
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
);
861 sortable
[FieldOriginalTitle
] = m_strOriginalTitle
;
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();
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;
908 const CRating
CVideoInfoTag::GetRating(std::string type
) const
911 type
= m_strDefaultRating
;
913 const auto& rating
= m_ratings
.find(type
);
914 if (rating
== m_ratings
.end())
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
934 if (m_firstAired
.IsValid())
935 return GetFirstAired().GetYear();
936 if (m_premiered
.IsValid())
937 return GetPremiered().GetYear();
941 bool CVideoInfoTag::HasPremiered() const
943 return m_bHasPremiered
;
946 const CDateTime
& CVideoInfoTag::GetPremiered() const
951 const CDateTime
& CVideoInfoTag::GetFirstAired() const
956 const std::string
CVideoInfoTag::GetUniqueID(std::string type
) const
959 type
= m_strDefaultUniqueID
;
961 const auto& uniqueid
= m_uniqueIDs
.find(type
);
962 if (uniqueid
== m_uniqueIDs
.end())
965 return uniqueid
->second
;
968 const std::map
<std::string
, std::string
>& CVideoInfoTag::GetUniqueIDs() const
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
);
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
)
1004 if (XMLUtils::GetString(movie
, "title", 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");
1019 for (const TiXmlElement
* child
= node
->FirstChildElement("rating"); child
!= nullptr; child
= child
->NextSiblingElement("rating"))
1023 if (child
->QueryStringAttribute("name", &name
) != TIXML_SUCCESS
)
1025 XMLUtils::GetFloat(child
, "value", r
.rating
);
1026 if (XMLUtils::GetString(child
, "votes", value
))
1027 r
.votes
= StringUtils::ReturnDigits(value
);
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
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
);
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");
1057 XMLUtils::GetDouble(epbookmark
, "position", m_EpBookmark
.timeInSeconds
);
1058 const TiXmlElement
*playerstate
= epbookmark
->FirstChildElement("playerstate");
1061 const TiXmlElement
*value
= playerstate
->FirstChildElement();
1063 m_EpBookmark
.playerState
<< *value
;
1067 XMLUtils::GetDouble(movie
, "epbookmark", m_EpBookmark
.timeInSeconds
);
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
);
1081 XMLUtils::GetInt(movie
, "displayafterseason",after
);
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
))
1094 if (XMLUtils::GetString(movie
, "tagline", 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
))
1110 if (XMLUtils::GetString(movie
, "path", value
))
1113 const TiXmlElement
* uniqueid
= movie
->FirstChildElement("uniqueid");
1114 if (uniqueid
== nullptr)
1116 if (XMLUtils::GetString(movie
, "id", value
))
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
);
1128 SetUniqueID(uniqueid
->FirstChild()->ValueStr());
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;
1149 if (XMLUtils::GetInt(movie
, "year", year
))
1153 if (XMLUtils::GetString(movie
, "status", 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
))
1164 if (XMLUtils::GetString(movie
, "trailer", value
))
1167 if (XMLUtils::GetString(movie
, "basepath", 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");
1178 m_strPictureURL
.ParseAndAppendUrl(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
))
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)
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");
1235 node
= movie
->FirstChildElement("actor");
1236 if (node
&& node
->FirstChild() && prioritise
)
1240 const TiXmlNode
*actor
= node
->FirstChild("name");
1241 if (actor
&& actor
->FirstChild())
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");
1253 info
.thumbUrl
.ParseAndAppendUrl(thumb
);
1254 thumb
= thumb
->NextSiblingElement("thumb");
1256 const char* clear
=node
->Attribute("clear");
1257 if (clear
&& StringUtils::CompareNoCase(clear
, "true"))
1259 m_cast
.push_back(info
);
1261 node
= node
->NextSiblingElement("actor");
1264 // Pre-Jarvis NFO file:
1266 m_updateSetOverview
= false;
1267 if (XMLUtils::GetString(movie
, "set", value
))
1270 // <set><name>A set</name><overview>A set with a number of movies...</overview></set>
1271 node
= movie
->FirstChildElement("set");
1275 if (XMLUtils::GetString(node
, "name", 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
))
1290 m_assetInfo
.ParseNative(movie
);
1292 std::vector
<std::string
> studio(m_studio
);
1293 if (XMLUtils::GetStringArray(movie
, "studio", studio
, prioritise
, itemSeparator
))
1297 std::vector
<std::string
> artist(m_artist
);
1298 node
= movie
->FirstChildElement("artist");
1299 if (node
&& node
->FirstChild() && prioritise
)
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();
1311 const char* clear
=node
->Attribute("clear");
1312 if (clear
&& StringUtils::CompareNoCase(clear
, "true") == 0)
1314 std::vector
<std::string
> newArtists
= StringUtils::Split(pValue
, itemSeparator
);
1315 artist
.insert(artist
.end(), newArtists
.begin(), newArtists
.end());
1317 node
= node
->NextSiblingElement("artist");
1321 node
= movie
->FirstChildElement("fileinfo");
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
);
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
);
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();
1380 if (m_strEpisodeGuide
.empty())
1382 const TiXmlElement
* epguide
= movie
->FirstChildElement("episodeguide");
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();
1393 std::stringstream stream
;
1395 m_strEpisodeGuide
= stream
.str();
1401 const TiXmlElement
*fanart
= movie
->FirstChildElement("fanart");
1404 // we prioritise mixed-mode nfo's with fanart set
1409 m_fanart
.m_xml
= temp
+m_fanart
.m_xml
;
1412 m_fanart
.m_xml
<< *fanart
;
1417 const TiXmlNode
*resume
= movie
->FirstChild("resume");
1420 XMLUtils::GetDouble(resume
, "position", m_resumePoint
.timeInSeconds
);
1421 XMLUtils::GetDouble(resume
, "total", m_resumePoint
.totalTimeInSeconds
);
1422 const TiXmlElement
*playerstate
= resume
->FirstChildElement("playerstate");
1425 const TiXmlElement
*value
= playerstate
->FirstChildElement();
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() &&
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)
1464 unsigned int CVideoInfoTag::GetStaticDuration() const
1469 unsigned int CVideoInfoTag::GetDurationFromMinuteString(const std::string
&runtime
)
1471 unsigned int duration
= (unsigned int)str2uint64(runtime
);
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
);
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
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)
1557 m_ratings
[m_strDefaultRating
] = rating
;
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)
1572 m_ratings
[m_strDefaultRating
].rating
= rating
;
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
/* = "" */)
1602 m_ratings
[m_strDefaultRating
].votes
= votes
;
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
)
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
));
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())
1734 m_uniqueIDs
[m_strDefaultUniqueID
] = uniqueid
;
1737 m_uniqueIDs
[type
] = uniqueid
;
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
;
1787 bool CVideoInfoTag::IncrementPlayCount()
1789 if (!IsPlayCountSet())
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
;
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
;
1829 void CVideoInfoTag::CAssetInfo::Clear()
1833 m_type
= VideoAssetType::UNKNOWN
;
1836 void CVideoInfoTag::CAssetInfo::Archive(CArchive
& ar
)
1842 ar
<< static_cast<int>(m_type
);
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
)
1864 if (XMLUtils::GetString(movie
, "videoassettitle", value
))
1867 XMLUtils::GetInt(movie
, "videoassetid", m_id
);
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)
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
)
1902 void CVideoInfoTag::CAssetInfo::SetType(VideoAssetType 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
;