[Windows] Fix driver version detection of AMD RDNA+ GPU on Windows 10
[xbmc.git] / xbmc / pvr / epg / EpgDatabase.cpp
blobdbe3a49c3f6e702dac0e7bd466fd0e8da84420c2
1 /*
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.
7 */
9 #include "EpgDatabase.h"
11 #include "ServiceBroker.h"
12 #include "dbwrappers/dataset.h"
13 #include "pvr/epg/Epg.h"
14 #include "pvr/epg/EpgInfoTag.h"
15 #include "pvr/epg/EpgSearchData.h"
16 #include "pvr/epg/EpgSearchFilter.h"
17 #include "settings/AdvancedSettings.h"
18 #include "settings/SettingsComponent.h"
19 #include "utils/StringUtils.h"
20 #include "utils/log.h"
22 #include <memory>
23 #include <mutex>
24 #include <string>
25 #include <vector>
27 using namespace dbiplus;
28 using namespace PVR;
30 bool CPVREpgDatabase::Open()
32 std::unique_lock<CCriticalSection> lock(m_critSection);
33 return CDatabase::Open(CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->m_databaseEpg);
36 void CPVREpgDatabase::Close()
38 std::unique_lock<CCriticalSection> lock(m_critSection);
39 CDatabase::Close();
42 void CPVREpgDatabase::Lock()
44 m_critSection.lock();
47 void CPVREpgDatabase::Unlock()
49 m_critSection.unlock();
52 void CPVREpgDatabase::CreateTables()
54 CLog::Log(LOGINFO, "Creating EPG database tables");
56 CLog::LogFC(LOGDEBUG, LOGEPG, "Creating table 'epg'");
58 std::unique_lock<CCriticalSection> lock(m_critSection);
60 m_pDS->exec(
61 "CREATE TABLE epg ("
62 "idEpg integer primary key, "
63 "sName varchar(64),"
64 "sScraperName varchar(32)"
65 ")"
68 CLog::LogFC(LOGDEBUG, LOGEPG, "Creating table 'epgtags'");
69 m_pDS->exec(
70 "CREATE TABLE epgtags ("
71 "idBroadcast integer primary key, "
72 "iBroadcastUid integer, "
73 "idEpg integer, "
74 "sTitle varchar(128), "
75 "sPlotOutline text, "
76 "sPlot text, "
77 "sOriginalTitle varchar(128), "
78 "sCast varchar(255), "
79 "sDirector varchar(255), "
80 "sWriter varchar(255), "
81 "iYear integer, "
82 "sIMDBNumber varchar(50), "
83 "sIconPath varchar(255), "
84 "iStartTime integer, "
85 "iEndTime integer, "
86 "iGenreType integer, "
87 "iGenreSubType integer, "
88 "sGenre varchar(128), "
89 "sFirstAired varchar(32), "
90 "iParentalRating integer, "
91 "iStarRating integer, "
92 "iSeriesId integer, "
93 "iEpisodeId integer, "
94 "iEpisodePart integer, "
95 "sEpisodeName varchar(128), "
96 "iFlags integer, "
97 "sSeriesLink varchar(255), "
98 "sParentalRatingCode varchar(64)"
99 ")"
102 CLog::LogFC(LOGDEBUG, LOGEPG, "Creating table 'lastepgscan'");
103 m_pDS->exec("CREATE TABLE lastepgscan ("
104 "idEpg integer primary key, "
105 "sLastScan varchar(20)"
109 CLog::LogFC(LOGDEBUG, LOGEPG, "Creating table 'savedsearches'");
110 m_pDS->exec("CREATE TABLE savedsearches ("
111 "idSearch integer primary key,"
112 "sTitle varchar(255), "
113 "sLastExecutedDateTime varchar(20), "
114 "sSearchTerm varchar(255), "
115 "bSearchInDescription bool, "
116 "iGenreType integer, "
117 "sStartDateTime varchar(20), "
118 "sEndDateTime varchar(20), "
119 "bIsCaseSensitive bool, "
120 "iMinimumDuration integer, "
121 "iMaximumDuration integer, "
122 "bIsRadio bool, "
123 "iClientId integer, "
124 "iChannelUid integer, "
125 "bIncludeUnknownGenres bool, "
126 "bRemoveDuplicates bool, "
127 "bIgnoreFinishedBroadcasts bool, "
128 "bIgnoreFutureBroadcasts bool, "
129 "bFreeToAirOnly bool, "
130 "bIgnorePresentTimers bool, "
131 "bIgnorePresentRecordings bool,"
132 "iChannelGroup integer"
133 ")");
136 void CPVREpgDatabase::CreateAnalytics()
138 CLog::LogFC(LOGDEBUG, LOGEPG, "Creating EPG database indices");
140 std::unique_lock<CCriticalSection> lock(m_critSection);
141 m_pDS->exec("CREATE UNIQUE INDEX idx_epg_idEpg_iStartTime on epgtags(idEpg, iStartTime desc);");
142 m_pDS->exec("CREATE INDEX idx_epg_iEndTime on epgtags(iEndTime);");
145 void CPVREpgDatabase::UpdateTables(int iVersion)
147 std::unique_lock<CCriticalSection> lock(m_critSection);
148 if (iVersion < 5)
149 m_pDS->exec("ALTER TABLE epgtags ADD sGenre varchar(128);");
151 if (iVersion < 9)
152 m_pDS->exec("ALTER TABLE epgtags ADD sIconPath varchar(255);");
154 if (iVersion < 10)
156 m_pDS->exec("ALTER TABLE epgtags ADD sOriginalTitle varchar(128);");
157 m_pDS->exec("ALTER TABLE epgtags ADD sCast varchar(255);");
158 m_pDS->exec("ALTER TABLE epgtags ADD sDirector varchar(255);");
159 m_pDS->exec("ALTER TABLE epgtags ADD sWriter varchar(255);");
160 m_pDS->exec("ALTER TABLE epgtags ADD iYear integer;");
161 m_pDS->exec("ALTER TABLE epgtags ADD sIMDBNumber varchar(50);");
164 if (iVersion < 11)
166 m_pDS->exec("ALTER TABLE epgtags ADD iFlags integer;");
169 if (iVersion < 12)
171 m_pDS->exec("ALTER TABLE epgtags ADD sSeriesLink varchar(255);");
174 if (iVersion < 13)
176 const bool isMySQL = StringUtils::EqualsNoCase(
177 CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->m_databaseEpg.type, "mysql");
179 m_pDS->exec(
180 "CREATE TABLE epgtags_new ("
181 "idBroadcast integer primary key, "
182 "iBroadcastUid integer, "
183 "idEpg integer, "
184 "sTitle varchar(128), "
185 "sPlotOutline text, "
186 "sPlot text, "
187 "sOriginalTitle varchar(128), "
188 "sCast varchar(255), "
189 "sDirector varchar(255), "
190 "sWriter varchar(255), "
191 "iYear integer, "
192 "sIMDBNumber varchar(50), "
193 "sIconPath varchar(255), "
194 "iStartTime integer, "
195 "iEndTime integer, "
196 "iGenreType integer, "
197 "iGenreSubType integer, "
198 "sGenre varchar(128), "
199 "sFirstAired varchar(32), "
200 "iParentalRating integer, "
201 "iStarRating integer, "
202 "iSeriesId integer, "
203 "iEpisodeId integer, "
204 "iEpisodePart integer, "
205 "sEpisodeName varchar(128), "
206 "iFlags integer, "
207 "sSeriesLink varchar(255)"
211 m_pDS->exec(
212 "INSERT INTO epgtags_new ("
213 "idBroadcast, "
214 "iBroadcastUid, "
215 "idEpg, "
216 "sTitle, "
217 "sPlotOutline, "
218 "sPlot, "
219 "sOriginalTitle, "
220 "sCast, "
221 "sDirector, "
222 "sWriter, "
223 "iYear, "
224 "sIMDBNumber, "
225 "sIconPath, "
226 "iStartTime, "
227 "iEndTime, "
228 "iGenreType, "
229 "iGenreSubType, "
230 "sGenre, "
231 "sFirstAired, "
232 "iParentalRating, "
233 "iStarRating, "
234 "iSeriesId, "
235 "iEpisodeId, "
236 "iEpisodePart, "
237 "sEpisodeName, "
238 "iFlags, "
239 "sSeriesLink"
240 ") "
241 "SELECT "
242 "idBroadcast, "
243 "iBroadcastUid, "
244 "idEpg, "
245 "sTitle, "
246 "sPlotOutline, "
247 "sPlot, "
248 "sOriginalTitle, "
249 "sCast, "
250 "sDirector, "
251 "sWriter, "
252 "iYear, "
253 "sIMDBNumber, "
254 "sIconPath, "
255 "iStartTime, "
256 "iEndTime, "
257 "iGenreType, "
258 "iGenreSubType, "
259 "sGenre, "
260 "'' AS sFirstAired, "
261 "iParentalRating, "
262 "iStarRating, "
263 "iSeriesId, "
264 "iEpisodeId, "
265 "iEpisodePart, "
266 "sEpisodeName, "
267 "iFlags, "
268 "sSeriesLink "
269 "FROM epgtags"
272 if (isMySQL)
273 m_pDS->exec(
274 "UPDATE epgtags_new INNER JOIN epgtags ON epgtags_new.idBroadcast = epgtags.idBroadcast "
275 "SET epgtags_new.sFirstAired = DATE(FROM_UNIXTIME(epgtags.iFirstAired)) "
276 "WHERE epgtags.iFirstAired > 0"
278 else
279 m_pDS->exec(
280 "UPDATE epgtags_new SET sFirstAired = "
281 "COALESCE((SELECT STRFTIME('%Y-%m-%d', iFirstAired, 'UNIXEPOCH') "
282 "FROM epgtags WHERE epgtags.idBroadcast = epgtags_new.idBroadcast "
283 "AND epgtags.iFirstAired > 0), '')"
286 m_pDS->exec("DROP TABLE epgtags");
287 m_pDS->exec("ALTER TABLE epgtags_new RENAME TO epgtags");
290 if (iVersion < 14)
292 m_pDS->exec("ALTER TABLE epgtags ADD sParentalRatingCode varchar(64);");
295 if (iVersion < 15)
297 m_pDS->exec("CREATE TABLE savedsearches ("
298 "idSearch integer primary key,"
299 "sTitle varchar(255), "
300 "sLastExecutedDateTime varchar(20), "
301 "sSearchTerm varchar(255), "
302 "bSearchInDescription bool, "
303 "iGenreType integer, "
304 "sStartDateTime varchar(20), "
305 "sEndDateTime varchar(20), "
306 "bIsCaseSensitive bool, "
307 "iMinimumDuration integer, "
308 "iMaximumDuration integer, "
309 "bIsRadio bool, "
310 "iClientId integer, "
311 "iChannelUid integer, "
312 "bIncludeUnknownGenres bool, "
313 "bRemoveDuplicates bool, "
314 "bIgnoreFinishedBroadcasts bool, "
315 "bIgnoreFutureBroadcasts bool, "
316 "bFreeToAirOnly bool, "
317 "bIgnorePresentTimers bool, "
318 "bIgnorePresentRecordings bool"
319 ")");
322 if (iVersion < 16)
324 m_pDS->exec("ALTER TABLE savedsearches ADD iChannelGroup integer;");
325 m_pDS->exec("UPDATE savedsearches SET iChannelGroup = -1");
329 bool CPVREpgDatabase::DeleteEpg()
331 bool bReturn(false);
332 CLog::LogFC(LOGDEBUG, LOGEPG, "Deleting all EPG data from the database");
334 std::unique_lock<CCriticalSection> lock(m_critSection);
336 bReturn = DeleteValues("epg") || bReturn;
337 bReturn = DeleteValues("epgtags") || bReturn;
338 bReturn = DeleteValues("lastepgscan") || bReturn;
340 return bReturn;
343 bool CPVREpgDatabase::QueueDeleteEpgQuery(const CPVREpg& table)
345 /* invalid channel */
346 if (table.EpgID() <= 0)
348 CLog::LogF(LOGERROR, "Invalid channel id: {}", table.EpgID());
349 return false;
352 Filter filter;
354 std::unique_lock<CCriticalSection> lock(m_critSection);
355 filter.AppendWhere(PrepareSQL("idEpg = %u", table.EpgID()));
357 std::string strQuery;
358 if (BuildSQL(PrepareSQL("DELETE FROM %s ", "epg"), filter, strQuery))
359 return QueueDeleteQuery(strQuery);
361 return false;
364 bool CPVREpgDatabase::QueueDeleteTagQuery(const CPVREpgInfoTag& tag)
366 /* tag without a database ID was not persisted */
367 if (tag.DatabaseID() <= 0)
368 return false;
370 Filter filter;
372 std::unique_lock<CCriticalSection> lock(m_critSection);
373 filter.AppendWhere(PrepareSQL("idBroadcast = %u", tag.DatabaseID()));
375 std::string strQuery;
376 BuildSQL(PrepareSQL("DELETE FROM %s ", "epgtags"), filter, strQuery);
377 return QueueDeleteQuery(strQuery);
380 std::vector<std::shared_ptr<CPVREpg>> CPVREpgDatabase::GetAll()
382 std::vector<std::shared_ptr<CPVREpg>> result;
384 std::unique_lock<CCriticalSection> lock(m_critSection);
385 std::string strQuery = PrepareSQL("SELECT idEpg, sName, sScraperName FROM epg;");
386 if (ResultQuery(strQuery))
390 while (!m_pDS->eof())
392 int iEpgID = m_pDS->fv("idEpg").get_asInt();
393 std::string strName = m_pDS->fv("sName").get_asString().c_str();
394 std::string strScraperName = m_pDS->fv("sScraperName").get_asString().c_str();
396 result.emplace_back(new CPVREpg(iEpgID, strName, strScraperName, shared_from_this()));
397 m_pDS->next();
399 m_pDS->close();
401 catch (...)
403 CLog::LogF(LOGERROR, "Could not load EPG data from the database");
407 return result;
410 std::shared_ptr<CPVREpgInfoTag> CPVREpgDatabase::CreateEpgTag(
411 const std::unique_ptr<dbiplus::Dataset>& pDS) const
413 if (!pDS->eof())
415 std::shared_ptr<CPVREpgInfoTag> newTag(
416 new CPVREpgInfoTag(m_pDS->fv("idEpg").get_asInt(), m_pDS->fv("sIconPath").get_asString()));
418 time_t iStartTime;
419 iStartTime = static_cast<time_t>(m_pDS->fv("iStartTime").get_asInt());
420 const CDateTime startTime(iStartTime);
421 newTag->m_startTime = startTime;
423 time_t iEndTime = static_cast<time_t>(m_pDS->fv("iEndTime").get_asInt());
424 const CDateTime endTime(iEndTime);
425 newTag->m_endTime = endTime;
427 const std::string sFirstAired = m_pDS->fv("sFirstAired").get_asString();
428 if (sFirstAired.length() > 0)
429 newTag->m_firstAired.SetFromW3CDate(sFirstAired);
431 int iBroadcastUID = m_pDS->fv("iBroadcastUid").get_asInt();
432 // Compat: null value for broadcast uid changed from numerical -1 to 0 with PVR Addon API v4.0.0
433 newTag->m_iUniqueBroadcastID = iBroadcastUID == -1 ? EPG_TAG_INVALID_UID : iBroadcastUID;
435 newTag->m_iDatabaseID = m_pDS->fv("idBroadcast").get_asInt();
436 newTag->m_strTitle = m_pDS->fv("sTitle").get_asString();
437 newTag->m_strPlotOutline = m_pDS->fv("sPlotOutline").get_asString();
438 newTag->m_strPlot = m_pDS->fv("sPlot").get_asString();
439 newTag->m_strOriginalTitle = m_pDS->fv("sOriginalTitle").get_asString();
440 newTag->m_cast = newTag->Tokenize(m_pDS->fv("sCast").get_asString());
441 newTag->m_directors = newTag->Tokenize(m_pDS->fv("sDirector").get_asString());
442 newTag->m_writers = newTag->Tokenize(m_pDS->fv("sWriter").get_asString());
443 newTag->m_iYear = m_pDS->fv("iYear").get_asInt();
444 newTag->m_strIMDBNumber = m_pDS->fv("sIMDBNumber").get_asString();
445 newTag->m_iParentalRating = m_pDS->fv("iParentalRating").get_asInt();
446 newTag->m_iStarRating = m_pDS->fv("iStarRating").get_asInt();
447 newTag->m_iEpisodeNumber = m_pDS->fv("iEpisodeId").get_asInt();
448 newTag->m_iEpisodePart = m_pDS->fv("iEpisodePart").get_asInt();
449 newTag->m_strEpisodeName = m_pDS->fv("sEpisodeName").get_asString();
450 newTag->m_iSeriesNumber = m_pDS->fv("iSeriesId").get_asInt();
451 newTag->m_iFlags = m_pDS->fv("iFlags").get_asInt();
452 newTag->m_strSeriesLink = m_pDS->fv("sSeriesLink").get_asString();
453 newTag->m_strParentalRatingCode = m_pDS->fv("sParentalRatingCode").get_asString();
454 newTag->m_iGenreType = m_pDS->fv("iGenreType").get_asInt();
455 newTag->m_iGenreSubType = m_pDS->fv("iGenreSubType").get_asInt();
456 newTag->m_strGenreDescription = m_pDS->fv("sGenre").get_asString();
458 return newTag;
460 return {};
463 bool CPVREpgDatabase::HasTags(int iEpgID) const
465 std::unique_lock<CCriticalSection> lock(m_critSection);
466 const std::string strQuery =
467 PrepareSQL("SELECT iStartTime FROM epgtags WHERE idEpg = %u LIMIT 1;", iEpgID);
468 std::string strValue = GetSingleValue(strQuery);
469 return !strValue.empty();
472 CDateTime CPVREpgDatabase::GetLastEndTime(int iEpgID) const
474 std::unique_lock<CCriticalSection> lock(m_critSection);
475 const std::string strQuery =
476 PrepareSQL("SELECT MAX(iEndTime) FROM epgtags WHERE idEpg = %u;", iEpgID);
477 std::string strValue = GetSingleValue(strQuery);
478 if (!strValue.empty())
479 return CDateTime(static_cast<time_t>(std::atoi(strValue.c_str())));
481 return {};
484 std::pair<CDateTime, CDateTime> CPVREpgDatabase::GetFirstAndLastEPGDate() const
486 CDateTime first;
487 CDateTime last;
489 std::unique_lock<CCriticalSection> lock(m_critSection);
491 // 1st query: get min start time
492 std::string strQuery = PrepareSQL("SELECT MIN(iStartTime) FROM epgtags;");
494 std::string strValue = GetSingleValue(strQuery);
495 if (!strValue.empty())
496 first = CDateTime(static_cast<time_t>(std::atoi(strValue.c_str())));
498 // 2nd query: get max end time
499 strQuery = PrepareSQL("SELECT MAX(iEndTime) FROM epgtags;");
501 strValue = GetSingleValue(strQuery);
502 if (!strValue.empty())
503 last = CDateTime(static_cast<time_t>(std::atoi(strValue.c_str())));
505 return {first, last};
508 CDateTime CPVREpgDatabase::GetMinStartTime(int iEpgID, const CDateTime& minStart) const
510 time_t t;
511 minStart.GetAsTime(t);
513 std::unique_lock<CCriticalSection> lock(m_critSection);
514 const std::string strQuery = PrepareSQL("SELECT MIN(iStartTime) "
515 "FROM epgtags "
516 "WHERE idEpg = %u AND iStartTime > %u;",
517 iEpgID, static_cast<unsigned int>(t));
518 std::string strValue = GetSingleValue(strQuery);
519 if (!strValue.empty())
520 return CDateTime(static_cast<time_t>(std::atoi(strValue.c_str())));
522 return {};
525 CDateTime CPVREpgDatabase::GetMaxEndTime(int iEpgID, const CDateTime& maxEnd) const
527 time_t t;
528 maxEnd.GetAsTime(t);
530 std::unique_lock<CCriticalSection> lock(m_critSection);
531 const std::string strQuery = PrepareSQL("SELECT MAX(iEndTime) "
532 "FROM epgtags "
533 "WHERE idEpg = %u AND iEndTime <= %u;",
534 iEpgID, static_cast<unsigned int>(t));
535 std::string strValue = GetSingleValue(strQuery);
536 if (!strValue.empty())
537 return CDateTime(static_cast<time_t>(std::atoi(strValue.c_str())));
539 return {};
542 namespace
545 class CSearchTermConverter
547 public:
548 explicit CSearchTermConverter(const std::string& strSearchTerm) { Parse(strSearchTerm); }
550 std::string ToSQL(const std::string& strFieldName) const
552 std::string result = "(";
554 for (auto it = m_fragments.cbegin(); it != m_fragments.cend();)
556 result += (*it);
558 ++it;
559 if (it != m_fragments.cend())
560 result += strFieldName;
563 StringUtils::TrimRight(result);
564 result += ")";
565 return result;
568 private:
569 void Parse(const std::string& strSearchTerm)
571 std::string strParsedSearchTerm(strSearchTerm);
572 StringUtils::Trim(strParsedSearchTerm);
574 std::string strFragment;
576 bool bNextOR = false;
577 while (!strParsedSearchTerm.empty())
579 StringUtils::TrimLeft(strParsedSearchTerm);
581 if (StringUtils::StartsWith(strParsedSearchTerm, "!") ||
582 StringUtils::StartsWithNoCase(strParsedSearchTerm, "not"))
584 std::string strDummy;
585 GetAndCutNextTerm(strParsedSearchTerm, strDummy);
586 strFragment += " NOT ";
587 bNextOR = false;
589 else if (StringUtils::StartsWith(strParsedSearchTerm, "+") ||
590 StringUtils::StartsWithNoCase(strParsedSearchTerm, "and"))
592 std::string strDummy;
593 GetAndCutNextTerm(strParsedSearchTerm, strDummy);
594 strFragment += " AND ";
595 bNextOR = false;
597 else if (StringUtils::StartsWith(strParsedSearchTerm, "|") ||
598 StringUtils::StartsWithNoCase(strParsedSearchTerm, "or"))
600 std::string strDummy;
601 GetAndCutNextTerm(strParsedSearchTerm, strDummy);
602 strFragment += " OR ";
603 bNextOR = false;
605 else
607 std::string strTerm;
608 GetAndCutNextTerm(strParsedSearchTerm, strTerm);
609 if (!strTerm.empty())
611 if (bNextOR && !m_fragments.empty())
612 strFragment += " OR "; // default operator
614 strFragment += "(UPPER(";
616 m_fragments.emplace_back(strFragment);
617 strFragment.clear();
619 strFragment += ") LIKE UPPER('%";
620 StringUtils::Replace(strTerm, "'", "''"); // escape '
621 strFragment += strTerm;
622 strFragment += "%')) ";
624 bNextOR = true;
626 else
628 break;
632 StringUtils::TrimLeft(strParsedSearchTerm);
635 if (!strFragment.empty())
636 m_fragments.emplace_back(strFragment);
639 static void GetAndCutNextTerm(std::string& strSearchTerm, std::string& strNextTerm)
641 std::string strFindNext(" ");
643 if (StringUtils::EndsWith(strSearchTerm, "\""))
645 strSearchTerm.erase(0, 1);
646 strFindNext = "\"";
649 const size_t iNextPos = strSearchTerm.find(strFindNext);
650 if (iNextPos != std::string::npos)
652 strNextTerm = strSearchTerm.substr(0, iNextPos);
653 strSearchTerm.erase(0, iNextPos + 1);
655 else
657 strNextTerm = strSearchTerm;
658 strSearchTerm.clear();
662 std::vector<std::string> m_fragments;
665 } // unnamed namespace
667 std::vector<std::shared_ptr<CPVREpgInfoTag>> CPVREpgDatabase::GetEpgTags(
668 const PVREpgSearchData& searchData) const
670 std::unique_lock<CCriticalSection> lock(m_critSection);
672 std::string strQuery = PrepareSQL("SELECT * FROM epgtags");
674 Filter filter;
676 /////////////////////////////////////////////////////////////////////////////////////////////
677 // min start datetime
678 /////////////////////////////////////////////////////////////////////////////////////////////
680 if (searchData.m_startDateTime.IsValid())
682 time_t minStart;
683 searchData.m_startDateTime.GetAsTime(minStart);
684 filter.AppendWhere(PrepareSQL("iStartTime >= %u", static_cast<unsigned int>(minStart)));
687 /////////////////////////////////////////////////////////////////////////////////////////////
688 // max end datetime
689 /////////////////////////////////////////////////////////////////////////////////////////////
691 if (searchData.m_endDateTime.IsValid())
693 time_t maxEnd;
694 searchData.m_endDateTime.GetAsTime(maxEnd);
695 filter.AppendWhere(PrepareSQL("iEndTime <= %u", static_cast<unsigned int>(maxEnd)));
698 /////////////////////////////////////////////////////////////////////////////////////////////
699 // ignore finished broadcasts
700 /////////////////////////////////////////////////////////////////////////////////////////////
702 if (searchData.m_bIgnoreFinishedBroadcasts)
704 const time_t minEnd = std::time(nullptr); // now
705 filter.AppendWhere(PrepareSQL("iEndTime > %u", static_cast<unsigned int>(minEnd)));
708 /////////////////////////////////////////////////////////////////////////////////////////////
709 // ignore future broadcasts
710 /////////////////////////////////////////////////////////////////////////////////////////////
712 if (searchData.m_bIgnoreFutureBroadcasts)
714 const time_t maxStart = std::time(nullptr); // now
715 filter.AppendWhere(PrepareSQL("iStartTime < %u", static_cast<unsigned int>(maxStart)));
718 /////////////////////////////////////////////////////////////////////////////////////////////
719 // genre type
720 /////////////////////////////////////////////////////////////////////////////////////////////
722 if (searchData.m_iGenreType != EPG_SEARCH_UNSET)
724 if (searchData.m_bIncludeUnknownGenres)
726 // match the exact genre and everything with unknown genre
727 filter.AppendWhere(PrepareSQL("(iGenreType == %u) OR (iGenreType < %u) OR (iGenreType > %u)",
728 searchData.m_iGenreType, EPG_EVENT_CONTENTMASK_MOVIEDRAMA,
729 EPG_EVENT_CONTENTMASK_USERDEFINED));
731 else
733 // match only the exact genre
734 filter.AppendWhere(PrepareSQL("iGenreType == %u", searchData.m_iGenreType));
738 /////////////////////////////////////////////////////////////////////////////////////////////
739 // search term
740 /////////////////////////////////////////////////////////////////////////////////////////////
742 if (!searchData.m_strSearchTerm.empty())
744 const CSearchTermConverter conv(searchData.m_strSearchTerm);
746 // title
747 std::string strWhere = conv.ToSQL("sTitle");
749 // plot outline
750 strWhere += " OR ";
751 strWhere += conv.ToSQL("sPlotOutline");
753 if (searchData.m_bSearchInDescription)
755 // plot
756 strWhere += " OR ";
757 strWhere += conv.ToSQL("sPlot");
760 filter.AppendWhere(strWhere);
763 if (BuildSQL(strQuery, filter, strQuery))
767 if (m_pDS->query(strQuery))
769 std::vector<std::shared_ptr<CPVREpgInfoTag>> tags;
770 while (!m_pDS->eof())
772 tags.emplace_back(CreateEpgTag(m_pDS));
773 m_pDS->next();
775 m_pDS->close();
776 return tags;
779 catch (...)
781 CLog::LogF(LOGERROR, "Could not load tags for given search criteria");
785 return {};
788 std::shared_ptr<CPVREpgInfoTag> CPVREpgDatabase::GetEpgTagByUniqueBroadcastID(
789 int iEpgID, unsigned int iUniqueBroadcastId) const
791 std::unique_lock<CCriticalSection> lock(m_critSection);
792 const std::string strQuery = PrepareSQL("SELECT * "
793 "FROM epgtags "
794 "WHERE idEpg = %u AND iBroadcastUid = %u;",
795 iEpgID, iUniqueBroadcastId);
797 if (ResultQuery(strQuery))
801 std::shared_ptr<CPVREpgInfoTag> tag = CreateEpgTag(m_pDS);
802 m_pDS->close();
803 return tag;
805 catch (...)
807 CLog::LogF(LOGERROR, "Could not load EPG tag with unique broadcast ID ({}) from the database",
808 iUniqueBroadcastId);
812 return {};
815 std::shared_ptr<CPVREpgInfoTag> CPVREpgDatabase::GetEpgTagByDatabaseID(int iEpgID,
816 int iDatabaseId) const
818 std::unique_lock<CCriticalSection> lock(m_critSection);
819 const std::string strQuery = PrepareSQL("SELECT * "
820 "FROM epgtags "
821 "WHERE idEpg = %u AND idBroadcast = %u;",
822 iEpgID, iDatabaseId);
824 if (ResultQuery(strQuery))
828 std::shared_ptr<CPVREpgInfoTag> tag = CreateEpgTag(m_pDS);
829 m_pDS->close();
830 return tag;
832 catch (...)
834 CLog::LogF(LOGERROR, "Could not load EPG tag with database ID ({}) from the database",
835 iDatabaseId);
839 return {};
842 std::shared_ptr<CPVREpgInfoTag> CPVREpgDatabase::GetEpgTagByStartTime(
843 int iEpgID, const CDateTime& startTime) const
845 time_t start;
846 startTime.GetAsTime(start);
848 std::unique_lock<CCriticalSection> lock(m_critSection);
849 const std::string strQuery = PrepareSQL("SELECT * "
850 "FROM epgtags "
851 "WHERE idEpg = %u AND iStartTime = %u;",
852 iEpgID, static_cast<unsigned int>(start));
854 if (ResultQuery(strQuery))
858 std::shared_ptr<CPVREpgInfoTag> tag = CreateEpgTag(m_pDS);
859 m_pDS->close();
860 return tag;
862 catch (...)
864 CLog::LogF(LOGERROR, "Could not load EPG tag with start time ({}) from the database",
865 startTime.GetAsDBDateTime());
869 return {};
872 std::shared_ptr<CPVREpgInfoTag> CPVREpgDatabase::GetEpgTagByMinStartTime(
873 int iEpgID, const CDateTime& minStartTime) const
875 time_t minStart;
876 minStartTime.GetAsTime(minStart);
878 std::unique_lock<CCriticalSection> lock(m_critSection);
879 const std::string strQuery =
880 PrepareSQL("SELECT * "
881 "FROM epgtags "
882 "WHERE idEpg = %u AND iStartTime >= %u ORDER BY iStartTime ASC LIMIT 1;",
883 iEpgID, static_cast<unsigned int>(minStart));
885 if (ResultQuery(strQuery))
889 std::shared_ptr<CPVREpgInfoTag> tag = CreateEpgTag(m_pDS);
890 m_pDS->close();
891 return tag;
893 catch (...)
895 CLog::LogF(LOGERROR, "Could not load tags with min start time ({}) for EPG ({})",
896 minStartTime.GetAsDBDateTime(), iEpgID);
900 return {};
903 std::shared_ptr<CPVREpgInfoTag> CPVREpgDatabase::GetEpgTagByMaxEndTime(
904 int iEpgID, const CDateTime& maxEndTime) const
906 time_t maxEnd;
907 maxEndTime.GetAsTime(maxEnd);
909 std::unique_lock<CCriticalSection> lock(m_critSection);
910 const std::string strQuery =
911 PrepareSQL("SELECT * "
912 "FROM epgtags "
913 "WHERE idEpg = %u AND iEndTime <= %u ORDER BY iStartTime DESC LIMIT 1;",
914 iEpgID, static_cast<unsigned int>(maxEnd));
916 if (ResultQuery(strQuery))
920 std::shared_ptr<CPVREpgInfoTag> tag = CreateEpgTag(m_pDS);
921 m_pDS->close();
922 return tag;
924 catch (...)
926 CLog::LogF(LOGERROR, "Could not load tags with max end time ({}) for EPG ({})",
927 maxEndTime.GetAsDBDateTime(), iEpgID);
931 return {};
934 std::vector<std::shared_ptr<CPVREpgInfoTag>> CPVREpgDatabase::GetEpgTagsByMinStartMaxEndTime(
935 int iEpgID, const CDateTime& minStartTime, const CDateTime& maxEndTime) const
937 time_t minStart;
938 minStartTime.GetAsTime(minStart);
940 time_t maxEnd;
941 maxEndTime.GetAsTime(maxEnd);
943 std::unique_lock<CCriticalSection> lock(m_critSection);
944 const std::string strQuery =
945 PrepareSQL("SELECT * "
946 "FROM epgtags "
947 "WHERE idEpg = %u AND iStartTime >= %u AND iEndTime <= %u ORDER BY iStartTime;",
948 iEpgID, static_cast<unsigned int>(minStart), static_cast<unsigned int>(maxEnd));
950 if (ResultQuery(strQuery))
954 std::vector<std::shared_ptr<CPVREpgInfoTag>> tags;
955 while (!m_pDS->eof())
957 tags.emplace_back(CreateEpgTag(m_pDS));
958 m_pDS->next();
960 m_pDS->close();
961 return tags;
963 catch (...)
965 CLog::LogF(LOGERROR,
966 "Could not load tags with min start time ({}) and max end time ({}) for EPG ({})",
967 minStartTime.GetAsDBDateTime(), maxEndTime.GetAsDBDateTime(), iEpgID);
971 return {};
974 std::vector<std::shared_ptr<CPVREpgInfoTag>> CPVREpgDatabase::GetEpgTagsByMinEndMaxStartTime(
975 int iEpgID, const CDateTime& minEndTime, const CDateTime& maxStartTime) const
977 time_t minEnd;
978 minEndTime.GetAsTime(minEnd);
980 time_t maxStart;
981 maxStartTime.GetAsTime(maxStart);
983 std::unique_lock<CCriticalSection> lock(m_critSection);
984 const std::string strQuery =
985 PrepareSQL("SELECT * "
986 "FROM epgtags "
987 "WHERE idEpg = %u AND iEndTime >= %u AND iStartTime <= %u ORDER BY iStartTime;",
988 iEpgID, static_cast<unsigned int>(minEnd), static_cast<unsigned int>(maxStart));
990 if (ResultQuery(strQuery))
994 std::vector<std::shared_ptr<CPVREpgInfoTag>> tags;
995 while (!m_pDS->eof())
997 tags.emplace_back(CreateEpgTag(m_pDS));
998 m_pDS->next();
1000 m_pDS->close();
1001 return tags;
1003 catch (...)
1005 CLog::LogF(LOGERROR,
1006 "Could not load tags with min end time ({}) and max start time ({}) for EPG ({})",
1007 minEndTime.GetAsDBDateTime(), maxStartTime.GetAsDBDateTime(), iEpgID);
1011 return {};
1014 bool CPVREpgDatabase::QueueDeleteEpgTagsByMinEndMaxStartTimeQuery(int iEpgID,
1015 const CDateTime& minEndTime,
1016 const CDateTime& maxStartTime)
1018 time_t minEnd;
1019 minEndTime.GetAsTime(minEnd);
1021 time_t maxStart;
1022 maxStartTime.GetAsTime(maxStart);
1024 Filter filter;
1026 std::unique_lock<CCriticalSection> lock(m_critSection);
1027 filter.AppendWhere(PrepareSQL("idEpg = %u AND iEndTime >= %u AND iStartTime <= %u", iEpgID,
1028 static_cast<unsigned int>(minEnd),
1029 static_cast<unsigned int>(maxStart)));
1031 std::string strQuery;
1032 if (BuildSQL("DELETE FROM epgtags", filter, strQuery))
1033 return QueueDeleteQuery(strQuery);
1035 return false;
1038 std::vector<std::shared_ptr<CPVREpgInfoTag>> CPVREpgDatabase::GetAllEpgTags(int iEpgID) const
1040 std::unique_lock<CCriticalSection> lock(m_critSection);
1041 const std::string strQuery =
1042 PrepareSQL("SELECT * FROM epgtags WHERE idEpg = %u ORDER BY iStartTime;", iEpgID);
1043 if (ResultQuery(strQuery))
1047 std::vector<std::shared_ptr<CPVREpgInfoTag>> tags;
1048 while (!m_pDS->eof())
1050 tags.emplace_back(CreateEpgTag(m_pDS));
1051 m_pDS->next();
1053 m_pDS->close();
1054 return tags;
1056 catch (...)
1058 CLog::LogF(LOGERROR, "Could not load tags for EPG ({})", iEpgID);
1061 return {};
1064 std::vector<std::string> CPVREpgDatabase::GetAllIconPaths(int iEpgID) const
1066 std::unique_lock<CCriticalSection> lock(m_critSection);
1067 const std::string strQuery =
1068 PrepareSQL("SELECT sIconPath FROM epgtags WHERE idEpg = %u;", iEpgID);
1069 if (ResultQuery(strQuery))
1073 std::vector<std::string> paths;
1074 while (!m_pDS->eof())
1076 paths.emplace_back(m_pDS->fv("sIconPath").get_asString());
1077 m_pDS->next();
1079 m_pDS->close();
1080 return paths;
1082 catch (...)
1084 CLog::LogF(LOGERROR, "Could not load tags for EPG ({})", iEpgID);
1087 return {};
1090 bool CPVREpgDatabase::GetLastEpgScanTime(int iEpgId, CDateTime* lastScan) const
1092 bool bReturn = false;
1094 std::unique_lock<CCriticalSection> lock(m_critSection);
1095 std::string strWhereClause = PrepareSQL("idEpg = %u", iEpgId);
1096 std::string strValue = GetSingleValue("lastepgscan", "sLastScan", strWhereClause);
1098 if (!strValue.empty())
1100 lastScan->SetFromDBDateTime(strValue);
1101 bReturn = true;
1103 else
1105 lastScan->SetValid(false);
1108 return bReturn;
1111 bool CPVREpgDatabase::QueuePersistLastEpgScanTimeQuery(int iEpgId, const CDateTime& lastScanTime)
1113 std::unique_lock<CCriticalSection> lock(m_critSection);
1114 std::string strQuery = PrepareSQL("REPLACE INTO lastepgscan(idEpg, sLastScan) VALUES (%u, '%s');",
1115 iEpgId, lastScanTime.GetAsDBDateTime().c_str());
1117 return QueueInsertQuery(strQuery);
1120 bool CPVREpgDatabase::QueueDeleteLastEpgScanTimeQuery(const CPVREpg& table)
1122 if (table.EpgID() <= 0)
1124 CLog::LogF(LOGERROR, "Invalid EPG id: {}", table.EpgID());
1125 return false;
1128 Filter filter;
1130 std::unique_lock<CCriticalSection> lock(m_critSection);
1131 filter.AppendWhere(PrepareSQL("idEpg = %u", table.EpgID()));
1133 std::string strQuery;
1134 if (BuildSQL(PrepareSQL("DELETE FROM %s ", "lastepgscan"), filter, strQuery))
1135 return QueueDeleteQuery(strQuery);
1137 return false;
1140 int CPVREpgDatabase::Persist(const CPVREpg& epg, bool bQueueWrite)
1142 int iReturn = -1;
1143 std::string strQuery;
1145 std::unique_lock<CCriticalSection> lock(m_critSection);
1146 if (epg.EpgID() > 0)
1147 strQuery = PrepareSQL("REPLACE INTO epg (idEpg, sName, sScraperName) "
1148 "VALUES (%u, '%s', '%s');",
1149 epg.EpgID(), epg.Name().c_str(), epg.ScraperName().c_str());
1150 else
1151 strQuery = PrepareSQL("INSERT INTO epg (sName, sScraperName) "
1152 "VALUES ('%s', '%s');",
1153 epg.Name().c_str(), epg.ScraperName().c_str());
1155 if (bQueueWrite)
1157 if (QueueInsertQuery(strQuery))
1158 iReturn = epg.EpgID() <= 0 ? 0 : epg.EpgID();
1160 else
1162 if (ExecuteQuery(strQuery))
1163 iReturn = epg.EpgID() <= 0 ? static_cast<int>(m_pDS->lastinsertid()) : epg.EpgID();
1166 return iReturn;
1169 bool CPVREpgDatabase::DeleteEpgTags(int iEpgId, const CDateTime& maxEndTime)
1171 time_t iMaxEndTime;
1172 maxEndTime.GetAsTime(iMaxEndTime);
1174 Filter filter;
1176 std::unique_lock<CCriticalSection> lock(m_critSection);
1177 filter.AppendWhere(
1178 PrepareSQL("idEpg = %u AND iEndTime < %u", iEpgId, static_cast<unsigned int>(iMaxEndTime)));
1179 return DeleteValues("epgtags", filter);
1182 bool CPVREpgDatabase::DeleteEpgTags(int iEpgId)
1184 Filter filter;
1186 std::unique_lock<CCriticalSection> lock(m_critSection);
1187 filter.AppendWhere(PrepareSQL("idEpg = %u", iEpgId));
1188 return DeleteValues("epgtags", filter);
1191 bool CPVREpgDatabase::QueueDeleteEpgTags(int iEpgId)
1193 Filter filter;
1195 std::unique_lock<CCriticalSection> lock(m_critSection);
1196 filter.AppendWhere(PrepareSQL("idEpg = %u", iEpgId));
1198 std::string strQuery;
1199 BuildSQL(PrepareSQL("DELETE FROM %s ", "epgtags"), filter, strQuery);
1200 return QueueDeleteQuery(strQuery);
1203 bool CPVREpgDatabase::QueuePersistQuery(const CPVREpgInfoTag& tag)
1205 if (tag.EpgID() <= 0)
1207 CLog::LogF(LOGERROR, "Tag '{}' does not have a valid table", tag.Title());
1208 return false;
1211 time_t iStartTime, iEndTime;
1212 tag.StartAsUTC().GetAsTime(iStartTime);
1213 tag.EndAsUTC().GetAsTime(iEndTime);
1215 std::string sFirstAired;
1216 if (tag.FirstAired().IsValid())
1217 sFirstAired = tag.FirstAired().GetAsW3CDate();
1219 int iBroadcastId = tag.DatabaseID();
1220 std::string strQuery;
1222 std::unique_lock<CCriticalSection> lock(m_critSection);
1224 if (iBroadcastId < 0)
1226 strQuery = PrepareSQL(
1227 "REPLACE INTO epgtags (idEpg, iStartTime, "
1228 "iEndTime, sTitle, sPlotOutline, sPlot, sOriginalTitle, sCast, sDirector, sWriter, iYear, "
1229 "sIMDBNumber, "
1230 "sIconPath, iGenreType, iGenreSubType, sGenre, sFirstAired, iParentalRating, iStarRating, "
1231 "iSeriesId, "
1232 "iEpisodeId, iEpisodePart, sEpisodeName, iFlags, sSeriesLink, sParentalRatingCode, "
1233 "iBroadcastUid) "
1234 "VALUES (%u, %u, %u, '%s', '%s', '%s', '%s', '%s', '%s', '%s', %i, '%s', '%s', %i, %i, "
1235 "'%s', '%s', %i, %i, %i, %i, %i, '%s', %i, '%s', '%s', %i);",
1236 tag.EpgID(), static_cast<unsigned int>(iStartTime), static_cast<unsigned int>(iEndTime),
1237 tag.Title().c_str(), tag.PlotOutline().c_str(), tag.Plot().c_str(),
1238 tag.OriginalTitle().c_str(), tag.DeTokenize(tag.Cast()).c_str(),
1239 tag.DeTokenize(tag.Directors()).c_str(), tag.DeTokenize(tag.Writers()).c_str(), tag.Year(),
1240 tag.IMDBNumber().c_str(), tag.ClientIconPath().c_str(), tag.GenreType(), tag.GenreSubType(),
1241 tag.GenreDescription().c_str(), sFirstAired.c_str(), tag.ParentalRating(), tag.StarRating(),
1242 tag.SeriesNumber(), tag.EpisodeNumber(), tag.EpisodePart(), tag.EpisodeName().c_str(),
1243 tag.Flags(), tag.SeriesLink().c_str(), tag.ParentalRatingCode().c_str(),
1244 tag.UniqueBroadcastID());
1246 else
1248 strQuery = PrepareSQL(
1249 "REPLACE INTO epgtags (idEpg, iStartTime, "
1250 "iEndTime, sTitle, sPlotOutline, sPlot, sOriginalTitle, sCast, sDirector, sWriter, iYear, "
1251 "sIMDBNumber, "
1252 "sIconPath, iGenreType, iGenreSubType, sGenre, sFirstAired, iParentalRating, iStarRating, "
1253 "iSeriesId, "
1254 "iEpisodeId, iEpisodePart, sEpisodeName, iFlags, sSeriesLink, sParentalRatingCode, "
1255 "iBroadcastUid, idBroadcast) "
1256 "VALUES (%u, %u, %u, '%s', '%s', '%s', '%s', '%s', '%s', '%s', %i, '%s', '%s', %i, %i, "
1257 "'%s', '%s', %i, %i, %i, %i, %i, '%s', %i, '%s', '%s', %i, %i);",
1258 tag.EpgID(), static_cast<unsigned int>(iStartTime), static_cast<unsigned int>(iEndTime),
1259 tag.Title().c_str(), tag.PlotOutline().c_str(), tag.Plot().c_str(),
1260 tag.OriginalTitle().c_str(), tag.DeTokenize(tag.Cast()).c_str(),
1261 tag.DeTokenize(tag.Directors()).c_str(), tag.DeTokenize(tag.Writers()).c_str(), tag.Year(),
1262 tag.IMDBNumber().c_str(), tag.ClientIconPath().c_str(), tag.GenreType(), tag.GenreSubType(),
1263 tag.GenreDescription().c_str(), sFirstAired.c_str(), tag.ParentalRating(), tag.StarRating(),
1264 tag.SeriesNumber(), tag.EpisodeNumber(), tag.EpisodePart(), tag.EpisodeName().c_str(),
1265 tag.Flags(), tag.SeriesLink().c_str(), tag.ParentalRatingCode().c_str(),
1266 tag.UniqueBroadcastID(), iBroadcastId);
1269 QueueInsertQuery(strQuery);
1270 return true;
1273 int CPVREpgDatabase::GetLastEPGId() const
1275 std::unique_lock<CCriticalSection> lock(m_critSection);
1276 std::string strQuery = PrepareSQL("SELECT MAX(idEpg) FROM epg");
1277 std::string strValue = GetSingleValue(strQuery);
1278 if (!strValue.empty())
1279 return std::atoi(strValue.c_str());
1280 return 0;
1283 /********** Saved searches methods **********/
1285 std::shared_ptr<CPVREpgSearchFilter> CPVREpgDatabase::CreateEpgSearchFilter(
1286 bool bRadio, const std::unique_ptr<dbiplus::Dataset>& pDS) const
1288 if (!pDS->eof())
1290 auto newSearch = std::make_shared<CPVREpgSearchFilter>(bRadio);
1292 newSearch->SetDatabaseId(m_pDS->fv("idSearch").get_asInt());
1293 newSearch->SetTitle(m_pDS->fv("sTitle").get_asString());
1295 const std::string lastExec = m_pDS->fv("sLastExecutedDateTime").get_asString();
1296 if (!lastExec.empty())
1297 newSearch->SetLastExecutedDateTime(CDateTime::FromDBDateTime(lastExec));
1299 newSearch->SetSearchTerm(m_pDS->fv("sSearchTerm").get_asString());
1300 newSearch->SetSearchInDescription(m_pDS->fv("bSearchInDescription").get_asBool());
1301 newSearch->SetGenreType(m_pDS->fv("iGenreType").get_asInt());
1303 const std::string start = m_pDS->fv("sStartDateTime").get_asString();
1304 if (!start.empty())
1305 newSearch->SetStartDateTime(CDateTime::FromDBDateTime(start));
1307 const std::string end = m_pDS->fv("sEndDateTime").get_asString();
1308 if (!end.empty())
1309 newSearch->SetEndDateTime(CDateTime::FromDBDateTime(end));
1311 newSearch->SetCaseSensitive(m_pDS->fv("bIsCaseSensitive").get_asBool());
1312 newSearch->SetMinimumDuration(m_pDS->fv("iMinimumDuration").get_asInt());
1313 newSearch->SetMaximumDuration(m_pDS->fv("iMaximumDuration").get_asInt());
1314 newSearch->SetClientID(m_pDS->fv("iClientId").get_asInt());
1315 newSearch->SetChannelUID(m_pDS->fv("iChannelUid").get_asInt());
1316 newSearch->SetIncludeUnknownGenres(m_pDS->fv("bIncludeUnknownGenres").get_asBool());
1317 newSearch->SetRemoveDuplicates(m_pDS->fv("bRemoveDuplicates").get_asBool());
1318 newSearch->SetIgnoreFinishedBroadcasts(m_pDS->fv("bIgnoreFinishedBroadcasts").get_asBool());
1319 newSearch->SetIgnoreFutureBroadcasts(m_pDS->fv("bIgnoreFutureBroadcasts").get_asBool());
1320 newSearch->SetFreeToAirOnly(m_pDS->fv("bFreeToAirOnly").get_asBool());
1321 newSearch->SetIgnorePresentTimers(m_pDS->fv("bIgnorePresentTimers").get_asBool());
1322 newSearch->SetIgnorePresentRecordings(m_pDS->fv("bIgnorePresentRecordings").get_asBool());
1323 newSearch->SetChannelGroupID(m_pDS->fv("iChannelGroup").get_asInt());
1325 newSearch->SetChanged(false);
1327 return newSearch;
1329 return {};
1332 std::vector<std::shared_ptr<CPVREpgSearchFilter>> CPVREpgDatabase::GetSavedSearches(
1333 bool bRadio) const
1335 std::vector<std::shared_ptr<CPVREpgSearchFilter>> result;
1337 std::unique_lock<CCriticalSection> lock(m_critSection);
1338 const std::string strQuery =
1339 PrepareSQL("SELECT * FROM savedsearches WHERE bIsRadio = %u", bRadio);
1340 if (ResultQuery(strQuery))
1344 while (!m_pDS->eof())
1346 result.emplace_back(CreateEpgSearchFilter(bRadio, m_pDS));
1347 m_pDS->next();
1349 m_pDS->close();
1351 catch (...)
1353 CLog::LogF(LOGERROR, "Could not load EPG search data from the database");
1356 return result;
1359 std::shared_ptr<CPVREpgSearchFilter> CPVREpgDatabase::GetSavedSearchById(bool bRadio, int iId) const
1361 std::unique_lock<CCriticalSection> lock(m_critSection);
1362 const std::string strQuery =
1363 PrepareSQL("SELECT * FROM savedsearches WHERE bIsRadio = %u AND idSearch = %u;", bRadio, iId);
1365 if (ResultQuery(strQuery))
1369 std::shared_ptr<CPVREpgSearchFilter> filter = CreateEpgSearchFilter(bRadio, m_pDS);
1370 m_pDS->close();
1371 return filter;
1373 catch (...)
1375 CLog::LogF(LOGERROR, "Could not load EPG search filter with id ({}) from the database", iId);
1379 return {};
1382 bool CPVREpgDatabase::Persist(CPVREpgSearchFilter& epgSearch)
1384 std::unique_lock<CCriticalSection> lock(m_critSection);
1386 // Insert a new entry if this is a new search, replace the existing otherwise
1387 std::string strQuery;
1388 if (epgSearch.GetDatabaseId() == -1)
1389 strQuery = PrepareSQL(
1390 "INSERT INTO savedsearches "
1391 "(sTitle, sLastExecutedDateTime, sSearchTerm, bSearchInDescription, bIsCaseSensitive, "
1392 "iGenreType, bIncludeUnknownGenres, sStartDateTime, sEndDateTime, iMinimumDuration, "
1393 "iMaximumDuration, bIsRadio, iClientId, iChannelUid, bRemoveDuplicates, "
1394 "bIgnoreFinishedBroadcasts, bIgnoreFutureBroadcasts, bFreeToAirOnly, bIgnorePresentTimers, "
1395 "bIgnorePresentRecordings, iChannelGroup) "
1396 "VALUES ('%s', '%s', '%s', %i, %i, %i, %i, '%s', '%s', %i, %i, %i, %i, %i, %i, %i, %i, "
1397 "%i, %i, %i, %i);",
1398 epgSearch.GetTitle().c_str(),
1399 epgSearch.GetLastExecutedDateTime().IsValid()
1400 ? epgSearch.GetLastExecutedDateTime().GetAsDBDateTime().c_str()
1401 : "",
1402 epgSearch.GetSearchTerm().c_str(), epgSearch.ShouldSearchInDescription() ? 1 : 0,
1403 epgSearch.IsCaseSensitive() ? 1 : 0, epgSearch.GetGenreType(),
1404 epgSearch.ShouldIncludeUnknownGenres() ? 1 : 0,
1405 epgSearch.GetStartDateTime().IsValid()
1406 ? epgSearch.GetStartDateTime().GetAsDBDateTime().c_str()
1407 : "",
1408 epgSearch.GetEndDateTime().IsValid() ? epgSearch.GetEndDateTime().GetAsDBDateTime().c_str()
1409 : "",
1410 epgSearch.GetMinimumDuration(), epgSearch.GetMaximumDuration(), epgSearch.IsRadio() ? 1 : 0,
1411 epgSearch.GetClientID(), epgSearch.GetChannelUID(),
1412 epgSearch.ShouldRemoveDuplicates() ? 1 : 0,
1413 epgSearch.ShouldIgnoreFinishedBroadcasts() ? 1 : 0,
1414 epgSearch.ShouldIgnoreFutureBroadcasts() ? 1 : 0, epgSearch.IsFreeToAirOnly() ? 1 : 0,
1415 epgSearch.ShouldIgnorePresentTimers() ? 1 : 0,
1416 epgSearch.ShouldIgnorePresentRecordings() ? 1 : 0, epgSearch.GetChannelGroupID());
1417 else
1418 strQuery = PrepareSQL(
1419 "REPLACE INTO savedsearches "
1420 "(idSearch, sTitle, sLastExecutedDateTime, sSearchTerm, bSearchInDescription, "
1421 "bIsCaseSensitive, iGenreType, bIncludeUnknownGenres, sStartDateTime, sEndDateTime, "
1422 "iMinimumDuration, iMaximumDuration, bIsRadio, iClientId, iChannelUid, bRemoveDuplicates, "
1423 "bIgnoreFinishedBroadcasts, bIgnoreFutureBroadcasts, bFreeToAirOnly, bIgnorePresentTimers, "
1424 "bIgnorePresentRecordings, iChannelGroup) "
1425 "VALUES (%i, '%s', '%s', '%s', %i, %i, %i, %i, '%s', '%s', %i, %i, %i, %i, %i, %i, %i, %i, "
1426 "%i, %i, %i, %i);",
1427 epgSearch.GetDatabaseId(), epgSearch.GetTitle().c_str(),
1428 epgSearch.GetLastExecutedDateTime().IsValid()
1429 ? epgSearch.GetLastExecutedDateTime().GetAsDBDateTime().c_str()
1430 : "",
1431 epgSearch.GetSearchTerm().c_str(), epgSearch.ShouldSearchInDescription() ? 1 : 0,
1432 epgSearch.IsCaseSensitive() ? 1 : 0, epgSearch.GetGenreType(),
1433 epgSearch.ShouldIncludeUnknownGenres() ? 1 : 0,
1434 epgSearch.GetStartDateTime().IsValid()
1435 ? epgSearch.GetStartDateTime().GetAsDBDateTime().c_str()
1436 : "",
1437 epgSearch.GetEndDateTime().IsValid() ? epgSearch.GetEndDateTime().GetAsDBDateTime().c_str()
1438 : "",
1439 epgSearch.GetMinimumDuration(), epgSearch.GetMaximumDuration(), epgSearch.IsRadio() ? 1 : 0,
1440 epgSearch.GetClientID(), epgSearch.GetChannelUID(),
1441 epgSearch.ShouldRemoveDuplicates() ? 1 : 0,
1442 epgSearch.ShouldIgnoreFinishedBroadcasts() ? 1 : 0,
1443 epgSearch.ShouldIgnoreFutureBroadcasts() ? 1 : 0, epgSearch.IsFreeToAirOnly() ? 1 : 0,
1444 epgSearch.ShouldIgnorePresentTimers() ? 1 : 0,
1445 epgSearch.ShouldIgnorePresentRecordings() ? 1 : 0, epgSearch.GetChannelGroupID());
1447 bool bReturn = ExecuteQuery(strQuery);
1449 if (bReturn)
1451 // Set the database id for searches persisted for the first time
1452 if (epgSearch.GetDatabaseId() == -1)
1453 epgSearch.SetDatabaseId(static_cast<int>(m_pDS->lastinsertid()));
1455 epgSearch.SetChanged(false);
1458 return bReturn;
1461 bool CPVREpgDatabase::UpdateSavedSearchLastExecuted(const CPVREpgSearchFilter& epgSearch)
1463 if (epgSearch.GetDatabaseId() == -1)
1464 return false;
1466 std::unique_lock<CCriticalSection> lock(m_critSection);
1468 const std::string strQuery = PrepareSQL(
1469 "UPDATE savedsearches SET sLastExecutedDateTime = '%s' WHERE idSearch = %i",
1470 epgSearch.GetLastExecutedDateTime().GetAsDBDateTime().c_str(), epgSearch.GetDatabaseId());
1471 return ExecuteQuery(strQuery);
1474 bool CPVREpgDatabase::Delete(const CPVREpgSearchFilter& epgSearch)
1476 if (epgSearch.GetDatabaseId() == -1)
1477 return false;
1479 CLog::LogFC(LOGDEBUG, LOGEPG, "Deleting saved search '{}' from the database",
1480 epgSearch.GetTitle());
1482 std::unique_lock<CCriticalSection> lock(m_critSection);
1484 Filter filter;
1485 filter.AppendWhere(PrepareSQL("idSearch = '%i'", epgSearch.GetDatabaseId()));
1487 return DeleteValues("savedsearches", filter);
1490 bool CPVREpgDatabase::DeleteSavedSearches()
1492 CLog::LogFC(LOGDEBUG, LOGEPG, "Deleting all saved searches from the database");
1494 std::unique_lock<CCriticalSection> lock(m_critSection);
1495 return DeleteValues("savedsearches");