Skip to content

Commit

Permalink
Add support for Autorec property 'Broadcast type' (HTSPv39+) and DVR …
Browse files Browse the repository at this point in the history
…entry property 'DVR configuration' (HTSPv40+).
  • Loading branch information
ksooo committed Aug 29, 2024
1 parent ce9ba70 commit eab6971
Show file tree
Hide file tree
Showing 19 changed files with 547 additions and 43 deletions.
2 changes: 2 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,8 @@ set(HTS_SOURCES_TVHEADEND
src/tvheadend/AutoRecordings.h
src/tvheadend/ChannelTuningPredictor.h
src/tvheadend/ChannelTuningPredictor.cpp
src/tvheadend/CustomTimerProperties.h
src/tvheadend/CustomTimerProperties.cpp
src/tvheadend/HTSPConnection.h
src/tvheadend/HTSPConnection.cpp
src/tvheadend/HTSPDemuxer.h
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.3.0"
version="22.4.0"
name="Tvheadend HTSP Client"
provider-name="Adam Sutton, Sam Stenvall, Lars Op den Kamp, Kai Sommerfeld">
<requires>@ADDON_DEPENDS@</requires>
Expand Down
5 changes: 5 additions & 0 deletions pvr.hts/changelog.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
v22.4.0
- PVR Add-on API v9.1.0
- Add support for Autorec property "Broadcast type" (HTSPv39+)
- Add support for DVR entry property "DVR configuration" (HTSPv40+)

v22.3.0
- Add support for PVR Providers (HTSPv38+)

Expand Down
42 changes: 38 additions & 4 deletions pvr.hts/resources/language/resource.language.en_gb/strings.po
Original file line number Diff line number Diff line change
Expand Up @@ -298,7 +298,13 @@ msgctxt "#30372"
msgid "Record if unique episode according EPG/XMLTV"
msgstr ""

#empty strings from id 30373 to 30374
#. Prevent duplicate episodes representation
#: src/Tvheadend.cpp
msgctxt "#30373"
msgid "Use DVR configuration"
msgstr ""

#empty string with id 30374

#. Recording lifetime representation
#: src/Tvheadend.cpp
Expand Down Expand Up @@ -441,7 +447,11 @@ msgctxt "#30456"
msgid "Subscription error"
msgstr ""

#empty strings from id 30457 to 30499
msgctxt "#30457"
msgid "DVR configuration"
msgstr ""

#empty strings from id 30458 to 30499

msgctxt "#30500"
msgid "Streaming profile"
Expand All @@ -451,8 +461,6 @@ msgctxt "#30501"
msgid "Profile to use (empty = default)"
msgstr ""

#. Check streaming profile validity during startup
#: src/client.cpp
msgctxt "#30502"
msgid "Streaming profile %s is not available"
msgstr ""
Expand All @@ -478,3 +486,29 @@ msgstr ""
msgctxt "#30511"
msgid "Server based play status"
msgstr ""

#empty strings from id 30512 to 30599

msgctxt "#30600"
msgid "Broadcast type"
msgstr ""

msgctxt "#30601"
msgid "Any"
msgstr ""

msgctxt "#30602"
msgid "New / premiere / unknown"
msgstr ""

msgctxt "#30603"
msgid "Repeated"
msgstr ""

msgctxt "#30604"
msgid "New / premiere"
msgstr ""

msgctxt "#30605"
msgid "(Default profile)"
msgstr ""
145 changes: 139 additions & 6 deletions src/Tvheadend.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

#include "Tvheadend.h"

#include "tvheadend/CustomTimerProperties.h"
#include "tvheadend/HTSPConnection.h"
#include "tvheadend/HTSPDemuxer.h"
#include "tvheadend/HTSPMessage.h"
Expand All @@ -22,6 +23,7 @@
#include <cstdlib>
#include <cstring>
#include <ctime>
#include <iterator>
#include <memory>

using namespace tvheadend;
Expand All @@ -36,8 +38,8 @@ CTvheadend::CTvheadend(const kodi::addon::IInstanceInfo& instance)
m_vfs(new HTSPVFS(m_settings, *m_conn)),
m_queue(static_cast<size_t>(-1)),
m_asyncState(m_settings->GetResponseTimeout()),
m_timeRecordings(*m_conn),
m_autoRecordings(m_settings, *m_conn),
m_timeRecordings(*m_conn, m_dvrConfigs),
m_autoRecordings(m_settings, *m_conn, m_dvrConfigs),
m_epgMaxDays(EpgMaxFutureDays()),
m_playingLiveStream(false),
m_playingRecording(nullptr)
Expand Down Expand Up @@ -167,6 +169,56 @@ std::string CTvheadend::GetImageURL(const char* str)
}
}

