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 // TODO: we currently assume that dependencies will install correctly (and each of their dependencies and so on).
244 // it may be better to install the dependencies first to minimise the chance of an addon becoming orphaned due to
246 if (!CheckDependencies(addon))
248 CGUIDialogKaiToast::QueueNotification(addon->Icon(), addon->Name(), g_localizeStrings.Get(24044), TOAST_DISPLAY_TIME, false);
254 unsigned int jobID = CJobManager::GetInstance().AddJob(new CAddonInstallJob(addon, hash, update, referer), this);
255 m_downloadJobs.insert(make_pair(addon->ID(), CDownloadJob(jobID)));
259 m_downloadJobs.insert(make_pair(addon->ID(), CDownloadJob(0)));
261 CAddonInstallJob job(addon, hash, update, referer);
263 { // TODO: dump something to debug log?
267 JobMap::iterator i = m_downloadJobs.find(addon->ID());
268 m_downloadJobs.erase(i);
273 bool CAddonInstaller::InstallFromZip(const CStdString &path)
275 // grab the descriptive XML document from the zip, and read it in
277 // BUG: some zip files return a single item (root folder) that we think is stored, so we don't use the zip:// protocol
279 URIUtils::CreateArchivePath(zipDir, "zip", path, "");
280 if (!CDirectory::GetDirectory(zipDir, items) || items.Size() != 1 || !items[0]->m_bIsFolder)
282 CGUIDialogKaiToast::QueueNotification("", path, g_localizeStrings.Get(24045), TOAST_DISPLAY_TIME, false);
286 // TODO: possibly add support for github generated zips here?
287 CStdString archive = URIUtils::AddFileToFolder(items[0]->GetPath(), "addon.xml");
291 if (xml.LoadFile(archive) && CAddonMgr::Get().LoadAddonDescriptionFromMemory(xml.RootElement(), addon))
293 // set the correct path
294 addon->Props().path = path;
297 return DoInstall(addon);
299 CGUIDialogKaiToast::QueueNotification("", path, g_localizeStrings.Get(24045), TOAST_DISPLAY_TIME, false);
303 void CAddonInstaller::InstallFromXBMCRepo(const set<CStdString> &addonIDs)
305 // first check we have the our repositories up to date (and wait until we do)
306 UpdateRepos(false, true);
308 // now install the addons
309 for (set<CStdString>::const_iterator i = addonIDs.begin(); i != addonIDs.end(); ++i)
313 bool CAddonInstaller::CheckDependencies(const AddonPtr &addon)
315 std::vector<std::string> preDeps;
316 preDeps.push_back(addon->ID());
317 return CheckDependencies(addon, preDeps);
320 bool CAddonInstaller::CheckDependencies(const AddonPtr &addon,
321 std::vector<std::string>& preDeps)
324 return true; // a NULL addon has no dependencies
325 ADDONDEPS deps = addon->GetDeps();
326 CAddonDatabase database;
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());
343 // 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
344 // TODO: should we assume that installed deps are OK?
345 if (dep && std::find(preDeps.begin(), preDeps.end(), dep->ID()) == preDeps.end())
347 if (!CheckDependencies(dep, preDeps))
349 preDeps.push_back(dep->ID());
355 void CAddonInstaller::UpdateRepos(bool force, bool wait)
357 CSingleLock lock(m_critSection);
361 { // wait for our job to complete
363 CLog::Log(LOGDEBUG, "%s - waiting for repository update job to finish...", __FUNCTION__);
364 m_repoUpdateDone.Wait();
368 // don't run repo update jobs while on the login screen which runs under the master profile
369 if((g_windowManager.GetActiveWindow() & WINDOW_ID_MASK) == WINDOW_LOGIN_SCREEN)
371 if (!force && m_repoUpdateWatch.IsRunning() && m_repoUpdateWatch.GetElapsedSeconds() < 600)
373 m_repoUpdateWatch.StartZero();
375 CAddonMgr::Get().GetAddons(ADDON_REPOSITORY,addons);
376 for (unsigned int i=0;i<addons.size();++i)
378 CAddonDatabase database;
380 CDateTime lastUpdate = database.GetRepoTimestamp(addons[i]->ID());
381 if (force || !lastUpdate.IsValid() || lastUpdate + CDateTimeSpan(0,6,0,0) < CDateTime::GetCurrentDateTime())
383 CLog::Log(LOGDEBUG,"Checking repositories for updates (triggered by %s)",addons[i]->Name().c_str());
384 m_repoUpdateJob = CJobManager::GetInstance().AddJob(new CRepositoryUpdateJob(addons), this);
386 { // wait for our job to complete
388 CLog::Log(LOGDEBUG, "%s - waiting for this repository update job to finish...", __FUNCTION__);
389 m_repoUpdateDone.Wait();
396 bool CAddonInstaller::HasJob(const CStdString& ID) const
398 CSingleLock lock(m_critSection);
399 return m_downloadJobs.find(ID) != m_downloadJobs.end();
402 void CAddonInstaller::PrunePackageCache()
404 std::map<CStdString,CFileItemList*> packs;
405 int64_t size = EnumeratePackageFolder(packs);
406 int64_t limit = (int64_t)g_advancedSettings.m_addonPackageFolderSize*1024*1024;
411 // 1. Remove the largest packages, leaving at least 2 for each add-on
413 for (std::map<CStdString,CFileItemList*>::const_iterator it = packs.begin();
414 it != packs.end();++it)
416 it->second->Sort(SortByLabel, SortOrderDescending);
417 for (int j=2;j<it->second->Size();++j)
418 items.Add(CFileItemPtr(new CFileItem(*it->second->Get(j))));
420 items.Sort(SortBySize, SortOrderDescending);
422 while (size > limit && i < items.Size())
424 size -= items[i]->m_dwSize;
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 CFileUtils::DeleteItem(items[i++],true);
447 for (std::map<CStdString,CFileItemList*>::iterator it = packs.begin();
448 it != packs.end();++it)
452 int64_t CAddonInstaller::EnumeratePackageFolder(std::map<CStdString,CFileItemList*>& result)
455 CDirectory::GetDirectory("special://home/addons/packages/",items,".zip",DIR_FLAG_NO_FILE_DIRS);
457 for (int i=0;i<items.Size();++i)
459 if (items[i]->m_bIsFolder)
461 size += items[i]->m_dwSize;
462 CStdString pack,dummy;
463 AddonVersion::SplitFileName(pack,dummy,items[i]->GetLabel());
464 if (result.find(pack) == result.end())
465 result[pack] = new CFileItemList;
466 result[pack]->Add(CFileItemPtr(new CFileItem(*items[i])));
472 CAddonInstallJob::CAddonInstallJob(const AddonPtr &addon, const CStdString &hash, bool update, const CStdString &referer)
473 : m_addon(addon), m_hash(hash), m_update(update), m_referer(referer)
477 AddonPtr CAddonInstallJob::GetRepoForAddon(const AddonPtr& addon)
479 CAddonDatabase database;
482 database.GetRepoForAddon(addon->ID(), repo);
484 CAddonMgr::Get().GetAddon(repo, repoPtr);
489 bool CAddonInstallJob::DoWork()
491 AddonPtr repoPtr = GetRepoForAddon(m_addon);
492 CStdString installFrom;
493 if (!repoPtr || repoPtr->Props().libname.IsEmpty())
495 // Addons are installed by downloading the .zip package on the server to the local
496 // packages folder, then extracting from the local .zip package into the addons folder
497 // Both these functions are achieved by "copying" using the vfs.
499 CStdString dest="special://home/addons/packages/";
500 CStdString package = URIUtils::AddFileToFolder("special://home/addons/packages/",
501 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();
509 // zip passed in - download + extract
510 CStdString path(m_addon->Path());
511 if (!m_referer.IsEmpty() && URIUtils::IsInternetStream(path))
514 url.SetProtocolOptions(m_referer);
517 if (!CFile::Exists(package) && !DownloadPackage(path, dest))
519 CFile::Delete(package);
523 // at this point we have the package - check that it is valid
524 if (!CFile::Exists(package) || !CheckHash(package))
526 CFile::Delete(package);
530 // check the archive as well - should have just a single folder in the root
532 URIUtils::CreateArchivePath(archive,"zip",package,"");
534 CFileItemList archivedFiles;
535 CDirectory::GetDirectory(archive, archivedFiles);
537 if (archivedFiles.Size() != 1 || !archivedFiles[0]->m_bIsFolder)
539 CFile::Delete(package);
542 installFrom = archivedFiles[0]->GetPath();
547 // run any pre-install functions
548 bool reloadAddon = OnPreInstall();
551 if (!Install(installFrom, repoPtr))
552 return false; // something went wrong
554 // run any post-install guff
555 OnPostInstall(reloadAddon);
561 bool CAddonInstallJob::DownloadPackage(const CStdString &path, const CStdString &dest)
562 { // need to download/copy the package first
564 list.Add(CFileItemPtr(new CFileItem(path,false)));
565 list[0]->Select(true);
566 SetFileOperation(CFileOperationJob::ActionReplace, list, dest);
567 return CFileOperationJob::DoWork();
570 bool CAddonInstallJob::OnPreInstall()
572 // check whether this is an active skin - we need to unload it if so
573 if (CSettings::Get().GetString("lookandfeel.skin") == m_addon->ID())
575 CApplicationMessenger::Get().ExecBuiltIn("UnloadSkin", true);
579 if (m_addon->Type() == ADDON_SERVICE)
581 bool running = !CAddonMgr::Get().IsAddonDisabled(m_addon->ID()); //grab a current state
582 CAddonMgr::Get().DisableAddon(m_addon->ID(),false); // enable it so we can remove it??
583 // regrab from manager to have the correct path set
585 ADDON::CAddonMgr::Get().GetAddon(m_addon->ID(), addon);
586 boost::shared_ptr<CService> service = boost::dynamic_pointer_cast<CService>(addon);
589 CAddonMgr::Get().RemoveAddon(m_addon->ID()); // remove it
593 if (m_addon->Type() == ADDON_PVRDLL)
595 // stop the pvr manager, so running pvr add-ons are stopped and closed
596 PVR::CPVRManager::Get().Stop();
601 bool CAddonInstallJob::DeleteAddon(const CStdString &addonFolder)
604 list.Add(CFileItemPtr(new CFileItem(addonFolder, true)));
605 list[0]->Select(true);
606 CFileOperationJob job(CFileOperationJob::ActionDelete, list, "");
610 bool CAddonInstallJob::Install(const CStdString &installFrom, const AddonPtr& repo)
616 s.Format("plugin://%s/?action=install"
617 "&package=%s&version=%s", repo->ID().c_str(),
618 m_addon->ID().c_str(),
619 m_addon->Version().c_str());
620 if (!CDirectory::GetDirectory(s, dummy))
625 CStdString addonFolder(installFrom);
626 URIUtils::RemoveSlashAtEnd(addonFolder);
627 addonFolder = URIUtils::AddFileToFolder("special://home/addons/",
628 URIUtils::GetFileName(addonFolder));
630 CFileItemList install;
631 install.Add(CFileItemPtr(new CFileItem(installFrom, true)));
632 install[0]->Select(true);
633 CFileOperationJob job(CFileOperationJob::ActionReplace, install, "special://home/addons/");
636 if (!job.DoWork() || !CAddonMgr::Get().LoadAddonDescription(addonFolder, addon))
637 { // failed extraction or failed to load addon description
638 CStdString addonID = URIUtils::GetFileName(addonFolder);
639 ReportInstallError(addonID, addonID);
640 CLog::Log(LOGERROR,"Could not read addon description of %s", addonID.c_str());
641 DeleteAddon(addonFolder);
645 // resolve dependencies
646 CAddonMgr::Get().FindAddons(); // needed as GetDeps() grabs directly from c-pluff via the addon manager
647 ADDONDEPS deps = addon->GetDeps();
649 referer.Format("Referer=%s-%s.zip",addon->ID().c_str(),addon->Version().c_str());
650 for (ADDONDEPS::iterator it = deps.begin(); it != deps.end(); ++it)
652 if (it->first.Equals("xbmc.metadata"))
655 const CStdString &addonID = it->first;
656 const AddonVersion &version = it->second.first;
657 bool optional = it->second.second;
659 bool haveAddon = CAddonMgr::Get().GetAddon(addonID, dependency);
660 if ((haveAddon && !dependency->MeetsVersion(version)) || (!haveAddon && !optional))
661 { // we have it but our version isn't good enough, or we don't have it and we need it
662 bool force=(dependency != NULL);
663 // dependency is already queued up for install - ::Install will fail
664 // instead we wait until the Job has finished. note that we
665 // recall install on purpose in case prior installation failed
666 if (CAddonInstaller::Get().HasJob(addonID))
668 while (CAddonInstaller::Get().HasJob(addonID))
672 // don't have the addon or the addon isn't new enough - grab it (no new job for these)
673 if (!CAddonInstaller::Get().Install(addonID, force, referer, false))
675 DeleteAddon(addonFolder);
684 void CAddonInstallJob::OnPostInstall(bool reloadAddon)
686 if (m_addon->Type() < ADDON_VIZ_LIBRARY && CSettings::Get().GetBool("general.addonnotifications"))
688 CGUIDialogKaiToast::QueueNotification(m_addon->Icon(),
690 g_localizeStrings.Get(m_update ? 24065 : 24064),
691 TOAST_DISPLAY_TIME,false,
694 if (m_addon->Type() == ADDON_SKIN)
696 if (reloadAddon || (!m_update && CGUIDialogYesNo::ShowAndGetInput(m_addon->Name(),
697 g_localizeStrings.Get(24099),"","")))
699 CGUIDialogKaiToast *toast = (CGUIDialogKaiToast *)g_windowManager.GetWindow(WINDOW_DIALOG_KAI_TOAST);
705 CSettings::Get().SetString("lookandfeel.skin",m_addon->ID().c_str());
709 if (m_addon->Type() == ADDON_SERVICE)
711 CAddonMgr::Get().DisableAddon(m_addon->ID(),!reloadAddon); //return it into state it was before OnPreInstall()
712 if (reloadAddon) // reload/start it if it was running
714 // regrab from manager to have the correct path set
716 CAddonMgr::Get().GetAddon(m_addon->ID(), addon);
717 boost::shared_ptr<CService> service = boost::dynamic_pointer_cast<CService>(addon);
723 if (m_addon->Type() == ADDON_REPOSITORY)
726 addons.push_back(m_addon);
727 CJobManager::GetInstance().AddJob(new CRepositoryUpdateJob(addons), &CAddonInstaller::Get());
730 if (m_addon->Type() == ADDON_PVRDLL)
732 // (re)start the pvr manager
733 PVR::CPVRManager::Get().Start(true);
737 void CAddonInstallJob::ReportInstallError(const CStdString& addonID,
738 const CStdString& fileName)
741 CAddonDatabase database;
743 database.GetAddon(addonID, addon);
747 CAddonMgr::Get().GetAddon(addonID, addon2);
748 CGUIDialogKaiToast::QueueNotification(addon->Icon(),
750 g_localizeStrings.Get(addon2 ? 113 : 114),
751 TOAST_DISPLAY_TIME, false);
755 CGUIDialogKaiToast::QueueNotification(CGUIDialogKaiToast::Error,
757 g_localizeStrings.Get(114),
758 TOAST_DISPLAY_TIME, false);
762 bool CAddonInstallJob::CheckHash(const CStdString& zipFile)
764 if (m_hash.IsEmpty())
766 CStdString md5 = CUtil::GetFileMD5(zipFile);
767 if (!md5.Equals(m_hash))
769 CFile::Delete(zipFile);
770 ReportInstallError(m_addon->ID(), URIUtils::GetFileName(zipFile));
771 CLog::Log(LOGERROR, "MD5 mismatch after download %s", zipFile.c_str());
777 CStdString CAddonInstallJob::AddonID() const
779 return (m_addon) ? m_addon->ID() : "";
782 CAddonUnInstallJob::CAddonUnInstallJob(const AddonPtr &addon)
787 bool CAddonUnInstallJob::DoWork()
789 if (m_addon->Type() == ADDON_PVRDLL)
791 // stop the pvr manager, so running pvr add-ons are stopped and closed
792 PVR::CPVRManager::Get().Stop();
794 if (m_addon->Type() == ADDON_SERVICE)
796 boost::shared_ptr<CService> service = boost::dynamic_pointer_cast<CService>(m_addon);
801 AddonPtr repoPtr = CAddonInstallJob::GetRepoForAddon(m_addon);
802 RepositoryPtr therepo = boost::dynamic_pointer_cast<CRepository>(repoPtr);
803 if (therepo && !therepo->Props().libname.IsEmpty())
807 s.Format("plugin://%s/?action=uninstall"
808 "&package=%s", therepo->ID().c_str(), m_addon->ID().c_str());
809 if (!CDirectory::GetDirectory(s, dummy))
814 if (!CAddonInstallJob::DeleteAddon(m_addon->Path()))
823 void CAddonUnInstallJob::OnPostUnInstall()
825 if (m_addon->Type() == ADDON_REPOSITORY)
827 CAddonDatabase database;
829 database.DeleteRepository(m_addon->ID());
834 XFILE::CFavouritesDirectory::Load(items);
835 for (int i=0; i < items.Size(); ++i)
837 if (items[i]->GetPath().Find(m_addon->ID()) > -1)
839 items.Remove(items[i].get());
845 CFavouritesDirectory::Save(items);
847 if (m_addon->Type() == ADDON_PVRDLL)
849 if (CSettings::Get().GetBool("pvrmanager.enabled"))
850 PVR::CPVRManager::Get().Start(true);