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();
83 bool CVideoInfoTag::Save(TiXmlNode *node, const CStdString &tag, bool savePathInfo)
85 if (!node) return false;
87 // we start with a <tag> tag
88 TiXmlElement movieElement(tag.c_str());
89 TiXmlNode *movie = node->InsertEndChild(movieElement);
91 if (!movie) return false;
93 XMLUtils::SetString(movie, "title", m_strTitle);
94 if (!m_strOriginalTitle.IsEmpty())
95 XMLUtils::SetString(movie, "originaltitle", m_strOriginalTitle);
96 if (!m_strSortTitle.IsEmpty())
97 XMLUtils::SetString(movie, "sorttitle", m_strSortTitle);
98 XMLUtils::SetFloat(movie, "rating", m_fRating);
99 XMLUtils::SetFloat(movie, "epbookmark", m_fEpBookmark);
100 XMLUtils::SetInt(movie, "year", m_iYear);
101 XMLUtils::SetInt(movie, "top250", m_iTop250);
102 if (tag == "episodedetails" || tag == "tvshow")
104 XMLUtils::SetInt(movie, "season", m_iSeason);
105 XMLUtils::SetInt(movie, "episode", m_iEpisode);
106 XMLUtils::SetInt(movie, "displayseason",m_iSpecialSortSeason);
107 XMLUtils::SetInt(movie, "displayepisode",m_iSpecialSortEpisode);
109 if (tag == "musicvideo")
111 XMLUtils::SetInt(movie, "track", m_iTrack);
112 XMLUtils::SetString(movie, "album", m_strAlbum);
114 XMLUtils::SetString(movie, "votes", m_strVotes);
115 XMLUtils::SetString(movie, "outline", m_strPlotOutline);
116 XMLUtils::SetString(movie, "plot", m_strPlot);
117 XMLUtils::SetString(movie, "tagline", m_strTagLine);
118 XMLUtils::SetString(movie, "runtime", m_strRuntime);
119 if (!m_strPictureURL.m_xml.empty())
122 doc.Parse(m_strPictureURL.m_xml);
123 const TiXmlNode* thumb = doc.FirstChild("thumb");
126 movie->InsertEndChild(*thumb);
127 thumb = thumb->NextSibling("thumb");
130 if (m_fanart.m_xml.size())
133 doc.Parse(m_fanart.m_xml);
134 movie->InsertEndChild(*doc.RootElement());
136 XMLUtils::SetString(movie, "mpaa", m_strMPAARating);
137 XMLUtils::SetInt(movie, "playcount", m_playCount);
138 XMLUtils::SetString(movie, "lastplayed", m_lastPlayed);
141 XMLUtils::SetString(movie, "file", m_strFile);
142 XMLUtils::SetString(movie, "path", m_strPath);
143 XMLUtils::SetString(movie, "filenameandpath", m_strFileNameAndPath);
145 if (!m_strEpisodeGuide.IsEmpty())
148 doc.Parse(m_strEpisodeGuide);
149 if (doc.RootElement())
150 movie->InsertEndChild(*doc.RootElement());
152 XMLUtils::SetString(movie, "episodeguide", m_strEpisodeGuide);
155 XMLUtils::SetString(movie, "id", m_strIMDBNumber);
156 XMLUtils::SetAdditiveString(movie, "genre",
157 g_advancedSettings.m_videoItemSeparator, m_strGenre);
158 XMLUtils::SetAdditiveString(movie, "country",
159 g_advancedSettings.m_videoItemSeparator, m_strCountry);
160 XMLUtils::SetAdditiveString(movie, "set",
161 g_advancedSettings.m_videoItemSeparator, m_strSet);
162 XMLUtils::SetAdditiveString(movie, "credits",
163 g_advancedSettings.m_videoItemSeparator, m_strWritingCredits);
164 XMLUtils::SetAdditiveString(movie, "director",
165 g_advancedSettings.m_videoItemSeparator, m_strDirector);
166 XMLUtils::SetString(movie, "premiered", m_strPremiered);
167 XMLUtils::SetString(movie, "status", m_strStatus);
168 XMLUtils::SetString(movie, "code", m_strProductionCode);
169 XMLUtils::SetString(movie, "aired", m_strFirstAired);
170 XMLUtils::SetAdditiveString(movie, "studio",
171 g_advancedSettings.m_videoItemSeparator, m_strStudio);
172 XMLUtils::SetString(movie, "trailer", m_strTrailer);
174 if (m_streamDetails.HasItems())
176 // it goes fileinfo/streamdetails/[video|audio|subtitle]
177 TiXmlElement fileinfo("fileinfo");
178 TiXmlElement streamdetails("streamdetails");
179 for (int iStream=1; iStream<=m_streamDetails.GetVideoStreamCount(); iStream++)
181 TiXmlElement stream("video");
182 XMLUtils::SetString(&stream, "codec", m_streamDetails.GetVideoCodec(iStream));
183 XMLUtils::SetFloat(&stream, "aspect", m_streamDetails.GetVideoAspect(iStream));
184 XMLUtils::SetInt(&stream, "width", m_streamDetails.GetVideoWidth(iStream));
185 XMLUtils::SetInt(&stream, "height", m_streamDetails.GetVideoHeight(iStream));
186 XMLUtils::SetInt(&stream, "durationinseconds", m_streamDetails.GetVideoDuration(iStream));
187 streamdetails.InsertEndChild(stream);
189 for (int iStream=1; iStream<=m_streamDetails.GetAudioStreamCount(); iStream++)
191 TiXmlElement stream("audio");
192 XMLUtils::SetString(&stream, "codec", m_streamDetails.GetAudioCodec(iStream));
193 XMLUtils::SetString(&stream, "language", m_streamDetails.GetAudioLanguage(iStream));
194 XMLUtils::SetInt(&stream, "channels", m_streamDetails.GetAudioChannels(iStream));
195 streamdetails.InsertEndChild(stream);
197 for (int iStream=1; iStream<=m_streamDetails.GetSubtitleStreamCount(); iStream++)
199 TiXmlElement stream("subtitle");
200 XMLUtils::SetString(&stream, "language", m_streamDetails.GetSubtitleLanguage(iStream));
201 streamdetails.InsertEndChild(stream);
203 fileinfo.InsertEndChild(streamdetails);
204 movie->InsertEndChild(fileinfo);
205 } /* if has stream details */
208 for (iCast it = m_cast.begin(); it != m_cast.end(); ++it)
211 TiXmlElement cast("actor");
212 TiXmlNode *node = movie->InsertEndChild(cast);
213 TiXmlElement actor("name");
214 TiXmlNode *actorNode = node->InsertEndChild(actor);
215 TiXmlText name(it->strName);
216 actorNode->InsertEndChild(name);
217 TiXmlElement role("role");
218 TiXmlNode *roleNode = node->InsertEndChild(role);
219 TiXmlText character(it->strRole);
220 roleNode->InsertEndChild(character);
221 TiXmlElement thumb("thumb");
222 TiXmlNode *thumbNode = node->InsertEndChild(thumb);
223 TiXmlText th(it->thumbUrl.GetFirstThumb().m_url);
224 thumbNode->InsertEndChild(th);
226 XMLUtils::SetAdditiveString(movie, "artist",
227 g_advancedSettings.m_videoItemSeparator, m_strArtist);
228 XMLUtils::SetAdditiveString(movie, "showlink",
229 g_advancedSettings.m_videoItemSeparator, m_strShowLink);
234 bool CVideoInfoTag::Load(const TiXmlElement *movie, bool chained /* = false */)
236 if (!movie) return false;
238 // reset our details if we aren't chained.
239 if (!chained) Reset();
246 void CVideoInfoTag::Archive(CArchive& ar)
251 ar << m_strWritingCredits;
255 ar << m_strPlotOutline;
257 ar << m_strPictureURL.m_spoof;
258 ar << m_strPictureURL.m_xml;
259 ar << m_fanart.m_xml;
261 ar << m_strSortTitle;
265 ar << (int)m_cast.size();
266 for (unsigned int i=0;i<m_cast.size();++i)
268 ar << m_cast[i].strName;
269 ar << m_cast[i].strRole;
270 ar << m_cast[i].thumbUrl.m_xml;
277 ar << m_strIMDBNumber;
278 ar << m_strMPAARating;
279 ar << m_strFileNameAndPath;
280 ar << m_strOriginalTitle;
281 ar << m_strEpisodeGuide;
282 ar << m_strPremiered;
284 ar << m_strProductionCode;
285 ar << m_strFirstAired;
286 ar << m_strShowTitle;
298 ar << m_iSpecialSortSeason;
299 ar << m_iSpecialSortEpisode;
302 ar << dynamic_cast<IArchivable&>(m_streamDetails);
309 ar >> m_strWritingCredits;
313 ar >> m_strPlotOutline;
315 ar >> m_strPictureURL.m_spoof;
316 ar >> m_strPictureURL.m_xml;
317 m_strPictureURL.Parse();
318 ar >> m_fanart.m_xml;
321 ar >> m_strSortTitle;
327 for (int i=0;i<iCastSize;++i)
334 info.thumbUrl.ParseString(strXml);
335 m_cast.push_back(info);
342 ar >> m_strIMDBNumber;
343 ar >> m_strMPAARating;
344 ar >> m_strFileNameAndPath;
345 ar >> m_strOriginalTitle;
346 ar >> m_strEpisodeGuide;
347 ar >> m_strPremiered;
349 ar >> m_strProductionCode;
350 ar >> m_strFirstAired;
351 ar >> m_strShowTitle;
363 ar >> m_iSpecialSortSeason;
364 ar >> m_iSpecialSortEpisode;
367 ar >> dynamic_cast<IArchivable&>(m_streamDetails);
373 void CVideoInfoTag::Serialize(CVariant& value)
375 value["director"] = m_strDirector;
376 value["writingcredits"] = m_strWritingCredits;
377 value["genre"] = m_strGenre;
378 value["country"] = m_strCountry;
379 value["tagline"] = m_strTagLine;
380 value["plotoutline"] = m_strPlotOutline;
381 value["plot"] = m_strPlot;
382 value["title"] = m_strTitle;
383 value["votes"] = m_strVotes;
384 value["studio"] = m_strStudio;
385 value["trailer"] = m_strTrailer;
386 for (unsigned int i = 0; i < m_cast.size(); ++i)
388 value["cast"][i]["name"] = m_cast[i].strName;
389 value["cast"][i]["role"] = m_cast[i].strRole;
391 value["set"] = m_strSet;
392 value["runtime"] = m_strRuntime;
393 value["file"] = m_strFile;
394 value["path"] = m_strPath;
395 value["imdbnumber"] = m_strIMDBNumber;
396 value["mpaa"] = m_strMPAARating;
397 value["filenameandpath"] = m_strFileNameAndPath;
398 value["originaltitle"] = m_strOriginalTitle;
399 value["episodeguide"] = m_strEpisodeGuide;
400 value["premiered"] = m_strPremiered;
401 value["status"] = m_strStatus;
402 value["productioncode"] = m_strProductionCode;
403 value["firstaired"] = m_strFirstAired;
404 value["showtitle"] = m_strShowTitle;
405 value["album"] = m_strAlbum;
406 value["artist"] = m_strArtist;
407 value["playcount"] = m_playCount;
408 value["lastPlayed"] = m_lastPlayed;
409 value["top250"] = m_iTop250;
410 value["year"] = m_iYear;
411 value["season"] = m_iSeason;
412 value["episode"] = m_iEpisode;
413 value["rating"] = m_fRating;
414 value["dbid"] = m_iDbId;
415 value["fileid"] = m_iFileId;
416 value["track"] = m_iTrack;
417 value["showlink"] = m_strShowLink;
418 m_streamDetails.Serialize(value["streamDetails"]);
421 const CStdString CVideoInfoTag::GetCast(bool bIncludeRole /*= false*/) const
424 for (iCast it = m_cast.begin(); it != m_cast.end(); ++it)
426 CStdString character;
427 if (it->strRole.IsEmpty() || !bIncludeRole)
428 character.Format("%s\n", it->strName.c_str());
430 character.Format("%s %s %s\n", it->strName.c_str(), g_localizeStrings.Get(20347).c_str(), it->strRole.c_str());
431 strLabel += character;
433 return strLabel.TrimRight("\n");
436 void CVideoInfoTag::ParseNative(const TiXmlElement* movie)
438 XMLUtils::GetString(movie, "title", m_strTitle);
439 XMLUtils::GetString(movie, "originaltitle", m_strOriginalTitle);
440 XMLUtils::GetString(movie, "sorttitle", m_strSortTitle);
441 XMLUtils::GetFloat(movie, "rating", m_fRating);
442 XMLUtils::GetFloat(movie, "epbookmark", m_fEpBookmark);
444 const TiXmlElement* rElement = movie->FirstChildElement("rating");
445 if (rElement && (rElement->QueryIntAttribute("max", &max_value) == TIXML_SUCCESS) && max_value>=1)
447 m_fRating = m_fRating / max_value * 10; // Normalise the Movie Rating to between 1 and 10
449 XMLUtils::GetInt(movie, "year", m_iYear);
450 XMLUtils::GetInt(movie, "top250", m_iTop250);
451 XMLUtils::GetInt(movie, "season", m_iSeason);
452 XMLUtils::GetInt(movie, "episode", m_iEpisode);
453 XMLUtils::GetInt(movie, "track", m_iTrack);
454 XMLUtils::GetInt(movie, "displayseason", m_iSpecialSortSeason);
455 XMLUtils::GetInt(movie, "displayepisode", m_iSpecialSortEpisode);
457 XMLUtils::GetInt(movie, "displayafterseason",after);
460 m_iSpecialSortSeason = after;
461 m_iSpecialSortEpisode = 0x1000; // should be more than any realistic episode number
463 XMLUtils::GetString(movie, "votes", m_strVotes);
464 XMLUtils::GetString(movie, "outline", m_strPlotOutline);
465 XMLUtils::GetString(movie, "plot", m_strPlot);
466 XMLUtils::GetString(movie, "tagline", m_strTagLine);
467 XMLUtils::GetString(movie, "runtime", m_strRuntime);
468 XMLUtils::GetString(movie, "mpaa", m_strMPAARating);
469 XMLUtils::GetInt(movie, "playcount", m_playCount);
470 XMLUtils::GetString(movie, "lastplayed", m_lastPlayed);
471 XMLUtils::GetString(movie, "file", m_strFile);
472 XMLUtils::GetString(movie, "path", m_strPath);
473 XMLUtils::GetString(movie, "id", m_strIMDBNumber);
474 XMLUtils::GetString(movie, "filenameandpath", m_strFileNameAndPath);
475 XMLUtils::GetString(movie, "premiered", m_strPremiered);
476 XMLUtils::GetString(movie, "status", m_strStatus);
477 XMLUtils::GetString(movie, "code", m_strProductionCode);
478 XMLUtils::GetString(movie, "aired", m_strFirstAired);
479 XMLUtils::GetString(movie, "album", m_strAlbum);
480 XMLUtils::GetString(movie, "trailer", m_strTrailer);
482 const TiXmlElement* thumb = movie->FirstChildElement("thumb");
485 m_strPictureURL.ParseElement(thumb);
486 thumb = thumb->NextSiblingElement("thumb");
489 XMLUtils::GetAdditiveString(movie,"genre",g_advancedSettings.m_videoItemSeparator,m_strGenre);
490 XMLUtils::GetAdditiveString(movie,"country",g_advancedSettings.m_videoItemSeparator,m_strCountry);
491 XMLUtils::GetAdditiveString(movie,"credits",g_advancedSettings.m_videoItemSeparator,m_strWritingCredits);
492 XMLUtils::GetAdditiveString(movie,"director",g_advancedSettings.m_videoItemSeparator,m_strDirector);
493 XMLUtils::GetAdditiveString(movie,"showlink",g_advancedSettings.m_videoItemSeparator,m_strShowLink);
496 const TiXmlElement* node = movie->FirstChildElement("actor");
499 const TiXmlNode *actor = node->FirstChild("name");
500 if (actor && actor->FirstChild())
503 info.strName = actor->FirstChild()->Value();
504 const TiXmlNode *roleNode = node->FirstChild("role");
505 if (roleNode && roleNode->FirstChild())
506 info.strRole = roleNode->FirstChild()->Value();
507 const TiXmlElement* thumb = node->FirstChildElement("thumb");
510 info.thumbUrl.ParseElement(thumb);
511 thumb = thumb->NextSiblingElement("thumb");
513 const char* clear=node->Attribute("clear");
514 if (clear && stricmp(clear,"true"))
516 m_cast.push_back(info);
518 node = node->NextSiblingElement("actor");
520 XMLUtils::GetAdditiveString(movie,"set",g_advancedSettings.m_videoItemSeparator,m_strSet);
521 XMLUtils::GetAdditiveString(movie,"studio",g_advancedSettings.m_videoItemSeparator,m_strStudio);
523 node = movie->FirstChildElement("artist");
526 const TiXmlNode* pNode = node->FirstChild("name");
527 const char* pValue=NULL;
528 if (pNode && pNode->FirstChild())
529 pValue = pNode->FirstChild()->Value();
530 else if (node->FirstChild())
531 pValue = node->FirstChild()->Value();
534 const char* clear=node->Attribute("clear");
535 if (m_strArtist.IsEmpty() || (clear && stricmp(clear,"true")==0))
536 m_strArtist += pValue;
538 m_strArtist += g_advancedSettings.m_videoItemSeparator + pValue;
540 node = node->NextSiblingElement("artist");
543 m_streamDetails.Reset();
544 node = movie->FirstChildElement("fileinfo");
547 // Try to pull from fileinfo/streamdetails/[video|audio|subtitle]
548 const TiXmlNode *nodeStreamDetails = node->FirstChild("streamdetails");
549 if (nodeStreamDetails)
551 const TiXmlNode *nodeDetail = NULL;
552 while ((nodeDetail = nodeStreamDetails->IterateChildren("audio", nodeDetail)))
554 CStreamDetailAudio *p = new CStreamDetailAudio();
555 XMLUtils::GetString(nodeDetail, "codec", p->m_strCodec);
556 XMLUtils::GetString(nodeDetail, "language", p->m_strLanguage);
557 XMLUtils::GetInt(nodeDetail, "channels", p->m_iChannels);
558 p->m_strCodec.MakeLower();
559 p->m_strLanguage.MakeLower();
560 m_streamDetails.AddStream(p);
563 while ((nodeDetail = nodeStreamDetails->IterateChildren("video", nodeDetail)))
565 CStreamDetailVideo *p = new CStreamDetailVideo();
566 XMLUtils::GetString(nodeDetail, "codec", p->m_strCodec);
567 XMLUtils::GetFloat(nodeDetail, "aspect", p->m_fAspect);
568 XMLUtils::GetInt(nodeDetail, "width", p->m_iWidth);
569 XMLUtils::GetInt(nodeDetail, "height", p->m_iHeight);
570 XMLUtils::GetInt(nodeDetail, "durationinseconds", p->m_iDuration);
571 p->m_strCodec.MakeLower();
572 m_streamDetails.AddStream(p);
575 while ((nodeDetail = nodeStreamDetails->IterateChildren("subtitle", nodeDetail)))
577 CStreamDetailSubtitle *p = new CStreamDetailSubtitle();
578 XMLUtils::GetString(nodeDetail, "language", p->m_strLanguage);
579 p->m_strLanguage.MakeLower();
580 m_streamDetails.AddStream(p);
583 m_streamDetails.DetermineBestStreams();
586 const TiXmlElement *epguide = movie->FirstChildElement("episodeguide");
589 // DEPRECIATE ME - support for old XML-encoded <episodeguide> blocks.
590 if (epguide->FirstChild() && strnicmp("<episodeguide", epguide->FirstChild()->Value(), 13) == 0)
591 m_strEpisodeGuide = epguide->FirstChild()->Value();
596 m_strEpisodeGuide = stream.str();
601 const TiXmlElement *fanart = movie->FirstChildElement("fanart");
604 m_fanart.m_xml << *fanart;
609 bool CVideoInfoTag::HasStreamDetails() const
611 return m_streamDetails.HasItems();
614 bool CVideoInfoTag::IsEmpty() const
616 return (m_strTitle.IsEmpty() &&
617 m_strFile.IsEmpty() &&
618 m_strPath.IsEmpty());