diff options
25 files changed, 592 insertions, 272 deletions
diff --git a/addons/resource.language.en_gb/resources/strings.po b/addons/resource.language.en_gb/resources/strings.po index e53ad89714..1f862ceaa9 100644 --- a/addons/resource.language.en_gb/resources/strings.po +++ b/addons/resource.language.en_gb/resources/strings.po @@ -10277,11 +10277,7 @@ msgctxt "#19194" msgid "Continue?" msgstr "" -#. label for a context menu entry for pvr client specific actions -#: xbmc/pvr/PVRContextMenus.cpp -msgctxt "#19195" -msgid "Client actions" -msgstr "" +#empty string with id 19195 #. value for "pvr client specific actions" dialog headers #: xbmc/pvr/PVRGUIActions.cpp diff --git a/addons/skin.estuary/xml/Home.xml b/addons/skin.estuary/xml/Home.xml index 86bfd88a4e..34361d3217 100644 --- a/addons/skin.estuary/xml/Home.xml +++ b/addons/skin.estuary/xml/Home.xml @@ -427,7 +427,7 @@ <include content="ImageWidget" condition="!System.HasPVRAddon"> <param name="text_label" value="$LOCALIZE[31143]" /> <param name="button_label" value="$LOCALIZE[31144]" /> - <param name="button_onclick" value="ActivateWindow(addonbrowser,addons://user/xbmc.pvrclient,return)"/> + <param name="button_onclick" value="ActivateWindow(addonbrowser,addons://repository.xbmc.org/xbmc.pvrclient,return)"/> <param name="button_id" value="12400"/> <param name="button2_onclick" value="Skin.SetBool(HomeMenuNoTVButton)"/> </include> @@ -500,7 +500,7 @@ <include content="ImageWidget" condition="!System.HasPVRAddon"> <param name="text_label" value="$LOCALIZE[31143]" /> <param name="button_label" value="$LOCALIZE[31144]" /> - <param name="button_onclick" value="ActivateWindow(addonbrowser,addons://user/xbmc.pvrclient,return)"/> + <param name="button_onclick" value="ActivateWindow(addonbrowser,addons://repository.xbmc.org/xbmc.pvrclient,return)"/> <param name="button_id" value="13400"/> <param name="button2_onclick" value="Skin.SetBool(HomeMenuNoRadioButton)"/> </include> @@ -768,8 +768,8 @@ </control> <include content="ImageWidget"> <param name="text_label" value="$LOCALIZE[31162]" /> - <param name="button_label" value="$LOCALIZE[31110]" /> - <param name="button_onclick" value="ActivateWindow(games)"/> + <param name="button_label" value="$LOCALIZE[31144]" /> + <param name="button_onclick" value="ActivateWindow(addonbrowser,addons://repository.xbmc.org/category.gameaddons,return)"/> <param name="button_id" value="17100"/> <param name="visible" value="!Integer.IsGreater(Container(17001).NumItems,0)"/> <param name="button2_onclick" value="Skin.SetBool(HomeMenuNoGamesButton)"/> diff --git a/docs/doxygen/Doxyfile.doxy b/docs/doxygen/Doxyfile.doxy index 170c3c4200..514e942d6d 100644 --- a/docs/doxygen/Doxyfile.doxy +++ b/docs/doxygen/Doxyfile.doxy @@ -249,6 +249,7 @@ ALIASES = "table_start=<table width= 100% style= border bgcolor= "python_class{1}=\htmlonly <h4><code><span style=\"font-style: italic;\">Class: </span><span style=\"font-style: bold;\"><font color=31363b><big>\1</big></font></span></code></h4> \endhtmlonly" \ "python_class_with_rev{2}=\htmlonly <h4><code><span style=\"font-style: italic;\">Class: </span><span style=\"font-style: bold;\"><font color=31363b><big>\1</big></font></span></code><span style="float:right;"><small>\2</small></span></h4> \endhtmlonly" \ "doc_header{1}=\htmlonly <h3><span style=\"text-decoration: underline;\"><span style=\"font-style: italic;\"><span style=\"color: rgb(102, 102, 102);\">\1</span></span></span></h3> \endhtmlonly" \ + "python_removed_function{3}=\htmlonly <dl class=\"reflist\"><dt>Member <a class="el" href=\"\2\">\1</a> (...)</dt><dd>\3</dd></dl>\endhtmlonly" \ "python_v12=\xrefitem python_v12 \"v12 Python API changes\" \"\"" \ "python_v13=\xrefitem python_v13 \"v13 Python API changes\" \"\"" \ "python_v14=\xrefitem python_v14 \"v14 Python API changes\" \"\"" \ diff --git a/project/Win32BuildSetup/genNsisInstaller.nsi b/project/Win32BuildSetup/genNsisInstaller.nsi index 1693c58d71..bba7eea574 100644 --- a/project/Win32BuildSetup/genNsisInstaller.nsi +++ b/project/Win32BuildSetup/genNsisInstaller.nsi @@ -143,6 +143,7 @@ Function HandleKodiInDestDir StrCpy $CleanDestDir "0" Abort done: + MessageBox MB_OK|MB_ICONINFORMATION "All binary add-ons (e.g. pvr, visualizations, inputstream, etc) that were previously included by default in the installer have been moved to the Kodi repository. You will have to install the ones you previously used from the repository.$\nYour add-on settings are kept intact and will be used again after installing the add-on." ${EndIf} FunctionEnd diff --git a/system/addon-manifest.xml b/system/addon-manifest.xml index e5ea2ed425..f0edcddbc1 100644 --- a/system/addon-manifest.xml +++ b/system/addon-manifest.xml @@ -3,10 +3,6 @@ <addon>audioencoder.kodi.builtin.wma</addon> <addon>game.controller.default</addon> <addon>game.controller.snes</addon> - <!-- TODO: Remove game add-ons when no longer applicable --> - <addon optional="true">game.libretro</addon> - <addon optional="true">game.libretro.2048</addon> - <addon optional="true">game.libretro.mrboom</addon> <addon>kodi.binary.global.audioengine</addon> <addon>kodi.binary.global.main</addon> <addon>kodi.binary.global.general</addon> diff --git a/tools/buildsteps/windows/make-addons.bat b/tools/buildsteps/windows/make-addons.bat index c68cf17052..b8e0ac59ac 100644 --- a/tools/buildsteps/windows/make-addons.bat +++ b/tools/buildsteps/windows/make-addons.bat @@ -10,16 +10,16 @@ SET store= SETLOCAL EnableDelayedExpansion FOR %%b IN (%*) DO ( - IF %%b == install ( + IF %%~b == install ( SET install=true - ) ELSE ( IF %%b == clean ( + ) ELSE ( IF %%~b == clean ( SET clean=true - ) ELSE ( IF %%b == package ( + ) ELSE ( IF %%~b == package ( SET package=true - ) ELSE ( IF %%b == win10 ( + ) ELSE ( IF %%~b == win10 ( SET store=store ) ELSE ( - SET addon=!addon! %%b + SET addon=!addon! %%~b )))) ) SETLOCAL DisableDelayedExpansion diff --git a/tools/depends/configure.ac b/tools/depends/configure.ac index ed6d2468ab..979819d752 100644 --- a/tools/depends/configure.ac +++ b/tools/depends/configure.ac @@ -620,8 +620,8 @@ else fi # add user supplied flags to the end, so they override our defaults -platform_cflags_release="$tmp_cflags $relase_cflags $optimize_flags $target_cflags" -platform_cxxflags_release="$tmp_cxxflags $relase_cflags $optimize_flags $target_cxxflags" +platform_cflags_release="$tmp_cflags $release_cflags $optimize_flags $target_cflags" +platform_cxxflags_release="$tmp_cxxflags $release_cflags $optimize_flags $target_cxxflags" platform_cflags_debug="$tmp_cflags $debug_cflags $target_cflags" platform_cxxflags_debug="$tmp_cxxflags $debug_cflags $target_cxxflags" platform_ldflags+=" $target_ldflags $LIBS" diff --git a/xbmc/ContextMenuManager.cpp b/xbmc/ContextMenuManager.cpp index 721af94256..20c6cec52a 100644 --- a/xbmc/ContextMenuManager.cpp +++ b/xbmc/ContextMenuManager.cpp @@ -40,6 +40,7 @@ CContextMenuManager::~CContextMenuManager() void CContextMenuManager::Deinit() { + CPVRContextMenuManager::GetInstance().Events().Unsubscribe(this); m_addonMgr.Events().Unsubscribe(this); m_items.clear(); } @@ -47,6 +48,7 @@ void CContextMenuManager::Deinit() void CContextMenuManager::Init() { m_addonMgr.Events().Subscribe(this, &CContextMenuManager::OnEvent); + CPVRContextMenuManager::GetInstance().Events().Subscribe(this, &CContextMenuManager::OnPVREvent); CSingleLock lock(m_criticalSection); m_items = { @@ -136,6 +138,30 @@ void CContextMenuManager::OnEvent(const ADDON::AddonEvent& event) } } +void CContextMenuManager::OnPVREvent(const PVRContextMenuEvent& event) +{ + switch (event.action) + { + case PVRContextMenuEventAction::ADD_ITEM: + { + CSingleLock lock(m_criticalSection); + m_items.emplace_back(event.item); + break; + } + case PVRContextMenuEventAction::REMOVE_ITEM: + { + CSingleLock lock(m_criticalSection); + auto it = std::find(m_items.begin(), m_items.end(), event.item); + if (it != m_items.end()) + m_items.erase(it); + break; + } + + default: + break; + } +} + bool CContextMenuManager::IsVisible( const CContextMenuItem& menuItem, const CContextMenuItem& root, const CFileItem& fileItem) const { diff --git a/xbmc/ContextMenuManager.h b/xbmc/ContextMenuManager.h index 5e124298e0..21e0e70578 100644 --- a/xbmc/ContextMenuManager.h +++ b/xbmc/ContextMenuManager.h @@ -14,6 +14,10 @@ #include "addons/ContextMenuAddon.h" #include "ContextMenuItem.h" +namespace PVR +{ + struct PVRContextMenuEvent; +} using ContextMenuView = std::vector<std::shared_ptr<const IContextMenuItem>>; @@ -45,6 +49,8 @@ private: void ReloadAddonItems(); void OnEvent(const ADDON::AddonEvent& event); + void OnPVREvent(const PVR::PVRContextMenuEvent& event); + ADDON::CAddonMgr& m_addonMgr; mutable CCriticalSection m_criticalSection; diff --git a/xbmc/addons/CMakeLists.txt b/xbmc/addons/CMakeLists.txt index f4130e494d..3bed5a412d 100644 --- a/xbmc/addons/CMakeLists.txt +++ b/xbmc/addons/CMakeLists.txt @@ -23,6 +23,7 @@ set(SOURCES Addon.cpp LanguageResource.cpp PluginSource.cpp PVRClient.cpp + PVRClientMenuHooks.cpp Repository.cpp RepositoryUpdater.cpp Scraper.cpp @@ -62,6 +63,7 @@ set(HEADERS Addon.h LanguageResource.h PluginSource.h PVRClient.h + PVRClientMenuHooks.h Repository.h RepositoryUpdater.h Resource.h diff --git a/xbmc/addons/PVRClient.cpp b/xbmc/addons/PVRClient.cpp index 128ea66ff1..0fb2e643d5 100644 --- a/xbmc/addons/PVRClient.cpp +++ b/xbmc/addons/PVRClient.cpp @@ -17,6 +17,7 @@ extern "C" { } #include "ServiceBroker.h" +#include "addons/PVRClientMenuHooks.h" #include "cores/VideoPlayer/DVDDemuxers/DVDDemuxUtils.h" #include "dialogs/GUIDialogKaiToast.h" #include "events/EventLog.h" @@ -110,7 +111,7 @@ void CPVRClient::ResetProperties(int iClientId /* = PVR_INVALID_CLIENT_ID */) m_strFriendlyName = DEFAULT_INFO_STRING_VALUE; m_strBackendName = DEFAULT_INFO_STRING_VALUE; m_strBackendHostname.clear(); - m_menuhooks.clear(); + m_menuhooks.reset(); m_timertypes.clear(); m_clientCapabilities.clear(); @@ -177,6 +178,9 @@ void CPVRClient::Destroy(void) /* destroy the add-on */ CAddonDll::Destroy(); + if (m_menuhooks) + m_menuhooks->Clear(); + /* reset all properties to defaults */ ResetProperties(); } @@ -265,22 +269,31 @@ void CPVRClient::WriteClientRecordingInfo(const CPVRRecording &xbmcRecording, PV xbmcRecording.RecordingTimeAsUTC().GetAsTime(recTime); addonRecording = {{0}}; - addonRecording.recordingTime = recTime - CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->m_iPVRTimeCorrection; strncpy(addonRecording.strRecordingId, xbmcRecording.m_strRecordingId.c_str(), sizeof(addonRecording.strRecordingId) - 1); strncpy(addonRecording.strTitle, xbmcRecording.m_strTitle.c_str(), sizeof(addonRecording.strTitle) - 1); + strncpy(addonRecording.strEpisodeName, xbmcRecording.m_strShowTitle.c_str(), sizeof(addonRecording.strEpisodeName) - 1); + addonRecording.iSeriesNumber = xbmcRecording.m_iSeason; + addonRecording.iEpisodeNumber = xbmcRecording.m_iEpisode; + addonRecording.iYear = xbmcRecording.GetYear(); + strncpy(addonRecording.strDirectory, xbmcRecording.m_strDirectory.c_str(), sizeof(addonRecording.strDirectory) - 1); strncpy(addonRecording.strPlotOutline, xbmcRecording.m_strPlotOutline.c_str(), sizeof(addonRecording.strPlotOutline) - 1); strncpy(addonRecording.strPlot, xbmcRecording.m_strPlot.c_str(), sizeof(addonRecording.strPlot) - 1); + strncpy(addonRecording.strGenreDescription, xbmcRecording.GetGenresLabel().c_str(), sizeof(addonRecording.strGenreDescription) - 1); strncpy(addonRecording.strChannelName, xbmcRecording.m_strChannelName.c_str(), sizeof(addonRecording.strChannelName) - 1); - addonRecording.iDuration = xbmcRecording.GetDuration(); - addonRecording.iPriority = xbmcRecording.m_iPriority; - addonRecording.iLifetime = xbmcRecording.m_iLifetime; - addonRecording.iPlayCount = xbmcRecording.GetLocalPlayCount(); - addonRecording.iLastPlayedPosition = lrint(xbmcRecording.GetLocalResumePoint().timeInSeconds); - addonRecording.bIsDeleted = xbmcRecording.IsDeleted(); - strncpy(addonRecording.strDirectory, xbmcRecording.m_strDirectory.c_str(), sizeof(addonRecording.strDirectory) - 1); strncpy(addonRecording.strIconPath, xbmcRecording.m_strIconPath.c_str(), sizeof(addonRecording.strIconPath) - 1); strncpy(addonRecording.strThumbnailPath, xbmcRecording.m_strThumbnailPath.c_str(), sizeof(addonRecording.strThumbnailPath) - 1); strncpy(addonRecording.strFanartPath, xbmcRecording.m_strFanartPath.c_str(), sizeof(addonRecording.strFanartPath) - 1); + addonRecording.recordingTime = recTime - CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->m_iPVRTimeCorrection; + addonRecording.iDuration = xbmcRecording.GetDuration(); + addonRecording.iPriority = xbmcRecording.m_iPriority; + addonRecording.iLifetime = xbmcRecording.m_iLifetime; + addonRecording.iGenreType = xbmcRecording.GenreType(); + addonRecording.iGenreSubType = xbmcRecording.GenreSubType(); + addonRecording.iPlayCount = xbmcRecording.GetLocalPlayCount(); + addonRecording.iLastPlayedPosition = lrint(xbmcRecording.GetLocalResumePoint().timeInSeconds); + addonRecording.bIsDeleted = xbmcRecording.IsDeleted(); + addonRecording.iChannelUid = xbmcRecording.ChannelUid(); + addonRecording.channelType = xbmcRecording.IsRadio() ? PVR_RECORDING_CHANNEL_TYPE_RADIO : PVR_RECORDING_CHANNEL_TYPE_TV; } /*! @@ -595,45 +608,6 @@ PVR_ERROR CPVRClient::RenameChannel(const CPVRChannelPtr &channel) }, m_clientCapabilities.SupportsChannelSettings()); } -PVR_ERROR CPVRClient::CallMenuHook(const PVR_MENUHOOK &hook, const CFileItemPtr item) -{ - return DoAddonCall(__FUNCTION__, [&hook, item](const AddonInstance* addon) { - PVR_MENUHOOK_DATA hookData; - hookData.cat = PVR_MENUHOOK_UNKNOWN; - - if (item) - { - if (item->IsEPG()) - { - hookData.cat = PVR_MENUHOOK_EPG; - hookData.data.iEpgUid = item->GetEPGInfoTag()->UniqueBroadcastID(); - } - else if (item->IsPVRChannel()) - { - hookData.cat = PVR_MENUHOOK_CHANNEL; - WriteClientChannelInfo(item->GetPVRChannelInfoTag(), hookData.data.channel); - } - else if (item->IsUsablePVRRecording()) - { - hookData.cat = PVR_MENUHOOK_RECORDING; - WriteClientRecordingInfo(*item->GetPVRRecordingInfoTag(), hookData.data.recording); - } - else if (item->IsDeletedPVRRecording()) - { - hookData.cat = PVR_MENUHOOK_DELETED_RECORDING; - WriteClientRecordingInfo(*item->GetPVRRecordingInfoTag(), hookData.data.recording); - } - else if (item->IsPVRTimer()) - { - hookData.cat = PVR_MENUHOOK_TIMER; - WriteClientTimerInfo(*item->GetPVRTimerInfoTag(), hookData.data.timer); - } - } - - return addon->MenuHook(hook, hookData); - }); -} - PVR_ERROR CPVRClient::GetEPGForChannel(const CPVRChannelPtr &channel, CPVREpg *epg, time_t start /* = 0 */, time_t end /* = 0 */, bool bSaveInDb /* = false*/) { return DoAddonCall(__FUNCTION__, [this, channel, epg, start, end, bSaveInDb](const AddonInstance* addon) { @@ -714,6 +688,7 @@ public: strEpisodeName = m_strEpisodeName.c_str(); strIconPath = m_strIconPath.c_str(); strSeriesLink = m_strSeriesLink.c_str(); + strGenreDescription = kodiTag->GetGenresLabel().c_str(); } virtual ~CAddonEpgTag() = default; @@ -1183,28 +1158,6 @@ PVR_ERROR CPVRClient::DemuxRead(DemuxPacket* &packet) }, m_clientCapabilities.HandlesDemuxing()); } -bool CPVRClient::HasMenuHooks(PVR_MENUHOOK_CAT cat) const -{ - bool bReturn(false); - if (m_bReadyToUse && !m_menuhooks.empty()) - { - for (auto hook : m_menuhooks) - { - if (hook.category == cat || hook.category == PVR_MENUHOOK_ALL) - { - bReturn = true; - break; - } - } - } - return bReturn; -} - -PVR_MENUHOOKS& CPVRClient::GetMenuHooks(void) -{ - return m_menuhooks; -} - const char *CPVRClient::ToString(const PVR_ERROR error) { switch (error) @@ -1392,6 +1345,67 @@ PVR_ERROR CPVRClient::OnPowerSavingDeactivated() }); } +std::shared_ptr<CPVRClientMenuHooks> CPVRClient::GetMenuHooks() +{ + if (!m_menuhooks) + m_menuhooks.reset(new CPVRClientMenuHooks(ID())); + + return m_menuhooks; +} + +PVR_ERROR CPVRClient::CallMenuHook(const CPVRClientMenuHook &hook, const CFileItemPtr &item) +{ + return DoAddonCall(__FUNCTION__, [&hook, &item](const AddonInstance* addon) { + PVR_MENUHOOK_DATA hookData; + hookData.cat = PVR_MENUHOOK_UNKNOWN; + + if (item) + { + if (item->IsEPG()) + { + hookData.cat = PVR_MENUHOOK_EPG; + hookData.data.iEpgUid = item->GetEPGInfoTag()->UniqueBroadcastID(); + } + else if (item->IsPVRChannel()) + { + hookData.cat = PVR_MENUHOOK_CHANNEL; + WriteClientChannelInfo(item->GetPVRChannelInfoTag(), hookData.data.channel); + } + else if (item->IsUsablePVRRecording()) + { + hookData.cat = PVR_MENUHOOK_RECORDING; + WriteClientRecordingInfo(*item->GetPVRRecordingInfoTag(), hookData.data.recording); + } + else if (item->IsDeletedPVRRecording()) + { + hookData.cat = PVR_MENUHOOK_DELETED_RECORDING; + WriteClientRecordingInfo(*item->GetPVRRecordingInfoTag(), hookData.data.recording); + } + else if (item->IsPVRTimer()) + { + hookData.cat = PVR_MENUHOOK_TIMER; + WriteClientTimerInfo(*item->GetPVRTimerInfoTag(), hookData.data.timer); + } + else + { + CLog::LogF(LOGERROR, "Unhandled item type."); + return PVR_ERROR_INVALID_PARAMETERS; + } + } + else + { + hookData.cat = PVR_MENUHOOK_SETTING; + } + + PVR_MENUHOOK menuHook = {0}; + menuHook.category = hookData.cat; + menuHook.iHookId = hook.GetId(); + menuHook.iLocalizedStringId = hook.GetLabelId(); + + return addon->MenuHook(menuHook, hookData); + }); +} + void CPVRClient::SetPriority(int iPriority) { CSingleLock lock(m_critSection); @@ -1566,15 +1580,7 @@ void CPVRClient::cb_add_menu_hook(void *kodiInstance, PVR_MENUHOOK *hook) return; } - PVR_MENUHOOKS& hooks = client->GetMenuHooks(); - - PVR_MENUHOOK hookInt; - hookInt.iHookId = hook->iHookId; - hookInt.iLocalizedStringId = hook->iLocalizedStringId; - hookInt.category = hook->category; - - /* add this new hook */ - hooks.emplace_back(hookInt); + client->GetMenuHooks()->AddHook(*hook); } void CPVRClient::cb_recording(void *kodiInstance, const char *strName, const char *strFileName, bool bOnOff) diff --git a/xbmc/addons/PVRClient.h b/xbmc/addons/PVRClient.h index d6b1a12d9f..5a5670cdd1 100644 --- a/xbmc/addons/PVRClient.h +++ b/xbmc/addons/PVRClient.h @@ -23,8 +23,8 @@ namespace PVR { class CPVRChannelGroups; class CPVRTimersContainer; - - typedef std::vector<PVR_MENUHOOK> PVR_MENUHOOKS; + class CPVRClientMenuHook; + class CPVRClientMenuHooks; class CPVRClient; typedef std::shared_ptr<CPVRClient> CPVRClientPtr; @@ -394,24 +394,6 @@ namespace PVR */ PVR_ERROR FillEpgTagStreamFileItem(CFileItem &fileItem); - /*! - * @return True if this add-on has menu hooks, false otherwise. - */ - bool HasMenuHooks(PVR_MENUHOOK_CAT cat) const; - - /*! - * @return The menu hooks for this add-on. - */ - PVR_MENUHOOKS& GetMenuHooks(); - - /*! - * @brief Call one of the menu hooks of this client. - * @param hook The hook to call. - * @param item The selected file item for which the hook was called. - * @return PVR_ERROR_NO_ERROR on success, respective error code otherwise. - */ - PVR_ERROR CallMenuHook(const PVR_MENUHOOK &hook, const CFileItemPtr item); - //@} /** @name PVR EPG methods */ //@{ @@ -824,6 +806,20 @@ namespace PVR bool GetAddonProperties(void); /*! + * @brief Get the client's menu hooks. + * @return The hooks. Guaranteed never to be null. + */ + std::shared_ptr<CPVRClientMenuHooks> GetMenuHooks(); + + /*! + * @brief Call one of the menu hooks of the client. + * @param hook The hook to call. + * @param item The item associated with the hook to be called. + * @return PVR_ERROR_NO_ERROR on success, respective error code otherwise. + */ + PVR_ERROR CallMenuHook(const CPVRClientMenuHook &hook, const CFileItemPtr &item); + + /*! * @brief Propagate power management events to this add-on * @return PVR_ERROR_NO_ERROR on success, respective error code otherwise. */ @@ -1071,7 +1067,6 @@ namespace PVR PVR_CONNECTION_STATE m_connectionState; /*!< the backend connection state */ PVR_CONNECTION_STATE m_prevConnectionState; /*!< the previous backend connection state */ bool m_ignoreClient; /*!< signals to PVRManager to ignore this client until it has been connected */ - PVR_MENUHOOKS m_menuhooks; /*!< the menu hooks for this add-on */ CPVRTimerTypes m_timertypes; /*!< timer types supported by this backend */ int m_iClientId; /*!< unique ID of the client */ mutable int m_iPriority; /*!< priority of the client */ @@ -1084,6 +1079,7 @@ namespace PVR std::string m_strFriendlyName; /*!< the cached friendly name */ std::string m_strBackendHostname; /*!< the cached backend hostname */ CPVRClientCapabilities m_clientCapabilities; /*!< the cached add-on's capabilities */ + std::shared_ptr<CPVRClientMenuHooks> m_menuhooks; /*!< the menu hooks for this add-on */ /* stored strings to make sure const char* members in PVR_PROPERTIES stay valid */ std::string m_strUserPath; /*!< @brief translated path to the user profile */ diff --git a/xbmc/addons/PVRClientMenuHooks.cpp b/xbmc/addons/PVRClientMenuHooks.cpp new file mode 100644 index 0000000000..dfabdc7d5a --- /dev/null +++ b/xbmc/addons/PVRClientMenuHooks.cpp @@ -0,0 +1,180 @@ +/* + * Copyright (C) 2012-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 "PVRClientMenuHooks.h" + +#include "pvr/PVRContextMenus.h" + +#include "addons/kodi-addon-dev-kit/include/kodi/xbmc_pvr_types.h" +#include "guilib/LocalizeStrings.h" +#include "utils/log.h" + +namespace PVR +{ + +CPVRClientMenuHook::CPVRClientMenuHook(const std::string &addonId, const PVR_MENUHOOK &hook) +: m_addonId(addonId), + m_hook(new PVR_MENUHOOK(hook)) +{ + if (hook.category != PVR_MENUHOOK_UNKNOWN && + hook.category != PVR_MENUHOOK_ALL && + hook.category != PVR_MENUHOOK_CHANNEL && + hook.category != PVR_MENUHOOK_TIMER && + hook.category != PVR_MENUHOOK_EPG && + hook.category != PVR_MENUHOOK_RECORDING && + hook.category != PVR_MENUHOOK_RECORDING && + hook.category != PVR_MENUHOOK_SETTING) + CLog::LogF(LOGERROR, "Unknown PVR_MENUHOOK_CAT value: %d", hook.category); +} + +bool CPVRClientMenuHook::operator ==(const CPVRClientMenuHook& right) const +{ + if (this == &right) + return true; + + return m_addonId == right.m_addonId && + m_hook->iHookId == right.m_hook->iHookId && + m_hook->iLocalizedStringId == right.m_hook->iLocalizedStringId && + m_hook->category == right.m_hook->category; +} + +bool CPVRClientMenuHook::IsAllHook() const +{ + return m_hook->category == PVR_MENUHOOK_ALL; +} + +bool CPVRClientMenuHook::IsChannelHook() const +{ + return m_hook->category == PVR_MENUHOOK_CHANNEL; +} + +bool CPVRClientMenuHook::IsTimerHook() const +{ + return m_hook->category == PVR_MENUHOOK_TIMER; +} + +bool CPVRClientMenuHook::IsEpgHook() const +{ + return m_hook->category == PVR_MENUHOOK_EPG; +} + +bool CPVRClientMenuHook::IsRecordingHook() const +{ + return m_hook->category == PVR_MENUHOOK_RECORDING; +} + +bool CPVRClientMenuHook::IsDeletedRecordingHook() const +{ + return m_hook->category == PVR_MENUHOOK_DELETED_RECORDING; +} + +bool CPVRClientMenuHook::IsSettingsHook() const +{ + return m_hook->category == PVR_MENUHOOK_SETTING; +} + +unsigned int CPVRClientMenuHook::GetId() const +{ + return m_hook->iHookId; +} + +unsigned int CPVRClientMenuHook::GetLabelId() const +{ + return m_hook->iLocalizedStringId; +} + +std::string CPVRClientMenuHook::GetLabel() const +{ + return g_localizeStrings.GetAddonString(m_addonId, m_hook->iLocalizedStringId); +} + +void CPVRClientMenuHooks::AddHook(const PVR_MENUHOOK &addonHook) +{ + if (!m_hooks) + m_hooks.reset(new std::vector<CPVRClientMenuHook>()); + + const CPVRClientMenuHook hook(m_addonId, addonHook); + m_hooks->emplace_back(hook); + CPVRContextMenuManager::GetInstance().AddMenuHook(hook); +} + +void CPVRClientMenuHooks::Clear() +{ + if (!m_hooks) + return; + + for (const auto& hook : *m_hooks) + CPVRContextMenuManager::GetInstance().RemoveMenuHook(hook); + + m_hooks.reset(); +} + +std::vector<CPVRClientMenuHook> CPVRClientMenuHooks::GetHooks(std::function<bool(const CPVRClientMenuHook& hook)> function) const +{ + std::vector<CPVRClientMenuHook> hooks; + + if (!m_hooks) + return hooks; + + for (const CPVRClientMenuHook& hook : *m_hooks) + { + if (function(hook) || hook.IsAllHook()) + hooks.emplace_back(hook); + } + return hooks; +} + +std::vector<CPVRClientMenuHook> CPVRClientMenuHooks::GetChannelHooks() const +{ + return GetHooks([](const CPVRClientMenuHook& hook) + { + return hook.IsChannelHook(); + }); +} + +std::vector<CPVRClientMenuHook> CPVRClientMenuHooks::GetTimerHooks() const +{ + return GetHooks([](const CPVRClientMenuHook& hook) + { + return hook.IsTimerHook(); + }); +} + +std::vector<CPVRClientMenuHook> CPVRClientMenuHooks::GetEpgHooks() const +{ + return GetHooks([](const CPVRClientMenuHook& hook) + { + return hook.IsEpgHook(); + }); +} + +std::vector<CPVRClientMenuHook> CPVRClientMenuHooks::GetRecordingHooks() const +{ + return GetHooks([](const CPVRClientMenuHook& hook) + { + return hook.IsRecordingHook(); + }); +} + +std::vector<CPVRClientMenuHook> CPVRClientMenuHooks::GetDeletedRecordingHooks() const +{ + return GetHooks([](const CPVRClientMenuHook& hook) + { + return hook.IsDeletedRecordingHook(); + }); +} + +std::vector<CPVRClientMenuHook> CPVRClientMenuHooks::GetSettingsHooks() const +{ + return GetHooks([](const CPVRClientMenuHook& hook) + { + return hook.IsSettingsHook(); + }); +} + +} // namespace PVR diff --git a/xbmc/addons/PVRClientMenuHooks.h b/xbmc/addons/PVRClientMenuHooks.h new file mode 100644 index 0000000000..cae91fe1f7 --- /dev/null +++ b/xbmc/addons/PVRClientMenuHooks.h @@ -0,0 +1,71 @@ +/* + * Copyright (C) 2012-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 <functional> +#include <memory> +#include <string> +#include <vector> + +struct PVR_MENUHOOK; + +namespace PVR +{ + class CPVRClientMenuHook + { + public: + CPVRClientMenuHook() = delete; + virtual ~CPVRClientMenuHook() = default; + + CPVRClientMenuHook(const std::string &addonId, const PVR_MENUHOOK &hook); + + bool operator ==(const CPVRClientMenuHook& right) const; + + bool IsAllHook() const; + bool IsChannelHook() const; + bool IsTimerHook() const; + bool IsEpgHook() const; + bool IsRecordingHook() const; + bool IsDeletedRecordingHook() const; + bool IsSettingsHook() const; + + unsigned int GetId() const; + unsigned int GetLabelId() const; + std::string GetLabel() const; + + private: + std::string m_addonId; + std::shared_ptr<PVR_MENUHOOK> m_hook; + }; + + class CPVRClientMenuHooks + { + public: + CPVRClientMenuHooks() = default; + virtual ~CPVRClientMenuHooks() = default; + + explicit CPVRClientMenuHooks(const std::string &addonId) : m_addonId(addonId) {} + + void AddHook(const PVR_MENUHOOK &addonHook); + void Clear(); + + std::vector<CPVRClientMenuHook> GetChannelHooks() const; + std::vector<CPVRClientMenuHook> GetTimerHooks() const; + std::vector<CPVRClientMenuHook> GetEpgHooks() const; + std::vector<CPVRClientMenuHook> GetRecordingHooks() const; + std::vector<CPVRClientMenuHook> GetDeletedRecordingHooks() const; + std::vector<CPVRClientMenuHook> GetSettingsHooks() const; + + private: + std::vector<CPVRClientMenuHook> GetHooks(std::function<bool(const CPVRClientMenuHook& hook)> function) const; + + std::string m_addonId; + std::unique_ptr<std::vector<CPVRClientMenuHook>> m_hooks; + }; +} diff --git a/xbmc/addons/kodi-addon-dev-kit/doxygen/Modules/modules_python.dox b/xbmc/addons/kodi-addon-dev-kit/doxygen/Modules/modules_python.dox index 18d28435ad..eb79674b48 100644 --- a/xbmc/addons/kodi-addon-dev-kit/doxygen/Modules/modules_python.dox +++ b/xbmc/addons/kodi-addon-dev-kit/doxygen/Modules/modules_python.dox @@ -151,6 +151,16 @@ web applications or frameworks for the Python programming language. */ /*! @page python_v17 Python API v17 +\python_removed_function{ + getCaptureState, + http://mirrors.kodi.tv/docs/python-docs/16.x-jarvis/xbmc.html#RenderCapture-getCaptureState, + <b>xbmc.RenderCapture().getCaptureState()</b> function was removed completely. +} +\python_removed_function{ + waitForCaptureStateChangeEvent, + http://mirrors.kodi.tv/docs/python-docs/16.x-jarvis/xbmc.html#RenderCapture-waitForCaptureStateChangeEvent, + <b>xbmc.RenderCapture().waitForCaptureStateChangeEvent()</b> function was removed completely. +} */ /*! @page python_v16 Python API v16 diff --git a/xbmc/interfaces/legacy/RenderCapture.h b/xbmc/interfaces/legacy/RenderCapture.h index bfa467634c..65d3307af5 100644 --- a/xbmc/interfaces/legacy/RenderCapture.h +++ b/xbmc/interfaces/legacy/RenderCapture.h @@ -109,6 +109,8 @@ namespace XBMCAddon /// Get image format /// /// @return Format of captured image: 'BGRA' + /// + /// ///----------------------------------------------------------------------- /// @python_v17 Image will now always be returned in BGRA /// @@ -132,6 +134,8 @@ namespace XBMCAddon /// @return Captured image as a bytearray /// /// @note The size of the image is m_width * m_height * 4 + /// + /// ///----------------------------------------------------------------------- /// @python_v17 Added the option to specify wait time in msec. /// @@ -156,6 +160,8 @@ namespace XBMCAddon /// /// @param width Width capture image should be rendered to /// @param height Height capture image should should be rendered to + /// + /// ///----------------------------------------------------------------------- /// @python_v17 Removed the option to pass **flags** /// @@ -176,24 +182,6 @@ namespace XBMCAddon g_application.GetAppPlayer().RenderCapture(m_captureId, m_width, m_height, CAPTUREFLAG_CONTINUOUS); } -#ifdef DOXYGEN_SHOULD_USE_THIS - /// - /// \ingroup python_xbmc_RenderCapture - /// @brief \python_func{ getCaptureState() } - ///----------------------------------------------------------------------- - /// @python_v17 Removed function completely. - /// -#endif - -#ifdef DOXYGEN_SHOULD_USE_THIS - /// - /// \ingroup python_xbmc_RenderCapture - /// @brief \python_func{ waitForCaptureStateChangeEvent() } - ///----------------------------------------------------------------------- - /// @python_v17 Removed function completely. - /// -#endif - // hide these from swig #ifndef SWIG inline bool GetPixels(unsigned int msec) diff --git a/xbmc/pvr/PVRActionListener.cpp b/xbmc/pvr/PVRActionListener.cpp index ac805d6995..7774a2b897 100644 --- a/xbmc/pvr/PVRActionListener.cpp +++ b/xbmc/pvr/PVRActionListener.cpp @@ -314,7 +314,7 @@ void CPVRActionListener::OnSettingAction(std::shared_ptr<const CSetting> setting } else if (settingId == CSettings::SETTING_PVRCLIENT_MENUHOOK) { - CServiceBroker::GetPVRManager().GUIActions()->ProcessMenuHooks(CFileItemPtr()); + CServiceBroker::GetPVRManager().GUIActions()->ProcessSettingsMenuHooks(); } } diff --git a/xbmc/pvr/PVRContextMenus.cpp b/xbmc/pvr/PVRContextMenus.cpp index cb7ca55ad5..84ae000194 100644 --- a/xbmc/pvr/PVRContextMenus.cpp +++ b/xbmc/pvr/PVRContextMenus.cpp @@ -11,6 +11,7 @@ #include "ContextMenuItem.h" #include "ServiceBroker.h" #include "addons/PVRClient.h" +#include "addons/PVRClientMenuHooks.h" #include "guilib/GUIWindowManager.h" #include "utils/URIUtils.h" @@ -62,7 +63,21 @@ namespace PVR DECL_STATICCONTEXTMENUITEM(UndeleteRecording); DECL_CONTEXTMENUITEM(ToggleTimerState); DECL_STATICCONTEXTMENUITEM(RenameTimer); - DECL_STATICCONTEXTMENUITEM(PVRClientMenuHook); + + class PVRClientMenuHook : public IContextMenuItem + { + public: + PVRClientMenuHook(const CPVRClientMenuHook& hook) : m_hook(hook) {}; + + std::string GetLabel(const CFileItem &item) const override; + bool IsVisible(const CFileItem &item) const override; + bool Execute(const CFileItemPtr &item) const override; + + const CPVRClientMenuHook& GetHook() const { return m_hook; } + + private: + const CPVRClientMenuHook m_hook; + }; CPVRTimerInfoTagPtr GetTimerInfoTagFromItem(const CFileItem &item) { @@ -537,39 +552,36 @@ namespace PVR /////////////////////////////////////////////////////////////////////////////// // PVR Client menu hook - bool PVRClientMenuHook::IsVisible(const CFileItem &item) const + std::string PVRClientMenuHook::GetLabel(const CFileItem &item) const { - const CPVRClientPtr client = CServiceBroker::GetPVRManager().GetClient(item); - if (!client) - return false; - - PVR_MENUHOOK_CAT cat = PVR_MENUHOOK_UNKNOWN; - - if (item.HasPVRChannelInfoTag()) - cat = PVR_MENUHOOK_CHANNEL; - else if (item.HasEPGInfoTag()) - cat = PVR_MENUHOOK_EPG; - else if (item.HasPVRTimerInfoTag() && - !URIUtils::PathEquals(item.GetPath(), CPVRTimersPath::PATH_ADDTIMER)) - cat = PVR_MENUHOOK_TIMER; - else if (item.HasPVRRecordingInfoTag()) - { - const CPVRRecordingPtr recording = item.GetPVRRecordingInfoTag(); - if (recording->IsDeleted()) - cat = PVR_MENUHOOK_DELETED_RECORDING; - else - cat = PVR_MENUHOOK_RECORDING; - } + return m_hook.GetLabel(); + } - if (cat == PVR_MENUHOOK_UNKNOWN) + bool PVRClientMenuHook::IsVisible(const CFileItem &item) const + { + if (m_hook.IsAllHook()) + return !item.m_bIsFolder && !URIUtils::PathEquals(item.GetPath(), CPVRTimersPath::PATH_ADDTIMER); + else if (m_hook.IsEpgHook()) + return item.IsEPG(); + else if (m_hook.IsChannelHook()) + return item.IsPVRChannel(); + else if (m_hook.IsDeletedRecordingHook()) + return item.IsDeletedPVRRecording(); + else if (m_hook.IsRecordingHook()) + return item.IsUsablePVRRecording(); + else if (m_hook.IsTimerHook()) + return item.IsPVRTimer(); + else return false; - - return client->HasMenuHooks(cat); } bool PVRClientMenuHook::Execute(const CFileItemPtr &item) const { - return CServiceBroker::GetPVRManager().GUIActions()->ProcessMenuHooks(item); + const CPVRClientPtr client = CServiceBroker::GetPVRManager().GetClient(*item); + if (!client) + return false; + + return client->CallMenuHook(m_hook, item) == PVR_ERROR_NO_ERROR; } } // namespace CONEXTMENUITEM @@ -602,8 +614,34 @@ namespace PVR std::make_shared<CONTEXTMENUITEM::RenameRecording>(118), /* Rename */ std::make_shared<CONTEXTMENUITEM::DeleteRecording>(), std::make_shared<CONTEXTMENUITEM::UndeleteRecording>(19290), /* Undelete */ - std::make_shared<CONTEXTMENUITEM::PVRClientMenuHook>(19195), /* PVR client specific action */ }; } + void CPVRContextMenuManager::AddMenuHook(const CPVRClientMenuHook& hook) + { + if (hook.IsSettingsHook()) + return; // settings hooks are not handled using context menus + + const auto item = std::make_shared<CONTEXTMENUITEM::PVRClientMenuHook>(hook); + m_items.emplace_back(item); + m_events.Publish(PVRContextMenuEvent(PVRContextMenuEventAction::ADD_ITEM, item)); + } + + void CPVRContextMenuManager::RemoveMenuHook(const CPVRClientMenuHook& hook) + { + if (hook.IsSettingsHook()) + return; // settings hooks are not handled using context menus + + for (auto it = m_items.begin(); it < m_items.end(); ++it) + { + const CONTEXTMENUITEM::PVRClientMenuHook* cmh = dynamic_cast<const CONTEXTMENUITEM::PVRClientMenuHook*>((*it).get()); + if (cmh && cmh->GetHook() == hook) + { + m_events.Publish(PVRContextMenuEvent(PVRContextMenuEventAction::REMOVE_ITEM, *it)); + m_items.erase(it); + return; + } + } + } + } // namespace PVR diff --git a/xbmc/pvr/PVRContextMenus.h b/xbmc/pvr/PVRContextMenus.h index 60014f4090..40af6076a3 100644 --- a/xbmc/pvr/PVRContextMenus.h +++ b/xbmc/pvr/PVRContextMenus.h @@ -11,10 +11,29 @@ #include <memory> #include <vector> +#include "utils/EventStream.h" + class IContextMenuItem; namespace PVR { + enum class PVRContextMenuEventAction + { + ADD_ITEM, + REMOVE_ITEM + }; + + struct PVRContextMenuEvent + { + PVRContextMenuEvent(const PVRContextMenuEventAction& a, const std::shared_ptr<IContextMenuItem>& i) + : action(a), item(i) {} + + PVRContextMenuEventAction action; + std::shared_ptr<IContextMenuItem> item; + }; + + class CPVRClientMenuHook; + class CPVRContextMenuManager { public: @@ -22,6 +41,14 @@ namespace PVR std::vector<std::shared_ptr<IContextMenuItem>> GetMenuItems() const { return m_items; } + void AddMenuHook(const CPVRClientMenuHook& hook); + void RemoveMenuHook(const CPVRClientMenuHook& hook); + + /*! + * @brief Query the events available for CEventStream + */ + CEventStream<PVRContextMenuEvent>& Events() { return m_events; } + private: CPVRContextMenuManager(); CPVRContextMenuManager(const CPVRContextMenuManager&) = delete; @@ -29,6 +56,7 @@ namespace PVR virtual ~CPVRContextMenuManager() = default; std::vector<std::shared_ptr<IContextMenuItem>> m_items; + CEventSource<PVRContextMenuEvent> m_events; }; } // namespace PVR diff --git a/xbmc/pvr/PVRGUIActions.cpp b/xbmc/pvr/PVRGUIActions.cpp index ab2af32fdc..1ab35b086b 100644 --- a/xbmc/pvr/PVRGUIActions.cpp +++ b/xbmc/pvr/PVRGUIActions.cpp @@ -12,6 +12,7 @@ #include "FileItem.h" #include "ServiceBroker.h" #include "addons/PVRClient.h" +#include "addons/PVRClientMenuHooks.h" #include "cores/DataCacheCore.h" #include "dialogs/GUIDialogBusy.h" #include "dialogs/GUIDialogKaiToast.h" @@ -1377,93 +1378,27 @@ namespace PVR return true; } - bool CPVRGUIActions::ProcessMenuHooks(const CFileItemPtr &item) + bool CPVRGUIActions::ProcessSettingsMenuHooks() { - if (!CServiceBroker::GetPVRManager().IsStarted()) - return false; - - int iClientID = -1; - PVR_MENUHOOK_CAT menuCategory = PVR_MENUHOOK_SETTING; + CPVRClientMap clients; + CServiceBroker::GetPVRManager().Clients()->GetCreatedClients(clients); - if (item) + std::vector<std::pair<CPVRClientPtr, CPVRClientMenuHook>> settingsHooks; + for (const auto& client : clients) { - if (item->IsEPG()) - { - if (item->GetEPGInfoTag()->HasChannel()) - { - iClientID = item->GetEPGInfoTag()->Channel()->ClientID(); - menuCategory = PVR_MENUHOOK_EPG; - } - else - return false; - } - else if (item->IsPVRChannel()) - { - iClientID = item->GetPVRChannelInfoTag()->ClientID(); - menuCategory = PVR_MENUHOOK_CHANNEL; - } - else if (item->IsDeletedPVRRecording()) - { - iClientID = item->GetPVRRecordingInfoTag()->m_iClientId; - menuCategory = PVR_MENUHOOK_DELETED_RECORDING; - } - else if (item->IsUsablePVRRecording()) - { - iClientID = item->GetPVRRecordingInfoTag()->m_iClientId; - menuCategory = PVR_MENUHOOK_RECORDING; - } - else if (item->IsPVRTimer()) + for (const auto& hook : client.second->GetMenuHooks()->GetSettingsHooks()) { - iClientID = item->GetPVRTimerInfoTag()->m_iClientId; - menuCategory = PVR_MENUHOOK_TIMER; + settingsHooks.emplace_back(std::make_pair(client.second, hook)); } } - // get client id - if (iClientID < 0 && menuCategory == PVR_MENUHOOK_SETTING) - { - CPVRClientMap clients; - CServiceBroker::GetPVRManager().Clients()->GetCreatedClients(clients); - - if (clients.size() == 1) - { - iClientID = clients.begin()->first; - } - else if (clients.size() > 1) - { - // have user select client - CGUIDialogSelect* pDialog= CServiceBroker::GetGUI()->GetWindowManager().GetWindow<CGUIDialogSelect>(WINDOW_DIALOG_SELECT); - if (!pDialog) - { - CLog::LogF(LOGERROR, "Unable to get WINDOW_DIALOG_SELECT!"); - return false; - } + if (settingsHooks.empty()) + return true; // no settings hooks, no error - pDialog->Reset(); - pDialog->SetHeading(CVariant{19196}); // "PVR client specific actions" + auto selectedHook = settingsHooks.begin(); - for (const auto client : clients) - { - pDialog->Add(client.second->GetBackendName()); - } - - pDialog->Open(); - - int selection = pDialog->GetSelectedItem(); - if (selection >= 0) - { - auto client = clients.begin(); - std::advance(client, selection); - iClientID = client->first; - } - } - } - - if (iClientID < 0) - iClientID = CServiceBroker::GetPVRManager().GetPlayingClientID(); - - CPVRClientPtr client; - if (CServiceBroker::GetPVRManager().Clients()->GetCreatedClient(iClientID, client) && client->HasMenuHooks(menuCategory)) + // if there is only one settings hook, execute it directly, otherwise let the user select + if (settingsHooks.size() > 1) { CGUIDialogSelect* pDialog= CServiceBroker::GetGUI()->GetWindowManager().GetWindow<CGUIDialogSelect>(WINDOW_DIALOG_SELECT); if (!pDialog) @@ -1475,34 +1410,23 @@ namespace PVR pDialog->Reset(); pDialog->SetHeading(CVariant{19196}); // "PVR client specific actions" - PVR_MENUHOOKS& hooks = client->GetMenuHooks(); - std::vector<int> hookIDs; - unsigned int i = 0; - - for (const auto& hook : hooks) + for (const auto& hook : settingsHooks) { - if (hook.category == menuCategory || hook.category == PVR_MENUHOOK_ALL) - { - pDialog->Add(g_localizeStrings.GetAddonString(client->ID(), hook.iLocalizedStringId)); - hookIDs.emplace_back(i); - } - ++i; + if (clients.size() == 1) + pDialog->Add(hook.second.GetLabel()); + else + pDialog->Add(hook.first->GetBackendName() + ": " + hook.second.GetLabel()); } - int selection = 0; - if (!hookIDs.empty()) - { - pDialog->Open(); - selection = pDialog->GetSelectedItem(); - } + pDialog->Open(); - if (selection >= 0) - client->CallMenuHook(hooks.at(hookIDs.at(selection)), item); - else - return false; - } + int selection = pDialog->GetSelectedItem(); + if (selection < 0) + return true; // cancelled - return true; + std::advance(selectedHook, selection); + } + return selectedHook->first->CallMenuHook(selectedHook->second, CFileItemPtr()) == PVR_ERROR_NO_ERROR; } bool CPVRGUIActions::ResetPVRDatabase(bool bResetEPGOnly) diff --git a/xbmc/pvr/PVRGUIActions.h b/xbmc/pvr/PVRGUIActions.h index 33b53f3f30..52e34979a9 100644 --- a/xbmc/pvr/PVRGUIActions.h +++ b/xbmc/pvr/PVRGUIActions.h @@ -306,11 +306,10 @@ namespace PVR bool IsRunningChannelScan() const { return m_bChannelScanRunning; } /*! - * @brief Open selection and progress PVR actions. - * @param item The selected file item for which the hook was called. + * @brief Select and invoke client-specific settings actions * @return true on success, false otherwise. */ - bool ProcessMenuHooks(const CFileItemPtr &item); + bool ProcessSettingsMenuHooks(); /*! * @brief Reset the TV database to it's initial state and delete all the data. diff --git a/xbmc/pvr/addons/PVRClients.h b/xbmc/pvr/addons/PVRClients.h index 8c461c777e..9cc52c9f40 100644 --- a/xbmc/pvr/addons/PVRClients.h +++ b/xbmc/pvr/addons/PVRClients.h @@ -160,6 +160,8 @@ namespace PVR */ int EnabledClientAmount(void) const; + //@} + /*! @name general methods */ //@{ diff --git a/xbmc/pvr/recordings/PVRRecording.cpp b/xbmc/pvr/recordings/PVRRecording.cpp index 615d49f8cf..871da80593 100644 --- a/xbmc/pvr/recordings/PVRRecording.cpp +++ b/xbmc/pvr/recordings/PVRRecording.cpp @@ -153,7 +153,10 @@ bool CPVRRecording::operator ==(const CPVRRecording& right) const m_bIsDeleted == right.m_bIsDeleted && m_iEpgEventId == right.m_iEpgEventId && m_iChannelUid == right.m_iChannelUid && - m_bRadio == right.m_bRadio); + m_bRadio == right.m_bRadio && + m_genre == right.m_genre && + m_iGenreType == right.m_iGenreType && + m_iGenreSubType == right.m_iGenreSubType); } bool CPVRRecording::operator !=(const CPVRRecording& right) const @@ -176,6 +179,7 @@ void CPVRRecording::Serialize(CVariant& value) const value["epgeventid"] = m_iEpgEventId; value["channeluid"] = m_iChannelUid; value["radio"] = m_bRadio; + value["genre"] = m_genre; if (!value.isMember("art")) value["art"] = CVariant(CVariant::VariantTypeObject); @@ -374,6 +378,17 @@ void CPVRRecording::Update(const CPVRRecording &tag) CVideoInfoTag::SetResumePoint(tag.GetLocalResumePoint()); SetDuration(tag.GetDuration()); + if (m_iGenreType == EPG_GENRE_USE_STRING) + { + /* No type/subtype. Use the provided description */ + m_genre = tag.m_genre; + } + else + { + /* Determine genre description by type/subtype */ + m_genre = StringUtils::Split(CPVREpg::ConvertGenreIdToString(tag.m_iGenreType, tag.m_iGenreSubType), CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->m_videoItemSeparator); + } + //Old Method of identifying TV show title and subtitle using m_strDirectory and strPlotOutline (deprecated) std::string strShow = StringUtils::Format("%s - ", g_localizeStrings.Get(20364).c_str()); if (StringUtils::StartsWithNoCase(m_strPlotOutline, strShow)) @@ -475,6 +490,9 @@ bool CPVRRecording::IsInProgress() const void CPVRRecording::SetGenre(int iGenreType, int iGenreSubType, const std::string &strGenre) { + m_iGenreType = iGenreType; + m_iGenreSubType = iGenreSubType; + if ((iGenreType == EPG_GENRE_USE_STRING) && !strGenre.empty()) { /* Type and sub type are not given. Use the provided genre description if available. */ @@ -486,3 +504,8 @@ void CPVRRecording::SetGenre(int iGenreType, int iGenreSubType, const std::strin m_genre = StringUtils::Split(CPVREpg::ConvertGenreIdToString(iGenreType, iGenreSubType), CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->m_videoItemSeparator); } } + +const std::string CPVRRecording::GetGenresLabel() const +{ + return StringUtils::Join(m_genre, CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->m_videoItemSeparator); +} diff --git a/xbmc/pvr/recordings/PVRRecording.h b/xbmc/pvr/recordings/PVRRecording.h index eead37e021..90ae6f521d 100644 --- a/xbmc/pvr/recordings/PVRRecording.h +++ b/xbmc/pvr/recordings/PVRRecording.h @@ -287,6 +287,30 @@ namespace PVR */ void SetGenre(int iGenreType, int iGenreSubType, const std::string &strGenre); + /*! + * @brief Get the genre type ID of this event. + * @return The genre type ID. + */ + int GenreType(void) const { return m_iGenreType; } + + /*! + * @brief Get the genre subtype ID of this event. + * @return The genre subtype ID. + */ + int GenreSubType(void) const { return m_iGenreSubType; } + + /*! + * @brief Get the genre as human readable string. + * @return The genre. + */ + const std::vector<std::string> Genre(void) const { return m_genre; } + + /*! + * @brief Get the genre(s) of this event as formatted string. + * @return The genres label. + */ + const std::string GetGenresLabel() const; + private: CDateTime m_recordingTime; /*!< start time of the recording */ bool m_bGotMetaData; @@ -294,6 +318,8 @@ namespace PVR unsigned int m_iEpgEventId; /*!< epg broadcast id associated with this recording */ int m_iChannelUid; /*!< channel uid associated with this recording */ bool m_bRadio; /*!< radio or tv recording */ + int m_iGenreType = 0; /*!< genre type */ + int m_iGenreSubType = 0; /*!< genre subtype */ void UpdatePath(void); }; diff --git a/xbmc/utils/CryptThreading.cpp b/xbmc/utils/CryptThreading.cpp index 0343c97190..c0a2c3e02c 100644 --- a/xbmc/utils/CryptThreading.cpp +++ b/xbmc/utils/CryptThreading.cpp @@ -55,6 +55,7 @@ CryptThreadingInitializer::~CryptThreadingInitializer() { #if KODI_OPENSSL_NEEDS_LOCK_CALLBACK CSingleLock l(m_locksLock); + CRYPTO_set_id_callback(nullptr); CRYPTO_set_locking_callback(nullptr); m_locks.clear(); #endif |