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 "EpgSearchFilter.h"
11 #include "ServiceBroker.h"
12 #include "pvr/PVRManager.h"
13 #include "pvr/addons/PVRClients.h"
14 #include "pvr/channels/PVRChannel.h"
15 #include "pvr/channels/PVRChannelGroup.h"
16 #include "pvr/channels/PVRChannelGroups.h"
17 #include "pvr/channels/PVRChannelGroupsContainer.h"
18 #include "pvr/epg/EpgContainer.h"
19 #include "pvr/epg/EpgInfoTag.h"
20 #include "pvr/epg/EpgSearchPath.h"
21 #include "pvr/recordings/PVRRecordings.h"
22 #include "pvr/timers/PVRTimers.h"
23 #include "utils/TextSearch.h"
24 #include "utils/log.h"
31 CPVREpgSearchFilter::CPVREpgSearchFilter(bool bRadio
)
37 void CPVREpgSearchFilter::Reset()
40 m_bEpgSearchDataFiltered
= false;
42 m_bIsCaseSensitive
= false;
43 m_iMinimumDuration
= EPG_SEARCH_UNSET
;
44 m_iMaximumDuration
= EPG_SEARCH_UNSET
;
45 m_bRemoveDuplicates
= false;
47 /* pvr specific filters */
49 m_iChannelGroupID
= -1;
51 m_bFreeToAirOnly
= false;
52 m_bIgnorePresentTimers
= true;
53 m_bIgnorePresentRecordings
= true;
55 m_groupIdMatches
.reset();
59 m_lastExecutedDateTime
.SetValid(false);
62 std::string
CPVREpgSearchFilter::GetPath() const
64 return CPVREpgSearchPath(*this).GetPath();
67 void CPVREpgSearchFilter::SetSearchTerm(const std::string
& strSearchTerm
)
69 if (m_searchData
.m_strSearchTerm
!= strSearchTerm
)
71 m_searchData
.m_strSearchTerm
= strSearchTerm
;
76 void CPVREpgSearchFilter::SetSearchPhrase(const std::string
& strSearchPhrase
)
78 // match the exact phrase
79 SetSearchTerm("\"" + strSearchPhrase
+ "\"");
82 void CPVREpgSearchFilter::SetCaseSensitive(bool bIsCaseSensitive
)
84 if (m_bIsCaseSensitive
!= bIsCaseSensitive
)
86 m_bIsCaseSensitive
= bIsCaseSensitive
;
91 void CPVREpgSearchFilter::SetSearchInDescription(bool bSearchInDescription
)
93 if (m_searchData
.m_bSearchInDescription
!= bSearchInDescription
)
95 m_searchData
.m_bSearchInDescription
= bSearchInDescription
;
100 void CPVREpgSearchFilter::SetGenreType(int iGenreType
)
102 if (m_searchData
.m_iGenreType
!= iGenreType
)
104 m_searchData
.m_iGenreType
= iGenreType
;
109 void CPVREpgSearchFilter::SetMinimumDuration(int iMinimumDuration
)
111 if (m_iMinimumDuration
!= iMinimumDuration
)
113 m_iMinimumDuration
= iMinimumDuration
;
118 void CPVREpgSearchFilter::SetMaximumDuration(int iMaximumDuration
)
120 if (m_iMaximumDuration
!= iMaximumDuration
)
122 m_iMaximumDuration
= iMaximumDuration
;
127 void CPVREpgSearchFilter::SetIgnoreFinishedBroadcasts(bool bIgnoreFinishedBroadcasts
)
129 if (m_searchData
.m_bIgnoreFinishedBroadcasts
!= bIgnoreFinishedBroadcasts
)
131 m_searchData
.m_bIgnoreFinishedBroadcasts
= bIgnoreFinishedBroadcasts
;
136 void CPVREpgSearchFilter::SetIgnoreFutureBroadcasts(bool bIgnoreFutureBroadcasts
)
138 if (m_searchData
.m_bIgnoreFutureBroadcasts
!= bIgnoreFutureBroadcasts
)
140 m_searchData
.m_bIgnoreFutureBroadcasts
= bIgnoreFutureBroadcasts
;
145 void CPVREpgSearchFilter::SetStartDateTime(const CDateTime
& startDateTime
)
147 if (m_searchData
.m_startDateTime
!= startDateTime
)
149 m_searchData
.m_startDateTime
= startDateTime
;
154 void CPVREpgSearchFilter::SetEndDateTime(const CDateTime
& endDateTime
)
156 if (m_searchData
.m_endDateTime
!= endDateTime
)
158 m_searchData
.m_endDateTime
= endDateTime
;
163 void CPVREpgSearchFilter::SetIncludeUnknownGenres(bool bIncludeUnknownGenres
)
165 if (m_searchData
.m_bIncludeUnknownGenres
!= bIncludeUnknownGenres
)
167 m_searchData
.m_bIncludeUnknownGenres
= bIncludeUnknownGenres
;
172 void CPVREpgSearchFilter::SetRemoveDuplicates(bool bRemoveDuplicates
)
174 if (m_bRemoveDuplicates
!= bRemoveDuplicates
)
176 m_bRemoveDuplicates
= bRemoveDuplicates
;
181 void CPVREpgSearchFilter::SetClientID(int iClientID
)
183 if (m_iClientID
!= iClientID
)
185 m_iClientID
= iClientID
;
190 void CPVREpgSearchFilter::SetChannelGroupID(int iChannelGroupID
)
192 if (m_iChannelGroupID
!= iChannelGroupID
)
194 m_iChannelGroupID
= iChannelGroupID
;
195 m_groupIdMatches
.reset();
200 void CPVREpgSearchFilter::SetChannelUID(int iChannelUID
)
202 if (m_iChannelUID
!= iChannelUID
)
204 m_iChannelUID
= iChannelUID
;
209 void CPVREpgSearchFilter::SetFreeToAirOnly(bool bFreeToAirOnly
)
211 if (m_bFreeToAirOnly
!= bFreeToAirOnly
)
213 m_bFreeToAirOnly
= bFreeToAirOnly
;
218 void CPVREpgSearchFilter::SetIgnorePresentTimers(bool bIgnorePresentTimers
)
220 if (m_bIgnorePresentTimers
!= bIgnorePresentTimers
)
222 m_bIgnorePresentTimers
= bIgnorePresentTimers
;
227 void CPVREpgSearchFilter::SetIgnorePresentRecordings(bool bIgnorePresentRecordings
)
229 if (m_bIgnorePresentRecordings
!= bIgnorePresentRecordings
)
231 m_bIgnorePresentRecordings
= bIgnorePresentRecordings
;
236 void CPVREpgSearchFilter::SetDatabaseId(int iDatabaseId
)
238 if (m_iDatabaseId
!= iDatabaseId
)
240 m_iDatabaseId
= iDatabaseId
;
245 void CPVREpgSearchFilter::SetTitle(const std::string
& title
)
247 if (m_title
!= title
)
254 void CPVREpgSearchFilter::SetLastExecutedDateTime(const CDateTime
& lastExecutedDateTime
)
256 // Note: No need to set m_bChanged here
257 m_lastExecutedDateTime
= lastExecutedDateTime
;
260 bool CPVREpgSearchFilter::MatchGenre(const std::shared_ptr
<const CPVREpgInfoTag
>& tag
) const
262 if (m_bEpgSearchDataFiltered
)
265 if (m_searchData
.m_iGenreType
!= EPG_SEARCH_UNSET
)
267 if (m_searchData
.m_bIncludeUnknownGenres
)
269 // match the exact genre and everything with unknown genre
270 return (tag
->GenreType() == m_searchData
.m_iGenreType
||
271 tag
->GenreType() < EPG_EVENT_CONTENTMASK_MOVIEDRAMA
||
272 tag
->GenreType() > EPG_EVENT_CONTENTMASK_USERDEFINED
);
276 // match only the exact genre
277 return (tag
->GenreType() == m_searchData
.m_iGenreType
);
285 bool CPVREpgSearchFilter::MatchDuration(const std::shared_ptr
<const CPVREpgInfoTag
>& tag
) const
289 if (m_iMinimumDuration
!= EPG_SEARCH_UNSET
)
290 bReturn
= (tag
->GetDuration() > m_iMinimumDuration
* 60);
292 if (bReturn
&& m_iMaximumDuration
!= EPG_SEARCH_UNSET
)
293 bReturn
= (tag
->GetDuration() < m_iMaximumDuration
* 60);
298 bool CPVREpgSearchFilter::MatchStartAndEndTimes(
299 const std::shared_ptr
<const CPVREpgInfoTag
>& tag
) const
301 if (m_bEpgSearchDataFiltered
)
304 return ((!m_searchData
.m_bIgnoreFinishedBroadcasts
||
305 tag
->EndAsUTC() > CDateTime::GetUTCDateTime()) &&
306 (!m_searchData
.m_bIgnoreFutureBroadcasts
||
307 tag
->StartAsUTC() < CDateTime::GetUTCDateTime()) &&
308 (!m_searchData
.m_startDateTime
.IsValid() || // invalid => match any datetime
309 tag
->StartAsUTC() >= m_searchData
.m_startDateTime
) &&
310 (!m_searchData
.m_endDateTime
.IsValid() || // invalid => match any datetime
311 tag
->EndAsUTC() <= m_searchData
.m_endDateTime
));
314 bool CPVREpgSearchFilter::MatchSearchTerm(const std::shared_ptr
<const CPVREpgInfoTag
>& tag
) const
318 if (!m_searchData
.m_strSearchTerm
.empty())
320 bReturn
= !CServiceBroker::GetPVRManager().IsParentalLocked(tag
);
321 if (bReturn
&& (m_bIsCaseSensitive
|| !m_bEpgSearchDataFiltered
))
323 CTextSearch
search(m_searchData
.m_strSearchTerm
, m_bIsCaseSensitive
, SEARCH_DEFAULT_OR
);
325 bReturn
= search
.Search(tag
->Title()) || search
.Search(tag
->PlotOutline()) ||
326 (m_searchData
.m_bSearchInDescription
&& search
.Search(tag
->Plot()));
333 bool CPVREpgSearchFilter::FilterEntry(const std::shared_ptr
<const CPVREpgInfoTag
>& tag
) const
335 return MatchGenre(tag
) && MatchDuration(tag
) && MatchStartAndEndTimes(tag
) &&
336 MatchSearchTerm(tag
) && MatchChannel(tag
) && MatchChannelGroup(tag
) && MatchTimers(tag
) &&
337 MatchRecordings(tag
) && MatchFreeToAir(tag
);
340 void CPVREpgSearchFilter::RemoveDuplicates(std::vector
<std::shared_ptr
<CPVREpgInfoTag
>>& results
)
342 for (auto it
= results
.begin(); it
!= results
.end();)
344 it
= results
.erase(std::remove_if(results
.begin(), results
.end(),
345 [&it
](const std::shared_ptr
<const CPVREpgInfoTag
>& entry
) {
346 return *it
!= entry
&& (*it
)->Title() == entry
->Title() &&
347 (*it
)->Plot() == entry
->Plot() &&
348 (*it
)->PlotOutline() == entry
->PlotOutline();
354 bool CPVREpgSearchFilter::MatchChannel(const std::shared_ptr
<const CPVREpgInfoTag
>& tag
) const
356 return tag
&& (tag
->IsRadio() == m_bIsRadio
) &&
357 (m_iClientID
== -1 || tag
->ClientID() == m_iClientID
) &&
358 (m_iChannelUID
== -1 || tag
->UniqueChannelID() == m_iChannelUID
) &&
359 CServiceBroker::GetPVRManager().Clients()->IsCreatedClient(tag
->ClientID());
362 bool CPVREpgSearchFilter::MatchChannelGroup(const std::shared_ptr
<const CPVREpgInfoTag
>& tag
) const
364 if (m_iChannelGroupID
!= -1)
366 if (!m_groupIdMatches
.has_value())
368 const std::shared_ptr
<const CPVRChannelGroup
> group
= CServiceBroker::GetPVRManager()
371 ->GetById(m_iChannelGroupID
);
373 group
&& (group
->GetByUniqueID({tag
->ClientID(), tag
->UniqueChannelID()}) != nullptr);
376 return *m_groupIdMatches
;
382 bool CPVREpgSearchFilter::MatchFreeToAir(const std::shared_ptr
<const CPVREpgInfoTag
>& tag
) const
384 if (m_bFreeToAirOnly
)
386 const std::shared_ptr
<const CPVRChannel
> channel
=
387 CServiceBroker::GetPVRManager().ChannelGroups()->GetChannelForEpgTag(tag
);
388 return channel
&& !channel
->IsEncrypted();
394 bool CPVREpgSearchFilter::MatchTimers(const std::shared_ptr
<const CPVREpgInfoTag
>& tag
) const
396 return (!m_bIgnorePresentTimers
|| !CServiceBroker::GetPVRManager().Timers()->GetTimerForEpgTag(tag
));
399 bool CPVREpgSearchFilter::MatchRecordings(const std::shared_ptr
<const CPVREpgInfoTag
>& tag
) const
401 return (!m_bIgnorePresentRecordings
|| !CServiceBroker::GetPVRManager().Recordings()->GetRecordingForEpgTag(tag
));