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 "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"
27 using namespace dbiplus
;
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
);
42 void CPVREpgDatabase::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
);
62 "idEpg integer primary key, "
64 "sScraperName varchar(32)"
68 CLog::LogFC(LOGDEBUG
, LOGEPG
, "Creating table 'epgtags'");
70 "CREATE TABLE epgtags ("
71 "idBroadcast integer primary key, "
72 "iBroadcastUid integer, "
74 "sTitle varchar(128), "
77 "sOriginalTitle varchar(128), "
78 "sCast varchar(255), "
79 "sDirector varchar(255), "
80 "sWriter varchar(255), "
82 "sIMDBNumber varchar(50), "
83 "sIconPath varchar(255), "
84 "iStartTime integer, "
86 "iGenreType integer, "
87 "iGenreSubType integer, "
88 "sGenre varchar(128), "
89 "sFirstAired varchar(32), "
90 "iParentalRating integer, "
91 "iStarRating integer, "
93 "iEpisodeId integer, "
94 "iEpisodePart integer, "
95 "sEpisodeName varchar(128), "
97 "sSeriesLink varchar(255), "
98 "sParentalRatingCode varchar(64)"
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, "
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"
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
);
149 m_pDS
->exec("ALTER TABLE epgtags ADD sGenre varchar(128);");
152 m_pDS
->exec("ALTER TABLE epgtags ADD sIconPath varchar(255);");
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);");
166 m_pDS
->exec("ALTER TABLE epgtags ADD iFlags integer;");
171 m_pDS
->exec("ALTER TABLE epgtags ADD sSeriesLink varchar(255);");
176 const bool isMySQL
= StringUtils::EqualsNoCase(
177 CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->m_databaseEpg
.type
, "mysql");
180 "CREATE TABLE epgtags_new ("
181 "idBroadcast integer primary key, "
182 "iBroadcastUid integer, "
184 "sTitle varchar(128), "
185 "sPlotOutline text, "
187 "sOriginalTitle varchar(128), "
188 "sCast varchar(255), "
189 "sDirector varchar(255), "
190 "sWriter varchar(255), "
192 "sIMDBNumber varchar(50), "
193 "sIconPath varchar(255), "
194 "iStartTime 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), "
207 "sSeriesLink varchar(255)"
212 "INSERT INTO epgtags_new ("
260 "'' AS sFirstAired, "
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"
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");
292 m_pDS
->exec("ALTER TABLE epgtags ADD sParentalRatingCode varchar(64);");
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, "
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"
324 m_pDS
->exec("ALTER TABLE savedsearches ADD iChannelGroup integer;");
325 m_pDS
->exec("UPDATE savedsearches SET iChannelGroup = -1");
329 bool CPVREpgDatabase::DeleteEpg()
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
;
343 bool CPVREpgDatabase::QueueDeleteEpgQuery(const CPVREpg
& table
)
345 /* invalid channel */
346 if (table
.EpgID() <= 0)
348 CLog::LogF(LOGERROR
, "Invalid channel id: {}", table
.EpgID());
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
);
364 bool CPVREpgDatabase::QueueDeleteTagQuery(const CPVREpgInfoTag
& tag
)
366 /* tag without a database ID was not persisted */
367 if (tag
.DatabaseID() <= 0)
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()));
403 CLog::LogF(LOGERROR
, "Could not load EPG data from the database");
410 std::shared_ptr
<CPVREpgInfoTag
> CPVREpgDatabase::CreateEpgTag(
411 const std::unique_ptr
<dbiplus::Dataset
>& pDS
) const
415 std::shared_ptr
<CPVREpgInfoTag
> newTag(
416 new CPVREpgInfoTag(m_pDS
->fv("idEpg").get_asInt(), m_pDS
->fv("sIconPath").get_asString()));
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();
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())));
484 std::pair
<CDateTime
, CDateTime
> CPVREpgDatabase::GetFirstAndLastEPGDate() const
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
511 minStart
.GetAsTime(t
);
513 std::unique_lock
<CCriticalSection
> lock(m_critSection
);
514 const std::string strQuery
= PrepareSQL("SELECT MIN(iStartTime) "
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())));
525 CDateTime
CPVREpgDatabase::GetMaxEndTime(int iEpgID
, const CDateTime
& maxEnd
) const
530 std::unique_lock
<CCriticalSection
> lock(m_critSection
);
531 const std::string strQuery
= PrepareSQL("SELECT MAX(iEndTime) "
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())));
545 class CSearchTermConverter
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();)
559 if (it
!= m_fragments
.cend())
560 result
+= strFieldName
;
563 StringUtils::TrimRight(result
);
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 ";
589 else if (StringUtils::StartsWith(strParsedSearchTerm
, "+") ||
590 StringUtils::StartsWithNoCase(strParsedSearchTerm
, "and"))
592 std::string strDummy
;
593 GetAndCutNextTerm(strParsedSearchTerm
, strDummy
);
594 strFragment
+= " AND ";
597 else if (StringUtils::StartsWith(strParsedSearchTerm
, "|") ||
598 StringUtils::StartsWithNoCase(strParsedSearchTerm
, "or"))
600 std::string strDummy
;
601 GetAndCutNextTerm(strParsedSearchTerm
, strDummy
);
602 strFragment
+= " OR ";
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
);
619 strFragment
+= ") LIKE UPPER('%";
620 StringUtils::Replace(strTerm
, "'", "''"); // escape '
621 strFragment
+= strTerm
;
622 strFragment
+= "%')) ";
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);
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);
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");
676 /////////////////////////////////////////////////////////////////////////////////////////////
677 // min start datetime
678 /////////////////////////////////////////////////////////////////////////////////////////////
680 if (searchData
.m_startDateTime
.IsValid())
683 searchData
.m_startDateTime
.GetAsTime(minStart
);
684 filter
.AppendWhere(PrepareSQL("iStartTime >= %u", static_cast<unsigned int>(minStart
)));
687 /////////////////////////////////////////////////////////////////////////////////////////////
689 /////////////////////////////////////////////////////////////////////////////////////////////
691 if (searchData
.m_endDateTime
.IsValid())
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 /////////////////////////////////////////////////////////////////////////////////////////////
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
));
733 // match only the exact genre
734 filter
.AppendWhere(PrepareSQL("iGenreType == %u", searchData
.m_iGenreType
));
738 /////////////////////////////////////////////////////////////////////////////////////////////
740 /////////////////////////////////////////////////////////////////////////////////////////////
742 if (!searchData
.m_strSearchTerm
.empty())
744 const CSearchTermConverter
conv(searchData
.m_strSearchTerm
);
747 std::string strWhere
= conv
.ToSQL("sTitle");
751 strWhere
+= conv
.ToSQL("sPlotOutline");
753 if (searchData
.m_bSearchInDescription
)
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
));
781 CLog::LogF(LOGERROR
, "Could not load tags for given search criteria");
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 * "
794 "WHERE idEpg = %u AND iBroadcastUid = %u;",
795 iEpgID
, iUniqueBroadcastId
);
797 if (ResultQuery(strQuery
))
801 std::shared_ptr
<CPVREpgInfoTag
> tag
= CreateEpgTag(m_pDS
);
807 CLog::LogF(LOGERROR
, "Could not load EPG tag with unique broadcast ID ({}) from the database",
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 * "
821 "WHERE idEpg = %u AND idBroadcast = %u;",
822 iEpgID
, iDatabaseId
);
824 if (ResultQuery(strQuery
))
828 std::shared_ptr
<CPVREpgInfoTag
> tag
= CreateEpgTag(m_pDS
);
834 CLog::LogF(LOGERROR
, "Could not load EPG tag with database ID ({}) from the database",
842 std::shared_ptr
<CPVREpgInfoTag
> CPVREpgDatabase::GetEpgTagByStartTime(
843 int iEpgID
, const CDateTime
& startTime
) const
846 startTime
.GetAsTime(start
);
848 std::unique_lock
<CCriticalSection
> lock(m_critSection
);
849 const std::string strQuery
= PrepareSQL("SELECT * "
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
);
864 CLog::LogF(LOGERROR
, "Could not load EPG tag with start time ({}) from the database",
865 startTime
.GetAsDBDateTime());
872 std::shared_ptr
<CPVREpgInfoTag
> CPVREpgDatabase::GetEpgTagByMinStartTime(
873 int iEpgID
, const CDateTime
& minStartTime
) const
876 minStartTime
.GetAsTime(minStart
);
878 std::unique_lock
<CCriticalSection
> lock(m_critSection
);
879 const std::string strQuery
=
880 PrepareSQL("SELECT * "
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
);
895 CLog::LogF(LOGERROR
, "Could not load tags with min start time ({}) for EPG ({})",
896 minStartTime
.GetAsDBDateTime(), iEpgID
);
903 std::shared_ptr
<CPVREpgInfoTag
> CPVREpgDatabase::GetEpgTagByMaxEndTime(
904 int iEpgID
, const CDateTime
& maxEndTime
) const
907 maxEndTime
.GetAsTime(maxEnd
);
909 std::unique_lock
<CCriticalSection
> lock(m_critSection
);
910 const std::string strQuery
=
911 PrepareSQL("SELECT * "
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
);
926 CLog::LogF(LOGERROR
, "Could not load tags with max end time ({}) for EPG ({})",
927 maxEndTime
.GetAsDBDateTime(), iEpgID
);
934 std::vector
<std::shared_ptr
<CPVREpgInfoTag
>> CPVREpgDatabase::GetEpgTagsByMinStartMaxEndTime(
935 int iEpgID
, const CDateTime
& minStartTime
, const CDateTime
& maxEndTime
) const
938 minStartTime
.GetAsTime(minStart
);
941 maxEndTime
.GetAsTime(maxEnd
);
943 std::unique_lock
<CCriticalSection
> lock(m_critSection
);
944 const std::string strQuery
=
945 PrepareSQL("SELECT * "
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
));
966 "Could not load tags with min start time ({}) and max end time ({}) for EPG ({})",
967 minStartTime
.GetAsDBDateTime(), maxEndTime
.GetAsDBDateTime(), iEpgID
);
974 std::vector
<std::shared_ptr
<CPVREpgInfoTag
>> CPVREpgDatabase::GetEpgTagsByMinEndMaxStartTime(
975 int iEpgID
, const CDateTime
& minEndTime
, const CDateTime
& maxStartTime
) const
978 minEndTime
.GetAsTime(minEnd
);
981 maxStartTime
.GetAsTime(maxStart
);
983 std::unique_lock
<CCriticalSection
> lock(m_critSection
);
984 const std::string strQuery
=
985 PrepareSQL("SELECT * "
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
));
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
);
1014 bool CPVREpgDatabase::QueueDeleteEpgTagsByMinEndMaxStartTimeQuery(int iEpgID
,
1015 const CDateTime
& minEndTime
,
1016 const CDateTime
& maxStartTime
)
1019 minEndTime
.GetAsTime(minEnd
);
1022 maxStartTime
.GetAsTime(maxStart
);
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
);
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
));
1058 CLog::LogF(LOGERROR
, "Could not load tags for EPG ({})", iEpgID
);
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());
1084 CLog::LogF(LOGERROR
, "Could not load tags for EPG ({})", iEpgID
);
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
);
1105 lastScan
->SetValid(false);
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());
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
);
1140 int CPVREpgDatabase::Persist(const CPVREpg
& epg
, bool bQueueWrite
)
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());
1151 strQuery
= PrepareSQL("INSERT INTO epg (sName, sScraperName) "
1152 "VALUES ('%s', '%s');",
1153 epg
.Name().c_str(), epg
.ScraperName().c_str());
1157 if (QueueInsertQuery(strQuery
))
1158 iReturn
= epg
.EpgID() <= 0 ? 0 : epg
.EpgID();
1162 if (ExecuteQuery(strQuery
))
1163 iReturn
= epg
.EpgID() <= 0 ? static_cast<int>(m_pDS
->lastinsertid()) : epg
.EpgID();
1169 bool CPVREpgDatabase::DeleteEpgTags(int iEpgId
, const CDateTime
& maxEndTime
)
1172 maxEndTime
.GetAsTime(iMaxEndTime
);
1176 std::unique_lock
<CCriticalSection
> lock(m_critSection
);
1178 PrepareSQL("idEpg = %u AND iEndTime < %u", iEpgId
, static_cast<unsigned int>(iMaxEndTime
)));
1179 return DeleteValues("epgtags", filter
);
1182 bool CPVREpgDatabase::DeleteEpgTags(int iEpgId
)
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
)
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());
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, "
1230 "sIconPath, iGenreType, iGenreSubType, sGenre, sFirstAired, iParentalRating, iStarRating, "
1232 "iEpisodeId, iEpisodePart, sEpisodeName, iFlags, sSeriesLink, sParentalRatingCode, "
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());
1248 strQuery
= PrepareSQL(
1249 "REPLACE INTO epgtags (idEpg, iStartTime, "
1250 "iEndTime, sTitle, sPlotOutline, sPlot, sOriginalTitle, sCast, sDirector, sWriter, iYear, "
1252 "sIconPath, iGenreType, iGenreSubType, sGenre, sFirstAired, iParentalRating, iStarRating, "
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
);
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());
1283 /********** Saved searches methods **********/
1285 std::shared_ptr
<CPVREpgSearchFilter
> CPVREpgDatabase::CreateEpgSearchFilter(
1286 bool bRadio
, const std::unique_ptr
<dbiplus::Dataset
>& pDS
) const
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();
1305 newSearch
->SetStartDateTime(CDateTime::FromDBDateTime(start
));
1307 const std::string end
= m_pDS
->fv("sEndDateTime").get_asString();
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);
1332 std::vector
<std::shared_ptr
<CPVREpgSearchFilter
>> CPVREpgDatabase::GetSavedSearches(
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
));
1353 CLog::LogF(LOGERROR
, "Could not load EPG search data from the database");
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
);
1375 CLog::LogF(LOGERROR
, "Could not load EPG search filter with id ({}) from the database", iId
);
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, "
1398 epgSearch
.GetTitle().c_str(),
1399 epgSearch
.GetLastExecutedDateTime().IsValid()
1400 ? epgSearch
.GetLastExecutedDateTime().GetAsDBDateTime().c_str()
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()
1408 epgSearch
.GetEndDateTime().IsValid() ? epgSearch
.GetEndDateTime().GetAsDBDateTime().c_str()
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());
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, "
1427 epgSearch
.GetDatabaseId(), epgSearch
.GetTitle().c_str(),
1428 epgSearch
.GetLastExecutedDateTime().IsValid()
1429 ? epgSearch
.GetLastExecutedDateTime().GetAsDBDateTime().c_str()
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()
1437 epgSearch
.GetEndDateTime().IsValid() ? epgSearch
.GetEndDateTime().GetAsDBDateTime().c_str()
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
);
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);
1461 bool CPVREpgDatabase::UpdateSavedSearchLastExecuted(const CPVREpgSearchFilter
& epgSearch
)
1463 if (epgSearch
.GetDatabaseId() == -1)
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)
1479 CLog::LogFC(LOGDEBUG
, LOGEPG
, "Deleting saved search '{}' from the database",
1480 epgSearch
.GetTitle());
1482 std::unique_lock
<CCriticalSection
> lock(m_critSection
);
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");