diff --git a/CMakeLists.txt b/CMakeLists.txt
index 0c84ae0e7..521b1c3f3 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -42,6 +42,7 @@ set(IPTV_SOURCES src/addon.cpp
src/iptvsimple/data/MediaEntry.cpp
src/iptvsimple/utilities/FileUtils.cpp
src/iptvsimple/utilities/Logger.cpp
+ src/iptvsimple/utilities/SettingsMigration.cpp
src/iptvsimple/utilities/StreamUtils.cpp
src/iptvsimple/utilities/WebUtils.cpp)
@@ -68,6 +69,7 @@ set(IPTV_HEADERS src/addon.h
src/iptvsimple/data/StreamEntry.h
src/iptvsimple/utilities/FileUtils.h
src/iptvsimple/utilities/Logger.h
+ src/iptvsimple/utilities/SettingsMigration.h
src/iptvsimple/utilities/StreamUtils.h
src/iptvsimple/utilities/TimeUtils.h
src/iptvsimple/utilities/WebUtils.h
diff --git a/pvr.iptvsimple/resources/settings.xml b/pvr.iptvsimple/resources/settings.xml
index 58632c853..b64cab3f4 100644
--- a/pvr.iptvsimple/resources/settings.xml
+++ b/pvr.iptvsimple/resources/settings.xml
@@ -4,9 +4,394 @@
+ settings definition to work.
+
+ Note that empty default values still require an allowempty constraint -->
+
+ 4
+ 1
+
+
+ 4
+
+
+ true
+
+
+
+ 4
+
+
+ true
+
+
+
+ 4
+ true
+
+
+ 4
+ 1
+
+
+ 4
+ false
+
+
+
+
+ 4
+ 0
+
+
+ 4
+ 60
+
+
+ 4
+ 4
+
+
+
+ 4
+
+
+ true
+
+
+
+ 4
+ false
+
+
+ 4
+ special://userdata/addon_data/pvr.iptvsimple/providers/providerMappings.xml
+
+
+
+
+ 4
+ 0
+
+
+ 4
+ 1
+
+
+ 4
+
+
+ true
+
+
+
+ 4
+
+
+ true
+
+
+
+ 4
+
+
+ true
+
+
+
+ 4
+
+
+ true
+
+
+
+ 4
+
+
+ true
+
+
+
+ 4
+ special://userdata/addon_data/pvr.iptvsimple/channelGroups/customTVGroups-example.xml
+
+
+ 4
+ false
+
+
+
+ 4
+ 0
+
+
+ 4
+ 1
+
+
+ 4
+
+
+ true
+
+
+
+ 4
+
+
+ true
+
+
+
+ 4
+
+
+ true
+
+
+
+ 4
+
+
+ true
+
+
+
+ 4
+
+
+ true
+
+
+
+ 4
+ special://userdata/addon_data/pvr.iptvsimple/channelGroups/customRadioGroups-example.xml
+
+
+ 4
+ false
+
+
+
+
+ 4
+ 1
+
+
+ 4
+
+
+ true
+
+
+
+ 4
+
+
+ true
+
+
+
+ 4
+ true
+
+
+ 4
+ 0
+
+
+ 4
+ false
+
+
+ 4
+ true
+
+
+
+
+ 4
+ false
+
+
+ 4
+ 0
+
+
+ 4
+ special://userdata/addon_data/pvr.iptvsimple/genres/genreTextMappings/genres.xml
+
+
+ 4
+
+
+ true
+
+
+
+
+
+ 4
+ 1
+
+
+ 4
+
+
+ true
+
+
+
+ 4
+
+
+ true
+
+
+
+ 4
+ false
+
+
+ 4
+ 1
+
+
+
+
+ 4
+ true
+
+
+ 4
+ true
+
+
+ 4
+ true
+
+
+ 4
+ false
+
+
+ 4
+ true
+
+
+
+
+ 4
+ false
+
+
+ 4
+ true
+
+
+ 4
+ true
+
+
+ 4
+ true
+
+
+ 4
+ false
+
+
+
+
+ 4
+ false
+
+
+ 4
+
+
+ true
+
+
+
+ 4
+ 5
+
+
+ 4
+ 0
+
+
+ 4
+ 0
+
+
+ 4
+ 0
+
+
+ 4
+ false
+
+
+ 4
+ 5
+
+
+ 4
+ 15
+
+
+ 4
+ false
+
+
+
+
+ 4
+ false
+
+
+ 4
+ 127.0.0.1
+
+
+ 4
+ 4022
+
+
+ 4
+ true
+
+
+ 4
+ false
+
+
+ 4
+
+
+ true
+
+
+
+ 4
+
+
+ true
+
+
+
+ 4
+
+
+ true
+
+
+
diff --git a/src/addon.cpp b/src/addon.cpp
index f156728ce..07f10a0c2 100644
--- a/src/addon.cpp
+++ b/src/addon.cpp
@@ -7,6 +7,7 @@
#include "addon.h"
#include "IptvSimple.h"
+#include "iptvsimple/utilities/SettingsMigration.h"
using namespace iptvsimple;
using namespace iptvsimple::data;
@@ -67,6 +68,13 @@ ADDON_STATUS CIptvSimpleAddon::CreateInstance(const kodi::addon::IInstanceInfo&
return ADDON_STATUS_PERMANENT_FAILURE;
}
+ // Try to migrate settings from a pre-multi-instance setup
+ if (SettingsMigration::MigrateSettings(*usedInstance))
+ {
+ // Initial client operated on old/incomplete settings
+ delete usedInstance;
+ usedInstance = new IptvSimple(instance);
+ }
hdl = usedInstance;
// Store this instance also on this class, currently support Kodi only one
diff --git a/src/iptvsimple/AddonSettings.cpp b/src/iptvsimple/AddonSettings.cpp
index 3ee03981f..babdf27e8 100644
--- a/src/iptvsimple/AddonSettings.cpp
+++ b/src/iptvsimple/AddonSettings.cpp
@@ -9,6 +9,7 @@
#include "utilities/FileUtils.h"
#include "utilities/Logger.h"
+#include "utilities/SettingsMigration.h"
#include "kodi/General.h"
@@ -30,5 +31,11 @@ void AddonSettings::ReadSettings()
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;
+ }
+
return ADDON_STATUS_UNKNOWN;
}
diff --git a/src/iptvsimple/utilities/SettingsMigration.cpp b/src/iptvsimple/utilities/SettingsMigration.cpp
new file mode 100644
index 000000000..e9628422c
--- /dev/null
+++ b/src/iptvsimple/utilities/SettingsMigration.cpp
@@ -0,0 +1,189 @@
+/*
+ * 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 iptvsimple;
+using namespace iptvsimple::utilities;
+
+namespace
+{
+// maps
+const std::vector> stringMap = {{"m3uPath", ""},
+ {"m3uUrl", ""},
+ {"defaultProviderName", ""},
+ {"providerMappingFile", "special://userdata/addon_data/pvr.iptvsimple/providers/providerMappings.xml"},
+ {"onetvgroup", ""},
+ {"twotvgroup", ""},
+ {"threetvgroup", ""},
+ {"fourtvgroup", ""},
+ {"fivetvgroup", ""},
+ {"customtvgroupsfile", "special://userdata/addon_data/pvr.iptvsimple/channelGroups/customTVGroups-example.xml"},
+ {"oneradiogroup", ""},
+ {"tworadiogroup", ""},
+ {"threeradiogroup", ""},
+ {"fourradiogroup", ""},
+ {"fiveradiogroup", ""},
+ {"customradiogroupsfile", "special://userdata/addon_data/pvr.iptvsimple/channelGroups/customRadioGroups-example.xml"},
+ {"epgPath", ""},
+ {"epgUrl", ""},
+ {"genresPath", "special://userdata/addon_data/pvr.iptvsimple/genres/genreTextMappings/genres.xml"},
+ {"genresUrl", ""},
+ {"logoPath", ""},
+ {"logoBaseUrl", ""},
+ {"udpxyHost", "127.0.0.1"},
+ {"defaultUserAgent", ""},
+ {"defaultInputstream", ""},
+ {"defaultMimeType", ""}};
+
+const std::vector> intMap = {{"m3uPathType", 0},
+ {"startNum", 1},
+ {"m3uRefreshMode", 0},
+ {"m3uRefreshIntervalMins", 60},
+ {"m3uRefreshHour", 4},
+ {"tvgroupmode", 0},
+ {"numtvgroups", 1},
+ {"radiogroupmode", 0},
+ {"numradiogroups", 1},
+ {"epgPathType", 1},
+ {"genresPathType", 0},
+ {"logoPathType", 1},
+ {"logoFromEpg", 1},
+ {"catchupDays", 5},
+ {"allChannelsCatchupMode", 0},
+ {"catchupWatchEpgBeginBufferMins", 5},
+ {"catchupWatchEpgEndBufferMins", 15},
+ {"udpxyPort", 4022}};
+
+const std::vector> floatMap = {{"epgTimeShift", 0.0},
+ {"catchupCorrection", 0.0}};
+
+const std::vector> boolMap = {{"m3uCache", true},
+ {"numberByOrder", false},
+ {"enableProviderMappings", false},
+ {"tvChannelGroupsOnly", false},
+ {"radioChannelGroupsOnly", false},
+ {"epgCache", true},
+ {"epgTSOverride", false},
+ {"epgIgnoreCaseForChannelIds", true},
+ {"useEpgGenreText", false},
+ {"useLogosLocalPathOnly", false},
+ {"mediaEnabled", true},
+ {"mediaGroupByTitle", true},
+ {"mediaGroupBySeason", true},
+ {"mediaTitleSeasonEpisode", false},
+ {"mediaVODAsRecordings", true},
+ {"timeshiftEnabled", false},
+ {"timeshiftEnabledAll", true},
+ {"timeshiftEnabledHttp", true},
+ {"timeshiftEnabledUdp", true},
+ {"timeshiftEnabledCustom", false},
+ {"catchupEnabled", false},
+ {"catchupPlayEpgAsLive", false},
+ {"catchupOnlyOnFinishedProgrammes", false},
+ {"transformMulticastStreamUrls", false},
+ {"useFFmpegReconnect", true},
+ {"useInputstreamAdaptiveforHls", 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 : floatMap)
+ mig.MigrateFloatSetting(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 = "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(floatMap.cbegin(), floatMap.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::MigrateFloatSetting(const char* key, float defaultValue)
+{
+ float value;
+ if (kodi::addon::CheckSettingFloat(key, value) && value != defaultValue)
+ {
+ m_target.SetInstanceSettingFloat(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/iptvsimple/utilities/SettingsMigration.h b/src/iptvsimple/utilities/SettingsMigration.h
new file mode 100644
index 000000000..aca076593
--- /dev/null
+++ b/src/iptvsimple/utilities/SettingsMigration.h
@@ -0,0 +1,46 @@
+/*
+ * 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 iptvsimple
+{
+namespace utilities
+{
+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 utilities
+} // namespace iptvsimple