Skip to content

Commit

Permalink
Merge pull request #795 from phunkyfish/fix-media-metadata-nexus
Browse files Browse the repository at this point in the history
Fix media metadata nexus
  • Loading branch information
phunkyfish authored Oct 23, 2023
2 parents 4f97b21 + 06d25b6 commit d26a305
Show file tree
Hide file tree
Showing 11 changed files with 115 additions and 35 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="20.11.0"
version="20.11.1"
name="IPTV Simple Client"
provider-name="nightik and Ross Nicholson">
<requires>@ADDON_DEPENDS@
Expand Down
6 changes: 6 additions & 0 deletions pvr.iptvsimple/changelog.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
v20.11.1
- EPG entry selection criteria for timezone shift calculation works for Media as well as channels
- Fix being able to disable media from settings
- Fix group name in recording path being disabled in addon settings when media is not enabled
- Add support for Genres for Media

v20.11.0
- M3U format specifier to override realtime processing in Kodi PVR where the stream should not be treated like VOD/Media in the UI
- Revert the support of Async connect that was causing Connection Lost error messages for users of this add-on
Expand Down
3 changes: 3 additions & 0 deletions pvr.iptvsimple/resources/instance-settings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -608,6 +608,9 @@
<option label="30159">2</option> <!-- USE_GROUP_TITLE_IF_NO_PATH -->
</options>
</constraints>
<dependencies>
<dependency type="enable" setting="mediaEnabled" operator="is">true</dependency>
</dependencies>
<control type="spinner" format="integer" />
</setting>
<setting id="mediaForcePlaylist" type="boolean" parent="mediaEnabled" label="30160" help="30807">
Expand Down
34 changes: 22 additions & 12 deletions src/iptvsimple/Epg.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ Epg::Epg(kodi::addon::CInstancePVRClient* client, Channels& channels, Media& med
{
MoveOldGenresXMLFileToNewLocation();
}

m_media.SetGenreMappings(m_genreMappings);
}

bool Epg::Init(int epgMaxPastDays, int epgMaxFutureDays)
Expand Down Expand Up @@ -83,7 +85,7 @@ void Epg::SetEPGMaxFutureDays(int epgMaxFutureDays)
m_epgMaxFutureDaysSeconds = DEFAULT_EPG_MAX_DAYS * 24 * 60 * 60;
}

bool Epg::LoadEPG(time_t start, time_t end)
bool Epg::LoadEPG(time_t epgWindowStart, time_t epgWindowEnd)
{
auto started = std::chrono::high_resolution_clock::now();
Logger::Log(LEVEL_DEBUG, "%s - EPG Load Start", __FUNCTION__);
Expand Down Expand Up @@ -125,7 +127,7 @@ bool Epg::LoadEPG(time_t start, time_t end)
if (!LoadChannelEpgs(rootElement))
return false;

LoadEpgEntries(rootElement, start, end);
LoadEpgEntries(rootElement, epgWindowStart, epgWindowEnd);

xmlDoc.reset();
}
Expand Down Expand Up @@ -283,7 +285,7 @@ bool Epg::LoadChannelEpgs(const xml_node& rootElement)
return true;
}

void Epg::LoadEpgEntries(const xml_node& rootElement, int start, int end)
void Epg::LoadEpgEntries(const xml_node& rootElement, int epgWindowStart, int epgWindowEnd)
{
int minShiftTime = m_epgTimeShift;
int maxShiftTime = m_epgTimeShift;
Expand All @@ -299,6 +301,14 @@ void Epg::LoadEpgEntries(const xml_node& rootElement, int start, int end)
if (channel.GetTvgShift() + m_epgTimeShift > maxShiftTime)
maxShiftTime = channel.GetTvgShift() + m_epgTimeShift;
}

for (const auto& mediaEntry : m_media.GetMediaEntryList())
{
if (mediaEntry.GetTvgShift() + m_epgTimeShift < minShiftTime)
minShiftTime = mediaEntry.GetTvgShift() + m_epgTimeShift;
if (mediaEntry.GetTvgShift() + m_epgTimeShift > maxShiftTime)
maxShiftTime = mediaEntry.GetTvgShift() + m_epgTimeShift;
}
}

ChannelEpg* channelEpg = nullptr;
Expand All @@ -317,7 +327,7 @@ void Epg::LoadEpgEntries(const xml_node& rootElement, int start, int end)
}

EpgEntry entry{m_settings};
if (entry.UpdateFrom(programmeNode, id, start, end, minShiftTime, maxShiftTime))
if (entry.UpdateFrom(programmeNode, id, epgWindowStart, epgWindowEnd, minShiftTime, maxShiftTime))
{
count++;

Expand Down Expand Up @@ -350,23 +360,23 @@ void Epg::ReloadEPG()
}
}

