@@ -38,9 +38,11 @@ void CatchupController::ProcessChannelForPlayback(const Channel& channel, std::m
3838 // Anything from here is live!
3939 m_playbackIsVideo = false ; // TODO: possible time jitter on UI as this will effect get stream times
4040
41+ // Always get the live EPG entry
42+ EpgEntry* liveEpgEntry = GetLiveEPGEntry (channel);
43+
4144 if (!m_fromTimeshiftedEpgTagCall)
4245 {
43- EpgEntry* liveEpgEntry = GetLiveEPGEntry (channel);
4446 if (m_controlsLiveStream && liveEpgEntry && !m_settings->CatchupOnlyOnFinishedProgrammes ())
4547 {
4648 // Live timeshifting support with EPG entry
@@ -55,6 +57,13 @@ void CatchupController::ProcessChannelForPlayback(const Channel& channel, std::m
5557 m_programmeCatchupId.clear ();
5658 m_catchupStartTime = 0 ;
5759 m_catchupEndTime = 0 ;
60+
61+ // Not from timeshifted EPG so safe to set the catchup ID here
62+ if (!m_controlsLiveStream && liveEpgEntry)
63+ {
64+ m_programmeCatchupId = liveEpgEntry->GetCatchupId ();
65+ UpdateProgrammeFrom (*liveEpgEntry, channel.GetTvgShift ());
66+ }
5867 }
5968 }
6069
@@ -132,6 +141,9 @@ void CatchupController::ProcessEPGTagForTimeshiftedPlayback(const kodi::addon::P
132141
133142 m_timeshiftBufferStartTime = 0 ;
134143 m_timeshiftBufferOffset = 0 ;
144+
145+ if (m_settings->CatchupPlayEpgAsLive ())
146+ catchupProperties.insert ({PVR_STREAM_PROPERTY_EPGPLAYBACKASLIVE, " true" });
135147 }
136148
137149 m_fromTimeshiftedEpgTagCall = true ;
@@ -393,9 +405,11 @@ std::string FormatDateTime(time_t timeStart, time_t duration, const std::string
393405 return formattedUrl;
394406}
395407
396- std::string FormatDateTimeNowOnly (const std::string &urlFormatString, int timezoneShiftSecs)
408+ std::string FormatDateTimeNowOnly (const std::string &urlFormatString, int timezoneShiftSecs, int timeStart, int duration )
397409{
398410 std::string formattedUrl = urlFormatString;
411+
412+ timeStart -= timezoneShiftSecs;
399413 const time_t timeNow = std::time (0 ) - timezoneShiftSecs;
400414 std::tm dateTimeNow = SafeLocaltime (timeNow);
401415
@@ -406,6 +420,46 @@ std::string FormatDateTimeNowOnly(const std::string &urlFormatString, int timezo
406420 FormatTime (" now" , &dateTimeNow, formattedUrl, true );
407421 FormatTime (" timestamp" , &dateTimeNow, formattedUrl, true );
408422
423+ // If we have the start time for a programme also process those specifiers
424+ // These can be useful for plugins that don't call ffmpegdirect and instead
425+ // play EPG as live for catchup="vod"
426+ if (timeStart > 0 )
427+ {
428+ std::tm dateTimeStart = SafeLocaltime (timeStart);
429+
430+ const time_t timeEnd = timeStart + duration;
431+ std::tm dateTimeEnd = SafeLocaltime (timeEnd);
432+
433+ FormatTime (' Y' , &dateTimeStart, formattedUrl);
434+ FormatTime (' m' , &dateTimeStart, formattedUrl);
435+ FormatTime (' d' , &dateTimeStart, formattedUrl);
436+ FormatTime (' H' , &dateTimeStart, formattedUrl);
437+ FormatTime (' M' , &dateTimeStart, formattedUrl);
438+ FormatTime (' S' , &dateTimeStart, formattedUrl);
439+ FormatUtc (" {utc}" , timeStart, formattedUrl);
440+ FormatUtc (" ${start}" , timeStart, formattedUrl);
441+ FormatUtc (" {utcend}" , timeStart + duration, formattedUrl);
442+ FormatUtc (" ${end}" , timeStart + duration, formattedUrl);
443+ FormatUtc (" {lutc}" , timeNow, formattedUrl);
444+ FormatUtc (" ${now}" , timeNow, formattedUrl);
445+ FormatUtc (" ${timestamp}" , timeNow, formattedUrl);
446+ FormatUtc (" ${duration}" , duration, formattedUrl);
447+ FormatUtc (" {duration}" , duration, formattedUrl);
448+ FormatUnits (" duration" , duration, formattedUrl);
449+ FormatUtc (" ${offset}" , timeNow - timeStart, formattedUrl);
450+ FormatUnits (" offset" , timeNow - timeStart, formattedUrl);
451+
452+ FormatTime (" utc" , &dateTimeStart, formattedUrl, false );
453+ FormatTime (" start" , &dateTimeStart, formattedUrl, true );
454+
455+ FormatTime (" utcend" , &dateTimeEnd, formattedUrl, false );
456+ FormatTime (" end" , &dateTimeEnd, formattedUrl, true );
457+
458+ FormatTime (" lutc" , &dateTimeNow, formattedUrl, false );
459+ FormatTime (" now" , &dateTimeNow, formattedUrl, true );
460+ FormatTime (" timestamp" , &dateTimeNow, formattedUrl, true );
461+ }
462+
409463 Logger::Log (LEVEL_DEBUG, " %s - \" %s\" " , __FUNCTION__, WebUtils::RedactUrl (formattedUrl).c_str ());
410464
411465 return formattedUrl;
@@ -440,7 +494,7 @@ std::string BuildEpgTagUrl(time_t startTime, time_t duration, const Channel& cha
440494 if ((startTime > 0 && offset < (timeNow - 5 )) || (channel.IgnoreCatchupDays () && !programmeCatchupId.empty ()))
441495 startTimeUrl = FormatDateTime (offset - timezoneShiftSecs, duration, channel.GetCatchupSource ());
442496 else
443- startTimeUrl = FormatDateTimeNowOnly (channel.GetStreamURL (), timezoneShiftSecs);
497+ startTimeUrl = FormatDateTimeNowOnly (channel.GetStreamURL (), timezoneShiftSecs, startTime, duration );
444498
445499 static const std::regex CATCHUP_ID_REGEX (" \\ {catchup-id\\ }" );
446500 if (!programmeCatchupId.empty ())
@@ -490,7 +544,13 @@ std::string CatchupController::GetCatchupUrl(const Channel& channel) const
490544std::string CatchupController::ProcessStreamUrl (const Channel& channel) const
491545{
492546 // We only process current time timestamps specifiers in this case
493- return FormatDateTimeNowOnly (channel.GetStreamURL (), m_epg.GetEPGTimezoneShiftSecs (channel) + channel.GetCatchupCorrectionSecs ());
547+ std::string processedUrl = FormatDateTimeNowOnly (channel.GetStreamURL (), m_epg.GetEPGTimezoneShiftSecs (channel) + channel.GetCatchupCorrectionSecs (), m_programmeStartTime, m_programmeEndTime - m_programmeStartTime);
548+
549+ static const std::regex CATCHUP_ID_REGEX (" \\ {catchup-id\\ }" );
550+ if (!m_programmeCatchupId.empty ())
551+ processedUrl = std::regex_replace (processedUrl, CATCHUP_ID_REGEX, m_programmeCatchupId);
552+
553+ return processedUrl;
494554}
495555
496556std::string CatchupController::GetStreamTestUrl (const Channel& channel, bool fromEpg) const
0 commit comments