Skip to content

Commit

Permalink
Support for backend artwork, extra info and resumepoints
Browse files Browse the repository at this point in the history
Also bump protocol version to 14
  • Loading branch information
Dis90 committed Mar 28, 2022
1 parent df8fdfa commit 39e0fc5
Show file tree
Hide file tree
Showing 7 changed files with 199 additions and 6 deletions.
10 changes: 9 additions & 1 deletion pvr.vdr.vnsi/resources/language/resource.language.en_gb/strings.po
100644 → 100755
Original file line number Diff line number Diff line change
Expand Up @@ -232,7 +232,15 @@ msgctxt "#30050"
msgid "Read chunksize for recordings"
msgstr ""

#empty strings from id 30051 to 30099
msgctxt "#30051"
msgid "Store recording resume location in VDR"
msgstr ""

msgctxt "#30052"
msgid "When disabled resume location and watched status will be managed only in Kodi"
msgstr ""

#empty strings from id 30053 to 30099

msgctxt "#30100"
msgid "VDR OSD"
Expand Down
4 changes: 4 additions & 0 deletions pvr.vdr.vnsi/resources/settings.xml
100644 → 100755
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,10 @@
<default>65536</default>
<control type="edit" format="integer" />
</setting>
<setting help="30052" id="backendresume" type="boolean" label="30051">
<default>false</default>
<control type="toggle" />
</setting>
</group>
</category>
</section>
Expand Down
157 changes: 153 additions & 4 deletions src/ClientInstance.cpp
100644 → 100755
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,12 @@ PVR_ERROR CVNSIClientInstance::GetCapabilities(kodi::addon::PVRCapabilities& cap
capabilities.SetSupportsRecordingsLifetimeChange(false);
capabilities.SetSupportsDescrambleInfo(false);

if (GetProtocol() >= 14)
{
capabilities.SetSupportsRecordingPlayCount(CVNSISettings::Get().GetBackendResume());
capabilities.SetSupportsLastPlayedPosition(CVNSISettings::Get().GetBackendResume());
}

return PVR_ERROR_NO_ERROR;
}

