summaryrefslogtreecommitdiffstats
path: root/xbmc/interfaces/builtins
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--xbmc/interfaces/builtins/AddonBuiltins.cpp522
-rw-r--r--xbmc/interfaces/builtins/AddonBuiltins.h19
-rw-r--r--xbmc/interfaces/builtins/AndroidBuiltins.cpp73
-rw-r--r--xbmc/interfaces/builtins/AndroidBuiltins.h19
-rw-r--r--xbmc/interfaces/builtins/ApplicationBuiltins.cpp222
-rw-r--r--xbmc/interfaces/builtins/ApplicationBuiltins.h19
-rw-r--r--xbmc/interfaces/builtins/Builtins.cpp168
-rw-r--r--xbmc/interfaces/builtins/Builtins.h55
-rw-r--r--xbmc/interfaces/builtins/CECBuiltins.cpp82
-rw-r--r--xbmc/interfaces/builtins/CECBuiltins.h19
-rw-r--r--xbmc/interfaces/builtins/CMakeLists.txt41
-rw-r--r--xbmc/interfaces/builtins/GUIBuiltins.cpp601
-rw-r--r--xbmc/interfaces/builtins/GUIBuiltins.h19
-rw-r--r--xbmc/interfaces/builtins/GUIContainerBuiltins.cpp189
-rw-r--r--xbmc/interfaces/builtins/GUIContainerBuiltins.h19
-rw-r--r--xbmc/interfaces/builtins/GUIControlBuiltins.cpp238
-rw-r--r--xbmc/interfaces/builtins/GUIControlBuiltins.h19
-rw-r--r--xbmc/interfaces/builtins/LibraryBuiltins.cpp416
-rw-r--r--xbmc/interfaces/builtins/LibraryBuiltins.h19
-rw-r--r--xbmc/interfaces/builtins/OpticalBuiltins.cpp75
-rw-r--r--xbmc/interfaces/builtins/OpticalBuiltins.h19
-rw-r--r--xbmc/interfaces/builtins/PVRBuiltins.cpp224
-rw-r--r--xbmc/interfaces/builtins/PVRBuiltins.h19
-rw-r--r--xbmc/interfaces/builtins/PictureBuiltins.cpp135
-rw-r--r--xbmc/interfaces/builtins/PictureBuiltins.h19
-rw-r--r--xbmc/interfaces/builtins/PlayerBuiltins.cpp842
-rw-r--r--xbmc/interfaces/builtins/PlayerBuiltins.h19
-rw-r--r--xbmc/interfaces/builtins/ProfileBuiltins.cpp133
-rw-r--r--xbmc/interfaces/builtins/ProfileBuiltins.h19
-rw-r--r--xbmc/interfaces/builtins/SkinBuiltins.cpp687
-rw-r--r--xbmc/interfaces/builtins/SkinBuiltins.h19
-rw-r--r--xbmc/interfaces/builtins/SystemBuiltins.cpp267
-rw-r--r--xbmc/interfaces/builtins/SystemBuiltins.h19
-rw-r--r--xbmc/interfaces/builtins/WeatherBuiltins.cpp88
-rw-r--r--xbmc/interfaces/builtins/WeatherBuiltins.h19
35 files changed, 5362 insertions, 0 deletions
diff --git a/xbmc/interfaces/builtins/AddonBuiltins.cpp b/xbmc/interfaces/builtins/AddonBuiltins.cpp
new file mode 100644
index 0000000..056128d
--- /dev/null
+++ b/xbmc/interfaces/builtins/AddonBuiltins.cpp
@@ -0,0 +1,522 @@
+/*
+ * Copyright (C) 2005-2018 Team Kodi
+ * This file is part of Kodi - https://kodi.tv
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ * See LICENSES/README.md for more information.
+ */
+
+#include "AddonBuiltins.h"
+
+#include "FileItem.h"
+#include "GUIPassword.h"
+#include "GUIUserMessages.h"
+#include "ServiceBroker.h"
+#include "addons/AddonInstaller.h"
+#include "addons/AddonManager.h"
+#include "addons/AddonSystemSettings.h"
+#include "addons/PluginSource.h"
+#include "addons/RepositoryUpdater.h"
+#include "addons/addoninfo/AddonInfo.h"
+#include "addons/addoninfo/AddonType.h"
+#include "addons/gui/GUIDialogAddonSettings.h"
+#include "addons/gui/GUIWindowAddonBrowser.h"
+#include "application/Application.h"
+#include "filesystem/PluginDirectory.h"
+#include "games/tags/GameInfoTag.h"
+#include "guilib/GUIComponent.h"
+#include "guilib/GUIWindowManager.h"
+#include "interfaces/generic/ScriptInvocationManager.h"
+#include "messaging/helpers/DialogHelper.h"
+#include "playlists/PlayListTypes.h"
+#include "utils/StringUtils.h"
+#include "utils/URIUtils.h"
+#include "utils/log.h"
+
+#include <memory>
+
+#if defined(TARGET_DARWIN)
+#include "filesystem/SpecialProtocol.h"
+#if defined(TARGET_DARWIN_OSX)
+#include "platform/darwin/osx/CocoaInterface.h"
+#endif
+#endif
+
+using namespace ADDON;
+using namespace KODI::MESSAGING;
+using KODI::MESSAGING::HELPERS::DialogResponse;
+
+/*! \brief Install an addon.
+ * \param params The parameters.
+ * \details params[0] = add-on id.
+ */
+static int InstallAddon(const std::vector<std::string>& params)
+{
+ const std::string& addonid = params[0];
+
+ AddonPtr addon;
+ CAddonInstaller::GetInstance().InstallModal(addonid, addon, InstallModalPrompt::CHOICE_YES);
+
+ return 0;
+}
+
+/*! \brief Enable an addon.
+ * \param params The parameters.
+ * \details params[0] = add-on id.
+ */
+static int EnableAddon(const std::vector<std::string>& params)
+{
+ const std::string& addonid = params[0];
+
+ if (!g_passwordManager.CheckMenuLock(WINDOW_ADDON_BROWSER))
+ return -1;
+
+ AddonPtr addon;
+ if (!CServiceBroker::GetAddonMgr().GetAddon(addonid, addon, OnlyEnabled::CHOICE_NO))
+ return -1;
+
+ auto response = HELPERS::ShowYesNoDialogLines(CVariant{24076}, CVariant{24135}, CVariant{addon->Name()}, CVariant{24136});
+ if (response == DialogResponse::CHOICE_YES)
+ CServiceBroker::GetAddonMgr().EnableAddon(addonid);
+
+ return 0;
+}
+
+/*! \brief Run a plugin.
+ * \param params The parameters.
+ * \details params[0] = plugin:// URL to script.
+ */
+static int RunPlugin(const std::vector<std::string>& params)
+{
+ if (params.size())
+ {
+ CFileItem item(params[0]);
+ if (!item.m_bIsFolder)
+ {
+ item.SetPath(params[0]);
+ XFILE::CPluginDirectory::RunScriptWithParams(item.GetPath(), false);
+ }
+ }
+ else
+ CLog::Log(LOGERROR, "RunPlugin called with no arguments.");
+
+ return 0;
+}
+
+/*! \brief Run a script, plugin or game add-on.
+ * \param params The parameters.
+ * \details params[0] = add-on id.
+ * params[1] is blank for no add-on parameters
+ * or
+ * params[1] = add-on parameters in url format
+ * or
+ * params[1,...] = additional parameters in format param=value.
+ */
+static int RunAddon(const std::vector<std::string>& params)
+{
+ if (params.size())
+ {
+ const std::string& addonid = params[0];
+
+ AddonPtr addon;
+ if (CServiceBroker::GetAddonMgr().GetAddon(addonid, addon, AddonType::PLUGIN,
+ OnlyEnabled::CHOICE_YES))
+ {
+ const auto plugin = std::dynamic_pointer_cast<CPluginSource>(addon);
+ std::string urlParameters;
+ std::vector<std::string> parameters;
+ if (params.size() == 2 &&
+ (StringUtils::StartsWith(params[1], "/") || StringUtils::StartsWith(params[1], "?")))
+ urlParameters = params[1];
+ else if (params.size() > 1)
+ {
+ parameters.insert(parameters.begin(), params.begin() + 1, params.end());
+ urlParameters = "?" + StringUtils::Join(parameters, "&");
+ }
+ else
+ {
+ // Add '/' if addon is run without params (will be removed later so it's safe)
+ // Otherwise there are 2 entries for the same plugin in ViewModesX.db
+ urlParameters = "/";
+ }
+
+ std::string cmd;
+ if (plugin->Provides(CPluginSource::VIDEO))
+ cmd = StringUtils::Format("ActivateWindow(Videos,plugin://{}{},return)", addonid,
+ urlParameters);
+ else if (plugin->Provides(CPluginSource::AUDIO))
+ cmd = StringUtils::Format("ActivateWindow(Music,plugin://{}{},return)", addonid,
+ urlParameters);
+ else if (plugin->Provides(CPluginSource::EXECUTABLE))
+ cmd = StringUtils::Format("ActivateWindow(Programs,plugin://{}{},return)", addonid,
+ urlParameters);
+ else if (plugin->Provides(CPluginSource::IMAGE))
+ cmd = StringUtils::Format("ActivateWindow(Pictures,plugin://{}{},return)", addonid,
+ urlParameters);
+ else if (plugin->Provides(CPluginSource::GAME))
+ cmd = StringUtils::Format("ActivateWindow(Games,plugin://{}{},return)", addonid,
+ urlParameters);
+ else
+ // Pass the script name (addonid) and all the parameters
+ // (params[1] ... params[x]) separated by a comma to RunPlugin
+ cmd = StringUtils::Format("RunPlugin({})", StringUtils::Join(params, ","));
+ CBuiltins::GetInstance().Execute(cmd);
+ }
+ else if (CServiceBroker::GetAddonMgr().GetAddon(addonid, addon, AddonType::SCRIPT,
+ OnlyEnabled::CHOICE_YES) ||
+ CServiceBroker::GetAddonMgr().GetAddon(addonid, addon, AddonType::SCRIPT_WEATHER,
+ OnlyEnabled::CHOICE_YES) ||
+ CServiceBroker::GetAddonMgr().GetAddon(addonid, addon, AddonType::SCRIPT_LYRICS,
+ OnlyEnabled::CHOICE_YES) ||
+ CServiceBroker::GetAddonMgr().GetAddon(addonid, addon, AddonType::SCRIPT_LIBRARY,
+ OnlyEnabled::CHOICE_YES))
+ {
+ // Pass the script name (addonid) and all the parameters
+ // (params[1] ... params[x]) separated by a comma to RunScript
+ CBuiltins::GetInstance().Execute(
+ StringUtils::Format("RunScript({})", StringUtils::Join(params, ",")));
+ }
+ else if (CServiceBroker::GetAddonMgr().GetAddon(addonid, addon, AddonType::GAMEDLL,
+ OnlyEnabled::CHOICE_YES))
+ {
+ CFileItem item;
+
+ if (params.size() >= 2)
+ {
+ item = CFileItem(params[1], false);
+ item.GetGameInfoTag()->SetGameClient(addonid);
+ }
+ else
+ item = CFileItem(addon);
+
+ if (!g_application.PlayMedia(item, "", PLAYLIST::TYPE_NONE))
+ {
+ CLog::Log(LOGERROR, "RunAddon could not start {}", addonid);
+ return false;
+ }
+ }
+ else
+ CLog::Log(
+ LOGERROR,
+ "RunAddon: unknown add-on id '{}', or unexpected add-on type (not a script or plugin).",
+ addonid);
+ }
+ else
+ {
+ CLog::Log(LOGERROR, "RunAddon called with no arguments.");
+ }
+
+ return 0;
+}
+
+/*! \brief Run a script add-on or an apple script.
+ * \param params The parameters.
+ * \details params[0] is the URL to the apple script
+ * or
+ * params[0] is the addon-ID to the script add-on
+ * or
+ * params[0] is the URL to the python script.
+ *
+ * Set the OnlyApple template parameter to true to only attempt
+ * execution of applescripts.
+ */
+template<bool OnlyApple=false>
+static int RunScript(const std::vector<std::string>& params)
+{
+#if defined(TARGET_DARWIN_OSX)
+ std::string execute;
+ std::string parameter = params.size() ? params[0] : "";
+ if (URIUtils::HasExtension(parameter, ".applescript|.scpt"))
+ {
+ std::string osxPath = CSpecialProtocol::TranslatePath(parameter);
+ Cocoa_DoAppleScriptFile(osxPath.c_str());
+ }
+ else if (OnlyApple)
+ return 1;
+ else
+#endif
+ {
+ AddonPtr addon;
+ std::string scriptpath;
+ // Test to see if the param is an addon ID
+ if (CServiceBroker::GetAddonMgr().GetAddon(params[0], addon, OnlyEnabled::CHOICE_YES))
+ {
+ //Get the correct extension point to run
+ if (CServiceBroker::GetAddonMgr().GetAddon(params[0], addon, AddonType::SCRIPT,
+ OnlyEnabled::CHOICE_YES) ||
+ CServiceBroker::GetAddonMgr().GetAddon(params[0], addon, AddonType::SCRIPT_WEATHER,
+ OnlyEnabled::CHOICE_YES) ||
+ CServiceBroker::GetAddonMgr().GetAddon(params[0], addon, AddonType::SCRIPT_LYRICS,
+ OnlyEnabled::CHOICE_YES) ||
+ CServiceBroker::GetAddonMgr().GetAddon(params[0], addon, AddonType::SCRIPT_LIBRARY,
+ OnlyEnabled::CHOICE_YES))
+ {
+ scriptpath = addon->LibPath();
+ }
+ else
+ {
+ // Run a random extension point (old behaviour).
+ if (CServiceBroker::GetAddonMgr().GetAddon(params[0], addon, OnlyEnabled::CHOICE_YES))
+ {
+ scriptpath = addon->LibPath();
+ CLog::Log(LOGWARNING,
+ "RunScript called for a non-script addon '{}'. This behaviour is deprecated.",
+ params[0]);
+ }
+ else
+ {
+ CLog::Log(LOGERROR, "{} - Could not get addon: {}", __FUNCTION__, params[0]);
+ }
+ }
+ }
+ else
+ scriptpath = params[0];
+
+ // split the path up to find the filename
+ std::vector<std::string> argv = params;
+ std::string filename = URIUtils::GetFileName(scriptpath);
+ if (!filename.empty())
+ argv[0] = filename;
+
+ CScriptInvocationManager::GetInstance().ExecuteAsync(scriptpath, addon, argv);
+ }
+
+ return 0;
+}
+
+/*! \brief Open the settings for the default add-on of a given type.
+ * \param params The parameters.
+ * \details params[0] = The add-on type.
+ */
+static int OpenDefaultSettings(const std::vector<std::string>& params)
+{
+ AddonPtr addon;
+ AddonType type = CAddonInfo::TranslateType(params[0]);
+ if (CAddonSystemSettings::GetInstance().GetActive(type, addon))
+ {
+ bool changed = CGUIDialogAddonSettings::ShowForAddon(addon);
+ if (type == AddonType::VISUALIZATION && changed)
+ CServiceBroker::GetGUI()->GetWindowManager().SendMessage(GUI_MSG_VISUALISATION_RELOAD, 0, 0);
+ }
+
+ return 0;
+}
+
+/*! \brief Set the default add-on for a given type.
+ * \param params The parameters.
+ * \details params[0] = The add-on type
+ */
+static int SetDefaultAddon(const std::vector<std::string>& params)
+{
+ std::string addonID;
+ AddonType type = CAddonInfo::TranslateType(params[0]);
+ bool allowNone = false;
+ if (type == AddonType::VISUALIZATION)
+ allowNone = true;
+
+ if (type != AddonType::UNKNOWN && CGUIWindowAddonBrowser::SelectAddonID(type, addonID, allowNone))
+ {
+ CAddonSystemSettings::GetInstance().SetActive(type, addonID);
+ if (type == AddonType::VISUALIZATION)
+ CServiceBroker::GetGUI()->GetWindowManager().SendMessage(GUI_MSG_VISUALISATION_RELOAD, 0, 0);
+ }
+
+ return 0;
+}
+
+/*! \brief Open the settings for a given add-on.
+ * \param params The parameters.
+ * \details params[0] = The add-on ID.
+ */
+static int AddonSettings(const std::vector<std::string>& params)
+{
+ AddonPtr addon;
+ if (CServiceBroker::GetAddonMgr().GetAddon(params[0], addon, OnlyEnabled::CHOICE_YES))
+ CGUIDialogAddonSettings::ShowForAddon(addon);
+
+ return 0;
+}
+
+/*! \brief Open the settings for a given add-on.
+* \param params The parameters.
+*/
+static int InstallFromZip(const std::vector<std::string>& params)
+{
+ CGUIWindowAddonBrowser::InstallFromZip();
+ return 0;
+}
+
+/*! \brief Stop a running script.
+ * \param params The parameters.
+ * \details params[0] = The add-on ID of the script to stop
+ * or
+ * params[0] = The URL of the script to stop.
+ */
+static int StopScript(const std::vector<std::string>& params)
+{
+ //! @todo FIXME: This does not work for addons with multiple extension points!
+ //! Are there any use for this? TODO: Fix hack in CScreenSaver::Destroy() and deprecate.
+ std::string scriptpath(params[0]);
+ // Test to see if the param is an addon ID
+ AddonPtr script;
+ if (CServiceBroker::GetAddonMgr().GetAddon(params[0], script, OnlyEnabled::CHOICE_YES))
+ scriptpath = script->LibPath();
+ CScriptInvocationManager::GetInstance().Stop(scriptpath);
+
+ return 0;
+}
+
+/*! \brief Check add-on repositories for updates.
+ * \param params (ignored)
+ */
+static int UpdateRepos(const std::vector<std::string>& params)
+{
+ CServiceBroker::GetRepositoryUpdater().CheckForUpdates();
+
+ return 0;
+}
+
+/*! \brief Check local add-on directories for updates.
+ * \param params (ignored)
+ */
+static int UpdateLocals(const std::vector<std::string>& params)
+{
+ CServiceBroker::GetAddonMgr().FindAddons();
+
+ return 0;
+}
+
+// Note: For new Texts with comma add a "\" before!!! Is used for table text.
+//
+/// \page page_List_of_built_in_functions List of built-in functions
+/// \section built_in_functions_1 Add-on built-in's
+///
+/// -----------------------------------------------------------------------------
+///
+/// \table_start
+/// \table_h2_l{
+/// Function,
+/// Description }
+/// \table_row2_l{
+/// <b>`Addon.Default.OpenSettings(extensionpoint)`</b>
+/// ,
+/// Open a settings dialog for the default addon of the given type
+/// (extensionpoint)
+/// @param[in] extensionpoint The add-on type
+/// }
+/// \table_row2_l{
+/// <b>`Addon.Default.Set(extensionpoint)`</b>
+/// ,
+/// Open a select dialog to allow choosing the default addon of the given type
+/// (extensionpoint)
+/// @param[in] extensionpoint The add-on type
+/// }
+/// \table_row2_l{
+/// <b>`Addon.OpenSettings(id)`</b>
+/// ,
+/// Open a settings dialog for the addon of the given id
+/// @param[in] id The add-on ID
+/// }
+/// \table_row2_l{
+/// <b>`EnableAddon(id)`</b>
+/// \anchor Builtin_EnableAddonId,
+/// Enable the specified plugin/script
+/// @param[in] id The add-on id
+/// <p><hr>
+/// @skinning_v19 **[New builtin]** \link Builtin_EnableAddonId `EnableAddon(id)`\endlink
+/// <p>
+/// }
+/// \table_row2_l{
+/// <b>`InstallAddon(id)`</b>
+/// ,
+/// Install the specified plugin/script
+/// @param[in] id The add-on id
+/// }
+/// \table_row2_l{
+/// <b>`InstallFromZip`</b>
+/// ,
+/// Opens the "Install from zip" dialog if "Unknown sources" is enabled. Prompts the warning message if not.
+/// }
+/// \table_row2_l{
+/// <b>`RunAddon(id[\,opt])`</b>
+/// ,
+/// Runs the specified plugin/script
+/// @param[in] id The add-on id.
+/// @param[in] opt is blank for no add-on parameters\n
+/// or
+/// @param[in] opt Add-on parameters in url format\n
+/// or
+/// @param[in] opt[\,...] Additional parameters in format param=value.
+/// }
+/// \table_row2_l{
+/// <b>`RunAppleScript(script[\,args]*)`</b>
+/// ,
+/// Run the specified AppleScript command
+/// @param[in] script Is the URL to the apple script\n
+/// or
+/// @param[in] script Is the addon-ID to the script add-on\n
+/// or
+/// @param[in] script Is the URL to the python script.
+///
+/// @note Set the OnlyApple template parameter to true to only attempt
+/// execution of applescripts.
+/// }
+/// \table_row2_l{
+/// <b>`RunPlugin(plugin)`</b>
+/// ,
+/// Runs the plugin. Full path must be specified. Does not work for folder
+/// plugins
+/// @param[in] plugin plugin:// URL to script.
+/// }
+/// \table_row2_l{
+/// <b>`RunScript(script[\,args]*)`</b>
+/// ,
+/// Runs the python script. You must specify the full path to the script. If
+/// the script is an add-on\, you can also execute it using its add-on id. As
+/// of 2007/02/24\, all extra parameters are passed to the script as arguments
+/// and can be accessed by python using sys.argv
+/// @param[in] script Is the addon-ID to the script add-on\n
+/// or
+/// @param[in] script Is the URL to the python script.
+/// }
+/// \table_row2_l{
+/// <b>`StopScript(id)`</b>
+/// ,
+/// Stop the script by ID or path\, if running
+/// @param[in] id The add-on ID of the script to stop\n
+/// or
+/// @param[in] id The URL of the script to stop.
+/// }
+/// \table_row2_l{
+/// <b>`UpdateAddonRepos`</b>
+/// ,
+/// Triggers a forced update of enabled add-on repositories.
+/// }
+/// \table_row2_l{
+/// <b>`UpdateLocalAddons`</b>
+/// ,
+/// Triggers a scan of local add-on directories.
+/// }
+/// \table_end
+///
+
+CBuiltins::CommandMap CAddonBuiltins::GetOperations() const
+{
+ return {
+ {"addon.default.opensettings", {"Open a settings dialog for the default addon of the given type", 1, OpenDefaultSettings}},
+ {"addon.default.set", {"Open a select dialog to allow choosing the default addon of the given type", 1, SetDefaultAddon}},
+ {"addon.opensettings", {"Open a settings dialog for the addon of the given id", 1, AddonSettings}},
+ {"enableaddon", {"Enables the specified plugin/script", 1, EnableAddon}},
+ {"installaddon", {"Install the specified plugin/script", 1, InstallAddon}},
+ {"installfromzip", { "Open the install from zip dialog", 0, InstallFromZip}},
+ {"runaddon", {"Run the specified plugin/script", 1, RunAddon}},
+#ifdef TARGET_DARWIN
+ {"runapplescript", {"Run the specified AppleScript command", 1, RunScript<true>}},
+#endif
+ {"runplugin", {"Run the specified plugin", 1, RunPlugin}},
+ {"runscript", {"Run the specified script", 1, RunScript}},
+ {"stopscript", {"Stop the script by ID or path, if running", 1, StopScript}},
+ {"updateaddonrepos", {"Check add-on repositories for updates", 0, UpdateRepos}},
+ {"updatelocaladdons", {"Check for local add-on changes", 0, UpdateLocals}}
+ };
+}
diff --git a/xbmc/interfaces/builtins/AddonBuiltins.h b/xbmc/interfaces/builtins/AddonBuiltins.h
new file mode 100644
index 0000000..9418420
--- /dev/null
+++ b/xbmc/interfaces/builtins/AddonBuiltins.h
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2005-2018 Team Kodi
+ * This file is part of Kodi - https://kodi.tv
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ * See LICENSES/README.md for more information.
+ */
+
+#pragma once
+
+#include "Builtins.h"
+
+//! \brief Class providing add-on related built-in commands.
+class CAddonBuiltins
+{
+public:
+ //! \brief Returns the map of operations.
+ CBuiltins::CommandMap GetOperations() const;
+};
diff --git a/xbmc/interfaces/builtins/AndroidBuiltins.cpp b/xbmc/interfaces/builtins/AndroidBuiltins.cpp
new file mode 100644
index 0000000..17f4076
--- /dev/null
+++ b/xbmc/interfaces/builtins/AndroidBuiltins.cpp
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2005-2018 Team Kodi
+ * This file is part of Kodi - https://kodi.tv
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ * See LICENSES/README.md for more information.
+ */
+
+#include "AndroidBuiltins.h"
+
+#include "ServiceBroker.h"
+#include "messaging/ApplicationMessenger.h"
+
+/*! \brief Launch an android system activity.
+ * \param params The parameters.
+ * \details params[0] = package
+ * params[1] = intent (optional)
+ * params[2] = datatype (optional)
+ * params[3] = dataURI (optional)
+ * params[4] = flags (optional)
+ * params[5] = extras (optional)
+ * params[6] = action (optional)
+ * params[7] = category (optional)
+ * params[8] = className (optional)
+ */
+static int LaunchAndroidActivity(const std::vector<std::string>& params)
+{
+ CServiceBroker::GetAppMessenger()->PostMsg(TMSG_START_ANDROID_ACTIVITY, -1, -1, nullptr, "",
+ params);
+
+ return 0;
+}
+
+// Note: For new Texts with comma add a "\" before!!! Is used for table text.
+//
+/// \page page_List_of_built_in_functions
+/// \section built_in_functions_2 Android built-in's
+///
+/// -----------------------------------------------------------------------------
+///
+/// \table_start
+/// \table_h2_l{
+/// Function,
+/// Description }
+/// \table_row2_l{
+/// <b>`StartAndroidActivity(package\,[intent\,dataType\,dataURI\,flags\,extras\,action\,category\,className])`</b>
+/// ,
+/// Launch an Android native app with the given package name. Optional parms
+/// (in order): intent\, dataType\, dataURI\, flags\, extras\, action\,
+/// category\, className.
+/// @param[in] package
+/// @param[in] intent (optional)
+/// @param[in] datatype (optional)
+/// @param[in] dataURI (optional)
+/// @param[in] flags (optional)
+/// @param[in] extras (optional)
+/// @param[in] action (optional)
+/// @param[in] category (optional)
+/// @param[in] className (optional)
+/// <p><hr>
+/// @skinning_v20 Added parameters `flags`\,`extras`\,`action`\,`category`\,`className`.
+/// <p>
+/// }
+/// \table_end
+///
+
+CBuiltins::CommandMap CAndroidBuiltins::GetOperations() const
+{
+ return {{"startandroidactivity",
+ {"Launch an Android native app with the given package name. Optional parms (in order): "
+ "intent, dataType, dataURI, flags, extras, action, category, className.",
+ 1, LaunchAndroidActivity}}};
+}
diff --git a/xbmc/interfaces/builtins/AndroidBuiltins.h b/xbmc/interfaces/builtins/AndroidBuiltins.h
new file mode 100644
index 0000000..d4059e2
--- /dev/null
+++ b/xbmc/interfaces/builtins/AndroidBuiltins.h
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2005-2018 Team Kodi
+ * This file is part of Kodi - https://kodi.tv
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ * See LICENSES/README.md for more information.
+ */
+
+#pragma once
+
+#include "Builtins.h"
+
+//! \brief Class providing CEC related built-in commands.
+class CAndroidBuiltins
+{
+public:
+ //! \brief Returns the map of operations.
+ CBuiltins::CommandMap GetOperations() const;
+};
diff --git a/xbmc/interfaces/builtins/ApplicationBuiltins.cpp b/xbmc/interfaces/builtins/ApplicationBuiltins.cpp
new file mode 100644
index 0000000..dfec66d
--- /dev/null
+++ b/xbmc/interfaces/builtins/ApplicationBuiltins.cpp
@@ -0,0 +1,222 @@
+/*
+ * Copyright (C) 2005-2018 Team Kodi
+ * This file is part of Kodi - https://kodi.tv
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ * See LICENSES/README.md for more information.
+ */
+
+#include "ApplicationBuiltins.h"
+
+#include "ServiceBroker.h"
+#include "application/ApplicationComponents.h"
+#include "application/ApplicationPowerHandling.h"
+#include "application/ApplicationVolumeHandling.h"
+#include "filesystem/ZipManager.h"
+#include "input/actions/ActionIDs.h"
+#include "interfaces/AnnouncementManager.h"
+#include "messaging/ApplicationMessenger.h"
+#include "network/Network.h"
+#include "settings/AdvancedSettings.h"
+#include "settings/Settings.h"
+#include "settings/SettingsComponent.h"
+#include "utils/JSONVariantParser.h"
+#include "utils/StringUtils.h"
+#include "utils/URIUtils.h"
+#include "utils/Variant.h"
+#include "utils/log.h"
+
+#include <stdlib.h>
+
+/*! \brief Extract an archive.
+ * \param params The parameters
+ * \details params[0] = The archive URL.
+ * params[1] = Destination path (optional).
+ * If not given, extracts to folder with archive.
+ */
+static int Extract(const std::vector<std::string>& params)
+{
+ // Detects if file is zip or rar then extracts
+ std::string strDestDirect;
+ if (params.size() < 2)
+ strDestDirect = URIUtils::GetDirectory(params[0]);
+ else
+ strDestDirect = params[1];
+
+ URIUtils::AddSlashAtEnd(strDestDirect);
+
+ if (URIUtils::IsZIP(params[0]))
+ g_ZipManager.ExtractArchive(params[0],strDestDirect);
+ else
+ CLog::Log(LOGERROR, "Extract, No archive given");
+
+ return 0;
+}
+
+/*! \brief Mute volume.
+ * \param params (ignored)
+ */
+static int Mute(const std::vector<std::string>& params)
+{
+ auto& components = CServiceBroker::GetAppComponents();
+ const auto appVolume = components.GetComponent<CApplicationVolumeHandling>();
+ appVolume->ToggleMute();
+
+ return 0;
+}
+
+/*! \brief Notify all listeners on announcement bus.
+ * \param params The parameters.
+ * \details params[0] = sender.
+ * params[1] = data.
+ * params[2] = JSON with extra parameters (optional).
+ */
+static int NotifyAll(const std::vector<std::string>& params)
+{
+ CVariant data;
+ if (params.size() > 2)
+ {
+ if (!CJSONVariantParser::Parse(params[2], data))
+ {
+ CLog::Log(LOGERROR, "NotifyAll failed to parse data: {}", params[2]);
+ return -3;
+ }
+ }
+
+ CServiceBroker::GetAnnouncementManager()->Announce(ANNOUNCEMENT::Other, params[0].c_str(), params[1].c_str(), data);
+
+ return 0;
+}
+
+/*! \brief Set volume.
+ * \param params the parameters.
+ * \details params[0] = Volume level.
+ * params[1] = "showVolumeBar" to show volume bar (optional).
+ */
+static int SetVolume(const std::vector<std::string>& params)
+{
+ auto& components = CServiceBroker::GetAppComponents();
+ const auto appVolume = components.GetComponent<CApplicationVolumeHandling>();
+ float oldVolume = appVolume->GetVolumePercent();
+ float volume = static_cast<float>(strtod(params[0].c_str(), nullptr));
+
+ appVolume->SetVolume(volume);
+ if (oldVolume != volume)
+ {
+ if (params.size() > 1 && StringUtils::EqualsNoCase(params[1], "showVolumeBar"))
+ {
+ CServiceBroker::GetAppMessenger()->PostMsg(
+ TMSG_VOLUME_SHOW, oldVolume < volume ? ACTION_VOLUME_UP : ACTION_VOLUME_DOWN);
+ }
+ }
+
+ return 0;
+}
+
+/*! \brief Toggle debug info.
+ * \param params (ignored)
+ */
+static int ToggleDebug(const std::vector<std::string>& params)
+{
+ bool debug = CServiceBroker::GetSettingsComponent()->GetSettings()->GetBool(CSettings::SETTING_DEBUG_SHOWLOGINFO);
+ CServiceBroker::GetSettingsComponent()->GetSettings()->SetBool(CSettings::SETTING_DEBUG_SHOWLOGINFO, !debug);
+ CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->SetDebugMode(!debug);
+
+ return 0;
+}
+
+/*! \brief Toggle DPMS state.
+ * \param params (ignored)
+ */
+static int ToggleDPMS(const std::vector<std::string>& params)
+{
+ auto& components = CServiceBroker::GetAppComponents();
+ const auto appPower = components.GetComponent<CApplicationPowerHandling>();
+ appPower->ToggleDPMS(true);
+
+ return 0;
+}
+
+/*! \brief Send a WOL packet to a given host.
+ * \param params The parameters.
+ * \details params[0] = The MAC of the host to wake.
+ */
+static int WakeOnLAN(const std::vector<std::string>& params)
+{
+ CServiceBroker::GetNetwork().WakeOnLan(params[0].c_str());
+
+ return 0;
+}
+
+// Note: For new Texts with comma add a "\" before!!! Is used for table text.
+//
+/// \page page_List_of_built_in_functions
+/// \section built_in_functions_3 Application built-in's
+///
+/// -----------------------------------------------------------------------------
+///
+/// \table_start
+/// \table_h2_l{
+/// Function,
+/// Description }
+/// \table_row2_l{
+/// <b>`Extract(url [\, dest])`</b>
+/// ,
+/// Extracts a specified archive to an optionally specified 'absolute' path.
+/// @param[in] url The archive URL.
+/// @param[in] dest Destination path (optional).
+/// @note If not given\, extracts to folder with archive.
+/// }
+/// \table_row2_l{
+/// <b>`Mute`</b>
+/// ,
+/// Mutes (or unmutes) the volume.
+/// }
+/// \table_row2_l{
+/// <b>`NotifyAll(sender\, data [\, json])`</b>
+/// ,
+/// Notify all connected clients
+/// @param[in] sender Sender.
+/// @param[in] data Data.
+/// @param[in] json JSON with extra parameters (optional).
+/// }
+/// \table_row2_l{
+/// <b>`SetVolume(percent[\,showvolumebar])`</b>
+/// ,
+/// Sets the volume to the percentage specified. Optionally\, show the Volume
+/// Dialog in Kodi when setting the volume.
+/// @param[in] percent Volume level.
+/// @param[in] showvolumebar Add "showVolumeBar" to show volume bar (optional).
+/// }
+/// \table_row2_l{
+/// <b>`ToggleDebug`</b>
+/// ,
+/// Toggles debug mode on/off
+/// }
+/// \table_row2_l{
+/// <b>`ToggleDPMS`</b>
+/// ,
+/// Toggle DPMS mode manually
+/// }
+/// \table_row2_l{
+/// <b>`WakeOnLan(mac)`</b>
+/// ,
+/// Sends the wake-up packet to the broadcast address for the specified MAC
+/// address (Format: FF:FF:FF:FF:FF:FF or FF-FF-FF-FF-FF-FF).
+/// @param[in] mac The MAC of the host to wake.
+/// }
+/// \table_end
+///
+
+CBuiltins::CommandMap CApplicationBuiltins::GetOperations() const
+{
+ return {
+ {"extract", {"Extracts the specified archive", 1, Extract}},
+ {"mute", {"Mute the player", 0, Mute}},
+ {"notifyall", {"Notify all connected clients", 2, NotifyAll}},
+ {"setvolume", {"Set the current volume", 1, SetVolume}},
+ {"toggledebug", {"Enables/disables debug mode", 0, ToggleDebug}},
+ {"toggledpms", {"Toggle DPMS mode manually", 0, ToggleDPMS}},
+ {"wakeonlan", {"Sends the wake-up packet to the broadcast address for the specified MAC address", 1, WakeOnLAN}}
+ };
+}
diff --git a/xbmc/interfaces/builtins/ApplicationBuiltins.h b/xbmc/interfaces/builtins/ApplicationBuiltins.h
new file mode 100644
index 0000000..f237fe4
--- /dev/null
+++ b/xbmc/interfaces/builtins/ApplicationBuiltins.h
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2005-2018 Team Kodi
+ * This file is part of Kodi - https://kodi.tv
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ * See LICENSES/README.md for more information.
+ */
+
+#pragma once
+
+#include "Builtins.h"
+
+//! \brief Class providing application related built-in commands.
+class CApplicationBuiltins
+{
+public:
+ //! \brief Returns the map of operations.
+ CBuiltins::CommandMap GetOperations() const;
+};
diff --git a/xbmc/interfaces/builtins/Builtins.cpp b/xbmc/interfaces/builtins/Builtins.cpp
new file mode 100644
index 0000000..624d9d1
--- /dev/null
+++ b/xbmc/interfaces/builtins/Builtins.cpp
@@ -0,0 +1,168 @@
+/*
+ * Copyright (C) 2005-2018 Team Kodi
+ * This file is part of Kodi - https://kodi.tv
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ * See LICENSES/README.md for more information.
+ */
+#include "Builtins.h"
+
+#include "AddonBuiltins.h"
+#include "ApplicationBuiltins.h"
+#include "CECBuiltins.h"
+#include "GUIBuiltins.h"
+#include "GUIContainerBuiltins.h"
+#include "GUIControlBuiltins.h"
+#include "LibraryBuiltins.h"
+#include "OpticalBuiltins.h"
+#include "PVRBuiltins.h"
+#include "PictureBuiltins.h"
+#include "PlayerBuiltins.h"
+#include "ProfileBuiltins.h"
+#include "ServiceBroker.h"
+#include "SkinBuiltins.h"
+#include "SystemBuiltins.h"
+#include "WeatherBuiltins.h"
+#include "input/InputManager.h"
+#include "powermanagement/PowerTypes.h"
+#include "settings/Settings.h"
+#include "settings/SettingsComponent.h"
+#include "utils/ExecString.h"
+#include "utils/StringUtils.h"
+#include "utils/log.h"
+
+#if defined(TARGET_ANDROID)
+#include "AndroidBuiltins.h"
+#endif
+
+#if defined(TARGET_POSIX)
+#include "PlatformDefs.h"
+#endif
+
+CBuiltins::CBuiltins()
+{
+ RegisterCommands<CAddonBuiltins>();
+ RegisterCommands<CApplicationBuiltins>();
+ RegisterCommands<CGUIBuiltins>();
+ RegisterCommands<CGUIContainerBuiltins>();
+ RegisterCommands<CGUIControlBuiltins>();
+ RegisterCommands<CLibraryBuiltins>();
+ RegisterCommands<COpticalBuiltins>();
+ RegisterCommands<CPictureBuiltins>();
+ RegisterCommands<CPlayerBuiltins>();
+ RegisterCommands<CProfileBuiltins>();
+ RegisterCommands<CPVRBuiltins>();
+ RegisterCommands<CSkinBuiltins>();
+ RegisterCommands<CSystemBuiltins>();
+ RegisterCommands<CWeatherBuiltins>();
+
+#if defined(HAVE_LIBCEC)
+ RegisterCommands<CCECBuiltins>();
+#endif
+
+#if defined(TARGET_ANDROID)
+ RegisterCommands<CAndroidBuiltins>();
+#endif
+}
+
+CBuiltins::~CBuiltins() = default;
+
+CBuiltins& CBuiltins::GetInstance()
+{
+ static CBuiltins sBuiltins;
+ return sBuiltins;
+}
+
+bool CBuiltins::HasCommand(const std::string& execString)
+{
+ const CExecString exec(execString);
+ if (!exec.IsValid())
+ return false;
+
+ const std::string function = exec.GetFunction();
+ const std::vector<std::string> parameters = exec.GetParams();
+
+ if (CServiceBroker::GetInputManager().HasBuiltin(function))
+ return true;
+
+ const auto& it = m_command.find(function);
+ if (it != m_command.end())
+ {
+ if (it->second.parameters == 0 || it->second.parameters <= parameters.size())
+ return true;
+ }
+
+ return false;
+}
+
+bool CBuiltins::IsSystemPowerdownCommand(const std::string& execString)
+{
+ const CExecString exec(execString);
+ if (!exec.IsValid())
+ return false;
+
+ const std::string execute = exec.GetFunction();
+
+ // Check if action is resulting in system powerdown.
+ if (execute == "reboot" ||
+ execute == "restart" ||
+ execute == "reset" ||
+ execute == "powerdown" ||
+ execute == "hibernate" ||
+ execute == "suspend" )
+ {
+ return true;
+ }
+ else if (execute == "shutdown")
+ {
+ switch (CServiceBroker::GetSettingsComponent()->GetSettings()->GetInt(CSettings::SETTING_POWERMANAGEMENT_SHUTDOWNSTATE))
+ {
+ case POWERSTATE_SHUTDOWN:
+ case POWERSTATE_SUSPEND:
+ case POWERSTATE_HIBERNATE:
+ return true;
+
+ default:
+ return false;
+ }
+ }
+ return false;
+}
+
+void CBuiltins::GetHelp(std::string &help)
+{
+ help.clear();
+
+ for (const auto& it : m_command)
+ {
+ help += it.first;
+ help += "\t";
+ help += it.second.description;
+ help += "\n";
+ }
+}
+
+int CBuiltins::Execute(const std::string& execString)
+{
+ const CExecString exec(execString);
+ if (!exec.IsValid())
+ return -1;
+
+ const std::string execute = exec.GetFunction();
+ const std::vector<std::string> params = exec.GetParams();
+
+ const auto& it = m_command.find(execute);
+ if (it != m_command.end())
+ {
+ if (it->second.parameters == 0 || params.size() >= it->second.parameters)
+ return it->second.Execute(params);
+ else
+ {
+ CLog::Log(LOGERROR, "{0} called with invalid number of parameters (should be: {1}, is {2})",
+ execute, it->second.parameters, params.size());
+ return -1;
+ }
+ }
+ else
+ return CServiceBroker::GetInputManager().ExecuteBuiltin(execute, params);
+}
diff --git a/xbmc/interfaces/builtins/Builtins.h b/xbmc/interfaces/builtins/Builtins.h
new file mode 100644
index 0000000..7d0345a
--- /dev/null
+++ b/xbmc/interfaces/builtins/Builtins.h
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2005-2018 Team Kodi
+ * This file is part of Kodi - https://kodi.tv
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ * See LICENSES/README.md for more information.
+ */
+
+#pragma once
+
+#include <map>
+#include <string>
+#include <vector>
+
+class CBuiltins
+{
+public:
+ //! \brief Struct representing a command from handler classes.
+ struct BUILT_IN
+ {
+ std::string description; //!< Description of command (help string)
+ size_t parameters; //!< Number of required parameters (can be 0)
+ int (*Execute)(const std::vector<std::string>& params); //!< Function to handle command
+ };
+
+ //! \brief A map of commands
+ typedef std::map<std::string,CBuiltins::BUILT_IN> CommandMap;
+
+ static CBuiltins& GetInstance();
+
+ bool HasCommand(const std::string& execString);
+ bool IsSystemPowerdownCommand(const std::string& execString);
+ void GetHelp(std::string &help);
+ int Execute(const std::string& execString);
+
+protected:
+ CBuiltins();
+ CBuiltins(const CBuiltins&) = delete;
+ const CBuiltins& operator=(const CBuiltins&) = delete;
+ virtual ~CBuiltins();
+
+private:
+ CommandMap m_command; //!< Map of registered commands
+
+
+ //! \brief Convenience template used to register commands from providers
+ template<class T>
+ void RegisterCommands()
+ {
+ T t;
+ CommandMap map = t.GetOperations();
+ m_command.insert(map.begin(), map.end());
+ }
+};
+
diff --git a/xbmc/interfaces/builtins/CECBuiltins.cpp b/xbmc/interfaces/builtins/CECBuiltins.cpp
new file mode 100644
index 0000000..d381816
--- /dev/null
+++ b/xbmc/interfaces/builtins/CECBuiltins.cpp
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2005-2018 Team Kodi
+ * This file is part of Kodi - https://kodi.tv
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ * See LICENSES/README.md for more information.
+ */
+
+#include "CECBuiltins.h"
+
+#include "ServiceBroker.h"
+#include "messaging/ApplicationMessenger.h"
+
+/*! \brief Wake up device through CEC.
+ * \param params (ignored)
+ */
+static int ActivateSource(const std::vector<std::string>& params)
+{
+ CServiceBroker::GetAppMessenger()->PostMsg(TMSG_CECACTIVATESOURCE);
+
+ return 0;
+}
+
+/*! \brief Put device in standby through CEC.
+ * \param params (ignored)
+ */
+static int Standby(const std::vector<std::string>& params)
+{
+ CServiceBroker::GetAppMessenger()->PostMsg(TMSG_CECSTANDBY);
+
+ return 0;
+}
+
+/*! \brief Toggle device state through CEC.
+ * \param params (ignored)
+ */
+static int ToggleState(const std::vector<std::string>& params)
+{
+ bool result;
+ CServiceBroker::GetAppMessenger()->SendMsg(TMSG_CECTOGGLESTATE, 0, 0,
+ static_cast<void*>(&result));
+
+ return 0;
+}
+
+// Note: For new Texts with comma add a "\" before!!! Is used for table text.
+//
+/// \page page_List_of_built_in_functions
+/// \section built_in_functions_4 CEC built-in's
+///
+/// -----------------------------------------------------------------------------
+///
+/// \table_start
+/// \table_h2_l{
+/// Function,
+/// Description }
+/// \table_row2_l{
+/// <b>`CECActivateSource`</b>
+/// ,
+/// Wake up playing device via a CEC peripheral
+/// }
+/// \table_row2_l{
+/// <b>`CECStandby`</b>
+/// ,
+/// Put playing device on standby via a CEC peripheral
+/// }
+/// \table_row2_l{
+/// <b>`CECToggleState`</b>
+/// ,
+/// Toggle state of playing device via a CEC peripheral
+/// }
+/// \table_end
+///
+
+CBuiltins::CommandMap CCECBuiltins::GetOperations() const
+{
+ return {
+ {"cectogglestate", {"Toggle state of playing device via a CEC peripheral", 0, ToggleState}},
+ {"cecactivatesource", {"Wake up playing device via a CEC peripheral", 0, ActivateSource}},
+ {"cecstandby", {"Put playing device on standby via a CEC peripheral", 0, Standby}}
+ };
+}
diff --git a/xbmc/interfaces/builtins/CECBuiltins.h b/xbmc/interfaces/builtins/CECBuiltins.h
new file mode 100644
index 0000000..d06c291
--- /dev/null
+++ b/xbmc/interfaces/builtins/CECBuiltins.h
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2005-2018 Team Kodi
+ * This file is part of Kodi - https://kodi.tv
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ * See LICENSES/README.md for more information.
+ */
+
+#pragma once
+
+#include "Builtins.h"
+
+//! \brief Class providing CEC related built-in commands.
+class CCECBuiltins
+{
+public:
+ //! \brief Returns the map of operations.
+ CBuiltins::CommandMap GetOperations() const;
+};
diff --git a/xbmc/interfaces/builtins/CMakeLists.txt b/xbmc/interfaces/builtins/CMakeLists.txt
new file mode 100644
index 0000000..27785af
--- /dev/null
+++ b/xbmc/interfaces/builtins/CMakeLists.txt
@@ -0,0 +1,41 @@
+set(SOURCES AddonBuiltins.cpp
+ ApplicationBuiltins.cpp
+ Builtins.cpp
+ CECBuiltins.cpp
+ GUIBuiltins.cpp
+ GUIControlBuiltins.cpp
+ GUIContainerBuiltins.cpp
+ LibraryBuiltins.cpp
+ OpticalBuiltins.cpp
+ PictureBuiltins.cpp
+ PlayerBuiltins.cpp
+ ProfileBuiltins.cpp
+ PVRBuiltins.cpp
+ SkinBuiltins.cpp
+ SystemBuiltins.cpp
+ WeatherBuiltins.cpp)
+
+set(HEADERS AddonBuiltins.h
+ AndroidBuiltins.h
+ ApplicationBuiltins.h
+ Builtins.h
+ CECBuiltins.h
+ GUIBuiltins.h
+ GUIContainerBuiltins.h
+ GUIControlBuiltins.h
+ LibraryBuiltins.h
+ OpticalBuiltins.h
+ PictureBuiltins.h
+ PlayerBuiltins.h
+ ProfileBuiltins.h
+ PVRBuiltins.h
+ SkinBuiltins.h
+ SystemBuiltins.h
+ WeatherBuiltins.h)
+
+if(CORE_SYSTEM_NAME STREQUAL android)
+ list(APPEND SOURCES AndroidBuiltins.cpp)
+ list(APPEND HEADERS AndroidBuiltins.h)
+endif()
+
+core_add_library(interfaces_builtins)
diff --git a/xbmc/interfaces/builtins/GUIBuiltins.cpp b/xbmc/interfaces/builtins/GUIBuiltins.cpp
new file mode 100644
index 0000000..f368e69
--- /dev/null
+++ b/xbmc/interfaces/builtins/GUIBuiltins.cpp
@@ -0,0 +1,601 @@
+/*
+ * Copyright (C) 2005-2018 Team Kodi
+ * This file is part of Kodi - https://kodi.tv
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ * See LICENSES/README.md for more information.
+ */
+
+#include "GUIBuiltins.h"
+
+#include "ServiceBroker.h"
+#include "Util.h"
+#include "application/ApplicationComponents.h"
+#include "application/ApplicationPowerHandling.h"
+#include "dialogs/GUIDialogKaiToast.h"
+#include "dialogs/GUIDialogNumeric.h"
+#include "filesystem/Directory.h"
+#include "guilib/GUIComponent.h"
+#include "guilib/GUIWindowManager.h"
+#include "guilib/LocalizeStrings.h"
+#include "guilib/StereoscopicsManager.h"
+#include "input/WindowTranslator.h"
+#include "input/actions/Action.h"
+#include "input/actions/ActionIDs.h"
+#include "input/actions/ActionTranslator.h"
+#include "messaging/ApplicationMessenger.h"
+#include "settings/AdvancedSettings.h"
+#include "settings/SettingsComponent.h"
+#include "utils/AlarmClock.h"
+#include "utils/RssManager.h"
+#include "utils/Screenshot.h"
+#include "utils/StringUtils.h"
+#include "utils/URIUtils.h"
+#include "utils/log.h"
+#include "windows/GUIMediaWindow.h"
+
+/*! \brief Execute a GUI action.
+ * \param params The parameters.
+ * \details params[0] = Action to execute.
+ * params[1] = Window to send action to (optional).
+ */
+static int Action(const std::vector<std::string>& params)
+{
+ // try translating the action from our ButtonTranslator
+ unsigned int actionID;
+ if (CActionTranslator::TranslateString(params[0], actionID))
+ {
+ int windowID = params.size() == 2 ? CWindowTranslator::TranslateWindow(params[1]) : WINDOW_INVALID;
+ CServiceBroker::GetAppMessenger()->SendMsg(TMSG_GUI_ACTION, windowID, -1,
+ static_cast<void*>(new CAction(actionID)));
+ }
+
+ return 0;
+}
+
+/*! \brief Activate a window.
+ * \param params The parameters.
+ * \details params[0] = The window name.
+ * params[1] = Window starting folder (optional).
+ *
+ * Set the Replace template parameter to true to replace current
+ * window in history.
+ */
+ template<bool Replace>
+static int ActivateWindow(const std::vector<std::string>& params2)
+{
+ std::vector<std::string> params(params2);
+ // get the parameters
+ std::string strWindow;
+ if (params.size())
+ {
+ strWindow = params[0];
+ params.erase(params.begin());
+ }
+
+ // confirm the window destination is valid prior to switching
+ int iWindow = CWindowTranslator::TranslateWindow(strWindow);
+ if (iWindow != WINDOW_INVALID)
+ {
+ // compare the given directory param with the current active directory
+ // if no directory is given, and you switch from a video window to another
+ // we retain history, so it makes sense to not switch to the same window in
+ // that case
+ bool bIsSameStartFolder = true;
+ if (!params.empty())
+ {
+ CGUIWindow *activeWindow = CServiceBroker::GetGUI()->GetWindowManager().GetWindow(CServiceBroker::GetGUI()->GetWindowManager().GetActiveWindow());
+ if (activeWindow && activeWindow->IsMediaWindow())
+ bIsSameStartFolder = static_cast<CGUIMediaWindow*>(activeWindow)->IsSameStartFolder(params[0]);
+ }
+
+ // activate window only if window and path differ from the current active window
+ if (iWindow != CServiceBroker::GetGUI()->GetWindowManager().GetActiveWindow() || !bIsSameStartFolder)
+ {
+ // if the window doesn't change, make sure it knows it's gonna be replaced
+ // this ensures setting the start directory if we switch paths
+ // if we change windows, that's done anyway
+ if (Replace && !params.empty() &&
+ iWindow == CServiceBroker::GetGUI()->GetWindowManager().GetActiveWindow())
+ params.emplace_back("replace");
+
+ auto& components = CServiceBroker::GetAppComponents();
+ const auto appPower = components.GetComponent<CApplicationPowerHandling>();
+ appPower->WakeUpScreenSaverAndDPMS();
+ CServiceBroker::GetGUI()->GetWindowManager().ActivateWindow(iWindow, params, Replace);
+ return 0;
+ }
+ }
+ else
+ {
+ CLog::Log(LOGERROR, "Activate/ReplaceWindow called with invalid destination window: {}",
+ strWindow);
+ return false;
+ }
+
+ return 1;
+}
+
+/*! \brief Activate a window and give given controls focus.
+ * \param params The parameters.
+ * \details params[0] = The window name.
+ * params[1,...] = Pair of (container ID, focus item).
+ *
+ * Set the Replace template parameter to true to replace current
+ * window in history.
+ */
+ template<bool Replace>
+static int ActivateAndFocus(const std::vector<std::string>& params)
+{
+ std::string strWindow = params[0];
+
+ // confirm the window destination is valid prior to switching
+ int iWindow = CWindowTranslator::TranslateWindow(strWindow);
+ if (iWindow != WINDOW_INVALID)
+ {
+ if (iWindow != CServiceBroker::GetGUI()->GetWindowManager().GetActiveWindow())
+ {
+ // disable the screensaver
+ auto& components = CServiceBroker::GetAppComponents();
+ const auto appPower = components.GetComponent<CApplicationPowerHandling>();
+ appPower->WakeUpScreenSaverAndDPMS();
+ CServiceBroker::GetGUI()->GetWindowManager().ActivateWindow(iWindow, {}, Replace);
+
+ unsigned int iPtr = 1;
+ while (params.size() > iPtr + 1)
+ {
+ CGUIMessage msg(GUI_MSG_SETFOCUS, CServiceBroker::GetGUI()->GetWindowManager().GetActiveWindowOrDialog(),
+ atol(params[iPtr].c_str()),
+ (params.size() >= iPtr + 2) ? atol(params[iPtr + 1].c_str())+1 : 0);
+ CServiceBroker::GetGUI()->GetWindowManager().SendMessage(msg);
+ iPtr += 2;
+ }
+ return 0;
+ }
+
+ }
+ else
+ CLog::Log(LOGERROR, "Replace/ActivateWindowAndFocus called with invalid destination window: {}",
+ strWindow);
+
+ return 1;
+}
+
+/*! \brief Start an alarm clock
+ * \param params The parameters.
+ * \details param[0] = name
+ * param[1] = command
+ * param[2] = Length in seconds (optional).
+ * param[3] = "silent" to suppress notifications.
+ * param[3] = "loop" to loop the alarm.
+ */
+static int AlarmClock(const std::vector<std::string>& params)
+{
+ // format is alarmclock(name,command[,time,true,false]);
+ float seconds = 0;
+ if (params.size() > 2)
+ {
+ if (params[2].find(':') == std::string::npos)
+ seconds = static_cast<float>(atoi(params[2].c_str())*60);
+ else
+ seconds = (float)StringUtils::TimeStringToSeconds(params[2]);
+ }
+ else
+ { // check if shutdown is specified in particular, and get the time for it
+ std::string strHeading;
+ if (StringUtils::EqualsNoCase(params[0], "shutdowntimer"))
+ strHeading = g_localizeStrings.Get(20145);
+ else
+ strHeading = g_localizeStrings.Get(13209);
+ std::string strTime;
+ if( CGUIDialogNumeric::ShowAndGetNumber(strTime, strHeading) )
+ seconds = static_cast<float>(atoi(strTime.c_str())*60);
+ else
+ return false;
+ }
+ bool silent = false;
+ bool loop = false;
+ for (unsigned int i = 3; i < params.size() ; i++)
+ {
+ // check "true" for backward comp
+ if (StringUtils::EqualsNoCase(params[i], "true") || StringUtils::EqualsNoCase(params[i], "silent"))
+ silent = true;
+ else if (StringUtils::EqualsNoCase(params[i], "loop"))
+ loop = true;
+ }
+
+ if( g_alarmClock.IsRunning() )
+ g_alarmClock.Stop(params[0],silent);
+ // no negative times not allowed, loop must have a positive time
+ if (seconds < 0 || (seconds == 0 && loop))
+ return false;
+ g_alarmClock.Start(params[0], seconds, params[1], silent, loop);
+
+ return 0;
+}
+
+/*! \brief Cancel an alarm clock.
+ * \param params The parameters.
+ * \details params[0] = "true" to silently cancel alarm (optional).
+ */
+static int CancelAlarm(const std::vector<std::string>& params)
+{
+ bool silent = (params.size() > 1 &&
+ (StringUtils::EqualsNoCase(params[1], "true") ||
+ StringUtils::EqualsNoCase(params[1], "silent")));
+ g_alarmClock.Stop(params[0],silent);
+
+ return 0;
+}
+
+/*! \brief Clear a property in a window.
+ * \param params The parameters.
+ * \details params[0] = The property to clear.
+ * params[1] = The window to clear property in (optional).
+ */
+static int ClearProperty(const std::vector<std::string>& params)
+{
+ CGUIWindow *window = CServiceBroker::GetGUI()->GetWindowManager().GetWindow(params.size() > 1 ? CWindowTranslator::TranslateWindow(params[1]) : CServiceBroker::GetGUI()->GetWindowManager().GetActiveWindowOrDialog());
+ if (window)
+ window->SetProperty(params[0],"");
+
+ return 0;
+}
+
+/*! \brief Close a dialog.
+ * \param params The parameters.
+ * \details params[0] = "all" to close all dialogs, or dialog name.
+ * params[1] = "true" to force close (skip animations) (optional).
+ */
+static int CloseDialog(const std::vector<std::string>& params)
+{
+ bool bForce = false;
+ if (params.size() > 1 && StringUtils::EqualsNoCase(params[1], "true"))
+ bForce = true;
+ if (StringUtils::EqualsNoCase(params[0], "all"))
+ {
+ CServiceBroker::GetGUI()->GetWindowManager().CloseDialogs(bForce);
+ }
+ else
+ {
+ int id = CWindowTranslator::TranslateWindow(params[0]);
+ CGUIWindow *window = CServiceBroker::GetGUI()->GetWindowManager().GetWindow(id);
+ if (window && window->IsDialog())
+ static_cast<CGUIDialog*>(window)->Close(bForce);
+ }
+
+ return 0;
+}
+
+/*! \brief Send a notification.
+ * \param params The parameters.
+ * \details params[0] = Notification title.
+ * params[1] = Notification text.
+ * params[2] = Display time in milliseconds (optional).
+ * params[3] = Notification icon (optional).
+ */
+static int Notification(const std::vector<std::string>& params)
+{
+ if (params.size() < 2)
+ return -1;
+ if (params.size() == 4)
+ CGUIDialogKaiToast::QueueNotification(params[3],params[0],params[1],atoi(params[2].c_str()));
+ else if (params.size() == 3)
+ CGUIDialogKaiToast::QueueNotification("",params[0],params[1],atoi(params[2].c_str()));
+ else
+ CGUIDialogKaiToast::QueueNotification(params[0],params[1]);
+
+ return 0;
+}
+
+/*! \brief Refresh RSS feed.
+ * \param params (ignored)
+ */
+static int RefreshRSS(const std::vector<std::string>& params)
+{
+ CRssManager::GetInstance().Reload();
+
+ return 0;
+}
+
+/*! \brief Take a screenshot.
+ * \param params The parameters.
+ * \details params[0] = URL to save file to. Blank to use default.
+ * params[1] = "sync" to run synchronously (optional).
+ */
+static int Screenshot(const std::vector<std::string>& params)
+{
+ if (!params.empty())
+ {
+ // get the parameters
+ std::string strSaveToPath = params[0];
+ bool sync = false;
+ if (params.size() >= 2)
+ sync = StringUtils::EqualsNoCase(params[1], "sync");
+
+ if (!strSaveToPath.empty())
+ {
+ if (XFILE::CDirectory::Exists(strSaveToPath))
+ {
+ std::string file = CUtil::GetNextFilename(
+ URIUtils::AddFileToFolder(strSaveToPath, "screenshot{:05}.png"), 65535);
+
+ if (!file.empty())
+ {
+ CScreenShot::TakeScreenshot(file, sync);
+ }
+ else
+ {
+ CLog::Log(LOGWARNING, "Too many screen shots or invalid folder {}", strSaveToPath);
+ }
+ }
+ else
+ CScreenShot::TakeScreenshot(strSaveToPath, sync);
+ }
+ }
+ else
+ CScreenShot::TakeScreenshot();
+
+ return 0;
+}
+
+/*! \brief Set GUI language.
+ * \param params The parameters.
+ * \details params[0] = The language to use.
+ */
+static int SetLanguage(const std::vector<std::string>& params)
+{
+ CServiceBroker::GetAppMessenger()->PostMsg(TMSG_SETLANGUAGE, -1, -1, nullptr, params[0]);
+
+ return 0;
+}
+
+/*! \brief Set a property in a window.
+ * \param params The parameters.
+ * \details params[0] = The property to set.
+ * params[1] = The property value.
+ * params[2] = The window to set property in (optional).
+ */
+static int SetProperty(const std::vector<std::string>& params)
+{
+ CGUIWindow *window = CServiceBroker::GetGUI()->GetWindowManager().GetWindow(params.size() > 2 ? CWindowTranslator::TranslateWindow(params[2]) : CServiceBroker::GetGUI()->GetWindowManager().GetActiveWindowOrDialog());
+ if (window)
+ window->SetProperty(params[0],params[1]);
+
+ return 0;
+}
+
+/*! \brief Set GUI stereo mode.
+ * \param params The parameters.
+ * \details param[0] = Stereo mode identifier.
+ */
+static int SetStereoMode(const std::vector<std::string>& params)
+{
+ CAction action = CStereoscopicsManager::ConvertActionCommandToAction("SetStereoMode", params[0]);
+ if (action.GetID() != ACTION_NONE)
+ CServiceBroker::GetAppMessenger()->SendMsg(TMSG_GUI_ACTION, WINDOW_INVALID, -1,
+ static_cast<void*>(new CAction(action)));
+ else
+ {
+ CLog::Log(LOGERROR, "Builtin 'SetStereoMode' called with unknown parameter: {}", params[0]);
+ return -2;
+ }
+
+ return 0;
+}
+
+/*! \brief Toggle visualization of dirty regions.
+ * \param params Ignored.
+ */
+static int ToggleDirty(const std::vector<std::string>&)
+{
+ CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->ToggleDirtyRegionVisualization();
+
+ return 0;
+}
+
+// Note: For new Texts with comma add a "\" before!!! Is used for table text.
+//
+/// \page page_List_of_built_in_functions
+/// \section built_in_functions_5 GUI built-in's
+///
+/// -----------------------------------------------------------------------------
+///
+/// \table_start
+/// \table_h2_l{
+/// Function,
+/// Description }
+/// \table_row2_l{
+/// <b>`Action(action[\,window])`</b>
+/// ,
+/// Executes an action (same as in keymap) for the given window or the
+/// active window if the parameter window is omitted. The parameter window
+/// can either be the window's id\, or in the case of a standard window\, the
+/// window's name. See here for a list of window names\, and their respective
+/// ids.
+/// @param[in] action Action to execute.
+/// @param[in] window Window to send action to (optional).
+/// }
+/// \table_row2_l{
+/// <b>`CancelAlarm(name[\,silent])`</b>
+/// ,
+/// Cancel a running alarm. Set silent to true to hide the alarm notification.
+/// @param[in] silent Send "true" or "silent" to silently cancel alarm (optional).
+/// }
+/// \table_row2_l{
+/// <b>`AlarmClock(name\,command[\,time\,silent\,loop])`</b>
+/// ,
+/// Pops up a dialog asking for the length of time for the alarm (unless the
+/// parameter time is specified)\, and starts a timer. When the timer runs out\,
+/// it'll execute the built-in command (the parameter command) if it is
+/// specified\, otherwise it'll pop up an alarm notice. Add silent to hide the
+/// alarm notification. Add loop for the alarm to execute the command each
+/// time the specified time interval expires.
+/// @note if using any of the last optional parameters (silent or loop)\, both must
+/// be provided for any to take effect.
+/// <p>
+/// <b>Example:</b>
+/// The following example will create an alarmclock named `mytimer` which will silently
+/// fire (a single time) and set a property (timerelapsed) with value 1 in the window with
+/// id 1109 after 5 seconds have passed.
+/// ~~~~~~~~~~~~~
+/// AlarmClock(mytimer\,SetProperty(timerelapsed\,1\,1109)\,00:00:05\,silent\,false])
+/// ~~~~~~~~~~~~~
+/// <p>
+/// @param[in] name name
+/// @param[in] command command
+/// @param[in] time [opt] <b>(a)</b> Length in minutes or <b>(b)</b> a timestring in the format `hh:mm:ss` or `mm min`.
+/// @param[in] silent [opt] Send "silent" to suppress notifications.
+/// @param[in] loop [opt] Send "loop" to loop the alarm.
+/// }
+/// \table_row2_l{
+/// <b>`ActivateWindow(window[\,dir\, return])`</b>
+/// ,
+/// Opens the given window. The parameter window can either be the window's id\,
+/// or in the case of a standard window\, the window's name. See \ref window_ids "here" for a list
+/// of window names\, and their respective ids.
+/// If\, furthermore\, the window is
+/// Music\, Video\, Pictures\, or Program files\, then the optional dir parameter
+/// specifies which folder Kodi should default to once the window is opened.
+/// This must be a source as specified in sources.xml\, or a subfolder of a
+/// valid source. For some windows (MusicLibrary and VideoLibrary)\, a third
+/// parameter (return) may be specified\, which indicates that Kodi should use this
+/// folder as the "root" of the level\, and thus the "parent directory" action
+/// from within this folder will return the user to where they were prior to
+/// the window activating.
+/// @param[in] window The window name.
+/// @param[in] dir Window starting folder (optional).
+/// @param[in] return if dir should be used as the rootfolder of the level
+/// }
+/// \table_row2_l{
+/// <b>`ActivateWindowAndFocus(id1\, id2\,item1\, id3\,item2)`</b>
+/// ,
+/// Activate window with id1\, first focus control id2 and then focus control
+/// id3. if either of the controls is a container\, you can specify which
+/// item to focus (else\, set it to 0).
+/// @param[in] id1 The window name.
+/// @param[in] params[1\,...] Pair of (container ID\, focus item).
+/// }
+/// \table_row2_l{
+/// <b>`ClearProperty(key[\,id])`</b>
+/// ,
+/// Clears a window property for the current focused window/dialog(key)\, or
+/// the specified window (key\,id).
+/// @param[in] key The property to clear.
+/// @param[in] id The window to clear property in (optional).
+/// }
+/// \table_row2_l{
+/// <b>`Dialog.Close(dialog[\,force])`</b>
+/// ,
+/// Close a dialog. Set force to true to bypass animations. Use (all\,true)
+/// to close all opened dialogs at once.
+/// @param[in] dialog Send "all" to close all dialogs\, or dialog name.
+/// @param[in] force Send "true" to force close (skip animations) (optional).
+/// }
+/// \table_row2_l{
+/// <b>`Notification(header\,message[\,time\,image])`</b>
+/// ,
+/// Will display a notification dialog with the specified header and message\,
+/// in addition you can set the length of time it displays in milliseconds
+/// and a icon image.
+/// @param[in] header Notification title.
+/// @param[in] message Notification text.
+/// @param[in] time Display time in milliseconds (optional).
+/// @param[in] image Notification icon (optional).
+/// }
+/// \table_row2_l{
+/// <b>`RefreshRSS`</b>
+/// ,
+/// Reload RSS feeds from RSSFeeds.xml
+/// }
+/// \table_row2_l{
+/// <b>`ReplaceWindow(window\,dir)`</b>
+/// ,
+/// Replaces the current window with the given window. This is the same as
+/// ActivateWindow() but it doesn't update the window history list\, so when
+/// you go back from the new window it will not return to the previous
+/// window\, rather will return to the previous window's previous window.
+/// @param[in] window The window name.
+/// @param[in] dir Window starting folder (optional).
+/// }
+/// \table_row2_l{
+/// <b>`ReplaceWindowAndFocus(id1\, id2\,item1\, id3\,item2)`</b>
+/// ,
+/// Replace window with id1\, first focus control id2 and then focus control
+/// id3. if either of the controls is a container\, you can specify which
+/// item to focus (else\, set it to 0).
+/// @param[in] id1 The window name.
+/// @param[in] params[1\,...] Pair of (container ID\, focus item).
+/// }
+/// \table_row2_l{
+/// <b>`Resolution(resIdent)`</b>
+/// ,
+/// Change Kodi's Resolution (default is 4x3).
+/// param[in] resIdent A resolution identifier.
+/// | | Identifiers | |
+/// |:--------:|:-----------:|:--------:|
+/// | pal | pal16x9 | ntsc |
+/// | ntsc16x9 | 720p | 720psbs |
+/// | 720ptb | 1080psbs | 1080ptb |
+/// | 1080i | | |
+/// }
+/// \table_row2_l{
+/// <b>`SetGUILanguage(lang)`</b>
+/// ,
+/// Set GUI Language
+/// @param[in] lang The language to use.
+/// }
+/// \table_row2_l{
+/// <b>`SetProperty(key\,value[\,id])`</b>
+/// ,
+/// Sets a window property for the current window (key\,value)\, or the
+/// specified window (key\,value\,id).
+/// @param[in] key The property to set.
+/// @param[in] value The property value.
+/// @param[in] id The window to set property in (optional).
+/// }
+/// \table_row2_l{
+/// <b>`SetStereoMode(ident)`</b>
+/// ,
+/// Changes the stereo mode of the GUI.
+/// Params can be:
+/// toggle\, next\, previous\, select\, tomono or any of the supported stereomodes (off\,
+/// split_vertical\, split_horizontal\, row_interleaved\, hardware_based\, anaglyph_cyan_red\, anaglyph_green_magenta\, monoscopic)
+/// @param[in] ident Stereo mode identifier.
+/// }
+/// \table_row2_l{
+/// <b>`TakeScreenshot(url[\,sync)`</b>
+/// ,
+/// Takes a Screenshot
+/// @param[in] url URL to save file to. Blank to use default.
+/// @param[in] sync Add "sync" to run synchronously (optional).
+/// }
+/// \table_row2_l{
+/// <b>`ToggleDirtyRegionVisualization`</b>
+/// ,
+/// makes dirty regions visible for debugging proposes.
+/// }
+/// \table_end
+///
+
+CBuiltins::CommandMap CGUIBuiltins::GetOperations() const
+{
+ return {
+ {"action", {"Executes an action for the active window (same as in keymap)", 1, Action}},
+ {"cancelalarm", {"Cancels an alarm", 1, CancelAlarm}},
+ {"alarmclock", {"Prompt for a length of time and start an alarm clock", 2, AlarmClock}},
+ {"activatewindow", {"Activate the specified window", 1, ActivateWindow<false>}},
+ {"activatewindowandfocus", {"Activate the specified window and sets focus to the specified id", 1, ActivateAndFocus<false>}},
+ {"clearproperty", {"Clears a window property for the current focused window/dialog (key,value)", 1, ClearProperty}},
+ {"dialog.close", {"Close a dialog", 1, CloseDialog}},
+ {"notification", {"Shows a notification on screen, specify header, then message, and optionally time in milliseconds and a icon.", 2, Notification}},
+ {"refreshrss", {"Reload RSS feeds from RSSFeeds.xml", 0, RefreshRSS}},
+ {"replacewindow", {"Replaces the current window with the new one", 1, ActivateWindow<true>}},
+ {"replacewindowandfocus", {"Replaces the current window with the new one and sets focus to the specified id", 1, ActivateAndFocus<true>}},
+ {"setguilanguage", {"Set GUI Language", 1, SetLanguage}},
+ {"setproperty", {"Sets a window property for the current focused window/dialog (key,value)", 2, SetProperty}},
+ {"setstereomode", {"Changes the stereo mode of the GUI. Params can be: toggle, next, previous, select, tomono or any of the supported stereomodes (off, split_vertical, split_horizontal, row_interleaved, hardware_based, anaglyph_cyan_red, anaglyph_green_magenta, anaglyph_yellow_blue, monoscopic)", 1, SetStereoMode}},
+ {"takescreenshot", {"Takes a Screenshot", 0, Screenshot}},
+ {"toggledirtyregionvisualization", {"Enables/disables dirty-region visualization", 0, ToggleDirty}}
+ };
+}
diff --git a/xbmc/interfaces/builtins/GUIBuiltins.h b/xbmc/interfaces/builtins/GUIBuiltins.h
new file mode 100644
index 0000000..fd8f238
--- /dev/null
+++ b/xbmc/interfaces/builtins/GUIBuiltins.h
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2005-2018 Team Kodi
+ * This file is part of Kodi - https://kodi.tv
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ * See LICENSES/README.md for more information.
+ */
+
+#pragma once
+
+#include "Builtins.h"
+
+//! \brief Class providing GUI related built-in commands.
+class CGUIBuiltins
+{
+public:
+ //! \brief Returns the map of operations.
+ CBuiltins::CommandMap GetOperations() const;
+};
diff --git a/xbmc/interfaces/builtins/GUIContainerBuiltins.cpp b/xbmc/interfaces/builtins/GUIContainerBuiltins.cpp
new file mode 100644
index 0000000..a97565c
--- /dev/null
+++ b/xbmc/interfaces/builtins/GUIContainerBuiltins.cpp
@@ -0,0 +1,189 @@
+/*
+ * Copyright (C) 2005-2018 Team Kodi
+ * This file is part of Kodi - https://kodi.tv
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ * See LICENSES/README.md for more information.
+ */
+
+#include "GUIContainerBuiltins.h"
+
+#include "GUIUserMessages.h"
+#include "ServiceBroker.h"
+#include "guilib/GUIComponent.h"
+#include "guilib/GUIWindowManager.h"
+#include "utils/StringUtils.h"
+
+/*! \brief Change sort method.
+ * \param params (ignored)
+ *
+ * Set the Dir template parameter to 1 to switch to next sort method
+ * or -1 to switch to previous sort method.
+ */
+ template<int Dir>
+static int ChangeSortMethod(const std::vector<std::string>& params)
+{
+ CGUIMessage message(GUI_MSG_CHANGE_SORT_METHOD, CServiceBroker::GetGUI()->GetWindowManager().GetActiveWindow(), 0, 0, Dir);
+ CServiceBroker::GetGUI()->GetWindowManager().SendMessage(message);
+
+ return 0;
+}
+
+/*! \brief Change view mode.
+ * \param params (ignored)
+ *
+ * Set the Dir template parameter to 1 to switch to next view mode
+ * or -1 to switch to previous view mode.
+ */
+ template<int Dir>
+static int ChangeViewMode(const std::vector<std::string>& params)
+{
+ CGUIMessage message(GUI_MSG_CHANGE_VIEW_MODE, CServiceBroker::GetGUI()->GetWindowManager().GetActiveWindow(), 0, 0, Dir);
+ CServiceBroker::GetGUI()->GetWindowManager().SendMessage(message);
+
+ return 0;
+}
+
+/*! \brief Refresh a media window.
+ * \param params The parameters.
+ * \details params[0] = The URL to refresh window at.
+ */
+static int Refresh(const std::vector<std::string>& params)
+{ // NOTE: These messages require a media window, thus they're sent to the current activewindow.
+ // This shouldn't stop a dialog intercepting it though.
+ CGUIMessage message(GUI_MSG_NOTIFY_ALL, CServiceBroker::GetGUI()->GetWindowManager().GetActiveWindow(), 0, GUI_MSG_UPDATE, 1); // 1 to reset the history
+ message.SetStringParam(!params.empty() ? params[0] : "");
+ CServiceBroker::GetGUI()->GetWindowManager().SendMessage(message);
+
+ return 0;
+}
+
+/*! \brief Set sort method.
+ * \param params The parameters.
+ * \details params[0] = ID of sort method.
+ */
+static int SetSortMethod(const std::vector<std::string>& params)
+{
+ CGUIMessage message(GUI_MSG_CHANGE_SORT_METHOD, CServiceBroker::GetGUI()->GetWindowManager().GetActiveWindow(), 0, atoi(params[0].c_str()));
+ CServiceBroker::GetGUI()->GetWindowManager().SendMessage(message);
+
+ return 0;
+}
+
+/*! \brief Set view mode.
+ * \param params The parameters.
+ * \details params[0] = ID of view mode.
+ */
+static int SetViewMode(const std::vector<std::string>& params)
+{
+ CGUIMessage message(GUI_MSG_CHANGE_VIEW_MODE, CServiceBroker::GetGUI()->GetWindowManager().GetActiveWindow(), 0, atoi(params[0].c_str()));
+ CServiceBroker::GetGUI()->GetWindowManager().SendMessage(message);
+
+ return 0;
+}
+
+/*! \brief Toggle sort direction.
+ * \param params (ignored)
+ */
+static int ToggleSortDirection(const std::vector<std::string>& params)
+{
+ CGUIMessage message(GUI_MSG_CHANGE_SORT_DIRECTION, CServiceBroker::GetGUI()->GetWindowManager().GetActiveWindow(), 0, 0);
+ CServiceBroker::GetGUI()->GetWindowManager().SendMessage(message);
+
+ return 0;
+}
+
+/*! \brief Update a listing in a media window.
+ * \param params The parameters.
+ * \details params[0] = The URL to update listing at.
+ * params[1] = "replace" to reset history (optional).
+ */
+static int Update(const std::vector<std::string>& params)
+{
+ CGUIMessage message(GUI_MSG_NOTIFY_ALL, CServiceBroker::GetGUI()->GetWindowManager().GetActiveWindow(), 0, GUI_MSG_UPDATE, 0);
+ message.SetStringParam(params[0]);
+ if (params.size() > 1 && StringUtils::EqualsNoCase(params[1], "replace"))
+ message.SetParam2(1); // reset the history
+ CServiceBroker::GetGUI()->GetWindowManager().SendMessage(message);
+
+ return 0;
+}
+
+// Note: For new Texts with comma add a "\" before!!! Is used for table text.
+//
+/// \page page_List_of_built_in_functions
+/// \section built_in_functions_6 GUI container built-in's
+///
+/// -----------------------------------------------------------------------------
+///
+/// \table_start
+/// \table_h2_l{
+/// Function,
+/// Description }
+/// \table_row2_l{
+/// <b>`Container.NextSortMethod`</b>
+/// ,
+/// Change to the next sort method.
+/// }
+/// \table_row2_l{
+/// <b>`Container.NextViewMode`</b>
+/// ,
+/// Select the next view mode.
+/// }
+/// \table_row2_l{
+/// <b>`Container.PreviousSortMethod`</b>
+/// ,
+/// Change to the previous sort method.
+/// }
+/// \table_row2_l{
+/// <b>`Container.PreviousViewMode`</b>
+/// ,
+/// Select the previous view mode.
+/// }
+/// \table_row2_l{
+/// <b>`Container.Refresh(url)`</b>
+/// ,
+/// Refresh current listing
+/// @param[in] url The URL to refresh window at.
+/// }
+/// \table_row2_l{
+/// <b>`Container.SetSortMethod(id)`</b>
+/// ,
+/// Change to the specified sort method. (For list of ID's \ref SortBy "see List" of sort methods below)
+/// @param[in] id ID of sort method.
+/// }
+/// \table_row2_l{
+/// <b>`Container.SetViewMode(id)`</b>
+/// ,
+/// Set the current view mode (list\, icons etc.) to the given container id.
+/// @param[in] id ID of view mode.
+/// }
+/// \table_row2_l{
+/// <b>`Container.SortDirection`</b>
+/// ,
+/// Toggle the sort direction
+/// }
+/// \table_row2_l{
+/// <b>`Container.Update(url\,[replace])`</b>
+/// ,
+/// Update current listing. Send `Container.Update(path\,replace)` to reset the path history.
+/// @param[in] url The URL to update listing at.
+/// @param[in] replace "replace" to reset history (optional).
+/// }
+/// \table_end
+///
+
+CBuiltins::CommandMap CGUIContainerBuiltins::GetOperations() const
+{
+ return {
+ {"container.nextsortmethod", {"Change to the next sort method", 0, ChangeSortMethod<1>}},
+ {"container.nextviewmode", {"Move to the next view type (and refresh the listing)", 0, ChangeViewMode<1>}},
+ {"container.previoussortmethod", {"Change to the previous sort method", 0, ChangeSortMethod<-1>}},
+ {"container.previousviewmode", {"Move to the previous view type (and refresh the listing)", 0, ChangeViewMode<-1>}},
+ {"container.refresh", {"Refresh current listing", 0, Refresh}},
+ {"container.setsortdirection", {"Toggle the sort direction", 0, ToggleSortDirection}},
+ {"container.setsortmethod", {"Change to the specified sort method", 1, SetSortMethod}},
+ {"container.setviewmode", {"Move to the view with the given id", 1, SetViewMode}},
+ {"container.update", {"Update current listing. Send Container.Update(path,replace) to reset the path history", 1, Update}}
+ };
+}
diff --git a/xbmc/interfaces/builtins/GUIContainerBuiltins.h b/xbmc/interfaces/builtins/GUIContainerBuiltins.h
new file mode 100644
index 0000000..906afa1
--- /dev/null
+++ b/xbmc/interfaces/builtins/GUIContainerBuiltins.h
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2005-2018 Team Kodi
+ * This file is part of Kodi - https://kodi.tv
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ * See LICENSES/README.md for more information.
+ */
+
+#pragma once
+
+#include "Builtins.h"
+
+//! \brief Class providing GUI container related built-in commands.
+class CGUIContainerBuiltins
+{
+public:
+ //! \brief Returns the map of operations.
+ CBuiltins::CommandMap GetOperations() const;
+};
diff --git a/xbmc/interfaces/builtins/GUIControlBuiltins.cpp b/xbmc/interfaces/builtins/GUIControlBuiltins.cpp
new file mode 100644
index 0000000..d8d89fa
--- /dev/null
+++ b/xbmc/interfaces/builtins/GUIControlBuiltins.cpp
@@ -0,0 +1,238 @@
+/*
+ * Copyright (C) 2005-2018 Team Kodi
+ * This file is part of Kodi - https://kodi.tv
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ * See LICENSES/README.md for more information.
+ */
+
+#include "GUIControlBuiltins.h"
+
+#include "ServiceBroker.h"
+#include "guilib/GUIComponent.h"
+#include "guilib/GUIWindowManager.h"
+#include "input/WindowTranslator.h"
+#include "utils/StringUtils.h"
+
+/*! \brief Send a move event to a GUI control.
+ * \param params The parameters.
+ * \details params[0] = ID of control.
+ * params[1] = Offset of move.
+ */
+static int ControlMove(const std::vector<std::string>& params)
+{
+ CGUIMessage message(GUI_MSG_MOVE_OFFSET, CServiceBroker::GetGUI()->GetWindowManager().GetActiveWindowOrDialog(),
+ atoi(params[0].c_str()), atoi(params[1].c_str()));
+ CServiceBroker::GetGUI()->GetWindowManager().SendMessage(message);
+
+ return 0;
+}
+
+/*! \brief Send a click event to a GUI control.
+ * \param params The parameters.
+ * \details params[0] = ID of control.
+ * params[1] = ID for window with control (optional).
+ */
+static int SendClick(const std::vector<std::string>& params)
+{
+ if (params.size() == 2)
+ {
+ // have a window - convert it
+ int windowID = CWindowTranslator::TranslateWindow(params[0]);
+ CGUIMessage message(GUI_MSG_CLICKED, atoi(params[1].c_str()), windowID);
+ CServiceBroker::GetGUI()->GetWindowManager().SendMessage(message);
+ }
+ else
+ { // single param - assume you meant the focused window
+ CGUIMessage message(GUI_MSG_CLICKED, atoi(params[0].c_str()), CServiceBroker::GetGUI()->GetWindowManager().GetActiveWindowOrDialog());
+ CServiceBroker::GetGUI()->GetWindowManager().SendMessage(message);
+ }
+
+ return 0;
+}
+
+/*! \brief Send a message to a control.
+ * \param params The parameters.
+ * \details params[0] = ID of control.
+ * params[1] = Action name.
+ * \params[2] = ID of window with control (optional).
+ */
+static int SendMessage(const std::vector<std::string>& params)
+{
+ int controlID = atoi(params[0].c_str());
+ int windowID = (params.size() == 3) ? CWindowTranslator::TranslateWindow(params[2]) : CServiceBroker::GetGUI()->GetWindowManager().GetActiveWindow();
+ if (params[1] == "moveup")
+ CServiceBroker::GetGUI()->GetWindowManager().SendMessage(GUI_MSG_MOVE_OFFSET, windowID, controlID, 1);
+ else if (params[1] == "movedown")
+ CServiceBroker::GetGUI()->GetWindowManager().SendMessage(GUI_MSG_MOVE_OFFSET, windowID, controlID, -1);
+ else if (params[1] == "pageup")
+ CServiceBroker::GetGUI()->GetWindowManager().SendMessage(GUI_MSG_PAGE_UP, windowID, controlID);
+ else if (params[1] == "pagedown")
+ CServiceBroker::GetGUI()->GetWindowManager().SendMessage(GUI_MSG_PAGE_DOWN, windowID, controlID);
+ else if (params[1] == "click")
+ CServiceBroker::GetGUI()->GetWindowManager().SendMessage(GUI_MSG_CLICKED, controlID, windowID);
+
+ return 0;
+}
+
+/*! \brief Give a control focus.
+ * \param params The parameters.
+ * \details params[0] = ID of control.
+ * params[1] = ID of subitem of control (optional).
+ * params[2] = "absolute" to focus the absolute position instead of the relative one (optional).
+ */
+static int SetFocus(const std::vector<std::string>& params)
+{
+ int controlID = atol(params[0].c_str());
+ int subItem = (params.size() > 1) ? atol(params[1].c_str())+1 : 0;
+ int absID = 0;
+ if (params.size() > 2 && StringUtils::EqualsNoCase(params[2].c_str(), "absolute"))
+ absID = 1;
+ CGUIMessage msg(GUI_MSG_SETFOCUS, CServiceBroker::GetGUI()->GetWindowManager().GetActiveWindowOrDialog(), controlID, subItem, absID);
+ CServiceBroker::GetGUI()->GetWindowManager().SendMessage(msg);
+
+ return 0;
+}
+
+/*! \brief Set a control to visible.
+ * \param params The parameters.
+ * \details params[0] = ID of control.
+ */
+static int SetVisible(const std::vector<std::string>& params)
+{
+ int controlID = std::stol(params[0]);
+ CGUIMessage msg{GUI_MSG_VISIBLE,
+ CServiceBroker::GetGUI()->GetWindowManager().GetActiveWindowOrDialog(),
+ controlID};
+ CServiceBroker::GetGUI()->GetWindowManager().SendMessage(msg);
+
+ return 0;
+}
+
+/*! \brief Set a control to hidden.
+ * \param params The parameters.
+ * \details params[0] = ID of control.
+ */
+static int SetHidden(const std::vector<std::string>& params)
+{
+ int controlID = std::stol(params[0]);
+ CGUIMessage msg{GUI_MSG_HIDDEN,
+ CServiceBroker::GetGUI()->GetWindowManager().GetActiveWindowOrDialog(),
+ controlID};
+ CServiceBroker::GetGUI()->GetWindowManager().SendMessage(msg);
+
+ return 0;
+}
+
+/*! \brief Shift page in a control.
+ * \param params The parameters.
+ * \details params[0] = ID of control
+ *
+ * Set Message template parameter to GUI_MSG_PAGE_DOWN/GUI_MSG_PAGE_UP.
+ */
+ template<int Message>
+static int ShiftPage(const std::vector<std::string>& params)
+{
+ int id = atoi(params[0].c_str());
+ CGUIMessage message(Message, CServiceBroker::GetGUI()->GetWindowManager().GetActiveWindowOrDialog(), id);
+ CServiceBroker::GetGUI()->GetWindowManager().SendMessage(message);
+
+ return 0;
+}
+
+// Note: For new Texts with comma add a "\" before!!! Is used for table text.
+//
+/// \page page_List_of_built_in_functions
+/// \section built_in_functions_7 GUI control built-in's
+///
+/// -----------------------------------------------------------------------------
+///
+/// \table_start
+/// \table_h2_l{
+/// Function,
+/// Description }
+/// \table_row2_l{
+/// <b>`control.message(controlId\, action[\, windowId])`</b>
+/// ,
+/// Send a given message to a control within a given window
+/// @param[in] controlId ID of control.
+/// @param[in] action Action name.
+/// @param[in] windowId ID of window with control (optional).
+/// }
+/// \table_row2_l{
+/// <b>`control.move(id\, offset)`</b>
+/// ,
+/// Tells the specified control to 'move' to another entry specified by offset
+/// @param[in] id ID of control.
+/// @param[in] offset Offset of move.
+/// }
+/// \table_row2_l{
+/// <b>`control.setfocus(controlId[\, subitemId])`</b>
+/// ,
+/// Change current focus to a different control id
+/// @param[in] controlId ID of control.
+/// @param[in] subitemId ID of subitem of control (optional).
+/// @param[in] absolute "absolute" to focus the absolute position instead of the relative one (optional).
+/// }
+/// \table_row2_l{
+/// <b>`control.setvisible(controlId)`</b>
+/// \anchor Builtin_SetVisible,
+/// Set the control id to visible
+/// @param[in] controlId ID of control.
+/// <p><hr>
+/// @skinning_v20 **[New builtin]** \link Builtin_SetVisible `SetVisible(id)`\endlink
+/// <p>
+/// }
+/// \table_row2_l{
+/// <b>`control.sethidden(controlId)`</b>
+/// \anchor Builtin_SetHidden,
+/// Set the control id to hidden
+/// @param[in] controlId ID of control.
+/// <p><hr>
+/// @skinning_v20 **[New builtin]** \link Builtin_SetHidden `SetHidden(id)`\endlink
+/// <p>
+/// }
+/// \table_row2_l{
+/// <b>`pagedown(controlId)`</b>
+/// ,
+/// Send a page down event to the pagecontrol with given id
+/// @param[in] controlId ID of control.
+/// }
+/// \table_row2_l{
+/// <b>`pageup(controlId)`</b>
+/// ,
+/// Send a page up event to the pagecontrol with given id
+/// @param[in] controlId ID of control.
+/// }
+/// \table_row2_l{
+/// <b>`sendclick(controlId [\, windowId])`</b>
+/// ,
+/// Send a click message from the given control to the given window
+/// @param[in] controlId ID of control.
+/// @param[in] windowId ID for window with control (optional).
+/// }
+/// \table_row2_l{
+/// <b>`setfocus`</b>
+/// ,
+/// Change current focus to a different control id
+/// @param[in] controlId ID of control.
+/// @param[in] subitemId ID of subitem of control (optional).
+/// @param[in] absolute "absolute" to focus the absolute position instead of the relative one (optional).
+/// }
+/// \table_end
+///
+
+CBuiltins::CommandMap CGUIControlBuiltins::GetOperations() const
+{
+ return {
+ {"control.message", {"Send a given message to a control within a given window", 2, SendMessage}},
+ {"control.move", {"Tells the specified control to 'move' to another entry specified by offset", 2, ControlMove}},
+ {"control.setfocus", {"Change current focus to a different control id", 1, SetFocus}},
+ {"control.setvisible", {"Set the control id to visible", 1, SetVisible}},
+ {"control.sethidden", {"Set the control id to Hidden", 1, SetHidden}},
+ {"pagedown", {"Send a page down event to the pagecontrol with given id", 1, ShiftPage<GUI_MSG_PAGE_DOWN>}},
+ {"pageup", {"Send a page up event to the pagecontrol with given id", 1, ShiftPage<GUI_MSG_PAGE_UP>}},
+ {"sendclick", {"Send a click message from the given control to the given window", 1, SendClick}},
+ {"setfocus", {"Change current focus to a different control id", 1, SetFocus}},
+ };
+}
diff --git a/xbmc/interfaces/builtins/GUIControlBuiltins.h b/xbmc/interfaces/builtins/GUIControlBuiltins.h
new file mode 100644
index 0000000..70831d6
--- /dev/null
+++ b/xbmc/interfaces/builtins/GUIControlBuiltins.h
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2005-2018 Team Kodi
+ * This file is part of Kodi - https://kodi.tv
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ * See LICENSES/README.md for more information.
+ */
+
+#pragma once
+
+#include "Builtins.h"
+
+//! \brief Class providing GUI control related built-in commands.
+class CGUIControlBuiltins
+{
+public:
+ //! \brief Returns the map of operations.
+ CBuiltins::CommandMap GetOperations() const;
+};
diff --git a/xbmc/interfaces/builtins/LibraryBuiltins.cpp b/xbmc/interfaces/builtins/LibraryBuiltins.cpp
new file mode 100644
index 0000000..5cb678a
--- /dev/null
+++ b/xbmc/interfaces/builtins/LibraryBuiltins.cpp
@@ -0,0 +1,416 @@
+/*
+ * Copyright (C) 2005-2018 Team Kodi
+ * This file is part of Kodi - https://kodi.tv
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ * See LICENSES/README.md for more information.
+ */
+
+#include "LibraryBuiltins.h"
+
+#include "GUIUserMessages.h"
+#include "MediaSource.h"
+#include "ServiceBroker.h"
+#include "dialogs/GUIDialogFileBrowser.h"
+#include "dialogs/GUIDialogYesNo.h"
+#include "guilib/GUIComponent.h"
+#include "guilib/GUIWindowManager.h"
+#include "guilib/LocalizeStrings.h"
+#include "messaging/helpers/DialogHelper.h"
+#include "messaging/helpers/DialogOKHelper.h"
+#include "music/MusicLibraryQueue.h"
+#include "music/infoscanner/MusicInfoScanner.h"
+#include "settings/LibExportSettings.h"
+#include "settings/Settings.h"
+#include "settings/SettingsComponent.h"
+#include "storage/MediaManager.h"
+#include "utils/StringUtils.h"
+#include "utils/log.h"
+#include "video/VideoDatabase.h"
+#include "video/VideoLibraryQueue.h"
+
+using namespace KODI::MESSAGING;
+
+/*! \brief Clean a library.
+ * \param params The parameters.
+ * \details params[0] = "video" or "music".
+ */
+static int CleanLibrary(const std::vector<std::string>& params)
+{
+ bool userInitiated = true;
+ if (params.size() > 1)
+ userInitiated = StringUtils::EqualsNoCase(params[1], "true");
+ if (!params.size() || StringUtils::EqualsNoCase(params[0], "video")
+ || StringUtils::EqualsNoCase(params[0], "movies")
+ || StringUtils::EqualsNoCase(params[0], "tvshows")
+ || StringUtils::EqualsNoCase(params[0], "musicvideos"))
+ {
+ if (!CVideoLibraryQueue::GetInstance().IsScanningLibrary())
+ {
+ if (userInitiated && CVideoLibraryQueue::GetInstance().IsRunning())
+ HELPERS::ShowOKDialogText(CVariant{700}, CVariant{703});
+ else
+ {
+ const std::string content = (params.empty() || params[0] == "video") ? "" : params[0];
+ const std::string directory = params.size() > 2 ? params[2] : "";
+
+ std::set<int> paths;
+ if (!content.empty() || !directory.empty())
+ {
+ CVideoDatabase db;
+ std::set<std::string> contentPaths;
+ if (db.Open())
+ {
+ if (!directory.empty())
+ contentPaths.insert(directory);
+ else
+ db.GetPaths(contentPaths);
+ for (const std::string& path : contentPaths)
+ {
+ if (db.GetContentForPath(path) == content)
+ {
+ paths.insert(db.GetPathId(path));
+ std::vector<std::pair<int, std::string>> sub;
+ if (db.GetSubPaths(path, sub))
+ {
+ for (const auto& it : sub)
+ paths.insert(it.first);
+ }
+ }
+ }
+ }
+ if (paths.empty())
+ return 0;
+ }
+
+ if (userInitiated)
+ CVideoLibraryQueue::GetInstance().CleanLibraryModal(paths);
+ else
+ CVideoLibraryQueue::GetInstance().CleanLibrary(paths, true);
+ }
+ }
+ else
+ CLog::Log(LOGERROR, "CleanLibrary is not possible while scanning or cleaning");
+ }
+ else if (StringUtils::EqualsNoCase(params[0], "music"))
+ {
+ if (!CMusicLibraryQueue::GetInstance().IsScanningLibrary())
+ {
+ if (!(userInitiated && CMusicLibraryQueue::GetInstance().IsRunning()))
+ CMusicLibraryQueue::GetInstance().CleanLibrary(userInitiated);
+ }
+ else
+ CLog::Log(LOGERROR, "CleanLibrary is not possible while scanning for media info");
+ }
+ else
+ CLog::Log(LOGERROR, "Unknown content type '{}' passed to CleanLibrary, ignoring", params[0]);
+
+ return 0;
+}
+
+/*! \brief Export a library.
+ * \param params The parameters.
+ * \details params[0] = "video" or "music".
+ * params[1] = "true" to export to separate files (optional).
+ * params[2] = "true" to export thumbs (optional) or the file path for export to singlefile.
+ * params[3] = "true" to overwrite existing files (optional).
+ * params[4] = "true" to export actor thumbs (optional).
+ */
+static int ExportLibrary(const std::vector<std::string>& params)
+{
+ int iHeading = 647;
+ if (StringUtils::EqualsNoCase(params[0], "music"))
+ iHeading = 20196;
+ std::string path;
+ VECSOURCES shares;
+ CServiceBroker::GetMediaManager().GetLocalDrives(shares);
+ CServiceBroker::GetMediaManager().GetNetworkLocations(shares);
+ CServiceBroker::GetMediaManager().GetRemovableDrives(shares);
+ bool singleFile;
+ bool thumbs=false;
+ bool actorThumbs=false;
+ bool overwrite=false;
+ bool cancelled=false;
+
+ if (params.size() > 1)
+ singleFile = StringUtils::EqualsNoCase(params[1], "false");
+ else
+ {
+ HELPERS::DialogResponse result = HELPERS::ShowYesNoDialogText(CVariant{iHeading}, CVariant{20426}, CVariant{20428}, CVariant{20429});
+ cancelled = result == HELPERS::DialogResponse::CHOICE_CANCELLED;
+ singleFile = result != HELPERS::DialogResponse::CHOICE_YES;
+ }
+
+ if (cancelled)
+ return -1;
+
+ if (!singleFile)
+ {
+ if (params.size() > 2)
+ thumbs = StringUtils::EqualsNoCase(params[2], "true");
+ else
+ {
+ HELPERS::DialogResponse result = HELPERS::ShowYesNoDialogText(CVariant{iHeading}, CVariant{20430});
+ cancelled = result == HELPERS::DialogResponse::CHOICE_CANCELLED;
+ thumbs = result == HELPERS::DialogResponse::CHOICE_YES;
+ }
+ }
+
+ if (cancelled)
+ return -1;
+
+ if (thumbs && !singleFile && StringUtils::EqualsNoCase(params[0], "video"))
+ {
+ std::string movieSetsInfoPath = CServiceBroker::GetSettingsComponent()->GetSettings()->
+ GetString(CSettings::SETTING_VIDEOLIBRARY_MOVIESETSFOLDER);
+ if (movieSetsInfoPath.empty())
+ {
+ auto result = HELPERS::ShowYesNoDialogText(CVariant{iHeading}, CVariant{36301});
+ cancelled = result != HELPERS::DialogResponse::CHOICE_YES;
+ }
+ }
+
+ if (cancelled)
+ return -1;
+
+ if (thumbs && StringUtils::EqualsNoCase(params[0], "video"))
+ {
+ if (params.size() > 4)
+ actorThumbs = StringUtils::EqualsNoCase(params[4], "true");
+ else
+ {
+ HELPERS::DialogResponse result = HELPERS::ShowYesNoDialogText(CVariant{iHeading}, CVariant{20436});
+ cancelled = result == HELPERS::DialogResponse::CHOICE_CANCELLED;
+ actorThumbs = result == HELPERS::DialogResponse::CHOICE_YES;
+ }
+ }
+
+ if (cancelled)
+ return -1;
+
+ if (!singleFile)
+ {
+ if (params.size() > 3)
+ overwrite = StringUtils::EqualsNoCase(params[3], "true");
+ else
+ {
+ HELPERS::DialogResponse result = HELPERS::ShowYesNoDialogText(CVariant{iHeading}, CVariant{20431});
+ cancelled = result == HELPERS::DialogResponse::CHOICE_CANCELLED;
+ overwrite = result == HELPERS::DialogResponse::CHOICE_YES;
+ }
+ }
+
+ if (cancelled)
+ return -1;
+
+ if (params.size() > 2)
+ path=params[2];
+ if (!singleFile || !path.empty() ||
+ CGUIDialogFileBrowser::ShowAndGetDirectory(shares, g_localizeStrings.Get(661),
+ path, true))
+ {
+ if (StringUtils::EqualsNoCase(params[0], "video"))
+ {
+ CVideoDatabase videodatabase;
+ videodatabase.Open();
+ videodatabase.ExportToXML(path, singleFile, thumbs, actorThumbs, overwrite);
+ videodatabase.Close();
+ }
+ else
+ {
+ CLibExportSettings settings;
+ // ELIBEXPORT_SINGLEFILE, ELIBEXPORT_ALBUMS + ELIBEXPORT_ALBUMARTISTS by default
+ settings.m_strPath = path;
+ if (!singleFile)
+ settings.SetExportType(ELIBEXPORT_TOLIBRARYFOLDER);
+ settings.m_artwork = thumbs;
+ settings.m_overwrite = overwrite;
+ // Export music library (not showing progress dialog)
+ CMusicLibraryQueue::GetInstance().ExportLibrary(settings, false);
+ }
+ }
+
+ return 0;
+}
+
+/*! \brief Export a library with extended parameters
+Avoiding breaking change to original ExportLibrary routine parameters
+* \param params The parameters.
+* \details params[0] = "video" or "music".
+* params[1] = export type "singlefile", "separate", or "library".
+* params[2] = path of destination folder.
+* params[3,...] = "unscraped" to include unscraped items
+* params[3,...] = "overwrite" to overwrite existing files.
+* params[3,...] = "artwork" to include images such as thumbs and fanart.
+* params[3,...] = "skipnfo" to not include nfo files (just art).
+* params[3,...] = "ablums" to include albums.
+* params[3,...] = "albumartists" to include album artists.
+* params[3,...] = "songartists" to include song artists.
+* params[3,...] = "otherartists" to include other artists.
+*/
+static int ExportLibrary2(const std::vector<std::string>& params)
+{
+ CLibExportSettings settings;
+ if (params.size() < 3)
+ return -1;
+ settings.m_strPath = params[2];
+ settings.SetExportType(ELIBEXPORT_SINGLEFILE);
+ if (StringUtils::EqualsNoCase(params[1], "separate"))
+ settings.SetExportType(ELIBEXPORT_SEPARATEFILES);
+ else if (StringUtils::EqualsNoCase(params[1], "library"))
+ {
+ settings.SetExportType(ELIBEXPORT_TOLIBRARYFOLDER);
+ settings.m_strPath.clear();
+ }
+ settings.ClearItems();
+
+ for (unsigned int i = 2; i < params.size(); i++)
+ {
+ if (StringUtils::EqualsNoCase(params[i], "artwork"))
+ settings.m_artwork = true;
+ else if (StringUtils::EqualsNoCase(params[i], "overwrite"))
+ settings.m_overwrite = true;
+ else if (StringUtils::EqualsNoCase(params[i], "unscraped"))
+ settings.m_unscraped = true;
+ else if (StringUtils::EqualsNoCase(params[i], "skipnfo"))
+ settings.m_skipnfo = true;
+ else if (StringUtils::EqualsNoCase(params[i], "albums"))
+ settings.AddItem(ELIBEXPORT_ALBUMS);
+ else if (StringUtils::EqualsNoCase(params[i], "albumartists"))
+ settings.AddItem(ELIBEXPORT_ALBUMARTISTS);
+ else if (StringUtils::EqualsNoCase(params[i], "songartists"))
+ settings.AddItem(ELIBEXPORT_SONGARTISTS);
+ else if (StringUtils::EqualsNoCase(params[i], "otherartists"))
+ settings.AddItem(ELIBEXPORT_OTHERARTISTS);
+ else if (StringUtils::EqualsNoCase(params[i], "actorthumbs"))
+ settings.AddItem(ELIBEXPORT_ACTORTHUMBS);
+ }
+ if (StringUtils::EqualsNoCase(params[0], "music"))
+ {
+ // Export music library (not showing progress dialog)
+ CMusicLibraryQueue::GetInstance().ExportLibrary(settings, false);
+ }
+ else
+ {
+ CVideoDatabase videodatabase;
+ videodatabase.Open();
+ videodatabase.ExportToXML(settings.m_strPath, settings.IsSingleFile(),
+ settings.m_artwork, settings.IsItemExported(ELIBEXPORT_ACTORTHUMBS), settings.m_overwrite);
+ videodatabase.Close();
+ }
+ return 0;
+}
+
+
+/*! \brief Update a library.
+ * \param params The parameters.
+ * \details params[0] = "video" or "music".
+ * params[1] = "true" to suppress dialogs (optional).
+ */
+static int UpdateLibrary(const std::vector<std::string>& params)
+{
+ bool userInitiated = true;
+ if (params.size() > 2)
+ userInitiated = StringUtils::EqualsNoCase(params[2], "true");
+ if (StringUtils::EqualsNoCase(params[0], "music"))
+ {
+ if (CMusicLibraryQueue::GetInstance().IsScanningLibrary())
+ CMusicLibraryQueue::GetInstance().StopLibraryScanning();
+ else
+ CMusicLibraryQueue::GetInstance().ScanLibrary(params.size() > 1 ? params[1] : "",
+ MUSIC_INFO::CMusicInfoScanner::SCAN_NORMAL,
+ userInitiated);
+ }
+ else if (StringUtils::EqualsNoCase(params[0], "video"))
+ {
+ if (CVideoLibraryQueue::GetInstance().IsScanningLibrary())
+ CVideoLibraryQueue::GetInstance().StopLibraryScanning();
+ else
+ CVideoLibraryQueue::GetInstance().ScanLibrary(params.size() > 1 ? params[1] : "", false,
+ userInitiated);
+ }
+
+ return 0;
+}
+
+/*! \brief Open a video library search.
+ * \param params (ignored)
+ */
+static int SearchVideoLibrary(const std::vector<std::string>& params)
+{
+ CGUIMessage msg(GUI_MSG_SEARCH, 0, 0, 0);
+ CServiceBroker::GetGUI()->GetWindowManager().SendMessage(msg, WINDOW_VIDEO_NAV);
+
+ return 0;
+}
+
+// Note: For new Texts with comma add a "\" before!!! Is used for table text.
+//
+/// \page page_List_of_built_in_functions
+/// \section built_in_functions_8 Library built-in's
+///
+/// -----------------------------------------------------------------------------
+///
+/// \table_start
+/// \table_h2_l{
+/// Function,
+/// Description }
+/// \table_row2_l{
+/// <b>`cleanlibrary(type)`</b>
+/// ,
+/// Clean the video/music library
+/// @param[in] type "video"\, "movies"\, "tvshows"\, "musicvideos" or "music".
+/// }
+/// \table_row2_l{
+/// <b>`exportlibrary(type [\, exportSingeFile\, exportThumbs\, overwrite\, exportActorThumbs])`</b>
+/// ,
+/// Export the video/music library
+/// @param[in] type "video" or "music".
+/// @param[in] exportSingleFile Add "true" to export to separate files (optional).
+/// @param[in] exportThumbs Add "true" to export thumbs (optional).
+/// @param[in] overwrite Add "true" to overwrite existing files (optional).
+/// @param[in] exportActorThumbs Add "true" to export actor thumbs (optional).
+/// }
+/// \table_row2_l{
+/// <b>`exportlibrary2(library\, exportFiletype\, path [\, unscraped][\, overwrite][\, artwork][\, skipnfo]
+/// [\, albums][\, albumartists][\, songartists][\, otherartists][\, actorthumbs])`</b>
+/// ,
+/// Export the video/music library with extended parameters
+/// @param[in] library "video" or "music".
+/// @param[in] exportFiletype "singlefile"\, "separate" or "library".
+/// @param[in] path Path to destination folder.
+/// @param[in] unscraped Add "unscraped" to include unscraped items.
+/// @param[in] overwrite Add "overwrite" to overwrite existing files.
+/// @param[in] artwork Add "artwork" to include images such as thumbs and fanart.
+/// @param[in] skipnfo Add "skipnfo" to not include nfo files(just art).
+/// @param[in] albums Add "ablums" to include albums.
+/// @param[in] albumartists Add "albumartists" to include album artists.
+/// @param[in] songartists Add "songartists" to include song artists.
+/// @param[in] otherartists Add "otherartists" to include other artists.
+/// @param[in] actorthumbs Add "actorthumbs" to include other actor thumbs.
+/// }
+/// \table_row2_l{
+/// <b>`updatelibrary([type\, suppressDialogs])`</b>
+/// ,
+/// Update the selected library (music or video)
+/// @param[in] type "video" or "music".
+/// @param[in] suppressDialogs Add "true" to suppress dialogs (optional).
+/// }
+/// \table_row2_l{
+/// <b>`videolibrary.search`</b>
+/// ,
+/// Brings up a search dialog which will search the library
+/// }
+/// \table_end
+///
+
+CBuiltins::CommandMap CLibraryBuiltins::GetOperations() const
+{
+ return {
+ {"cleanlibrary", {"Clean the video/music library", 1, CleanLibrary}},
+ {"exportlibrary", {"Export the video/music library", 1, ExportLibrary}},
+ {"exportlibrary2", {"Export the video/music library", 1, ExportLibrary2}},
+ {"updatelibrary", {"Update the selected library (music or video)", 1, UpdateLibrary}},
+ {"videolibrary.search", {"Brings up a search dialog which will search the library", 0, SearchVideoLibrary}}
+ };
+}
diff --git a/xbmc/interfaces/builtins/LibraryBuiltins.h b/xbmc/interfaces/builtins/LibraryBuiltins.h
new file mode 100644
index 0000000..1bc8744
--- /dev/null
+++ b/xbmc/interfaces/builtins/LibraryBuiltins.h
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2005-2018 Team Kodi
+ * This file is part of Kodi - https://kodi.tv
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ * See LICENSES/README.md for more information.
+ */
+
+#pragma once
+
+#include "Builtins.h"
+
+//! \brief Class providing library related built-in commands.
+class CLibraryBuiltins
+{
+public:
+ //! \brief Returns the map of operations.
+ CBuiltins::CommandMap GetOperations() const;
+};
diff --git a/xbmc/interfaces/builtins/OpticalBuiltins.cpp b/xbmc/interfaces/builtins/OpticalBuiltins.cpp
new file mode 100644
index 0000000..4a0ec5d
--- /dev/null
+++ b/xbmc/interfaces/builtins/OpticalBuiltins.cpp
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2005-2018 Team Kodi
+ * This file is part of Kodi - https://kodi.tv
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ * See LICENSES/README.md for more information.
+ */
+
+#include "OpticalBuiltins.h"
+
+#include "ServiceBroker.h"
+
+#ifdef HAS_DVD_DRIVE
+#include "storage/MediaManager.h"
+#endif
+
+#ifdef HAS_CDDA_RIPPER
+#include "cdrip/CDDARipper.h"
+#endif
+
+/*! \brief Eject the tray of an optical drive.
+ * \param params (ignored)
+ */
+static int Eject(const std::vector<std::string>& params)
+{
+#ifdef HAS_DVD_DRIVE
+ CServiceBroker::GetMediaManager().ToggleTray();
+#endif
+
+ return 0;
+}
+
+/*! \brief Rip currently inserted CD.
+ * \param params (ignored)
+ */
+static int RipCD(const std::vector<std::string>& params)
+{
+#ifdef HAS_CDDA_RIPPER
+ KODI::CDRIP::CCDDARipper::GetInstance().RipCD();
+#endif
+
+ return 0;
+}
+
+// Note: For new Texts with comma add a "\" before!!! Is used for table text.
+//
+/// \page page_List_of_built_in_functions
+/// \section built_in_functions_9 Optical container built-in's
+///
+/// -----------------------------------------------------------------------------
+///
+/// \table_start
+/// \table_h2_l{
+/// Function,
+/// Description }
+/// \table_row2_l{
+/// <b>`EjectTray`</b>
+/// ,
+/// Either opens or closes the DVD tray\, depending on its current state.
+/// }
+/// \table_row2_l{
+/// <b>`RipCD`</b>
+/// ,
+/// Will rip the inserted CD from the DVD-ROM drive.
+/// }
+/// \table_end
+///
+
+CBuiltins::CommandMap COpticalBuiltins::GetOperations() const
+{
+ return {
+ {"ejecttray", {"Close or open the DVD tray", 0, Eject}},
+ {"ripcd", {"Rip the currently inserted audio CD", 0, RipCD}}
+ };
+}
diff --git a/xbmc/interfaces/builtins/OpticalBuiltins.h b/xbmc/interfaces/builtins/OpticalBuiltins.h
new file mode 100644
index 0000000..c1b218e
--- /dev/null
+++ b/xbmc/interfaces/builtins/OpticalBuiltins.h
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2005-2018 Team Kodi
+ * This file is part of Kodi - https://kodi.tv
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ * See LICENSES/README.md for more information.
+ */
+
+#pragma once
+
+#include "Builtins.h"
+
+//! \brief Class providing optical media related built-in commands.
+class COpticalBuiltins
+{
+public:
+ //! \brief Returns the map of operations.
+ CBuiltins::CommandMap GetOperations() const;
+};
diff --git a/xbmc/interfaces/builtins/PVRBuiltins.cpp b/xbmc/interfaces/builtins/PVRBuiltins.cpp
new file mode 100644
index 0000000..f87d0d6
--- /dev/null
+++ b/xbmc/interfaces/builtins/PVRBuiltins.cpp
@@ -0,0 +1,224 @@
+/*
+ * Copyright (C) 2005-2018 Team Kodi
+ * This file is part of Kodi - https://kodi.tv
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ * See LICENSES/README.md for more information.
+ */
+
+#include "PVRBuiltins.h"
+
+#include "GUIInfoManager.h"
+#include "ServiceBroker.h"
+#include "application/Application.h"
+#include "application/ApplicationComponents.h"
+#include "application/ApplicationPlayer.h"
+#include "guilib/GUIComponent.h"
+#include "guilib/GUIWindowManager.h"
+#include "guilib/guiinfo/GUIInfoLabels.h"
+#include "pvr/PVRManager.h"
+#include "pvr/guilib/PVRGUIActionsTimers.h"
+#include "pvr/windows/GUIWindowPVRGuide.h"
+#include "utils/StringUtils.h"
+#include "utils/log.h"
+
+#include <algorithm>
+#include <cstdlib>
+#include <regex>
+
+using namespace PVR;
+
+/*! \brief Search for missing channel icons
+ * \param params (ignored)
+ */
+static int SearchMissingIcons(const std::vector<std::string>& params)
+{
+ CServiceBroker::GetPVRManager().TriggerSearchMissingChannelIcons();
+ return 0;
+}
+
+/*! \brief will toggle recording of playing channel, if any.
+ * \param params (ignored)
+ */
+static int ToggleRecordPlayingChannel(const std::vector<std::string>& params)
+{
+ CServiceBroker::GetPVRManager().Get<PVR::GUI::Timers>().ToggleRecordingOnPlayingChannel();
+ return 0;
+}
+
+/*! \brief seeks to the given percentage in timeshift buffer, if timeshifting is supported.
+ * \param params The parameters
+ * \details params[0] = percentage to seek to in the timeshift buffer.
+ */
+static int SeekPercentage(const std::vector<std::string>& params)
+{
+ const auto& components = CServiceBroker::GetAppComponents();
+ const auto appPlayer = components.GetComponent<CApplicationPlayer>();
+
+ if (params.empty())
+ {
+ CLog::Log(LOGERROR,"PVR.SeekPercentage(n) - No argument given");
+ }
+ else
+ {
+ const float fTimeshiftPercentage = static_cast<float>(std::atof(params.front().c_str()));
+ if (fTimeshiftPercentage < 0 || fTimeshiftPercentage > 100)
+ {
+ CLog::Log(LOGERROR, "PVR.SeekPercentage(n) - Invalid argument ({:f}), must be in range 0-100",
+ fTimeshiftPercentage);
+ }
+ else if (appPlayer->IsPlaying())
+ {
+ CGUIInfoManager& infoMgr = CServiceBroker::GetGUI()->GetInfoManager();
+
+ int iTimeshiftProgressDuration = 0;
+ infoMgr.GetInt(iTimeshiftProgressDuration, PVR_TIMESHIFT_PROGRESS_DURATION,
+ INFO::DEFAULT_CONTEXT);
+
+ int iTimeshiftBufferStart = 0;
+ infoMgr.GetInt(iTimeshiftBufferStart, PVR_TIMESHIFT_PROGRESS_BUFFER_START,
+ INFO::DEFAULT_CONTEXT);
+
+ float fPlayerPercentage = static_cast<float>(iTimeshiftProgressDuration) /
+ static_cast<float>(g_application.GetTotalTime()) *
+ (fTimeshiftPercentage - static_cast<float>(iTimeshiftBufferStart));
+ fPlayerPercentage = std::max(0.0f, std::min(fPlayerPercentage, 100.0f));
+
+ g_application.SeekPercentage(fPlayerPercentage);
+ }
+ }
+ return 0;
+}
+
+namespace
+{
+/*! \brief Control PVR Guide window's EPG grid.
+ * \param params The parameters
+ * \details params[0] = Control to execute.
+ */
+int EpgGridControl(const std::vector<std::string>& params)
+{
+ if (params.empty())
+ {
+ CLog::Log(LOGERROR, "EpgGridControl(n) - No argument given");
+ return 0;
+ }
+
+ int activeWindow = CServiceBroker::GetGUI()->GetWindowManager().GetActiveWindow();
+ if (activeWindow != WINDOW_TV_GUIDE && activeWindow != WINDOW_RADIO_GUIDE)
+ {
+ CLog::Log(LOGERROR, "EpgGridControl(n) - Guide window not active");
+ return 0;
+ }
+
+ CGUIWindowPVRGuideBase* guideWindow = CServiceBroker::GetGUI()->GetWindowManager().GetWindow<CGUIWindowPVRGuideBase>(activeWindow);
+ if (!guideWindow)
+ {
+ CLog::Log(LOGERROR, "EpgGridControl(n) - Unable to get Guide window instance");
+ return 0;
+ }
+
+ std::string param(params[0]);
+ StringUtils::ToLower(param);
+
+ if (param == "firstprogramme")
+ {
+ guideWindow->GotoBegin();
+ }
+ else if (param == "lastprogramme")
+ {
+ guideWindow->GotoEnd();
+ }
+ else if (param == "currentprogramme")
+ {
+ guideWindow->GotoCurrentProgramme();
+ }
+ else if (param == "selectdate")
+ {
+ guideWindow->OpenDateSelectionDialog();
+ }
+ else if (StringUtils::StartsWithNoCase(param, "+") || StringUtils::StartsWithNoCase(param, "-"))
+ {
+ // jump back/forward n hours
+ if (std::regex_match(param, std::regex("[(-|+)|][0-9]+")))
+ {
+ guideWindow->GotoDate(std::atoi(param.c_str()));
+ }
+ else
+ {
+ CLog::Log(LOGERROR, "EpgGridControl(n) - invalid argument");
+ }
+ }
+ else if (param == "firstchannel")
+ {
+ guideWindow->GotoFirstChannel();
+ }
+ else if (param == "playingchannel")
+ {
+ guideWindow->GotoPlayingChannel();
+ }
+ else if (param == "lastchannel")
+ {
+ guideWindow->GotoLastChannel();
+ }
+ else if (param == "previousgroup")
+ {
+ guideWindow->ActivatePreviousChannelGroup();
+ }
+ else if (param == "nextgroup")
+ {
+ guideWindow->ActivateNextChannelGroup();
+ }
+ else if (param == "selectgroup")
+ {
+ guideWindow->OpenChannelGroupSelectionDialog();
+ }
+
+ return 0;
+}
+
+} // unnamed namespace
+
+// Note: For new Texts with comma add a "\" before!!! Is used for table text.
+//
+/// \page page_List_of_built_in_functions
+/// \section built_in_functions_10 PVR built-in's
+///
+/// -----------------------------------------------------------------------------
+///
+/// \table_start
+/// \table_h2_l{
+/// Function,
+/// Description }
+/// \table_row2_l{
+/// <b>`PVR.SearchMissingChannelIcons`</b>
+/// ,
+/// Will start a search for missing channel icons
+/// }
+/// \table_row2_l{
+/// <b>`PVR.ToggleRecordPlayingChannel`</b>
+/// ,
+/// Will toggle recording on playing channel\, if any
+/// }
+/// \table_row2_l{
+/// <b>`PVR.SeekPercentage`</b>
+/// ,
+/// Performs a seek to the given percentage in timeshift buffer\, if timeshifting is supported
+/// }
+/// \table_row2_l{
+/// <b>`PVR.EpgGridControl`</b>
+/// ,
+/// Control PVR Guide window's EPG grid
+/// }
+/// \table_end
+///
+
+CBuiltins::CommandMap CPVRBuiltins::GetOperations() const
+{
+ return {
+ {"pvr.searchmissingchannelicons", {"Search for missing channel icons", 0, SearchMissingIcons}},
+ {"pvr.togglerecordplayingchannel", {"Toggle recording on playing channel", 0, ToggleRecordPlayingChannel}},
+ {"pvr.seekpercentage", {"Performs a seek to the given percentage in timeshift buffer", 1, SeekPercentage}},
+ {"pvr.epggridcontrol", {"Control PVR Guide window's EPG grid", 1, EpgGridControl}},
+ };
+}
diff --git a/xbmc/interfaces/builtins/PVRBuiltins.h b/xbmc/interfaces/builtins/PVRBuiltins.h
new file mode 100644
index 0000000..a22259b
--- /dev/null
+++ b/xbmc/interfaces/builtins/PVRBuiltins.h
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2005-2018 Team Kodi
+ * This file is part of Kodi - https://kodi.tv
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ * See LICENSES/README.md for more information.
+ */
+
+#pragma once
+
+#include "Builtins.h"
+
+//! \brief Class providing PVR related built-in commands.
+class CPVRBuiltins
+{
+public:
+ //! \brief Returns the map of operations.
+ CBuiltins::CommandMap GetOperations() const;
+};
diff --git a/xbmc/interfaces/builtins/PictureBuiltins.cpp b/xbmc/interfaces/builtins/PictureBuiltins.cpp
new file mode 100644
index 0000000..15cfe0a
--- /dev/null
+++ b/xbmc/interfaces/builtins/PictureBuiltins.cpp
@@ -0,0 +1,135 @@
+/*
+ * Copyright (C) 2005-2018 Team Kodi
+ * This file is part of Kodi - https://kodi.tv
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ * See LICENSES/README.md for more information.
+ */
+
+#include "PictureBuiltins.h"
+
+#include "GUIUserMessages.h"
+#include "ServiceBroker.h"
+#include "guilib/GUIComponent.h"
+#include "guilib/GUIWindowManager.h"
+#include "utils/StringUtils.h"
+
+/*! \brief Show a picture.
+ * \param params The parameters.
+ * \details params[0] = URL of picture.
+ */
+static int Show(const std::vector<std::string>& params)
+{
+ CGUIMessage msg(GUI_MSG_SHOW_PICTURE, 0, 0);
+ msg.SetStringParam(params[0]);
+ CGUIWindow *pWindow = CServiceBroker::GetGUI()->GetWindowManager().GetWindow(WINDOW_SLIDESHOW);
+ if (pWindow)
+ pWindow->OnMessage(msg);
+
+ return 0;
+}
+
+/*! \brief Start a slideshow.
+ * \param params The parameters.
+ * \details params[0] = Path to run slideshow for.
+ * params[1,..] = "recursive" to run a recursive slideshow.
+ * params[1,...] = "random" to randomize slideshow.
+ * params[1,...] = "notrandom" to not randomize slideshow.
+ * params[1,...] = "pause" to start slideshow paused.
+ * params[1,...] = "beginslide=<number>" to start at a given slide.
+ *
+ * Set the template parameter Recursive to true to run a recursive slideshow.
+ */
+ template<bool Recursive>
+static int Slideshow(const std::vector<std::string>& params)
+{
+ std::string beginSlidePath;
+ // leave RecursiveSlideShow command as-is
+ unsigned int flags = 0;
+ if (Recursive)
+ flags |= 1;
+
+ // SlideShow(dir[,recursive][,[not]random][,pause][,beginslide="/path/to/start/slide.jpg"])
+ // the beginslide value need be escaped (for '"' or '\' in it, by backslash)
+ // and then quoted, or not. See CUtil::SplitParams()
+ else
+ {
+ for (unsigned int i = 1 ; i < params.size() ; i++)
+ {
+ if (StringUtils::EqualsNoCase(params[i], "recursive"))
+ flags |= 1;
+ else if (StringUtils::EqualsNoCase(params[i], "random")) // set fullscreen or windowed
+ flags |= 2;
+ else if (StringUtils::EqualsNoCase(params[i], "notrandom"))
+ flags |= 4;
+ else if (StringUtils::EqualsNoCase(params[i], "pause"))
+ flags |= 8;
+ else if (StringUtils::StartsWithNoCase(params[i], "beginslide="))
+ beginSlidePath = params[i].substr(11);
+ }
+ }
+
+ CGUIMessage msg(GUI_MSG_START_SLIDESHOW, 0, 0, flags);
+ std::vector<std::string> strParams;
+ strParams.push_back(params[0]);
+ strParams.push_back(beginSlidePath);
+ msg.SetStringParams(strParams);
+ CGUIWindow *pWindow = CServiceBroker::GetGUI()->GetWindowManager().GetWindow(WINDOW_SLIDESHOW);
+ if (pWindow)
+ pWindow->OnMessage(msg);
+
+ return 0;
+}
+
+// Note: For new Texts with comma add a "\" before!!! Is used for table text
+//
+/// \page page_List_of_built_in_functions
+/// \section built_in_functions_11 Picture built-in's
+///
+/// -----------------------------------------------------------------------------
+///
+/// \table_start
+/// \table_h2_l{
+/// Function,
+/// Description }
+/// \table_row2_l{
+/// <b>`RecursiveSlideShow(dir)`</b>
+/// ,
+/// Run a slideshow from the specified directory\, including all subdirs.
+/// @param[in] dir Path to run slideshow for.
+/// @param[in] random Add "random" to randomize slideshow (optional).
+/// @param[in] notrandom Add "notrandom" to not randomize slideshow (optional).
+/// @param[in] pause Add "pause" to start slideshow paused (optional).
+/// @param[in] beginslide Add "beginslide=<number>" to start at a given slide (optional).
+/// }
+/// \table_row2_l{
+/// <b>`ShowPicture(picture)`</b>
+/// ,
+/// Display a picture by file path.
+/// @param[in] url URL of picture.
+/// }
+/// \table_row2_l{
+/// <b>`SlideShow(dir [\,recursive\, [not]random])`</b>
+/// ,
+/// Starts a slideshow of pictures in the folder dir. Optional parameters are
+/// <b>recursive</b>\, and **random** or **notrandom** slideshow\, adding images
+/// from sub-folders. The **random** and **notrandom** parameters override
+/// the Randomize setting found in the pictures media window.
+/// @param[in] dir Path to run slideshow for.
+/// @param[in] recursive Add "recursive" to run a recursive slideshow (optional).
+/// @param[in] random Add "random" to randomize slideshow (optional).
+/// @param[in] notrandom Add "notrandom" to not randomize slideshow (optional).
+/// @param[in] pause Add "pause" to start slideshow paused (optional).
+/// @param[in] beginslide Add "beginslide=<number>" to start at a given slide (optional).
+/// }
+/// \table_end
+///
+
+CBuiltins::CommandMap CPictureBuiltins::GetOperations() const
+{
+ return {
+ {"recursiveslideshow", {"Run a slideshow from the specified directory, including all subdirs", 1, Slideshow<true>}},
+ {"showpicture", {"Display a picture by file path", 1, Show}},
+ {"slideshow", {"Run a slideshow from the specified directory", 1, Slideshow<false>}}
+ };
+}
diff --git a/xbmc/interfaces/builtins/PictureBuiltins.h b/xbmc/interfaces/builtins/PictureBuiltins.h
new file mode 100644
index 0000000..4dd396b
--- /dev/null
+++ b/xbmc/interfaces/builtins/PictureBuiltins.h
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2005-2018 Team Kodi
+ * This file is part of Kodi - https://kodi.tv
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ * See LICENSES/README.md for more information.
+ */
+
+#pragma once
+
+#include "Builtins.h"
+
+//! \brief Class providing picture related built-in commands.
+class CPictureBuiltins
+{
+public:
+ //! \brief Returns the map of operations.
+ CBuiltins::CommandMap GetOperations() const;
+};
diff --git a/xbmc/interfaces/builtins/PlayerBuiltins.cpp b/xbmc/interfaces/builtins/PlayerBuiltins.cpp
new file mode 100644
index 0000000..ca69d69
--- /dev/null
+++ b/xbmc/interfaces/builtins/PlayerBuiltins.cpp
@@ -0,0 +1,842 @@
+/*
+ * Copyright (C) 2005-2018 Team Kodi
+ * This file is part of Kodi - https://kodi.tv
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ * See LICENSES/README.md for more information.
+ */
+
+#include "PlayerBuiltins.h"
+
+#include "FileItem.h"
+#include "GUIUserMessages.h"
+#include "PartyModeManager.h"
+#include "PlayListPlayer.h"
+#include "SeekHandler.h"
+#include "ServiceBroker.h"
+#include "Util.h"
+#include "application/Application.h"
+#include "application/ApplicationComponents.h"
+#include "application/ApplicationPlayer.h"
+#include "application/ApplicationPowerHandling.h"
+#include "guilib/GUIComponent.h"
+#include "guilib/GUIWindowManager.h"
+#include "music/MusicUtils.h"
+#include "playlists/PlayList.h"
+#include "pvr/PVRManager.h"
+#include "pvr/channels/PVRChannel.h"
+#include "pvr/guilib/PVRGUIActionsChannels.h"
+#include "pvr/recordings/PVRRecording.h"
+#include "settings/MediaSettings.h"
+#include "settings/Settings.h"
+#include "settings/SettingsComponent.h"
+#include "storage/MediaManager.h"
+#include "utils/StringUtils.h"
+#include "utils/URIUtils.h"
+#include "utils/log.h"
+#include "video/PlayerController.h"
+#include "video/VideoUtils.h"
+#include "video/windows/GUIWindowVideoBase.h"
+
+#include <math.h>
+
+#ifdef HAS_DVD_DRIVE
+#include "Autorun.h"
+#endif
+
+/*! \brief Clear current playlist
+ * \param params (ignored)
+ */
+static int ClearPlaylist(const std::vector<std::string>& params)
+{
+ CServiceBroker::GetPlaylistPlayer().Clear();
+
+ return 0;
+}
+
+/*! \brief Start a playlist from a given offset.
+ * \param params The parameters.
+ * \details params[0] = Position in playlist or playlist type.
+ * params[1] = Position in playlist if params[0] is playlist type (optional).
+ */
+static int PlayOffset(const std::vector<std::string>& params)
+{
+ // playlist.playoffset(offset)
+ // playlist.playoffset(music|video,offset)
+ std::string strPos = params[0];
+ std::string paramlow(params[0]);
+ StringUtils::ToLower(paramlow);
+ if (params.size() > 1)
+ {
+ // ignore any other parameters if present
+ std::string strPlaylist = params[0];
+ strPos = params[1];
+
+ PLAYLIST::Id playlistId = PLAYLIST::TYPE_NONE;
+ if (paramlow == "music")
+ playlistId = PLAYLIST::TYPE_MUSIC;
+ else if (paramlow == "video")
+ playlistId = PLAYLIST::TYPE_VIDEO;
+
+ // unknown playlist
+ if (playlistId == PLAYLIST::TYPE_NONE)
+ {
+ CLog::Log(LOGERROR, "Playlist.PlayOffset called with unknown playlist: {}", strPlaylist);
+ return false;
+ }
+
+ // user wants to play the 'other' playlist
+ if (playlistId != CServiceBroker::GetPlaylistPlayer().GetCurrentPlaylist())
+ {
+ g_application.StopPlaying();
+ CServiceBroker::GetPlaylistPlayer().Reset();
+ CServiceBroker::GetPlaylistPlayer().SetCurrentPlaylist(playlistId);
+ }
+ }
+ // play the desired offset
+ int pos = atol(strPos.c_str());
+
+ const auto& components = CServiceBroker::GetAppComponents();
+ const auto appPlayer = components.GetComponent<CApplicationPlayer>();
+ // playlist is already playing
+ if (appPlayer->IsPlaying())
+ CServiceBroker::GetPlaylistPlayer().PlayNext(pos);
+ // we start playing the 'other' playlist so we need to use play to initialize the player state
+ else
+ CServiceBroker::GetPlaylistPlayer().Play(pos, "");
+
+ return 0;
+}
+
+/*! \brief Control player.
+ * \param params The parameters
+ * \details params[0] = Control to execute.
+ * params[1] = "notify" to notify user (optional, certain controls).
+ */
+static int PlayerControl(const std::vector<std::string>& params)
+{
+ auto& components = CServiceBroker::GetAppComponents();
+ const auto appPower = components.GetComponent<CApplicationPowerHandling>();
+ appPower->ResetScreenSaver();
+ appPower->WakeUpScreenSaverAndDPMS();
+
+ std::string paramlow(params[0]);
+ StringUtils::ToLower(paramlow);
+
+ const auto appPlayer = components.GetComponent<CApplicationPlayer>();
+
+ if (paramlow == "play")
+ { // play/pause
+ // either resume playing, or pause
+ if (appPlayer->IsPlaying())
+ {
+ if (appPlayer->GetPlaySpeed() != 1)
+ appPlayer->SetPlaySpeed(1);
+ else
+ appPlayer->Pause();
+ }
+ }
+ else if (paramlow == "stop")
+ {
+ g_application.StopPlaying();
+ }
+ else if (StringUtils::StartsWithNoCase(params[0], "frameadvance"))
+ {
+ std::string strFrames;
+ if (params[0].size() == 12)
+ CLog::Log(LOGERROR, "PlayerControl(frameadvance(n)) called with no argument");
+ else if (params[0].size() < 15) // arg must be at least "(N)"
+ CLog::Log(LOGERROR, "PlayerControl(frameadvance(n)) called with invalid argument: \"{}\"",
+ params[0].substr(13));
+ else
+
+ strFrames = params[0].substr(13);
+ StringUtils::TrimRight(strFrames, ")");
+ float frames = (float) atof(strFrames.c_str());
+ appPlayer->FrameAdvance(frames);
+ }
+ else if (paramlow =="rewind" || paramlow == "forward")
+ {
+ if (appPlayer->IsPlaying() && !appPlayer->IsPaused())
+ {
+ float playSpeed = appPlayer->GetPlaySpeed();
+
+ if (paramlow == "rewind" && playSpeed == 1) // Enables Rewinding
+ playSpeed *= -2;
+ else if (paramlow == "rewind" && playSpeed > 1) //goes down a notch if you're FFing
+ playSpeed /= 2;
+ else if (paramlow == "forward" && playSpeed < 1) //goes up a notch if you're RWing
+ {
+ playSpeed /= 2;
+ if (playSpeed == -1)
+ playSpeed = 1;
+ }
+ else
+ playSpeed *= 2;
+
+ if (playSpeed > 32 || playSpeed < -32)
+ playSpeed = 1;
+
+ appPlayer->SetPlaySpeed(playSpeed);
+ }
+ }
+ else if (paramlow =="tempoup" || paramlow == "tempodown")
+ {
+ if (appPlayer->SupportsTempo() && appPlayer->IsPlaying() && !appPlayer->IsPaused())
+ {
+ float playTempo = appPlayer->GetPlayTempo();
+ if (paramlow == "tempodown")
+ playTempo -= 0.1f;
+ else if (paramlow == "tempoup")
+ playTempo += 0.1f;
+
+ appPlayer->SetTempo(playTempo);
+ }
+ }
+ else if (StringUtils::StartsWithNoCase(params[0], "tempo"))
+ {
+ if (params[0].size() == 5)
+ CLog::Log(LOGERROR, "PlayerControl(tempo(n)) called with no argument");
+ else if (params[0].size() < 8) // arg must be at least "(N)"
+ CLog::Log(LOGERROR, "PlayerControl(tempo(n)) called with invalid argument: \"{}\"",
+ params[0].substr(6));
+ else
+ {
+ if (appPlayer->SupportsTempo() && appPlayer->IsPlaying() && !appPlayer->IsPaused())
+ {
+ std::string strTempo = params[0].substr(6);
+ StringUtils::TrimRight(strTempo, ")");
+ float playTempo = strtof(strTempo.c_str(), nullptr);
+
+ appPlayer->SetTempo(playTempo);
+ }
+ }
+ }
+ else if (paramlow == "next")
+ {
+ g_application.OnAction(CAction(ACTION_NEXT_ITEM));
+ }
+ else if (paramlow == "previous")
+ {
+ g_application.OnAction(CAction(ACTION_PREV_ITEM));
+ }
+ else if (paramlow == "bigskipbackward")
+ {
+ if (appPlayer->IsPlaying())
+ appPlayer->Seek(false, true);
+ }
+ else if (paramlow == "bigskipforward")
+ {
+ if (appPlayer->IsPlaying())
+ appPlayer->Seek(true, true);
+ }
+ else if (paramlow == "smallskipbackward")
+ {
+ if (appPlayer->IsPlaying())
+ appPlayer->Seek(false, false);
+ }
+ else if (paramlow == "smallskipforward")
+ {
+ if (appPlayer->IsPlaying())
+ appPlayer->Seek(true, false);
+ }
+ else if (StringUtils::StartsWithNoCase(params[0], "seekpercentage"))
+ {
+ std::string offset;
+ if (params[0].size() == 14)
+ CLog::Log(LOGERROR,"PlayerControl(seekpercentage(n)) called with no argument");
+ else if (params[0].size() < 17) // arg must be at least "(N)"
+ CLog::Log(LOGERROR, "PlayerControl(seekpercentage(n)) called with invalid argument: \"{}\"",
+ params[0].substr(14));
+ else
+ {
+ // Don't bother checking the argument: an invalid arg will do seek(0)
+ offset = params[0].substr(15);
+ StringUtils::TrimRight(offset, ")");
+ float offsetpercent = (float) atof(offset.c_str());
+ if (offsetpercent < 0 || offsetpercent > 100)
+ CLog::Log(LOGERROR, "PlayerControl(seekpercentage(n)) argument, {:f}, must be 0-100",
+ offsetpercent);
+ else if (appPlayer->IsPlaying())
+ g_application.SeekPercentage(offsetpercent);
+ }
+ }
+ else if (paramlow == "showvideomenu")
+ {
+ if (appPlayer->IsPlaying())
+ appPlayer->OnAction(CAction(ACTION_SHOW_VIDEOMENU));
+ }
+ else if (StringUtils::StartsWithNoCase(params[0], "partymode"))
+ {
+ std::string strXspPath;
+ //empty param=music, "music"=music, "video"=video, else xsp path
+ PartyModeContext context = PARTYMODECONTEXT_MUSIC;
+ if (params[0].size() > 9)
+ {
+ if (params[0].size() == 16 && StringUtils::EndsWithNoCase(params[0], "video)"))
+ context = PARTYMODECONTEXT_VIDEO;
+ else if (params[0].size() != 16 || !StringUtils::EndsWithNoCase(params[0], "music)"))
+ {
+ strXspPath = params[0].substr(10);
+ StringUtils::TrimRight(strXspPath, ")");
+ context = PARTYMODECONTEXT_UNKNOWN;
+ }
+ }
+ if (g_partyModeManager.IsEnabled())
+ g_partyModeManager.Disable();
+ else
+ g_partyModeManager.Enable(context, strXspPath);
+ }
+ else if (paramlow == "random" || paramlow == "randomoff" || paramlow == "randomon")
+ {
+ // get current playlist
+ PLAYLIST::Id playlistId = CServiceBroker::GetPlaylistPlayer().GetCurrentPlaylist();
+
+ // reverse the current setting
+ bool shuffled = CServiceBroker::GetPlaylistPlayer().IsShuffled(playlistId);
+ if ((shuffled && paramlow == "randomon") || (!shuffled && paramlow == "randomoff"))
+ return 0;
+
+ // check to see if we should notify the user
+ bool notify = (params.size() == 2 && StringUtils::EqualsNoCase(params[1], "notify"));
+ CServiceBroker::GetPlaylistPlayer().SetShuffle(playlistId, !shuffled, notify);
+
+ // save settings for now playing windows
+ switch (playlistId)
+ {
+ case PLAYLIST::TYPE_MUSIC:
+ CMediaSettings::GetInstance().SetMusicPlaylistShuffled(
+ CServiceBroker::GetPlaylistPlayer().IsShuffled(playlistId));
+ CServiceBroker::GetSettingsComponent()->GetSettings()->Save();
+ break;
+ case PLAYLIST::TYPE_VIDEO:
+ CMediaSettings::GetInstance().SetVideoPlaylistShuffled(
+ CServiceBroker::GetPlaylistPlayer().IsShuffled(playlistId));
+ CServiceBroker::GetSettingsComponent()->GetSettings()->Save();
+ default:
+ break;
+ }
+
+ // send message
+ CGUIMessage msg(GUI_MSG_PLAYLISTPLAYER_RANDOM, 0, 0, playlistId,
+ CServiceBroker::GetPlaylistPlayer().IsShuffled(playlistId));
+ CServiceBroker::GetGUI()->GetWindowManager().SendThreadMessage(msg);
+ }
+ else if (StringUtils::StartsWithNoCase(params[0], "repeat"))
+ {
+ // get current playlist
+ PLAYLIST::Id playlistId = CServiceBroker::GetPlaylistPlayer().GetCurrentPlaylist();
+ PLAYLIST::RepeatState prevRepeatState =
+ CServiceBroker::GetPlaylistPlayer().GetRepeat(playlistId);
+
+ std::string paramlow(params[0]);
+ StringUtils::ToLower(paramlow);
+
+ PLAYLIST::RepeatState repeatState;
+ if (paramlow == "repeatall")
+ repeatState = PLAYLIST::RepeatState::ALL;
+ else if (paramlow == "repeatone")
+ repeatState = PLAYLIST::RepeatState::ONE;
+ else if (paramlow == "repeatoff")
+ repeatState = PLAYLIST::RepeatState::NONE;
+ else if (prevRepeatState == PLAYLIST::RepeatState::NONE)
+ repeatState = PLAYLIST::RepeatState::ALL;
+ else if (prevRepeatState == PLAYLIST::RepeatState::ALL)
+ repeatState = PLAYLIST::RepeatState::ONE;
+ else
+ repeatState = PLAYLIST::RepeatState::NONE;
+
+ if (repeatState == prevRepeatState)
+ return 0;
+
+ // check to see if we should notify the user
+ bool notify = (params.size() == 2 && StringUtils::EqualsNoCase(params[1], "notify"));
+ CServiceBroker::GetPlaylistPlayer().SetRepeat(playlistId, repeatState, notify);
+
+ // save settings for now playing windows
+ switch (playlistId)
+ {
+ case PLAYLIST::TYPE_MUSIC:
+ CMediaSettings::GetInstance().SetMusicPlaylistRepeat(repeatState ==
+ PLAYLIST::RepeatState::ALL);
+ CServiceBroker::GetSettingsComponent()->GetSettings()->Save();
+ break;
+ case PLAYLIST::TYPE_VIDEO:
+ CMediaSettings::GetInstance().SetVideoPlaylistRepeat(repeatState ==
+ PLAYLIST::RepeatState::ALL);
+ CServiceBroker::GetSettingsComponent()->GetSettings()->Save();
+ }
+
+ // send messages so now playing window can get updated
+ CGUIMessage msg(GUI_MSG_PLAYLISTPLAYER_REPEAT, 0, 0, playlistId, static_cast<int>(repeatState));
+ CServiceBroker::GetGUI()->GetWindowManager().SendThreadMessage(msg);
+ }
+ else if (StringUtils::StartsWithNoCase(params[0], "resumelivetv"))
+ {
+ CFileItem& fileItem(g_application.CurrentFileItem());
+ std::shared_ptr<PVR::CPVRChannel> channel = fileItem.HasPVRRecordingInfoTag() ? fileItem.GetPVRRecordingInfoTag()->Channel() : std::shared_ptr<PVR::CPVRChannel>();
+
+ if (channel)
+ {
+ const std::shared_ptr<PVR::CPVRChannelGroupMember> groupMember =
+ CServiceBroker::GetPVRManager().Get<PVR::GUI::Channels>().GetChannelGroupMember(channel);
+ if (!groupMember)
+ {
+ CLog::Log(LOGERROR, "ResumeLiveTv could not obtain channel group member for channel: {}",
+ channel->ChannelName());
+ return false;
+ }
+
+ CFileItem playItem(groupMember);
+ if (!g_application.PlayMedia(
+ playItem, "", channel->IsRadio() ? PLAYLIST::TYPE_MUSIC : PLAYLIST::TYPE_VIDEO))
+ {
+ CLog::Log(LOGERROR, "ResumeLiveTv could not play channel: {}", channel->ChannelName());
+ return false;
+ }
+ }
+ }
+ else if (paramlow == "reset")
+ {
+ g_application.OnAction(CAction(ACTION_PLAYER_RESET));
+ }
+
+ return 0;
+}
+
+/*! \brief Play currently inserted DVD.
+ * \param params The parameters.
+ * \details params[0] = "restart" to restart from resume point (optional).
+ */
+static int PlayDVD(const std::vector<std::string>& params)
+{
+#ifdef HAS_DVD_DRIVE
+ bool restart = false;
+ if (!params.empty() && StringUtils::EqualsNoCase(params[0], "restart"))
+ restart = true;
+ MEDIA_DETECT::CAutorun::PlayDisc(CServiceBroker::GetMediaManager().GetDiscPath(), true, restart);
+#endif
+
+ return 0;
+}
+
+namespace
+{
+void GetItemsForPlayList(const std::shared_ptr<CFileItem>& item, CFileItemList& queuedItems)
+{
+ if (VIDEO_UTILS::IsItemPlayable(*item))
+ VIDEO_UTILS::GetItemsForPlayList(item, queuedItems);
+ else if (MUSIC_UTILS::IsItemPlayable(*item))
+ MUSIC_UTILS::GetItemsForPlayList(item, queuedItems);
+ else
+ CLog::LogF(LOGERROR, "Unable to get playlist items for {}", item->GetPath());
+}
+
+int PlayOrQueueMedia(const std::vector<std::string>& params, bool forcePlay)
+{
+ // restore to previous window if needed
+ if( CServiceBroker::GetGUI()->GetWindowManager().GetActiveWindow() == WINDOW_SLIDESHOW ||
+ CServiceBroker::GetGUI()->GetWindowManager().GetActiveWindow() == WINDOW_FULLSCREEN_VIDEO ||
+ CServiceBroker::GetGUI()->GetWindowManager().GetActiveWindow() == WINDOW_FULLSCREEN_GAME ||
+ CServiceBroker::GetGUI()->GetWindowManager().GetActiveWindow() == WINDOW_VISUALISATION )
+ CServiceBroker::GetGUI()->GetWindowManager().PreviousWindow();
+
+ // reset screensaver
+ auto& components = CServiceBroker::GetAppComponents();
+ const auto appPower = components.GetComponent<CApplicationPowerHandling>();
+ appPower->ResetScreenSaver();
+ appPower->WakeUpScreenSaverAndDPMS();
+
+ CFileItem item(params[0], URIUtils::HasSlashAtEnd(params[0], true));
+
+ // at this point the item instance has only the path and the folder flag set. We
+ // need some extended item properties to process resume successfully. Load them.
+ item.LoadDetails();
+
+ // ask if we need to check guisettings to resume
+ bool askToResume = true;
+ int playOffset = 0;
+ bool hasPlayOffset = false;
+ bool playNext = true;
+ for (unsigned int i = 1 ; i < params.size() ; i++)
+ {
+ if (StringUtils::EqualsNoCase(params[i], "isdir"))
+ item.m_bIsFolder = true;
+ else if (params[i] == "1") // set fullscreen or windowed
+ CMediaSettings::GetInstance().SetMediaStartWindowed(true);
+ else if (StringUtils::EqualsNoCase(params[i], "resume"))
+ {
+ // force the item to resume (if applicable)
+ if (VIDEO_UTILS::GetItemResumeInformation(item).isResumable)
+ item.SetStartOffset(STARTOFFSET_RESUME);
+ else
+ item.SetStartOffset(0);
+
+ askToResume = false;
+ }
+ else if (StringUtils::EqualsNoCase(params[i], "noresume"))
+ {
+ // force the item to start at the beginning
+ item.SetStartOffset(0);
+ askToResume = false;
+ }
+ else if (StringUtils::StartsWithNoCase(params[i], "playoffset="))
+ {
+ playOffset = atoi(params[i].substr(11).c_str()) - 1;
+ item.SetProperty("playlist_starting_track", playOffset);
+ hasPlayOffset = true;
+ }
+ else if (StringUtils::StartsWithNoCase(params[i], "playlist_type_hint="))
+ {
+ // Set the playlist type for the playlist file (e.g. STRM)
+ int playlistTypeHint = std::stoi(params[i].substr(19));
+ item.SetProperty("playlist_type_hint", playlistTypeHint);
+ }
+ else if (StringUtils::EqualsNoCase(params[i], "playnext"))
+ {
+ // If app player is currently playing, the queued media shall be played next.
+ playNext = true;
+ }
+ }
+
+ if (!item.m_bIsFolder && item.IsPlugin())
+ item.SetProperty("IsPlayable", true);
+
+ if (askToResume == true)
+ {
+ if (CGUIWindowVideoBase::ShowResumeMenu(item) == false)
+ return false;
+ item.SetProperty("check_resume", false);
+ }
+
+ if (item.m_bIsFolder || item.IsPlayList())
+ {
+ CFileItemList items;
+ GetItemsForPlayList(std::make_shared<CFileItem>(item), items);
+ if (!items.IsEmpty()) // fall through on non expandable playlist
+ {
+ bool containsMusic = false;
+ bool containsVideo = false;
+ for (const auto& i : items)
+ {
+ const bool isVideo = i->IsVideo();
+ containsMusic |= !isVideo;
+ containsVideo |= isVideo;
+
+ if (containsMusic && containsVideo)
+ break;
+ }
+
+ PLAYLIST::Id playlistId = containsVideo ? PLAYLIST::TYPE_VIDEO : PLAYLIST::TYPE_MUSIC;
+ // Mixed playlist item played by music player, mixed content folder has music removed
+ if (containsMusic && containsVideo)
+ {
+ if (item.IsPlayList())
+ playlistId = PLAYLIST::TYPE_MUSIC;
+ else
+ {
+ for (int i = items.Size() - 1; i >= 0; i--) //remove music entries
+ {
+ if (!items[i]->IsVideo())
+ items.Remove(i);
+ }
+ }
+ }
+
+ auto& playlistPlayer = CServiceBroker::GetPlaylistPlayer();
+
+ // Play vs. Queue (+Play)
+ if (forcePlay)
+ {
+ playlistPlayer.ClearPlaylist(playlistId);
+ playlistPlayer.Reset();
+ playlistPlayer.Add(playlistId, items);
+ playlistPlayer.SetCurrentPlaylist(playlistId);
+ playlistPlayer.Play(playOffset, "");
+ }
+ else
+ {
+ const int oldSize = playlistPlayer.GetPlaylist(playlistId).size();
+
+ const auto appPlayer = components.GetComponent<CApplicationPlayer>();
+ if (playNext)
+ {
+ if (appPlayer->IsPlaying())
+ playlistPlayer.Insert(playlistId, items, playlistPlayer.GetCurrentSong() + 1);
+ else
+ playlistPlayer.Add(playlistId, items);
+ }
+ else
+ {
+ playlistPlayer.Add(playlistId, items);
+ }
+
+ if (items.Size() && !appPlayer->IsPlaying())
+ {
+ playlistPlayer.SetCurrentPlaylist(playlistId);
+
+ if (containsMusic)
+ {
+ // video does not auto play on queue like music
+ playlistPlayer.Play(hasPlayOffset ? playOffset : oldSize, "");
+ }
+ }
+ }
+
+ return 0;
+ }
+ }
+
+ if (forcePlay)
+ {
+ if (item.HasVideoInfoTag() && item.GetStartOffset() == STARTOFFSET_RESUME)
+ {
+ const CBookmark bookmark = item.GetVideoInfoTag()->GetResumePoint();
+ if (bookmark.IsSet())
+ item.SetStartOffset(CUtil::ConvertSecsToMilliSecs(bookmark.timeInSeconds));
+ }
+
+ if ((item.IsAudio() || item.IsVideo()) && !item.IsSmartPlayList())
+ {
+ if (!item.HasProperty("playlist_type_hint"))
+ item.SetProperty("playlist_type_hint", PLAYLIST::TYPE_MUSIC);
+
+ CServiceBroker::GetPlaylistPlayer().Play(std::make_shared<CFileItem>(item), "");
+ }
+ else
+ {
+ g_application.PlayMedia(item, "", PLAYLIST::TYPE_NONE);
+ }
+ }
+
+ return 0;
+}
+
+/*! \brief Start playback of media.
+ * \param params The parameters.
+ * \details params[0] = URL to media to play (optional).
+ * params[1,...] = "isdir" if media is a directory (optional).
+ * params[1,...] = "1" to start playback in fullscreen (optional).
+ * params[1,...] = "resume" to force resuming (optional).
+ * params[1,...] = "noresume" to force not resuming (optional).
+ * params[1,...] = "playoffset=<offset>" to start playback from a given position in a playlist (optional).
+ * params[1,...] = "playlist_type_hint=<id>" to set the playlist type if a playlist file (e.g. STRM) is played (optional),
+ * for <id> value refer to PLAYLIST::TYPE_MUSIC / PLAYLIST::TYPE_VIDEO values, if not set will fallback to music playlist.
+ */
+int PlayMedia(const std::vector<std::string>& params)
+{
+ return PlayOrQueueMedia(params, true);
+}
+
+/*! \brief Queue media in the video or music playlist, according to type of media items. If both audio and video items are contained, queue to video
+ * playlist. Start playback at requested position if player is not playing.
+ * \param params The parameters.
+ * \details params[0] = URL of media to queue.
+ * params[1,...] = "isdir" if media is a directory (optional).
+ * params[1,...] = "1" to start playback in fullscreen (optional).
+ * params[1,...] = "resume" to force resuming (optional).
+ * params[1,...] = "noresume" to force not resuming (optional).
+ * params[1,...] = "playoffset=<offset>" to start playback from a given position in a playlist (optional).
+ * params[1,...] = "playlist_type_hint=<id>" to set the playlist type if a playlist file (e.g. STRM) is played (optional),
+ * for <id> value refer to PLAYLIST::TYPE_MUSIC / PLAYLIST::TYPE_VIDEO values, if not set will fallback to music playlist.
+ * params[1,...] = "playnext" if player is currently playing, to play the media right after the currently playing item. If player is not
+ * playing, append media to current playlist (optional).
+ */
+int QueueMedia(const std::vector<std::string>& params)
+{
+ return PlayOrQueueMedia(params, false);
+}
+
+} // unnamed namespace
+
+/*! \brief Start playback with a given playback core.
+ * \param params The parameters.
+ * \details params[0] = Name of playback core.
+ */
+static int PlayWith(const std::vector<std::string>& params)
+{
+ g_application.OnAction(CAction(ACTION_PLAYER_PLAY, params[0]));
+
+ return 0;
+}
+
+/*! \brief Seek in currently playing media.
+ * \param params The parameters.
+ * \details params[0] = Number of seconds to seek.
+ */
+static int Seek(const std::vector<std::string>& params)
+{
+ auto& components = CServiceBroker::GetAppComponents();
+ const auto appPlayer = components.GetComponent<CApplicationPlayer>();
+ if (appPlayer->IsPlaying())
+ appPlayer->GetSeekHandler().SeekSeconds(atoi(params[0].c_str()));
+
+ return 0;
+}
+
+static int SubtitleShiftUp(const std::vector<std::string>& params)
+{
+ CAction action{ACTION_SUBTITLE_VSHIFT_UP};
+ if (!params.empty() && params[0] == "save")
+ action.SetText("save");
+ CPlayerController::GetInstance().OnAction(action);
+ return 0;
+}
+
+static int SubtitleShiftDown(const std::vector<std::string>& params)
+{
+ CAction action{ACTION_SUBTITLE_VSHIFT_DOWN};
+ if (!params.empty() && params[0] == "save")
+ action.SetText("save");
+ CPlayerController::GetInstance().OnAction(action);
+ return 0;
+}
+
+// Note: For new Texts with comma add a "\" before!!! Is used for table text.
+//
+/// \page page_List_of_built_in_functions
+/// \section built_in_functions_12 Player built-in's
+///
+/// -----------------------------------------------------------------------------
+///
+/// \table_start
+/// \table_h2_l{
+/// Function,
+/// Description }
+/// \table_row2_l{
+/// <b>`PlaysDisc(parm)`</b>\n
+/// <b>`PlayDVD(param)`</b>(deprecated)
+/// ,
+/// Plays the inserted disc\, like CD\, DVD or Blu-ray\, in the disc drive.
+/// @param[in] param "restart" to restart from resume point (optional)
+/// }
+/// \table_row2_l{
+/// <b>`PlayerControl(control[\,param])`</b>
+/// ,
+/// Allows control of music and videos. <br>
+/// <br>
+/// | Control | Video playback behaviour | Audio playback behaviour | Added in |
+/// |:------------------------|:---------------------------------------|:----------------------------|:------------|
+/// | Play | Play/Pause | Play/Pause | |
+/// | Stop | Stop | Stop | |
+/// | Forward | Fast Forward | Fast Forward | |
+/// | Rewind | Rewind | Rewind | |
+/// | Next | Next chapter or movie in playlists | Next track | |
+/// | Previous | Previous chapter or movie in playlists | Previous track | |
+/// | TempoUp | Increases playback speed | none | Kodi v18 |
+/// | TempoDown | Decreases playback speed | none | Kodi v18 |
+/// | Tempo(n) | Sets playback speed to given value | none | Kodi v19 |
+/// | BigSkipForward | Big Skip Forward | Big Skip Forward | Kodi v15 |
+/// | BigSkipBackward | Big Skip Backward | Big Skip Backward | Kodi v15 |
+/// | SmallSkipForward | Small Skip Forward | Small Skip Forward | Kodi v15 |
+/// | SmallSkipBackward | Small Skip Backward | Small Skip Backward | Kodi v15 |
+/// | SeekPercentage(n) | Seeks to given percentage | Seeks to given percentage | |
+/// | Random * | Toggle Random Playback | Toggle Random Playback | |
+/// | RandomOn | Sets 'Random' to 'on' | Sets 'Random' to 'on' | |
+/// | RandomOff | Sets 'Random' to 'off' | Sets 'Random' to 'off' | |
+/// | Repeat * | Cycles through repeat modes | Cycles through repeat modes | |
+/// | RepeatOne | Repeats a single video | Repeats a single track | |
+/// | RepeatAll | Repeat all videos in a list | Repeats all tracks in a list| |
+/// | RepeatOff | Sets 'Repeat' to 'off' | Sets 'Repeat' to 'off' | |
+/// | Partymode(music) ** | none | Toggles music partymode | |
+/// | Partymode(video) ** | Toggles video partymode | none | |
+/// | Partymode(path to .xsp) | Partymode for *.xsp-file | Partymode for *.xsp-file | |
+/// | ShowVideoMenu | Shows the DVD/BR menu if available | none | |
+/// | FrameAdvance(n) *** | Advance video by _n_ frames | none | Kodi v18 |
+/// | SubtitleShiftUp(save) | Shift up the subtitle position, add "save" to save the change permanently | none | Kodi v20 |
+/// | SubtitleShiftDown(save) | Shift down the subtitle position, add "save" to save the change permanently | none | Kodi v20 |
+/// <br>
+/// '*' = For these controls\, the PlayerControl built-in function can make use of the 'notify'-parameter. For example: PlayerControl(random\, notify)
+/// <br>
+/// '**' = If no argument is given for 'partymode'\, the control will default to music.
+/// <br>
+/// '***' = This only works if the player is paused.
+/// <br>
+/// @param[in] control Control to execute.
+/// @param[in] param "notify" to notify user (optional\, certain controls).
+///
+/// @note 'TempoUp' or 'TempoDown' only works if "Sync playback to display" is enabled.
+/// @note 'Next' will behave differently while using video playlists. In those\, chapters will be ignored and the next movie will be played.
+/// }
+/// \table_row2_l{
+/// <b>`Playlist.Clear`</b>
+/// ,
+/// Clear the current playlist
+/// @param[in] (ignored)
+/// }
+/// \table_row2_l{
+/// <b>`Playlist.PlayOffset(positionType[\,position])`</b>
+/// ,
+/// Start playing from a particular offset in the playlist
+/// @param[in] positionType Position in playlist or playlist type.
+/// @param[in] position Position in playlist if params[0] is playlist type (optional).
+/// }
+/// \table_row2_l{
+/// <b>`PlayMedia(media[\,isdir][\,1]\,[playoffset=xx])`</b>
+/// ,
+/// Plays the media. This can be a playlist\, music\, or video file\, directory\,
+/// plugin or an Url. The optional parameter "\,isdir" can be used for playing
+/// a directory. "\,1" will start the media without switching to fullscreen.
+/// If media is a playlist\, you can use playoffset=xx where xx is
+/// the position to start playback from.
+/// @param[in] media URL to media to play (optional).
+/// @param[in] isdir Set "isdir" if media is a directory (optional).
+/// @param[in] windowed Set "1" to start playback without switching to fullscreen (optional).
+/// @param[in] resume Set "resume" to force resuming (optional).
+/// @param[in] noresume Set "noresume" to force not resuming (optional).
+/// @param[in] playeroffset Set "playoffset=<offset>" to start playback from a given position in a playlist (optional).
+/// }
+/// \table_row2_l{
+/// <b>`PlayWith(core)`</b>
+/// ,
+/// Play the selected item with the specified player core.
+/// @param[in] core Name of playback core.
+/// }
+/// \table_row2_l{
+/// <b>`Seek(seconds)`</b>
+/// ,
+/// Seeks to the specified relative amount of seconds within the current
+/// playing media. A negative value will seek backward and a positive value forward.
+/// @param[in] seconds Number of seconds to seek.
+/// }
+/// \table_row2_l{
+/// <b>`QueueMedia(media[\,isdir][\,1][\,playnext]\,[playoffset=xx])`</b>
+/// \anchor Builtin_QueueMedia,
+/// Queues the given media. This can be a playlist\, music\, or video file\, directory\,
+/// plugin or an Url. The optional parameter "\,isdir" can be used for playing
+/// a directory. "\,1" will start the media without switching to fullscreen.
+/// If media is a playlist\, you can use playoffset=xx where xx is
+/// the position to start playback from.
+/// @param[in] media URL of media to queue.
+/// @param[in] isdir Set "isdir" if media is a directory (optional).
+/// @param[in] 1 Set "1" to start playback without switching to fullscreen (optional).
+/// @param[in] resume Set "resume" to force resuming (optional).
+/// @param[in] noresume Set "noresume" to force not resuming (optional).
+/// @param[in] playeroffset Set "playoffset=<offset>" to start playback from a given position in a playlist (optional).
+/// @param[in] playnext Set "playnext" to play the media right after the currently playing item, if player is currently
+/// playing. If player is not playing, append media to current playlist (optional).
+/// <p><hr>
+/// @skinning_v20 **[New builtin]** \link Builtin_QueueMedia `QueueMedia(media[\,isdir][\,1][\,playnext]\,[playoffset=xx])`\endlink
+/// <p>
+/// }
+/// \table_end
+///
+
+// clang-format off
+CBuiltins::CommandMap CPlayerBuiltins::GetOperations() const
+{
+ return {
+ {"playdisc", {"Plays the inserted disc, like CD, DVD or Blu-ray, in the disc drive.", 0, PlayDVD}},
+ {"playdvd", {"Plays the inserted disc, like CD, DVD or Blu-ray, in the disc drive.", 0, PlayDVD}},
+ {"playlist.clear", {"Clear the current playlist", 0, ClearPlaylist}},
+ {"playlist.playoffset", {"Start playing from a particular offset in the playlist", 1, PlayOffset}},
+ {"playercontrol", {"Control the music or video player", 1, PlayerControl}},
+ {"playmedia", {"Play the specified media file (or playlist)", 1, PlayMedia}},
+ {"queuemedia", {"Queue the specified media in video or music playlist", 1, QueueMedia}},
+ {"playwith", {"Play the selected item with the specified core", 1, PlayWith}},
+ {"seek", {"Performs a seek in seconds on the current playing media file", 1, Seek}},
+ {"subtitleshiftup", {"Shift up the subtitle position", 0, SubtitleShiftUp}},
+ {"subtitleshiftdown", {"Shift down the subtitle position", 0, SubtitleShiftDown}},
+ };
+}
+// clang-format on
diff --git a/xbmc/interfaces/builtins/PlayerBuiltins.h b/xbmc/interfaces/builtins/PlayerBuiltins.h
new file mode 100644
index 0000000..efa9474
--- /dev/null
+++ b/xbmc/interfaces/builtins/PlayerBuiltins.h
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2005-2018 Team Kodi
+ * This file is part of Kodi - https://kodi.tv
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ * See LICENSES/README.md for more information.
+ */
+
+#pragma once
+
+#include "Builtins.h"
+
+//! \brief Class providing player related built-in commands.
+class CPlayerBuiltins
+{
+public:
+ //! \brief Returns the map of operations.
+ CBuiltins::CommandMap GetOperations() const;
+};
diff --git a/xbmc/interfaces/builtins/ProfileBuiltins.cpp b/xbmc/interfaces/builtins/ProfileBuiltins.cpp
new file mode 100644
index 0000000..448f821
--- /dev/null
+++ b/xbmc/interfaces/builtins/ProfileBuiltins.cpp
@@ -0,0 +1,133 @@
+/*
+ * Copyright (C) 2005-2018 Team Kodi
+ * This file is part of Kodi - https://kodi.tv
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ * See LICENSES/README.md for more information.
+ */
+
+#include "ProfileBuiltins.h"
+
+#include "GUIPassword.h"
+#include "GUIUserMessages.h"
+#include "ServiceBroker.h"
+#include "Util.h"
+#include "dialogs/GUIDialogKaiToast.h"
+#include "favourites/FavouritesService.h"
+#include "guilib/GUIComponent.h"
+#include "guilib/GUIWindowManager.h"
+#include "guilib/LocalizeStrings.h"
+#include "messaging/ApplicationMessenger.h"
+#include "profiles/ProfileManager.h"
+#include "settings/SettingsComponent.h"
+#include "utils/StringUtils.h"
+
+/*! \brief Load a profile.
+ * \param params The parameters.
+ * \details params[0] = The profile name.
+ * params[1] = "prompt" to allow unlocking dialogs (optional)
+ */
+static int LoadProfile(const std::vector<std::string>& params)
+{
+ const std::shared_ptr<CProfileManager> profileManager = CServiceBroker::GetSettingsComponent()->GetProfileManager();
+
+ int index = profileManager->GetProfileIndex(params[0]);
+ bool prompt = (params.size() == 2 && StringUtils::EqualsNoCase(params[1], "prompt"));
+ bool bCanceled;
+ if (index >= 0
+ && (profileManager->GetMasterProfile().getLockMode() == LOCK_MODE_EVERYONE
+ || g_passwordManager.IsProfileLockUnlocked(index,bCanceled,prompt)))
+ {
+ CServiceBroker::GetAppMessenger()->PostMsg(TMSG_LOADPROFILE, index);
+ }
+
+ return 0;
+}
+
+/*! \brief Log off currently signed in profile.
+ * \param params (ignored)
+ */
+static int LogOff(const std::vector<std::string>& params)
+{
+ const std::shared_ptr<CProfileManager> profileManager = CServiceBroker::GetSettingsComponent()->GetProfileManager();
+ profileManager->LogOff();
+
+ return 0;
+}
+
+/*! \brief Toggle master mode.
+ * \param params (ignored)
+ */
+static int MasterMode(const std::vector<std::string>& params)
+{
+ if (g_passwordManager.bMasterUser)
+ {
+ g_passwordManager.bMasterUser = false;
+ g_passwordManager.LockSources(true);
+
+ // master mode turned OFF => refresh favourites due to possible visibility changes
+ CServiceBroker::GetFavouritesService().RefreshFavourites();
+
+ CGUIDialogKaiToast::QueueNotification(CGUIDialogKaiToast::Warning, g_localizeStrings.Get(20052),g_localizeStrings.Get(20053));
+ }
+ else if (g_passwordManager.IsMasterLockUnlocked(true)) // prompt user for code
+ {
+ g_passwordManager.LockSources(false);
+ g_passwordManager.bMasterUser = true;
+
+ // master mode turned ON => refresh favourites due to possible visibility changes
+ CServiceBroker::GetFavouritesService().RefreshFavourites();
+
+ CGUIDialogKaiToast::QueueNotification(CGUIDialogKaiToast::Warning, g_localizeStrings.Get(20052),g_localizeStrings.Get(20054));
+ }
+
+ CUtil::DeleteVideoDatabaseDirectoryCache();
+ CGUIMessage msg(GUI_MSG_NOTIFY_ALL, 0, 0, GUI_MSG_UPDATE);
+ CServiceBroker::GetGUI()->GetWindowManager().SendMessage(msg);
+
+ return 0;
+}
+
+
+// Note: For new Texts with comma add a "\" before!!! Is used for table text.
+//
+/// \page page_List_of_built_in_functions
+/// \section built_in_functions_13 Profile built-in's
+///
+/// -----------------------------------------------------------------------------
+///
+/// \table_start
+/// \table_h2_l{
+/// Function,
+/// Description }
+/// \table_row2_l{
+/// <b>`LoadProfile(profilename\,[prompt])`</b>
+/// ,
+/// Load the specified profile. If prompt is not specified\, and a password
+/// would be required for the requested profile\, this command will silently
+/// fail. If prompt is specified and a password is required\, a password
+/// dialog will be shown.
+/// @param[in] profilename The profile name.
+/// @param[in] prompt Add "prompt" to allow unlocking dialogs (optional)
+/// }
+/// \table_row2_l{
+/// <b>`Mastermode`</b>
+/// ,
+/// Runs Kodi in master mode
+/// }
+/// \table_row2_l{
+/// <b>`System.LogOff`</b>
+/// ,
+/// Log off current user.
+/// }
+/// \table_end
+///
+
+CBuiltins::CommandMap CProfileBuiltins::GetOperations() const
+{
+ return {
+ {"loadprofile", {"Load the specified profile (note; if locks are active it won't work)", 1, LoadProfile}},
+ {"mastermode", {"Control master mode", 0, MasterMode}},
+ {"system.logoff", {"Log off current user", 0, LogOff}}
+ };
+}
diff --git a/xbmc/interfaces/builtins/ProfileBuiltins.h b/xbmc/interfaces/builtins/ProfileBuiltins.h
new file mode 100644
index 0000000..e30c1ee
--- /dev/null
+++ b/xbmc/interfaces/builtins/ProfileBuiltins.h
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2005-2018 Team Kodi
+ * This file is part of Kodi - https://kodi.tv
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ * See LICENSES/README.md for more information.
+ */
+
+#pragma once
+
+#include "Builtins.h"
+
+//! \brief Class providing profile related built-in commands.
+class CProfileBuiltins
+{
+public:
+ //! \brief Returns the map of operations.
+ CBuiltins::CommandMap GetOperations() const;
+};
diff --git a/xbmc/interfaces/builtins/SkinBuiltins.cpp b/xbmc/interfaces/builtins/SkinBuiltins.cpp
new file mode 100644
index 0000000..5b2b4c2
--- /dev/null
+++ b/xbmc/interfaces/builtins/SkinBuiltins.cpp
@@ -0,0 +1,687 @@
+/*
+ * Copyright (C) 2005-2018 Team Kodi
+ * This file is part of Kodi - https://kodi.tv
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ * See LICENSES/README.md for more information.
+ */
+
+#include "SkinBuiltins.h"
+
+#include "MediaSource.h"
+#include "ServiceBroker.h"
+#include "URL.h"
+#include "Util.h"
+#include "addons/addoninfo/AddonInfo.h"
+#include "addons/addoninfo/AddonType.h"
+#include "addons/gui/GUIWindowAddonBrowser.h"
+#include "application/ApplicationComponents.h"
+#include "application/ApplicationSkinHandling.h"
+#include "dialogs/GUIDialogColorPicker.h"
+#include "dialogs/GUIDialogFileBrowser.h"
+#include "dialogs/GUIDialogNumeric.h"
+#include "dialogs/GUIDialogSelect.h"
+#include "guilib/GUIComponent.h"
+#include "guilib/GUIKeyboardFactory.h"
+#include "guilib/GUIWindowManager.h"
+#include "guilib/LocalizeStrings.h"
+#include "settings/Settings.h"
+#include "settings/SettingsComponent.h"
+#include "settings/SkinSettings.h"
+#include "storage/MediaManager.h"
+#include "utils/StringUtils.h"
+#include "utils/URIUtils.h"
+
+using namespace ADDON;
+
+/*! \brief Reload current skin.
+ * \param params The parameters.
+ * \details params[0] = "confirm" to show a confirmation dialog (optional).
+ */
+static int ReloadSkin(const std::vector<std::string>& params)
+{
+ // Reload the skin
+ auto& components = CServiceBroker::GetAppComponents();
+ const auto appSkin = components.GetComponent<CApplicationSkinHandling>();
+ appSkin->ReloadSkin(!params.empty() && StringUtils::EqualsNoCase(params[0], "confirm"));
+
+ return 0;
+}
+
+/*! \brief Unload current skin.
+ * \param params (ignored)
+ */
+static int UnloadSkin(const std::vector<std::string>& params)
+{
+ auto& components = CServiceBroker::GetAppComponents();
+ const auto appSkin = components.GetComponent<CApplicationSkinHandling>();
+ appSkin->UnloadSkin();
+
+ return 0;
+}
+
+/*! \brief Toggle a skin bool setting.
+ * \param params The parameters.
+ * \details params[0] = Skin setting to toggle.
+ */
+static int ToggleSetting(const std::vector<std::string>& params)
+{
+ int setting = CSkinSettings::GetInstance().TranslateBool(params[0]);
+ CSkinSettings::GetInstance().SetBool(setting, !CSkinSettings::GetInstance().GetBool(setting));
+ CServiceBroker::GetSettingsComponent()->GetSettings()->Save();
+
+ return 0;
+}
+
+/*! \brief Set an add-on type skin setting.
+ * \param params The parameters.
+ * \details params[0] = Skin setting to store result in.
+ * params[1,...] = Add-on types to allow selecting.
+ */
+static int SetAddon(const std::vector<std::string>& params)
+{
+ int string = CSkinSettings::GetInstance().TranslateString(params[0]);
+ std::vector<ADDON::AddonType> types;
+ for (unsigned int i = 1 ; i < params.size() ; i++)
+ {
+ ADDON::AddonType type = CAddonInfo::TranslateType(params[i]);
+ if (type != AddonType::UNKNOWN)
+ types.push_back(type);
+ }
+ std::string result;
+ if (!types.empty() && CGUIWindowAddonBrowser::SelectAddonID(types, result, true) == 1)
+ {
+ CSkinSettings::GetInstance().SetString(string, result);
+ CServiceBroker::GetSettingsComponent()->GetSettings()->Save();
+ }
+
+ return 0;
+}
+
+/*! \brief Select and set a skin bool setting.
+ * \param params The parameters.
+ * \details params[0] = Names of skin settings.
+ */
+static int SelectBool(const std::vector<std::string>& params)
+{
+ std::vector<std::pair<std::string, std::string>> settings;
+
+ CGUIDialogSelect* pDlgSelect = CServiceBroker::GetGUI()->GetWindowManager().GetWindow<CGUIDialogSelect>(WINDOW_DIALOG_SELECT);
+ pDlgSelect->Reset();
+ pDlgSelect->SetHeading(CVariant{g_localizeStrings.Get(atoi(params[0].c_str()))});
+
+ for (unsigned int i = 1 ; i < params.size() ; i++)
+ {
+ if (params[i].find('|') != std::string::npos)
+ {
+ std::vector<std::string> values = StringUtils::Split(params[i], '|');
+ std::string label = g_localizeStrings.Get(atoi(values[0].c_str()));
+ settings.emplace_back(label, values[1].c_str());
+ pDlgSelect->Add(label);
+ }
+ }
+
+ pDlgSelect->Open();
+
+ if(pDlgSelect->IsConfirmed())
+ {
+ unsigned int iItem = pDlgSelect->GetSelectedItem();
+
+ for (unsigned int i = 0 ; i < settings.size() ; i++)
+ {
+ std::string item = settings[i].second;
+ int setting = CSkinSettings::GetInstance().TranslateBool(item);
+ if (i == iItem)
+ CSkinSettings::GetInstance().SetBool(setting, true);
+ else
+ CSkinSettings::GetInstance().SetBool(setting, false);
+ }
+ CServiceBroker::GetSettingsComponent()->GetSettings()->Save();
+ }
+
+ return 0;
+}
+
+/*! \brief Set a skin bool setting.
+ * \param params The parameters.
+ * \details params[0] = Name of skin setting.
+ * params[1] = Value to set ("false", or "true") (optional).
+ */
+static int SetBool(const std::vector<std::string>& params)
+{
+ if (params.size() > 1)
+ {
+ int string = CSkinSettings::GetInstance().TranslateBool(params[0]);
+ CSkinSettings::GetInstance().SetBool(string, StringUtils::EqualsNoCase(params[1], "true"));
+ CServiceBroker::GetSettingsComponent()->GetSettings()->Save();
+ return 0;
+ }
+ // default is to set it to true
+ int setting = CSkinSettings::GetInstance().TranslateBool(params[0]);
+ CSkinSettings::GetInstance().SetBool(setting, true);
+ CServiceBroker::GetSettingsComponent()->GetSettings()->Save();
+
+ return 0;
+}
+
+/*! \brief Set a numeric skin setting.
+ * \param params The parameters.
+ * \details params[0] = Name of skin setting.
+ */
+static int SetNumeric(const std::vector<std::string>& params)
+{
+ int string = CSkinSettings::GetInstance().TranslateString(params[0]);
+ std::string value = CSkinSettings::GetInstance().GetString(string);
+ if (CGUIDialogNumeric::ShowAndGetNumber(value, g_localizeStrings.Get(611)))
+ CSkinSettings::GetInstance().SetString(string, value);
+
+ return 0;
+}
+
+/*! \brief Set a path skin setting.
+ * \param params The parameters.
+ * \details params[0] = Name of skin setting.
+ * params[1] = Extra URL to allow selection from (optional).
+ */
+static int SetPath(const std::vector<std::string>& params)
+{
+ int string = CSkinSettings::GetInstance().TranslateString(params[0]);
+ std::string value = CSkinSettings::GetInstance().GetString(string);
+ VECSOURCES localShares;
+ CServiceBroker::GetMediaManager().GetLocalDrives(localShares);
+ CServiceBroker::GetMediaManager().GetNetworkLocations(localShares);
+ if (params.size() > 1)
+ {
+ value = params[1];
+ URIUtils::AddSlashAtEnd(value);
+ bool bIsSource;
+ if (CUtil::GetMatchingSource(value,localShares,bIsSource) < 0) // path is outside shares - add it as a separate one
+ {
+ CMediaSource share;
+ share.strName = g_localizeStrings.Get(13278);
+ share.strPath = value;
+ localShares.push_back(share);
+ }
+ }
+
+ if (CGUIDialogFileBrowser::ShowAndGetDirectory(localShares, g_localizeStrings.Get(657), value))
+ CSkinSettings::GetInstance().SetString(string, value);
+
+ CServiceBroker::GetSettingsComponent()->GetSettings()->Save();
+
+ return 0;
+}
+
+/*! \brief Set a skin file setting.
+ * \param params The parameters.
+ * \details params[0] = Name of skin setting.
+ * params[1] = File mask or add-on type (optional).
+ * params[2] = Extra URL to allow selection from or
+ * content type if mask is an addon-on type (optional).
+ */
+static int SetFile(const std::vector<std::string>& params)
+{
+ int string = CSkinSettings::GetInstance().TranslateString(params[0]);
+ std::string value = CSkinSettings::GetInstance().GetString(string);
+ VECSOURCES localShares;
+ CServiceBroker::GetMediaManager().GetLocalDrives(localShares);
+
+ // Note. can only browse one addon type from here
+ // if browsing for addons, required param[1] is addontype string, with optional param[2]
+ // as contenttype string see IAddon.h & ADDON::TranslateXX
+ std::string strMask = (params.size() > 1) ? params[1] : "";
+ StringUtils::ToLower(strMask);
+ ADDON::AddonType type;
+ if ((type = CAddonInfo::TranslateType(strMask)) != AddonType::UNKNOWN)
+ {
+ CURL url;
+ url.SetProtocol("addons");
+ url.SetHostName("enabled");
+ url.SetFileName(strMask+"/");
+ localShares.clear();
+ std::string content = (params.size() > 2) ? params[2] : "";
+ StringUtils::ToLower(content);
+ url.SetPassword(content);
+ std::string strMask;
+ if (type == AddonType::SCRIPT)
+ strMask = ".py";
+ std::string replace;
+ if (CGUIDialogFileBrowser::ShowAndGetFile(url.Get(), strMask, CAddonInfo::TranslateType(type, true), replace, true, true, true))
+ {
+ if (StringUtils::StartsWithNoCase(replace, "addons://"))
+ CSkinSettings::GetInstance().SetString(string, URIUtils::GetFileName(replace));
+ else
+ CSkinSettings::GetInstance().SetString(string, replace);
+ }
+ }
+ else
+ {
+ if (params.size() > 2)
+ {
+ value = params[2];
+ URIUtils::AddSlashAtEnd(value);
+ bool bIsSource;
+ if (CUtil::GetMatchingSource(value,localShares,bIsSource) < 0) // path is outside shares - add it as a separate one
+ {
+ CMediaSource share;
+ share.strName = g_localizeStrings.Get(13278);
+ share.strPath = value;
+ localShares.push_back(share);
+ }
+ }
+ if (CGUIDialogFileBrowser::ShowAndGetFile(localShares, strMask, g_localizeStrings.Get(1033), value))
+ CSkinSettings::GetInstance().SetString(string, value);
+ }
+
+ return 0;
+}
+
+/*! \brief Set a skin image setting.
+ * \param params The parameters.
+ * \details params[0] = Name of skin setting.
+ * params[1] = Extra URL to allow selection from (optional).
+ */
+static int SetImage(const std::vector<std::string>& params)
+{
+ int string = CSkinSettings::GetInstance().TranslateString(params[0]);
+ std::string value = CSkinSettings::GetInstance().GetString(string);
+ VECSOURCES localShares;
+ CServiceBroker::GetMediaManager().GetLocalDrives(localShares);
+ if (params.size() > 1)
+ {
+ value = params[1];
+ URIUtils::AddSlashAtEnd(value);
+ bool bIsSource;
+ if (CUtil::GetMatchingSource(value,localShares,bIsSource) < 0) // path is outside shares - add it as a separate one
+ {
+ CMediaSource share;
+ share.strName = g_localizeStrings.Get(13278);
+ share.strPath = value;
+ localShares.push_back(share);
+ }
+ }
+ if (CGUIDialogFileBrowser::ShowAndGetImage(localShares, g_localizeStrings.Get(1030), value))
+ CSkinSettings::GetInstance().SetString(string, value);
+
+ return 0;
+}
+
+/*! \brief Set a skin color setting.
+ * \param params The parameters.
+ * \details params[0] = Name of skin setting.
+ * params[1] = Dialog header text.
+ * params[2] = Hex value of the preselected color (optional).
+ * params[3] = XML file containing color definitions (optional).
+ */
+static int SetColor(const std::vector<std::string>& params)
+{
+ int string = CSkinSettings::GetInstance().TranslateString(params[0]);
+ std::string value = CSkinSettings::GetInstance().GetString(string);
+
+ if (value.empty() && params.size() > 2)
+ {
+ value = params[2];
+ }
+
+ CGUIDialogColorPicker* pDlgColorPicker =
+ CServiceBroker::GetGUI()->GetWindowManager().GetWindow<CGUIDialogColorPicker>(
+ WINDOW_DIALOG_COLOR_PICKER);
+ pDlgColorPicker->Reset();
+ pDlgColorPicker->SetHeading(CVariant{g_localizeStrings.Get(atoi(params[1].c_str()))});
+
+ if (params.size() > 3)
+ {
+ pDlgColorPicker->LoadColors(params[3]);
+ }
+ else
+ {
+ pDlgColorPicker->LoadColors();
+ }
+
+ pDlgColorPicker->SetSelectedColor(value);
+
+ pDlgColorPicker->Open();
+
+ if (pDlgColorPicker->IsConfirmed())
+ {
+ value = pDlgColorPicker->GetSelectedColor();
+ CSkinSettings::GetInstance().SetString(string, value);
+ }
+
+ return 0;
+}
+
+/*! \brief Set a string skin setting.
+ * \param params The parameters.
+ * \details params[0] = Name of skin setting.
+ * params[1] = Value of skin setting (optional).
+ */
+static int SetString(const std::vector<std::string>& params)
+{
+ // break the parameter up if necessary
+ int string = 0;
+ if (params.size() > 1)
+ {
+ string = CSkinSettings::GetInstance().TranslateString(params[0]);
+ CSkinSettings::GetInstance().SetString(string, params[1]);
+ CServiceBroker::GetSettingsComponent()->GetSettings()->Save();
+ return 0;
+ }
+ else
+ string = CSkinSettings::GetInstance().TranslateString(params[0]);
+
+ std::string value = CSkinSettings::GetInstance().GetString(string);
+ if (CGUIKeyboardFactory::ShowAndGetInput(value, CVariant{g_localizeStrings.Get(1029)}, true))
+ CSkinSettings::GetInstance().SetString(string, value);
+
+ return 0;
+}
+
+/*! \brief Select skin theme.
+ * \param params The parameters.
+ * \details params[0] = 0 or 1 to increase theme, -1 to decrease.
+ */
+static int SetTheme(const std::vector<std::string>& params)
+{
+ // enumerate themes
+ std::vector<std::string> vecTheme;
+ CUtil::GetSkinThemes(vecTheme);
+
+ int iTheme = -1;
+
+ // find current theme
+ const std::shared_ptr<CSettings> settings = CServiceBroker::GetSettingsComponent()->GetSettings();
+ const std::string strTheme = settings->GetString(CSettings::SETTING_LOOKANDFEEL_SKINTHEME);
+ if (!StringUtils::EqualsNoCase(strTheme, "SKINDEFAULT"))
+ {
+ for (size_t i=0;i<vecTheme.size();++i)
+ {
+ std::string strTmpTheme(strTheme);
+ URIUtils::RemoveExtension(strTmpTheme);
+ if (StringUtils::EqualsNoCase(vecTheme[i], strTmpTheme))
+ {
+ iTheme=i;
+ break;
+ }
+ }
+ }
+
+ int iParam = atoi(params[0].c_str());
+ if (iParam == 0 || iParam == 1)
+ iTheme++;
+ else if (iParam == -1)
+ iTheme--;
+ if (iTheme > (int)vecTheme.size()-1)
+ iTheme = -1;
+ if (iTheme < -1)
+ iTheme = vecTheme.size()-1;
+
+ std::string strSkinTheme = "SKINDEFAULT";
+ if (iTheme != -1 && iTheme < (int)vecTheme.size())
+ strSkinTheme = vecTheme[iTheme];
+
+ settings->SetString(CSettings::SETTING_LOOKANDFEEL_SKINTHEME, strSkinTheme);
+ // also set the default color theme
+ std::string colorTheme(URIUtils::ReplaceExtension(strSkinTheme, ".xml"));
+ if (StringUtils::EqualsNoCase(colorTheme, "Textures.xml"))
+ colorTheme = "defaults.xml";
+ settings->SetString(CSettings::SETTING_LOOKANDFEEL_SKINCOLORS, colorTheme);
+ auto& components = CServiceBroker::GetAppComponents();
+ const auto appSkin = components.GetComponent<CApplicationSkinHandling>();
+ appSkin->ReloadSkin();
+
+ return 0;
+}
+
+/*! \brief Reset a skin setting.
+ * \param params The parameters.
+ * \details params[0] = Name of setting to reset.
+ */
+static int SkinReset(const std::vector<std::string>& params)
+{
+ CSkinSettings::GetInstance().Reset(params[0]);
+ CServiceBroker::GetSettingsComponent()->GetSettings()->Save();
+
+ return 0;
+}
+
+/*! \brief Reset all skin settings.
+ * \param params (ignored)
+ */
+static int SkinResetAll(const std::vector<std::string>& params)
+{
+ CSkinSettings::GetInstance().Reset();
+ CServiceBroker::GetSettingsComponent()->GetSettings()->Save();
+
+ return 0;
+}
+
+/*! \brief Toggle skin debug.
+ * \param params (ignored)
+ */
+static int SkinDebug(const std::vector<std::string>& params)
+{
+ g_SkinInfo->ToggleDebug();
+
+ return 0;
+}
+
+/*! \brief Starts a given skin timer
+ * \param params The parameters.
+ * \details params[0] = Name of the timer.
+ * \return -1 in case of error, 0 in case of success
+ */
+static int SkinTimerStart(const std::vector<std::string>& params)
+{
+ if (params.empty())
+ {
+ return -1;
+ }
+
+ g_SkinInfo->TimerStart(params[0]);
+ return 0;
+}
+
+/*! \brief Stops a given skin timer
+ * \param params The parameters.
+ * \details params[0] = Name of the timer.
+ * \return -1 in case of error, 0 in case of success
+ */
+static int SkinTimerStop(const std::vector<std::string>& params)
+{
+ if (params.empty())
+ {
+ return -1;
+ }
+
+ g_SkinInfo->TimerStop(params[0]);
+ return 0;
+}
+
+// Note: For new Texts with comma add a "\" before!!! Is used for table text.
+//
+/// \page page_List_of_built_in_functions
+/// \section built_in_functions_14 Skin built-in's
+///
+/// -----------------------------------------------------------------------------
+///
+/// \table_start
+/// \table_h2_l{
+/// Function,
+/// Description }
+/// \table_row2_l{
+/// <b>`ReloadSkin(reload)`</b>
+/// ,
+/// Reloads the current skin – useful for Skinners to use after they upload
+/// modified skin files (saves power cycling)
+/// @param[in] reload <b>"confirm"</b> to show a confirmation dialog (optional).
+/// }
+/// \table_row2_l{
+/// <b>`UnloadSkin()`</b>
+/// ,
+/// Unloads the current skin
+/// }
+/// \table_row2_l{
+/// <b>`Skin.Reset(setting)`</b>
+/// ,
+/// Resets the skin `setting`. If `setting` is a bool setting (i.e. set via
+/// `SetBool` or `ToggleSetting`) then the setting is reset to false. If
+/// `setting` is a string (Set via <c>SetString</c>\, <c>SetImage</c> or
+/// <c>SetPath</c>) then it is set to empty.
+/// @param[in] setting Name of setting to reset.
+/// }
+/// \table_row2_l{
+/// <b>`Skin.ResetSettings()`</b>
+/// ,
+/// Resets all the above skin settings to their defaults (toggles all set to
+/// false\, strings all set to empty.)
+/// }
+/// \table_row2_l{
+/// <b>`Skin.SetAddon(string\,type)`</b>
+/// ,
+/// Pops up a select dialog and allows the user to select an add-on of the
+/// given type to be used elsewhere in the skin via the info tag
+/// `Skin.String(string)`. The most common types are xbmc.addon.video\,
+/// xbmc.addon.audio\, xbmc.addon.image and xbmc.addon.executable.
+/// @param[in] string[0] Skin setting to store result in.
+/// @param[in] type[1\,...] Add-on types to allow selecting.
+/// }
+/// \table_row2_l{
+/// <b>`Skin.SetBool(setting[\,value])`</b>
+/// \anchor Skin_SetBool,
+/// Sets the skin `setting` to true\, for use with the conditional visibility
+/// tags containing \link Skin_HasSetting `Skin.HasSetting(setting)`\endlink. The settings are saved
+/// per-skin in settings.xml just like all the other Kodi settings.
+/// @param[in] setting Name of skin setting.
+/// @param[in] value Value to set ("false"\, or "true") (optional).
+/// }
+/// \table_row2_l{
+/// <b>`Skin.SetFile(string\,mask\,folderpath)`</b>
+/// ,
+/// \" minus quotes. If the folderpath parameter is set the file browser will
+/// start in that folder.
+/// @param[in] string Name of skin setting.
+/// @param[in] mask File mask or add-on type (optional).
+/// @param[in] folderpath Extra URL to allow selection from or.
+/// content type if mask is an addon-on type (optional).
+/// }
+/// \table_row2_l{
+/// <b>`Skin.SetImage(string[\,url])`</b>
+/// ,
+/// Pops up a file browser and allows the user to select an image file to be
+/// used in an image control elsewhere in the skin via the info tag
+/// `Skin.String(string)`. If the value parameter is specified\, then the
+/// file browser dialog does not pop up\, and the image path is set directly.
+/// The path option allows you to open the file browser in the specified
+/// folder.
+/// @param[in] string Name of skin setting.
+/// @param[in] url Extra URL to allow selection from (optional).
+/// }
+/// \table_row2_l{
+/// <b>`Skin.SetColor(string\,header[\,colorfile\,selectedcolor])`</b>
+/// \anchor Builtin_SetColor,
+/// Pops up a color selection dialog and allows the user to select a color to be
+/// used to define the color of a label control or as a colordiffuse value for a texture
+/// elsewhere in the skin via the info tag `Skin.String(string)`.
+/// Skinners can optionally set the color that needs to be preselected in the
+/// dialog by specifying the hex value of this color.
+/// Also optionally\, skinners can include their own color definition file. If not specified\,
+/// the default colorfile included with Kodi will be used.
+/// @param[in] string Name of skin setting.
+/// @param[in] string Dialog header text.
+/// @param[in] string Hex value of the color to preselect (optional)\,
+/// example: FF00FF00.
+/// @param[in] string Filepath of the color definition file (optional).
+/// <p><hr>
+/// @skinning_v20 **[New builtin]** \link Builtin_SetColor `SetColor(string\,header[\,colorfile\,selectedcolor])`\endlink
+/// <p>
+/// }
+/// \table_row2_l{
+/// <b>`Skin.SetNumeric(numeric[\,value])`</b>
+/// \anchor Skin_SetNumeric,
+/// Pops up a keyboard dialog and allows the user to input a numerical.
+/// @param[in] numeric Name of skin setting.
+/// @param[in] value Value of skin setting (optional).
+/// }
+/// \table_row2_l{
+/// <b>`Skin.SetPath(string[\,value])`</b>
+/// ,
+/// Pops up a folder browser and allows the user to select a folder of images
+/// to be used in a multi image control else where in the skin via the info
+/// tag `Skin.String(string)`. If the value parameter is specified\, then the
+/// file browser dialog does not pop up\, and the path is set directly.
+/// @param[in] string Name of skin setting.
+/// @param[in] value Extra URL to allow selection from (optional).
+/// }
+/// \table_row2_l{
+/// <b>`Skin.SetString(string[\,value])`</b>
+/// \anchor Skin_SetString,
+/// Pops up a keyboard dialog and allows the user to input a string which can
+/// be used in a label control elsewhere in the skin via the info tag
+/// \link Skin_StringValue `Skin.String(string)`\endlink. The value of the setting
+/// can also be compared to another value using the info bool \link Skin_StringCompare `Skin.String(string\, value)`\endlink.
+/// If the value parameter is specified\, then the
+/// keyboard dialog does not pop up\, and the string is set directly.
+/// @param[in] string Name of skin setting.
+/// @param[in] value Value of skin setting (optional).
+/// }
+/// \table_row2_l{
+/// <b>`Skin.Theme(cycle)`</b>
+/// \anchor Skin_CycleTheme,
+/// Cycles the skin theme. Skin.theme(-1) will go backwards.
+/// @param[in] cycle 0 or 1 to increase theme\, -1 to decrease.
+/// }
+/// \table_row2_l{
+/// <b>`Skin.ToggleDebug`</b>
+/// ,
+/// Toggles skin debug info on/off
+/// }
+/// \table_row2_l{
+/// <b>`Skin.ToggleSetting(setting)`</b>
+/// ,
+/// Toggles the skin `setting` for use with conditional visibility tags
+/// containing `Skin.HasSetting(setting)`.
+/// @param[in] setting Skin setting to toggle
+/// }
+/// \table_row2_l{
+/// <b>`Skin.TimerStart(timer)`</b>
+/// \anchor Builtin_SkinStartTimer,
+/// Starts the timer with name `timer`
+/// @param[in] timer The name of the timer
+/// <p><hr>
+/// @skinning_v20 **[New builtin]** \link Builtin_SkinStartTimer `Skin.TimerStart(timer)`\endlink
+/// <p>
+/// }
+/// \table_row2_l{
+/// <b>`Skin.TimerStop(timer)`</b>
+/// \anchor Builtin_SkinStopTimer,
+/// Stops the timer with name `timer`
+/// @param[in] timer The name of the timer
+/// <p><hr>
+/// @skinning_v20 **[New builtin]** \link Builtin_SkinStopTimer `Skin.TimerStop(timer)`\endlink
+/// <p>
+/// }
+/// \table_end
+///
+
+CBuiltins::CommandMap CSkinBuiltins::GetOperations() const
+{
+ return {{"reloadskin", {"Reload Kodi's skin", 0, ReloadSkin}},
+ {"unloadskin", {"Unload Kodi's skin", 0, UnloadSkin}},
+ {"skin.reset", {"Resets a skin setting to default", 1, SkinReset}},
+ {"skin.resetsettings", {"Resets all skin settings", 0, SkinResetAll}},
+ {"skin.setaddon", {"Prompts and set an addon", 2, SetAddon}},
+ {"skin.selectbool", {"Prompts and set a skin setting", 2, SelectBool}},
+ {"skin.setbool", {"Sets a skin setting on", 1, SetBool}},
+ {"skin.setfile", {"Prompts and sets a file", 1, SetFile}},
+ {"skin.setimage", {"Prompts and sets a skin image", 1, SetImage}},
+ {"skin.setcolor", {"Prompts and sets a skin color", 1, SetColor}},
+ {"skin.setnumeric", {"Prompts and sets numeric input", 1, SetNumeric}},
+ {"skin.setpath", {"Prompts and sets a skin path", 1, SetPath}},
+ {"skin.setstring", {"Prompts and sets skin string", 1, SetString}},
+ {"skin.theme", {"Control skin theme", 1, SetTheme}},
+ {"skin.toggledebug", {"Toggle skin debug", 0, SkinDebug}},
+ {"skin.togglesetting", {"Toggles a skin setting on or off", 1, ToggleSetting}},
+ {"skin.timerstart", {"Starts a given skin timer", 1, SkinTimerStart}},
+ {"skin.timerstop", {"Stops a given skin timer", 1, SkinTimerStop}}};
+}
diff --git a/xbmc/interfaces/builtins/SkinBuiltins.h b/xbmc/interfaces/builtins/SkinBuiltins.h
new file mode 100644
index 0000000..48a4306
--- /dev/null
+++ b/xbmc/interfaces/builtins/SkinBuiltins.h
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2005-2018 Team Kodi
+ * This file is part of Kodi - https://kodi.tv
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ * See LICENSES/README.md for more information.
+ */
+
+#pragma once
+
+#include "Builtins.h"
+
+//! \brief Class providing skin related built-in commands.
+class CSkinBuiltins
+{
+public:
+ //! \brief Returns the map of operations.
+ CBuiltins::CommandMap GetOperations() const;
+};
diff --git a/xbmc/interfaces/builtins/SystemBuiltins.cpp b/xbmc/interfaces/builtins/SystemBuiltins.cpp
new file mode 100644
index 0000000..3a41a5f
--- /dev/null
+++ b/xbmc/interfaces/builtins/SystemBuiltins.cpp
@@ -0,0 +1,267 @@
+/*
+ * Copyright (C) 2005-2018 Team Kodi
+ * This file is part of Kodi - https://kodi.tv
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ * See LICENSES/README.md for more information.
+ */
+
+#include "SystemBuiltins.h"
+
+#include "ServiceBroker.h"
+#include "messaging/ApplicationMessenger.h"
+#include "utils/StringUtils.h"
+
+/*! \brief Execute a system executable.
+ * \param params The parameters.
+ * \details params[0] = The path to the executable.
+ *
+ * Set the template parameter Wait to true to wait for execution exit.
+ */
+ template<int Wait=0>
+static int Exec(const std::vector<std::string>& params)
+{
+ CServiceBroker::GetAppMessenger()->PostMsg(TMSG_MINIMIZE);
+ CServiceBroker::GetAppMessenger()->PostMsg(TMSG_EXECUTE_OS, Wait, -1, nullptr, params[0]);
+
+ return 0;
+}
+
+/*! \brief Hibernate system.
+ * \param params (ignored)
+ */
+static int Hibernate(const std::vector<std::string>& params)
+{
+ CServiceBroker::GetAppMessenger()->PostMsg(TMSG_HIBERNATE);
+
+ return 0;
+}
+
+/*! \brief Inhibit idle shutdown timer.
+ * \param params The parameters.
+ * \details params[0] = "true" to inhibit shutdown timer (optional).
+ */
+static int InhibitIdle(const std::vector<std::string>& params)
+{
+ bool inhibit = (params.size() == 1 && StringUtils::EqualsNoCase(params[0], "true"));
+ CServiceBroker::GetAppMessenger()->PostMsg(TMSG_INHIBITIDLESHUTDOWN, inhibit);
+
+ return 0;
+}
+
+/*! \brief Minimize application.
+ * \param params (ignored)
+ */
+static int Minimize(const std::vector<std::string>& params)
+{
+ CServiceBroker::GetAppMessenger()->PostMsg(TMSG_MINIMIZE);
+
+ return 0;
+}
+
+/*! \brief Powerdown system.
+ * \param params (ignored)
+ */
+static int Powerdown(const std::vector<std::string>& params)
+{
+ CServiceBroker::GetAppMessenger()->PostMsg(TMSG_POWERDOWN);
+
+ return 0;
+}
+
+/*! \brief Quit application.
+ * \param params (ignored)
+ */
+static int Quit(const std::vector<std::string>& params)
+{
+ CServiceBroker::GetAppMessenger()->PostMsg(TMSG_QUIT);
+
+ return 0;
+}
+
+/*! \brief Reboot system.
+ * \param params (ignored)
+ */
+static int Reboot(const std::vector<std::string>& params)
+{
+ CServiceBroker::GetAppMessenger()->PostMsg(TMSG_RESTART);
+
+ return 0;
+}
+
+/*! \brief Restart application.
+ * \param params (ignored)
+ */
+static int RestartApp(const std::vector<std::string>& params)
+{
+ CServiceBroker::GetAppMessenger()->PostMsg(TMSG_RESTARTAPP);
+
+ return 0;
+}
+
+/*! \brief Activate screensaver.
+ * \param params (ignored)
+ */
+static int ActivateScreensaver(const std::vector<std::string>& params)
+{
+ CServiceBroker::GetAppMessenger()->PostMsg(TMSG_ACTIVATESCREENSAVER);
+
+ return 0;
+}
+
+/*! \brief Reset screensaver.
+ * \param params (ignored)
+ */
+static int ResetScreensaver(const std::vector<std::string>& params)
+{
+ CServiceBroker::GetAppMessenger()->PostMsg(TMSG_RESETSCREENSAVER);
+
+ return 0;
+}
+
+/*! \brief Inhibit screensaver.
+ * \param params The parameters.
+ * \details params[0] = "true" to inhibit screensaver (optional).
+ */
+static int InhibitScreenSaver(const std::vector<std::string>& params)
+{
+ bool inhibit = (params.size() == 1 && StringUtils::EqualsNoCase(params[0], "true"));
+ CServiceBroker::GetAppMessenger()->PostMsg(TMSG_INHIBITSCREENSAVER, inhibit);
+
+ return 0;
+}
+
+/*! \brief Shutdown system.
+ * \param params (ignored)
+ */
+static int Shutdown(const std::vector<std::string>& params)
+{
+ CServiceBroker::GetAppMessenger()->PostMsg(TMSG_SHUTDOWN);
+
+ return 0;
+}
+
+/*! \brief Suspend system.
+ * \param params (ignored)
+ */
+static int Suspend(const std::vector<std::string>& params)
+{
+ CServiceBroker::GetAppMessenger()->PostMsg(TMSG_SUSPEND);
+
+ return 0;
+}
+
+
+// Note: For new Texts with comma add a "\" before!!! Is used for table text.
+//
+/// \page page_List_of_built_in_functions
+/// \section built_in_functions_15 System built-in's
+///
+/// -----------------------------------------------------------------------------
+///
+/// \table_start
+/// \table_h2_l{
+/// Function,
+/// Description }
+/// \table_row2_l{
+/// <b>`ActivateScreensaver`</b>
+/// ,
+/// Starts the screensaver
+/// }
+/// \table_row2_l{
+/// <b>`InhibitScreensaver(yesNo)`</b>
+/// ,
+/// Inhibit the screensaver
+/// @param[in] yesNo value with "true" or "false" to inhibit or allow screensaver (leaving empty defaults to false)
+/// }
+/// \table_row2_l{
+/// <b>`Hibernate`</b>
+/// ,
+/// Hibernate (S4) the System
+/// }
+/// \table_row2_l{
+/// <b>`InhibitIdleShutdown(true/false)`</b>
+/// ,
+/// Prevent the system to shutdown on idle.
+/// @param[in] value "true" to inhibit shutdown timer (optional).
+/// }
+/// \table_row2_l{
+/// <b>`Minimize`</b>
+/// ,
+/// Minimizes Kodi
+/// }
+/// \table_row2_l{
+/// <b>`Powerdown`</b>
+/// ,
+/// Powerdown system
+/// }
+/// \table_row2_l{
+/// <b>`Quit`</b>
+/// ,
+/// Quits Kodi
+/// }
+/// \table_row2_l{
+/// <b>`Reboot`</b>
+/// ,
+/// Cold reboots the system (power cycle)
+/// }
+/// \table_row2_l{
+/// <b>`Reset`</b>
+/// ,
+/// Reset the system (same as reboot)
+/// }
+/// \table_row2_l{
+/// <b>`Restart`</b>
+/// ,
+/// Restart the system (same as reboot)
+/// }
+/// \table_row2_l{
+/// <b>`RestartApp`</b>
+/// ,
+/// Restarts Kodi (only implemented under Windows and Linux)
+/// }
+/// \table_row2_l{
+/// <b>`ShutDown`</b>
+/// ,
+/// Trigger default Shutdown action defined in System Settings
+/// }
+/// \table_row2_l{
+/// <b>`Suspend`</b>
+/// ,
+/// Suspends (S3 / S1 depending on bios setting) the System
+/// }
+/// \table_row2_l{
+/// <b>`System.Exec(exec)`</b>
+/// ,
+/// Execute shell commands
+/// @param[in] exec The path to the executable
+/// }
+/// \table_row2_l{
+/// <b>`System.ExecWait(exec)`</b>
+/// ,
+/// Execute shell commands and freezes Kodi until shell is closed
+/// @param[in] exec The path to the executable
+/// }
+/// \table_end
+///
+
+CBuiltins::CommandMap CSystemBuiltins::GetOperations() const
+{
+ return {{"activatescreensaver", {"Activate Screensaver", 0, ActivateScreensaver}},
+ {"resetscreensaver", {"Reset Screensaver", 0, ResetScreensaver}},
+ {"hibernate", {"Hibernates the system", 0, Hibernate}},
+ {"inhibitidleshutdown", {"Inhibit idle shutdown", 0, InhibitIdle}},
+ {"inhibitscreensaver", {"Inhibit Screensaver", 0, InhibitScreenSaver}},
+ {"minimize", {"Minimize Kodi", 0, Minimize}},
+ {"powerdown", {"Powerdown system", 0, Powerdown}},
+ {"quit", {"Quit Kodi", 0, Quit}},
+ {"reboot", {"Reboot the system", 0, Reboot}},
+ {"reset", {"Reset the system (same as reboot)", 0, Reboot}},
+ {"restart", {"Restart the system (same as reboot)", 0, Reboot}},
+ {"restartapp", {"Restart Kodi", 0, RestartApp}},
+ {"shutdown", {"Shutdown the system", 0, Shutdown}},
+ {"suspend", {"Suspends the system", 0, Suspend}},
+ {"system.exec", {"Execute shell commands", 1, Exec<0>}},
+ {"system.execwait",
+ {"Execute shell commands and freezes Kodi until shell is closed", 1, Exec<1>}}};
+}
diff --git a/xbmc/interfaces/builtins/SystemBuiltins.h b/xbmc/interfaces/builtins/SystemBuiltins.h
new file mode 100644
index 0000000..ad5c16c
--- /dev/null
+++ b/xbmc/interfaces/builtins/SystemBuiltins.h
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2005-2018 Team Kodi
+ * This file is part of Kodi - https://kodi.tv
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ * See LICENSES/README.md for more information.
+ */
+
+#pragma once
+
+#include "Builtins.h"
+
+//! \brief Class providing system related built-in commands.
+class CSystemBuiltins
+{
+public:
+ //! \brief Returns the map of operations.
+ CBuiltins::CommandMap GetOperations() const;
+};
diff --git a/xbmc/interfaces/builtins/WeatherBuiltins.cpp b/xbmc/interfaces/builtins/WeatherBuiltins.cpp
new file mode 100644
index 0000000..8769a88
--- /dev/null
+++ b/xbmc/interfaces/builtins/WeatherBuiltins.cpp
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2005-2018 Team Kodi
+ * This file is part of Kodi - https://kodi.tv
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ * See LICENSES/README.md for more information.
+ */
+
+#include "WeatherBuiltins.h"
+
+#include "ServiceBroker.h"
+#include "guilib/GUIComponent.h"
+#include "guilib/GUIWindowManager.h"
+
+#include <stdlib.h>
+
+/*! \brief Switch to a given weather location.
+ * \param params The parameters.
+ * \details params[0] = 1, 2 or 3.
+ */
+static int SetLocation(const std::vector<std::string>& params)
+{
+ int loc = atoi(params[0].c_str());
+ CGUIMessage msg(GUI_MSG_ITEM_SELECT, 0, 0, loc);
+ CServiceBroker::GetGUI()->GetWindowManager().SendMessage(msg, WINDOW_WEATHER);
+
+ return 0;
+}
+
+/*! \brief Switch weather location or refresh current.
+ * \param params (ignored)
+ *
+ * The Direction template parameter can be -1 for previous location,
+ * 1 for next location or 0 to refresh current location.
+ */
+ template<int Direction>
+static int SwitchLocation(const std::vector<std::string>& params)
+{
+ CGUIMessage msg(GUI_MSG_MOVE_OFFSET, 0, 0, Direction);
+ CServiceBroker::GetGUI()->GetWindowManager().SendMessage(msg, WINDOW_WEATHER);
+
+ return 0;
+}
+
+// Note: For new Texts with comma add a "\" before!!! Is used for table text.
+//
+/// \page page_List_of_built_in_functions
+/// \section built_in_functions_16 Weather built-in's
+///
+/// -----------------------------------------------------------------------------
+///
+/// \table_start
+/// \table_h2_l{
+/// Function,
+/// Description }
+/// \table_row2_l{
+/// <b>`Weather.Refresh`</b>
+/// ,
+/// Force weather data refresh.
+/// }
+/// \table_row2_l{
+/// <b>`Weather.LocationNext`</b>
+/// ,
+/// Switch to next weather location
+/// }
+/// \table_row2_l{
+/// <b>`Weather.LocationPrevious`</b>
+/// ,
+/// Switch to previous weather location
+/// }
+/// \table_row2_l{
+/// <b>`Weather.LocationSet`</b>
+/// ,
+/// Switch to given weather location (parameter can be 1-3).
+/// @param[in] parameter 1-3
+/// }
+/// \table_end
+///
+
+CBuiltins::CommandMap CWeatherBuiltins::GetOperations() const
+{
+ return {
+ {"weather.refresh", {"Force weather data refresh", 0, SwitchLocation<0>}},
+ {"weather.locationnext", {"Switch to next weather location", 0, SwitchLocation<1>}},
+ {"weather.locationprevious", {"Switch to previous weather location", 0, SwitchLocation<-1>}},
+ {"weather.locationset", {"Switch to given weather location (parameter can be 1-3)", 1, SetLocation}}
+ };
+}
diff --git a/xbmc/interfaces/builtins/WeatherBuiltins.h b/xbmc/interfaces/builtins/WeatherBuiltins.h
new file mode 100644
index 0000000..6827cfc
--- /dev/null
+++ b/xbmc/interfaces/builtins/WeatherBuiltins.h
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2005-2018 Team Kodi
+ * This file is part of Kodi - https://kodi.tv
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ * See LICENSES/README.md for more information.
+ */
+
+#pragma once
+
+#include "Builtins.h"
+
+//! \brief Class providing weather related built-in commands.
+class CWeatherBuiltins
+{
+public:
+ //! \brief Returns the map of operations.
+ CBuiltins::CommandMap GetOperations() const;
+};