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 "EpgInfoTag.h"
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 "utils/StringUtils.h"
21 #include "utils/Variant.h"
22 #include "utils/log.h"
31 const std::string
CPVREpgInfoTag::IMAGE_OWNER_PATTERN
= "epgtag_{}";
33 CPVREpgInfoTag::CPVREpgInfoTag(int iEpgID
, const std::string
& iconPath
)
34 : m_iUniqueBroadcastID(EPG_TAG_INVALID_UID
),
35 m_iconPath(iconPath
, StringUtils::Format(IMAGE_OWNER_PATTERN
, iEpgID
)),
36 m_iFlags(EPG_TAG_FLAG_UNDEFINED
),
37 m_channelData(new CPVREpgChannelData
),
42 CPVREpgInfoTag::CPVREpgInfoTag(const std::shared_ptr
<CPVREpgChannelData
>& channelData
,
44 const CDateTime
& start
,
47 : m_iUniqueBroadcastID(EPG_TAG_INVALID_UID
),
48 m_iconPath(StringUtils::Format(IMAGE_OWNER_PATTERN
, iEpgID
)),
49 m_iFlags(EPG_TAG_FLAG_UNDEFINED
),
50 m_bIsGapTag(bIsGapTag
),
54 m_channelData
= channelData
;
56 m_channelData
= std::make_shared
<CPVREpgChannelData
>();
58 const CDateTimeSpan
correction(
59 0, 0, 0, CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->m_iPVRTimeCorrection
);
60 m_startTime
= start
+ correction
;
61 m_endTime
= end
+ correction
;
64 CPVREpgInfoTag::CPVREpgInfoTag(const EPG_TAG
& data
,
66 const std::shared_ptr
<CPVREpgChannelData
>& channelData
,
68 : m_iGenreType(data
.iGenreType
),
69 m_iGenreSubType(data
.iGenreSubType
),
70 m_iParentalRating(data
.iParentalRating
),
71 m_iStarRating(data
.iStarRating
),
72 m_iSeriesNumber(data
.iSeriesNumber
),
73 m_iEpisodeNumber(data
.iEpisodeNumber
),
74 m_iEpisodePart(data
.iEpisodePartNumber
),
75 m_iUniqueBroadcastID(data
.iUniqueBroadcastId
),
77 m_iconPath(data
.strIconPath
? data
.strIconPath
: "",
78 StringUtils::Format(IMAGE_OWNER_PATTERN
, iEpgID
)),
81 CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->m_iPVRTimeCorrection
),
82 m_endTime(data
.endTime
+
83 CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->m_iPVRTimeCorrection
),
84 m_iFlags(data
.iFlags
),
87 // strFirstAired is optional, so check if supported before assigning it
88 if (data
.strFirstAired
&& strlen(data
.strFirstAired
) > 0)
89 m_firstAired
.SetFromW3CDate(data
.strFirstAired
);
93 m_channelData
= channelData
;
95 if (m_channelData
->ClientId() != iClientId
)
96 CLog::LogF(LOGERROR
, "Client id mismatch (channel: {}, epg: {})!", m_channelData
->ClientId(),
98 if (m_channelData
->UniqueClientChannelId() != static_cast<int>(data
.iUniqueChannelId
))
99 CLog::LogF(LOGERROR
, "Channel uid mismatch (channel: {}, epg: {})!",
100 m_channelData
->UniqueClientChannelId(), data
.iUniqueChannelId
);
104 // provide minimalistic channel data until we get fully initialized later
105 m_channelData
= std::make_shared
<CPVREpgChannelData
>(iClientId
, data
.iUniqueChannelId
);
108 // explicit NULL check, because there is no implicit NULL constructor for std::string
110 m_strTitle
= data
.strTitle
;
111 if (data
.strGenreDescription
)
112 m_strGenreDescription
= data
.strGenreDescription
;
113 if (data
.strPlotOutline
)
114 m_strPlotOutline
= data
.strPlotOutline
;
116 m_strPlot
= data
.strPlot
;
117 if (data
.strOriginalTitle
)
118 m_strOriginalTitle
= data
.strOriginalTitle
;
120 m_cast
= Tokenize(data
.strCast
);
121 if (data
.strDirector
)
122 m_directors
= Tokenize(data
.strDirector
);
124 m_writers
= Tokenize(data
.strWriter
);
125 if (data
.strIMDBNumber
)
126 m_strIMDBNumber
= data
.strIMDBNumber
;
127 if (data
.strEpisodeName
)
128 m_strEpisodeName
= data
.strEpisodeName
;
129 if (data
.strSeriesLink
)
130 m_strSeriesLink
= data
.strSeriesLink
;
131 if (data
.strParentalRatingCode
)
132 m_strParentalRatingCode
= data
.strParentalRatingCode
;
135 void CPVREpgInfoTag::SetChannelData(const std::shared_ptr
<CPVREpgChannelData
>& data
)
137 std::unique_lock
<CCriticalSection
> lock(m_critSection
);
139 m_channelData
= data
;
141 m_channelData
= std::make_shared
<CPVREpgChannelData
>();
144 bool CPVREpgInfoTag::operator==(const CPVREpgInfoTag
& right
) const
149 std::unique_lock
<CCriticalSection
> lock(m_critSection
);
150 return (m_iUniqueBroadcastID
== right
.m_iUniqueBroadcastID
&& m_channelData
&&
151 right
.m_channelData
&&
152 m_channelData
->UniqueClientChannelId() == right
.m_channelData
->UniqueClientChannelId() &&
153 m_channelData
->ClientId() == right
.m_channelData
->ClientId());
156 bool CPVREpgInfoTag::operator!=(const CPVREpgInfoTag
& right
) const
161 return !(*this == right
);
164 void CPVREpgInfoTag::Serialize(CVariant
& value
) const
166 std::unique_lock
<CCriticalSection
> lock(m_critSection
);
167 value
["broadcastid"] = m_iDatabaseID
; // Use DB id here as it is unique across PVR clients
168 value
["channeluid"] = m_channelData
->UniqueClientChannelId();
169 value
["parentalrating"] = m_iParentalRating
;
170 value
["parentalratingcode"] = m_strParentalRatingCode
;
171 value
["rating"] = m_iStarRating
;
172 value
["title"] = m_strTitle
;
173 value
["plotoutline"] = m_strPlotOutline
;
174 value
["plot"] = m_strPlot
;
175 value
["originaltitle"] = m_strOriginalTitle
;
176 value
["thumbnail"] = ClientIconPath();
177 value
["cast"] = DeTokenize(m_cast
);
178 value
["director"] = DeTokenize(m_directors
);
179 value
["writer"] = DeTokenize(m_writers
);
180 value
["year"] = m_iYear
;
181 value
["imdbnumber"] = m_strIMDBNumber
;
182 value
["genre"] = Genre();
183 value
["filenameandpath"] = Path();
184 value
["starttime"] = m_startTime
.IsValid() ? m_startTime
.GetAsDBDateTime() : StringUtils::Empty
;
185 value
["endtime"] = m_endTime
.IsValid() ? m_endTime
.GetAsDBDateTime() : StringUtils::Empty
;
186 value
["runtime"] = GetDuration() / 60;
187 value
["firstaired"] = m_firstAired
.IsValid() ? m_firstAired
.GetAsDBDate() : StringUtils::Empty
;
188 value
["progress"] = Progress();
189 value
["progresspercentage"] = ProgressPercentage();
190 value
["episodename"] = m_strEpisodeName
;
191 value
["episodenum"] = m_iEpisodeNumber
;
192 value
["episodepart"] = m_iEpisodePart
;
193 value
["seasonnum"] = m_iSeriesNumber
;
194 value
["isactive"] = IsActive();
195 value
["wasactive"] = WasActive();
196 value
["isseries"] = IsSeries();
197 value
["serieslink"] = m_strSeriesLink
;
198 value
["clientid"] = m_channelData
->ClientId();
201 int CPVREpgInfoTag::ClientID() const
203 std::unique_lock
<CCriticalSection
> lock(m_critSection
);
204 return m_channelData
->ClientId();
207 CDateTime
CPVREpgInfoTag::GetCurrentPlayingTime() const
209 return CServiceBroker::GetPVRManager().PlaybackState()->GetChannelPlaybackTime(ClientID(),
213 bool CPVREpgInfoTag::IsActive() const
215 CDateTime now
= GetCurrentPlayingTime();
216 return (m_startTime
<= now
&& m_endTime
> now
);
219 bool CPVREpgInfoTag::WasActive() const
221 CDateTime now
= GetCurrentPlayingTime();
222 return (m_endTime
< now
);
225 bool CPVREpgInfoTag::IsUpcoming() const
227 CDateTime now
= GetCurrentPlayingTime();
228 return (m_startTime
> now
);
231 float CPVREpgInfoTag::ProgressPercentage() const
233 float fReturn
= 0.0f
;
235 time_t currentTime
, startTime
, endTime
;
236 CDateTime::GetCurrentDateTime().GetAsUTCDateTime().GetAsTime(currentTime
);
237 m_startTime
.GetAsTime(startTime
);
238 m_endTime
.GetAsTime(endTime
);
239 int iDuration
= endTime
- startTime
> 0 ? endTime
- startTime
: 3600;
241 if (currentTime
>= startTime
&& currentTime
<= endTime
)
242 fReturn
= static_cast<float>(currentTime
- startTime
) * 100.0f
/ iDuration
;
243 else if (currentTime
> endTime
)
249 int CPVREpgInfoTag::Progress() const
251 time_t currentTime
, startTime
;
252 CDateTime::GetCurrentDateTime().GetAsUTCDateTime().GetAsTime(currentTime
);
253 m_startTime
.GetAsTime(startTime
);
254 int iDuration
= currentTime
- startTime
;
262 void CPVREpgInfoTag::SetUniqueBroadcastID(unsigned int iUniqueBroadcastID
)
264 m_iUniqueBroadcastID
= iUniqueBroadcastID
;
267 unsigned int CPVREpgInfoTag::UniqueBroadcastID() const
269 return m_iUniqueBroadcastID
;
272 int CPVREpgInfoTag::DatabaseID() const
274 return m_iDatabaseID
;
277 int CPVREpgInfoTag::UniqueChannelID() const
279 std::unique_lock
<CCriticalSection
> lock(m_critSection
);
280 return m_channelData
->UniqueClientChannelId();
283 std::string
CPVREpgInfoTag::ChannelIconPath() const
285 std::unique_lock
<CCriticalSection
> lock(m_critSection
);
286 return m_channelData
->ChannelIconPath();
289 CDateTime
CPVREpgInfoTag::StartAsUTC() const
294 CDateTime
CPVREpgInfoTag::StartAsLocalTime() const
297 retVal
.SetFromUTCDateTime(m_startTime
);
301 CDateTime
CPVREpgInfoTag::EndAsUTC() const
306 CDateTime
CPVREpgInfoTag::EndAsLocalTime() const
309 retVal
.SetFromUTCDateTime(m_endTime
);
313 void CPVREpgInfoTag::SetEndFromUTC(const CDateTime
& end
)
318 int CPVREpgInfoTag::GetDuration() const
321 m_startTime
.GetAsTime(start
);
322 m_endTime
.GetAsTime(end
);
323 return end
- start
> 0 ? end
- start
: 3600;
326 std::string
CPVREpgInfoTag::Title() const
331 std::string
CPVREpgInfoTag::PlotOutline() const
333 return m_strPlotOutline
;
336 std::string
CPVREpgInfoTag::Plot() const
341 std::string
CPVREpgInfoTag::OriginalTitle() const
343 return m_strOriginalTitle
;
346 const std::vector
<std::string
> CPVREpgInfoTag::Cast() const
351 const std::vector
<std::string
> CPVREpgInfoTag::Directors() const
356 const std::vector
<std::string
> CPVREpgInfoTag::Writers() const
361 const std::string
CPVREpgInfoTag::GetCastLabel() const
363 // Note: see CVideoInfoTag::GetCast for reference implementation.
364 std::string strLabel
;
365 for (const auto& castEntry
: m_cast
)
366 strLabel
+= StringUtils::Format("{}\n", castEntry
);
368 return StringUtils::TrimRight(strLabel
, "\n");
371 const std::string
CPVREpgInfoTag::GetDirectorsLabel() const
373 return StringUtils::Join(
375 CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->m_videoItemSeparator
);
378 const std::string
CPVREpgInfoTag::GetWritersLabel() const
380 return StringUtils::Join(
382 CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->m_videoItemSeparator
);
385 const std::string
CPVREpgInfoTag::GetGenresLabel() const
387 return StringUtils::Join(
388 Genre(), CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->m_videoItemSeparator
);
391 int CPVREpgInfoTag::Year() const
396 std::string
CPVREpgInfoTag::IMDBNumber() const
398 return m_strIMDBNumber
;
401 int CPVREpgInfoTag::GenreType() const
406 int CPVREpgInfoTag::GenreSubType() const
408 return m_iGenreSubType
;
411 std::string
CPVREpgInfoTag::GenreDescription() const
413 return m_strGenreDescription
;
416 const std::vector
<std::string
> CPVREpgInfoTag::Genre() const
420 if ((m_iGenreType
== EPG_GENRE_USE_STRING
|| m_iGenreSubType
== EPG_GENRE_USE_STRING
) &&
421 !m_strGenreDescription
.empty())
423 // Type and sub type are both not given. No EPG color coding possible unless sub type is
424 // used to specify EPG_GENRE_USE_STRING leaving type available for genre category, use the
425 // provided genre description for the text.
426 m_genre
= Tokenize(m_strGenreDescription
);
431 // Determine the genre from the type and subtype IDs.
432 m_genre
= Tokenize(CPVREpg::ConvertGenreIdToString(m_iGenreType
, m_iGenreSubType
));
438 CDateTime
CPVREpgInfoTag::FirstAired() const
443 int CPVREpgInfoTag::ParentalRating() const
445 return m_iParentalRating
;
448 std::string
CPVREpgInfoTag::ParentalRatingCode() const
450 return m_strParentalRatingCode
;
453 int CPVREpgInfoTag::StarRating() const
455 return m_iStarRating
;
458 int CPVREpgInfoTag::SeriesNumber() const
460 return m_iSeriesNumber
;
463 std::string
CPVREpgInfoTag::SeriesLink() const
465 return m_strSeriesLink
;
468 int CPVREpgInfoTag::EpisodeNumber() const
470 return m_iEpisodeNumber
;
473 int CPVREpgInfoTag::EpisodePart() const
475 return m_iEpisodePart
;
478 std::string
CPVREpgInfoTag::EpisodeName() const
480 return m_strEpisodeName
;
483 std::string
CPVREpgInfoTag::IconPath() const
485 return m_iconPath
.GetLocalImage();
488 std::string
CPVREpgInfoTag::ClientIconPath() const
490 return m_iconPath
.GetClientImage();
493 std::string
CPVREpgInfoTag::Path() const
495 return StringUtils::Format("pvr://guide/{:04}/{}.epg", EpgID(), m_startTime
.GetAsDBDateTime());
498 bool CPVREpgInfoTag::Update(const CPVREpgInfoTag
& tag
, bool bUpdateBroadcastId
/* = true */)
500 std::unique_lock
<CCriticalSection
> lock(m_critSection
);
502 (m_strTitle
!= tag
.m_strTitle
|| m_strPlotOutline
!= tag
.m_strPlotOutline
||
503 m_strPlot
!= tag
.m_strPlot
|| m_strOriginalTitle
!= tag
.m_strOriginalTitle
||
504 m_cast
!= tag
.m_cast
|| m_directors
!= tag
.m_directors
|| m_writers
!= tag
.m_writers
||
505 m_iYear
!= tag
.m_iYear
|| m_strIMDBNumber
!= tag
.m_strIMDBNumber
||
506 m_startTime
!= tag
.m_startTime
|| m_endTime
!= tag
.m_endTime
||
507 m_iGenreType
!= tag
.m_iGenreType
|| m_iGenreSubType
!= tag
.m_iGenreSubType
||
508 m_strGenreDescription
!= tag
.m_strGenreDescription
|| m_firstAired
!= tag
.m_firstAired
||
509 m_iParentalRating
!= tag
.m_iParentalRating
||
510 m_strParentalRatingCode
!= tag
.m_strParentalRatingCode
||
511 m_iStarRating
!= tag
.m_iStarRating
|| m_iEpisodeNumber
!= tag
.m_iEpisodeNumber
||
512 m_iEpisodePart
!= tag
.m_iEpisodePart
|| m_iSeriesNumber
!= tag
.m_iSeriesNumber
||
513 m_strEpisodeName
!= tag
.m_strEpisodeName
||
514 m_iUniqueBroadcastID
!= tag
.m_iUniqueBroadcastID
|| m_iEpgID
!= tag
.m_iEpgID
||
515 m_genre
!= tag
.m_genre
|| m_iconPath
!= tag
.m_iconPath
|| m_iFlags
!= tag
.m_iFlags
||
516 m_strSeriesLink
!= tag
.m_strSeriesLink
|| m_channelData
!= tag
.m_channelData
);
518 if (bUpdateBroadcastId
)
519 bChanged
|= (m_iDatabaseID
!= tag
.m_iDatabaseID
);
523 if (bUpdateBroadcastId
)
524 m_iDatabaseID
= tag
.m_iDatabaseID
;
526 m_strTitle
= tag
.m_strTitle
;
527 m_strPlotOutline
= tag
.m_strPlotOutline
;
528 m_strPlot
= tag
.m_strPlot
;
529 m_strOriginalTitle
= tag
.m_strOriginalTitle
;
531 m_directors
= tag
.m_directors
;
532 m_writers
= tag
.m_writers
;
533 m_iYear
= tag
.m_iYear
;
534 m_strIMDBNumber
= tag
.m_strIMDBNumber
;
535 m_startTime
= tag
.m_startTime
;
536 m_endTime
= tag
.m_endTime
;
537 m_iGenreType
= tag
.m_iGenreType
;
538 m_iGenreSubType
= tag
.m_iGenreSubType
;
539 m_strGenreDescription
= tag
.m_strGenreDescription
;
540 m_genre
= tag
.m_genre
;
541 m_iEpgID
= tag
.m_iEpgID
;
542 m_iFlags
= tag
.m_iFlags
;
543 m_strSeriesLink
= tag
.m_strSeriesLink
;
544 m_firstAired
= tag
.m_firstAired
;
545 m_iParentalRating
= tag
.m_iParentalRating
;
546 m_strParentalRatingCode
= tag
.m_strParentalRatingCode
;
547 m_iStarRating
= tag
.m_iStarRating
;
548 m_iEpisodeNumber
= tag
.m_iEpisodeNumber
;
549 m_iEpisodePart
= tag
.m_iEpisodePart
;
550 m_iSeriesNumber
= tag
.m_iSeriesNumber
;
551 m_strEpisodeName
= tag
.m_strEpisodeName
;
552 m_iUniqueBroadcastID
= tag
.m_iUniqueBroadcastID
;
553 m_iconPath
= tag
.m_iconPath
;
554 m_channelData
= tag
.m_channelData
;
560 bool CPVREpgInfoTag::QueuePersistQuery(const std::shared_ptr
<CPVREpgDatabase
>& database
)
564 CLog::LogF(LOGERROR
, "Could not open the EPG database");
568 return database
->QueuePersistQuery(*this);
571 std::vector
<PVR_EDL_ENTRY
> CPVREpgInfoTag::GetEdl() const
573 std::vector
<PVR_EDL_ENTRY
> edls
;
575 std::unique_lock
<CCriticalSection
> lock(m_critSection
);
576 const std::shared_ptr
<const CPVRClient
> client
=
577 CServiceBroker::GetPVRManager().GetClient(m_channelData
->ClientId());
579 if (client
&& client
->GetClientCapabilities().SupportsEpgTagEdl())
580 client
->GetEpgTagEdl(shared_from_this(), edls
);
585 int CPVREpgInfoTag::EpgID() const
590 void CPVREpgInfoTag::SetEpgID(int iEpgID
)
593 m_iconPath
.SetOwner(StringUtils::Format(IMAGE_OWNER_PATTERN
, m_iEpgID
));
596 bool CPVREpgInfoTag::IsRecordable() const
598 bool bIsRecordable
= false;
600 std::unique_lock
<CCriticalSection
> lock(m_critSection
);
601 const std::shared_ptr
<const CPVRClient
> client
=
602 CServiceBroker::GetPVRManager().GetClient(m_channelData
->ClientId());
603 if (!client
|| (client
->IsRecordable(shared_from_this(), bIsRecordable
) != PVR_ERROR_NO_ERROR
))
605 // event end time based fallback
606 bIsRecordable
= EndAsLocalTime() > CDateTime::GetCurrentDateTime();
608 return bIsRecordable
;
611 bool CPVREpgInfoTag::IsPlayable() const
613 bool bIsPlayable
= false;
615 std::unique_lock
<CCriticalSection
> lock(m_critSection
);
616 const std::shared_ptr
<const CPVRClient
> client
=
617 CServiceBroker::GetPVRManager().GetClient(m_channelData
->ClientId());
618 if (!client
|| (client
->IsPlayable(shared_from_this(), bIsPlayable
) != PVR_ERROR_NO_ERROR
))
626 bool CPVREpgInfoTag::IsSeries() const
628 if ((m_iFlags
& EPG_TAG_FLAG_IS_SERIES
) > 0 || SeriesNumber() >= 0 || EpisodeNumber() >= 0 ||
635 bool CPVREpgInfoTag::IsRadio() const
637 std::unique_lock
<CCriticalSection
> lock(m_critSection
);
638 return m_channelData
->IsRadio();
641 bool CPVREpgInfoTag::IsParentalLocked() const
643 std::unique_lock
<CCriticalSection
> lock(m_critSection
);
644 return m_channelData
->IsLocked();
647 bool CPVREpgInfoTag::IsGapTag() const
649 std::unique_lock
<CCriticalSection
> lock(m_critSection
);
653 bool CPVREpgInfoTag::IsNew() const
655 return (m_iFlags
& EPG_TAG_FLAG_IS_NEW
) > 0;
658 bool CPVREpgInfoTag::IsPremiere() const
660 return (m_iFlags
& EPG_TAG_FLAG_IS_PREMIERE
) > 0;
663 bool CPVREpgInfoTag::IsFinale() const
665 return (m_iFlags
& EPG_TAG_FLAG_IS_FINALE
) > 0;
668 bool CPVREpgInfoTag::IsLive() const
670 return (m_iFlags
& EPG_TAG_FLAG_IS_LIVE
) > 0;
673 const std::vector
<std::string
> CPVREpgInfoTag::Tokenize(const std::string
& str
)
675 return StringUtils::Split(str
, EPG_STRING_TOKEN_SEPARATOR
);
678 const std::string
CPVREpgInfoTag::DeTokenize(const std::vector
<std::string
>& tokens
)
680 return StringUtils::Join(tokens
, EPG_STRING_TOKEN_SEPARATOR
);