From 3d6259cbbf8d73a099303ec3aab8a25d861d996c Mon Sep 17 00:00:00 2001 From: nirvana-7777 Date: Wed, 8 Nov 2023 08:58:51 +0100 Subject: [PATCH] - Implement inputstream ffmpegdirect - Improve debug & error handling --- pvr.eon/addon.xml.in | 3 +- pvr.eon/changelog.txt | 2 +- .../resource.language.de_de/strings.po | 12 + .../resource.language.en_gb/strings.po | 12 + .../resource.language.en_us/strings.po | 12 + src/Globals.h | 27 -- src/PVREon.cpp | 281 +++++++----------- src/PVREon.h | 55 +++- src/Utils.cpp | 24 ++ src/Utils.h | 1 + src/http/HttpClient.cpp | 11 +- src/http/HttpClient.h | 2 + 12 files changed, 219 insertions(+), 223 deletions(-) diff --git a/pvr.eon/addon.xml.in b/pvr.eon/addon.xml.in index b22b61b..dbc954d 100644 --- a/pvr.eon/addon.xml.in +++ b/pvr.eon/addon.xml.in @@ -6,7 +6,8 @@ provider-name="Nirvana"> @ADDON_DEPENDS@ - + + diff --git a/pvr.eon/changelog.txt b/pvr.eon/changelog.txt index 5df959a..b0976b8 100644 --- a/pvr.eon/changelog.txt +++ b/pvr.eon/changelog.txt @@ -9,7 +9,7 @@ v20.6.1 - Switch from adaptive to manual-osd for inputstream - Fix not subscribed channels leading to crash v20.7.0 - - Add Android TV platform (e.g. BigScreen/4K + - Add Android TV platform (e.g. BigScreen/4K) v20.7.1 - Adapt user agent header for platforms when streaming v20.7.2 diff --git a/pvr.eon/resources/language/resource.language.de_de/strings.po b/pvr.eon/resources/language/resource.language.de_de/strings.po index cf27f2d..c30df98 100644 --- a/pvr.eon/resources/language/resource.language.de_de/strings.po +++ b/pvr.eon/resources/language/resource.language.de_de/strings.po @@ -231,3 +231,15 @@ msgstr "inputstream.adaptive" msgctxt "#30053" msgid "inputstream.ffmpegdirect" msgstr "inputstream.ffmpegdirect" + +msgctxt "#30500" +msgid "Inputstream error" +msgstr "Inputstream Fehler" + +msgctxt "#30501" +msgid "The %s addon is not installed." +msgstr "Das %s Addon ist nicht installiert" + +msgctxt "#30502" +msgid "The %s addon is not enabled." +msgstr "Das %s Addon ist nicht aktiviert" diff --git a/pvr.eon/resources/language/resource.language.en_gb/strings.po b/pvr.eon/resources/language/resource.language.en_gb/strings.po index 5373fb6..fdf47a1 100644 --- a/pvr.eon/resources/language/resource.language.en_gb/strings.po +++ b/pvr.eon/resources/language/resource.language.en_gb/strings.po @@ -219,3 +219,15 @@ msgstr "" msgctxt "#30053" msgid "inputstream.ffmpegdirect" msgstr "" + +msgctxt "#30500" +msgid "Inputstream error" +msgstr "" + +msgctxt "#30501" +msgid "The %s addon is not installed." +msgstr "" + +msgctxt "#30502" +msgid "The %s addon is not enabled." +msgstr "" diff --git a/pvr.eon/resources/language/resource.language.en_us/strings.po b/pvr.eon/resources/language/resource.language.en_us/strings.po index d3f1f1e..244036f 100644 --- a/pvr.eon/resources/language/resource.language.en_us/strings.po +++ b/pvr.eon/resources/language/resource.language.en_us/strings.po @@ -223,3 +223,15 @@ msgstr "" msgctxt "#30053" msgid "inputstream.ffmpegdirect" msgstr "" + +msgctxt "#30500" +msgid "Inputstream error" +msgstr "" + +msgctxt "#30501" +msgid "The %s addon is not installed." +msgstr "" + +msgctxt "#30502" +msgid "The %s addon is not enabled." +msgstr "" diff --git a/src/Globals.h b/src/Globals.h index b465d08..0d052db 100644 --- a/src/Globals.h +++ b/src/Globals.h @@ -22,37 +22,10 @@ static const std::string CLIENT_SECRET_WEB = "1w4dmww87x1e9l89essqvc81pidrqsa0li static const std::string CLIENT_ID_ATV = "5a3e24b8-70cd-4958-b716-af9ce053e594"; static const std::string CLIENT_SECRET_ATV = "aazy6orsi9elhhs17e47lfb4palgszw6igf4y26z"; -static const std::string SS_PORTAL = "https://mojtelemach.ba"; static const std::string SS_DOMAIN = "TBA"; static const std::string SS_USER = "webscuser"; static const std::string SS_SECRET = "k4md93!k334f3"; static const std::string SS_PASS = "xD8iMq1!m94z"; -//Web Client -static const std::string API_PREFIX_WEB = "web"; -static const std::string API_SELECTOR_WEB = "be"; -static const std::string DEVICE_TYPE_WEB = "web_linux_chrome"; -static const std::string DEVICE_NAME_WEB = ""; -static const std::string DEVICE_MODEL_WEB = "Chrome 116"; -static const std::string DEVICE_PLATFORM_WEB = "web"; -static const std::string DEVICE_MAC_WEB = ""; -static const std::string CLIENT_SW_VERSION_WEB = ""; -static const std::string CLIENT_SW_BUILD_WEB = ""; -static const std::string SYSTEM_SW_WEB = "Linux"; -static const std::string SYSTEM_VERSION_WEB = "x86_64"; -static const std::string USER_AGENT_WEB = "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.0.0 Safari/537.36"; -//Android TV -static const std::string API_PREFIX_ATV = "android-tv"; -static const std::string API_SELECTOR_ATV = "af31"; -static const std::string DEVICE_TYPE_ATV = "Android 11"; -static const std::string DEVICE_NAME_ATV = "Android TV 30"; -static const std::string DEVICE_MODEL_ATV = "SHIELD Android TV"; -static const std::string DEVICE_PLATFORM_ATV = "android_tv"; -static const std::string DEVICE_MAC_ATV = ""; -static const std::string CLIENT_SW_VERSION_ATV = "8.1.3"; -static const std::string CLIENT_SW_BUILD_ATV = "8.1.35906"; -static const std::string SYSTEM_SW_ATV = "Android"; -static const std::string SYSTEM_VERSION_ATV = "11"; -static const std::string USER_AGENT_ATV = "Mozilla/5.0 (Linux; Android 11; SHIELD Android TV Build/RQ1A.210105.003; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/114.0.5735.196 Mobile Safari/537.36; XDKAndroidWebView/3.0.1/XDKWebView NVIDIA NVIDIA/mdarcy/mdarcy:11/RQ1A.210105.003/7825230_3167.5736:user/release-keys NVIDIA AndroidTV 1.00A_ATV SHIELD Android TV Android/11 ExoPlayer ((1.00A_ATV::1.14.1::androidtv::)"; //LG TV //static const std::string DEVICE_TYPE = "lgw-z81-8jg"; //static const std::string DEVICE_NAME = "LG WEB OS 2020"; diff --git a/src/PVREon.cpp b/src/PVREon.cpp index 93cbfcf..5db850b 100644 --- a/src/PVREon.cpp +++ b/src/PVREon.cpp @@ -12,6 +12,7 @@ #include #include +#include #include "Utils.h" #include "rapidjson/document.h" #include "rapidjson/writer.h" @@ -127,37 +128,30 @@ std::string aes_encrypt_cbc(const std::string &iv_str, const std::string &key, c return output; } -bool CPVREon::Parametrize(const int id) { - switch (id) { - case 1: - kodi::Log(ADDON_LOG_DEBUG,"Parametrizing for Android TV"); - m_parameters.api_prefix = API_PREFIX_ATV; - m_parameters.api_selector = API_SELECTOR_ATV; - m_parameters.device_type = DEVICE_TYPE_ATV; - m_parameters.device_name = DEVICE_NAME_ATV; - m_parameters.device_model = DEVICE_MODEL_ATV; - m_parameters.device_platform = DEVICE_PLATFORM_ATV; - m_parameters.device_mac = DEVICE_MAC_ATV; - m_parameters.client_sw_version = CLIENT_SW_VERSION_ATV; - m_parameters.client_sw_build = CLIENT_SW_BUILD_ATV; - m_parameters.system_sw = SYSTEM_SW_ATV; - m_parameters.system_version = SYSTEM_VERSION_ATV; - m_parameters.user_agent = USER_AGENT_ATV; - break; - default: - kodi::Log(ADDON_LOG_DEBUG,"Parametrizing for Web"); - m_parameters.api_prefix = API_PREFIX_WEB; - m_parameters.api_selector = API_SELECTOR_WEB; - m_parameters.device_type = DEVICE_TYPE_WEB; - m_parameters.device_name = DEVICE_NAME_WEB; - m_parameters.device_model = DEVICE_MODEL_WEB; - m_parameters.device_platform = DEVICE_PLATFORM_WEB; - m_parameters.device_mac = DEVICE_MAC_WEB; - m_parameters.client_sw_version = CLIENT_SW_VERSION_WEB; - m_parameters.client_sw_build = CLIENT_SW_BUILD_WEB; - m_parameters.system_sw = SYSTEM_SW_WEB; - m_parameters.system_version = SYSTEM_VERSION_WEB; - m_parameters.user_agent = USER_AGENT_WEB; +bool CPVREon::GetPostJson(const std::string& url, const std::string& body, rapidjson::Document& doc) +{ + int statusCode = 0; + std::string result; + + if (body.empty()) { + result = m_httpClient->HttpGet(url, statusCode); + } else + { + // kodi::Log(ADDON_LOG_DEBUG, "Body: %s", body.c_str()); + result = m_httpClient->HttpPost(url, body, statusCode); + } + //kodi::Log(ADDON_LOG_DEBUG, "Result: %s", result.c_str()); + doc.Parse(result.c_str()); + if ((doc.GetParseError()) || (statusCode != 200 && statusCode != 206)) + { + kodi::Log(ADDON_LOG_ERROR, "Failed to get JSON %s status code: %i", url.c_str(), statusCode); + if (doc.HasMember("error") && doc.HasMember("errorMessage")) + { + std::string title = Utils::JsonStringOrEmpty(doc, "error"); + std::string abstract = Utils::JsonStringOrEmpty(doc, "errorMessage"); + kodi::gui::dialogs::OK::ShowAndGetInput(title, abstract); + } + return false; } return true; } @@ -220,15 +214,10 @@ std::string CPVREon::GetBaseApi(const std::string& cdn_identifier) { std::string CPVREon::GetBrandIdentifier() { - std::string jsonString; - int statusCode = 0; - - jsonString = m_httpClient->HttpGet(BROKER_URL + "v2/brands", statusCode); + std::string url = BROKER_URL + "v2/brands"; rapidjson::Document doc; - doc.Parse(jsonString.c_str()); - if (doc.GetParseError()) - { + if (!GetPostJson(url, "", doc)) { kodi::Log(ADDON_LOG_ERROR, "Failed to get brands"); return ""; } @@ -254,15 +243,10 @@ std::string CPVREon::GetBrandIdentifier() bool CPVREon::GetCDNInfo() { - std::string jsonString; - int statusCode = 0; - - jsonString = m_httpClient->HttpGet(BROKER_URL + "v1/cdninfo", statusCode); + std::string url = BROKER_URL + "v1/cdninfo"; rapidjson::Document doc; - doc.Parse(jsonString.c_str()); - if (doc.GetParseError()) - { + if (!GetPostJson(url, "", doc)) { kodi::Log(ADDON_LOG_ERROR, "Failed to get cdninfo"); return false; } @@ -281,7 +265,7 @@ bool CPVREon::GetCDNInfo() const rapidjson::Value& baseApi = cdnItem["domains"]["baseApi"]; - cdn.baseApi = Utils::JsonStringOrEmpty(baseApi, m_parameters.api_selector.c_str()); + cdn.baseApi = Utils::JsonStringOrEmpty(baseApi, EonParameters[m_params].api_selector.c_str()); m_cdns.emplace_back(cdn); } @@ -290,17 +274,10 @@ bool CPVREon::GetCDNInfo() bool CPVREon::GetDeviceData() { - std::string url = SS_PORTAL + "/gateway/SelfCareAPI/1.0/selfcareapi/" + SS_DOMAIN + "/subscriber/" + m_settings->GetSSIdentity() + "/devices/eon/2/product"; - - std::string jsonString; - int statusCode = 0; - - jsonString = m_httpClient->HttpGet(url, statusCode); + std::string url = m_support_web + "/gateway/SelfCareAPI/1.0/selfcareapi/" + SS_DOMAIN + "/subscriber/" + m_settings->GetSSIdentity() + "/devices/eon/2/product"; rapidjson::Document doc; - doc.Parse(jsonString.c_str()); - if (doc.GetParseError()) - { + if (!GetPostJson(url, "", doc)) { kodi::Log(ADDON_LOG_ERROR, "Failed to get self service product data"); return false; } @@ -328,38 +305,33 @@ bool CPVREon::GetDeviceData() bool CPVREon::GetDeviceFromSerial() { - std::string jsonString; - int statusCode = 0; - std::string postData; - if (m_settings->GetPlatform() == 1) { - postData = "{\"deviceName\":\"" + m_parameters.device_name + - "\",\"deviceType\":\"" + m_parameters.device_type + - "\",\"modelName\":\"" + m_parameters.device_model + - "\",\"platform\":\"" + m_parameters.device_platform + + if (m_settings->GetPlatform() == PLATFORM_ANDROIDTV) { + postData = "{\"deviceName\":\"" + EonParameters[m_params].device_name + + "\",\"deviceType\":\"" + EonParameters[m_params].device_type + + "\",\"modelName\":\"" + EonParameters[m_params].device_model + + "\",\"platform\":\"" + EonParameters[m_params].device_platform + "\",\"serial\":\"" + m_device_serial + - "\",\"clientSwVersion\":\"" + m_parameters.client_sw_version + - "\",\"clientSwBuild\":\"" + m_parameters.client_sw_build + - "\",\"systemSwVersion\":{\"name\":\"" + m_parameters.system_sw + - "\",\"version\":\"" + m_parameters.system_version + + "\",\"clientSwVersion\":\"" + EonParameters[m_params].client_sw_version + + "\",\"clientSwBuild\":\"" + EonParameters[m_params].client_sw_build + + "\",\"systemSwVersion\":{\"name\":\"" + EonParameters[m_params].system_sw + + "\",\"version\":\"" + EonParameters[m_params].system_version + "\"},\"fcmToken\":\"\"}"; //TODO: implement parameter fcmToken... } else { - postData = "{\"deviceName\":\"\",\"deviceType\":\"" + m_parameters.device_type + - "\",\"modelName\":\"" + m_parameters.device_model + - "\",\"platform\":\"" + m_parameters.device_platform + + postData = "{\"deviceName\":\"\",\"deviceType\":\"" + EonParameters[m_params].device_type + + "\",\"modelName\":\"" + EonParameters[m_params].device_model + + "\",\"platform\":\"" + EonParameters[m_params].device_platform + "\",\"serial\":\"" + m_device_serial + - "\",\"clientSwVersion\":\"\",\"systemSwVersion\":{\"name\":\"" + m_parameters.system_sw + - "\",\"version\":\"" + m_parameters.system_version + "\"}}"; + "\",\"clientSwVersion\":\"\",\"systemSwVersion\":{\"name\":\"" + EonParameters[m_params].system_sw + + "\",\"version\":\"" + EonParameters[m_params].system_version + "\"}}"; } - jsonString = m_httpClient->HttpPost(m_api + "v1/devices", postData, statusCode); + std::string url = m_api + "v1/devices"; rapidjson::Document doc; - doc.Parse(jsonString.c_str()); - if (doc.GetParseError()) - { + if (!GetPostJson(url, postData, doc)) { kodi::Log(ADDON_LOG_ERROR, "Failed to get devices"); return false; } @@ -373,15 +345,10 @@ bool CPVREon::GetDeviceFromSerial() bool CPVREon::GetServers() { - std::string jsonString; - int statusCode = 0; - - jsonString = m_httpClient->HttpGet(m_api + "v1/servers", statusCode); + std::string url = m_api + "v1/servers"; rapidjson::Document doc; - doc.Parse(jsonString.c_str()); - if (doc.GetParseError()) - { + if (!GetPostJson(url, "", doc)) { kodi::Log(ADDON_LOG_ERROR, "Failed to get servers"); return false; } @@ -422,15 +389,10 @@ bool CPVREon::GetServers() bool CPVREon::GetHouseholds() { - std::string jsonString; - int statusCode = 0; - - jsonString = m_httpClient->HttpGet(m_api + "v1/households", statusCode); + std::string url = m_api + "v1/households"; rapidjson::Document doc; - doc.Parse(jsonString.c_str()); - if (doc.GetParseError()) - { + if (!GetPostJson(url, "", doc)) { kodi::Log(ADDON_LOG_ERROR, "Failed to get households"); return false; } @@ -443,15 +405,10 @@ bool CPVREon::GetHouseholds() std::string CPVREon::GetTime() { - std::string jsonString; - int statusCode = 0; - - jsonString = m_httpClient->HttpGet(m_api + "v1/time", statusCode); + std::string url = m_api + "v1/time"; rapidjson::Document doc; - doc.Parse(jsonString.c_str()); - if (doc.GetParseError()) - { + if (!GetPostJson(url, "", doc)) { kodi::Log(ADDON_LOG_ERROR, "Failed to get time"); return ""; } @@ -461,20 +418,17 @@ std::string CPVREon::GetTime() bool CPVREon::GetServiceProvider() { - std::string jsonString; - int statusCode = 0; - - jsonString = m_httpClient->HttpGet(m_api + "v1/sp", statusCode); + std::string url = m_api + "v1/sp"; rapidjson::Document doc; - doc.Parse(jsonString.c_str()); - if (doc.GetParseError()) - { + if (!GetPostJson(url, "", doc)) { kodi::Log(ADDON_LOG_ERROR, "Failed to get service provider"); return false; } + m_service_provider = Utils::JsonStringOrEmpty(doc, "identifier"); m_support_web = Utils::JsonStringOrEmpty(doc, "supportWebAddress"); + m_httpClient->SetSupportApi(m_support_web); kodi::Log(ADDON_LOG_DEBUG, "Got Service Provider: %s and Support Web: %s", m_service_provider.c_str(), m_support_web.c_str()); return true; @@ -482,19 +436,13 @@ bool CPVREon::GetServiceProvider() bool CPVREon::GetRenderingProfiles() { - std::string jsonString; - int statusCode = 0; - - jsonString = m_httpClient->HttpGet(m_api + "v1/rndprofiles", statusCode); + std::string url = m_api + "v1/rndprofiles"; rapidjson::Document doc; - doc.Parse(jsonString.c_str()); - if (doc.GetParseError()) - { + if (!GetPostJson(url, "", doc)) { kodi::Log(ADDON_LOG_ERROR, "Failed to get rendering profiles"); return false; } - //kodi::Log(ADDON_LOG_DEBUG, "Got Rendering Profiles: %s", jsonString.c_str()); const rapidjson::Value& rndProfiles = doc; @@ -520,23 +468,14 @@ bool CPVREon::GetRenderingProfiles() bool CPVREon::GetCategories(const bool isRadio) { - std::string jsonString; - int statusCode = 0; - - if (isRadio) { - jsonString = m_httpClient->HttpGet(m_api + "v2/categories/RADIO", statusCode); - } else { - jsonString = m_httpClient->HttpGet(m_api + "v2/categories/TV", statusCode); - } + std::string url = m_api + "v2/categories/" + (isRadio ? "RADIO" : "TV"); rapidjson::Document doc; - doc.Parse(jsonString.c_str()); - if (doc.GetParseError()) - { + if (!GetPostJson(url, "", doc)) { kodi::Log(ADDON_LOG_ERROR, "Failed to get categories"); return false; } - //kodi::Log(ADDON_LOG_DEBUG, "Got C: %s", m_service_provider.c_str()); + const rapidjson::Value& categories = doc; for (rapidjson::Value::ConstValueIterator itr1 = categories.Begin(); @@ -576,7 +515,9 @@ CPVREon::CPVREon() : m_settings->Load(); m_httpClient = new HttpClient(m_settings); - Parametrize(m_settings->GetPlatform()); + m_params = m_settings->GetPlatform(); + m_support_web = "API_NOT_SET_YET"; + m_httpClient->SetSupportApi(m_support_web); srand(time(nullptr)); @@ -584,24 +525,24 @@ CPVREon::CPVREon() : std::string cdn_identifier = GetBrandIdentifier(); kodi::Log(ADDON_LOG_DEBUG, "CDN Identifier: %s", cdn_identifier.c_str()); std::string baseApi = GetBaseApi(cdn_identifier); - m_api = "https://api-" + m_parameters.api_prefix + "." + baseApi + "/"; - m_images_api = "https://images-" + m_parameters.api_prefix + "." + baseApi + "/"; + m_api = "https://api-" + EonParameters[m_params].api_prefix + "." + baseApi + "/"; + m_images_api = "https://images-" + EonParameters[m_params].api_prefix + "." + baseApi + "/"; } else { - m_api = "https://api-" + m_parameters.api_prefix + "." + GLOBAL_URL; - m_images_api = "https://images-" + m_parameters.api_prefix + "." + GLOBAL_URL; + m_api = "https://api-" + EonParameters[m_params].api_prefix + "." + GLOBAL_URL; + m_images_api = "https://images-" + EonParameters[m_params].api_prefix + "." + GLOBAL_URL; } m_httpClient->SetApi(m_api); kodi::Log(ADDON_LOG_DEBUG, "API set to: %s", m_api.c_str()); m_device_id = m_settings->GetEonDeviceID(); m_device_number = m_settings->GetEonDeviceNumber(); - +/* m_ss_identity = m_settings->GetSSIdentity(); if (m_ss_identity.empty()) { m_httpClient->RefreshSSToken(); m_ss_identity = m_settings->GetSSIdentity(); } - +*/ if (m_device_id.empty() || m_device_number.empty()) { /* if (GetDeviceData()) { @@ -670,20 +611,11 @@ ADDON_STATUS CPVREon::SetSetting(const std::string& settingName, const std::stri bool CPVREon::LoadChannels(const bool isRadio) { kodi::Log(ADDON_LOG_DEBUG, "Load Eon Channels"); - std::string jsonString; - int statusCode = 0; -// std::string postData = "{}"; - if (isRadio) { - jsonString = m_httpClient->HttpGet(m_api + "v3/channels?channelType=RADIO&channelSort=RECOMMENDED&sortDir=DESC", statusCode); - } else { - jsonString = m_httpClient->HttpGet(m_api + "v3/channels?channelType=TV", statusCode); - } + std::string url = m_api + "v3/channels?channelType=" + (isRadio ? "RADIO&channelSort=RECOMMENDED&sortDir=DESC" : "TV"); rapidjson::Document doc; - doc.Parse(jsonString.c_str()); - if (doc.GetParseError()) - { + if (!GetPostJson(url, "", doc)) { kodi::Log(ADDON_LOG_ERROR, "Failed to load channels"); return false; } @@ -815,15 +747,10 @@ bool CPVREon::HandleSession(bool start, int cid, int epg_id) ",\"viewing_time\":500,\"silent_event_change\":false}]"; kodi::Log(ADDON_LOG_DEBUG, "Session PostData: %s", postData.c_str()); - int statusCode = 0; - std::string jsonString = m_httpClient->HttpPost("https://aes.ug.cdn.united.cloud/v1/events", postData, statusCode); - - kodi::Log(ADDON_LOG_DEBUG, "Event register returned: %s", jsonString.c_str()); + std::string url = m_api + "v1/events"; rapidjson::Document doc; - doc.Parse(jsonString.c_str()); - if (doc.GetParseError()) - { + if (!GetPostJson(url, postData, doc)) { kodi::Log(ADDON_LOG_ERROR, "Failed to register event"); return false; } @@ -846,25 +773,39 @@ void CPVREon::SetStreamProperties(std::vector& p if (inputstream == INPUTSTREAM_ADAPTIVE) { + if (!Utils::CheckInputstreamInstalledAndEnabled("inputstream.adaptive")) + { + kodi::Log(ADDON_LOG_DEBUG, "inputstream.adaptive selected but not installed or enabled"); + return; + } + kodi::Log(ADDON_LOG_DEBUG, "...using inputstream.adaptive"); properties.emplace_back(PVR_STREAM_PROPERTY_INPUTSTREAM, "inputstream.adaptive"); properties.emplace_back("inputstream.adaptive.manifest_type", "hls"); // properties.emplace_back("inputstream.adaptive.original_audio_language", "bs"); // properties.emplace_back("inputstream.adaptive.stream_selection_type", "adaptive"); properties.emplace_back("inputstream.adaptive.stream_selection_type", "manual-osd"); - properties.emplace_back("inputstream.adaptive.manifest_headers", "User-Agent=" + m_parameters.user_agent); - properties.emplace_back("inputstream.adaptive.manifest_update_parameter", "full"); + properties.emplace_back("inputstream.adaptive.manifest_headers", "User-Agent=" + EonParameters[m_params].user_agent); + // properties.emplace_back("inputstream.adaptive.manifest_update_parameter", "full"); } else if (inputstream == INPUTSTREAM_FFMPEGDIRECT) { + if (!Utils::CheckInputstreamInstalledAndEnabled("inputstream.ffmpegdirect")) + { + kodi::Log(ADDON_LOG_DEBUG, "inputstream.ffmpegdirect selected but not installed or enabled"); + return; + } + kodi::Log(ADDON_LOG_DEBUG, "...using inputstream.ffmpegdirect"); properties.emplace_back(PVR_STREAM_PROPERTY_INPUTSTREAM, "inputstream.ffmpegdirect"); properties.emplace_back("inputstream.ffmpegdirect.manifest_type", "hls"); properties.emplace_back("inputstream.ffmpegdirect.is_realtime_stream", "true"); properties.emplace_back("inputstream.ffmpegdirect.stream_mode", isLive ? "timeshift" : "catchup"); +/* if (!isLive) { properties.emplace_back("inputstream.ffmpegdirect.catchup_buffer_start_time", std::to_string(starttime)); properties.emplace_back("inputstream.ffmpegdirect.catchup_buffer_end_time", std::to_string(endtime)); properties.emplace_back("inputstream.ffmpegdirect.programme_start_time", std::to_string(starttime)); - properties.emplace_back("inputstream.ffmpegdirect.programme_end_time", std::to_string(endtime)); + properties.emplace_back("inputstream.ffmpegdirect.programme_end_time", std::to_string(endtime)); } +*/ } else { kodi::Log(ADDON_LOG_DEBUG, "Unknown inputstream detected"); } @@ -932,28 +873,19 @@ PVR_ERROR CPVREon::GetEPGForChannel(int channelUid, if (channel.iUniqueId != channelUid) continue; - - std::string jsonEpg; - int statusCode = 0; - kodi::Log(ADDON_LOG_DEBUG, "EPG Request for Channel %u Start %u End %u", channel.iUniqueId, start, end); - std::string url = m_api + "v1/events/epg"; - std::string params = "?cid=" + std::to_string(channel.iUniqueId) + - "&fromTime=" + std::to_string(start) + "000" + - "&toTime=" + std::to_string(end) + "000"; - - jsonEpg = m_httpClient->HttpGet(url + params, statusCode); - -// kodi::Log(ADDON_LOG_DEBUG, "EPG Events returned: %s", jsonEpg.c_str()); + std::string url = m_api + "v1/events/epg" + + "?cid=" + std::to_string(channel.iUniqueId) + + "&fromTime=" + std::to_string(start) + "000" + + "&toTime=" + std::to_string(end) + "000"; rapidjson::Document epgDoc; - epgDoc.Parse(jsonEpg.c_str()); - if (epgDoc.GetParseError()) - { + if (!GetPostJson(url, "", epgDoc)) { kodi::Log(ADDON_LOG_ERROR, "[GetEPG] ERROR: error while parsing json"); return PVR_ERROR_SERVER_ERROR; } + kodi::Log(ADDON_LOG_DEBUG, "[epg] iterate entries"); // std::string cid = "\"" + std::to_string(channel.referenceID) + "\""; @@ -1110,7 +1042,7 @@ PVR_ERROR CPVREon::GetStreamProperties( std::string plain_aes; - if (m_settings->GetPlatform() == 1) { + if (m_settings->GetPlatform() == PLATFORM_ANDROIDTV) { plain_aes = "channel=" + channel.publishingPoints[0].publishingPoint + ";" + "stream=" + streaming_profile + ";" + "sp=" + m_service_provider + ";" + @@ -1168,14 +1100,14 @@ PVR_ERROR CPVREon::GetStreamProperties( std::string enc_url = "https://" + currentServer.hostname + "/stream?i=" + urlsafeencode(base64_encode(iv_str.c_str(), iv_str.length())) + "&a=" + urlsafeencode(base64_encode(enc_str.c_str(), enc_str.length())); - if (m_settings->GetPlatform() == 1) { + if (m_settings->GetPlatform() == PLATFORM_ANDROIDTV) { enc_url = enc_url + "&lang=eng"; } enc_url = enc_url + "&sp=" + m_service_provider + "&u=" + m_settings->GetEonStreamUser() + "&player=" + PLAYER + "&session=" + m_session_id; - if (m_settings->GetPlatform() != 1) { + if (m_settings->GetPlatform() != PLATFORM_ANDROIDTV) { enc_url = enc_url + "&sig=" + channel.sig; } @@ -1298,7 +1230,7 @@ PVR_ERROR CPVREon::GetTimers(kodi::addon::PVRTimersResultSet& results) { return PVR_ERROR_NO_ERROR; } - +/* PVR_ERROR CPVREon::CallEPGMenuHook(const kodi::addon::PVRMenuhook& menuhook, const kodi::addon::PVREPGTag& item) { @@ -1333,7 +1265,7 @@ PVR_ERROR CPVREon::CallSettingsMenuHook(const kodi::addon::PVRMenuhook& menuhook PVR_ERROR CPVREon::CallMenuHook(const kodi::addon::PVRMenuhook& menuhook) { -/* + int iMsg; switch (menuhook.GetHookId()) { @@ -1353,10 +1285,9 @@ PVR_ERROR CPVREon::CallMenuHook(const kodi::addon::PVRMenuhook& menuhook) return PVR_ERROR_INVALID_PARAMETERS; } kodi::QueueNotification(QUEUE_INFO, "", kodi::addon::GetLocalizedString(iMsg)); -*/ return PVR_ERROR_NO_ERROR; } - +*/ bool CPVREon::GetChannel(const kodi::addon::PVRChannel& channel, EonChannel& myChannel) { kodi::Log(ADDON_LOG_DEBUG, "function call: [%s]", __FUNCTION__); @@ -1399,7 +1330,7 @@ bool CPVREon::GetServer(bool isLive, EonServer& myServer) servers = m_timeshift_servers; } int target_server = 2; - if (m_settings->GetPlatform() == 1) { + if (m_settings->GetPlatform() == PLATFORM_ANDROIDTV) { target_server = 3; } int count = 0; diff --git a/src/PVREon.h b/src/PVREon.h index b7ba59a..a9ab60f 100644 --- a/src/PVREon.h +++ b/src/PVREon.h @@ -17,6 +17,9 @@ static const int INPUTSTREAM_ADAPTIVE = 0; static const int INPUTSTREAM_FFMPEGDIRECT = 1; +static const int PLATFORM_WEB = 0; +static const int PLATFORM_ANDROIDTV = 1; + struct EonChannelCategory { int id; @@ -103,15 +106,15 @@ struct EonCDN bool isDefault; }; -struct EonParameters +struct EonParameter { std::string api_prefix; std::string api_selector; std::string device_type; - std::string device_mac; std::string device_name; std::string device_model; std::string device_platform; + std::string device_mac; std::string client_sw_version; std::string client_sw_build; std::string system_sw; @@ -119,6 +122,38 @@ struct EonParameters std::string user_agent; }; +EonParameter EonParameters[2] = {{ + //Web Client + "web", + "be", + "web_linux_chrome", + "", + "Chrome 116", + "web", + "", + "", + "", + "Linux", + "x86_64", + "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.0.0 Safari/537.36" + }, + { + //Android TV + "android-tv", + "af31", + "Android 11", + "Android TV 30", + "SHIELD Android TV", + "android_tv", + "", + "8.1.3", + "8.1.35906", + "Android", + "11", + "Mozilla/5.0 (Linux; Android 11; SHIELD Android TV Build/RQ1A.210105.003; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/114.0.5735.196 Mobile Safari/537.36; XDKAndroidWebView/3.0.1/XDKWebView NVIDIA NVIDIA/mdarcy/mdarcy:11/RQ1A.210105.003/7825230_3167.5736:user/release-keys NVIDIA AndroidTV 1.00A_ATV SHIELD Android TV Android/11 ExoPlayer ((1.00A_ATV::1.14.1::androidtv::)" + } + }; + class ATTR_DLL_LOCAL CPVREon : public kodi::addon::CAddonBase, public kodi::addon::CInstancePVRClient { @@ -133,17 +168,6 @@ class ATTR_DLL_LOCAL CPVREon : public kodi::addon::CAddonBase, PVR_ERROR GetCapabilities(kodi::addon::PVRCapabilities& capabilities) override; PVR_ERROR GetDriveSpace(uint64_t& total, uint64_t& used) override; - - PVR_ERROR CallEPGMenuHook(const kodi::addon::PVRMenuhook& menuhook, - const kodi::addon::PVREPGTag& item) override; - PVR_ERROR CallChannelMenuHook(const kodi::addon::PVRMenuhook& menuhook, - const kodi::addon::PVRChannel& item) override; - PVR_ERROR CallTimerMenuHook(const kodi::addon::PVRMenuhook& menuhook, - const kodi::addon::PVRTimer& item) override; - PVR_ERROR CallRecordingMenuHook(const kodi::addon::PVRMenuhook& menuhook, - const kodi::addon::PVRRecording& item) override; - PVR_ERROR CallSettingsMenuHook(const kodi::addon::PVRMenuhook& menuhook) override; - PVR_ERROR GetEPGForChannel(int channelUid, time_t start, time_t end, @@ -193,8 +217,6 @@ class ATTR_DLL_LOCAL CPVREon : public kodi::addon::CAddonBase, const EonChannel& channel, std::vector& properties, const int& starttime, const int& endtime, const bool& isLive); - bool Parametrize(const int id); - std::vector m_channels; std::vector m_live_servers; std::vector m_timeshift_servers; @@ -224,6 +246,7 @@ class ATTR_DLL_LOCAL CPVREon : public kodi::addon::CAddonBase, std::string m_api; std::string m_images_api; + int m_params; // std::string m_ss_refresh; // int m_active_profile_id; @@ -231,10 +254,10 @@ class ATTR_DLL_LOCAL CPVREon : public kodi::addon::CAddonBase, HttpClient *m_httpClient; CSettings* m_settings; - EonParameters m_parameters; std::string GetTime(); int getBitrate(const bool isRadio, const int id); + bool GetPostJson(const std::string& url, const std::string& body, rapidjson::Document& doc); std::string getCoreStreamId(const int id); std::string GetBaseApi(const std::string& cdn_identifier); std::string GetBrandIdentifier(); diff --git a/src/Utils.cpp b/src/Utils.cpp index abec0ca..d9ad631 100644 --- a/src/Utils.cpp +++ b/src/Utils.cpp @@ -238,3 +238,27 @@ std::string Utils::CreateUUID() } return uuid; } + +bool Utils::CheckInputstreamInstalledAndEnabled(const std::string& inputstreamName) +{ + std::string version; + bool enabled; + + if (kodi::IsAddonAvailable(inputstreamName, version, enabled)) + { + if (!enabled) + { + std::string message = kodi::tools::StringUtils::Format(kodi::addon::GetLocalizedString(30502).c_str(), inputstreamName.c_str()); + kodi::QueueNotification(QueueMsg::QUEUE_ERROR, kodi::addon::GetLocalizedString(30500), message); + return false; + } + } + else // Not installed + { + std::string message = kodi::tools::StringUtils::Format(kodi::addon::GetLocalizedString(30501).c_str(), inputstreamName.c_str()); + kodi::QueueNotification(QueueMsg::QUEUE_ERROR, kodi::addon::GetLocalizedString(30500), message); + return false; + } + + return true; +} diff --git a/src/Utils.h b/src/Utils.h index 8e4bcab..c370199 100644 --- a/src/Utils.h +++ b/src/Utils.h @@ -26,4 +26,5 @@ class Utils static double JsonDoubleOrZero(const rapidjson::Value& jsonValue, const char* fieldName); static bool JsonBoolOrFalse(const rapidjson::Value& jsonValue, const char* fieldName); static std::string CreateUUID(); + static bool CheckInputstreamInstalledAndEnabled(const std::string& inputstreamName); }; diff --git a/src/http/HttpClient.cpp b/src/http/HttpClient.cpp index ee27a09..471cb83 100644 --- a/src/http/HttpClient.cpp +++ b/src/http/HttpClient.cpp @@ -15,6 +15,11 @@ void HttpClient::SetApi(const std::string& api) m_api = api; } +void HttpClient::SetSupportApi(const std::string& supportApi) +{ + m_supportApi = supportApi; +} + bool HttpClient::RefreshGenericToken() { Curl curl_auth; @@ -57,7 +62,7 @@ bool HttpClient::RefreshSSToken() { Curl curl_auth; - std::string url = SS_PORTAL + "/gateway/SCAuthAPI/1.0/scauth/auth/authentication"; //TODO: Fix URL + std::string url = m_supportApi + "/gateway/SCAuthAPI/1.0/scauth/auth/authentication"; //TODO: Fix URL std::string refresh_token = m_settings->GetSSRefreshToken(); std::string access_token = m_settings->GetSSAccessToken(); std::string username = m_settings->GetEonUsername(); @@ -308,7 +313,7 @@ std::string HttpClient::HttpRequest(const std::string& action, const std::string curl.AddHeader("User-Agent", EON_USER_AGENT); - size_t found = url.find(SS_PORTAL); + size_t found = url.find(m_supportApi); if (found != std::string::npos) { access_token = m_settings->GetSSAccessToken(); if (!access_token.empty()) { @@ -340,7 +345,7 @@ std::string HttpClient::HttpRequest(const std::string& action, const std::string if (statusCode == 401) { Curl curl_reauth; - size_t found = url.find(SS_PORTAL); + size_t found = url.find(m_supportApi); bool refresh_successful = true; if (found != std::string::npos) { if (RefreshSSToken()) { diff --git a/src/http/HttpClient.h b/src/http/HttpClient.h index 3509c29..7436247 100644 --- a/src/http/HttpClient.h +++ b/src/http/HttpClient.h @@ -21,6 +21,7 @@ class HttpClient bool RefreshGenericToken(); void ClearSession(); void SetApi(const std::string& api); + void SetSupportApi(const std::string& supportApi); std::string GetUUID(); void SetStatusCodeHandler(HttpStatusCodeHandler* statusCodeHandler) { m_statusCodeHandler = statusCodeHandler; @@ -31,6 +32,7 @@ class HttpClient std::string GenerateUUID(); std::string m_uuid; std::string m_api; + std::string m_supportApi; std::string client_id; std::string client_secret; CSettings* m_settings;