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 "GUIWindowPVRRecordings.h"
11 #include "FileItemList.h"
12 #include "GUIInfoManager.h"
13 #include "ServiceBroker.h"
14 #include "guilib/GUIComponent.h"
15 #include "guilib/GUIMessage.h"
16 #include "guilib/GUIRadioButtonControl.h"
17 #include "guilib/GUIWindowManager.h"
18 #include "guilib/LocalizeStrings.h"
19 #include "input/actions/Action.h"
20 #include "input/actions/ActionIDs.h"
21 #include "pvr/PVRManager.h"
22 #include "pvr/guilib/PVRGUIActionsPlayback.h"
23 #include "pvr/guilib/PVRGUIActionsRecordings.h"
24 #include "pvr/recordings/PVRRecording.h"
25 #include "pvr/recordings/PVRRecordings.h"
26 #include "pvr/recordings/PVRRecordingsPath.h"
27 #include "settings/MediaSettings.h"
28 #include "settings/Settings.h"
29 #include "settings/SettingsComponent.h"
30 #include "utils/URIUtils.h"
31 #include "video/VideoLibraryQueue.h"
32 #include "video/guilib/VideoGUIUtils.h"
33 #include "video/guilib/VideoPlayActionProcessor.h"
34 #include "video/guilib/VideoSelectActionProcessor.h"
35 #include "video/windows/GUIWindowVideoBase.h"
44 CGUIWindowPVRRecordingsBase::CGUIWindowPVRRecordingsBase(bool bRadio
,
46 const std::string
& xmlFile
)
47 : CGUIWindowPVRBase(bRadio
, id
, xmlFile
),
48 m_settings({CSettings::SETTING_PVRRECORD_GROUPRECORDINGS
})
52 CGUIWindowPVRRecordingsBase::~CGUIWindowPVRRecordingsBase() = default;
54 void CGUIWindowPVRRecordingsBase::OnWindowLoaded()
56 CONTROL_SELECT(CONTROL_BTNGROUPITEMS
);
59 std::string
CGUIWindowPVRRecordingsBase::GetDirectoryPath()
61 const std::string basePath
= CPVRRecordingsPath(m_bShowDeletedRecordings
, m_bRadio
);
62 return URIUtils::PathHasParent(m_vecItems
->GetPath(), basePath
) ? m_vecItems
->GetPath()
66 void CGUIWindowPVRRecordingsBase::GetContextButtons(int itemNumber
, CContextButtons
& buttons
)
68 if (itemNumber
< 0 || itemNumber
>= m_vecItems
->Size())
70 CFileItemPtr pItem
= m_vecItems
->Get(itemNumber
);
72 if (pItem
->IsParentFolder())
74 // No context menu for ".." items
78 bool isDeletedRecording
= false;
80 std::shared_ptr
<CPVRRecording
> recording(pItem
->GetPVRRecordingInfoTag());
83 isDeletedRecording
= recording
->IsDeleted();
85 if (isDeletedRecording
)
87 if (m_vecItems
->GetObjectCount() > 1)
88 buttons
.Add(CONTEXT_BUTTON_DELETE_ALL
, 19292); /* Delete all permanently */
92 if (!isDeletedRecording
)
93 CGUIWindowPVRBase::GetContextButtons(itemNumber
, buttons
);
96 bool CGUIWindowPVRRecordingsBase::OnAction(const CAction
& action
)
98 if (action
.GetID() == ACTION_PARENT_DIR
|| action
.GetID() == ACTION_NAV_BACK
)
100 CPVRRecordingsPath
path(m_vecItems
->GetPath());
101 if (path
.IsValid() && !path
.IsRecordingsRoot())
107 else if (action
.GetID() == ACTION_TOGGLE_WATCHED
)
109 const std::shared_ptr
<CFileItem
> pItem
= m_vecItems
->Get(m_viewControl
.GetSelectedItem());
110 if (!pItem
|| pItem
->IsParentFolder())
113 bool bUnWatched
= false;
114 if (pItem
->HasPVRRecordingInfoTag())
115 bUnWatched
= pItem
->GetPVRRecordingInfoTag()->GetPlayCount() == 0;
116 else if (pItem
->m_bIsFolder
)
117 bUnWatched
= pItem
->GetProperty("unwatchedepisodes").asInteger() > 0;
121 CVideoLibraryQueue::GetInstance().MarkAsWatched(pItem
, bUnWatched
);
125 return CGUIWindowPVRBase::OnAction(action
);
128 bool CGUIWindowPVRRecordingsBase::OnPopupMenu(int iItem
)
130 if (iItem
>= 0 && iItem
< m_vecItems
->Size())
132 const auto item
= m_vecItems
->Get(iItem
);
133 item
->SetProperty("CheckAutoPlayNextItem", true);
136 return CGUIWindowPVRBase::OnPopupMenu(iItem
);
139 bool CGUIWindowPVRRecordingsBase::OnContextButton(int itemNumber
, CONTEXT_BUTTON button
)
141 if (itemNumber
< 0 || itemNumber
>= m_vecItems
->Size())
143 CFileItemPtr pItem
= m_vecItems
->Get(itemNumber
);
145 return OnContextButtonDeleteAll(pItem
.get(), button
) ||
146 CGUIMediaWindow::OnContextButton(itemNumber
, button
);
149 bool CGUIWindowPVRRecordingsBase::Update(const std::string
& strDirectory
,
150 bool updateFilterPath
/* = true */)
152 m_thumbLoader
.StopThread();
154 int iOldCount
= m_vecItems
->GetObjectCount();
155 const std::string oldPath
= m_vecItems
->GetPath();
157 bool bReturn
= CGUIWindowPVRBase::Update(strDirectory
);
161 // TODO: does it make sense to show the non-deleted recordings, although user wants
162 // to see the deleted recordings? Or is this just another hack to avoid misbehavior
163 // of CGUIMediaWindow if it has no content?
165 std::unique_lock
<CCriticalSection
> lock(m_critSection
);
167 /* empty list for deleted recordings */
168 if (m_vecItems
->GetObjectCount() == 0 && m_bShowDeletedRecordings
)
170 /* show the normal recordings instead */
171 m_bShowDeletedRecordings
= false;
173 Update(GetDirectoryPath());
178 if (bReturn
&& iOldCount
> 0 && m_vecItems
->GetObjectCount() == 0 &&
179 oldPath
== m_vecItems
->GetPath())
181 /* go to the parent folder if we're in a subdirectory and for instance just deleted the last item */
182 const CPVRRecordingsPath
path(m_vecItems
->GetPath());
183 if (path
.IsValid() && !path
.IsRecordingsRoot())
189 void CGUIWindowPVRRecordingsBase::UpdateButtons()
191 int iWatchMode
= CMediaSettings::GetInstance().GetWatchedMode("recordings");
192 int iStringId
= 257; // "Error"
194 if (iWatchMode
== WatchedModeAll
)
195 iStringId
= 22015; // "All recordings"
196 else if (iWatchMode
== WatchedModeUnwatched
)
197 iStringId
= 16101; // "Unwatched"
198 else if (iWatchMode
== WatchedModeWatched
)
199 iStringId
= 16102; // "Watched"
201 SET_CONTROL_LABEL(CONTROL_BTNSHOWMODE
, g_localizeStrings
.Get(iStringId
));
203 bool bGroupRecordings
= m_settings
.GetBoolValue(CSettings::SETTING_PVRRECORD_GROUPRECORDINGS
);
204 SET_CONTROL_SELECTED(GetID(), CONTROL_BTNGROUPITEMS
, bGroupRecordings
);
206 CGUIRadioButtonControl
* btnShowDeleted
=
207 static_cast<CGUIRadioButtonControl
*>(GetControl(CONTROL_BTNSHOWDELETED
));
210 btnShowDeleted
->SetVisible(
211 m_bRadio
? CServiceBroker::GetPVRManager().Recordings()->HasDeletedRadioRecordings()
212 : CServiceBroker::GetPVRManager().Recordings()->HasDeletedTVRecordings());
213 btnShowDeleted
->SetSelected(m_bShowDeletedRecordings
);
216 CGUIWindowPVRBase::UpdateButtons();
217 SET_CONTROL_LABEL(CONTROL_LABEL_HEADER1
, m_bShowDeletedRecordings
218 ? g_localizeStrings
.Get(19179)
219 : ""); /* Deleted recordings trash */
221 const CPVRRecordingsPath
path(m_vecItems
->GetPath());
222 SET_CONTROL_LABEL(CONTROL_LABEL_HEADER2
,
223 bGroupRecordings
&& path
.IsValid() ? path
.GetUnescapedDirectoryPath() : "");
228 class CVideoSelectActionProcessor
: public VIDEO::GUILIB::CVideoSelectActionProcessorBase
231 CVideoSelectActionProcessor(CGUIWindowPVRRecordingsBase
& window
,
232 const std::shared_ptr
<CFileItem
>& item
,
234 : CVideoSelectActionProcessorBase(item
), m_window(window
), m_itemIndex(itemIndex
)
239 bool OnPlayPartSelected(unsigned int part
) override
241 //! @todo pvr recordings do not support video stacking (yet).
245 bool OnResumeSelected() override
247 CServiceBroker::GetPVRManager().Get
<PVR::GUI::Playback
>().ResumePlayRecording(
248 *m_item
, true /* fall back to play if no resume possible */);
252 bool OnPlaySelected() override
254 CServiceBroker::GetPVRManager().Get
<PVR::GUI::Playback
>().PlayRecording(
255 *m_item
, false /* no resume check */);
259 bool OnQueueSelected() override
261 VIDEO::UTILS::QueueItem(m_item
, VIDEO::UTILS::QueuePosition::POSITION_END
);
265 bool OnInfoSelected() override
267 CServiceBroker::GetPVRManager().Get
<PVR::GUI::Recordings
>().ShowRecordingInfo(*m_item
);
271 bool OnMoreSelected() override
273 m_window
.OnPopupMenu(m_itemIndex
);
278 CGUIWindowPVRRecordingsBase
& m_window
;
279 const int m_itemIndex
{-1};
282 class CVideoPlayActionProcessor
: public VIDEO::GUILIB::CVideoPlayActionProcessorBase
285 explicit CVideoPlayActionProcessor(const std::shared_ptr
<CFileItem
>& item
)
286 : CVideoPlayActionProcessorBase(item
)
291 bool OnResumeSelected() override
293 if (m_item
->m_bIsFolder
)
295 m_item
->SetStartOffset(STARTOFFSET_RESUME
);
296 CServiceBroker::GetPVRManager().Get
<PVR::GUI::Playback
>().PlayRecordingFolder(
297 *m_item
, false /* no resume check */);
301 CServiceBroker::GetPVRManager().Get
<PVR::GUI::Playback
>().ResumePlayRecording(
302 *m_item
, true /* fall back to play if no resume possible */);
307 bool OnPlaySelected() override
309 if (m_item
->m_bIsFolder
)
311 m_item
->SetStartOffset(0);
312 CServiceBroker::GetPVRManager().Get
<PVR::GUI::Playback
>().PlayRecordingFolder(
313 *m_item
, false /* no resume check */);
317 CServiceBroker::GetPVRManager().Get
<PVR::GUI::Playback
>().PlayRecording(
318 *m_item
, false /* no resume check */);
325 bool CGUIWindowPVRRecordingsBase::OnMessage(CGUIMessage
& message
)
327 bool bReturn
= false;
328 switch (message
.GetMessage())
330 case GUI_MSG_CLICKED
:
331 if (message
.GetSenderId() == m_viewControl
.GetCurrentControl())
333 int iItem
= m_viewControl
.GetSelectedItem();
334 if (iItem
>= 0 && iItem
< m_vecItems
->Size())
336 const CFileItemPtr
item(m_vecItems
->Get(iItem
));
337 switch (message
.GetParam1())
339 case ACTION_SELECT_ITEM
:
340 case ACTION_MOUSE_LEFT_CLICK
:
341 case ACTION_PLAYER_PLAY
:
343 const CPVRRecordingsPath
path(m_vecItems
->GetPath());
344 if (path
.IsValid() && path
.IsRecordingsRoot() && item
->IsParentFolder())
346 // handle .. item, which is only visible if list of recordings is empty.
347 CServiceBroker::GetGUI()->GetWindowManager().ActivateWindow(WINDOW_HOME
);
352 if (!item
->IsParentFolder() && message
.GetParam1() == ACTION_PLAYER_PLAY
)
354 CVideoPlayActionProcessor proc
{item
};
355 bReturn
= proc
.ProcessDefaultAction();
357 else if (item
->m_bIsFolder
)
359 // recording folders and ".." folders in subfolders are handled by base class.
364 CVideoSelectActionProcessor
proc(*this, item
, iItem
);
365 bReturn
= proc
.ProcessDefaultAction();
369 case ACTION_CONTEXT_MENU
:
370 case ACTION_MOUSE_RIGHT_CLICK
:
374 case ACTION_SHOW_INFO
:
375 CServiceBroker::GetPVRManager().Get
<PVR::GUI::Recordings
>().ShowRecordingInfo(*item
);
378 case ACTION_DELETE_ITEM
:
379 CServiceBroker::GetPVRManager().Get
<PVR::GUI::Recordings
>().DeleteRecording(*item
);
388 else if (message
.GetSenderId() == CONTROL_BTNGROUPITEMS
)
390 const std::shared_ptr
<CSettings
> settings
=
391 CServiceBroker::GetSettingsComponent()->GetSettings();
392 settings
->ToggleBool(CSettings::SETTING_PVRRECORD_GROUPRECORDINGS
);
396 else if (message
.GetSenderId() == CONTROL_BTNSHOWDELETED
)
398 CGUIRadioButtonControl
* radioButton
=
399 static_cast<CGUIRadioButtonControl
*>(GetControl(CONTROL_BTNSHOWDELETED
));
402 m_bShowDeletedRecordings
= radioButton
->IsSelected();
403 Update(GetDirectoryPath());
407 else if (message
.GetSenderId() == CONTROL_BTNSHOWMODE
)
409 CMediaSettings::GetInstance().CycleWatchedMode("recordings");
410 CServiceBroker::GetSettingsComponent()->GetSettings()->Save();
411 OnFilterItems(GetProperty("filter").asString());
416 case GUI_MSG_REFRESH_LIST
:
418 switch (static_cast<PVREvent
>(message
.GetParam1()))
420 case PVREvent::CurrentItem
:
422 case PVREvent::EpgActiveItem
:
423 case PVREvent::EpgContainer
:
424 case PVREvent::Timers
:
428 case PVREvent::RecordingsInvalidated
:
429 case PVREvent::TimersInvalidated
:
440 return bReturn
|| CGUIWindowPVRBase::OnMessage(message
);
443 bool CGUIWindowPVRRecordingsBase::OnContextButtonDeleteAll(CFileItem
* item
, CONTEXT_BUTTON button
)
445 if (button
== CONTEXT_BUTTON_DELETE_ALL
)
447 CServiceBroker::GetPVRManager().Get
<PVR::GUI::Recordings
>().DeleteAllRecordingsFromTrash();
454 void CGUIWindowPVRRecordingsBase::OnPrepareFileItems(CFileItemList
& items
)
460 for (const auto& item
: items
)
462 if (!item
->m_bIsFolder
)
466 if (!files
.IsEmpty())
468 if (m_database
.Open())
470 CGUIWindowVideoBase::LoadVideoInfo(files
, m_database
, false);
473 m_thumbLoader
.Load(files
);
476 CGUIWindowPVRBase::OnPrepareFileItems(items
);
479 bool CGUIWindowPVRRecordingsBase::GetFilteredItems(const std::string
& filter
, CFileItemList
& items
)
481 bool listchanged
= CGUIWindowPVRBase::GetFilteredItems(filter
, items
);
483 int watchMode
= CMediaSettings::GetInstance().GetWatchedMode("recordings");
486 for (int i
= 0; i
< items
.Size(); i
++)
490 if (item
->IsParentFolder()) // Don't delete the go to parent folder
493 if (!item
->HasPVRRecordingInfoTag())
496 int iPlayCount
= item
->GetPVRRecordingInfoTag()->GetPlayCount();
497 if ((watchMode
== WatchedModeWatched
&& iPlayCount
== 0) ||
498 (watchMode
== WatchedModeUnwatched
&& iPlayCount
> 0))
506 // Remove the parent folder item, if it's the only item in the folder.
507 if (items
.GetObjectCount() == 0 && items
.GetFileCount() > 0 && items
.Get(0)->IsParentFolder())
513 std::string
CGUIWindowPVRTVRecordings::GetRootPath() const
515 return CPVRRecordingsPath(m_bShowDeletedRecordings
, false);
518 std::string
CGUIWindowPVRRadioRecordings::GetRootPath() const
520 return CPVRRecordingsPath(m_bShowDeletedRecordings
, true);