aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--addons/resource.language.en_gb/resources/strings.po1
-rw-r--r--cmake/platform/android/android.cmake2
-rw-r--r--docs/README.Android.md4
-rwxr-xr-xsystem/settings/settings.xml18
-rw-r--r--tools/android/packaging/build.gradle2
-rw-r--r--xbmc/addons/binary-addons/AddonInstanceHandler.cpp5
-rw-r--r--xbmc/addons/binary-addons/AddonInstanceHandler.h1
-rw-r--r--xbmc/favourites/GUIWindowFavourites.cpp2
-rw-r--r--xbmc/guilib/listproviders/DirectoryProvider.cpp2
-rw-r--r--xbmc/input/keymaps/keyboard/KeyboardTranslator.cpp6
-rw-r--r--xbmc/music/Song.cpp11
-rw-r--r--xbmc/pvr/PVRDatabase.cpp165
-rw-r--r--xbmc/pvr/PVRDatabase.h12
-rw-r--r--xbmc/pvr/addons/PVRClientUID.cpp58
-rw-r--r--xbmc/pvr/addons/PVRClientUID.h6
-rw-r--r--xbmc/pvr/addons/PVRClients.cpp16
-rw-r--r--xbmc/pvr/windows/GUIWindowPVRRecordings.cpp2
-rw-r--r--xbmc/video/ContextMenus.cpp2
-rw-r--r--xbmc/video/guilib/VideoAction.h2
-rw-r--r--xbmc/video/guilib/VideoSelectActionProcessor.cpp38
-rw-r--r--xbmc/video/guilib/VideoSelectActionProcessor.h3
-rw-r--r--xbmc/video/windows/GUIWindowVideoBase.cpp2
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())