2 * Copyright (C) 2005-2013 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, see
17 * <http://www.gnu.org/licenses/>.
24 #include "AddonUtils.h"
26 #include "video/VideoInfoTag.h"
27 #include "music/tags/MusicInfoTag.h"
28 #include "pictures/PictureInfoTag.h"
29 #include "utils/log.h"
30 #include "utils/Variant.h"
31 #include "utils/StringUtils.h"
32 #include "settings/AdvancedSettings.h"
38 ListItem::ListItem(const String& label,
40 const String& iconImage,
41 const String& thumbnailImage,
47 item.reset(new CFileItem());
48 if (!item) // not sure if this is really possible
52 item->SetLabel( label );
54 item->SetLabel2( label2 );
55 if (!iconImage.empty())
56 item->SetIconImage( iconImage );
57 if (!thumbnailImage.empty())
58 item->SetArt("thumb", thumbnailImage );
68 String ListItem::getLabel()
75 ret = item->GetLabel();
81 String ListItem::getLabel2()
88 ret = item->GetLabel2();
94 void ListItem::setLabel(const String& label)
100 item->SetLabel(label);
104 void ListItem::setLabel2(const String& label)
110 item->SetLabel2(label);
114 void ListItem::setIconImage(const String& iconImage)
119 item->SetIconImage(iconImage);
123 void ListItem::setThumbnailImage(const String& thumbFilename)
128 item->SetArt("thumb", thumbFilename);
132 void ListItem::select(bool selected)
137 item->Select(selected);
142 bool ListItem::isSelected()
144 if (!item) return false;
149 ret = item->IsSelected();
155 void ListItem::setProperty(const char * key, const String& value)
158 CStdString lowerKey = key;
159 if (lowerKey.CompareNoCase("startoffset") == 0)
160 { // special case for start offset - don't actually store in a property,
161 // we store it in item.m_lStartOffset instead
162 item->m_lStartOffset = (int)(atof(value.c_str()) * 75.0); // we store the offset in frames, or 1/75th of a second
164 else if (lowerKey.CompareNoCase("mimetype") == 0)
165 { // special case for mime type - don't actually stored in a property,
166 item->SetMimeType(value.c_str());
168 else if (lowerKey.CompareNoCase("totaltime") == 0)
169 item->GetVideoInfoTag()->m_resumePoint.totalTimeInSeconds = (float)atof(value.c_str());
170 else if (lowerKey.CompareNoCase("resumetime") == 0)
171 item->GetVideoInfoTag()->m_resumePoint.timeInSeconds = (float)atof(value.c_str());
172 else if (lowerKey.CompareNoCase("specialsort") == 0)
174 if (value == "bottom")
175 item->SetSpecialSort(SortSpecialOnBottom);
176 else if (value == "top")
177 item->SetSpecialSort(SortSpecialOnTop);
179 else if (lowerKey.CompareNoCase("fanart_image") == 0)
180 item->SetArt("fanart", value);
182 item->SetProperty(lowerKey.ToLower(), value.c_str());
185 String ListItem::getProperty(const char* key)
188 CStdString lowerKey = key;
190 if (lowerKey.CompareNoCase("startoffset") == 0)
191 { // special case for start offset - don't actually store in a property,
192 // we store it in item.m_lStartOffset instead
193 value = StringUtils::Format("%f", item->m_lStartOffset / 75.0);
195 else if (lowerKey.CompareNoCase("totaltime") == 0)
196 value = StringUtils::Format("%f", item->GetVideoInfoTag()->m_resumePoint.totalTimeInSeconds);
197 else if (lowerKey.CompareNoCase("resumetime") == 0)
198 value = StringUtils::Format("%f", item->GetVideoInfoTag()->m_resumePoint.timeInSeconds);
199 else if (lowerKey.CompareNoCase("fanart_image") == 0)
200 value = item->GetArt("fanart");
202 value = item->GetProperty(lowerKey.ToLower()).asString();
204 return value.c_str();
207 void ListItem::setPath(const String& path)
213 void ListItem::setMimeType(const String& mimetype)
216 item->SetMimeType(mimetype);
219 String ListItem::getdescription()
221 return item->GetLabel();
224 String ListItem::getduration()
226 if (item->LoadMusicTag())
228 std::ostringstream oss;
229 oss << item->GetMusicInfoTag()->GetDuration();
233 if (item->HasVideoInfoTag())
235 std::ostringstream oss;
236 oss << item->GetVideoInfoTag()->GetDuration() / 60;
242 String ListItem::getfilename()
244 return item->GetPath();
247 void ListItem::setInfo(const char* type, const Dictionary& infoLabels)
251 if (strcmpi(type, "video") == 0)
253 for (Dictionary::const_iterator it = infoLabels.begin(); it != infoLabels.end(); ++it)
255 CStdString key = it->first;
257 const CStdString value(it->second.c_str());
260 item->GetVideoInfoTag()->m_iYear = strtol(value.c_str(), NULL, 10);
261 else if (key == "episode")
262 item->GetVideoInfoTag()->m_iEpisode = strtol(value.c_str(), NULL, 10);
263 else if (key == "season")
264 item->GetVideoInfoTag()->m_iSeason = strtol(value.c_str(), NULL, 10);
265 else if (key == "top250")
266 item->GetVideoInfoTag()->m_iTop250 = strtol(value.c_str(), NULL, 10);
267 else if (key == "tracknumber")
268 item->GetVideoInfoTag()->m_iTrack = strtol(value.c_str(), NULL, 10);
269 else if (key == "count")
270 item->m_iprogramCount = strtol(value.c_str(), NULL, 10);
271 else if (key == "rating")
272 item->GetVideoInfoTag()->m_fRating = (float)strtod(value.c_str(), NULL);
273 else if (key == "size")
274 item->m_dwSize = (int64_t)strtoll(value.c_str(), NULL, 10);
275 else if (key == "watched") // backward compat - do we need it?
276 item->GetVideoInfoTag()->m_playCount = strtol(value.c_str(), NULL, 10);
277 else if (key == "playcount")
278 item->GetVideoInfoTag()->m_playCount = strtol(value.c_str(), NULL, 10);
279 else if (key == "overlay")
281 long overlay = strtol(value.c_str(), NULL, 10);
282 if (overlay >= 0 && overlay <= 8)
283 item->SetOverlayImage((CGUIListItem::GUIIconOverlay)overlay);
285 // TODO: This is a dynamic type for the value where a list is expected as the
287 // else if (key == "cast" || key == "castandrole")
289 // if (!PyObject_TypeCheck(value, &PyList_Type)) continue;
290 // item->GetVideoInfoTag()->m_cast.clear();
291 // for (int i = 0; i < PyList_Size(value); i++)
293 // PyObject *pTuple = NULL;
294 // pTuple = PyList_GetItem(value, i);
295 // if (pTuple == NULL) continue;
296 // PyObject *pActor = NULL;
297 // PyObject *pRole = NULL;
298 // if (PyObject_TypeCheck(pTuple, &PyTuple_Type))
300 // if (!PyArg_ParseTuple(pTuple, (char*)"O|O", &pActor, &pRole)) continue;
305 // if (!PyXBMCGetUnicodeString(info.strName, pActor, 1)) continue;
306 // if (pRole != NULL)
307 // PyXBMCGetUnicodeString(info.strRole, pRole, 1);
308 // item->GetVideoInfoTag()->m_cast.push_back(info);
311 // else if (strcmpi(PyString_AsString(key), "artist") == 0)
313 // if (!PyObject_TypeCheck(value, &PyList_Type)) continue;
314 // self->item->GetVideoInfoTag()->m_artist.clear();
315 // for (int i = 0; i < PyList_Size(value); i++)
317 // PyObject *pActor = PyList_GetItem(value, i);
318 // if (pActor == NULL) continue;
320 // if (!PyXBMCGetUnicodeString(actor, pActor, 1)) continue;
321 // self->item->GetVideoInfoTag()->m_artist.push_back(actor);
324 else if (key == "genre")
325 item->GetVideoInfoTag()->m_genre = StringUtils::Split(value, g_advancedSettings.m_videoItemSeparator);
326 else if (key == "director")
327 item->GetVideoInfoTag()->m_director = StringUtils::Split(value, g_advancedSettings.m_videoItemSeparator);
328 else if (key == "mpaa")
329 item->GetVideoInfoTag()->m_strMPAARating = value;
330 else if (key == "plot")
331 item->GetVideoInfoTag()->m_strPlot = value;
332 else if (key == "plotoutline")
333 item->GetVideoInfoTag()->m_strPlotOutline = value;
334 else if (key == "title")
335 item->GetVideoInfoTag()->m_strTitle = value;
336 else if (key == "originaltitle")
337 item->GetVideoInfoTag()->m_strOriginalTitle = value;
338 else if (key == "sorttitle")
339 item->GetVideoInfoTag()->m_strSortTitle = value;
340 else if (key == "duration")
341 item->GetVideoInfoTag()->m_duration = CVideoInfoTag::GetDurationFromMinuteString(value);
342 else if (key == "studio")
343 item->GetVideoInfoTag()->m_studio = StringUtils::Split(value, g_advancedSettings.m_videoItemSeparator);
344 else if (key == "tagline")
345 item->GetVideoInfoTag()->m_strTagLine = value;
346 else if (key == "writer")
347 item->GetVideoInfoTag()->m_writingCredits = StringUtils::Split(value, g_advancedSettings.m_videoItemSeparator);
348 else if (key == "tvshowtitle")
349 item->GetVideoInfoTag()->m_strShowTitle = value;
350 else if (key == "premiered")
351 item->GetVideoInfoTag()->m_premiered.SetFromDateString(value);
352 else if (key == "status")
353 item->GetVideoInfoTag()->m_strStatus = value;
354 else if (key == "code")
355 item->GetVideoInfoTag()->m_strProductionCode = value;
356 else if (key == "aired")
357 item->GetVideoInfoTag()->m_firstAired.SetFromDateString(value);
358 else if (key == "credits")
359 item->GetVideoInfoTag()->m_writingCredits = StringUtils::Split(value, g_advancedSettings.m_videoItemSeparator);
360 else if (key == "lastplayed")
361 item->GetVideoInfoTag()->m_lastPlayed.SetFromDBDateTime(value);
362 else if (key == "album")
363 item->GetVideoInfoTag()->m_strAlbum = value;
364 else if (key == "votes")
365 item->GetVideoInfoTag()->m_strVotes = value;
366 else if (key == "trailer")
367 item->GetVideoInfoTag()->m_strTrailer = value;
368 else if (key == "date")
370 if (value.length() == 10)
371 item->m_dateTime.SetDate(atoi(value.Right(4).c_str()), atoi(value.Mid(3,4).c_str()), atoi(value.Left(2).c_str()));
373 CLog::Log(LOGERROR,"NEWADDON Invalid Date Format \"%s\"",value.c_str());
375 else if (key == "dateadded")
376 item->GetVideoInfoTag()->m_dateAdded.SetFromDBDateTime(value.c_str());
379 else if (strcmpi(type, "music") == 0)
381 for (Dictionary::const_iterator it = infoLabels.begin(); it != infoLabels.end(); ++it)
383 CStdString key = it->first;
386 const CStdString value(it->second.c_str());
388 // TODO: add the rest of the infolabels
389 if (key == "tracknumber")
390 item->GetMusicInfoTag()->SetTrackNumber(strtol(value, NULL, 10));
391 else if (key == "count")
392 item->m_iprogramCount = strtol(value, NULL, 10);
393 else if (key == "size")
394 item->m_dwSize = (int64_t)strtoll(value, NULL, 10);
395 else if (key == "duration")
396 item->GetMusicInfoTag()->SetDuration(strtol(value, NULL, 10));
397 else if (key == "year")
398 item->GetMusicInfoTag()->SetYear(strtol(value, NULL, 10));
399 else if (key == "listeners")
400 item->GetMusicInfoTag()->SetListeners(strtol(value, NULL, 10));
401 else if (key == "playcount")
402 item->GetMusicInfoTag()->SetPlayCount(strtol(value, NULL, 10));
403 else if (key == "genre")
404 item->GetMusicInfoTag()->SetGenre(value);
405 else if (key == "album")
406 item->GetMusicInfoTag()->SetAlbum(value);
407 else if (key == "artist")
408 item->GetMusicInfoTag()->SetArtist(value);
409 else if (key == "title")
410 item->GetMusicInfoTag()->SetTitle(value);
411 else if (key == "rating")
412 item->GetMusicInfoTag()->SetRating(*value);
413 else if (key == "lyrics")
414 item->GetMusicInfoTag()->SetLyrics(value);
415 else if (key == "lastplayed")
416 item->GetMusicInfoTag()->SetLastPlayed(value);
417 else if (key == "musicbrainztrackid")
418 item->GetMusicInfoTag()->SetMusicBrainzTrackID(value);
419 else if (key == "musicbrainzartistid")
420 item->GetMusicInfoTag()->SetMusicBrainzArtistID(StringUtils::Split(value, g_advancedSettings.m_musicItemSeparator));
421 else if (key == "musicbrainzalbumid")
422 item->GetMusicInfoTag()->SetMusicBrainzAlbumID(value);
423 else if (key == "musicbrainzalbumartistid")
424 item->GetMusicInfoTag()->SetMusicBrainzAlbumArtistID(StringUtils::Split(value, g_advancedSettings.m_musicItemSeparator));
425 else if (key == "musicbrainztrmid")
426 item->GetMusicInfoTag()->SetMusicBrainzTRMID(value);
427 else if (key == "comment")
428 item->GetMusicInfoTag()->SetComment(value);
429 else if (key == "date")
431 if (strlen(value) == 10)
432 item->m_dateTime.SetDate(atoi(value.Right(4)), atoi(value.Mid(3,4)), atoi(value.Left(2)));
435 CLog::Log(LOGERROR,"NEWADDON Unknown Music Info Key \"%s\"", key.c_str());
437 // This should probably be set outside of the loop but since the original
438 // implementation set it inside of the loop, I'll leave it that way. - Jim C.
439 item->GetMusicInfoTag()->SetLoaded(true);
442 else if (strcmpi(type,"pictures") == 0)
444 for (Dictionary::const_iterator it = infoLabels.begin(); it != infoLabels.end(); ++it)
446 CStdString key = it->first;
448 const CStdString value(it->second.c_str());
451 item->m_iprogramCount = strtol(value, NULL, 10);
452 else if (key == "size")
453 item->m_dwSize = (int64_t)strtoll(value, NULL, 10);
454 else if (key == "title")
455 item->m_strTitle = value;
456 else if (key == "picturepath")
457 item->SetPath(value);
458 else if (key == "date")
460 if (strlen(value) == 10)
461 item->m_dateTime.SetDate(atoi(value.Right(4)), atoi(value.Mid(3,4)), atoi(value.Left(2)));
465 const CStdString& exifkey = key;
466 if (!StringUtils::StartsWithNoCase(exifkey, "exif:") || exifkey.length() < 6) continue;
467 int info = CPictureInfoTag::TranslateString(exifkey.Mid(5));
468 item->GetPictureInfoTag()->SetInfo(info, value);
472 } // end ListItem::setInfo
474 void ListItem::addStreamInfo(const char* cType, const Dictionary& dictionary)
478 if (strcmpi(cType, "video") == 0)
480 CStreamDetailVideo* video = new CStreamDetailVideo;
481 for (Dictionary::const_iterator it = dictionary.begin(); it != dictionary.end(); ++it)
483 const String& key = it->first;
484 const CStdString value(it->second.c_str());
487 video->m_strCodec = value;
488 else if (key == "aspect")
489 video->m_fAspect = (float)atof(value);
490 else if (key == "width")
491 video->m_iWidth = strtol(value, NULL, 10);
492 else if (key == "height")
493 video->m_iHeight = strtol(value, NULL, 10);
494 else if (key == "duration")
495 video->m_iDuration = strtol(value, NULL, 10);
496 else if (key == "stereomode")
497 video->m_strStereoMode = value;
499 item->GetVideoInfoTag()->m_streamDetails.AddStream(video);
501 else if (strcmpi(cType, "audio") == 0)
503 CStreamDetailAudio* audio = new CStreamDetailAudio;
504 for (Dictionary::const_iterator it = dictionary.begin(); it != dictionary.end(); ++it)
506 const String& key = it->first;
507 const String& value = it->second;
510 audio->m_strCodec = value;
511 else if (key == "language")
512 audio->m_strLanguage = value;
513 else if (key == "channels")
514 audio->m_iChannels = strtol(value.c_str(), NULL, 10);
516 item->GetVideoInfoTag()->m_streamDetails.AddStream(audio);
518 else if (strcmpi(cType, "subtitle") == 0)
520 CStreamDetailSubtitle* subtitle = new CStreamDetailSubtitle;
521 for (Dictionary::const_iterator it = dictionary.begin(); it != dictionary.end(); ++it)
523 const String& key = it->first;
524 const String& value = it->second;
526 if (key == "language")
527 subtitle->m_strLanguage = value;
529 item->GetVideoInfoTag()->m_streamDetails.AddStream(subtitle);
531 } // end ListItem::addStreamInfo
533 void ListItem::addContextMenuItems(const std::vector<Tuple<String,String> >& items, bool replaceItems /* = false */)
534 throw (ListItemException)
537 for (std::vector<Tuple<String,String> >::const_iterator iter = items.begin(); iter < items.end(); ++iter, ++itemCount)
539 Tuple<String,String> tuple = *iter;
540 if (tuple.GetNumValuesSet() != 2)
541 throw ListItemException("Must pass in a list of tuples of pairs of strings. One entry in the list only has %d elements.",tuple.GetNumValuesSet());
542 std::string uText = tuple.first();
543 std::string uAction = tuple.second();
547 property = StringUtils::Format("contextmenulabel(%i)", itemCount);
548 item->SetProperty(property, uText);
550 property = StringUtils::Format("contextmenuaction(%i)", itemCount);
551 item->SetProperty(property, uAction);
554 // set our replaceItems status
556 item->SetProperty("pluginreplacecontextitems", replaceItems);
557 } // end addContextMenuItems