Skip to content

Commit

Permalink
Merge pull request #677 from ksooo/providers-support
Browse files Browse the repository at this point in the history
Add support for PVR Providers (HTSPv38+)
  • Loading branch information
ksooo authored Aug 25, 2024
2 parents 2674084 + 66305f1 commit ce9ba70
Show file tree
Hide file tree
Showing 10 changed files with 195 additions and 6 deletions.
1 change: 1 addition & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ set(HTS_SOURCES_TVHEADEND_ENTITY
src/tvheadend/entity/Entity.h
src/tvheadend/entity/Event.h
src/tvheadend/entity/Event.cpp
src/tvheadend/entity/Provider.h
src/tvheadend/entity/Recording.h
src/tvheadend/entity/RecordingBase.h
src/tvheadend/entity/RecordingBase.cpp
Expand Down
2 changes: 1 addition & 1 deletion pvr.hts/addon.xml.in
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<addon
id="pvr.hts"
version="22.2.0"
version="22.3.0"
name="Tvheadend HTSP Client"
provider-name="Adam Sutton, Sam Stenvall, Lars Op den Kamp, Kai Sommerfeld">
<requires>@ADDON_DEPENDS@</requires>
Expand Down
3 changes: 3 additions & 0 deletions pvr.hts/changelog.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
v22.3.0
- Add support for PVR Providers (HTSPv38+)

v22.2.0
- Add support for TVH Parental Rating fields
- Misc cleanup and smaller fixes
Expand Down
111 changes: 109 additions & 2 deletions src/Tvheadend.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,7 @@ PVR_ERROR CTvheadend::GetCapabilities(kodi::addon::PVRCapabilities& capabilities
}

capabilities.SetSupportsRecordingSize(m_conn->GetProtocol() >= 35);
capabilities.SetSupportsProviders(m_conn->GetProtocol() >= 38);

return PVR_ERROR_NO_ERROR;
}
Expand Down Expand Up @@ -223,6 +224,46 @@ bool CTvheadend::HasStreamingProfile(const std::string& streamingProfile) const
{ return profile.GetName() == streamingProfile; }) != m_profiles.cend();
}

/* **************************************************************************
* Providers
* *************************************************************************/

PVR_ERROR CTvheadend::GetProvidersAmount(int& amount)
{
if (!m_asyncState.WaitForState(ASYNC_DVR))
return PVR_ERROR_FAILED;

std::lock_guard<std::recursive_mutex> lock(m_mutex);
amount = m_providers.size();
return PVR_ERROR_NO_ERROR;
}

PVR_ERROR CTvheadend::GetProviders(kodi::addon::PVRProvidersResultSet& results)
{
std::vector<kodi::addon::PVRProvider> providers;
{
std::lock_guard<std::recursive_mutex> lock(m_mutex);

providers.reserve(m_providers.size());
for (const auto& entry : m_providers)
{
kodi::addon::PVRProvider provider;
provider.SetUniqueId(entry.second.GetId());
provider.SetName(entry.second.GetName());

providers.emplace_back(std::move(provider));
}
}

for (const auto& provider : providers)
{
/* Callback. */
results.Add(std::move(provider));
}

return PVR_ERROR_NO_ERROR;
}