Expand Down Expand Up @@ -571,17 +577,39 @@ PVR_ERROR CVNSIClientInstance::GetEPGForChannel(int channelUid,
tag.SetTitle(vresp->extract_String());
tag.SetPlotOutline(vresp->extract_String());
tag.SetPlot(vresp->extract_String());
tag.SetOriginalTitle("");
tag.SetCast("");

if (GetProtocol() >= 14)
{
tag.SetIconPath(vresp->extract_String());
int season = vresp->extract_U32();
int episode = vresp->extract_U32();
if (season > 0)
tag.SetSeriesNumber(season);
if (episode > 0)
tag.SetEpisodeNumber(episode);
tag.SetFirstAired(vresp->extract_String());
tag.SetStarRating(vresp->extract_U32());
tag.SetOriginalTitle(vresp->extract_String());
tag.SetCast(vresp->extract_String());
tag.SetDirector(vresp->extract_String());
tag.SetWriter(vresp->extract_String());
tag.SetIMDBNumber(vresp->extract_String());
}
else
{
tag.SetSeriesNumber(EPG_TAG_INVALID_SERIES_EPISODE);
tag.SetEpisodeNumber(EPG_TAG_INVALID_SERIES_EPISODE);
tag.SetOriginalTitle("");
tag.SetCast("");
}

tag.SetDirector("");
tag.SetWriter("");
tag.SetYear(0);
tag.SetIMDBNumber("");
if (!tag.GetPlotOutline().empty())
tag.SetEpisodeName(tag.GetPlotOutline());
tag.SetFlags(EPG_TAG_FLAG_UNDEFINED);
tag.SetSeriesNumber(EPG_TAG_INVALID_SERIES_EPISODE);
tag.SetEpisodeNumber(EPG_TAG_INVALID_SERIES_EPISODE);
tag.SetEpisodePartNumber(EPG_TAG_INVALID_SERIES_EPISODE);

results.Add(tag);
Expand Down Expand Up @@ -654,6 +682,9 @@ PVR_ERROR CVNSIClientInstance::GetAvailableRecordings(kodi::addon::PVRRecordings
return PVR_ERROR_UNKNOWN;
}

m_lastPlayed.clear();
m_playCount.clear();

std::string strRecordingId;
while (vresp->getRemainingLength() >= 5 * 4 + 5)
{
Expand Down Expand Up @@ -693,6 +724,38 @@ PVR_ERROR CVNSIClientInstance::GetAvailableRecordings(kodi::addon::PVRRecordings
tag.SetPlot(vresp->extract_String());
tag.SetDirectory(vresp->extract_String());
tag.SetRecordingId(std::to_string(vresp->extract_U32()));

if (GetProtocol() >= 14)
{
tag.SetThumbnailPath(vresp->extract_String());
tag.SetFanartPath(vresp->extract_String());
int season = vresp->extract_U32();
int episode = vresp->extract_U32();
if (season > 0)
tag.SetSeriesNumber(season);
if (episode > 0)
tag.SetEpisodeNumber(episode);

tag.SetFirstAired(vresp->extract_String());

int LastPlayedPosition = vresp->extract_U32();

if (CVNSISettings::Get().GetBackendResume())
{
tag.SetPlayCount(0);
tag.SetLastPlayedPosition(LastPlayedPosition);
if (tag.GetLastPlayedPosition() > 0)
{
if (tag.GetLastPlayedPosition() >= tag.GetDuration() - 60)
{
tag.SetPlayCount(1);
tag.SetLastPlayedPosition(0);
}
}
m_lastPlayed[std::stoi(tag.GetRecordingId())] = tag.GetLastPlayedPosition();
m_playCount[std::stoi(tag.GetRecordingId())] = tag.GetPlayCount();
}
}

results.Add(tag);
}
Expand Down Expand Up @@ -935,6 +998,92 @@ PVR_ERROR CVNSIClientInstance::GetRecordingEdl(const kodi::addon::PVRRecording&
}
}

// Code for GetRecordingLastPlayedPosition, SetRecordingLastPlayedPosition and SetRecordingPlayCount
// mostly taken from https://github.com/kodi-pvr/pvr.nextpvr/blob/Matrix/src/Recordings.cpp
PVR_ERROR CVNSIClientInstance::GetRecordingLastPlayedPosition(const kodi::addon::PVRRecording& recording, int& position)
{
position = m_lastPlayed[std::stoi(recording.GetRecordingId())];
if (position == recording.GetDuration())
position = 0;
return PVR_ERROR_NO_ERROR;
}

//==============================================================================
/// SetRecordingLastPlayedPosition will be called when
/// Set watched - postion = 0, play count incremented. Note it is not called if
/// the watched positions is already 0
/// Resume reset - position = 0
/// Recording start position = 0 at start of file
/// Recording end actual position or when end is in ignorepercentatend zone the
/// position is -1 with play count incremented
/// Set unwatched - Not called by core
///
PVR_ERROR CVNSIClientInstance::SetRecordingLastPlayedPosition(const kodi::addon::PVRRecording& recording, int lastplayedposition)
{
try
{
cRequestPacket vrp;
vrp.init(VNSI_RECORDINGS_SETLASTPLAYEDPOSITION);

int originalPosition = lastplayedposition;
int current = m_playCount[std::stoi(recording.GetRecordingId())];
if (recording.GetPlayCount() > current && lastplayedposition == 0)
{
// Kodi rolled the play count but didn't send EOF
lastplayedposition = recording.GetDuration();
m_playCount[std::stoi(recording.GetRecordingId())] = recording.GetPlayCount();
}

if ( m_lastPlayed[std::stoi(recording.GetRecordingId())] != lastplayedposition )
{
if (lastplayedposition == -1)
{
lastplayedposition = recording.GetDuration();
}
}
vrp.add_U32(std::stoi(recording.GetRecordingId()));
vrp.add_U32(lastplayedposition);

auto vresp = ReadResult(&vrp);
if (vresp == nullptr || vresp->noResponse())
{
kodi::Log(ADDON_LOG_ERROR, "%s - Can't get response packed", __func__);
return PVR_ERROR_UNKNOWN;
}

return PVR_ERROR_NO_ERROR;
}
catch (std::exception e)
{
kodi::Log(ADDON_LOG_ERROR, "%s - %s", __func__, e.what());
return PVR_ERROR_SERVER_ERROR;
}
}

//==============================================================================
/// SetRecordingPlayCount will be called when
/// Set watched - play count increases
/// Set unwatched - count set to 0,
/// Recording start - no change in count
/// Recording end when end is in playcountminimumpercent zone then the count
/// is incremented
///
PVR_ERROR CVNSIClientInstance::SetRecordingPlayCount(const kodi::addon::PVRRecording& recording, int count)
{
int current = m_playCount[std::stoi(recording.GetRecordingId())];
kodi::Log(ADDON_LOG_DEBUG, "Playcount %s %d %d", recording.GetTitle().c_str(), count, current);
if (count < current)
{
// unwatch count is zero.
SetRecordingLastPlayedPosition(recording, 0);
m_playCount[std::stoi(recording.GetRecordingId())] = count;
}
else
{

}
return PVR_ERROR_NO_ERROR;
}

/*******************************************/
/** PVR Timer Functions **/
Expand Down
9 changes: 9 additions & 0 deletions src/ClientInstance.h
100644 → 100755
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,11 @@ class ATTR_DLL_LOCAL CVNSIClientInstance : public kodi::addon::CInstancePVRClien
PVR_ERROR RenameRecording(const kodi::addon::PVRRecording& recording) override;
PVR_ERROR GetRecordingEdl(const kodi::addon::PVRRecording& recording,
std::vector<kodi::addon::PVREDLEntry>& edl) override;
PVR_ERROR GetRecordingLastPlayedPosition(const kodi::addon::PVRRecording& recording,
int& position) override;
PVR_ERROR SetRecordingLastPlayedPosition(const kodi::addon::PVRRecording& recording,
int lastplayedposition) override;
PVR_ERROR SetRecordingPlayCount(const kodi::addon::PVRRecording& recording, int count) override;

//--==----==----==----==----==----==----==----==----==----==----==----==----==

Expand Down Expand Up @@ -175,4 +180,8 @@ class ATTR_DLL_LOCAL CVNSIClientInstance : public kodi::addon::CInstancePVRClien
std::atomic<bool> m_running = {false};
std::thread m_thread;
std::thread m_startInformThread;

// update these at end of counting loop can be called during action
std::map<int, int> m_lastPlayed;
std::map<int, int> m_playCount;
};
19 changes: 19 additions & 0 deletions src/Settings.cpp
100644 → 100755
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,15 @@ bool CVNSISettings::Load()
m_iChunkSize = DEFAULT_CHUNKSIZE;
}

