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 "PVRRecordings.h"
11 #include "ServiceBroker.h"
12 #include "addons/kodi-dev-kit/include/kodi/c-api/addon-instance/pvr/pvr_epg.h" // EPG_TAG_INVALID_UID
13 #include "pvr/PVRCachedImages.h"
14 #include "pvr/PVRManager.h"
15 #include "pvr/addons/PVRClients.h"
16 #include "pvr/epg/EpgInfoTag.h"
17 #include "pvr/recordings/PVRRecording.h"
18 #include "pvr/recordings/PVRRecordingsPath.h"
19 #include "utils/URIUtils.h"
20 #include "utils/log.h"
21 #include "video/VideoDatabase.h"
32 CPVRRecordings::CPVRRecordings() = default;
34 CPVRRecordings::~CPVRRecordings()
36 if (m_database
&& m_database
->IsOpen())
40 bool CPVRRecordings::UpdateFromClients(const std::vector
<std::shared_ptr
<CPVRClient
>>& clients
)
42 std::unique_lock
<CCriticalSection
> lock(m_critSection
);
49 for (const auto& recording
: m_recordings
)
50 recording
.second
->SetDirty(true);
52 std::vector
<int> failedClients
;
53 CServiceBroker::GetPVRManager().Clients()->GetRecordings(clients
, this, false, failedClients
);
54 CServiceBroker::GetPVRManager().Clients()->GetRecordings(clients
, this, true, failedClients
);
56 // remove recordings that were deleted at the backend
57 for (auto it
= m_recordings
.begin(); it
!= m_recordings
.end();)
59 if ((*it
).second
->IsDirty() && std::find(failedClients
.begin(), failedClients
.end(),
60 (*it
).second
->ClientID()) == failedClients
.end())
61 it
= m_recordings
.erase(it
);
66 m_bIsUpdating
= false;
67 CServiceBroker::GetPVRManager().PublishEvent(PVREvent::RecordingsInvalidated
);
71 bool CPVRRecordings::Update(const std::vector
<std::shared_ptr
<CPVRClient
>>& clients
)
73 return UpdateFromClients(clients
);
76 void CPVRRecordings::Unload()
78 std::unique_lock
<CCriticalSection
> lock(m_critSection
);
79 m_bDeletedTVRecordings
= false;
80 m_bDeletedRadioRecordings
= false;
82 m_iRadioRecordings
= 0;
86 void CPVRRecordings::UpdateInProgressSize()
88 std::unique_lock
<CCriticalSection
> lock(m_critSection
);
93 bool bHaveUpdatedInProgessRecording
= false;
94 for (auto& recording
: m_recordings
)
96 if (recording
.second
->IsInProgress())
98 if (recording
.second
->UpdateRecordingSize())
99 bHaveUpdatedInProgessRecording
= true;
103 m_bIsUpdating
= false;
105 if (bHaveUpdatedInProgessRecording
)
106 CServiceBroker::GetPVRManager().PublishEvent(PVREvent::RecordingsInvalidated
);
109 int CPVRRecordings::GetNumTVRecordings() const
111 std::unique_lock
<CCriticalSection
> lock(m_critSection
);
112 return m_iTVRecordings
;
115 bool CPVRRecordings::HasDeletedTVRecordings() const
117 std::unique_lock
<CCriticalSection
> lock(m_critSection
);
118 return m_bDeletedTVRecordings
;
121 int CPVRRecordings::GetNumRadioRecordings() const
123 std::unique_lock
<CCriticalSection
> lock(m_critSection
);
124 return m_iRadioRecordings
;
127 bool CPVRRecordings::HasDeletedRadioRecordings() const
129 std::unique_lock
<CCriticalSection
> lock(m_critSection
);
130 return m_bDeletedRadioRecordings
;
133 std::vector
<std::shared_ptr
<CPVRRecording
>> CPVRRecordings::GetAll() const
135 std::vector
<std::shared_ptr
<CPVRRecording
>> recordings
;
137 std::unique_lock
<CCriticalSection
> lock(m_critSection
);
138 std::transform(m_recordings
.cbegin(), m_recordings
.cend(), std::back_inserter(recordings
),
139 [](const auto& recordingEntry
) { return recordingEntry
.second
; });
144 std::shared_ptr
<CPVRRecording
> CPVRRecordings::GetById(unsigned int iId
) const
146 std::unique_lock
<CCriticalSection
> lock(m_critSection
);
148 std::find_if(m_recordings
.cbegin(), m_recordings
.cend(),
149 [iId
](const auto& recording
) { return recording
.second
->RecordingID() == iId
; });
150 return it
!= m_recordings
.cend() ? (*it
).second
: std::shared_ptr
<CPVRRecording
>();
153 std::shared_ptr
<CPVRRecording
> CPVRRecordings::GetByPath(const std::string
& path
) const
155 std::unique_lock
<CCriticalSection
> lock(m_critSection
);
157 CPVRRecordingsPath
recPath(path
);
158 if (recPath
.IsValid())
160 bool bDeleted
= recPath
.IsDeleted();
161 bool bRadio
= recPath
.IsRadio();
163 for (const auto& recording
: m_recordings
)
165 std::shared_ptr
<CPVRRecording
> current
= recording
.second
;
166 // Omit recordings not matching criteria
167 if (!URIUtils::PathEquals(path
, current
->m_strFileNameAndPath
) ||
168 bDeleted
!= current
->IsDeleted() || bRadio
!= current
->IsRadio())
178 std::shared_ptr
<CPVRRecording
> CPVRRecordings::GetById(int iClientId
,
179 const std::string
& strRecordingId
) const
181 std::shared_ptr
<CPVRRecording
> retVal
;
182 std::unique_lock
<CCriticalSection
> lock(m_critSection
);
183 const auto it
= m_recordings
.find(CPVRRecordingUid(iClientId
, strRecordingId
));
184 if (it
!= m_recordings
.end())
190 void CPVRRecordings::UpdateFromClient(const std::shared_ptr
<CPVRRecording
>& tag
,
191 const CPVRClient
& client
)
193 std::unique_lock
<CCriticalSection
> lock(m_critSection
);
195 if (tag
->IsDeleted())
198 m_bDeletedRadioRecordings
= true;
200 m_bDeletedTVRecordings
= true;
203 std::shared_ptr
<CPVRRecording
> existingTag
= GetById(tag
->ClientID(), tag
->ClientRecordingID());
206 existingTag
->Update(*tag
, client
);
207 existingTag
->SetDirty(false);
211 tag
->UpdateMetadata(GetVideoDatabase(), client
);
212 tag
->SetRecordingID(++m_iLastId
);
213 m_recordings
.insert({CPVRRecordingUid(tag
->ClientID(), tag
->ClientRecordingID()), tag
});
215 ++m_iRadioRecordings
;
221 std::shared_ptr
<CPVRRecording
> CPVRRecordings::GetRecordingForEpgTag(
222 const std::shared_ptr
<const CPVREpgInfoTag
>& epgTag
) const
227 std::unique_lock
<CCriticalSection
> lock(m_critSection
);
229 for (const auto& recording
: m_recordings
)
231 if (recording
.second
->IsDeleted())
234 if (recording
.second
->ClientID() != epgTag
->ClientID())
237 if (recording
.second
->ChannelUid() != epgTag
->UniqueChannelID())
240 unsigned int iEpgEvent
= recording
.second
->BroadcastUid();
241 if (iEpgEvent
!= EPG_TAG_INVALID_UID
)
243 if (iEpgEvent
== epgTag
->UniqueBroadcastID())
244 return recording
.second
;
248 if (recording
.second
->RecordingTimeAsUTC() <= epgTag
->StartAsUTC() &&
249 recording
.second
->EndTimeAsUTC() >= epgTag
->EndAsUTC())
250 return recording
.second
;
254 return std::shared_ptr
<CPVRRecording
>();
257 bool CPVRRecordings::SetRecordingsPlayCount(const std::shared_ptr
<CPVRRecording
>& recording
,
260 return ChangeRecordingsPlayCount(recording
, count
);
263 bool CPVRRecordings::IncrementRecordingsPlayCount(const std::shared_ptr
<CPVRRecording
>& recording
)
265 return ChangeRecordingsPlayCount(recording
, INCREMENT_PLAY_COUNT
);
268 bool CPVRRecordings::ChangeRecordingsPlayCount(const std::shared_ptr
<CPVRRecording
>& recording
,
273 std::unique_lock
<CCriticalSection
> lock(m_critSection
);
275 CVideoDatabase
& db
= GetVideoDatabase();
278 if (count
== INCREMENT_PLAY_COUNT
)
279 recording
->IncrementPlayCount();
281 recording
->SetPlayCount(count
);
283 // Clear resume bookmark
284 if (recording
->GetPlayCount() > 0)
286 db
.ClearBookMarksOfFile(recording
->m_strFileNameAndPath
, CBookmark::RESUME
);
287 recording
->SetResumePoint(CBookmark());
290 CServiceBroker::GetPVRManager().PublishEvent(PVREvent::RecordingsInvalidated
);
298 bool CPVRRecordings::MarkWatched(const std::shared_ptr
<CPVRRecording
>& recording
, bool bWatched
)
301 return IncrementRecordingsPlayCount(recording
);
303 return SetRecordingsPlayCount(recording
, 0);
306 bool CPVRRecordings::ResetResumePoint(const std::shared_ptr
<CPVRRecording
>& recording
)
308 bool bResult
= false;
312 std::unique_lock
<CCriticalSection
> lock(m_critSection
);
314 CVideoDatabase
& db
= GetVideoDatabase();
319 db
.ClearBookMarksOfFile(recording
->m_strFileNameAndPath
, CBookmark::RESUME
);
320 recording
->SetResumePoint(CBookmark());
322 CServiceBroker::GetPVRManager().PublishEvent(PVREvent::RecordingsInvalidated
);
328 CVideoDatabase
& CPVRRecordings::GetVideoDatabase()
332 m_database
= std::make_unique
<CVideoDatabase
>();
335 if (!m_database
->IsOpen())
336 CLog::LogF(LOGERROR
, "Failed to open the video database");
342 int CPVRRecordings::CleanupCachedImages()
344 std::vector
<std::string
> urlsToCheck
;
346 std::unique_lock
<CCriticalSection
> lock(m_critSection
);
347 for (const auto& recording
: m_recordings
)
349 urlsToCheck
.emplace_back(recording
.second
->ClientIconPath());
350 urlsToCheck
.emplace_back(recording
.second
->ClientThumbnailPath());
351 urlsToCheck
.emplace_back(recording
.second
->ClientFanartPath());
352 urlsToCheck
.emplace_back(recording
.second
->m_strFileNameAndPath
);
356 static const std::vector
<PVRImagePattern
> urlPatterns
= {
357 {CPVRRecording::IMAGE_OWNER_PATTERN
, ""}, // client-supplied icon, thumbnail, fanart
358 {"video", "pvr://recordings/"}, // kodi-generated video thumbnail
360 return CPVRCachedImages::Cleanup(urlPatterns
, urlsToCheck
);