[Windows] Fix driver version detection of AMD RDNA+ GPU on Windows 10
[xbmc.git] / xbmc / music / windows / GUIWindowMusicBase.cpp
blob7bef12c9c05006a53062c42ba61726e96c10aa81
1 /*
2 * Copyright (C) 2005-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 "GUIWindowMusicBase.h"
11 #include "Autorun.h"
12 #include "FileItem.h"
13 #include "FileItemList.h"
14 #include "GUIInfoManager.h"
15 #include "GUIPassword.h"
16 #include "GUIUserMessages.h"
17 #include "PartyModeManager.h"
18 #include "PlayListPlayer.h"
19 #include "ServiceBroker.h"
20 #include "URL.h"
21 #include "Util.h"
22 #include "addons/gui/GUIDialogAddonInfo.h"
23 #include "application/Application.h"
24 #include "application/ApplicationComponents.h"
25 #include "application/ApplicationPlayer.h"
26 #include "music/MusicFileItemClassify.h"
27 #include "network/NetworkFileItemClassify.h"
28 #include "video/VideoFileItemClassify.h"
29 #ifdef HAS_CDDA_RIPPER
30 #include "cdrip/CDDARipper.h"
31 #endif
32 #include "dialogs/GUIDialogMediaSource.h"
33 #include "dialogs/GUIDialogProgress.h"
34 #include "dialogs/GUIDialogSmartPlaylistEditor.h"
35 #include "dialogs/GUIDialogYesNo.h"
36 #include "filesystem/Directory.h"
37 #include "filesystem/MusicDatabaseDirectory.h"
38 #include "guilib/GUIComponent.h"
39 #include "guilib/GUIWindowManager.h"
40 #include "guilib/LocalizeStrings.h"
41 #include "guilib/guiinfo/GUIInfoLabels.h"
42 #include "input/actions/Action.h"
43 #include "input/actions/ActionIDs.h"
44 #include "messaging/helpers/DialogHelper.h"
45 #include "messaging/helpers/DialogOKHelper.h"
46 #include "music/MusicDbUrl.h"
47 #include "music/MusicLibraryQueue.h"
48 #include "music/MusicUtils.h"
49 #include "music/dialogs/GUIDialogInfoProviderSettings.h"
50 #include "music/dialogs/GUIDialogMusicInfo.h"
51 #include "music/infoscanner/MusicInfoScanner.h"
52 #include "music/tags/MusicInfoTag.h"
53 #include "playlists/PlayList.h"
54 #include "playlists/PlayListFactory.h"
55 #include "profiles/ProfileManager.h"
56 #include "settings/AdvancedSettings.h"
57 #include "settings/MediaSourceSettings.h"
58 #include "settings/Settings.h"
59 #include "settings/SettingsComponent.h"
60 #include "storage/MediaManager.h"
61 #include "utils/FileUtils.h"
62 #include "utils/StringUtils.h"
63 #include "utils/URIUtils.h"
64 #include "utils/Variant.h"
65 #include "utils/XTimeUtils.h"
66 #include "utils/log.h"
67 #include "video/VideoInfoTag.h"
68 #include "video/dialogs/GUIDialogVideoInfo.h"
69 #include "view/GUIViewState.h"
71 #include <algorithm>
72 #include <memory>
74 using namespace XFILE;
75 using namespace MUSICDATABASEDIRECTORY;
76 using namespace MUSIC_GRABBER;
77 using namespace MUSIC_INFO;
78 using namespace KODI;
79 using namespace KODI::MESSAGING;
80 using KODI::MESSAGING::HELPERS::DialogResponse;
81 using namespace KODI::VIDEO;
83 using namespace std::chrono_literals;
85 #define CONTROL_BTNVIEWASICONS 2
86 #define CONTROL_BTNSORTBY 3
87 #define CONTROL_BTNSORTASC 4
88 #define CONTROL_BTNPLAYLISTS 7
89 #define CONTROL_BTNSCAN 9
90 #define CONTROL_BTNRIP 11
92 CGUIWindowMusicBase::CGUIWindowMusicBase(int id, const std::string &xmlFile)
93 : CGUIMediaWindow(id, xmlFile.c_str())
95 m_dlgProgress = NULL;
96 m_thumbLoader.SetObserver(this);
99 CGUIWindowMusicBase::~CGUIWindowMusicBase () = default;
101 bool CGUIWindowMusicBase::OnBack(int actionID)
103 if (!CMusicLibraryQueue::GetInstance().IsScanningLibrary())
105 CUtil::RemoveTempFiles();
107 return CGUIMediaWindow::OnBack(actionID);
111 \brief Handle messages on window.
112 \param message GUI Message that can be reacted on.
113 \return if a message can't be processed, return \e false
115 On these messages this class reacts.\n
116 When retrieving...
117 - #GUI_MSG_WINDOW_DEINIT\n
118 ...the last focused control is saved to m_iLastControl.
119 - #GUI_MSG_WINDOW_INIT\n
120 ...the musicdatabase is opend and the music extensions and shares are set.
121 The last focused control is set.
122 - #GUI_MSG_CLICKED\n
123 ... the base class reacts on the following controls:\n
124 Buttons:\n
125 - #CONTROL_BTNVIEWASICONS - switch between list, thumb and with large items
126 - #CONTROL_BTNSEARCH - Search for items\n
127 Other Controls:
128 - The container controls\n
129 Have the following actions in message them clicking on them.
130 - #ACTION_QUEUE_ITEM - add selected item to end of playlist
131 - #ACTION_QUEUE_ITEM_NEXT - add selected item to next pos in playlist
132 - #ACTION_SHOW_INFO - retrieve album info from the internet
133 - #ACTION_SELECT_ITEM - Item has been selected. Overwrite OnClick() to react on it
135 bool CGUIWindowMusicBase::OnMessage(CGUIMessage& message)
137 switch ( message.GetMessage() )
139 case GUI_MSG_WINDOW_DEINIT:
141 if (m_thumbLoader.IsLoading())
142 m_thumbLoader.StopThread();
143 m_musicdatabase.Close();
145 break;
147 case GUI_MSG_WINDOW_INIT:
149 m_dlgProgress = CServiceBroker::GetGUI()->GetWindowManager().GetWindow<CGUIDialogProgress>(WINDOW_DIALOG_PROGRESS);
151 m_musicdatabase.Open();
153 if (!CGUIMediaWindow::OnMessage(message))
154 return false;
156 return true;
158 break;
159 case GUI_MSG_DIRECTORY_SCANNED:
161 CFileItem directory(message.GetStringParam(), true);
163 // Only update thumb on a local drive
164 if (directory.IsHD())
166 std::string strParent;
167 URIUtils::GetParentPath(directory.GetPath(), strParent);
168 if (directory.GetPath() == m_vecItems->GetPath() || strParent == m_vecItems->GetPath())
169 Refresh();
172 break;
174 // update the display
175 case GUI_MSG_SCAN_FINISHED:
176 case GUI_MSG_REFRESH_THUMBS: // Never called as is secondary msg sent as GUI_MSG_NOTIFY_ALL
177 Refresh();
178 break;
180 case GUI_MSG_CLICKED:
182 int iControl = message.GetSenderId();
183 if (iControl == CONTROL_BTNRIP)
185 OnRipCD();
187 else if (iControl == CONTROL_BTNPLAYLISTS)
189 if (!m_vecItems->IsPath("special://musicplaylists/"))
190 Update("special://musicplaylists/");
192 else if (iControl == CONTROL_BTNSCAN)
194 OnScan(-1);
196 else if (m_viewControl.HasControl(iControl)) // list/thumb control
198 int iItem = m_viewControl.GetSelectedItem();
199 int iAction = message.GetParam1();
201 // iItem is checked for validity inside these routines
202 if (iAction == ACTION_QUEUE_ITEM || iAction == ACTION_MOUSE_MIDDLE_CLICK)
204 OnQueueItem(iItem);
206 else if (iAction == ACTION_QUEUE_ITEM_NEXT)
208 OnQueueItem(iItem, true);
210 else if (iAction == ACTION_SHOW_INFO)
212 OnItemInfo(iItem);
214 else if (iAction == ACTION_DELETE_ITEM)
216 // is delete allowed?
217 // must be at the playlists directory
218 if (m_vecItems->IsPath("special://musicplaylists/"))
219 OnDeleteItem(iItem);
221 else
222 return false;
224 // use play button to add folders of items to temp playlist
225 else if (iAction == ACTION_PLAYER_PLAY)
227 const auto& components = CServiceBroker::GetAppComponents();
228 const auto appPlayer = components.GetComponent<CApplicationPlayer>();
229 // if playback is paused or playback speed != 1, return
230 if (appPlayer->IsPlayingAudio())
232 if (appPlayer->IsPausedPlayback())
233 return false;
234 if (appPlayer->GetPlaySpeed() != 1)
235 return false;
238 // not playing audio, or playback speed == 1
239 PlayItem(iItem);
241 return true;
245 break;
246 case GUI_MSG_NOTIFY_ALL:
248 if (message.GetParam1()==GUI_MSG_REMOVED_MEDIA)
249 CUtil::DeleteDirectoryCache("r-");
251 break;
253 return CGUIMediaWindow::OnMessage(message);
256 bool CGUIWindowMusicBase::OnAction(const CAction &action)
258 if (action.GetID() == ACTION_SHOW_PLAYLIST)
260 if (CServiceBroker::GetPlaylistPlayer().GetCurrentPlaylist() == PLAYLIST::Id::TYPE_MUSIC ||
261 CServiceBroker::GetPlaylistPlayer().GetPlaylist(PLAYLIST::Id::TYPE_MUSIC).size() > 0)
263 CServiceBroker::GetGUI()->GetWindowManager().ActivateWindow(WINDOW_MUSIC_PLAYLIST);
264 return true;
268 if (action.GetID() == ACTION_SCAN_ITEM)
270 int item = m_viewControl.GetSelectedItem();
271 if (item > -1 && m_vecItems->Get(item)->m_bIsFolder)
272 OnScan(item);
274 return true;
277 return CGUIMediaWindow::OnAction(action);
280 void CGUIWindowMusicBase::OnItemInfoAll(const std::string& strPath, bool refresh)
282 if (StringUtils::EqualsNoCase(m_vecItems->GetContent(), "albums"))
284 if (CMusicLibraryQueue::GetInstance().IsScanningLibrary())
285 return;
287 CMusicLibraryQueue::GetInstance().StartAlbumScan(strPath, refresh);
289 else if (StringUtils::EqualsNoCase(m_vecItems->GetContent(), "artists"))
291 if (CMusicLibraryQueue::GetInstance().IsScanningLibrary())
292 return;
294 CMusicLibraryQueue::GetInstance().StartArtistScan(strPath, refresh);
298 void CGUIWindowMusicBase::OnItemInfo(int iItem)
300 if ( iItem < 0 || iItem >= m_vecItems->Size() )
301 return;
303 CFileItemPtr item = m_vecItems->Get(iItem);
305 // Match visibility test of CMusicInfo::IsVisible
306 if (IsVideoDb(*item) && item->HasVideoInfoTag() &&
307 (item->HasProperty("artist_musicid") || item->HasProperty("album_musicid")))
309 // Music video artist or album (navigation by music > music video > artist))
310 CGUIDialogMusicInfo::ShowFor(item.get());
311 return;
314 if (IsVideo(*item) && item->HasVideoInfoTag() &&
315 item->GetVideoInfoTag()->m_type == MediaTypeMusicVideo)
316 { // Music video on a mixed current playlist or navigation by music > music video > artist > video
317 CGUIDialogVideoInfo::ShowFor(*item);
318 return;
321 if (!m_vecItems->IsPlugin() && (item->IsPlugin() || item->IsScript()))
323 CGUIDialogAddonInfo::ShowForItem(item);
324 return;
327 // Match visibility test of CMusicInfo::IsVisible
328 if (item->HasMusicInfoTag() && (item->GetMusicInfoTag()->GetType() == MediaTypeSong ||
329 item->GetMusicInfoTag()->GetType() == MediaTypeAlbum ||
330 item->GetMusicInfoTag()->GetType() == MediaTypeArtist))
331 CGUIDialogMusicInfo::ShowFor(item.get());
334 void CGUIWindowMusicBase::RefreshContent(const std::string& strContent)
336 if ( CServiceBroker::GetGUI()->GetWindowManager().GetActiveWindow() == WINDOW_MUSIC_NAV &&
337 m_vecItems->GetContent() == strContent &&
338 m_vecItems->GetSortMethod() == SortByUserRating)
339 // When music library window is active and showing songs or albums sorted
340 // by userrating refresh the list to resort items and show new userrating
341 Refresh(true);
344 /// \brief Retrieve tag information for \e m_vecItems
345 void CGUIWindowMusicBase::RetrieveMusicInfo()
347 auto start = std::chrono::steady_clock::now();
349 OnRetrieveMusicInfo(*m_vecItems);
351 //! @todo Scan for multitrack items here...
352 std::vector<std::string> itemsForRemove;
353 CFileItemList itemsForAdd;
354 for (int i = 0; i < m_vecItems->Size(); ++i)
356 CFileItemPtr pItem = (*m_vecItems)[i];
357 if (pItem->m_bIsFolder || pItem->IsPlayList() || pItem->IsPicture() ||
358 MUSIC::IsLyrics(*pItem) || IsVideo(*pItem))
359 continue;
361 CMusicInfoTag& tag = *pItem->GetMusicInfoTag();
362 if (tag.Loaded() && !tag.GetCueSheet().empty())
363 pItem->LoadEmbeddedCue();
365 if (pItem->HasCueDocument()
366 && pItem->LoadTracksFromCueDocument(itemsForAdd))
368 itemsForRemove.push_back(pItem->GetPath());
371 for (size_t i = 0; i < itemsForRemove.size(); ++i)
373 for (int j = 0; j < m_vecItems->Size(); ++j)
375 if ((*m_vecItems)[j]->GetPath() == itemsForRemove[i])
377 m_vecItems->Remove(j);
378 break;
382 m_vecItems->Append(itemsForAdd);
384 auto end = std::chrono::steady_clock::now();
385 auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(end - start);
387 CLog::Log(LOGDEBUG, "RetrieveMusicInfo() took {} ms", duration.count());
390 /// \brief Add selected list/thumb control item to playlist and start playing
391 /// \param iItem Selected Item in list/thumb control
392 void CGUIWindowMusicBase::OnQueueItem(int iItem, bool first)
394 // don't re-queue items from playlist window
395 if (iItem < 0 || iItem >= m_vecItems->Size() || GetID() == WINDOW_MUSIC_PLAYLIST)
396 return;
398 // add item 2 playlist
399 const auto item = m_vecItems->Get(iItem);
401 if (item->IsRAR() || item->IsZIP())
402 return;
404 MUSIC_UTILS::QueueItem(item, first ? MUSIC_UTILS::QueuePosition::POSITION_BEGIN
405 : MUSIC_UTILS::QueuePosition::POSITION_END);
407 // select next item
408 m_viewControl.SetSelectedItem(iItem + 1);
411 void CGUIWindowMusicBase::UpdateButtons()
413 CONTROL_ENABLE_ON_CONDITION(CONTROL_BTNRIP, CServiceBroker::GetMediaManager().IsAudio());
415 CONTROL_ENABLE_ON_CONDITION(
416 CONTROL_BTNSCAN, !(m_vecItems->IsVirtualDirectoryRoot() || MUSIC::IsMusicDb(*m_vecItems)));
418 if (CMusicLibraryQueue::GetInstance().IsScanningLibrary())
419 SET_CONTROL_LABEL(CONTROL_BTNSCAN, 14056); // Stop Scan
420 else
421 SET_CONTROL_LABEL(CONTROL_BTNSCAN, 102); // Scan
423 CGUIMediaWindow::UpdateButtons();
426 void CGUIWindowMusicBase::GetContextButtons(int itemNumber, CContextButtons &buttons)
428 CFileItemPtr item;
429 if (itemNumber >= 0 && itemNumber < m_vecItems->Size())
430 item = m_vecItems->Get(itemNumber);
432 if (item)
434 const std::shared_ptr<CProfileManager> profileManager = CServiceBroker::GetSettingsComponent()->GetProfileManager();
436 // Check for the partymode playlist item.
437 // When "PartyMode.xsp" not exist, only context menu button is edit
438 if (item->IsSmartPlayList() &&
439 (item->GetPath() == profileManager->GetUserDataItem("PartyMode.xsp")) &&
440 !CFileUtils::Exists(item->GetPath()))
442 buttons.Add(CONTEXT_BUTTON_EDIT_SMART_PLAYLIST, 586);
443 return;
446 if (!item->IsParentFolder())
448 //! @todo get rid of IsAddonsPath and IsScript check. CanQueue should be enough!
449 if (item->CanQueue() && !item->IsAddonsPath() && !item->IsScript())
451 if (item->IsSmartPlayList())
452 buttons.Add(CONTEXT_BUTTON_PLAY_PARTYMODE, 15216); // Play in Partymode
454 if (item->IsSmartPlayList() || m_vecItems->IsSmartPlayList())
455 buttons.Add(CONTEXT_BUTTON_EDIT_SMART_PLAYLIST, 586);
456 else if (item->IsPlayList() || m_vecItems->IsPlayList())
457 buttons.Add(CONTEXT_BUTTON_EDIT, 586);
459 #ifdef HAS_OPTICAL_DRIVE
460 // enable Rip CD Audio or Track button if we have an audio disc
461 if (CServiceBroker::GetMediaManager().IsDiscInDrive() && MUSIC::IsCDDA(*m_vecItems))
463 // those cds can also include Audio Tracks: CDExtra and MixedMode!
464 MEDIA_DETECT::CCdInfo* pCdInfo = CServiceBroker::GetMediaManager().GetCdInfo();
465 if (pCdInfo->IsAudio(1) || pCdInfo->IsCDExtra(1) || pCdInfo->IsMixedMode(1))
466 buttons.Add(CONTEXT_BUTTON_RIP_TRACK, 610);
468 #endif
471 // enable CDDB lookup if the current dir is CDDA
472 if (CServiceBroker::GetMediaManager().IsDiscInDrive() && MUSIC::IsCDDA(*m_vecItems) &&
473 (profileManager->GetCurrentProfile().canWriteDatabases() || g_passwordManager.bMasterUser))
475 buttons.Add(CONTEXT_BUTTON_CDDB, 16002);
478 CGUIMediaWindow::GetContextButtons(itemNumber, buttons);
481 void CGUIWindowMusicBase::GetNonContextButtons(CContextButtons &buttons)
485 bool CGUIWindowMusicBase::OnContextButton(int itemNumber, CONTEXT_BUTTON button)
487 CFileItemPtr item;
488 if (itemNumber >= 0 && itemNumber < m_vecItems->Size())
489 item = m_vecItems->Get(itemNumber);
491 if (CGUIDialogContextMenu::OnContextButton("music", item, button))
493 if (button == CONTEXT_BUTTON_REMOVE_SOURCE)
494 OnRemoveSource(itemNumber);
496 Update(m_vecItems->GetPath());
497 return true;
500 switch (button)
502 case CONTEXT_BUTTON_INFO:
503 OnItemInfo(itemNumber);
504 return true;
506 case CONTEXT_BUTTON_EDIT:
508 std::string playlist = item->IsPlayList() ? item->GetPath() : m_vecItems->GetPath(); // save path as activatewindow will destroy our items
509 CServiceBroker::GetGUI()->GetWindowManager().ActivateWindow(WINDOW_MUSIC_PLAYLIST_EDITOR, playlist);
510 // need to update
511 m_vecItems->RemoveDiscCache(GetID());
512 return true;
515 case CONTEXT_BUTTON_EDIT_SMART_PLAYLIST:
517 std::string playlist = item->IsSmartPlayList() ? item->GetPath() : m_vecItems->GetPath(); // save path as activatewindow will destroy our items
518 if (CGUIDialogSmartPlaylistEditor::EditPlaylist(playlist, "music"))
519 Refresh(true); // need to update
520 return true;
523 case CONTEXT_BUTTON_PLAY_PARTYMODE:
524 g_partyModeManager.Enable(PARTYMODECONTEXT_MUSIC, item->GetPath());
525 return true;
527 case CONTEXT_BUTTON_RIP_CD:
528 OnRipCD();
529 return true;
531 #ifdef HAS_CDDA_RIPPER
532 case CONTEXT_BUTTON_CANCEL_RIP_CD:
533 KODI::CDRIP::CCDDARipper::GetInstance().CancelJobs();
534 return true;
535 #endif
537 case CONTEXT_BUTTON_RIP_TRACK:
538 OnRipTrack(itemNumber);
539 return true;
541 case CONTEXT_BUTTON_SCAN:
542 // Check if scanning already and inform user
543 if (CMusicLibraryQueue::GetInstance().IsScanningLibrary())
544 HELPERS::ShowOKDialogText(CVariant{ 189 }, CVariant{ 14057 });
545 else
546 OnScan(itemNumber, true);
547 return true;
549 case CONTEXT_BUTTON_CDDB:
550 if (m_musicdatabase.LookupCDDBInfo(true))
551 Refresh();
552 return true;
554 default:
555 break;
558 return CGUIMediaWindow::OnContextButton(itemNumber, button);
561 bool CGUIWindowMusicBase::OnAddMediaSource()
563 return CGUIDialogMediaSource::ShowAndAddMediaSource("music");
566 void CGUIWindowMusicBase::OnRipCD()
568 if (CServiceBroker::GetMediaManager().IsAudio())
570 if (!MUSIC::IsCDDA(g_application.CurrentFileItem()))
572 #ifdef HAS_CDDA_RIPPER
573 KODI::CDRIP::CCDDARipper::GetInstance().RipCD();
574 #endif
576 else
577 HELPERS::ShowOKDialogText(CVariant{257}, CVariant{20099});
581 void CGUIWindowMusicBase::OnRipTrack(int iItem)
583 if (CServiceBroker::GetMediaManager().IsAudio())
585 if (!MUSIC::IsCDDA(g_application.CurrentFileItem()))
587 #ifdef HAS_CDDA_RIPPER
588 CFileItemPtr item = m_vecItems->Get(iItem);
589 KODI::CDRIP::CCDDARipper::GetInstance().RipTrack(item.get());
590 #endif
592 else
593 HELPERS::ShowOKDialogText(CVariant{257}, CVariant{20099});
597 void CGUIWindowMusicBase::PlayItem(int iItem)
599 // restrictions should be placed in the appropriate window code
600 // only call the base code if the item passes since this clears
601 // the current playlist
603 const CFileItemPtr pItem = m_vecItems->Get(iItem);
604 #ifdef HAS_OPTICAL_DRIVE
605 if (pItem->IsDVD())
607 MEDIA_DETECT::CAutorun::PlayDiscAskResume(pItem->GetPath());
608 return;
610 #endif
612 // Check for the partymode playlist item, do nothing when "PartyMode.xsp" not exist
613 if (pItem->IsSmartPlayList())
615 const std::shared_ptr<CProfileManager> profileManager =
616 CServiceBroker::GetSettingsComponent()->GetProfileManager();
617 if ((pItem->GetPath() == profileManager->GetUserDataItem("PartyMode.xsp")) &&
618 !CFileUtils::Exists(pItem->GetPath()))
619 return;
622 // if its a folder, build a playlist
623 if (pItem->m_bIsFolder && !pItem->IsPlugin())
625 // make a copy so that we can alter the queue state
626 CFileItemPtr item(new CFileItem(*m_vecItems->Get(iItem)));
628 // Allow queuing of unqueueable items
629 // when we try to queue them directly
630 if (!item->CanQueue())
631 item->SetCanQueue(true);
633 // skip ".."
634 if (item->IsParentFolder())
635 return;
637 CFileItemList queuedItems;
638 MUSIC_UTILS::GetItemsForPlayList(item, queuedItems);
639 if (g_partyModeManager.IsEnabled())
641 g_partyModeManager.AddUserSongs(queuedItems, true);
642 return;
646 std::string strPlayListDirectory = m_vecItems->GetPath();
647 URIUtils::RemoveSlashAtEnd(strPlayListDirectory);
650 CServiceBroker::GetPlaylistPlayer().ClearPlaylist(PLAYLIST::Id::TYPE_MUSIC);
651 CServiceBroker::GetPlaylistPlayer().Reset();
652 CServiceBroker::GetPlaylistPlayer().Add(PLAYLIST::Id::TYPE_MUSIC, queuedItems);
653 CServiceBroker::GetPlaylistPlayer().SetCurrentPlaylist(PLAYLIST::Id::TYPE_MUSIC);
655 // play!
656 CServiceBroker::GetPlaylistPlayer().Play();
658 else if (pItem->IsPlayList())
660 // load the playlist the old way
661 LoadPlayList(pItem->GetPath());
663 else
665 // just a single item, play it
666 //! @todo Add music-specific code for single playback of an item here (See OnClick in MediaWindow, and OnPlayMedia below)
667 OnClick(iItem);
671 void CGUIWindowMusicBase::LoadPlayList(const std::string& strPlayList)
673 // if partymode is active, we disable it
674 if (g_partyModeManager.IsEnabled())
675 g_partyModeManager.Disable();
677 // load a playlist like .m3u, .pls
678 // first get correct factory to load playlist
679 std::unique_ptr<PLAYLIST::CPlayList> pPlayList(PLAYLIST::CPlayListFactory::Create(strPlayList));
680 if (pPlayList)
682 // load it
683 if (!pPlayList->Load(strPlayList))
685 HELPERS::ShowOKDialogText(CVariant{6}, CVariant{477});
686 return; //hmmm unable to load playlist?
690 int iSize = pPlayList->size();
691 if (g_application.ProcessAndStartPlaylist(strPlayList, *pPlayList, PLAYLIST::Id::TYPE_MUSIC))
693 if (m_guiState)
694 m_guiState->SetPlaylistDirectory("playlistmusic://");
695 // activate the playlist window if its not activated yet
696 if (GetID() == CServiceBroker::GetGUI()->GetWindowManager().GetActiveWindow() && iSize > 1)
698 CServiceBroker::GetGUI()->GetWindowManager().ActivateWindow(WINDOW_MUSIC_PLAYLIST);
703 bool CGUIWindowMusicBase::OnPlayMedia(int iItem, const std::string &player)
705 CFileItemPtr pItem = m_vecItems->Get(iItem);
707 // party mode
708 if (g_partyModeManager.IsEnabled())
710 PLAYLIST::CPlayList playlistTemp;
711 playlistTemp.Add(pItem);
712 g_partyModeManager.AddUserSongs(playlistTemp, !CServiceBroker::GetSettingsComponent()->GetSettings()->GetBool(CSettings::SETTING_MUSICPLAYER_QUEUEBYDEFAULT));
713 return true;
715 else if (!pItem->IsPlayList() && !NETWORK::IsInternetStream(*pItem))
716 { // single music file - if we get here then we have autoplaynextitem turned off or queuebydefault
717 // turned on, but we still want to use the playlist player in order to handle more queued items
718 // following etc.
719 if ( (CServiceBroker::GetSettingsComponent()->GetSettings()->GetBool(CSettings::SETTING_MUSICPLAYER_QUEUEBYDEFAULT) && CServiceBroker::GetGUI()->GetWindowManager().GetActiveWindow() != WINDOW_MUSIC_PLAYLIST_EDITOR) )
721 //! @todo Should the playlist be cleared if nothing is already playing?
722 OnQueueItem(iItem);
723 return true;
725 pItem->SetProperty("playlist_type_hint", static_cast<int>(m_guiState->GetPlaylist()));
726 CServiceBroker::GetPlaylistPlayer().Play(pItem, player);
727 return true;
729 return CGUIMediaWindow::OnPlayMedia(iItem, player);
732 /// \brief Can be overwritten to implement an own tag filling function.
733 /// \param items File items to fill
734 void CGUIWindowMusicBase::OnRetrieveMusicInfo(CFileItemList& items)
736 // No need to attempt to read music file tags for music videos
737 if (IsVideoDb(items))
738 return;
739 if (items.GetFolderCount() == items.Size() || MUSIC::IsMusicDb(items) ||
740 (!CServiceBroker::GetSettingsComponent()->GetSettings()->GetBool(
741 CSettings::SETTING_MUSICFILES_USETAGS) &&
742 !MUSIC::IsCDDA(items)))
744 return;
746 // Start the music info loader thread
747 m_musicInfoLoader.SetProgressCallback(m_dlgProgress);
748 m_musicInfoLoader.Load(items);
750 bool bShowProgress = !CServiceBroker::GetGUI()->GetWindowManager().HasModalDialog(true);
751 bool bProgressVisible = false;
753 auto start = std::chrono::steady_clock::now();
755 while (m_musicInfoLoader.IsLoading())
757 if (bShowProgress)
758 { // Do we have to init a progress dialog?
759 auto end = std::chrono::steady_clock::now();
760 auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(end - start);
762 if (!bProgressVisible && duration.count() > 1500 && m_dlgProgress)
763 { // tag loading takes more then 1.5 secs, show a progress dialog
764 CURL url(items.GetPath());
765 m_dlgProgress->SetHeading(CVariant{189});
766 m_dlgProgress->SetLine(0, CVariant{505});
767 m_dlgProgress->SetLine(1, CVariant{""});
768 m_dlgProgress->SetLine(2, CVariant{url.GetWithoutUserDetails()});
769 m_dlgProgress->Open();
770 m_dlgProgress->ShowProgressBar(true);
771 bProgressVisible = true;
774 if (bProgressVisible && m_dlgProgress && !m_dlgProgress->IsCanceled())
775 { // keep GUI alive
776 m_dlgProgress->Progress();
778 } // if (bShowProgress)
779 KODI::TIME::Sleep(1ms);
780 } // while (m_musicInfoLoader.IsLoading())
782 if (bProgressVisible && m_dlgProgress)
783 m_dlgProgress->Close();
786 bool CGUIWindowMusicBase::GetDirectory(const std::string &strDirectory, CFileItemList &items)
788 items.ClearArt();
789 bool bResult = CGUIMediaWindow::GetDirectory(strDirectory, items);
790 if (bResult)
792 // We want to expand disc images when browsing in file view but not on library, smartplaylist
793 // or node menu music windows
794 if (!items.GetPath().empty() && !StringUtils::StartsWithNoCase(items.GetPath(), "musicdb://") &&
795 !StringUtils::StartsWithNoCase(items.GetPath(), "special://") &&
796 !StringUtils::StartsWithNoCase(items.GetPath(), "library://"))
797 CDirectory::FilterFileDirectories(items, ".iso", true);
799 CMusicThumbLoader loader;
800 loader.FillThumb(items);
802 CQueryParams params;
803 CDirectoryNode::GetDatabaseInfo(items.GetPath(), params);
805 // Get art for directory when album or artist
806 bool artfound = false;
807 std::vector<ArtForThumbLoader> art;
808 if (params.GetAlbumId() > 0)
809 { // Get album and related artist(s) art
810 artfound = m_musicdatabase.GetArtForItem(-1, params.GetAlbumId(), -1, false, art);
812 else if (params.GetArtistId() > 0)
813 { // get artist art
814 artfound = m_musicdatabase.GetArtForItem(-1, -1, params.GetArtistId(), true, art);
816 if (artfound)
818 std::string dirType = MediaTypeArtist;
819 if (params.GetAlbumId() > 0)
820 dirType = MediaTypeAlbum;
821 std::map<std::string, std::string> artmap;
822 for (auto artitem : art)
824 std::string artname;
825 if (dirType == artitem.mediaType)
826 artname = artitem.artType;
827 else if (artitem.prefix.empty())
828 artname = artitem.mediaType + "." + artitem.artType;
829 else
831 if (dirType == MediaTypeAlbum)
832 StringUtils::Replace(artitem.prefix, "albumartist", "artist");
833 artname = artitem.prefix + "." + artitem.artType;
835 artmap.insert(std::make_pair(artname, artitem.url));
837 items.SetArt(artmap);
840 int iWindow = GetID();
841 // Add "New Playlist" items when in the playlists folder, except on playlist editor screen
842 if ((iWindow != WINDOW_MUSIC_PLAYLIST_EDITOR) &&
843 (items.GetPath() == "special://musicplaylists/") && !items.Contains("newplaylist://"))
845 const std::shared_ptr<CProfileManager> profileManager = CServiceBroker::GetSettingsComponent()->GetProfileManager();
847 CFileItemPtr newPlaylist(new CFileItem(profileManager->GetUserDataItem("PartyMode.xsp"),false));
848 newPlaylist->SetLabel(g_localizeStrings.Get(16035));
849 newPlaylist->SetLabelPreformatted(true);
850 newPlaylist->SetArt("icon", "DefaultPartyMode.png");
851 newPlaylist->m_bIsFolder = true;
852 items.Add(newPlaylist);
854 newPlaylist = std::make_shared<CFileItem>("newplaylist://", false);
855 newPlaylist->SetLabel(g_localizeStrings.Get(525));
856 newPlaylist->SetArt("icon", "DefaultAddSource.png");
857 newPlaylist->SetLabelPreformatted(true);
858 newPlaylist->SetSpecialSort(SortSpecialOnBottom);
859 newPlaylist->SetCanQueue(false);
860 items.Add(newPlaylist);
862 newPlaylist = std::make_shared<CFileItem>("newsmartplaylist://music", false);
863 newPlaylist->SetLabel(g_localizeStrings.Get(21437));
864 newPlaylist->SetArt("icon", "DefaultAddSource.png");
865 newPlaylist->SetLabelPreformatted(true);
866 newPlaylist->SetSpecialSort(SortSpecialOnBottom);
867 newPlaylist->SetCanQueue(false);
868 items.Add(newPlaylist);
871 // check for .CUE files here.
872 items.FilterCueItems();
874 std::string label;
875 if (items.GetLabel().empty() && m_rootDir.IsSource(items.GetPath(), CMediaSourceSettings::GetInstance().GetSources("music"), &label))
876 items.SetLabel(label);
879 return bResult;
882 bool CGUIWindowMusicBase::CheckFilterAdvanced(CFileItemList &items) const
884 const std::string& content = items.GetContent();
885 if ((MUSIC::IsMusicDb(items) || CanContainFilter(m_strFilterPath)) &&
886 (StringUtils::EqualsNoCase(content, "artists") ||
887 StringUtils::EqualsNoCase(content, "albums") || StringUtils::EqualsNoCase(content, "songs")))
888 return true;
890 return false;
893 bool CGUIWindowMusicBase::CanContainFilter(const std::string &strDirectory) const
895 return URIUtils::IsProtocol(strDirectory, "musicdb");
898 bool CGUIWindowMusicBase::OnSelect(int iItem)
900 auto item = m_vecItems->Get(iItem);
901 if (MUSIC::IsAudioBook(*item))
903 int bookmark;
904 if (m_musicdatabase.GetResumeBookmarkForAudioBook(*item, bookmark) && bookmark > 0)
906 // find which chapter the bookmark belongs to
907 auto itemIt =
908 std::find_if(m_vecItems->cbegin(), m_vecItems->cend(),
909 [&](const CFileItemPtr& item) { return bookmark < item->GetEndOffset(); });
911 if (itemIt != m_vecItems->cend())
913 // ask the user if they want to play or resume
914 CContextButtons choices;
915 choices.Add(MUSIC_SELECT_ACTION_PLAY, 208); // 208 = Play
916 choices.Add(MUSIC_SELECT_ACTION_RESUME,
917 StringUtils::Format(g_localizeStrings.Get(12022), // 12022 = Resume from ...
918 (*itemIt)->GetMusicInfoTag()->GetTitle()));
920 auto choice = CGUIDialogContextMenu::Show(choices);
921 if (choice == MUSIC_SELECT_ACTION_RESUME)
923 (*itemIt)->SetProperty("audiobook_bookmark", bookmark);
924 return CGUIMediaWindow::OnSelect(static_cast<int>(itemIt - m_vecItems->cbegin()));
926 else if (choice < 0)
927 return true;
932 return CGUIMediaWindow::OnSelect(iItem);
935 void CGUIWindowMusicBase::OnInitWindow()
937 CGUIMediaWindow::OnInitWindow();
938 // Prompt for rescan of library to read music file tags that were not processed by previous versions
939 // and accommodate any changes to the way some tags are processed
940 if (m_musicdatabase.GetMusicNeedsTagScan() != 0)
942 if (CServiceBroker::GetGUI()
943 ->GetInfoManager()
944 .GetInfoProviders()
945 .GetLibraryInfoProvider()
946 .GetLibraryBool(LIBRARY_HAS_MUSIC) &&
947 !CMusicLibraryQueue::GetInstance().IsScanningLibrary())
949 // rescan of music library required
950 if (CGUIDialogYesNo::ShowAndGetInput(CVariant{799}, CVariant{38060}))
952 int flags = CMusicInfoScanner::SCAN_RESCAN;
953 // When set to fetch information on update enquire about scraping that as well
954 // It may take some time, so the user may want to do it later by "Query Info For All"
955 if (CServiceBroker::GetSettingsComponent()->GetSettings()->GetBool(CSettings::SETTING_MUSICLIBRARY_DOWNLOADINFO))
956 if (CGUIDialogYesNo::ShowAndGetInput(CVariant{799}, CVariant{38061}))
957 flags |= CMusicInfoScanner::SCAN_ONLINE;
959 CMusicLibraryQueue::GetInstance().ScanLibrary("", flags, true);
961 m_musicdatabase.SetMusicTagScanVersion(); // once is enough (user may interrupt, but that's up to them)
964 else
966 // no need to force a rescan if there's no music in the library or if a library scan is already active
967 m_musicdatabase.SetMusicTagScanVersion();
972 std::string CGUIWindowMusicBase::GetStartFolder(const std::string &dir)
974 std::string lower(dir); StringUtils::ToLower(lower);
975 if (lower == "plugins" || lower == "addons")
976 return "addons://sources/audio/";
977 else if (lower == "$playlists" || lower == "playlists")
978 return "special://musicplaylists/";
979 return CGUIMediaWindow::GetStartFolder(dir);
982 void CGUIWindowMusicBase::OnScan(int iItem, bool bPromptRescan /*= false*/)
984 std::string strPath;
985 if (iItem < 0 || iItem >= m_vecItems->Size())
986 strPath = m_vecItems->GetPath();
987 else if (m_vecItems->Get(iItem)->m_bIsFolder)
988 strPath = m_vecItems->Get(iItem)->GetPath();
989 else
990 { //! @todo MUSICDB - should we allow scanning a single item into the database?
991 //! This will require changes to the info scanner, which assumes we're running on a folder
992 strPath = m_vecItems->GetPath();
994 // Ask for full rescan of music files when scan item from file view context menu
995 bool doRescan = false;
996 if (bPromptRescan)
997 doRescan = CGUIDialogYesNo::ShowAndGetInput(CVariant{ 799 }, CVariant{ 38062 });
999 DoScan(strPath, doRescan);
1002 void CGUIWindowMusicBase::DoScan(const std::string &strPath, bool bRescan /*= false*/)
1004 if (CMusicLibraryQueue::GetInstance().IsScanningLibrary())
1006 CMusicLibraryQueue::GetInstance().StopLibraryScanning();
1007 return;
1010 // Start background loader
1011 int iControl=GetFocusedControlID();
1012 int flags = 0;
1013 if (bRescan)
1014 flags = CMusicInfoScanner::SCAN_RESCAN;
1015 if (CServiceBroker::GetSettingsComponent()->GetSettings()->GetBool(CSettings::SETTING_MUSICLIBRARY_DOWNLOADINFO))
1016 flags |= CMusicInfoScanner::SCAN_ONLINE;
1018 CMusicLibraryQueue::GetInstance().ScanLibrary(strPath, flags, true);
1020 SET_CONTROL_FOCUS(iControl, 0);
1021 UpdateButtons();
1024 void CGUIWindowMusicBase::OnRemoveSource(int iItem)
1027 //Remove music source from library, even when leaving songs
1028 CMusicDatabase database;
1029 database.Open();
1030 database.RemoveSource(m_vecItems->Get(iItem)->GetLabel());
1032 bool bCanceled;
1033 if (CGUIDialogYesNo::ShowAndGetInput(CVariant{522}, CVariant{20340}, bCanceled, CVariant{""}, CVariant{""}, CGUIDialogYesNo::NO_TIMEOUT))
1035 MAPSONGS songs;
1036 database.RemoveSongsFromPath(m_vecItems->Get(iItem)->GetPath(), songs, false);
1037 database.CleanupOrphanedItems();
1038 database.CheckArtistLinksChanged();
1039 CServiceBroker::GetGUI()->GetInfoManager().GetInfoProviders().GetLibraryInfoProvider().ResetLibraryBools();
1040 m_vecItems->RemoveDiscCache(GetID());
1042 database.Close();
1045 void CGUIWindowMusicBase::OnPrepareFileItems(CFileItemList &items)
1047 CGUIMediaWindow::OnPrepareFileItems(items);
1049 if (!MUSIC::IsMusicDb(items) && !items.IsSmartPlayList())
1050 RetrieveMusicInfo();
1053 void CGUIWindowMusicBase::OnAssignContent(const std::string& oldName, const CMediaSource& source)
1055 // Music scrapers are not source specific, so unlike video there is no content selection logic here.
1056 // Called on having added or edited a music source, this starts scanning items into library when required
1058 //! @todo: do async as updating sources for all albums could be slow??
1059 //Store music source in the music library, even those not scanned
1060 CMusicDatabase database;
1061 database.Open();
1062 database.UpdateSource(oldName, source.strName, source.strPath, source.vecPaths);
1063 database.Close();
1065 // "Add to library" yes/no dialog with additional "settings" custom button
1066 // "Do you want to add the media from this source to your library?"
1067 DialogResponse rep = DialogResponse::CHOICE_CUSTOM;
1068 while (rep == DialogResponse::CHOICE_CUSTOM)
1070 rep = HELPERS::ShowYesNoCustomDialog(CVariant{20444}, CVariant{20447}, CVariant{106}, CVariant{107}, CVariant{10004});
1071 if (rep == DialogResponse::CHOICE_CUSTOM)
1072 // Edit default info provider settings so can be applied during scan
1073 CGUIDialogInfoProviderSettings::Show();
1075 if (rep == DialogResponse::CHOICE_YES)
1076 CMusicLibraryQueue::GetInstance().ScanLibrary(source.strPath,
1077 MUSIC_INFO::CMusicInfoScanner::SCAN_NORMAL, true);