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["episodeguide"] = m_strEpisodeGuide;
410 value["premiered"] = m_strPremiered;
411 value["status"] = m_strStatus;
412 value["productioncode"] = m_strProductionCode;
413 value["firstaired"] = m_strFirstAired;
414 value["showtitle"] = m_strShowTitle;
415 value["album"] = m_strAlbum;
416 value["artist"] = m_strArtist;
417 value["playcount"] = m_playCount;
418 value["lastPlayed"] = m_lastPlayed;
419 value["top250"] = m_iTop250;
420 value["year"] = m_iYear;
421 value["season"] = m_iSeason;
422 value["episode"] = m_iEpisode;
423 value["rating"] = m_fRating;
424 value["dbid"] = m_iDbId;
425 value["fileid"] = m_iFileId;
426 value["track"] = m_iTrack;
427 value["showlink"] = m_strShowLink;
428 m_streamDetails.Serialize(value["streamDetails"]);
431 const CStdString CVideoInfoTag::GetCast(bool bIncludeRole /*= false*/) const
434 for (iCast it = m_cast.begin(); it != m_cast.end(); ++it)
436 CStdString character;
437 if (it->strRole.IsEmpty() || !bIncludeRole)
438 character.Format("%s\n", it->strName.c_str());
440 character.Format("%s %s %s\n", it->strName.c_str(), g_localizeStrings.Get(20347).c_str(), it->strRole.c_str());
441 strLabel += character;
443 return strLabel.TrimRight("\n");
446 void CVideoInfoTag::ParseNative(const TiXmlElement* movie)
448 XMLUtils::GetString(movie, "title", m_strTitle);
449 XMLUtils::GetString(movie, "originaltitle", m_strOriginalTitle);
450 XMLUtils::GetString(movie, "sorttitle", m_strSortTitle);
451 XMLUtils::GetFloat(movie, "rating", m_fRating);
452 XMLUtils::GetFloat(movie, "epbookmark", m_fEpBookmark);
454 const TiXmlElement* rElement = movie->FirstChildElement("rating");
455 if (rElement && (rElement->QueryIntAttribute("max", &max_value) == TIXML_SUCCESS) && max_value>=1)
457 m_fRating = m_fRating / max_value * 10; // Normalise the Movie Rating to between 1 and 10
459 XMLUtils::GetInt(movie, "year", m_iYear);
460 XMLUtils::GetInt(movie, "top250", m_iTop250);
461 XMLUtils::GetInt(movie, "season", m_iSeason);
462 XMLUtils::GetInt(movie, "episode", m_iEpisode);
463 XMLUtils::GetInt(movie, "track", m_iTrack);
464 XMLUtils::GetInt(movie, "displayseason", m_iSpecialSortSeason);
465 XMLUtils::GetInt(movie, "displayepisode", m_iSpecialSortEpisode);
467 XMLUtils::GetInt(movie, "displayafterseason",after);
470 m_iSpecialSortSeason = after;
471 m_iSpecialSortEpisode = 0x1000; // should be more than any realistic episode number
473 XMLUtils::GetString(movie, "votes", m_strVotes);
474 XMLUtils::GetString(movie, "outline", m_strPlotOutline);
475 XMLUtils::GetString(movie, "plot", m_strPlot);
476 XMLUtils::GetString(movie, "tagline", m_strTagLine);
477 XMLUtils::GetString(movie, "runtime", m_strRuntime);
478 XMLUtils::GetString(movie, "mpaa", m_strMPAARating);
479 XMLUtils::GetInt(movie, "playcount", m_playCount);
480 XMLUtils::GetString(movie, "lastplayed", m_lastPlayed);
481 XMLUtils::GetString(movie, "file", m_strFile);
482 XMLUtils::GetString(movie, "path", m_strPath);
483 XMLUtils::GetString(movie, "id", m_strIMDBNumber);
484 XMLUtils::GetString(movie, "filenameandpath", m_strFileNameAndPath);
485 XMLUtils::GetString(movie, "premiered", m_strPremiered);
486 XMLUtils::GetString(movie, "status", m_strStatus);
487 XMLUtils::GetString(movie, "code", m_strProductionCode);
488 XMLUtils::GetString(movie, "aired", m_strFirstAired);
489 XMLUtils::GetString(movie, "album", m_strAlbum);
490 XMLUtils::GetString(movie, "trailer", m_strTrailer);
491 XMLUtils::GetString(movie, "basepath", m_basePath);
493 const TiXmlElement* thumb = movie->FirstChildElement("thumb");
496 m_strPictureURL.ParseElement(thumb);
497 thumb = thumb->NextSiblingElement("thumb");
500 XMLUtils::GetAdditiveString(movie,"genre",g_advancedSettings.m_videoItemSeparator,m_strGenre);
501 XMLUtils::GetAdditiveString(movie,"country",g_advancedSettings.m_videoItemSeparator,m_strCountry);
502 XMLUtils::GetAdditiveString(movie,"credits",g_advancedSettings.m_videoItemSeparator,m_strWritingCredits);
503 XMLUtils::GetAdditiveString(movie,"director",g_advancedSettings.m_videoItemSeparator,m_strDirector);
504 XMLUtils::GetAdditiveString(movie,"showlink",g_advancedSettings.m_videoItemSeparator,m_strShowLink);
507 const TiXmlElement* node = movie->FirstChildElement("actor");
510 const TiXmlNode *actor = node->FirstChild("name");
511 if (actor && actor->FirstChild())
514 info.strName = actor->FirstChild()->Value();
515 const TiXmlNode *roleNode = node->FirstChild("role");
516 if (roleNode && roleNode->FirstChild())
517 info.strRole = roleNode->FirstChild()->Value();
518 const TiXmlElement* thumb = node->FirstChildElement("thumb");
521 info.thumbUrl.ParseElement(thumb);
522 thumb = thumb->NextSiblingElement("thumb");
524 const char* clear=node->Attribute("clear");
525 if (clear && stricmp(clear,"true"))
527 m_cast.push_back(info);
529 node = node->NextSiblingElement("actor");
531 XMLUtils::GetAdditiveString(movie,"set",g_advancedSettings.m_videoItemSeparator,m_strSet);
532 XMLUtils::GetAdditiveString(movie,"studio",g_advancedSettings.m_videoItemSeparator,m_strStudio);
534 node = movie->FirstChildElement("artist");
537 const TiXmlNode* pNode = node->FirstChild("name");
538 const char* pValue=NULL;
539 if (pNode && pNode->FirstChild())
540 pValue = pNode->FirstChild()->Value();
541 else if (node->FirstChild())
542 pValue = node->FirstChild()->Value();
545 const char* clear=node->Attribute("clear");
546 if (m_strArtist.IsEmpty() || (clear && stricmp(clear,"true")==0))
547 m_strArtist += pValue;
549 m_strArtist += g_advancedSettings.m_videoItemSeparator + pValue;
551 node = node->NextSiblingElement("artist");
554 m_streamDetails.Reset();
555 node = movie->FirstChildElement("fileinfo");
558 // Try to pull from fileinfo/streamdetails/[video|audio|subtitle]
559 const TiXmlNode *nodeStreamDetails = node->FirstChild("streamdetails");
560 if (nodeStreamDetails)
562 const TiXmlNode *nodeDetail = NULL;
563 while ((nodeDetail = nodeStreamDetails->IterateChildren("audio", nodeDetail)))
565 CStreamDetailAudio *p = new CStreamDetailAudio();
566 XMLUtils::GetString(nodeDetail, "codec", p->m_strCodec);
567 XMLUtils::GetString(nodeDetail, "language", p->m_strLanguage);
568 XMLUtils::GetInt(nodeDetail, "channels", p->m_iChannels);
569 p->m_strCodec.MakeLower();
570 p->m_strLanguage.MakeLower();
571 m_streamDetails.AddStream(p);
574 while ((nodeDetail = nodeStreamDetails->IterateChildren("video", nodeDetail)))
576 CStreamDetailVideo *p = new CStreamDetailVideo();
577 XMLUtils::GetString(nodeDetail, "codec", p->m_strCodec);
578 XMLUtils::GetFloat(nodeDetail, "aspect", p->m_fAspect);
579 XMLUtils::GetInt(nodeDetail, "width", p->m_iWidth);
580 XMLUtils::GetInt(nodeDetail, "height", p->m_iHeight);
581 XMLUtils::GetInt(nodeDetail, "durationinseconds", p->m_iDuration);
582 p->m_strCodec.MakeLower();
583 m_streamDetails.AddStream(p);
586 while ((nodeDetail = nodeStreamDetails->IterateChildren("subtitle", nodeDetail)))
588 CStreamDetailSubtitle *p = new CStreamDetailSubtitle();
589 XMLUtils::GetString(nodeDetail, "language", p->m_strLanguage);
590 p->m_strLanguage.MakeLower();
591 m_streamDetails.AddStream(p);
594 m_streamDetails.DetermineBestStreams();
597 const TiXmlElement *epguide = movie->FirstChildElement("episodeguide");
600 // DEPRECIATE ME - support for old XML-encoded <episodeguide> blocks.
601 if (epguide->FirstChild() && strnicmp("<episodeguide", epguide->FirstChild()->Value(), 13) == 0)
602 m_strEpisodeGuide = epguide->FirstChild()->Value();
607 m_strEpisodeGuide = stream.str();
612 const TiXmlElement *fanart = movie->FirstChildElement("fanart");
615 m_fanart.m_xml << *fanart;
620 bool CVideoInfoTag::HasStreamDetails() const
622 return m_streamDetails.HasItems();
625 bool CVideoInfoTag::IsEmpty() const
627 return (m_strTitle.IsEmpty() &&
628 m_strFile.IsEmpty() &&
629 m_strPath.IsEmpty());