void CTvheadend::QueryAvailableDvrConfigurations(std::unique_lock<std::recursive_mutex>& lock)
{
/* Build message */
htsmsg_t* m = htsmsg_create_map();

/* Send */
m = m_conn->SendAndWait0(lock, "getDvrConfigs", m);

/* Validate */
if (!m)
return;

htsmsg_t* l = htsmsg_get_list(m, "dvrconfigs");

if (!l)
{
Logger::Log(LogLevel::LEVEL_ERROR, "malformed getDvrConfigs: 'dvrconfigs' missing");
htsmsg_destroy(m);
return;
}

/* Process */
Logger::Log(LogLevel::LEVEL_INFO, " Available DVR configurations:");

htsmsg_field_t* f = nullptr;
HTSMSG_FOREACH(f, l)
{
Profile profile;

const char* str = htsmsg_get_str(&f->hmf_msg, "uuid");
if (str)
profile.SetUuid(str);

str = htsmsg_get_str(&f->hmf_msg, "name");
if (str)
profile.SetName(str);

str = htsmsg_get_str(&f->hmf_msg, "comment");
if (str)
profile.SetComment(str);

Logger::Log(LogLevel::LEVEL_INFO, " Name: %s, Comment: %s", profile.GetName().c_str(),
profile.GetComment().c_str());

m_dvrConfigs.emplace_back(std::move(profile));
}

htsmsg_destroy(m);
}

void CTvheadend::QueryAvailableProfiles(std::unique_lock<std::recursive_mutex>& lock)
{
/* Build message */
Expand Down Expand Up @@ -840,16 +892,16 @@ struct TimerType : kodi::addon::PVRTimerType
unsigned int id,
unsigned int attributes,
const std::string& description,
const std::vector<kodi::addon::PVRTypeIntValue>& priorityValues =
std::vector<kodi::addon::PVRTypeIntValue>(),
const std::vector<kodi::addon::PVRTypeIntValue>& lifetimeValues =
std::vector<kodi::addon::PVRTypeIntValue>(),
const std::vector<kodi::addon::PVRIntSettingDefinition>& customIntSettingDefs,
const std::vector<kodi::addon::PVRTypeIntValue>& priorityValues,
const std::vector<kodi::addon::PVRTypeIntValue>& lifetimeValues,
const std::vector<kodi::addon::PVRTypeIntValue>& dupEpisodesValues =
std::vector<kodi::addon::PVRTypeIntValue>())
{
SetId(id);
SetAttributes(attributes);
SetDescription(description);
SetCustomIntSettingDefinitions(customIntSettingDefs);
SetPriorities(priorityValues, settings->GetDvrPriority());
SetLifetimes(lifetimeValues, LifetimeMapper::TvhToKodi(settings->GetDvrLifetime()));
SetPreventDuplicateEpisodes(dupEpisodesValues, settings->GetDvrDupdetect());
Expand Down Expand Up @@ -880,6 +932,40 @@ void CTvheadend::GetLivetimeValues(std::vector<kodi::addon::PVRTypeIntValue>& li
};
}

const std::vector<kodi::addon::PVRIntSettingDefinition> CTvheadend::
GetCustomIntSettingDefinitions() const
{
std::vector<kodi::addon::PVRIntSettingDefinition> ret;

//! @todo CustomTimerProperties???
if (m_dvrConfigs.size() > 1 && m_conn->GetProtocol() >= 40)
{
// DVR configuration
int defaultConfigId{0};
std::vector<kodi::addon::PVRTypeIntValue> dvrConfigValues;
for (const auto& entry : m_dvrConfigs)
{
std::string name{entry.GetName()};
if (name.empty())
{
name = kodi::addon::GetLocalizedString(30605); // (default profile)
defaultConfigId = entry.GetId();
}

dvrConfigValues.emplace_back(entry.GetId(), name);
}

kodi::addon::PVRIntSettingDefinition settingDef;
settingDef.SetId(CUSTOM_PROP_ID_DVR_CONFIGURATION);
settingDef.SetName(kodi::addon::GetLocalizedString(30457)); // DVR configuration
settingDef.SetValues(dvrConfigValues);
settingDef.SetDefaultValue(defaultConfigId);
ret.emplace_back(std::move(settingDef));
}

return ret;
}

