2 * Copyright (C) 2012-2013 Team XBMC
5 * This Program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2, or (at your option)
10 * This Program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with XBMC; see the file COPYING. If not, see
17 * <http://www.gnu.org/licenses/>.
21 #include "PVRDatabase.h"
22 #include "dbwrappers/dataset.h"
23 #include "settings/AdvancedSettings.h"
24 #include "settings/VideoSettings.h"
25 #include "utils/log.h"
27 #include "PVRManager.h"
28 #include "channels/PVRChannelGroupsContainer.h"
29 #include "channels/PVRChannelGroupInternal.h"
30 #include "addons/PVRClient.h"
33 using namespace dbiplus;
35 using namespace ADDON;
37 #define PVRDB_DEBUGGING 0
39 bool CPVRDatabase::Open()
41 return CDatabase::Open(g_advancedSettings.m_databaseTV);
44 bool CPVRDatabase::CreateTables()
50 if (!CDatabase::CreateTables())
54 CLog::Log(LOGINFO, "PVR - %s - creating tables", __FUNCTION__);
56 CLog::Log(LOGDEBUG, "PVR - %s - creating table 'clients'", __FUNCTION__);
58 "CREATE TABLE clients ("
59 "idClient integer primary key, "
65 CLog::Log(LOGDEBUG, "PVR - %s - creating table 'channels'", __FUNCTION__);
67 "CREATE TABLE channels ("
68 "idChannel integer primary key, "
72 "bIsUserSetIcon bool, "
74 "sIconPath varchar(255), "
75 "sChannelName varchar(64), "
78 "sEPGScraper varchar(32), "
79 "iLastWatched integer,"
81 // TODO use mapping table
83 "iClientChannelNumber integer, "
84 "sInputFormat varchar(32), "
85 "sStreamURL varchar(255), "
86 "iEncryptionSystem integer, "
91 m_pDS->exec("CREATE UNIQUE INDEX idx_channels_iClientId_iUniqueId on channels(iClientId, iUniqueId);");
93 // TODO use a mapping table so multiple backends per channel can be implemented
94 // CLog::Log(LOGDEBUG, "PVR - %s - creating table 'map_channels_clients'", __FUNCTION__);
96 // "CREATE TABLE map_channels_clients ("
97 // "idChannel integer primary key, "
98 // "idClient integer, "
99 // "iClientChannelNumber integer,"
100 // "sInputFormat string,"
101 // "sStreamURL string,"
102 // "iEncryptionSystem integer"
105 // m_pDS->exec("CREATE UNIQUE INDEX idx_idChannel_idClient on map_channels_clients(idChannel, idClient);");
107 CLog::Log(LOGDEBUG, "PVR - %s - creating table 'channelgroups'", __FUNCTION__);
109 "CREATE TABLE channelgroups ("
110 "idGroup integer primary key,"
112 "iGroupType integer, "
116 m_pDS->exec("CREATE INDEX idx_channelgroups_bIsRadio on channelgroups(bIsRadio);");
118 CLog::Log(LOGDEBUG, "PVR - %s - creating table 'map_channelgroups_channels'", __FUNCTION__);
120 "CREATE TABLE map_channelgroups_channels ("
121 "idChannel integer, "
123 "iChannelNumber integer"
126 m_pDS->exec("CREATE UNIQUE INDEX idx_idGroup_idChannel on map_channelgroups_channels(idGroup, idChannel);");
128 CLog::Log(LOGDEBUG, "PVR - %s - creating table 'channelsettings'", __FUNCTION__);
130 "CREATE TABLE channelsettings ("
131 "idChannel integer primary key, "
132 "iInterlaceMethod integer, "
133 "iViewMode integer, "
134 "fCustomZoomAmount float, "
135 "fPixelRatio float, "
136 "iAudioStream integer, "
137 "iSubtitleStream integer,"
138 "fSubtitleDelay float, "
140 "fBrightness float, "
143 "fVolumeAmplification float, "
144 "fAudioDelay float, "
145 "bOutputToAllSpeakers bool, "
147 "iCropLeft integer, "
148 "iCropRight integer, "
150 "iCropBottom integer, "
152 "fNoiseReduction float, "
153 "fCustomVerticalShift float, "
154 "bCustomNonLinStretch bool, "
155 "bPostProcess bool, "
156 "iScalingMethod integer, "
157 "iDeinterlaceMode integer "
166 CLog::Log(LOGERROR, "PVR - %s - unable to create PVR database tables (error %i)", __FUNCTION__, (int)GetLastError());
167 RollbackTransaction();
173 // disable all PVR add-on when started the first time
174 ADDON::VECADDONS addons;
175 if ((bReturn = CAddonMgr::Get().GetAddons(ADDON_PVRDLL, addons, true)) == false)
176 CLog::Log(LOGERROR, "PVR - %s - failed to get add-ons from the add-on manager", __FUNCTION__);
179 CAddonDatabase database;
181 for (IVECADDONS it = addons.begin(); it != addons.end(); it++)
182 database.DisableAddon(it->get()->ID());
190 bool CPVRDatabase::UpdateOldVersion(int iVersion)
200 CLog::Log(LOGERROR, "PVR - %s - updating from table versions < 11 not supported. please delete '%s'",
201 __FUNCTION__, GetBaseDBName());
207 m_pDS->exec("DROP VIEW vw_last_watched;");
210 m_pDS->exec("ALTER TABLE channels ADD idEpg integer;");
213 m_pDS->exec("ALTER TABLE channelsettings ADD fCustomVerticalShift float;");
217 m_pDS->exec("ALTER TABLE channelsettings ADD bCustomNonLinStretch bool;");
218 m_pDS->exec("ALTER TABLE channelsettings ADD bPostProcess bool;");
219 m_pDS->exec("ALTER TABLE channelsettings ADD iScalingMethod integer;");
223 /* sqlite apparently can't delete columns from an existing table, so just leave the extra column alone */
227 m_pDS->exec("ALTER TABLE channelsettings ADD iDeinterlaceMode integer");
228 m_pDS->exec("UPDATE channelsettings SET iDeinterlaceMode = 2 WHERE iInterlaceMethod NOT IN (0,1)"); // anything other than none: method auto => mode force
229 m_pDS->exec("UPDATE channelsettings SET iDeinterlaceMode = 1 WHERE iInterlaceMethod = 1"); // method auto => mode auto
230 m_pDS->exec("UPDATE channelsettings SET iDeinterlaceMode = 0, iInterlaceMethod = 1 WHERE iInterlaceMethod = 0"); // method none => mode off, method auto
234 m_pDS->exec("DROP INDEX idx_channels_iClientId;");
235 m_pDS->exec("DROP INDEX idx_channels_iLastWatched;");
236 m_pDS->exec("DROP INDEX idx_channels_bIsRadio;");
237 m_pDS->exec("DROP INDEX idx_channels_bIsHidden;");
238 m_pDS->exec("DROP INDEX idx_idChannel_idGroup;");
239 m_pDS->exec("DROP INDEX idx_idGroup_iChannelNumber;");
240 m_pDS->exec("CREATE UNIQUE INDEX idx_channels_iClientId_iUniqueId on channels(iClientId, iUniqueId);");
241 m_pDS->exec("CREATE UNIQUE INDEX idx_idGroup_idChannel on map_channelgroups_channels(idGroup, idChannel);");
245 // bit of a hack, but we need to keep the version/contents of the non-pvr databases the same to allow clean upgrades
246 ADDON::VECADDONS addons;
247 if ((bReturn = CAddonMgr::Get().GetAddons(ADDON_PVRDLL, addons, true)) == false)
248 CLog::Log(LOGERROR, "PVR - %s - failed to get add-ons from the add-on manager", __FUNCTION__);
251 CAddonDatabase database;
253 for (IVECADDONS it = addons.begin(); it != addons.end(); it++)
255 if (!database.IsSystemPVRAddonEnabled(it->get()->ID()))
256 database.DisableAddon(it->get()->ID());
262 m_pDS->exec("ALTER TABLE channels ADD bIsUserSetIcon bool");
265 m_pDS->exec("ALTER TABLE channelgroups ADD iGroupType integer");
268 m_pDS->exec("ALTER TABLE channels ADD bIsLocked bool");
273 CLog::Log(LOGERROR, "PVR - %s - error attempting to update the database version!", __FUNCTION__);
280 RollbackTransaction();
285 int CPVRDatabase::GetLastChannelId(void)
289 CStdString strQuery = FormatSQL("SELECT MAX(idChannel) as iMaxChannel FROM channels");
290 if (ResultQuery(strQuery))
295 iReturn = m_pDS->fv("iMaxChannel").get_asInt();
303 /********** Channel methods **********/
305 bool CPVRDatabase::DeleteChannels(void)
307 CLog::Log(LOGDEBUG, "PVR - %s - deleting all channels from the database", __FUNCTION__);
308 return DeleteValues("channels");
311 bool CPVRDatabase::DeleteClientChannels(const CPVRClient &client)
313 /* invalid client Id */
314 if (client.GetID() <= 0)
316 CLog::Log(LOGERROR, "PVR - %s - invalid client id: %i", __FUNCTION__, client.GetID());
320 CLog::Log(LOGDEBUG, "PVR - %s - deleting all channels from client '%i' from the database", __FUNCTION__, client.GetID());
321 CStdString strWhereClause = FormatSQL("iClientId = %u", client.GetID());
322 return DeleteValues("channels", strWhereClause);
325 bool CPVRDatabase::Delete(const CPVRChannel &channel)
327 /* invalid channel */
328 if (channel.ChannelID() <= 0)
331 CLog::Log(LOGDEBUG, "PVR - %s - deleting channel '%s' from the database", __FUNCTION__, channel.ChannelName().c_str());
332 CStdString strWhereClause = FormatSQL("idChannel = %u", channel.ChannelID());
333 return DeleteValues("channels", strWhereClause);
336 int CPVRDatabase::Get(CPVRChannelGroupInternal &results)
340 CStdString strQuery = FormatSQL("SELECT channels.idChannel, channels.iUniqueId, channels.bIsRadio, channels.bIsHidden, channels.bIsUserSetIcon, "
341 "channels.sIconPath, channels.sChannelName, channels.bIsVirtual, channels.bEPGEnabled, channels.sEPGScraper, channels.iLastWatched, channels.iClientId, channels.bIsLocked, "
342 "channels.iClientChannelNumber, channels.sInputFormat, channels.sInputFormat, channels.sStreamURL, channels.iEncryptionSystem, map_channelgroups_channels.iChannelNumber, channels.idEpg "
343 "FROM map_channelgroups_channels "
344 "LEFT JOIN channels ON channels.idChannel = map_channelgroups_channels.idChannel "
345 "WHERE map_channelgroups_channels.idGroup = %u", results.IsRadio() ? XBMC_INTERNAL_GROUP_RADIO : XBMC_INTERNAL_GROUP_TV);
346 if (ResultQuery(strQuery))
350 while (!m_pDS->eof())
352 CPVRChannelPtr channel = CPVRChannelPtr(new CPVRChannel());
354 channel->m_iChannelId = m_pDS->fv("idChannel").get_asInt();
355 channel->m_iUniqueId = m_pDS->fv("iUniqueId").get_asInt();
356 channel->m_bIsRadio = m_pDS->fv("bIsRadio").get_asBool();
357 channel->m_bIsHidden = m_pDS->fv("bIsHidden").get_asBool();
358 channel->m_bIsUserSetIcon = m_pDS->fv("bIsUserSetIcon").get_asBool();
359 channel->m_bIsLocked = m_pDS->fv("bIsLocked").get_asBool();
360 channel->m_strIconPath = m_pDS->fv("sIconPath").get_asString();
361 channel->m_strChannelName = m_pDS->fv("sChannelName").get_asString();
362 channel->m_bIsVirtual = m_pDS->fv("bIsVirtual").get_asBool();
363 channel->m_bEPGEnabled = m_pDS->fv("bEPGEnabled").get_asBool();
364 channel->m_strEPGScraper = m_pDS->fv("sEPGScraper").get_asString();
365 channel->m_iLastWatched = (time_t) m_pDS->fv("iLastWatched").get_asInt();
366 channel->m_iClientId = m_pDS->fv("iClientId").get_asInt();
367 channel->m_iClientChannelNumber = m_pDS->fv("iClientChannelNumber").get_asInt();
368 channel->m_strInputFormat = m_pDS->fv("sInputFormat").get_asString();
369 channel->m_strStreamURL = m_pDS->fv("sStreamURL").get_asString();
370 channel->m_iClientEncryptionSystem = m_pDS->fv("iEncryptionSystem").get_asInt();
371 channel->m_iEpgId = m_pDS->fv("idEpg").get_asInt();
372 channel->UpdateEncryptionName();
375 CLog::Log(LOGDEBUG, "PVR - %s - channel '%s' loaded from the database", __FUNCTION__, channel->m_strChannelName.c_str());
377 PVRChannelGroupMember newMember = { channel, (unsigned int)m_pDS->fv("iChannelNumber").get_asInt() };
378 results.m_members.push_back(newMember);
387 CLog::Log(LOGERROR, "PVR - %s - couldn't load channels from the database", __FUNCTION__);
392 CLog::Log(LOGERROR, "PVR - %s - query failed", __FUNCTION__);
399 bool CPVRDatabase::DeleteChannelSettings()
401 CLog::Log(LOGDEBUG, "PVR - %s - deleting all channel settings from the database", __FUNCTION__);
402 return DeleteValues("channelsettings");
405 bool CPVRDatabase::DeleteChannelSettings(const CPVRChannel &channel)
409 /* invalid channel */
410 if (channel.ChannelID() <= 0)
412 CLog::Log(LOGERROR, "PVR - %s - invalid channel id: %i", __FUNCTION__, channel.ChannelID());
416 CStdString strWhereClause = FormatSQL("idChannel = %u", channel.ChannelID());
417 return DeleteValues("channelsettings", strWhereClause);
420 bool CPVRDatabase::GetChannelSettings(const CPVRChannel &channel, CVideoSettings &settings)
424 /* invalid channel */
425 if (channel.ChannelID() <= 0)
427 CLog::Log(LOGERROR, "PVR - %s - invalid channel id: %i", __FUNCTION__, channel.ChannelID());
431 CStdString strQuery = FormatSQL("SELECT * FROM channelsettings WHERE idChannel = %u;", channel.ChannelID());
433 if (ResultQuery(strQuery))
437 if (m_pDS->num_rows() > 0)
439 settings.m_AudioDelay = m_pDS->fv("fAudioDelay").get_asFloat();
440 settings.m_AudioStream = m_pDS->fv("iAudioStream").get_asInt();
441 settings.m_Brightness = m_pDS->fv("fBrightness").get_asFloat();
442 settings.m_Contrast = m_pDS->fv("fContrast").get_asFloat();
443 settings.m_CustomPixelRatio = m_pDS->fv("fPixelRatio").get_asFloat();
444 settings.m_CustomNonLinStretch = m_pDS->fv("bCustomNonLinStretch").get_asBool();
445 settings.m_NoiseReduction = m_pDS->fv("fNoiseReduction").get_asFloat();
446 settings.m_PostProcess = m_pDS->fv("bPostProcess").get_asBool();
447 settings.m_Sharpness = m_pDS->fv("fSharpness").get_asFloat();
448 settings.m_CustomZoomAmount = m_pDS->fv("fCustomZoomAmount").get_asFloat();
449 settings.m_CustomVerticalShift = m_pDS->fv("fCustomVerticalShift").get_asFloat();
450 settings.m_Gamma = m_pDS->fv("fGamma").get_asFloat();
451 settings.m_SubtitleDelay = m_pDS->fv("fSubtitleDelay").get_asFloat();
452 settings.m_SubtitleOn = m_pDS->fv("bSubtitles").get_asBool();
453 settings.m_SubtitleStream = m_pDS->fv("iSubtitleStream").get_asInt();
454 settings.m_ViewMode = m_pDS->fv("iViewMode").get_asInt();
455 settings.m_Crop = m_pDS->fv("bCrop").get_asBool();
456 settings.m_CropLeft = m_pDS->fv("iCropLeft").get_asInt();
457 settings.m_CropRight = m_pDS->fv("iCropRight").get_asInt();
458 settings.m_CropTop = m_pDS->fv("iCropTop").get_asInt();
459 settings.m_CropBottom = m_pDS->fv("iCropBottom").get_asInt();
460 settings.m_InterlaceMethod = (EINTERLACEMETHOD)m_pDS->fv("iInterlaceMethod").get_asInt();
461 settings.m_DeinterlaceMode = (EDEINTERLACEMODE)m_pDS->fv("iDeinterlaceMode").get_asInt();
462 settings.m_VolumeAmplification = m_pDS->fv("fVolumeAmplification").get_asFloat();
463 settings.m_OutputToAllSpeakers = m_pDS->fv("bOutputToAllSpeakers").get_asBool();
464 settings.m_ScalingMethod = (ESCALINGMETHOD)m_pDS->fv("iScalingMethod").get_asInt();
473 CLog::Log(LOGERROR, "PVR - %s - failed to get channel settings for channel '%s'", __FUNCTION__, channel.ChannelName().c_str());
478 CLog::Log(LOGERROR, "PVR - %s - query failed", __FUNCTION__);
484 bool CPVRDatabase::PersistChannelSettings(const CPVRChannel &channel, const CVideoSettings &settings)
486 /* invalid channel */
487 if (channel.ChannelID() <= 0)
489 CLog::Log(LOGERROR, "PVR - %s - invalid channel id: %i", __FUNCTION__, channel.ChannelID());
493 CStdString strQuery = FormatSQL(
494 "REPLACE INTO channelsettings "
495 "(idChannel, iInterlaceMethod, iViewMode, fCustomZoomAmount, fPixelRatio, iAudioStream, iSubtitleStream, fSubtitleDelay, "
496 "bSubtitles, fBrightness, fContrast, fGamma, fVolumeAmplification, fAudioDelay, bOutputToAllSpeakers, bCrop, iCropLeft, "
497 "iCropRight, iCropTop, iCropBottom, fSharpness, fNoiseReduction, fCustomVerticalShift, bCustomNonLinStretch, bPostProcess, iScalingMethod, iDeinterlaceMode) VALUES "
498 "(%i, %i, %i, %f, %f, %i, %i, %f, %i, %f, %f, %f, %f, %f, %i, %i, %i, %i, %i, %i, %f, %f, %f, %i, %i, %i, %i);",
499 channel.ChannelID(), settings.m_InterlaceMethod, settings.m_ViewMode, settings.m_CustomZoomAmount, settings.m_CustomPixelRatio,
500 settings.m_AudioStream, settings.m_SubtitleStream, settings.m_SubtitleDelay, settings.m_SubtitleOn ? 1 :0,
501 settings.m_Brightness, settings.m_Contrast, settings.m_Gamma, settings.m_VolumeAmplification, settings.m_AudioDelay,
502 settings.m_OutputToAllSpeakers ? 1 : 0, settings.m_Crop ? 1 : 0, settings.m_CropLeft, settings.m_CropRight, settings.m_CropTop,
503 settings.m_CropBottom, settings.m_Sharpness, settings.m_NoiseReduction, settings.m_CustomVerticalShift,
504 settings.m_CustomNonLinStretch ? 1 : 0, settings.m_PostProcess ? 1 : 0, settings.m_ScalingMethod, settings.m_DeinterlaceMode);
506 return ExecuteQuery(strQuery);
509 /********** Channel group methods **********/
511 bool CPVRDatabase::RemoveChannelsFromGroup(const CPVRChannelGroup &group)
513 CStdString strWhereClause = FormatSQL("idGroup = %u", group.GroupID());
514 return DeleteValues("map_channelgroups_channels", strWhereClause);
517 bool CPVRDatabase::GetCurrentGroupMembers(const CPVRChannelGroup &group, vector<int> &members)
520 /* invalid group id */
521 if (group.GroupID() <= 0)
523 CLog::Log(LOGERROR, "PVR - %s - invalid group id: %d", __FUNCTION__, group.GroupID());
527 CStdString strCurrentMembersQuery = FormatSQL("SELECT idChannel FROM map_channelgroups_channels WHERE idGroup = %u", group.GroupID());
528 if (ResultQuery(strCurrentMembersQuery))
532 while (!m_pDS->eof())
534 members.push_back(m_pDS->fv("idChannel").get_asInt());
542 CLog::Log(LOGERROR, "PVR - %s - couldn't load channels from the database", __FUNCTION__);
547 CLog::Log(LOGERROR, "PVR - %s - query failed", __FUNCTION__);
553 bool CPVRDatabase::DeleteChannelsFromGroup(const CPVRChannelGroup &group)
555 /* invalid group id */
556 if (group.GroupID() <= 0)
558 CLog::Log(LOGERROR, "PVR - %s - invalid group id: %d", __FUNCTION__, group.GroupID());
562 CStdString strWhereClause;
563 strWhereClause = FormatSQL("idGroup = %u", group.GroupID());
564 return DeleteValues("map_channelgroups_channels", strWhereClause);
567 bool CPVRDatabase::DeleteChannelsFromGroup(const CPVRChannelGroup &group, const vector<int> &channelsToDelete)
570 unsigned int iDeletedChannels(0);
571 /* invalid group id */
572 if (group.GroupID() <= 0)
574 CLog::Log(LOGERROR, "PVR - %s - invalid group id: %d", __FUNCTION__, group.GroupID());
578 while (iDeletedChannels < channelsToDelete.size())
580 CStdString strChannelsToDelete;
581 CStdString strWhereClause;
583 for (unsigned int iChannelPtr = 0; iChannelPtr + iDeletedChannels < channelsToDelete.size() && iChannelPtr < 50; iChannelPtr++)
584 strChannelsToDelete.AppendFormat(", %d", channelsToDelete.at(iDeletedChannels + iChannelPtr));
586 if (!strChannelsToDelete.IsEmpty())
588 strChannelsToDelete = strChannelsToDelete.Right(strChannelsToDelete.length() - 2);
589 strWhereClause = FormatSQL("idGroup = %u AND idChannel IN (%s)", group.GroupID(), strChannelsToDelete.c_str());
590 bDelete = DeleteValues("map_channelgroups_channels", strWhereClause) && bDelete;
593 iDeletedChannels += 50;
599 bool CPVRDatabase::RemoveStaleChannelsFromGroup(const CPVRChannelGroup &group)
602 /* invalid group id */
603 if (group.GroupID() <= 0)
605 CLog::Log(LOGERROR, "PVR - %s - invalid group id: %d", __FUNCTION__, group.GroupID());
609 if (!group.IsInternalGroup())
611 /* First remove channels that don't exist in the main channels table */
613 // XXX work around for frodo: fix this up so it uses one query for all db types
614 // mysql doesn't support subqueries when deleting and sqlite doesn't support joins when deleting
615 if (g_advancedSettings.m_databaseTV.type.Equals("mysql"))
617 CStdString strQuery = FormatSQL("DELETE m FROM map_channelgroups_channels m LEFT JOIN channels c ON (c.idChannel = m.idChannel) WHERE c.idChannel IS NULL");
618 bDelete = ExecuteQuery(strQuery);
622 CStdString strWhereClause = FormatSQL("idChannel IN (SELECT m.idChannel FROM map_channelgroups_channels m LEFT JOIN channels on m.idChannel = channels.idChannel WHERE channels.idChannel IS NULL)");
623 bDelete = DeleteValues("map_channelgroups_channels", strWhereClause);
627 if (group.m_members.size() > 0)
629 vector<int> currentMembers;
630 if (GetCurrentGroupMembers(group, currentMembers))
632 vector<int> channelsToDelete;
633 for (unsigned int iChannelPtr = 0; iChannelPtr < currentMembers.size(); iChannelPtr++)
635 if (!group.IsGroupMember(currentMembers.at(iChannelPtr)))
636 channelsToDelete.push_back(currentMembers.at(iChannelPtr));
639 bDelete = DeleteChannelsFromGroup(group, channelsToDelete) && bDelete;
644 CStdString strWhereClause = FormatSQL("idGroup = %u", group.GroupID());
645 bDelete = DeleteValues("map_channelgroups_channels", strWhereClause) && bDelete;
651 bool CPVRDatabase::DeleteChannelGroups(void)
653 CLog::Log(LOGDEBUG, "PVR - %s - deleting all channel groups from the database", __FUNCTION__);
655 return DeleteValues("channelgroups") &&
656 DeleteValues("map_channelgroups_channels");
659 bool CPVRDatabase::Delete(const CPVRChannelGroup &group)
661 /* invalid group id */
662 if (group.GroupID() <= 0)
664 CLog::Log(LOGERROR, "PVR - %s - invalid group id: %d", __FUNCTION__, group.GroupID());
668 CStdString strWhereClause = FormatSQL("idGroup = %u AND bIsRadio = %u", group.GroupID(), group.IsRadio());
669 return RemoveChannelsFromGroup(group) &&
670 DeleteValues("channelgroups", strWhereClause);
673 bool CPVRDatabase::Get(CPVRChannelGroups &results)
675 bool bReturn = false;
676 CStdString strQuery = FormatSQL("SELECT * from channelgroups WHERE bIsRadio = %u", results.IsRadio());
678 if (ResultQuery(strQuery))
682 while (!m_pDS->eof())
684 CPVRChannelGroup data(m_pDS->fv("bIsRadio").get_asBool(), m_pDS->fv("idGroup").get_asInt(), m_pDS->fv("sName").get_asString());
685 data.SetGroupType(m_pDS->fv("iGroupType").get_asInt());
686 results.Update(data);
688 CLog::Log(LOGDEBUG, "PVR - %s - group '%s' loaded from the database", __FUNCTION__, data.GroupName().c_str());
696 CLog::Log(LOGERROR, "%s - couldn't load channels from the database", __FUNCTION__);
703 int CPVRDatabase::Get(CPVRChannelGroup &group)
707 /* invalid group id */
708 if (group.GroupID() < 0)
710 CLog::Log(LOGERROR, "PVR - %s - invalid group id: %d", __FUNCTION__, group.GroupID());
714 CStdString strQuery = FormatSQL("SELECT idChannel, iChannelNumber FROM map_channelgroups_channels WHERE idGroup = %u ORDER BY iChannelNumber", group.GroupID());
715 if (ResultQuery(strQuery))
721 while (!m_pDS->eof())
723 int iChannelId = m_pDS->fv("idChannel").get_asInt();
724 int iChannelNumber = m_pDS->fv("iChannelNumber").get_asInt();
725 CPVRChannelPtr channel = g_PVRChannelGroups->GetGroupAll(group.IsRadio())->GetByChannelID(iChannelId);
730 CLog::Log(LOGDEBUG, "PVR - %s - channel '%s' loaded from the database", __FUNCTION__, channel->m_strChannelName.c_str());
732 PVRChannelGroupMember newMember = { channel, (unsigned int)iChannelNumber };
733 group.m_members.push_back(newMember);
738 // remove a channel that doesn't exist (anymore) from the table
739 DeleteValues("map_channelgroups_channels", FormatSQL("idGroup = %u AND idChannel = %u", group.GroupID(), iChannelId));
748 CLog::Log(LOGERROR, "PVR - %s - failed to get channels", __FUNCTION__);
753 group.SortByChannelNumber();
758 bool CPVRDatabase::PersistChannels(CPVRChannelGroup &group)
763 /* we can only safely get this from a local db */
765 iLastChannel = GetLastChannelId();
767 for (unsigned int iChannelPtr = 0; iChannelPtr < group.m_members.size(); iChannelPtr++)
769 PVRChannelGroupMember member = group.m_members.at(iChannelPtr);
770 if (member.channel->IsChanged() || member.channel->IsNew())
772 if (m_sqlite && member.channel->IsNew())
773 member.channel->SetChannelID(++iLastChannel);
774 bReturn &= Persist(*member.channel, m_sqlite || !member.channel->IsNew());
778 bReturn &= CommitInsertQueries();
783 bool CPVRDatabase::PersistGroupMembers(CPVRChannelGroup &group)
786 bool bRemoveChannels = true;
788 CSingleLock lock(group.m_critSection);
790 if (group.m_members.size() > 0)
792 for (unsigned int iChannelPtr = 0; iChannelPtr < group.m_members.size(); iChannelPtr++)
794 PVRChannelGroupMember member = group.m_members.at(iChannelPtr);
796 CStdString strWhereClause = FormatSQL("idChannel = %u AND idGroup = %u AND iChannelNumber = %u",
797 member.channel->ChannelID(), group.GroupID(), member.iChannelNumber);
799 CStdString strValue = GetSingleValue("map_channelgroups_channels", "idChannel", strWhereClause);
800 if (strValue.IsEmpty())
802 strQuery = FormatSQL("REPLACE INTO map_channelgroups_channels ("
803 "idGroup, idChannel, iChannelNumber) "
804 "VALUES (%i, %i, %i);",
805 group.GroupID(), member.channel->ChannelID(), member.iChannelNumber);
806 QueueInsertQuery(strQuery);
811 bReturn = CommitInsertQueries();
812 bRemoveChannels = RemoveStaleChannelsFromGroup(group);
815 return bReturn && bRemoveChannels;
818 /********** Client methods **********/
820 bool CPVRDatabase::DeleteClients()
822 CLog::Log(LOGDEBUG, "PVR - %s - deleting all clients from the database", __FUNCTION__);
824 return DeleteValues("clients");
825 //TODO && DeleteValues("map_channels_clients");
828 bool CPVRDatabase::Delete(const CPVRClient &client)
830 /* invalid client uid */
831 if (client.ID().IsEmpty())
833 CLog::Log(LOGERROR, "PVR - %s - invalid client uid", __FUNCTION__);
837 CStdString strWhereClause = FormatSQL("sUid = '%s'", client.ID().c_str());
838 return DeleteValues("clients", strWhereClause);
841 int CPVRDatabase::GetClientId(const CStdString &strClientUid)
843 CStdString strWhereClause = FormatSQL("sUid = '%s'", strClientUid.c_str());
844 CStdString strValue = GetSingleValue("clients", "idClient", strWhereClause);
846 if (strValue.IsEmpty())
849 return atol(strValue.c_str());
852 bool CPVRDatabase::ResetEPG(void)
854 CStdString strQuery = FormatSQL("UPDATE channels SET idEpg = 0");
855 return ExecuteQuery(strQuery);
858 bool CPVRDatabase::Persist(CPVRChannelGroup &group)
861 if (group.GroupName().IsEmpty())
863 CLog::Log(LOGERROR, "%s - empty group name", __FUNCTION__);
870 CSingleLock lock(group.m_critSection);
872 /* insert a new entry when this is a new group, or replace the existing one otherwise */
873 if (group.GroupID() <= 0)
874 strQuery = FormatSQL("INSERT INTO channelgroups (bIsRadio, iGroupType, sName) VALUES (%i, %i, '%s')",
875 (group.IsRadio() ? 1 :0), group.GroupType(), group.GroupName().c_str());
877 strQuery = FormatSQL("REPLACE INTO channelgroups (idGroup, bIsRadio, iGroupType, sName) VALUES (%i, %i, %i, '%s')",
878 group.GroupID(), (group.IsRadio() ? 1 :0), group.GroupType(), group.GroupName().c_str());
880 bReturn = ExecuteQuery(strQuery);
882 /* set the group id if it was <= 0 */
883 if (bReturn && group.GroupID() <= 0)
884 group.m_iGroupId = (int) m_pDS->lastinsertid();
887 /* only persist the channel data for the internal groups */
888 if (group.IsInternalGroup())
889 bReturn &= PersistChannels(group);
891 /* persist the group member entries */
893 bReturn = PersistGroupMembers(group);
898 int CPVRDatabase::Persist(const AddonPtr client)
902 /* invalid client uid or name */
903 if (client->Name().IsEmpty() || client->ID().IsEmpty())
905 CLog::Log(LOGERROR, "PVR - %s - invalid client uid or name", __FUNCTION__);
909 CStdString strQuery = FormatSQL("REPLACE INTO clients (sName, sUid) VALUES ('%s', '%s');",
910 client->Name().c_str(), client->ID().c_str());
912 if (ExecuteQuery(strQuery))
913 iReturn = (int) m_pDS->lastinsertid();
918 bool CPVRDatabase::Persist(CPVRChannel &channel, bool bQueueWrite /* = false */)
922 /* invalid channel */
923 if (channel.UniqueID() <= 0)
925 CLog::Log(LOGERROR, "PVR - %s - invalid channel uid: %d", __FUNCTION__, channel.UniqueID());
930 if (channel.ChannelID() <= 0)
933 strQuery = FormatSQL("INSERT INTO channels ("
934 "iUniqueId, bIsRadio, bIsHidden, bIsUserSetIcon, bIsLocked, "
935 "sIconPath, sChannelName, bIsVirtual, bEPGEnabled, sEPGScraper, iLastWatched, iClientId, "
936 "iClientChannelNumber, sInputFormat, sStreamURL, iEncryptionSystem, idEpg) "
937 "VALUES (%i, %i, %i, %i, %i, '%s', '%s', %i, %i, '%s', %u, %i, %i, '%s', '%s', %i, %i)",
938 channel.UniqueID(), (channel.IsRadio() ? 1 :0), (channel.IsHidden() ? 1 : 0), (channel.IsUserSetIcon() ? 1 : 0), (channel.IsLocked() ? 1 : 0),
939 channel.IconPath().c_str(), channel.ChannelName().c_str(), (channel.IsVirtual() ? 1 : 0), (channel.EPGEnabled() ? 1 : 0), channel.EPGScraper().c_str(), channel.LastWatched(), channel.ClientID(),
940 channel.ClientChannelNumber(), channel.InputFormat().c_str(), channel.StreamURL().c_str(), channel.EncryptionSystem(),
946 strQuery = FormatSQL("REPLACE INTO channels ("
947 "iUniqueId, bIsRadio, bIsHidden, bIsUserSetIcon, bIsLocked, "
948 "sIconPath, sChannelName, bIsVirtual, bEPGEnabled, sEPGScraper, iLastWatched, iClientId, "
949 "iClientChannelNumber, sInputFormat, sStreamURL, iEncryptionSystem, idChannel, idEpg) "
950 "VALUES (%i, %i, %i, %i, %i, '%s', '%s', %i, %i, '%s', %u, %i, %i, '%s', '%s', %i, %i, %i)",
951 channel.UniqueID(), (channel.IsRadio() ? 1 :0), (channel.IsHidden() ? 1 : 0), (channel.IsUserSetIcon() ? 1 : 0), (channel.IsLocked() ? 1 : 0),
952 channel.IconPath().c_str(), channel.ChannelName().c_str(), (channel.IsVirtual() ? 1 : 0), (channel.EPGEnabled() ? 1 : 0), channel.EPGScraper().c_str(), channel.LastWatched(), channel.ClientID(),
953 channel.ClientChannelNumber(), channel.InputFormat().c_str(), channel.StreamURL().c_str(), channel.EncryptionSystem(), channel.ChannelID(),
959 QueueInsertQuery(strQuery);
962 else if (ExecuteQuery(strQuery))
964 CSingleLock lock(channel.m_critSection);
965 if (channel.m_iChannelId <= 0)
966 channel.m_iChannelId = (int)m_pDS->lastinsertid();