diff --git a/CMakeLists.txt b/CMakeLists.txt index 5fa3cb7..9f388d5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2,25 +2,21 @@ cmake_minimum_required(VERSION 3.5) project(pvr.wmc) find_package(Kodi REQUIRED) -find_package(p8-platform REQUIRED) -include_directories(${p8-platform_INCLUDE_DIRS} - ${KODI_INCLUDE_DIR}/..) # Hack way with "/..", need bigger Kodi cmake rework to match right include ways +include_directories(${KODI_INCLUDE_DIR}/..) # Hack way with "/..", need bigger Kodi cmake rework to match right include ways -add_definitions(-DUSE_DEMUX) - -set(WMC_SOURCES src/client.cpp +set(WMC_SOURCES src/addon.cpp src/pvr2wmc.cpp + src/settings.cpp src/Socket.cpp src/utilities.cpp) -set(WMC_HEADERS src/client.h +set(WMC_HEADERS src/addon.h src/pvr2wmc.h + src/settings.h src/Socket.h src/utilities.h) -set(DEPLIBS ${p8-platform_LIBRARIES}) - if (WIN32) list(APPEND DEPLIBS ws2_32) add_compile_options(/wd4996) # Need gethostbyname and inet_addr change to prevent diff --git a/debian/control b/debian/control index ce5e6d8..2bee815 100644 --- a/debian/control +++ b/debian/control @@ -1,8 +1,7 @@ Source: kodi-pvr-wmc Priority: extra Maintainer: Nobody -Build-Depends: debhelper (>= 9.0.0), cmake, libp8-platform-dev, - kodi-addon-dev +Build-Depends: debhelper (>= 9.0.0), cmake, kodi-addon-dev Standards-Version: 4.1.2 Section: libs diff --git a/depends/common/p8-platform/p8-platform.txt b/depends/common/p8-platform/p8-platform.txt deleted file mode 100644 index 98ed58a..0000000 --- a/depends/common/p8-platform/p8-platform.txt +++ /dev/null @@ -1 +0,0 @@ -p8-platform https://github.com/xbmc/platform.git cee64e9dc0b69e8d286dc170a78effaabfa09c44 diff --git a/depends/windowsstore/p8-platform/p8-platform.txt b/depends/windowsstore/p8-platform/p8-platform.txt deleted file mode 100644 index db6f782..0000000 --- a/depends/windowsstore/p8-platform/p8-platform.txt +++ /dev/null @@ -1 +0,0 @@ -p8-platform https://github.com/afedchin/platform.git win10 diff --git a/src/Socket.cpp b/src/Socket.cpp index d267a58..e9c695a 100644 --- a/src/Socket.cpp +++ b/src/Socket.cpp @@ -8,28 +8,27 @@ #include "Socket.h" -#include "client.h" +#include "pvr2wmc.h" #include "utilities.h" -#include "kodi/libXBMC_addon.h" -#include "p8-platform/os.h" -#include "p8-platform/threads/mutex.h" -#include "p8-platform/util/timeutils.h" - +#include +#include +#include +#include #include +#include -using namespace std; -using namespace ADDON; - -P8PLATFORM::CMutex m_mutex; +std::mutex m_mutex; /* Master defines for client control */ //#define RECEIVE_TIMEOUT 6 //sec -Socket::Socket(const enum SocketFamily family, +Socket::Socket(Pvr2Wmc& client, + const enum SocketFamily family, const enum SocketDomain domain, const enum SocketType type, const enum SocketProtocol protocol) + : _client(client) { _sd = INVALID_SOCKET; _family = family; @@ -37,10 +36,10 @@ Socket::Socket(const enum SocketFamily family, _type = type; _protocol = protocol; memset(&_sockaddr, 0, sizeof(_sockaddr)); - //set_non_blocking(1); + // set_non_blocking(1); } -Socket::Socket() +Socket::Socket(Pvr2Wmc& client) : _client(client) { // Default constructor, default settings _sd = INVALID_SOCKET; @@ -109,9 +108,9 @@ bool Socket::create() } _sd = socket(_family, _type, _protocol); - //0 indicates that the default protocol for the type selected is to be used. - //For example, IPPROTO_TCP is chosen for the protocol if the type was set to - //SOCK_STREAM and the address family is AF_INET. + // 0 indicates that the default protocol for the type selected is to be used. + // For example, IPPROTO_TCP is chosen for the protocol if the type was set to + // SOCK_STREAM and the address family is AF_INET. if (_sd == INVALID_SOCKET) { @@ -170,13 +169,13 @@ int Socket::send(const char* data, const unsigned int len) if (result < 0) { - XBMC->Log(LOG_ERROR, "Socket::send - select failed"); + kodi::Log(ADDON_LOG_ERROR, "Socket::send - select failed"); _sd = INVALID_SOCKET; return 0; } if (FD_ISSET(_sd, &set_w)) { - XBMC->Log(LOG_ERROR, "Socket::send - failed to send data"); + kodi::Log(ADDON_LOG_ERROR, "Socket::send - failed to send data"); _sd = INVALID_SOCKET; return 0; } @@ -186,14 +185,14 @@ int Socket::send(const char* data, const unsigned int len) if (status == -1) { errormessage(getLastError(), "Socket::send"); - XBMC->Log(LOG_ERROR, "Socket::send - failed to send data"); + kodi::Log(ADDON_LOG_ERROR, "Socket::send - failed to send data"); _sd = INVALID_SOCKET; } return status; } -//Receive until error or \n -bool Socket::ReadResponses(int& code, vector& lines) +// Receive until error or \n +bool Socket::ReadResponses(int& code, std::vector& lines) { int result; char buffer[4096]; // this buff size has to be known in server @@ -209,9 +208,9 @@ bool Socket::ReadResponses(int& code, vector& lines) { #ifdef TARGET_WINDOWS int errorCode = WSAGetLastError(); - XBMC->Log(LOG_DEBUG, "ReadResponse ERROR - recv failed, Err: %d", errorCode); + kodi::Log(ADDON_LOG_DEBUG, "ReadResponse ERROR - recv failed, Err: %d", errorCode); #else - XBMC->Log(LOG_DEBUG, "ReadResponse ERROR - recv failed"); + kodi::Log(ADDON_LOG_DEBUG, "ReadResponse ERROR - recv failed"); #endif code = 1; _sd = INVALID_SOCKET; @@ -227,15 +226,15 @@ bool Socket::ReadResponses(int& code, vector& lines) } while (result > 0); // keep reading until result returns '0', meaning server is done sending reponses - if (EndsWith(bigString, "")) + if (Utils::EndsWith(bigString, "")) { readComplete = true; // all server data has benn read - lines = split(bigString, ""); // split each reponse by delimiters + lines = Utils::Split(bigString, ""); // split each reponse by delimiters lines.erase(lines.end() - 1); // erase at end } else { - XBMC->Log(LOG_DEBUG, "ReadResponse ERROR - in read reponses not found"); + kodi::Log(ADDON_LOG_DEBUG, "ReadResponse ERROR - in read reponses not found"); _sd = INVALID_SOCKET; } @@ -255,7 +254,7 @@ bool Socket::connect(const std::string& host, const unsigned short port) if (!setHostname(host)) { - XBMC->Log(LOG_ERROR, "Socket::setHostname(%s) failed.\n", host.c_str()); + kodi::Log(ADDON_LOG_ERROR, "Socket::setHostname(%s) failed.\n", host.c_str()); return false; } @@ -263,7 +262,7 @@ bool Socket::connect(const std::string& host, const unsigned short port) if (status == SOCKET_ERROR) { - XBMC->Log(LOG_ERROR, "Socket::connect %s:%u\n", host.c_str(), port); + kodi::Log(ADDON_LOG_ERROR, "Socket::connect %s:%u\n", host.c_str(), port); errormessage(getLastError(), "Socket::connect"); return false; } @@ -309,7 +308,8 @@ bool Socket::set_non_blocking(const bool b) if (ioctlsocket(_sd, FIONBIO, &iMode) == -1) { - XBMC->Log(LOG_ERROR, "Socket::set_non_blocking - Can't set socket condition to: %i", iMode); + kodi::Log(ADDON_LOG_ERROR, "Socket::set_non_blocking - Can't set socket condition to: %i", + iMode); return false; } @@ -400,7 +400,7 @@ void Socket::errormessage(int errnum, const char* functionname) const default: errmsg = "WSA Error"; } - XBMC->Log(LOG_ERROR, "%s: (Winsock error=%i) %s\n", functionname, errnum, errmsg); + kodi::Log(ADDON_LOG_ERROR, "%s: (Winsock error=%i) %s\n", functionname, errnum, errmsg); } int Socket::getLastError() const @@ -408,7 +408,7 @@ int Socket::getLastError() const return WSAGetLastError(); } -int Socket::win_usage_count = 0; //Declared static in Socket class +int Socket::win_usage_count = 0; // Declared static in Socket class bool Socket::osInit() { @@ -458,7 +458,7 @@ bool Socket::set_non_blocking(const bool b) if (fcntl(_sd, F_SETFL, opts) == -1) { - XBMC->Log(LOG_ERROR, "Socket::set_non_blocking - Can't set socket flags to: %i", opts); + kodi::Log(ADDON_LOG_ERROR, "Socket::set_non_blocking - Can't set socket flags to: %i", opts); return false; } return true; @@ -470,7 +470,7 @@ void Socket::errormessage(int errnum, const char* functionname) const switch (errnum) { - case EAGAIN: //same as EWOULDBLOCK + case EAGAIN: // same as EWOULDBLOCK errmsg = "EAGAIN: The socket is marked non-blocking and the requested operation would block"; break; case EBADF: @@ -532,13 +532,13 @@ void Socket::errormessage(int errnum, const char* functionname) const errmsg = "ENOTCONN: The socket is associated with a connection-oriented protocol and has not " "been connected"; break; - //case E: + // case E: // errmsg = ""; // break; default: break; } - XBMC->Log(LOG_ERROR, "%s: (errno=%i) %s\n", functionname, errnum, errmsg); + kodi::Log(ADDON_LOG_ERROR, "%s: (errno=%i) %s\n", functionname, errnum, errmsg); } int Socket::getLastError() const @@ -556,7 +556,7 @@ void Socket::osCleanup() { // Not needed for Linux } -#endif //TARGET_WINDOWS || TARGET_LINUX || TARGET_DARWIN || TARGET_FREEBSD +#endif // TARGET_WINDOWS || TARGET_LINUX || TARGET_DARWIN || TARGET_FREEBSD void Socket::SetServerName(std::string strServerName) @@ -577,7 +577,7 @@ void Socket::SetServerPort(int port) int Socket::SendRequest(std::string requestStr) { std::string sRequest; - sRequest = string_format("%s|%s", _clientName.c_str(), + sRequest = Utils::Format("%s|%s", _clientName.c_str(), requestStr.c_str()); // build the request string int status = send(sRequest); return status; @@ -596,7 +596,7 @@ std::vector Socket::GetVector(const std::string& request, int maxAttempts = 3; int sleepAttemptsMs = 1000; - P8PLATFORM::CLockObject lock(m_mutex); // only process one request at a time + std::lock_guard lock(m_mutex); // only process one request at a time int code; std::vector reponses; @@ -604,47 +604,49 @@ std::vector Socket::GetVector(const std::string& request, int cntAttempts = 1; while (cntAttempts <= maxAttempts) { - XBMC->Log(LOG_DEBUG, "Socket::GetVector> Send request \"%s\"", request.c_str()); + kodi::Log(ADDON_LOG_DEBUG, "Socket::GetVector> Send request \"%s\"", request.c_str()); reponses.clear(); if (!create()) // create the socket { - XBMC->Log(LOG_ERROR, "Socket::GetVector> error could not create socket"); + kodi::Log(ADDON_LOG_ERROR, "Socket::GetVector> error could not create socket"); reponses.push_back("SocketError"); // set a SocketError message (not fatal) } else // socket created OK { // Attempt Wake On Lan - if (g_BackendOnline != BACKEND_UP && allowWOL && g_bWakeOnLAN && g_strServerMAC != "") + if (_client.GetBackendStatus() != BACKEND_UP && allowWOL && + _client.GetSettings().GetWakeOnLAN() && !_client.GetSettings().GetServerMAC().empty()) { - XBMC->Log(LOG_INFO, "Socket::GetVector> Sending WOL packet to %s", g_strServerMAC.c_str()); - if (g_BackendOnline != BACKEND_UNKNOWN) + kodi::Log(ADDON_LOG_INFO, "Socket::GetVector> Sending WOL packet to %s", + _client.GetSettings().GetServerMAC().c_str()); + if (_client.GetBackendStatus() != BACKEND_UNKNOWN) { - std::string infoStr = XBMC->GetLocalizedString(30026); - XBMC->QueueNotification(QUEUE_INFO, infoStr.c_str()); // Notify WOL is being sent + std::string infoStr = kodi::GetLocalizedString(30026); + kodi::QueueNotification(QUEUE_INFO, "", infoStr); // Notify WOL is being sent } - XBMC->WakeOnLan(g_strServerMAC.c_str()); // Send WOL request + kodi::network::WakeOnLan(_client.GetSettings().GetServerMAC()); // Send WOL request } if (!connect(_serverName, (unsigned short)_port)) // if this fails, it is likely due to server down { // Failed to connect - g_BackendOnline = BACKEND_DOWN; - XBMC->Log(LOG_ERROR, "Socket::GetVector> Server is down"); + _client.SetBackendStatus(BACKEND_DOWN); + kodi::Log(ADDON_LOG_ERROR, "Socket::GetVector> Server is down"); reponses.push_back("ServerDown"); // set a server down error message (not fatal) } else { // Connected OK - g_BackendOnline = BACKEND_UP; + _client.SetBackendStatus(BACKEND_UP); int bytesSent = SendRequest(request.c_str()); // send request to server if (bytesSent > 0) // if request was sent successfully { if (!ReadResponses(code, reponses)) { - XBMC->Log(LOG_ERROR, "Socket::GetVector> error getting responses"); + kodi::Log(ADDON_LOG_ERROR, "Socket::GetVector> error getting responses"); reponses.clear(); reponses.push_back("SocketError"); } @@ -655,7 +657,7 @@ std::vector Socket::GetVector(const std::string& request, } else // error sending request { - XBMC->Log(LOG_ERROR, "Socket::GetVector> error sending server request"); + kodi::Log(ADDON_LOG_ERROR, "Socket::GetVector> error sending server request"); reponses.push_back("SocketError"); } } @@ -667,8 +669,8 @@ std::vector Socket::GetVector(const std::string& request, } cntAttempts++; - XBMC->Log(LOG_DEBUG, "Socket::GetVector> Retrying in %ims", sleepAttemptsMs); - usleep(sleepAttemptsMs * 1000); + kodi::Log(ADDON_LOG_DEBUG, "Socket::GetVector> Retrying in %ims", sleepAttemptsMs); + std::this_thread::sleep_for(std::chrono::milliseconds(sleepAttemptsMs)); } close(); // close socket diff --git a/src/Socket.h b/src/Socket.h index 61d231e..96509e6 100644 --- a/src/Socket.h +++ b/src/Socket.h @@ -8,7 +8,9 @@ #pragma once -//Include platform specific datatypes, header files, defines and constants: +#include + +// Include platform specific datatypes, header files, defines and constants: #if defined TARGET_WINDOWS #define WIN32_LEAN_AND_MEAN // Enable LEAN_AND_MEAN support #pragma warning(disable : 4005) // Disable "warning C4005: '_WINSOCKAPI_' : macro redefinition" @@ -64,6 +66,8 @@ typedef sockaddr_in SOCKADDR_IN; #define MAXCONNECTIONS 1 ///< Maximum number of pending connections before "Connection refused" #define MAXRECV 1500 ///< Maximum packet size +class Pvr2Wmc; + enum SocketFamily { #ifdef CONFIG_SOCKET_IPV6 @@ -102,7 +106,7 @@ enum SocketProtocol #endif }; -class Socket +class ATTRIBUTE_HIDDEN Socket { public: /*! @@ -112,19 +116,19 @@ class Socket * If the socket cannot be created, an exception is thrown. * * \param family Socket family (IPv4 or IPv6) - * \param domain The domain parameter specifies a communications domain within which communication will take place; - * this selects the protocol family which should be used. - * \param type base type and protocol family of the socket. - * \param protocol specific protocol to apply. + * \param domain The domain parameter specifies a communications domain within which communication + * will take place; this selects the protocol family which should be used. \param type base type + * and protocol family of the socket. \param protocol specific protocol to apply. */ - Socket(const enum SocketFamily family, + Socket(Pvr2Wmc& client, + const enum SocketFamily family, const enum SocketDomain domain, const enum SocketType type, const enum SocketProtocol protocol = tcp); - Socket(void); + Socket(Pvr2Wmc& client); virtual ~Socket(); - //Socket settings + // Socket settings /*! * Socket setFamily @@ -177,9 +181,9 @@ class Socket /*! * Socket bind */ - //bool bind ( const unsigned short port ); - //bool listen() const; - //bool accept ( Socket& socket ) const; + // bool bind ( const unsigned short port ); + // bool listen() const; + // bool accept ( Socket& socket ) const; // Client initialization bool connect(const std::string& host, const unsigned short port); @@ -222,8 +226,8 @@ class Socket #ifdef TARGET_WINDOWS WSADATA _wsaData; ///< Windows Socket data - static int - win_usage_count; ///< Internal Windows usage counter used to prevent a global WSACleanup when more than one Socket object is used + static int win_usage_count; ///< Internal Windows usage counter used to prevent a global + ///< WSACleanup when more than one Socket object is used #endif void errormessage(int errornum, const char* functionname = NULL) const; @@ -233,6 +237,7 @@ class Socket // client interface private: + Pvr2Wmc& _client; std::string _serverName; std::string _clientName; int _port; diff --git a/src/addon.cpp b/src/addon.cpp new file mode 100644 index 0000000..10ce617 --- /dev/null +++ b/src/addon.cpp @@ -0,0 +1,67 @@ +/* + * Copyright (C) 2005-2020 Team Kodi + * https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSE.md for more information. + */ + +#include "addon.h" + +#include "pvr2wmc.h" + +ADDON_STATUS CPvr2WmcAddon::CreateInstance(int instanceType, + const std::string& instanceID, + KODI_HANDLE instance, + const std::string& version, + KODI_HANDLE& addonInstance) +{ + ADDON_STATUS status = ADDON_STATUS_UNKNOWN; + + if (instanceType == ADDON_INSTANCE_PVR) + { + kodi::Log(ADDON_LOG_DEBUG, "%s - Creating the PVR-WMC add-on instance", __func__); + + _settings.Load(); + + Pvr2Wmc* client = new Pvr2Wmc(*this, instance, version); // create interface to ServerWMC + if (client->IsServerDown()) // check if server is down, if it is shut her down + { + status = ADDON_STATUS_LOST_CONNECTION; + } + else + { + status = ADDON_STATUS_OK; + } + + addonInstance = client; + _usedInstances.emplace(std::make_pair(instanceID, client)); + } + + return status; +} + +void CPvr2WmcAddon::DestroyInstance(int instanceType, + const std::string& instanceID, + KODI_HANDLE addonInstance) +{ + if (instanceType == ADDON_INSTANCE_PVR) + { + kodi::Log(ADDON_LOG_DEBUG, "%s - Destoying the PVR-WMC add-on instance", __func__); + + const auto& it = _usedInstances.find(instanceID); + if (it != _usedInstances.end()) + { + it->second->UnLoading(); + _usedInstances.erase(it); + } + } +} + +ADDON_STATUS CPvr2WmcAddon::SetSetting(const std::string& settingName, + const kodi::CSettingValue& settingValue) +{ + return _settings.SetSetting(settingName, settingValue); +} + +ADDONCREATOR(CPvr2WmcAddon) diff --git a/src/addon.h b/src/addon.h new file mode 100644 index 0000000..3a61979 --- /dev/null +++ b/src/addon.h @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2005-2020 Team Kodi + * https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSE.md for more information. + */ + +#pragma once + +#include "settings.h" + +#include +#include + +class Pvr2Wmc; + +class ATTRIBUTE_HIDDEN CPvr2WmcAddon : public kodi::addon::CAddonBase +{ +public: + CPvr2WmcAddon() = default; + + ADDON_STATUS CreateInstance(int instanceType, + const std::string& instanceID, + KODI_HANDLE instance, + const std::string& version, + KODI_HANDLE& addonInstance) override; + void DestroyInstance(int instanceType, + const std::string& instanceID, + KODI_HANDLE addonInstance) override; + + ADDON_STATUS SetSetting(const std::string& settingName, + const kodi::CSettingValue& settingValue) override; + CSettings& GetSettings() { return _settings; } + +private: + CSettings _settings; + std::unordered_map _usedInstances; +}; diff --git a/src/client.cpp b/src/client.cpp deleted file mode 100644 index b0e5922..0000000 --- a/src/client.cpp +++ /dev/null @@ -1,687 +0,0 @@ -/* - * Copyright (C) 2005-2020 Team Kodi - * https://kodi.tv - * - * SPDX-License-Identifier: GPL-2.0-or-later - * See LICENSE.md for more information. - */ - -#include "client.h" - -#include "pvr2wmc.h" -#include "utilities.h" - -#include "kodi/xbmc_pvr_dll.h" -#include "p8-platform/util/util.h" - -using namespace ADDON; - - -#define DEFAULT_PORT 9080 -#define DEFAULT_WAKEONLAN_ENABLE false -#define DEFAULT_SIGNAL_ENABLE false -#define DEFAULT_SIGNAL_THROTTLE 10 -#define DEFAULT_MULTI_RESUME true - -Pvr2Wmc* _wmc = NULL; -bool _bCreated = false; -ADDON_STATUS _CurStatus = ADDON_STATUS_UNKNOWN; -bool _bIsPlaying = false; -PVR_CHANNEL _currentChannel; -PVR_MENUHOOK* menuHook = NULL; -bool m_bRecordingPlayback = false; - -std::string g_strServerName; // the name of the server to connect to -std::string g_strClientName; // the name of the computer running addon -int g_port; -bool g_bWakeOnLAN; // whether to send wake on LAN to server -std::string g_strServerMAC; // MAC address of server -bool g_bSignalEnable; -int g_signalThrottle; -bool g_bEnableMultiResume; -std::string g_clientOS; // OS of client, passed to server - -backend_status g_BackendOnline; // whether the backend is online - -/* User adjustable settings are saved here. -* Default values are defined inside client.h -* and exported to the other source files. -*/ -std::string g_strUserPath = ""; -std::string g_strClientPath = ""; -std::string g_AddonDataCustom = ""; // location of custom addondata settings file - -CHelper_libXBMC_addon* XBMC = NULL; -CHelper_libXBMC_pvr* PVR = NULL; - -#define LOCALHOST "127.0.0.1" - -extern "C" -{ - - void ADDON_ReadSettings(void) - { - char buffer[512]; - - if (!XBMC) - return; - - g_strServerName = LOCALHOST; - g_strServerMAC = ""; - g_bWakeOnLAN = false; - g_port = DEFAULT_PORT; - g_bSignalEnable = DEFAULT_SIGNAL_ENABLE; - g_signalThrottle = DEFAULT_SIGNAL_THROTTLE; - g_bEnableMultiResume = DEFAULT_MULTI_RESUME; - - /* Read setting "port" from settings.xml */ - if (!XBMC->GetSetting("port", &g_port)) - { - XBMC->Log(LOG_ERROR, "Couldn't get 'port' setting, using '%i'", DEFAULT_PORT); - } - - if (XBMC->GetSetting("host", &buffer)) - { - g_strServerName = buffer; - XBMC->Log(LOG_DEBUG, "Settings: host='%s', port=%i", g_strServerName.c_str(), g_port); - } - else - { - XBMC->Log(LOG_ERROR, "Couldn't get 'host' setting, using '127.0.0.1'"); - } - - if (!XBMC->GetSetting("wake_on_lan", &g_bWakeOnLAN)) - { - XBMC->Log(LOG_ERROR, "Couldn't get 'wake_on_lan' setting, using '%s'", - DEFAULT_WAKEONLAN_ENABLE); - } - - std::string fileContent; - if (ReadFileContents(g_AddonDataCustom, fileContent)) - { - g_strServerMAC = fileContent; - XBMC->Log(LOG_ERROR, "Using ServerWMC MAC address from custom addondata '%s'", - g_strServerMAC.c_str()); - } - else - { - XBMC->Log(LOG_ERROR, - "Couldn't get ServerWMC MAC address from custom addondata, using empty value"); - } - - if (!XBMC->GetSetting("signal", &g_bSignalEnable)) - { - XBMC->Log(LOG_ERROR, "Couldn't get 'signal' setting, using '%s'", DEFAULT_SIGNAL_ENABLE); - } - - if (!XBMC->GetSetting("signal_throttle", &g_signalThrottle)) - { - XBMC->Log(LOG_ERROR, "Couldn't get 'signal_throttle' setting, using '%s'", - DEFAULT_SIGNAL_THROTTLE); - } - - if (!XBMC->GetSetting("multiResume", &g_bEnableMultiResume)) - { - XBMC->Log(LOG_ERROR, "Couldn't get 'multiResume' setting, using '%s'", DEFAULT_MULTI_RESUME); - } - - - // get the name of the computer client is running on - gethostname(buffer, 50); - g_strClientName = buffer; // send this computers name to server - -#ifdef TARGET_WINDOWS - g_clientOS = "windows"; // set to the client OS name -#elif defined TARGET_LINUX - g_clientOS = "linux"; // set to the client OS name -#elif defined TARGET_DARWIN - g_clientOS = "darwin"; // set to the client OS name -#elif defined TARGET_FREEBSD - g_clientOS = "freeBSD"; // set to the client OS name -#else - g_clientOS = ""; // set blank client OS name -#endif - } - - // create this addon (called by xbmc) - ADDON_STATUS ADDON_Create(void* hdl, const char* globalApiVersion, void* props) - { - if (!hdl || !props) - return ADDON_STATUS_UNKNOWN; - - AddonProperties_PVR* pvrprops = (AddonProperties_PVR*)props; - - // register the addon - XBMC = new CHelper_libXBMC_addon; - - if (!XBMC->RegisterMe(hdl)) - { - SAFE_DELETE(XBMC); - return ADDON_STATUS_PERMANENT_FAILURE; - } - - // register as pvr - PVR = new CHelper_libXBMC_pvr; - if (!PVR->RegisterMe(hdl)) - { - SAFE_DELETE(PVR); - SAFE_DELETE(XBMC); - return ADDON_STATUS_PERMANENT_FAILURE; - } - - XBMC->Log(LOG_DEBUG, "%s - Creating the PVR-WMC add-on", __FUNCTION__); - - _CurStatus = ADDON_STATUS_UNKNOWN; - g_strUserPath = pvrprops->strUserPath; - g_strClientPath = pvrprops->strClientPath; - g_AddonDataCustom = g_strUserPath + "ServerMACAddr.txt"; - - ADDON_ReadSettings(); - - _wmc = new Pvr2Wmc; // create interface to ServerWMC - if (_wmc->IsServerDown()) // check if server is down, if it is shut her down - { - SAFE_DELETE(_wmc); - SAFE_DELETE(PVR); - SAFE_DELETE(XBMC); - _CurStatus = ADDON_STATUS_LOST_CONNECTION; - } - else - { - _bCreated = true; - _CurStatus = ADDON_STATUS_OK; - } - return _CurStatus; - } - - // get status of addon's interface to wmc server - ADDON_STATUS ADDON_GetStatus() - { - // check whether we're still connected - if (_CurStatus == ADDON_STATUS_OK) - { - if (_wmc == NULL) - _CurStatus = ADDON_STATUS_LOST_CONNECTION; - else if (_wmc->IsServerDown()) - _CurStatus = ADDON_STATUS_LOST_CONNECTION; - } - - return _CurStatus; - } - - void ADDON_Destroy() - { - if (_wmc) - _wmc->UnLoading(); - SAFE_DELETE(PVR); - _bCreated = false; - _CurStatus = ADDON_STATUS_UNKNOWN; - } - - // Called everytime a setting is changed by the user and to inform AddOn about - // new setting and to do required stuff to apply it. - ADDON_STATUS ADDON_SetSetting(const char* settingName, const void* settingValue) - { - if (!XBMC) - return ADDON_STATUS_OK; - - std::string sName = settingName; - - if (sName == "host") - { - std::string oldName = g_strServerName; - g_strServerName = (const char*)settingValue; - - XBMC->Log(LOG_INFO, "Setting 'host' changed from %s to %s", g_strServerName.c_str(), - (const char*)settingValue); - if (oldName != g_strServerName) - return ADDON_STATUS_NEED_RESTART; - } - - return ADDON_STATUS_OK; - } - - /*********************************************************** - * PVR Client AddOn specific public library functions - ***********************************************************/ - - void OnSystemSleep() {} - - void OnSystemWake() {} - - void OnPowerSavingActivated() {} - - void OnPowerSavingDeactivated() {} - - PVR_ERROR GetCapabilities(PVR_ADDON_CAPABILITIES* pCapabilities) - { - pCapabilities->bSupportsEPG = true; - pCapabilities->bSupportsTV = true; - pCapabilities->bSupportsRadio = true; - pCapabilities->bSupportsRecordings = true; - pCapabilities->bSupportsRecordingsUndelete = false; - pCapabilities->bSupportsTimers = true; - pCapabilities->bSupportsChannelGroups = true; - pCapabilities->bSupportsChannelScan = false; - pCapabilities->bHandlesInputStream = true; - pCapabilities->bHandlesDemuxing = false; - pCapabilities->bSupportsRecordingPlayCount = true; - pCapabilities->bSupportsLastPlayedPosition = g_bEnableMultiResume; - pCapabilities->bSupportsRecordingEdl = true; - pCapabilities->bSupportsRecordingsRename = true; - pCapabilities->bSupportsRecordingsLifetimeChange = false; - pCapabilities->bSupportsDescrambleInfo = false; - - return PVR_ERROR_NO_ERROR; - } - - const char* GetBackendName(void) - { - static const char* strBackendName = "ServerWMC"; - return strBackendName; - } - - const char* GetBackendVersion(void) - { - if (_wmc) - return _wmc->GetBackendVersion(); - else - return "0.0"; - } - - const char* GetConnectionString(void) - { - static std::string strConnectionString; - strConnectionString = string_format("%s:%u", g_strServerName.c_str(), g_port); - return strConnectionString.c_str(); - } - - const char* GetBackendHostname(void) { return g_strServerName.c_str(); } - - PVR_ERROR GetDriveSpace(long long* iTotal, long long* iUsed) - { - if (_wmc) - return _wmc->GetDriveSpace(iTotal, iUsed); - - return PVR_ERROR_SERVER_ERROR; - } - - PVR_ERROR GetEPGForChannel(ADDON_HANDLE handle, int iChannelUid, time_t iStart, time_t iEnd) - { - if (_wmc) - return _wmc->GetEPGForChannel(handle, iChannelUid, iStart, iEnd); - - return PVR_ERROR_SERVER_ERROR; - } - - - PVR_ERROR GetSignalStatus(int channelUid, PVR_SIGNAL_STATUS* signalStatus) - { - if (_wmc) - return _wmc->SignalStatus(signalStatus); - - return PVR_ERROR_NO_ERROR; - } - - // channel functions ----------------------------- - - int GetChannelsAmount(void) - { - if (_wmc) - return _wmc->GetChannelsAmount(); - - return -1; - } - - PVR_ERROR GetChannels(ADDON_HANDLE handle, bool bRadio) - { - if (_wmc) - return _wmc->GetChannels(handle, bRadio); - - return PVR_ERROR_SERVER_ERROR; - } - - int GetChannelGroupsAmount(void) - { - if (_wmc) - return _wmc->GetChannelGroupsAmount(); - - return -1; - } - - PVR_ERROR GetChannelGroups(ADDON_HANDLE handle, bool bRadio) - { - if (_wmc) - return _wmc->GetChannelGroups(handle, bRadio); - - return PVR_ERROR_SERVER_ERROR; - } - - PVR_ERROR GetChannelGroupMembers(ADDON_HANDLE handle, const PVR_CHANNEL_GROUP& group) - { - if (_wmc) - return _wmc->GetChannelGroupMembers(handle, group); - - return PVR_ERROR_SERVER_ERROR; - } - - - // timer functions ----------------------- - - PVR_ERROR GetTimerTypes(PVR_TIMER_TYPE types[], int* size) - { - if (_wmc) - return _wmc->GetTimerTypes(types, size); - - return PVR_ERROR_NOT_IMPLEMENTED; - } - - int GetTimersAmount(void) - { - if (_wmc) - return _wmc->GetTimersAmount(); - - return PVR_ERROR_SERVER_ERROR; - } - - PVR_ERROR GetTimers(ADDON_HANDLE handle) - { - if (_wmc) - return _wmc->GetTimers(handle); - - return PVR_ERROR_SERVER_ERROR; - } - - PVR_ERROR AddTimer(const PVR_TIMER& timer) - { - if (_wmc) - return _wmc->AddTimer(timer); - - return PVR_ERROR_NO_ERROR; - } - - PVR_ERROR UpdateTimer(const PVR_TIMER& timer) - { - if (_wmc) - return _wmc->AddTimer(timer); - return PVR_ERROR_NO_ERROR; - } - - PVR_ERROR DeleteTimer(const PVR_TIMER& timer, bool bForceDelete) - { - if (_wmc) - return _wmc->DeleteTimer(timer, bForceDelete); - return PVR_ERROR_NO_ERROR; - } - - // recording file functions - PVR_ERROR GetRecordings(ADDON_HANDLE handle, bool deleted) - { - if (!deleted && _wmc) - return _wmc->GetRecordings(handle); - return PVR_ERROR_NO_ERROR; - } - - int GetRecordingsAmount(bool deleted) - { - if (!deleted && _wmc) - return _wmc->GetRecordingsAmount(); - - return -1; - } - - PVR_ERROR RenameRecording(const PVR_RECORDING& recording) - { - if (_wmc) - return _wmc->RenameRecording(recording); - return PVR_ERROR_NOT_IMPLEMENTED; - } - - - // live stream functions --------------------------- - - bool OpenLiveStream(const PVR_CHANNEL& channel) - { - if (_wmc) - { - if (_wmc->OpenLiveStream(channel)) - { - _bIsPlaying = true; - return true; - } - } - return false; - } - - int ReadLiveStream(unsigned char* pBuffer, unsigned int iBufferSize) - { - if (_wmc) - { - return _wmc->ReadLiveStream(pBuffer, iBufferSize); - } - return -1; - } - - void CloseLiveStream(void) - { - _bIsPlaying = false; - if (_wmc) - { - _wmc->CloseLiveStream(); - } - } - - long long SeekLiveStream(long long iPosition, int iWhence /* = SEEK_SET */) - { - if (_wmc) - return _wmc->SeekLiveStream(iPosition, iWhence); - else - return -1; - } - - long long PositionLiveStream(void) - { - if (_wmc) - return _wmc->PositionLiveStream(); - else - return -1; - } - - long long LengthLiveStream(void) - { - if (_wmc) - return _wmc->LengthLiveStream(); - else - return -1; - } - - void PauseStream(bool bPaused) - { - if (_wmc) - return _wmc->PauseStream(bPaused); - } - - bool CanPauseStream(void) { return true; } - - bool CanSeekStream(void) { return true; } - - // recorded stream functions ----------------------------- - - bool OpenRecordedStream(const PVR_RECORDING& recording) - { - if (_wmc) - { - CloseLiveStream(); - if (_wmc->OpenRecordedStream(recording)) - { - _bIsPlaying = true; - m_bRecordingPlayback = true; - return true; - } - } - return false; - } - - int ReadRecordedStream(unsigned char* pBuffer, unsigned int iBufferSize) - { - if (_wmc) - { - return _wmc->ReadLiveStream(pBuffer, iBufferSize); - } - return -1; - } - - void CloseRecordedStream(void) - { - _bIsPlaying = false; - m_bRecordingPlayback = false; - if (_wmc) - { - _wmc->CloseLiveStream(); - } - } - - long long SeekRecordedStream(long long iPosition, int iWhence /* = SEEK_SET */) - { - if (_wmc) - return _wmc->SeekLiveStream(iPosition, iWhence); - else - return -1; - } - - long long PositionRecordedStream(void) - { - if (_wmc) - return _wmc->PositionLiveStream(); - else - return -1; - } - - long long LengthRecordedStream(void) - { - if (_wmc) - return _wmc->LengthLiveStream(); - else - return -1; - } - - // recorded file functions ----------------- - - PVR_ERROR DeleteRecording(const PVR_RECORDING& recording) - { - if (_wmc) - return _wmc->DeleteRecording(recording); - return PVR_ERROR_NO_ERROR; - } - - - PVR_ERROR SetRecordingLastPlayedPosition(const PVR_RECORDING& recording, int lastplayedposition) - { - if (_wmc && g_bEnableMultiResume) - return _wmc->SetRecordingLastPlayedPosition(recording, lastplayedposition); - return PVR_ERROR_NOT_IMPLEMENTED; - } - - int GetRecordingLastPlayedPosition(const PVR_RECORDING& recording) - { - if (_wmc && g_bEnableMultiResume) - return _wmc->GetRecordingLastPlayedPosition(recording); - return -1; - } - - PVR_ERROR SetRecordingPlayCount(const PVR_RECORDING& recording, int count) - { - if (_wmc && g_bEnableMultiResume) - return _wmc->SetRecordingPlayCount(recording, count); - return PVR_ERROR_NOT_IMPLEMENTED; - } - - // time shift functions ----------------- - - bool IsTimeshifting(void) - { - if (_wmc) - return _wmc->IsTimeShifting(); - else - return false; - } - - PVR_ERROR GetStreamTimes(PVR_STREAM_TIMES* strTimes) - { - if (_wmc) - return _wmc->GetStreamTimes(strTimes); - else - return PVR_ERROR_SERVER_ERROR; - } - - - PVR_ERROR GetRecordingEdl(const PVR_RECORDING& recording, PVR_EDL_ENTRY edlEntries[], int* count) - { - if (_wmc) - return _wmc->GetRecordingEdl(recording, edlEntries, count); - else - return PVR_ERROR_SERVER_ERROR; - } - - bool IsRealTimeStream() { return !m_bRecordingPlayback; } - - /** UNUSED API FUNCTIONS */ - PVR_ERROR OpenDialogChannelScan(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 OpenDialogChannelSettings(const PVR_CHANNEL& channel) - { - return PVR_ERROR_NOT_IMPLEMENTED; - } - PVR_ERROR OpenDialogChannelAdd(const PVR_CHANNEL& channel) { return PVR_ERROR_NOT_IMPLEMENTED; } - void DemuxReset(void) {} - void DemuxFlush(void) {} - void DemuxAbort(void) {} - DemuxPacket* DemuxRead(void) { return NULL; } - void FillBuffer(bool mode) {} - bool SeekTime(double, bool, double*) { return false; } - void SetSpeed(int){}; - PVR_ERROR UndeleteRecording(const PVR_RECORDING& recording) { return PVR_ERROR_NOT_IMPLEMENTED; } - PVR_ERROR DeleteAllRecordingsFromTrash() { return PVR_ERROR_NOT_IMPLEMENTED; } - PVR_ERROR GetRecordingSize(const PVR_RECORDING* recording, int64_t* sizeInBytes) - { - return PVR_ERROR_NOT_IMPLEMENTED; - } - PVR_ERROR SetEPGTimeFrame(int) { return PVR_ERROR_NOT_IMPLEMENTED; } - PVR_ERROR GetDescrambleInfo(int channelUid, PVR_DESCRAMBLE_INFO*) - { - return PVR_ERROR_NOT_IMPLEMENTED; - } - PVR_ERROR SetRecordingLifetime(const PVR_RECORDING*) { return PVR_ERROR_NOT_IMPLEMENTED; } - PVR_ERROR CallMenuHook(const PVR_MENUHOOK& menuhook, const PVR_MENUHOOK_DATA& item) - { - return PVR_ERROR_NOT_IMPLEMENTED; - } - - PVR_ERROR IsEPGTagPlayable(const EPG_TAG*, bool*) { return PVR_ERROR_NOT_IMPLEMENTED; } - PVR_ERROR IsEPGTagRecordable(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; - } - - PVR_ERROR GetStreamProperties(PVR_STREAM_PROPERTIES* pProperties) - { - return PVR_ERROR_NOT_IMPLEMENTED; - } - PVR_ERROR GetChannelStreamProperties(const PVR_CHANNEL* channel, - PVR_NAMED_VALUE* properties, - unsigned int* iPropertiesCount) - { - return PVR_ERROR_NOT_IMPLEMENTED; - } - PVR_ERROR GetRecordingStreamProperties(const PVR_RECORDING*, PVR_NAMED_VALUE*, unsigned int*) - { - return PVR_ERROR_NOT_IMPLEMENTED; - } - PVR_ERROR GetStreamReadChunkSize(int* chunksize) { return PVR_ERROR_NOT_IMPLEMENTED; } -} diff --git a/src/client.h b/src/client.h deleted file mode 100644 index b823f30..0000000 --- a/src/client.h +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright (C) 2005-2020 Team Kodi - * https://kodi.tv - * - * SPDX-License-Identifier: GPL-2.0-or-later - * See LICENSE.md for more information. - */ - -#pragma once - -#include "kodi/libXBMC_addon.h" -#include "kodi/libXBMC_pvr.h" - -#include -#include - -#define string_format StringUtils::Format -#define split StringUtils::Split - -extern bool m_bCreated; -extern std::string g_strUserPath; -extern std::string g_strClientPath; -extern std::string g_strServerName; // the name of the server to connect to -extern std::string g_strClientName; // the name of the computer running addon -extern std::string g_clientOS; // OS of client, passed to server -extern int g_port; -extern bool g_bSignalEnable; -extern int g_signalThrottle; -extern bool g_bEnableMultiResume; -extern ADDON::CHelper_libXBMC_addon* XBMC; -extern CHelper_libXBMC_pvr* PVR; -extern std::string g_strServerMAC; -extern bool g_bWakeOnLAN; -extern std::string g_AddonDataCustom; - -enum backend_status -{ - BACKEND_UNKNOWN, - BACKEND_DOWN, - BACKEND_UP -}; -extern backend_status g_BackendOnline; diff --git a/src/pvr2wmc.cpp b/src/pvr2wmc.cpp index 51e2d5f..8729eae 100644 --- a/src/pvr2wmc.cpp +++ b/src/pvr2wmc.cpp @@ -10,69 +10,27 @@ #include "utilities.h" -#include "p8-platform/util/timeutils.h" +#include +#include -#include +long Pvr2Wmc::_buffTimesCnt; // filter how often we do buffer status reads +long Pvr2Wmc::_buffTimeFILTER; -using namespace std; -using namespace ADDON; -using namespace P8PLATFORM; - -#define null NULL - -#define STRCPY(dest, src) strncpy(dest, src, sizeof(dest) - 1); -#define FOREACH(ss, vv) \ - for (std::vector::iterator ss = vv.begin(); ss != vv.end(); ++ss) - -int64_t _lastRecordingUpdateTime; // the time of the last recording display update - -long _buffTimesCnt; // filter how often we do buffer status reads -long _buffTimeFILTER; - -Pvr2Wmc::Pvr2Wmc(void) -{ - _socketClient.SetServerName(g_strServerName); - _socketClient.SetClientName(g_strClientName); - _socketClient.SetServerPort(g_port); - - _diskTotal = 0; - _diskUsed = 0; - - _signalStatusCount = 0; // for signal status display - _discardSignalStatus = false; - - _streamFile = 0; // handle to a streamed file - _streamFileName = ""; - _readCnt = 0; - - _initialStreamResetCnt = - 0; // used to count how many times we reset the stream position (due to 2 pass demuxer) - _initialStreamPosition = - 0; // used to set an initial position (multiple clients watching the same live tv buffer) - - _lastRecordingUpdateTime = 0; // time of last recording display update - _lostStream = false; // set to true if stream is lost - _lastStreamSize = 0; // the last value found for the stream file size - _isStreamFileGrowing = false; // true if stream file is growing (server determines) - _streamWTV = true; // by default, assume we are streaming Wtv files (not ts files) - - _defaultPriority = WMC_PRIORITY_NORMAL; - _defaultLiftetime = WMC_LIFETIME_ELIGIBLE; - _defaultLimit = WMC_LIMIT_ASMANY; - _defaultShowType = WMC_SHOWTYPE_ANY; -} - -Pvr2Wmc::~Pvr2Wmc(void) +Pvr2Wmc::Pvr2Wmc(CPvr2WmcAddon& addon, KODI_HANDLE instance, const std::string& kodiVersion) + : kodi::addon::CInstancePVRClient(instance, kodiVersion), _socketClient(*this), _addon(addon) { + _socketClient.SetServerName(_addon.GetSettings().GetServerName()); + _socketClient.SetClientName(_addon.GetSettings().GetClientName()); + _socketClient.SetServerPort(_addon.GetSettings().GetPort()); } bool Pvr2Wmc::IsServerDown() { std::string request; - request = string_format("GetServiceStatus|%s|%s", STR(WMC_VERSION), - g_clientOS.c_str()); + request = Utils::Format("GetServiceStatus|%s|%s", STR(WMC_VERSION), + _addon.GetSettings().GetClientOS().c_str()); _socketClient.SetTimeOut(10); // set a timout interval for checking if server is down - vector results = _socketClient.GetVector(request, true); // get serverstatus + std::vector results = _socketClient.GetVector(request, true); // get serverstatus bool isServerDown = (results[0] != "True"); // true if server is down // GetServiceStatus may return any updates requested by server @@ -89,178 +47,189 @@ void Pvr2Wmc::UnLoading() _socketClient.GetBool("ClientGoingDown", true, false); // returns true if server is up } -const char* Pvr2Wmc::GetBackendVersion(void) +PVR_ERROR Pvr2Wmc::GetCapabilities(kodi::addon::PVRCapabilities& capabilities) +{ + capabilities.SetSupportsEPG(true); + capabilities.SetSupportsTV(true); + capabilities.SetSupportsRadio(true); + capabilities.SetSupportsRecordings(true); + capabilities.SetSupportsRecordingsUndelete(false); + capabilities.SetSupportsTimers(true); + capabilities.SetSupportsChannelGroups(true); + capabilities.SetSupportsChannelScan(false); + capabilities.SetHandlesInputStream(true); + capabilities.SetHandlesDemuxing(false); + capabilities.SetSupportsRecordingPlayCount(true); + capabilities.SetSupportsLastPlayedPosition(_addon.GetSettings().GetEnableMultiResume()); + capabilities.SetSupportsRecordingEdl(true); + capabilities.SetSupportsRecordingsRename(true); + capabilities.SetSupportsRecordingsLifetimeChange(false); + capabilities.SetSupportsDescrambleInfo(false); + + return PVR_ERROR_NO_ERROR; +} + +PVR_ERROR Pvr2Wmc::GetBackendName(std::string& name) +{ + name = "ServerWMC"; + return PVR_ERROR_NO_ERROR; +} + +PVR_ERROR Pvr2Wmc::GetBackendVersion(std::string& version) { if (!IsServerDown()) { - static std::string strVersion = "0.0"; - // Send client's time (in UTC) to backend - time_t now = time(NULL); + time_t now = time(nullptr); char datestr[32]; strftime(datestr, 32, "%Y-%m-%d %H:%M:%S", gmtime(&now)); - // Also send this client's setting for backend servername (so server knows how it is being accessed) + // Also send this client's setting for backend servername (so server knows how it is being + // accessed) std::string request; - request = string_format("GetServerVersion|%s|%s", datestr, g_strServerName.c_str()); - vector results = _socketClient.GetVector(request, true); + request = Utils::Format("GetServerVersion|%s|%s", datestr, + _addon.GetSettings().GetServerName().c_str()); + std::vector results = _socketClient.GetVector(request, true); if (results.size() > 0) { - strVersion = std::string(results[0]); + version = std::string(results[0]); } if (results.size() > 1) { - _serverBuild = atoi(results[1].c_str()); // get server build number for feature checking + _serverBuild = std::stoi(results[1]); // get server build number for feature checking } // check if recorded tv folder is accessible from client if (results.size() > 2 && results[2] != "") // if server sends empty string, skip check { - if (!XBMC->DirectoryExists(results[2].c_str())) + std::vector items; + if (!kodi::vfs::DirectoryExists(results[2])) { - XBMC->Log(LOG_ERROR, "Recorded tv '%s' does not exist", results[2].c_str()); - std::string infoStr = XBMC->GetLocalizedString(30017); - XBMC->QueueNotification(QUEUE_ERROR, infoStr.c_str()); + kodi::Log(ADDON_LOG_ERROR, "Recorded tv '%s' does not exist", results[2].c_str()); + std::string infoStr = kodi::GetLocalizedString(30017); + kodi::QueueNotification(QUEUE_ERROR, "", infoStr); } - else if (!XBMC->CanOpenDirectory(results[2].c_str())) + else if (!kodi::vfs::GetDirectory(results[2], "", items)) { - XBMC->Log(LOG_ERROR, "Recorded tv '%s' count not be opened", results[2].c_str()); - std::string infoStr = XBMC->GetLocalizedString(30018); - XBMC->QueueNotification(QUEUE_ERROR, infoStr.c_str()); + kodi::Log(ADDON_LOG_ERROR, "Recorded tv '%s' count not be opened", results[2].c_str()); + std::string infoStr = kodi::GetLocalizedString(30018); + kodi::QueueNotification(QUEUE_ERROR, "", infoStr); } } // check if server returned it's MAC address - if (results.size() > 3 && results[3] != "" && results[3] != g_strServerMAC) + if (results.size() > 3 && results[3] != "" && results[3] != _addon.GetSettings().GetServerMAC()) { - XBMC->Log(LOG_INFO, "Setting ServerWMC Server MAC Address to '%s'", results[3].c_str()); - g_strServerMAC = results[3]; + kodi::Log(ADDON_LOG_INFO, "Setting ServerWMC Server MAC Address to '%s'", results[3].c_str()); + _addon.GetSettings().SetServerMAC(results[3]); // Attempt to save MAC address to custom addon data - WriteFileContents(g_AddonDataCustom, g_strServerMAC); + Utils::WriteFileContents(kodi::GetBaseUserPath("ServerMACAddr.txt"), + _addon.GetSettings().GetServerMAC()); } - return strVersion.c_str(); // return server version to caller + return PVR_ERROR_NO_ERROR; } - return "Not accessible"; // server version check failed + + version = "Not accessible"; // server version check failed + return PVR_ERROR_SERVER_ERROR; +} + +PVR_ERROR Pvr2Wmc::GetBackendHostname(std::string& hostname) +{ + hostname = _addon.GetSettings().GetServerName(); + return PVR_ERROR_NO_ERROR; +} + +PVR_ERROR Pvr2Wmc::GetConnectionString(std::string& connection) +{ + connection = Utils::Format("%s:%u", _addon.GetSettings().GetServerName().c_str(), + _addon.GetSettings().GetPort()); + return PVR_ERROR_NO_ERROR; +} + +PVR_ERROR Pvr2Wmc::GetDriveSpace(uint64_t& total, uint64_t& used) +{ + total = _diskTotal; + used = _diskUsed; + + return PVR_ERROR_NO_ERROR; } namespace { -struct TimerType : PVR_TIMER_TYPE +struct TimerType : kodi::addon::PVRTimerType { TimerType(unsigned int id, unsigned int attributes, const std::string& description, - const std::vector>& priorityValues, + const std::vector& priorityValues, int priorityDefault, - const std::vector>& lifetimeValues, + const std::vector& lifetimeValues, int lifetimeDefault, - const std::vector>& maxRecordingsValues, + const std::vector& maxRecordingsValues, int maxRecordingsDefault, - const std::vector>& dupEpisodesValues, + const std::vector& dupEpisodesValues, int dupEpisodesDefault) { - memset(this, 0, sizeof(PVR_TIMER_TYPE)); - - iId = id; - iAttributes = attributes; - iPrioritiesSize = priorityValues.size(); - iPrioritiesDefault = priorityDefault; - iLifetimesSize = lifetimeValues.size(); - iLifetimesDefault = lifetimeDefault; - iMaxRecordingsSize = maxRecordingsValues.size(); - iMaxRecordingsDefault = maxRecordingsDefault; - iPreventDuplicateEpisodesSize = dupEpisodesValues.size(); - iPreventDuplicateEpisodesDefault = dupEpisodesDefault; - strncpy(strDescription, description.c_str(), sizeof(strDescription) - 1); - - int i = 0; - for (auto it = priorityValues.begin(); it != priorityValues.end(); ++it, ++i) - { - priorities[i].iValue = it->first; - strncpy(priorities[i].strDescription, it->second.c_str(), - sizeof(priorities[i].strDescription) - 1); - } - - i = 0; - for (auto it = lifetimeValues.begin(); it != lifetimeValues.end(); ++it, ++i) - { - lifetimes[i].iValue = it->first; - strncpy(lifetimes[i].strDescription, it->second.c_str(), - sizeof(lifetimes[i].strDescription) - 1); - } - - i = 0; - for (auto it = maxRecordingsValues.begin(); it != maxRecordingsValues.end(); ++it, ++i) - { - maxRecordings[i].iValue = it->first; - strncpy(maxRecordings[i].strDescription, it->second.c_str(), - sizeof(maxRecordings[i].strDescription) - 1); - } - - i = 0; - for (auto it = dupEpisodesValues.begin(); it != dupEpisodesValues.end(); ++it, ++i) - { - preventDuplicateEpisodes[i].iValue = it->first; - strncpy(preventDuplicateEpisodes[i].strDescription, it->second.c_str(), - sizeof(preventDuplicateEpisodes[i].strDescription) - 1); - } + SetId(id); + SetAttributes(attributes); + SetPriorities(priorityValues, priorityDefault); + SetLifetimes(lifetimeValues, lifetimeDefault); + SetMaxRecordings(maxRecordingsValues, maxRecordingsDefault); + SetPreventDuplicateEpisodes(dupEpisodesValues, dupEpisodesDefault); + SetDescription(description); } }; } // unnamed namespace -PVR_ERROR Pvr2Wmc::GetTimerTypes(PVR_TIMER_TYPE types[], int* size) +PVR_ERROR Pvr2Wmc::GetTimerTypes(std::vector& types) { /* PVR_Timer.iPriority values and presentation.*/ - static std::vector> priorityValues; + static std::vector priorityValues; if (priorityValues.size() == 0) { - priorityValues.push_back(std::make_pair(WMC_PRIORITY_NORMAL, XBMC->GetLocalizedString(30140))); - priorityValues.push_back(std::make_pair(WMC_PRIORITY_HIGH, XBMC->GetLocalizedString(30141))); - priorityValues.push_back(std::make_pair(WMC_PRIORITY_LOW, XBMC->GetLocalizedString(30142))); + priorityValues.emplace_back(WMC_PRIORITY_NORMAL, kodi::GetLocalizedString(30140)); + priorityValues.emplace_back(WMC_PRIORITY_HIGH, kodi::GetLocalizedString(30141)); + priorityValues.emplace_back(WMC_PRIORITY_LOW, kodi::GetLocalizedString(30142)); } /* PVR_Timer.iLifeTime values and presentation.*/ - static std::vector> lifetimeValues; + static std::vector lifetimeValues; if (lifetimeValues.size() == 0) { - lifetimeValues.push_back(std::make_pair(WMC_LIFETIME_NOTSET, XBMC->GetLocalizedString(30160))); - lifetimeValues.push_back(std::make_pair(WMC_LIFETIME_LATEST, XBMC->GetLocalizedString(30161))); - lifetimeValues.push_back(std::make_pair(WMC_LIFETIME_WATCHED, XBMC->GetLocalizedString(30162))); - lifetimeValues.push_back( - std::make_pair(WMC_LIFETIME_ELIGIBLE, XBMC->GetLocalizedString(30163))); - lifetimeValues.push_back(std::make_pair(WMC_LIFETIME_DELETED, XBMC->GetLocalizedString(30164))); - lifetimeValues.push_back(std::make_pair(WMC_LIFETIME_ONEWEEK, XBMC->GetLocalizedString(30165))); + lifetimeValues.emplace_back(WMC_LIFETIME_NOTSET, kodi::GetLocalizedString(30160)); + lifetimeValues.emplace_back(WMC_LIFETIME_LATEST, kodi::GetLocalizedString(30161)); + lifetimeValues.emplace_back(WMC_LIFETIME_WATCHED, kodi::GetLocalizedString(30162)); + lifetimeValues.emplace_back(WMC_LIFETIME_ELIGIBLE, kodi::GetLocalizedString(30163)); + lifetimeValues.emplace_back(WMC_LIFETIME_DELETED, kodi::GetLocalizedString(30164)); + lifetimeValues.emplace_back(WMC_LIFETIME_ONEWEEK, kodi::GetLocalizedString(30165)); } /* PVR_Timer.iMaxRecordings values and presentation. */ - static std::vector> recordingLimitValues; + static std::vector recordingLimitValues; if (recordingLimitValues.size() == 0) { - recordingLimitValues.push_back( - std::make_pair(WMC_LIMIT_ASMANY, XBMC->GetLocalizedString(30170))); - recordingLimitValues.push_back(std::make_pair(WMC_LIMIT_1, XBMC->GetLocalizedString(30171))); - recordingLimitValues.push_back(std::make_pair(WMC_LIMIT_2, XBMC->GetLocalizedString(30172))); - recordingLimitValues.push_back(std::make_pair(WMC_LIMIT_3, XBMC->GetLocalizedString(30173))); - recordingLimitValues.push_back(std::make_pair(WMC_LIMIT_4, XBMC->GetLocalizedString(30174))); - recordingLimitValues.push_back(std::make_pair(WMC_LIMIT_5, XBMC->GetLocalizedString(30175))); - recordingLimitValues.push_back(std::make_pair(WMC_LIMIT_6, XBMC->GetLocalizedString(30176))); - recordingLimitValues.push_back(std::make_pair(WMC_LIMIT_7, XBMC->GetLocalizedString(30177))); - recordingLimitValues.push_back(std::make_pair(WMC_LIMIT_10, XBMC->GetLocalizedString(30178))); + recordingLimitValues.emplace_back(WMC_LIMIT_ASMANY, kodi::GetLocalizedString(30170)); + recordingLimitValues.emplace_back(WMC_LIMIT_1, kodi::GetLocalizedString(30171)); + recordingLimitValues.emplace_back(WMC_LIMIT_2, kodi::GetLocalizedString(30172)); + recordingLimitValues.emplace_back(WMC_LIMIT_3, kodi::GetLocalizedString(30173)); + recordingLimitValues.emplace_back(WMC_LIMIT_4, kodi::GetLocalizedString(30174)); + recordingLimitValues.emplace_back(WMC_LIMIT_5, kodi::GetLocalizedString(30175)); + recordingLimitValues.emplace_back(WMC_LIMIT_6, kodi::GetLocalizedString(30176)); + recordingLimitValues.emplace_back(WMC_LIMIT_7, kodi::GetLocalizedString(30177)); + recordingLimitValues.emplace_back(WMC_LIMIT_10, kodi::GetLocalizedString(30178)); } /* PVR_Timer.iPreventDuplicateEpisodes values and presentation.*/ - static std::vector> showTypeValues; + static std::vector showTypeValues; if (showTypeValues.size() == 0) { - showTypeValues.push_back( - std::make_pair(WMC_SHOWTYPE_FIRSTRUNONLY, XBMC->GetLocalizedString(30150))); - showTypeValues.push_back(std::make_pair(WMC_SHOWTYPE_ANY, XBMC->GetLocalizedString(30151))); - showTypeValues.push_back( - std::make_pair(WMC_SHOWTYPE_LIVEONLY, XBMC->GetLocalizedString(30152))); + showTypeValues.emplace_back(WMC_SHOWTYPE_FIRSTRUNONLY, kodi::GetLocalizedString(30150)); + showTypeValues.emplace_back(WMC_SHOWTYPE_ANY, kodi::GetLocalizedString(30151)); + showTypeValues.emplace_back(WMC_SHOWTYPE_LIVEONLY, kodi::GetLocalizedString(30152)); } - static std::vector> emptyList; + static std::vector emptyList; static const unsigned int TIMER_MANUAL_ATTRIBS = PVR_TIMER_TYPE_IS_MANUAL | PVR_TIMER_TYPE_SUPPORTS_CHANNELS | @@ -298,7 +267,7 @@ PVR_ERROR Pvr2Wmc::GetTimerTypes(PVR_TIMER_TYPE types[], int* size) static std::vector> timerTypes; if (timerTypes.size() == 0) { - timerTypes.push_back( + timerTypes.emplace_back( /* One-shot manual (time and channel based) */ std::unique_ptr(new TimerType( /* Type id. */ @@ -306,12 +275,12 @@ PVR_ERROR Pvr2Wmc::GetTimerTypes(PVR_TIMER_TYPE types[], int* size) /* Attributes. */ TIMER_MANUAL_ATTRIBS, /* Description. */ - XBMC->GetLocalizedString(30131), // "One time (manual)", + kodi::GetLocalizedString(30131), // "One time (manual)", /* Values definitions for attributes. */ priorityValues, _defaultPriority, lifetimeValues, _defaultLiftetime, recordingLimitValues, _defaultLimit, showTypeValues, _defaultShowType))); - timerTypes.push_back( + timerTypes.emplace_back( /* One-shot epg based */ std::unique_ptr(new TimerType( /* Type id. */ @@ -319,12 +288,12 @@ PVR_ERROR Pvr2Wmc::GetTimerTypes(PVR_TIMER_TYPE types[], int* size) /* Attributes. */ TIMER_EPG_ATTRIBS, /* Description. */ - XBMC->GetLocalizedString(30132), // "One time (guide)", + kodi::GetLocalizedString(30132), // "One time (guide)", /* Values definitions for attributes. */ priorityValues, _defaultPriority, lifetimeValues, _defaultLiftetime, recordingLimitValues, _defaultLimit, showTypeValues, _defaultShowType))); - timerTypes.push_back( + timerTypes.emplace_back( /* One shot Keyword based */ std::unique_ptr(new TimerType( /* Type id. */ @@ -332,12 +301,12 @@ PVR_ERROR Pvr2Wmc::GetTimerTypes(PVR_TIMER_TYPE types[], int* size) /* Attributes. */ TIMER_KEYWORD_ATTRIBS, /* Description. */ - XBMC->GetLocalizedString(30133), // "One time (wishlist)" + kodi::GetLocalizedString(30133), // "One time (wishlist)" /* Values definitions for attributes. */ priorityValues, _defaultPriority, lifetimeValues, _defaultLiftetime, recordingLimitValues, _defaultLimit, showTypeValues, _defaultShowType))); - timerTypes.push_back( + timerTypes.emplace_back( /* Read-only one-shot for timers generated by timerec */ std::unique_ptr(new TimerType( /* Type id. */ @@ -345,12 +314,12 @@ PVR_ERROR Pvr2Wmc::GetTimerTypes(PVR_TIMER_TYPE types[], int* size) /* Attributes. */ TIMER_MANUAL_ATTRIBS | TIMER_CHILD_ATTRIBUTES, /* Description. */ - XBMC->GetLocalizedString(30130), // "Created by Repeating Timer" + kodi::GetLocalizedString(30130), // "Created by Repeating Timer" /* Values definitions for attributes. */ priorityValues, _defaultPriority, lifetimeValues, _defaultLiftetime, recordingLimitValues, _defaultLimit, showTypeValues, _defaultShowType))); - timerTypes.push_back( + timerTypes.emplace_back( /* Read-only one-shot for timers generated by autorec */ std::unique_ptr(new TimerType( /* Type id. */ @@ -358,12 +327,12 @@ PVR_ERROR Pvr2Wmc::GetTimerTypes(PVR_TIMER_TYPE types[], int* size) /* Attributes. */ TIMER_EPG_ATTRIBS | TIMER_CHILD_ATTRIBUTES, /* Description. */ - XBMC->GetLocalizedString(30130), // "Created by Repeating Timer" + kodi::GetLocalizedString(30130), // "Created by Repeating Timer" /* Values definitions for attributes. */ priorityValues, _defaultPriority, lifetimeValues, _defaultLiftetime, recordingLimitValues, _defaultLimit, showTypeValues, _defaultShowType))); - timerTypes.push_back( + timerTypes.emplace_back( /* One shot Keyword based */ std::unique_ptr(new TimerType( /* Type id. */ @@ -371,12 +340,12 @@ PVR_ERROR Pvr2Wmc::GetTimerTypes(PVR_TIMER_TYPE types[], int* size) /* Attributes. */ TIMER_KEYWORD_ATTRIBS | TIMER_CHILD_ATTRIBUTES, /* Description. */ - XBMC->GetLocalizedString(30130), // "Created by Repeating Timer" + kodi::GetLocalizedString(30130), // "Created by Repeating Timer" /* Values definitions for attributes. */ priorityValues, _defaultPriority, lifetimeValues, _defaultLiftetime, recordingLimitValues, _defaultLimit, showTypeValues, _defaultShowType))); - timerTypes.push_back( + timerTypes.emplace_back( /* Repeating manual (time and channel based) Parent */ std::unique_ptr(new TimerType( /* Type id. */ @@ -384,12 +353,12 @@ PVR_ERROR Pvr2Wmc::GetTimerTypes(PVR_TIMER_TYPE types[], int* size) /* Attributes. */ TIMER_MANUAL_ATTRIBS | TIMER_REPEATING_MANUAL_ATTRIBS, /* Description. */ - XBMC->GetLocalizedString(30134), // "Repeating (manual)" + kodi::GetLocalizedString(30134), // "Repeating (manual)" /* Values definitions for attributes. */ priorityValues, _defaultPriority, lifetimeValues, _defaultLiftetime, recordingLimitValues, _defaultLimit, showTypeValues, _defaultShowType))); - timerTypes.push_back( + timerTypes.emplace_back( /* Repeating epg based Parent*/ std::unique_ptr(new TimerType( /* Type id. */ @@ -397,12 +366,12 @@ PVR_ERROR Pvr2Wmc::GetTimerTypes(PVR_TIMER_TYPE types[], int* size) /* Attributes. */ TIMER_EPG_ATTRIBS | TIMER_REPEATING_EPG_ATTRIBS, /* Description. */ - XBMC->GetLocalizedString(30135), // "Repeating (guide)" + kodi::GetLocalizedString(30135), // "Repeating (guide)" /* Values definitions for attributes. */ priorityValues, _defaultPriority, lifetimeValues, _defaultLiftetime, recordingLimitValues, _defaultLimit, showTypeValues, _defaultShowType))); - timerTypes.push_back( + timerTypes.emplace_back( /* Repeating Keyword (Generic) based */ std::unique_ptr(new TimerType( /* Type id. */ @@ -410,53 +379,44 @@ PVR_ERROR Pvr2Wmc::GetTimerTypes(PVR_TIMER_TYPE types[], int* size) /* Attributes. */ TIMER_KEYWORD_ATTRIBS | TIMER_REPEATING_KEYWORD_ATTRIBS, /* Description. */ - XBMC->GetLocalizedString(30136), // "Repeating (wishlist)" + kodi::GetLocalizedString(30136), // "Repeating (wishlist)" /* Values definitions for attributes. */ priorityValues, _defaultPriority, lifetimeValues, _defaultLiftetime, recordingLimitValues, _defaultLimit, showTypeValues, _defaultShowType))); } /* Copy data to target array. */ - int i = 0; - for (auto it = timerTypes.begin(); it != timerTypes.end(); ++it, ++i) - types[i] = **it; + for (auto it = timerTypes.begin(); it != timerTypes.end(); ++it) + types.emplace_back(**it); - *size = timerTypes.size(); return PVR_ERROR_NO_ERROR; } -PVR_ERROR Pvr2Wmc::GetDriveSpace(long long* iTotal, long long* iUsed) -{ - *iTotal = _diskTotal; - *iUsed = _diskUsed; - - return PVR_ERROR_NO_ERROR; -} - -int Pvr2Wmc::GetChannelsAmount(void) +PVR_ERROR Pvr2Wmc::GetChannelsAmount(int& amount) { if (IsServerDown()) return PVR_ERROR_SERVER_ERROR; - return _socketClient.GetInt("GetChannelCount", true); + amount = _socketClient.GetInt("GetChannelCount", true); + return PVR_ERROR_NO_ERROR; } // test for returned error vector from server, handle accompanying messages if any -bool isServerError(vector results) +bool isServerError(std::vector results) { if (results[0] == "error") { if (results.size() > 1 && results[1].length() != 0) { - XBMC->Log(LOG_ERROR, results[1].c_str()); // log more info on error + kodi::Log(ADDON_LOG_ERROR, results[1].c_str()); // log more info on error } if (results.size() > 2 != 0) { - int errorID = atoi(results[2].c_str()); + int errorID = std::stoi(results[2]); if (errorID != 0) { - std::string errStr = XBMC->GetLocalizedString(errorID); - XBMC->QueueNotification(QUEUE_ERROR, errStr.c_str()); + std::string errStr = kodi::GetLocalizedString(errorID); + kodi::QueueNotification(QUEUE_ERROR, "", errStr); } } return true; @@ -466,47 +426,47 @@ bool isServerError(vector results) } // look at result vector from server and perform any updates requested -void Pvr2Wmc::TriggerUpdates(vector results) +void Pvr2Wmc::TriggerUpdates(std::vector results) { - FOREACH(response, results) + for (const auto& response : results) { - vector v = split(*response, "|"); // split to unpack string + std::vector v = Utils::Split(response, "|"); // split to unpack string if (v.size() < 1) { - XBMC->Log(LOG_DEBUG, "Wrong number of fields xfered for Triggers/Message"); + kodi::Log(ADDON_LOG_DEBUG, "Wrong number of fields xfered for Triggers/Message"); return; } if (v[0] == "updateTimers") - PVR->TriggerTimerUpdate(); + kodi::addon::CInstancePVRClient::TriggerTimerUpdate(); else if (v[0] == "updateRecordings") - PVR->TriggerRecordingUpdate(); + kodi::addon::CInstancePVRClient::TriggerRecordingUpdate(); else if (v[0] == "updateChannels") - PVR->TriggerChannelUpdate(); + kodi::addon::CInstancePVRClient::TriggerChannelUpdate(); else if (v[0] == "updateChannelGroups") - PVR->TriggerChannelGroupsUpdate(); + kodi::addon::CInstancePVRClient::TriggerChannelGroupsUpdate(); else if (v[0] == "updateEPGForChannel") { if (v.size() > 1) { unsigned int channelUid = strtoul(v[1].c_str(), 0, 10); - PVR->TriggerEpgUpdate(channelUid); + kodi::addon::CInstancePVRClient::TriggerEpgUpdate(channelUid); } } else if (v[0] == "message") { if (v.size() < 4) { - XBMC->Log(LOG_DEBUG, "Wrong number of fields xfered for Message"); + kodi::Log(ADDON_LOG_DEBUG, "Wrong number of fields xfered for Message"); return; } - XBMC->Log(LOG_INFO, "Received message from backend: %s", response->c_str()); + kodi::Log(ADDON_LOG_INFO, "Received message from backend: %s", response.c_str()); std::string infoStr; // Get notification level - int level = atoi(v[1].c_str()); + int level = std::stoi(v[1]); if (level < QUEUE_INFO) { level = QUEUE_INFO; @@ -517,8 +477,8 @@ void Pvr2Wmc::TriggerUpdates(vector results) } // Get localised string for this stringID - int stringId = atoi(v[2].c_str()); - infoStr = XBMC->GetLocalizedString(stringId); + int stringId = std::stoi(v[2]); + infoStr = kodi::GetLocalizedString(stringId); // Use text from backend if stringID not found if (infoStr == "") @@ -529,35 +489,36 @@ void Pvr2Wmc::TriggerUpdates(vector results) // Send XBMC Notification (support up to 4 parameter replaced arguments from the backend) if (v.size() == 4) { - XBMC->QueueNotification((QueueMsg)level, infoStr.c_str()); + kodi::QueueFormattedNotification((QueueMsg)level, infoStr.c_str()); } else if (v.size() == 5) { - XBMC->QueueNotification((QueueMsg)level, infoStr.c_str(), v[4].c_str()); + kodi::QueueFormattedNotification((QueueMsg)level, infoStr.c_str(), v[4].c_str()); } else if (v.size() == 6) { - XBMC->QueueNotification((QueueMsg)level, infoStr.c_str(), v[4].c_str(), v[5].c_str()); + kodi::QueueFormattedNotification((QueueMsg)level, infoStr.c_str(), v[4].c_str(), + v[5].c_str()); } else if (v.size() == 7) { - XBMC->QueueNotification((QueueMsg)level, infoStr.c_str(), v[4].c_str(), v[5].c_str(), - v[6].c_str()); + kodi::QueueFormattedNotification((QueueMsg)level, infoStr.c_str(), v[4].c_str(), + v[5].c_str(), v[6].c_str()); } else { - XBMC->QueueNotification((QueueMsg)level, infoStr.c_str(), v[4].c_str(), v[5].c_str(), - v[6].c_str(), v[7].c_str()); + kodi::QueueFormattedNotification((QueueMsg)level, infoStr.c_str(), v[4].c_str(), + v[5].c_str(), v[6].c_str(), v[7].c_str()); } } } } -void Pvr2Wmc::ExtractDriveSpace(vector results) +void Pvr2Wmc::ExtractDriveSpace(std::vector results) { - FOREACH(response, results) + for (const auto& response : results) { - vector v = split(*response, "|"); // split to unpack string + std::vector v = Utils::Split(response, "|"); // split to unpack string if (v.size() < 1) { @@ -569,137 +530,139 @@ void Pvr2Wmc::ExtractDriveSpace(vector results) if (v.size() > 1) { - long long totalSpace = strtoll(v[1].c_str(), 0, 10); - long long freeSpace = strtoll(v[2].c_str(), 0, 10); - long long usedSpace = strtoll(v[3].c_str(), 0, 10); + uint64_t totalSpace = std::stoull(v[1]); + uint64_t freeSpace = std::stoull(v[2]); + uint64_t usedSpace = std::stoull(v[3]); _diskTotal = totalSpace / 1024; _diskUsed = usedSpace / 1024; } } } } + // xbmc call: get all channels for either tv or radio -PVR_ERROR Pvr2Wmc::GetChannels(ADDON_HANDLE handle, bool bRadio) +PVR_ERROR Pvr2Wmc::GetChannels(bool radio, kodi::addon::PVRChannelsResultSet& channelResults) { if (IsServerDown()) return PVR_ERROR_SERVER_ERROR; std::string request; - request = string_format("GetChannels|%s", bRadio ? "True" : "False"); - vector results = _socketClient.GetVector(request, true); + request = Utils::Format("GetChannels|%s", radio ? "True" : "False"); + std::vector results = _socketClient.GetVector(request, true); - FOREACH(response, results) + for (const auto& response : results) { - PVR_CHANNEL xChannel; + kodi::addon::PVRChannel xChannel; - memset(&xChannel, 0, sizeof(PVR_CHANNEL)); // set all mem to zero - vector v = StringUtils::Split(*response, "|"); + std::vector v = Utils::Split(response, "|"); // packing: id, bradio, c.OriginalNumber, c.CallSign, c.IsEncrypted, imageStr, c.IsBlocked if (v.size() < 9) { - XBMC->Log(LOG_DEBUG, "Wrong number of fields xfered for channel data"); + kodi::Log(ADDON_LOG_DEBUG, "Wrong number of fields xfered for channel data"); continue; } // Populate Channel (and optionally subchannel if one was provided) - vector c = split(v[7], "."); + std::vector c = Utils::Split(v[7], "."); if (c.size() > 1) { - xChannel.iChannelNumber = atoi(c[0].c_str()); - xChannel.iSubChannelNumber = atoi(c[1].c_str()); + xChannel.SetChannelNumber(std::stoi(c[0])); + xChannel.SetSubChannelNumber(std::stoi(c[1])); } else { - xChannel.iChannelNumber = atoi(v[2].c_str()); + xChannel.SetChannelNumber(std::stoi(v[2])); } - xChannel.iUniqueId = strtoul(v[0].c_str(), 0, 10); // convert to unsigned int - xChannel.bIsRadio = Str2Bool(v[1]); - STRCPY(xChannel.strChannelName, v[3].c_str()); - xChannel.iEncryptionSystem = Str2Bool(v[4]); + xChannel.SetUniqueId(std::stoul(v[0])); // convert to unsigned int + xChannel.SetIsRadio(Utils::Str2Bool(v[1])); + xChannel.SetChannelName(v[3]); + xChannel.SetEncryptionSystem(Utils::Str2Bool(v[4])); if (v[5].compare("NULL") != 0) // if icon path is not null - STRCPY(xChannel.strIconPath, v[5].c_str()); - xChannel.bIsHidden = Str2Bool(v[6]); + xChannel.SetIconPath(v[5]); + xChannel.SetIsHidden(Utils::Str2Bool(v[6])); - PVR->TransferChannelEntry(handle, &xChannel); + channelResults.Add(xChannel); } return PVR_ERROR_NO_ERROR; } -int Pvr2Wmc::GetChannelGroupsAmount(void) +PVR_ERROR Pvr2Wmc::GetChannelGroupsAmount(int& amount) { if (IsServerDown()) return PVR_ERROR_SERVER_ERROR; - return _socketClient.GetInt("GetChannelGroupCount", true); + amount = _socketClient.GetInt("GetChannelGroupCount", true); + return PVR_ERROR_NO_ERROR; } -PVR_ERROR Pvr2Wmc::GetChannelGroups(ADDON_HANDLE handle, bool bRadio) +PVR_ERROR Pvr2Wmc::GetChannelGroups(bool radio, + kodi::addon::PVRChannelGroupsResultSet& groupResults) { if (IsServerDown()) return PVR_ERROR_SERVER_ERROR; std::string request; - request = string_format("GetChannelGroups|%s", bRadio ? "True" : "False"); - vector results = _socketClient.GetVector(request, true); + request = Utils::Format("GetChannelGroups|%s", radio ? "True" : "False"); + std::vector results = _socketClient.GetVector(request, true); - FOREACH(response, results) + for (const auto& response : results) { - PVR_CHANNEL_GROUP xGroup; - memset(&xGroup, 0, sizeof(PVR_CHANNEL_GROUP)); + kodi::addon::PVRChannelGroup xGroup; - vector v = split(*response, "|"); + std::vector v = Utils::Split(response, "|"); if (v.size() < 1) { - XBMC->Log(LOG_DEBUG, "Wrong number of fields xfered for channel group data"); + kodi::Log(ADDON_LOG_DEBUG, "Wrong number of fields xfered for channel group data"); continue; } - xGroup.bIsRadio = bRadio; - strncpy(xGroup.strGroupName, v[0].c_str(), sizeof(xGroup.strGroupName) - 1); + xGroup.SetIsRadio(radio); + xGroup.SetGroupName(v[0]); // PVR API 1.9.6 adds Position field if (v.size() >= 2) { - xGroup.iPosition = atoi(v[1].c_str()); + xGroup.SetPosition(std::stoi(v[1])); } - PVR->TransferChannelGroup(handle, &xGroup); + groupResults.Add(xGroup); } return PVR_ERROR_NO_ERROR; } -PVR_ERROR Pvr2Wmc::GetChannelGroupMembers(ADDON_HANDLE handle, const PVR_CHANNEL_GROUP& group) +PVR_ERROR Pvr2Wmc::GetChannelGroupMembers( + const kodi::addon::PVRChannelGroup& group, + kodi::addon::PVRChannelGroupMembersResultSet& groupMemberResults) { if (IsServerDown()) return PVR_ERROR_SERVER_ERROR; std::string request; - request = string_format("GetChannelGroupMembers|%s|%s", group.bIsRadio ? "True" : "False", - group.strGroupName); - vector results = _socketClient.GetVector(request, true); + request = Utils::Format("GetChannelGroupMembers|%s|%s", group.GetIsRadio() ? "True" : "False", + group.GetGroupName().c_str()); + std::vector results = _socketClient.GetVector(request, true); - FOREACH(response, results) + for (const auto& response : results) { - PVR_CHANNEL_GROUP_MEMBER xGroupMember; - memset(&xGroupMember, 0, sizeof(PVR_CHANNEL_GROUP_MEMBER)); + kodi::addon::PVRChannelGroupMember xGroupMember; - vector v = split(*response, "|"); + std::vector v = Utils::Split(response, "|"); if (v.size() < 2) { - XBMC->Log(LOG_DEBUG, "Wrong number of fields xfered for channel group member data"); + kodi::Log(ADDON_LOG_DEBUG, "Wrong number of fields xfered for channel group member data"); continue; } - strncpy(xGroupMember.strGroupName, group.strGroupName, sizeof(xGroupMember.strGroupName) - 1); - xGroupMember.iChannelUniqueId = strtoul(v[0].c_str(), 0, 10); // convert to unsigned int - xGroupMember.iChannelNumber = atoi(v[1].c_str()); + xGroupMember.SetGroupName(group.GetGroupName()); + xGroupMember.SetChannelUniqueId(std::stoul(v[0])); // convert to unsigned int + xGroupMember.SetChannelNumber(std::stoi(v[1])); - PVR->TransferChannelGroupMember(handle, &xGroupMember); + groupMemberResults.Add(xGroupMember); } return PVR_ERROR_NO_ERROR; @@ -719,29 +682,29 @@ std::string ParseAsW3CDateString(time_t time) } // unnamed namespace -PVR_ERROR Pvr2Wmc::GetEPGForChannel(ADDON_HANDLE handle, - int iChannelUid, - time_t iStart, - time_t iEnd) +PVR_ERROR Pvr2Wmc::GetEPGForChannel(int channelUid, + time_t start, + time_t end, + kodi::addon::PVREPGTagsResultSet& epgResults) { if (IsServerDown()) return PVR_ERROR_SERVER_ERROR; std::string request; - request = string_format("GetEntries|%u|%lld|%lld", iChannelUid, (long long)iStart, - (long long)iEnd); // build the request string + request = Utils::Format("GetEntries|%u|%lld|%lld", channelUid, (long long)start, + (long long)end); // build the request string - vector results = _socketClient.GetVector(request, true); // get entries from server + std::vector results = + _socketClient.GetVector(request, true); // get entries from server - FOREACH(response, results) + for (const auto& response : results) { - EPG_TAG xEpg; - memset(&xEpg, 0, sizeof(EPG_TAG)); // set all mem to zero - vector v = split(*response, "|"); // split to unpack string + kodi::addon::PVREPGTag xEpg; + std::vector v = Utils::Split(response, "|"); // split to unpack string if (v.size() < 16) { - XBMC->Log(LOG_DEBUG, "Wrong number of fields xfered for epg data"); + kodi::Log(ADDON_LOG_DEBUG, "Wrong number of fields xfered for epg data"); continue; } @@ -752,79 +715,84 @@ PVR_ERROR Pvr2Wmc::GetEPGForChannel(ADDON_HANDLE handle, // e.Program.EpisodeTitle // (MB3 fields) channelID, audioFormat, GenreString, ProgramType // actors, directors, writers, year, movieID - xEpg.iUniqueChannelId = iChannelUid; // assign unique channel ID - xEpg.iUniqueBroadcastId = atoi(v[0].c_str()); // entry ID - xEpg.strTitle = v[1].c_str(); // entry title - xEpg.startTime = atol(v[3].c_str()); // start time - xEpg.endTime = atol(v[4].c_str()); // end time - xEpg.strPlotOutline = - v[5].c_str(); // short plot description (currently using episode name, if there is one) - xEpg.strPlot = v[6].c_str(); // long plot description - time_t firstAired = atol(v[7].c_str()); // orig air date + xEpg.SetUniqueChannelId(channelUid); // assign unique channel ID + xEpg.SetUniqueBroadcastId(std::stoi(v[0])); // entry ID + xEpg.SetTitle(v[1]); // entry title + xEpg.SetStartTime(std::stol(v[3])); // start time + xEpg.SetEndTime(std::stol(v[4])); // end time + xEpg.SetPlotOutline( + v[5]); // short plot description (currently using episode name, if there is one) + xEpg.SetPlot(v[6]); // long plot description + time_t firstAired = std::stol(v[7]); // orig air date std::string strFirstAired((firstAired > 0) ? ParseAsW3CDateString(firstAired) : ""); - xEpg.strFirstAired = strFirstAired.c_str(); - xEpg.iParentalRating = atoi(v[8].c_str()); // tv rating - xEpg.iStarRating = atoi(v[9].c_str()); // star rating - xEpg.iSeriesNumber = atoi(v[10].c_str()); // season (?) number - xEpg.iEpisodeNumber = atoi(v[11].c_str()); // episode number - if (xEpg.iSeriesNumber == 0 && xEpg.iEpisodeNumber == 0) + xEpg.SetFirstAired(strFirstAired); + xEpg.SetParentalRating(std::stoi(v[8])); // tv rating + xEpg.SetStarRating(std::stoi(v[9])); // star rating + xEpg.SetSeriesNumber(std::stoi(v[10])); // season (?) number + xEpg.SetEpisodeNumber(std::stoi(v[11])); // episode number + if (xEpg.GetSeriesNumber() == 0 && xEpg.GetEpisodeNumber() == 0) { - xEpg.iSeriesNumber = EPG_TAG_INVALID_SERIES_EPISODE; - xEpg.iEpisodeNumber = EPG_TAG_INVALID_SERIES_EPISODE; + xEpg.SetSeriesNumber(EPG_TAG_INVALID_SERIES_EPISODE); + xEpg.SetEpisodeNumber(EPG_TAG_INVALID_SERIES_EPISODE); } - xEpg.iEpisodePartNumber = EPG_TAG_INVALID_SERIES_EPISODE; - xEpg.iGenreType = atoi(v[12].c_str()); // season (?) number - xEpg.iGenreSubType = atoi(v[13].c_str()); // general genre type - xEpg.strIconPath = v[14].c_str(); // the icon url - xEpg.strEpisodeName = v[15].c_str(); // the episode name - xEpg.strGenreDescription = ""; + xEpg.SetEpisodePartNumber(EPG_TAG_INVALID_SERIES_EPISODE); + xEpg.SetGenreType(std::stoi(v[12])); // season (?) number + xEpg.SetGenreSubType(std::stoi(v[13])); // general genre type + xEpg.SetIconPath(v[14]); // the icon url + xEpg.SetEpisodeName(v[15]); // the episode name + xEpg.SetGenreDescription(""); // Kodi PVR API 1.9.6 adds new EPG fields if (v.size() >= 25) { - xEpg.strCast = v[20].c_str(); - xEpg.strDirector = v[21].c_str(); - xEpg.strWriter = v[22].c_str(); - xEpg.iYear = atoi(v[23].c_str()); - xEpg.strIMDBNumber = v[24].c_str(); + xEpg.SetCast(v[20]); + xEpg.SetDirector(v[21]); + xEpg.SetWriter(v[22]); + xEpg.SetYear(std::stoi(v[23])); + xEpg.SetIMDBNumber(v[24]); } // Kodi PVR API 4.1.0 adds new EPG iFlags field + unsigned int flags = 0; if (v.size() >= 26) { - if (Str2Bool(v[25].c_str())) + if (Utils::Str2Bool(v[25])) { - xEpg.iFlags |= EPG_TAG_FLAG_IS_SERIES; + flags |= EPG_TAG_FLAG_IS_SERIES; } } + xEpg.SetFlags(flags); - PVR->TransferEpgEntry(handle, &xEpg); + epgResults.Add(xEpg); } return PVR_ERROR_NO_ERROR; } // timer functions ------------------------------------------------------------- -int Pvr2Wmc::GetTimersAmount(void) +PVR_ERROR Pvr2Wmc::GetTimersAmount(int& amount) { if (IsServerDown()) return PVR_ERROR_SERVER_ERROR; - return _socketClient.GetInt("GetTimerCount", true); + amount = _socketClient.GetInt("GetTimerCount", true); + return PVR_ERROR_NO_ERROR; } -PVR_ERROR Pvr2Wmc::AddTimer(const PVR_TIMER& xTmr) +PVR_ERROR Pvr2Wmc::AddTimer(const kodi::addon::PVRTimer& timer) { if (IsServerDown()) return PVR_ERROR_SERVER_ERROR; // Send request to ServerWMC std::string command = ""; - command = "SetTimerKodi" + Timer2String(xTmr); // convert timer to string + command = "SetTimerKodi" + Timer2String(timer); // convert timer to string - vector results = _socketClient.GetVector(command, false); // get results from server + std::vector results = + _socketClient.GetVector(command, false); // get results from server - PVR->TriggerTimerUpdate(); // update timers regardless of whether there is an error + kodi::addon::CInstancePVRClient::TriggerTimerUpdate(); // update timers regardless of whether + // there is an error if (isServerError(results)) { @@ -832,50 +800,52 @@ PVR_ERROR Pvr2Wmc::AddTimer(const PVR_TIMER& xTmr) } else { - XBMC->Log(LOG_DEBUG, "recording added for timer '%s', with rec state %s", xTmr.strTitle, - results[0].c_str()); + kodi::Log(ADDON_LOG_DEBUG, "recording added for timer '%s', with rec state %s", + timer.GetTitle().c_str(), results[0].c_str()); if (results.size() > 1) // if there is extra results sent from server... { - FOREACH(result, results) + for (const auto& response : results) { - vector splitResult = - split(*result, "|"); // split to unpack extra info on each result + std::vector splitResult = + Utils::Split(response, "|"); // split to unpack extra info on each result std::string infoStr; if (splitResult[0] == "recordingNow") // recording is active now { - XBMC->Log(LOG_DEBUG, "timer recording is in progress"); + kodi::Log(ADDON_LOG_DEBUG, "timer recording is in progress"); } else if (splitResult[0] == "recordingNowTimedOut") // swmc timed out waiting for the recording to start { - XBMC->Log(LOG_DEBUG, "server timed out waiting for in-progress recording to start"); + kodi::Log(ADDON_LOG_DEBUG, "server timed out waiting for in-progress recording to start"); } else if (splitResult[0] == "recordingChannel") // service picked a different channel for timer { - XBMC->Log(LOG_DEBUG, "timer channel changed by wmc to '%s'", splitResult[1].c_str()); + kodi::Log(ADDON_LOG_DEBUG, "timer channel changed by wmc to '%s'", + splitResult[1].c_str()); // build info string and notify user of channel change - infoStr = XBMC->GetLocalizedString(30009) + splitResult[1]; - XBMC->QueueNotification(QUEUE_WARNING, infoStr.c_str()); + infoStr = kodi::GetLocalizedString(30009) + splitResult[1]; + kodi::QueueNotification(QUEUE_WARNING, "", infoStr); } else if (splitResult[0] == "recordingTime") // service picked a different start time for timer { - XBMC->Log(LOG_DEBUG, "timer start time changed by wmc to '%s'", splitResult[1].c_str()); + kodi::Log(ADDON_LOG_DEBUG, "timer start time changed by wmc to '%s'", + splitResult[1].c_str()); // build info string and notify user of time change - infoStr = XBMC->GetLocalizedString(30010) + splitResult[1]; - XBMC->QueueNotification(QUEUE_WARNING, infoStr.c_str()); + infoStr = kodi::GetLocalizedString(30010) + splitResult[1]; + kodi::QueueNotification(QUEUE_WARNING, "", infoStr); } else if (splitResult[0] == "increasedEndTime") // end time has been increased on an instant record { - XBMC->Log(LOG_DEBUG, "instant record end time increased by '%s' minutes", + kodi::Log(ADDON_LOG_DEBUG, "instant record end time increased by '%s' minutes", splitResult[1].c_str()); // build info string and notify user of time increase - infoStr = XBMC->GetLocalizedString(30013) + splitResult[1] + " min"; - XBMC->QueueNotification(QUEUE_INFO, infoStr.c_str()); + infoStr = kodi::GetLocalizedString(30013) + splitResult[1] + " min"; + kodi::QueueNotification(QUEUE_INFO, "", infoStr); } } } @@ -884,52 +854,56 @@ PVR_ERROR Pvr2Wmc::AddTimer(const PVR_TIMER& xTmr) } } -std::string Pvr2Wmc::Timer2String(const PVR_TIMER& xTmr) +std::string Pvr2Wmc::Timer2String(const kodi::addon::PVRTimer& xTmr) { std::string tStr; bool bRepeating = - xTmr.iTimerType >= TIMER_REPEATING_MIN && xTmr.iTimerType <= TIMER_REPEATING_MAX; - bool bKeyword = xTmr.iTimerType == TIMER_REPEATING_KEYWORD || - xTmr.iTimerType == TIMER_ONCE_KEYWORD || - xTmr.iTimerType == TIMER_ONCE_KEYWORD_CHILD; - bool bManual = xTmr.iTimerType == TIMER_ONCE_MANUAL || - xTmr.iTimerType == TIMER_ONCE_MANUAL_CHILD || - xTmr.iTimerType == TIMER_REPEATING_MANUAL; - - //was ("|%d|%d|%d|%d|%d|%s|%d|%d|%d|%d|%d", - tStr = string_format( - "|%u|%d|%lld|%lld|%d|%s|%d|%u|%u|%d|%u", xTmr.iClientIndex, xTmr.iClientChannelUid, - (long long)xTmr.startTime, (long long)xTmr.endTime, PVR_TIMER_STATE_NEW, // 0-4 - xTmr.strTitle, xTmr.iPriority, xTmr.iMarginStart, xTmr.iMarginEnd, bRepeating, // 5-9 - xTmr.iEpgUid); // 10 + xTmr.GetTimerType() >= TIMER_REPEATING_MIN && xTmr.GetTimerType() <= TIMER_REPEATING_MAX; + bool bKeyword = xTmr.GetTimerType() == TIMER_REPEATING_KEYWORD || + xTmr.GetTimerType() == TIMER_ONCE_KEYWORD || + xTmr.GetTimerType() == TIMER_ONCE_KEYWORD_CHILD; + bool bManual = xTmr.GetTimerType() == TIMER_ONCE_MANUAL || + xTmr.GetTimerType() == TIMER_ONCE_MANUAL_CHILD || + xTmr.GetTimerType() == TIMER_REPEATING_MANUAL; + + // was ("|%d|%d|%d|%d|%d|%s|%d|%d|%d|%d|%d", + tStr = Utils::Format("|%u|%d|%lld|%lld|%d|%s|%d|%u|%u|%d|%u", xTmr.GetClientIndex(), + xTmr.GetClientChannelUid(), (long long)xTmr.GetStartTime(), + (long long)xTmr.GetEndTime(), PVR_TIMER_STATE_NEW, // 0-4 + xTmr.GetTitle().c_str(), xTmr.GetPriority(), xTmr.GetMarginStart(), + xTmr.GetMarginEnd(), bRepeating, // 5-9 + xTmr.GetEPGUid()); // 10 // Append extra fields from Kodi 16 std::string extra; - //was ("|%d|%d|%d|%d|%d|%d|%s|%d|%d", - extra = string_format("|%u|%d|%u|%d|%d|%d|%s|%d|%d", xTmr.iPreventDuplicateEpisodes, - xTmr.bStartAnyTime, xTmr.iWeekdays, // 11-13 param - xTmr.iLifetime, bKeyword, xTmr.bFullTextEpgSearch, xTmr.strEpgSearchString, - xTmr.iMaxRecordings, bManual); // 14-19 + // was ("|%d|%d|%d|%d|%d|%d|%s|%d|%d", + extra = + Utils::Format("|%u|%d|%u|%d|%d|%d|%s|%d|%d", xTmr.GetPreventDuplicateEpisodes(), + xTmr.GetStartAnyTime(), xTmr.GetWeekdays(), // 11-13 param + xTmr.GetLifetime(), bKeyword, xTmr.GetFullTextEpgSearch(), + xTmr.GetEPGSearchString().c_str(), xTmr.GetMaxRecordings(), bManual); // 14-19 tStr.append(extra); return tStr; } -PVR_ERROR Pvr2Wmc::DeleteTimer(const PVR_TIMER& xTmr, bool bForceDelete) +PVR_ERROR Pvr2Wmc::DeleteTimer(const kodi::addon::PVRTimer& timer, bool forceDelete) { if (IsServerDown()) return PVR_ERROR_SERVER_ERROR; bool bRepeating = - xTmr.iTimerType >= TIMER_REPEATING_MIN && xTmr.iTimerType <= TIMER_REPEATING_MAX; + timer.GetTimerType() >= TIMER_REPEATING_MIN && timer.GetTimerType() <= TIMER_REPEATING_MAX; std::string command = "DeleteTimerKodi"; - command = string_format("DeleteTimerKodi|%u|%d", xTmr.iClientIndex, bRepeating); + command = Utils::Format("DeleteTimerKodi|%u|%d", timer.GetClientIndex(), bRepeating); - vector results = _socketClient.GetVector(command, false); // get results from server + std::vector results = + _socketClient.GetVector(command, false); // get results from server - PVR->TriggerTimerUpdate(); // update timers regardless of whether there is an error + kodi::addon::CInstancePVRClient::TriggerTimerUpdate(); // update timers regardless of whether + // there is an error if (isServerError(results)) // did the server do it? { @@ -937,183 +911,186 @@ PVR_ERROR Pvr2Wmc::DeleteTimer(const PVR_TIMER& xTmr, bool bForceDelete) } else { - XBMC->Log(LOG_DEBUG, "deleted timer '%s', with rec state %s", xTmr.strTitle, + kodi::Log(ADDON_LOG_DEBUG, "deleted timer '%s', with rec state %s", timer.GetTitle().c_str(), results[0].c_str()); return PVR_ERROR_NO_ERROR; } } -PVR_ERROR Pvr2Wmc::GetTimers(ADDON_HANDLE handle) +PVR_ERROR Pvr2Wmc::GetTimers(kodi::addon::PVRTimersResultSet& results) { if (IsServerDown()) return PVR_ERROR_SERVER_ERROR; - vector responsesSeries = _socketClient.GetVector("GetSeriesTimers", true); - FOREACH(response, responsesSeries) + std::vector responsesSeries = _socketClient.GetVector("GetSeriesTimers", true); + for (const auto& response : responsesSeries) { - PVR_TIMER xTmr; - memset(&xTmr, 0, sizeof(PVR_TIMER)); // set all struct to zero + kodi::addon::PVRTimer xTmr; - vector v = split(*response, "|"); // split to unpack string + std::vector v = Utils::Split(response, "|"); // split to unpack string if (v.size() < 24) { - XBMC->Log(LOG_DEBUG, "Wrong number of fields xfered for SeriesTimer data"); + kodi::Log(ADDON_LOG_DEBUG, "Wrong number of fields xfered for SeriesTimer data"); continue; } - xTmr.iTimerType = PVR_TIMER_TYPE_NONE; + xTmr.SetTimerType(PVR_TIMER_TYPE_NONE); // [0] Timer ID (need UINT32 value, see [21]) // [1] Title (superceded by [17] Timer Name) - xTmr.iClientChannelUid = atoi(v[2].c_str()); // [2] channel id - xTmr.iEpgUid = - atoi(v[3].c_str()); // [3] epg ID (same as client ID, except for a 'manual' record) - STRCPY(xTmr.strSummary, v[4].c_str()); // [4] currently set to description - xTmr.startTime = atoi(v[5].c_str()); // [5] start time - xTmr.endTime = atoi(v[6].c_str()); // [6] end time - xTmr.iMarginStart = atoi(v[7].c_str()); // [7] rec margin at start (sec) - xTmr.iMarginEnd = atoi(v[8].c_str()); // [8] rec margin at end (sec) + xTmr.SetClientChannelUid(std::stoi(v[2])); // [2] channel id + xTmr.SetEPGUid( + std::stoul(v[3])); // [3] epg ID (same as client ID, except for a 'manual' record) + xTmr.SetSummary(v[4]); // [4] currently set to description + xTmr.SetStartTime(std::stoi(v[5])); // [5] start time + xTmr.SetEndTime(std::stoi(v[6])); // [6] end time + xTmr.SetMarginStart(std::stoul(v[7])); // [7] rec margin at start (sec) + xTmr.SetMarginEnd(std::stoul(v[8])); // [8] rec margin at end (sec) // [9] isPreMarginRequired // [10] isPostMarginRequired // [11] WMC Priority (need Kodi compatible value, see [26]) // [12] NewEpisodesOnly (superceded by RunType) - if (Str2Bool(v[13].c_str())) // [13] Any Channel + if (Utils::Str2Bool(v[13].c_str())) // [13] Any Channel { - xTmr.iClientChannelUid = 0; + xTmr.SetClientChannelUid(0); } - if (Str2Bool(v[14].c_str())) // [14] Any Time + if (Utils::Str2Bool(v[14].c_str())) // [14] Any Time { - xTmr.bStartAnyTime = true; - xTmr.bEndAnyTime = true; + xTmr.SetStartAnyTime(true); + xTmr.SetEndAnyTime(true); } - xTmr.iWeekdays = - atoi(v[15].c_str()); // [15] DaysOfWeek (converted to Kodi values in the backend) - xTmr.state = (PVR_TIMER_STATE)atoi(v[16].c_str()); // [16] current state of timer - STRCPY(xTmr.strTitle, v[17].c_str()); // [17] timer name - xTmr.iGenreType = atoi(v[18].c_str()); // [18] genre ID - xTmr.iGenreSubType = atoi(v[19].c_str()); // [19] sub genre ID - xTmr.iPreventDuplicateEpisodes = atoi(v[20].c_str()); // [20] WMC RunType - xTmr.iClientIndex = atoi(v[21].c_str()); // [21] Timer ID (in UINT32 form) - STRCPY(xTmr.strEpgSearchString, v[22].c_str()); // [22] Keyword Search - xTmr.bFullTextEpgSearch = Str2Bool(v[23].c_str()); // [23] Keyword is FullText - xTmr.iLifetime = atoi(v[24].c_str()); // [24] Lifetime - xTmr.iMaxRecordings = atoi(v[25].c_str()); // [25] Maximum Recordings (Recording Limit) - xTmr.iPriority = atoi(v[26].c_str()); // [26] Priority (in Kodi enum value form) + xTmr.SetWeekdays( + std::stoul(v[15])); // [15] DaysOfWeek (converted to Kodi values in the backend) + xTmr.SetState((PVR_TIMER_STATE)std::stoi(v[16])); // [16] current state of timer + xTmr.SetTitle(v[17]); // [17] timer name + xTmr.SetGenreType(std::stoi(v[18])); // [18] genre ID + xTmr.SetGenreSubType(std::stoi(v[19])); // [19] sub genre ID + xTmr.SetPreventDuplicateEpisodes(std::stoul(v[20])); // [20] WMC RunType + xTmr.SetClientIndex(std::stoul(v[21])); // [21] Timer ID (in UINT32 form) + xTmr.SetEPGSearchString(v[22]); // [22] Keyword Search + xTmr.SetFullTextEpgSearch(Utils::Str2Bool(v[23])); // [23] Keyword is FullText + xTmr.SetLifetime(std::stoi(v[24])); // [24] Lifetime + xTmr.SetMaxRecordings(std::stoi(v[25])); // [25] Maximum Recordings (Recording Limit) + xTmr.SetPriority(std::stoi(v[26])); // [26] Priority (in Kodi enum value form) // Determine TimerType - bool hasKeyword = strlen(xTmr.strEpgSearchString) > 0; - bool hasEPG = (xTmr.iEpgUid != PVR_TIMER_NO_EPG_UID); - xTmr.iTimerType = hasKeyword ? TIMER_REPEATING_KEYWORD - : hasEPG ? TIMER_REPEATING_EPG : TIMER_REPEATING_MANUAL; + bool hasKeyword = !xTmr.GetEPGSearchString().empty(); + bool hasEPG = (xTmr.GetEPGUid() != PVR_TIMER_NO_EPG_UID); + xTmr.SetTimerType(hasKeyword ? TIMER_REPEATING_KEYWORD + : hasEPG ? TIMER_REPEATING_EPG : TIMER_REPEATING_MANUAL); - PVR->TransferTimerEntry(handle, &xTmr); + results.Add(xTmr); } - vector responsesTimers = _socketClient.GetVector("GetTimers", true); - FOREACH(response, responsesTimers) + std::vector responsesTimers = _socketClient.GetVector("GetTimers", true); + for (const auto& response : responsesTimers) { - PVR_TIMER xTmr; - memset(&xTmr, 0, sizeof(PVR_TIMER)); // set all struct to zero + kodi::addon::PVRTimer xTmr; - vector v = split(*response, "|"); // split to unpack string + std::vector v = Utils::Split(response, "|"); // split to unpack string // eId, chId, start_t, end_t, pState, - // rp.Program.Title, ""/*recdir*/, rp.Program.EpisodeTitle/*summary?*/, rp.Priority, rp.Request.IsRecurring, - // eId, preMargin, postMargin, genre, subgenre + // rp.Program.Title, ""/*recdir*/, rp.Program.EpisodeTitle/*summary?*/, rp.Priority, + // rp.Request.IsRecurring, eId, preMargin, postMargin, genre, subgenre if (v.size() < 24) { - XBMC->Log(LOG_DEBUG, "Wrong number of fields xfered for timer data"); + kodi::Log(ADDON_LOG_DEBUG, "Wrong number of fields xfered for timer data"); continue; } - xTmr.iTimerType = PVR_TIMER_TYPE_NONE; - xTmr.iClientIndex = atoi(v[0].c_str()); // [0] Timer ID - xTmr.iClientChannelUid = atoi(v[1].c_str()); // [1] channel id - xTmr.startTime = atoi(v[2].c_str()); // [2] start time - xTmr.endTime = atoi(v[3].c_str()); // [3] end time - xTmr.state = (PVR_TIMER_STATE)atoi(v[4].c_str()); // [4] current state of time - STRCPY(xTmr.strTitle, v[5].c_str()); // [5] timer name (set to same as Program title) - STRCPY(xTmr.strDirectory, v[6].c_str()); // [6] rec directory - STRCPY(xTmr.strSummary, v[7].c_str()); // [7] set to program description + xTmr.SetTimerType(PVR_TIMER_TYPE_NONE); + xTmr.SetClientIndex(std::stoul(v[0])); // [0] Timer ID + xTmr.SetClientChannelUid(std::stoi(v[1])); // [1] channel id + xTmr.SetStartTime(std::stoi(v[2])); // [2] start time + xTmr.SetEndTime(std::stoi(v[3])); // [3] end time + xTmr.SetState((PVR_TIMER_STATE)std::stoi(v[4])); // [4] current state of time + xTmr.SetTitle(v[5]); // [5] timer name (set to same as Program title) + xTmr.SetDirectory(v[6]); // [6] rec directory + xTmr.SetSummary(v[7]); // [7] set to program description // [8] WMC Priority (need Kodi compatible value, see [26]) // [9] IsRecurring - xTmr.iEpgUid = atoi(v[10].c_str()); // [10] epg ID - xTmr.iMarginStart = atoi(v[11].c_str()); // [11] rec margin at start (sec) - xTmr.iMarginEnd = atoi(v[12].c_str()); // [12] rec margin at end (sec) - xTmr.iGenreType = atoi(v[13].c_str()); // [13] genre ID - xTmr.iGenreSubType = atoi(v[14].c_str()); // [14] sub genre ID + xTmr.SetEPGUid(std::stoul(v[10])); // [10] epg ID + xTmr.SetMarginStart(std::stoul(v[11])); // [11] rec margin at start (sec) + xTmr.SetMarginEnd(std::stoul(v[12])); // [12] rec margin at end (sec) + xTmr.SetGenreType(std::stoi(v[13])); // [13] genre ID + xTmr.SetGenreSubType(std::stoi(v[14])); // [14] sub genre ID // [15] epg ID (duplicated from [9] for some reason) // [16] Parent Series ID (need in UINT32 form, see [23]) // [17] isPreMarginRequired // [18] isPostMarginRequired - xTmr.iPreventDuplicateEpisodes = atoi(v[19].c_str()); // [19] WMC runType - if (Str2Bool(v[20].c_str())) // [20] Any Channel + xTmr.SetPreventDuplicateEpisodes(std::stoul(v[19])); // [19] WMC runType + if (Utils::Str2Bool(v[20].c_str())) // [20] Any Channel { // As this is a child instance recording, we want to preserve the actual channel } - if (Str2Bool(v[21].c_str())) // [21] Any Time + if (Utils::Str2Bool(v[21].c_str())) // [21] Any Time { // As this is a child instance recording, we want to preserve the actual start/finish times } - xTmr.iWeekdays = - atoi(v[22].c_str()); // [22] DaysOfWeek (converted to Kodi values in the backend) - xTmr.iParentClientIndex = atoi(v[23].c_str()); // [23] Parent Series ID (in UINT32 form) - xTmr.iLifetime = atoi(v[24].c_str()); // [24] Lifetime - xTmr.iMaxRecordings = atoi(v[25].c_str()); // [25] Maximum Recordings (Recording Limit) - xTmr.iPriority = atoi(v[26].c_str()); // [26] Priority (in Kodi enum value form) - STRCPY(xTmr.strEpgSearchString, v[27].c_str()); // [27] Keyword Search - xTmr.bFullTextEpgSearch = Str2Bool(v[28].c_str()); // [28] Keyword is FullText + xTmr.SetWeekdays( + std::stoul(v[22])); // [22] DaysOfWeek (converted to Kodi values in the backend) + xTmr.SetParentClientIndex(std::stoul(v[23])); // [23] Parent Series ID (in UINT32 form) + xTmr.SetLifetime(std::stoi((v[24]))); // [24] Lifetime + xTmr.SetMaxRecordings(std::stoi(v[25])); // [25] Maximum Recordings (Recording Limit) + xTmr.SetPriority(std::stoi(v[26])); // [26] Priority (in Kodi enum value form) + xTmr.SetEPGSearchString(v[27]); // [27] Keyword Search + xTmr.SetFullTextEpgSearch(Utils::Str2Bool(v[28].c_str())); // [28] Keyword is FullText // Determine TimerType - bool hasParent = (xTmr.iParentClientIndex != 0); - bool hasKeyword = strlen(xTmr.strEpgSearchString) > 0; - bool hasEPG = (xTmr.iEpgUid != PVR_TIMER_NO_EPG_UID); + bool hasParent = (xTmr.GetParentClientIndex() != 0); + bool hasKeyword = !xTmr.GetEPGSearchString().empty(); + bool hasEPG = (xTmr.GetEPGUid() != PVR_TIMER_NO_EPG_UID); if (hasParent) { - xTmr.iTimerType = hasKeyword ? TIMER_ONCE_KEYWORD_CHILD - : hasEPG ? TIMER_ONCE_EPG_CHILD : TIMER_ONCE_MANUAL_CHILD; + xTmr.SetTimerType(hasKeyword ? TIMER_ONCE_KEYWORD_CHILD + : hasEPG ? TIMER_ONCE_EPG_CHILD : TIMER_ONCE_MANUAL_CHILD); } else { - xTmr.iTimerType = - hasKeyword ? TIMER_ONCE_KEYWORD : hasEPG ? TIMER_ONCE_EPG : TIMER_ONCE_MANUAL; + xTmr.SetTimerType(hasKeyword ? TIMER_ONCE_KEYWORD + : hasEPG ? TIMER_ONCE_EPG : TIMER_ONCE_MANUAL); } - PVR->TransferTimerEntry(handle, &xTmr); + results.Add(xTmr); } // check time since last time Recordings were updated, update if it has been awhile - if (_lastRecordingUpdateTime != 0 && P8PLATFORM::GetTimeMs() > _lastRecordingUpdateTime + 120000) + std::chrono::high_resolution_clock::time_point now = std::chrono::high_resolution_clock::now(); + if (static_cast( + std::chrono::duration_cast(now - _lastRecordingUpdateTime).count() > + 120)) { - PVR->TriggerRecordingUpdate(); + kodi::addon::CInstancePVRClient::TriggerRecordingUpdate(); } return PVR_ERROR_NO_ERROR; } // recording functions ------------------------------------------------------------------------ -int Pvr2Wmc::GetRecordingsAmount(void) +PVR_ERROR Pvr2Wmc::GetRecordingsAmount(bool deleted, int& amount) { if (IsServerDown()) return PVR_ERROR_SERVER_ERROR; - return _socketClient.GetInt("GetRecordingsAmount", true); + if (!deleted) + amount = _socketClient.GetInt("GetRecordingsAmount", true); + return PVR_ERROR_NO_ERROR; } // recording file functions -PVR_ERROR Pvr2Wmc::GetRecordings(ADDON_HANDLE handle) +PVR_ERROR Pvr2Wmc::GetRecordings(bool deleted, kodi::addon::PVRRecordingsResultSet& results) { if (IsServerDown()) return PVR_ERROR_SERVER_ERROR; - vector responses = _socketClient.GetVector("GetRecordings", true); + std::vector responses = _socketClient.GetVector("GetRecordings", true); - FOREACH(response, responses) + for (const auto& response : responses) { - PVR_RECORDING xRec; - memset(&xRec, 0, sizeof(PVR_RECORDING)); // set all struct to zero - xRec.iSeriesNumber = PVR_RECORDING_INVALID_SERIES_EPISODE; - xRec.iEpisodeNumber = PVR_RECORDING_INVALID_SERIES_EPISODE; + kodi::addon::PVRRecording xRec; + + xRec.SetSeriesNumber(PVR_RECORDING_INVALID_SERIES_EPISODE); + xRec.SetEpisodeNumber(PVR_RECORDING_INVALID_SERIES_EPISODE); - vector v = split(*response, "|"); // split to unpack string + std::vector v = Utils::Split(response, "|"); // split to unpack string // r.Id, r.Program.Title, r.FileName, recDir, plotOutline, // plot, r.Channel.CallSign, ""/*icon path*/, ""/*thumbnail path*/, ToTime_t(r.RecordingTime), @@ -1122,70 +1099,71 @@ PVR_ERROR Pvr2Wmc::GetRecordings(ADDON_HANDLE handle) if (v.size() < 16) { - XBMC->Log(LOG_DEBUG, "Wrong number of fields xfered for recording data"); + kodi::Log(ADDON_LOG_DEBUG, "Wrong number of fields xfered for recording data"); continue; } - STRCPY(xRec.strRecordingId, v[0].c_str()); - STRCPY(xRec.strTitle, v[1].c_str()); - STRCPY(xRec.strDirectory, v[3].c_str()); - STRCPY(xRec.strPlotOutline, v[4].c_str()); - STRCPY(xRec.strPlot, v[5].c_str()); - STRCPY(xRec.strChannelName, v[6].c_str()); - STRCPY(xRec.strIconPath, v[7].c_str()); - STRCPY(xRec.strThumbnailPath, v[8].c_str()); - xRec.recordingTime = atol(v[9].c_str()); - xRec.iDuration = atoi(v[10].c_str()); - xRec.iPriority = atoi(v[11].c_str()); - xRec.iLifetime = atoi(v[12].c_str()); - xRec.iGenreType = atoi(v[13].c_str()); - xRec.iGenreSubType = atoi(v[14].c_str()); - if (g_bEnableMultiResume) + xRec.SetRecordingId(v[0]); + xRec.SetTitle(v[1]); + xRec.SetDirectory(v[3]); + xRec.SetPlotOutline(v[4]); + xRec.SetPlot(v[5]); + xRec.SetChannelName(v[6]); + xRec.SetIconPath(v[7]); + xRec.SetThumbnailPath(v[8]); + xRec.SetRecordingTime(std::stoi(v[9])); + xRec.SetDuration(std::stoi(v[10])); + xRec.SetPriority(std::stoi(v[11])); + xRec.SetLifetime(std::stoi(v[12])); + xRec.SetGenreType(std::stoi(v[13])); + xRec.SetGenreSubType(std::stoi(v[14])); + if (_addon.GetSettings().GetEnableMultiResume()) { - xRec.iLastPlayedPosition = atoi(v[15].c_str()); + xRec.SetLastPlayedPosition(std::stoi(v[15])); if (v.size() > 24) { - xRec.iPlayCount = atoi(v[24].c_str()); + xRec.SetPlayCount(std::stoi(v[24])); } } // Kodi PVR API 1.9.5 adds EPG ID field if (v.size() > 19) { - xRec.iEpgEventId = atoi(v[18].c_str()); + xRec.SetEPGEventId(std::stoul(v[18])); } // Kodi PVR API 5.0.0 adds EPG ID field if (v.size() > 18) { - xRec.iChannelUid = atoi(v[17].c_str()); + xRec.SetChannelUid(std::stoi(v[17])); } else { - xRec.iChannelUid = PVR_CHANNEL_INVALID_UID; + xRec.SetChannelUid(PVR_CHANNEL_INVALID_UID); } /* TODO: PVR API 5.1.0: Implement this */ - xRec.channelType = PVR_RECORDING_CHANNEL_TYPE_UNKNOWN; + xRec.SetChannelType(PVR_RECORDING_CHANNEL_TYPE_UNKNOWN); - PVR->TransferRecordingEntry(handle, &xRec); + results.Add(xRec); } - _lastRecordingUpdateTime = P8PLATFORM::GetTimeMs(); + _lastRecordingUpdateTime = std::chrono::high_resolution_clock::now(); return PVR_ERROR_NO_ERROR; } -PVR_ERROR Pvr2Wmc::DeleteRecording(const PVR_RECORDING& recording) +PVR_ERROR Pvr2Wmc::DeleteRecording(const kodi::addon::PVRRecording& recording) { if (IsServerDown()) return PVR_ERROR_SERVER_ERROR; std::string command; - command = - string_format("DeleteRecording|%s|%s|%s", recording.strRecordingId, recording.strTitle, ""); + command = Utils::Format("DeleteRecording|%s|%s|%s", recording.GetRecordingId().c_str(), + recording.GetTitle().c_str(), ""); - vector results = _socketClient.GetVector(command, false); // get results from server + std::vector results = + _socketClient.GetVector(command, false); // get results from server if (isServerError(results)) // did the server do it? @@ -1195,27 +1173,30 @@ PVR_ERROR Pvr2Wmc::DeleteRecording(const PVR_RECORDING& recording) else { TriggerUpdates(results); - //PVR->TriggerRecordingUpdate(); // tell xbmc to update recording display - XBMC->Log(LOG_DEBUG, "deleted recording '%s'", recording.strTitle); + // kodi::addon::CInstancePVRClient::TriggerRecordingUpdate(); // tell xbmc to update recording + // display + kodi::Log(ADDON_LOG_DEBUG, "deleted recording '%s'", recording.GetTitle().c_str()); - //if (results.size() == 2 && results[0] == "updateTimers") // if deleted recording was actually recording a the time - // PVR->TriggerTimerUpdate(); // update timer display too + // if (results.size() == 2 && results[0] == "updateTimers") // if deleted recording was actually + // recording a the time + // kodi::addon::CInstancePVRClient::TriggerTimerUpdate(); // update timer display too return PVR_ERROR_NO_ERROR; } } - -PVR_ERROR Pvr2Wmc::RenameRecording(const PVR_RECORDING& recording) +PVR_ERROR Pvr2Wmc::RenameRecording(const kodi::addon::PVRRecording& recording) { if (IsServerDown()) return PVR_ERROR_SERVER_ERROR; - std::string - command; // = format("RenameRecording|%s|%s", recording.strRecordingId, recording.strTitle); - command = string_format("RenameRecording|%s|%s", recording.strRecordingId, recording.strTitle); + std::string command; // = format("RenameRecording|%s|%s", recording.GetRecordingId().c_str(), + // recording.GetTitle().c_str()); + command = Utils::Format("RenameRecording|%s|%s", recording.GetRecordingId().c_str(), + recording.GetTitle().c_str()); - vector results = _socketClient.GetVector(command, false); // get results from server + std::vector results = + _socketClient.GetVector(command, false); // get results from server if (isServerError(results)) // did the server do it? { @@ -1224,67 +1205,80 @@ PVR_ERROR Pvr2Wmc::RenameRecording(const PVR_RECORDING& recording) else { TriggerUpdates(results); - XBMC->Log(LOG_DEBUG, "deleted recording '%s'", recording.strTitle); + kodi::Log(ADDON_LOG_DEBUG, "deleted recording '%s'", recording.GetTitle().c_str()); return PVR_ERROR_NO_ERROR; } } // set the recording resume position in the wmc database -PVR_ERROR Pvr2Wmc::SetRecordingLastPlayedPosition(const PVR_RECORDING& recording, +PVR_ERROR Pvr2Wmc::SetRecordingLastPlayedPosition(const kodi::addon::PVRRecording& recording, int lastplayedposition) { + if (!_addon.GetSettings().GetEnableMultiResume()) + return PVR_ERROR_NOT_IMPLEMENTED; if (IsServerDown()) return PVR_ERROR_SERVER_ERROR; std::string command; - command = string_format("SetResumePosition|%s|%d", recording.strRecordingId, lastplayedposition); + command = Utils::Format("SetResumePosition|%s|%d", recording.GetRecordingId().c_str(), + lastplayedposition); - vector results = _socketClient.GetVector(command, true); - PVR->TriggerRecordingUpdate(); // this is needed to get the new resume point actually used by the player (xbmc bug) + std::vector results = _socketClient.GetVector(command, true); + kodi::addon::CInstancePVRClient::TriggerRecordingUpdate(); // this is needed to get the new resume + // point actually used by the player + // (xbmc bug) return PVR_ERROR_NO_ERROR; } // get the rercording resume position from the wmc database -// note: although this resume point time will be displayed to the user in the gui (in the resume dlog) -// the return value is ignored by the xbmc player. That's why TriggerRecordingUpdate is required in the setting above -int Pvr2Wmc::GetRecordingLastPlayedPosition(const PVR_RECORDING& recording) +// note: although this resume point time will be displayed to the user in the gui (in the resume +// dlog) the return value is ignored by the xbmc player. That's why TriggerRecordingUpdate is +// required in the setting above +PVR_ERROR Pvr2Wmc::GetRecordingLastPlayedPosition(const kodi::addon::PVRRecording& recording, + int& position) { + if (!_addon.GetSettings().GetEnableMultiResume()) + return PVR_ERROR_NOT_IMPLEMENTED; if (IsServerDown()) return PVR_ERROR_SERVER_ERROR; std::string command; - command = string_format("GetResumePosition|%s", recording.strRecordingId); - int pos = _socketClient.GetInt(command, true); - return pos; + command = Utils::Format("GetResumePosition|%s", recording.GetRecordingId().c_str()); + position = _socketClient.GetInt(command, true); + return PVR_ERROR_NO_ERROR; } // set the recording playcount in the wmc database -PVR_ERROR Pvr2Wmc::SetRecordingPlayCount(const PVR_RECORDING& recording, int count) +PVR_ERROR Pvr2Wmc::SetRecordingPlayCount(const kodi::addon::PVRRecording& recording, int count) { + if (!_addon.GetSettings().GetEnableMultiResume()) + return PVR_ERROR_NOT_IMPLEMENTED; if (IsServerDown()) return PVR_ERROR_SERVER_ERROR; std::string command; - command = string_format("SetPlayCount|%s|%d", recording.strRecordingId, count); - vector results = _socketClient.GetVector(command, true); + command = Utils::Format("SetPlayCount|%s|%d", recording.GetRecordingId().c_str(), count); + std::vector results = _socketClient.GetVector(command, true); if (count <= 0) - PVR->TriggerRecordingUpdate(); // this is needed to get the new play count actually used by the player (xbmc bug) + kodi::addon::CInstancePVRClient::TriggerRecordingUpdate(); // this is needed to get the new play + // count actually used by the player + // (xbmc bug) return PVR_ERROR_NO_ERROR; } -std::string Pvr2Wmc::Channel2String(const PVR_CHANNEL& xCh) +std::string Pvr2Wmc::Channel2String(const kodi::addon::PVRChannel& xCh) { // packing: id, bradio, c.OriginalNumber, c.CallSign, c.IsEncrypted, imageStr, c.IsBlocked std::string chStr; - chStr = string_format("|%u|%d|%u|%s", xCh.iUniqueId, xCh.bIsRadio, xCh.iChannelNumber, - xCh.strChannelName); + chStr = Utils::Format("|%u|%d|%u|%s", xCh.GetUniqueId(), xCh.GetIsRadio(), xCh.GetChannelNumber(), + xCh.GetChannelName().c_str()); return chStr; } // live/recorded stream functions -------------------------------------------------------------- -bool Pvr2Wmc::OpenLiveStream(const PVR_CHANNEL& channel) +bool Pvr2Wmc::OpenLiveStream(const kodi::addon::PVRChannel& channel) { if (IsServerDown()) return false; @@ -1293,12 +1287,13 @@ bool Pvr2Wmc::OpenLiveStream(const PVR_CHANNEL& channel) _readCnt = 0; _buffTimesCnt = 0; _buffTimeFILTER = 0; + _bRecordingPlayback = false; - CloseLiveStream(false); // close current stream (if any) + CloseStream(false); // close current stream (if any) std::string request = "OpenLiveStream" + Channel2String(channel); // request a live stream using channel - vector results = + std::vector results = _socketClient.GetVector(request, false); // try to open live stream, get path to stream file if (isServerError(results)) // test if server reported an error @@ -1308,37 +1303,37 @@ bool Pvr2Wmc::OpenLiveStream(const PVR_CHANNEL& channel) else { _streamFileName = results[0]; // get path of streamed file - _streamWTV = EndsWith(results[0], "wtv"); // true if stream file is a wtv file + _streamWTV = Utils::EndsWith(results[0], "wtv"); // true if stream file is a wtv file if (results.size() > 1) - XBMC->Log(LOG_DEBUG, "OpenLiveStream> opening stream: %s", + kodi::Log(ADDON_LOG_DEBUG, "OpenLiveStream> opening stream: %s", results[1].c_str()); // log password safe path of client if available else - XBMC->Log(LOG_DEBUG, "OpenLiveStream> opening stream: %s", _streamFileName.c_str()); + kodi::Log(ADDON_LOG_DEBUG, "OpenLiveStream> opening stream: %s", _streamFileName.c_str()); // Initialise variables for starting stream at an offset _initialStreamResetCnt = 0; _initialStreamPosition = 0; - // Check for a specified initial position and save it for the first ReadLiveStream command to use + // Check for a specified initial position and save it for the first ReadLiveStream command to + // use if (results.size() > 2) { - _initialStreamPosition = atoll(results[2].c_str()); + _initialStreamPosition = std::stoll(results[2]); } - _streamFile = XBMC->OpenFile(_streamFileName.c_str(), - 0); // open the video file for streaming, same handle + _streamFile.OpenFile(_streamFileName); // open the video file for streaming, same handle - if (!_streamFile) // something went wrong + if (!_streamFile.IsOpen()) // something went wrong { std::string lastError; #ifdef TARGET_WINDOWS int errorVal = GetLastError(); - lastError = string_format("Error opening stream file, Win32 error code: %d", errorVal); + lastError = Utils::Format("Error opening stream file, Win32 error code: %d", errorVal); #else lastError = "Error opening stream file"; #endif - XBMC->Log(LOG_ERROR, lastError.c_str()); // log more info on error + kodi::Log(ADDON_LOG_ERROR, lastError.c_str()); // log more info on error _socketClient.GetBool("StreamStartError|" + _streamFileName, true); // tell server stream did not start @@ -1348,7 +1343,7 @@ bool Pvr2Wmc::OpenLiveStream(const PVR_CHANNEL& channel) else { _discardSignalStatus = false; // reset signal status discard flag - XBMC->Log(LOG_DEBUG, "OpenLiveStream> stream file opened successfully"); + kodi::Log(ADDON_LOG_DEBUG, "OpenLiveStream> stream file opened successfully"); } _lostStream = false; // if we got to here, stream started ok, so set default values @@ -1360,10 +1355,10 @@ bool Pvr2Wmc::OpenLiveStream(const PVR_CHANNEL& channel) // read from the live stream file opened in OpenLiveStream -int Pvr2Wmc::ReadLiveStream(unsigned char* pBuffer, unsigned int iBufferSize) +int Pvr2Wmc::ReadStream(unsigned char* pBuffer, unsigned int iBufferSize) { if (_lostStream) // if stream has already been flagged as lost, return 0 bytes - return 0; // after this happens a few time, xbmc will call CloseLiveStream + return 0; // after this happens a few time, xbmc will call CloseStream _readCnt++; // keep a count of the number of reads executed if (!_streamWTV) // if NOT streaming wtv, make sure stream is big enough before it is read @@ -1371,31 +1366,36 @@ int Pvr2Wmc::ReadLiveStream(unsigned char* pBuffer, unsigned int iBufferSize) int timeout = 0; // reset timeout counter - // If we are trying to skip to an initial start position (eg we are watching an existing live stream - // in a multiple client scenario), we need to do it here, as the Seek command didnt work in OpenLiveStream, - // XBMC just started playing from the start of the file anyway. But once the stream is open, XBMC repeatedly - // calls ReadLiveStream and a Seek() command done here DOES get actioned. + // If we are trying to skip to an initial start position (eg we are watching an existing live + // stream in a multiple client scenario), we need to do it here, as the Seek command didnt work + // in OpenLiveStream, XBMC just started playing from the start of the file anyway. But once the + // stream is open, XBMC repeatedly calls ReadLiveStream and a Seek() command done here DOES get + // actioned. // // So the first time we come in here, we can Seek() to our desired start offset. // - // However I found the XBMC demuxer makes an initial pass first and then sets the position back to 0 again and makes a 2nd pass, - // So we actually need to Seek() to our initial start position more than once. Because there are other situations where we can end up - // at the start of the file (such as the user rewinding) the easiest option at this point is to simply assume the demuxer makes 2 passes, - // and to reset the Seek position twice before clearing the stored value and thus no longer performing the reset. - - // Seek to initial file position if OpenLiveStream stored a starting offset and we are at position 0 (start of the file) - if (_initialStreamPosition > 0 && PositionLiveStream() == 0) + // However I found the XBMC demuxer makes an initial pass first and then sets the position back + // to 0 again and makes a 2nd pass, So we actually need to Seek() to our initial start position + // more than once. Because there are other situations where we can end up at the start of the + // file (such as the user rewinding) the easiest option at this point is to simply assume the + // demuxer makes 2 passes, and to reset the Seek position twice before clearing the stored value + // and thus no longer performing the reset. + + // Seek to initial file position if OpenLiveStream stored a starting offset and we are at + // position 0 (start of the file) + if (_initialStreamPosition > 0 && PositionStream() == 0) { - long long newPosition = XBMC->SeekFile(_streamFile, _initialStreamPosition, SEEK_SET); + int64_t newPosition = _streamFile.Seek(_initialStreamPosition, SEEK_SET); if (newPosition == _initialStreamPosition) { - XBMC->Log(LOG_DEBUG, "ReadLiveStream> stream file seek to initial position %llu successful", + kodi::Log(ADDON_LOG_DEBUG, + "ReadLiveStream> stream file seek to initial position %llu successful", _initialStreamPosition); } else { - XBMC->Log( - LOG_DEBUG, + kodi::Log( + ADDON_LOG_DEBUG, "ReadLiveStream> stream file seek to initial position %llu failed (new position: %llu)", _initialStreamPosition, newPosition); } @@ -1408,8 +1408,8 @@ int Pvr2Wmc::ReadLiveStream(unsigned char* pBuffer, unsigned int iBufferSize) } } - long long currentPos = PositionLiveStream(); // get the current file position - long long fileSize = _lastStreamSize; // use the last fileSize found, rather than querying host + int64_t currentPos = PositionStream(); // get the current file position + int64_t fileSize = _lastStreamSize; // use the last fileSize found, rather than querying host if (_isStreamFileGrowing && currentPos + iBufferSize > fileSize) // if its not big enough for the readsize @@ -1419,7 +1419,8 @@ int Pvr2Wmc::ReadLiveStream(unsigned char* pBuffer, unsigned int iBufferSize) // if its not, wait until it is while (_isStreamFileGrowing && currentPos + iBufferSize > fileSize) { - usleep(600000); // wait a little (600ms) before we try again + std::this_thread::sleep_for( + std::chrono::milliseconds(600)); // wait a little (600ms) before we try again timeout++; fileSize = ActualFileSize(timeout); // get new file size after delay @@ -1436,9 +1437,9 @@ int Pvr2Wmc::ReadLiveStream(unsigned char* pBuffer, unsigned int iBufferSize) else if (fileSize == -1) // if fileSize -1, server is reporting an 'unkown' error with the stream { - XBMC->QueueNotification( - QUEUE_ERROR, XBMC->GetLocalizedString(30003)); // display generic error with stream - XBMC->Log(LOG_DEBUG, "live tv error, server reported error"); + kodi::QueueNotification( + QUEUE_ERROR, "", kodi::GetLocalizedString(30003)); // display generic error with stream + kodi::Log(ADDON_LOG_DEBUG, "live tv error, server reported error"); _lostStream = true; // flag that stream is down return -1; } @@ -1448,14 +1449,14 @@ int Pvr2Wmc::ReadLiveStream(unsigned char* pBuffer, unsigned int iBufferSize) _lostStream = true; // flag that stream is down if (currentPos == 0 && fileSize == 0) // if no data was ever read, assume no video signal { - XBMC->QueueNotification(QUEUE_ERROR, XBMC->GetLocalizedString(30004)); - XBMC->Log(LOG_DEBUG, "no video found for stream"); + kodi::QueueNotification(QUEUE_ERROR, "", kodi::GetLocalizedString(30004)); + kodi::Log(ADDON_LOG_DEBUG, "no video found for stream"); } else // a mysterious reason caused timeout { - XBMC->QueueNotification( - QUEUE_ERROR, XBMC->GetLocalizedString(30003)); // otherwise display generic error - XBMC->Log(LOG_DEBUG, "live tv timed out, unknown reason"); + kodi::QueueNotification( + QUEUE_ERROR, "", kodi::GetLocalizedString(30003)); // otherwise display generic error + kodi::Log(ADDON_LOG_DEBUG, "live tv timed out, unknown reason"); } return -1; // this makes xbmc call closelivestream } @@ -1463,7 +1464,7 @@ int Pvr2Wmc::ReadLiveStream(unsigned char* pBuffer, unsigned int iBufferSize) } // !_streamWTV // finally, read data from stream file - unsigned int lpNumberOfBytesRead = XBMC->ReadFile(_streamFile, pBuffer, iBufferSize); + unsigned int lpNumberOfBytesRead = _streamFile.Read(pBuffer, iBufferSize); return lpNumberOfBytesRead; } @@ -1476,41 +1477,38 @@ bool Pvr2Wmc::CheckErrorOnServer() { std::string request; request = "CheckError"; - vector results = + std::vector results = _socketClient.GetVector(request, true); // see if server posted an error for active stream return isServerError(results); } return false; } -//#define SEEK_SET 0 -//#define SEEK_CUR 1 -//#define SEEK_END 2 -long long Pvr2Wmc::SeekLiveStream(long long iPosition, int iWhence /* = SEEK_SET */) +int64_t Pvr2Wmc::SeekStream(int64_t iPosition, int iWhence /* = SEEK_SET */) { int64_t lFilePos = 0; - if (_streamFile != 0) + if (_streamFile.IsOpen()) { - lFilePos = XBMC->SeekFile(_streamFile, iPosition, iWhence); + lFilePos = _streamFile.Seek(iPosition, iWhence); } return lFilePos; } // return the current file pointer position -long long Pvr2Wmc::PositionLiveStream(void) +int64_t Pvr2Wmc::PositionStream() { int64_t lFilePos = -1; - if (_streamFile != 0) + if (_streamFile.IsOpen()) { - lFilePos = XBMC->GetFilePosition(_streamFile); + lFilePos = _streamFile.GetPosition(); } return lFilePos; } // get stream file size, querying it from server if needed -long long Pvr2Wmc::ActualFileSize(int count) +int64_t Pvr2Wmc::ActualFileSize(int count) { - long long lFileSize = 0; + int64_t lFileSize = 0; if (_lostStream) // if stream was lost, return 0 file size (may not be needed) return 0; @@ -1522,13 +1520,13 @@ long long Pvr2Wmc::ActualFileSize(int count) else { std::string request; - request = string_format( + request = Utils::Format( "StreamFileSize|%d", count); // request stream size form client, passing number of consecutive queries lFileSize = _socketClient.GetLL(request, true); // get file size form client - if (lFileSize < - -1) // if server returns a negative file size, it means the stream file is no longer growing (-1 => error) + if (lFileSize < -1) // if server returns a negative file size, it means the stream file is no + // longer growing (-1 => error) { lFileSize = -lFileSize; // make stream file length positive _isStreamFileGrowing = false; // flag that stream file is no longer growing @@ -1539,40 +1537,35 @@ long long Pvr2Wmc::ActualFileSize(int count) } // return the length of the current stream file -long long Pvr2Wmc::LengthLiveStream(void) +int64_t Pvr2Wmc::LengthStream() { if (_lastStreamSize > 0) return _lastStreamSize; return -1; } -void Pvr2Wmc::PauseStream(bool bPaused) -{ -} - -bool Pvr2Wmc::CloseLiveStream(bool notifyServer /*=true*/) +bool Pvr2Wmc::CloseStream(bool notifyServer /*=true*/) { if (IsServerDown()) return false; - if (_streamFile != 0) // if file is still open, close it - XBMC->CloseFile(_streamFile); + if (_streamFile.IsOpen()) // if file is still open, close it + _streamFile.Close(); - _streamFile = 0; // file handle now closed _streamFileName = ""; _lostStream = true; // for cleanliness + _bRecordingPlayback = false; if (notifyServer) { - return _socketClient.GetBool("CloseLiveStream", false); // tell server to close down stream + return _socketClient.GetBool("CloseStream", false); // tell server to close down stream } else return true; } - -bool Pvr2Wmc::OpenRecordedStream(const PVR_RECORDING& recording) +bool Pvr2Wmc::OpenRecordedStream(const kodi::addon::PVRRecording& recording) { if (IsServerDown()) return false; @@ -1581,11 +1574,12 @@ bool Pvr2Wmc::OpenRecordedStream(const PVR_RECORDING& recording) _readCnt = 0; _buffTimesCnt = 0; _buffTimeFILTER = 0; + _bRecordingPlayback = true; // request an active recording stream std::string request; - request = string_format("OpenRecordingStream|%s", recording.strRecordingId); - vector results = _socketClient.GetVector( + request = Utils::Format("OpenRecordingStream|%s", recording.GetRecordingId().c_str()); + std::vector results = _socketClient.GetVector( request, false); // try to open recording stream, get path to stream file if (isServerError(results)) // test if server reported an error @@ -1595,38 +1589,37 @@ bool Pvr2Wmc::OpenRecordedStream(const PVR_RECORDING& recording) else { _streamFileName = results[0]; - _streamWTV = EndsWith(_streamFileName, "wtv"); // true if stream file is a wtv file + _streamWTV = Utils::EndsWith(_streamFileName, "wtv"); // true if stream file is a wtv file // handle additional args from server if (results.size() > 1) - XBMC->Log(LOG_DEBUG, "OpenRecordedStream> rec stream type: %s", + kodi::Log(ADDON_LOG_DEBUG, "OpenRecordedStream> rec stream type: %s", results[1].c_str()); // either a 'passive' or 'active' WTV OR a TS file if (results.size() > 2) - XBMC->Log(LOG_DEBUG, "OpenRecordedStream> opening stream: %s", + kodi::Log(ADDON_LOG_DEBUG, "OpenRecordedStream> opening stream: %s", results[2].c_str()); // log password safe path of client if available else - XBMC->Log(LOG_DEBUG, "OpenRecordedStream> opening stream: %s", _streamFileName.c_str()); + kodi::Log(ADDON_LOG_DEBUG, "OpenRecordedStream> opening stream: %s", _streamFileName.c_str()); - _streamFile = XBMC->OpenFile(_streamFileName.c_str(), - 0); // open the video file for streaming, same handle + _streamFile.OpenFile(_streamFileName); // open the video file for streaming, same handle - if (!_streamFile) // something went wrong + if (!_streamFile.IsOpen()) // something went wrong { std::string lastError; #ifdef TARGET_WINDOWS int errorVal = GetLastError(); - lastError = string_format("Error opening stream file, Win32 error code: %d", errorVal); + lastError = Utils::Format("Error opening stream file, Win32 error code: %d", errorVal); #else lastError = "Error opening stream file"; #endif - XBMC->Log(LOG_ERROR, lastError.c_str()); // log more info on error + kodi::Log(ADDON_LOG_ERROR, lastError.c_str()); // log more info on error _socketClient.GetBool("StreamStartError|" + _streamFileName, true); // tell server stream did not start return false; } else - XBMC->Log(LOG_DEBUG, "OpenRecordedStream> stream file opened successfully"); + kodi::Log(ADDON_LOG_DEBUG, "OpenRecordedStream> stream file opened successfully"); _lostStream = false; // stream is open _lastStreamSize = 0; // current size is empty @@ -1642,14 +1635,14 @@ bool Pvr2Wmc::OpenRecordedStream(const PVR_RECORDING& recording) } } -PVR_ERROR Pvr2Wmc::SignalStatus(PVR_SIGNAL_STATUS* signalStatus) +PVR_ERROR Pvr2Wmc::GetSignalStatus(int channelUid, kodi::addon::PVRSignalStatus& signalStatus) { - if (!g_bSignalEnable || _discardSignalStatus) + if (!_addon.GetSettings().GetSignalEnable() || _discardSignalStatus) { return PVR_ERROR_NO_ERROR; } - static PVR_SIGNAL_STATUS cachedSignalStatus; + static kodi::addon::PVRSignalStatus cachedSignalStatus; // Only send request to backend every N times if (_signalStatusCount-- <= 0) @@ -1658,12 +1651,13 @@ PVR_ERROR Pvr2Wmc::SignalStatus(PVR_SIGNAL_STATUS* signalStatus) return PVR_ERROR_SERVER_ERROR; // Reset count to throttle value - _signalStatusCount = g_signalThrottle; + _signalStatusCount = _addon.GetSettings().GetSignalThrottle(); std::string command; command = "SignalStatus"; - vector results = _socketClient.GetVector(command, true); // get results from server + std::vector results = + _socketClient.GetVector(command, true); // get results from server // strDeviceName, strDeviceStatus, strProvider, strService, strMux // iSignal, dVideoBitrate, dAudioBitrate, Error @@ -1676,20 +1670,14 @@ PVR_ERROR Pvr2Wmc::SignalStatus(PVR_SIGNAL_STATUS* signalStatus) { if (results.size() >= 9) { - memset(&cachedSignalStatus, 0, sizeof(cachedSignalStatus)); - snprintf(cachedSignalStatus.strAdapterName, sizeof(cachedSignalStatus.strAdapterName), "%s", - results[0].c_str()); - snprintf(cachedSignalStatus.strAdapterStatus, sizeof(cachedSignalStatus.strAdapterStatus), - "%s", results[1].c_str()); - snprintf(cachedSignalStatus.strProviderName, sizeof(cachedSignalStatus.strProviderName), - "%s", results[2].c_str()); - snprintf(cachedSignalStatus.strServiceName, sizeof(cachedSignalStatus.strServiceName), "%s", - results[3].c_str()); - snprintf(cachedSignalStatus.strMuxName, sizeof(cachedSignalStatus.strMuxName), "%s", - results[4].c_str()); - cachedSignalStatus.iSignal = (int)(atoi(results[5].c_str()) * 655.35); - - bool error = atoi(results[8].c_str()) == 1; + cachedSignalStatus.SetAdapterName(results[0]); + cachedSignalStatus.SetAdapterStatus(results[1]); + cachedSignalStatus.SetProviderName(results[2]); + cachedSignalStatus.SetServiceName(results[3]); + cachedSignalStatus.SetMuxName(results[4]); + cachedSignalStatus.SetSignal((int)(std::stoi(results[5]) * 655.35)); + + bool error = std::stoi(results[8]) == 1; if (error) { // Backend indicates it can't provide SignalStatus for this channel @@ -1700,32 +1688,18 @@ PVR_ERROR Pvr2Wmc::SignalStatus(PVR_SIGNAL_STATUS* signalStatus) } } - *signalStatus = cachedSignalStatus; + signalStatus = cachedSignalStatus; return PVR_ERROR_NO_ERROR; } - -bool Pvr2Wmc::IsTimeShifting() -{ - if (_streamFile) - return true; - else - return false; -} - -// save the last pos of buffer pointers so that we can filter how often swmc is quered for buff data -time_t _savBuffStart; -int64_t _savBuffEnd; - - -PVR_ERROR Pvr2Wmc::GetStreamTimes(PVR_STREAM_TIMES* strTimes) +PVR_ERROR Pvr2Wmc::GetStreamTimes(kodi::addon::PVRStreamTimes& times) { - if (_streamFile) + if (_streamFile.IsOpen()) { if (_buffTimesCnt >= _buffTimeFILTER) // filter queries to slow down queries to swmc { _buffTimesCnt = 0; - vector results = + std::vector results = _socketClient.GetVector("GetBufferTimes", false); // get buffer status if (results.size() < 3) @@ -1733,24 +1707,24 @@ PVR_ERROR Pvr2Wmc::GetStreamTimes(PVR_STREAM_TIMES* strTimes) return PVR_ERROR_SERVER_ERROR; } - strTimes->startTime = atoll(results[0].c_str()); // get time_t utc of when stream was started - strTimes->ptsStart = 0; // relative to the above time, time when the stream starts (?) - strTimes->ptsBegin = - 0; // how far back the buffer data goes, which is always stream start for swmc - strTimes->ptsEnd = - atoll(results[1].c_str()) * - DVD_TIME_BASE; // get the current length of the live buffer or recording duration (uSec) - _savBuffStart = strTimes->startTime; // save values last found to filter queries - _savBuffEnd = strTimes->ptsEnd; - _buffTimeFILTER = atol(results[2].c_str()); // get filter value from swmc + times.SetStartTime(std::stoll(results[0])); // get time_t utc of when stream was started + times.SetPTSStart(0); // relative to the above time, time when the stream starts (?) + times.SetPTSBegin( + 0); // how far back the buffer data goes, which is always stream start for swmc + times.SetPTSEnd( + std::stoll(results[1]) * + DVD_TIME_BASE); // get the current length of the live buffer or recording duration (uSec) + _savBuffStart = times.GetStartTime(); // save values last found to filter queries + _savBuffEnd = times.GetPTSEnd(); + _buffTimeFILTER = std::stol(results[2]); // get filter value from swmc } else { // if filtering, used saved values - strTimes->startTime = _savBuffStart; - strTimes->ptsStart = 0; - strTimes->ptsBegin = 0; - strTimes->ptsEnd = _savBuffEnd; + times.SetStartTime(_savBuffStart); + times.SetPTSStart(0); + times.SetPTSBegin(0); + times.SetPTSEnd(_savBuffEnd); _buffTimesCnt++; // increment how many times saved values were used } return PVR_ERROR_NO_ERROR; @@ -1758,12 +1732,10 @@ PVR_ERROR Pvr2Wmc::GetStreamTimes(PVR_STREAM_TIMES* strTimes) return PVR_ERROR_SERVER_ERROR; } -PVR_ERROR Pvr2Wmc::GetRecordingEdl(const PVR_RECORDING& recording, - PVR_EDL_ENTRY entries[], - int* size) +PVR_ERROR Pvr2Wmc::GetRecordingEdl(const kodi::addon::PVRRecording& recording, + std::vector& edl) { - - if (_streamFileName != "") // read the edl for the current stream file + if (!_streamFileName.empty()) // read the edl for the current stream file { // see if edl file for currently streaming recording exists std::string theEdlFile = _streamFileName; @@ -1773,43 +1745,40 @@ PVR_ERROR Pvr2Wmc::GetRecordingEdl(const PVR_RECORDING& recording, theEdlFile.erase(result); else { - XBMC->Log(LOG_DEBUG, "File extender error: '%s'", theEdlFile.c_str()); + kodi::Log(ADDON_LOG_DEBUG, "File extender error: '%s'", theEdlFile.c_str()); return PVR_ERROR_FAILED; } theEdlFile.append(".edl"); - XBMC->Log(LOG_DEBUG, "Opening EDL file: '%s'", theEdlFile.c_str()); + kodi::Log(ADDON_LOG_DEBUG, "Opening EDL file: '%s'", theEdlFile.c_str()); - void* fileHandle = XBMC->OpenFile(theEdlFile.c_str(), 0); - if (fileHandle) + kodi::vfs::CFile fileHandle; + if (fileHandle.OpenFile(theEdlFile)) { - int index = 0; - char buffer[256]; - while (XBMC->ReadFileString(fileHandle, buffer, 1024)) + std::string svals; + while (fileHandle.ReadLine(svals)) { - std::string svals(buffer); - svals = StringUtils::TrimRight(svals, "\r"); // trim windows /r if its there - vector vals = split(svals, "\t"); // split on tabs + size_t nidx = svals.find_last_not_of("\r"); + svals.erase(svals.npos == nidx ? 0 : ++nidx); // trim windows /r if its there + + std::vector vals = Utils::Split(svals, "\t"); // split on tabs if (vals.size() == 3) { - PVR_EDL_ENTRY entry; - entry.start = std::strtod(vals[0].c_str(), null) * 1000; // convert s to ms - entry.end = std::strtod(vals[1].c_str(), null) * 1000; - entry.type = (PVR_EDL_TYPE)atoi(vals[2].c_str()); - entries[index] = entry; - index++; + kodi::addon::PVREDLEntry entry; + entry.SetStart(int64_t(std::stold(vals[0]) * 1000)); // convert s to ms + entry.SetEnd(int64_t(std::stold(vals[1]) * 1000)); + entry.SetType(PVR_EDL_TYPE(std::stoi(vals[2]))); + edl.emplace_back(entry); } } - if (index > 0) - XBMC->Log(LOG_DEBUG, "EDL data found."); + if (!edl.empty()) + kodi::Log(ADDON_LOG_DEBUG, "EDL data found."); else - XBMC->Log(LOG_DEBUG, "No EDL data found."); - *size = index; - XBMC->CloseFile(fileHandle); + kodi::Log(ADDON_LOG_DEBUG, "No EDL data found."); return PVR_ERROR_NO_ERROR; } else - XBMC->Log(LOG_DEBUG, "No EDL file found."); + kodi::Log(ADDON_LOG_DEBUG, "No EDL file found."); } return PVR_ERROR_FAILED; } diff --git a/src/pvr2wmc.h b/src/pvr2wmc.h index 529f1b2..49b2b89 100644 --- a/src/pvr2wmc.h +++ b/src/pvr2wmc.h @@ -9,9 +9,12 @@ #pragma once #include "Socket.h" -#include "client.h" +#include "addon.h" +#include "settings.h" -#include +#include +#include +#include /* timer type ids */ #define TIMER_MANUAL_MIN (PVR_TIMER_TYPE_NONE + 1) @@ -66,107 +69,168 @@ typedef enum WMC_LIMIT_10 = 10 } wmc_recordinglimit_t; -class Pvr2Wmc +enum backend_status +{ + BACKEND_UNKNOWN, + BACKEND_DOWN, + BACKEND_UP +}; + +class ATTRIBUTE_HIDDEN Pvr2Wmc : public kodi::addon::CInstancePVRClient { public: - Pvr2Wmc(void); - virtual ~Pvr2Wmc(void); + Pvr2Wmc(CPvr2WmcAddon& addon, KODI_HANDLE instance, const std::string& kodiVersion); + ~Pvr2Wmc() override = default; - virtual bool IsServerDown(); - virtual void UnLoading(); - const char* GetBackendVersion(void); - PVR_ERROR GetTimerTypes(PVR_TIMER_TYPE types[], int* size); - virtual PVR_ERROR GetDriveSpace(long long* iTotal, long long* iUsed); + bool IsServerDown(); + void UnLoading(); + + PVR_ERROR GetCapabilities(kodi::addon::PVRCapabilities& capabilities) override; + PVR_ERROR GetBackendName(std::string& name) override; + PVR_ERROR GetBackendVersion(std::string& version) override; + PVR_ERROR GetBackendHostname(std::string& hostname) override; + PVR_ERROR GetConnectionString(std::string& connection) override; + PVR_ERROR GetDriveSpace(uint64_t& total, uint64_t& used) override; // channels - virtual int GetChannelsAmount(void); - virtual PVR_ERROR GetChannels(ADDON_HANDLE handle, bool bRadio); + PVR_ERROR GetChannelsAmount(int& amount) override; + PVR_ERROR GetChannels(bool radio, kodi::addon::PVRChannelsResultSet& results) override; + + PVR_ERROR GetChannelGroupsAmount(int& amount) override; + PVR_ERROR GetChannelGroups(bool radio, kodi::addon::PVRChannelGroupsResultSet& results) override; + PVR_ERROR GetChannelGroupMembers(const kodi::addon::PVRChannelGroup& group, + kodi::addon::PVRChannelGroupMembersResultSet& results) override; - virtual int GetChannelGroupsAmount(void); - virtual PVR_ERROR GetChannelGroups(ADDON_HANDLE handle, bool bRadio); - virtual PVR_ERROR GetChannelGroupMembers(ADDON_HANDLE handle, const PVR_CHANNEL_GROUP& group); + PVR_ERROR GetSignalStatus(int channelUid, kodi::addon::PVRSignalStatus& signalStatus) override; // epg - virtual PVR_ERROR GetEPGForChannel(ADDON_HANDLE handle, - int iChannelUid, - time_t iStart, - time_t iEnd); + PVR_ERROR GetEPGForChannel(int channelUid, + time_t start, + time_t end, + kodi::addon::PVREPGTagsResultSet& results) override; // timers - virtual PVR_ERROR GetTimers(ADDON_HANDLE handle); - virtual PVR_ERROR AddTimer(const PVR_TIMER& timer); - virtual PVR_ERROR DeleteTimer(const PVR_TIMER& timer, bool bForceDelete); - virtual int GetTimersAmount(void); + PVR_ERROR GetTimerTypes(std::vector& types) override; + + PVR_ERROR GetTimers(kodi::addon::PVRTimersResultSet& results) override; + PVR_ERROR AddTimer(const kodi::addon::PVRTimer& timer) override; + PVR_ERROR DeleteTimer(const kodi::addon::PVRTimer& timer, bool forceDelete) override; + PVR_ERROR GetTimersAmount(int& amount) override; // recording files - virtual PVR_ERROR GetRecordings(ADDON_HANDLE handle); - PVR_ERROR DeleteRecording(const PVR_RECORDING& recording); - PVR_ERROR RenameRecording(const PVR_RECORDING& recording); - PVR_ERROR SetRecordingLastPlayedPosition(const PVR_RECORDING& recording, int lastplayedposition); - int GetRecordingLastPlayedPosition(const PVR_RECORDING& recording); - PVR_ERROR SetRecordingPlayCount(const PVR_RECORDING& recording, int count); + PVR_ERROR GetRecordingsAmount(bool deleted, int& amount) override; + PVR_ERROR GetRecordings(bool deleted, kodi::addon::PVRRecordingsResultSet& results) override; + PVR_ERROR DeleteRecording(const kodi::addon::PVRRecording& recording) override; + PVR_ERROR RenameRecording(const kodi::addon::PVRRecording& recording) override; + PVR_ERROR SetRecordingLastPlayedPosition(const kodi::addon::PVRRecording& recording, + int lastplayedposition) override; + PVR_ERROR GetRecordingLastPlayedPosition(const kodi::addon::PVRRecording& recording, + int& position) override; + PVR_ERROR SetRecordingPlayCount(const kodi::addon::PVRRecording& recording, int count) override; + + // comm skip + PVR_ERROR GetRecordingEdl(const kodi::addon::PVRRecording& recording, + std::vector& edl) override; // recording streams - bool OpenRecordedStream(const PVR_RECORDING& recording); - virtual int GetRecordingsAmount(void); - void UpdateRecordingTimer(int msec); + bool OpenRecordedStream(const kodi::addon::PVRRecording& recording) override; + void CloseRecordedStream() override { CloseStream(); } + int ReadRecordedStream(unsigned char* buffer, unsigned int size) override + { + return ReadStream(buffer, size); + } + int64_t SeekRecordedStream(int64_t position, int whence) override + { + return SeekStream(position, whence); + } + int64_t LengthRecordedStream() override { return LengthStream(); } // live tv - bool OpenLiveStream(const PVR_CHANNEL& channel); - bool CloseLiveStream(bool notifyServer = true); - int ReadLiveStream(unsigned char* pBuffer, unsigned int iBufferSize); - void PauseStream(bool bPaused); - long long SeekLiveStream(long long iPosition, int iWhence /* = SEEK_SET */); - long long PositionLiveStream(void); - bool SwitchChannel(const PVR_CHANNEL& channel); - long long LengthLiveStream(void); - long long ActualFileSize(int count); - PVR_ERROR SignalStatus(PVR_SIGNAL_STATUS* signalStatus); + bool OpenLiveStream(const kodi::addon::PVRChannel& channel) override; + void CloseLiveStream() override { CloseStream(); } + int ReadLiveStream(unsigned char* buffer, unsigned int size) override + { + return ReadStream(buffer, size); + } + int64_t SeekLiveStream(int64_t position, int whence) override + { + return SeekStream(position, whence); + } + int64_t LengthLiveStream() override { return LengthStream(); } // time shifting - bool IsTimeShifting(); - PVR_ERROR GetStreamTimes(PVR_STREAM_TIMES*); + PVR_ERROR GetStreamTimes(kodi::addon::PVRStreamTimes& times) override; + bool IsRealTimeStream() override { return !_bRecordingPlayback; } + bool CanPauseStream() override { return true; } + bool CanSeekStream() override { return true; } - // comm skip - PVR_ERROR GetRecordingEdl(const PVR_RECORDING&, PVR_EDL_ENTRY[], int*); + const CSettings& GetSettings() const { return _addon.GetSettings(); } - bool CheckErrorOnServer(); - void TriggerUpdates(std::vector results); - void ExtractDriveSpace(std::vector results); + void SetBackendStatus(backend_status status) { _BackendOnline = status; } + backend_status GetBackendStatus() const { return _BackendOnline; } private: int _serverBuild; - std::string Timer2String(const PVR_TIMER& xTmr); - std::string SeriesTimer2String(const PVR_TIMER& xTmr); - std::string Channel2String(const PVR_CHANNEL& xTmr); + std::string Timer2String(const kodi::addon::PVRTimer& xTmr); + std::string Channel2String(const kodi::addon::PVRChannel& xTmr); + + bool CloseStream(bool notifyServer = true); + int ReadStream(unsigned char* pBuffer, unsigned int iBufferSize); + int64_t SeekStream(int64_t iPosition, int iWhence /* = SEEK_SET */); + int64_t PositionStream(); + int64_t LengthStream(); + int64_t ActualFileSize(int count); + + bool CheckErrorOnServer(); + void TriggerUpdates(std::vector results); + void ExtractDriveSpace(std::vector results); Socket _socketClient; - long long _diskTotal; - long long _diskUsed; + uint64_t _diskTotal = 0; + uint64_t _diskUsed = 0; - int _signalStatusCount; // call backend for signal status every N calls (because XBMC calls every 1 second!) - bool - _discardSignalStatus; // flag to discard signal status for channels where the backend had an error + int _signalStatusCount = + 0; // call backend for signal status every N calls (because XBMC calls every 1 second!) + bool _discardSignalStatus = + false; // flag to discard signal status for channels where the backend had an error - void* _streamFile; // handle to a streamed file + kodi::vfs::CFile _streamFile; // handle to a streamed file std::string _streamFileName; // the name of the stream file - bool _lostStream; // set to true if stream is lost + bool _lostStream = false; // set to true if stream is lost + bool _bRecordingPlayback = false; - bool _streamWTV; // if true, stream wtv files - long long _lastStreamSize; // last value found for file stream - bool _isStreamFileGrowing; // true if server reports that a live/rec stream is still growing - long long _readCnt; // keep a count of the number of reads executed during playback + // save the last pos of buffer pointers so that we can filter how often swmc is quered for buff + // data + time_t _savBuffStart; + int64_t _savBuffEnd; - int _initialStreamResetCnt; // used to count how many times we reset the stream position (due to 2 pass demuxer) - long long - _initialStreamPosition; // used to set an initial position (multiple clients watching the same live tv buffer) + static long _buffTimesCnt; // filter how often we do buffer status reads + static long _buffTimeFILTER; - bool _insertDurationHeader; // if true, insert a duration header for active Rec TS file + bool _streamWTV = true; // if true, stream wtv files + int64_t _lastStreamSize = 0; // last value found for file stream + bool _isStreamFileGrowing = + false; // true if server reports that a live/rec stream is still growing + int64_t _readCnt = 0; // keep a count of the number of reads executed during playback + + int _initialStreamResetCnt = + 0; // used to count how many times we reset the stream position (due to 2 pass demuxer) + int64_t _initialStreamPosition = + 0; // used to set an initial position (multiple clients watching the same live tv buffer) + + bool _insertDurationHeader = false; // if true, insert a duration header for active Rec TS file std::string _durationHeader; // the header to insert (received from server) - int _defaultPriority; - int _defaultLiftetime; - int _defaultLimit; - int _defaultShowType; + int _defaultPriority = WMC_PRIORITY_NORMAL; + int _defaultLiftetime = WMC_LIFETIME_ELIGIBLE; + int _defaultLimit = WMC_LIMIT_ASMANY; + int _defaultShowType = WMC_SHOWTYPE_ANY; + + std::chrono::high_resolution_clock::time_point + _lastRecordingUpdateTime; // the time of the last recording display update + + backend_status _BackendOnline = BACKEND_DOWN; + + CPvr2WmcAddon& _addon; }; diff --git a/src/settings.cpp b/src/settings.cpp new file mode 100644 index 0000000..1e0ca97 --- /dev/null +++ b/src/settings.cpp @@ -0,0 +1,103 @@ +/* + * Copyright (C) 2020 Team Kodi (https://kodi.tv) + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSE.md for more information. + */ + +#include "settings.h" + +#include "utilities.h" + +#include + +bool CSettings::Load() +{ + /* Read setting "port" from settings.xml */ + if (!kodi::CheckSettingInt("port", _port)) + { + kodi::Log(ADDON_LOG_ERROR, "Couldn't get 'port' setting, using '%i'", DEFAULT_PORT); + } + + if (kodi::CheckSettingString("host", _strServerName)) + { + kodi::Log(ADDON_LOG_DEBUG, "Settings: host='%s', port=%i", _strServerName.c_str(), _port); + } + else + { + kodi::Log(ADDON_LOG_ERROR, "Couldn't get 'host' setting, using '127.0.0.1'"); + } + + if (!kodi::CheckSettingBoolean("wake_on_lan", _bWakeOnLAN)) + { + kodi::Log(ADDON_LOG_ERROR, "Couldn't get 'wake_on_lan' setting, using '%s'", + DEFAULT_WAKEONLAN_ENABLE); + } + + std::string fileContent; + if (Utils::ReadFileContents(_strAddonDataCustom, fileContent)) + { + _strServerMAC = fileContent; + kodi::Log(ADDON_LOG_ERROR, "Using ServerWMC MAC address from custom addondata '%s'", + _strServerMAC.c_str()); + } + else + { + kodi::Log(ADDON_LOG_ERROR, + "Couldn't get ServerWMC MAC address from custom addondata, using empty value"); + } + + if (!kodi::CheckSettingBoolean("signal", _bSignalEnable)) + { + kodi::Log(ADDON_LOG_ERROR, "Couldn't get 'signal' setting, using '%s'", DEFAULT_SIGNAL_ENABLE); + } + + if (!kodi::CheckSettingInt("signal_throttle", _signalThrottle)) + { + kodi::Log(ADDON_LOG_ERROR, "Couldn't get 'signal_throttle' setting, using '%s'", + DEFAULT_SIGNAL_THROTTLE); + } + + if (!kodi::CheckSettingBoolean("multiResume", _bEnableMultiResume)) + { + kodi::Log(ADDON_LOG_ERROR, "Couldn't get 'multiResume' setting, using '%s'", + DEFAULT_MULTI_RESUME); + } + + + // get the name of the computer client is running on + _strClientName = kodi::network::GetHostname(); + +#ifdef TARGET_WINDOWS + _clientOS = "windows"; // set to the client OS name +#elif defined TARGET_LINUX + _clientOS = "linux"; // set to the client OS name +#elif defined TARGET_DARWIN + _clientOS = "darwin"; // set to the client OS name +#elif defined TARGET_FREEBSD + _clientOS = "freeBSD"; // set to the client OS name +#else + _clientOS = ""; // set blank client OS name +#endif + + return true; +} + +ADDON_STATUS CSettings::SetSetting(const std::string& settingName, + const kodi::CSettingValue& settingValue) +{ + std::string sName = settingName; + + if (sName == "host") + { + std::string oldName = _strServerName; + _strServerName = settingValue.GetString(); + + kodi::Log(ADDON_LOG_INFO, "Setting 'host' changed from %s to %s", oldName.c_str(), + _strServerName.c_str()); + if (oldName != _strServerName) + return ADDON_STATUS_NEED_RESTART; + } + + return ADDON_STATUS_OK; +} diff --git a/src/settings.h b/src/settings.h new file mode 100644 index 0000000..2fc034b --- /dev/null +++ b/src/settings.h @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2020 Team Kodi (https://kodi.tv) + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSE.md for more information. + */ + +#pragma once + +#include + +#define LOCALHOST "127.0.0.1" + +#define DEFAULT_PORT 9080 +#define DEFAULT_WAKEONLAN_ENABLE false +#define DEFAULT_SIGNAL_ENABLE false +#define DEFAULT_SIGNAL_THROTTLE 10 +#define DEFAULT_MULTI_RESUME true + +class ATTRIBUTE_HIDDEN CSettings +{ +public: + CSettings() = default; + + bool Load(); + ADDON_STATUS SetSetting(const std::string& settingName, const kodi::CSettingValue& settingValue); + + const std::string& GetServerName() const { return _strServerName; } + const std::string& GetClientName() const { return _strClientName; } + const std::string& GetServerMAC() const { return _strServerMAC; } + void SetServerMAC(const std::string& strServerMAC) { _strServerMAC = strServerMAC; } + const std::string& GetClientOS() const { return _clientOS; } + bool GetWakeOnLAN() const { return _bWakeOnLAN; } + int GetPort() const { return _port; } + bool GetSignalEnable() const { return _bSignalEnable; } + int GetSignalThrottle() const { return _signalThrottle; } + bool GetEnableMultiResume() const { return _bEnableMultiResume; } + const std::string& GetAddonDataCustom() const { return _strAddonDataCustom; } + +private: + std::string _strServerName = LOCALHOST; // the name of the server to connect to + std::string _strClientName; // the name of the computer running addon + std::string _strServerMAC; // MAC address of server + std::string _clientOS; // OS of client, passed to server + bool _bWakeOnLAN = false; // whether to send wake on LAN to server + int _port = DEFAULT_PORT; + bool _bSignalEnable = DEFAULT_SIGNAL_ENABLE; + int _signalThrottle = DEFAULT_SIGNAL_THROTTLE; + bool _bEnableMultiResume = DEFAULT_MULTI_RESUME; + std::string _strAddonDataCustom; // location of custom addondata settings file +}; diff --git a/src/utilities.cpp b/src/utilities.cpp index b3671f5..6de77bf 100644 --- a/src/utilities.cpp +++ b/src/utilities.cpp @@ -8,17 +8,15 @@ #include "utilities.h" -#include "client.h" - #include +#include #include #include -using namespace ADDON; - #define FORMAT_BLOCK_SIZE 2048 // # of bytes to increment per try -using namespace Utils; +namespace Utils +{ // format related string functions taken from: // http://www.flipcode.com/archives/Safe_sprintf.shtml @@ -28,7 +26,9 @@ bool Str2Bool(const std::string& str) return str.compare("True") == 0 ? true : false; } -std::vector Split(const std::string& input, const std::string& delimiter, unsigned int iMaxStrings /* = 0 */) +std::vector Split(const std::string& input, + const std::string& delimiter, + unsigned int iMaxStrings /* = 0 */) { std::vector results; if (input.empty()) @@ -62,12 +62,12 @@ std::vector Split(const std::string& input, const std::string& deli if (iMaxStrings > 0 && numFound >= iMaxStrings) numFound = iMaxStrings - 1; - for ( unsigned int i = 0; i <= numFound; i++ ) + for (unsigned int i = 0; i <= numFound; i++) { std::string s; - if ( i == 0 ) + if (i == 0) { - if ( i == numFound ) + if (i == numFound) s = input; else s = input.substr(i, positions[i]); @@ -75,13 +75,12 @@ std::vector Split(const std::string& input, const std::string& deli else { size_t offset = positions[i - 1] + sizeS2; - if ( offset < isize ) + if (offset < isize) { - if ( i == numFound ) + if (i == numFound) s = input.substr(offset); - else if ( i > 0 ) - s = input.substr( positions[i - 1] + sizeS2, - positions[i] - positions[i - 1] - sizeS2 ); + else if (i > 0) + s = input.substr(positions[i - 1] + sizeS2, positions[i] - positions[i - 1] - sizeS2); } } results.push_back(s); @@ -89,7 +88,7 @@ std::vector Split(const std::string& input, const std::string& deli return results; } -std::string Format(const char *fmt, ...) +std::string Format(const char* fmt, ...) { va_list args; va_start(args, fmt); @@ -99,7 +98,7 @@ std::string Format(const char *fmt, ...) return str; } -std::string FormatV(const char *fmt, va_list args) +std::string FormatV(const char* fmt, va_list args) { if (fmt == nullptr) return ""; @@ -107,7 +106,7 @@ std::string FormatV(const char *fmt, va_list args) int size = FORMAT_BLOCK_SIZE; va_list argCopy; - char *cstr = reinterpret_cast(malloc(sizeof(char) * size)); + char* cstr = reinterpret_cast(malloc(sizeof(char) * size)); if (cstr == nullptr) return ""; @@ -124,12 +123,12 @@ std::string FormatV(const char *fmt, va_list args) free(cstr); return str; } - if (nActual > -1) // Exactly what we will need (glibc 2.1) + if (nActual > -1) // Exactly what we will need (glibc 2.1) size = nActual + 1; - else // Let's try to double the size (glibc 2.0) + else // Let's try to double the size (glibc 2.0) size *= 2; - char *new_cstr = reinterpret_cast(realloc(cstr, sizeof(char) * size)); + char* new_cstr = reinterpret_cast(realloc(cstr, sizeof(char) * size)); if (new_cstr == nullptr) { free(cstr); @@ -180,34 +179,34 @@ std::string GetDirectoryPath(std::string const& path) bool ReadFileContents(std::string const& strFileName, std::string& strContent) { - void* fileHandle = XBMC->OpenFile(strFileName.c_str(), 0); - if (fileHandle) + kodi::vfs::CFile fileHandle; + if (fileHandle.OpenFile(strFileName)) { - char buffer[1024]; - while (XBMC->ReadFileString(fileHandle, buffer, 1024)) + std::string buffer; + while (fileHandle.ReadLine(buffer)) strContent.append(buffer); - XBMC->CloseFile(fileHandle); return true; } return false; } -bool WriteFileContents(std::string const& strFileName, std::string& strContent) +bool WriteFileContents(std::string const& strFileName, const std::string& strContent) { - void* fileHandle = XBMC->OpenFileForWrite(strFileName.c_str(), true); - if (fileHandle) + kodi::vfs::CFile fileHandle; + if (fileHandle.OpenFileForWrite(strFileName, true)) { - int rc = XBMC->WriteFile(fileHandle, strContent.c_str(), strContent.length()); + int rc = fileHandle.Write(strContent.c_str(), strContent.length()); if (rc) { - XBMC->Log(LOG_DEBUG, "wrote file %s", strFileName.c_str()); + kodi::Log(ADDON_LOG_DEBUG, "wrote file %s", strFileName.c_str()); } else { - XBMC->Log(LOG_ERROR, "can not write to %s", strFileName.c_str()); + kodi::Log(ADDON_LOG_ERROR, "can not write to %s", strFileName.c_str()); } - XBMC->CloseFile(fileHandle); return rc >= 0; } return false; -} \ No newline at end of file +} + +} /* namespace Utils */ diff --git a/src/utilities.h b/src/utilities.h index 4fbcbea..4e26279 100644 --- a/src/utilities.h +++ b/src/utilities.h @@ -16,14 +16,16 @@ namespace Utils { -std::vector Split(const std::string& input, const std::string& delimiter, unsigned int iMaxStrings = 0); +std::vector Split(const std::string& input, + const std::string& delimiter, + unsigned int iMaxStrings = 0); bool Str2Bool(const std::string& str); -std::string Format(const char *fmt, ...); -std::string FormatV(const char *fmt, va_list args); +std::string Format(const char* fmt, ...); +std::string FormatV(const char* fmt, va_list args); bool EndsWith(std::string const& fullString, std::string const& ending); bool StartsWith(std::string const& fullString, std::string const& starting); std::string GetDirectoryPath(std::string const& path); bool ReadFileContents(std::string const& strFileName, std::string& strResult); -bool WriteFileContents(std::string const& strFileName, std::string& strContent); +bool WriteFileContents(std::string const& strFileName, const std::string& strContent); } /* namespace Utils */