aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--addons/resource.language.en_gb/resources/strings.po2
-rw-r--r--xbmc/interfaces/builtins/PlayerBuiltins.cpp40
-rw-r--r--xbmc/utils/URIUtils.cpp10
-rw-r--r--xbmc/utils/URIUtils.h2
-rw-r--r--xbmc/video/CMakeLists.txt2
-rw-r--r--xbmc/video/ContextMenus.cpp113
-rw-r--r--xbmc/video/VideoUtils.cpp402
-rw-r--r--xbmc/video/VideoUtils.h55
-rw-r--r--xbmc/video/windows/GUIWindowVideoBase.cpp168
-rw-r--r--xbmc/video/windows/GUIWindowVideoBase.h2
10 files changed, 523 insertions, 273 deletions
diff --git a/addons/resource.language.en_gb/resources/strings.po b/addons/resource.language.en_gb/resources/strings.po
index bd31344ec8..6ed4ecdcd8 100644
--- a/addons/resource.language.en_gb/resources/strings.po
+++ b/addons/resource.language.en_gb/resources/strings.po
@@ -4915,7 +4915,6 @@ msgstr ""
#: addons/skin.estuary/xml/Variables.xml
#: xbmc/music/ContextMenus.h
#: xbmc/video/ContextMenus.cpp
-#: xbmc/video/windows/GUIWindowVideoBase.cpp
msgctxt "#10008"
msgid "Play next"
msgstr ""
@@ -6929,7 +6928,6 @@ msgstr ""
#: system/settings/settings.xml
#: xbmc/music/ContextMenus.h
#: xbmc/video/ContextMenus.cpp
-#: xbmc/video/windows/GUIWindowVideoBase.cpp
msgctxt "#13347"
msgid "Queue item"
msgstr ""
diff --git a/xbmc/interfaces/builtins/PlayerBuiltins.cpp b/xbmc/interfaces/builtins/PlayerBuiltins.cpp
index a7e1380582..ac02169e71 100644
--- a/xbmc/interfaces/builtins/PlayerBuiltins.cpp
+++ b/xbmc/interfaces/builtins/PlayerBuiltins.cpp
@@ -19,9 +19,9 @@
#include "application/ApplicationComponents.h"
#include "application/ApplicationPlayer.h"
#include "application/ApplicationPowerHandling.h"
-#include "filesystem/Directory.h"
#include "guilib/GUIComponent.h"
#include "guilib/GUIWindowManager.h"
+#include "music/MusicUtils.h"
#include "playlists/PlayList.h"
#include "pvr/PVRManager.h"
#include "pvr/channels/PVRChannel.h"
@@ -31,13 +31,12 @@
#include "settings/Settings.h"
#include "settings/SettingsComponent.h"
#include "storage/MediaManager.h"
-#include "utils/FileExtensionProvider.h"
#include "utils/StringUtils.h"
#include "utils/URIUtils.h"
#include "utils/log.h"
#include "video/PlayerController.h"
+#include "video/VideoUtils.h"
#include "video/windows/GUIWindowVideoBase.h"
-#include "view/GUIViewState.h"
#include <math.h>
@@ -423,6 +422,16 @@ static int PlayDVD(const std::vector<std::string>& params)
namespace
{
+void GetItemsForPlayList(const std::shared_ptr<CFileItem>& item, CFileItemList& queuedItems)
+{
+ if (VIDEO_UTILS::IsItemPlayable(*item))
+ VIDEO_UTILS::GetItemsForPlayList(item, queuedItems);
+ else if (MUSIC_UTILS::IsItemPlayable(*item))
+ MUSIC_UTILS::GetItemsForPlayList(item, queuedItems);
+ else
+ CLog::LogF(LOGERROR, "Unable to get playlist items for {}", item->GetPath());
+}
+
int PlayOrQueueMedia(const std::vector<std::string>& params, bool forcePlay)
{
// restore to previous window if needed
@@ -497,16 +506,14 @@ int PlayOrQueueMedia(const std::vector<std::string>& params, bool forcePlay)
if (item.m_bIsFolder || item.IsPlayList())
{
CFileItemList items;
- auto& provider = CServiceBroker::GetFileExtensionProvider();
- const std::string exts = provider.GetVideoExtensions() + "|" + provider.GetMusicExtensions();
- CUtil::GetRecursiveListing(item.GetPath(), items, exts, XFILE::DIR_FLAG_DEFAULTS);
-
+ GetItemsForPlayList(std::make_shared<CFileItem>(item), items);
if (!items.IsEmpty()) // fall through on non expandable playlist
{
- bool containsMusic = false, containsVideo = false;
- for (int i = 0; i < items.Size(); i++)
+ bool containsMusic = false;
+ bool containsVideo = false;
+ for (const auto& i : items)
{
- bool isVideo = items[i]->IsVideo();
+ const bool isVideo = i->IsVideo();
containsMusic |= !isVideo;
containsVideo |= isVideo;
@@ -514,12 +521,6 @@ int PlayOrQueueMedia(const std::vector<std::string>& params, bool forcePlay)
break;
}
- std::unique_ptr<CGUIViewState> state(CGUIViewState::GetViewState(containsVideo ? WINDOW_VIDEO_NAV : WINDOW_MUSIC_NAV, items));
- if (state)
- items.Sort(state->GetSortMethod());
- else
- items.Sort(SortByLabel, SortOrderAscending);
-
PLAYLIST::Id playlistId = containsVideo ? PLAYLIST::TYPE_VIDEO : PLAYLIST::TYPE_MUSIC;
// Mixed playlist item played by music player, mixed content folder has music removed
if (containsMusic && containsVideo)
@@ -567,7 +568,12 @@ int PlayOrQueueMedia(const std::vector<std::string>& params, bool forcePlay)
if (items.Size() && !appPlayer->IsPlaying())
{
playlistPlayer.SetCurrentPlaylist(playlistId);
- playlistPlayer.Play(hasPlayOffset ? playOffset : oldSize, "");
+
+ if (containsMusic)
+ {
+ // video does not auto play on queue like music
+ playlistPlayer.Play(hasPlayOffset ? playOffset : oldSize, "");
+ }
}
}
diff --git a/xbmc/utils/URIUtils.cpp b/xbmc/utils/URIUtils.cpp
index c5353b9f2d..b2b9b23bc0 100644
--- a/xbmc/utils/URIUtils.cpp
+++ b/xbmc/utils/URIUtils.cpp
@@ -1108,6 +1108,16 @@ bool URIUtils::IsPVRRecordingFileOrFolder(const std::string& strFile)
return StringUtils::StartsWith(strFile, "pvr://recordings");
}
+bool URIUtils::IsPVRTVRecordingFileOrFolder(const std::string& strFile)
+{
+ return StringUtils::StartsWith(strFile, "pvr://recordings/tv");
+}
+
+bool URIUtils::IsPVRRadioRecordingFileOrFolder(const std::string& strFile)
+{
+ return StringUtils::StartsWith(strFile, "pvr://recordings/radio");
+}
+
bool URIUtils::IsMusicDb(const std::string& strFile)
{
return IsProtocol(strFile, "musicdb");
diff --git a/xbmc/utils/URIUtils.h b/xbmc/utils/URIUtils.h
index 0594ce0997..85ba81bd81 100644
--- a/xbmc/utils/URIUtils.h
+++ b/xbmc/utils/URIUtils.h
@@ -140,6 +140,8 @@ public:
static bool IsLiveTV(const std::string& strFile);
static bool IsPVRRecording(const std::string& strFile);
static bool IsPVRRecordingFileOrFolder(const std::string& strFile);
+ static bool IsPVRTVRecordingFileOrFolder(const std::string& strFile);
+ static bool IsPVRRadioRecordingFileOrFolder(const std::string& strFile);
static bool IsMultiPath(const std::string& strPath);
static bool IsMusicDb(const std::string& strFile);
static bool IsNfs(const std::string& strFile);
diff --git a/xbmc/video/CMakeLists.txt b/xbmc/video/CMakeLists.txt
index 4cb7944725..00f9bafb54 100644
--- a/xbmc/video/CMakeLists.txt
+++ b/xbmc/video/CMakeLists.txt
@@ -10,6 +10,7 @@ set(SOURCES Bookmark.cpp
VideoInfoTag.cpp
VideoLibraryQueue.cpp
VideoThumbLoader.cpp
+ VideoUtils.cpp
ViewModeSettings.cpp)
set(HEADERS Bookmark.h
@@ -26,6 +27,7 @@ set(HEADERS Bookmark.h
VideoInfoTag.h
VideoLibraryQueue.h
VideoThumbLoader.h
+ VideoUtils.h
ViewModeSettings.h)
core_add_library(video)
diff --git a/xbmc/video/ContextMenus.cpp b/xbmc/video/ContextMenus.cpp
index eaec3064ea..ec400a9541 100644
--- a/xbmc/video/ContextMenus.cpp
+++ b/xbmc/video/ContextMenus.cpp
@@ -9,6 +9,7 @@
#include "ContextMenus.h"
#include "Autorun.h"
+#include "GUIUserMessages.h"
#include "PlayListPlayer.h"
#include "ServiceBroker.h"
#include "application/Application.h"
@@ -20,7 +21,9 @@
#include "guilib/LocalizeStrings.h"
#include "playlists/PlayList.h"
#include "settings/MediaSettings.h"
+#include "utils/StringUtils.h"
#include "utils/URIUtils.h"
+#include "video/VideoUtils.h"
#include "video/dialogs/GUIDialogVideoInfo.h"
#include "video/windows/GUIWindowVideoBase.h"
#include "view/GUIViewState.h"
@@ -180,31 +183,6 @@ void AddRecordingsToPlayListAndSort(const std::shared_ptr<CFileItem>& item,
}
}
-void QueueRecordings(const std::shared_ptr<CFileItem>& item, bool bPlayNext)
-{
- CFileItemList queuedItems;
- AddRecordingsToPlayListAndSort(item, queuedItems);
-
- PLAYLIST::CPlayListPlayer& player = CServiceBroker::GetPlaylistPlayer();
-
- // Determine the proper list to queue this element
- PLAYLIST::Id playlistId = player.GetCurrentPlaylist();
- const auto& components = CServiceBroker::GetAppComponents();
- const auto appPlayer = components.GetComponent<CApplicationPlayer>();
-
- if (playlistId == PLAYLIST::TYPE_NONE)
- playlistId = appPlayer->GetPreferredPlaylist();
- if (playlistId == PLAYLIST::TYPE_NONE)
- playlistId = PLAYLIST::TYPE_VIDEO;
-
- if (bPlayNext && appPlayer && appPlayer->IsPlaying())
- player.Insert(playlistId, queuedItems, player.GetCurrentSong() + 1);
- else
- player.Add(playlistId, queuedItems);
-
- player.SetCurrentPlaylist(playlistId);
-}
-
void PlayAndQueueRecordings(const std::shared_ptr<CFileItem>& item, int windowId)
{
const std::shared_ptr<CFileItem> parentFolderItem =
@@ -243,23 +221,9 @@ void PlayAndQueueRecordings(const std::shared_ptr<CFileItem>& item, int windowId
player.Play(itemToPlay, "");
}
-bool IsActiveRecordingsFolder(const CFileItem& item)
-{
- if (item.m_bIsFolder && !item.IsParentFolder() &&
- URIUtils::IsPVRRecordingFileOrFolder(item.GetPath()))
- {
- // Note: Recordings contained in the folder must be sorted properly, thus this
- // item is only available if one of the recordings windows is active.
- const int windowId = CServiceBroker::GetGUI()->GetWindowManager().GetActiveWindow();
- return windowId == WINDOW_TV_RECORDINGS || windowId == WINDOW_RADIO_RECORDINGS;
- }
-
- return false;
-}
-
void SetPathAndPlay(CFileItem& item)
{
- if (item.IsVideoDb())
+ if (!item.m_bIsFolder && item.IsVideoDb())
{
item.SetProperty("original_listitem_url", item.GetPath());
item.SetPath(item.GetVideoInfoTag()->m_strFileNameAndPath);
@@ -270,25 +234,10 @@ void SetPathAndPlay(CFileItem& item)
{
g_application.PlayMedia(item, "", PLAYLIST::TYPE_VIDEO);
}
- else if (IsActiveRecordingsFolder(item))
- {
- // recursively add items to play list
- CFileItemList queuedItems;
- AddRecordingsToPlayListAndSort(std::make_shared<CFileItem>(item), queuedItems);
-
- PLAYLIST::CPlayListPlayer& player = CServiceBroker::GetPlaylistPlayer();
-
- player.ClearPlaylist(PLAYLIST::TYPE_VIDEO);
- player.Reset();
- player.Add(PLAYLIST::TYPE_VIDEO, queuedItems);
- player.SetCurrentPlaylist(PLAYLIST::TYPE_VIDEO);
-
- player.Play();
- }
else
{
item.SetProperty("playlist_type_hint", PLAYLIST::TYPE_VIDEO);
- CServiceBroker::GetPlaylistPlayer().Play(std::make_shared<CFileItem>(item), "");
+ VIDEO_UTILS::PlayItem(std::make_shared<CFileItem>(item));
}
}
@@ -317,25 +266,9 @@ std::string CVideoPlay::GetLabel(const CFileItem& itemIn) const
return g_localizeStrings.Get(208); // Play
}
-bool CVideoPlay::IsVisible(const CFileItem& itemIn) const
+bool CVideoPlay::IsVisible(const CFileItem& item) const
{
- CFileItem item(itemIn.GetItemToPlay());
- if (item.IsDeleted()) // e.g. trashed pvr recording
- return false;
-
- if (IsActiveRecordingsFolder(item))
- return true;
-
- // Music nav window has own "Play" context menu button, do not show this one. Playlist files
- // like .m3u and .strm return IsVideo() true but from music nav window play with paplayer.
- const int currentWindow = CServiceBroker::GetGUI()->GetWindowManager().GetActiveWindow();
- if (currentWindow == WINDOW_MUSIC_NAV)
- return false;
-
- if (item.m_bIsFolder)
- return false; //! @todo implement
-
- return item.IsVideo() || item.IsLiveTV() || item.IsDVD() || item.IsCDDA();
+ return VIDEO_UTILS::IsItemPlayable(item);
}
bool CVideoPlay::Execute(const std::shared_ptr<CFileItem>& itemIn) const
@@ -354,10 +287,10 @@ bool CVideoQueue::IsVisible(const CFileItem& item) const
if (CServiceBroker::GetGUI()->GetWindowManager().GetActiveWindow() == WINDOW_VIDEO_PLAYLIST)
return false; // Already queued
- if (item.IsUsablePVRRecording() || IsActiveRecordingsFolder(item))
- return true;
+ if (!item.CanQueue())
+ return false;
- return false; //! @todo implement
+ return VIDEO_UTILS::IsItemPlayable(item);
}
bool CVideoQueue::Execute(const std::shared_ptr<CFileItem>& item) const
@@ -365,14 +298,8 @@ bool CVideoQueue::Execute(const std::shared_ptr<CFileItem>& item) const
if (CServiceBroker::GetGUI()->GetWindowManager().GetActiveWindow() == WINDOW_VIDEO_PLAYLIST)
return false; // Already queued
- if (item->IsUsablePVRRecording() || IsActiveRecordingsFolder(*item))
- {
- // recursively add items to play list
- QueueRecordings(item, false);
- return true;
- }
-
- return true; //! @todo implement
+ VIDEO_UTILS::QueueItem(item, VIDEO_UTILS::QueuePosition::POSITION_END);
+ return true;
};
bool CVideoPlayNext::IsVisible(const CFileItem& item) const
@@ -380,10 +307,10 @@ bool CVideoPlayNext::IsVisible(const CFileItem& item) const
if (CServiceBroker::GetGUI()->GetWindowManager().GetActiveWindow() == WINDOW_VIDEO_PLAYLIST)
return false; // Already queued
- if (item.IsUsablePVRRecording() || IsActiveRecordingsFolder(item))
- return true;
+ if (!item.CanQueue())
+ return false;
- return false; //! @todo implement
+ return VIDEO_UTILS::IsItemPlayable(item);
}
bool CVideoPlayNext::Execute(const std::shared_ptr<CFileItem>& item) const
@@ -391,14 +318,8 @@ bool CVideoPlayNext::Execute(const std::shared_ptr<CFileItem>& item) const
if (CServiceBroker::GetGUI()->GetWindowManager().GetActiveWindow() == WINDOW_VIDEO_PLAYLIST)
return false; // Already queued
- if (item->IsUsablePVRRecording() || IsActiveRecordingsFolder(*item))
- {
- // recursively add items to play list
- QueueRecordings(item, true);
- return true;
- }
-
- return true; //! @todo implement
+ VIDEO_UTILS::QueueItem(item, VIDEO_UTILS::QueuePosition::POSITION_BEGIN);
+ return true;
};
bool CVideoPlayAndQueue::IsVisible(const CFileItem& item) const
diff --git a/xbmc/video/VideoUtils.cpp b/xbmc/video/VideoUtils.cpp
new file mode 100644
index 0000000000..f35c59d818
--- /dev/null
+++ b/xbmc/video/VideoUtils.cpp
@@ -0,0 +1,402 @@
+/*
+ * Copyright (C) 2022 Team Kodi
+ * This file is part of Kodi - https://kodi.tv
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ * See LICENSES/README.md for more information.
+ */
+
+#include "VideoUtils.h"
+
+#include "FileItem.h"
+#include "GUIPassword.h"
+#include "PartyModeManager.h"
+#include "PlayListPlayer.h"
+#include "ServiceBroker.h"
+#include "URL.h"
+#include "Util.h"
+#include "application/ApplicationComponents.h"
+#include "application/ApplicationPlayer.h"
+#include "dialogs/GUIDialogBusy.h"
+#include "filesystem/Directory.h"
+#include "filesystem/VideoDatabaseDirectory.h"
+#include "guilib/GUIComponent.h"
+#include "guilib/GUIWindowManager.h"
+#include "playlists/PlayList.h"
+#include "playlists/PlayListFactory.h"
+#include "settings/MediaSettings.h"
+#include "settings/Settings.h"
+#include "settings/SettingsComponent.h"
+#include "threads/IRunnable.h"
+#include "utils/StringUtils.h"
+#include "utils/URIUtils.h"
+#include "utils/log.h"
+#include "video/VideoInfoTag.h"
+#include "view/GUIViewState.h"
+
+namespace
+{
+class CAsyncGetItemsForPlaylist : public IRunnable
+{
+public:
+ CAsyncGetItemsForPlaylist(const std::shared_ptr<CFileItem>& item, CFileItemList& queuedItems)
+ : m_item(item),
+ m_resume(item->GetStartOffset() == STARTOFFSET_RESUME),
+ m_queuedItems(queuedItems)
+ {
+ }
+
+ ~CAsyncGetItemsForPlaylist() override = default;
+
+ void Run() override
+ {
+ // fast lookup is needed here
+ m_queuedItems.SetFastLookup(true);
+
+ GetItemsForPlaylist(m_item);
+ }
+
+private:
+ void GetItemsForPlaylist(const std::shared_ptr<CFileItem>& item);
+
+ const std::shared_ptr<CFileItem> m_item;
+ const bool m_resume{false};
+ CFileItemList& m_queuedItems;
+};
+
+void CAsyncGetItemsForPlaylist::GetItemsForPlaylist(const std::shared_ptr<CFileItem>& item)
+{
+ if (item->IsParentFolder() || !item->CanQueue() || item->IsRAR() || item->IsZIP())
+ return;
+
+ if (item->m_bIsFolder)
+ {
+ // check if it's a folder with dvd or bluray files, then just add the relevant file
+ const std::string mediapath = item->GetOpticalMediaPath();
+ if (!mediapath.empty())
+ {
+ m_queuedItems.Add(std::make_shared<CFileItem>(mediapath, false));
+ return;
+ }
+
+ // Check if we add a locked share
+ if (!item->IsPVR() && item->m_bIsShareOrDrive)
+ {
+ if (!g_passwordManager.IsItemUnlocked(item.get(), "video"))
+ return;
+ }
+
+ CFileItemList items;
+ XFILE::CDirectory::GetDirectory(item->GetPath(), items, "", XFILE::DIR_FLAG_DEFAULTS);
+
+ int viewStateWindowId = WINDOW_VIDEO_NAV;
+ if (URIUtils::IsPVRRadioRecordingFileOrFolder(item->GetPath()))
+ viewStateWindowId = WINDOW_RADIO_RECORDINGS;
+ else if (URIUtils::IsPVRTVRecordingFileOrFolder(item->GetPath()))
+ viewStateWindowId = WINDOW_TV_RECORDINGS;
+
+ const std::unique_ptr<CGUIViewState> state(
+ CGUIViewState::GetViewState(viewStateWindowId, items));
+ if (state)
+ {
+ LABEL_MASKS labelMasks;
+ state->GetSortMethodLabelMasks(labelMasks);
+
+ const CLabelFormatter fileFormatter(labelMasks.m_strLabelFile, labelMasks.m_strLabel2File);
+ const CLabelFormatter folderFormatter(labelMasks.m_strLabelFolder,
+ labelMasks.m_strLabel2Folder);
+ for (const auto& i : items)
+ {
+ if (i->IsLabelPreformatted())
+ continue;
+
+ if (i->m_bIsFolder)
+ folderFormatter.FormatLabels(i.get());
+ else
+ fileFormatter.FormatLabels(i.get());
+ }
+
+ if (items.GetSortMethod() == SortByLabel)
+ items.ClearSortState();
+
+ items.Sort(state->GetSortMethod());
+ }
+
+ if (m_resume)
+ {
+ // put last played item at the begin of the playlist; add start offsets for videos
+ std::shared_ptr<CFileItem> lastPlayedItem;
+ CDateTime lastPlayed;
+ for (const auto& i : items)
+ {
+ if (!i->HasVideoInfoTag())
+ continue;
+
+ const auto videoTag = i->GetVideoInfoTag();
+
+ const CBookmark& bookmark = videoTag->GetResumePoint();
+ if (bookmark.IsSet())
+ i->SetStartOffset(CUtil::ConvertSecsToMilliSecs(bookmark.timeInSeconds));
+
+ const CDateTime& currLastPlayed = videoTag->m_lastPlayed;
+ if (currLastPlayed.IsValid() && (!lastPlayed.IsValid() || (lastPlayed < currLastPlayed)))
+ {
+ lastPlayedItem = i;
+ lastPlayed = currLastPlayed;
+ }
+ }
+
+ if (lastPlayedItem)
+ {
+ items.Remove(lastPlayedItem.get());
+ items.AddFront(lastPlayedItem, 0);
+ }
+ }
+
+ int watchedMode;
+ if (m_resume)
+ watchedMode = WatchedModeUnwatched;
+ else
+ watchedMode = CMediaSettings::GetInstance().GetWatchedMode(items.GetContent());
+
+ const bool unwatchedOnly = watchedMode == WatchedModeUnwatched;
+ const bool watchedOnly = watchedMode == WatchedModeWatched;
+ for (const auto& i : items)
+ {
+ if (i->m_bIsFolder)
+ {
+ std::string path = i->GetPath();
+ URIUtils::RemoveSlashAtEnd(path);
+ if (StringUtils::EndsWithNoCase(path, "sample")) // skip sample folders
+ continue;
+ }
+ else if (i->HasVideoInfoTag() &&
+ ((unwatchedOnly && i->GetVideoInfoTag()->GetPlayCount() > 0) ||
+ (watchedOnly && i->GetVideoInfoTag()->GetPlayCount() <= 0)))
+ continue;
+
+ GetItemsForPlaylist(i);
+ }
+ }
+ else if (item->IsPlayList())
+ {
+ const std::unique_ptr<PLAYLIST::CPlayList> playList(PLAYLIST::CPlayListFactory::Create(*item));
+ if (!playList)
+ {
+ CLog::Log(LOGERROR, "{} failed to create playlist {}", __FUNCTION__, item->GetPath());
+ return;
+ }
+
+ if (!playList->Load(item->GetPath()))
+ {
+ CLog::Log(LOGERROR, "{} failed to load playlist {}", __FUNCTION__, item->GetPath());
+ return;
+ }
+
+ for (int i = 0; i < playList->size(); ++i)
+ {
+ GetItemsForPlaylist((*playList)[i]);
+ }
+ }
+ else if (item->IsInternetStream())
+ {
+ // just queue the internet stream, it will be expanded on play
+ m_queuedItems.Add(item);
+ }
+ else if (item->IsPlugin() && item->GetProperty("isplayable").asBoolean())
+ {
+ // a playable python files
+ m_queuedItems.Add(item);
+ }
+ else if (item->IsVideoDb())
+ {
+ // this case is needed unless we allow IsVideo() to return true for videodb items,
+ // but then we have issues with playlists of videodb items
+ const auto itemCopy = std::make_shared<CFileItem>(*item->GetVideoInfoTag());
+ itemCopy->SetStartOffset(item->GetStartOffset());
+ m_queuedItems.Add(itemCopy);
+ }
+ else if (!item->IsNFO() && item->IsVideo())
+ {
+ m_queuedItems.Add(item);
+ }
+}
+
+} // unnamed namespace
+
+namespace VIDEO_UTILS
+{
+void PlayItem(const std::shared_ptr<CFileItem>& itemIn)
+{
+ auto item = itemIn;
+
+ // Allow queuing of unqueueable items
+ // when we try to queue them directly
+ if (!itemIn->CanQueue())
+ {
+ // make a copy to not alter the original item
+ item = std::make_shared<CFileItem>(*itemIn);
+ item->SetCanQueue(true);
+ }
+
+ if (item->m_bIsFolder && !item->IsPlugin())
+ {
+ // recursively add items to list
+ CFileItemList queuedItems;
+ GetItemsForPlayList(item, queuedItems);
+
+ auto& player = CServiceBroker::GetPlaylistPlayer();
+ player.ClearPlaylist(PLAYLIST::TYPE_VIDEO);
+ player.Reset();
+ player.Add(PLAYLIST::TYPE_VIDEO, queuedItems);
+ player.SetCurrentPlaylist(PLAYLIST::TYPE_VIDEO);
+ player.Play();
+ }
+ else if (item->HasVideoInfoTag())
+ {
+ // single item, play it
+ CServiceBroker::GetPlaylistPlayer().Play(item, "");
+ }
+}
+
+void QueueItem(const std::shared_ptr<CFileItem>& itemIn, QueuePosition pos)
+{
+ auto item = itemIn;
+
+ // Allow queuing of unqueueable items
+ // when we try to queue them directly
+ if (!itemIn->CanQueue())
+ {
+ // make a copy to not alter the original item
+ item = std::make_shared<CFileItem>(*itemIn);
+ item->SetCanQueue(true);
+ }
+
+ auto& player = CServiceBroker::GetPlaylistPlayer();
+ const auto& components = CServiceBroker::GetAppComponents();
+
+ // Determine the proper list to queue this element
+ PLAYLIST::Id playlistId = player.GetCurrentPlaylist();
+ if (playlistId == PLAYLIST::TYPE_NONE)
+ playlistId = components.GetComponent<CApplicationPlayer>()->GetPreferredPlaylist();
+
+ if (playlistId == PLAYLIST::TYPE_NONE)
+ playlistId = PLAYLIST::TYPE_VIDEO;
+
+ CFileItemList queuedItems;
+ GetItemsForPlayList(item, queuedItems);
+
+ // if party mode, add items but DONT start playing
+ if (g_partyModeManager.IsEnabled(PARTYMODECONTEXT_VIDEO))
+ {
+ g_partyModeManager.AddUserSongs(queuedItems, false);
+ return;
+ }
+
+ if (pos == QueuePosition::POSITION_BEGIN &&
+ components.GetComponent<CApplicationPlayer>()->IsPlaying())
+ player.Insert(playlistId, queuedItems, player.GetCurrentSong() + 1);
+ else
+ player.Add(playlistId, queuedItems);
+
+ player.SetCurrentPlaylist(playlistId);
+
+ // Note: video does not auto play on queue like music
+}
+
+bool GetItemsForPlayList(const std::shared_ptr<CFileItem>& item, CFileItemList& queuedItems)
+{
+ CAsyncGetItemsForPlaylist getItems(item, queuedItems);
+ return CGUIDialogBusy::Wait(&getItems,
+ 500, // 500ms before busy dialog appears
+ true); // can be cancelled
+}
+
+bool IsItemPlayable(const CFileItem& item)
+{
+ if (item.IsParentFolder())
+ return false;
+
+ if (item.IsDeleted())
+ return false;
+
+ // Include all PVR recordings and recordings folders
+ if (URIUtils::IsPVRRecordingFileOrFolder(item.GetPath()))
+ return true;
+
+ // Include Live TV
+ if (!item.m_bIsFolder && (item.IsLiveTV() || item.IsEPG()))
+ return true;
+
+ // Exclude all music library items
+ if (item.IsMusicDb() || StringUtils::StartsWithNoCase(item.GetPath(), "library://music/"))
+ return false;
+
+ // Exclude other components
+ if (item.IsPlugin() || item.IsScript() || item.IsAddonsPath())
+ return false;
+
+ // Exclude unwanted windows
+ if (CServiceBroker::GetGUI()->GetWindowManager().GetActiveWindow() == WINDOW_VIDEO_PLAYLIST)
+ return false;
+
+ // Exclude special items
+ if (StringUtils::StartsWithNoCase(item.GetPath(), "newsmartplaylist://") ||
+ StringUtils::StartsWithNoCase(item.GetPath(), "newplaylist://") ||
+ StringUtils::StartsWithNoCase(item.GetPath(), "newtag://"))
+ return false;
+
+ // Include playlists located at one of the possible video/mixed playlist locations
+ if (item.IsPlayList())
+ {
+ if (StringUtils::StartsWithNoCase(item.GetPath(), "special://videoplaylists/") ||
+ StringUtils::StartsWithNoCase(item.GetPath(), "special://profile/playlists/video/") ||
+ StringUtils::StartsWithNoCase(item.GetPath(), "special://profile/playlists/mixed/"))
+ return true;
+
+ // Has user changed default playlists location and the list is located there?
+ const auto settings = CServiceBroker::GetSettingsComponent()->GetSettings();
+ std::string path = settings->GetString(CSettings::SETTING_SYSTEM_PLAYLISTSPATH);
+ StringUtils::TrimRight(path, "/");
+ if (StringUtils::StartsWith(item.GetPath(), StringUtils::Format("{}/video/", path)) ||
+ StringUtils::StartsWith(item.GetPath(), StringUtils::Format("{}/mixed/", path)))
+ return true;
+
+ // Unknown location. Type cannot be determined.
+ return false;
+ }
+
+ if (item.m_bIsFolder &&
+ (item.IsVideoDb() || StringUtils::StartsWithNoCase(item.GetPath(), "library://video/")))
+ {
+ // Exclude top level nodes - eg can't play 'genres' just a specific genre etc
+ const XFILE::VIDEODATABASEDIRECTORY::NODE_TYPE node =
+ XFILE::CVideoDatabaseDirectory::GetDirectoryParentType(item.GetPath());
+ if (node == XFILE::VIDEODATABASEDIRECTORY::NODE_TYPE_OVERVIEW ||
+ node == XFILE::VIDEODATABASEDIRECTORY::NODE_TYPE_MOVIES_OVERVIEW ||
+ node == XFILE::VIDEODATABASEDIRECTORY::NODE_TYPE_TVSHOWS_OVERVIEW ||
+ node == XFILE::VIDEODATABASEDIRECTORY::NODE_TYPE_MUSICVIDEOS_OVERVIEW)
+ return false;
+
+ return true;
+ }
+
+ if (item.HasVideoInfoTag() && item.CanQueue())
+ {
+ return true;
+ }
+ else if ((!item.m_bIsFolder && item.IsVideo()) || item.IsDVD() || item.IsCDDA())
+ {
+ return true;
+ }
+ else if (!item.m_bIsShareOrDrive && item.m_bIsFolder)
+ {
+ // Not a video-specific folder (like file:// or nfs://). Allow play if context is Video window.
+ if (CServiceBroker::GetGUI()->GetWindowManager().GetActiveWindow() == WINDOW_VIDEO_NAV)
+ return true;
+ }
+
+ return false;
+}
+
+} // namespace VIDEO_UTILS
diff --git a/xbmc/video/VideoUtils.h b/xbmc/video/VideoUtils.h
new file mode 100644
index 0000000000..0694f7d6e5
--- /dev/null
+++ b/xbmc/video/VideoUtils.h
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2022 Team Kodi
+ * This file is part of Kodi - https://kodi.tv
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ * See LICENSES/README.md for more information.
+ */
+
+#pragma once
+
+#include <memory>
+
+class CFileItem;
+class CFileItemList;
+
+namespace VIDEO_UTILS
+{
+/*! \brief Start playback of the given item. If the item is a folder, build a playlist with
+ all items contained in the folder and start playback of the playlist. If item is a single video
+ item, start playback directly, without adding it to the video playlist first.
+ \param item [in] the item to play
+ */
+void PlayItem(const std::shared_ptr<CFileItem>& item);
+
+enum class QueuePosition
+{
+ POSITION_BEGIN, // place at begin of queue, before other items
+ POSITION_END, // place at end of queue, after other items
+};
+
+/*! \brief Queue the given item in the currently active playlist. If no playlist is active,
+ put the item into the video playlist.
+ \param item [in] the item to queue
+ \param pos [in] whether to place the item and the begin or the end of the queue
+ */
+void QueueItem(const std::shared_ptr<CFileItem>& item, QueuePosition pos);
+
+/*! \brief For a given item, get the items to put in a playlist. If the item is a folder, all
++ subitems will be added recursively to the returned item list. If the item is a playlist, the
++ playlist will be loaded and contained items will be added to the returned item list. Shows a
++ busy dialog if action takes certain amount of time to give the user visual feedback.
++ \param item [in] the item to add to the playlist
++ \param queuedItems [out] the items that can be put in a play list
++ \return true on success, false otherwise
++ */
+bool GetItemsForPlayList(const std::shared_ptr<CFileItem>& item, CFileItemList& queuedItems);
+
+/*!
+ \brief Check whether the given item can be played by the app playlist player as one or more videos.
+ \param item The item to check
+ \return True if playable, false otherwise.
+ */
+bool IsItemPlayable(const CFileItem& item);
+
+} // namespace VIDEO_UTILS
diff --git a/xbmc/video/windows/GUIWindowVideoBase.cpp b/xbmc/video/windows/GUIWindowVideoBase.cpp
index ac9ba1bffd..086da0336f 100644
--- a/xbmc/video/windows/GUIWindowVideoBase.cpp
+++ b/xbmc/video/windows/GUIWindowVideoBase.cpp
@@ -40,11 +40,11 @@
#include "playlists/PlayListFactory.h"
#include "profiles/ProfileManager.h"
#include "settings/AdvancedSettings.h"
-#include "settings/MediaSettings.h"
#include "settings/SettingUtils.h"
#include "settings/Settings.h"
#include "settings/SettingsComponent.h"
#include "settings/dialogs/GUIDialogContentSettings.h"
+#include "settings/lib/Setting.h"
#include "storage/MediaManager.h"
#include "utils/FileExtensionProvider.h"
#include "utils/FileUtils.h"
@@ -55,6 +55,7 @@
#include "utils/log.h"
#include "video/VideoInfoScanner.h"
#include "video/VideoLibraryQueue.h"
+#include "video/VideoUtils.h"
#include "video/dialogs/GUIDialogVideoInfo.h"
#include "view/GUIViewState.h"
@@ -441,143 +442,24 @@ bool CGUIWindowVideoBase::ShowIMDB(CFileItemPtr item, const ScraperPtr &info2, b
void CGUIWindowVideoBase::OnQueueItem(int iItem, bool first)
{
- const auto& components = CServiceBroker::GetAppComponents();
- const auto appPlayer = components.GetComponent<CApplicationPlayer>();
-
- // Determine the proper list to queue this element
- PLAYLIST::Id playlistId = CServiceBroker::GetPlaylistPlayer().GetCurrentPlaylist();
- if (playlistId == PLAYLIST::TYPE_NONE)
- playlistId = appPlayer->GetPreferredPlaylist();
- if (playlistId == PLAYLIST::TYPE_NONE)
- playlistId = PLAYLIST::TYPE_VIDEO;
-
// don't re-queue items from playlist window
- if ( iItem < 0 || iItem >= m_vecItems->Size() || GetID() == WINDOW_VIDEO_PLAYLIST ) return ;
-
- // we take a copy so that we can alter the queue state
- CFileItemPtr item(new CFileItem(*m_vecItems->Get(iItem)));
- if (item->IsRAR() || item->IsZIP())
+ if (GetID() == WINDOW_VIDEO_PLAYLIST)
return;
- // Allow queuing of unqueueable items
- // when we try to queue them directly
- if (!item->CanQueue())
- item->SetCanQueue(true);
-
- CFileItemList queuedItems;
- AddItemToPlayList(item, queuedItems);
- // if party mode, add items but DONT start playing
- if (g_partyModeManager.IsEnabled(PARTYMODECONTEXT_VIDEO))
- {
- g_partyModeManager.AddUserSongs(queuedItems, false);
+ if (iItem < 0 || iItem >= m_vecItems->Size())
return;
- }
- if (first && appPlayer->IsPlaying())
- {
- CServiceBroker::GetPlaylistPlayer().Insert(
- playlistId, queuedItems, CServiceBroker::GetPlaylistPlayer().GetCurrentSong() + 1);
- }
- else
- CServiceBroker::GetPlaylistPlayer().Add(playlistId, queuedItems);
- CServiceBroker::GetPlaylistPlayer().SetCurrentPlaylist(playlistId);
- // video does not auto play on queue like music
- m_viewControl.SetSelectedItem(iItem + 1);
-}
+ // add item 2 playlist
+ const auto item = m_vecItems->Get(iItem);
-void CGUIWindowVideoBase::AddItemToPlayList(const CFileItemPtr &pItem, CFileItemList &queuedItems)
-{
- if (!pItem->CanQueue() || pItem->IsRAR() || pItem->IsZIP() || pItem->IsParentFolder()) // no zip/rar enqueues thank you!
+ if (item->IsRAR() || item->IsZIP())
return;
- if (pItem->m_bIsFolder)
- {
- // check if it's a folder with dvd or bluray files, then just add the relevant file
- std::string mediapath(pItem->GetOpticalMediaPath());
- if (!mediapath.empty())
- {
- CFileItemPtr item(new CFileItem(mediapath, false));
- queuedItems.Add(item);
- return;
- }
-
- // Check if we add a locked share
- if ( pItem->m_bIsShareOrDrive )
- {
- CFileItem item = *pItem;
- if ( !g_passwordManager.IsItemUnlocked( &item, "video" ) )
- return;
- }
-
- // recursive
- CFileItemList items;
- GetDirectory(pItem->GetPath(), items);
- FormatAndSort(items);
-
- int watchedMode = CMediaSettings::GetInstance().GetWatchedMode(items.GetContent());
- bool unwatchedOnly = watchedMode == WatchedModeUnwatched;
- bool watchedOnly = watchedMode == WatchedModeWatched;
- for (int i = 0; i < items.Size(); ++i)
- {
- if (items[i]->m_bIsFolder)
- {
- std::string strPath = items[i]->GetPath();
- URIUtils::RemoveSlashAtEnd(strPath);
- if (StringUtils::EndsWithNoCase(strPath, "sample")) // skip sample folders
- {
- continue;
- }
- }
- else if (items[i]->HasVideoInfoTag() &&
- ((unwatchedOnly && items[i]->GetVideoInfoTag()->GetPlayCount() > 0) ||
- (watchedOnly && items[i]->GetVideoInfoTag()->GetPlayCount() <= 0)))
- continue;
-
- AddItemToPlayList(items[i], queuedItems);
- }
- }
- else
- {
- // just an item
- if (pItem->IsPlayList())
- {
- std::unique_ptr<PLAYLIST::CPlayList> pPlayList(PLAYLIST::CPlayListFactory::Create(*pItem));
- if (pPlayList)
- {
- // load it
- if (!pPlayList->Load(pItem->GetPath()))
- {
- HELPERS::ShowOKDialogText(CVariant{6}, CVariant{477});
- return; //hmmm unable to load playlist?
- }
+ VIDEO_UTILS::QueueItem(item, first ? VIDEO_UTILS::QueuePosition::POSITION_BEGIN
+ : VIDEO_UTILS::QueuePosition::POSITION_END);
- PLAYLIST::CPlayList playlist = *pPlayList;
- for (int i = 0; i < playlist.size(); ++i)
- {
- AddItemToPlayList(playlist[i], queuedItems);
- }
- return;
- }
- }
- else if(pItem->IsInternetStream())
- { // just queue the internet stream, it will be expanded on play
- queuedItems.Add(pItem);
- }
- else if (pItem->IsPlugin() && pItem->GetProperty("isplayable").asBoolean())
- { // a playable python files
- queuedItems.Add(pItem);
- }
- else if (pItem->IsVideoDb())
- { // this case is needed unless we allow IsVideo() to return true for videodb items,
- // but then we have issues with playlists of videodb items
- CFileItemPtr item(new CFileItem(*pItem->GetVideoInfoTag()));
- queuedItems.Add(item);
- }
- else if (!pItem->IsNFO() && pItem->IsVideo())
- {
- queuedItems.Add(pItem);
- }
- }
+ // select next item
+ m_viewControl.SetSelectedItem(iItem + 1);
}
void CGUIWindowVideoBase::GetResumeItemOffset(const CFileItem *item, int64_t& startoffset, int& partNumber)
@@ -1035,20 +917,6 @@ void CGUIWindowVideoBase::GetContextButtons(int itemNumber, CContextButtons &but
if (m_database.GetStackTimes(path,times) || CFileItem(CStackDirectory::GetFirstStackedFile(path),false).IsDiscImage())
buttons.Add(CONTEXT_BUTTON_PLAY_PART, 20324);
}
-
- // allow a folder to be ad-hoc queued and played by the default player
- if (item->m_bIsFolder || (item->IsPlayList() &&
- !CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->m_playlistAsFolders))
- {
- buttons.Add(CONTEXT_BUTTON_PLAY_ITEM, 208);
- }
-
- if (!m_vecItems->GetPath().empty() && !StringUtils::StartsWithNoCase(item->GetPath(), "newsmartplaylist://") && !StringUtils::StartsWithNoCase(item->GetPath(), "newtag://")
- && !m_vecItems->IsSourcesPath())
- {
- buttons.Add(CONTEXT_BUTTON_QUEUE_ITEM, 13347); // Add to Playlist
- buttons.Add(CONTEXT_BUTTON_PLAY_NEXT, 10008); // Play next
- }
}
if (!item->m_bIsFolder && !(item->IsPlayList() && !CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->m_playlistAsFolders))
@@ -1206,18 +1074,6 @@ bool CGUIWindowVideoBase::OnContextButton(int itemNumber, CONTEXT_BUTTON button)
else
return false;
}
- case CONTEXT_BUTTON_QUEUE_ITEM:
- OnQueueItem(itemNumber);
- return true;
-
- case CONTEXT_BUTTON_PLAY_NEXT:
- OnQueueItem(itemNumber, true);
- return true;
-
- case CONTEXT_BUTTON_PLAY_ITEM:
- PlayItem(itemNumber);
- return true;
-
case CONTEXT_BUTTON_PLAY_WITH:
{
const CPlayerCoreFactory &playerCoreFactory = CServiceBroker::GetPlayerCoreFactory();
@@ -1454,7 +1310,7 @@ void CGUIWindowVideoBase::PlayItem(int iItem, const std::string &player)
// recursively add items to list
CFileItemList queuedItems;
- AddItemToPlayList(item, queuedItems);
+ VIDEO_UTILS::GetItemsForPlayList(item, queuedItems);
CServiceBroker::GetPlaylistPlayer().ClearPlaylist(PLAYLIST::TYPE_VIDEO);
CServiceBroker::GetPlaylistPlayer().Reset();
diff --git a/xbmc/video/windows/GUIWindowVideoBase.h b/xbmc/video/windows/GUIWindowVideoBase.h
index 1108568aaa..99ac54187c 100644
--- a/xbmc/video/windows/GUIWindowVideoBase.h
+++ b/xbmc/video/windows/GUIWindowVideoBase.h
@@ -115,8 +115,6 @@ protected:
bool ShowIMDB(CFileItemPtr item, const ADDON::ScraperPtr& content, bool fromDB);
- void AddItemToPlayList(const CFileItemPtr &pItem, CFileItemList &queuedItems);
-
void OnSearch();
void OnSearchItemFound(const CFileItem* pSelItem);
int GetScraperForItem(CFileItem *item, ADDON::ScraperPtr &info, VIDEO::SScanSettings& settings);