From 3c29f0d8fe45c08aed4b875da7bc95b03bac8068 Mon Sep 17 00:00:00 2001 From: Martin Vallevand Date: Fri, 15 Nov 2024 11:00:38 -0500 Subject: [PATCH] Test channel list update Reload changed channel lists during EPG updates --- src/Channels.cpp | 52 ++++++++++++++++++++++++++++++++++++++- src/Channels.h | 3 +++ src/EPG.cpp | 2 ++ src/Timers.cpp | 1 + src/pvrclient-nextpvr.cpp | 8 ++++++ 5 files changed, 65 insertions(+), 1 deletion(-) diff --git a/src/Channels.cpp b/src/Channels.cpp index 95baf002..8ceb5e9d 100644 --- a/src/Channels.cpp +++ b/src/Channels.cpp @@ -9,6 +9,7 @@ #include "utilities/XMLUtils.h" #include "pvrclient-nextpvr.h" +#include #include #include "zlib.h" @@ -26,6 +27,7 @@ Channels::Channels(const std::shared_ptr& settings, Request& r int Channels::GetNumChannels() { // Kodi polls this while recordings are open avoid calls to backend + std::lock_guard lock(m_channelMutex); int channelCount = m_channelDetails.size(); if (channelCount == 0) { @@ -91,6 +93,7 @@ PVR_ERROR Channels::GetChannels(bool radio, kodi::addon::PVRChannelsResultSet& r return PVR_ERROR_NO_ERROR; PVR_ERROR returnValue = PVR_ERROR_NO_ERROR; std::string stream; + std::lock_guard lock(m_channelMutex); std::map>::iterator itr = m_channelDetails.begin(); while (itr != m_channelDetails.end()) { @@ -168,6 +171,44 @@ PVR_ERROR Channels::GetChannels(bool radio, kodi::addon::PVRChannelsResultSet& r return returnValue; } +bool Channels::ResetChannelList(time_t updateTime) +{ + // normally GetChannels loads the internal channel list; + // force update in case GetChannels is not called before EPG triggers + std::string checksum = m_checksumChannelList; + CacheAllChannels(updateTime); + if (checksum != m_checksumChannelList) + { + std::lock_guard lock(m_channelMutex); + m_channelDetails.clear(); + tinyxml2::XMLDocument doc; + if (ReadCachedChannelList(doc) == tinyxml2::XML_SUCCESS) + { + tinyxml2::XMLNode* channelsNode = doc.RootElement()->FirstChildElement("channels"); + tinyxml2::XMLNode* pChannelNode; + for (pChannelNode = channelsNode->FirstChildElement("channel"); pChannelNode; pChannelNode = pChannelNode->NextSiblingElement()) + { + std::string buffer; + bool isRadio = false; + XMLUtils::GetString(pChannelNode, "type", buffer); + if (buffer == "0xa") + { + if (!m_settings->m_showRadio) + continue; + isRadio = true; + } + std::string epg; + if (XMLUtils::GetString(pChannelNode, "epg", epg)) + m_channelDetails[XMLUtils::GetUIntValue(pChannelNode, "id")] = std::make_pair(epg == "None", isRadio); + else + m_channelDetails[XMLUtils::GetUIntValue(pChannelNode, "id")] = std::make_pair(false, isRadio); + } + return true; + } + } + return false; +} + /************************************************************/ /** Channel group handling **/ @@ -182,6 +223,7 @@ PVR_ERROR Channels::GetChannelGroupsAmount(int& amount) PVR_RECORDING_CHANNEL_TYPE Channels::GetChannelType(unsigned int uid) { // when uid is invalid we assume TV because Kodi will + std::lock_guard lock(m_channelMutex); if (m_channelDetails.count(uid) > 0 && m_channelDetails[uid].second == true) return PVR_RECORDING_CHANNEL_TYPE_RADIO; @@ -192,7 +234,7 @@ PVR_ERROR Channels::GetChannelGroups(bool radio, kodi::addon::PVRChannelGroupsRe { if (radio && !m_settings->m_showRadio) return PVR_ERROR_NO_ERROR; - + std::lock_guard lock(m_channelMutex); PVR_ERROR returnValue = PVR_ERROR_NO_ERROR; int priority = 1; @@ -299,6 +341,7 @@ PVR_ERROR Channels::GetChannelGroupMembers(const kodi::addon::PVRChannelGroup& g if (retCode == tinyxml2::XML_SUCCESS) { + std::lock_guard lock(m_channelMutex); tinyxml2::XMLNode* channelsNode = doc.RootElement()->FirstChildElement("channels"); tinyxml2::XMLNode* pChannelNode; for (pChannelNode = channelsNode->FirstChildElement("channel"); pChannelNode; pChannelNode = pChannelNode->NextSiblingElement()) @@ -399,6 +442,12 @@ bool Channels::CacheAllChannels(time_t updateTime) { gz_file = gzopen(kodi::vfs::TranslateSpecialProtocol(filename).c_str(), "rb"); gzread(gz_file, (void*)&header, sizeof(header)); + if (m_checksumChannelList.empty()) + { + response.resize(header.size / sizeof(char)); + gzread(gz_file, (void*)response.data(), header.size); + m_checksumChannelList = kodi::GetMD5(response); + } gzclose(gz_file); if (updateTime == header.update) { @@ -407,6 +456,7 @@ bool Channels::CacheAllChannels(time_t updateTime) } if (m_request.DoRequest("/service?method=channel.list&extras=true", response) == HTTP_OK) { + m_checksumChannelList = kodi::GetMD5(response); gz_file = gzopen(kodi::vfs::TranslateSpecialProtocol(filename).c_str(), "wb"); header.size = sizeof(char) * response.size(); header.update = updateTime - m_settings->m_serverTimeOffset; diff --git a/src/Channels.h b/src/Channels.h index 06054de0..25285820 100644 --- a/src/Channels.h +++ b/src/Channels.h @@ -28,6 +28,7 @@ namespace NextPVR bool CacheAllChannels(time_t updateTime); PVR_ERROR GetChannels(bool radio, kodi::addon::PVRChannelsResultSet& results); + bool ResetChannelList(time_t updateTime); /* Channel group handling */ PVR_ERROR GetChannelGroupsAmount(int& amount); PVR_ERROR GetChannelGroups(bool radio, kodi::addon::PVRChannelGroupsResultSet& results); @@ -43,6 +44,7 @@ namespace NextPVR std::map> m_channelDetails; std::unordered_set m_tvGroups; std::unordered_set m_radioGroups; + mutable std::recursive_mutex m_channelMutex; private: Channels() = default; @@ -54,5 +56,6 @@ namespace NextPVR const std::shared_ptr m_settings; Request& m_request; tinyxml2::XMLError ReadCachedChannelList(tinyxml2::XMLDocument& doc); + std::string m_checksumChannelList; }; } // namespace NextPVR diff --git a/src/EPG.cpp b/src/EPG.cpp index 499b2940..00ffe926 100644 --- a/src/EPG.cpp +++ b/src/EPG.cpp @@ -30,7 +30,9 @@ EPG::EPG(const std::shared_ptr& settings, Request& request, Re PVR_ERROR EPG::GetEPGForChannel(int channelUid, time_t start, time_t end, kodi::addon::PVREPGTagsResultSet& results) { std::pair channelDetail; + std::lock_guard lock(m_channels.m_channelMutex); channelDetail = m_channels.m_channelDetails[channelUid]; + std::lock_guard unlock(m_channels.m_channelMutex); if (channelDetail.first == true) { kodi::Log(ADDON_LOG_DEBUG, "Skipping %d", channelUid); diff --git a/src/Timers.cpp b/src/Timers.cpp index 2ae9c5f7..a585be01 100644 --- a/src/Timers.cpp +++ b/src/Timers.cpp @@ -78,6 +78,7 @@ PVR_ERROR Timers::GetTimers(kodi::addon::PVRTimersResultSet& results) int timerCount = 0; // first add the recurring recordings tinyxml2::XMLDocument doc; + std::lock_guard lock(m_channels.m_channelMutex); if (m_request.DoMethodRequest("recording.recurring.list", doc) == tinyxml2::XML_SUCCESS) { tinyxml2::XMLNode* recurringsNode = doc.RootElement()->FirstChildElement("recurrings"); diff --git a/src/pvrclient-nextpvr.cpp b/src/pvrclient-nextpvr.cpp index d5542294..f798d725 100644 --- a/src/pvrclient-nextpvr.cpp +++ b/src/pvrclient-nextpvr.cpp @@ -335,6 +335,14 @@ bool cPVRClientNextPVR::IsUp() { if (lastUpdate > m_lastEPGUpdateTime) { + // if channel list changed trigger channel updates + if (m_channels.ResetChannelList(lastUpdate)) + { + kodi::Log(ADDON_LOG_DEBUG, "Trigger Channel update start"); + TriggerChannelUpdate(); + kodi::Log(ADDON_LOG_DEBUG, "Trigger Channel Groups update start"); + TriggerChannelGroupsUpdate(); + } // trigger EPG updates for all channels with a guide source kodi::Log(ADDON_LOG_DEBUG, "Trigger EPG update start"); int channels = 0;