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();
84 bool CVideoInfoTag::Save(TiXmlNode *node, const CStdString &tag, bool savePathInfo)
86 if (!node) return false;
88 // we start with a <tag> tag
89 TiXmlElement movieElement(tag.c_str());
90 TiXmlNode *movie = node->InsertEndChild(movieElement);
92 if (!movie) return false;
94 XMLUtils::SetString(movie, "title", m_strTitle);
95 if (!m_strOriginalTitle.IsEmpty())
96 XMLUtils::SetString(movie, "originaltitle", m_strOriginalTitle);
97 if (!m_strSortTitle.IsEmpty())
98 XMLUtils::SetString(movie, "sorttitle", m_strSortTitle);
99 XMLUtils::SetFloat(movie, "rating", m_fRating);
100 XMLUtils::SetFloat(movie, "epbookmark", m_fEpBookmark);
101 XMLUtils::SetInt(movie, "year", m_iYear);
102 XMLUtils::SetInt(movie, "top250", m_iTop250);
103 if (tag == "episodedetails" || tag == "tvshow")
105 XMLUtils::SetInt(movie, "season", m_iSeason);
106 XMLUtils::SetInt(movie, "episode", m_iEpisode);
107 XMLUtils::SetInt(movie, "displayseason",m_iSpecialSortSeason);
108 XMLUtils::SetInt(movie, "displayepisode",m_iSpecialSortEpisode);
110 if (tag == "musicvideo")
112 XMLUtils::SetInt(movie, "track", m_iTrack);
113 XMLUtils::SetString(movie, "album", m_strAlbum);
115 XMLUtils::SetString(movie, "votes", m_strVotes);
116 XMLUtils::SetString(movie, "outline", m_strPlotOutline);
117 XMLUtils::SetString(movie, "plot", m_strPlot);
118 XMLUtils::SetString(movie, "tagline", m_strTagLine);
119 XMLUtils::SetString(movie, "runtime", m_strRuntime);
120 if (!m_strPictureURL.m_xml.empty())
123 doc.Parse(m_strPictureURL.m_xml);
124 const TiXmlNode* thumb = doc.FirstChild("thumb");
127 movie->InsertEndChild(*thumb);
128 thumb = thumb->NextSibling("thumb");
131 if (m_fanart.m_xml.size())
134 doc.Parse(m_fanart.m_xml);
135 movie->InsertEndChild(*doc.RootElement());
137 XMLUtils::SetString(movie, "mpaa", m_strMPAARating);
138 XMLUtils::SetInt(movie, "playcount", m_playCount);
139 XMLUtils::SetString(movie, "lastplayed", m_lastPlayed);
142 XMLUtils::SetString(movie, "file", m_strFile);
143 XMLUtils::SetString(movie, "path", m_strPath);
144 XMLUtils::SetString(movie, "filenameandpath", m_strFileNameAndPath);
145 XMLUtils::SetString(movie, "basepath", m_basePath);
147 if (!m_strEpisodeGuide.IsEmpty())
150 doc.Parse(m_strEpisodeGuide);
151 if (doc.RootElement())
152 movie->InsertEndChild(*doc.RootElement());
154 XMLUtils::SetString(movie, "episodeguide", m_strEpisodeGuide);
157 XMLUtils::SetString(movie, "id", m_strIMDBNumber);
158 XMLUtils::SetAdditiveString(movie, "genre",
159 g_advancedSettings.m_videoItemSeparator, m_strGenre);
160 XMLUtils::SetAdditiveString(movie, "country",
161 g_advancedSettings.m_videoItemSeparator, m_strCountry);
162 XMLUtils::SetAdditiveString(movie, "set",
163 g_advancedSettings.m_videoItemSeparator, m_strSet);
164 XMLUtils::SetAdditiveString(movie, "credits",
165 g_advancedSettings.m_videoItemSeparator, m_strWritingCredits);
166 XMLUtils::SetAdditiveString(movie, "director",
167 g_advancedSettings.m_videoItemSeparator, m_strDirector);
168 XMLUtils::SetString(movie, "premiered", m_strPremiered);
169 XMLUtils::SetString(movie, "status", m_strStatus);
170 XMLUtils::SetString(movie, "code", m_strProductionCode);
171 XMLUtils::SetString(movie, "aired", m_strFirstAired);
172 XMLUtils::SetAdditiveString(movie, "studio",
173 g_advancedSettings.m_videoItemSeparator, m_strStudio);
174 XMLUtils::SetString(movie, "trailer", m_strTrailer);
176 if (m_streamDetails.HasItems())
178 // it goes fileinfo/streamdetails/[video|audio|subtitle]
179 TiXmlElement fileinfo("fileinfo");
180 TiXmlElement streamdetails("streamdetails");
181 for (int iStream=1; iStream<=m_streamDetails.GetVideoStreamCount(); iStream++)
183 TiXmlElement stream("video");
184 XMLUtils::SetString(&stream, "codec", m_streamDetails.GetVideoCodec(iStream));
185 XMLUtils::SetFloat(&stream, "aspect", m_streamDetails.GetVideoAspect(iStream));
186 XMLUtils::SetInt(&stream, "width", m_streamDetails.GetVideoWidth(iStream));
187 XMLUtils::SetInt(&stream, "height", m_streamDetails.GetVideoHeight(iStream));
188 XMLUtils::SetInt(&stream, "durationinseconds", m_streamDetails.GetVideoDuration(iStream));
189 streamdetails.InsertEndChild(stream);
191 for (int iStream=1; iStream<=m_streamDetails.GetAudioStreamCount(); iStream++)
193 TiXmlElement stream("audio");
194 XMLUtils::SetString(&stream, "codec", m_streamDetails.GetAudioCodec(iStream));
195 XMLUtils::SetString(&stream, "language", m_streamDetails.GetAudioLanguage(iStream));
196 XMLUtils::SetInt(&stream, "channels", m_streamDetails.GetAudioChannels(iStream));
197 streamdetails.InsertEndChild(stream);
199 for (int iStream=1; iStream<=m_streamDetails.GetSubtitleStreamCount(); iStream++)
201 TiXmlElement stream("subtitle");
202 XMLUtils::SetString(&stream, "language", m_streamDetails.GetSubtitleLanguage(iStream));
203 streamdetails.InsertEndChild(stream);
205 fileinfo.InsertEndChild(streamdetails);
206 movie->InsertEndChild(fileinfo);
207 } /* if has stream details */
210 for (iCast it = m_cast.begin(); it != m_cast.end(); ++it)
213 TiXmlElement cast("actor");
214 TiXmlNode *node = movie->InsertEndChild(cast);
215 TiXmlElement actor("name");
216 TiXmlNode *actorNode = node->InsertEndChild(actor);
217 TiXmlText name(it->strName);
218 actorNode->InsertEndChild(name);
219 TiXmlElement role("role");
220 TiXmlNode *roleNode = node->InsertEndChild(role);
221 TiXmlText character(it->strRole);
222 roleNode->InsertEndChild(character);
223 TiXmlElement thumb("thumb");
224 TiXmlNode *thumbNode = node->InsertEndChild(thumb);
225 TiXmlText th(it->thumbUrl.GetFirstThumb().m_url);
226 thumbNode->InsertEndChild(th);
228 XMLUtils::SetAdditiveString(movie, "artist",
229 g_advancedSettings.m_videoItemSeparator, m_strArtist);
230 XMLUtils::SetAdditiveString(movie, "showlink",
231 g_advancedSettings.m_videoItemSeparator, m_strShowLink);
236 bool CVideoInfoTag::Load(const TiXmlElement *movie, bool chained /* = false */)
238 if (!movie) return false;
240 // reset our details if we aren't chained.
241 if (!chained) Reset();
248 void CVideoInfoTag::Archive(CArchive& ar)
253 ar << m_strWritingCredits;
257 ar << m_strPlotOutline;
259 ar << m_strPictureURL.m_spoof;
260 ar << m_strPictureURL.m_xml;
261 ar << m_fanart.m_xml;
263 ar << m_strSortTitle;
267 ar << (int)m_cast.size();
268 for (unsigned int i=0;i<m_cast.size();++i)
270 ar << m_cast[i].strName;
271 ar << m_cast[i].strRole;
272 ar << m_cast[i].thumbUrl.m_xml;
279 ar << m_strIMDBNumber;
280 ar << m_strMPAARating;
281 ar << m_strFileNameAndPath;
282 ar << m_strOriginalTitle;
283 ar << m_strEpisodeGuide;
284 ar << m_strPremiered;
286 ar << m_strProductionCode;
287 ar << m_strFirstAired;
288 ar << m_strShowTitle;
300 ar << m_iSpecialSortSeason;
301 ar << m_iSpecialSortEpisode;
304 ar << dynamic_cast<IArchivable&>(m_streamDetails);
312 ar >> m_strWritingCredits;
316 ar >> m_strPlotOutline;
318 ar >> m_strPictureURL.m_spoof;
319 ar >> m_strPictureURL.m_xml;
320 m_strPictureURL.Parse();
321 ar >> m_fanart.m_xml;
324 ar >> m_strSortTitle;
330 for (int i=0;i<iCastSize;++i)
337 info.thumbUrl.ParseString(strXml);
338 m_cast.push_back(info);
345 ar >> m_strIMDBNumber;
346 ar >> m_strMPAARating;
347 ar >> m_strFileNameAndPath;
348 ar >> m_strOriginalTitle;
349 ar >> m_strEpisodeGuide;
350 ar >> m_strPremiered;
352 ar >> m_strProductionCode;
353 ar >> m_strFirstAired;
354 ar >> m_strShowTitle;
366 ar >> m_iSpecialSortSeason;
367 ar >> m_iSpecialSortEpisode;
370 ar >> dynamic_cast<IArchivable&>(m_streamDetails);
377 void CVideoInfoTag::Serialize(CVariant& value)
379 value["director"] = m_strDirector;
380 value["writingcredits"] = m_strWritingCredits;
381 value["genre"] = m_strGenre;
382 value["country"] = m_strCountry;
383 value["tagline"] = m_strTagLine;
384 value["plotoutline"] = m_strPlotOutline;
385 value["plot"] = m_strPlot;
386 value["title"] = m_strTitle;
387 value["votes"] = m_strVotes;
388 value["studio"] = m_strStudio;
389 value["trailer"] = m_strTrailer;
390 value["cast"] = CVariant(CVariant::VariantTypeArray);
391 for (unsigned int i = 0; i < m_cast.size(); ++i)
394 actor["name"] = m_cast[i].strName;
395 actor["role"] = m_cast[i].strRole;
396 value["cast"].push_back(actor);
398 value["set"] = m_strSet;
399 value["runtime"] = m_strRuntime;
400 value["file"] = m_strFile;
401 value["path"] = m_strPath;
402 value["imdbnumber"] = m_strIMDBNumber;
403 value["mpaa"] = m_strMPAARating;
404 value["filenameandpath"] = m_strFileNameAndPath;
405 value["originaltitle"] = m_strOriginalTitle;
406 value["sorttitle"] = m_strSortTitle;
407 value["episodeguide"] = m_strEpisodeGuide;
408 value["premiered"] = m_strPremiered;
409 value["status"] = m_strStatus;
410 value["productioncode"] = m_strProductionCode;
411 value["firstaired"] = m_strFirstAired;
412 value["showtitle"] = m_strShowTitle;
413 value["album"] = m_strAlbum;
414 value["artist"] = m_strArtist;
415 value["playcount"] = m_playCount;
416 value["lastPlayed"] = m_lastPlayed;
417 value["top250"] = m_iTop250;
418 value["year"] = m_iYear;
419 value["season"] = m_iSeason;
420 value["episode"] = m_iEpisode;
421 value["rating"] = m_fRating;
422 value["dbid"] = m_iDbId;
423 value["fileid"] = m_iFileId;
424 value["track"] = m_iTrack;
425 value["showlink"] = m_strShowLink;
426 m_streamDetails.Serialize(value["streamDetails"]);
429 const CStdString CVideoInfoTag::GetCast(bool bIncludeRole /*= false*/) const
432 for (iCast it = m_cast.begin(); it != m_cast.end(); ++it)
434 CStdString character;
435 if (it->strRole.IsEmpty() || !bIncludeRole)
436 character.Format("%s\n", it->strName.c_str());
438 character.Format("%s %s %s\n", it->strName.c_str(), g_localizeStrings.Get(20347).c_str(), it->strRole.c_str());
439 strLabel += character;
441 return strLabel.TrimRight("\n");
444 void CVideoInfoTag::ParseNative(const TiXmlElement* movie)
446 XMLUtils::GetString(movie, "title", m_strTitle);
447 XMLUtils::GetString(movie, "originaltitle", m_strOriginalTitle);
448 XMLUtils::GetString(movie, "sorttitle", m_strSortTitle);
449 XMLUtils::GetFloat(movie, "rating", m_fRating);
450 XMLUtils::GetFloat(movie, "epbookmark", m_fEpBookmark);
452 const TiXmlElement* rElement = movie->FirstChildElement("rating");
453 if (rElement && (rElement->QueryIntAttribute("max", &max_value) == TIXML_SUCCESS) && max_value>=1)
455 m_fRating = m_fRating / max_value * 10; // Normalise the Movie Rating to between 1 and 10
457 XMLUtils::GetInt(movie, "year", m_iYear);
458 XMLUtils::GetInt(movie, "top250", m_iTop250);
459 XMLUtils::GetInt(movie, "season", m_iSeason);
460 XMLUtils::GetInt(movie, "episode", m_iEpisode);
461 XMLUtils::GetInt(movie, "track", m_iTrack);
462 XMLUtils::GetInt(movie, "displayseason", m_iSpecialSortSeason);
463 XMLUtils::GetInt(movie, "displayepisode", m_iSpecialSortEpisode);
465 XMLUtils::GetInt(movie, "displayafterseason",after);
468 m_iSpecialSortSeason = after;
469 m_iSpecialSortEpisode = 0x1000; // should be more than any realistic episode number
471 XMLUtils::GetString(movie, "votes", m_strVotes);
472 XMLUtils::GetString(movie, "outline", m_strPlotOutline);
473 XMLUtils::GetString(movie, "plot", m_strPlot);
474 XMLUtils::GetString(movie, "tagline", m_strTagLine);
475 XMLUtils::GetString(movie, "runtime", m_strRuntime);
476 XMLUtils::GetString(movie, "mpaa", m_strMPAARating);
477 XMLUtils::GetInt(movie, "playcount", m_playCount);
478 XMLUtils::GetString(movie, "lastplayed", m_lastPlayed);
479 XMLUtils::GetString(movie, "file", m_strFile);
480 XMLUtils::GetString(movie, "path", m_strPath);
481 XMLUtils::GetString(movie, "id", m_strIMDBNumber);
482 XMLUtils::GetString(movie, "filenameandpath", m_strFileNameAndPath);
483 XMLUtils::GetString(movie, "premiered", m_strPremiered);
484 XMLUtils::GetString(movie, "status", m_strStatus);
485 XMLUtils::GetString(movie, "code", m_strProductionCode);
486 XMLUtils::GetString(movie, "aired", m_strFirstAired);
487 XMLUtils::GetString(movie, "album", m_strAlbum);
488 XMLUtils::GetString(movie, "trailer", m_strTrailer);
489 XMLUtils::GetString(movie, "basepath", m_basePath);
491 const TiXmlElement* thumb = movie->FirstChildElement("thumb");
494 m_strPictureURL.ParseElement(thumb);
495 thumb = thumb->NextSiblingElement("thumb");
498 XMLUtils::GetAdditiveString(movie,"genre",g_advancedSettings.m_videoItemSeparator,m_strGenre);
499 XMLUtils::GetAdditiveString(movie,"country",g_advancedSettings.m_videoItemSeparator,m_strCountry);
500 XMLUtils::GetAdditiveString(movie,"credits",g_advancedSettings.m_videoItemSeparator,m_strWritingCredits);
501 XMLUtils::GetAdditiveString(movie,"director",g_advancedSettings.m_videoItemSeparator,m_strDirector);
502 XMLUtils::GetAdditiveString(movie,"showlink",g_advancedSettings.m_videoItemSeparator,m_strShowLink);
505 const TiXmlElement* node = movie->FirstChildElement("actor");
508 const TiXmlNode *actor = node->FirstChild("name");
509 if (actor && actor->FirstChild())
512 info.strName = actor->FirstChild()->Value();
513 const TiXmlNode *roleNode = node->FirstChild("role");
514 if (roleNode && roleNode->FirstChild())
515 info.strRole = roleNode->FirstChild()->Value();
516 const TiXmlElement* thumb = node->FirstChildElement("thumb");
519 info.thumbUrl.ParseElement(thumb);
520 thumb = thumb->NextSiblingElement("thumb");
522 const char* clear=node->Attribute("clear");
523 if (clear && stricmp(clear,"true"))
525 m_cast.push_back(info);
527 node = node->NextSiblingElement("actor");
529 XMLUtils::GetAdditiveString(movie,"set",g_advancedSettings.m_videoItemSeparator,m_strSet);
530 XMLUtils::GetAdditiveString(movie,"studio",g_advancedSettings.m_videoItemSeparator,m_strStudio);
532 node = movie->FirstChildElement("artist");
535 const TiXmlNode* pNode = node->FirstChild("name");
536 const char* pValue=NULL;
537 if (pNode && pNode->FirstChild())
538 pValue = pNode->FirstChild()->Value();
539 else if (node->FirstChild())
540 pValue = node->FirstChild()->Value();
543 const char* clear=node->Attribute("clear");
544 if (m_strArtist.IsEmpty() || (clear && stricmp(clear,"true")==0))
545 m_strArtist += pValue;
547 m_strArtist += g_advancedSettings.m_videoItemSeparator + pValue;
549 node = node->NextSiblingElement("artist");
552 m_streamDetails.Reset();
553 node = movie->FirstChildElement("fileinfo");
556 // Try to pull from fileinfo/streamdetails/[video|audio|subtitle]
557 const TiXmlNode *nodeStreamDetails = node->FirstChild("streamdetails");
558 if (nodeStreamDetails)
560 const TiXmlNode *nodeDetail = NULL;
561 while ((nodeDetail = nodeStreamDetails->IterateChildren("audio", nodeDetail)))
563 CStreamDetailAudio *p = new CStreamDetailAudio();
564 XMLUtils::GetString(nodeDetail, "codec", p->m_strCodec);
565 XMLUtils::GetString(nodeDetail, "language", p->m_strLanguage);
566 XMLUtils::GetInt(nodeDetail, "channels", p->m_iChannels);
567 p->m_strCodec.MakeLower();
568 p->m_strLanguage.MakeLower();
569 m_streamDetails.AddStream(p);
572 while ((nodeDetail = nodeStreamDetails->IterateChildren("video", nodeDetail)))
574 CStreamDetailVideo *p = new CStreamDetailVideo();
575 XMLUtils::GetString(nodeDetail, "codec", p->m_strCodec);
576 XMLUtils::GetFloat(nodeDetail, "aspect", p->m_fAspect);
577 XMLUtils::GetInt(nodeDetail, "width", p->m_iWidth);
578 XMLUtils::GetInt(nodeDetail, "height", p->m_iHeight);
579 XMLUtils::GetInt(nodeDetail, "durationinseconds", p->m_iDuration);
580 p->m_strCodec.MakeLower();
581 m_streamDetails.AddStream(p);
584 while ((nodeDetail = nodeStreamDetails->IterateChildren("subtitle", nodeDetail)))
586 CStreamDetailSubtitle *p = new CStreamDetailSubtitle();
587 XMLUtils::GetString(nodeDetail, "language", p->m_strLanguage);
588 p->m_strLanguage.MakeLower();
589 m_streamDetails.AddStream(p);
592 m_streamDetails.DetermineBestStreams();
595 const TiXmlElement *epguide = movie->FirstChildElement("episodeguide");
598 // DEPRECIATE ME - support for old XML-encoded <episodeguide> blocks.
599 if (epguide->FirstChild() && strnicmp("<episodeguide", epguide->FirstChild()->Value(), 13) == 0)
600 m_strEpisodeGuide = epguide->FirstChild()->Value();
605 m_strEpisodeGuide = stream.str();
610 const TiXmlElement *fanart = movie->FirstChildElement("fanart");
613 m_fanart.m_xml << *fanart;
618 bool CVideoInfoTag::HasStreamDetails() const
620 return m_streamDetails.HasItems();
623 bool CVideoInfoTag::IsEmpty() const
625 return (m_strTitle.IsEmpty() &&
626 m_strFile.IsEmpty() &&
627 m_strPath.IsEmpty());