Skip to content

Commit

Permalink
Add support for Autorec and Timerec properties "Start after" and "Sta…
Browse files Browse the repository at this point in the history
…rt before"
  • Loading branch information
ksooo committed Nov 6, 2024
1 parent 54ef280 commit b627738
Show file tree
Hide file tree
Showing 17 changed files with 144 additions and 305 deletions.
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.5.0"
version="22.6.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.6.0
- Add support for Autorec and Timerec properties "Start after" and "Start before"

v22.5.0
- PVR Add-on API v9.2.0
- Add support for multiple recorded streams at a time (used by Kodi for thumbnail extraction)
Expand Down
24 changes: 0 additions & 24 deletions pvr.hts/resources/instance-settings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -166,30 +166,6 @@

<category id="timer" label="30010" help="-1">
<group id="1" label="30050">
<setting id="autorec_approxtime" type="integer" label="30051" help="-1">
<level>0</level>
<default>0</default> <!-- Strict (start time + end time) -->
<constraints>
<options>
<option label="30052">0</option> <!-- Strict (start time + end time) -->
<option label="30053">1</option> <!-- Relaxed (start time +/- margin) -->
</options>
</constraints>
<control type="list" format="string" />
</setting>
<setting id="autorec_maxdiff" type="integer" label="30054" help="-1">
<level>0</level>
<default>15</default>
<constraints>
<minimum>0</minimum>
<step>5</step>
<maximum>120</maximum>
</constraints>
<dependencies>
<dependency type="enable" setting="autorec_approxtime">1</dependency>
</dependencies>
<control type="slider" format="integer" />
</setting>
<setting id="autorec_use_regex" type="boolean" label="30061" help="-1">
<level>0</level>
<default>false</default>
Expand Down
24 changes: 8 additions & 16 deletions pvr.hts/resources/language/resource.language.en_gb/strings.po
Original file line number Diff line number Diff line change
Expand Up @@ -88,22 +88,6 @@ msgctxt "#30050"
msgid "Auto recordings"
msgstr ""

msgctxt "#30051"
msgid "Start time window calculation"
msgstr ""

msgctxt "#30052"
msgid "Strict (start time + end time)"
msgstr ""

msgctxt "#30053"
msgid "Relaxed (start time +/- margin)"
msgstr ""

msgctxt "#30054"
msgid "Maximum start time margin (minutes)"
msgstr ""

msgctxt "#30055"
msgid "Default priority"
msgstr ""
Expand Down Expand Up @@ -516,3 +500,11 @@ msgstr ""
msgctxt "#30605"
msgid "(Default profile)"
msgstr ""

msgctxt "#30606"
msgid "Start after"
msgstr ""

