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.
9 #include "GUIWindowMusicBase.h"
11 #include "GUIUserMessages.h"
12 #include "PlayListPlayer.h"
13 #include "ServiceBroker.h"
15 #include "application/Application.h"
16 #include "application/ApplicationComponents.h"
17 #include "application/ApplicationPlayer.h"
18 #include "dialogs/GUIDialogMediaSource.h"
19 #include "input/actions/Action.h"
20 #include "input/actions/ActionIDs.h"
21 #include "music/MusicDbUrl.h"
22 #include "music/MusicLibraryQueue.h"
23 #include "music/MusicUtils.h"
24 #include "music/dialogs/GUIDialogInfoProviderSettings.h"
25 #include "music/dialogs/GUIDialogMusicInfo.h"
26 #include "playlists/PlayList.h"
27 #include "playlists/PlayListFactory.h"
28 #ifdef HAS_CDDA_RIPPER
29 #include "cdrip/CDDARipper.h"
33 #include "GUIInfoManager.h"
34 #include "GUIPassword.h"
35 #include "PartyModeManager.h"
37 #include "addons/gui/GUIDialogAddonInfo.h"
38 #include "cores/playercorefactory/PlayerCoreFactory.h"
39 #include "dialogs/GUIDialogProgress.h"
40 #include "dialogs/GUIDialogSmartPlaylistEditor.h"
41 #include "dialogs/GUIDialogYesNo.h"
42 #include "filesystem/Directory.h"
43 #include "filesystem/MusicDatabaseDirectory.h"
44 #include "guilib/GUIComponent.h"
45 #include "guilib/GUIWindowManager.h"
46 #include "guilib/LocalizeStrings.h"
47 #include "guilib/guiinfo/GUIInfoLabels.h"
48 #include "messaging/helpers/DialogHelper.h"
49 #include "messaging/helpers/DialogOKHelper.h"
50 #include "music/infoscanner/MusicInfoScanner.h"
51 #include "music/tags/MusicInfoTag.h"
52 #include "profiles/ProfileManager.h"
53 #include "settings/AdvancedSettings.h"
54 #include "settings/MediaSourceSettings.h"
55 #include "settings/Settings.h"
56 #include "settings/SettingsComponent.h"
57 #include "storage/MediaManager.h"
58 #include "utils/FileUtils.h"
59 #include "utils/StringUtils.h"
60 #include "utils/URIUtils.h"
61 #include "utils/Variant.h"
62 #include "utils/XTimeUtils.h"
63 #include "utils/log.h"
64 #include "video/VideoInfoTag.h"
65 #include "video/dialogs/GUIDialogVideoInfo.h"
66 #include "view/GUIViewState.h"
70 using namespace XFILE
;
71 using namespace MUSICDATABASEDIRECTORY
;
72 using namespace MUSIC_GRABBER
;
73 using namespace MUSIC_INFO
;
74 using namespace KODI::MESSAGING
;
75 using KODI::MESSAGING::HELPERS::DialogResponse
;
77 using namespace std::chrono_literals
;
79 #define CONTROL_BTNVIEWASICONS 2
80 #define CONTROL_BTNSORTBY 3
81 #define CONTROL_BTNSORTASC 4
82 #define CONTROL_BTNPLAYLISTS 7
83 #define CONTROL_BTNSCAN 9
84 #define CONTROL_BTNRIP 11
86 CGUIWindowMusicBase::CGUIWindowMusicBase(int id
, const std::string
&xmlFile
)
87 : CGUIMediaWindow(id
, xmlFile
.c_str())
90 m_thumbLoader
.SetObserver(this);
93 CGUIWindowMusicBase::~CGUIWindowMusicBase () = default;
95 bool CGUIWindowMusicBase::OnBack(int actionID
)
97 if (!CMusicLibraryQueue::GetInstance().IsScanningLibrary())
99 CUtil::RemoveTempFiles();
101 return CGUIMediaWindow::OnBack(actionID
);
105 \brief Handle messages on window.
106 \param message GUI Message that can be reacted on.
107 \return if a message can't be processed, return \e false
109 On these messages this class reacts.\n
111 - #GUI_MSG_WINDOW_DEINIT\n
112 ...the last focused control is saved to m_iLastControl.
113 - #GUI_MSG_WINDOW_INIT\n
114 ...the musicdatabase is opend and the music extensions and shares are set.
115 The last focused control is set.
117 ... the base class reacts on the following controls:\n
119 - #CONTROL_BTNVIEWASICONS - switch between list, thumb and with large items
120 - #CONTROL_BTNSEARCH - Search for items\n
122 - The container controls\n
123 Have the following actions in message them clicking on them.
124 - #ACTION_QUEUE_ITEM - add selected item to end of playlist
125 - #ACTION_QUEUE_ITEM_NEXT - add selected item to next pos in playlist
126 - #ACTION_SHOW_INFO - retrieve album info from the internet
127 - #ACTION_SELECT_ITEM - Item has been selected. Overwrite OnClick() to react on it
129 bool CGUIWindowMusicBase::OnMessage(CGUIMessage
& message
)
131 switch ( message
.GetMessage() )
133 case GUI_MSG_WINDOW_DEINIT
:
135 if (m_thumbLoader
.IsLoading())
136 m_thumbLoader
.StopThread();
137 m_musicdatabase
.Close();
141 case GUI_MSG_WINDOW_INIT
:
143 m_dlgProgress
= CServiceBroker::GetGUI()->GetWindowManager().GetWindow
<CGUIDialogProgress
>(WINDOW_DIALOG_PROGRESS
);
145 m_musicdatabase
.Open();
147 if (!CGUIMediaWindow::OnMessage(message
))
153 case GUI_MSG_DIRECTORY_SCANNED
:
155 CFileItem
directory(message
.GetStringParam(), true);
157 // Only update thumb on a local drive
158 if (directory
.IsHD())
160 std::string strParent
;
161 URIUtils::GetParentPath(directory
.GetPath(), strParent
);
162 if (directory
.GetPath() == m_vecItems
->GetPath() || strParent
== m_vecItems
->GetPath())
168 // update the display
169 case GUI_MSG_SCAN_FINISHED
:
170 case GUI_MSG_REFRESH_THUMBS
: // Never called as is secondary msg sent as GUI_MSG_NOTIFY_ALL
174 case GUI_MSG_CLICKED
:
176 int iControl
= message
.GetSenderId();
177 if (iControl
== CONTROL_BTNRIP
)
181 else if (iControl
== CONTROL_BTNPLAYLISTS
)
183 if (!m_vecItems
->IsPath("special://musicplaylists/"))
184 Update("special://musicplaylists/");
186 else if (iControl
== CONTROL_BTNSCAN
)
190 else if (m_viewControl
.HasControl(iControl
)) // list/thumb control
192 int iItem
= m_viewControl
.GetSelectedItem();
193 int iAction
= message
.GetParam1();
195 // iItem is checked for validity inside these routines
196 if (iAction
== ACTION_QUEUE_ITEM
|| iAction
== ACTION_MOUSE_MIDDLE_CLICK
)
200 else if (iAction
== ACTION_QUEUE_ITEM_NEXT
)
202 OnQueueItem(iItem
, true);
204 else if (iAction
== ACTION_SHOW_INFO
)
208 else if (iAction
== ACTION_DELETE_ITEM
)
210 // is delete allowed?
211 // must be at the playlists directory
212 if (m_vecItems
->IsPath("special://musicplaylists/"))
218 // use play button to add folders of items to temp playlist
219 else if (iAction
== ACTION_PLAYER_PLAY
)
221 const auto& components
= CServiceBroker::GetAppComponents();
222 const auto appPlayer
= components
.GetComponent
<CApplicationPlayer
>();
223 // if playback is paused or playback speed != 1, return
224 if (appPlayer
->IsPlayingAudio())
226 if (appPlayer
->IsPausedPlayback())
228 if (appPlayer
->GetPlaySpeed() != 1)
232 // not playing audio, or playback speed == 1
240 case GUI_MSG_NOTIFY_ALL
:
242 if (message
.GetParam1()==GUI_MSG_REMOVED_MEDIA
)
243 CUtil::DeleteDirectoryCache("r-");
247 return CGUIMediaWindow::OnMessage(message
);
250 bool CGUIWindowMusicBase::OnAction(const CAction
&action
)
252 if (action
.GetID() == ACTION_SHOW_PLAYLIST
)
254 if (CServiceBroker::GetPlaylistPlayer().GetCurrentPlaylist() == PLAYLIST::TYPE_MUSIC
||
255 CServiceBroker::GetPlaylistPlayer().GetPlaylist(PLAYLIST::TYPE_MUSIC
).size() > 0)
257 CServiceBroker::GetGUI()->GetWindowManager().ActivateWindow(WINDOW_MUSIC_PLAYLIST
);
262 if (action
.GetID() == ACTION_SCAN_ITEM
)
264 int item
= m_viewControl
.GetSelectedItem();
265 if (item
> -1 && m_vecItems
->Get(item
)->m_bIsFolder
)
271 return CGUIMediaWindow::OnAction(action
);
274 void CGUIWindowMusicBase::OnItemInfoAll(const std::string
& strPath
, bool refresh
)
276 if (StringUtils::EqualsNoCase(m_vecItems
->GetContent(), "albums"))
278 if (CMusicLibraryQueue::GetInstance().IsScanningLibrary())
281 CMusicLibraryQueue::GetInstance().StartAlbumScan(strPath
, refresh
);
283 else if (StringUtils::EqualsNoCase(m_vecItems
->GetContent(), "artists"))
285 if (CMusicLibraryQueue::GetInstance().IsScanningLibrary())
288 CMusicLibraryQueue::GetInstance().StartArtistScan(strPath
, refresh
);
292 void CGUIWindowMusicBase::OnItemInfo(int iItem
)
294 if ( iItem
< 0 || iItem
>= m_vecItems
->Size() )
297 CFileItemPtr item
= m_vecItems
->Get(iItem
);
299 // Match visibility test of CMusicInfo::IsVisible
300 if (item
->IsVideoDb() && item
->HasVideoInfoTag() &&
301 (item
->HasProperty("artist_musicid") || item
->HasProperty("album_musicid")))
303 // Music video artist or album (navigation by music > music video > artist))
304 CGUIDialogMusicInfo::ShowFor(item
.get());
308 if (item
->IsVideo() && item
->HasVideoInfoTag() &&
309 item
->GetVideoInfoTag()->m_type
== MediaTypeMusicVideo
)
310 { // Music video on a mixed current playlist or navigation by music > music video > artist > video
311 CGUIDialogVideoInfo::ShowFor(*item
);
315 if (!m_vecItems
->IsPlugin() && (item
->IsPlugin() || item
->IsScript()))
317 CGUIDialogAddonInfo::ShowForItem(item
);
321 // Match visibility test of CMusicInfo::IsVisible
322 if (item
->HasMusicInfoTag() && (item
->GetMusicInfoTag()->GetType() == MediaTypeSong
||
323 item
->GetMusicInfoTag()->GetType() == MediaTypeAlbum
||
324 item
->GetMusicInfoTag()->GetType() == MediaTypeArtist
))
325 CGUIDialogMusicInfo::ShowFor(item
.get());
328 void CGUIWindowMusicBase::RefreshContent(const std::string
& strContent
)
330 if ( CServiceBroker::GetGUI()->GetWindowManager().GetActiveWindow() == WINDOW_MUSIC_NAV
&&
331 m_vecItems
->GetContent() == strContent
&&
332 m_vecItems
->GetSortMethod() == SortByUserRating
)
333 // When music library window is active and showing songs or albums sorted
334 // by userrating refresh the list to resort items and show new userrating
338 /// \brief Retrieve tag information for \e m_vecItems
339 void CGUIWindowMusicBase::RetrieveMusicInfo()
341 auto start
= std::chrono::steady_clock::now();
343 OnRetrieveMusicInfo(*m_vecItems
);
345 //! @todo Scan for multitrack items here...
346 std::vector
<std::string
> itemsForRemove
;
347 CFileItemList itemsForAdd
;
348 for (int i
= 0; i
< m_vecItems
->Size(); ++i
)
350 CFileItemPtr pItem
= (*m_vecItems
)[i
];
351 if (pItem
->m_bIsFolder
|| pItem
->IsPlayList() || pItem
->IsPicture() || pItem
->IsLyrics() || pItem
->IsVideo())
354 CMusicInfoTag
& tag
= *pItem
->GetMusicInfoTag();
355 if (tag
.Loaded() && !tag
.GetCueSheet().empty())
356 pItem
->LoadEmbeddedCue();
358 if (pItem
->HasCueDocument()
359 && pItem
->LoadTracksFromCueDocument(itemsForAdd
))
361 itemsForRemove
.push_back(pItem
->GetPath());
364 for (size_t i
= 0; i
< itemsForRemove
.size(); ++i
)
366 for (int j
= 0; j
< m_vecItems
->Size(); ++j
)
368 if ((*m_vecItems
)[j
]->GetPath() == itemsForRemove
[i
])
370 m_vecItems
->Remove(j
);
375 m_vecItems
->Append(itemsForAdd
);
377 auto end
= std::chrono::steady_clock::now();
378 auto duration
= std::chrono::duration_cast
<std::chrono::milliseconds
>(end
- start
);
380 CLog::Log(LOGDEBUG
, "RetrieveMusicInfo() took {} ms", duration
.count());
383 /// \brief Add selected list/thumb control item to playlist and start playing
384 /// \param iItem Selected Item in list/thumb control
385 void CGUIWindowMusicBase::OnQueueItem(int iItem
, bool first
)
387 // don't re-queue items from playlist window
388 if (iItem
< 0 || iItem
>= m_vecItems
->Size() || GetID() == WINDOW_MUSIC_PLAYLIST
)
391 // add item 2 playlist
392 const auto item
= m_vecItems
->Get(iItem
);
394 if (item
->IsRAR() || item
->IsZIP())
397 MUSIC_UTILS::QueueItem(item
, first
? MUSIC_UTILS::QueuePosition::POSITION_BEGIN
398 : MUSIC_UTILS::QueuePosition::POSITION_END
);
401 m_viewControl
.SetSelectedItem(iItem
+ 1);
404 void CGUIWindowMusicBase::UpdateButtons()
406 CONTROL_ENABLE_ON_CONDITION(CONTROL_BTNRIP
, CServiceBroker::GetMediaManager().IsAudio());
408 CONTROL_ENABLE_ON_CONDITION(CONTROL_BTNSCAN
,
409 !(m_vecItems
->IsVirtualDirectoryRoot() ||
410 m_vecItems
->IsMusicDb()));
412 if (CMusicLibraryQueue::GetInstance().IsScanningLibrary())
413 SET_CONTROL_LABEL(CONTROL_BTNSCAN
, 14056); // Stop Scan
415 SET_CONTROL_LABEL(CONTROL_BTNSCAN
, 102); // Scan
417 CGUIMediaWindow::UpdateButtons();
420 void CGUIWindowMusicBase::GetContextButtons(int itemNumber
, CContextButtons
&buttons
)
423 if (itemNumber
>= 0 && itemNumber
< m_vecItems
->Size())
424 item
= m_vecItems
->Get(itemNumber
);
428 const std::shared_ptr
<CProfileManager
> profileManager
= CServiceBroker::GetSettingsComponent()->GetProfileManager();
430 // Check for the partymode playlist item.
431 // When "PartyMode.xsp" not exist, only context menu button is edit
432 if (item
->IsSmartPlayList() &&
433 (item
->GetPath() == profileManager
->GetUserDataItem("PartyMode.xsp")) &&
434 !CFileUtils::Exists(item
->GetPath()))
436 buttons
.Add(CONTEXT_BUTTON_EDIT_SMART_PLAYLIST
, 586);
440 if (!item
->IsParentFolder())
442 //! @todo get rid of IsAddonsPath and IsScript check. CanQueue should be enough!
443 if (item
->CanQueue() && !item
->IsAddonsPath() && !item
->IsScript())
445 if (!item
->m_bIsFolder
&&
446 (!item
->IsPlayList() ||
447 CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->m_playlistAsFolders
))
449 const CPlayerCoreFactory
& playerCoreFactory
= CServiceBroker::GetPlayerCoreFactory();
451 // check what players we have, if we have multiple display play with option
452 std::vector
<std::string
> players
;
453 playerCoreFactory
.GetPlayers(*item
, players
);
454 if (players
.size() >= 1)
455 buttons
.Add(CONTEXT_BUTTON_PLAY_WITH
, 15213); // Play With...
458 if (item
->IsSmartPlayList())
459 buttons
.Add(CONTEXT_BUTTON_PLAY_PARTYMODE
, 15216); // Play in Partymode
461 if (item
->IsSmartPlayList() || m_vecItems
->IsSmartPlayList())
462 buttons
.Add(CONTEXT_BUTTON_EDIT_SMART_PLAYLIST
, 586);
463 else if (item
->IsPlayList() || m_vecItems
->IsPlayList())
464 buttons
.Add(CONTEXT_BUTTON_EDIT
, 586);
467 // enable Rip CD Audio or Track button if we have an audio disc
468 if (CServiceBroker::GetMediaManager().IsDiscInDrive() && m_vecItems
->IsCDDA())
470 // those cds can also include Audio Tracks: CDExtra and MixedMode!
471 MEDIA_DETECT::CCdInfo
* pCdInfo
= CServiceBroker::GetMediaManager().GetCdInfo();
472 if (pCdInfo
->IsAudio(1) || pCdInfo
->IsCDExtra(1) || pCdInfo
->IsMixedMode(1))
473 buttons
.Add(CONTEXT_BUTTON_RIP_TRACK
, 610);
478 // enable CDDB lookup if the current dir is CDDA
479 if (CServiceBroker::GetMediaManager().IsDiscInDrive() && m_vecItems
->IsCDDA() &&
480 (profileManager
->GetCurrentProfile().canWriteDatabases() || g_passwordManager
.bMasterUser
))
482 buttons
.Add(CONTEXT_BUTTON_CDDB
, 16002);
485 CGUIMediaWindow::GetContextButtons(itemNumber
, buttons
);
488 void CGUIWindowMusicBase::GetNonContextButtons(CContextButtons
&buttons
)
492 bool CGUIWindowMusicBase::OnContextButton(int itemNumber
, CONTEXT_BUTTON button
)
495 if (itemNumber
>= 0 && itemNumber
< m_vecItems
->Size())
496 item
= m_vecItems
->Get(itemNumber
);
498 if (CGUIDialogContextMenu::OnContextButton("music", item
, button
))
500 if (button
== CONTEXT_BUTTON_REMOVE_SOURCE
)
501 OnRemoveSource(itemNumber
);
503 Update(m_vecItems
->GetPath());
509 case CONTEXT_BUTTON_INFO
:
510 OnItemInfo(itemNumber
);
513 case CONTEXT_BUTTON_EDIT
:
515 std::string playlist
= item
->IsPlayList() ? item
->GetPath() : m_vecItems
->GetPath(); // save path as activatewindow will destroy our items
516 CServiceBroker::GetGUI()->GetWindowManager().ActivateWindow(WINDOW_MUSIC_PLAYLIST_EDITOR
, playlist
);
518 m_vecItems
->RemoveDiscCache(GetID());
522 case CONTEXT_BUTTON_EDIT_SMART_PLAYLIST
:
524 std::string playlist
= item
->IsSmartPlayList() ? item
->GetPath() : m_vecItems
->GetPath(); // save path as activatewindow will destroy our items
525 if (CGUIDialogSmartPlaylistEditor::EditPlaylist(playlist
, "music"))
526 Refresh(true); // need to update
530 case CONTEXT_BUTTON_PLAY_WITH
:
532 const CPlayerCoreFactory
&playerCoreFactory
= CServiceBroker::GetPlayerCoreFactory();
534 std::vector
<std::string
> players
;
535 playerCoreFactory
.GetPlayers(*item
, players
);
536 std::string player
= playerCoreFactory
.SelectPlayerDialog(players
);
538 OnClick(itemNumber
, player
);
542 case CONTEXT_BUTTON_PLAY_PARTYMODE
:
543 g_partyModeManager
.Enable(PARTYMODECONTEXT_MUSIC
, item
->GetPath());
546 case CONTEXT_BUTTON_RIP_CD
:
550 #ifdef HAS_CDDA_RIPPER
551 case CONTEXT_BUTTON_CANCEL_RIP_CD
:
552 KODI::CDRIP::CCDDARipper::GetInstance().CancelJobs();
556 case CONTEXT_BUTTON_RIP_TRACK
:
557 OnRipTrack(itemNumber
);
560 case CONTEXT_BUTTON_SCAN
:
561 // Check if scanning already and inform user
562 if (CMusicLibraryQueue::GetInstance().IsScanningLibrary())
563 HELPERS::ShowOKDialogText(CVariant
{ 189 }, CVariant
{ 14057 });
565 OnScan(itemNumber
, true);
568 case CONTEXT_BUTTON_CDDB
:
569 if (m_musicdatabase
.LookupCDDBInfo(true))
577 return CGUIMediaWindow::OnContextButton(itemNumber
, button
);
580 bool CGUIWindowMusicBase::OnAddMediaSource()
582 return CGUIDialogMediaSource::ShowAndAddMediaSource("music");
585 void CGUIWindowMusicBase::OnRipCD()
587 if (CServiceBroker::GetMediaManager().IsAudio())
589 if (!g_application
.CurrentFileItem().IsCDDA())
591 #ifdef HAS_CDDA_RIPPER
592 KODI::CDRIP::CCDDARipper::GetInstance().RipCD();
596 HELPERS::ShowOKDialogText(CVariant
{257}, CVariant
{20099});
600 void CGUIWindowMusicBase::OnRipTrack(int iItem
)
602 if (CServiceBroker::GetMediaManager().IsAudio())
604 if (!g_application
.CurrentFileItem().IsCDDA())
606 #ifdef HAS_CDDA_RIPPER
607 CFileItemPtr item
= m_vecItems
->Get(iItem
);
608 KODI::CDRIP::CCDDARipper::GetInstance().RipTrack(item
.get());
612 HELPERS::ShowOKDialogText(CVariant
{257}, CVariant
{20099});
616 void CGUIWindowMusicBase::PlayItem(int iItem
)
618 // restrictions should be placed in the appropriate window code
619 // only call the base code if the item passes since this clears
620 // the current playlist
622 const CFileItemPtr pItem
= m_vecItems
->Get(iItem
);
626 MEDIA_DETECT::CAutorun::PlayDiscAskResume(pItem
->GetPath());
631 // Check for the partymode playlist item, do nothing when "PartyMode.xsp" not exist
632 if (pItem
->IsSmartPlayList())
634 const std::shared_ptr
<CProfileManager
> profileManager
=
635 CServiceBroker::GetSettingsComponent()->GetProfileManager();
636 if ((pItem
->GetPath() == profileManager
->GetUserDataItem("PartyMode.xsp")) &&
637 !CFileUtils::Exists(pItem
->GetPath()))
641 // if its a folder, build a playlist
642 if (pItem
->m_bIsFolder
&& !pItem
->IsPlugin())
644 // make a copy so that we can alter the queue state
645 CFileItemPtr
item(new CFileItem(*m_vecItems
->Get(iItem
)));
647 // Allow queuing of unqueueable items
648 // when we try to queue them directly
649 if (!item
->CanQueue())
650 item
->SetCanQueue(true);
653 if (item
->IsParentFolder())
656 CFileItemList queuedItems
;
657 MUSIC_UTILS::GetItemsForPlayList(item
, queuedItems
);
658 if (g_partyModeManager
.IsEnabled())
660 g_partyModeManager
.AddUserSongs(queuedItems
, true);
665 std::string strPlayListDirectory = m_vecItems->GetPath();
666 URIUtils::RemoveSlashAtEnd(strPlayListDirectory);
669 CServiceBroker::GetPlaylistPlayer().ClearPlaylist(PLAYLIST::TYPE_MUSIC
);
670 CServiceBroker::GetPlaylistPlayer().Reset();
671 CServiceBroker::GetPlaylistPlayer().Add(PLAYLIST::TYPE_MUSIC
, queuedItems
);
672 CServiceBroker::GetPlaylistPlayer().SetCurrentPlaylist(PLAYLIST::TYPE_MUSIC
);
675 CServiceBroker::GetPlaylistPlayer().Play();
677 else if (pItem
->IsPlayList())
679 // load the playlist the old way
680 LoadPlayList(pItem
->GetPath());
684 // just a single item, play it
685 //! @todo Add music-specific code for single playback of an item here (See OnClick in MediaWindow, and OnPlayMedia below)
690 void CGUIWindowMusicBase::LoadPlayList(const std::string
& strPlayList
)
692 // if partymode is active, we disable it
693 if (g_partyModeManager
.IsEnabled())
694 g_partyModeManager
.Disable();
696 // load a playlist like .m3u, .pls
697 // first get correct factory to load playlist
698 std::unique_ptr
<PLAYLIST::CPlayList
> pPlayList(PLAYLIST::CPlayListFactory::Create(strPlayList
));
702 if (!pPlayList
->Load(strPlayList
))
704 HELPERS::ShowOKDialogText(CVariant
{6}, CVariant
{477});
705 return; //hmmm unable to load playlist?
709 int iSize
= pPlayList
->size();
710 if (g_application
.ProcessAndStartPlaylist(strPlayList
, *pPlayList
, PLAYLIST::TYPE_MUSIC
))
713 m_guiState
->SetPlaylistDirectory("playlistmusic://");
714 // activate the playlist window if its not activated yet
715 if (GetID() == CServiceBroker::GetGUI()->GetWindowManager().GetActiveWindow() && iSize
> 1)
717 CServiceBroker::GetGUI()->GetWindowManager().ActivateWindow(WINDOW_MUSIC_PLAYLIST
);
722 bool CGUIWindowMusicBase::OnPlayMedia(int iItem
, const std::string
&player
)
724 CFileItemPtr pItem
= m_vecItems
->Get(iItem
);
727 if (g_partyModeManager
.IsEnabled())
729 PLAYLIST::CPlayList playlistTemp
;
730 playlistTemp
.Add(pItem
);
731 g_partyModeManager
.AddUserSongs(playlistTemp
, !CServiceBroker::GetSettingsComponent()->GetSettings()->GetBool(CSettings::SETTING_MUSICPLAYER_QUEUEBYDEFAULT
));
734 else if (!pItem
->IsPlayList() && !pItem
->IsInternetStream())
735 { // single music file - if we get here then we have autoplaynextitem turned off or queuebydefault
736 // turned on, but we still want to use the playlist player in order to handle more queued items
738 if ( (CServiceBroker::GetSettingsComponent()->GetSettings()->GetBool(CSettings::SETTING_MUSICPLAYER_QUEUEBYDEFAULT
) && CServiceBroker::GetGUI()->GetWindowManager().GetActiveWindow() != WINDOW_MUSIC_PLAYLIST_EDITOR
) )
740 //! @todo Should the playlist be cleared if nothing is already playing?
744 pItem
->SetProperty("playlist_type_hint", m_guiState
->GetPlaylist());
745 CServiceBroker::GetPlaylistPlayer().Play(pItem
, player
);
748 return CGUIMediaWindow::OnPlayMedia(iItem
, player
);
751 /// \brief Can be overwritten to implement an own tag filling function.
752 /// \param items File items to fill
753 void CGUIWindowMusicBase::OnRetrieveMusicInfo(CFileItemList
& items
)
755 // No need to attempt to read music file tags for music videos
756 if (items
.IsVideoDb())
758 if (items
.GetFolderCount()==items
.Size() || items
.IsMusicDb() ||
759 (!CServiceBroker::GetSettingsComponent()->GetSettings()->GetBool(CSettings::SETTING_MUSICFILES_USETAGS
) && !items
.IsCDDA()))
763 // Start the music info loader thread
764 m_musicInfoLoader
.SetProgressCallback(m_dlgProgress
);
765 m_musicInfoLoader
.Load(items
);
767 bool bShowProgress
= !CServiceBroker::GetGUI()->GetWindowManager().HasModalDialog(true);
768 bool bProgressVisible
= false;
770 auto start
= std::chrono::steady_clock::now();
772 while (m_musicInfoLoader
.IsLoading())
775 { // Do we have to init a progress dialog?
776 auto end
= std::chrono::steady_clock::now();
777 auto duration
= std::chrono::duration_cast
<std::chrono::milliseconds
>(end
- start
);
779 if (!bProgressVisible
&& duration
.count() > 1500 && m_dlgProgress
)
780 { // tag loading takes more then 1.5 secs, show a progress dialog
781 CURL
url(items
.GetPath());
782 m_dlgProgress
->SetHeading(CVariant
{189});
783 m_dlgProgress
->SetLine(0, CVariant
{505});
784 m_dlgProgress
->SetLine(1, CVariant
{""});
785 m_dlgProgress
->SetLine(2, CVariant
{url
.GetWithoutUserDetails()});
786 m_dlgProgress
->Open();
787 m_dlgProgress
->ShowProgressBar(true);
788 bProgressVisible
= true;
791 if (bProgressVisible
&& m_dlgProgress
&& !m_dlgProgress
->IsCanceled())
793 m_dlgProgress
->Progress();
795 } // if (bShowProgress)
796 KODI::TIME::Sleep(1ms
);
797 } // while (m_musicInfoLoader.IsLoading())
799 if (bProgressVisible
&& m_dlgProgress
)
800 m_dlgProgress
->Close();
803 bool CGUIWindowMusicBase::GetDirectory(const std::string
&strDirectory
, CFileItemList
&items
)
806 bool bResult
= CGUIMediaWindow::GetDirectory(strDirectory
, items
);
809 // We want to expand disc images when browsing in file view but not on library, smartplaylist
810 // or node menu music windows
811 if (!items
.GetPath().empty() && !StringUtils::StartsWithNoCase(items
.GetPath(), "musicdb://") &&
812 !StringUtils::StartsWithNoCase(items
.GetPath(), "special://") &&
813 !StringUtils::StartsWithNoCase(items
.GetPath(), "library://"))
814 CDirectory::FilterFileDirectories(items
, ".iso", true);
816 CMusicThumbLoader loader
;
817 loader
.FillThumb(items
);
820 CDirectoryNode::GetDatabaseInfo(items
.GetPath(), params
);
822 // Get art for directory when album or artist
823 bool artfound
= false;
824 std::vector
<ArtForThumbLoader
> art
;
825 if (params
.GetAlbumId() > 0)
826 { // Get album and related artist(s) art
827 artfound
= m_musicdatabase
.GetArtForItem(-1, params
.GetAlbumId(), -1, false, art
);
829 else if (params
.GetArtistId() > 0)
831 artfound
= m_musicdatabase
.GetArtForItem(-1, -1, params
.GetArtistId(), true, art
);
835 std::string dirType
= MediaTypeArtist
;
836 if (params
.GetAlbumId() > 0)
837 dirType
= MediaTypeAlbum
;
838 std::map
<std::string
, std::string
> artmap
;
839 for (auto artitem
: art
)
842 if (dirType
== artitem
.mediaType
)
843 artname
= artitem
.artType
;
844 else if (artitem
.prefix
.empty())
845 artname
= artitem
.mediaType
+ "." + artitem
.artType
;
848 if (dirType
== MediaTypeAlbum
)
849 StringUtils::Replace(artitem
.prefix
, "albumartist", "artist");
850 artname
= artitem
.prefix
+ "." + artitem
.artType
;
852 artmap
.insert(std::make_pair(artname
, artitem
.url
));
854 items
.SetArt(artmap
);
857 int iWindow
= GetID();
858 // Add "New Playlist" items when in the playlists folder, except on playlist editor screen
859 if ((iWindow
!= WINDOW_MUSIC_PLAYLIST_EDITOR
) &&
860 (items
.GetPath() == "special://musicplaylists/") && !items
.Contains("newplaylist://"))
862 const std::shared_ptr
<CProfileManager
> profileManager
= CServiceBroker::GetSettingsComponent()->GetProfileManager();
864 CFileItemPtr
newPlaylist(new CFileItem(profileManager
->GetUserDataItem("PartyMode.xsp"),false));
865 newPlaylist
->SetLabel(g_localizeStrings
.Get(16035));
866 newPlaylist
->SetLabelPreformatted(true);
867 newPlaylist
->SetArt("icon", "DefaultPartyMode.png");
868 newPlaylist
->m_bIsFolder
= true;
869 items
.Add(newPlaylist
);
871 newPlaylist
.reset(new CFileItem("newplaylist://", false));
872 newPlaylist
->SetLabel(g_localizeStrings
.Get(525));
873 newPlaylist
->SetArt("icon", "DefaultAddSource.png");
874 newPlaylist
->SetLabelPreformatted(true);
875 newPlaylist
->SetSpecialSort(SortSpecialOnBottom
);
876 newPlaylist
->SetCanQueue(false);
877 items
.Add(newPlaylist
);
879 newPlaylist
.reset(new CFileItem("newsmartplaylist://music", false));
880 newPlaylist
->SetLabel(g_localizeStrings
.Get(21437));
881 newPlaylist
->SetArt("icon", "DefaultAddSource.png");
882 newPlaylist
->SetLabelPreformatted(true);
883 newPlaylist
->SetSpecialSort(SortSpecialOnBottom
);
884 newPlaylist
->SetCanQueue(false);
885 items
.Add(newPlaylist
);
888 // check for .CUE files here.
889 items
.FilterCueItems();
892 if (items
.GetLabel().empty() && m_rootDir
.IsSource(items
.GetPath(), CMediaSourceSettings::GetInstance().GetSources("music"), &label
))
893 items
.SetLabel(label
);
899 bool CGUIWindowMusicBase::CheckFilterAdvanced(CFileItemList
&items
) const
901 const std::string
& content
= items
.GetContent();
902 if ((items
.IsMusicDb() || CanContainFilter(m_strFilterPath
)) &&
903 (StringUtils::EqualsNoCase(content
, "artists") ||
904 StringUtils::EqualsNoCase(content
, "albums") ||
905 StringUtils::EqualsNoCase(content
, "songs")))
911 bool CGUIWindowMusicBase::CanContainFilter(const std::string
&strDirectory
) const
913 return URIUtils::IsProtocol(strDirectory
, "musicdb");
916 bool CGUIWindowMusicBase::OnSelect(int iItem
)
918 auto item
= m_vecItems
->Get(iItem
);
919 if (item
->IsAudioBook())
922 if (m_musicdatabase
.GetResumeBookmarkForAudioBook(*item
, bookmark
) && bookmark
> 0)
924 // find which chapter the bookmark belongs to
926 std::find_if(m_vecItems
->cbegin(), m_vecItems
->cend(),
927 [&](const CFileItemPtr
& item
) { return bookmark
< item
->GetEndOffset(); });
929 if (itemIt
!= m_vecItems
->cend())
931 // ask the user if they want to play or resume
932 CContextButtons choices
;
933 choices
.Add(MUSIC_SELECT_ACTION_PLAY
, 208); // 208 = Play
934 choices
.Add(MUSIC_SELECT_ACTION_RESUME
,
935 StringUtils::Format(g_localizeStrings
.Get(12022), // 12022 = Resume from ...
936 (*itemIt
)->GetMusicInfoTag()->GetTitle()));
938 auto choice
= CGUIDialogContextMenu::Show(choices
);
939 if (choice
== MUSIC_SELECT_ACTION_RESUME
)
941 (*itemIt
)->SetProperty("audiobook_bookmark", bookmark
);
942 return CGUIMediaWindow::OnSelect(static_cast<int>(itemIt
- m_vecItems
->cbegin()));
950 return CGUIMediaWindow::OnSelect(iItem
);
953 void CGUIWindowMusicBase::OnInitWindow()
955 CGUIMediaWindow::OnInitWindow();
956 // Prompt for rescan of library to read music file tags that were not processed by previous versions
957 // and accommodate any changes to the way some tags are processed
958 if (m_musicdatabase
.GetMusicNeedsTagScan() != 0)
960 if (CServiceBroker::GetGUI()
963 .GetLibraryInfoProvider()
964 .GetLibraryBool(LIBRARY_HAS_MUSIC
) &&
965 !CMusicLibraryQueue::GetInstance().IsScanningLibrary())
967 // rescan of music library required
968 if (CGUIDialogYesNo::ShowAndGetInput(CVariant
{799}, CVariant
{38060}))
970 int flags
= CMusicInfoScanner::SCAN_RESCAN
;
971 // When set to fetch information on update enquire about scraping that as well
972 // It may take some time, so the user may want to do it later by "Query Info For All"
973 if (CServiceBroker::GetSettingsComponent()->GetSettings()->GetBool(CSettings::SETTING_MUSICLIBRARY_DOWNLOADINFO
))
974 if (CGUIDialogYesNo::ShowAndGetInput(CVariant
{799}, CVariant
{38061}))
975 flags
|= CMusicInfoScanner::SCAN_ONLINE
;
977 CMusicLibraryQueue::GetInstance().ScanLibrary("", flags
, true);
979 m_musicdatabase
.SetMusicTagScanVersion(); // once is enough (user may interrupt, but that's up to them)
984 // no need to force a rescan if there's no music in the library or if a library scan is already active
985 m_musicdatabase
.SetMusicTagScanVersion();
990 std::string
CGUIWindowMusicBase::GetStartFolder(const std::string
&dir
)
992 std::string
lower(dir
); StringUtils::ToLower(lower
);
993 if (lower
== "plugins" || lower
== "addons")
994 return "addons://sources/audio/";
995 else if (lower
== "$playlists" || lower
== "playlists")
996 return "special://musicplaylists/";
997 return CGUIMediaWindow::GetStartFolder(dir
);
1000 void CGUIWindowMusicBase::OnScan(int iItem
, bool bPromptRescan
/*= false*/)
1002 std::string strPath
;
1003 if (iItem
< 0 || iItem
>= m_vecItems
->Size())
1004 strPath
= m_vecItems
->GetPath();
1005 else if (m_vecItems
->Get(iItem
)->m_bIsFolder
)
1006 strPath
= m_vecItems
->Get(iItem
)->GetPath();
1008 { //! @todo MUSICDB - should we allow scanning a single item into the database?
1009 //! This will require changes to the info scanner, which assumes we're running on a folder
1010 strPath
= m_vecItems
->GetPath();
1012 // Ask for full rescan of music files when scan item from file view context menu
1013 bool doRescan
= false;
1015 doRescan
= CGUIDialogYesNo::ShowAndGetInput(CVariant
{ 799 }, CVariant
{ 38062 });
1017 DoScan(strPath
, doRescan
);
1020 void CGUIWindowMusicBase::DoScan(const std::string
&strPath
, bool bRescan
/*= false*/)
1022 if (CMusicLibraryQueue::GetInstance().IsScanningLibrary())
1024 CMusicLibraryQueue::GetInstance().StopLibraryScanning();
1028 // Start background loader
1029 int iControl
=GetFocusedControlID();
1032 flags
= CMusicInfoScanner::SCAN_RESCAN
;
1033 if (CServiceBroker::GetSettingsComponent()->GetSettings()->GetBool(CSettings::SETTING_MUSICLIBRARY_DOWNLOADINFO
))
1034 flags
|= CMusicInfoScanner::SCAN_ONLINE
;
1036 CMusicLibraryQueue::GetInstance().ScanLibrary(strPath
, flags
, true);
1038 SET_CONTROL_FOCUS(iControl
, 0);
1042 void CGUIWindowMusicBase::OnRemoveSource(int iItem
)
1045 //Remove music source from library, even when leaving songs
1046 CMusicDatabase database
;
1048 database
.RemoveSource(m_vecItems
->Get(iItem
)->GetLabel());
1051 if (CGUIDialogYesNo::ShowAndGetInput(CVariant
{522}, CVariant
{20340}, bCanceled
, CVariant
{""}, CVariant
{""}, CGUIDialogYesNo::NO_TIMEOUT
))
1054 database
.RemoveSongsFromPath(m_vecItems
->Get(iItem
)->GetPath(), songs
, false);
1055 database
.CleanupOrphanedItems();
1056 database
.CheckArtistLinksChanged();
1057 CServiceBroker::GetGUI()->GetInfoManager().GetInfoProviders().GetLibraryInfoProvider().ResetLibraryBools();
1058 m_vecItems
->RemoveDiscCache(GetID());
1063 void CGUIWindowMusicBase::OnPrepareFileItems(CFileItemList
&items
)
1065 CGUIMediaWindow::OnPrepareFileItems(items
);
1067 if (!items
.IsMusicDb() && !items
.IsSmartPlayList())
1068 RetrieveMusicInfo();
1071 void CGUIWindowMusicBase::OnAssignContent(const std::string
& oldName
, const CMediaSource
& source
)
1073 // Music scrapers are not source specific, so unlike video there is no content selection logic here.
1074 // Called on having added or edited a music source, this starts scanning items into library when required
1076 //! @todo: do async as updating sources for all albums could be slow??
1077 //Store music source in the music library, even those not scanned
1078 CMusicDatabase database
;
1080 database
.UpdateSource(oldName
, source
.strName
, source
.strPath
, source
.vecPaths
);
1083 // "Add to library" yes/no dialog with additional "settings" custom button
1084 // "Do you want to add the media from this source to your library?"
1085 DialogResponse rep
= DialogResponse::CHOICE_CUSTOM
;
1086 while (rep
== DialogResponse::CHOICE_CUSTOM
)
1088 rep
= HELPERS::ShowYesNoCustomDialog(CVariant
{20444}, CVariant
{20447}, CVariant
{106}, CVariant
{107}, CVariant
{10004});
1089 if (rep
== DialogResponse::CHOICE_CUSTOM
)
1090 // Edit default info provider settings so can be applied during scan
1091 CGUIDialogInfoProviderSettings::Show();
1093 if (rep
== DialogResponse::CHOICE_YES
)
1094 CMusicLibraryQueue::GetInstance().ScanLibrary(source
.strPath
,
1095 MUSIC_INFO::CMusicInfoScanner::SCAN_NORMAL
, true);