[video] fix selection after changing video or extra art
[xbmc.git] / xbmc / pvr / recordings / PVRRecordings.cpp
blobb6049ee6fb6b7e8d8687907eb2c113f539922172
1 /*
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.
7 */
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"
23 #include <algorithm>
24 #include <iterator>
25 #include <memory>
26 #include <mutex>
27 #include <utility>
28 #include <vector>
30 using namespace PVR;
32 CPVRRecordings::CPVRRecordings() = default;
34 CPVRRecordings::~CPVRRecordings()
36 if (m_database && m_database->IsOpen())
37 m_database->Close();
40 bool CPVRRecordings::UpdateFromClients(const std::vector<std::shared_ptr<CPVRClient>>& clients)
42 std::unique_lock<CCriticalSection> lock(m_critSection);
44 if (m_bIsUpdating)
45 return false;
47 m_bIsUpdating = true;
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);
62 else
63 ++it;
66 m_bIsUpdating = false;
67 CServiceBroker::GetPVRManager().PublishEvent(PVREvent::RecordingsInvalidated);
68 return true;
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;
81 m_iTVRecordings = 0;
82 m_iRadioRecordings = 0;
83 m_recordings.clear();
86 void CPVRRecordings::UpdateInProgressSize()
88 std::unique_lock<CCriticalSection> lock(m_critSection);
89 if (m_bIsUpdating)
90 return;
91 m_bIsUpdating = true;
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; });
141 return recordings;
144 std::shared_ptr<CPVRRecording> CPVRRecordings::GetById(unsigned int iId) const
146 std::unique_lock<CCriticalSection> lock(m_critSection);
147 const auto it =
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())
169 continue;
171 return current;
175 return {};
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())
185 retVal = it->second;
187 return retVal;
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())
197 if (tag->IsRadio())
198 m_bDeletedRadioRecordings = true;
199 else
200 m_bDeletedTVRecordings = true;
203 std::shared_ptr<CPVRRecording> existingTag = GetById(tag->ClientID(), tag->ClientRecordingID());
204 if (existingTag)
206 existingTag->Update(*tag, client);
207 existingTag->SetDirty(false);
209 else
211 tag->UpdateMetadata(GetVideoDatabase(), client);
212 tag->SetRecordingID(++m_iLastId);
213 m_recordings.insert({CPVRRecordingUid(tag->ClientID(), tag->ClientRecordingID()), tag});
214 if (tag->IsRadio())
215 ++m_iRadioRecordings;
216 else
217 ++m_iTVRecordings;
221 std::shared_ptr<CPVRRecording> CPVRRecordings::GetRecordingForEpgTag(
222 const std::shared_ptr<const CPVREpgInfoTag>& epgTag) const
224 if (!epgTag)
225 return {};
227 std::unique_lock<CCriticalSection> lock(m_critSection);
229 for (const auto& recording : m_recordings)
231 if (recording.second->IsDeleted())
232 continue;
234 if (recording.second->ClientID() != epgTag->ClientID())
235 continue;
237 if (recording.second->ChannelUid() != epgTag->UniqueChannelID())
238 continue;
240 unsigned int iEpgEvent = recording.second->BroadcastUid();
241 if (iEpgEvent != EPG_TAG_INVALID_UID)
243 if (iEpgEvent == epgTag->UniqueBroadcastID())
244 return recording.second;
246 else
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,
258 int count)
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,
269 int count)
271 if (recording)
273 std::unique_lock<CCriticalSection> lock(m_critSection);
275 CVideoDatabase& db = GetVideoDatabase();
276 if (db.IsOpen())
278 if (count == INCREMENT_PLAY_COUNT)
279 recording->IncrementPlayCount();
280 else
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);
291 return true;
295 return false;
298 bool CPVRRecordings::MarkWatched(const std::shared_ptr<CPVRRecording>& recording, bool bWatched)
300 if (bWatched)
301 return IncrementRecordingsPlayCount(recording);
302 else
303 return SetRecordingsPlayCount(recording, 0);
306 bool CPVRRecordings::ResetResumePoint(const std::shared_ptr<CPVRRecording>& recording)
308 bool bResult = false;
310 if (recording)
312 std::unique_lock<CCriticalSection> lock(m_critSection);
314 CVideoDatabase& db = GetVideoDatabase();
315 if (db.IsOpen())
317 bResult = true;
319 db.ClearBookMarksOfFile(recording->m_strFileNameAndPath, CBookmark::RESUME);
320 recording->SetResumePoint(CBookmark());
322 CServiceBroker::GetPVRManager().PublishEvent(PVREvent::RecordingsInvalidated);
325 return bResult;
328 CVideoDatabase& CPVRRecordings::GetVideoDatabase()
330 if (!m_database)
332 m_database = std::make_unique<CVideoDatabase>();
333 m_database->Open();
335 if (!m_database->IsOpen())
336 CLog::LogF(LOGERROR, "Failed to open the video database");
339 return *m_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);