diff -Nru kodi-pvr-zattoo-19.7.4/debian/changelog kodi-pvr-zattoo-19.7.8/debian/changelog --- kodi-pvr-zattoo-19.7.4/debian/changelog 2013-05-31 22:59:22.000000000 +0000 +++ kodi-pvr-zattoo-19.7.8/debian/changelog 2013-05-31 22:59:22.000000000 +0000 @@ -1,4 +1,4 @@ -kodi-pvr-zattoo (19.7.4-1~bionic) bionic; urgency=low +kodi-pvr-zattoo (19.7.8-1~bionic) bionic; urgency=low [ kodi ] * autogenerated dummy changelog diff -Nru kodi-pvr-zattoo-19.7.4/pvr.zattoo/addon.xml.in kodi-pvr-zattoo-19.7.8/pvr.zattoo/addon.xml.in --- kodi-pvr-zattoo-19.7.4/pvr.zattoo/addon.xml.in 2021-03-03 09:38:20.000000000 +0000 +++ kodi-pvr-zattoo-19.7.8/pvr.zattoo/addon.xml.in 2021-03-09 18:35:43.000000000 +0000 @@ -1,6 +1,6 @@ @@ -32,6 +32,14 @@ fanart.jpg +v19.7.8 + - Fix concurrent access to list +v19.7.7 + - Fix loading detailed epg information +v19.7.6 + - Try to get current epg from Zattoo if replay fails +v19.7.5 + - Do not load EPG for channels not listed v19.7.4 - Load additional EPG information directly from Zattoo - Remove enhanced EPG provider diff -Nru kodi-pvr-zattoo-19.7.4/src/epg/ZattooEpgProvider.cpp kodi-pvr-zattoo-19.7.8/src/epg/ZattooEpgProvider.cpp --- kodi-pvr-zattoo-19.7.4/src/epg/ZattooEpgProvider.cpp 2021-03-03 09:38:20.000000000 +0000 +++ kodi-pvr-zattoo-19.7.8/src/epg/ZattooEpgProvider.cpp 2021-03-09 18:35:43.000000000 +0000 @@ -7,13 +7,15 @@ using namespace rapidjson; +std::mutex ZattooEpgProvider::loadedTimeslotsMutex; + ZattooEpgProvider::ZattooEpgProvider( kodi::addon::CInstancePVRClient *addon, std::string providerUrl, EpgDB &epgDB, HttpClient &httpClient, Categories &categories, - std::map &channelsByUid, + std::map &visibleChannelsByCid, std::string powerHash ): EpgProvider(addon), @@ -22,7 +24,7 @@ m_categories(categories), m_powerHash(powerHash), m_providerUrl(providerUrl), - m_channelsByUid(channelsByUid) + m_visibleChannelsByCid(visibleChannelsByCid) { time(&lastCleanup); m_detailsThreadRunning = true; @@ -36,10 +38,10 @@ } bool ZattooEpgProvider::LoadEPGForChannel(ZatChannel ¬Used, time_t iStart, time_t iEnd) { + CleanupAlreadyLoaded(); time_t tempStart = iStart - (iStart % (3600 / 2)) - 86400; tempStart = SkipAlreadyLoaded(tempStart, iEnd); time_t tempEnd = tempStart + 3600 * 5; //Add 5 hours - CleanupAlreadyLoaded(); while (tempStart < iEnd) { if (tempEnd > iEnd) { @@ -70,7 +72,7 @@ int uniqueChannelId = Utils::GetChannelId(cid.c_str()); - if (m_channelsByUid.count(uniqueChannelId) == 0) { + if (m_visibleChannelsByCid.count(cid) == 0) { continue; } @@ -125,6 +127,10 @@ void ZattooEpgProvider::SendEpgDBInfo(EpgDBInfo &epgDBInfo) { + if (m_visibleChannelsByCid.count(epgDBInfo.cid) == 0) { + return; + } + int uniqueChannelId = Utils::GetChannelId(epgDBInfo.cid.c_str()); kodi::addon::PVREPGTag tag; @@ -169,14 +175,14 @@ void ZattooEpgProvider::CleanupAlreadyLoaded() { time_t now; time(&now); - if (lastCleanup < now + 60) { + if (lastCleanup + 60 > now) { return; } lastCleanup = now; - + std::lock_guard lock(loadedTimeslotsMutex); m_loadedTimeslots.erase( std::remove_if(m_loadedTimeslots.begin(), m_loadedTimeslots.end(), - [&now](const LoadedTimeslots & o) { return o.loaded < now - 180; }), + [&now](const LoadedTimeslots & o) { return o.loaded < now - 60; }), m_loadedTimeslots.end()); } @@ -185,11 +191,13 @@ slot.start = startTime; slot.end = endTime; time(&slot.loaded); + std::lock_guard lock(loadedTimeslotsMutex); m_loadedTimeslots.push_back(slot); } time_t ZattooEpgProvider::SkipAlreadyLoaded(time_t startTime, time_t endTime) { time_t newStartTime = startTime; + std::lock_guard lock(loadedTimeslotsMutex); std::vector slots(m_loadedTimeslots.begin(), m_loadedTimeslots.end()); for (LoadedTimeslots slot: slots) { if (slot.start <= newStartTime && slot.end > newStartTime) { @@ -215,7 +223,7 @@ m_epgDB.BeginTransaction(); std::vector infos(epgDBInfos.begin(), epgDBInfos.end()); std::ostringstream ids; - std::map epgDBInfoById; + std::map epgDBInfoById; bool first = true; for (EpgDBInfo &epgDBInfo: infos) { @@ -225,7 +233,7 @@ ids << ","; } ids << epgDBInfo.programId; - epgDBInfoById[epgDBInfo.programId] = epgDBInfo; + epgDBInfoById[epgDBInfo.programId] = &epgDBInfo; } std::ostringstream urlStream; @@ -251,14 +259,14 @@ { const Value &program = *progItr; int programId = program["id"].GetInt(); - EpgDBInfo &epgDBInfo = epgDBInfoById[programId]; - epgDBInfo.description = Utils::JsonStringOrEmpty(program, "d"); - epgDBInfo.season = program.HasMember("s_no") && !program["s_no"].IsNull() ? program["s_no"].GetInt() : -1; - epgDBInfo.episode = program.HasMember("e_no") && !program["e_no"].IsNull() ? program["e_no"].GetInt() : -1; - - epgDBInfo.detailsLoaded=1; - m_epgDB.Update(epgDBInfo); - SendEpgDBInfo(epgDBInfo); + EpgDBInfo *epgDBInfo = epgDBInfoById[programId]; + epgDBInfo->description = Utils::JsonStringOrEmpty(program, "d"); + epgDBInfo->season = program.HasMember("s_no") && !program["s_no"].IsNull() ? program["s_no"].GetInt() : -1; + epgDBInfo->episode = program.HasMember("e_no") && !program["e_no"].IsNull() ? program["e_no"].GetInt() : -1; + + epgDBInfo->detailsLoaded=1; + m_epgDB.Update(*epgDBInfo); + SendEpgDBInfo(*epgDBInfo); } } for (EpgDBInfo &epgDBInfo: infos) { diff -Nru kodi-pvr-zattoo-19.7.4/src/epg/ZattooEpgProvider.h kodi-pvr-zattoo-19.7.8/src/epg/ZattooEpgProvider.h --- kodi-pvr-zattoo-19.7.4/src/epg/ZattooEpgProvider.h 2021-03-03 09:38:20.000000000 +0000 +++ kodi-pvr-zattoo-19.7.8/src/epg/ZattooEpgProvider.h 2021-03-09 18:35:43.000000000 +0000 @@ -26,12 +26,13 @@ EpgDB &epgDB, HttpClient &httpClient, Categories &categories, - std::map &channelsByUid, + std::map &visibleChannelsByCid, std::string powerHash ); virtual ~ZattooEpgProvider(); virtual bool LoadEPGForChannel(ZatChannel &zatChannel, time_t iStart, time_t iEnd); private: + static std::mutex loadedTimeslotsMutex; time_t SkipAlreadyLoaded(time_t startTime, time_t endTime); void RegisterAlreadyLoaded(time_t startTime, time_t endTime); void CleanupAlreadyLoaded(); @@ -44,10 +45,9 @@ std::string m_powerHash; std::string m_providerUrl; std::list m_loadedTimeslots; - std::map &m_channelsByUid; + std::map &m_visibleChannelsByCid; std::atomic m_detailsThreadRunning = {false}; std::thread m_detailsThread; - }; #endif /* SRC_EPG_ZATTOOEPGPROVIDER_H_ */ diff -Nru kodi-pvr-zattoo-19.7.4/src/sql/EpgDB.cpp kodi-pvr-zattoo-19.7.8/src/sql/EpgDB.cpp --- kodi-pvr-zattoo-19.7.4/src/sql/EpgDB.cpp 2021-03-03 09:38:20.000000000 +0000 +++ kodi-pvr-zattoo-19.7.8/src/sql/EpgDB.cpp 2021-03-09 18:35:43.000000000 +0000 @@ -1,6 +1,6 @@ #include "EpgDB.h" -const int DB_VERSION = 2; +const int DB_VERSION = 3; class ProcessEpgDBInfoRowCallback : public ProcessRowCallback { public: @@ -86,6 +86,11 @@ return false; } break; + case 2: + if (!Migrate2To3()) { + return false; + } + break; } currentVersion = GetVersion(); } @@ -146,6 +151,14 @@ return SetVersion(2); } +bool EpgDB::Migrate2To3() { + kodi::Log(ADDON_LOG_INFO, "%s: Migrate to version 3.", m_name.c_str()); + if (!Execute("update EPG_INFO set DETAILS_LOADED = 0;")) { + return false; + } + return SetVersion(3); +} + void EpgDB::Cleanup() { time_t now; time(&now); diff -Nru kodi-pvr-zattoo-19.7.4/src/sql/EpgDB.h kodi-pvr-zattoo-19.7.8/src/sql/EpgDB.h --- kodi-pvr-zattoo-19.7.4/src/sql/EpgDB.h 2021-03-03 09:38:20.000000000 +0000 +++ kodi-pvr-zattoo-19.7.8/src/sql/EpgDB.h 2021-03-09 18:35:43.000000000 +0000 @@ -36,6 +36,7 @@ bool MigrateDbIfRequired(); bool Migrate0To1(); bool Migrate1To2(); + bool Migrate2To3(); void Cleanup(); time_t m_nextCleanupDue = 0; sqlite3_stmt *m_prepareInsertStatement; diff -Nru kodi-pvr-zattoo-19.7.4/src/ZatData.cpp kodi-pvr-zattoo-19.7.8/src/ZatData.cpp --- kodi-pvr-zattoo-19.7.4/src/ZatData.cpp 2021-03-03 09:38:20.000000000 +0000 +++ kodi-pvr-zattoo-19.7.8/src/ZatData.cpp 2021-03-09 18:35:43.000000000 +0000 @@ -268,7 +268,7 @@ if (m_epgProvider) { delete m_epgProvider; } - m_epgProvider = new ZattooEpgProvider(this, m_providerUrl, *m_epgDB, *m_httpClient, m_categories, m_channelsByUid, m_powerHash); + m_epgProvider = new ZattooEpgProvider(this, m_providerUrl, *m_epgDB, *m_httpClient, m_categories, m_visibleChannelsByCid, m_powerHash); return true; } @@ -367,7 +367,12 @@ if (!favGroup.channels.empty()) m_channelGroups.insert(m_channelGroups.end(), favGroup); - + + for (const PVRZattooChannelGroup &group : m_channelGroups) { + for (const ZatChannel &channel : group.channels) { + m_visibleChannelsByCid[channel.cid] = channel; + } + } return true; } @@ -580,7 +585,7 @@ PVR_ERROR ZatData::GetChannelsAmount(int& amount) { - amount = static_cast(m_channelsByCid.size()); + amount = static_cast(m_visibleChannelsByCid.size()); return PVR_ERROR_NO_ERROR; } @@ -1249,26 +1254,68 @@ PVR_ERROR ZatData::GetEPGTagStreamProperties(const kodi::addon::PVREPGTag& tag, std::vector& properties) { - PVR_ERROR ret = PVR_ERROR_FAILED; std::ostringstream dataStream; ZatChannel channel = m_channelsByUid[tag.GetUniqueChannelId()]; + + std::string url = GetStreamUrlForProgram(channel.cid, tag.GetUniqueBroadcastId(), properties); + + if (url.empty()) { + kodi::Log(ADDON_LOG_WARNING, "Could not get url for channel %s and program %i. Try to get new EPG tag.", channel.cid.c_str(), tag.GetUniqueBroadcastId()); + time_t referenceTime = (tag.GetStartTime() + tag.GetEndTime()) / 2; + std::ostringstream urlStream; + urlStream << m_providerUrl << "/zapi/v3/cached/" + m_powerHash + "/guide" + << "?end=" << referenceTime << "&start=" << referenceTime + << "&format=json"; + + int statusCode; + std::string jsonString = m_httpClient->HttpGet(urlStream.str(), statusCode); + + Document doc; + doc.Parse(jsonString.c_str()); + if (doc.GetParseError()) + { + kodi::Log(ADDON_LOG_ERROR, "Loading epg failed at %i", referenceTime); + return PVR_ERROR_FAILED; + } + const Value& channels = doc["channels"]; + if (!channels.HasMember(channel.cid.c_str())) { + kodi::Log(ADDON_LOG_ERROR, "Channel not found in epg."); + return PVR_ERROR_FAILED; + } + const Value& channelEpg = channels[channel.cid.c_str()]; + if (!channelEpg.IsArray() || channelEpg.GetArray().Empty()) { + kodi::Log(ADDON_LOG_ERROR, "Channel has no program at time %i.", referenceTime); + return PVR_ERROR_FAILED; + } + const Value& program = channelEpg.GetArray()[0]; + int newProgramId = program["id"].GetInt(); + + url = GetStreamUrlForProgram(channel.cid, newProgramId, properties); + + if (url.empty()) { + kodi::Log(ADDON_LOG_ERROR, "Could not get url for channel %s and program %i.", channel.cid.c_str(), newProgramId); + return PVR_ERROR_FAILED; + } + } + + SetStreamProperties(properties, url); + return PVR_ERROR_NO_ERROR; +} + +std::string ZatData::GetStreamUrlForProgram(const std::string& cid, int programId, std::vector& properties) +{ + std::ostringstream dataStream; std::string jsonString; - kodi::Log(ADDON_LOG_DEBUG, "Get timeshift url for channel %s and program %i", channel.cid.c_str(), tag.GetUniqueBroadcastId()); + kodi::Log(ADDON_LOG_DEBUG, "Get timeshift url for channel %s and program %i", cid.c_str(), programId); dataStream << GetStreamParameters(); dataStream << "&pre_padding=0&post_padding=0"; - jsonString = HttpPostWithRetry(m_providerUrl + "/zapi/v3/watch/replay/" + channel.cid + "/" + std::to_string(tag.GetUniqueBroadcastId()), dataStream.str()); + jsonString = HttpPostWithRetry(m_providerUrl + "/zapi/v3/watch/replay/" + cid + "/" + std::to_string(programId), dataStream.str()); std::string strUrl = GetStreamUrl(jsonString, properties); - if (!strUrl.empty()) - { - SetStreamProperties(properties, strUrl); - ret = PVR_ERROR_NO_ERROR; - } - - return ret; + return strUrl; } PVR_ERROR ZatData::GetEPGTagEdl(const kodi::addon::PVREPGTag& tag, std::vector& edl) diff -Nru kodi-pvr-zattoo-19.7.4/src/ZatData.h kodi-pvr-zattoo-19.7.8/src/ZatData.h --- kodi-pvr-zattoo-19.7.4/src/ZatData.h 2021-03-03 09:38:20.000000000 +0000 +++ kodi-pvr-zattoo-19.7.8/src/ZatData.h 2021-03-09 18:35:43.000000000 +0000 @@ -92,6 +92,7 @@ std::vector m_channelGroups; std::map m_channelsByUid; std::map m_channelsByCid; + std::map m_visibleChannelsByCid; std::vector m_updateThreads; Categories m_categories; std::string m_providerUrl; @@ -123,6 +124,7 @@ std::string GetManifestType(); std::string GetMimeType(); void SetStreamProperties(std::vector& properties, const std::string& url); + std::string GetStreamUrlForProgram(const std::string& cid, int programId, std::vector& properties); bool TryToReinitIf403(int statusCode); std::string HttpGetWithRetry(std::string url); std::string HttpPostWithRetry(std::string url, const std::string& postData);