1 /*
2  *  Copyright (C) 2012-2018 Team Kodi
3  *  This file is part of Kodi - https://kodi.tv
4  *
5  *  SPDX-License-Identifier: GPL-2.0-or-later
6  *  See LICENSES/README.md for more information.
7  */
8 
9 #include "EpgInfoTag.h"
10 
11 #include "ServiceBroker.h"
12 #include "pvr/PVRManager.h"
13 #include "pvr/PVRPlaybackState.h"
14 #include "pvr/addons/PVRClient.h"
15 #include "pvr/epg/Epg.h"
16 #include "pvr/epg/EpgChannelData.h"
17 #include "pvr/epg/EpgDatabase.h"
18 #include "settings/AdvancedSettings.h"
19 #include "settings/SettingsComponent.h"
20 #include "threads/SingleLock.h"
21 #include "utils/StringUtils.h"
22 #include "utils/Variant.h"
23 #include "utils/log.h"
24 
25 #include <memory>
26 #include <string>
27 #include <vector>
28 
29 using namespace PVR;
30 
CPVREpgInfoTag()31 CPVREpgInfoTag::CPVREpgInfoTag()
32 : m_iUniqueBroadcastID(EPG_TAG_INVALID_UID),
33   m_iFlags(EPG_TAG_FLAG_UNDEFINED),
34   m_channelData(new CPVREpgChannelData)
35 {
36 }
37 
CPVREpgInfoTag(const std::shared_ptr<CPVREpgChannelData> & channelData,int iEpgID,const CDateTime & start,const CDateTime & end,bool bIsGapTag)38 CPVREpgInfoTag::CPVREpgInfoTag(const std::shared_ptr<CPVREpgChannelData>& channelData,
39                                int iEpgID,
40                                const CDateTime& start,
41                                const CDateTime& end,
42                                bool bIsGapTag)
43   : m_iUniqueBroadcastID(EPG_TAG_INVALID_UID),
44     m_iFlags(EPG_TAG_FLAG_UNDEFINED),
45     m_bIsGapTag(bIsGapTag),
46     m_iEpgID(iEpgID)
47 {
48   if (channelData)
49     m_channelData = channelData;
50   else
51     m_channelData = std::make_shared<CPVREpgChannelData>();
52 
53   const CDateTimeSpan correction(
54       0, 0, 0, CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->m_iPVRTimeCorrection);
55   m_startTime = start + correction;
56   m_endTime = end + correction;
57 
58   UpdatePath();
59 }
60 
CPVREpgInfoTag(const EPG_TAG & data,int iClientId,const std::shared_ptr<CPVREpgChannelData> & channelData,int iEpgID)61 CPVREpgInfoTag::CPVREpgInfoTag(const EPG_TAG& data, int iClientId, const std::shared_ptr<CPVREpgChannelData>& channelData, int iEpgID)
62 : m_iParentalRating(data.iParentalRating),
63   m_iStarRating(data.iStarRating),
64   m_iSeriesNumber(data.iSeriesNumber),
65   m_iEpisodeNumber(data.iEpisodeNumber),
66   m_iEpisodePart(data.iEpisodePartNumber),
67   m_iUniqueBroadcastID(data.iUniqueBroadcastId),
68   m_iYear(data.iYear),
69   m_startTime(data.startTime + CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->m_iPVRTimeCorrection),
70   m_endTime(data.endTime + CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->m_iPVRTimeCorrection),
71   m_iFlags(data.iFlags),
72   m_iEpgID(iEpgID)
73 {
74   // strFirstAired is optional, so check if supported before assigning it
75   if (data.strFirstAired && strlen(data.strFirstAired) > 0)
76     m_firstAired.SetFromW3CDate(data.strFirstAired);
77 
78   if (channelData)
79   {
80     m_channelData = channelData;
81 
82     if (m_channelData->ClientId() != iClientId)
83       CLog::LogF(LOGERROR, "Client id mismatch (channel: {}, epg: {})!", m_channelData->ClientId(),
84                  iClientId);
85     if (m_channelData->UniqueClientChannelId() != static_cast<int>(data.iUniqueChannelId))
86       CLog::LogF(LOGERROR, "Channel uid mismatch (channel: {}, epg: {})!",
87                  m_channelData->UniqueClientChannelId(), data.iUniqueChannelId);
88   }
89   else
90   {
91     // provide minimalistic channel data until we get fully initialized later
92     m_channelData = std::make_shared<CPVREpgChannelData>(iClientId, data.iUniqueChannelId);
93   }
94 
95   SetGenre(data.iGenreType, data.iGenreSubType, data.strGenreDescription);
96 
97   // explicit NULL check, because there is no implicit NULL constructor for std::string
98   if (data.strTitle)
99     m_strTitle = data.strTitle;
100   if (data.strPlotOutline)
101     m_strPlotOutline = data.strPlotOutline;
102   if (data.strPlot)
103     m_strPlot = data.strPlot;
104   if (data.strOriginalTitle)
105     m_strOriginalTitle = data.strOriginalTitle;
106   if (data.strCast)
107     m_cast = Tokenize(data.strCast);
108   if (data.strDirector)
109     m_directors = Tokenize(data.strDirector);
110   if (data.strWriter)
111     m_writers = Tokenize(data.strWriter);
112   if (data.strIMDBNumber)
113     m_strIMDBNumber = data.strIMDBNumber;
114   if (data.strEpisodeName)
115     m_strEpisodeName = data.strEpisodeName;
116   if (data.strIconPath)
117     m_strIconPath = data.strIconPath;
118   if (data.strSeriesLink)
119     m_strSeriesLink = data.strSeriesLink;
120 
121   UpdatePath();
122 }
123 
SetChannelData(const std::shared_ptr<CPVREpgChannelData> & data)124 void CPVREpgInfoTag::SetChannelData(const std::shared_ptr<CPVREpgChannelData>& data)
125 {
126   CSingleLock lock(m_critSection);
127   if (data)
128     m_channelData = data;
129   else
130     m_channelData.reset(new CPVREpgChannelData);
131 }
132 
operator ==(const CPVREpgInfoTag & right) const133 bool CPVREpgInfoTag::operator ==(const CPVREpgInfoTag& right) const
134 {
135   if (this == &right)
136     return true;
137 
138   CSingleLock lock(m_critSection);
139   return (m_iUniqueBroadcastID == right.m_iUniqueBroadcastID && m_channelData &&
140           right.m_channelData &&
141           m_channelData->UniqueClientChannelId() == right.m_channelData->UniqueClientChannelId() &&
142           m_channelData->ClientId() == right.m_channelData->ClientId());
143 }
144 
operator !=(const CPVREpgInfoTag & right) const145 bool CPVREpgInfoTag::operator !=(const CPVREpgInfoTag& right) const
146 {
147   if (this == &right)
148     return false;
149 
150   return !(*this == right);
151 }
152 
Serialize(CVariant & value) const153 void CPVREpgInfoTag::Serialize(CVariant& value) const
154 {
155   CSingleLock lock(m_critSection);
156   value["broadcastid"] = m_iDatabaseID; // Use DB id here as it is unique across PVR clients
157   value["channeluid"] = m_channelData->UniqueClientChannelId();
158   value["parentalrating"] = m_iParentalRating;
159   value["rating"] = m_iStarRating;
160   value["title"] = m_strTitle;
161   value["plotoutline"] = m_strPlotOutline;
162   value["plot"] = m_strPlot;
163   value["originaltitle"] = m_strOriginalTitle;
164   value["thumbnail"] = m_strIconPath;
165   value["cast"] = DeTokenize(m_cast);
166   value["director"] = DeTokenize(m_directors);
167   value["writer"] = DeTokenize(m_writers);
168   value["year"] = m_iYear;
169   value["imdbnumber"] = m_strIMDBNumber;
170   value["genre"] = m_genre;
171   value["filenameandpath"] = m_strFileNameAndPath;
172   value["starttime"] = m_startTime.IsValid() ? m_startTime.GetAsDBDateTime() : StringUtils::Empty;
173   value["endtime"] = m_endTime.IsValid() ? m_endTime.GetAsDBDateTime() : StringUtils::Empty;
174   value["runtime"] = GetDuration() / 60;
175   value["firstaired"] = m_firstAired.IsValid() ? m_firstAired.GetAsDBDate() : StringUtils::Empty;
176   value["progress"] = Progress();
177   value["progresspercentage"] = ProgressPercentage();
178   value["episodename"] = m_strEpisodeName;
179   value["episodenum"] = m_iEpisodeNumber;
180   value["episodepart"] = m_iEpisodePart;
181   value["seasonnum"] = m_iSeriesNumber;
182   value["isactive"] = IsActive();
183   value["wasactive"] = WasActive();
184   value["isseries"] = IsSeries();
185   value["serieslink"] = m_strSeriesLink;
186   value["clientid"] = m_channelData->ClientId();
187 }
188 
ClientID() const189 int CPVREpgInfoTag::ClientID() const
190 {
191   CSingleLock lock(m_critSection);
192   return m_channelData->ClientId();
193 }
194 
GetCurrentPlayingTime() const195 CDateTime CPVREpgInfoTag::GetCurrentPlayingTime() const
196 {
197   return CServiceBroker::GetPVRManager().PlaybackState()->GetChannelPlaybackTime(ClientID(),
198                                                                                  UniqueChannelID());
199 }
200 
IsActive() const201 bool CPVREpgInfoTag::IsActive() const
202 {
203   CDateTime now = GetCurrentPlayingTime();
204   return (m_startTime <= now && m_endTime > now);
205 }
206 
WasActive() const207 bool CPVREpgInfoTag::WasActive() const
208 {
209   CDateTime now = GetCurrentPlayingTime();
210   return (m_endTime < now);
211 }
212 
IsUpcoming() const213 bool CPVREpgInfoTag::IsUpcoming() const
214 {
215   CDateTime now = GetCurrentPlayingTime();
216   return (m_startTime > now);
217 }
218 
ProgressPercentage() const219 float CPVREpgInfoTag::ProgressPercentage() const
220 {
221   float fReturn = 0.0f;
222 
223   time_t currentTime, startTime, endTime;
224   CDateTime::GetCurrentDateTime().GetAsUTCDateTime().GetAsTime(currentTime);
225   m_startTime.GetAsTime(startTime);
226   m_endTime.GetAsTime(endTime);
227   int iDuration = endTime - startTime > 0 ? endTime - startTime : 3600;
228 
229   if (currentTime >= startTime && currentTime <= endTime)
230     fReturn = static_cast<float>(currentTime - startTime) * 100.0f / iDuration;
231   else if (currentTime > endTime)
232     fReturn = 100.0f;
233 
234   return fReturn;
235 }
236 
Progress() const237 int CPVREpgInfoTag::Progress() const
238 {
239   time_t currentTime, startTime;
240   CDateTime::GetCurrentDateTime().GetAsUTCDateTime().GetAsTime(currentTime);
241   m_startTime.GetAsTime(startTime);
242   int iDuration = currentTime - startTime;
243 
244   if (iDuration <= 0)
245     return 0;
246 
247   return iDuration;
248 }
249 
SetUniqueBroadcastID(unsigned int iUniqueBroadcastID)250 void CPVREpgInfoTag::SetUniqueBroadcastID(unsigned int iUniqueBroadcastID)
251 {
252   m_iUniqueBroadcastID = iUniqueBroadcastID;
253 }
254 
UniqueBroadcastID() const255 unsigned int CPVREpgInfoTag::UniqueBroadcastID() const
256 {
257   return m_iUniqueBroadcastID;
258 }
259 
DatabaseID() const260 int CPVREpgInfoTag::DatabaseID() const
261 {
262   return m_iDatabaseID;
263 }
264 
UniqueChannelID() const265 int CPVREpgInfoTag::UniqueChannelID() const
266 {
267   CSingleLock lock(m_critSection);
268   return m_channelData->UniqueClientChannelId();
269 }
270 
StartAsUTC() const271 CDateTime CPVREpgInfoTag::StartAsUTC() const
272 {
273   return m_startTime;
274 }
275 
StartAsLocalTime() const276 CDateTime CPVREpgInfoTag::StartAsLocalTime() const
277 {
278   CDateTime retVal;
279   retVal.SetFromUTCDateTime(m_startTime);
280   return retVal;
281 }
282 
EndAsUTC() const283 CDateTime CPVREpgInfoTag::EndAsUTC() const
284 {
285   return m_endTime;
286 }
287 
EndAsLocalTime() const288 CDateTime CPVREpgInfoTag::EndAsLocalTime() const
289 {
290   CDateTime retVal;
291   retVal.SetFromUTCDateTime(m_endTime);
292   return retVal;
293 }
294 
SetEndFromUTC(const CDateTime & end)295 void CPVREpgInfoTag::SetEndFromUTC(const CDateTime& end)
296 {
297   m_endTime = end;
298 }
299 
GetDuration() const300 int CPVREpgInfoTag::GetDuration() const
301 {
302   time_t start, end;
303   m_startTime.GetAsTime(start);
304   m_endTime.GetAsTime(end);
305   return end - start > 0 ? end - start : 3600;
306 }
307 
Title() const308 std::string CPVREpgInfoTag::Title() const
309 {
310   return m_strTitle;
311 }
312 
PlotOutline() const313 std::string CPVREpgInfoTag::PlotOutline() const
314 {
315   return m_strPlotOutline;
316 }
317 
Plot() const318 std::string CPVREpgInfoTag::Plot() const
319 {
320   return m_strPlot;
321 }
322 
OriginalTitle() const323 std::string CPVREpgInfoTag::OriginalTitle() const
324 {
325   return m_strOriginalTitle;
326 }
327 
Cast() const328 const std::vector<std::string> CPVREpgInfoTag::Cast() const
329 {
330   return m_cast;
331 }
332 
Directors() const333 const std::vector<std::string> CPVREpgInfoTag::Directors() const
334 {
335   return m_directors;
336 }
337 
Writers() const338 const std::vector<std::string> CPVREpgInfoTag::Writers() const
339 {
340   return m_writers;
341 }
342 
GetCastLabel() const343 const std::string CPVREpgInfoTag::GetCastLabel() const
344 {
345   // Note: see CVideoInfoTag::GetCast for reference implementation.
346   std::string strLabel;
347   for (const auto& castEntry : m_cast)
348     strLabel += StringUtils::Format("%s\n", castEntry.c_str());
349 
350   return StringUtils::TrimRight(strLabel, "\n");
351 }
352 
GetDirectorsLabel() const353 const std::string CPVREpgInfoTag::GetDirectorsLabel() const
354 {
355   return StringUtils::Join(m_directors, CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->m_videoItemSeparator);
356 }
357 
GetWritersLabel() const358 const std::string CPVREpgInfoTag::GetWritersLabel() const
359 {
360   return StringUtils::Join(m_writers, CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->m_videoItemSeparator);
361 }
362 
GetGenresLabel() const363 const std::string CPVREpgInfoTag::GetGenresLabel() const
364 {
365   return StringUtils::Join(m_genre, CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->m_videoItemSeparator);
366 }
367 
Year() const368 int CPVREpgInfoTag::Year() const
369 {
370   return m_iYear;
371 }
372 
IMDBNumber() const373 std::string CPVREpgInfoTag::IMDBNumber() const
374 {
375   return m_strIMDBNumber;
376 }
377 
SetGenre(int iGenreType,int iGenreSubType,const char * strGenre)378 void CPVREpgInfoTag::SetGenre(int iGenreType, int iGenreSubType, const char* strGenre)
379 {
380   if (m_iGenreType != iGenreType || m_iGenreSubType != iGenreSubType)
381   {
382     m_iGenreType = iGenreType;
383     m_iGenreSubType = iGenreSubType;
384     if ((iGenreType == EPG_GENRE_USE_STRING || iGenreSubType == EPG_GENRE_USE_STRING) && (strGenre != NULL) && (strlen(strGenre) > 0))
385     {
386       /* Type and sub type are both not given. No EPG color coding possible unless sub type is used to specify
387        * EPG_GENRE_USE_STRING leaving type available for genre category, use the provided genre description for the text. */
388       m_genre = Tokenize(strGenre);
389     }
390   }
391 
392   if (m_genre.empty())
393   {
394     // Determine the genre description from the type and subtype IDs.
395     m_genre = StringUtils::Split(
396         CPVREpg::ConvertGenreIdToString(iGenreType, iGenreSubType),
397         CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->m_videoItemSeparator);
398   }
399 }
400 
GenreType() const401 int CPVREpgInfoTag::GenreType() const
402 {
403   return m_iGenreType;
404 }
405 
GenreSubType() const406 int CPVREpgInfoTag::GenreSubType() const
407 {
408   return m_iGenreSubType;
409 }
410 
Genre() const411 const std::vector<std::string> CPVREpgInfoTag::Genre() const
412 {
413   return m_genre;
414 }
415 
FirstAired() const416 CDateTime CPVREpgInfoTag::FirstAired() const
417 {
418   return m_firstAired;
419 }
420 
ParentalRating() const421 int CPVREpgInfoTag::ParentalRating() const
422 {
423   return m_iParentalRating;
424 }
425 
StarRating() const426 int CPVREpgInfoTag::StarRating() const
427 {
428   return m_iStarRating;
429 }
430 
SeriesNumber() const431 int CPVREpgInfoTag::SeriesNumber() const
432 {
433   return m_iSeriesNumber;
434 }
435 
SeriesLink() const436 std::string CPVREpgInfoTag::SeriesLink() const
437 {
438   return m_strSeriesLink;
439 }
440 
EpisodeNumber() const441 int CPVREpgInfoTag::EpisodeNumber() const
442 {
443   return m_iEpisodeNumber;
444 }
445 
EpisodePart() const446 int CPVREpgInfoTag::EpisodePart() const
447 {
448   return m_iEpisodePart;
449 }
450 
EpisodeName() const451 std::string CPVREpgInfoTag::EpisodeName() const
452 {
453   return m_strEpisodeName;
454 }
455 
Icon() const456 std::string CPVREpgInfoTag::Icon() const
457 {
458   return m_strIconPath;
459 }
460 
Path() const461 std::string CPVREpgInfoTag::Path() const
462 {
463   return m_strFileNameAndPath;
464 }
465 
Update(const CPVREpgInfoTag & tag,bool bUpdateBroadcastId)466 bool CPVREpgInfoTag::Update(const CPVREpgInfoTag& tag, bool bUpdateBroadcastId /* = true */)
467 {
468   CSingleLock lock(m_critSection);
469   bool bChanged = (
470       m_strTitle           != tag.m_strTitle ||
471       m_strPlotOutline     != tag.m_strPlotOutline ||
472       m_strPlot            != tag.m_strPlot ||
473       m_strOriginalTitle   != tag.m_strOriginalTitle ||
474       m_cast               != tag.m_cast ||
475       m_directors          != tag.m_directors ||
476       m_writers            != tag.m_writers ||
477       m_iYear              != tag.m_iYear ||
478       m_strIMDBNumber      != tag.m_strIMDBNumber ||
479       m_startTime          != tag.m_startTime ||
480       m_endTime            != tag.m_endTime ||
481       m_iGenreType         != tag.m_iGenreType ||
482       m_iGenreSubType      != tag.m_iGenreSubType ||
483       m_firstAired         != tag.m_firstAired ||
484       m_iParentalRating    != tag.m_iParentalRating ||
485       m_iStarRating        != tag.m_iStarRating ||
486       m_iEpisodeNumber     != tag.m_iEpisodeNumber ||
487       m_iEpisodePart       != tag.m_iEpisodePart ||
488       m_iSeriesNumber      != tag.m_iSeriesNumber ||
489       m_strEpisodeName     != tag.m_strEpisodeName ||
490       m_iUniqueBroadcastID != tag.m_iUniqueBroadcastID ||
491       m_iEpgID             != tag.m_iEpgID ||
492       m_genre              != tag.m_genre ||
493       m_strIconPath        != tag.m_strIconPath ||
494       m_iFlags             != tag.m_iFlags ||
495       m_strSeriesLink      != tag.m_strSeriesLink ||
496       m_channelData        != tag.m_channelData
497   );
498 
499   if (bUpdateBroadcastId)
500     bChanged |= (m_iDatabaseID != tag.m_iDatabaseID);
501 
502   if (bChanged)
503   {
504     if (bUpdateBroadcastId)
505       m_iDatabaseID = tag.m_iDatabaseID;
506 
507     m_strTitle = tag.m_strTitle;
508     m_strPlotOutline = tag.m_strPlotOutline;
509     m_strPlot = tag.m_strPlot;
510     m_strOriginalTitle = tag.m_strOriginalTitle;
511     m_cast = tag.m_cast;
512     m_directors = tag.m_directors;
513     m_writers = tag.m_writers;
514     m_iYear = tag.m_iYear;
515     m_strIMDBNumber = tag.m_strIMDBNumber;
516     m_startTime = tag.m_startTime;
517     m_endTime = tag.m_endTime;
518     m_iGenreType = tag.m_iGenreType;
519     m_iGenreSubType = tag.m_iGenreSubType;
520     m_iEpgID = tag.m_iEpgID;
521     m_iFlags = tag.m_iFlags;
522     m_strSeriesLink = tag.m_strSeriesLink;
523 
524     if (m_iGenreType == EPG_GENRE_USE_STRING || m_iGenreSubType == EPG_GENRE_USE_STRING)
525     {
526       /* No type/subtype. Use the provided description */
527       m_genre = tag.m_genre;
528     }
529     else
530     {
531       /* Determine genre description by type/subtype */
532       m_genre = StringUtils::Split(CPVREpg::ConvertGenreIdToString(tag.m_iGenreType, tag.m_iGenreSubType), CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->m_videoItemSeparator);
533     }
534     m_firstAired = tag.m_firstAired;
535     m_iParentalRating = tag.m_iParentalRating;
536     m_iStarRating = tag.m_iStarRating;
537     m_iEpisodeNumber = tag.m_iEpisodeNumber;
538     m_iEpisodePart = tag.m_iEpisodePart;
539     m_iSeriesNumber = tag.m_iSeriesNumber;
540     m_strEpisodeName = tag.m_strEpisodeName;
541     m_iUniqueBroadcastID = tag.m_iUniqueBroadcastID;
542     m_strIconPath = tag.m_strIconPath;
543     m_channelData = tag.m_channelData;
544   }
545 
546   if (bChanged)
547     UpdatePath();
548 
549   return bChanged;
550 }
551 
QueuePersistQuery(const std::shared_ptr<CPVREpgDatabase> & database)552 bool CPVREpgInfoTag::QueuePersistQuery(const std::shared_ptr<CPVREpgDatabase>& database)
553 {
554   if (!database)
555   {
556     CLog::LogF(LOGERROR, "Could not open the EPG database");
557     return false;
558   }
559 
560   return database->QueuePersistQuery(*this);
561 }
562 
GetEdl() const563 std::vector<PVR_EDL_ENTRY> CPVREpgInfoTag::GetEdl() const
564 {
565   std::vector<PVR_EDL_ENTRY> edls;
566 
567   CSingleLock lock(m_critSection);
568   const std::shared_ptr<CPVRClient> client = CServiceBroker::GetPVRManager().GetClient(m_channelData->ClientId());
569 
570   if (client && client->GetClientCapabilities().SupportsEpgTagEdl())
571     client->GetEpgTagEdl(shared_from_this(), edls);
572 
573   return edls;
574 }
575 
UpdatePath()576 void CPVREpgInfoTag::UpdatePath()
577 {
578   m_strFileNameAndPath = StringUtils::Format("pvr://guide/%04i/%s.epg", EpgID(), m_startTime.GetAsDBDateTime().c_str());
579 }
580 
EpgID() const581 int CPVREpgInfoTag::EpgID() const
582 {
583   return m_iEpgID;
584 }
585 
SetEpgID(int iEpgID)586 void CPVREpgInfoTag::SetEpgID(int iEpgID)
587 {
588   m_iEpgID = iEpgID;
589   UpdatePath(); // Note: path contains epg id.
590 }
591 
IsRecordable() const592 bool CPVREpgInfoTag::IsRecordable() const
593 {
594   bool bIsRecordable = false;
595 
596   CSingleLock lock(m_critSection);
597   const std::shared_ptr<CPVRClient> client = CServiceBroker::GetPVRManager().GetClient(m_channelData->ClientId());
598   if (!client || (client->IsRecordable(shared_from_this(), bIsRecordable) != PVR_ERROR_NO_ERROR))
599   {
600     // event end time based fallback
601     bIsRecordable = EndAsLocalTime() > CDateTime::GetCurrentDateTime();
602   }
603   return bIsRecordable;
604 }
605 
IsPlayable() const606 bool CPVREpgInfoTag::IsPlayable() const
607 {
608   bool bIsPlayable = false;
609 
610   CSingleLock lock(m_critSection);
611   const std::shared_ptr<CPVRClient> client = CServiceBroker::GetPVRManager().GetClient(m_channelData->ClientId());
612   if (!client || (client->IsPlayable(shared_from_this(), bIsPlayable) != PVR_ERROR_NO_ERROR))
613   {
614     // fallback
615     bIsPlayable = false;
616   }
617   return bIsPlayable;
618 }
619 
IsSeries() const620 bool CPVREpgInfoTag::IsSeries() const
621 {
622   if ((m_iFlags & EPG_TAG_FLAG_IS_SERIES) > 0 || SeriesNumber() >= 0 || EpisodeNumber() >= 0 || EpisodePart() >= 0)
623     return true;
624   else
625     return false;
626 }
627 
IsRadio() const628 bool CPVREpgInfoTag::IsRadio() const
629 {
630   CSingleLock lock(m_critSection);
631   return m_channelData->IsRadio();
632 }
633 
IsParentalLocked() const634 bool CPVREpgInfoTag::IsParentalLocked() const
635 {
636   CSingleLock lock(m_critSection);
637   return m_channelData->IsLocked();
638 }
639 
IsGapTag() const640 bool CPVREpgInfoTag::IsGapTag() const
641 {
642   CSingleLock lock(m_critSection);
643   return m_bIsGapTag;
644 }
645 
IsNew() const646 bool CPVREpgInfoTag::IsNew() const
647 {
648   return (m_iFlags & EPG_TAG_FLAG_IS_NEW) > 0;
649 }
650 
IsPremiere() const651 bool CPVREpgInfoTag::IsPremiere() const
652 {
653   return (m_iFlags & EPG_TAG_FLAG_IS_PREMIERE) > 0;
654 }
655 
IsFinale() const656 bool CPVREpgInfoTag::IsFinale() const
657 {
658   return (m_iFlags & EPG_TAG_FLAG_IS_FINALE) > 0;
659 }
660 
IsLive() const661 bool CPVREpgInfoTag::IsLive() const
662 {
663   return (m_iFlags & EPG_TAG_FLAG_IS_LIVE) > 0;
664 }
665 
Tokenize(const std::string & str)666 const std::vector<std::string> CPVREpgInfoTag::Tokenize(const std::string& str)
667 {
668   return StringUtils::Split(str.c_str(), EPG_STRING_TOKEN_SEPARATOR);
669 }
670 
DeTokenize(const std::vector<std::string> & tokens)671 const std::string CPVREpgInfoTag::DeTokenize(const std::vector<std::string>& tokens)
672 {
673   return StringUtils::Join(tokens, EPG_STRING_TOKEN_SEPARATOR);
674 }
675