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"
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"
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())
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
);
64 bool CVideoInfo::IsVisible(const CFileItem
& item
) const
66 if (CVideoInfoBase::IsVisible(item
))
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
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
);
95 bool CVideoMarkWatched::IsVisible(const CFileItem
& item
) const
97 if (item
.IsDeleted()) // e.g. trashed pvr recording
100 if (item
.m_bIsFolder
&& item
.IsPlugin()) // we cannot manage plugin folder's watched state
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
))
116 else if (StringUtils::StartsWithNoCase(item
.GetPath(), "library://video/"))
118 else if (item
.GetProperty("IsVideoFolder").asBoolean())
121 return !item
.IsParentFolder() && URIUtils::IsPVRRecordingFileOrFolder(item
.GetPath());
123 else if (!item
.HasVideoInfoTag())
126 return item
.GetVideoInfoTag()->GetPlayCount() == 0;
129 bool CVideoMarkWatched::Execute(const std::shared_ptr
<CFileItem
>& item
) const
131 CVideoLibraryQueue::GetInstance().MarkAsWatched(item
, true);
135 bool CVideoMarkUnWatched::IsVisible(const CFileItem
& item
) const
137 if (item
.IsDeleted()) // e.g. trashed pvr recording
140 if (item
.m_bIsFolder
&& item
.IsPlugin()) // we cannot manage plugin folder's watched state
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
))
155 else if (StringUtils::StartsWithNoCase(item
.GetPath(), "library://video/"))
157 else if (item
.GetProperty("IsVideoFolder").asBoolean())
160 return !item
.IsParentFolder() && URIUtils::IsPVRRecordingFileOrFolder(item
.GetPath());
162 else if (!item
.HasVideoInfoTag())
165 return item
.GetVideoInfoTag()->GetPlayCount() > 0;
168 bool CVideoMarkUnWatched::Execute(const std::shared_ptr
<CFileItem
>& item
) const
170 CVideoLibraryQueue::GetInstance().MarkAsWatched(item
, false);
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
;
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()
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
);
204 windowMgr
.ActivateWindow(target
, {path
, "return"});
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");
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
240 return VIDEO::UTILS::GetItemResumeInformation(item
).isResumable
;
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
);
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);
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);
313 item
->SetStartOffset(STARTOFFSET_RESUME
);
314 SetPathAndPlay(item
, PlayMode::RESUME
);
318 std::string
CVideoPlay::GetLabel(const CFileItem
& itemIn
) const
320 CFileItem
item(itemIn
.GetItemToPlay());
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);
340 SetPathAndPlay(item
, PlayMode::PLAY
);
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
))
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
);
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
);
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
);
395 void SelectNextItem(int windowID
)
397 auto& windowMgr
= CServiceBroker::GetGUI()->GetWindowManager();
398 CGUIWindow
* window
= windowMgr
.GetWindow(windowID
);
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())
418 const int windowId
= CServiceBroker::GetGUI()->GetWindowManager().GetActiveWindow();
419 if (windowId
== WINDOW_VIDEO_PLAYLIST
)
420 return false; // Already queued
424 } // unnamed namespace
426 bool CVideoQueue::IsVisible(const CFileItem
& item
) const
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
);
445 bool CVideoPlayNext::IsVisible(const CFileItem
& item
) const
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
);
459 std::string
CVideoPlayAndQueue::GetLabel(const CFileItem
& item
) const
461 if (VIDEO::UTILS::IsAutoPlayNextItem(item
))
462 return g_localizeStrings
.Get(13434); // Play only this
464 return g_localizeStrings
.Get(13412); // Play from here
467 bool CVideoPlayAndQueue::IsVisible(const CFileItem
& item
) const
472 const int windowId
= CServiceBroker::GetGUI()->GetWindowManager().GetActiveWindow();
473 if ((windowId
== WINDOW_TV_RECORDINGS
|| windowId
== WINDOW_RADIO_RECORDINGS
) &&
474 item
.IsUsablePVRRecording())
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
);
493 return true; //! @todo implement