Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Fetching contributors…

Cannot retrieve contributors at this time

617 lines (550 sloc) 16.447 kb
/*
* Copyright (C) 2005-2009 Team XBMC
* http://www.xbmc.org
*
* This Program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This Program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with XBMC; see the file COPYING. If not, write to
* the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
* http://www.gnu.org/copyleft/gpl.html
*
*/
#include "Addon.h"
#include "AddonManager.h"
#include "Settings.h"
#include "GUISettings.h"
#include "StringUtils.h"
#include "FileSystem/Directory.h"
#include "FileSystem/File.h"
#ifdef __APPLE__
#include "../osx/OSXGNUReplacements.h"
#endif
#include "log.h"
#include <vector>
#include <string.h>
using XFILE::CDirectory;
using XFILE::CFile;
using namespace std;
namespace ADDON
{
/**
* helper functions
*
*/
const CStdString TranslateContent(const CONTENT_TYPE &type, bool pretty/*=false*/)
{
switch (type)
{
case CONTENT_ALBUMS:
if (pretty) return g_localizeStrings.Get(132);
return "albums";
case CONTENT_ARTISTS:
if (pretty) return g_localizeStrings.Get(133);
return "artists";
case CONTENT_MOVIES:
if (pretty) return g_localizeStrings.Get(20342);
return "movies";
case CONTENT_TVSHOWS:
if (pretty) return g_localizeStrings.Get(20343);
return "tvshows";
case CONTENT_MUSICVIDEOS:
if (pretty) return g_localizeStrings.Get(20389);
return "musicvideos";
case CONTENT_EPISODES:
if (pretty) return g_localizeStrings.Get(20360);
return "episodes";
case CONTENT_AUDIO:
return "audio";
case CONTENT_IMAGE:
return "image";
case CONTENT_PROGRAM:
return "program";
case CONTENT_VIDEO:
return "video";
case CONTENT_NONE:
default:
if (pretty) return g_localizeStrings.Get(231);
return "";
}
}
const CONTENT_TYPE TranslateContent(const CStdString &string)
{
if (string.Equals("albums")) return CONTENT_ALBUMS;
else if (string.Equals("music")) return CONTENT_ALBUMS;
else if (string.Equals("artists")) return CONTENT_ARTISTS;
else if (string.Equals("movies")) return CONTENT_MOVIES;
else if (string.Equals("tvshows")) return CONTENT_TVSHOWS;
else if (string.Equals("episodes")) return CONTENT_EPISODES;
else if (string.Equals("musicvideos")) return CONTENT_MUSICVIDEOS;
else if (string.Equals("audio")) return CONTENT_AUDIO;
else if (string.Equals("image")) return CONTENT_IMAGE;
else if (string.Equals("program")) return CONTENT_PROGRAM;
else if (string.Equals("video")) return CONTENT_VIDEO;
else return CONTENT_NONE;
}
const CStdString TranslateType(const ADDON::TYPE &type, bool pretty/*=false*/)
{
switch (type)
{
case ADDON::ADDON_SCRAPER:
{
if (pretty)
return g_localizeStrings.Get(24007);
return "xbmc.metadata.scraper";
}
case ADDON::ADDON_SCRAPER_LIBRARY:
{
return "xbmc.metadata.scraper.library";
}
case ADDON::ADDON_SCREENSAVER:
{
if (pretty)
return g_localizeStrings.Get(24008);
return "xbmc.ui.screensaver";
}
case ADDON::ADDON_VIZ:
{
if (pretty)
return g_localizeStrings.Get(24010);
return "xbmc.player.musicviz";
}
case ADDON::ADDON_VIZ_LIBRARY:
{
return "visualization-library";
}
case ADDON::ADDON_PLUGIN:
{
if (pretty)
return g_localizeStrings.Get(24005);
return "plugin";
}
case ADDON::ADDON_SCRIPT:
{
if (pretty)
return g_localizeStrings.Get(24009);
return "xbmc.python.script";
}
case ADDON::ADDON_SKIN:
{
if (pretty)
return g_localizeStrings.Get(166);
return "xbmc.gui.skin";
}
case ADDON::ADDON_REPOSITORY:
{
if (pretty)
return g_localizeStrings.Get(24011);
return "xbmc.addon.repository";
}
default:
{
return "";
}
}
}
const ADDON::TYPE TranslateType(const CStdString &string)
{
if (string.Equals("pvrclient")) return ADDON_PVRDLL;
else if (string.Equals("xbmc.metadata.scraper")) return ADDON_SCRAPER;
else if (string.Equals("xbmc.metadata.scraper.library")) return ADDON_SCRAPER_LIBRARY;
else if (string.Equals("xbmc.ui.screensaver")) return ADDON_SCREENSAVER;
else if (string.Equals("xbmc.player.musicviz")) return ADDON_VIZ;
else if (string.Equals("visualization-library")) return ADDON_VIZ_LIBRARY;
else if (string.Equals("plugin")) return ADDON_PLUGIN;
else if (string.Equals("xbmc.python.script")) return ADDON_SCRIPT;
else if (string.Equals("xbmc.gui.skin")) return ADDON_SKIN;
else if (string.Equals("xbmc.addon.repository")) return ADDON_REPOSITORY;
else return ADDON_UNKNOWN;
}
/**
* AddonVersion
*
*/
bool AddonVersion::operator==(const AddonVersion &rhs) const
{
return str.Equals(rhs.str);
}
bool AddonVersion::operator!=(const AddonVersion &rhs) const
{
return !(*this == rhs);
}
bool AddonVersion::operator>(const AddonVersion &rhs) const
{
return (strverscmp(str.c_str(), rhs.str.c_str()) > 0);
}
bool AddonVersion::operator>=(const AddonVersion &rhs) const
{
return (*this == rhs) || (*this > rhs);
}
bool AddonVersion::operator<(const AddonVersion &rhs) const
{
return (strverscmp(str.c_str(), rhs.str.c_str()) < 0);
}
bool AddonVersion::operator<=(const AddonVersion &rhs) const
{
return (*this == rhs) || !(*this > rhs);
}
CStdString AddonVersion::Print() const
{
CStdString out;
out.Format("%s %s", g_localizeStrings.Get(24051), str); // "Version <str>"
return CStdString(out);
}
AddonProps::AddonProps(cp_plugin_info_t *props)
: id(props->identifier)
, version(props->version)
, name(props->name)
, path(props->plugin_path)
, author(props->provider_name)
, stars(0)
{
//FIXME only considers the first registered extension for each addon
if (props->extensions->ext_point_id)
type = TranslateType(props->extensions->ext_point_id);
// Grab more detail from the props...
const cp_extension_t *metadata = CAddonMgr::Get().GetExtension(props, "xbmc.addon.metadata");
if (metadata)
{
CStdString platforms = CAddonMgr::Get().GetExtValue(metadata->configuration, "platform");
summary = CAddonMgr::Get().GetTranslatedString(metadata->configuration, "summary");
description = CAddonMgr::Get().GetTranslatedString(metadata->configuration, "description");
disclaimer = CAddonMgr::Get().GetTranslatedString(metadata->configuration, "disclaimer");
license = CAddonMgr::Get().GetExtValue(metadata->configuration, "license");
vector<CStdString> content = CAddonMgr::Get().GetExtValues(metadata->configuration,"supportedcontent");
for (unsigned int i=0;i<content.size();++i)
contents.insert(TranslateContent(content[i]));
//FIXME other stuff goes here
//CStdString version = CAddonMgr::Get().GetExtValue(metadata->configuration, "minversion/xbmc");
}
icon = "icon.png";
fanart = CUtil::AddFileToFolder(path, "fanart.jpg");
changelog = CUtil::AddFileToFolder(path, "changelog.txt");
}
/**
* CAddon
*
*/
CAddon::CAddon(cp_plugin_info_t *props)
: m_props(props)
, m_parent(AddonPtr())
{
BuildLibName(props);
BuildProfilePath();
CUtil::AddFileToFolder(Profile(), "settings.xml", m_userSettingsPath);
m_enabled = true;
m_hasStrings = false;
m_checkedStrings = false;
}
CAddon::CAddon(const AddonProps &props)
: m_props(props)
, m_parent(AddonPtr())
{
if (props.libname.IsEmpty()) BuildLibName();
else m_strLibName = props.libname;
BuildProfilePath();
CUtil::AddFileToFolder(Profile(), "settings.xml", m_userSettingsPath);
m_enabled = true;
m_hasStrings = false;
m_checkedStrings = false;
}
CAddon::CAddon(const CAddon &rhs, const AddonPtr &parent)
: m_props(rhs.Props())
, m_parent(parent)
{
m_userXmlDoc = rhs.m_userXmlDoc;
BuildProfilePath();
CUtil::AddFileToFolder(Profile(), "settings.xml", m_userSettingsPath);
m_strLibName = rhs.LibName();
m_enabled = rhs.Enabled();
m_hasStrings = false;
m_checkedStrings = false;
}
AddonPtr CAddon::Clone(const AddonPtr &self) const
{
return AddonPtr(new CAddon(*this, self));
}
const AddonVersion CAddon::Version()
{
return m_props.version;
}
//TODO platform/path crap should be negotiated between the addon and
// the handler for it's type
void CAddon::BuildLibName(cp_plugin_info_t *props)
{
if (!props)
{
m_strLibName = "default";
CStdString ext;
switch (m_props.type)
{
case ADDON_SCRAPER:
case ADDON_SCRAPER_LIBRARY:
ext = ADDON_SCRAPER_EXT;
break;
case ADDON_SCREENSAVER:
ext = ADDON_SCREENSAVER_EXT;
break;
case ADDON_SKIN:
m_strLibName = "skin.xml";
return;
case ADDON_VIZ:
ext = ADDON_VIS_EXT;
break;
case ADDON_SCRIPT:
case ADDON_PLUGIN:
ext = ADDON_PYTHON_EXT;
break;
default:
m_strLibName.clear();
return;
}
// extensions are returned as *.ext
// so remove the asterisk
ext.erase(0,1);
m_strLibName.append(ext);
}
else
{
switch (m_props.type)
{
case ADDON_SCREENSAVER:
case ADDON_SCRIPT:
case ADDON_SCRAPER:
case ADDON_SCRAPER_LIBRARY:
{
CStdString temp = CAddonMgr::Get().GetExtValue(props->extensions->configuration, "@library");
m_strLibName = temp;
}
break;
default:
m_strLibName.clear();
break;
}
}
}
/**
* Language File Handling
*/
bool CAddon::LoadStrings()
{
// Path where the language strings reside
CStdString chosenPath;
chosenPath.Format("resources/language/%s/strings.xml", g_guiSettings.GetString("locale.language").c_str());
CStdString chosen = CUtil::AddFileToFolder(m_props.path, chosenPath);
CStdString fallback = CUtil::AddFileToFolder(m_props.path, "resources/language/English/strings.xml");
m_hasStrings = m_strings.Load(chosen, fallback);
return m_checkedStrings = true;
}
void CAddon::ClearStrings()
{
// Unload temporary language strings
m_strings.Clear();
m_hasStrings = false;
}
CStdString CAddon::GetString(uint32_t id)
{
if (!m_hasStrings && ! m_checkedStrings && !LoadStrings())
return "";
return m_strings.Get(id);
}
/**
* Settings Handling
*/
bool CAddon::HasSettings()
{
CStdString addonFileName = CUtil::AddFileToFolder(m_props.path, "resources/settings.xml");
// Load the settings file to verify it's valid
TiXmlDocument xmlDoc;
if (!xmlDoc.LoadFile(addonFileName))
return false;
// Make sure that the addon XML has the settings element
TiXmlElement *setting = xmlDoc.RootElement();
if (!setting || strcmpi(setting->Value(), "settings") != 0)
return false;
return true;
}
bool CAddon::LoadSettings()
{
CStdString addonFileName = CUtil::AddFileToFolder(m_props.path, "resources/settings.xml");
if (!m_addonXmlDoc.LoadFile(addonFileName))
{
CLog::Log(LOGERROR, "Unable to load: %s, Line %d\n%s", addonFileName.c_str(), m_addonXmlDoc.ErrorRow(), m_addonXmlDoc.ErrorDesc());
return false;
}
// Make sure that the addon XML has the settings element
TiXmlElement *setting = m_addonXmlDoc.RootElement();
if (!setting || strcmpi(setting->Value(), "settings") != 0)
{
CLog::Log(LOGERROR, "Error loading Settings %s: cannot find root element 'settings'", addonFileName.c_str());
return false;
}
return LoadUserSettings();
}
bool CAddon::LoadUserSettings(bool create)
{
// Load the user saved settings. If it does not exist, create it
if (!m_userXmlDoc.LoadFile(m_userSettingsPath))
{
if (!create)
return false;
TiXmlDocument doc;
TiXmlDeclaration decl("1.0", "UTF-8", "yes");
doc.InsertEndChild(decl);
TiXmlElement xmlRootElement("settings");
doc.InsertEndChild(xmlRootElement);
m_userXmlDoc = doc;
}
return true;
}
void CAddon::SaveSettings(void)
{
// break down the path into directories
CStdString strRoot, strAddon;
CUtil::GetDirectory(m_userSettingsPath, strAddon);
CUtil::RemoveSlashAtEnd(strAddon);
CUtil::GetDirectory(strAddon, strRoot);
CUtil::RemoveSlashAtEnd(strRoot);
// create the individual folders
if (!CDirectory::Exists(strRoot))
CDirectory::Create(strRoot);
if (!CDirectory::Exists(strAddon))
CDirectory::Create(strAddon);
m_userXmlDoc.SaveFile(m_userSettingsPath);
}
void CAddon::SaveFromDefault()
{
if (!GetSettingsXML())
{ // no settings found
return;
}
const TiXmlElement *setting = GetSettingsXML()->FirstChildElement("setting");
while (setting)
{
CStdString id;
if (setting->Attribute("id"))
id = setting->Attribute("id");
CStdString type;
if (setting->Attribute("type"))
type = setting->Attribute("type");
CStdString value;
if (setting->Attribute("default"))
value = setting->Attribute("default");
UpdateSetting(id, type, value);
setting = setting->NextSiblingElement("setting");
}
// now save to file
SaveSettings();
}
CStdString CAddon::GetSetting(const CStdString& key) const
{
if (m_userXmlDoc.RootElement())
{
// Try to find the setting and return its value
const TiXmlElement *setting = m_userXmlDoc.RootElement()->FirstChildElement("setting");
while (setting)
{
const char *id = setting->Attribute("id");
if (id && strcmpi(id, key) == 0)
return setting->Attribute("value");
setting = setting->NextSiblingElement("setting");
}
}
if (m_addonXmlDoc.RootElement())
{
// Try to find the setting in the addon and return its default value
const TiXmlElement* setting = m_addonXmlDoc.RootElement()->FirstChildElement("setting");
while (setting)
{
const char *id = setting->Attribute("id");
if (id && strcmpi(id, key) == 0 && setting->Attribute("default"))
return setting->Attribute("default");
setting = setting->NextSiblingElement("setting");
}
}
// Otherwise return empty string
return "";
}
void CAddon::UpdateSetting(const CStdString& key, const CStdString& value, const CStdString& type/* = "" */)
{
if (key.empty()) return;
// Try to find the setting and change its value
if (!m_userXmlDoc.RootElement())
{
TiXmlElement node("settings");
m_userXmlDoc.InsertEndChild(node);
}
TiXmlElement *setting = m_userXmlDoc.RootElement()->FirstChildElement("setting");
while (setting)
{
const char *id = setting->Attribute("id");
const char *storedtype = setting->Attribute("type");
if (id && strcmpi(id, key) == 0)
{
if (!type.empty() && storedtype && strcmpi(storedtype, type) != 0)
setting->SetAttribute("type", type.c_str());
setting->SetAttribute("value", value.c_str());
return;
}
setting = setting->NextSiblingElement("setting");
}
// Setting not found, add it
TiXmlElement nodeSetting("setting");
nodeSetting.SetAttribute("id", key.c_str()); //FIXME otherwise attribute value isn't updated
if (!type.empty())
nodeSetting.SetAttribute("type", type.c_str());
else
nodeSetting.SetAttribute("type", "text");
nodeSetting.SetAttribute("value", value.c_str());
m_userXmlDoc.RootElement()->InsertEndChild(nodeSetting);
}
TiXmlElement* CAddon::GetSettingsXML()
{
return m_addonXmlDoc.RootElement();
}
void CAddon::BuildProfilePath()
{
m_profile.Format("special://profile/addon_data/%s/", ID().c_str());
}
const CStdString CAddon::Icon() const
{
if (CURL::IsFullPath(m_props.icon))
return m_props.icon;
return CUtil::AddFileToFolder(m_props.path, m_props.icon);
}
ADDONDEPS CAddon::GetDeps()
{
return CAddonMgr::Get().GetDeps(ID());
}
/**
* CAddonLibrary
*
*/
CAddonLibrary::CAddonLibrary(cp_plugin_info_t *props)
: CAddon(props)
, m_addonType(SetAddonType())
{
}
CAddonLibrary::CAddonLibrary(const AddonProps& props)
: CAddon(props)
, m_addonType(SetAddonType())
{
}
TYPE CAddonLibrary::SetAddonType()
{
if (Type() == ADDON_SCRAPER_LIBRARY)
return ADDON_SCRAPER;
else if (Type() == ADDON_VIZ_LIBRARY)
return ADDON_VIZ;
else
return ADDON_UNKNOWN;
}
} /* namespace ADDON */
Jump to Line
Something went wrong with that request. Please try again.