2 * Copyright (C) 2005-2013 Team XBMC
5 * This Program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2, or (at your option)
10 * This Program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with XBMC; see the file COPYING. If not, see
17 * <http://www.gnu.org/licenses/>.
21 #include "threads/SystemClock.h"
23 #include "GUIUserMessages.h"
24 #include "GUIWindowMusicBase.h"
25 #include "music/dialogs/GUIDialogMusicInfo.h"
26 #include "filesystem/ZipManager.h"
27 #ifdef HAS_FILESYSTEM_DAAP
28 #include "filesystem/DAAPDirectory.h"
30 #include "playlists/PlayListFactory.h"
32 #include "playlists/PlayListM3U.h"
33 #include "Application.h"
34 #include "PlayListPlayer.h"
35 #include "filesystem/DirectoryCache.h"
36 #ifdef HAS_CDDA_RIPPER
37 #include "cdrip/CDDARipper.h"
39 #include "GUIPassword.h"
40 #include "dialogs/GUIDialogMediaSource.h"
41 #include "PartyModeManager.h"
42 #include "GUIInfoManager.h"
43 #include "filesystem/MusicDatabaseDirectory.h"
44 #include "music/dialogs/GUIDialogSongInfo.h"
45 #include "addons/GUIDialogAddonInfo.h"
46 #include "dialogs/GUIDialogSmartPlaylistEditor.h"
47 #include "music/tags/MusicInfoTag.h"
48 #include "guilib/GUIWindowManager.h"
49 #include "guilib/Key.h"
50 #include "dialogs/GUIDialogOK.h"
51 #include "dialogs/GUIDialogYesNo.h"
52 #include "guilib/GUIKeyboardFactory.h"
53 #include "dialogs/GUIDialogProgress.h"
55 #include "filesystem/File.h"
56 #include "profiles/ProfilesManager.h"
57 #include "storage/MediaManager.h"
58 #include "settings/AdvancedSettings.h"
59 #include "settings/MediaSettings.h"
60 #include "settings/Settings.h"
61 #include "guilib/LocalizeStrings.h"
62 #include "utils/TimeUtils.h"
63 #include "utils/log.h"
64 #include "utils/URIUtils.h"
65 #include "video/VideoInfoTag.h"
66 #include "utils/StringUtils.h"
68 #include "music/infoscanner/MusicInfoScanner.h"
69 #include "cores/IPlayer.h"
72 using namespace XFILE;
73 using namespace MUSICDATABASEDIRECTORY;
74 using namespace PLAYLIST;
75 using namespace MUSIC_GRABBER;
76 using namespace MUSIC_INFO;
78 #define CONTROL_BTNVIEWASICONS 2
79 #define CONTROL_BTNSORTBY 3
80 #define CONTROL_BTNSORTASC 4
81 #define CONTROL_BTNTYPE 5
83 CGUIWindowMusicBase::CGUIWindowMusicBase(int id, const CStdString &xmlFile)
84 : CGUIMediaWindow(id, xmlFile)
89 CGUIWindowMusicBase::~CGUIWindowMusicBase ()
93 bool CGUIWindowMusicBase::OnBack(int actionID)
95 if (!g_application.IsMusicScanning())
97 CUtil::RemoveTempFiles();
99 return CGUIMediaWindow::OnBack(actionID);
103 \brief Handle messages on window.
104 \param message GUI Message that can be reacted on.
105 \return if a message can't be processed, return \e false
107 On these messages this class reacts.\n
109 - #GUI_MSG_WINDOW_DEINIT\n
110 ...the last focused control is saved to m_iLastControl.
111 - #GUI_MSG_WINDOW_INIT\n
112 ...the musicdatabase is opend and the music extensions and shares are set.
113 The last focused control is set.
115 ... the base class reacts on the following controls:\n
117 - #CONTROL_BTNVIEWASICONS - switch between list, thumb and with large items
118 - #CONTROL_BTNTYPE - switch between music windows
119 - #CONTROL_BTNSEARCH - Search for items\n
121 - The container controls\n
122 Have the following actions in message them clicking on them.
123 - #ACTION_QUEUE_ITEM - add selected item to playlist
124 - #ACTION_SHOW_INFO - retrieve album info from the internet
125 - #ACTION_SELECT_ITEM - Item has been selected. Overwrite OnClick() to react on it
127 bool CGUIWindowMusicBase::OnMessage(CGUIMessage& message)
129 switch ( message.GetMessage() )
131 case GUI_MSG_WINDOW_DEINIT:
133 m_musicdatabase.Close();
137 case GUI_MSG_WINDOW_INIT:
139 m_dlgProgress = (CGUIDialogProgress*)g_windowManager.GetWindow(WINDOW_DIALOG_PROGRESS);
141 m_musicdatabase.Open();
143 if (!CGUIMediaWindow::OnMessage(message))
146 // save current window, unless the current window is the music playlist window
147 if (GetID() != WINDOW_MUSIC_PLAYLIST &&
148 CSettings::Get().GetInt("mymusic.startwindow") != GetID())
150 CSettings::Get().SetInt("mymusic.startwindow", GetID());
151 CSettings::Get().Save();
158 // update the display
159 case GUI_MSG_SCAN_FINISHED:
160 case GUI_MSG_REFRESH_THUMBS:
164 case GUI_MSG_CLICKED:
166 int iControl = message.GetSenderId();
167 if (iControl == CONTROL_BTNTYPE)
169 CGUIMessage msg(GUI_MSG_ITEM_SELECTED, GetID(), CONTROL_BTNTYPE);
170 g_windowManager.SendMessage(msg);
172 int nWindow = WINDOW_MUSIC_FILES + msg.GetParam1();
174 if (nWindow == GetID())
177 CSettings::Get().SetInt("mymusic.startwindow", nWindow);
178 CSettings::Get().Save();
179 g_windowManager.ChangeActiveWindow(nWindow);
181 CGUIMessage msg2(GUI_MSG_SETFOCUS, CSettings::Get().GetInt("mymusic.startwindow"), CONTROL_BTNTYPE);
182 g_windowManager.SendMessage(msg2);
186 else if (m_viewControl.HasControl(iControl)) // list/thumb control
188 int iItem = m_viewControl.GetSelectedItem();
189 int iAction = message.GetParam1();
191 // iItem is checked for validity inside these routines
192 if (iAction == ACTION_QUEUE_ITEM || iAction == ACTION_MOUSE_MIDDLE_CLICK)
196 else if (iAction == ACTION_SHOW_INFO)
200 else if (iAction == ACTION_DELETE_ITEM)
202 // is delete allowed?
203 // must be at the playlists directory
204 if (m_vecItems->GetPath().Equals("special://musicplaylists/"))
207 // or be at the files window and have file deletion enabled
208 else if (GetID() == WINDOW_MUSIC_FILES &&
209 CSettings::Get().GetBool("filelists.allowfiledeletion"))
217 // use play button to add folders of items to temp playlist
218 else if (iAction == ACTION_PLAYER_PLAY)
220 // if playback is paused or playback speed != 1, return
221 if (g_application.m_pPlayer->IsPlayingAudio())
223 if (g_application.m_pPlayer->IsPausedPlayback())
225 if (g_application.m_pPlayer->GetPlaySpeed() != 1)
229 // not playing audio, or playback speed == 1
237 return CGUIMediaWindow::OnMessage(message);
240 bool CGUIWindowMusicBase::OnAction(const CAction &action)
242 if (action.GetID() == ACTION_SHOW_PLAYLIST)
244 if (g_playlistPlayer.GetCurrentPlaylist() == PLAYLIST_MUSIC ||
245 g_playlistPlayer.GetPlaylist(PLAYLIST_MUSIC).size() > 0)
247 g_windowManager.ActivateWindow(WINDOW_MUSIC_PLAYLIST);
252 return CGUIMediaWindow::OnAction(action);
255 void CGUIWindowMusicBase::OnInfoAll(int iItem, bool bCurrent, bool refresh)
257 CMusicDatabaseDirectory dir;
258 CStdString strPath = m_vecItems->GetPath();
260 strPath = m_vecItems->Get(iItem)->GetPath();
262 if (dir.HasAlbumInfo(strPath) ||
263 CMusicDatabaseDirectory::GetDirectoryChildType(strPath) ==
264 MUSICDATABASEDIRECTORY::NODE_TYPE_ALBUM)
265 g_application.StartMusicAlbumScan(strPath,refresh);
267 g_application.StartMusicArtistScan(strPath,refresh);
270 /// \brief Retrieves music info for albums from allmusic.com and displays them in CGUIDialogMusicInfo
271 /// \param iItem Item in list/thumb control
272 void CGUIWindowMusicBase::OnInfo(int iItem, bool bShowInfo)
274 if ( iItem < 0 || iItem >= m_vecItems->Size() )
277 CFileItemPtr item = m_vecItems->Get(iItem);
279 if (item->IsVideoDb())
281 OnContextButton(iItem, CONTEXT_BUTTON_INFO);
285 if (!m_vecItems->IsPlugin() && (item->IsPlugin() || item->IsScript()))
287 CGUIDialogAddonInfo::ShowForItem(item);
291 OnInfo(item.get(), bShowInfo);
294 void CGUIWindowMusicBase::OnInfo(CFileItem *pItem, bool bShowInfo)
296 if ((pItem->IsMusicDb() && !pItem->HasMusicInfoTag()) || pItem->IsParentFolder() ||
297 URIUtils::IsSpecial(pItem->GetPath()) || StringUtils::StartsWithNoCase(pItem->GetPath(), "musicsearch://"))
298 return; // nothing to do
300 if (!pItem->m_bIsFolder)
306 // this function called from outside this window - make sure the database is open
307 m_musicdatabase.Open();
310 if (pItem->IsMusicDb())
313 CDirectoryNode::GetDatabaseInfo(pItem->GetPath(), params);
314 if (params.GetAlbumId() == -1)
315 ShowArtistInfo(pItem);
317 ShowAlbumInfo(pItem);
319 if (m_dlgProgress && bShowInfo)
320 m_dlgProgress->Close();
325 GetDirectory(pItem->GetPath(), items);
327 // show dialog box indicating we're searching the album name
328 if (m_dlgProgress && bShowInfo)
330 m_dlgProgress->SetHeading(185);
331 m_dlgProgress->SetLine(0, 501);
332 m_dlgProgress->SetLine(1, "");
333 m_dlgProgress->SetLine(2, "");
334 m_dlgProgress->StartModal();
335 m_dlgProgress->Progress();
336 if (m_dlgProgress->IsCanceled())
342 // check the first song we find in the folder, and grab its album info
343 for (int i = 0; i < items.Size(); i++)
345 CFileItemPtr pItem = items[i];
346 pItem->LoadMusicTag();
347 if (pItem->HasMusicInfoTag() && pItem->GetMusicInfoTag()->Loaded() &&
348 !pItem->GetMusicInfoTag()->GetAlbum().empty())
350 if (m_dlgProgress && bShowInfo)
351 m_dlgProgress->Close();
353 if (!ShowAlbumInfo(pItem.get())) // Something went wrong, so bail for the rest
358 CLog::Log(LOGINFO, "%s called on a folder containing no songs with tag info - nothing can be done", __FUNCTION__);
359 if (m_dlgProgress && bShowInfo)
360 m_dlgProgress->Close();
363 void CGUIWindowMusicBase::ShowArtistInfo(const CFileItem *pItem, bool bShowInfo /* = true */)
366 CDirectoryNode::GetDatabaseInfo(pItem->GetPath(), params);
367 CMusicArtistInfo artistInfo;
370 // Check if we have the information in the database first
371 if (!m_musicdatabase.HasArtistInfo(params.GetArtistId()) ||
372 !m_musicdatabase.GetArtistInfo(params.GetArtistId(), artistInfo.GetArtist()))
374 if (!CProfilesManager::Get().GetCurrentProfile().canWriteDatabases() && !g_passwordManager.bMasterUser)
375 break; // should display a dialog saying no permissions
377 if (g_application.IsMusicScanning())
379 CGUIDialogOK::ShowAndGetInput(189, 14057, 0, 0);
383 // show dialog box indicating we're searching the album
384 if (m_dlgProgress && bShowInfo)
386 m_dlgProgress->SetHeading(21889);
387 m_dlgProgress->SetLine(0, pItem->GetMusicInfoTag()->GetArtist());
388 m_dlgProgress->SetLine(1, "");
389 m_dlgProgress->SetLine(2, "");
390 m_dlgProgress->StartModal();
393 CMusicInfoScanner scanner;
394 if (scanner.UpdateDatabaseArtistInfo(pItem->GetPath(), artistInfo, bShowInfo) != INFO_ADDED || !artistInfo.Loaded())
396 CGUIDialogOK::ShowAndGetInput(21889, 0, 20199, 0);
402 m_dlgProgress->Close();
404 CGUIDialogMusicInfo *pDlgArtistInfo = (CGUIDialogMusicInfo*)g_windowManager.GetWindow(WINDOW_DIALOG_MUSIC_INFO);
408 m_musicdatabase.GetArtistPath(params.GetArtistId(), strPath);
409 pDlgArtistInfo->SetArtist(artistInfo.GetArtist(), strPath);
410 pDlgArtistInfo->DoModal();
412 if (pDlgArtistInfo->NeedRefresh())
414 m_musicdatabase.DeleteArtistInfo(params.GetArtistId());
417 else if (pDlgArtistInfo->HasUpdatedThumb())
419 Update(m_vecItems->GetPath());
425 m_dlgProgress->Close();
428 bool CGUIWindowMusicBase::ShowAlbumInfo(const CFileItem *pItem, bool bShowInfo /* = true */)
431 CDirectoryNode::GetDatabaseInfo(pItem->GetPath(), params);
432 CMusicAlbumInfo albumInfo;
435 if (!m_musicdatabase.HasAlbumInfo(params.GetAlbumId()) ||
436 !m_musicdatabase.GetAlbumInfo(params.GetAlbumId(), albumInfo.GetAlbum(), &albumInfo.GetAlbum().songs))
438 if (!CProfilesManager::Get().GetCurrentProfile().canWriteDatabases() && !g_passwordManager.bMasterUser)
440 // TODO: should display a dialog saying no permissions
442 m_dlgProgress->Close();
446 if (g_application.IsMusicScanning())
448 CGUIDialogOK::ShowAndGetInput(189, 14057, 0, 0);
450 m_dlgProgress->Close();
454 // show dialog box indicating we're searching the album
455 if (m_dlgProgress && bShowInfo)
457 m_dlgProgress->SetHeading(185);
458 m_dlgProgress->SetLine(0, pItem->GetMusicInfoTag()->GetAlbum());
459 m_dlgProgress->SetLine(1, StringUtils::Join(pItem->GetMusicInfoTag()->GetAlbumArtist(), g_advancedSettings.m_musicItemSeparator));
460 m_dlgProgress->SetLine(2, "");
461 m_dlgProgress->StartModal();
464 CMusicInfoScanner scanner;
465 if (scanner.UpdateDatabaseAlbumInfo(pItem->GetPath(), albumInfo, bShowInfo) != INFO_ADDED || !albumInfo.Loaded())
467 CGUIDialogOK::ShowAndGetInput(185, 0, 500, 0);
469 m_dlgProgress->Close();
475 m_dlgProgress->Close();
477 CGUIDialogMusicInfo *pDlgAlbumInfo = (CGUIDialogMusicInfo*)g_windowManager.GetWindow(WINDOW_DIALOG_MUSIC_INFO);
481 m_musicdatabase.GetAlbumPath(params.GetAlbumId(), strPath);
482 pDlgAlbumInfo->SetAlbum(albumInfo.GetAlbum(), strPath);
483 pDlgAlbumInfo->DoModal();
485 if (pDlgAlbumInfo->NeedRefresh())
487 m_musicdatabase.DeleteAlbumInfo(params.GetAlbumId());
490 else if (pDlgAlbumInfo->HasUpdatedThumb())
492 UpdateThumb(albumInfo.GetAlbum(), strPath);
498 m_dlgProgress->Close();
502 void CGUIWindowMusicBase::ShowSongInfo(CFileItem* pItem)
504 CGUIDialogSongInfo *dialog = (CGUIDialogSongInfo *)g_windowManager.GetWindow(WINDOW_DIALOG_SONG_INFO);
507 if (!pItem->IsMusicDb())
508 pItem->LoadMusicTag();
509 if (!pItem->HasMusicInfoTag())
512 dialog->SetSong(pItem);
513 dialog->DoModal(GetID());
514 if (dialog->NeedsUpdate())
515 Refresh(true); // update our file list
520 /// \brief Can be overwritten to implement an own tag filling function.
521 /// \param items File items to fill
522 void CGUIWindowMusicBase::OnRetrieveMusicInfo(CFileItemList& items)
527 /// \brief Retrieve tag information for \e m_vecItems
528 void CGUIWindowMusicBase::RetrieveMusicInfo()
530 unsigned int startTick = XbmcThreads::SystemClockMillis();
532 OnRetrieveMusicInfo(*m_vecItems);
534 CLog::Log(LOGDEBUG, "RetrieveMusicInfo() took %u msec",
535 XbmcThreads::SystemClockMillis() - startTick);
538 /// \brief Add selected list/thumb control item to playlist and start playing
539 /// \param iItem Selected Item in list/thumb control
540 void CGUIWindowMusicBase::OnQueueItem(int iItem)
542 // don't re-queue items from playlist window
543 if ( iItem < 0 || iItem >= m_vecItems->Size() || GetID() == WINDOW_MUSIC_PLAYLIST) return ;
545 int iOldSize=g_playlistPlayer.GetPlaylist(PLAYLIST_MUSIC).size();
547 // add item 2 playlist (make a copy as we alter the queuing state)
548 CFileItemPtr item(new CFileItem(*m_vecItems->Get(iItem)));
550 if (item->IsRAR() || item->IsZIP())
553 // Allow queuing of unqueueable items
554 // when we try to queue them directly
555 if (!item->CanQueue())
556 item->SetCanQueue(true);
558 CLog::Log(LOGDEBUG, "Adding file %s%s to music playlist", item->GetPath().c_str(), item->m_bIsFolder ? " (folder) " : "");
559 CFileItemList queuedItems;
560 AddItemToPlayList(item, queuedItems);
563 m_viewControl.SetSelectedItem(iItem + 1);
565 // if party mode, add items but DONT start playing
566 if (g_partyModeManager.IsEnabled())
568 g_partyModeManager.AddUserSongs(queuedItems, false);
572 g_playlistPlayer.Add(PLAYLIST_MUSIC, queuedItems);
573 if (g_playlistPlayer.GetPlaylist(PLAYLIST_MUSIC).size() && !g_application.m_pPlayer->IsPlayingAudio())
575 if (m_guiState.get())
576 m_guiState->SetPlaylistDirectory("playlistmusic://");
578 g_playlistPlayer.Reset();
579 g_playlistPlayer.SetCurrentPlaylist(PLAYLIST_MUSIC);
580 g_playlistPlayer.Play(iOldSize); // start playing at the first new item
584 /// \brief Add unique file and folders and its subfolders to playlist
585 /// \param pItem The file item to add
586 void CGUIWindowMusicBase::AddItemToPlayList(const CFileItemPtr &pItem, CFileItemList &queuedItems)
588 if (!pItem->CanQueue() || pItem->IsRAR() || pItem->IsZIP() || pItem->IsParentFolder()) // no zip/rar enques thank you!
591 // fast lookup is needed here
592 queuedItems.SetFastLookup(true);
594 if (pItem->IsMusicDb() && pItem->m_bIsFolder && !pItem->IsParentFolder())
595 { // we have a music database folder, just grab the "all" item underneath it
596 CMusicDatabaseDirectory dir;
597 if (!dir.ContainsSongs(pItem->GetPath()))
598 { // grab the ALL item in this category
599 // Genres will still require 2 lookups, and queuing the entire Genre folder
600 // will require 3 lookups (genre, artist, album)
601 CMusicDbUrl musicUrl;
602 if (musicUrl.FromString(pItem->GetPath()))
604 musicUrl.AppendPath("-1/");
605 CFileItemPtr item(new CFileItem(musicUrl.ToString(), true));
606 item->SetCanQueue(true); // workaround for CanQueue() check above
607 AddItemToPlayList(item, queuedItems);
612 if (pItem->m_bIsFolder || (g_windowManager.GetActiveWindow() == WINDOW_MUSIC_NAV && pItem->IsPlayList()))
614 // Check if we add a locked share
615 if ( pItem->m_bIsShareOrDrive )
617 CFileItem item = *pItem;
618 if ( !g_passwordManager.IsItemUnlocked( &item, "music" ) )
624 GetDirectory(pItem->GetPath(), items);
625 //OnRetrieveMusicInfo(items);
626 FormatAndSort(items);
627 for (int i = 0; i < items.Size(); ++i)
628 AddItemToPlayList(items[i], queuedItems);
632 if (pItem->IsPlayList())
634 auto_ptr<CPlayList> pPlayList (CPlayListFactory::Create(*pItem));
638 if (!pPlayList->Load(pItem->GetPath()))
640 CGUIDialogOK::ShowAndGetInput(6, 0, 477, 0);
641 return; //hmmm unable to load playlist?
644 CPlayList playlist = *pPlayList;
645 for (int i = 0; i < (int)playlist.size(); ++i)
647 AddItemToPlayList(playlist[i], queuedItems);
652 else if(pItem->IsInternetStream())
653 { // just queue the internet stream, it will be expanded on play
654 queuedItems.Add(pItem);
656 else if (pItem->IsPlugin() && pItem->GetProperty("isplayable") == "true")
658 // python files can be played
659 queuedItems.Add(pItem);
661 else if (!pItem->IsNFO() && pItem->IsAudio())
663 CFileItemPtr itemCheck = queuedItems.Get(pItem->GetPath());
664 if (!itemCheck || itemCheck->m_lStartOffset != pItem->m_lStartOffset)
666 CFileItemPtr item(new CFileItem(*pItem));
667 m_musicdatabase.SetPropertiesForFileItem(*item);
668 queuedItems.Add(item);
674 void CGUIWindowMusicBase::UpdateButtons()
676 // Update window selection control
678 // Remove labels from the window selection
679 CGUIMessage msg(GUI_MSG_LABEL_RESET, GetID(), CONTROL_BTNTYPE);
680 g_windowManager.SendMessage(msg);
682 // Add labels to the window selection
683 CGUIMessage msg2(GUI_MSG_LABEL_ADD, GetID(), CONTROL_BTNTYPE);
684 msg2.SetLabel(g_localizeStrings.Get(744)); // Files
685 g_windowManager.SendMessage(msg2);
687 msg2.SetLabel(g_localizeStrings.Get(15100)); // Library
688 g_windowManager.SendMessage(msg2);
690 // Select the current window as default item
691 CONTROL_SELECT_ITEM(CONTROL_BTNTYPE, CSettings::Get().GetInt("mymusic.startwindow") - WINDOW_MUSIC_FILES);
693 CGUIMediaWindow::UpdateButtons();
696 void CGUIWindowMusicBase::GetContextButtons(int itemNumber, CContextButtons &buttons)
699 if (itemNumber >= 0 && itemNumber < m_vecItems->Size())
700 item = m_vecItems->Get(itemNumber);
702 if (item && !item->GetProperty("pluginreplacecontextitems").asBoolean())
704 if (item && !item->IsParentFolder())
706 if (!m_vecItems->IsPlugin() && (item->IsPlugin() || item->IsScript()))
707 buttons.Add(CONTEXT_BUTTON_INFO,24003); // Add-on info
708 if (item->CanQueue() && !item->IsAddonsPath() && !item->IsScript())
710 buttons.Add(CONTEXT_BUTTON_QUEUE_ITEM, 13347); //queue
712 // allow a folder to be ad-hoc queued and played by the default player
713 if (item->m_bIsFolder || (item->IsPlayList() &&
714 !g_advancedSettings.m_playlistAsFolders))
716 buttons.Add(CONTEXT_BUTTON_PLAY_ITEM, 208); // Play
719 { // check what players we have, if we have multiple display play with option
720 VECPLAYERCORES vecCores;
721 CPlayerCoreFactory::Get().GetPlayers(*item, vecCores);
722 if (vecCores.size() >= 1)
723 buttons.Add(CONTEXT_BUTTON_PLAY_WITH, 15213); // Play With...
725 if (item->IsSmartPlayList())
727 buttons.Add(CONTEXT_BUTTON_PLAY_PARTYMODE, 15216); // Play in Partymode
730 if (item->IsSmartPlayList() || m_vecItems->IsSmartPlayList())
731 buttons.Add(CONTEXT_BUTTON_EDIT_SMART_PLAYLIST, 586);
732 else if (item->IsPlayList() || m_vecItems->IsPlayList())
733 buttons.Add(CONTEXT_BUTTON_EDIT, 586);
737 CGUIMediaWindow::GetContextButtons(itemNumber, buttons);
740 void CGUIWindowMusicBase::GetNonContextButtons(CContextButtons &buttons)
742 if (!m_vecItems->IsVirtualDirectoryRoot())
743 buttons.Add(CONTEXT_BUTTON_GOTO_ROOT, 20128);
744 buttons.Add(CONTEXT_BUTTON_SETTINGS, 5);
747 bool CGUIWindowMusicBase::OnContextButton(int itemNumber, CONTEXT_BUTTON button)
750 if (itemNumber >= 0 && itemNumber < m_vecItems->Size())
751 item = m_vecItems->Get(itemNumber);
755 case CONTEXT_BUTTON_QUEUE_ITEM:
756 OnQueueItem(itemNumber);
759 case CONTEXT_BUTTON_INFO:
763 case CONTEXT_BUTTON_SONG_INFO:
765 ShowSongInfo(item.get());
769 case CONTEXT_BUTTON_EDIT:
771 CStdString playlist = item->IsPlayList() ? item->GetPath() : m_vecItems->GetPath(); // save path as activatewindow will destroy our items
772 g_windowManager.ActivateWindow(WINDOW_MUSIC_PLAYLIST_EDITOR, playlist);
774 m_vecItems->RemoveDiscCache(GetID());
778 case CONTEXT_BUTTON_EDIT_SMART_PLAYLIST:
780 CStdString playlist = item->IsSmartPlayList() ? item->GetPath() : m_vecItems->GetPath(); // save path as activatewindow will destroy our items
781 if (CGUIDialogSmartPlaylistEditor::EditPlaylist(playlist, "music"))
782 Refresh(true); // need to update
786 case CONTEXT_BUTTON_PLAY_ITEM:
787 PlayItem(itemNumber);
790 case CONTEXT_BUTTON_PLAY_WITH:
792 VECPLAYERCORES vecCores; // base class?
793 CPlayerCoreFactory::Get().GetPlayers(*item, vecCores);
794 g_application.m_eForcedNextPlayer = CPlayerCoreFactory::Get().SelectPlayerDialog(vecCores);
795 if( g_application.m_eForcedNextPlayer != EPC_NONE )
800 case CONTEXT_BUTTON_PLAY_PARTYMODE:
801 g_partyModeManager.Enable(PARTYMODECONTEXT_MUSIC, item->GetPath());
804 case CONTEXT_BUTTON_STOP_SCANNING:
806 g_application.StopMusicScan();
810 case CONTEXT_BUTTON_GOTO_ROOT:
814 case CONTEXT_BUTTON_SETTINGS:
815 g_windowManager.ActivateWindow(WINDOW_SETTINGS_MYMUSIC);
821 return CGUIMediaWindow::OnContextButton(itemNumber, button);
824 void CGUIWindowMusicBase::OnRipCD()
826 if(g_mediaManager.IsAudio())
828 if (!g_application.CurrentFileItem().IsCDDA())
830 #ifdef HAS_CDDA_RIPPER
831 CCDDARipper::GetInstance().RipCD();
835 CGUIDialogOK::ShowAndGetInput(257, 20099, 0, 0);
839 void CGUIWindowMusicBase::OnRipTrack(int iItem)
841 if(g_mediaManager.IsAudio())
843 if (!g_application.CurrentFileItem().IsCDDA())
845 #ifdef HAS_CDDA_RIPPER
846 CFileItemPtr item = m_vecItems->Get(iItem);
847 CCDDARipper::GetInstance().RipTrack(item.get());
851 CGUIDialogOK::ShowAndGetInput(257, 20099, 0, 0);
855 void CGUIWindowMusicBase::PlayItem(int iItem)
857 // restrictions should be placed in the appropiate window code
858 // only call the base code if the item passes since this clears
859 // the current playlist
861 const CFileItemPtr pItem = m_vecItems->Get(iItem);
863 // special case for DAAP playlist folders
864 bool bIsDAAPplaylist = false;
865 #ifdef HAS_FILESYSTEM_DAAP
866 if (pItem->IsDAAP() && pItem->m_bIsFolder)
868 CDAAPDirectory dirDAAP;
869 if (dirDAAP.GetCurrLevel(pItem->GetPath()) == 0)
870 bIsDAAPplaylist = true;
873 // if its a folder, build a playlist
874 if ((pItem->m_bIsFolder && !pItem->IsPlugin()) || (g_windowManager.GetActiveWindow() == WINDOW_MUSIC_NAV && pItem->IsPlayList()))
876 // make a copy so that we can alter the queue state
877 CFileItemPtr item(new CFileItem(*m_vecItems->Get(iItem)));
879 // Allow queuing of unqueueable items
880 // when we try to queue them directly
881 if (!item->CanQueue())
882 item->SetCanQueue(true);
885 if (item->IsParentFolder())
888 CFileItemList queuedItems;
889 AddItemToPlayList(item, queuedItems);
890 if (g_partyModeManager.IsEnabled())
892 g_partyModeManager.AddUserSongs(queuedItems, true);
897 CStdString strPlayListDirectory = m_vecItems->GetPath();
898 URIUtils::RemoveSlashAtEnd(strPlayListDirectory);
901 g_playlistPlayer.ClearPlaylist(PLAYLIST_MUSIC);
902 g_playlistPlayer.Reset();
903 g_playlistPlayer.Add(PLAYLIST_MUSIC, queuedItems);
904 g_playlistPlayer.SetCurrentPlaylist(PLAYLIST_MUSIC);
906 // activate the playlist window if its not activated yet
907 if (bIsDAAPplaylist && GetID() == g_windowManager.GetActiveWindow())
908 g_windowManager.ActivateWindow(WINDOW_MUSIC_PLAYLIST);
911 g_playlistPlayer.Play();
913 else if (pItem->IsPlayList())
915 // load the playlist the old way
916 LoadPlayList(pItem->GetPath());
920 // just a single item, play it
921 // TODO: Add music-specific code for single playback of an item here (See OnClick in MediaWindow, and OnPlayMedia below)
926 void CGUIWindowMusicBase::LoadPlayList(const CStdString& strPlayList)
928 // if partymode is active, we disable it
929 if (g_partyModeManager.IsEnabled())
930 g_partyModeManager.Disable();
932 // load a playlist like .m3u, .pls
933 // first get correct factory to load playlist
934 auto_ptr<CPlayList> pPlayList (CPlayListFactory::Create(strPlayList));
938 if (!pPlayList->Load(strPlayList))
940 CGUIDialogOK::ShowAndGetInput(6, 0, 477, 0);
941 return; //hmmm unable to load playlist?
945 int iSize = pPlayList->size();
946 if (g_application.ProcessAndStartPlaylist(strPlayList, *pPlayList, PLAYLIST_MUSIC))
948 if (m_guiState.get())
949 m_guiState->SetPlaylistDirectory("playlistmusic://");
950 // activate the playlist window if its not activated yet
951 if (GetID() == g_windowManager.GetActiveWindow() && iSize > 1)
953 g_windowManager.ActivateWindow(WINDOW_MUSIC_PLAYLIST);
958 bool CGUIWindowMusicBase::OnPlayMedia(int iItem)
960 CFileItemPtr pItem = m_vecItems->Get(iItem);
963 if (g_partyModeManager.IsEnabled())
965 CPlayList playlistTemp;
966 playlistTemp.Add(pItem);
967 g_partyModeManager.AddUserSongs(playlistTemp, true);
970 else if (!pItem->IsPlayList() && !pItem->IsInternetStream())
971 { // single music file - if we get here then we have autoplaynextitem turned off or queuebydefault
972 // turned on, but we still want to use the playlist player in order to handle more queued items
974 // Karaoke items also can be added in runtime (while the song is played), so it should be queued too.
975 if ( (CSettings::Get().GetBool("musicplayer.queuebydefault") && g_windowManager.GetActiveWindow() != WINDOW_MUSIC_PLAYLIST_EDITOR)
976 || pItem->IsKaraoke() )
978 // TODO: Should the playlist be cleared if nothing is already playing?
982 g_playlistPlayer.Reset();
983 g_playlistPlayer.ClearPlaylist(PLAYLIST_MUSIC);
984 g_playlistPlayer.Add(PLAYLIST_MUSIC, pItem);
985 g_playlistPlayer.SetCurrentPlaylist(PLAYLIST_MUSIC);
986 g_playlistPlayer.Play();
989 return CGUIMediaWindow::OnPlayMedia(iItem);
992 void CGUIWindowMusicBase::UpdateThumb(const CAlbum &album, const CStdString &path)
994 // check user permissions
995 bool saveDb = album.idAlbum != -1;
996 bool saveDirThumb = true;
997 if (!CProfilesManager::Get().GetCurrentProfile().canWriteDatabases() && !g_passwordManager.bMasterUser)
1000 saveDirThumb = false;
1003 CStdString albumThumb = m_musicdatabase.GetArtForItem(album.idAlbum, "album", "thumb");
1005 // Update the thumb in the music database (songs + albums)
1006 CStdString albumPath(path);
1007 if (saveDb && CFile::Exists(albumThumb))
1008 m_musicdatabase.SaveAlbumThumb(album.idAlbum, albumThumb);
1010 // Update currently playing song if it's from the same album. This is necessary as when the album
1011 // first gets it's cover, the info manager's item doesn't have the updated information (so will be
1012 // sending a blank thumb to the skin.)
1013 if (g_application.m_pPlayer->IsPlayingAudio())
1015 const CMusicInfoTag* tag=g_infoManager.GetCurrentSongTag();
1018 // really, this may not be enough as it is to reliably update this item. eg think of various artists albums
1019 // that aren't tagged as such (and aren't yet scanned). But we probably can't do anything better than this
1021 if (album.strAlbum == tag->GetAlbum() && (album.artist == tag->GetAlbumArtist() ||
1022 album.artist == tag->GetArtist()))
1024 g_infoManager.SetCurrentAlbumThumb(albumThumb);
1029 // Save this thumb as the directory thumb if it's the only album in the folder (files view nicety)
1030 // We do this by grabbing all the songs in the folder, and checking to see whether they come
1031 // from the same album.
1032 if (saveDirThumb && CFile::Exists(albumThumb) && !albumPath.empty() && !URIUtils::IsCDDA(albumPath))
1034 CFileItemList items;
1035 GetDirectory(albumPath, items);
1036 OnRetrieveMusicInfo(items);
1038 CMusicInfoScanner::FileItemsToAlbums(items, albums);
1039 if (albums.size() == 1)
1040 { // set as folder thumb as well
1041 CMusicThumbLoader loader;
1042 loader.SetCachedImage(items, "thumb", albumPath);
1046 // update the file listing - we have to update the whole lot, as it's likely that
1047 // more than just our thumbnaias changed
1048 // TODO: Ideally this would only be done when needed - at the moment we appear to be
1049 // doing this for every lookup, possibly twice (see ShowAlbumInfo)
1052 // Do we have to autoswitch to the thumb control?
1053 m_guiState.reset(CGUIViewState::GetViewState(GetID(), *m_vecItems));
1057 void CGUIWindowMusicBase::OnRetrieveMusicInfo(CFileItemList& items)
1059 if (items.GetFolderCount()==items.Size() || items.IsMusicDb() ||
1060 (!CSettings::Get().GetBool("musicfiles.usetags") && !items.IsCDDA()))
1064 // Start the music info loader thread
1065 m_musicInfoLoader.SetProgressCallback(m_dlgProgress);
1066 m_musicInfoLoader.Load(items);
1068 bool bShowProgress=!g_windowManager.HasModalDialog();
1069 bool bProgressVisible=false;
1071 unsigned int tick=XbmcThreads::SystemClockMillis();
1073 while (m_musicInfoLoader.IsLoading())
1076 { // Do we have to init a progress dialog?
1077 unsigned int elapsed=XbmcThreads::SystemClockMillis()-tick;
1079 if (!bProgressVisible && elapsed>1500 && m_dlgProgress)
1080 { // tag loading takes more then 1.5 secs, show a progress dialog
1081 CURL url(items.GetPath());
1082 CStdString strStrippedPath = url.GetWithoutUserDetails();
1083 m_dlgProgress->SetHeading(189);
1084 m_dlgProgress->SetLine(0, 505);
1085 m_dlgProgress->SetLine(1, "");
1086 m_dlgProgress->SetLine(2, strStrippedPath );
1087 m_dlgProgress->StartModal();
1088 m_dlgProgress->ShowProgressBar(true);
1089 bProgressVisible = true;
1092 if (bProgressVisible && m_dlgProgress && !m_dlgProgress->IsCanceled())
1094 m_dlgProgress->Progress();
1096 } // if (bShowProgress)
1098 } // while (m_musicInfoLoader.IsLoading())
1100 if (bProgressVisible && m_dlgProgress)
1101 m_dlgProgress->Close();
1104 bool CGUIWindowMusicBase::GetDirectory(const CStdString &strDirectory, CFileItemList &items)
1106 items.SetArt("thumb", "");
1107 bool bResult = CGUIMediaWindow::GetDirectory(strDirectory, items);
1110 CMusicThumbLoader loader;
1111 loader.FillThumb(items);
1114 // add in the "New Playlist" item if we're in the playlists folder
1115 if ((items.GetPath() == "special://musicplaylists/") && !items.Contains("newplaylist://"))
1117 CFileItemPtr newPlaylist(new CFileItem(CProfilesManager::Get().GetUserDataItem("PartyMode.xsp"),false));
1118 newPlaylist->SetLabel(g_localizeStrings.Get(16035));
1119 newPlaylist->SetLabelPreformated(true);
1120 newPlaylist->m_bIsFolder = true;
1121 items.Add(newPlaylist);
1123 newPlaylist.reset(new CFileItem("newplaylist://", false));
1124 newPlaylist->SetLabel(g_localizeStrings.Get(525));
1125 newPlaylist->SetLabelPreformated(true);
1126 newPlaylist->SetSpecialSort(SortSpecialOnBottom);
1127 newPlaylist->SetCanQueue(false);
1128 items.Add(newPlaylist);
1130 newPlaylist.reset(new CFileItem("newsmartplaylist://music", false));
1131 newPlaylist->SetLabel(g_localizeStrings.Get(21437));
1132 newPlaylist->SetLabelPreformated(true);
1133 newPlaylist->SetSpecialSort(SortSpecialOnBottom);
1134 newPlaylist->SetCanQueue(false);
1135 items.Add(newPlaylist);
1141 bool CGUIWindowMusicBase::CheckFilterAdvanced(CFileItemList &items) const
1143 CStdString content = items.GetContent();
1144 if ((items.IsMusicDb() || CanContainFilter(m_strFilterPath)) &&
1145 (content.Equals("artists") || content.Equals("albums") || content.Equals("songs")))
1151 bool CGUIWindowMusicBase::CanContainFilter(const CStdString &strDirectory) const
1153 return StringUtils::StartsWithNoCase(strDirectory, "musicdb://");
1156 void CGUIWindowMusicBase::OnInitWindow()
1158 CGUIMediaWindow::OnInitWindow();
1159 if (CMediaSettings::Get().GetMusicNeedsUpdate() == 35 && !g_application.IsMusicScanning() &&
1160 g_infoManager.GetLibraryBool(LIBRARY_HAS_MUSIC))
1162 // rescan of music library required
1163 if (CGUIDialogYesNo::ShowAndGetInput(799, 800, 801, -1))
1165 int flags = CMusicInfoScanner::SCAN_RESCAN;
1166 if (CSettings::Get().GetBool("musiclibrary.downloadinfo"))
1167 flags |= CMusicInfoScanner::SCAN_ONLINE;
1168 if (CSettings::Get().GetBool("musiclibrary.backgroundupdate"))
1169 flags |= CMusicInfoScanner::SCAN_BACKGROUND;
1170 g_application.StartMusicScan("", flags);
1171 CMediaSettings::Get().SetMusicNeedsUpdate(0); // once is enough (user may interrupt, but that's up to them)
1172 CSettings::Get().Save();
1177 CStdString CGUIWindowMusicBase::GetStartFolder(const CStdString &dir)
1179 if (dir.Equals("Plugins") || dir.Equals("Addons"))
1180 return "addons://sources/audio/";
1181 else if (dir.Equals("$PLAYLISTS") || dir.Equals("Playlists"))
1182 return "special://musicplaylists/";
1183 return CGUIMediaWindow::GetStartFolder(dir);