Skip to content

Commit c0bd15b

Browse files
committed
Support for backend artwork, extra info and resumepoints
Also bump protocol version to 14
1 parent 6fa9d9c commit c0bd15b

File tree

7 files changed

+199
-6
lines changed

7 files changed

+199
-6
lines changed

pvr.vdr.vnsi/resources/language/resource.language.en_gb/strings.po

100644100755
Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -232,7 +232,15 @@ msgctxt "#30050"
232232
msgid "Read chunksize for recordings"
233233
msgstr ""
234234

235-
#empty strings from id 30051 to 30099
235+
msgctxt "#30051"
236+
msgid "Store recording resume location in VDR"
237+
msgstr ""
238+
239+
msgctxt "#30052"
240+
msgid "When disabled resume location and watched status will be managed only in Kodi"
241+
msgstr ""
242+
243+
#empty strings from id 30053 to 30099
236244

237245
msgctxt "#30100"
238246
msgid "VDR OSD"

pvr.vdr.vnsi/resources/settings.xml

100644100755
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,10 @@
7979
<default>65536</default>
8080
<control type="edit" format="integer" />
8181
</setting>
82+
<setting help="30052" id="backendresume" type="boolean" label="30051">
83+
<default>false</default>
84+
<control type="toggle" />
85+
</setting>
8286
</group>
8387
</category>
8488
</section>

src/ClientInstance.cpp

Lines changed: 153 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -221,6 +221,12 @@ PVR_ERROR CVNSIClientInstance::GetCapabilities(kodi::addon::PVRCapabilities& cap
221221
capabilities.SetSupportsRecordingsLifetimeChange(false);
222222
capabilities.SetSupportsDescrambleInfo(false);
223223

224+
if (GetProtocol() >= 14)
225+
{
226+
capabilities.SetSupportsRecordingPlayCount(CVNSISettings::Get().GetBackendResume());
227+
capabilities.SetSupportsLastPlayedPosition(CVNSISettings::Get().GetBackendResume());
228+
}
229+
224230
return PVR_ERROR_NO_ERROR;
225231
}
226232

