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