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 "ContextMenus.h"
12 #include "ContextMenuManager.h"
14 #include "GUIUserMessages.h"
15 #include "ServiceBroker.h"
16 #include "application/Application.h"
17 #include "cores/playercorefactory/PlayerCoreFactory.h"
18 #include "guilib/GUIComponent.h"
19 #include "guilib/GUIWindowManager.h"
20 #include "guilib/LocalizeStrings.h"
21 #include "music/MusicFileItemClassify.h"
22 #include "settings/Settings.h"
23 #include "settings/SettingsComponent.h"
24 #include "utils/ContentUtils.h"
25 #include "utils/ExecString.h"
26 #include "utils/StringUtils.h"
27 #include "utils/URIUtils.h"
28 #include "video/VideoFileItemClassify.h"
29 #include "video/VideoInfoTag.h"
30 #include "video/VideoManagerTypes.h"
31 #include "video/VideoUtils.h"
32 #include "video/dialogs/GUIDialogVideoInfo.h"
33 #include "video/guilib/VideoGUIUtils.h"
34 #include "video/guilib/VideoPlayActionProcessor.h"
35 #include "video/guilib/VideoSelectActionProcessor.h"
36 #include "video/guilib/VideoVersionHelper.h"
45 CVideoInfo::CVideoInfo(MediaType mediaType
)
46 : CStaticContextMenuAction(19033), m_mediaType(std::move(mediaType
))
50 bool CVideoInfo::IsVisible(const CFileItem
& item
) const
52 if (!item
.HasVideoInfoTag())
55 if (item
.IsPVRRecording())
56 return false; // pvr recordings have its own implementation for this
58 return item
.GetVideoInfoTag()->m_type
== m_mediaType
;
61 bool CVideoInfo::Execute(const std::shared_ptr
<CFileItem
>& item
) const
63 CGUIDialogVideoInfo::ShowFor(*item
);
67 bool CVideoRemoveResumePoint::IsVisible(const CFileItem
& itemIn
) const
69 CFileItem
item(itemIn
.GetItemToPlay());
70 if (item
.IsDeleted()) // e.g. trashed pvr recording
73 // Folders don't have a resume point
74 return !item
.m_bIsFolder
&& VIDEO::UTILS::GetItemResumeInformation(item
).isResumable
;
77 bool CVideoRemoveResumePoint::Execute(const std::shared_ptr
<CFileItem
>& item
) const
79 CVideoLibraryQueue::GetInstance().ResetResumePoint(item
);
83 bool CVideoMarkWatched::IsVisible(const CFileItem
& item
) const
85 if (item
.IsDeleted()) // e.g. trashed pvr recording
88 if (item
.m_bIsFolder
&& item
.IsPlugin()) // we cannot manage plugin folder's watched state
91 if (item
.m_bIsFolder
) // Only allow video db content, video and recording folders to be updated recursively
93 if (item
.HasVideoInfoTag())
94 return VIDEO::IsVideoDb(item
);
95 else if (item
.GetProperty("IsVideoFolder").asBoolean())
98 return !item
.IsParentFolder() && URIUtils::IsPVRRecordingFileOrFolder(item
.GetPath());
100 else if (!item
.HasVideoInfoTag())
103 return item
.GetVideoInfoTag()->GetPlayCount() == 0;
106 bool CVideoMarkWatched::Execute(const std::shared_ptr
<CFileItem
>& item
) const
108 CVideoLibraryQueue::GetInstance().MarkAsWatched(item
, true);
112 bool CVideoMarkUnWatched::IsVisible(const CFileItem
& item
) const
114 if (item
.IsDeleted()) // e.g. trashed pvr recording
117 if (item
.m_bIsFolder
&& item
.IsPlugin()) // we cannot manage plugin folder's watched state
120 if (item
.m_bIsFolder
) // Only allow video db content, video and recording folders to be updated recursively
122 if (item
.HasVideoInfoTag())
123 return VIDEO::IsVideoDb(item
);
124 else if (item
.GetProperty("IsVideoFolder").asBoolean())
127 return !item
.IsParentFolder() && URIUtils::IsPVRRecordingFileOrFolder(item
.GetPath());
129 else if (!item
.HasVideoInfoTag())
132 return item
.GetVideoInfoTag()->GetPlayCount() > 0;
135 bool CVideoMarkUnWatched::Execute(const std::shared_ptr
<CFileItem
>& item
) const
137 CVideoLibraryQueue::GetInstance().MarkAsWatched(item
, false);
141 bool CVideoBrowse::IsVisible(const CFileItem
& item
) const
143 return ((item
.m_bIsFolder
|| item
.IsFileFolder(EFILEFOLDER_MASK_ONBROWSE
)) &&
144 VIDEO::UTILS::IsItemPlayable(item
));
147 bool CVideoBrowse::Execute(const std::shared_ptr
<CFileItem
>& item
) const
149 int target
= WINDOW_INVALID
;
150 if (URIUtils::IsPVRRadioRecordingFileOrFolder(item
->GetPath()))
151 target
= WINDOW_RADIO_RECORDINGS
;
152 else if (URIUtils::IsPVRTVRecordingFileOrFolder(item
->GetPath()))
153 target
= WINDOW_TV_RECORDINGS
;
155 target
= WINDOW_VIDEO_NAV
;
157 auto& windowMgr
= CServiceBroker::GetGUI()->GetWindowManager();
159 // For file directory browsing, we need item's dyn path, for everything else the path.
160 const std::string path
{item
->IsFileFolder(EFILEFOLDER_MASK_ONBROWSE
) ? item
->GetDynPath()
163 if (target
== windowMgr
.GetActiveWindow())
165 CGUIMessage
msg(GUI_MSG_NOTIFY_ALL
, target
, 0, GUI_MSG_UPDATE
);
166 msg
.SetStringParam(path
);
167 windowMgr
.SendMessage(msg
);
171 windowMgr
.ActivateWindow(target
, {path
, "return"});
178 bool ExecuteAction(const CExecString
& execute
)
180 const std::string
& execStr
{execute
.GetExecString()};
181 if (!execStr
.empty())
183 CGUIMessage
message(GUI_MSG_EXECUTE
, 0, 0);
184 message
.SetStringParam(execStr
);
185 CServiceBroker::GetGUI()->GetWindowManager().SendMessage(message
);
191 class CVideoSelectActionProcessor
: public VIDEO::GUILIB::CVideoSelectActionProcessorBase
194 explicit CVideoSelectActionProcessor(const std::shared_ptr
<CFileItem
>& item
)
195 : CVideoSelectActionProcessorBase(item
)
200 bool OnPlayPartSelected(unsigned int part
) override
202 // part numbers are 1-based
203 ExecuteAction({"PlayMedia", *m_item
, StringUtils::Format("playoffset={}", part
- 1)});
207 bool OnResumeSelected() override
209 ExecuteAction({"PlayMedia", *m_item
, "resume"});
213 bool OnPlaySelected() override
215 ExecuteAction({"PlayMedia", *m_item
, "noresume"});
219 bool OnQueueSelected() override
221 ExecuteAction({"QueueMedia", *m_item
, ""});
225 bool OnInfoSelected() override
227 CGUIDialogVideoInfo::ShowFor(*m_item
);
231 bool OnMoreSelected() override
233 CONTEXTMENU::ShowFor(m_item
, CContextMenuManager::MAIN
);
237 } // unnamed namespace
239 bool CVideoChooseVersion::IsVisible(const CFileItem
& item
) const
241 return item
.HasVideoVersions() &&
242 !CServiceBroker::GetSettingsComponent()->GetSettings()->GetBool(
243 CSettings::SETTING_VIDEOLIBRARY_SHOWVIDEOVERSIONSASFOLDER
) &&
244 !VIDEO::IsVideoAssetFile(item
);
247 bool CVideoChooseVersion::Execute(const std::shared_ptr
<CFileItem
>& item
) const
249 // force selection dialog, regardless of any settings like 'Select default video version'
250 item
->SetProperty("needs_resolved_video_asset", true);
251 item
->SetProperty("video_asset_type", static_cast<int>(VideoAssetType::VERSION
));
252 CVideoSelectActionProcessor proc
{item
};
253 const bool ret
= proc
.ProcessDefaultAction();
254 item
->ClearProperty("needs_resolved_video_asset");
255 item
->ClearProperty("video_asset_type");
259 std::string
CVideoResume::GetLabel(const CFileItem
& item
) const
261 return VIDEO::UTILS::GetResumeString(item
.GetItemToPlay());
264 bool CVideoResume::IsVisible(const CFileItem
& itemIn
) const
266 CFileItem
item(itemIn
.GetItemToPlay());
267 if (item
.IsDeleted()) // e.g. trashed pvr recording
270 return VIDEO::UTILS::GetItemResumeInformation(item
).isResumable
;
275 std::vector
<std::string
> GetPlayers(const CPlayerCoreFactory
& playerCoreFactory
,
276 const CFileItem
& item
)
278 std::vector
<std::string
> players
;
279 if (VIDEO::IsVideoDb(item
))
281 //! @todo CPlayerCoreFactory and classes called from there do not handle dyn path correctly.
282 CFileItem item2
{item
};
283 item2
.SetPath(item
.GetDynPath());
284 playerCoreFactory
.GetPlayers(item2
, players
);
287 playerCoreFactory
.GetPlayers(item
, players
);
292 class CVideoPlayActionProcessor
: public VIDEO::GUILIB::CVideoPlayActionProcessorBase
295 CVideoPlayActionProcessor(const std::shared_ptr
<CFileItem
>& item
, bool choosePlayer
)
296 : CVideoPlayActionProcessorBase(item
), m_choosePlayer(choosePlayer
)
301 bool OnResumeSelected() override
303 m_item
->SetStartOffset(STARTOFFSET_RESUME
);
308 bool OnPlaySelected() override
313 const CPlayerCoreFactory
& playerCoreFactory
{CServiceBroker::GetPlayerCoreFactory()};
314 const std::vector
<std::string
> players
{GetPlayers(playerCoreFactory
, *m_item
)};
315 player
= playerCoreFactory
.SelectPlayerDialog(players
);
318 m_userCancelled
= true;
319 return true; // User cancelled player selection. We're done.
328 void Play(const std::string
& player
= "")
330 m_item
->SetProperty("playlist_type_hint", static_cast<int>(PLAYLIST::Id::TYPE_VIDEO
));
331 const ContentUtils::PlayMode mode
{m_item
->GetProperty("CheckAutoPlayNextItem").asBoolean()
332 ? ContentUtils::PlayMode::CHECK_AUTO_PLAY_NEXT_ITEM
333 : ContentUtils::PlayMode::PLAY_ONLY_THIS
};
334 VIDEO::UTILS::PlayItem(m_item
, player
, mode
);
337 const bool m_choosePlayer
{false};
347 void SetPathAndPlay(const std::shared_ptr
<CFileItem
>& item
, PlayMode mode
)
349 item
->SetProperty("check_resume", false);
351 if (item
->IsLiveTV()) // pvr tv or pvr radio?
353 g_application
.PlayMedia(*item
, "", PLAYLIST::Id::TYPE_VIDEO
);
357 const auto itemCopy
{std::make_shared
<CFileItem
>(*item
)};
358 if (VIDEO::IsVideoDb(*itemCopy
))
360 if (!itemCopy
->m_bIsFolder
)
362 itemCopy
->SetProperty("original_listitem_url", item
->GetPath());
363 itemCopy
->SetPath(item
->GetVideoInfoTag()->m_strFileNameAndPath
);
365 else if (itemCopy
->HasVideoInfoTag() && itemCopy
->GetVideoInfoTag()->IsDefaultVideoVersion())
367 //! @todo get rid of "videos with versions as folder" hack!
368 itemCopy
->m_bIsFolder
= false;
372 if (mode
== PlayMode::PLAY_VERSION_USING
)
374 // force video version selection dialog
375 itemCopy
->SetProperty("needs_resolved_video_asset", true);
379 // play the given/default video version, if multiple versions are available
380 itemCopy
->SetProperty("has_resolved_video_asset", true);
383 const bool choosePlayer
{mode
== PlayMode::PLAY_USING
|| mode
== PlayMode::PLAY_VERSION_USING
};
384 CVideoPlayActionProcessor proc
{itemCopy
, choosePlayer
};
385 if (mode
== PlayMode::RESUME
&& (itemCopy
->GetStartOffset() == STARTOFFSET_RESUME
||
386 VIDEO::UTILS::GetItemResumeInformation(*item
).isResumable
))
387 proc
.ProcessAction(VIDEO::GUILIB::ACTION_RESUME
);
388 else // all other modes are actually PLAY
389 proc
.ProcessAction(VIDEO::GUILIB::ACTION_PLAY_FROM_BEGINNING
);
392 } // unnamed namespace
394 bool CVideoResume::Execute(const std::shared_ptr
<CFileItem
>& itemIn
) const
396 const auto item
{std::make_shared
<CFileItem
>(itemIn
->GetItemToPlay())};
397 #ifdef HAS_OPTICAL_DRIVE
398 if (item
->IsDVD() || MUSIC::IsCDDA(*item
))
399 return MEDIA_DETECT::CAutorun::PlayDisc(item
->GetPath(), true, false);
402 item
->SetStartOffset(STARTOFFSET_RESUME
);
403 SetPathAndPlay(item
, PlayMode::RESUME
);
407 std::string
CVideoPlay::GetLabel(const CFileItem
& itemIn
) const
409 CFileItem
item(itemIn
.GetItemToPlay());
411 return g_localizeStrings
.Get(19000); // Switch to channel
412 if (VIDEO::UTILS::GetItemResumeInformation(item
).isResumable
)
413 return g_localizeStrings
.Get(12021); // Play from beginning
414 return g_localizeStrings
.Get(208); // Play
417 bool CVideoPlay::IsVisible(const CFileItem
& item
) const
419 return VIDEO::UTILS::IsItemPlayable(item
);
422 bool CVideoPlay::Execute(const std::shared_ptr
<CFileItem
>& itemIn
) const
424 const auto item
{std::make_shared
<CFileItem
>(itemIn
->GetItemToPlay())};
425 #ifdef HAS_OPTICAL_DRIVE
426 if (item
->IsDVD() || MUSIC::IsCDDA(*item
))
427 return MEDIA_DETECT::CAutorun::PlayDisc(item
->GetPath(), true, true);
429 SetPathAndPlay(item
, PlayMode::PLAY
);
433 bool CVideoPlayUsing::IsVisible(const CFileItem
& item
) const
435 if (item
.HasVideoVersions() &&
436 !CServiceBroker::GetSettingsComponent()->GetSettings()->GetBool(
437 CSettings::SETTING_VIDEOLIBRARY_SHOWVIDEOVERSIONSASFOLDER
) &&
438 !VIDEO::IsVideoAssetFile(item
))
444 const CPlayerCoreFactory
& playerCoreFactory
{CServiceBroker::GetPlayerCoreFactory()};
445 return (GetPlayers(playerCoreFactory
, item
).size() > 1) && VIDEO::UTILS::IsItemPlayable(item
);
448 bool CVideoPlayUsing::Execute(const std::shared_ptr
<CFileItem
>& itemIn
) const
450 const auto item
{std::make_shared
<CFileItem
>(itemIn
->GetItemToPlay())};
451 SetPathAndPlay(item
, PlayMode::PLAY_USING
);
455 bool CVideoPlayVersionUsing::IsVisible(const CFileItem
& item
) const
457 return item
.HasVideoVersions() &&
458 !CServiceBroker::GetSettingsComponent()->GetSettings()->GetBool(
459 CSettings::SETTING_VIDEOLIBRARY_SHOWVIDEOVERSIONSASFOLDER
) &&
460 !VIDEO::IsVideoAssetFile(item
);
463 bool CVideoPlayVersionUsing::Execute(const std::shared_ptr
<CFileItem
>& itemIn
) const
465 const auto item
{std::make_shared
<CFileItem
>(itemIn
->GetItemToPlay())};
466 item
->SetProperty("video_asset_type", static_cast<int>(VideoAssetType::VERSION
));
467 SetPathAndPlay(item
, PlayMode::PLAY_VERSION_USING
);
473 void SelectNextItem(int windowID
)
475 auto& windowMgr
= CServiceBroker::GetGUI()->GetWindowManager();
476 CGUIWindow
* window
= windowMgr
.GetWindow(windowID
);
479 const int viewContainerID
= window
->GetViewContainerID();
480 if (viewContainerID
> 0)
482 CGUIMessage
msg1(GUI_MSG_ITEM_SELECTED
, windowID
, viewContainerID
);
483 windowMgr
.SendMessage(msg1
, windowID
);
485 CGUIMessage
msg2(GUI_MSG_ITEM_SELECT
, windowID
, viewContainerID
, msg1
.GetParam1() + 1);
486 windowMgr
.SendMessage(msg2
, windowID
);
491 bool CanQueue(const CFileItem
& item
)
493 if (!item
.CanQueue())
496 const int windowId
= CServiceBroker::GetGUI()->GetWindowManager().GetActiveWindow();
497 if (windowId
== WINDOW_VIDEO_PLAYLIST
)
498 return false; // Already queued
502 } // unnamed namespace
504 bool CVideoQueue::IsVisible(const CFileItem
& item
) const
509 return VIDEO::UTILS::IsItemPlayable(item
);
512 bool CVideoQueue::Execute(const std::shared_ptr
<CFileItem
>& item
) const
514 VIDEO::UTILS::QueueItem(item
, VIDEO::UTILS::QueuePosition::POSITION_END
);
516 // Set selection to next item in active window's view.
517 const int windowID
= CServiceBroker::GetGUI()->GetWindowManager().GetActiveWindow();
518 SelectNextItem(windowID
);
523 bool CVideoPlayNext::IsVisible(const CFileItem
& item
) const
528 return VIDEO::UTILS::IsItemPlayable(item
);
531 bool CVideoPlayNext::Execute(const std::shared_ptr
<CFileItem
>& item
) const
533 VIDEO::UTILS::QueueItem(item
, VIDEO::UTILS::QueuePosition::POSITION_BEGIN
);
537 std::string
CVideoPlayAndQueue::GetLabel(const CFileItem
& item
) const
539 if (VIDEO::UTILS::IsAutoPlayNextItem(item
))
540 return g_localizeStrings
.Get(13434); // Play only this
542 return g_localizeStrings
.Get(13412); // Play from here
545 bool CVideoPlayAndQueue::IsVisible(const CFileItem
& item
) const
550 const int windowId
= CServiceBroker::GetGUI()->GetWindowManager().GetActiveWindow();
551 if ((windowId
== WINDOW_TV_RECORDINGS
|| windowId
== WINDOW_RADIO_RECORDINGS
) &&
552 item
.IsUsablePVRRecording())
555 return false; //! @todo implement
558 bool CVideoPlayAndQueue::Execute(const std::shared_ptr
<CFileItem
>& item
) const
560 const int windowId
= CServiceBroker::GetGUI()->GetWindowManager().GetActiveWindow();
561 if ((windowId
== WINDOW_TV_RECORDINGS
|| windowId
== WINDOW_RADIO_RECORDINGS
) &&
562 item
->IsUsablePVRRecording())
564 const ContentUtils::PlayMode mode
= VIDEO::UTILS::IsAutoPlayNextItem(*item
)
565 ? ContentUtils::PlayMode::PLAY_ONLY_THIS
566 : ContentUtils::PlayMode::PLAY_FROM_HERE
;
567 VIDEO::UTILS::PlayItem(item
, "", mode
);
571 return true; //! @todo implement