@@ -583,17 +589,39 @@ PVR_ERROR CVNSIClientInstance::GetEPGForChannel(int channelUid,
583589
tag.SetTitle(vresp->extract_String());
584590
tag.SetPlotOutline(vresp->extract_String());
585591
tag.SetPlot(vresp->extract_String());
586-
tag.SetOriginalTitle("");
587-
tag.SetCast("");
592+
593+
if (GetProtocol() >= 14)
594+
{
595+
tag.SetIconPath(vresp->extract_String());
596+
int season = vresp->extract_U32();
597+
int episode = vresp->extract_U32();
598+
if (season > 0)
599+
tag.SetSeriesNumber(season);
600+
if (episode > 0)
601+
tag.SetEpisodeNumber(episode);
602+
tag.SetFirstAired(vresp->extract_String());
603+
tag.SetStarRating(vresp->extract_U32());
604+
tag.SetOriginalTitle(vresp->extract_String());
605+
tag.SetCast(vresp->extract_String());
606+
tag.SetDirector(vresp->extract_String());
607+
tag.SetWriter(vresp->extract_String());
608+
tag.SetIMDBNumber(vresp->extract_String());
609+
}
610+
else
611+
{
612+
tag.SetSeriesNumber(EPG_TAG_INVALID_SERIES_EPISODE);
613+
tag.SetEpisodeNumber(EPG_TAG_INVALID_SERIES_EPISODE);
614+
tag.SetOriginalTitle("");
615+
tag.SetCast("");
616+
}
617+
588618
tag.SetDirector("");
589619
tag.SetWriter("");
590620
tag.SetYear(0);
591621
tag.SetIMDBNumber("");
592622
if (!tag.GetPlotOutline().empty())
593623
tag.SetEpisodeName(tag.GetPlotOutline());
594624
tag.SetFlags(EPG_TAG_FLAG_UNDEFINED);
595-
tag.SetSeriesNumber(EPG_TAG_INVALID_SERIES_EPISODE);
596-
tag.SetEpisodeNumber(EPG_TAG_INVALID_SERIES_EPISODE);
597625
tag.SetEpisodePartNumber(EPG_TAG_INVALID_SERIES_EPISODE);
598626

599627
results.Add(tag);
@@ -666,6 +694,9 @@ PVR_ERROR CVNSIClientInstance::GetAvailableRecordings(kodi::addon::PVRRecordings
666694
return PVR_ERROR_UNKNOWN;
667695
}
668696

697+
m_lastPlayed.clear();
698+
m_playCount.clear();
699+
669700
std::string strRecordingId;
670701
while (vresp->getRemainingLength() >= 5 * 4 + 5)
671702
{
@@ -705,6 +736,38 @@ PVR_ERROR CVNSIClientInstance::GetAvailableRecordings(kodi::addon::PVRRecordings
705736
tag.SetPlot(vresp->extract_String());
706737
tag.SetDirectory(vresp->extract_String());
707738
tag.SetRecordingId(std::to_string(vresp->extract_U32()));
739+
740+
if (GetProtocol() >= 14)
741+
{
742+
tag.SetThumbnailPath(vresp->extract_String());
743+
tag.SetFanartPath(vresp->extract_String());
744+
int season = vresp->extract_U32();
745+
int episode = vresp->extract_U32();
746+
if (season > 0)
747+
tag.SetSeriesNumber(season);
748+
if (episode > 0)
749+
tag.SetEpisodeNumber(episode);
750+
751+
tag.SetFirstAired(vresp->extract_String());
752+
753+
int LastPlayedPosition = vresp->extract_U32();
754+
755+
if (CVNSISettings::Get().GetBackendResume())
756+
{
757+
tag.SetPlayCount(0);
758+
tag.SetLastPlayedPosition(LastPlayedPosition);
759+
if (tag.GetLastPlayedPosition() > 0)
760+
{
761+
if (tag.GetLastPlayedPosition() >= tag.GetDuration() - 60)
762+
{
763+
tag.SetPlayCount(1);
764+
tag.SetLastPlayedPosition(0);
765+
}
766+
}
767+
m_lastPlayed[std::stoi(tag.GetRecordingId())] = tag.GetLastPlayedPosition();
768+
m_playCount[std::stoi(tag.GetRecordingId())] = tag.GetPlayCount();
769+
}
770+
}
708771

709772
results.Add(tag);
710773
}
@@ -947,6 +1010,92 @@ PVR_ERROR CVNSIClientInstance::GetRecordingEdl(const kodi::addon::PVRRecording&
9471010
}
9481011
}
9491012

1013+
// Code for GetRecordingLastPlayedPosition, SetRecordingLastPlayedPosition and SetRecordingPlayCount
1014+
// mostly taken from https://github.com/kodi-pvr/pvr.nextpvr/blob/Matrix/src/Recordings.cpp
1015+
PVR_ERROR CVNSIClientInstance::GetRecordingLastPlayedPosition(const kodi::addon::PVRRecording& recording, int& position)
1016+
{
1017+
position = m_lastPlayed[std::stoi(recording.GetRecordingId())];
1018+
if (position == recording.GetDuration())
1019+
position = 0;
1020+
return PVR_ERROR_NO_ERROR;
1021+
}
1022+
1023+
//==============================================================================
1024+
/// SetRecordingLastPlayedPosition will be called when
1025+
/// Set watched - postion = 0, play count incremented. Note it is not called if
1026+
/// the watched positions is already 0
1027+
/// Resume reset - position = 0
1028+
/// Recording start position = 0 at start of file
1029+
/// Recording end actual position or when end is in ignorepercentatend zone the
1030+
/// position is -1 with play count incremented
1031+
/// Set unwatched - Not called by core
1032+
///
1033+
PVR_ERROR CVNSIClientInstance::SetRecordingLastPlayedPosition(const kodi::addon::PVRRecording& recording, int lastplayedposition)
1034+
{
1035+
try
1036+
{
1037+
cRequestPacket vrp;
1038+
vrp.init(VNSI_RECORDINGS_SETLASTPLAYEDPOSITION);
1039+
1040+
int originalPosition = lastplayedposition;
1041+
int current = m_playCount[std::stoi(recording.GetRecordingId())];
1042+
if (recording.GetPlayCount() > current && lastplayedposition == 0)
1043+
{
1044+
// Kodi rolled the play count but didn't send EOF
1045+
lastplayedposition = recording.GetDuration();
1046+
m_playCount[std::stoi(recording.GetRecordingId())] = recording.GetPlayCount();
1047+
}
1048+
1049+
if ( m_lastPlayed[std::stoi(recording.GetRecordingId())] != lastplayedposition )
1050+
{
1051+
if (lastplayedposition == -1)
1052+
{
1053+
lastplayedposition = recording.GetDuration();
1054+
}
1055+
}
1056+
vrp.add_U32(std::stoi(recording.GetRecordingId()));
1057+
vrp.add_U32(lastplayedposition);
1058+
1059+
auto vresp = ReadResult(&vrp);
1060+
if (vresp == nullptr || vresp->noResponse())
1061+
{
1062+
kodi::Log(ADDON_LOG_ERROR, "%s - Can't get response packed", __func__);
1063+
return PVR_ERROR_UNKNOWN;
1064+
}
1065+
1066+
return PVR_ERROR_NO_ERROR;
1067+
}
1068+
catch (std::exception e)
1069+
{
1070+
kodi::Log(ADDON_LOG_ERROR, "%s - %s", __func__, e.what());
1071+
return PVR_ERROR_SERVER_ERROR;
1072+
}
1073+
}
1074+
1075+
//==============================================================================
1076+
/// SetRecordingPlayCount will be called when
1077+
/// Set watched - play count increases
1078+
/// Set unwatched - count set to 0,
1079+
/// Recording start - no change in count
1080+
/// Recording end when end is in playcountminimumpercent zone then the count
1081+
/// is incremented
1082+
///
1083+
PVR_ERROR CVNSIClientInstance::SetRecordingPlayCount(const kodi::addon::PVRRecording& recording, int count)
1084+
{
1085+
int current = m_playCount[std::stoi(recording.GetRecordingId())];
1086+
kodi::Log(ADDON_LOG_DEBUG, "Playcount %s %d %d", recording.GetTitle().c_str(), count, current);
1087+
if (count < current)
1088+
{
1089+
// unwatch count is zero.
1090+
SetRecordingLastPlayedPosition(recording, 0);
1091+
m_playCount[std::stoi(recording.GetRecordingId())] = count;
1092+
}
1093+
else
1094+
{
1095+
1096+
}
1097+
return PVR_ERROR_NO_ERROR;
1098+
}
9501099

9511100
/*******************************************/
9521101
/** PVR Timer Functions **/

src/ClientInstance.h

100644100755
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,11 @@ class ATTR_DLL_LOCAL CVNSIClientInstance : public kodi::addon::CInstancePVRClien
9292
PVR_ERROR RenameRecording(const kodi::addon::PVRRecording& recording) override;
9393
PVR_ERROR GetRecordingEdl(const kodi::addon::PVRRecording& recording,
9494
std::vector<kodi::addon::PVREDLEntry>& edl) override;
95+
PVR_ERROR GetRecordingLastPlayedPosition(const kodi::addon::PVRRecording& recording,
96+
int& position) override;
97+
PVR_ERROR SetRecordingLastPlayedPosition(const kodi::addon::PVRRecording& recording,
98+
int lastplayedposition) override;
99+
PVR_ERROR SetRecordingPlayCount(const kodi::addon::PVRRecording& recording, int count) override;
95100

96101
//--==----==----==----==----==----==----==----==----==----==----==----==----==
97102

@@ -175,4 +180,8 @@ class ATTR_DLL_LOCAL CVNSIClientInstance : public kodi::addon::CInstancePVRClien
175180
std::atomic<bool> m_running = {false};
176181
std::thread m_thread;
177182
std::thread m_startInformThread;
183+
184+
// update these at end of counting loop can be called during action
185+
std::map<int, int> m_lastPlayed;
186+
std::map<int, int> m_playCount;
178187
};

