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.
9 #include "PVRGUIActionsRecordings.h"
12 #include "FileItemList.h"
13 #include "ServiceBroker.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"
44 using namespace KODI::MESSAGING
;
48 enum PVRRECORD_DELETE_AFTER_WATCH
50 // Values must match those defined in settings.xml -> pvrrecord.deleteafterwatch
56 class AsyncRecordingAction
: private IRunnable
59 bool Execute(const CFileItem
& item
);
62 AsyncRecordingAction() = default;
65 // IRunnable implementation
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);
82 void AsyncRecordingAction::Run()
84 m_bSuccess
= DoRun(m_item
);
87 CServiceBroker::GetPVRManager().TriggerRecordingsUpdate();
90 class AsyncRenameRecording
: public AsyncRecordingAction
93 explicit AsyncRenameRecording(const std::string
& strNewName
) : m_strNewName(strNewName
) {}
96 bool DoRun(const std::shared_ptr
<CFileItem
>& item
) override
98 if (item
->IsUsablePVRRecording())
100 return item
->GetPVRRecordingInfoTag()->Rename(m_strNewName
);
104 CLog::LogF(LOGERROR
, "Cannot rename item '{}': no valid recording tag", item
->GetPath());
108 std::string m_strNewName
;
111 class AsyncDeleteRecording
: public AsyncRecordingAction
114 explicit AsyncDeleteRecording(bool bWatchedOnly
= false) : m_bWatchedOnly(bWatchedOnly
) {}
117 bool DoRun(const std::shared_ptr
<CFileItem
>& item
) override
120 if (item
->m_bIsFolder
)
122 CUtil::GetRecursiveListing(item
->GetPath(), items
, "", XFILE::DIR_FLAG_NO_FILE_INFO
);
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()))
141 bool m_bWatchedOnly
= false;
144 class AsyncEmptyRecordingsTrash
: public AsyncRecordingAction
147 bool DoRun(const std::shared_ptr
<CFileItem
>& item
) override
149 return CServiceBroker::GetPVRManager().Clients()->DeleteAllRecordingsFromTrash() ==
154 class AsyncUndeleteRecording
: public AsyncRecordingAction
157 bool DoRun(const std::shared_ptr
<CFileItem
>& item
) override
159 if (item
->IsDeletedPVRRecording())
161 return item
->GetPVRRecordingInfoTag()->Undelete();
165 CLog::LogF(LOGERROR
, "Cannot undelete item '{}': no valid recording tag", item
->GetPath());
171 class AsyncSetRecordingPlayCount
: public AsyncRecordingAction
174 bool DoRun(const std::shared_ptr
<CFileItem
>& item
) override
176 const std::shared_ptr
<CPVRClient
> client
= CServiceBroker::GetPVRManager().GetClient(*item
);
179 const std::shared_ptr
<const CPVRRecording
> recording
= item
->GetPVRRecordingInfoTag();
180 return client
->SetRecordingPlayCount(*recording
, recording
->GetLocalPlayCount()) ==
187 class AsyncSetRecordingLifetime
: public AsyncRecordingAction
190 bool DoRun(const std::shared_ptr
<CFileItem
>& item
) override
192 const std::shared_ptr
<CPVRClient
> client
= CServiceBroker::GetPVRManager().GetClient(*item
);
194 return client
->SetRecordingLifetime(*item
->GetPVRRecordingInfoTag()) == PVR_ERROR_NO_ERROR
;
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!");
214 CGUIDialogPVRRecordingInfo
* pDlgInfo
=
215 CServiceBroker::GetGUI()->GetWindowManager().GetWindow
<CGUIDialogPVRRecordingInfo
>(
216 WINDOW_DIALOG_PVR_RECORDING_INFO
);
219 CLog::LogF(LOGERROR
, "Unable to get WINDOW_DIALOG_PVR_RECORDING_INFO!");
223 pDlgInfo
->SetRecording(item
);
228 bool CPVRGUIActionsRecordings::EditRecording(const CFileItem
& item
) const
230 const std::shared_ptr
<CPVRRecording
> recording
= CPVRItem(item
).GetRecording();
233 CLog::LogF(LOGERROR
, "No recording!");
237 std::shared_ptr
<CPVRRecording
> origRecording(new CPVRRecording
);
238 origRecording
->Update(*recording
,
239 *CServiceBroker::GetPVRManager().GetClient(recording
->ClientID()));
241 if (!ShowRecordingSettings(recording
))
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!");
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())
275 if (!ConfirmDeleteRecording(item
))
278 if (!AsyncDeleteRecording().Execute(item
))
280 HELPERS::ShowOKDialogText(CVariant
{257}, CVariant
{19111}); // "Error", "PVR backend error."
287 bool CPVRGUIActionsRecordings::ConfirmDeleteRecording(const CFileItem
& item
) const
289 return CGUIDialogYesNo::ShowAndGetInput(
290 CVariant
{122}, // "Confirm delete"
292 ? CVariant
{19113} // "Delete all recordings in this folder?"
293 : item
.GetPVRRecordingInfoTag()->IsDeleted()
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())
305 if (!ConfirmDeleteWatchedRecordings(item
))
308 if (!AsyncDeleteRecording(true).Execute(item
))
310 HELPERS::ShowOKDialogText(CVariant
{257}, CVariant
{19111}); // "Error", "PVR backend error."
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())
330 if (!AsyncEmptyRecordingsTrash().Execute({}))
336 bool CPVRGUIActionsRecordings::ConfirmDeleteAllRecordingsFromTrash() const
338 return CGUIDialogYesNo::ShowAndGetInput(
339 CVariant
{19292}, // "Delete all permanently"
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())
349 if (!AsyncUndeleteRecording().Execute(item
))
351 HELPERS::ShowOKDialogText(CVariant
{257}, CVariant
{19111}); // "Error", "PVR backend error."
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
);
366 CLog::LogF(LOGERROR
, "Unable to get WINDOW_DIALOG_PVR_RECORDING_SETTING!");
370 pDlgInfo
->SetRecording(recording
);
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
)};
383 case PVRRECORD_DELETE_AFTER_WATCH::NO
:
384 deleteRecording
= false;
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
);
395 case PVRRECORD_DELETE_AFTER_WATCH::YES
:
396 deleteRecording
= true;
400 CLog::LogF(LOGERROR
, "Unhandled delete after watch action! Defaulting to 'no delete'.");
401 deleteRecording
= false;
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);
420 HELPERS::ShowOKDialogText(CVariant
{257}, // "Error"
421 CVariant
{19111}); // "PVR backend error."
428 bool CPVRGUIActionsRecordings::IncrementPlayCount(const CFileItem
& item
) const
430 if (!item
.IsPVRRecording())
433 if (item
.GetPVRRecordingInfoTag()->IncrementPlayCount())
435 // Item must now be watched (because play count > 0).
436 return ProcessDeleteAfterWatch(item
);
441 bool CPVRGUIActionsRecordings::MarkWatched(const CFileItem
& item
, bool watched
) const
443 if (!item
.IsPVRRecording())
446 if (CServiceBroker::GetPVRManager().Recordings()->MarkWatched(item
.GetPVRRecordingInfoTag(),
450 return ProcessDeleteAfterWatch(item
);