aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKai Sommerfeld <3226626+ksooo@users.noreply.github.com>2023-06-19 22:30:24 +0200
committerGitHub <noreply@github.com>2023-06-19 22:30:24 +0200
commit44c795ebf53b1840f16f7ed60a70d9cad7c5ba08 (patch)
tree810fb302dfb7ad6720022f240fe9412cf3ae52b5
parentbccecd2bf69a56284187990b0a6a0dfe85e25f4b (diff)
parentabae60b0c3f593d34af4905e3b66ed450a772a58 (diff)
Merge pull request #23393 from ksooo/pvr-dynamic-timer-types
[PVR] Dynamic timer types: Update timer types from client whenever a …
-rw-r--r--xbmc/pvr/addons/PVRClient.cpp208
-rw-r--r--xbmc/pvr/addons/PVRClient.h11
-rw-r--r--xbmc/pvr/addons/PVRClients.cpp34
-rw-r--r--xbmc/pvr/addons/PVRClients.h14
-rw-r--r--xbmc/pvr/timers/PVRTimerType.cpp26
-rw-r--r--xbmc/pvr/timers/PVRTimerType.h6
-rw-r--r--xbmc/pvr/timers/PVRTimers.cpp5
7 files changed, 192 insertions, 112 deletions
diff --git a/xbmc/pvr/addons/PVRClient.cpp b/xbmc/pvr/addons/PVRClient.cpp
index 642fdbbcd4..9c553d7aa8 100644
--- a/xbmc/pvr/addons/PVRClient.cpp
+++ b/xbmc/pvr/addons/PVRClient.cpp
@@ -305,7 +305,6 @@ bool CPVRClient::GetAddonProperties()
return false;
PVR_ADDON_CAPABILITIES addonCapabilities = {};
- std::vector<std::shared_ptr<CPVRTimerType>> timerTypes;
/* get the capabilities */
PVR_ERROR retVal = DoAddonCall(
@@ -318,96 +317,8 @@ bool CPVRClient::GetAddonProperties()
if (retVal != PVR_ERROR_NO_ERROR)
return false;
- /* timer types */
- retVal = DoAddonCall(
- __func__,
- [this, &addonCapabilities, &timerTypes](const AddonInstance* addon) {
- std::unique_ptr<PVR_TIMER_TYPE[]> types_array(
- new PVR_TIMER_TYPE[PVR_ADDON_TIMERTYPE_ARRAY_SIZE]);
- int size = PVR_ADDON_TIMERTYPE_ARRAY_SIZE;
-
- PVR_ERROR retval = addon->toAddon->GetTimerTypes(addon, types_array.get(), &size);
-
- if (retval == PVR_ERROR_NOT_IMPLEMENTED)
- {
- // begin compat section
- CLog::LogF(LOGWARNING,
- "Add-on {} does not support timer types. It will work, but not benefit from "
- "the timer features introduced with PVR Addon API 2.0.0",
- GetFriendlyName());
-
- // Create standard timer types (mostly) matching the timer functionality available in Isengard.
- // This is for migration only and does not make changes to the addons obsolete. Addons should
- // work and benefit from some UI changes (e.g. some of the timer settings dialog enhancements),
- // but all old problems/bugs due to static attributes and values will remain the same as in
- // Isengard. Also, new features (like epg search) are not available to addons automatically.
- // This code can be removed once all addons actually support the respective PVR Addon API version.
-
- size = 0;
- // manual one time
- memset(&types_array[size], 0, sizeof(types_array[size]));
- types_array[size].iId = size + 1;
- types_array[size].iAttributes =
- PVR_TIMER_TYPE_IS_MANUAL | PVR_TIMER_TYPE_SUPPORTS_ENABLE_DISABLE |
- PVR_TIMER_TYPE_SUPPORTS_CHANNELS | PVR_TIMER_TYPE_SUPPORTS_START_TIME |
- PVR_TIMER_TYPE_SUPPORTS_END_TIME | PVR_TIMER_TYPE_SUPPORTS_PRIORITY |
- PVR_TIMER_TYPE_SUPPORTS_LIFETIME | PVR_TIMER_TYPE_SUPPORTS_RECORDING_FOLDERS;
- ++size;
-
- // manual timer rule
- memset(&types_array[size], 0, sizeof(types_array[size]));
- types_array[size].iId = size + 1;
- types_array[size].iAttributes =
- PVR_TIMER_TYPE_IS_MANUAL | PVR_TIMER_TYPE_IS_REPEATING |
- PVR_TIMER_TYPE_SUPPORTS_ENABLE_DISABLE | PVR_TIMER_TYPE_SUPPORTS_CHANNELS |
- PVR_TIMER_TYPE_SUPPORTS_START_TIME | PVR_TIMER_TYPE_SUPPORTS_END_TIME |
- PVR_TIMER_TYPE_SUPPORTS_PRIORITY | PVR_TIMER_TYPE_SUPPORTS_LIFETIME |
- PVR_TIMER_TYPE_SUPPORTS_FIRST_DAY | PVR_TIMER_TYPE_SUPPORTS_WEEKDAYS |
- PVR_TIMER_TYPE_SUPPORTS_RECORDING_FOLDERS;
- ++size;
-
- if (addonCapabilities.bSupportsEPG)
- {
- // One-shot epg-based
- memset(&types_array[size], 0, sizeof(types_array[size]));
- types_array[size].iId = size + 1;
- types_array[size].iAttributes =
- PVR_TIMER_TYPE_SUPPORTS_ENABLE_DISABLE | PVR_TIMER_TYPE_REQUIRES_EPG_TAG_ON_CREATE |
- PVR_TIMER_TYPE_SUPPORTS_CHANNELS | PVR_TIMER_TYPE_SUPPORTS_START_TIME |
- PVR_TIMER_TYPE_SUPPORTS_END_TIME | PVR_TIMER_TYPE_SUPPORTS_PRIORITY |
- PVR_TIMER_TYPE_SUPPORTS_LIFETIME | PVR_TIMER_TYPE_SUPPORTS_RECORDING_FOLDERS;
- ++size;
- }
-
- retval = PVR_ERROR_NO_ERROR;
- // end compat section
- }
-
- if (retval == PVR_ERROR_NO_ERROR)
- {
- timerTypes.reserve(size);
- for (int i = 0; i < size; ++i)
- {
- if (types_array[i].iId == PVR_TIMER_TYPE_NONE)
- {
- CLog::LogF(LOGERROR, "Invalid timer type supplied by add-on {}.", GetID());
- continue;
- }
- timerTypes.emplace_back(
- std::shared_ptr<CPVRTimerType>(new CPVRTimerType(types_array[i], m_iClientId)));
- }
- }
- return retval;
- },
- addonCapabilities.bSupportsTimers, false);
-
- if (retVal == PVR_ERROR_NOT_IMPLEMENTED)
- retVal = PVR_ERROR_NO_ERROR; // timer support is optional.
-
- /* update the members */
std::unique_lock<CCriticalSection> lock(m_critSection);
m_clientCapabilities = addonCapabilities;
- m_timertypes = timerTypes;
return retVal == PVR_ERROR_NO_ERROR;
}
@@ -1078,11 +989,124 @@ PVR_ERROR CPVRClient::UpdateTimer(const CPVRTimerInfoTag& timer)
m_clientCapabilities.SupportsTimers());
}
-PVR_ERROR CPVRClient::GetTimerTypes(std::vector<std::shared_ptr<CPVRTimerType>>& results) const
+const std::vector<std::shared_ptr<CPVRTimerType>>& CPVRClient::GetTimerTypes() const
{
std::unique_lock<CCriticalSection> lock(m_critSection);
- results = m_timertypes;
- return PVR_ERROR_NO_ERROR;
+ return m_timertypes;
+}
+
+PVR_ERROR CPVRClient::UpdateTimerTypes()
+{
+ std::vector<std::shared_ptr<CPVRTimerType>> timerTypes;
+
+ PVR_ERROR retVal = DoAddonCall(
+ __func__,
+ [this, &timerTypes](const AddonInstance* addon) {
+ std::unique_ptr<PVR_TIMER_TYPE[]> types_array(
+ new PVR_TIMER_TYPE[PVR_ADDON_TIMERTYPE_ARRAY_SIZE]);
+ int size = PVR_ADDON_TIMERTYPE_ARRAY_SIZE;
+
+ PVR_ERROR retval = addon->toAddon->GetTimerTypes(addon, types_array.get(), &size);
+
+ if (retval == PVR_ERROR_NOT_IMPLEMENTED)
+ {
+ // begin compat section
+ CLog::LogF(LOGWARNING,
+ "Add-on {} does not support timer types. It will work, but not benefit from "
+ "the timer features introduced with PVR Addon API 2.0.0",
+ GetFriendlyName());
+
+ // Create standard timer types (mostly) matching the timer functionality available in Isengard.
+ // This is for migration only and does not make changes to the addons obsolete. Addons should
+ // work and benefit from some UI changes (e.g. some of the timer settings dialog enhancements),
+ // but all old problems/bugs due to static attributes and values will remain the same as in
+ // Isengard. Also, new features (like epg search) are not available to addons automatically.
+ // This code can be removed once all addons actually support the respective PVR Addon API version.
+
+ size = 0;
+ // manual one time
+ memset(&types_array[size], 0, sizeof(types_array[size]));
+ types_array[size].iId = size + 1;
+ types_array[size].iAttributes =
+ PVR_TIMER_TYPE_IS_MANUAL | PVR_TIMER_TYPE_SUPPORTS_ENABLE_DISABLE |
+ PVR_TIMER_TYPE_SUPPORTS_CHANNELS | PVR_TIMER_TYPE_SUPPORTS_START_TIME |
+ PVR_TIMER_TYPE_SUPPORTS_END_TIME | PVR_TIMER_TYPE_SUPPORTS_PRIORITY |
+ PVR_TIMER_TYPE_SUPPORTS_LIFETIME | PVR_TIMER_TYPE_SUPPORTS_RECORDING_FOLDERS;
+ ++size;
+
+ // manual timer rule
+ memset(&types_array[size], 0, sizeof(types_array[size]));
+ types_array[size].iId = size + 1;
+ types_array[size].iAttributes =
+ PVR_TIMER_TYPE_IS_MANUAL | PVR_TIMER_TYPE_IS_REPEATING |
+ PVR_TIMER_TYPE_SUPPORTS_ENABLE_DISABLE | PVR_TIMER_TYPE_SUPPORTS_CHANNELS |
+ PVR_TIMER_TYPE_SUPPORTS_START_TIME | PVR_TIMER_TYPE_SUPPORTS_END_TIME |
+ PVR_TIMER_TYPE_SUPPORTS_PRIORITY | PVR_TIMER_TYPE_SUPPORTS_LIFETIME |
+ PVR_TIMER_TYPE_SUPPORTS_FIRST_DAY | PVR_TIMER_TYPE_SUPPORTS_WEEKDAYS |
+ PVR_TIMER_TYPE_SUPPORTS_RECORDING_FOLDERS;
+ ++size;
+
+ if (m_clientCapabilities.SupportsEPG())
+ {
+ // One-shot epg-based
+ memset(&types_array[size], 0, sizeof(types_array[size]));
+ types_array[size].iId = size + 1;
+ types_array[size].iAttributes =
+ PVR_TIMER_TYPE_SUPPORTS_ENABLE_DISABLE | PVR_TIMER_TYPE_REQUIRES_EPG_TAG_ON_CREATE |
+ PVR_TIMER_TYPE_SUPPORTS_CHANNELS | PVR_TIMER_TYPE_SUPPORTS_START_TIME |
+ PVR_TIMER_TYPE_SUPPORTS_END_TIME | PVR_TIMER_TYPE_SUPPORTS_PRIORITY |
+ PVR_TIMER_TYPE_SUPPORTS_LIFETIME | PVR_TIMER_TYPE_SUPPORTS_RECORDING_FOLDERS;
+ ++size;
+ }
+
+ retval = PVR_ERROR_NO_ERROR;
+ // end compat section
+ }
+
+ if (retval == PVR_ERROR_NO_ERROR)
+ {
+ timerTypes.reserve(size);
+ for (int i = 0; i < size; ++i)
+ {
+ if (types_array[i].iId == PVR_TIMER_TYPE_NONE)
+ {
+ CLog::LogF(LOGERROR, "Invalid timer type supplied by add-on {}.", GetID());
+ continue;
+ }
+ timerTypes.emplace_back(std::make_shared<CPVRTimerType>(types_array[i], m_iClientId));
+ }
+ }
+ return retval;
+ },
+ m_clientCapabilities.SupportsTimers(), false);
+
+ if (retVal == PVR_ERROR_NO_ERROR)
+ {
+ std::vector<std::shared_ptr<CPVRTimerType>> newTimerTypes;
+ newTimerTypes.reserve(timerTypes.size());
+
+ std::unique_lock<CCriticalSection> lock(m_critSection);
+
+ for (const auto& type : timerTypes)
+ {
+ const auto it = std::find_if(m_timertypes.cbegin(), m_timertypes.cend(),
+ [&type](const std::shared_ptr<CPVRTimerType>& entry) {
+ return entry->GetTypeId() == type->GetTypeId();
+ });
+ if (it == m_timertypes.cend())
+ {
+ newTimerTypes.emplace_back(type);
+ }
+ else
+ {
+ (*it)->Update(*type);
+ newTimerTypes.emplace_back(*it);
+ }
+ }
+
+ m_timertypes = newTimerTypes;
+ }
+ return retVal;
}
PVR_ERROR CPVRClient::GetStreamReadChunkSize(int& iChunkSize)
diff --git a/xbmc/pvr/addons/PVRClient.h b/xbmc/pvr/addons/PVRClient.h
index b088a579ec..c045431d25 100644
--- a/xbmc/pvr/addons/PVRClient.h
+++ b/xbmc/pvr/addons/PVRClient.h
@@ -489,11 +489,16 @@ public:
PVR_ERROR UpdateTimer(const CPVRTimerInfoTag& timer);
/*!
- * @brief Get all timer types supported by the backend.
- * @param results The container to store the result in.
+ * @brief Update all timer types supported by the backend.
* @return PVR_ERROR_NO_ERROR if the list has been fetched successfully.
*/
- PVR_ERROR GetTimerTypes(std::vector<std::shared_ptr<CPVRTimerType>>& results) const;
+ PVR_ERROR UpdateTimerTypes();
+
+ /*!
+ * @brief Get the timer types supported by the backend, without updating them from the backend.
+ * @return the types.
+ */
+ const std::vector<std::shared_ptr<CPVRTimerType>>& GetTimerTypes() const;
//@}
/** @name PVR live stream methods */
diff --git a/xbmc/pvr/addons/PVRClients.cpp b/xbmc/pvr/addons/PVRClients.cpp
index bceda373a5..db4aee11d5 100644
--- a/xbmc/pvr/addons/PVRClients.cpp
+++ b/xbmc/pvr/addons/PVRClients.cpp
@@ -600,15 +600,31 @@ bool CPVRClients::GetTimers(const std::vector<std::shared_ptr<CPVRClient>>& clie
failedClients) == PVR_ERROR_NO_ERROR;
}
-PVR_ERROR CPVRClients::GetTimerTypes(std::vector<std::shared_ptr<CPVRTimerType>>& results) const
-{
- return ForCreatedClients(__FUNCTION__, [&results](const std::shared_ptr<CPVRClient>& client) {
- std::vector<std::shared_ptr<CPVRTimerType>> types;
- PVR_ERROR ret = client->GetTimerTypes(types);
- if (ret == PVR_ERROR_NO_ERROR)
- results.insert(results.end(), types.begin(), types.end());
- return ret;
- });
+PVR_ERROR CPVRClients::UpdateTimerTypes(const std::vector<std::shared_ptr<CPVRClient>>& clients,
+ std::vector<int>& failedClients)
+{
+ return ForClients(
+ __FUNCTION__, clients,
+ [](const std::shared_ptr<CPVRClient>& client) { return client->UpdateTimerTypes(); },
+ failedClients);
+}
+
+const std::vector<std::shared_ptr<CPVRTimerType>> CPVRClients::GetTimerTypes() const
+{
+ std::vector<std::shared_ptr<CPVRTimerType>> types;
+
+ std::unique_lock<CCriticalSection> lock(m_critSection);
+ for (const auto& entry : m_clientMap)
+ {
+ const auto& client = entry.second;
+ if (client->ReadyToUse() && !client->IgnoreClient())
+ {
+ const auto& clientTypes = client->GetTimerTypes();
+ types.insert(types.end(), clientTypes.begin(), clientTypes.end());
+ }
+ }
+
+ return types;
}
PVR_ERROR CPVRClients::GetRecordings(const std::vector<std::shared_ptr<CPVRClient>>& clients,
diff --git a/xbmc/pvr/addons/PVRClients.h b/xbmc/pvr/addons/PVRClients.h
index 32248fa322..23ee19eade 100644
--- a/xbmc/pvr/addons/PVRClients.h
+++ b/xbmc/pvr/addons/PVRClients.h
@@ -212,11 +212,19 @@ struct SBackend
std::vector<int>& failedClients);
/*!
- * @brief Get all supported timer types.
- * @param results The container to store the result in.
+ * @brief Update all timer types from the given clients
+ * @param clients The clients to fetch data from. Leave empty to fetch data from all created clients.
+ * @param failedClients in case of errors will contain the ids of the clients for which the timer types could not be obtained.
* @return PVR_ERROR_NO_ERROR if the operation succeeded, the respective PVR_ERROR value otherwise.
*/
- PVR_ERROR GetTimerTypes(std::vector<std::shared_ptr<CPVRTimerType>>& results) const;
+ PVR_ERROR UpdateTimerTypes(const std::vector<std::shared_ptr<CPVRClient>>& clients,
+ std::vector<int>& failedClients);
+
+ /*!
+ * @brief Get all timer types supported by the backends, without updating them from the backends.
+ * @return the types.
+ */
+ const std::vector<std::shared_ptr<CPVRTimerType>> GetTimerTypes() const;
//@}
diff --git a/xbmc/pvr/timers/PVRTimerType.cpp b/xbmc/pvr/timers/PVRTimerType.cpp
index db20fa10af..fd4cc0eba6 100644
--- a/xbmc/pvr/timers/PVRTimerType.cpp
+++ b/xbmc/pvr/timers/PVRTimerType.cpp
@@ -27,8 +27,8 @@ using namespace PVR;
const std::vector<std::shared_ptr<CPVRTimerType>> CPVRTimerType::GetAllTypes()
{
- std::vector<std::shared_ptr<CPVRTimerType>> allTypes;
- CServiceBroker::GetPVRManager().Clients()->GetTimerTypes(allTypes);
+ std::vector<std::shared_ptr<CPVRTimerType>> allTypes =
+ CServiceBroker::GetPVRManager().Clients()->GetTimerTypes();
// Add local reminder timer types. Local reminders are always available.
int iTypeId = PVR_TIMER_TYPE_NONE;
@@ -107,8 +107,8 @@ const std::shared_ptr<CPVRTimerType> CPVRTimerType::GetFirstAvailableType(const
{
if (client)
{
- std::vector<std::shared_ptr<CPVRTimerType>> types;
- if (client->GetTimerTypes(types) == PVR_ERROR_NO_ERROR && !types.empty())
+ const std::vector<std::shared_ptr<CPVRTimerType>>& types = client->GetTimerTypes();
+ if (!types.empty())
{
return *(types.begin());
}
@@ -214,6 +214,24 @@ bool CPVRTimerType::operator !=(const CPVRTimerType& right) const
return !(*this == right);
}
+void CPVRTimerType::Update(const CPVRTimerType& type)
+{
+ m_iClientId = type.m_iClientId;
+ m_iTypeId = type.m_iTypeId;
+ m_iAttributes = type.m_iAttributes;
+ m_strDescription = type.m_strDescription;
+ m_priorityValues = type.m_priorityValues;
+ m_iPriorityDefault = type.m_iPriorityDefault;
+ m_lifetimeValues = type.m_lifetimeValues;
+ m_iLifetimeDefault = type.m_iLifetimeDefault;
+ m_maxRecordingsValues = type.m_maxRecordingsValues;
+ m_iMaxRecordingsDefault = type.m_iMaxRecordingsDefault;
+ m_preventDupEpisodesValues = type.m_preventDupEpisodesValues;
+ m_iPreventDupEpisodesDefault = type.m_iPreventDupEpisodesDefault;
+ m_recordingGroupValues = type.m_recordingGroupValues;
+ m_iRecordingGroupDefault = type.m_iRecordingGroupDefault;
+}
+
void CPVRTimerType::InitDescription()
{
// if no description was given, compile it
diff --git a/xbmc/pvr/timers/PVRTimerType.h b/xbmc/pvr/timers/PVRTimerType.h
index 8ee9e225d6..34405a7e1b 100644
--- a/xbmc/pvr/timers/PVRTimerType.h
+++ b/xbmc/pvr/timers/PVRTimerType.h
@@ -75,6 +75,12 @@ namespace PVR
bool operator !=(const CPVRTimerType& right) const;
/*!
+ * @brief Update the data of this instance with the data given by another type instance.
+ * @param type The instance containing the updated data.
+ */
+ void Update(const CPVRTimerType& type);
+
+ /*!
* @brief Get the PVR client id for this type.
* @return The PVR client id.
*/
diff --git a/xbmc/pvr/timers/PVRTimers.cpp b/xbmc/pvr/timers/PVRTimers.cpp
index 760bb192f7..9537a52016 100644
--- a/xbmc/pvr/timers/PVRTimers.cpp
+++ b/xbmc/pvr/timers/PVRTimers.cpp
@@ -157,9 +157,12 @@ bool CPVRTimers::UpdateFromClients(const std::vector<std::shared_ptr<CPVRClient>
m_bIsUpdating = true;
}
+ CLog::LogFC(LOGDEBUG, LOGPVR, "Updating timer types");
+ std::vector<int> failedClients;
+ CServiceBroker::GetPVRManager().Clients()->UpdateTimerTypes(clients, failedClients);
+
CLog::LogFC(LOGDEBUG, LOGPVR, "Updating timers");
CPVRTimersContainer newTimerList;
- std::vector<int> failedClients;
CServiceBroker::GetPVRManager().Clients()->GetTimers(clients, &newTimerList, failedClients);
return UpdateEntries(newTimerList, failedClients);
}