Skip to content

Commit

Permalink
Merge pull request #831 from phunkyfish/catchup-id-live-urls
Browse files Browse the repository at this point in the history
Catchup id live urls
  • Loading branch information
phunkyfish authored Feb 13, 2024
2 parents 1b3f277 + c7231ac commit 19a9813
Show file tree
Hide file tree
Showing 6 changed files with 84 additions and 8 deletions.
2 changes: 1 addition & 1 deletion pvr.iptvsimple/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.iptvsimple"
version="21.7.2"
version="21.8.0"
name="IPTV Simple Client"
provider-name="nightik and Ross Nicholson">
<requires>@ADDON_DEPENDS@
Expand Down
9 changes: 9 additions & 0 deletions pvr.iptvsimple/changelog.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,12 @@
v2.8.0
- Support catchup-id for live URLs where possible
- Support the Y, m, d, H, M, S specifiers for live URLs, useful for plugins and debugging
- Enable Play from EPG in Live TV mode setting for Catchup VOD
- Only set connection-timeout for connection manager if it's not NFS
- Support the duration specifier for live URLs, useful for plugins and debugging
- Support the all specifier for live URLs, useful for plugins and debugging
- Fix timezone shift not applied for start time for live URLs

v21.7.2
- Only reset the catchup state if not playing a timeshifted EPG tag

