From ffca21f2743a7b367fa212799c6e2fea6190dd5d Mon Sep 17 00:00:00 2001 From: manuel Date: Tue, 3 Mar 2015 16:53:59 +0100 Subject: initial commit for kodi master --- xbmc/addons/Addon.cpp | 653 +++++++++ xbmc/addons/Addon.h | 278 ++++ xbmc/addons/AddonCallbacks.cpp | 173 +++ xbmc/addons/AddonCallbacks.h | 466 ++++++ xbmc/addons/AddonCallbacksAddon.cpp | 532 +++++++ xbmc/addons/AddonCallbacksAddon.h | 73 + xbmc/addons/AddonCallbacksCodec.cpp | 116 ++ xbmc/addons/AddonCallbacksCodec.h | 46 + xbmc/addons/AddonCallbacksGUI.cpp | 2281 +++++++++++++++++++++++++++++ xbmc/addons/AddonCallbacksGUI.h | 272 ++++ xbmc/addons/AddonCallbacksPVR.cpp | 324 ++++ xbmc/addons/AddonCallbacksPVR.h | 166 +++ xbmc/addons/AddonDatabase.cpp | 797 ++++++++++ xbmc/addons/AddonDatabase.h | 175 +++ xbmc/addons/AddonDll.h | 572 ++++++++ xbmc/addons/AddonInstaller.cpp | 974 ++++++++++++ xbmc/addons/AddonInstaller.h | 229 +++ xbmc/addons/AddonManager.cpp | 1030 +++++++++++++ xbmc/addons/AddonManager.h | 253 ++++ xbmc/addons/AddonStatusHandler.cpp | 175 +++ xbmc/addons/AddonStatusHandler.h | 56 + xbmc/addons/AddonVersion.cpp | 138 ++ xbmc/addons/AddonVersion.h | 72 + xbmc/addons/AudioEncoder.cpp | 90 ++ xbmc/addons/AudioEncoder.h | 51 + xbmc/addons/ContextItemAddon.cpp | 108 ++ xbmc/addons/ContextItemAddon.h | 72 + xbmc/addons/DllAddon.h | 70 + xbmc/addons/DllLibCPluff.h | 116 ++ xbmc/addons/DllPVRClient.h | 29 + xbmc/addons/GUIDialogAddonInfo.cpp | 458 ++++++ xbmc/addons/GUIDialogAddonInfo.h | 80 + xbmc/addons/GUIDialogAddonSettings.cpp | 1200 +++++++++++++++ xbmc/addons/GUIDialogAddonSettings.h | 91 ++ xbmc/addons/GUIViewStateAddonBrowser.cpp | 132 ++ xbmc/addons/GUIViewStateAddonBrowser.h | 35 + xbmc/addons/GUIWindowAddonBrowser.cpp | 677 +++++++++ xbmc/addons/GUIWindowAddonBrowser.h | 79 + xbmc/addons/IAddon.h | 138 ++ xbmc/addons/Makefile | 31 + xbmc/addons/PluginSource.cpp | 96 ++ xbmc/addons/PluginSource.h | 58 + xbmc/addons/Repository.cpp | 395 +++++ xbmc/addons/Repository.h | 82 ++ xbmc/addons/Scraper.cpp | 1033 +++++++++++++ xbmc/addons/Scraper.h | 178 +++ xbmc/addons/ScreenSaver.cpp | 125 ++ xbmc/addons/ScreenSaver.h | 47 + xbmc/addons/Service.cpp | 158 ++ xbmc/addons/Service.h | 63 + xbmc/addons/Skin.cpp | 491 +++++++ xbmc/addons/Skin.h | 155 ++ xbmc/addons/Visualisation.cpp | 486 ++++++ xbmc/addons/Visualisation.h | 111 ++ xbmc/addons/Webinterface.cpp | 75 + xbmc/addons/Webinterface.h | 54 + xbmc/addons/addon-bindings.mk | 20 + xbmc/addons/include/NOTE | 12 + xbmc/addons/include/xbmc_addon_cpp_dll.h | 191 +++ xbmc/addons/include/xbmc_addon_dll.h | 55 + xbmc/addons/include/xbmc_addon_types.h | 64 + xbmc/addons/include/xbmc_audioenc_dll.h | 61 + xbmc/addons/include/xbmc_audioenc_types.h | 113 ++ xbmc/addons/include/xbmc_codec_types.h | 55 + xbmc/addons/include/xbmc_epg_types.h | 96 ++ xbmc/addons/include/xbmc_pvr_dll.h | 710 +++++++++ xbmc/addons/include/xbmc_pvr_types.h | 415 ++++++ xbmc/addons/include/xbmc_scr_dll.h | 45 + xbmc/addons/include/xbmc_scr_types.h | 53 + xbmc/addons/include/xbmc_stream_utils.hpp | 264 ++++ xbmc/addons/include/xbmc_vis_dll.h | 55 + xbmc/addons/include/xbmc_vis_types.h | 111 ++ xbmc/addons/test/Makefile | 9 + xbmc/addons/test/TestAddonVersion.cpp | 254 ++++ 74 files changed, 19498 insertions(+) create mode 100644 xbmc/addons/Addon.cpp create mode 100644 xbmc/addons/Addon.h create mode 100644 xbmc/addons/AddonCallbacks.cpp create mode 100644 xbmc/addons/AddonCallbacks.h create mode 100644 xbmc/addons/AddonCallbacksAddon.cpp create mode 100644 xbmc/addons/AddonCallbacksAddon.h create mode 100644 xbmc/addons/AddonCallbacksCodec.cpp create mode 100644 xbmc/addons/AddonCallbacksCodec.h create mode 100644 xbmc/addons/AddonCallbacksGUI.cpp create mode 100644 xbmc/addons/AddonCallbacksGUI.h create mode 100644 xbmc/addons/AddonCallbacksPVR.cpp create mode 100644 xbmc/addons/AddonCallbacksPVR.h create mode 100644 xbmc/addons/AddonDatabase.cpp create mode 100644 xbmc/addons/AddonDatabase.h create mode 100644 xbmc/addons/AddonDll.h create mode 100644 xbmc/addons/AddonInstaller.cpp create mode 100644 xbmc/addons/AddonInstaller.h create mode 100644 xbmc/addons/AddonManager.cpp create mode 100644 xbmc/addons/AddonManager.h create mode 100644 xbmc/addons/AddonStatusHandler.cpp create mode 100644 xbmc/addons/AddonStatusHandler.h create mode 100644 xbmc/addons/AddonVersion.cpp create mode 100644 xbmc/addons/AddonVersion.h create mode 100644 xbmc/addons/AudioEncoder.cpp create mode 100644 xbmc/addons/AudioEncoder.h create mode 100644 xbmc/addons/ContextItemAddon.cpp create mode 100644 xbmc/addons/ContextItemAddon.h create mode 100644 xbmc/addons/DllAddon.h create mode 100644 xbmc/addons/DllLibCPluff.h create mode 100644 xbmc/addons/DllPVRClient.h create mode 100644 xbmc/addons/GUIDialogAddonInfo.cpp create mode 100644 xbmc/addons/GUIDialogAddonInfo.h create mode 100644 xbmc/addons/GUIDialogAddonSettings.cpp create mode 100644 xbmc/addons/GUIDialogAddonSettings.h create mode 100644 xbmc/addons/GUIViewStateAddonBrowser.cpp create mode 100644 xbmc/addons/GUIViewStateAddonBrowser.h create mode 100644 xbmc/addons/GUIWindowAddonBrowser.cpp create mode 100644 xbmc/addons/GUIWindowAddonBrowser.h create mode 100644 xbmc/addons/IAddon.h create mode 100644 xbmc/addons/Makefile create mode 100644 xbmc/addons/PluginSource.cpp create mode 100644 xbmc/addons/PluginSource.h create mode 100644 xbmc/addons/Repository.cpp create mode 100644 xbmc/addons/Repository.h create mode 100644 xbmc/addons/Scraper.cpp create mode 100644 xbmc/addons/Scraper.h create mode 100644 xbmc/addons/ScreenSaver.cpp create mode 100644 xbmc/addons/ScreenSaver.h create mode 100644 xbmc/addons/Service.cpp create mode 100644 xbmc/addons/Service.h create mode 100644 xbmc/addons/Skin.cpp create mode 100644 xbmc/addons/Skin.h create mode 100644 xbmc/addons/Visualisation.cpp create mode 100644 xbmc/addons/Visualisation.h create mode 100644 xbmc/addons/Webinterface.cpp create mode 100644 xbmc/addons/Webinterface.h create mode 100644 xbmc/addons/addon-bindings.mk create mode 100644 xbmc/addons/include/NOTE create mode 100644 xbmc/addons/include/xbmc_addon_cpp_dll.h create mode 100644 xbmc/addons/include/xbmc_addon_dll.h create mode 100644 xbmc/addons/include/xbmc_addon_types.h create mode 100644 xbmc/addons/include/xbmc_audioenc_dll.h create mode 100644 xbmc/addons/include/xbmc_audioenc_types.h create mode 100644 xbmc/addons/include/xbmc_codec_types.h create mode 100644 xbmc/addons/include/xbmc_epg_types.h create mode 100644 xbmc/addons/include/xbmc_pvr_dll.h create mode 100644 xbmc/addons/include/xbmc_pvr_types.h create mode 100644 xbmc/addons/include/xbmc_scr_dll.h create mode 100644 xbmc/addons/include/xbmc_scr_types.h create mode 100644 xbmc/addons/include/xbmc_stream_utils.hpp create mode 100644 xbmc/addons/include/xbmc_vis_dll.h create mode 100644 xbmc/addons/include/xbmc_vis_types.h create mode 100644 xbmc/addons/test/Makefile create mode 100644 xbmc/addons/test/TestAddonVersion.cpp (limited to 'xbmc/addons') diff --git a/xbmc/addons/Addon.cpp b/xbmc/addons/Addon.cpp new file mode 100644 index 0000000..2aa849f --- /dev/null +++ b/xbmc/addons/Addon.cpp @@ -0,0 +1,653 @@ +/* + * Copyright (C) 2005-2013 Team XBMC + * http://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, see + * . + * + */ + +#include "Addon.h" +#include "AddonManager.h" +#include "settings/Settings.h" +#include "filesystem/Directory.h" +#include "filesystem/File.h" +#include "system.h" +#ifdef HAS_PYTHON +#include "interfaces/python/XBPython.h" +#endif +#if defined(TARGET_DARWIN) +#include "../osx/OSXGNUReplacements.h" +#endif +#ifdef TARGET_FREEBSD +#include "freebsd/FreeBSDGNUReplacements.h" +#endif +#include "utils/log.h" +#include "utils/StringUtils.h" +#include "utils/URIUtils.h" +#include "URL.h" +#include "Util.h" +#include +#include +#include + +using XFILE::CDirectory; +using XFILE::CFile; +using namespace std; + +namespace ADDON +{ + +/** + * helper functions + * + */ + +typedef struct +{ + const char* name; + TYPE type; + int pretty; + const char* icon; +} TypeMapping; + +static const TypeMapping types[] = + {{"unknown", ADDON_UNKNOWN, 0, "" }, + {"xbmc.metadata.scraper.albums", ADDON_SCRAPER_ALBUMS, 24016, "DefaultAddonAlbumInfo.png" }, + {"xbmc.metadata.scraper.artists", ADDON_SCRAPER_ARTISTS, 24017, "DefaultAddonArtistInfo.png" }, + {"xbmc.metadata.scraper.movies", ADDON_SCRAPER_MOVIES, 24007, "DefaultAddonMovieInfo.png" }, + {"xbmc.metadata.scraper.musicvideos", ADDON_SCRAPER_MUSICVIDEOS, 24015, "DefaultAddonMusicVideoInfo.png" }, + {"xbmc.metadata.scraper.tvshows", ADDON_SCRAPER_TVSHOWS, 24014, "DefaultAddonTvInfo.png" }, + {"xbmc.metadata.scraper.library", ADDON_SCRAPER_LIBRARY, 24083, "DefaultAddonInfoLibrary.png" }, + {"xbmc.ui.screensaver", ADDON_SCREENSAVER, 24008, "DefaultAddonScreensaver.png" }, + {"xbmc.player.musicviz", ADDON_VIZ, 24010, "DefaultAddonVisualization.png" }, + {"visualization-library", ADDON_VIZ_LIBRARY, 24084, "" }, + {"xbmc.python.pluginsource", ADDON_PLUGIN, 24005, "" }, + {"xbmc.python.script", ADDON_SCRIPT, 24009, "" }, + {"xbmc.python.weather", ADDON_SCRIPT_WEATHER, 24027, "DefaultAddonWeather.png" }, + {"xbmc.python.lyrics", ADDON_SCRIPT_LYRICS, 24013, "DefaultAddonLyrics.png" }, + {"xbmc.python.library", ADDON_SCRIPT_LIBRARY, 24081, "DefaultAddonHelper.png" }, + {"xbmc.python.module", ADDON_SCRIPT_MODULE, 24082, "DefaultAddonLibrary.png" }, + {"xbmc.subtitle.module", ADDON_SUBTITLE_MODULE, 24012, "DefaultAddonSubtitles.png" }, + {"kodi.context.item", ADDON_CONTEXT_ITEM, 24025, "DefaultAddonContextItem.png" }, + {"xbmc.gui.skin", ADDON_SKIN, 166, "DefaultAddonSkin.png" }, + {"xbmc.webinterface", ADDON_WEB_INTERFACE, 199, "DefaultAddonWebSkin.png" }, + {"xbmc.addon.repository", ADDON_REPOSITORY, 24011, "DefaultAddonRepository.png" }, + {"xbmc.pvrclient", ADDON_PVRDLL, 24019, "DefaultAddonPVRClient.png" }, + {"xbmc.addon.video", ADDON_VIDEO, 1037, "DefaultAddonVideo.png" }, + {"xbmc.addon.audio", ADDON_AUDIO, 1038, "DefaultAddonMusic.png" }, + {"xbmc.addon.image", ADDON_IMAGE, 1039, "DefaultAddonPicture.png" }, + {"xbmc.addon.executable", ADDON_EXECUTABLE, 1043, "DefaultAddonProgram.png" }, + {"xbmc.audioencoder", ADDON_AUDIOENCODER, 200, "DefaultAddonAudioEncoder.png" }, + {"xbmc.service", ADDON_SERVICE, 24018, "DefaultAddonService.png" }}; + +const std::string TranslateType(const ADDON::TYPE &type, bool pretty/*=false*/) +{ + for (unsigned int index=0; index < ARRAY_SIZE(types); ++index) + { + const TypeMapping &map = types[index]; + if (type == map.type) + { + if (pretty && map.pretty) + return g_localizeStrings.Get(map.pretty); + else + return map.name; + } + } + return ""; +} + +TYPE TranslateType(const std::string &string) +{ + for (unsigned int index=0; index < ARRAY_SIZE(types); ++index) + { + const TypeMapping &map = types[index]; + if (string == map.name) + return map.type; + } + + return ADDON_UNKNOWN; +} + +const std::string GetIcon(const ADDON::TYPE& type) +{ + for (unsigned int index=0; index < ARRAY_SIZE(types); ++index) + { + const TypeMapping &map = types[index]; + if (type == map.type) + return map.icon; + } + return ""; +} + +#define EMPTY_IF(x,y) \ + { \ + std::string fan=CAddonMgr::Get().GetExtValue(metadata->configuration, x); \ + if (fan == "true") \ + y.clear(); \ + } + +#define SS(x) (x) ? x : "" + +AddonProps::AddonProps(const cp_extension_t *ext) + : id(SS(ext->plugin->identifier)) + , version(SS(ext->plugin->version)) + , minversion(SS(ext->plugin->abi_bw_compatibility)) + , name(SS(ext->plugin->name)) + , path(SS(ext->plugin->plugin_path)) + , author(SS(ext->plugin->provider_name)) + , stars(0) +{ + if (ext->ext_point_id) + type = TranslateType(ext->ext_point_id); + + icon = "icon.png"; + fanart = URIUtils::AddFileToFolder(path, "fanart.jpg"); + changelog = URIUtils::AddFileToFolder(path, "changelog.txt"); + // Grab more detail from the props... + const cp_extension_t *metadata = CAddonMgr::Get().GetExtension(ext->plugin, "xbmc.addon.metadata"); //plugin, "kodi.addon.metadata"); + if (metadata) + { + 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"); + std::string language; + language = CAddonMgr::Get().GetExtValue(metadata->configuration, "language"); + if (!language.empty()) + extrainfo.insert(make_pair("language",language)); + broken = CAddonMgr::Get().GetExtValue(metadata->configuration, "broken"); + EMPTY_IF("nofanart",fanart) + EMPTY_IF("noicon",icon) + EMPTY_IF("nochangelog",changelog) + } + BuildDependencies(ext->plugin); +} + +AddonProps::AddonProps(const cp_plugin_info_t *plugin) + : id(SS(plugin->identifier)) + , type(ADDON_UNKNOWN) + , version(SS(plugin->version)) + , minversion(SS(plugin->abi_bw_compatibility)) + , name(SS(plugin->name)) + , path(SS(plugin->plugin_path)) + , author(SS(plugin->provider_name)) + , stars(0) +{ + BuildDependencies(plugin); +} + +void AddonProps::Serialize(CVariant &variant) const +{ + variant["addonid"] = id; + variant["type"] = TranslateType(type); + variant["version"] = version.asString(); + variant["minversion"] = minversion.asString(); + variant["name"] = name; + variant["license"] = license; + variant["summary"] = summary; + variant["description"] = description; + variant["path"] = path; + variant["libname"] = libname; + variant["author"] = author; + variant["source"] = source; + + if (CURL::IsFullPath(icon)) + variant["icon"] = icon; + else + variant["icon"] = URIUtils::AddFileToFolder(path, icon); + + variant["thumbnail"] = variant["icon"]; + variant["disclaimer"] = disclaimer; + variant["changelog"] = changelog; + + if (CURL::IsFullPath(fanart)) + variant["fanart"] = fanart; + else + variant["fanart"] = URIUtils::AddFileToFolder(path, fanart); + + variant["dependencies"] = CVariant(CVariant::VariantTypeArray); + for (ADDONDEPS::const_iterator it = dependencies.begin(); it != dependencies.end(); ++it) + { + CVariant dep(CVariant::VariantTypeObject); + dep["addonid"] = it->first; + dep["version"] = it->second.first.asString(); + dep["optional"] = it->second.second; + variant["dependencies"].push_back(dep); + } + if (broken.empty()) + variant["broken"] = false; + else + variant["broken"] = broken; + variant["extrainfo"] = CVariant(CVariant::VariantTypeArray); + for (InfoMap::const_iterator it = extrainfo.begin(); it != extrainfo.end(); ++it) + { + CVariant info(CVariant::VariantTypeObject); + info["key"] = it->first; + info["value"] = it->second; + variant["extrainfo"].push_back(info); + } + variant["rating"] = stars; +} + +void AddonProps::BuildDependencies(const cp_plugin_info_t *plugin) +{ + if (!plugin) + return; + for (unsigned int i = 0; i < plugin->num_imports; ++i) + dependencies.insert(make_pair(std::string(plugin->imports[i].plugin_id), + make_pair(AddonVersion(SS(plugin->imports[i].version)), plugin->imports[i].optional != 0))); +} + +/** + * CAddon + * + */ + +CAddon::CAddon(const cp_extension_t *ext) + : m_props(ext) +{ + BuildLibName(ext); + Props().libname = m_strLibName; + BuildProfilePath(); + m_userSettingsPath = URIUtils::AddFileToFolder(Profile(), "settings.xml"); + m_enabled = true; + m_hasSettings = true; + m_hasStrings = false; + m_checkedStrings = false; + m_settingsLoaded = false; + m_userSettingsLoaded = false; +} + +CAddon::CAddon(const cp_plugin_info_t *plugin) + : m_props(plugin) +{ + m_enabled = true; + m_hasSettings = false; + m_hasStrings = false; + m_checkedStrings = true; + m_settingsLoaded = false; + m_userSettingsLoaded = false; +} + +CAddon::CAddon(const AddonProps &props) + : m_props(props) +{ + if (props.libname.empty()) BuildLibName(); + else m_strLibName = props.libname; + BuildProfilePath(); + m_userSettingsPath = URIUtils::AddFileToFolder(Profile(), "settings.xml"); + m_enabled = true; + m_hasSettings = true; + m_hasStrings = false; + m_checkedStrings = false; + m_settingsLoaded = false; + m_userSettingsLoaded = false; +} + +CAddon::CAddon(const CAddon &rhs) + : m_props(rhs.Props()), + m_settings(rhs.m_settings) +{ + m_addonXmlDoc = rhs.m_addonXmlDoc; + m_settingsLoaded = rhs.m_settingsLoaded; + m_userSettingsLoaded = rhs.m_userSettingsLoaded; + m_hasSettings = rhs.m_hasSettings; + BuildProfilePath(); + m_userSettingsPath = URIUtils::AddFileToFolder(Profile(), "settings.xml"); + m_strLibName = rhs.m_strLibName; + m_enabled = rhs.Enabled(); + m_hasStrings = false; + m_checkedStrings = false; +} + +AddonPtr CAddon::Clone() const +{ + return AddonPtr(new CAddon(*this)); +} + +bool CAddon::MeetsVersion(const AddonVersion &version) const +{ + return m_props.minversion <= version && version <= m_props.version; +} + +//TODO platform/path crap should be negotiated between the addon and +// the handler for it's type +void CAddon::BuildLibName(const cp_extension_t *extension) +{ + if (!extension) + { + m_strLibName = "default"; + std::string ext; + switch (m_props.type) + { + case ADDON_SCRAPER_ALBUMS: + case ADDON_SCRAPER_ARTISTS: + case ADDON_SCRAPER_MOVIES: + case ADDON_SCRAPER_MUSICVIDEOS: + case ADDON_SCRAPER_TVSHOWS: + 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_PVRDLL: + ext = ADDON_PVRDLL_EXT; + break; + case ADDON_SCRIPT: + case ADDON_SCRIPT_LIBRARY: + case ADDON_SCRIPT_LYRICS: + case ADDON_SCRIPT_WEATHER: + case ADDON_SUBTITLE_MODULE: + case ADDON_PLUGIN: + case ADDON_SERVICE: + case ADDON_CONTEXT_ITEM: + 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_SCRIPT_LIBRARY: + case ADDON_SCRIPT_LYRICS: + case ADDON_SCRIPT_WEATHER: + case ADDON_SCRIPT_MODULE: + case ADDON_SUBTITLE_MODULE: + case ADDON_SCRAPER_ALBUMS: + case ADDON_SCRAPER_ARTISTS: + case ADDON_SCRAPER_MOVIES: + case ADDON_SCRAPER_MUSICVIDEOS: + case ADDON_SCRAPER_TVSHOWS: + case ADDON_SCRAPER_LIBRARY: + case ADDON_PVRDLL: + case ADDON_PLUGIN: + case ADDON_WEB_INTERFACE: + case ADDON_SERVICE: + case ADDON_REPOSITORY: + case ADDON_AUDIOENCODER: + case ADDON_CONTEXT_ITEM: + { + std::string temp = CAddonMgr::Get().GetExtValue(extension->configuration, "@library"); + m_strLibName = temp; + } + break; + default: + m_strLibName.clear(); + break; + } + } +} + +/** + * Language File Handling + */ +bool CAddon::LoadStrings() +{ + // Path where the language strings reside + std::string chosenPath = URIUtils::AddFileToFolder(m_props.path, "resources/language/"); + + m_hasStrings = m_strings.Load(chosenPath, CSettings::Get().GetString("locale.language")); + return m_checkedStrings = true; +} + +void CAddon::ClearStrings() +{ + // Unload temporary language strings + m_strings.Clear(); + m_hasStrings = false; +} + +std::string CAddon::GetString(uint32_t id) +{ + if (!m_hasStrings && ! m_checkedStrings && !LoadStrings()) + return ""; + + return m_strings.Get(id); +} + +/** + * Settings Handling + */ +bool CAddon::HasSettings() +{ + return LoadSettings(); +} + +bool CAddon::LoadSettings(bool bForce /* = false*/) +{ + if (m_settingsLoaded && !bForce) + return true; + if (!m_hasSettings) + return false; + std::string addonFileName = URIUtils::AddFileToFolder(m_props.path, "resources/settings.xml"); + + if (!m_addonXmlDoc.LoadFile(addonFileName)) + { + if (CFile::Exists(addonFileName)) + CLog::Log(LOGERROR, "Unable to load: %s, Line %d\n%s", addonFileName.c_str(), m_addonXmlDoc.ErrorRow(), m_addonXmlDoc.ErrorDesc()); + m_hasSettings = false; + 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; + } + SettingsFromXML(m_addonXmlDoc, true); + LoadUserSettings(); + m_settingsLoaded = true; + return true; +} + +bool CAddon::HasUserSettings() +{ + if (!LoadSettings()) + return false; + + return m_userSettingsLoaded; +} + +bool CAddon::ReloadSettings() +{ + return LoadSettings(true); +} + +bool CAddon::LoadUserSettings() +{ + m_userSettingsLoaded = false; + CXBMCTinyXML doc; + if (doc.LoadFile(m_userSettingsPath)) + m_userSettingsLoaded = SettingsFromXML(doc); + return m_userSettingsLoaded; +} + +void CAddon::SaveSettings(void) +{ + if (m_settings.empty()) + return; // no settings to save + + // break down the path into directories + std::string strAddon = URIUtils::GetDirectory(m_userSettingsPath); + URIUtils::RemoveSlashAtEnd(strAddon); + std::string strRoot = URIUtils::GetDirectory(strAddon); + URIUtils::RemoveSlashAtEnd(strRoot); + + // create the individual folders + if (!CDirectory::Exists(strRoot)) + CDirectory::Create(strRoot); + if (!CDirectory::Exists(strAddon)) + CDirectory::Create(strAddon); + + // create the XML file + CXBMCTinyXML doc; + SettingsToXML(doc); + doc.SaveFile(m_userSettingsPath); + m_userSettingsLoaded = true; + + CAddonMgr::Get().ReloadSettings(ID());//push the settings changes to the running addon instance +#ifdef HAS_PYTHON + g_pythonParser.OnSettingsChanged(ID()); +#endif +} + +std::string CAddon::GetSetting(const std::string& key) +{ + if (!LoadSettings()) + return ""; // no settings available + + map::const_iterator i = m_settings.find(key); + if (i != m_settings.end()) + return i->second; + return ""; +} + +void CAddon::UpdateSetting(const std::string& key, const std::string& value) +{ + LoadSettings(); + if (key.empty()) return; + m_settings[key] = value; +} + +bool CAddon::SettingsFromXML(const CXBMCTinyXML &doc, bool loadDefaults /*=false */) +{ + if (!doc.RootElement()) + return false; + + if (loadDefaults) + m_settings.clear(); + + const TiXmlElement* category = doc.RootElement()->FirstChildElement("category"); + if (!category) + category = doc.RootElement(); + + bool foundSetting = false; + while (category) + { + const TiXmlElement *setting = category->FirstChildElement("setting"); + while (setting) + { + const char *id = setting->Attribute("id"); + const char *value = setting->Attribute(loadDefaults ? "default" : "value"); + if (id && value) + { + m_settings[id] = value; + foundSetting = true; + } + setting = setting->NextSiblingElement("setting"); + } + category = category->NextSiblingElement("category"); + } + return foundSetting; +} + +void CAddon::SettingsToXML(CXBMCTinyXML &doc) const +{ + TiXmlElement node("settings"); + doc.InsertEndChild(node); + for (map::const_iterator i = m_settings.begin(); i != m_settings.end(); ++i) + { + TiXmlElement nodeSetting("setting"); + nodeSetting.SetAttribute("id", i->first.c_str()); + nodeSetting.SetAttribute("value", i->second.c_str()); + doc.RootElement()->InsertEndChild(nodeSetting); + } + doc.SaveFile(m_userSettingsPath); +} + +TiXmlElement* CAddon::GetSettingsXML() +{ + return m_addonXmlDoc.RootElement(); +} + +void CAddon::BuildProfilePath() +{ + m_profile = StringUtils::Format("special://profile/addon_data/%s/", ID().c_str()); +} + +const std::string CAddon::Icon() const +{ + if (CURL::IsFullPath(m_props.icon)) + return m_props.icon; + return URIUtils::AddFileToFolder(m_props.path, m_props.icon); +} + +const std::string CAddon::LibPath() const +{ + return URIUtils::AddFileToFolder(m_props.path, m_strLibName); +} + +AddonVersion CAddon::GetDependencyVersion(const std::string &dependencyID) const +{ + const ADDON::ADDONDEPS &deps = GetDeps(); + ADDONDEPS::const_iterator it = deps.find(dependencyID); + if (it != deps.end()) + return it->second.first; + return AddonVersion("0.0.0"); +} + +/** + * CAddonLibrary + * + */ + +CAddonLibrary::CAddonLibrary(const cp_extension_t *ext) + : CAddon(ext) + , m_addonType(SetAddonType()) +{ +} + +CAddonLibrary::CAddonLibrary(const AddonProps& props) + : CAddon(props) + , m_addonType(SetAddonType()) +{ +} + +AddonPtr CAddonLibrary::Clone() const +{ + return AddonPtr(new CAddonLibrary(*this)); +} + +TYPE CAddonLibrary::SetAddonType() +{ + if (Type() == ADDON_VIZ_LIBRARY) + return ADDON_VIZ; + else + return ADDON_UNKNOWN; +} + +} /* namespace ADDON */ + diff --git a/xbmc/addons/Addon.h b/xbmc/addons/Addon.h new file mode 100644 index 0000000..4fbf085 --- /dev/null +++ b/xbmc/addons/Addon.h @@ -0,0 +1,278 @@ +#pragma once +/* + * Copyright (C) 2005-2013 Team XBMC + * http://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, see + * . + * + */ + +#include "IAddon.h" +#include "addons/AddonVersion.h" +#include "utils/XBMCTinyXML.h" +#include "guilib/LocalizeStrings.h" +#include "utils/ISerializable.h" +#include + +class TiXmlElement; +class CAddonCallbacksAddon; + +typedef struct cp_plugin_info_t cp_plugin_info_t; +typedef struct cp_extension_t cp_extension_t; + +namespace ADDON +{ + typedef std::vector VECADDONS; + typedef std::vector::iterator IVECADDONS; + +// utils +const std::string TranslateType(const TYPE &type, bool pretty=false); +const std::string GetIcon(const TYPE &type); + TYPE TranslateType(const std::string &string); + +class AddonProps : public ISerializable +{ +public: + AddonProps(const std::string &id, TYPE type, const std::string &versionstr, const std::string &minversionstr) + : id(id) + , type(type) + , version(versionstr) + , minversion(minversionstr) + , stars(0) + { + } + + virtual ~AddonProps() {} + + AddonProps(const cp_extension_t *ext); + AddonProps(const cp_plugin_info_t *plugin); + + bool operator==(const AddonProps &rhs) + { + return (*this).id == rhs.id + && (*this).type == rhs.type + && (*this).version == rhs.version; + } + + void Serialize(CVariant &variant) const; + + std::string id; + TYPE type; + AddonVersion version; + AddonVersion minversion; + std::string name; + std::string license; + std::string summary; + std::string description; + std::string path; + std::string libname; + std::string author; + std::string source; + std::string icon; + std::string disclaimer; + std::string changelog; + std::string fanart; + ADDONDEPS dependencies; + std::string broken; + InfoMap extrainfo; + int stars; +private: + void BuildDependencies(const cp_plugin_info_t *plugin); +}; + +typedef std::vector VECADDONPROPS; + +class CAddon : public IAddon +{ +public: + CAddon(const AddonProps &addonprops); + CAddon(const cp_extension_t *ext); + CAddon(const cp_plugin_info_t *plugin); + virtual ~CAddon() {} + virtual AddonPtr Clone() const; + + /*! \brief Check whether the this addon can be configured or not + \return true if the addon has settings, false otherwise + \sa LoadSettings, LoadUserSettings, SaveSettings, HasUserSettings, GetSetting, UpdateSetting + */ + bool HasSettings(); + + /*! \brief Check whether the user has configured this addon or not + \return true if previously saved settings are found, false otherwise + \sa LoadSettings, LoadUserSettings, SaveSettings, HasSettings, GetSetting, UpdateSetting + */ + bool HasUserSettings(); + + /*! \brief Save any user configured settings + \sa LoadSettings, LoadUserSettings, HasSettings, HasUserSettings, GetSetting, UpdateSetting + */ + virtual void SaveSettings(); + + /*! \brief Update a user-configured setting with a new value + \param key the id of the setting to update + \param value the value that the setting should take + \sa LoadSettings, LoadUserSettings, SaveSettings, HasSettings, HasUserSettings, GetSetting + */ + void UpdateSetting(const std::string& key, const std::string& value); + + /*! \brief Retrieve a particular settings value + If a previously configured user setting is available, we return it's value, else we return the default (if available) + \param key the id of the setting to retrieve + \return the current value of the setting, or the default if the setting has yet to be configured. + \sa LoadSettings, LoadUserSettings, SaveSettings, HasSettings, HasUserSettings, UpdateSetting + */ + virtual std::string GetSetting(const std::string& key); + + TiXmlElement* GetSettingsXML(); + virtual std::string GetString(uint32_t id); + + // properties + TYPE Type() const { return m_props.type; } + bool IsType(TYPE type) const { return type == m_props.type; } + AddonProps Props() const { return m_props; } + AddonProps& Props() { return m_props; } + const std::string ID() const { return m_props.id; } + const std::string Name() const { return m_props.name; } + bool Enabled() const { return m_enabled; } + virtual bool IsInUse() const { return false; }; + const AddonVersion Version() const { return m_props.version; } + const AddonVersion MinVersion() const { return m_props.minversion; } + const std::string Summary() const { return m_props.summary; } + const std::string Description() const { return m_props.description; } + const std::string Path() const { return m_props.path; } + const std::string Profile() const { return m_profile; } + const std::string LibPath() const; + const std::string Author() const { return m_props.author; } + const std::string ChangeLog() const { return m_props.changelog; } + const std::string FanArt() const { return m_props.fanart; } + const std::string Icon() const; + int Stars() const { return m_props.stars; } + const std::string Disclaimer() const { return m_props.disclaimer; } + const InfoMap &ExtraInfo() const { return m_props.extrainfo; } + const ADDONDEPS &GetDeps() const { return m_props.dependencies; } + + /*! \brief get the required version of a dependency. + \param dependencyID the addon ID of the dependency. + \return the version this addon requires. + */ + AddonVersion GetDependencyVersion(const std::string &dependencyID) const; + + /*! \brief return whether or not this addon satisfies the given version requirements + \param version the version to meet. + \return true if min_version <= version <= current_version, false otherwise. + */ + bool MeetsVersion(const AddonVersion &version) const; + virtual bool ReloadSettings(); + + void MarkAsDisabled() { m_enabled = false; } + + /*! \brief callback for when this add-on is disabled. + Use to perform any needed actions (e.g. stop a service) + */ + virtual void OnDisabled() {}; + + /*! \brief callback for when this add-on is enabled. + Use to perform any needed actions (e.g. start a service) + */ + virtual void OnEnabled() {}; + + /*! \brief retrieve the running instance of an add-on if it persists while running. + */ + virtual AddonPtr GetRunningInstance() const { return AddonPtr(); } + + /*! \brief callbacks for special install/uninstall behaviour */ + virtual bool OnPreInstall() { return false; }; + virtual void OnPostInstall(bool restart, bool update, bool modal) {}; + virtual void OnPreUnInstall() {}; + virtual void OnPostUnInstall() {}; + virtual bool CanInstall(const std::string& referer) { return true; } +protected: + friend class CAddonCallbacksAddon; + + CAddon(const CAddon &rhs); // protected as all copying is handled by Clone() + virtual void BuildLibName(const cp_extension_t *ext = NULL); + + /*! \brief Load the default settings and override these with any previously configured user settings + \param bForce force the load of settings even if they are already loaded (reload) + \return true if settings exist, false otherwise + \sa LoadUserSettings, SaveSettings, HasSettings, HasUserSettings, GetSetting, UpdateSetting + */ + virtual bool LoadSettings(bool bForce = false); + + /*! \brief Load the user settings + \return true if user settings exist, false otherwise + \sa LoadSettings, SaveSettings, HasSettings, HasUserSettings, GetSetting, UpdateSetting + */ + bool LoadUserSettings(); + + /*! \brief Parse settings from an XML document + \param doc XML document to parse for settings + \param loadDefaults if true, the default attribute is used and settings are reset prior to parsing, else the value attribute is used. + \return true if settings are loaded, false otherwise + \sa SettingsToXML + */ + bool SettingsFromXML(const CXBMCTinyXML &doc, bool loadDefaults = false); + + /*! \brief Parse settings into an XML document + \param doc XML document to receive the settings + \sa SettingsFromXML + */ + void SettingsToXML(CXBMCTinyXML &doc) const; + + CXBMCTinyXML m_addonXmlDoc; + std::string m_strLibName; + bool m_settingsLoaded; + bool m_userSettingsLoaded; + + virtual void ClearStrings(); +private: + friend class CAddonMgr; + AddonProps m_props; + std::string m_userSettingsPath; + void BuildProfilePath(); + + virtual bool IsAddonLibrary() { return false; } + + void Enable() { LoadStrings(); m_enabled = true; } + void Disable() { m_enabled = false; ClearStrings();} + + virtual bool LoadStrings(); + + bool m_hasStrings; + bool m_checkedStrings; + bool m_hasSettings; + + std::string m_profile; + bool m_enabled; + CLocalizeStrings m_strings; + std::map m_settings; +}; + +class CAddonLibrary : public CAddon +{ +public: + CAddonLibrary(const AddonProps &props); + CAddonLibrary(const cp_extension_t *ext); + + virtual AddonPtr Clone() const; + +private: + virtual bool IsAddonLibrary() { return true; } + TYPE SetAddonType(); + const TYPE m_addonType; // addon type this library enhances +}; + +}; /* namespace ADDON */ + diff --git a/xbmc/addons/AddonCallbacks.cpp b/xbmc/addons/AddonCallbacks.cpp new file mode 100644 index 0000000..8634373 --- /dev/null +++ b/xbmc/addons/AddonCallbacks.cpp @@ -0,0 +1,173 @@ +/* + * Copyright (C) 2012-2013 Team XBMC + * http://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, see + * . + * + */ + +#include "Addon.h" +#include "AddonCallbacks.h" +#include "AddonCallbacksAddon.h" +#include "AddonCallbacksCodec.h" +#include "AddonCallbacksGUI.h" +#include "AddonCallbacksPVR.h" +#include "filesystem/SpecialProtocol.h" +#include "utils/log.h" + +namespace ADDON +{ + +CAddonCallbacks::CAddonCallbacks(CAddon* addon) +{ + m_addon = addon; + m_callbacks = new AddonCB; + m_helperAddon = NULL; + m_helperGUI = NULL; + m_helperPVR = NULL; + m_helperCODEC = NULL; + + m_callbacks->libBasePath = strdup(CSpecialProtocol::TranslatePath("special://xbmcbin/addons").c_str()); + m_callbacks->addonData = this; + m_callbacks->AddOnLib_RegisterMe = CAddonCallbacks::AddOnLib_RegisterMe; + m_callbacks->AddOnLib_UnRegisterMe = CAddonCallbacks::AddOnLib_UnRegisterMe; + m_callbacks->CODECLib_RegisterMe = CAddonCallbacks::CODECLib_RegisterMe; + m_callbacks->CODECLib_UnRegisterMe = CAddonCallbacks::CODECLib_UnRegisterMe; + m_callbacks->GUILib_RegisterMe = CAddonCallbacks::GUILib_RegisterMe; + m_callbacks->GUILib_UnRegisterMe = CAddonCallbacks::GUILib_UnRegisterMe; + m_callbacks->PVRLib_RegisterMe = CAddonCallbacks::PVRLib_RegisterMe; + m_callbacks->PVRLib_UnRegisterMe = CAddonCallbacks::PVRLib_UnRegisterMe; +} + +CAddonCallbacks::~CAddonCallbacks() +{ + delete m_helperAddon; + m_helperAddon = NULL; + delete m_helperCODEC; + m_helperCODEC = NULL; + delete m_helperGUI; + m_helperGUI = NULL; + delete m_helperPVR; + m_helperPVR = NULL; + free((char*)m_callbacks->libBasePath); + delete m_callbacks; + m_callbacks = NULL; +} + +CB_AddOnLib* CAddonCallbacks::AddOnLib_RegisterMe(void *addonData) +{ + CAddonCallbacks* addon = (CAddonCallbacks*) addonData; + if (addon == NULL) + { + CLog::Log(LOGERROR, "CAddonCallbacks - %s - called with a null pointer", __FUNCTION__); + return NULL; + } + + addon->m_helperAddon = new CAddonCallbacksAddon(addon->m_addon); + return addon->m_helperAddon->GetCallbacks(); +} + +void CAddonCallbacks::AddOnLib_UnRegisterMe(void *addonData, CB_AddOnLib *cbTable) +{ + CAddonCallbacks* addon = (CAddonCallbacks*) addonData; + if (addon == NULL) + { + CLog::Log(LOGERROR, "CAddonCallbacks - %s - called with a null pointer", __FUNCTION__); + return; + } + + delete addon->m_helperAddon; + addon->m_helperAddon = NULL; +} + +CB_CODECLib* CAddonCallbacks::CODECLib_RegisterMe(void *addonData) +{ + CAddonCallbacks* addon = (CAddonCallbacks*) addonData; + if (addon == NULL) + { + CLog::Log(LOGERROR, "CAddonCallbacks - %s - called with a null pointer", __FUNCTION__); + return NULL; + } + + addon->m_helperCODEC = new CAddonCallbacksCodec(addon->m_addon); + return addon->m_helperCODEC->GetCallbacks(); +} + +void CAddonCallbacks::CODECLib_UnRegisterMe(void *addonData, CB_CODECLib *cbTable) +{ + CAddonCallbacks* addon = (CAddonCallbacks*) addonData; + if (addon == NULL) + { + CLog::Log(LOGERROR, "CAddonCallbacks - %s - called with a null pointer", __FUNCTION__); + return; + } + + delete addon->m_helperCODEC; + addon->m_helperCODEC = NULL; +} + +CB_GUILib* CAddonCallbacks::GUILib_RegisterMe(void *addonData) +{ + CAddonCallbacks* addon = (CAddonCallbacks*) addonData; + if (addon == NULL) + { + CLog::Log(LOGERROR, "CAddonCallbacks - %s - called with a null pointer", __FUNCTION__); + return NULL; + } + + addon->m_helperGUI = new CAddonCallbacksGUI(addon->m_addon); + return addon->m_helperGUI->GetCallbacks(); +} + +void CAddonCallbacks::GUILib_UnRegisterMe(void *addonData, CB_GUILib *cbTable) +{ + CAddonCallbacks* addon = (CAddonCallbacks*) addonData; + if (addon == NULL) + { + CLog::Log(LOGERROR, "CAddonCallbacks - %s - called with a null pointer", __FUNCTION__); + return; + } + + delete addon->m_helperGUI; + addon->m_helperGUI = NULL; +} + +CB_PVRLib* CAddonCallbacks::PVRLib_RegisterMe(void *addonData) +{ + CAddonCallbacks* addon = (CAddonCallbacks*) addonData; + if (addon == NULL) + { + CLog::Log(LOGERROR, "CAddonCallbacks - %s - called with a null pointer", __FUNCTION__); + return NULL; + } + + addon->m_helperPVR = new CAddonCallbacksPVR(addon->m_addon); + return addon->m_helperPVR->GetCallbacks(); +} + +void CAddonCallbacks::PVRLib_UnRegisterMe(void *addonData, CB_PVRLib *cbTable) +{ + CAddonCallbacks* addon = (CAddonCallbacks*) addonData; + if (addon == NULL) + { + CLog::Log(LOGERROR, "CAddonCallbacks - %s - called with a null pointer", __FUNCTION__); + return; + } + + delete addon->m_helperPVR; + addon->m_helperPVR = NULL; +} + +}; /* namespace ADDON */ diff --git a/xbmc/addons/AddonCallbacks.h b/xbmc/addons/AddonCallbacks.h new file mode 100644 index 0000000..f0902e4 --- /dev/null +++ b/xbmc/addons/AddonCallbacks.h @@ -0,0 +1,466 @@ +#pragma once +/* + * Copyright (C) 2012-2013 Team XBMC + * http://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, see + * . + * + */ + +#include +#include "cores/dvdplayer/DVDDemuxers/DVDDemuxUtils.h" +#include "addons/include/xbmc_pvr_types.h" +#include "addons/include/xbmc_codec_types.h" +#include "../../addons/library.xbmc.gui/libXBMC_gui.h" + +#ifdef TARGET_WINDOWS +#ifndef _SSIZE_T_DEFINED +typedef intptr_t ssize_t; +#define _SSIZE_T_DEFINED +#endif // !_SSIZE_T_DEFINED +#endif // TARGET_WINDOWS + +typedef void (*AddOnLogCallback)(void *addonData, const ADDON::addon_log_t loglevel, const char *msg); +typedef void (*AddOnQueueNotification)(void *addonData, const ADDON::queue_msg_t type, const char *msg); +typedef bool (*AddOnWakeOnLan)(const char* mac); +typedef bool (*AddOnGetSetting)(void *addonData, const char *settingName, void *settingValue); +typedef char* (*AddOnUnknownToUTF8)(const char *sourceDest); +typedef char* (*AddOnGetLocalizedString)(const void* addonData, long dwCode); +typedef char* (*AddOnGetDVDMenuLanguage)(const void* addonData); +typedef void (*AddOnFreeString)(const void* addonData, char* str); + +typedef void* (*AddOnOpenFile)(const void* addonData, const char* strFileName, unsigned int flags); +typedef void* (*AddOnOpenFileForWrite)(const void* addonData, const char* strFileName, bool bOverWrite); +typedef ssize_t (*AddOnReadFile)(const void* addonData, void* file, void* lpBuf, size_t uiBufSize); +typedef bool (*AddOnReadFileString)(const void* addonData, void* file, char *szLine, int iLineLength); +typedef ssize_t (*AddOnWriteFile)(const void* addonData, void* file, const void* lpBuf, size_t uiBufSize); +typedef void (*AddOnFlushFile)(const void* addonData, void* file); +typedef int64_t (*AddOnSeekFile)(const void* addonData, void* file, int64_t iFilePosition, int iWhence); +typedef int (*AddOnTruncateFile)(const void* addonData, void* file, int64_t iSize); +typedef int64_t (*AddOnGetFilePosition)(const void* addonData, void* file); +typedef int64_t (*AddOnGetFileLength)(const void* addonData, void* file); +typedef void (*AddOnCloseFile)(const void* addonData, void* file); +typedef int (*AddOnGetFileChunkSize)(const void* addonData, void* file); +typedef bool (*AddOnFileExists)(const void* addonData, const char *strFileName, bool bUseCache); +typedef int (*AddOnStatFile)(const void* addonData, const char *strFileName, struct __stat64* buffer); +typedef bool (*AddOnDeleteFile)(const void* addonData, const char *strFileName); +typedef bool (*AddOnCanOpenDirectory)(const void* addonData, const char* strURL); +typedef bool (*AddOnCreateDirectory)(const void* addonData, const char *strPath); +typedef bool (*AddOnDirectoryExists)(const void* addonData, const char *strPath); +typedef bool (*AddOnRemoveDirectory)(const void* addonData, const char *strPath); + +typedef struct CB_AddOn +{ + AddOnLogCallback Log; + AddOnQueueNotification QueueNotification; + AddOnWakeOnLan WakeOnLan; + AddOnGetSetting GetSetting; + AddOnUnknownToUTF8 UnknownToUTF8; + AddOnGetLocalizedString GetLocalizedString; + AddOnGetDVDMenuLanguage GetDVDMenuLanguage; + AddOnFreeString FreeString; + + AddOnOpenFile OpenFile; + AddOnOpenFileForWrite OpenFileForWrite; + AddOnReadFile ReadFile; + AddOnReadFileString ReadFileString; + AddOnWriteFile WriteFile; + AddOnFlushFile FlushFile; + AddOnSeekFile SeekFile; + AddOnTruncateFile TruncateFile; + AddOnGetFilePosition GetFilePosition; + AddOnGetFileLength GetFileLength; + AddOnCloseFile CloseFile; + AddOnGetFileChunkSize GetFileChunkSize; + AddOnFileExists FileExists; + AddOnStatFile StatFile; + AddOnDeleteFile DeleteFile; + AddOnCanOpenDirectory CanOpenDirectory; + AddOnCreateDirectory CreateDirectory; + AddOnDirectoryExists DirectoryExists; + AddOnRemoveDirectory RemoveDirectory; +} CB_AddOnLib; + +typedef xbmc_codec_t (*CODECGetCodecByName)(const void* addonData, const char* strCodecName); + +typedef struct CB_CODEC +{ + CODECGetCodecByName GetCodecByName; +} CB_CODECLib; + +typedef void (*GUILock)(); +typedef void (*GUIUnlock)(); +typedef int (*GUIGetScreenHeight)(); +typedef int (*GUIGetScreenWidth)(); +typedef int (*GUIGetVideoResolution)(); +typedef GUIHANDLE (*GUIWindow_New)(void *addonData, const char *xmlFilename, const char *defaultSkin, bool forceFallback, bool asDialog); +typedef void (*GUIWindow_Delete)(void *addonData, GUIHANDLE handle); +typedef void (*GUIWindow_SetCallbacks)(void *addonData, GUIHANDLE handle, GUIHANDLE clienthandle, bool (*)(GUIHANDLE handle), bool (*)(GUIHANDLE handle, int), bool (*)(GUIHANDLE handle, int), bool (*)(GUIHANDLE handle, int)); +typedef bool (*GUIWindow_Show)(void *addonData, GUIHANDLE handle); +typedef bool (*GUIWindow_Close)(void *addonData, GUIHANDLE handle); +typedef bool (*GUIWindow_DoModal)(void *addonData, GUIHANDLE handle); +typedef bool (*GUIWindow_SetFocusId)(void *addonData, GUIHANDLE handle, int iControlId); +typedef int (*GUIWindow_GetFocusId)(void *addonData, GUIHANDLE handle); +typedef bool (*GUIWindow_SetCoordinateResolution)(void *addonData, GUIHANDLE handle, int res); +typedef void (*GUIWindow_SetProperty)(void *addonData, GUIHANDLE handle, const char *key, const char *value); +typedef void (*GUIWindow_SetPropertyInt)(void *addonData, GUIHANDLE handle, const char *key, int value); +typedef void (*GUIWindow_SetPropertyBool)(void *addonData, GUIHANDLE handle, const char *key, bool value); +typedef void (*GUIWindow_SetPropertyDouble)(void *addonData, GUIHANDLE handle, const char *key, double value); +typedef const char* (*GUIWindow_GetProperty)(void *addonData, GUIHANDLE handle, const char *key); +typedef int (*GUIWindow_GetPropertyInt)(void *addonData, GUIHANDLE handle, const char *key); +typedef bool (*GUIWindow_GetPropertyBool)(void *addonData, GUIHANDLE handle, const char *key); +typedef double (*GUIWindow_GetPropertyDouble)(void *addonData, GUIHANDLE handle, const char *key); +typedef void (*GUIWindow_ClearProperties)(void *addonData, GUIHANDLE handle); +typedef int (*GUIWindow_GetListSize)(void *addonData, GUIHANDLE handle); +typedef void (*GUIWindow_ClearList)(void *addonData, GUIHANDLE handle); +typedef GUIHANDLE (*GUIWindow_AddItem)(void *addonData, GUIHANDLE handle, GUIHANDLE item, int itemPosition); +typedef GUIHANDLE (*GUIWindow_AddStringItem)(void *addonData, GUIHANDLE handle, const char *itemName, int itemPosition); +typedef void (*GUIWindow_RemoveItem)(void *addonData, GUIHANDLE handle, int itemPosition); +typedef GUIHANDLE (*GUIWindow_GetListItem)(void *addonData, GUIHANDLE handle, int listPos); +typedef void (*GUIWindow_SetCurrentListPosition)(void *addonData, GUIHANDLE handle, int listPos); +typedef int (*GUIWindow_GetCurrentListPosition)(void *addonData, GUIHANDLE handle); +typedef GUIHANDLE (*GUIWindow_GetControl_Spin)(void *addonData, GUIHANDLE handle, int controlId); +typedef GUIHANDLE (*GUIWindow_GetControl_Button)(void *addonData, GUIHANDLE handle, int controlId); +typedef GUIHANDLE (*GUIWindow_GetControl_RadioButton)(void *addonData, GUIHANDLE handle, int controlId); +typedef GUIHANDLE (*GUIWindow_GetControl_Edit)(void *addonData, GUIHANDLE handle, int controlId); +typedef GUIHANDLE (*GUIWindow_GetControl_Progress)(void *addonData, GUIHANDLE handle, int controlId); +typedef GUIHANDLE (*GUIWindow_GetControl_RenderAddon)(void *addonData, GUIHANDLE handle, int controlId); +typedef void (*GUIWindow_SetControlLabel)(void *addonData, GUIHANDLE handle, int controlId, const char *label); +typedef void (*GUIWindow_MarkDirtyRegion)(void *addonData, GUIHANDLE handle); +typedef void (*GUIControl_Spin_SetVisible)(void *addonData, GUIHANDLE spinhandle, bool yesNo); +typedef void (*GUIControl_Spin_SetText)(void *addonData, GUIHANDLE spinhandle, const char *label); +typedef void (*GUIControl_Spin_Clear)(void *addonData, GUIHANDLE spinhandle); +typedef void (*GUIControl_Spin_AddLabel)(void *addonData, GUIHANDLE spinhandle, const char *label, int iValue); +typedef int (*GUIControl_Spin_GetValue)(void *addonData, GUIHANDLE spinhandle); +typedef void (*GUIControl_Spin_SetValue)(void *addonData, GUIHANDLE spinhandle, int iValue); +typedef void (*GUIControl_RadioButton_SetVisible)(void *addonData, GUIHANDLE handle, bool yesNo); +typedef void (*GUIControl_RadioButton_SetText)(void *addonData, GUIHANDLE handle, const char *label); +typedef void (*GUIControl_RadioButton_SetSelected)(void *addonData, GUIHANDLE handle, bool yesNo); +typedef bool (*GUIControl_RadioButton_IsSelected)(void *addonData, GUIHANDLE handle); +typedef void (*GUIControl_Progress_SetPercentage)(void *addonData, GUIHANDLE handle, float fPercent); +typedef float (*GUIControl_Progress_GetPercentage)(void *addonData, GUIHANDLE handle); +typedef void (*GUIControl_Progress_SetInfo)(void *addonData, GUIHANDLE handle, int iInfo); +typedef int (*GUIControl_Progress_GetInfo)(void *addonData, GUIHANDLE handle); +typedef const char* (*GUIControl_Progress_GetDescription)(void *addonData, GUIHANDLE handle); +typedef GUIHANDLE (*GUIWindow_GetControl_Slider)(void *addonData, GUIHANDLE handle, int controlId); +typedef void (*GUIControl_Slider_SetVisible)(void *addonData, GUIHANDLE handle, bool yesNo); +typedef const char *(*GUIControl_Slider_GetDescription)(void *addonData, GUIHANDLE handle); +typedef void (*GUIControl_Slider_SetIntRange)(void *addonData, GUIHANDLE handle, int iStart, int iEnd); +typedef void (*GUIControl_Slider_SetIntValue)(void *addonData, GUIHANDLE handle, int iValue); +typedef int (*GUIControl_Slider_GetIntValue)(void *addonData, GUIHANDLE handle); +typedef void (*GUIControl_Slider_SetIntInterval)(void *addonData, GUIHANDLE handle, int iInterval); +typedef void (*GUIControl_Slider_SetPercentage)(void *addonData, GUIHANDLE handle, float fPercent); +typedef float (*GUIControl_Slider_GetPercentage)(void *addonData, GUIHANDLE handle); +typedef void (*GUIControl_Slider_SetFloatRange)(void *addonData, GUIHANDLE handle, float fStart, float fEnd); +typedef void (*GUIControl_Slider_SetFloatValue)(void *addonData, GUIHANDLE handle, float fValue); +typedef float (*GUIControl_Slider_GetFloatValue)(void *addonData, GUIHANDLE handle); +typedef void (*GUIControl_Slider_SetFloatInterval)(void *addonData, GUIHANDLE handle, float fInterval); +typedef GUIHANDLE (*GUIWindow_GetControl_SettingsSlider)(void *addonData, GUIHANDLE handle, int controlId); +typedef void (*GUIControl_SettingsSlider_SetVisible)(void *addonData, GUIHANDLE handle, bool yesNo); +typedef void (*GUIControl_SettingsSlider_SetText)(void *addonData, GUIHANDLE handle, const char *label); +typedef const char *(*GUIControl_SettingsSlider_GetDescription)(void *addonData, GUIHANDLE handle); +typedef void (*GUIControl_SettingsSlider_SetIntRange)(void *addonData, GUIHANDLE handle, int iStart, int iEnd); +typedef void (*GUIControl_SettingsSlider_SetIntValue)(void *addonData, GUIHANDLE handle, int iValue); +typedef int (*GUIControl_SettingsSlider_GetIntValue)(void *addonData, GUIHANDLE handle); +typedef void (*GUIControl_SettingsSlider_SetIntInterval)(void *addonData, GUIHANDLE handle, int iInterval); +typedef void (*GUIControl_SettingsSlider_SetPercentage)(void *addonData, GUIHANDLE handle, float fPercent); +typedef float (*GUIControl_SettingsSlider_GetPercentage)(void *addonData, GUIHANDLE handle); +typedef void (*GUIControl_SettingsSlider_SetFloatRange)(void *addonData, GUIHANDLE handle, float fStart, float fEnd); +typedef void (*GUIControl_SettingsSlider_SetFloatValue)(void *addonData, GUIHANDLE handle, float fValue); +typedef float (*GUIControl_SettingsSlider_GetFloatValue)(void *addonData, GUIHANDLE handle); +typedef void (*GUIControl_SettingsSlider_SetFloatInterval)(void *addonData, GUIHANDLE handle, float fInterval); +typedef GUIHANDLE (*GUIListItem_Create)(void *addonData, const char *label, const char *label2, const char *iconImage, const char *thumbnailImage, const char *path); +typedef const char* (*GUIListItem_GetLabel)(void *addonData, GUIHANDLE handle); +typedef void (*GUIListItem_SetLabel)(void *addonData, GUIHANDLE handle, const char *label); +typedef const char* (*GUIListItem_GetLabel2)(void *addonData, GUIHANDLE handle); +typedef void (*GUIListItem_SetLabel2)(void *addonData, GUIHANDLE handle, const char *label); +typedef void (*GUIListItem_SetIconImage)(void *addonData, GUIHANDLE handle, const char *image); +typedef void (*GUIListItem_SetThumbnailImage)(void *addonData, GUIHANDLE handle, const char *image); +typedef void (*GUIListItem_SetInfo)(void *addonData, GUIHANDLE handle, const char *info); +typedef void (*GUIListItem_SetProperty)(void *addonData, GUIHANDLE handle, const char *key, const char *value); +typedef const char* (*GUIListItem_GetProperty)(void *addonData, GUIHANDLE handle, const char *key); +typedef void (*GUIListItem_SetPath)(void *addonData, GUIHANDLE handle, const char *path); +typedef void (*GUIRenderAddon_SetCallbacks)(void *addonData, GUIHANDLE handle, GUIHANDLE clienthandle, bool (*createCB)(GUIHANDLE,int,int,int,int,void*), void (*renderCB)(GUIHANDLE), void (*stopCB)(GUIHANDLE), bool (*dirtyCB)(GUIHANDLE)); +typedef void (*GUIRenderAddon_Delete)(void *addonData, GUIHANDLE handle); +typedef void (*GUIRenderAddon_MarkDirty)(void *addonData, GUIHANDLE handle); + +typedef bool (*GUIDialog_Keyboard_ShowAndGetInputWithHead)(char &strTextString, unsigned int iMaxStringSize, const char *heading, bool allowEmptyResult, bool hiddenInput, unsigned int autoCloseMs); +typedef bool (*GUIDialog_Keyboard_ShowAndGetInput)(char &strTextString, unsigned int iMaxStringSize, bool allowEmptyResult, unsigned int autoCloseMs); +typedef bool (*GUIDialog_Keyboard_ShowAndGetNewPasswordWithHead)(char &newPassword, unsigned int iMaxStringSize, const char *strHeading, bool allowEmptyResult, unsigned int autoCloseMs); +typedef bool (*GUIDialog_Keyboard_ShowAndGetNewPassword)(char &strNewPassword, unsigned int iMaxStringSize, unsigned int autoCloseMs); +typedef bool (*GUIDialog_Keyboard_ShowAndVerifyNewPasswordWithHead)(char &strNewPassword, unsigned int iMaxStringSize, const char *strHeading, bool allowEmpty, unsigned int autoCloseMs); +typedef bool (*GUIDialog_Keyboard_ShowAndVerifyNewPassword)(char &strNewPassword, unsigned int iMaxStringSize, unsigned int autoCloseMs); +typedef int (*GUIDialog_Keyboard_ShowAndVerifyPassword)(char &strPassword, unsigned int iMaxStringSize, const char *strHeading, int iRetries, unsigned int autoCloseMs); +typedef bool (*GUIDialog_Keyboard_ShowAndGetFilter)(char &aTextString, unsigned int iMaxStringSize, bool searching, unsigned int autoCloseMs); +typedef bool (*GUIDialog_Keyboard_SendTextToActiveKeyboard)(const char *aTextString, bool closeKeyboard); +typedef bool (*GUIDialog_Keyboard_isKeyboardActivated)(); + +typedef bool (*GUIDialog_Numeric_ShowAndVerifyNewPassword)(char &strNewPassword, unsigned int iMaxStringSize); +typedef int (*GUIDialog_Numeric_ShowAndVerifyPassword)(char &strPassword, unsigned int iMaxStringSize, const char *strHeading, int iRetries); +typedef bool (*GUIDialog_Numeric_ShowAndVerifyInput)(char &strPassword, unsigned int iMaxStringSize, const char *strHeading, bool bGetUserInput); +typedef bool (*GUIDialog_Numeric_ShowAndGetTime)(tm &time, const char *strHeading); +typedef bool (*GUIDialog_Numeric_ShowAndGetDate)(tm &date, const char *strHeading); +typedef bool (*GUIDialog_Numeric_ShowAndGetIPAddress)(char &strIPAddress, unsigned int iMaxStringSize, const char *strHeading); +typedef bool (*GUIDialog_Numeric_ShowAndGetNumber)(char &strInput, unsigned int iMaxStringSize, const char *strHeading, unsigned int iAutoCloseTimeoutMs); +typedef bool (*GUIDialog_Numeric_ShowAndGetSeconds)(char &timeString, unsigned int iMaxStringSize, const char *strHeading); + +typedef bool (*GUIDialog_FileBrowser_ShowAndGetFile)(const char *directory, const char *mask, const char *heading, char &path, unsigned int iMaxStringSize, bool useThumbs, bool useFileDirectories, bool singleList); + +typedef void (*GUIDialog_OK_ShowAndGetInputSingleText)(const char *heading, const char *text); +typedef void (*GUIDialog_OK_ShowAndGetInputLineText)(const char *heading, const char *line0, const char *line1, const char *line2); + +typedef bool (*GUIDialog_YesNo_ShowAndGetInputSingleText)(const char *heading, const char *text, bool& bCanceled, const char *noLabel, const char *yesLabel); +typedef bool (*GUIDialog_YesNo_ShowAndGetInputLineText)(const char *heading, const char *line0, const char *line1, const char *line2, const char *noLabel, const char *yesLabel); +typedef bool (*GUIDialog_YesNo_ShowAndGetInputLineButtonText)(const char *heading, const char *line0, const char *line1, const char *line2, bool &bCanceled, const char *noLabel, const char *yesLabel); + +typedef void (*GUIDialog_TextViewer)(const char *heading, const char *text); + +typedef int (*GUIDialog_Select)(const char *heading, const char *entries[], unsigned int size, int selected); + +typedef struct CB_GUILib +{ + GUILock Lock; + GUIUnlock Unlock; + GUIGetScreenHeight GetScreenHeight; + GUIGetScreenWidth GetScreenWidth; + GUIGetVideoResolution GetVideoResolution; + GUIWindow_New Window_New; + GUIWindow_Delete Window_Delete; + GUIWindow_SetCallbacks Window_SetCallbacks; + GUIWindow_Show Window_Show; + GUIWindow_Close Window_Close; + GUIWindow_DoModal Window_DoModal; + GUIWindow_SetFocusId Window_SetFocusId; + GUIWindow_GetFocusId Window_GetFocusId; + GUIWindow_SetCoordinateResolution Window_SetCoordinateResolution; + GUIWindow_SetProperty Window_SetProperty; + GUIWindow_SetPropertyInt Window_SetPropertyInt; + GUIWindow_SetPropertyBool Window_SetPropertyBool; + GUIWindow_SetPropertyDouble Window_SetPropertyDouble; + GUIWindow_GetProperty Window_GetProperty; + GUIWindow_GetPropertyInt Window_GetPropertyInt; + GUIWindow_GetPropertyBool Window_GetPropertyBool; + GUIWindow_GetPropertyDouble Window_GetPropertyDouble; + GUIWindow_ClearProperties Window_ClearProperties; + GUIWindow_GetListSize Window_GetListSize; + GUIWindow_ClearList Window_ClearList; + GUIWindow_AddItem Window_AddItem; + GUIWindow_AddStringItem Window_AddStringItem; + GUIWindow_RemoveItem Window_RemoveItem; + GUIWindow_GetListItem Window_GetListItem; + GUIWindow_SetCurrentListPosition Window_SetCurrentListPosition; + GUIWindow_GetCurrentListPosition Window_GetCurrentListPosition; + GUIWindow_GetControl_Spin Window_GetControl_Spin; + GUIWindow_GetControl_Button Window_GetControl_Button; + GUIWindow_GetControl_RadioButton Window_GetControl_RadioButton; + GUIWindow_GetControl_Edit Window_GetControl_Edit; + GUIWindow_GetControl_Progress Window_GetControl_Progress; + GUIWindow_GetControl_RenderAddon Window_GetControl_RenderAddon; + GUIWindow_SetControlLabel Window_SetControlLabel; + GUIWindow_MarkDirtyRegion Window_MarkDirtyRegion; + GUIControl_Spin_SetVisible Control_Spin_SetVisible; + GUIControl_Spin_SetText Control_Spin_SetText; + GUIControl_Spin_Clear Control_Spin_Clear; + GUIControl_Spin_AddLabel Control_Spin_AddLabel; + GUIControl_Spin_GetValue Control_Spin_GetValue; + GUIControl_Spin_SetValue Control_Spin_SetValue; + GUIControl_RadioButton_SetVisible Control_RadioButton_SetVisible; + GUIControl_RadioButton_SetText Control_RadioButton_SetText; + GUIControl_RadioButton_SetSelected Control_RadioButton_SetSelected; + GUIControl_RadioButton_IsSelected Control_RadioButton_IsSelected; + GUIControl_Progress_SetPercentage Control_Progress_SetPercentage; + GUIControl_Progress_GetPercentage Control_Progress_GetPercentage; + GUIControl_Progress_SetInfo Control_Progress_SetInfo; + GUIControl_Progress_GetInfo Control_Progress_GetInfo; + GUIControl_Progress_GetDescription Control_Progress_GetDescription; + GUIListItem_Create ListItem_Create; + GUIListItem_GetLabel ListItem_GetLabel; + GUIListItem_SetLabel ListItem_SetLabel; + GUIListItem_GetLabel2 ListItem_GetLabel2; + GUIListItem_SetLabel2 ListItem_SetLabel2; + GUIListItem_SetIconImage ListItem_SetIconImage; + GUIListItem_SetThumbnailImage ListItem_SetThumbnailImage; + GUIListItem_SetInfo ListItem_SetInfo; + GUIListItem_SetProperty ListItem_SetProperty; + GUIListItem_GetProperty ListItem_GetProperty; + GUIListItem_SetPath ListItem_SetPath; + GUIRenderAddon_SetCallbacks RenderAddon_SetCallbacks; + GUIRenderAddon_Delete RenderAddon_Delete; + + GUIWindow_GetControl_Slider Window_GetControl_Slider; + GUIControl_Slider_SetVisible Control_Slider_SetVisible; + GUIControl_Slider_GetDescription Control_Slider_GetDescription; + GUIControl_Slider_SetIntRange Control_Slider_SetIntRange; + GUIControl_Slider_SetIntValue Control_Slider_SetIntValue; + GUIControl_Slider_GetIntValue Control_Slider_GetIntValue; + GUIControl_Slider_SetIntInterval Control_Slider_SetIntInterval; + GUIControl_Slider_SetPercentage Control_Slider_SetPercentage; + GUIControl_Slider_GetPercentage Control_Slider_GetPercentage; + GUIControl_Slider_SetFloatRange Control_Slider_SetFloatRange; + GUIControl_Slider_SetFloatValue Control_Slider_SetFloatValue; + GUIControl_Slider_GetFloatValue Control_Slider_GetFloatValue; + GUIControl_Slider_SetFloatInterval Control_Slider_SetFloatInterval; + + GUIWindow_GetControl_SettingsSlider Window_GetControl_SettingsSlider; + GUIControl_SettingsSlider_SetVisible Control_SettingsSlider_SetVisible; + GUIControl_SettingsSlider_SetText Control_SettingsSlider_SetText; + GUIControl_SettingsSlider_GetDescription Control_SettingsSlider_GetDescription; + GUIControl_SettingsSlider_SetIntRange Control_SettingsSlider_SetIntRange; + GUIControl_SettingsSlider_SetIntValue Control_SettingsSlider_SetIntValue; + GUIControl_SettingsSlider_GetIntValue Control_SettingsSlider_GetIntValue; + GUIControl_SettingsSlider_SetIntInterval Control_SettingsSlider_SetIntInterval; + GUIControl_SettingsSlider_SetPercentage Control_SettingsSlider_SetPercentage; + GUIControl_SettingsSlider_GetPercentage Control_SettingsSlider_GetPercentage; + GUIControl_SettingsSlider_SetFloatRange Control_SettingsSlider_SetFloatRange; + GUIControl_SettingsSlider_SetFloatValue Control_SettingsSlider_SetFloatValue; + GUIControl_SettingsSlider_GetFloatValue Control_SettingsSlider_GetFloatValue; + GUIControl_SettingsSlider_SetFloatInterval Control_SettingsSlider_SetFloatInterval; + + GUIDialog_Keyboard_ShowAndGetInputWithHead Dialog_Keyboard_ShowAndGetInputWithHead; + GUIDialog_Keyboard_ShowAndGetInput Dialog_Keyboard_ShowAndGetInput; + GUIDialog_Keyboard_ShowAndGetNewPasswordWithHead Dialog_Keyboard_ShowAndGetNewPasswordWithHead; + GUIDialog_Keyboard_ShowAndGetNewPassword Dialog_Keyboard_ShowAndGetNewPassword; + GUIDialog_Keyboard_ShowAndVerifyNewPasswordWithHead Dialog_Keyboard_ShowAndVerifyNewPasswordWithHead; + GUIDialog_Keyboard_ShowAndVerifyNewPassword Dialog_Keyboard_ShowAndVerifyNewPassword; + GUIDialog_Keyboard_ShowAndVerifyPassword Dialog_Keyboard_ShowAndVerifyPassword; + GUIDialog_Keyboard_ShowAndGetFilter Dialog_Keyboard_ShowAndGetFilter; + GUIDialog_Keyboard_SendTextToActiveKeyboard Dialog_Keyboard_SendTextToActiveKeyboard; + GUIDialog_Keyboard_isKeyboardActivated Dialog_Keyboard_isKeyboardActivated; + + GUIDialog_Numeric_ShowAndVerifyNewPassword Dialog_Numeric_ShowAndVerifyNewPassword; + GUIDialog_Numeric_ShowAndVerifyPassword Dialog_Numeric_ShowAndVerifyPassword; + GUIDialog_Numeric_ShowAndVerifyInput Dialog_Numeric_ShowAndVerifyInput; + GUIDialog_Numeric_ShowAndGetTime Dialog_Numeric_ShowAndGetTime; + GUIDialog_Numeric_ShowAndGetDate Dialog_Numeric_ShowAndGetDate; + GUIDialog_Numeric_ShowAndGetIPAddress Dialog_Numeric_ShowAndGetIPAddress; + GUIDialog_Numeric_ShowAndGetNumber Dialog_Numeric_ShowAndGetNumber; + GUIDialog_Numeric_ShowAndGetSeconds Dialog_Numeric_ShowAndGetSeconds; + + GUIDialog_FileBrowser_ShowAndGetFile Dialog_FileBrowser_ShowAndGetFile; + + GUIDialog_OK_ShowAndGetInputSingleText Dialog_OK_ShowAndGetInputSingleText; + GUIDialog_OK_ShowAndGetInputLineText Dialog_OK_ShowAndGetInputLineText; + + GUIDialog_YesNo_ShowAndGetInputSingleText Dialog_YesNo_ShowAndGetInputSingleText; + GUIDialog_YesNo_ShowAndGetInputLineText Dialog_YesNo_ShowAndGetInputLineText; + GUIDialog_YesNo_ShowAndGetInputLineButtonText Dialog_YesNo_ShowAndGetInputLineButtonText; + + GUIDialog_TextViewer Dialog_TextViewer; + GUIDialog_Select Dialog_Select; +} CB_GUILib; + +typedef void (*PVRTransferEpgEntry)(void *userData, const ADDON_HANDLE handle, const EPG_TAG *epgentry); +typedef void (*PVRTransferChannelEntry)(void *userData, const ADDON_HANDLE handle, const PVR_CHANNEL *chan); +typedef void (*PVRTransferTimerEntry)(void *userData, const ADDON_HANDLE handle, const PVR_TIMER *timer); +typedef void (*PVRTransferRecordingEntry)(void *userData, const ADDON_HANDLE handle, const PVR_RECORDING *recording); +typedef void (*PVRAddMenuHook)(void *addonData, PVR_MENUHOOK *hook); +typedef void (*PVRRecording)(void *addonData, const char *Name, const char *FileName, bool On); +typedef void (*PVRTriggerChannelUpdate)(void *addonData); +typedef void (*PVRTriggerTimerUpdate)(void *addonData); +typedef void (*PVRTriggerRecordingUpdate)(void *addonData); +typedef void (*PVRTriggerChannelGroupsUpdate)(void *addonData); +typedef void (*PVRTriggerEpgUpdate)(void *addonData, unsigned int iChannelUid); + +typedef void (*PVRTransferChannelGroup)(void *addonData, const ADDON_HANDLE handle, const PVR_CHANNEL_GROUP *group); +typedef void (*PVRTransferChannelGroupMember)(void *addonData, const ADDON_HANDLE handle, const PVR_CHANNEL_GROUP_MEMBER *member); + +typedef void (*PVRFreeDemuxPacket)(void *addonData, DemuxPacket* pPacket); +typedef DemuxPacket* (*PVRAllocateDemuxPacket)(void *addonData, int iDataSize); + +typedef struct CB_PVRLib +{ + PVRTransferEpgEntry TransferEpgEntry; + PVRTransferChannelEntry TransferChannelEntry; + PVRTransferTimerEntry TransferTimerEntry; + PVRTransferRecordingEntry TransferRecordingEntry; + PVRAddMenuHook AddMenuHook; + PVRRecording Recording; + PVRTriggerChannelUpdate TriggerChannelUpdate; + PVRTriggerTimerUpdate TriggerTimerUpdate; + PVRTriggerRecordingUpdate TriggerRecordingUpdate; + PVRTriggerChannelGroupsUpdate TriggerChannelGroupsUpdate; + PVRTriggerEpgUpdate TriggerEpgUpdate; + PVRFreeDemuxPacket FreeDemuxPacket; + PVRAllocateDemuxPacket AllocateDemuxPacket; + PVRTransferChannelGroup TransferChannelGroup; + PVRTransferChannelGroupMember TransferChannelGroupMember; + +} CB_PVRLib; + + +typedef CB_AddOnLib* (*XBMCAddOnLib_RegisterMe)(void *addonData); +typedef void (*XBMCAddOnLib_UnRegisterMe)(void *addonData, CB_AddOnLib *cbTable); +typedef CB_CODECLib* (*XBMCCODECLib_RegisterMe)(void *addonData); +typedef void (*XBMCCODECLib_UnRegisterMe)(void *addonData, CB_CODECLib *cbTable); +typedef CB_GUILib* (*XBMCGUILib_RegisterMe)(void *addonData); +typedef void (*XBMCGUILib_UnRegisterMe)(void *addonData, CB_GUILib *cbTable); +typedef CB_PVRLib* (*XBMCPVRLib_RegisterMe)(void *addonData); +typedef void (*XBMCPVRLib_UnRegisterMe)(void *addonData, CB_PVRLib *cbTable); + +typedef struct AddonCB +{ + const char *libBasePath; ///> Never, never change this!!! + void *addonData; + XBMCAddOnLib_RegisterMe AddOnLib_RegisterMe; + XBMCAddOnLib_UnRegisterMe AddOnLib_UnRegisterMe; + XBMCCODECLib_RegisterMe CODECLib_RegisterMe; + XBMCCODECLib_UnRegisterMe CODECLib_UnRegisterMe; + XBMCGUILib_RegisterMe GUILib_RegisterMe; + XBMCGUILib_UnRegisterMe GUILib_UnRegisterMe; + XBMCPVRLib_RegisterMe PVRLib_RegisterMe; + XBMCPVRLib_UnRegisterMe PVRLib_UnRegisterMe; +} AddonCB; + + +namespace ADDON +{ + +class CAddon; +class CAddonCallbacksAddon; +class CAddonCallbacksCodec; +class CAddonCallbacksGUI; +class CAddonCallbacksPVR; + +class CAddonCallbacks +{ +public: + CAddonCallbacks(CAddon* addon); + ~CAddonCallbacks(); + AddonCB *GetCallbacks() { return m_callbacks; } + + static CB_AddOnLib* AddOnLib_RegisterMe(void *addonData); + static void AddOnLib_UnRegisterMe(void *addonData, CB_AddOnLib *cbTable); + static CB_CODECLib* CODECLib_RegisterMe(void *addonData); + static void CODECLib_UnRegisterMe(void *addonData, CB_CODECLib *cbTable); + static CB_GUILib* GUILib_RegisterMe(void *addonData); + static void GUILib_UnRegisterMe(void *addonData, CB_GUILib *cbTable); + static CB_PVRLib* PVRLib_RegisterMe(void *addonData); + static void PVRLib_UnRegisterMe(void *addonData, CB_PVRLib *cbTable); + + CAddonCallbacksAddon *GetHelperAddon() { return m_helperAddon; } + CAddonCallbacksCodec *GetHelperCodec() { return m_helperCODEC; } + CAddonCallbacksGUI *GetHelperGUI() { return m_helperGUI; } + CAddonCallbacksPVR *GetHelperPVR() { return m_helperPVR; } + +private: + AddonCB *m_callbacks; + CAddon *m_addon; + CAddonCallbacksAddon *m_helperAddon; + CAddonCallbacksCodec *m_helperCODEC; + CAddonCallbacksGUI *m_helperGUI; + CAddonCallbacksPVR *m_helperPVR; +}; + +}; /* namespace ADDON */ diff --git a/xbmc/addons/AddonCallbacksAddon.cpp b/xbmc/addons/AddonCallbacksAddon.cpp new file mode 100644 index 0000000..e54aebe --- /dev/null +++ b/xbmc/addons/AddonCallbacksAddon.cpp @@ -0,0 +1,532 @@ +/* + * Copyright (C) 2012-2013 Team XBMC + * http://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, see + * . + * + */ + +#include "Application.h" +#include "Addon.h" +#include "AddonCallbacksAddon.h" +#include "utils/log.h" +#include "LangInfo.h" +#include "dialogs/GUIDialogKaiToast.h" +#include "filesystem/File.h" +#include "filesystem/Directory.h" +#include "utils/URIUtils.h" +#include "FileItem.h" +#include "network/Network.h" +#include "utils/CharsetConverter.h" +#include "utils/StringUtils.h" +#include "utils/XMLUtils.h" +#include "cores/dvdplayer/DVDCodecs/DVDCodecs.h" + +using namespace XFILE; + +namespace ADDON +{ + +CAddonCallbacksAddon::CAddonCallbacksAddon(CAddon* addon) +{ + m_addon = addon; + m_callbacks = new CB_AddOnLib; + + /* write XBMC addon-on specific add-on function addresses to the callback table */ + m_callbacks->Log = AddOnLog; + m_callbacks->QueueNotification = QueueNotification; + m_callbacks->WakeOnLan = WakeOnLan; + m_callbacks->GetSetting = GetAddonSetting; + m_callbacks->UnknownToUTF8 = UnknownToUTF8; + m_callbacks->GetLocalizedString = GetLocalizedString; + m_callbacks->GetDVDMenuLanguage = GetDVDMenuLanguage; + m_callbacks->FreeString = FreeString; + + m_callbacks->OpenFile = OpenFile; + m_callbacks->OpenFileForWrite = OpenFileForWrite; + m_callbacks->ReadFile = ReadFile; + m_callbacks->ReadFileString = ReadFileString; + m_callbacks->WriteFile = WriteFile; + m_callbacks->FlushFile = FlushFile; + m_callbacks->SeekFile = SeekFile; + m_callbacks->TruncateFile = TruncateFile; + m_callbacks->GetFilePosition = GetFilePosition; + m_callbacks->GetFileLength = GetFileLength; + m_callbacks->CloseFile = CloseFile; + m_callbacks->GetFileChunkSize = GetFileChunkSize; + m_callbacks->FileExists = FileExists; + m_callbacks->StatFile = StatFile; + m_callbacks->DeleteFile = DeleteFile; + + m_callbacks->CanOpenDirectory = CanOpenDirectory; + m_callbacks->CreateDirectory = CreateDirectory; + m_callbacks->DirectoryExists = DirectoryExists; + m_callbacks->RemoveDirectory = RemoveDirectory; +} + +CAddonCallbacksAddon::~CAddonCallbacksAddon() +{ + /* delete the callback table */ + delete m_callbacks; +} + +void CAddonCallbacksAddon::AddOnLog(void *addonData, const addon_log_t addonLogLevel, const char *strMessage) +{ + CAddonCallbacks* addon = (CAddonCallbacks*) addonData; + if (addon == NULL || strMessage == NULL) + { + CLog::Log(LOGERROR, "CAddonCallbacksAddon - %s - called with a null pointer", __FUNCTION__); + return; + } + + CAddonCallbacksAddon* addonHelper = addon->GetHelperAddon(); + + try + { + int xbmcLogLevel = LOGNONE; + switch (addonLogLevel) + { + case LOG_ERROR: + xbmcLogLevel = LOGERROR; + break; + case LOG_INFO: + xbmcLogLevel = LOGINFO; + break; + case LOG_NOTICE: + xbmcLogLevel = LOGNOTICE; + break; + case LOG_DEBUG: + default: + xbmcLogLevel = LOGDEBUG; + break; + } + + std::string strXbmcMessage = StringUtils::Format("AddOnLog: %s: %s", addonHelper->m_addon->Name().c_str(), strMessage); + CLog::Log(xbmcLogLevel, "%s", strXbmcMessage.c_str()); + } + catch (std::exception &e) + { + CLog::Log(LOGERROR, "CAddonCallbacksAddon - %s - exception '%s' caught in call in add-on '%s'. please contact the developer of this addon: %s", + __FUNCTION__, e.what(), addonHelper->m_addon->Name().c_str(), addonHelper->m_addon->Author().c_str()); + } +} + +void CAddonCallbacksAddon::QueueNotification(void *addonData, const queue_msg_t type, const char *strMessage) +{ + CAddonCallbacks* addon = (CAddonCallbacks*) addonData; + if (addon == NULL || strMessage == NULL) + { + CLog::Log(LOGERROR, "CAddonCallbacksAddon - %s - called with a null pointer", __FUNCTION__); + return; + } + + CAddonCallbacksAddon* addonHelper = addon->GetHelperAddon(); + + try + { + switch (type) + { + case QUEUE_WARNING: + CGUIDialogKaiToast::QueueNotification(CGUIDialogKaiToast::Warning, addonHelper->m_addon->Name(), strMessage, 3000, true); + CLog::Log(LOGDEBUG, "CAddonCallbacksAddon - %s - %s - Warning Message: '%s'", __FUNCTION__, addonHelper->m_addon->Name().c_str(), strMessage); + break; + + case QUEUE_ERROR: + CGUIDialogKaiToast::QueueNotification(CGUIDialogKaiToast::Error, addonHelper->m_addon->Name(), strMessage, 3000, true); + CLog::Log(LOGDEBUG, "CAddonCallbacksAddon - %s - %s - Error Message : '%s'", __FUNCTION__, addonHelper->m_addon->Name().c_str(), strMessage); + break; + + case QUEUE_INFO: + default: + CGUIDialogKaiToast::QueueNotification(CGUIDialogKaiToast::Info, addonHelper->m_addon->Name(), strMessage, 3000, false); + CLog::Log(LOGDEBUG, "CAddonCallbacksAddon - %s - %s - Info Message : '%s'", __FUNCTION__, addonHelper->m_addon->Name().c_str(), strMessage); + break; + } + } + catch (std::exception &e) + { + CLog::Log(LOGERROR, "CAddonCallbacksAddon - %s - exception '%s' caught in call in add-on '%s'. please contact the developer of this addon: %s", + __FUNCTION__, e.what(), addonHelper->m_addon->Name().c_str(), addonHelper->m_addon->Author().c_str()); + } +} + +bool CAddonCallbacksAddon::WakeOnLan(const char *mac) +{ + return g_application.getNetwork().WakeOnLan(mac); +} + +bool CAddonCallbacksAddon::GetAddonSetting(void *addonData, const char *strSettingName, void *settingValue) +{ + CAddonCallbacks* addon = (CAddonCallbacks*) addonData; + if (addon == NULL || strSettingName == NULL || settingValue == NULL) + { + CLog::Log(LOGERROR, "CAddonCallbacksAddon - %s - called with a null pointer", __FUNCTION__); + return false; + } + + CAddonCallbacksAddon* addonHelper = addon->GetHelperAddon(); + + try + { + CLog::Log(LOGDEBUG, "CAddonCallbacksAddon - %s - add-on '%s' requests setting '%s'", __FUNCTION__, addonHelper->m_addon->Name().c_str(), strSettingName); + + if (!addonHelper->m_addon->ReloadSettings()) + { + CLog::Log(LOGERROR, "CAddonCallbacksAddon - %s - could't get settings for add-on '%s'", __FUNCTION__, addonHelper->m_addon->Name().c_str()); + return false; + } + + const TiXmlElement *category = addonHelper->m_addon->GetSettingsXML()->FirstChildElement("category"); + if (!category) // add a default one... + category = addonHelper->m_addon->GetSettingsXML(); + + while (category) + { + const TiXmlElement *setting = category->FirstChildElement("setting"); + while (setting) + { + const std::string id = XMLUtils::GetAttribute(setting, "id"); + const std::string type = XMLUtils::GetAttribute(setting, "type"); + + if (id == strSettingName && !type.empty()) + { + if (type == "text" || type == "ipaddress" || + type == "folder" || type == "action" || + type == "music" || type == "pictures" || + type == "programs" || type == "fileenum" || + type == "file") + { + strcpy((char*) settingValue, addonHelper->m_addon->GetSetting(id).c_str()); + return true; + } + else if (type == "number" || type == "enum" || + type == "labelenum") + { + *(int*) settingValue = (int) atoi(addonHelper->m_addon->GetSetting(id).c_str()); + return true; + } + else if (type == "bool") + { + *(bool*) settingValue = (bool) (addonHelper->m_addon->GetSetting(id) == "true" ? true : false); + return true; + } + else if (type == "slider") + { + const char *option = setting->Attribute("option"); + if (option && strcmpi(option, "int") == 0) + { + *(int*) settingValue = (int) atoi(addonHelper->m_addon->GetSetting(id).c_str()); + return true; + } + else + { + *(float*) settingValue = (float) atof(addonHelper->m_addon->GetSetting(id).c_str()); + return true; + } + } + } + setting = setting->NextSiblingElement("setting"); + } + category = category->NextSiblingElement("category"); + } + CLog::Log(LOGERROR, "CAddonCallbacksAddon - %s - can't find setting '%s' in '%s'", __FUNCTION__, strSettingName, addonHelper->m_addon->Name().c_str()); + } + catch (std::exception &e) + { + CLog::Log(LOGERROR, "CAddonCallbacksAddon - %s - exception '%s' caught in call in add-on '%s'. please contact the developer of this addon: %s", + __FUNCTION__, e.what(), addonHelper->m_addon->Name().c_str(), addonHelper->m_addon->Author().c_str()); + } + + return false; +} + +char* CAddonCallbacksAddon::UnknownToUTF8(const char *strSource) +{ + std::string string; + if (strSource != NULL) + g_charsetConverter.unknownToUTF8(strSource, string); + else + string = ""; + char* buffer = strdup(string.c_str()); + return buffer; +} + +char* CAddonCallbacksAddon::GetLocalizedString(const void* addonData, long dwCode) +{ + CAddonCallbacks* helper = (CAddonCallbacks*) addonData; + if (!helper || g_application.m_bStop) + return NULL; + + CAddonCallbacksAddon* addonHelper = helper->GetHelperAddon(); + + std::string string; + if (dwCode >= 30000 && dwCode <= 30999) + string = addonHelper->m_addon->GetString(dwCode).c_str(); + else if (dwCode >= 32000 && dwCode <= 32999) + string = addonHelper->m_addon->GetString(dwCode).c_str(); + else + string = g_localizeStrings.Get(dwCode).c_str(); + + char* buffer = strdup(string.c_str()); + return buffer; +} + +char* CAddonCallbacksAddon::GetDVDMenuLanguage(const void* addonData) +{ + CAddonCallbacks* helper = (CAddonCallbacks*) addonData; + if (!helper) + return NULL; + + std::string string = g_langInfo.GetDVDMenuLanguage(); + + char* buffer = strdup(string.c_str()); + return buffer; +} + +void CAddonCallbacksAddon::FreeString(const void* addonData, char* str) +{ + free(str); +} + +void* CAddonCallbacksAddon::OpenFile(const void* addonData, const char* strFileName, unsigned int flags) +{ + CAddonCallbacks* helper = (CAddonCallbacks*) addonData; + if (!helper) + return NULL; + + CFile* file = new CFile; + if (file->Open(strFileName, flags)) + return ((void*)file); + + delete file; + return NULL; +} + +void* CAddonCallbacksAddon::OpenFileForWrite(const void* addonData, const char* strFileName, bool bOverwrite) +{ + CAddonCallbacks* helper = (CAddonCallbacks*) addonData; + if (!helper) + return NULL; + + CFile* file = new CFile; + if (file->OpenForWrite(strFileName, bOverwrite)) + return ((void*)file); + + delete file; + return NULL; +} + +ssize_t CAddonCallbacksAddon::ReadFile(const void* addonData, void* file, void* lpBuf, size_t uiBufSize) +{ + CAddonCallbacks* helper = (CAddonCallbacks*) addonData; + if (!helper) + return 0; + + CFile* cfile = (CFile*)file; + if (!cfile) + return 0; + + return cfile->Read(lpBuf, uiBufSize); +} + +bool CAddonCallbacksAddon::ReadFileString(const void* addonData, void* file, char *szLine, int iLineLength) +{ + CAddonCallbacks* helper = (CAddonCallbacks*) addonData; + if (!helper) + return false; + + CFile* cfile = (CFile*)file; + if (!cfile) + return false; + + return cfile->ReadString(szLine, iLineLength); +} + +ssize_t CAddonCallbacksAddon::WriteFile(const void* addonData, void* file, const void* lpBuf, size_t uiBufSize) +{ + CAddonCallbacks* helper = (CAddonCallbacks*) addonData; + if (!helper) + return -1; + + CFile* cfile = (CFile*)file; + if (!cfile) + return -1; + + return cfile->Write(lpBuf, uiBufSize); +} + +void CAddonCallbacksAddon::FlushFile(const void* addonData, void* file) +{ + CAddonCallbacks* helper = (CAddonCallbacks*) addonData; + if (!helper) + return; + + CFile* cfile = (CFile*)file; + if (!cfile) + return; + + cfile->Flush(); +} + +int64_t CAddonCallbacksAddon::SeekFile(const void* addonData, void* file, int64_t iFilePosition, int iWhence) +{ + CAddonCallbacks* helper = (CAddonCallbacks*) addonData; + if (!helper) + return 0; + + CFile* cfile = (CFile*)file; + if (!cfile) + return 0; + + return cfile->Seek(iFilePosition, iWhence); +} + +int CAddonCallbacksAddon::TruncateFile(const void* addonData, void* file, int64_t iSize) +{ + CAddonCallbacks* helper = (CAddonCallbacks*) addonData; + if (!helper) + return 0; + + CFile* cfile = (CFile*)file; + if (!cfile) + return 0; + + return cfile->Truncate(iSize); +} + +int64_t CAddonCallbacksAddon::GetFilePosition(const void* addonData, void* file) +{ + CAddonCallbacks* helper = (CAddonCallbacks*) addonData; + if (!helper) + return 0; + + CFile* cfile = (CFile*)file; + if (!cfile) + return 0; + + return cfile->GetPosition(); +} + +int64_t CAddonCallbacksAddon::GetFileLength(const void* addonData, void* file) +{ + CAddonCallbacks* helper = (CAddonCallbacks*) addonData; + if (!helper) + return 0; + + CFile* cfile = (CFile*)file; + if (!cfile) + return 0; + + return cfile->GetLength(); +} + +void CAddonCallbacksAddon::CloseFile(const void* addonData, void* file) +{ + CAddonCallbacks* helper = (CAddonCallbacks*) addonData; + if (!helper) + return; + + CFile* cfile = (CFile*)file; + if (cfile) + { + cfile->Close(); + delete cfile; + } +} + +int CAddonCallbacksAddon::GetFileChunkSize(const void* addonData, void* file) +{ + CAddonCallbacks* helper = (CAddonCallbacks*) addonData; + if (!helper) + return 0; + + CFile* cfile = (CFile*)file; + if (!cfile) + return 0; + + return cfile->GetChunkSize(); +} + +bool CAddonCallbacksAddon::FileExists(const void* addonData, const char *strFileName, bool bUseCache) +{ + CAddonCallbacks* helper = (CAddonCallbacks*) addonData; + if (!helper) + return false; + + return CFile::Exists(strFileName, bUseCache); +} + +int CAddonCallbacksAddon::StatFile(const void* addonData, const char *strFileName, struct __stat64* buffer) +{ + CAddonCallbacks* helper = (CAddonCallbacks*) addonData; + if (!helper) + return -1; + + return CFile::Stat(strFileName, buffer); +} + +bool CAddonCallbacksAddon::DeleteFile(const void* addonData, const char *strFileName) +{ + CAddonCallbacks* helper = (CAddonCallbacks*) addonData; + if (!helper) + return false; + + return CFile::Delete(strFileName); +} + +bool CAddonCallbacksAddon::CanOpenDirectory(const void* addonData, const char* strURL) +{ + CAddonCallbacks* helper = (CAddonCallbacks*) addonData; + if (!helper) + return false; + + CFileItemList items; + return CDirectory::GetDirectory(strURL, items); +} + +bool CAddonCallbacksAddon::CreateDirectory(const void* addonData, const char *strPath) +{ + CAddonCallbacks* helper = (CAddonCallbacks*) addonData; + if (!helper) + return false; + + return CDirectory::Create(strPath); +} + +bool CAddonCallbacksAddon::DirectoryExists(const void* addonData, const char *strPath) +{ + CAddonCallbacks* helper = (CAddonCallbacks*) addonData; + if (!helper) + return false; + + return CDirectory::Exists(strPath); +} + +bool CAddonCallbacksAddon::RemoveDirectory(const void* addonData, const char *strPath) +{ + CAddonCallbacks* helper = (CAddonCallbacks*) addonData; + if (!helper) + return false; + + // Empty directory + CFileItemList fileItems; + CDirectory::GetDirectory(strPath, fileItems); + for (int i = 0; i < fileItems.Size(); ++i) + CFile::Delete(fileItems.Get(i)->GetPath()); + + return CDirectory::Remove(strPath); +} + +}; /* namespace ADDON */ diff --git a/xbmc/addons/AddonCallbacksAddon.h b/xbmc/addons/AddonCallbacksAddon.h new file mode 100644 index 0000000..6eed7cd --- /dev/null +++ b/xbmc/addons/AddonCallbacksAddon.h @@ -0,0 +1,73 @@ +#pragma once +/* + * Copyright (C) 2012-2013 Team XBMC + * http://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, see + * . + * + */ + +#include "AddonCallbacks.h" + +namespace ADDON +{ + +class CAddonCallbacksAddon +{ +public: + CAddonCallbacksAddon(CAddon* addon); + ~CAddonCallbacksAddon(); + + /*! + * @return The callback table. + */ + CB_AddOnLib *GetCallbacks() { return m_callbacks; } + + static void AddOnLog(void *addonData, const addon_log_t addonLogLevel, const char *strMessage); + static bool GetAddonSetting(void *addonData, const char *strSettingName, void *settingValue); + static void QueueNotification(void *addonData, const queue_msg_t type, const char *strMessage); + static bool WakeOnLan(const char *mac); + static char* UnknownToUTF8(const char *strSource); + static char* GetLocalizedString(const void* addonData, long dwCode); + static char* GetDVDMenuLanguage(const void* addonData); + static void FreeString(const void* addonData, char* str); + + // file operations + static void* OpenFile(const void* addonData, const char* strFileName, unsigned int flags); + static void* OpenFileForWrite(const void* addonData, const char* strFileName, bool bOverwrite); + static ssize_t ReadFile(const void* addonData, void* file, void* lpBuf, size_t uiBufSize); + static bool ReadFileString(const void* addonData, void* file, char *szLine, int iLineLength); + static ssize_t WriteFile(const void* addonData, void* file, const void* lpBuf, size_t uiBufSize); + static void FlushFile(const void* addonData, void* file); + static int64_t SeekFile(const void* addonData, void* file, int64_t iFilePosition, int iWhence); + static int TruncateFile(const void* addonData, void* file, int64_t iSize); + static int64_t GetFilePosition(const void* addonData, void* file); + static int64_t GetFileLength(const void* addonData, void* file); + static void CloseFile(const void* addonData, void* file); + static int GetFileChunkSize(const void* addonData, void* file); + static bool FileExists(const void* addonData, const char *strFileName, bool bUseCache); + static int StatFile(const void* addonData, const char *strFileName, struct __stat64* buffer); + static bool DeleteFile(const void* addonData, const char *strFileName); + static bool CanOpenDirectory(const void* addonData, const char* strURL); + static bool CreateDirectory(const void* addonData, const char *strPath); + static bool DirectoryExists(const void* addonData, const char *strPath); + static bool RemoveDirectory(const void* addonData, const char *strPath); + +private: + CB_AddOnLib *m_callbacks; /*!< callback addresses */ + CAddon *m_addon; /*!< the add-on */ +}; + +}; /* namespace ADDON */ diff --git a/xbmc/addons/AddonCallbacksCodec.cpp b/xbmc/addons/AddonCallbacksCodec.cpp new file mode 100644 index 0000000..9c7be30 --- /dev/null +++ b/xbmc/addons/AddonCallbacksCodec.cpp @@ -0,0 +1,116 @@ +/* + * Copyright (C) 2012-2013 Team XBMC + * http://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, see + * . + * + */ + +#include "Application.h" +#include "Addon.h" +#include "AddonCallbacksCodec.h" +#include "utils/StringUtils.h" + +extern "C" { +#include "libavcodec/avcodec.h" +#include "libavformat/avformat.h" +} + +namespace ADDON +{ +class CCodecIds +{ +public: + virtual ~CCodecIds(void) {} + + static CCodecIds& Get(void) + { + static CCodecIds _instance; + return _instance; + } + + xbmc_codec_t GetCodecByName(const char* strCodecName) + { + xbmc_codec_t retVal = XBMC_INVALID_CODEC; + if (strlen(strCodecName) == 0) + return retVal; + + std::string strUpperCodecName = strCodecName; + StringUtils::ToUpper(strUpperCodecName); + + std::map::const_iterator it = m_lookup.find(strUpperCodecName); + if (it != m_lookup.end()) + retVal = it->second; + + return retVal; + } + +private: + CCodecIds(void) + { + // get ids and names + AVCodec* codec = NULL; + xbmc_codec_t tmp; + while ((codec = av_codec_next(codec))) + { + if (av_codec_is_decoder(codec)) + { + tmp.codec_type = (xbmc_codec_type_t)codec->type; + tmp.codec_id = codec->id; + + std::string strUpperCodecName = codec->name; + StringUtils::ToUpper(strUpperCodecName); + + m_lookup.insert(std::make_pair(strUpperCodecName, tmp)); + } + } + + // teletext is not returned by av_codec_next. we got our own decoder + tmp.codec_type = XBMC_CODEC_TYPE_SUBTITLE; + tmp.codec_id = AV_CODEC_ID_DVB_TELETEXT; + m_lookup.insert(std::make_pair("TELETEXT", tmp)); + + // rds is not returned by av_codec_next. we got our own decoder + tmp.codec_type = XBMC_CODEC_TYPE_RDS; + tmp.codec_id = AV_CODEC_ID_NONE; + m_lookup.insert(std::make_pair("RDS", tmp)); + } + + std::map m_lookup; +}; + +CAddonCallbacksCodec::CAddonCallbacksCodec(CAddon* addon) +{ + m_addon = addon; + m_callbacks = new CB_CODECLib; + + /* write XBMC addon-on specific add-on function addresses to the callback table */ + m_callbacks->GetCodecByName = GetCodecByName; +} + +CAddonCallbacksCodec::~CAddonCallbacksCodec() +{ + /* delete the callback table */ + delete m_callbacks; +} + +xbmc_codec_t CAddonCallbacksCodec::GetCodecByName(const void* addonData, const char* strCodecName) +{ + (void)addonData; + return CCodecIds::Get().GetCodecByName(strCodecName); +} + +}; /* namespace ADDON */ + diff --git a/xbmc/addons/AddonCallbacksCodec.h b/xbmc/addons/AddonCallbacksCodec.h new file mode 100644 index 0000000..5b816fe --- /dev/null +++ b/xbmc/addons/AddonCallbacksCodec.h @@ -0,0 +1,46 @@ +#pragma once +/* + * Copyright (C) 2012-2013 Team XBMC + * http://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, see + * . + * + */ + +#include "AddonCallbacks.h" + +namespace ADDON +{ + +class CAddonCallbacksCodec +{ +public: + CAddonCallbacksCodec(CAddon* addon); + ~CAddonCallbacksCodec(); + + /*! + * @return The callback table. + */ + CB_CODECLib *GetCallbacks() { return m_callbacks; } + + static xbmc_codec_t GetCodecByName(const void* addonData, const char* strCodecName); + +private: + CB_CODECLib* m_callbacks; /*!< callback addresses */ + CAddon* m_addon; /*!< the add-on */ +}; + +}; /* namespace ADDON */ + diff --git a/xbmc/addons/AddonCallbacksGUI.cpp b/xbmc/addons/AddonCallbacksGUI.cpp new file mode 100644 index 0000000..3069039 --- /dev/null +++ b/xbmc/addons/AddonCallbacksGUI.cpp @@ -0,0 +1,2281 @@ +/* + * Copyright (C) 2012-2013 Team XBMC + * http://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, see + * . + * + */ + +#include "Application.h" +#include "ApplicationMessenger.h" +#include "Addon.h" +#include "AddonCallbacksGUI.h" +#include "utils/log.h" +#include "Skin.h" +#include "FileItem.h" +#include "filesystem/File.h" +#include "utils/URIUtils.h" +#include "utils/TimeUtils.h" +#include "utils/StringUtils.h" +#include "guilib/GUIWindowManager.h" +#include "input/Key.h" +#include "guilib/TextureManager.h" +#include "guilib/GUISpinControlEx.h" +#include "guilib/GUIRadioButtonControl.h" +#include "guilib/GUISettingsSliderControl.h" +#include "guilib/GUIEditControl.h" +#include "guilib/GUIProgressControl.h" +#include "guilib/GUIRenderingControl.h" +#include "guilib/GUIKeyboardFactory.h" +#include "dialogs/GUIDialogNumeric.h" +#include "dialogs/GUIDialogOK.h" +#include "dialogs/GUIDialogYesNo.h" +#include "dialogs/GUIDialogFileBrowser.h" +#include "dialogs/GUIDialogTextViewer.h" +#include "dialogs/GUIDialogSelect.h" + +#define CONTROL_BTNVIEWASICONS 2 +#define CONTROL_BTNSORTBY 3 +#define CONTROL_BTNSORTASC 4 +#define CONTROL_LABELFILES 12 + +using namespace std; + +namespace ADDON +{ + +static int iXBMCGUILockRef = 0; + +CAddonCallbacksGUI::CAddonCallbacksGUI(CAddon* addon) +{ + m_addon = addon; + m_callbacks = new CB_GUILib; + + /* GUI Helper functions */ + m_callbacks->Lock = CAddonCallbacksGUI::Lock; + m_callbacks->Unlock = CAddonCallbacksGUI::Unlock; + m_callbacks->GetScreenHeight = CAddonCallbacksGUI::GetScreenHeight; + m_callbacks->GetScreenWidth = CAddonCallbacksGUI::GetScreenWidth; + m_callbacks->GetVideoResolution = CAddonCallbacksGUI::GetVideoResolution; + m_callbacks->Window_New = CAddonCallbacksGUI::Window_New; + m_callbacks->Window_Delete = CAddonCallbacksGUI::Window_Delete; + m_callbacks->Window_SetCallbacks = CAddonCallbacksGUI::Window_SetCallbacks; + m_callbacks->Window_Show = CAddonCallbacksGUI::Window_Show; + m_callbacks->Window_Close = CAddonCallbacksGUI::Window_Close; + m_callbacks->Window_DoModal = CAddonCallbacksGUI::Window_DoModal; + m_callbacks->Window_SetFocusId = CAddonCallbacksGUI::Window_SetFocusId; + m_callbacks->Window_GetFocusId = CAddonCallbacksGUI::Window_GetFocusId; + m_callbacks->Window_SetCoordinateResolution = CAddonCallbacksGUI::Window_SetCoordinateResolution; + m_callbacks->Window_SetProperty = CAddonCallbacksGUI::Window_SetProperty; + m_callbacks->Window_SetPropertyInt = CAddonCallbacksGUI::Window_SetPropertyInt; + m_callbacks->Window_SetPropertyBool = CAddonCallbacksGUI::Window_SetPropertyBool; + m_callbacks->Window_SetPropertyDouble = CAddonCallbacksGUI::Window_SetPropertyDouble; + m_callbacks->Window_GetProperty = CAddonCallbacksGUI::Window_GetProperty; + m_callbacks->Window_GetPropertyInt = CAddonCallbacksGUI::Window_GetPropertyInt; + m_callbacks->Window_GetPropertyBool = CAddonCallbacksGUI::Window_GetPropertyBool; + m_callbacks->Window_GetPropertyDouble = CAddonCallbacksGUI::Window_GetPropertyDouble; + m_callbacks->Window_ClearProperties = CAddonCallbacksGUI::Window_ClearProperties; + + m_callbacks->Window_GetListSize = CAddonCallbacksGUI::Window_GetListSize; + m_callbacks->Window_ClearList = CAddonCallbacksGUI::Window_ClearList; + m_callbacks->Window_AddItem = CAddonCallbacksGUI::Window_AddItem; + m_callbacks->Window_AddStringItem = CAddonCallbacksGUI::Window_AddStringItem; + m_callbacks->Window_RemoveItem = CAddonCallbacksGUI::Window_RemoveItem; + m_callbacks->Window_GetListItem = CAddonCallbacksGUI::Window_GetListItem; + m_callbacks->Window_SetCurrentListPosition = CAddonCallbacksGUI::Window_SetCurrentListPosition; + m_callbacks->Window_GetCurrentListPosition = CAddonCallbacksGUI::Window_GetCurrentListPosition; + + m_callbacks->Window_GetControl_Spin = CAddonCallbacksGUI::Window_GetControl_Spin; + m_callbacks->Window_GetControl_Button = CAddonCallbacksGUI::Window_GetControl_Button; + m_callbacks->Window_GetControl_RadioButton = CAddonCallbacksGUI::Window_GetControl_RadioButton; + m_callbacks->Window_GetControl_Edit = CAddonCallbacksGUI::Window_GetControl_Edit; + m_callbacks->Window_GetControl_Progress = CAddonCallbacksGUI::Window_GetControl_Progress; + m_callbacks->Window_GetControl_RenderAddon = CAddonCallbacksGUI::Window_GetControl_RenderAddon; + m_callbacks->Window_GetControl_Slider = CAddonCallbacksGUI::Window_GetControl_Slider; + m_callbacks->Window_GetControl_SettingsSlider= CAddonCallbacksGUI::Window_GetControl_SettingsSlider; + + m_callbacks->Window_SetControlLabel = CAddonCallbacksGUI::Window_SetControlLabel; + m_callbacks->Window_MarkDirtyRegion = CAddonCallbacksGUI::Window_MarkDirtyRegion; + + m_callbacks->Control_Spin_SetVisible = CAddonCallbacksGUI::Control_Spin_SetVisible; + m_callbacks->Control_Spin_SetText = CAddonCallbacksGUI::Control_Spin_SetText; + m_callbacks->Control_Spin_Clear = CAddonCallbacksGUI::Control_Spin_Clear; + m_callbacks->Control_Spin_AddLabel = CAddonCallbacksGUI::Control_Spin_AddLabel; + m_callbacks->Control_Spin_GetValue = CAddonCallbacksGUI::Control_Spin_GetValue; + m_callbacks->Control_Spin_SetValue = CAddonCallbacksGUI::Control_Spin_SetValue; + + m_callbacks->Control_RadioButton_SetVisible = CAddonCallbacksGUI::Control_RadioButton_SetVisible; + m_callbacks->Control_RadioButton_SetText = CAddonCallbacksGUI::Control_RadioButton_SetText; + m_callbacks->Control_RadioButton_SetSelected= CAddonCallbacksGUI::Control_RadioButton_SetSelected; + m_callbacks->Control_RadioButton_IsSelected = CAddonCallbacksGUI::Control_RadioButton_IsSelected; + + m_callbacks->Control_Progress_SetPercentage = CAddonCallbacksGUI::Control_Progress_SetPercentage; + m_callbacks->Control_Progress_GetPercentage = CAddonCallbacksGUI::Control_Progress_GetPercentage; + m_callbacks->Control_Progress_SetInfo = CAddonCallbacksGUI::Control_Progress_SetInfo; + m_callbacks->Control_Progress_GetInfo = CAddonCallbacksGUI::Control_Progress_GetInfo; + m_callbacks->Control_Progress_GetDescription= CAddonCallbacksGUI::Control_Progress_GetDescription; + + m_callbacks->ListItem_Create = CAddonCallbacksGUI::ListItem_Create; + m_callbacks->ListItem_GetLabel = CAddonCallbacksGUI::ListItem_GetLabel; + m_callbacks->ListItem_SetLabel = CAddonCallbacksGUI::ListItem_SetLabel; + m_callbacks->ListItem_GetLabel2 = CAddonCallbacksGUI::ListItem_GetLabel2; + m_callbacks->ListItem_SetLabel2 = CAddonCallbacksGUI::ListItem_SetLabel2; + m_callbacks->ListItem_SetIconImage = CAddonCallbacksGUI::ListItem_SetIconImage; + m_callbacks->ListItem_SetThumbnailImage = CAddonCallbacksGUI::ListItem_SetThumbnailImage; + m_callbacks->ListItem_SetInfo = CAddonCallbacksGUI::ListItem_SetInfo; + m_callbacks->ListItem_SetProperty = CAddonCallbacksGUI::ListItem_SetProperty; + m_callbacks->ListItem_GetProperty = CAddonCallbacksGUI::ListItem_GetProperty; + m_callbacks->ListItem_SetPath = CAddonCallbacksGUI::ListItem_SetPath; + + m_callbacks->RenderAddon_SetCallbacks = CAddonCallbacksGUI::RenderAddon_SetCallbacks; + m_callbacks->RenderAddon_Delete = CAddonCallbacksGUI::RenderAddon_Delete; + + m_callbacks->Control_Slider_SetVisible = CAddonCallbacksGUI::Control_Slider_SetVisible; + m_callbacks->Control_Slider_GetDescription = CAddonCallbacksGUI::Control_Slider_GetDescription; + m_callbacks->Control_Slider_SetIntRange = CAddonCallbacksGUI::Control_Slider_SetIntRange; + m_callbacks->Control_Slider_SetIntValue = CAddonCallbacksGUI::Control_Slider_SetIntValue; + m_callbacks->Control_Slider_GetIntValue = CAddonCallbacksGUI::Control_Slider_GetIntValue; + m_callbacks->Control_Slider_SetIntInterval = CAddonCallbacksGUI::Control_Slider_SetIntInterval; + m_callbacks->Control_Slider_SetPercentage = CAddonCallbacksGUI::Control_Slider_SetPercentage; + m_callbacks->Control_Slider_GetPercentage = CAddonCallbacksGUI::Control_Slider_GetPercentage; + m_callbacks->Control_Slider_SetFloatRange = CAddonCallbacksGUI::Control_Slider_SetFloatRange; + m_callbacks->Control_Slider_SetFloatValue = CAddonCallbacksGUI::Control_Slider_SetFloatValue; + m_callbacks->Control_Slider_GetFloatValue = CAddonCallbacksGUI::Control_Slider_GetFloatValue; + m_callbacks->Control_Slider_SetFloatInterval = CAddonCallbacksGUI::Control_Slider_SetFloatInterval; + + m_callbacks->Control_SettingsSlider_SetVisible = CAddonCallbacksGUI::Control_SettingsSlider_SetVisible; + m_callbacks->Control_SettingsSlider_SetText = CAddonCallbacksGUI::Control_SettingsSlider_SetText; + m_callbacks->Control_SettingsSlider_GetDescription = CAddonCallbacksGUI::Control_SettingsSlider_GetDescription; + m_callbacks->Control_SettingsSlider_SetIntRange = CAddonCallbacksGUI::Control_SettingsSlider_SetIntRange; + m_callbacks->Control_SettingsSlider_SetIntValue = CAddonCallbacksGUI::Control_SettingsSlider_SetIntValue; + m_callbacks->Control_SettingsSlider_GetIntValue = CAddonCallbacksGUI::Control_SettingsSlider_GetIntValue; + m_callbacks->Control_SettingsSlider_SetIntInterval = CAddonCallbacksGUI::Control_SettingsSlider_SetIntInterval; + m_callbacks->Control_SettingsSlider_SetPercentage = CAddonCallbacksGUI::Control_SettingsSlider_SetPercentage; + m_callbacks->Control_SettingsSlider_GetPercentage = CAddonCallbacksGUI::Control_SettingsSlider_GetPercentage; + m_callbacks->Control_SettingsSlider_SetFloatRange = CAddonCallbacksGUI::Control_SettingsSlider_SetFloatRange; + m_callbacks->Control_SettingsSlider_SetFloatValue = CAddonCallbacksGUI::Control_SettingsSlider_SetFloatValue; + m_callbacks->Control_SettingsSlider_GetFloatValue = CAddonCallbacksGUI::Control_SettingsSlider_GetFloatValue; + m_callbacks->Control_SettingsSlider_SetFloatInterval = CAddonCallbacksGUI::Control_SettingsSlider_SetFloatInterval; + + m_callbacks->Dialog_Keyboard_ShowAndGetInputWithHead = CAddonCallbacksGUI::Dialog_Keyboard_ShowAndGetInputWithHead; + m_callbacks->Dialog_Keyboard_ShowAndGetInput = CAddonCallbacksGUI::Dialog_Keyboard_ShowAndGetInput; + m_callbacks->Dialog_Keyboard_ShowAndGetNewPasswordWithHead = CAddonCallbacksGUI::Dialog_Keyboard_ShowAndGetNewPasswordWithHead; + m_callbacks->Dialog_Keyboard_ShowAndGetNewPassword = CAddonCallbacksGUI::Dialog_Keyboard_ShowAndGetNewPassword; + m_callbacks->Dialog_Keyboard_ShowAndVerifyNewPasswordWithHead = CAddonCallbacksGUI::Dialog_Keyboard_ShowAndVerifyNewPasswordWithHead; + m_callbacks->Dialog_Keyboard_ShowAndVerifyNewPassword = CAddonCallbacksGUI::Dialog_Keyboard_ShowAndVerifyNewPassword; + m_callbacks->Dialog_Keyboard_ShowAndVerifyPassword = CAddonCallbacksGUI::Dialog_Keyboard_ShowAndVerifyPassword; + m_callbacks->Dialog_Keyboard_ShowAndGetFilter = CAddonCallbacksGUI::Dialog_Keyboard_ShowAndGetFilter; + m_callbacks->Dialog_Keyboard_SendTextToActiveKeyboard = CAddonCallbacksGUI::Dialog_Keyboard_SendTextToActiveKeyboard; + m_callbacks->Dialog_Keyboard_isKeyboardActivated = CAddonCallbacksGUI::Dialog_Keyboard_isKeyboardActivated; + + m_callbacks->Dialog_Numeric_ShowAndVerifyNewPassword = CAddonCallbacksGUI::Dialog_Numeric_ShowAndVerifyNewPassword; + m_callbacks->Dialog_Numeric_ShowAndVerifyPassword = CAddonCallbacksGUI::Dialog_Numeric_ShowAndVerifyPassword; + m_callbacks->Dialog_Numeric_ShowAndVerifyInput = CAddonCallbacksGUI::Dialog_Numeric_ShowAndVerifyInput; + m_callbacks->Dialog_Numeric_ShowAndGetTime = CAddonCallbacksGUI::Dialog_Numeric_ShowAndGetTime; + m_callbacks->Dialog_Numeric_ShowAndGetDate = CAddonCallbacksGUI::Dialog_Numeric_ShowAndGetDate; + m_callbacks->Dialog_Numeric_ShowAndGetIPAddress = CAddonCallbacksGUI::Dialog_Numeric_ShowAndGetIPAddress; + m_callbacks->Dialog_Numeric_ShowAndGetNumber = CAddonCallbacksGUI::Dialog_Numeric_ShowAndGetNumber; + m_callbacks->Dialog_Numeric_ShowAndGetSeconds = CAddonCallbacksGUI::Dialog_Numeric_ShowAndGetSeconds; + + m_callbacks->Dialog_FileBrowser_ShowAndGetFile = CAddonCallbacksGUI::Dialog_FileBrowser_ShowAndGetFile; + + m_callbacks->Dialog_OK_ShowAndGetInputSingleText = CAddonCallbacksGUI::Dialog_OK_ShowAndGetInputSingleText; + m_callbacks->Dialog_OK_ShowAndGetInputLineText = CAddonCallbacksGUI::Dialog_OK_ShowAndGetInputLineText; + + m_callbacks->Dialog_YesNo_ShowAndGetInputSingleText = CAddonCallbacksGUI::Dialog_YesNo_ShowAndGetInputSingleText; + m_callbacks->Dialog_YesNo_ShowAndGetInputLineText = CAddonCallbacksGUI::Dialog_YesNo_ShowAndGetInputLineText; + m_callbacks->Dialog_YesNo_ShowAndGetInputLineButtonText = CAddonCallbacksGUI::Dialog_YesNo_ShowAndGetInputLineButtonText; + + m_callbacks->Dialog_TextViewer = CAddonCallbacksGUI::Dialog_TextViewer; + + m_callbacks->Dialog_Select = CAddonCallbacksGUI::Dialog_Select; +} + +CAddonCallbacksGUI::~CAddonCallbacksGUI() +{ + delete m_callbacks; +} + +void CAddonCallbacksGUI::Lock() +{ + if (iXBMCGUILockRef == 0) g_graphicsContext.Lock(); + iXBMCGUILockRef++; +} + +void CAddonCallbacksGUI::Unlock() +{ + if (iXBMCGUILockRef > 0) + { + iXBMCGUILockRef--; + if (iXBMCGUILockRef == 0) g_graphicsContext.Unlock(); + } +} + +int CAddonCallbacksGUI::GetScreenHeight() +{ + return g_graphicsContext.GetHeight(); +} + +int CAddonCallbacksGUI::GetScreenWidth() +{ + return g_graphicsContext.GetWidth(); +} + +int CAddonCallbacksGUI::GetVideoResolution() +{ + return (int)g_graphicsContext.GetVideoResolution(); +} + +GUIHANDLE CAddonCallbacksGUI::Window_New(void *addonData, const char *xmlFilename, const char *defaultSkin, bool forceFallback, bool asDialog) +{ + CAddonCallbacks* helper = (CAddonCallbacks*) addonData; + if (!helper) + return NULL; + + CAddonCallbacksGUI* guiHelper = helper->GetHelperGUI(); + + RESOLUTION_INFO res; + std::string strSkinPath; + if (!forceFallback) + { + /* Check to see if the XML file exists in current skin. If not use + fallback path to find a skin for the addon */ + strSkinPath = g_SkinInfo->GetSkinPath(xmlFilename, &res); + + if (!XFILE::CFile::Exists(strSkinPath)) + { + /* Check for the matching folder for the skin in the fallback skins folder */ + std::string basePath = URIUtils::AddFileToFolder(guiHelper->m_addon->Path(), "resources"); + basePath = URIUtils::AddFileToFolder(basePath, "skins"); + basePath = URIUtils::AddFileToFolder(basePath, URIUtils::GetFileName(g_SkinInfo->Path())); + strSkinPath = g_SkinInfo->GetSkinPath(xmlFilename, &res, basePath); + if (!XFILE::CFile::Exists(strSkinPath)) + { + /* Finally fallback to the DefaultSkin as it didn't exist in either the + XBMC Skin folder or the fallback skin folder */ + forceFallback = true; + } + } + } + + if (forceFallback) + { + //FIXME make this static method of current skin? + std::string str("none"); + AddonProps props(str, ADDON_SKIN, str, str); + std::string basePath = URIUtils::AddFileToFolder(guiHelper->m_addon->Path(), "resources"); + basePath = URIUtils::AddFileToFolder(basePath, "skins"); + basePath = URIUtils::AddFileToFolder(basePath, defaultSkin); + props.path = basePath; + + CSkinInfo skinInfo(props); + skinInfo.Start(); + strSkinPath = skinInfo.GetSkinPath(xmlFilename, &res, basePath); + + if (!XFILE::CFile::Exists(strSkinPath)) + { + CLog::Log(LOGERROR, "Window_New: %s/%s - XML File '%s' for Window is missing, contact Developer '%s' of this AddOn", TranslateType(guiHelper->m_addon->Type()).c_str(), guiHelper->m_addon->Name().c_str(), strSkinPath.c_str(), guiHelper->m_addon->Author().c_str()); + return NULL; + } + } + // window id's 14000 - 14100 are reserved for addons + // get first window id that is not in use + int id = WINDOW_ADDON_START; + // if window 14099 is in use it means addon can't create more windows + Lock(); + if (g_windowManager.GetWindow(WINDOW_ADDON_END)) + { + Unlock(); + CLog::Log(LOGERROR, "Window_New: %s/%s - maximum number of windows reached", TranslateType(guiHelper->m_addon->Type()).c_str(), guiHelper->m_addon->Name().c_str()); + return NULL; + } + while(id < WINDOW_ADDON_END && g_windowManager.GetWindow(id) != NULL) id++; + Unlock(); + + CGUIWindow *window; + if (!asDialog) + window = new CGUIAddonWindow(id, strSkinPath, guiHelper->m_addon); + else + window = new CGUIAddonWindowDialog(id, strSkinPath, guiHelper->m_addon); + + Lock(); + g_windowManager.Add(window); + Unlock(); + + window->SetCoordsRes(res); + + return window; +} + +void CAddonCallbacksGUI::Window_Delete(void *addonData, GUIHANDLE handle) +{ + CAddonCallbacks* helper = (CAddonCallbacks*) addonData; + if (!helper) + return; + + CAddonCallbacksGUI* guiHelper = helper->GetHelperGUI(); + + if (!handle) + { + CLog::Log(LOGERROR, "Window_Show: %s/%s - No Window", TranslateType(guiHelper->m_addon->Type()).c_str(), guiHelper->m_addon->Name().c_str()); + return; + } + + CGUIAddonWindow *pAddonWindow = (CGUIAddonWindow*)handle; + CGUIWindow *pWindow = (CGUIWindow*)g_windowManager.GetWindow(pAddonWindow->m_iWindowId); + if (!pWindow) + return; + + Lock(); + // first change to an existing window + if (g_windowManager.GetActiveWindow() == pAddonWindow->m_iWindowId && !g_application.m_bStop) + { + if(g_windowManager.GetWindow(pAddonWindow->m_iOldWindowId)) + g_windowManager.ActivateWindow(pAddonWindow->m_iOldWindowId); + else // old window does not exist anymore, switch to home + g_windowManager.ActivateWindow(WINDOW_HOME); + } + // Free any window properties + pAddonWindow->ClearProperties(); + // free the window's resources and unload it (free all guicontrols) + pAddonWindow->FreeResources(true); + + g_windowManager.Remove(pAddonWindow->GetID()); + delete pAddonWindow; + Unlock(); +} + +void CAddonCallbacksGUI::Window_SetCallbacks(void *addonData, GUIHANDLE handle, GUIHANDLE clienthandle, bool (*initCB)(GUIHANDLE), bool (*clickCB)(GUIHANDLE, int), bool (*focusCB)(GUIHANDLE, int), bool (*onActionCB)(GUIHANDLE handle, int)) +{ + CAddonCallbacks* helper = (CAddonCallbacks*) addonData; + if (!helper || !handle) + return; + + CGUIAddonWindow *pAddonWindow = (CGUIAddonWindow*)handle; + + Lock(); + pAddonWindow->m_clientHandle = clienthandle; + pAddonWindow->CBOnInit = initCB; + pAddonWindow->CBOnClick = clickCB; + pAddonWindow->CBOnFocus = focusCB; + pAddonWindow->CBOnAction = onActionCB; + Unlock(); +} + +bool CAddonCallbacksGUI::Window_Show(void *addonData, GUIHANDLE handle) +{ + CAddonCallbacks* helper = (CAddonCallbacks*) addonData; + if (!helper) + return false; + + CAddonCallbacksGUI* guiHelper = helper->GetHelperGUI(); + + if (!handle) + { + CLog::Log(LOGERROR, "Window_Show: %s/%s - No Window", TranslateType(guiHelper->m_addon->Type()).c_str(), guiHelper->m_addon->Name().c_str()); + return false; + } + + CGUIAddonWindow *pAddonWindow = (CGUIAddonWindow*)handle; + CGUIWindow *pWindow = (CGUIWindow*)g_windowManager.GetWindow(pAddonWindow->m_iWindowId); + if (!pWindow) + return false; + + if (pAddonWindow->m_iOldWindowId != pAddonWindow->m_iWindowId && pAddonWindow->m_iWindowId != g_windowManager.GetActiveWindow()) + pAddonWindow->m_iOldWindowId = g_windowManager.GetActiveWindow(); + + Lock(); + if (pAddonWindow->IsDialog()) + ((CGUIAddonWindowDialog*)pAddonWindow)->Show(); + else + g_windowManager.ActivateWindow(pAddonWindow->m_iWindowId); + Unlock(); + + return true; +} + +bool CAddonCallbacksGUI::Window_Close(void *addonData, GUIHANDLE handle) +{ + CAddonCallbacks* helper = (CAddonCallbacks*) addonData; + if (!helper) + return false; + + CAddonCallbacksGUI* guiHelper = helper->GetHelperGUI(); + + if (!handle) + { + CLog::Log(LOGERROR, "Window_Close: %s/%s - No Window", TranslateType(guiHelper->m_addon->Type()).c_str(), guiHelper->m_addon->Name().c_str()); + return false; + } + + CGUIAddonWindow *pAddonWindow = (CGUIAddonWindow*)handle; + CGUIWindow *pWindow = (CGUIWindow*)g_windowManager.GetWindow(pAddonWindow->m_iWindowId); + if (!pWindow) + return false; + + pAddonWindow->m_bModal = false; + if (pAddonWindow->IsDialog()) + ((CGUIAddonWindowDialog*)pAddonWindow)->PulseActionEvent(); + else + ((CGUIAddonWindow*)pAddonWindow)->PulseActionEvent(); + + Lock(); + // if it's a dialog, we have to close it a bit different + if (pAddonWindow->IsDialog()) + ((CGUIAddonWindowDialog*)pAddonWindow)->Show(false); + else + g_windowManager.ActivateWindow(pAddonWindow->m_iOldWindowId); + pAddonWindow->m_iOldWindowId = 0; + + Unlock(); + + return true; +} + +bool CAddonCallbacksGUI::Window_DoModal(void *addonData, GUIHANDLE handle) +{ + CAddonCallbacks* helper = (CAddonCallbacks*) addonData; + if (!helper) + return false; + + CAddonCallbacksGUI* guiHelper = helper->GetHelperGUI(); + + if (!handle) + { + CLog::Log(LOGERROR, "Window_DoModal: %s/%s - No Window", TranslateType(guiHelper->m_addon->Type()).c_str(), guiHelper->m_addon->Name().c_str()); + return false; + } + + CGUIAddonWindow *pAddonWindow = (CGUIAddonWindow*)handle; + CGUIWindow *pWindow = (CGUIWindow*)g_windowManager.GetWindow(pAddonWindow->m_iWindowId); + if (!pWindow) + return false; + + pAddonWindow->m_bModal = true; + + if (pAddonWindow->m_iWindowId != g_windowManager.GetActiveWindow()) + Window_Show(addonData, handle); + + return true; +} + +bool CAddonCallbacksGUI::Window_SetFocusId(void *addonData, GUIHANDLE handle, int iControlId) +{ + CAddonCallbacks* helper = (CAddonCallbacks*) addonData; + if (!helper) + return false; + + CAddonCallbacksGUI* guiHelper = helper->GetHelperGUI(); + + if (!handle) + { + CLog::Log(LOGERROR, "Window_SetFocusId: %s/%s - No Window", TranslateType(guiHelper->m_addon->Type()).c_str(), guiHelper->m_addon->Name().c_str()); + return false; + } + + CGUIAddonWindow *pAddonWindow = (CGUIAddonWindow*)handle; + CGUIWindow *pWindow = (CGUIWindow*)g_windowManager.GetWindow(pAddonWindow->m_iWindowId); + if (!pWindow) + return false; + + if(!pWindow->GetControl(iControlId)) + { + CLog::Log(LOGERROR, "Window_SetFocusId: %s/%s - Control does not exist in window", TranslateType(guiHelper->m_addon->Type()).c_str(), guiHelper->m_addon->Name().c_str()); + return false; + } + + Lock(); + CGUIMessage msg = CGUIMessage(GUI_MSG_SETFOCUS, pAddonWindow->m_iWindowId, iControlId); + pWindow->OnMessage(msg); + Unlock(); + + return true; +} + +int CAddonCallbacksGUI::Window_GetFocusId(void *addonData, GUIHANDLE handle) +{ + int iControlId = -1; + + CAddonCallbacks* helper = (CAddonCallbacks*) addonData; + if (!helper) + return iControlId; + + CAddonCallbacksGUI* guiHelper = helper->GetHelperGUI(); + + if (!handle) + { + CLog::Log(LOGERROR, "Window_GetFocusId: %s/%s - No Window", TranslateType(guiHelper->m_addon->Type()).c_str(), guiHelper->m_addon->Name().c_str()); + return iControlId; + } + + CGUIAddonWindow *pAddonWindow = (CGUIAddonWindow*)handle; + CGUIWindow *pWindow = (CGUIWindow*)g_windowManager.GetWindow(pAddonWindow->m_iWindowId); + if (!pWindow) + return iControlId; + + Lock(); + iControlId = pWindow->GetFocusedControlID(); + Unlock(); + + if (iControlId == -1) + { + CLog::Log(LOGERROR, "Window_GetFocusId: %s/%s - No control in this window has focus", TranslateType(guiHelper->m_addon->Type()).c_str(), guiHelper->m_addon->Name().c_str()); + return iControlId; + } + + return iControlId; +} + +bool CAddonCallbacksGUI::Window_SetCoordinateResolution(void *addonData, GUIHANDLE handle, int res) +{ + CAddonCallbacks* helper = (CAddonCallbacks*) addonData; + if (!helper) + return false; + + CAddonCallbacksGUI* guiHelper = helper->GetHelperGUI(); + + if (!handle) + { + CLog::Log(LOGERROR, "SetCoordinateResolution: %s/%s - No Window", TranslateType(guiHelper->m_addon->Type()).c_str(), guiHelper->m_addon->Name().c_str()); + return false; + } + + if (res < RES_HDTV_1080i || res > RES_AUTORES) + { + CLog::Log(LOGERROR, "SetCoordinateResolution: %s/%s - Invalid resolution", TranslateType(guiHelper->m_addon->Type()).c_str(), guiHelper->m_addon->Name().c_str()); + return false; + } + + CGUIAddonWindow *pAddonWindow = (CGUIAddonWindow*)handle; + CGUIWindow *pWindow = (CGUIWindow*)g_windowManager.GetWindow(pAddonWindow->m_iWindowId); + if (!pWindow) + return false; + + pWindow->SetCoordsRes((RESOLUTION)res); + + return true; +} + +void CAddonCallbacksGUI::Window_SetProperty(void *addonData, GUIHANDLE handle, const char *key, const char *value) +{ + CAddonCallbacks* helper = (CAddonCallbacks*) addonData; + if (!helper) + return; + + CAddonCallbacksGUI* guiHelper = helper->GetHelperGUI(); + + if (!handle || !key || !value) + { + CLog::Log(LOGERROR, "Window_SetProperty: %s/%s - No Window or NULL key or value", TranslateType(guiHelper->m_addon->Type()).c_str(), guiHelper->m_addon->Name().c_str()); + return; + } + + CGUIAddonWindow *pAddonWindow = (CGUIAddonWindow*)handle; + CGUIWindow *pWindow = (CGUIWindow*)g_windowManager.GetWindow(pAddonWindow->m_iWindowId); + if (!pWindow) + return; + + std::string lowerKey = key; + StringUtils::ToLower(lowerKey); + + Lock(); + pWindow->SetProperty(lowerKey, value); + Unlock(); +} + +void CAddonCallbacksGUI::Window_SetPropertyInt(void *addonData, GUIHANDLE handle, const char *key, int value) +{ + CAddonCallbacks* helper = (CAddonCallbacks*) addonData; + if (!helper) + return; + + CAddonCallbacksGUI* guiHelper = helper->GetHelperGUI(); + + if (!handle || !key) + { + CLog::Log(LOGERROR, "Window_SetPropertyInt: %s/%s - No Window or NULL key", TranslateType(guiHelper->m_addon->Type()).c_str(), guiHelper->m_addon->Name().c_str()); + return; + } + + CGUIAddonWindow *pAddonWindow = (CGUIAddonWindow*)handle; + CGUIWindow *pWindow = (CGUIWindow*)g_windowManager.GetWindow(pAddonWindow->m_iWindowId); + if (!pWindow) + return; + + std::string lowerKey = key; + StringUtils::ToLower(lowerKey); + + Lock(); + pWindow->SetProperty(lowerKey, value); + Unlock(); +} + +void CAddonCallbacksGUI::Window_SetPropertyBool(void *addonData, GUIHANDLE handle, const char *key, bool value) +{ + CAddonCallbacks* helper = (CAddonCallbacks*) addonData; + if (!helper) + return; + + CAddonCallbacksGUI* guiHelper = helper->GetHelperGUI(); + + if (!handle || !key) + { + CLog::Log(LOGERROR, "Window_SetPropertyBool: %s/%s - No Window or NULL key", TranslateType(guiHelper->m_addon->Type()).c_str(), guiHelper->m_addon->Name().c_str()); + return; + } + + CGUIAddonWindow *pAddonWindow = (CGUIAddonWindow*)handle; + CGUIWindow *pWindow = (CGUIWindow*)g_windowManager.GetWindow(pAddonWindow->m_iWindowId); + if (!pWindow) + return; + + std::string lowerKey = key; + StringUtils::ToLower(lowerKey); + + Lock(); + pWindow->SetProperty(lowerKey, value); + Unlock(); +} + +void CAddonCallbacksGUI::Window_SetPropertyDouble(void *addonData, GUIHANDLE handle, const char *key, double value) +{ + CAddonCallbacks* helper = (CAddonCallbacks*) addonData; + if (!helper) + return; + + CAddonCallbacksGUI* guiHelper = helper->GetHelperGUI(); + + if (!handle || !key) + { + CLog::Log(LOGERROR, "Window_SetPropertyDouble: %s/%s - No Window or NULL key", TranslateType(guiHelper->m_addon->Type()).c_str(), guiHelper->m_addon->Name().c_str()); + return; + } + + CGUIAddonWindow *pAddonWindow = (CGUIAddonWindow*)handle; + CGUIWindow *pWindow = (CGUIWindow*)g_windowManager.GetWindow(pAddonWindow->m_iWindowId); + if (!pWindow) + return; + + std::string lowerKey = key; + StringUtils::ToLower(lowerKey); + + Lock(); + pWindow->SetProperty(lowerKey, value); + Unlock(); +} + +const char* CAddonCallbacksGUI::Window_GetProperty(void *addonData, GUIHANDLE handle, const char *key) +{ + CAddonCallbacks* helper = (CAddonCallbacks*) addonData; + if (!helper) + return NULL; + + CAddonCallbacksGUI* guiHelper = helper->GetHelperGUI(); + + if (!handle || !key) + { + CLog::Log(LOGERROR, "Window_GetProperty: %s/%s - No Window or NULL key", TranslateType(guiHelper->m_addon->Type()).c_str(), guiHelper->m_addon->Name().c_str()); + return NULL; + } + + CGUIAddonWindow *pAddonWindow = (CGUIAddonWindow*)handle; + CGUIWindow *pWindow = (CGUIWindow*)g_windowManager.GetWindow(pAddonWindow->m_iWindowId); + if (!pWindow) + return NULL; + + std::string lowerKey = key; + StringUtils::ToLower(lowerKey); + + Lock(); + string value = pWindow->GetProperty(lowerKey).asString(); + Unlock(); + + return strdup(value.c_str()); +} + +int CAddonCallbacksGUI::Window_GetPropertyInt(void *addonData, GUIHANDLE handle, const char *key) +{ + CAddonCallbacks* helper = (CAddonCallbacks*) addonData; + if (!helper) + return -1; + + CAddonCallbacksGUI* guiHelper = helper->GetHelperGUI(); + + if (!handle || !key) + { + CLog::Log(LOGERROR, "Window_GetPropertyInt: %s/%s - No Window or NULL key", TranslateType(guiHelper->m_addon->Type()).c_str(), guiHelper->m_addon->Name().c_str()); + return -1; + } + + CGUIAddonWindow *pAddonWindow = (CGUIAddonWindow*)handle; + CGUIWindow *pWindow = (CGUIWindow*)g_windowManager.GetWindow(pAddonWindow->m_iWindowId); + if (!pWindow) + return -1; + + std::string lowerKey = key; + StringUtils::ToLower(lowerKey); + + Lock(); + int value = (int)pWindow->GetProperty(lowerKey).asInteger(); + Unlock(); + + return value; +} + +bool CAddonCallbacksGUI::Window_GetPropertyBool(void *addonData, GUIHANDLE handle, const char *key) +{ + CAddonCallbacks* helper = (CAddonCallbacks*) addonData; + if (!helper) + return false; + + CAddonCallbacksGUI* guiHelper = helper->GetHelperGUI(); + + if (!handle || !key) + { + CLog::Log(LOGERROR, "Window_GetPropertyBool: %s/%s - No Window or NULL key", TranslateType(guiHelper->m_addon->Type()).c_str(), guiHelper->m_addon->Name().c_str()); + return false; + } + + CGUIAddonWindow *pAddonWindow = (CGUIAddonWindow*)handle; + CGUIWindow *pWindow = (CGUIWindow*)g_windowManager.GetWindow(pAddonWindow->m_iWindowId); + if (!pWindow) + return false; + + std::string lowerKey = key; + StringUtils::ToLower(lowerKey); + + Lock(); + bool value = pWindow->GetProperty(lowerKey).asBoolean(); + Unlock(); + + return value; +} + +double CAddonCallbacksGUI::Window_GetPropertyDouble(void *addonData, GUIHANDLE handle, const char *key) +{ + CAddonCallbacks* helper = (CAddonCallbacks*) addonData; + if (!helper) + return 0.0; + + CAddonCallbacksGUI* guiHelper = helper->GetHelperGUI(); + + if (!handle || !key) + { + CLog::Log(LOGERROR, "Window_GetPropertyDouble: %s/%s - No Window or NULL key", TranslateType(guiHelper->m_addon->Type()).c_str(), guiHelper->m_addon->Name().c_str()); + return 0.0; + } + + CGUIAddonWindow *pAddonWindow = (CGUIAddonWindow*)handle; + CGUIWindow *pWindow = (CGUIWindow*)g_windowManager.GetWindow(pAddonWindow->m_iWindowId); + if (!pWindow) + return 0.0; + + std::string lowerKey = key; + StringUtils::ToLower(lowerKey); + + Lock(); + double value = pWindow->GetProperty(lowerKey).asDouble(); + Unlock(); + + return value; +} + +void CAddonCallbacksGUI::Window_ClearProperties(void *addonData, GUIHANDLE handle) +{ + CAddonCallbacks* helper = (CAddonCallbacks*) addonData; + if (!helper) + return; + + CAddonCallbacksGUI* guiHelper = helper->GetHelperGUI(); + + if (!handle) + { + CLog::Log(LOGERROR, "Window_ClearProperties: %s/%s - No Window", TranslateType(guiHelper->m_addon->Type()).c_str(), guiHelper->m_addon->Name().c_str()); + return; + } + + CGUIAddonWindow *pAddonWindow = (CGUIAddonWindow*)handle; + CGUIWindow *pWindow = (CGUIWindow*)g_windowManager.GetWindow(pAddonWindow->m_iWindowId); + if (!pWindow) + return; + + Lock(); + pWindow->ClearProperties(); + Unlock(); +} + +int CAddonCallbacksGUI::Window_GetListSize(void *addonData, GUIHANDLE handle) +{ + CAddonCallbacks* helper = (CAddonCallbacks*) addonData; + if (!helper || !handle) + return -1; + + CGUIAddonWindow *pAddonWindow = (CGUIAddonWindow*)handle; + + Lock(); + int listSize = pAddonWindow->GetListSize(); + Unlock(); + + return listSize; +} + +void CAddonCallbacksGUI::Window_ClearList(void *addonData, GUIHANDLE handle) +{ + CAddonCallbacks* helper = (CAddonCallbacks*) addonData; + if (!helper || !handle) + return; + + CGUIAddonWindow *pAddonWindow = (CGUIAddonWindow*)handle; + + Lock(); + pAddonWindow->ClearList(); + Unlock(); + + return; +} + +GUIHANDLE CAddonCallbacksGUI::Window_AddItem(void *addonData, GUIHANDLE handle, GUIHANDLE item, int itemPosition) +{ + CAddonCallbacks* helper = (CAddonCallbacks*) addonData; + if (!helper || !handle || !item) + return NULL; + + CGUIAddonWindow *pAddonWindow = (CGUIAddonWindow*)handle; + CFileItemPtr pItem((CFileItem*)item); + Lock(); + pAddonWindow->AddItem(pItem, itemPosition); + Unlock(); + + return item; +} + +GUIHANDLE CAddonCallbacksGUI::Window_AddStringItem(void *addonData, GUIHANDLE handle, const char *itemName, int itemPosition) +{ + CAddonCallbacks* helper = (CAddonCallbacks*) addonData; + if (!helper || !handle || !itemName) + return NULL; + + CGUIAddonWindow *pAddonWindow = (CGUIAddonWindow*)handle; + CFileItemPtr item(new CFileItem(itemName)); + Lock(); + pAddonWindow->AddItem(item, itemPosition); + Unlock(); + + return item.get(); +} + +void CAddonCallbacksGUI::Window_RemoveItem(void *addonData, GUIHANDLE handle, int itemPosition) +{ + CAddonCallbacks* helper = (CAddonCallbacks*) addonData; + if (!helper || !handle) + return; + + CGUIAddonWindow *pAddonWindow = (CGUIAddonWindow*)handle; + + Lock(); + pAddonWindow->RemoveItem(itemPosition); + Unlock(); + + return; +} + +GUIHANDLE CAddonCallbacksGUI::Window_GetListItem(void *addonData, GUIHANDLE handle, int listPos) +{ + CAddonCallbacks* helper = (CAddonCallbacks*) addonData; + if (!helper || !handle) + return NULL; + + CAddonCallbacksGUI* guiHelper = helper->GetHelperGUI(); + CGUIAddonWindow *pAddonWindow = (CGUIAddonWindow*)handle; + + Lock(); + CFileItemPtr fi = pAddonWindow->GetListItem(listPos); + if (fi == NULL) + { + Unlock(); + CLog::Log(LOGERROR, "Window_GetListItem: %s/%s - Index out of range", TranslateType(guiHelper->m_addon->Type()).c_str(), guiHelper->m_addon->Name().c_str()); + return NULL; + } + Unlock(); + + return fi.get(); +} + +void CAddonCallbacksGUI::Window_SetCurrentListPosition(void *addonData, GUIHANDLE handle, int listPos) +{ + CAddonCallbacks* helper = (CAddonCallbacks*) addonData; + if (!helper || !handle) + return; + + CGUIAddonWindow *pAddonWindow = (CGUIAddonWindow*)handle; + + Lock(); + pAddonWindow->SetCurrentListPosition(listPos); + Unlock(); + + return; +} + +int CAddonCallbacksGUI::Window_GetCurrentListPosition(void *addonData, GUIHANDLE handle) +{ + CAddonCallbacks* helper = (CAddonCallbacks*) addonData; + if (!helper || !handle) + return -1; + + CGUIAddonWindow *pAddonWindow = (CGUIAddonWindow*)handle; + + Lock(); + int listPos = pAddonWindow->GetCurrentListPosition(); + Unlock(); + + return listPos; +} + +GUIHANDLE CAddonCallbacksGUI::Window_GetControl_Spin(void *addonData, GUIHANDLE handle, int controlId) +{ + CAddonCallbacks* helper = (CAddonCallbacks*) addonData; + if (!helper || !handle) + return NULL; + + CGUIAddonWindow *pAddonWindow = (CGUIAddonWindow*)handle; + CGUIControl* pGUIControl = (CGUIControl*)pAddonWindow->GetControl(controlId); + if (pGUIControl && pGUIControl->GetControlType() != CGUIControl::GUICONTROL_SPINEX) + return NULL; + + return pGUIControl; +} + +GUIHANDLE CAddonCallbacksGUI::Window_GetControl_Button(void *addonData, GUIHANDLE handle, int controlId) +{ + CAddonCallbacks* helper = (CAddonCallbacks*) addonData; + if (!helper || !handle) + return NULL; + + CGUIAddonWindow *pAddonWindow = (CGUIAddonWindow*)handle; + CGUIControl* pGUIControl = (CGUIControl*)pAddonWindow->GetControl(controlId); + if (pGUIControl && pGUIControl->GetControlType() != CGUIControl::GUICONTROL_BUTTON) + return NULL; + + return pGUIControl; +} + +GUIHANDLE CAddonCallbacksGUI::Window_GetControl_RadioButton(void *addonData, GUIHANDLE handle, int controlId) +{ + CAddonCallbacks* helper = (CAddonCallbacks*) addonData; + if (!helper || !handle) + return NULL; + + CGUIAddonWindow *pAddonWindow = (CGUIAddonWindow*)handle; + CGUIControl* pGUIControl = (CGUIControl*)pAddonWindow->GetControl(controlId); + if (pGUIControl && pGUIControl->GetControlType() != CGUIControl::GUICONTROL_RADIO) + return NULL; + + return pGUIControl; +} + +GUIHANDLE CAddonCallbacksGUI::Window_GetControl_Edit(void *addonData, GUIHANDLE handle, int controlId) +{ + CAddonCallbacks* helper = (CAddonCallbacks*) addonData; + if (!helper || !handle) + return NULL; + + CGUIAddonWindow *pAddonWindow = (CGUIAddonWindow*)handle; + CGUIControl* pGUIControl = (CGUIControl*)pAddonWindow->GetControl(controlId); + if (pGUIControl && pGUIControl->GetControlType() != CGUIControl::GUICONTROL_EDIT) + return NULL; + + return pGUIControl; +} + +GUIHANDLE CAddonCallbacksGUI::Window_GetControl_Progress(void *addonData, GUIHANDLE handle, int controlId) +{ + CAddonCallbacks* helper = (CAddonCallbacks*) addonData; + if (!helper || !handle) + return NULL; + + CGUIAddonWindow *pAddonWindow = (CGUIAddonWindow*)handle; + CGUIControl* pGUIControl = (CGUIControl*)pAddonWindow->GetControl(controlId); + if (pGUIControl && pGUIControl->GetControlType() != CGUIControl::GUICONTROL_PROGRESS) + return NULL; + + return pGUIControl; +} + +GUIHANDLE CAddonCallbacksGUI::Window_GetControl_RenderAddon(void *addonData, GUIHANDLE handle, int controlId) +{ + CAddonCallbacks* helper = (CAddonCallbacks*) addonData; + if (!helper || !handle) + return NULL; + + CGUIAddonWindow *pAddonWindow = (CGUIAddonWindow*)handle; + CGUIControl* pGUIControl = (CGUIControl*)pAddonWindow->GetControl(controlId); + if (pGUIControl && pGUIControl->GetControlType() != CGUIControl::GUICONTROL_RENDERADDON) + return NULL; + + CGUIAddonRenderingControl *pProxyControl; + pProxyControl = new CGUIAddonRenderingControl((CGUIRenderingControl*)pGUIControl); + return pProxyControl; +} + +void CAddonCallbacksGUI::Window_SetControlLabel(void *addonData, GUIHANDLE handle, int controlId, const char *label) +{ + CAddonCallbacks* helper = (CAddonCallbacks*) addonData; + if (!helper || !handle) + return; + + CGUIAddonWindow *pAddonWindow = (CGUIAddonWindow*)handle; + + CGUIMessage msg(GUI_MSG_LABEL_SET, pAddonWindow->m_iWindowId, controlId); + msg.SetLabel(label); + pAddonWindow->OnMessage(msg); +} + +void CAddonCallbacksGUI::Window_MarkDirtyRegion(void *addonData, GUIHANDLE handle) +{ + CAddonCallbacks* helper = (CAddonCallbacks*) addonData; + if (!helper || !handle) + return; + + CGUIAddonWindow *pAddonWindow = (CGUIAddonWindow*)handle; + + pAddonWindow->MarkDirtyRegion(); +} + +void CAddonCallbacksGUI::Control_Spin_SetVisible(void *addonData, GUIHANDLE spinhandle, bool yesNo) +{ + CAddonCallbacks* helper = (CAddonCallbacks*) addonData; + if (!helper || !spinhandle) + return; + + CGUISpinControlEx *pSpin = (CGUISpinControlEx*)spinhandle; + pSpin->SetVisible(yesNo); +} + +void CAddonCallbacksGUI::Control_Spin_SetText(void *addonData, GUIHANDLE spinhandle, const char *label) +{ + CAddonCallbacks* helper = (CAddonCallbacks*) addonData; + if (!helper || !spinhandle) + return; + + CGUISpinControlEx *pSpin = (CGUISpinControlEx*)spinhandle; + pSpin->SetText(label); +} + +void CAddonCallbacksGUI::Control_Spin_Clear(void *addonData, GUIHANDLE spinhandle) +{ + CAddonCallbacks* helper = (CAddonCallbacks*) addonData; + if (!helper || !spinhandle) + return; + + CGUISpinControlEx *pSpin = (CGUISpinControlEx*)spinhandle; + pSpin->Clear(); +} + +void CAddonCallbacksGUI::Control_Spin_AddLabel(void *addonData, GUIHANDLE spinhandle, const char *label, int iValue) +{ + CAddonCallbacks* helper = (CAddonCallbacks*) addonData; + if (!helper || !spinhandle) + return; + + CGUISpinControlEx *pSpin = (CGUISpinControlEx*)spinhandle; + pSpin->AddLabel(label, iValue); +} + +int CAddonCallbacksGUI::Control_Spin_GetValue(void *addonData, GUIHANDLE spinhandle) +{ + CAddonCallbacks* helper = (CAddonCallbacks*) addonData; + if (!helper || !spinhandle) + return -1; + + CGUISpinControlEx *pSpin = (CGUISpinControlEx*)spinhandle; + return pSpin->GetValue(); +} + +void CAddonCallbacksGUI::Control_Spin_SetValue(void *addonData, GUIHANDLE spinhandle, int iValue) +{ + CAddonCallbacks* helper = (CAddonCallbacks*) addonData; + if (!helper || !spinhandle) + return; + + CGUISpinControlEx *pSpin = (CGUISpinControlEx*)spinhandle; + pSpin->SetValue(iValue); +} + +void CAddonCallbacksGUI::Control_RadioButton_SetVisible(void *addonData, GUIHANDLE handle, bool yesNo) +{ + CAddonCallbacks* helper = (CAddonCallbacks*) addonData; + if (!helper || !handle) + return; + + CGUIRadioButtonControl *pRadioButton = (CGUIRadioButtonControl*)handle; + pRadioButton->SetVisible(yesNo); +} + +void CAddonCallbacksGUI::Control_RadioButton_SetText(void *addonData, GUIHANDLE handle, const char *label) +{ + CAddonCallbacks* helper = (CAddonCallbacks*) addonData; + if (!helper || !handle) + return; + + CGUIRadioButtonControl *pRadioButton = (CGUIRadioButtonControl*)handle; + pRadioButton->SetLabel(label); +} + +void CAddonCallbacksGUI::Control_RadioButton_SetSelected(void *addonData, GUIHANDLE handle, bool yesNo) +{ + CAddonCallbacks* helper = (CAddonCallbacks*) addonData; + if (!helper || !handle) + return; + + CGUIRadioButtonControl *pRadioButton = (CGUIRadioButtonControl*)handle; + pRadioButton->SetSelected(yesNo); +} + +bool CAddonCallbacksGUI::Control_RadioButton_IsSelected(void *addonData, GUIHANDLE handle) +{ + CAddonCallbacks* helper = (CAddonCallbacks*) addonData; + if (!helper || !handle) + return false; + + CGUIRadioButtonControl *pRadioButton = (CGUIRadioButtonControl*)handle; + return pRadioButton->IsSelected(); +} + +void CAddonCallbacksGUI::Control_Progress_SetPercentage(void *addonData, GUIHANDLE handle, float fPercent) +{ + CAddonCallbacks* helper = (CAddonCallbacks*) addonData; + if (!helper || !handle) + return; + + CGUIProgressControl *pControl = (CGUIProgressControl*)handle; + pControl->SetPercentage(fPercent); +} + +float CAddonCallbacksGUI::Control_Progress_GetPercentage(void *addonData, GUIHANDLE handle) +{ + CAddonCallbacks* helper = (CAddonCallbacks*) addonData; + if (!helper || !handle) + return 0.0; + + CGUIProgressControl *pControl = (CGUIProgressControl*)handle; + return pControl->GetPercentage(); +} + +void CAddonCallbacksGUI::Control_Progress_SetInfo(void *addonData, GUIHANDLE handle, int iInfo) +{ + CAddonCallbacks* helper = (CAddonCallbacks*) addonData; + if (!helper || !handle) + return; + + CGUIProgressControl *pControl = (CGUIProgressControl*)handle; + pControl->SetInfo(iInfo); +} + +int CAddonCallbacksGUI::Control_Progress_GetInfo(void *addonData, GUIHANDLE handle) +{ + CAddonCallbacks* helper = (CAddonCallbacks*) addonData; + if (!helper || !handle) + return -1; + + CGUIProgressControl *pControl = (CGUIProgressControl*)handle; + return pControl->GetInfo(); +} + +const char* CAddonCallbacksGUI::Control_Progress_GetDescription(void *addonData, GUIHANDLE handle) +{ + CAddonCallbacks* helper = (CAddonCallbacks*) addonData; + if (!helper || !handle) + return NULL; + + CGUIProgressControl *pControl = (CGUIProgressControl*)handle; + std::string string = pControl->GetDescription(); + + char *buffer = (char*) malloc (string.length()+1); + strcpy(buffer, string.c_str()); + return buffer; +} + +/* + * GUI slider control callback functions + */ +GUIHANDLE CAddonCallbacksGUI::Window_GetControl_Slider(void *addonData, GUIHANDLE handle, int controlId) +{ + CAddonCallbacks* helper = (CAddonCallbacks*) addonData; + if (!helper || !handle) + return NULL; + + CGUIAddonWindow *pAddonWindow = (CGUIAddonWindow*)handle; + CGUIControl* pGUIControl = (CGUIControl*)pAddonWindow->GetControl(controlId); + if (pGUIControl && pGUIControl->GetControlType() != CGUIControl::GUICONTROL_SLIDER) + return NULL; + + return pGUIControl; +} + +void CAddonCallbacksGUI::Control_Slider_SetVisible(void *addonData, GUIHANDLE handle, bool yesNo) +{ + CAddonCallbacks* helper = (CAddonCallbacks*) addonData; + if (!helper || !handle) + return; + + CGUIControl *pControl = (CGUIControl*)handle; + pControl->SetVisible(yesNo); +} + +const char* CAddonCallbacksGUI::Control_Slider_GetDescription(void *addonData, GUIHANDLE handle) +{ + CAddonCallbacks* helper = (CAddonCallbacks*) addonData; + if (!helper || !handle) + return NULL; + + CGUISliderControl *pControl = (CGUISliderControl*)handle; + std::string string = pControl->GetDescription(); + + char *buffer = (char*) malloc (string.length()+1); + strcpy(buffer, string.c_str()); + return buffer; +} + +void CAddonCallbacksGUI::Control_Slider_SetIntRange(void *addonData, GUIHANDLE handle, int iStart, int iEnd) +{ + CAddonCallbacks* helper = (CAddonCallbacks*) addonData; + if (!helper || !handle) + return; + + CGUISliderControl *pControl = (CGUISliderControl*)handle; + pControl->SetRange(iStart, iEnd); +} + +void CAddonCallbacksGUI::Control_Slider_SetIntValue(void *addonData, GUIHANDLE handle, int iValue) +{ + CAddonCallbacks* helper = (CAddonCallbacks*) addonData; + if (!helper || !handle) + return; + + CGUISliderControl *pControl = (CGUISliderControl*)handle; + pControl->SetType(SPIN_CONTROL_TYPE_INT); + pControl->SetIntValue(iValue); +} + +int CAddonCallbacksGUI::Control_Slider_GetIntValue(void *addonData, GUIHANDLE handle) +{ + CAddonCallbacks* helper = (CAddonCallbacks*) addonData; + if (!helper || !handle) + return 0; + + CGUISliderControl *pControl = (CGUISliderControl*)handle; + return pControl->GetIntValue(); +} + +void CAddonCallbacksGUI::Control_Slider_SetIntInterval(void *addonData, GUIHANDLE handle, int iInterval) +{ + CAddonCallbacks* helper = (CAddonCallbacks*) addonData; + if (!helper || !handle) + return; + + CGUISliderControl *pControl = (CGUISliderControl*)handle; + pControl->SetIntInterval(iInterval); +} + +void CAddonCallbacksGUI::Control_Slider_SetPercentage(void *addonData, GUIHANDLE handle, float fPercent) +{ + CAddonCallbacks* helper = (CAddonCallbacks*) addonData; + if (!helper || !handle) + return; + + CGUISliderControl *pControl = (CGUISliderControl*)handle; + pControl->SetType(SPIN_CONTROL_TYPE_FLOAT); + pControl->SetPercentage(fPercent); +} + +float CAddonCallbacksGUI::Control_Slider_GetPercentage(void *addonData, GUIHANDLE handle) +{ + CAddonCallbacks* helper = (CAddonCallbacks*) addonData; + if (!helper || !handle) + return 0.0f; + + CGUISliderControl *pControl = (CGUISliderControl*)handle; + return pControl->GetPercentage(); +} + +void CAddonCallbacksGUI::Control_Slider_SetFloatRange(void *addonData, GUIHANDLE handle, float fStart, float fEnd) +{ + CAddonCallbacks* helper = (CAddonCallbacks*) addonData; + if (!helper || !handle) + return; + + CGUISliderControl *pControl = (CGUISliderControl*)handle; + pControl->SetFloatRange(fStart, fEnd); +} + +void CAddonCallbacksGUI::Control_Slider_SetFloatValue(void *addonData, GUIHANDLE handle, float iValue) +{ + CAddonCallbacks* helper = (CAddonCallbacks*) addonData; + if (!helper || !handle) + return; + + CGUISliderControl *pControl = (CGUISliderControl*)handle; + pControl->SetType(SPIN_CONTROL_TYPE_FLOAT); + pControl->SetFloatValue(iValue); +} + +float CAddonCallbacksGUI::Control_Slider_GetFloatValue(void *addonData, GUIHANDLE handle) +{ + CAddonCallbacks* helper = (CAddonCallbacks*) addonData; + if (!helper || !handle) + return 0.0f; + + CGUISliderControl *pControl = (CGUISliderControl*)handle; + return pControl->GetFloatValue(); +} + +void CAddonCallbacksGUI::Control_Slider_SetFloatInterval(void *addonData, GUIHANDLE handle, float fInterval) +{ + CAddonCallbacks* helper = (CAddonCallbacks*) addonData; + if (!helper || !handle) + return; + + CGUISliderControl *pControl = (CGUISliderControl*)handle; + pControl->SetFloatInterval(fInterval); +} + +/* + * GUI settings slider control callback functions + */ +GUIHANDLE CAddonCallbacksGUI::Window_GetControl_SettingsSlider(void *addonData, GUIHANDLE handle, int controlId) +{ + CAddonCallbacks* helper = (CAddonCallbacks*) addonData; + if (!helper || !handle) + return NULL; + + CGUIAddonWindow *pAddonWindow = (CGUIAddonWindow*)handle; + CGUIControl* pGUIControl = (CGUIControl*)pAddonWindow->GetControl(controlId); + if (pGUIControl && pGUIControl->GetControlType() != CGUIControl::GUICONTROL_SETTINGS_SLIDER) + return NULL; + + return pGUIControl; +} + +void CAddonCallbacksGUI::Control_SettingsSlider_SetVisible(void *addonData, GUIHANDLE handle, bool yesNo) +{ + CAddonCallbacks* helper = (CAddonCallbacks*) addonData; + if (!helper || !handle) + return; + + CGUIControl *pControl = (CGUIControl*)handle; + pControl->SetVisible(yesNo); +} + +void CAddonCallbacksGUI::Control_SettingsSlider_SetText(void *addonData, GUIHANDLE handle, const char *label) +{ + CAddonCallbacks* helper = (CAddonCallbacks*) addonData; + if (!helper || !handle) + return; + + CGUISettingsSliderControl *pControl = (CGUISettingsSliderControl*)handle; + pControl->SetText(label); +} + +const char* CAddonCallbacksGUI::Control_SettingsSlider_GetDescription(void *addonData, GUIHANDLE handle) +{ + CAddonCallbacks* helper = (CAddonCallbacks*) addonData; + if (!helper || !handle) + return NULL; + + CGUISettingsSliderControl *pControl = (CGUISettingsSliderControl*)handle; + std::string string = pControl->GetDescription(); + + char *buffer = (char*) malloc (string.length()+1); + strcpy(buffer, string.c_str()); + return buffer; +} + +void CAddonCallbacksGUI::Control_SettingsSlider_SetIntRange(void *addonData, GUIHANDLE handle, int iStart, int iEnd) +{ + CAddonCallbacks* helper = (CAddonCallbacks*) addonData; + if (!helper || !handle) + return; + + CGUISettingsSliderControl *pControl = (CGUISettingsSliderControl*)handle; + pControl->SetRange(iStart, iEnd); +} + +void CAddonCallbacksGUI::Control_SettingsSlider_SetIntValue(void *addonData, GUIHANDLE handle, int iValue) +{ + CAddonCallbacks* helper = (CAddonCallbacks*) addonData; + if (!helper || !handle) + return; + + CGUISettingsSliderControl *pControl = (CGUISettingsSliderControl*)handle; + pControl->SetType(SPIN_CONTROL_TYPE_INT); + pControl->SetIntValue(iValue); +} + +int CAddonCallbacksGUI::Control_SettingsSlider_GetIntValue(void *addonData, GUIHANDLE handle) +{ + CAddonCallbacks* helper = (CAddonCallbacks*) addonData; + if (!helper || !handle) + return 0; + + CGUISettingsSliderControl *pControl = (CGUISettingsSliderControl*)handle; + return pControl->GetIntValue(); +} + +void CAddonCallbacksGUI::Control_SettingsSlider_SetIntInterval(void *addonData, GUIHANDLE handle, int iInterval) +{ + CAddonCallbacks* helper = (CAddonCallbacks*) addonData; + if (!helper || !handle) + return; + + CGUISettingsSliderControl *pControl = (CGUISettingsSliderControl*)handle; + pControl->SetIntInterval(iInterval); +} + +void CAddonCallbacksGUI::Control_SettingsSlider_SetPercentage(void *addonData, GUIHANDLE handle, float fPercent) +{ + CAddonCallbacks* helper = (CAddonCallbacks*) addonData; + if (!helper || !handle) + return; + + CGUISettingsSliderControl *pControl = (CGUISettingsSliderControl*)handle; + pControl->SetType(SPIN_CONTROL_TYPE_FLOAT); + pControl->SetPercentage(fPercent); +} + +float CAddonCallbacksGUI::Control_SettingsSlider_GetPercentage(void *addonData, GUIHANDLE handle) +{ + CAddonCallbacks* helper = (CAddonCallbacks*) addonData; + if (!helper || !handle) + return 0.0f; + + CGUISettingsSliderControl *pControl = (CGUISettingsSliderControl*)handle; + return pControl->GetPercentage(); +} + +void CAddonCallbacksGUI::Control_SettingsSlider_SetFloatRange(void *addonData, GUIHANDLE handle, float fStart, float fEnd) +{ + CAddonCallbacks* helper = (CAddonCallbacks*) addonData; + if (!helper || !handle) + return; + + CGUISettingsSliderControl *pControl = (CGUISettingsSliderControl*)handle; + pControl->SetFloatRange(fStart, fEnd); +} + +void CAddonCallbacksGUI::Control_SettingsSlider_SetFloatValue(void *addonData, GUIHANDLE handle, float fValue) +{ + CAddonCallbacks* helper = (CAddonCallbacks*) addonData; + if (!helper || !handle) + return; + + CGUISettingsSliderControl *pControl = (CGUISettingsSliderControl*)handle; + pControl->SetType(SPIN_CONTROL_TYPE_FLOAT); + pControl->SetFloatValue(fValue); +} + +float CAddonCallbacksGUI::Control_SettingsSlider_GetFloatValue(void *addonData, GUIHANDLE handle) +{ + CAddonCallbacks* helper = (CAddonCallbacks*) addonData; + if (!helper || !handle) + return 0.0f; + + CGUISettingsSliderControl *pControl = (CGUISettingsSliderControl*)handle; + return pControl->GetFloatValue(); +} + +void CAddonCallbacksGUI::Control_SettingsSlider_SetFloatInterval(void *addonData, GUIHANDLE handle, float fInterval) +{ + CAddonCallbacks* helper = (CAddonCallbacks*) addonData; + if (!helper || !handle) + return; + + CGUISettingsSliderControl *pControl = (CGUISettingsSliderControl*)handle; + pControl->SetFloatInterval(fInterval); +} + +/* + * GUI list item control callback functions + */ +GUIHANDLE CAddonCallbacksGUI::ListItem_Create(void *addonData, const char *label, const char *label2, const char *iconImage, const char *thumbnailImage, const char *path) +{ + CAddonCallbacks* helper = (CAddonCallbacks*) addonData; + if (!helper) + return NULL; + + // create CFileItem + CFileItem *pItem = new CFileItem(); + if (!pItem) + return NULL; + + if (label) + pItem->SetLabel(label); + if (label2) + pItem->SetLabel2(label2); + if (iconImage) + pItem->SetIconImage(iconImage); + if (thumbnailImage) + pItem->SetArt("thumb", thumbnailImage); + if (path) + pItem->SetPath(path); + + return pItem; +} + +const char* CAddonCallbacksGUI::ListItem_GetLabel(void *addonData, GUIHANDLE handle) +{ + CAddonCallbacks* helper = (CAddonCallbacks*) addonData; + if (!helper || !handle) + return NULL; + + std::string string = ((CFileItem*)handle)->GetLabel(); + char *buffer = (char*) malloc (string.length()+1); + strcpy(buffer, string.c_str()); + return buffer; +} + +void CAddonCallbacksGUI::ListItem_SetLabel(void *addonData, GUIHANDLE handle, const char *label) +{ + CAddonCallbacks* helper = (CAddonCallbacks*) addonData; + if (!helper || !handle) + return; + + ((CFileItem*)handle)->SetLabel(label); +} + +const char* CAddonCallbacksGUI::ListItem_GetLabel2(void *addonData, GUIHANDLE handle) +{ + CAddonCallbacks* helper = (CAddonCallbacks*) addonData; + if (!helper || !handle) + return NULL; + + std::string string = ((CFileItem*)handle)->GetLabel2(); + + char *buffer = (char*) malloc (string.length()+1); + strcpy(buffer, string.c_str()); + return buffer; +} + +void CAddonCallbacksGUI::ListItem_SetLabel2(void *addonData, GUIHANDLE handle, const char *label) +{ + CAddonCallbacks* helper = (CAddonCallbacks*) addonData; + if (!helper || !handle) + return; + + ((CFileItem*)handle)->SetLabel2(label); +} + +void CAddonCallbacksGUI::ListItem_SetIconImage(void *addonData, GUIHANDLE handle, const char *image) +{ + CAddonCallbacks* helper = (CAddonCallbacks*) addonData; + if (!helper || !handle) + return; + + ((CFileItem*)handle)->SetIconImage(image); +} + +void CAddonCallbacksGUI::ListItem_SetThumbnailImage(void *addonData, GUIHANDLE handle, const char *image) +{ + CAddonCallbacks* helper = (CAddonCallbacks*) addonData; + if (!helper || !handle) + return; + + ((CFileItem*)handle)->SetArt("thumb", image); +} + +void CAddonCallbacksGUI::ListItem_SetInfo(void *addonData, GUIHANDLE handle, const char *info) +{ + CAddonCallbacks* helper = (CAddonCallbacks*) addonData; + if (!helper || !handle) + return; + +} + +void CAddonCallbacksGUI::ListItem_SetProperty(void *addonData, GUIHANDLE handle, const char *key, const char *value) +{ + CAddonCallbacks* helper = (CAddonCallbacks*) addonData; + if (!helper || !handle) + return; + + ((CFileItem*)handle)->SetProperty(key, value); +} + +const char* CAddonCallbacksGUI::ListItem_GetProperty(void *addonData, GUIHANDLE handle, const char *key) +{ + CAddonCallbacks* helper = (CAddonCallbacks*) addonData; + if (!helper || !handle) + return NULL; + + string string = ((CFileItem*)handle)->GetProperty(key).asString(); + char *buffer = (char*) malloc (string.length()+1); + strcpy(buffer, string.c_str()); + return buffer; +} + +void CAddonCallbacksGUI::ListItem_SetPath(void *addonData, GUIHANDLE handle, const char *path) +{ + CAddonCallbacks* helper = (CAddonCallbacks*) addonData; + if (!helper || !handle) + return; + + ((CFileItem*)handle)->SetPath(path); +} + +void CAddonCallbacksGUI::RenderAddon_SetCallbacks(void *addonData, GUIHANDLE handle, GUIHANDLE clienthandle, bool (*createCB)(GUIHANDLE,int,int,int,int,void*), void (*renderCB)(GUIHANDLE), void (*stopCB)(GUIHANDLE), bool (*dirtyCB)(GUIHANDLE)) +{ + CAddonCallbacks* helper = (CAddonCallbacks*) addonData; + if (!helper || !handle) + return; + + CGUIAddonRenderingControl *pAddonControl = (CGUIAddonRenderingControl*)handle; + + Lock(); + pAddonControl->m_clientHandle = clienthandle; + pAddonControl->CBCreate = createCB; + pAddonControl->CBRender = renderCB; + pAddonControl->CBStop = stopCB; + pAddonControl->CBDirty = dirtyCB; + Unlock(); + + pAddonControl->m_pControl->InitCallback(pAddonControl); +} + +void CAddonCallbacksGUI::RenderAddon_Delete(void *addonData, GUIHANDLE handle) +{ + CAddonCallbacks* helper = (CAddonCallbacks*) addonData; + if (!helper || !handle) + return; + + CGUIAddonRenderingControl *pAddonControl = (CGUIAddonRenderingControl*)handle; + + Lock(); + pAddonControl->Delete(); + Unlock(); +} + +/*! @name GUI Keyboard functions */ +//@{ +bool CAddonCallbacksGUI::Dialog_Keyboard_ShowAndGetInputWithHead(char &aTextString, unsigned int iMaxStringSize, const char *strHeading, bool allowEmptyResult, bool hiddenInput, unsigned int autoCloseMs) +{ + std::string str = &aTextString; + bool bRet = CGUIKeyboardFactory::ShowAndGetInput(str, strHeading, allowEmptyResult, hiddenInput, autoCloseMs); + if (bRet) + strncpy(&aTextString, str.c_str(), iMaxStringSize); + return bRet; +} + +bool CAddonCallbacksGUI::Dialog_Keyboard_ShowAndGetInput(char &aTextString, unsigned int iMaxStringSize, bool allowEmptyResult, unsigned int autoCloseMs) +{ + std::string str = &aTextString; + bool bRet = CGUIKeyboardFactory::ShowAndGetInput(str, allowEmptyResult, autoCloseMs); + if (bRet) + strncpy(&aTextString, str.c_str(), iMaxStringSize); + return bRet; +} + +bool CAddonCallbacksGUI::Dialog_Keyboard_ShowAndGetNewPasswordWithHead(char &strNewPassword, unsigned int iMaxStringSize, const char *strHeading, bool allowEmptyResult, unsigned int autoCloseMs) +{ + std::string str = &strNewPassword; + bool bRet = CGUIKeyboardFactory::ShowAndGetNewPassword(str, strHeading, allowEmptyResult, autoCloseMs); + if (bRet) + strncpy(&strNewPassword, str.c_str(), iMaxStringSize); + return bRet; +} + +bool CAddonCallbacksGUI::Dialog_Keyboard_ShowAndGetNewPassword(char &strNewPassword, unsigned int iMaxStringSize, unsigned int autoCloseMs) +{ + std::string str = &strNewPassword; + bool bRet = CGUIKeyboardFactory::ShowAndGetNewPassword(str, autoCloseMs); + if (bRet) + strncpy(&strNewPassword, str.c_str(), iMaxStringSize); + return bRet; +} + +bool CAddonCallbacksGUI::Dialog_Keyboard_ShowAndVerifyNewPasswordWithHead(char &strNewPassword, unsigned int iMaxStringSize, const char *strHeading, bool allowEmptyResult, unsigned int autoCloseMs) +{ + std::string str = &strNewPassword; + bool bRet = CGUIKeyboardFactory::ShowAndVerifyNewPassword(str, strHeading, allowEmptyResult, autoCloseMs); + if (bRet) + strncpy(&strNewPassword, str.c_str(), iMaxStringSize); + return bRet; +} + +bool CAddonCallbacksGUI::Dialog_Keyboard_ShowAndVerifyNewPassword(char &strNewPassword, unsigned int iMaxStringSize, unsigned int autoCloseMs) +{ + std::string str = &strNewPassword; + bool bRet = CGUIKeyboardFactory::ShowAndVerifyNewPassword(str, autoCloseMs); + if (bRet) + strncpy(&strNewPassword, str.c_str(), iMaxStringSize); + return bRet; +} + +int CAddonCallbacksGUI::Dialog_Keyboard_ShowAndVerifyPassword(char &strPassword, unsigned int iMaxStringSize, const char *strHeading, int iRetries, unsigned int autoCloseMs) +{ + std::string str = &strPassword; + int iRet = CGUIKeyboardFactory::ShowAndVerifyPassword(str, strHeading, iRetries, autoCloseMs); + if (iRet) + strncpy(&strPassword, str.c_str(), iMaxStringSize); + return iRet; +} + +bool CAddonCallbacksGUI::Dialog_Keyboard_ShowAndGetFilter(char &aTextString, unsigned int iMaxStringSize, bool searching, unsigned int autoCloseMs) +{ + std::string strText = &aTextString; + bool bRet = CGUIKeyboardFactory::ShowAndGetFilter(strText, searching, autoCloseMs); + if (bRet) + strncpy(&aTextString, strText.c_str(), iMaxStringSize); + return bRet; +} + +bool CAddonCallbacksGUI::Dialog_Keyboard_SendTextToActiveKeyboard(const char *aTextString, bool closeKeyboard) +{ + return CGUIKeyboardFactory::SendTextToActiveKeyboard(aTextString, closeKeyboard); +} + +bool CAddonCallbacksGUI::Dialog_Keyboard_isKeyboardActivated() +{ + return CGUIKeyboardFactory::isKeyboardActivated(); +} +//@} + +/*! @name GUI Numeric functions */ +//@{ +bool CAddonCallbacksGUI::Dialog_Numeric_ShowAndVerifyNewPassword(char &strNewPassword, unsigned int iMaxStringSize) +{ + std::string str = &strNewPassword; + bool bRet = CGUIDialogNumeric::ShowAndVerifyNewPassword(str); + if (bRet) + strncpy(&strNewPassword, str.c_str(), iMaxStringSize); + return bRet; +} + +int CAddonCallbacksGUI::Dialog_Numeric_ShowAndVerifyPassword(char &strPassword, unsigned int iMaxStringSize, const char *strHeading, int iRetries) +{ + std::string str = &strPassword; + int bRet = CGUIDialogNumeric::ShowAndVerifyPassword(str, strHeading, iRetries); + if (bRet) + strncpy(&strPassword, str.c_str(), iMaxStringSize); + return bRet; +} + +bool CAddonCallbacksGUI::Dialog_Numeric_ShowAndVerifyInput(char &strPassword, unsigned int iMaxStringSize, const char *strHeading, bool bGetUserInput) +{ + std::string str = &strPassword; + bool bRet = CGUIDialogNumeric::ShowAndVerifyInput(str, strHeading, bGetUserInput); + if (bRet) + strncpy(&strPassword, str.c_str(), iMaxStringSize); + return bRet; +} + +bool CAddonCallbacksGUI::Dialog_Numeric_ShowAndGetTime(tm &time, const char *strHeading) +{ + SYSTEMTIME systemTime; + CDateTime dateTime(time); + dateTime.GetAsSystemTime(systemTime); + if (CGUIDialogNumeric::ShowAndGetTime(systemTime, strHeading)) + { + dateTime = systemTime; + dateTime.GetAsTm(time); + return true; + } + return false; +} + +bool CAddonCallbacksGUI::Dialog_Numeric_ShowAndGetDate(tm &date, const char *strHeading) +{ + SYSTEMTIME systemTime; + CDateTime dateTime(date); + dateTime.GetAsSystemTime(systemTime); + if (CGUIDialogNumeric::ShowAndGetDate(systemTime, strHeading)) + { + dateTime = systemTime; + dateTime.GetAsTm(date); + return true; + } + return false; +} + +bool CAddonCallbacksGUI::Dialog_Numeric_ShowAndGetIPAddress(char &strIPAddress, unsigned int iMaxStringSize, const char *strHeading) +{ + std::string strIP = &strIPAddress; + bool bRet = CGUIDialogNumeric::ShowAndGetIPAddress(strIP, strHeading); + if (bRet) + strncpy(&strIPAddress, strIP.c_str(), iMaxStringSize); + return bRet; +} + +bool CAddonCallbacksGUI::Dialog_Numeric_ShowAndGetNumber(char &strInput, unsigned int iMaxStringSize, const char *strHeading, unsigned int iAutoCloseTimeoutMs) +{ + std::string str = &strInput; + bool bRet = CGUIDialogNumeric::ShowAndGetNumber(str, strHeading, iAutoCloseTimeoutMs); + if (bRet) + strncpy(&strInput, str.c_str(), iMaxStringSize); + return bRet; +} + +bool CAddonCallbacksGUI::Dialog_Numeric_ShowAndGetSeconds(char &timeString, unsigned int iMaxStringSize, const char *strHeading) +{ + std::string str = &timeString; + bool bRet = CGUIDialogNumeric::ShowAndGetSeconds(str, strHeading); + if (bRet) + strncpy(&timeString, str.c_str(), iMaxStringSize); + return bRet; +} +//@} + +/*! @name GUI File browser functions */ +//@{ +bool CAddonCallbacksGUI::Dialog_FileBrowser_ShowAndGetFile(const char *directory, const char *mask, const char *heading, char &path, unsigned int iMaxStringSize, bool useThumbs, bool useFileDirectories, bool singleList) +{ + std::string strPath = &path; + bool bRet = CGUIDialogFileBrowser::ShowAndGetFile(directory, mask, heading, strPath, useThumbs, useFileDirectories, singleList); + if (bRet) + strncpy(&path, strPath.c_str(), iMaxStringSize); + return bRet; +} +//@} + +/*! @name GUI OK Dialog */ +//@{ +void CAddonCallbacksGUI::Dialog_OK_ShowAndGetInputSingleText(const char *heading, const char *text) +{ + CGUIDialogOK::ShowAndGetInput(heading, text); +} + +void CAddonCallbacksGUI::Dialog_OK_ShowAndGetInputLineText(const char *heading, const char *line0, const char *line1, const char *line2) +{ + CGUIDialogOK::ShowAndGetInput(heading, line0, line1, line2); +} +//@} + +/*! @name GUI Yes No Dialog */ +//@{ +bool CAddonCallbacksGUI::Dialog_YesNo_ShowAndGetInputSingleText(const char *heading, const char *text, bool& bCanceled, const char *noLabel, const char *yesLabel) +{ + return CGUIDialogYesNo::ShowAndGetInput(heading, text, bCanceled, noLabel, yesLabel); +} + +bool CAddonCallbacksGUI::Dialog_YesNo_ShowAndGetInputLineText(const char *heading, const char *line0, const char *line1, const char *line2, const char *noLabel, const char *yesLabel) +{ + return CGUIDialogYesNo::ShowAndGetInput(heading, line0, line1, line2, noLabel, yesLabel); +} + +bool CAddonCallbacksGUI::Dialog_YesNo_ShowAndGetInputLineButtonText(const char *heading, const char *line0, const char *line1, const char *line2, bool &bCanceled, const char *noLabel, const char *yesLabel) +{ + return CGUIDialogYesNo::ShowAndGetInput(heading, line0, line1, line2, bCanceled, noLabel, yesLabel); +} +//@} + +/*! @name GUI Text viewer Dialog */ +//@{ +void CAddonCallbacksGUI::Dialog_TextViewer(const char *heading, const char *text) +{ + CGUIDialogTextViewer* pDialog = (CGUIDialogTextViewer*)g_windowManager.GetWindow(WINDOW_DIALOG_TEXT_VIEWER); + pDialog->SetHeading(heading); + pDialog->SetText(text); + pDialog->DoModal(); +} +//@} + +/*! @name GUI select Dialog */ +//@{ +int CAddonCallbacksGUI::Dialog_Select(const char *heading, const char *entries[], unsigned int size, int selected) +{ + CGUIDialogSelect* pDialog = (CGUIDialogSelect*)g_windowManager.GetWindow(WINDOW_DIALOG_SELECT); + pDialog->Reset(); + pDialog->SetHeading(heading); + + for (unsigned int i = 0; i < size; i++) + pDialog->Add(entries[i]); + + if (selected > 0) + pDialog->SetSelected(selected); + + pDialog->DoModal(); + return pDialog->GetSelectedLabel(); +} +//@} + +CGUIAddonWindow::CGUIAddonWindow(int id, const std::string& strXML, CAddon* addon) + : CGUIMediaWindow(id, strXML.c_str()) + , m_iWindowId(id) + , m_iOldWindowId(0) + , m_bModal(false) + , m_bIsDialog(false) + , m_actionEvent(true) + , m_addon(addon) +{ + m_loadType = LOAD_ON_GUI_INIT; + CBOnInit = NULL; + CBOnFocus = NULL; + CBOnClick = NULL; + CBOnAction = NULL; +} + +CGUIAddonWindow::~CGUIAddonWindow(void) +{ +} + +bool CGUIAddonWindow::OnAction(const CAction &action) +{ + // Let addon decide whether it wants to hande action first + if (CBOnAction && CBOnAction(m_clientHandle, action.GetID())) + return true; + + return CGUIWindow::OnAction(action); +} + +bool CGUIAddonWindow::OnMessage(CGUIMessage& message) +{ + // TODO: We shouldn't be dropping down to CGUIWindow in any of this ideally. + // We have to make up our minds about what python should be doing and + // what this side of things should be doing + switch (message.GetMessage()) + { + case GUI_MSG_WINDOW_DEINIT: + { + return CGUIMediaWindow::OnMessage(message); + } + break; + + case GUI_MSG_WINDOW_INIT: + { + CGUIMediaWindow::OnMessage(message); + if (CBOnInit) + CBOnInit(m_clientHandle); + + return true; + } + break; + + case GUI_MSG_SETFOCUS: + { + if (m_viewControl.HasControl(message.GetControlId()) && m_viewControl.GetCurrentControl() != (int)message.GetControlId()) + { + m_viewControl.SetFocused(); + return true; + } + // check if our focused control is one of our category buttons + int iControl = message.GetControlId(); + if (CBOnFocus) + { + CBOnFocus(m_clientHandle, iControl); + } + } + break; + + case GUI_MSG_FOCUSED: + { + if (HasID(message.GetSenderId()) && CBOnFocus) + { + CBOnFocus(m_clientHandle, message.GetControlId()); + } + } + break; + + case GUI_MSG_CLICKED: + { + int iControl=message.GetSenderId(); + // Handle Sort/View internally. Scripters shouldn't use ID 2, 3 or 4. + if (iControl == CONTROL_BTNSORTASC) // sort asc + { + CLog::Log(LOGINFO, "WindowXML: Internal asc/dsc button not implemented"); + /*if (m_guiState.get()) + m_guiState->SetNextSortOrder(); + UpdateFileList();*/ + return true; + } + else if (iControl == CONTROL_BTNSORTBY) // sort by + { + CLog::Log(LOGINFO, "WindowXML: Internal sort button not implemented"); + /*if (m_guiState.get()) + m_guiState->SetNextSortMethod(); + UpdateFileList();*/ + return true; + } + + if (CBOnClick && iControl && iControl != (int)this->GetID()) + { + CGUIControl* controlClicked = (CGUIControl*)this->GetControl(iControl); + + // The old python way used to check list AND SELECITEM method or if its a button, checkmark. + // Its done this way for now to allow other controls without a python version like togglebutton to still raise a onAction event + if (controlClicked) // Will get problems if we the id is not on the window and we try to do GetControlType on it. So check to make sure it exists + { + if ((controlClicked->IsContainer() && (message.GetParam1() == ACTION_SELECT_ITEM || + message.GetParam1() == ACTION_MOUSE_LEFT_CLICK)) || + !controlClicked->IsContainer()) + { + if (CBOnClick(m_clientHandle, iControl)) + return true; + } + else if (controlClicked->IsContainer() && message.GetParam1() == ACTION_MOUSE_RIGHT_CLICK) + { +// PyXBMCAction* inf = new PyXBMCAction; +// inf->pObject = Action_FromAction(CAction(ACTION_CONTEXT_MENU)); +// inf->pCallbackWindow = pCallbackWindow; +// +// // aquire lock? +// PyXBMC_AddPendingCall(Py_XBMC_Event_OnAction, inf); +// PulseActionEvent(); + } + } + } + } + break; + } + + return CGUIMediaWindow::OnMessage(message); +} + +void CGUIAddonWindow::AllocResources(bool forceLoad /*= FALSE */) +{ + std::string tmpDir = URIUtils::GetDirectory(GetProperty("xmlfile").asString()); + std::string fallbackMediaPath; + URIUtils::GetParentPath(tmpDir, fallbackMediaPath); + URIUtils::RemoveSlashAtEnd(fallbackMediaPath); + m_mediaDir = fallbackMediaPath; + + //CLog::Log(LOGDEBUG, "CGUIPythonWindowXML::AllocResources called: %s", fallbackMediaPath.c_str()); + g_TextureManager.AddTexturePath(m_mediaDir); + CGUIMediaWindow::AllocResources(forceLoad); + g_TextureManager.RemoveTexturePath(m_mediaDir); +} + +void CGUIAddonWindow::FreeResources(bool forceUnLoad /*= FALSE */) +{ + CGUIMediaWindow::FreeResources(forceUnLoad); +} + +void CGUIAddonWindow::Render() +{ + g_TextureManager.AddTexturePath(m_mediaDir); + CGUIMediaWindow::Render(); + g_TextureManager.RemoveTexturePath(m_mediaDir); +} + +void CGUIAddonWindow::Update() +{ +} + +void CGUIAddonWindow::AddItem(CFileItemPtr fileItem, int itemPosition) +{ + if (itemPosition == -1 || itemPosition > m_vecItems->Size()) + { + m_vecItems->Add(fileItem); + } + else if (itemPosition < -1 && !(itemPosition-1 < m_vecItems->Size())) + { + m_vecItems->AddFront(fileItem,0); + } + else + { + m_vecItems->AddFront(fileItem,itemPosition); + } + m_viewControl.SetItems(*m_vecItems); + UpdateButtons(); +} + +void CGUIAddonWindow::RemoveItem(int itemPosition) +{ + m_vecItems->Remove(itemPosition); + m_viewControl.SetItems(*m_vecItems); + UpdateButtons(); +} + +int CGUIAddonWindow::GetCurrentListPosition() +{ + return m_viewControl.GetSelectedItem(); +} + +void CGUIAddonWindow::SetCurrentListPosition(int item) +{ + m_viewControl.SetSelectedItem(item); +} + +int CGUIAddonWindow::GetListSize() +{ + return m_vecItems->Size(); +} + +CFileItemPtr CGUIAddonWindow::GetListItem(int position) +{ + if (position < 0 || position >= m_vecItems->Size()) return CFileItemPtr(); + return m_vecItems->Get(position); +} + +void CGUIAddonWindow::ClearList() +{ + ClearFileItems(); + + m_viewControl.SetItems(*m_vecItems); + UpdateButtons(); +} + +void CGUIAddonWindow::GetContextButtons(int itemNumber, CContextButtons &buttons) +{ + // maybe on day we can make an easy way to do this context menu + // with out this method overriding the MediaWindow version, it will display 'Add to Favorites' +} + +void CGUIAddonWindow::WaitForActionEvent(unsigned int timeout) +{ + m_actionEvent.WaitMSec(timeout); + m_actionEvent.Reset(); +} + +void CGUIAddonWindow::PulseActionEvent() +{ + m_actionEvent.Set(); +} + +bool CGUIAddonWindow::OnClick(int iItem) +{ + // Hook Over calling CGUIMediaWindow::OnClick(iItem) results in it trying to PLAY the file item + // which if its not media is BAD and 99 out of 100 times undesireable. + return false; +} + +// SetupShares(); +/* + CGUIMediaWindow::OnWindowLoaded() calls SetupShares() so override it +and just call UpdateButtons(); +*/ +void CGUIAddonWindow::SetupShares() +{ + UpdateButtons(); +} + + +CGUIAddonWindowDialog::CGUIAddonWindowDialog(int id, const std::string& strXML, CAddon* addon) +: CGUIAddonWindow(id,strXML,addon) +{ + m_bRunning = false; + m_bIsDialog = true; +} + +CGUIAddonWindowDialog::~CGUIAddonWindowDialog(void) +{ +} + +bool CGUIAddonWindowDialog::OnMessage(CGUIMessage &message) +{ + if (message.GetMessage() == GUI_MSG_WINDOW_DEINIT) + { + CGUIWindow *pWindow = g_windowManager.GetWindow(g_windowManager.GetActiveWindow()); + if (pWindow) + g_windowManager.ShowOverlay(pWindow->GetOverlayState()); + return CGUIWindow::OnMessage(message); + } + return CGUIAddonWindow::OnMessage(message); +} + +void CGUIAddonWindowDialog::Show(bool show /* = true */) +{ + unsigned int iCount = g_graphicsContext.exit(); + ThreadMessage tMsg = {TMSG_GUI_ADDON_DIALOG, 1, show ? 1 : 0}; + tMsg.lpVoid = this; + CApplicationMessenger::Get().SendMessage(tMsg, true); + g_graphicsContext.restore(iCount); +} + +void CGUIAddonWindowDialog::Show_Internal(bool show /* = true */) +{ + if (show) + { + m_bModal = true; + m_bRunning = true; + g_windowManager.RouteToWindow(this); + + // active this window... + CGUIMessage msg(GUI_MSG_WINDOW_INIT, 0, 0, WINDOW_INVALID, m_iWindowId); + OnMessage(msg); + + // this dialog is derived from GUiMediaWindow + // make sure it is rendered last + m_renderOrder = 1; + while (m_bRunning && !g_application.m_bStop) + { + g_windowManager.ProcessRenderLoop(); + } + } + else // hide + { + m_bRunning = false; + + CGUIMessage msg(GUI_MSG_WINDOW_DEINIT,0,0); + OnMessage(msg); + + g_windowManager.RemoveDialog(GetID()); + } +} + +CGUIAddonRenderingControl::CGUIAddonRenderingControl(CGUIRenderingControl *pControl) +{ + m_pControl = pControl; + m_refCount = 1; +} + +bool CGUIAddonRenderingControl::Create(int x, int y, int w, int h, void *device) +{ + if (CBCreate) + { + if (CBCreate(m_clientHandle, x, y, w, h, device)) + { + m_refCount++; + return true; + } + } + return false; +} + +void CGUIAddonRenderingControl::Render() +{ + if (CBRender) + { + g_graphicsContext.BeginPaint(); + CBRender(m_clientHandle); + g_graphicsContext.EndPaint(); + } +} + +void CGUIAddonRenderingControl::Stop() +{ + if (CBStop) + { + CBStop(m_clientHandle); + } + m_refCount--; + if (m_refCount <= 0) + delete this; +} + +void CGUIAddonRenderingControl::Delete() +{ + m_refCount--; + if (m_refCount <= 0) + delete this; +} + +bool CGUIAddonRenderingControl::IsDirty() +{ + bool ret = true; + if (CBDirty) + { + ret = CBDirty(m_clientHandle); + } + return ret; +} + +}; /* namespace ADDON */ diff --git a/xbmc/addons/AddonCallbacksGUI.h b/xbmc/addons/AddonCallbacksGUI.h new file mode 100644 index 0000000..ae032a7 --- /dev/null +++ b/xbmc/addons/AddonCallbacksGUI.h @@ -0,0 +1,272 @@ +#pragma once +/* + * Copyright (C) 2012-2013 Team XBMC + * http://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, see + * . + * + */ + + +#include "AddonCallbacks.h" +#include "windows/GUIMediaWindow.h" +#include "threads/Event.h" +#include "guilib/IRenderingCallback.h" + +class CGUISpinControlEx; +class CGUIButtonControl; +class CGUIRadioButtonControl; +class CGUISliderControl; +class CGUISettingsSliderControl; +class CGUIEditControl; +class CGUIRenderingControl; + +namespace ADDON +{ + +class CAddonCallbacksGUI +{ +public: + CAddonCallbacksGUI(CAddon* addon); + ~CAddonCallbacksGUI(); + + /**! \name General Functions */ + CB_GUILib *GetCallbacks() { return m_callbacks; } + + static void Lock(); + static void Unlock(); + static int GetScreenHeight(); + static int GetScreenWidth(); + static int GetVideoResolution(); + + static GUIHANDLE Window_New(void *addonData, const char *xmlFilename, const char *defaultSkin, bool forceFallback, bool asDialog); + static void Window_Delete(void *addonData, GUIHANDLE handle); + static void Window_SetCallbacks(void *addonData, GUIHANDLE handle, GUIHANDLE clienthandle, bool (*initCB)(GUIHANDLE), bool (*clickCB)(GUIHANDLE, int), bool (*focusCB)(GUIHANDLE, int), bool (*onActionCB)(GUIHANDLE handle, int)); + static bool Window_Show(void *addonData, GUIHANDLE handle); + static bool Window_Close(void *addonData, GUIHANDLE handle); + static bool Window_DoModal(void *addonData, GUIHANDLE handle); + static bool Window_SetFocusId(void *addonData, GUIHANDLE handle, int iControlId); + static int Window_GetFocusId(void *addonData, GUIHANDLE handle); + static bool Window_SetCoordinateResolution(void *addonData, GUIHANDLE handle, int res); + static void Window_SetProperty(void *addonData, GUIHANDLE handle, const char *key, const char *value); + static void Window_SetPropertyInt(void *addonData, GUIHANDLE handle, const char *key, int value); + static void Window_SetPropertyBool(void *addonData, GUIHANDLE handle, const char *key, bool value); + static void Window_SetPropertyDouble(void *addonData, GUIHANDLE handle, const char *key, double value); + static const char * Window_GetProperty(void *addonData, GUIHANDLE handle, const char *key); + static int Window_GetPropertyInt(void *addonData, GUIHANDLE handle, const char *key); + static bool Window_GetPropertyBool(void *addonData, GUIHANDLE handle, const char *key); + static double Window_GetPropertyDouble(void *addonData, GUIHANDLE handle, const char *key); + static void Window_ClearProperties(void *addonData, GUIHANDLE handle); + static int Window_GetListSize(void *addonData, GUIHANDLE handle); + static void Window_ClearList(void *addonData, GUIHANDLE handle); + static GUIHANDLE Window_AddItem(void *addonData, GUIHANDLE handle, GUIHANDLE item, int itemPosition); + static GUIHANDLE Window_AddStringItem(void *addonData, GUIHANDLE handle, const char *itemName, int itemPosition); + static void Window_RemoveItem(void *addonData, GUIHANDLE handle, int itemPosition); + static GUIHANDLE Window_GetListItem(void *addonData, GUIHANDLE handle, int listPos); + static void Window_SetCurrentListPosition(void *addonData, GUIHANDLE handle, int listPos); + static int Window_GetCurrentListPosition(void *addonData, GUIHANDLE handle); + static GUIHANDLE Window_GetControl_Spin(void *addonData, GUIHANDLE handle, int controlId); + static GUIHANDLE Window_GetControl_Button(void *addonData, GUIHANDLE handle, int controlId); + static GUIHANDLE Window_GetControl_RadioButton(void *addonData, GUIHANDLE handle, int controlId); + static GUIHANDLE Window_GetControl_Edit(void *addonData, GUIHANDLE handle, int controlId); + static GUIHANDLE Window_GetControl_Progress(void *addonData, GUIHANDLE handle, int controlId); + static GUIHANDLE Window_GetControl_RenderAddon(void *addonData, GUIHANDLE handle, int controlId); + static void Window_SetControlLabel(void *addonData, GUIHANDLE handle, int controlId, const char *label); + static void Window_MarkDirtyRegion(void *addonData, GUIHANDLE handle); + static void Control_Spin_SetVisible(void *addonData, GUIHANDLE spinhandle, bool yesNo); + static void Control_Spin_SetText(void *addonData, GUIHANDLE spinhandle, const char *label); + static void Control_Spin_Clear(void *addonData, GUIHANDLE spinhandle); + static void Control_Spin_AddLabel(void *addonData, GUIHANDLE spinhandle, const char *label, int iValue); + static int Control_Spin_GetValue(void *addonData, GUIHANDLE spinhandle); + static void Control_Spin_SetValue(void *addonData, GUIHANDLE spinhandle, int iValue); + static void Control_RadioButton_SetVisible(void *addonData, GUIHANDLE handle, bool yesNo); + static void Control_RadioButton_SetText(void *addonData, GUIHANDLE handle, const char *label); + static void Control_RadioButton_SetSelected(void *addonData, GUIHANDLE handle, bool yesNo); + static bool Control_RadioButton_IsSelected(void *addonData, GUIHANDLE handle); + static void Control_Progress_SetPercentage(void *addonData, GUIHANDLE handle, float fPercent); + static float Control_Progress_GetPercentage(void *addonData, GUIHANDLE handle); + static void Control_Progress_SetInfo(void *addonData, GUIHANDLE handle, int iInfo); + static int Control_Progress_GetInfo(void *addonData, GUIHANDLE handle); + static const char * Control_Progress_GetDescription(void *addonData, GUIHANDLE handle); + + static GUIHANDLE Window_GetControl_Slider(void *addonData, GUIHANDLE handle, int controlId); + static void Control_Slider_SetVisible(void *addonData, GUIHANDLE handle, bool yesNo); + static const char * Control_Slider_GetDescription(void *addonData, GUIHANDLE handle); + static void Control_Slider_SetIntRange(void *addonData, GUIHANDLE handle, int iStart, int iEnd); + static void Control_Slider_SetIntValue(void *addonData, GUIHANDLE handle, int iValue); + static int Control_Slider_GetIntValue(void *addonData, GUIHANDLE handle); + static void Control_Slider_SetIntInterval(void *addonData, GUIHANDLE handle, int iInterval); + static void Control_Slider_SetPercentage(void *addonData, GUIHANDLE handle, float fPercent); + static float Control_Slider_GetPercentage(void *addonData, GUIHANDLE handle); + static void Control_Slider_SetFloatRange(void *addonData, GUIHANDLE handle, float fStart, float fEnd); + static void Control_Slider_SetFloatValue(void *addonData, GUIHANDLE handle, float fValue); + static float Control_Slider_GetFloatValue(void *addonData, GUIHANDLE handle); + static void Control_Slider_SetFloatInterval(void *addonData, GUIHANDLE handle, float fInterval); + + static GUIHANDLE Window_GetControl_SettingsSlider(void *addonData, GUIHANDLE handle, int controlId); + static void Control_SettingsSlider_SetVisible(void *addonData, GUIHANDLE handle, bool yesNo); + static void Control_SettingsSlider_SetText(void *addonData, GUIHANDLE handle, const char *label); + static const char * Control_SettingsSlider_GetDescription(void *addonData, GUIHANDLE handle); + static void Control_SettingsSlider_SetIntRange(void *addonData, GUIHANDLE handle, int iStart, int iEnd); + static void Control_SettingsSlider_SetIntValue(void *addonData, GUIHANDLE handle, int iValue); + static int Control_SettingsSlider_GetIntValue(void *addonData, GUIHANDLE handle); + static void Control_SettingsSlider_SetIntInterval(void *addonData, GUIHANDLE handle, int iInterval); + static void Control_SettingsSlider_SetPercentage(void *addonData, GUIHANDLE handle, float fPercent); + static float Control_SettingsSlider_GetPercentage(void *addonData, GUIHANDLE handle); + static void Control_SettingsSlider_SetFloatRange(void *addonData, GUIHANDLE handle, float fStart, float fEnd); + static void Control_SettingsSlider_SetFloatValue(void *addonData, GUIHANDLE handle, float fValue); + static float Control_SettingsSlider_GetFloatValue(void *addonData, GUIHANDLE handle); + static void Control_SettingsSlider_SetFloatInterval(void *addonData, GUIHANDLE handle, float fInterval); + + static GUIHANDLE ListItem_Create(void *addonData, const char *label, const char *label2, const char *iconImage, const char *thumbnailImage, const char *path); + static const char * ListItem_GetLabel(void *addonData, GUIHANDLE handle); + static void ListItem_SetLabel(void *addonData, GUIHANDLE handle, const char *label); + static const char * ListItem_GetLabel2(void *addonData, GUIHANDLE handle); + static void ListItem_SetLabel2(void *addonData, GUIHANDLE handle, const char *label); + static void ListItem_SetIconImage(void *addonData, GUIHANDLE handle, const char *image); + static void ListItem_SetThumbnailImage(void *addonData, GUIHANDLE handle, const char *image); + static void ListItem_SetInfo(void *addonData, GUIHANDLE handle, const char *info); + static void ListItem_SetProperty(void *addonData, GUIHANDLE handle, const char *key, const char *value); + static const char * ListItem_GetProperty(void *addonData, GUIHANDLE handle, const char *key); + static void ListItem_SetPath(void *addonData, GUIHANDLE handle, const char *path); + static void RenderAddon_SetCallbacks(void *addonData, GUIHANDLE handle, GUIHANDLE clienthandle, bool (*createCB)(GUIHANDLE,int,int,int,int,void*), void (*renderCB)(GUIHANDLE), void (*stopCB)(GUIHANDLE), bool (*dirtyCB)(GUIHANDLE)); + static void RenderAddon_Delete(void *addonData, GUIHANDLE handle); + static void RenderAddon_MarkDirty(void *addonData, GUIHANDLE handle); + + static bool Dialog_Keyboard_ShowAndGetInput(char &aTextString, unsigned int iMaxStringSize, bool allowEmptyResult, unsigned int autoCloseMs); + static bool Dialog_Keyboard_ShowAndGetInputWithHead(char &aTextString, unsigned int iMaxStringSize, const char *heading, bool allowEmptyResult, bool hiddenInput, unsigned int autoCloseMs); + static bool Dialog_Keyboard_ShowAndGetNewPassword(char &strNewPassword, unsigned int iMaxStringSize, unsigned int autoCloseMs); + static bool Dialog_Keyboard_ShowAndGetNewPasswordWithHead(char &newPassword, unsigned int iMaxStringSize, const char *strHeading, bool allowEmptyResult, unsigned int autoCloseMs); + static bool Dialog_Keyboard_ShowAndVerifyNewPassword(char &strNewPassword, unsigned int iMaxStringSize, unsigned int autoCloseMs); + static bool Dialog_Keyboard_ShowAndVerifyNewPasswordWithHead(char &strNewPassword, unsigned int iMaxStringSize, const char *strHeading, bool allowEmpty, unsigned int autoCloseMs); + static int Dialog_Keyboard_ShowAndVerifyPassword(char &strPassword, unsigned int iMaxStringSize, const char *strHeading, int iRetries, unsigned int autoCloseMs); + static bool Dialog_Keyboard_ShowAndGetFilter(char &aTextString, unsigned int iMaxStringSize, bool searching, unsigned int autoCloseMs); + static bool Dialog_Keyboard_SendTextToActiveKeyboard(const char *aTextString, bool closeKeyboard); + static bool Dialog_Keyboard_isKeyboardActivated(); + + static bool Dialog_Numeric_ShowAndVerifyNewPassword(char &strNewPasswor, unsigned int iMaxStringSized); + static int Dialog_Numeric_ShowAndVerifyPassword(char &strPassword, unsigned int iMaxStringSize, const char *strHeading, int iRetries); + static bool Dialog_Numeric_ShowAndVerifyInput(char &strPassword, unsigned int iMaxStringSize, const char *strHeading, bool bGetUserInput); + static bool Dialog_Numeric_ShowAndGetTime(tm &time, const char *strHeading); + static bool Dialog_Numeric_ShowAndGetDate(tm &date, const char *strHeading); + static bool Dialog_Numeric_ShowAndGetIPAddress(char &strIPAddress, unsigned int iMaxStringSize, const char *strHeading); + static bool Dialog_Numeric_ShowAndGetNumber(char &strInput, unsigned int iMaxStringSize, const char *strHeading, unsigned int iAutoCloseTimeoutMs); + static bool Dialog_Numeric_ShowAndGetSeconds(char &timeString, unsigned int iMaxStringSize, const char *strHeading); + + static bool Dialog_FileBrowser_ShowAndGetFile(const char *directory, const char *mask, const char *heading, char &path, unsigned int iMaxStringSize, bool useThumbs, bool useFileDirectories, bool singleList); + + static void Dialog_OK_ShowAndGetInputSingleText(const char *heading, const char *text); + static void Dialog_OK_ShowAndGetInputLineText(const char *heading, const char *line0, const char *line1, const char *line2); + + static bool Dialog_YesNo_ShowAndGetInputSingleText(const char *heading, const char *text, bool& bCanceled, const char *noLabel, const char *yesLabel); + static bool Dialog_YesNo_ShowAndGetInputLineText(const char *heading, const char *line0, const char *line1, const char *line2, const char *noLabel, const char *yesLabel); + static bool Dialog_YesNo_ShowAndGetInputLineButtonText(const char *heading, const char *line0, const char *line1, const char *line2, bool &bCanceled, const char *noLabel, const char *yesLabel); + + static void Dialog_TextViewer(const char *heading, const char *text); + static int Dialog_Select(const char *heading, const char *entries[], unsigned int size, int selected); + +private: + CB_GUILib *m_callbacks; + CAddon *m_addon; +}; + +class CGUIAddonWindow : public CGUIMediaWindow +{ +friend class CAddonCallbacksGUI; + +public: + CGUIAddonWindow(int id, const std::string& strXML, CAddon* addon); + virtual ~CGUIAddonWindow(void); + + virtual bool OnMessage(CGUIMessage& message); + virtual bool OnAction(const CAction &action); + virtual void AllocResources(bool forceLoad = false); + virtual void FreeResources(bool forceUnLoad = false); + virtual void Render(); + void WaitForActionEvent(unsigned int timeout); + void PulseActionEvent(); + void AddItem(CFileItemPtr fileItem, int itemPosition); + void RemoveItem(int itemPosition); + void ClearList(); + CFileItemPtr GetListItem(int position); + int GetListSize(); + int GetCurrentListPosition(); + void SetCurrentListPosition(int item); + virtual bool OnClick(int iItem); + +protected: + virtual void Update(); + virtual void GetContextButtons(int itemNumber, CContextButtons &buttons); + void SetupShares(); + + bool (*CBOnInit)(GUIHANDLE cbhdl); + bool (*CBOnFocus)(GUIHANDLE cbhdl, int controlId); + bool (*CBOnClick)(GUIHANDLE cbhdl, int controlId); + bool (*CBOnAction)(GUIHANDLE cbhdl, int); + + GUIHANDLE m_clientHandle; + const int m_iWindowId; + int m_iOldWindowId; + bool m_bModal; + bool m_bIsDialog; + +private: + CEvent m_actionEvent; + CAddon *m_addon; + std::string m_mediaDir; +}; + +class CGUIAddonWindowDialog : public CGUIAddonWindow +{ +public: + CGUIAddonWindowDialog(int id, const std::string& strXML, CAddon* addon); + virtual ~CGUIAddonWindowDialog(void); + + void Show(bool show = true); + virtual bool OnMessage(CGUIMessage &message); + virtual bool IsDialogRunning() const { return m_bRunning; } + virtual bool IsDialog() const { return true;}; + virtual bool IsModalDialog() const { return true; }; + virtual bool IsMediaWindow() const { return false; }; + + void Show_Internal(bool show = true); + +private: + bool m_bRunning; +}; + +class CGUIAddonRenderingControl : public IRenderingCallback +{ +friend class CAddonCallbacksGUI; +public: + CGUIAddonRenderingControl(CGUIRenderingControl *pControl); + virtual ~CGUIAddonRenderingControl() {} + virtual bool Create(int x, int y, int w, int h, void *device); + virtual void Render(); + virtual void Stop(); + virtual bool IsDirty(); + virtual void Delete(); +protected: + bool (*CBCreate) (GUIHANDLE cbhdl, int x, int y, int w, int h, void *device); + void (*CBRender)(GUIHANDLE cbhdl); + void (*CBStop)(GUIHANDLE cbhdl); + bool (*CBDirty)(GUIHANDLE cbhdl); + + GUIHANDLE m_clientHandle; + CGUIRenderingControl *m_pControl; + int m_refCount; +}; + +}; /* namespace ADDON */ diff --git a/xbmc/addons/AddonCallbacksPVR.cpp b/xbmc/addons/AddonCallbacksPVR.cpp new file mode 100644 index 0000000..1d769e1 --- /dev/null +++ b/xbmc/addons/AddonCallbacksPVR.cpp @@ -0,0 +1,324 @@ +/* + * Copyright (C) 2012-2013 Team XBMC + * http://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, see + * . + * + */ + +#include "Application.h" +#include "AddonCallbacksPVR.h" +#include "settings/AdvancedSettings.h" +#include "utils/log.h" +#include "utils/StringUtils.h" +#include "dialogs/GUIDialogKaiToast.h" + +#include "epg/EpgContainer.h" +#include "pvr/PVRManager.h" +#include "pvr/channels/PVRChannelGroupsContainer.h" +#include "pvr/channels/PVRChannelGroupInternal.h" +#include "pvr/addons/PVRClient.h" +#include "pvr/recordings/PVRRecordings.h" +#include "pvr/timers/PVRTimers.h" +#include "pvr/timers/PVRTimerInfoTag.h" + +using namespace PVR; +using namespace EPG; + +namespace ADDON +{ + +CAddonCallbacksPVR::CAddonCallbacksPVR(CAddon* addon) +{ + m_addon = addon; + m_callbacks = new CB_PVRLib; + + /* write XBMC PVR specific add-on function addresses to callback table */ + m_callbacks->TransferEpgEntry = PVRTransferEpgEntry; + m_callbacks->TransferChannelEntry = PVRTransferChannelEntry; + m_callbacks->TransferTimerEntry = PVRTransferTimerEntry; + m_callbacks->TransferRecordingEntry = PVRTransferRecordingEntry; + m_callbacks->AddMenuHook = PVRAddMenuHook; + m_callbacks->Recording = PVRRecording; + m_callbacks->TriggerChannelUpdate = PVRTriggerChannelUpdate; + m_callbacks->TriggerChannelGroupsUpdate = PVRTriggerChannelGroupsUpdate; + m_callbacks->TriggerTimerUpdate = PVRTriggerTimerUpdate; + m_callbacks->TriggerRecordingUpdate = PVRTriggerRecordingUpdate; + m_callbacks->TriggerEpgUpdate = PVRTriggerEpgUpdate; + m_callbacks->FreeDemuxPacket = PVRFreeDemuxPacket; + m_callbacks->AllocateDemuxPacket = PVRAllocateDemuxPacket; + m_callbacks->TransferChannelGroup = PVRTransferChannelGroup; + m_callbacks->TransferChannelGroupMember = PVRTransferChannelGroupMember; +} + +CAddonCallbacksPVR::~CAddonCallbacksPVR() +{ + /* delete the callback table */ + delete m_callbacks; +} + +CPVRClient *CAddonCallbacksPVR::GetPVRClient(void *addonData) +{ + CAddonCallbacks *addon = static_cast(addonData); + if (!addon || !addon->GetHelperPVR()) + { + CLog::Log(LOGERROR, "PVR - %s - called with a null pointer", __FUNCTION__); + return NULL; + } + + return dynamic_cast(addon->GetHelperPVR()->m_addon); +} + +void CAddonCallbacksPVR::PVRTransferChannelGroup(void *addonData, const ADDON_HANDLE handle, const PVR_CHANNEL_GROUP *group) +{ + if (!handle) + { + CLog::Log(LOGERROR, "PVR - %s - invalid handler data", __FUNCTION__); + return; + } + + CPVRChannelGroups *xbmcGroups = static_cast(handle->dataAddress); + if (!group || !xbmcGroups) + { + CLog::Log(LOGERROR, "PVR - %s - invalid handler data", __FUNCTION__); + return; + } + + if (strlen(group->strGroupName) == 0) + { + CLog::Log(LOGERROR, "PVR - %s - empty group name", __FUNCTION__); + return; + } + + /* transfer this entry to the groups container */ + CPVRChannelGroup transferGroup(*group); + xbmcGroups->UpdateFromClient(transferGroup); +} + +void CAddonCallbacksPVR::PVRTransferChannelGroupMember(void *addonData, const ADDON_HANDLE handle, const PVR_CHANNEL_GROUP_MEMBER *member) +{ + if (!handle) + { + CLog::Log(LOGERROR, "PVR - %s - invalid handler data", __FUNCTION__); + return; + } + + CPVRClient *client = GetPVRClient(addonData); + CPVRChannelGroup *group = static_cast(handle->dataAddress); + if (!member || !client || !group) + { + CLog::Log(LOGERROR, "PVR - %s - invalid handler data", __FUNCTION__); + return; + } + + CPVRChannelPtr channel = g_PVRChannelGroups->GetByUniqueID(member->iChannelUniqueId, client->GetID()); + if (!channel) + { + CLog::Log(LOGERROR, "PVR - %s - cannot find group '%s' or channel '%d'", __FUNCTION__, member->strGroupName, member->iChannelUniqueId); + } + else if (group->IsRadio() == channel->IsRadio()) + { + /* transfer this entry to the group */ + group->AddToGroup(channel, member->iChannelNumber); + } +} + +void CAddonCallbacksPVR::PVRTransferEpgEntry(void *addonData, const ADDON_HANDLE handle, const EPG_TAG *epgentry) +{ + if (!handle) + { + CLog::Log(LOGERROR, "PVR - %s - invalid handler data", __FUNCTION__); + return; + } + + CEpg *xbmcEpg = static_cast(handle->dataAddress); + if (!xbmcEpg) + { + CLog::Log(LOGERROR, "PVR - %s - invalid handler data", __FUNCTION__); + return; + } + + /* transfer this entry to the epg */ + xbmcEpg->UpdateEntry(epgentry, handle->dataIdentifier == 1 /* update db */); +} + +void CAddonCallbacksPVR::PVRTransferChannelEntry(void *addonData, const ADDON_HANDLE handle, const PVR_CHANNEL *channel) +{ + if (!handle) + { + CLog::Log(LOGERROR, "PVR - %s - invalid handler data", __FUNCTION__); + return; + } + + CPVRClient *client = GetPVRClient(addonData); + CPVRChannelGroupInternal *xbmcChannels = static_cast(handle->dataAddress); + if (!channel || !client || !xbmcChannels) + { + CLog::Log(LOGERROR, "PVR - %s - invalid handler data", __FUNCTION__); + return; + } + + /* transfer this entry to the internal channels group */ + CPVRChannelPtr transferChannel(new CPVRChannel(*channel, client->GetID())); + xbmcChannels->UpdateFromClient(transferChannel); +} + +void CAddonCallbacksPVR::PVRTransferRecordingEntry(void *addonData, const ADDON_HANDLE handle, const PVR_RECORDING *recording) +{ + if (!handle) + { + CLog::Log(LOGERROR, "PVR - %s - invalid handler data", __FUNCTION__); + return; + } + + CPVRClient *client = GetPVRClient(addonData); + CPVRRecordings *xbmcRecordings = static_cast(handle->dataAddress); + if (!recording || !client || !xbmcRecordings) + { + CLog::Log(LOGERROR, "PVR - %s - invalid handler data", __FUNCTION__); + return; + } + + /* transfer this entry to the recordings container */ + CPVRRecordingPtr transferRecording(new CPVRRecording(*recording, client->GetID())); + xbmcRecordings->UpdateFromClient(transferRecording); +} + +void CAddonCallbacksPVR::PVRTransferTimerEntry(void *addonData, const ADDON_HANDLE handle, const PVR_TIMER *timer) +{ + if (!handle) + { + CLog::Log(LOGERROR, "PVR - %s - invalid handler data", __FUNCTION__); + return; + } + + CPVRClient *client = GetPVRClient(addonData); + CPVRTimers *xbmcTimers = static_cast(handle->dataAddress); + if (!timer || !client || !xbmcTimers) + { + CLog::Log(LOGERROR, "PVR - %s - invalid handler data", __FUNCTION__); + return; + } + + CPVRChannelPtr channel = g_PVRChannelGroups->GetByUniqueID(timer->iClientChannelUid, client->GetID()); + if (!channel) + { + CLog::Log(LOGERROR, "PVR - %s - cannot find channel %d on client %d", __FUNCTION__, timer->iClientChannelUid, client->GetID()); + return; + } + + /* transfer this entry to the timers container */ + CPVRTimerInfoTag transferTimer(*timer, channel, client->GetID()); + xbmcTimers->UpdateFromClient(transferTimer); +} + +void CAddonCallbacksPVR::PVRAddMenuHook(void *addonData, PVR_MENUHOOK *hook) +{ + CPVRClient *client = GetPVRClient(addonData); + if (!hook || !client) + { + CLog::Log(LOGERROR, "PVR - %s - invalid handler data", __FUNCTION__); + return; + } + + PVR_MENUHOOKS *hooks = client->GetMenuHooks(); + if (hooks) + { + PVR_MENUHOOK hookInt; + hookInt.iHookId = hook->iHookId; + hookInt.iLocalizedStringId = hook->iLocalizedStringId; + hookInt.category = hook->category; + + /* add this new hook */ + hooks->push_back(hookInt); + } +} + +void CAddonCallbacksPVR::PVRRecording(void *addonData, const char *strName, const char *strFileName, bool bOnOff) +{ + CPVRClient *client = GetPVRClient(addonData); + if (!client || !strFileName) + { + CLog::Log(LOGERROR, "PVR - %s - invalid handler data", __FUNCTION__); + return; + } + + std::string strLine1; + if (bOnOff) + strLine1 = StringUtils::Format(g_localizeStrings.Get(19197).c_str(), client->Name().c_str()); + else + strLine1 = StringUtils::Format(g_localizeStrings.Get(19198).c_str(), client->Name().c_str()); + + std::string strLine2; + if (strName) + strLine2 = strName; + else if (strFileName) + strLine2 = strFileName; + + /* display a notification for 5 seconds */ + CGUIDialogKaiToast::QueueNotification(CGUIDialogKaiToast::Info, strLine1, strLine2, 5000, false); + + CLog::Log(LOGDEBUG, "PVR - %s - recording %s on client '%s'. name='%s' filename='%s'", + __FUNCTION__, bOnOff ? "started" : "finished", client->Name().c_str(), strName, strFileName); +} + +void CAddonCallbacksPVR::PVRTriggerChannelUpdate(void *addonData) +{ + /* update the channels table in the next iteration of the pvrmanager's main loop */ + g_PVRManager.TriggerChannelsUpdate(); +} + +void CAddonCallbacksPVR::PVRTriggerTimerUpdate(void *addonData) +{ + /* update the timers table in the next iteration of the pvrmanager's main loop */ + g_PVRManager.TriggerTimersUpdate(); +} + +void CAddonCallbacksPVR::PVRTriggerRecordingUpdate(void *addonData) +{ + /* update the recordings table in the next iteration of the pvrmanager's main loop */ + g_PVRManager.TriggerRecordingsUpdate(); +} + +void CAddonCallbacksPVR::PVRTriggerChannelGroupsUpdate(void *addonData) +{ + /* update all channel groups in the next iteration of the pvrmanager's main loop */ + g_PVRManager.TriggerChannelGroupsUpdate(); +} + +void CAddonCallbacksPVR::PVRTriggerEpgUpdate(void *addonData, unsigned int iChannelUid) +{ + // get the client + CPVRClient *client = GetPVRClient(addonData); + if (!client) + { + CLog::Log(LOGERROR, "PVR - %s - invalid handler data", __FUNCTION__); + return; + } + + g_EpgContainer.UpdateRequest(client->GetID(), iChannelUid); +} + +void CAddonCallbacksPVR::PVRFreeDemuxPacket(void *addonData, DemuxPacket* pPacket) +{ + CDVDDemuxUtils::FreeDemuxPacket(pPacket); +} + +DemuxPacket* CAddonCallbacksPVR::PVRAllocateDemuxPacket(void *addonData, int iDataSize) +{ + return CDVDDemuxUtils::AllocateDemuxPacket(iDataSize); +} + +}; /* namespace ADDON */ diff --git a/xbmc/addons/AddonCallbacksPVR.h b/xbmc/addons/AddonCallbacksPVR.h new file mode 100644 index 0000000..3fa48ec --- /dev/null +++ b/xbmc/addons/AddonCallbacksPVR.h @@ -0,0 +1,166 @@ +#pragma once +/* + * Copyright (C) 2012-2013 Team XBMC + * http://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, see + * . + * + */ + +#include "AddonCallbacks.h" +#include "include/xbmc_pvr_types.h" + +namespace PVR +{ + class CPVRClient; +} + +namespace ADDON +{ + +/*! + * Callbacks for a PVR add-on to XBMC. + * + * Also translates the addon's C structures to XBMC's C++ structures. + */ +class CAddonCallbacksPVR +{ +public: + CAddonCallbacksPVR(CAddon* addon); + ~CAddonCallbacksPVR(void); + + /*! + * @return The callback table. + */ + CB_PVRLib *GetCallbacks() { return m_callbacks; } + + /*! + * @brief Transfer a channel group from the add-on to XBMC. The group will be created if it doesn't exist. + * @param addonData A pointer to the add-on. + * @param handle The handle parameter that XBMC used when requesting the channel groups list + * @param entry The entry to transfer to XBMC + */ + static void PVRTransferChannelGroup(void* addonData, const ADDON_HANDLE handle, const PVR_CHANNEL_GROUP* entry); + + /*! + * @brief Transfer a channel group member entry from the add-on to XBMC. The channel will be added to the group if the group can be found. + * @param addonData A pointer to the add-on. + * @param handle The handle parameter that XBMC used when requesting the channel group members list + * @param entry The entry to transfer to XBMC + */ + static void PVRTransferChannelGroupMember(void* addonData, const ADDON_HANDLE handle, const PVR_CHANNEL_GROUP_MEMBER* entry); + + /*! + * @brief Transfer an EPG tag from the add-on to XBMC + * @param addonData A pointer to the add-on. + * @param handle The handle parameter that XBMC used when requesting the EPG data + * @param entry The entry to transfer to XBMC + */ + static void PVRTransferEpgEntry(void* addonData, const ADDON_HANDLE handle, const EPG_TAG* entry); + + /*! + * @brief Transfer a channel entry from the add-on to XBMC + * @param addonData A pointer to the add-on. + * @param handle The handle parameter that XBMC used when requesting the channel list + * @param entry The entry to transfer to XBMC + */ + static void PVRTransferChannelEntry(void* addonData, const ADDON_HANDLE handle, const PVR_CHANNEL* entry); + + /*! + * @brief Transfer a timer entry from the add-on to XBMC + * @param addonData A pointer to the add-on. + * @param handle The handle parameter that XBMC used when requesting the timers list + * @param entry The entry to transfer to XBMC + */ + static void PVRTransferTimerEntry(void* addonData, const ADDON_HANDLE handle, const PVR_TIMER* entry); + + /*! + * @brief Transfer a recording entry from the add-on to XBMC + * @param addonData A pointer to the add-on. + * @param handle The handle parameter that XBMC used when requesting the recordings list + * @param entry The entry to transfer to XBMC + */ + static void PVRTransferRecordingEntry(void* addonData, const ADDON_HANDLE handle, const PVR_RECORDING* entry); + + /*! + * @brief Add or replace a menu hook for the context menu for this add-on + * @param addonData A pointer to the add-on. + * @param hook The hook to add. + */ + static void PVRAddMenuHook(void* addonData, PVR_MENUHOOK* hook); + + /*! + * @brief Display a notification in XBMC that a recording started or stopped on the server + * @param addonData A pointer to the add-on. + * @param strName The name of the recording to display + * @param strFileName The filename of the recording + * @param bOnOff True when recording started, false when it stopped + */ + static void PVRRecording(void* addonData, const char* strName, const char* strFileName, bool bOnOff); + + /*! + * @brief Request XBMC to update it's list of channels + * @param addonData A pointer to the add-on. + */ + static void PVRTriggerChannelUpdate(void* addonData); + + /*! + * @brief Request XBMC to update it's list of timers + * @param addonData A pointer to the add-on. + */ + static void PVRTriggerTimerUpdate(void* addonData); + + /*! + * @brief Request XBMC to update it's list of recordings + * @param addonData A pointer to the add-on. + */ + static void PVRTriggerRecordingUpdate(void* addonData); + + /*! + * @brief Request XBMC to update it's list of channel groups + * @param addonData A pointer to the add-on. + */ + static void PVRTriggerChannelGroupsUpdate(void* addonData); + + /*! + * @brief Schedule an EPG update for the given channel channel + * @param addonData A pointer to the add-on + * @param iChannelUid The unique id of the channel for this add-on + */ + static void PVRTriggerEpgUpdate(void* addonData, unsigned int iChannelUid); + + /*! + * @brief Free a packet that was allocated with AllocateDemuxPacket + * @param addonData A pointer to the add-on. + * @param pPacket The packet to free. + */ + static void PVRFreeDemuxPacket(void* addonData, DemuxPacket* pPacket); + + /*! + * @brief Allocate a demux packet. Free with FreeDemuxPacket + * @param addonData A pointer to the add-on. + * @param iDataSize The size of the data that will go into the packet + * @return The allocated packet. + */ + static DemuxPacket* PVRAllocateDemuxPacket(void* addonData, int iDataSize = 0); + +private: + static PVR::CPVRClient* GetPVRClient(void* addonData); + + CB_PVRLib *m_callbacks; /*!< callback addresses */ + CAddon *m_addon; /*!< the addon */ +}; + +}; /* namespace ADDON */ diff --git a/xbmc/addons/AddonDatabase.cpp b/xbmc/addons/AddonDatabase.cpp new file mode 100644 index 0000000..69b47ea --- /dev/null +++ b/xbmc/addons/AddonDatabase.cpp @@ -0,0 +1,797 @@ +/* + * Copyright (C) 2005-2013 Team XBMC + * http://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, see + * . + * + */ + +#include "AddonDatabase.h" +#include "addons/AddonManager.h" +#include "utils/log.h" +#include "utils/Variant.h" +#include "utils/StringUtils.h" +#include "XBDateTime.h" +#include "dbwrappers/dataset.h" +#include "addons/ContextItemAddon.h" + +using namespace ADDON; +using namespace std; + +CAddonDatabase::CAddonDatabase() +{ +} + +CAddonDatabase::~CAddonDatabase() +{ +} + +bool CAddonDatabase::Open() +{ + return CDatabase::Open(); +} + +void CAddonDatabase::CreateTables() +{ + CLog::Log(LOGINFO, "create addon table"); + m_pDS->exec("CREATE TABLE addon (id integer primary key, type text," + "name text, summary text, description text, stars integer," + "path text, addonID text, icon text, version text, " + "changelog text, fanart text, author text, disclaimer text," + "minversion text)\n"); + + CLog::Log(LOGINFO, "create addonextra table"); + m_pDS->exec("CREATE TABLE addonextra (id integer, key text, value text)\n"); + + CLog::Log(LOGINFO, "create dependencies table"); + m_pDS->exec("CREATE TABLE dependencies (id integer, addon text, version text, optional boolean)\n"); + + CLog::Log(LOGINFO, "create repo table"); + m_pDS->exec("CREATE TABLE repo (id integer primary key, addonID text," + "checksum text, lastcheck text, version text)\n"); + + CLog::Log(LOGINFO, "create addonlinkrepo table"); + m_pDS->exec("CREATE TABLE addonlinkrepo (idRepo integer, idAddon integer)\n"); + + CLog::Log(LOGINFO, "create disabled table"); + m_pDS->exec("CREATE TABLE disabled (id integer primary key, addonID text)\n"); + + CLog::Log(LOGINFO, "create broken table"); + m_pDS->exec("CREATE TABLE broken (id integer primary key, addonID text, reason text)\n"); + + CLog::Log(LOGINFO, "create blacklist table"); + m_pDS->exec("CREATE TABLE blacklist (id integer primary key, addonID text, version text)\n"); + + CLog::Log(LOGINFO, "create package table"); + m_pDS->exec("CREATE TABLE package (id integer primary key, addonID text, filename text, hash text)\n"); +} + +void CAddonDatabase::CreateAnalytics() +{ + CLog::Log(LOGINFO, "%s creating indicies", __FUNCTION__); + m_pDS->exec("CREATE INDEX idxAddon ON addon(addonID)"); + m_pDS->exec("CREATE INDEX idxAddonExtra ON addonextra(id)"); + m_pDS->exec("CREATE INDEX idxDependencies ON dependencies(id)"); + m_pDS->exec("CREATE UNIQUE INDEX ix_addonlinkrepo_1 ON addonlinkrepo ( idAddon, idRepo )\n"); + m_pDS->exec("CREATE UNIQUE INDEX ix_addonlinkrepo_2 ON addonlinkrepo ( idRepo, idAddon )\n"); + m_pDS->exec("CREATE UNIQUE INDEX idxDisabled ON disabled(addonID)"); + m_pDS->exec("CREATE UNIQUE INDEX idxBroken ON broken(addonID)"); + m_pDS->exec("CREATE UNIQUE INDEX idxBlack ON blacklist(addonID)"); + m_pDS->exec("CREATE UNIQUE INDEX idxPackage ON package(filename)"); +} + +void CAddonDatabase::UpdateTables(int version) +{ + if (version < 16) + { + m_pDS->exec("CREATE TABLE package (id integer primary key, addonID text, filename text, hash text)\n"); + } + if (version < 17) + { + m_pDS->exec("DELETE FROM repo"); + m_pDS->exec("ALTER TABLE repo ADD version text"); + } +} + +int CAddonDatabase::AddAddon(const AddonPtr& addon, + int idRepo) +{ + try + { + if (NULL == m_pDB.get()) return -1; + if (NULL == m_pDS.get()) return -1; + + std::string sql = PrepareSQL("insert into addon (id, type, name, summary," + "description, stars, path, icon, changelog, " + "fanart, addonID, version, author, disclaimer, minversion)" + " values(NULL, '%s', '%s', '%s', '%s', %i," + "'%s', '%s', '%s', '%s', '%s','%s','%s','%s','%s')", + TranslateType(addon->Type(),false).c_str(), + addon->Name().c_str(), addon->Summary().c_str(), + addon->Description().c_str(),addon->Stars(), + addon->Path().c_str(), addon->Props().icon.c_str(), + addon->ChangeLog().c_str(),addon->FanArt().c_str(), + addon->ID().c_str(), addon->Version().asString().c_str(), + addon->Author().c_str(),addon->Disclaimer().c_str(), + addon->MinVersion().asString().c_str()); + m_pDS->exec(sql.c_str()); + int idAddon = (int)m_pDS->lastinsertid(); + + sql = PrepareSQL("insert into addonlinkrepo (idRepo, idAddon) values (%i,%i)",idRepo,idAddon); + m_pDS->exec(sql.c_str()); + + const InfoMap &info = addon->ExtraInfo(); + for (InfoMap::const_iterator i = info.begin(); i != info.end(); ++i) + { + sql = PrepareSQL("insert into addonextra(id, key, value) values (%i, '%s', '%s')", idAddon, i->first.c_str(), i->second.c_str()); + m_pDS->exec(sql.c_str()); + } + const ADDONDEPS &deps = addon->GetDeps(); + for (ADDONDEPS::const_iterator i = deps.begin(); i != deps.end(); ++i) + { + sql = PrepareSQL("insert into dependencies(id, addon, version, optional) values (%i, '%s', '%s', %i)", idAddon, i->first.c_str(), i->second.first.asString().c_str(), i->second.second ? 1 : 0); + m_pDS->exec(sql.c_str()); + } + return idAddon; + } + catch (...) + { + CLog::Log(LOGERROR, "%s failed on addon '%s'", __FUNCTION__, addon->Name().c_str()); + } + return -1; +} + +AddonVersion CAddonDatabase::GetAddonVersion(const std::string &id) +{ + AddonVersion maxversion("0.0.0"); + try + { + if (NULL == m_pDB.get()) return maxversion; + if (NULL == m_pDS2.get()) return maxversion; + + // there may be multiple addons with this id (eg from different repositories) in the database, + // so we want to retrieve the latest version. Order by version won't work as the database + // won't know that 1.10 > 1.2, so grab them all and order outside + std::string sql = PrepareSQL("select version from addon where addonID='%s'",id.c_str()); + m_pDS2->query(sql.c_str()); + + if (m_pDS2->eof()) + return maxversion; + + while (!m_pDS2->eof()) + { + AddonVersion version(m_pDS2->fv(0).get_asString()); + if (version > maxversion) + maxversion = version; + m_pDS2->next(); + } + return maxversion; + } + catch (...) + { + CLog::Log(LOGERROR, "%s failed on addon %s", __FUNCTION__, id.c_str()); + } + return maxversion; +} + +bool CAddonDatabase::GetAddon(const std::string& id, AddonPtr& addon) +{ + try + { + if (NULL == m_pDB.get()) return false; + if (NULL == m_pDS2.get()) return false; + + // there may be multiple addons with this id (eg from different repositories) in the database, + // so we want to retrieve the latest version. Order by version won't work as the database + // won't know that 1.10 > 1.2, so grab them all and order outside + std::string sql = PrepareSQL("select id,version from addon where addonID='%s'",id.c_str()); + m_pDS2->query(sql.c_str()); + + if (m_pDS2->eof()) + return false; + + AddonVersion maxversion("0.0.0"); + int maxid = 0; + while (!m_pDS2->eof()) + { + AddonVersion version(m_pDS2->fv(1).get_asString()); + if (version > maxversion) + { + maxid = m_pDS2->fv(0).get_asInt(); + maxversion = version; + } + m_pDS2->next(); + } + return GetAddon(maxid,addon); + } + catch (...) + { + CLog::Log(LOGERROR, "%s failed on addon %s", __FUNCTION__, id.c_str()); + } + addon.reset(); + return false; +} + +bool CAddonDatabase::GetRepoForAddon(const std::string& addonID, std::string& repo) +{ + try + { + if (NULL == m_pDB.get()) return false; + if (NULL == m_pDS2.get()) return false; + + std::string sql = PrepareSQL("select repo.addonID from repo join addonlinkrepo on repo.id=addonlinkrepo.idRepo join addon on addonlinkrepo.idAddon=addon.id where addon.addonID like '%s'", addonID.c_str()); + m_pDS2->query(sql.c_str()); + if (!m_pDS2->eof()) + { + repo = m_pDS2->fv(0).get_asString(); + m_pDS2->close(); + return true; + } + } + catch (...) + { + CLog::Log(LOGERROR, "%s failed for addon %s", __FUNCTION__, addonID.c_str()); + } + return false; +} + +bool CAddonDatabase::GetAddon(int id, AddonPtr &addon) +{ + try + { + if (NULL == m_pDB.get()) return false; + if (NULL == m_pDS2.get()) return false; + + std::string sql = "SELECT addon.*," + " broken.reason," + " addonextra.key, addonextra.value," + " dependencies.addon, dependencies.version, dependencies.optional" + " FROM addon" + " LEFT JOIN broken" + " ON broken.addonID = addon.addonID" + " LEFT JOIN addonextra" + " ON addonextra.id = addon.id" + " LEFT JOIN dependencies" + " ON dependencies.id = addon.id"; + + sql += PrepareSQL(" WHERE addon.id=%i", id); + + m_pDS2->query(sql.c_str()); + if (!m_pDS2->eof()) + { + const dbiplus::query_data &data = m_pDS2->get_result_set().records; + const dbiplus::sql_record* const record = data[0]; + AddonProps props(record->at(addon_addonID).get_asString(), + TranslateType(record->at(addon_type).get_asString()), + record->at(addon_version).get_asString(), + record->at(addon_minversion).get_asString()); + props.name = record->at(addon_name).get_asString(); + props.summary = record->at(addon_summary).get_asString(); + props.description = record->at(addon_description).get_asString(); + props.changelog = record->at(addon_changelog).get_asString(); + props.path = record->at(addon_path).get_asString(); + props.icon = record->at(addon_icon).get_asString(); + props.fanart = record->at(addon_fanart).get_asString(); + props.author = record->at(addon_author).get_asString(); + props.disclaimer = record->at(addon_disclaimer).get_asString(); + props.broken = record->at(broken_reason).get_asString(); + + /* while this is a cartesion join and we'll typically get multiple rows, we rely on the fact that + extrainfo and dependencies are maps, so insert() will insert the first instance only */ + for (dbiplus::query_data::const_iterator i = data.begin(); i != data.end(); ++i) + { + const dbiplus::sql_record* const record = *i; + if (!record->at(addonextra_key).get_asString().empty()) + props.extrainfo.insert(make_pair(record->at(addonextra_key).get_asString(), record->at(addonextra_value).get_asString())); + if (!m_pDS2->fv(dependencies_addon).get_asString().empty()) + props.dependencies.insert(make_pair(record->at(dependencies_addon).get_asString(), make_pair(AddonVersion(record->at(dependencies_version).get_asString()), record->at(dependencies_optional).get_asBool()))); + } + + addon = CAddonMgr::AddonFromProps(props); + return NULL != addon.get(); + } + } + catch (...) + { + CLog::Log(LOGERROR, "%s failed on addon %i", __FUNCTION__, id); + } + addon.reset(); + return false; +} + +bool CAddonDatabase::GetAddons(VECADDONS& addons) +{ + try + { + if (NULL == m_pDB.get()) return false; + if (NULL == m_pDS2.get()) return false; + + std::string sql = PrepareSQL("select distinct addonID from addon"); + m_pDS->query(sql.c_str()); + while (!m_pDS->eof()) + { + AddonPtr addon; + if (GetAddon(m_pDS->fv(0).get_asString(),addon)) + addons.push_back(addon); + m_pDS->next(); + } + m_pDS->close(); + return true; + } + catch (...) + { + CLog::Log(LOGERROR, "%s failed", __FUNCTION__); + } + return false; +} + +void CAddonDatabase::DeleteRepository(const std::string& id) +{ + try + { + if (NULL == m_pDB.get()) return; + if (NULL == m_pDS.get()) return; + + std::string sql = PrepareSQL("select id from repo where addonID='%s'",id.c_str()); + m_pDS->query(sql.c_str()); + if (!m_pDS->eof()) + DeleteRepository(m_pDS->fv(0).get_asInt()); + } + catch (...) + { + CLog::Log(LOGERROR, "%s failed on repo '%s'", __FUNCTION__, id.c_str()); + } +} + +void CAddonDatabase::DeleteRepository(int idRepo) +{ + try + { + if (NULL == m_pDB.get()) return; + if (NULL == m_pDS.get()) return; + + std::string sql = PrepareSQL("delete from repo where id=%i",idRepo); + m_pDS->exec(sql.c_str()); + sql = PrepareSQL("delete from addon where id in (select idAddon from addonlinkrepo where idRepo=%i)",idRepo); + m_pDS->exec(sql.c_str()); + sql = PrepareSQL("delete from addonextra where id in (select idAddon from addonlinkrepo where idRepo=%i)",idRepo); + m_pDS->exec(sql.c_str()); + sql = PrepareSQL("delete from dependencies where id in (select idAddon from addonlinkrepo where idRepo=%i)",idRepo); + m_pDS->exec(sql.c_str()); + sql = PrepareSQL("delete from addonlinkrepo where idRepo=%i",idRepo); + m_pDS->exec(sql.c_str()); + + } + catch (...) + { + CLog::Log(LOGERROR, "%s failed on repo %i", __FUNCTION__, idRepo); + } +} + +int CAddonDatabase::AddRepository(const std::string& id, const VECADDONS& addons, const std::string& checksum, const AddonVersion& version) +{ + try + { + if (NULL == m_pDB.get()) return -1; + if (NULL == m_pDS.get()) return -1; + + std::string sql; + int idRepo = GetRepoChecksum(id,sql); + if (idRepo > -1) + DeleteRepository(idRepo); + + BeginTransaction(); + + CDateTime time = CDateTime::GetCurrentDateTime(); + sql = PrepareSQL("insert into repo (id,addonID,checksum,lastcheck,version) values (NULL,'%s','%s','%s','%s')", + id.c_str(), checksum.c_str(), time.GetAsDBDateTime().c_str(), version.asString().c_str()); + m_pDS->exec(sql.c_str()); + idRepo = (int)m_pDS->lastinsertid(); + for (unsigned int i=0;iquery(strSQL.c_str()); + if (!m_pDS->eof()) + { + checksum = m_pDS->fv("checksum").get_asString(); + return m_pDS->fv("id").get_asInt(); + } + } + catch (...) + { + CLog::Log(LOGERROR, "%s failed on repo '%s'", __FUNCTION__, id.c_str()); + } + checksum.clear(); + return -1; +} + +CDateTime CAddonDatabase::GetRepoTimestamp(const std::string& id) +{ + CDateTime date; + try + { + if (NULL == m_pDB.get()) return date; + if (NULL == m_pDS.get()) return date; + + std::string strSQL = PrepareSQL("select * from repo where addonID='%s'",id.c_str()); + m_pDS->query(strSQL.c_str()); + if (!m_pDS->eof()) + { + date.SetFromDBDateTime(m_pDS->fv("lastcheck").get_asString()); + return date; + } + } + catch (...) + { + CLog::Log(LOGERROR, "%s failed on repo '%s'", __FUNCTION__, id.c_str()); + } + return date; +} + + +AddonVersion CAddonDatabase::GetRepoVersion(const std::string& id) +{ + AddonVersion version("0.0.0"); + try + { + if (NULL == m_pDB.get()) return version; + if (NULL == m_pDS2.get()) return version; + + std::string strSQL = PrepareSQL("select * from repo where addonID='%s'",id.c_str()); + m_pDS->query(strSQL.c_str()); + if (!m_pDS->eof()) + { + return AddonVersion(m_pDS->fv("version").get_asString()); + } + } + catch (...) + { + CLog::Log(LOGERROR, "%s failed on addon %s", __FUNCTION__, id.c_str()); + } + return version; +} + +bool CAddonDatabase::SetRepoTimestamp(const std::string& id, const std::string& time, const ADDON::AddonVersion& version) +{ + try + { + if (NULL == m_pDB.get()) return false; + if (NULL == m_pDS.get()) return false; + + std::string sql = PrepareSQL("UPDATE repo SET lastcheck='%s', version='%s' WHERE addonID='%s'", + time.c_str(), version.asString().c_str(), id.c_str()); + m_pDS->exec(sql.c_str()); + + return true; + } + catch (...) + { + CLog::Log(LOGERROR, "%s failed on repo '%s'", __FUNCTION__, id.c_str()); + } + return false; +} + +bool CAddonDatabase::GetRepository(int id, VECADDONS& addons) +{ + try + { + if (NULL == m_pDB.get()) return false; + if (NULL == m_pDS.get()) return false; + + std::string strSQL = PrepareSQL("select * from addonlinkrepo where idRepo=%i",id); + m_pDS->query(strSQL.c_str()); + while (!m_pDS->eof()) + { + AddonPtr addon; + if (GetAddon(m_pDS->fv("idAddon").get_asInt(),addon)) + addons.push_back(addon); + m_pDS->next(); + } + return true; + } + catch (...) + { + CLog::Log(LOGERROR, "%s failed on repo %i", __FUNCTION__, id); + } + return false; +} + +bool CAddonDatabase::GetRepository(const std::string& id, VECADDONS& addons) +{ + try + { + if (NULL == m_pDB.get()) return false; + if (NULL == m_pDS.get()) return false; + + std::string strSQL = PrepareSQL("select id from repo where addonID='%s'",id.c_str()); + m_pDS->query(strSQL.c_str()); + if (!m_pDS->eof()) + return GetRepository(m_pDS->fv(0).get_asInt(),addons); + } + catch (...) + { + CLog::Log(LOGERROR, "%s failed on repo %s", __FUNCTION__, id.c_str()); + } + return false; +} + +bool CAddonDatabase::Search(const std::string& search, VECADDONS& addons) +{ + try + { + if (NULL == m_pDB.get()) return false; + if (NULL == m_pDS.get()) return false; + + std::string strSQL; + strSQL=PrepareSQL("SELECT addonID FROM addon WHERE name LIKE '%%%s%%' OR summary LIKE '%%%s%%' OR description LIKE '%%%s%%'", search.c_str(), search.c_str(), search.c_str()); + CLog::Log(LOGDEBUG, "%s query: %s", __FUNCTION__, strSQL.c_str()); + + if (!m_pDS->query(strSQL.c_str())) return false; + if (m_pDS->num_rows() == 0) return false; + + while (!m_pDS->eof()) + { + AddonPtr addon; + GetAddon(m_pDS->fv(0).get_asString(),addon); + if (addon->Type() >= ADDON_UNKNOWN+1 && addon->Type() < ADDON_SCRAPER_LIBRARY) + addons.push_back(addon); + m_pDS->next(); + } + m_pDS->close(); + return true; + } + catch (...) + { + CLog::Log(LOGERROR, "%s failed", __FUNCTION__); + } + return false; +} + +void CAddonDatabase::SetPropertiesFromAddon(const AddonPtr& addon, + CFileItemPtr& pItem) +{ + pItem->SetProperty("Addon.ID", addon->ID()); + pItem->SetProperty("Addon.Type", TranslateType(addon->Type(),true)); + pItem->SetProperty("Addon.intType", TranslateType(addon->Type())); + pItem->SetProperty("Addon.Name", addon->Name()); + pItem->SetProperty("Addon.Version", addon->Version().asString()); + pItem->SetProperty("Addon.Summary", addon->Summary()); + pItem->SetProperty("Addon.Description", addon->Description()); + pItem->SetProperty("Addon.Creator", addon->Author()); + pItem->SetProperty("Addon.Disclaimer", addon->Disclaimer()); + pItem->SetProperty("Addon.Rating", addon->Stars()); + std::string starrating = StringUtils::Format("rating%d.png", addon->Stars()); + pItem->SetProperty("Addon.StarRating",starrating); + pItem->SetProperty("Addon.Path", addon->Path()); + if (addon->Props().broken == "DEPSNOTMET") + pItem->SetProperty("Addon.Broken", g_localizeStrings.Get(24044)); + else + pItem->SetProperty("Addon.Broken", addon->Props().broken); + std::map::iterator it = + addon->Props().extrainfo.find("language"); + if (it != addon->Props().extrainfo.end()) + pItem->SetProperty("Addon.Language", it->second); +} + +bool CAddonDatabase::DisableAddon(const std::string &addonID, bool disable /* = true */) +{ + try + { + if (NULL == m_pDB.get()) return false; + if (NULL == m_pDS.get()) return false; + + if (disable) + { + if (!IsAddonDisabled(addonID)) // Enabled + { + std::string sql = PrepareSQL("insert into disabled(id, addonID) values(NULL, '%s')", addonID.c_str()); + m_pDS->exec(sql); + + // If the addon is a special, call the disabled handler + AddonPtr addon; + if ((CAddonMgr::Get().GetAddon(addonID, addon, ADDON_SERVICE, false) + || CAddonMgr::Get().GetAddon(addonID, addon, ADDON_PVRDLL, false) + || CAddonMgr::Get().GetAddon(addonID, addon, ADDON_CONTEXT_ITEM, false)) && addon) + addon->OnDisabled(); + + return true; + } + return false; // already disabled or failed query + } + else + { + bool disabled = IsAddonDisabled(addonID); //we need to know if service addon is running + std::string sql = PrepareSQL("delete from disabled where addonID='%s'", addonID.c_str()); + m_pDS->exec(sql); + + if (disabled) + { + // If the addon is a special, call the enabled handler + AddonPtr addon; + if ((CAddonMgr::Get().GetAddon(addonID, addon, ADDON_SERVICE, false) + || CAddonMgr::Get().GetAddon(addonID, addon, ADDON_PVRDLL, false) + || CAddonMgr::Get().GetAddon(addonID, addon, ADDON_CONTEXT_ITEM, false)) && addon) + addon->OnEnabled(); + } + } + return true; + } + catch (...) + { + CLog::Log(LOGERROR, "%s failed on addon '%s'", __FUNCTION__, addonID.c_str()); + } + return false; +} + +bool CAddonDatabase::BreakAddon(const std::string &addonID, const std::string& reason) +{ + if (reason.empty()) + return ExecuteQuery(PrepareSQL("DELETE FROM broken WHERE addonID='%s'", addonID.c_str())); + else + return ExecuteQuery(PrepareSQL("REPLACE INTO broken(addonID, reason) VALUES('%s', '%s')", + addonID.c_str(), reason.c_str())); +} + +bool CAddonDatabase::HasAddon(const std::string &addonID) +{ + std::string strWhereClause = PrepareSQL("addonID = '%s'", addonID.c_str()); + std::string strHasAddon = GetSingleValue("addon", "id", strWhereClause); + + return !strHasAddon.empty(); +} + +bool CAddonDatabase::IsAddonDisabled(const std::string &addonID) +{ + try + { + if (NULL == m_pDB.get()) return false; + if (NULL == m_pDS.get()) return false; + + std::string sql = PrepareSQL("select id from disabled where addonID='%s'", addonID.c_str()); + m_pDS->query(sql.c_str()); + bool ret = !m_pDS->eof(); // in the disabled table -> disabled + m_pDS->close(); + return ret; + } + catch (...) + { + CLog::Log(LOGERROR, "%s failed on addon %s", __FUNCTION__, addonID.c_str()); + } + return false; +} + +bool CAddonDatabase::IsSystemPVRAddonEnabled(const std::string &addonID) +{ + std::string strWhereClause = PrepareSQL("addonID = '%s'", addonID.c_str()); + std::string strEnabled = GetSingleValue("pvrenabled", "id", strWhereClause); + + return !strEnabled.empty(); +} + +std::string CAddonDatabase::IsAddonBroken(const std::string &addonID) +{ + return GetSingleValue(PrepareSQL("SELECT reason FROM broken WHERE addonID='%s'", addonID.c_str())); +} + +bool CAddonDatabase::HasDisabledAddons() +{ + try + { + if (NULL == m_pDB.get()) return false; + if (NULL == m_pDS.get()) return false; + + m_pDS->query("select count(id) from disabled"); + bool ret = !m_pDS->eof() && m_pDS->fv(0).get_asInt() > 0; // have rows -> have disabled addons + m_pDS->close(); + return ret; + } + catch (...) + { + CLog::Log(LOGERROR, "%s failed", __FUNCTION__); + } + return false; +} + +bool CAddonDatabase::BlacklistAddon(const std::string& addonID, + const std::string& version) +{ + try + { + if (NULL == m_pDB.get()) return false; + if (NULL == m_pDS.get()) return false; + + std::string sql = PrepareSQL("insert into blacklist(id, addonID, version) values(NULL, '%s', '%s')", addonID.c_str(),version.c_str()); + m_pDS->exec(sql); + + return true; + } + catch (...) + { + CLog::Log(LOGERROR, "%s failed on addon '%s' for version '%s'", __FUNCTION__, addonID.c_str(),version.c_str()); + } + return false; +} + +bool CAddonDatabase::IsAddonBlacklisted(const std::string& addonID, + const std::string& version) +{ + std::string where = PrepareSQL("addonID='%s' and version='%s'",addonID.c_str(),version.c_str()); + return !GetSingleValue("blacklist","addonID",where).empty(); +} + +bool CAddonDatabase::RemoveAddonFromBlacklist(const std::string& addonID, + const std::string& version) +{ + try + { + if (NULL == m_pDB.get()) return false; + if (NULL == m_pDS.get()) return false; + + std::string sql = PrepareSQL("delete from blacklist where addonID='%s' and version='%s'",addonID.c_str(),version.c_str()); + m_pDS->exec(sql); + return true; + } + catch (...) + { + CLog::Log(LOGERROR, "%s failed on addon '%s' for version '%s'", __FUNCTION__, addonID.c_str(),version.c_str()); + } + return false; +} + +bool CAddonDatabase::AddPackage(const std::string& addonID, + const std::string& packageFileName, + const std::string& hash) +{ + std::string sql = PrepareSQL("insert into package(id, addonID, filename, hash)" + "values(NULL, '%s', '%s', '%s')", + addonID.c_str(), packageFileName.c_str(), hash.c_str()); + return ExecuteQuery(sql); +} + +bool CAddonDatabase::GetPackageHash(const std::string& addonID, + const std::string& packageFileName, + std::string& hash) +{ + std::string where = PrepareSQL("addonID='%s' and filename='%s'", + addonID.c_str(), packageFileName.c_str()); + hash = GetSingleValue("package", "hash", where); + return !hash.empty(); +} + +bool CAddonDatabase::RemovePackage(const std::string& packageFileName) +{ + std::string sql = PrepareSQL("delete from package where filename='%s'", packageFileName.c_str()); + return ExecuteQuery(sql); +} + diff --git a/xbmc/addons/AddonDatabase.h b/xbmc/addons/AddonDatabase.h new file mode 100644 index 0000000..b32f2a4 --- /dev/null +++ b/xbmc/addons/AddonDatabase.h @@ -0,0 +1,175 @@ +#pragma once +/* + * Copyright (C) 2005-2013 Team XBMC + * http://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, see + * . + * + */ + +#include "dbwrappers/Database.h" +#include "addons/Addon.h" +#include "FileItem.h" +#include + +class CAddonDatabase : public CDatabase +{ +public: + CAddonDatabase(); + virtual ~CAddonDatabase(); + virtual bool Open(); + + int AddAddon(const ADDON::AddonPtr& item, int idRepo); + bool GetAddon(const std::string& addonID, ADDON::AddonPtr& addon); + bool GetAddons(ADDON::VECADDONS& addons); + + /*! \brief grab the (largest) add-on version for an add-on */ + ADDON::AddonVersion GetAddonVersion(const std::string &id); + + /*! \brief Grab the repository from which a given addon came + \param addonID - the id of the addon in question + \param repo [out] - the id of the repository + \return true if a repo was found, false otherwise. + */ + bool GetRepoForAddon(const std::string& addonID, std::string& repo); + int AddRepository(const std::string& id, const ADDON::VECADDONS& addons, const std::string& checksum, const ADDON::AddonVersion& version); + void DeleteRepository(const std::string& id); + void DeleteRepository(int id); + int GetRepoChecksum(const std::string& id, std::string& checksum); + bool GetRepository(const std::string& id, ADDON::VECADDONS& addons); + bool GetRepository(int id, ADDON::VECADDONS& addons); + bool SetRepoTimestamp(const std::string& id, const std::string& timestamp, const ADDON::AddonVersion& version); + + /*! \brief Retrieve the time a repository was last checked + \param id id of the repo + \return last time the repo was checked, current time if not available + \sa SetRepoTimestamp */ + CDateTime GetRepoTimestamp(const std::string& id); + + ADDON::AddonVersion GetRepoVersion(const std::string& id); + + bool Search(const std::string& search, ADDON::VECADDONS& items); + static void SetPropertiesFromAddon(const ADDON::AddonPtr& addon, CFileItemPtr& item); + + /*! \brief Disable an addon. + Sets a flag that this addon has been disabled. If disabled, it is usually still available on disk. + \param addonID id of the addon to disable + \param disable whether to enable or disable. Defaults to true (disable) + \return true on success, false on failure + \sa IsAddonDisabled, HasDisabledAddons */ + bool DisableAddon(const std::string &addonID, bool disable = true); + + /*! \brief Checks if an addon is in the database. + \param addonID id of the addon to be checked + \return true if addon is in database, false if addon is not in database yet */ + bool HasAddon(const std::string &addonID); + + /*! \brief Check whether an addon has been disabled via DisableAddon. + \param addonID id of the addon to check + \return true if the addon is disabled, false otherwise + \sa DisableAddon, HasDisabledAddons */ + bool IsAddonDisabled(const std::string &addonID); + + /*! \brief Check whether we have disabled addons. + \return true if we have disabled addons, false otherwise + \sa DisableAddon, IsAddonDisabled */ + bool HasDisabledAddons(); + + /*! @deprecated only here to allow clean upgrades from earlier pvr versions + */ + bool IsSystemPVRAddonEnabled(const std::string &addonID); + + /*! \brief Mark an addon as broken + Sets a flag that this addon has been marked as broken in the repository. + \param addonID id of the addon to mark as broken + \param reason why it is broken - if non empty we take it as broken. Defaults to empty + \return true on success, false on failure + \sa IsAddonBroken */ + bool BreakAddon(const std::string &addonID, const std::string& reason=""); + + /*! \brief Check whether an addon has been marked as broken via BreakAddon. + \param addonID id of the addon to check + \return reason if the addon is broken, blank otherwise + \sa BreakAddon */ + std::string IsAddonBroken(const std::string &addonID); + + bool BlacklistAddon(const std::string& addonID, const std::string& version); + bool IsAddonBlacklisted(const std::string& addonID, const std::string& version); + bool RemoveAddonFromBlacklist(const std::string& addonID, + const std::string& version); + + /*! \brief Store an addon's package filename and that file's hash for future verification + \param addonID id of the addon we're adding a package for + \param packageFileName filename of the package + \param hash MD5 checksum of the package + \return Whether or not the info successfully made it into the DB. + \sa GetPackageHash, RemovePackage + */ + bool AddPackage(const std::string& addonID, + const std::string& packageFileName, + const std::string& hash); + /*! \brief Query the MD5 checksum of the given given addon's given package + \param addonID id of the addon we're who's package we're querying + \param packageFileName filename of the package + \param hash return the MD5 checksum of the package + \return Whether or not we found a hash for the given addon's given package + \sa AddPackage, RemovePackage + */ + bool GetPackageHash(const std::string& addonID, + const std::string& packageFileName, + std::string& hash); + /*! \brief Remove a package's info from the database + \param packageFileName filename of the package + \return Whether or not we succeeded in removing the package + \sa AddPackage, GetPackageHash + */ + bool RemovePackage(const std::string& packageFileName); +protected: + virtual void CreateTables(); + virtual void CreateAnalytics(); + virtual void UpdateTables(int version); + virtual int GetMinSchemaVersion() const { return 15; } + virtual int GetSchemaVersion() const { return 17; } + const char *GetBaseDBName() const { return "Addons"; } + + bool GetAddon(int id, ADDON::AddonPtr& addon); + + /* keep in sync with the select in GetAddon */ + enum _AddonFields + { + addon_id=0, + addon_type, + addon_name, + addon_summary, + addon_description, + addon_stars, + addon_path, + addon_addonID, + addon_icon, + addon_version, + addon_changelog, + addon_fanart, + addon_author, + addon_disclaimer, + addon_minversion, + broken_reason, + addonextra_key, + addonextra_value, + dependencies_addon, + dependencies_version, + dependencies_optional + } AddonFields; +}; + diff --git a/xbmc/addons/AddonDll.h b/xbmc/addons/AddonDll.h new file mode 100644 index 0000000..d5e4e9e --- /dev/null +++ b/xbmc/addons/AddonDll.h @@ -0,0 +1,572 @@ +#pragma once +/* + * Copyright (C) 2005-2013 Team XBMC + * http://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, see + * . + * + */ +#include +#include + +#include "Addon.h" +#include "DllAddon.h" +#include "AddonManager.h" +#include "AddonStatusHandler.h" +#include "AddonCallbacks.h" +#include "utils/URIUtils.h" +#include "filesystem/File.h" +#include "filesystem/SpecialProtocol.h" +#include "filesystem/Directory.h" +#include "utils/log.h" +#include "interfaces/IAnnouncer.h" +#include "interfaces/AnnouncementManager.h" +#include "utils/XMLUtils.h" +#include "Util.h" + +namespace ADDON +{ + template + class CAddonDll : public CAddon, public ANNOUNCEMENT::IAnnouncer + { + public: + CAddonDll(const AddonProps &props); + CAddonDll(const cp_extension_t *ext); + CAddonDll(const CAddonDll &rhs); + virtual ~CAddonDll(); + virtual AddonPtr Clone() const; + virtual ADDON_STATUS GetStatus(); + + // addon settings + virtual void SaveSettings(); + virtual std::string GetSetting(const std::string& key); + + ADDON_STATUS Create(); + virtual void Stop(); + virtual bool CheckAPIVersion(void) { return true; } + void Destroy(); + + bool DllLoaded(void) const; + + void Announce(ANNOUNCEMENT::AnnouncementFlag flag, const char *sender, const char *message, const CVariant &data); + + protected: + void HandleException(std::exception &e, const char* context); + bool Initialized() { return m_initialized; } + virtual void BuildLibName(const cp_extension_t *ext = NULL) {} + virtual bool LoadSettings(); + TheStruct* m_pStruct; + TheProps* m_pInfo; + CAddonCallbacks* m_pHelpers; + bool m_bIsChild; + + private: + TheDll* m_pDll; + bool m_initialized; + bool LoadDll(); + bool m_needsavedsettings; + + virtual ADDON_STATUS TransferSettings(); + TiXmlElement MakeSetting(DllSetting& setting) const; + + static void AddOnStatusCallback(void *userData, const ADDON_STATUS status, const char* msg); + static bool AddOnGetSetting(void *userData, const char *settingName, void *settingValue); + static void AddOnOpenSettings(const char *url, bool bReload); + static void AddOnOpenOwnSettings(void *userData, bool bReload); + static const char* AddOnGetAddonDirectory(void *userData); + static const char* AddOnGetUserDirectory(void *userData); + }; + +template +CAddonDll::CAddonDll(const cp_extension_t *ext) + : CAddon(ext), + m_bIsChild(false) +{ + // if library attribute isn't present, look for a system-dependent one + if (ext && m_strLibName.empty()) + { +#if defined(TARGET_ANDROID) + m_strLibName = CAddonMgr::Get().GetExtValue(ext->configuration, "@library_android"); +#elif defined(TARGET_LINUX) || defined(TARGET_FREEBSD) + m_strLibName = CAddonMgr::Get().GetExtValue(ext->configuration, "@library_linux"); +#elif defined(TARGET_WINDOWS) && defined(HAS_DX) + m_strLibName = CAddonMgr::Get().GetExtValue(ext->configuration, "@library_windx"); +#elif defined(TARGET_DARWIN) + m_strLibName = CAddonMgr::Get().GetExtValue(ext->configuration, "@library_osx"); +#endif + } + + m_pStruct = NULL; + m_initialized = false; + m_pDll = NULL; + m_pInfo = NULL; + m_pHelpers = NULL; + m_needsavedsettings = false; +} + +template +CAddonDll::CAddonDll(const AddonProps &props) + : CAddon(props), + m_bIsChild(false) +{ + m_pStruct = NULL; + m_initialized = false; + m_pDll = NULL; + m_pInfo = NULL; + m_pHelpers = NULL; + m_needsavedsettings = false; +} + +template +CAddonDll::CAddonDll(const CAddonDll &rhs) + : CAddon(rhs), + m_bIsChild(true) +{ + m_pStruct = rhs.m_pStruct; + m_initialized = rhs.m_initialized; + m_pDll = rhs.m_pDll; + m_pInfo = rhs.m_pInfo; + m_pHelpers = rhs.m_pHelpers; + m_needsavedsettings = rhs.m_needsavedsettings; +} + +template +CAddonDll::~CAddonDll() +{ + if (m_initialized) + Destroy(); +} + +template +AddonPtr CAddonDll::Clone() const +{ + return AddonPtr(new CAddonDll(*this)); +} + +template +bool CAddonDll::LoadDll() +{ + std::string strFileName; + if (!m_bIsChild) + { + strFileName = LibPath(); + } + else + { //FIXME hack to load same Dll twice + std::string extension = URIUtils::GetExtension(m_strLibName); + strFileName = "special://temp/" + m_strLibName; + URIUtils::RemoveExtension(strFileName); + strFileName += "-" + ID() + extension; + + if (!XFILE::CFile::Exists(strFileName)) + XFILE::CFile::Copy(LibPath(), strFileName); + + CLog::Log(LOGNOTICE, "ADDON: Loaded virtual child addon %s", strFileName.c_str()); + } + + /* Check if lib being loaded exists, else check in XBMC binary location */ +#if defined(TARGET_ANDROID) + // Android libs MUST live in this path, else multi-arch will break. + // The usual soname requirements apply. no subdirs, and filename is ^lib.*\.so$ + if (!XFILE::CFile::Exists(strFileName)) + { + std::string tempbin = getenv("XBMC_ANDROID_LIBS"); + strFileName = tempbin + "/" + m_strLibName; + } +#endif + if (!XFILE::CFile::Exists(strFileName)) + { + std::string temp = CSpecialProtocol::TranslatePath("special://xbmc/"); + std::string tempbin = CSpecialProtocol::TranslatePath("special://xbmcbin/"); + strFileName.erase(0, temp.size()); + strFileName = tempbin + strFileName; + if (!XFILE::CFile::Exists(strFileName)) + { + CLog::Log(LOGERROR, "ADDON: Could not locate %s", m_strLibName.c_str()); + return false; + } + } + + /* Load the Dll */ + m_pDll = new TheDll; + m_pDll->SetFile(strFileName); + m_pDll->EnableDelayedUnload(false); + if (!m_pDll->Load()) + { + delete m_pDll; + m_pDll = NULL; + new CAddonStatusHandler(ID(), ADDON_STATUS_UNKNOWN, "Can't load Dll", false); + return false; + } + + m_pStruct = (TheStruct*)malloc(sizeof(TheStruct)); + if (m_pStruct) + { + ZeroMemory(m_pStruct, sizeof(TheStruct)); + m_pDll->GetAddon(m_pStruct); + return true; + } + + return false; +} + +template +ADDON_STATUS CAddonDll::Create() +{ + ADDON_STATUS status(ADDON_STATUS_UNKNOWN); + CLog::Log(LOGDEBUG, "ADDON: Dll Initializing - %s", Name().c_str()); + m_initialized = false; + + if (!LoadDll() || !CheckAPIVersion()) + return ADDON_STATUS_PERMANENT_FAILURE; + + /* Allocate the helper function class to allow crosstalk over + helper libraries */ + m_pHelpers = new CAddonCallbacks(this); + + /* Call Create to make connections, initializing data or whatever is + needed to become the AddOn running */ + try + { + status = m_pDll->Create(m_pHelpers->GetCallbacks(), m_pInfo); + if (status == ADDON_STATUS_OK) + { + m_initialized = true; + ANNOUNCEMENT::CAnnouncementManager::Get().AddAnnouncer(this); + } + else if ((status == ADDON_STATUS_NEED_SETTINGS) || (status == ADDON_STATUS_NEED_SAVEDSETTINGS)) + { + m_needsavedsettings = (status == ADDON_STATUS_NEED_SAVEDSETTINGS); + if ((status = TransferSettings()) == ADDON_STATUS_OK) + m_initialized = true; + else + new CAddonStatusHandler(ID(), status, "", false); + } + else + { // Addon failed initialization + CLog::Log(LOGERROR, "ADDON: Dll %s - Client returned bad status (%i) from Create and is not usable", Name().c_str(), status); + new CAddonStatusHandler(ID(), status, "", false); + } + } + catch (std::exception &e) + { + HandleException(e, "m_pDll->Create"); + } + + if (!m_initialized) + SAFE_DELETE(m_pHelpers); + + return status; +} + +template +void CAddonDll::Stop() +{ + /* Inform dll to stop all activities */ + try + { + if (m_needsavedsettings) // If the addon supports it we save some settings to settings.xml before stop + { + char str_id[64] = ""; + char str_value[1024]; + CAddon::LoadUserSettings(); + for (unsigned int i=0; (strcmp(str_id,"###End") != 0); i++) + { + strcpy(str_id, "###GetSavedSettings"); + sprintf (str_value, "%i", i); + ADDON_STATUS status = m_pDll->SetSetting((const char*)&str_id, (void*)&str_value); + + if (status == ADDON_STATUS_UNKNOWN) + break; + + if (strcmp(str_id,"###End") != 0) UpdateSetting(str_id, str_value); + } + CAddon::SaveSettings(); + } + if (m_pDll) + { + m_pDll->Stop(); + CLog::Log(LOGINFO, "ADDON: Dll Stopped - %s", Name().c_str()); + } + } + catch (std::exception &e) + { + HandleException(e, "m_pDll->Stop"); + } +} + +template +void CAddonDll::Destroy() +{ + ANNOUNCEMENT::CAnnouncementManager::Get().RemoveAnnouncer(this); + + /* Unload library file */ + try + { + if (m_pDll) + { + m_pDll->Destroy(); + m_pDll->Unload(); + } + } + catch (std::exception &e) + { + HandleException(e, "m_pDll->Unload"); + } + delete m_pHelpers; + m_pHelpers = NULL; + free(m_pStruct); + m_pStruct = NULL; + if (m_pDll) + { + delete m_pDll; + m_pDll = NULL; + CLog::Log(LOGINFO, "ADDON: Dll Destroyed - %s", Name().c_str()); + } + m_initialized = false; +} + +template +bool CAddonDll::DllLoaded(void) const +{ + return m_pDll != NULL; +} + +template +ADDON_STATUS CAddonDll::GetStatus() +{ + try + { + return m_pDll->GetStatus(); + } + catch (std::exception &e) + { + HandleException(e, "m_pDll->GetStatus()"); + } + return ADDON_STATUS_UNKNOWN; +} + +template +bool CAddonDll::LoadSettings() +{ + if (m_settingsLoaded) + return true; + + if (!LoadDll()) + return false; + + ADDON_StructSetting** sSet; + std::vector vSet; + unsigned entries = 0; + try + { + entries = m_pDll->GetSettings(&sSet); + DllUtils::StructToVec(entries, &sSet, &vSet); + m_pDll->FreeSettings(); + } + catch (std::exception &e) + { + HandleException(e, "m_pDll->GetSettings()"); + return false; + } + + if (vSet.size()) + { + // regenerate XML doc + m_addonXmlDoc.Clear(); + TiXmlElement node("settings"); + m_addonXmlDoc.InsertEndChild(node); + + for (unsigned i=0; i < entries; i++) + { + DllSetting& setting = vSet[i]; + m_addonXmlDoc.RootElement()->InsertEndChild(MakeSetting(setting)); + } + CAddon::SettingsFromXML(m_addonXmlDoc, true); + } + else + return CAddon::LoadSettings(); + + m_settingsLoaded = true; + CAddon::LoadUserSettings(); + return true; +} + +template +TiXmlElement CAddonDll::MakeSetting(DllSetting& setting) const +{ + TiXmlElement node("setting"); + + switch (setting.type) + { + case DllSetting::CHECK: + { + node.SetAttribute("id", setting.id); + node.SetAttribute("type", "bool"); + node.SetAttribute("label", setting.label); + break; + } + case DllSetting::SPIN: + { + node.SetAttribute("id", setting.id); + node.SetAttribute("type", "enum"); + node.SetAttribute("label", setting.label); + std::string values; + for (unsigned int i = 0; i < setting.entry.size(); i++) + { + values.append(setting.entry[i]); + values.append("|"); + } + node.SetAttribute("values", values.c_str()); + break; + } + default: + break; + } + + return node; +} + +template +void CAddonDll::SaveSettings() +{ + // must save first, as TransferSettings() reloads saved settings! + CAddon::SaveSettings(); + if (m_initialized) + TransferSettings(); +} + +template +std::string CAddonDll::GetSetting(const std::string& key) +{ + return CAddon::GetSetting(key); +} + +template +ADDON_STATUS CAddonDll::TransferSettings() +{ + bool restart = false; + ADDON_STATUS reportStatus = ADDON_STATUS_OK; + + CLog::Log(LOGDEBUG, "Calling TransferSettings for: %s", Name().c_str()); + + LoadSettings(); + + const TiXmlElement *category = m_addonXmlDoc.RootElement() ? m_addonXmlDoc.RootElement()->FirstChildElement("category") : NULL; + if (!category) + category = m_addonXmlDoc.RootElement(); // no categories + + while (category) + { + const TiXmlElement *setting = category->FirstChildElement("setting"); + while (setting) + { + ADDON_STATUS status = ADDON_STATUS_OK; + const char *id = setting->Attribute("id"); + const std::string type = XMLUtils::GetAttribute(setting, "type"); + const char *option = setting->Attribute("option"); + + if (id && !type.empty()) + { + if (type == "sep" || type == "lsep") + { + /* Don't propagate separators */ + } + else if (type == "text" || type == "ipaddress" || + type == "video" || type == "audio" || + type == "image" || type == "folder" || + type == "executable" || type == "file" || + type == "action" || type == "date" || + type == "time" || type == "select" || + type == "addon" || type == "labelenum" || + type == "fileenum" ) + { + status = m_pDll->SetSetting(id, (const char*) GetSetting(id).c_str()); + } + else if (type == "enum" || type =="integer" || + type == "labelenum" || type == "rangeofnum") + { + int tmp = atoi(GetSetting(id).c_str()); + status = m_pDll->SetSetting(id, (int*) &tmp); + } + else if (type == "bool") + { + bool tmp = (GetSetting(id) == "true") ? true : false; + status = m_pDll->SetSetting(id, (bool*) &tmp); + } + else if (type == "rangeofnum" || type == "slider" || + type == "number") + { + float tmpf = (float)atof(GetSetting(id).c_str()); + int tmpi; + + if (option && strcmpi(option,"int") == 0) + { + tmpi = (int)floor(tmpf); + status = m_pDll->SetSetting(id, (int*) &tmpi); + } + else + { + status = m_pDll->SetSetting(id, (float*) &tmpf); + } + } + else + { + /* Log unknowns as an error, but go ahead and transfer the string */ + CLog::Log(LOGERROR, "Unknown setting type '%s' for %s", type.c_str(), Name().c_str()); + status = m_pDll->SetSetting(id, (const char*) GetSetting(id).c_str()); + } + + if (status == ADDON_STATUS_NEED_RESTART) + restart = true; + else if (status != ADDON_STATUS_OK) + reportStatus = status; + } + setting = setting->NextSiblingElement("setting"); + } + category = category->NextSiblingElement("category"); + } + + if (restart || reportStatus != ADDON_STATUS_OK) + { + new CAddonStatusHandler(ID(), restart ? ADDON_STATUS_NEED_RESTART : reportStatus, "", true); + } + + return ADDON_STATUS_OK; +} + +template +void CAddonDll::Announce(ANNOUNCEMENT::AnnouncementFlag flag, const char *sender, const char *message, const CVariant &data) +{ + try + { + m_pDll->Announce(ANNOUNCEMENT::AnnouncementFlagToString(flag), sender, message, &data); + } + catch (std::exception &e) + { + HandleException(e, "m_pDll->Announce()"); + } +} + +template +void CAddonDll::HandleException(std::exception &e, const char* context) +{ + m_initialized = false; + m_pDll->Unload(); + CLog::Log(LOGERROR, "ADDON: Dll %s, throws an exception '%s' during %s. Contact developer '%s' with bug reports", Name().c_str(), e.what(), context, Author().c_str()); +} + +}; /* namespace ADDON */ + diff --git a/xbmc/addons/AddonInstaller.cpp b/xbmc/addons/AddonInstaller.cpp new file mode 100644 index 0000000..f4a241c --- /dev/null +++ b/xbmc/addons/AddonInstaller.cpp @@ -0,0 +1,974 @@ +/* + * Copyright (C) 2011-2013 Team XBMC + * http://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, see + * . + * + */ + +#include "AddonInstaller.h" +#include "utils/log.h" +#include "utils/FileUtils.h" +#include "utils/URIUtils.h" +#include "Util.h" +#include "guilib/LocalizeStrings.h" +#include "filesystem/Directory.h" +#include "settings/AdvancedSettings.h" +#include "settings/Settings.h" +#include "ApplicationMessenger.h" +#include "filesystem/FavouritesDirectory.h" +#include "utils/JobManager.h" +#include "dialogs/GUIDialogYesNo.h" +#include "addons/AddonManager.h" +#include "addons/Repository.h" +#include "guilib/GUIWindowManager.h" // for callback +#include "GUIUserMessages.h" // for callback +#include "utils/StringUtils.h" +#include "dialogs/GUIDialogKaiToast.h" +#include "dialogs/GUIDialogOK.h" +#include "dialogs/GUIDialogProgress.h" +#include "URL.h" + +#include + +using namespace std; +using namespace XFILE; +using namespace ADDON; + + +struct find_map : public binary_function +{ + bool operator() (CAddonInstaller::JobMap::value_type t, unsigned int id) const + { + return (t.second.jobID == id); + } +}; + +CAddonInstaller::CAddonInstaller() + : m_repoUpdateJob(0) +{ } + +CAddonInstaller::~CAddonInstaller() +{ } + +CAddonInstaller &CAddonInstaller::Get() +{ + static CAddonInstaller addonInstaller; + return addonInstaller; +} + +void CAddonInstaller::OnJobComplete(unsigned int jobID, bool success, CJob* job) +{ + if (success) + CAddonMgr::Get().FindAddons(); + + CSingleLock lock(m_critSection); + if (strncmp(job->GetType(), "repoupdate", 10) == 0) + { + // repo job finished + m_repoUpdateDone.Set(); + m_repoUpdateJob = 0; + lock.Leave(); + } + else + { + // download job + JobMap::iterator i = find_if(m_downloadJobs.begin(), m_downloadJobs.end(), bind2nd(find_map(), jobID)); + if (i != m_downloadJobs.end()) + m_downloadJobs.erase(i); + lock.Leave(); + PrunePackageCache(); + } + + CGUIMessage msg(GUI_MSG_NOTIFY_ALL, 0, 0, GUI_MSG_UPDATE); + g_windowManager.SendThreadMessage(msg); +} + +void CAddonInstaller::OnJobProgress(unsigned int jobID, unsigned int progress, unsigned int total, const CJob *job) +{ + CSingleLock lock(m_critSection); + JobMap::iterator i = find_if(m_downloadJobs.begin(), m_downloadJobs.end(), bind2nd(find_map(), jobID)); + if (i != m_downloadJobs.end()) + { + // update job progress + i->second.progress = progress; + CGUIMessage msg(GUI_MSG_NOTIFY_ALL, 0, 0, GUI_MSG_UPDATE_ITEM); + msg.SetStringParam(i->first); + lock.Leave(); + g_windowManager.SendThreadMessage(msg); + } +} + +bool CAddonInstaller::IsDownloading() const +{ + CSingleLock lock(m_critSection); + return !m_downloadJobs.empty(); +} + +void CAddonInstaller::GetInstallList(VECADDONS &addons) const +{ + CSingleLock lock(m_critSection); + vector addonIDs; + for (JobMap::const_iterator i = m_downloadJobs.begin(); i != m_downloadJobs.end(); ++i) + { + if (i->second.jobID) + addonIDs.push_back(i->first); + } + lock.Leave(); + + CAddonDatabase database; + database.Open(); + for (vector::iterator it = addonIDs.begin(); it != addonIDs.end(); ++it) + { + AddonPtr addon; + if (database.GetAddon(*it, addon)) + addons.push_back(addon); + } +} + +bool CAddonInstaller::GetProgress(const std::string &addonID, unsigned int &percent) const +{ + CSingleLock lock(m_critSection); + JobMap::const_iterator i = m_downloadJobs.find(addonID); + if (i != m_downloadJobs.end()) + { + percent = i->second.progress; + return true; + } + return false; +} + +bool CAddonInstaller::Cancel(const std::string &addonID) +{ + CSingleLock lock(m_critSection); + JobMap::iterator i = m_downloadJobs.find(addonID); + if (i != m_downloadJobs.end()) + { + CJobManager::GetInstance().CancelJob(i->second.jobID); + m_downloadJobs.erase(i); + return true; + } + + return false; +} + +bool CAddonInstaller::InstallModal(const std::string &addonID, ADDON::AddonPtr &addon, bool promptForInstall /* = true */) +{ + if (!g_passwordManager.CheckMenuLock(WINDOW_ADDON_BROWSER)) + return false; + + // we assume that addons that are enabled don't get to this routine (i.e. that GetAddon() has been called) + if (CAddonMgr::Get().GetAddon(addonID, addon, ADDON_UNKNOWN, false)) + return false; // addon is installed but disabled, and the user has specifically activated something that needs + // the addon - should we enable it? + + // check we have it available + CAddonDatabase database; + database.Open(); + if (!database.GetAddon(addonID, addon)) + return false; + + // if specified ask the user if he wants it installed + if (promptForInstall && + !CGUIDialogYesNo::ShowAndGetInput(g_localizeStrings.Get(24076), g_localizeStrings.Get(24100), + addon->Name().c_str(), g_localizeStrings.Get(24101))) + return false; + + if (!Install(addonID, true, "", false, true)) + return false; + + return CAddonMgr::Get().GetAddon(addonID, addon); +} + +bool CAddonInstaller::Install(const std::string &addonID, bool force /* = false */, const std::string &referer /* = "" */, bool background /* = true */, bool modal /* = false */) +{ + AddonPtr addon; + bool addonInstalled = CAddonMgr::Get().GetAddon(addonID, addon, ADDON_UNKNOWN, false); + if (addonInstalled && !force) + return true; + + if (referer.empty()) + { + if (!g_passwordManager.CheckMenuLock(WINDOW_ADDON_BROWSER)) + return false; + } + + // check whether we have it available in a repository + std::string hash; + if (!CAddonInstallJob::GetAddonWithHash(addonID, addon, hash)) + return false; + + return DoInstall(addon, hash, addonInstalled, referer, background, modal); +} + +bool CAddonInstaller::DoInstall(const AddonPtr &addon, const std::string &hash /* = "" */, bool update /* = false */, const std::string &referer /* = "" */, bool background /* = true */, bool modal /* = false */) +{ + // check whether we already have the addon installing + CSingleLock lock(m_critSection); + if (m_downloadJobs.find(addon->ID()) != m_downloadJobs.end()) + return false; + + CAddonInstallJob* installJob = new CAddonInstallJob(addon, hash, update, referer); + if (background) + { + unsigned int jobID = CJobManager::GetInstance().AddJob(installJob, this); + m_downloadJobs.insert(make_pair(addon->ID(), CDownloadJob(jobID))); + return true; + } + + m_downloadJobs.insert(make_pair(addon->ID(), CDownloadJob(0))); + lock.Leave(); + + bool result = false; + if (modal) + result = installJob->DoModal(); + else + result = installJob->DoWork(); + delete installJob; + + lock.Enter(); + JobMap::iterator i = m_downloadJobs.find(addon->ID()); + m_downloadJobs.erase(i); + + return result; +} + +bool CAddonInstaller::InstallFromZip(const std::string &path) +{ + if (!g_passwordManager.CheckMenuLock(WINDOW_ADDON_BROWSER)) + return false; + + // grab the descriptive XML document from the zip, and read it in + CFileItemList items; + // BUG: some zip files return a single item (root folder) that we think is stored, so we don't use the zip:// protocol + CURL pathToUrl(path); + CURL zipDir = URIUtils::CreateArchivePath("zip", pathToUrl, ""); + if (!CDirectory::GetDirectory(zipDir, items) || items.Size() != 1 || !items[0]->m_bIsFolder) + { + CGUIDialogKaiToast::QueueNotification("", path, g_localizeStrings.Get(24045), TOAST_DISPLAY_TIME, false); + return false; + } + + // TODO: possibly add support for github generated zips here? + std::string archive = URIUtils::AddFileToFolder(items[0]->GetPath(), "addon.xml"); + + CXBMCTinyXML xml; + AddonPtr addon; + if (xml.LoadFile(archive) && CAddonMgr::Get().LoadAddonDescriptionFromMemory(xml.RootElement(), addon)) + { + // set the correct path + addon->Props().path = items[0]->GetPath(); + addon->Props().icon = URIUtils::AddFileToFolder(items[0]->GetPath(), "icon.png"); + + // install the addon + return DoInstall(addon); + } + + CGUIDialogKaiToast::QueueNotification("", path, g_localizeStrings.Get(24045), TOAST_DISPLAY_TIME, false); + return false; +} + +void CAddonInstaller::InstallFromXBMCRepo(const set &addonIDs) +{ + // first check we have the our repositories up to date (and wait until we do) + UpdateRepos(false, true); + + // now install the addons + for (set::const_iterator i = addonIDs.begin(); i != addonIDs.end(); ++i) + Install(*i); +} + +bool CAddonInstaller::CheckDependencies(const AddonPtr &addon, CAddonDatabase *database /* = NULL */) +{ + std::vector preDeps; + preDeps.push_back(addon->ID()); + CAddonDatabase localDB; + if (!database) + database = &localDB; + + return CheckDependencies(addon, preDeps, *database); +} + +bool CAddonInstaller::CheckDependencies(const AddonPtr &addon, + std::vector& preDeps, CAddonDatabase &database) +{ + if (addon == NULL) + return true; // a NULL addon has no dependencies + + if (!database.Open()) + return false; + + ADDONDEPS deps = addon->GetDeps(); + for (ADDONDEPS::const_iterator i = deps.begin(); i != deps.end(); ++i) + { + const std::string &addonID = i->first; + const AddonVersion &version = i->second.first; + bool optional = i->second.second; + AddonPtr dep; + bool haveAddon = CAddonMgr::Get().GetAddon(addonID, dep); + if ((haveAddon && !dep->MeetsVersion(version)) || (!haveAddon && !optional)) + { + // we have it but our version isn't good enough, or we don't have it and we need it + if (!database.GetAddon(addonID, dep) || !dep->MeetsVersion(version)) + { + // we don't have it in a repo, or we have it but the version isn't good enough, so dep isn't satisfied. + CLog::Log(LOGDEBUG, "CAddonInstallJob[%s]: requires %s version %s which is not available", addon->ID().c_str(), addonID.c_str(), version.asString().c_str()); + database.Close(); + return false; + } + } + + // 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 + // TODO: should we assume that installed deps are OK? + if (dep && std::find(preDeps.begin(), preDeps.end(), dep->ID()) == preDeps.end()) + { + if (!CheckDependencies(dep, preDeps, database)) + { + database.Close(); + preDeps.push_back(dep->ID()); + return false; + } + } + } + database.Close(); + + return true; +} + +CDateTime CAddonInstaller::LastRepoUpdate() const +{ + CDateTime update; + CAddonDatabase database; + if (!database.Open()) + return update; + + VECADDONS addons; + CAddonMgr::Get().GetAddons(ADDON_REPOSITORY, addons); + for (unsigned int i = 0; i < addons.size(); i++) + { + CDateTime lastUpdate = database.GetRepoTimestamp(addons[i]->ID()); + if (lastUpdate.IsValid() && lastUpdate > update) + update = lastUpdate; + } + + return update; +} + +void CAddonInstaller::UpdateRepos(bool force, bool wait) +{ + CSingleLock lock(m_critSection); + if (m_repoUpdateJob) + { + if (wait) + { + // wait for our job to complete + lock.Leave(); + CLog::Log(LOGDEBUG, "%s - waiting for repository update job to finish...", __FUNCTION__); + m_repoUpdateDone.Wait(); + } + return; + } + + // don't run repo update jobs while on the login screen which runs under the master profile + if((g_windowManager.GetActiveWindow() & WINDOW_ID_MASK) == WINDOW_LOGIN_SCREEN) + return; + + if (!force && m_repoUpdateWatch.IsRunning() && m_repoUpdateWatch.GetElapsedSeconds() < 600) + return; + + CAddonDatabase database; + if (!database.Open()) + return; + + m_repoUpdateWatch.StartZero(); + + VECADDONS addons; + if (CAddonMgr::Get().GetAddons(ADDON_REPOSITORY, addons)) + { + for (const auto& repo : addons) + { + CDateTime lastUpdate = database.GetRepoTimestamp(repo->ID()); + if (force || !lastUpdate.IsValid() || lastUpdate + CDateTimeSpan(0, 24, 0, 0) < CDateTime::GetCurrentDateTime() + || repo->Version() != database.GetRepoVersion(repo->ID())) + { + CLog::Log(LOGDEBUG, "Checking repositories for updates (triggered by %s)", repo->Name().c_str()); + m_repoUpdateJob = CJobManager::GetInstance().AddJob(new CRepositoryUpdateJob(addons), this); + if (wait) + { + // wait for our job to complete + lock.Leave(); + CLog::Log(LOGDEBUG, "%s - waiting for this repository update job to finish...", __FUNCTION__); + m_repoUpdateDone.Wait(); + } + + return; + } + } + } +} + +bool CAddonInstaller::HasJob(const std::string& ID) const +{ + CSingleLock lock(m_critSection); + return m_downloadJobs.find(ID) != m_downloadJobs.end(); +} + +void CAddonInstaller::PrunePackageCache() +{ + std::map packs; + int64_t size = EnumeratePackageFolder(packs); + int64_t limit = (int64_t)g_advancedSettings.m_addonPackageFolderSize * 1024 * 1024; + if (size < limit) + return; + + // Prune packages + // 1. Remove the largest packages, leaving at least 2 for each add-on + CFileItemList items; + CAddonDatabase db; + db.Open(); + for (std::map::const_iterator it = packs.begin(); it != packs.end(); ++it) + { + it->second->Sort(SortByLabel, SortOrderDescending); + for (int j = 2; j < it->second->Size(); j++) + items.Add(CFileItemPtr(new CFileItem(*it->second->Get(j)))); + } + + items.Sort(SortBySize, SortOrderDescending); + int i = 0; + while (size > limit && i < items.Size()) + { + size -= items[i]->m_dwSize; + db.RemovePackage(items[i]->GetPath()); + CFileUtils::DeleteItem(items[i++], true); + } + + if (size > limit) + { + // 2. Remove the oldest packages (leaving least 1 for each add-on) + items.Clear(); + for (std::map::iterator it = packs.begin(); it != packs.end(); ++it) + { + if (it->second->Size() > 1) + items.Add(CFileItemPtr(new CFileItem(*it->second->Get(1)))); + } + + items.Sort(SortByDate, SortOrderAscending); + i = 0; + while (size > limit && i < items.Size()) + { + size -= items[i]->m_dwSize; + db.RemovePackage(items[i]->GetPath()); + CFileUtils::DeleteItem(items[i++],true); + } + } + + // clean up our mess + for (std::map::iterator it = packs.begin(); it != packs.end(); ++it) + delete it->second; +} + +int64_t CAddonInstaller::EnumeratePackageFolder(std::map& result) +{ + CFileItemList items; + CDirectory::GetDirectory("special://home/addons/packages/",items,".zip",DIR_FLAG_NO_FILE_DIRS); + int64_t size = 0; + for (int i = 0; i < items.Size(); i++) + { + if (items[i]->m_bIsFolder) + continue; + + size += items[i]->m_dwSize; + std::string pack,dummy; + AddonVersion::SplitFileName(pack, dummy, items[i]->GetLabel()); + if (result.find(pack) == result.end()) + result[pack] = new CFileItemList; + result[pack]->Add(CFileItemPtr(new CFileItem(*items[i]))); + } + + return size; +} + +CAddonInstallJob::CAddonInstallJob(const AddonPtr &addon, const std::string &hash /* = "" */, bool update /* = false */, const std::string &referer /* = "" */) + : m_addon(addon), + m_hash(hash), + m_update(update), + m_referer(referer) +{ } + +AddonPtr CAddonInstallJob::GetRepoForAddon(const AddonPtr& addon) +{ + AddonPtr repoPtr; + + CAddonDatabase database; + if (!database.Open()) + return repoPtr; + + std::string repo; + if (!database.GetRepoForAddon(addon->ID(), repo)) + return repoPtr; + + if (!CAddonMgr::Get().GetAddon(repo, repoPtr)) + return repoPtr; + + if (std::dynamic_pointer_cast(repoPtr) == NULL) + { + repoPtr.reset(); + return repoPtr; + } + + return repoPtr; +} + +bool CAddonInstallJob::GetAddonWithHash(const std::string& addonID, ADDON::AddonPtr& addon, std::string& hash) +{ + CAddonDatabase database; + if (!database.Open()) + return false; + + if (!database.GetAddon(addonID, addon)) + return false; + + AddonPtr ptr = GetRepoForAddon(addon); + if (ptr == NULL) + return false; + + RepositoryPtr repo = std::dynamic_pointer_cast(ptr); + if (repo == NULL) + return false; + + hash = repo->GetAddonHash(addon); + return true; +} + +bool CAddonInstallJob::DoWork() +{ + SetTitle(StringUtils::Format(g_localizeStrings.Get(24057).c_str(), m_addon->Name().c_str())); + SetProgress(0); + + // check whether all the dependencies are available or not + SetText(g_localizeStrings.Get(24058)); + if (!CAddonInstaller::Get().CheckDependencies(m_addon)) + { + CLog::Log(LOGERROR, "CAddonInstallJob[%s]: dependency check failed", m_addon->ID().c_str()); + ReportInstallError(m_addon->ID(), m_addon->ID(), g_localizeStrings.Get(24044)); + return false; + } + + AddonPtr repoPtr = GetRepoForAddon(m_addon); + std::string installFrom; + if (!repoPtr || repoPtr->Props().libname.empty()) + { + // Addons are installed by downloading the .zip package on the server to the local + // packages folder, then extracting from the local .zip package into the addons folder + // Both these functions are achieved by "copying" using the vfs. + + std::string dest = "special://home/addons/packages/"; + std::string package = URIUtils::AddFileToFolder("special://home/addons/packages/", + URIUtils::GetFileName(m_addon->Path())); + if (URIUtils::HasSlashAtEnd(m_addon->Path())) + { // passed in a folder - all we need do is copy it across + installFrom = m_addon->Path(); + } + else + { + CAddonDatabase db; + if (!db.Open()) + { + CLog::Log(LOGERROR, "CAddonInstallJob[%s]: failed to open database", m_addon->ID().c_str()); + ReportInstallError(m_addon->ID(), m_addon->ID()); + return false; + } + + std::string md5; + // check that we don't already have a valid copy + if (!m_hash.empty() && CFile::Exists(package)) + { + if (db.GetPackageHash(m_addon->ID(), package, md5) && m_hash != md5) + { + db.RemovePackage(package); + CFile::Delete(package); + } + } + + // zip passed in - download + extract + if (!CFile::Exists(package)) + { + std::string path(m_addon->Path()); + if (!m_referer.empty() && URIUtils::IsInternetStream(path)) + { + CURL url(path); + url.SetProtocolOptions(m_referer); + path = url.Get(); + } + + if (!DownloadPackage(path, dest)) + { + CFile::Delete(package); + + CLog::Log(LOGERROR, "CAddonInstallJob[%s]: failed to download %s", m_addon->ID().c_str(), package.c_str()); + ReportInstallError(m_addon->ID(), URIUtils::GetFileName(package)); + return false; + } + } + + // at this point we have the package - check that it is valid + SetText(g_localizeStrings.Get(24077)); + if (!m_hash.empty()) + { + md5 = CUtil::GetFileMD5(package); + if (!StringUtils::EqualsNoCase(md5, m_hash)) + { + CFile::Delete(package); + + CLog::Log(LOGERROR, "CAddonInstallJob[%s]: MD5 mismatch after download %s", m_addon->ID().c_str(), package.c_str()); + ReportInstallError(m_addon->ID(), URIUtils::GetFileName(package)); + return false; + } + + db.AddPackage(m_addon->ID(), package, md5); + } + + // check the archive as well - should have just a single folder in the root + CURL archive = URIUtils::CreateArchivePath("zip", CURL(package), ""); + + CFileItemList archivedFiles; + CDirectory::GetDirectory(archive, archivedFiles); + + if (archivedFiles.Size() != 1 || !archivedFiles[0]->m_bIsFolder) + { + // invalid package + db.RemovePackage(package); + CFile::Delete(package); + + CLog::Log(LOGERROR, "CAddonInstallJob[%s]: invalid package %s", m_addon->ID().c_str(), package.c_str()); + ReportInstallError(m_addon->ID(), URIUtils::GetFileName(package)); + return false; + } + + installFrom = archivedFiles[0]->GetPath(); + } + repoPtr.reset(); + } + + // run any pre-install functions + bool reloadAddon = OnPreInstall(); + + // perform install + if (!Install(installFrom, repoPtr)) + return false; + + // run any post-install guff + OnPostInstall(reloadAddon); + + // and we're done! + MarkFinished(); + return true; +} + +bool CAddonInstallJob::DownloadPackage(const std::string &path, const std::string &dest) +{ + if (ShouldCancel(0, 1)) + return false; + + SetText(g_localizeStrings.Get(24078)); + + // need to download/copy the package first + CFileItemList list; + list.Add(CFileItemPtr(new CFileItem(path, false))); + list[0]->Select(true); + + return DoFileOperation(CFileOperationJob::ActionReplace, list, dest, true); +} + +bool CAddonInstallJob::DoFileOperation(FileAction action, CFileItemList &items, const std::string &file, bool useSameJob /* = true */) +{ + bool result = false; + if (useSameJob) + { + SetFileOperation(action, items, file); + + // temporarily disable auto-closing so not to close the current progress indicator + bool autoClose = GetAutoClose(); + if (autoClose) + SetAutoClose(false); + // temporarily disable updating title or text + bool updateInformation = GetUpdateInformation(); + if (updateInformation) + SetUpdateInformation(false); + + result = CFileOperationJob::DoWork(); + + SetUpdateInformation(updateInformation); + SetAutoClose(autoClose); + } + else + { + CFileOperationJob job(action, items, file); + + // pass our progress indicators to the temporary job and only allow it to + // show progress updates (no title or text changes) + job.SetProgressIndicators(GetProgressBar(), GetProgressDialog(), GetUpdateProgress(), false); + + result = job.DoWork(); + } + + return result; +} + +bool CAddonInstallJob::OnPreInstall() +{ + return m_addon->OnPreInstall(); +} + +bool CAddonInstallJob::DeleteAddon(const std::string &addonFolder) +{ + CFileItemList list; + list.Add(CFileItemPtr(new CFileItem(addonFolder, true))); + list[0]->Select(true); + + return DoFileOperation(CFileOperationJob::ActionDelete, list, "", false); +} + +bool CAddonInstallJob::Install(const std::string &installFrom, const AddonPtr& repo) +{ + SetText(g_localizeStrings.Get(24079)); + ADDONDEPS deps = m_addon->GetDeps(); + + unsigned int totalSteps = static_cast(deps.size()); + if (ShouldCancel(0, totalSteps)) + return false; + + // The first thing we do is install dependencies + std::string referer = StringUtils::Format("Referer=%s-%s.zip",m_addon->ID().c_str(),m_addon->Version().asString().c_str()); + for (ADDONDEPS::iterator it = deps.begin(); it != deps.end(); ++it) + { + if (it->first != "xbmc.metadata") + { + const std::string &addonID = it->first; + const AddonVersion &version = it->second.first; + bool optional = it->second.second; + AddonPtr dependency; + bool haveAddon = CAddonMgr::Get().GetAddon(addonID, dependency); + if ((haveAddon && !dependency->MeetsVersion(version)) || (!haveAddon && !optional)) + { + // we have it but our version isn't good enough, or we don't have it and we need it + bool force = dependency != NULL; + + // dependency is already queued up for install - ::Install will fail + // instead we wait until the Job has finished. note that we + // recall install on purpose in case prior installation failed + if (CAddonInstaller::Get().HasJob(addonID)) + { + while (CAddonInstaller::Get().HasJob(addonID)) + Sleep(50); + force = false; + + if (!CAddonMgr::Get().IsAddonInstalled(addonID)) + { + CLog::Log(LOGERROR, "CAddonInstallJob[%s]: failed to install dependency %s", m_addon->ID().c_str(), addonID.c_str()); + ReportInstallError(m_addon->ID(), m_addon->ID(), g_localizeStrings.Get(24085)); + return false; + } + } + // don't have the addon or the addon isn't new enough - grab it (no new job for these) + else if (IsModal()) + { + AddonPtr addon; + std::string hash; + if (!CAddonInstallJob::GetAddonWithHash(addonID, addon, hash)) + { + CLog::Log(LOGERROR, "CAddonInstallJob[%s]: failed to find dependency %s", m_addon->ID().c_str(), addonID.c_str()); + ReportInstallError(m_addon->ID(), m_addon->ID(), g_localizeStrings.Get(24085)); + return false; + } + + CAddonInstallJob dependencyJob(addon, hash, force, referer); + + // pass our progress indicators to the temporary job and don't allow it to + // show progress or information updates (no progress, title or text changes) + dependencyJob.SetProgressIndicators(GetProgressBar(), GetProgressDialog(), false, false); + + if (!dependencyJob.DoModal()) + { + CLog::Log(LOGERROR, "CAddonInstallJob[%s]: failed to install dependency %s", m_addon->ID().c_str(), addonID.c_str()); + ReportInstallError(m_addon->ID(), m_addon->ID(), g_localizeStrings.Get(24085)); + return false; + } + } + else if (!CAddonInstaller::Get().Install(addonID, force, referer, false)) + { + CLog::Log(LOGERROR, "CAddonInstallJob[%s]: failed to install dependency %s", m_addon->ID().c_str(), addonID.c_str()); + ReportInstallError(m_addon->ID(), m_addon->ID(), g_localizeStrings.Get(24085)); + return false; + } + } + } + + if (ShouldCancel(std::distance(deps.begin(), it), totalSteps)) + return false; + } + + SetText(g_localizeStrings.Get(24086)); + SetProgress(0); + + // now that we have all our dependencies, we can install our add-on + if (repo != NULL) + { + CFileItemList dummy; + std::string s = StringUtils::Format("plugin://%s/?action=install&package=%s&version=%s", repo->ID().c_str(), + m_addon->ID().c_str(), m_addon->Version().asString().c_str()); + if (!CDirectory::GetDirectory(s, dummy)) + { + CLog::Log(LOGERROR, "CAddonInstallJob[%s]: installation of repository failed", m_addon->ID().c_str()); + ReportInstallError(m_addon->ID(), m_addon->ID()); + return false; + } + } + else + { + std::string addonFolder = installFrom; + URIUtils::RemoveSlashAtEnd(addonFolder); + addonFolder = URIUtils::AddFileToFolder("special://home/addons/", URIUtils::GetFileName(addonFolder)); + + CFileItemList install; + install.Add(CFileItemPtr(new CFileItem(installFrom, true))); + install[0]->Select(true); + + AddonPtr addon; + if (!DoFileOperation(CFileOperationJob::ActionReplace, install, "special://home/addons/", false) || + !CAddonMgr::Get().LoadAddonDescription(addonFolder, addon)) + { + // failed extraction or failed to load addon description + DeleteAddon(addonFolder); + + std::string addonID = URIUtils::GetFileName(addonFolder); + CLog::Log(LOGERROR, "CAddonInstallJob[%s]: could not read addon description of %s", addonID.c_str(), addonFolder.c_str()); + ReportInstallError(addonID, addonID); + return false; + } + + // Update the addon manager so that it has the newly installed add-on. + CAddonMgr::Get().FindAddons(); + } + SetProgress(100); + + return true; +} + +void CAddonInstallJob::OnPostInstall(bool reloadAddon) +{ + if (!IsModal() && CSettings::Get().GetBool("general.addonnotifications")) + CGUIDialogKaiToast::QueueNotification(m_addon->Icon(), m_addon->Name(), + g_localizeStrings.Get(m_update ? 24065 : 24064), + TOAST_DISPLAY_TIME, false, TOAST_DISPLAY_TIME); + + m_addon->OnPostInstall(reloadAddon, m_update, IsModal()); +} + +void CAddonInstallJob::ReportInstallError(const std::string& addonID, const std::string& fileName, const std::string& message /* = "" */) +{ + AddonPtr addon; + CAddonDatabase database; + if (database.Open()) + { + database.GetAddon(addonID, addon); + database.Close(); + } + + MarkFinished(); + + std::string msg = message; + if (addon != NULL) + { + AddonPtr addon2; + CAddonMgr::Get().GetAddon(addonID, addon2); + if (msg.empty()) + msg = g_localizeStrings.Get(addon2 != NULL ? 113 : 114); + + if (IsModal()) + CGUIDialogOK::ShowAndGetInput(m_addon->Name(), msg); + else + CGUIDialogKaiToast::QueueNotification(addon->Icon(), addon->Name(), msg, TOAST_DISPLAY_TIME, false); + } + else + { + if (msg.empty()) + msg = g_localizeStrings.Get(114); + if (IsModal()) + CGUIDialogOK::ShowAndGetInput(fileName, msg); + else + CGUIDialogKaiToast::QueueNotification(CGUIDialogKaiToast::Error, fileName, msg, TOAST_DISPLAY_TIME, false); + } +} + +std::string CAddonInstallJob::AddonID() const +{ + return m_addon ? m_addon->ID() : ""; +} + +CAddonUnInstallJob::CAddonUnInstallJob(const AddonPtr &addon) + : m_addon(addon) +{ } + +bool CAddonUnInstallJob::DoWork() +{ + m_addon->OnPreUnInstall(); + + AddonPtr repoPtr = CAddonInstallJob::GetRepoForAddon(m_addon); + RepositoryPtr therepo = std::dynamic_pointer_cast(repoPtr); + if (therepo != NULL && !therepo->Props().libname.empty()) + { + CFileItemList dummy; + std::string s = StringUtils::Format("plugin://%s/?action=uninstall&package=%s", therepo->ID().c_str(), m_addon->ID().c_str()); + if (!CDirectory::GetDirectory(s, dummy)) + return false; + } + else if (!DeleteAddon(m_addon->Path())) + return false; + + OnPostUnInstall(); + + return true; +} + +bool CAddonUnInstallJob::DeleteAddon(const std::string &addonFolder) +{ + CFileItemList list; + list.Add(CFileItemPtr(new CFileItem(addonFolder, true))); + list[0]->Select(true); + + SetFileOperation(CFileOperationJob::ActionDelete, list, ""); + return CFileOperationJob::DoWork(); +} + +void CAddonUnInstallJob::OnPostUnInstall() +{ + bool bSave = false; + CFileItemList items; + XFILE::CFavouritesDirectory::Load(items); + for (int i = 0; i < items.Size(); i++) + { + if (items[i]->GetPath().find(m_addon->ID()) != std::string::npos) + { + items.Remove(items[i].get()); + bSave = true; + } + } + + if (bSave) + CFavouritesDirectory::Save(items); + + m_addon->OnPostUnInstall(); +} diff --git a/xbmc/addons/AddonInstaller.h b/xbmc/addons/AddonInstaller.h new file mode 100644 index 0000000..aca93d8 --- /dev/null +++ b/xbmc/addons/AddonInstaller.h @@ -0,0 +1,229 @@ +#pragma once +/* + * Copyright (C) 2011-2013 Team XBMC + * http://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, see + * . + * + */ + +#include "utils/FileOperationJob.h" +#include "addons/Addon.h" +#include "utils/Stopwatch.h" +#include "threads/Event.h" + +class CAddonDatabase; + +enum { + AUTO_UPDATES_ON = 0, + AUTO_UPDATES_NOTIFY, + AUTO_UPDATES_NEVER, + AUTO_UPDATES_MAX +}; + +class CAddonInstaller : public IJobCallback +{ +public: + static CAddonInstaller &Get(); + + bool IsDownloading() const; + void GetInstallList(ADDON::VECADDONS &addons) const; + bool GetProgress(const std::string &addonID, unsigned int &percent) const; + bool Cancel(const std::string &addonID); + + /*! \brief Installs the addon while showing a modal progress dialog + \param addonID the addon ID of the item to install. + \param addon [out] the installed addon for later use. + \param promptForInstall Whether or not to prompt the user before installing the addon. + \return true on successful install, false otherwise. + \sa Install + */ + bool InstallModal(const std::string &addonID, ADDON::AddonPtr &addon, bool promptForInstall = true); + + /*! \brief Install an addon if it is available in a repository + \param addonID the addon ID of the item to install + \param force whether to force the install even if the addon is already installed (eg for updating). Defaults to false. + \param referer string to use for referer for http fetch. Set to previous version when updating, parent when fetching a dependency + \param background whether to install in the background or not. Defaults to true. + \param modal whether to show a modal dialog when not installing in background + \return true on successful install, false on failure. + \sa DoInstall + */ + bool Install(const std::string &addonID, bool force = false, const std::string &referer="", bool background = true, bool modal = false); + + /*! \brief Install an addon from the given zip path + \param path the zip file to install from + \return true if successful, false otherwise + \sa DoInstall + */ + bool InstallFromZip(const std::string &path); + + /*! \brief Install a set of addons from the official repository (if needed) + \param addonIDs a set of addon IDs to install + */ + void InstallFromXBMCRepo(const std::set &addonIDs); + + /*! \brief Check whether dependencies of an addon exist or are installable. + Iterates through the addon's dependencies, checking they're installed or installable. + Each dependency must also satisfies CheckDependencies in turn. + \param addon the addon to check + \param database the database instance to update. Defaults to NULL. + \return true if dependencies are available, false otherwise. + */ + bool CheckDependencies(const ADDON::AddonPtr &addon, CAddonDatabase *database = NULL); + + /*! \brief Update all repositories (if needed) + Runs through all available repositories and queues an update of them if they + need it (according to the set timeouts) or if forced. Optionally busy wait + until the repository updates are complete. + \param force whether we should run an update regardless of the normal update cycle. Defaults to false. + \param wait whether we should busy wait for the updates to be performed. Defaults to false. + */ + + /*! \brief Check if an installation job for a given add-on is already queued up + * \param ID The ID of the add-on + * \return true if a job exists, false otherwise + */ + bool HasJob(const std::string& ID) const; + + /*! \brief Fetch the last repository update time. + \return the last time a repository was updated. + */ + CDateTime LastRepoUpdate() const; + void UpdateRepos(bool force = false, bool wait = false); + + void OnJobComplete(unsigned int jobID, bool success, CJob* job); + void OnJobProgress(unsigned int jobID, unsigned int progress, unsigned int total, const CJob *job); + + class CDownloadJob + { + public: + CDownloadJob(unsigned int id) + { + jobID = id; + progress = 0; + } + unsigned int jobID; + unsigned int progress; + }; + + typedef std::map JobMap; + +private: + // private construction, and no assignements; use the provided singleton methods + CAddonInstaller(); + CAddonInstaller(const CAddonInstaller&); + CAddonInstaller const& operator=(CAddonInstaller const&); + virtual ~CAddonInstaller(); + + /*! \brief Install an addon from a repository or zip + \param addon the AddonPtr describing the addon + \param hash the hash to verify the install. Defaults to "". + \param update whether this is an update of an existing addon, or a new install. Defaults to false. + \param referer string to use for referer for http fetch. Defaults to "". + \param background whether to install in the background or not. Defaults to true. + \return true on successful install, false on failure. + */ + bool DoInstall(const ADDON::AddonPtr &addon, const std::string &hash = "", bool update = false, const std::string &referer = "", bool background = true, bool modal = false); + + /*! \brief Check whether dependencies of an addon exist or are installable. + Iterates through the addon's dependencies, checking they're installed or installable. + Each dependency must also satisfies CheckDependencies in turn. + \param addon the addon to check + \param preDeps previous dependencies encountered during recursion. aids in avoiding infinite recursion + \param database database instance to update + \return true if dependencies are available, false otherwise. + */ + bool CheckDependencies(const ADDON::AddonPtr &addon, std::vector& preDeps, CAddonDatabase &database); + + void PrunePackageCache(); + int64_t EnumeratePackageFolder(std::map& result); + + CCriticalSection m_critSection; + JobMap m_downloadJobs; + CStopWatch m_repoUpdateWatch; ///< repository updates are done based on this counter + unsigned int m_repoUpdateJob; ///< the job ID of the repository updates + CEvent m_repoUpdateDone; ///< event set when the repository updates are complete +}; + +class CAddonInstallJob : public CFileOperationJob +{ +public: + CAddonInstallJob(const ADDON::AddonPtr &addon, const std::string &hash = "", bool update = false, const std::string &referer = ""); + + virtual bool DoWork(); + + /*! \brief return the id of the addon being installed + \return id of the installing addon + */ + std::string AddonID() const; + + /*! \brief Find which repository hosts an add-on + * \param addon The add-on to find the repository for + * \return The hosting repository + */ + static ADDON::AddonPtr GetRepoForAddon(const ADDON::AddonPtr& addon); + + /*! \brief Find the add-on and itshash for the given add-on ID + * \param addonID ID of the add-on to find + * \param addon Add-on with the given add-on ID + * \param hash Hash of the add-on + * \return True if the add-on and its hash were found, false otherwise. + */ + static bool GetAddonWithHash(const std::string& addonID, ADDON::AddonPtr& addon, std::string& hash); + +private: + bool OnPreInstall(); + void OnPostInstall(bool reloadAddon); + bool Install(const std::string &installFrom, const ADDON::AddonPtr& repo = ADDON::AddonPtr()); + bool DownloadPackage(const std::string &path, const std::string &dest); + + /*! \brief Delete an addon following install failure + \param addonFolder - the folder to delete + */ + bool DeleteAddon(const std::string &addonFolder); + + bool DoFileOperation(FileAction action, CFileItemList &items, const std::string &file, bool useSameJob = true); + + /*! \brief Queue a notification for addon installation/update failure + \param addonID - addon id + \param fileName - filename which is shown in case the addon id is unknown + \param message - error message to be displayed + */ + void ReportInstallError(const std::string& addonID, const std::string& fileName, const std::string& message = ""); + + ADDON::AddonPtr m_addon; + std::string m_hash; + bool m_update; + std::string m_referer; +}; + +class CAddonUnInstallJob : public CFileOperationJob +{ +public: + CAddonUnInstallJob(const ADDON::AddonPtr &addon); + + virtual bool DoWork(); + +private: + /*! \brief Delete an addon following install failure + \param addonFolder - the folder to delete + */ + bool DeleteAddon(const std::string &addonFolder); + + void OnPostUnInstall(); + + ADDON::AddonPtr m_addon; +}; diff --git a/xbmc/addons/AddonManager.cpp b/xbmc/addons/AddonManager.cpp new file mode 100644 index 0000000..9ab5c31 --- /dev/null +++ b/xbmc/addons/AddonManager.cpp @@ -0,0 +1,1030 @@ +/* + * Copyright (C) 2005-2013 Team XBMC + * http://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, see + * . + * + */ +#include "AddonManager.h" +#include "Addon.h" +#include "AudioEncoder.h" +#include "DllLibCPluff.h" +#include "utils/StringUtils.h" +#include "utils/JobManager.h" +#include "threads/SingleLock.h" +#include "FileItem.h" +#include "LangInfo.h" +#include "settings/AdvancedSettings.h" +#include "settings/Settings.h" +#include "utils/log.h" +#include "utils/XBMCTinyXML.h" +#ifdef HAS_VISUALISATION +#include "Visualisation.h" +#endif +#ifdef HAS_SCREENSAVER +#include "ScreenSaver.h" +#endif +#ifdef HAS_PVRCLIENTS +#include "DllPVRClient.h" +#include "pvr/addons/PVRClient.h" +#endif +//#ifdef HAS_SCRAPERS +#include "Scraper.h" +//#endif +#include "PluginSource.h" +#include "Repository.h" +#include "Skin.h" +#include "Service.h" +#include "ContextItemAddon.h" +#include "Util.h" +#include "addons/Webinterface.h" + +using namespace std; +using namespace XFILE; + +namespace ADDON +{ + +cp_log_severity_t clog_to_cp(int lvl); +void cp_fatalErrorHandler(const char *msg); +void cp_logger(cp_log_severity_t level, const char *msg, const char *apid, void *user_data); + +/********************************************************** + * CAddonMgr + * + */ + +map CAddonMgr::m_managers; + +AddonPtr CAddonMgr::Factory(const cp_extension_t *props) +{ + if (!PlatformSupportsAddon(props->plugin)) + return AddonPtr(); + + /* Check if user directories need to be created */ + const cp_cfg_element_t *settings = GetExtElement(props->configuration, "settings"); + if (settings) + CheckUserDirs(settings); + + const TYPE type = TranslateType(props->ext_point_id); + switch (type) + { + case ADDON_PLUGIN: + case ADDON_SCRIPT: + return AddonPtr(new CPluginSource(props)); + case ADDON_SCRIPT_LIBRARY: + case ADDON_SCRIPT_LYRICS: + case ADDON_SCRIPT_MODULE: + case ADDON_SUBTITLE_MODULE: + return AddonPtr(new CAddon(props)); + case ADDON_WEB_INTERFACE: + return AddonPtr(new CWebinterface(props)); + case ADDON_SCRIPT_WEATHER: + { + // Eden (API v2.0) broke old weather add-ons + AddonPtr result(new CAddon(props)); + AddonVersion ver1 = result->GetDependencyVersion("xbmc.python"); + AddonVersion ver2 = AddonVersion("2.0"); + if (ver1 < ver2) + { + CLog::Log(LOGINFO,"%s: Weather add-ons for api < 2.0 unsupported (%s)",__FUNCTION__,result->ID().c_str()); + return AddonPtr(); + } + return result; + } + case ADDON_SERVICE: + return AddonPtr(new CService(props)); + case ADDON_SCRAPER_ALBUMS: + case ADDON_SCRAPER_ARTISTS: + case ADDON_SCRAPER_MOVIES: + case ADDON_SCRAPER_MUSICVIDEOS: + case ADDON_SCRAPER_TVSHOWS: + case ADDON_SCRAPER_LIBRARY: + return AddonPtr(new CScraper(props)); + case ADDON_VIZ: + case ADDON_SCREENSAVER: + case ADDON_PVRDLL: + case ADDON_AUDIOENCODER: + { // begin temporary platform handling for Dlls + // ideally platforms issues will be handled by C-Pluff + // this is not an attempt at a solution + std::string value; + if (type == ADDON_SCREENSAVER && 0 == strnicmp(props->plugin->identifier, "screensaver.xbmc.builtin.", 25)) + { // built in screensaver + return AddonPtr(new CAddon(props)); + } + if (type == ADDON_SCREENSAVER) + { // Python screensaver + std::string library = CAddonMgr::Get().GetExtValue(props->configuration, "@library"); + if (URIUtils::HasExtension(library, ".py")) + return AddonPtr(new CScreenSaver(props)); + } + if (type == ADDON_AUDIOENCODER && 0 == strncmp(props->plugin->identifier, + "audioencoder.xbmc.builtin.", 26)) + { // built in audio encoder + return AddonPtr(new CAudioEncoder(props)); + } + std::string tograb; +#if defined(TARGET_ANDROID) + tograb = "@library_android"; +#elif defined(TARGET_LINUX) || defined(TARGET_FREEBSD) + tograb = "@library_linux"; +#elif defined(TARGET_WINDOWS) && defined(HAS_DX) + tograb = "@library_windx"; +#elif defined(TARGET_DARWIN) + tograb = "@library_osx"; +#endif + value = GetExtValue(props->plugin->extensions->configuration, tograb.c_str()); + if (value.empty()) + break; + if (type == ADDON_VIZ) + { +#if defined(HAS_VISUALISATION) + return AddonPtr(new CVisualisation(props)); +#endif + } + else if (type == ADDON_PVRDLL) + { +#ifdef HAS_PVRCLIENTS + return AddonPtr(new PVR::CPVRClient(props)); +#endif + } + else if (type == ADDON_AUDIOENCODER) + return AddonPtr(new CAudioEncoder(props)); + else + return AddonPtr(new CScreenSaver(props)); + } + case ADDON_SKIN: + return AddonPtr(new CSkinInfo(props)); + case ADDON_VIZ_LIBRARY: + return AddonPtr(new CAddonLibrary(props)); + case ADDON_REPOSITORY: + return AddonPtr(new CRepository(props)); + case ADDON_CONTEXT_ITEM: + return AddonPtr(new CContextItemAddon(props)); + default: + break; + } + return AddonPtr(); +} + +bool CAddonMgr::CheckUserDirs(const cp_cfg_element_t *settings) +{ + if (!settings) + return false; + + const cp_cfg_element_t *userdirs = GetExtElement((cp_cfg_element_t *)settings, "userdirs"); + if (!userdirs) + return false; + + ELEMENTS elements; + if (!GetExtElements((cp_cfg_element_t *)userdirs, "userdir", elements)) + return false; + + ELEMENTS::iterator itr = elements.begin(); + while (itr != elements.end()) + { + std::string path = GetExtValue(*itr++, "@path"); + if (!CDirectory::Exists(path)) + { + if (!CUtil::CreateDirectoryEx(path)) + { + CLog::Log(LOGERROR, "CAddonMgr::CheckUserDirs: Unable to create directory %s.", path.c_str()); + return false; + } + } + } + + return true; +} + +CAddonMgr::CAddonMgr() +{ + m_cpluff = NULL; +} + +CAddonMgr::~CAddonMgr() +{ + DeInit(); +} + +CAddonMgr &CAddonMgr::Get() +{ + static CAddonMgr sAddonMgr; + return sAddonMgr; +} + +IAddonMgrCallback* CAddonMgr::GetCallbackForType(TYPE type) +{ + if (m_managers.find(type) == m_managers.end()) + return NULL; + else + return m_managers[type]; +} + +bool CAddonMgr::RegisterAddonMgrCallback(const TYPE type, IAddonMgrCallback* cb) +{ + if (cb == NULL) + return false; + + m_managers.erase(type); + m_managers[type] = cb; + + return true; +} + +void CAddonMgr::UnregisterAddonMgrCallback(TYPE type) +{ + m_managers.erase(type); +} + +bool CAddonMgr::Init() +{ + m_cpluff = new DllLibCPluff; + m_cpluff->Load(); + + m_database.Open(); + + if (!m_cpluff->IsLoaded()) + { + CLog::Log(LOGERROR, "ADDONS: Fatal Error, could not load libcpluff"); + return false; + } + + m_cpluff->set_fatal_error_handler(cp_fatalErrorHandler); + + cp_status_t status; + status = m_cpluff->init(); + if (status != CP_OK) + { + CLog::Log(LOGERROR, "ADDONS: Fatal Error, cp_init() returned status: %i", status); + return false; + } + + //TODO could separate addons into different contexts + // would allow partial unloading of addon framework + m_cp_context = m_cpluff->create_context(&status); + assert(m_cp_context); + status = m_cpluff->register_pcollection(m_cp_context, CSpecialProtocol::TranslatePath("special://home/addons").c_str()); + if (status != CP_OK) + { + CLog::Log(LOGERROR, "ADDONS: Fatal Error, cp_register_pcollection() returned status: %i", status); + return false; + } + + status = m_cpluff->register_pcollection(m_cp_context, CSpecialProtocol::TranslatePath("special://xbmc/addons").c_str()); + if (status != CP_OK) + { + CLog::Log(LOGERROR, "ADDONS: Fatal Error, cp_register_pcollection() returned status: %i", status); + return false; + } + + status = m_cpluff->register_pcollection(m_cp_context, CSpecialProtocol::TranslatePath("special://xbmcbin/addons").c_str()); + if (status != CP_OK) + { + CLog::Log(LOGERROR, "ADDONS: Fatal Error, cp_register_pcollection() returned status: %i", status); + return false; + } + + status = m_cpluff->register_logger(m_cp_context, cp_logger, + &CAddonMgr::Get(), clog_to_cp(g_advancedSettings.m_logLevel)); + if (status != CP_OK) + { + CLog::Log(LOGERROR, "ADDONS: Fatal Error, cp_register_logger() returned status: %i", status); + return false; + } + + FindAddons(); + + VECADDONS repos; + if (GetAddons(ADDON_REPOSITORY, repos)) + { + VECADDONS::iterator it = repos.begin(); + for (;it != repos.end(); ++it) + CLog::Log(LOGNOTICE, "ADDONS: Using repository %s", (*it)->ID().c_str()); + } + + return true; +} + +void CAddonMgr::DeInit() +{ + if (m_cpluff) + m_cpluff->destroy(); + delete m_cpluff; + m_cpluff = NULL; + m_database.Close(); + m_disabled.clear(); +} + +bool CAddonMgr::HasAddons(const TYPE &type, bool enabled /*= true*/) +{ + // TODO: This isn't particularly efficient as we create an addon type for each addon using the Factory, just so + // we can check addon dependencies in the addon constructor. + VECADDONS addons; + return GetAddons(type, addons, enabled); +} + +bool CAddonMgr::GetAllAddons(VECADDONS &addons, bool enabled /*= true*/, bool allowRepos /* = false */) +{ + for (int i = ADDON_UNKNOWN+1; i < ADDON_MAX; ++i) + { + if (!allowRepos && ADDON_REPOSITORY == (TYPE)i) + continue; + VECADDONS temp; + if (CAddonMgr::Get().GetAddons((TYPE)i, temp, enabled)) + addons.insert(addons.end(), temp.begin(), temp.end()); + } + return !addons.empty(); +} + +void CAddonMgr::AddToUpdateableAddons(AddonPtr &pAddon) +{ + CSingleLock lock(m_critSection); + m_updateableAddons.push_back(pAddon); +} + +void CAddonMgr::RemoveFromUpdateableAddons(AddonPtr &pAddon) +{ + CSingleLock lock(m_critSection); + VECADDONS::iterator it = std::find(m_updateableAddons.begin(), m_updateableAddons.end(), pAddon); + + if(it != m_updateableAddons.end()) + { + m_updateableAddons.erase(it); + } +} + +struct AddonIdFinder +{ + AddonIdFinder(const std::string& id) + : m_id(id) + {} + + bool operator()(const AddonPtr& addon) + { + return m_id == addon->ID(); + } + private: + std::string m_id; +}; + +bool CAddonMgr::ReloadSettings(const std::string &id) +{ + CSingleLock lock(m_critSection); + VECADDONS::iterator it = std::find_if(m_updateableAddons.begin(), m_updateableAddons.end(), AddonIdFinder(id)); + + if( it != m_updateableAddons.end()) + { + return (*it)->ReloadSettings(); + } + return false; +} + +bool CAddonMgr::GetAllOutdatedAddons(VECADDONS &addons, bool getLocalVersion /*= false*/) +{ + CSingleLock lock(m_critSection); + for (int i = ADDON_UNKNOWN+1; i < ADDON_MAX; ++i) + { + VECADDONS temp; + if (CAddonMgr::Get().GetAddons((TYPE)i, temp, true)) + { + AddonPtr repoAddon; + for (unsigned int j = 0; j < temp.size(); j++) + { + // Ignore duplicates due to add-ons with multiple extension points + bool found = false; + for (VECADDONS::const_iterator addonIt = addons.begin(); addonIt != addons.end(); ++addonIt) + { + if ((*addonIt)->ID() == temp[j]->ID()) + found = true; + } + + if (found || !m_database.GetAddon(temp[j]->ID(), repoAddon)) + continue; + + if (temp[j]->Version() < repoAddon->Version() && + !m_database.IsAddonBlacklisted(temp[j]->ID(), + repoAddon->Version().asString().c_str())) + { + if (getLocalVersion) + repoAddon->Props().version = temp[j]->Version(); + addons.push_back(repoAddon); + } + } + } + } + return !addons.empty(); +} + +bool CAddonMgr::HasOutdatedAddons() +{ + VECADDONS dummy; + return GetAllOutdatedAddons(dummy); +} + +bool CAddonMgr::GetAddons(const TYPE &type, VECADDONS &addons, bool enabled /* = true */) +{ + CSingleLock lock(m_critSection); + addons.clear(); + if (!m_cp_context) + return false; + cp_status_t status; + int num; + std::string ext_point(TranslateType(type)); + cp_extension_t **exts = m_cpluff->get_extensions_info(m_cp_context, ext_point.c_str(), &status, &num); + for(int i=0; i plugin->identifier) != enabled) + { + AddonPtr addon(Factory(props)); + if (addon) + { + if (enabled) + { + // if the addon has a running instance, grab that + AddonPtr runningAddon = addon->GetRunningInstance(); + if (runningAddon) + addon = runningAddon; + } + addons.push_back(addon); + } + } + } + m_cpluff->release_info(m_cp_context, exts); + return addons.size() > 0; +} + +bool CAddonMgr::GetAddon(const std::string &str, AddonPtr &addon, const TYPE &type/*=ADDON_UNKNOWN*/, bool enabledOnly /*= true*/) +{ + CSingleLock lock(m_critSection); + + cp_status_t status; + cp_plugin_info_t *cpaddon = m_cpluff->get_plugin_info(m_cp_context, str.c_str(), &status); + if (status == CP_OK && cpaddon) + { + addon = GetAddonFromDescriptor(cpaddon, type==ADDON_UNKNOWN?"":TranslateType(type)); + m_cpluff->release_info(m_cp_context, cpaddon); + + if (addon) + { + if (enabledOnly && IsAddonDisabled(addon->ID())) + return false; + + // if the addon has a running instance, grab that + AddonPtr runningAddon = addon->GetRunningInstance(); + if (runningAddon) + addon = runningAddon; + } + return NULL != addon.get(); + } + if (cpaddon) + m_cpluff->release_info(m_cp_context, cpaddon); + + return false; +} + +//TODO handle all 'default' cases here, not just scrapers & vizs +bool CAddonMgr::GetDefault(const TYPE &type, AddonPtr &addon) +{ + std::string setting; + switch (type) + { + case ADDON_VIZ: + setting = CSettings::Get().GetString("musicplayer.visualisation"); + break; + case ADDON_SCREENSAVER: + setting = CSettings::Get().GetString("screensaver.mode"); + break; + case ADDON_SCRAPER_ALBUMS: + setting = CSettings::Get().GetString("musiclibrary.albumsscraper"); + break; + case ADDON_SCRAPER_ARTISTS: + setting = CSettings::Get().GetString("musiclibrary.artistsscraper"); + break; + case ADDON_SCRAPER_MOVIES: + setting = CSettings::Get().GetString("scrapers.moviesdefault"); + break; + case ADDON_SCRAPER_MUSICVIDEOS: + setting = CSettings::Get().GetString("scrapers.musicvideosdefault"); + break; + case ADDON_SCRAPER_TVSHOWS: + setting = CSettings::Get().GetString("scrapers.tvshowsdefault"); + break; + case ADDON_WEB_INTERFACE: + setting = CSettings::Get().GetString("services.webskin"); + break; + default: + return false; + } + return GetAddon(setting, addon, type); +} + +bool CAddonMgr::SetDefault(const TYPE &type, const std::string &addonID) +{ + switch (type) + { + case ADDON_VIZ: + CSettings::Get().SetString("musicplayer.visualisation",addonID); + break; + case ADDON_SCREENSAVER: + CSettings::Get().SetString("screensaver.mode",addonID); + break; + case ADDON_SCRAPER_ALBUMS: + CSettings::Get().SetString("musiclibrary.albumsscraper",addonID); + break; + case ADDON_SCRAPER_ARTISTS: + CSettings::Get().SetString("musiclibrary.artistsscraper",addonID); + break; + case ADDON_SCRAPER_MOVIES: + CSettings::Get().SetString("scrapers.moviesdefault",addonID); + break; + case ADDON_SCRAPER_MUSICVIDEOS: + CSettings::Get().SetString("scrapers.musicvideosdefault",addonID); + break; + case ADDON_SCRAPER_TVSHOWS: + CSettings::Get().SetString("scrapers.tvshowsdefault",addonID); + break; + default: + return false; + } + + return true; +} + +std::string CAddonMgr::GetString(const std::string &id, const int number) +{ + AddonPtr addon; + if (GetAddon(id, addon)) + return addon->GetString(number); + + return ""; +} + +void CAddonMgr::FindAddons() +{ + { + CSingleLock lock(m_critSection); + if (m_cpluff && m_cp_context) + { + m_cpluff->scan_plugins(m_cp_context, CP_SP_UPGRADE); + SetChanged(); + } + } + NotifyObservers(ObservableMessageAddons); +} + +void CAddonMgr::RemoveAddon(const std::string& ID) +{ + if (m_cpluff && m_cp_context) + { + m_cpluff->uninstall_plugin(m_cp_context,ID.c_str()); + SetChanged(); + NotifyObservers(ObservableMessageAddons); + } +} + +bool CAddonMgr::DisableAddon(const std::string& ID, bool disable) +{ + CSingleLock lock(m_critSection); + if (m_database.DisableAddon(ID, disable)) + { + m_disabled[ID] = disable; + return true; + } + + return false; +} + +bool CAddonMgr::IsAddonDisabled(const std::string& ID) +{ + CSingleLock lock(m_critSection); + std::map::const_iterator it = m_disabled.find(ID); + if (it != m_disabled.end()) + return it->second; + + bool ret = m_database.IsAddonDisabled(ID); + m_disabled.insert(pair(ID, ret)); + + return ret; +} + +bool CAddonMgr::CanAddonBeDisabled(const std::string& ID) +{ + if (ID.empty()) + return false; + + CSingleLock lock(m_critSection); + AddonPtr localAddon; + // can't disable an addon that isn't installed + if (!IsAddonInstalled(ID, localAddon)) + return false; + + // can't disable an addon that is in use + if (localAddon->IsInUse()) + return false; + + // installed PVR addons can always be disabled + if (localAddon->Type() == ADDON_PVRDLL) + return true; + + std::string systemAddonsPath = CSpecialProtocol::TranslatePath("special://xbmc/addons"); + // can't disable system addons + if (StringUtils::StartsWith(localAddon->Path(), systemAddonsPath)) + return false; + + return true; +} + +bool CAddonMgr::IsAddonInstalled(const std::string& ID) +{ + AddonPtr tmp; + return IsAddonInstalled(ID, tmp); +} + +bool CAddonMgr::IsAddonInstalled(const std::string& ID, AddonPtr& addon) +{ + return GetAddon(ID, addon, ADDON_UNKNOWN, false); +} + +bool CAddonMgr::CanAddonBeInstalled(const std::string& ID) +{ + if (ID.empty()) + return false; + + CSingleLock lock(m_critSection); + // can't install already installed addon + if (IsAddonInstalled(ID)) + return false; + + // can't install broken addons + if (!m_database.IsAddonBroken(ID).empty()) + return false; + + return true; +} + +bool CAddonMgr::CanAddonBeInstalled(const AddonPtr& addon) +{ + if (addon == NULL) + return false; + + CSingleLock lock(m_critSection); + // can't install already installed addon + if (IsAddonInstalled(addon->ID())) + return false; + + // can't install broken addons + if (!addon->Props().broken.empty()) + return false; + + return true; +} + +std::string CAddonMgr::GetTranslatedString(const cp_cfg_element_t *root, const char *tag) +{ + if (!root) + return ""; + + const cp_cfg_element_t *eng = NULL; + for (unsigned int i = 0; i < root->num_children; i++) + { + const cp_cfg_element_t &child = root->children[i]; + if (strcmp(tag, child.name) == 0) + { // see if we have a "lang" attribute + const char *lang = m_cpluff->lookup_cfg_value((cp_cfg_element_t*)&child, "@lang"); + if (lang && 0 == strcmp(lang,g_langInfo.GetLanguageLocale().c_str())) + return child.value ? child.value : ""; + if (!lang || 0 == strcmp(lang, "en")) + eng = &child; + } + } + return (eng && eng->value) ? eng->value : ""; +} + +AddonPtr CAddonMgr::AddonFromProps(AddonProps& addonProps) +{ + switch (addonProps.type) + { + case ADDON_PLUGIN: + case ADDON_SCRIPT: + return AddonPtr(new CPluginSource(addonProps)); + case ADDON_SCRIPT_LIBRARY: + case ADDON_SCRIPT_LYRICS: + case ADDON_SCRIPT_WEATHER: + case ADDON_SCRIPT_MODULE: + case ADDON_SUBTITLE_MODULE: + return AddonPtr(new CAddon(addonProps)); + case ADDON_WEB_INTERFACE: + return AddonPtr(new CWebinterface(addonProps)); + case ADDON_SERVICE: + return AddonPtr(new CService(addonProps)); + case ADDON_SCRAPER_ALBUMS: + case ADDON_SCRAPER_ARTISTS: + case ADDON_SCRAPER_MOVIES: + case ADDON_SCRAPER_MUSICVIDEOS: + case ADDON_SCRAPER_TVSHOWS: + case ADDON_SCRAPER_LIBRARY: + return AddonPtr(new CScraper(addonProps)); + case ADDON_SKIN: + return AddonPtr(new CSkinInfo(addonProps)); +#if defined(HAS_VISUALISATION) + case ADDON_VIZ: + return AddonPtr(new CVisualisation(addonProps)); +#endif + case ADDON_SCREENSAVER: + return AddonPtr(new CScreenSaver(addonProps)); + case ADDON_VIZ_LIBRARY: + return AddonPtr(new CAddonLibrary(addonProps)); + case ADDON_PVRDLL: + return AddonPtr(new PVR::CPVRClient(addonProps)); + case ADDON_AUDIOENCODER: + return AddonPtr(new CAudioEncoder(addonProps)); + case ADDON_REPOSITORY: + return AddonPtr(new CRepository(addonProps)); + case ADDON_CONTEXT_ITEM: + return AddonPtr(new CContextItemAddon(addonProps)); + default: + break; + } + return AddonPtr(); +} + +/* + * libcpluff interaction + */ + +bool CAddonMgr::PlatformSupportsAddon(const cp_plugin_info_t *plugin) const +{ + if (!plugin || !plugin->num_extensions) + return false; + const cp_extension_t *metadata = GetExtension(plugin, "xbmc.addon.metadata"); // platforms; + if (CAddonMgr::Get().GetExtList(metadata->configuration, "platform", platforms)) + { + for (vector::const_iterator platform = platforms.begin(); platform != platforms.end(); ++platform) + { + if (*platform == "all") + return true; +#if defined(TARGET_ANDROID) + if (*platform == "android") +#elif defined(TARGET_LINUX) || defined(TARGET_FREEBSD) + if (*platform == "linux") +#elif defined(TARGET_WINDOWS) && defined(HAS_DX) + if (*platform == "windx") +#elif defined(TARGET_DARWIN_OSX) +// Remove this after Frodo and add an architecture filter +// in addition to platform. +#if defined(__x86_64__) + if (*platform == "osx64" || *platform == "osx") +#else + if (*platform == "osx32" || *platform == "osx") +#endif +#elif defined(TARGET_DARWIN_IOS) + if (*platform == "ios") +#endif + return true; + } + return false; // no works for us + } + return true; // assume no is equivalent to all +} + +const cp_cfg_element_t *CAddonMgr::GetExtElement(cp_cfg_element_t *base, const char *path) +{ + const cp_cfg_element_t *element = NULL; + if (base) + element = m_cpluff->lookup_cfg_element(base, path); + return element; +} + +bool CAddonMgr::GetExtElements(cp_cfg_element_t *base, const char *path, ELEMENTS &elements) +{ + if (!base || !path) + return false; + + for (unsigned int i = 0; i < base->num_children; i++) + { + std::string temp = base->children[i].name; + if (!temp.compare(path)) + elements.push_back(&base->children[i]); + } + + return !elements.empty(); +} + +const cp_extension_t *CAddonMgr::GetExtension(const cp_plugin_info_t *props, const char *extension) const +{ + if (!props) + return NULL; + for (unsigned int i = 0; i < props->num_extensions; ++i) + { + if (0 == strcmp(props->extensions[i].ext_point_id, extension)) + return &props->extensions[i]; + } + return NULL; +} + +std::string CAddonMgr::GetExtValue(cp_cfg_element_t *base, const char *path) +{ + const char *value = ""; + if (base && (value = m_cpluff->lookup_cfg_value(base, path))) + return value; + else + return ""; +} + +bool CAddonMgr::GetExtList(cp_cfg_element_t *base, const char *path, vector &result) const +{ + result.clear(); + if (!base || !path) + return false; + const char *all = m_cpluff->lookup_cfg_value(base, path); + if (!all || *all == 0) + return false; + StringUtils::Tokenize(all, result, ' '); + return true; +} + +AddonPtr CAddonMgr::GetAddonFromDescriptor(const cp_plugin_info_t *info, + const std::string& type) +{ + if (!info) + return AddonPtr(); + + if (!info->extensions && type.empty()) + { // no extensions, so we need only the dep information + return AddonPtr(new CAddon(info)); + } + + // grab a relevant extension point, ignoring our kodi.addon.metadata extension point + for (unsigned int i = 0; i < info->num_extensions; ++i) + { + if (0 != strcmp("xbmc.addon.metadata" , info->extensions[i].ext_point_id) && //extensions[i].ext_point_id) && + (type.empty() || 0 == strcmp(type.c_str(), info->extensions[i].ext_point_id))) + { // note that Factory takes care of whether or not we have platform support + return Factory(&info->extensions[i]); + } + } + return AddonPtr(); +} + +// FIXME: This function may not be required +bool CAddonMgr::LoadAddonDescription(const std::string &path, AddonPtr &addon) +{ + cp_status_t status; + cp_plugin_info_t *info = m_cpluff->load_plugin_descriptor(m_cp_context, CSpecialProtocol::TranslatePath(path).c_str(), &status); + if (info) + { + addon = GetAddonFromDescriptor(info); + m_cpluff->release_info(m_cp_context, info); + return NULL != addon.get(); + } + return false; +} + +bool CAddonMgr::AddonsFromRepoXML(const TiXmlElement *root, VECADDONS &addons) +{ + // create a context for these addons + cp_status_t status; + cp_context_t *context = m_cpluff->create_context(&status); + if (!root || !context) + return false; + + // each addon XML should have a UTF-8 declaration + TiXmlDeclaration decl("1.0", "UTF-8", ""); + const TiXmlElement *element = root->FirstChildElement("addon"); + while (element) + { + // dump the XML back to text + std::string xml; + xml << decl; + xml << *element; + cp_status_t status; + cp_plugin_info_t *info = m_cpluff->load_plugin_descriptor_from_memory(context, xml.c_str(), xml.size(), &status); + if (info) + { + AddonPtr addon = GetAddonFromDescriptor(info); + if (addon.get()) + addons.push_back(addon); + m_cpluff->release_info(context, info); + } + element = element->NextSiblingElement("addon"); + } + m_cpluff->destroy_context(context); + return true; +} + +bool CAddonMgr::LoadAddonDescriptionFromMemory(const TiXmlElement *root, AddonPtr &addon) +{ + // create a context for these addons + cp_status_t status; + cp_context_t *context = m_cpluff->create_context(&status); + if (!root || !context) + return false; + + // dump the XML back to text + std::string xml; + xml << TiXmlDeclaration("1.0", "UTF-8", ""); + xml << *root; + cp_plugin_info_t *info = m_cpluff->load_plugin_descriptor_from_memory(context, xml.c_str(), xml.size(), &status); + if (info) + { + addon = GetAddonFromDescriptor(info); + m_cpluff->release_info(context, info); + } + m_cpluff->destroy_context(context); + return addon != NULL; +} + +bool CAddonMgr::StartServices(const bool beforelogin) +{ + CLog::Log(LOGDEBUG, "ADDON: Starting service addons."); + + VECADDONS services; + if (!GetAddons(ADDON_SERVICE, services)) + return false; + + bool ret = true; + for (IVECADDONS it = services.begin(); it != services.end(); ++it) + { + std::shared_ptr service = std::dynamic_pointer_cast(*it); + if (service) + { + if ( (beforelogin && service->GetStartOption() == CService::STARTUP) + || (!beforelogin && service->GetStartOption() == CService::LOGIN) ) + ret &= service->Start(); + } + } + + return ret; +} + +void CAddonMgr::StopServices(const bool onlylogin) +{ + CLog::Log(LOGDEBUG, "ADDON: Stopping service addons."); + + VECADDONS services; + if (!GetAddons(ADDON_SERVICE, services)) + return; + + for (IVECADDONS it = services.begin(); it != services.end(); ++it) + { + std::shared_ptr service = std::dynamic_pointer_cast(*it); + if (service) + { + if ( (onlylogin && service->GetStartOption() == CService::LOGIN) + || (!onlylogin) ) + service->Stop(); + } + } +} + +int cp_to_clog(cp_log_severity_t lvl) +{ + if (lvl >= CP_LOG_ERROR) + return LOGINFO; + return LOGDEBUG; +} + +cp_log_severity_t clog_to_cp(int lvl) +{ + if (lvl >= LOG_LEVEL_DEBUG) + return CP_LOG_INFO; + return CP_LOG_ERROR; +} + +void cp_fatalErrorHandler(const char *msg) +{ + CLog::Log(LOGERROR, "ADDONS: CPluffFatalError(%s)", msg); +} + +void cp_logger(cp_log_severity_t level, const char *msg, const char *apid, void *user_data) +{ + if(!apid) + CLog::Log(cp_to_clog(level), "ADDON: cpluff: '%s'", msg); + else + CLog::Log(cp_to_clog(level), "ADDON: cpluff: '%s' reports '%s'", apid, msg); +} + +} /* namespace ADDON */ + diff --git a/xbmc/addons/AddonManager.h b/xbmc/addons/AddonManager.h new file mode 100644 index 0000000..8bc058d --- /dev/null +++ b/xbmc/addons/AddonManager.h @@ -0,0 +1,253 @@ +#pragma once +/* + * Copyright (C) 2005-2013 Team XBMC + * http://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, see + * . + * + */ +#include "Addon.h" +#include "threads/CriticalSection.h" +#include "utils/Observer.h" +#include +#include +#include +#include +#include "AddonDatabase.h" + +class DllLibCPluff; +extern "C" +{ +#include "lib/cpluff/libcpluff/cpluff.h" +} + +namespace ADDON +{ + typedef std::map MAPADDONS; + typedef std::map::iterator IMAPADDONS; + typedef std::vector ELEMENTS; + + const std::string ADDON_METAFILE = "description.xml"; + const std::string ADDON_VIS_EXT = "*.vis"; + const std::string ADDON_PYTHON_EXT = "*.py"; + const std::string ADDON_SCRAPER_EXT = "*.xml"; + const std::string ADDON_SCREENSAVER_EXT = "*.xbs"; + const std::string ADDON_PVRDLL_EXT = "*.pvr"; + const std::string ADDON_DSP_AUDIO_EXT = "*.adsp"; + const std::string ADDON_VERSION_RE = "(?\\d*)\\.?(?\\d*)?\\.?(?\\d*)?\\.?(?\\d*)?"; + + /** + * Class - IAddonMgrCallback + * This callback should be inherited by any class which manages + * specific addon types. Could be mostly used for Dll addon types to handle + * cleanup before restart/removal + */ + class IAddonMgrCallback + { + public: + virtual ~IAddonMgrCallback() {}; + virtual bool RequestRestart(AddonPtr addon, bool datachanged)=0; + virtual bool RequestRemoval(AddonPtr addon)=0; + }; + + /** + * Class - CAddonMgr + * Holds references to all addons, enabled or + * otherwise. Services the generic callbacks available + * to all addon variants. + */ + class CAddonMgr : public Observable + { + public: + static CAddonMgr &Get(); + bool ReInit() { DeInit(); return Init(); } + bool Init(); + void DeInit(); + + IAddonMgrCallback* GetCallbackForType(TYPE type); + bool RegisterAddonMgrCallback(TYPE type, IAddonMgrCallback* cb); + void UnregisterAddonMgrCallback(TYPE type); + + /* Addon access */ + bool GetDefault(const TYPE &type, AddonPtr &addon); + bool SetDefault(const TYPE &type, const std::string &addonID); + /*! \brief Retrieve a specific addon (of a specific type) + \param id the id of the addon to retrieve. + \param addon [out] the retrieved addon pointer - only use if the function returns true. + \param type type of addon to retrieve - defaults to any type. + \param enabledOnly whether we only want enabled addons - set to false to allow both enabled and disabled addons - defaults to true. + \return true if an addon matching the id of the given type is available and is enabled (if enabledOnly is true). + */ + bool GetAddon(const std::string &id, AddonPtr &addon, const TYPE &type = ADDON_UNKNOWN, bool enabledOnly = true); + bool HasAddons(const TYPE &type, bool enabled = true); + bool GetAddons(const TYPE &type, VECADDONS &addons, bool enabled = true); + bool GetAllAddons(VECADDONS &addons, bool enabled = true, bool allowRepos = false); + void AddToUpdateableAddons(AddonPtr &pAddon); + void RemoveFromUpdateableAddons(AddonPtr &pAddon); + bool ReloadSettings(const std::string &id); + /*! \brief Get all addons with available updates + \param addons List to fill with all outdated addons + \param getLocalVersion Whether to get the local addon version or the addon verion from the repository + \return True if there are outdated addons otherwise false + */ + bool GetAllOutdatedAddons(VECADDONS &addons, bool getLocalVersion = false); + /*! \brief Checks if there is any addon with available updates + \return True if there are outdated addons otherwise false + */ + bool HasOutdatedAddons(); + std::string GetString(const std::string &id, const int number); + + std::string GetTranslatedString(const cp_cfg_element_t *root, const char *tag); + static AddonPtr AddonFromProps(AddonProps& props); + void FindAddons(); + void RemoveAddon(const std::string& ID); + + /* \brief Disable an addon + Triggers the database routine and saves the current addon state to cache. + \param ID id of the addon + \param disable whether to enable or disable. Defaults to true (disable) + \sa IsAddonDisabled, + */ + bool DisableAddon(const std::string& ID, bool disable = true); + + /* \brief Check whether an addon has been disabled via DisableAddon. + In case the disabled cache does not know about the current state the database routine will be used. + \param ID id of the addon + \sa DisableAddon + */ + bool IsAddonDisabled(const std::string& ID); + + /* \brief Checks whether an addon can be disabled via DisableAddon. + \param ID id of the addon + \sa DisableAddon + */ + bool CanAddonBeDisabled(const std::string& ID); + + /* \brief Checks whether an addon is installed. + \param ID id of the addon + */ + bool IsAddonInstalled(const std::string& ID); + + /* \brief Checks whether an addon is installed. + \param ID id of the addon + \param addon Installed addon + */ + bool IsAddonInstalled(const std::string& ID, AddonPtr& addon); + + /* \brief Checks whether an addon can be installed. Broken addons can't be installed. + \param ID id of the addon + */ + bool CanAddonBeInstalled(const std::string& ID); + + /* \brief Checks whether an addon can be installed. Broken addons can't be installed. + \param addon addon to be checked + */ + bool CanAddonBeInstalled(const AddonPtr& addon); + + /* libcpluff */ + std::string GetExtValue(cp_cfg_element_t *base, const char *path); + + /*! \brief Retrieve a vector of repeated elements from a given configuration element + \param base the base configuration element. + \param path the path to the configuration element from the base element. + \param result [out] returned list of elements. + \return true if the configuration element is present and the list of elements is non-empty + */ + bool GetExtElements(cp_cfg_element_t *base, const char *path, ELEMENTS &result); + + /*! \brief Retrieve a list of strings from a given configuration element + Assumes the configuration element or attribute contains a whitespace separated list of values (eg xs:list schema). + \param base the base configuration element. + \param path the path to the configuration element or attribute from the base element. + \param result [out] returned list of strings. + \return true if the configuration element is present and the list of strings is non-empty + */ + bool GetExtList(cp_cfg_element_t *base, const char *path, std::vector &result) const; + + const cp_extension_t *GetExtension(const cp_plugin_info_t *props, const char *extension) const; + + /*! \brief Load the addon in the given path + This loads the addon using c-pluff which parses the addon descriptor file. + \param path folder that contains the addon. + \param addon [out] returned addon. + \return true if addon is set, false otherwise. + */ + bool LoadAddonDescription(const std::string &path, AddonPtr &addon); + + /*! \brief Load the addon in the given in-memory xml + This loads the addon using c-pluff which parses the addon descriptor file. + \param root Root element of an XML document. + \param addon [out] returned addon. + \return true if addon is set, false otherwise. + */ + bool LoadAddonDescriptionFromMemory(const TiXmlElement *root, AddonPtr &addon); + + /*! \brief Parse a repository XML file for addons and load their descriptors + A repository XML is essentially a concatenated list of addon descriptors. + \param root Root element of an XML document. + \param addons [out] returned list of addons. + \return true if the repository XML file is parsed, false otherwise. + */ + bool AddonsFromRepoXML(const TiXmlElement *root, VECADDONS &addons); + + /*! \brief Start all services addons. + \return True is all addons are started, false otherwise + */ + bool StartServices(const bool beforelogin); + /*! \brief Stop all services addons. + */ + void StopServices(const bool onlylogin); + + private: + void LoadAddons(const std::string &path, + std::map& unresolved); + + /* libcpluff */ + const cp_cfg_element_t *GetExtElement(cp_cfg_element_t *base, const char *path); + cp_context_t *m_cp_context; + DllLibCPluff *m_cpluff; + VECADDONS m_updateableAddons; + + /*! \brief Fetch a (single) addon from a plugin descriptor. + Assumes that there is a single (non-trivial) extension point per addon. + \param info the plugin descriptor + \param type the extension point we want + \return an AddonPtr based on the descriptor. May be NULL if no suitable extension point is found. + */ + AddonPtr GetAddonFromDescriptor(const cp_plugin_info_t *info, + const std::string& type=""); + + /*! \brief Check whether this addon is supported on the current platform + \param info the plugin descriptor + \return true if the addon is supported, false otherwise. + */ + bool PlatformSupportsAddon(const cp_plugin_info_t *info) const; + + AddonPtr Factory(const cp_extension_t *props); + bool CheckUserDirs(const cp_cfg_element_t *element); + + // private construction, and no assignements; use the provided singleton methods + CAddonMgr(); + CAddonMgr(const CAddonMgr&); + CAddonMgr const& operator=(CAddonMgr const&); + virtual ~CAddonMgr(); + + std::map m_disabled; + static std::map m_managers; + CCriticalSection m_critSection; + CAddonDatabase m_database; + }; + +}; /* namespace ADDON */ diff --git a/xbmc/addons/AddonStatusHandler.cpp b/xbmc/addons/AddonStatusHandler.cpp new file mode 100644 index 0000000..7bb874a --- /dev/null +++ b/xbmc/addons/AddonStatusHandler.cpp @@ -0,0 +1,175 @@ +/* + * Copyright (C) 2005-2013 Team XBMC + * http://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, see + * . + * + */ +#include "AddonStatusHandler.h" +#include "AddonManager.h" +#include "threads/SingleLock.h" +#include "ApplicationMessenger.h" +#include "guilib/GUIWindowManager.h" +#include "GUIDialogAddonSettings.h" +#include "dialogs/GUIDialogYesNo.h" +#include "dialogs/GUIDialogOK.h" +#include "dialogs/GUIDialogKaiToast.h" +#include "settings/Settings.h" +#include "utils/log.h" +#include "utils/StringUtils.h" + +namespace ADDON +{ + +/********************************************************** + * CAddonStatusHandler - AddOn Status Report Class + * + * Used to informate the user about occurred errors and + * changes inside Add-on's, and ask him what to do. + * + */ + +CCriticalSection CAddonStatusHandler::m_critSection; + +CAddonStatusHandler::CAddonStatusHandler(const std::string &addonID, ADDON_STATUS status, std::string message, bool sameThread) + : CThread(("AddonStatus " + addonID).c_str()) +{ + if (!CAddonMgr::Get().GetAddon(addonID, m_addon)) + return; + + CLog::Log(LOGINFO, "Called Add-on status handler for '%u' of clientName:%s, clientID:%s (same Thread=%s)", status, m_addon->Name().c_str(), m_addon->ID().c_str(), sameThread ? "yes" : "no"); + + m_status = status; + m_message = message; + + if (sameThread) + { + Process(); + } + else + { + Create(true, THREAD_MINSTACKSIZE); + } +} + +CAddonStatusHandler::~CAddonStatusHandler() +{ + StopThread(); +} + +void CAddonStatusHandler::OnStartup() +{ + SetPriority(GetMinPriority()); +} + +void CAddonStatusHandler::OnExit() +{ +} + +void CAddonStatusHandler::Process() +{ + CSingleLock lock(m_critSection); + + std::string heading = StringUtils::Format("%s: %s", TranslateType(m_addon->Type(), true).c_str(), m_addon->Name().c_str()); + + /* AddOn lost connection to his backend (for ones that use Network) */ + if (m_status == ADDON_STATUS_LOST_CONNECTION) + { + if (m_addon->Type() == ADDON_PVRDLL) + { + if (!CSettings::Get().GetBool("pvrmanager.hideconnectionlostwarning")) + CGUIDialogKaiToast::QueueNotification(CGUIDialogKaiToast::Info, m_addon->Name().c_str(), g_localizeStrings.Get(36030)); // connection lost + // TODO handle disconnects after the add-on's been initialised + } + else + { + CGUIDialogYesNo* pDialog = (CGUIDialogYesNo*)g_windowManager.GetWindow(WINDOW_DIALOG_YES_NO); + if (!pDialog) return; + + pDialog->SetHeading(heading); + pDialog->SetLine(1, 24070); + pDialog->SetLine(2, 24073); + + //send message and wait for user input + ThreadMessage tMsg = {TMSG_DIALOG_DOMODAL, WINDOW_DIALOG_YES_NO, g_windowManager.GetActiveWindow()}; + CApplicationMessenger::Get().SendMessage(tMsg, true); + + if (pDialog->IsConfirmed()) + CAddonMgr::Get().GetCallbackForType(m_addon->Type())->RequestRestart(m_addon, false); + } + } + /* Request to restart the AddOn and data structures need updated */ + else if (m_status == ADDON_STATUS_NEED_RESTART) + { + CGUIDialogOK* pDialog = (CGUIDialogOK*)g_windowManager.GetWindow(WINDOW_DIALOG_OK); + if (!pDialog) return; + + pDialog->SetHeading(heading); + pDialog->SetLine(1, 24074); + + //send message and wait for user input + ThreadMessage tMsg = {TMSG_DIALOG_DOMODAL, WINDOW_DIALOG_OK, g_windowManager.GetActiveWindow()}; + CApplicationMessenger::Get().SendMessage(tMsg, true); + + CAddonMgr::Get().GetCallbackForType(m_addon->Type())->RequestRestart(m_addon, true); + } + /* Some required settings are missing/invalid */ + else if ((m_status == ADDON_STATUS_NEED_SETTINGS) || (m_status == ADDON_STATUS_NEED_SAVEDSETTINGS)) + { + CGUIDialogYesNo* pDialogYesNo = (CGUIDialogYesNo*)g_windowManager.GetWindow(WINDOW_DIALOG_YES_NO); + if (!pDialogYesNo) return; + + pDialogYesNo->SetHeading(heading); + pDialogYesNo->SetLine(1, 24070); + pDialogYesNo->SetLine(2, 24072); + pDialogYesNo->SetLine(3, m_message); + + //send message and wait for user input + ThreadMessage tMsg = {TMSG_DIALOG_DOMODAL, WINDOW_DIALOG_YES_NO, g_windowManager.GetActiveWindow()}; + CApplicationMessenger::Get().SendMessage(tMsg, true); + + if (!pDialogYesNo->IsConfirmed()) return; + + if (!m_addon->HasSettings()) + return; + + if (CGUIDialogAddonSettings::ShowAndGetInput(m_addon)) + { + //todo doesn't dialogaddonsettings save these automatically? should do + m_addon->SaveSettings(); + CAddonMgr::Get().GetCallbackForType(m_addon->Type())->RequestRestart(m_addon, true); + } + } + /* A unknown event has occurred */ + else if (m_status == ADDON_STATUS_UNKNOWN) + { + //CAddonMgr::Get().DisableAddon(m_addon->ID()); + CGUIDialogOK* pDialog = (CGUIDialogOK*)g_windowManager.GetWindow(WINDOW_DIALOG_OK); + if (!pDialog) return; + + pDialog->SetHeading(heading); + pDialog->SetLine(1, 24070); + pDialog->SetLine(2, 24071); + pDialog->SetLine(3, m_message); + + //send message and wait for user input + ThreadMessage tMsg = {TMSG_DIALOG_DOMODAL, WINDOW_DIALOG_OK, g_windowManager.GetActiveWindow()}; + CApplicationMessenger::Get().SendMessage(tMsg, true); + } +} + + +} /*namespace ADDON*/ + diff --git a/xbmc/addons/AddonStatusHandler.h b/xbmc/addons/AddonStatusHandler.h new file mode 100644 index 0000000..c9b65bd --- /dev/null +++ b/xbmc/addons/AddonStatusHandler.h @@ -0,0 +1,56 @@ +#pragma once +/* + * Copyright (C) 2005-2013 Team XBMC + * http://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, see + * . + * + */ + +#include "threads/Thread.h" +#include "IAddon.h" +#include "include/xbmc_addon_types.h" +#include "threads/CriticalSection.h" +#include + +namespace ADDON +{ + /** + * Class - CAddonStatusHandler + * Used to informate the user about occurred errors and + * changes inside Add-on's, and ask him what to do. + * It can executed in the same thread as the calling + * function or in a seperate thread. + */ + class CAddonStatusHandler : private CThread + { + public: + CAddonStatusHandler(const std::string &addonID, ADDON_STATUS status, std::string message, bool sameThread = true); + ~CAddonStatusHandler(); + + /* Thread handling */ + virtual void Process(); + virtual void OnStartup(); + virtual void OnExit(); + + private: + static CCriticalSection m_critSection; + AddonPtr m_addon; + ADDON_STATUS m_status; + std::string m_message; + }; + + +} diff --git a/xbmc/addons/AddonVersion.cpp b/xbmc/addons/AddonVersion.cpp new file mode 100644 index 0000000..9d5a112 --- /dev/null +++ b/xbmc/addons/AddonVersion.cpp @@ -0,0 +1,138 @@ +/* + * Copyright (C) 2005-2013 Team XBMC + * http://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, see + * . + * + */ + +#include +#include +#include +#include + +#include "AddonVersion.h" +#include "utils/StringUtils.h" + +namespace ADDON +{ + AddonVersion::AddonVersion(const std::string& version) + : mEpoch(0), mUpstream(version.empty() ? "0.0.0" : version) + { + size_t pos = mUpstream.find(':'); + if (pos != std::string::npos) + { + mEpoch = strtol(mUpstream.c_str(), NULL, 10); + mUpstream.erase(0, pos+1); + } + + pos = mUpstream.find('-'); + if (pos != std::string::npos) + { + mRevision = mUpstream.substr(pos+1); + mUpstream.erase(pos); + } + } + + /**Compare two components of a Debian-style version. Return -1, 0, or 1 + * if a is less than, equal to, or greater than b, respectively. + */ + int AddonVersion::CompareComponent(const char *a, const char *b) + { + while (*a && *b) + { + while (*a && *b && !isdigit(*a) && !isdigit(*b)) + { + if (*a != *b) + { + if (*a == '~') return -1; + if (*b == '~') return 1; + return *a < *b ? -1 : 1; + } + a++; + b++; + } + if (*a && *b && (!isdigit(*a) || !isdigit(*b))) + { + if (*a == '~') return -1; + if (*b == '~') return 1; + return isdigit(*a) ? -1 : 1; + } + + char *next_a, *next_b; + long int num_a = strtol(a, &next_a, 10); + long int num_b = strtol(b, &next_b, 10); + if (num_a != num_b) + return num_a < num_b ? -1 : 1; + + a = next_a; + b = next_b; + } + if (!*a && !*b) + return 0; + if (*a) + return *a == '~' ? -1 : 1; + else + return *b == '~' ? 1 : -1; + } + + bool AddonVersion::operator<(const AddonVersion& other) const + { + if (mEpoch != other.mEpoch) + return mEpoch < other.mEpoch; + + int result = CompareComponent(mUpstream.c_str(), other.mUpstream.c_str()); + if (result) + return (result < 0); + + return (CompareComponent(mRevision.c_str(), other.mRevision.c_str()) < 0); + } + + bool AddonVersion::operator==(const AddonVersion& other) const + { + return mEpoch == other.mEpoch + && CompareComponent(mUpstream.c_str(), other.mUpstream.c_str()) == 0 + && CompareComponent(mRevision.c_str(), other.mRevision.c_str()) == 0; + } + + bool AddonVersion::empty() const + { + return mEpoch == 0 && mUpstream == "0.0.0" && mRevision.empty(); + } + + std::string AddonVersion::asString() const + { + std::string out; + if (mEpoch) + out = StringUtils::Format("%i:", mEpoch); + out += mUpstream; + if (!mRevision.empty()) + out += "-" + mRevision; + return out; + } + + bool AddonVersion::SplitFileName(std::string& ID, std::string& version, + const std::string& filename) + { + size_t dpos = filename.rfind("-"); + if (dpos == std::string::npos) + return false; + ID = filename.substr(0, dpos); + version = filename.substr(dpos + 1); + version = version.substr(0, version.size() - 4); + + return true; + } +} diff --git a/xbmc/addons/AddonVersion.h b/xbmc/addons/AddonVersion.h new file mode 100644 index 0000000..3c23370 --- /dev/null +++ b/xbmc/addons/AddonVersion.h @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2005-2013 Team XBMC + * http://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, see + * . + * + */ + +#include +#include + +namespace ADDON +{ + /* \brief Addon versioning using the debian versioning scheme + + AddonVersion uses debian versioning, which means in the each section of the period + separated version string, numbers are compared numerically rather than lexicographically, + thus any preceding zeros are ignored. + + i.e. 1.00 is considered the same as 1.0, and 1.01 is considered the same as 1.1. + + Further, 1.0 < 1.0.0 + + See here for more info: http://www.debian.org/doc/debian-policy/ch-controlfields.html#s-f-Version + */ + class AddonVersion : public boost::totally_ordered { + public: + AddonVersion(const AddonVersion& other) { *this = other; } + explicit AddonVersion(const std::string& version); + virtual ~AddonVersion() {}; + + int Epoch() const { return mEpoch; } + const std::string &Upstream() const { return mUpstream; } + const std::string &Revision() const { return mRevision; } + + AddonVersion& operator=(const AddonVersion& other); + bool operator<(const AddonVersion& other) const; + bool operator==(const AddonVersion& other) const; + std::string asString() const; + bool empty() const; + + static bool SplitFileName(std::string& ID, std::string& version, + const std::string& filename); + + protected: + int mEpoch; + std::string mUpstream; + std::string mRevision; + + static int CompareComponent(const char *a, const char *b); + }; + + inline AddonVersion& AddonVersion::operator=(const AddonVersion& other) + { + mEpoch = other.mEpoch; + mUpstream = other.mUpstream; + mRevision = other.mRevision; + return *this; + } +} diff --git a/xbmc/addons/AudioEncoder.cpp b/xbmc/addons/AudioEncoder.cpp new file mode 100644 index 0000000..2737bba --- /dev/null +++ b/xbmc/addons/AudioEncoder.cpp @@ -0,0 +1,90 @@ +/* + * Copyright (C) 2013 Arne Morten Kvarving + * + * 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, see + * . + * + */ +#include "AudioEncoder.h" + +namespace ADDON +{ + +CAudioEncoder::CAudioEncoder(const cp_extension_t* ext) + : AudioEncoderDll(ext), + extension(CAddonMgr::Get().GetExtValue(ext->configuration, "@extension")), + m_context(NULL) +{ +} + +AddonPtr CAudioEncoder::Clone() const +{ + // Copy constructor is generated by compiler and calls parent copy constructor + return AddonPtr(new CAudioEncoder(*this)); +} + +bool CAudioEncoder::Init(audioenc_callbacks &callbacks) +{ + if (!Initialized()) + return false; + + // create encoder instance + m_context = m_pStruct->Create(&callbacks); + if (!m_context) + return false; + + return m_pStruct->Start(m_context, + m_iInChannels, + m_iInSampleRate, + m_iInBitsPerSample, + m_strTitle.c_str(), + m_strArtist.c_str(), + m_strAlbumArtist.c_str(), + m_strAlbum.c_str(), + m_strYear.c_str(), + m_strTrack.c_str(), + m_strGenre.c_str(), + m_strComment.c_str(), + m_iTrackLength); +} + +int CAudioEncoder::Encode(int nNumBytesRead, uint8_t* pbtStream) +{ + if (!Initialized() || !m_context) + return 0; + + return m_pStruct->Encode(m_context, nNumBytesRead, pbtStream); +} + +bool CAudioEncoder::Close() +{ + if (!Initialized() || !m_context) + return false; + + if (!m_pStruct->Finish(m_context)) + return false; + + m_pStruct->Free(m_context); + m_context = NULL; + + return true; +} + +void CAudioEncoder::Destroy() +{ + AudioEncoderDll::Destroy(); +} + +} /*namespace ADDON*/ + diff --git a/xbmc/addons/AudioEncoder.h b/xbmc/addons/AudioEncoder.h new file mode 100644 index 0000000..3900333 --- /dev/null +++ b/xbmc/addons/AudioEncoder.h @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2013 Arne Morten Kvarving + * + * 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, see + * . + * + */ +#pragma once + +#include "AddonDll.h" +#include "include/xbmc_audioenc_types.h" +#include "cdrip/IEncoder.h" + +typedef DllAddon DllAudioEncoder; +namespace ADDON +{ + typedef CAddonDll AudioEncoderDll; + + class CAudioEncoder : public AudioEncoderDll, public IEncoder + { + public: + CAudioEncoder(const AddonProps &props) : AudioEncoderDll(props) {}; + CAudioEncoder(const cp_extension_t *ext); + virtual ~CAudioEncoder() {} + virtual AddonPtr Clone() const; + + // Things that MUST be supplied by the child classes + bool Init(audioenc_callbacks &callbacks); + int Encode(int nNumBytesRead, uint8_t* pbtStream); + bool Close(); + void Destroy(); + + const std::string extension; + + private: + void *m_context; ///< audio encoder context + }; + +} /*namespace ADDON*/ diff --git a/xbmc/addons/ContextItemAddon.cpp b/xbmc/addons/ContextItemAddon.cpp new file mode 100644 index 0000000..4222936 --- /dev/null +++ b/xbmc/addons/ContextItemAddon.cpp @@ -0,0 +1,108 @@ +/* + * Copyright (C) 2013-2015 Team XBMC + * http://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, see + * . + * + */ + +#include "ContextItemAddon.h" +#include "AddonManager.h" +#include "ContextMenuManager.h" +#include "dialogs/GUIDialogContextMenu.h" +#include "GUIInfoManager.h" +#include "interfaces/info/InfoBool.h" +#include "utils/log.h" +#include "utils/StringUtils.h" +#include "video/dialogs/GUIDialogVideoInfo.h" +#include + +using namespace std; + +namespace ADDON +{ + +CContextItemAddon::CContextItemAddon(const AddonProps &props) + : CAddon(props) +{ } + +CContextItemAddon::~CContextItemAddon() +{ } + +CContextItemAddon::CContextItemAddon(const cp_extension_t *ext) + : CAddon(ext) +{ + ELEMENTS items; + if (CAddonMgr::Get().GetExtElements(ext->configuration, "item", items)) + { + cp_cfg_element_t *item = items[0]; + + m_label = CAddonMgr::Get().GetExtValue(item, "label"); + if (StringUtils::IsNaturalNumber(m_label)) + { + m_label = GetString(boost::lexical_cast(m_label.c_str())); + ClearStrings(); + } + + m_parent = CAddonMgr::Get().GetExtValue(item, "parent"); + + string visible = CAddonMgr::Get().GetExtValue(item, "visible"); + if (visible.empty()) + visible = "false"; + + m_visCondition = g_infoManager.Register(visible, 0); + } +} + +bool CContextItemAddon::OnPreInstall() +{ + return CContextMenuManager::Get().Unregister(std::dynamic_pointer_cast(shared_from_this())); +} + +void CContextItemAddon::OnPostInstall(bool restart, bool update) +{ + if (restart) + { + // need to grab the local addon so we have the correct library path to run + AddonPtr localAddon; + if (CAddonMgr::Get().GetAddon(ID(), localAddon, ADDON_CONTEXT_ITEM)) + { + ContextItemAddonPtr contextItem = std::dynamic_pointer_cast(localAddon); + if (contextItem) + CContextMenuManager::Get().Register(contextItem); + } + } +} + +void CContextItemAddon::OnPreUnInstall() +{ + CContextMenuManager::Get().Unregister(std::dynamic_pointer_cast(shared_from_this())); +} + +void CContextItemAddon::OnDisabled() +{ + CContextMenuManager::Get().Unregister(std::dynamic_pointer_cast(shared_from_this())); +} +void CContextItemAddon::OnEnabled() +{ + CContextMenuManager::Get().Register(std::dynamic_pointer_cast(shared_from_this())); +} + +bool CContextItemAddon::IsVisible(const CFileItemPtr& item) const +{ + return item && m_visCondition->Get(item.get()); +} + +} diff --git a/xbmc/addons/ContextItemAddon.h b/xbmc/addons/ContextItemAddon.h new file mode 100644 index 0000000..628fdcd --- /dev/null +++ b/xbmc/addons/ContextItemAddon.h @@ -0,0 +1,72 @@ +#pragma once +/* + * Copyright (C) 2013-2015 Team XBMC + * http://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, see + * . + * + */ + +#include +#include +#include "Addon.h" + +class CFileItem; +typedef std::shared_ptr CFileItemPtr; + +namespace INFO +{ + class InfoBool; + typedef std::shared_ptr InfoPtr; +} + +namespace ADDON +{ + class CContextItemAddon : public CAddon + { + public: + CContextItemAddon(const cp_extension_t *ext); + CContextItemAddon(const AddonProps &props); + virtual ~CContextItemAddon(); + + const std::string& GetLabel() const { return m_label; } + + /*! + * \brief Get the parent category of this context item. + * + * \details Returns empty string if at root level or + * CONTEXT_MENU_GROUP_MANAGE when it should be in the 'manage' submenu. + */ + const std::string& GetParent() const { return m_parent; } + + /*! + * \brief Returns true if this contex menu should be visible for given item. + */ + bool IsVisible(const CFileItemPtr& item) const; + + virtual bool OnPreInstall(); + virtual void OnPostInstall(bool restart, bool update); + virtual void OnPreUnInstall(); + virtual void OnDisabled(); + virtual void OnEnabled(); + + private: + std::string m_label; + std::string m_parent; + INFO::InfoPtr m_visCondition; + }; + + typedef std::shared_ptr ContextItemAddonPtr; +} diff --git a/xbmc/addons/DllAddon.h b/xbmc/addons/DllAddon.h new file mode 100644 index 0000000..3ea4e8d --- /dev/null +++ b/xbmc/addons/DllAddon.h @@ -0,0 +1,70 @@ +#pragma once +/* +* Copyright (C) 2005-2013 Team XBMC +* http://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, see +* . +* +*/ + +#include "DynamicDll.h" +#include "addons/include/xbmc_addon_cpp_dll.h" + +template +class DllAddonInterface +{ +public: + virtual ~DllAddonInterface() {} + virtual void GetAddon(TheStruct* pAddon) =0; + virtual ADDON_STATUS Create(void *cb, Props *info) =0; + virtual void Stop() =0; + virtual void Destroy() =0; + virtual ADDON_STATUS GetStatus() =0; + virtual bool HasSettings() =0; + virtual unsigned int GetSettings(ADDON_StructSetting*** sSet)=0; + virtual void FreeSettings()=0; + virtual ADDON_STATUS SetSetting(const char *settingName, const void *settingValue) =0; + virtual void Announce(const char *flag, const char *sender, const char *message, const void *data) =0; +}; + +template +class DllAddon : public DllDynamic, public DllAddonInterface +{ +public: + DECLARE_DLL_WRAPPER_TEMPLATE(DllAddon) + DEFINE_METHOD2(ADDON_STATUS, Create, (void* p1, Props* p2)) + DEFINE_METHOD0(void, Stop) + DEFINE_METHOD0(void, Destroy) + DEFINE_METHOD0(ADDON_STATUS, GetStatus) + DEFINE_METHOD0(bool, HasSettings) + DEFINE_METHOD1(unsigned int, GetSettings, (ADDON_StructSetting ***p1)) + DEFINE_METHOD0(void, FreeSettings) + DEFINE_METHOD2(ADDON_STATUS, SetSetting, (const char *p1, const void *p2)) + DEFINE_METHOD1(void, GetAddon, (TheStruct* p1)) + DEFINE_METHOD4(void, Announce, (const char *p1, const char *p2, const char *p3, const void *p4)) + BEGIN_METHOD_RESOLVE() + RESOLVE_METHOD_RENAME(get_addon,GetAddon) + RESOLVE_METHOD_RENAME(ADDON_Create, Create) + RESOLVE_METHOD_RENAME(ADDON_Stop, Stop) + RESOLVE_METHOD_RENAME(ADDON_Destroy, Destroy) + RESOLVE_METHOD_RENAME(ADDON_GetStatus, GetStatus) + RESOLVE_METHOD_RENAME(ADDON_HasSettings, HasSettings) + RESOLVE_METHOD_RENAME(ADDON_SetSetting, SetSetting) + RESOLVE_METHOD_RENAME(ADDON_GetSettings, GetSettings) + RESOLVE_METHOD_RENAME(ADDON_FreeSettings, FreeSettings) + RESOLVE_METHOD_RENAME(ADDON_Announce, Announce) + END_METHOD_RESOLVE() +}; + diff --git a/xbmc/addons/DllLibCPluff.h b/xbmc/addons/DllLibCPluff.h new file mode 100644 index 0000000..6e3f03c --- /dev/null +++ b/xbmc/addons/DllLibCPluff.h @@ -0,0 +1,116 @@ +#pragma once +/* + * Copyright (C) 2005-2013 Team XBMC + * http://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, see + * . + * + */ + +#include "DynamicDll.h" + +extern "C" { +#include "lib/cpluff/libcpluff/cpluff.h" +} + +class DllLibCPluffInterface +{ +public: + virtual ~DllLibCPluffInterface() {} + virtual const char *get_version(void) =0; + virtual void set_fatal_error_handler(cp_fatal_error_func_t error_handler) =0; + virtual cp_status_t init(void) =0; + virtual void destroy(void) =0; + virtual cp_context_t * create_context(cp_status_t *status) =0; + virtual void destroy_context(cp_context_t *ctx) =0; + virtual cp_status_t register_pcollection(cp_context_t *ctx, const char *dir) =0; + virtual void unregister_pcollection(cp_context_t *ctx, const char *dir) =0; + virtual void unregister_pcollections(cp_context_t *ctx) =0; + virtual cp_status_t register_logger(cp_context_t *ctx, cp_logger_func_t logger, void *user_data, cp_log_severity_t min_severity) =0; + virtual void unregister_logger(cp_context_t *ctx, cp_logger_func_t logger) =0; + virtual cp_status_t scan_plugins(cp_context_t *ctx, int flags) =0; + virtual cp_plugin_info_t * get_plugin_info(cp_context_t *ctx, const char *id, cp_status_t *status) =0; + virtual cp_plugin_info_t ** get_plugins_info(cp_context_t *ctx, cp_status_t *status, int *num) =0; + virtual cp_extension_t ** get_extensions_info(cp_context_t *ctx, const char *extpt_id, cp_status_t *status, int *num) =0; + virtual void release_info(cp_context_t *ctx, void *info) =0; + virtual cp_cfg_element_t * lookup_cfg_element(cp_cfg_element_t *base, const char *path) =0; + virtual char * lookup_cfg_value(cp_cfg_element_t *base, const char *path) =0; + virtual cp_status_t define_symbol(cp_context_t *ctx, const char *name, void *ptr) =0; + virtual void *resolve_symbol(cp_context_t *ctx, const char *id, const char *name, cp_status_t *status) =0; + virtual void release_symbol(cp_context_t *ctx, const void *ptr) =0; + virtual cp_plugin_info_t *load_plugin_descriptor(cp_context_t *ctx, const char *path, cp_status_t *status) =0; + virtual cp_plugin_info_t *load_plugin_descriptor_from_memory(cp_context_t *ctx, const char *buffer, unsigned int buffer_len, cp_status_t *status) =0; + virtual cp_status_t uninstall_plugin(cp_context_t *ctx, const char *id)=0; +}; + +class DllLibCPluff : public DllDynamic, DllLibCPluffInterface +{ + DECLARE_DLL_WRAPPER(DllLibCPluff, DLL_PATH_CPLUFF) + DEFINE_METHOD0(const char*, get_version) + DEFINE_METHOD1(void, set_fatal_error_handler, (cp_fatal_error_func_t p1)) + DEFINE_METHOD0(cp_status_t, init) + DEFINE_METHOD0(void, destroy) + DEFINE_METHOD1(cp_context_t*, create_context, (cp_status_t *p1)) + DEFINE_METHOD1(void, destroy_context, (cp_context_t *p1)) + + DEFINE_METHOD2(cp_status_t, register_pcollection, (cp_context_t *p1, const char *p2)) + DEFINE_METHOD2(void, unregister_pcollection, (cp_context_t *p1, const char *p2)) + DEFINE_METHOD1(void, unregister_pcollections, (cp_context_t *p1)) + + DEFINE_METHOD4(cp_status_t, register_logger, (cp_context_t *p1, cp_logger_func_t p2, void *p3, cp_log_severity_t p4)) + DEFINE_METHOD2(void, unregister_logger, (cp_context_t *p1, cp_logger_func_t p2)) + DEFINE_METHOD2(cp_status_t, scan_plugins, (cp_context_t *p1, int p2)) + DEFINE_METHOD3(cp_plugin_info_t*, get_plugin_info, (cp_context_t *p1, const char *p2, cp_status_t *p3)) + DEFINE_METHOD3(cp_plugin_info_t**, get_plugins_info, (cp_context_t *p1, cp_status_t *p2, int *p3)) + DEFINE_METHOD4(cp_extension_t**, get_extensions_info, (cp_context_t *p1, const char *p2, cp_status_t *p3, int *p4)) + DEFINE_METHOD2(void, release_info, (cp_context_t *p1, void *p2)) + + DEFINE_METHOD2(cp_cfg_element_t*, lookup_cfg_element, (cp_cfg_element_t *p1, const char *p2)) + DEFINE_METHOD2(char*, lookup_cfg_value, (cp_cfg_element_t *p1, const char *p2)) + + DEFINE_METHOD3(cp_status_t, define_symbol, (cp_context_t *p1, const char *p2, void *p3)) + DEFINE_METHOD4(void*, resolve_symbol, (cp_context_t *p1, const char *p2, const char *p3, cp_status_t *p4)) + DEFINE_METHOD2(void, release_symbol, (cp_context_t *p1, const void *p2)) + DEFINE_METHOD3(cp_plugin_info_t*, load_plugin_descriptor, (cp_context_t *p1, const char *p2, cp_status_t *p3)) + DEFINE_METHOD4(cp_plugin_info_t*, load_plugin_descriptor_from_memory, (cp_context_t *p1, const char *p2, unsigned int p3, cp_status_t *p4)) + DEFINE_METHOD2(cp_status_t, uninstall_plugin, (cp_context_t *p1, const char *p2)) + + BEGIN_METHOD_RESOLVE() + RESOLVE_METHOD_RENAME(cp_get_version, get_version) + RESOLVE_METHOD_RENAME(cp_set_fatal_error_handler, set_fatal_error_handler) + RESOLVE_METHOD_RENAME(cp_init, init) + RESOLVE_METHOD_RENAME(cp_destroy, destroy) + RESOLVE_METHOD_RENAME(cp_create_context, create_context) + RESOLVE_METHOD_RENAME(cp_destroy_context, destroy_context) + RESOLVE_METHOD_RENAME(cp_register_pcollection, register_pcollection) + RESOLVE_METHOD_RENAME(cp_unregister_pcollection, unregister_pcollection) + RESOLVE_METHOD_RENAME(cp_unregister_pcollections, unregister_pcollections) + RESOLVE_METHOD_RENAME(cp_register_logger, register_logger) + RESOLVE_METHOD_RENAME(cp_unregister_logger, unregister_logger) + RESOLVE_METHOD_RENAME(cp_scan_plugins, scan_plugins) + RESOLVE_METHOD_RENAME(cp_get_plugin_info, get_plugin_info) + RESOLVE_METHOD_RENAME(cp_get_plugins_info, get_plugins_info) + RESOLVE_METHOD_RENAME(cp_get_extensions_info, get_extensions_info) + RESOLVE_METHOD_RENAME(cp_release_info, release_info) + RESOLVE_METHOD_RENAME(cp_lookup_cfg_element, lookup_cfg_element) + RESOLVE_METHOD_RENAME(cp_lookup_cfg_value, lookup_cfg_value) + RESOLVE_METHOD_RENAME(cp_define_symbol, define_symbol) + RESOLVE_METHOD_RENAME(cp_resolve_symbol, resolve_symbol) + RESOLVE_METHOD_RENAME(cp_release_symbol, release_symbol) + RESOLVE_METHOD_RENAME(cp_load_plugin_descriptor, load_plugin_descriptor) + RESOLVE_METHOD_RENAME(cp_load_plugin_descriptor_from_memory, load_plugin_descriptor_from_memory) + RESOLVE_METHOD_RENAME(cp_uninstall_plugin, uninstall_plugin) + END_METHOD_RESOLVE() +}; diff --git a/xbmc/addons/DllPVRClient.h b/xbmc/addons/DllPVRClient.h new file mode 100644 index 0000000..7948858 --- /dev/null +++ b/xbmc/addons/DllPVRClient.h @@ -0,0 +1,29 @@ +#pragma once +/* + * Copyright (C) 2012-2013 Team XBMC + * http://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, see + * . + * + */ + +#include "DllAddon.h" +#include "include/xbmc_pvr_types.h" + +class DllPVRClient : public DllAddon +{ + // this is populated via Macro calls in DllAddon.h +}; + diff --git a/xbmc/addons/GUIDialogAddonInfo.cpp b/xbmc/addons/GUIDialogAddonInfo.cpp new file mode 100644 index 0000000..fe3484f --- /dev/null +++ b/xbmc/addons/GUIDialogAddonInfo.cpp @@ -0,0 +1,458 @@ +/* + * Copyright (C) 2005-2013 Team XBMC + * http://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, see + * . + * + */ + +#include "GUIDialogAddonInfo.h" +#include "dialogs/GUIDialogYesNo.h" +#include "dialogs/GUIDialogOK.h" +#include "addons/AddonManager.h" +#include "AddonDatabase.h" +#include "FileItem.h" +#include "filesystem/Directory.h" +#include "filesystem/SpecialProtocol.h" +#include "GUIDialogAddonSettings.h" +#include "dialogs/GUIDialogContextMenu.h" +#include "dialogs/GUIDialogTextViewer.h" +#include "GUIUserMessages.h" +#include "guilib/GUIWindowManager.h" +#include "input/Key.h" +#include "utils/JobManager.h" +#include "utils/FileOperationJob.h" +#include "utils/StringUtils.h" +#include "utils/URIUtils.h" +#include "utils/log.h" +#include "addons/AddonInstaller.h" +#include "pvr/PVRManager.h" +#include "Util.h" +#include "interfaces/Builtins.h" + +#define CONTROL_BTN_INSTALL 6 +#define CONTROL_BTN_ENABLE 7 +#define CONTROL_BTN_UPDATE 8 +#define CONTROL_BTN_SETTINGS 9 +#define CONTROL_BTN_CHANGELOG 10 +#define CONTROL_BTN_ROLLBACK 11 +#define CONTROL_BTN_SELECT 12 + +using namespace std; +using namespace ADDON; +using namespace XFILE; + +CGUIDialogAddonInfo::CGUIDialogAddonInfo(void) + : CGUIDialog(WINDOW_DIALOG_ADDON_INFO, "DialogAddonInfo.xml"), m_jobid(0) +{ + m_item = CFileItemPtr(new CFileItem); + m_loadType = KEEP_IN_MEMORY; +} + +CGUIDialogAddonInfo::~CGUIDialogAddonInfo(void) +{ +} + +bool CGUIDialogAddonInfo::OnMessage(CGUIMessage& message) +{ + switch ( message.GetMessage() ) + { + case GUI_MSG_WINDOW_DEINIT: + { + if (m_jobid) + CJobManager::GetInstance().CancelJob(m_jobid); + } + break; + + case GUI_MSG_CLICKED: + { + int iControl = message.GetSenderId(); + if (iControl == CONTROL_BTN_UPDATE) + { + OnUpdate(); + return true; + } + if (iControl == CONTROL_BTN_INSTALL) + { + if (!m_localAddon) + { + OnInstall(); + return true; + } + else + { + OnUninstall(); + return true; + } + } + else if (iControl == CONTROL_BTN_SELECT) + { + OnLaunch(); + return true; + } + else if (iControl == CONTROL_BTN_ENABLE) + { + OnEnable(!m_item->GetProperty("Addon.Enabled").asBoolean()); + return true; + } + else if (iControl == CONTROL_BTN_SETTINGS) + { + OnSettings(); + return true; + } + else if (iControl == CONTROL_BTN_CHANGELOG) + { + OnChangeLog(); + return true; + } + else if (iControl == CONTROL_BTN_ROLLBACK) + { + OnRollback(); + return true; + } + } + break; +default: + break; + } + + return CGUIDialog::OnMessage(message); +} + +bool CGUIDialogAddonInfo::OnAction(const CAction &action) +{ + if (action.GetID() == ACTION_SHOW_INFO) + { + Close(); + return true; + } + return CGUIDialog::OnAction(action); +} + +void CGUIDialogAddonInfo::OnInitWindow() +{ + UpdateControls(); + CGUIDialog::OnInitWindow(); + m_changelog = false; +} + +void CGUIDialogAddonInfo::UpdateControls() +{ + bool isInstalled = NULL != m_localAddon.get(); + bool isEnabled = isInstalled && m_item->GetProperty("Addon.Enabled").asBoolean(); + bool isUpdatable = isInstalled && m_item->GetProperty("Addon.UpdateAvail").asBoolean(); + bool isExecutable = isInstalled && (m_localAddon->Type() == ADDON_PLUGIN || m_localAddon->Type() == ADDON_SCRIPT); + if (isInstalled) + GrabRollbackVersions(); + + bool canDisable = isInstalled && CAddonMgr::Get().CanAddonBeDisabled(m_localAddon->ID()); + bool canInstall = !isInstalled && m_item->GetProperty("Addon.Broken").empty(); + bool isRepo = (isInstalled && m_localAddon->Type() == ADDON_REPOSITORY) || (m_addon && m_addon->Type() == ADDON_REPOSITORY); + + CONTROL_ENABLE_ON_CONDITION(CONTROL_BTN_INSTALL, canDisable || canInstall); + SET_CONTROL_LABEL(CONTROL_BTN_INSTALL, isInstalled ? 24037 : 24038); + + CONTROL_ENABLE_ON_CONDITION(CONTROL_BTN_ENABLE, canDisable); + SET_CONTROL_LABEL(CONTROL_BTN_ENABLE, isEnabled ? 24021 : 24022); + + CONTROL_ENABLE_ON_CONDITION(CONTROL_BTN_UPDATE, isUpdatable); + CONTROL_ENABLE_ON_CONDITION(CONTROL_BTN_SETTINGS, isInstalled && m_localAddon->HasSettings()); + CONTROL_ENABLE_ON_CONDITION(CONTROL_BTN_SELECT, isExecutable); + CONTROL_ENABLE_ON_CONDITION(CONTROL_BTN_CHANGELOG, !isRepo); + CONTROL_ENABLE_ON_CONDITION(CONTROL_BTN_ROLLBACK, m_rollbackVersions.size() > 1); +} + +void CGUIDialogAddonInfo::OnUpdate() +{ + std::string referer = StringUtils::Format("Referer=%s-%s.zip",m_localAddon->ID().c_str(),m_localAddon->Version().asString().c_str()); + CAddonInstaller::Get().Install(m_addon->ID(), true, referer); // force install + Close(); +} + +void CGUIDialogAddonInfo::OnInstall() +{ + CAddonInstaller::Get().Install(m_addon->ID()); + Close(); +} + +void CGUIDialogAddonInfo::OnLaunch() +{ + if (!m_localAddon) + return; + + CBuiltins::Execute("RunAddon(" + m_localAddon->ID() + ")"); + Close(); +} + +bool CGUIDialogAddonInfo::PromptIfDependency(int heading, int line2) +{ + if (!m_localAddon) + return false; + + VECADDONS addons; + vector deps; + CAddonMgr::Get().GetAllAddons(addons); + for (VECADDONS::const_iterator it = addons.begin(); + it != addons.end();++it) + { + ADDONDEPS::const_iterator i = (*it)->GetDeps().find(m_localAddon->ID()); + if (i != (*it)->GetDeps().end() && !i->second.second) // non-optional dependency + deps.push_back((*it)->Name()); + } + + if (!deps.empty()) + { + string line0 = StringUtils::Format(g_localizeStrings.Get(24046).c_str(), m_localAddon->Name().c_str()); + string line1 = StringUtils::Join(deps, ", "); + CGUIDialogOK::ShowAndGetInput(heading, line0, line1, line2); + return true; + } + return false; +} + +void CGUIDialogAddonInfo::OnUninstall() +{ + if (!m_localAddon.get()) + return; + + if (!g_passwordManager.CheckMenuLock(WINDOW_ADDON_BROWSER)) + return; + + // ensure the addon is not a dependency of other installed addons + if (PromptIfDependency(24037, 24047)) + return; + + // prompt user to be sure + if (!CGUIDialogYesNo::ShowAndGetInput(24037, 750, 0, 0)) + return; + + // ensure the addon isn't disabled in our database + CAddonMgr::Get().DisableAddon(m_localAddon->ID(), false); + + CJobManager::GetInstance().AddJob(new CAddonUnInstallJob(m_localAddon), + &CAddonInstaller::Get()); + CAddonMgr::Get().RemoveAddon(m_localAddon->ID()); + Close(); +} + +void CGUIDialogAddonInfo::OnEnable(bool enable) +{ + if (!m_localAddon.get()) + return; + + if (!g_passwordManager.CheckMenuLock(WINDOW_ADDON_BROWSER)) + return; + + if (!enable && PromptIfDependency(24075, 24091)) + return; + + CAddonMgr::Get().DisableAddon(m_localAddon->ID(), !enable); + SetItem(m_item); + UpdateControls(); + g_windowManager.SendMessage(GUI_MSG_NOTIFY_ALL, 0, 0, GUI_MSG_UPDATE); +} + +void CGUIDialogAddonInfo::OnSettings() +{ + CGUIDialogAddonSettings::ShowAndGetInput(m_localAddon); +} + +void CGUIDialogAddonInfo::OnChangeLog() +{ + CGUIDialogTextViewer* pDlgInfo = (CGUIDialogTextViewer*)g_windowManager.GetWindow(WINDOW_DIALOG_TEXT_VIEWER); + std::string name; + if (m_addon) + name = m_addon->Name(); + else if (m_localAddon) + name = m_localAddon->Name(); + pDlgInfo->SetHeading(g_localizeStrings.Get(24054)+" - "+name); + if (m_item->GetProperty("Addon.Changelog").empty()) + { + pDlgInfo->SetText(g_localizeStrings.Get(13413)); + CFileItemList items; + if (m_localAddon && + !m_item->GetProperty("Addon.UpdateAvail").asBoolean()) + { + items.Add(CFileItemPtr(new CFileItem(m_localAddon->ChangeLog(),false))); + } + else + items.Add(CFileItemPtr(new CFileItem(m_addon->ChangeLog(),false))); + items[0]->Select(true); + m_jobid = CJobManager::GetInstance().AddJob( + new CFileOperationJob(CFileOperationJob::ActionCopy,items, + "special://temp/"),this); + } + else + pDlgInfo->SetText(m_item->GetProperty("Addon.Changelog").asString()); + + m_changelog = true; + pDlgInfo->DoModal(); + m_changelog = false; +} + +void CGUIDialogAddonInfo::OnRollback() +{ + if (!g_passwordManager.CheckMenuLock(WINDOW_ADDON_BROWSER)) + return; + + CGUIDialogContextMenu* dlg = (CGUIDialogContextMenu*)g_windowManager.GetWindow(WINDOW_DIALOG_CONTEXT_MENU); + CAddonDatabase database; + database.Open(); + + CContextButtons buttons; + for (unsigned int i=0;iVersion().asString()) + label += " "+g_localizeStrings.Get(24094); + if (database.IsAddonBlacklisted(m_localAddon->ID(),label)) + label += " "+g_localizeStrings.Get(24095); + + buttons.Add(i,label); + } + int choice; + if ((choice=dlg->ShowAndGetChoice(buttons)) > -1) + { + // blacklist everything newer + for (unsigned int j=choice+1;jID(),m_rollbackVersions[j]); + std::string path = "special://home/addons/packages/"; + path += m_localAddon->ID()+"-"+m_rollbackVersions[choice]+".zip"; + // needed as cpluff won't downgrade + if (!m_localAddon->IsType(ADDON_SERVICE)) + //we will handle this for service addons in CAddonInstallJob::OnPostInstall + CAddonMgr::Get().RemoveAddon(m_localAddon->ID()); + CAddonInstaller::Get().InstallFromZip(path); + database.RemoveAddonFromBlacklist(m_localAddon->ID(),m_rollbackVersions[choice]); + Close(); + } +} + +bool CGUIDialogAddonInfo::ShowForItem(const CFileItemPtr& item) +{ + CGUIDialogAddonInfo* dialog = (CGUIDialogAddonInfo*)g_windowManager.GetWindow(WINDOW_DIALOG_ADDON_INFO); + if (!dialog) + return false; + if (!dialog->SetItem(item)) + return false; + + dialog->DoModal(); + return true; +} + +bool CGUIDialogAddonInfo::SetItem(const CFileItemPtr& item) +{ + *m_item = *item; + m_rollbackVersions.clear(); + + // grab the local addon, if it's available + m_localAddon.reset(); + m_addon.reset(); + if (CAddonMgr::Get().GetAddon(item->GetProperty("Addon.ID").asString(), m_localAddon)) // sets m_localAddon if installed regardless of enabled state + m_item->SetProperty("Addon.Enabled", "true"); + else + m_item->SetProperty("Addon.Enabled", "false"); + m_item->SetProperty("Addon.Installed", m_localAddon ? "true" : "false"); + + CAddonDatabase database; + database.Open(); + database.GetAddon(item->GetProperty("Addon.ID").asString(),m_addon); + + if (TranslateType(item->GetProperty("Addon.intType").asString()) == ADDON_REPOSITORY) + { + CAddonDatabase database; + database.Open(); + VECADDONS addons; + if (m_addon) + database.GetRepository(m_addon->ID(), addons); + else if (m_localAddon) // sanity + database.GetRepository(m_localAddon->ID(), addons); + int tot=0; + for (int i = ADDON_UNKNOWN+1;iType() == (TYPE)i) + ++num; + } + m_item->SetProperty("Repo." + TranslateType((TYPE)i), num); + tot += num; + } + m_item->SetProperty("Repo.Addons", tot); + } + return true; +} + +void CGUIDialogAddonInfo::OnJobComplete(unsigned int jobID, bool success, + CJob* job) +{ + if (!m_changelog) + return; + + CGUIDialogTextViewer* pDlgInfo = (CGUIDialogTextViewer*)g_windowManager.GetWindow(WINDOW_DIALOG_TEXT_VIEWER); + + m_jobid = 0; + if (!success) + { + pDlgInfo->SetText(g_localizeStrings.Get(195)); + } + else + { + CFile file; + XFILE::auto_buffer buf; + if (file.LoadFile("special://temp/" + + URIUtils::GetFileName(((CFileOperationJob*)job)->GetItems()[0]->GetPath()), buf) > 0) + { + std::string str(buf.get(), buf.length()); + m_item->SetProperty("Addon.Changelog", str); + pDlgInfo->SetText(str); + } + } + CGUIMessage msg(GUI_MSG_NOTIFY_ALL, WINDOW_DIALOG_TEXT_VIEWER, 0, GUI_MSG_UPDATE); + g_windowManager.SendThreadMessage(msg); +} + +void CGUIDialogAddonInfo::GrabRollbackVersions() +{ + CFileItemList items; + XFILE::CDirectory::GetDirectory("special://home/addons/packages/",items,".zip",DIR_FLAG_NO_FILE_DIRS); + items.Sort(SortByLabel, SortOrderAscending); + CAddonDatabase db; + db.Open(); + for (int i=0;im_bIsFolder) + continue; + std::string ID, version; + AddonVersion::SplitFileName(ID,version,items[i]->GetLabel()); + if (ID == m_localAddon->ID()) + { + std::string hash, path(items[i]->GetPath()); + if (db.GetPackageHash(m_localAddon->ID(), path, hash)) + { + std::string md5 = CUtil::GetFileMD5(path); + if (md5 == hash) + m_rollbackVersions.push_back(version); + else /* The package has been corrupted */ + { + CLog::Log(LOGWARNING, "%s: Removing corrupt addon package %s.", __FUNCTION__, path.c_str()); + CFile::Delete(path); + db.RemovePackage(path); + } + } + } + } +} diff --git a/xbmc/addons/GUIDialogAddonInfo.h b/xbmc/addons/GUIDialogAddonInfo.h new file mode 100644 index 0000000..53b7a27 --- /dev/null +++ b/xbmc/addons/GUIDialogAddonInfo.h @@ -0,0 +1,80 @@ +#pragma once + +/* + * Copyright (C) 2005-2013 Team XBMC + * http://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, see + * . + * + */ + +#include "guilib/GUIDialog.h" +#include "addons/IAddon.h" +#include "utils/Job.h" + +class CGUIDialogAddonInfo : + public CGUIDialog, + public IJobCallback +{ +public: + CGUIDialogAddonInfo(void); + virtual ~CGUIDialogAddonInfo(void); + virtual bool OnMessage(CGUIMessage& message); + virtual bool OnAction(const CAction &action); + + virtual CFileItemPtr GetCurrentListItem(int offset = 0) { return m_item; } + virtual bool HasListItems() const { return true; } + + static bool ShowForItem(const CFileItemPtr& item); + + // job callback + void OnJobComplete(unsigned int jobID, bool success, CJob* job); +protected: + void OnInitWindow(); + + /*! \brief Set the item to display addon info on. + \param item to display + \return true if we can display information, false otherwise + */ + bool SetItem(const CFileItemPtr &item); + void UpdateControls(); + + void OnUpdate(); + void OnInstall(); + void OnUninstall(); + void OnEnable(bool enable); + void OnSettings(); + void OnChangeLog(); + void OnRollback(); + void OnLaunch(); + + /*! \brief check if the add-on is a dependency of others, and if so prompt the user. + \param heading the label for the heading of the prompt dialog + \param line2 the action that could not be completed. + \return true if prompted, false otherwise. + */ + bool PromptIfDependency(int heading, int line2); + + CFileItemPtr m_item; + ADDON::AddonPtr m_addon; + ADDON::AddonPtr m_localAddon; + unsigned int m_jobid; + bool m_changelog; + + // rollback data + void GrabRollbackVersions(); + std::vector m_rollbackVersions; +}; + diff --git a/xbmc/addons/GUIDialogAddonSettings.cpp b/xbmc/addons/GUIDialogAddonSettings.cpp new file mode 100644 index 0000000..ef1c3c2 --- /dev/null +++ b/xbmc/addons/GUIDialogAddonSettings.cpp @@ -0,0 +1,1200 @@ +/* + * Copyright (C) 2005-2013 Team XBMC + * http://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, see + * . + * + */ + +#include "GUIDialogAddonSettings.h" +#include "filesystem/PluginDirectory.h" +#include "addons/IAddon.h" +#include "addons/AddonManager.h" +#include "dialogs/GUIDialogNumeric.h" +#include "dialogs/GUIDialogFileBrowser.h" +#include "dialogs/GUIDialogOK.h" +#include "guilib/GUIControlGroupList.h" +#include "guilib/GUISettingsSliderControl.h" +#include "utils/URIUtils.h" +#include "utils/StringUtils.h" +#include "storage/MediaManager.h" +#include "guilib/GUILabelControl.h" +#include "guilib/GUIRadioButtonControl.h" +#include "guilib/GUISpinControlEx.h" +#include "guilib/GUIImage.h" +#include "input/Key.h" +#include "filesystem/Directory.h" +#include "video/VideoInfoScanner.h" +#include "addons/Scraper.h" +#include "guilib/GUIWindowManager.h" +#include "ApplicationMessenger.h" +#include "guilib/GUIKeyboardFactory.h" +#include "FileItem.h" +#include "settings/AdvancedSettings.h" +#include "settings/MediaSourceSettings.h" +#include "GUIInfoManager.h" +#include "GUIUserMessages.h" +#include "dialogs/GUIDialogSelect.h" +#include "GUIWindowAddonBrowser.h" +#include "utils/log.h" +#include "Util.h" +#include "URL.h" +#include "utils/XMLUtils.h" + +using namespace std; +using namespace ADDON; +using XFILE::CDirectory; + +#define CONTROL_SETTINGS_AREA 2 +#define CONTROL_DEFAULT_BUTTON 3 +#define CONTROL_DEFAULT_RADIOBUTTON 4 +#define CONTROL_DEFAULT_SPIN 5 +#define CONTROL_DEFAULT_SEPARATOR 6 +#define CONTROL_DEFAULT_LABEL_SEPARATOR 7 +#define CONTROL_DEFAULT_SLIDER 8 +#define CONTROL_SECTION_AREA 9 +#define CONTROL_DEFAULT_SECTION_BUTTON 13 + +#define ID_BUTTON_OK 10 +#define ID_BUTTON_CANCEL 11 +#define ID_BUTTON_DEFAULT 12 +#define CONTROL_HEADING_LABEL 20 + +#define CONTROL_START_SECTION 100 +#define CONTROL_START_SETTING 200 + +CGUIDialogAddonSettings::CGUIDialogAddonSettings() + : CGUIDialogBoxBase(WINDOW_DIALOG_ADDON_SETTINGS, "DialogAddonSettings.xml") +{ + m_currentSection = 0; + m_totalSections = 1; + m_saveToDisk = false; +} + +CGUIDialogAddonSettings::~CGUIDialogAddonSettings(void) +{ +} + +bool CGUIDialogAddonSettings::OnMessage(CGUIMessage& message) +{ + switch (message.GetMessage()) + { + case GUI_MSG_WINDOW_DEINIT: + { + FreeSections(); + } + break; + case GUI_MSG_CLICKED: + { + int iControl = message.GetSenderId(); + bool bCloseDialog = false; + + if (iControl == ID_BUTTON_DEFAULT) + SetDefaultSettings(); + else if (iControl != ID_BUTTON_OK) + bCloseDialog = ShowVirtualKeyboard(iControl); + + if (iControl == ID_BUTTON_OK || iControl == ID_BUTTON_CANCEL || bCloseDialog) + { + if (iControl == ID_BUTTON_OK || bCloseDialog) + { + m_bConfirmed = true; + SaveSettings(); + } + Close(); + return true; + } + } + break; + case GUI_MSG_FOCUSED: + { + CGUIDialogBoxBase::OnMessage(message); + int focusedControl = GetFocusedControlID(); + if (focusedControl >= CONTROL_START_SECTION && focusedControl < (int)(CONTROL_START_SECTION + m_totalSections) && + focusedControl - CONTROL_START_SECTION != (int)m_currentSection) + { // changing section + UpdateFromControls(); + m_currentSection = focusedControl - CONTROL_START_SECTION; + CreateControls(); + } + return true; + } + case GUI_MSG_SETTING_UPDATED: + { + std::string id = message.GetStringParam(0); + std::string value = message.GetStringParam(1); + m_settings[id] = value; + if (GetFocusedControl()) + { + int iControl = GetFocusedControl()->GetID(); + CreateControls(); + CGUIMessage msg(GUI_MSG_SETFOCUS,GetID(),iControl); + OnMessage(msg); + } + return true; + } + } + return CGUIDialogBoxBase::OnMessage(message); +} + +bool CGUIDialogAddonSettings::OnAction(const CAction& action) +{ + if (action.GetID() == ACTION_DELETE_ITEM) + { + CGUIControl* pControl = GetFocusedControl(); + if (pControl) + { + int iControl = pControl->GetID(); + int controlId = CONTROL_START_SETTING; + const TiXmlElement* setting = GetFirstSetting(); + UpdateFromControls(); + while (setting) + { + if (controlId == iControl) + { + const char* id = setting->Attribute("id"); + const char* value = setting->Attribute("default"); + if (id && value) + m_settings[id] = value; + CreateControls(); + CGUIMessage msg(GUI_MSG_SETFOCUS,GetID(),iControl); + OnMessage(msg); + return true; + } + setting = setting->NextSiblingElement("setting"); + controlId++; + } + } + } + return CGUIDialogBoxBase::OnAction(action); +} + +void CGUIDialogAddonSettings::OnInitWindow() +{ + m_currentSection = 0; + m_totalSections = 1; + CreateSections(); + CreateControls(); + CGUIDialogBoxBase::OnInitWindow(); +} + +// \brief Show CGUIDialogOK dialog, then wait for user to dismiss it. +bool CGUIDialogAddonSettings::ShowAndGetInput(const AddonPtr &addon, bool saveToDisk /* = true */) +{ + if (!addon) + return false; + + if (!g_passwordManager.CheckMenuLock(WINDOW_ADDON_BROWSER)) + return false; + + bool ret(false); + if (addon->HasSettings()) + { + // Create the dialog + CGUIDialogAddonSettings* pDialog = NULL; + pDialog = (CGUIDialogAddonSettings*) g_windowManager.GetWindow(WINDOW_DIALOG_ADDON_SETTINGS); + if (!pDialog) + return false; + + // Set the heading + std::string heading = StringUtils::Format("$LOCALIZE[10004] - %s", addon->Name().c_str()); // "Settings - AddonName" + pDialog->m_strHeading = heading; + + pDialog->m_addon = addon; + pDialog->m_saveToDisk = saveToDisk; + pDialog->DoModal(); + ret = true; + } + else + { // addon does not support settings, inform user + CGUIDialogOK::ShowAndGetInput(24000,0,24030,0); + } + + return ret; +} + +bool CGUIDialogAddonSettings::ShowVirtualKeyboard(int iControl) +{ + int controlId = CONTROL_START_SETTING; + bool bCloseDialog = false; + + const TiXmlElement *setting = GetFirstSetting(); + while (setting) + { + if (controlId == iControl) + { + const CGUIControl* control = GetControl(controlId); + const std::string id = XMLUtils::GetAttribute(setting, "id"); + const std::string type = XMLUtils::GetAttribute(setting, "type"); + + //Special handling for actions: does not require id attribute. TODO: refactor me. + if (control && control->GetControlType() == CGUIControl::GUICONTROL_BUTTON && type == "action") + { + const char *option = setting->Attribute("option"); + std::string action = XMLUtils::GetAttribute(setting, "action"); + if (!action.empty()) + { + // replace $CWD with the url of plugin/script + StringUtils::Replace(action, "$CWD", m_addon->Path()); + StringUtils::Replace(action, "$ID", m_addon->ID()); + if (option) + bCloseDialog = (strcmpi(option, "close") == 0); + CApplicationMessenger::Get().ExecBuiltIn(action); + } + break; + } + + if (control && control->GetControlType() == CGUIControl::GUICONTROL_BUTTON && + !id.empty() && !type.empty()) + { + const char *option = setting->Attribute("option"); + const char *source = setting->Attribute("source"); + std::string value = m_buttonValues[id]; + std::string label = GetString(setting->Attribute("label")); + + if (type == "text") + { + // get any options + bool bHidden = false; + bool bEncoded = false; + if (option) + { + bHidden = (strstr(option, "hidden") != NULL); + bEncoded = (strstr(option, "urlencoded") != NULL); + } + if (bEncoded) + value = CURL::Decode(value); + + if (CGUIKeyboardFactory::ShowAndGetInput(value, label, true, bHidden)) + { + // if hidden hide input + if (bHidden) + { + std::string hiddenText; + hiddenText.append(value.size(), L'*'); + ((CGUIButtonControl *)control)->SetLabel2(hiddenText); + } + else + ((CGUIButtonControl*) control)->SetLabel2(value); + if (bEncoded) + value = CURL::Encode(value); + } + } + else if (type == "number" && CGUIDialogNumeric::ShowAndGetNumber(value, label)) + { + ((CGUIButtonControl*) control)->SetLabel2(value); + } + else if (type == "ipaddress" && CGUIDialogNumeric::ShowAndGetIPAddress(value, label)) + { + ((CGUIButtonControl*) control)->SetLabel2(value); + } + else if (type == "select") + { + CGUIDialogSelect *pDlg = (CGUIDialogSelect*)g_windowManager.GetWindow(WINDOW_DIALOG_SELECT); + if (pDlg) + { + pDlg->SetHeading(label.c_str()); + pDlg->Reset(); + + int selected = -1; + vector valuesVec; + if (setting->Attribute("values")) + StringUtils::Tokenize(setting->Attribute("values"), valuesVec, "|"); + else if (setting->Attribute("lvalues")) + { // localize + StringUtils::Tokenize(setting->Attribute("lvalues"), valuesVec, "|"); + for (unsigned int i = 0; i < valuesVec.size(); i++) + { + if (i == (unsigned int)atoi(value.c_str())) + selected = i; + std::string localized = m_addon->GetString(atoi(valuesVec[i].c_str())); + if (localized.empty()) + localized = g_localizeStrings.Get(atoi(valuesVec[i].c_str())); + valuesVec[i] = localized; + } + } + else if (source) + { + valuesVec = GetFileEnumValues(source, XMLUtils::GetAttribute(setting, "mask"), XMLUtils::GetAttribute(setting, "option")); + } + + for (unsigned int i = 0; i < valuesVec.size(); i++) + { + pDlg->Add(valuesVec[i]); + if (selected == (int)i || (selected < 0 && StringUtils::EqualsNoCase(valuesVec[i], value))) + pDlg->SetSelected(i); // FIXME: the SetSelected() does not select "i", it always defaults to the first position + } + pDlg->DoModal(); + int iSelected = pDlg->GetSelectedLabel(); + if (iSelected >= 0) + { + if (setting->Attribute("lvalues")) + value = StringUtils::Format("%i", iSelected); + else + value = valuesVec[iSelected]; + ((CGUIButtonControl*) control)->SetLabel2(valuesVec[iSelected]); + } + } + } + else if (type == "audio" || type == "video" + || type == "image" || type == "executable" + || type == "file" || type == "folder") + { + // setup the shares + VECSOURCES *shares = NULL; + if (source && strcmpi(source, "") != 0) + shares = CMediaSourceSettings::Get().GetSources(source); + + VECSOURCES localShares; + if (!shares) + { + g_mediaManager.GetLocalDrives(localShares); + if (!source || strcmpi(source, "local") != 0) + g_mediaManager.GetNetworkLocations(localShares); + } + else // always append local drives + { + localShares = *shares; + g_mediaManager.GetLocalDrives(localShares); + } + + if (type == "folder") + { + // get any options + bool bWriteOnly = false; + if (option) + bWriteOnly = (strcmpi(option, "writeable") == 0); + + if (CGUIDialogFileBrowser::ShowAndGetDirectory(localShares, label, value, bWriteOnly)) + ((CGUIButtonControl*) control)->SetLabel2(value); + } + else if (type == "image") + { + if (CGUIDialogFileBrowser::ShowAndGetImage(localShares, label, value)) + ((CGUIButtonControl*) control)->SetLabel2(value); + } + else + { + // set the proper mask + std::string strMask; + if (setting->Attribute("mask")) + { + strMask = setting->Attribute("mask"); + // convert mask qualifiers + StringUtils::Replace(strMask, "$AUDIO", g_advancedSettings.m_musicExtensions); + StringUtils::Replace(strMask, "$VIDEO", g_advancedSettings.m_videoExtensions); + StringUtils::Replace(strMask, "$IMAGE", g_advancedSettings.m_pictureExtensions); +#if defined(_WIN32_WINNT) + StringUtils::Replace(strMask, "$EXECUTABLE", ".exe|.bat|.cmd|.py"); +#else + StringUtils::Replace(strMask, "$EXECUTABLE", ""); +#endif + } + else + { + if (type == "video") + strMask = g_advancedSettings.m_videoExtensions; + else if (type == "audio") + strMask = g_advancedSettings.m_musicExtensions; + else if (type == "executable") +#if defined(_WIN32_WINNT) + strMask = ".exe|.bat|.cmd|.py"; +#else + strMask = ""; +#endif + } + + // get any options + bool bUseThumbs = false; + bool bUseFileDirectories = false; + if (option) + { + vector options = StringUtils::Split(option, '|'); + bUseThumbs = find(options.begin(), options.end(), "usethumbs") != options.end(); + bUseFileDirectories = find(options.begin(), options.end(), "treatasfolder") != options.end(); + } + + if (CGUIDialogFileBrowser::ShowAndGetFile(localShares, strMask, label, value, bUseThumbs, bUseFileDirectories)) + ((CGUIButtonControl*) control)->SetLabel2(value); + } + } + else if (type == "date") + { + CDateTime date; + if (!value.empty()) + date.SetFromDBDate(value); + SYSTEMTIME timedate; + date.GetAsSystemTime(timedate); + if(CGUIDialogNumeric::ShowAndGetDate(timedate, label)) + { + date = timedate; + value = date.GetAsDBDate(); + ((CGUIButtonControl*) control)->SetLabel2(value); + } + } + else if (type == "time") + { + SYSTEMTIME timedate; + if (value.size() >= 5) + { + // assumes HH:MM + timedate.wHour = atoi(value.substr(0, 2).c_str()); + timedate.wMinute = atoi(value.substr(3, 2).c_str()); + } + if (CGUIDialogNumeric::ShowAndGetTime(timedate, label)) + { + value = StringUtils::Format("%02d:%02d", timedate.wHour, timedate.wMinute); + ((CGUIButtonControl*) control)->SetLabel2(value); + } + } + else if (type == "addon") + { + const char *strType = setting->Attribute("addontype"); + if (strType) + { + vector addonTypes = StringUtils::Split(strType, ','); + vector types; + for (vector::iterator i = addonTypes.begin(); i != addonTypes.end(); ++i) + { + StringUtils::Trim(*i); + ADDON::TYPE type = TranslateType(*i); + if (type != ADDON_UNKNOWN) + types.push_back(type); + } + if (types.size() > 0) + { + const char *strMultiselect = setting->Attribute("multiselect"); + bool multiSelect = strMultiselect && strcmpi(strMultiselect, "true") == 0; + if (multiSelect) + { + // construct vector of addon IDs (IDs are comma seperated in single string) + vector addonIDs = StringUtils::Split(value, ','); + if (CGUIWindowAddonBrowser::SelectAddonID(types, addonIDs, false) == 1) + { + value = StringUtils::Join(addonIDs, ","); + ((CGUIButtonControl*) control)->SetLabel2(GetAddonNames(value)); + } + } + else // no need of string splitting/joining if we select only 1 addon + if (CGUIWindowAddonBrowser::SelectAddonID(types, value, false) == 1) + ((CGUIButtonControl*) control)->SetLabel2(GetAddonNames(value)); + } + } + } + m_buttonValues[id] = value; + break; + } + } + setting = setting->NextSiblingElement("setting"); + controlId++; + } + EnableControls(); + return bCloseDialog; +} + +void CGUIDialogAddonSettings::UpdateFromControls() +{ + int controlID = CONTROL_START_SETTING; + const TiXmlElement *setting = GetFirstSetting(); + while (setting) + { + const std::string id = XMLUtils::GetAttribute(setting, "id"); + const std::string type = XMLUtils::GetAttribute(setting, "type"); + const CGUIControl* control = GetControl(controlID++); + + if (control) + { + std::string value; + switch (control->GetControlType()) + { + case CGUIControl::GUICONTROL_BUTTON: + value = m_buttonValues[id]; + break; + case CGUIControl::GUICONTROL_RADIO: + value = ((CGUIRadioButtonControl*) control)->IsSelected() ? "true" : "false"; + break; + case CGUIControl::GUICONTROL_SPINEX: + if (type == "fileenum" || type == "labelenum") + value = ((CGUISpinControlEx*) control)->GetLabel(); + else + value = StringUtils::Format("%i", ((CGUISpinControlEx*) control)->GetValue()); + break; + case CGUIControl::GUICONTROL_SETTINGS_SLIDER: + { + std::string option = XMLUtils::GetAttribute(setting, "option"); + if (option.size() == 0 || StringUtils::EqualsNoCase(option, "float")) + value = StringUtils::Format("%f", ((CGUISettingsSliderControl *)control)->GetFloatValue()); + else + value = StringUtils::Format("%i", ((CGUISettingsSliderControl *)control)->GetIntValue()); + } + break; + default: + break; + } + m_settings[id] = value; + } + + setting = setting->NextSiblingElement("setting"); + } +} + +void CGUIDialogAddonSettings::SaveSettings(void) +{ + UpdateFromControls(); + + for (map::iterator i = m_settings.begin(); i != m_settings.end(); ++i) + m_addon->UpdateSetting(i->first, i->second); + + if (m_saveToDisk) + { + m_addon->SaveSettings(); + } +} + +void CGUIDialogAddonSettings::FreeSections() +{ + CGUIControlGroupList *group = dynamic_cast(GetControl(CONTROL_SECTION_AREA)); + if (group) + { + group->FreeResources(); + group->ClearAll(); + } + m_settings.clear(); + m_buttonValues.clear(); + FreeControls(); +} + +void CGUIDialogAddonSettings::FreeControls() +{ + // clear the category group + CGUIControlGroupList *control = dynamic_cast(GetControl(CONTROL_SETTINGS_AREA)); + if (control) + { + control->FreeResources(); + control->ClearAll(); + } +} + +void CGUIDialogAddonSettings::CreateSections() +{ + CGUIControlGroupList *group = dynamic_cast(GetControl(CONTROL_SECTION_AREA)); + CGUIButtonControl *originalButton = dynamic_cast(GetControl(CONTROL_DEFAULT_SECTION_BUTTON)); + if (!m_addon) + return; + + if (originalButton) + originalButton->SetVisible(false); + + // clear the category group + FreeSections(); + + // grab our categories + const TiXmlElement *category = m_addon->GetSettingsXML()->FirstChildElement("category"); + if (!category) // add a default one... + category = m_addon->GetSettingsXML(); + + int buttonID = CONTROL_START_SECTION; + while (category) + { // add a category + CGUIButtonControl *button = originalButton ? originalButton->Clone() : NULL; + + std::string label = GetString(category->Attribute("label")); + if (label.empty()) + label = g_localizeStrings.Get(128); + + if (buttonID >= CONTROL_START_SETTING) + { + CLog::Log(LOGERROR, "%s - cannot have more than %d categories - simplify your addon!", __FUNCTION__, CONTROL_START_SETTING - CONTROL_START_SECTION); + break; + } + + // add the category button + if (button && group) + { + button->SetID(buttonID++); + button->SetLabel(label); + button->SetVisible(true); + group->AddControl(button); + } + + // grab a local copy of all the settings in this category + const TiXmlElement *setting = category->FirstChildElement("setting"); + while (setting) + { + const std::string id = XMLUtils::GetAttribute(setting, "id"); + if (!id.empty()) + m_settings[id] = m_addon->GetSetting(id); + setting = setting->NextSiblingElement("setting"); + } + category = category->NextSiblingElement("category"); + } + m_totalSections = buttonID - CONTROL_START_SECTION; +} + +void CGUIDialogAddonSettings::CreateControls() +{ + FreeControls(); + + CGUISpinControlEx *pOriginalSpin = dynamic_cast(GetControl(CONTROL_DEFAULT_SPIN)); + CGUIRadioButtonControl *pOriginalRadioButton = dynamic_cast(GetControl(CONTROL_DEFAULT_RADIOBUTTON)); + CGUIButtonControl *pOriginalButton = dynamic_cast(GetControl(CONTROL_DEFAULT_BUTTON)); + CGUIImage *pOriginalImage = dynamic_cast(GetControl(CONTROL_DEFAULT_SEPARATOR)); + CGUILabelControl *pOriginalLabel = dynamic_cast(GetControl(CONTROL_DEFAULT_LABEL_SEPARATOR)); + CGUISettingsSliderControl *pOriginalSlider = dynamic_cast(GetControl(CONTROL_DEFAULT_SLIDER)); + + if (!m_addon || !pOriginalSpin || !pOriginalRadioButton || !pOriginalButton || !pOriginalImage + || !pOriginalLabel || !pOriginalSlider) + return; + + pOriginalSpin->SetVisible(false); + pOriginalRadioButton->SetVisible(false); + pOriginalButton->SetVisible(false); + pOriginalImage->SetVisible(false); + pOriginalLabel->SetVisible(false); + pOriginalSlider->SetVisible(false); + + CGUIControlGroupList *group = dynamic_cast(GetControl(CONTROL_SETTINGS_AREA)); + if (!group) + return; + + // set our dialog heading + SET_CONTROL_LABEL(CONTROL_HEADING_LABEL, m_strHeading); + + CGUIControl* pControl = NULL; + int controlId = CONTROL_START_SETTING; + const TiXmlElement *setting = GetFirstSetting(); + while (setting) + { + const std::string type = XMLUtils::GetAttribute(setting, "type"); + const std::string id = XMLUtils::GetAttribute(setting, "id"); + const std::string values = XMLUtils::GetAttribute(setting, "values"); + const std::string lvalues = XMLUtils::GetAttribute(setting, "lvalues"); + const std::string entries = XMLUtils::GetAttribute(setting, "entries"); + const std::string defaultVal = XMLUtils::GetAttribute(setting, "default"); + const std::string subsetting = XMLUtils::GetAttribute(setting, "subsetting"); + const std::string label = GetString(setting->Attribute("label"), subsetting == "true"); + + bool bSort = XMLUtils::GetAttribute(setting, "sort") == "yes"; + if (!type.empty()) + { + bool isAddonSetting = false; + if (type == "text" || type == "ipaddress" + || type == "number" || type == "video" + || type == "audio" || type == "image" + || type == "folder" || type == "executable" + || type == "file" || type == "action" + || type == "date" || type == "time" + || type == "select" || (isAddonSetting = type == "addon")) + { + pControl = new CGUIButtonControl(*pOriginalButton); + if (!pControl) return; + ((CGUIButtonControl *)pControl)->SetLabel(label); + if (!id.empty()) + { + std::string value = m_settings[id]; + m_buttonValues[id] = value; + // get any option to test for hidden + const std::string option = XMLUtils::GetAttribute(setting, "option"); + if (option == "urlencoded") + value = CURL::Decode(value); + else if (option == "hidden") + { + std::string hiddenText; + hiddenText.append(value.size(), L'*'); + ((CGUIButtonControl *)pControl)->SetLabel2(hiddenText); + } + else + { + if (isAddonSetting) + ((CGUIButtonControl *)pControl)->SetLabel2(GetAddonNames(value)); + else if (type == "select" && !lvalues.empty()) + { + vector valuesVec = StringUtils::Split(lvalues, '|'); + int selected = atoi(value.c_str()); + if (selected >= 0 && selected < (int)valuesVec.size()) + { + std::string label = m_addon->GetString(atoi(valuesVec[selected].c_str())); + if (label.empty()) + label = g_localizeStrings.Get(atoi(valuesVec[selected].c_str())); + ((CGUIButtonControl *)pControl)->SetLabel2(label); + } + } + else + ((CGUIButtonControl *)pControl)->SetLabel2(value); + } + } + else + ((CGUIButtonControl *)pControl)->SetLabel2(defaultVal); + } + else if (type == "bool" && !id.empty()) + { + pControl = new CGUIRadioButtonControl(*pOriginalRadioButton); + if (!pControl) return; + ((CGUIRadioButtonControl *)pControl)->SetLabel(label); + ((CGUIRadioButtonControl *)pControl)->SetSelected(m_settings[id] == "true"); + } + else if ((type == "enum" || type == "labelenum") && !id.empty()) + { + vector valuesVec; + vector entryVec; + + pControl = new CGUISpinControlEx(*pOriginalSpin); + if (!pControl) return; + ((CGUISpinControlEx *)pControl)->SetText(label); + + if (!lvalues.empty()) + StringUtils::Tokenize(lvalues, valuesVec, "|"); + else if (values == "$HOURS") + { + for (unsigned int i = 0; i < 24; i++) + { + CDateTime time(2000, 1, 1, i, 0, 0); + valuesVec.push_back(g_infoManager.LocalizeTime(time, TIME_FORMAT_HH_MM_XX)); + } + } + else + StringUtils::Tokenize(values, valuesVec, "|"); + if (!entries.empty()) + StringUtils::Tokenize(entries, entryVec, "|"); + + if(bSort && type == "labelenum") + std::sort(valuesVec.begin(), valuesVec.end(), sortstringbyname()); + + for (unsigned int i = 0; i < valuesVec.size(); i++) + { + int iAdd = i; + if (entryVec.size() > i) + iAdd = atoi(entryVec[i].c_str()); + if (!lvalues.empty()) + { + std::string replace = m_addon->GetString(atoi(valuesVec[i].c_str())); + if (replace.empty()) + replace = g_localizeStrings.Get(atoi(valuesVec[i].c_str())); + ((CGUISpinControlEx *)pControl)->AddLabel(replace, iAdd); + } + else + ((CGUISpinControlEx *)pControl)->AddLabel(valuesVec[i], iAdd); + } + if (type == "labelenum") + { // need to run through all our settings and find the one that matches + ((CGUISpinControlEx*) pControl)->SetValueFromLabel(m_settings[id]); + } + else + ((CGUISpinControlEx*) pControl)->SetValue(atoi(m_settings[id].c_str())); + + } + else if (type == "fileenum" && !id.empty()) + { + pControl = new CGUISpinControlEx(*pOriginalSpin); + if (!pControl) return; + ((CGUISpinControlEx *)pControl)->SetText(label); + ((CGUISpinControlEx *)pControl)->SetFloatValue(1.0f); + + vector items = GetFileEnumValues(values, XMLUtils::GetAttribute(setting, "mask"), XMLUtils::GetAttribute(setting, "option")); + for (unsigned int i = 0; i < items.size(); ++i) + { + ((CGUISpinControlEx *)pControl)->AddLabel(items[i], i); + if (StringUtils::EqualsNoCase(items[i], m_settings[id])) + ((CGUISpinControlEx *)pControl)->SetValue(i); + } + } + // Sample: + // in strings.xml: %2.0f mp + // creates 11 piece, text formated number labels from 0 to 100 + else if (type == "rangeofnum" && !id.empty()) + { + pControl = new CGUISpinControlEx(*pOriginalSpin); + if (!pControl) + return; + ((CGUISpinControlEx *)pControl)->SetText(label); + ((CGUISpinControlEx *)pControl)->SetFloatValue(1.0f); + + double rangestart = 0, rangeend = 1; + setting->Attribute("rangestart", &rangestart); + setting->Attribute("rangeend", &rangeend); + + int elements = 2; + setting->Attribute("elements", &elements); + + std::string valueformat; + if (setting->Attribute("valueformat")) + valueformat = m_addon->GetString(atoi(setting->Attribute("valueformat"))); + for (int i = 0; i < elements; i++) + { + std::string valuestring; + if (elements < 2) + valuestring = StringUtils::Format(valueformat.c_str(), rangestart); + else + valuestring = StringUtils::Format(valueformat.c_str(), rangestart+(rangeend-rangestart)/(elements-1)*i); + ((CGUISpinControlEx *)pControl)->AddLabel(valuestring, i); + } + ((CGUISpinControlEx *)pControl)->SetValue(atoi(m_settings[id].c_str())); + } + // Sample: + // to make ints from 5-60 with 5 steps + else if (type == "slider" && !id.empty()) + { + pControl = new CGUISettingsSliderControl(*pOriginalSlider); + if (!pControl) return; + ((CGUISettingsSliderControl *)pControl)->SetText(label); + + float fMin = 0.0f; + float fMax = 100.0f; + float fInc = 1.0f; + vector range = StringUtils::Split(XMLUtils::GetAttribute(setting, "range"), ','); + if (range.size() > 1) + { + fMin = (float)atof(range[0].c_str()); + if (range.size() > 2) + { + fMax = (float)atof(range[2].c_str()); + fInc = (float)atof(range[1].c_str()); + } + else + fMax = (float)atof(range[1].c_str()); + } + + std::string option = XMLUtils::GetAttribute(setting, "option"); + int iType=0; + + if (option.empty() || StringUtils::EqualsNoCase(option, "float")) + iType = SLIDER_CONTROL_TYPE_FLOAT; + else if (StringUtils::EqualsNoCase(option, "int")) + iType = SLIDER_CONTROL_TYPE_INT; + else if (StringUtils::EqualsNoCase(option, "percent")) + iType = SLIDER_CONTROL_TYPE_PERCENTAGE; + + ((CGUISettingsSliderControl *)pControl)->SetType(iType); + ((CGUISettingsSliderControl *)pControl)->SetFloatRange(fMin, fMax); + ((CGUISettingsSliderControl *)pControl)->SetFloatInterval(fInc); + ((CGUISettingsSliderControl *)pControl)->SetFloatValue((float)atof(m_settings[id].c_str())); + } + else if (type == "lsep") + { + pControl = new CGUILabelControl(*pOriginalLabel); + if (pControl) + ((CGUILabelControl *)pControl)->SetLabel(label); + } + else if (type == "sep") + pControl = new CGUIImage(*pOriginalImage); + } + + if (pControl) + { + pControl->SetWidth(group->GetWidth()); + pControl->SetVisible(true); + pControl->SetID(controlId); + pControl->AllocResources(); + group->AddControl(pControl); + pControl = NULL; + } + + setting = setting->NextSiblingElement("setting"); + controlId++; + } + EnableControls(); +} + +std::string CGUIDialogAddonSettings::GetAddonNames(const std::string& addonIDslist) const +{ + std::string retVal; + vector addons = StringUtils::Split(addonIDslist, ','); + for (vector::const_iterator it = addons.begin(); it != addons.end() ; ++it) + { + if (!retVal.empty()) + retVal += ", "; + AddonPtr addon; + if (CAddonMgr::Get().GetAddon(*it ,addon)) + retVal += addon->Name(); + else + retVal += *it; + } + return retVal; +} + +vector CGUIDialogAddonSettings::GetFileEnumValues(const std::string &path, const std::string &mask, const std::string &options) const +{ + // Create our base path, used for type "fileenum" settings + // replace $PROFILE with the profile path of the plugin/script + std::string fullPath = path; + if (fullPath.find("$PROFILE") != std::string::npos) + StringUtils::Replace(fullPath, "$PROFILE", m_addon->Profile()); + else + fullPath = URIUtils::AddFileToFolder(m_addon->Path(), path); + + bool hideExtensions = StringUtils::EqualsNoCase(options, "hideext"); + // fetch directory + CFileItemList items; + if (!mask.empty()) + CDirectory::GetDirectory(fullPath, items, mask, XFILE::DIR_FLAG_NO_FILE_DIRS); + else + CDirectory::GetDirectory(fullPath, items, "", XFILE::DIR_FLAG_NO_FILE_DIRS); + + vector values; + for (int i = 0; i < items.Size(); ++i) + { + CFileItemPtr pItem = items[i]; + if ((mask == "/" && pItem->m_bIsFolder) || !pItem->m_bIsFolder) + { + if (hideExtensions) + pItem->RemoveExtension(); + values.push_back(pItem->GetLabel()); + } + } + return values; +} + +// Go over all the settings and set their enabled condition according to the values of the enabled attribute +void CGUIDialogAddonSettings::EnableControls() +{ + int controlId = CONTROL_START_SETTING; + const TiXmlElement *setting = GetFirstSetting(); + while (setting) + { + const CGUIControl* control = GetControl(controlId); + if (control) + { + // set enable status + const char *enable = setting->Attribute("enable"); + if (enable) + ((CGUIControl*) control)->SetEnabled(GetCondition(enable, controlId)); + else + ((CGUIControl*) control)->SetEnabled(true); + // set visible status + const char *visible = setting->Attribute("visible"); + if (visible) + ((CGUIControl*) control)->SetVisible(GetCondition(visible, controlId)); + else + ((CGUIControl*) control)->SetVisible(true); + } + setting = setting->NextSiblingElement("setting"); + controlId++; + } +} + +bool CGUIDialogAddonSettings::GetCondition(const std::string &condition, const int controlId) +{ + if (condition.empty()) return true; + + bool bCondition = true; + bool bCompare = true; + bool bControlDependend = false;//flag if the condition depends on another control + vector conditionVec; + + if (condition.find("+") != std::string::npos) + StringUtils::Tokenize(condition, conditionVec, "+"); + else + { + bCondition = false; + bCompare = false; + StringUtils::Tokenize(condition, conditionVec, "|"); + } + + for (unsigned int i = 0; i < conditionVec.size(); i++) + { + vector condVec; + if (!TranslateSingleString(conditionVec[i], condVec)) continue; + + const CGUIControl* control2 = GetControl(controlId + atoi(condVec[1].c_str())); + if (!control2) + continue; + + bControlDependend = true; //once we are here - this condition depends on another control + + std::string value; + switch (control2->GetControlType()) + { + case CGUIControl::GUICONTROL_BUTTON: + value = ((CGUIButtonControl*) control2)->GetLabel2(); + break; + case CGUIControl::GUICONTROL_RADIO: + value = ((CGUIRadioButtonControl*) control2)->IsSelected() ? "true" : "false"; + break; + case CGUIControl::GUICONTROL_SPINEX: + if (((CGUISpinControlEx*) control2)->GetFloatValue() > 0.0f) + value = ((CGUISpinControlEx*) control2)->GetLabel(); + else + value = StringUtils::Format("%i", ((CGUISpinControlEx*) control2)->GetValue()); + break; + default: + break; + } + + if (condVec[0] == "eq") + { + if (bCompare) + bCondition &= StringUtils::EqualsNoCase(value, condVec[2]); + else + bCondition |= StringUtils::EqualsNoCase(value, condVec[2]); + } + else if (condVec[0] == "!eq") + { + if (bCompare) + bCondition &= !StringUtils::EqualsNoCase(value, condVec[2]); + else + bCondition |= !StringUtils::EqualsNoCase(value, condVec[2]); + } + else if (condVec[0] == "gt") + { + if (bCompare) + bCondition &= (atoi(value.c_str()) > atoi(condVec[2].c_str())); + else + bCondition |= (atoi(value.c_str()) > atoi(condVec[2].c_str())); + } + else if (condVec[0] == "lt") + { + if (bCompare) + bCondition &= (atoi(value.c_str()) < atoi(condVec[2].c_str())); + else + bCondition |= (atoi(value.c_str()) < atoi(condVec[2].c_str())); + } + } + + if (!bControlDependend)//if condition doesn't depend on another control - try if its an infobool expression + { + bCondition = g_infoManager.EvaluateBool(condition); + } + + return bCondition; +} + +bool CGUIDialogAddonSettings::TranslateSingleString(const std::string &strCondition, vector &condVec) +{ + std::string strTest = strCondition; + StringUtils::ToLower(strTest); + StringUtils::Trim(strTest); + + size_t pos1 = strTest.find("("); + size_t pos2 = strTest.find(",", pos1); + size_t pos3 = strTest.find(")", pos2); + if (pos1 != std::string::npos && + pos2 != std::string::npos && + pos3 != std::string::npos) + { + condVec.push_back(strTest.substr(0, pos1)); + condVec.push_back(strTest.substr(pos1 + 1, pos2 - pos1 - 1)); + condVec.push_back(strTest.substr(pos2 + 1, pos3 - pos2 - 1)); + return true; + } + return false; +} + +std::string CGUIDialogAddonSettings::GetString(const char *value, bool subSetting) const +{ + if (!value) + return ""; + std::string prefix(subSetting ? "- " : ""); + if (StringUtils::IsNaturalNumber(value)) + return prefix + m_addon->GetString(atoi(value)); + return prefix + value; +} + +// Go over all the settings and set their default values +void CGUIDialogAddonSettings::SetDefaultSettings() +{ + if(!m_addon) + return; + + const TiXmlElement *category = m_addon->GetSettingsXML()->FirstChildElement("category"); + if (!category) // add a default one... + category = m_addon->GetSettingsXML(); + + while (category) + { + const TiXmlElement *setting = category->FirstChildElement("setting"); + while (setting) + { + const std::string id = XMLUtils::GetAttribute(setting, "id"); + const std::string type = XMLUtils::GetAttribute(setting, "type"); + const char *value = setting->Attribute("default"); + if (!id.empty()) + { + if (value) + m_settings[id] = value; + else if (type == "bool") + m_settings[id] = "false"; + else if (type == "slider" || type == "enum") + m_settings[id] = "0"; + else + m_settings[id] = ""; + } + setting = setting->NextSiblingElement("setting"); + } + category = category->NextSiblingElement("category"); + } + CreateControls(); +} + +const TiXmlElement *CGUIDialogAddonSettings::GetFirstSetting() const +{ + const TiXmlElement *category = m_addon->GetSettingsXML()->FirstChildElement("category"); + if (!category) + category = m_addon->GetSettingsXML(); + for (unsigned int i = 0; i < m_currentSection && category; i++) + category = category->NextSiblingElement("category"); + if (category) + return category->FirstChildElement("setting"); + return NULL; +} + +void CGUIDialogAddonSettings::DoProcess(unsigned int currentTime, CDirtyRegionList &dirtyregions) +{ + // update status of current section button + bool alphaFaded = false; + CGUIControl *control = GetFirstFocusableControl(CONTROL_START_SECTION + m_currentSection); + if (control && !control->HasFocus()) + { + if (control->GetControlType() == CGUIControl::GUICONTROL_BUTTON) + { + control->SetFocus(true); + ((CGUIButtonControl *)control)->SetAlpha(0x80); + alphaFaded = true; + } + else if (control->GetControlType() == CGUIControl::GUICONTROL_TOGGLEBUTTON) + { + control->SetFocus(true); + ((CGUIButtonControl *)control)->SetSelected(true); + alphaFaded = true; + } + } + CGUIDialogBoxBase::DoProcess(currentTime, dirtyregions); + if (alphaFaded && m_active) // dialog may close + { + control->SetFocus(false); + if (control->GetControlType() == CGUIControl::GUICONTROL_BUTTON) + ((CGUIButtonControl *)control)->SetAlpha(0xFF); + else + ((CGUIButtonControl *)control)->SetSelected(false); + } +} + +std::string CGUIDialogAddonSettings::GetCurrentID() const +{ + if (m_addon) + return m_addon->ID(); + return ""; +} + +int CGUIDialogAddonSettings::GetDefaultLabelID(int controlId) const +{ + if (controlId == ID_BUTTON_OK) + return 186; + else if (controlId == ID_BUTTON_CANCEL) + return 222; + else if (controlId == ID_BUTTON_DEFAULT) + return 409; + + return CGUIDialogBoxBase::GetDefaultLabelID(controlId); +} diff --git a/xbmc/addons/GUIDialogAddonSettings.h b/xbmc/addons/GUIDialogAddonSettings.h new file mode 100644 index 0000000..9c9c156 --- /dev/null +++ b/xbmc/addons/GUIDialogAddonSettings.h @@ -0,0 +1,91 @@ +#pragma once +/* + * Copyright (C) 2005-2013 Team XBMC + * http://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, see + * . + * + */ + +#include "dialogs/GUIDialogBoxBase.h" +#include "addons/Addon.h" + +class CGUIDialogAddonSettings : public CGUIDialogBoxBase +{ +public: + CGUIDialogAddonSettings(void); + virtual ~CGUIDialogAddonSettings(void); + virtual bool OnMessage(CGUIMessage& message); + virtual bool OnAction(const CAction& action); + /*! \brief Show the addon settings dialog, allowing the user to configure an addon + \param addon the addon to configure + \param saveToDisk whether the changes should be saved to disk or just made local to the addon. Defaults to true + \return true if settings were changed and the dialog confirmed, false otherwise. + */ + static bool ShowAndGetInput(const ADDON::AddonPtr &addon, bool saveToDisk = true); + virtual void DoProcess(unsigned int currentTime, CDirtyRegionList &dirtyregions); + + std::string GetCurrentID() const; +protected: + virtual void OnInitWindow(); + virtual int GetDefaultLabelID(int controlId) const; + +private: + /*! \brief return a (localized) addon string. + \param value either a character string (which is used directly) or a number to lookup in the addons strings.xml + \param subsetting whether the character string should be prefixed by "- ", defaults to false + \return the localized addon string + */ + std::string GetString(const char *value, bool subSetting = false) const; + + /*! \brief return a the values for a fileenum setting + \param path the path to use for files + \param mask the mask to use + \param options any options, such as "hideext" to hide extensions + \return the filenames in the path that match the mask + */ + std::vector GetFileEnumValues(const std::string &path, const std::string &mask, const std::string &options) const; + + /*! \brief Translate list of addon IDs to list of addon names + \param addonIDslist comma seperated list of addon IDs + \return comma seperated list of addon names + */ + std::string GetAddonNames(const std::string& addonIDslist) const; + + void CreateSections(); + void FreeSections(); + void CreateControls(); + void FreeControls(); + void UpdateFromControls(); + void EnableControls(); + void SetDefaultSettings(); + bool GetCondition(const std::string &condition, const int controlId); + + void SaveSettings(void); + bool ShowVirtualKeyboard(int iControl); + bool TranslateSingleString(const std::string &strCondition, std::vector &enableVec); + + const TiXmlElement *GetFirstSetting() const; + + ADDON::AddonPtr m_addon; + std::map m_buttonValues; + bool m_saveToDisk; // whether the addon settings should be saved to disk or just stored locally in the addon + + unsigned int m_currentSection; + unsigned int m_totalSections; + + std::map m_settings; // local storage of values +}; + diff --git a/xbmc/addons/GUIViewStateAddonBrowser.cpp b/xbmc/addons/GUIViewStateAddonBrowser.cpp new file mode 100644 index 0000000..4f42b2e --- /dev/null +++ b/xbmc/addons/GUIViewStateAddonBrowser.cpp @@ -0,0 +1,132 @@ +/* + * Copyright (C) 2005-2013 Team XBMC + * http://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, see + * . + * + */ + +#include "GUIViewStateAddonBrowser.h" +#include "FileItem.h" +#include "filesystem/File.h" +#include "guilib/GraphicContext.h" +#include "guilib/WindowIDs.h" +#include "view/ViewState.h" +#include "addons/Addon.h" +#include "addons/AddonManager.h" +#include "addons/AddonInstaller.h" +#include "AddonDatabase.h" +#include "utils/StringUtils.h" + +using namespace XFILE; +using namespace ADDON; + +CGUIViewStateAddonBrowser::CGUIViewStateAddonBrowser(const CFileItemList& items) : CGUIViewState(items) +{ + if (items.IsVirtualDirectoryRoot()) + { + AddSortMethod(SortByNone, 551, LABEL_MASKS("%F", "", "%L", "")); + SetSortMethod(SortByNone); + } + else + { + AddSortMethod(SortByLabel, SortAttributeIgnoreFolders, 551, LABEL_MASKS("%L", "%I", "%L", "")); // Filename, Size | Foldername, empty + AddSortMethod(SortByDate, 552, LABEL_MASKS("%L", "%J", "%L", "%J")); // Filename, Date | Foldername, Date + SetSortMethod(SortByLabel); + } + SetViewAsControl(DEFAULT_VIEW_AUTO); + + SetSortOrder(SortOrderAscending); + LoadViewState(items.GetPath(), WINDOW_ADDON_BROWSER); +} + +void CGUIViewStateAddonBrowser::SaveViewState() +{ + SaveViewToDb(m_items.GetPath(), WINDOW_ADDON_BROWSER); +} + +std::string CGUIViewStateAddonBrowser::GetExtensions() +{ + return ""; +} + +VECSOURCES& CGUIViewStateAddonBrowser::GetSources() +{ + m_sources.clear(); + + { // check for updates + CMediaSource share; + share.strPath = "addons://check/"; + share.m_iDriveType = CMediaSource::SOURCE_TYPE_REMOTE; // hack for sorting + share.strName = g_localizeStrings.Get(24055); // "Check for updates" + CDateTime lastChecked = CAddonInstaller::Get().LastRepoUpdate(); + if (lastChecked.IsValid()) + share.strStatus = StringUtils::Format(g_localizeStrings.Get(24056).c_str(), + lastChecked.GetAsLocalizedDateTime(false, false).c_str()); + m_sources.push_back(share); + } + if (CAddonMgr::Get().HasOutdatedAddons()) + { + CMediaSource share; + share.strPath = "addons://outdated/"; + share.m_iDriveType = CMediaSource::SOURCE_TYPE_LOCAL; + share.strName = g_localizeStrings.Get(24043); // "Available updates" + m_sources.push_back(share); + } + CAddonDatabase db; + if (db.Open() && db.HasDisabledAddons()) + { + CMediaSource share; + share.strPath = "addons://disabled/"; + share.m_iDriveType = CMediaSource::SOURCE_TYPE_LOCAL; + share.strName = g_localizeStrings.Get(24039); + m_sources.push_back(share); + } + // we always have some enabled addons + { + CMediaSource share; + share.strPath = "addons://enabled/"; + share.m_iDriveType = CMediaSource::SOURCE_TYPE_LOCAL; + share.strName = g_localizeStrings.Get(24062); + m_sources.push_back(share); + } + if (CAddonMgr::Get().HasAddons(ADDON_REPOSITORY,true)) + { + CMediaSource share; + share.strPath = "addons://repos/"; + share.m_iDriveType = CMediaSource::SOURCE_TYPE_LOCAL; + share.strName = g_localizeStrings.Get(24033); + m_sources.push_back(share); + } + // add "install from zip" + { + CMediaSource share; + share.strPath = "addons://install/"; + share.m_iDriveType = CMediaSource::SOURCE_TYPE_LOCAL; + share.strName = g_localizeStrings.Get(24041); + m_sources.push_back(share); + } + // add "search" + { + CMediaSource share; + share.strPath = "addons://search/"; + share.m_iDriveType = CMediaSource::SOURCE_TYPE_LOCAL; + share.strName = g_localizeStrings.Get(137); + m_sources.push_back(share); + } + + return CGUIViewState::GetSources(); +} + diff --git a/xbmc/addons/GUIViewStateAddonBrowser.h b/xbmc/addons/GUIViewStateAddonBrowser.h new file mode 100644 index 0000000..565cdc8 --- /dev/null +++ b/xbmc/addons/GUIViewStateAddonBrowser.h @@ -0,0 +1,35 @@ +#pragma once + +/* + * Copyright (C) 2005-2013 Team XBMC + * http://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, see + * . + * + */ + +#include "view/GUIViewState.h" + +class CGUIViewStateAddonBrowser : public CGUIViewState +{ +public: + CGUIViewStateAddonBrowser(const CFileItemList& items); + +protected: + virtual void SaveViewState(); + virtual std::string GetExtensions(); + virtual VECSOURCES& GetSources(); +}; + diff --git a/xbmc/addons/GUIWindowAddonBrowser.cpp b/xbmc/addons/GUIWindowAddonBrowser.cpp new file mode 100644 index 0000000..f4560dd --- /dev/null +++ b/xbmc/addons/GUIWindowAddonBrowser.cpp @@ -0,0 +1,677 @@ +/* + * Copyright (C) 2005-2013 Team XBMC + * http://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, see + * . + * + */ + +#include "GUIWindowAddonBrowser.h" +#include "addons/AddonManager.h" +#include "addons/Repository.h" +#include "GUIDialogAddonInfo.h" +#include "GUIDialogAddonSettings.h" +#include "dialogs/GUIDialogBusy.h" +#include "dialogs/GUIDialogOK.h" +#include "dialogs/GUIDialogYesNo.h" +#include "dialogs/GUIDialogSelect.h" +#include "dialogs/GUIDialogFileBrowser.h" +#include "GUIUserMessages.h" +#include "guilib/GUIWindowManager.h" +#include "utils/URIUtils.h" +#include "URL.h" +#include "FileItem.h" +#include "filesystem/File.h" +#include "filesystem/Directory.h" +#include "filesystem/AddonsDirectory.h" +#include "addons/AddonInstaller.h" +#include "utils/JobManager.h" +#include "utils/log.h" +#include "threads/SingleLock.h" +#include "settings/Settings.h" +#include "settings/MediaSourceSettings.h" +#include "utils/StringUtils.h" +#include "AddonDatabase.h" +#include "settings/AdvancedSettings.h" +#include "storage/MediaManager.h" +#include "LangInfo.h" +#include "input/Key.h" +#include "ContextMenuManager.h" + +#define CONTROL_AUTOUPDATE 5 +#define CONTROL_SHUTUP 6 +#define CONTROL_FOREIGNFILTER 7 +#define CONTROL_BROKENFILTER 8 + +using namespace ADDON; +using namespace XFILE; +using namespace std; + +CGUIWindowAddonBrowser::CGUIWindowAddonBrowser(void) +: CGUIMediaWindow(WINDOW_ADDON_BROWSER, "AddonBrowser.xml") +{ +} + +CGUIWindowAddonBrowser::~CGUIWindowAddonBrowser() +{ +} + +bool CGUIWindowAddonBrowser::OnMessage(CGUIMessage& message) +{ + switch ( message.GetMessage() ) + { + case GUI_MSG_WINDOW_DEINIT: + { + if (m_thumbLoader.IsLoading()) + m_thumbLoader.StopThread(); + } + break; + case GUI_MSG_WINDOW_INIT: + { + m_rootDir.AllowNonLocalSources(false); + + // is this the first time the window is opened? + if (m_vecItems->GetPath() == "?" && message.GetStringParam().empty()) + m_vecItems->SetPath(""); + } + break; + case GUI_MSG_CLICKED: + { + int iControl = message.GetSenderId(); + if (iControl == CONTROL_AUTOUPDATE) + { + const CGUIControl *control = GetControl(CONTROL_AUTOUPDATE); + if (control && control->GetControlType() == CGUIControl::GUICONTROL_BUTTON) + CSettings::Get().SetInt("general.addonupdates", (CSettings::Get().GetInt("general.addonupdates")+1) % AUTO_UPDATES_MAX); + else + CSettings::Get().SetInt("general.addonupdates", (CSettings::Get().GetInt("general.addonupdates") == 0) ? 1 : 0); + UpdateButtons(); + return true; + } + else if (iControl == CONTROL_SHUTUP) + { + CSettings::Get().ToggleBool("general.addonnotifications"); + CSettings::Get().Save(); + return true; + } + else if (iControl == CONTROL_FOREIGNFILTER) + { + CSettings::Get().ToggleBool("general.addonforeignfilter"); + CSettings::Get().Save(); + Refresh(); + return true; + } + else if (iControl == CONTROL_BROKENFILTER) + { + CSettings::Get().ToggleBool("general.addonbrokenfilter"); + CSettings::Get().Save(); + Refresh(); + return true; + } + else if (m_viewControl.HasControl(iControl)) // list/thumb control + { + // get selected item + int iItem = m_viewControl.GetSelectedItem(); + int iAction = message.GetParam1(); + + // iItem is checked for validity inside these routines + if (iAction == ACTION_SHOW_INFO) + { + if (!m_vecItems->Get(iItem)->GetProperty("Addon.ID").empty()) + return CGUIDialogAddonInfo::ShowForItem((*m_vecItems)[iItem]); + return false; + } + } + } + break; + case GUI_MSG_NOTIFY_ALL: + { + if (message.GetParam1() == GUI_MSG_UPDATE_ITEM && IsActive() && message.GetNumStringParams() == 1) + { // update this item + for (int i = 0; i < m_vecItems->Size(); ++i) + { + CFileItemPtr item = m_vecItems->Get(i); + if (item->GetProperty("Addon.ID") == message.GetStringParam()) + { + SetItemLabel2(item); + return true; + } + } + } + } + break; + default: + break; + } + return CGUIMediaWindow::OnMessage(message); +} + +void CGUIWindowAddonBrowser::GetContextButtons(int itemNumber, CContextButtons& buttons) +{ + if (itemNumber < 0 || itemNumber >= m_vecItems->Size()) + return; + + CFileItemPtr pItem = m_vecItems->Get(itemNumber); + if (!pItem->IsPath("addons://enabled/")) + buttons.Add(CONTEXT_BUTTON_SCAN,24034); + + AddonPtr addon; + if (!CAddonMgr::Get().GetAddon(pItem->GetProperty("Addon.ID").asString(), addon, ADDON_UNKNOWN, false)) // allow disabled addons + return; + + if (addon->Type() == ADDON_REPOSITORY && pItem->m_bIsFolder) + { + buttons.Add(CONTEXT_BUTTON_SCAN,24034); + buttons.Add(CONTEXT_BUTTON_REFRESH,24035); + } + + buttons.Add(CONTEXT_BUTTON_INFO,24003); + + if (addon->HasSettings()) + buttons.Add(CONTEXT_BUTTON_SETTINGS,24020); + + CContextMenuManager::Get().AddVisibleItems(pItem, buttons); +} + +bool CGUIWindowAddonBrowser::OnContextButton(int itemNumber, + CONTEXT_BUTTON button) +{ + CFileItemPtr pItem = m_vecItems->Get(itemNumber); + if (pItem->IsPath("addons://enabled/")) + { + if (button == CONTEXT_BUTTON_SCAN) + { + CAddonMgr::Get().FindAddons(); + return true; + } + } + + AddonPtr addon; + if (CAddonMgr::Get().GetAddon(pItem->GetProperty("Addon.ID").asString(), addon, ADDON_UNKNOWN, false)) + { + if (button == CONTEXT_BUTTON_SETTINGS) + return CGUIDialogAddonSettings::ShowAndGetInput(addon); + + if (button == CONTEXT_BUTTON_REFRESH) + { + CAddonDatabase database; + database.Open(); + database.DeleteRepository(addon->ID()); + button = CONTEXT_BUTTON_SCAN; + } + + if (button == CONTEXT_BUTTON_SCAN) + { + CAddonInstaller::Get().UpdateRepos(true); + return true; + } + + if (button == CONTEXT_BUTTON_INFO) + { + CGUIDialogAddonInfo::ShowForItem(pItem); + return true; + } + } + + return CGUIMediaWindow::OnContextButton(itemNumber, button); +} + +class UpdateAddons : public IRunnable +{ + virtual void Run() + { + VECADDONS addons; + CAddonMgr::Get().GetAllOutdatedAddons(addons, true); // get local + for (VECADDONS::iterator i = addons.begin(); i != addons.end(); ++i) + { + std::string referer = StringUtils::Format("Referer=%s-%s.zip",(*i)->ID().c_str(),(*i)->Version().asString().c_str()); + CAddonInstaller::Get().Install((*i)->ID(), true, referer); // force install + } + } +}; + +class UpdateRepos : public IRunnable +{ + virtual void Run() + { + CAddonInstaller::Get().UpdateRepos(true, true); + } +}; + +bool CGUIWindowAddonBrowser::OnClick(int iItem) +{ + CFileItemPtr item = m_vecItems->Get(iItem); + if (item->GetPath() == "addons://install/") + { + // pop up filebrowser to grab an installed folder + VECSOURCES shares = *CMediaSourceSettings::Get().GetSources("files"); + g_mediaManager.GetLocalDrives(shares); + g_mediaManager.GetNetworkLocations(shares); + std::string path; + if (CGUIDialogFileBrowser::ShowAndGetFile(shares, "*.zip", g_localizeStrings.Get(24041), path)) + CAddonInstaller::Get().InstallFromZip(path); + return true; + } + else if (item->GetPath() == "addons://check/") + { + // perform the check for updates + UpdateRepos updater; + if (CGUIDialogBusy::Wait(&updater)) + Refresh(); + return true; + } + if (item->GetPath() == "addons://update_all/") + { + // fire off a threaded update of all addons + UpdateAddons updater; + if (CGUIDialogBusy::Wait(&updater)) + return Update("addons://downloading/"); + return true; + } + if (!item->m_bIsFolder) + { + // cancel a downloading job + if (item->HasProperty("Addon.Downloading")) + { + if (CGUIDialogYesNo::ShowAndGetInput(g_localizeStrings.Get(24000), + item->GetProperty("Addon.Name").asString(), + g_localizeStrings.Get(24066),"")) + { + if (CAddonInstaller::Get().Cancel(item->GetProperty("Addon.ID").asString())) + Refresh(); + } + return true; + } + + CGUIDialogAddonInfo::ShowForItem(item); + return true; + } + if (item->IsPath("addons://search/")) + return Update(item->GetPath()); + + return CGUIMediaWindow::OnClick(iItem); +} + +void CGUIWindowAddonBrowser::UpdateButtons() +{ + const CGUIControl *control = GetControl(CONTROL_AUTOUPDATE); + if (control && control->GetControlType() == CGUIControl::GUICONTROL_BUTTON) + { // set label + CSettingInt *setting = (CSettingInt *)CSettings::Get().GetSetting("general.addonupdates"); + if (setting) + { + const StaticIntegerSettingOptions& options = setting->GetOptions(); + for (StaticIntegerSettingOptions::const_iterator it = options.begin(); it != options.end(); ++it) + { + if (it->second == setting->GetValue()) + { + SET_CONTROL_LABEL(CONTROL_AUTOUPDATE, it->first); + break; + } + } + } + } + else + { // old skin with toggle button - set on if auto updates are on + SET_CONTROL_SELECTED(GetID(),CONTROL_AUTOUPDATE, CSettings::Get().GetInt("general.addonupdates") == AUTO_UPDATES_ON); + } + SET_CONTROL_SELECTED(GetID(),CONTROL_SHUTUP, CSettings::Get().GetBool("general.addonnotifications")); + SET_CONTROL_SELECTED(GetID(),CONTROL_FOREIGNFILTER, CSettings::Get().GetBool("general.addonforeignfilter")); + SET_CONTROL_SELECTED(GetID(),CONTROL_BROKENFILTER, CSettings::Get().GetBool("general.addonbrokenfilter")); + CGUIMediaWindow::UpdateButtons(); +} + +static bool FilterVar(bool valid, const CVariant& variant, + const std::string& check) +{ + if (!valid) + return false; + + if (variant.isNull() || variant.asString().empty()) + return false; + + std::string regions = variant.asString(); + return regions.find(check) == std::string::npos; +} + +bool CGUIWindowAddonBrowser::GetDirectory(const std::string& strDirectory, + CFileItemList& items) +{ + bool result; + if (URIUtils::PathEquals(strDirectory, "addons://downloading/")) + { + VECADDONS addons; + CAddonInstaller::Get().GetInstallList(addons); + + CURL url(strDirectory); + CAddonsDirectory::GenerateListing(url,addons,items); + result = true; + items.SetProperty("reponame",g_localizeStrings.Get(24067)); + items.SetPath(strDirectory); + + if (m_guiState.get() && !m_guiState->HideParentDirItems()) + { + CFileItemPtr pItem(new CFileItem("..")); + pItem->SetPath(m_history.GetParentPath()); + pItem->m_bIsFolder = true; + pItem->m_bIsShareOrDrive = false; + items.AddFront(pItem, 0); + } + + } + else + { + result = CGUIMediaWindow::GetDirectory(strDirectory,items); + if (CSettings::Get().GetBool("general.addonforeignfilter")) + { + int i=0; + while (i < items.Size()) + { + if (!FilterVar(true, items[i]->GetProperty("Addon.Language"), "en") || + !FilterVar(true, items[i]->GetProperty("Addon.Language"), g_langInfo.GetLanguageLocale())) + { + i++; + } + else + items.Remove(i); + } + } + if (CSettings::Get().GetBool("general.addonbrokenfilter")) + { + for (int i = items.Size() - 1; i >= 0; i--) + { + if (!items[i]->GetProperty("Addon.Broken").empty()) + { //check if it's installed + AddonPtr addon; + if (!CAddonMgr::Get().GetAddon(items[i]->GetProperty("Addon.ID").asString(), addon)) + items.Remove(i); + } + } + } + } + + if (strDirectory.empty() && CAddonInstaller::Get().IsDownloading()) + { + CFileItemPtr item(new CFileItem("addons://downloading/",true)); + item->SetLabel(g_localizeStrings.Get(24067)); + item->SetLabelPreformated(true); + item->SetIconImage("DefaultNetwork.png"); + items.Add(item); + } + + items.SetContent("addons"); + + for (int i=0;im_bIsFolder) return; + unsigned int percent; + if (CAddonInstaller::Get().GetProgress(item->GetProperty("Addon.ID").asString(), percent)) + { + std::string progress = StringUtils::Format(g_localizeStrings.Get(24042).c_str(), percent); + item->SetProperty("Addon.Status", progress); + item->SetProperty("Addon.Downloading", true); + } + else + item->ClearProperty("Addon.Downloading"); + item->SetLabel2(item->GetProperty("Addon.Status").asString()); + // to avoid the view state overriding label 2 + item->SetLabelPreformated(true); +} + +bool CGUIWindowAddonBrowser::Update(const std::string &strDirectory, bool updateFilterPath /* = true */) +{ + if (m_thumbLoader.IsLoading()) + m_thumbLoader.StopThread(); + + if (!CGUIMediaWindow::Update(strDirectory, updateFilterPath)) + return false; + + m_thumbLoader.Load(*m_vecItems); + + return true; +} + +int CGUIWindowAddonBrowser::SelectAddonID(TYPE type, std::string &addonID, bool showNone /* = false */, bool showDetails /* = true */, bool showInstalled /* = true */, bool showInstallable /*= false */, bool showMore /* = true */) +{ + vector types; + types.push_back(type); + return SelectAddonID(types, addonID, showNone, showDetails, showInstalled, showInstallable, showMore); +} + +int CGUIWindowAddonBrowser::SelectAddonID(ADDON::TYPE type, vector &addonIDs, bool showNone /* = false */, bool showDetails /* = true */, bool multipleSelection /* = true */, bool showInstalled /* = true */, bool showInstallable /* = false */, bool showMore /* = true */) +{ + vector types; + types.push_back(type); + return SelectAddonID(types, addonIDs, showNone, showDetails, multipleSelection, showInstalled, showInstallable, showMore); +} + +int CGUIWindowAddonBrowser::SelectAddonID(const vector &types, std::string &addonID, bool showNone /* = false */, bool showDetails /* = true */, bool showInstalled /* = true */, bool showInstallable /* = false */, bool showMore /* = true */) +{ + vector addonIDs; + if (!addonID.empty()) + addonIDs.push_back(addonID); + int retval = SelectAddonID(types, addonIDs, showNone, showDetails, false, showInstalled, showInstallable, showMore); + if (addonIDs.size() > 0) + addonID = addonIDs.at(0); + else + addonID = ""; + return retval; +} + +int CGUIWindowAddonBrowser::SelectAddonID(const vector &types, vector &addonIDs, bool showNone /* = false */, bool showDetails /* = true */, bool multipleSelection /* = true */, bool showInstalled /* = true */, bool showInstallable /* = false */, bool showMore /* = true */) +{ + // if we shouldn't show neither installed nor installable addons the list will be empty + if (!showInstalled && !showInstallable) + return 0; + + // can't show the "Get More" button if we already show installable addons + if (showInstallable) + showMore = false; + + CGUIDialogSelect *dialog = (CGUIDialogSelect*)g_windowManager.GetWindow(WINDOW_DIALOG_SELECT); + if (!dialog) + return 0; + + // get rid of any invalid addon types + vector validTypes(types.size()); + std::copy_if(types.begin(), types.end(), validTypes.begin(), [](ADDON::TYPE type) { return type != ADDON_UNKNOWN; }); + + if (validTypes.empty()) + return 0; + + // get all addons to show + VECADDONS addons; + if (showInstalled) + { + for (vector::const_iterator type = validTypes.begin(); type != validTypes.end(); ++type) + { + VECADDONS typeAddons; + if (*type == ADDON_AUDIO) + CAddonsDirectory::GetScriptsAndPlugins("audio", typeAddons); + else if (*type == ADDON_EXECUTABLE) + CAddonsDirectory::GetScriptsAndPlugins("executable", typeAddons); + else if (*type == ADDON_IMAGE) + CAddonsDirectory::GetScriptsAndPlugins("image", typeAddons); + else if (*type == ADDON_VIDEO) + CAddonsDirectory::GetScriptsAndPlugins("video", typeAddons); + else + CAddonMgr::Get().GetAddons(*type, typeAddons); + + addons.insert(addons.end(), typeAddons.begin(), typeAddons.end()); + } + } + + if (showInstallable || showMore) + { + VECADDONS installableAddons; + CAddonDatabase database; + if (database.Open() && database.GetAddons(installableAddons)) + { + for (ADDON::IVECADDONS addon = installableAddons.begin(); addon != installableAddons.end();) + { + AddonPtr pAddon = *addon; + + // check if the addon matches one of the provided addon types + bool matchesType = false; + for (vector::const_iterator type = validTypes.begin(); type != validTypes.end(); ++type) + { + if (pAddon->IsType(*type)) + { + matchesType = true; + break; + } + } + + // only show addons that match one of the provided addon types and that aren't disabled + if (matchesType && !CAddonMgr::Get().IsAddonDisabled(pAddon->ID())) + { + // check if the addon is installed + bool isInstalled = CAddonMgr::Get().IsAddonInstalled(pAddon->ID()); + + // check if the addon is installed or can be installed + if ((showInstallable || showMore) && !isInstalled && CAddonMgr::Get().CanAddonBeInstalled(pAddon)) + { + ++addon; + continue; + } + } + + addon = installableAddons.erase(addon); + } + + if (showInstallable) + addons.insert(addons.end(), installableAddons.begin(), installableAddons.end()); + else if (showMore) + showMore = !installableAddons.empty(); + } + } + + if (addons.empty() && !showNone) + return 0; + + // turn the addons into items + std::map addonMap; + CFileItemList items; + for (ADDON::IVECADDONS addon = addons.begin(); addon != addons.end(); ++addon) + { + CFileItemPtr item(CAddonsDirectory::FileItemFromAddon(*addon, "")); + if (!items.Contains(item->GetPath())) + { + items.Add(item); + addonMap.insert(std::make_pair(item->GetPath(), *addon)); + } + } + + if (items.IsEmpty() && !showNone) + return 0; + + std::string heading; + for (vector::const_iterator type = validTypes.begin(); type != validTypes.end(); ++type) + { + if (!heading.empty()) + heading += ", "; + heading += TranslateType(*type, true); + } + + dialog->SetHeading(heading); + dialog->Reset(); + dialog->SetUseDetails(showDetails); + + if (multipleSelection) + { + showNone = false; + showMore = false; + dialog->EnableButton(true, 186); + } + else if (showMore) + dialog->EnableButton(true, 21452); + + if (showNone) + { + CFileItemPtr item(new CFileItem("", false)); + item->SetLabel(g_localizeStrings.Get(231)); + item->SetLabel2(g_localizeStrings.Get(24040)); + item->SetIconImage("DefaultAddonNone.png"); + item->SetSpecialSort(SortSpecialOnTop); + items.Add(item); + } + items.Sort(SortByLabel, SortOrderAscending); + + if (addonIDs.size() > 0) + { + for (vector::const_iterator it = addonIDs.begin(); it != addonIDs.end() ; ++it) + { + CFileItemPtr item = items.Get(*it); + if (item) + item->Select(true); + } + } + dialog->SetItems(&items); + dialog->SetMultiSelection(multipleSelection); + dialog->DoModal(); + + // if the "Get More" button has been pressed and we haven't shown the + // installable addons so far show a list of installable addons + if (showMore&& dialog->IsButtonPressed()) + return SelectAddonID(types, addonIDs, showNone, showDetails, multipleSelection, false, true, false); + + if (!dialog->IsConfirmed()) + return 0; + + addonIDs.clear(); + const CFileItemList& list = dialog->GetSelectedItems(); + for (int i = 0 ; i < list.Size() ; i++) + { + const CFileItemPtr& item = list.Get(i); + + // check if one of the selected addons needs to be installed + if (showInstallable) + { + std::map::const_iterator itAddon = addonMap.find(item->GetPath()); + if (itAddon != addonMap.end()) + { + const AddonPtr& addon = itAddon->second; + + // if the addon isn't installed we need to install it + if (!CAddonMgr::Get().IsAddonInstalled(addon->ID())) + { + AddonPtr installedAddon; + if (!CAddonInstaller::Get().InstallModal(addon->ID(), installedAddon, false)) + continue; + } + + // if the addon is disabled we need to enable it + if (CAddonMgr::Get().IsAddonDisabled(addon->ID())) + CAddonMgr::Get().DisableAddon(addon->ID(), false); + } + } + + addonIDs.push_back(item->GetPath()); + } + return 1; +} + +std::string CGUIWindowAddonBrowser::GetStartFolder(const std::string &dir) +{ + if (URIUtils::PathStarts(dir, "addons://")) + return dir; + return CGUIMediaWindow::GetStartFolder(dir); +} diff --git a/xbmc/addons/GUIWindowAddonBrowser.h b/xbmc/addons/GUIWindowAddonBrowser.h new file mode 100644 index 0000000..d7c0ee0 --- /dev/null +++ b/xbmc/addons/GUIWindowAddonBrowser.h @@ -0,0 +1,79 @@ +#pragma once + +/* + * Copyright (C) 2005-2013 Team XBMC + * http://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, see + * . + * + */ + +#include "addons/Addon.h" +#include "windows/GUIMediaWindow.h" +#include "ThumbLoader.h" + +class CFileItem; +class CFileItemList; + +class CGUIWindowAddonBrowser : public CGUIMediaWindow +{ +public: + CGUIWindowAddonBrowser(void); + virtual ~CGUIWindowAddonBrowser(void); + virtual bool OnMessage(CGUIMessage& message); + + /*! \brief Popup a selection dialog with a list of addons of the given type + \param type the type of addon wanted + \param addonID [out] the addon ID of the selected item + \param showNone whether there should be a "None" item in the list (defaults to false) + \param showDetails whether to show details of the addons or not + \param showInstalled whether installed addons should be in the list + \param showInstallable whether installable addons should be in the list + \param showMore whether to show the "Get More" button (only makes sense if showInstalled is true and showInstallable is false) + \return 1 if an addon was selected, 2 if "Get More" was chosen, or 0 if an error occurred or if the selection process was cancelled + */ + static int SelectAddonID(ADDON::TYPE type, std::string &addonID, bool showNone = false, bool showDetails = true, bool showInstalled = true, bool showInstallable = false, bool showMore = true); + static int SelectAddonID(const std::vector &types, std::string &addonID, bool showNone = false, bool showDetails = true, bool showInstalled = true, bool showInstallable = false, bool showMore = true); + /*! \brief Popup a selection dialog with a list of addons of the given type + \param type the type of addon wanted + \param addonIDs [out] array of selected addon IDs + \param showNone whether there should be a "None" item in the list (defaults to false) + \param showDetails whether to show details of the addons or not + \param multipleSelection allow selection of multiple addons, if set to true showNone will automaticly switch to false + \param showInstalled whether installed addons should be in the list + \param showInstallable whether installable addons should be in the list + \param showMore whether to show the "Get More" button (only makes sense if showInstalled is true and showInstallable is false) + \return 1 if an addon was selected or multiple selection was specified, 2 if "Get More" was chosen, or 0 if an error occurred or if the selection process was cancelled + */ + static int SelectAddonID(ADDON::TYPE type, std::vector &addonIDs, bool showNone = false, bool showDetails = true, bool multipleSelection = true, bool showInstalled = true, bool showInstallable = false, bool showMore = true); + static int SelectAddonID(const std::vector &types, std::vector &addonIDs, bool showNone = false, bool showDetails = true, bool multipleSelection = true, bool showInstalled = true, bool showInstallable = false, bool showMore = true); + +protected: + /* \brief set label2 of an item based on the Addon.Status property + \param item the item to update + */ + void SetItemLabel2(CFileItemPtr item); + + virtual void GetContextButtons(int itemNumber, CContextButtons &buttons); + virtual bool OnContextButton(int itemNumber, CONTEXT_BUTTON button); + virtual bool OnClick(int iItem); + virtual void UpdateButtons(); + virtual bool GetDirectory(const std::string &strDirectory, CFileItemList &items); + virtual bool Update(const std::string &strDirectory, bool updateFilterPath = true); + virtual std::string GetStartFolder(const std::string &dir); +private: + CProgramThumbLoader m_thumbLoader; +}; + diff --git a/xbmc/addons/IAddon.h b/xbmc/addons/IAddon.h new file mode 100644 index 0000000..76f22fa --- /dev/null +++ b/xbmc/addons/IAddon.h @@ -0,0 +1,138 @@ +#pragma once +/* +* Copyright (C) 2005-2013 Team XBMC +* http://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, see +* . +* +*/ +#include + +#include +#include +#include +#include + +class TiXmlElement; + +namespace ADDON +{ + typedef enum + { + ADDON_UNKNOWN, + ADDON_VIZ, + ADDON_SKIN, + ADDON_PVRDLL, + ADDON_SCRIPT, + ADDON_SCRIPT_WEATHER, + ADDON_SUBTITLE_MODULE, + ADDON_SCRIPT_LYRICS, + ADDON_SCRAPER_ALBUMS, + ADDON_SCRAPER_ARTISTS, + ADDON_SCRAPER_MOVIES, + ADDON_SCRAPER_MUSICVIDEOS, + ADDON_SCRAPER_TVSHOWS, + ADDON_SCREENSAVER, + ADDON_PLUGIN, + ADDON_REPOSITORY, + ADDON_WEB_INTERFACE, + ADDON_SERVICE, + ADDON_AUDIOENCODER, + ADDON_CONTEXT_ITEM, + ADDON_VIDEO, // virtual addon types + ADDON_AUDIO, + ADDON_IMAGE, + ADDON_EXECUTABLE, + ADDON_VIZ_LIBRARY, + ADDON_SCRAPER_LIBRARY, + ADDON_SCRIPT_LIBRARY, + ADDON_SCRIPT_MODULE, + ADDON_MAX + } TYPE; + + class IAddon; + typedef std::shared_ptr AddonPtr; + class CVisualisation; + typedef std::shared_ptr VizPtr; + class CSkinInfo; + typedef std::shared_ptr SkinPtr; + class CPluginSource; + typedef std::shared_ptr PluginPtr; + + class CAddonMgr; + class AddonVersion; + typedef std::map > ADDONDEPS; + typedef std::map InfoMap; + class AddonProps; + + class IAddon : public std::enable_shared_from_this + { + public: + virtual ~IAddon() {}; + virtual AddonPtr Clone() const =0; + virtual TYPE Type() const =0; + virtual bool IsType(TYPE type) const =0; + virtual AddonProps Props() const =0; + virtual AddonProps& Props() =0; + virtual const std::string ID() const =0; + virtual const std::string Name() const =0; + virtual bool Enabled() const =0; + virtual bool IsInUse() const =0; + virtual const AddonVersion Version() const =0; + virtual const AddonVersion MinVersion() const =0; + virtual const std::string Summary() const =0; + virtual const std::string Description() const =0; + virtual const std::string Path() const =0; + virtual const std::string Profile() const =0; + virtual const std::string LibPath() const =0; + virtual const std::string ChangeLog() const =0; + virtual const std::string FanArt() const =0; + virtual const std::string Author() const =0; + virtual const std::string Icon() const =0; + virtual int Stars() const =0; + virtual const std::string Disclaimer() const =0; + virtual const InfoMap &ExtraInfo() const =0; + virtual bool HasSettings() =0; + virtual void SaveSettings() =0; + virtual void UpdateSetting(const std::string& key, const std::string& value) =0; + virtual std::string GetSetting(const std::string& key) =0; + virtual TiXmlElement* GetSettingsXML() =0; + virtual std::string GetString(uint32_t id) =0; + virtual const ADDONDEPS &GetDeps() const =0; + virtual AddonVersion GetDependencyVersion(const std::string &dependencyID) const =0; + virtual bool MeetsVersion(const AddonVersion &version) const =0; + virtual bool ReloadSettings() =0; + virtual void OnDisabled() =0; + virtual void OnEnabled() =0; + virtual AddonPtr GetRunningInstance() const=0; + virtual bool OnPreInstall() =0; + virtual void OnPostInstall(bool restart, bool update, bool modal) =0; + virtual void OnPreUnInstall() =0; + virtual void OnPostUnInstall() =0; + virtual bool CanInstall(const std::string& referer) =0; + + protected: + virtual bool LoadSettings(bool bForce = false) =0; + + private: + friend class CAddonMgr; + virtual bool IsAddonLibrary() =0; + virtual void Enable() =0; + virtual void Disable() =0; + virtual bool LoadStrings() =0; + virtual void ClearStrings() =0; + }; +}; + diff --git a/xbmc/addons/Makefile b/xbmc/addons/Makefile new file mode 100644 index 0000000..4cf5b17 --- /dev/null +++ b/xbmc/addons/Makefile @@ -0,0 +1,31 @@ +SRCS=Addon.cpp \ + AddonCallbacks.cpp \ + AddonCallbacksAddon.cpp \ + AddonCallbacksCodec.cpp \ + AddonCallbacksGUI.cpp \ + AddonCallbacksPVR.cpp \ + AddonDatabase.cpp \ + AddonInstaller.cpp \ + AddonManager.cpp \ + AddonStatusHandler.cpp \ + AddonVersion.cpp \ + AudioEncoder.cpp \ + ContextItemAddon.cpp \ + GUIDialogAddonInfo.cpp \ + GUIDialogAddonSettings.cpp \ + GUIViewStateAddonBrowser.cpp \ + GUIWindowAddonBrowser.cpp \ + PluginSource.cpp \ + Repository.cpp \ + Scraper.cpp \ + ScreenSaver.cpp \ + Service.cpp \ + Skin.cpp \ + Visualisation.cpp \ + Webinterface.cpp \ + +LIB=addons.a + +include ../../Makefile.include +-include $(patsubst %.cpp,%.P,$(patsubst %.c,%.P,$(SRCS))) + diff --git a/xbmc/addons/PluginSource.cpp b/xbmc/addons/PluginSource.cpp new file mode 100644 index 0000000..a5729cf --- /dev/null +++ b/xbmc/addons/PluginSource.cpp @@ -0,0 +1,96 @@ +/* + * Copyright (C) 2005-2013 Team XBMC + * http://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, see + * . + * + */ +#include "PluginSource.h" +#include "AddonManager.h" +#include "utils/StringUtils.h" + +using namespace std; + +namespace ADDON +{ + +CPluginSource::CPluginSource(const AddonProps &props) + : CAddon(props) +{ + std::string provides; + InfoMap::const_iterator i = Props().extrainfo.find("provides"); + if (i != Props().extrainfo.end()) + provides = i->second; + SetProvides(provides); +} + +CPluginSource::CPluginSource(const cp_extension_t *ext) + : CAddon(ext) +{ + std::string provides; + if (ext) + { + provides = CAddonMgr::Get().GetExtValue(ext->configuration, "provides"); + if (!provides.empty()) + Props().extrainfo.insert(make_pair("provides", provides)); + } + SetProvides(provides); +} + +AddonPtr CPluginSource::Clone() const +{ + return AddonPtr(new CPluginSource(*this)); +} + +void CPluginSource::SetProvides(const std::string &content) +{ + if (!content.empty()) + { + vector provides = StringUtils::Split(content, ' '); + for (vector::const_iterator i = provides.begin(); i != provides.end(); ++i) + { + Content content = Translate(*i); + if (content != UNKNOWN) + m_providedContent.insert(content); + } + } + if (Type() == ADDON_SCRIPT && m_providedContent.empty()) + m_providedContent.insert(EXECUTABLE); +} + +CPluginSource::Content CPluginSource::Translate(const std::string &content) +{ + if (content == "audio") + return CPluginSource::AUDIO; + else if (content == "image") + return CPluginSource::IMAGE; + else if (content == "executable") + return CPluginSource::EXECUTABLE; + else if (content == "video") + return CPluginSource::VIDEO; + else + return CPluginSource::UNKNOWN; +} + +bool CPluginSource::IsType(TYPE type) const +{ + return ((type == ADDON_VIDEO && Provides(VIDEO)) + || (type == ADDON_AUDIO && Provides(AUDIO)) + || (type == ADDON_IMAGE && Provides(IMAGE)) + || (type == ADDON_EXECUTABLE && Provides(EXECUTABLE))); +} + +} /*namespace ADDON*/ + diff --git a/xbmc/addons/PluginSource.h b/xbmc/addons/PluginSource.h new file mode 100644 index 0000000..87e1f34 --- /dev/null +++ b/xbmc/addons/PluginSource.h @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2005-2013 Team XBMC + * http://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, see + * . + * + */ +#pragma once + +#include "Addon.h" + +namespace ADDON +{ + +class CPluginSource : public CAddon +{ +public: + + enum Content { UNKNOWN, AUDIO, IMAGE, EXECUTABLE, VIDEO }; + + CPluginSource(const cp_extension_t *ext); + CPluginSource(const AddonProps &props); + virtual ~CPluginSource() {} + virtual AddonPtr Clone() const; + virtual bool IsType(TYPE type) const; + bool Provides(const Content& content) const + { + return content == UNKNOWN ? false : m_providedContent.count(content) > 0; + } + + bool ProvidesSeveral() const + { + return m_providedContent.size() > 1; + } + + static Content Translate(const std::string &content); +private: + /*! \brief Set the provided content for this plugin + If no valid content types are passed in, we set the EXECUTABLE type + \param content a space-separated list of content types + */ + void SetProvides(const std::string &content); + std::set m_providedContent; +}; + +} /*namespace ADDON*/ diff --git a/xbmc/addons/Repository.cpp b/xbmc/addons/Repository.cpp new file mode 100644 index 0000000..e65a195 --- /dev/null +++ b/xbmc/addons/Repository.cpp @@ -0,0 +1,395 @@ +/* + * Copyright (C) 2005-2013 Team XBMC + * http://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, see + * . + * + */ + +#include "Repository.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 "filesystem/PluginDirectory.h" +#include "settings/Settings.h" +#include "utils/log.h" +#include "utils/JobManager.h" +#include "utils/StringUtils.h" +#include "utils/URIUtils.h" +#include "utils/XBMCTinyXML.h" +#include "FileItem.h" +#include "TextureDatabase.h" +#include "URL.h" + +using namespace std; +using namespace XFILE; +using namespace ADDON; + +AddonPtr CRepository::Clone() const +{ + return AddonPtr(new CRepository(*this)); +} + +CRepository::CRepository(const AddonProps& props) : + CAddon(props) +{ +} + +CRepository::CRepository(const cp_extension_t *ext) + : CAddon(ext) +{ + // read in the other props that we need + if (ext) + { + 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") == "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") == "true"; + dir.hashes = CAddonMgr::Get().GetExtValue(&ext->configuration->children[i], "hashes") == "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") == "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") == "true"; + info.hashes = CAddonMgr::Get().GetExtValue(ext->configuration, "hashes") == "true"; + m_dirs.push_back(info); + } + } +} + +CRepository::CRepository(const CRepository &rhs) + : CAddon(rhs), m_dirs(rhs.m_dirs) +{ +} + +CRepository::~CRepository() +{ +} + +string CRepository::FetchChecksum(const string& url) +{ + CFile file; + try + { + if (file.Open(url)) + { + // we intentionally avoid using file.GetLength() for + // Transfer-Encoding: chunked servers. + std::stringstream str; + char temp[1024]; + int read; + while ((read=file.Read(temp, sizeof(temp))) > 0) + str.write(temp, read); + return str.str(); + } + return ""; + } + catch (...) + { + return ""; + } +} + +string CRepository::GetAddonHash(const AddonPtr& addon) const +{ + string checksum; + DirList::const_iterator it; + for (it = m_dirs.begin();it != m_dirs.end(); ++it) + if (URIUtils::IsInPath(addon->Path(), it->datadir)) + break; + if (it != m_dirs.end() && it->hashes) + { + checksum = FetchChecksum(addon->Path()+".md5"); + size_t pos = checksum.find_first_of(" \n"); + if (pos != string::npos) + return checksum.substr(0, pos); + } + return checksum; +} + +#define SET_IF_NOT_EMPTY(x,y) \ + { \ + if (!x.empty()) \ + x = y; \ + } + +bool CRepository::Parse(const DirInfo& dir, VECADDONS &result) +{ + string file = dir.info; + if (dir.compressed) + { + CURL url(dir.info); + string opts = url.GetProtocolOptions(); + if (!opts.empty()) + opts += "&"; + url.SetProtocolOptions(opts+"Encoding=gzip"); + file = url.Get(); + } + + CXBMCTinyXML doc; + if (doc.LoadFile(file) && doc.RootElement() && + CAddonMgr::Get().AddonsFromRepoXML(doc.RootElement(), result)) + { + for (IVECADDONS i = result.begin(); i != result.end(); ++i) + { + AddonPtr addon = *i; + if (dir.zipped) + { + string file = StringUtils::Format("%s/%s-%s.zip", addon->ID().c_str(), addon->ID().c_str(), addon->Version().asString().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().asString().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(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")) + } + } + return true; + } + return false; +} + +void CRepository::OnPostInstall(bool restart, bool update, bool modal) +{ + VECADDONS addons; + AddonPtr repo(new CRepository(*this)); + addons.push_back(repo); + CJobManager::GetInstance().AddJob(new CRepositoryUpdateJob(addons), &CAddonInstaller::Get()); +} + +void CRepository::OnPostUnInstall() +{ + CAddonDatabase database; + database.Open(); + database.DeleteRepository(ID()); +} + +CRepositoryUpdateJob::CRepositoryUpdateJob(const VECADDONS &repos) + : m_repos(repos) +{ +} + +void MergeAddons(map &addons, const VECADDONS &new_addons) +{ + for (VECADDONS::const_iterator it = new_addons.begin(); it != new_addons.end(); ++it) + { + map::iterator existing = addons.find((*it)->ID()); + if (existing != addons.end()) + { // already got it - replace if we have a newer version + if (existing->second->Version() < (*it)->Version()) + existing->second = *it; + } + else + addons.insert(make_pair((*it)->ID(), *it)); + } +} + +bool CRepositoryUpdateJob::DoWork() +{ + map addons; + for (VECADDONS::const_iterator i = m_repos.begin(); i != m_repos.end(); ++i) + { + if (ShouldCancel(0, 0)) + return false; + const RepositoryPtr repo = std::dynamic_pointer_cast(*i); + VECADDONS newAddons; + if (GrabAddons(repo, newAddons)) + MergeAddons(addons, newAddons); + } + if (addons.empty()) + return true; //Nothing to do + + // check for updates + CAddonDatabase database; + database.Open(); + database.BeginMultipleExecute(); + + CTextureDatabase textureDB; + textureDB.Open(); + textureDB.BeginMultipleExecute(); + VECADDONS notifications; + for (map::const_iterator i = addons.begin(); i != addons.end(); ++i) + { + // manager told us to feck off + if (ShouldCancel(0,0)) + break; + + AddonPtr newAddon = i->second; + bool deps_met = CAddonInstaller::Get().CheckDependencies(newAddon, &database); + if (!deps_met && newAddon->Props().broken.empty()) + newAddon->Props().broken = "DEPSNOTMET"; + + // invalidate the art associated with this item + if (!newAddon->Props().fanart.empty()) + textureDB.InvalidateCachedTexture(newAddon->Props().fanart); + if (!newAddon->Props().icon.empty()) + textureDB.InvalidateCachedTexture(newAddon->Props().icon); + + AddonPtr addon; + CAddonMgr::Get().GetAddon(newAddon->ID(),addon); + if (addon && newAddon->Version() > addon->Version() && + !database.IsAddonBlacklisted(newAddon->ID(),newAddon->Version().asString()) && + deps_met) + { + if (CSettings::Get().GetInt("general.addonupdates") == AUTO_UPDATES_ON) + { + string referer; + if (URIUtils::IsInternetStream(newAddon->Path())) + referer = StringUtils::Format("Referer=%s-%s.zip",addon->ID().c_str(),addon->Version().asString().c_str()); + + if (newAddon->CanInstall(referer)) + CAddonInstaller::Get().Install(addon->ID(), true, referer); + } + else + notifications.push_back(addon); + } + + // Check if we should mark the add-on as broken. We may have a newer version + // of this add-on in the database or installed - if so, we keep it unbroken. + bool haveNewer = (addon && addon->Version() > newAddon->Version()) || + database.GetAddonVersion(newAddon->ID()) > newAddon->Version(); + if (!haveNewer) + { + if (!newAddon->Props().broken.empty()) + { + if (database.IsAddonBroken(newAddon->ID()).empty()) + { + std::string line = g_localizeStrings.Get(24096); + if (newAddon->Props().broken == "DEPSNOTMET") + line = g_localizeStrings.Get(24104); + if (addon && CGUIDialogYesNo::ShowAndGetInput(newAddon->Name(), + line, + g_localizeStrings.Get(24097), + "")) + CAddonMgr::Get().DisableAddon(newAddon->ID()); + } + } + database.BreakAddon(newAddon->ID(), newAddon->Props().broken); + } + } + database.CommitMultipleExecute(); + textureDB.CommitMultipleExecute(); + if (!notifications.empty() && CSettings::Get().GetBool("general.addonnotifications")) + { + if (notifications.size() == 1) + CGUIDialogKaiToast::QueueNotification(notifications[0]->Icon(), + g_localizeStrings.Get(24061), + notifications[0]->Name(),TOAST_DISPLAY_TIME,false,TOAST_DISPLAY_TIME); + else + CGUIDialogKaiToast::QueueNotification("", + g_localizeStrings.Get(24001), + g_localizeStrings.Get(24061),TOAST_DISPLAY_TIME,false,TOAST_DISPLAY_TIME); + } + + return true; +} + +bool CRepositoryUpdateJob::GrabAddons(const RepositoryPtr& repo, VECADDONS& addons) +{ + CAddonDatabase database; + database.Open(); + string oldReposum; + if (!database.GetRepoChecksum(repo->ID(), oldReposum)) + oldReposum = ""; + + string reposum; + for (CRepository::DirList::const_iterator it = repo->m_dirs.begin(); it != repo->m_dirs.end(); ++it) + { + if (ShouldCancel(0, 0)) + return false; + if (!it->checksum.empty()) + { + const string dirsum = CRepository::FetchChecksum(it->checksum); + if (dirsum.empty()) + { + CLog::Log(LOGERROR, "Failed to fetch checksum for directory listing %s for repository %s. ", (*it).info.c_str(), repo->ID().c_str()); + return false; + } + reposum += dirsum; + } + } + + if (oldReposum != reposum || oldReposum.empty()) + { + map uniqueAddons; + for (CRepository::DirList::const_iterator it = repo->m_dirs.begin(); it != repo->m_dirs.end(); ++it) + { + if (ShouldCancel(0, 0)) + return false; + VECADDONS addons; + if (!CRepository::Parse(*it, addons)) + { //TODO: Hash is invalid and should not be saved, but should we fail? + //We can still report a partial addon listing. + CLog::Log(LOGERROR, "Failed to read directory listing %s for repository %s. ", (*it).info.c_str(), repo->ID().c_str()); + return false; + } + MergeAddons(uniqueAddons, addons); + } + + bool add = true; + if (!repo->Props().libname.empty()) + { + CFileItemList dummy; + string s = StringUtils::Format("plugin://%s/?action=update", repo->ID().c_str()); + add = CDirectory::GetDirectory(s, dummy); + } + if (add) + { + for (map::const_iterator i = uniqueAddons.begin(); i != uniqueAddons.end(); ++i) + addons.push_back(i->second); + database.AddRepository(repo->ID(), addons, reposum, repo->Version()); + } + } + else + { + CLog::Log(LOGDEBUG, "Checksum for repository %s not changed.", repo->ID().c_str()); + database.GetRepository(repo->ID(), addons); + database.SetRepoTimestamp(repo->ID(), CDateTime::GetCurrentDateTime().GetAsDBDateTime(), repo->Version()); + } + return true; +} + diff --git a/xbmc/addons/Repository.h b/xbmc/addons/Repository.h new file mode 100644 index 0000000..5120c38 --- /dev/null +++ b/xbmc/addons/Repository.h @@ -0,0 +1,82 @@ +#pragma once +/* + * Copyright (C) 2005-2013 Team XBMC + * http://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, see + * . + * + */ + +#include "Addon.h" +#include "utils/Job.h" + +namespace ADDON +{ + class CRepository; + typedef std::shared_ptr RepositoryPtr; + class CRepository : public CAddon + { + public: + virtual AddonPtr Clone() const; + CRepository(const AddonProps& props); + CRepository(const cp_extension_t *props); + virtual ~CRepository(); + + /*! \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. + */ + 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 DirList; + DirList m_dirs; + + static bool Parse(const DirInfo& dir, VECADDONS& addons); + static std::string FetchChecksum(const std::string& url); + + virtual void OnPostInstall(bool restart, bool update, bool modal); + virtual void OnPostUnInstall(); + + private: + CRepository(const CRepository &rhs); + }; + + class CRepositoryUpdateJob : public CJob + { + public: + CRepositoryUpdateJob(const VECADDONS& repos); + virtual ~CRepositoryUpdateJob() {} + + virtual const char *GetType() const { return "repoupdate"; }; + virtual bool DoWork(); + private: + bool GrabAddons(const RepositoryPtr& repo, VECADDONS& addons); + + VECADDONS m_repos; + }; +} + diff --git a/xbmc/addons/Scraper.cpp b/xbmc/addons/Scraper.cpp new file mode 100644 index 0000000..06f34f2 --- /dev/null +++ b/xbmc/addons/Scraper.cpp @@ -0,0 +1,1033 @@ +/* +* Copyright (C) 2005-2013 Team XBMC +* http://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, see +* . +* +*/ +#include "Scraper.h" +#include "filesystem/File.h" +#include "filesystem/Directory.h" +#include "filesystem/CurlFile.h" +#include "AddonManager.h" +#include "utils/ScraperParser.h" +#include "utils/ScraperUrl.h" +#include "utils/CharsetConverter.h" +#include "utils/log.h" +#include "music/infoscanner/MusicAlbumInfo.h" +#include "music/infoscanner/MusicArtistInfo.h" +#include "utils/fstrcmp.h" +#include "settings/AdvancedSettings.h" +#include "FileItem.h" +#include "utils/URIUtils.h" +#include "utils/XMLUtils.h" +#include "utils/StringUtils.h" +#include "music/MusicDatabase.h" +#include "video/VideoDatabase.h" +#include "music/Album.h" +#include "music/Artist.h" +#include "Util.h" +#include "URL.h" + +#include +#include + +using namespace std; +using namespace XFILE; +using namespace MUSIC_GRABBER; +using namespace VIDEO; + +namespace ADDON +{ + +typedef struct +{ + const char* name; + CONTENT_TYPE type; + int pretty; +} ContentMapping; + +static const ContentMapping content[] = + {{"unknown", CONTENT_NONE, 231 }, + {"albums", CONTENT_ALBUMS, 132 }, + {"music", CONTENT_ALBUMS, 132 }, + {"artists", CONTENT_ARTISTS, 133 }, + {"movies", CONTENT_MOVIES, 20342 }, + {"tvshows", CONTENT_TVSHOWS, 20343 }, + {"musicvideos", CONTENT_MUSICVIDEOS, 20389 }}; + +std::string TranslateContent(const CONTENT_TYPE &type, bool pretty/*=false*/) +{ + for (unsigned int index=0; index < ARRAY_SIZE(content); ++index) + { + const ContentMapping &map = content[index]; + if (type == map.type) + { + if (pretty && map.pretty) + return g_localizeStrings.Get(map.pretty); + else + return map.name; + } + } + return ""; +} + +CONTENT_TYPE TranslateContent(const std::string &string) +{ + for (unsigned int index=0; index < ARRAY_SIZE(content); ++index) + { + const ContentMapping &map = content[index]; + if (string == map.name) + return map.type; + } + return CONTENT_NONE; +} + +TYPE ScraperTypeFromContent(const CONTENT_TYPE &content) +{ + switch (content) + { + case CONTENT_ALBUMS: + return ADDON_SCRAPER_ALBUMS; + case CONTENT_ARTISTS: + return ADDON_SCRAPER_ARTISTS; + case CONTENT_MOVIES: + return ADDON_SCRAPER_MOVIES; + case CONTENT_MUSICVIDEOS: + return ADDON_SCRAPER_MUSICVIDEOS; + case CONTENT_TVSHOWS: + return ADDON_SCRAPER_TVSHOWS; + default: + return ADDON_UNKNOWN; + } +} + +// if the XML root is , throw CScraperError with enclosed /<message> values +static void CheckScraperError(const TiXmlElement *pxeRoot) +{ + if (!pxeRoot || stricmp(pxeRoot->Value(), "error")) + return; + std::string sTitle; + std::string sMessage; + XMLUtils::GetString(pxeRoot, "title", sTitle); + XMLUtils::GetString(pxeRoot, "message", sMessage); + throw CScraperError(sTitle, sMessage); +} + +CScraper::CScraper(const cp_extension_t *ext) : CAddon(ext), m_fLoaded(false) +{ + if (ext) + { + m_language = CAddonMgr::Get().GetExtValue(ext->configuration, "@language"); + m_requiressettings = CAddonMgr::Get().GetExtValue(ext->configuration,"@requiressettings") == "true"; + std::string persistence = CAddonMgr::Get().GetExtValue(ext->configuration, "@cachepersistence"); + if (!persistence.empty()) + m_persistence.SetFromTimeString(persistence); + } + switch (Type()) + { + case ADDON_SCRAPER_ALBUMS: + m_pathContent = CONTENT_ALBUMS; + break; + case ADDON_SCRAPER_ARTISTS: + m_pathContent = CONTENT_ARTISTS; + break; + case ADDON_SCRAPER_MOVIES: + m_pathContent = CONTENT_MOVIES; + break; + case ADDON_SCRAPER_MUSICVIDEOS: + m_pathContent = CONTENT_MUSICVIDEOS; + break; + case ADDON_SCRAPER_TVSHOWS: + m_pathContent = CONTENT_TVSHOWS; + break; + default: + m_pathContent = CONTENT_NONE; + break; + } +} + +AddonPtr CScraper::Clone() const +{ + return AddonPtr(new CScraper(*this)); +} + +CScraper::CScraper(const CScraper &rhs) + : CAddon(rhs), m_fLoaded(false), + m_language(rhs.m_language), + m_requiressettings(rhs.m_requiressettings), + m_persistence(rhs.m_persistence), + m_pathContent(rhs.m_pathContent) +{ +} + +bool CScraper::Supports(const CONTENT_TYPE &content) const +{ + return Type() == ScraperTypeFromContent(content); +} + +bool CScraper::SetPathSettings(CONTENT_TYPE content, const std::string& xml) +{ + m_pathContent = content; + if (!LoadSettings()) + return false; + + if (xml.empty()) + return true; + + CXBMCTinyXML doc; + doc.Parse(xml); + m_userSettingsLoaded = SettingsFromXML(doc); + + return m_userSettingsLoaded; +} + +std::string CScraper::GetPathSettings() +{ + if (!LoadSettings()) + return ""; + + stringstream stream; + CXBMCTinyXML doc; + SettingsToXML(doc); + if (doc.RootElement()) + stream << *doc.RootElement(); + + return stream.str(); +} + +void CScraper::ClearCache() +{ + std::string strCachePath = URIUtils::AddFileToFolder(g_advancedSettings.m_cachePath, "scrapers"); + + // create scraper cache dir if needed + if (!CDirectory::Exists(strCachePath)) + CDirectory::Create(strCachePath); + + strCachePath = URIUtils::AddFileToFolder(strCachePath, ID()); + URIUtils::AddSlashAtEnd(strCachePath); + + if (CDirectory::Exists(strCachePath)) + { + CFileItemList items; + CDirectory::GetDirectory(strCachePath,items); + for (int i=0;i<items.Size();++i) + { + // wipe cache + if (items[i]->m_dateTime + m_persistence <= CDateTime::GetCurrentDateTime()) + CFile::Delete(items[i]->GetPath()); + } + } + else + CDirectory::Create(strCachePath); +} + +// returns a vector of strings: the first is the XML output by the function; the rest +// is XML output by chained functions, possibly recursively +// the CCurlFile object is passed in so that URL fetches can be canceled from other threads +// throws CScraperError abort on internal failures (e.g., parse errors) +vector<string> CScraper::Run(const std::string& function, + const CScraperUrl& scrURL, + CCurlFile& http, + const vector<string>* extras) +{ + if (!Load()) + throw CScraperError(); + + std::string strXML = InternalRun(function,scrURL,http,extras); + if (strXML.empty()) + { + if (function != "NfoUrl" && function != "ResolveIDToUrl") + CLog::Log(LOGERROR, "%s: Unable to parse web site",__FUNCTION__); + throw CScraperError(); + } + + CLog::Log(LOGDEBUG,"scraper: %s returned %s",function.c_str(),strXML.c_str()); + + CXBMCTinyXML doc; + /* all data was converted to UTF-8 before being processed by scraper */ + doc.Parse(strXML, TIXML_ENCODING_UTF8); + if (!doc.RootElement()) + { + CLog::Log(LOGERROR, "%s: Unable to parse XML",__FUNCTION__); + throw CScraperError(); + } + + vector<string> result; + result.push_back(strXML); + TiXmlElement* xchain = doc.RootElement()->FirstChildElement(); + // skip children of the root element until <url> or <chain> + while (xchain && strcmp(xchain->Value(),"url") && strcmp(xchain->Value(),"chain")) + xchain = xchain->NextSiblingElement(); + while (xchain) + { + // <chain|url function="...">param</> + const char* szFunction = xchain->Attribute("function"); + if (szFunction) + { + CScraperUrl scrURL2; + vector<string> extras; + // for <chain>, pass the contained text as a parameter; for <url>, as URL content + if (strcmp(xchain->Value(),"chain")==0) + { + if (xchain->FirstChild()) + extras.push_back(xchain->FirstChild()->Value()); + } + else + scrURL2.ParseElement(xchain); + // Fix for empty chains. $$1 would still contain the + // previous value as there is no child of the xml node. + // since $$1 will always either contain the data from an + // url or the parameters to a chain, we can safely clear it here + // to fix this issue + m_parser.m_param[0].clear(); + vector<string> result2 = RunNoThrow(szFunction,scrURL2,http,&extras); + result.insert(result.end(),result2.begin(),result2.end()); + } + xchain = xchain->NextSiblingElement(); + // continue to skip past non-<url> or <chain> elements + while (xchain && strcmp(xchain->Value(),"url") && strcmp(xchain->Value(),"chain")) + xchain = xchain->NextSiblingElement(); + } + + return result; +} + +// just like Run, but returns an empty list instead of throwing in case of error +// don't use in new code; errors should be handled appropriately +vector<string> CScraper::RunNoThrow(const std::string& function, + const CScraperUrl& url, + XFILE::CCurlFile& http, + const vector<string>* extras) +{ + vector<string> vcs; + try + { + vcs = Run(function, url, http, extras); + } + catch (const CScraperError &sce) + { + assert(sce.FAborted()); // the only kind we should get + } + return vcs; +} + +std::string CScraper::InternalRun(const std::string& function, + const CScraperUrl& scrURL, + CCurlFile& http, + const vector<string>* extras) +{ + // walk the list of input URLs and fetch each into parser parameters + unsigned int i; + for (i=0;i<scrURL.m_url.size();++i) + { + if (!CScraperUrl::Get(scrURL.m_url[i],m_parser.m_param[i],http,ID()) || m_parser.m_param[i].size() == 0) + return ""; + } + // put the 'extra' parameterts into the parser parameter list too + if (extras) + { + for (unsigned int j=0;j<extras->size();++j) + m_parser.m_param[j+i] = (*extras)[j]; + } + + return m_parser.Parse(function,this); +} + +bool CScraper::Load() +{ + if (m_fLoaded) + return true; + + bool result=m_parser.Load(LibPath()); + if (result) + { + // TODO: this routine assumes that deps are a single level, and assumes the dep is installed. + // 1. Does it make sense to have recursive dependencies? + // 2. Should we be checking the dep versions or do we assume it is ok? + ADDONDEPS deps = GetDeps(); + ADDONDEPS::iterator itr = deps.begin(); + while (itr != deps.end()) + { + if (itr->first == "xbmc.metadata") + { + ++itr; + continue; + } + AddonPtr dep; + + bool bOptional = itr->second.second; + + if (CAddonMgr::Get().GetAddon((*itr).first, dep)) + { + CXBMCTinyXML doc; + if (dep->Type() == ADDON_SCRAPER_LIBRARY && doc.LoadFile(dep->LibPath())) + m_parser.AddDocument(&doc); + } + else + { + if (!bOptional) + { + result = false; + break; + } + } + ++itr; + } + } + + if (!result) + CLog::Log(LOGWARNING, "failed to load scraper XML from %s", LibPath().c_str()); + return m_fLoaded = result; +} + +bool CScraper::IsInUse() const +{ + if (Supports(CONTENT_ALBUMS) || Supports(CONTENT_ARTISTS)) + { // music scraper + CMusicDatabase db; + if (db.Open() && db.ScraperInUse(ID())) + return true; + } + else + { // video scraper + CVideoDatabase db; + if (db.Open() && db.ScraperInUse(ID())) + return true; + } + return false; +} + +bool CScraper::IsNoop() +{ + if (!Load()) + throw CScraperError(); + + return m_parser.IsNoop(); +} + +// pass in contents of .nfo file; returns URL (possibly empty if none found) +// and may populate strId, or throws CScraperError on error +CScraperUrl CScraper::NfoUrl(const std::string &sNfoContent) +{ + CScraperUrl scurlRet; + + if (IsNoop()) + return scurlRet; + + // scraper function takes contents of .nfo file, returns XML (see below) + vector<string> vcsIn; + vcsIn.push_back(sNfoContent); + CScraperUrl scurl; + CCurlFile fcurl; + vector<string> vcsOut = Run("NfoUrl", scurl, fcurl, &vcsIn); + if (vcsOut.empty() || vcsOut[0].empty()) + return scurlRet; + if (vcsOut.size() > 1) + CLog::Log(LOGWARNING, "%s: scraper returned multiple results; using first", __FUNCTION__); + + // parse returned XML: either <error> element on error, blank on failure, + // or <url>...</url> or <url>...</url><id>...</id> on success + for (unsigned int i=0; i < vcsOut.size(); ++i) + { + CXBMCTinyXML doc; + doc.Parse(vcsOut[i], TIXML_ENCODING_UTF8); + CheckScraperError(doc.RootElement()); + + if (doc.RootElement()) + { + /* + NOTE: Scrapers might return invalid xml with some loose + elements (eg. '<url>http://some.url</url><id>123</id>'). + Since XMLUtils::GetString() is assuming well formed xml + with start and end-tags we're not able to use it. + Check for the desired Elements instead. + */ + TiXmlElement* pxeUrl=NULL; + TiXmlElement* pId=NULL; + if (!strcmp(doc.RootElement()->Value(),"details")) + { + pxeUrl = doc.RootElement()->FirstChildElement("url"); + pId = doc.RootElement()->FirstChildElement("id"); + } + else + { + pId = doc.FirstChildElement("id"); + pxeUrl = doc.FirstChildElement("url"); + } + if (pId && pId->FirstChild()) + scurlRet.strId = pId->FirstChild()->Value(); + + if (pxeUrl && pxeUrl->Attribute("function")) + continue; + + if (pxeUrl) + scurlRet.ParseElement(pxeUrl); + else if (!strcmp(doc.RootElement()->Value(), "url")) + scurlRet.ParseElement(doc.RootElement()); + else + continue; + break; + } + } + return scurlRet; +} + +CScraperUrl CScraper::ResolveIDToUrl(const std::string& externalID) +{ + CScraperUrl scurlRet; + + // scraper function takes an external ID, returns XML (see below) + vector<string> vcsIn; + vcsIn.push_back(externalID); + CScraperUrl scurl; + CCurlFile fcurl; + vector<string> vcsOut = Run("ResolveIDToUrl", scurl, fcurl, &vcsIn); + if (vcsOut.empty() || vcsOut[0].empty()) + return scurlRet; + if (vcsOut.size() > 1) + CLog::Log(LOGWARNING, "%s: scraper returned multiple results; using first", __FUNCTION__); + + // parse returned XML: either <error> element on error, blank on failure, + // or <url>...</url> or <url>...</url><id>...</id> on success + for (unsigned int i=0; i < vcsOut.size(); ++i) + { + CXBMCTinyXML doc; + doc.Parse(vcsOut[i], TIXML_ENCODING_UTF8); + CheckScraperError(doc.RootElement()); + + if (doc.RootElement()) + { + /* + NOTE: Scrapers might return invalid xml with some loose + elements (eg. '<url>http://some.url</url><id>123</id>'). + Since XMLUtils::GetString() is assuming well formed xml + with start and end-tags we're not able to use it. + Check for the desired Elements instead. + */ + TiXmlElement* pxeUrl=NULL; + TiXmlElement* pId=NULL; + if (!strcmp(doc.RootElement()->Value(),"details")) + { + pxeUrl = doc.RootElement()->FirstChildElement("url"); + pId = doc.RootElement()->FirstChildElement("id"); + } + else + { + pId = doc.FirstChildElement("id"); + pxeUrl = doc.FirstChildElement("url"); + } + if (pId && pId->FirstChild()) + scurlRet.strId = pId->FirstChild()->Value(); + + if (pxeUrl && pxeUrl->Attribute("function")) + continue; + + if (pxeUrl) + scurlRet.ParseElement(pxeUrl); + else if (!strcmp(doc.RootElement()->Value(), "url")) + scurlRet.ParseElement(doc.RootElement()); + else + continue; + break; + } + } + return scurlRet; +} + +static bool RelevanceSortFunction(const CScraperUrl &left, const CScraperUrl &right) +{ + return left.relevance > right.relevance; +} + +// fetch list of matching movies sorted by relevance (may be empty); +// throws CScraperError on error; first called with fFirst set, then unset if first try fails +std::vector<CScraperUrl> CScraper::FindMovie(XFILE::CCurlFile &fcurl, const std::string &sMovie, + bool fFirst) +{ + // prepare parameters for URL creation + std::string sTitle, sTitleYear, sYear; + CUtil::CleanString(sMovie, sTitle, sTitleYear, sYear, true/*fRemoveExt*/, fFirst); + + CLog::Log(LOGDEBUG, "%s: Searching for '%s' using %s scraper " + "(path: '%s', content: '%s', version: '%s')", __FUNCTION__, sTitle.c_str(), + Name().c_str(), Path().c_str(), + ADDON::TranslateContent(Content()).c_str(), Version().asString().c_str()); + + std::vector<CScraperUrl> vcscurl; + if (IsNoop()) + return vcscurl; + + if (!fFirst) + StringUtils::Replace(sTitle, '-',' '); + + vector<string> vcsIn(1); + g_charsetConverter.utf8To(SearchStringEncoding(), sTitle, vcsIn[0]); + vcsIn[0] = CURL::Encode(vcsIn[0]); + if (fFirst && !sYear.empty()) + vcsIn.push_back(sYear); + + // request a search URL from the title/filename/etc. + CScraperUrl scurl; + vector<string> vcsOut = Run("CreateSearchUrl", scurl, fcurl, &vcsIn); + if (vcsOut.empty()) + { + CLog::Log(LOGDEBUG, "%s: CreateSearchUrl failed", __FUNCTION__); + throw CScraperError(); + } + scurl.ParseString(vcsOut[0]); + + // do the search, and parse the result into a list + vcsIn.clear(); + vcsIn.push_back(scurl.m_url[0].m_url); + vcsOut = Run("GetSearchResults", scurl, fcurl, &vcsIn); + + bool fSort(true); + std::set<std::string> stsDupeCheck; + bool fResults(false); + for (vector<string>::const_iterator i = vcsOut.begin(); i != vcsOut.end(); ++i) + { + CXBMCTinyXML doc; + doc.Parse(*i, TIXML_ENCODING_UTF8); + if (!doc.RootElement()) + { + CLog::Log(LOGERROR, "%s: Unable to parse XML", __FUNCTION__); + continue; // might have more valid results later + } + + CheckScraperError(doc.RootElement()); + + TiXmlHandle xhDoc(&doc); + TiXmlHandle xhResults = xhDoc.FirstChild("results"); + if (!xhResults.Element()) + continue; + fResults = true; // even if empty + + // we need to sort if returned results don't specify 'sorted="yes"' + if (fSort) + { + const char *sorted = xhResults.Element()->Attribute("sorted"); + if (sorted != NULL) + fSort = !StringUtils::EqualsNoCase(sorted, "yes"); + } + + for (TiXmlElement *pxeMovie = xhResults.FirstChild("entity").Element(); + pxeMovie; pxeMovie = pxeMovie->NextSiblingElement()) + { + CScraperUrl scurlMovie; + TiXmlNode *pxnTitle = pxeMovie->FirstChild("title"); + TiXmlElement *pxeLink = pxeMovie->FirstChildElement("url"); + if (pxnTitle && pxnTitle->FirstChild() && pxeLink && pxeLink->FirstChild()) + { + scurlMovie.strTitle = pxnTitle->FirstChild()->Value(); + XMLUtils::GetString(pxeMovie, "id", scurlMovie.strId); + + for ( ; pxeLink && pxeLink->FirstChild(); pxeLink = pxeLink->NextSiblingElement("url")) + scurlMovie.ParseElement(pxeLink); + + // calculate the relavance of this hit + std::string sCompareTitle = scurlMovie.strTitle; + StringUtils::ToLower(sCompareTitle); + std::string sMatchTitle = sTitle; + StringUtils::ToLower(sMatchTitle); + + /* + * Identify the best match by performing a fuzzy string compare on the search term and + * the result. Additionally, use the year (if available) to further refine the best match. + * An exact match scores 1, a match off by a year scores 0.5 (release dates can vary between + * countries), otherwise it scores 0. + */ + std::string sCompareYear; + XMLUtils::GetString(pxeMovie, "year", sCompareYear); + + double yearScore = 0; + if (!sYear.empty() && !sCompareYear.empty()) + yearScore = std::max(0.0, 1-0.5*abs(atoi(sYear.c_str())-atoi(sCompareYear.c_str()))); + + scurlMovie.relevance = fstrcmp(sMatchTitle.c_str(), sCompareTitle.c_str(), 0.0) + yearScore; + + // reconstruct a title for the user + if (!sCompareYear.empty()) + scurlMovie.strTitle += StringUtils::Format(" (%s)", sCompareYear.c_str()); + + std::string sLanguage; + if (XMLUtils::GetString(pxeMovie, "language", sLanguage) && !sLanguage.empty()) + scurlMovie.strTitle += StringUtils::Format(" (%s)", sLanguage.c_str()); + + // filter for dupes from naughty scrapers + if (stsDupeCheck.insert(scurlMovie.m_url[0].m_url + " " + scurlMovie.strTitle).second) + vcscurl.push_back(scurlMovie); + } + } + } + + if (!fResults) + throw CScraperError(); // scraper aborted + + if (fSort) + std::stable_sort(vcscurl.begin(), vcscurl.end(), RelevanceSortFunction); + + return vcscurl; +} + +// find album by artist, using fcurl for web fetches +// returns a list of albums (empty if no match or failure) +std::vector<CMusicAlbumInfo> CScraper::FindAlbum(CCurlFile &fcurl, const std::string &sAlbum, + const std::string &sArtist) +{ + CLog::Log(LOGDEBUG, "%s: Searching for '%s - %s' using %s scraper " + "(path: '%s', content: '%s', version: '%s')", __FUNCTION__, sArtist.c_str(), + sAlbum.c_str(), Name().c_str(), Path().c_str(), + ADDON::TranslateContent(Content()).c_str(), Version().asString().c_str()); + + std::vector<CMusicAlbumInfo> vcali; + if (IsNoop()) + return vcali; + + // scraper function is given the album and artist as parameters and + // returns an XML <url> element parseable by CScraperUrl + std::vector<string> extras(2); + g_charsetConverter.utf8To(SearchStringEncoding(), sAlbum, extras[0]); + g_charsetConverter.utf8To(SearchStringEncoding(), sArtist, extras[1]); + extras[0] = CURL::Encode(extras[0]); + extras[1] = CURL::Encode(extras[1]); + CScraperUrl scurl; + vector<string> vcsOut = RunNoThrow("CreateAlbumSearchUrl", scurl, fcurl, &extras); + if (vcsOut.size() > 1) + CLog::Log(LOGWARNING, "%s: scraper returned multiple results; using first", __FUNCTION__); + + if (vcsOut.empty() || vcsOut[0].empty()) + return vcali; + scurl.ParseString(vcsOut[0]); + + // the next function is passed the contents of the returned URL, and returns + // an empty string on failure; on success, returns XML matches in the form: + // <results> + // <entity> + // <title>... + // ... (with the usual CScraperUrl decorations like post or spoof) + // ... + // ... + // ... (scale defaults to 1; score is divided by it) + // + // ... + // + vcsOut = RunNoThrow("GetAlbumSearchResults", scurl, fcurl); + + // parse the returned XML into a vector of album objects + for (vector::const_iterator i = vcsOut.begin(); i != vcsOut.end(); ++i) + { + CXBMCTinyXML doc; + doc.Parse(*i, TIXML_ENCODING_UTF8); + TiXmlHandle xhDoc(&doc); + + for (TiXmlElement* pxeAlbum = xhDoc.FirstChild("results").FirstChild("entity").Element(); + pxeAlbum; pxeAlbum = pxeAlbum->NextSiblingElement()) + { + std::string sTitle; + if (XMLUtils::GetString(pxeAlbum, "title", sTitle) && !sTitle.empty()) + { + std::string sArtist; + std::string sAlbumName; + if (XMLUtils::GetString(pxeAlbum, "artist", sArtist) && !sArtist.empty()) + sAlbumName = StringUtils::Format("%s - %s", sArtist.c_str(), sTitle.c_str()); + else + sAlbumName = sTitle; + + std::string sYear; + if (XMLUtils::GetString(pxeAlbum, "year", sYear) && !sYear.empty()) + sAlbumName = StringUtils::Format("%s (%s)", sAlbumName.c_str(), sYear.c_str()); + + // if no URL is provided, use the URL we got back from CreateAlbumSearchUrl + // (e.g., in case we only got one result back and were sent to the detail page) + TiXmlElement* pxeLink = pxeAlbum->FirstChildElement("url"); + CScraperUrl scurlAlbum; + if (!pxeLink) + scurlAlbum.ParseString(scurl.m_xml); + for ( ; pxeLink && pxeLink->FirstChild(); pxeLink = pxeLink->NextSiblingElement("url")) + scurlAlbum.ParseElement(pxeLink); + + if (!scurlAlbum.m_url.size()) + continue; + + CMusicAlbumInfo ali(sTitle, sArtist, sAlbumName, scurlAlbum); + + TiXmlElement* pxeRel = pxeAlbum->FirstChildElement("relevance"); + if (pxeRel && pxeRel->FirstChild()) + { + const char* szScale = pxeRel->Attribute("scale"); + float flScale = szScale ? float(atof(szScale)) : 1; + ali.SetRelevance(float(atof(pxeRel->FirstChild()->Value())) / flScale); + } + + vcali.push_back(ali); + } + } + } + return vcali; +} + +// find artist, using fcurl for web fetches +// returns a list of artists (empty if no match or failure) +std::vector CScraper::FindArtist(CCurlFile &fcurl, + const std::string &sArtist) +{ + CLog::Log(LOGDEBUG, "%s: Searching for '%s' using %s scraper " + "(file: '%s', content: '%s', version: '%s')", __FUNCTION__, sArtist.c_str(), + Name().c_str(), Path().c_str(), + ADDON::TranslateContent(Content()).c_str(), Version().asString().c_str()); + + std::vector vcari; + if (IsNoop()) + return vcari; + + // scraper function is given the artist as parameter and + // returns an XML element parseable by CScraperUrl + std::vector extras(1); + g_charsetConverter.utf8To(SearchStringEncoding(), sArtist, extras[0]); + extras[0] = CURL::Encode(extras[0]); + CScraperUrl scurl; + vector vcsOut = RunNoThrow("CreateArtistSearchUrl", scurl, fcurl, &extras); + + if (vcsOut.empty() || vcsOut[0].empty()) + return vcari; + scurl.ParseString(vcsOut[0]); + + // the next function is passed the contents of the returned URL, and returns + // an empty string on failure; on success, returns XML matches in the form: + // + // + // ... + // ... + // ... + // ... (with the usual CScraperUrl decorations like post or spoof) + // + // ... + // + vcsOut = RunNoThrow("GetArtistSearchResults", scurl, fcurl); + + // parse the returned XML into a vector of artist objects + for (vector::const_iterator i = vcsOut.begin(); i != vcsOut.end(); ++i) + { + CXBMCTinyXML doc; + doc.Parse(*i, TIXML_ENCODING_UTF8); + if (!doc.RootElement()) + { + CLog::Log(LOGERROR, "%s: Unable to parse XML", __FUNCTION__); + return vcari; + } + TiXmlHandle xhDoc(&doc); + for (TiXmlElement* pxeArtist = xhDoc.FirstChild("results").FirstChild("entity").Element(); + pxeArtist; pxeArtist = pxeArtist->NextSiblingElement()) + { + TiXmlNode* pxnTitle = pxeArtist->FirstChild("title"); + if (pxnTitle && pxnTitle->FirstChild()) + { + CScraperUrl scurlArtist; + + TiXmlElement* pxeLink = pxeArtist->FirstChildElement("url"); + if (!pxeLink) + scurlArtist.ParseString(scurl.m_xml); + for ( ; pxeLink && pxeLink->FirstChild(); pxeLink = pxeLink->NextSiblingElement("url")) + scurlArtist.ParseElement(pxeLink); + + if (!scurlArtist.m_url.size()) + continue; + + CMusicArtistInfo ari(pxnTitle->FirstChild()->Value(), scurlArtist); + std::string genre; + XMLUtils::GetString(pxeArtist, "genre", genre); + if (!genre.empty()) + ari.GetArtist().genre = StringUtils::Split(genre, g_advancedSettings.m_musicItemSeparator); + XMLUtils::GetString(pxeArtist, "year", ari.GetArtist().strBorn); + + vcari.push_back(ari); + } + } + } + return vcari; +} + +// fetch list of episodes from URL (from video database) +EPISODELIST CScraper::GetEpisodeList(XFILE::CCurlFile &fcurl, const CScraperUrl &scurl) +{ + EPISODELIST vcep; + if (scurl.m_url.empty()) + return vcep; + + CLog::Log(LOGDEBUG, "%s: Searching '%s' using %s scraper " + "(file: '%s', content: '%s', version: '%s')", __FUNCTION__, + scurl.m_url[0].m_url.c_str(), Name().c_str(), Path().c_str(), + ADDON::TranslateContent(Content()).c_str(), Version().asString().c_str()); + + vector vcsIn; + vcsIn.push_back(scurl.m_url[0].m_url); + vector vcsOut = RunNoThrow("GetEpisodeList", scurl, fcurl, &vcsIn); + + // parse the XML response + for (vector::const_iterator i = vcsOut.begin(); i != vcsOut.end(); ++i) + { + CXBMCTinyXML doc; + doc.Parse(*i); + if (!doc.RootElement()) + { + CLog::Log(LOGERROR, "%s: Unable to parse XML",__FUNCTION__); + continue; + } + + TiXmlHandle xhDoc(&doc); + for (TiXmlElement *pxeMovie = xhDoc.FirstChild("episodeguide").FirstChild("episode"). + Element(); pxeMovie; pxeMovie = pxeMovie->NextSiblingElement()) + { + EPISODE ep; + TiXmlElement *pxeLink = pxeMovie->FirstChildElement("url"); + std::string strEpNum; + if (pxeLink && XMLUtils::GetInt(pxeMovie, "season", ep.iSeason) && + XMLUtils::GetString(pxeMovie, "epnum", strEpNum) && !strEpNum.empty()) + { + CScraperUrl &scurlEp(ep.cScraperUrl); + size_t dot = strEpNum.find("."); + ep.iEpisode = atoi(strEpNum.c_str()); + ep.iSubepisode = (dot != std::string::npos) ? atoi(strEpNum.substr(dot + 1).c_str()) : 0; + if (!XMLUtils::GetString(pxeMovie, "title", scurlEp.strTitle) || scurlEp.strTitle.empty() ) + scurlEp.strTitle = g_localizeStrings.Get(416); + XMLUtils::GetString(pxeMovie, "id", scurlEp.strId); + + for ( ; pxeLink && pxeLink->FirstChild(); pxeLink = pxeLink->NextSiblingElement("url")) + scurlEp.ParseElement(pxeLink); + + // date must be the format of yyyy-mm-dd + ep.cDate.SetValid(FALSE); + std::string sDate; + if (XMLUtils::GetString(pxeMovie, "aired", sDate) && sDate.length() == 10) + { + tm tm; + if (strptime(sDate.c_str(), "%Y-%m-%d", &tm)) + ep.cDate.SetDate(1900+tm.tm_year, tm.tm_mon + 1, tm.tm_mday); + } + vcep.push_back(ep); + } + } + } + + return vcep; +} + +// takes URL; returns true and populates video details on success, false otherwise +bool CScraper::GetVideoDetails(XFILE::CCurlFile &fcurl, const CScraperUrl &scurl, + bool fMovie/*else episode*/, CVideoInfoTag &video) +{ + CLog::Log(LOGDEBUG, "%s: Reading %s '%s' using %s scraper " + "(file: '%s', content: '%s', version: '%s')", __FUNCTION__, + fMovie ? MediaTypeMovie : MediaTypeEpisode, scurl.m_url[0].m_url.c_str(), Name().c_str(), Path().c_str(), + ADDON::TranslateContent(Content()).c_str(), Version().asString().c_str()); + + video.Reset(); + std::string sFunc = fMovie ? "GetDetails" : "GetEpisodeDetails"; + vector vcsIn; + vcsIn.push_back(scurl.strId); + vcsIn.push_back(scurl.m_url[0].m_url); + vector vcsOut = RunNoThrow(sFunc, scurl, fcurl, &vcsIn); + + // parse XML output + bool fRet(false); + for (vector::const_iterator i = vcsOut.begin(); i != vcsOut.end(); ++i) + { + CXBMCTinyXML doc; + doc.Parse(*i, TIXML_ENCODING_UTF8); + if (!doc.RootElement()) + { + CLog::Log(LOGERROR, "%s: Unable to parse XML", __FUNCTION__); + continue; + } + + TiXmlHandle xhDoc(&doc); + TiXmlElement *pxeDetails = xhDoc.FirstChild("details").Element(); + if (!pxeDetails) + { + CLog::Log(LOGERROR, "%s: Invalid XML file (want
)", __FUNCTION__); + continue; + } + video.Load(pxeDetails, true/*fChain*/); + fRet = true; // but don't exit in case of chaining + } + return fRet; +} + +// takes a URL; returns true and populates album on success, false otherwise +bool CScraper::GetAlbumDetails(CCurlFile &fcurl, const CScraperUrl &scurl, CAlbum &album) +{ + CLog::Log(LOGDEBUG, "%s: Reading '%s' using %s scraper " + "(file: '%s', content: '%s', version: '%s')", __FUNCTION__, + scurl.m_url[0].m_url.c_str(), Name().c_str(), Path().c_str(), + ADDON::TranslateContent(Content()).c_str(), Version().asString().c_str()); + + vector vcsOut = RunNoThrow("GetAlbumDetails", scurl, fcurl); + + // parse the returned XML into an album object (see CAlbum::Load for details) + bool fRet(false); + for (vector::const_iterator i = vcsOut.begin(); i != vcsOut.end(); ++i) + { + CXBMCTinyXML doc; + doc.Parse(*i, TIXML_ENCODING_UTF8); + if (!doc.RootElement()) + { + CLog::Log(LOGERROR, "%s: Unable to parse XML", __FUNCTION__); + return false; + } + fRet = album.Load(doc.RootElement(), i != vcsOut.begin()); + } + return fRet; +} + +// takes a URL (one returned from FindArtist), the original search string, and +// returns true and populates artist on success, false on failure +bool CScraper::GetArtistDetails(CCurlFile &fcurl, const CScraperUrl &scurl, + const std::string &sSearch, CArtist &artist) +{ + if (!scurl.m_url.size()) + return false; + + CLog::Log(LOGDEBUG, "%s: Reading '%s' ('%s') using %s scraper " + "(file: '%s', content: '%s', version: '%s')", __FUNCTION__, + scurl.m_url[0].m_url.c_str(), sSearch.c_str(), Name().c_str(), Path().c_str(), + ADDON::TranslateContent(Content()).c_str(), Version().asString().c_str()); + + // pass in the original search string for chaining to search other sites + vector vcIn; + vcIn.push_back(sSearch); + vcIn[0] = CURL::Encode(vcIn[0]); + + vector vcsOut = RunNoThrow("GetArtistDetails", scurl, fcurl, &vcIn); + + // ok, now parse the xml file + bool fRet(false); + for (vector::const_iterator i = vcsOut.begin(); i != vcsOut.end(); ++i) + { + CXBMCTinyXML doc; + doc.Parse(*i, TIXML_ENCODING_UTF8); + if (!doc.RootElement()) + { + CLog::Log(LOGERROR, "%s: Unable to parse XML", __FUNCTION__); + return false; + } + + fRet = artist.Load(doc.RootElement(), i != vcsOut.begin()); + } + return fRet; +} + +} + diff --git a/xbmc/addons/Scraper.h b/xbmc/addons/Scraper.h new file mode 100644 index 0000000..7302972 --- /dev/null +++ b/xbmc/addons/Scraper.h @@ -0,0 +1,178 @@ +#pragma once +/* + * Copyright (C) 2005-2013 Team XBMC + * http://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, see + * . + * + */ +#include "addons/Addon.h" +#include "XBDateTime.h" +#include "utils/ScraperUrl.h" +#include "utils/ScraperParser.h" +#include "video/Episode.h" + +class CAlbum; +class CArtist; +class CVideoInfoTag; + +namespace MUSIC_GRABBER +{ +class CMusicAlbumInfo; +class CMusicArtistInfo; +} + +typedef enum +{ + CONTENT_MOVIES, + CONTENT_TVSHOWS, + CONTENT_MUSICVIDEOS, + CONTENT_ALBUMS, + CONTENT_ARTISTS, + CONTENT_NONE, +} CONTENT_TYPE; + +namespace XFILE +{ + class CCurlFile; +} + +class CScraperUrl; + +namespace ADDON +{ +class CScraper; +typedef std::shared_ptr ScraperPtr; + +std::string TranslateContent(const CONTENT_TYPE &content, bool pretty=false); +CONTENT_TYPE TranslateContent(const std::string &string); +TYPE ScraperTypeFromContent(const CONTENT_TYPE &content); + +// thrown as exception to signal abort or show error dialog +class CScraperError +{ +public: + CScraperError() : m_fAborted(true) {} + CScraperError(const std::string &sTitle, const std::string &sMessage) : + m_fAborted(false), m_sTitle(sTitle), m_sMessage(sMessage) {} + + bool FAborted() const { return m_fAborted; } + const std::string &Title() const { return m_sTitle; } + const std::string &Message() const { return m_sMessage; } + +private: + bool m_fAborted; + std::string m_sTitle; + std::string m_sMessage; +}; + +class CScraper : public CAddon +{ +public: + CScraper(const AddonProps &props) : CAddon(props), m_fLoaded(false) {} + CScraper(const cp_extension_t *ext); + virtual ~CScraper() {} + virtual AddonPtr Clone() const; + + /*! \brief Set the scraper settings for a particular path from an XML string + Loads the default and user settings (if not already loaded) and, if the given XML string is non-empty, + overrides the user settings with the XML. + \param content Content type of the path + \param xml string of XML with the settings. If non-empty this overrides any saved user settings. + \return true if settings are available, false otherwise + \sa GetPathSettings + */ + bool SetPathSettings(CONTENT_TYPE content, const std::string& xml); + + /*! \brief Get the scraper settings for a particular path in the form of an XML string + Loads the default and user settings (if not already loaded) and returns the user settings in the + form or an XML string + \return a string containing the XML settings + \sa SetPathSettings + */ + std::string GetPathSettings(); + + /*! \brief Clear any previously cached results for this scraper + Any previously cached files are cleared if they have been cached for longer than the specified + cachepersistence. + */ + void ClearCache(); + + CONTENT_TYPE Content() const { return m_pathContent; } + const std::string& Language() const { return m_language; } + bool RequiresSettings() const { return m_requiressettings; } + bool Supports(const CONTENT_TYPE &content) const; + + bool IsInUse() const; + bool IsNoop(); + + // scraper media functions + CScraperUrl NfoUrl(const std::string &sNfoContent); + + /*! \brief Resolve an external ID (e.g. MusicBrainz IDs) to a URL using scrapers + If we have an ID in hand, e.g. MusicBrainz IDs or TheTVDB Season IDs + we can get directly to a URL instead of searching by name and choosing from + the search results. The correct scraper type should be used to get the right + URL for a given ID, so we can differentiate albums, artists, TV Seasons, etc. + \param externalID the external ID - e.g. MusicBrainzArtist/AlbumID + \return a populated URL pointing to the details page for the given ID or + an empty URL if we couldn't resolve the ID. + */ + CScraperUrl ResolveIDToUrl(const std::string &externalID); + + std::vector FindMovie(XFILE::CCurlFile &fcurl, + const std::string &sMovie, bool fFirst); + std::vector FindAlbum(XFILE::CCurlFile &fcurl, + const std::string &sAlbum, const std::string &sArtist = ""); + std::vector FindArtist( + XFILE::CCurlFile &fcurl, const std::string &sArtist); + VIDEO::EPISODELIST GetEpisodeList(XFILE::CCurlFile &fcurl, const CScraperUrl &scurl); + + bool GetVideoDetails(XFILE::CCurlFile &fcurl, const CScraperUrl &scurl, + bool fMovie/*else episode*/, CVideoInfoTag &video); + bool GetAlbumDetails(XFILE::CCurlFile &fcurl, const CScraperUrl &scurl, + CAlbum &album); + bool GetArtistDetails(XFILE::CCurlFile &fcurl, const CScraperUrl &scurl, + const std::string &sSearch, CArtist &artist); + +private: + CScraper(const CScraper &rhs); + std::string SearchStringEncoding() const + { return m_parser.GetSearchStringEncoding(); } + + bool Load(); + std::vector Run(const std::string& function, + const CScraperUrl& url, + XFILE::CCurlFile& http, + const std::vector* extras = NULL); + std::vector RunNoThrow(const std::string& function, + const CScraperUrl& url, + XFILE::CCurlFile& http, + const std::vector* extras = NULL); + std::string InternalRun(const std::string& function, + const CScraperUrl& url, + XFILE::CCurlFile& http, + const std::vector* extras); + + bool m_fLoaded; + std::string m_language; + bool m_requiressettings; + CDateTimeSpan m_persistence; + CONTENT_TYPE m_pathContent; + CScraperParser m_parser; +}; + +} + diff --git a/xbmc/addons/ScreenSaver.cpp b/xbmc/addons/ScreenSaver.cpp new file mode 100644 index 0000000..ef345f5 --- /dev/null +++ b/xbmc/addons/ScreenSaver.cpp @@ -0,0 +1,125 @@ +/* + * Copyright (C) 2005-2013 Team XBMC + * http://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, see + * . + * + */ +#include "ScreenSaver.h" +#include "guilib/GraphicContext.h" +#include "interfaces/generic/ScriptInvocationManager.h" +#include "settings/DisplaySettings.h" +#include "utils/AlarmClock.h" +#include "windowing/WindowingFactory.h" + +// What sound does a python screensaver make? +#define SCRIPT_ALARM "sssssscreensaver" + +#define SCRIPT_TIMEOUT 5 // seconds + +namespace ADDON +{ + + CScreenSaver::CScreenSaver(const char *addonID) + : ADDON::CAddonDll(AddonProps(addonID, ADDON_UNKNOWN, "", "")) + { + } + +AddonPtr CScreenSaver::Clone() const +{ + // Copy constructor is generated by compiler and calls parent copy constructor + return AddonPtr(new CScreenSaver(*this)); +} + +bool CScreenSaver::CreateScreenSaver() +{ + if (CScriptInvocationManager::Get().HasLanguageInvoker(LibPath())) + { + // Don't allow a previously-scheduled alarm to kill our new screensaver + g_alarmClock.Stop(SCRIPT_ALARM, true); + + if (!CScriptInvocationManager::Get().Stop(LibPath())) + CScriptInvocationManager::Get().ExecuteAsync(LibPath(), Clone()); + return true; + } + // pass it the screen width,height + // and the name of the screensaver + int iWidth = g_graphicsContext.GetWidth(); + int iHeight = g_graphicsContext.GetHeight(); + + m_pInfo = new SCR_PROPS; +#ifdef HAS_DX + m_pInfo->device = g_Windowing.Get3DDevice(); +#else + m_pInfo->device = NULL; +#endif + m_pInfo->x = 0; + m_pInfo->y = 0; + m_pInfo->width = iWidth; + m_pInfo->height = iHeight; + m_pInfo->pixelRatio = g_graphicsContext.GetResInfo().fPixelRatio; + m_pInfo->name = strdup(Name().c_str()); + m_pInfo->presets = strdup(CSpecialProtocol::TranslatePath(Path()).c_str()); + m_pInfo->profile = strdup(CSpecialProtocol::TranslatePath(Profile()).c_str()); + + if (CAddonDll::Create() == ADDON_STATUS_OK) + return true; + + return false; +} + +void CScreenSaver::Start() +{ + // notify screen saver that they should start + if (Initialized()) m_pStruct->Start(); +} + +void CScreenSaver::Render() +{ + // ask screensaver to render itself + if (Initialized()) m_pStruct->Render(); +} + +void CScreenSaver::GetInfo(SCR_INFO *info) +{ + // get info from screensaver + if (Initialized()) m_pStruct->GetInfo(info); +} + +void CScreenSaver::Destroy() +{ +#ifdef HAS_PYTHON + if (URIUtils::HasExtension(LibPath(), ".py")) + { + g_alarmClock.Start(SCRIPT_ALARM, SCRIPT_TIMEOUT, "StopScript(" + LibPath() + ")", true, false); + return; + } +#endif + // Release what was allocated in method CScreenSaver::CreateScreenSaver. + if (m_pInfo) + { + free((void *) m_pInfo->name); + free((void *) m_pInfo->presets); + free((void *) m_pInfo->profile); + + delete m_pInfo; + m_pInfo = NULL; + } + + CAddonDll::Destroy(); +} + +} /*namespace ADDON*/ + diff --git a/xbmc/addons/ScreenSaver.h b/xbmc/addons/ScreenSaver.h new file mode 100644 index 0000000..1b2a741 --- /dev/null +++ b/xbmc/addons/ScreenSaver.h @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2005-2013 Team XBMC + * http://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, see + * . + * + */ +#pragma once + +#include "AddonDll.h" +#include "include/xbmc_scr_types.h" + +typedef DllAddon DllScreenSaver; + +namespace ADDON +{ + +class CScreenSaver : public ADDON::CAddonDll +{ +public: + CScreenSaver(const AddonProps &props) : ADDON::CAddonDll(props) {}; + CScreenSaver(const cp_extension_t *ext) : ADDON::CAddonDll(ext) {}; + CScreenSaver(const char *addonID); + virtual ~CScreenSaver() {} + virtual AddonPtr Clone() const; + + // Things that MUST be supplied by the child classes + bool CreateScreenSaver(); + void Start(); + void Render(); + void GetInfo(SCR_INFO *info); + void Destroy(); +}; + +} /*namespace ADDON*/ diff --git a/xbmc/addons/Service.cpp b/xbmc/addons/Service.cpp new file mode 100644 index 0000000..2fc7670 --- /dev/null +++ b/xbmc/addons/Service.cpp @@ -0,0 +1,158 @@ +/* + * Copyright (C) 2005-2013 Team XBMC + * http://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, see + * . + * + */ +#include "Service.h" +#include "AddonManager.h" +#include "interfaces/generic/ScriptInvocationManager.h" +#include "utils/log.h" +#include "system.h" + +using namespace std; + +namespace ADDON +{ + +CService::CService(const cp_extension_t *ext) + : CAddon(ext), m_type(UNKNOWN), m_startOption(LOGIN) +{ + BuildServiceType(); + + std::string start = CAddonMgr::Get().GetExtValue(ext->configuration, "@start"); + if (start == "startup") + m_startOption = STARTUP; +} + + +CService::CService(const AddonProps &props) + : CAddon(props), m_type(UNKNOWN), m_startOption(LOGIN) +{ + BuildServiceType(); +} + +AddonPtr CService::Clone() const +{ + return AddonPtr(new CService(*this)); +} + +bool CService::Start() +{ + bool ret = true; + switch (m_type) + { +#ifdef HAS_PYTHON + case PYTHON: + ret = (CScriptInvocationManager::Get().ExecuteAsync(LibPath(), this->shared_from_this()) != -1); + break; +#endif + + case UNKNOWN: + default: + ret = false; + break; + } + + return ret; +} + +bool CService::Stop() +{ + bool ret = true; + + switch (m_type) + { +#ifdef HAS_PYTHON + case PYTHON: + ret = CScriptInvocationManager::Get().Stop(LibPath()); + break; +#endif + + case UNKNOWN: + default: + ret = false; + break; + } + + return ret; +} + +void CService::BuildServiceType() +{ + std::string str = LibPath(); + std::string ext; + + size_t p = str.find_last_of('.'); + if (p != string::npos) + ext = str.substr(p + 1); + +#ifdef HAS_PYTHON + std::string pythonExt = ADDON_PYTHON_EXT; + pythonExt.erase(0, 2); + if ( ext == pythonExt ) + m_type = PYTHON; + else +#endif + { + m_type = UNKNOWN; + CLog::Log(LOGERROR, "ADDON: extension '%s' is not currently supported for service addon", ext.c_str()); + } +} + +void CService::OnDisabled() +{ + Stop(); +} + +void CService::OnEnabled() +{ + Start(); +} + +bool CService::OnPreInstall() +{ + // make sure the addon is stopped + AddonPtr localAddon; // need to grab the local addon so we have the correct library path to stop + if (CAddonMgr::Get().GetAddon(ID(), localAddon, ADDON_SERVICE, false)) + { + std::shared_ptr service = std::dynamic_pointer_cast(localAddon); + if (service) + service->Stop(); + } + return !CAddonMgr::Get().IsAddonDisabled(ID()); +} + +void CService::OnPostInstall(bool restart, bool update, bool modal) +{ + if (restart) // reload/start it if it was running + { + AddonPtr localAddon; // need to grab the local addon so we have the correct library path to stop + if (CAddonMgr::Get().GetAddon(ID(), localAddon, ADDON_SERVICE, false)) + { + std::shared_ptr service = std::dynamic_pointer_cast(localAddon); + if (service) + service->Start(); + } + } +} + +void CService::OnPreUnInstall() +{ + Stop(); +} + +} diff --git a/xbmc/addons/Service.h b/xbmc/addons/Service.h new file mode 100644 index 0000000..f7394de --- /dev/null +++ b/xbmc/addons/Service.h @@ -0,0 +1,63 @@ +#pragma once +/* + * Copyright (C) 2005-2013 Team XBMC + * http://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, see + * . + * + */ +#include "Addon.h" + +namespace ADDON +{ + + class CService: public CAddon + { + public: + + enum TYPE + { + UNKNOWN, + PYTHON + }; + + enum START_OPTION + { + STARTUP, + LOGIN + }; + + CService(const cp_extension_t *ext); + CService(const AddonProps &props); + virtual AddonPtr Clone() const; + + bool Start(); + bool Stop(); + TYPE GetServiceType() { return m_type; } + START_OPTION GetStartOption() { return m_startOption; } + virtual void OnDisabled(); + virtual void OnEnabled(); + virtual bool OnPreInstall(); + virtual void OnPostInstall(bool restart, bool update, bool modal); + virtual void OnPreUnInstall(); + + protected: + void BuildServiceType(); + + private: + TYPE m_type; + START_OPTION m_startOption; + }; +} diff --git a/xbmc/addons/Skin.cpp b/xbmc/addons/Skin.cpp new file mode 100644 index 0000000..acb5799 --- /dev/null +++ b/xbmc/addons/Skin.cpp @@ -0,0 +1,491 @@ +/* + * Copyright (C) 2005-2013 Team XBMC + * http://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, see + * . + * + */ + +#include "Skin.h" +#include "AddonManager.h" +#include "LangInfo.h" +#include "Util.h" +#include "dialogs/GUIDialogKaiToast.h" +#include "dialogs/GUIDialogYesNo.h" +#include "filesystem/File.h" +#include "filesystem/SpecialProtocol.h" +#include "guilib/GUIWindowManager.h" +#include "guilib/WindowIDs.h" +#include "settings/Settings.h" +#include "settings/lib/Setting.h" +#include "utils/URIUtils.h" +#include "utils/log.h" +#include "utils/StringUtils.h" +#include "ApplicationMessenger.h" + +// fallback for new skin resolution code +#include "filesystem/Directory.h" + +using namespace std; +using namespace XFILE; + +std::shared_ptr g_SkinInfo; + +namespace ADDON +{ + +CSkinInfo::CSkinInfo(const AddonProps &props, const RESOLUTION_INFO &resolution) + : CAddon(props), m_defaultRes(resolution), m_version(""), m_effectsSlowDown(1.f), m_debugging(false) +{ +} + +CSkinInfo::CSkinInfo(const cp_extension_t *ext) + : CAddon(ext), m_version(""), m_effectsSlowDown(1.f) +{ + ELEMENTS elements; + if (CAddonMgr::Get().GetExtElements(ext->configuration, "res", elements)) + { + for (ELEMENTS::iterator i = elements.begin(); i != elements.end(); ++i) + { + int width = atoi(CAddonMgr::Get().GetExtValue(*i, "@width").c_str()); + int height = atoi(CAddonMgr::Get().GetExtValue(*i, "@height").c_str()); + bool defRes = CAddonMgr::Get().GetExtValue(*i, "@default") == "true"; + std::string folder = CAddonMgr::Get().GetExtValue(*i, "@folder"); + float aspect = 0; + std::string strAspect = CAddonMgr::Get().GetExtValue(*i, "@aspect"); + vector fracs = StringUtils::Split(strAspect, ':'); + if (fracs.size() == 2) + aspect = (float)(atof(fracs[0].c_str())/atof(fracs[1].c_str())); + if (width > 0 && height > 0) + { + RESOLUTION_INFO res(width, height, aspect, folder); + res.strId = strAspect; // for skin usage, store aspect string in strId + if (defRes) + m_defaultRes = res; + m_resolutions.push_back(res); + } + } + } + else + { // no resolutions specified -> backward compatibility + std::string defaultWide = CAddonMgr::Get().GetExtValue(ext->configuration, "@defaultwideresolution"); + if (defaultWide.empty()) + defaultWide = CAddonMgr::Get().GetExtValue(ext->configuration, "@defaultresolution"); + TranslateResolution(defaultWide, m_defaultRes); + } + + std::string str = CAddonMgr::Get().GetExtValue(ext->configuration, "@effectslowdown"); + if (!str.empty()) + m_effectsSlowDown = (float)atof(str.c_str()); + + m_debugging = CAddonMgr::Get().GetExtValue(ext->configuration, "@debugging") == "true"; + + LoadStartupWindows(ext); + + // figure out the version + m_version = GetDependencyVersion("xbmc.gui"); +} + +CSkinInfo::~CSkinInfo() +{ +} + +AddonPtr CSkinInfo::Clone() const +{ + return AddonPtr(new CSkinInfo(*this)); +} + +struct closestRes +{ + closestRes(const RESOLUTION_INFO &target) : m_target(target) { }; + bool operator()(const RESOLUTION_INFO &i, const RESOLUTION_INFO &j) + { + float diff = fabs(i.DisplayRatio() - m_target.DisplayRatio()) - fabs(j.DisplayRatio() - m_target.DisplayRatio()); + if (diff < 0) return true; + if (diff > 0) return false; + diff = fabs((float)i.iHeight - m_target.iHeight) - fabs((float)j.iHeight - m_target.iHeight); + if (diff < 0) return true; + if (diff > 0) return false; + return fabs((float)i.iWidth - m_target.iWidth) < fabs((float)j.iWidth - m_target.iWidth); + } + RESOLUTION_INFO m_target; +}; + +void CSkinInfo::Start() +{ + if (!m_resolutions.size()) + { // try falling back to whatever resolutions exist in the directory + CFileItemList items; + CDirectory::GetDirectory(Path(), items, "", DIR_FLAG_NO_FILE_DIRS); + for (int i = 0; i < items.Size(); i++) + { + RESOLUTION_INFO res; + if (items[i]->m_bIsFolder && TranslateResolution(items[i]->GetLabel(), res)) + m_resolutions.push_back(res); + } + } + + if (!m_resolutions.empty()) + { + // find the closest resolution + const RESOLUTION_INFO &target = g_graphicsContext.GetResInfo(); + RESOLUTION_INFO& res = *std::min_element(m_resolutions.begin(), m_resolutions.end(), closestRes(target)); + m_currentAspect = res.strId; + } +} + +std::string CSkinInfo::GetSkinPath(const std::string& strFile, RESOLUTION_INFO *res, const std::string& strBaseDir /* = "" */) const +{ + if (m_resolutions.empty()) + return ""; // invalid skin + + std::string strPathToUse = Path(); + if (!strBaseDir.empty()) + strPathToUse = strBaseDir; + + // if the caller doesn't care about the resolution just use a temporary + RESOLUTION_INFO tempRes; + if (!res) + res = &tempRes; + + // find the closest resolution + const RESOLUTION_INFO &target = g_graphicsContext.GetResInfo(); + *res = *std::min_element(m_resolutions.begin(), m_resolutions.end(), closestRes(target)); + + std::string strPath = URIUtils::AddFileToFolder(strPathToUse, res->strMode); + strPath = URIUtils::AddFileToFolder(strPath, strFile); + if (CFile::Exists(strPath)) + return strPath; + + // use the default resolution + *res = m_defaultRes; + + strPath = URIUtils::AddFileToFolder(strPathToUse, res->strMode); + strPath = URIUtils::AddFileToFolder(strPath, strFile); + return strPath; +} + +bool CSkinInfo::HasSkinFile(const std::string &strFile) const +{ + return CFile::Exists(GetSkinPath(strFile)); +} + +void CSkinInfo::LoadIncludes() +{ + std::string includesPath = CSpecialProtocol::TranslatePathConvertCase(GetSkinPath("includes.xml")); + CLog::Log(LOGINFO, "Loading skin includes from %s", includesPath.c_str()); + m_includes.ClearIncludes(); + m_includes.LoadIncludes(includesPath); +} + +void CSkinInfo::ResolveIncludes(TiXmlElement *node, std::map* xmlIncludeConditions /* = NULL */) +{ + if(xmlIncludeConditions) + xmlIncludeConditions->clear(); + + m_includes.ResolveIncludes(node, xmlIncludeConditions); +} + +int CSkinInfo::GetStartWindow() const +{ + int windowID = CSettings::Get().GetInt("lookandfeel.startupwindow"); + assert(m_startupWindows.size()); + for (vector::const_iterator it = m_startupWindows.begin(); it != m_startupWindows.end(); ++it) + { + if (windowID == (*it).m_id) + return windowID; + } + // return our first one + return m_startupWindows[0].m_id; +} + +bool CSkinInfo::LoadStartupWindows(const cp_extension_t *ext) +{ + m_startupWindows.clear(); + m_startupWindows.push_back(CStartupWindow(WINDOW_HOME, "513")); + m_startupWindows.push_back(CStartupWindow(WINDOW_TV_CHANNELS, "19180")); + m_startupWindows.push_back(CStartupWindow(WINDOW_RADIO_CHANNELS, "19183")); + m_startupWindows.push_back(CStartupWindow(WINDOW_PROGRAMS, "0")); + m_startupWindows.push_back(CStartupWindow(WINDOW_PICTURES, "1")); + m_startupWindows.push_back(CStartupWindow(WINDOW_MUSIC, "2")); + m_startupWindows.push_back(CStartupWindow(WINDOW_VIDEOS, "3")); + m_startupWindows.push_back(CStartupWindow(WINDOW_FILES, "7")); + m_startupWindows.push_back(CStartupWindow(WINDOW_SETTINGS_MENU, "5")); + m_startupWindows.push_back(CStartupWindow(WINDOW_WEATHER, "8")); + return true; +} + +void CSkinInfo::GetSkinPaths(std::vector &paths) const +{ + RESOLUTION_INFO res; + GetSkinPath("Home.xml", &res); + if (!res.strMode.empty()) + paths.push_back(URIUtils::AddFileToFolder(Path(), res.strMode)); + if (res.strMode != m_defaultRes.strMode) + paths.push_back(URIUtils::AddFileToFolder(Path(), m_defaultRes.strMode)); +} + +bool CSkinInfo::TranslateResolution(const std::string &name, RESOLUTION_INFO &res) +{ + std::string lower(name); StringUtils::ToLower(lower); + if (lower == "pal") + res = RESOLUTION_INFO(720, 576, 4.0f/3, "pal"); + else if (lower == "pal16x9") + res = RESOLUTION_INFO(720, 576, 16.0f/9, "pal16x9"); + else if (lower == "ntsc") + res = RESOLUTION_INFO(720, 480, 4.0f/3, "ntsc"); + else if (lower == "ntsc16x9") + res = RESOLUTION_INFO(720, 480, 16.0f/9, "ntsc16x9"); + else if (lower == "720p") + res = RESOLUTION_INFO(1280, 720, 0, "720p"); + else if (lower == "1080i") + res = RESOLUTION_INFO(1920, 1080, 0, "1080i"); + else + return false; + return true; +} + +int CSkinInfo::GetFirstWindow() const +{ + int startWindow = GetStartWindow(); + if (HasSkinFile("Startup.xml")) + startWindow = WINDOW_STARTUP_ANIM; + return startWindow; +} + +bool CSkinInfo::IsInUse() const +{ + // Could extend this to prompt for reverting to the standard skin perhaps + return CSettings::Get().GetString("lookandfeel.skin") == ID(); +} + +const INFO::CSkinVariableString* CSkinInfo::CreateSkinVariable(const std::string& name, int context) +{ + return m_includes.CreateSkinVariable(name, context); +} + +bool CSkinInfo::OnPreInstall() +{ + // check whether this is an active skin - we need to unload it if so + if (IsInUse()) + { + CApplicationMessenger::Get().ExecBuiltIn("UnloadSkin", true); + return true; + } + return false; +} + +void CSkinInfo::OnPostInstall(bool restart, bool update, bool modal) +{ + if (restart || (!update && !modal && CGUIDialogYesNo::ShowAndGetInput(Name(), g_localizeStrings.Get(24099),"",""))) + { + CGUIDialogKaiToast *toast = (CGUIDialogKaiToast *)g_windowManager.GetWindow(WINDOW_DIALOG_KAI_TOAST); + if (toast) + { + toast->ResetTimer(); + toast->Close(true); + } + if (CSettings::Get().GetString("lookandfeel.skin") == ID()) + CApplicationMessenger::Get().ExecBuiltIn("ReloadSkin", true); + else + CSettings::Get().SetString("lookandfeel.skin", ID()); + } +} + +void CSkinInfo::SettingOptionsSkinColorsFiller(const CSetting *setting, std::vector< std::pair > &list, std::string ¤t, void *data) +{ + std::string settingValue = ((const CSettingString*)setting)->GetValue(); + // Remove the .xml extension from the Themes + if (URIUtils::HasExtension(settingValue, ".xml")) + URIUtils::RemoveExtension(settingValue); + current = "SKINDEFAULT"; + + // There is a default theme (just defaults.xml) + // any other *.xml files are additional color themes on top of this one. + + // add the default label + list.push_back(make_pair(g_localizeStrings.Get(15109), "SKINDEFAULT")); // the standard defaults.xml will be used! + + // Search for colors in the Current skin! + vector vecColors; + string strPath = URIUtils::AddFileToFolder(g_SkinInfo->Path(), "colors"); + + CFileItemList items; + CDirectory::GetDirectory(CSpecialProtocol::TranslatePathConvertCase(strPath), items, ".xml"); + // Search for Themes in the Current skin! + for (int i = 0; i < items.Size(); ++i) + { + CFileItemPtr pItem = items[i]; + if (!pItem->m_bIsFolder && !StringUtils::EqualsNoCase(pItem->GetLabel(), "defaults.xml")) + { // not the default one + vecColors.push_back(pItem->GetLabel().substr(0, pItem->GetLabel().size() - 4)); + } + } + sort(vecColors.begin(), vecColors.end(), sortstringbyname()); + for (int i = 0; i < (int) vecColors.size(); ++i) + list.push_back(make_pair(vecColors[i], vecColors[i])); + + // try to find the best matching value + for (vector< pair >::const_iterator it = list.begin(); it != list.end(); ++it) + { + if (StringUtils::EqualsNoCase(it->second, settingValue)) + current = settingValue; + } +} + +void CSkinInfo::SettingOptionsSkinFontsFiller(const CSetting *setting, std::vector< std::pair > &list, std::string ¤t, void *data) +{ + std::string settingValue = ((const CSettingString*)setting)->GetValue(); + bool currentValueSet = false; + std::string strPath = g_SkinInfo->GetSkinPath("Font.xml"); + + CXBMCTinyXML xmlDoc; + if (!xmlDoc.LoadFile(strPath)) + { + CLog::Log(LOGERROR, "FillInSkinFonts: Couldn't load %s", strPath.c_str()); + return; + } + + const TiXmlElement* pRootElement = xmlDoc.RootElement(); + if (!pRootElement || pRootElement->ValueStr() != "fonts") + { + CLog::Log(LOGERROR, "FillInSkinFonts: file %s doesn't start with ", strPath.c_str()); + return; + } + + const TiXmlElement *pChild = pRootElement->FirstChildElement("fontset"); + while (pChild) + { + const char* idAttr = pChild->Attribute("id"); + const char* idLocAttr = pChild->Attribute("idloc"); + if (idAttr != NULL) + { + if (idLocAttr) + list.push_back(make_pair(g_localizeStrings.Get(atoi(idLocAttr)), idAttr)); + else + list.push_back(make_pair(idAttr, idAttr)); + + if (StringUtils::EqualsNoCase(idAttr, settingValue)) + currentValueSet = true; + } + pChild = pChild->NextSiblingElement("fontset"); + } + + if (list.empty()) + { // Since no fontset is defined, there is no selection of a fontset, so disable the component + list.push_back(make_pair(g_localizeStrings.Get(13278), "")); + current = ""; + currentValueSet = true; + } + + if (!currentValueSet) + current = list[0].second; +} + +void CSkinInfo::SettingOptionsSkinSoundFiller(const CSetting *setting, std::vector< std::pair > &list, std::string ¤t, void *data) +{ + std::string settingValue = ((const CSettingString*)setting)->GetValue(); + current = "SKINDEFAULT"; + + //find skins... + CFileItemList items; + CDirectory::GetDirectory("special://xbmc/sounds/", items); + CDirectory::GetDirectory("special://home/sounds/", items); + + vector vecSoundSkins; + for (int i = 0; i < items.Size(); i++) + { + CFileItemPtr pItem = items[i]; + if (pItem->m_bIsFolder) + { + if (StringUtils::EqualsNoCase(pItem->GetLabel(), ".svn") || + StringUtils::EqualsNoCase(pItem->GetLabel(), "fonts") || + StringUtils::EqualsNoCase(pItem->GetLabel(), "media")) + continue; + + vecSoundSkins.push_back(pItem->GetLabel()); + } + } + + list.push_back(make_pair(g_localizeStrings.Get(474), "OFF")); + list.push_back(make_pair(g_localizeStrings.Get(15109), "SKINDEFAULT")); + + sort(vecSoundSkins.begin(), vecSoundSkins.end(), sortstringbyname()); + for (unsigned int i = 0; i < vecSoundSkins.size(); i++) + list.push_back(make_pair(vecSoundSkins[i], vecSoundSkins[i])); + + // try to find the best matching value + for (vector< pair >::const_iterator it = list.begin(); it != list.end(); ++it) + { + if (StringUtils::EqualsNoCase(it->second, settingValue)) + current = settingValue; + } +} + +void CSkinInfo::SettingOptionsSkinThemesFiller(const CSetting *setting, std::vector< std::pair > &list, std::string ¤t, void *data) +{ + // get the choosen theme and remove the extension from the current theme (backward compat) + std::string settingValue = ((const CSettingString*)setting)->GetValue(); + URIUtils::RemoveExtension(settingValue); + current = "SKINDEFAULT"; + + // there is a default theme (just Textures.xpr/xbt) + // any other *.xpr|*.xbt files are additional themes on top of this one. + + // add the default Label + list.push_back(make_pair(g_localizeStrings.Get(15109), "SKINDEFAULT")); // the standard Textures.xpr/xbt will be used + + // search for themes in the current skin! + vector vecTheme; + CUtil::GetSkinThemes(vecTheme); + + // sort the themes for GUI and list them + for (int i = 0; i < (int) vecTheme.size(); ++i) + list.push_back(make_pair(vecTheme[i], vecTheme[i])); + + // try to find the best matching value + for (vector< pair >::const_iterator it = list.begin(); it != list.end(); ++it) + { + if (StringUtils::EqualsNoCase(it->second, settingValue)) + current = settingValue; + } +} + +void CSkinInfo::SettingOptionsStartupWindowsFiller(const CSetting *setting, std::vector< std::pair > &list, int ¤t, void *data) +{ + int settingValue = ((const CSettingInt *)setting)->GetValue(); + current = -1; + + const vector &startupWindows = g_SkinInfo->GetStartupWindows(); + + for (vector::const_iterator it = startupWindows.begin(); it != startupWindows.end(); ++it) + { + string windowName = it->m_name; + if (StringUtils::IsNaturalNumber(windowName)) + windowName = g_localizeStrings.Get(atoi(windowName.c_str())); + int windowID = it->m_id; + + list.push_back(make_pair(windowName, windowID)); + + if (settingValue == windowID) + current = settingValue; + } + + // if the current value hasn't been properly set, set it to the first window in the list + if (current < 0) + current = list[0].second; +} + +} /*namespace ADDON*/ diff --git a/xbmc/addons/Skin.h b/xbmc/addons/Skin.h new file mode 100644 index 0000000..42bddf8 --- /dev/null +++ b/xbmc/addons/Skin.h @@ -0,0 +1,155 @@ +#pragma once + +/* + * Copyright (C) 2005-2013 Team XBMC + * http://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, see + * . + * + */ + +#include + +#include "Addon.h" +#include "guilib/GraphicContext.h" // needed for the RESOLUTION members +#include "guilib/GUIIncludes.h" // needed for the GUIInclude member +#define CREDIT_LINE_LENGTH 50 + +class CSetting; + +namespace ADDON +{ + +class CSkinInfo : public CAddon +{ +public: + class CStartupWindow + { + public: + CStartupWindow(int id, const std::string &name): + m_id(id), m_name(name) + { + }; + int m_id; + std::string m_name; + }; + + //FIXME remove this, kept for current repo handling + CSkinInfo(const AddonProps &props, const RESOLUTION_INFO &res = RESOLUTION_INFO()); + CSkinInfo(const cp_extension_t *ext); + virtual ~CSkinInfo(); + virtual AddonPtr Clone() const; + + /*! \brief Load resultion information from directories in Path(). + */ + void Start(); + + bool HasSkinFile(const std::string &strFile) const; + + /*! \brief Get the full path to the specified file in the skin + We search for XML files in the skin folder that best matches the current resolution. + \param file XML file to look for + \param res [out] If non-NULL, the resolution that the returned XML file is in is returned. Defaults to NULL. + \param baseDir [in] If non-empty, the given directory is searched instead of the skin's directory. Defaults to empty. + \return path to the XML file + */ + std::string GetSkinPath(const std::string& file, RESOLUTION_INFO *res = NULL, const std::string& baseDir = "") const; + + AddonVersion APIVersion() const { return m_version; }; + + /*! \brief Return whether skin debugging is enabled + \return true if skin debugging (set via true in skin.xml) is enabled. + */ + bool IsDebugging() const { return m_debugging; }; + + /*! \brief Get the id of the first window to load + The first window is generally Startup.xml unless it doesn't exist or if the skinner + has specified which start windows they support and the user is going to somewhere other + than the home screen. + \return id of the first window to load + */ + int GetFirstWindow() const; + + /*! \brief Get the id of the window the user wants to start in after any skin animation + \return id of the start window + */ + int GetStartWindow() const; + + /*! \brief Translate a resolution string + \param name the string to translate + \param res [out] the resolution structure if name is valid + \return true if the resolution is valid, false otherwise + */ + static bool TranslateResolution(const std::string &name, RESOLUTION_INFO &res); + + void ResolveIncludes(TiXmlElement *node, std::map* xmlIncludeConditions = NULL); + + float GetEffectsSlowdown() const { return m_effectsSlowDown; }; + + const std::vector &GetStartupWindows() const { return m_startupWindows; }; + + /*! \brief Retrieve the skin paths to search for skin XML files + \param paths [out] vector of paths to search, in order. + */ + void GetSkinPaths(std::vector &paths) const; + + bool IsInUse() const; + + const std::string& GetCurrentAspect() const { return m_currentAspect; } + + void LoadIncludes(); + const INFO::CSkinVariableString* CreateSkinVariable(const std::string& name, int context); + + static void SettingOptionsSkinColorsFiller(const CSetting *setting, std::vector< std::pair > &list, std::string ¤t, void *data); + static void SettingOptionsSkinFontsFiller(const CSetting *setting, std::vector< std::pair > &list, std::string ¤t, void *data); + static void SettingOptionsSkinSoundFiller(const CSetting *setting, std::vector< std::pair > &list, std::string ¤t, void *data); + static void SettingOptionsSkinThemesFiller(const CSetting *setting, std::vector< std::pair > &list, std::string ¤t, void *data); + static void SettingOptionsStartupWindowsFiller(const CSetting *setting, std::vector< std::pair > &list, int ¤t, void *data); + + virtual bool OnPreInstall(); + virtual void OnPostInstall(bool restart, bool update, bool modal); +protected: + /*! \brief Given a resolution, retrieve the corresponding directory name + \param res RESOLUTION to translate + \return directory name for res + */ + std::string GetDirFromRes(RESOLUTION res) const; + + /*! \brief grab a resolution tag from a skin's configuration data + \param props passed addoninfo structure to check for resolution + \param tag name of the tag to look for + \param res resolution to return + \return true if we find a valid resolution, false otherwise + */ + void GetDefaultResolution(const cp_extension_t *ext, const char *tag, RESOLUTION &res, const RESOLUTION &def) const; + + bool LoadStartupWindows(const cp_extension_t *ext); + + RESOLUTION_INFO m_defaultRes; + std::vector m_resolutions; + + AddonVersion m_version; + + float m_effectsSlowDown; + CGUIIncludes m_includes; + std::string m_currentAspect; + + std::vector m_startupWindows; + bool m_debugging; +}; + +} /*namespace ADDON*/ + +extern std::shared_ptr g_SkinInfo; diff --git a/xbmc/addons/Visualisation.cpp b/xbmc/addons/Visualisation.cpp new file mode 100644 index 0000000..a64ee59 --- /dev/null +++ b/xbmc/addons/Visualisation.cpp @@ -0,0 +1,486 @@ +/* + * Copyright (C) 2005-2013 Team XBMC + * http://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, see + * . + * + */ +#include "system.h" +#include "Visualisation.h" +#include "utils/fft.h" +#include "GUIInfoManager.h" +#include "Application.h" +#include "guilib/GraphicContext.h" +#include "guilib/WindowIDs.h" +#include "music/tags/MusicInfoTag.h" +#include "settings/Settings.h" +#include "settings/AdvancedSettings.h" +#include "settings/DisplaySettings.h" +#include "windowing/WindowingFactory.h" +#include "utils/URIUtils.h" +#include "utils/StringUtils.h" +#include "cores/IPlayer.h" +#include "cores/AudioEngine/AEFactory.h" +#ifdef TARGET_POSIX +#include +#include "filesystem/SpecialProtocol.h" +#endif + +using namespace std; +using namespace MUSIC_INFO; +using namespace ADDON; + +CAudioBuffer::CAudioBuffer(int iSize) +{ + m_iLen = iSize; + m_pBuffer = new float[iSize]; +} + +CAudioBuffer::~CAudioBuffer() +{ + delete [] m_pBuffer; +} + +const float* CAudioBuffer::Get() const +{ + return m_pBuffer; +} + +void CAudioBuffer::Set(const float* psBuffer, int iSize) +{ + if (iSize<0) + return; + memcpy(m_pBuffer, psBuffer, iSize * sizeof(float)); + for (int i = iSize; i < m_iLen; ++i) m_pBuffer[i] = 0; +} + +bool CVisualisation::Create(int x, int y, int w, int h, void *device) +{ + m_pInfo = new VIS_PROPS; + #ifdef HAS_DX + m_pInfo->device = g_Windowing.Get3DDevice(); +#else + m_pInfo->device = NULL; +#endif + m_pInfo->x = x; + m_pInfo->y = y; + m_pInfo->width = w; + m_pInfo->height = h; + m_pInfo->pixelRatio = g_graphicsContext.GetResInfo().fPixelRatio; + + m_pInfo->name = strdup(Name().c_str()); + m_pInfo->presets = strdup(CSpecialProtocol::TranslatePath(Path()).c_str()); + m_pInfo->profile = strdup(CSpecialProtocol::TranslatePath(Profile()).c_str()); + m_pInfo->submodule = NULL; + + if (CAddonDll::Create() == ADDON_STATUS_OK) + { + // Start the visualisation + std::string strFile = URIUtils::GetFileName(g_application.CurrentFile()); + CLog::Log(LOGDEBUG, "Visualisation::Start()\n"); + try + { + m_pStruct->Start(m_iChannels, m_iSamplesPerSec, m_iBitsPerSample, strFile.c_str()); + } + catch (std::exception e) + { + HandleException(e, "m_pStruct->Start() (CVisualisation::Create)"); + return false; + } + + GetPresets(); + + if (GetSubModules()) + m_pInfo->submodule = strdup(CSpecialProtocol::TranslatePath(m_submodules.front()).c_str()); + else + m_pInfo->submodule = NULL; + + CreateBuffers(); + + CAEFactory::RegisterAudioCallback(this); + + return true; + } + return false; +} + +void CVisualisation::Start(int iChannels, int iSamplesPerSec, int iBitsPerSample, const std::string &strSongName) +{ + // notify visz. that new song has been started + // pass it the nr of audio channels, sample rate, bits/sample and offcourse the songname + if (Initialized()) + { + try + { + m_pStruct->Start(iChannels, iSamplesPerSec, iBitsPerSample, strSongName.c_str()); + } + catch (std::exception e) + { + HandleException(e, "m_pStruct->Start (CVisualisation::Start)"); + } + } +} + +void CVisualisation::AudioData(const float* pAudioData, int iAudioDataLength, float *pFreqData, int iFreqDataLength) +{ + // pass audio data to visz. + // audio data: is short audiodata [channel][iAudioDataLength] containing the raw audio data + // iAudioDataLength = length of audiodata array + // pFreqData = fft-ed audio data + // iFreqDataLength = length of pFreqData + if (Initialized()) + { + try + { + m_pStruct->AudioData(pAudioData, iAudioDataLength, pFreqData, iFreqDataLength); + } + catch (std::exception e) + { + HandleException(e, "m_pStruct->AudioData (CVisualisation::AudioData)"); + } + } +} + +void CVisualisation::Render() +{ + // ask visz. to render itself + g_graphicsContext.BeginPaint(); + if (Initialized()) + { + try + { + m_pStruct->Render(); + } + catch (std::exception e) + { + HandleException(e, "m_pStruct->Render (CVisualisation::Render)"); + } + } + g_graphicsContext.EndPaint(); +} + +void CVisualisation::Stop() +{ + CAEFactory::UnregisterAudioCallback(); + if (Initialized()) + { + CAddonDll::Stop(); + } +} + +void CVisualisation::GetInfo(VIS_INFO *info) +{ + if (Initialized()) + { + try + { + m_pStruct->GetInfo(info); + } + catch (std::exception e) + { + HandleException(e, "m_pStruct->GetInfo (CVisualisation::GetInfo)"); + } + } +} + +bool CVisualisation::OnAction(VIS_ACTION action, void *param) +{ + if (!Initialized()) + return false; + + // see if vis wants to handle the input + // returns false if vis doesnt want the input + // returns true if vis handled the input + try + { + if (action != VIS_ACTION_NONE && m_pStruct->OnAction) + { + // if this is a VIS_ACTION_UPDATE_TRACK action, copy relevant + // tags from CMusicInfoTag to VisTag + if ( action == VIS_ACTION_UPDATE_TRACK && param ) + { + const CMusicInfoTag* tag = (const CMusicInfoTag*)param; + std::string artist(StringUtils::Join(tag->GetArtist(), g_advancedSettings.m_musicItemSeparator)); + std::string albumArtist(StringUtils::Join(tag->GetAlbumArtist(), g_advancedSettings.m_musicItemSeparator)); + std::string genre(StringUtils::Join(tag->GetGenre(), g_advancedSettings.m_musicItemSeparator)); + + VisTrack track; + track.title = tag->GetTitle().c_str(); + track.artist = artist.c_str(); + track.album = tag->GetAlbum().c_str(); + track.albumArtist = albumArtist.c_str(); + track.genre = genre.c_str(); + track.comment = tag->GetComment().c_str(); + track.lyrics = tag->GetLyrics().c_str(); + track.trackNumber = tag->GetTrackNumber(); + track.discNumber = tag->GetDiscNumber(); + track.duration = tag->GetDuration(); + track.year = tag->GetYear(); + track.rating = tag->GetRating(); + + return m_pStruct->OnAction(action, &track); + } + return m_pStruct->OnAction((int)action, param); + } + } + catch (std::exception e) + { + HandleException(e, "m_pStruct->OnAction (CVisualisation::OnAction)"); + } + return false; +} + +void CVisualisation::OnInitialize(int iChannels, int iSamplesPerSec, int iBitsPerSample) +{ + if (!m_pStruct) + return ; + CLog::Log(LOGDEBUG, "OnInitialize() started"); + + m_iChannels = iChannels; + m_iSamplesPerSec = iSamplesPerSec; + m_iBitsPerSample = iBitsPerSample; + UpdateTrack(); + + CLog::Log(LOGDEBUG, "OnInitialize() done"); +} + +void CVisualisation::OnAudioData(const float* pAudioData, int iAudioDataLength) +{ + if (!m_pStruct) + return ; + + // FIXME: iAudioDataLength should never be less than 0 + if (iAudioDataLength<0) + return; + + // Save our audio data in the buffers + unique_ptr pBuffer ( new CAudioBuffer(AUDIO_BUFFER_SIZE) ); + pBuffer->Set(pAudioData, iAudioDataLength); + m_vecBuffers.push_back( pBuffer.release() ); + + if ( (int)m_vecBuffers.size() < m_iNumBuffers) return ; + + unique_ptr ptrAudioBuffer ( m_vecBuffers.front() ); + m_vecBuffers.pop_front(); + // Fourier transform the data if the vis wants it... + if (m_bWantsFreq) + { + const float *psAudioData = ptrAudioBuffer->Get(); + memcpy(m_fFreq, psAudioData, AUDIO_BUFFER_SIZE * sizeof(float)); + + // FFT the data + twochanwithwindow(m_fFreq, AUDIO_BUFFER_SIZE); + + // Normalize the data + float fMinData = (float)AUDIO_BUFFER_SIZE * AUDIO_BUFFER_SIZE * 3 / 8 * 0.5 * 0.5; // 3/8 for the Hann window, 0.5 as minimum amplitude + float fInvMinData = 1.0f/fMinData; + for (int i = 0; i < AUDIO_BUFFER_SIZE + 2; i++) + { + m_fFreq[i] *= fInvMinData; + } + + // Transfer data to our visualisation + AudioData(psAudioData, AUDIO_BUFFER_SIZE, m_fFreq, AUDIO_BUFFER_SIZE); + } + else + { // Transfer data to our visualisation + AudioData(ptrAudioBuffer->Get(), AUDIO_BUFFER_SIZE, NULL, 0); + } + return ; +} + +void CVisualisation::CreateBuffers() +{ + ClearBuffers(); + + // Get the number of buffers from the current vis + VIS_INFO info; + m_pStruct->GetInfo(&info); + m_iNumBuffers = info.iSyncDelay + 1; + m_bWantsFreq = (info.bWantsFreq != 0); + if (m_iNumBuffers > MAX_AUDIO_BUFFERS) + m_iNumBuffers = MAX_AUDIO_BUFFERS; + if (m_iNumBuffers < 1) + m_iNumBuffers = 1; +} + +void CVisualisation::ClearBuffers() +{ + m_bWantsFreq = false; + m_iNumBuffers = 0; + + while (!m_vecBuffers.empty()) + { + CAudioBuffer* pAudioBuffer = m_vecBuffers.front(); + delete pAudioBuffer; + m_vecBuffers.pop_front(); + } + for (int j = 0; j < AUDIO_BUFFER_SIZE*2; j++) + { + m_fFreq[j] = 0.0f; + } +} + +bool CVisualisation::UpdateTrack() +{ + bool handled = false; + if (Initialized()) + { + // get the current album art filename + m_AlbumThumb = CSpecialProtocol::TranslatePath(g_infoManager.GetImage(MUSICPLAYER_COVER, WINDOW_INVALID)); + + // get the current track tag + const CMusicInfoTag* tag = g_infoManager.GetCurrentSongTag(); + + if (m_AlbumThumb == "DefaultAlbumCover.png") + m_AlbumThumb = ""; + else + CLog::Log(LOGDEBUG,"Updating visualisation albumart: %s", m_AlbumThumb.c_str()); + + // inform the visualisation of the current album art + if (OnAction( VIS_ACTION_UPDATE_ALBUMART, (void*)( m_AlbumThumb.c_str() ) ) ) + handled = true; + + // inform the visualisation of the current track's tag information + if ( tag && OnAction( VIS_ACTION_UPDATE_TRACK, (void*)tag ) ) + handled = true; + } + return handled; +} + +bool CVisualisation::GetPresetList(std::vector &vecpresets) +{ + vecpresets = m_presets; + return !m_presets.empty(); +} + +bool CVisualisation::GetPresets() +{ + m_presets.clear(); + char **presets = NULL; + unsigned int entries = 0; + try + { + entries = m_pStruct->GetPresets(&presets); + } + catch (std::exception e) + { + HandleException(e, "m_pStruct->OnAction (CVisualisation::GetPresets)"); + return false; + } + if (presets && entries > 0) + { + for (unsigned i=0; i < entries; i++) + { + if (presets[i]) + { + m_presets.push_back(presets[i]); + } + } + } + return (!m_presets.empty()); +} + +bool CVisualisation::GetSubModuleList(std::vector &vecmodules) +{ + vecmodules = m_submodules; + return !m_submodules.empty(); +} + +bool CVisualisation::GetSubModules() +{ + m_submodules.clear(); + char **modules = NULL; + unsigned int entries = 0; + try + { + entries = m_pStruct->GetSubModules(&modules); + } + catch (...) + { + CLog::Log(LOGERROR, "Exception in Visualisation::GetSubModules()"); + return false; + } + if (modules && entries > 0) + { + for (unsigned i=0; i < entries; i++) + { + if (modules[i]) + { + m_submodules.push_back(modules[i]); + } + } + } + return (!m_submodules.empty()); +} + +std::string CVisualisation::GetFriendlyName(const std::string& strVisz, + const std::string& strSubModule) +{ + // should be of the format "moduleName (visName)" + return strSubModule + " (" + strVisz + ")"; +} + +bool CVisualisation::IsLocked() +{ + if (!m_presets.empty()) + { + if (!m_pStruct) + return false; + + return m_pStruct->IsLocked(); + } + return false; +} + +void CVisualisation::Destroy() +{ + // Free what was allocated in method CVisualisation::Create + if (m_pInfo) + { + free((void *) m_pInfo->name); + free((void *) m_pInfo->presets); + free((void *) m_pInfo->profile); + free((void *) m_pInfo->submodule); + + delete m_pInfo; + m_pInfo = NULL; + } + + CAddonDll::Destroy(); +} + +unsigned CVisualisation::GetPreset() +{ + unsigned index = 0; + try + { + index = m_pStruct->GetPreset(); + } + catch(...) + { + return 0; + } + return index; +} + +std::string CVisualisation::GetPresetName() +{ + if (!m_presets.empty()) + return m_presets[GetPreset()]; + else + return ""; +} + diff --git a/xbmc/addons/Visualisation.h b/xbmc/addons/Visualisation.h new file mode 100644 index 0000000..0a2a1cb --- /dev/null +++ b/xbmc/addons/Visualisation.h @@ -0,0 +1,111 @@ +/* + * Copyright (C) 2005-2013 Team XBMC + * http://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, see + * . + * + */ +#pragma once + +#include "AddonDll.h" +#include "cores/IAudioCallback.h" +#include "include/xbmc_vis_types.h" +#include "guilib/IRenderingCallback.h" + +#include +#include +#include + +#define AUDIO_BUFFER_SIZE 512 // MUST BE A POWER OF 2!!! +#define MAX_AUDIO_BUFFERS 16 + +class CCriticalSection; + +typedef DllAddon DllVisualisation; + +class CAudioBuffer +{ +public: + CAudioBuffer(int iSize); + virtual ~CAudioBuffer(); + const float* Get() const; + void Set(const float* psBuffer, int iSize); +private: + CAudioBuffer(); + float* m_pBuffer; + int m_iLen; +}; + +namespace ADDON +{ + class CVisualisation : public CAddonDll + , public IAudioCallback + , public IRenderingCallback + { + public: + CVisualisation(const ADDON::AddonProps &props) : CAddonDll(props) {} + CVisualisation(const cp_extension_t *ext) : CAddonDll(ext) {} + virtual void OnInitialize(int iChannels, int iSamplesPerSec, int iBitsPerSample); + virtual void OnAudioData(const float* pAudioData, int iAudioDataLength); + bool Create(int x, int y, int w, int h, void *device); + void Start(int iChannels, int iSamplesPerSec, int iBitsPerSample, const std::string &strSongName); + void AudioData(const float *pAudioData, int iAudioDataLength, float *pFreqData, int iFreqDataLength); + void Render(); + void Stop(); + void GetInfo(VIS_INFO *info); + bool OnAction(VIS_ACTION action, void *param = NULL); + bool UpdateTrack(); + bool HasSubModules() { return !m_submodules.empty(); } + bool IsLocked(); + unsigned GetPreset(); + std::string GetPresetName(); + bool GetPresetList(std::vector& vecpresets); + bool GetSubModuleList(std::vector& vecmodules); + static std::string GetFriendlyName(const std::string& vis, const std::string& module); + void Destroy(); + + private: + void CreateBuffers(); + void ClearBuffers(); + + bool GetPresets(); + bool GetSubModules(); + + // attributes of the viewport we render to + int m_xPos; + int m_yPos; + int m_width; + int m_height; + + // cached preset list + std::vector m_presets; + // cached submodule list + std::vector m_submodules; + int m_currentModule; + + // audio properties + int m_iChannels; + int m_iSamplesPerSec; + int m_iBitsPerSample; + std::list m_vecBuffers; + int m_iNumBuffers; // Number of Audio buffers + bool m_bWantsFreq; + float m_fFreq[2*AUDIO_BUFFER_SIZE]; // Frequency data + bool m_bCalculate_Freq; // True if the vis wants freq data + + // track information + std::string m_AlbumThumb; + }; +} diff --git a/xbmc/addons/Webinterface.cpp b/xbmc/addons/Webinterface.cpp new file mode 100644 index 0000000..5745b1c --- /dev/null +++ b/xbmc/addons/Webinterface.cpp @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2015 Team XBMC + * http://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, see + * . + * + */ + +#include "Webinterface.h" +#include "addons/AddonManager.h" +#include "utils/log.h" +#include "utils/StringUtils.h" +#include "utils/URIUtils.h" + +using namespace ADDON; + +CWebinterface::CWebinterface(const ADDON::AddonProps &props, WebinterfaceType type /* = WebinterfaceTypeStatic */, const std::string &entryPoint /* = "WEBINTERFACE_DEFAULT_ENTRY_POINT" */) + : CAddon(props), + m_type(type), + m_entryPoint(entryPoint) +{ } + +CWebinterface::CWebinterface(const cp_extension_t *ext) + : CAddon(ext), + m_type(WebinterfaceTypeStatic), + m_entryPoint(WEBINTERFACE_DEFAULT_ENTRY_POINT) +{ + // determine the type of the webinterface + std::string webinterfaceType = CAddonMgr::Get().GetExtValue(ext->configuration, "@type"); + if (StringUtils::EqualsNoCase(webinterfaceType.c_str(), "wsgi")) + m_type = WebinterfaceTypeWsgi; + else if (!webinterfaceType.empty() && !StringUtils::EqualsNoCase(webinterfaceType.c_str(), "static") && !StringUtils::EqualsNoCase(webinterfaceType.c_str(), "html")) + CLog::Log(LOGWARNING, "Webinterface addon \"%s\" has specified an unsupported type \"%s\"", ID().c_str(), webinterfaceType.c_str()); + + // determine the entry point of the webinterface + std::string entryPoint = CAddonMgr::Get().GetExtValue(ext->configuration, "@entry"); + if (!entryPoint.empty()) + m_entryPoint = entryPoint; +} + +CWebinterface::~CWebinterface() +{ } + +std::string CWebinterface::GetEntryPoint(const std::string &path) const +{ + if (m_type == WebinterfaceTypeWsgi) + return LibPath(); + + return URIUtils::AddFileToFolder(path, m_entryPoint); +} + +std::string CWebinterface::GetBaseLocation() const +{ + if (m_type == WebinterfaceTypeWsgi) + return "/addons/" + ID(); + + return ""; +} + +AddonPtr CWebinterface::Clone() const +{ + return AddonPtr(new CWebinterface(*this)); +} \ No newline at end of file diff --git a/xbmc/addons/Webinterface.h b/xbmc/addons/Webinterface.h new file mode 100644 index 0000000..0a4773f --- /dev/null +++ b/xbmc/addons/Webinterface.h @@ -0,0 +1,54 @@ +#pragma once +/* + * Copyright (C) 2015 Team XBMC + * http://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, see + * . + * + */ + +#include "Addon.h" + +#define WEBINTERFACE_DEFAULT_ENTRY_POINT "index.html" + +namespace ADDON +{ + typedef enum WebinterfaceType + { + WebinterfaceTypeStatic = 0, + WebinterfaceTypeWsgi + } WebinterfaceType; + + class CWebinterface : public CAddon + { + public: + explicit CWebinterface(const ADDON::AddonProps &props, WebinterfaceType type = WebinterfaceTypeStatic, const std::string &entryPoint = WEBINTERFACE_DEFAULT_ENTRY_POINT); + explicit CWebinterface(const cp_extension_t *ext); + virtual ~CWebinterface(); + + WebinterfaceType GetType() const { return m_type; } + const std::string& EntryPoint() const { return m_entryPoint; } + + std::string GetEntryPoint(const std::string &path) const; + std::string GetBaseLocation() const; + + // specializations of CAddon + virtual AddonPtr Clone() const; + + private: + WebinterfaceType m_type; + std::string m_entryPoint; + }; +} diff --git a/xbmc/addons/addon-bindings.mk b/xbmc/addons/addon-bindings.mk new file mode 100644 index 0000000..50d75bc --- /dev/null +++ b/xbmc/addons/addon-bindings.mk @@ -0,0 +1,20 @@ +BINDINGS =xbmc/addons/include/xbmc_addon_cpp_dll.h +BINDINGS+=xbmc/addons/include/xbmc_addon_dll.h +BINDINGS+=xbmc/addons/include/xbmc_addon_types.h +BINDINGS+=xbmc/addons/include/xbmc_audioenc_dll.h +BINDINGS+=xbmc/addons/include/xbmc_audioenc_types.h +BINDINGS+=xbmc/addons/include/xbmc_codec_types.h +BINDINGS+=xbmc/addons/include/xbmc_epg_types.h +BINDINGS+=xbmc/addons/include/xbmc_pvr_dll.h +BINDINGS+=xbmc/addons/include/xbmc_pvr_types.h +BINDINGS+=xbmc/addons/include/xbmc_scr_dll.h +BINDINGS+=xbmc/addons/include/xbmc_scr_types.h +BINDINGS+=xbmc/addons/include/xbmc_vis_dll.h +BINDINGS+=xbmc/addons/include/xbmc_vis_types.h +BINDINGS+=xbmc/addons/include/xbmc_stream_utils.hpp +BINDINGS+=addons/library.xbmc.addon/libXBMC_addon.h +BINDINGS+=addons/library.xbmc.gui/libXBMC_gui.h +BINDINGS+=addons/library.xbmc.pvr/libXBMC_pvr.h +BINDINGS+=addons/library.xbmc.codec/libXBMC_codec.h +BINDINGS+=xbmc/cores/dvdplayer/DVDDemuxers/DVDDemuxPacket.h + diff --git a/xbmc/addons/include/NOTE b/xbmc/addons/include/NOTE new file mode 100644 index 0000000..dbcc329 --- /dev/null +++ b/xbmc/addons/include/NOTE @@ -0,0 +1,12 @@ +NOTE: + +This directory contains independent Headers to build Add-on's +without the whole XBMC source tree. The Add-on itself can add +this headers to his source tree without dependencies to any +XBMC related classes or functions. + +Also this headers are never changed without a API Version +change. + +The current PVR API version can be found in xbmc_pvr_types.h: +XBMC_PVR_API_VERSION diff --git a/xbmc/addons/include/xbmc_addon_cpp_dll.h b/xbmc/addons/include/xbmc_addon_cpp_dll.h new file mode 100644 index 0000000..3944525 --- /dev/null +++ b/xbmc/addons/include/xbmc_addon_cpp_dll.h @@ -0,0 +1,191 @@ +#ifndef __XBMC_ADDON_CPP_H__ +#define __XBMC_ADDON_CPP_H__ + +/* + * Copyright (C) 2005-2013 Team XBMC + * http://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, see + * . + * + */ + +#include "xbmc_addon_types.h" + +#include +#include +#include + +class DllSetting +{ +public: + enum SETTING_TYPE { NONE=0, CHECK, SPIN }; + + DllSetting(SETTING_TYPE t, const char *n, const char *l) + { + id = NULL; + label = NULL; + if (n) + { + id = new char[strlen(n)+1]; + strcpy(id, n); + } + if (l) + { + label = new char[strlen(l)+1]; + strcpy(label, l); + } + current = 0; + type = t; + } + + DllSetting(const DllSetting &rhs) // copy constructor + { + id = NULL; + label = NULL; + if (rhs.id) + { + id = new char[strlen(rhs.id)+1]; + strcpy(id, rhs.id); + } + if (rhs.label) + { + label = new char[strlen(rhs.label)+1]; + strcpy(label, rhs.label); + } + current = rhs.current; + type = rhs.type; + for (unsigned int i = 0; i < rhs.entry.size(); i++) + { + char *lab = new char[strlen(rhs.entry[i]) + 1]; + strcpy(lab, rhs.entry[i]); + entry.push_back(lab); + } + } + + ~DllSetting() + { + delete[] id; + delete[] label; + for (unsigned int i=0; i < entry.size(); i++) + delete[] entry[i]; + } + + void AddEntry(const char *label) + { + if (!label || type != SPIN) return; + char *lab = new char[strlen(label) + 1]; + strcpy(lab, label); + entry.push_back(lab); + } + + // data members + SETTING_TYPE type; + char* id; + char* label; + int current; + std::vector entry; +}; + +class DllUtils +{ +public: + + static unsigned int VecToStruct(std::vector &vecSet, ADDON_StructSetting*** sSet) + { + *sSet = NULL; + if(vecSet.size() == 0) + return 0; + + unsigned int uiElements=0; + + *sSet = (ADDON_StructSetting**)malloc(vecSet.size()*sizeof(ADDON_StructSetting*)); + for(unsigned int i=0;iid = NULL; + (*sSet)[i]->label = NULL; + uiElements++; + + if (vecSet[i].id && vecSet[i].label) + { + (*sSet)[i]->id = strdup(vecSet[i].id); + (*sSet)[i]->label = strdup(vecSet[i].label); + (*sSet)[i]->type = vecSet[i].type; + (*sSet)[i]->current = vecSet[i].current; + (*sSet)[i]->entry_elements = 0; + (*sSet)[i]->entry = NULL; + if(vecSet[i].type == DllSetting::SPIN && vecSet[i].entry.size() > 0) + { + (*sSet)[i]->entry = (char**)malloc(vecSet[i].entry.size()*sizeof(char**)); + for(unsigned int j=0;j 0) + { + (*sSet)[i]->entry[j] = strdup(vecSet[i].entry[j]); + (*sSet)[i]->entry_elements++; + } + } + } + } + } + return uiElements; + } + + static void StructToVec(unsigned int iElements, ADDON_StructSetting*** sSet, std::vector *vecSet) + { + if(iElements == 0) + return; + + vecSet->clear(); + for(unsigned int i=0;itype, (*sSet)[i]->id, (*sSet)[i]->label); + if((*sSet)[i]->type == DllSetting::SPIN) + { + for(unsigned int j=0;j<(*sSet)[i]->entry_elements;j++) + { + vSet.AddEntry((*sSet)[i]->entry[j]); + } + } + vSet.current = (*sSet)[i]->current; + vecSet->push_back(vSet); + } + } + + static void FreeStruct(unsigned int iElements, ADDON_StructSetting*** sSet) + { + if(iElements == 0) + return; + + for(unsigned int i=0;itype == DllSetting::SPIN) + { + for(unsigned int j=0;j<(*sSet)[i]->entry_elements;j++) + { + free((*sSet)[i]->entry[j]); + } + free((*sSet)[i]->entry); + } + free((*sSet)[i]->id); + free((*sSet)[i]->label); + free((*sSet)[i]); + } + free(*sSet); + } +}; + +#endif diff --git a/xbmc/addons/include/xbmc_addon_dll.h b/xbmc/addons/include/xbmc_addon_dll.h new file mode 100644 index 0000000..fa6415f --- /dev/null +++ b/xbmc/addons/include/xbmc_addon_dll.h @@ -0,0 +1,55 @@ +#ifndef __XBMC_ADDON_DLL_H__ +#define __XBMC_ADDON_DLL_H__ + +/* + * Copyright (C) 2005-2013 Team XBMC + * http://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, see + * . + * + */ + +#ifdef TARGET_WINDOWS +#include +#else +#ifndef __cdecl +#define __cdecl +#endif +#ifndef __declspec +#define __declspec(X) +#endif +#endif + +#include "xbmc_addon_types.h" + +#ifdef __cplusplus +extern "C" { +#endif + + ADDON_STATUS __declspec(dllexport) ADDON_Create(void *callbacks, void* props); + void __declspec(dllexport) ADDON_Stop(); + void __declspec(dllexport) ADDON_Destroy(); + ADDON_STATUS __declspec(dllexport) ADDON_GetStatus(); + bool __declspec(dllexport) ADDON_HasSettings(); + unsigned int __declspec(dllexport) ADDON_GetSettings(ADDON_StructSetting ***sSet); + ADDON_STATUS __declspec(dllexport) ADDON_SetSetting(const char *settingName, const void *settingValue); + void __declspec(dllexport) ADDON_FreeSettings(); + void __declspec(dllexport) ADDON_Announce(const char *flag, const char *sender, const char *message, const void *data); + +#ifdef __cplusplus +}; +#endif + +#endif diff --git a/xbmc/addons/include/xbmc_addon_types.h b/xbmc/addons/include/xbmc_addon_types.h new file mode 100644 index 0000000..bd6cbe8 --- /dev/null +++ b/xbmc/addons/include/xbmc_addon_types.h @@ -0,0 +1,64 @@ +#ifndef __XBMC_ADDON_TYPES_H__ +#define __XBMC_ADDON_TYPES_H__ + +/* + * Copyright (C) 2005-2013 Team XBMC + * http://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, see + * . + * + */ + +#ifdef __cplusplus +extern "C" { +#endif + +enum ADDON_STATUS +{ + ADDON_STATUS_OK, + ADDON_STATUS_LOST_CONNECTION, + ADDON_STATUS_NEED_RESTART, + ADDON_STATUS_NEED_SETTINGS, + ADDON_STATUS_UNKNOWN, + ADDON_STATUS_NEED_SAVEDSETTINGS, + ADDON_STATUS_PERMANENT_FAILURE /**< permanent failure, like failing to resolve methods */ +}; + +typedef struct +{ + int type; + char* id; + char* label; + int current; + char** entry; + unsigned int entry_elements; +} ADDON_StructSetting; + +/*! + * @brief Handle used to return data from the PVR add-on to CPVRClient + */ +struct ADDON_HANDLE_STRUCT +{ + void *callerAddress; /*!< address of the caller */ + void *dataAddress; /*!< address to store data in */ + int dataIdentifier; /*!< parameter to pass back when calling the callback */ +}; +typedef ADDON_HANDLE_STRUCT *ADDON_HANDLE; + +#ifdef __cplusplus +}; +#endif + +#endif diff --git a/xbmc/addons/include/xbmc_audioenc_dll.h b/xbmc/addons/include/xbmc_audioenc_dll.h new file mode 100644 index 0000000..01e8d12 --- /dev/null +++ b/xbmc/addons/include/xbmc_audioenc_dll.h @@ -0,0 +1,61 @@ +#pragma once +/* + * Copyright (C) 2005-2013 Team XBMC + * http://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, see + * . + * + */ + +#ifndef __XBMC_AUDIOENC_H__ +#define __XBMC_AUDIOENC_H__ + +#include +#include "xbmc_addon_dll.h" +#include "xbmc_audioenc_types.h" + +extern "C" +{ + //! \copydoc AudioEncoder::Create + void* Create(audioenc_callbacks *callbacks); + + //! \copydoc AudioEncoder::Start + bool Start(void* context, int iInChannels, int iInRate, int iInBits, + const char* title, const char* artist, + const char* albumartist, const char* album, + const char* year, const char* track, + const char* genre, const char* comment, int iTrackLength); + + //! \copydoc AudioEncoder::Encode + int Encode(void* context, int nNumBytesRead, uint8_t* pbtStream); + + //! \copydoc AudioEncoder::Finish + bool Finish(void* context); + + //! \copydoc AudioEncoder::Free + void Free(void* context); + + // function to export the above structure to XBMC + void __declspec(dllexport) get_addon(struct AudioEncoder* pScr) + { + pScr->Create = Create; + pScr->Start = Start; + pScr->Encode = Encode; + pScr->Finish = Finish; + pScr->Free = Free; + }; +}; + +#endif diff --git a/xbmc/addons/include/xbmc_audioenc_types.h b/xbmc/addons/include/xbmc_audioenc_types.h new file mode 100644 index 0000000..aa527db --- /dev/null +++ b/xbmc/addons/include/xbmc_audioenc_types.h @@ -0,0 +1,113 @@ +#pragma once +/* + * Copyright (C) 2005-2013 Team XBMC + * http://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, see + * . + * + */ + +#ifndef __AUDIOENC_TYPES_H__ +#define __AUDIOENC_TYPES_H__ + +#ifdef TARGET_WINDOWS +#include +#else +#ifndef __cdecl +#define __cdecl +#endif +#ifndef __declspec +#define __declspec(X) +#endif +#endif + +#include + +extern "C" +{ + struct AUDIOENC_INFO + { + int dummy; + }; + + struct AUDIOENC_PROPS + { + int dummy; + }; + + typedef int (*audioenc_write_callback)(void* opaque, uint8_t* data, int len); + typedef int64_t (*audioenc_seek_callback)(void* opaque, int64_t pos, int whence); + + typedef struct + { + void* opaque; + audioenc_write_callback write; + audioenc_seek_callback seek; + } audioenc_callbacks; + + struct AudioEncoder + { + /*! \brief Create encoder context + \param callbacks Pointer to audioenc_callbacks structure. + \return opaque pointer to encoder context, to be passed to other methods. + \sa IEncoder::Init + */ + void (*(__cdecl *Create) (audioenc_callbacks* callbacks)); + + /*! \brief Start encoder + \param context Encoder context from Create. + \param iInChannels Number of channels + \param iInRate Sample rate of input data + \param iInBits Bits per sample in input data + \param title The title of the song + \param artist The artist of the song + \param albumartist The albumartist of the song + \param year The year of the song + \param track The track number of the song + \param genre The genre of the song + \param comment A comment to attach to the song + \param iTrackLength Total track length in seconds + \sa IEncoder::Init + */ + bool (__cdecl* Start) (void* context, int iInChannels, int iInRate, int iInBits, + const char* title, const char* artist, + const char* albumartist, const char* album, + const char* year, const char* track, + const char* genre, const char* comment, + int iTrackLength); + + /*! \brief Encode a chunk of audio + \param context Encoder context from Create. + \param nNumBytesRead Number of bytes in input buffer + \param pbtStream the input buffer + \return Number of bytes consumed + \sa IEncoder::Encode + */ + int (__cdecl* Encode) (void* context, int nNumBytesRead, uint8_t* pbtStream); + + /*! \brief Finalize encoding + \param context Encoder context from Create. + \return True on success, false on failure. + */ + bool (__cdecl* Finish) (void* context); + + /*! \brief Free encoder context + \param context Encoder context to free. + */ + void (__cdecl* Free)(void* context); + }; +} + +#endif diff --git a/xbmc/addons/include/xbmc_codec_types.h b/xbmc/addons/include/xbmc_codec_types.h new file mode 100644 index 0000000..98003e0 --- /dev/null +++ b/xbmc/addons/include/xbmc_codec_types.h @@ -0,0 +1,55 @@ +#ifndef __XBMC_CODEC_TYPES_H__ +#define __XBMC_CODEC_TYPES_H__ + +/* + * Copyright (C) 2005-2013 Team XBMC + * http://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, see + * . + * + */ + +#ifdef __cplusplus +extern "C" { +#endif + +typedef unsigned int xbmc_codec_id_t; + +typedef enum +{ + XBMC_CODEC_TYPE_UNKNOWN = -1, + XBMC_CODEC_TYPE_VIDEO, + XBMC_CODEC_TYPE_AUDIO, + XBMC_CODEC_TYPE_DATA, + XBMC_CODEC_TYPE_SUBTITLE, + XBMC_CODEC_TYPE_RDS, + XBMC_CODEC_TYPE_NB +} xbmc_codec_type_t; + +typedef struct +{ + xbmc_codec_type_t codec_type; + xbmc_codec_id_t codec_id; +} xbmc_codec_t; + +#define XBMC_INVALID_CODEC_ID 0 +#define XBMC_INVALID_CODEC { XBMC_CODEC_TYPE_UNKNOWN, XBMC_INVALID_CODEC_ID } + +#ifdef __cplusplus +}; +#endif + +#endif + diff --git a/xbmc/addons/include/xbmc_epg_types.h b/xbmc/addons/include/xbmc_epg_types.h new file mode 100644 index 0000000..97cea40 --- /dev/null +++ b/xbmc/addons/include/xbmc_epg_types.h @@ -0,0 +1,96 @@ +#pragma once +/* + * Copyright (C) 2005-2013 Team XBMC + * http://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, see + * . + * + */ + +#include +#include + +#undef ATTRIBUTE_PACKED +#undef PRAGMA_PACK_BEGIN +#undef PRAGMA_PACK_END + +#if defined(__GNUC__) +#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 95) +#define ATTRIBUTE_PACKED __attribute__ ((packed)) +#define PRAGMA_PACK 0 +#endif +#endif + +#if !defined(ATTRIBUTE_PACKED) +#define ATTRIBUTE_PACKED +#define PRAGMA_PACK 1 +#endif + +/*! @name EPG entry content event types */ +//@{ +/* These IDs come from the DVB-SI EIT table "content descriptor" + * Also known under the name "E-book genre assignments" + */ +#define EPG_EVENT_CONTENTMASK_UNDEFINED 0x00 +#define EPG_EVENT_CONTENTMASK_MOVIEDRAMA 0x10 +#define EPG_EVENT_CONTENTMASK_NEWSCURRENTAFFAIRS 0x20 +#define EPG_EVENT_CONTENTMASK_SHOW 0x30 +#define EPG_EVENT_CONTENTMASK_SPORTS 0x40 +#define EPG_EVENT_CONTENTMASK_CHILDRENYOUTH 0x50 +#define EPG_EVENT_CONTENTMASK_MUSICBALLETDANCE 0x60 +#define EPG_EVENT_CONTENTMASK_ARTSCULTURE 0x70 +#define EPG_EVENT_CONTENTMASK_SOCIALPOLITICALECONOMICS 0x80 +#define EPG_EVENT_CONTENTMASK_EDUCATIONALSCIENCE 0x90 +#define EPG_EVENT_CONTENTMASK_LEISUREHOBBIES 0xA0 +#define EPG_EVENT_CONTENTMASK_SPECIAL 0xB0 +#define EPG_EVENT_CONTENTMASK_USERDEFINED 0xF0 +//@} + +/* Set EPGTAG.iGenreType to EPG_GENRE_USE_STRING to transfer genre strings to XBMC */ +#define EPG_GENRE_USE_STRING 0x100 + +#ifdef __cplusplus +extern "C" { +#endif + + /*! + * @brief Representation of an EPG event. + */ + typedef struct EPG_TAG { + unsigned int iUniqueBroadcastId; /*!< @brief (required) identifier for this event */ + const char * strTitle; /*!< @brief (required) this event's title */ + unsigned int iChannelNumber; /*!< @brief (required) the number of the channel this event occurs on */ + time_t startTime; /*!< @brief (required) start time in UTC */ + time_t endTime; /*!< @brief (required) end time in UTC */ + const char * strPlotOutline; /*!< @brief (optional) plot outline */ + const char * strPlot; /*!< @brief (optional) plot */ + const char * strIconPath; /*!< @brief (optional) icon path */ + int iGenreType; /*!< @brief (optional) genre type */ + int iGenreSubType; /*!< @brief (optional) genre sub type */ + const char * strGenreDescription; /*!< @brief (optional) genre. Will be used only when iGenreType = EPG_GENRE_USE_STRING */ + time_t firstAired; /*!< @brief (optional) first aired in UTC */ + int iParentalRating; /*!< @brief (optional) parental rating */ + int iStarRating; /*!< @brief (optional) star rating */ + bool bNotify; /*!< @brief (optional) notify the user when this event starts */ + int iSeriesNumber; /*!< @brief (optional) series number */ + int iEpisodeNumber; /*!< @brief (optional) episode number */ + int iEpisodePartNumber; /*!< @brief (optional) episode part number */ + const char * strEpisodeName; /*!< @brief (optional) episode name */ + const char * strRecordingId; /*!< @brief (optional) unique id of the recording on the client which represents this event */ + } ATTRIBUTE_PACKED EPG_TAG; + +#ifdef __cplusplus +} +#endif diff --git a/xbmc/addons/include/xbmc_pvr_dll.h b/xbmc/addons/include/xbmc_pvr_dll.h new file mode 100644 index 0000000..3ad46fc --- /dev/null +++ b/xbmc/addons/include/xbmc_pvr_dll.h @@ -0,0 +1,710 @@ +/* + * Copyright (C) 2005-2013 Team XBMC + * http://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, see + * . + * + */ + +#ifndef __XBMC_PVR_H__ +#define __XBMC_PVR_H__ + +#include "xbmc_addon_dll.h" +#include "xbmc_pvr_types.h" + +/*! + * Functions that the PVR client add-on must implement, but some can be empty. + * + * The 'remarks' field indicates which methods should be implemented, and which ones are optional. + */ + +extern "C" +{ + /*! @name PVR add-on methods */ + //@{ + /*! + * Get the XBMC_PVR_API_VERSION that was used to compile this add-on. + * Used to check if this add-on is compatible with XBMC. + * @return The XBMC_PVR_API_VERSION that was used to compile this add-on. + * @remarks Valid implementation required. + */ + const char* GetPVRAPIVersion(void); + + /*! + * Get the XBMC_PVR_MIN_API_VERSION that was used to compile this add-on. + * Used to check if this add-on is compatible with XBMC. + * @return The XBMC_PVR_MIN_API_VERSION that was used to compile this add-on. + * @remarks Valid implementation required. + */ + const char* GetMininumPVRAPIVersion(void); + + /*! + * Get the XBMC_GUI_API_VERSION that was used to compile this add-on. + * Used to check if this add-on is compatible with XBMC. + * @return The XBMC_GUI_API_VERSION that was used to compile this add-on. + * @remarks Valid implementation required. + */ + const char* GetGUIAPIVersion(void); + + /*! + * Get the XBMC_GUI_MIN_API_VERSION that was used to compile this add-on. + * Used to check if this add-on is compatible with XBMC. + * @return The XBMC_GUI_MIN_API_VERSION that was used to compile this add-on. + * @remarks Valid implementation required. + */ + const char* GetMininumGUIAPIVersion(void); + + /*! + * Get the list of features that this add-on provides. + * Called by XBMC to query the add-on's capabilities. + * Used to check which options should be presented in the UI, which methods to call, etc. + * All capabilities that the add-on supports should be set to true. + * @param pCapabilities The add-on's capabilities. + * @return PVR_ERROR_NO_ERROR if the properties were fetched successfully. + * @remarks Valid implementation required. + */ + PVR_ERROR GetAddonCapabilities(PVR_ADDON_CAPABILITIES *pCapabilities); + + /*! + * @return The name reported by the backend that will be displayed in the UI. + * @remarks Valid implementation required. + */ + const char* GetBackendName(void); + + /*! + * @return The version string reported by the backend that will be displayed in the UI. + * @remarks Valid implementation required. + */ + const char* GetBackendVersion(void); + + /*! + * @return The connection string reported by the backend that will be displayed in the UI. + * @remarks Valid implementation required. + */ + const char* GetConnectionString(void); + + /*! + * Get the disk space reported by the backend (if supported). + * @param iTotal The total disk space in bytes. + * @param iUsed The used disk space in bytes. + * @return PVR_ERROR_NO_ERROR if the drive space has been fetched successfully. + * @remarks Optional. Return PVR_ERROR_NOT_IMPLEMENTED if this add-on won't provide this function. + */ + PVR_ERROR GetDriveSpace(long long* iTotal, long long* iUsed); + + /*! + * Call one of the menu hooks (if supported). + * Supported PVR_MENUHOOK instances have to be added in ADDON_Create(), by calling AddMenuHook() on the callback. + * @param menuhook The hook to call. + * @param item The selected item for which the hook was called. + * @return PVR_ERROR_NO_ERROR if the hook was called successfully. + * @remarks Optional. Return PVR_ERROR_NOT_IMPLEMENTED if this add-on won't provide this function. + */ + PVR_ERROR CallMenuHook(const PVR_MENUHOOK& menuhook, const PVR_MENUHOOK_DATA &item); + //@} + + /*! @name PVR EPG methods + * @remarks Only used by XBMC if bSupportsEPG is set to true. + */ + //@{ + /*! + * Request the EPG for a channel from the backend. + * EPG entries are added to XBMC by calling TransferEpgEntry() on the callback. + * @param handle Handle to pass to the callback method. + * @param channel The channel to get the EPG table for. + * @param iStart Get events after this time (UTC). + * @param iEnd Get events before this time (UTC). + * @return PVR_ERROR_NO_ERROR if the table has been fetched successfully. + * @remarks Required if bSupportsEPG is set to true. Return PVR_ERROR_NOT_IMPLEMENTED if this add-on won't provide this function. + */ + PVR_ERROR GetEPGForChannel(ADDON_HANDLE handle, const PVR_CHANNEL& channel, time_t iStart, time_t iEnd); + //@} + + /*! @name PVR channel group methods + * @remarks Only used by XBMC is bSupportsChannelGroups is set to true. + * If a group or one of the group members changes after the initial import, or if a new one was added, then the add-on + * should call TriggerChannelGroupsUpdate() + */ + //@{ + /*! + * Get the total amount of channel groups on the backend if it supports channel groups. + * @return The amount of channels, or -1 on error. + * @remarks Required if bSupportsChannelGroups is set to true. Return -1 if this add-on won't provide this function. + */ + int GetChannelGroupsAmount(void); + + /*! + * Request the list of all channel groups from the backend if it supports channel groups. + * Channel group entries are added to XBMC by calling TransferChannelGroup() on the callback. + * @param handle Handle to pass to the callback method. + * @param bRadio True to get the radio channel groups, false to get the TV channel groups. + * @return PVR_ERROR_NO_ERROR if the list has been fetched successfully. + * @remarks Required if bSupportsChannelGroups is set to true. Return PVR_ERROR_NOT_IMPLEMENTED if this add-on won't provide this function. + */ + PVR_ERROR GetChannelGroups(ADDON_HANDLE handle, bool bRadio); + + /*! + * Request the list of all group members of a group from the backend if it supports channel groups. + * Member entries are added to XBMC by calling TransferChannelGroupMember() on the callback. + * @param handle Handle to pass to the callback method. + * @param group The group to get the members for. + * @return PVR_ERROR_NO_ERROR if the list has been fetched successfully. + * @remarks Required if bSupportsChannelGroups is set to true. Return PVR_ERROR_NOT_IMPLEMENTED if this add-on won't provide this function. + */ + PVR_ERROR GetChannelGroupMembers(ADDON_HANDLE handle, const PVR_CHANNEL_GROUP& group); + //@} + + /** @name PVR channel methods + * @remarks Either bSupportsTV or bSupportsRadio is required to be set to true. + * If a channel changes after the initial import, or if a new one was added, then the add-on + * should call TriggerChannelUpdate() + */ + //@{ + /*! + * Show the channel scan dialog if this backend supports it. + * @return PVR_ERROR_NO_ERROR if the dialog was displayed successfully. + * @remarks Required if bSupportsChannelScan is set to true. Return PVR_ERROR_NOT_IMPLEMENTED if this add-on won't provide this function. + * @note see libXBMC_gui.h about related parts + */ + PVR_ERROR OpenDialogChannelScan(void); + + /*! + * @return The total amount of channels on the backend, or -1 on error. + * @remarks Valid implementation required. + */ + int GetChannelsAmount(void); + + /*! + * Request the list of all channels from the backend. + * Channel entries are added to XBMC by calling TransferChannelEntry() on the callback. + * @param handle Handle to pass to the callback method. + * @param bRadio True to get the radio channels, false to get the TV channels. + * @return PVR_ERROR_NO_ERROR if the list has been fetched successfully. + * @remarks If bSupportsTV is set to true, a valid result set needs to be provided for bRadio = false. + * If bSupportsRadio is set to true, a valid result set needs to be provided for bRadio = true. + * At least one of these two must provide a valid result set. + */ + PVR_ERROR GetChannels(ADDON_HANDLE handle, bool bRadio); + + /*! + * Delete a channel from the backend. + * @param channel The channel to delete. + * @return PVR_ERROR_NO_ERROR if the channel has been deleted successfully. + * @remarks Required if bSupportsChannelSettings is set to true. + */ + PVR_ERROR DeleteChannel(const PVR_CHANNEL& channel); + + /*! + * Rename a channel on the backend. + * @param channel The channel to rename, containing the new channel name. + * @return PVR_ERROR_NO_ERROR if the channel has been renamed successfully. + * @remarks Optional, and only used if bSupportsChannelSettings is set to true. Return PVR_ERROR_NOT_IMPLEMENTED if this add-on won't provide this function. + */ + PVR_ERROR RenameChannel(const PVR_CHANNEL& channel); + + /*! + * Move a channel to another channel number on the backend. + * @param channel The channel to move, containing the new channel number. + * @return PVR_ERROR_NO_ERROR if the channel has been moved successfully. + * @remarks Optional, and only used if bSupportsChannelSettings is set to true. Return PVR_ERROR_NOT_IMPLEMENTED if this add-on won't provide this function. + */ + PVR_ERROR MoveChannel(const PVR_CHANNEL& channel); + + /*! + * Show the channel settings dialog, if supported by the backend. + * @param channel The channel to show the dialog for. + * @return PVR_ERROR_NO_ERROR if the dialog has been displayed successfully. + * @remarks Required if bSupportsChannelSettings is set to true. + * @note see libXBMC_gui.h about related parts + */ + PVR_ERROR OpenDialogChannelSettings(const PVR_CHANNEL& channel); + + /*! + * Show the dialog to add a channel on the backend, if supported by the backend. + * @param channel The channel to add. + * @return PVR_ERROR_NO_ERROR if the channel has been added successfully. + * @remarks Required if bSupportsChannelSettings is set to true. + * @note see libXBMC_gui.h about related parts + */ + PVR_ERROR OpenDialogChannelAdd(const PVR_CHANNEL& channel); + //@} + + /** @name PVR recording methods + * @remarks Only used by XBMC is bSupportsRecordings is set to true. + * If a recording changes after the initial import, or if a new one was added, + * then the add-on should call TriggerRecordingUpdate() + */ + //@{ + /*! + * @return The total amount of recordings on the backend or -1 on error. + * @param deleted if set return deleted recording (called if bSupportsRecordingsUndelete set to true) + * @remarks Required if bSupportsRecordings is set to true. Return PVR_ERROR_NOT_IMPLEMENTED if this add-on won't provide this function. + */ + int GetRecordingsAmount(bool deleted); + + /*! + * Request the list of all recordings from the backend, if supported. + * Recording entries are added to XBMC by calling TransferRecordingEntry() on the callback. + * @param handle Handle to pass to the callback method. + * @param deleted if set return deleted recording (called if bSupportsRecordingsUndelete set to true) + * @return PVR_ERROR_NO_ERROR if the recordings have been fetched successfully. + * @remarks Required if bSupportsRecordings is set to true. Return PVR_ERROR_NOT_IMPLEMENTED if this add-on won't provide this function. + */ + PVR_ERROR GetRecordings(ADDON_HANDLE handle, bool deleted); + + /*! + * Delete a recording on the backend. + * @param recording The recording to delete. + * @return PVR_ERROR_NO_ERROR if the recording has been deleted successfully. + * @remarks Optional, and only used if bSupportsRecordings is set to true. Return PVR_ERROR_NOT_IMPLEMENTED if this add-on won't provide this function. + */ + PVR_ERROR DeleteRecording(const PVR_RECORDING& recording); + + /*! + * Undelete a recording on the backend. + * @param recording The recording to undelete. + * @return PVR_ERROR_NO_ERROR if the recording has been undeleted successfully. + * @remarks Optional, and only used if bSupportsRecordingsUndelete is set to true. Return PVR_ERROR_NOT_IMPLEMENTED if this add-on won't provide this function. + */ + PVR_ERROR UndeleteRecording(const PVR_RECORDING& recording); + + /*! + * @brief Delete all recordings permanent which in the deleted folder on the backend. + * @return PVR_ERROR_NO_ERROR if the recordings has been deleted successfully. + */ + PVR_ERROR DeleteAllRecordingsFromTrash(); + + /*! + * Rename a recording on the backend. + * @param recording The recording to rename, containing the new name. + * @return PVR_ERROR_NO_ERROR if the recording has been renamed successfully. + * @remarks Optional, and only used if bSupportsRecordings is set to true. Return PVR_ERROR_NOT_IMPLEMENTED if this add-on won't provide this function. + */ + PVR_ERROR RenameRecording(const PVR_RECORDING& recording); + + /*! + * Set the play count of a recording on the backend. + * @param recording The recording to change the play count. + * @param count Play count. + * @return PVR_ERROR_NO_ERROR if the recording's play count has been set successfully. + * @remarks Required if bSupportsRecordingPlayCount is set to true. Return PVR_ERROR_NOT_IMPLEMENTED if this add-on won't provide this function. + */ + PVR_ERROR SetRecordingPlayCount(const PVR_RECORDING& recording, int count); + + /*! + * Set the last watched position of a recording on the backend. + * @param recording The recording. + * @param position The last watched position in seconds + * @return PVR_ERROR_NO_ERROR if the position has been stored successfully. + * @remarks Required if bSupportsLastPlayedPosition is set to true. Return PVR_ERROR_NOT_IMPLEMENTED if this add-on won't provide this function. + */ + PVR_ERROR SetRecordingLastPlayedPosition(const PVR_RECORDING& recording, int lastplayedposition); + + /*! + * Retrieve the last watched position of a recording on the backend. + * @param recording The recording. + * @return The last watched position in seconds or -1 on error + * @remarks Required if bSupportsRecordingPlayCount is set to true. Return -1 if this add-on won't provide this function. + */ + int GetRecordingLastPlayedPosition(const PVR_RECORDING& recording); + + /*! + * Retrieve the edit decision list (EDL) of a recording on the backend. + * @param recording The recording. + * @param edl out: The function has to write the EDL list into this array. + * @param size in: The maximum size of the EDL, out: the actual size of the EDL. + * @return PVR_ERROR_NO_ERROR if the EDL was successfully read. + * @remarks Required if bSupportsRecordingEdl is set to true. Return PVR_ERROR_NOT_IMPLEMENTED if this add-on won't provide this function. + */ + PVR_ERROR GetRecordingEdl(const PVR_RECORDING&, PVR_EDL_ENTRY edl[], int *size); + + //@} + /** @name PVR timer methods + * @remarks Only used by XBMC is bSupportsTimers is set to true. + * If a timer changes after the initial import, or if a new one was added, + * then the add-on should call TriggerTimerUpdate() + */ + //@{ + /*! + * @return The total amount of timers on the backend or -1 on error. + * @remarks Required if bSupportsTimers is set to true. Return -1 if this add-on won't provide this function. + */ + int GetTimersAmount(void); + + /*! + * Request the list of all timers from the backend if supported. + * Timer entries are added to XBMC by calling TransferTimerEntry() on the callback. + * @param handle Handle to pass to the callback method. + * @return PVR_ERROR_NO_ERROR if the list has been fetched successfully. + * @remarks Required if bSupportsTimers is set to true. Return PVR_ERROR_NOT_IMPLEMENTED if this add-on won't provide this function. + */ + PVR_ERROR GetTimers(ADDON_HANDLE handle); + + /*! + * Add a timer on the backend. + * @param timer The timer to add. + * @return PVR_ERROR_NO_ERROR if the timer has been added successfully. + * @remarks Required if bSupportsTimers is set to true. Return PVR_ERROR_NOT_IMPLEMENTED if this add-on won't provide this function. + */ + PVR_ERROR AddTimer(const PVR_TIMER& timer); + + /*! + * Delete a timer on the backend. + * @param timer The timer to delete. + * @param bForceDelete Set to true to delete a timer that is currently recording a program. + * @return PVR_ERROR_NO_ERROR if the timer has been deleted successfully. + * @remarks Required if bSupportsTimers is set to true. Return PVR_ERROR_NOT_IMPLEMENTED if this add-on won't provide this function. + */ + PVR_ERROR DeleteTimer(const PVR_TIMER& timer, bool bForceDelete); + + /*! + * Update the timer information on the backend. + * @param timer The timer to update. + * @return PVR_ERROR_NO_ERROR if the timer has been updated successfully. + * @remarks Required if bSupportsTimers is set to true. Return PVR_ERROR_NOT_IMPLEMENTED if this add-on won't provide this function. + */ + PVR_ERROR UpdateTimer(const PVR_TIMER& timer); + + //@} + + /** @name PVR live stream methods, used to open and close a stream to a channel, and optionally perform read operations on the stream */ + //@{ + /*! + * Open a live stream on the backend. + * @param channel The channel to stream. + * @return True if the stream has been opened successfully, false otherwise. + * @remarks Required if bHandlesInputStream or bHandlesDemuxing is set to true. Return false if this add-on won't provide this function. + */ + bool OpenLiveStream(const PVR_CHANNEL& channel); + + /*! + * Close an open live stream. + * @remarks Required if bHandlesInputStream or bHandlesDemuxing is set to true. + */ + void CloseLiveStream(void); + + /*! + * Read from an open live stream. + * @param pBuffer The buffer to store the data in. + * @param iBufferSize The amount of bytes to read. + * @return The amount of bytes that were actually read from the stream. + * @remarks Required if bHandlesInputStream is set to true. Return -1 if this add-on won't provide this function. + */ + int ReadLiveStream(unsigned char* pBuffer, unsigned int iBufferSize); + + /*! + * Seek in a live stream on a backend that supports timeshifting. + * @param iPosition The position to seek to. + * @param iWhence ? + * @return The new position. + * @remarks Optional, and only used if bHandlesInputStream is set to true. Return -1 if this add-on won't provide this function. + */ + long long SeekLiveStream(long long iPosition, int iWhence = SEEK_SET); + + /*! + * @return The position in the stream that's currently being read. + * @remarks Optional, and only used if bHandlesInputStream is set to true. Return -1 if this add-on won't provide this function. + */ + long long PositionLiveStream(void); + + /*! + * @return The total length of the stream that's currently being read. + * @remarks Optional, and only used if bHandlesInputStream is set to true. Return -1 if this add-on won't provide this function. + */ + long long LengthLiveStream(void); + + /*! + * @return The channel number on the backend of the live stream that's currently being read. + * @remarks Required if bHandlesInputStream or bHandlesDemuxing is set to true. Return -1 if this add-on won't provide this function. + */ + int GetCurrentClientChannel(void); + + /*! + * Switch to another channel. Only to be called when a live stream has already been opened. + * @param channel The channel to switch to. + * @return True if the switch was successful, false otherwise. + * @remarks Required if bHandlesInputStream or bHandlesDemuxing is set to true. Return false if this add-on won't provide this function. + */ + bool SwitchChannel(const PVR_CHANNEL& channel); + + /*! + * Get the signal status of the stream that's currently open. + * @param signalStatus The signal status. + * @return True if the signal status has been read successfully, false otherwise. + * @remarks Optional, and only used if bHandlesInputStream or bHandlesDemuxing is set to true. Return PVR_ERROR_NOT_IMPLEMENTED if this add-on won't provide this function. + */ + PVR_ERROR SignalStatus(PVR_SIGNAL_STATUS& signalStatus); + + /*! + * Get the stream URL for a channel from the backend. Used by the MediaPortal add-on. + * @param channel The channel to get the stream URL for. + * @return The requested URL. + * @remarks Optional, and only used if bHandlesInputStream is set to true. Return NULL if this add-on won't provide this function. + */ + const char* GetLiveStreamURL(const PVR_CHANNEL& channel); + + /*! + * Get the stream properties of the stream that's currently being read. + * @param pProperties The properties of the currently playing stream. + * @return PVR_ERROR_NO_ERROR if the properties have been fetched successfully. + * @remarks Required if bHandlesInputStream or bHandlesDemuxing is set to true. Return PVR_ERROR_NOT_IMPLEMENTED if this add-on won't provide this function. + */ + PVR_ERROR GetStreamProperties(PVR_STREAM_PROPERTIES* pProperties); + //@} + + /** @name PVR recording stream methods, used to open and close a stream to a recording, and perform read operations on the stream. + * @remarks This will only be used if the backend doesn't provide a direct URL in the recording tag. + */ + //@{ + /*! + * Open a stream to a recording on the backend. + * @param recording The recording to open. + * @return True if the stream has been opened successfully, false otherwise. + * @remarks Optional, and only used if bSupportsRecordings is set to true. Return false if this add-on won't provide this function. + */ + bool OpenRecordedStream(const PVR_RECORDING& recording); + + /*! + * Close an open stream from a recording. + * @remarks Optional, and only used if bSupportsRecordings is set to true. + */ + void CloseRecordedStream(void); + + /*! + * Read from a recording. + * @param pBuffer The buffer to store the data in. + * @param iBufferSize The amount of bytes to read. + * @return The amount of bytes that were actually read from the stream. + * @remarks Optional, and only used if bSupportsRecordings is set to true, but required if OpenRecordedStream() is implemented. Return -1 if this add-on won't provide this function. + */ + int ReadRecordedStream(unsigned char* pBuffer, unsigned int iBufferSize); + + /*! + * Seek in a recorded stream. + * @param iPosition The position to seek to. + * @param iWhence ? + * @return The new position. + * @remarks Optional, and only used if bSupportsRecordings is set to true. Return -1 if this add-on won't provide this function. + */ + long long SeekRecordedStream(long long iPosition, int iWhence = SEEK_SET); + + /*! + * @return The position in the stream that's currently being read. + * @remarks Optional, and only used if bSupportsRecordings is set to true. Return -1 if this add-on won't provide this function. + */ + long long PositionRecordedStream(void); + + /*! + * @return The total length of the stream that's currently being read. + * @remarks Optional, and only used if bSupportsRecordings is set to true. Return -1 if this add-on won't provide this function. + */ + long long LengthRecordedStream(void); + //@} + + /** @name PVR demultiplexer methods + * @remarks Only used by XBMC is bHandlesDemuxing is set to true. + */ + //@{ + /*! + * Reset the demultiplexer in the add-on. + * @remarks Required if bHandlesDemuxing is set to true. + */ + void DemuxReset(void); + + /*! + * Abort the demultiplexer thread in the add-on. + * @remarks Required if bHandlesDemuxing is set to true. + */ + void DemuxAbort(void); + + /*! + * Flush all data that's currently in the demultiplexer buffer in the add-on. + * @remarks Required if bHandlesDemuxing is set to true. + */ + void DemuxFlush(void); + + /*! + * Read the next packet from the demultiplexer, if there is one. + * @return The next packet. + * If there is no next packet, then the add-on should return the + * packet created by calling AllocateDemuxPacket(0) on the callback. + * If the stream changed and XBMC's player needs to be reinitialised, + * then, the add-on should call AllocateDemuxPacket(0) on the + * callback, and set the streamid to DMX_SPECIALID_STREAMCHANGE and + * return the value. + * The add-on should return NULL if an error occured. + * @remarks Required if bHandlesDemuxing is set to true. Return NULL if this add-on won't provide this function. + */ + DemuxPacket* DemuxRead(void); + //@} + + /*! + * Delay to use when using switching channels for add-ons not providing an input stream. + * If the add-on does provide an input stream, then this method will not be called. + * Those add-ons can do that in OpenLiveStream() if needed. + * @return The delay in milliseconds. + */ + unsigned int GetChannelSwitchDelay(void); + + /*! + * Check if the backend support pausing the currently playing stream + * This will enable/disable the pause button in XBMC based on the return value + * @return false if the PVR addon/backend does not support pausing, true if possible + */ + bool CanPauseStream(); + + /*! + * Check if the backend supports seeking for the currently playing stream + * This will enable/disable the rewind/forward buttons in XBMC based on the return value + * @return false if the PVR addon/backend does not support seeking, true if possible + */ + bool CanSeekStream(); + + /*! + * @brief Notify the pvr addon that XBMC (un)paused the currently playing stream + */ + void PauseStream(bool bPaused); + + /*! + * Notify the pvr addon/demuxer that XBMC wishes to seek the stream by time + * @param time The absolute time since stream start + * @param backwards True to seek to keyframe BEFORE time, else AFTER + * @param startpts can be updated to point to where display should start + * @return True if the seek operation was possible + * @remarks Optional, and only used if addon has its own demuxer. Return False if this add-on won't provide this function. + */ + bool SeekTime(int time, bool backwards, double *startpts); + + /*! + * Notify the pvr addon/demuxer that XBMC wishes to change playback speed + * @param speed The requested playback speed + * @remarks Optional, and only used if addon has its own demuxer. + */ + void SetSpeed(int speed); + + /*! + * Get actual playing time from addon. With timeshift enabled this is + * different to live. + * @return time as UTC + */ + time_t GetPlayingTime(); + + /*! + * Get time of oldest packet in timeshift buffer + * @return time as UTC + */ + time_t GetBufferTimeStart(); + + /*! + * Get time of latest packet in timeshift buffer + * @return time as UTC + */ + time_t GetBufferTimeEnd(); + + /*! + * Get the hostname of the pvr backend server + * @return hostname as ip address or alias. If backend does not + * utilize a server, return empty string. + */ + const char* GetBackendHostname(); + + /*! + * Called by XBMC to assign the function pointers of this add-on to pClient. + * @param pClient The struct to assign the function pointers to. + */ + void __declspec(dllexport) get_addon(struct PVRClient* pClient) + { + pClient->GetPVRAPIVersion = GetPVRAPIVersion; + pClient->GetMininumPVRAPIVersion = GetMininumPVRAPIVersion; + pClient->GetGUIAPIVersion = GetGUIAPIVersion; + pClient->GetMininumGUIAPIVersion = GetMininumGUIAPIVersion; + pClient->GetAddonCapabilities = GetAddonCapabilities; + pClient->GetStreamProperties = GetStreamProperties; + pClient->GetConnectionString = GetConnectionString; + pClient->GetBackendName = GetBackendName; + pClient->GetBackendVersion = GetBackendVersion; + pClient->GetDriveSpace = GetDriveSpace; + pClient->OpenDialogChannelScan = OpenDialogChannelScan; + pClient->MenuHook = CallMenuHook; + + pClient->GetEpg = GetEPGForChannel; + + pClient->GetChannelGroupsAmount = GetChannelGroupsAmount; + pClient->GetChannelGroups = GetChannelGroups; + pClient->GetChannelGroupMembers = GetChannelGroupMembers; + + pClient->GetChannelsAmount = GetChannelsAmount; + pClient->GetChannels = GetChannels; + pClient->DeleteChannel = DeleteChannel; + pClient->RenameChannel = RenameChannel; + pClient->MoveChannel = MoveChannel; + pClient->OpenDialogChannelSettings = OpenDialogChannelSettings; + pClient->OpenDialogChannelAdd = OpenDialogChannelAdd; + + pClient->GetRecordingsAmount = GetRecordingsAmount; + pClient->GetRecordings = GetRecordings; + pClient->DeleteRecording = DeleteRecording; + pClient->UndeleteRecording = UndeleteRecording; + pClient->DeleteAllRecordingsFromTrash = DeleteAllRecordingsFromTrash; + pClient->RenameRecording = RenameRecording; + pClient->SetRecordingPlayCount = SetRecordingPlayCount; + pClient->SetRecordingLastPlayedPosition = SetRecordingLastPlayedPosition; + pClient->GetRecordingLastPlayedPosition = GetRecordingLastPlayedPosition; + pClient->GetRecordingEdl = GetRecordingEdl; + + pClient->GetTimersAmount = GetTimersAmount; + pClient->GetTimers = GetTimers; + pClient->AddTimer = AddTimer; + pClient->DeleteTimer = DeleteTimer; + pClient->UpdateTimer = UpdateTimer; + + pClient->OpenLiveStream = OpenLiveStream; + pClient->CloseLiveStream = CloseLiveStream; + pClient->ReadLiveStream = ReadLiveStream; + pClient->SeekLiveStream = SeekLiveStream; + pClient->PositionLiveStream = PositionLiveStream; + pClient->LengthLiveStream = LengthLiveStream; + pClient->GetCurrentClientChannel = GetCurrentClientChannel; + pClient->SwitchChannel = SwitchChannel; + pClient->SignalStatus = SignalStatus; + pClient->GetLiveStreamURL = GetLiveStreamURL; + pClient->GetChannelSwitchDelay = GetChannelSwitchDelay; + pClient->CanPauseStream = CanPauseStream; + pClient->PauseStream = PauseStream; + pClient->CanSeekStream = CanSeekStream; + pClient->SeekTime = SeekTime; + pClient->SetSpeed = SetSpeed; + + pClient->OpenRecordedStream = OpenRecordedStream; + pClient->CloseRecordedStream = CloseRecordedStream; + pClient->ReadRecordedStream = ReadRecordedStream; + pClient->SeekRecordedStream = SeekRecordedStream; + pClient->PositionRecordedStream = PositionRecordedStream; + pClient->LengthRecordedStream = LengthRecordedStream; + + pClient->DemuxReset = DemuxReset; + pClient->DemuxAbort = DemuxAbort; + pClient->DemuxFlush = DemuxFlush; + pClient->DemuxRead = DemuxRead; + + pClient->GetPlayingTime = GetPlayingTime; + pClient->GetBufferTimeStart = GetBufferTimeStart; + pClient->GetBufferTimeEnd = GetBufferTimeEnd; + + pClient->GetBackendHostname = GetBackendHostname; + }; +}; + +#endif diff --git a/xbmc/addons/include/xbmc_pvr_types.h b/xbmc/addons/include/xbmc_pvr_types.h new file mode 100644 index 0000000..5285bd1 --- /dev/null +++ b/xbmc/addons/include/xbmc_pvr_types.h @@ -0,0 +1,415 @@ +#pragma once +/* + * Copyright (C) 2005-2013 Team XBMC + * http://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, see + * . + * + */ + +#ifndef __PVRCLIENT_TYPES_H__ +#define __PVRCLIENT_TYPES_H__ + +#ifdef TARGET_WINDOWS +#include +#else +#ifndef __cdecl +#define __cdecl +#endif +#ifndef __declspec +#define __declspec(X) +#endif +#endif +#include +#include + +#include "xbmc_addon_types.h" +#include "xbmc_epg_types.h" +#include "xbmc_codec_types.h" + +/*! @note Define "USE_DEMUX" at compile time if demuxing in the PVR add-on is used. + * Also XBMC's "DVDDemuxPacket.h" file must be in the include path of the add-on, + * and the add-on should set bHandlesDemuxing to true. + */ +#ifdef USE_DEMUX +#include "DVDDemuxPacket.h" +#else +struct DemuxPacket; +#endif + +#undef ATTRIBUTE_PACKED +#undef PRAGMA_PACK_BEGIN +#undef PRAGMA_PACK_END + +#if defined(__GNUC__) +#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 95) +#define ATTRIBUTE_PACKED __attribute__ ((packed)) +#define PRAGMA_PACK 0 +#endif +#endif + +#if !defined(ATTRIBUTE_PACKED) +#define ATTRIBUTE_PACKED +#define PRAGMA_PACK 1 +#endif + +#define PVR_ADDON_NAME_STRING_LENGTH 1024 +#define PVR_ADDON_URL_STRING_LENGTH 1024 +#define PVR_ADDON_DESC_STRING_LENGTH 1024 +#define PVR_ADDON_INPUT_FORMAT_STRING_LENGTH 32 +#define PVR_ADDON_EDL_LENGTH 32 + +/* using the default avformat's MAX_STREAMS value to be safe */ +#define PVR_STREAM_MAX_STREAMS 20 + +/* current PVR API version */ +#define XBMC_PVR_API_VERSION "1.9.4" + +/* min. PVR API version */ +#define XBMC_PVR_MIN_API_VERSION "1.9.4" + +#ifdef __cplusplus +extern "C" { +#endif + + /*! + * @brief PVR add-on error codes + */ + typedef enum + { + PVR_ERROR_NO_ERROR = 0, /*!< @brief no error occurred */ + PVR_ERROR_UNKNOWN = -1, /*!< @brief an unknown error occurred */ + PVR_ERROR_NOT_IMPLEMENTED = -2, /*!< @brief the method that XBMC called is not implemented by the add-on */ + PVR_ERROR_SERVER_ERROR = -3, /*!< @brief the backend reported an error, or the add-on isn't connected */ + PVR_ERROR_SERVER_TIMEOUT = -4, /*!< @brief the command was sent to the backend, but the response timed out */ + PVR_ERROR_REJECTED = -5, /*!< @brief the command was rejected by the backend */ + PVR_ERROR_ALREADY_PRESENT = -6, /*!< @brief the requested item can not be added, because it's already present */ + PVR_ERROR_INVALID_PARAMETERS = -7, /*!< @brief the parameters of the method that was called are invalid for this operation */ + PVR_ERROR_RECORDING_RUNNING = -8, /*!< @brief a recording is running, so the timer can't be deleted without doing a forced delete */ + PVR_ERROR_FAILED = -9, /*!< @brief the command failed */ + } PVR_ERROR; + + /*! + * @brief PVR timer states + */ + typedef enum + { + PVR_TIMER_STATE_NEW = 0, /*!< @brief a new, unsaved timer */ + PVR_TIMER_STATE_SCHEDULED = 1, /*!< @brief the timer is scheduled for recording */ + PVR_TIMER_STATE_RECORDING = 2, /*!< @brief the timer is currently recordings */ + PVR_TIMER_STATE_COMPLETED = 3, /*!< @brief the recording completed successfully */ + PVR_TIMER_STATE_ABORTED = 4, /*!< @brief recording started, but was aborted */ + PVR_TIMER_STATE_CANCELLED = 5, /*!< @brief the timer was scheduled, but was canceled */ + PVR_TIMER_STATE_CONFLICT_OK = 6, /*!< @brief the scheduled timer conflicts with another one, but will be recorded */ + PVR_TIMER_STATE_CONFLICT_NOK = 7, /*!< @brief the scheduled timer conflicts with another one and won't be recorded */ + PVR_TIMER_STATE_ERROR = 8 /*!< @brief the timer is scheduled, but can't be recorded for some reason */ + } PVR_TIMER_STATE; + + /*! + * @brief PVR menu hook categories + */ + typedef enum + { + PVR_MENUHOOK_UNKNOWN =-1, /*!< @brief unknown menu hook */ + PVR_MENUHOOK_ALL = 0, /*!< @brief all categories */ + PVR_MENUHOOK_CHANNEL = 1, /*!< @brief for channels */ + PVR_MENUHOOK_TIMER = 2, /*!< @brief for timers */ + PVR_MENUHOOK_EPG = 3, /*!< @brief for EPG */ + PVR_MENUHOOK_RECORDING = 4, /*!< @brief for recordings */ + PVR_MENUHOOK_DELETED_RECORDING = 5, /*!< @brief for deleted recordings */ + PVR_MENUHOOK_SETTING = 6, /*!< @brief for settings */ + } PVR_MENUHOOK_CAT; + + /*! + * @brief Properties passed to the Create() method of an add-on. + */ + typedef struct PVR_PROPERTIES + { + const char* strUserPath; /*!< @brief path to the user profile */ + const char* strClientPath; /*!< @brief path to this add-on */ + } PVR_PROPERTIES; + + /*! + * @brief PVR add-on capabilities. All capabilities are set to "false" as default. + * If a capabilty is set to true, then the corresponding methods from xbmc_pvr_dll.h need to be implemented. + */ + typedef struct PVR_ADDON_CAPABILITIES + { + bool bSupportsEPG; /*!< @brief true if the add-on provides EPG information */ + bool bSupportsTV; /*!< @brief true if this add-on provides TV channels */ + bool bSupportsRadio; /*!< @brief true if this add-on supports radio channels */ + bool bSupportsRecordings; /*!< @brief true if this add-on supports playback of recordings stored on the backend */ + bool bSupportsRecordingsUndelete; /*!< @brief true if this add-on supports undelete of recordings stored on the backend */ + bool bSupportsTimers; /*!< @brief true if this add-on supports the creation and editing of timers */ + bool bSupportsChannelGroups; /*!< @brief true if this add-on supports channel groups */ + bool bSupportsChannelScan; /*!< @brief true if this add-on support scanning for new channels on the backend */ + bool bSupportsChannelSettings; /*!< @brief true if this add-on supports the following functions: DeleteChannel, RenameChannel, MoveChannel, DialogChannelSettings and DialogAddChannel */ + bool bHandlesInputStream; /*!< @brief true if this add-on provides an input stream. false if XBMC handles the stream. */ + bool bHandlesDemuxing; /*!< @brief true if this add-on demultiplexes packets. */ + bool bSupportsRecordingFolders; /*!< @brief true if the backend supports timers / recordings in folders. */ + bool bSupportsRecordingPlayCount; /*!< @brief true if the backend supports play count for recordings. */ + bool bSupportsLastPlayedPosition; /*!< @brief true if the backend supports store/retrieve of last played position for recordings. */ + bool bSupportsRecordingEdl; /*!< @brief true if the backend supports retrieving an edit decision list for recordings. */ + } ATTRIBUTE_PACKED PVR_ADDON_CAPABILITIES; + + /*! + * @brief PVR stream properties + */ + typedef struct PVR_STREAM_PROPERTIES + { + unsigned int iStreamCount; + struct PVR_STREAM + { + unsigned int iPhysicalId; /*!< @brief (required) physical index */ + xbmc_codec_type_t iCodecType; /*!< @brief (required) codec type this stream */ + xbmc_codec_id_t iCodecId; /*!< @brief (required) codec id of this stream */ + char strLanguage[4]; /*!< @brief (required) language id */ + int iIdentifier; /*!< @brief (required) stream id */ + int iFPSScale; /*!< @brief (required) scale of 1000 and a rate of 29970 will result in 29.97 fps */ + int iFPSRate; /*!< @brief (required) FPS rate */ + int iHeight; /*!< @brief (required) height of the stream reported by the demuxer */ + int iWidth; /*!< @brief (required) width of the stream reported by the demuxer */ + float fAspect; /*!< @brief (required) display aspect ratio of the stream */ + int iChannels; /*!< @brief (required) amount of channels */ + int iSampleRate; /*!< @brief (required) sample rate */ + int iBlockAlign; /*!< @brief (required) block alignment */ + int iBitRate; /*!< @brief (required) bit rate */ + int iBitsPerSample; /*!< @brief (required) bits per sample */ + } stream[PVR_STREAM_MAX_STREAMS]; /*!< @brief (required) the streams */ + } ATTRIBUTE_PACKED PVR_STREAM_PROPERTIES; + + /*! + * @brief Signal status information + */ + typedef struct PVR_SIGNAL_STATUS + { + char strAdapterName[PVR_ADDON_NAME_STRING_LENGTH]; /*!< @brief (optional) name of the adapter that's being used */ + char strAdapterStatus[PVR_ADDON_NAME_STRING_LENGTH]; /*!< @brief (optional) status of the adapter that's being used */ + char strServiceName[PVR_ADDON_NAME_STRING_LENGTH]; /*!< @brief (optional) name of the current service */ + char strProviderName[PVR_ADDON_NAME_STRING_LENGTH]; /*!< @brief (optional) name of the current service's provider */ + char strMuxName[PVR_ADDON_NAME_STRING_LENGTH]; /*!< @brief (optional) name of the current mux */ + int iSNR; /*!< @brief (optional) signal/noise ratio */ + int iSignal; /*!< @brief (optional) signal strength */ + long iBER; /*!< @brief (optional) bit error rate */ + long iUNC; /*!< @brief (optional) uncorrected blocks */ + double dVideoBitrate; /*!< @brief (optional) video bitrate */ + double dAudioBitrate; /*!< @brief (optional) audio bitrate */ + double dDolbyBitrate; /*!< @brief (optional) dolby bitrate */ + } ATTRIBUTE_PACKED PVR_SIGNAL_STATUS; + + /*! + * @brief Menu hooks that are available in the context menus while playing a stream via this add-on. + * And in the Live TV settings dialog + */ + typedef struct PVR_MENUHOOK + { + unsigned int iHookId; /*!< @brief (required) this hook's identifier */ + unsigned int iLocalizedStringId; /*!< @brief (required) the id of the label for this hook in g_localizeStrings */ + PVR_MENUHOOK_CAT category; /*!< @brief (required) category of menu hook */ + } ATTRIBUTE_PACKED PVR_MENUHOOK; + + /*! + * @brief Representation of a TV or radio channel. + */ + typedef struct PVR_CHANNEL + { + unsigned int iUniqueId; /*!< @brief (required) unique identifier for this channel */ + bool bIsRadio; /*!< @brief (required) true if this is a radio channel, false if it's a TV channel */ + unsigned int iChannelNumber; /*!< @brief (optional) channel number of this channel on the backend */ + unsigned int iSubChannelNumber; /*!< @brief (optional) sub channel number of this channel on the backend (ATSC) */ + char strChannelName[PVR_ADDON_NAME_STRING_LENGTH]; /*!< @brief (optional) channel name given to this channel */ + char strInputFormat[PVR_ADDON_INPUT_FORMAT_STRING_LENGTH]; /*!< @brief (optional) input format type. types can be found in ffmpeg/libavformat/allformats.c + leave empty if unknown */ + char strStreamURL[PVR_ADDON_URL_STRING_LENGTH]; /*!< @brief (optional) the URL to use to access this channel. + leave empty to use this add-on to access the stream. + set to a path that's supported by XBMC otherwise. */ + unsigned int iEncryptionSystem; /*!< @brief (optional) the encryption ID or CaID of this channel */ + char strIconPath[PVR_ADDON_URL_STRING_LENGTH]; /*!< @brief (optional) path to the channel icon (if present) */ + bool bIsHidden; /*!< @brief (optional) true if this channel is marked as hidden */ + } ATTRIBUTE_PACKED PVR_CHANNEL; + + typedef struct PVR_CHANNEL_GROUP + { + char strGroupName[PVR_ADDON_NAME_STRING_LENGTH]; /*!< @brief (required) name of this channel group */ + bool bIsRadio; /*!< @brief (required) true if this is a radio channel group, false otherwise. */ + } ATTRIBUTE_PACKED PVR_CHANNEL_GROUP; + + typedef struct PVR_CHANNEL_GROUP_MEMBER + { + char strGroupName[PVR_ADDON_NAME_STRING_LENGTH]; /*!< @brief (required) name of the channel group to add the channel to */ + unsigned int iChannelUniqueId; /*!< @brief (required) unique id of the member */ + unsigned int iChannelNumber; /*!< @brief (optional) channel number within the group */ + } ATTRIBUTE_PACKED PVR_CHANNEL_GROUP_MEMBER; + + /*! + * @brief Representation of a timer event. + */ + typedef struct PVR_TIMER { + unsigned int iClientIndex; /*!< @brief (required) the index of this timer given by the client */ + int iClientChannelUid; /*!< @brief (required) unique identifier of the channel to record on */ + time_t startTime; /*!< @brief (required) start time of the recording in UTC. instant timers that are sent to the add-on by xbmc will have this value set to 0 */ + time_t endTime; /*!< @brief (required) end time of the recording in UTC */ + PVR_TIMER_STATE state; /*!< @brief (required) the state of this timer */ + char strTitle[PVR_ADDON_NAME_STRING_LENGTH]; /*!< @brief (optional) title of this timer */ + char strDirectory[PVR_ADDON_URL_STRING_LENGTH]; /*!< @brief (optional) the directory where the recording will be stored in */ + char strSummary[PVR_ADDON_DESC_STRING_LENGTH]; /*!< @brief (optional) the summary for this timer */ + int iPriority; /*!< @brief (optional) the priority of this timer */ + int iLifetime; /*!< @brief (optional) lifetimer of this timer in days */ + bool bIsRepeating; /*!< @brief (optional) true if this is a recurring timer */ + time_t firstDay; /*!< @brief (optional) the first day this recording is active in case of a repeating event */ + int iWeekdays; /*!< @brief (optional) weekday mask */ + int iEpgUid; /*!< @brief (optional) epg event id */ + unsigned int iMarginStart; /*!< @brief (optional) if set, the backend starts the recording iMarginStart minutes before startTime. */ + unsigned int iMarginEnd; /*!< @brief (optional) if set, the backend ends the recording iMarginEnd minutes after endTime. */ + int iGenreType; /*!< @brief (optional) genre type */ + int iGenreSubType; /*!< @brief (optional) genre sub type */ + } ATTRIBUTE_PACKED PVR_TIMER; + /*! + * @brief Representation of a recording. + */ + typedef struct PVR_RECORDING { + char strRecordingId[PVR_ADDON_NAME_STRING_LENGTH]; /*!< @brief (required) unique id of the recording on the client. */ + char strTitle[PVR_ADDON_NAME_STRING_LENGTH]; /*!< @brief (required) the title of this recording */ + char strStreamURL[PVR_ADDON_URL_STRING_LENGTH]; /*!< @brief (required) stream URL to access this recording */ + char strDirectory[PVR_ADDON_URL_STRING_LENGTH]; /*!< @brief (optional) directory of this recording on the client */ + char strPlotOutline[PVR_ADDON_DESC_STRING_LENGTH]; /*!< @brief (optional) plot outline */ + char strPlot[PVR_ADDON_DESC_STRING_LENGTH]; /*!< @brief (optional) plot */ + char strChannelName[PVR_ADDON_NAME_STRING_LENGTH]; /*!< @brief (optional) channel name */ + char strIconPath[PVR_ADDON_URL_STRING_LENGTH]; /*!< @brief (optional) icon path */ + char strThumbnailPath[PVR_ADDON_URL_STRING_LENGTH]; /*!< @brief (optional) thumbnail path */ + char strFanartPath[PVR_ADDON_URL_STRING_LENGTH]; /*!< @brief (optional) fanart path */ + time_t recordingTime; /*!< @brief (optional) start time of the recording */ + int iDuration; /*!< @brief (optional) duration of the recording in seconds */ + int iPriority; /*!< @brief (optional) priority of this recording (from 0 - 100) */ + int iLifetime; /*!< @brief (optional) life time in days of this recording */ + int iGenreType; /*!< @brief (optional) genre type */ + int iGenreSubType; /*!< @brief (optional) genre sub type */ + int iPlayCount; /*!< @brief (optional) play count of this recording on the client */ + int iLastPlayedPosition; /*!< @brief (optional) last played position of this recording on the client */ + bool bIsDeleted; /*!< @brief (optional) shows this recording is deleted and can be undelete */ + } ATTRIBUTE_PACKED PVR_RECORDING; + + /*! + * @brief Edit definition list (EDL) + */ + typedef enum + { + PVR_EDL_TYPE_CUT = 0, /*!< @brief cut (completly remove content) */ + PVR_EDL_TYPE_MUTE = 1, /*!< @brief mute audio */ + PVR_EDL_TYPE_SCENE = 2, /*!< @brief scene markers (chapter seeking) */ + PVR_EDL_TYPE_COMBREAK = 3 /*!< @brief commercial breaks */ + } PVR_EDL_TYPE; + + typedef struct PVR_EDL_ENTRY + { + int64_t start; // ms + int64_t end; // ms + PVR_EDL_TYPE type; + } ATTRIBUTE_PACKED PVR_EDL_ENTRY; + + /*! + * @brief PVR menu hook data + */ + typedef struct PVR_MENUHOOK_DATA + { + PVR_MENUHOOK_CAT cat; + union data { + int iEpgUid; + PVR_CHANNEL channel; + PVR_TIMER timer; + PVR_RECORDING recording; + } data; + } ATTRIBUTE_PACKED PVR_MENUHOOK_DATA; + + /*! + * @brief Structure to transfer the methods from xbmc_pvr_dll.h to XBMC + */ + typedef struct PVRClient + { + const char* (__cdecl* GetPVRAPIVersion)(void); + const char* (__cdecl* GetMininumPVRAPIVersion)(void); + const char* (__cdecl* GetGUIAPIVersion)(void); + const char* (__cdecl* GetMininumGUIAPIVersion)(void); + PVR_ERROR (__cdecl* GetAddonCapabilities)(PVR_ADDON_CAPABILITIES*); + PVR_ERROR (__cdecl* GetStreamProperties)(PVR_STREAM_PROPERTIES*); + const char* (__cdecl* GetBackendName)(void); + const char* (__cdecl* GetBackendVersion)(void); + const char* (__cdecl* GetConnectionString)(void); + PVR_ERROR (__cdecl* GetDriveSpace)(long long*, long long*); + PVR_ERROR (__cdecl* MenuHook)(const PVR_MENUHOOK&, const PVR_MENUHOOK_DATA&); + PVR_ERROR (__cdecl* GetEpg)(ADDON_HANDLE, const PVR_CHANNEL&, time_t, time_t); + int (__cdecl* GetChannelGroupsAmount)(void); + PVR_ERROR (__cdecl* GetChannelGroups)(ADDON_HANDLE, bool); + PVR_ERROR (__cdecl* GetChannelGroupMembers)(ADDON_HANDLE, const PVR_CHANNEL_GROUP&); + PVR_ERROR (__cdecl* OpenDialogChannelScan)(void); + int (__cdecl* GetChannelsAmount)(void); + PVR_ERROR (__cdecl* GetChannels)(ADDON_HANDLE, bool); + PVR_ERROR (__cdecl* DeleteChannel)(const PVR_CHANNEL&); + PVR_ERROR (__cdecl* RenameChannel)(const PVR_CHANNEL&); + PVR_ERROR (__cdecl* MoveChannel)(const PVR_CHANNEL&); + PVR_ERROR (__cdecl* OpenDialogChannelSettings)(const PVR_CHANNEL&); + PVR_ERROR (__cdecl* OpenDialogChannelAdd)(const PVR_CHANNEL&); + int (__cdecl* GetRecordingsAmount)(bool); + PVR_ERROR (__cdecl* GetRecordings)(ADDON_HANDLE, bool); + PVR_ERROR (__cdecl* DeleteRecording)(const PVR_RECORDING&); + PVR_ERROR (__cdecl* UndeleteRecording)(const PVR_RECORDING&); + PVR_ERROR (__cdecl* DeleteAllRecordingsFromTrash)(void); + PVR_ERROR (__cdecl* RenameRecording)(const PVR_RECORDING&); + PVR_ERROR (__cdecl* SetRecordingPlayCount)(const PVR_RECORDING&, int); + PVR_ERROR (__cdecl* SetRecordingLastPlayedPosition)(const PVR_RECORDING&, int); + int (__cdecl* GetRecordingLastPlayedPosition)(const PVR_RECORDING&); + PVR_ERROR (__cdecl* GetRecordingEdl)(const PVR_RECORDING&, PVR_EDL_ENTRY[], int*); + int (__cdecl* GetTimersAmount)(void); + PVR_ERROR (__cdecl* GetTimers)(ADDON_HANDLE); + PVR_ERROR (__cdecl* AddTimer)(const PVR_TIMER&); + PVR_ERROR (__cdecl* DeleteTimer)(const PVR_TIMER&, bool); + PVR_ERROR (__cdecl* UpdateTimer)(const PVR_TIMER&); + bool (__cdecl* OpenLiveStream)(const PVR_CHANNEL&); + void (__cdecl* CloseLiveStream)(void); + int (__cdecl* ReadLiveStream)(unsigned char*, unsigned int); + long long (__cdecl* SeekLiveStream)(long long, int); + long long (__cdecl* PositionLiveStream)(void); + long long (__cdecl* LengthLiveStream)(void); + int (__cdecl* GetCurrentClientChannel)(void); + bool (__cdecl* SwitchChannel)(const PVR_CHANNEL&); + PVR_ERROR (__cdecl* SignalStatus)(PVR_SIGNAL_STATUS&); + const char* (__cdecl* GetLiveStreamURL)(const PVR_CHANNEL&); + bool (__cdecl* OpenRecordedStream)(const PVR_RECORDING&); + void (__cdecl* CloseRecordedStream)(void); + int (__cdecl* ReadRecordedStream)(unsigned char*, unsigned int); + long long (__cdecl* SeekRecordedStream)(long long, int); + long long (__cdecl* PositionRecordedStream)(void); + long long (__cdecl* LengthRecordedStream)(void); + void (__cdecl* DemuxReset)(void); + void (__cdecl* DemuxAbort)(void); + void (__cdecl* DemuxFlush)(void); + DemuxPacket* (__cdecl* DemuxRead)(void); + unsigned int (__cdecl* GetChannelSwitchDelay)(void); + bool (__cdecl* CanPauseStream)(void); + void (__cdecl* PauseStream)(bool); + bool (__cdecl* CanSeekStream)(void); + bool (__cdecl* SeekTime)(int, bool, double*); + void (__cdecl* SetSpeed)(int); + time_t (__cdecl* GetPlayingTime)(void); + time_t (__cdecl* GetBufferTimeStart)(void); + time_t (__cdecl* GetBufferTimeEnd)(void); + const char* (__cdecl* GetBackendHostname)(void); + } PVRClient; + +#ifdef __cplusplus +} +#endif + +#endif //__PVRCLIENT_TYPES_H__ diff --git a/xbmc/addons/include/xbmc_scr_dll.h b/xbmc/addons/include/xbmc_scr_dll.h new file mode 100644 index 0000000..c4257b4 --- /dev/null +++ b/xbmc/addons/include/xbmc_scr_dll.h @@ -0,0 +1,45 @@ +#pragma once +/* + * Copyright (C) 2005-2013 Team XBMC + * http://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, see + * . + * + */ + +#ifndef __XBMC_SCR_H__ +#define __XBMC_SCR_H__ + +#include "xbmc_addon_dll.h" +#include "xbmc_scr_types.h" + +extern "C" +{ + + // Functions that your visualisation must implement + void Start(); + void Render(); + void GetInfo(SCR_INFO* pInfo); + + // function to export the above structure to XBMC + void __declspec(dllexport) get_addon(struct ScreenSaver* pScr) + { + pScr->Start = Start; + pScr->Render = Render; + pScr->GetInfo = GetInfo; + }; +}; + +#endif diff --git a/xbmc/addons/include/xbmc_scr_types.h b/xbmc/addons/include/xbmc_scr_types.h new file mode 100644 index 0000000..fec3040 --- /dev/null +++ b/xbmc/addons/include/xbmc_scr_types.h @@ -0,0 +1,53 @@ +#pragma once +/* + * Copyright (C) 2005-2013 Team XBMC + * http://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, see + * . + * + */ + +#ifndef __SCREENSAVER_TYPES_H__ +#define __SCREENSAVER_TYPES_H__ + +extern "C" +{ + struct SCR_INFO + { + int dummy; + }; + + struct SCR_PROPS + { + void *device; + int x; + int y; + int width; + int height; + float pixelRatio; + const char *name; + const char *presets; + const char *profile; + }; + + struct ScreenSaver + { + void (__cdecl* Start) (); + void (__cdecl* Render) (); + void (__cdecl* GetInfo)(SCR_INFO *info); + }; +} + +#endif // __SCREENSAVER_TYPES_H__ diff --git a/xbmc/addons/include/xbmc_stream_utils.hpp b/xbmc/addons/include/xbmc_stream_utils.hpp new file mode 100644 index 0000000..927fe33 --- /dev/null +++ b/xbmc/addons/include/xbmc_stream_utils.hpp @@ -0,0 +1,264 @@ +#pragma once +/* + * Copyright (C) 2005-2013 Team XBMC + * http://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, see + * . + * + */ + +#include "xbmc_pvr_types.h" +#include +#include + +namespace ADDON +{ + /** + * Represents a single stream. It extends the PODS to provide some operators + * overloads. + */ + class XbmcPvrStream : public PVR_STREAM_PROPERTIES::PVR_STREAM + { + public: + XbmcPvrStream() + { + Clear(); + } + + XbmcPvrStream(const XbmcPvrStream &other) + { + memcpy(this, &other, sizeof(PVR_STREAM_PROPERTIES::PVR_STREAM)); + } + + XbmcPvrStream& operator=(const XbmcPvrStream &other) + { + memcpy(this, &other, sizeof(PVR_STREAM_PROPERTIES::PVR_STREAM)); + return *this; + } + + /** + * Compares this stream based on another stream + * @param other + * @return + */ + inline bool operator==(const XbmcPvrStream &other) const + { + return iPhysicalId == other.iPhysicalId && iCodecId == other.iCodecId; + } + + /** + * Compares this stream with another one so that video streams are sorted + * before any other streams and the others are sorted by the physical ID + * @param other + * @return + */ + bool operator<(const XbmcPvrStream &other) const + { + if (iCodecType == XBMC_CODEC_TYPE_VIDEO) + return true; + else if (other.iCodecType != XBMC_CODEC_TYPE_VIDEO) + return iPhysicalId < other.iPhysicalId; + else + return false; + } + + /** + * Clears the stream + */ + void Clear() + { + memset(this, 0, sizeof(PVR_STREAM_PROPERTIES::PVR_STREAM)); + iCodecId = XBMC_INVALID_CODEC_ID; + iCodecType = XBMC_CODEC_TYPE_UNKNOWN; + } + + /** + * Checks whether the stream has been cleared + * @return + */ + inline bool IsCleared() const + { + return iCodecId == XBMC_INVALID_CODEC_ID && + iCodecType == XBMC_CODEC_TYPE_UNKNOWN; + } + }; + + class XbmcStreamProperties + { + public: + typedef std::vector stream_vector; + + XbmcStreamProperties(void) + { + // make sure the vector won't have to resize itself later + m_streamVector = new stream_vector(); + m_streamVector->reserve(PVR_STREAM_MAX_STREAMS); + } + + virtual ~XbmcStreamProperties(void) + { + delete m_streamVector; + } + + /** + * Resets the streams + */ + void Clear(void) + { + m_streamVector->clear(); + m_streamIndex.clear(); + } + + /** + * Returns the index of the stream with the specified physical ID, or -1 if + * there no stream is found. This method is called very often which is why + * we keep a separate map for this. + * @param iPhysicalId + * @return + */ + int GetStreamId(unsigned int iPhysicalId) const + { + std::map::const_iterator it = m_streamIndex.find(iPhysicalId); + if (it != m_streamIndex.end()) + return it->second; + + return -1; + } + + /** + * Returns the stream with the specified physical ID, or null if no such + * stream exists + * @param iPhysicalId + * @return + */ + XbmcPvrStream* GetStreamById(unsigned int iPhysicalId) const + { + int position = GetStreamId(iPhysicalId); + return position != -1 ? &m_streamVector->at(position) : NULL; + } + + /** + * Populates the specified stream with the stream having the specified + * physical ID. If the stream is not found only target stream's physical ID + * will be populated. + * @param iPhysicalId + * @param stream + */ + void GetStreamData(unsigned int iPhysicalId, XbmcPvrStream* stream) + { + XbmcPvrStream *foundStream = GetStreamById(iPhysicalId); + if (foundStream) + stream = foundStream; + else + { + stream->iIdentifier = -1; + stream->iPhysicalId = iPhysicalId; + } + } + + /** + * Populates props with the current streams and returns whether there are + * any streams at the moment or not. + * @param props + * @return + */ + bool GetProperties(PVR_STREAM_PROPERTIES* props) + { + unsigned int i = 0; + for (stream_vector::const_iterator it = m_streamVector->begin(); + it != m_streamVector->end(); ++it, ++i) + { + memcpy(&props->stream[i], &(*it), sizeof(PVR_STREAM_PROPERTIES::PVR_STREAM)); + } + + props->iStreamCount = m_streamVector->size(); + return (props->iStreamCount > 0); + } + + /** + * Merges new streams into the current list of streams. Identical streams + * will retain their respective indexes and new streams will replace unused + * indexes or be appended. + * @param newStreams + */ + void UpdateStreams(stream_vector &newStreams) + { + // sort the new streams + std::sort(newStreams.begin(), newStreams.end()); + + // ensure we never have more than PVR_STREAMS_MAX_STREAMS streams + if (newStreams.size() > PVR_STREAM_MAX_STREAMS) + { + while (newStreams.size() > PVR_STREAM_MAX_STREAMS) + newStreams.pop_back(); + + XBMC->Log(LOG_ERROR, "%s - max amount of streams reached", __FUNCTION__); + } + + stream_vector::iterator newStreamPosition; + for (stream_vector::iterator it = m_streamVector->begin(); it != m_streamVector->end(); ++it) + { + newStreamPosition = std::find(newStreams.begin(), newStreams.end(), *it); + + // if the current stream no longer exists we clear it, otherwise we + // copy it and remove it from newStreams + if (newStreamPosition == newStreams.end()) + it->Clear(); + else + { + *it = *newStreamPosition; + newStreams.erase(newStreamPosition); + } + } + + // replace cleared streams with new streams + for (stream_vector::iterator it = m_streamVector->begin(); + it != m_streamVector->end() && !newStreams.empty(); ++it) + { + if (it->IsCleared()) + { + *it = newStreams.front(); + newStreams.erase(newStreams.begin()); + } + } + + // append any remaining new streams + m_streamVector->insert(m_streamVector->end(), newStreams.begin(), newStreams.end()); + + // remove trailing cleared streams + while (m_streamVector->back().IsCleared()) + m_streamVector->pop_back(); + + // update the index + UpdateIndex(); + } + + private: + stream_vector *m_streamVector; + std::map m_streamIndex; + + /** + * Updates the stream index + */ + void UpdateIndex() + { + m_streamIndex.clear(); + + int i = 0; + for (stream_vector::const_iterator it = m_streamVector->begin(); it != m_streamVector->end(); ++it, ++i) + m_streamIndex[it->iPhysicalId] = i; + } + }; +} diff --git a/xbmc/addons/include/xbmc_vis_dll.h b/xbmc/addons/include/xbmc_vis_dll.h new file mode 100644 index 0000000..c65f844 --- /dev/null +++ b/xbmc/addons/include/xbmc_vis_dll.h @@ -0,0 +1,55 @@ +#ifndef __XBMC_VIS_H__ +#define __XBMC_VIS_H__ + +/* + * Copyright (C) 2005-2013 Team XBMC + * http://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, see + * . + * + */ + +#include "xbmc_addon_dll.h" +#include "xbmc_vis_types.h" + +extern "C" +{ + // Functions that your visualisation must implement + void Start(int iChannels, int iSamplesPerSec, int iBitsPerSample, const char* szSongName); + void AudioData(const float* pAudioData, int iAudioDataLength, float *pFreqData, int iFreqDataLength); + void Render(); + bool OnAction(long action, const void *param); + void GetInfo(VIS_INFO* pInfo); + unsigned int GetPresets(char ***presets); + unsigned GetPreset(); + unsigned int GetSubModules(char ***presets); + bool IsLocked(); + + // function to export the above structure to XBMC + void __declspec(dllexport) get_addon(struct Visualisation* pVisz) + { + pVisz->Start = Start; + pVisz->AudioData = AudioData; + pVisz->Render = Render; + pVisz->OnAction = OnAction; + pVisz->GetInfo = GetInfo; + pVisz->GetPresets = GetPresets; + pVisz->GetPreset = GetPreset; + pVisz->GetSubModules = GetSubModules; + pVisz->IsLocked = IsLocked; + }; +}; + +#endif diff --git a/xbmc/addons/include/xbmc_vis_types.h b/xbmc/addons/include/xbmc_vis_types.h new file mode 100644 index 0000000..e6b0ccd --- /dev/null +++ b/xbmc/addons/include/xbmc_vis_types.h @@ -0,0 +1,111 @@ +/* + * Copyright (C) 2005-2013 Team XBMC + * http://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, see + * . + * + */ + +/* + Common data structures shared between XBMC and XBMC's visualisations + */ + +#ifndef __VISUALISATION_TYPES_H__ +#define __VISUALISATION_TYPES_H__ +#include + +extern "C" +{ + struct VIS_INFO + { + int bWantsFreq; + int iSyncDelay; + }; + + struct VIS_PROPS + { + void *device; + int x; + int y; + int width; + int height; + float pixelRatio; + const char *name; + const char *presets; + const char *profile; + const char *submodule; + }; + + enum VIS_ACTION + { + VIS_ACTION_NONE = 0, + VIS_ACTION_NEXT_PRESET, + VIS_ACTION_PREV_PRESET, + VIS_ACTION_LOAD_PRESET, + VIS_ACTION_RANDOM_PRESET, + VIS_ACTION_LOCK_PRESET, + VIS_ACTION_RATE_PRESET_PLUS, + VIS_ACTION_RATE_PRESET_MINUS, + VIS_ACTION_UPDATE_ALBUMART, + VIS_ACTION_UPDATE_TRACK + }; + + class VisTrack + { + public: + VisTrack() + { + title = artist = album = albumArtist = NULL; + genre = comment = lyrics = reserved1 = reserved2 = NULL; + trackNumber = discNumber = duration = year = 0; + rating = 0; + reserved3 = reserved4 = 0; + } + + const char *title; + const char *artist; + const char *album; + const char *albumArtist; + const char *genre; + const char *comment; + const char *lyrics; + const char *reserved1; + const char *reserved2; + + int trackNumber; + int discNumber; + int duration; + int year; + char rating; + int reserved3; + int reserved4; + }; + + struct Visualisation + { + void (__cdecl* Start)(int iChannels, int iSamplesPerSec, int iBitsPerSample, const char* szSongName); + void (__cdecl* AudioData)(const float* pAudioData, int iAudioDataLength, float *pFreqData, int iFreqDataLength); + void (__cdecl* Render) (); + void (__cdecl* GetInfo)(VIS_INFO *info); + bool (__cdecl* OnAction)(long flags, const void *param); + int (__cdecl* HasPresets)(); + unsigned int (__cdecl *GetPresets)(char ***presets); + unsigned int (__cdecl *GetPreset)(); + unsigned int (__cdecl *GetSubModules)(char ***modules); + bool (__cdecl* IsLocked)(); + }; +} + +#endif //__VISUALISATION_TYPES_H__ diff --git a/xbmc/addons/test/Makefile b/xbmc/addons/test/Makefile new file mode 100644 index 0000000..bf74bd6 --- /dev/null +++ b/xbmc/addons/test/Makefile @@ -0,0 +1,9 @@ +SRCS= \ + TestAddonVersion.cpp + +LIB=addonsTest.a + +INCLUDES += -I../../../lib/gtest/include + +include ../../../Makefile.include +-include $(patsubst %.cpp,%.P,$(patsubst %.c,%.P,$(SRCS))) diff --git a/xbmc/addons/test/TestAddonVersion.cpp b/xbmc/addons/test/TestAddonVersion.cpp new file mode 100644 index 0000000..41e71a4 --- /dev/null +++ b/xbmc/addons/test/TestAddonVersion.cpp @@ -0,0 +1,254 @@ +/* + * Copyright (C) 2005-2014 Team XBMC + * http://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, see + * . + * + */ + +#include "addons/AddonVersion.h" + +#include "gtest/gtest.h" + +using namespace ADDON; + +class TestAddonVersion : public testing::Test +{ +public: + TestAddonVersion() + : v1_0("1.0"), + v1_00("1.00"), + v1_0_0("1.0.0"), + v1_1("1.1"), + v1_01("1.01"), + v1_0_1("1.0.1"), + e1_v1_0_0("1:1.0.0"), + e1_v1_0_1("1:1.0.1"), + e2_v1_0_0("2:1.0.0"), + e1_v1_0_0_r1("1:1.0.0-1"), + e1_v1_0_1_r1("1:1.0.1-1"), + e1_v1_0_0_r2("1:1.0.0-2"), + v1_0_0_beta("1.0.0~beta"), + v1_0_0_alpha("1.0.0~alpha"), + v1_0_0_alpha2("1.0.0~alpha2"), + v1_0_0_alpha3("1.0.0~alpha3"), + v1_0_0_alpha10("1.0.0~alpha10") + { + } + + AddonVersion v1_0; + AddonVersion v1_00; + AddonVersion v1_0_0; + AddonVersion v1_1; + AddonVersion v1_01; + AddonVersion v1_0_1; + AddonVersion e1_v1_0_0; + AddonVersion e1_v1_0_1; + AddonVersion e2_v1_0_0; + AddonVersion e1_v1_0_0_r1; + AddonVersion e1_v1_0_1_r1; + AddonVersion e1_v1_0_0_r2; + AddonVersion v1_0_0_beta; + AddonVersion v1_0_0_alpha; + AddonVersion v1_0_0_alpha2; + AddonVersion v1_0_0_alpha3; + AddonVersion v1_0_0_alpha10; +}; + +TEST_F(TestAddonVersion, Constructor) +{ + EXPECT_EQ(v1_0.Upstream(), "1.0"); + EXPECT_EQ(v1_0.Epoch(), 0); + EXPECT_TRUE(v1_0.Revision().empty()); + + EXPECT_EQ(v1_00.Upstream(), "1.00"); + EXPECT_EQ(v1_00.Epoch(), 0); + EXPECT_TRUE(v1_00.Revision().empty()); + + EXPECT_EQ(v1_0_0.Upstream(), "1.0.0"); + EXPECT_EQ(v1_0_0.Epoch(), 0); + EXPECT_TRUE(v1_0_0.Revision().empty()); + + EXPECT_EQ(v1_1.Upstream(), "1.1"); + EXPECT_EQ(v1_1.Epoch(), 0); + EXPECT_TRUE(v1_1.Revision().empty()); + + EXPECT_EQ(v1_01.Upstream(), "1.01"); + EXPECT_EQ(v1_01.Epoch(), 0); + EXPECT_TRUE(v1_01.Revision().empty()); + + EXPECT_EQ(v1_0_1.Upstream(), "1.0.1"); + EXPECT_EQ(v1_0_1.Epoch(), 0); + EXPECT_TRUE(v1_0_1.Revision().empty()); + + EXPECT_EQ(e1_v1_0_0.Upstream(), "1.0.0"); + EXPECT_EQ(e1_v1_0_0.Epoch(), 1); + EXPECT_TRUE(e1_v1_0_0.Revision().empty()); + + EXPECT_EQ(e1_v1_0_1.Upstream(), "1.0.1"); + EXPECT_EQ(e1_v1_0_1.Epoch(), 1); + EXPECT_TRUE(e1_v1_0_1.Revision().empty()); + + EXPECT_EQ(e2_v1_0_0.Upstream(), "1.0.0"); + EXPECT_EQ(e2_v1_0_0.Epoch(), 2); + EXPECT_TRUE(e2_v1_0_0.Revision().empty()); + + EXPECT_EQ(e1_v1_0_0_r1.Upstream(), "1.0.0"); + EXPECT_EQ(e1_v1_0_0_r1.Epoch(), 1); + EXPECT_EQ(e1_v1_0_0_r1.Revision(), "1"); + + EXPECT_EQ(e1_v1_0_1_r1.Upstream(), "1.0.1"); + EXPECT_EQ(e1_v1_0_1_r1.Epoch(), 1); + EXPECT_EQ(e1_v1_0_1_r1.Revision(), "1"); + + EXPECT_EQ(e1_v1_0_0_r2.Upstream(), "1.0.0"); + EXPECT_EQ(e1_v1_0_0_r2.Epoch(), 1); + EXPECT_EQ(e1_v1_0_0_r2.Revision(), "2"); + + EXPECT_EQ(v1_0_0_beta.Upstream(), "1.0.0~beta"); + EXPECT_EQ(v1_0_0_beta.Epoch(), 0); + EXPECT_TRUE(v1_0_0_beta.Revision().empty()); + + EXPECT_EQ(v1_0_0_alpha.Upstream(), "1.0.0~alpha"); + EXPECT_EQ(v1_0_0_alpha.Epoch(), 0); + EXPECT_TRUE(v1_0_0_alpha.Revision().empty()); + + EXPECT_EQ(v1_0_0_alpha2.Upstream(), "1.0.0~alpha2"); + EXPECT_EQ(v1_0_0_alpha2.Epoch(), 0); + EXPECT_TRUE(v1_0_0_alpha2.Revision().empty()); + + EXPECT_EQ(v1_0_0_alpha3.Upstream(), "1.0.0~alpha3"); + EXPECT_EQ(v1_0_0_alpha3.Epoch(), 0); + EXPECT_TRUE(v1_0_0_alpha3.Revision().empty()); + + EXPECT_EQ(v1_0_0_alpha10.Upstream(), "1.0.0~alpha10"); + EXPECT_EQ(v1_0_0_alpha10.Epoch(), 0); + EXPECT_TRUE(v1_0_0_alpha10.Revision().empty()); +} + +TEST_F(TestAddonVersion, asString) +{ + EXPECT_EQ(v1_0.asString(), "1.0"); + EXPECT_EQ(v1_00.asString(), "1.00"); + EXPECT_EQ(v1_0_0.asString(), "1.0.0"); + EXPECT_EQ(v1_1.asString(), "1.1"); + EXPECT_EQ(v1_01.asString(), "1.01"); + EXPECT_EQ(v1_0_1.asString(), "1.0.1"); + EXPECT_EQ(e1_v1_0_0.asString(), "1:1.0.0"); + EXPECT_EQ(e1_v1_0_1.asString(), "1:1.0.1"); + EXPECT_EQ(e2_v1_0_0.asString(), "2:1.0.0"); + EXPECT_EQ(e1_v1_0_0_r1.asString(), "1:1.0.0-1"); + EXPECT_EQ(e1_v1_0_1_r1.asString(), "1:1.0.1-1"); + EXPECT_EQ(e1_v1_0_0_r2.asString(), "1:1.0.0-2"); + EXPECT_EQ(v1_0_0_beta.asString(), "1.0.0~beta"); + EXPECT_EQ(v1_0_0_alpha.asString(), "1.0.0~alpha"); + EXPECT_EQ(v1_0_0_alpha2.asString(), "1.0.0~alpha2"); + EXPECT_EQ(v1_0_0_alpha3.asString(), "1.0.0~alpha3"); + EXPECT_EQ(v1_0_0_alpha10.asString(), "1.0.0~alpha10"); +} + +TEST_F(TestAddonVersion, Equals) +{ + EXPECT_EQ(v1_0, AddonVersion("1.0")); + EXPECT_EQ(v1_00, AddonVersion("1.00")); + EXPECT_EQ(v1_0_0, AddonVersion("1.0.0")); + EXPECT_EQ(v1_1, AddonVersion("1.1")); + EXPECT_EQ(v1_01, AddonVersion("1.01")); + EXPECT_EQ(v1_0_1, AddonVersion("1.0.1")); + EXPECT_EQ(e1_v1_0_0, AddonVersion("1:1.0.0")); + EXPECT_EQ(e1_v1_0_1, AddonVersion("1:1.0.1")); + EXPECT_EQ(e2_v1_0_0, AddonVersion("2:1.0.0")); + EXPECT_EQ(e1_v1_0_0_r1, AddonVersion("1:1.0.0-1")); + EXPECT_EQ(e1_v1_0_1_r1, AddonVersion("1:1.0.1-1")); + EXPECT_EQ(e1_v1_0_0_r2, AddonVersion("1:1.0.0-2")); + EXPECT_EQ(v1_0_0_beta, AddonVersion("1.0.0~beta")); + EXPECT_EQ(v1_0_0_alpha, AddonVersion("1.0.0~alpha")); + EXPECT_EQ(v1_0_0_alpha2, AddonVersion("1.0.0~alpha2")); + EXPECT_EQ(v1_0_0_alpha3, AddonVersion("1.0.0~alpha3")); + EXPECT_EQ(v1_0_0_alpha10, AddonVersion("1.0.0~alpha10")); +} + +TEST_F(TestAddonVersion, Equivalent) +{ + EXPECT_FALSE(v1_0 != v1_00); + EXPECT_FALSE(v1_0 < v1_00); + EXPECT_FALSE(v1_0 > v1_00); + EXPECT_TRUE(v1_0 == v1_00); + + EXPECT_FALSE(v1_01 != v1_1); + EXPECT_FALSE(v1_01 < v1_1); + EXPECT_FALSE(v1_01 > v1_1); + EXPECT_TRUE(v1_01 == v1_1); +} + +TEST_F(TestAddonVersion, LessThan) +{ + EXPECT_LT(v1_0, v1_0_0); + EXPECT_LT(v1_0, v1_1); + EXPECT_LT(v1_0, v1_01); + EXPECT_LT(v1_0, v1_0_1); + + EXPECT_LT(v1_00, v1_0_0); + EXPECT_LT(v1_00, v1_1); + EXPECT_LT(v1_00, v1_01); + EXPECT_LT(v1_00, v1_0_1); + + EXPECT_LT(v1_0_0, v1_1); + EXPECT_LT(v1_0_0, v1_01); + EXPECT_LT(v1_0_0, v1_0_1); + + EXPECT_LT(v1_0_1, v1_01); + EXPECT_LT(v1_0_1, v1_1); + + // epochs + EXPECT_LT(v1_0_0, e1_v1_0_0); + EXPECT_LT(v1_0_0, e1_v1_0_1); + EXPECT_LT(v1_0_0, e2_v1_0_0); + EXPECT_LT(v1_0_1, e1_v1_0_1); + EXPECT_LT(v1_0_1, e2_v1_0_0); + + EXPECT_LT(e1_v1_0_0, e1_v1_0_1); + EXPECT_LT(e1_v1_0_0, e2_v1_0_0); + EXPECT_LT(e1_v1_0_1, e2_v1_0_0); + + // revisions + EXPECT_LT(e1_v1_0_0, e1_v1_0_0_r1); + EXPECT_LT(e1_v1_0_0, e1_v1_0_1_r1); + EXPECT_LT(e1_v1_0_0, e1_v1_0_0_r2); + EXPECT_LT(e1_v1_0_1, e1_v1_0_1_r1); + EXPECT_LT(e1_v1_0_0_r1, e1_v1_0_1); + EXPECT_LT(e1_v1_0_0_r1, e1_v1_0_1_r1); + EXPECT_LT(e1_v1_0_0_r1, e1_v1_0_0_r2); + EXPECT_LT(e1_v1_0_0_r2, e1_v1_0_1); + EXPECT_LT(e1_v1_0_0_r2, e1_v1_0_1_r1); + EXPECT_LT(e1_v1_0_1_r1, e2_v1_0_0); + + // alpha, beta + EXPECT_LT(v1_0_0_beta, v1_0_0); + EXPECT_LT(v1_0_0_alpha, v1_0_0); + EXPECT_LT(v1_0_0_alpha, v1_0_0_beta); + EXPECT_LT(v1_0_0_alpha, v1_0_0_alpha2); + EXPECT_LT(v1_0_0_alpha, v1_0_0_alpha3); + EXPECT_LT(v1_0_0_alpha, v1_0_0_alpha10); + EXPECT_LT(v1_0_0_alpha2, v1_0_0); + EXPECT_LT(v1_0_0_alpha2, v1_0_0_beta); + EXPECT_LT(v1_0_0_alpha2, v1_0_0_alpha3); + EXPECT_LT(v1_0_0_alpha2, v1_0_0_alpha10); + EXPECT_LT(v1_0_0_alpha3, v1_0_0); + EXPECT_LT(v1_0_0_alpha3, v1_0_0_beta); + EXPECT_LT(v1_0_0_alpha3, v1_0_0_alpha10); + EXPECT_LT(v1_0_0_alpha10, v1_0_0); + EXPECT_LT(v1_0_0_alpha10, v1_0_0_beta); +} -- cgit v1.2.3