2 * Copyright (C) 2005-2008 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, write to
17 * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
18 * http://www.gnu.org/copyleft/gpl.html
22 #include "VideoInfoTag.h"
23 #include "utils/XMLUtils.h"
24 #include "guilib/LocalizeStrings.h"
25 #include "settings/GUISettings.h"
26 #include "settings/AdvancedSettings.h"
27 #include "utils/log.h"
28 #include "utils/Variant.h"
29 #include "utils/CharsetConverter.h"
35 void CVideoInfoTag::Reset()
38 m_strWritingCredits = "";
42 m_strPlotOutline = "";
44 m_strPictureURL.Clear();
46 m_strOriginalTitle = "";
57 m_strProductionCode= "";
67 m_iSpecialSortSeason = -1;
68 m_iSpecialSortEpisode = -1;
78 m_streamDetails.Reset();
85 bool CVideoInfoTag::Save(TiXmlNode *node, const CStdString &tag, bool savePathInfo)
87 if (!node) return false;
89 // we start with a <tag> tag
90 TiXmlElement movieElement(tag.c_str());
91 TiXmlNode *movie = node->InsertEndChild(movieElement);
93 if (!movie) return false;
95 XMLUtils::SetString(movie, "title", m_strTitle);
96 if (!m_strOriginalTitle.IsEmpty())
97 XMLUtils::SetString(movie, "originaltitle", m_strOriginalTitle);
98 if (!m_strSortTitle.IsEmpty())
99 XMLUtils::SetString(movie, "sorttitle", m_strSortTitle);
100 XMLUtils::SetFloat(movie, "rating", m_fRating);
101 XMLUtils::SetFloat(movie, "epbookmark", m_fEpBookmark);
102 XMLUtils::SetInt(movie, "year", m_iYear);
103 XMLUtils::SetInt(movie, "top250", m_iTop250);
104 if (tag == "episodedetails" || tag == "tvshow")
106 XMLUtils::SetInt(movie, "season", m_iSeason);
107 XMLUtils::SetInt(movie, "episode", m_iEpisode);
108 XMLUtils::SetInt(movie, "displayseason",m_iSpecialSortSeason);
109 XMLUtils::SetInt(movie, "displayepisode",m_iSpecialSortEpisode);
111 if (tag == "musicvideo")
113 XMLUtils::SetInt(movie, "track", m_iTrack);
114 XMLUtils::SetString(movie, "album", m_strAlbum);
116 XMLUtils::SetString(movie, "votes", m_strVotes);
117 XMLUtils::SetString(movie, "outline", m_strPlotOutline);
118 XMLUtils::SetString(movie, "plot", m_strPlot);
119 XMLUtils::SetString(movie, "tagline", m_strTagLine);
120 XMLUtils::SetString(movie, "runtime", m_strRuntime);
121 if (!m_strPictureURL.m_xml.empty())
124 doc.Parse(m_strPictureURL.m_xml);
125 const TiXmlNode* thumb = doc.FirstChild("thumb");
128 movie->InsertEndChild(*thumb);
129 thumb = thumb->NextSibling("thumb");
132 if (m_fanart.m_xml.size())
135 doc.Parse(m_fanart.m_xml);
136 movie->InsertEndChild(*doc.RootElement());
138 XMLUtils::SetString(movie, "mpaa", m_strMPAARating);
139 XMLUtils::SetInt(movie, "playcount", m_playCount);
140 XMLUtils::SetString(movie, "lastplayed", m_lastPlayed);
143 XMLUtils::SetString(movie, "file", m_strFile);
144 XMLUtils::SetString(movie, "path", m_strPath);
145 XMLUtils::SetString(movie, "filenameandpath", m_strFileNameAndPath);
146 XMLUtils::SetString(movie, "basepath", m_basePath);
148 if (!m_strEpisodeGuide.IsEmpty())
151 doc.Parse(m_strEpisodeGuide);
152 if (doc.RootElement())
153 movie->InsertEndChild(*doc.RootElement());
155 XMLUtils::SetString(movie, "episodeguide", m_strEpisodeGuide);
158 XMLUtils::SetString(movie, "id", m_strIMDBNumber);
159 XMLUtils::SetAdditiveString(movie, "genre",
160 g_advancedSettings.m_videoItemSeparator, m_strGenre);
161 XMLUtils::SetAdditiveString(movie, "country",
162 g_advancedSettings.m_videoItemSeparator, m_strCountry);
163 XMLUtils::SetAdditiveString(movie, "set",
164 g_advancedSettings.m_videoItemSeparator, m_strSet);
165 XMLUtils::SetAdditiveString(movie, "credits",
166 g_advancedSettings.m_videoItemSeparator, m_strWritingCredits);
167 XMLUtils::SetAdditiveString(movie, "director",
168 g_advancedSettings.m_videoItemSeparator, m_strDirector);
169 XMLUtils::SetString(movie, "premiered", m_strPremiered);
170 XMLUtils::SetString(movie, "status", m_strStatus);
171 XMLUtils::SetString(movie, "code", m_strProductionCode);
172 XMLUtils::SetString(movie, "aired", m_strFirstAired);
173 XMLUtils::SetAdditiveString(movie, "studio",
174 g_advancedSettings.m_videoItemSeparator, m_strStudio);
175 XMLUtils::SetString(movie, "trailer", m_strTrailer);
177 if (m_streamDetails.HasItems())
179 // it goes fileinfo/streamdetails/[video|audio|subtitle]
180 TiXmlElement fileinfo("fileinfo");
181 TiXmlElement streamdetails("streamdetails");
182 for (int iStream=1; iStream<=m_streamDetails.GetVideoStreamCount(); iStream++)
184 TiXmlElement stream("video");
185 XMLUtils::SetString(&stream, "codec", m_streamDetails.GetVideoCodec(iStream));
186 XMLUtils::SetFloat(&stream, "aspect", m_streamDetails.GetVideoAspect(iStream));
187 XMLUtils::SetInt(&stream, "width", m_streamDetails.GetVideoWidth(iStream));
188 XMLUtils::SetInt(&stream, "height", m_streamDetails.GetVideoHeight(iStream));
189 XMLUtils::SetInt(&stream, "durationinseconds", m_streamDetails.GetVideoDuration(iStream));
190 streamdetails.InsertEndChild(stream);
192 for (int iStream=1; iStream<=m_streamDetails.GetAudioStreamCount(); iStream++)
194 TiXmlElement stream("audio");
195 XMLUtils::SetString(&stream, "codec", m_streamDetails.GetAudioCodec(iStream));
196 XMLUtils::SetString(&stream, "language", m_streamDetails.GetAudioLanguage(iStream));
197 XMLUtils::SetInt(&stream, "channels", m_streamDetails.GetAudioChannels(iStream));
198 streamdetails.InsertEndChild(stream);
200 for (int iStream=1; iStream<=m_streamDetails.GetSubtitleStreamCount(); iStream++)
202 TiXmlElement stream("subtitle");
203 XMLUtils::SetString(&stream, "language", m_streamDetails.GetSubtitleLanguage(iStream));
204 streamdetails.InsertEndChild(stream);
206 fileinfo.InsertEndChild(streamdetails);
207 movie->InsertEndChild(fileinfo);
208 } /* if has stream details */
211 for (iCast it = m_cast.begin(); it != m_cast.end(); ++it)
214 TiXmlElement cast("actor");
215 TiXmlNode *node = movie->InsertEndChild(cast);
216 TiXmlElement actor("name");
217 TiXmlNode *actorNode = node->InsertEndChild(actor);
218 TiXmlText name(it->strName);
219 actorNode->InsertEndChild(name);
220 TiXmlElement role("role");
221 TiXmlNode *roleNode = node->InsertEndChild(role);
222 TiXmlText character(it->strRole);
223 roleNode->InsertEndChild(character);
224 TiXmlElement thumb("thumb");
225 TiXmlNode *thumbNode = node->InsertEndChild(thumb);
226 TiXmlText th(it->thumbUrl.GetFirstThumb().m_url);
227 thumbNode->InsertEndChild(th);
229 XMLUtils::SetAdditiveString(movie, "artist",
230 g_advancedSettings.m_videoItemSeparator, m_strArtist);
231 XMLUtils::SetAdditiveString(movie, "showlink",
232 g_advancedSettings.m_videoItemSeparator, m_strShowLink);
237 bool CVideoInfoTag::Load(const TiXmlElement *movie, bool chained /* = false */)
239 if (!movie) return false;
241 // reset our details if we aren't chained.
242 if (!chained) Reset();
249 void CVideoInfoTag::Archive(CArchive& ar)
254 ar << m_strWritingCredits;
258 ar << m_strPlotOutline;
260 ar << m_strPictureURL.m_spoof;
261 ar << m_strPictureURL.m_xml;
262 ar << m_fanart.m_xml;
264 ar << m_strSortTitle;
268 ar << (int)m_cast.size();
269 for (unsigned int i=0;i<m_cast.size();++i)
271 ar << m_cast[i].strName;
272 ar << m_cast[i].strRole;
273 ar << m_cast[i].thumbUrl.m_xml;
280 ar << m_strIMDBNumber;
281 ar << m_strMPAARating;
282 ar << m_strFileNameAndPath;
283 ar << m_strOriginalTitle;
284 ar << m_strEpisodeGuide;
285 ar << m_strPremiered;
287 ar << m_strProductionCode;
288 ar << m_strFirstAired;
289 ar << m_strShowTitle;
301 ar << m_iSpecialSortSeason;
302 ar << m_iSpecialSortEpisode;
305 ar << dynamic_cast<IArchivable&>(m_streamDetails);
309 ar << m_parentPathID;
314 ar >> m_strWritingCredits;
318 ar >> m_strPlotOutline;
320 ar >> m_strPictureURL.m_spoof;
321 ar >> m_strPictureURL.m_xml;
322 m_strPictureURL.Parse();
323 ar >> m_fanart.m_xml;
326 ar >> m_strSortTitle;
332 for (int i=0;i<iCastSize;++i)
339 info.thumbUrl.ParseString(strXml);
340 m_cast.push_back(info);
347 ar >> m_strIMDBNumber;
348 ar >> m_strMPAARating;
349 ar >> m_strFileNameAndPath;
350 ar >> m_strOriginalTitle;
351 ar >> m_strEpisodeGuide;
352 ar >> m_strPremiered;
354 ar >> m_strProductionCode;
355 ar >> m_strFirstAired;
356 ar >> m_strShowTitle;
368 ar >> m_iSpecialSortSeason;
369 ar >> m_iSpecialSortEpisode;
372 ar >> dynamic_cast<IArchivable&>(m_streamDetails);
376 ar >> m_parentPathID;
380 void CVideoInfoTag::Serialize(CVariant& value)
382 value["director"] = m_strDirector;
383 value["writingcredits"] = m_strWritingCredits;
384 value["genre"] = m_strGenre;
385 value["country"] = m_strCountry;
386 value["tagline"] = m_strTagLine;
387 value["plotoutline"] = m_strPlotOutline;
388 value["plot"] = m_strPlot;
389 value["title"] = m_strTitle;
390 value["votes"] = m_strVotes;
391 value["studio"] = m_strStudio;
392 value["trailer"] = m_strTrailer;
393 value["cast"] = CVariant(CVariant::VariantTypeArray);
394 for (unsigned int i = 0; i < m_cast.size(); ++i)
397 actor["name"] = m_cast[i].strName;
398 actor["role"] = m_cast[i].strRole;
399 value["cast"].push_back(actor);
401 value["set"] = m_strSet;
402 value["runtime"] = m_strRuntime;
403 value["file"] = m_strFile;
404 value["path"] = m_strPath;
405 value["imdbnumber"] = m_strIMDBNumber;
406 value["mpaa"] = m_strMPAARating;
407 value["filenameandpath"] = m_strFileNameAndPath;
408 value["originaltitle"] = m_strOriginalTitle;
409 value["sorttitle"] = m_strSortTitle;
410 value["episodeguide"] = m_strEpisodeGuide;
411 value["premiered"] = m_strPremiered;
412 value["status"] = m_strStatus;
413 value["productioncode"] = m_strProductionCode;
414 value["firstaired"] = m_strFirstAired;
415 value["showtitle"] = m_strShowTitle;
416 value["album"] = m_strAlbum;
417 value["artist"] = m_strArtist;
418 value["playcount"] = m_playCount;
419 value["lastPlayed"] = m_lastPlayed;
420 value["top250"] = m_iTop250;
421 value["year"] = m_iYear;
422 value["season"] = m_iSeason;
423 value["episode"] = m_iEpisode;
424 value["rating"] = m_fRating;
425 value["dbid"] = m_iDbId;
426 value["fileid"] = m_iFileId;
427 value["track"] = m_iTrack;
428 value["showlink"] = m_strShowLink;
429 m_streamDetails.Serialize(value["streamDetails"]);
432 const CStdString CVideoInfoTag::GetCast(bool bIncludeRole /*= false*/) const
435 for (iCast it = m_cast.begin(); it != m_cast.end(); ++it)
437 CStdString character;
438 if (it->strRole.IsEmpty() || !bIncludeRole)
439 character.Format("%s\n", it->strName.c_str());
441 character.Format("%s %s %s\n", it->strName.c_str(), g_localizeStrings.Get(20347).c_str(), it->strRole.c_str());
442 strLabel += character;
444 return strLabel.TrimRight("\n");
447 void CVideoInfoTag::ParseNative(const TiXmlElement* movie)
449 XMLUtils::GetString(movie, "title", m_strTitle);
450 XMLUtils::GetString(movie, "originaltitle", m_strOriginalTitle);
451 XMLUtils::GetString(movie, "sorttitle", m_strSortTitle);
452 XMLUtils::GetFloat(movie, "rating", m_fRating);
453 XMLUtils::GetFloat(movie, "epbookmark", m_fEpBookmark);
455 const TiXmlElement* rElement = movie->FirstChildElement("rating");
456 if (rElement && (rElement->QueryIntAttribute("max", &max_value) == TIXML_SUCCESS) && max_value>=1)
458 m_fRating = m_fRating / max_value * 10; // Normalise the Movie Rating to between 1 and 10
460 XMLUtils::GetInt(movie, "year", m_iYear);
461 XMLUtils::GetInt(movie, "top250", m_iTop250);
462 XMLUtils::GetInt(movie, "season", m_iSeason);
463 XMLUtils::GetInt(movie, "episode", m_iEpisode);
464 XMLUtils::GetInt(movie, "track", m_iTrack);
465 XMLUtils::GetInt(movie, "displayseason", m_iSpecialSortSeason);
466 XMLUtils::GetInt(movie, "displayepisode", m_iSpecialSortEpisode);
468 XMLUtils::GetInt(movie, "displayafterseason",after);
471 m_iSpecialSortSeason = after;
472 m_iSpecialSortEpisode = 0x1000; // should be more than any realistic episode number
474 XMLUtils::GetString(movie, "votes", m_strVotes);
475 XMLUtils::GetString(movie, "outline", m_strPlotOutline);
476 XMLUtils::GetString(movie, "plot", m_strPlot);
477 XMLUtils::GetString(movie, "tagline", m_strTagLine);
478 XMLUtils::GetString(movie, "runtime", m_strRuntime);
479 XMLUtils::GetString(movie, "mpaa", m_strMPAARating);
480 XMLUtils::GetInt(movie, "playcount", m_playCount);
481 XMLUtils::GetString(movie, "lastplayed", m_lastPlayed);
482 XMLUtils::GetString(movie, "file", m_strFile);
483 XMLUtils::GetString(movie, "path", m_strPath);
484 XMLUtils::GetString(movie, "id", m_strIMDBNumber);
485 XMLUtils::GetString(movie, "filenameandpath", m_strFileNameAndPath);
486 XMLUtils::GetString(movie, "premiered", m_strPremiered);
487 XMLUtils::GetString(movie, "status", m_strStatus);
488 XMLUtils::GetString(movie, "code", m_strProductionCode);
489 XMLUtils::GetString(movie, "aired", m_strFirstAired);
490 XMLUtils::GetString(movie, "album", m_strAlbum);
491 XMLUtils::GetString(movie, "trailer", m_strTrailer);
492 XMLUtils::GetString(movie, "basepath", m_basePath);
494 const TiXmlElement* thumb = movie->FirstChildElement("thumb");
497 m_strPictureURL.ParseElement(thumb);
498 thumb = thumb->NextSiblingElement("thumb");
501 XMLUtils::GetAdditiveString(movie,"genre",g_advancedSettings.m_videoItemSeparator,m_strGenre);
502 XMLUtils::GetAdditiveString(movie,"country",g_advancedSettings.m_videoItemSeparator,m_strCountry);
503 XMLUtils::GetAdditiveString(movie,"credits",g_advancedSettings.m_videoItemSeparator,m_strWritingCredits);
504 XMLUtils::GetAdditiveString(movie,"director",g_advancedSettings.m_videoItemSeparator,m_strDirector);
505 XMLUtils::GetAdditiveString(movie,"showlink",g_advancedSettings.m_videoItemSeparator,m_strShowLink);
508 const TiXmlElement* node = movie->FirstChildElement("actor");
511 const TiXmlNode *actor = node->FirstChild("name");
512 if (actor && actor->FirstChild())
515 info.strName = actor->FirstChild()->Value();
516 const TiXmlNode *roleNode = node->FirstChild("role");
517 if (roleNode && roleNode->FirstChild())
518 info.strRole = roleNode->FirstChild()->Value();
519 const TiXmlElement* thumb = node->FirstChildElement("thumb");
522 info.thumbUrl.ParseElement(thumb);
523 thumb = thumb->NextSiblingElement("thumb");
525 const char* clear=node->Attribute("clear");
526 if (clear && stricmp(clear,"true"))
528 m_cast.push_back(info);
530 node = node->NextSiblingElement("actor");
532 XMLUtils::GetAdditiveString(movie,"set",g_advancedSettings.m_videoItemSeparator,m_strSet);
533 XMLUtils::GetAdditiveString(movie,"studio",g_advancedSettings.m_videoItemSeparator,m_strStudio);
535 node = movie->FirstChildElement("artist");
538 const TiXmlNode* pNode = node->FirstChild("name");
539 const char* pValue=NULL;
540 if (pNode && pNode->FirstChild())
541 pValue = pNode->FirstChild()->Value();
542 else if (node->FirstChild())
543 pValue = node->FirstChild()->Value();
546 const char* clear=node->Attribute("clear");
547 if (m_strArtist.IsEmpty() || (clear && stricmp(clear,"true")==0))
548 m_strArtist += pValue;
550 m_strArtist += g_advancedSettings.m_videoItemSeparator + pValue;
552 node = node->NextSiblingElement("artist");
555 m_streamDetails.Reset();
556 node = movie->FirstChildElement("fileinfo");
559 // Try to pull from fileinfo/streamdetails/[video|audio|subtitle]
560 const TiXmlNode *nodeStreamDetails = node->FirstChild("streamdetails");
561 if (nodeStreamDetails)
563 const TiXmlNode *nodeDetail = NULL;
564 while ((nodeDetail = nodeStreamDetails->IterateChildren("audio", nodeDetail)))
566 CStreamDetailAudio *p = new CStreamDetailAudio();
567 XMLUtils::GetString(nodeDetail, "codec", p->m_strCodec);
568 XMLUtils::GetString(nodeDetail, "language", p->m_strLanguage);
569 XMLUtils::GetInt(nodeDetail, "channels", p->m_iChannels);
570 p->m_strCodec.MakeLower();
571 p->m_strLanguage.MakeLower();
572 m_streamDetails.AddStream(p);
575 while ((nodeDetail = nodeStreamDetails->IterateChildren("video", nodeDetail)))
577 CStreamDetailVideo *p = new CStreamDetailVideo();
578 XMLUtils::GetString(nodeDetail, "codec", p->m_strCodec);
579 XMLUtils::GetFloat(nodeDetail, "aspect", p->m_fAspect);
580 XMLUtils::GetInt(nodeDetail, "width", p->m_iWidth);
581 XMLUtils::GetInt(nodeDetail, "height", p->m_iHeight);
582 XMLUtils::GetInt(nodeDetail, "durationinseconds", p->m_iDuration);
583 p->m_strCodec.MakeLower();
584 m_streamDetails.AddStream(p);
587 while ((nodeDetail = nodeStreamDetails->IterateChildren("subtitle", nodeDetail)))
589 CStreamDetailSubtitle *p = new CStreamDetailSubtitle();
590 XMLUtils::GetString(nodeDetail, "language", p->m_strLanguage);
591 p->m_strLanguage.MakeLower();
592 m_streamDetails.AddStream(p);
595 m_streamDetails.DetermineBestStreams();
598 const TiXmlElement *epguide = movie->FirstChildElement("episodeguide");
601 // DEPRECIATE ME - support for old XML-encoded <episodeguide> blocks.
602 if (epguide->FirstChild() && strnicmp("<episodeguide", epguide->FirstChild()->Value(), 13) == 0)
603 m_strEpisodeGuide = epguide->FirstChild()->Value();
608 m_strEpisodeGuide = stream.str();
613 const TiXmlElement *fanart = movie->FirstChildElement("fanart");
616 m_fanart.m_xml << *fanart;
621 bool CVideoInfoTag::HasStreamDetails() const
623 return m_streamDetails.HasItems();
626 bool CVideoInfoTag::IsEmpty() const
628 return (m_strTitle.IsEmpty() &&
629 m_strFile.IsEmpty() &&
630 m_strPath.IsEmpty());