Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Merge pull request #3509 from jmarshallnz/repo_versioning

Allow repositories to have more than one (conditional) addons.xml sources
  • Loading branch information...
commit befbf46faa0f9ac2692ceeb6fad2b809906680e1 2 parents a942217 + f5f9b56
@jmarshallnz jmarshallnz authored
View
6 addons/repository.xbmc.org/addon.xml
@@ -8,6 +8,12 @@
</requires>
<extension point="xbmc.addon.repository"
name="Official XBMC.org Add-on Repository">
+ <dir minversion="12.9.0">
+ <info compressed="true">http://mirrors.xbmc.org/addons/gotham/addons.xml</info>
+ <checksum>http://mirrors.xbmc.org/addons/gotham/addons.xml.md5</checksum>
+ <datadir zip="true">http://mirrors.xbmc.org/addons/gotham</datadir>
+ <hashes>true</hashes>
+ </dir>
<info compressed="true">http://mirrors.xbmc.org/addons/frodo/addons.xml</info>
<checksum>http://mirrors.xbmc.org/addons/frodo/addons.xml.md5</checksum>
<datadir zip="true">http://mirrors.xbmc.org/addons/frodo</datadir>
View
6 xbmc/addons/AddonDatabase.cpp
@@ -392,14 +392,14 @@ int CAddonDatabase::AddRepository(const CStdString& id, const VECADDONS& addons,
return -1;
}
-int CAddonDatabase::GetRepoChecksum(const CStdString& id, CStdString& checksum)
+int CAddonDatabase::GetRepoChecksum(const std::string& id, std::string& checksum)
{
try
{
if (NULL == m_pDB.get()) return -1;
if (NULL == m_pDS.get()) return -1;
- CStdString strSQL = PrepareSQL("select * from repo where addonID='%s'",id.c_str());
+ std::string strSQL = PrepareSQL("select * from repo where addonID='%s'",id.c_str());
m_pDS->query(strSQL.c_str());
if (!m_pDS->eof())
{
@@ -411,7 +411,7 @@ int CAddonDatabase::GetRepoChecksum(const CStdString& id, CStdString& checksum)
{
CLog::Log(LOGERROR, "%s failed on repo '%s'", __FUNCTION__, id.c_str());
}
- checksum.Empty();
+ checksum.clear();
return -1;
}
View
2  xbmc/addons/AddonDatabase.h
@@ -45,7 +45,7 @@ class CAddonDatabase : public CDatabase
int AddRepository(const CStdString& id, const ADDON::VECADDONS& addons, const CStdString& checksum);
void DeleteRepository(const CStdString& id);
void DeleteRepository(int id);
- int GetRepoChecksum(const CStdString& id, CStdString& checksum);
+ int GetRepoChecksum(const std::string& id, std::string& checksum);
bool GetRepository(const CStdString& id, ADDON::VECADDONS& addons);
bool GetRepository(int id, ADDON::VECADDONS& addons);
bool SetRepoTimestamp(const CStdString& id, const CStdString& timestamp);
View
176 xbmc/addons/Repository.cpp
@@ -19,22 +19,24 @@
*/
#include "Repository.h"
-#include "utils/XBMCTinyXML.h"
+#include "addons/AddonDatabase.h"
+#include "addons/AddonInstaller.h"
+#include "addons/AddonManager.h"
+#include "dialogs/GUIDialogYesNo.h"
+#include "dialogs/GUIDialogKaiToast.h"
#include "filesystem/File.h"
-#include "AddonDatabase.h"
+#include "filesystem/PluginDirectory.h"
+#include "pvr/PVRManager.h"
#include "settings/Settings.h"
-#include "FileItem.h"
-#include "utils/JobManager.h"
-#include "addons/AddonInstaller.h"
#include "utils/log.h"
+#include "utils/StringUtils.h"
#include "utils/URIUtils.h"
-#include "dialogs/GUIDialogYesNo.h"
-#include "dialogs/GUIDialogKaiToast.h"
+#include "utils/XBMCTinyXML.h"
+#include "FileItem.h"
#include "TextureDatabase.h"
#include "URL.h"
-#include "pvr/PVRManager.h"
-#include "filesystem/PluginDirectory.h"
+using namespace std;
using namespace XFILE;
using namespace ADDON;
@@ -46,52 +48,76 @@ AddonPtr CRepository::Clone() const
CRepository::CRepository(const AddonProps& props) :
CAddon(props)
{
- m_compressed = false;
- m_zipped = false;
}
CRepository::CRepository(const cp_extension_t *ext)
: CAddon(ext)
{
- m_compressed = false;
- m_zipped = false;
// read in the other props that we need
if (ext)
{
- m_checksum = CAddonMgr::Get().GetExtValue(ext->configuration, "checksum");
- m_compressed = CAddonMgr::Get().GetExtValue(ext->configuration, "info@compressed").Equals("true");
- m_info = CAddonMgr::Get().GetExtValue(ext->configuration, "info");
- m_datadir = CAddonMgr::Get().GetExtValue(ext->configuration, "datadir");
- m_zipped = CAddonMgr::Get().GetExtValue(ext->configuration, "datadir@zip").Equals("true");
- m_hashes = CAddonMgr::Get().GetExtValue(ext->configuration, "hashes").Equals("true");
+ AddonVersion version("0.0.0");
+ AddonPtr addonver;
+ if (CAddonMgr::Get().GetAddon("xbmc.addon", addonver))
+ version = addonver->Version();
+ for (size_t i = 0; i < ext->configuration->num_children; ++i)
+ {
+ if(ext->configuration->children[i].name &&
+ strcmp(ext->configuration->children[i].name, "dir") == 0)
+ {
+ AddonVersion min_version(CAddonMgr::Get().GetExtValue(&ext->configuration->children[i], "@minversion"));
+ if (min_version <= version)
+ {
+ DirInfo dir;
+ dir.version = min_version;
+ dir.checksum = CAddonMgr::Get().GetExtValue(&ext->configuration->children[i], "checksum");
+ dir.compressed = CAddonMgr::Get().GetExtValue(&ext->configuration->children[i], "info@compressed").Equals("true");
+ dir.info = CAddonMgr::Get().GetExtValue(&ext->configuration->children[i], "info");
+ dir.datadir = CAddonMgr::Get().GetExtValue(&ext->configuration->children[i], "datadir");
+ dir.zipped = CAddonMgr::Get().GetExtValue(&ext->configuration->children[i], "datadir@zip").Equals("true");
+ dir.hashes = CAddonMgr::Get().GetExtValue(&ext->configuration->children[i], "hashes").Equals("true");
+ m_dirs.push_back(dir);
+ }
+ }
+ }
+ // backward compatibility
+ if (!CAddonMgr::Get().GetExtValue(ext->configuration, "info").empty())
+ {
+ DirInfo info;
+ info.checksum = CAddonMgr::Get().GetExtValue(ext->configuration, "checksum");
+ info.compressed = CAddonMgr::Get().GetExtValue(ext->configuration, "info@compressed").Equals("true");
+ info.info = CAddonMgr::Get().GetExtValue(ext->configuration, "info");
+ info.datadir = CAddonMgr::Get().GetExtValue(ext->configuration, "datadir");
+ info.zipped = CAddonMgr::Get().GetExtValue(ext->configuration, "datadir@zip").Equals("true");
+ info.hashes = CAddonMgr::Get().GetExtValue(ext->configuration, "hashes").Equals("true");
+ m_dirs.push_back(info);
+ }
}
}
CRepository::CRepository(const CRepository &rhs)
: CAddon(rhs)
{
- m_info = rhs.m_info;
- m_checksum = rhs.m_checksum;
- m_datadir = rhs.m_datadir;
- m_compressed = rhs.m_compressed;
- m_zipped = rhs.m_zipped;
- m_hashes = rhs.m_hashes;
+ m_dirs = rhs.m_dirs;
}
CRepository::~CRepository()
{
}
-CStdString CRepository::Checksum()
+string CRepository::Checksum() const
{
- if (!m_checksum.IsEmpty())
- return FetchChecksum(m_checksum);
- return "";
+ string result;
+ for (DirList::const_iterator it = m_dirs.begin(); it != m_dirs.end(); ++it)
+ {
+ if (!it->checksum.empty())
+ result += FetchChecksum(it->checksum);
+ }
+ return result;
}
-CStdString CRepository::FetchChecksum(const CStdString& url)
+string CRepository::FetchChecksum(const string& url)
{
- CSingleLock lock(m_critSection);
CFile file;
try
{
@@ -114,15 +140,19 @@ CStdString CRepository::FetchChecksum(const CStdString& url)
}
}
-CStdString CRepository::GetAddonHash(const AddonPtr& addon)
+string CRepository::GetAddonHash(const AddonPtr& addon) const
{
- CStdString checksum;
- if (m_hashes)
+ string checksum;
+ DirList::const_iterator it;
+ for (it = m_dirs.begin();it != m_dirs.end(); ++it)
+ if (it->datadir == addon->Path())
+ break;
+ if (it != m_dirs.end() && it->hashes)
{
checksum = FetchChecksum(addon->Path()+".md5");
size_t pos = checksum.find_first_of(" \n");
- if (pos != CStdString::npos)
- return checksum.Left(pos);
+ if (pos != string::npos)
+ return checksum.substr(0, pos);
}
return checksum;
}
@@ -133,19 +163,17 @@ CStdString CRepository::GetAddonHash(const AddonPtr& addon)
x = y; \
}
-VECADDONS CRepository::Parse()
+VECADDONS CRepository::Parse(const DirInfo& dir)
{
- CSingleLock lock(m_critSection);
-
VECADDONS result;
CXBMCTinyXML doc;
- CStdString file = m_info;
- if (m_compressed)
+ string file = dir.info;
+ if (dir.compressed)
{
- CURL url(m_info);
- CStdString opts = url.GetProtocolOptions();
- if (!opts.IsEmpty())
+ CURL url(dir.info);
+ string opts = url.GetProtocolOptions();
+ if (!opts.empty())
opts += "&";
url.SetProtocolOptions(opts+"Encoding=gzip");
file = url.Get();
@@ -157,22 +185,21 @@ VECADDONS CRepository::Parse()
for (IVECADDONS i = result.begin(); i != result.end(); ++i)
{
AddonPtr addon = *i;
- if (m_zipped)
+ if (dir.zipped)
{
- CStdString file;
- file.Format("%s/%s-%s.zip", addon->ID().c_str(), addon->ID().c_str(), addon->Version().c_str());
- addon->Props().path = URIUtils::AddFileToFolder(m_datadir,file);
- SET_IF_NOT_EMPTY(addon->Props().icon,URIUtils::AddFileToFolder(m_datadir,addon->ID()+"/icon.png"))
- file.Format("%s/changelog-%s.txt", addon->ID().c_str(), addon->Version().c_str());
- SET_IF_NOT_EMPTY(addon->Props().changelog,URIUtils::AddFileToFolder(m_datadir,file))
- SET_IF_NOT_EMPTY(addon->Props().fanart,URIUtils::AddFileToFolder(m_datadir,addon->ID()+"/fanart.jpg"))
+ string file = StringUtils::Format("%s/%s-%s.zip", addon->ID().c_str(), addon->ID().c_str(), addon->Version().c_str());
+ addon->Props().path = URIUtils::AddFileToFolder(dir.datadir,file);
+ SET_IF_NOT_EMPTY(addon->Props().icon,URIUtils::AddFileToFolder(dir.datadir,addon->ID()+"/icon.png"))
+ file = StringUtils::Format("%s/changelog-%s.txt", addon->ID().c_str(), addon->Version().c_str());
+ SET_IF_NOT_EMPTY(addon->Props().changelog,URIUtils::AddFileToFolder(dir.datadir,file))
+ SET_IF_NOT_EMPTY(addon->Props().fanart,URIUtils::AddFileToFolder(dir.datadir,addon->ID()+"/fanart.jpg"))
}
else
{
- addon->Props().path = URIUtils::AddFileToFolder(m_datadir,addon->ID()+"/");
- SET_IF_NOT_EMPTY(addon->Props().icon,URIUtils::AddFileToFolder(m_datadir,addon->ID()+"/icon.png"))
- SET_IF_NOT_EMPTY(addon->Props().changelog,URIUtils::AddFileToFolder(m_datadir,addon->ID()+"/changelog.txt"))
- SET_IF_NOT_EMPTY(addon->Props().fanart,URIUtils::AddFileToFolder(m_datadir,addon->ID()+"/fanart.jpg"))
+ addon->Props().path = URIUtils::AddFileToFolder(dir.datadir,addon->ID()+"/");
+ SET_IF_NOT_EMPTY(addon->Props().icon,URIUtils::AddFileToFolder(dir.datadir,addon->ID()+"/icon.png"))
+ SET_IF_NOT_EMPTY(addon->Props().changelog,URIUtils::AddFileToFolder(dir.datadir,addon->ID()+"/changelog.txt"))
+ SET_IF_NOT_EMPTY(addon->Props().fanart,URIUtils::AddFileToFolder(dir.datadir,addon->ID()+"/fanart.jpg"))
}
}
}
@@ -225,9 +252,9 @@ bool CRepositoryUpdateJob::DoWork()
{
if (CSettings::Get().GetBool("general.addonautoupdate") || addon->Type() >= ADDON_VIZ_LIBRARY)
{
- CStdString referer;
+ string referer;
if (URIUtils::IsInternetStream(addons[i]->Path()))
- referer.Format("Referer=%s-%s.zip",addon->ID().c_str(),addon->Version().c_str());
+ referer = StringUtils::Format("Referer=%s-%s.zip",addon->ID().c_str(),addon->Version().c_str());
if (addons[i]->Type() == ADDON_PVRDLL &&
!PVR::CPVRManager::Get().InstallAddonAllowed(addons[i]->ID()))
@@ -263,14 +290,30 @@ VECADDONS CRepositoryUpdateJob::GrabAddons(RepositoryPtr& repo)
{
CAddonDatabase database;
database.Open();
- CStdString checksum;
+ string checksum;
database.GetRepoChecksum(repo->ID(),checksum);
- CStdString reposum = repo->Checksum();
+ string reposum = repo->Checksum();
VECADDONS addons;
- if (!checksum.Equals(reposum) || checksum.empty())
+ if (checksum != reposum || checksum.empty())
{
- addons = repo->Parse();
- if (addons.empty())
+ map<string, AddonPtr> uniqueAddons;
+ for (CRepository::DirList::const_iterator it = repo->m_dirs.begin(); it != repo->m_dirs.end(); ++it)
+ {
+ VECADDONS addons2 = CRepository::Parse(*it);
+ for (VECADDONS::const_iterator it2 = addons2.begin(); it2 != addons2.end(); ++it2)
+ {
+ map<string, AddonPtr>::iterator existing = uniqueAddons.find((*it2)->ID());
+ if (existing != uniqueAddons.end())
+ { // already got it - replace if we have a newer version
+ if (existing->second->Version() < (*it2)->Version())
+ existing->second = *it2;
+ }
+ else
+ uniqueAddons.insert(make_pair((*it2)->ID(), *it2));
+ }
+ }
+
+ if (uniqueAddons.empty())
{
CLog::Log(LOGERROR,"Repository %s returned no add-ons, listing may have failed",repo->Name().c_str());
reposum = checksum; // don't update the checksum
@@ -281,12 +324,15 @@ VECADDONS CRepositoryUpdateJob::GrabAddons(RepositoryPtr& repo)
if (!repo->Props().libname.empty())
{
CFileItemList dummy;
- CStdString s;
- s.Format("plugin://%s/?action=update", repo->ID());
+ string s = StringUtils::Format("plugin://%s/?action=update", repo->ID().c_str());
add = CDirectory::GetDirectory(s, dummy);
}
if (add)
+ {
+ for (map<string, AddonPtr>::const_iterator i = uniqueAddons.begin(); i != uniqueAddons.end(); ++i)
+ addons.push_back(i->second);
database.AddRepository(repo->ID(),addons,reposum);
+ }
}
}
else
View
35 xbmc/addons/Repository.h
@@ -20,11 +20,7 @@
*/
#include "Addon.h"
-#include "AddonManager.h"
-#include "XBDateTime.h"
#include "utils/Job.h"
-#include "threads/CriticalSection.h"
-#include "threads/SingleLock.h"
namespace ADDON
{
@@ -38,24 +34,33 @@ namespace ADDON
CRepository(const cp_extension_t *props);
virtual ~CRepository();
- CStdString Checksum();
+ std::string Checksum() const;
/*! \brief Get the md5 hash for an addon.
\param the addon in question.
\return the md5 hash for the given addon, empty if non exists.
*/
- CStdString GetAddonHash(const AddonPtr& addon);
- VECADDONS Parse();
+ std::string GetAddonHash(const AddonPtr& addon) const;
+
+ struct DirInfo
+ {
+ DirInfo() : version("0.0.0"), compressed(false), zipped(false), hashes(false) {}
+ AddonVersion version;
+ std::string info;
+ std::string checksum;
+ std::string datadir;
+ bool compressed;
+ bool zipped;
+ bool hashes;
+ };
+
+ typedef std::vector<DirInfo> DirList;
+ DirList m_dirs;
+
+ static VECADDONS Parse(const DirInfo& dir);
private:
- CStdString FetchChecksum(const CStdString& url);
+ static std::string FetchChecksum(const std::string& url);
CRepository(const CRepository &rhs);
- CStdString m_info;
- CStdString m_checksum;
- CStdString m_datadir;
- bool m_compressed; // gzipped info xml
- bool m_zipped; // zipped addons
- bool m_hashes; // repo supports hashes. e.g. plugin.i.rule-1.0.5.zip.md5
- CCriticalSection m_critSection;
};
class CRepositoryUpdateJob : public CJob
Please sign in to comment.
Something went wrong with that request. Please try again.