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 "PVRRecording.h"
10
11 #include "ServiceBroker.h"
12 #include "addons/kodi-dev-kit/include/kodi/c-api/addon-instance/pvr/pvr_recordings.h"
13 #include "guilib/LocalizeStrings.h"
14 #include "pvr/PVRManager.h"
15 #include "pvr/addons/PVRClient.h"
16 #include "pvr/channels/PVRChannel.h"
17 #include "pvr/channels/PVRChannelGroupsContainer.h"
18 #include "pvr/epg/Epg.h"
19 #include "pvr/recordings/PVRRecordingsPath.h"
20 #include "pvr/timers/PVRTimerInfoTag.h"
21 #include "pvr/timers/PVRTimers.h"
22 #include "settings/AdvancedSettings.h"
23 #include "settings/SettingsComponent.h"
24 #include "utils/StringUtils.h"
25 #include "utils/Variant.h"
26 #include "utils/log.h"
27 #include "video/VideoDatabase.h"
28
29 #include <memory>
30 #include <string>
31 #include <vector>
32
33 using namespace PVR;
34
CPVRRecordingUid(int iClientId,const std::string & strRecordingId)35 CPVRRecordingUid::CPVRRecordingUid(int iClientId, const std::string& strRecordingId) :
36 m_iClientId(iClientId),
37 m_strRecordingId(strRecordingId)
38 {
39 }
40
operator >(const CPVRRecordingUid & right) const41 bool CPVRRecordingUid::operator >(const CPVRRecordingUid& right) const
42 {
43 return (m_iClientId == right.m_iClientId) ?
44 m_strRecordingId > right.m_strRecordingId :
45 m_iClientId > right.m_iClientId;
46 }
47
operator <(const CPVRRecordingUid & right) const48 bool CPVRRecordingUid::operator <(const CPVRRecordingUid& right) const
49 {
50 return (m_iClientId == right.m_iClientId) ?
51 m_strRecordingId < right.m_strRecordingId :
52 m_iClientId < right.m_iClientId;
53 }
54
operator ==(const CPVRRecordingUid & right) const55 bool CPVRRecordingUid::operator ==(const CPVRRecordingUid& right) const
56 {
57 return m_iClientId == right.m_iClientId && m_strRecordingId == right.m_strRecordingId;
58 }
59
operator !=(const CPVRRecordingUid & right) const60 bool CPVRRecordingUid::operator !=(const CPVRRecordingUid& right) const
61 {
62 return m_iClientId != right.m_iClientId || m_strRecordingId != right.m_strRecordingId;
63 }
64
65
CPVRRecording()66 CPVRRecording::CPVRRecording()
67 {
68 Reset();
69 }
70
CPVRRecording(const PVR_RECORDING & recording,unsigned int iClientId)71 CPVRRecording::CPVRRecording(const PVR_RECORDING& recording, unsigned int iClientId)
72 {
73 Reset();
74
75 m_strRecordingId = recording.strRecordingId;
76 m_strTitle = recording.strTitle;
77 m_strShowTitle = recording.strEpisodeName;
78 m_iSeason = recording.iSeriesNumber;
79 m_iEpisode = recording.iEpisodeNumber;
80 if (recording.iYear > 0)
81 SetYear(recording.iYear);
82 m_iClientId = iClientId;
83 m_recordingTime = recording.recordingTime + CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->m_iPVRTimeCorrection;
84 m_iPriority = recording.iPriority;
85 m_iLifetime = recording.iLifetime;
86 // Deleted recording is placed at the root of the deleted view
87 m_strDirectory = recording.bIsDeleted ? "" : recording.strDirectory;
88 m_strPlot = recording.strPlot;
89 m_strPlotOutline = recording.strPlotOutline;
90 m_strChannelName = recording.strChannelName;
91 m_strIconPath = recording.strIconPath;
92 m_strThumbnailPath = recording.strThumbnailPath;
93 m_strFanartPath = recording.strFanartPath;
94 m_bIsDeleted = recording.bIsDeleted;
95 m_iEpgEventId = recording.iEpgEventId;
96 m_iChannelUid = recording.iChannelUid;
97 if (strlen(recording.strFirstAired) > 0)
98 m_firstAired.SetFromW3CDateTime(recording.strFirstAired);
99 m_iFlags = recording.iFlags;
100 if (recording.sizeInBytes >= 0)
101 m_sizeInBytes = recording.sizeInBytes;
102
103 SetGenre(recording.iGenreType, recording.iGenreSubType, recording.strGenreDescription);
104 CVideoInfoTag::SetPlayCount(recording.iPlayCount);
105 CVideoInfoTag::SetResumePoint(recording.iLastPlayedPosition, recording.iDuration, "");
106 SetDuration(recording.iDuration);
107
108 // As the channel a recording was done on (probably long time ago) might no longer be
109 // available today prefer addon-supplied channel type (tv/radio) over channel attribute.
110 if (recording.channelType != PVR_RECORDING_CHANNEL_TYPE_UNKNOWN)
111 {
112 m_bRadio = recording.channelType == PVR_RECORDING_CHANNEL_TYPE_RADIO;
113 }
114 else
115 {
116 const std::shared_ptr<CPVRChannel> channel(Channel());
117 if (channel)
118 {
119 m_bRadio = channel->IsRadio();
120 }
121 else
122 {
123 const std::shared_ptr<CPVRClient> client = CServiceBroker::GetPVRManager().GetClient(m_iClientId);
124 bool bSupportsRadio = client && client->GetClientCapabilities().SupportsRadio();
125 if (bSupportsRadio && client && client->GetClientCapabilities().SupportsTV())
126 {
127 CLog::Log(LOGWARNING, "Unable to determine channel type. Defaulting to TV.");
128 m_bRadio = false; // Assume TV.
129 }
130 else
131 {
132 m_bRadio = bSupportsRadio;
133 }
134 }
135 }
136
137 UpdatePath();
138 }
139
operator ==(const CPVRRecording & right) const140 bool CPVRRecording::operator ==(const CPVRRecording& right) const
141 {
142 CSingleLock lock(m_critSection);
143 return (this == &right) ||
144 (m_strRecordingId == right.m_strRecordingId &&
145 m_iClientId == right.m_iClientId &&
146 m_strChannelName == right.m_strChannelName &&
147 m_recordingTime == right.m_recordingTime &&
148 GetDuration() == right.GetDuration() &&
149 m_strPlotOutline == right.m_strPlotOutline &&
150 m_strPlot == right.m_strPlot &&
151 m_iPriority == right.m_iPriority &&
152 m_iLifetime == right.m_iLifetime &&
153 m_strDirectory == right.m_strDirectory &&
154 m_strFileNameAndPath == right.m_strFileNameAndPath &&
155 m_strTitle == right.m_strTitle &&
156 m_strShowTitle == right.m_strShowTitle &&
157 m_iSeason == right.m_iSeason &&
158 m_iEpisode == right.m_iEpisode &&
159 GetPremiered() == right.GetPremiered() &&
160 m_strIconPath == right.m_strIconPath &&
161 m_strThumbnailPath == right.m_strThumbnailPath &&
162 m_strFanartPath == right.m_strFanartPath &&
163 m_iRecordingId == right.m_iRecordingId &&
164 m_bIsDeleted == right.m_bIsDeleted &&
165 m_iEpgEventId == right.m_iEpgEventId &&
166 m_iChannelUid == right.m_iChannelUid &&
167 m_bRadio == right.m_bRadio &&
168 m_genre == right.m_genre &&
169 m_iGenreType == right.m_iGenreType &&
170 m_iGenreSubType == right.m_iGenreSubType &&
171 m_firstAired == right.m_firstAired &&
172 m_iFlags == right.m_iFlags &&
173 m_sizeInBytes == right.m_sizeInBytes);
174 }
175
operator !=(const CPVRRecording & right) const176 bool CPVRRecording::operator !=(const CPVRRecording& right) const
177 {
178 return !(*this == right);
179 }
180
Serialize(CVariant & value) const181 void CPVRRecording::Serialize(CVariant& value) const
182 {
183 CVideoInfoTag::Serialize(value);
184
185 value["channel"] = m_strChannelName;
186 value["lifetime"] = m_iLifetime;
187 value["directory"] = m_strDirectory;
188 value["icon"] = m_strIconPath;
189 value["starttime"] = m_recordingTime.IsValid() ? m_recordingTime.GetAsDBDateTime() : "";
190 value["endtime"] = m_recordingTime.IsValid() ? EndTimeAsUTC().GetAsDBDateTime() : "";
191 value["recordingid"] = m_iRecordingId;
192 value["isdeleted"] = m_bIsDeleted;
193 value["epgeventid"] = m_iEpgEventId;
194 value["channeluid"] = m_iChannelUid;
195 value["radio"] = m_bRadio;
196 value["genre"] = m_genre;
197
198 if (!value.isMember("art"))
199 value["art"] = CVariant(CVariant::VariantTypeObject);
200 if (!m_strThumbnailPath.empty())
201 value["art"]["thumb"] = m_strThumbnailPath;
202 if (!m_strFanartPath.empty())
203 value["art"]["fanart"] = m_strFanartPath;
204
205 value["clientid"] = m_iClientId;
206 }
207
ToSortable(SortItem & sortable,Field field) const208 void CPVRRecording::ToSortable(SortItem& sortable, Field field) const
209 {
210 CSingleLock lock(m_critSection);
211 if (field == FieldSize)
212 sortable[FieldSize] = m_sizeInBytes;
213 else
214 CVideoInfoTag::ToSortable(sortable, field);
215 }
216
Reset()217 void CPVRRecording::Reset()
218 {
219 m_strRecordingId .clear();
220 m_iClientId = -1;
221 m_strChannelName .clear();
222 m_strDirectory .clear();
223 m_iPriority = -1;
224 m_iLifetime = -1;
225 m_strFileNameAndPath .clear();
226 m_strIconPath .clear();
227 m_strThumbnailPath .clear();
228 m_strFanartPath .clear();
229 m_bGotMetaData = false;
230 m_iRecordingId = 0;
231 m_bIsDeleted = false;
232 m_iEpgEventId = EPG_TAG_INVALID_UID;
233 m_iSeason = -1;
234 m_iEpisode = -1;
235 m_iChannelUid = PVR_CHANNEL_INVALID_UID;
236 m_bRadio = false;
237 m_iFlags = PVR_RECORDING_FLAG_UNDEFINED;
238 {
239 CSingleLock lock(m_critSection);
240 m_sizeInBytes = 0;
241 }
242
243 m_recordingTime.Reset();
244 CVideoInfoTag::Reset();
245 }
246
Delete()247 bool CPVRRecording::Delete()
248 {
249 std::shared_ptr<CPVRClient> client = CServiceBroker::GetPVRManager().GetClient(m_iClientId);
250 return client && (client->DeleteRecording(*this) == PVR_ERROR_NO_ERROR);
251 }
252
Undelete()253 bool CPVRRecording::Undelete()
254 {
255 const std::shared_ptr<CPVRClient> client = CServiceBroker::GetPVRManager().GetClient(m_iClientId);
256 return client && (client->UndeleteRecording(*this) == PVR_ERROR_NO_ERROR);
257 }
258
Rename(const std::string & strNewName)259 bool CPVRRecording::Rename(const std::string& strNewName)
260 {
261 m_strTitle = strNewName;
262 const std::shared_ptr<CPVRClient> client = CServiceBroker::GetPVRManager().GetClient(m_iClientId);
263 return client && (client->RenameRecording(*this) == PVR_ERROR_NO_ERROR);
264 }
265
SetPlayCount(int count)266 bool CPVRRecording::SetPlayCount(int count)
267 {
268 const std::shared_ptr<CPVRClient> client = CServiceBroker::GetPVRManager().GetClient(m_iClientId);
269 if (client && client->GetClientCapabilities().SupportsRecordingsPlayCount())
270 {
271 if (client->SetRecordingPlayCount(*this, count) != PVR_ERROR_NO_ERROR)
272 return false;
273 }
274
275 return CVideoInfoTag::SetPlayCount(count);
276 }
277
IncrementPlayCount()278 bool CPVRRecording::IncrementPlayCount()
279 {
280 const std::shared_ptr<CPVRClient> client = CServiceBroker::GetPVRManager().GetClient(m_iClientId);
281 if (client && client->GetClientCapabilities().SupportsRecordingsPlayCount())
282 {
283 if (client->SetRecordingPlayCount(*this, CVideoInfoTag::GetPlayCount() + 1) != PVR_ERROR_NO_ERROR)
284 return false;
285 }
286
287 return CVideoInfoTag::IncrementPlayCount();
288 }
289
SetResumePoint(const CBookmark & resumePoint)290 bool CPVRRecording::SetResumePoint(const CBookmark& resumePoint)
291 {
292 const std::shared_ptr<CPVRClient> client = CServiceBroker::GetPVRManager().GetClient(m_iClientId);
293 if (client && client->GetClientCapabilities().SupportsRecordingsLastPlayedPosition())
294 {
295 if (client->SetRecordingLastPlayedPosition(*this, lrint(resumePoint.timeInSeconds)) != PVR_ERROR_NO_ERROR)
296 return false;
297 }
298
299 return CVideoInfoTag::SetResumePoint(resumePoint);
300 }
301
SetResumePoint(double timeInSeconds,double totalTimeInSeconds,const std::string & playerState)302 bool CPVRRecording::SetResumePoint(double timeInSeconds, double totalTimeInSeconds, const std::string& playerState /* = "" */)
303 {
304 const std::shared_ptr<CPVRClient> client = CServiceBroker::GetPVRManager().GetClient(m_iClientId);
305 if (client && client->GetClientCapabilities().SupportsRecordingsLastPlayedPosition())
306 {
307 if (client->SetRecordingLastPlayedPosition(*this, lrint(timeInSeconds)) != PVR_ERROR_NO_ERROR)
308 return false;
309 }
310
311 return CVideoInfoTag::SetResumePoint(timeInSeconds, totalTimeInSeconds, playerState);
312 }
313
GetResumePoint() const314 CBookmark CPVRRecording::GetResumePoint() const
315 {
316 const std::shared_ptr<CPVRClient> client = CServiceBroker::GetPVRManager().GetClient(m_iClientId);
317 if (client && client->GetClientCapabilities().SupportsRecordingsLastPlayedPosition() &&
318 m_resumePointRefetchTimeout.IsTimePast())
319 {
320 // @todo: root cause should be fixed. details: https://github.com/xbmc/xbmc/pull/14961
321 m_resumePointRefetchTimeout.Set(10000); // update resume point from backend at most every 10 secs
322
323 int pos = -1;
324 client->GetRecordingLastPlayedPosition(*this, pos);
325
326 if (pos >= 0)
327 {
328 CBookmark resumePoint(CVideoInfoTag::GetResumePoint());
329 resumePoint.timeInSeconds = pos;
330 CPVRRecording* pThis = const_cast<CPVRRecording*>(this);
331 pThis->CVideoInfoTag::SetResumePoint(resumePoint);
332 }
333 }
334 return CVideoInfoTag::GetResumePoint();
335 }
336
UpdateRecordingSize()337 bool CPVRRecording::UpdateRecordingSize()
338 {
339 const std::shared_ptr<CPVRClient> client = CServiceBroker::GetPVRManager().GetClient(m_iClientId);
340 if (client && client->GetClientCapabilities().SupportsRecordingsSize() &&
341 m_recordingSizeRefetchTimeout.IsTimePast())
342 {
343 // @todo: root cause should be fixed. details: https://github.com/xbmc/xbmc/pull/14961
344 m_recordingSizeRefetchTimeout.Set(10000); // update size from backend at most every 10 secs
345
346 int64_t sizeInBytes = -1;
347 client->GetRecordingSize(*this, sizeInBytes);
348
349 CSingleLock lock(m_critSection);
350 if (sizeInBytes >= 0 && sizeInBytes != m_sizeInBytes)
351 {
352 CSingleLock lock(m_critSection);
353 m_sizeInBytes = sizeInBytes;
354 return true;
355 }
356 }
357
358 return false;
359 }
360
UpdateMetadata(CVideoDatabase & db,const CPVRClient & client)361 void CPVRRecording::UpdateMetadata(CVideoDatabase& db, const CPVRClient& client)
362 {
363 if (m_bGotMetaData || !db.IsOpen())
364 return;
365
366 if (!client.GetClientCapabilities().SupportsRecordingsPlayCount())
367 CVideoInfoTag::SetPlayCount(db.GetPlayCount(m_strFileNameAndPath));
368
369 if (!client.GetClientCapabilities().SupportsRecordingsLastPlayedPosition())
370 {
371 CBookmark resumePoint;
372 if (db.GetResumeBookMark(m_strFileNameAndPath, resumePoint))
373 CVideoInfoTag::SetResumePoint(resumePoint);
374 }
375
376 m_bGotMetaData = true;
377 }
378
GetEdl() const379 std::vector<PVR_EDL_ENTRY> CPVRRecording::GetEdl() const
380 {
381 std::vector<PVR_EDL_ENTRY> edls;
382
383 const std::shared_ptr<CPVRClient> client = CServiceBroker::GetPVRManager().GetClient(m_iClientId);
384 if (client && client->GetClientCapabilities().SupportsRecordingsEdl())
385 client->GetRecordingEdl(*this, edls);
386
387 return edls;
388 }
389
Update(const CPVRRecording & tag,const CPVRClient & client)390 void CPVRRecording::Update(const CPVRRecording& tag, const CPVRClient& client)
391 {
392 m_strRecordingId = tag.m_strRecordingId;
393 m_iClientId = tag.m_iClientId;
394 m_strTitle = tag.m_strTitle;
395 m_strShowTitle = tag.m_strShowTitle;
396 m_iSeason = tag.m_iSeason;
397 m_iEpisode = tag.m_iEpisode;
398 SetPremiered(tag.GetPremiered());
399 m_recordingTime = tag.m_recordingTime;
400 m_iPriority = tag.m_iPriority;
401 m_iLifetime = tag.m_iLifetime;
402 m_strDirectory = tag.m_strDirectory;
403 m_strPlot = tag.m_strPlot;
404 m_strPlotOutline = tag.m_strPlotOutline;
405 m_strChannelName = tag.m_strChannelName;
406 m_genre = tag.m_genre;
407 m_strIconPath = tag.m_strIconPath;
408 m_strThumbnailPath = tag.m_strThumbnailPath;
409 m_strFanartPath = tag.m_strFanartPath;
410 m_bIsDeleted = tag.m_bIsDeleted;
411 m_iEpgEventId = tag.m_iEpgEventId;
412 m_iChannelUid = tag.m_iChannelUid;
413 m_bRadio = tag.m_bRadio;
414 m_firstAired = tag.m_firstAired;
415 m_iFlags = tag.m_iFlags;
416 {
417 CSingleLock lock(m_critSection);
418 m_sizeInBytes = tag.m_sizeInBytes;
419 }
420
421 if (client.GetClientCapabilities().SupportsRecordingsPlayCount())
422 CVideoInfoTag::SetPlayCount(tag.GetLocalPlayCount());
423
424 if (client.GetClientCapabilities().SupportsRecordingsLastPlayedPosition())
425 CVideoInfoTag::SetResumePoint(tag.GetLocalResumePoint());
426
427 SetDuration(tag.GetDuration());
428
429 if (m_iGenreType == EPG_GENRE_USE_STRING || m_iGenreSubType == EPG_GENRE_USE_STRING)
430 {
431 /* No type/subtype. Use the provided description */
432 m_genre = tag.m_genre;
433 }
434 else
435 {
436 /* Determine genre description by type/subtype */
437 m_genre = StringUtils::Split(CPVREpg::ConvertGenreIdToString(tag.m_iGenreType, tag.m_iGenreSubType), CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->m_videoItemSeparator);
438 }
439
440 //Old Method of identifying TV show title and subtitle using m_strDirectory and strPlotOutline (deprecated)
441 std::string strShow = StringUtils::Format("%s - ", g_localizeStrings.Get(20364).c_str());
442 if (StringUtils::StartsWithNoCase(m_strPlotOutline, strShow))
443 {
444 CLog::Log(LOGWARNING, "PVR addon provides episode name in strPlotOutline which is deprecated");
445 std::string strEpisode = m_strPlotOutline;
446 std::string strTitle = m_strDirectory;
447
448 size_t pos = strTitle.rfind('/');
449 strTitle.erase(0, pos + 1);
450 strEpisode.erase(0, strShow.size());
451 m_strTitle = strTitle;
452 pos = strEpisode.find('-');
453 strEpisode.erase(0, pos + 2);
454 m_strShowTitle = strEpisode;
455 }
456
457 UpdatePath();
458 }
459
UpdatePath()460 void CPVRRecording::UpdatePath()
461 {
462 m_strFileNameAndPath = CPVRRecordingsPath(
463 m_bIsDeleted, m_bRadio, m_strDirectory, m_strTitle, m_iSeason, m_iEpisode, GetYear(), m_strShowTitle, m_strChannelName, m_recordingTime, m_strRecordingId);
464 }
465
RecordingTimeAsLocalTime() const466 const CDateTime& CPVRRecording::RecordingTimeAsLocalTime() const
467 {
468 static CDateTime tmp;
469 tmp.SetFromUTCDateTime(m_recordingTime);
470
471 return tmp;
472 }
473
EndTimeAsUTC() const474 CDateTime CPVRRecording::EndTimeAsUTC() const
475 {
476 unsigned int duration = GetDuration();
477 return m_recordingTime + CDateTimeSpan(0, 0, duration / 60, duration % 60);
478 }
479
EndTimeAsLocalTime() const480 CDateTime CPVRRecording::EndTimeAsLocalTime() const
481 {
482 CDateTime ret;
483 ret.SetFromUTCDateTime(EndTimeAsUTC());
484 return ret;
485 }
486
WillBeExpiredWithNewLifetime(int iLifetime) const487 bool CPVRRecording::WillBeExpiredWithNewLifetime(int iLifetime) const
488 {
489 if (iLifetime > 0)
490 return (EndTimeAsUTC() + CDateTimeSpan(iLifetime, 0, 0, 0)) <= CDateTime::GetUTCDateTime();
491
492 return false;
493 }
494
ExpirationTimeAsLocalTime() const495 CDateTime CPVRRecording::ExpirationTimeAsLocalTime() const
496 {
497 CDateTime ret;
498 if (m_iLifetime > 0)
499 ret = EndTimeAsLocalTime() + CDateTimeSpan(m_iLifetime, 0, 0, 0);
500
501 return ret;
502 }
503
GetTitleFromURL(const std::string & url)504 std::string CPVRRecording::GetTitleFromURL(const std::string& url)
505 {
506 return CPVRRecordingsPath(url).GetTitle();
507 }
508
Channel() const509 std::shared_ptr<CPVRChannel> CPVRRecording::Channel() const
510 {
511 if (m_iChannelUid != PVR_CHANNEL_INVALID_UID)
512 return CServiceBroker::GetPVRManager().ChannelGroups()->GetByUniqueID(m_iChannelUid, m_iClientId);
513
514 return std::shared_ptr<CPVRChannel>();
515 }
516
ChannelUid() const517 int CPVRRecording::ChannelUid() const
518 {
519 return m_iChannelUid;
520 }
521
ClientID() const522 int CPVRRecording::ClientID() const
523 {
524 return m_iClientId;
525 }
526
GetRecordingTimer() const527 std::shared_ptr<CPVRTimerInfoTag> CPVRRecording::GetRecordingTimer() const
528 {
529 const std::vector<std::shared_ptr<CPVRTimerInfoTag>> recordingTimers = CServiceBroker::GetPVRManager().Timers()->GetActiveRecordings();
530
531 for (const auto& timer : recordingTimers)
532 {
533 if (timer->m_iClientId == ClientID() &&
534 timer->m_iClientChannelUid == ChannelUid())
535 {
536 // first, match epg event uids, if available
537 if (timer->UniqueBroadcastID() == BroadcastUid() &&
538 timer->UniqueBroadcastID() != EPG_TAG_INVALID_UID)
539 return timer;
540
541 // alternatively, match start and end times
542 const CDateTime timerStart = timer->StartAsUTC() - CDateTimeSpan(0, 0, timer->m_iMarginStart, 0);
543 const CDateTime timerEnd = timer->EndAsUTC() + CDateTimeSpan(0, 0, timer->m_iMarginEnd, 0);
544 if (timerStart <= RecordingTimeAsUTC() &&
545 timerEnd >= EndTimeAsUTC())
546 return timer;
547 }
548 }
549 return {};
550 }
551
IsInProgress() const552 bool CPVRRecording::IsInProgress() const
553 {
554 // Note: It is not enough to only check recording time and duration against 'now'.
555 // Only the state of the related timer is a safe indicator that the backend
556 // actually is recording this.
557
558 return GetRecordingTimer() != nullptr;
559 }
560
SetGenre(int iGenreType,int iGenreSubType,const std::string & strGenre)561 void CPVRRecording::SetGenre(int iGenreType, int iGenreSubType, const std::string& strGenre)
562 {
563 m_iGenreType = iGenreType;
564 m_iGenreSubType = iGenreSubType;
565
566 if ((iGenreType == EPG_GENRE_USE_STRING || iGenreSubType == EPG_GENRE_USE_STRING) && !strGenre.empty())
567 {
568 /* Type and sub type are not given. Use the provided genre description if available. */
569 m_genre = StringUtils::Split(strGenre, CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->m_videoItemSeparator);
570 }
571 else
572 {
573 /* Determine the genre description from the type and subtype IDs */
574 m_genre = StringUtils::Split(CPVREpg::ConvertGenreIdToString(iGenreType, iGenreSubType), CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->m_videoItemSeparator);
575 }
576 }
577
GetGenresLabel() const578 const std::string CPVRRecording::GetGenresLabel() const
579 {
580 return StringUtils::Join(m_genre, CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->m_videoItemSeparator);
581 }
582
FirstAired() const583 CDateTime CPVRRecording::FirstAired() const
584 {
585 return m_firstAired;
586 }
587
SetYear(int year)588 void CPVRRecording::SetYear(int year)
589 {
590 if (year > 0)
591 m_premiered = CDateTime(year, 1, 1, 0, 0, 0);
592 }
593
GetYear() const594 int CPVRRecording::GetYear() const
595 {
596 return m_premiered.GetYear();
597 }
598
HasYear() const599 bool CPVRRecording::HasYear() const
600 {
601 return m_premiered.IsValid();
602 }
603
IsNew() const604 bool CPVRRecording::IsNew() const
605 {
606 return (m_iFlags & PVR_RECORDING_FLAG_IS_NEW) > 0;
607 }
608
IsPremiere() const609 bool CPVRRecording::IsPremiere() const
610 {
611 return (m_iFlags & PVR_RECORDING_FLAG_IS_PREMIERE) > 0;
612 }
613
IsLive() const614 bool CPVRRecording::IsLive() const
615 {
616 return (m_iFlags & PVR_RECORDING_FLAG_IS_LIVE) > 0;
617 }
618
IsFinale() const619 bool CPVRRecording::IsFinale() const
620 {
621 return (m_iFlags & PVR_RECORDING_FLAG_IS_FINALE) > 0;
622 }
623
GetSizeInBytes() const624 int64_t CPVRRecording::GetSizeInBytes() const
625 {
626 CSingleLock lock(m_critSection);
627 return m_sizeInBytes;
628 }
629