Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for catchup source for the latest programme #815

Open
wants to merge 2 commits into
base: Omega
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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.6.0"
version="21.7.1"
name="IPTV Simple Client"
provider-name="nightik and Ross Nicholson">
<requires>@ADDON_DEPENDS@
Expand Down
3 changes: 3 additions & 0 deletions pvr.iptvsimple/changelog.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
v21.7.1
- Add support for catchup source for the latest programme

v21.6.0
- Only reset channel group list when a channel URL is read from M3U Playlist
- Modify EXTGRP behaviour so it is a begin directive for channels groups, i.e. it carries across channels unless reset by an empty EXTGRP directive or any group-title tag for a EXTINF channel directive
Expand Down
2 changes: 1 addition & 1 deletion src/IptvSimple.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -324,7 +324,7 @@ PVR_ERROR IptvSimple::IsEPGTagPlayable(const kodi::addon::PVREPGTag& tag, bool&
bIsPlayable = bIsPlayable &&
tag.GetStartTime() < now &&
tag.GetStartTime() >= (now - static_cast<time_t>(channel.GetCatchupDaysInSeconds())) &&
(!m_settings->CatchupOnlyOnFinishedProgrammes() || tag.GetEndTime() < now);
((!m_settings->CatchupOnlyOnFinishedProgrammes() || !channel.GetCatchupLatestSource().empty()) || tag.GetEndTime() < now);
}

