diff --git a/CMakeLists.txt b/CMakeLists.txt index 776efa38..a0ce7ca1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -20,6 +20,8 @@ set(VBOX_SOURCES src/VBoxInstance.cpp) set(VBOX_SOURCES_VBOX + src/vbox/AddonSettings.h + src/vbox/AddonSettings.cpp src/vbox/CategoryGenreMapper.h src/vbox/CategoryGenreMapper.cpp src/vbox/Channel.h @@ -29,13 +31,16 @@ set(VBOX_SOURCES_VBOX src/vbox/Exceptions.h src/vbox/GuideChannelMapper.h src/vbox/GuideChannelMapper.cpp + src/vbox/InstanceSettings.h + src/vbox/InstanceSettings.cpp src/vbox/Recording.h src/vbox/Recording.cpp src/vbox/RecordingReader.cpp src/vbox/RecordingReader.h src/vbox/SeriesRecording.h src/vbox/SeriesRecording.cpp - src/vbox/Settings.h + src/vbox/SettingsMigration.h + src/vbox/SettingsMigration.cpp src/vbox/SoftwareVersion.h src/vbox/SoftwareVersion.cpp src/vbox/StartupStateHandler.h diff --git a/README.md b/README.md index f7f8871c..4ec503ee 100644 --- a/README.md +++ b/README.md @@ -87,7 +87,6 @@ Settings related to Channels & EPG. - `LCN (Logical Channel Number) from backend` - The channel numbers as set on the backend. - `Channel index in backend` - Starting from 1 number the channels as per the order they appear on the backend. * **Reminder time (minutes before programme start)**: The amount of time in minutes prior to a programme start that a reminder should pop up. -* **Skip initial EPG load**: Ignore the initial EPG load. Enabled by default to prevent crash issues on LibreElec/CoreElec. ### Timeshift Settings related to the timeshift. diff --git a/pvr.vbox/addon.xml.in b/pvr.vbox/addon.xml.in index 12745c54..73b35950 100644 --- a/pvr.vbox/addon.xml.in +++ b/pvr.vbox/addon.xml.in @@ -1,7 +1,7 @@ @ADDON_DEPENDS@ diff --git a/pvr.vbox/changelog.txt b/pvr.vbox/changelog.txt index 1ec9a391..c3cb93ce 100644 --- a/pvr.vbox/changelog.txt +++ b/pvr.vbox/changelog.txt @@ -1,3 +1,7 @@ +v21.1.0 +- Add support for multiple backends +- Remove skip intial EPG, no longer required in Kodi Nexus + v20.3.0 - Kodi inputstream API update to version 3.2.0 - Kodi PVR API update to version 8.0.2 diff --git a/pvr.vbox/resources/instance-settings.xml b/pvr.vbox/resources/instance-settings.xml new file mode 100644 index 00000000..60a3a22a --- /dev/null +++ b/pvr.vbox/resources/instance-settings.xml @@ -0,0 +1,157 @@ + + +
+ + + + + + 0 + + + true + + + + + 1 + 80 + + 1 + 1 + 65535 + + + + + 2 + 0 + + 0 + 1 + 65535 + + + + + 2 + 55555 + + 1 + 1 + 65535 + + + + + 3 + 3 + + 1 + 1 + 60 + + + true + 14045 + + + + + + 2 + + + true + + + + + 2 + 19999 + + 1 + 1 + 65535 + + + + + 2 + 0 + + 0 + 1 + 65535 + + + + + 2 + 55555 + + 1 + 1 + 65535 + + + + + 3 + 10 + + 1 + 1 + 60 + + + true + 14045 + + + + + + + + + + 0 + 0 + + + + + + + + + + + + + + + + 0 + false + + + + 2 + special://userdata/addon_data/pvr.vbox + + true + true + + + true + + + 657 + + + + +
+
\ No newline at end of file diff --git a/pvr.vbox/resources/language/resource.language.en_gb/strings.po b/pvr.vbox/resources/language/resource.language.en_gb/strings.po index d579c690..94f760b2 100644 --- a/pvr.vbox/resources/language/resource.language.en_gb/strings.po +++ b/pvr.vbox/resources/language/resource.language.en_gb/strings.po @@ -89,11 +89,7 @@ msgctxt "#30026" msgid "Reminder time (minutes before program starts)" msgstr "" -msgctxt "#30027" -msgid "Skip initial EPG load" -msgstr "" - -#empty strings from id 30028 to 30039 +#empty strings from id 30027 to 30039 msgctxt "#30040" msgid "Timeshift" @@ -205,7 +201,7 @@ msgstr "" #empty strings from id 30624 to 30639 msgctxt "#30640" -msgid "Settings related to the timeshift." +msgid "Settings related to timeshift." msgstr "" msgctxt "#30641" diff --git a/pvr.vbox/resources/settings.xml b/pvr.vbox/resources/settings.xml index f12f9bb5..01c39e9f 100644 --- a/pvr.vbox/resources/settings.xml +++ b/pvr.vbox/resources/settings.xml @@ -1,196 +1,77 @@ -
+
- - - - - 0 + + + + + 4 true - - - 1 + + 4 80 - - 1 - 1 - 65535 - - - - 2 + + 4 0 - - 0 - 1 - 65535 - - - - 2 + + 4 55555 - - 1 - 1 - 65535 - - - - 3 + + 4 3 - - 1 - 1 - 60 - - - true - 14045 - - - - - 2 + + 4 true - - - 2 + + 4 19999 - - 1 - 1 - 65535 - - - - 2 + + 4 0 - - 0 - 1 - 65535 - - - - 2 + + 4 55555 - - 1 - 1 - 65535 - - - - 3 + + 4 10 - - 1 - 1 - 60 - - - true - 14045 - - - - - - - - 0 + + + 4 0 - - - - - - - - - - - 2 - true - - - - - - - - - 0 + + + 4 false - - - 2 + + 4 special://userdata/addon_data/pvr.vbox - - true - true - - - true - - - 657 -
- - \ No newline at end of file diff --git a/src/VBoxInstance.cpp b/src/VBoxInstance.cpp index 32ba0223..f7b6d02f 100644 --- a/src/VBoxInstance.cpp +++ b/src/VBoxInstance.cpp @@ -12,7 +12,7 @@ #include "timeshift/FilesystemBuffer.h" #include "vbox/ContentIdentifier.h" #include "vbox/RecordingReader.h" -#include "vbox/Settings.h" +#include "vbox/InstanceSettings.h" #include @@ -24,11 +24,10 @@ using namespace vbox; unsigned int MENUHOOK_ID_RESCAN_EPG = 1; unsigned int MENUHOOK_ID_SYNC_EPG = 2; -CVBoxInstance::CVBoxInstance(const Settings& settings, const kodi::addon::IInstanceInfo& instance) - : kodi::addon::CInstancePVRClient(instance), - VBox(settings) +CVBoxInstance::CVBoxInstance(const kodi::addon::IInstanceInfo& instance) + : kodi::addon::CInstancePVRClient(instance), VBox() { - + m_settings = std::make_shared(*this); } CVBoxInstance::~CVBoxInstance() @@ -36,6 +35,12 @@ CVBoxInstance::~CVBoxInstance() delete m_timeshiftBuffer; } +ADDON_STATUS CVBoxInstance::SetInstanceSetting(const std::string& settingName, + const kodi::addon::CSettingValue& settingValue) +{ + return m_settings->SetSetting(settingName, settingValue); +} + ADDON_STATUS CVBoxInstance::Initialize() { ADDON_STATUS status = ADDON_STATUS_UNKNOWN; @@ -62,8 +67,8 @@ ADDON_STATUS CVBoxInstance::Initialize() }; // Create the timeshift buffer - if (VBox::GetSettings().m_timeshiftEnabled) - m_timeshiftBuffer = new timeshift::FilesystemBuffer(VBox::GetSettings().m_timeshiftBufferPath); + if (m_settings->m_timeshiftEnabled) + m_timeshiftBuffer = new timeshift::FilesystemBuffer(m_settings->m_timeshiftBufferPath); else m_timeshiftBuffer = new timeshift::DummyBuffer(); @@ -211,7 +216,7 @@ PVR_ERROR CVBoxInstance::GetChannels(bool radio, kodi::addon::PVRChannelsResultS // Override LCN if backend channel order should be forced ++i; - if (VBox::GetSettings().m_setChannelIdUsingOrder == CH_ORDER_BY_INDEX) + if (m_settings->m_setChannelIdUsingOrder == CH_ORDER_BY_INDEX) channel.SetChannelNumber(i); // default - CH_ORDER_BY_LCN else @@ -665,13 +670,6 @@ PVR_ERROR CVBoxInstance::GetEPGForChannel(int channelUid, time_t start, time_t e if (!channelPtr) return PVR_ERROR_INVALID_PARAMETERS; - if (m_skippingInitialEpgLoad) - { - kodi::Log(ADDON_LOG_DEBUG, "%s Skipping Initial EPG for channel: %s", __FUNCTION__, channelPtr->m_name.c_str()); - VBox::MarkChannelAsInitialEpgSkipped(channelUid); - return PVR_ERROR_NO_ERROR; - } - // Retrieve the schedule const auto schedule = VBox::GetSchedule(channelPtr); @@ -777,12 +775,12 @@ int64_t CVBoxInstance::SeekLiveStream(int64_t iPosition, int iWhence /* = SEEK_S bool CVBoxInstance::CanPauseStream() { - return VBox::GetSettings().m_timeshiftEnabled; + return m_settings->m_timeshiftEnabled; } bool CVBoxInstance::CanSeekStream() { - return VBox::GetSettings().m_timeshiftEnabled; + return m_settings->m_timeshiftEnabled; } bool CVBoxInstance::IsRealTimeStream() @@ -796,7 +794,7 @@ bool CVBoxInstance::IsRealTimeStream() PVR_ERROR CVBoxInstance::GetStreamTimes(kodi::addon::PVRStreamTimes& times) { // Addon API 5.8.0 - if (IsRealTimeStream() && m_timeshiftBuffer && VBox::GetSettings().m_timeshiftEnabled) + if (IsRealTimeStream() && m_timeshiftBuffer && m_settings->m_timeshiftEnabled) { times.SetStartTime(m_timeshiftBuffer->GetStartTime()); times.SetPTSStart(0); diff --git a/src/VBoxInstance.h b/src/VBoxInstance.h index 48dec3ff..708d4d3d 100644 --- a/src/VBoxInstance.h +++ b/src/VBoxInstance.h @@ -25,11 +25,15 @@ class RecordingReader; class ATTR_DLL_LOCAL CVBoxInstance : public kodi::addon::CInstancePVRClient, private vbox::VBox { public: - CVBoxInstance(const vbox::Settings& settings, const kodi::addon::IInstanceInfo& instance); + CVBoxInstance(const kodi::addon::IInstanceInfo& instance); ~CVBoxInstance(); + // kodi::addon::CInstancePVRClient -> kodi::addon::IAddonInstance overrides + ADDON_STATUS SetInstanceSetting(const std::string& settingName, + const kodi::addon::CSettingValue& settingValue) override; + ADDON_STATUS Initialize(); - const vbox::Settings& GetSettings() const { return vbox::VBox::GetSettings(); } + const vbox::InstanceSettings& GetSettings() const { return vbox::VBox::GetSettings(); } PVR_ERROR GetCapabilities(kodi::addon::PVRCapabilities& capabilities) override; PVR_ERROR GetBackendName(std::string& name) override; diff --git a/src/addon.cpp b/src/addon.cpp index ca7f508b..96ac4fdb 100644 --- a/src/addon.cpp +++ b/src/addon.cpp @@ -8,21 +8,37 @@ #include "addon.h" #include "VBoxInstance.h" +#include "vbox/SettingsMigration.h" using namespace vbox; +ADDON_STATUS CVBoxAddon::Create() +{ + /* Init settings */ + m_settings.reset(new AddonSettings()); + + kodi::Log(ADDON_LOG_DEBUG, "%s starting PVR client...", __func__); + + return ADDON_STATUS_OK; +} + ADDON_STATUS CVBoxAddon::CreateInstance(const kodi::addon::IInstanceInfo& instance, KODI_ADDON_INSTANCE_HDL& hdl) { if (instance.IsType(ADDON_INSTANCE_PVR)) { kodi::Log(ADDON_LOG_DEBUG, "creating VBox Gateway PVR addon"); - Settings settings; - ReadSettings(settings); - - m_vbox = new CVBoxInstance(settings, instance); + m_vbox = new CVBoxInstance(instance); ADDON_STATUS status = m_vbox->Initialize(); + // Try to migrate settings from a pre-multi-instance setup + if (SettingsMigration::MigrateSettings(*m_vbox)) + { + // Initial client operated on old/incomplete settings + delete m_vbox; + m_vbox = new CVBoxInstance(instance); + } + hdl = m_vbox; return status; @@ -31,93 +47,17 @@ ADDON_STATUS CVBoxAddon::CreateInstance(const kodi::addon::IInstanceInfo& instan return ADDON_STATUS_UNKNOWN; } -void CVBoxAddon::DestroyInstance(const kodi::addon::IInstanceInfo& instance, const KODI_ADDON_INSTANCE_HDL hdl) -{ - if (instance.IsType(ADDON_INSTANCE_PVR)) - { - m_vbox = nullptr; - } -} - -void CVBoxAddon::ReadSettings(Settings& settings) +ADDON_STATUS CVBoxAddon::SetSetting(const std::string& settingName, const kodi::addon::CSettingValue& settingValue) { - settings.m_internalConnectionParams.hostname = kodi::addon::GetSettingString("hostname", ""); - settings.m_internalConnectionParams.httpPort = kodi::addon::GetSettingInt("http_port", 80); - settings.m_internalConnectionParams.httpsPort = kodi::addon::GetSettingInt("https_port", 0); - settings.m_internalConnectionParams.upnpPort = kodi::addon::GetSettingInt("upnp_port", 55555); - settings.m_internalConnectionParams.timeout = kodi::addon::GetSettingInt("connection_timeout", 3); - - settings.m_externalConnectionParams.hostname = kodi::addon::GetSettingString("external_hostname", ""); - settings.m_externalConnectionParams.httpPort = kodi::addon::GetSettingInt("external_http_port", 19999); - settings.m_externalConnectionParams.httpsPort = kodi::addon::GetSettingInt("external_https_port", 0); - settings.m_externalConnectionParams.upnpPort = kodi::addon::GetSettingInt("external_upnp_port", 55555); - settings.m_externalConnectionParams.timeout = kodi::addon::GetSettingInt("connection_timeout", 10); - - settings.m_setChannelIdUsingOrder = kodi::addon::GetSettingEnum("set_channelid_using_order", CH_ORDER_BY_LCN); - settings.m_skipInitialEpgLoad = kodi::addon::GetSettingBoolean("skip_initial_epg_load", true); - settings.m_timeshiftEnabled = kodi::addon::GetSettingBoolean("timeshift_enabled", false); - settings.m_timeshiftBufferPath = kodi::addon::GetSettingString("timeshift_path", ""); + return m_settings->SetSetting(settingName, settingValue); } -ADDON_STATUS CVBoxAddon::SetSetting(const std::string& settingName, const kodi::addon::CSettingValue& settingValue) +void CVBoxAddon::DestroyInstance(const kodi::addon::IInstanceInfo& instance, const KODI_ADDON_INSTANCE_HDL hdl) { -#define UPDATE_STR(key, var) \ - if (settingName == key) \ - { \ - if (var != settingValue.GetString()) \ - { \ - kodi::Log(ADDON_LOG_INFO, "updated setting %s from '%s' to '%s'", settingName.c_str(), var.c_str(), settingValue.GetString().c_str()); \ - return ADDON_STATUS_NEED_RESTART; \ - } \ - return ADDON_STATUS_OK; \ - } - -#define UPDATE_INT(key, var) \ - if (settingName == key) \ - { \ - if (var != settingValue.GetInt()) \ - { \ - kodi::Log(ADDON_LOG_INFO, "updated setting %s from '%d' to '%d'", settingName.c_str(), var, settingValue.GetInt()); \ - return ADDON_STATUS_NEED_RESTART; \ - } \ - return ADDON_STATUS_OK; \ - } - -#define UPDATE_BOOL(key, var) \ - if (settingName == key) \ - { \ - if (var != settingValue.GetBoolean()) \ - { \ - kodi::Log(ADDON_LOG_INFO, "updated setting %s from '%d' to '%d'", settingName.c_str(), var, settingValue.GetBoolean()); \ - return ADDON_STATUS_NEED_RESTART; \ - } \ - return ADDON_STATUS_OK; \ - } - - if (m_vbox) + if (instance.IsType(ADDON_INSTANCE_PVR)) { - const vbox::Settings& settings = m_vbox->GetSettings(); - - UPDATE_STR("hostname", settings.m_internalConnectionParams.hostname); - UPDATE_INT("http_port", settings.m_internalConnectionParams.httpPort); - UPDATE_INT("https_port", settings.m_internalConnectionParams.httpsPort); - UPDATE_INT("upnp_port", settings.m_internalConnectionParams.upnpPort); - UPDATE_INT("connection_timeout", settings.m_internalConnectionParams.timeout); - UPDATE_STR("external_hostname", settings.m_externalConnectionParams.hostname); - UPDATE_INT("external_http_port", settings.m_externalConnectionParams.httpPort); - UPDATE_INT("external_https_port", settings.m_externalConnectionParams.httpsPort); - UPDATE_INT("external_upnp_port", settings.m_externalConnectionParams.upnpPort); - UPDATE_INT("external_connection_timeout", settings.m_externalConnectionParams.timeout); - UPDATE_INT("set_channelid_using_order", settings.m_setChannelIdUsingOrder); - UPDATE_BOOL("skip_initial_epg_load", settings.m_skipInitialEpgLoad); - UPDATE_BOOL("timeshift_enabled", settings.m_timeshiftEnabled); - UPDATE_STR("timeshift_path", settings.m_timeshiftBufferPath); + m_vbox = nullptr; } - - return ADDON_STATUS_OK; -#undef UPDATE_BOOL -#undef UPDATE_INT -#undef UPDATE_STR } ADDONCREATOR(CVBoxAddon) diff --git a/src/addon.h b/src/addon.h index b1e5139e..9cda242a 100644 --- a/src/addon.h +++ b/src/addon.h @@ -10,9 +10,13 @@ #include +#include + +#include "vbox/AddonSettings.h" + namespace vbox { -class Settings; +class InstanceSettings; } class CVBoxInstance; @@ -22,12 +26,13 @@ class ATTR_DLL_LOCAL CVBoxAddon : public kodi::addon::CAddonBase public: CVBoxAddon() = default; + ADDON_STATUS Create() override; ADDON_STATUS CreateInstance(const kodi::addon::IInstanceInfo& instance, KODI_ADDON_INSTANCE_HDL& hdl) override; - void DestroyInstance(const kodi::addon::IInstanceInfo& instance, const KODI_ADDON_INSTANCE_HDL hdl) override; ADDON_STATUS SetSetting(const std::string& settingName, const kodi::addon::CSettingValue& settingValue) override; + void DestroyInstance(const kodi::addon::IInstanceInfo& instance, const KODI_ADDON_INSTANCE_HDL hdl) override; private: - void ReadSettings(vbox::Settings& settings); CVBoxInstance* m_vbox = nullptr; + std::shared_ptr m_settings; }; diff --git a/src/vbox/AddonSettings.cpp b/src/vbox/AddonSettings.cpp new file mode 100644 index 00000000..f62b176f --- /dev/null +++ b/src/vbox/AddonSettings.cpp @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2005-2022 Team Kodi (https://kodi.tv) + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSE.md for more information. + */ + +#include "AddonSettings.h" + +#include "SettingsMigration.h" + +#include "kodi/General.h" + +using namespace vbox; + +AddonSettings::AddonSettings() +{ + ReadSettings(); +} + +void AddonSettings::ReadSettings() +{ + // This add-on only has instance settings! +} + +ADDON_STATUS AddonSettings::SetSetting(const std::string& settingName, + const kodi::addon::CSettingValue& settingValue) +{ + if (SettingsMigration::IsMigrationSetting(settingName)) + { + // ignore settings from pre-multi-instance setup + return ADDON_STATUS_OK; + } + + kodi::Log(ADDON_LOG_ERROR, "AddonSettings::SetSetting - unknown setting '%s'", + settingName.c_str()); + return ADDON_STATUS_UNKNOWN; +} \ No newline at end of file diff --git a/src/vbox/AddonSettings.h b/src/vbox/AddonSettings.h new file mode 100644 index 00000000..01c2123b --- /dev/null +++ b/src/vbox/AddonSettings.h @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2005-2022 Team Kodi (https://kodi.tv) + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSE.md for more information. + */ + +#pragma once + +#include + +#include "kodi/AddonBase.h" + +namespace vbox +{ +/** + * Represents the current addon settings + */ +class AddonSettings +{ + public: + AddonSettings(); + + /** + * Set a value according to key definition in settings.xml + */ + ADDON_STATUS SetSetting(const std::string& settingName, const kodi::addon::CSettingValue& settingValue); + + private: + AddonSettings(const AddonSettings&) = delete; + void operator=(const AddonSettings&) = delete; + + /** + * Read all settings defined in settings.xml + */ + void ReadSettings(); +}; + +} // namespace enigma2 \ No newline at end of file diff --git a/src/vbox/InstanceSettings.cpp b/src/vbox/InstanceSettings.cpp new file mode 100644 index 00000000..348376f9 --- /dev/null +++ b/src/vbox/InstanceSettings.cpp @@ -0,0 +1,90 @@ +/* + * Copyright (C) 2005-2021 Team Kodi (https://kodi.tv) + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSE.md for more information. + */ + +#include "InstanceSettings.h" + +using namespace vbox; + +InstanceSettings::InstanceSettings(kodi::addon::IAddonInstance& instance) + : m_instance(instance) +{ + ReadSettings(); +} + +void InstanceSettings::ReadSettings() +{ + m_internalConnectionParams.hostname = kodi::addon::GetSettingString("hostname", ""); + m_internalConnectionParams.httpPort = kodi::addon::GetSettingInt("http_port", 80); + m_internalConnectionParams.httpsPort = kodi::addon::GetSettingInt("https_port", 0); + m_internalConnectionParams.upnpPort = kodi::addon::GetSettingInt("upnp_port", 55555); + m_internalConnectionParams.timeout = kodi::addon::GetSettingInt("connection_timeout", 3); + + m_externalConnectionParams.hostname = kodi::addon::GetSettingString("external_hostname", ""); + m_externalConnectionParams.httpPort = kodi::addon::GetSettingInt("external_http_port", 19999); + m_externalConnectionParams.httpsPort = kodi::addon::GetSettingInt("external_https_port", 0); + m_externalConnectionParams.upnpPort = kodi::addon::GetSettingInt("external_upnp_port", 55555); + m_externalConnectionParams.timeout = kodi::addon::GetSettingInt("connection_timeout", 10); + + m_setChannelIdUsingOrder = kodi::addon::GetSettingEnum("set_channelid_using_order", CH_ORDER_BY_LCN); + m_timeshiftEnabled = kodi::addon::GetSettingBoolean("timeshift_enabled", false); + m_timeshiftBufferPath = kodi::addon::GetSettingString("timeshift_path", ""); +} + +ADDON_STATUS InstanceSettings::SetSetting(const std::string& settingName, const kodi::addon::CSettingValue& settingValue) +{ +#define UPDATE_STR(key, var) \ + if (settingName == key) \ + { \ + if (var != settingValue.GetString()) \ + { \ + kodi::Log(ADDON_LOG_INFO, "updated setting %s from '%s' to '%s'", settingName.c_str(), var.c_str(), settingValue.GetString().c_str()); \ + return ADDON_STATUS_NEED_RESTART; \ + } \ + return ADDON_STATUS_OK; \ + } + +#define UPDATE_INT(key, var) \ + if (settingName == key) \ + { \ + if (var != settingValue.GetInt()) \ + { \ + kodi::Log(ADDON_LOG_INFO, "updated setting %s from '%d' to '%d'", settingName.c_str(), var, settingValue.GetInt()); \ + return ADDON_STATUS_NEED_RESTART; \ + } \ + return ADDON_STATUS_OK; \ + } + +#define UPDATE_BOOL(key, var) \ + if (settingName == key) \ + { \ + if (var != settingValue.GetBoolean()) \ + { \ + kodi::Log(ADDON_LOG_INFO, "updated setting %s from '%d' to '%d'", settingName.c_str(), var, settingValue.GetBoolean()); \ + return ADDON_STATUS_NEED_RESTART; \ + } \ + return ADDON_STATUS_OK; \ + } + + UPDATE_STR("hostname", m_internalConnectionParams.hostname); + UPDATE_INT("http_port", m_internalConnectionParams.httpPort); + UPDATE_INT("https_port", m_internalConnectionParams.httpsPort); + UPDATE_INT("upnp_port", m_internalConnectionParams.upnpPort); + UPDATE_INT("connection_timeout", m_internalConnectionParams.timeout); + UPDATE_STR("external_hostname", m_externalConnectionParams.hostname); + UPDATE_INT("external_http_port", m_externalConnectionParams.httpPort); + UPDATE_INT("external_https_port", m_externalConnectionParams.httpsPort); + UPDATE_INT("external_upnp_port", m_externalConnectionParams.upnpPort); + UPDATE_INT("external_connection_timeout", m_externalConnectionParams.timeout); + UPDATE_INT("set_channelid_using_order", m_setChannelIdUsingOrder); + UPDATE_BOOL("timeshift_enabled", m_timeshiftEnabled); + UPDATE_STR("timeshift_path", m_timeshiftBufferPath); + + return ADDON_STATUS_OK; +#undef UPDATE_BOOL +#undef UPDATE_INT +#undef UPDATE_STR +} diff --git a/src/vbox/Settings.h b/src/vbox/InstanceSettings.h similarity index 76% rename from src/vbox/Settings.h rename to src/vbox/InstanceSettings.h index 3adad0e3..94958145 100644 --- a/src/vbox/Settings.h +++ b/src/vbox/InstanceSettings.h @@ -11,6 +11,8 @@ #include #include +#include + namespace vbox { @@ -23,7 +25,7 @@ namespace vbox /** * Represents a set of parameters required to make a connection */ - class ConnectionParameters + class ATTR_DLL_LOCAL ConnectionParameters { public: std::string hostname; @@ -69,14 +71,24 @@ namespace vbox /** * Represents the settings for this addon */ - class Settings + class ATTR_DLL_LOCAL InstanceSettings { public: + explicit InstanceSettings(kodi::addon::IAddonInstance& instance); + ADDON_STATUS SetSetting(const std::string& settingName, const kodi::addon::CSettingValue& settingValue); + ConnectionParameters m_internalConnectionParams; ConnectionParameters m_externalConnectionParams; ChannelOrder m_setChannelIdUsingOrder; - bool m_skipInitialEpgLoad; bool m_timeshiftEnabled; std::string m_timeshiftBufferPath; + + private: + InstanceSettings(const InstanceSettings&) = delete; + void operator=(const InstanceSettings&) = delete; + + void ReadSettings(); + + kodi::addon::IAddonInstance& m_instance; }; } // namespace vbox diff --git a/src/vbox/SettingsMigration.cpp b/src/vbox/SettingsMigration.cpp new file mode 100644 index 00000000..a999f7d4 --- /dev/null +++ b/src/vbox/SettingsMigration.cpp @@ -0,0 +1,117 @@ +/* + * Copyright (C) 2005-2022 Team Kodi (https://kodi.tv) + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSE.md for more information. + */ + +#include "SettingsMigration.h" + +#include "kodi/General.h" + +#include +#include +#include + +using namespace vbox; + +namespace +{ +// maps +const std::vector> stringMap = {{"hostname", ""}, + {"external_hostname", ""}, + {"timeshift_path", "special://userdata/addon_data/pvr.vbox"}}; + +const std::vector> intMap = {{"http_port", 80}, + {"https_port", 0}, + {"upnp_port", 55555}, + {"connection_timeout", 3}, + {"external_http_port", 19999}, + {"external_https_port", 0}, + {"external_upnp_port", 55555}, + {"external_connection_timeout", 10}, + {"set_channelid_using_order", 0}}; + +const std::vector> boolMap = {{"timeshift_enabled", false}}; + +} // unnamed namespace + +bool SettingsMigration::MigrateSettings(kodi::addon::IAddonInstance& target) +{ + std::string stringValue; + bool boolValue{false}; + int intValue{0}; + + if (target.CheckInstanceSettingString("kodi_addon_instance_name", stringValue) && + !stringValue.empty()) + { + // Instance already has valid instance settings + return false; + } + + // Read pre-multi-instance settings from settings.xml, transfer to instance settings + SettingsMigration mig(target); + + for (const auto& setting : stringMap) + mig.MigrateStringSetting(setting.first, setting.second); + + for (const auto& setting : intMap) + mig.MigrateIntSetting(setting.first, setting.second); + + for (const auto& setting : boolMap) + mig.MigrateBoolSetting(setting.first, setting.second); + + if (mig.Changed()) + { + // Set a title for the new instance settings + std::string title; + target.CheckInstanceSettingString("hostname", title); + if (title.empty()) + title = "Migrated Add-on Config"; + + target.SetInstanceSettingString("kodi_addon_instance_name", title); + + return true; + } + return false; +} + +bool SettingsMigration::IsMigrationSetting(const std::string& key) +{ + return std::any_of(stringMap.cbegin(), stringMap.cend(), + [&key](const auto& entry) { return entry.first == key; }) || + std::any_of(intMap.cbegin(), intMap.cend(), + [&key](const auto& entry) { return entry.first == key; }) || + std::any_of(boolMap.cbegin(), boolMap.cend(), + [&key](const auto& entry) { return entry.first == key; }); +} + +void SettingsMigration::MigrateStringSetting(const char* key, const std::string& defaultValue) +{ + std::string value; + if (kodi::addon::CheckSettingString(key, value) && value != defaultValue) + { + m_target.SetInstanceSettingString(key, value); + m_changed = true; + } +} + +void SettingsMigration::MigrateIntSetting(const char* key, int defaultValue) +{ + int value; + if (kodi::addon::CheckSettingInt(key, value) && value != defaultValue) + { + m_target.SetInstanceSettingInt(key, value); + m_changed = true; + } +} + +void SettingsMigration::MigrateBoolSetting(const char* key, bool defaultValue) +{ + bool value; + if (kodi::addon::CheckSettingBoolean(key, value) && value != defaultValue) + { + m_target.SetInstanceSettingBoolean(key, value); + m_changed = true; + } +} diff --git a/src/vbox/SettingsMigration.h b/src/vbox/SettingsMigration.h new file mode 100644 index 00000000..73d8a40d --- /dev/null +++ b/src/vbox/SettingsMigration.h @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2005-2022 Team Kodi (https://kodi.tv) + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSE.md for more information. + */ + +#pragma once + +#include + +namespace kodi +{ +namespace addon +{ +class IAddonInstance; +} +} // namespace kodi + +namespace vbox +{ + +class SettingsMigration +{ +public: + static bool MigrateSettings(kodi::addon::IAddonInstance& target); + static bool IsMigrationSetting(const std::string& key); + +private: + SettingsMigration() = delete; + explicit SettingsMigration(kodi::addon::IAddonInstance& target) : m_target(target) {} + + void MigrateStringSetting(const char* key, const std::string& defaultValue); + void MigrateIntSetting(const char* key, int defaultValue); + void MigrateFloatSetting(const char* key, float defaultValue); + void MigrateBoolSetting(const char* key, bool defaultValue); + + bool Changed() const { return m_changed; } + + kodi::addon::IAddonInstance& m_target; + bool m_changed{false}; +}; + +} // namespace vbox diff --git a/src/vbox/VBox.cpp b/src/vbox/VBox.cpp index 97c24e68..eb1588f4 100644 --- a/src/vbox/VBox.cpp +++ b/src/vbox/VBox.cpp @@ -33,9 +33,8 @@ const int CHANNELS_PER_CHANNELBATCH = 100; const int CHANNELS_PER_EPGBATCH = 10; const size_t VBOX_LOG_BUFFER = 16384; -VBox::VBox(const Settings& settings) - : m_settings(settings), - m_currentChannel(nullptr), +VBox::VBox() + : m_currentChannel(nullptr), m_categoryGenreMapper(nullptr), m_shouldSyncEpg(false), m_lastStreamStatus({ChannelStreamingStatus(), time(nullptr)}) @@ -124,14 +123,7 @@ void VBox::Initialize() // Consider the addon initialized m_stateHandler.EnterState(StartupState::INITIALIZED); - m_skippingInitialEpgLoad = m_settings.m_skipInitialEpgLoad; - RetrieveChannels(false); - { - std::unique_lock lock(m_mutex); - for (auto channel : m_channels) - m_unskippedInitialEpgChannelsMap.insert({channel->m_uniqueId, channel->m_uniqueId}); - } // Start the background updater thread m_active = true; @@ -144,7 +136,7 @@ void VBox::Initialize() void VBox::DetermineConnectionParams() { // Attempt to perform a request using the internal connection parameters - m_currentConnectionParameters = m_settings.m_internalConnectionParams; + m_currentConnectionParameters = m_settings->m_internalConnectionParams; try { @@ -155,10 +147,10 @@ void VBox::DetermineConnectionParams() catch (VBoxException&) { // Retry the request with the external parameters - if (m_settings.m_externalConnectionParams.AreValid()) + if (m_settings->m_externalConnectionParams.AreValid()) { kodi::Log(ADDON_LOG_INFO, "Unable to connect using internal connection settings, trying with external"); - m_currentConnectionParameters = m_settings.m_externalConnectionParams; + m_currentConnectionParameters = m_settings->m_externalConnectionParams; request::ApiRequest request("QuerySwVersion", GetConnectionParams().hostname, GetConnectionParams().upnpPort); request.SetTimeout(m_currentConnectionParameters.timeout); @@ -242,18 +234,6 @@ void VBox::BackgroundUpdater() RetrieveRecordings(false); RetrieveGuide(false); - // Wait for the initial EPG update to complete - int totalWaitSecs = 0; - while (m_active && totalWaitSecs < INITIAL_EPG_WAIT_SECS) - { - totalWaitSecs += INITIAL_EPG_STEP_SECS; - - if (!IsInitialEpgSkippingCompleted()) - std::this_thread::sleep_for(std::chrono::milliseconds(INITIAL_EPG_STEP_SECS * 1000)); - } - - m_skippingInitialEpgLoad = false; - // Whether or not initial EPG updates occurred now Trigger "Real" EPG updates // This will regard Initial EPG as completed anyway. TriggerEpgUpdatesForChannels(); @@ -286,23 +266,12 @@ void VBox::BackgroundUpdater() } } -bool VBox::IsInitialEpgSkippingCompleted() -{ - kodi::Log(ADDON_LOG_DEBUG, "%s Waiting to Get Initial EPG for %d remaining channels", __FUNCTION__, - m_unskippedInitialEpgChannelsMap.size()); - - return m_unskippedInitialEpgChannelsMap.size() == 0; -} - void VBox::TriggerEpgUpdatesForChannels() { { std::unique_lock lock(m_mutex); for (auto& channel : m_channels) { - //We want to trigger full updates only so let's make sure it's not an initialEpg - m_unskippedInitialEpgChannelsMap.erase(channel->m_uniqueId); - kodi::Log(ADDON_LOG_DEBUG, "%s - Trigger EPG update for channel: %s (%s)", __FUNCTION__, channel->m_name.c_str(), channel->m_uniqueId.c_str()); } @@ -311,32 +280,20 @@ void VBox::TriggerEpgUpdatesForChannels() OnGuideUpdated(); } -void VBox::MarkChannelAsInitialEpgSkipped(unsigned int channelUid) -{ - const ChannelPtr channelPtr = GetChannel(channelUid); - - m_unskippedInitialEpgChannelsMap.erase(channelPtr->m_uniqueId); -} - bool VBox::ValidateSettings() const { // Check connection settings - if (!m_settings.m_internalConnectionParams.AreValid()) + if (!m_settings->m_internalConnectionParams.AreValid()) return false; // Check timeshift settings std::vector items; - if (m_settings.m_timeshiftEnabled && !kodi::vfs::GetDirectory(m_settings.m_timeshiftBufferPath, "", items)) + if (m_settings->m_timeshiftEnabled && !kodi::vfs::GetDirectory(m_settings->m_timeshiftBufferPath, "", items)) return false; return true; } -const Settings& VBox::GetSettings() const -{ - return m_settings; -} - const ConnectionParameters& VBox::GetConnectionParams() const { return m_currentConnectionParameters; diff --git a/src/vbox/VBox.h b/src/vbox/VBox.h index 67b78728..021ac1f8 100644 --- a/src/vbox/VBox.h +++ b/src/vbox/VBox.h @@ -18,7 +18,7 @@ #include "GuideChannelMapper.h" #include "Recording.h" #include "SeriesRecording.h" -#include "Settings.h" +#include "InstanceSettings.h" #include "SoftwareVersion.h" #include "StartupStateHandler.h" #include "request/ApiRequest.h" @@ -28,6 +28,7 @@ #include #include #include +#include #include #include #include @@ -124,7 +125,7 @@ namespace vbox */ static const char* MINIMUM_SOFTWARE_VERSION; - VBox(const Settings& settings); + VBox(); ~VBox(); /** @@ -133,7 +134,7 @@ namespace vbox void Initialize(); void DetermineConnectionParams(); bool ValidateSettings() const; - const Settings& GetSettings() const; + InstanceSettings& GetSettings() const; const ConnectionParameters& GetConnectionParams() const; StartupStateHandler& GetStateHandler(); std::string GetApiBaseUrl() const; @@ -191,8 +192,6 @@ namespace vbox void StartEPGScan(); void SyncEPGNow(); void TriggerEpgUpdatesForChannels(); - bool IsInitialEpgSkippingCompleted(); - void MarkChannelAsInitialEpgSkipped(unsigned int channelUid); // Helpers static void LogException(VBoxException& e); @@ -204,7 +203,10 @@ namespace vbox std::function OnGuideUpdated; protected: - bool m_skippingInitialEpgLoad = false; + /** + * The addons settings + */ + std::shared_ptr m_settings; private: static const int INITIAL_EPG_WAIT_SECS = 60; @@ -227,11 +229,6 @@ namespace vbox void LogGuideStatistics(const ::xmltv::Guide& guide) const; response::ResponsePtr PerformRequest(const request::Request& request) const; - /** - * The addons settings - */ - const Settings m_settings; - /** * The connection parameters to use for requests */ @@ -324,11 +321,6 @@ namespace vbox */ ChannelPtr m_currentChannel; - /** - * Map of channels to be skipped on inital EPG load - */ - std::map m_unskippedInitialEpgChannelsMap; - /** * Mutex for protecting access to m_channels and m_recordings */