Merge branch 'single_query_playcounts'
[vuplus_xbmc] / xbmc / video / VideoInfoTag.cpp
1 /*
2  *      Copyright (C) 2005-2008 Team XBMC
3  *      http://www.xbmc.org
4  *
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)
8  *  any later version.
9  *
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.
14  *
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
19  *
20  */
21
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"
30
31 #include <sstream>
32
33 using namespace std;
34
35 void CVideoInfoTag::Reset()
36 {
37   m_strDirector = "";
38   m_strWritingCredits = "";
39   m_strGenre = "";
40   m_strCountry = "";
41   m_strTagLine = "";
42   m_strPlotOutline = "";
43   m_strPlot = "";
44   m_strPictureURL.Clear();
45   m_strTitle = "";
46   m_strOriginalTitle = "";
47   m_strSortTitle = "";
48   m_strVotes = "";
49   m_cast.clear();
50   m_strSet = "";
51   m_strFile = "";
52   m_strPath = "";
53   m_strIMDBNumber = "";
54   m_strMPAARating = "";
55   m_strPremiered= "";
56   m_strStatus= "";
57   m_strProductionCode= "";
58   m_strFirstAired= "";
59   m_strStudio = "";
60   m_strAlbum = "";
61   m_strArtist = "";
62   m_strTrailer = "";
63   m_iTop250 = 0;
64   m_iYear = 0;
65   m_iSeason = -1;
66   m_iEpisode = -1;
67   m_iSpecialSortSeason = -1;
68   m_iSpecialSortEpisode = -1;
69   m_fRating = 0.0f;
70   m_iDbId = -1;
71   m_iFileId = -1;
72   m_iBookmarkId = -1;
73   m_iTrack = -1;
74   m_fanart.m_xml = "";
75   m_strRuntime = "";
76   m_lastPlayed = "";
77   m_strShowLink = "";
78   m_streamDetails.Reset();
79   m_playCount = 0;
80   m_fEpBookmark = 0;
81   m_basePath = "";
82   m_parentPathID = -1;
83 }
84
85 bool CVideoInfoTag::Save(TiXmlNode *node, const CStdString &tag, bool savePathInfo)
86 {
87   if (!node) return false;
88
89   // we start with a <tag> tag
90   TiXmlElement movieElement(tag.c_str());
91   TiXmlNode *movie = node->InsertEndChild(movieElement);
92
93   if (!movie) return false;
94
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")
105   {
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);
110   }
111   if (tag == "musicvideo")
112   {
113     XMLUtils::SetInt(movie, "track", m_iTrack);
114     XMLUtils::SetString(movie, "album", m_strAlbum);
115   }
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())
122   {
123     TiXmlDocument doc;
124     doc.Parse(m_strPictureURL.m_xml);
125     const TiXmlNode* thumb = doc.FirstChild("thumb");
126     while (thumb)
127     {
128       movie->InsertEndChild(*thumb);
129       thumb = thumb->NextSibling("thumb");
130     }
131   }
132   if (m_fanart.m_xml.size())
133   {
134     TiXmlDocument doc;
135     doc.Parse(m_fanart.m_xml);
136     movie->InsertEndChild(*doc.RootElement());
137   }
138   XMLUtils::SetString(movie, "mpaa", m_strMPAARating);
139   XMLUtils::SetInt(movie, "playcount", m_playCount);
140   XMLUtils::SetString(movie, "lastplayed", m_lastPlayed);
141   if (savePathInfo)
142   {
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);
147   }
148   if (!m_strEpisodeGuide.IsEmpty())
149   {
150     TiXmlDocument doc;
151     doc.Parse(m_strEpisodeGuide);
152     if (doc.RootElement())
153       movie->InsertEndChild(*doc.RootElement());
154     else
155       XMLUtils::SetString(movie, "episodeguide", m_strEpisodeGuide);
156   }
157
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);
176
177   if (m_streamDetails.HasItems())
178   {
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++)
183     {
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);
191     }
192     for (int iStream=1; iStream<=m_streamDetails.GetAudioStreamCount(); iStream++)
193     {
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);
199     }
200     for (int iStream=1; iStream<=m_streamDetails.GetSubtitleStreamCount(); iStream++)
201     {
202       TiXmlElement stream("subtitle");
203       XMLUtils::SetString(&stream, "language", m_streamDetails.GetSubtitleLanguage(iStream));
204       streamdetails.InsertEndChild(stream);
205     }
206     fileinfo.InsertEndChild(streamdetails);
207     movie->InsertEndChild(fileinfo);
208   }  /* if has stream details */
209
210   // cast
211   for (iCast it = m_cast.begin(); it != m_cast.end(); ++it)
212   {
213     // add a <actor> tag
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);
228   }
229   XMLUtils::SetAdditiveString(movie, "artist",
230                          g_advancedSettings.m_videoItemSeparator, m_strArtist);
231   XMLUtils::SetAdditiveString(movie, "showlink",
232                          g_advancedSettings.m_videoItemSeparator, m_strShowLink);
233
234   return true;
235 }
236
237 bool CVideoInfoTag::Load(const TiXmlElement *movie, bool chained /* = false */)
238 {
239   if (!movie) return false;
240
241   // reset our details if we aren't chained.
242   if (!chained) Reset();
243
244   ParseNative(movie);
245
246   return true;
247 }
248
249 void CVideoInfoTag::Archive(CArchive& ar)
250 {
251   if (ar.IsStoring())
252   {
253     ar << m_strDirector;
254     ar << m_strWritingCredits;
255     ar << m_strGenre;
256     ar << m_strCountry;
257     ar << m_strTagLine;
258     ar << m_strPlotOutline;
259     ar << m_strPlot;
260     ar << m_strPictureURL.m_spoof;
261     ar << m_strPictureURL.m_xml;
262     ar << m_fanart.m_xml;
263     ar << m_strTitle;
264     ar << m_strSortTitle;
265     ar << m_strVotes;
266     ar << m_strStudio;
267     ar << m_strTrailer;
268     ar << (int)m_cast.size();
269     for (unsigned int i=0;i<m_cast.size();++i)
270     {
271       ar << m_cast[i].strName;
272       ar << m_cast[i].strRole;
273       ar << m_cast[i].thumbUrl.m_xml;
274     }
275
276     ar << m_strSet;
277     ar << m_strRuntime;
278     ar << m_strFile;
279     ar << m_strPath;
280     ar << m_strIMDBNumber;
281     ar << m_strMPAARating;
282     ar << m_strFileNameAndPath;
283     ar << m_strOriginalTitle;
284     ar << m_strEpisodeGuide;
285     ar << m_strPremiered;
286     ar << m_strStatus;
287     ar << m_strProductionCode;
288     ar << m_strFirstAired;
289     ar << m_strShowTitle;
290     ar << m_strAlbum;
291     ar << m_strArtist;
292     ar << m_playCount;
293     ar << m_lastPlayed;
294     ar << m_iTop250;
295     ar << m_iYear;
296     ar << m_iSeason;
297     ar << m_iEpisode;
298     ar << m_fRating;
299     ar << m_iDbId;
300     ar << m_iFileId;
301     ar << m_iSpecialSortSeason;
302     ar << m_iSpecialSortEpisode;
303     ar << m_iBookmarkId;
304     ar << m_iTrack;
305     ar << dynamic_cast<IArchivable&>(m_streamDetails);
306     ar << m_strShowLink;
307     ar << m_fEpBookmark;
308     ar << m_basePath;
309     ar << m_parentPathID;
310   }
311   else
312   {
313     ar >> m_strDirector;
314     ar >> m_strWritingCredits;
315     ar >> m_strGenre;
316     ar >> m_strCountry;
317     ar >> m_strTagLine;
318     ar >> m_strPlotOutline;
319     ar >> m_strPlot;
320     ar >> m_strPictureURL.m_spoof;
321     ar >> m_strPictureURL.m_xml;
322     m_strPictureURL.Parse();
323     ar >> m_fanart.m_xml;
324     m_fanart.Unpack();
325     ar >> m_strTitle;
326     ar >> m_strSortTitle;
327     ar >> m_strVotes;
328     ar >> m_strStudio;
329     ar >> m_strTrailer;
330     int iCastSize;
331     ar >> iCastSize;
332     for (int i=0;i<iCastSize;++i)
333     {
334       SActorInfo info;
335       ar >> info.strName;
336       ar >> info.strRole;
337       CStdString strXml;
338       ar >> strXml;
339       info.thumbUrl.ParseString(strXml);
340       m_cast.push_back(info);
341     }
342
343     ar >> m_strSet;
344     ar >> m_strRuntime;
345     ar >> m_strFile;
346     ar >> m_strPath;
347     ar >> m_strIMDBNumber;
348     ar >> m_strMPAARating;
349     ar >> m_strFileNameAndPath;
350     ar >> m_strOriginalTitle;
351     ar >> m_strEpisodeGuide;
352     ar >> m_strPremiered;
353     ar >> m_strStatus;
354     ar >> m_strProductionCode;
355     ar >> m_strFirstAired;
356     ar >> m_strShowTitle;
357     ar >> m_strAlbum;
358     ar >> m_strArtist;
359     ar >> m_playCount;
360     ar >> m_lastPlayed;
361     ar >> m_iTop250;
362     ar >> m_iYear;
363     ar >> m_iSeason;
364     ar >> m_iEpisode;
365     ar >> m_fRating;
366     ar >> m_iDbId;
367     ar >> m_iFileId;
368     ar >> m_iSpecialSortSeason;
369     ar >> m_iSpecialSortEpisode;
370     ar >> m_iBookmarkId;
371     ar >> m_iTrack;
372     ar >> dynamic_cast<IArchivable&>(m_streamDetails);
373     ar >> m_strShowLink;
374     ar >> m_fEpBookmark;
375     ar >> m_basePath;
376     ar >> m_parentPathID;
377   }
378 }
379
380 void CVideoInfoTag::Serialize(CVariant& value)
381 {
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)
395   {
396     CVariant actor;
397     actor["name"] = m_cast[i].strName;
398     actor["role"] = m_cast[i].strRole;
399     value["cast"].push_back(actor);
400   }
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"]);
430 }
431
432 const CStdString CVideoInfoTag::GetCast(bool bIncludeRole /*= false*/) const
433 {
434   CStdString strLabel;
435   for (iCast it = m_cast.begin(); it != m_cast.end(); ++it)
436   {
437     CStdString character;
438     if (it->strRole.IsEmpty() || !bIncludeRole)
439       character.Format("%s\n", it->strName.c_str());
440     else
441       character.Format("%s %s %s\n", it->strName.c_str(), g_localizeStrings.Get(20347).c_str(), it->strRole.c_str());
442     strLabel += character;
443   }
444   return strLabel.TrimRight("\n");
445 }
446
447 void CVideoInfoTag::ParseNative(const TiXmlElement* movie)
448 {
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);
454   int max_value = 10;
455   const TiXmlElement* rElement = movie->FirstChildElement("rating");
456   if (rElement && (rElement->QueryIntAttribute("max", &max_value) == TIXML_SUCCESS) && max_value>=1)
457   {
458     m_fRating = m_fRating / max_value * 10; // Normalise the Movie Rating to between 1 and 10
459   }
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);
467   int after=0;
468   XMLUtils::GetInt(movie, "displayafterseason",after);
469   if (after > 0)
470   {
471     m_iSpecialSortSeason = after;
472     m_iSpecialSortEpisode = 0x1000; // should be more than any realistic episode number
473   }
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);
493
494   const TiXmlElement* thumb = movie->FirstChildElement("thumb");
495   while (thumb)
496   {
497     m_strPictureURL.ParseElement(thumb);
498     thumb = thumb->NextSiblingElement("thumb");
499   }
500
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);
506
507   // cast
508   const TiXmlElement* node = movie->FirstChildElement("actor");
509   while (node)
510   {
511     const TiXmlNode *actor = node->FirstChild("name");
512     if (actor && actor->FirstChild())
513     {
514       SActorInfo info;
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");
520       while (thumb)
521       {
522         info.thumbUrl.ParseElement(thumb);
523         thumb = thumb->NextSiblingElement("thumb");
524       }
525       const char* clear=node->Attribute("clear");
526       if (clear && stricmp(clear,"true"))
527         m_cast.clear();
528       m_cast.push_back(info);
529     }
530     node = node->NextSiblingElement("actor");
531   }
532   XMLUtils::GetAdditiveString(movie,"set",g_advancedSettings.m_videoItemSeparator,m_strSet);
533   XMLUtils::GetAdditiveString(movie,"studio",g_advancedSettings.m_videoItemSeparator,m_strStudio);
534   // artists
535   node = movie->FirstChildElement("artist");
536   while (node)
537   {
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();
544     if (pValue)
545     {
546       const char* clear=node->Attribute("clear");
547       if (m_strArtist.IsEmpty() || (clear && stricmp(clear,"true")==0))
548         m_strArtist += pValue;
549       else
550         m_strArtist += g_advancedSettings.m_videoItemSeparator + pValue;
551     }
552     node = node->NextSiblingElement("artist");
553   }
554
555   m_streamDetails.Reset();
556   node = movie->FirstChildElement("fileinfo");
557   if (node)
558   {
559     // Try to pull from fileinfo/streamdetails/[video|audio|subtitle]
560     const TiXmlNode *nodeStreamDetails = node->FirstChild("streamdetails");
561     if (nodeStreamDetails)
562     {
563       const TiXmlNode *nodeDetail = NULL;
564       while ((nodeDetail = nodeStreamDetails->IterateChildren("audio", nodeDetail)))
565       {
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);
573       }
574       nodeDetail = NULL;
575       while ((nodeDetail = nodeStreamDetails->IterateChildren("video", nodeDetail)))
576       {
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);
585       }
586       nodeDetail = NULL;
587       while ((nodeDetail = nodeStreamDetails->IterateChildren("subtitle", nodeDetail)))
588       {
589         CStreamDetailSubtitle *p = new CStreamDetailSubtitle();
590         XMLUtils::GetString(nodeDetail, "language", p->m_strLanguage);
591         p->m_strLanguage.MakeLower();
592         m_streamDetails.AddStream(p);
593       }
594     }
595     m_streamDetails.DetermineBestStreams();
596   }  /* if fileinfo */
597
598   const TiXmlElement *epguide = movie->FirstChildElement("episodeguide");
599   if (epguide)
600   {
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();
604     else
605     {
606       stringstream stream;
607       stream << *epguide;
608       m_strEpisodeGuide = stream.str();
609     }
610   }
611
612   // fanart
613   const TiXmlElement *fanart = movie->FirstChildElement("fanart");
614   if (fanart)
615   {
616     m_fanart.m_xml << *fanart;
617     m_fanart.Unpack();
618   }
619 }
620
621 bool CVideoInfoTag::HasStreamDetails() const
622 {
623   return m_streamDetails.HasItems();
624 }
625
626 bool CVideoInfoTag::IsEmpty() const
627 {
628   return (m_strTitle.IsEmpty() &&
629           m_strFile.IsEmpty() &&
630           m_strPath.IsEmpty());
631 }