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

Support for backend artwork, extra info and resumepoints #180

Open
wants to merge 1 commit into
base: Nexus
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
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
153 changes: 149 additions & 4 deletions src/ClientInstance.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -221,6 +221,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 @@ -583,17 +589,36 @@ 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());
}
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 @@ -666,6 +691,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 @@ -705,6 +733,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 @@ -947,6 +1007,91 @@ 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 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