2 * Copyright (C) 2005-2013 Team XBMC
5 * This Program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2, or (at your option)
10 * This Program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with XBMC; see the file COPYING. If not, see
17 * <http://www.gnu.org/licenses/>.
21 #include "VideoInfoTag.h"
22 #include "utils/XMLUtils.h"
23 #include "guilib/LocalizeStrings.h"
24 #include "settings/AdvancedSettings.h"
25 #include "utils/log.h"
26 #include "utils/StringUtils.h"
27 #include "utils/Variant.h"
28 #include "TextureDatabase.h"
29 #include "filesystem/File.h"
35 void CVideoInfoTag::Reset()
38 m_writingCredits.clear();
42 m_strPlotOutline.clear();
44 m_strPictureURL.Clear();
46 m_strShowTitle.clear();
47 m_strOriginalTitle.clear();
48 m_strSortTitle.clear();
56 m_strIMDBNumber.clear();
57 m_strMPAARating.clear();
58 m_strFileNameAndPath.clear();
61 m_strProductionCode.clear();
71 m_strUniqueId.clear();
72 m_iSpecialSortSeason = -1;
73 m_iSpecialSortEpisode = -1;
79 m_fanart.m_xml.clear();
83 m_streamDetails.Reset();
88 m_resumePoint.Reset();
89 m_resumePoint.type = CBookmark::RESUME;
92 m_strShowPath.clear();
97 bool CVideoInfoTag::Save(TiXmlNode *node, const CStdString &tag, bool savePathInfo, const TiXmlElement *additionalNode)
99 if (!node) return false;
101 // we start with a <tag> tag
102 TiXmlElement movieElement(tag.c_str());
103 TiXmlNode *movie = node->InsertEndChild(movieElement);
105 if (!movie) return false;
107 XMLUtils::SetString(movie, "title", m_strTitle);
108 if (!m_strOriginalTitle.IsEmpty())
109 XMLUtils::SetString(movie, "originaltitle", m_strOriginalTitle);
110 if (!m_strShowTitle.IsEmpty())
111 XMLUtils::SetString(movie, "showtitle", m_strShowTitle);
112 if (!m_strSortTitle.IsEmpty())
113 XMLUtils::SetString(movie, "sorttitle", m_strSortTitle);
114 XMLUtils::SetFloat(movie, "rating", m_fRating);
115 XMLUtils::SetFloat(movie, "epbookmark", m_fEpBookmark);
116 XMLUtils::SetInt(movie, "year", m_iYear);
117 XMLUtils::SetInt(movie, "top250", m_iTop250);
118 if (tag == "episodedetails" || tag == "tvshow")
120 XMLUtils::SetInt(movie, "season", m_iSeason);
121 XMLUtils::SetInt(movie, "episode", m_iEpisode);
122 XMLUtils::SetString(movie, "uniqueid", m_strUniqueId);
123 XMLUtils::SetInt(movie, "displayseason",m_iSpecialSortSeason);
124 XMLUtils::SetInt(movie, "displayepisode",m_iSpecialSortEpisode);
126 if (tag == "musicvideo")
128 XMLUtils::SetInt(movie, "track", m_iTrack);
129 XMLUtils::SetString(movie, "album", m_strAlbum);
131 XMLUtils::SetString(movie, "votes", m_strVotes);
132 XMLUtils::SetString(movie, "outline", m_strPlotOutline);
133 XMLUtils::SetString(movie, "plot", m_strPlot);
134 XMLUtils::SetString(movie, "tagline", m_strTagLine);
135 XMLUtils::SetInt(movie, "runtime", GetDuration() / 60);
136 if (!m_strPictureURL.m_xml.empty())
139 doc.Parse(m_strPictureURL.m_xml);
140 const TiXmlNode* thumb = doc.FirstChild("thumb");
143 movie->InsertEndChild(*thumb);
144 thumb = thumb->NextSibling("thumb");
147 if (m_fanart.m_xml.size())
150 doc.Parse(m_fanart.m_xml);
151 movie->InsertEndChild(*doc.RootElement());
153 XMLUtils::SetString(movie, "mpaa", m_strMPAARating);
154 XMLUtils::SetInt(movie, "playcount", m_playCount);
155 XMLUtils::SetDate(movie, "lastplayed", m_lastPlayed);
158 XMLUtils::SetString(movie, "file", m_strFile);
159 XMLUtils::SetString(movie, "path", m_strPath);
160 XMLUtils::SetString(movie, "filenameandpath", m_strFileNameAndPath);
161 XMLUtils::SetString(movie, "basepath", m_basePath);
163 if (!m_strEpisodeGuide.IsEmpty())
166 doc.Parse(m_strEpisodeGuide);
167 if (doc.RootElement())
168 movie->InsertEndChild(*doc.RootElement());
170 XMLUtils::SetString(movie, "episodeguide", m_strEpisodeGuide);
173 XMLUtils::SetString(movie, "id", m_strIMDBNumber);
174 XMLUtils::SetStringArray(movie, "genre", m_genre);
175 XMLUtils::SetStringArray(movie, "country", m_country);
176 XMLUtils::SetString(movie, "set", m_strSet);
177 XMLUtils::SetStringArray(movie, "tag", m_tags);
178 XMLUtils::SetStringArray(movie, "credits", m_writingCredits);
179 XMLUtils::SetStringArray(movie, "director", m_director);
180 XMLUtils::SetDate(movie, "premiered", m_premiered);
181 XMLUtils::SetString(movie, "status", m_strStatus);
182 XMLUtils::SetString(movie, "code", m_strProductionCode);
183 XMLUtils::SetDate(movie, "aired", m_firstAired);
184 XMLUtils::SetStringArray(movie, "studio", m_studio);
185 XMLUtils::SetString(movie, "trailer", m_strTrailer);
187 if (m_streamDetails.HasItems())
189 // it goes fileinfo/streamdetails/[video|audio|subtitle]
190 TiXmlElement fileinfo("fileinfo");
191 TiXmlElement streamdetails("streamdetails");
192 for (int iStream=1; iStream<=m_streamDetails.GetVideoStreamCount(); iStream++)
194 TiXmlElement stream("video");
195 XMLUtils::SetString(&stream, "codec", m_streamDetails.GetVideoCodec(iStream));
196 XMLUtils::SetFloat(&stream, "aspect", m_streamDetails.GetVideoAspect(iStream));
197 XMLUtils::SetInt(&stream, "width", m_streamDetails.GetVideoWidth(iStream));
198 XMLUtils::SetInt(&stream, "height", m_streamDetails.GetVideoHeight(iStream));
199 XMLUtils::SetInt(&stream, "durationinseconds", m_streamDetails.GetVideoDuration(iStream));
200 XMLUtils::SetString(&stream, "stereomode", m_streamDetails.GetStereoMode(iStream));
201 streamdetails.InsertEndChild(stream);
203 for (int iStream=1; iStream<=m_streamDetails.GetAudioStreamCount(); iStream++)
205 TiXmlElement stream("audio");
206 XMLUtils::SetString(&stream, "codec", m_streamDetails.GetAudioCodec(iStream));
207 XMLUtils::SetString(&stream, "language", m_streamDetails.GetAudioLanguage(iStream));
208 XMLUtils::SetInt(&stream, "channels", m_streamDetails.GetAudioChannels(iStream));
209 streamdetails.InsertEndChild(stream);
211 for (int iStream=1; iStream<=m_streamDetails.GetSubtitleStreamCount(); iStream++)
213 TiXmlElement stream("subtitle");
214 XMLUtils::SetString(&stream, "language", m_streamDetails.GetSubtitleLanguage(iStream));
215 streamdetails.InsertEndChild(stream);
217 fileinfo.InsertEndChild(streamdetails);
218 movie->InsertEndChild(fileinfo);
219 } /* if has stream details */
222 for (iCast it = m_cast.begin(); it != m_cast.end(); ++it)
225 TiXmlElement cast("actor");
226 TiXmlNode *node = movie->InsertEndChild(cast);
227 XMLUtils::SetString(node, "name", it->strName);
228 XMLUtils::SetString(node, "role", it->strRole);
229 XMLUtils::SetInt(node, "order", it->order);
230 XMLUtils::SetString(node, "thumb", it->thumbUrl.GetFirstThumb().m_url);
232 XMLUtils::SetStringArray(movie, "artist", m_artist);
233 XMLUtils::SetStringArray(movie, "showlink", m_showLink);
235 TiXmlElement resume("resume");
236 XMLUtils::SetFloat(&resume, "position", (float)m_resumePoint.timeInSeconds);
237 XMLUtils::SetFloat(&resume, "total", (float)m_resumePoint.totalTimeInSeconds);
238 movie->InsertEndChild(resume);
240 XMLUtils::SetString(movie, "dateadded", m_dateAdded.GetAsDBDateTime());
243 movie->InsertEndChild(*additionalNode);
248 bool CVideoInfoTag::Load(const TiXmlElement *element, bool append, bool prioritise)
254 ParseNative(element, prioritise);
258 void CVideoInfoTag::Archive(CArchive& ar)
263 ar << m_writingCredits;
267 ar << m_strPlotOutline;
269 ar << m_strPictureURL.m_spoof;
270 ar << m_strPictureURL.m_xml;
271 ar << m_fanart.m_xml;
273 ar << m_strSortTitle;
277 ar << (int)m_cast.size();
278 for (unsigned int i=0;i<m_cast.size();++i)
280 ar << m_cast[i].strName;
281 ar << m_cast[i].strRole;
282 ar << m_cast[i].order;
283 ar << m_cast[i].thumb;
284 ar << m_cast[i].thumbUrl.m_xml;
293 ar << m_strIMDBNumber;
294 ar << m_strMPAARating;
295 ar << m_strFileNameAndPath;
296 ar << m_strOriginalTitle;
297 ar << m_strEpisodeGuide;
300 ar << m_strProductionCode;
302 ar << m_strShowTitle;
315 ar << m_iSpecialSortSeason;
316 ar << m_iSpecialSortEpisode;
319 ar << dynamic_cast<IArchivable&>(m_streamDetails);
323 ar << m_parentPathID;
324 ar << m_resumePoint.timeInSeconds;
325 ar << m_resumePoint.totalTimeInSeconds;
328 ar << m_dateAdded.GetAsDBDateTime();
335 ar >> m_writingCredits;
339 ar >> m_strPlotOutline;
341 ar >> m_strPictureURL.m_spoof;
342 ar >> m_strPictureURL.m_xml;
343 ar >> m_fanart.m_xml;
345 ar >> m_strSortTitle;
351 m_cast.reserve(iCastSize);
352 for (int i=0;i<iCastSize;++i)
361 info.thumbUrl.ParseString(strXml);
362 m_cast.push_back(info);
371 ar >> m_strIMDBNumber;
372 ar >> m_strMPAARating;
373 ar >> m_strFileNameAndPath;
374 ar >> m_strOriginalTitle;
375 ar >> m_strEpisodeGuide;
378 ar >> m_strProductionCode;
380 ar >> m_strShowTitle;
393 ar >> m_iSpecialSortSeason;
394 ar >> m_iSpecialSortEpisode;
397 ar >> dynamic_cast<IArchivable&>(m_streamDetails);
401 ar >> m_parentPathID;
402 ar >> m_resumePoint.timeInSeconds;
403 ar >> m_resumePoint.totalTimeInSeconds;
407 CStdString dateAdded;
409 m_dateAdded.SetFromDBDateTime(dateAdded);
415 void CVideoInfoTag::Serialize(CVariant& value) const
417 value["director"] = m_director;
418 value["writer"] = m_writingCredits;
419 value["genre"] = m_genre;
420 value["country"] = m_country;
421 value["tagline"] = m_strTagLine;
422 value["plotoutline"] = m_strPlotOutline;
423 value["plot"] = m_strPlot;
424 value["title"] = m_strTitle;
425 value["votes"] = m_strVotes;
426 value["studio"] = m_studio;
427 value["trailer"] = m_strTrailer;
428 value["cast"] = CVariant(CVariant::VariantTypeArray);
429 for (unsigned int i = 0; i < m_cast.size(); ++i)
432 actor["name"] = m_cast[i].strName;
433 actor["role"] = m_cast[i].strRole;
434 actor["order"] = m_cast[i].order;
435 if (!m_cast[i].thumb.IsEmpty())
436 actor["thumbnail"] = CTextureUtils::GetWrappedImageURL(m_cast[i].thumb);
437 value["cast"].push_back(actor);
439 value["set"] = m_strSet;
440 value["setid"] = m_iSetId;
441 value["tag"] = m_tags;
442 value["runtime"] = GetDuration();
443 value["file"] = m_strFile;
444 value["path"] = m_strPath;
445 value["imdbnumber"] = m_strIMDBNumber;
446 value["mpaa"] = m_strMPAARating;
447 value["filenameandpath"] = m_strFileNameAndPath;
448 value["originaltitle"] = m_strOriginalTitle;
449 value["sorttitle"] = m_strSortTitle;
450 value["episodeguide"] = m_strEpisodeGuide;
451 value["premiered"] = m_premiered.IsValid() ? m_premiered.GetAsDBDate() : StringUtils::EmptyString;
452 value["status"] = m_strStatus;
453 value["productioncode"] = m_strProductionCode;
454 value["firstaired"] = m_firstAired.IsValid() ? m_firstAired.GetAsDBDate() : StringUtils::EmptyString;
455 value["showtitle"] = m_strShowTitle;
456 value["album"] = m_strAlbum;
457 value["artist"] = m_artist;
458 value["playcount"] = m_playCount;
459 value["lastplayed"] = m_lastPlayed.IsValid() ? m_lastPlayed.GetAsDBDateTime() : StringUtils::EmptyString;
460 value["top250"] = m_iTop250;
461 value["year"] = m_iYear;
462 value["season"] = m_iSeason;
463 value["episode"] = m_iEpisode;
464 value["uniqueid"]["unknown"] = m_strUniqueId;
465 value["rating"] = m_fRating;
466 value["dbid"] = m_iDbId;
467 value["fileid"] = m_iFileId;
468 value["track"] = m_iTrack;
469 value["showlink"] = m_showLink;
470 m_streamDetails.Serialize(value["streamdetails"]);
471 CVariant resume = CVariant(CVariant::VariantTypeObject);
472 resume["position"] = (float)m_resumePoint.timeInSeconds;
473 resume["total"] = (float)m_resumePoint.totalTimeInSeconds;
474 value["resume"] = resume;
475 value["tvshowid"] = m_iIdShow;
476 value["tvshowpath"] = m_strShowPath;
477 value["dateadded"] = m_dateAdded.IsValid() ? m_dateAdded.GetAsDBDateTime() : StringUtils::EmptyString;
478 value["type"] = m_type;
479 value["seasonid"] = m_iIdSeason;
482 void CVideoInfoTag::ToSortable(SortItem& sortable, Field field) const
486 case FieldDirector: sortable[FieldDirector] = m_director; break;
487 case FieldWriter: sortable[FieldWriter] = m_writingCredits; break;
488 case FieldGenre: sortable[FieldGenre] = m_genre; break;
489 case FieldCountry: sortable[FieldCountry] = m_country; break;
490 case FieldTagline: sortable[FieldTagline] = m_strTagLine; break;
491 case FieldPlotOutline: sortable[FieldPlotOutline] = m_strPlotOutline; break;
492 case FieldPlot: sortable[FieldPlot] = m_strPlot; break;
493 case FieldTitle: sortable[FieldTitle] = m_strTitle; break;
494 case FieldVotes: sortable[FieldVotes] = m_strVotes; break;
495 case FieldStudio: sortable[FieldStudio] = m_studio; break;
496 case FieldTrailer: sortable[FieldTrailer] = m_strTrailer; break;
497 case FieldSet: sortable[FieldSet] = m_strSet; break;
498 case FieldTime: sortable[FieldTime] = GetDuration(); break;
499 case FieldFilename: sortable[FieldFilename] = m_strFile; break;
500 case FieldMPAA: sortable[FieldMPAA] = m_strMPAARating; break;
501 case FieldPath: sortable[FieldPath] = m_strFileNameAndPath; break;
502 case FieldSortTitle: sortable[FieldSortTitle] = m_strSortTitle; break;
503 case FieldTvShowStatus: sortable[FieldTvShowStatus] = m_strStatus; break;
504 case FieldProductionCode: sortable[FieldProductionCode] = m_strProductionCode; break;
505 case FieldAirDate: sortable[FieldAirDate] = m_firstAired.IsValid() ? m_firstAired.GetAsDBDate() : (m_premiered.IsValid() ? m_premiered.GetAsDBDate() : StringUtils::EmptyString); break;
506 case FieldTvShowTitle: sortable[FieldTvShowTitle] = m_strShowTitle; break;
507 case FieldAlbum: sortable[FieldAlbum] = m_strAlbum; break;
508 case FieldArtist: sortable[FieldArtist] = m_artist; break;
509 case FieldPlaycount: sortable[FieldPlaycount] = m_playCount; break;
510 case FieldLastPlayed: sortable[FieldLastPlayed] = m_lastPlayed.IsValid() ? m_lastPlayed.GetAsDBDateTime() : StringUtils::EmptyString; break;
511 case FieldTop250: sortable[FieldTop250] = m_iTop250; break;
512 case FieldYear: sortable[FieldYear] = m_iYear; break;
513 case FieldSeason: sortable[FieldSeason] = m_iSeason; break;
514 case FieldEpisodeNumber: sortable[FieldEpisodeNumber] = m_iEpisode; break;
515 case FieldEpisodeNumberSpecialSort: sortable[FieldEpisodeNumberSpecialSort] = m_iSpecialSortEpisode; break;
516 case FieldSeasonSpecialSort: sortable[FieldSeasonSpecialSort] = m_iSpecialSortSeason; break;
517 case FieldRating: sortable[FieldRating] = m_fRating; break;
518 case FieldId: sortable[FieldId] = m_iDbId; break;
519 case FieldTrackNumber: sortable[FieldTrackNumber] = m_iTrack; break;
520 case FieldTag: sortable[FieldTag] = m_tags; break;
522 case FieldVideoResolution: sortable[FieldVideoResolution] = m_streamDetails.GetVideoHeight(); break;
523 case FieldVideoAspectRatio: sortable[FieldVideoAspectRatio] = m_streamDetails.GetVideoAspect(); break;
524 case FieldVideoCodec: sortable[FieldVideoCodec] = m_streamDetails.GetVideoCodec(); break;
525 case FieldStereoMode: sortable[FieldStereoMode] = m_streamDetails.GetStereoMode(); break;
527 case FieldAudioChannels: sortable[FieldAudioChannels] = m_streamDetails.GetAudioChannels(); break;
528 case FieldAudioCodec: sortable[FieldAudioCodec] = m_streamDetails.GetAudioCodec(); break;
529 case FieldAudioLanguage: sortable[FieldAudioLanguage] = m_streamDetails.GetAudioLanguage(); break;
531 case FieldSubtitleLanguage: sortable[FieldSubtitleLanguage] = m_streamDetails.GetSubtitleLanguage(); break;
533 case FieldInProgress: sortable[FieldInProgress] = m_resumePoint.IsPartWay(); break;
534 case FieldDateAdded: sortable[FieldDateAdded] = m_dateAdded.IsValid() ? m_dateAdded.GetAsDBDateTime() : StringUtils::EmptyString; break;
535 case FieldMediaType: sortable[FieldMediaType] = DatabaseUtils::MediaTypeFromString(m_type); break;
540 const CStdString CVideoInfoTag::GetCast(bool bIncludeRole /*= false*/) const
543 for (iCast it = m_cast.begin(); it != m_cast.end(); ++it)
545 CStdString character;
546 if (it->strRole.IsEmpty() || !bIncludeRole)
547 character = StringUtils::Format("%s\n", it->strName.c_str());
549 character = StringUtils::Format("%s %s %s\n", it->strName.c_str(), g_localizeStrings.Get(20347).c_str(), it->strRole.c_str());
550 strLabel += character;
552 return strLabel.TrimRight("\n");
555 void CVideoInfoTag::ParseNative(const TiXmlElement* movie, bool prioritise)
557 XMLUtils::GetString(movie, "title", m_strTitle);
558 XMLUtils::GetString(movie, "originaltitle", m_strOriginalTitle);
559 XMLUtils::GetString(movie, "showtitle", m_strShowTitle);
560 XMLUtils::GetString(movie, "sorttitle", m_strSortTitle);
561 XMLUtils::GetFloat(movie, "rating", m_fRating);
562 XMLUtils::GetFloat(movie, "epbookmark", m_fEpBookmark);
564 const TiXmlElement* rElement = movie->FirstChildElement("rating");
565 if (rElement && (rElement->QueryIntAttribute("max", &max_value) == TIXML_SUCCESS) && max_value>=1)
567 m_fRating = m_fRating / max_value * 10; // Normalise the Movie Rating to between 1 and 10
569 XMLUtils::GetInt(movie, "year", m_iYear);
570 XMLUtils::GetInt(movie, "top250", m_iTop250);
571 XMLUtils::GetInt(movie, "season", m_iSeason);
572 XMLUtils::GetInt(movie, "episode", m_iEpisode);
573 XMLUtils::GetInt(movie, "track", m_iTrack);
574 XMLUtils::GetString(movie, "uniqueid", m_strUniqueId);
575 XMLUtils::GetInt(movie, "displayseason", m_iSpecialSortSeason);
576 XMLUtils::GetInt(movie, "displayepisode", m_iSpecialSortEpisode);
578 XMLUtils::GetInt(movie, "displayafterseason",after);
581 m_iSpecialSortSeason = after;
582 m_iSpecialSortEpisode = 0x1000; // should be more than any realistic episode number
584 XMLUtils::GetString(movie, "votes", m_strVotes);
585 XMLUtils::GetString(movie, "outline", m_strPlotOutline);
586 XMLUtils::GetString(movie, "plot", m_strPlot);
587 XMLUtils::GetString(movie, "tagline", m_strTagLine);
589 if (XMLUtils::GetString(movie, "runtime", runtime) && !runtime.empty())
590 m_duration = GetDurationFromMinuteString(runtime);
591 XMLUtils::GetString(movie, "mpaa", m_strMPAARating);
592 XMLUtils::GetInt(movie, "playcount", m_playCount);
593 XMLUtils::GetDate(movie, "lastplayed", m_lastPlayed);
594 XMLUtils::GetString(movie, "file", m_strFile);
595 XMLUtils::GetString(movie, "path", m_strPath);
596 XMLUtils::GetString(movie, "id", m_strIMDBNumber);
597 XMLUtils::GetString(movie, "filenameandpath", m_strFileNameAndPath);
598 XMLUtils::GetDate(movie, "premiered", m_premiered);
599 XMLUtils::GetString(movie, "status", m_strStatus);
600 XMLUtils::GetString(movie, "code", m_strProductionCode);
601 XMLUtils::GetDate(movie, "aired", m_firstAired);
602 XMLUtils::GetString(movie, "album", m_strAlbum);
603 XMLUtils::GetString(movie, "trailer", m_strTrailer);
604 XMLUtils::GetString(movie, "basepath", m_basePath);
606 size_t iThumbCount = m_strPictureURL.m_url.size();
607 CStdString xmlAdd = m_strPictureURL.m_xml;
609 const TiXmlElement* thumb = movie->FirstChildElement("thumb");
612 m_strPictureURL.ParseElement(thumb);
617 xmlAdd = temp+xmlAdd;
619 thumb = thumb->NextSiblingElement("thumb");
622 // prioritise thumbs from nfos
623 if (prioritise && iThumbCount && iThumbCount != m_strPictureURL.m_url.size())
625 rotate(m_strPictureURL.m_url.begin(),
626 m_strPictureURL.m_url.begin()+iThumbCount,
627 m_strPictureURL.m_url.end());
628 m_strPictureURL.m_xml = xmlAdd;
631 XMLUtils::GetStringArray(movie, "genre", m_genre, prioritise, g_advancedSettings.m_videoItemSeparator);
632 XMLUtils::GetStringArray(movie, "country", m_country, prioritise, g_advancedSettings.m_videoItemSeparator);
633 XMLUtils::GetStringArray(movie, "credits", m_writingCredits, prioritise, g_advancedSettings.m_videoItemSeparator);
634 XMLUtils::GetStringArray(movie, "director", m_director, prioritise, g_advancedSettings.m_videoItemSeparator);
635 XMLUtils::GetStringArray(movie, "showlink", m_showLink, prioritise, g_advancedSettings.m_videoItemSeparator);
638 const TiXmlElement* node = movie->FirstChildElement("actor");
639 if (node && node->FirstChild() && prioritise)
643 const TiXmlNode *actor = node->FirstChild("name");
644 if (actor && actor->FirstChild())
647 info.strName = actor->FirstChild()->Value();
648 XMLUtils::GetString(node, "role", info.strRole);
649 XMLUtils::GetInt(node, "order", info.order);
650 const TiXmlElement* thumb = node->FirstChildElement("thumb");
653 info.thumbUrl.ParseElement(thumb);
654 thumb = thumb->NextSiblingElement("thumb");
656 const char* clear=node->Attribute("clear");
657 if (clear && stricmp(clear,"true"))
659 m_cast.push_back(info);
661 node = node->NextSiblingElement("actor");
664 XMLUtils::GetString(movie, "set", m_strSet);
665 XMLUtils::GetStringArray(movie, "tag", m_tags, prioritise, g_advancedSettings.m_videoItemSeparator);
666 XMLUtils::GetStringArray(movie, "studio", m_studio, prioritise, g_advancedSettings.m_videoItemSeparator);
668 node = movie->FirstChildElement("artist");
669 if (node && node->FirstChild() && prioritise)
673 const TiXmlNode* pNode = node->FirstChild("name");
674 const char* pValue=NULL;
675 if (pNode && pNode->FirstChild())
676 pValue = pNode->FirstChild()->Value();
677 else if (node->FirstChild())
678 pValue = node->FirstChild()->Value();
681 const char* clear=node->Attribute("clear");
682 if (clear && stricmp(clear,"true")==0)
684 vector<string> artists = StringUtils::Split(pValue, g_advancedSettings.m_videoItemSeparator);
685 m_artist.insert(m_artist.end(), artists.begin(), artists.end());
687 node = node->NextSiblingElement("artist");
690 node = movie->FirstChildElement("fileinfo");
693 // Try to pull from fileinfo/streamdetails/[video|audio|subtitle]
694 const TiXmlNode *nodeStreamDetails = node->FirstChild("streamdetails");
695 if (nodeStreamDetails)
697 const TiXmlNode *nodeDetail = NULL;
698 while ((nodeDetail = nodeStreamDetails->IterateChildren("audio", nodeDetail)))
700 CStreamDetailAudio *p = new CStreamDetailAudio();
701 XMLUtils::GetString(nodeDetail, "codec", p->m_strCodec);
702 XMLUtils::GetString(nodeDetail, "language", p->m_strLanguage);
703 XMLUtils::GetInt(nodeDetail, "channels", p->m_iChannels);
704 p->m_strCodec.MakeLower();
705 p->m_strLanguage.MakeLower();
706 m_streamDetails.AddStream(p);
709 while ((nodeDetail = nodeStreamDetails->IterateChildren("video", nodeDetail)))
711 CStreamDetailVideo *p = new CStreamDetailVideo();
712 XMLUtils::GetString(nodeDetail, "codec", p->m_strCodec);
713 XMLUtils::GetFloat(nodeDetail, "aspect", p->m_fAspect);
714 XMLUtils::GetInt(nodeDetail, "width", p->m_iWidth);
715 XMLUtils::GetInt(nodeDetail, "height", p->m_iHeight);
716 XMLUtils::GetInt(nodeDetail, "durationinseconds", p->m_iDuration);
717 XMLUtils::GetString(nodeDetail, "stereomode", p->m_strStereoMode);
718 p->m_strCodec.MakeLower();
719 StringUtils::ToLower(p->m_strStereoMode);
720 m_streamDetails.AddStream(p);
723 while ((nodeDetail = nodeStreamDetails->IterateChildren("subtitle", nodeDetail)))
725 CStreamDetailSubtitle *p = new CStreamDetailSubtitle();
726 XMLUtils::GetString(nodeDetail, "language", p->m_strLanguage);
727 p->m_strLanguage.MakeLower();
728 m_streamDetails.AddStream(p);
731 m_streamDetails.DetermineBestStreams();
734 const TiXmlElement *epguide = movie->FirstChildElement("episodeguide");
737 // DEPRECIATE ME - support for old XML-encoded <episodeguide> blocks.
738 if (epguide->FirstChild() && strnicmp("<episodeguide", epguide->FirstChild()->Value(), 13) == 0)
739 m_strEpisodeGuide = epguide->FirstChild()->Value();
744 m_strEpisodeGuide = stream.str();
749 const TiXmlElement *fanart = movie->FirstChildElement("fanart");
752 // we prioritise mixed-mode nfo's with fanart set
757 m_fanart.m_xml = temp+m_fanart.m_xml;
760 m_fanart.m_xml << *fanart;
765 const TiXmlNode *resume = movie->FirstChild("resume");
768 XMLUtils::GetDouble(resume, "position", m_resumePoint.timeInSeconds);
769 XMLUtils::GetDouble(resume, "total", m_resumePoint.totalTimeInSeconds);
773 CStdString dateAdded;
774 XMLUtils::GetString(movie, "dateadded", dateAdded);
775 m_dateAdded.SetFromDBDateTime(dateAdded);
778 bool CVideoInfoTag::HasStreamDetails() const
780 return m_streamDetails.HasItems();
783 bool CVideoInfoTag::IsEmpty() const
785 return (m_strTitle.IsEmpty() &&
786 m_strFile.IsEmpty() &&
787 m_strPath.IsEmpty());
790 unsigned int CVideoInfoTag::GetDuration() const
793 Prefer the duration from the stream if it isn't too
794 small (60%) compared to the duration from the tag.
796 unsigned int duration = m_streamDetails.GetVideoDuration();
797 if (duration > m_duration * 0.6)
803 unsigned int CVideoInfoTag::GetDurationFromMinuteString(const std::string &runtime)
805 unsigned int duration = (unsigned int)str2uint64(runtime);
807 { // failed for some reason, or zero
808 duration = strtoul(runtime.c_str(), NULL, 10);
809 CLog::Log(LOGWARNING, "%s <runtime> should be in minutes. Interpreting '%s' as %u minutes", __FUNCTION__, runtime.c_str(), duration);