Merge pull request #26373 from ksooo/app-fix-multi-resolve-playback
[xbmc.git] / xbmc / video / ContextMenus.cpp
blob4646aef87745651be7f78f0a6824468584482d5d
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 "ContextMenus.h"
11 #include "Autorun.h"
12 #include "FileItem.h"
13 #include "GUIUserMessages.h"
14 #include "ServiceBroker.h"
15 #include "application/Application.h"
16 #include "guilib/GUIComponent.h"
17 #include "guilib/GUIWindowManager.h"
18 #include "guilib/LocalizeStrings.h"
19 #include "music/MusicFileItemClassify.h"
20 #include "settings/Settings.h"
21 #include "settings/SettingsComponent.h"
22 #include "utils/PlayerUtils.h"
23 #include "utils/StringUtils.h"
24 #include "utils/URIUtils.h"
25 #include "video/VideoFileItemClassify.h"
26 #include "video/VideoInfoTag.h"
27 #include "video/VideoManagerTypes.h"
28 #include "video/VideoUtils.h"
29 #include "video/dialogs/GUIDialogVideoInfo.h"
30 #include "video/guilib/VideoGUIUtils.h"
31 #include "video/guilib/VideoPlayActionProcessor.h"
32 #include "video/guilib/VideoSelectActionProcessor.h"
33 #include "video/guilib/VideoVersionHelper.h"
35 #include <utility>
37 using namespace KODI;
39 namespace CONTEXTMENU
42 CVideoInfoBase::CVideoInfoBase(MediaType mediaType)
43 : CStaticContextMenuAction(19033), m_mediaType(std::move(mediaType))
47 bool CVideoInfoBase::IsVisible(const CFileItem& item) const
49 if (!item.HasVideoInfoTag())
50 return false;
52 if (item.IsPVRRecording())
53 return false; // pvr recordings have its own implementation for this
55 return item.GetVideoInfoTag()->m_type == m_mediaType;
58 bool CVideoInfoBase::Execute(const std::shared_ptr<CFileItem>& item) const
60 CGUIDialogVideoInfo::ShowFor(*item);
61 return true;
64 bool CVideoInfo::IsVisible(const CFileItem& item) const
66 if (CVideoInfoBase::IsVisible(item))
67 return true;
69 if (item.m_bIsFolder)
70 return false;
72 if (item.IsPVRRecording())
73 return false; // pvr recordings have its own implementation for this
75 const auto* tag{item.GetVideoInfoTag()};
76 return tag && tag->m_type == MediaTypeNone && !tag->IsEmpty() && VIDEO::IsVideo(item);
79 bool CVideoRemoveResumePoint::IsVisible(const CFileItem& itemIn) const
81 CFileItem item(itemIn.GetItemToPlay());
82 if (item.IsDeleted()) // e.g. trashed pvr recording
83 return false;
85 // Folders don't have a resume point
86 return !item.m_bIsFolder && VIDEO::UTILS::GetItemResumeInformation(item).isResumable;
89 bool CVideoRemoveResumePoint::Execute(const std::shared_ptr<CFileItem>& item) const
91 CVideoLibraryQueue::GetInstance().ResetResumePoint(item);
92 return true;
95 bool CVideoMarkWatched::IsVisible(const CFileItem& item) const
97 if (item.IsDeleted()) // e.g. trashed pvr recording
98 return false;
100 if (item.m_bIsFolder && item.IsPlugin()) // we cannot manage plugin folder's watched state
101 return false;
103 if (item.m_bIsFolder)
105 if (item.HasProperty("watchedepisodes") && item.HasProperty("totalepisodes"))
107 return item.GetProperty("watchedepisodes").asInteger() <
108 item.GetProperty("totalepisodes").asInteger();
110 else if (item.HasProperty("watched") && item.HasProperty("total"))
112 return item.GetProperty("watched").asInteger() < item.GetProperty("total").asInteger();
114 else if (VIDEO::IsVideoDb(item))
115 return true;
116 else if (StringUtils::StartsWithNoCase(item.GetPath(), "library://video/"))
117 return true;
118 else if (item.GetProperty("IsVideoFolder").asBoolean())
119 return true;
120 else
121 return !item.IsParentFolder() && URIUtils::IsPVRRecordingFileOrFolder(item.GetPath());
123 else if (!item.HasVideoInfoTag())
124 return false;
126 return item.GetVideoInfoTag()->GetPlayCount() == 0;
129 bool CVideoMarkWatched::Execute(const std::shared_ptr<CFileItem>& item) const
131 CVideoLibraryQueue::GetInstance().MarkAsWatched(item, true);
132 return true;
135 bool CVideoMarkUnWatched::IsVisible(const CFileItem& item) const
137 if (item.IsDeleted()) // e.g. trashed pvr recording
138 return false;
140 if (item.m_bIsFolder && item.IsPlugin()) // we cannot manage plugin folder's watched state
141 return false;
143 if (item.m_bIsFolder)
145 if (item.HasProperty("watchedepisodes"))
147 return item.GetProperty("watchedepisodes").asInteger() > 0;
149 else if (item.HasProperty("watched"))
151 return item.GetProperty("watched").asInteger() > 0;
153 else if (VIDEO::IsVideoDb(item))
154 return true;
155 else if (StringUtils::StartsWithNoCase(item.GetPath(), "library://video/"))
156 return true;
157 else if (item.GetProperty("IsVideoFolder").asBoolean())
158 return true;
159 else
160 return !item.IsParentFolder() && URIUtils::IsPVRRecordingFileOrFolder(item.GetPath());
162 else if (!item.HasVideoInfoTag())
163 return false;
165 return item.GetVideoInfoTag()->GetPlayCount() > 0;
168 bool CVideoMarkUnWatched::Execute(const std::shared_ptr<CFileItem>& item) const
170 CVideoLibraryQueue::GetInstance().MarkAsWatched(item, false);
171 return true;
174 bool CVideoBrowse::IsVisible(const CFileItem& item) const
176 return ((item.m_bIsFolder || item.IsFileFolder(FileFolderType::MASK_ONBROWSE)) &&
177 VIDEO::UTILS::IsItemPlayable(item));
180 bool CVideoBrowse::Execute(const std::shared_ptr<CFileItem>& item) const
182 int target = WINDOW_INVALID;
183 if (URIUtils::IsPVRRadioRecordingFileOrFolder(item->GetPath()))
184 target = WINDOW_RADIO_RECORDINGS;
185 else if (URIUtils::IsPVRTVRecordingFileOrFolder(item->GetPath()))
186 target = WINDOW_TV_RECORDINGS;
187 else
188 target = WINDOW_VIDEO_NAV;
190 auto& windowMgr = CServiceBroker::GetGUI()->GetWindowManager();
192 // For file directory browsing, we need item's dyn path, for everything else the path.
193 const std::string path{item->IsFileFolder(FileFolderType::MASK_ONBROWSE) ? item->GetDynPath()
194 : item->GetPath()};
196 if (target == windowMgr.GetActiveWindow())
198 CGUIMessage msg(GUI_MSG_NOTIFY_ALL, target, 0, GUI_MSG_UPDATE);
199 msg.SetStringParam(path);
200 windowMgr.SendMessage(msg);
202 else
204 windowMgr.ActivateWindow(target, {path, "return"});
206 return true;
209 bool CVideoChooseVersion::IsVisible(const CFileItem& item) const
211 return item.HasVideoVersions() &&
212 !CServiceBroker::GetSettingsComponent()->GetSettings()->GetBool(
213 CSettings::SETTING_VIDEOLIBRARY_SHOWVIDEOVERSIONSASFOLDER) &&
214 !VIDEO::IsVideoAssetFile(item);
217 bool CVideoChooseVersion::Execute(const std::shared_ptr<CFileItem>& item) const
219 // force selection dialog, regardless of any settings like 'Select default video version'
220 item->SetProperty("needs_resolved_video_asset", true);
221 item->SetProperty("video_asset_type", static_cast<int>(VideoAssetType::VERSION));
222 KODI::VIDEO::GUILIB::CVideoSelectActionProcessor proc{item};
223 const bool ret = proc.ProcessDefaultAction();
224 item->ClearProperty("needs_resolved_video_asset");
225 item->ClearProperty("video_asset_type");
226 return ret;
229 std::string CVideoResume::GetLabel(const CFileItem& item) const
231 return VIDEO::UTILS::GetResumeString(item.GetItemToPlay());
234 bool CVideoResume::IsVisible(const CFileItem& itemIn) const
236 CFileItem item(itemIn.GetItemToPlay());
237 if (item.IsDeleted()) // e.g. trashed pvr recording
238 return false;
240 return VIDEO::UTILS::GetItemResumeInformation(item).isResumable;
243 namespace
245 enum class PlayMode
247 PLAY,
248 PLAY_USING,
249 PLAY_VERSION_USING,
250 PLAY_STACK_PART,
251 RESUME,
254 void SetPathAndPlay(const std::shared_ptr<CFileItem>& item, PlayMode mode)
256 if (item->IsLiveTV()) // pvr tv or pvr radio?
258 g_application.PlayMedia(*item, "", PLAYLIST::Id::TYPE_VIDEO);
260 else
262 const auto itemCopy{std::make_shared<CFileItem>(*item)};
263 if (VIDEO::IsVideoDb(*itemCopy))
265 if (!itemCopy->m_bIsFolder)
267 itemCopy->SetProperty("original_listitem_url", item->GetPath());
268 itemCopy->SetPath(item->GetVideoInfoTag()->m_strFileNameAndPath);
270 else if (itemCopy->HasVideoInfoTag() && itemCopy->GetVideoInfoTag()->IsDefaultVideoVersion())
272 //! @todo get rid of "videos with versions as folder" hack!
273 itemCopy->m_bIsFolder = false;
277 if (mode == PlayMode::PLAY_VERSION_USING)
279 // force video version selection dialog
280 itemCopy->SetProperty("needs_resolved_video_asset", true);
282 else
284 // play the given/default video version, if multiple versions are available
285 itemCopy->SetProperty("has_resolved_video_asset", true);
288 KODI::VIDEO::GUILIB::CVideoPlayActionProcessor proc{itemCopy};
289 if (mode == PlayMode::PLAY_USING || mode == PlayMode::PLAY_VERSION_USING)
290 proc.SetChoosePlayer();
291 else if (mode == PlayMode::PLAY_STACK_PART)
292 proc.SetChooseStackPart();
294 if (mode == PlayMode::RESUME && (itemCopy->GetStartOffset() == STARTOFFSET_RESUME ||
295 VIDEO::UTILS::GetItemResumeInformation(*item).isResumable))
296 proc.ProcessAction(VIDEO::GUILIB::ACTION_RESUME);
297 else if (mode == PlayMode::PLAY_STACK_PART)
298 proc.ProcessDefaultAction();
299 else // all other modes are actually PLAY
300 proc.ProcessAction(VIDEO::GUILIB::ACTION_PLAY_FROM_BEGINNING);
303 } // unnamed namespace
305 bool CVideoResume::Execute(const std::shared_ptr<CFileItem>& itemIn) const
307 const auto item{std::make_shared<CFileItem>(itemIn->GetItemToPlay())};
308 #ifdef HAS_OPTICAL_DRIVE
309 if (item->IsDVD() || MUSIC::IsCDDA(*item))
310 return MEDIA_DETECT::CAutorun::PlayDisc(item->GetPath(), true, false);
311 #endif
313 item->SetStartOffset(STARTOFFSET_RESUME);
314 SetPathAndPlay(item, PlayMode::RESUME);
315 return true;
318 std::string CVideoPlay::GetLabel(const CFileItem& itemIn) const
320 CFileItem item(itemIn.GetItemToPlay());
321 if (item.IsLiveTV())
322 return g_localizeStrings.Get(19000); // Switch to channel
323 if (VIDEO::UTILS::GetItemResumeInformation(item).isResumable)
324 return g_localizeStrings.Get(12021); // Play from beginning
325 return g_localizeStrings.Get(208); // Play
328 bool CVideoPlay::IsVisible(const CFileItem& item) const
330 return VIDEO::UTILS::IsItemPlayable(item);
333 bool CVideoPlay::Execute(const std::shared_ptr<CFileItem>& itemIn) const
335 const auto item{std::make_shared<CFileItem>(itemIn->GetItemToPlay())};
336 #ifdef HAS_OPTICAL_DRIVE
337 if (item->IsDVD() || MUSIC::IsCDDA(*item))
338 return MEDIA_DETECT::CAutorun::PlayDisc(item->GetPath(), true, true);
339 #endif
340 SetPathAndPlay(item, PlayMode::PLAY);
341 return true;
344 bool CVideoPlayUsing::IsVisible(const CFileItem& item) const
346 if (item.HasVideoVersions() &&
347 !CServiceBroker::GetSettingsComponent()->GetSettings()->GetBool(
348 CSettings::SETTING_VIDEOLIBRARY_SHOWVIDEOVERSIONSASFOLDER) &&
349 !VIDEO::IsVideoAssetFile(item))
350 return false;
352 if (item.IsLiveTV())
353 return false;
355 return (CPlayerUtils::HasItemMultiplePlayers(item) && VIDEO::UTILS::IsItemPlayable(item));
358 bool CVideoPlayUsing::Execute(const std::shared_ptr<CFileItem>& itemIn) const
360 const auto item{std::make_shared<CFileItem>(itemIn->GetItemToPlay())};
361 SetPathAndPlay(item, PlayMode::PLAY_USING);
362 return true;
365 bool CVideoPlayStackPart::IsVisible(const CFileItem& item) const
367 return !item.IsParentFolder() && item.IsStack();
370 bool CVideoPlayStackPart::Execute(const std::shared_ptr<CFileItem>& itemIn) const
372 const auto item{std::make_shared<CFileItem>(itemIn->GetItemToPlay())};
373 SetPathAndPlay(item, PlayMode::PLAY_STACK_PART);
374 return true;
377 bool CVideoPlayVersionUsing::IsVisible(const CFileItem& item) const
379 return item.HasVideoVersions() &&
380 !CServiceBroker::GetSettingsComponent()->GetSettings()->GetBool(
381 CSettings::SETTING_VIDEOLIBRARY_SHOWVIDEOVERSIONSASFOLDER) &&
382 !VIDEO::IsVideoAssetFile(item);
385 bool CVideoPlayVersionUsing::Execute(const std::shared_ptr<CFileItem>& itemIn) const
387 const auto item{std::make_shared<CFileItem>(itemIn->GetItemToPlay())};
388 item->SetProperty("video_asset_type", static_cast<int>(VideoAssetType::VERSION));
389 SetPathAndPlay(item, PlayMode::PLAY_VERSION_USING);
390 return true;
393 namespace
395 void SelectNextItem(int windowID)
397 auto& windowMgr = CServiceBroker::GetGUI()->GetWindowManager();
398 CGUIWindow* window = windowMgr.GetWindow(windowID);
399 if (window)
401 const int viewContainerID = window->GetViewContainerID();
402 if (viewContainerID > 0)
404 CGUIMessage msg1(GUI_MSG_ITEM_SELECTED, windowID, viewContainerID);
405 windowMgr.SendMessage(msg1, windowID);
407 CGUIMessage msg2(GUI_MSG_ITEM_SELECT, windowID, viewContainerID, msg1.GetParam1() + 1);
408 windowMgr.SendMessage(msg2, windowID);
413 bool CanQueue(const CFileItem& item)
415 if (!item.CanQueue())
416 return false;
418 const int windowId = CServiceBroker::GetGUI()->GetWindowManager().GetActiveWindow();
419 if (windowId == WINDOW_VIDEO_PLAYLIST)
420 return false; // Already queued
422 return true;
424 } // unnamed namespace
426 bool CVideoQueue::IsVisible(const CFileItem& item) const
428 if (!CanQueue(item))
429 return false;
431 return VIDEO::UTILS::IsItemPlayable(item);
434 bool CVideoQueue::Execute(const std::shared_ptr<CFileItem>& item) const
436 VIDEO::UTILS::QueueItem(item, VIDEO::UTILS::QueuePosition::POSITION_END);
438 // Set selection to next item in active window's view.
439 const int windowID = CServiceBroker::GetGUI()->GetWindowManager().GetActiveWindow();
440 SelectNextItem(windowID);
442 return true;
445 bool CVideoPlayNext::IsVisible(const CFileItem& item) const
447 if (!CanQueue(item))
448 return false;
450 return VIDEO::UTILS::IsItemPlayable(item);
453 bool CVideoPlayNext::Execute(const std::shared_ptr<CFileItem>& item) const
455 VIDEO::UTILS::QueueItem(item, VIDEO::UTILS::QueuePosition::POSITION_BEGIN);
456 return true;
459 std::string CVideoPlayAndQueue::GetLabel(const CFileItem& item) const
461 if (VIDEO::UTILS::IsAutoPlayNextItem(item))
462 return g_localizeStrings.Get(13434); // Play only this
463 else
464 return g_localizeStrings.Get(13412); // Play from here
467 bool CVideoPlayAndQueue::IsVisible(const CFileItem& item) const
469 if (!CanQueue(item))
470 return false;
472 const int windowId = CServiceBroker::GetGUI()->GetWindowManager().GetActiveWindow();
473 if ((windowId == WINDOW_TV_RECORDINGS || windowId == WINDOW_RADIO_RECORDINGS) &&
474 item.IsUsablePVRRecording())
475 return true;
477 return false; //! @todo implement
480 bool CVideoPlayAndQueue::Execute(const std::shared_ptr<CFileItem>& item) const
482 const int windowId = CServiceBroker::GetGUI()->GetWindowManager().GetActiveWindow();
483 if ((windowId == WINDOW_TV_RECORDINGS || windowId == WINDOW_RADIO_RECORDINGS) &&
484 item->IsUsablePVRRecording())
486 const ContentUtils::PlayMode mode = VIDEO::UTILS::IsAutoPlayNextItem(*item)
487 ? ContentUtils::PlayMode::PLAY_ONLY_THIS
488 : ContentUtils::PlayMode::PLAY_FROM_HERE;
489 VIDEO::UTILS::PlayItem(item, "", mode);
490 return true;
493 return true; //! @todo implement