msgctxt "#30607"
msgid "Start before"
msgstr ""
19 changes: 1 addition & 18 deletions src/Tvheadend.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1093,7 +1093,6 @@ PVR_ERROR CTvheadend::GetTimerTypes(std::vector<kodi::addon::PVRTimerType>& type
/* Attributes. */
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_WEEKDAYS | PVR_TIMER_TYPE_SUPPORTS_PRIORITY |
PVR_TIMER_TYPE_SUPPORTS_LIFETIME | PVR_TIMER_TYPE_SUPPORTS_RECORDING_FOLDERS,
/* Let Kodi generate the description. */
Expand All @@ -1113,19 +1112,11 @@ PVR_ERROR CTvheadend::GetTimerTypes(std::vector<kodi::addon::PVRTimerType>& type
{
unsigned int TIMER_REPEATING_SERIESLINK_ATTRIBS =
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_START_ANYTIME | PVR_TIMER_TYPE_SUPPORTS_WEEKDAYS |
PVR_TIMER_TYPE_SUPPORTS_CHANNELS | PVR_TIMER_TYPE_SUPPORTS_WEEKDAYS |
PVR_TIMER_TYPE_SUPPORTS_START_END_MARGIN | PVR_TIMER_TYPE_SUPPORTS_PRIORITY |
PVR_TIMER_TYPE_SUPPORTS_LIFETIME | PVR_TIMER_TYPE_SUPPORTS_RECORDING_FOLDERS |
PVR_TIMER_TYPE_SUPPORTS_ANY_CHANNEL | PVR_TIMER_TYPE_REQUIRES_EPG_SERIESLINK_ON_CREATE;

if (!m_settings->GetAutorecApproxTime())
{
/* We need the end time to represent the end of the tvh starting window */
TIMER_REPEATING_SERIESLINK_ATTRIBS |= PVR_TIMER_TYPE_SUPPORTS_END_TIME;
TIMER_REPEATING_SERIESLINK_ATTRIBS |= PVR_TIMER_TYPE_SUPPORTS_END_ANYTIME;
}

/* Repeating epg based - series link autorec */
types.emplace_back(TimerType(
/* Settings */
Expand All @@ -1147,19 +1138,11 @@ PVR_ERROR CTvheadend::GetTimerTypes(std::vector<kodi::addon::PVRTimerType>& type
unsigned int TIMER_REPEATING_EPG_ATTRIBS =
PVR_TIMER_TYPE_IS_REPEATING | PVR_TIMER_TYPE_SUPPORTS_ENABLE_DISABLE |
PVR_TIMER_TYPE_SUPPORTS_TITLE_EPG_MATCH | PVR_TIMER_TYPE_SUPPORTS_CHANNELS |
PVR_TIMER_TYPE_SUPPORTS_START_TIME | PVR_TIMER_TYPE_SUPPORTS_START_ANYTIME |
PVR_TIMER_TYPE_SUPPORTS_WEEKDAYS | PVR_TIMER_TYPE_SUPPORTS_START_END_MARGIN |
PVR_TIMER_TYPE_SUPPORTS_PRIORITY | PVR_TIMER_TYPE_SUPPORTS_LIFETIME |
PVR_TIMER_TYPE_SUPPORTS_RECORDING_FOLDERS | PVR_TIMER_TYPE_SUPPORTS_ANY_CHANNEL |
PVR_TIMER_TYPE_SUPPORTS_FULLTEXT_EPG_MATCH | PVR_TIMER_TYPE_SUPPORTS_RECORD_ONLY_NEW_EPISODES;

if (!m_settings->GetAutorecApproxTime())
{
/* We need the end time to represent the end of the tvh starting window */
TIMER_REPEATING_EPG_ATTRIBS |= PVR_TIMER_TYPE_SUPPORTS_END_TIME;
TIMER_REPEATING_EPG_ATTRIBS |= PVR_TIMER_TYPE_SUPPORTS_END_ANYTIME;
}

/* Repeating epg based - autorec */
types.emplace_back(TimerType(
/* Settings */
Expand Down
81 changes: 2 additions & 79 deletions src/tvheadend/AutoRecordings.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,8 @@ AutoRecordings::AutoRecordings(const std::shared_ptr<InstanceSettings>& settings
Profiles& dvrConfigs)
: m_settings(settings),
m_conn(conn),
m_customTimerProps({CUSTOM_PROP_ID_DVR_CONFIGURATION, CUSTOM_PROP_ID_AUTOREC_BROADCASTTYPE,
m_customTimerProps({CUSTOM_PROP_ID_SERIESREC_START, CUSTOM_PROP_ID_SERIESREC_STARTWINDOW,
CUSTOM_PROP_ID_AUTOREC_BROADCASTTYPE, CUSTOM_PROP_ID_DVR_CONFIGURATION,
CUSTOM_PROP_ID_DVR_COMMENT},
conn,
dvrConfigs)
Expand Down Expand Up @@ -69,23 +70,6 @@ void AutoRecordings::GetAutorecTimers(std::vector<kodi::addon::PVRTimer>& timers
tmr.SetClientIndex(rec.second.GetId());
tmr.SetClientChannelUid((rec.second.GetChannel() > 0) ? rec.second.GetChannel()
: PVR_TIMER_ANY_CHANNEL);
tmr.SetStartTime(rec.second.GetStart());
tmr.SetEndTime(rec.second.GetStop());
if (tmr.GetStartTime() == 0)
tmr.SetStartAnyTime(true);
if (tmr.GetEndTime() == 0)
tmr.SetEndAnyTime(true);

if (!tmr.GetStartAnyTime() && tmr.GetEndAnyTime())
tmr.SetEndTime(tmr.GetStartTime() + 60 * 60); // Nominal 1 hour duration
if (tmr.GetStartAnyTime() && !tmr.GetEndAnyTime())
tmr.SetStartTime(tmr.GetEndTime() - 60 * 60); // Nominal 1 hour duration
if (tmr.GetStartAnyTime() && tmr.GetEndAnyTime())
{
tmr.SetStartTime(std::time(nullptr)); // now
tmr.SetEndTime(tmr.GetStartTime() + 60 * 60); // Nominal 1 hour duration
}

if (rec.second.GetName().empty()) // timers created on backend may not contain a name
tmr.SetTitle(rec.second.GetTitle());
else
Expand Down Expand Up @@ -211,66 +195,6 @@ PVR_ERROR AutoRecordings::SendAutorecAddOrUpdate(const kodi::addon::PVRTimer& ti
if (timer.GetDirectory() != "/")
htsmsg_add_str(m, "directory", timer.GetDirectory().c_str());


/* bAutorecApproxTime enabled: => start time in kodi = approximate start time in tvh */
/* => 'approximate' = starting window / 2 */
/* */
/* bAutorecApproxTime disabled: => start time in kodi = begin of starting window in tvh */
/* => end time in kodi = end of starting window in tvh */
if (m_settings->GetAutorecApproxTime())
{
/* Not sending causes server to set start and startWindow to any time */
if (timer.GetStartTime() > 0 && !timer.GetStartAnyTime())
{
time_t startTime = timer.GetStartTime();
struct tm* tm_start = std::localtime(&startTime);
int32_t startWindowBegin =
tm_start->tm_hour * 60 + tm_start->tm_min - m_settings->GetAutorecMaxDiff();
int32_t startWindowEnd =
tm_start->tm_hour * 60 + tm_start->tm_min + m_settings->GetAutorecMaxDiff();

/* Past midnight correction */
if (startWindowBegin < 0)
startWindowBegin += (24 * 60);
if (startWindowEnd > (24 * 60))
startWindowEnd -= (24 * 60);

htsmsg_add_s32(m, "start", startWindowBegin);
htsmsg_add_s32(m, "startWindow", startWindowEnd);
}
else
{
htsmsg_add_s32(m, "start", -1);
htsmsg_add_s32(m, "startWindow", -1);
}
}
else
{
if (timer.GetStartTime() > 0 && !timer.GetStartAnyTime())
{
/* Exact start time (minutes from midnight). */
time_t startTime = timer.GetStartTime();
struct tm* tm_start = std::localtime(&startTime);
htsmsg_add_s32(m, "start", tm_start->tm_hour * 60 + tm_start->tm_min);
}
else
htsmsg_add_s32(
m, "start",
25 * 60); // -1 or not sending causes server to set start and startWindow to any time

if (timer.GetEndTime() > 0 && !timer.GetEndAnyTime())
{
/* Exact stop time (minutes from midnight). */
time_t endTime = timer.GetEndTime();
struct tm* tm_stop = std::localtime(&endTime);
htsmsg_add_s32(m, "startWindow", tm_stop->tm_hour * 60 + tm_stop->tm_min);
}
else
htsmsg_add_s32(
m, "startWindow",
25 * 60); // -1 or not sending causes server to set start and startWindow to any time
}

/* series link */
if (timer.GetTimerType() == TIMER_REPEATING_SERIESLINK)
htsmsg_add_str(m, "serieslinkUri", timer.GetSeriesLink().c_str());
Expand Down Expand Up @@ -341,7 +265,6 @@ bool AutoRecordings::ParseAutorecAddOrUpdate(htsmsg_t* msg, bool bAdd)

/* Locate/create entry */
AutoRecording& rec = m_autoRecordings[std::string(str)];
rec.SetSettings(m_settings);
rec.SetStringId(std::string(str));
rec.SetDirty(false);

Expand Down
98 changes: 98 additions & 0 deletions src/tvheadend/CustomTimerProperties.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -37,11 +37,22 @@ std::vector<kodi::addon::PVRSettingKeyValuePair> CustomTimerProperties::GetPrope
return customProps;
}

std::vector<kodi::addon::PVRSettingKeyValuePair> CustomTimerProperties::GetProperties(
const tvheadend::entity::TimeRecording& timerec) const
{
std::vector<kodi::addon::PVRSettingKeyValuePair> customProps;
GetCommonProperties(customProps, timerec);
GetSeriesCommonProperties(customProps, timerec);

return customProps;
}

std::vector<kodi::addon::PVRSettingKeyValuePair> CustomTimerProperties::GetProperties(
const tvheadend::entity::AutoRecording& autorec) const
{
std::vector<kodi::addon::PVRSettingKeyValuePair> customProps;
GetCommonProperties(customProps, autorec);
GetSeriesCommonProperties(customProps, autorec);

for (unsigned int propId : m_propIds)
{
Expand Down Expand Up @@ -96,6 +107,32 @@ void CustomTimerProperties::GetCommonProperties(
}
}

void CustomTimerProperties::GetSeriesCommonProperties(
std::vector<kodi::addon::PVRSettingKeyValuePair>& props,
const tvheadend::entity::SeriesRecordingBase& seriesrec) const
{
for (unsigned int propId : m_propIds)
{
switch (propId)
{
case CUSTOM_PROP_ID_SERIESREC_START:
{
// Start
props.emplace_back(CUSTOM_PROP_ID_SERIESREC_START, seriesrec.GetStartWindowBegin());
break;
}
case CUSTOM_PROP_ID_SERIESREC_STARTWINDOW:
{
// Start window
props.emplace_back(CUSTOM_PROP_ID_SERIESREC_STARTWINDOW, seriesrec.GetStartWindowEnd());
break;
}
default:
break;
}
}
}

const std::vector<kodi::addon::PVRSettingDefinition> CustomTimerProperties::GetSettingDefinitions()
const
{
Expand All @@ -105,6 +142,28 @@ const std::vector<kodi::addon::PVRSettingDefinition> CustomTimerProperties::GetS
{
switch (propId)
{
case CUSTOM_PROP_ID_SERIESREC_START:
{
int defaultValue{SERIESREC_START_ANYTIME};
const std::vector<kodi::addon::PVRTypeIntValue> startValues{
GetPossibleValues(CUSTOM_PROP_ID_SERIESREC_START, defaultValue)};
ret.emplace_back(CreateSettingDefinition(CUSTOM_PROP_ID_SERIESREC_START,
30606, // Start after
startValues, defaultValue,
PVR_SETTING_READONLY_CONDITION_NONE));
break;
}
case CUSTOM_PROP_ID_SERIESREC_STARTWINDOW:
{
int defaultValue{SERIESREC_START_ANYTIME};
const std::vector<kodi::addon::PVRTypeIntValue> startWindowValues{
GetPossibleValues(CUSTOM_PROP_ID_SERIESREC_STARTWINDOW, defaultValue)};
ret.emplace_back(CreateSettingDefinition(CUSTOM_PROP_ID_SERIESREC_STARTWINDOW,
30607, // Start before
startWindowValues, defaultValue,
PVR_SETTING_READONLY_CONDITION_NONE));
break;
}
case CUSTOM_PROP_ID_AUTOREC_BROADCASTTYPE:
{
// Broadcast type
Expand Down Expand Up @@ -166,6 +225,33 @@ const std::vector<kodi::addon::PVRTypeIntValue> CustomTimerProperties::GetPossib
{
switch (propId)
{
case CUSTOM_PROP_ID_SERIESREC_START:
case CUSTOM_PROP_ID_SERIESREC_STARTWINDOW:
{
// Start, Start window

// Any : SERIESREC_START_ANYTIME (-1)
// 0:00 : 0
// 0:10 : 0 * 60 + 10
// ...
// 23:50 : 23 * 60 + 50
static std::vector<kodi::addon::PVRTypeIntValue> startValues{};
if (startValues.empty())
{
startValues.reserve(24 * 60 / 10 + 1);
startValues.emplace_back(
kodi::addon::PVRTypeIntValue(SERIESREC_START_ANYTIME,
kodi::addon::GetLocalizedString(30601))); // Any
for (int i = 0; i < 24 * 60; i += 10)
{
const std::string hours{std::to_string(i / 60)};
const std::string minutes{(i % 60) == 0 ? "00" : std::to_string(i % 60)};
startValues.emplace_back(kodi::addon::PVRTypeIntValue(i, hours + ":" + minutes));
}
}
defaultValue = SERIESREC_START_ANYTIME; // Any
return startValues;
}
case CUSTOM_PROP_ID_AUTOREC_BROADCASTTYPE:
{
// Broadcast type
Expand Down Expand Up @@ -241,6 +327,18 @@ void CustomTimerProperties::AppendPropertiesToHTSPMessage(
{
switch (prop.GetKey())
{
case CUSTOM_PROP_ID_SERIESREC_START:
{
// Start
htsmsg_add_s32(msg, "start", prop.GetIntValue());
break;
}
case CUSTOM_PROP_ID_SERIESREC_STARTWINDOW:
{
// Start window
htsmsg_add_s32(msg, "startWindow", prop.GetIntValue());
break;
}
case CUSTOM_PROP_ID_AUTOREC_BROADCASTTYPE:
{
// Broadcast type
Expand Down
Loading

0 comments on commit b627738

Please sign in to comment.