CStdString CThumbLoader::GetCachedImage(const CFileItem &item, const CStdString &type) { CTextureDatabase db; if (db.Open()) return db.GetTextureForPath(item.GetPath(), type); return ""; }
CStdString CThumbLoader::GetCachedThumb(const CFileItem &item) { CTextureDatabase db; if (db.Open()) return db.GetTextureForPath(item.GetPath()); return ""; }
bool CTextureCleanupJob::DoWork() { CTextureDatabase db; if (db.Open()) { std::string path; std::string fn; std::string prevPath = ""; std::vector<std::string> pathContent; XFILE::CSpecialProtocolDirectory thumbDir; CFileItemList files; int fileIdx = 0; int totFileDel = 0; int totDbDel = 0; auto deleteFile = [&]() { std::string todel = files.Get(fileIdx)->GetPath(); CLog::Log(LOGDEBUG, "CTextureCleanupJob: deleting %s", todel.c_str()); XFILE::CFile::Delete(todel); totFileDel++; fileIdx++; }; std::vector<std::pair<int, std::string>> urls = db.GetCachedTextureUrls(); for (const auto &url : urls) { std::string cachedurl = url.second; URIUtils::Split(cachedurl, path, fn); std::string cmpurl = "special://thumbnails/" + cachedurl; if (path != prevPath) { while (fileIdx < files.Size()) deleteFile(); files.Clear(); thumbDir.GetDirectory(CURL("special://thumbnails/" + path), files); files.Sort(SortByPath, SortOrderAscending); prevPath = path; fileIdx = 0; } while (fileIdx < files.Size() && files.Get(fileIdx)->GetPath() < cmpurl) deleteFile(); if (fileIdx < files.Size() && files.Get(fileIdx)->GetPath() == cmpurl) fileIdx++; else { // No file for current entry; delete it CLog::Log(LOGDEBUG, "CTextureCleanupJob: deleting from Db: %d / %s", url.first, cachedurl.c_str()); db.ClearCachedTexture(url.first, cachedurl); totDbDel++; } } while (fileIdx < files.Size()) deleteFile(); CLog::Log(LOGDEBUG, "CTextureCleanupJob: %d thumbnails deleted / %d db entries removed", totFileDel, totDbDel); } return true; }
bool CPictureThumbLoader::LoadItem(CFileItem* pItem) { if (pItem->m_bIsShareOrDrive) return true; if (pItem->IsParentFolder()) return true; if (pItem->HasThumbnail() && m_regenerateThumbs) { CTextureCache::Get().ClearCachedImage(pItem->GetThumbnailImage()); CTextureDatabase db; if (db.Open()) db.ClearTextureForPath(pItem->GetPath(), "thumb"); pItem->SetThumbnailImage(""); } CStdString thumb; if (pItem->IsPicture() && !pItem->IsZIP() && !pItem->IsRAR() && !pItem->IsCBZ() && !pItem->IsCBR() && !pItem->IsPlayList()) { // load the thumb from the image file thumb = pItem->HasThumbnail() ? pItem->GetThumbnailImage() : CTextureCache::GetWrappedThumbURL(pItem->GetPath()); } else if (pItem->IsVideo() && !pItem->IsZIP() && !pItem->IsRAR() && !pItem->IsCBZ() && !pItem->IsCBR() && !pItem->IsPlayList()) { // video thumb = pItem->GetCachedVideoThumb(); if (!CFile::Exists(thumb)) { CStdString strPath, strFileName; URIUtils::Split(thumb, strPath, strFileName); CStdString autoThumb = strPath + "auto-" + strFileName; // this is abit of a hack to avoid loading zero sized images // which we know will fail. They will just display empty image // we should really have some way for the texture loader to // do fallbacks to default images for a failed image instead if (CFile::Exists(autoThumb)) { thumb = autoThumb; } else if (g_guiSettings.GetBool("myvideos.extractthumb") && g_guiSettings.GetBool("myvideos.extractflags")) { CFileItem item(*pItem); CThumbExtractor* extract = new CThumbExtractor(item, pItem->GetPath(), true, autoThumb); AddJob(extract); thumb.clear(); } } } else if (!pItem->HasThumbnail()) { // folder, zip, cbz, rar, cbr, playlist may have a previously cached image thumb = GetCachedImage(*pItem, "thumb"); } if (!thumb.IsEmpty()) { CTextureCache::Get().BackgroundCacheImage(thumb); pItem->SetThumbnailImage(thumb); } pItem->FillInDefaultIcon(); return true; }
bool CTextureUseCountJob::DoWork() { CTextureDatabase db; if (db.Open()) { db.BeginTransaction(); for (std::vector<CTextureDetails>::const_iterator i = m_textures.begin(); i != m_textures.end(); ++i) db.IncrementUseCount(*i); db.CommitTransaction(); } return true; }
bool CPictureThumbLoader::LoadItem(CFileItem* pItem) { if (pItem->m_bIsShareOrDrive) return true; if (pItem->IsParentFolder()) return true; if (pItem->HasThumbnail() && m_regenerateThumbs) { CTextureCache::Get().ClearCachedImage(pItem->GetThumbnailImage()); CTextureDatabase db; if (db.Open()) db.ClearTextureForPath(pItem->GetPath(), "thumb"); pItem->SetThumbnailImage(""); } CStdString thumb; if (pItem->IsPicture() && !pItem->IsZIP() && !pItem->IsRAR() && !pItem->IsCBZ() && !pItem->IsCBR() && !pItem->IsPlayList()) { // load the thumb from the image file thumb = pItem->HasThumbnail() ? pItem->GetThumbnailImage() : CTextureCache::GetWrappedThumbURL(pItem->GetPath()); } else if (pItem->IsVideo() && !pItem->IsZIP() && !pItem->IsRAR() && !pItem->IsCBZ() && !pItem->IsCBR() && !pItem->IsPlayList()) { // video if (!CVideoThumbLoader::FillThumb(*pItem)) { CStdString thumbURL = CVideoThumbLoader::GetEmbeddedThumbURL(*pItem); CStdString cachedThumb = CTextureCache::Get().GetCachedImage(thumbURL); if (!cachedThumb.IsEmpty()) { thumb = thumbURL; } else if (g_guiSettings.GetBool("myvideos.extractthumb") && g_guiSettings.GetBool("myvideos.extractflags")) { CFileItem item(*pItem); CThumbExtractor* extract = new CThumbExtractor(item, pItem->GetPath(), true, thumbURL); AddJob(extract); thumb.clear(); } } } else if (!pItem->HasThumbnail()) { // folder, zip, cbz, rar, cbr, playlist may have a previously cached image thumb = GetCachedImage(*pItem, "thumb"); } if (!thumb.IsEmpty()) { CTextureCache::Get().BackgroundCacheImage(thumb); pItem->SetThumbnailImage(thumb); } pItem->FillInDefaultIcon(); return true; }
bool CTextureRemoveJob::DoWork() { // clear thumb database CVariant items; CTextureDatabase db; if (db.Open()) { db.GetTextures(items, ""); db.Close(); for (unsigned int index = 0; index < items.size(); index++) { int id = (int)items[index]["textureid"].asInteger(); CTextureCache::GetInstance().ClearCachedImage(id); } CGUIDialogKaiToast::QueueNotification(CGUIDialogKaiToast::Info, g_localizeStrings.Get(617),"", 3000, true); } return true; }
bool CRepositoryUpdateJob::DoWork() { CLog::Log(LOGDEBUG, "CRepositoryUpdateJob[%s] checking for updates.", m_repo->ID().c_str()); CAddonDatabase database; database.Open(); std::string oldChecksum; if (database.GetRepoChecksum(m_repo->ID(), oldChecksum) == -1) oldChecksum = ""; std::string newChecksum; VECADDONS addons; auto status = m_repo->FetchIfChanged(oldChecksum, newChecksum, addons); database.SetLastChecked(m_repo->ID(), m_repo->Version(), CDateTime::GetCurrentDateTime().GetAsDBDateTime()); MarkFinished(); if (status == CRepository::STATUS_ERROR) return false; if (status == CRepository::STATUS_NOT_MODIFIED) { CLog::Log(LOGDEBUG, "CRepositoryUpdateJob[%s] checksum not changed.", m_repo->ID().c_str()); return true; } //Invalidate art. { CTextureDatabase textureDB; textureDB.Open(); textureDB.BeginMultipleExecute(); for (const auto& addon : addons) { AddonPtr oldAddon; if (database.GetAddon(addon->ID(), oldAddon) && addon->Version() > oldAddon->Version()) { if (!oldAddon->Icon().empty() || !oldAddon->Art().empty() || !oldAddon->Screenshots().empty()) CLog::Log(LOGDEBUG, "CRepository: invalidating cached art for '%s'", addon->ID().c_str()); if (!oldAddon->Icon().empty()) textureDB.InvalidateCachedTexture(oldAddon->Icon()); for (const auto& path : oldAddon->Screenshots()) textureDB.InvalidateCachedTexture(path); for (const auto& art : oldAddon->Art()) textureDB.InvalidateCachedTexture(art.second); } } textureDB.CommitMultipleExecute(); } database.UpdateRepositoryContent(m_repo->ID(), m_repo->Version(), newChecksum, addons); return true; }
bool CProgramThumbLoader::FillThumb(CFileItem &item) { // no need to do anything if we already have a thumb set if (CheckAndCacheThumb(item) || item.HasThumbnail()) return true; // see whether we have a cached image for this item CStdString thumb = GetCachedThumb(item); if (!thumb.IsEmpty()) { item.SetThumbnailImage(CTextureCache::Get().CheckAndCacheImage(thumb)); return true; } thumb = GetLocalThumb(item); if (!thumb.IsEmpty()) { CTextureDatabase db; if (db.Open()) db.SetTextureForPath(item.GetPath(), thumb); thumb = CTextureCache::Get().CheckAndCacheImage(thumb); } item.SetThumbnailImage(thumb); return true; }
void CPictureThumbLoader::ProcessFoldersAndArchives(CFileItem *pItem) { if (pItem->HasThumbnail()) return; CTextureDatabase db; db.Open(); if (pItem->IsCBR() || pItem->IsCBZ()) { CStdString strTBN(URIUtils::ReplaceExtension(pItem->GetPath(),".tbn")); if (CFile::Exists(strTBN)) { db.SetTextureForPath(pItem->GetPath(), "thumb", strTBN); CTextureCache::Get().BackgroundCacheImage(strTBN); pItem->SetThumbnailImage(strTBN); return; } } if ((pItem->m_bIsFolder || pItem->IsCBR() || pItem->IsCBZ()) && !pItem->m_bIsShareOrDrive && !pItem->IsParentFolder()) { // first check for a folder.jpg CStdString thumb = "folder.jpg"; CStdString strPath = pItem->GetPath(); if (pItem->IsCBR()) { URIUtils::CreateArchivePath(strPath,"rar",pItem->GetPath(),""); thumb = "cover.jpg"; } if (pItem->IsCBZ()) { URIUtils::CreateArchivePath(strPath,"zip",pItem->GetPath(),""); thumb = "cover.jpg"; } if (pItem->IsMultiPath()) strPath = CMultiPathDirectory::GetFirstPath(pItem->GetPath()); thumb = URIUtils::AddFileToFolder(strPath, thumb); if (CFile::Exists(thumb)) { db.SetTextureForPath(pItem->GetPath(), "thumb", thumb); CTextureCache::Get().BackgroundCacheImage(thumb); pItem->SetThumbnailImage(thumb); return; } if (!pItem->IsPlugin()) { // we load the directory, grab 4 random thumb files (if available) and then generate // the thumb. CFileItemList items; CDirectory::GetDirectory(strPath, items, g_settings.m_pictureExtensions, DIR_FLAG_NO_FILE_DIRS); // create the folder thumb by choosing 4 random thumbs within the folder and putting // them into one thumb. // count the number of images for (int i=0; i < items.Size();) { if (!items[i]->IsPicture() || items[i]->IsZIP() || items[i]->IsRAR() || items[i]->IsPlayList()) { items.Remove(i); } else i++; } if (items.IsEmpty()) { if (pItem->IsCBZ() || pItem->IsCBR()) { CDirectory::GetDirectory(strPath, items, g_settings.m_pictureExtensions, DIR_FLAG_NO_FILE_DIRS); for (int i=0;i<items.Size();++i) { CFileItemPtr item = items[i]; if (item->m_bIsFolder) { ProcessFoldersAndArchives(item.get()); pItem->SetThumbnailImage(items[i]->GetThumbnailImage()); pItem->SetIconImage(items[i]->GetIconImage()); return; } } } return; // no images in this folder } // randomize them items.Randomize(); if (items.Size() < 4 || pItem->IsCBR() || pItem->IsCBZ()) { // less than 4 items, so just grab the first thumb items.Sort(SORT_METHOD_LABEL, SORT_ORDER_ASC); CStdString thumb = CTextureCache::GetWrappedThumbURL(items[0]->GetPath()); db.SetTextureForPath(pItem->GetPath(), "thumb", thumb); CTextureCache::Get().BackgroundCacheImage(thumb); pItem->SetThumbnailImage(thumb); } else { // ok, now we've got the files to get the thumbs from, lets create it... // we basically load the 4 images and combine them vector<string> files; for (int thumb = 0; thumb < 4; thumb++) files.push_back(items[thumb]->GetPath()); CStdString thumb = CTextureCache::GetWrappedImageURL(pItem->GetPath(), "picturefolder"); CStdString relativeCacheFile = CTextureCache::GetCacheFile(thumb) + ".png"; if (CPicture::CreateTiledThumb(files, CTextureCache::GetCachedPath(relativeCacheFile))) { CTextureDetails details; details.file = relativeCacheFile; details.width = g_advancedSettings.m_thumbSize; details.height = g_advancedSettings.m_thumbSize; CTextureCache::Get().AddCachedTexture(thumb, details); db.SetTextureForPath(pItem->GetPath(), "thumb", thumb); pItem->SetThumbnailImage(CTextureCache::GetCachedPath(relativeCacheFile)); } } } // refill in the icon to get it to update pItem->FillInDefaultIcon(); } }
bool CGUIDialogContextMenu::OnContextButton(const std::string &type, const CFileItemPtr& item, CONTEXT_BUTTON button) { // buttons that are available on both sources and autosourced items if (!item) return false; // the rest of the operations require a valid share CMediaSource *share = GetShare(type, item.get()); if (!share) return false; switch (button) { case CONTEXT_BUTTON_EDIT_SOURCE: if (CProfilesManager::GetInstance().IsMasterProfile()) { if (!g_passwordManager.IsMasterLockUnlocked(true)) return false; } else if (!g_passwordManager.IsProfileLockUnlocked()) return false; return CGUIDialogMediaSource::ShowAndEditMediaSource(type, *share); case CONTEXT_BUTTON_REMOVE_SOURCE: { if (CProfilesManager::GetInstance().IsMasterProfile()) { if (!g_passwordManager.IsMasterLockUnlocked(true)) return false; } else { if (!CProfilesManager::GetInstance().GetCurrentProfile().canWriteSources() && !g_passwordManager.IsMasterLockUnlocked(false)) return false; if (CProfilesManager::GetInstance().GetCurrentProfile().canWriteSources() && !g_passwordManager.IsProfileLockUnlocked()) return false; } // prompt user if they want to really delete the source if (!CGUIDialogYesNo::ShowAndGetInput(CVariant{751}, CVariant{750})) return false; // check default before we delete, as deletion will kill the share object std::string defaultSource(GetDefaultShareNameByType(type)); if (!defaultSource.empty()) { if (share->strName == defaultSource) ClearDefault(type); } CMediaSourceSettings::GetInstance().DeleteSource(type, share->strName, share->strPath); return true; } case CONTEXT_BUTTON_SET_DEFAULT: if (CProfilesManager::GetInstance().GetCurrentProfile().canWriteSources() && !g_passwordManager.IsProfileLockUnlocked()) return false; else if (!g_passwordManager.IsMasterLockUnlocked(true)) return false; // make share default SetDefault(type, share->strName); return true; case CONTEXT_BUTTON_CLEAR_DEFAULT: if (CProfilesManager::GetInstance().GetCurrentProfile().canWriteSources() && !g_passwordManager.IsProfileLockUnlocked()) return false; else if (!g_passwordManager.IsMasterLockUnlocked(true)) return false; // remove share default ClearDefault(type); return true; case CONTEXT_BUTTON_SET_THUMB: { if (CProfilesManager::GetInstance().GetCurrentProfile().canWriteSources() && !g_passwordManager.IsProfileLockUnlocked()) return false; else if (!g_passwordManager.IsMasterLockUnlocked(true)) return false; // setup our thumb list CFileItemList items; // add the current thumb, if available if (!share->m_strThumbnailImage.empty()) { CFileItemPtr current(new CFileItem("thumb://Current", false)); current->SetArt("thumb", share->m_strThumbnailImage); current->SetLabel(g_localizeStrings.Get(20016)); items.Add(current); } else if (item->HasArt("thumb")) { // already have a thumb that the share doesn't know about - must be a local one, so we mayaswell reuse it. CFileItemPtr current(new CFileItem("thumb://Current", false)); current->SetArt("thumb", item->GetArt("thumb")); current->SetLabel(g_localizeStrings.Get(20016)); items.Add(current); } // see if there's a local thumb for this item std::string folderThumb = item->GetFolderThumb(); if (XFILE::CFile::Exists(folderThumb)) { CFileItemPtr local(new CFileItem("thumb://Local", false)); local->SetArt("thumb", folderThumb); local->SetLabel(g_localizeStrings.Get(20017)); items.Add(local); } // and add a "no thumb" entry as well CFileItemPtr nothumb(new CFileItem("thumb://None", false)); nothumb->SetIconImage(item->GetIconImage()); nothumb->SetLabel(g_localizeStrings.Get(20018)); items.Add(nothumb); std::string strThumb; VECSOURCES shares; g_mediaManager.GetLocalDrives(shares); if (!CGUIDialogFileBrowser::ShowAndGetImage(items, shares, g_localizeStrings.Get(1030), strThumb)) return false; if (strThumb == "thumb://Current") return true; if (strThumb == "thumb://Local") strThumb = folderThumb; if (strThumb == "thumb://None") strThumb = ""; if (!share->m_ignore) { CMediaSourceSettings::GetInstance().UpdateSource(type,share->strName,"thumbnail",strThumb); CMediaSourceSettings::GetInstance().Save(); } else if (!strThumb.empty()) { // this is some sort of an auto-share, so store in the texture database CTextureDatabase db; if (db.Open()) db.SetTextureForPath(item->GetPath(), "thumb", strThumb); } CGUIMessage msg(GUI_MSG_NOTIFY_ALL,0,0,GUI_MSG_UPDATE_SOURCES); g_windowManager.SendThreadMessage(msg); return true; } case CONTEXT_BUTTON_ADD_LOCK: { // prompt user for mastercode when changing lock settings) only for default user if (!g_passwordManager.IsMasterLockUnlocked(true)) return false; std::string strNewPassword = ""; if (!CGUIDialogLockSettings::ShowAndGetLock(share->m_iLockMode,strNewPassword)) return false; // password entry and re-entry succeeded, write out the lock data share->m_iHasLock = 2; CMediaSourceSettings::GetInstance().UpdateSource(type, share->strName, "lockcode", strNewPassword); strNewPassword = StringUtils::Format("%i", share->m_iLockMode); CMediaSourceSettings::GetInstance().UpdateSource(type, share->strName, "lockmode", strNewPassword); CMediaSourceSettings::GetInstance().UpdateSource(type, share->strName, "badpwdcount", "0"); CMediaSourceSettings::GetInstance().Save(); CGUIMessage msg(GUI_MSG_NOTIFY_ALL,0,0,GUI_MSG_UPDATE_SOURCES); g_windowManager.SendThreadMessage(msg); return true; } case CONTEXT_BUTTON_RESET_LOCK: { // prompt user for profile lock when changing lock settings if (!g_passwordManager.IsMasterLockUnlocked(true)) return false; CMediaSourceSettings::GetInstance().UpdateSource(type, share->strName, "badpwdcount", "0"); CMediaSourceSettings::GetInstance().Save(); CGUIMessage msg(GUI_MSG_NOTIFY_ALL,0,0,GUI_MSG_UPDATE_SOURCES); g_windowManager.SendThreadMessage(msg); return true; } case CONTEXT_BUTTON_REMOVE_LOCK: { if (!g_passwordManager.IsMasterLockUnlocked(true)) return false; if (!CGUIDialogYesNo::ShowAndGetInput(CVariant{12335}, CVariant{750})) return false; share->m_iHasLock = 0; CMediaSourceSettings::GetInstance().UpdateSource(type, share->strName, "lockmode", "0"); CMediaSourceSettings::GetInstance().UpdateSource(type, share->strName, "lockcode", "0"); CMediaSourceSettings::GetInstance().UpdateSource(type, share->strName, "badpwdcount", "0"); CMediaSourceSettings::GetInstance().Save(); CGUIMessage msg(GUI_MSG_NOTIFY_ALL,0,0,GUI_MSG_UPDATE_SOURCES); g_windowManager.SendThreadMessage(msg); return true; } case CONTEXT_BUTTON_REACTIVATE_LOCK: { bool maxRetryExceeded = false; if (CServiceBroker::GetSettings().GetInt(CSettings::SETTING_MASTERLOCK_MAXRETRIES) != 0) maxRetryExceeded = (share->m_iBadPwdCount >= CServiceBroker::GetSettings().GetInt(CSettings::SETTING_MASTERLOCK_MAXRETRIES)); if (!maxRetryExceeded) { // don't prompt user for mastercode when reactivating a lock g_passwordManager.LockSource(type, share->strName, true); return true; } return false; } case CONTEXT_BUTTON_CHANGE_LOCK: { if (!g_passwordManager.IsMasterLockUnlocked(true)) return false; std::string strNewPW; std::string strNewLockMode; if (CGUIDialogLockSettings::ShowAndGetLock(share->m_iLockMode,strNewPW)) strNewLockMode = StringUtils::Format("%i",share->m_iLockMode); else return false; // password ReSet and re-entry succeeded, write out the lock data CMediaSourceSettings::GetInstance().UpdateSource(type, share->strName, "lockcode", strNewPW); CMediaSourceSettings::GetInstance().UpdateSource(type, share->strName, "lockmode", strNewLockMode); CMediaSourceSettings::GetInstance().UpdateSource(type, share->strName, "badpwdcount", "0"); CMediaSourceSettings::GetInstance().Save(); CGUIMessage msg(GUI_MSG_NOTIFY_ALL,0,0,GUI_MSG_UPDATE_SOURCES); g_windowManager.SendThreadMessage(msg); return true; } default: break; } return false; }
void CThumbLoader::SetCachedImage(const CFileItem &item, const CStdString &type, const CStdString &image) { CTextureDatabase db; if (db.Open()) db.SetTextureForPath(item.GetPath(), type, image); }
void CPictureThumbLoader::ProcessFoldersAndArchives(CFileItem *pItem) { if (pItem->HasArt("thumb")) return; CTextureDatabase db; db.Open(); if ((pItem->m_bIsFolder) && !pItem->m_bIsShareOrDrive && !pItem->IsParentFolder() && !pItem->IsPath("add")) { // first check for a folder.jpg std::string thumb = "folder.jpg"; CURL pathToUrl = pItem->GetURL(); if (pItem->IsMultiPath()) pathToUrl = CURL(CMultiPathDirectory::GetFirstPath(pItem->GetPath())); thumb = URIUtils::AddFileToFolder(pathToUrl.Get(), thumb); if (CFile::Exists(thumb)) { db.SetTextureForPath(pItem->GetPath(), "thumb", thumb); CTextureCache::GetInstance().BackgroundCacheImage(thumb); pItem->SetArt("thumb", thumb); return; } // we load the directory, grab 4 random thumb files (if available) and then generate // the thumb. CFileItemList items; CDirectory::GetDirectory(pathToUrl, items, g_advancedSettings.m_pictureExtensions, DIR_FLAG_NO_FILE_DIRS); // create the folder thumb by choosing 4 random thumbs within the folder and putting // them into one thumb. // count the number of images for (int i=0; i < items.Size();) { if (!items[i]->IsPicture() || items[i]->IsPlayList()) { items.Remove(i); } else i++; } if (items.IsEmpty()) return; // no images in this folder // randomize them items.Randomize(); if (items.Size() < 4) { // less than 4 items, so just grab the first thumb items.Sort(SortByLabel, SortOrderAscending); std::string thumb = CTextureUtils::GetWrappedThumbURL(items[0]->GetPath()); db.SetTextureForPath(pItem->GetPath(), "thumb", thumb); CTextureCache::GetInstance().BackgroundCacheImage(thumb); pItem->SetArt("thumb", thumb); } else { // ok, now we've got the files to get the thumbs from, lets create it... // we basically load the 4 images and combine them vector<string> files; for (int thumb = 0; thumb < 4; thumb++) files.push_back(items[thumb]->GetPath()); std::string thumb = CTextureUtils::GetWrappedImageURL(pItem->GetPath(), "picturefolder"); std::string relativeCacheFile = CTextureCache::GetCacheFile(thumb) + ".png"; if (CPicture::CreateTiledThumb(files, CTextureCache::GetCachedPath(relativeCacheFile))) { CTextureDetails details; details.file = relativeCacheFile; details.width = g_advancedSettings.GetThumbSize(); details.height = g_advancedSettings.GetThumbSize(); CTextureCache::GetInstance().AddCachedTexture(thumb, details); db.SetTextureForPath(pItem->GetPath(), "thumb", thumb); pItem->SetArt("thumb", CTextureCache::GetCachedPath(relativeCacheFile)); } } // refill in the icon to get it to update pItem->FillInDefaultIcon(); } }
JSONRPC_STATUS CTextureOperations::GetTextures(const std::string &method, ITransportLayer *transport, IClient *client, const CVariant ¶meterObject, CVariant &result) { CFileItemList listItems; CTextureDatabase db; if (!db.Open()) return InternalError; CDatabase::Filter dbFilter; const CVariant &filter = parameterObject["filter"]; if (filter.isObject()) { CVariant xspObj(CVariant::VariantTypeObject); if (filter.isMember("field")) { xspObj["and"] = CVariant(CVariant::VariantTypeArray); xspObj["and"].push_back(filter); } else xspObj = filter; // decipher the rules CDatabaseQueryRuleCombination rule; if (!rule.Load(xspObj, &db)) return InvalidParams; dbFilter.AppendWhere(rule.GetWhereClause(db, "")); } // fetch textures from the database CVariant items = CVariant(CVariant::VariantTypeArray); if (!db.GetTextures(items, dbFilter)) return InternalError; // return only what was asked for, plus textureid CVariant prop = parameterObject["properties"]; prop.push_back("textureid"); if (!items.empty() && prop.isArray()) { std::set<std::string> fields; CVariant &item = items[0]; for (CVariant::const_iterator_map field = item.begin_map(); field != item.end_map(); ++field) { if (std::find(prop.begin_array(), prop.end_array(), field->first) == prop.end_array()) fields.insert(field->first); } // erase these fields for (CVariant::iterator_array item = items.begin_array(); item != items.end_array(); ++item) { for (std::set<std::string>::const_iterator i = fields.begin(); i != fields.end(); ++i) item->erase(*i); } if (fields.find("url") == fields.end()) { // wrap cached url to something retrieval from Files.GetFiles() for (CVariant::iterator_array item = items.begin_array(); item != items.end_array(); ++item) { CVariant &cachedUrl = (*item)["url"]; cachedUrl = CTextureUtils::GetWrappedImageURL(cachedUrl.asString()); } } } result["textures"] = items; return OK; }
bool CRepositoryUpdateJob::DoWork() { CLog::Log(LOGDEBUG, "CRepositoryUpdateJob[%s] checking for updates.", m_repo->ID().c_str()); CAddonDatabase database; database.Open(); std::string oldChecksum; if (database.GetRepoChecksum(m_repo->ID(), oldChecksum) == -1) oldChecksum = ""; std::string newChecksum; VECADDONS addons; auto status = m_repo->FetchIfChanged(oldChecksum, newChecksum, addons); database.SetLastChecked(m_repo->ID(), m_repo->Version(), CDateTime::GetCurrentDateTime().GetAsDBDateTime()); MarkFinished(); if (status == CRepository::STATUS_ERROR) return false; if (status == CRepository::STATUS_NOT_MODIFIED) { CLog::Log(LOGDEBUG, "CRepositoryUpdateJob[%s] checksum not changed.", m_repo->ID().c_str()); return true; } //Invalidate art. { CTextureDatabase textureDB; textureDB.Open(); textureDB.BeginMultipleExecute(); for (const auto& addon : addons) { AddonPtr oldAddon; if (database.GetAddon(addon->ID(), oldAddon) && addon->Version() > oldAddon->Version()) { if (!oldAddon->Icon().empty() || !oldAddon->FanArt().empty() || !oldAddon->Screenshots().empty()) CLog::Log(LOGDEBUG, "CRepository: invalidating cached art for '%s'", addon->ID().c_str()); if (!oldAddon->Icon().empty()) textureDB.InvalidateCachedTexture(oldAddon->Icon()); if (!oldAddon->FanArt().empty()) textureDB.InvalidateCachedTexture(oldAddon->Icon()); for (const auto& path : oldAddon->Screenshots()) textureDB.InvalidateCachedTexture(path); } } textureDB.CommitMultipleExecute(); } database.UpdateRepositoryContent(m_repo->ID(), m_repo->Version(), newChecksum, addons); //Notify about broken status changes for (const auto& addon : addons) { AddonPtr localAddon; if (!CAddonMgr::GetInstance().GetAddon(addon->ID(), localAddon)) continue; if (localAddon && localAddon->Version() > addon->Version()) //We have a newer version locally continue; AddonPtr oldAddon; database.GetAddon(addon->ID(), oldAddon); if (database.GetAddonVersion(addon->ID()).first > addon->Version()) //Newer version in db (ie. in a different repo) continue; std::string broken = addon->Broken(); bool isBroken = !addon->Broken().empty(); bool isBrokenInDb = oldAddon && !oldAddon->Broken().empty(); if (isBroken && !isBrokenInDb) { //newly broken if (HELPERS::ShowYesNoDialogLines(CVariant{addon->Name()}, CVariant{24096}, CVariant{24097}, CVariant{""}) == DialogResponse::YES) { CAddonMgr::GetInstance().DisableAddon(addon->ID()); } CLog::Log(LOGDEBUG, "CRepositoryUpdateJob[%s] addon '%s' marked broken. reason: \"%s\"", m_repo->ID().c_str(), addon->ID().c_str(), broken.c_str()); CEventLog::GetInstance().Add(EventPtr(new CAddonManagementEvent(addon, 24096))); } else if (!isBroken && isBrokenInDb) { //Unbroken CLog::Log(LOGDEBUG, "CRepositoryUpdateJob[%s] addon '%s' unbroken", m_repo->ID().c_str(), addon->ID().c_str()); } } return true; }
bool CVideoLibraryRefreshingJob::Work(CVideoDatabase &db) { if (m_item == nullptr) return false; // determine the scraper for the item's path VIDEO::SScanSettings scanSettings; ADDON::ScraperPtr scraper = db.GetScraperForPath(m_item->GetPath(), scanSettings); if (scraper == nullptr) return false; // copy the scraper in case we need it again ADDON::ScraperPtr originalScraper(scraper); // get the item's correct title std::string itemTitle = m_searchTitle; if (itemTitle.empty()) itemTitle = m_item->GetMovieName(scanSettings.parent_name); CScraperUrl scraperUrl; VIDEO::CVideoInfoScanner scanner; bool needsRefresh = m_forceRefresh; bool hasDetails = false; bool ignoreNfo = m_ignoreNfo; // run this in a loop in case we need to refresh again bool failure = false; do { if (!ignoreNfo) { // check if there's an NFO for the item CNfoFile::NFOResult nfoResult = scanner.CheckForNFOFile(m_item.get(), scanSettings.parent_name_root, scraper, scraperUrl); // if there's no NFO remember it in case we have to refresh again if (nfoResult == CNfoFile::ERROR_NFO) ignoreNfo = true; else if (nfoResult != CNfoFile::NO_NFO) hasDetails = true; // if we are performing a forced refresh ask the user to choose between using a valid NFO and a valid scraper if (needsRefresh && IsModal() && !scraper->IsNoop() && (nfoResult == CNfoFile::URL_NFO || nfoResult == CNfoFile::COMBINED_NFO || nfoResult == CNfoFile::FULL_NFO)) { int heading = 20159; if (scraper->Content() == CONTENT_MOVIES) heading = 13346; else if (scraper->Content() == CONTENT_TVSHOWS) heading = m_item->m_bIsFolder ? 20351 : 20352; else if (scraper->Content() == CONTENT_MUSICVIDEOS) heading = 20393; if (CGUIDialogYesNo::ShowAndGetInput(heading, 20446)) { hasDetails = false; ignoreNfo = true; scraperUrl.Clear(); scraper = originalScraper; } } } // no need to re-fetch the episode guide for episodes if (scraper->Content() == CONTENT_TVSHOWS && !m_item->m_bIsFolder) hasDetails = true; // if we don't have an url or need to refresh anyway do the web search if (!hasDetails && (needsRefresh || scraperUrl.m_url.empty())) { SetTitle(StringUtils::Format(g_localizeStrings.Get(197).c_str(), scraper->Name().c_str())); SetText(itemTitle); SetProgress(0); // clear any cached data from the scraper scraper->ClearCache(); // create the info downloader for the scraper CVideoInfoDownloader infoDownloader(scraper); // try to find a matching item MOVIELIST itemResultList; int result = infoDownloader.FindMovie(itemTitle, itemResultList, GetProgressDialog()); // close the progress dialog MarkFinished(); if (result > 0) { // there are multiple matches for the item if (!itemResultList.empty()) { // choose the first match if (!IsModal()) scraperUrl = itemResultList.at(0); else { // ask the user what to do CGUIDialogSelect* selectDialog = static_cast<CGUIDialogSelect*>(g_windowManager.GetWindow(WINDOW_DIALOG_SELECT)); selectDialog->Reset(); selectDialog->SetHeading(scraper->Content() == CONTENT_TVSHOWS ? 20356 : 196); for (const auto& itemResult : itemResultList) selectDialog->Add(itemResult.strTitle); selectDialog->EnableButton(true, 413); // "Manual" selectDialog->Open(); // check if the user has chosen one of the results int selectedItem = selectDialog->GetSelectedItem(); if (selectedItem >= 0) scraperUrl = itemResultList.at(selectedItem); // the user hasn't chosen one of the results and but has chosen to manually enter a title to use else if (selectDialog->IsButtonPressed()) { // ask the user to input a title to use if (!CGUIKeyboardFactory::ShowAndGetInput(itemTitle, g_localizeStrings.Get(scraper->Content() == CONTENT_TVSHOWS ? 20357 : 16009), false)) return false; // go through the whole process again needsRefresh = true; continue; } // nothing else we can do else return false; } CLog::Log(LOGDEBUG, "CVideoLibraryRefreshingJob: user selected item '%s' with URL '%s'", scraperUrl.strTitle.c_str(), scraperUrl.m_url.at(0).m_url.c_str()); } } else if (result < 0 || !VIDEO::CVideoInfoScanner::DownloadFailed(GetProgressDialog())) { failure = true; break; } } // if the URL is still empty, check whether or not we're allowed // to prompt and ask the user to input a new search title if (!hasDetails && scraperUrl.m_url.empty()) { if (IsModal()) { // ask the user to input a title to use if (!CGUIKeyboardFactory::ShowAndGetInput(itemTitle, g_localizeStrings.Get(scraper->Content() == CONTENT_TVSHOWS ? 20357 : 16009), false)) return false; // go through the whole process again needsRefresh = true; continue; } // nothing else we can do failure = true; break; } // before we start downloading all the necessary information cleanup any existing artwork and hashes CTextureDatabase textureDb; if (textureDb.Open()) { for (const auto& artwork : m_item->GetArt()) textureDb.InvalidateCachedTexture(artwork.second); textureDb.Close(); } m_item->ClearArt(); // put together the list of items to refresh std::string path = m_item->GetPath(); CFileItemList items; if (m_item->HasVideoInfoTag() && m_item->GetVideoInfoTag()->m_iDbId > 0) { // for a tvshow we need to handle all paths of it std::vector<std::string> tvshowPaths; if (CMediaTypes::IsMediaType(m_item->GetVideoInfoTag()->m_type, MediaTypeTvShow) && m_refreshAll && db.GetPathsLinkedToTvShow(m_item->GetVideoInfoTag()->m_iDbId, tvshowPaths)) { for (const auto& tvshowPath : tvshowPaths) { CFileItemPtr tvshowItem(new CFileItem(*m_item->GetVideoInfoTag())); tvshowItem->SetPath(tvshowPath); items.Add(tvshowItem); } } // otherwise just add a copy of the item else items.Add(CFileItemPtr(new CFileItem(*m_item->GetVideoInfoTag()))); // update the path to the real path (instead of a videodb:// one) path = m_item->GetVideoInfoTag()->m_strPath; } else items.Add(CFileItemPtr(new CFileItem(*m_item))); // set the proper path of the list of items to lookup items.SetPath(m_item->m_bIsFolder ? URIUtils::GetParentPath(path) : URIUtils::GetDirectory(path)); int headingLabel = 198; if (scraper->Content() == CONTENT_TVSHOWS) { if (m_item->m_bIsFolder) headingLabel = 20353; else headingLabel = 20361; } else if (scraper->Content() == CONTENT_MUSICVIDEOS) headingLabel = 20394; // prepare the progress dialog for downloading all the necessary information SetTitle(g_localizeStrings.Get(headingLabel)); SetText(scraperUrl.strTitle); SetProgress(0); // remove any existing data for the item we're going to refresh if (m_item->GetVideoInfoTag()->m_iDbId > 0) { int dbId = m_item->GetVideoInfoTag()->m_iDbId; if (scraper->Content() == CONTENT_MOVIES) db.DeleteMovie(dbId); else if (scraper->Content() == CONTENT_MUSICVIDEOS) db.DeleteMusicVideo(dbId); else if (scraper->Content() == CONTENT_TVSHOWS) { if (!m_item->m_bIsFolder) db.DeleteEpisode(dbId); else if (m_refreshAll) db.DeleteTvShow(dbId); else db.DeleteDetailsForTvShow(dbId); } } // finally download the information for the item if (!scanner.RetrieveVideoInfo(items, scanSettings.parent_name, scraper->Content(), !ignoreNfo, &scraperUrl, m_refreshAll, GetProgressDialog())) { // something went wrong MarkFinished(); // check if the user cancelled if (!IsCancelled() && IsModal()) CGUIDialogOK::ShowAndGetInput(195, itemTitle); return false; } // retrieve the updated information from the database if (scraper->Content() == CONTENT_MOVIES) db.GetMovieInfo(m_item->GetPath(), *m_item->GetVideoInfoTag()); else if (scraper->Content() == CONTENT_MUSICVIDEOS) db.GetMusicVideoInfo(m_item->GetPath(), *m_item->GetVideoInfoTag()); else if (scraper->Content() == CONTENT_TVSHOWS) { // update tvshow info to get updated episode numbers if (m_item->m_bIsFolder) db.GetTvShowInfo(m_item->GetPath(), *m_item->GetVideoInfoTag()); else db.GetEpisodeInfo(m_item->GetPath(), *m_item->GetVideoInfoTag()); } // we're finally done MarkFinished(); break; } while (needsRefresh); if (failure && IsModal()) CGUIDialogOK::ShowAndGetInput(195, itemTitle); return true; }
bool CGUIDialogContextMenu::OnContextButton(const CStdString &type, const CFileItemPtr item, CONTEXT_BUTTON button) { // Add Source doesn't require a valid share if (button == CONTEXT_BUTTON_ADD_SOURCE) { if (g_settings.IsMasterUser()) { if (!g_passwordManager.IsMasterLockUnlocked(true)) return false; } else if (!g_settings.GetCurrentProfile().canWriteSources() && !g_passwordManager.IsProfileLockUnlocked()) return false; return CGUIDialogMediaSource::ShowAndAddMediaSource(type); } // buttons that are available on both sources and autosourced items if (!item) return false; switch (button) { case CONTEXT_BUTTON_EJECT_DRIVE: return g_mediaManager.Eject(item->m_strPath); #ifdef HAS_DVD_DRIVE case CONTEXT_BUTTON_PLAY_DISC: return MEDIA_DETECT::CAutorun::PlayDisc(); case CONTEXT_BUTTON_EJECT_DISC: #ifdef _WIN32 CWIN32Util::ToggleTray(g_mediaManager.TranslateDevicePath(item->m_strPath)[0]); #else CIoSupport::ToggleTray(); #endif #endif return true; default: break; } // the rest of the operations require a valid share CMediaSource *share = GetShare(type, item.get()); if (!share) return false; switch (button) { case CONTEXT_BUTTON_EDIT_SOURCE: if (g_settings.IsMasterUser()) { if (!g_passwordManager.IsMasterLockUnlocked(true)) return false; } else if (!g_passwordManager.IsProfileLockUnlocked()) return false; return CGUIDialogMediaSource::ShowAndEditMediaSource(type, *share); case CONTEXT_BUTTON_REMOVE_SOURCE: { if (g_settings.IsMasterUser()) { if (!g_passwordManager.IsMasterLockUnlocked(true)) return false; } else { if (!g_settings.GetCurrentProfile().canWriteSources() && !g_passwordManager.IsMasterLockUnlocked(false)) return false; if (g_settings.GetCurrentProfile().canWriteSources() && !g_passwordManager.IsProfileLockUnlocked()) return false; } // prompt user if they want to really delete the source if (CGUIDialogYesNo::ShowAndGetInput(751, 0, 750, 0)) { // check default before we delete, as deletion will kill the share object CStdString defaultSource(GetDefaultShareNameByType(type)); if (!defaultSource.IsEmpty()) { if (share->strName.Equals(defaultSource)) ClearDefault(type); } g_settings.DeleteSource(type, share->strName, share->strPath); } return true; } case CONTEXT_BUTTON_SET_DEFAULT: if (g_settings.GetCurrentProfile().canWriteSources() && !g_passwordManager.IsProfileLockUnlocked()) return false; else if (!g_passwordManager.IsMasterLockUnlocked(true)) return false; // make share default SetDefault(type, share->strName); return true; case CONTEXT_BUTTON_CLEAR_DEFAULT: if (g_settings.GetCurrentProfile().canWriteSources() && !g_passwordManager.IsProfileLockUnlocked()) return false; else if (!g_passwordManager.IsMasterLockUnlocked(true)) return false; // remove share default ClearDefault(type); return true; case CONTEXT_BUTTON_SET_THUMB: { if (g_settings.GetCurrentProfile().canWriteSources() && !g_passwordManager.IsProfileLockUnlocked()) return false; else if (!g_passwordManager.IsMasterLockUnlocked(true)) return false; // setup our thumb list CFileItemList items; // add the current thumb, if available if (!share->m_strThumbnailImage.IsEmpty()) { CFileItemPtr current(new CFileItem("thumb://Current", false)); current->SetThumbnailImage(share->m_strThumbnailImage); current->SetLabel(g_localizeStrings.Get(20016)); items.Add(current); } else if (item->HasThumbnail()) { // already have a thumb that the share doesn't know about - must be a local one, so we mayaswell reuse it. CFileItemPtr current(new CFileItem("thumb://Current", false)); current->SetThumbnailImage(item->GetThumbnailImage()); current->SetLabel(g_localizeStrings.Get(20016)); items.Add(current); } // see if there's a local thumb for this item CStdString folderThumb = item->GetFolderThumb(); if (XFILE::CFile::Exists(folderThumb)) { CFileItemPtr local(new CFileItem("thumb://Local", false)); local->SetThumbnailImage(folderThumb); local->SetLabel(g_localizeStrings.Get(20017)); items.Add(local); } // and add a "no thumb" entry as well CFileItemPtr nothumb(new CFileItem("thumb://None", false)); nothumb->SetIconImage(item->GetIconImage()); nothumb->SetLabel(g_localizeStrings.Get(20018)); items.Add(nothumb); CStdString strThumb; VECSOURCES shares; g_mediaManager.GetLocalDrives(shares); if (!CGUIDialogFileBrowser::ShowAndGetImage(items, shares, g_localizeStrings.Get(1030), strThumb)) return false; if (strThumb == "thumb://Current") return true; if (strThumb == "thumb://Local") strThumb = folderThumb; if (strThumb == "thumb://None") strThumb = ""; if (!share->m_ignore) { g_settings.UpdateSource(type,share->strName,"thumbnail",strThumb); g_settings.SaveSources(); } else if (!strThumb.IsEmpty()) { // this is some sort of an auto-share, so we have to cache it based on the criteria we use to retrieve them CStdString cachedThumb; if (type == "music") { cachedThumb = item->m_strPath; URIUtils::RemoveSlashAtEnd(cachedThumb); cachedThumb = CUtil::GetCachedMusicThumb(cachedThumb); } else if (type == "video") cachedThumb = item->GetCachedVideoThumb(); else // assume "programs" { // store the thumb for this share CTextureDatabase db; if (db.Open()) { cachedThumb = CTextureCache::GetUniqueImage(item->m_strPath, URIUtils::GetExtension(strThumb)); db.SetTextureForPath(item->m_strPath, cachedThumb); } } XFILE::CFile::Cache(strThumb, cachedThumb); } CGUIMessage msg(GUI_MSG_NOTIFY_ALL,0,0,GUI_MSG_UPDATE_SOURCES); g_windowManager.SendThreadMessage(msg); return true; } case CONTEXT_BUTTON_ADD_LOCK: { // prompt user for mastercode when changing lock settings) only for default user if (!g_passwordManager.IsMasterLockUnlocked(true)) return false; CStdString strNewPassword = ""; if (!CGUIDialogLockSettings::ShowAndGetLock(share->m_iLockMode,strNewPassword)) return false; // password entry and re-entry succeeded, write out the lock data share->m_iHasLock = 2; g_settings.UpdateSource(type, share->strName, "lockcode", strNewPassword); strNewPassword.Format("%i",share->m_iLockMode); g_settings.UpdateSource(type, share->strName, "lockmode", strNewPassword); g_settings.UpdateSource(type, share->strName, "badpwdcount", "0"); g_settings.SaveSources(); CGUIMessage msg(GUI_MSG_NOTIFY_ALL,0,0,GUI_MSG_UPDATE_SOURCES); g_windowManager.SendThreadMessage(msg); return true; } case CONTEXT_BUTTON_RESET_LOCK: { // prompt user for profile lock when changing lock settings if (!g_passwordManager.IsMasterLockUnlocked(true)) return false; g_settings.UpdateSource(type, share->strName, "badpwdcount", "0"); g_settings.SaveSources(); CGUIMessage msg(GUI_MSG_NOTIFY_ALL,0,0,GUI_MSG_UPDATE_SOURCES); g_windowManager.SendThreadMessage(msg); return true; } case CONTEXT_BUTTON_REMOVE_LOCK: { if (!g_passwordManager.IsMasterLockUnlocked(true)) return false; if (!CGUIDialogYesNo::ShowAndGetInput(12335, 0, 750, 0)) return false; share->m_iHasLock = 0; g_settings.UpdateSource(type, share->strName, "lockmode", "0"); g_settings.UpdateSource(type, share->strName, "lockcode", "0"); g_settings.UpdateSource(type, share->strName, "badpwdcount", "0"); g_settings.SaveSources(); CGUIMessage msg(GUI_MSG_NOTIFY_ALL,0,0,GUI_MSG_UPDATE_SOURCES); g_windowManager.SendThreadMessage(msg); return true; } case CONTEXT_BUTTON_REACTIVATE_LOCK: { bool maxRetryExceeded = false; if (g_guiSettings.GetInt("masterlock.maxretries") != 0) maxRetryExceeded = (share->m_iBadPwdCount >= g_guiSettings.GetInt("masterlock.maxretries")); if (!maxRetryExceeded) { // don't prompt user for mastercode when reactivating a lock g_passwordManager.LockSource(type, share->strName, true); return true; } return false; } case CONTEXT_BUTTON_CHANGE_LOCK: { if (!g_passwordManager.IsMasterLockUnlocked(true)) return false; CStdString strNewPW; CStdString strNewLockMode; if (CGUIDialogLockSettings::ShowAndGetLock(share->m_iLockMode,strNewPW)) strNewLockMode.Format("%i",share->m_iLockMode); else return false; // password ReSet and re-entry succeeded, write out the lock data g_settings.UpdateSource(type, share->strName, "lockcode", strNewPW); g_settings.UpdateSource(type, share->strName, "lockmode", strNewLockMode); g_settings.UpdateSource(type, share->strName, "badpwdcount", "0"); g_settings.SaveSources(); CGUIMessage msg(GUI_MSG_NOTIFY_ALL,0,0,GUI_MSG_UPDATE_SOURCES); g_windowManager.SendThreadMessage(msg); return true; } default: break; } return false; }