/* **************************************************************************
* Tags
* *************************************************************************/
Expand Down Expand Up @@ -360,6 +401,7 @@ PVR_ERROR CTvheadend::GetChannels(bool radio, kodi::addon::PVRChannelsResultSet&
chn.SetIsHidden(false);
chn.SetChannelName(channel.GetName());
chn.SetIconPath(channel.GetIcon());
chn.SetClientProviderUid(channel.GetProviderUid());

channels.emplace_back(std::move(chn));
}
Expand Down Expand Up @@ -490,11 +532,16 @@ PVR_ERROR CTvheadend::GetRecordings(bool deleted, kodi::addon::PVRRecordingsResu
/* Setup entry */
kodi::addon::PVRRecording rec;

/* Channel icon */
const auto& cit = m_channels.find(recording.GetChannel());
if (cit != m_channels.end())
{
/* Channel icon */
rec.SetIconPath(cit->second.GetIcon());

/* Provider */
rec.SetClientProviderUid(cit->second.GetProviderUid());
}

/* Channel name */
rec.SetChannelName(recording.GetChannelName());

Expand Down Expand Up @@ -1848,6 +1895,9 @@ void CTvheadend::Process()
{
switch (event.m_type)
{
case HTSP_EVENT_PRV_UPDATE:
kodi::addon::CInstancePVRClient::TriggerProvidersUpdate();
break;
case HTSP_EVENT_TAG_UPDATE:
kodi::addon::CInstancePVRClient::TriggerChannelGroupsUpdate();
break;
Expand All @@ -1868,6 +1918,11 @@ void CTvheadend::Process()
}
}

void CTvheadend::TriggerProviderUpdate()
{
m_events.emplace_back(SHTSPEvent(HTSP_EVENT_PRV_UPDATE));
}

void CTvheadend::TriggerChannelGroupsUpdate()
{
m_events.emplace_back(SHTSPEvent(HTSP_EVENT_TAG_UPDATE));
Expand Down Expand Up @@ -1919,6 +1974,8 @@ void CTvheadend::SyncInitCompleted()
/* Flag all async fields in case they've been deleted */
for (auto& entry : m_channels)
entry.second.SetDirty(true);
for (auto& entry : m_providers)
entry.second.SetDirty(true);
for (auto& entry : m_tags)
entry.second.SetDirty(true);
for (auto& entry : m_schedules)
Expand Down Expand Up @@ -1949,6 +2006,12 @@ void CTvheadend::SyncChannelsCompleted()

TriggerChannelUpdate();

/* Providers */
utilities::erase_if(m_providers,
[](const ProviderMapEntry& entry) { return entry.second.IsDirty(); });

TriggerProvidersUpdate();

/* Next */
m_asyncState.SetState(ASYNC_DVR);
}
Expand Down Expand Up @@ -2224,6 +2287,31 @@ void CTvheadend::ParseChannelAddOrUpdate(htsmsg_t* msg, bool bAdd)
/* CAID */
if (caid == 0)
htsmsg_get_u32(&f->hmf_msg, "caid", &caid);

/* Service provider */
str = htsmsg_get_str(&f->hmf_msg, "providername");
if (str && strlen(str) > 0)
{
const int32_t uid{utilities::hash_str_int32(str)};
channel.SetProviderUid(uid);

/* Locate/create provider object */
Provider& provider = m_providers[uid];
Provider comparison = provider;
provider.SetId(uid);
provider.SetDirty(false);

provider.SetName(str);

if (provider != comparison)
{
Logger::Log(LogLevel::LEVEL_DEBUG, "provider %s id:%u, name:%s",
(bAdd ? "added" : "updated"), provider.GetId(), provider.GetName().c_str());

if (m_asyncState.GetState() > ASYNC_CHN)
TriggerProvidersUpdate();
}
}
}

channel.SetCaid(caid);
Expand Down Expand Up @@ -2256,10 +2344,29 @@ void CTvheadend::ParseChannelDelete(htsmsg_t* msg)
}
Logger::Log(LogLevel::LEVEL_DEBUG, "delete channel %u", u32);

/* Erase */
/* We need to check and update providers. May be the deleted channel is the last channel with this provider */
int32_t providerUid{PVR_PROVIDER_INVALID_UID};
const auto it = m_channels.find(u32);
if (it != m_channels.cend())
providerUid = (*it).second.GetProviderUid();

/* Erase channel */
m_channels.erase(u32);
m_channelTuningPredictor.RemoveChannel(u32);
TriggerChannelUpdate();

if (providerUid != PVR_PROVIDER_INVALID_UID)
{
const auto it2 = m_providers.find(providerUid);
if (std::none_of(m_channels.cbegin(), m_channels.cend(),
[providerUid](const ChannelMapEntry& entry)
{ return entry.second.GetProviderUid() == providerUid; }))
{
/* Erase provider */
m_providers.erase(it2);
TriggerProvidersUpdate();
}
}
}

void CTvheadend::ParseRecordingAddOrUpdate(htsmsg_t* msg, bool bAdd)
Expand Down
6 changes: 6 additions & 0 deletions src/Tvheadend.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ extern "C"
#include "tvheadend/Profile.h"
#include "tvheadend/TimeRecordings.h"
#include "tvheadend/entity/Channel.h"
#include "tvheadend/entity/Provider.h"
#include "tvheadend/entity/Recording.h"
#include "tvheadend/entity/Schedule.h"
#include "tvheadend/entity/Tag.h"
Expand Down Expand Up @@ -90,6 +91,9 @@ class ATTR_DLL_LOCAL CTvheadend : public kodi::addon::CInstancePVRClient,
PVR_ERROR GetCapabilities(kodi::addon::PVRCapabilities& capabilities) override;
PVR_ERROR GetDriveSpace(uint64_t& total, uint64_t& used) override;

PVR_ERROR GetProvidersAmount(int& amount) override;
PVR_ERROR GetProviders(kodi::addon::PVRProvidersResultSet& results) override;