src/Settings.cpp

100644100755
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,15 @@ bool CVNSISettings::Load()
118118
m_iChunkSize = DEFAULT_CHUNKSIZE;
119119
}
120120

121+
// Read setting "backendresume" from settings.xml
122+
if (!kodi::addon::CheckSettingBoolean("backendresume", m_bBackendResume))
123+
{
124+
// If setting is unknown fallback to defaults
125+
kodi::Log(ADDON_LOG_ERROR,
126+
"Couldn't get 'backendresume' setting, falling back to 'false' as default");
127+
m_bBackendResume = DEFAULT_BACKENDRESUME;
128+
}
129+
121130
return true;
122131
}
123132

@@ -195,6 +204,16 @@ ADDON_STATUS CVNSISettings::SetSetting(const std::string& settingName,
195204
settingValue.GetInt());
196205
m_iChunkSize = settingValue.GetInt();
197206
}
207+
else if (settingName == "backendresume")
208+
{
209+
kodi::Log(ADDON_LOG_INFO, "Changed Setting 'backendresume' from %u to %u",
210+
m_bBackendResume, settingValue.GetBoolean());
211+
if (m_bBackendResume != settingValue.GetBoolean())
212+
{
213+
m_bBackendResume = settingValue.GetBoolean();
214+
return ADDON_STATUS_NEED_RESTART;
215+
}
216+
}
198217