// Read setting "backendresume" from settings.xml
if (!kodi::addon::CheckSettingBoolean("backendresume", m_bBackendResume))
{
// If setting is unknown fallback to defaults
kodi::Log(ADDON_LOG_ERROR,
"Couldn't get 'backendresume' setting, falling back to 'false' as default");
m_bBackendResume = DEFAULT_BACKENDRESUME;
}

return true;
}

Expand Down Expand Up @@ -195,6 +204,16 @@ ADDON_STATUS CVNSISettings::SetSetting(const std::string& settingName,
settingValue.GetInt());
m_iChunkSize = settingValue.GetInt();
}
else if (settingName == "backendresume")
{
kodi::Log(ADDON_LOG_INFO, "Changed Setting 'backendresume' from %u to %u",
m_bBackendResume, settingValue.GetBoolean());
if (m_bBackendResume != settingValue.GetBoolean())
{
m_bBackendResume = settingValue.GetBoolean();
return ADDON_STATUS_NEED_RESTART;
}
}

return ADDON_STATUS_OK;
}
3 changes: 3 additions & 0 deletions src/Settings.h
100644 → 100755
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
#define DEFAULT_TIMEOUT 3
#define DEFAULT_AUTOGROUPS false
#define DEFAULT_CHUNKSIZE 65536
#define DEFAULT_BACKENDRESUME false

class ATTR_DLL_LOCAL CVNSISettings
{
Expand All @@ -38,6 +39,7 @@ class ATTR_DLL_LOCAL CVNSISettings
int GetTimeshift() const { return m_iTimeshift; }
const std::string& GetIconPath() const { return m_szIconPath; }
int GetChunkSize() const { return m_iChunkSize; }
bool GetBackendResume() const { return m_bBackendResume; }

private:
CVNSISettings() = default;
Expand All @@ -56,4 +58,5 @@ class ATTR_DLL_LOCAL CVNSISettings
int m_iTimeshift = 1;
std::string m_szIconPath; /*!< path to channel icons */
int m_iChunkSize = DEFAULT_CHUNKSIZE;
bool m_bBackendResume = DEFAULT_BACKENDRESUME;
};
3 changes: 2 additions & 1 deletion src/vnsicommand.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
#pragma once

/** Current VNSI Protocol Version number */
#define VNSI_PROTOCOLVERSION 13
#define VNSI_PROTOCOLVERSION 14

/** Start of RDS support protocol Version */
#define VNSI_RDS_PROTOCOLVERSION 8
Expand Down Expand Up @@ -90,6 +90,7 @@
#define VNSI_RECORDINGS_RENAME 103
#define VNSI_RECORDINGS_DELETE 104
#define VNSI_RECORDINGS_GETEDL 105
#define VNSI_RECORDINGS_SETLASTPLAYEDPOSITION 106

/* OPCODE 120 - 139: VNSI network functions for epg access and manipulating */
#define VNSI_EPG_GETFORCHANNEL 120
Expand Down

0 comments on commit 39e0fc5

Please sign in to comment.