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