2 * Copyright (C) 2023 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 "GUIDialogVideoManager.h"
12 #include "FileItemList.h"
13 #include "GUIUserMessages.h"
14 #include "MediaSource.h"
15 #include "ServiceBroker.h"
16 #include "dialogs/GUIDialogOK.h"
17 #include "dialogs/GUIDialogSelect.h"
18 #include "dialogs/GUIDialogYesNo.h"
19 #include "filesystem/Directory.h"
20 #include "guilib/GUIComponent.h"
21 #include "guilib/GUIKeyboardFactory.h"
22 #include "guilib/GUIWindowManager.h"
23 #include "guilib/LocalizeStrings.h"
24 #include "input/actions/Action.h"
25 #include "input/actions/ActionIDs.h"
26 #include "playlists/PlayListTypes.h"
27 #include "utils/ContentUtils.h"
28 #include "utils/StringUtils.h"
29 #include "utils/URIUtils.h"
30 #include "utils/log.h"
31 #include "video/VideoManagerTypes.h"
32 #include "video/VideoThumbLoader.h"
33 #include "video/dialogs/GUIDialogVideoInfo.h"
34 #include "video/guilib/VideoGUIUtils.h"
35 #include "video/guilib/VideoPlayActionProcessor.h"
42 static constexpr unsigned int CONTROL_LABEL_TITLE
= 2;
44 static constexpr unsigned int CONTROL_BUTTON_PLAY
= 21;
45 static constexpr unsigned int CONTROL_BUTTON_REMOVE
= 26;
46 static constexpr unsigned int CONTROL_BUTTON_CHOOSE_ART
= 27;
48 static constexpr unsigned int CONTROL_LIST_ASSETS
= 50;
50 CGUIDialogVideoManager::CGUIDialogVideoManager(int windowId
)
51 : CGUIDialog(windowId
, "DialogVideoManager.xml"),
52 m_videoAsset(std::make_shared
<CFileItem
>()),
53 m_videoAssetsList(std::make_unique
<CFileItemList
>()),
54 m_selectedVideoAsset(std::make_shared
<CFileItem
>())
56 m_loadType
= KEEP_IN_MEMORY
;
59 bool CGUIDialogVideoManager::OnMessage(CGUIMessage
& message
)
61 switch (message
.GetMessage())
63 case GUI_MSG_WINDOW_DEINIT
:
71 const int control
{message
.GetSenderId()};
72 if (control
== CONTROL_LIST_ASSETS
)
74 const int action
{message
.GetParam1()};
75 if (action
== ACTION_SELECT_ITEM
|| action
== ACTION_MOUSE_LEFT_CLICK
)
77 if (UpdateSelectedAsset())
78 SET_CONTROL_FOCUS(CONTROL_BUTTON_PLAY
, 0);
81 else if (control
== CONTROL_BUTTON_PLAY
)
85 else if (control
== CONTROL_BUTTON_REMOVE
)
89 else if (control
== CONTROL_BUTTON_CHOOSE_ART
)
97 return CGUIDialog::OnMessage(message
);
100 bool CGUIDialogVideoManager::OnAction(const CAction
& action
)
102 const int actionId
{action
.GetID()};
103 if (actionId
== ACTION_MOVE_DOWN
|| actionId
== ACTION_MOVE_UP
|| actionId
== ACTION_PAGE_DOWN
||
104 actionId
== ACTION_PAGE_UP
|| actionId
== ACTION_FIRST_PAGE
|| actionId
== ACTION_LAST_PAGE
)
106 if (GetFocusedControlID() == CONTROL_LIST_ASSETS
)
108 CGUIDialog::OnAction(action
);
109 return UpdateSelectedAsset();
113 return CGUIDialog::OnAction(action
);
116 void CGUIDialogVideoManager::OnInitWindow()
118 if (!m_database
.IsOpen() && !m_database
.Open())
119 CLog::LogF(LOGERROR
, "Failed to open video database!");
121 CGUIDialog::OnInitWindow();
123 SET_CONTROL_LABEL(CONTROL_LABEL_TITLE
,
124 StringUtils::Format(g_localizeStrings
.Get(GetHeadingId()),
125 m_videoAsset
->GetVideoInfoTag()->GetTitle()));
127 CGUIMessage msg
{GUI_MSG_LABEL_BIND
, GetID(), CONTROL_LIST_ASSETS
, 0, 0, m_videoAssetsList
.get()};
133 void CGUIDialogVideoManager::OnDeinitWindow(int nextWindowID
)
135 CGUIDialog::OnDeinitWindow(nextWindowID
);
139 void CGUIDialogVideoManager::Clear()
141 m_videoAssetsList
->Clear();
144 void CGUIDialogVideoManager::UpdateButtons()
146 // Enabled if list not empty
147 if (!m_videoAssetsList
->IsEmpty())
149 CONTROL_ENABLE(CONTROL_BUTTON_CHOOSE_ART
);
150 CONTROL_ENABLE(CONTROL_BUTTON_REMOVE
);
151 CONTROL_ENABLE(CONTROL_BUTTON_PLAY
);
153 SET_CONTROL_FOCUS(CONTROL_LIST_ASSETS
, 0);
157 CONTROL_DISABLE(CONTROL_BUTTON_CHOOSE_ART
);
158 CONTROL_DISABLE(CONTROL_BUTTON_REMOVE
);
159 CONTROL_DISABLE(CONTROL_BUTTON_PLAY
);
163 void CGUIDialogVideoManager::UpdateAssetsList()
165 // find new item in list and select it
166 for (int i
= 0; i
< m_videoAssetsList
->Size(); ++i
)
168 if (m_videoAssetsList
->Get(i
)->GetVideoInfoTag()->m_iDbId
==
169 m_selectedVideoAsset
->GetVideoInfoTag()->m_iDbId
)
171 CONTROL_SELECT_ITEM(CONTROL_LIST_ASSETS
, i
);
177 bool CGUIDialogVideoManager::UpdateSelectedAsset()
179 CGUIMessage msg
{GUI_MSG_ITEM_SELECTED
, GetID(), CONTROL_LIST_ASSETS
};
182 const int item
{msg
.GetParam1()};
183 if (item
>= 0 && item
< m_videoAssetsList
->Size())
185 m_selectedVideoAsset
= m_videoAssetsList
->Get(item
);
192 void CGUIDialogVideoManager::DisableRemove()
194 CONTROL_DISABLE(CONTROL_BUTTON_REMOVE
);
197 void CGUIDialogVideoManager::EnableRemove()
199 CONTROL_ENABLE(CONTROL_BUTTON_REMOVE
);
202 void CGUIDialogVideoManager::UpdateControls()
208 void CGUIDialogVideoManager::Refresh()
210 if (!m_database
.IsOpen() && !m_database
.Open())
211 CLog::LogF(LOGERROR
, "Failed to open video database!");
215 const int dbId
{m_videoAsset
->GetVideoInfoTag()->m_iDbId
};
216 const MediaType mediaType
{m_videoAsset
->GetVideoInfoTag()->m_type
};
217 const VideoDbContentType itemType
{m_videoAsset
->GetVideoContentType()};
219 //! @todo db refactor: should not be versions, but assets
220 m_database
.GetVideoVersions(itemType
, dbId
, *m_videoAssetsList
, GetVideoAssetType());
221 m_videoAssetsList
->SetContent(CMediaTypes::ToPlural(mediaType
));
223 CVideoThumbLoader loader
;
225 for (auto& item
: *m_videoAssetsList
)
227 item
->SetProperty("noartfallbacktoowner", true);
228 loader
.LoadItem(item
.get());
231 CGUIMessage msg
{GUI_MSG_LABEL_BIND
, GetID(), CONTROL_LIST_ASSETS
, 0, 0, m_videoAssetsList
.get()};
235 void CGUIDialogVideoManager::SetVideoAsset(const std::shared_ptr
<CFileItem
>& item
)
237 if (!item
|| !item
->HasVideoInfoTag() || item
->GetVideoInfoTag()->m_type
!= MediaTypeMovie
)
239 CLog::LogF(LOGERROR
, "Unexpected video item!");
248 void CGUIDialogVideoManager::CloseAll()
253 // close the video info dialog if exists
254 CGUIDialogVideoInfo
* dialog
{
255 CServiceBroker::GetGUI()->GetWindowManager().GetWindow
<CGUIDialogVideoInfo
>(
256 WINDOW_DIALOG_VIDEO_INFO
)};
263 class CVideoPlayActionProcessor
: public VIDEO::GUILIB::CVideoPlayActionProcessorBase
266 explicit CVideoPlayActionProcessor(const std::shared_ptr
<CFileItem
>& item
)
267 : CVideoPlayActionProcessorBase(item
)
272 bool OnResumeSelected() override
274 m_item
->SetStartOffset(STARTOFFSET_RESUME
);
279 bool OnPlaySelected() override
288 m_item
->SetProperty("playlist_type_hint", static_cast<int>(PLAYLIST::Id::TYPE_VIDEO
));
289 const ContentUtils::PlayMode mode
{m_item
->GetProperty("CheckAutoPlayNextItem").asBoolean()
290 ? ContentUtils::PlayMode::CHECK_AUTO_PLAY_NEXT_ITEM
291 : ContentUtils::PlayMode::PLAY_ONLY_THIS
};
292 VIDEO::UTILS::PlayItem(m_item
, "", mode
);
295 } // unnamed namespace
297 void CGUIDialogVideoManager::Play()
301 CVideoPlayActionProcessor proc
{m_selectedVideoAsset
};
302 proc
.ProcessDefaultAction();
305 void CGUIDialogVideoManager::Remove()
308 if (!CGUIDialogYesNo::ShowAndGetInput(
310 StringUtils::Format(g_localizeStrings
.Get(40020),
311 m_selectedVideoAsset
->GetVideoInfoTag()->GetAssetInfo().GetTitle())))
316 m_database
.DeleteVideoAsset(m_selectedVideoAsset
->GetVideoInfoTag()->m_iDbId
);
318 // refresh data and controls
320 RefreshSelectedVideoAsset();
324 void CGUIDialogVideoManager::Rename()
327 ChooseVideoAsset(m_videoAsset
, GetVideoAssetType(), m_selectedVideoAsset
->m_strTitle
)};
330 //! @todo db refactor: should not be version, but asset
331 m_database
.SetVideoVersion(m_selectedVideoAsset
->GetVideoInfoTag()->m_iDbId
, idAsset
);
334 // refresh data and controls
339 void CGUIDialogVideoManager::ChooseArt()
341 if (!CGUIDialogVideoInfo::ChooseAndManageVideoItemArtwork(m_selectedVideoAsset
))
344 // refresh data and controls
349 void CGUIDialogVideoManager::SetSelectedVideoAsset(const std::shared_ptr
<CFileItem
>& asset
)
351 const int dbId
{asset
->GetVideoInfoTag()->m_iDbId
};
352 const auto it
{std::find_if(m_videoAssetsList
->cbegin(), m_videoAssetsList
->cend(),
353 [dbId
](const auto& entry
)
354 { return entry
->GetVideoInfoTag()->m_iDbId
== dbId
; })};
355 if (it
!= m_videoAssetsList
->cend())
356 m_selectedVideoAsset
= (*it
);
358 CLog::LogF(LOGERROR
, "Item to select not found in asset list!");
363 int CGUIDialogVideoManager::ChooseVideoAsset(const std::shared_ptr
<CFileItem
>& item
,
364 VideoAssetType assetType
,
365 const std::string
& defaultName
)
367 if (!item
|| !item
->HasVideoInfoTag())
370 const VideoDbContentType itemType
{item
->GetVideoContentType()};
371 if (itemType
!= VideoDbContentType::MOVIES
)
374 int dialogHeadingMsgId
{};
375 int dialogButtonMsgId
{};
376 int dialogNewHeadingMsgId
{};
380 case VideoAssetType::VERSION
:
381 dialogHeadingMsgId
= 40215;
382 dialogButtonMsgId
= 40216;
383 dialogNewHeadingMsgId
= 40217;
385 case VideoAssetType::EXTRA
:
386 dialogHeadingMsgId
= 40218;
387 dialogButtonMsgId
= 40219;
388 dialogNewHeadingMsgId
= 40220;
391 CLog::LogF(LOGERROR
, "Unknown asset type ({})", static_cast<int>(assetType
));
395 CVideoDatabase videodb
;
398 CLog::LogF(LOGERROR
, "Failed to open video database!");
402 CGUIDialogSelect
* dialog
{CServiceBroker::GetGUI()->GetWindowManager().GetWindow
<CGUIDialogSelect
>(
403 WINDOW_DIALOG_SELECT
)};
406 CLog::LogF(LOGERROR
, "Unable to get WINDOW_DIALOG_SELECT instance!");
410 //! @todo db refactor: should not be version, but asset
412 videodb
.GetVideoVersionTypes(itemType
, assetType
, list
);
417 std::string assetTitle
;
420 dialog
->SetItems(list
);
421 dialog
->SetHeading(dialogHeadingMsgId
);
422 dialog
->EnableButton(true, dialogButtonMsgId
);
425 if (dialog
->IsButtonPressed())
427 // create a new asset
428 assetTitle
= defaultName
;
429 if (CGUIKeyboardFactory::ShowAndGetInput(assetTitle
,
430 g_localizeStrings
.Get(dialogNewHeadingMsgId
), false))
432 assetTitle
= StringUtils::Trim(assetTitle
);
433 //! @todo db refactor: should not be version, but asset
434 assetId
= videodb
.AddVideoVersionType(assetTitle
, VideoAssetTypeOwner::USER
, assetType
);
437 else if (dialog
->IsConfirmed())
439 const std::shared_ptr
<CFileItem
> selectedItem
{dialog
->GetSelectedFileItem()};
440 assetId
= selectedItem
->GetVideoInfoTag()->GetAssetInfo().GetId();
441 assetTitle
= selectedItem
->GetVideoInfoTag()->GetAssetInfo().GetTitle();
449 const int dbId
{item
->GetVideoInfoTag()->m_iDbId
};
451 //! @todo db refactor: should not be versions, but assets
452 CFileItemList assets
;
453 videodb
.GetVideoVersions(itemType
, dbId
, assets
, assetType
);
455 // the selected video asset already exists
456 if (std::any_of(assets
.cbegin(), assets
.cend(),
457 [assetId
](const std::shared_ptr
<CFileItem
>& asset
)
458 { return asset
->GetVideoInfoTag()->m_iDbId
== assetId
; }))
460 CGUIDialogOK::ShowAndGetInput(CVariant
{40005},
461 StringUtils::Format(g_localizeStrings
.Get(40007), assetTitle
));
470 void CGUIDialogVideoManager::AppendItemFolderToFileBrowserSources(
471 std::vector
<CMediaSource
>& sources
)
473 const std::string itemDir
{URIUtils::GetParentPath(m_videoAsset
->GetDynPath())};
474 if (!itemDir
.empty() && XFILE::CDirectory::Exists(itemDir
))
476 CMediaSource itemSource
{};
477 itemSource
.strName
= g_localizeStrings
.Get(36041); // * Item folder
478 itemSource
.strPath
= itemDir
;
479 sources
.emplace_back(itemSource
);
483 void CGUIDialogVideoManager::RefreshSelectedVideoAsset()
485 if (!m_selectedVideoAsset
|| !m_selectedVideoAsset
->HasVideoInfoTag() ||
486 !m_videoAssetsList
->Size())
488 m_selectedVideoAsset
= std::make_shared
<CFileItem
>();
492 const int dbId
{m_selectedVideoAsset
->GetVideoInfoTag()->m_iDbId
};
493 const auto it
{std::find_if(m_videoAssetsList
->cbegin(), m_videoAssetsList
->cend(),
494 [dbId
](const auto& entry
)
495 { return entry
->GetVideoInfoTag()->m_iDbId
== dbId
; })};
497 if (it
== m_videoAssetsList
->cend())
498 m_selectedVideoAsset
= m_videoAssetsList
->Get(0);