diff -Nru kodi-pvr-vbox-4.4.8/appveyor.yml kodi-pvr-vbox-4.5.0/appveyor.yml --- kodi-pvr-vbox-4.4.8/appveyor.yml 1970-01-01 00:00:00.000000000 +0000 +++ kodi-pvr-vbox-4.5.0/appveyor.yml 2013-05-31 22:59:22.000000000 +0000 @@ -0,0 +1,33 @@ +version: BuildNr.{build} + +image: Visual Studio 2015 + +shallow_clone: true + +clone_folder: c:\projects\pvr.vbox + +environment: + app_id: pvr.vbox + + matrix: + - GENERATOR: "Visual Studio 14" + CONFIG: Release + - GENERATOR: "Visual Studio 14 Win64" + CONFIG: Release + - GENERATOR: "Visual Studio 14 Win64" + CONFIG: Release + WINSTORE: -DCMAKE_SYSTEM_NAME=WindowsStore -DCMAKE_SYSTEM_VERSION="10.0.16299.0" + - GENERATOR: "Visual Studio 14 ARM" + CONFIG: Release + WINSTORE: -DCMAKE_SYSTEM_NAME=WindowsStore -DCMAKE_SYSTEM_VERSION="10.0.16299.0" + +build_script: + - cd .. + - git clone --branch Leia --depth=1 https://github.com/xbmc/xbmc.git + - cd %app_id% + - mkdir build + - cd build + - mkdir -p definition\%app_id% + - echo %app_id% %APPVEYOR_BUILD_FOLDER% %APPVEYOR_REPO_COMMIT% > definition\%app_id%\%app_id%.txt + - cmake -T host=x64 -G "%GENERATOR%" %WINSTORE% -DADDONS_TO_BUILD=%app_id% -DCMAKE_BUILD_TYPE=%CONFIG% -DADDONS_DEFINITION_DIR=%APPVEYOR_BUILD_FOLDER%/build/definition -DADDON_SRC_PREFIX=../.. -DCMAKE_INSTALL_PREFIX=../../xbmc/addons -DPACKAGE_ZIP=1 ../../xbmc/cmake/addons + - cmake --build . --config %CONFIG% --target %app_id% diff -Nru kodi-pvr-vbox-4.4.8/build-install-mac.sh kodi-pvr-vbox-4.5.0/build-install-mac.sh --- kodi-pvr-vbox-4.4.8/build-install-mac.sh 1970-01-01 00:00:00.000000000 +0000 +++ kodi-pvr-vbox-4.5.0/build-install-mac.sh 2013-05-31 22:59:22.000000000 +0000 @@ -0,0 +1,59 @@ +#!/bin/bash + +set -e + +if [ "$#" -ne 1 ] || ! [ -d "$1" ]; then + echo "Usage: $0 " >&2 + exit 1 +fi + +if [[ "$OSTYPE" != "darwin"* ]]; then + echo "Error: Script only for use on MacOSX" >&2 + exit 1 +fi + +SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )" +if [[ "$1" = /* ]] +then + #absolute path + SCRIPT_DIR="" +else + #relative + SCRIPT_DIR="$SCRIPT_DIR/" +fi + +BINARY_ADDONS_TARGET_DIR="$1/tools/depends/target/binary-addons" +MACOSX_BINARY_ADDONS_TARGET_DIR="" +KODI_ADDONS_DIR="$HOME/Library/Application Support/Kodi/addons" +ADDON_NAME=`basename -s .git \`git config --get remote.origin.url\`` + +if [ ! -d "$BINARY_ADDONS_TARGET_DIR" ]; then + echo "Error: Could not find binary addons directory at: $BINARY_ADDONS_TARGET_DIR" >&2 + exit 1 +fi + +for DIR in "$BINARY_ADDONS_TARGET_DIR/"macosx*; do + if [ -d "${DIR}" ]; then + MACOSX_BINARY_ADDONS_TARGET_DIR="${DIR}" + break + fi +done + +if [ -z "$MACOSX_BINARY_ADDONS_TARGET_DIR" ]; then + echo "Error: Could not find binary addons build directory at: $BINARY_ADDONS_TARGET_DIR/macosx*" >&2 + exit 1 +fi + +if [ ! -d "$KODI_ADDONS_DIR" ]; then + echo "Error: Kodi addons dir does not exist at: $KODI_ADDONS_DIR" >&2 + exit 1 +fi + +cd "$MACOSX_BINARY_ADDONS_TARGET_DIR" +make + +XBMC_BUILD_ADDON_INSTALL_DIR=$(cd "$SCRIPT_DIR$1/addons/$ADDON_NAME" 2> /dev/null && pwd -P) +rm -rf "$KODI_ADDONS_DIR/$ADDON_NAME" +echo "Removed previous addon build from: $KODI_ADDONS_DIR" +cp -rf "$XBMC_BUILD_ADDON_INSTALL_DIR" "$KODI_ADDONS_DIR" +echo "Copied new addon build to: $KODI_ADDONS_DIR" diff -Nru kodi-pvr-vbox-4.4.8/CMakeLists.txt kodi-pvr-vbox-4.5.0/CMakeLists.txt --- kodi-pvr-vbox-4.4.8/CMakeLists.txt 2018-11-16 03:37:13.000000000 +0000 +++ kodi-pvr-vbox-4.5.0/CMakeLists.txt 2013-05-31 22:59:22.000000000 +0000 @@ -1,18 +1,15 @@ +cmake_minimum_required(VERSION 3.5) project(pvr.vbox) -cmake_minimum_required(VERSION 2.6) - set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${PROJECT_SOURCE_DIR}) -enable_language(CXX) - find_package(Kodi REQUIRED) find_package(kodiplatform REQUIRED) find_package(p8-platform REQUIRED) include_directories(${kodiplatform_INCLUDE_DIRS} ${p8-platform_INCLUDE_DIRS} - ${KODI_INCLUDE_DIR} + ${KODI_INCLUDE_DIR}/.. # Hack way with "/..", need bigger Kodi cmake rework to match right include ways ${PROJECT_SOURCE_DIR}) # Sources and headers @@ -33,6 +30,8 @@ src/vbox/GuideChannelMapper.cpp src/vbox/Recording.h src/vbox/Recording.cpp + src/vbox/RecordingReader.cpp + src/vbox/RecordingReader.h src/vbox/Reminder.h src/vbox/Reminder.cpp src/vbox/ReminderManager.h diff -Nru kodi-pvr-vbox-4.4.8/debian/changelog kodi-pvr-vbox-4.5.0/debian/changelog --- kodi-pvr-vbox-4.4.8/debian/changelog 2019-01-15 11:11:38.000000000 +0000 +++ kodi-pvr-vbox-4.5.0/debian/changelog 2013-05-31 22:59:22.000000000 +0000 @@ -1,4 +1,4 @@ -kodi-pvr-vbox (4.4.8-1~xenial) xenial; urgency=low +kodi-pvr-vbox (4.5.0-1~xenial) xenial; urgency=low [ kodi ] * autogenerated dummy changelog diff -Nru kodi-pvr-vbox-4.4.8/Jenkinsfile kodi-pvr-vbox-4.5.0/Jenkinsfile --- kodi-pvr-vbox-4.4.8/Jenkinsfile 2018-11-16 03:37:13.000000000 +0000 +++ kodi-pvr-vbox-4.5.0/Jenkinsfile 2013-05-31 22:59:22.000000000 +0000 @@ -1 +1 @@ -buildPlugin() +buildPlugin(version: "Leia") diff -Nru kodi-pvr-vbox-4.4.8/pvr.vbox/addon.xml.in kodi-pvr-vbox-4.5.0/pvr.vbox/addon.xml.in --- kodi-pvr-vbox-4.4.8/pvr.vbox/addon.xml.in 2019-01-15 11:11:28.000000000 +0000 +++ kodi-pvr-vbox-4.5.0/pvr.vbox/addon.xml.in 2013-05-31 22:59:22.000000000 +0000 @@ -1,7 +1,7 @@ @ADDON_DEPENDS@ diff -Nru kodi-pvr-vbox-4.4.8/pvr.vbox/changelog.txt kodi-pvr-vbox-4.5.0/pvr.vbox/changelog.txt --- kodi-pvr-vbox-4.4.8/pvr.vbox/changelog.txt 2018-11-16 03:37:13.000000000 +0000 +++ kodi-pvr-vbox-4.5.0/pvr.vbox/changelog.txt 2013-05-31 22:59:22.000000000 +0000 @@ -1,6 +1,12 @@ +4.5.0 + - Implement GetStreamTimes API for timeshifting support + - Mac build script + - Fix compiler warnings + - Add appveyor.yml + 4.4.3 - Fixed wrong size caused crash in Android Oreo (8) - + 4.4.1 - Fix recordings playback not working (was broken since v4.2.0) @@ -59,10 +65,10 @@ - fixed recordings and timers not loading 3.6.5 - - updated Language files from Transifex + - updated Language files from Transifex 3.6.4 - - updated Language files from Transifex + - updated Language files from Transifex 3.6.3 - fixed crash caused by empty XML strings @@ -71,7 +77,7 @@ 3.6.2 - fixed more Coverity issues - - updated Language files from Transifex + - updated Language files from Transifex 3.6.1 - fixed some Coverity issues @@ -165,7 +171,7 @@ 2.0.1 - support event/programme icons from external guide data - - improve XMLTV reading, should hopefully fix some issues + - improve XMLTV reading, should hopefully fix some issues 2.0.0 - updated to PVR Addon API v2.0.0 @@ -254,7 +260,7 @@ - fixed some issues with the timeshift buffer - fixed Linux build (thanks dhead666) - changed so that recordings without an ID (mostly external ones) are not visible since they can't be deleted - - + - 0.9.4 - fixed compatibility with older tinyxml2 libraries - fixed Debian packaging diff -Nru kodi-pvr-vbox-4.4.8/README.md kodi-pvr-vbox-4.5.0/README.md --- kodi-pvr-vbox-4.4.8/README.md 2018-11-16 03:37:13.000000000 +0000 +++ kodi-pvr-vbox-4.5.0/README.md 2013-05-31 22:59:22.000000000 +0000 @@ -12,13 +12,41 @@ ### Linux -1. `git clone https://github.com/xbmc/xbmc.git` +1. `git clone --branch Leia https://github.com/xbmc/xbmc.git` 2. `git clone https://github.com/kodi-pvr/pvr.vbox.git` 3. `cd pvr.vbox && mkdir build && cd build` 4. `cmake -DADDONS_TO_BUILD=pvr.vbox -DADDON_SRC_PREFIX=../.. -DCMAKE_BUILD_TYPE=Debug -DCMAKE_INSTALL_PREFIX=../../xbmc/kodi-build/addons -DPACKAGE_ZIP=1 ../../xbmc/cmake/addons` 5. `make` -#### Windows +### Mac OSX + +In order to build the addon on mac the steps are different to Linux and Windows as the cmake command above will not produce an addon that will run in kodi. Instead using make directly as per the supported build steps for kodi on mac we can build the tools and just the addon on it's own. Following this we copy the addon into kodi. Note that we checkout kodi to a separate directory as this repo will only only be used to build the addon and nothing else. + +#### Build tools and initial addon build + +1. Get the repos + * `cd $HOME` + * `git clone https://github.com/xbmc/xbmc xbmc-addon` + * `git clone https://github.com/kodi-pvr/pvr.vbox` +2. Build the kodi tools + * `cd $HOME/xbmc-addon/tools/depends` + * `./bootstrap` + * `./configure --host=x86_64-apple-darwin` + * `make -j$(getconf _NPROCESSORS_ONLN)` +3. Build the addon + * `cd $HOME/xbmc-addon` + * `make -j$(getconf _NPROCESSORS_ONLN) -C tools/depends/target/binary-addons ADDONS="pvr.vbox" ADDON_SRC_PREFIX=$HOME` + +Note that the steps in the following section need to be performed before the addon is installed and you can run it in Kodi. + +#### To rebuild the addon and copy to kodi after changes (after the initial addon build) + +1. `cd $HOME/pvr.vbox` +2. `./build-install-mac.sh ../xbmc-addon` + +If you would prefer to run the rebuild steps manually instead of using the above helper script check the appendix [here](#manual-steps-to-rebuild-the-addon-on-macosx) + +### Windows These instructions may be outdated. I'm assuming here that you'll check out all source code into `C:\Projects`, you'll have to adjust that if @@ -32,8 +60,6 @@ 6. The addon DLL is built and located in `C:\Projects\xbmc\addons`. If you run Kodi now from inside Visual Studio the addon will appear automatically under "System addons". If you don't want to bother compiling Kodi from source, install it as you normally would and copy the `pvr.vbox` into `%APPDATA%\Kodi\addons`. 7. Run Kodi, configure and enable the addon, then enable Live TV. - - ## Settings This list contains some explanation for the non-obvious settings: @@ -92,3 +118,23 @@ The code is licensed under the GNU GPL version 2 license. +## Appendix + +### Manual Steps to rebuild the addon on MacOSX + +The following steps can be followed manually instead of using the `build-install-mac.sh` in the root of the addon repo after the [initial addon build](#build-tools-and-initial-addon-build) has been completed. + +**To rebuild the addon after changes** + +1. `rm tools/depends/target/binary-addons/.installed-macosx*` +2. `make -j$(getconf _NPROCESSORS_ONLN) -C tools/depends/target/binary-addons ADDONS="pvr.vbox" ADDON_SRC_PREFIX=$HOME` + +or + +1. `cd tools/depends/target/binary-addons/macosx*` +2. `make` + +**Copy the addon to the Kodi addon directory on Mac** + +1. `rm -rf "$HOME/Library/Application Support/Kodi/addons/pvr.vbox"` +2. `cp -rf $HOME/xbmc-addon/addons/pvr.vbox "$HOME/Library/Application Support/Kodi/addons"` diff -Nru kodi-pvr-vbox-4.4.8/src/client.cpp kodi-pvr-vbox-4.5.0/src/client.cpp --- kodi-pvr-vbox-4.4.8/src/client.cpp 2018-11-16 03:37:13.000000000 +0000 +++ kodi-pvr-vbox-4.5.0/src/client.cpp 2013-05-31 22:59:22.000000000 +0000 @@ -21,12 +21,13 @@ #include #include "p8-platform/util/util.h" -#include "xbmc_pvr_dll.h" +#include "kodi/xbmc_pvr_dll.h" #include "client.h" #include "compat.h" #include "vbox/Exceptions.h" #include "vbox/VBox.h" #include "vbox/ContentIdentifier.h" +#include "vbox/RecordingReader.h" #include "timeshift/DummyBuffer.h" #include "timeshift/FilesystemBuffer.h" #include "xmltv/Utilities.h" @@ -43,6 +44,7 @@ ADDON_STATUS g_status = ADDON_STATUS_UNKNOWN; VBox *g_vbox = nullptr; timeshift::Buffer *g_timeshiftBuffer = nullptr; +RecordingReader* recordingReader = nullptr; std::string g_internalHostname; std::string g_externalHostname; @@ -76,8 +78,8 @@ extern "C" { - void ADDON_ReadSettings() - { +void ADDON_ReadSettings() +{ #define UPDATE_INT(var, key, def)\ if (!XBMC->GetSetting(key, &var))\ var = def; @@ -88,154 +90,154 @@ else\ var = def; - char buffer[1024]; + char buffer[1024]; - UPDATE_STR(g_internalHostname, "hostname", buffer, ""); - UPDATE_INT(g_internalHttpPort, "http_port", 80); - UPDATE_INT(g_internalHttpsPort, "https_port", 0); - UPDATE_INT(g_internalUpnpPort, "upnp_port", 55555); - UPDATE_STR(g_externalHostname, "external_hostname", buffer, ""); - UPDATE_INT(g_externalHttpPort, "external_http_port", 19999); - UPDATE_INT(g_externalHttpsPort, "external_https_port", 0); - UPDATE_INT(g_externalUpnpPort, "external_upnp_port", 55555); - UPDATE_INT(g_internalConnectionTimeout, "connection_timeout", 3); - UPDATE_INT(g_externalConnectionTimeout, "external_connection_timeout", 10); - UPDATE_INT(g_useExternalXmltv, "use_external_xmltv", false); - UPDATE_STR(g_externalXmltvPath, "external_xmltv_path", buffer, ""); - UPDATE_INT(g_preferExternalXmltv, "prefer_external_xmltv", false); - UPDATE_INT(g_useExternalXmltvIcons, "use_external_xmltv_icons", false); - UPDATE_INT(g_setChannelIdUsingOrder, "set_channelid_using_order", CH_ORDER_BY_LCN); - UPDATE_INT(g_remindMinsBeforeProg, "reminder_mins_before_prog", 0); - UPDATE_INT(g_timeshiftEnabled, "timeshift_enabled", false); - UPDATE_STR(g_timeshiftBufferPath, "timeshift_path", buffer, ""); + UPDATE_STR(g_internalHostname, "hostname", buffer, ""); + UPDATE_INT(g_internalHttpPort, "http_port", 80); + UPDATE_INT(g_internalHttpsPort, "https_port", 0); + UPDATE_INT(g_internalUpnpPort, "upnp_port", 55555); + UPDATE_STR(g_externalHostname, "external_hostname", buffer, ""); + UPDATE_INT(g_externalHttpPort, "external_http_port", 19999); + UPDATE_INT(g_externalHttpsPort, "external_https_port", 0); + UPDATE_INT(g_externalUpnpPort, "external_upnp_port", 55555); + UPDATE_INT(g_internalConnectionTimeout, "connection_timeout", 3); + UPDATE_INT(g_externalConnectionTimeout, "external_connection_timeout", 10); + UPDATE_INT(g_useExternalXmltv, "use_external_xmltv", false); + UPDATE_STR(g_externalXmltvPath, "external_xmltv_path", buffer, ""); + UPDATE_INT(g_preferExternalXmltv, "prefer_external_xmltv", false); + UPDATE_INT(g_useExternalXmltvIcons, "use_external_xmltv_icons", false); + UPDATE_INT(g_setChannelIdUsingOrder, "set_channelid_using_order", CH_ORDER_BY_LCN); + UPDATE_INT(g_remindMinsBeforeProg, "reminder_mins_before_prog", 0); + UPDATE_INT(g_timeshiftEnabled, "timeshift_enabled", false); + UPDATE_STR(g_timeshiftBufferPath, "timeshift_path", buffer, ""); #undef UPDATE_INT #undef UPDATE_STR - } +} - ADDON_STATUS ADDON_Create(void* hdl, void* props) - { - if (!hdl || !props) - return ADDON_STATUS_UNKNOWN; +ADDON_STATUS ADDON_Create(void* hdl, void* props) +{ + if (!hdl || !props) + return ADDON_STATUS_UNKNOWN; + + // Instantiate helpers + XBMC = new CHelper_libXBMC_addon; + PVR = new CHelper_libXBMC_pvr; + GUI = new CHelper_libKODI_guilib; + + if (!XBMC->RegisterMe(hdl) || + !PVR->RegisterMe(hdl) || + !GUI->RegisterMe(hdl) ) + { + SAFE_DELETE(XBMC); + SAFE_DELETE(PVR); + SAFE_DELETE(GUI); + return ADDON_STATUS_PERMANENT_FAILURE; + } + + // Read the settings. The addon is restarted whenever a setting is changed + // so we only need to read them here. + ADDON_ReadSettings(); + Settings settings; + + settings.m_internalConnectionParams = + { + g_internalHostname, + g_internalHttpPort, + g_internalHttpsPort, + g_internalUpnpPort, + g_internalConnectionTimeout + }; + + settings.m_externalConnectionParams = + { + g_externalHostname, + g_externalHttpPort, + g_externalHttpsPort, + g_externalUpnpPort, + g_externalConnectionTimeout + }; + + settings.m_useExternalXmltv = g_useExternalXmltv; + settings.m_externalXmltvPath = g_externalXmltvPath; + settings.m_preferExternalXmltv = g_preferExternalXmltv; + settings.m_useExternalXmltvIcons = g_useExternalXmltvIcons; + settings.m_setChannelIdUsingOrder = g_setChannelIdUsingOrder; + settings.m_remindMinsBeforeProg = g_remindMinsBeforeProg; + settings.m_timeshiftEnabled = g_timeshiftEnabled; + settings.m_timeshiftBufferPath = g_timeshiftBufferPath; + + // Create the addon + VBox::Log(LOG_DEBUG, "creating VBox Gateway PVR addon"); + g_status = ADDON_STATUS_UNKNOWN; + g_vbox = new VBox(settings); - // Instantiate helpers - XBMC = new CHelper_libXBMC_addon; - PVR = new CHelper_libXBMC_pvr; - GUI = new CHelper_libKODI_guilib; - - if (!XBMC->RegisterMe(hdl) || - !PVR->RegisterMe(hdl) || - !GUI->RegisterMe(hdl) ) - { - SAFE_DELETE(XBMC); - SAFE_DELETE(PVR); - SAFE_DELETE(GUI); - return ADDON_STATUS_PERMANENT_FAILURE; - } + // Validate settings + if (g_vbox->ValidateSettings()) + { + // Start the addon + try { + g_vbox->Initialize(); + g_status = ADDON_STATUS_OK; - // Read the settings. The addon is restarted whenever a setting is changed - // so we only need to read them here. - ADDON_ReadSettings(); - Settings settings; - - settings.m_internalConnectionParams = - { - g_internalHostname, - g_internalHttpPort, - g_internalHttpsPort, - g_internalUpnpPort, - g_internalConnectionTimeout - }; - - settings.m_externalConnectionParams = - { - g_externalHostname, - g_externalHttpPort, - g_externalHttpsPort, - g_externalUpnpPort, - g_externalConnectionTimeout - }; - - settings.m_useExternalXmltv = g_useExternalXmltv; - settings.m_externalXmltvPath = g_externalXmltvPath; - settings.m_preferExternalXmltv = g_preferExternalXmltv; - settings.m_useExternalXmltvIcons = g_useExternalXmltvIcons; - settings.m_setChannelIdUsingOrder = g_setChannelIdUsingOrder; - settings.m_remindMinsBeforeProg = g_remindMinsBeforeProg; - settings.m_timeshiftEnabled = g_timeshiftEnabled; - settings.m_timeshiftBufferPath = g_timeshiftBufferPath; - - // Create the addon - VBox::Log(LOG_DEBUG, "creating VBox Gateway PVR addon"); - g_status = ADDON_STATUS_UNKNOWN; - g_vbox = new VBox(settings); + // Attach event handlers + g_vbox->OnChannelsUpdated = []() { PVR->TriggerChannelUpdate(); }; + g_vbox->OnRecordingsUpdated = []() { PVR->TriggerRecordingUpdate(); }; + g_vbox->OnTimersUpdated = []() { PVR->TriggerTimerUpdate(); }; + g_vbox->OnGuideUpdated = []() + { + for (const auto &channel : g_vbox->GetChannels()) + PVR->TriggerEpgUpdate(ContentIdentifier::GetUniqueId(channel)); + }; + + // Create the timeshift buffer + if (settings.m_timeshiftEnabled) + g_timeshiftBuffer = new timeshift::FilesystemBuffer(settings.m_timeshiftBufferPath); + else + g_timeshiftBuffer = new timeshift::DummyBuffer(); - // Validate settings - if (g_vbox->ValidateSettings()) - { - // Start the addon - try { - g_vbox->Initialize(); - g_status = ADDON_STATUS_OK; + g_timeshiftBuffer->SetReadTimeout(g_vbox->GetConnectionParams().timeout); - // Attach event handlers - g_vbox->OnChannelsUpdated = []() { PVR->TriggerChannelUpdate(); }; - g_vbox->OnRecordingsUpdated = []() { PVR->TriggerRecordingUpdate(); }; - g_vbox->OnTimersUpdated = []() { PVR->TriggerTimerUpdate(); }; - g_vbox->OnGuideUpdated = []() - { - for (const auto &channel : g_vbox->GetChannels()) - PVR->TriggerEpgUpdate(ContentIdentifier::GetUniqueId(channel)); - }; - - // Create the timeshift buffer - if (settings.m_timeshiftEnabled) - g_timeshiftBuffer = new timeshift::FilesystemBuffer(settings.m_timeshiftBufferPath); - else - g_timeshiftBuffer = new timeshift::DummyBuffer(); - - g_timeshiftBuffer->SetReadTimeout(g_vbox->GetConnectionParams().timeout); - - // initializing TV Settings Client Specific menu hooks - PVR_MENUHOOK hooks[] = { { MENUHOOK_ID_RESCAN_EPG, 30106, PVR_MENUHOOK_SETTING }, - { MENUHOOK_ID_SYNC_EPG, 30107, PVR_MENUHOOK_SETTING }, - { MENUHOOK_ID_EPG_REMINDER, 30110, PVR_MENUHOOK_EPG }, - { MENUHOOK_ID_CANCEL_EPG_REMINDER, 30112, PVR_MENUHOOK_EPG }, - { MENUHOOK_ID_MANUAL_REMINDER, 30111, PVR_MENUHOOK_CHANNEL }, - { MENUHOOK_ID_CANCEL_CHANNEL_REMINDER, 30113, PVR_MENUHOOK_CHANNEL } }; + // initializing TV Settings Client Specific menu hooks + PVR_MENUHOOK hooks[] = { { MENUHOOK_ID_RESCAN_EPG, 30106, PVR_MENUHOOK_SETTING }, + { MENUHOOK_ID_SYNC_EPG, 30107, PVR_MENUHOOK_SETTING }, + { MENUHOOK_ID_EPG_REMINDER, 30110, PVR_MENUHOOK_EPG }, + { MENUHOOK_ID_CANCEL_EPG_REMINDER, 30112, PVR_MENUHOOK_EPG }, + { MENUHOOK_ID_MANUAL_REMINDER, 30111, PVR_MENUHOOK_CHANNEL }, + { MENUHOOK_ID_CANCEL_CHANNEL_REMINDER, 30113, PVR_MENUHOOK_CHANNEL } }; - for (int i = 0; i < sizeof(hooks) / sizeof(PVR_MENUHOOK); ++i) - PVR->AddMenuHook(&hooks[i]); - } - catch (FirmwareVersionException &e) { - XBMC->QueueNotification(ADDON::QUEUE_ERROR, e.what()); - g_status = ADDON_STATUS_PERMANENT_FAILURE; - } - catch (VBoxException &e) { - VBox::LogException(e); - g_status = ADDON_STATUS_LOST_CONNECTION; - } + for (int i = 0; i < sizeof(hooks) / sizeof(PVR_MENUHOOK); ++i) + PVR->AddMenuHook(&hooks[i]); + } + catch (FirmwareVersionException &e) { + XBMC->QueueNotification(ADDON::QUEUE_ERROR, e.what()); + g_status = ADDON_STATUS_PERMANENT_FAILURE; + } + catch (VBoxException &e) { + VBox::LogException(e); + g_status = ADDON_STATUS_LOST_CONNECTION; } - else - g_status = ADDON_STATUS_NEED_SETTINGS; - - return g_status; } + else + g_status = ADDON_STATUS_NEED_SETTINGS; - ADDON_STATUS ADDON_GetStatus() - { - return g_status; - } + return g_status; +} - void ADDON_Destroy() - { - SAFE_DELETE(g_vbox); - SAFE_DELETE(g_timeshiftBuffer); - g_status = ADDON_STATUS_UNKNOWN; - } +ADDON_STATUS ADDON_GetStatus() +{ + return g_status; +} - ADDON_STATUS ADDON_SetSetting(const char *settingName, const void *settingValue) - { +void ADDON_Destroy() +{ + SAFE_DELETE(g_vbox); + SAFE_DELETE(g_timeshiftBuffer); + g_status = ADDON_STATUS_UNKNOWN; +} + +ADDON_STATUS ADDON_SetSetting(const char *settingName, const void *settingValue) +{ #define UPDATE_STR(key, var)\ if (!strcmp(settingName, key))\ {\ @@ -260,963 +262,1031 @@ return ADDON_STATUS_OK;\ } - const vbox::Settings &settings = g_vbox->GetSettings(); + const vbox::Settings &settings = g_vbox->GetSettings(); - UPDATE_STR("hostname", settings.m_internalConnectionParams.hostname); - UPDATE_INT("http_port", int, settings.m_internalConnectionParams.httpPort); - UPDATE_INT("https_port", int, settings.m_internalConnectionParams.httpsPort); - UPDATE_INT("upnp_port", int, settings.m_internalConnectionParams.upnpPort); - UPDATE_INT("connection_timeout", int, settings.m_internalConnectionParams.timeout); - UPDATE_STR("external_hostname", settings.m_externalConnectionParams.hostname); - UPDATE_INT("external_http_port", int, settings.m_externalConnectionParams.httpPort); - UPDATE_INT("external_https_port", int, settings.m_externalConnectionParams.httpsPort); - UPDATE_INT("external_upnp_port", int, settings.m_externalConnectionParams.upnpPort); - UPDATE_INT("external_connection_timeout", int, settings.m_externalConnectionParams.timeout); - UPDATE_INT("use_external_xmltv", bool, settings.m_useExternalXmltv); - UPDATE_STR("external_xmltv_path", settings.m_externalXmltvPath); - UPDATE_INT("prefer_external_xmltv", bool, settings.m_preferExternalXmltv); - UPDATE_INT("use_external_xmltv_icons", bool, settings.m_useExternalXmltvIcons); - UPDATE_INT("set_channelid_using_order", ChannelOrder, settings.m_setChannelIdUsingOrder); - UPDATE_INT("reminder_mins_before_prog", unsigned int, settings.m_remindMinsBeforeProg) - UPDATE_INT("timeshift_enabled", bool, settings.m_timeshiftEnabled); - UPDATE_STR("timeshift_path", settings.m_timeshiftBufferPath); + UPDATE_STR("hostname", settings.m_internalConnectionParams.hostname); + UPDATE_INT("http_port", int, settings.m_internalConnectionParams.httpPort); + UPDATE_INT("https_port", int, settings.m_internalConnectionParams.httpsPort); + UPDATE_INT("upnp_port", int, settings.m_internalConnectionParams.upnpPort); + UPDATE_INT("connection_timeout", int, settings.m_internalConnectionParams.timeout); + UPDATE_STR("external_hostname", settings.m_externalConnectionParams.hostname); + UPDATE_INT("external_http_port", int, settings.m_externalConnectionParams.httpPort); + UPDATE_INT("external_https_port", int, settings.m_externalConnectionParams.httpsPort); + UPDATE_INT("external_upnp_port", int, settings.m_externalConnectionParams.upnpPort); + UPDATE_INT("external_connection_timeout", int, settings.m_externalConnectionParams.timeout); + UPDATE_INT("use_external_xmltv", bool, settings.m_useExternalXmltv); + UPDATE_STR("external_xmltv_path", settings.m_externalXmltvPath); + UPDATE_INT("prefer_external_xmltv", bool, settings.m_preferExternalXmltv); + UPDATE_INT("use_external_xmltv_icons", bool, settings.m_useExternalXmltvIcons); + UPDATE_INT("set_channelid_using_order", ChannelOrder, settings.m_setChannelIdUsingOrder); + UPDATE_INT("reminder_mins_before_prog", unsigned int, settings.m_remindMinsBeforeProg) + UPDATE_INT("timeshift_enabled", bool, settings.m_timeshiftEnabled); + UPDATE_STR("timeshift_path", settings.m_timeshiftBufferPath); - return ADDON_STATUS_OK; + return ADDON_STATUS_OK; #undef UPDATE_INT #undef UPDATE_STR - } +} - void OnSystemSleep() - { - } +void OnSystemSleep() +{ +} - void OnSystemWake() - { - } +void OnSystemWake() +{ +} - void OnPowerSavingActivated() - { - } +void OnPowerSavingActivated() +{ +} - void OnPowerSavingDeactivated() - { - } +void OnPowerSavingDeactivated() +{ +} - PVR_ERROR GetAddonCapabilities(PVR_ADDON_CAPABILITIES* pCapabilities) - { - pCapabilities->bSupportsTV = true; - pCapabilities->bSupportsRadio = true; - pCapabilities->bSupportsChannelGroups = false; - pCapabilities->bSupportsEPG = true; - pCapabilities->bHandlesInputStream = true; +PVR_ERROR GetAddonCapabilities(PVR_ADDON_CAPABILITIES* pCapabilities) +{ + pCapabilities->bSupportsTV = true; + pCapabilities->bSupportsRadio = true; + pCapabilities->bSupportsChannelGroups = false; + pCapabilities->bSupportsEPG = true; + pCapabilities->bHandlesInputStream = true; + + // Recording capability is determined further down, we'll assume false + // in case the real capabilities cannot be determined for some reason + pCapabilities->bSupportsRecordings = false; + pCapabilities->bSupportsTimers = false; + + // Unsupported features + pCapabilities->bSupportsRecordingsUndelete = false; + pCapabilities->bSupportsChannelScan = false; + pCapabilities->bSupportsChannelSettings = false; + pCapabilities->bHandlesDemuxing = false; + pCapabilities->bSupportsRecordingPlayCount = false; + pCapabilities->bSupportsLastPlayedPosition = false; + pCapabilities->bSupportsRecordingEdl = false; + + // Wait for initialization until we decide if we support recordings or not. + // Recording is only possible when external media is present + if (g_vbox->GetStateHandler().WaitForState(StartupState::INITIALIZED) + && g_vbox->SupportsRecordings()) + { + pCapabilities->bSupportsRecordings = true; + pCapabilities->bSupportsTimers = true; + } + + pCapabilities->bSupportsRecordingsRename = false; + pCapabilities->bSupportsRecordingsLifetimeChange = false; + pCapabilities->bSupportsDescrambleInfo = false; - // Recording capability is determined further down, we'll assume false - // in case the real capabilities cannot be determined for some reason - pCapabilities->bSupportsRecordings = false; - pCapabilities->bSupportsTimers = false; + return PVR_ERROR_NO_ERROR; +} - // Unsupported features - pCapabilities->bSupportsRecordingsUndelete = false; - pCapabilities->bSupportsChannelScan = false; - pCapabilities->bSupportsChannelSettings = false; - pCapabilities->bHandlesDemuxing = false; - pCapabilities->bSupportsRecordingPlayCount = false; - pCapabilities->bSupportsLastPlayedPosition = false; - pCapabilities->bSupportsRecordingEdl = false; +const char *GetBackendName() +{ + static std::string backendName = g_vbox->GetBackendName(); + return backendName.c_str(); +} - // Wait for initialization until we decide if we support recordings or not. - // Recording is only possible when external media is present - if (g_vbox->GetStateHandler().WaitForState(StartupState::INITIALIZED) - && g_vbox->SupportsRecordings()) - { - pCapabilities->bSupportsRecordings = true; - pCapabilities->bSupportsTimers = true; - } +const char *GetBackendVersion() +{ + static std::string backendVersion = g_vbox->GetBackendVersion(); + return backendVersion.c_str(); +} - pCapabilities->bSupportsRecordingsRename = false; - pCapabilities->bSupportsRecordingsLifetimeChange = false; - pCapabilities->bSupportsDescrambleInfo = false; +const char *GetConnectionString() +{ + static std::string connectionString = g_vbox->GetConnectionString(); + return connectionString.c_str(); +} - return PVR_ERROR_NO_ERROR; - } +const char *GetBackendHostname() +{ + static std::string backendHostname = g_vbox->GetBackendHostname(); + return backendHostname.c_str(); +} - const char *GetBackendName(void) - { - static std::string backendName = g_vbox->GetBackendName(); - return backendName.c_str(); +int GetChannelsAmount() +{ + try { + return g_vbox->GetChannelsAmount(); } - - const char *GetBackendVersion(void) + catch (VBoxException &e) { - static std::string backendVersion = g_vbox->GetBackendVersion(); - return backendVersion.c_str(); + g_vbox->LogException(e); + return PVR_ERROR_SERVER_ERROR; } +} - const char *GetConnectionString(void) - { - static std::string connectionString = g_vbox->GetConnectionString(); - return connectionString.c_str(); - } +PVR_ERROR GetChannels(ADDON_HANDLE handle, bool bRadio) +{ + auto &channels = g_vbox->GetChannels(); + unsigned int i = 0; + + for (const auto &item : channels) + { + // Skip those that are not of the correct type + if (item->m_radio != bRadio) + continue; + + PVR_CHANNEL channel; + memset(&channel, 0, sizeof(PVR_CHANNEL)); + + channel.iUniqueId = ContentIdentifier::GetUniqueId(item); + channel.bIsRadio = item->m_radio; + + // Override LCN if backend channel order should be forced + ++i; + if (g_vbox->GetSettings().m_setChannelIdUsingOrder == CH_ORDER_BY_INDEX) + channel.iChannelNumber = i; + // default - CH_ORDER_BY_LCN + else + channel.iChannelNumber = item->m_number; - const char *GetBackendHostname(void) - { - static std::string backendHostname = g_vbox->GetBackendHostname(); - return backendHostname.c_str(); - } + channel.iEncryptionSystem = item->m_encrypted ? 0xFFFF : 0x0000; - int GetChannelsAmount(void) - { - try { - return g_vbox->GetChannelsAmount(); - } - catch (VBoxException &e) + strncpy(channel.strChannelName, item->m_name.c_str(), + sizeof(channel.strChannelName)); + strncpy(channel.strIconPath, item->m_iconUrl.c_str(), + sizeof(channel.strIconPath)); + + // Set stream format for TV channels + if (!item->m_radio) { - g_vbox->LogException(e); - return PVR_ERROR_SERVER_ERROR; + strncpy(channel.strInputFormat, "video/mp2t", + sizeof(channel.strInputFormat)); } + + VBox::Log(LOG_INFO, "Adding channel %d: %s. Icon: %s", + channel.iChannelNumber, channel.strChannelName, channel.strIconPath); + + PVR->TransferChannelEntry(handle, &channel); } - PVR_ERROR GetChannels(ADDON_HANDLE handle, bool bRadio) - { - auto &channels = g_vbox->GetChannels(); - unsigned int i = 0; + return PVR_ERROR_NO_ERROR; +} - for (const auto &item : channels) - { - // Skip those that are not of the correct type - if (item->m_radio != bRadio) - continue; +PVR_ERROR GetDriveSpace(long long *iTotal, long long *iUsed) +{ + *iTotal = g_vbox->GetRecordingTotalSpace() / 1024; + *iUsed = g_vbox->GetRecordingUsedSpace() / 1024; - PVR_CHANNEL channel; - memset(&channel, 0, sizeof(PVR_CHANNEL)); + return PVR_ERROR_NO_ERROR; +} - channel.iUniqueId = ContentIdentifier::GetUniqueId(item); - channel.bIsRadio = item->m_radio; +int GetRecordingsAmount(bool deleted) +{ + return g_vbox->GetRecordingsAmount(); +} - // Override LCN if backend channel order should be forced - ++i; - if (g_vbox->GetSettings().m_setChannelIdUsingOrder == CH_ORDER_BY_INDEX) - channel.iChannelNumber = i; - // default - CH_ORDER_BY_LCN - else - channel.iChannelNumber = item->m_number; +PVR_ERROR GetRecordings(ADDON_HANDLE handle, bool deleted) +{ + auto &recordings = g_vbox->GetRecordingsAndTimers(); + + for (const auto &item : recordings) + { + // Skip timers + if (!item->IsRecording()) + continue; + + PVR_RECORDING recording; + memset(&recording, 0, sizeof(PVR_RECORDING)); + + time_t startTime = xmltv::Utilities::XmltvToUnixTime(item->m_startTime); + time_t endTime = xmltv::Utilities::XmltvToUnixTime(item->m_endTime); + unsigned int id = item->m_id; + + recording.recordingTime = startTime; + std::time_t now = std::time(nullptr); + if (now > endTime) + recording.iDuration = static_cast(endTime - startTime); + else + recording.iDuration = static_cast(now - startTime); + recording.iEpgEventId = id; - channel.iEncryptionSystem = item->m_encrypted ? 0xFFFF : 0x0000; + strncpy(recording.strChannelName, item->m_channelName.c_str(), + sizeof(recording.strChannelName)); - strncpy(channel.strChannelName, item->m_name.c_str(), - sizeof(channel.strChannelName)); - strncpy(channel.strIconPath, item->m_iconUrl.c_str(), - sizeof(channel.strIconPath)); + strncpy(recording.strRecordingId, compat::to_string(id).c_str(), + sizeof(recording.strRecordingId)); - // Set stream format for TV channels - if (!item->m_radio) - { - strncpy(channel.strInputFormat, "video/mp2t", - sizeof(channel.strInputFormat)); - } + strncpy(recording.strTitle, item->m_title.c_str(), + sizeof(recording.strTitle)); + + strncpy(recording.strPlot, item->m_description.c_str(), + sizeof(recording.strPlot)); - VBox::Log(LOG_INFO, "Adding channel %d: %s. Icon: %s", - channel.iChannelNumber, channel.strChannelName, channel.strIconPath); + recording.iChannelUid = PVR_CHANNEL_INVALID_UID; + recording.channelType = PVR_RECORDING_CHANNEL_TYPE_UNKNOWN; + + // Find the recordings channel and use its unique ID if we find one + auto &channels = g_vbox->GetChannels(); + auto it = std::find_if(channels.cbegin(), channels.cend(), + [&item](const ChannelPtr &channel) + { + return channel->m_xmltvName == item->m_channelId; + }); - PVR->TransferChannelEntry(handle, &channel); + if (it != channels.cend()) + { + ChannelPtr channel = *it; + recording.iChannelUid = ContentIdentifier::GetUniqueId(channel); + if (channel->m_radio) + recording.channelType = PVR_RECORDING_CHANNEL_TYPE_RADIO; + else + recording.channelType = PVR_RECORDING_CHANNEL_TYPE_TV; } - return PVR_ERROR_NO_ERROR; + PVR->TransferRecordingEntry(handle, &recording); } - PVR_ERROR GetDriveSpace(long long *iTotal, long long *iUsed) - { - *iTotal = g_vbox->GetRecordingTotalSpace() / 1024; - *iUsed = g_vbox->GetRecordingUsedSpace() / 1024; + return PVR_ERROR_NO_ERROR; +} - return PVR_ERROR_NO_ERROR; - } +PVR_ERROR DeleteRecording(const PVR_RECORDING &recording) +{ + try { + unsigned int id = compat::stoui(recording.strRecordingId); - int GetRecordingsAmount(bool deleted) + if (g_vbox->DeleteRecordingOrTimer(id)) + return PVR_ERROR_NO_ERROR; + else + return PVR_ERROR_FAILED; + } + catch (...) { - return g_vbox->GetRecordingsAmount(); + return PVR_ERROR_INVALID_PARAMETERS; } +} - PVR_ERROR GetRecordings(ADDON_HANDLE handle, bool deleted) - { - auto &recordings = g_vbox->GetRecordingsAndTimers(); +// Recording stream methods +bool OpenRecordedStream(const PVR_RECORDING& recording) +{ + if (recordingReader) + SAFE_DELETE(recordingReader); + recordingReader = nullptr; + + unsigned int id = compat::stoui(recording.strRecordingId); + auto &recordings = g_vbox->GetRecordingsAndTimers(); + auto recIt = std::find_if(recordings.begin(), recordings.end(), + [id](const RecordingPtr &item) + { + return item->IsRecording() && id == item->m_id; + }); + + if (recIt == recordings.end()) + return PVR_ERROR_SERVER_ERROR; + + std::time_t now = std::time(nullptr), start = 0, end = 0; + std::string channelName = recording.strChannelName; + time_t recordingTime = recording.recordingTime; + auto timerIt = std::find_if(recordings.begin(), recordings.end(), + [now, channelName, recordingTime](const RecordingPtr &item) + { + return item->IsTimer() && item->IsRunning(now, channelName, recordingTime); + }); + if (timerIt != recordings.end()) + { + auto& timer = *timerIt; + start = xmltv::Utilities::XmltvToUnixTime(timer->m_startTime); + end = xmltv::Utilities::XmltvToUnixTime(timer->m_endTime); + } - for (const auto &item : recordings) - { - // Skip timers - if (!item->IsRecording()) - continue; + recordingReader = new RecordingReader((*recIt)->m_url.c_str(), start, end, recording.iDuration); - PVR_RECORDING recording; - memset(&recording, 0, sizeof(PVR_RECORDING)); + return recordingReader->Start(); +} - time_t startTime = xmltv::Utilities::XmltvToUnixTime(item->m_startTime); - time_t endTime = xmltv::Utilities::XmltvToUnixTime(item->m_endTime); - unsigned int id = item->m_id; +void CloseRecordedStream() +{ + if (recordingReader) + SAFE_DELETE(recordingReader); + recordingReader = nullptr; +} - recording.recordingTime = startTime; - recording.iDuration = static_cast(endTime - startTime); - recording.iEpgEventId = id; +int ReadRecordedStream(unsigned char* buffer, unsigned int size) +{ + if (!recordingReader) + return 0; - strncpy(recording.strChannelName, item->m_channelName.c_str(), - sizeof(recording.strChannelName)); + return recordingReader->ReadData(buffer, size); +} - strncpy(recording.strRecordingId, compat::to_string(id).c_str(), - sizeof(recording.strRecordingId)); +long long SeekRecordedStream(long long position, int whence) +{ + if (!recordingReader) + return 0; - strncpy(recording.strTitle, item->m_title.c_str(), - sizeof(recording.strTitle)); + return recordingReader->Seek(position, whence); +} - strncpy(recording.strPlot, item->m_description.c_str(), - sizeof(recording.strPlot)); +long long LengthRecordedStream() +{ + if (!recordingReader) + return -1; - /* TODO: PVR API 5.0.0: Implement this */ - recording.iChannelUid = PVR_CHANNEL_INVALID_UID; + return recordingReader->Length(); +} - /* TODO: PVR API 5.1.0: Implement this */ - recording.channelType = PVR_RECORDING_CHANNEL_TYPE_UNKNOWN; +PVR_ERROR GetTimerTypes(PVR_TIMER_TYPE types[], int *size) +{ + int numOfTimerTypes = 0; + + memset(&types[numOfTimerTypes], 0, sizeof(types[numOfTimerTypes])); + // EPG based single recording + types[numOfTimerTypes].iId = vbox::TIMER_VBOX_TYPE_EPG_BASED_SINGLE; + strcpy(types[numOfTimerTypes].strDescription, "EPG-based one time recording"); + types[numOfTimerTypes].iAttributes = + PVR_TIMER_TYPE_REQUIRES_EPG_TAG_ON_CREATE | + PVR_TIMER_TYPE_SUPPORTS_START_TIME | + PVR_TIMER_TYPE_SUPPORTS_END_TIME; + ++numOfTimerTypes; + // episode recording + memset(&types[numOfTimerTypes], 0, sizeof(types[numOfTimerTypes])); + types[numOfTimerTypes].iId = TIMER_VBOX_TYPE_EPISODE; + strcpy(types[numOfTimerTypes].strDescription, "Episode recording"); + types[numOfTimerTypes].iAttributes = + PVR_TIMER_TYPE_IS_READONLY | + PVR_TIMER_TYPE_SUPPORTS_START_TIME | + PVR_TIMER_TYPE_SUPPORTS_END_TIME; + ++numOfTimerTypes; + // manual recording + memset(&types[numOfTimerTypes], 0, sizeof(types[numOfTimerTypes])); + types[numOfTimerTypes].iId = TIMER_VBOX_TYPE_MANUAL_SINGLE; + strcpy(types[numOfTimerTypes].strDescription, "Manual one time recording"); + types[numOfTimerTypes].iAttributes = + PVR_TIMER_TYPE_IS_MANUAL | + PVR_TIMER_TYPE_FORBIDS_EPG_TAG_ON_CREATE | + PVR_TIMER_TYPE_SUPPORTS_CHANNELS | + PVR_TIMER_TYPE_SUPPORTS_START_TIME | + PVR_TIMER_TYPE_SUPPORTS_END_TIME; + ++numOfTimerTypes; + + //Automatic series recording + memset(&types[numOfTimerTypes], 0, sizeof(types[numOfTimerTypes])); + types[numOfTimerTypes].iId = TIMER_VBOX_TYPE_EPG_BASED_AUTO_SERIES; + strcpy(types[numOfTimerTypes].strDescription, "EPG-based automatic series recording"); +types[numOfTimerTypes].iAttributes = + PVR_TIMER_TYPE_REQUIRES_EPG_SERIES_ON_CREATE; + ++numOfTimerTypes; + + // EPG based series recording + memset(&types[numOfTimerTypes], 0, sizeof(types[numOfTimerTypes])); + types[numOfTimerTypes].iId = TIMER_VBOX_TYPE_EPG_BASED_MANUAL_SERIES; + strcpy(types[numOfTimerTypes].strDescription, "EPG-based manual series recording"); + types[numOfTimerTypes].iAttributes = + PVR_TIMER_TYPE_IS_REPEATING | + PVR_TIMER_TYPE_REQUIRES_EPG_TAG_ON_CREATE | + PVR_TIMER_TYPE_SUPPORTS_START_TIME | + PVR_TIMER_TYPE_SUPPORTS_END_TIME | + PVR_TIMER_TYPE_SUPPORTS_WEEKDAYS; + ++numOfTimerTypes; + + //Manual series recording + memset(&types[numOfTimerTypes], 0, sizeof(types[numOfTimerTypes])); + types[numOfTimerTypes].iId = TIMER_VBOX_TYPE_MANUAL_SERIES; + strcpy(types[numOfTimerTypes].strDescription, "Manual series recording"); + types[numOfTimerTypes].iAttributes = + PVR_TIMER_TYPE_IS_MANUAL | + PVR_TIMER_TYPE_IS_REPEATING | + PVR_TIMER_TYPE_FORBIDS_EPG_TAG_ON_CREATE | + PVR_TIMER_TYPE_SUPPORTS_CHANNELS | + PVR_TIMER_TYPE_SUPPORTS_START_TIME | + PVR_TIMER_TYPE_SUPPORTS_END_TIME | + PVR_TIMER_TYPE_SUPPORTS_WEEKDAYS; + ++numOfTimerTypes; - PVR->TransferRecordingEntry(handle, &recording); - } + *size = numOfTimerTypes; + return PVR_ERROR_NO_ERROR; +} - return PVR_ERROR_NO_ERROR; - } +int GetTimersAmount() +{ + return g_vbox->GetTimersAmount(); +} - PVR_ERROR DeleteRecording(const PVR_RECORDING &recording) - { - try { - unsigned int id = compat::stoui(recording.strRecordingId); +PVR_ERROR GetTimers(ADDON_HANDLE handle) +{ + /* TODO: Change implementation to get support for the timer features introduced with PVR API 1.9.7 */ + auto &recordings = g_vbox->GetRecordingsAndTimers(); + + // first get timers from single recordings (scheduled) + for (const auto &item : recordings) + { + // Skip recordings + if (!item->IsTimer()) + continue; + + PVR_TIMER timer; + memset(&timer, 0, sizeof(PVR_TIMER)); + timer.iTimerType = (item->m_seriesId > 0)? vbox::TIMER_VBOX_TYPE_EPISODE : vbox::TIMER_VBOX_TYPE_MANUAL_SINGLE; + timer.startTime = xmltv::Utilities::XmltvToUnixTime(item->m_startTime); + timer.endTime = xmltv::Utilities::XmltvToUnixTime(item->m_endTime); + timer.iClientIndex = item->m_id; - if (g_vbox->DeleteRecordingOrTimer(id)) - return PVR_ERROR_NO_ERROR; - else - return PVR_ERROR_FAILED; - } - catch (...) + // Convert the internal timer state to PVR_TIMER_STATE + switch (item->GetState()) { - return PVR_ERROR_INVALID_PARAMETERS; + case RecordingState::SCHEDULED: + timer.state = PVR_TIMER_STATE_SCHEDULED; + break; + case RecordingState::RECORDED: + case RecordingState::EXTERNAL: + timer.state = PVR_TIMER_STATE_COMPLETED; + break; + case RecordingState::RECORDING: + timer.state = PVR_TIMER_STATE_RECORDING; + break; + case RecordingState::RECORDING_ERROR: + break; } - } - PVR_ERROR GetRecordingStreamProperties( - const PVR_RECORDING* recording, PVR_NAMED_VALUE* properties, unsigned int* iPropertiesCount) - { - if (!recording || !properties || !iPropertiesCount) - return PVR_ERROR_SERVER_ERROR; - - if (*iPropertiesCount < 1) - return PVR_ERROR_INVALID_PARAMETERS; - - unsigned int id = compat::stoui(recording->strRecordingId); - auto &recordings = g_vbox->GetRecordingsAndTimers(); - auto recIt = std::find_if(recordings.begin(), recordings.end(), - [id](const RecordingPtr &item) + // Find the timer's channel and use its unique ID + auto &channels = g_vbox->GetChannels(); + auto it = std::find_if(channels.cbegin(), channels.cend(), + [&item](const ChannelPtr &channel) { - return item->IsRecording() && id == item->m_id; + return channel->m_xmltvName == item->m_channelId; }); - if (recIt == recordings.end()) - return PVR_ERROR_SERVER_ERROR; + if (it != channels.cend()) + timer.iClientChannelUid = ContentIdentifier::GetUniqueId(*it); + else + continue; - strncpy(properties[0].strName, PVR_STREAM_PROPERTY_STREAMURL, sizeof(properties[0].strName) - 1); - strncpy(properties[0].strValue, (*recIt)->m_url.c_str(), sizeof(properties[0].strValue) - 1); - *iPropertiesCount = 1; - return PVR_ERROR_NO_ERROR; - } + strncpy(timer.strTitle, item->m_title.c_str(), + sizeof(timer.strTitle)); - PVR_ERROR GetTimerTypes(PVR_TIMER_TYPE types[], int *size) - { - int numOfTimerTypes = 0; - - memset(&types[numOfTimerTypes], 0, sizeof(types[numOfTimerTypes])); - // EPG based single recording - types[numOfTimerTypes].iId = vbox::TIMER_VBOX_TYPE_EPG_BASED_SINGLE; - strcpy(types[numOfTimerTypes].strDescription, "EPG-based one time recording"); - types[numOfTimerTypes].iAttributes = - PVR_TIMER_TYPE_REQUIRES_EPG_TAG_ON_CREATE | - PVR_TIMER_TYPE_SUPPORTS_START_TIME | - PVR_TIMER_TYPE_SUPPORTS_END_TIME; - ++numOfTimerTypes; - // episode recording - memset(&types[numOfTimerTypes], 0, sizeof(types[numOfTimerTypes])); - types[numOfTimerTypes].iId = TIMER_VBOX_TYPE_EPISODE; - strcpy(types[numOfTimerTypes].strDescription, "Episode recording"); - types[numOfTimerTypes].iAttributes = - PVR_TIMER_TYPE_IS_READONLY | - PVR_TIMER_TYPE_SUPPORTS_START_TIME | - PVR_TIMER_TYPE_SUPPORTS_END_TIME; - ++numOfTimerTypes; - // manual recording - memset(&types[numOfTimerTypes], 0, sizeof(types[numOfTimerTypes])); - types[numOfTimerTypes].iId = TIMER_VBOX_TYPE_MANUAL_SINGLE; - strcpy(types[numOfTimerTypes].strDescription, "Manual one time recording"); - types[numOfTimerTypes].iAttributes = - PVR_TIMER_TYPE_IS_MANUAL | - PVR_TIMER_TYPE_FORBIDS_EPG_TAG_ON_CREATE | - PVR_TIMER_TYPE_SUPPORTS_CHANNELS | - PVR_TIMER_TYPE_SUPPORTS_START_TIME | - PVR_TIMER_TYPE_SUPPORTS_END_TIME; - ++numOfTimerTypes; - - //Automatic series recording - memset(&types[numOfTimerTypes], 0, sizeof(types[numOfTimerTypes])); - types[numOfTimerTypes].iId = TIMER_VBOX_TYPE_EPG_BASED_AUTO_SERIES; - strcpy(types[numOfTimerTypes].strDescription, "EPG-based automatic series recording"); - types[numOfTimerTypes].iAttributes = - PVR_TIMER_TYPE_REQUIRES_EPG_SERIES_ON_CREATE; - ++numOfTimerTypes; - - // EPG based series recording - memset(&types[numOfTimerTypes], 0, sizeof(types[numOfTimerTypes])); - types[numOfTimerTypes].iId = TIMER_VBOX_TYPE_EPG_BASED_MANUAL_SERIES; - strcpy(types[numOfTimerTypes].strDescription, "EPG-based manual series recording"); - types[numOfTimerTypes].iAttributes = - PVR_TIMER_TYPE_IS_REPEATING | - PVR_TIMER_TYPE_REQUIRES_EPG_TAG_ON_CREATE | - PVR_TIMER_TYPE_SUPPORTS_START_TIME | - PVR_TIMER_TYPE_SUPPORTS_END_TIME | - PVR_TIMER_TYPE_SUPPORTS_WEEKDAYS; - ++numOfTimerTypes; - - //Manual series recording - memset(&types[numOfTimerTypes], 0, sizeof(types[numOfTimerTypes])); - types[numOfTimerTypes].iId = TIMER_VBOX_TYPE_MANUAL_SERIES; - strcpy(types[numOfTimerTypes].strDescription, "Manual series recording"); - types[numOfTimerTypes].iAttributes = - PVR_TIMER_TYPE_IS_MANUAL | - PVR_TIMER_TYPE_IS_REPEATING | - PVR_TIMER_TYPE_FORBIDS_EPG_TAG_ON_CREATE | - PVR_TIMER_TYPE_SUPPORTS_CHANNELS | - PVR_TIMER_TYPE_SUPPORTS_START_TIME | - PVR_TIMER_TYPE_SUPPORTS_END_TIME | - PVR_TIMER_TYPE_SUPPORTS_WEEKDAYS; - ++numOfTimerTypes; + strncpy(timer.strSummary, item->m_description.c_str(), + sizeof(timer.strSummary)); - *size = numOfTimerTypes; - return PVR_ERROR_NO_ERROR; + g_vbox->Log(LOG_DEBUG, "GetTimers(): adding timer to show %s", item->m_title.c_str()); + // TODO: Set margins to whatever the API reports + PVR->TransferTimerEntry(handle, &timer); } - - int GetTimersAmount(void) + // second: get timer rules for series + auto &series = g_vbox->GetSeriesTimers(); + for (const auto &item : series) { - return g_vbox->GetTimersAmount(); - } + PVR_TIMER timer; + memset(&timer, 0, sizeof(PVR_TIMER)); - PVR_ERROR GetTimers(ADDON_HANDLE handle) - { - /* TODO: Change implementation to get support for the timer features introduced with PVR API 1.9.7 */ - auto &recordings = g_vbox->GetRecordingsAndTimers(); + timer.iTimerType = (item->m_fIsAuto)? vbox::TIMER_VBOX_TYPE_EPG_BASED_AUTO_SERIES : vbox::TIMER_VBOX_TYPE_MANUAL_SERIES; + timer.iClientIndex = item->m_id; + timer.state = PVR_TIMER_STATE_SCHEDULED; - // first get timers from single recordings (scheduled) - for (const auto &item : recordings) + // Find the timer's channel and use its unique ID + auto &channels = g_vbox->GetChannels(); + auto it = std::find_if(channels.cbegin(), channels.cend(), + [&item](const ChannelPtr &channel) { - // Skip recordings - if (!item->IsTimer()) - continue; - - PVR_TIMER timer; - memset(&timer, 0, sizeof(PVR_TIMER)); - timer.iTimerType = (item->m_seriesId > 0)? vbox::TIMER_VBOX_TYPE_EPISODE : vbox::TIMER_VBOX_TYPE_MANUAL_SINGLE; - timer.startTime = xmltv::Utilities::XmltvToUnixTime(item->m_startTime); - timer.endTime = xmltv::Utilities::XmltvToUnixTime(item->m_endTime); - timer.iClientIndex = item->m_id; - - // Convert the internal timer state to PVR_TIMER_STATE - switch (item->GetState()) - { - case RecordingState::SCHEDULED: - timer.state = PVR_TIMER_STATE_SCHEDULED; - break; - case RecordingState::RECORDED: - case RecordingState::EXTERNAL: - timer.state = PVR_TIMER_STATE_COMPLETED; - break; - case RecordingState::RECORDING: - timer.state = PVR_TIMER_STATE_RECORDING; - break; - } - - // Find the timer's channel and use its unique ID - auto &channels = g_vbox->GetChannels(); - auto it = std::find_if(channels.cbegin(), channels.cend(), - [&item](const ChannelPtr &channel) - { - return channel->m_xmltvName == item->m_channelId; - }); - - if (it != channels.cend()) - timer.iClientChannelUid = ContentIdentifier::GetUniqueId(*it); - else - continue; + return channel->m_xmltvName == item->m_channelId; + }); - strncpy(timer.strTitle, item->m_title.c_str(), - sizeof(timer.strTitle)); + if (it != channels.cend()) + timer.iClientChannelUid = ContentIdentifier::GetUniqueId(*it); - strncpy(timer.strSummary, item->m_description.c_str(), - sizeof(timer.strSummary)); + unsigned int nextScheduledId = item->m_scheduledId; + // Find next recording of the series + auto recIt = std::find_if(recordings.begin(), recordings.end(), + [nextScheduledId](const RecordingPtr &recording) + { + return nextScheduledId == recording->m_id; + }); + // if it doesn't exist (canceled) - don't add series + if (recIt == recordings.end()) + continue; - g_vbox->Log(LOG_DEBUG, "GetTimers(): adding timer to show %s", item->m_title.c_str()); - // TODO: Set margins to whatever the API reports - PVR->TransferTimerEntry(handle, &timer); + timer.startTime = xmltv::Utilities::XmltvToUnixTime(item->m_startTime); + // automatic starts & stops whenever detected (will appear as episode) + if (item->m_fIsAuto) + { + timer.bStartAnyTime = true; + timer.bEndAnyTime = true; } - // second: get timer rules for series - auto &series = g_vbox->GetSeriesTimers(); - for (const auto &item : series) + else { - PVR_TIMER timer; - memset(&timer, 0, sizeof(PVR_TIMER)); - - timer.iTimerType = (item->m_fIsAuto)? vbox::TIMER_VBOX_TYPE_EPG_BASED_AUTO_SERIES : vbox::TIMER_VBOX_TYPE_MANUAL_SERIES; - timer.iClientIndex = item->m_id; - timer.state = PVR_TIMER_STATE_SCHEDULED; - - // Find the timer's channel and use its unique ID - auto &channels = g_vbox->GetChannels(); - auto it = std::find_if(channels.cbegin(), channels.cend(), - [&item](const ChannelPtr &channel) - { - return channel->m_xmltvName == item->m_channelId; - }); - - if (it != channels.cend()) - timer.iClientChannelUid = ContentIdentifier::GetUniqueId(*it); - - unsigned int nextScheduledId = item->m_scheduledId; - // Find next recording of the series - auto recIt = std::find_if(recordings.begin(), recordings.end(), - [nextScheduledId](const RecordingPtr &recording) - { - return nextScheduledId == recording->m_id; - }); - // if it doesn't exist (canceled) - don't add series - if (recIt == recordings.end()) - continue; - - timer.startTime = xmltv::Utilities::XmltvToUnixTime(item->m_startTime); - // automatic starts & stops whenever detected (will appear as episode) - if (item->m_fIsAuto) - { - timer.bStartAnyTime = true; - timer.bEndAnyTime = true; - } - else - { - // set periodic times - timer.firstDay = xmltv::Utilities::XmltvToUnixTime(item->m_startTime); - timer.iWeekdays = item->m_weekdays; - timer.endTime = xmltv::Utilities::XmltvToUnixTime(item->m_endTime); - } - strncpy(timer.strTitle, item->m_title.c_str(), - sizeof(timer.strTitle)); - - strncpy(timer.strSummary, item->m_description.c_str(), - sizeof(timer.strSummary)); - - // TODO: Set margins to whatever the API reports - PVR->TransferTimerEntry(handle, &timer); + // set periodic times + timer.firstDay = xmltv::Utilities::XmltvToUnixTime(item->m_startTime); + timer.iWeekdays = item->m_weekdays; + timer.endTime = xmltv::Utilities::XmltvToUnixTime(item->m_endTime); } - return PVR_ERROR_NO_ERROR; - } + strncpy(timer.strTitle, item->m_title.c_str(), + sizeof(timer.strTitle)); - PVR_ERROR AddTimer(const PVR_TIMER &timer) - { - VBox::Log(LOG_DEBUG, "AddTimer() : entering with timer type 0x%x", timer.iTimerType); - // Find the channel the timer is for - auto &channels = g_vbox->GetChannels(); - auto it = std::find_if(channels.cbegin(), channels.cend(), - [&timer](const ChannelPtr &channel) - { - return timer.iClientChannelUid == ContentIdentifier::GetUniqueId(channel); - }); + strncpy(timer.strSummary, item->m_description.c_str(), + sizeof(timer.strSummary)); - if (it == channels.end()) - return PVR_ERROR_INVALID_PARAMETERS; + // TODO: Set margins to whatever the API reports + PVR->TransferTimerEntry(handle, &timer); + } + return PVR_ERROR_NO_ERROR; +} - const ChannelPtr channel = *it; +PVR_ERROR AddTimer(const PVR_TIMER &timer) +{ + VBox::Log(LOG_DEBUG, "AddTimer() : entering with timer type 0x%x", timer.iTimerType); + // Find the channel the timer is for + auto &channels = g_vbox->GetChannels(); + auto it = std::find_if(channels.cbegin(), channels.cend(), + [&timer](const ChannelPtr &channel) + { + return timer.iClientChannelUid == ContentIdentifier::GetUniqueId(channel); + }); + + if (it == channels.end()) + return PVR_ERROR_INVALID_PARAMETERS; + + const ChannelPtr channel = *it; + + // Find the channel's schedule + const Schedule schedule = g_vbox->GetSchedule(channel); + + try { + // update the recording margins in the backend + g_vbox->UpdateRecordingMargins( {timer.iMarginStart, timer.iMarginEnd} ); + // Set start time to now if it's missing + time_t startTime = timer.startTime; + time_t endTime = timer.endTime; + std::string title(timer.strTitle); + std::string desc(timer.strSummary); + + if (startTime == 0) + startTime = time(nullptr); - // Find the channel's schedule - const Schedule schedule = g_vbox->GetSchedule(channel); + // Add a programme-based timer if the programme exists in the schedule + const xmltv::ProgrammePtr programme = (schedule.schedule)? schedule.schedule->GetProgramme(timer.iEpgUid) : nullptr; - try { - // update the recording margins in the backend - g_vbox->UpdateRecordingMargins( {timer.iMarginStart, timer.iMarginEnd} ); - // Set start time to now if it's missing - time_t startTime = timer.startTime; - time_t endTime = timer.endTime; - std::string title(timer.strTitle); - std::string desc(timer.strSummary); - - if (startTime == 0) - startTime = time(nullptr); - - // Add a programme-based timer if the programme exists in the schedule - const xmltv::ProgrammePtr programme = (schedule.schedule)? schedule.schedule->GetProgramme(timer.iEpgUid) : nullptr; - - switch (timer.iTimerType) + switch (timer.iTimerType) + { + case TIMER_VBOX_TYPE_EPG_BASED_SINGLE: + case TIMER_VBOX_TYPE_EPISODE: + if (programme) { - case TIMER_VBOX_TYPE_EPG_BASED_SINGLE: - case TIMER_VBOX_TYPE_EPISODE: - if (programme) - { - switch (schedule.origin) - { - case Schedule::Origin::INTERNAL_GUIDE: - g_vbox->AddTimer(channel, programme); - break; - case Schedule::Origin::EXTERNAL_GUIDE: - title = programme->m_title; - desc = programme->m_description; - g_vbox->AddTimer(channel, startTime, endTime, title, desc); - break; - } - return PVR_ERROR_NO_ERROR; - } - else + switch (schedule.origin) { + case Schedule::Origin::INTERNAL_GUIDE: + g_vbox->AddTimer(channel, programme); + break; + case Schedule::Origin::EXTERNAL_GUIDE: + title = programme->m_title; + desc = programme->m_description; g_vbox->AddTimer(channel, startTime, endTime, title, desc); - return PVR_ERROR_NO_ERROR; + break; } - case TIMER_VBOX_TYPE_MANUAL_SINGLE: - g_vbox->AddTimer(channel, startTime, endTime, title, desc); - return PVR_ERROR_NO_ERROR; - case TIMER_VBOX_TYPE_EPG_BASED_AUTO_SERIES: - { - if (!programme) - return PVR_ERROR_INVALID_PARAMETERS; - g_vbox->AddSeriesTimer(channel, programme); - return PVR_ERROR_NO_ERROR; - } - case TIMER_VBOX_TYPE_EPG_BASED_MANUAL_SERIES: - { - if (!programme) - return PVR_ERROR_INVALID_PARAMETERS; - g_vbox->AddTimer(channel, startTime, endTime, title, desc, timer.iWeekdays); return PVR_ERROR_NO_ERROR; } - case TIMER_VBOX_TYPE_MANUAL_SERIES: + else { - g_vbox->AddTimer(channel, startTime, endTime, title, desc, timer.iWeekdays); + g_vbox->AddTimer(channel, startTime, endTime, title, desc); return PVR_ERROR_NO_ERROR; } - default: - // any other timer type is wrong + case TIMER_VBOX_TYPE_MANUAL_SINGLE: + g_vbox->AddTimer(channel, startTime, endTime, title, desc); + return PVR_ERROR_NO_ERROR; + case TIMER_VBOX_TYPE_EPG_BASED_AUTO_SERIES: + { + if (!programme) return PVR_ERROR_INVALID_PARAMETERS; - } + g_vbox->AddSeriesTimer(channel, programme); + return PVR_ERROR_NO_ERROR; } - catch (VBoxException &e) + case TIMER_VBOX_TYPE_EPG_BASED_MANUAL_SERIES: { - g_vbox->LogException(e); - return PVR_ERROR_FAILED; + if (!programme) + return PVR_ERROR_INVALID_PARAMETERS; + g_vbox->AddTimer(channel, startTime, endTime, title, desc, timer.iWeekdays); + return PVR_ERROR_NO_ERROR; + } + case TIMER_VBOX_TYPE_MANUAL_SERIES: + { + g_vbox->AddTimer(channel, startTime, endTime, title, desc, timer.iWeekdays); + return PVR_ERROR_NO_ERROR; + } + default: + // any other timer type is wrong + return PVR_ERROR_INVALID_PARAMETERS; } } - - PVR_ERROR DeleteTimer(const PVR_TIMER &timer, bool bForceDelete) + catch (VBoxException &e) { - if (g_vbox->DeleteRecordingOrTimer(timer.iClientIndex)) - return PVR_ERROR_NO_ERROR; - + g_vbox->LogException(e); return PVR_ERROR_FAILED; } +} - PVR_ERROR UpdateTimer(const PVR_TIMER &timer) - { - PVR_ERROR err = DeleteTimer(timer, true); +PVR_ERROR DeleteTimer(const PVR_TIMER &timer, bool bForceDelete) +{ + if (g_vbox->DeleteRecordingOrTimer(timer.iClientIndex)) + return PVR_ERROR_NO_ERROR; - if (err == PVR_ERROR_NO_ERROR) - return AddTimer(timer); - return err; - } + return PVR_ERROR_FAILED; +} - PVR_ERROR GetEPGForChannel(ADDON_HANDLE handle, const PVR_CHANNEL &channel, time_t iStart, time_t iEnd) - { - const ChannelPtr channelPtr = g_vbox->GetChannel(channel.iUniqueId); +PVR_ERROR UpdateTimer(const PVR_TIMER &timer) +{ + PVR_ERROR err = DeleteTimer(timer, true); + + if (err == PVR_ERROR_NO_ERROR) + return AddTimer(timer); + return err; +} - if (!channelPtr) - return PVR_ERROR_INVALID_PARAMETERS; +PVR_ERROR GetEPGForChannel(ADDON_HANDLE handle, const PVR_CHANNEL &channel, time_t iStart, time_t iEnd) +{ + const ChannelPtr channelPtr = g_vbox->GetChannel(channel.iUniqueId); - // Retrieve the schedule - const auto schedule = g_vbox->GetSchedule(channelPtr); + if (!channelPtr) + return PVR_ERROR_INVALID_PARAMETERS; - if (!schedule.schedule) - return PVR_ERROR_NO_ERROR; - - // Transfer the programmes between the start and end times - for (const auto &programme : schedule.schedule->GetSegment(iStart, iEnd)) - { - EPG_TAG event; - memset(&event, 0, sizeof(EPG_TAG)); - - event.startTime = xmltv::Utilities::XmltvToUnixTime(programme->m_startTime); - event.endTime = xmltv::Utilities::XmltvToUnixTime(programme->m_endTime); - event.iUniqueChannelId = channel.iUniqueId; - event.iUniqueBroadcastId = ContentIdentifier::GetUniqueId(programme.get()); - event.strTitle = programme->m_title.c_str(); - event.strPlot = programme->m_description.c_str(); - event.iYear = programme->m_year; - event.strEpisodeName = programme->m_subTitle.c_str(); - event.strIconPath = programme->m_icon.c_str(); - - std::string directors = xmltv::Utilities::ConcatenateStringList(programme->GetDirectors()); - std::string writers = xmltv::Utilities::ConcatenateStringList(programme->GetWriters()); - std::vector categories = programme->GetCategories(); - std::string catStrings = xmltv::Utilities::ConcatenateStringList(categories); - - event.strDirector = directors.c_str(); - event.strWriter = writers.c_str(); - - // use genre mapper to find the most common genre type (from categories) - event.iGenreType = g_vbox->GetCategoriesGenreType(categories); - event.strGenreDescription = catStrings.c_str(); - - // Extract up to five cast members only - std::vector actorNames; - const auto &actors = programme->GetActors(); - int numActors = std::min(static_cast(actors.size()), 5); - - for (unsigned int i = 0; i < numActors; i++) - actorNames.push_back(actors.at(i).name); - - std::string cast = xmltv::Utilities::ConcatenateStringList(actorNames); - event.strCast = cast.c_str(); - - event.iFlags = EPG_TAG_FLAG_UNDEFINED; - - if (!programme->m_seriesIds.empty()) - { - VBox::Log(LOG_DEBUG, "GetEPGForChannel():programme %s marked as belonging to a series", programme->m_title.c_str()); - event.iFlags |= EPG_TAG_FLAG_IS_SERIES; - } - PVR->TransferEpgEntry(handle, &event); - } + // Retrieve the schedule + const auto schedule = g_vbox->GetSchedule(channelPtr); + if (!schedule.schedule) return PVR_ERROR_NO_ERROR; - } - PVR_ERROR SetEPGTimeFrame(int) + // Transfer the programmes between the start and end times + for (const auto &programme : schedule.schedule->GetSegment(iStart, iEnd)) { - return PVR_ERROR_NOT_IMPLEMENTED; - } + EPG_TAG event; + memset(&event, 0, sizeof(EPG_TAG)); - PVR_ERROR SignalStatus(PVR_SIGNAL_STATUS &signalStatus) - { - const ChannelPtr currentChannel = g_vbox->GetCurrentChannel(); + event.startTime = xmltv::Utilities::XmltvToUnixTime(programme->m_startTime); + event.endTime = xmltv::Utilities::XmltvToUnixTime(programme->m_endTime); + event.iUniqueChannelId = channel.iUniqueId; + event.iUniqueBroadcastId = ContentIdentifier::GetUniqueId(programme.get()); + event.strTitle = programme->m_title.c_str(); + event.strPlot = programme->m_description.c_str(); + event.iYear = programme->m_year; + event.strEpisodeName = programme->m_subTitle.c_str(); + event.strIconPath = programme->m_icon.c_str(); - if (!currentChannel) - return PVR_ERROR_NO_ERROR; + std::string directors = xmltv::Utilities::ConcatenateStringList(programme->GetDirectors()); + std::string writers = xmltv::Utilities::ConcatenateStringList(programme->GetWriters()); + std::vector categories = programme->GetCategories(); + std::string catStrings = xmltv::Utilities::ConcatenateStringList(categories); - try { - ChannelStreamingStatus status = g_vbox->GetChannelStreamingStatus(currentChannel); + event.strDirector = directors.c_str(); + event.strWriter = writers.c_str(); - // Adjust for Kodi's weird handling of the signal strength - signalStatus.iSignal = static_cast(status.GetSignalStrength()) * 655; - signalStatus.iSNR = static_cast(status.m_signalQuality) * 655; - signalStatus.iBER = status.GetBer(); - - strncpy(signalStatus.strAdapterName, - status.GetTunerName().c_str(), sizeof(signalStatus.strAdapterName)); - - strncpy(signalStatus.strAdapterStatus, - status.m_lockStatus.c_str(), sizeof(signalStatus.strAdapterStatus)); - - strncpy(signalStatus.strServiceName, - status.GetServiceName().c_str(), sizeof(signalStatus.strServiceName)); + // use genre mapper to find the most common genre type (from categories) + event.iGenreType = g_vbox->GetCategoriesGenreType(categories); + event.strGenreDescription = catStrings.c_str(); - strncpy(signalStatus.strMuxName, - status.GetMuxName().c_str(), sizeof(signalStatus.strMuxName)); - } - catch (VBoxException &e) + // Extract up to five cast members only + std::vector actorNames; + const auto &actors = programme->GetActors(); + int numActors = std::min(static_cast(actors.size()), 5); + + for (unsigned int i = 0; i < numActors; i++) + actorNames.push_back(actors.at(i).name); + + std::string cast = xmltv::Utilities::ConcatenateStringList(actorNames); + event.strCast = cast.c_str(); + + event.iFlags = EPG_TAG_FLAG_UNDEFINED; + + if (!programme->m_seriesIds.empty()) { - g_vbox->LogException(e); + VBox::Log(LOG_DEBUG, "GetEPGForChannel():programme %s marked as belonging to a series", programme->m_title.c_str()); + event.iFlags |= EPG_TAG_FLAG_IS_SERIES; } + PVR->TransferEpgEntry(handle, &event); + } + + return PVR_ERROR_NO_ERROR; +} + +PVR_ERROR SetEPGTimeFrame(int) +{ + return PVR_ERROR_NOT_IMPLEMENTED; +} + +PVR_ERROR SignalStatus(PVR_SIGNAL_STATUS &signalStatus) +{ + const ChannelPtr currentChannel = g_vbox->GetCurrentChannel(); + if (!currentChannel) return PVR_ERROR_NO_ERROR; - } - PVR_ERROR GetDescrambleInfo(PVR_DESCRAMBLE_INFO*) + try { + ChannelStreamingStatus status = g_vbox->GetChannelStreamingStatus(currentChannel); + + // Adjust for Kodi's weird handling of the signal strength + signalStatus.iSignal = static_cast(status.GetSignalStrength()) * 655; + signalStatus.iSNR = static_cast(status.m_signalQuality) * 655; + signalStatus.iBER = status.GetBer(); + + strncpy(signalStatus.strAdapterName, + status.GetTunerName().c_str(), sizeof(signalStatus.strAdapterName)); + + strncpy(signalStatus.strAdapterStatus, + status.m_lockStatus.c_str(), sizeof(signalStatus.strAdapterStatus)); + + strncpy(signalStatus.strServiceName, + status.GetServiceName().c_str(), sizeof(signalStatus.strServiceName)); + + strncpy(signalStatus.strMuxName, + status.GetMuxName().c_str(), sizeof(signalStatus.strMuxName)); + } + catch (VBoxException &e) { - return PVR_ERROR_NOT_IMPLEMENTED; + g_vbox->LogException(e); } - bool OpenLiveStream(const PVR_CHANNEL &channel) - { - // Find the channel - const ChannelPtr channelPtr = g_vbox->GetChannel(channel.iUniqueId); - - if (!channelPtr) - return false; + return PVR_ERROR_NO_ERROR; +} - // Remember the current channel if the buffer was successfully opened - if (g_timeshiftBuffer->Open(channelPtr->m_url)) - { - g_vbox->SetCurrentChannel(channelPtr); - return true; - } +PVR_ERROR GetDescrambleInfo(PVR_DESCRAMBLE_INFO*) +{ + return PVR_ERROR_NOT_IMPLEMENTED; +} - CloseLiveStream(); - g_vbox->SetChannelStreamingStatus(channelPtr); +bool OpenLiveStream(const PVR_CHANNEL &channel) +{ + // Find the channel + const ChannelPtr channelPtr = g_vbox->GetChannel(channel.iUniqueId); + + if (!channelPtr) return false; - } - void CloseLiveStream(void) + // Remember the current channel if the buffer was successfully opened + if (g_timeshiftBuffer->Open(channelPtr->m_url)) { - g_timeshiftBuffer->Close(); + g_vbox->SetCurrentChannel(channelPtr); + return true; } - int ReadLiveStream(unsigned char *pBuffer, unsigned int iBufferSize) - { - return g_timeshiftBuffer->Read(pBuffer, iBufferSize); - } + CloseLiveStream(); + g_vbox->SetChannelStreamingStatus(channelPtr); + return false; +} - long long LengthLiveStream(void) - { - return g_timeshiftBuffer->Length(); - } +void CloseLiveStream() +{ + g_timeshiftBuffer->Close(); + g_vbox->SetCurrentChannel(nullptr); +} - long long SeekLiveStream(long long iPosition, int iWhence /* = SEEK_SET */) - { - return g_timeshiftBuffer->Seek(iPosition, iWhence); - } +int ReadLiveStream(unsigned char *pBuffer, unsigned int iBufferSize) +{ + return g_timeshiftBuffer->Read(pBuffer, iBufferSize); +} - bool CanPauseStream(void) - { - return g_vbox->GetSettings().m_timeshiftEnabled; - } +long long LengthLiveStream() +{ + return g_timeshiftBuffer->Length(); +} - bool CanSeekStream(void) - { - return g_vbox->GetSettings().m_timeshiftEnabled; - } +long long SeekLiveStream(long long iPosition, int iWhence /* = SEEK_SET */) +{ + return g_timeshiftBuffer->Seek(iPosition, iWhence); +} - bool IsRealTimeStream(void) +bool CanPauseStream() +{ + return g_vbox->GetSettings().m_timeshiftEnabled; +} + +bool CanSeekStream() +{ + return g_vbox->GetSettings().m_timeshiftEnabled; +} + +bool IsRealTimeStream() +{ + const ChannelPtr currentChannel = g_vbox->GetCurrentChannel(); + + return currentChannel != nullptr; +} + +// Stream times +PVR_ERROR GetStreamTimes(PVR_STREAM_TIMES* times) +{ + // Addon API 5.8.0 + if (!times) + return PVR_ERROR_INVALID_PARAMETERS; + + if (IsRealTimeStream() && g_timeshiftBuffer && g_vbox->GetSettings().m_timeshiftEnabled) + { + times->startTime = g_timeshiftBuffer->GetStartTime(); + times->ptsStart = 0; + times->ptsBegin = 0; + times->ptsEnd = (!g_timeshiftBuffer->CanSeekStream()) ? 0 + : (g_timeshiftBuffer->GetEndTime() - g_timeshiftBuffer->GetStartTime()) * DVD_TIME_BASE; + + return PVR_ERROR_NO_ERROR; + } + else if (recordingReader) { - const ChannelPtr currentChannel = g_vbox->GetCurrentChannel(); + times->startTime = 0; + times->ptsStart = 0; + times->ptsBegin = 0; + times->ptsEnd = static_cast(recordingReader->CurrentDuration()) * DVD_TIME_BASE; - return currentChannel != nullptr; + return PVR_ERROR_NO_ERROR; } - bool SetProgramReminder(unsigned int epgUid) - { - ChannelPtr selectedChannel = nullptr; + return PVR_ERROR_NOT_IMPLEMENTED; +} - // find channel with the program in context - auto &channels = g_vbox->GetChannels(); - auto it = std::find_if(channels.cbegin(), channels.cend(), - [&epgUid](const ChannelPtr &channel) - { - const Schedule schedule = g_vbox->GetSchedule(channel); - const xmltv::ProgrammePtr programme = (schedule.schedule) ? schedule.schedule->GetProgramme(epgUid) : nullptr; - return (programme != nullptr); - }); - // if channel doesn't exist - return error - if (it == channels.cend()) - { - XBMC->QueueNotification(QUEUE_ERROR, "Program not found for that channel"); - return false; - } - else +void PauseStream(bool bPaused) +{ + // TODO: Setting for timeshift on pause as well as always +} + +bool SetProgramReminder(unsigned int epgUid) +{ + ChannelPtr selectedChannel = nullptr; + + // find channel with the program in context + auto &channels = g_vbox->GetChannels(); + auto it = std::find_if(channels.cbegin(), channels.cend(), + [&epgUid](const ChannelPtr &channel) + { + const Schedule schedule = g_vbox->GetSchedule(channel); + const xmltv::ProgrammePtr programme = (schedule.schedule) ? schedule.schedule->GetProgramme(epgUid) : nullptr; + return (programme != nullptr); + }); + // if channel doesn't exist - return error + if (it == channels.cend()) + { + XBMC->QueueNotification(QUEUE_ERROR, "Program not found for that channel"); + return false; + } + else + { + // otherwise - get the program object and add a reminder with it as parameter + selectedChannel = *it; + const Schedule schedule = g_vbox->GetSchedule(selectedChannel); + const xmltv::ProgrammePtr programme = (schedule.schedule) ? schedule.schedule->GetProgramme(epgUid) : nullptr; + if (programme) { - // otherwise - get the program object and add a reminder with it as parameter - selectedChannel = *it; - const Schedule schedule = g_vbox->GetSchedule(selectedChannel); - const xmltv::ProgrammePtr programme = (schedule.schedule) ? schedule.schedule->GetProgramme(epgUid) : nullptr; - if (programme) + try { + g_vbox->AddReminder(selectedChannel, programme); + } + catch (VBoxException &e) { - try { - g_vbox->AddReminder(selectedChannel, programme); - } - catch (VBoxException &e) - { - g_vbox->LogException(e); - return false; - } - XBMC->QueueNotification(QUEUE_INFO, "Reminder added"); + g_vbox->LogException(e); + return false; } + XBMC->QueueNotification(QUEUE_INFO, "Reminder added"); } - return true; } + return true; +} - const ChannelPtr FindChannelForEPGReminder(int epgUid) - { - const xmltv::ProgrammePtr programme = nullptr; - const std::vector &channels = g_vbox->GetChannels(); - - // Find channel that contains this programme - const std::vector::const_iterator it = std::find_if(channels.cbegin(), channels.cend(), - [&epgUid](const ChannelPtr &channel) - { - const Schedule schedule = g_vbox->GetSchedule(channel); - const xmltv::ProgrammePtr programme = (schedule.schedule) ? schedule.schedule->GetProgramme(epgUid) : nullptr; - return (programme); - }); - // Find the channel's schedule - if (it == channels.cend()) - XBMC->QueueNotification(QUEUE_WARNING, "Reminder could not find the requested channel"); - return *it; - } +static time_t GetOffsetTime(time_t time) +{ + std::string xmltvTime = g_vbox->CreateTimestamp(time); + std::string tzString = ::xmltv::Utilities::GetTimezoneOffset(xmltvTime); + return ::xmltv::Utilities::GetTimezoneAdjustment(tzString); +} - static time_t GetOffsetTime(time_t time) - { - std::string xmltvTime = g_vbox->CreateTimestamp(time); - std::string tzString = ::xmltv::Utilities::GetTimezoneOffset(xmltvTime); - return ::xmltv::Utilities::GetTimezoneAdjustment(tzString); - } +static bool SetManualReminder(const PVR_MENUHOOK_DATA &item) +{ + time_t currTime = time(nullptr), reminderTime; + ChannelPtr selectedChannel = nullptr; + char buffer[256]; + + memset(buffer, 0, sizeof(buffer)); + // get channel in context + selectedChannel = g_vbox->GetChannel(item.data.channel.iUniqueId); + if (!selectedChannel) + return false; - static bool SetManualReminder(const PVR_MENUHOOK_DATA &item) - { - time_t currTime = time(nullptr), reminderTime; - ChannelPtr selectedChannel = nullptr; - char buffer[256]; + try { + // create the current time's formatted timestamp (for user input) + time_t tzOs = GetOffsetTime(currTime); + // add timezone offset + currTime += tzOs; + std::tm tm = *std::gmtime(&currTime); - memset(buffer, 0, sizeof(buffer)); - // get channel in context - selectedChannel = g_vbox->GetChannel(item.data.channel.iUniqueId); - if (!selectedChannel) + // get program time & name (from user dialogs) + if (!GUI->Dialog_Numeric_ShowAndGetDate(tm, "Program starts at")) + return false; + if (!GUI->Dialog_Numeric_ShowAndGetTime(tm, "Program starts at")) + return false; + if (!GUI->Dialog_Keyboard_ShowAndGetInput(*buffer, sizeof(buffer), "Program title", true, false)) return false; - try { - // create the current time's formatted timestamp (for user input) - time_t tzOs = GetOffsetTime(currTime); - // add timezone offset - currTime += tzOs; - std::tm tm = *std::gmtime(&currTime); - - // get program time & name (from user dialogs) - if (!GUI->Dialog_Numeric_ShowAndGetDate(tm, "Program starts at")) - return false; - if (!GUI->Dialog_Numeric_ShowAndGetTime(tm, "Program starts at")) - return false; - if (!GUI->Dialog_Keyboard_ShowAndGetInput(*buffer, sizeof(buffer), "Program title", true, false)) - return false; + std::string progTitle(buffer); + // remove timezone offset + reminderTime = compat::timegm(&tm) - tzOs; + // add reminder using manual time & title + g_vbox->AddReminder(selectedChannel, reminderTime, progTitle); + } + catch (VBoxException &e) + { + g_vbox->LogException(e); + return false; + } + XBMC->QueueNotification(QUEUE_INFO, "Reminder added"); + return true; +} - std::string progTitle(buffer); - // remove timezone offset - reminderTime = compat::timegm(&tm) - tzOs; - // add reminder using manual time & title - g_vbox->AddReminder(selectedChannel, reminderTime, progTitle); +PVR_ERROR CallMenuHook(const PVR_MENUHOOK &menuhook, const PVR_MENUHOOK_DATA &item) +{ + if (menuhook.category == PVR_MENUHOOK_SETTING) + { + if (menuhook.iHookId == MENUHOOK_ID_RESCAN_EPG) + { + XBMC->QueueNotification(ADDON::QUEUE_INFO, "Rescanning EPG, this will take a while"); + g_vbox->StartEPGScan(); + return PVR_ERROR_NO_ERROR; } - catch (VBoxException &e) + else if (menuhook.iHookId == MENUHOOK_ID_SYNC_EPG) { - g_vbox->LogException(e); - return false; + XBMC->QueueNotification(ADDON::QUEUE_INFO, "Getting EPG from VBox device"); + g_vbox->SyncEPGNow(); + return PVR_ERROR_NO_ERROR; } - XBMC->QueueNotification(QUEUE_INFO, "Reminder added"); - return true; + return PVR_ERROR_INVALID_PARAMETERS; } - - PVR_ERROR CallMenuHook(const PVR_MENUHOOK &menuhook, const PVR_MENUHOOK_DATA &item) + if (menuhook.category == PVR_MENUHOOK_EPG) { - if (menuhook.category == PVR_MENUHOOK_SETTING) + if (menuhook.iHookId == MENUHOOK_ID_EPG_REMINDER) { - if (menuhook.iHookId == MENUHOOK_ID_RESCAN_EPG) - { - XBMC->QueueNotification(ADDON::QUEUE_INFO, "Rescanning EPG, this will take a while"); - g_vbox->StartEPGScan(); + if (SetProgramReminder(item.data.iEpgUid)) return PVR_ERROR_NO_ERROR; - } - else if (menuhook.iHookId == MENUHOOK_ID_SYNC_EPG) - { - XBMC->QueueNotification(ADDON::QUEUE_INFO, "Getting EPG from VBox device"); - g_vbox->SyncEPGNow(); - return PVR_ERROR_NO_ERROR; - } - return PVR_ERROR_INVALID_PARAMETERS; } - if (menuhook.category == PVR_MENUHOOK_EPG) + else if (menuhook.iHookId == MENUHOOK_ID_CANCEL_EPG_REMINDER) { - if (menuhook.iHookId == MENUHOOK_ID_EPG_REMINDER) - { - if (SetProgramReminder(item.data.iEpgUid)) - return PVR_ERROR_NO_ERROR; - } - else if (menuhook.iHookId == MENUHOOK_ID_CANCEL_EPG_REMINDER) - { - if (g_vbox->DeleteProgramReminders(item.data.iEpgUid)) - XBMC->QueueNotification(ADDON::QUEUE_INFO, "Reminder canceled"); - else - XBMC->QueueNotification(ADDON::QUEUE_WARNING, "Program does not have a reminder to cancel"); - return PVR_ERROR_NO_ERROR; - } - return PVR_ERROR_INVALID_PARAMETERS; + if (g_vbox->DeleteProgramReminders(item.data.iEpgUid)) + XBMC->QueueNotification(ADDON::QUEUE_INFO, "Reminder canceled"); + else + XBMC->QueueNotification(ADDON::QUEUE_WARNING, "Program does not have a reminder to cancel"); + return PVR_ERROR_NO_ERROR; } - else if (menuhook.category == PVR_MENUHOOK_CHANNEL) + return PVR_ERROR_INVALID_PARAMETERS; + } + else if (menuhook.category == PVR_MENUHOOK_CHANNEL) + { + if (menuhook.iHookId == MENUHOOK_ID_MANUAL_REMINDER) { - if (menuhook.iHookId == MENUHOOK_ID_MANUAL_REMINDER) - { - if (SetManualReminder(item)) - return PVR_ERROR_NO_ERROR; - } - else if (menuhook.iHookId == MENUHOOK_ID_CANCEL_CHANNEL_REMINDER) - { - if (g_vbox->DeleteChannelReminders(g_vbox->GetChannel(item.data.channel.iUniqueId))) - XBMC->QueueNotification(ADDON::QUEUE_INFO, "Removed channel's existing reminders"); - else - XBMC->QueueNotification(ADDON::QUEUE_WARNING, "Channel does not have reminders to cancel"); + if (SetManualReminder(item)) return PVR_ERROR_NO_ERROR; - } - return PVR_ERROR_INVALID_PARAMETERS; } - return PVR_ERROR_NOT_IMPLEMENTED; - } - - // Stream times - PVR_ERROR GetStreamTimes(PVR_STREAM_TIMES *) - { - // TODO: Addon API 5.8.0 - return PVR_ERROR_NOT_IMPLEMENTED; + else if (menuhook.iHookId == MENUHOOK_ID_CANCEL_CHANNEL_REMINDER) + { + if (g_vbox->DeleteChannelReminders(g_vbox->GetChannel(item.data.channel.iUniqueId))) + XBMC->QueueNotification(ADDON::QUEUE_INFO, "Removed channel's existing reminders"); + else + XBMC->QueueNotification(ADDON::QUEUE_WARNING, "Channel does not have reminders to cancel"); + return PVR_ERROR_NO_ERROR; + } + return PVR_ERROR_INVALID_PARAMETERS; } + return PVR_ERROR_NOT_IMPLEMENTED; +} - // Management methods - PVR_ERROR DialogChannelScan(void) { return PVR_ERROR_NOT_IMPLEMENTED; } - PVR_ERROR DeleteChannel(const PVR_CHANNEL &channel) { return PVR_ERROR_NOT_IMPLEMENTED; } - PVR_ERROR RenameChannel(const PVR_CHANNEL &channel) { return PVR_ERROR_NOT_IMPLEMENTED; } - PVR_ERROR DialogChannelSettings(const PVR_CHANNEL &channel) { return PVR_ERROR_NOT_IMPLEMENTED; } - PVR_ERROR DialogAddChannel(const PVR_CHANNEL &channel) { return PVR_ERROR_NOT_IMPLEMENTED; } - PVR_ERROR OpenDialogChannelScan(void) { return PVR_ERROR_NOT_IMPLEMENTED; } - PVR_ERROR OpenDialogChannelSettings(const PVR_CHANNEL &channel) { return PVR_ERROR_NOT_IMPLEMENTED; } - PVR_ERROR OpenDialogChannelAdd(const PVR_CHANNEL &channel) { return PVR_ERROR_NOT_IMPLEMENTED; } - PVR_ERROR UndeleteRecording(const PVR_RECORDING& recording) { return PVR_ERROR_NOT_IMPLEMENTED; } - - // Channel group methods - int GetChannelGroupsAmount(void) { return PVR_ERROR_NOT_IMPLEMENTED; } - PVR_ERROR GetChannelGroups(ADDON_HANDLE handle, bool bRadio) { return PVR_ERROR_NOT_IMPLEMENTED; } - PVR_ERROR GetChannelGroupMembers(ADDON_HANDLE handle, const PVR_CHANNEL_GROUP &group) { return PVR_ERROR_NOT_IMPLEMENTED; } - - // Recording stream methods - bool OpenRecordedStream(const PVR_RECORDING &recording) { return false; } - void CloseRecordedStream(void) {} - int ReadRecordedStream(unsigned char *pBuffer, unsigned int iBufferSize) { return 0; } - long long SeekRecordedStream(long long iPosition, int iWhence /* = SEEK_SET */) { return 0; } - long long LengthRecordedStream(void) { return 0; } - - // Demuxer methods - void DemuxReset(void) {} - void DemuxFlush(void) {} - void DemuxAbort(void) {} - DemuxPacket* DemuxRead(void) { return NULL; } - PVR_ERROR GetStreamProperties(PVR_STREAM_PROPERTIES* pProperties) { return PVR_ERROR_NOT_IMPLEMENTED; } - - // Recording methods (not supported by VBox) - PVR_ERROR RenameRecording(const PVR_RECORDING &recording) { return PVR_ERROR_NOT_IMPLEMENTED; } - PVR_ERROR SetRecordingLifetime(const PVR_RECORDING*) { return PVR_ERROR_NOT_IMPLEMENTED; } - 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; }; - PVR_ERROR DeleteAllRecordingsFromTrash() { return PVR_ERROR_NOT_IMPLEMENTED; } - - // EPG Methods - PVR_ERROR IsEPGTagRecordable(const EPG_TAG*, bool*) { return PVR_ERROR_NOT_IMPLEMENTED; } - PVR_ERROR IsEPGTagPlayable(const EPG_TAG*, bool*) { return PVR_ERROR_NOT_IMPLEMENTED; } - PVR_ERROR GetEPGTagStreamProperties(const EPG_TAG*, PVR_NAMED_VALUE*, unsigned int*) { return PVR_ERROR_NOT_IMPLEMENTED; } - PVR_ERROR GetEPGTagEdl(const EPG_TAG* epgTag, PVR_EDL_ENTRY edl[], int *size) { return PVR_ERROR_NOT_IMPLEMENTED; } - - // Timeshift methods - void PauseStream(bool bPaused) {} - bool SeekTime(double, bool, double*) { return false; } - void SetSpeed(int) {}; - bool IsTimeshifting(void) { return false; } - - // Miscellaneous unimplemented methods - PVR_ERROR GetChannelStreamProperties(const PVR_CHANNEL*, PVR_NAMED_VALUE*, unsigned int*) { return PVR_ERROR_NOT_IMPLEMENTED; } - PVR_ERROR GetStreamReadChunkSize(int* chunksize) { return PVR_ERROR_NOT_IMPLEMENTED; } +// Management methods +PVR_ERROR DialogChannelScan() { return PVR_ERROR_NOT_IMPLEMENTED; } +PVR_ERROR DeleteChannel(const PVR_CHANNEL &channel) { return PVR_ERROR_NOT_IMPLEMENTED; } +PVR_ERROR RenameChannel(const PVR_CHANNEL &channel) { return PVR_ERROR_NOT_IMPLEMENTED; } +PVR_ERROR DialogChannelSettings(const PVR_CHANNEL &channel) { return PVR_ERROR_NOT_IMPLEMENTED; } +PVR_ERROR DialogAddChannel(const PVR_CHANNEL &channel) { return PVR_ERROR_NOT_IMPLEMENTED; } +PVR_ERROR OpenDialogChannelScan() { return PVR_ERROR_NOT_IMPLEMENTED; } +PVR_ERROR OpenDialogChannelSettings(const PVR_CHANNEL &channel) { return PVR_ERROR_NOT_IMPLEMENTED; } +PVR_ERROR OpenDialogChannelAdd(const PVR_CHANNEL &channel) { return PVR_ERROR_NOT_IMPLEMENTED; } +PVR_ERROR UndeleteRecording(const PVR_RECORDING& recording) { return PVR_ERROR_NOT_IMPLEMENTED; } + +// Channel group methods +int GetChannelGroupsAmount() { return PVR_ERROR_NOT_IMPLEMENTED; } +PVR_ERROR GetChannelGroups(ADDON_HANDLE handle, bool bRadio) { return PVR_ERROR_NOT_IMPLEMENTED; } +PVR_ERROR GetChannelGroupMembers(ADDON_HANDLE handle, const PVR_CHANNEL_GROUP &group) { return PVR_ERROR_NOT_IMPLEMENTED; } + + +// Demuxer methods +void DemuxReset() {} +void DemuxFlush() {} +void DemuxAbort() {} +DemuxPacket* DemuxRead() { return NULL; } +PVR_ERROR GetStreamProperties(PVR_STREAM_PROPERTIES* pProperties) { return PVR_ERROR_NOT_IMPLEMENTED; } + +// Recording methods (not supported by VBox) +PVR_ERROR RenameRecording(const PVR_RECORDING &recording) { return PVR_ERROR_NOT_IMPLEMENTED; } +PVR_ERROR SetRecordingLifetime(const PVR_RECORDING*) { return PVR_ERROR_NOT_IMPLEMENTED; } +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; }; +PVR_ERROR DeleteAllRecordingsFromTrash() { return PVR_ERROR_NOT_IMPLEMENTED; } + +// EPG Methods +PVR_ERROR IsEPGTagRecordable(const EPG_TAG*, bool*) { return PVR_ERROR_NOT_IMPLEMENTED; } +PVR_ERROR IsEPGTagPlayable(const EPG_TAG*, bool*) { return PVR_ERROR_NOT_IMPLEMENTED; } +PVR_ERROR GetEPGTagStreamProperties(const EPG_TAG*, PVR_NAMED_VALUE*, unsigned int*) { return PVR_ERROR_NOT_IMPLEMENTED; } +PVR_ERROR GetEPGTagEdl(const EPG_TAG* epgTag, PVR_EDL_ENTRY edl[], int *size) { return PVR_ERROR_NOT_IMPLEMENTED; } + +// Timeshift methods +bool SeekTime(double, bool, double*) { return false; } +void SetSpeed(int) {}; +bool IsTimeshifting() { return false; } + +// Miscellaneous unimplemented methods +PVR_ERROR GetChannelStreamProperties(const PVR_CHANNEL*, PVR_NAMED_VALUE*, unsigned int*) { return PVR_ERROR_NOT_IMPLEMENTED; } +PVR_ERROR GetRecordingStreamProperties(const PVR_RECORDING* recording, PVR_NAMED_VALUE* properties, unsigned int* iPropertiesCount) { return PVR_ERROR_NOT_IMPLEMENTED; } +PVR_ERROR GetStreamReadChunkSize(int* chunksize) { return PVR_ERROR_NOT_IMPLEMENTED; } } diff -Nru kodi-pvr-vbox-4.4.8/src/client.h kodi-pvr-vbox-4.5.0/src/client.h --- kodi-pvr-vbox-4.4.8/src/client.h 2018-11-16 03:37:13.000000000 +0000 +++ kodi-pvr-vbox-4.5.0/src/client.h 2013-05-31 22:59:22.000000000 +0000 @@ -20,9 +20,9 @@ * */ -#include "libXBMC_addon.h" -#include "libXBMC_pvr.h" -#include +#include "kodi/libXBMC_addon.h" +#include "kodi/libXBMC_pvr.h" +#include #include "vbox/VBox.h" #ifdef TARGET_WINDOWS diff -Nru kodi-pvr-vbox-4.4.8/src/compat.h kodi-pvr-vbox-4.5.0/src/compat.h --- kodi-pvr-vbox-4.4.8/src/compat.h 2018-11-16 03:37:13.000000000 +0000 +++ kodi-pvr-vbox-4.5.0/src/compat.h 2013-05-31 22:59:22.000000000 +0000 @@ -43,14 +43,14 @@ template int stoi(const T& value) { std::istringstream iss(value); - + int result; iss >> result; return result; } /** - * Android doesn't fully support C++11 so std::stol() is missing, we provide + * Android doesn't fully support C++11 so std::stol() is missing, we provide * a limited version which does the job */ template long stol(const T& value) diff -Nru kodi-pvr-vbox-4.4.8/src/timeshift/Buffer.h kodi-pvr-vbox-4.5.0/src/timeshift/Buffer.h --- kodi-pvr-vbox-4.4.8/src/timeshift/Buffer.h 2018-11-16 03:37:13.000000000 +0000 +++ kodi-pvr-vbox-4.5.0/src/timeshift/Buffer.h 2013-05-31 22:59:22.000000000 +0000 @@ -128,7 +128,7 @@ void *m_inputHandle; /** - * The time (in seconds) to wait when opening a read handle and when + * The time (in seconds) to wait when opening a read handle and when * waiting for the buffer to have enough data */ int m_readTimeout; diff -Nru kodi-pvr-vbox-4.4.8/src/vbox/CategoryGenreMapper.cpp kodi-pvr-vbox-4.5.0/src/vbox/CategoryGenreMapper.cpp --- kodi-pvr-vbox-4.4.8/src/vbox/CategoryGenreMapper.cpp 2018-11-16 03:37:13.000000000 +0000 +++ kodi-pvr-vbox-4.5.0/src/vbox/CategoryGenreMapper.cpp 2013-05-31 22:59:22.000000000 +0000 @@ -53,7 +53,7 @@ LoadCategoryToGenreXML(xmlFileName); } -bool CategoryGenreMapper::LoadCategoryToGenreXML(const std::string &xmlFileName) +bool CategoryGenreMapper::LoadCategoryToGenreXML(const std::string &xmlFileName) { if (!XBMC->FileExists(xmlFileName.c_str(), false)) { @@ -175,9 +175,9 @@ // if no category matches - use string and return no specific genre if (matches.empty()) return EPG_GENRE_USE_STRING; - + UpdateFinalMatch(matches, finalMatch); - + XBMC->Log(ADDON::LOG_DEBUG, "Final match is %d", finalMatch->first); return finalMatch->first; } diff -Nru kodi-pvr-vbox-4.4.8/src/vbox/ChannelStreamingStatus.cpp kodi-pvr-vbox-4.5.0/src/vbox/ChannelStreamingStatus.cpp --- kodi-pvr-vbox-4.4.8/src/vbox/ChannelStreamingStatus.cpp 2018-11-16 03:37:13.000000000 +0000 +++ kodi-pvr-vbox-4.5.0/src/vbox/ChannelStreamingStatus.cpp 2013-05-31 22:59:22.000000000 +0000 @@ -86,7 +86,7 @@ } catch (std::invalid_argument) { - + } return rfLevel; diff -Nru kodi-pvr-vbox-4.4.8/src/vbox/ChannelStreamingStatus.h kodi-pvr-vbox-4.5.0/src/vbox/ChannelStreamingStatus.h --- kodi-pvr-vbox-4.4.8/src/vbox/ChannelStreamingStatus.h 2018-11-16 03:37:13.000000000 +0000 +++ kodi-pvr-vbox-4.5.0/src/vbox/ChannelStreamingStatus.h 2013-05-31 22:59:22.000000000 +0000 @@ -31,7 +31,7 @@ class ChannelStreamingStatus { public: - ChannelStreamingStatus() + ChannelStreamingStatus() : m_active(false), m_signalQuality(0), m_sid(0) {}; ~ChannelStreamingStatus() {}; diff -Nru kodi-pvr-vbox-4.4.8/src/vbox/GuideChannelMapper.cpp kodi-pvr-vbox-4.5.0/src/vbox/GuideChannelMapper.cpp --- kodi-pvr-vbox-4.4.8/src/vbox/GuideChannelMapper.cpp 2018-11-16 03:37:13.000000000 +0000 +++ kodi-pvr-vbox-4.5.0/src/vbox/GuideChannelMapper.cpp 2013-05-31 22:59:22.000000000 +0000 @@ -64,7 +64,7 @@ ChannelMappings mappings; std::vector channelNames = m_vboxGuide.GetChannelNames(); - // Add a mapping for every channel which display names matches, otherwise + // Add a mapping for every channel which display names matches, otherwise // leave it empty for (const std::string &channelName : channelNames) { diff -Nru kodi-pvr-vbox-4.4.8/src/vbox/Recording.cpp kodi-pvr-vbox-4.5.0/src/vbox/Recording.cpp --- kodi-pvr-vbox-4.4.8/src/vbox/Recording.cpp 2018-11-16 03:37:13.000000000 +0000 +++ kodi-pvr-vbox-4.5.0/src/vbox/Recording.cpp 2013-05-31 22:59:22.000000000 +0000 @@ -21,9 +21,11 @@ #include "Recording.h" +#include "../xmltv/Utilities.h" + using namespace vbox; -Recording::Recording(const std::string &channelId, +Recording::Recording(const std::string &channelId, const std::string &channelName, RecordingState state) : m_id(0), m_seriesId(0), m_channelId(channelId), m_channelName(channelName), m_state(state) { @@ -32,3 +34,16 @@ Recording::~Recording() { } + +bool Recording::IsRunning(const std::time_t now, const std::string& channelName, std::time_t startTime) const +{ + time_t timerStartTime = xmltv::Utilities::XmltvToUnixTime(m_startTime); + time_t timerEndTime = xmltv::Utilities::XmltvToUnixTime(m_endTime); + if (!(timerStartTime <= now && now <= timerEndTime)) + return false; + if (!channelName.empty() && m_channelName != channelName) + return false; + if (timerStartTime != startTime) + return false; + return true; +} \ No newline at end of file diff -Nru kodi-pvr-vbox-4.4.8/src/vbox/Recording.h kodi-pvr-vbox-4.5.0/src/vbox/Recording.h --- kodi-pvr-vbox-4.4.8/src/vbox/Recording.h 2018-11-16 03:37:13.000000000 +0000 +++ kodi-pvr-vbox-4.5.0/src/vbox/Recording.h 2013-05-31 22:59:22.000000000 +0000 @@ -20,6 +20,7 @@ * */ +#include #include #include @@ -45,7 +46,7 @@ class Recording { public: - Recording(const std::string &channelId, + Recording(const std::string &channelId, const std::string &channelName, RecordingState state); ~Recording(); @@ -60,6 +61,7 @@ m_description == other.m_description && m_startTime == other.m_startTime && m_endTime == other.m_endTime && + m_duration == other.m_duration && m_state == other.m_state; } @@ -68,13 +70,15 @@ return !(*this == other); } + bool IsRunning(const std::time_t now, const std::string& channelName, std::time_t startTime) const; + /** * Whether this object represents a timer * @return true if timer */ bool IsTimer() const { - return m_state == RecordingState::SCHEDULED || + return m_state == RecordingState::SCHEDULED || m_state == RecordingState::RECORDING; } @@ -84,7 +88,7 @@ */ bool IsRecording() const { - return m_state == RecordingState::RECORDED || + return m_state == RecordingState::RECORDED || m_state == RecordingState::RECORDING || m_state == RecordingState::RECORDING_ERROR || m_state == RecordingState::EXTERNAL; @@ -109,6 +113,7 @@ std::string m_description; std::string m_startTime; std::string m_endTime; + int m_duration; private: RecordingState m_state; diff -Nru kodi-pvr-vbox-4.4.8/src/vbox/RecordingReader.cpp kodi-pvr-vbox-4.5.0/src/vbox/RecordingReader.cpp --- kodi-pvr-vbox-4.4.8/src/vbox/RecordingReader.cpp 1970-01-01 00:00:00.000000000 +0000 +++ kodi-pvr-vbox-4.5.0/src/vbox/RecordingReader.cpp 2013-05-31 22:59:22.000000000 +0000 @@ -0,0 +1,126 @@ +/* + * Copyright (C) 2005-2019 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, 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1335, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "RecordingReader.h" + +#include "../client.h" + +#include + +using namespace ADDON; +using namespace vbox; + +RecordingReader::RecordingReader(const std::string& streamURL, std::time_t start, std::time_t end, int duration) + : m_streamURL(streamURL), m_start(start), m_end(end), m_duration(duration) +{ + m_readHandle = XBMC->CURLCreate(m_streamURL.c_str()); + XBMC->CURLOpen(m_readHandle, XFILE::READ_NO_CACHE); + m_len = XBMC->GetFileLength(m_readHandle); + m_nextReopen = std::time(nullptr) + REOPEN_INTERVAL; + + //If this is an ongoing recording set the duration to the eventual length of the recording + if (start > 0 && end > 0) + { + m_duration = static_cast(end - start); + } + + g_vbox->Log(ADDON::LOG_DEBUG, "%s RecordingReader: Started - url=%s, start=%u, end=%u, duration=%d", __FUNCTION__, m_streamURL.c_str(), + m_start, m_end, m_duration); +} + +RecordingReader::~RecordingReader() +{ + if (m_readHandle) + XBMC->CloseFile(m_readHandle); + g_vbox->Log(ADDON::LOG_DEBUG, "%s RecordingReader: Stopped", __FUNCTION__); +} + +bool RecordingReader::Start() +{ + return (m_readHandle != nullptr); +} + +ssize_t RecordingReader::ReadData(unsigned char* buffer, unsigned int size) +{ + /* check for playback of ongoing recording */ + if (m_end) + { + std::time_t now = std::time(nullptr); + if (m_pos == m_len || now > m_nextReopen) + { + /* reopen stream */ + g_vbox->Log(ADDON::LOG_DEBUG, "%s RecordingReader: Reopening stream...", __FUNCTION__); + XBMC->CURLOpen(m_readHandle, XFILE::READ_REOPEN | XFILE::READ_NO_CACHE); + m_len = XBMC->GetFileLength(m_readHandle); + XBMC->SeekFile(m_readHandle, m_pos, SEEK_SET); + + // random value (10 MiB) we choose to switch to fast reopen interval + bool nearEnd = m_len - m_pos <= 10 * 1024 * 1024; + m_nextReopen = now + (nearEnd ? REOPEN_INTERVAL_FAST : REOPEN_INTERVAL); + + /* recording has finished */ + if (now > m_end) + m_end = 0; + } + } + + ssize_t read = XBMC->ReadFile(m_readHandle, buffer, size); + m_pos += read; + return read; +} + +int64_t RecordingReader::Seek(long long position, int whence) +{ + int64_t ret = XBMC->SeekFile(m_readHandle, position, whence); + // for unknown reason seek sometimes doesn't return the correct position + // so let's sync with the underlaying implementation + m_pos = XBMC->GetFilePosition(m_readHandle); + m_len = XBMC->GetFileLength(m_readHandle); + return ret; +} + +int64_t RecordingReader::Position() +{ + return m_pos; +} + +int64_t RecordingReader::Length() +{ + return m_len; +} + +int RecordingReader::CurrentDuration() +{ + if (m_end != 0) + { + time_t now = std::time(nullptr); + + if (now < m_end) + { + g_vbox->Log(ADDON::LOG_DEBUG, "%s RecordingReader - Partial: %d", __FUNCTION__, static_cast(now - m_start)); + return now - m_start; + } + } + + g_vbox->Log(ADDON::LOG_DEBUG, "%s RecordingReader - Full: %d", __FUNCTION__, m_duration); + return m_duration; +} diff -Nru kodi-pvr-vbox-4.4.8/src/vbox/RecordingReader.h kodi-pvr-vbox-4.5.0/src/vbox/RecordingReader.h --- kodi-pvr-vbox-4.4.8/src/vbox/RecordingReader.h 1970-01-01 00:00:00.000000000 +0000 +++ kodi-pvr-vbox-4.5.0/src/vbox/RecordingReader.h 2013-05-31 22:59:22.000000000 +0000 @@ -0,0 +1,62 @@ +#pragma once +/* + * Copyright (C) 2005-2019 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, 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1335, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "kodi/libXBMC_addon.h" + +#include +#include + +namespace vbox +{ + class RecordingReader + { + public: + RecordingReader(const std::string& streamURL, std::time_t start, std::time_t end, int duration); + ~RecordingReader(); + + bool Start(); + ssize_t ReadData(unsigned char* buffer, unsigned int size); + int64_t Seek(long long position, int whence); + int64_t Position(); + int64_t Length(); + int CurrentDuration(); + + + private: + static const int REOPEN_INTERVAL = 30; + static const int REOPEN_INTERVAL_FAST = 10; + + const std::string& m_streamURL; + void* m_readHandle; + + int m_duration; + + /*!< @brief start and end time of the recording set only in case this an ongoing recording */ + std::time_t m_start; + std::time_t m_end; + + std::time_t m_nextReopen; + uint64_t m_pos = {0}; + uint64_t m_len; + }; +} // namespace vbox \ No newline at end of file diff -Nru kodi-pvr-vbox-4.4.8/src/vbox/Reminder.cpp kodi-pvr-vbox-4.5.0/src/vbox/Reminder.cpp --- kodi-pvr-vbox-4.4.8/src/vbox/Reminder.cpp 2018-11-16 03:37:13.000000000 +0000 +++ kodi-pvr-vbox-4.5.0/src/vbox/Reminder.cpp 2013-05-31 22:59:22.000000000 +0000 @@ -53,14 +53,14 @@ Reminder::Reminder(const ChannelPtr &channel, const ::xmltv::ProgrammePtr &programme, unsigned int minsInAdvance) : m_minsInAdvance(minsInAdvance), m_startTime(xmltv::Utilities::XmltvToUnixTime(programme->m_startTime)), - m_popTime(xmltv::Utilities::XmltvToUnixTime(programme->m_startTime) - (60 * m_minsInAdvance)), m_progName(programme->m_title), + m_popTime(xmltv::Utilities::XmltvToUnixTime(programme->m_startTime) - (60 * m_minsInAdvance)), m_progName(programme->m_title), m_channelName(channel->m_name), m_channelXmltvName(channel->m_xmltvName) { m_channelNum = FindChannelNumber(channel); } Reminder::Reminder(const ChannelPtr &channel, time_t startTime, std::string &progName, unsigned int minsInAdvance) : - m_minsInAdvance(minsInAdvance), m_startTime(startTime), + m_minsInAdvance(minsInAdvance), m_startTime(startTime), m_popTime(startTime - (60 * m_minsInAdvance)), m_progName(progName), m_channelName(channel->m_name), m_channelXmltvName(channel->m_xmltvName) { @@ -72,7 +72,7 @@ char buf[32], minBuf[32]; memset(minBuf, 0, sizeof(buf)); - + sprintf(buf, "[%u] ", m_channelNum); m_msgTitle = "Program reminder:"; diff -Nru kodi-pvr-vbox-4.4.8/src/vbox/Reminder.h kodi-pvr-vbox-4.5.0/src/vbox/Reminder.h --- kodi-pvr-vbox-4.4.8/src/vbox/Reminder.h 2018-11-16 03:37:13.000000000 +0000 +++ kodi-pvr-vbox-4.5.0/src/vbox/Reminder.h 2013-05-31 22:59:22.000000000 +0000 @@ -23,20 +23,20 @@ #include #include "Channel.h" #include "../xmltv/Programme.h" -#include "xbmc_pvr_types.h" +#include "kodi/xbmc_pvr_types.h" namespace vbox { class ReminderManager; /** - * Represents a single-program reminder. + * Represents a single-program reminder. * Contains a message reminding the user of the program */ class Reminder { public: - + /** * Creates a reminder from a channel and a specific program * @param channel the channel containing the program to remind @@ -44,7 +44,7 @@ * @param minsInAdvance minutes before the program's start time to pop the reminder */ Reminder(const ChannelPtr &channel, const ::xmltv::ProgrammePtr &programme, unsigned int minsInAdvance); - + /** * Creates a reminder according to a manually given program name and its' start time * @param channel the channel containing the program to remind @@ -52,7 +52,7 @@ * @param minsInAdvance minutes before the program's start time to pop the reminder */ Reminder(const ChannelPtr &channel, time_t startTime, std::string &progName, unsigned int minsInAdvance); - + /** * For comparing reminders' pop times */ @@ -62,7 +62,7 @@ } /** - * Composes & returns a message for a certain moment in time, showing details + * Composes & returns a message for a certain moment in time, showing details * of the program and the time left for it to start * @param currTime the current time of showing the reminder pop-up * @return the reminder's pop-up message diff -Nru kodi-pvr-vbox-4.4.8/src/vbox/ReminderManager.cpp kodi-pvr-vbox-4.5.0/src/vbox/ReminderManager.cpp --- kodi-pvr-vbox-4.4.8/src/vbox/ReminderManager.cpp 2018-11-16 03:37:13.000000000 +0000 +++ kodi-pvr-vbox-4.5.0/src/vbox/ReminderManager.cpp 2013-05-31 22:59:22.000000000 +0000 @@ -283,7 +283,7 @@ g_vbox->Log(ADDON::LOG_INFO, "Save(4): queue size %d, m_reminders size %d", queue.size(), m_reminders.size()); m_reminders = queue; g_vbox->Log(ADDON::LOG_INFO, "Save(5): queue size %d, m_reminders size %d", queue.size(), m_reminders.size()); - + XBMC->DeleteFile(REMINDERS_XML.c_str()); // Save the file void *fileHandle = XBMC->OpenFileForWrite(REMINDERS_XML.c_str(), false); diff -Nru kodi-pvr-vbox-4.4.8/src/vbox/ReminderManager.h kodi-pvr-vbox-4.5.0/src/vbox/ReminderManager.h --- kodi-pvr-vbox-4.4.8/src/vbox/ReminderManager.h 2018-11-16 03:37:13.000000000 +0000 +++ kodi-pvr-vbox-4.5.0/src/vbox/ReminderManager.h 2013-05-31 22:59:22.000000000 +0000 @@ -26,7 +26,7 @@ #include #include "Reminder.h" #include "../xmltv/Programme.h" -#include "xbmc_pvr_types.h" +#include "kodi/xbmc_pvr_types.h" namespace vbox { @@ -57,7 +57,7 @@ * @return the success of adding the newly created reminder */ bool AddReminder(const ChannelPtr &channel, const ::xmltv::ProgrammePtr &programme, unsigned int minsBeforePop); - + /** * Creates and stores a reminder with a given channel, and a manually given program name and its' start time * @param channel the channel containing the program to remind diff -Nru kodi-pvr-vbox-4.4.8/src/vbox/request/ApiRequest.cpp kodi-pvr-vbox-4.5.0/src/vbox/request/ApiRequest.cpp --- kodi-pvr-vbox-4.4.8/src/vbox/request/ApiRequest.cpp 2018-11-16 03:37:13.000000000 +0000 +++ kodi-pvr-vbox-4.5.0/src/vbox/request/ApiRequest.cpp 2013-05-31 22:59:22.000000000 +0000 @@ -50,7 +50,7 @@ // Add external IP and port options to the methods that support it if (std::find( - externalCapableMethods.cbegin(), + externalCapableMethods.cbegin(), externalCapableMethods.cend(), method) != externalCapableMethods.cend()) { AddParameter("ExternalIP", g_vbox->GetConnectionParams().hostname); diff -Nru kodi-pvr-vbox-4.4.8/src/vbox/request/ApiRequest.h kodi-pvr-vbox-4.5.0/src/vbox/request/ApiRequest.h --- kodi-pvr-vbox-4.4.8/src/vbox/request/ApiRequest.h 2018-11-16 03:37:13.000000000 +0000 +++ kodi-pvr-vbox-4.5.0/src/vbox/request/ApiRequest.h 2013-05-31 22:59:22.000000000 +0000 @@ -66,7 +66,7 @@ std::map> m_parameters; /** - * The timeout to use for the request. Defaults to zero which means the + * The timeout to use for the request. Defaults to zero which means the * default underlying systems timeout is used. */ int m_timeout; diff -Nru kodi-pvr-vbox-4.4.8/src/vbox/request/FileRequest.h kodi-pvr-vbox-4.5.0/src/vbox/request/FileRequest.h --- kodi-pvr-vbox-4.4.8/src/vbox/request/FileRequest.h 2018-11-16 03:37:13.000000000 +0000 +++ kodi-pvr-vbox-4.5.0/src/vbox/request/FileRequest.h 2013-05-31 22:59:22.000000000 +0000 @@ -32,10 +32,10 @@ class FileRequest : public Request { public: - FileRequest(const std::string &path) + FileRequest(const std::string &path) : m_path(path) {} virtual ~FileRequest() {} - + virtual response::ResponseType GetResponseType() const override { // Currently we always expect local files to contain XMLTV diff -Nru kodi-pvr-vbox-4.4.8/src/vbox/response/Content.cpp kodi-pvr-vbox-4.5.0/src/vbox/response/Content.cpp --- kodi-pvr-vbox-4.4.8/src/vbox/response/Content.cpp 2018-11-16 03:37:13.000000000 +0000 +++ kodi-pvr-vbox-4.5.0/src/vbox/response/Content.cpp 2013-05-31 22:59:22.000000000 +0000 @@ -20,7 +20,7 @@ */ #include "Content.h" -#include "xbmc_pvr_types.h" +#include "kodi/xbmc_pvr_types.h" #include "lib/tinyxml2/tinyxml2.h" #include "../Channel.h" #include "../../xmltv/Utilities.h" @@ -35,7 +35,7 @@ std::string Content::GetString(const std::string ¶meter) const { const XMLElement *element = GetParameterElement(parameter); - + if (element) return xmltv::Utilities::GetStdString(element->GetText()); @@ -167,7 +167,7 @@ { // Extract mandatory properties std::string channelId = xmltv::Utilities::UrlDecode(xmltv::Utilities::GetStdString(xml->Attribute("channel"))); - + const XMLElement *element = xml->FirstChildElement("channel-name"); if (!element) return nullptr; @@ -200,6 +200,14 @@ else recording->m_endTime = xmltv::Utilities::UnixTimeToXmltv(time(nullptr) + 86400); + std::time_t now = std::time(nullptr); + std::time_t startTime = xmltv::Utilities::XmltvToUnixTime(recording->m_startTime); + std::time_t endTime = xmltv::Utilities::XmltvToUnixTime(recording->m_endTime); + if (startTime > now && now < endTime) + recording->m_duration = static_cast(now - startTime); + else + recording->m_duration = static_cast(endTime - startTime); + element = xml->FirstChildElement("programme-title"); if (element) recording->m_title = xmltv::Utilities::GetStdString(element->GetText()); diff -Nru kodi-pvr-vbox-4.4.8/src/vbox/response/Response.cpp kodi-pvr-vbox-4.5.0/src/vbox/response/Response.cpp --- kodi-pvr-vbox-4.4.8/src/vbox/response/Response.cpp 2018-11-16 03:37:13.000000000 +0000 +++ kodi-pvr-vbox-4.5.0/src/vbox/response/Response.cpp 2013-05-31 22:59:22.000000000 +0000 @@ -69,12 +69,12 @@ errorCode = xmltv::Utilities::QueryIntText(errCodeEl); m_error.code = static_cast(errorCode); } - + if (errDescEl) { errorDescription = xmltv::Utilities::GetStdString(errDescEl->GetText()); m_error.description = errorDescription; - } + } } } diff -Nru kodi-pvr-vbox-4.4.8/src/vbox/response/Response.h kodi-pvr-vbox-4.5.0/src/vbox/response/Response.h --- kodi-pvr-vbox-4.4.8/src/vbox/response/Response.h 2018-11-16 03:37:13.000000000 +0000 +++ kodi-pvr-vbox-4.5.0/src/vbox/response/Response.h 2013-05-31 22:59:22.000000000 +0000 @@ -31,7 +31,7 @@ typedef std::unique_ptr ResponsePtr; /** - * The various response types (indicates what kind of content the response + * The various response types (indicates what kind of content the response * will contain) */ enum ResponseType { diff -Nru kodi-pvr-vbox-4.4.8/src/vbox/SoftwareVersion.cpp kodi-pvr-vbox-4.5.0/src/vbox/SoftwareVersion.cpp --- kodi-pvr-vbox-4.4.8/src/vbox/SoftwareVersion.cpp 2018-11-16 03:37:13.000000000 +0000 +++ kodi-pvr-vbox-4.5.0/src/vbox/SoftwareVersion.cpp 2013-05-31 22:59:22.000000000 +0000 @@ -35,7 +35,7 @@ { SoftwareVersion version; std::string format = "%d.%d.%d"; - + if (string.substr(0, 1) == "V") format = string.substr(0, 2) + ".%d.%d.%d"; diff -Nru kodi-pvr-vbox-4.4.8/src/vbox/SoftwareVersion.h kodi-pvr-vbox-4.5.0/src/vbox/SoftwareVersion.h --- kodi-pvr-vbox-4.4.8/src/vbox/SoftwareVersion.h 2018-11-16 03:37:13.000000000 +0000 +++ kodi-pvr-vbox-4.5.0/src/vbox/SoftwareVersion.h 2013-05-31 22:59:22.000000000 +0000 @@ -40,7 +40,7 @@ bool operator> (const SoftwareVersion &other) const { - return m_major > other.m_major || + return m_major > other.m_major || m_minor > other.m_minor || m_revision > other.m_revision; } @@ -59,7 +59,7 @@ { return !(*this > other); } - + /** * @return the software version as a string */ diff -Nru kodi-pvr-vbox-4.4.8/src/vbox/StartupStateHandler.h kodi-pvr-vbox-4.5.0/src/vbox/StartupStateHandler.h --- kodi-pvr-vbox-4.4.8/src/vbox/StartupStateHandler.h 2018-11-16 03:37:13.000000000 +0000 +++ kodi-pvr-vbox-4.5.0/src/vbox/StartupStateHandler.h 2013-05-31 22:59:22.000000000 +0000 @@ -65,7 +65,7 @@ std::unique_lock lock(m_mutex); // Wait for the state to change - m_condition.wait_for(lock, std::chrono::seconds(STATE_WAIT_TIMEOUT), + m_condition.wait_for(lock, std::chrono::seconds(STATE_WAIT_TIMEOUT), [this, state]() { return m_state >= state; @@ -102,7 +102,7 @@ private: /** - * The maximum amount of seconds to block while waiting for a state + * The maximum amount of seconds to block while waiting for a state * change */ const static int STATE_WAIT_TIMEOUT; diff -Nru kodi-pvr-vbox-4.4.8/src/vbox/VBox.cpp kodi-pvr-vbox-4.5.0/src/vbox/VBox.cpp --- kodi-pvr-vbox-4.4.8/src/vbox/VBox.cpp 2018-11-16 03:37:13.000000000 +0000 +++ kodi-pvr-vbox-4.5.0/src/vbox/VBox.cpp 2013-05-31 22:59:22.000000000 +0000 @@ -47,7 +47,7 @@ const size_t VBOX_LOG_BUFFER = 16384; VBox::VBox(const Settings &settings) - : m_settings(settings), m_currentChannel(nullptr), m_categoryGenreMapper(nullptr), m_shouldSyncEpg(false), m_reminderManager(nullptr), + : m_settings(settings), m_currentChannel(nullptr), m_categoryGenreMapper(nullptr), m_shouldSyncEpg(false), m_reminderManager(nullptr), m_lastStreamStatus({ChannelStreamingStatus(), time(nullptr)}) { } @@ -97,13 +97,13 @@ // Check that the backend uses a compatible software version if (m_backendInformation.version < SoftwareVersion::ParseString(MINIMUM_SOFTWARE_VERSION)) { - std::string error = std::string("Firmware version ") + + std::string error = std::string("Firmware version ") + MINIMUM_SOFTWARE_VERSION + " or higher is required"; throw FirmwareVersionException(error); } - // Query external media status. The request will error if no external media + // Query external media status. The request will error if no external media // is attached try { request::ApiRequest mediaRequest("QueryExternalMediaStatus"); @@ -183,7 +183,7 @@ // determine wether the device is in External XMLTV mode (internal, not through Kodi definitions) SendScanEPG(rScanMethod); GetEpgDetectionState(rGetStatusMethod, rfIsScanningFlag); - // if state is not collecting EPG - we're not in External XMLTV so + // if state is not collecting EPG - we're not in External XMLTV so // try the Live Signal mode syncing methods. If not - keep External XMLTV methods if (m_epgScanState != EPGSCAN_SCANNING) { @@ -223,6 +223,8 @@ m_epgScanState = EPGSCAN_NO_SCAN; } } + case EPGSCAN_NO_SCAN: + break; } } @@ -261,11 +263,11 @@ void VBox::BackgroundUpdater() { - // Keep count of how many times the loop has run so we can perform some + // Keep count of how many times the loop has run so we can perform some // tasks only on some iterations static unsigned int lapCounter = 1; - // Retrieve everything in order once before starting the loop, without + // Retrieve everything in order once before starting the loop, without // triggering the event handlers RetrieveChannels(false); RetrieveReminders(); @@ -293,7 +295,7 @@ DisplayReminder(reminder); m_reminderManager->DeleteNextReminder(); } - + // Update recordings every 12 iterations = 1 minute if (lapCounter % 12 == 0) RetrieveRecordings(); @@ -449,6 +451,25 @@ return m_reminderManager->DeleteProgramReminders(epgUid); } +const ChannelPtr VBox::FindChannelForEPGReminder(int epgUid) +{ + const xmltv::ProgrammePtr programme = nullptr; + const std::vector &channels = g_vbox->GetChannels(); + + // Find channel that contains this programme + const std::vector::const_iterator it = std::find_if(channels.cbegin(), channels.cend(), + [&epgUid](const ChannelPtr &channel) + { + const Schedule schedule = g_vbox->GetSchedule(channel); + const xmltv::ProgrammePtr programme = (schedule.schedule) ? schedule.schedule->GetProgramme(epgUid) : nullptr; + return (programme); + }); + // Find the channel's schedule + if (it == channels.cend()) + XBMC->QueueNotification(QUEUE_WARNING, "Reminder could not find the requested channel"); + return *it; +} + void VBox::StartEPGScan() { m_epgScanState = EPGSCAN_SHOULD_SCAN; @@ -518,7 +539,7 @@ if (currTime - lastUpdateTime >= STREAMING_STATUS_UPDATE_INTERVAL) SetChannelStreamingStatus(channel); - + return m_lastStreamStatus.m_streamStatus; } @@ -551,8 +572,8 @@ { RecordingState state = recording->GetState(); unsigned int idToDelete = (recording->m_seriesId > 0)? recording->m_seriesId : recording->m_id; - - // Determine the request method to use. If a recording is active we want to + + // Determine the request method to use. If a recording is active we want to // cancel it instead of deleting it std::string requestMethod = "DeleteRecord"; @@ -630,7 +651,7 @@ throw vbox::RequestFailedException("Could not find timer's ID in backend"); } } - + // Fire events OnRecordingsUpdated(); OnTimersUpdated(); @@ -698,7 +719,7 @@ RecordingMargins updatedMargins; bool fBackendSingleMargin = true; - // if version < 2.57 (support only for 1 margin), set updated margin for both + // if version < 2.57 (support only for 1 margin), set updated margin for both // before + after the program times as the larger margin of the timer margins (before / after) if (version < SoftwareVersion::ParseString("2.57")) { @@ -715,7 +736,7 @@ } const RecordingMargins currMargins = GetRecordingMargins(fBackendSingleMargin); - // set the updated margins in backend, if different from the current ones + // set the updated margins in backend, if different from the current ones if (updatedMargins != currMargins) g_vbox->SetRecordingMargins(updatedMargins, fBackendSingleMargin); } @@ -769,7 +790,7 @@ void VBox::AddSeriesTimer(const ChannelPtr &channel, const ::xmltv::ProgrammePtr programme) { Log(LOG_DEBUG, "Series timer for channel %s, program %s", channel->m_name.c_str(), programme->m_title.c_str()); - + // Add the timer request::ApiRequest request("ScheduleProgramRecord"); request.AddParameter("ChannelID", channel->m_xmltvName); @@ -861,7 +882,7 @@ Schedule schedule; schedule.schedule = m_guide.GetSchedule(channel->m_xmltvName); - // Try to use the external guide data if a) it's loaded, b) the user prefers + // Try to use the external guide data if a) it's loaded, b) the user prefers // it or c) if no schedule was found if (m_stateHandler.GetState() >= StartupState::EXTERNAL_GUIDE_LOADED && (m_settings.m_preferExternalXmltv || !schedule.schedule)) @@ -895,7 +916,7 @@ std::string VBox::CreateTimestamp(const time_t unixTimestamp) const { std::string tzOffset = m_backendInformation.timezoneOffset; - + return ::xmltv::Utilities::UnixTimeToXmltv(unixTimestamp, tzOffset); } @@ -946,7 +967,7 @@ return; int toIndex = std::min(fromIndex + (CHANNELS_PER_CHANNELBATCH - 1) , lastChannelIndex); - // Swallow exceptions, we don't want channel loading to fail just because + // Swallow exceptions, we don't want channel loading to fail just because // one request failed try { @@ -979,7 +1000,7 @@ m_channels = allChannels; Log(LOG_INFO, "Channels database version updated to %u", newDBversion); m_channelsDBVersion = newDBversion; - + if (triggerEvent) OnChannelsUpdated(); } @@ -1024,7 +1045,7 @@ } catch (VBoxException &e) { - // Intentionally don't return, the request fails if there are no + // Intentionally don't return, the request fails if there are no // recordings (which is technically not an error) LogException(e); } @@ -1046,7 +1067,7 @@ if (!m_shouldSyncEpg && newDBversion == m_programsDBVersion) return; - // Retrieving the whole XMLTV file is too slow so we fetch sections in + // Retrieving the whole XMLTV file is too slow so we fetch sections in // batches of 10 channels and merge the results int lastChannelIndex; @@ -1065,7 +1086,7 @@ int toIndex = std::min(fromIndex + (CHANNELS_PER_EPGBATCH - 1), lastChannelIndex); - // Swallow exceptions, we don't want guide loading to fail just because + // Swallow exceptions, we don't want guide loading to fail just because // one request failed try { @@ -1141,7 +1162,7 @@ void VBox::InitializeChannelMapper() { // Abort if we're already initialized or the external guide is not loaded - if (m_guideChannelMapper || + if (m_guideChannelMapper || m_stateHandler.GetState() < StartupState::EXTERNAL_GUIDE_LOADED) { return; @@ -1245,7 +1266,7 @@ } // The request failed completely - throw RequestFailedException("Unable to perform request (" + + throw RequestFailedException("Unable to perform request (" + request.GetIdentifier() + ")"); } diff -Nru kodi-pvr-vbox-4.4.8/src/vbox/VBox.h kodi-pvr-vbox-4.5.0/src/vbox/VBox.h --- kodi-pvr-vbox-4.4.8/src/vbox/VBox.h 2018-11-16 03:37:13.000000000 +0000 +++ kodi-pvr-vbox-4.5.0/src/vbox/VBox.h 2013-05-31 22:59:22.000000000 +0000 @@ -26,7 +26,7 @@ #include #include #include -#include +#include #include "Channel.h" #include "ChannelStreamingStatus.h" #include "GuideChannelMapper.h" @@ -70,7 +70,7 @@ }; /** - * Represents a schedule. It contains an actual schedule and an indicator + * Represents a schedule. It contains an actual schedule and an indicator * which tells if the schedule is from the internal or external guide */ struct Schedule @@ -150,7 +150,7 @@ std::string GetApiBaseUrl() const; /** - * Converts a UTC UNIX timestamp to an XMLTV timestamp localized for the + * Converts a UTC UNIX timestamp to an XMLTV timestamp localized for the * backends timezone offset * @param unixTimestamp a UTC UNIX timestamp * @return XMLTV timestamp localized for the current backend @@ -163,7 +163,7 @@ std::string GetBackendHostname() const; std::string GetBackendVersion() const; std::string GetConnectionString() const; - + // Channel methods int GetChannelsAmount() const; const std::vector& GetChannels() const; @@ -207,6 +207,7 @@ bool AddReminder(const ChannelPtr &channel, time_t startTime, std::string &progName); bool DeleteChannelReminders(const ChannelPtr &channel); bool DeleteProgramReminders(unsigned int epgUid); + const ChannelPtr FindChannelForEPGReminder(int epgUid); // Helpers static void Log(const ADDON::addon_log level, const char *format, ...); @@ -219,7 +220,7 @@ std::function OnGuideUpdated; private: - + void BackgroundUpdater(); unsigned int GetDBVersion(std::string &versionName) const; void RetrieveChannels(bool triggerEvent = true); @@ -273,7 +274,7 @@ std::vector m_series; /** - * The guide data. The XMLTV channel name is the key, the value is the + * The guide data. The XMLTV channel name is the key, the value is the * schedule for the channel */ ::xmltv::Guide m_guide; @@ -307,7 +308,7 @@ * The background update thread */ std::thread m_backgroundThread; - + /** * The state of EPG scanning - if set to EPGSCAN_SHOULD_SCAN --> EPG scanning starts */ @@ -339,7 +340,7 @@ std::atomic m_shouldSyncEpg; /** - * The currently active channel, or the last active channel when no + * The currently active channel, or the last active channel when no * channel is playing */ ChannelPtr m_currentChannel; diff -Nru kodi-pvr-vbox-4.4.8/src/xmltv/Channel.cpp kodi-pvr-vbox-4.5.0/src/xmltv/Channel.cpp --- kodi-pvr-vbox-4.4.8/src/xmltv/Channel.cpp 2018-11-16 03:37:13.000000000 +0000 +++ kodi-pvr-vbox-4.5.0/src/xmltv/Channel.cpp 2013-05-31 22:59:22.000000000 +0000 @@ -26,5 +26,5 @@ Channel::Channel(const std::string& id, const std::string& displayName) : m_id(id), m_displayName(displayName), m_icon("") { - + } diff -Nru kodi-pvr-vbox-4.4.8/src/xmltv/Guide.h kodi-pvr-vbox-4.5.0/src/xmltv/Guide.h --- kodi-pvr-vbox-4.4.8/src/xmltv/Guide.h 2018-11-16 03:37:13.000000000 +0000 +++ kodi-pvr-vbox-4.5.0/src/xmltv/Guide.h 2013-05-31 22:59:22.000000000 +0000 @@ -63,7 +63,7 @@ // Merge the display name mappings m_displayNameMappings.insert( - other.m_displayNameMappings.begin(), + other.m_displayNameMappings.begin(), other.m_displayNameMappings.end()); return *this; diff -Nru kodi-pvr-vbox-4.4.8/src/xmltv/Programme.cpp kodi-pvr-vbox-4.5.0/src/xmltv/Programme.cpp --- kodi-pvr-vbox-4.4.8/src/xmltv/Programme.cpp 2018-11-16 03:37:13.000000000 +0000 +++ kodi-pvr-vbox-4.5.0/src/xmltv/Programme.cpp 2013-05-31 22:59:22.000000000 +0000 @@ -64,7 +64,7 @@ if (element) m_icon = xmltv::Utilities::GetStdString(element->Attribute("src")); - // Categories. Skip "movie" and "series" since most people treat categories + // Categories. Skip "movie" and "series" since most people treat categories // as genres for (element = xml->FirstChildElement("category"); element != NULL; element = element->NextSiblingElement("category")) diff -Nru kodi-pvr-vbox-4.4.8/src/xmltv/Programme.h kodi-pvr-vbox-4.5.0/src/xmltv/Programme.h --- kodi-pvr-vbox-4.4.8/src/xmltv/Programme.h 2018-11-16 03:37:13.000000000 +0000 +++ kodi-pvr-vbox-4.5.0/src/xmltv/Programme.h 2013-05-31 22:59:22.000000000 +0000 @@ -32,7 +32,7 @@ } namespace xmltv { - + class Programme; typedef std::shared_ptr ProgrammePtr; typedef std::map SeriesIDMap; diff -Nru kodi-pvr-vbox-4.4.8/src/xmltv/Schedule.cpp kodi-pvr-vbox-4.5.0/src/xmltv/Schedule.cpp --- kodi-pvr-vbox-4.4.8/src/xmltv/Schedule.cpp 2018-11-16 03:37:13.000000000 +0000 +++ kodi-pvr-vbox-4.5.0/src/xmltv/Schedule.cpp 2013-05-31 22:59:22.000000000 +0000 @@ -29,7 +29,7 @@ Schedule::Schedule(ChannelPtr& channel) : m_channel(channel) { - + } void Schedule::AddProgramme(ProgrammePtr programme) diff -Nru kodi-pvr-vbox-4.4.8/src/xmltv/Schedule.h kodi-pvr-vbox-4.5.0/src/xmltv/Schedule.h --- kodi-pvr-vbox-4.4.8/src/xmltv/Schedule.h 2018-11-16 03:37:13.000000000 +0000 +++ kodi-pvr-vbox-4.5.0/src/xmltv/Schedule.h 2013-05-31 22:59:22.000000000 +0000 @@ -25,7 +25,7 @@ #include #include -// Visual Studio can't handle type names longer than 255 characters in debug +// Visual Studio can't handle type names longer than 255 characters in debug // mode, disable that warning since it's not important #ifdef _MSC_VER #pragma warning(disable : 4503) @@ -63,7 +63,7 @@ const ProgrammePtr GetProgramme(int programmeUniqueId) const; /** - * Returns a schedule segment containing all programmes between the + * Returns a schedule segment containing all programmes between the * specified timestamps * @param startTime the start time * @param endTime the end time diff -Nru kodi-pvr-vbox-4.4.8/src/xmltv/Utilities.h kodi-pvr-vbox-4.5.0/src/xmltv/Utilities.h --- kodi-pvr-vbox-4.4.8/src/xmltv/Utilities.h 2018-11-16 03:37:13.000000000 +0000 +++ kodi-pvr-vbox-4.5.0/src/xmltv/Utilities.h 2013-05-31 22:59:22.000000000 +0000 @@ -46,13 +46,13 @@ /** * Returns the timezone offset part of the specified XMLTV timestamp * @param timestamp an XMLTV timestamp - * @return the timezone offset, e.g. "+0200", or an empty string if no + * @return the timezone offset, e.g. "+0200", or an empty string if no * timezone offset could be parsed. */ static std::string GetTimezoneOffset(const std::string timestamp); /** - * Converts the specified timezone offset into the number of seconds it + * Converts the specified timezone offset into the number of seconds it * differs from UTC * @param tzOffset the timezone offset, e.g. "+0200" * @return the number of seconds to adjust @@ -67,7 +67,7 @@ static time_t XmltvToUnixTime(const std::string &time); /** - * Converts a UTC time_t to an XMLTV datetime string, optionally adjusted + * Converts a UTC time_t to an XMLTV datetime string, optionally adjusted * for the specified timezone offset * @param time a UNIX timestamp * @param tzOffset the timezone offset, e.g. "+0200" @@ -85,13 +85,13 @@ static std::string UnixTimeToDailyTime(const time_t timestamp, const std::string tzOffset = ""); /** - * Parses the contents of the specified element into an integer. We need + * Parses the contents of the specified element into an integer. We need * this for backward-compatibility with older versions of tinyxml2. */ static int QueryIntText(const tinyxml2::XMLElement *element); /** - * Parses the contents of the specified element into an unsigned integer. + * Parses the contents of the specified element into an unsigned integer. * We need this for backward-compatibility with older versions of tinyxml2. */ static unsigned int QueryUnsignedText(const tinyxml2::XMLElement *element); @@ -118,7 +118,7 @@ * @param separator the separator * @return a concatenated string */ - static std::string ConcatenateStringList(const std::vector &vector, + static std::string ConcatenateStringList(const std::vector &vector, const std::string &separator = ", "); /** diff -Nru kodi-pvr-vbox-4.4.8/.travis.yml kodi-pvr-vbox-4.5.0/.travis.yml --- kodi-pvr-vbox-4.4.8/.travis.yml 2018-11-16 03:37:13.000000000 +0000 +++ kodi-pvr-vbox-4.5.0/.travis.yml 2013-05-31 22:59:22.000000000 +0000 @@ -32,7 +32,7 @@ # before_script: - cd $TRAVIS_BUILD_DIR/.. - - git clone --depth=1 https://github.com/xbmc/xbmc.git + - git clone --branch Leia --depth=1 https://github.com/xbmc/xbmc.git - cd ${app_id} && mkdir build && cd build - mkdir -p definition/${app_id} - echo ${app_id} $TRAVIS_BUILD_DIR $TRAVIS_COMMIT > definition/${app_id}/${app_id}.txt