199218
return ADDON_STATUS_OK;
200219
}

src/Settings.h

100644100755
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
#define DEFAULT_TIMEOUT 3
1919
#define DEFAULT_AUTOGROUPS false
2020
#define DEFAULT_CHUNKSIZE 65536
21+
#define DEFAULT_BACKENDRESUME false
2122

2223
class ATTR_DLL_LOCAL CVNSISettings
2324
{
@@ -38,6 +39,7 @@ class ATTR_DLL_LOCAL CVNSISettings
3839
int GetTimeshift() const { return m_iTimeshift; }
3940
const std::string& GetIconPath() const { return m_szIconPath; }
4041
int GetChunkSize() const { return m_iChunkSize; }
42+
bool GetBackendResume() const { return m_bBackendResume; }
4143

4244
private:
4345
CVNSISettings() = default;
@@ -56,4 +58,5 @@ class ATTR_DLL_LOCAL CVNSISettings
5658
int m_iTimeshift = 1;
5759
std::string m_szIconPath; /*!< path to channel icons */
5860
int m_iChunkSize = DEFAULT_CHUNKSIZE;
61+
bool m_bBackendResume = DEFAULT_BACKENDRESUME;
5962
};

src/vnsicommand.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
#pragma once
1010

1111
/** Current VNSI Protocol Version number */
12-
#define VNSI_PROTOCOLVERSION 13
12+
#define VNSI_PROTOCOLVERSION 14
1313

1414
/** Start of RDS support protocol Version */
1515
#define VNSI_RDS_PROTOCOLVERSION 8
@@ -90,6 +90,7 @@
9090
#define VNSI_RECORDINGS_RENAME 103
9191
#define VNSI_RECORDINGS_DELETE 104
9292
#define VNSI_RECORDINGS_GETEDL 105
93+
#define VNSI_RECORDINGS_SETLASTPLAYEDPOSITION 106
9394

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

0 commit comments

Comments
 (0)