PVR_ERROR Epg::GetEPGForChannel(int channelUid, time_t start, time_t end, kodi::addon::PVREPGTagsResultSet& results)
PVR_ERROR Epg::GetEPGForChannel(int channelUid, time_t epgWindowStart, time_t epgWindowEnd, kodi::addon::PVREPGTagsResultSet& results)
{
for (const auto& myChannel : m_channels.GetChannelsList())
{
if (myChannel.GetUniqueId() != channelUid)
continue;

if (start > m_lastStart || end > m_lastEnd)
if (epgWindowStart > m_lastStart || epgWindowEnd > m_lastEnd)
{
// reload EPG for new time interval only
LoadEPG(start, end);
LoadEPG(epgWindowStart, epgWindowEnd);
{
MergeEpgDataIntoMedia();

// doesn't matter is epg loaded or not we shouldn't try to load it for same interval
m_lastStart = static_cast<int>(start);
m_lastEnd = static_cast<int>(end);
m_lastStart = static_cast<int>(epgWindowStart);
m_lastEnd = static_cast<int>(epgWindowEnd);
}
}

Expand All @@ -379,7 +389,7 @@ PVR_ERROR Epg::GetEPGForChannel(int channelUid, time_t start, time_t end, kodi::
for (auto& epgEntryPair : channelEpg->GetEpgEntries())
{
auto& epgEntry = epgEntryPair.second;
if ((epgEntry.GetEndTime() + shift) < start)
if ((epgEntry.GetEndTime() + shift) < epgWindowStart)
continue;

kodi::addon::PVREPGTag tag;
Expand All @@ -388,7 +398,7 @@ PVR_ERROR Epg::GetEPGForChannel(int channelUid, time_t start, time_t end, kodi::

results.Add(tag);

if ((epgEntry.GetStartTime() + shift) > end)
if ((epgEntry.GetStartTime() + shift) > epgWindowEnd)
break;
}

Expand Down Expand Up @@ -609,6 +619,6 @@ void Epg::MergeEpgDataIntoMedia()
// then return the first entry as matching. This is a common pattern
// for channel that only contain a single media item.
if (channelEpg && !channelEpg->GetEpgEntries().empty())
mediaEntry.UpdateFrom(channelEpg->GetEpgEntries().begin()->second);
mediaEntry.UpdateFrom(channelEpg->GetEpgEntries().begin()->second, m_genreMappings);
}
}
4 changes: 2 additions & 2 deletions src/iptvsimple/Epg.h
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ namespace iptvsimple

bool Init(int epgMaxPastDays, int epgMaxFutureDays);

PVR_ERROR GetEPGForChannel(int channelUid, time_t start, time_t end, kodi::addon::PVREPGTagsResultSet& results);
PVR_ERROR GetEPGForChannel(int channelUid, time_t epgWindowStart, time_t epgWindowEnd, kodi::addon::PVREPGTagsResultSet& results);
void SetEPGMaxPastDays(int epgMaxPastDays);
void SetEPGMaxFutureDays(int epgMaxFutureDays);
void Clear();
Expand All @@ -62,7 +62,7 @@ namespace iptvsimple
bool GetXMLTVFileWithRetries(std::string& data);
char* FillBufferFromXMLTVData(std::string& data, std::string& decompressedData);
bool LoadChannelEpgs(const pugi::xml_node& rootElement);
void LoadEpgEntries(const pugi::xml_node& rootElement, int start, int end);
void LoadEpgEntries(const pugi::xml_node& rootElement, int epgWindowStart, int epgWindowEnd);
bool LoadGenres();

void MergeEpgDataIntoMedia();
Expand Down
5 changes: 5 additions & 0 deletions src/iptvsimple/Media.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
#pragma once

#include "ChannelGroups.h"
#include "data/EpgGenre.h"
#include "data/MediaEntry.h"

#include <string>
Expand All @@ -31,13 +32,17 @@ namespace iptvsimple

std::vector<iptvsimple::data::MediaEntry>& GetMediaEntryList() { return m_media; }

void SetGenreMappings(std::vector<iptvsimple::data::EpgGenre>& genreMappings) { m_genreMappings = genreMappings; }

private:
data::MediaEntry GetMediaEntry(const std::string& mediaEntryId) const;
bool IsInVirtualMediaEntryFolder(const data::MediaEntry& mediaEntry) const;

std::vector<iptvsimple::data::MediaEntry> m_media;
std::unordered_map<std::string, iptvsimple::data::MediaEntry> m_mediaIdMap;

std::vector<iptvsimple::data::EpgGenre> m_genreMappings;

bool m_haveMediaTypes = false;

std::shared_ptr<iptvsimple::InstanceSettings> m_settings;
Expand Down
22 changes: 12 additions & 10 deletions src/iptvsimple/PlaylistLoader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -215,9 +215,19 @@ bool PlaylistLoader::LoadPlayList()
}
else if (line[0] != '#')
{
Logger::Log(LEVEL_DEBUG, "%s - Adding channel '%s' with URL: '%s'", __FUNCTION__, tmpChannel.GetChannelName().c_str(), line.c_str());
Logger::Log(LEVEL_DEBUG, "%s - Adding channel or Media Entry '%s' with URL: '%s'", __FUNCTION__, tmpChannel.GetChannelName().c_str(), line.c_str());

if ((isRealTime || overrideRealTime || !m_settings->IsMediaEnabled() || !m_settings->ShowVodAsRecordings()) && !isMediaEntry)
if (m_settings->IsMediaEnabled() &&
(isMediaEntry || (m_settings->ShowVodAsRecordings() && !isRealTime)))
{
MediaEntry entry = tmpMediaEntry;
entry.UpdateFrom(tmpChannel);
entry.SetStreamURL(line);

if (!m_media.AddMediaEntry(entry, currentChannelGroupIdList, m_channelGroups, channelHadGroups))
Logger::Log(LEVEL_DEBUG, "%s - Counld not add media entry as an entry with the same gnenerated unique ID already exists", __func__);
}
else
{
// There are cases where we want the stream to be represetned as a channel with live streaming disabled
// to allow features such as passthrough to work. We don't want this to be VOD as then it would be treated like media.
Expand All @@ -230,15 +240,7 @@ bool PlaylistLoader::LoadPlayList()

if (!m_channels.AddChannel(channel, currentChannelGroupIdList, m_channelGroups, channelHadGroups))
Logger::Log(LEVEL_DEBUG, "%s - Not adding channel '%s' as only channels with groups are supported for %s channels per add-on settings", __func__, tmpChannel.GetChannelName().c_str(), channel.IsRadio() ? "radio" : "tv");
}
else // We have media
{
MediaEntry entry = tmpMediaEntry;
entry.UpdateFrom(tmpChannel);
entry.SetStreamURL(line);

if (!m_media.AddMediaEntry(entry, currentChannelGroupIdList, m_channelGroups, channelHadGroups))
Logger::Log(LEVEL_DEBUG, "%s - Counld not add media entry as an entry with the same gnenerated unique ID already exists", __func__);
}

tmpChannel.Reset();
Expand Down
23 changes: 16 additions & 7 deletions src/iptvsimple/data/EpgEntry.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -186,31 +186,40 @@ int ParseStarRating(const std::string& starRatingString)
return static_cast<int>(std::round(starRating));
}

bool FirstRun(int start, int end)
{
return start == 0 && end == 0;
}

} // unnamed namespace

