void CAddonInstaller::Install(const std::string& addonId, const AddonVersion& version, const std::string& repoId) { CLog::Log(LOGDEBUG, "CAddonInstaller: installing '%s' version '%s' from repository '%s'", addonId.c_str(), version.asString().c_str(), repoId.c_str()); AddonPtr addon; CAddonDatabase database; if (!database.Open() || !database.GetAddon(addonId, version, repoId, addon)) return; AddonPtr repo; if (!CAddonMgr::GetInstance().GetAddon(repoId, repo, ADDON_REPOSITORY)) return; std::string hash; if (!std::static_pointer_cast<CRepository>(repo)->GetAddonHash(addon, hash)) return; DoInstall(addon, std::static_pointer_cast<CRepository>(repo), hash, true, false); }
void CAddonInstaller::GetInstallList(VECADDONS &addons) const { CSingleLock lock(m_critSection); std::vector<std::string> addonIDs; for (JobMap::const_iterator i = m_downloadJobs.begin(); i != m_downloadJobs.end(); ++i) { if (i->second.jobID) addonIDs.push_back(i->first); } lock.Leave(); CAddonDatabase database; database.Open(); for (std::vector<std::string>::iterator it = addonIDs.begin(); it != addonIDs.end(); ++it) { AddonPtr addon; if (database.GetAddon(*it, addon)) addons.push_back(addon); } }
void CGUIDialogAddonInfo::OnEnable(bool enable) { if (!m_localAddon.get()) return; CStdString xbmcPath = _P("special://xbmc/addons"); CAddonDatabase database; database.Open(); if (m_localAddon->Type() == ADDON_PVRDLL && m_localAddon->Path().Left(xbmcPath.size()).Equals(xbmcPath)) database.EnableSystemPVRAddon(m_localAddon->ID(), enable); else database.DisableAddon(m_localAddon->ID(), !enable); if (m_localAddon->Type() == ADDON_PVRDLL && enable) g_application.StartPVRManager(); SetItem(m_item); UpdateControls(); g_windowManager.SendMessage(GUI_MSG_NOTIFY_ALL, 0, 0, GUI_MSG_UPDATE); }
CDateTime CRepositoryUpdater::LastUpdated() const { VECADDONS repos; if (!CAddonMgr::GetInstance().GetAddons(ADDON_REPOSITORY, repos) || repos.empty()) return CDateTime(); CAddonDatabase db; db.Open(); std::vector<CDateTime> updateTimes; std::transform(repos.begin(), repos.end(), std::back_inserter(updateTimes), [&](const AddonPtr& repo) { auto lastCheck = db.LastChecked(repo->ID()); if (lastCheck.first.IsValid() && lastCheck.second == repo->Version()) return lastCheck.first; return CDateTime(); }); return *std::min_element(updateTimes.begin(), updateTimes.end()); }
void CAddonInstaller::UpdateRepos(bool force, bool wait) { CSingleLock lock(m_critSection); if (m_repoUpdateJob) { if (wait) { // wait for our job to complete lock.Leave(); CLog::Log(LOGDEBUG, "%s - waiting for repository update job to finish...", __FUNCTION__); m_repoUpdateDone.Wait(); } return; } // don't run repo update jobs while on the login screen which runs under the master profile if((g_windowManager.GetActiveWindow() & WINDOW_ID_MASK) == WINDOW_LOGIN_SCREEN) return; if (!force && m_repoUpdateWatch.IsRunning() && m_repoUpdateWatch.GetElapsedSeconds() < 600) return; m_repoUpdateWatch.StartZero(); VECADDONS addons; CAddonMgr::Get().GetAddons(ADDON_REPOSITORY,addons); for (unsigned int i=0;i<addons.size();++i) { CAddonDatabase database; database.Open(); CDateTime lastUpdate = database.GetRepoTimestamp(addons[i]->ID()); if (force || !lastUpdate.IsValid() || lastUpdate + CDateTimeSpan(0,6,0,0) < CDateTime::GetCurrentDateTime()) { CLog::Log(LOGDEBUG,"Checking repositories for updates (triggered by %s)",addons[i]->Name().c_str()); m_repoUpdateJob = CJobManager::GetInstance().AddJob(new CRepositoryUpdateJob(addons), this); if (wait) { // wait for our job to complete lock.Leave(); CLog::Log(LOGDEBUG, "%s - waiting for this repository update job to finish...", __FUNCTION__); m_repoUpdateDone.Wait(); } return; } } }
bool CAddonUnInstallJob::DoWork() { ADDON::OnPreUnInstall(m_addon); //TODO: looks broken. it just calls the repo with the most recent version, not the owner RepositoryPtr repoPtr; CAddonInstaller::GetRepoForAddon(m_addon->ID(), repoPtr); if (repoPtr != NULL && !repoPtr->Props().libname.empty()) { CFileItemList dummy; std::string s = StringUtils::Format("plugin://%s/?action=uninstall&package=%s", repoPtr->ID().c_str(), m_addon->ID().c_str()); if (!CDirectory::GetDirectory(s, dummy)) return false; } else { //Unregister addon with the manager to ensure nothing tries //to interact with it while we are uninstalling. CAddonMgr::GetInstance().UnregisterAddon(m_addon->ID()); if (!DeleteAddon(m_addon->Path())) { CLog::Log(LOGERROR, "CAddonUnInstallJob[%s]: could not delete addon data.", m_addon->ID().c_str()); return false; } } ClearFavourites(); AddonPtr addon; CAddonDatabase database; // try to get the addon object from the repository as the local one does not exist anymore // if that doesn't work fall back to the local one if (!database.Open() || !database.GetAddon(m_addon->ID(), addon) || addon == NULL) addon = m_addon; CEventLog::GetInstance().Add(EventPtr(new CAddonManagementEvent(addon, 24144))); ADDON::OnPostUnInstall(m_addon); return true; }
void CGUIDialogAddonInfo::OnRollback() { if (!g_passwordManager.CheckMenuLock(WINDOW_ADDON_BROWSER)) return; CGUIDialogContextMenu* dlg = (CGUIDialogContextMenu*)g_windowManager.GetWindow(WINDOW_DIALOG_CONTEXT_MENU); CAddonDatabase database; database.Open(); CContextButtons buttons; for (unsigned int i=0;i<m_rollbackVersions.size();++i) { std::string label(m_rollbackVersions[i]); if (m_rollbackVersions[i] == m_localAddon->Version().asString()) label += " "+g_localizeStrings.Get(24094); if (database.IsAddonBlacklisted(m_localAddon->ID(),label)) label += " "+g_localizeStrings.Get(24095); buttons.Add(i,label); } int choice; if ((choice=dlg->ShowAndGetChoice(buttons)) > -1) { // blacklist everything newer for (unsigned int j=choice+1;j<m_rollbackVersions.size();++j) database.BlacklistAddon(m_localAddon->ID(),m_rollbackVersions[j]); std::string path = "special://home/addons/packages/"; path += m_localAddon->ID()+"-"+m_rollbackVersions[choice]+".zip"; //FIXME: this is probably broken // needed as cpluff won't downgrade if (!m_localAddon->IsType(ADDON_SERVICE)) //we will handle this for service addons in CAddonInstallJob::OnPostInstall CAddonMgr::Get().UnregisterAddon(m_localAddon->ID()); CAddonInstaller::Get().InstallFromZip(path); database.RemoveAddonFromBlacklist(m_localAddon->ID(),m_rollbackVersions[choice]); Close(); } }
bool CAddonInstaller::CheckDependencies(const AddonPtr &addon, std::vector<std::string>& preDeps, CAddonDatabase &database) { if (!addon.get()) return true; // a NULL addon has no dependencies ADDONDEPS deps = addon->GetDeps(); database.Open(); for (ADDONDEPS::const_iterator i = deps.begin(); i != deps.end(); ++i) { const CStdString &addonID = i->first; const AddonVersion &version = i->second.first; bool optional = i->second.second; AddonPtr dep; bool haveAddon = CAddonMgr::Get().GetAddon(addonID, dep); if ((haveAddon && !dep->MeetsVersion(version)) || (!haveAddon && !optional)) { // we have it but our version isn't good enough, or we don't have it and we need it if (!database.GetAddon(addonID, dep) || !dep->MeetsVersion(version)) { // we don't have it in a repo, or we have it but the version isn't good enough, so dep isn't satisfied. CLog::Log(LOGDEBUG, "Addon %s requires %s version %s which is not available", addon->ID().c_str(), addonID.c_str(), version.c_str()); database.Close(); return false; } } // at this point we have our dep, or the dep is optional (and we don't have it) so check that it's OK as well // TODO: should we assume that installed deps are OK? if (dep && std::find(preDeps.begin(), preDeps.end(), dep->ID()) == preDeps.end()) { if (!CheckDependencies(dep, preDeps, database)) { database.Close(); return false; } preDeps.push_back(dep->ID()); } } database.Close(); return true; }
void CAddonInstallJob::ReportInstallError(const std::string& addonID, const std::string& fileName, const std::string& message /* = "" */) { AddonPtr addon; CAddonDatabase database; if (database.Open()) { database.GetAddon(addonID, addon); database.Close(); } MarkFinished(); std::string msg = message; EventPtr activity; if (addon != NULL) { AddonPtr addon2; CAddonMgr::GetInstance().GetAddon(addonID, addon2); if (msg.empty()) msg = g_localizeStrings.Get(addon2 != NULL ? 113 : 114); activity = EventPtr(new CAddonManagementEvent(addon, EventLevelError, msg)); if (IsModal()) CGUIDialogOK::ShowAndGetInput(CVariant{m_addon->Name()}, CVariant{msg}); } else { activity = EventPtr(new CNotificationEvent(EventLevelError, 24045, !msg.empty() ? msg : StringUtils::Format(g_localizeStrings.Get(24143).c_str(), fileName.c_str()))); if (IsModal()) CGUIDialogOK::ShowAndGetInput(CVariant{fileName}, CVariant{msg}); } CEventLog::GetInstance().Add(activity, !IsModal(), false); }
AddonPtr CAddonInstallJob::GetRepoForAddon(const AddonPtr& addon) { AddonPtr repoPtr; CAddonDatabase database; if (!database.Open()) return repoPtr; std::string repo; if (!database.GetRepoForAddon(addon->ID(), repo)) return repoPtr; if (!CAddonMgr::GetInstance().GetAddon(repo, repoPtr)) return repoPtr; if (std::dynamic_pointer_cast<CRepository>(repoPtr) == NULL) { repoPtr.reset(); return repoPtr; } return repoPtr; }
bool CAddonUnInstallJob::DoWork() { ADDON::OnPreUnInstall(m_addon); //Unregister addon with the manager to ensure nothing tries //to interact with it while we are uninstalling. if (!CAddonMgr::GetInstance().UnloadAddon(m_addon)) { CLog::Log(LOGERROR, "CAddonUnInstallJob[%s]: failed to unload addon.", m_addon->ID().c_str()); return false; } CFilesystemInstaller fsInstaller; if (!fsInstaller.UnInstallFromFilesystem(m_addon->Path())) { CLog::Log(LOGERROR, "CAddonUnInstallJob[%s]: could not delete addon data.", m_addon->ID().c_str()); return false; } ClearFavourites(); if (m_removeData) CFileUtils::DeleteItem("special://profile/addon_data/"+m_addon->ID()+"/", true); AddonPtr addon; CAddonDatabase database; // try to get the addon object from the repository as the local one does not exist anymore // if that doesn't work fall back to the local one if (!database.Open() || !database.GetAddon(m_addon->ID(), addon) || addon == NULL) addon = m_addon; CEventLog::GetInstance().Add(EventPtr(new CAddonManagementEvent(addon, 24144))); CAddonMgr::GetInstance().OnPostUnInstall(m_addon->ID()); database.OnPostUnInstall(m_addon->ID()); ADDON::OnPostUnInstall(m_addon); return true; }
void CAddonUnInstallJob::OnPostUnInstall() { if (m_addon->Type() == ADDON_REPOSITORY) { CAddonDatabase database; database.Open(); database.DeleteRepository(m_addon->ID()); } bool bSave(false); CFileItemList items; CFavourites::Load(items); for (int i=0; i < items.Size(); ++i) { if (items[i]->GetPath().Find(m_addon->ID()) > -1) { items.Remove(items[i].get()); bSave = true; } } if (bSave) CFavourites::Save(items); }
void CAddonInstallJob::ReportInstallError(const CStdString& addonID, const CStdString& fileName) { AddonPtr addon; CAddonDatabase database; database.Open(); database.GetAddon(addonID, addon); if (addon) { AddonPtr addon2; CAddonMgr::Get().GetAddon(addonID, addon2); CGUIDialogKaiToast::QueueNotification(addon->Icon(), addon->Name(), g_localizeStrings.Get(addon2 ? 113 : 114), TOAST_DISPLAY_TIME, false); } else { CGUIDialogKaiToast::QueueNotification(CGUIDialogKaiToast::Error, fileName, g_localizeStrings.Get(114), TOAST_DISPLAY_TIME, false); } }
bool CAddonInstaller::Install(const CStdString &addonID, bool force, const CStdString &referer, bool background) { AddonPtr addon; bool addonInstalled = CAddonMgr::Get().GetAddon(addonID, addon, ADDON_UNKNOWN, false); if (addonInstalled && !force) return true; // check whether we have it available in a repository CAddonDatabase database; database.Open(); if (database.GetAddon(addonID, addon)) { CStdString repo; database.GetRepoForAddon(addonID,repo); AddonPtr ptr; CAddonMgr::Get().GetAddon(repo,ptr); RepositoryPtr therepo = boost::dynamic_pointer_cast<CRepository>(ptr); CStdString hash; if (therepo) hash = therepo->GetAddonHash(addon); return DoInstall(addon, hash, addonInstalled, referer, background); } return false; }
bool CLangInfo::SetLanguage(bool& fallback, const std::string &strLanguage /* = "" */, bool reloadServices /* = true */) { fallback = false; std::string language = strLanguage; if (language.empty()) { language = CSettings::GetInstance().GetString(CSettings::SETTING_LOCALE_LANGUAGE); if (language.empty()) { CLog::Log(LOGFATAL, "CLangInfo: cannot load empty language."); return false; } } LanguageResourcePtr languageAddon = GetLanguageAddon(language); if (languageAddon == NULL) { CLog::Log(LOGWARNING, "CLangInfo: unable to load language \"%s\". Trying to determine matching language addon...", language.c_str()); // we may have to fall back to the default language std::string defaultLanguage = static_cast<CSettingString*>(CSettings::GetInstance().GetSetting(CSettings::SETTING_LOCALE_LANGUAGE))->GetDefault(); std::string newLanguage = defaultLanguage; // try to determine a language addon matching the given language in name if (!ADDON::CLanguageResource::FindLanguageAddonByName(language, newLanguage)) { CLog::Log(LOGWARNING, "CLangInfo: unable to find an installed language addon matching \"%s\". Trying to find an installable language...", language.c_str()); bool foundMatchingAddon = false; CAddonDatabase addondb; if (addondb.Open()) { // update the addon repositories to check if there's a matching language addon available for download CAddonInstaller::GetInstance().UpdateRepos(true, true); ADDON::VECADDONS languageAddons; if (addondb.GetAddons(languageAddons, ADDON::ADDON_RESOURCE_LANGUAGE) && !languageAddons.empty()) { // try to get the proper language addon by its name from all available language addons if (ADDON::CLanguageResource::FindLanguageAddonByName(language, newLanguage, languageAddons)) { if (CAddonInstaller::GetInstance().Install(newLanguage, true, "", false, false)) { CLog::Log(LOGINFO, "CLangInfo: successfully installed language addon \"%s\" matching current language \"%s\"", newLanguage.c_str(), language.c_str()); foundMatchingAddon = true; } else CLog::Log(LOGERROR, "CLangInfo: failed to installed language addon \"%s\" matching current language \"%s\"", newLanguage.c_str(), language.c_str()); } else CLog::Log(LOGERROR, "CLangInfo: unable to match old language \"%s\" to any available language addon", language.c_str()); } else CLog::Log(LOGERROR, "CLangInfo: no language addons available to match against \"%s\"", language.c_str()); } else CLog::Log(LOGERROR, "CLangInfo: unable to open addon database to look for a language addon matching \"%s\"", language.c_str()); // if the new language matches the default language we are loading the // default language as a fallback if (!foundMatchingAddon && newLanguage == defaultLanguage) { CLog::Log(LOGINFO, "CLangInfo: fall back to the default language \"%s\"", defaultLanguage.c_str()); fallback = true; } } if (!CSettings::GetInstance().SetString(CSettings::SETTING_LOCALE_LANGUAGE, newLanguage)) return false; CSettings::GetInstance().Save(); return true; } CLog::Log(LOGINFO, "CLangInfo: loading %s language information...", language.c_str()); if (!Load(language)) { CLog::LogF(LOGFATAL, "CLangInfo: failed to load %s language information", language.c_str()); return false; } CLog::Log(LOGINFO, "CLangInfo: loading %s language strings...", language.c_str()); if (!g_localizeStrings.Load(GetLanguagePath(), language)) { CLog::LogF(LOGFATAL, "CLangInfo: failed to load %s language strings", language.c_str()); return false; } if (reloadServices) { // also tell our weather and skin to reload as these are localized g_weatherManager.Refresh(); g_PVRManager.LocalizationChanged(); CApplicationMessenger::GetInstance().PostMsg(TMSG_EXECUTE_BUILT_IN, -1, -1, nullptr, "ReloadSkin"); } return true; }
bool CAddonInstallJob::DoWork() { SetTitle(StringUtils::Format(g_localizeStrings.Get(24057).c_str(), m_addon->Name().c_str())); SetProgress(0); // check whether all the dependencies are available or not SetText(g_localizeStrings.Get(24058)); std::pair<std::string, std::string> failedDep; if (!CAddonInstaller::GetInstance().CheckDependencies(m_addon, failedDep)) { std::string details = StringUtils::Format(g_localizeStrings.Get(24142).c_str(), failedDep.first.c_str(), failedDep.second.c_str()); CLog::Log(LOGERROR, "CAddonInstallJob[%s]: %s", m_addon->ID().c_str(), details.c_str()); ReportInstallError(m_addon->ID(), m_addon->ID(), details); return false; } std::string installFrom; if (!m_repo || m_repo->Props().libname.empty()) { // Addons are installed by downloading the .zip package on the server to the local // packages folder, then extracting from the local .zip package into the addons folder // Both these functions are achieved by "copying" using the vfs. std::string dest = "special://home/addons/packages/"; std::string package = URIUtils::AddFileToFolder("special://home/addons/packages/", URIUtils::GetFileName(m_addon->Path())); if (URIUtils::HasSlashAtEnd(m_addon->Path())) { // passed in a folder - all we need do is copy it across installFrom = m_addon->Path(); } else { CAddonDatabase db; if (!db.Open()) { CLog::Log(LOGERROR, "CAddonInstallJob[%s]: failed to open database", m_addon->ID().c_str()); ReportInstallError(m_addon->ID(), m_addon->ID()); return false; } std::string md5; // check that we don't already have a valid copy if (!m_hash.empty() && CFile::Exists(package)) { if (db.GetPackageHash(m_addon->ID(), package, md5) && m_hash != md5) { db.RemovePackage(package); CFile::Delete(package); } } // zip passed in - download + extract if (!CFile::Exists(package)) { std::string path(m_addon->Path()); if (!DownloadPackage(path, dest)) { CFile::Delete(package); CLog::Log(LOGERROR, "CAddonInstallJob[%s]: failed to download %s", m_addon->ID().c_str(), package.c_str()); ReportInstallError(m_addon->ID(), URIUtils::GetFileName(package)); return false; } } // at this point we have the package - check that it is valid SetText(g_localizeStrings.Get(24077)); if (!m_hash.empty()) { md5 = CUtil::GetFileMD5(package); if (!StringUtils::EqualsNoCase(md5, m_hash)) { CFile::Delete(package); CLog::Log(LOGERROR, "CAddonInstallJob[%s]: MD5 mismatch after download %s", m_addon->ID().c_str(), package.c_str()); ReportInstallError(m_addon->ID(), URIUtils::GetFileName(package)); return false; } db.AddPackage(m_addon->ID(), package, md5); } // check the archive as well - should have just a single folder in the root CURL archive = URIUtils::CreateArchivePath("zip", CURL(package), ""); CFileItemList archivedFiles; CDirectory::GetDirectory(archive, archivedFiles); if (archivedFiles.Size() != 1 || !archivedFiles[0]->m_bIsFolder) { // invalid package db.RemovePackage(package); CFile::Delete(package); CLog::Log(LOGERROR, "CAddonInstallJob[%s]: invalid package %s", m_addon->ID().c_str(), package.c_str()); ReportInstallError(m_addon->ID(), URIUtils::GetFileName(package)); return false; } installFrom = archivedFiles[0]->GetPath(); } m_repo.reset(); } // run any pre-install functions ADDON::OnPreInstall(m_addon); // perform install if (!Install(installFrom, m_repo)) return false; CAddonMgr::GetInstance().UnregisterAddon(m_addon->ID()); CAddonMgr::GetInstance().FindAddons(); // run any post-install guff CEventLog::GetInstance().Add( EventPtr(new CAddonManagementEvent(m_addon, m_update ? 24065 : 24064)), !IsModal() && CSettings::GetInstance().GetBool(CSettings::SETTING_GENERAL_ADDONNOTIFICATIONS), false); ADDON::OnPostInstall(m_addon, m_update, IsModal()); //Clear addon from the disabled table CAddonDatabase database; database.Open(); database.DisableAddon(m_addon->ID(), false); // and we're done! MarkFinished(); 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->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 CAddonsDirectory::GetDirectory(const CStdString& strPath, CFileItemList &items) { CStdString path1(strPath); CUtil::RemoveSlashAtEnd(path1); CURL path(path1); items.ClearProperties(); items.SetContent("addons"); VECADDONS addons; // get info from repository bool reposAsFolders = true; if (path.GetHostName().Equals("enabled")) { CAddonMgr::Get().GetAllAddons(addons, true); items.SetProperty("reponame",g_localizeStrings.Get(24062)); } else if (path.GetHostName().Equals("disabled")) { // grab all disabled addons, including disabled repositories reposAsFolders = false; CAddonMgr::Get().GetAllAddons(addons, false, true); items.SetProperty("reponame",g_localizeStrings.Get(24039)); } else if (path.GetHostName().Equals("outdated")) { reposAsFolders = false; CAddonMgr::Get().GetAllOutdatedAddons(addons); items.SetProperty("reponame",g_localizeStrings.Get(24043)); } else if (path.GetHostName().Equals("repos")) { CAddonMgr::Get().GetAddons(ADDON_REPOSITORY,addons,true); } else if (path.GetHostName().Equals("sources")) { return GetScriptsAndPlugins(path.GetFileName(), items); } else if (path.GetHostName().Equals("all")) { CAddonDatabase database; database.Open(); database.GetAddons(addons); items.SetProperty("reponame",g_localizeStrings.Get(24032)); } else { AddonPtr addon; CAddonMgr::Get().GetAddon(path.GetHostName(),addon); if (!addon) return false; CAddonDatabase database; database.Open(); if (!database.GetRepository(addon->ID(),addons)) { RepositoryPtr repo = boost::dynamic_pointer_cast<CRepository>(addon); addons = CRepositoryUpdateJob::GrabAddons(repo,false); } items.SetProperty("reponame",addon->Name()); } if (path.GetFileName().IsEmpty()) { if (!path.GetHostName().Equals("repos")) { for (int i=ADDON_UNKNOWN+1;i<ADDON_VIZ_LIBRARY;++i) { for (unsigned int j=0;j<addons.size();++j) { if (addons[j]->IsType((TYPE)i)) { CFileItemPtr item(new CFileItem(TranslateType((TYPE)i,true))); item->m_strPath = CUtil::AddFileToFolder(strPath,TranslateType((TYPE)i,false)); item->m_bIsFolder = true; CStdString thumb = GetIcon((TYPE)i); if (!thumb.IsEmpty() && g_TextureManager.HasTexture(thumb)) item->SetThumbnailImage(thumb); items.Add(item); break; } } } items.m_strPath = strPath; return true; } } else { TYPE type = TranslateType(path.GetFileName()); items.SetProperty("addoncategory",TranslateType(type, true)); items.m_strPath = strPath; // FIXME: Categorisation of addons needs adding here for (unsigned int j=0;j<addons.size();++j) { if (!addons[j]->IsType(type)) addons.erase(addons.begin()+j--); } } items.m_strPath = strPath; GenerateListing(path, addons, items, reposAsFolders); // check for available updates if (path.GetHostName().Equals("enabled")) { CAddonDatabase database; database.Open(); for (int i=0;i<items.Size();++i) { AddonPtr addon2; database.GetAddon(items[i]->GetProperty("Addon.ID"),addon2); if (addon2 && addon2->Version() > AddonVersion(items[i]->GetProperty("Addon.Version"))) { items[i]->SetProperty("Addon.Status",g_localizeStrings.Get(24068)); items[i]->SetProperty("Addon.UpdateAvail","true"); } } } if (path.GetHostName().Equals("repos") && items.Size() > 1) { CFileItemPtr item(new CFileItem("addons://all/",true)); item->SetLabel(g_localizeStrings.Get(24032)); items.Add(item); } return true; }
int CGUIWindowAddonBrowser::SelectAddonID(const vector<ADDON::TYPE> &types, vector<string> &addonIDs, bool showNone /* = false */, bool showDetails /* = true */, bool multipleSelection /* = true */, bool showInstalled /* = true */, bool showInstallable /* = false */, bool showMore /* = true */) { // if we shouldn't show neither installed nor installable addons the list will be empty if (!showInstalled && !showInstallable) return 0; // can't show the "Get More" button if we already show installable addons if (showInstallable) showMore = false; CGUIDialogSelect *dialog = (CGUIDialogSelect*)g_windowManager.GetWindow(WINDOW_DIALOG_SELECT); if (!dialog) return 0; // get rid of any invalid addon types vector<ADDON::TYPE> validTypes(types.size()); std::copy_if(types.begin(), types.end(), validTypes.begin(), [](ADDON::TYPE type) { return type != ADDON_UNKNOWN; }); if (validTypes.empty()) return 0; // get all addons to show VECADDONS addons; if (showInstalled) { for (vector<ADDON::TYPE>::const_iterator type = validTypes.begin(); type != validTypes.end(); ++type) { VECADDONS typeAddons; if (*type == ADDON_AUDIO) CAddonsDirectory::GetScriptsAndPlugins("audio", typeAddons); else if (*type == ADDON_EXECUTABLE) CAddonsDirectory::GetScriptsAndPlugins("executable", typeAddons); else if (*type == ADDON_IMAGE) CAddonsDirectory::GetScriptsAndPlugins("image", typeAddons); else if (*type == ADDON_VIDEO) CAddonsDirectory::GetScriptsAndPlugins("video", typeAddons); else CAddonMgr::Get().GetAddons(*type, typeAddons); addons.insert(addons.end(), typeAddons.begin(), typeAddons.end()); } } if (showInstallable || showMore) { VECADDONS installableAddons; CAddonDatabase database; if (database.Open() && database.GetAddons(installableAddons)) { for (ADDON::IVECADDONS addon = installableAddons.begin(); addon != installableAddons.end();) { AddonPtr pAddon = *addon; // check if the addon matches one of the provided addon types bool matchesType = false; for (vector<ADDON::TYPE>::const_iterator type = validTypes.begin(); type != validTypes.end(); ++type) { if (pAddon->IsType(*type)) { matchesType = true; break; } } // only show addons that match one of the provided addon types and that aren't disabled if (matchesType && !CAddonMgr::Get().IsAddonDisabled(pAddon->ID())) { // check if the addon is installed bool isInstalled = CAddonMgr::Get().IsAddonInstalled(pAddon->ID()); // check if the addon is installed or can be installed if ((showInstallable || showMore) && !isInstalled && CAddonMgr::Get().CanAddonBeInstalled(pAddon)) { ++addon; continue; } } addon = installableAddons.erase(addon); } if (showInstallable) addons.insert(addons.end(), installableAddons.begin(), installableAddons.end()); else if (showMore) showMore = !installableAddons.empty(); } } if (addons.empty() && !showNone) return 0; // turn the addons into items std::map<std::string, AddonPtr> addonMap; CFileItemList items; for (ADDON::IVECADDONS addon = addons.begin(); addon != addons.end(); ++addon) { CFileItemPtr item(CAddonsDirectory::FileItemFromAddon(*addon, (*addon)->ID())); if (!items.Contains(item->GetPath())) { items.Add(item); addonMap.insert(std::make_pair(item->GetPath(), *addon)); } } if (items.IsEmpty() && !showNone) return 0; std::string heading; for (vector<ADDON::TYPE>::const_iterator type = validTypes.begin(); type != validTypes.end(); ++type) { if (!heading.empty()) heading += ", "; heading += TranslateType(*type, true); } dialog->SetHeading(CVariant{std::move(heading)}); dialog->Reset(); dialog->SetUseDetails(showDetails); if (multipleSelection) { showNone = false; showMore = false; dialog->EnableButton(true, 186); } else if (showMore) dialog->EnableButton(true, 21452); if (showNone) { CFileItemPtr item(new CFileItem("", false)); item->SetLabel(g_localizeStrings.Get(231)); item->SetLabel2(g_localizeStrings.Get(24040)); item->SetIconImage("DefaultAddonNone.png"); item->SetSpecialSort(SortSpecialOnTop); items.Add(item); } items.Sort(SortByLabel, SortOrderAscending); if (addonIDs.size() > 0) { for (vector<string>::const_iterator it = addonIDs.begin(); it != addonIDs.end() ; ++it) { CFileItemPtr item = items.Get(*it); if (item) item->Select(true); } } dialog->SetItems(&items); dialog->SetMultiSelection(multipleSelection); dialog->Open(); // if the "Get More" button has been pressed and we haven't shown the // installable addons so far show a list of installable addons if (showMore&& dialog->IsButtonPressed()) return SelectAddonID(types, addonIDs, showNone, showDetails, multipleSelection, false, true, false); if (!dialog->IsConfirmed()) return 0; addonIDs.clear(); const CFileItemList& list = dialog->GetSelectedItems(); for (int i = 0; i < list.Size(); i++) { const CFileItemPtr& item = list.Get(i); // check if one of the selected addons needs to be installed if (showInstallable) { std::map<std::string, AddonPtr>::const_iterator itAddon = addonMap.find(item->GetPath()); if (itAddon != addonMap.end()) { const AddonPtr& addon = itAddon->second; // if the addon isn't installed we need to install it if (!CAddonMgr::Get().IsAddonInstalled(addon->ID())) { AddonPtr installedAddon; if (!CAddonInstaller::Get().InstallModal(addon->ID(), installedAddon, false)) continue; } // if the addon is disabled we need to enable it if (CAddonMgr::Get().IsAddonDisabled(addon->ID())) CAddonMgr::Get().EnableAddon(addon->ID()); } } addonIDs.push_back(item->GetPath()); } return 1; }
bool CAddonInstallJob::DoWork() { AddonPtr repoPtr = GetRepoForAddon(m_addon); CStdString installFrom; if (!repoPtr || repoPtr->Props().libname.empty()) { // Addons are installed by downloading the .zip package on the server to the local // packages folder, then extracting from the local .zip package into the addons folder // Both these functions are achieved by "copying" using the vfs. CStdString dest="special://home/addons/packages/"; CStdString package = URIUtils::AddFileToFolder("special://home/addons/packages/", URIUtils::GetFileName(m_addon->Path())); if (URIUtils::HasSlashAtEnd(m_addon->Path())) { // passed in a folder - all we need do is copy it across installFrom = m_addon->Path(); } else { CStdString md5; CAddonDatabase db; db.Open(); // check that we don't already have a valid copy if (!m_hash.empty() && CFile::Exists(package)) { if (db.GetPackageHash(m_addon->ID(), package, md5) && m_hash != md5) { db.RemovePackage(package); CFile::Delete(package); } } // zip passed in - download + extract if (!CFile::Exists(package)) { CStdString path(m_addon->Path()); if (!m_referer.empty() && URIUtils::IsInternetStream(path)) { CURL url(path); url.SetProtocolOptions(m_referer); path = url.Get(); } if (!DownloadPackage(path, dest)) { CFile::Delete(package); return false; } } // at this point we have the package - check that it is valid if (!m_hash.empty()) { md5 = CUtil::GetFileMD5(package); if (!md5.Equals(m_hash)) { CFile::Delete(package); ReportInstallError(m_addon->ID(), URIUtils::GetFileName(package)); CLog::Log(LOGERROR, "MD5 mismatch after download %s", package.c_str()); return false; } db.AddPackage(m_addon->ID(), package, md5); } // check the archive as well - should have just a single folder in the root CStdString archive; URIUtils::CreateArchivePath(archive,"zip",package,""); CFileItemList archivedFiles; CDirectory::GetDirectory(archive, archivedFiles); if (archivedFiles.Size() != 1 || !archivedFiles[0]->m_bIsFolder) { // invalid package db.RemovePackage(package); CFile::Delete(package); return false; } installFrom = archivedFiles[0]->GetPath(); } repoPtr.reset(); } // run any pre-install functions bool reloadAddon = OnPreInstall(); // perform install if (!Install(installFrom, repoPtr)) return false; // something went wrong // run any post-install guff OnPostInstall(reloadAddon); // and we're done! return true; }
bool CPVRDatabase::UpdateOldVersion(int iVersion) { bool bReturn = true; BeginTransaction(); try { if (iVersion < 11) { CLog::Log(LOGERROR, "PVR - %s - updating from table versions < 11 not supported. please delete '%s'", __FUNCTION__, GetBaseDBName()); bReturn = false; } else { if (iVersion < 12) m_pDS->exec("DROP VIEW vw_last_watched;"); if (iVersion < 13) m_pDS->exec("ALTER TABLE channels ADD idEpg integer;"); if (iVersion < 14) m_pDS->exec("ALTER TABLE channelsettings ADD fCustomVerticalShift float;"); if (iVersion < 15) { m_pDS->exec("ALTER TABLE channelsettings ADD bCustomNonLinStretch bool;"); m_pDS->exec("ALTER TABLE channelsettings ADD bPostProcess bool;"); m_pDS->exec("ALTER TABLE channelsettings ADD iScalingMethod integer;"); } if (iVersion < 16) { /* sqlite apparently can't delete columns from an existing table, so just leave the extra column alone */ } if (iVersion < 17) { m_pDS->exec("ALTER TABLE channelsettings ADD iDeinterlaceMode integer"); m_pDS->exec("UPDATE channelsettings SET iDeinterlaceMode = 2 WHERE iInterlaceMethod NOT IN (0,1)"); // anything other than none: method auto => mode force m_pDS->exec("UPDATE channelsettings SET iDeinterlaceMode = 1 WHERE iInterlaceMethod = 1"); // method auto => mode auto m_pDS->exec("UPDATE channelsettings SET iDeinterlaceMode = 0, iInterlaceMethod = 1 WHERE iInterlaceMethod = 0"); // method none => mode off, method auto } if (iVersion < 18) { m_pDS->exec("DROP INDEX idx_channels_iClientId;"); m_pDS->exec("DROP INDEX idx_channels_iLastWatched;"); m_pDS->exec("DROP INDEX idx_channels_bIsRadio;"); m_pDS->exec("DROP INDEX idx_channels_bIsHidden;"); m_pDS->exec("DROP INDEX idx_idChannel_idGroup;"); m_pDS->exec("DROP INDEX idx_idGroup_iChannelNumber;"); m_pDS->exec("CREATE UNIQUE INDEX idx_channels_iClientId_iUniqueId on channels(iClientId, iUniqueId);"); m_pDS->exec("CREATE UNIQUE INDEX idx_idGroup_idChannel on map_channelgroups_channels(idGroup, idChannel);"); } if (iVersion < 19) { // bit of a hack, but we need to keep the version/contents of the non-pvr databases the same to allow clean upgrades ADDON::VECADDONS addons; if ((bReturn = CAddonMgr::Get().GetAddons(ADDON_PVRDLL, addons, true, false)) == false) CLog::Log(LOGERROR, "PVR - %s - failed to get add-ons from the add-on manager", __FUNCTION__); else { CAddonDatabase database; database.Open(); for (IVECADDONS it = addons.begin(); it != addons.end(); it++) { if (!database.IsSystemPVRAddonEnabled(it->get()->ID())) database.DisableAddon(it->get()->ID()); } database.Close(); } } if (iVersion < 20) m_pDS->exec("ALTER TABLE channels ADD bIsUserSetIcon bool"); if (iVersion < 21) m_pDS->exec("ALTER TABLE channelgroups ADD iGroupType integer"); } } catch (...) { CLog::Log(LOGERROR, "PVR - %s - error attempting to update the database version!", __FUNCTION__); bReturn = false; } if (bReturn) CommitTransaction(); else RollbackTransaction(); return bReturn; }
void CPVRDatabase::UpdateTables(int iVersion) { if (iVersion < 13) m_pDS->exec("ALTER TABLE channels ADD idEpg integer;"); if (iVersion < 20) m_pDS->exec("ALTER TABLE channels ADD bIsUserSetIcon bool"); if (iVersion < 21) m_pDS->exec("ALTER TABLE channelgroups ADD iGroupType integer"); if (iVersion < 22) m_pDS->exec("ALTER TABLE channels ADD bIsLocked bool"); if (iVersion < 23) m_pDS->exec("ALTER TABLE channelgroups ADD iLastWatched integer"); if (iVersion < 24) m_pDS->exec("ALTER TABLE channels ADD bIsUserSetName bool"); if (iVersion < 25) m_pDS->exec("DROP TABLE IF EXISTS channelsettings"); if (iVersion < 26) { m_pDS->exec("ALTER TABLE channels ADD iClientSubChannelNumber integer"); m_pDS->exec("UPDATE channels SET iClientSubChannelNumber = 0"); m_pDS->exec("ALTER TABLE map_channelgroups_channels ADD iSubChannelNumber integer"); m_pDS->exec("UPDATE map_channelgroups_channels SET iSubChannelNumber = 0"); } if (iVersion < 27) m_pDS->exec("ALTER TABLE channelgroups ADD bIsHidden bool"); if (iVersion < 28) { VECADDONS addons; CAddonDatabase database; if (database.Open() && CAddonMgr::Get().GetAddons(ADDON_PVRDLL, addons, true)) { /** find all old client IDs */ std::string strQuery(PrepareSQL("SELECT idClient, sUid FROM clients")); m_pDS->query(strQuery); while (!m_pDS->eof() && !addons.empty()) { /** try to find an add-on that matches the sUid */ for (VECADDONS::iterator it = addons.begin(); it != addons.end(); ++it) { if ((*it)->ID() == m_pDS->fv(1).get_asString()) { /** try to get the current ID from the database */ int iAddonId = database.GetAddonId(*it); /** register a new id if it didn't exist */ if (iAddonId <= 0) iAddonId = database.AddAddon(*it, 0); if (iAddonId > 0) { // this fails when an id becomes the id of one that's being replaced next iteration // but since almost everyone only has 1 add-on enabled... /** update the iClientId in the channels table */ strQuery = PrepareSQL("UPDATE channels SET iClientId = %u WHERE iClientId = %u", iAddonId, m_pDS->fv(0).get_asInt()); m_pDS->exec(strQuery); /** no need to check this add-on again */ it = addons.erase(it); break; } } } m_pDS->next(); } } m_pDS->exec("DROP TABLE clients"); } if (iVersion < 29) m_pDS->exec("ALTER TABLE channelgroups ADD iPosition integer"); }
bool CAddonsDirectory::GetDirectory(const CStdString& strPath, CFileItemList &items) { CStdString path1(strPath); URIUtils::RemoveSlashAtEnd(path1); CURL path(path1); items.ClearProperties(); items.SetContent("addons"); VECADDONS addons; // get info from repository bool reposAsFolders = true; if (path.GetHostName().Equals("enabled")) { CAddonMgr::Get().GetAllAddons(addons, true); items.SetProperty("reponame",g_localizeStrings.Get(24062)); items.SetLabel(g_localizeStrings.Get(24062)); } else if (path.GetHostName().Equals("disabled")) { // grab all disabled addons, including disabled repositories reposAsFolders = false; CAddonMgr::Get().GetAllAddons(addons, false, true); items.SetProperty("reponame",g_localizeStrings.Get(24039)); items.SetLabel(g_localizeStrings.Get(24039)); } else if (path.GetHostName().Equals("outdated")) { reposAsFolders = false; CAddonMgr::Get().GetAllOutdatedAddons(addons); items.SetProperty("reponame",g_localizeStrings.Get(24043)); items.SetLabel(g_localizeStrings.Get(24043)); } else if (path.GetHostName().Equals("repos")) { CAddonMgr::Get().GetAddons(ADDON_REPOSITORY,addons,true); items.SetLabel(g_localizeStrings.Get(24033)); // Get Add-ons } else if (path.GetHostName().Equals("sources")) { return GetScriptsAndPlugins(path.GetFileName(), items); } else if (path.GetHostName().Equals("all")) { CAddonDatabase database; database.Open(); database.GetAddons(addons); items.SetProperty("reponame",g_localizeStrings.Get(24032)); items.SetLabel(g_localizeStrings.Get(24032)); } else if (path.GetHostName().Equals("search")) { CStdString search(path.GetFileName()); if (search.IsEmpty() && !GetKeyboardInput(16017, search)) return false; items.SetProperty("reponame",g_localizeStrings.Get(283)); items.SetLabel(g_localizeStrings.Get(283)); CAddonDatabase database; database.Open(); database.Search(search, addons); GenerateListing(path, addons, items, true); path.SetFileName(search); items.SetPath(path.Get()); return true; } else { reposAsFolders = false; AddonPtr addon; CAddonMgr::Get().GetAddon(path.GetHostName(),addon); if (!addon) return false; // ensure our repos are up to date CAddonInstaller::Get().UpdateRepos(false, true); CAddonDatabase database; database.Open(); database.GetRepository(addon->ID(),addons); items.SetProperty("reponame",addon->Name()); items.SetLabel(addon->Name()); } if (path.GetFileName().IsEmpty()) { if (!path.GetHostName().Equals("repos")) { for (int i=ADDON_UNKNOWN+1;i<ADDON_VIZ_LIBRARY;++i) { for (unsigned int j=0;j<addons.size();++j) { if (addons[j]->IsType((TYPE)i)) { CFileItemPtr item(new CFileItem(TranslateType((TYPE)i,true))); item->SetPath(URIUtils::AddFileToFolder(strPath,TranslateType((TYPE)i,false))); item->m_bIsFolder = true; CStdString thumb = GetIcon((TYPE)i); if (!thumb.IsEmpty() && g_TextureManager.HasTexture(thumb)) item->SetArt("thumb", thumb); items.Add(item); break; } } } items.SetPath(strPath); return true; } } else { TYPE type = TranslateType(path.GetFileName()); items.SetProperty("addoncategory",TranslateType(type, true)); items.SetLabel(TranslateType(type, true)); items.SetPath(strPath); // FIXME: Categorisation of addons needs adding here for (unsigned int j=0;j<addons.size();++j) { if (!addons[j]->IsType(type)) addons.erase(addons.begin()+j--); } } items.SetPath(strPath); GenerateListing(path, addons, items, reposAsFolders); // check for available updates if (path.GetHostName().Equals("enabled")) { CAddonDatabase database; database.Open(); for (int i=0;i<items.Size();++i) { AddonPtr addon2; database.GetAddon(items[i]->GetProperty("Addon.ID").asString(),addon2); if (addon2 && addon2->Version() > AddonVersion(items[i]->GetProperty("Addon.Version").asString()) && !database.IsAddonBlacklisted(addon2->ID(),addon2->Version().c_str())) { items[i]->SetProperty("Addon.Status",g_localizeStrings.Get(24068)); items[i]->SetProperty("Addon.UpdateAvail", true); } } } if (path.GetHostName().Equals("repos") && items.Size() > 1) { CFileItemPtr item(new CFileItem("addons://all/",true)); item->SetLabel(g_localizeStrings.Get(24032)); items.Add(item); } return true; }
bool CPVRDatabase::CreateTables() { bool bReturn(false); try { if (!CDatabase::CreateTables()) return false; BeginTransaction(); CLog::Log(LOGINFO, "PVR - %s - creating tables", __FUNCTION__); CLog::Log(LOGDEBUG, "PVR - %s - creating table 'clients'", __FUNCTION__); m_pDS->exec( "CREATE TABLE clients (" "idClient integer primary key, " "sName varchar(64), " "sUid varchar(32)" ")" ); CLog::Log(LOGDEBUG, "PVR - %s - creating table 'channels'", __FUNCTION__); m_pDS->exec( "CREATE TABLE channels (" "idChannel integer primary key, " "iUniqueId integer, " "bIsRadio bool, " "bIsHidden bool, " "bIsUserSetIcon bool, " "sIconPath varchar(255), " "sChannelName varchar(64), " "bIsVirtual bool, " "bEPGEnabled bool, " "sEPGScraper varchar(32), " "iLastWatched integer," // TODO use mapping table "iClientId integer, " "iClientChannelNumber integer, " "sInputFormat varchar(32), " "sStreamURL varchar(255), " "iEncryptionSystem integer, " "idEpg integer" ")" ); m_pDS->exec("CREATE UNIQUE INDEX idx_channels_iClientId_iUniqueId on channels(iClientId, iUniqueId);"); // TODO use a mapping table so multiple backends per channel can be implemented // CLog::Log(LOGDEBUG, "PVR - %s - creating table 'map_channels_clients'", __FUNCTION__); // m_pDS->exec( // "CREATE TABLE map_channels_clients (" // "idChannel integer primary key, " // "idClient integer, " // "iClientChannelNumber integer," // "sInputFormat string," // "sStreamURL string," // "iEncryptionSystem integer" // ");" // ); // m_pDS->exec("CREATE UNIQUE INDEX idx_idChannel_idClient on map_channels_clients(idChannel, idClient);"); CLog::Log(LOGDEBUG, "PVR - %s - creating table 'channelgroups'", __FUNCTION__); m_pDS->exec( "CREATE TABLE channelgroups (" "idGroup integer primary key," "bIsRadio bool, " "iGroupType integer, " "sName varchar(64)" ")" ); m_pDS->exec("CREATE INDEX idx_channelgroups_bIsRadio on channelgroups(bIsRadio);"); CLog::Log(LOGDEBUG, "PVR - %s - creating table 'map_channelgroups_channels'", __FUNCTION__); m_pDS->exec( "CREATE TABLE map_channelgroups_channels (" "idChannel integer, " "idGroup integer, " "iChannelNumber integer" ")" ); m_pDS->exec("CREATE UNIQUE INDEX idx_idGroup_idChannel on map_channelgroups_channels(idGroup, idChannel);"); CLog::Log(LOGDEBUG, "PVR - %s - creating table 'channelsettings'", __FUNCTION__); m_pDS->exec( "CREATE TABLE channelsettings (" "idChannel integer primary key, " "iInterlaceMethod integer, " "iViewMode integer, " "fCustomZoomAmount float, " "fPixelRatio float, " "iAudioStream integer, " "iSubtitleStream integer," "fSubtitleDelay float, " "bSubtitles bool, " "fBrightness float, " "fContrast float, " "fGamma float," "fVolumeAmplification float, " "fAudioDelay float, " "bOutputToAllSpeakers bool, " "bCrop bool, " "iCropLeft integer, " "iCropRight integer, " "iCropTop integer, " "iCropBottom integer, " "fSharpness float, " "fNoiseReduction float, " "fCustomVerticalShift float, " "bCustomNonLinStretch bool, " "bPostProcess bool, " "iScalingMethod integer, " "iDeinterlaceMode integer " ")" ); CommitTransaction(); bReturn = true; } catch (...) { CLog::Log(LOGERROR, "PVR - %s - unable to create PVR database tables (error %i)", __FUNCTION__, (int)GetLastError()); RollbackTransaction(); bReturn = false; } if (bReturn) { // disable all PVR add-on when started the first time ADDON::VECADDONS addons; if ((bReturn = CAddonMgr::Get().GetAddons(ADDON_PVRDLL, addons, true, false)) == false) CLog::Log(LOGERROR, "PVR - %s - failed to get add-ons from the add-on manager", __FUNCTION__); else { CAddonDatabase database; database.Open(); for (IVECADDONS it = addons.begin(); it != addons.end(); it++) database.DisableAddon(it->get()->ID()); database.Close(); } } return bReturn; }
void CGUIDialogAddonInfo::OnUpdate() { if (!m_localAddon) return; CAddonDatabase database; if (!database.Open()) return; std::vector<std::pair<AddonVersion, std::string>> versions; if (!database.GetAvailableVersions(m_localAddon->ID(), versions)) return; CFileItemList items; if (XFILE::CDirectory::GetDirectory("special://home/addons/packages/", items, ".zip", DIR_FLAG_NO_FILE_DIRS)) { for (int i = 0; i < items.Size(); ++i) { std::string packageId; std::string versionString; if (AddonVersion::SplitFileName(packageId, versionString, items[i]->GetLabel())) { if (packageId == m_localAddon->ID()) { std::string hash; std::string path(items[i]->GetPath()); if (database.GetPackageHash(m_localAddon->ID(), items[i]->GetPath(), hash)) { std::string md5 = CUtil::GetFileMD5(path); if (md5 == hash) versions.push_back(std::make_pair(AddonVersion(versionString), LOCAL_CACHE)); } } } } } if (versions.empty()) { CGUIDialogOK::ShowAndGetInput(CVariant{21341}, CVariant{21342}); return; } auto* dialog = static_cast<CGUIDialogSelect*>(g_windowManager.GetWindow(WINDOW_DIALOG_SELECT)); dialog->Reset(); dialog->SetHeading(CVariant{21338}); dialog->SetUseDetails(true); std::stable_sort(versions.begin(), versions.end(), CompareVersion); for (const auto& versionInfo : versions) { CFileItem item(StringUtils::Format(g_localizeStrings.Get(21339).c_str(), versionInfo.first.asString().c_str())); if (versionInfo.first == m_localAddon->Version()) item.Select(true); AddonPtr repo; if (versionInfo.second == LOCAL_CACHE) { item.SetProperty("Addon.Summary", g_localizeStrings.Get(24095)); item.SetIconImage("DefaultAddonRepository.png"); dialog->Add(item); } else if (CAddonMgr::GetInstance().GetAddon(versionInfo.second, repo, ADDON_REPOSITORY)) { item.SetProperty("Addon.Summary", repo->Name()); item.SetIconImage(repo->Icon()); dialog->Add(item); } } dialog->Open(); if (dialog->IsConfirmed()) { Close(); auto selected = versions.at(dialog->GetSelectedLabel()); //turn auto updating off if downgrading if (selected.first < m_localAddon->Version()) CAddonMgr::GetInstance().AddToUpdateBlacklist(m_localAddon->ID()); if (selected.second == LOCAL_CACHE) CAddonInstaller::GetInstance().InstallFromZip(StringUtils::Format("special://home/addons/packages/%s-%s.zip", m_localAddon->ID().c_str(), selected.first.asString().c_str())); else CAddonInstaller::GetInstance().Install(m_addon->ID(), selected.first, selected.second); } }
bool CAddonInstallJob::DoWork() { SetTitle(StringUtils::Format(g_localizeStrings.Get(24057).c_str(), m_addon->Name().c_str())); SetProgress(0); // check whether all the dependencies are available or not SetText(g_localizeStrings.Get(24058)); std::pair<std::string, std::string> failedDep; if (!CAddonInstaller::GetInstance().CheckDependencies(m_addon, failedDep)) { std::string details = StringUtils::Format(g_localizeStrings.Get(24142).c_str(), failedDep.first.c_str(), failedDep.second.c_str()); CLog::Log(LOGERROR, "CAddonInstallJob[%s]: %s", m_addon->ID().c_str(), details.c_str()); ReportInstallError(m_addon->ID(), m_addon->ID(), details); return false; } std::string installFrom; { // Addons are installed by downloading the .zip package on the server to the local // packages folder, then extracting from the local .zip package into the addons folder // Both these functions are achieved by "copying" using the vfs. std::string dest = "special://home/addons/packages/"; std::string package = URIUtils::AddFileToFolder("special://home/addons/packages/", URIUtils::GetFileName(m_addon->Path())); if (URIUtils::HasSlashAtEnd(m_addon->Path())) { // passed in a folder - all we need do is copy it across installFrom = m_addon->Path(); } else { CAddonDatabase db; if (!db.Open()) { CLog::Log(LOGERROR, "CAddonInstallJob[%s]: failed to open database", m_addon->ID().c_str()); ReportInstallError(m_addon->ID(), m_addon->ID()); return false; } std::string md5; // check that we don't already have a valid copy if (!m_hash.empty() && CFile::Exists(package)) { if (db.GetPackageHash(m_addon->ID(), package, md5) && m_hash != md5) { db.RemovePackage(package); CFile::Delete(package); } } // zip passed in - download + extract if (!CFile::Exists(package)) { std::string path(m_addon->Path()); if (!DownloadPackage(path, dest)) { CFile::Delete(package); CLog::Log(LOGERROR, "CAddonInstallJob[%s]: failed to download %s", m_addon->ID().c_str(), package.c_str()); ReportInstallError(m_addon->ID(), URIUtils::GetFileName(package)); return false; } } // at this point we have the package - check that it is valid SetText(g_localizeStrings.Get(24077)); if (!m_hash.empty()) { md5 = CUtil::GetFileMD5(package); if (!StringUtils::EqualsNoCase(md5, m_hash)) { CFile::Delete(package); CLog::Log(LOGERROR, "CAddonInstallJob[%s]: MD5 mismatch after download. Expected %s, was %s", m_addon->ID().c_str(), m_hash.c_str(), md5.c_str()); ReportInstallError(m_addon->ID(), URIUtils::GetFileName(package)); return false; } db.AddPackage(m_addon->ID(), package, md5); } // check if the archive is valid CURL archive = URIUtils::CreateArchivePath("zip", CURL(package), ""); CFileItemList archivedFiles; AddonPtr temp; if (!CDirectory::GetDirectory(archive, archivedFiles) || archivedFiles.Size() != 1 || !archivedFiles[0]->m_bIsFolder || !CAddonMgr::GetInstance().LoadAddonDescription(archivedFiles[0]->GetPath(), temp)) { CLog::Log(LOGERROR, "CAddonInstallJob[%s]: invalid package %s", m_addon->ID().c_str(), package.c_str()); db.RemovePackage(package); CFile::Delete(package); ReportInstallError(m_addon->ID(), URIUtils::GetFileName(package)); return false; } installFrom = package; } } // run any pre-install functions ADDON::OnPreInstall(m_addon); // perform install if (!Install(installFrom, m_repo)) return false; if (!CAddonMgr::GetInstance().ReloadAddon(m_addon)) { CLog::Log(LOGERROR, "CAddonInstallJob[%s]: failed to reload addon", m_addon->ID().c_str()); return false; } g_localizeStrings.LoadAddonStrings(URIUtils::AddFileToFolder(m_addon->Path(), "resources/language/"), CServiceBroker::GetSettings().GetString(CSettings::SETTING_LOCALE_LANGUAGE), m_addon->ID()); ADDON::OnPostInstall(m_addon, m_isUpdate, IsModal()); { CAddonDatabase database; database.Open(); database.SetOrigin(m_addon->ID(), m_repo ? m_repo->ID() : ""); if (m_isUpdate) database.SetLastUpdated(m_addon->ID(), CDateTime::GetCurrentDateTime()); } bool notify = (CServiceBroker::GetSettings().GetBool(CSettings::SETTING_ADDONS_NOTIFICATIONS) || !m_isAutoUpdate) && !IsModal(); CEventLog::GetInstance().Add( EventPtr(new CAddonManagementEvent(m_addon, m_isUpdate ? 24065 : 24084)), notify, false); if (m_isAutoUpdate && !m_addon->Broken().empty()) { CLog::Log(LOGDEBUG, "CAddonInstallJob[%s]: auto-disabling due to being marked as broken", m_addon->ID().c_str()); CAddonMgr::GetInstance().DisableAddon(m_addon->ID()); CEventLog::GetInstance().Add(EventPtr(new CAddonManagementEvent(m_addon, 24094)), true, false); } // and we're done! MarkFinished(); return true; }