[Windows] Remove redundant DirectSound error codes
[xbmc.git] / xbmc / video / guilib / VideoGUIUtils.cpp
blobe12f072016a76ceb76a4a2a60dc9759bbf727727
1 /*
2 * Copyright (C) 2022 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 "VideoGUIUtils.h"
11 #include "FileItem.h"
12 #include "FileItemList.h"
13 #include "GUIPassword.h"
14 #include "PartyModeManager.h"
15 #include "PlayListPlayer.h"
16 #include "ServiceBroker.h"
17 #include "Util.h"
18 #include "application/ApplicationComponents.h"
19 #include "application/ApplicationPlayer.h"
20 #include "dialogs/GUIDialogBusy.h"
21 #include "filesystem/Directory.h"
22 #include "filesystem/VideoDatabaseDirectory.h"
23 #include "guilib/GUIComponent.h"
24 #include "guilib/GUIWindowManager.h"
25 #include "guilib/LocalizeStrings.h"
26 #include "music/MusicFileItemClassify.h"
27 #include "network/NetworkFileItemClassify.h"
28 #include "playlists/PlayList.h"
29 #include "playlists/PlayListFactory.h"
30 #include "playlists/PlayListFileItemClassify.h"
31 #include "profiles/ProfileManager.h"
32 #include "settings/MediaSettings.h"
33 #include "settings/SettingUtils.h"
34 #include "settings/Settings.h"
35 #include "settings/SettingsComponent.h"
36 #include "threads/IRunnable.h"
37 #include "utils/FileUtils.h"
38 #include "utils/StringUtils.h"
39 #include "utils/URIUtils.h"
40 #include "utils/log.h"
41 #include "video/VideoDatabase.h"
42 #include "video/VideoFileItemClassify.h"
43 #include "video/VideoInfoTag.h"
44 #include "video/VideoUtils.h"
45 #include "view/GUIViewState.h"
47 namespace KODI
50 namespace
52 class CAsyncGetItemsForPlaylist : public IRunnable
54 public:
55 CAsyncGetItemsForPlaylist(const std::shared_ptr<CFileItem>& item, CFileItemList& queuedItems)
56 : m_item(item),
57 m_resume((item->GetStartOffset() == STARTOFFSET_RESUME) &&
58 VIDEO::UTILS::GetItemResumeInformation(*item).isResumable),
59 m_queuedItems(queuedItems)
63 ~CAsyncGetItemsForPlaylist() override = default;
65 void Run() override
67 // fast lookup is needed here
68 m_queuedItems.SetFastLookup(true);
70 GetItemsForPlaylist(m_item);
73 private:
74 void GetItemsForPlaylist(const std::shared_ptr<CFileItem>& item);
76 const std::shared_ptr<CFileItem> m_item;
77 const bool m_resume{false};
78 CFileItemList& m_queuedItems;
81 SortDescription GetSortDescription(const CGUIViewState& state, const CFileItemList& items)
83 SortDescription sortDescDate;
85 auto sortDescriptions = state.GetSortDescriptions();
86 for (auto& sortDescription : sortDescriptions)
88 if (sortDescription.sortBy == SortByEpisodeNumber)
90 // check whether at least one item has actually an episode number set
91 for (const auto& item : items)
93 if (item->HasVideoInfoTag() && item->GetVideoInfoTag()->m_iEpisode > 0)
95 // first choice for folders containig episodes
96 sortDescription.sortOrder = SortOrderAscending;
97 return sortDescription;
100 continue;
102 else if (sortDescription.sortBy == SortByYear)
104 // check whether at least one item has actually a year set
105 for (const auto& item : items)
107 if (item->HasVideoInfoTag() && item->GetVideoInfoTag()->HasYear())
109 // first choice for folders containing movies
110 sortDescription.sortOrder = SortOrderAscending;
111 return sortDescription;
115 else if (sortDescription.sortBy == SortByDate)
117 // check whether at least one item has actually a valid date set
118 for (const auto& item : items)
120 if (item->m_dateTime.IsValid())
122 // fallback, if neither ByEpisode nor ByYear is available
123 sortDescDate = sortDescription;
124 sortDescDate.sortOrder = SortOrderAscending;
125 break; // leave items loop. we can still find ByEpisode or ByYear. so, no return here.
131 if (sortDescDate.sortBy != SortByNone)
132 return sortDescDate;
133 else
134 return state.GetSortMethod(); // last resort
137 void CAsyncGetItemsForPlaylist::GetItemsForPlaylist(const std::shared_ptr<CFileItem>& item)
139 if (item->IsParentFolder() || !item->CanQueue() || item->IsRAR() || item->IsZIP())
140 return;
142 if (item->m_bIsFolder)
144 if (!item->IsPlugin())
146 // check if it's a folder with dvd or bluray files, then just add the relevant file
147 const std::string mediapath = VIDEO::UTILS::GetOpticalMediaPath(*item);
148 if (!mediapath.empty())
150 m_queuedItems.Add(std::make_shared<CFileItem>(mediapath, false));
151 return;
155 // Check if we add a locked share
156 if (!item->IsPVR() && item->m_bIsShareOrDrive)
158 if (!g_passwordManager.IsItemUnlocked(item.get(), "video"))
159 return;
162 CFileItemList items;
163 XFILE::CDirectory::GetDirectory(item->GetPath(), items, "", XFILE::DIR_FLAG_DEFAULTS);
165 int viewStateWindowId = WINDOW_VIDEO_NAV;
166 if (URIUtils::IsPVRRadioRecordingFileOrFolder(item->GetPath()))
167 viewStateWindowId = WINDOW_RADIO_RECORDINGS;
168 else if (URIUtils::IsPVRTVRecordingFileOrFolder(item->GetPath()))
169 viewStateWindowId = WINDOW_TV_RECORDINGS;
171 const std::unique_ptr<CGUIViewState> state(
172 CGUIViewState::GetViewState(viewStateWindowId, items));
173 if (state)
175 LABEL_MASKS labelMasks;
176 state->GetSortMethodLabelMasks(labelMasks);
178 const CLabelFormatter fileFormatter(labelMasks.m_strLabelFile, labelMasks.m_strLabel2File);
179 const CLabelFormatter folderFormatter(labelMasks.m_strLabelFolder,
180 labelMasks.m_strLabel2Folder);
181 for (const auto& i : items)
183 if (i->IsLabelPreformatted())
184 continue;
186 if (i->m_bIsFolder)
187 folderFormatter.FormatLabels(i.get());
188 else
189 fileFormatter.FormatLabels(i.get());
192 SortDescription sortDesc;
193 if (CServiceBroker::GetGUI()->GetWindowManager().GetActiveWindow() == viewStateWindowId)
195 sortDesc = state->GetSortMethod();
197 // It makes no sense to play from younger to older.
198 if (sortDesc.sortBy == SortByDate || sortDesc.sortBy == SortByYear ||
199 sortDesc.sortBy == SortByEpisodeNumber)
200 sortDesc.sortOrder = SortOrderAscending;
202 else
203 sortDesc = GetSortDescription(*state, items);
205 if (sortDesc.sortBy == SortByLabel)
206 items.ClearSortState();
208 items.Sort(sortDesc);
211 if (items.GetContent().empty() && !VIDEO::IsVideoDb(items) && !items.IsVirtualDirectoryRoot() &&
212 !items.IsSourcesPath() && !items.IsLibraryFolder())
214 CVideoDatabase db;
215 if (db.Open())
217 std::string content = db.GetContentForPath(items.GetPath());
218 if (content.empty() && !items.IsPlugin())
219 content = "files";
221 items.SetContent(content);
225 if (m_resume)
227 // put last played item at the begin of the playlist; add start offsets for videos
228 std::shared_ptr<CFileItem> lastPlayedItem;
229 CDateTime lastPlayed;
230 for (const auto& i : items)
232 if (!i->HasVideoInfoTag())
233 continue;
235 const auto videoTag = i->GetVideoInfoTag();
237 const CBookmark& bookmark = videoTag->GetResumePoint();
238 if (bookmark.IsSet())
240 i->SetStartOffset(CUtil::ConvertSecsToMilliSecs(bookmark.timeInSeconds));
242 const CDateTime& currLastPlayed = videoTag->m_lastPlayed;
243 if (currLastPlayed.IsValid() && (!lastPlayed.IsValid() || (lastPlayed < currLastPlayed)))
245 lastPlayedItem = i;
246 lastPlayed = currLastPlayed;
251 if (lastPlayedItem)
253 items.Remove(lastPlayedItem.get());
254 items.AddFront(lastPlayedItem, 0);
258 int watchedMode;
259 if (m_resume)
260 watchedMode = WatchedModeUnwatched;
261 else
262 watchedMode = CMediaSettings::GetInstance().GetWatchedMode(items.GetContent());
264 const bool unwatchedOnly = watchedMode == WatchedModeUnwatched;
265 const bool watchedOnly = watchedMode == WatchedModeWatched;
266 bool fetchedPlayCounts = false;
267 for (const auto& i : items)
269 if (i->m_bIsFolder)
271 std::string path = i->GetPath();
272 URIUtils::RemoveSlashAtEnd(path);
273 if (StringUtils::EndsWithNoCase(path, "sample")) // skip sample folders
274 continue;
276 else
278 if (!fetchedPlayCounts &&
279 (!i->HasVideoInfoTag() || !i->GetVideoInfoTag()->IsPlayCountSet()))
281 CVideoDatabase db;
282 if (db.Open())
284 fetchedPlayCounts = true;
285 db.GetPlayCounts(items.GetPath(), items);
288 if (i->HasVideoInfoTag() && i->GetVideoInfoTag()->IsPlayCountSet())
290 const int playCount = i->GetVideoInfoTag()->GetPlayCount();
291 if ((unwatchedOnly && playCount > 0) || (watchedOnly && playCount <= 0))
292 continue;
295 GetItemsForPlaylist(i);
298 else if (PLAYLIST::IsPlayList(*item))
300 // just queue the playlist, it will be expanded on play
301 m_queuedItems.Add(item);
303 else if (NETWORK::IsInternetStream(*item))
305 // just queue the internet stream, it will be expanded on play
306 m_queuedItems.Add(item);
308 else if (item->IsPlugin() && item->GetProperty("isplayable").asBoolean())
310 // a playable python files
311 m_queuedItems.Add(item);
313 else if (VIDEO::IsVideoDb(*item))
315 // this case is needed unless we allow IsVideo() to return true for videodb items,
316 // but then we have issues with playlists of videodb items
317 const auto itemCopy = std::make_shared<CFileItem>(*item->GetVideoInfoTag());
318 itemCopy->SetStartOffset(item->GetStartOffset());
319 m_queuedItems.Add(itemCopy);
321 else if (!item->IsNFO() && VIDEO::IsVideo(*item))
323 m_queuedItems.Add(item);
327 std::string GetVideoDbItemPath(const CFileItem& item)
329 std::string path = item.GetPath();
330 if (!URIUtils::IsVideoDb(path))
331 path = item.GetProperty("original_listitem_url").asString();
333 if (URIUtils::IsVideoDb(path))
334 return path;
336 return {};
339 void AddItemToPlayListAndPlay(const std::shared_ptr<CFileItem>& itemToQueue,
340 const std::shared_ptr<CFileItem>& itemToPlay,
341 const std::string& player)
343 // recursively add items to list
344 CFileItemList queuedItems;
345 VIDEO::UTILS::GetItemsForPlayList(itemToQueue, queuedItems);
347 auto& playlistPlayer = CServiceBroker::GetPlaylistPlayer();
348 playlistPlayer.ClearPlaylist(PLAYLIST::Id::TYPE_VIDEO);
349 playlistPlayer.Reset();
350 playlistPlayer.Add(PLAYLIST::Id::TYPE_VIDEO, queuedItems);
352 // figure out where to start playback
353 PLAYLIST::CPlayList& playList = playlistPlayer.GetPlaylist(PLAYLIST::Id::TYPE_VIDEO);
354 int pos = 0;
355 if (itemToPlay)
357 for (const std::shared_ptr<CFileItem>& queuedItem : queuedItems)
359 if (queuedItem->IsSamePath(itemToPlay.get()))
360 break;
362 pos++;
366 if (playlistPlayer.IsShuffled(PLAYLIST::Id::TYPE_VIDEO))
368 playList.Swap(0, playList.FindOrder(pos));
369 pos = 0;
372 playlistPlayer.SetCurrentPlaylist(PLAYLIST::Id::TYPE_VIDEO);
373 playlistPlayer.Play(pos, player);
376 } // unnamed namespace
378 } // namespace KODI
380 namespace KODI::VIDEO::UTILS
382 void PlayItem(
383 const std::shared_ptr<CFileItem>& itemIn,
384 const std::string& player,
385 ContentUtils::PlayMode mode /* = ContentUtils::PlayMode::CHECK_AUTO_PLAY_NEXT_VIDEO */)
387 auto item = itemIn;
389 // Allow queuing of unqueueable items
390 // when we try to queue them directly
391 if (!itemIn->CanQueue())
393 // make a copy to not alter the original item
394 item = std::make_shared<CFileItem>(*itemIn);
395 item->SetCanQueue(true);
398 if (item->m_bIsFolder && !item->IsPlugin())
400 AddItemToPlayListAndPlay(item, nullptr, player);
402 else if (item->HasVideoInfoTag())
404 if (mode == ContentUtils::PlayMode::PLAY_FROM_HERE ||
405 (mode == ContentUtils::PlayMode::CHECK_AUTO_PLAY_NEXT_ITEM && IsAutoPlayNextItem(*item)))
407 // Add item and all its siblings to the playlist and play. Prefer videodb path if available,
408 // because it provides more information than just a plain file system path for example.
409 std::string parentPath = item->GetProperty("ParentPath").asString();
410 if (parentPath.empty())
412 std::string path = GetVideoDbItemPath(*item);
413 if (path.empty())
414 path = item->GetPath();
416 URIUtils::GetParentPath(path, parentPath);
418 if (parentPath.empty())
420 CLog::LogF(LOGERROR, "Unable to obtain parent path for '{}'", item->GetPath());
421 return;
425 const auto parentItem = std::make_shared<CFileItem>(parentPath, true);
426 if (item->GetStartOffset() == STARTOFFSET_RESUME)
427 parentItem->SetStartOffset(STARTOFFSET_RESUME);
429 AddItemToPlayListAndPlay(parentItem, item, player);
431 else // mode == PlayMode::PLAY_ONLY_THIS
433 // single item, play it
434 auto& playlistPlayer = CServiceBroker::GetPlaylistPlayer();
435 playlistPlayer.Reset();
436 playlistPlayer.SetCurrentPlaylist(PLAYLIST::Id::TYPE_NONE);
437 playlistPlayer.Play(item, player);
442 void QueueItem(const std::shared_ptr<CFileItem>& itemIn, QueuePosition pos)
444 auto item = itemIn;
446 // Allow queuing of unqueueable items
447 // when we try to queue them directly
448 if (!itemIn->CanQueue())
450 // make a copy to not alter the original item
451 item = std::make_shared<CFileItem>(*itemIn);
452 item->SetCanQueue(true);
455 auto& player = CServiceBroker::GetPlaylistPlayer();
456 const auto& components = CServiceBroker::GetAppComponents();
458 // Determine the proper list to queue this element
459 PLAYLIST::Id playlistId = player.GetCurrentPlaylist();
460 if (playlistId == PLAYLIST::Id::TYPE_NONE)
461 playlistId = components.GetComponent<CApplicationPlayer>()->GetPreferredPlaylist();
463 if (playlistId == PLAYLIST::Id::TYPE_NONE)
464 playlistId = PLAYLIST::Id::TYPE_VIDEO;
466 CFileItemList queuedItems;
467 GetItemsForPlayList(item, queuedItems);
469 // if party mode, add items but DONT start playing
470 if (g_partyModeManager.IsEnabled(PARTYMODECONTEXT_VIDEO))
472 g_partyModeManager.AddUserSongs(queuedItems, false);
473 return;
476 if (pos == QueuePosition::POSITION_BEGIN &&
477 components.GetComponent<CApplicationPlayer>()->IsPlaying())
478 player.Insert(playlistId, queuedItems, player.GetCurrentItemIdx() + 1);
479 else
480 player.Add(playlistId, queuedItems);
482 player.SetCurrentPlaylist(playlistId);
484 // Note: video does not auto play on queue like music
487 bool GetItemsForPlayList(const std::shared_ptr<CFileItem>& item, CFileItemList& queuedItems)
489 CAsyncGetItemsForPlaylist getItems(item, queuedItems);
490 return CGUIDialogBusy::Wait(&getItems,
491 500, // 500ms before busy dialog appears
492 true); // can be cancelled
495 namespace
497 bool IsNonExistingUserPartyModePlaylist(const CFileItem& item)
499 if (!PLAYLIST::IsSmartPlayList(item))
500 return false;
502 const std::string& path{item.GetPath()};
503 const auto profileManager{CServiceBroker::GetSettingsComponent()->GetProfileManager()};
504 return ((profileManager->GetUserDataItem("PartyMode-Video.xsp") == path) &&
505 !CFileUtils::Exists(path));
507 } // unnamed namespace
509 bool IsItemPlayable(const CFileItem& item)
511 if (item.IsParentFolder())
512 return false;
514 if (item.IsDeleted())
515 return false;
517 // Include all PVR recordings and recordings folders
518 if (URIUtils::IsPVRRecordingFileOrFolder(item.GetPath()))
519 return true;
521 // Include Live TV
522 if (!item.m_bIsFolder && (item.IsLiveTV() || item.IsEPG()))
523 return true;
525 // Exclude all music library items
526 if (MUSIC::IsMusicDb(item) || StringUtils::StartsWithNoCase(item.GetPath(), "library://music/"))
527 return false;
529 // Exclude other components
530 if (item.IsPlugin() || item.IsScript() || item.IsAddonsPath())
531 return false;
533 // Exclude special items
534 if (StringUtils::StartsWithNoCase(item.GetPath(), "newsmartplaylist://") ||
535 StringUtils::StartsWithNoCase(item.GetPath(), "newplaylist://") ||
536 StringUtils::StartsWithNoCase(item.GetPath(), "newtag://"))
537 return false;
539 // Include playlists located at one of the possible video/mixed playlist locations
540 if (PLAYLIST::IsPlayList(item))
542 if (StringUtils::StartsWithNoCase(item.GetMimeType(), "video/"))
543 return true;
545 if (StringUtils::StartsWithNoCase(item.GetPath(), "special://videoplaylists/") ||
546 StringUtils::StartsWithNoCase(item.GetPath(), "special://profile/playlists/video/") ||
547 StringUtils::StartsWithNoCase(item.GetPath(), "special://profile/playlists/mixed/"))
548 return true;
550 // Has user changed default playlists location and the list is located there?
551 const auto settings = CServiceBroker::GetSettingsComponent()->GetSettings();
552 std::string path = settings->GetString(CSettings::SETTING_SYSTEM_PLAYLISTSPATH);
553 StringUtils::TrimRight(path, "/");
554 if (StringUtils::StartsWith(item.GetPath(), StringUtils::Format("{}/video/", path)) ||
555 StringUtils::StartsWith(item.GetPath(), StringUtils::Format("{}/mixed/", path)))
556 return true;
558 if (!item.m_bIsFolder && !item.HasVideoInfoTag())
560 // Unknown location. Type cannot be determined for non-folder items.
561 return false;
565 if (IsNonExistingUserPartyModePlaylist(item))
566 return false;
568 if (item.m_bIsFolder &&
569 (IsVideoDb(item) || StringUtils::StartsWithNoCase(item.GetPath(), "library://video/")))
571 // Exclude top level nodes - eg can't play 'genres' just a specific genre etc
572 const XFILE::VIDEODATABASEDIRECTORY::NODE_TYPE node =
573 XFILE::CVideoDatabaseDirectory::GetDirectoryParentType(item.GetPath());
574 if (node == XFILE::VIDEODATABASEDIRECTORY::NODE_TYPE_OVERVIEW ||
575 node == XFILE::VIDEODATABASEDIRECTORY::NODE_TYPE_MOVIES_OVERVIEW ||
576 node == XFILE::VIDEODATABASEDIRECTORY::NODE_TYPE_TVSHOWS_OVERVIEW ||
577 node == XFILE::VIDEODATABASEDIRECTORY::NODE_TYPE_MUSICVIDEOS_OVERVIEW)
578 return false;
580 return true;
583 if (item.HasVideoInfoTag() && item.CanQueue())
585 return true;
587 else if ((!item.m_bIsFolder && IsVideo(item)) || item.IsDVD() || MUSIC::IsCDDA(item))
589 return true;
591 else if (item.m_bIsFolder)
593 // Not a video-specific folder (like file:// or nfs://). Allow play if context is Video window.
594 if (CServiceBroker::GetGUI()->GetWindowManager().GetActiveWindow() == WINDOW_VIDEO_NAV &&
595 item.GetPath() != "add") // Exclude "Add video source" item
596 return true;
599 return false;
602 bool HasItemVideoDbInformation(const CFileItem& item)
604 CVideoDatabase db;
605 if (!db.Open())
607 CLog::LogF(LOGERROR, "Cannot open VideoDatabase");
608 return false;
611 return db.HasMovieInfo(item.GetDynPath()) ||
612 db.HasTvShowInfo(URIUtils::GetDirectory(item.GetPath())) ||
613 db.HasEpisodeInfo(item.GetDynPath()) || db.HasMusicVideoInfo(item.GetDynPath());
616 std::string GetResumeString(const CFileItem& item)
618 const ResumeInformation resumeInfo = GetItemResumeInformation(item);
619 if (resumeInfo.isResumable)
621 if (resumeInfo.startOffset > 0)
623 std::string resumeString = StringUtils::Format(
624 g_localizeStrings.Get(12022),
625 StringUtils::SecondsToTimeString(
626 static_cast<long>(CUtil::ConvertMilliSecsToSecsInt(resumeInfo.startOffset)),
627 TIME_FORMAT_HH_MM_SS));
628 if (resumeInfo.partNumber > 0)
630 const std::string partString =
631 StringUtils::Format(g_localizeStrings.Get(23051), resumeInfo.partNumber);
632 resumeString += " (" + partString + ")";
634 return resumeString;
636 else
638 return g_localizeStrings.Get(13362); // Continue watching
641 return {};
644 } // namespace KODI::VIDEO::UTILS