return PVR_ERROR_NO_ERROR;
Expand Down
30 changes: 21 additions & 9 deletions src/iptvsimple/CatchupController.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,13 +31,16 @@ void CatchupController::ProcessChannelForPlayback(const Channel& channel, std::m
{
StreamType streamType = StreamTypeLookup(channel);

bool isLiveEntry = false;

// Anything from here is live!
m_playbackIsVideo = false; // TODO: possible time jitter on UI as this will effect get stream times

if (!m_fromEpgTag || m_controlsLiveStream)
{
EpgEntry* liveEpgEntry = GetLiveEPGEntry(channel);
if (m_controlsLiveStream && liveEpgEntry && !m_settings->CatchupOnlyOnFinishedProgrammes())
isLiveEntry = liveEpgEntry != nullptr;
if (m_controlsLiveStream && liveEpgEntry && (!m_settings->CatchupOnlyOnFinishedProgrammes() || !channel.GetCatchupLatestSource().empty()))
{
// Live timeshifting support with EPG entry
UpdateProgrammeFrom(*liveEpgEntry, channel.GetTvgShift());
Expand Down Expand Up @@ -84,7 +87,7 @@ void CatchupController::ProcessChannelForPlayback(const Channel& channel, std::m
// TODO: Need a method of updating an inputstream if already running such as web call to stream etc.
// this will avoid inputstream restarts which are expensive, may be better placed in client.cpp
// this also mean knowing when a stream has stopped
SetCatchupInputStreamProperties(true, channel, catchupProperties, streamType);
SetCatchupInputStreamProperties(true, channel, catchupProperties, streamType, isLiveEntry);
}
}

Expand All @@ -95,6 +98,8 @@ void CatchupController::ProcessEPGTagForTimeshiftedPlayback(const kodi::addon::P
if (epgEntry)
m_programmeCatchupId = epgEntry->GetCatchupId();

bool isLiveEntry = epgEntry == GetLiveEPGEntry(channel);

StreamType streamType = StreamTypeLookup(channel, true);

if (m_controlsLiveStream)
Expand All @@ -116,7 +121,7 @@ void CatchupController::ProcessEPGTagForTimeshiftedPlayback(const kodi::addon::P
// TODO: Need a method of updating an inputstream if already running such as web call to stream etc.
// this will avoid inputstream restarts which are expensive, may be better placed in client.cpp
// this also mean knowing when a stream has stopped
SetCatchupInputStreamProperties(true, channel, catchupProperties, streamType);
SetCatchupInputStreamProperties(true, channel, catchupProperties, streamType, isLiveEntry);
}
else
{
Expand All @@ -138,6 +143,8 @@ void CatchupController::ProcessEPGTagForVideoPlayback(const kodi::addon::PVREPGT
if (epgEntry)
m_programmeCatchupId = epgEntry->GetCatchupId();

bool isLiveEntry = epgEntry == GetLiveEPGEntry(channel);

StreamType streamType = StreamTypeLookup(channel, true);

if (m_controlsLiveStream)
Expand All @@ -161,7 +168,7 @@ void CatchupController::ProcessEPGTagForVideoPlayback(const kodi::addon::PVREPGT
// TODO: Need a method of updating an inputstream if already running such as web call to stream etc.
// this will avoid inputstream restarts which are expensive, may be better placed in client.cpp
// this also mean knowing when a stream has stopped
SetCatchupInputStreamProperties(false, channel, catchupProperties, streamType);
SetCatchupInputStreamProperties(false, channel, catchupProperties, streamType, isLiveEntry);
}
else
{
Expand All @@ -179,7 +186,7 @@ void CatchupController::ProcessEPGTagForVideoPlayback(const kodi::addon::PVREPGT
m_playbackIsVideo = true;
}

void CatchupController::SetCatchupInputStreamProperties(bool playbackAsLive, const Channel& channel, std::map<std::string, std::string>& catchupProperties, const StreamType& streamType)
void CatchupController::SetCatchupInputStreamProperties(bool playbackAsLive, const Channel& channel, std::map<std::string, std::string>& catchupProperties, const StreamType& streamType, bool isLiveEntry)
{
catchupProperties.insert({PVR_STREAM_PROPERTY_EPGPLAYBACKASLIVE, playbackAsLive ? "true" : "false"});

Expand All @@ -189,7 +196,7 @@ void CatchupController::SetCatchupInputStreamProperties(bool playbackAsLive, con

catchupProperties.insert({"inputstream.ffmpegdirect.default_url", channel.GetStreamURL()});
catchupProperties.insert({"inputstream.ffmpegdirect.playback_as_live", playbackAsLive ? "true" : "false"});
catchupProperties.insert({"inputstream.ffmpegdirect.catchup_url_format_string", GetCatchupUrlFormatString(channel)});
catchupProperties.insert({"inputstream.ffmpegdirect.catchup_url_format_string", GetCatchupUrlFormatString(channel, isLiveEntry)});
catchupProperties.insert({"inputstream.ffmpegdirect.catchup_buffer_start_time", std::to_string(m_catchupStartTime)});
catchupProperties.insert({"inputstream.ffmpegdirect.catchup_buffer_end_time", std::to_string(m_catchupEndTime)});
catchupProperties.insert({"inputstream.ffmpegdirect.catchup_buffer_offset", std::to_string(m_timeshiftBufferOffset)});
Expand All @@ -205,7 +212,7 @@ void CatchupController::SetCatchupInputStreamProperties(bool playbackAsLive, con

Logger::Log(LEVEL_DEBUG, "default_url - %s", WebUtils::RedactUrl(channel.GetStreamURL()).c_str());
Logger::Log(LEVEL_DEBUG, "playback_as_live - %s", playbackAsLive ? "true" : "false");
Logger::Log(LEVEL_DEBUG, "catchup_url_format_string - %s", WebUtils::RedactUrl(GetCatchupUrlFormatString(channel)).c_str());
Logger::Log(LEVEL_DEBUG, "catchup_url_format_string - %s", WebUtils::RedactUrl(GetCatchupUrlFormatString(channel, isLiveEntry)).c_str());
Logger::Log(LEVEL_DEBUG, "catchup_buffer_start_time - %s", std::to_string(m_catchupStartTime).c_str());
Logger::Log(LEVEL_DEBUG, "catchup_buffer_end_time - %s", std::to_string(m_catchupEndTime).c_str());
Logger::Log(LEVEL_DEBUG, "catchup_buffer_offset - %s", std::to_string(m_timeshiftBufferOffset).c_str());
Expand Down Expand Up @@ -437,10 +444,15 @@ std::string BuildEpgTagUrl(time_t startTime, time_t duration, const Channel& cha

} // unnamed namespace

std::string CatchupController::GetCatchupUrlFormatString(const Channel& channel) const
std::string CatchupController::GetCatchupUrlFormatString(const Channel& channel, bool isLiveEntry) const
{
if (m_catchupStartTime > 0)
return channel.GetCatchupSource();
{
if (isLiveEntry && !channel.GetCatchupLatestSource().empty())
return channel.GetCatchupLatestSource();
else
return channel.GetCatchupSource();
}

return "";
}
Expand Down
4 changes: 2 additions & 2 deletions src/iptvsimple/CatchupController.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ namespace iptvsimple
void ProcessEPGTagForTimeshiftedPlayback(const kodi::addon::PVREPGTag& epgTag, const data::Channel& channel, std::map<std::string, std::string>& catchupProperties);
void ProcessEPGTagForVideoPlayback(const kodi::addon::PVREPGTag& epgTag, const data::Channel& channel, std::map<std::string, std::string>& catchupProperties);

std::string GetCatchupUrlFormatString(const data::Channel& channel) const;
std::string GetCatchupUrlFormatString(const data::Channel& channel, bool isLiveEntry) const;
std::string GetCatchupUrl(const data::Channel& channel) const;
std::string ProcessStreamUrl(const data::Channel& channel) const;

Expand All @@ -43,7 +43,7 @@ namespace iptvsimple

private:
data::EpgEntry* GetLiveEPGEntry(const iptvsimple::data::Channel& myChannel);
void SetCatchupInputStreamProperties(bool playbackAsLive, const iptvsimple::data::Channel& channel, std::map<std::string, std::string>& catchupProperties, const StreamType& streamType);
void SetCatchupInputStreamProperties(bool playbackAsLive, const iptvsimple::data::Channel& channel, std::map<std::string, std::string>& catchupProperties, const StreamType& streamType, bool isLiveEntry);
StreamType StreamTypeLookup(const data::Channel& channel, bool fromEpg = false);
std::string GetStreamTestUrl(const data::Channel& channel, bool fromEpg) const;
std::string GetStreamKey(const data::Channel& channel, bool fromEpg) const;
Expand Down
3 changes: 3 additions & 0 deletions src/iptvsimple/PlaylistLoader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -330,6 +330,7 @@ std::string PlaylistLoader::ParseIntoChannel(const std::string& line, Channel& c
std::string strCatchupDays = ReadMarkerValue(infoLine, CATCHUP_DAYS);
std::string strTvgRec = ReadMarkerValue(infoLine, TVG_INFO_REC);
std::string strCatchupSource = ReadMarkerValue(infoLine, CATCHUP_SOURCE);
std::string strCatchupLatestSource = ReadMarkerValue(infoLine, CATCHUP_LATEST_SOURCE);
std::string strCatchupSiptv = ReadMarkerValue(infoLine, CATCHUP_SIPTV);
std::string strCatchupCorrection = ReadMarkerValue(infoLine, CATCHUP_CORRECTION);
std::string strProviderName = ReadMarkerValue(infoLine, PROVIDER);
Expand All @@ -343,6 +344,7 @@ std::string PlaylistLoader::ParseIntoChannel(const std::string& line, Channel& c

kodi::UnknownToUTF8(strTvgName, strTvgName);
kodi::UnknownToUTF8(strCatchupSource, strCatchupSource);
kodi::UnknownToUTF8(strCatchupLatestSource, strCatchupLatestSource);

// Some providers use a 'catchup-type' tag instead of 'catchup'
if (strCatchup.empty())
Expand Down Expand Up @@ -388,6 +390,7 @@ std::string PlaylistLoader::ParseIntoChannel(const std::string& line, Channel& c
// If we still don't have a value use the header supplied value if there is one
if (strCatchupSource.empty() && !m_m3uHeaderStrings.m_catchupSource.empty())
strCatchupSource = m_m3uHeaderStrings.m_catchupSource;
channel.SetCatchupLatestSource(strCatchupLatestSource);
channel.SetTvgShift(static_cast<int>(tvgShiftDecimal * 3600.0));
channel.SetRadio(isRadio);
if (m_settings->GetLogoPathType() == PathType::LOCAL_PATH && m_settings->UseLocalLogosOnlyIgnoreM3U())
Expand Down
1 change: 1 addition & 0 deletions src/iptvsimple/PlaylistLoader.h
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ namespace iptvsimple
static const std::string CATCHUP_TYPE = "catchup-type=";
static const std::string CATCHUP_DAYS = "catchup-days=";
static const std::string CATCHUP_SOURCE = "catchup-source=";
static const std::string CATCHUP_LATEST_SOURCE = "catchup-latest-source=";
static const std::string CATCHUP_SIPTV = "timeshift=";
static const std::string CATCHUP_CORRECTION = "catchup-correction=";
static const std::string PROVIDER = "provider=";
Expand Down
2 changes: 2 additions & 0 deletions src/iptvsimple/data/Channel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ void Channel::UpdateTo(Channel& left) const
left.m_catchupMode = m_catchupMode;
left.m_catchupDays = m_catchupDays;
left.m_catchupSource = m_catchupSource;
left.m_catchupLatestSource = m_catchupLatestSource;
left.m_isCatchupTSStream = m_isCatchupTSStream;
left.m_catchupSupportsTimeshifting = m_catchupSupportsTimeshifting;
left.m_catchupSourceTerminates = m_catchupSourceTerminates;
Expand Down Expand Up @@ -104,6 +105,7 @@ void Channel::Reset()
m_catchupMode = CatchupMode::DISABLED;
m_catchupDays = 0;
m_catchupSource.clear();
m_catchupLatestSource.clear();
m_catchupSupportsTimeshifting = false;
m_catchupSourceTerminates = false;
m_catchupGranularitySeconds = 1;
Expand Down
6 changes: 5 additions & 1 deletion src/iptvsimple/data/Channel.h
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ namespace iptvsimple
m_channelNumber(c.GetChannelNumber()), m_subChannelNumber(c.GetSubChannelNumber()),
m_encryptionSystem(c.GetEncryptionSystem()), m_tvgShift(c.GetTvgShift()), m_channelName(c.GetChannelName()),
m_iconPath(c.GetIconPath()), m_streamURL(c.GetStreamURL()), m_hasCatchup(c.HasCatchup()),
m_catchupMode(c.GetCatchupMode()), m_catchupDays(c.GetCatchupDays()), m_catchupSource(c.GetCatchupSource()),
m_catchupMode(c.GetCatchupMode()), m_catchupDays(c.GetCatchupDays()), m_catchupSource(c.GetCatchupSource()), m_catchupLatestSource(c.GetCatchupLatestSource()),
m_isCatchupTSStream(c.IsCatchupTSStream()), m_catchupSupportsTimeshifting(c.CatchupSupportsTimeshifting()),
m_catchupSourceTerminates(c.CatchupSourceTerminates()), m_catchupGranularitySeconds(c.GetCatchupGranularitySeconds()),
m_catchupCorrectionSecs(c.GetCatchupCorrectionSecs()), m_tvgId(c.GetTvgId()), m_tvgName(c.GetTvgName()),
Expand Down Expand Up @@ -95,6 +95,9 @@ namespace iptvsimple
const std::string& GetCatchupSource() const { return m_catchupSource; }
void SetCatchupSource(const std::string& value) { m_catchupSource = value; }

const std::string& GetCatchupLatestSource() const { return m_catchupLatestSource; }
void SetCatchupLatestSource(const std::string& value) { m_catchupLatestSource = value; }

bool IsCatchupTSStream() const { return m_isCatchupTSStream; }
void SetCatchupTSStream(bool value) { m_isCatchupTSStream = value; }

Expand Down Expand Up @@ -161,6 +164,7 @@ namespace iptvsimple
CatchupMode m_catchupMode = CatchupMode::DISABLED;
int m_catchupDays = 0;
std::string m_catchupSource = "";
std::string m_catchupLatestSource = "";
bool m_isCatchupTSStream = false;
bool m_catchupSupportsTimeshifting = false;
bool m_catchupSourceTerminates = false;
Expand Down
Loading