PVR_ERROR CTvheadend::GetTimerTypes(std::vector<kodi::addon::PVRTimerType>& types)
{
/* PVR_Timer.iPriority values and presentation.*/
Expand All @@ -898,6 +984,7 @@ PVR_ERROR CTvheadend::GetTimerTypes(std::vector<kodi::addon::PVRTimerType>& type

/* PVR_Timer.iPreventDuplicateEpisodes values and presentation.*/
std::vector<kodi::addon::PVRTypeIntValue> deDupValues = {
{DVR_AUTOREC_RECORD_DVR_PROFILE, kodi::addon::GetLocalizedString(30373)},
{DVR_AUTOREC_RECORD_ALL, kodi::addon::GetLocalizedString(30356)},
{DVR_AUTOREC_RECORD_DIFFERENT_EPISODE_NUMBER, kodi::addon::GetLocalizedString(30357)},
{DVR_AUTOREC_RECORD_DIFFERENT_SUBTITLE, kodi::addon::GetLocalizedString(30358)},
Expand Down Expand Up @@ -951,6 +1038,10 @@ PVR_ERROR CTvheadend::GetTimerTypes(std::vector<kodi::addon::PVRTimerType>& type

/* Timer types definition. */

/* Custom integer setting definitions */
const std::vector<kodi::addon::PVRIntSettingDefinition> customIntSettingDefs{
GetCustomIntSettingDefinitions()};

/* One-shot manual (time and channel based) */
types.emplace_back(TimerType(
/* Settings */
Expand All @@ -961,6 +1052,8 @@ PVR_ERROR CTvheadend::GetTimerTypes(std::vector<kodi::addon::PVRTimerType>& type
TIMER_ONCE_MANUAL_ATTRIBS,
/* Let Kodi generate the description. */
"",
/* Custom int settings definitions. */
customIntSettingDefs,
/* Values definitions for priorities. */
priorityValues,
/* Values definitions for lifetime. */
Expand All @@ -976,6 +1069,8 @@ PVR_ERROR CTvheadend::GetTimerTypes(std::vector<kodi::addon::PVRTimerType>& type
TIMER_ONCE_EPG_ATTRIBS,
/* Let Kodi generate the description. */
"",
/* Custom int settings definitions. */
customIntSettingDefs,
/* Values definitions for priorities. */
priorityValues,
/* Values definitions for lifetime. */
Expand All @@ -991,6 +1086,8 @@ PVR_ERROR CTvheadend::GetTimerTypes(std::vector<kodi::addon::PVRTimerType>& type
TIMER_ONCE_MANUAL_ATTRIBS | PVR_TIMER_TYPE_IS_READONLY | PVR_TIMER_TYPE_FORBIDS_NEW_INSTANCES,
/* Description. */
kodi::addon::GetLocalizedString(30350), // "One Time (Scheduled by timer rule)"
/* Custom int settings definitions. */
customIntSettingDefs,
/* Values definitions for priorities. */
priorityValues,
/* Values definitions for lifetime. */
Expand All @@ -1006,6 +1103,8 @@ PVR_ERROR CTvheadend::GetTimerTypes(std::vector<kodi::addon::PVRTimerType>& type
TIMER_ONCE_EPG_ATTRIBS | PVR_TIMER_TYPE_IS_READONLY | PVR_TIMER_TYPE_FORBIDS_NEW_INSTANCES,
/* Description. */
kodi::addon::GetLocalizedString(30350), // "One Time (Scheduled by timer rule)"
/* Custom int settings definitions. */
customIntSettingDefs,
/* Values definitions for priorities. */
priorityValues,
/* Values definitions for lifetime. */
Expand All @@ -1025,11 +1124,19 @@ PVR_ERROR CTvheadend::GetTimerTypes(std::vector<kodi::addon::PVRTimerType>& type
PVR_TIMER_TYPE_SUPPORTS_LIFETIME | PVR_TIMER_TYPE_SUPPORTS_RECORDING_FOLDERS,
/* Let Kodi generate the description. */
"",
/* Custom int settings definitions. */
customIntSettingDefs,
/* Values definitions for priorities. */
priorityValues,
/* Values definitions for lifetime. */
lifetimeValues));

/* Custom Autorec integer setting definitions */
std::vector<kodi::addon::PVRIntSettingDefinition> customAutorecIntSettingDefs{
m_autoRecordings.GetCustomAutorecIntSettingDefinitions()};
std::copy(customIntSettingDefs.cbegin(), customIntSettingDefs.cend(),
std::back_inserter(customAutorecIntSettingDefs));

if (m_conn->GetProtocol() >= 29)
{
unsigned int TIMER_REPEATING_SERIESLINK_ATTRIBS =
Expand Down Expand Up @@ -1057,6 +1164,8 @@ PVR_ERROR CTvheadend::GetTimerTypes(std::vector<kodi::addon::PVRTimerType>& type
TIMER_REPEATING_SERIESLINK_ATTRIBS,
/* Description. */
kodi::addon::GetLocalizedString(30369), // "Timer rule (series link)"
/* Custom int settings definitions. */
customAutorecIntSettingDefs,
/* Values definitions for priorities. */
priorityValues,
/* Values definitions for lifetime. */
Expand Down Expand Up @@ -1089,6 +1198,8 @@ PVR_ERROR CTvheadend::GetTimerTypes(std::vector<kodi::addon::PVRTimerType>& type
TIMER_REPEATING_EPG_ATTRIBS,
/* Let Kodi generate the description. */
"",
/* Custom int settings definitions. */
customAutorecIntSettingDefs,
/* Values definitions for priorities. */
priorityValues,
/* Values definitions for lifetime. */
Expand Down Expand Up @@ -1148,6 +1259,13 @@ bool CTvheadend::CreateTimer(const Recording& tvhTmr, kodi::addon::PVRTimer& tmr
: tmr.GetTimerType() == TIMER_ONCE_CREATED_BY_AUTOREC
? m_autoRecordings.GetTimerIntIdFromStringId(tvhTmr.GetAutorecId())
: 0);

/* Custom integer props. Currently only DVR configuration */
const CustomTimerProperties props(m_conn->GetProtocol(), m_dvrConfigs);
const std::vector<kodi::addon::PVRIntKeyValuePair> customProps{
props.GetProperties(tvhTmr, {CUSTOM_PROP_ID_DVR_CONFIGURATION})};
tmr.SetCustomIntProperties(std::move(customProps));

return true;
}

Expand Down Expand Up @@ -1231,6 +1349,10 @@ PVR_ERROR CTvheadend::AddTimer(const kodi::addon::PVRTimer& timer)
LifetimeMapper::KodiToTvh(timer.GetLifetime())); // remove from disk
htsmsg_add_u32(m, "priority", timer.GetPriority());

/* Custom integer props. */
const CustomTimerProperties props(m_conn->GetProtocol(), m_dvrConfigs);
props.AppendPropertiesToHTSPMessage(timer.GetCustomIntProperties(), m);

/* Send and Wait */
{
std::unique_lock<std::recursive_mutex> lock(m_conn->Mutex());
Expand Down Expand Up @@ -1346,6 +1468,10 @@ PVR_ERROR CTvheadend::UpdateTimer(const kodi::addon::PVRTimer& timer)
LifetimeMapper::KodiToTvh(timer.GetLifetime())); // remove from disk
htsmsg_add_u32(m, "priority", timer.GetPriority());

/* Custom integer props. */
const CustomTimerProperties props(m_conn->GetProtocol(), m_dvrConfigs);
props.AppendPropertiesToHTSPMessage(timer.GetCustomIntProperties(), m);

return SendDvrUpdate(m);
}
else if (timer.GetTimerType() == TIMER_REPEATING_MANUAL)
Expand Down Expand Up @@ -1539,6 +1665,9 @@ bool CTvheadend::Connected(std::unique_lock<std::recursive_mutex>& lock)
/* Query the server for available streaming profiles */
QueryAvailableProfiles(lock);

/* Query the server for available DVR configurations */
QueryAvailableDvrConfigurations(lock);

/* Show a notification if the profile is not available */
const std::string streamingProfile = m_settings->GetStreamingProfile();

Expand Down Expand Up @@ -2686,6 +2815,10 @@ void CTvheadend::ParseRecordingAddOrUpdate(htsmsg_t* msg, bool bAdd)
rec.SetRatingSource(str);
}

str = htsmsg_get_str(msg, "configId");
if (str)
rec.SetConfigUuid(str);

if (m_conn->GetProtocol() >= 32)
{
if (rec.GetDescription().empty() && !rec.GetSubtitle().empty())
Expand Down
Loading

0 comments on commit eab6971

Please sign in to comment.