PVR_ERROR GetChannelGroupsAmount(int& amount) override;
PVR_ERROR GetChannelGroups(bool radio, kodi::addon::PVRChannelGroupsResultSet& results) override;
PVR_ERROR GetChannelGroupMembers(const kodi::addon::PVRChannelGroup& group,
Expand Down Expand Up @@ -161,6 +165,7 @@ class ATTR_DLL_LOCAL CTvheadend : public kodi::addon::CInstancePVRClient,
/*
* Event handling
*/
void TriggerProviderUpdate();
void TriggerChannelGroupsUpdate();
void TriggerChannelUpdate();
void TriggerRecordingUpdate();
Expand Down Expand Up @@ -277,6 +282,7 @@ class ATTR_DLL_LOCAL CTvheadend : public kodi::addon::CInstancePVRClient,
HTSPMessageQueue m_queue;

tvheadend::entity::Channels m_channels;
tvheadend::entity::Providers m_providers;
tvheadend::entity::Tags m_tags;
tvheadend::entity::Recordings m_recordings;
tvheadend::entity::Schedules m_schedules;
Expand Down
2 changes: 1 addition & 1 deletion src/tvheadend/HTSPConnection.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ using namespace tvheadend::utilities;

#define HTSP_MIN_SERVER_VERSION (26) // Server must support at least this htsp version
#define HTSP_CLIENT_VERSION \
(37) // Client uses HTSP features up to this version. If the respective \
(38) // Client uses HTSP features up to this version. If the respective \
// addon feature requires htsp features introduced after \
// HTSP_MIN_SERVER_VERSION this feature will only be available if the \
// actual server HTSP version matches (runtime htsp version check).
Expand Down
1 change: 1 addition & 0 deletions src/tvheadend/HTSPTypes.h
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,7 @@ enum eHTSPEventType
HTSP_EVENT_TAG_UPDATE = 2,
HTSP_EVENT_EPG_UPDATE = 3,
HTSP_EVENT_REC_UPDATE = 4,
HTSP_EVENT_PRV_UPDATE = 5,
};

struct SHTSPEvent
Expand Down
6 changes: 5 additions & 1 deletion src/tvheadend/entity/Channel.h
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ class Channel : public Entity
{
return m_id == other.m_id && m_num == other.m_num && m_numMinor == other.m_numMinor &&
m_type == other.m_type && m_caid == other.m_caid && m_name == other.m_name &&
m_icon == other.m_icon;
m_icon == other.m_icon && m_providerUid == other.m_providerUid;
}

bool operator!=(const Channel& other) const { return !(*this == other); }
Expand All @@ -60,13 +60,17 @@ class Channel : public Entity
const std::string& GetIcon() const { return m_icon; }
void SetIcon(const std::string& icon) { m_icon = icon; }

int32_t GetProviderUid() const { return m_providerUid; }
void SetProviderUid(int32_t providerUid) { m_providerUid = providerUid; }

private:
uint32_t m_num;
uint32_t m_numMinor;
uint32_t m_type;
uint32_t m_caid;
std::string m_name;
std::string m_icon;
int32_t m_providerUid{PVR_PROVIDER_INVALID_UID};
};
} // namespace entity
} // namespace tvheadend
45 changes: 45 additions & 0 deletions src/tvheadend/entity/Provider.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
/*
* Copyright (C) 2024 Team Kodi (https://kodi.tv)
*
* SPDX-License-Identifier: GPL-2.0-or-later
* See LICENSE.md for more information.
*/

#pragma once

#include "Entity.h"

#include <cstdint>
#include <map>
#include <string>
#include <utility>

namespace tvheadend::entity
{

class Provider;
using ProviderMapEntry = std::pair<int32_t, Provider>;
using Providers = std::map<int32_t, Provider>;

/**
* Represents a provider
*/
class Provider : public Entity
{
public:
Provider() = default;

bool operator==(const Provider& other) const
{
return m_id == other.m_id && m_name == other.m_name;
}

bool operator!=(const Provider& other) const { return !(*this == other); }

const std::string& GetName() const { return m_name; }
void SetName(const std::string& name) { m_name = name; }

private:
std::string m_name;
};
} // namespace tvheadend::entity
24 changes: 23 additions & 1 deletion src/tvheadend/utilities/Utilities.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,29 @@ void erase_if(ContainerT& items, const PredicateT& predicate)
else
++it;
}
};
}

/**
* Simple hash function. Borrowed from:
* https://stackoverflow.com/questions/16075271/hashing-a-string-to-an-integer-in-c
*/
static int32_t hash_str_int32(const std::string& str)
{
int32_t hash = 0x811c9dc5;
int32_t prime = 0x1000193;

for (size_t i = 0; i < str.size(); ++i)
{
uint8_t value = str[i];
hash = hash ^ value;
hash *= prime;
}

if (hash < 0)
hash = -hash;

return hash;
}

} // namespace utilities
} // namespace tvheadend

0 comments on commit ce9ba70

Please sign in to comment.