diff options
22 files changed, 267 insertions, 93 deletions
diff --git a/addons/resource.language.en_gb/resources/strings.po b/addons/resource.language.en_gb/resources/strings.po index 056b1747a0..c99ae80f5d 100644 --- a/addons/resource.language.en_gb/resources/strings.po +++ b/addons/resource.language.en_gb/resources/strings.po @@ -15461,7 +15461,6 @@ msgstr "" #. Label to denote that there is something 'more' #. xbmc/favourites/ContextMenus.h #: xbmc/guilib/listproviders/DirectoryProvider.cpp -#: xbmc/video/guilib/VideoSelectActionProcessor.cpp #: system/settings/settings.xml msgctxt "#22082" msgid "More..." diff --git a/cmake/platform/android/android.cmake b/cmake/platform/android/android.cmake index 26c7821f89..49105e353d 100644 --- a/cmake/platform/android/android.cmake +++ b/cmake/platform/android/android.cmake @@ -4,7 +4,7 @@ set(APP_RENDER_SYSTEM gles) list(APPEND PLATFORM_OPTIONAL_DEPS LibDovi) # Store SDK compile version -set(TARGET_SDK 33) +set(TARGET_SDK 34) # Minimum supported SDK version set(TARGET_MINSDK 21) diff --git a/docs/README.Android.md b/docs/README.Android.md index 8ce91a31e1..08d04c82a2 100644 --- a/docs/README.Android.md +++ b/docs/README.Android.md @@ -68,7 +68,7 @@ sudo apt install autoconf bison build-essential curl default-jdk flex gawk git g > If you're running a 32bit Debian/Ubuntu distribution, remove `lib32stdc++6 lib32z1 lib32z1-dev` from the command. > [!NOTE] -> Gradle 7.0.2+ requires Jave Runtime 11+. Check java version by running `java --version`. If version is < 11, set JAVA_HOME to java 11+ home directory._ +> Gradle 8.0+ requires JDK 17+. Check java version by running `java --version`. If version is < 17, set `JAVA_HOME` environment variable to java 17+ home directory. **[back to top](#table-of-contents)** @@ -98,7 +98,7 @@ Before Android SDK can be used, you need to accept the licenses and configure it cd $HOME/android-tools/android-sdk-linux/cmdline-tools/bin ./sdkmanager --sdk_root=$(pwd)/../.. --licenses ./sdkmanager --sdk_root=$(pwd)/../.. platform-tools -./sdkmanager --sdk_root=$(pwd)/../.. "platforms;android-33" +./sdkmanager --sdk_root=$(pwd)/../.. "platforms;android-34" ./sdkmanager --sdk_root=$(pwd)/../.. "build-tools;33.0.1" ./sdkmanager --sdk_root=$(pwd)/../.. "ndk;21.4.7075529" ``` diff --git a/system/settings/settings.xml b/system/settings/settings.xml index 5863c36279..791e129c80 100755 --- a/system/settings/settings.xml +++ b/system/settings/settings.xml @@ -1034,14 +1034,14 @@ <group id="1" label="593"> <setting id="myvideos.selectaction" type="integer" label="22079" help="36177"> <level>0</level> - <default>1</default> <!-- SELECT_ACTION_PLAY_OR_RESUME --> + <default>1</default> <!-- ACTION_PLAY_OR_RESUME --> <constraints> <options> - <option label="22080">0</option> <!-- SELECT_ACTION_CHOOSE --> - <option label="208">1</option> <!-- SELECT_ACTION_PLAY_OR_RESUME --> - <option label="13404">2</option> <!-- SELECT_ACTION_RESUME --> - <option label="22081">3</option> <!-- SELECT_ACTION_INFO --> - <option label="13347">7</option> <!-- SELECT_ACTION_QUEUE --> + <option label="22080">0</option> <!-- ACTION_CHOOSE --> + <option label="208">1</option> <!-- ACTION_PLAY_OR_RESUME --> + <option label="13404">2</option> <!-- ACTION_RESUME --> + <option label="22081">3</option> <!-- ACTION_INFO --> + <option label="13347">7</option> <!-- ACTION_QUEUE --> </options> </constraints> <control type="list" format="string" /> @@ -1053,11 +1053,11 @@ </setting> <setting id="myvideos.playaction" type="integer" label="22076" help="36204"> <level>0</level> - <default>1</default> <!-- PLAY_ACTION_PLAY_OR_RESUME --> + <default>1</default> <!-- ACTION_PLAY_OR_RESUME --> <constraints> <options> - <option label="22077">1</option> <!-- PLAY_ACTION_PLAY_OR_RESUME --> - <option label="22078">2</option> <!-- PLAY_ACTION_RESUME --> + <option label="22077">1</option> <!-- ACTION_PLAY_OR_RESUME --> + <option label="22078">2</option> <!-- ACTION_RESUME --> </options> </constraints> <control type="list" format="string" /> diff --git a/tools/android/packaging/build.gradle b/tools/android/packaging/build.gradle index 6c30dfffc8..1c08ce20c8 100644 --- a/tools/android/packaging/build.gradle +++ b/tools/android/packaging/build.gradle @@ -6,7 +6,7 @@ buildscript { google() } dependencies { - classpath 'com.android.tools.build:gradle:7.4.0' + classpath 'com.android.tools.build:gradle:8.1.1' // NOTE: Do not place your application dependencies here; they belong // in the individual module build.gradle files diff --git a/xbmc/addons/binary-addons/AddonInstanceHandler.cpp b/xbmc/addons/binary-addons/AddonInstanceHandler.cpp index 34c303b965..695efd3290 100644 --- a/xbmc/addons/binary-addons/AddonInstanceHandler.cpp +++ b/xbmc/addons/binary-addons/AddonInstanceHandler.cpp @@ -79,6 +79,11 @@ std::string IAddonInstanceHandler::ID() const return m_addon ? m_addon->ID() : ""; } +AddonInstanceId IAddonInstanceHandler::InstanceID() const +{ + return m_instanceId; +} + std::string IAddonInstanceHandler::Name() const { return m_addon ? m_addon->Name() : ""; diff --git a/xbmc/addons/binary-addons/AddonInstanceHandler.h b/xbmc/addons/binary-addons/AddonInstanceHandler.h index 27ba95b2be..d3b40e03d4 100644 --- a/xbmc/addons/binary-addons/AddonInstanceHandler.h +++ b/xbmc/addons/binary-addons/AddonInstanceHandler.h @@ -70,6 +70,7 @@ public: const std::string& UniqueWorkID() { return m_uniqueWorkID; } std::string ID() const; + AddonInstanceId InstanceID() const; std::string Name() const; std::string Author() const; std::string Icon() const; diff --git a/xbmc/favourites/GUIWindowFavourites.cpp b/xbmc/favourites/GUIWindowFavourites.cpp index 1a139aec0a..78fe7af6eb 100644 --- a/xbmc/favourites/GUIWindowFavourites.cpp +++ b/xbmc/favourites/GUIWindowFavourites.cpp @@ -87,7 +87,7 @@ protected: return UTILS::GUILIB::CGUIContentUtils::ShowInfoForItem(*m_item); } - bool OnMoreSelected() override + bool OnChooseSelected() override { CONTEXTMENU::ShowFor(m_item, CContextMenuManager::MAIN); return true; diff --git a/xbmc/guilib/listproviders/DirectoryProvider.cpp b/xbmc/guilib/listproviders/DirectoryProvider.cpp index 8d035004b0..d6b3bc207d 100644 --- a/xbmc/guilib/listproviders/DirectoryProvider.cpp +++ b/xbmc/guilib/listproviders/DirectoryProvider.cpp @@ -511,7 +511,7 @@ protected: return true; } - bool OnMoreSelected() override + bool OnChooseSelected() override { m_provider.OnContextMenu(m_item); return true; diff --git a/xbmc/input/keymaps/keyboard/KeyboardTranslator.cpp b/xbmc/input/keymaps/keyboard/KeyboardTranslator.cpp index 2767022ba8..637e551ab3 100644 --- a/xbmc/input/keymaps/keyboard/KeyboardTranslator.cpp +++ b/xbmc/input/keymaps/keyboard/KeyboardTranslator.cpp @@ -51,10 +51,10 @@ uint32_t CKeyboardTranslator::TranslateButton(const tinyxml2::XMLElement* pButto button_id = TranslateString(szButton); // Process the ctrl/shift/alt modifiers - const char* strMod; - if (pButton->QueryStringAttribute("mod", &strMod) == tinyxml2::XML_SUCCESS) + const char* cstrMod; + if (pButton->QueryStringAttribute("mod", &cstrMod) == tinyxml2::XML_SUCCESS) { - StringUtils::ToLower(strMod); + const std::string strMod = StringUtils::ToLower(cstrMod); std::vector<std::string> modArray = StringUtils::Split(strMod, ","); for (auto substr : modArray) diff --git a/xbmc/music/Song.cpp b/xbmc/music/Song.cpp index 93b2156d7b..4446224602 100644 --- a/xbmc/music/Song.cpp +++ b/xbmc/music/Song.cpp @@ -91,12 +91,13 @@ void CSong::SetArtistCredits(const std::vector<std::string>& names, const std::v //Split the artist sort string to try and get sort names for individual artists std::vector<std::string> artistSort = StringUtils::Split(strArtistSort, CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->m_musicItemSeparator); + // Vector of possible separators in the order least likely to be part of artist name + static const std::vector<std::string> separators{ + " feat. ", " ft. ", " Feat. ", " Ft. ", ";", ":", "|", "#", "/", " with ", "&"}; + if (!mbids.empty()) { // Have musicbrainz artist info, so use it - // Vector of possible separators in the order least likely to be part of artist name - const std::vector<std::string> separators{ " feat. ", " ft. ", " Feat. "," Ft. ", ";", ":", "|", "#", "/", " with ", ",", "&" }; - // Establish tag consistency - do the number of musicbrainz ids and number of names in hints or artist match if (mbids.size() != artistHints.size() && mbids.size() != names.size()) { @@ -147,7 +148,7 @@ void CSong::SetArtistCredits(const std::vector<std::string>& names, const std::v // Try to get number of artist sort names and musicbrainz ids to match. Split sort names // further using multiple possible delimiters, over single separator applied in Tag loader if (artistSort.size() != mbids.size()) - artistSort = StringUtils::SplitMulti(artistSort, { ";", ":", "|", "#" }); + artistSort = StringUtils::SplitMulti(artistSort, separators); for (size_t i = 0; i < mbids.size(); i++) { @@ -185,7 +186,7 @@ void CSong::SetArtistCredits(const std::vector<std::string>& names, const std::v if (artistSort.size() != artists.size()) // Split artist sort names further using multiple possible delimiters, over single separator applied in Tag loader - artistSort = StringUtils::SplitMulti(artistSort, { ";", ":", "|", "#" }); + artistSort = StringUtils::SplitMulti(artistSort, separators); for (size_t i = 0; i < artists.size(); i++) { diff --git a/xbmc/pvr/PVRDatabase.cpp b/xbmc/pvr/PVRDatabase.cpp index a26bd5f636..0dfa051d8b 100644 --- a/xbmc/pvr/PVRDatabase.cpp +++ b/xbmc/pvr/PVRDatabase.cpp @@ -9,8 +9,12 @@ #include "PVRDatabase.h" #include "ServiceBroker.h" +#include "addons/AddonManager.h" +#include "addons/addoninfo/AddonInfo.h" +#include "addons/addoninfo/AddonType.h" #include "dbwrappers/dataset.h" #include "pvr/addons/PVRClient.h" +#include "pvr/addons/PVRClientUID.h" #include "pvr/channels/PVRChannel.h" #include "pvr/channels/PVRChannelGroupMember.h" #include "pvr/channels/PVRChannelGroups.h" @@ -28,6 +32,7 @@ #include <memory> #include <mutex> #include <string> +#include <tuple> #include <utility> #include <vector> @@ -156,7 +161,8 @@ void CPVRDatabase::CreateTables() "bHasArchive bool, " "iClientProviderUid integer, " "bIsUserSetHidden bool, " - "iLastWatchedGroupId integer" + "iLastWatchedGroupId integer, " + "sDateTimeAdded varchar(20)" ")"); CLog::LogFC(LOGDEBUG, LOGPVR, "Creating table 'channelgroups'"); @@ -189,12 +195,12 @@ void CPVRDatabase::CreateTables() ); CLog::LogFC(LOGDEBUG, LOGPVR, "Creating table 'clients'"); - m_pDS->exec( - "CREATE TABLE clients (" - "idClient integer primary key, " - "iPriority integer" - ")" - ); + m_pDS->exec("CREATE TABLE clients (" + "idClient integer primary key, " + "iPriority integer, " + "sAddonID TEXT, " + "iInstanceID integer" + ")"); CLog::LogFC(LOGDEBUG, LOGPVR, "Creating table 'timers'"); m_pDS->exec(sqlCreateTimersTable); @@ -368,6 +374,20 @@ void CPVRDatabase::UpdateTables(int iVersion) m_pDS->exec("ALTER TABLE channels ADD iLastWatchedGroupId integer"); m_pDS->exec("UPDATE channels SET iLastWatchedGroupId = -1"); } + + if (iVersion < 45) + { + m_pDS->exec("ALTER TABLE channels ADD sDateTimeAdded varchar(20)"); + m_pDS->exec("UPDATE channels SET sDateTimeAdded = ''"); + } + + if (iVersion < 46) + { + m_pDS->exec("ALTER TABLE clients ADD sAddonID TEXT"); + m_pDS->exec("ALTER TABLE clients ADD iInstanceID integer"); + + FixupClientIDs(); + } } /********** Client methods **********/ @@ -389,10 +409,10 @@ bool CPVRDatabase::Persist(const CPVRClient& client) std::unique_lock<CCriticalSection> lock(m_critSection); - const std::string strQuery = PrepareSQL("REPLACE INTO clients (idClient, iPriority) VALUES (%i, %i);", - client.GetID(), client.GetPriority()); - - return ExecuteQuery(strQuery); + const std::string sql{PrepareSQL( + "REPLACE INTO clients (idClient, iPriority, sAddonID, iInstanceID) VALUES (%i, %i, '%s', %i)", + client.GetID(), client.GetPriority(), client.ID().c_str(), client.InstanceID())}; + return ExecuteQuery(sql); } bool CPVRDatabase::Delete(const CPVRClient& client) @@ -428,6 +448,113 @@ int CPVRDatabase::GetPriority(const CPVRClient& client) const return atoi(strValue.c_str()); } +void CPVRDatabase::FixupClientIDs() +{ + // Get enabled and disabled PVR client addon infos + std::vector<ADDON::AddonInfoPtr> addonInfos; + CServiceBroker::GetAddonMgr().GetAddonInfos(addonInfos, false, ADDON::AddonType::PVRDLL); + + std::vector<std::tuple<std::string, ADDON::AddonInstanceId, std::string>> clientInfos; + for (const auto& addonInfo : addonInfos) + { + const std::vector<ADDON::AddonInstanceId> instanceIds{addonInfo->GetKnownInstanceIds()}; + for (const auto instanceId : instanceIds) + { + clientInfos.emplace_back(addonInfo->ID(), instanceId, addonInfo->Name()); + } + } + + for (const auto& [addonID, instanceID, addonName] : clientInfos) + { + // Entry with legacy client id present in clients or channels table? + const CPVRClientUID uid{addonID, instanceID}; + int legacyID{uid.GetLegacyUID()}; + std::string sql{PrepareSQL("idClient = %i", legacyID)}; + int id{GetSingleValueInt("clients", "idClient", sql)}; + if (id == legacyID) + { + // Add addon id and instance id to existing clients table entry. + sql = PrepareSQL("UPDATE clients SET sAddonID = '%s', iInstanceID = %i WHERE idClient = %i", + addonID.c_str(), instanceID, legacyID); + ExecuteQuery(sql); + } + else + { + sql = PrepareSQL("iClientId = %i", legacyID); + id = GetSingleValueInt("channels", "iClientId", sql); + if (id == legacyID) + { + // Create a new entry with the legacy client id in clients table. + sql = PrepareSQL("REPLACE INTO clients (idClient, iPriority, sAddonID, iInstanceID) VALUES " + "(%i, %i, '%s', %i)", + legacyID, 0, addonID.c_str(), instanceID); + ExecuteQuery(sql); + } + else + { + // The legacy id was not found in channels table. This happens if the std::hash + // implementation changed (for example after a Kodi update), which according to std::hash + // documentation can happen: "Hash functions are only required to produce the same result + // for the same input within a single execution of a program" + + // We can only fix some of the ids in this case: We can try to find the legacy id via the + // addon's name in the providers table. This is not guaranteed to always work (theoretically + // addon name might have changed) and cannot work if providers table contains more than one + // entry for the same multi-instance addon. + + sql = PrepareSQL("SELECT iClientId FROM providers WHERE iType = 1 AND sName = '%s'", + addonName.c_str()); + + if (ResultQuery(sql)) + { + if (m_pDS->num_rows() != 1) + { + CLog::Log(LOGERROR, "Unable to fixup client id {} for addon '{}', instance {}!", + legacyID, addonID.c_str(), instanceID); + } + else + { + // There is exactly one provider with the addon name in question. + // Its client id is the legacy id we're looking for! + try + { + legacyID = m_pDS->fv("iClientId").get_asInt(); + sql = PrepareSQL( + "REPLACE INTO clients (idClient, iPriority, sAddonID, iInstanceID) VALUES " + "(%i, %i, '%s', %i)", + legacyID, 0, addonID.c_str(), instanceID); + ExecuteQuery(sql); + } + catch (...) + { + CLog::LogF(LOGERROR, "Couldn't obtain providers for addon '{}'.", addonID); + } + } + } + } + } + } +} + +int CPVRDatabase::GetClientID(const std::string& addonID, unsigned int instanceID) +{ + std::unique_lock<CCriticalSection> lock(m_critSection); + + // Client id already present in clients table? + std::string sql{PrepareSQL("sAddonID = '%s' AND iInstanceID = %i", addonID.c_str(), instanceID)}; + const std::string idString{GetSingleValue("clients", "idClient", sql)}; + if (!idString.empty()) + return std::atoi(idString.c_str()); + + // Create a new entry with a new client id in clients table. + sql = PrepareSQL("INSERT INTO clients (iPriority, sAddonID, iInstanceID) VALUES (%i, '%s', %i)", + 0, addonID.c_str(), instanceID); + if (ExecuteQuery(sql)) + return static_cast<int>(m_pDS->lastinsertid()); + + return -1; +} + /********** Channel provider methods **********/ bool CPVRDatabase::DeleteProviders() @@ -1037,15 +1164,17 @@ bool CPVRDatabase::Persist(CPVRChannel& channel, bool bCommit) "INSERT INTO channels (" "iUniqueId, bIsRadio, bIsHidden, bIsUserSetIcon, bIsUserSetName, bIsLocked, " "sIconPath, sChannelName, bIsVirtual, bEPGEnabled, sEPGScraper, iLastWatched, iClientId, " - "idEpg, bHasArchive, iClientProviderUid, bIsUserSetHidden, iLastWatchedGroupId) " - "VALUES (%i, %i, %i, %i, %i, %i, '%s', '%s', %i, %i, '%s', %u, %i, %i, %i, %i, %i, %i)", + "idEpg, bHasArchive, iClientProviderUid, bIsUserSetHidden, iLastWatchedGroupId, " + "sDateTimeAdded) " + "VALUES (%i, %i, %i, %i, %i, %i, '%s', '%s', %i, %i, '%s', %u, %i, %i, %i, %i, %i, %i, " + "'%s')", channel.UniqueID(), (channel.IsRadio() ? 1 : 0), (channel.IsHidden() ? 1 : 0), (channel.IsUserSetIcon() ? 1 : 0), (channel.IsUserSetName() ? 1 : 0), (channel.IsLocked() ? 1 : 0), channel.IconPath().c_str(), channel.ChannelName().c_str(), 0, (channel.EPGEnabled() ? 1 : 0), channel.EPGScraper().c_str(), static_cast<unsigned int>(channel.LastWatched()), channel.ClientID(), channel.EpgID(), channel.HasArchive(), channel.ClientProviderUid(), channel.IsUserSetHidden() ? 1 : 0, - channel.LastWatchedGroupId()); + channel.LastWatchedGroupId(), ""); } else { @@ -1054,8 +1183,10 @@ bool CPVRDatabase::Persist(CPVRChannel& channel, bool bCommit) "REPLACE INTO channels (" "iUniqueId, bIsRadio, bIsHidden, bIsUserSetIcon, bIsUserSetName, bIsLocked, " "sIconPath, sChannelName, bIsVirtual, bEPGEnabled, sEPGScraper, iLastWatched, iClientId, " - "idChannel, idEpg, bHasArchive, iClientProviderUid, bIsUserSetHidden, iLastWatchedGroupId) " - "VALUES (%i, %i, %i, %i, %i, %i, '%s', '%s', %i, %i, '%s', %u, %i, %s, %i, %i, %i, %i, %i)", + "idChannel, idEpg, bHasArchive, iClientProviderUid, bIsUserSetHidden, iLastWatchedGroupId, " + "sDateTimeAdded) " + "VALUES (%i, %i, %i, %i, %i, %i, '%s', '%s', %i, %i, '%s', %u, %i, %s, %i, %i, %i, %i, %i, " + "'%s')", channel.UniqueID(), (channel.IsRadio() ? 1 : 0), (channel.IsHidden() ? 1 : 0), (channel.IsUserSetIcon() ? 1 : 0), (channel.IsUserSetName() ? 1 : 0), (channel.IsLocked() ? 1 : 0), channel.ClientIconPath().c_str(), @@ -1063,7 +1194,7 @@ bool CPVRDatabase::Persist(CPVRChannel& channel, bool bCommit) channel.EPGScraper().c_str(), static_cast<unsigned int>(channel.LastWatched()), channel.ClientID(), strValue.c_str(), channel.EpgID(), channel.HasArchive(), channel.ClientProviderUid(), channel.IsUserSetHidden() ? 1 : 0, - channel.LastWatchedGroupId()); + channel.LastWatchedGroupId(), ""); } if (QueueInsertQuery(strQuery)) diff --git a/xbmc/pvr/PVRDatabase.h b/xbmc/pvr/PVRDatabase.h index 7a8932e95d..69272b5503 100644 --- a/xbmc/pvr/PVRDatabase.h +++ b/xbmc/pvr/PVRDatabase.h @@ -64,7 +64,7 @@ namespace PVR * @brief Get the minimal database version that is required to operate correctly. * @return The minimal database version. */ - int GetSchemaVersion() const override { return 44; } + int GetSchemaVersion() const override { return 46; } /*! * @brief Get the default sqlite database filename. @@ -102,6 +102,14 @@ namespace PVR */ int GetPriority(const CPVRClient& client) const; + /*! + * @brief Get the numeric client ID for given addon ID and instance ID from the database. + * @param addonID The addon ID. + * @param instanceID The instance ID. + * @return The client ID on success, -1 otherwise. + */ + int GetClientID(const std::string& addonID, unsigned int instanceID); + /*! @name Channel methods */ //@{ @@ -327,6 +335,8 @@ namespace PVR bool RemoveChannelsFromGroup(const CPVRChannelGroup& group); + void FixupClientIDs(); + mutable CCriticalSection m_critSection; }; } diff --git a/xbmc/pvr/addons/PVRClientUID.cpp b/xbmc/pvr/addons/PVRClientUID.cpp index 87883851ea..72b5cbed02 100644 --- a/xbmc/pvr/addons/PVRClientUID.cpp +++ b/xbmc/pvr/addons/PVRClientUID.cpp @@ -8,26 +8,68 @@ #include "PVRClientUID.h" +#include "pvr/PVRDatabase.h" +#include "utils/log.h" + #include <functional> +#include <map> +#include <utility> using namespace PVR; +namespace +{ +using ClientUIDParts = std::pair<std::string, ADDON::AddonInstanceId>; +static std::map<ClientUIDParts, int> s_idMap; +} // unnamed namespace + int CPVRClientUID::GetUID() const { if (!m_uidCreated) { - std::hash<std::string> hasher; + const auto it = s_idMap.find({m_addonID, m_instanceID}); + if (it != s_idMap.cend()) + { + // Cache hit + m_uid = (*it).second; + } + else + { + // Cache miss. Read from db and cache. + CPVRDatabase db; + if (!db.Open()) + { + CLog::LogF(LOGERROR, "Unable to open TV database!"); + return -1; + } - // Note: For database backwards compatibility reasons the hash of the first instance - // must be calculated just from the addonId, not from addonId and instanceId. - m_uid = static_cast<int>(hasher( - (m_instanceID > ADDON::ADDON_FIRST_INSTANCE_ID ? std::to_string(m_instanceID) + "@" : "") + - m_addonID)); - if (m_uid < 0) - m_uid = -m_uid; + m_uid = db.GetClientID(m_addonID, m_instanceID); + if (m_uid == -1) + { + CLog::LogF(LOGERROR, "Unable to get client id from TV database!"); + return -1; + } + s_idMap.insert({{m_addonID, m_instanceID}, m_uid}); + } m_uidCreated = true; } return m_uid; } + +int CPVRClientUID::GetLegacyUID() const +{ + // Note: For database backwards compatibility reasons the hash of the first instance + // must be calculated just from the addonId, not from addonId and instanceId. + std::string prefix; + if (m_instanceID > ADDON::ADDON_FIRST_INSTANCE_ID) + prefix = std::to_string(m_instanceID) + "@"; + + std::hash<std::string> hasher; + int uid{static_cast<int>(hasher(prefix + m_addonID))}; + if (uid < 0) + uid = -uid; + + return uid; +} diff --git a/xbmc/pvr/addons/PVRClientUID.h b/xbmc/pvr/addons/PVRClientUID.h index 5b5d1c0507..d3acec2dcf 100644 --- a/xbmc/pvr/addons/PVRClientUID.h +++ b/xbmc/pvr/addons/PVRClientUID.h @@ -30,6 +30,12 @@ public: */ int GetUID() const; + /*! + * @brief Return the numeric legacy UID (compatibility/migration purposes only). + * @return The numeric legacy UID. + */ + int GetLegacyUID() const; + private: CPVRClientUID() = delete; diff --git a/xbmc/pvr/addons/PVRClients.cpp b/xbmc/pvr/addons/PVRClients.cpp index b2261d376d..8adf8ba934 100644 --- a/xbmc/pvr/addons/PVRClients.cpp +++ b/xbmc/pvr/addons/PVRClients.cpp @@ -126,9 +126,17 @@ void CPVRClients::UpdateClients( instanceEnabled = client->IsEnabled(); if (instanceEnabled) + { + CLog::LogF(LOGINFO, "Creating PVR client: addonId={}, instanceId={}, clientId={}", + addon->ID(), instanceId, clientId); clientsToCreate.emplace_back(client); + } else if (isKnownClient) + { + CLog::LogF(LOGINFO, "Destroying PVR client: addonId={}, instanceId={}, clientId={}", + addon->ID(), instanceId, clientId); clientsToDestroy.emplace_back(clientId); + } } else if (IsCreatedClient(clientId)) { @@ -140,9 +148,17 @@ void CPVRClients::UpdateClients( } if (instanceEnabled) + { + CLog::LogF(LOGINFO, "Recreating PVR client: addonId={}, instanceId={}, clientId={}", + addon->ID(), instanceId, clientId); clientsToReCreate.emplace_back(clientId, addon->Name()); + } else + { + CLog::LogF(LOGINFO, "Destroying PVR client: addonId={}, instanceId={}, clientId={}", + addon->ID(), instanceId, clientId); clientsToDestroy.emplace_back(clientId); + } } } } diff --git a/xbmc/pvr/windows/GUIWindowPVRRecordings.cpp b/xbmc/pvr/windows/GUIWindowPVRRecordings.cpp index 3f545098de..12f5537063 100644 --- a/xbmc/pvr/windows/GUIWindowPVRRecordings.cpp +++ b/xbmc/pvr/windows/GUIWindowPVRRecordings.cpp @@ -267,7 +267,7 @@ protected: return true; } - bool OnMoreSelected() override + bool OnChooseSelected() override { m_window.OnPopupMenu(m_itemIndex); return true; diff --git a/xbmc/video/ContextMenus.cpp b/xbmc/video/ContextMenus.cpp index fa7f454136..4ebfb2b5bf 100644 --- a/xbmc/video/ContextMenus.cpp +++ b/xbmc/video/ContextMenus.cpp @@ -222,7 +222,7 @@ protected: return true; } - bool OnMoreSelected() override + bool OnChooseSelected() override { CONTEXTMENU::ShowFor(m_item, CContextMenuManager::MAIN); return true; diff --git a/xbmc/video/guilib/VideoAction.h b/xbmc/video/guilib/VideoAction.h index d2efd86d3c..f914699aa5 100644 --- a/xbmc/video/guilib/VideoAction.h +++ b/xbmc/video/guilib/VideoAction.h @@ -20,7 +20,7 @@ enum Action ACTION_PLAY_OR_RESUME = 1, // if resume is possible, ask user. play from beginning otherwise ACTION_RESUME = 2, // resume if possibly, play from beginning otherwise ACTION_INFO = 3, - ACTION_MORE = 4, + // 4 unused ACTION_PLAY_FROM_BEGINNING = 5, // play from beginning, also if resume would be possible ACTION_PLAYPART = 6, ACTION_QUEUE = 7, diff --git a/xbmc/video/guilib/VideoSelectActionProcessor.cpp b/xbmc/video/guilib/VideoSelectActionProcessor.cpp index 507f59a1fd..90797e9742 100644 --- a/xbmc/video/guilib/VideoSelectActionProcessor.cpp +++ b/xbmc/video/guilib/VideoSelectActionProcessor.cpp @@ -10,7 +10,6 @@ #include "FileItem.h" #include "ServiceBroker.h" -#include "dialogs/GUIDialogContextMenu.h" #include "dialogs/GUIDialogSelect.h" #include "filesystem/Directory.h" #include "guilib/GUIComponent.h" @@ -20,7 +19,6 @@ #include "settings/SettingsComponent.h" #include "utils/StringUtils.h" #include "utils/Variant.h" -#include "video/VideoInfoTag.h" #include "video/VideoUtils.h" using namespace VIDEO::GUILIB; @@ -44,16 +42,7 @@ bool CVideoSelectActionProcessorBase::Process(Action action) switch (action) { case ACTION_CHOOSE: - { - const Action selectedAction = ChooseVideoItemSelectAction(); - if (selectedAction < 0) - { - m_userCancelled = true; - return true; // User cancelled the select menu. We're done. - } - - return Process(selectedAction); - } + return OnChooseSelected(); case ACTION_PLAYPART: { @@ -79,9 +68,6 @@ bool CVideoSelectActionProcessorBase::Process(Action action) return OnInfoSelected(); } - case ACTION_MORE: - return OnMoreSelected(); - default: break; } @@ -110,25 +96,3 @@ unsigned int CVideoSelectActionProcessorBase::ChooseStackItemPartNumber() const return dialog->GetSelectedItem() + 1; // part numbers are 1-based } - -Action CVideoSelectActionProcessorBase::ChooseVideoItemSelectAction() const -{ - CContextButtons choices; - - const std::string resumeString = VIDEO_UTILS::GetResumeString(*m_item); - if (!resumeString.empty()) - { - choices.Add(ACTION_RESUME, resumeString); - choices.Add(ACTION_PLAY_FROM_BEGINNING, 12021); // Play from beginning - } - else - { - choices.Add(ACTION_PLAY_FROM_BEGINNING, 208); // Play - } - - choices.Add(ACTION_INFO, 22081); // Show information - choices.Add(ACTION_QUEUE, 13347); // Queue item - choices.Add(ACTION_MORE, 22082); // More - - return static_cast<Action>(CGUIDialogContextMenu::ShowAndGetChoice(choices)); -} diff --git a/xbmc/video/guilib/VideoSelectActionProcessor.h b/xbmc/video/guilib/VideoSelectActionProcessor.h index 8a4e6ee80e..49287ca64c 100644 --- a/xbmc/video/guilib/VideoSelectActionProcessor.h +++ b/xbmc/video/guilib/VideoSelectActionProcessor.h @@ -37,11 +37,10 @@ protected: virtual bool OnPlayPartSelected(unsigned int part) = 0; virtual bool OnQueueSelected() = 0; virtual bool OnInfoSelected() = 0; - virtual bool OnMoreSelected() = 0; + virtual bool OnChooseSelected() = 0; private: CVideoSelectActionProcessorBase() = delete; - Action ChooseVideoItemSelectAction() const; unsigned int ChooseStackItemPartNumber() const; }; } // namespace GUILIB diff --git a/xbmc/video/windows/GUIWindowVideoBase.cpp b/xbmc/video/windows/GUIWindowVideoBase.cpp index e8ba199e19..702a056a08 100644 --- a/xbmc/video/windows/GUIWindowVideoBase.cpp +++ b/xbmc/video/windows/GUIWindowVideoBase.cpp @@ -600,7 +600,7 @@ protected: bool OnInfoSelected() override { return m_window.OnItemInfo(*m_item); } - bool OnMoreSelected() override + bool OnChooseSelected() override { // window only shows the default version, so no window specific context menu items available if (m_item->HasVideoVersions() && !m_item->GetVideoInfoTag()->IsDefaultVideoVersion()) |