Expand Down
2 changes: 1 addition & 1 deletion src/IptvSimple.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -308,7 +308,7 @@ PVR_ERROR IptvSimple::GetEPGTagStreamProperties(const kodi::addon::PVREPGTag& ta
Logger::Log(LEVEL_DEBUG, "%s - GetPlayEpgAsLive is %s", __FUNCTION__, m_settings->CatchupPlayEpgAsLive() ? "enabled" : "disabled");

std::map<std::string, std::string> catchupProperties;
if (m_settings->CatchupPlayEpgAsLive() && m_currentChannel.CatchupSupportsTimeshifting())
if (m_settings->CatchupPlayEpgAsLive() && (m_currentChannel.CatchupSupportsTimeshifting() || m_currentChannel.GetCatchupMode() == CatchupMode::VOD))
{
m_catchupController.ProcessEPGTagForTimeshiftedPlayback(tag, m_currentChannel, catchupProperties);
}
Expand Down
68 changes: 64 additions & 4 deletions src/iptvsimple/CatchupController.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -38,9 +38,11 @@ void CatchupController::ProcessChannelForPlayback(const Channel& channel, std::m
// Anything from here is live!
m_playbackIsVideo = false; // TODO: possible time jitter on UI as this will effect get stream times

//Always get the live EPG entry
EpgEntry* liveEpgEntry = GetLiveEPGEntry(channel);

if (!m_fromTimeshiftedEpgTagCall)
{
EpgEntry* liveEpgEntry = GetLiveEPGEntry(channel);
if (m_controlsLiveStream && liveEpgEntry && !m_settings->CatchupOnlyOnFinishedProgrammes())
{
// Live timeshifting support with EPG entry
Expand All @@ -55,6 +57,13 @@ void CatchupController::ProcessChannelForPlayback(const Channel& channel, std::m
m_programmeCatchupId.clear();
m_catchupStartTime = 0;
m_catchupEndTime = 0;

// Not from timeshifted EPG so safe to set the catchup ID here
if (!m_controlsLiveStream && liveEpgEntry)
{
m_programmeCatchupId = liveEpgEntry->GetCatchupId();
UpdateProgrammeFrom(*liveEpgEntry, channel.GetTvgShift());
}
}
}

Expand Down Expand Up @@ -132,6 +141,9 @@ void CatchupController::ProcessEPGTagForTimeshiftedPlayback(const kodi::addon::P

m_timeshiftBufferStartTime = 0;
m_timeshiftBufferOffset = 0;

if (m_settings->CatchupPlayEpgAsLive())
catchupProperties.insert({PVR_STREAM_PROPERTY_EPGPLAYBACKASLIVE, "true"});
}

m_fromTimeshiftedEpgTagCall = true;
Expand Down Expand Up @@ -393,9 +405,11 @@ std::string FormatDateTime(time_t timeStart, time_t duration, const std::string
return formattedUrl;
}

std::string FormatDateTimeNowOnly(const std::string &urlFormatString, int timezoneShiftSecs)
std::string FormatDateTimeNowOnly(const std::string &urlFormatString, int timezoneShiftSecs, int timeStart, int duration)
{
std::string formattedUrl = urlFormatString;

timeStart -= timezoneShiftSecs;
const time_t timeNow = std::time(0) - timezoneShiftSecs;
std::tm dateTimeNow = SafeLocaltime(timeNow);

Expand All @@ -406,6 +420,46 @@ std::string FormatDateTimeNowOnly(const std::string &urlFormatString, int timezo
FormatTime("now", &dateTimeNow, formattedUrl, true);
FormatTime("timestamp", &dateTimeNow, formattedUrl, true);

// If we have the start time for a programme also process those specifiers
// These can be useful for plugins that don't call ffmpegdirect and instead
// play EPG as live for catchup="vod"
if (timeStart > 0)
{
std::tm dateTimeStart = SafeLocaltime(timeStart);

const time_t timeEnd = timeStart + duration;
std::tm dateTimeEnd = SafeLocaltime(timeEnd);

FormatTime('Y', &dateTimeStart, formattedUrl);
FormatTime('m', &dateTimeStart, formattedUrl);
FormatTime('d', &dateTimeStart, formattedUrl);
FormatTime('H', &dateTimeStart, formattedUrl);
FormatTime('M', &dateTimeStart, formattedUrl);
FormatTime('S', &dateTimeStart, formattedUrl);
FormatUtc("{utc}", timeStart, formattedUrl);
FormatUtc("${start}", timeStart, formattedUrl);
FormatUtc("{utcend}", timeStart + duration, formattedUrl);
FormatUtc("${end}", timeStart + duration, formattedUrl);
FormatUtc("{lutc}", timeNow, formattedUrl);
FormatUtc("${now}", timeNow, formattedUrl);
FormatUtc("${timestamp}", timeNow, formattedUrl);
FormatUtc("${duration}", duration, formattedUrl);
FormatUtc("{duration}", duration, formattedUrl);
FormatUnits("duration", duration, formattedUrl);
FormatUtc("${offset}", timeNow - timeStart, formattedUrl);
FormatUnits("offset", timeNow - timeStart, formattedUrl);

FormatTime("utc", &dateTimeStart, formattedUrl, false);
FormatTime("start", &dateTimeStart, formattedUrl, true);

FormatTime("utcend", &dateTimeEnd, formattedUrl, false);
FormatTime("end", &dateTimeEnd, formattedUrl, true);

FormatTime("lutc", &dateTimeNow, formattedUrl, false);
FormatTime("now", &dateTimeNow, formattedUrl, true);
FormatTime("timestamp", &dateTimeNow, formattedUrl, true);
}

Logger::Log(LEVEL_DEBUG, "%s - \"%s\"", __FUNCTION__, WebUtils::RedactUrl(formattedUrl).c_str());

return formattedUrl;
Expand Down Expand Up @@ -440,7 +494,7 @@ std::string BuildEpgTagUrl(time_t startTime, time_t duration, const Channel& cha
if ((startTime > 0 && offset < (timeNow - 5)) || (channel.IgnoreCatchupDays() && !programmeCatchupId.empty()))
startTimeUrl = FormatDateTime(offset - timezoneShiftSecs, duration, channel.GetCatchupSource());
else
startTimeUrl = FormatDateTimeNowOnly(channel.GetStreamURL(), timezoneShiftSecs);
startTimeUrl = FormatDateTimeNowOnly(channel.GetStreamURL(), timezoneShiftSecs, startTime, duration);

static const std::regex CATCHUP_ID_REGEX("\\{catchup-id\\}");
if (!programmeCatchupId.empty())
Expand Down Expand Up @@ -490,7 +544,13 @@ std::string CatchupController::GetCatchupUrl(const Channel& channel) const
std::string CatchupController::ProcessStreamUrl(const Channel& channel) const
{
//We only process current time timestamps specifiers in this case
return FormatDateTimeNowOnly(channel.GetStreamURL(), m_epg.GetEPGTimezoneShiftSecs(channel) + channel.GetCatchupCorrectionSecs());
std::string processedUrl = FormatDateTimeNowOnly(channel.GetStreamURL(), m_epg.GetEPGTimezoneShiftSecs(channel) + channel.GetCatchupCorrectionSecs(), m_programmeStartTime, m_programmeEndTime - m_programmeStartTime);

static const std::regex CATCHUP_ID_REGEX("\\{catchup-id\\}");
if (!m_programmeCatchupId.empty())
processedUrl = std::regex_replace(processedUrl, CATCHUP_ID_REGEX, m_programmeCatchupId);

return processedUrl;
}

std::string CatchupController::GetStreamTestUrl(const Channel& channel, bool fromEpg) const
Expand Down
9 changes: 7 additions & 2 deletions src/iptvsimple/utilities/WebUtils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,11 @@ bool WebUtils::IsHttpUrl(const std::string& url)
return StringUtils::StartsWith(url, HTTP_PREFIX) || StringUtils::StartsWith(url, HTTPS_PREFIX);
}

bool WebUtils::IsNfsUrl(const std::string& url)
{
return StringUtils::StartsWith(url, NFS_PREFIX);
}

std::string WebUtils::RedactUrl(const std::string& url)
{
std::string redactedUrl = url;
Expand Down Expand Up @@ -145,8 +150,8 @@ bool WebUtils::Check(const std::string& strURL, int connectionTimeoutSecs, bool
return false;
}

fileHandle.CURLAddOption(ADDON_CURL_OPTION_PROTOCOL, "connection-timeout",
std::to_string(connectionTimeoutSecs));
if (!IsNfsUrl(strURL))
fileHandle.CURLAddOption(ADDON_CURL_OPTION_PROTOCOL, "connection-timeout", std::to_string(connectionTimeoutSecs));

if (!fileHandle.CURLOpen(ADDON_READ_NO_CACHE))
{
Expand Down
2 changes: 2 additions & 0 deletions src/iptvsimple/utilities/WebUtils.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ namespace iptvsimple
{
static const std::string HTTP_PREFIX = "http://";
static const std::string HTTPS_PREFIX = "https://";
static const std::string NFS_PREFIX = "nfs://";
static const std::string UDP_MULTICAST_PREFIX = "udp://@";
static const std::string RTP_MULTICAST_PREFIX = "rtp://@";

Expand All @@ -26,6 +27,7 @@ namespace iptvsimple
static bool IsEncoded(const std::string& value);
static std::string ReadFileContentsStartOnly(const std::string& url, int* httpCode);
static bool IsHttpUrl(const std::string& url);
static bool IsNfsUrl(const std::string& url);
static std::string RedactUrl(const std::string& url);
static bool Check(const std::string& url, int connectionTimeoutSecs, bool isLocalPath = false);
};
Expand Down

0 comments on commit 19a9813

Please sign in to comment.