[video] fix selection after changing video or extra art
[xbmc.git] / xbmc / video / dialogs / GUIDialogVideoManager.cpp
blob344d338d726a7acc723ac12734bdcc293bdb0f3c
1 /*
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.
7 */
9 #include "GUIDialogVideoManager.h"
11 #include "FileItem.h"
12 #include "GUIUserMessages.h"
13 #include "MediaSource.h"
14 #include "ServiceBroker.h"
15 #include "dialogs/GUIDialogOK.h"
16 #include "dialogs/GUIDialogSelect.h"
17 #include "dialogs/GUIDialogYesNo.h"
18 #include "filesystem/Directory.h"
19 #include "guilib/GUIComponent.h"
20 #include "guilib/GUIKeyboardFactory.h"
21 #include "guilib/GUIWindowManager.h"
22 #include "guilib/LocalizeStrings.h"
23 #include "input/actions/Action.h"
24 #include "input/actions/ActionIDs.h"
25 #include "playlists/PlayListTypes.h"
26 #include "utils/StringUtils.h"
27 #include "utils/URIUtils.h"
28 #include "utils/log.h"
29 #include "video/VideoManagerTypes.h"
30 #include "video/VideoThumbLoader.h"
31 #include "video/VideoUtils.h"
32 #include "video/dialogs/GUIDialogVideoInfo.h"
33 #include "video/guilib/VideoPlayActionProcessor.h"
35 #include <algorithm>
36 #include <string>
38 static constexpr unsigned int CONTROL_LABEL_TITLE = 2;
40 static constexpr unsigned int CONTROL_BUTTON_PLAY = 21;
41 static constexpr unsigned int CONTROL_BUTTON_REMOVE = 26;
42 static constexpr unsigned int CONTROL_BUTTON_CHOOSE_ART = 27;
44 static constexpr unsigned int CONTROL_LIST_ASSETS = 50;
46 CGUIDialogVideoManager::CGUIDialogVideoManager(int windowId)
47 : CGUIDialog(windowId, "DialogVideoManager.xml"),
48 m_videoAsset(std::make_shared<CFileItem>()),
49 m_videoAssetsList(std::make_unique<CFileItemList>()),
50 m_selectedVideoAsset(std::make_shared<CFileItem>())
52 m_loadType = KEEP_IN_MEMORY;
54 if (!m_database.Open())
55 CLog::LogF(LOGERROR, "Failed to open video database!");
58 bool CGUIDialogVideoManager::OnMessage(CGUIMessage& message)
60 switch (message.GetMessage())
62 case GUI_MSG_WINDOW_DEINIT:
64 Clear();
65 break;
68 case GUI_MSG_CLICKED:
70 const int control{message.GetSenderId()};
71 if (control == CONTROL_LIST_ASSETS)
73 const int action{message.GetParam1()};
74 if (action == ACTION_SELECT_ITEM || action == ACTION_MOUSE_LEFT_CLICK)
76 if (UpdateSelectedAsset())
77 SET_CONTROL_FOCUS(CONTROL_BUTTON_PLAY, 0);
80 else if (control == CONTROL_BUTTON_PLAY)
82 Play();
84 else if (control == CONTROL_BUTTON_REMOVE)
86 Remove();
88 else if (control == CONTROL_BUTTON_CHOOSE_ART)
90 ChooseArt();
92 break;
96 return CGUIDialog::OnMessage(message);
99 bool CGUIDialogVideoManager::OnAction(const CAction& action)
101 const int actionId{action.GetID()};
102 if (actionId == ACTION_MOVE_DOWN || actionId == ACTION_MOVE_UP || actionId == ACTION_PAGE_DOWN ||
103 actionId == ACTION_PAGE_UP || actionId == ACTION_FIRST_PAGE || actionId == ACTION_LAST_PAGE)
105 if (GetFocusedControlID() == CONTROL_LIST_ASSETS)
107 CGUIDialog::OnAction(action);
108 return UpdateSelectedAsset();
112 return CGUIDialog::OnAction(action);
115 void CGUIDialogVideoManager::OnInitWindow()
117 CGUIDialog::OnInitWindow();
119 SET_CONTROL_LABEL(CONTROL_LABEL_TITLE,
120 StringUtils::Format(g_localizeStrings.Get(GetHeadingId()),
121 m_videoAsset->GetVideoInfoTag()->GetTitle()));
123 CGUIMessage msg{GUI_MSG_LABEL_BIND, GetID(), CONTROL_LIST_ASSETS, 0, 0, m_videoAssetsList.get()};
124 OnMessage(msg);
126 UpdateControls();
129 void CGUIDialogVideoManager::Clear()
131 m_videoAssetsList->Clear();
134 void CGUIDialogVideoManager::UpdateButtons()
136 // Enabled if list not empty
137 if (!m_videoAssetsList->IsEmpty())
139 CONTROL_ENABLE(CONTROL_BUTTON_CHOOSE_ART);
140 CONTROL_ENABLE(CONTROL_BUTTON_REMOVE);
141 CONTROL_ENABLE(CONTROL_BUTTON_PLAY);
143 SET_CONTROL_FOCUS(CONTROL_LIST_ASSETS, 0);
145 else
147 CONTROL_DISABLE(CONTROL_BUTTON_CHOOSE_ART);
148 CONTROL_DISABLE(CONTROL_BUTTON_REMOVE);
149 CONTROL_DISABLE(CONTROL_BUTTON_PLAY);
153 void CGUIDialogVideoManager::UpdateAssetsList()
155 // find new item in list and select it
156 for (int i = 0; i < m_videoAssetsList->Size(); ++i)
158 if (m_videoAssetsList->Get(i)->GetVideoInfoTag()->m_iDbId ==
159 m_selectedVideoAsset->GetVideoInfoTag()->m_iDbId)
161 CONTROL_SELECT_ITEM(CONTROL_LIST_ASSETS, i);
162 break;
167 bool CGUIDialogVideoManager::UpdateSelectedAsset()
169 CGUIMessage msg{GUI_MSG_ITEM_SELECTED, GetID(), CONTROL_LIST_ASSETS};
170 OnMessage(msg);
172 const int item{msg.GetParam1()};
173 if (item >= 0 && item < m_videoAssetsList->Size())
175 m_selectedVideoAsset = m_videoAssetsList->Get(item);
176 UpdateControls();
177 return true;
179 return false;
182 void CGUIDialogVideoManager::DisableRemove()
184 CONTROL_DISABLE(CONTROL_BUTTON_REMOVE);
187 void CGUIDialogVideoManager::EnableRemove()
189 CONTROL_ENABLE(CONTROL_BUTTON_REMOVE);
192 void CGUIDialogVideoManager::UpdateControls()
194 UpdateButtons();
195 UpdateAssetsList();
198 void CGUIDialogVideoManager::Refresh()
200 Clear();
202 const int dbId{m_videoAsset->GetVideoInfoTag()->m_iDbId};
203 const MediaType mediaType{m_videoAsset->GetVideoInfoTag()->m_type};
204 const VideoDbContentType itemType{m_videoAsset->GetVideoContentType()};
206 //! @todo db refactor: should not be versions, but assets
207 m_database.GetVideoVersions(itemType, dbId, *m_videoAssetsList, GetVideoAssetType());
208 m_videoAssetsList->SetContent(CMediaTypes::ToPlural(mediaType));
210 CVideoThumbLoader loader;
212 for (auto& item : *m_videoAssetsList)
214 item->SetProperty("noartfallbacktoowner", true);
215 loader.LoadItem(item.get());
218 CGUIMessage msg{GUI_MSG_LABEL_BIND, GetID(), CONTROL_LIST_ASSETS, 0, 0, m_videoAssetsList.get()};
219 OnMessage(msg);
222 void CGUIDialogVideoManager::SetVideoAsset(const std::shared_ptr<CFileItem>& item)
224 if (!item || !item->HasVideoInfoTag() || item->GetVideoInfoTag()->m_type != MediaTypeMovie)
226 CLog::LogF(LOGERROR, "Unexpected video item!");
227 return;
230 m_videoAsset = item;
232 Refresh();
235 void CGUIDialogVideoManager::CloseAll()
237 // close our dialog
238 Close(true);
240 // close the video info dialog if exists
241 CGUIDialogVideoInfo* dialog{
242 CServiceBroker::GetGUI()->GetWindowManager().GetWindow<CGUIDialogVideoInfo>(
243 WINDOW_DIALOG_VIDEO_INFO)};
244 if (dialog)
245 dialog->Close(true);
248 namespace
250 class CVideoPlayActionProcessor : public VIDEO::GUILIB::CVideoPlayActionProcessorBase
252 public:
253 explicit CVideoPlayActionProcessor(const std::shared_ptr<CFileItem>& item)
254 : CVideoPlayActionProcessorBase(item)
258 protected:
259 bool OnResumeSelected() override
261 m_item->SetStartOffset(STARTOFFSET_RESUME);
262 Play();
263 return true;
266 bool OnPlaySelected() override
268 Play();
269 return true;
272 private:
273 void Play()
275 m_item->SetProperty("playlist_type_hint", PLAYLIST::TYPE_VIDEO);
276 const ContentUtils::PlayMode mode{m_item->GetProperty("CheckAutoPlayNextItem").asBoolean()
277 ? ContentUtils::PlayMode::CHECK_AUTO_PLAY_NEXT_ITEM
278 : ContentUtils::PlayMode::PLAY_ONLY_THIS};
279 VIDEO_UTILS::PlayItem(m_item, "", mode);
282 } // unnamed namespace
284 void CGUIDialogVideoManager::Play()
286 CloseAll();
288 CVideoPlayActionProcessor proc{m_selectedVideoAsset};
289 proc.ProcessDefaultAction();
292 void CGUIDialogVideoManager::Remove()
294 // confirm to remove
295 if (!CGUIDialogYesNo::ShowAndGetInput(
296 CVariant(40018),
297 StringUtils::Format(g_localizeStrings.Get(40020),
298 m_selectedVideoAsset->GetVideoInfoTag()->GetAssetInfo().GetTitle())))
300 return;
303 //! @todo db refactor: should not be version, but asset
304 m_database.RemoveVideoVersion(m_selectedVideoAsset->GetVideoInfoTag()->m_iDbId);
306 // refresh data and controls
307 Refresh();
308 UpdateControls();
311 void CGUIDialogVideoManager::Rename()
313 const int idAsset{
314 ChooseVideoAsset(m_videoAsset, GetVideoAssetType(), m_selectedVideoAsset->m_strTitle)};
315 if (idAsset != -1)
317 //! @todo db refactor: should not be version, but asset
318 m_database.SetVideoVersion(m_selectedVideoAsset->GetVideoInfoTag()->m_iDbId, idAsset);
321 // refresh data and controls
322 Refresh();
323 UpdateControls();
326 void CGUIDialogVideoManager::ChooseArt()
328 if (!CGUIDialogVideoInfo::ChooseAndManageVideoItemArtwork(m_selectedVideoAsset))
329 return;
331 // refresh data and controls
332 Refresh();
333 UpdateControls();
336 void CGUIDialogVideoManager::SetSelectedVideoAsset(const std::shared_ptr<CFileItem>& asset)
338 const int dbId{asset->GetVideoInfoTag()->m_iDbId};
339 const auto it{std::find_if(m_videoAssetsList->cbegin(), m_videoAssetsList->cend(),
340 [dbId](const auto& entry)
341 { return entry->GetVideoInfoTag()->m_iDbId == dbId; })};
342 if (it != m_videoAssetsList->cend())
343 m_selectedVideoAsset = (*it);
344 else
345 CLog::LogF(LOGERROR, "Item to select not found in asset list!");
347 UpdateControls();
350 int CGUIDialogVideoManager::ChooseVideoAsset(const std::shared_ptr<CFileItem>& item,
351 VideoAssetType assetType,
352 const std::string& defaultName)
354 if (!item || !item->HasVideoInfoTag())
355 return -1;
357 const VideoDbContentType itemType{item->GetVideoContentType()};
358 if (itemType != VideoDbContentType::MOVIES)
359 return -1;
361 int dialogHeadingMsgId{};
362 int dialogButtonMsgId{};
363 int dialogNewHeadingMsgId{};
365 switch (assetType)
367 case VideoAssetType::VERSION:
368 dialogHeadingMsgId = 40215;
369 dialogButtonMsgId = 40216;
370 dialogNewHeadingMsgId = 40217;
371 break;
372 case VideoAssetType::EXTRA:
373 dialogHeadingMsgId = 40218;
374 dialogButtonMsgId = 40219;
375 dialogNewHeadingMsgId = 40220;
376 break;
377 default:
378 CLog::LogF(LOGERROR, "Unknown asset type ({})", static_cast<int>(assetType));
379 return -1;
382 CVideoDatabase videodb;
383 if (!videodb.Open())
385 CLog::LogF(LOGERROR, "Failed to open video database!");
386 return -1;
389 CGUIDialogSelect* dialog{CServiceBroker::GetGUI()->GetWindowManager().GetWindow<CGUIDialogSelect>(
390 WINDOW_DIALOG_SELECT)};
391 if (!dialog)
393 CLog::LogF(LOGERROR, "Unable to get WINDOW_DIALOG_SELECT instance!");
394 return -1;
397 //! @todo db refactor: should not be version, but asset
398 CFileItemList list;
399 videodb.GetVideoVersionTypes(itemType, assetType, list);
401 int assetId{-1};
402 while (true)
404 std::string assetTitle;
406 dialog->Reset();
407 dialog->SetItems(list);
408 dialog->SetHeading(dialogHeadingMsgId);
409 dialog->EnableButton(true, dialogButtonMsgId);
410 dialog->Open();
412 if (dialog->IsButtonPressed())
414 // create a new asset
415 assetTitle = defaultName;
416 if (CGUIKeyboardFactory::ShowAndGetInput(assetTitle,
417 g_localizeStrings.Get(dialogNewHeadingMsgId), false))
419 assetTitle = StringUtils::Trim(assetTitle);
420 //! @todo db refactor: should not be version, but asset
421 assetId = videodb.AddVideoVersionType(assetTitle, VideoAssetTypeOwner::USER, assetType);
424 else if (dialog->IsConfirmed())
426 const std::shared_ptr<CFileItem> selectedItem{dialog->GetSelectedFileItem()};
427 assetId = selectedItem->GetVideoInfoTag()->GetAssetInfo().GetId();
428 assetTitle = selectedItem->GetVideoInfoTag()->GetAssetInfo().GetTitle();
430 else
431 return -1;
433 if (assetId < 0)
434 return -1;
436 const int dbId{item->GetVideoInfoTag()->m_iDbId};
438 //! @todo db refactor: should not be versions, but assets
439 CFileItemList assets;
440 videodb.GetVideoVersions(itemType, dbId, assets, assetType);
442 // the selected video asset already exists
443 if (std::any_of(assets.cbegin(), assets.cend(),
444 [assetId](const std::shared_ptr<CFileItem>& asset)
445 { return asset->GetVideoInfoTag()->m_iDbId == assetId; }))
447 CGUIDialogOK::ShowAndGetInput(CVariant{40005},
448 StringUtils::Format(g_localizeStrings.Get(40007), assetTitle));
450 else
451 break;
454 return assetId;
457 void CGUIDialogVideoManager::AppendItemFolderToFileBrowserSources(
458 std::vector<CMediaSource>& sources)
460 const std::string itemDir{URIUtils::GetParentPath(m_videoAsset->GetDynPath())};
461 if (!itemDir.empty() && XFILE::CDirectory::Exists(itemDir))
463 CMediaSource itemSource{};
464 itemSource.strName = g_localizeStrings.Get(36041); // * Item folder
465 itemSource.strPath = itemDir;
466 sources.emplace_back(itemSource);