diff --git a/src/Channels.cpp b/src/Channels.cpp index 754f8a82..b0d78c37 100644 --- a/src/Channels.cpp +++ b/src/Channels.cpp @@ -314,6 +314,7 @@ PVR_ERROR Channels::GetChannelGroupMembers(const kodi::addon::PVRChannelGroup& g results.Add(tag); } } + returnValue = PVR_ERROR_NO_ERROR; } else { @@ -350,13 +351,12 @@ bool Channels::IsChannelAPlugin(int uid) /************************************************************/ void Channels::LoadLiveStreams() { - const std::string URL = "/public/LiveStreams.xml"; m_liveStreams.clear(); - if (m_request.FileCopy(URL.c_str(), m_settings->m_instanceDirectory + "LiveStreams.xml") == HTTP_OK) + if (m_request.FileCopy("/public/LiveStreams.xml", m_settings->m_instanceDirectory + "LiveStreams.cache") == HTTP_OK) { tinyxml2::XMLDocument doc; - std::string liveStreams = kodi::vfs::TranslateSpecialProtocol(m_settings->m_instanceDirectory + "LiveStreams.xml"); - kodi::Log(ADDON_LOG_DEBUG, "Loading LiveStreams.xml %s", liveStreams.c_str()); + std::string liveStreams = kodi::vfs::TranslateSpecialProtocol(m_settings->m_instanceDirectory + "LiveStreams.cache"); + kodi::Log(ADDON_LOG_DEBUG, "Loading LiveStreams.cache %s", liveStreams.c_str()); if (doc.LoadFile(liveStreams.c_str()) == tinyxml2::XML_SUCCESS) { tinyxml2::XMLNode* streamsNode = doc.FirstChildElement("streams"); diff --git a/src/InstanceSettings.cpp b/src/InstanceSettings.cpp index 7e36ddf1..005ea2cf 100644 --- a/src/InstanceSettings.cpp +++ b/src/InstanceSettings.cpp @@ -9,6 +9,8 @@ #include "InstanceSettings.h" #include #include "uri.h" +#include + #include #include @@ -25,7 +27,12 @@ InstanceSettings::InstanceSettings(kodi::addon::IAddonInstance& instance, const { m_instanceNumber = m_instanceInfo.GetNumber(); m_instanceDirectory = kodi::tools::StringUtils::Format("special://profile/addon_data/pvr.nextpvr/%d/", m_instanceNumber); + std::string savedSettings = kodi::tools::StringUtils::Format("special://profile/addon_data/pvr.nextpvr/instance-settings-%d.xml", m_instanceNumber); ReadFromAddon(); + if (kodi::vfs::FileExists(savedSettings)) + { + ReadFromSavedSettings(savedSettings); + } } /*************************************************************************** @@ -115,7 +122,14 @@ void InstanceSettings::ReadFromAddon() m_instance.SetInstanceSettingInt("instance", m_instanceNumber); } - m_instanceName = ReadStringSetting("kodi_addon_instance_name", "Unknown"); + m_instanceName = ReadStringSetting("kodi_addon_instance_name", ""); + + if (m_instanceName.empty()) + { + kodi::Log(ADDON_LOG_WARNING, "Instance name not set using hostname"); + m_instanceName = m_hostname; + m_instance.SetInstanceSettingString("kodi_addon_instance_name", m_instanceName); + } m_allChannels = ReadBoolSetting("instanceallgroup", false); @@ -136,10 +150,183 @@ void InstanceSettings::ReadFromAddon() /* Log the current settings for debugging purposes */ - kodi::Log(ADDON_LOG_DEBUG, "settings: host='%s', port=%i, instance=%d, mac=%4.4s...", m_hostname.c_str(), m_port, m_instanceNumber, m_hostMACAddress.c_str()); + kodi::Log(ADDON_LOG_INFO, "settings: host='%s', port=%i, instance=%d, mac=%4.4s...", m_hostname.c_str(), m_port, m_instanceNumber, m_hostMACAddress.c_str()); } +void InstanceSettings::ReadFromSavedSettings(std::string savedSettings) +{ + const std::string filename = kodi::vfs::TranslateSpecialProtocol(savedSettings); + tinyxml2::XMLError errorLoad = tinyxml2::XML_NO_TEXT_NODE; + kodi::vfs::CFile setting; + tinyxml2::XMLDocument doc; + if (setting.OpenFile(filename, ADDON_READ_NO_CACHE)) + { + std::string response; + char buffer[1025] = { 0 }; + int count; + while ((count = setting.Read(buffer, 1024))) + { + response.append(buffer, count); + } + setting.Close(); + errorLoad = doc.Parse(response.c_str()); + } + kodi::Log(ADDON_LOG_DEBUG, "Saved settings open: %d", errorLoad); + if (errorLoad) + return; + + /* Connection settings */ + /***********************/ + + std::string protocol = ReadSavedStringSetting("hostprotocol", protocol, doc.FirstChild()); + + m_hostname = ReadSavedStringSetting("host", DEFAULT_HOST, doc.FirstChild()); + uri::decode(m_hostname); + + m_port = ReadSavedIntSetting("port", DEFAULT_PORT, doc.FirstChildElement()); + + m_PIN = ReadSavedStringSetting("pin", DEFAULT_PIN, doc.FirstChild()); + + sprintf(m_urlBase, "%s://%.255s:%d", protocol.c_str(), m_hostname.c_str(), m_port); + + m_enableWOL = ReadSavedBoolSetting("wolenable", false, doc.FirstChild()); + m_hostMACAddress = ReadSavedStringSetting("host_mac", "", doc.FirstChild()); + kodi::Log(ADDON_LOG_DEBUG, "settings: @1"); + if (m_enableWOL) + { + if (m_hostMACAddress.empty()) + m_enableWOL = false; + else if (m_hostname == "127.0.0.1" || m_hostname == "localhost" || m_hostname == "::1") + m_enableWOL = false; + } + + m_timeoutWOL = ReadSavedIntSetting("woltimeout", 20, doc.FirstChild()); + + m_remoteAccess = ReadSavedBoolSetting("remoteaccess", false, doc.FirstChild()); + + m_liveStreamingMethod = static_cast(ReadSavedIntSetting("livestreamingmethod5", DEFAULT_LIVE_STREAM, doc.FirstChild())); + + m_flattenRecording = ReadSavedBoolSetting("flattenrecording", false, doc.FirstChild()); + + m_separateSeasons = ReadSavedBoolSetting("separateseasons", false, doc.FirstChild()); + + m_showRoot = ReadSavedBoolSetting("showroot", false, doc.FirstChild()); + + kodi::Log(ADDON_LOG_DEBUG, "settings: @1"); + + m_prebuffer5 = ReadSavedIntSetting("prebuffer5", 0, doc.FirstChild()); + + m_liveChunkSize = ReadSavedIntSetting("chunklivetv", 64, doc.FirstChild()); + + m_chunkRecording = ReadSavedIntSetting("chunkrecording", 32, doc.FirstChild()); + + m_ignorePadding = ReadSavedBoolSetting("ignorepadding", true, doc.FirstChild()); + + m_resolution = ReadSavedStringSetting("resolution", "720", doc.FirstChild()); + + m_showRadio = ReadSavedBoolSetting("showradio", true, doc.FirstChild()); + + m_backendResume = ReadSavedBoolSetting("backendresume", true, doc.FirstChild()); + + m_connectionConfirmed = kodi::vfs::FileExists(m_instanceDirectory + connectionFlag); + + if (m_PIN != "0000" && m_remoteAccess) + { + m_downloadGuideArtwork = false; + m_sendSidWithMetadata = true; + } + else { + m_downloadGuideArtwork = ReadSavedBoolSetting("guideartwork", DEFAULT_GUIDE_ARTWORK, doc.FirstChild()); + m_sendSidWithMetadata = false; + } + + m_guideArtPortrait = ReadSavedBoolSetting("guideartworkportrait", false, doc.FirstChild()); + + m_genreString = ReadSavedBoolSetting("genrestring", false, doc.FirstChild()); + + m_showRecordingSize = ReadSavedBoolSetting("recordingsize", false, doc.FirstChild()); + + m_diskSpace = ReadSavedStringSetting("diskspace", "Default", doc.FirstChild()); + + m_transcodedTimeshift = ReadSavedBoolSetting("ffmpegdirect", false, doc.FirstChild()); + + m_castcrew = ReadSavedBoolSetting("castcrew", false, doc.FirstChild()); + + m_useLiveStreams = ReadSavedBoolSetting("uselivestreams", false, doc.FirstChild()); + + m_instanceName = ReadSavedStringSetting("kodi_addon_instance_name", "", doc.FirstChild()); + + if (m_instanceName.empty()) + { + kodi::Log(ADDON_LOG_WARNING, "Instance name not set in cache using hostname %s", m_hostname.c_str()); + m_instanceName = m_hostname; + m_instance.SetInstanceSettingString("kodi_addon_instance_name", m_instanceName); + } + + + m_allChannels = ReadSavedBoolSetting("instanceallgroup", false, doc.FirstChild()); + + m_addChannelInstance = ReadSavedBoolSetting("instancechannel", false, doc.FirstChild()); + + m_comskip = ReadSavedBoolSetting("comskip", true, doc.FirstChild()); + + enum eHeartbeat m_heartbeat = static_cast (ReadSavedIntSetting("heartbeat", eHeartbeat::Default, doc.FirstChild())); + + if (m_heartbeat == eHeartbeat::Default) + m_heartbeatInterval = DEFAULT_HEARTBEAT; + else if (m_heartbeat == eHeartbeat::FiveMinutes) + m_heartbeatInterval = 300; + else if (m_heartbeat == eHeartbeat::Hourly) + m_heartbeatInterval = 7200; + else if (m_heartbeat == eHeartbeat::None) + m_heartbeatInterval = std::numeric_limits::max(); + + + /* Log the current settings for debugging purposes */ + kodi::Log(ADDON_LOG_INFO, "saved settings: host='%s', port=%i, instance=%d, mac=%4.4s...", m_hostname.c_str(), m_port, m_instanceNumber, m_hostMACAddress.c_str()); + +} + +std::string InstanceSettings::ReadSavedSetting(const char* key, tinyxml2::XMLNode* rootNode) +{ + std::string value; + tinyxml2::XMLElement* child = rootNode->FirstChildElement("setting"); + while (child != nullptr) + { + if (child->Attribute("id", key)) + { + value = child->GetText(); + break; + } + child = child->NextSiblingElement(); + } + return value; +} + +std::string InstanceSettings::ReadSavedStringSetting(const char* key, const std::string& defaultValue, tinyxml2::XMLNode* rootNode) +{ + std::string value = ReadSavedSetting(key, rootNode); + if (value.empty()) + return defaultValue; + return value; +} +int InstanceSettings::ReadSavedIntSetting(const char* key, int defaultValue, tinyxml2::XMLNode* rootNode) +{ + std::string value = ReadSavedSetting(key, rootNode); + if (value.empty()) + return defaultValue; + return stoi(value); +} + +bool InstanceSettings::ReadSavedBoolSetting(const char* key, bool defaultValue, tinyxml2::XMLNode* rootNode) +{ + std::string value = ReadSavedSetting(key, rootNode); + if (value.empty()) + return defaultValue; + return value == "true"; +} + ADDON_STATUS InstanceSettings::ReadBackendSettings(tinyxml2::XMLDocument& settingsDoc) { // check server version @@ -204,9 +391,37 @@ void InstanceSettings::SetConnection(bool status) { if (status == true) { + std::string filename = m_instanceDirectory + connectionFlag; + if (kodi::vfs::FileExists(filename)) + { kodi::vfs::CFile outputFile; - outputFile.OpenFileForWrite(m_instanceDirectory + connectionFlag); - m_connectionConfirmed = true; + outputFile.OpenFileForWrite(filename); + outputFile.Close(); + std::string cachedSettings = kodi::tools::StringUtils::Format("%sinstance-settings-%d.cache", kodi::vfs::TranslateSpecialProtocol(m_instanceDirectory).c_str(), m_instanceNumber); + if (!kodi::vfs::FileExists(cachedSettings)) + { + kodi::vfs::CFile inputFile; + std::string addonSettings = kodi::tools::StringUtils::Format("special://profile/addon_data/pvr.nextpvr/instance-settings-%d.xml", m_instanceNumber); + if (inputFile.OpenFile(kodi::vfs::TranslateSpecialProtocol(addonSettings), ADDON_READ_NO_CACHE)) + { + ssize_t written = 0; + if (outputFile.OpenFileForWrite(cachedSettings)) + { + char buffer[1024]; + int datalen; + while ((datalen = inputFile.Read(buffer, sizeof(buffer)))) + { + outputFile.Write(buffer, datalen); + written += datalen; + + } + outputFile.Close(); + } + inputFile.Close(); + } + } + } + m_connectionConfirmed = true; } else { @@ -219,6 +434,24 @@ bool InstanceSettings::CheckInstanceSettings() { const std::string instanceFile = kodi::tools::StringUtils::Format("special://profile/addon_data/pvr.nextpvr/instance-settings-%d.xml", m_instanceNumber); bool instanceExists = kodi::vfs::FileExists(instanceFile); + + #if defined(TARGET_DARWIN_EMBEDDED) + struct stat sb; + std::string original = kodi::vfs::TranslateSpecialProtocol(instanceFile); + kodi::Log(ADDON_LOG_INFO, "Instance xml exit check %d %s", instanceExists, instanceFile.c_str()); + kodi::Log(ADDON_LOG_INFO, "Instance xml exit stat check %d %s %d", stat(original.c_str(), &sb), original.c_str(), errno); + if (!m_instance.CheckInstanceSettingString("host", original)) + { + kodi::Log(ADDON_LOG_INFO, "Removing tvOS instance cache %s", m_instanceDirectory.c_str()); + instanceExists = false; + } + else + { + kodi::Log(ADDON_LOG_INFO, "Instance tvOS returned host %s", original.c_str()); + instanceExists = true; + } + #endif + if (!instanceExists) { // instance xml deleted by Addon core remove cache for this instance. diff --git a/src/InstanceSettings.h b/src/InstanceSettings.h index 035c535e..e74c1e55 100644 --- a/src/InstanceSettings.h +++ b/src/InstanceSettings.h @@ -60,7 +60,6 @@ namespace NextPVR void SetConnection(bool status); void SetVersionSpecificSettings(); void UpdateServerPort(std::string hostname, int port); - void ReadFromAddon(); ADDON_STATUS SetValue(const std::string& settingName, const kodi::addon::CSettingValue& settingValue); //Connection @@ -137,6 +136,12 @@ namespace NextPVR int ReadIntSetting(const std::string& key, int def) const; bool ReadBoolSetting(const std::string& key, bool def) const; + void ReadFromAddon(); + void ReadFromSavedSettings(std::string savedSettings); + std::string ReadSavedSetting(const char* key, tinyxml2::XMLNode* rootNode); + std::string ReadSavedStringSetting(const char* key, const std::string& defaultValue, tinyxml2::XMLNode* rootNode); + int ReadSavedIntSetting(const char* key, int defaultValue, tinyxml2::XMLNode* rootNode); + bool ReadSavedBoolSetting(const char* key, bool defaultValue, tinyxml2::XMLNode* rootNode); template V SetSetting(const std::string& settingName, const kodi::addon::CSettingValue& settingValue, T& currentValue, V returnValueIfChanged, V defaultReturnValue) { diff --git a/src/addon.cpp b/src/addon.cpp index 7fe962eb..5e6e7310 100644 --- a/src/addon.cpp +++ b/src/addon.cpp @@ -38,7 +38,7 @@ ADDON_STATUS CNextPVRAddon::CreateInstance(const kodi::addon::IInstanceInfo& ins /* Create connection to NextPVR KODI TV client */ cPVRClientNextPVR* client = new cPVRClientNextPVR(*this, instance, IsFirstInstance()); - if (SettingsMigration::MigrateSettings(*client)) + if (IsFirstInstance() && SettingsMigration::MigrateSettings(*client)) { // Initial client operated on old/incomplete settings delete client; diff --git a/src/utilities/SettingsMigration.cpp b/src/utilities/SettingsMigration.cpp index be39fea2..abfd344e 100644 --- a/src/utilities/SettingsMigration.cpp +++ b/src/utilities/SettingsMigration.cpp @@ -11,6 +11,7 @@ #include "kodi/General.h" #include "kodi/Filesystem.h" +#include #include #include @@ -56,12 +57,66 @@ bool SettingsMigration::MigrateSettings(kodi::addon::IAddonInstance& target) bool boolValue{false}; int intValue{0}; - if (target.CheckInstanceSettingString("kodi_addon_instance_name", stringValue) && - !stringValue.empty()) + #if defined(TARGET_DARWIN_EMBEDDED) + struct stat sb; + kodi::vfs::CFile setting; + std::string instanceFile = kodi::tools::StringUtils::Format("special://profile/addon_data/pvr.nextpvr/instance-settings-1.xml"); + if (setting.OpenFile(instanceFile, ADDON_READ_NO_CACHE)) + { + kodi::Log(ADDON_LOG_INFO, "Instance vfs xml opened for read"); + } + else + { + kodi::Log(ADDON_LOG_INFO, "Instance vfs xml did not open"); + } + setting.Close(); + bool instanceExists = kodi::vfs::FileExists(instanceFile); + std::string original = kodi::vfs::TranslateSpecialProtocol(instanceFile); + kodi::Log(ADDON_LOG_INFO, "Instance xml check %d %s", instanceExists, instanceFile.c_str()); + kodi::Log(ADDON_LOG_INFO, "Instance xml check stat %d %s %d", stat(original.c_str(), &sb), original.c_str(), errno); + std::string response; + if (setting.OpenFile(original, ADDON_READ_NO_CACHE)) + { + kodi::Log(ADDON_LOG_INFO, "Instance xml opened %d %s", setting.GetLength(), original.c_str()); + char buffer[1025] = { 0 }; + int count; + while ((count = setting.Read(buffer, 1024))) + { + response.append(buffer, count); + } + kodi::Log(ADDON_LOG_INFO, "Instance xml read %s", response.c_str()); + } + else + { + kodi::Log(ADDON_LOG_INFO, "Instance xml read error %d", errno); + } + setting.Close(); + #endif + target.CheckInstanceSettingString("kodi_addon_instance_name", stringValue); + if (!stringValue.empty()) { // Instance already has valid instance settings + kodi::Log(ADDON_LOG_INFO, "Using saved instance file %s", stringValue.c_str()); + return false; + } + + #if defined(TARGET_DARWIN_EMBEDDED) + kodi::Log(ADDON_LOG_INFO, "Empty instance name tvOS %d %s", target.IsInstanceSettingUsingDefault("kodi_addon_instance_name"), stringValue.c_str()); + std::string title; + target.CheckInstanceSettingString("host", title); + kodi::Log(ADDON_LOG_INFO, "Use tvOS hostname %d %s", target.IsInstanceSettingUsingDefault("host"), title.c_str()); + target.SetInstanceSettingString("kodi_addon_instance_name", title); + instanceFile = kodi::tools::StringUtils::Format("special://profile/addon_data/pvr.nextpvr/1/"); + instanceExists = kodi::vfs::DirectoryExists(instanceFile); + original = kodi::vfs::TranslateSpecialProtocol(instanceFile); + kodi::Log(ADDON_LOG_INFO, "Instance folder check %d %s", instanceExists, instanceFile.c_str()); + kodi::Log(ADDON_LOG_INFO, "Instance folder check stat %d %s %d", stat(original.c_str(), &sb), original.c_str(), errno); + if (instanceExists || stat(original.c_str(), &sb) == 0) + { return false; } + #endif + // ask XBMC to read settings for us tinyxml2::XMLDocument doc; @@ -102,7 +157,8 @@ bool SettingsMigration::MigrateSettings(kodi::addon::IAddonInstance& target) void SettingsMigration::MoveResourceFiles() { - std::string marti = kodi::vfs::TranslateSpecialProtocol("special://profile/addon_data/pvr.nextpvr/"); + std::string original = kodi::vfs::TranslateSpecialProtocol("special://profile/addon_data/pvr.nextpvr/"); + std::vector icons; if (kodi::vfs::GetDirectory("special://profile/addon_data/pvr.nextpvr/", "nextpvr-ch*.png", icons)) {