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();
86 JobMap::iterator i = find_if(m_downloadJobs.begin(), m_downloadJobs.end(), bind2nd(find_map(), jobID));
87 if (i != m_downloadJobs.end())
88 m_downloadJobs.erase(i);
93 CGUIMessage msg(GUI_MSG_NOTIFY_ALL, 0, 0, GUI_MSG_UPDATE);
94 g_windowManager.SendThreadMessage(msg);
97 void CAddonInstaller::OnJobProgress(unsigned int jobID, unsigned int progress, unsigned int total, const CJob *job)
99 CSingleLock lock(m_critSection);
100 JobMap::iterator i = find_if(m_downloadJobs.begin(), m_downloadJobs.end(), bind2nd(find_map(), jobID));
101 if (i != m_downloadJobs.end())
102 { // update job progress
103 i->second.progress = progress;
104 CGUIMessage msg(GUI_MSG_NOTIFY_ALL, 0, 0, GUI_MSG_UPDATE_ITEM);
105 msg.SetStringParam(i->first);
107 g_windowManager.SendThreadMessage(msg);
111 bool CAddonInstaller::IsDownloading() const
113 CSingleLock lock(m_critSection);
114 return !m_downloadJobs.empty();
117 void CAddonInstaller::GetInstallList(VECADDONS &addons) const
119 CSingleLock lock(m_critSection);
120 vector<CStdString> addonIDs;
121 for (JobMap::const_iterator i = m_downloadJobs.begin(); i != m_downloadJobs.end(); ++i)
124 addonIDs.push_back(i->first);
128 CAddonDatabase database;
130 for (vector<CStdString>::iterator it = addonIDs.begin(); it != addonIDs.end();++it)
133 if (database.GetAddon(*it, addon))
134 addons.push_back(addon);
138 bool CAddonInstaller::GetProgress(const CStdString &addonID, unsigned int &percent) const
140 CSingleLock lock(m_critSection);
141 JobMap::const_iterator i = m_downloadJobs.find(addonID);
142 if (i != m_downloadJobs.end())
144 percent = i->second.progress;
150 bool CAddonInstaller::Cancel(const CStdString &addonID)
152 CSingleLock lock(m_critSection);
153 JobMap::iterator i = m_downloadJobs.find(addonID);
154 if (i != m_downloadJobs.end())
156 CJobManager::GetInstance().CancelJob(i->second.jobID);
157 m_downloadJobs.erase(i);
163 bool CAddonInstaller::PromptForInstall(const CStdString &addonID, AddonPtr &addon)
165 // we assume that addons that are enabled don't get to this routine (i.e. that GetAddon() has been called)
166 if (CAddonMgr::Get().GetAddon(addonID, addon, ADDON_UNKNOWN, false))
167 return false; // addon is installed but disabled, and the user has specifically activated something that needs
168 // the addon - should we enable it?
170 // check we have it available
171 CAddonDatabase database;
173 if (database.GetAddon(addonID, addon))
174 { // yes - ask user if they want it installed
175 if (!CGUIDialogYesNo::ShowAndGetInput(g_localizeStrings.Get(24076), g_localizeStrings.Get(24100),
176 addon->Name().c_str(), g_localizeStrings.Get(24101)))
178 if (Install(addonID, true))
180 CGUIDialogProgress *progress = (CGUIDialogProgress *)g_windowManager.GetWindow(WINDOW_DIALOG_PROGRESS);
183 progress->SetHeading(13413); // Downloading
184 progress->SetLine(0, "");
185 progress->SetLine(1, addon->Name());
186 progress->SetLine(2, "");
187 progress->SetPercentage(0);
188 progress->StartModal();
191 progress->Progress();
192 unsigned int percent;
193 if (progress->IsCanceled())
198 if (!GetProgress(addonID, percent))
200 progress->SetPercentage(percent);
204 return CAddonMgr::Get().GetAddon(addonID, addon);
210 bool CAddonInstaller::Install(const CStdString &addonID, bool force, const CStdString &referer, bool background)
213 bool addonInstalled = CAddonMgr::Get().GetAddon(addonID, addon, ADDON_UNKNOWN, false);
214 if (addonInstalled && !force)
217 // check whether we have it available in a repository
218 CAddonDatabase database;
220 if (database.GetAddon(addonID, addon))
223 database.GetRepoForAddon(addonID,repo);
225 CAddonMgr::Get().GetAddon(repo,ptr);
226 RepositoryPtr therepo = boost::dynamic_pointer_cast<CRepository>(ptr);
229 hash = therepo->GetAddonHash(addon);
230 return DoInstall(addon, hash, addonInstalled, referer, background);
235 bool CAddonInstaller::DoInstall(const AddonPtr &addon, const CStdString &hash, bool update, const CStdString &referer, bool background)
237 // check whether we already have the addon installing
238 CSingleLock lock(m_critSection);
239 if (m_downloadJobs.find(addon->ID()) != m_downloadJobs.end())
242 // check whether all the dependencies are available or not
243 if (!CheckDependencies(addon))
245 CGUIDialogKaiToast::QueueNotification(addon->Icon(), addon->Name(), g_localizeStrings.Get(24044), TOAST_DISPLAY_TIME, false);
251 unsigned int jobID = CJobManager::GetInstance().AddJob(new CAddonInstallJob(addon, hash, update, referer), this);
252 m_downloadJobs.insert(make_pair(addon->ID(), CDownloadJob(jobID)));
256 m_downloadJobs.insert(make_pair(addon->ID(), CDownloadJob(0)));
258 CAddonInstallJob job(addon, hash, update, referer);
260 { // TODO: dump something to debug log?
264 JobMap::iterator i = m_downloadJobs.find(addon->ID());
265 m_downloadJobs.erase(i);
270 bool CAddonInstaller::InstallFromZip(const CStdString &path)
272 // grab the descriptive XML document from the zip, and read it in
274 // BUG: some zip files return a single item (root folder) that we think is stored, so we don't use the zip:// protocol
276 URIUtils::CreateArchivePath(zipDir, "zip", path, "");
277 if (!CDirectory::GetDirectory(zipDir, items) || items.Size() != 1 || !items[0]->m_bIsFolder)
279 CGUIDialogKaiToast::QueueNotification("", path, g_localizeStrings.Get(24045), TOAST_DISPLAY_TIME, false);
283 // TODO: possibly add support for github generated zips here?
284 CStdString archive = URIUtils::AddFileToFolder(items[0]->GetPath(), "addon.xml");
288 if (xml.LoadFile(archive) && CAddonMgr::Get().LoadAddonDescriptionFromMemory(xml.RootElement(), addon))
290 // set the correct path
291 addon->Props().path = path;
294 return DoInstall(addon);
296 CGUIDialogKaiToast::QueueNotification("", path, g_localizeStrings.Get(24045), TOAST_DISPLAY_TIME, false);
300 void CAddonInstaller::InstallFromXBMCRepo(const set<CStdString> &addonIDs)
302 // first check we have the our repositories up to date (and wait until we do)
303 UpdateRepos(false, true);
305 // now install the addons
306 for (set<CStdString>::const_iterator i = addonIDs.begin(); i != addonIDs.end(); ++i)
310 bool CAddonInstaller::CheckDependencies(const AddonPtr &addon)
312 std::vector<std::string> preDeps;
313 preDeps.push_back(addon->ID());
314 return CheckDependencies(addon, preDeps);
317 bool CAddonInstaller::CheckDependencies(const AddonPtr &addon,
318 std::vector<std::string>& preDeps)
321 return true; // a NULL addon has no dependencies
322 ADDONDEPS deps = addon->GetDeps();
323 CAddonDatabase database;
325 for (ADDONDEPS::const_iterator i = deps.begin(); i != deps.end(); ++i)
327 const CStdString &addonID = i->first;
328 const AddonVersion &version = i->second.first;
329 bool optional = i->second.second;
331 bool haveAddon = CAddonMgr::Get().GetAddon(addonID, dep);
332 if ((haveAddon && !dep->MeetsVersion(version)) || (!haveAddon && !optional))
333 { // we have it but our version isn't good enough, or we don't have it and we need it
334 if (!database.GetAddon(addonID, dep) || !dep->MeetsVersion(version))
335 { // we don't have it in a repo, or we have it but the version isn't good enough, so dep isn't satisfied.
336 CLog::Log(LOGDEBUG, "Addon %s requires %s version %s which is not available", addon->ID().c_str(), addonID.c_str(), version.c_str());
340 // 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
341 // TODO: should we assume that installed deps are OK?
342 if (dep && std::find(preDeps.begin(), preDeps.end(), dep->ID()) == preDeps.end())
344 if (!CheckDependencies(dep, preDeps))
346 preDeps.push_back(dep->ID());
352 void CAddonInstaller::UpdateRepos(bool force, bool wait)
354 CSingleLock lock(m_critSection);
358 { // wait for our job to complete
360 CLog::Log(LOGDEBUG, "%s - waiting for repository update job to finish...", __FUNCTION__);
361 m_repoUpdateDone.Wait();
365 // don't run repo update jobs while on the login screen which runs under the master profile
366 if((g_windowManager.GetActiveWindow() & WINDOW_ID_MASK) == WINDOW_LOGIN_SCREEN)
368 if (!force && m_repoUpdateWatch.IsRunning() && m_repoUpdateWatch.GetElapsedSeconds() < 600)
370 m_repoUpdateWatch.StartZero();
372 CAddonMgr::Get().GetAddons(ADDON_REPOSITORY,addons);
373 for (unsigned int i=0;i<addons.size();++i)
375 CAddonDatabase database;
377 CDateTime lastUpdate = database.GetRepoTimestamp(addons[i]->ID());
378 if (force || !lastUpdate.IsValid() || lastUpdate + CDateTimeSpan(0,6,0,0) < CDateTime::GetCurrentDateTime())
380 CLog::Log(LOGDEBUG,"Checking repositories for updates (triggered by %s)",addons[i]->Name().c_str());
381 m_repoUpdateJob = CJobManager::GetInstance().AddJob(new CRepositoryUpdateJob(addons), this);
383 { // wait for our job to complete
385 CLog::Log(LOGDEBUG, "%s - waiting for this repository update job to finish...", __FUNCTION__);
386 m_repoUpdateDone.Wait();
393 bool CAddonInstaller::HasJob(const CStdString& ID) const
395 CSingleLock lock(m_critSection);
396 return m_downloadJobs.find(ID) != m_downloadJobs.end();
399 void CAddonInstaller::PrunePackageCache()
401 std::map<CStdString,CFileItemList*> packs;
402 int64_t size = EnumeratePackageFolder(packs);
403 int64_t limit = (int64_t)g_advancedSettings.m_addonPackageFolderSize*1024*1024;
408 // 1. Remove the largest packages, leaving at least 2 for each add-on
412 for (std::map<CStdString,CFileItemList*>::const_iterator it = packs.begin();
413 it != packs.end();++it)
415 it->second->Sort(SortByLabel, SortOrderDescending);
416 for (int j=2;j<it->second->Size();++j)
417 items.Add(CFileItemPtr(new CFileItem(*it->second->Get(j))));
419 items.Sort(SortBySize, SortOrderDescending);
421 while (size > limit && i < items.Size())
423 size -= items[i]->m_dwSize;
424 db.RemovePackage(items[i]->GetPath());
425 CFileUtils::DeleteItem(items[i++],true);
430 // 2. Remove the oldest packages (leaving least 1 for each add-on)
432 for (std::map<CStdString,CFileItemList*>::iterator it = packs.begin();
433 it != packs.end();++it)
435 if (it->second->Size() > 1)
436 items.Add(CFileItemPtr(new CFileItem(*it->second->Get(1))));
438 items.Sort(SortByDate, SortOrderAscending);
440 while (size > limit && i < items.Size())
442 size -= items[i]->m_dwSize;
443 db.RemovePackage(items[i]->GetPath());
444 CFileUtils::DeleteItem(items[i++],true);
448 for (std::map<CStdString,CFileItemList*>::iterator it = packs.begin();
449 it != packs.end();++it)
453 int64_t CAddonInstaller::EnumeratePackageFolder(std::map<CStdString,CFileItemList*>& result)
456 CDirectory::GetDirectory("special://home/addons/packages/",items,".zip",DIR_FLAG_NO_FILE_DIRS);
458 for (int i=0;i<items.Size();++i)
460 if (items[i]->m_bIsFolder)
462 size += items[i]->m_dwSize;
463 CStdString pack,dummy;
464 AddonVersion::SplitFileName(pack,dummy,items[i]->GetLabel());
465 if (result.find(pack) == result.end())
466 result[pack] = new CFileItemList;
467 result[pack]->Add(CFileItemPtr(new CFileItem(*items[i])));
473 CAddonInstallJob::CAddonInstallJob(const AddonPtr &addon, const CStdString &hash, bool update, const CStdString &referer)
474 : m_addon(addon), m_hash(hash), m_update(update), m_referer(referer)
478 AddonPtr CAddonInstallJob::GetRepoForAddon(const AddonPtr& addon)
480 CAddonDatabase database;
483 database.GetRepoForAddon(addon->ID(), repo);
485 CAddonMgr::Get().GetAddon(repo, repoPtr);
490 bool CAddonInstallJob::DoWork()
492 AddonPtr repoPtr = GetRepoForAddon(m_addon);
493 CStdString installFrom;
494 if (!repoPtr || repoPtr->Props().libname.empty())
496 // Addons are installed by downloading the .zip package on the server to the local
497 // packages folder, then extracting from the local .zip package into the addons folder
498 // Both these functions are achieved by "copying" using the vfs.
500 CStdString dest="special://home/addons/packages/";
501 CStdString package = URIUtils::AddFileToFolder("special://home/addons/packages/",
502 URIUtils::GetFileName(m_addon->Path()));
503 if (URIUtils::HasSlashAtEnd(m_addon->Path()))
504 { // passed in a folder - all we need do is copy it across
505 installFrom = m_addon->Path();
513 // check that we don't already have a valid copy
514 if (!m_hash.empty() && CFile::Exists(package))
516 if (db.GetPackageHash(m_addon->ID(), package, md5) && m_hash != md5)
518 db.RemovePackage(package);
519 CFile::Delete(package);
523 // zip passed in - download + extract
524 if (!CFile::Exists(package))
526 CStdString path(m_addon->Path());
527 if (!m_referer.empty() && URIUtils::IsInternetStream(path))
530 url.SetProtocolOptions(m_referer);
534 if (!DownloadPackage(path, dest))
536 CFile::Delete(package);
541 // at this point we have the package - check that it is valid
544 md5 = CUtil::GetFileMD5(package);
545 if (!md5.Equals(m_hash))
547 CFile::Delete(package);
548 ReportInstallError(m_addon->ID(), URIUtils::GetFileName(package));
549 CLog::Log(LOGERROR, "MD5 mismatch after download %s", package.c_str());
552 db.AddPackage(m_addon->ID(), package, md5);
555 // check the archive as well - should have just a single folder in the root
557 URIUtils::CreateArchivePath(archive,"zip",package,"");
559 CFileItemList archivedFiles;
560 CDirectory::GetDirectory(archive, archivedFiles);
562 if (archivedFiles.Size() != 1 || !archivedFiles[0]->m_bIsFolder)
564 db.RemovePackage(package);
565 CFile::Delete(package);
568 installFrom = archivedFiles[0]->GetPath();
573 // run any pre-install functions
574 bool reloadAddon = OnPreInstall();
577 if (!Install(installFrom, repoPtr))
578 return false; // something went wrong
580 // run any post-install guff
581 OnPostInstall(reloadAddon);
587 bool CAddonInstallJob::DownloadPackage(const CStdString &path, const CStdString &dest)
588 { // need to download/copy the package first
590 list.Add(CFileItemPtr(new CFileItem(path,false)));
591 list[0]->Select(true);
592 SetFileOperation(CFileOperationJob::ActionReplace, list, dest);
593 return CFileOperationJob::DoWork();
596 bool CAddonInstallJob::OnPreInstall()
598 // check whether this is an active skin - we need to unload it if so
599 if (CSettings::Get().GetString("lookandfeel.skin") == m_addon->ID())
601 CApplicationMessenger::Get().ExecBuiltIn("UnloadSkin", true);
605 if (m_addon->Type() == ADDON_SERVICE)
607 bool running = !CAddonMgr::Get().IsAddonDisabled(m_addon->ID()); //grab a current state
608 CAddonMgr::Get().DisableAddon(m_addon->ID(),false); // enable it so we can remove it??
609 // regrab from manager to have the correct path set
611 ADDON::CAddonMgr::Get().GetAddon(m_addon->ID(), addon);
612 boost::shared_ptr<CService> service = boost::dynamic_pointer_cast<CService>(addon);
615 CAddonMgr::Get().RemoveAddon(m_addon->ID()); // remove it
619 if (m_addon->Type() == ADDON_PVRDLL)
621 // stop the pvr manager, so running pvr add-ons are stopped and closed
622 PVR::CPVRManager::Get().Stop();
627 bool CAddonInstallJob::DeleteAddon(const CStdString &addonFolder)
630 list.Add(CFileItemPtr(new CFileItem(addonFolder, true)));
631 list[0]->Select(true);
632 CFileOperationJob job(CFileOperationJob::ActionDelete, list, "");
636 bool CAddonInstallJob::Install(const CStdString &installFrom, const AddonPtr& repo)
638 // The first thing we do is install dependencies
639 ADDONDEPS deps = m_addon->GetDeps();
640 CStdString referer = StringUtils::Format("Referer=%s-%s.zip",m_addon->ID().c_str(),m_addon->Version().c_str());
641 for (ADDONDEPS::iterator it = deps.begin(); it != deps.end(); ++it)
643 if (it->first.Equals("xbmc.metadata"))
646 const CStdString &addonID = it->first;
647 const AddonVersion &version = it->second.first;
648 bool optional = it->second.second;
650 bool haveAddon = CAddonMgr::Get().GetAddon(addonID, dependency);
651 if ((haveAddon && !dependency->MeetsVersion(version)) || (!haveAddon && !optional))
652 { // we have it but our version isn't good enough, or we don't have it and we need it
653 bool force=(dependency != NULL);
654 // dependency is already queued up for install - ::Install will fail
655 // instead we wait until the Job has finished. note that we
656 // recall install on purpose in case prior installation failed
657 if (CAddonInstaller::Get().HasJob(addonID))
659 while (CAddonInstaller::Get().HasJob(addonID))
663 // don't have the addon or the addon isn't new enough - grab it (no new job for these)
664 if (!CAddonInstaller::Get().Install(addonID, force, referer, false))
669 // now that we have all our dependencies, we can install our add-on
673 CStdString s = StringUtils::Format("plugin://%s/?action=install"
674 "&package=%s&version=%s", repo->ID().c_str(),
675 m_addon->ID().c_str(),
676 m_addon->Version().c_str());
677 if (!CDirectory::GetDirectory(s, dummy))
682 CStdString addonFolder(installFrom);
683 URIUtils::RemoveSlashAtEnd(addonFolder);
684 addonFolder = URIUtils::AddFileToFolder("special://home/addons/",
685 URIUtils::GetFileName(addonFolder));
687 CFileItemList install;
688 install.Add(CFileItemPtr(new CFileItem(installFrom, true)));
689 install[0]->Select(true);
690 CFileOperationJob job(CFileOperationJob::ActionReplace, install, "special://home/addons/");
693 if (!job.DoWork() || !CAddonMgr::Get().LoadAddonDescription(addonFolder, addon))
694 { // failed extraction or failed to load addon description
695 CStdString addonID = URIUtils::GetFileName(addonFolder);
696 ReportInstallError(addonID, addonID);
697 CLog::Log(LOGERROR,"Could not read addon description of %s", addonID.c_str());
698 DeleteAddon(addonFolder);
702 // Update the addon manager so that it has the newly installed add-on.
703 CAddonMgr::Get().FindAddons();
708 void CAddonInstallJob::OnPostInstall(bool reloadAddon)
710 if (m_addon->Type() < ADDON_VIZ_LIBRARY && CSettings::Get().GetBool("general.addonnotifications"))
712 CGUIDialogKaiToast::QueueNotification(m_addon->Icon(),
714 g_localizeStrings.Get(m_update ? 24065 : 24064),
715 TOAST_DISPLAY_TIME,false,
718 if (m_addon->Type() == ADDON_SKIN)
720 if (reloadAddon || (!m_update && CGUIDialogYesNo::ShowAndGetInput(m_addon->Name(),
721 g_localizeStrings.Get(24099),"","")))
723 CGUIDialogKaiToast *toast = (CGUIDialogKaiToast *)g_windowManager.GetWindow(WINDOW_DIALOG_KAI_TOAST);
729 CSettings::Get().SetString("lookandfeel.skin",m_addon->ID().c_str());
733 if (m_addon->Type() == ADDON_SERVICE)
735 CAddonMgr::Get().DisableAddon(m_addon->ID(),!reloadAddon); //return it into state it was before OnPreInstall()
736 if (reloadAddon) // reload/start it if it was running
738 // regrab from manager to have the correct path set
740 CAddonMgr::Get().GetAddon(m_addon->ID(), addon);
741 boost::shared_ptr<CService> service = boost::dynamic_pointer_cast<CService>(addon);
747 if (m_addon->Type() == ADDON_REPOSITORY)
750 addons.push_back(m_addon);
751 CJobManager::GetInstance().AddJob(new CRepositoryUpdateJob(addons), &CAddonInstaller::Get());
754 if (m_addon->Type() == ADDON_PVRDLL)
756 // (re)start the pvr manager
757 PVR::CPVRManager::Get().Start(true);
761 void CAddonInstallJob::ReportInstallError(const CStdString& addonID,
762 const CStdString& fileName)
765 CAddonDatabase database;
767 database.GetAddon(addonID, addon);
771 CAddonMgr::Get().GetAddon(addonID, addon2);
772 CGUIDialogKaiToast::QueueNotification(addon->Icon(),
774 g_localizeStrings.Get(addon2 ? 113 : 114),
775 TOAST_DISPLAY_TIME, false);
779 CGUIDialogKaiToast::QueueNotification(CGUIDialogKaiToast::Error,
781 g_localizeStrings.Get(114),
782 TOAST_DISPLAY_TIME, false);
786 CStdString CAddonInstallJob::AddonID() const
788 return (m_addon) ? m_addon->ID() : "";
791 CAddonUnInstallJob::CAddonUnInstallJob(const AddonPtr &addon)
796 bool CAddonUnInstallJob::DoWork()
798 if (m_addon->Type() == ADDON_PVRDLL)
800 // stop the pvr manager, so running pvr add-ons are stopped and closed
801 PVR::CPVRManager::Get().Stop();
803 if (m_addon->Type() == ADDON_SERVICE)
805 boost::shared_ptr<CService> service = boost::dynamic_pointer_cast<CService>(m_addon);
810 AddonPtr repoPtr = CAddonInstallJob::GetRepoForAddon(m_addon);
811 RepositoryPtr therepo = boost::dynamic_pointer_cast<CRepository>(repoPtr);
812 if (therepo && !therepo->Props().libname.empty())
815 CStdString s = StringUtils::Format("plugin://%s/?action=uninstall"
816 "&package=%s", therepo->ID().c_str(), m_addon->ID().c_str());
817 if (!CDirectory::GetDirectory(s, dummy))
822 if (!CAddonInstallJob::DeleteAddon(m_addon->Path()))
831 void CAddonUnInstallJob::OnPostUnInstall()
833 if (m_addon->Type() == ADDON_REPOSITORY)
835 CAddonDatabase database;
837 database.DeleteRepository(m_addon->ID());
842 XFILE::CFavouritesDirectory::Load(items);
843 for (int i=0; i < items.Size(); ++i)
845 if (items[i]->GetPath().find(m_addon->ID()) != std::string::npos)
847 items.Remove(items[i].get());
853 CFavouritesDirectory::Save(items);
855 if (m_addon->Type() == ADDON_PVRDLL)
857 if (CSettings::Get().GetBool("pvrmanager.enabled"))
858 PVR::CPVRManager::Get().Start(true);