Merge pull request #25614 from garbear/show-version
[xbmc.git] / xbmc / pvr / guilib / PVRGUIActionsRecordings.cpp
blob71c991ee9967c5ed63f79e55ba04da74a6231443
1 /*
2 * Copyright (C) 2016-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 "PVRGUIActionsRecordings.h"
11 #include "FileItem.h"
12 #include "FileItemList.h"
13 #include "ServiceBroker.h"
14 #include "Util.h"
15 #include "dialogs/GUIDialogBusy.h"
16 #include "dialogs/GUIDialogYesNo.h"
17 #include "filesystem/IDirectory.h"
18 #include "guilib/GUIComponent.h"
19 #include "guilib/GUIWindowManager.h"
20 #include "guilib/LocalizeStrings.h"
21 #include "guilib/WindowIDs.h"
22 #include "messaging/helpers/DialogHelper.h"
23 #include "messaging/helpers/DialogOKHelper.h"
24 #include "pvr/PVREventLogJob.h"
25 #include "pvr/PVRItem.h"
26 #include "pvr/PVRManager.h"
27 #include "pvr/addons/PVRClient.h"
28 #include "pvr/addons/PVRClients.h"
29 #include "pvr/dialogs/GUIDialogPVRRecordingInfo.h"
30 #include "pvr/dialogs/GUIDialogPVRRecordingSettings.h"
31 #include "pvr/recordings/PVRRecording.h"
32 #include "pvr/recordings/PVRRecordings.h"
33 #include "settings/Settings.h"
34 #include "threads/IRunnable.h"
35 #include "utils/StringUtils.h"
36 #include "utils/Variant.h"
37 #include "utils/log.h"
39 #include <memory>
40 #include <numeric>
41 #include <string>
43 using namespace PVR;
44 using namespace KODI::MESSAGING;
46 namespace
48 enum PVRRECORD_DELETE_AFTER_WATCH
50 // Values must match those defined in settings.xml -> pvrrecord.deleteafterwatch
51 NO = 0,
52 ASK = 1,
53 YES = 2,
56 class AsyncRecordingAction : private IRunnable
58 public:
59 bool Execute(const CFileItem& item);
61 protected:
62 AsyncRecordingAction() = default;
64 private:
65 // IRunnable implementation
66 void Run() override;
68 // the worker function
69 virtual bool DoRun(const std::shared_ptr<CFileItem>& item) = 0;
71 std::shared_ptr<CFileItem> m_item;
72 bool m_bSuccess = false;
75 bool AsyncRecordingAction::Execute(const CFileItem& item)
77 m_item = std::make_shared<CFileItem>(item);
78 CGUIDialogBusy::Wait(this, 100, false);
79 return m_bSuccess;
82 void AsyncRecordingAction::Run()
84 m_bSuccess = DoRun(m_item);
86 if (m_bSuccess)
87 CServiceBroker::GetPVRManager().TriggerRecordingsUpdate();
90 class AsyncRenameRecording : public AsyncRecordingAction
92 public:
93 explicit AsyncRenameRecording(const std::string& strNewName) : m_strNewName(strNewName) {}
95 private:
96 bool DoRun(const std::shared_ptr<CFileItem>& item) override
98 if (item->IsUsablePVRRecording())
100 return item->GetPVRRecordingInfoTag()->Rename(m_strNewName);
102 else
104 CLog::LogF(LOGERROR, "Cannot rename item '{}': no valid recording tag", item->GetPath());
105 return false;
108 std::string m_strNewName;
111 class AsyncDeleteRecording : public AsyncRecordingAction
113 public:
114 explicit AsyncDeleteRecording(bool bWatchedOnly = false) : m_bWatchedOnly(bWatchedOnly) {}
116 private:
117 bool DoRun(const std::shared_ptr<CFileItem>& item) override
119 CFileItemList items;
120 if (item->m_bIsFolder)
122 CUtil::GetRecursiveListing(item->GetPath(), items, "", XFILE::DIR_FLAG_NO_FILE_INFO);
124 else
126 items.Add(item);
129 const auto recordings{CServiceBroker::GetPVRManager().Recordings()};
130 return std::accumulate(
131 items.cbegin(), items.cend(), true,
132 [this, &recordings](bool success, const auto& itemToDelete)
134 return (itemToDelete->IsPVRRecording() &&
135 (!m_bWatchedOnly || itemToDelete->GetPVRRecordingInfoTag()->GetPlayCount() > 0) &&
136 !recordings->DeleteRecording(itemToDelete->GetPVRRecordingInfoTag()))
137 ? false
138 : success;
141 bool m_bWatchedOnly = false;
144 class AsyncEmptyRecordingsTrash : public AsyncRecordingAction
146 private:
147 bool DoRun(const std::shared_ptr<CFileItem>& item) override
149 return CServiceBroker::GetPVRManager().Clients()->DeleteAllRecordingsFromTrash() ==
150 PVR_ERROR_NO_ERROR;
154 class AsyncUndeleteRecording : public AsyncRecordingAction
156 private:
157 bool DoRun(const std::shared_ptr<CFileItem>& item) override
159 if (item->IsDeletedPVRRecording())
161 return item->GetPVRRecordingInfoTag()->Undelete();
163 else
165 CLog::LogF(LOGERROR, "Cannot undelete item '{}': no valid recording tag", item->GetPath());
166 return false;
171 class AsyncSetRecordingPlayCount : public AsyncRecordingAction
173 private:
174 bool DoRun(const std::shared_ptr<CFileItem>& item) override
176 const std::shared_ptr<CPVRClient> client = CServiceBroker::GetPVRManager().GetClient(*item);
177 if (client)
179 const std::shared_ptr<const CPVRRecording> recording = item->GetPVRRecordingInfoTag();
180 return client->SetRecordingPlayCount(*recording, recording->GetLocalPlayCount()) ==
181 PVR_ERROR_NO_ERROR;
183 return false;
187 class AsyncSetRecordingLifetime : public AsyncRecordingAction
189 private:
190 bool DoRun(const std::shared_ptr<CFileItem>& item) override
192 const std::shared_ptr<CPVRClient> client = CServiceBroker::GetPVRManager().GetClient(*item);
193 if (client)
194 return client->SetRecordingLifetime(*item->GetPVRRecordingInfoTag()) == PVR_ERROR_NO_ERROR;
195 return false;
199 } // unnamed namespace
201 CPVRGUIActionsRecordings::CPVRGUIActionsRecordings()
202 : m_settings({CSettings::SETTING_PVRRECORD_DELETEAFTERWATCH})
206 bool CPVRGUIActionsRecordings::ShowRecordingInfo(const CFileItem& item) const
208 if (!item.IsPVRRecording())
210 CLog::LogF(LOGERROR, "No recording!");
211 return false;
214 CGUIDialogPVRRecordingInfo* pDlgInfo =
215 CServiceBroker::GetGUI()->GetWindowManager().GetWindow<CGUIDialogPVRRecordingInfo>(
216 WINDOW_DIALOG_PVR_RECORDING_INFO);
217 if (!pDlgInfo)
219 CLog::LogF(LOGERROR, "Unable to get WINDOW_DIALOG_PVR_RECORDING_INFO!");
220 return false;
223 pDlgInfo->SetRecording(item);
224 pDlgInfo->Open();
225 return true;
228 bool CPVRGUIActionsRecordings::EditRecording(const CFileItem& item) const
230 const std::shared_ptr<CPVRRecording> recording = CPVRItem(item).GetRecording();
231 if (!recording)
233 CLog::LogF(LOGERROR, "No recording!");
234 return false;
237 std::shared_ptr<CPVRRecording> origRecording(new CPVRRecording);
238 origRecording->Update(*recording,
239 *CServiceBroker::GetPVRManager().GetClient(recording->ClientID()));
241 if (!ShowRecordingSettings(recording))
242 return false;
244 if (origRecording->m_strTitle != recording->m_strTitle)
246 if (!AsyncRenameRecording(recording->m_strTitle).Execute(item))
247 CLog::LogF(LOGERROR, "Renaming recording failed!");
250 if (origRecording->GetLocalPlayCount() != recording->GetLocalPlayCount())
252 if (!AsyncSetRecordingPlayCount().Execute(item))
253 CLog::LogF(LOGERROR, "Setting recording playcount failed!");
256 if (origRecording->LifeTime() != recording->LifeTime())
258 if (!AsyncSetRecordingLifetime().Execute(item))
259 CLog::LogF(LOGERROR, "Setting recording lifetime failed!");
262 return true;
265 bool CPVRGUIActionsRecordings::CanEditRecording(const CFileItem& item) const
267 return CGUIDialogPVRRecordingSettings::CanEditRecording(item);
270 bool CPVRGUIActionsRecordings::DeleteRecording(const CFileItem& item) const
272 if ((!item.IsPVRRecording() && !item.m_bIsFolder) || item.IsParentFolder())
273 return false;
275 if (!ConfirmDeleteRecording(item))
276 return false;
278 if (!AsyncDeleteRecording().Execute(item))
280 HELPERS::ShowOKDialogText(CVariant{257}, CVariant{19111}); // "Error", "PVR backend error."
281 return false;
284 return true;
287 bool CPVRGUIActionsRecordings::ConfirmDeleteRecording(const CFileItem& item) const
289 return CGUIDialogYesNo::ShowAndGetInput(
290 CVariant{122}, // "Confirm delete"
291 item.m_bIsFolder
292 ? CVariant{19113} // "Delete all recordings in this folder?"
293 : item.GetPVRRecordingInfoTag()->IsDeleted()
294 ? CVariant{19294}
295 // "Remove this deleted recording from trash? This operation cannot be reverted."
296 : CVariant{19112}, // "Delete this recording?"
297 CVariant{""}, CVariant{item.GetLabel()});
300 bool CPVRGUIActionsRecordings::DeleteWatchedRecordings(const CFileItem& item) const
302 if (!item.m_bIsFolder || item.IsParentFolder())
303 return false;
305 if (!ConfirmDeleteWatchedRecordings(item))
306 return false;
308 if (!AsyncDeleteRecording(true).Execute(item))
310 HELPERS::ShowOKDialogText(CVariant{257}, CVariant{19111}); // "Error", "PVR backend error."
311 return false;
314 return true;
317 bool CPVRGUIActionsRecordings::ConfirmDeleteWatchedRecordings(const CFileItem& item) const
319 return CGUIDialogYesNo::ShowAndGetInput(
320 CVariant{122}, // "Confirm delete"
321 CVariant{19328}, // "Delete all watched recordings in this folder?"
322 CVariant{""}, CVariant{item.GetLabel()});
325 bool CPVRGUIActionsRecordings::DeleteAllRecordingsFromTrash() const
327 if (!ConfirmDeleteAllRecordingsFromTrash())
328 return false;
330 if (!AsyncEmptyRecordingsTrash().Execute({}))
331 return false;
333 return true;
336 bool CPVRGUIActionsRecordings::ConfirmDeleteAllRecordingsFromTrash() const
338 return CGUIDialogYesNo::ShowAndGetInput(
339 CVariant{19292}, // "Delete all permanently"
340 CVariant{
341 19293}); // "Remove all deleted recordings from trash? This operation cannot be reverted."
344 bool CPVRGUIActionsRecordings::UndeleteRecording(const CFileItem& item) const
346 if (!item.IsDeletedPVRRecording())
347 return false;
349 if (!AsyncUndeleteRecording().Execute(item))
351 HELPERS::ShowOKDialogText(CVariant{257}, CVariant{19111}); // "Error", "PVR backend error."
352 return false;
355 return true;
358 bool CPVRGUIActionsRecordings::ShowRecordingSettings(
359 const std::shared_ptr<CPVRRecording>& recording) const
361 CGUIDialogPVRRecordingSettings* pDlgInfo =
362 CServiceBroker::GetGUI()->GetWindowManager().GetWindow<CGUIDialogPVRRecordingSettings>(
363 WINDOW_DIALOG_PVR_RECORDING_SETTING);
364 if (!pDlgInfo)
366 CLog::LogF(LOGERROR, "Unable to get WINDOW_DIALOG_PVR_RECORDING_SETTING!");
367 return false;
370 pDlgInfo->SetRecording(recording);
371 pDlgInfo->Open();
373 return pDlgInfo->IsConfirmed();
376 bool CPVRGUIActionsRecordings::ProcessDeleteAfterWatch(const CFileItem& item) const
378 bool deleteRecording{false};
380 const int action{m_settings.GetIntValue(CSettings::SETTING_PVRRECORD_DELETEAFTERWATCH)};
381 switch (action)
383 case PVRRECORD_DELETE_AFTER_WATCH::NO:
384 deleteRecording = false;
385 break;
387 case PVRRECORD_DELETE_AFTER_WATCH::ASK:
388 deleteRecording = (HELPERS::ShowYesNoDialogLines(
389 CVariant{860}, // "Delete after watching"
390 CVariant{865}, // "Do you want to delete this recording?"
391 CVariant{""}, CVariant{item.GetPVRRecordingInfoTag()->GetTitle()}) ==
392 HELPERS::DialogResponse::CHOICE_YES);
393 break;
395 case PVRRECORD_DELETE_AFTER_WATCH::YES:
396 deleteRecording = true;
397 break;
399 default:
400 CLog::LogF(LOGERROR, "Unhandled delete after watch action! Defaulting to 'no delete'.");
401 deleteRecording = false;
402 break;
405 if (deleteRecording)
407 if (AsyncDeleteRecording().Execute(item))
409 CPVREventLogJob* job = new CPVREventLogJob;
410 job->AddEvent(true, // display a toast, and log event
411 EventLevel::Information, // info, no error
412 g_localizeStrings.Get(860), // "Delete after watching"
413 StringUtils::Format(g_localizeStrings.Get(866), // Recording deleted: <title>
414 item.GetPVRRecordingInfoTag()->GetTitle()),
415 item.GetPVRRecordingInfoTag()->IconPath());
416 CServiceBroker::GetJobManager()->AddJob(job, nullptr);
418 else
420 HELPERS::ShowOKDialogText(CVariant{257}, // "Error"
421 CVariant{19111}); // "PVR backend error."
422 return false;
425 return true;
428 bool CPVRGUIActionsRecordings::IncrementPlayCount(const CFileItem& item) const
430 if (!item.IsPVRRecording())
431 return false;
433 if (item.GetPVRRecordingInfoTag()->IncrementPlayCount())
435 // Item must now be watched (because play count > 0).
436 return ProcessDeleteAfterWatch(item);
438 return false;
441 bool CPVRGUIActionsRecordings::MarkWatched(const CFileItem& item, bool watched) const
443 if (!item.IsPVRRecording())
444 return false;
446 if (CServiceBroker::GetPVRManager().Recordings()->MarkWatched(item.GetPVRRecordingInfoTag(),
447 watched))
449 if (watched)
450 return ProcessDeleteAfterWatch(item);
452 return true;
454 return false;