bool CFileItemHandler::GetField(const std::string &field, const CVariant &info, const CFileItemPtr &item, CVariant &result, bool &fetchedArt, CThumbLoader *thumbLoader /* = NULL */) { if (result.isMember(field) && !result[field].empty()) return true; // overwrite serialized values if (item) { if (field == "mimetype" && item->GetMimeType().empty()) { item->FillInMimeType(false); result[field] = item->GetMimeType(); return true; } } // check for serialized values if (info.isMember(field) && !info[field].isNull()) { result[field] = info[field]; return true; } // check if the field requires special handling if (item) { if (item->IsAlbum()) { if (field == "albumlabel") { result[field] = item->GetProperty("album_label"); return true; } if (item->HasProperty("album_" + field + "_array")) { result[field] = item->GetProperty("album_" + field + "_array"); return true; } if (item->HasProperty("album_" + field)) { result[field] = item->GetProperty("album_" + field); return true; } } if (item->HasProperty("artist_" + field + "_array")) { result[field] = item->GetProperty("artist_" + field + "_array"); return true; } if (item->HasProperty("artist_" + field)) { result[field] = item->GetProperty("artist_" + field); return true; } if (field == "art") { if (thumbLoader != NULL && item->GetArt().size() <= 0 && !fetchedArt && ((item->HasVideoInfoTag() && item->GetVideoInfoTag()->m_iDbId > -1) || (item->HasMusicInfoTag() && item->GetMusicInfoTag()->GetDatabaseId() > -1))) { thumbLoader->FillLibraryArt(*item); fetchedArt = true; } CGUIListItem::ArtMap artMap = item->GetArt(); CVariant artObj(CVariant::VariantTypeObject); for (CGUIListItem::ArtMap::const_iterator artIt = artMap.begin(); artIt != artMap.end(); ++artIt) { if (!artIt->second.empty()) artObj[artIt->first] = CTextureCache::GetWrappedImageURL(artIt->second); } result["art"] = artObj; return true; } if (field == "thumbnail") { if (thumbLoader != NULL && !item->HasArt("thumb") && !fetchedArt && ((item->HasVideoInfoTag() && item->GetVideoInfoTag()->m_iDbId > -1) || (item->HasMusicInfoTag() && item->GetMusicInfoTag()->GetDatabaseId() > -1))) { thumbLoader->FillLibraryArt(*item); fetchedArt = true; } else if (item->HasPictureInfoTag() && !item->HasArt("thumb")) item->SetArt("thumb", CTextureCache::GetWrappedThumbURL(item->GetPath())); if (item->HasArt("thumb")) result["thumbnail"] = CTextureCache::GetWrappedImageURL(item->GetArt("thumb")); else result["thumbnail"] = ""; return true; } if (field == "fanart") { if (thumbLoader != NULL && !item->HasArt("fanart") && !fetchedArt && ((item->HasVideoInfoTag() && item->GetVideoInfoTag()->m_iDbId > -1) || (item->HasMusicInfoTag() && item->GetMusicInfoTag()->GetDatabaseId() > -1))) { thumbLoader->FillLibraryArt(*item); fetchedArt = true; } if (item->HasArt("fanart")) result["fanart"] = CTextureCache::GetWrappedImageURL(item->GetArt("fanart")); else result["fanart"] = ""; return true; } if (item->HasVideoInfoTag() && item->GetVideoContentType() == VIDEODB_CONTENT_TVSHOWS) { if (item->GetVideoInfoTag()->m_iSeason < 0 && field == "season") { result[field] = (int)item->GetProperty("totalseasons").asInteger(); return true; } if (field == "watchedepisodes") { result[field] = (int)item->GetProperty("watchedepisodes").asInteger(); return true; } } if (field == "lastmodified" && item->m_dateTime.IsValid()) { result[field] = item->m_dateTime.GetAsLocalizedDateTime(); return true; } if (item->HasProperty(field)) { result[field] = item->GetProperty(field); return true; } } return false; }
JSONRPC_STATUS CAudioLibrary::GetSongs(const std::string &method, ITransportLayer *transport, IClient *client, const CVariant ¶meterObject, CVariant &result) { CMusicDatabase musicdatabase; if (!musicdatabase.Open()) return InternalError; CMusicDbUrl musicUrl; if (!musicUrl.FromString("musicdb://songs/")) return InternalError; if (parameterObject["singlesonly"].asBoolean()) musicUrl.AddOption("singles", true); else if (!parameterObject["includesingles"].asBoolean()) musicUrl.AddOption("singles", false); bool allroles = false; if (parameterObject["allroles"].isBoolean()) allroles = parameterObject["allroles"].asBoolean(); const CVariant &filter = parameterObject["filter"]; if (allroles) musicUrl.AddOption("roleid", -1000); //All roles, override implicit roleid=1 filter required for backward compatibility else if (filter.isMember("roleid")) musicUrl.AddOption("roleid", static_cast<int>(filter["roleid"].asInteger())); else if (filter.isMember("role")) musicUrl.AddOption("role", filter["role"].asString()); // Only one of genreid/genre, artistid/artist, albumid/album or rules type filter is allowed by filter syntax if (filter.isMember("artistid")) musicUrl.AddOption("artistid", static_cast<int>(filter["artistid"].asInteger())); else if (filter.isMember("artist")) musicUrl.AddOption("artist", filter["artist"].asString()); else if (filter.isMember("genreid")) musicUrl.AddOption("genreid", static_cast<int>(filter["genreid"].asInteger())); else if (filter.isMember("genre")) musicUrl.AddOption("genre", filter["genre"].asString()); else if (filter.isMember("albumid")) musicUrl.AddOption("albumid", static_cast<int>(filter["albumid"].asInteger())); else if (filter.isMember("album")) musicUrl.AddOption("album", filter["album"].asString()); else if (filter.isObject()) { std::string xsp; if (!GetXspFiltering("songs", filter, xsp)) return InvalidParams; musicUrl.AddOption("xsp", xsp); } SortDescription sorting; ParseLimits(parameterObject, sorting.limitStart, sorting.limitEnd); if (!ParseSorting(parameterObject, sorting.sortBy, sorting.sortOrder, sorting.sortAttributes)) return InvalidParams; int total; std::set<std::string> fields; if (parameterObject.isMember("properties") && parameterObject["properties"].isArray()) { for (CVariant::const_iterator_array field = parameterObject["properties"].begin_array(); field != parameterObject["properties"].end_array(); field++) fields.insert(field->asString()); } if (!musicdatabase.GetSongsByWhereJSON(fields, musicUrl.ToString(), result, total, sorting)) return InternalError; if (!result.isNull()) { bool bFetchArt = fields.find("art") != fields.end(); bool bFetchFanart = fields.find("fanart") != fields.end(); bool bFetchThumb = fields.find("thumbnail") != fields.end(); if (bFetchArt || bFetchFanart || bFetchThumb) { CThumbLoader* thumbLoader = new CMusicThumbLoader(); thumbLoader->OnLoaderStart(); std::set<std::string> artfields; if (bFetchArt) artfields.insert("art"); if (bFetchFanart) artfields.insert("fanart"); if (bFetchThumb) artfields.insert("thumbnail"); for (unsigned int index = 0; index < result["songs"].size(); index++) { CFileItem item; // Only needs song and album id (if we have it) set to get art // Getting art is quicker if "albumid" has been fetched item.GetMusicInfoTag()->SetDatabaseId(result["songs"][index]["songid"].asInteger(), MediaTypeSong); if (result["songs"][index].isMember("albumid")) item.GetMusicInfoTag()->SetAlbumId(result["songs"][index]["albumid"].asInteger()); else item.GetMusicInfoTag()->SetAlbumId(-1); // Could use FillDetails, but it does unnecessary serialization of empty MusiInfoTag // CFileItemPtr itemptr(new CFileItem(item)); // FillDetails(item.GetMusicInfoTag(), itemptr, artfields, result["songs"][index], thumbLoader); thumbLoader->FillLibraryArt(item); if (bFetchThumb) { if (item.HasArt("thumb")) result["songs"][index]["thumbnail"] = CTextureUtils::GetWrappedImageURL(item.GetArt("thumb")); else result["songs"][index]["thumbnail"] = ""; } if (bFetchFanart) { if (item.HasArt("fanart")) result["songs"][index]["fanart"] = CTextureUtils::GetWrappedImageURL(item.GetArt("fanart")); else result["songs"][index]["fanart"] = ""; } if (bFetchArt) { CGUIListItem::ArtMap artMap = item.GetArt(); CVariant artObj(CVariant::VariantTypeObject); for (CGUIListItem::ArtMap::const_iterator artIt = artMap.begin(); artIt != artMap.end(); ++artIt) { if (!artIt->second.empty()) artObj[artIt->first] = CTextureUtils::GetWrappedImageURL(artIt->second); } result["songs"][index]["art"] = artObj; } } delete thumbLoader; } } int start, end; HandleLimits(parameterObject, result, total, start, end); return OK; }
void CGUIDialogVideoInfo::SetMovie(const CFileItem *item) { *m_movieItem = *item; // setup cast list + determine type. We need to do this here as it makes // sure that content type (among other things) is set correctly for the // old fixed id labels that we have floating around (they may be using // content type to determine visibility, so we'll set the wrong label) ClearCastList(); VIDEODB_CONTENT_TYPE type = (VIDEODB_CONTENT_TYPE)m_movieItem->GetVideoContentType(); if (type == VIDEODB_CONTENT_MUSICVIDEOS) { // music video CMusicDatabase database; database.Open(); const std::vector<std::string> &artists = m_movieItem->GetVideoInfoTag()->m_artist; for (std::vector<std::string>::const_iterator it = artists.begin(); it != artists.end(); ++it) { int idArtist = database.GetArtistByName(*it); CStdString thumb = database.GetArtForItem(idArtist, "artist", "thumb"); CFileItemPtr item(new CFileItem(*it)); if (!thumb.empty()) item->SetArt("thumb", thumb); item->SetIconImage("DefaultArtist.png"); m_castList->Add(item); } m_castList->SetContent("musicvideos"); } else { // movie/show/episode for (CVideoInfoTag::iCast it = m_movieItem->GetVideoInfoTag()->m_cast.begin(); it != m_movieItem->GetVideoInfoTag()->m_cast.end(); ++it) { CStdString character; if (it->strRole.IsEmpty()) character = it->strName; else character.Format("%s %s %s", it->strName.c_str(), g_localizeStrings.Get(20347).c_str(), it->strRole.c_str()); CFileItemPtr item(new CFileItem(it->strName)); if (!it->thumb.IsEmpty()) item->SetArt("thumb", it->thumb); else if (g_guiSettings.GetBool("videolibrary.actorthumbs")) { // backward compatibility CStdString thumb = CScraperUrl::GetThumbURL(it->thumbUrl.GetFirstThumb()); if (!thumb.IsEmpty()) { item->SetArt("thumb", thumb); CTextureCache::Get().BackgroundCacheImage(thumb); } } item->SetIconImage("DefaultActor.png"); item->SetLabel(character); m_castList->Add(item); } // determine type: if (type == VIDEODB_CONTENT_TVSHOWS) { m_castList->SetContent("tvshows"); // special case stuff for shows (not currently retrieved from the library in filemode (ref: GetTvShowInfo vs GetTVShowsByWhere) m_movieItem->m_dateTime = m_movieItem->GetVideoInfoTag()->m_premiered; if(m_movieItem->GetVideoInfoTag()->m_iYear == 0 && m_movieItem->m_dateTime.IsValid()) m_movieItem->GetVideoInfoTag()->m_iYear = m_movieItem->m_dateTime.GetYear(); m_movieItem->SetProperty("totalepisodes", m_movieItem->GetVideoInfoTag()->m_iEpisode); m_movieItem->SetProperty("numepisodes", m_movieItem->GetVideoInfoTag()->m_iEpisode); // info view has no concept of current watched/unwatched filter as we could come here from files view, but set for consistency m_movieItem->SetProperty("watchedepisodes", m_movieItem->GetVideoInfoTag()->m_playCount); m_movieItem->SetProperty("unwatchedepisodes", m_movieItem->GetVideoInfoTag()->m_iEpisode - m_movieItem->GetVideoInfoTag()->m_playCount); m_movieItem->GetVideoInfoTag()->m_playCount = (m_movieItem->GetVideoInfoTag()->m_iEpisode == m_movieItem->GetVideoInfoTag()->m_playCount) ? 1 : 0; } else if (type == VIDEODB_CONTENT_EPISODES) { m_castList->SetContent("episodes"); // special case stuff for episodes (not currently retrieved from the library in filemode (ref: GetEpisodeInfo vs GetEpisodesByWhere) m_movieItem->m_dateTime = m_movieItem->GetVideoInfoTag()->m_firstAired; if(m_movieItem->GetVideoInfoTag()->m_iYear == 0 && m_movieItem->m_dateTime.IsValid()) m_movieItem->GetVideoInfoTag()->m_iYear = m_movieItem->m_dateTime.GetYear(); // retrieve the season thumb. // TODO: should we use the thumbloader for this? CVideoDatabase db; if (db.Open()) { if (m_movieItem->GetVideoInfoTag()->m_iSeason > -1) { int seasonID = m_movieItem->GetVideoInfoTag()->m_iIdSeason; if (seasonID < 0) seasonID = db.GetSeasonId(m_movieItem->GetVideoInfoTag()->m_iIdShow, m_movieItem->GetVideoInfoTag()->m_iSeason); CGUIListItem::ArtMap thumbs; if (db.GetArtForItem(seasonID, "season", thumbs)) { for (CGUIListItem::ArtMap::iterator i = thumbs.begin(); i != thumbs.end(); i++) m_movieItem->SetArt("season." + i->first, i->second); } } db.Close(); } } else if (type == VIDEODB_CONTENT_MOVIES) { m_castList->SetContent("movies"); // local trailers should always override non-local, so check // for a local one if the registered trailer is online if (m_movieItem->GetVideoInfoTag()->m_strTrailer.IsEmpty() || URIUtils::IsInternetStream(m_movieItem->GetVideoInfoTag()->m_strTrailer)) { CStdString localTrailer = m_movieItem->FindTrailer(); if (!localTrailer.IsEmpty()) { m_movieItem->GetVideoInfoTag()->m_strTrailer = localTrailer; CVideoDatabase database; if(database.Open()) { database.SetDetail(m_movieItem->GetVideoInfoTag()->m_strTrailer, m_movieItem->GetVideoInfoTag()->m_iDbId, VIDEODB_ID_TRAILER, VIDEODB_CONTENT_MOVIES); database.Close(); CUtil::DeleteVideoDatabaseDirectoryCache(); } } } } } CVideoThumbLoader loader; loader.LoadItem(m_movieItem.get()); }
/* Allow user to choose artwork for the artist or album For each type of art the options are: 1. Current art 2. Local art (thumb found by filename) 3. Remote art (scraped list of urls from online sources e.g. fanart.tv) 5. Embedded art (@todo) 6. None */ void CGUIDialogMusicInfo::OnGetArt() { std::string type = MUSIC_UTILS::ShowSelectArtTypeDialog(m_artTypeList); if (type.empty()) return; // Cancelled CFileItemList items; CGUIListItem::ArtMap primeArt = m_item->GetArt(); // art without fallbacks bool bHasArt = m_item->HasArt(type); bool bFallback(false); if (bHasArt) { // Check if that type of art is actually a fallback, e.g. artist fanart CGUIListItem::ArtMap::const_iterator i = primeArt.find(type); bFallback = (i == primeArt.end()); } // Build list of possible images of that art type if (bHasArt) { // Add item for current artwork // For album it could be a fallback from artist CFileItemPtr item(new CFileItem("thumb://Current", false)); item->SetArt("thumb", m_item->GetArt(type)); item->SetIconImage("DefaultPicture.png"); item->SetLabel(g_localizeStrings.Get(13512)); items.Add(item); } else if (m_item->HasArt("thumb")) { // For missing art of that type add the thumb (when it exists and not a fallback) CGUIListItem::ArtMap::const_iterator i = primeArt.find("thumb"); if (i != primeArt.end()) { CFileItemPtr item(new CFileItem("thumb://Thumb", false)); item->SetArt("thumb", m_item->GetArt("thumb")); if (m_bArtistInfo) item->SetIconImage("DefaultArtistCover.png"); else item->SetIconImage("DefaultAlbumCover.png"); item->SetLabel(g_localizeStrings.Get(21371)); items.Add(item); } } // Grab the thumbnails of this art type scraped from the web std::vector<std::string> remotethumbs; if (type == "fanart" && m_bArtistInfo) { // Scraped artist fanart URLs are held separately from other art types //! @todo Change once scraping all art types is unified for (unsigned int i = 0; i < m_artist.fanart.GetNumFanarts(); i++) { std::string strItemPath; strItemPath = StringUtils::Format("fanart://Remote%i", i); CFileItemPtr item(new CFileItem(strItemPath, false)); // Preview "thumb" of fanart image for browsing std::string thumb = m_artist.fanart.GetPreviewURL(i); std::string wrappedthumb = CTextureUtils::GetWrappedThumbURL(thumb); item->SetArt("thumb", wrappedthumb); item->SetIconImage("DefaultPicture.png"); item->SetLabel(g_localizeStrings.Get(20441)); items.Add(item); } } else { // Art type is encoded into the scraper XML as optional "aspect=" field // Type "thumb" returns URLs for all types of art including those without aspect. // Those URL without aspect are also returned for all other type values. if (m_bArtistInfo) m_artist.thumbURL.GetThumbURLs(remotethumbs, type); else m_album.thumbURL.GetThumbURLs(remotethumbs, type); for (unsigned int i = 0; i < remotethumbs.size(); ++i) { std::string strItemPath; strItemPath = StringUtils::Format("thumb://Remote%i", i); CFileItemPtr item(new CFileItem(strItemPath, false)); item->SetArt("thumb", remotethumbs[i]); item->SetIconImage("DefaultPicture.png"); item->SetLabel(g_localizeStrings.Get(13513)); items.Add(item); } } // Local art std::string localArt; std::vector<std::string> paths; if (m_bArtistInfo) { // Individual artist subfolder within the Artist Information Folder paths.emplace_back(m_artist.strPath); // Fallback local to music files (when there is a unique folder) paths.emplace_back(m_fallbackartpath); } else // Album folder, when a unique one exists, no fallback paths.emplace_back(m_album.strPath); for (const auto& path : paths) { if (!localArt.empty() && CFile::Exists(localArt)) break; if (!path.empty()) { CFileItem item(path, true); if (type == "thumb") // Local music thumbnail images named by <musicthumbs> localArt = item.GetUserMusicThumb(true); else if (type == "fanart") // Local fanart images named by <fanart> localArt = item.GetLocalFanart(); else { // Check case and ext insenitively for local images with type as name // e.g. <arttype>.jpg CFileItemList items; CDirectory::GetDirectory(path, items, CServiceBroker::GetFileExtensionProvider().GetPictureExtensions(), DIR_FLAG_NO_FILE_DIRS | DIR_FLAG_READ_CACHE | DIR_FLAG_NO_FILE_INFO); for (int j = 0; j < items.Size(); j++) { std::string strCandidate = URIUtils::GetFileName(items[j]->GetPath()); URIUtils::RemoveExtension(strCandidate); if (StringUtils::EqualsNoCase(strCandidate, type)) { localArt = items[j]->GetPath(); break; } } } } } if (!localArt.empty() && CFile::Exists(localArt)) { CFileItemPtr item(new CFileItem("Local Art: " + localArt, false)); item->SetArt("thumb", localArt); item->SetLabel(g_localizeStrings.Get(13514)); // "Local art" items.Add(item); } // No art if (bHasArt && !bFallback) { // Actually has this type of art (not a fallback) so // allow the user to delete it by selecting "no art". CFileItemPtr item(new CFileItem("thumb://None", false)); if (m_bArtistInfo) item->SetIconImage("DefaultArtist.png"); else item->SetIconImage("DefaultAlbumCover.png"); item->SetLabel(g_localizeStrings.Get(13515)); items.Add(item); } //! @todo: Add support for extracting embedded art from song files to use for album // Clear local images of this type from cache so user will see any recent // local file changes immediately for (auto& item : items) { // Skip images from remote sources, recache done by refresh (could be slow) if (StringUtils::StartsWith(item->GetPath(), "fanart://Remote") || StringUtils::StartsWith(item->GetPath(), "thumb://Remote")) continue; std::string thumb(item->GetArt("thumb")); if (thumb.empty()) continue; CURL url(CTextureUtils::UnwrapImageURL(thumb)); // Skip images from remote sources (current thumb could be remote) if (url.IsProtocol("http") || url.IsProtocol("https")) continue; CTextureCache::GetInstance().ClearCachedImage(thumb); // Remove any thumbnail of local image too (created when browsing files) std::string thumbthumb(CTextureUtils::GetWrappedThumbURL(thumb)); CTextureCache::GetInstance().ClearCachedImage(thumbthumb); } // Show list of possible art for user selection // Note that during selection thumbs of *all* images shown are cached. When // browsing folders there could be many irrelevant thumbs cached that are // never used by Kodi again, but there is no obvious way to clear these // thumbs from the cache automatically. std::string result; VECSOURCES sources(*CMediaSourceSettings::GetInstance().GetSources("music")); CGUIDialogMusicInfo::AddItemPathToFileBrowserSources(sources, *m_item); g_mediaManager.GetLocalDrives(sources); if (CGUIDialogFileBrowser::ShowAndGetImage(items, sources, g_localizeStrings.Get(13511), result) && result != "thumb://Current") { // User didn't choose the one they have. // Overwrite with the new art or clear it std::string newArt; if (StringUtils::StartsWith(result, "thumb://Remote")) { int number = atoi(result.substr(14).c_str()); newArt = remotethumbs[number]; } else if (StringUtils::StartsWith(result, "fanart://Remote")) { int iFanart = atoi(result.substr(15).c_str()); m_artist.fanart.SetPrimaryFanart(iFanart); newArt = m_artist.fanart.GetImageURL(); } else if (result == "thumb://Thumb") newArt = m_item->GetArt("thumb"); else if (StringUtils::StartsWith(result, "Local Art: ")) newArt = localArt; else if (CFile::Exists(result)) newArt = result; else // none newArt.clear(); // Asynchronously update that type of art in the database and then // refresh artist, album and fallback art of currently playing song MUSIC_UTILS::UpdateArtJob(m_item, type, newArt); // Update local item and art list with current art m_item->SetArt(type, newArt); for (const auto artitem : m_artTypeList) { if (artitem->GetProperty("artType") == type) { artitem->SetArt("thumb", newArt); break; } } // Get new artwork to show in other places e.g. on music lib window, but this does // not update artist, album or fallback art for the currently playing song as it // is a different item with different ID and media type CGUIMessage msg(GUI_MSG_NOTIFY_ALL, 0, 0, GUI_MSG_UPDATE_ITEM, 0, m_item); CServiceBroker::GetGUI()->GetWindowManager().SendMessage(msg); } // Re-open the art type selection dialog as we come back from // the image selection dialog OnGetArt(); }