Skip to content
Browse files

Merge pull request #240 from sub3/master

Further enhancements and bug fixes to pvr.nextpvr addon
  • Loading branch information...
2 parents d4cb4d5 + 7a20ea7 commit ca5d4d5ecb8004822d27f1c76bd7889dbfb39e4a @opdenkamp committed Dec 10, 2013
View
3 addons/pvr.nextpvr/Makefile.am
@@ -22,5 +22,6 @@ libnextpvr_addon_la_SOURCES = src/client.cpp \
src/uri.cpp \
src/md5.cpp \
src/liveshift.cpp \
- src/RingBuffer.cpp
+ src/RingBuffer.cpp \
+ src/DialogRecordPref.cpp
libnextpvr_addon_la_LDFLAGS = @TARGET_LDFLAGS@
View
8 addons/pvr.nextpvr/addon/changelog.txt
@@ -1,3 +1,11 @@
+v1.9.6
+- added dialog to set recurring recordings, including recurrence type, padding, number of recordings to keep, and recording directory
+- added support for EDL
+- added support for retrieving and storing last playback position
+- now requires NextPVR 3.1.1 or higher
+- fixed a bug that could cause an EPG event with no description to have a copy of last show's description, or cause a crash if the user was unlucky.
+- more flexible approach to genre
+
v1.9.5
- add timeshift buffer functions
View
75 addons/pvr.nextpvr/addon/resources/language/English/strings.po
@@ -64,3 +64,78 @@ msgstr ""
msgctxt "#30053"
msgid "Tuner no longer available"
msgstr ""
+
+msgctxt "#30054"
+msgid "[RECUR]"
+msgstr ""
+
+msgctxt "#30100"
+msgid "Add Timer"
+msgstr ""
+
+#Recording type
+
+msgctxt "#30120"
+msgid "Recording Type"
+msgstr ""
+
+msgctxt "#30121"
+msgid "Record Once"
+msgstr ""
+
+msgctxt "#30122"
+msgid "Record Season (NEW episodes on this channel)"
+msgstr ""
+
+msgctxt "#30123"
+msgid "Record Season (All episodes on this channel)"
+msgstr ""
+
+msgctxt "#30124"
+msgid "Record Season (Daily, this timeslot)"
+msgstr ""
+
+msgctxt "#30125"
+msgid "Record Season (Weekly, this timeslot)"
+msgstr ""
+
+msgctxt "#30126"
+msgid "Record Season (Monday-Friday, this timeslot)"
+msgstr ""
+
+msgctxt "#30127"
+msgid "Record Season (Weekends, this timeslot)"
+msgstr ""
+
+
+#Keep count
+
+msgctxt "#30130"
+msgid "Keep"
+msgstr ""
+
+msgctxt "#30131"
+msgid "All Recordings"
+msgstr ""
+
+#Padding
+
+msgctxt "#30132"
+msgid "Pre-Padding (minutes)"
+msgstr ""
+
+msgctxt "#30133"
+msgid "Pre-Padding (minutes)"
+msgstr ""
+
+
+#Recording directory
+
+msgctxt "#30134"
+msgid "Recording Directory"
+msgstr ""
+
+msgctxt "#30135"
+msgid "Default"
+msgstr ""
+
View
215 addons/pvr.nextpvr/addon/resources/skins/skin.confluence/720p/RecordPrefs.xml
@@ -0,0 +1,215 @@
+<window>
+ <defaultcontrol always="true">21</defaultcontrol>
+ <coordinates>
+ <system>1</system>
+ <posx>240</posx>
+ <posy>100</posy>
+ </coordinates>
+ <include>dialogeffect</include>
+ <controls>
+ <control type="image">
+ <description>background image</description>
+ <posx>0</posx>
+ <posy>0</posy>
+ <width>800</width>
+ <height>500</height>
+ <texture border="40">DialogBack.png</texture>
+ </control>
+ <control type="image">
+ <description>Dialog Header image</description>
+ <posx>40</posx>
+ <posy>16</posy>
+ <width>720</width>
+ <height>40</height>
+ <texture>dialogheader.png</texture>
+ </control>
+ <control type="label">
+ <description>header label</description>
+ <posx>40</posx>
+ <posy>20</posy>
+ <width>720</width>
+ <height>30</height>
+ <font>font13_title</font>
+ <label>$ADDON[pvr.nextpvr 30100]</label>
+ <align>center</align>
+ <aligny>center</aligny>
+ <textcolor>selected</textcolor>
+ <shadowcolor>black</shadowcolor>
+ </control>
+ <control type="button" id="22">
+ <description>Close Window button</description>
+ <posx>710</posx>
+ <posy>15</posy>
+ <width>64</width>
+ <height>32</height>
+ <label>-</label>
+ <font>-</font>
+ <!--<onclick>PreviousMenu</onclick>-->
+ <texturefocus>DialogCloseButton-focus.png</texturefocus>
+ <texturenofocus>DialogCloseButton.png</texturenofocus>
+ <onleft>10</onleft>
+ <onright>10</onright>
+ <onup>10</onup>
+ <ondown>10</ondown>
+ <visible>system.getbool(input.enablemouse)</visible>
+ </control>
+
+
+ <control type="label" description="program title" id = "20">
+ <posx>40</posx>
+ <posy>60</posy>
+ <width>720</width>
+ <height>30</height>
+ <font>font13_title</font>
+ <!--<label>filled in by code</label>-->
+ <align>left</align>
+ <aligny>center</aligny>
+ <textcolor>white</textcolor>
+ <!--<shadowcolor>black</shadowcolor>-->
+ </control>
+
+ <control type="label" description="program description" id = "24">
+ <posx>40</posx>
+ <posy>100</posy>
+ <width>720</width>
+ <height>100</height>
+ <font>font13_title</font>
+ <!--<label>filled in by code</label>-->
+ <align>left</align>
+ <aligny>center</aligny>
+ <textcolor>grey2</textcolor>
+ <!--<shadowcolor>black</shadowcolor>-->
+ <wrapmultiline>true</wrapmultiline>
+ </control>
+
+ <control type="spincontrolex" id="21">
+ <description>recording type</description>
+ <posx>40</posx>
+ <posy>200</posy>
+ <width>720</width>
+ <height>40</height>
+ <font>font13</font>
+ <textcolor>grey2</textcolor>
+ <focusedcolor>white</focusedcolor>
+ <texturenofocus border="5">button-nofocus.png</texturenofocus>
+ <texturefocus border="5">button-focus2.png</texturefocus>
+ <label>$ADDON[pvr.nextpvr 30120]</label>
+ <onright>21</onright>
+ <onleft>21</onleft>
+ <onup>1</onup>
+ <ondown>25</ondown>
+ </control>
+
+ <control type="spincontrolex" id="25">
+ <description>pre padding</description>
+ <posx>40</posx>
+ <posy>245</posy>
+ <width>720</width>
+ <height>40</height>
+ <font>font13</font>
+ <textcolor>grey2</textcolor>
+ <focusedcolor>white</focusedcolor>
+ <texturenofocus border="5">button-nofocus.png</texturenofocus>
+ <texturefocus border="5">button-focus2.png</texturefocus>
+ <label>$ADDON[pvr.nextpvr 30132]</label>
+ <onright>23</onright>
+ <onleft>23</onleft>
+ <onup>21</onup>
+ <ondown>26</ondown>
+ </control>
+
+ <control type="spincontrolex" id="26">
+ <description>post padding</description>
+ <posx>40</posx>
+ <posy>290</posy>
+ <width>720</width>
+ <height>40</height>
+ <font>font13</font>
+ <textcolor>grey2</textcolor>
+ <focusedcolor>white</focusedcolor>
+ <texturenofocus border="5">button-nofocus.png</texturenofocus>
+ <texturefocus border="5">button-focus2.png</texturefocus>
+ <label>$ADDON[pvr.nextpvr 30133]</label>
+ <onright>23</onright>
+ <onleft>23</onleft>
+ <onup>25</onup>
+ <ondown>23</ondown>
+ </control>
+
+ <control type="spincontrolex" id="23">
+ <description>keep count</description>
+ <posx>40</posx>
+ <posy>335</posy>
+ <width>720</width>
+ <height>40</height>
+ <font>font13</font>
+ <textcolor>grey2</textcolor>
+ <focusedcolor>white</focusedcolor>
+ <texturenofocus border="5">button-nofocus.png</texturenofocus>
+ <texturefocus border="5">button-focus2.png</texturefocus>
+ <label>$ADDON[pvr.nextpvr 30130]</label>
+ <onright>23</onright>
+ <onleft>23</onleft>
+ <onup>26</onup>
+ <ondown>27</ondown>
+ </control>
+
+ <control type="spincontrolex" id="27">
+ <description>recording directory</description>
+ <posx>40</posx>
+ <posy>380</posy>
+ <width>720</width>
+ <height>40</height>
+ <font>font13</font>
+ <textcolor>grey2</textcolor>
+ <focusedcolor>white</focusedcolor>
+ <texturenofocus border="5">button-nofocus.png</texturenofocus>
+ <texturefocus border="5">button-focus2.png</texturefocus>
+ <label>$ADDON[pvr.nextpvr 30134]</label>
+ <onright>23</onright>
+ <onleft>23</onleft>
+ <onup>23</onup>
+ <ondown>1</ondown>
+ </control>
+
+ <control type="group" id="9001">
+ <posx>190</posx>
+ <posy>435</posy>
+ <control type="button" id="1">
+ <description>Ok Button</description>
+ <posx>0</posx>
+ <posy>0</posy>
+ <width>200</width>
+ <height>40</height>
+ <align>center</align>
+ <aligny>center</aligny>
+ <texturenofocus border="5">button-nofocus.png</texturenofocus>
+ <texturefocus border="5">button-focus.png</texturefocus>
+ <label>186</label>
+ <font>font12_title</font>
+ <onup>27</onup>
+ <onleft>2</onleft>
+ <onright>2</onright>
+ <ondown>21</ondown>
+ </control>
+ <control type="button" id="2">
+ <description>Cancel Button</description>
+ <posx>210</posx>
+ <posy>0</posy>
+ <width>200</width>
+ <height>40</height>
+ <align>center</align>
+ <aligny>center</aligny>
+ <texturenofocus border="5">button-nofocus.png</texturenofocus>
+ <texturefocus border="5">button-focus.png</texturefocus>
+ <label>222</label>
+ <font>font12_title</font>
+ <onup>27</onup>
+ <onleft>1</onleft>
+ <onright>1</onright>
+ <ondown>21</ondown>
+ </control>
+ </control>
+
+ </controls>
+</window>
View
6 addons/pvr.nextpvr/project/VS2010Express/XBMC_NextPVR.vcxproj
@@ -107,6 +107,10 @@
</DataExecutionPrevention>
<TargetMachine>MachineX86</TargetMachine>
</Link>
+ <PostBuildEvent>
+ <Command>
+ </Command>
+ </PostBuildEvent>
</ItemDefinitionGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\..\project\VS2010Express\platform\platform.vcxproj">
@@ -118,6 +122,7 @@
</ItemGroup>
<ItemGroup>
<ClCompile Include="..\..\src\client.cpp" />
+ <ClCompile Include="..\..\src\DialogRecordPref.cpp" />
<ClCompile Include="..\..\src\liveshift.cpp" />
<ClCompile Include="..\..\src\md5.cpp" />
<ClCompile Include="..\..\src\pvrclient-nextpvr.cpp" />
@@ -127,6 +132,7 @@
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\..\src\client.h" />
+ <ClInclude Include="..\..\src\DialogRecordPref.h" />
<ClInclude Include="..\..\src\liveshift.h" />
<ClInclude Include="..\..\src\md5.h" />
<ClInclude Include="..\..\src\pvrclient-nextpvr.h" />
View
6 addons/pvr.nextpvr/project/VS2010Express/XBMC_NextPVR.vcxproj.filters
@@ -32,6 +32,9 @@
<ClCompile Include="..\..\src\liveshift.cpp">
<Filter>Source Files</Filter>
</ClCompile>
+ <ClCompile Include="..\..\src\DialogRecordPref.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\..\src\client.h">
@@ -55,5 +58,8 @@
<ClInclude Include="..\..\src\liveshift.h">
<Filter>Header Files</Filter>
</ClInclude>
+ <ClInclude Include="..\..\src\DialogRecordPref.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
</ItemGroup>
</Project>
View
214 addons/pvr.nextpvr/src/DialogRecordPref.cpp
@@ -0,0 +1,214 @@
+/*
+ * Copyright (C) 2005-2011 Team XBMC
+ * http://xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, write to
+ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#include "DialogRecordPref.h"
+#include "libXBMC_gui.h"
+
+#include <sstream>
+#include <iostream>
+
+#define BUTTON_OK 1
+#define BUTTON_CANCEL 2
+#define BUTTON_CLOSE 22
+
+#define RADIO_BUTTON_EPISODE 10
+#define RADIO_BUTTON_SERIES 11
+#define SPIN_CONTROLRunType 12
+#define SPIN_CONTROL_CHANNEL 13
+#define SPIN_CONTROL_AIRTIME 14
+#define LABEL_SHOW_NAME 20
+#define SPIN_CONTROL_RECORD_TYPE 21
+#define SPIN_CONTROL_KEEP_COUNT 23
+#define LABEL_SHOW_DESCRIPTION 24
+#define SPIN_CONTROL_PRE_PADDING 25
+#define SPIN_CONTROL_POST_PADDING 26
+#define SPIN_CONTROL_RECORDING_DIR 27
+
+
+CDialogRecordPref::CDialogRecordPref(std::string showName, std::string showDescription, int iPrePadding, int iPostPadding, std::string recordingDirectories)
+{
+ _showName = showName;
+ _showDescription = showDescription;
+
+ PrePadding = iPrePadding;
+ PostPadding = iPostPadding;
+ RecordingDirectory = recordingDirectories;
+
+ // needed for every dialog
+ _confirmed = -1; // init to failed load value (due to xml file not being found)
+ _window = GUI->Window_create("RecordPrefs.xml", "Confluence", false, true);
+ _window->m_cbhdl = this;
+ _window->CBOnInit = OnInitCB;
+ _window->CBOnFocus = OnFocusCB;
+ _window->CBOnClick = OnClickCB;
+ _window->CBOnAction = OnActionCB;
+}
+
+CDialogRecordPref::~CDialogRecordPref()
+{
+ GUI->Window_destroy(_window);
+}
+
+
+bool CDialogRecordPref::OnInit()
+{
+ // display the show name in the window
+ _window->SetControlLabel(LABEL_SHOW_NAME, _showName.c_str());
+ _window->SetControlLabel(LABEL_SHOW_DESCRIPTION, _showDescription.c_str());
+
+ // init record-type
+ _spinRecordType = GUI->Control_getSpin(_window, SPIN_CONTROL_RECORD_TYPE);
+ _spinRecordType->AddLabel(XBMC->GetLocalizedString(30121), 0); // record-once
+ _spinRecordType->AddLabel(XBMC->GetLocalizedString(30122), 1); // record-season (new episodes)
+ _spinRecordType->AddLabel(XBMC->GetLocalizedString(30123), 2); // record-season (all episodes)
+ _spinRecordType->AddLabel(XBMC->GetLocalizedString(30124), 3); // record-season (daily, this timeslot)
+ _spinRecordType->AddLabel(XBMC->GetLocalizedString(30125), 4); // record-season (weekly, this timeslot)
+ _spinRecordType->AddLabel(XBMC->GetLocalizedString(30126), 5); // record-season (mon-fri, this timeslot)
+ _spinRecordType->AddLabel(XBMC->GetLocalizedString(30127), 6); // record-season (weekends, this timeslot)
+ _spinRecordType->SetValue(0);
+
+ // init keep count
+ char *msg = XBMC->GetLocalizedString(30131);
+ _spinKeepCount = GUI->Control_getSpin(_window, SPIN_CONTROL_KEEP_COUNT);
+ _spinKeepCount->AddLabel(XBMC->GetLocalizedString(30131), 0); // keep all recordings
+ for (int i=1; i<31; i++)
+ {
+ char text[20];
+ sprintf(text, "%d", i);
+ _spinKeepCount->AddLabel(text, i); // keep all recordings
+ }
+ _spinKeepCount->SetValue(0);
+
+ // init padding
+ _spinPrePadding = GUI->Control_getSpin(_window, SPIN_CONTROL_PRE_PADDING);
+ _spinPostPadding = GUI->Control_getSpin(_window, SPIN_CONTROL_POST_PADDING);
+ for (int i=0; i<90; i++)
+ {
+ char text[20];
+ sprintf(text, "%d", i);
+ _spinPrePadding->AddLabel(text, i);
+ _spinPostPadding->AddLabel(text, i);
+ }
+ _spinPrePadding->SetValue(PrePadding);
+ _spinPostPadding->SetValue(PostPadding);
+
+ // recording directories
+ _spinRecordingDirectory = GUI->Control_getSpin(_window, SPIN_CONTROL_RECORDING_DIR);
+ _spinRecordingDirectory->AddLabel(XBMC->GetLocalizedString(30135), 0); // Default
+ std::istringstream directories(RecordingDirectory);
+ std::string directory;
+ int index = 0;
+ while (std::getline(directories, directory, ','))
+ {
+ _spinRecordingDirectory->AddLabel(directory.c_str(), index++);
+ _recordingDirectories.push_back(directory);
+ }
+
+ return true;
+}
+
+bool CDialogRecordPref::OnClick(int controlId)
+{
+ switch(controlId)
+ {
+ case BUTTON_OK: // save value from GUI, then FALLS THROUGH TO CANCEL
+ RecordingType = _spinRecordType->GetValue();
+ Keep = _spinKeepCount->GetValue();
+ PrePadding = _spinPrePadding->GetValue();
+ PostPadding = _spinPostPadding->GetValue();
+ RecordingDirectory = "[";
+ RecordingDirectory +=_recordingDirectories[_spinRecordingDirectory->GetValue()];
+ RecordingDirectory += "]";
+ case BUTTON_CANCEL:
+ case BUTTON_CLOSE:
+ if (_confirmed == -1)// if not previously confirmed, set to cancel value
+ _confirmed = 0;
+ _window->Close();
+ GUI->Control_releaseSpin(_spinRecordType);
+ GUI->Control_releaseSpin(_spinKeepCount);
+ break;
+ }
+
+ return true;
+}
+
+bool CDialogRecordPref::OnInitCB(GUIHANDLE cbhdl)
+{
+ CDialogRecordPref* dialog = static_cast<CDialogRecordPref*>(cbhdl);
+ return dialog->OnInit();
+}
+
+bool CDialogRecordPref::OnClickCB(GUIHANDLE cbhdl, int controlId)
+{
+ CDialogRecordPref* dialog = static_cast<CDialogRecordPref*>(cbhdl);
+ if (controlId == BUTTON_OK)
+ dialog->_confirmed = 1;
+ //dialog->_confirmed = (controlId == BUTTON_OK);
+ return dialog->OnClick(controlId);
+}
+
+bool CDialogRecordPref::OnFocusCB(GUIHANDLE cbhdl, int controlId)
+{
+ CDialogRecordPref* dialog = static_cast<CDialogRecordPref*>(cbhdl);
+ return dialog->OnFocus(controlId);
+}
+
+bool CDialogRecordPref::OnActionCB(GUIHANDLE cbhdl, int actionId)
+{
+ CDialogRecordPref* dialog = static_cast<CDialogRecordPref*>(cbhdl);
+ return dialog->OnAction(actionId);
+}
+
+bool CDialogRecordPref::Show()
+{
+ if (_window)
+ return _window->Show();
+
+ return false;
+}
+
+void CDialogRecordPref::Close()
+{
+ if (_window)
+ _window->Close();
+}
+
+int CDialogRecordPref::DoModal()
+{
+ if (_window)
+ _window->DoModal();
+ return _confirmed; // return true if user didn't cancel dialog
+}
+
+
+bool CDialogRecordPref::OnFocus(int controlId)
+{
+ return true;
+}
+
+bool CDialogRecordPref::OnAction(int actionId)
+{
+ if (actionId == ADDON_ACTION_CLOSE_DIALOG || actionId == ADDON_ACTION_PREVIOUS_MENU || actionId == 92/*back*/)
+ return OnClick(BUTTON_CANCEL);
+ else
+ return false;
+}
+
View
88 addons/pvr.nextpvr/src/DialogRecordPref.h
@@ -0,0 +1,88 @@
+#pragma once
+
+/*
+ * Copyright (C) 2005-2012 Team XBMC
+ * http://www.xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, write to
+ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#include "client.h"
+#include "pvrclient-nextpvr.h"
+#include <vector>
+
+//struct TimerInfo
+//{
+// bool isSeries;
+// int runType;
+// bool anyChannel;
+// bool anyTime;
+// std::string currentChannelName;
+// std::string currentAirTime;
+// std::string showName;
+//}
+
+class CDialogRecordPref
+{
+
+public:
+ CDialogRecordPref(std::string showName, std::string showDescription, int iDefaultPrePadding, int iDefaultPostPadding, std::string recordingDirectories);
+ virtual ~CDialogRecordPref();
+
+ bool Show();
+ void Close();
+ int DoModal(); // returns -1=> load failed, 0=>canceled, 1=>confirmed
+ //bool LoadFailed();
+
+ // dialog specific params
+ int RecordingType;
+ int Keep;
+ int PrePadding;
+ int PostPadding;
+ std::string RecordingDirectory;
+private:
+ std::string _currentChannel; // these are just used for dialog display
+ std::string _currentAirTime;
+ std::string _showName;
+ std::string _showDescription;
+ std::vector<std::string> _recordingDirectories;
+
+private:
+ CAddonGUISpinControl *_spinRecordType;
+ CAddonGUISpinControl *_spinPrePadding;
+ CAddonGUISpinControl *_spinPostPadding;
+ CAddonGUISpinControl *_spinKeepCount;
+ CAddonGUISpinControl *_spinRecordingDirectory;
+
+ // following is needed for every dialog
+private:
+ CAddonGUIWindow *_window;
+ int _confirmed; //-1=> load failed, 0=>canceled, 1=>confirmed
+
+ bool OnClick(int controlId);
+ bool OnFocus(int controlId);
+ bool OnInit();
+ bool OnAction(int actionId);
+
+ static bool OnClickCB(GUIHANDLE cbhdl, int controlId);
+ static bool OnFocusCB(GUIHANDLE cbhdl, int controlId);
+ static bool OnInitCB(GUIHANDLE cbhdl);
+ static bool OnActionCB(GUIHANDLE cbhdl, int actionId);
+
+
+};
+
View
4 addons/pvr.nextpvr/src/README
@@ -11,8 +11,8 @@ Supported platforms:
- OSX (should work, not tested by me)
Dependencies:
-- NextPVR 2.6. May work also 2.5.8 (which is 2.5 + extra patches)
-
+- NextPVR 3.1.1 or higher.
+
This addon is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
View
39 addons/pvr.nextpvr/src/client.cpp
@@ -42,6 +42,7 @@ std::string g_szUserPath = "";
std::string g_szClientPath = "";
CHelper_libXBMC_addon *XBMC = NULL;
CHelper_libXBMC_pvr *PVR = NULL;
+CHelper_libXBMC_gui *GUI = NULL;
bool g_bUseTimeshift = false;
extern "C" {
@@ -69,6 +70,15 @@ ADDON_STATUS ADDON_Create(void* hdl, void* props)
return ADDON_STATUS_PERMANENT_FAILURE;
}
+ // register gui
+ GUI = new CHelper_libXBMC_gui;
+ if (!GUI->RegisterMe(hdl))
+ {
+ SAFE_DELETE(GUI);
+ SAFE_DELETE(XBMC);
+ return ADDON_STATUS_PERMANENT_FAILURE;
+ }
+
PVR = new CHelper_libXBMC_pvr;
if (!PVR->RegisterMe(hdl))
{
@@ -85,7 +95,7 @@ ADDON_STATUS ADDON_Create(void* hdl, void* props)
ADDON_ReadSettings();
- /* Create connection to MediaPortal XBMC TV client */
+ /* Create connection to NextPVR XBMC TV client */
g_client = new cPVRClientNextPVR();
if (!g_client->Connect())
{
@@ -300,7 +310,8 @@ PVR_ERROR GetAddonCapabilities(PVR_ADDON_CAPABILITIES *pCapabilities)
pCapabilities->bHandlesInputStream = true;
pCapabilities->bHandlesDemuxing = false;
pCapabilities->bSupportsChannelScan = false;
- pCapabilities->bSupportsLastPlayedPosition = false;
+ pCapabilities->bSupportsLastPlayedPosition = true;
+ pCapabilities->bSupportsRecordingEdl = true;
return PVR_ERROR_NO_ERROR;
}
@@ -681,6 +692,27 @@ bool CanSeekStream(void)
return false;
}
+PVR_ERROR SetRecordingLastPlayedPosition(const PVR_RECORDING &recording, int lastplayedposition)
+{
+ if (g_client)
+ return g_client->SetRecordingLastPlayedPosition(recording, lastplayedposition);
+ return PVR_ERROR_SERVER_ERROR;
+}
+
+int GetRecordingLastPlayedPosition(const PVR_RECORDING &recording)
+{
+ if (g_client)
+ return g_client->GetRecordingLastPlayedPosition(recording);
+ return -1;
+}
+
+PVR_ERROR GetRecordingEdl(const PVR_RECORDING &recording, PVR_EDL_ENTRY entries[], int *size)
+{
+ if (g_client)
+ return g_client->GetRecordingEdl(recording, entries, size);
+ return PVR_ERROR_SERVER_ERROR;
+}
+
/** UNUSED API FUNCTIONS */
PVR_ERROR MoveChannel(const PVR_CHANNEL &channel) { return PVR_ERROR_NOT_IMPLEMENTED; }
@@ -690,9 +722,6 @@ void DemuxReset(void) {}
void DemuxFlush(void) {}
PVR_ERROR SetRecordingPlayCount(const PVR_RECORDING &recording, int count) { return PVR_ERROR_NOT_IMPLEMENTED; }
-PVR_ERROR SetRecordingLastPlayedPosition(const PVR_RECORDING &recording, int lastplayedposition) { return PVR_ERROR_NOT_IMPLEMENTED; }
-int GetRecordingLastPlayedPosition(const PVR_RECORDING &recording) { return -1; }
-PVR_ERROR GetRecordingEdl(const PVR_RECORDING&, PVR_EDL_ENTRY[], int*) { return PVR_ERROR_NOT_IMPLEMENTED; };
unsigned int GetChannelSwitchDelay(void) { return 0; }
bool SeekTime(int,bool,double*) { return false; }
View
1 addons/pvr.nextpvr/src/client.h
@@ -52,6 +52,7 @@ extern bool g_bUseTimeshift;
extern ADDON::CHelper_libXBMC_addon *XBMC;
extern CHelper_libXBMC_pvr *PVR;
+extern CHelper_libXBMC_gui *GUI;
extern int g_iTVServerXBMCBuild;
View
11 addons/pvr.nextpvr/src/liveshift.cpp
@@ -87,6 +87,17 @@ LiveShiftSource::~LiveShiftSource(void)
}
}
+void LiveShiftSource::Close()
+{
+ if (m_pSocket != NULL)
+ {
+ char request[48];
+ memset(request, 0, sizeof(request));
+ snprintf(request, sizeof(request), "Close");
+ m_pSocket->send(request, sizeof(request));
+ }
+}
+
void LiveShiftSource::LOG(char const *fmt, ... )
{
if (m_log != NULL)
View
2 addons/pvr.nextpvr/src/liveshift.h
@@ -41,6 +41,8 @@ class LiveShiftSource
long long GetPosition();
void Seek(long long offset);
+ void Close();
+
private:
void LOG(char const *fmt, ... );
View
294 addons/pvr.nextpvr/src/pvrclient-nextpvr.cpp
@@ -26,6 +26,7 @@
#include "client.h"
#include "pvrclient-nextpvr.h"
+#include "DialogRecordPref.h"
#include "md5.h"
@@ -44,7 +45,7 @@ int g_iNextPVRXBMCBuild = 0;
/* PVR client version (don't forget to update also the addon.xml and the Changelog.txt files) */
#define PVRCLIENT_NEXTPVR_VERSION_STRING "1.0.0.0"
-#define NEXTPVRC_MIN_VERSION_STRING "2.5.9"
+#define NEXTPVRC_MIN_VERSION_STRING "3.1.1"
#define HTTP_OK 200
#define HTTP_NOTFOUND 404
@@ -217,6 +218,21 @@ bool cPVRClientNextPVR::Connect()
{
m_supportsLiveTimeshift = true;
}
+
+ // load padding defaults
+ m_iDefaultPrePadding = 1;
+ m_iDefaultPostPadding = 2;
+ if ( settingsDoc.RootElement()->FirstChildElement("PrePadding") != NULL && settingsDoc.RootElement()->FirstChildElement("PrePadding")->FirstChild() != NULL)
+ {
+ m_iDefaultPrePadding = atoi(settingsDoc.RootElement()->FirstChildElement("PrePadding")->FirstChild()->Value());
+ m_iDefaultPostPadding = atoi( settingsDoc.RootElement()->FirstChildElement("PostPadding")->FirstChild()->Value());
+ }
+
+ m_recordingDirectories = "";
+ if ( settingsDoc.RootElement()->FirstChildElement("RecordingDirectories") != NULL && settingsDoc.RootElement()->FirstChildElement("RecordingDirectories")->FirstChild() != NULL)
+ {
+ m_recordingDirectories += settingsDoc.RootElement()->FirstChildElement("RecordingDirectories")->FirstChild()->Value();
+ }
}
}
@@ -347,6 +363,10 @@ PVR_ERROR cPVRClientNextPVR::GetEpg(ADDON_HANDLE handle, const PVR_CHANNEL &chan
{
PVR_STRCPY(description, pListingNode->FirstChildElement("description")->FirstChild()->Value());
}
+ else
+ {
+ description[0] = '\0';
+ }
char start[32];
strncpy(start, pListingNode->FirstChildElement("start")->FirstChild()->Value(), sizeof start);
@@ -372,6 +392,20 @@ PVR_ERROR cPVRClientNextPVR::GetEpg(ADDON_HANDLE handle, const PVR_CHANNEL &chan
PVR_STRCPY(genre, pListingNode->FirstChildElement("genre")->FirstChild()->Value());
broadcast.strGenreDescription = genre;
}
+ else
+ {
+ // genre type
+ if (pListingNode->FirstChildElement("genre_type") != NULL && pListingNode->FirstChildElement("genre_type")->FirstChild() != NULL)
+ {
+ broadcast.iGenreType = atoi(pListingNode->FirstChildElement("genre_type")->FirstChild()->Value());
+ }
+
+ // genre subtype
+ if (pListingNode->FirstChildElement("genre_subtype") != NULL && pListingNode->FirstChildElement("genre_subtype")->FirstChild() != NULL)
+ {
+ broadcast.iGenreSubType = atoi(pListingNode->FirstChildElement("genre_subtype")->FirstChild()->Value());
+ }
+ }
broadcast.bNotify = false;
@@ -706,6 +740,11 @@ PVR_ERROR cPVRClientNextPVR::GetRecordings(ADDON_HANDLE handle)
tag.recordingTime = atoi(pRecordingNode->FirstChildElement("start_time_ticks")->FirstChild()->Value());
tag.iDuration = atoi(pRecordingNode->FirstChildElement("duration_seconds")->FirstChild()->Value());
+ if (pRecordingNode->FirstChildElement("playback_position") != NULL && pRecordingNode->FirstChildElement("playback_position")->FirstChild() != NULL)
+ {
+ tag.iLastPlayedPosition = atoi(pRecordingNode->FirstChildElement("playback_position")->FirstChild()->Value());
+ }
+
CStdString strStream;
strStream.Format("http://%s:%d/live?recording=%s", g_szHostname, g_iPort, tag.strRecordingId);
strncpy(tag.strStreamURL, strStream.c_str(), sizeof(tag.strStreamURL));
@@ -791,6 +830,62 @@ PVR_ERROR cPVRClientNextPVR::RenameRecording(const PVR_RECORDING &recording)
return PVR_ERROR_NO_ERROR;
}
+PVR_ERROR cPVRClientNextPVR::SetRecordingLastPlayedPosition(const PVR_RECORDING &recording, int lastplayedposition)
+{
+ XBMC->Log(LOG_DEBUG, "SetRecordingLastPlayedPosition");
+ char request[512];
+ sprintf(request, "/service?method=recording.watched.set&recording_id=%s&position=%d", recording.strRecordingId, lastplayedposition);
+
+ CStdString response;
+ if (DoRequest(request, response) == HTTP_OK)
+ {
+ if (strstr(response, "<rsp stat=\"ok\">") == NULL)
+ {
+ XBMC->Log(LOG_DEBUG, "SetRecordingLastPlayedPosition failed");
+ return PVR_ERROR_FAILED;
+ }
+ }
+ return PVR_ERROR_NO_ERROR;
+}
+
+int cPVRClientNextPVR::GetRecordingLastPlayedPosition(const PVR_RECORDING &recording)
+{
+ return recording.iLastPlayedPosition;
+}
+
+PVR_ERROR cPVRClientNextPVR::GetRecordingEdl(const PVR_RECORDING& recording, PVR_EDL_ENTRY entries[], int *size)
+{
+ XBMC->Log(LOG_DEBUG, "GetRecordingEdl");
+ char request[512];
+ sprintf(request, "/service?method=recording.edl&recording_id=%s", recording.strRecordingId);
+
+ CStdString response;
+ if (DoRequest(request, response) == HTTP_OK)
+ {
+ if (strstr(response, "<rsp stat=\"ok\">") != NULL)
+ {
+ TiXmlDocument doc;
+ if (doc.Parse(response) != NULL)
+ {
+ int index = 0;
+ TiXmlElement* commercialsNode = doc.RootElement()->FirstChildElement("commercials");
+ TiXmlElement* pCommercialNode = commercialsNode->FirstChildElement("commercial");
+ for( pCommercialNode; pCommercialNode; pCommercialNode=pCommercialNode->NextSiblingElement())
+ {
+ PVR_EDL_ENTRY entry;
+ entry.start = atoi(pCommercialNode->FirstChildElement("start")->FirstChild()->Value()) * 1000;
+ entry.end = atoi(pCommercialNode->FirstChildElement("end")->FirstChild()->Value()) * 1000 ;
+ entry.type = PVR_EDL_TYPE_COMBREAK;
+ entries[index] = entry;
+ index++;
+ }
+ *size = index;
+ return PVR_ERROR_NO_ERROR;
+ }
+ }
+ }
+ return PVR_ERROR_FAILED;
+}
/************************************************************/
/** Timer handling */
@@ -799,6 +894,28 @@ int cPVRClientNextPVR::GetNumTimers(void)
{
int timerCount = 0;
CStdString response;
+
+ // get list of recurring recordings
+ if (DoRequest("/service?method=recording.recurring.list", response) == HTTP_OK)
+ {
+ TiXmlDocument doc;
+ if (doc.Parse(response) != NULL)
+ {
+ TiXmlElement* recordingsNode = doc.RootElement()->FirstChildElement("recurrings");
+ if (recordingsNode != NULL)
+ {
+ TiXmlElement* pRecordingNode = recordingsNode->FirstChildElement("recurring");
+ for( pRecordingNode; pRecordingNode; pRecordingNode=pRecordingNode->NextSiblingElement())
+ {
+ timerCount++;
+ }
+ }
+ }
+ }
+
+
+ // get list of pending recordings
+ response = "";
if (DoRequest("/service?method=recording.list&filter=pending", response) == HTTP_OK)
{
TiXmlDocument doc;
@@ -822,6 +939,48 @@ int cPVRClientNextPVR::GetNumTimers(void)
PVR_ERROR cPVRClientNextPVR::GetTimers(ADDON_HANDLE handle)
{
CStdString response;
+
+ // first add the recurring recordings
+ if (DoRequest("/service?method=recording.recurring.list&filter=pending", response) == HTTP_OK)
+ {
+ TiXmlDocument doc;
+ if (doc.Parse(response) != NULL)
+ {
+ PVR_TIMER tag;
+ TiXmlElement* recurringsNode = doc.RootElement()->FirstChildElement("recurrings");
+ TiXmlElement* pRecurringNode = recurringsNode->FirstChildElement("recurring");
+ for( pRecurringNode; pRecurringNode; pRecurringNode=pRecurringNode->NextSiblingElement())
+ {
+ memset(&tag, 0, sizeof(tag));
+ tag.iClientIndex = 0xF000000 + atoi(pRecurringNode->FirstChildElement("id")->FirstChild()->Value());
+
+ tag.iClientChannelUid = 8101;
+
+ char strTitle[PVR_ADDON_NAME_STRING_LENGTH];
+ strncpy(strTitle, pRecurringNode->FirstChildElement("name")->FirstChild()->Value(), sizeof(strTitle)-1);
+ strncat(tag.strTitle, XBMC->GetLocalizedString(30054), sizeof(tag.strTitle) - 1);
+ strncat(tag.strTitle, " ", sizeof(tag.strTitle) - 1);
+ strncat(tag.strTitle, strTitle, sizeof(tag.strTitle) - 1);
+
+ //PVR_STRCPY(tag.strTitle, pRecurringNode->FirstChildElement("name")->FirstChild()->Value());
+ //tag.iClientChannelUid = atoi(pRecurringNode->FirstChildElement("channel_id")->FirstChild()->Value());
+ tag.state = PVR_TIMER_STATE_SCHEDULED;
+
+ tag.startTime = time(NULL) - 96000;
+ tag.endTime = time(NULL) - 86000;
+
+ PVR_STRCPY(tag.strSummary, "summary");
+
+ tag.bIsRepeating = true;
+
+ // pass timer to xbmc
+ PVR->TransferTimerEntry(handle, &tag);
+ }
+ }
+ }
+
+ // next add the one-off recordings.
+ response = "";
if (DoRequest("/service?method=recording.list&filter=pending", response) == HTTP_OK)
{
TiXmlDocument doc;
@@ -893,23 +1052,83 @@ PVR_ERROR cPVRClientNextPVR::GetTimerInfo(unsigned int timernumber, PVR_TIMER &t
PVR_ERROR cPVRClientNextPVR::AddTimer(const PVR_TIMER &timerinfo)
{
+ // editing recording is not supported
+ if (timerinfo.iClientIndex != -1)
+ {
+ return PVR_ERROR_NOT_IMPLEMENTED;
+ }
+
std::string encodedName = UriEncode(timerinfo.strTitle);
-
- // build request string
- char request[1024];
- snprintf(request, sizeof(request), "/service?method=recording.save&name=%s&channel=%d&time_t=%d&duration=%d",
- encodedName.c_str(),
- timerinfo.iClientChannelUid,
- (int)timerinfo.startTime,
- (int)(timerinfo.endTime - timerinfo.startTime));
-
- // ask NextPVR to schedule our timer
- CStdString response;
- if (DoRequest(request, response) == HTTP_OK)
+
+ // manual recording (iEpgUid == -1) or instant recording (timerinfo.startTime == 0)
+ if (timerinfo.startTime == 0 || timerinfo.iEpgUid == -1)
{
- if (strstr(response, "<rsp stat=\"ok\">"))
+ // build request
+ char request[1024];
+ snprintf(request, sizeof(request), "/service?method=recording.save&name=%s&channel=%d&time_t=%d&duration=%d",
+ encodedName.c_str(),
+ timerinfo.iClientChannelUid,
+ (int)timerinfo.startTime,
+ (int)(timerinfo.endTime - timerinfo.startTime));
+
+ // send request to NextPVR
+ CStdString response;
+ if (DoRequest(request, response) == HTTP_OK)
{
- PVR->TriggerTimerUpdate();
+ if (strstr(response, "<rsp stat=\"ok\">"))
+ {
+ PVR->TriggerTimerUpdate();
+ return PVR_ERROR_NO_ERROR;
+ }
+ }
+ }
+ else
+ {
+ CDialogRecordPref vWindow(timerinfo.strTitle, timerinfo.strSummary, m_iDefaultPrePadding, m_iDefaultPostPadding, m_recordingDirectories);
+ if (vWindow.DoModal() == 1) // user hit ok
+ {
+ // build request string
+ char request[1024];
+ if (vWindow.RecordingType == 0)
+ {
+ // build one-off recording request
+ snprintf(request, sizeof(request), "/service?method=recording.save&name=%s&channel=%d&time_t=%d&duration=%d&pre_padding=%d&post_padding=%d&directory_id=%s",
+ encodedName.c_str(),
+ timerinfo.iClientChannelUid,
+ (int)timerinfo.startTime,
+ (int)(timerinfo.endTime - timerinfo.startTime),
+ vWindow.PrePadding,
+ vWindow.PostPadding,
+ vWindow.RecordingDirectory.c_str()
+ );
+ }
+ else
+ {
+ // build recurring recording request
+ snprintf(request, sizeof(request), "/service?method=recording.recurring.save&event_id=%d&recurring_type=%d&keep=%d&pre_padding=%d&post_padding=%d&directory_id=%s",
+ timerinfo.iEpgUid,
+ vWindow.RecordingType,
+ vWindow.Keep,
+ vWindow.PrePadding,
+ vWindow.PostPadding,
+ vWindow.RecordingDirectory.c_str()
+ );
+ }
+
+ // send request to NextPVR
+ CStdString response;
+ if (DoRequest(request, response) == HTTP_OK)
+ {
+ if (strstr(response, "<rsp stat=\"ok\">"))
+ {
+ PVR->TriggerTimerUpdate();
+ return PVR_ERROR_NO_ERROR;
+ }
+ }
+ }
+ else
+ {
+ // cancel
return PVR_ERROR_NO_ERROR;
}
}
@@ -922,6 +1141,12 @@ PVR_ERROR cPVRClientNextPVR::DeleteTimer(const PVR_TIMER &timer, bool bForceDele
char request[512];
sprintf(request, "/service?method=recording.delete&recording_id=%d", timer.iClientIndex);
+ // handle special-case for recurring recordings
+ if (timer.iClientIndex > 0xF000000)
+ {
+ sprintf(request, "/service?method=recording.recurring.delete&recurring_id=%d", (timer.iClientIndex - 0xF000000));
+ }
+
CStdString response;
if (DoRequest(request, response) == HTTP_OK)
{
@@ -952,12 +1177,28 @@ bool cPVRClientNextPVR::OpenLiveStream(const PVR_CHANNEL &channelinfo)
XBMC->Log(LOG_DEBUG, "OpenLiveStream(%d:%s) (oid=%d)", channelinfo.iChannelNumber, channelinfo.strChannelName, channelinfo.iUniqueId);
if (strstr(channelinfo.strStreamURL, "live?channel") == NULL)
{
+ if (m_pLiveShiftSource != NULL)
+ {
+ XBMC->Log(LOG_DEBUG, "OpenLiveStream() informing NextPVR of existing channel stream closing");
+
+ char request[512];
+ sprintf(request, "/service?method=channel.stop");
+ CStdString response;
+ DoRequest(request, response);
+
+ m_pLiveShiftSource->Close();
+ delete m_pLiveShiftSource;
+ m_pLiveShiftSource = NULL;
+ }
+
if (!m_streamingclient->create())
{
XBMC->Log(LOG_ERROR, "Could not connect create streaming socket");
return false;
}
+ m_incomingStreamBuffer.Clear();
+
if (!m_streamingclient->connect(g_szHostname, g_iPort))
{
XBMC->Log(LOG_ERROR, "Could not connect to NextPVR backend for streaming");
@@ -1042,7 +1283,7 @@ bool cPVRClientNextPVR::OpenLiveStream(const PVR_CHANNEL &channelinfo)
int cPVRClientNextPVR::ReadLiveStream(unsigned char *pBuffer, unsigned int iBufferSize)
{
- XBMC->Log(LOG_DEBUG, "ReadLiveStream");
+ //XBMC->Log(LOG_DEBUG, "ReadLiveStream");
int read = iBufferSize;
@@ -1057,7 +1298,7 @@ int cPVRClientNextPVR::ReadLiveStream(unsigned char *pBuffer, unsigned int iBuff
static int total = 0;
total += rc;
- XBMC->Log(LOG_DEBUG, "ReadLiveStream read %d bytes. (total %d)", rc, total);
+ //XBMC->Log(LOG_DEBUG, "ReadLiveStream read %d bytes. (total %d)", rc, total);
return rc;
}
@@ -1080,7 +1321,7 @@ int cPVRClientNextPVR::ReadLiveStream(unsigned char *pBuffer, unsigned int iBuff
int read = m_streamingclient->receive((char *)buf, sizeof buf, 0);
if (read > 0)
{
- XBMC->Log(LOG_DEBUG, "ReadLiveStream() added %d bytes to buffer. (now at %d bytes)", read, m_incomingStreamBuffer.getMaxReadSize());
+ //XBMC->Log(LOG_DEBUG, "ReadLiveStream() added %d bytes to buffer. (now at %d bytes)", read, m_incomingStreamBuffer.getMaxReadSize());
// write it to incoming ring buffer
m_incomingStreamBuffer.WriteData((char *)buf, read);
}
@@ -1122,7 +1363,7 @@ int cPVRClientNextPVR::ReadLiveStream(unsigned char *pBuffer, unsigned int iBuff
m_currentLivePosition += read;
}
- XBMC->Log(LOG_DEBUG, "ReadLiveStream return %d bytes (%d bytes remaining in buffer)", read, m_incomingStreamBuffer.getMaxReadSize());
+ //XBMC->Log(LOG_DEBUG, "ReadLiveStream return %d bytes (%d bytes remaining in buffer)", read, m_incomingStreamBuffer.getMaxReadSize());
return read;
}
@@ -1133,12 +1374,21 @@ void cPVRClientNextPVR::CloseLiveStream(void)
if (m_pLiveShiftSource)
{
+ XBMC->Log(LOG_DEBUG, "Telling backend of live session closure");
+
+ char request[512];
+ sprintf(request, "/service?method=channel.stop");
+ CStdString response;
+ DoRequest(request, response);
+
+ m_pLiveShiftSource->Close();
delete m_pLiveShiftSource;
m_pLiveShiftSource = NULL;
}
// Socket no longer required. Server will clean up when socket is closed.
- m_streamingclient->close();
+ m_streamingclient->close();
+ XBMC->Log(LOG_DEBUG, "CloseLiveStream@exit");
}
@@ -1242,7 +1492,7 @@ bool cPVRClientNextPVR::OpenRecordingInternal(long long seekOffset)
return false;
}
- if (!m_streamingclient->connect("127.0.0.1", 8866))
+ if (!m_streamingclient->connect(g_szHostname, g_iPort))
{
XBMC->Log(LOG_ERROR, "Could not connect to NextPVR backend for streaming");
return false;
@@ -1317,7 +1567,7 @@ bool cPVRClientNextPVR::OpenRecordingInternal(long long seekOffset)
bool cPVRClientNextPVR::OpenRecordedStream(const PVR_RECORDING &recording)
{
- XBMC->Log(LOG_DEBUG, "OpenRecordedStream(%d:%s)", recording.strRecordingId, recording.strTitle);
+ XBMC->Log(LOG_DEBUG, "OpenRecordedStream(%s:%s)", recording.strRecordingId, recording.strTitle);
m_currentRecordingLength = 0;
m_currentRecordingPosition = 0;
View
6 addons/pvr.nextpvr/src/pvrclient-nextpvr.h
@@ -67,6 +67,9 @@ class cPVRClientNextPVR
PVR_ERROR GetRecordings(ADDON_HANDLE handle);
PVR_ERROR DeleteRecording(const PVR_RECORDING &recording);
PVR_ERROR RenameRecording(const PVR_RECORDING &recording);
+ PVR_ERROR SetRecordingLastPlayedPosition(const PVR_RECORDING &recording, int lastplayedposition);
+ int GetRecordingLastPlayedPosition(const PVR_RECORDING &recording);
+ PVR_ERROR GetRecordingEdl(const PVR_RECORDING& recording, PVR_EDL_ENTRY[], int *size);
/* Timer handling */
int GetNumTimers(void);
@@ -124,6 +127,9 @@ class cPVRClientNextPVR
bool m_supportsLiveTimeshift;
long long m_currentLiveLength;
long long m_currentLivePosition;
+ int m_iDefaultPrePadding;
+ int m_iDefaultPostPadding;
+ std::string m_recordingDirectories;
CStdString m_PlaybackURL;
LiveShiftSource *m_pLiveShiftSource;

0 comments on commit ca5d4d5

Please sign in to comment.
Something went wrong with that request. Please try again.