2 * Copyright (C) 2012-2018 Team Kodi
3 * This file is part of Kodi - https://kodi.tv
5 * SPDX-License-Identifier: GPL-2.0-or-later
6 * See LICENSES/README.md for more information.
9 #include "PVRDatabase.h"
11 #include "ServiceBroker.h"
12 #include "dbwrappers/dataset.h"
13 #include "pvr/addons/PVRClient.h"
14 #include "pvr/channels/PVRChannel.h"
15 #include "pvr/channels/PVRChannelGroupMember.h"
16 #include "pvr/channels/PVRChannelGroups.h"
17 #include "pvr/providers/PVRProvider.h"
18 #include "pvr/providers/PVRProviders.h"
19 #include "pvr/timers/PVRTimerInfoTag.h"
20 #include "pvr/timers/PVRTimerType.h"
21 #include "settings/AdvancedSettings.h"
22 #include "settings/SettingsComponent.h"
23 #include "utils/StringUtils.h"
24 #include "utils/log.h"
34 using namespace dbiplus
;
41 static const std::string sqlCreateTimersTable
=
42 "CREATE TABLE timers ("
43 "iClientIndex integer primary key, "
44 "iParentClientIndex integer, "
46 "iTimerType integer, "
48 "sTitle varchar(255), "
49 "iClientChannelUid integer, "
50 "sSeriesLink varchar(255), "
51 "sStartTime varchar(20), "
52 "bStartAnyTime bool, "
53 "sEndTime varchar(20), "
55 "sFirstDay varchar(20), "
58 "iMarginStart integer, "
59 "iMarginEnd integer, "
60 "sEpgSearchString varchar(255), "
61 "bFullTextEpgSearch bool, "
62 "iPreventDuplicates integer,"
65 "iMaxRecordings integer,"
66 "iRecordingGroup integer"
69 static const std::string sqlCreateProvidersTable
=
70 "CREATE TABLE providers ("
71 "idProvider integer primary key, "
76 "sIconPath varchar(255), "
77 "sCountries varchar(64), "
78 "sLanguages varchar(64) "
83 std::string
GetClientIdsSQL(const std::vector
<std::shared_ptr
<CPVRClient
>>& clients
,
86 if (clients
.empty() && !migrate
)
89 std::string clientIds
= "(";
90 for (auto it
= clients
.cbegin(); it
!= clients
.cend(); ++it
)
92 if (it
!= clients
.cbegin())
95 clientIds
+= "iClientId = ";
96 clientIds
+= std::to_string((*it
)->GetID());
100 if (!clients
.empty())
103 clientIds
+= "iClientId = -2"; // PVR_GROUP_CLIENT_ID_UNKNOWN
109 } // unnamed namespace
111 bool CPVRDatabase::Open()
113 std::unique_lock
<CCriticalSection
> lock(m_critSection
);
114 return CDatabase::Open(CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->m_databaseTV
);
117 void CPVRDatabase::Close()
119 std::unique_lock
<CCriticalSection
> lock(m_critSection
);
123 void CPVRDatabase::Lock()
125 m_critSection
.lock();
128 void CPVRDatabase::Unlock()
130 m_critSection
.unlock();
133 void CPVRDatabase::CreateTables()
135 std::unique_lock
<CCriticalSection
> lock(m_critSection
);
137 CLog::LogF(LOGINFO
, "Creating PVR database tables");
139 CLog::LogFC(LOGDEBUG
, LOGPVR
, "Creating table 'channels'");
140 m_pDS
->exec("CREATE TABLE channels ("
141 "idChannel integer primary key, "
142 "iUniqueId integer, "
145 "bIsUserSetIcon bool, "
146 "bIsUserSetName bool, "
148 "sIconPath varchar(255), "
149 "sChannelName varchar(64), "
152 "sEPGScraper varchar(32), "
153 "iLastWatched integer, "
154 "iClientId integer, " //! @todo use mapping table
157 "iClientProviderUid integer, "
158 "bIsUserSetHidden bool, "
159 "iLastWatchedGroupId integer"
162 CLog::LogFC(LOGDEBUG
, LOGPVR
, "Creating table 'channelgroups'");
163 m_pDS
->exec("CREATE TABLE channelgroups ("
164 "idGroup integer primary key, "
166 "iGroupType integer, "
167 "sName varchar(64), "
168 "iLastWatched integer, "
170 "iPosition integer, "
171 "iLastOpened bigint unsigned, "
172 "iClientId integer, "
173 "bIsUserSetName bool, "
174 "sClientName varchar(64), "
175 "iClientPosition integer"
178 CLog::LogFC(LOGDEBUG
, LOGPVR
, "Creating table 'map_channelgroups_channels'");
180 "CREATE TABLE map_channelgroups_channels ("
181 "idChannel integer, "
183 "iChannelNumber integer, "
184 "iSubChannelNumber integer, "
186 "iClientChannelNumber integer, "
187 "iClientSubChannelNumber integer"
191 CLog::LogFC(LOGDEBUG
, LOGPVR
, "Creating table 'clients'");
193 "CREATE TABLE clients ("
194 "idClient integer primary key, "
199 CLog::LogFC(LOGDEBUG
, LOGPVR
, "Creating table 'timers'");
200 m_pDS
->exec(sqlCreateTimersTable
);
202 CLog::LogFC(LOGDEBUG
, LOGPVR
, "Creating table 'providers'");
203 m_pDS
->exec(sqlCreateProvidersTable
);
206 void CPVRDatabase::CreateAnalytics()
208 std::unique_lock
<CCriticalSection
> lock(m_critSection
);
210 CLog::LogF(LOGINFO
, "Creating PVR database indices");
211 m_pDS
->exec("CREATE INDEX idx_clients_idClient on clients(idClient);");
212 m_pDS
->exec("CREATE UNIQUE INDEX idx_channels_iClientId_iUniqueId on channels(iClientId, iUniqueId);");
213 m_pDS
->exec("CREATE INDEX idx_channelgroups_bIsRadio on channelgroups(bIsRadio);");
214 m_pDS
->exec("CREATE UNIQUE INDEX idx_idGroup_idChannel on map_channelgroups_channels(idGroup, idChannel);");
215 m_pDS
->exec("CREATE INDEX idx_timers_iClientIndex on timers(iClientIndex);");
218 void CPVRDatabase::UpdateTables(int iVersion
)
220 std::unique_lock
<CCriticalSection
> lock(m_critSection
);
223 m_pDS
->exec("ALTER TABLE channels ADD idEpg integer;");
226 m_pDS
->exec("ALTER TABLE channels ADD bIsUserSetIcon bool");
229 m_pDS
->exec("ALTER TABLE channelgroups ADD iGroupType integer");
232 m_pDS
->exec("ALTER TABLE channels ADD bIsLocked bool");
235 m_pDS
->exec("ALTER TABLE channelgroups ADD iLastWatched integer");
238 m_pDS
->exec("ALTER TABLE channels ADD bIsUserSetName bool");
241 m_pDS
->exec("DROP TABLE IF EXISTS channelsettings");
245 m_pDS
->exec("ALTER TABLE channels ADD iClientSubChannelNumber integer");
246 m_pDS
->exec("UPDATE channels SET iClientSubChannelNumber = 0");
247 m_pDS
->exec("ALTER TABLE map_channelgroups_channels ADD iSubChannelNumber integer");
248 m_pDS
->exec("UPDATE map_channelgroups_channels SET iSubChannelNumber = 0");
252 m_pDS
->exec("ALTER TABLE channelgroups ADD bIsHidden bool");
255 m_pDS
->exec("DROP TABLE clients");
258 m_pDS
->exec("ALTER TABLE channelgroups ADD iPosition integer");
261 m_pDS
->exec("CREATE TABLE clients (idClient integer primary key, iPriority integer)");
264 m_pDS
->exec(sqlCreateTimersTable
);
267 m_pDS
->exec("ALTER TABLE channels ADD bHasArchive bool");
271 m_pDS
->exec("ALTER TABLE map_channelgroups_channels ADD iOrder integer");
272 m_pDS
->exec("UPDATE map_channelgroups_channels SET iOrder = 0");
277 m_pDS
->exec("ALTER TABLE map_channelgroups_channels ADD iClientChannelNumber integer");
278 m_pDS
->exec("UPDATE map_channelgroups_channels SET iClientChannelNumber = 0");
279 m_pDS
->exec("ALTER TABLE map_channelgroups_channels ADD iClientSubChannelNumber integer");
280 m_pDS
->exec("UPDATE map_channelgroups_channels SET iClientSubChannelNumber = 0");
284 m_pDS
->exec("ALTER TABLE channelgroups ADD iLastOpened integer");
288 m_pDS
->exec("ALTER TABLE channelgroups "
289 "RENAME TO channelgroups_old");
291 m_pDS
->exec("CREATE TABLE channelgroups ("
292 "idGroup integer primary key, "
294 "iGroupType integer, "
295 "sName varchar(64), "
296 "iLastWatched integer, "
298 "iPosition integer, "
299 "iLastOpened bigint unsigned"
303 "INSERT INTO channelgroups (bIsRadio, iGroupType, sName, iLastWatched, bIsHidden, "
304 "iPosition, iLastOpened) "
305 "SELECT bIsRadio, iGroupType, sName, iLastWatched, bIsHidden, iPosition, iLastOpened "
306 "FROM channelgroups_old");
308 m_pDS
->exec("DROP TABLE channelgroups_old");
313 m_pDS
->exec(sqlCreateProvidersTable
);
314 m_pDS
->exec("CREATE UNIQUE INDEX idx_iUniqueId_iClientId on providers(iUniqueId, iClientId);");
315 m_pDS
->exec("ALTER TABLE channels ADD iClientProviderUid integer");
316 m_pDS
->exec("UPDATE channels SET iClientProviderUid = -1");
321 m_pDS
->exec("ALTER TABLE channels ADD bIsUserSetHidden bool");
322 m_pDS
->exec("UPDATE channels SET bIsUserSetHidden = bIsHidden");
329 // cleanup orphaned entries that might have piled up over time...
330 m_pDS
->exec("DELETE FROM map_channelgroups_channels WHERE idChannel NOT IN (SELECT "
331 "idChannel FROM channels)");
332 m_pDS
->exec("DELETE FROM channelgroups WHERE idGroup NOT IN (SELECT idGroup FROM "
333 "map_channelgroups_channels)");
337 CLog::LogFC(LOGERROR
, LOGPVR
, "Exception in database cleanup");
340 m_pDS
->exec("ALTER TABLE channelgroups ADD iClientId integer");
341 // set "PVR_GROUP_CLIENT_ID_UNKNOWN for migration of backend provided groups
342 m_pDS
->exec("UPDATE channelgroups SET iClientId = -2 WHERE iGroupType = 0");
343 // set PVR_GROUP_CLIENT_ID_LOCAL for local groups
344 m_pDS
->exec("UPDATE channelgroups SET iClientId = -1 WHERE iGroupType != 0");
349 m_pDS
->exec("ALTER TABLE channelgroups ADD bIsUserSetName bool");
350 m_pDS
->exec("UPDATE channelgroups SET bIsUserSetName = 0");
352 m_pDS
->exec("ALTER TABLE channelgroups ADD sClientName varchar(64)");
353 m_pDS
->exec("UPDATE channelgroups SET sClientName = sName WHERE iGroupType = 0");
354 m_pDS
->exec("UPDATE channelgroups SET sClientName = '' WHERE iGroupType != 0");
359 m_pDS
->exec("ALTER TABLE channelgroups ADD iClientPosition integer");
360 m_pDS
->exec("UPDATE channelgroups SET iClientPosition = iPosition");
361 // Setup initial local order, not perfect but at least unique across all groups.
362 // Should mostly be the order the groups appeared from backend or were created by user locally.
363 m_pDS
->exec("UPDATE channelgroups SET iPosition = idGroup");
368 m_pDS
->exec("ALTER TABLE channels ADD iLastWatchedGroupId integer");
369 m_pDS
->exec("UPDATE channels SET iLastWatchedGroupId = -1");
373 /********** Client methods **********/
375 bool CPVRDatabase::DeleteClients()
377 CLog::LogFC(LOGDEBUG
, LOGPVR
, "Deleting all clients from the database");
379 std::unique_lock
<CCriticalSection
> lock(m_critSection
);
380 return DeleteValues("clients");
383 bool CPVRDatabase::Persist(const CPVRClient
& client
)
385 if (client
.GetID() == PVR_INVALID_CLIENT_ID
)
388 CLog::LogFC(LOGDEBUG
, LOGPVR
, "Persisting client {} to database", client
.GetID());
390 std::unique_lock
<CCriticalSection
> lock(m_critSection
);
392 const std::string strQuery
= PrepareSQL("REPLACE INTO clients (idClient, iPriority) VALUES (%i, %i);",
393 client
.GetID(), client
.GetPriority());
395 return ExecuteQuery(strQuery
);
398 bool CPVRDatabase::Delete(const CPVRClient
& client
)
400 if (client
.GetID() == PVR_INVALID_CLIENT_ID
)
403 CLog::LogFC(LOGDEBUG
, LOGPVR
, "Deleting client {} from the database", client
.GetID());
405 std::unique_lock
<CCriticalSection
> lock(m_critSection
);
408 filter
.AppendWhere(PrepareSQL("idClient = '%i'", client
.GetID()));
410 return DeleteValues("clients", filter
);
413 int CPVRDatabase::GetPriority(const CPVRClient
& client
) const
415 if (client
.GetID() == PVR_INVALID_CLIENT_ID
)
418 CLog::LogFC(LOGDEBUG
, LOGPVR
, "Getting priority for client {} from the database", client
.GetID());
420 std::unique_lock
<CCriticalSection
> lock(m_critSection
);
422 const std::string strWhereClause
= PrepareSQL("idClient = '%i'", client
.GetID());
423 const std::string strValue
= GetSingleValue("clients", "iPriority", strWhereClause
);
425 if (strValue
.empty())
428 return atoi(strValue
.c_str());
431 /********** Channel provider methods **********/
433 bool CPVRDatabase::DeleteProviders()
435 CLog::LogFC(LOGDEBUG
, LOGPVR
, "Deleting all providers from the database");
437 std::unique_lock
<CCriticalSection
> lock(m_critSection
);
438 return DeleteValues("providers");
441 bool CPVRDatabase::Persist(CPVRProvider
& provider
, bool updateRecord
/* = false */)
443 bool bReturn
= false;
444 if (provider
.GetName().empty())
446 CLog::LogF(LOGERROR
, "Empty provider name");
450 std::string strQuery
;
452 std::unique_lock
<CCriticalSection
> lock(m_critSection
);
454 /* insert a new entry when this is a new group, or replace the existing one otherwise */
457 PrepareSQL("INSERT INTO providers (idProvider, iUniqueId, iClientId, sName, "
458 "iType, sIconPath, sCountries, sLanguages) "
459 "VALUES (%i, %i, %i, '%s', %i, '%s', '%s', '%s');",
460 provider
.GetDatabaseId(), provider
.GetUniqueId(), provider
.GetClientId(),
461 provider
.GetName().c_str(), static_cast<int>(provider
.GetType()),
462 provider
.GetClientIconPath().c_str(), provider
.GetCountriesDBString().c_str(),
463 provider
.GetLanguagesDBString().c_str());
466 PrepareSQL("REPLACE INTO providers (idProvider, iUniqueId, iClientId, sName, "
467 "iType, sIconPath, sCountries, sLanguages) "
468 "VALUES (%i, %i, %i, '%s', %i, '%s', '%s', '%s');",
469 provider
.GetDatabaseId(), provider
.GetUniqueId(), provider
.GetClientId(),
470 provider
.GetName().c_str(), static_cast<int>(provider
.GetType()),
471 provider
.GetClientIconPath().c_str(), provider
.GetCountriesDBString().c_str(),
472 provider
.GetLanguagesDBString().c_str());
474 bReturn
= ExecuteQuery(strQuery
);
476 /* set the provider id if it was <= 0 */
477 if (bReturn
&& provider
.GetDatabaseId() <= 0)
479 provider
.SetDatabaseId(static_cast<int>(m_pDS
->lastinsertid()));
486 bool CPVRDatabase::Delete(const CPVRProvider
& provider
)
488 CLog::LogFC(LOGDEBUG
, LOGPVR
, "Deleting provider '{}' from the database",
491 std::unique_lock
<CCriticalSection
> lock(m_critSection
);
494 filter
.AppendWhere(PrepareSQL("idProvider = '%i'", provider
.GetDatabaseId()));
496 return DeleteValues("providers", filter
);
499 bool CPVRDatabase::Get(CPVRProviders
& results
,
500 const std::vector
<std::shared_ptr
<CPVRClient
>>& clients
) const
502 bool bReturn
= false;
504 std::string strQuery
= "SELECT * from providers ";
505 const std::string clientIds
= GetClientIdsSQL(clients
);
506 if (!clientIds
.empty())
507 strQuery
+= "WHERE " + clientIds
+ " OR iType = 1"; // always load addon providers
509 std::unique_lock
<CCriticalSection
> lock(m_critSection
);
510 strQuery
= PrepareSQL(strQuery
);
511 if (ResultQuery(strQuery
))
515 while (!m_pDS
->eof())
517 std::shared_ptr
<CPVRProvider
> provider
= std::make_shared
<CPVRProvider
>(
518 m_pDS
->fv("iUniqueId").get_asInt(), m_pDS
->fv("iClientId").get_asInt());
520 provider
->SetDatabaseId(m_pDS
->fv("idProvider").get_asInt());
521 provider
->SetName(m_pDS
->fv("sName").get_asString());
523 static_cast<PVR_PROVIDER_TYPE
>(m_pDS
->fv("iType").get_asInt()));
524 provider
->SetIconPath(m_pDS
->fv("sIconPath").get_asString());
525 provider
->SetCountriesFromDBString(m_pDS
->fv("sCountries").get_asString());
526 provider
->SetLanguagesFromDBString(m_pDS
->fv("sLanguages").get_asString());
528 results
.CheckAndAddEntry(provider
, ProviderUpdateMode::BY_DATABASE
);
530 CLog::LogFC(LOGDEBUG
, LOGPVR
, "Channel Provider '{}' loaded from PVR database",
531 provider
->GetName());
540 CLog::LogF(LOGERROR
, "Couldn't load providers from PVR database");
547 int CPVRDatabase::GetMaxProviderId() const
549 std::string strQuery
= PrepareSQL("SELECT max(idProvider) as maxProviderId from providers");
550 std::unique_lock
<CCriticalSection
> lock(m_critSection
);
552 return GetSingleValueInt(strQuery
);
555 /********** Channel methods **********/
557 int CPVRDatabase::Get(bool bRadio
,
558 const std::vector
<std::shared_ptr
<CPVRClient
>>& clients
,
559 std::map
<std::pair
<int, int>, std::shared_ptr
<CPVRChannel
>>& results
) const
563 std::string strQuery
= "SELECT * from channels WHERE bIsRadio = %u ";
564 const std::string clientIds
= GetClientIdsSQL(clients
);
565 if (!clientIds
.empty())
566 strQuery
+= "AND " + clientIds
;
568 std::unique_lock
<CCriticalSection
> lock(m_critSection
);
569 strQuery
= PrepareSQL(strQuery
, bRadio
);
570 if (ResultQuery(strQuery
))
574 while (!m_pDS
->eof())
576 const std::shared_ptr
<CPVRChannel
> channel(new CPVRChannel(
577 m_pDS
->fv("bIsRadio").get_asBool(), m_pDS
->fv("sIconPath").get_asString()));
579 channel
->m_iChannelId
= m_pDS
->fv("idChannel").get_asInt();
580 channel
->m_iUniqueId
= m_pDS
->fv("iUniqueId").get_asInt();
581 channel
->m_bIsHidden
= m_pDS
->fv("bIsHidden").get_asBool();
582 channel
->m_bIsUserSetIcon
= m_pDS
->fv("bIsUserSetIcon").get_asBool();
583 channel
->m_bIsUserSetName
= m_pDS
->fv("bIsUserSetName").get_asBool();
584 channel
->m_bIsLocked
= m_pDS
->fv("bIsLocked").get_asBool();
585 channel
->m_strChannelName
= m_pDS
->fv("sChannelName").get_asString();
586 channel
->m_bEPGEnabled
= m_pDS
->fv("bEPGEnabled").get_asBool();
587 channel
->m_strEPGScraper
= m_pDS
->fv("sEPGScraper").get_asString();
588 channel
->m_iLastWatched
= static_cast<time_t>(m_pDS
->fv("iLastWatched").get_asInt());
589 channel
->m_iClientId
= m_pDS
->fv("iClientId").get_asInt();
590 channel
->m_iEpgId
= m_pDS
->fv("idEpg").get_asInt();
591 channel
->m_bHasArchive
= m_pDS
->fv("bHasArchive").get_asBool();
592 channel
->m_iClientProviderUid
= m_pDS
->fv("iClientProviderUid").get_asInt();
593 channel
->m_bIsUserSetHidden
= m_pDS
->fv("bIsUserSetHidden").get_asBool();
594 channel
->m_lastWatchedGroupId
= m_pDS
->fv("iLastWatchedGroupId").get_asInt();
596 channel
->UpdateEncryptionName();
598 results
.insert({channel
->StorageId(), channel
});
607 CLog::LogF(LOGERROR
, "Couldn't load channels from PVR database");
612 CLog::LogF(LOGERROR
, "PVR database query failed");
619 bool CPVRDatabase::DeleteChannels()
621 CLog::LogFC(LOGDEBUG
, LOGPVR
, "Deleting all channels from the database");
623 std::unique_lock
<CCriticalSection
> lock(m_critSection
);
624 return DeleteValues("channels");
627 bool CPVRDatabase::QueueDeleteQuery(const CPVRChannel
& channel
)
629 /* invalid channel */
630 if (channel
.ChannelID() <= 0)
632 CLog::LogF(LOGERROR
, "Invalid channel id: {}", channel
.ChannelID());
636 CLog::LogFC(LOGDEBUG
, LOGPVR
, "Queueing delete for channel '{}' from the database",
637 channel
.ChannelName());
640 filter
.AppendWhere(PrepareSQL("idChannel = %i", channel
.ChannelID()));
642 std::string strQuery
;
643 if (BuildSQL(PrepareSQL("DELETE FROM %s ", "channels"), filter
, strQuery
))
644 return CDatabase::QueueDeleteQuery(strQuery
);
649 /********** Channel group member methods **********/
651 bool CPVRDatabase::QueueDeleteQuery(const CPVRChannelGroupMember
& groupMember
)
653 CLog::LogFC(LOGDEBUG
, LOGPVR
, "Queueing delete for channel group member '{}' from the database",
654 groupMember
.Channel() ? groupMember
.Channel()->ChannelName()
655 : std::to_string(groupMember
.ChannelDatabaseID()));
658 filter
.AppendWhere(PrepareSQL("idGroup = %i", groupMember
.GroupID()));
659 filter
.AppendWhere(PrepareSQL("idChannel = %i", groupMember
.ChannelDatabaseID()));
661 std::string strQuery
;
662 if (BuildSQL(PrepareSQL("DELETE FROM %s ", "map_channelgroups_channels"), filter
, strQuery
))
663 return CDatabase::QueueDeleteQuery(strQuery
);
668 /********** Channel group methods **********/
670 bool CPVRDatabase::RemoveChannelsFromGroup(const CPVRChannelGroup
& group
)
673 filter
.AppendWhere(PrepareSQL("idGroup = %i", group
.GroupID()));
675 std::unique_lock
<CCriticalSection
> lock(m_critSection
);
676 return DeleteValues("map_channelgroups_channels", filter
);
679 bool CPVRDatabase::DeleteChannelGroups()
681 CLog::LogFC(LOGDEBUG
, LOGPVR
, "Deleting all channel groups from the database");
683 std::unique_lock
<CCriticalSection
> lock(m_critSection
);
684 return DeleteValues("channelgroups") && DeleteValues("map_channelgroups_channels");
687 bool CPVRDatabase::Delete(const CPVRChannelGroup
& group
)
689 /* invalid group id */
690 if (group
.GroupID() <= 0)
692 CLog::LogF(LOGERROR
, "Invalid channel group id: {}", group
.GroupID());
696 std::unique_lock
<CCriticalSection
> lock(m_critSection
);
699 filter
.AppendWhere(PrepareSQL("idGroup = %i", group
.GroupID()));
700 filter
.AppendWhere(PrepareSQL("bIsRadio = %u", group
.IsRadio()));
702 return RemoveChannelsFromGroup(group
) && DeleteValues("channelgroups", filter
);
705 int CPVRDatabase::GetGroups(CPVRChannelGroups
& results
, const std::string
& query
) const
708 if (ResultQuery(query
))
712 while (!m_pDS
->eof())
714 const std::shared_ptr
<CPVRChannelGroup
> group
= results
.CreateChannelGroup(
715 m_pDS
->fv("iGroupType").get_asInt(),
716 CPVRChannelsPath(m_pDS
->fv("bIsRadio").get_asBool(), m_pDS
->fv("sName").get_asString(),
717 m_pDS
->fv("iClientId").get_asInt()));
719 group
->m_iGroupId
= m_pDS
->fv("idGroup").get_asInt();
720 group
->m_iLastWatched
= static_cast<time_t>(m_pDS
->fv("iLastWatched").get_asInt());
721 group
->m_bHidden
= m_pDS
->fv("bIsHidden").get_asBool();
722 group
->m_iPosition
= m_pDS
->fv("iPosition").get_asInt();
723 group
->m_iLastOpened
= static_cast<uint64_t>(m_pDS
->fv("iLastOpened").get_asInt64());
724 group
->m_isUserSetName
= m_pDS
->fv("bIsUserSetName").get_asBool();
725 group
->m_clientGroupName
= m_pDS
->fv("sClientName").get_asString();
726 group
->m_iClientPosition
= m_pDS
->fv("iClientPosition").get_asInt();
727 results
.Update(group
);
729 CLog::LogFC(LOGDEBUG
, LOGPVR
, "Group '{}' loaded from PVR database", group
->GroupName());
737 CLog::LogF(LOGERROR
, "Couldn't load channel groups from PVR database. Exception.");
742 CLog::LogF(LOGERROR
, "Couldn't load channel groups from PVR database. Query failed.");
748 int CPVRDatabase::GetLocalGroups(CPVRChannelGroups
& results
) const
750 std::unique_lock
<CCriticalSection
> lock(m_critSection
);
751 const std::string query
= PrepareSQL(
752 "SELECT * from channelgroups WHERE bIsRadio = %u AND iClientId = -1", results
.IsRadio());
753 return GetGroups(results
, query
);
756 int CPVRDatabase::Get(CPVRChannelGroups
& results
,
757 const std::vector
<std::shared_ptr
<CPVRClient
>>& clients
) const
759 std::string query
= "SELECT * from channelgroups WHERE bIsRadio = %u ";
761 const std::string clientIds
=
762 GetClientIdsSQL(clients
, true /* add PVR_GROUP_CLIENT_ID_UNKNOWN for db data migration */);
763 if (!clientIds
.empty())
764 query
+= "AND " + clientIds
;
766 std::unique_lock
<CCriticalSection
> lock(m_critSection
);
767 query
= PrepareSQL(query
, results
.IsRadio());
768 return GetGroups(results
, query
);
771 std::vector
<std::shared_ptr
<CPVRChannelGroupMember
>> CPVRDatabase::Get(
772 const CPVRChannelGroup
& group
, const std::vector
<std::shared_ptr
<CPVRClient
>>& clients
) const
774 std::vector
<std::shared_ptr
<CPVRChannelGroupMember
>> results
;
776 /* invalid group id */
777 if (group
.GroupID() < 0)
779 CLog::LogF(LOGERROR
, "Invalid channel group id: {}", group
.GroupID());
783 std::string strQuery
=
784 "SELECT map_channelgroups_channels.idChannel, "
785 "map_channelgroups_channels.iChannelNumber, "
786 "map_channelgroups_channels.iSubChannelNumber, "
787 "map_channelgroups_channels.iOrder, "
788 "map_channelgroups_channels.iClientChannelNumber, "
789 "map_channelgroups_channels.iClientSubChannelNumber, "
790 "channels.iClientId, channels.iUniqueId, channels.bIsRadio "
791 "FROM map_channelgroups_channels "
792 "LEFT JOIN channels ON channels.idChannel = map_channelgroups_channels.idChannel "
793 "WHERE map_channelgroups_channels.idGroup = %i ";
794 const std::string clientIds
= GetClientIdsSQL(clients
);
795 if (!clientIds
.empty())
796 strQuery
+= "AND " + clientIds
;
797 strQuery
+= " ORDER BY map_channelgroups_channels.iChannelNumber";
799 std::unique_lock
<CCriticalSection
> lock(m_critSection
);
800 strQuery
= PrepareSQL(strQuery
, group
.GroupID());
801 if (ResultQuery(strQuery
))
805 while (!m_pDS
->eof())
807 const auto newMember
= std::make_shared
<CPVRChannelGroupMember
>();
808 newMember
->m_iChannelDatabaseID
= m_pDS
->fv("idChannel").get_asInt();
809 newMember
->m_iChannelClientID
= m_pDS
->fv("iClientId").get_asInt();
810 newMember
->m_iChannelUID
= m_pDS
->fv("iUniqueId").get_asInt();
811 newMember
->m_iGroupID
= group
.GroupID();
812 newMember
->m_iGroupClientID
= group
.GetClientID();
813 newMember
->m_bIsRadio
= m_pDS
->fv("bIsRadio").get_asBool();
814 newMember
->m_channelNumber
= {
815 static_cast<unsigned int>(m_pDS
->fv("iChannelNumber").get_asInt()),
816 static_cast<unsigned int>(m_pDS
->fv("iSubChannelNumber").get_asInt())};
817 newMember
->m_clientChannelNumber
= {
818 static_cast<unsigned int>(m_pDS
->fv("iClientChannelNumber").get_asInt()),
819 static_cast<unsigned int>(m_pDS
->fv("iClientSubChannelNumber").get_asInt())};
820 newMember
->m_iOrder
= static_cast<int>(m_pDS
->fv("iOrder").get_asInt());
821 newMember
->SetGroupName(group
.GroupName());
823 results
.emplace_back(newMember
);
830 CLog::LogF(LOGERROR
, "Failed to get channel group members");
837 bool CPVRDatabase::PersistChannels(const CPVRChannelGroup
& group
)
839 /* invalid group id */
840 if (group
.GroupID() < 0)
842 CLog::LogF(LOGERROR
, "Invalid channel group id: {}", group
.GroupID());
848 std::shared_ptr
<CPVRChannel
> channel
;
849 for (const auto& groupMember
: group
.m_members
)
851 channel
= groupMember
.second
->Channel();
852 if (channel
->IsChanged() || channel
->IsNew())
854 if (Persist(*channel
, false))
856 channel
->Persisted();
862 bReturn
&= CommitInsertQueries();
866 std::string strQuery
;
867 std::string strValue
;
868 for (const auto& groupMember
: group
.m_members
)
870 channel
= groupMember
.second
->Channel();
872 PrepareSQL("iUniqueId = %i AND iClientId = %i", channel
->UniqueID(), channel
->ClientID());
873 strValue
= GetSingleValue("channels", "idChannel", strQuery
);
874 if (!strValue
.empty() && StringUtils::IsInteger(strValue
))
876 const int iChannelID
= std::atoi(strValue
.c_str());
877 channel
->SetChannelID(iChannelID
);
878 groupMember
.second
->m_iChannelDatabaseID
= iChannelID
;
886 bool CPVRDatabase::PersistGroupMembers(const CPVRChannelGroup
& group
)
888 /* invalid group id */
889 if (group
.GroupID() < 0)
891 CLog::LogF(LOGERROR
, "Invalid channel group id: {}", group
.GroupID());
897 if (group
.HasChannels())
899 for (const auto& groupMember
: group
.m_sortedMembers
)
901 if (groupMember
->NeedsSave())
903 if (groupMember
->ChannelDatabaseID() <= 0)
905 CLog::LogF(LOGERROR
, "Invalid channel id: {}", groupMember
->ChannelDatabaseID());
909 const std::string strWhereClause
=
910 PrepareSQL("idChannel = %i AND idGroup = %i AND iChannelNumber = %u AND "
911 "iSubChannelNumber = %u AND "
912 "iOrder = %i AND iClientChannelNumber = %u AND iClientSubChannelNumber = %u",
913 groupMember
->ChannelDatabaseID(), group
.GroupID(),
914 groupMember
->ChannelNumber().GetChannelNumber(),
915 groupMember
->ChannelNumber().GetSubChannelNumber(), groupMember
->Order(),
916 groupMember
->ClientChannelNumber().GetChannelNumber(),
917 groupMember
->ClientChannelNumber().GetSubChannelNumber());
919 const std::string strValue
=
920 GetSingleValue("map_channelgroups_channels", "idChannel", strWhereClause
);
921 if (strValue
.empty())
923 const std::string strQuery
=
924 PrepareSQL("REPLACE INTO map_channelgroups_channels ("
925 "idGroup, idChannel, iChannelNumber, iSubChannelNumber, iOrder, "
926 "iClientChannelNumber, iClientSubChannelNumber) "
927 "VALUES (%i, %i, %i, %i, %i, %i, %i);",
928 group
.GroupID(), groupMember
->ChannelDatabaseID(),
929 groupMember
->ChannelNumber().GetChannelNumber(),
930 groupMember
->ChannelNumber().GetSubChannelNumber(), groupMember
->Order(),
931 groupMember
->ClientChannelNumber().GetChannelNumber(),
932 groupMember
->ClientChannelNumber().GetSubChannelNumber());
933 QueueInsertQuery(strQuery
);
938 bReturn
= CommitInsertQueries();
942 for (const auto& groupMember
: group
.m_sortedMembers
)
944 groupMember
->SetSaved();
952 bool CPVRDatabase::ResetEPG()
954 std::unique_lock
<CCriticalSection
> lock(m_critSection
);
955 const std::string strQuery
= PrepareSQL("UPDATE channels SET idEpg = 0");
956 return ExecuteQuery(strQuery
);
959 bool CPVRDatabase::Persist(CPVRChannelGroup
& group
)
962 if (group
.GroupName().empty())
964 CLog::LogF(LOGERROR
, "Empty group name");
968 std::string strQuery
;
970 std::unique_lock
<CCriticalSection
> lock(m_critSection
);
972 if (group
.HasChanges() || group
.IsNew())
974 /* insert a new entry when this is a new group, or replace the existing one otherwise */
976 strQuery
= PrepareSQL("INSERT INTO channelgroups (bIsRadio, iGroupType, sName, iLastWatched, "
977 "bIsHidden, iPosition, iLastOpened, iClientId, bIsUserSetName, "
978 "sClientName, iClientPosition) VALUES (%i, %i, '%s', "
979 "%u, %i, %i, %llu, %i, %i, '%s', %i)",
980 (group
.IsRadio() ? 1 : 0), group
.GroupType(), group
.GroupName().c_str(),
981 static_cast<unsigned int>(group
.LastWatched()), group
.IsHidden(),
982 group
.GetPosition(), group
.LastOpened(), group
.GetClientID(),
983 (group
.IsUserSetName() ? 1 : 0), group
.ClientGroupName().c_str(),
984 group
.GetClientPosition());
986 strQuery
= PrepareSQL(
987 "REPLACE INTO channelgroups (idGroup, bIsRadio, iGroupType, sName, iLastWatched, "
988 "bIsHidden, iPosition, iLastOpened, iClientId, bIsUserSetName, sClientName, "
989 "iClientPosition) VALUES (%i, %i, %i, '%s', %u, %i, %i, %llu, %i, %i, '%s', %i)",
990 group
.GroupID(), (group
.IsRadio() ? 1 : 0), group
.GroupType(), group
.GroupName().c_str(),
991 static_cast<unsigned int>(group
.LastWatched()), group
.IsHidden(), group
.GetPosition(),
992 group
.LastOpened(), group
.GetClientID(), (group
.IsUserSetName() ? 1 : 0),
993 group
.ClientGroupName().c_str(), group
.GetClientPosition());
995 bReturn
= ExecuteQuery(strQuery
);
997 // set the group ID for new groups
998 if (bReturn
&& group
.IsNew())
999 group
.SetGroupID(static_cast<int>(m_pDS
->lastinsertid()));
1004 // only persist the channel data for the all channels groups as all groups
1005 // share the same channel instances and all groups has them all.
1006 if (group
.IsChannelsOwner())
1007 bReturn
&= PersistChannels(group
);
1009 /* persist the group member entries */
1011 bReturn
= PersistGroupMembers(group
);
1016 bool CPVRDatabase::Persist(CPVRChannel
& channel
, bool bCommit
)
1018 bool bReturn(false);
1020 /* invalid channel */
1021 if (channel
.UniqueID() <= 0)
1023 CLog::LogF(LOGERROR
, "Invalid channel uid: {}", channel
.UniqueID());
1027 std::unique_lock
<CCriticalSection
> lock(m_critSection
);
1029 // Note: Do not use channel.ChannelID value to check presence of channel in channels table. It might not yet be set correctly.
1030 std::string strQuery
=
1031 PrepareSQL("iUniqueId = %i AND iClientId = %i", channel
.UniqueID(), channel
.ClientID());
1032 const std::string strValue
= GetSingleValue("channels", "idChannel", strQuery
);
1033 if (strValue
.empty())
1036 strQuery
= PrepareSQL(
1037 "INSERT INTO channels ("
1038 "iUniqueId, bIsRadio, bIsHidden, bIsUserSetIcon, bIsUserSetName, bIsLocked, "
1039 "sIconPath, sChannelName, bIsVirtual, bEPGEnabled, sEPGScraper, iLastWatched, iClientId, "
1040 "idEpg, bHasArchive, iClientProviderUid, bIsUserSetHidden, iLastWatchedGroupId) "
1041 "VALUES (%i, %i, %i, %i, %i, %i, '%s', '%s', %i, %i, '%s', %u, %i, %i, %i, %i, %i, %i)",
1042 channel
.UniqueID(), (channel
.IsRadio() ? 1 : 0), (channel
.IsHidden() ? 1 : 0),
1043 (channel
.IsUserSetIcon() ? 1 : 0), (channel
.IsUserSetName() ? 1 : 0),
1044 (channel
.IsLocked() ? 1 : 0), channel
.IconPath().c_str(), channel
.ChannelName().c_str(), 0,
1045 (channel
.EPGEnabled() ? 1 : 0), channel
.EPGScraper().c_str(),
1046 static_cast<unsigned int>(channel
.LastWatched()), channel
.ClientID(), channel
.EpgID(),
1047 channel
.HasArchive(), channel
.ClientProviderUid(), channel
.IsUserSetHidden() ? 1 : 0,
1048 channel
.LastWatchedGroupId());
1052 /* update channel */
1053 strQuery
= PrepareSQL(
1054 "REPLACE INTO channels ("
1055 "iUniqueId, bIsRadio, bIsHidden, bIsUserSetIcon, bIsUserSetName, bIsLocked, "
1056 "sIconPath, sChannelName, bIsVirtual, bEPGEnabled, sEPGScraper, iLastWatched, iClientId, "
1057 "idChannel, idEpg, bHasArchive, iClientProviderUid, bIsUserSetHidden, iLastWatchedGroupId) "
1058 "VALUES (%i, %i, %i, %i, %i, %i, '%s', '%s', %i, %i, '%s', %u, %i, %s, %i, %i, %i, %i, %i)",
1059 channel
.UniqueID(), (channel
.IsRadio() ? 1 : 0), (channel
.IsHidden() ? 1 : 0),
1060 (channel
.IsUserSetIcon() ? 1 : 0), (channel
.IsUserSetName() ? 1 : 0),
1061 (channel
.IsLocked() ? 1 : 0), channel
.ClientIconPath().c_str(),
1062 channel
.ChannelName().c_str(), 0, (channel
.EPGEnabled() ? 1 : 0),
1063 channel
.EPGScraper().c_str(), static_cast<unsigned int>(channel
.LastWatched()),
1064 channel
.ClientID(), strValue
.c_str(), channel
.EpgID(), channel
.HasArchive(),
1065 channel
.ClientProviderUid(), channel
.IsUserSetHidden() ? 1 : 0,
1066 channel
.LastWatchedGroupId());
1069 if (QueueInsertQuery(strQuery
))
1074 bReturn
= CommitInsertQueries();
1080 bool CPVRDatabase::UpdateLastWatched(const CPVRChannel
& channel
, int groupId
)
1082 std::unique_lock
<CCriticalSection
> lock(m_critSection
);
1083 const std::string strQuery
= PrepareSQL(
1084 "UPDATE channels SET iLastWatched = %u, iLastWatchedGroupId = %i WHERE idChannel = %i",
1085 static_cast<unsigned int>(channel
.LastWatched()), groupId
, channel
.ChannelID());
1086 return ExecuteQuery(strQuery
);
1089 bool CPVRDatabase::UpdateLastWatched(const CPVRChannelGroup
& group
)
1091 std::unique_lock
<CCriticalSection
> lock(m_critSection
);
1092 const std::string strQuery
=
1093 PrepareSQL("UPDATE channelgroups SET iLastWatched = %u WHERE idGroup = %i",
1094 static_cast<unsigned int>(group
.LastWatched()), group
.GroupID());
1095 return ExecuteQuery(strQuery
);
1098 bool CPVRDatabase::UpdateLastOpened(const CPVRChannelGroup
& group
)
1100 std::unique_lock
<CCriticalSection
> lock(m_critSection
);
1101 const std::string strQuery
=
1102 PrepareSQL("UPDATE channelgroups SET iLastOpened = %llu WHERE idGroup = %i",
1103 group
.LastOpened(), group
.GroupID());
1104 return ExecuteQuery(strQuery
);
1107 /********** Timer methods **********/
1109 std::vector
<std::shared_ptr
<CPVRTimerInfoTag
>> CPVRDatabase::GetTimers(
1110 CPVRTimers
& timers
, const std::vector
<std::shared_ptr
<CPVRClient
>>& clients
) const
1112 std::vector
<std::shared_ptr
<CPVRTimerInfoTag
>> result
;
1114 std::string strQuery
= "SELECT * FROM timers ";
1115 const std::string clientIds
= GetClientIdsSQL(clients
);
1116 if (!clientIds
.empty())
1117 strQuery
+= "WHERE " + clientIds
;
1119 std::unique_lock
<CCriticalSection
> lock(m_critSection
);
1120 strQuery
= PrepareSQL(strQuery
);
1121 if (ResultQuery(strQuery
))
1125 while (!m_pDS
->eof())
1127 std::shared_ptr
<CPVRTimerInfoTag
> newTag(new CPVRTimerInfoTag());
1129 newTag
->m_iClientIndex
= -m_pDS
->fv("iClientIndex").get_asInt();
1130 newTag
->m_iParentClientIndex
= m_pDS
->fv("iParentClientIndex").get_asInt();
1131 newTag
->m_iClientId
= m_pDS
->fv("iClientId").get_asInt();
1132 newTag
->SetTimerType(CPVRTimerType::CreateFromIds(m_pDS
->fv("iTimerType").get_asInt(), -1));
1133 newTag
->m_state
= static_cast<PVR_TIMER_STATE
>(m_pDS
->fv("iState").get_asInt());
1134 newTag
->m_strTitle
= m_pDS
->fv("sTitle").get_asString().c_str();
1135 newTag
->m_iClientChannelUid
= m_pDS
->fv("iClientChannelUid").get_asInt();
1136 newTag
->m_strSeriesLink
= m_pDS
->fv("sSeriesLink").get_asString().c_str();
1137 newTag
->SetStartFromUTC(CDateTime::FromDBDateTime(m_pDS
->fv("sStartTime").get_asString().c_str()));
1138 newTag
->m_bStartAnyTime
= m_pDS
->fv("bStartAnyTime").get_asBool();
1139 newTag
->SetEndFromUTC(CDateTime::FromDBDateTime(m_pDS
->fv("sEndTime").get_asString().c_str()));
1140 newTag
->m_bEndAnyTime
= m_pDS
->fv("bEndAnyTime").get_asBool();
1141 newTag
->SetFirstDayFromUTC(CDateTime::FromDBDateTime(m_pDS
->fv("sFirstDay").get_asString().c_str()));
1142 newTag
->m_iWeekdays
= m_pDS
->fv("iWeekdays").get_asInt();
1143 newTag
->m_iEpgUid
= m_pDS
->fv("iEpgUid").get_asInt();
1144 newTag
->m_iMarginStart
= m_pDS
->fv("iMarginStart").get_asInt();
1145 newTag
->m_iMarginEnd
= m_pDS
->fv("iMarginEnd").get_asInt();
1146 newTag
->m_strEpgSearchString
= m_pDS
->fv("sEpgSearchString").get_asString().c_str();
1147 newTag
->m_bFullTextEpgSearch
= m_pDS
->fv("bFullTextEpgSearch").get_asBool();
1148 newTag
->m_iPreventDupEpisodes
= m_pDS
->fv("iPreventDuplicates").get_asInt();
1149 newTag
->m_iPriority
= m_pDS
->fv("iPrority").get_asInt();
1150 newTag
->m_iLifetime
= m_pDS
->fv("iLifetime").get_asInt();
1151 newTag
->m_iMaxRecordings
= m_pDS
->fv("iMaxRecordings").get_asInt();
1152 newTag
->m_iRecordingGroup
= m_pDS
->fv("iRecordingGroup").get_asInt();
1153 newTag
->UpdateSummary();
1155 result
.emplace_back(newTag
);
1163 CLog::LogF(LOGERROR
, "Could not load timer data from the database");
1169 bool CPVRDatabase::Persist(CPVRTimerInfoTag
& timer
)
1171 std::unique_lock
<CCriticalSection
> lock(m_critSection
);
1173 // insert a new entry if this is a new timer, or replace the existing one otherwise
1174 std::string strQuery
;
1175 if (timer
.m_iClientIndex
== PVR_TIMER_NO_CLIENT_INDEX
)
1176 strQuery
= PrepareSQL("INSERT INTO timers "
1177 "(iParentClientIndex, iClientId, iTimerType, iState, sTitle, iClientChannelUid, sSeriesLink, sStartTime,"
1178 " bStartAnyTime, sEndTime, bEndAnyTime, sFirstDay, iWeekdays, iEpgUid, iMarginStart, iMarginEnd,"
1179 " sEpgSearchString, bFullTextEpgSearch, iPreventDuplicates, iPrority, iLifetime, iMaxRecordings, iRecordingGroup) "
1180 "VALUES (%i, %i, %u, %i, '%s', %i, '%s', '%s', %i, '%s', %i, '%s', %i, %u, %i, %i, '%s', %i, %i, %i, %i, %i, %i);",
1181 timer
.m_iParentClientIndex
, timer
.m_iClientId
, timer
.GetTimerType()->GetTypeId(), timer
.m_state
,
1182 timer
.Title().c_str(), timer
.m_iClientChannelUid
, timer
.SeriesLink().c_str(),
1183 timer
.StartAsUTC().GetAsDBDateTime().c_str(), timer
.m_bStartAnyTime
? 1 : 0,
1184 timer
.EndAsUTC().GetAsDBDateTime().c_str(), timer
.m_bEndAnyTime
? 1 : 0,
1185 timer
.FirstDayAsUTC().GetAsDBDateTime().c_str(), timer
.m_iWeekdays
, timer
.UniqueBroadcastID(),
1186 timer
.m_iMarginStart
, timer
.m_iMarginEnd
, timer
.m_strEpgSearchString
.c_str(), timer
.m_bFullTextEpgSearch
? 1 : 0,
1187 timer
.m_iPreventDupEpisodes
, timer
.m_iPriority
, timer
.m_iLifetime
, timer
.m_iMaxRecordings
, timer
.m_iRecordingGroup
);
1189 strQuery
= PrepareSQL("REPLACE INTO timers "
1191 " iParentClientIndex, iClientId, iTimerType, iState, sTitle, iClientChannelUid, sSeriesLink, sStartTime,"
1192 " bStartAnyTime, sEndTime, bEndAnyTime, sFirstDay, iWeekdays, iEpgUid, iMarginStart, iMarginEnd,"
1193 " sEpgSearchString, bFullTextEpgSearch, iPreventDuplicates, iPrority, iLifetime, iMaxRecordings, iRecordingGroup) "
1194 "VALUES (%i, %i, %i, %u, %i, '%s', %i, '%s', '%s', %i, '%s', %i, '%s', %i, %u, %i, %i, '%s', %i, %i, %i, %i, %i, %i);",
1195 -timer
.m_iClientIndex
,
1196 timer
.m_iParentClientIndex
, timer
.m_iClientId
, timer
.GetTimerType()->GetTypeId(), timer
.m_state
,
1197 timer
.Title().c_str(), timer
.m_iClientChannelUid
, timer
.SeriesLink().c_str(),
1198 timer
.StartAsUTC().GetAsDBDateTime().c_str(), timer
.m_bStartAnyTime
? 1 : 0,
1199 timer
.EndAsUTC().GetAsDBDateTime().c_str(), timer
.m_bEndAnyTime
? 1 : 0,
1200 timer
.FirstDayAsUTC().GetAsDBDateTime().c_str(), timer
.m_iWeekdays
, timer
.UniqueBroadcastID(),
1201 timer
.m_iMarginStart
, timer
.m_iMarginEnd
, timer
.m_strEpgSearchString
.c_str(), timer
.m_bFullTextEpgSearch
? 1 : 0,
1202 timer
.m_iPreventDupEpisodes
, timer
.m_iPriority
, timer
.m_iLifetime
, timer
.m_iMaxRecordings
, timer
.m_iRecordingGroup
);
1204 bool bReturn
= ExecuteQuery(strQuery
);
1206 // set the client index for just inserted timers
1207 if (bReturn
&& timer
.m_iClientIndex
== PVR_TIMER_NO_CLIENT_INDEX
)
1209 // index must be negative for local timers!
1210 timer
.m_iClientIndex
= -static_cast<int>(m_pDS
->lastinsertid());
1216 bool CPVRDatabase::Delete(const CPVRTimerInfoTag
& timer
)
1218 if (timer
.m_iClientIndex
== PVR_TIMER_NO_CLIENT_INDEX
)
1221 CLog::LogFC(LOGDEBUG
, LOGPVR
, "Deleting timer '{}' from the database", timer
.m_iClientIndex
);
1223 std::unique_lock
<CCriticalSection
> lock(m_critSection
);
1226 filter
.AppendWhere(PrepareSQL("iClientIndex = '%i'", -timer
.m_iClientIndex
));
1228 return DeleteValues("timers", filter
);
1231 bool CPVRDatabase::DeleteTimers()
1233 CLog::LogFC(LOGDEBUG
, LOGPVR
, "Deleting all timers from the database");
1235 std::unique_lock
<CCriticalSection
> lock(m_critSection
);
1236 return DeleteValues("timers");