2 * Copyright (C) 2011-2013 Team XBMC
5 * This Program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2, or (at your option)
10 * This Program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with XBMC; see the file COPYING. If not, see
17 * <http://www.gnu.org/licenses/>.
21 #include "AddonInstaller.h"
23 #include "utils/log.h"
24 #include "utils/FileUtils.h"
25 #include "utils/URIUtils.h"
27 #include "guilib/LocalizeStrings.h"
28 #include "filesystem/Directory.h"
29 #include "settings/AdvancedSettings.h"
30 #include "settings/Settings.h"
31 #include "ApplicationMessenger.h"
32 #include "filesystem/FavouritesDirectory.h"
33 #include "utils/JobManager.h"
34 #include "dialogs/GUIDialogYesNo.h"
35 #include "addons/AddonManager.h"
36 #include "addons/Repository.h"
37 #include "guilib/GUIWindowManager.h" // for callback
38 #include "GUIUserMessages.h" // for callback
39 #include "utils/StringUtils.h"
40 #include "dialogs/GUIDialogKaiToast.h"
41 #include "dialogs/GUIDialogProgress.h"
43 #include "pvr/PVRManager.h"
46 using namespace XFILE;
47 using namespace ADDON;
50 struct find_map : public binary_function<CAddonInstaller::JobMap::value_type, unsigned int, bool>
52 bool operator() (CAddonInstaller::JobMap::value_type t, unsigned int id) const
54 return (t.second.jobID == id);
58 CAddonInstaller::CAddonInstaller()
63 CAddonInstaller::~CAddonInstaller()
67 CAddonInstaller &CAddonInstaller::Get()
69 static CAddonInstaller addonInstaller;
70 return addonInstaller;
73 void CAddonInstaller::OnJobComplete(unsigned int jobID, bool success, CJob* job)
76 CAddonMgr::Get().FindAddons();
78 CSingleLock lock(m_critSection);
79 if (strncmp(job->GetType(), "repoupdate", 10) == 0)
80 { // repo job finished
81 m_repoUpdateDone.Set();
87 JobMap::iterator i = find_if(m_downloadJobs.begin(), m_downloadJobs.end(), bind2nd(find_map(), jobID));
88 if (i != m_downloadJobs.end())
89 m_downloadJobs.erase(i);
94 CGUIMessage msg(GUI_MSG_NOTIFY_ALL, 0, 0, GUI_MSG_UPDATE);
95 g_windowManager.SendThreadMessage(msg);
98 void CAddonInstaller::OnJobProgress(unsigned int jobID, unsigned int progress, unsigned int total, const CJob *job)
100 CSingleLock lock(m_critSection);
101 JobMap::iterator i = find_if(m_downloadJobs.begin(), m_downloadJobs.end(), bind2nd(find_map(), jobID));
102 if (i != m_downloadJobs.end())
103 { // update job progress
104 i->second.progress = progress;
105 CGUIMessage msg(GUI_MSG_NOTIFY_ALL, 0, 0, GUI_MSG_UPDATE_ITEM);
106 msg.SetStringParam(i->first);
108 g_windowManager.SendThreadMessage(msg);
112 bool CAddonInstaller::IsDownloading() const
114 CSingleLock lock(m_critSection);
115 return !m_downloadJobs.empty();
118 void CAddonInstaller::GetInstallList(VECADDONS &addons) const
120 CSingleLock lock(m_critSection);
121 vector<CStdString> addonIDs;
122 for (JobMap::const_iterator i = m_downloadJobs.begin(); i != m_downloadJobs.end(); ++i)
125 addonIDs.push_back(i->first);
129 CAddonDatabase database;
131 for (vector<CStdString>::iterator it = addonIDs.begin(); it != addonIDs.end();++it)
134 if (database.GetAddon(*it, addon))
135 addons.push_back(addon);
139 bool CAddonInstaller::GetProgress(const CStdString &addonID, unsigned int &percent) const
141 CSingleLock lock(m_critSection);
142 JobMap::const_iterator i = m_downloadJobs.find(addonID);
143 if (i != m_downloadJobs.end())
145 percent = i->second.progress;
151 bool CAddonInstaller::Cancel(const CStdString &addonID)
153 CSingleLock lock(m_critSection);
154 JobMap::iterator i = m_downloadJobs.find(addonID);
155 if (i != m_downloadJobs.end())
157 CJobManager::GetInstance().CancelJob(i->second.jobID);
158 m_downloadJobs.erase(i);
164 bool CAddonInstaller::PromptForInstall(const CStdString &addonID, AddonPtr &addon)
166 // we assume that addons that are enabled don't get to this routine (i.e. that GetAddon() has been called)
167 if (CAddonMgr::Get().GetAddon(addonID, addon, ADDON_UNKNOWN, false))
168 return false; // addon is installed but disabled, and the user has specifically activated something that needs
169 // the addon - should we enable it?
171 // check we have it available
172 CAddonDatabase database;
174 if (database.GetAddon(addonID, addon))
175 { // yes - ask user if they want it installed
176 if (!CGUIDialogYesNo::ShowAndGetInput(g_localizeStrings.Get(24076), g_localizeStrings.Get(24100),
177 addon->Name().c_str(), g_localizeStrings.Get(24101)))
179 if (Install(addonID, true))
181 CGUIDialogProgress *progress = (CGUIDialogProgress *)g_windowManager.GetWindow(WINDOW_DIALOG_PROGRESS);
184 progress->SetHeading(13413); // Downloading
185 progress->SetLine(0, "");
186 progress->SetLine(1, addon->Name());
187 progress->SetLine(2, "");
188 progress->SetPercentage(0);
189 progress->StartModal();
192 progress->Progress();
193 unsigned int percent;
194 if (progress->IsCanceled())
199 if (!GetProgress(addonID, percent))
201 progress->SetPercentage(percent);
205 return CAddonMgr::Get().GetAddon(addonID, addon);
211 bool CAddonInstaller::Install(const CStdString &addonID, bool force, const CStdString &referer, bool background)
214 bool addonInstalled = CAddonMgr::Get().GetAddon(addonID, addon, ADDON_UNKNOWN, false);
215 if (addonInstalled && !force)
218 // check whether we have it available in a repository
219 CAddonDatabase database;
221 if (database.GetAddon(addonID, addon))
224 database.GetRepoForAddon(addonID,repo);
226 CAddonMgr::Get().GetAddon(repo,ptr);
227 RepositoryPtr therepo = boost::dynamic_pointer_cast<CRepository>(ptr);
230 hash = therepo->GetAddonHash(addon);
231 return DoInstall(addon, hash, addonInstalled, referer, background);
236 bool CAddonInstaller::DoInstall(const AddonPtr &addon, const CStdString &hash, bool update, const CStdString &referer, bool background)
238 // check whether we already have the addon installing
239 CSingleLock lock(m_critSection);
240 if (m_downloadJobs.find(addon->ID()) != m_downloadJobs.end())
243 // check whether all the dependencies are available or not
244 if (!CheckDependencies(addon))
246 CGUIDialogKaiToast::QueueNotification(addon->Icon(), addon->Name(), g_localizeStrings.Get(24044), TOAST_DISPLAY_TIME, false);
252 unsigned int jobID = CJobManager::GetInstance().AddJob(new CAddonInstallJob(addon, hash, update, referer), this);
253 m_downloadJobs.insert(make_pair(addon->ID(), CDownloadJob(jobID)));
257 m_downloadJobs.insert(make_pair(addon->ID(), CDownloadJob(0)));
259 CAddonInstallJob job(addon, hash, update, referer);
261 { // TODO: dump something to debug log?
265 JobMap::iterator i = m_downloadJobs.find(addon->ID());
266 m_downloadJobs.erase(i);
271 bool CAddonInstaller::InstallFromZip(const CStdString &path)
273 // grab the descriptive XML document from the zip, and read it in
275 // BUG: some zip files return a single item (root folder) that we think is stored, so we don't use the zip:// protocol
277 URIUtils::CreateArchivePath(zipDir, "zip", path, "");
278 if (!CDirectory::GetDirectory(zipDir, items) || items.Size() != 1 || !items[0]->m_bIsFolder)
280 CGUIDialogKaiToast::QueueNotification("", path, g_localizeStrings.Get(24045), TOAST_DISPLAY_TIME, false);
284 // TODO: possibly add support for github generated zips here?
285 CStdString archive = URIUtils::AddFileToFolder(items[0]->GetPath(), "addon.xml");
289 if (xml.LoadFile(archive) && CAddonMgr::Get().LoadAddonDescriptionFromMemory(xml.RootElement(), addon))
291 // set the correct path
292 addon->Props().path = path;
295 return DoInstall(addon);
297 CGUIDialogKaiToast::QueueNotification("", path, g_localizeStrings.Get(24045), TOAST_DISPLAY_TIME, false);
301 void CAddonInstaller::InstallFromXBMCRepo(const set<CStdString> &addonIDs)
303 // first check we have the our repositories up to date (and wait until we do)
304 UpdateRepos(false, true);
306 // now install the addons
307 for (set<CStdString>::const_iterator i = addonIDs.begin(); i != addonIDs.end(); ++i)
311 bool CAddonInstaller::CheckDependencies(const AddonPtr &addon, CAddonDatabase *database /* = NULL */)
313 std::vector<std::string> preDeps;
314 preDeps.push_back(addon->ID());
315 CAddonDatabase localDB;
318 return CheckDependencies(addon, preDeps, *database);
321 bool CAddonInstaller::CheckDependencies(const AddonPtr &addon,
322 std::vector<std::string>& preDeps, CAddonDatabase &database)
325 return true; // a NULL addon has no dependencies
326 ADDONDEPS deps = addon->GetDeps();
328 for (ADDONDEPS::const_iterator i = deps.begin(); i != deps.end(); ++i)
330 const CStdString &addonID = i->first;
331 const AddonVersion &version = i->second.first;
332 bool optional = i->second.second;
334 bool haveAddon = CAddonMgr::Get().GetAddon(addonID, dep);
335 if ((haveAddon && !dep->MeetsVersion(version)) || (!haveAddon && !optional))
336 { // we have it but our version isn't good enough, or we don't have it and we need it
337 if (!database.GetAddon(addonID, dep) || !dep->MeetsVersion(version))
338 { // we don't have it in a repo, or we have it but the version isn't good enough, so dep isn't satisfied.
339 CLog::Log(LOGDEBUG, "Addon %s requires %s version %s which is not available", addon->ID().c_str(), addonID.c_str(), version.c_str());
344 // 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
345 // TODO: should we assume that installed deps are OK?
346 if (dep && std::find(preDeps.begin(), preDeps.end(), dep->ID()) == preDeps.end())
348 if (!CheckDependencies(dep, preDeps, database))
353 preDeps.push_back(dep->ID());
360 void CAddonInstaller::UpdateRepos(bool force, bool wait)
362 CSingleLock lock(m_critSection);
366 { // wait for our job to complete
368 CLog::Log(LOGDEBUG, "%s - waiting for repository update job to finish...", __FUNCTION__);
369 m_repoUpdateDone.Wait();
373 // don't run repo update jobs while on the login screen which runs under the master profile
374 if((g_windowManager.GetActiveWindow() & WINDOW_ID_MASK) == WINDOW_LOGIN_SCREEN)
376 if (!force && m_repoUpdateWatch.IsRunning() && m_repoUpdateWatch.GetElapsedSeconds() < 600)
378 m_repoUpdateWatch.StartZero();
380 CAddonMgr::Get().GetAddons(ADDON_REPOSITORY,addons);
381 for (unsigned int i=0;i<addons.size();++i)
383 CAddonDatabase database;
385 CDateTime lastUpdate = database.GetRepoTimestamp(addons[i]->ID());
386 if (force || !lastUpdate.IsValid() || lastUpdate + CDateTimeSpan(0,6,0,0) < CDateTime::GetCurrentDateTime())
388 CLog::Log(LOGDEBUG,"Checking repositories for updates (triggered by %s)",addons[i]->Name().c_str());
389 m_repoUpdateJob = CJobManager::GetInstance().AddJob(new CRepositoryUpdateJob(addons), this);
391 { // wait for our job to complete
393 CLog::Log(LOGDEBUG, "%s - waiting for this repository update job to finish...", __FUNCTION__);
394 m_repoUpdateDone.Wait();
401 bool CAddonInstaller::HasJob(const CStdString& ID) const
403 CSingleLock lock(m_critSection);
404 return m_downloadJobs.find(ID) != m_downloadJobs.end();
407 void CAddonInstaller::PrunePackageCache()
409 std::map<CStdString,CFileItemList*> packs;
410 int64_t size = EnumeratePackageFolder(packs);
411 int64_t limit = (int64_t)g_advancedSettings.m_addonPackageFolderSize*1024*1024;
416 // 1. Remove the largest packages, leaving at least 2 for each add-on
420 for (std::map<CStdString,CFileItemList*>::const_iterator it = packs.begin();
421 it != packs.end();++it)
423 it->second->Sort(SortByLabel, SortOrderDescending);
424 for (int j=2;j<it->second->Size();++j)
425 items.Add(CFileItemPtr(new CFileItem(*it->second->Get(j))));
427 items.Sort(SortBySize, SortOrderDescending);
429 while (size > limit && i < items.Size())
431 size -= items[i]->m_dwSize;
432 db.RemovePackage(items[i]->GetPath());
433 CFileUtils::DeleteItem(items[i++],true);
438 // 2. Remove the oldest packages (leaving least 1 for each add-on)
440 for (std::map<CStdString,CFileItemList*>::iterator it = packs.begin();
441 it != packs.end();++it)
443 if (it->second->Size() > 1)
444 items.Add(CFileItemPtr(new CFileItem(*it->second->Get(1))));
446 items.Sort(SortByDate, SortOrderAscending);
448 while (size > limit && i < items.Size())
450 size -= items[i]->m_dwSize;
451 db.RemovePackage(items[i]->GetPath());
452 CFileUtils::DeleteItem(items[i++],true);
456 for (std::map<CStdString,CFileItemList*>::iterator it = packs.begin();
457 it != packs.end();++it)
461 int64_t CAddonInstaller::EnumeratePackageFolder(std::map<CStdString,CFileItemList*>& result)
464 CDirectory::GetDirectory("special://home/addons/packages/",items,".zip",DIR_FLAG_NO_FILE_DIRS);
466 for (int i=0;i<items.Size();++i)
468 if (items[i]->m_bIsFolder)
470 size += items[i]->m_dwSize;
471 CStdString pack,dummy;
472 AddonVersion::SplitFileName(pack,dummy,items[i]->GetLabel());
473 if (result.find(pack) == result.end())
474 result[pack] = new CFileItemList;
475 result[pack]->Add(CFileItemPtr(new CFileItem(*items[i])));
481 CAddonInstallJob::CAddonInstallJob(const AddonPtr &addon, const CStdString &hash, bool update, const CStdString &referer)
482 : m_addon(addon), m_hash(hash), m_update(update), m_referer(referer)
486 AddonPtr CAddonInstallJob::GetRepoForAddon(const AddonPtr& addon)
488 CAddonDatabase database;
491 database.GetRepoForAddon(addon->ID(), repo);
493 CAddonMgr::Get().GetAddon(repo, repoPtr);
498 bool CAddonInstallJob::DoWork()
500 AddonPtr repoPtr = GetRepoForAddon(m_addon);
501 CStdString installFrom;
502 if (!repoPtr || repoPtr->Props().libname.empty())
504 // Addons are installed by downloading the .zip package on the server to the local
505 // packages folder, then extracting from the local .zip package into the addons folder
506 // Both these functions are achieved by "copying" using the vfs.
508 CStdString dest="special://home/addons/packages/";
509 CStdString package = URIUtils::AddFileToFolder("special://home/addons/packages/",
510 URIUtils::GetFileName(m_addon->Path()));
511 if (URIUtils::HasSlashAtEnd(m_addon->Path()))
512 { // passed in a folder - all we need do is copy it across
513 installFrom = m_addon->Path();
521 // check that we don't already have a valid copy
522 if (!m_hash.empty() && CFile::Exists(package))
524 if (db.GetPackageHash(m_addon->ID(), package, md5) && m_hash != md5)
526 db.RemovePackage(package);
527 CFile::Delete(package);
531 // zip passed in - download + extract
532 if (!CFile::Exists(package))
534 CStdString path(m_addon->Path());
535 if (!m_referer.empty() && URIUtils::IsInternetStream(path))
538 url.SetProtocolOptions(m_referer);
542 if (!DownloadPackage(path, dest))
544 CFile::Delete(package);
549 // at this point we have the package - check that it is valid
552 md5 = CUtil::GetFileMD5(package);
553 if (!md5.Equals(m_hash))
555 CFile::Delete(package);
556 ReportInstallError(m_addon->ID(), URIUtils::GetFileName(package));
557 CLog::Log(LOGERROR, "MD5 mismatch after download %s", package.c_str());
560 db.AddPackage(m_addon->ID(), package, md5);
563 // check the archive as well - should have just a single folder in the root
565 URIUtils::CreateArchivePath(archive,"zip",package,"");
567 CFileItemList archivedFiles;
568 CDirectory::GetDirectory(archive, archivedFiles);
570 if (archivedFiles.Size() != 1 || !archivedFiles[0]->m_bIsFolder)
572 db.RemovePackage(package);
573 CFile::Delete(package);
576 installFrom = archivedFiles[0]->GetPath();
581 // run any pre-install functions
582 bool reloadAddon = OnPreInstall();
585 if (!Install(installFrom, repoPtr))
586 return false; // something went wrong
588 // run any post-install guff
589 OnPostInstall(reloadAddon);
595 bool CAddonInstallJob::DownloadPackage(const CStdString &path, const CStdString &dest)
596 { // need to download/copy the package first
598 list.Add(CFileItemPtr(new CFileItem(path,false)));
599 list[0]->Select(true);
600 SetFileOperation(CFileOperationJob::ActionReplace, list, dest);
601 return CFileOperationJob::DoWork();
604 bool CAddonInstallJob::OnPreInstall()
606 // check whether this is an active skin - we need to unload it if so
607 if (CSettings::Get().GetString("lookandfeel.skin") == m_addon->ID())
609 CApplicationMessenger::Get().ExecBuiltIn("UnloadSkin", true);
613 if (m_addon->Type() == ADDON_SERVICE)
615 bool running = !CAddonMgr::Get().IsAddonDisabled(m_addon->ID()); //grab a current state
616 CAddonMgr::Get().DisableAddon(m_addon->ID(),false); // enable it so we can remove it??
617 // regrab from manager to have the correct path set
619 ADDON::CAddonMgr::Get().GetAddon(m_addon->ID(), addon);
620 boost::shared_ptr<CService> service = boost::dynamic_pointer_cast<CService>(addon);
623 CAddonMgr::Get().RemoveAddon(m_addon->ID()); // remove it
627 if (m_addon->Type() == ADDON_PVRDLL)
629 // stop the pvr manager, so running pvr add-ons are stopped and closed
630 PVR::CPVRManager::Get().Stop();
635 bool CAddonInstallJob::DeleteAddon(const CStdString &addonFolder)
638 list.Add(CFileItemPtr(new CFileItem(addonFolder, true)));
639 list[0]->Select(true);
640 CFileOperationJob job(CFileOperationJob::ActionDelete, list, "");
644 bool CAddonInstallJob::Install(const CStdString &installFrom, const AddonPtr& repo)
646 // The first thing we do is install dependencies
647 ADDONDEPS deps = m_addon->GetDeps();
648 CStdString referer = StringUtils::Format("Referer=%s-%s.zip",m_addon->ID().c_str(),m_addon->Version().c_str());
649 for (ADDONDEPS::iterator it = deps.begin(); it != deps.end(); ++it)
651 if (it->first.Equals("xbmc.metadata"))
654 const CStdString &addonID = it->first;
655 const AddonVersion &version = it->second.first;
656 bool optional = it->second.second;
658 bool haveAddon = CAddonMgr::Get().GetAddon(addonID, dependency);
659 if ((haveAddon && !dependency->MeetsVersion(version)) || (!haveAddon && !optional))
660 { // we have it but our version isn't good enough, or we don't have it and we need it
661 bool force=(dependency != NULL);
662 // dependency is already queued up for install - ::Install will fail
663 // instead we wait until the Job has finished. note that we
664 // recall install on purpose in case prior installation failed
665 if (CAddonInstaller::Get().HasJob(addonID))
667 while (CAddonInstaller::Get().HasJob(addonID))
671 // don't have the addon or the addon isn't new enough - grab it (no new job for these)
672 if (!CAddonInstaller::Get().Install(addonID, force, referer, false))
677 // now that we have all our dependencies, we can install our add-on
681 CStdString s = StringUtils::Format("plugin://%s/?action=install"
682 "&package=%s&version=%s", repo->ID().c_str(),
683 m_addon->ID().c_str(),
684 m_addon->Version().c_str());
685 if (!CDirectory::GetDirectory(s, dummy))
690 CStdString addonFolder(installFrom);
691 URIUtils::RemoveSlashAtEnd(addonFolder);
692 addonFolder = URIUtils::AddFileToFolder("special://home/addons/",
693 URIUtils::GetFileName(addonFolder));
695 CFileItemList install;
696 install.Add(CFileItemPtr(new CFileItem(installFrom, true)));
697 install[0]->Select(true);
698 CFileOperationJob job(CFileOperationJob::ActionReplace, install, "special://home/addons/");
701 if (!job.DoWork() || !CAddonMgr::Get().LoadAddonDescription(addonFolder, addon))
702 { // failed extraction or failed to load addon description
703 CStdString addonID = URIUtils::GetFileName(addonFolder);
704 ReportInstallError(addonID, addonID);
705 CLog::Log(LOGERROR,"Could not read addon description of %s", addonID.c_str());
706 DeleteAddon(addonFolder);
710 // Update the addon manager so that it has the newly installed add-on.
711 CAddonMgr::Get().FindAddons();
716 void CAddonInstallJob::OnPostInstall(bool reloadAddon)
718 if (m_addon->Type() < ADDON_VIZ_LIBRARY && CSettings::Get().GetBool("general.addonnotifications"))
720 CGUIDialogKaiToast::QueueNotification(m_addon->Icon(),
722 g_localizeStrings.Get(m_update ? 24065 : 24064),
723 TOAST_DISPLAY_TIME,false,
726 if (m_addon->Type() == ADDON_SKIN)
728 if (reloadAddon || (!m_update && CGUIDialogYesNo::ShowAndGetInput(m_addon->Name(),
729 g_localizeStrings.Get(24099),"","")))
731 CGUIDialogKaiToast *toast = (CGUIDialogKaiToast *)g_windowManager.GetWindow(WINDOW_DIALOG_KAI_TOAST);
737 if (CSettings::Get().GetString("lookandfeel.skin") == m_addon->ID())
738 CApplicationMessenger::Get().ExecBuiltIn("ReloadSkin", true);
740 CSettings::Get().SetString("lookandfeel.skin",m_addon->ID().c_str());
744 if (m_addon->Type() == ADDON_SERVICE)
746 CAddonMgr::Get().DisableAddon(m_addon->ID(),!reloadAddon); //return it into state it was before OnPreInstall()
747 if (reloadAddon) // reload/start it if it was running
749 // regrab from manager to have the correct path set
751 CAddonMgr::Get().GetAddon(m_addon->ID(), addon);
752 boost::shared_ptr<CService> service = boost::dynamic_pointer_cast<CService>(addon);
758 if (m_addon->Type() == ADDON_REPOSITORY)
761 addons.push_back(m_addon);
762 CJobManager::GetInstance().AddJob(new CRepositoryUpdateJob(addons), &CAddonInstaller::Get());
765 if (m_addon->Type() == ADDON_PVRDLL)
767 // (re)start the pvr manager
768 PVR::CPVRManager::Get().Start(true);
772 void CAddonInstallJob::ReportInstallError(const CStdString& addonID,
773 const CStdString& fileName)
776 CAddonDatabase database;
778 database.GetAddon(addonID, addon);
782 CAddonMgr::Get().GetAddon(addonID, addon2);
783 CGUIDialogKaiToast::QueueNotification(addon->Icon(),
785 g_localizeStrings.Get(addon2 ? 113 : 114),
786 TOAST_DISPLAY_TIME, false);
790 CGUIDialogKaiToast::QueueNotification(CGUIDialogKaiToast::Error,
792 g_localizeStrings.Get(114),
793 TOAST_DISPLAY_TIME, false);
797 CStdString CAddonInstallJob::AddonID() const
799 return (m_addon) ? m_addon->ID() : "";
802 CAddonUnInstallJob::CAddonUnInstallJob(const AddonPtr &addon)
807 bool CAddonUnInstallJob::DoWork()
809 if (m_addon->Type() == ADDON_PVRDLL)
811 // stop the pvr manager, so running pvr add-ons are stopped and closed
812 PVR::CPVRManager::Get().Stop();
814 if (m_addon->Type() == ADDON_SERVICE)
816 boost::shared_ptr<CService> service = boost::dynamic_pointer_cast<CService>(m_addon);
821 AddonPtr repoPtr = CAddonInstallJob::GetRepoForAddon(m_addon);
822 RepositoryPtr therepo = boost::dynamic_pointer_cast<CRepository>(repoPtr);
823 if (therepo && !therepo->Props().libname.empty())
826 CStdString s = StringUtils::Format("plugin://%s/?action=uninstall"
827 "&package=%s", therepo->ID().c_str(), m_addon->ID().c_str());
828 if (!CDirectory::GetDirectory(s, dummy))
833 if (!CAddonInstallJob::DeleteAddon(m_addon->Path()))
842 void CAddonUnInstallJob::OnPostUnInstall()
844 if (m_addon->Type() == ADDON_REPOSITORY)
846 CAddonDatabase database;
848 database.DeleteRepository(m_addon->ID());
853 XFILE::CFavouritesDirectory::Load(items);
854 for (int i=0; i < items.Size(); ++i)
856 if (items[i]->GetPath().find(m_addon->ID()) != std::string::npos)
858 items.Remove(items[i].get());
864 CFavouritesDirectory::Save(items);
866 if (m_addon->Type() == ADDON_PVRDLL)
868 if (CSettings::Get().GetBool("pvrmanager.enabled"))
869 PVR::CPVRManager::Get().Start(true);