Merge pull request #1129 from jmarshallnz/remove_smb_auth_details_in_add_source
[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/StringUtils.h"
29 #include "utils/Variant.h"
30 #include "utils/CharsetConverter.h"
31 #include "TextureCache.h"
32 #include "filesystem/File.h"
33
34 #include <sstream>
35
36 using namespace std;
37
38 void CVideoInfoTag::Reset()
39 {
40   m_director.clear();
41   m_writingCredits.clear();
42   m_genre.clear();
43   m_country.clear();
44   m_strTagLine.clear();
45   m_strPlotOutline.clear();
46   m_strPlot.clear();
47   m_strPictureURL.Clear();
48   m_strTitle.clear();
49   m_strShowTitle.clear();
50   m_strOriginalTitle.clear();
51   m_strSortTitle.clear();
52   m_strVotes.clear();
53   m_cast.clear();
54   m_set.clear();
55   m_setId.clear();
56   m_tags.clear();
57   m_strFile.clear();
58   m_strPath.clear();
59   m_strIMDBNumber.clear();
60   m_strMPAARating.clear();
61   m_strFileNameAndPath.clear();
62   m_premiered.Reset();
63   m_strStatus.clear();
64   m_strProductionCode.clear();
65   m_firstAired.Reset();
66   m_studio.clear();
67   m_strAlbum.clear();
68   m_artist.clear();
69   m_strTrailer.clear();
70   m_iTop250 = 0;
71   m_iYear = 0;
72   m_iSeason = -1;
73   m_iEpisode = -1;
74   m_iSpecialSortSeason = -1;
75   m_iSpecialSortEpisode = -1;
76   m_fRating = 0.0f;
77   m_iDbId = -1;
78   m_iFileId = -1;
79   m_iBookmarkId = -1;
80   m_iTrack = -1;
81   m_fanart.m_xml.clear();
82   m_strRuntime.clear();
83   m_lastPlayed.Reset();
84   m_showLink.clear();
85   m_streamDetails.Reset();
86   m_playCount = 0;
87   m_fEpBookmark = 0;
88   m_basePath.clear();
89   m_parentPathID = -1;
90   m_resumePoint.Reset();
91   m_resumePoint.type = CBookmark::RESUME;
92   m_iIdShow = -1;
93   m_iIdSeason = -1;
94   m_strShowPath.clear();
95   m_dateAdded.Reset();
96   m_type.clear();
97 }
98
99 bool CVideoInfoTag::Save(TiXmlNode *node, const CStdString &tag, bool savePathInfo, const TiXmlElement *additionalNode)
100 {
101   if (!node) return false;
102
103   // we start with a <tag> tag
104   TiXmlElement movieElement(tag.c_str());
105   TiXmlNode *movie = node->InsertEndChild(movieElement);
106
107   if (!movie) return false;
108
109   XMLUtils::SetString(movie, "title", m_strTitle);
110   if (!m_strOriginalTitle.IsEmpty())
111     XMLUtils::SetString(movie, "originaltitle", m_strOriginalTitle);
112   if (!m_strShowTitle.IsEmpty())
113     XMLUtils::SetString(movie, "showtitle", m_strShowTitle);
114   if (!m_strSortTitle.IsEmpty())
115     XMLUtils::SetString(movie, "sorttitle", m_strSortTitle);
116   XMLUtils::SetFloat(movie, "rating", m_fRating);
117   XMLUtils::SetFloat(movie, "epbookmark", m_fEpBookmark);
118   XMLUtils::SetInt(movie, "year", m_iYear);
119   XMLUtils::SetInt(movie, "top250", m_iTop250);
120   if (tag == "episodedetails" || tag == "tvshow")
121   {
122     XMLUtils::SetInt(movie, "season", m_iSeason);
123     XMLUtils::SetInt(movie, "episode", m_iEpisode);
124     XMLUtils::SetInt(movie, "displayseason",m_iSpecialSortSeason);
125     XMLUtils::SetInt(movie, "displayepisode",m_iSpecialSortEpisode);
126   }
127   if (tag == "musicvideo")
128   {
129     XMLUtils::SetInt(movie, "track", m_iTrack);
130     XMLUtils::SetString(movie, "album", m_strAlbum);
131   }
132   XMLUtils::SetString(movie, "votes", m_strVotes);
133   XMLUtils::SetString(movie, "outline", m_strPlotOutline);
134   XMLUtils::SetString(movie, "plot", m_strPlot);
135   XMLUtils::SetString(movie, "tagline", m_strTagLine);
136   XMLUtils::SetString(movie, "runtime", m_strRuntime);
137   if (!m_strPictureURL.m_xml.empty())
138   {
139     CXBMCTinyXML doc;
140     doc.Parse(m_strPictureURL.m_xml);
141     const TiXmlNode* thumb = doc.FirstChild("thumb");
142     while (thumb)
143     {
144       movie->InsertEndChild(*thumb);
145       thumb = thumb->NextSibling("thumb");
146     }
147   }
148   if (m_fanart.m_xml.size())
149   {
150     CXBMCTinyXML doc;
151     doc.Parse(m_fanart.m_xml);
152     movie->InsertEndChild(*doc.RootElement());
153   }
154   XMLUtils::SetString(movie, "mpaa", m_strMPAARating);
155   XMLUtils::SetInt(movie, "playcount", m_playCount);
156   XMLUtils::SetDate(movie, "lastplayed", m_lastPlayed);
157   if (savePathInfo)
158   {
159     XMLUtils::SetString(movie, "file", m_strFile);
160     XMLUtils::SetString(movie, "path", m_strPath);
161     XMLUtils::SetString(movie, "filenameandpath", m_strFileNameAndPath);
162     XMLUtils::SetString(movie, "basepath", m_basePath);
163   }
164   if (!m_strEpisodeGuide.IsEmpty())
165   {
166     CXBMCTinyXML doc;
167     doc.Parse(m_strEpisodeGuide);
168     if (doc.RootElement())
169       movie->InsertEndChild(*doc.RootElement());
170     else
171       XMLUtils::SetString(movie, "episodeguide", m_strEpisodeGuide);
172   }
173
174   XMLUtils::SetString(movie, "id", m_strIMDBNumber);
175   XMLUtils::SetStringArray(movie, "genre", m_genre);
176   XMLUtils::SetStringArray(movie, "country", m_country);
177   XMLUtils::SetStringArray(movie, "set", m_set);
178   XMLUtils::SetStringArray(movie, "tag", m_tags);
179   XMLUtils::SetStringArray(movie, "credits", m_writingCredits);
180   XMLUtils::SetStringArray(movie, "director", m_director);
181   XMLUtils::SetDate(movie, "premiered", m_premiered);
182   XMLUtils::SetString(movie, "status", m_strStatus);
183   XMLUtils::SetString(movie, "code", m_strProductionCode);
184   XMLUtils::SetDate(movie, "aired", m_firstAired);
185   XMLUtils::SetStringArray(movie, "studio", m_studio);
186   XMLUtils::SetString(movie, "trailer", m_strTrailer);
187
188   if (m_streamDetails.HasItems())
189   {
190     // it goes fileinfo/streamdetails/[video|audio|subtitle]
191     TiXmlElement fileinfo("fileinfo");
192     TiXmlElement streamdetails("streamdetails");
193     for (int iStream=1; iStream<=m_streamDetails.GetVideoStreamCount(); iStream++)
194     {
195       TiXmlElement stream("video");
196       XMLUtils::SetString(&stream, "codec", m_streamDetails.GetVideoCodec(iStream));
197       XMLUtils::SetFloat(&stream, "aspect", m_streamDetails.GetVideoAspect(iStream));
198       XMLUtils::SetInt(&stream, "width", m_streamDetails.GetVideoWidth(iStream));
199       XMLUtils::SetInt(&stream, "height", m_streamDetails.GetVideoHeight(iStream));
200       XMLUtils::SetInt(&stream, "durationinseconds", m_streamDetails.GetVideoDuration(iStream));
201       streamdetails.InsertEndChild(stream);
202     }
203     for (int iStream=1; iStream<=m_streamDetails.GetAudioStreamCount(); iStream++)
204     {
205       TiXmlElement stream("audio");
206       XMLUtils::SetString(&stream, "codec", m_streamDetails.GetAudioCodec(iStream));
207       XMLUtils::SetString(&stream, "language", m_streamDetails.GetAudioLanguage(iStream));
208       XMLUtils::SetInt(&stream, "channels", m_streamDetails.GetAudioChannels(iStream));
209       streamdetails.InsertEndChild(stream);
210     }
211     for (int iStream=1; iStream<=m_streamDetails.GetSubtitleStreamCount(); iStream++)
212     {
213       TiXmlElement stream("subtitle");
214       XMLUtils::SetString(&stream, "language", m_streamDetails.GetSubtitleLanguage(iStream));
215       streamdetails.InsertEndChild(stream);
216     }
217     fileinfo.InsertEndChild(streamdetails);
218     movie->InsertEndChild(fileinfo);
219   }  /* if has stream details */
220
221   // cast
222   for (iCast it = m_cast.begin(); it != m_cast.end(); ++it)
223   {
224     // add a <actor> tag
225     TiXmlElement cast("actor");
226     TiXmlNode *node = movie->InsertEndChild(cast);
227     TiXmlElement actor("name");
228     TiXmlNode *actorNode = node->InsertEndChild(actor);
229     TiXmlText name(it->strName);
230     actorNode->InsertEndChild(name);
231     TiXmlElement role("role");
232     TiXmlNode *roleNode = node->InsertEndChild(role);
233     TiXmlText character(it->strRole);
234     roleNode->InsertEndChild(character);
235     TiXmlElement thumb("thumb");
236     TiXmlNode *thumbNode = node->InsertEndChild(thumb);
237     TiXmlText th(it->thumbUrl.GetFirstThumb().m_url);
238     thumbNode->InsertEndChild(th);
239   }
240   XMLUtils::SetStringArray(movie, "artist", m_artist);
241   XMLUtils::SetStringArray(movie, "showlink", m_showLink);
242  
243   TiXmlElement resume("resume");
244   XMLUtils::SetFloat(&resume, "position", (float)m_resumePoint.timeInSeconds);
245   XMLUtils::SetFloat(&resume, "total", (float)m_resumePoint.totalTimeInSeconds);
246   movie->InsertEndChild(resume);
247
248   XMLUtils::SetString(movie, "dateadded", m_dateAdded.GetAsDBDateTime());
249
250   if (additionalNode)
251     movie->InsertEndChild(*additionalNode);
252
253   return true;
254 }
255
256 bool CVideoInfoTag::Load(const TiXmlElement *element, bool append, bool prioritise)
257 {
258   if (!element)
259     return false;
260   if (!append)
261     Reset();
262   ParseNative(element, prioritise);
263   return true;
264 }
265
266 void CVideoInfoTag::Archive(CArchive& ar)
267 {
268   if (ar.IsStoring())
269   {
270     ar << m_director;
271     ar << m_writingCredits;
272     ar << m_genre;
273     ar << m_country;
274     ar << m_strTagLine;
275     ar << m_strPlotOutline;
276     ar << m_strPlot;
277     ar << m_strPictureURL.m_spoof;
278     ar << m_strPictureURL.m_xml;
279     ar << m_fanart.m_xml;
280     ar << m_strTitle;
281     ar << m_strSortTitle;
282     ar << m_strVotes;
283     ar << m_studio;
284     ar << m_strTrailer;
285     ar << (int)m_cast.size();
286     for (unsigned int i=0;i<m_cast.size();++i)
287     {
288       ar << m_cast[i].strName;
289       ar << m_cast[i].strRole;
290       ar << m_cast[i].thumb;
291       ar << m_cast[i].thumbUrl.m_xml;
292     }
293
294     ar << m_set;
295     ar << m_setId;
296     ar << m_tags;
297     ar << m_strRuntime;
298     ar << m_strFile;
299     ar << m_strPath;
300     ar << m_strIMDBNumber;
301     ar << m_strMPAARating;
302     ar << m_strFileNameAndPath;
303     ar << m_strOriginalTitle;
304     ar << m_strEpisodeGuide;
305     ar << m_premiered;
306     ar << m_strStatus;
307     ar << m_strProductionCode;
308     ar << m_firstAired;
309     ar << m_strShowTitle;
310     ar << m_strAlbum;
311     ar << m_artist;
312     ar << m_playCount;
313     ar << m_lastPlayed;
314     ar << m_iTop250;
315     ar << m_iYear;
316     ar << m_iSeason;
317     ar << m_iEpisode;
318     ar << m_fRating;
319     ar << m_iDbId;
320     ar << m_iFileId;
321     ar << m_iSpecialSortSeason;
322     ar << m_iSpecialSortEpisode;
323     ar << m_iBookmarkId;
324     ar << m_iTrack;
325     ar << dynamic_cast<IArchivable&>(m_streamDetails);
326     ar << m_showLink;
327     ar << m_fEpBookmark;
328     ar << m_basePath;
329     ar << m_parentPathID;
330     ar << m_resumePoint.timeInSeconds;
331     ar << m_resumePoint.totalTimeInSeconds;
332     ar << m_iIdShow;
333     ar << m_strShowPath;
334     ar << m_dateAdded.GetAsDBDateTime();
335     ar << m_type;
336     ar << m_iIdSeason;
337   }
338   else
339   {
340     ar >> m_director;
341     ar >> m_writingCredits;
342     ar >> m_genre;
343     ar >> m_country;
344     ar >> m_strTagLine;
345     ar >> m_strPlotOutline;
346     ar >> m_strPlot;
347     ar >> m_strPictureURL.m_spoof;
348     ar >> m_strPictureURL.m_xml;
349     m_strPictureURL.Parse();
350     ar >> m_fanart.m_xml;
351     m_fanart.Unpack();
352     ar >> m_strTitle;
353     ar >> m_strSortTitle;
354     ar >> m_strVotes;
355     ar >> m_studio;
356     ar >> m_strTrailer;
357     int iCastSize;
358     ar >> iCastSize;
359     for (int i=0;i<iCastSize;++i)
360     {
361       SActorInfo info;
362       ar >> info.strName;
363       ar >> info.strRole;
364       ar >> info.thumb;
365       CStdString strXml;
366       ar >> strXml;
367       info.thumbUrl.ParseString(strXml);
368       m_cast.push_back(info);
369     }
370
371     ar >> m_set;
372     ar >> m_setId;
373     ar >> m_tags;
374     ar >> m_strRuntime;
375     ar >> m_strFile;
376     ar >> m_strPath;
377     ar >> m_strIMDBNumber;
378     ar >> m_strMPAARating;
379     ar >> m_strFileNameAndPath;
380     ar >> m_strOriginalTitle;
381     ar >> m_strEpisodeGuide;
382     ar >> m_premiered;
383     ar >> m_strStatus;
384     ar >> m_strProductionCode;
385     ar >> m_firstAired;
386     ar >> m_strShowTitle;
387     ar >> m_strAlbum;
388     ar >> m_artist;
389     ar >> m_playCount;
390     ar >> m_lastPlayed;
391     ar >> m_iTop250;
392     ar >> m_iYear;
393     ar >> m_iSeason;
394     ar >> m_iEpisode;
395     ar >> m_fRating;
396     ar >> m_iDbId;
397     ar >> m_iFileId;
398     ar >> m_iSpecialSortSeason;
399     ar >> m_iSpecialSortEpisode;
400     ar >> m_iBookmarkId;
401     ar >> m_iTrack;
402     ar >> dynamic_cast<IArchivable&>(m_streamDetails);
403     ar >> m_showLink;
404     ar >> m_fEpBookmark;
405     ar >> m_basePath;
406     ar >> m_parentPathID;
407     ar >> m_resumePoint.timeInSeconds;
408     ar >> m_resumePoint.totalTimeInSeconds;
409     ar >> m_iIdShow;
410     ar >> m_strShowPath;
411
412     CStdString dateAdded;
413     ar >> dateAdded;
414     m_dateAdded.SetFromDBDateTime(dateAdded);
415     ar >> m_type;
416     ar >> m_iIdSeason;
417   }
418 }
419
420 void CVideoInfoTag::Serialize(CVariant& value)
421 {
422   /* TODO:
423      All the StringUtils::Join() calls can be removed once backwards-compatibility to
424      JSON-RPC v4 can be broken */
425   value["director"] = StringUtils::Join(m_director, " / ");
426   value["writer"] = StringUtils::Join(m_writingCredits, " / ");
427   value["genre"] = StringUtils::Join(m_genre, " / ");
428   value["country"] = StringUtils::Join(m_country, " / ");
429   value["tagline"] = m_strTagLine;
430   value["plotoutline"] = m_strPlotOutline;
431   value["plot"] = m_strPlot;
432   value["title"] = m_strTitle;
433   value["votes"] = m_strVotes;
434   value["studio"] = StringUtils::Join(m_studio, " / ");
435   value["trailer"] = m_strTrailer;
436   value["cast"] = CVariant(CVariant::VariantTypeArray);
437   for (unsigned int i = 0; i < m_cast.size(); ++i)
438   {
439     CVariant actor;
440     actor["name"] = m_cast[i].strName;
441     actor["role"] = m_cast[i].strRole;
442     if (!m_cast[i].thumb.IsEmpty())
443       actor["thumbnail"] = CTextureCache::GetWrappedImageURL(m_cast[i].thumb);
444     value["cast"].push_back(actor);
445   }
446   value["set"] = m_set;
447   value["setid"] = CVariant(CVariant::VariantTypeArray);
448   for (unsigned int i = 0; i < m_setId.size(); i++)
449     value["setid"].push_back(m_setId[i]);
450   value["tags"] = m_tags;
451   value["runtime"] = m_strRuntime;
452   value["file"] = m_strFile;
453   value["path"] = m_strPath;
454   value["imdbnumber"] = m_strIMDBNumber;
455   value["mpaa"] = m_strMPAARating;
456   value["filenameandpath"] = m_strFileNameAndPath;
457   value["originaltitle"] = m_strOriginalTitle;
458   value["sorttitle"] = m_strSortTitle;
459   value["episodeguide"] = m_strEpisodeGuide;
460   value["premiered"] = m_premiered.IsValid() ? m_premiered.GetAsDBDate() : StringUtils::EmptyString;
461   value["status"] = m_strStatus;
462   value["productioncode"] = m_strProductionCode;
463   value["firstaired"] = m_firstAired.IsValid() ? m_firstAired.GetAsDBDate() : StringUtils::EmptyString;
464   value["showtitle"] = m_strShowTitle;
465   value["album"] = m_strAlbum;
466   value["artist"] = StringUtils::Join(m_artist, " / ");
467   value["playcount"] = m_playCount;
468   value["lastplayed"] = m_lastPlayed.IsValid() ? m_lastPlayed.GetAsDBDateTime() : StringUtils::EmptyString;
469   value["top250"] = m_iTop250;
470   value["year"] = m_iYear;
471   value["season"] = m_iSeason;
472   value["episode"] = m_iEpisode;
473   value["rating"] = m_fRating;
474   value["dbid"] = m_iDbId;
475   value["fileid"] = m_iFileId;
476   value["track"] = m_iTrack;
477   value["showlink"] = StringUtils::Join(m_showLink, " / ");
478   m_streamDetails.Serialize(value["streamdetails"]);
479   CVariant resume = CVariant(CVariant::VariantTypeObject);
480   resume["position"] = (float)m_resumePoint.timeInSeconds;
481   resume["total"] = (float)m_resumePoint.totalTimeInSeconds;
482   value["resume"] = resume;
483   value["tvshowid"] = m_iIdShow;
484   value["tvshowpath"] = m_strShowPath;
485   value["dateadded"] = m_dateAdded.IsValid() ? m_dateAdded.GetAsDBDateTime() : StringUtils::EmptyString;
486   value["type"] = m_type;
487   value["seasonid"] = m_iIdSeason;
488 }
489
490 void CVideoInfoTag::ToSortable(SortItem& sortable)
491 {
492   sortable[FieldDirector] = m_director;
493   sortable[FieldWriter] = m_writingCredits;
494   sortable[FieldGenre] = m_genre;
495   sortable[FieldCountry] = m_country;
496   sortable[FieldTagline] = m_strTagLine;
497   sortable[FieldPlotOutline] = m_strPlotOutline;
498   sortable[FieldPlot] = m_strPlot;
499   sortable[FieldTitle] = m_strTitle;
500   sortable[FieldVotes] = m_strVotes;
501   sortable[FieldStudio] = m_studio;
502   sortable[FieldTrailer] = m_strTrailer;
503   sortable[FieldSet] = m_set;
504   sortable[FieldTime] = m_strRuntime;
505   sortable[FieldFilename] = m_strFile;
506   sortable[FieldMPAA] = m_strMPAARating;
507   sortable[FieldPath] = m_strFileNameAndPath;
508   sortable[FieldSortTitle] = m_strSortTitle;
509   sortable[FieldTvShowStatus] = m_strStatus;
510   sortable[FieldProductionCode] = m_strProductionCode;
511   sortable[FieldAirDate] = m_firstAired.IsValid() ? m_firstAired.GetAsDBDate() : (m_premiered.IsValid() ? m_premiered.GetAsDBDate() : StringUtils::EmptyString);
512   sortable[FieldTvShowTitle] = m_strShowTitle;
513   sortable[FieldAlbum] = m_strAlbum;
514   sortable[FieldArtist] = m_artist;
515   sortable[FieldPlaycount] = m_playCount;
516   sortable[FieldLastPlayed] = m_lastPlayed.IsValid() ? m_lastPlayed.GetAsDBDateTime() : StringUtils::EmptyString;
517   sortable[FieldTop250] = m_iTop250;
518   sortable[FieldYear] = m_iYear;
519   sortable[FieldSeason] = m_iSeason;
520   sortable[FieldEpisodeNumber] = m_iEpisode;
521   sortable[FieldRating] = m_fRating;
522   sortable[FieldId] = m_iDbId;
523   sortable[FieldTrackNumber] = m_iTrack;
524
525   sortable[FieldTime] = m_streamDetails.GetVideoDuration();
526   sortable[FieldVideoResolution] = m_streamDetails.GetVideoHeight();
527   sortable[FieldVideoAspectRatio] = m_streamDetails.GetVideoAspect();
528   sortable[FieldVideoCodec] = m_streamDetails.GetVideoCodec();
529   
530   sortable[FieldAudioChannels] = m_streamDetails.GetAudioChannels();
531   sortable[FieldAudioCodec] = m_streamDetails.GetAudioCodec();
532   sortable[FieldAudioLanguage] = m_streamDetails.GetAudioLanguage();
533   
534   sortable[FieldSubtitleLanguage] = m_streamDetails.GetSubtitleLanguage();
535
536   sortable[FieldInProgress] = m_resumePoint.timeInSeconds > 0 && m_resumePoint.totalTimeInSeconds > 0;
537   sortable[FieldDateAdded] = m_dateAdded.IsValid() ? m_dateAdded.GetAsDBDateTime() : StringUtils::EmptyString;
538   sortable[FieldMediaType] = DatabaseUtils::MediaTypeFromString(m_type);
539 }
540
541 const CStdString CVideoInfoTag::GetCast(bool bIncludeRole /*= false*/) const
542 {
543   CStdString strLabel;
544   for (iCast it = m_cast.begin(); it != m_cast.end(); ++it)
545   {
546     CStdString character;
547     if (it->strRole.IsEmpty() || !bIncludeRole)
548       character.Format("%s\n", it->strName.c_str());
549     else
550       character.Format("%s %s %s\n", it->strName.c_str(), g_localizeStrings.Get(20347).c_str(), it->strRole.c_str());
551     strLabel += character;
552   }
553   return strLabel.TrimRight("\n");
554 }
555
556 void CVideoInfoTag::ParseNative(const TiXmlElement* movie, bool prioritise)
557 {
558   XMLUtils::GetString(movie, "title", m_strTitle);
559   XMLUtils::GetString(movie, "originaltitle", m_strOriginalTitle);
560   XMLUtils::GetString(movie, "showtitle", m_strShowTitle);
561   XMLUtils::GetString(movie, "sorttitle", m_strSortTitle);
562   XMLUtils::GetFloat(movie, "rating", m_fRating);
563   XMLUtils::GetFloat(movie, "epbookmark", m_fEpBookmark);
564   int max_value = 10;
565   const TiXmlElement* rElement = movie->FirstChildElement("rating");
566   if (rElement && (rElement->QueryIntAttribute("max", &max_value) == TIXML_SUCCESS) && max_value>=1)
567   {
568     m_fRating = m_fRating / max_value * 10; // Normalise the Movie Rating to between 1 and 10
569   }
570   XMLUtils::GetInt(movie, "year", m_iYear);
571   XMLUtils::GetInt(movie, "top250", m_iTop250);
572   XMLUtils::GetInt(movie, "season", m_iSeason);
573   XMLUtils::GetInt(movie, "episode", m_iEpisode);
574   XMLUtils::GetInt(movie, "track", m_iTrack);
575   XMLUtils::GetInt(movie, "displayseason", m_iSpecialSortSeason);
576   XMLUtils::GetInt(movie, "displayepisode", m_iSpecialSortEpisode);
577   int after=0;
578   XMLUtils::GetInt(movie, "displayafterseason",after);
579   if (after > 0)
580   {
581     m_iSpecialSortSeason = after;
582     m_iSpecialSortEpisode = 0x1000; // should be more than any realistic episode number
583   }
584   XMLUtils::GetString(movie, "votes", m_strVotes);
585   XMLUtils::GetString(movie, "outline", m_strPlotOutline);
586   XMLUtils::GetString(movie, "plot", m_strPlot);
587   XMLUtils::GetString(movie, "tagline", m_strTagLine);
588   XMLUtils::GetString(movie, "runtime", m_strRuntime);
589   XMLUtils::GetString(movie, "mpaa", m_strMPAARating);
590   XMLUtils::GetInt(movie, "playcount", m_playCount);
591   XMLUtils::GetDate(movie, "lastplayed", m_lastPlayed);
592   XMLUtils::GetString(movie, "file", m_strFile);
593   XMLUtils::GetString(movie, "path", m_strPath);
594   XMLUtils::GetString(movie, "id", m_strIMDBNumber);
595   XMLUtils::GetString(movie, "filenameandpath", m_strFileNameAndPath);
596   XMLUtils::GetDate(movie, "premiered", m_premiered);
597   XMLUtils::GetString(movie, "status", m_strStatus);
598   XMLUtils::GetString(movie, "code", m_strProductionCode);
599   XMLUtils::GetDate(movie, "aired", m_firstAired);
600   XMLUtils::GetString(movie, "album", m_strAlbum);
601   XMLUtils::GetString(movie, "trailer", m_strTrailer);
602   XMLUtils::GetString(movie, "basepath", m_basePath);
603
604   size_t iThumbCount = m_strPictureURL.m_url.size();
605   CStdString xmlAdd = m_strPictureURL.m_xml;
606
607   const TiXmlElement* thumb = movie->FirstChildElement("thumb");
608   while (thumb)
609   {
610     m_strPictureURL.ParseElement(thumb);
611     if (prioritise)
612     {
613       CStdString temp;
614       temp << *thumb;
615       xmlAdd = temp+xmlAdd;
616     }
617     thumb = thumb->NextSiblingElement("thumb");
618   }
619
620   // prioritise thumbs from nfos
621   if (prioritise && iThumbCount && iThumbCount != m_strPictureURL.m_url.size())
622   {
623     rotate(m_strPictureURL.m_url.begin(),
624            m_strPictureURL.m_url.begin()+iThumbCount, 
625            m_strPictureURL.m_url.end());
626     m_strPictureURL.m_xml = xmlAdd;
627   }
628
629   XMLUtils::GetStringArray(movie, "genre", m_genre, prioritise, g_advancedSettings.m_videoItemSeparator);
630   XMLUtils::GetStringArray(movie, "country", m_country, prioritise, g_advancedSettings.m_videoItemSeparator);
631   XMLUtils::GetStringArray(movie, "credits", m_writingCredits, prioritise, g_advancedSettings.m_videoItemSeparator);
632   XMLUtils::GetStringArray(movie, "director", m_director, prioritise, g_advancedSettings.m_videoItemSeparator);
633   XMLUtils::GetStringArray(movie, "showlink", m_showLink, prioritise, g_advancedSettings.m_videoItemSeparator);
634
635   // cast
636   const TiXmlElement* node = movie->FirstChildElement("actor");
637   if (node && node->FirstChild() && prioritise)
638     m_cast.clear();
639   while (node)
640   {
641     const TiXmlNode *actor = node->FirstChild("name");
642     if (actor && actor->FirstChild())
643     {
644       SActorInfo info;
645       info.strName = actor->FirstChild()->Value();
646       const TiXmlNode *roleNode = node->FirstChild("role");
647       if (roleNode && roleNode->FirstChild())
648         info.strRole = roleNode->FirstChild()->Value();
649       const TiXmlElement* thumb = node->FirstChildElement("thumb");
650       while (thumb)
651       {
652         info.thumbUrl.ParseElement(thumb);
653         thumb = thumb->NextSiblingElement("thumb");
654       }
655       const char* clear=node->Attribute("clear");
656       if (clear && stricmp(clear,"true"))
657         m_cast.clear();
658       m_cast.push_back(info);
659     }
660     node = node->NextSiblingElement("actor");
661   }
662
663   XMLUtils::GetStringArray(movie, "set", m_set, prioritise, g_advancedSettings.m_videoItemSeparator);
664   XMLUtils::GetStringArray(movie, "tag", m_tags, prioritise, g_advancedSettings.m_videoItemSeparator);
665   XMLUtils::GetStringArray(movie, "studio", m_studio, prioritise, g_advancedSettings.m_videoItemSeparator);
666   // artists
667   node = movie->FirstChildElement("artist");
668   if (node && node->FirstChild() && prioritise)
669     m_artist.clear();
670   while (node)
671   {
672     const TiXmlNode* pNode = node->FirstChild("name");
673     const char* pValue=NULL;
674     if (pNode && pNode->FirstChild())
675       pValue = pNode->FirstChild()->Value();
676     else if (node->FirstChild())
677       pValue = node->FirstChild()->Value();
678     if (pValue)
679     {
680       const char* clear=node->Attribute("clear");
681       if (clear && stricmp(clear,"true")==0)
682         m_artist.clear();
683       vector<string> artists = StringUtils::Split(pValue, g_advancedSettings.m_videoItemSeparator);
684       m_artist.insert(m_artist.end(), artists.begin(), artists.end());
685     }
686     node = node->NextSiblingElement("artist");
687   }
688
689   node = movie->FirstChildElement("fileinfo");
690   if (node)
691   {
692     // Try to pull from fileinfo/streamdetails/[video|audio|subtitle]
693     const TiXmlNode *nodeStreamDetails = node->FirstChild("streamdetails");
694     if (nodeStreamDetails)
695     {
696       const TiXmlNode *nodeDetail = NULL;
697       while ((nodeDetail = nodeStreamDetails->IterateChildren("audio", nodeDetail)))
698       {
699         CStreamDetailAudio *p = new CStreamDetailAudio();
700         XMLUtils::GetString(nodeDetail, "codec", p->m_strCodec);
701         XMLUtils::GetString(nodeDetail, "language", p->m_strLanguage);
702         XMLUtils::GetInt(nodeDetail, "channels", p->m_iChannels);
703         p->m_strCodec.MakeLower();
704         p->m_strLanguage.MakeLower();
705         m_streamDetails.AddStream(p);
706       }
707       nodeDetail = NULL;
708       while ((nodeDetail = nodeStreamDetails->IterateChildren("video", nodeDetail)))
709       {
710         CStreamDetailVideo *p = new CStreamDetailVideo();
711         XMLUtils::GetString(nodeDetail, "codec", p->m_strCodec);
712         XMLUtils::GetFloat(nodeDetail, "aspect", p->m_fAspect);
713         XMLUtils::GetInt(nodeDetail, "width", p->m_iWidth);
714         XMLUtils::GetInt(nodeDetail, "height", p->m_iHeight);
715         XMLUtils::GetInt(nodeDetail, "durationinseconds", p->m_iDuration);
716         p->m_strCodec.MakeLower();
717         m_streamDetails.AddStream(p);
718       }
719       nodeDetail = NULL;
720       while ((nodeDetail = nodeStreamDetails->IterateChildren("subtitle", nodeDetail)))
721       {
722         CStreamDetailSubtitle *p = new CStreamDetailSubtitle();
723         XMLUtils::GetString(nodeDetail, "language", p->m_strLanguage);
724         p->m_strLanguage.MakeLower();
725         m_streamDetails.AddStream(p);
726       }
727     }
728     m_streamDetails.DetermineBestStreams();
729   }  /* if fileinfo */
730
731   const TiXmlElement *epguide = movie->FirstChildElement("episodeguide");
732   if (epguide)
733   {
734     // DEPRECIATE ME - support for old XML-encoded <episodeguide> blocks.
735     if (epguide->FirstChild() && strnicmp("<episodeguide", epguide->FirstChild()->Value(), 13) == 0)
736       m_strEpisodeGuide = epguide->FirstChild()->Value();
737     else
738     {
739       stringstream stream;
740       stream << *epguide;
741       m_strEpisodeGuide = stream.str();
742     }
743   }
744
745   // fanart
746   const TiXmlElement *fanart = movie->FirstChildElement("fanart");
747   if (fanart)
748   {
749     // we prioritise mixed-mode nfo's with fanart set
750     if (prioritise)
751     {
752       CStdString temp;
753       temp << *fanart;
754       m_fanart.m_xml = temp+m_fanart.m_xml;
755     }
756     else
757       m_fanart.m_xml << *fanart;
758     m_fanart.Unpack();
759   }
760
761   // resumePoint
762   const TiXmlNode *resume = movie->FirstChild("resume");
763   if (resume)
764   {
765     XMLUtils::GetDouble(resume, "position", m_resumePoint.timeInSeconds);
766     XMLUtils::GetDouble(resume, "total", m_resumePoint.totalTimeInSeconds);
767   }
768
769   // dateAdded
770   CStdString dateAdded;
771   XMLUtils::GetString(movie, "dateadded", dateAdded);
772   m_dateAdded.SetFromDBDateTime(dateAdded);
773 }
774
775 bool CVideoInfoTag::HasStreamDetails() const
776 {
777   return m_streamDetails.HasItems();
778 }
779
780 bool CVideoInfoTag::IsEmpty() const
781 {
782   return (m_strTitle.IsEmpty() &&
783           m_strFile.IsEmpty() &&
784           m_strPath.IsEmpty());
785 }