bool EpgEntry::UpdateFrom(const xml_node& programmeNode, const std::string& id,
int start, int end, int minShiftTime, int maxShiftTime)
int epgWindowsStart, int epgWindowsEnd, int minShiftTime, int maxShiftTime)
{
std::string strStart, strStop;
if (!GetAttributeValue(programmeNode, "start", strStart) || !GetAttributeValue(programmeNode, "stop", strStop))
return false;

long long tmpStart = ParseDateTime(strStart);
long long tmpEnd = ParseDateTime(strStop);
long long programmeStart = ParseDateTime(strStart);
long long programmeEnd = ParseDateTime(strStop);

GetAttributeValue(programmeNode, "catchup-id", m_catchupId);
m_catchupId = StringUtils::Trim(m_catchupId);

if ((tmpEnd + maxShiftTime < start) || (tmpStart + minShiftTime > end))
// Discard only if this is not a first run AND
// - The programme end time + the max timeshift is earlier than the EPG window start OR
// - The programme start time + the min timeshift is after than the EPG window end
// I.e. we discard any programme that does not start of finish during the EPG window
if (!FirstRun(epgWindowsStart, epgWindowsEnd) && ((programmeEnd + maxShiftTime < epgWindowsStart) || (programmeStart + minShiftTime > epgWindowsEnd)))
return false;

m_broadcastId = static_cast<int>(tmpStart);
m_broadcastId = static_cast<int>(programmeStart);
m_channelId = std::atoi(id.c_str());
m_genreType = 0;
m_genreSubType = 0;
m_plotOutline.clear();
m_startTime = static_cast<time_t>(tmpStart);
m_endTime = static_cast<time_t>(tmpEnd);
m_startTime = static_cast<time_t>(programmeStart);
m_endTime = static_cast<time_t>(programmeEnd);
m_year = 0;
m_starRating = 0;
m_episodeNumber = EPG_TAG_INVALID_SERIES_EPISODE;
Expand Down
2 changes: 1 addition & 1 deletion src/iptvsimple/data/EpgEntry.h
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ namespace iptvsimple

void UpdateTo(kodi::addon::PVREPGTag& left, int iChannelUid, int timeShift, const std::vector<EpgGenre>& genres);
bool UpdateFrom(const pugi::xml_node& programmeNode, const std::string& id,
int start, int end, int minShiftTime, int maxShiftTime);
int epgWindowsStart, int epgWindowsEnd, int minShiftTime, int maxShiftTime);

private:
bool SetEpgGenre(std::vector<EpgGenre> genreMappings);
Expand Down
42 changes: 41 additions & 1 deletion src/iptvsimple/data/MediaEntry.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,8 @@ void MediaEntry::Reset()
m_providerUniqueId = PVR_PROVIDER_INVALID_UID;
m_directory.clear();
m_sizeInBytes = 0;

m_tvgShift = 0;
}

void MediaEntry::UpdateFrom(iptvsimple::data::Channel channel)
Expand All @@ -66,14 +68,15 @@ void MediaEntry::UpdateFrom(iptvsimple::data::Channel channel)
m_iconPath = channel.GetIconPath();
m_tvgId = channel.GetTvgId();
m_tvgName = channel.GetTvgId();
m_tvgShift = channel.GetTvgShift();
m_startTime = std::time(nullptr);

m_providerUniqueId = channel.GetProviderUniqueId();
m_properties = channel.GetProperties();
m_inputStreamName = channel.GetInputStreamName();
}

void MediaEntry::UpdateFrom(iptvsimple::data::EpgEntry epgEntry)
void MediaEntry::UpdateFrom(iptvsimple::data::EpgEntry epgEntry, const std::vector<EpgGenre>& genreMappings)
{
// All from Base Entry
m_startTime = epgEntry.GetStartTime();
Expand All @@ -92,6 +95,19 @@ void MediaEntry::UpdateFrom(iptvsimple::data::EpgEntry epgEntry)
if (!epgEntry.GetIconPath().empty())
m_iconPath = epgEntry.GetIconPath();
m_genreString = epgEntry.GetGenreString();
if (SetEpgGenre(genreMappings))
{
if (m_settings->UseEpgGenreTextWhenMapping())
{
//Setting this value in sub type allows custom text to be displayed
//while still sending the type used for EPG colour
m_genreSubType = EPG_GENRE_USE_STRING;
}
}
else
{
m_genreType = EPG_GENRE_USE_STRING;
}
m_cast = epgEntry.GetCast();
m_director = epgEntry.GetDirector();
m_writer = epgEntry.GetWriter();
Expand All @@ -105,6 +121,30 @@ void MediaEntry::UpdateFrom(iptvsimple::data::EpgEntry epgEntry)
m_premiere = epgEntry.IsPremiere();
}

bool MediaEntry::SetEpgGenre(const std::vector<EpgGenre> genreMappings)
{
if (genreMappings.empty())
return false;

for (const auto& genre : StringUtils::Split(m_genreString, EPG_STRING_TOKEN_SEPARATOR))
{
if (genre.empty())
continue;

for (const auto& genreMapping : genreMappings)
{
if (StringUtils::EqualsNoCase(genreMapping.GetGenreString(), genre))
{
m_genreType = genreMapping.GetGenreType();
m_genreSubType = genreMapping.GetGenreSubType();
return true;
}
}
}

return false;
}

namespace
{

Expand Down
7 changes: 6 additions & 1 deletion src/iptvsimple/data/MediaEntry.h
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,8 @@ namespace iptvsimple
const std::string& GetM3UName() const { return m_m3uName; }
const std::string& GetTvgId() const { return m_tvgId; }
const std::string& GetTvgName() const { return m_tvgName; }
int GetTvgShift() const { return m_tvgShift; }
void SetTvgShift(int value) { m_tvgShift = value; }

const std::map<std::string, std::string>& GetProperties() const { return m_properties; }
void SetProperties(std::map<std::string, std::string>& value) { m_properties = value; }
Expand All @@ -84,10 +86,12 @@ namespace iptvsimple
void Reset();

void UpdateFrom(iptvsimple::data::Channel channel);
void UpdateFrom(iptvsimple::data::EpgEntry epgEntry);
void UpdateFrom(iptvsimple::data::EpgEntry epgEntry, const std::vector<EpgGenre>& genres);
void UpdateTo(kodi::addon::PVRRecording& left, bool isInVirtualMediaEntryFolder, bool haveMediaTypes);

private:
bool SetEpgGenre(std::vector<EpgGenre> genreMappings);

std::string m_mediaEntryId;
bool m_radio = false;
time_t m_startTime = 0;
Expand All @@ -106,6 +110,7 @@ namespace iptvsimple
std::string m_m3uName;
std::string m_tvgId;
std::string m_tvgName;
int m_tvgShift = 0;

// Props
std::map<std::string, std::string> m_properties;
Expand Down

0 comments on commit d26a305

Please sign in to comment.